케라스 창시자에게 배우는 딥러닝/ 머신 러닝의 기본 요소

머신 러닝의 네 가지 분류

  • 앞서 다뤘던 세 종류의 머신 러닝 문제는 이진 분류, 다중 분류, 스칼라 회귀였다. 이들은 모두 지도 학습(supervised learning)의 예이다. 지도 학습의 목표는 훈련 데이터 입력과 타깃 사이의 관계를 학습하는 것이다.
  • 지도 학습은 머신 러닝 알고리즘의 일부일 뿐이고, 머신 러닝은 크게 4개의 커다란 범주로 나눈다.

지도 학습

  • 가장 흔한 경우로 (종종 사람이 레이블링한) 샘플 데이터가 주어지면 알고 있는 타깃(꼬리표(annotation))에 입력 데이터를 매핑하는 방법을 학습한다.
  • 요즘 각광받는 광학 문자 판독, 음성 인식, 이미지 분류, 언어 번역 같은 딥러닝의 거의 모든 애플리케이션이 일반적으로 이 범주에 속한다.
  • 지도 학습은 대부분 분류와 회귀로 구성되지만 다음과 같은 특이한 변종도 있다.
    • 시퀀스 생성(sequence generation): 사진이 주어지면 이를 설명하는 캡션을 생성한다. 시퀀스 생성은 이따금 일련의 분류 문제로 재구성할 수 있다.
    • 구문 트리(syntax tree) 예측: 문장이 주어지면 분해된 구문 트리를 예측한다.
    • 물체 감지(object detection) 사진이 주어지면 사진 안의 특정 물체 주위에 경계 상자(bounding box)를 그린다. 이는 분류 문제로 표현되거나 경계 상자의 좌표를 벡터 회귀로 예측하는 회귀와 분류가 결합된 문제로 표현할 수 있다.
    • 이미지 분할(image segmentation): 사진이 주어졌을 때 픽셀 단위로 특정 물체에 마스킹(masking)을 한다.

비지도 학습

  • 이 부류의 머신 러닝은 어떤 타깃도 사용하지 않고 입력 데이터에 대한 흥미로운 변환을 찾는다. 데이터 시각화, 데이터 압축, 데이터의 노이즈 제거 또는 데이터에 있는 상관관계를 더 잘 이해하기 위해 사용한다.
  • 비지도 학습(unsupervised learning)은 데이터 분석에서 빼 놓을 수 없는 요소이며, 종종 지도 학습 문제를 풀기 전에 데이터셋을 잘 이해하기 위해 필수적으로 거치는 단계이다.
  • 차원 축소(dimensionality reduction)와 군집(clustering)이 비지도 학습에서 잘 알려진 범주이다.

자기 지도 학습

  • 자기 지도 학습(self-supervised learning)은 지도 학습의 특별한 케이스지만 별도의 범주로 할 만큼 충분히 다르다.
    • 자기 지도 학습은 지도 학습이지만 사람이 만든 레이블을 사용하지 않는다. 즉 학습 과정에 사람이 개입하지 않는 지도 학습이라고 생각할 수 있다.
    • 레이블이 여전히 필요하지만 보통 경험적인 알고리즘(heuristic algorithm)을 사용해서 입력 데이터로부터 생성한다.
  • 예컨대 오토인코더(autoencoder)가 잘 알려진 자기 지도 학습의 예이다.
    • 여기서 생성된 타깃은 수정하지 않은 원본 입력이다. 같은 방식으로 지난 프레임이 주어졌을 때 비디오의 다음 프레임을 예측하는 것이나, 이전 단어가 주어졌을 때 다음 단어를 예측하는 것이 자기 지도 학습의 예이다. (이 경우 미래의 입력 데이터로부터 지도되기 때문에 시간에 따른 지도학습(temporally supervised learning)이다)
  • 지도 학습, 자기 지도 학습, 비지도 학습의 구분은 가끔 모호할 수 있다.
    • 이 범주들은 명확한 경계가 없고 연속적이다.
    • 자기 지도 학습은 학습 메커니즘과 애플리케이션 측면 중에 어디에 중점을 두는지에 따라 지도 학습 또는 비지도 학습으로 재해석 될 수 있다.

강화 학습

  • 강화 학습(reinforcement learning)은 구글 딥마인드가 아타리 게임 플레이를 학습하는데 성공적으로 적용하면서 최근 많은 관심을 받기 싲가했다.
    • 강화 학습에서 에이전트(agent)는 환경에 대한 정보를 받아 보상을 최대화하는 행동을 선택하도록 학습된다.
    • 예컨대 강화 학습으로 훈련된 신경망은 비디오 게임 화면을 입력으로 받고 게임 점수를 최대화하기 위한 게임 내 행동을 출력할 수 있다.
  • 현재 강화 학습은 대부분 연구 영역에 속해 있고 게임 이외의 실제적인 성공 사례는 아직 없다. 
    • 하지만 때가 되면 강화 학습이 실제 세상의 많은 애플리케이션을 대체할 것으로 기대하는데, 이런 애플리케이션의 예로는 자율 주행 자동차, 자원 관리, 교육 등이 있다.
  • Note) 분류와 회귀에서 사용하는 용어
    • 샘플 또는 입력: 모델에 주입될 하나의 데이터 포인트
    • 예측 또는 출력: 모델로부터 나오는 값
    • 타깃: 정답. 외부 데이터 소스에 근거하여 모델이 완벽하게 예측해야 하는 값
    • 예측 오차 또는 손실 값: 모델의 예측과 타깃 사이의 거리를 측정한 값
    • 클래스: 분류 문제에서 선택할 수 있는 가능한 레이블의 집합. 예컨대 고양이와 강아지 사진을 분류할 때 클래스는 ‘고양이’와 ‘강아지’ 2개이다.
    • 레이블: 분류 문제에서 클래스 할당의 구체적인 사례. 예컨대 사진 #1234에 ‘강아지’ 클래스가 들어 있다고 표시한다면 ‘강아지’는 사진 #1234의 레이블이 된다.
    • 참 값(ground-truth) 또는 꼬리표(annotation): 데이터셋에 대한 모든 타깃. 일반적으로 사람에 의해 수집된다.
    • 이진 분류: 각 입력 샘플이 2개의 배타적인 범주로 구분되는 분류 작업
    • 다중 분류: 각 입력 샘플이 2개 이상의 범주로 구분되는 분류 작업. 예컨대 손글씨 숫자 분류
    • 다중 레이블 분류: 각 입력 샘플이 여러 개의 레이블에 할당될 수 있는 분류 작업. 예컨대 하나의 이미지에 고양이와 강아지가 모두 들어 있을 때는 ‘고양이’ 레이블과 ‘강아지’ 레이블을 모두 할당해야 한다.
    • 스칼라 회귀: 타깃이 연속적인 스칼라 값인 작업. 주택 가격 예측이 그 예이다.
    • 벡터 회귀: 타깃이 연속적인 값의 집합인 작업. 예컨대 연속적인 값으로 이루어진 벡터이다. (이미지에 있는 경계 상자의 좌표 같은) 여러 개의 값에 대한 회귀를 한다면 벡터 회귀이다.
    • 미니 배치 또는 배치: 모델에 의해 동시에 처리되는 소량의 샘플 묶음(일반적으로 8개에서 128개 사이). 샘플 개수는 GPU의 메모리 할당이 용이하도록 2의 거듭제곱으로 하는 경우가 많다. 훈련할 때 미니 배치마다 한 번씩 모델의 가중치에 적용할 경사 하강법 업데이트 값을 계산한다.

머신 러닝 모델 평가

  • 3장에서 본 3개의 예제에서 데이터를 훈련 세트, 검증 세트, 테스트 세트로 나누었다. 훈련에 사용된 동일한 데이터로 모델을 평가하지 않은 이유는 과대적합 때문이다.
    • 훈련 데이터의 성능은 훈련이 진행될 수록 항상 증가되지만, 새로운 데이터에 대한 성능은 좋아지지 않는다. 또는 더 나빠진다.
  • 머신 러닝의 목표는 처음 본 데이터에서 잘 작동하는 일반화된 모델을 얻는 것이다. 여기서 과대적합은 주요 장애물이다.
    • 관측할 수 있는 것만 제어할 수 있으므로 모델의 일반화 성능에 대한 신뢰를 측정할 수 있는 방법이 아주 중요하다.
    • 다음 절에서 과대 적합을 완화하고 일반화를 최대화하기 위한 전략을 살펴보자.

훈련, 검증, 테스트 세트

  • 모델 평가의 핵심은 가용한 데이터를 항상 훈련, 검증, 테스트 3개의 세트로 나누는 것이다. 훈련 세트에서 모델을 훈련하고 검증 세트에서 모델을 평가한다. 모델을 출시할 준비가 되면 테스트 세트에서 최종적으로 딱 한 번 모델을 테스트한다.
  • 훈련 세트와 테스트 세트를 2개 사용하지 않는 이유는 모델을 개발할 때 항상 모델의 설정을 튜닝하기 때문이다.
    • 예컨대 층의 수나 층의 유닛 수를 선택한다. (이런 파라미터를 네트워크의 가중치와 구분하기 위해 하이퍼파라미터(hyperparameter)라고 부른다) 검증 세트에서 모델의 성능을 평가하여 이런 튜닝을 수행한다.
    • 본질적으로 이런 튜닝도 어떤 파라미터 공간에서 좋은 설정을 찾는 학습이다. 결국 검증 세트의 성능을 기반으로 모델의 설정을 튜닝하면 검증 세트로 모델을 직접 훈련하지 않더라도 빠르게 검증 세트에 과대적합될 수 있다.
  • 이 현상의 핵심은 정보 누설(information leak) 개념에 있다. 
    • 검증 세트의 모델 성능에 기반하여 모델의 하이퍼파라미터를 조정할 때마다 검증 데이터에 관한 정보가 모델로 새는 것이다.
    • 하나의 파라미터에 대해서 단 한번만 튜닝한다면 아주 적은 정보가 누설된다. 이런 검증 세트로는 모델을 평가할만하다.
    • 하지만 한 번 튜닝하고 나서 검증 세트에 평가한 결과를 가지고 다시 모델을 조정하는 과정을 여러 번 반복하면 검증 세트에 관한 정보를 모델에 아주 많이 노출시키게 된다.
    • 결국 검증 데이터에 맞추어 최적화했기 때문에 검증 데이터에 의도적으로 잘 수행되는 모델이 만들어진다.
    • 검증 데이터가 아니고 완전히 새로운 데이터에 대한 성능이 관심 대상이라면 모델을 평가하기 위해 이전에 본 적 없는 완전히 다른 데이터셋을 사용해야 한다. 바로 테스트 세트이다.
    • 모델은 간접적으로라도 테스트 세트에 대한 어떤 정보도 얻어서는 안된다. 테스트 세트 성능에 기초하여 튜닝한 모델의 모든 설정은 일반화 성능을 왜곡시킬 것이다.
  • 데이터를 훈련, 검증, 테스트 세트로 나누는 것은 간단해 보일 수 있지만, 데이터가 적을 때는 몇 가지 고급 기법을 사용하면 도움이 된다. 대표적인 세 가지 평가 방법인 단순 홀드아웃 검증(hold-out validation), K-겹 교차 검증(K-fold cross-validation), 셔플링(shuffling)을 사용한 K-겹 교차 검증(iterated K-fold cross-validation)을 살펴보겠다.

단순 홀드아웃 검증

  • 데이터의 일정량을 테스트 세트로 떼어 놓는다. 남은 데이터에서 훈련하고 테스트 세트로 평가한다.
    • 앞서 설명했듯이 정보 누설을 막기 위해 테스트 세트를 사용해서 모델을 튜닝해서는 안되기 때문에 검증 세트도 따로 떼어 놓아야 한다.
    • 홀드아웃 검증은 아래 그림과 같다.

num_validation_samples = 10000

np.random.shuffle(data)

validation_data = data[:num_validation_samples]
data = data[num_validation_samples:]

training_data = data[:]

# 훈련세트에서 모델을 훈련하고 검증 세트로 평가한다.
model = get_model()
model.train(training_data)
validation_score = model.evaluate(validation_data)

# 여기에서 모델을 튜닝하고
# 다시 훈련하고 평가하고, 다시 튜닝하고...

# 하이퍼파라미터 튜닝이 끝나면 테스트 데이터를 제외한 모든 데이터를 사용하여 모델을 다시 훈련시킨다.
model = get_model()
model.train(np.concatenate([training_data, validation_data]))
test_score = model.evaluate(test_data)
  • 이 평가 방법은 단순해서 한 가지 단점이 있는데, 데이터가 적을 때는 검증 세트와 테스트 세트의 샘플이 너무 적어 주어진 전체 데이터를 통계적으로 대표하지 못할 수 있다.
    • 이를 쉽게 확인하는 방법은 다른 난수 초깃값으로 셔플링해서 데이터를 나누었을 때 모델의 성능이 매우 달라지는지를 확인하는 것이다.
    • 이 문제는 이어서 설명할 K-겹 교차 검증과 반복 K-겹 교차 검증이 해결할 수 있다.

K-겹 교차 검증

  • 이 방식에서는 데이터를 동일한 크기를 가진 K개 분할로 나눈 후, 각 분할 i에 대해 남은 K-1개의 분할로 모델을 훈련하고 분할 i에서 모델을 평가한다. 최종 점수는 이렇게 얻은 K개의 점수를 평균한다.
    • 이 방법은 모델의 성능이 데이터 분할에 따라 편차가 클 때 도움이 된다.
    • 홀드아웃 검증처럼 모델의 튜닝에 별개의 검증 세트를 사용하게 된다.

k = 4
num_validation_samples = len(data) // k

np.random.shuffle(data)

validation_scores = []

for fold in range(k):
# 검증 데이터 부분을 선택한다.
validation_data = data[num_validation_samples * fold: num_validation_samples * (fold+1)]
# 남은 데이터를 훈련 데이터로 사용한다.
training_data = data[:num_validation_samples * fold] + data[num_validation_samples * (fold+1):]

# 훈련되지 않은 새로운 모델을 만든다.
model = get_model()
model.train(training_data)
validation_score = model.evaluate(validation_data)
validation_scores.append(validation_score)

# 검증 점수: K개 폴드의 검증 점수 평균
validation_score = np.average(validation_scores)

# 테스트 데이터를 제외한 전체 데이터로 최종 모델을 훈련한다.
model = get_model()
model.train(data)
test_score = model.evaluate(test_data)

셔플링을 사용한 반복 K-겹 교차 검증

  • 이 방법은 비교적 가용 데이터가 적고 가능한 정확하게 모델을 평가하고자 할 때 사용한다. 캐글 경연에서는 이 방법이 아주 크게 도움이 된다.
    • 이 방법은 K-겹 교차 검증을 여러 번 적용하되 K개의 분할로 나누기 전에 매번 데이터를 무작위로 섞는다.
    • 최종 점수는 모든 K-겹 교차 검증을 실행해서 얻은 점수의 평균이 된다.
    • 결국 P x K개(P는 반복 횟수)의 모델을 훈련하고 평가하므로 비용이 매우 많이 든다.

기억해야 할 것

  • 평가 방식을 선택할 때 다음 사항을 유념해야 한다.
    • 대표성 있는 데이터
      • 훈련 세트와 테스트 세트가 주어진 데이터에 대한 대표성이 있어야 한다. 숫자 이미지 분류하는 문제에서 80:20으로 훈련과 테스트 세트를 만들면 훈련 세트에는 0-7까지의 숫자가 있고 테스트 세트에는 8-9 숫자가 담기게 된다. 이런 어처구니 없는 실수는 놀랍게도 자주 일어난다.
    • 시간의 방향
      • 과거로부터 미래를 예측하고자 한다면 데이터를 분할하기 전에 무작위로 섞어서는 안된다. 이러면 미래의 정보가 누설되기 때문이다. 즉 모델이 사실상 미래 데이터에서 훈련될 것이다.
    • 데이터 중복
      • 한 데이터셋에서 어떤 데이터 포인트가 두 번 등장하면 데이터를 섞고 훈련 세트와 검증 세트로 나누었을 때 훈련 세트와 검증 세트에 데이터 포인트가 중복될 수 있다. 이로 인해 훈련 데이터의 일부로 테스트하는 최악의 경우가 발생한다.

데이터 전처리, 특성 공학, 특성 학습

  • 많은 데이터 전처리와 특성 공학 기법은 특정 도메인에 종속적이기 때문에, 여기서는 모든 종류의 데이터에 공통되는 기본 사항을 살펴보겠다.

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

  • 데이터 전처리 목적은 주어진 원본 데이터를 신경망에 적용하기 쉽도록 만드는 것이다. 벡터화(vectorization), 정규화(normalization), 누락된 값 다루기, 특성 추출 등이 포함된다.

벡터화

  • 신경망에서 모든 입력과 타깃은 부동 소수 데이터로 이루어진 텐서여야 한다. (때로는 정수로 된 텐서)
  • 사운드, 이미지, 텍스트 등 처리해야 할 것이 무엇이든 먼저 텐서로 변환해야 하는데, 이 단계를 데이터 벡터화(data vectorization)이라고 한다.

값 정규화

  • 숫자 이미지 분류 예에서 이미지 데이터를 그레이스케일 인코딩인 0-255 사이의 정수로 인코딩했는데, 이 데이터를 네트워크에 주입하기 전에 float32 타입으로 변경하고 255로 나누어서 0-1사이의 부동 소수 값으로 만들었다.
  • 주택 가격을 예측할 때는 특성들의 범위가 제각각이었기 때문에, 이 데이터를 네트워크에 주입하기 전에 각 특성을 독립적으로 정규화하여 평균이 0이고 표준 편차가 1이 되도록 만들었다.
  • 일반적으로 비교적 큰 값이나 균일하지 않은 데이터를 신경망에 주입하는 것은 위험한데, 업데이트할 그래디언트가 커져 네트워크가 수렴하는 것을 방해하기 때문이다.
  • 네트워크가 쉽게 학습시키려면 데이터가 다음 특징을 따라야 한다.
    • 작은 값을 취한다. 일반적으로 대부분의 값이 0-1 사이여야 한다.
      • (0-1사이의 값은 계속 곱하면 수렴하는 이유도 있을 듯. 1이 넘으면 발산할 가능성이 높다)
    • 균일해야 한다. 즉 모든 특성이 대체로 비슷한 범위를 가져야 한다.
  • 추가적으로 다음에 나오는 엄격한 정규화 방법은 꼭 필수적이지는 않지만 자주 사용되고 도움이 될 수 있다.
    • 각 특성별로 평균이 0이 되도록 정규화한다.
    • 각 특성별로 표준 편차가 1이 되도록 정규화한다.
    • (이말은 데이터가 scale-free해서 평균과 표준편차 정규화가 안되는 데이터는 다룰 수 없다는 뜻인가?)
  • 넘파이 배열에서는 다음처럼 간단히 할 수 있다.
x -= x.mean(axis=0)
x /= x.std(axis=0)

누락된 값 다루기

  • 이따금 데이터에 값이 누락된 경우가 있다.
    • 예컨대 주택 가격 예측 문제에서 첫 번째 특성은 1인당 범죄율인데, 이 특성이 모든 샘플에 들어 있지 않은 경우 어떻게 될까? 훈련 데이터나 테스트 데이터에 누락된 값이 포함된다.
    • 일반적으로 신경망에서 0이 사전에 정의된 의미 있는 값이 아니라면 누락된 값을 0으로 입력해도 괜찮다. 네트워크가 0이 누락된 데이터를 의미한다는 것을 학습하면 이 값을 무시하기 싲가할 것이다.
  • 테스트 데이터에 누락된 값이 포함될 가능성이 있다고 가정하자. 하지만 네트워크가 누락된 값이 없는 데이터에서 훈련되었다면 이 네트워크는 누락된 값을 무시하는 법을 알지 못한다.
    • 이런 경우에는 누락된 값이 있는 훈련 샘플을 고의적으로 만들어야 한다. 훈련 샘플의 일부를 여러벌 복사해서 테스트 데이터에서 빠질 것 같은 특성을 제거한다.

특성 공학

  • 특성 공학은 데이터와 머신 러닝 알고리즘(여기서는 신경망)에 관한 지식을 사용하는 단계이다.
    • 모델에 데이터를 주입하기 전에 (학습이 아닌) 하드코딩된 변환을 적용하여 알고리즘이 더 잘 수행되도록 만들어준다.
    • 많은 경우에 머신 러닝 모델이 임의의 데이터로부터 완벽한 학습을 한다고 기대하기는 어렵다. 모델이 수월하게 작업할 수 있는 어떤 방식으로 데이터가 표현될 필요가 있다.
  • 예컨대 시계 이미지를 입력으로 받고 하루의 시간을 출력하는 모델을 개발한다고 가정하자. 
    • 이미지의 원본 픽셀을 입력으로 사용한다면 어려운 머신 러닝 문제가될 것이다.

  • 고수준에서 이 문제를 이해하고 있다면(우리는 시계에서 시간을 읽는 방법을 알고 있다) 머신 러닝 알고리즘을 위해 훨씬 더 좋은 입력 특성을 만들 수 있다.
    • 예컨대 시계 바늘의 검은색 픽셀을 따라 각 바늘 끝의 (x, y) 좌표를 출력하는 간단한 파이썬 스크립트를 만든다. 그 다음 머신 러닝 알고리즘이 이 좌표와 적절한 시각을 연결하도록 쉽게 학습될 수 있다.
    • 이보다 더 좋은 특성을 만들 수도 있는데, 좌표를 바꾸어 (x, y) 포인트를 이미지 중심에 대한 극좌표로 나타낼 수 있다. 이제 각 시계 바늘의 각도가 입력이 된다.
    • 이렇게 특성을 준비하면 문제가 너무 쉬워져서 머신 러닝이 전혀 필요하지 않다. 간단한 반올림 연산과 딕셔너리 참조만으로 하루의 시간을 추정하기 충분하다.
  • 이것이 특성 공학의 핵심이다. 특성을 더 간단한 방식으로 표현하여 문제를 쉽게 만든다. 일반적으로 해당 문제를 아주 잘 이해하고 있어야 한다.
  • 딥러닝 이전에는 특성 공학이 아주 중요했다. 전통적인 얕은 학습 방법의 알고리즘들은 스스로 유용한 특성을 학습할 만큼 충분히 넓은 가설 공간을 가지고 있지 않다. 알고리즘에 데이터를 표현하는 방식에 성공 여부가 달려있다.
    • 예컨대 합성곱 신경망이 MNIST 숫자 이미지 분류 문제를 해결하기 전까지 전형적인 해결책은 하드코딩된 특성을 사용하는 것이었다.
    • 숫자 이미지에 있는 동신원의 수, 이미지에 있는 숫자의 높이, 픽셀 값의 히스토그램(histogram) 등이다.
  • 다행히 최근 딥러닝은 대부분 특성 공학이 필요하지 않다. 신경망이 자동으로 원본 데이터에서 유용한 특성을 추출할 수 있기 때문이다. 그렇다면 심층 신경망을 사용할 때 특성 공학에 대해 신경 쓰지 않아도 될것인가 하면 그것은 두 가지 이유로 그렇지 않다.
    • 좋은 특성은 적은 자원을 사용하여 문제를 더 멋지게 풀어낼 수 있다. 예컨대 시계 바늘을 읽는 문제에 합성곱 신경망을 사용하는 것은 어울리지 않다.
    • 좋은 특성은 더 적은 데이터로 문제를 풀 수 있다. 딥러닝 모델이 스스로 특성을 학습하는 능력은 가용한 훈련 데이터가 많을 때 발휘된다. 샘플의 개수가 적다면 특성에 있는 정보가 매우 중요해진다.

과대적합과 과소적합

  • 머신 러닝의 근본적인 이슈는 최적화와 일반화 사이의 줄다리기이다. 최적화(optimization)는 가능한 훈련 데이터에서 최고의 성능을 얻으려고 모델을 조정하는 과정이고, 일반화(generalization)은 훈련된 모델이 이전에 본 적 없는 데이터에서 얼마나 잘 수행되는지를 의미한다.
  • 훈련 초기에 최적화와 일반화는 상호 연관되어 있다. 훈련 데이터의 손실이 낮아질수록 테스트 데이터의 손실도 낮아진다. 이런 상황이 발생할 때 모델이 과소적합(underfitting)되었다고 말한다.
    • 이때는 모델의 성능이 계속 발전될 여지가 있다. 즉, 네트워크가 훈련 데이터에 있는 관련 특성을 모두 학습하지 못한 상태다.
  • 훈련 데이터에 여러 번 반복 학습하고 나면 어느 시점부터 일반화 성능이 더 높아지지 않고, 검증 세트의 성능이 멈추고 감소되기 시작한다. 즉 모델이 과대적합되기 시작한다.
    • 이는 훈련 데이터에 특화된 패턴을 학습하기 시작했다는 의미이다. 이 패턴은 새로운 데이터와 관련성이 적기 때문에 잘못된 판단을 하게 만든다.
  • 모델이 관련성이 없고 좋지 못한 패턴을 훈련 데이터에서 학습하지 못하도록 하려면 가장 좋은 방법은 더 많은 훈련 데이터를 모으는 것이다.
    • 더 많은 데이터에서 훈련된 모델은 자연히 일반화 성능이 더 뛰어나다.
    • 데이터를 더 모으는 것이 불가능할 때 차선책은 모델이 수용할 수 있는 정보의 양을 조절하거나 저장할 수 있는 정보에 제약을 가하는 것이다.
    • 네트워크가 적은 수의 패턴만 기억할 수 있다면 최적화 과정에서 가장 중요한 패턴에 집중하게 될 것이다. 이런 패턴은 더 나은 일반화 성능을 제공할 수 있다.
  • 이런 식으로 과대적합을 피하는 처리 과정을 규제(regularization)이라고 한다.

네트워크 크기 축소

  • 과대적합을 막는 가장 단순한 방법은 모델의 크기, 즉 모델에 있는 학습 파라미터의 수를 줄이는 것이다.
    • 파라미터의 수는 층의 수와 각 층의 유닛 수에 의해 결정된다. 딥러닝에서 모델에 있는 학습 파라미터의 수를 종종 모델의 용량(capacity)라고 한다.
    • 당연하게도 파라미터가 많은 모델이 기억 용량이 더 많다. 훈련 샘플과 타깃 사이를 딕셔너리 같은 일대일 매핑으로 완벽하게 학습할 수도 있다. 이런 매핑은 일반화 능력이 없다.
    • 항상 유념해야 할 것은 딥러닝 모델은 훈련 데이터에 잘 맞추려는 경향이 있다는 점이다. 하지만 진짜 문제는 최적화가 아니고 일반화이다.
  • 다른 한편으로 네트워크가 기억 용량에 제한이 있다면 이런 매핑을 쉽게 학습하지 못할 것이다. 따라서 손실을 최소화하기 위해 타깃에 대한 예측 성능을 가진 압축된 표현을 학습해야 한다. 정확히 이런 표현이 우리 관심 대상이다.
    • 동시에 기억해야 할 것은 과소적합되지 않도록 충분한 파라미터를 가진 모델을 사용해야 한다는 점이다. 모델의 기억 용량이 부족해서는 안 된다.
  • 너무 많은 용량과 충분하지 않은 용량 사이의 절충점을 찾아야 한다. 하지만 알맞은 층의 수나 각 층의 유닛 수를 결정할 수 있는 마법 같은 공식은 없다.
    • 데이터에 알맞은 모델 크기를 찾으려면 각기 다른 구조를 평가해 보아야 한다.
    • 적절한 모델 크기를 찾는 일반적인 작업 흐름은 비교적 적은 수의 층과 파라미터로 싲가한다. 그 다음 검증 손실이 감소되기 시작할 때까지 층이나 유닛의 수를 늘리는 것이다.
  • 영화 리뷰 분류 모델을 예로 들어보자. 원래 네트워크는 다음과 같다.
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(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(1024, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(1024, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • 아래 그림은 더 큰 네트워크가 기본 네트워크에 비해 얼마나 차이 나는지 보여준다.

  • 욜량이 큰 네트워크는 첫 번째 에포크 이후 거의 바로 과대적합이 시작되어 갈수록 더 심해진다. 검증 손실도 매우 불안정해진다.
  • 한편 아래 그림은 두 네트워크의 훈련 손실을 보여준다. 여기서 볼 수 있듯이 용량이 큰 네트워크는 훈련 손실이 매우 빠르게 0에 가까워진다.
    • 용량이 많은 네트워크일수록 더 빠르게 훈련 데이터를 모델링할 수 있다.(결국 훈련 손실이 낮아진다)
    • 하지만 더욱 과대적합에 민감해진다. (결국 훈련과 검증 손실 사이에 큰 차이가 발생한다.)

가중치 규제 추가

  • 오캄의 면도날(Occam’s razor)에 따르면 더 적은 가정이 필요한 간단한 설명이 옳다. 이는 신경망으로 학습되는 모델에도 적용된다.
    • 어떤 훈련 데이터와 네트워크 구조가 주어졌을 때 데이터를 설명할 수 있는 가중치 값의 집합은 여러 개이다. 간단한 모델이 복잡한 모델보다 덜 과대적합될 가능성이 높다.
  • 여기에서 간단한 모델은 파라미터 값 분포의 엔트로피가 작은 모델이다. 그러므로 과대적합을 완화하기 위한 일반적인 방법은 네트워크 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 강제하는 것이다.
    • 가중치 값의 분포가 더 균일하게 된다. 이를 가중치 규제(weight regularization)이라고 하며, 네트워크의 손실 함수에 큰 가중치에 연관된 비용을 추가한다. 두가지 형태의 비용이 있다.
    • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가된다. (가중치의 L1 노름(norm))
    •  L2 규제: 가중치의 제곱에 비례하는 비용이 추가된다. (가중치의 L2 노름). L2 규제는 신경망에서 가중치 감쇠(weight decay)라고 한다. 가중치 감쇠는 수학적으로 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을 곱하여 네트워크 전체 손실에 더해진다는 의미이다. 이 페널티(penalty) 항은 훈련할 때만 추가된다. 이 네트워크의 손실은 테스트보다 훈련할 때 더 높을 것이다.
  • 아래 그림은 L2 규제 페널티의 효과를 보여준다. 여기서 볼 수 있듯이 두 모델이 동일한 파라미터 수를 가지고 있더라도 L2 규제를 사용한 모델이 기본 모델보다 훨씬 더 과대적합에 잘 견디고 있다.

  • 케라스에서는 L2 규제 대신 다음 가중치 규제 중 하나를 사용할 수 있다.
from keras import regularizers

regularizers.l1(0.001) -- L1 규제
regularizers.l1_l2(l1=0.001, l2=0.001) -- L1과 L2 규제 병행

드롭아웃 추가

  • 드롭아웃(dropout)은 토론토 대학의 제프리 힌튼과 그의 학생들이 개발한 것으로 신경망을 위해 사용되는 규제 기법 중 가장 효과적이고 널리 사용되는 방법 중 하나이다.
    • 네트워크 층에 드롭아웃을 적용하면 훈련하는 동안 무작위로 층의 일부 출력 특성을 제외시킨다(0으로 만든다)
  • 한 층이 정상적으로 훈련하는 동안에는 어떤 입력 샘플에 대해 [0.2, 0.5, 1.3, 0.8, 1.1] 벡터를 출력한다고 가정해보자. 드롭아웃을 적용하면 이 벡터의 일부가 무작위로 0으로 바뀐다.
    • 드롭아웃의 비율은 0이 될 특성의 비율이다. 보통 0.2-0.5 사이로 지정된다.
    • 테스트 단계에서는 어떤 유닛도 드롭아웃되지 않는다. 그 대신 층의 출력을 드롭아웃 비율에 비례해서 줄여준다. 훈련할 때보다 더 많은 유닛이 활성화 되기 때문이다.
  • 크기가 (batch_size, features)인 한 층의 출력을 담고 있는 넘파이 행렬을 생각해보자. 훈련할 때는 이 행렬 값의 일부가 랜덤하게 0이 된다.
# 훈련할 때 유닛의 출력 중 50%를 버린다.
layer_output += np.random.randint(0, high=2, size=layer_output.shape)
  • 테스트할 때는 드롭아웃 비율로 출력을 낮추어 주어야 한다. 여기서 0.5배만큼 스케일을 조정했다. (앞에서 절반의 유닛을 드롭아웃 했으므로)
layer_output *= 0.5  --테스트 단계
  • 훈련 단계에 이 두 연산을 포함시켜 테스트 단계에는 출력을 그대로 두도록 구현할 수 있다. 실제로 종종 이런 방식으로 구현한다.
layer_output += np.random.randint(0, high=2, size=layer_output.shape) --훈련 단계
layer_output /= 0.5 --여기서는 스케일을 낮추는 대신 높인다.

  • 이 기법이 이상하고 무계획적으로 보일 수 있다. 왜 드롭아웃이 과대적합을 줄이는데 도움이 될까?
    • 힌튼은 은행에서 사용하는 부정 방비 메커니즘에서 착안했다고 한다. 
    • “은행에 갔을 때 행원들이 계속 바뀌길래 왜 그런지 물었더니, ‘자신들도 이유는 모르지만 자주 업무가 바뀐다’고 했다. 나는 은행에서 부정 행위를 하려면 직원들 사이의 유대가 필요하기 때문이라고 판단했다. 각 샘플에 대해 뉴런의 일부를 무작위하게 제거하면 뉴런의 부정한 협업을 방지하고 결국 과대적합을 감소시킨다는 것을 깨달았다.”
    • 핵심 아이디어는 층의 출력 값에 노이즈를 추가하여 중요하지 않은 우연한 패턴(힌튼이 말한 부정한 협업)을 깨드리는 것이다.
    • 노이즈가 없다면 네트워크가 이 패턴을 기억하기 시작할 것이다.
  • 케라스에서는 층의 바로 뒤에 Dropout 층을 추가하여 네트워크에 드롭아웃을 적용할 수 있다.
model.add(layers.Dropout(0.5))
  • IMDB 네트워크에 2개의 Dropout 층을 추가하고 과대적합을 얼마나 줄여주는지 확인해 보자.
model = models.Sequential()
model.add(layers.Dense(1024, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1024, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))
  • 아래는 결과 그래프이다. 여기서도 기본 네트워크보다 확실히 더 향상된 것을 볼 수 있다.

  • 정리하면 신경망에서 과대적합을 방지하기 위해 가장 널리 사용되는 방법은 다음과 같다.
    • 훈련 데이터를 더 모은다.
    • 네트워크의 용량을 감소시킨다.
    • 가중치 규제를 추가한다.
    • 드롭아웃을 추가한다.

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

문제 정의와 데이터셋 수집

  • 먼저 주어진 문제를 정의해야 한다.
    • 입력데이터는 무엇이고 어떤 것을 예측하려고 하고 가용한 훈련 데이터가 있어야 하고 등
    • 당면한 문제가 어떤 종류인지, 이진분류인지 다중 분류인지 스칼라 회귀인지 벡터 회귀인지, 다중 레이블 다중 분류인지, 군집, 생성 또는 강화 학습 문제인지
  • 입력과 출력이 무엇인지와 어떤 데이터를 사용할 것인지 알기 전까지는 다음 단계로 넘어갈 수 없다. 이 단계에서 가설을 세워야 한다.
    • 주어진 입력으로 출력을 예측할 수 있다고 가설을 세운다.
    • 가용한 데이터에 입력과 출력 사이의 관계를 학습하는데 충분한 정보가 있다고 가설을 세운다.
  • 모델이 작동하기 전까지 이는 가설에 불과하기 때문에 검증될지 아닐지 기다려 보아야 한다. 모든 문제가 해결되지는 않는다.
  • 풀기 어려운 종류의 문제는 시간에 따라 변하는 문제(nonstationary problem)이다. 
    • 옷을 위한 추천 엔진을 예로 들어보자. 8월 데이터로 훈련하고 겨울에 이 엔진을 사용하려고 하면 결과가 좋게 나올 수가 없다.
  • 머신 러닝은 훈련 데이터에 있는 패턴을 기억하기 위해서만 사용한다는 것을 기억하라. 이미 보았던 것만 인식할 수 있다. 미래를 예측하기 위해 과거 데이터에서 훈련한 머신 러닝을 사용하는 것은 미래가 과거처럼 움직인다고 가정한 것이다. 사실 대부분 그렇지 않다.

성공 지표 선택

  • 어떤 것을 제어하려면 관측할 수 있어야 한다. 성공 하기 위해서는 성공은 무엇인가를 정의해야 한다. 
    • 정확도, 정밀도, 재현율, 재방문율 등 성공의 지표가 모델이 최적화할 손실 함수를 선택하는 기준이 된다. 비즈니스 성공처럼 고수준의 목표와 직접적으로 연결되어 있어야 한다.
  • 클래스 분포가 균일한 분류 문제에서는 정확도와 ROC AUC가 일반적인 지표이다.
    • 클래스 분포가 균일하지 않은 문제에서는 정밀도와 재현율을 사용할 수 있다.
    • 랭킹 문제나 다중 레이블 문제에는 평균 정밀도를 사용할 수 있다.
    • 성공을 측정하기 위해 자신만의 지표를 정의하는 일은 일반적이지 않다.

평가 방법 선택

  • 목표를 정했다면 현재의 진척 상황을 평가할 방법을 정의해야 한다. 
    • 홀드아웃 검증 세트 분리: 데이터가 풍부할 때 사용한다.
    • K-겹 교차 검증: 홀드아웃 검증을 사용하기에 샘플의 수가 너무 적을 때 사용한다.
    • 반복 K-겹 교차 검증: 데이터가 적고 매우 정확한 모델 평가가 필요할 때 사용한다.
  • 위 3가지 중 하나를 선택하면 된다. 대부분의 경우 첫 번째로 충분할 것이다.

데이터 준비

  • 무엇을 훈련할지와 무엇을 최적화할지, 그리고 어떻게 평가할지를 정했다면 거의 모델을 훈련 시킬 준비가 된 것이다. 하지만 머신 러닝 모델에 주입할 데이터를 구성해야 한다. 여기에서는 이 머신 로닝 모델을 심층 신경망이라고 가정한다.
    • 데이터는 텐서로 구성된다.
    • 이 텐서에 있는 값은 일반적으로 작은 값으로 스케일 조정되어 있다. 예컨대 [-1, 1] 이나 [0, 1] 범위이다.
    • 특성마다 범위가 다르면 (여러 종류의 값으로 이루어진 데이터라면) 정규화해야 한다.
    • 특성 공학을 수행할 수 있다. 특히 데이터가 적을 때이다.
  • 입력 데이터와 타깃 데이터의 텐서가 준비되면 모델을 훈련시킬 수 있다.

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

  • 이 단계의 목표는 통계적 검정력(statistical power)을 달성하는 것이다. 즉 아주 단순한 모델보다 나은 수준의 작은 모델을 개발한다.
    • MNIST 숫자 이미지 분류 예에서는 0.1보다 높은 정확도를 내는 모델이 통계적 검정력을 가졌다고 말할 수 있다.
    • IMDB 에서는 0.5보다 높은 정확도를 갖는 것이다.
  • 통계적 검정력을 달성하는 것이 항상 가능하지는 않다. 여러 개의 타당성이 있는 네트워크 구조를 시도해 보고 무작위로 예측하는 모델보다 낫지 않다면 입력 데이터에 존재하지 않는 것을 얻으려고 한다는 신호이다. 2개의 가설이 있다는 것을 기억하라.
    • 주어진 입력으로 출력을 예측할 수 있다고 가설을 세운다.
    • 가용한 데이터에 입력과 출력 사이의 관계를 학습하는데 충분한 정보가 있다고 가설을 세운다.
  • 이 가설이 잘못된 것일 수 있다. 이때는 기획부터 다시 해야 한다.
  • 일이 잘 진행된다고 가정하면 첫 번째 모델을 만들기 위해 세 가지 중요한 선택을 해야 한다.
    • 마지막 층의 활성화 함수: 네트워크의 출력에 필요한 제한을 가한다. 예컨대 IMDB 분류 예는 마지막 층에 시그모이드 함수를 사용한다. 회귀 예에서는 마지막 층에 활성화 함수를 사용하지 않는다.
    • 손실 함수: 풀려고 하는 문제의 종류에 적합해야 한다. 예컨대 IMDB 예제는 binary_crossentropy를 사용하고, 회귀 예제는 mse를 사용하는 식이다.
    • 최적화 설정: 어떤 옵티마이저를 사용하는지? 학습률은 얼마인지? 대부분의 경우 rmsprop과 기본 학습률을 사용하는 것이 무난하다.
  • 손실 함수의 선택에 대해서 언급할 것은 주어진 문제의 성공 지표를 직접 최적화하는 것이 항상 가능하지는 않다는 점이다. 때로는 이 지표를 손실 함수로 바꿀 수 있는 방법이 없다.
    • 무엇보다도 손실 함수는 주어진 미니 배치 데이터에서 계산 가능해야 하고,(이상적으로 손실 함수는 하나의 데이터 포인트에서 계산 가능해야 한다) 미분 가능해야 한다.(그렇지 않으면 역전파 알고리즘을 사용하여 네트워크를 훈련시킬 수 없다.)
    • 예컨대 널리 사용되는 분류 지표인 ROC AUC는 직접 최적화될 수 없다. 그래서 분류 작업에는 크로스엔트로피처럼 ROC AUC를 대신할 지표를 최적화하는 것이 보통이다. 일반적으로 크로스엔트로피가 낮을수록 ROC AUC가 높다고 기대할 수 있다.
  • 다음 표는 자주 등장하는 문제 유형에 따라 선택할 수 있는 마지막 츠으이 활성화 함수와 손실 함수이다.
문제 유형 마지막 층의 활성화 함수 손실 함수
이진 분류 시그모이드 binary_crossentropy
단일 레이블 다중 분류 소프트맥스 categorical_crossentropy
다중 레이블 다중 분류 시그모이드 binary_crossentropy
임의 값에 대한 회귀 없음 mse
0과 1 사이 값에 대한 회귀 시그모이드 mse 또는 binary_crossentropy

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

  • 통계적 검정력을 가진 모델을 얻었다면 이제 이 모델이 충분히 성능을 내는지 질문해 보아야 한다. 주어진 문제를 적절히 모델링하기에 충분한 층과 파라미터가 있는가?
    • 예컨대 2개의 유닛을 가진 하나의 은닉 층으로 구성된 네트워크가 있다고 가정하자. 이 네트워크가 MNIST 데이터셋에서 통계적 검정력을 가질 수 있지만 문제를 잘 해결하기에는 충분하지 않을 것이다.
    • 머신러닝은 최적화와 일반화 사이의 줄다리기라는 점을 기억하라. 과소적합과 과대적합 사이, 즉 과소용량과 과대용량의 경계에 적절히 위치한 모델이 이상적이다.
    • 이 경계가 어디에 위치하는지 찾기 위해서는 먼저 지나쳐 보아야 한다.
  • 얼마나 큰 모델을 만들어야 하는지 알기 위해서는 과대적합된 모델을 만들어야 한다. 이는 아주 쉽다.
    • 층을 추가한다.
    • 층의 크기를 키운다.
    • 더 많은 에포크 동안 훈련한다.
  • 관심 대상인 훈련과 검증 지표는 물론 항상 훈련 손실과 검증 손실을 모니터링하라. 검증 데이터에서 모델 성능이 감소하기 시작했을 때 과대적합에 도달한 것이다.
  • 다음 단계에서 규제와 모델 튜닝을 싲가하여 과소적합도 아니고 과대적합도 아닌 이상적인 모델에 가능한 가깝도록 만든다.

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

  • 이 단계가 대부분의 시간을 차지한다. 반복적으로 모델을 수정하고 훈련하고 검증 데이터에서 평가한다 (이때 테스트 데이터를 사용하지 않는다). 그리고 다시 수정하고 가능한 좋은 모델을 얻을 때까지 반복한다. 적용해 볼 것은 다음과 같다.
    • 드롭아웃을 추가한다.
    • 층을 추가하거나 제거해서 다른 구조를 시도해 본다.
    • L1이나 L2 또는 두 가지 모두 추가한다.
    • 최적의 설정을 찾기 위해 하이퍼파라미터를 바꾸어 시도해 본다. (층의 유닛 수나 옵티마이저의 학습률 등)
    • 선택적으로 특성 공학을 시도해 본다. 새로운 특성을 추가하거나 유용하지 않을 것 같은 특성을 제거한다.
  • 다음 사항을 유념하라. 검증 과정에서 얻은 피드백을 사용하여 모델을 튜닝할 때마다 검증 과정에 대한 정보를 모델에 누설하고 있다는 것이다.
    • 몇 번만 반복하는 것은 큰 문제가 되지 않지만, 많이 반복하게 되면 모델이 검증 데이터에서 전혀 훈련하지 않았음에도 결국 모델이 검증 과정에 과댖거합될 것이다. 이는 검증 과정의 신뢰도를 감소시킨다.
  • 만족할 만한 모델 설정을 얻었다면 가용한 모든 데이터(훈련 데이터와 검증 데이터)를 사용해서 제품에 투입할 최종 모델을 훈련 시킨다. 그리고 마지막에 딱 한 번 테스트 세트에서 평가한다.
    • 테스트 세트의 성능이 검증 데이터에서 측정한 것보다 많이 나쁘다면, 검증 과정에 전혀 신뢰성이 없거나 모델의 하이퍼파라미터를 튜닝하는 동안 검증 데이터에 과대적합된 것이다.
    • 이런 경우에는 좀 더 신뢰할만한 평가 방법으로 바꾸는 것이 좋다. (반복 K-겹 교차 검증)

요약

  • 주어진 문제와 훈련할 데이터를 정의한다. 이 데이터를 수집하고 필요하면 레이블을 태깅한다.
  • 성공을 어떻게 측정할지 선택하라. 검증 데이터에서 모니터링할 지표는 무엇인가?
  • 평가 방법을 결정하라. 홀드아웃 검증이나 K-겹 교차 검증인가? 검증에 사용해야 할 데이터의 양은 얼마나 되는가?
  • 단순한 랜덤 선택 모델보다 나은 통계적 검정력이 있는 첫 번째 모델을 만든다.
  • 과대적합된 모델을 만든다.
  • 검증 데이터의 성능에 기초하여 모델에 규제를 적용하고 하이퍼파라미터를 튜닝한다. 머신 러닝 연구의 대부분은 이 단계에 집중된다.
[ssba]

The author

지성을 추구하는 사람/ suyeongpark@abyne.com

댓글 남기기

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.