[AIFFEL] 22.Jan.04 - Exploration, making first deep-learning model

Deok Jong Moon·2022년 1월 4일
0
post-thumbnail

배경

사실 난 K-Digital-Training 과정을 듣기 전에 나의 메타인지를 위해(난 도대체 어떤 부분이 모자랄까...를 알기 위해) 맛보기로 KDC 과정으로 아이펠의 '쫄지말자 딥러닝'과 '캐글러로 가는 데이터 분석 첫걸음'을 들었었다. 그러다 보니 오늘 하는 '인공지능으로 가위바위보 만들기' 프로젝트도 한 번 했던 것 같다. 그래서 나는 이번에는 했던걸 반복한다는 느낌보다 나 혼자서 전체적인 그림 생각해보기, 혹시나 새롭게 이 문제에 다가갈 방법은 없을지를 고민하며 진행할 예정이다.

오늘이 지난 후 후기)
: 사실 해보니 새롭게 이 문제를 바라보게 된다기 보다 복습하는 듯한 느낌이었다. 이전에 놓쳤던 부분인데 이번에는 제대로 이해됐다라고 할까...

학습한 개념

  • 일반적으로 딥러닝 기술은

    "데이터 준비 → 딥러닝 네트워크 설계 → 학습 → 테스트(평가)"의 순서대로
    만들게 된단다.

  • the error rate improves when the digits are centered by bounding box rather than center of mass(데이터 양질화)
  • error graph를 그린다면
    • training error와 test error가 있고, 이것의 최저 지점이 되는 파라미터가 좋은 것
    • 근데 여기서 test는 나중에 실제 평가를 하기 위한 것이니
    • 결국 validation dataset을 test인냥 써보면서 그것의 error를 갖고 training error vs test error 그래프를 그리는 것이다.
  • np.min(), np.max()에 3차원 벡터가 들어가면 뭘 기준으로 값을 뽑아내나 찾아보니 기본적으로 flattened input을 사용한단다.
  • Keras Sequential API를 오늘 쓴 이유

    Sequential API는 개발의 자유도는 많이 떨어지지만, 매우 간단하게 딥러닝 모델을 만들어낼 수 있는 방법입니다. 여러분들은 이 방법을 통해 미리 정의된 딥러닝 레이어(layer)를 손쉽게 추가할 수 있습니다.

  • Sequential API 코드 요소 설명(출처 : AIFFEL Exploration 노드)
  • 최종 Dense layer의 class 수는 생각해보니 숫자 인식기에서는 10, 알파벳 인식기에서는 52개(대소문자 구분)여야 한다.
  • Sequential API에서 모델 학습 및 평가 코드 순서
    1) model = keras.model.Sequential() # 모델 생성
    2) model.add(keras.layers...) # 레이어 쌓기
    3) model.compile(optimizer='adam'...) # 학습할 수 있게 configuration
    4) model.fit(x_train, y_train, epoch=) # 모델 학습
    5) model.evaluate(x_test, y_test) # 모델 평가(테스트 데이터로)
    6) model.predict(x_test) # 모델이 추론한 값만 보기
  • dataset을 0~1로 정규화를 나름 해주었다.
    : 기존에는 0~255의 값을 가진 array였는데, 그걸 다 255로 나누었다.
    : 다른 더 큰 범위(예 : 1,000 ~ 10,000)를 가진 데이터 컬럼이 존재하는 것도 아닌데, 이렇게 했다.
    : 이유가 궁금하다...(아마도 그냥 0~255 자체도 학습에 별로 안 좋은 크기의 범위여서일까...?)
  • glob 모듈
    : regex 비슷하게 특정 패턴을 가진 파일 directory를 갖고온다.
    : 그래서 뭔가 다량의 파일을 for loop으로 열 때 좋은 모듈 같다.

미니프로젝트

  • 하이퍼파라미터 튜닝
    • 두번째 Conv2D의 이미지 특징 수를 32->64로 두배 늘리니까 accuracy가 제일 좋아졌다.
    • 보니까 trainable params 수가 60000대로 확 뛰는데(다른 건 30000대) 아마 그 수가 결정적인 영향을 끼치는 것 같다.
each multiplied by 2 from originaltotal paramstest_accuracy
n_channel_1(16 -> 32)35,5300.9871
n_channel_2(32 -> 64)61,0020.9904
n_dense(32 -> 64)56,7140.9899
n_train_epoch(10 -> 20)30,7620.9905
  • 하이퍼파라미터 튜닝 등을 위해 모델을 여러 개 만든다면...

    • 모델 변수명을 다르게 하는 것을 추천한다.
    • 모델 성과가 좋았어도 이름을 다 model로 하니 나중에 찾을 수가 없다...
  • 데이터 퀄리티의 중요성 !

    • 이 부분 정말 뼈저리게 느꼈다.
    • 일단 내가 accuracy를 높이기 위해 시도한 것들을 정리하자면
      1) train 300개(내 손사진) : test 300개(다른 사람 손 사진)
      -> accuracy : 0.3333...(이건 클래스 3개 1개로만 해도 이 정도 아님..?)
      2) 모델 하이퍼파라미터 튜닝
      -> accuracy : 0.33....
      3) train data 늘려봄(train 300 + 2,520개(깨끗한 CG 이미지) : test 300개)
      -> accuracy : 0.379...
      4) train data 늘렸으니 다시 하이퍼파라미터 튜닝(더 복잡하게 하기)
      -> accuracy : 0.27...(overfitting이란 걸 겪어봤다)
      5) 다시 하이퍼파라미터 튜닝(덜 복잡하게)
      -> accuracy : 0.56...(오 나름의 효과. 근데 아직 기준인 0.6 등은 안 나옴)
      6) 혹시 내 손 사진이 학습에 안 좋을까 빼 봄
      -> accuracy : 0.36...(test 데이터가 내 손 같은데 뭐...)
      7) 그래서 그냥 내 손 같은 데이터 약 2,000개 더 가져옴(train 4,917개 : test 300개)
      -> accuracy : 0.99(와우...)

      결론은 test, 즉 실제 데이터에 흡사한 train 데이터가 많은 게 좋다.

  • 이미지 데이터의 전처리(이 부분도 살짝 헤맴)

    • png는 array로 가져와보니 채널이 4개
      • 알고보니 (R,G,B,transparency)가 있단다.
      • 원하면 이걸 Image.open().convert('RGB')로 3채널로 바꿀 수 있다.
      • 혹은 이미지 array 자체를 4채널로 정해놓고 딥러닝 모델의 인풋도 그렇게 하면 문제 없다.

질문

1) 왜 숫자(0~9)는 10개의 class로 구현할까? 잘 모르니 던져보자면 '숫자'라는 1개의 class에 10개의 정보를 담을 수는 없을까?
-> 어쩌면 진짜 python의 class 개념이 아니라 kind의 개념일 수도 있겠다...

2) dataset이 array 1개(0~255)까지인데(물론 shape은 이미지이니 4차원) 다른 abnormaly different한 데이터 컬럼이 없는데도 255를 나눠서 정규화를 한 이유는 뭘까...?

더 공부할 것

  • 리눅스 command...(개념들을 알면 더 재미있게 terminal에 칠 수 있을 것 같다. 갈수록 알아가고, 재미있어짐에 감사)
  • keras에서 model.evaluate(), model.predict()의 차이(evaluate 후 같은 데이터를 predict에 써도 될까...?)

오늘의 코드

import numpy as np

def load_data(img_path, number_of_data=300):  # 가위바위보 이미지 개수 총합에 주의하세요.
    # 가위 : 0, 바위 : 1, 보 : 2
    img_size=28
    color=3
    #이미지 데이터와 라벨(가위 : 0, 바위 : 1, 보 : 2) 데이터를 담을 행렬(matrix) 영역을 생성합니다.
    imgs=np.zeros(number_of_data*img_size*img_size*color,dtype=np.int32).reshape(number_of_data,img_size,img_size,color)
    labels=np.zeros(number_of_data,dtype=np.int32)

    idx=0
    for file in glob.iglob(img_path+'/scissor/*.jpg'):
        img = np.array(Image.open(file),dtype=np.int32)
        imgs[idx,:,:,:]=img    # 데이터 영역에 이미지 행렬을 복사
        labels[idx]=0   # 가위 : 0
        idx=idx+1

    for file in glob.iglob(img_path+'/rock/*.jpg'):
        img = np.array(Image.open(file),dtype=np.int32)
        imgs[idx,:,:,:]=img    # 데이터 영역에 이미지 행렬을 복사
        labels[idx]=1   # 바위 : 1
        idx=idx+1  
    
    for file in glob.iglob(img_path+'/paper/*.jpg'):
        img = np.array(Image.open(file),dtype=np.int32)
        imgs[idx,:,:,:]=img    # 데이터 영역에 이미지 행렬을 복사
        labels[idx]=2   # 보 : 2
        idx=idx+1
        
    print("학습데이터(x_train)의 이미지 개수는", idx,"입니다.")
    return imgs, labels

image_dir_path = os.getenv("HOME") + "/aiffel/rock_scissor_paper"
(x_train, y_train)=load_data(image_dir_path)
x_train_norm = x_train/255.0   # 입력은 0~1 사이의 값으로 정규화

print("x_train shape: {}".format(x_train.shape))
print("y_train shape: {}".format(y_train.shape))

>>>
학습데이터(x_train)의 이미지 개수는 300 입니다.
x_train shape: (300, 28, 28, 3)
y_train shape: (300,)

이 코드가 하는 걸 정리하자면,
1) np.zeros() array를 만듦
2) 그 array는 크기가 받을 이미지 데이터의 갯수와 가로, 세로 픽셀수 그리고 컬러채널 수의 곱, 즉 다 담는 크기이다.
3) 그리고 이미지 파일을 불러온다.
4) for loop를 사용하고, 불러온 순서대로 값을 지정한다.
5) 어떤 값이냐면, 이미지 데이터 그 자체를 다시 할당,
6) 다른 쪽에서는 그 값의 label을 같은 순서에 담는 array 업데이트

profile
'어떻게든 자야겠어'라는 저 아이를 닮고 싶습니다

0개의 댓글

관련 채용 정보