deep learning with python


케라스 창시자에게 배우는 딥러닝을 실습하면서 정리한 포스트입니다. 코드 예제와 코드 설명은 역자 깃허브에서 받아볼 수 있습니다. 출판물이고 개인적으로만 참고하기 위한 요약노트이다 보니 설명이 불친절한 점은 양해 바랍니다. 보다 자세한 내용을 원하시는 분은 위 링크의 책을 참고하시기 바랍니다.


4장. 머신러닝의 기본 요소

머신러닝의 4가지 분류

지도학습

  • 가장 흔한 경우. 앞의 예제들과 광학 문자 판독, 음성 인식, 이미지 분류, 번역 등
  • 대부분 회귀지만 이런 변종도 있음
    • sequence generation: 사진이 주어지면 이를 설명하는 캡션 생성
    • syntax tree expectation: 문장이 주어지면 분해된 구문 트리를 예측
    • object detection: 사진 안의 특정 물체에 bounding box를 그림
    • image segmentation: 사진을 픽셀 단위로 특정 물체에 masking

비지도학습

  • 타깃 사용하지 않고 입력에 대한 흥미로운 변환을 찾는다
  • 데이터 시각화, 데이터 압축, 데이터 노이즈 제거, 상관관계 이해에 사용
  • 차원 축소와 클러스터링

자기지도학습

  • 지도학습의 특별한 경우. 지도학습이지만 사람이 만든 라벨을 사용하지 않음
  • 라벨이 필요하지만 휴리스틱 알고리즘(경험적인 알고리즘)을 사용해 입력 데이터에서 생성
  • ex) 오토인코더, 지난 프레임이 주어졌을 때 다음 프레임을 예측, 단어가 주어졌을때 다음 단어를 예측

강화학습

  • 자율주행 자동차, 자원 관리, 교육 등에서 애플리케이션 등장 예상됨

머신러닝 모델 평가

  • 과대적합을 완화하고 일반화를 최대화하기 위한 전략(처음 본 데이터에서 잘 작동하는 모델 찾기)

훈련, 검증, 테스트셋

  • 데이터가 적을 때 데이터셋을 나누려면 다음과 같은 고급 기법이 도움이 됨
    • 단순 홀드아웃 검증: 데이터 일부를 테스트셋으로 떼어 둠
    • K-겹 교차검증: 데이터를 동일한 크기를 가진 K개 분할로 나눠 각 분할 i에 대해 남은 K-1개의 분할로 모델을 훈련하고 분할 i 에서 모델을 평가
    • 셔플링을 사용한 반복 K-겹 교차 검증: K개의 분할로 나누기 전에 매번 데이터를 무작위로 섞기

기억해야 할 것

  • 대표성 있는 데이터를 골라야 한다. 타깃이 0~9까지 9가지 숫자인데 테스트셋에 타깃이 0~7까지 있는 데이터만 넣는다면?
  • 시간의 방향: 과거로부터 미래를 예측하려고 한다면 테스트셋에 있는 데이터가 트레이닝셋 데이터보다 미래에 있어야 한다
  • 데이터 중복: 한 데이터셋에 같은 데이터가 두 번 등장하면 트레이닝셋의 일부로 테스트를 하는 일이 발생할 수 있다.

데이터 전처리, 피쳐 엔지니어링, 피쳐 학습

신경망을 위한 데이터 전처리

  • 원본 데이터를 신경망에 적용하기 쉽게 만들기 위해 데이터를 전처리
  • 벡터화
    • 신경망에서 모든 입력과 타깃은 부동 소수 데이터로 이뤄진 텐서여야 함(특정 경우에는 정수로 이뤄진 텐서)
    • 데이터가 사운드 이미지 텍스트 뭐든 일단 텐서로 변환
  • 값 정규화
    • (MNIST) 숫자 이미지를 그레이스케일 인코딩인 0~255 사이의 정수로 인코딩. 이를 네트워크에 주입하기 전 float32 타입으로 변경하고 255로 나눠 최종적으로 0~1 사이의 부동 소수 값으로 만듦.
    • (보스턴 집값) 데이터를 네트워크에 주입하기 전 각 특성을 정규화해 평균 0, 표준편차 1이 되도록 만듦
    • 비교적 큰 값이나 균일하지 않은 데이터를 신경망에 주입하는건 위험(업데이트할 그래디언트가 커져 네트워크가 수렴하는걸 방해함)
    • 대부분 값이 0~1 사이, 모든 특성이 대체로 비슷한 범위를 가질수록 네트워크를 쉽게 학습시킬 수있음
    • 도움이 되는 정규화 방법
x -= x.mean(axix=0)
x /= x.std(axis=0)
  • 누락값 처리
    • 일반적으로 0이 사전에 정의된 의미 있는 값이 아니라면 누락값을 0으로 처리해도 괜찮음
    • 트레이닝셋에는 누락값이 없는데 테스트셋에 누락값이 있을 가능성이 있다면 트레이닝셋에 고의적으로 누락값이 있는 샘플을 만들어야 함

특성 공학

  • 모델이 수월하게 작업할 수 있는 어떤 방식으로 데이터가 표현될 필요

과대적합과 과소적합

  • 언더피팅
    • 훈련 데이터의 손실이 낮아질수록 테스트 데이터의 손실도 낮아짐
    • 모델의 성능이 계속 발전될 여지가 있음
  • 오버피팅
    • 어느 시점부터 일반화 성능이 더 높아지지 않음
    • 검증 세트의 성능이 멈추고 감소하기 시작
    • 훈련 데이터에 특화된 패턴을 학습하기 시작했다는 의미
  • regularization
    • 과대적합을 피하는 저리 과정

네트워크 크기 축소

  • 오버피팅을 막는 가장 단순한 방법은 모델에 있는 학습 파라미터의 수를 줄이는 것
  • 파라미터의 수(모델의 용량)는 층의 수와 각 층의 유닛 수에 의해 결정
  • 언더피팅되지 않도록 충분한 파라미터를 가진 모델을 사용해야 함.
  • 데이터에 알맞은 모델 크기를 찾으려면 각기 다른 구조를 평가해봐야 함
    • 비교적 적은 수의 층과 파라미터로 시작해 검증 손실이 감소되기 시작할 때까지 층이나 유닛 수를 늘리는게 일반적인 작업 흐름

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

# 작은 용량의 모델
model = models.Sequential()
model.add(layers.Dense(6, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(6, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

# 큰 용량의 모델
model = models.Sequential()
model.add(layers.Dense(1024, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(1024, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • 작은 네트워크가 기본 네트워크보다 더 나중에 과대적합되기 시작. 과대적합이 시작됐을 때 성능이 더 천천히 감소
  • 용량이 큰 네트워크는 빨리 과대적합이 시작돼 갈수록 더 심해짐. 검증손실도 불안정
  • 용량이 큰 네트워크일수록 빠르게 훈련 데이터를 모델링하지만 과대적합에 민감해짐(트레이닝과 테스트 손실 사이 차이 발생)

가중치 규제 추가

  • 오캄의 면도날 이론
    • 두 가지 설명이 있다면 더 적은 가정이 필요한 간단한 설명이 옳다는 이론
    • 신경망 학습모델에도 적용됨. 복잡한 모델이 간단한 모델보다 과대적합될 가능성이 높음
  • 간단한 모델이란 파라미터 값 분포의 엔트로피가 작은 모델(혹은 적은 수의 파라미터를 가진 모델)
  • 과대적합 완화법: 네트워크의 복잡도에 제한을 둬서 가중치가 작은 값을 가지도록 강제하는 것
  • 가중치 값의 분포가 더 균일해짐(가중치 규제) -> 네트워크의 손실 함수에 큰 가중치에 연관된 두 가지 형태의 비용을 추가함
    • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가됨(가중치의 L1 norm)
    • L2 규제(=가중치 감쇠, weight decay): 가중치의 제곱에 비례하는 비용이 추가됨(가중치의 L2 norm).
# 모델에 L2 가중치 추가하기
from keras import regularizers

model = models.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • l2(0.001)는 가중치 행렬의 모든 원소를 제곱하고 0.001을 곱해 네트워크 전체 손실에 더해진다는 의미. 이 페널티 항은 트레이닝에서만 추가됨
  • L2 규제를 사용한 모델이 사용하지 않은 모델보다 과대적합을 잘 견딤(에포크 반복에 따라 loss가 덜 오름)
  • L2 규제 대신 사용 가능한 옵션
    • L1 규제 regularizers.l1(0.001)
    • L1, L2 규제 병행 regularizers.l1_l2(l1=0.001, l2=0.001)

dropout 추가

  • 네트워크 층에 드랍아웃을 적용하면 트레이닝 동안 랜덤으로 층의 일부 출력 특성을 제외시킴(0으로..)
    • ex) 한 층이 트레이닝되는 동안 어떤 입력샘플에 대해 [0.2, 0.5, 1.3, 0.8, 1.1] 벡터를 출력한다고 가정하면, 일부가 무작위로 0이 됨([0, 0.5, 1.3, 0, 1.1]
  • 드랍아웃 비율은 0이 될 특성의 비율(대개 0.2~0.5 로 지정)
  • 테스트 단계에서는 드랍아웃이 일어나지 않는다

  • layer_output *= np.random.randint(0, high=2, size=layer_output.shape) : 트레이닝시 유닛의 출력 중 50%를 버림

  • 테스트 시 드랍아웃 비율로 출력을 낮춰야: layer_output *= 0.5
  • 드랍아웃이 과대적합을 줄이는 원리
    • 층의 출력값에 노이즈를 추가해 중요하지 않은 우연한 패턴을 깨뜨림
  • 케라스에선 층의 출력 바로 뒤에 Dropout 층을 추가해 네트워크에 드랍아웃 적용 가능
# IMDB 네트워크에 드랍아웃 추가
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

보편적인 머신러닝 작업 흐름

문제 정의와 데이터셋 수집

  • 무엇을 예측할 것인가
  • 입력 데이터는?
  • 어떤 종류의 문제인가? (이진분류 / 다중분류 / 스칼라 회귀 / 벡터회귀 / 다중 레이블 분류 / 군집 / 생성 / 강화학습)
  • 입력과 출력이 무엇인지

성공 지표 선택

  • 클래스 분포가 균일한 분류 문제
    • 정확도와 ROC AUC
  • 클래스 분폴가 균일하지 않은 문제
    • 정밀도와 재현율
  • 랭킹 문제나 다중 레이블 문제
    • 평균 정밀도

평가 방법 선택

  • 현재의 진척 상황 평가법
    • 홀드아웃 검증 세트 분리(데이터가 풍부할 때)
    • K-겹 교차 검증(샘플 수가 너무 적을 때)
    • 반복 K-겹 교차 검증(데이터가 적고 정확한 모델 평가 필요시)

데이터 준비

  • 머신러닝 모델을 심층 신경망이라 가정
    • 데이터는 텐서로 구성
    • 텐서에 있는 값은 일반적으로 작은 값으로 스케일 조정돼 있음 [-1, 1] or [0, 1]
    • 특성마다 범위가 다르면 정규화
    • 피처 엔지니어링

기본보다 나은 모델 훈련하기

  • 통계적 검정력을 달성하는게 목표
  • MNIST에서 통계적 검정력을 달성하려면 0.1보다 높은 정확도를 내는 모델이어야 함
  • 모델을 위해 고려할 세 가지
    • 마지막 층의 활성화 함수: 네트워크 출력에 필요한 제한을 가함. IMDB 분류에선 마지막 층에 시그모이드 함수 사용. 회귀에서는 마지막 층에 활성화 함수 사용 안함
    • 손실 함수: 풀려고 하는 문제의 종류에 적합해야. IMDB에선 binary_crossentropy, 회귀에선 mse.
    • 최적화 설정: 대부분의 경우 rmsprop과 기본 학습률 사용하는게 무난함
  • 손실함수는 미니 배치 데이터에서 계산 가능해야 하고 미분 가능해야 함
  • 문제 유형에 따른 마지막층 활성화 함수와 손실 함수 선택
    • 이진 분류
      • 시그모이드 / binary_crossentropy
    • 단일 레이블 다중 분류
      • 소프트맥스 / categorical_crossentropy
    • 다중 레이블 다중 분류
      • 시그모이드 / binary_crossentropy
    • 임의 값에 대한 회귀
      • 없음 / mse
    • 0과 1 가시 값에 대한 회귀
      • 시그모이드 / mse or binary_crossentropy

몸집 키우기: 과대적합 모델 구축

  • 머신러닝은 최적화와 일반화 사이의 줄다리기
    • 과소적합과 과대적합 사이
    • 과소용량과 과대용량 사이
  • 얼마나 큰 모델을 만들어야 할까? 일단 과대적합된 모델을 만들어본다
    1. 층을 추가
    2. 층의 크기를 키움
    3. 더 많은 에포크 동안 트레이닝
  • 훈련 손실과 검증 손실을 모니터링. 검증 데이터에서 모델 성능이 감소하기 시작했을 때 과대적합에 도달한 것

모델 규제와 하이퍼파라미터 튜닝

  • 드랍아웃 추가
  • 층을 추가하거나 제거
  • L1, L2 또는 둘 다를 추가해보기
  • 하이퍼파라미터를 바꿔보기(층의 유닛 수나 옵티마이저의 학습률 등)
  • 피처 엔지니어링