4장 신경망 활용: 분류와 회귀

감사말: 프랑소와 숄레의 Deep Learning with Python, Second Edition 4장에 사용된 코드에 대한 설명을 담고 있으며 텐서플로우 2.6 버전에서 작성되었습니다. 소스코드를 공개한 저자에게 감사드립니다.

tensorflow 버전과 GPU 확인

주요 내용

4.1 영화 리뷰 분류: 이항 분류

영화 리뷰가 긍정적인지 부정적인지를 판단하는 이항 분류 모델을 구성한다.

IMDB 데이터셋

IMDB 데이터셋 불러오기

훈련셋, 테스트셋의 크기 모두 25,000이다.

각 샘플은 num_words=10000에 의해 1~9999 사이의 정수로 이루어진 리스트이다. 최솟값은 1, 최댓값은 9999임은 아래와 같이 확인한다.

샘플들의 크기는 서로 다르다.

0번 샘플의 처음 10개 값은 다음과 같다.

각 샘플의 레이블은 0(부정) 또는 1(긍정)이다.

리뷰 내용 확인하기

주의사항: 모델 훈련을 위해 반드시 필요한 사항은 아님!

word_index에 포함된 10개 항목을 확인하면 다음과 같다.

reverse_word_index에 포함된 10개 항목을 확인하면 다음과 같다.

첫째 리뷰 내용을 아래와 같이 확인할 수 있다.

데이터 전처리: 벡터화

정수들의 리스트, 그것도 길이가 다른 여러 개의 리스트를 신경망의 입력값으로 사용할 수 없다. 또한 모든 샘플의 길이를 통일시킨다 하더라도 정수들의 리스트를 직접 신경망 모델의 입력값으로 사용하려면 나중에 다룰 Embedding 층(layer)과 같은 전처리 층을 사용해야 한다. 여기서는 대신에 멀티-핫-인코딩을 이용하여 정수들의 리스트를 0과 1로만 이루어진 일정한 길이의 벡터(1차원 어레이)로 변환한다.

멀티-핫-인코딩

앞서 보았듯이 리뷰 리스트에 사용된 숫자들은 1부터 9999 사이의 값이다. 이 정보를 이용하여 리뷰 샘플을 길이가 10,000인 벡터(1차원 어레이)로 변환할 수 있다.

예를 들어, [1, 18, 13]은 길이가 10,000인 1차원 어레이(벡터)로 변환되는데 1번, 18번, 13번 인덱스의 항목만 1이고 나머지는 0으로 채워진다. 이러한 변환을 멀티-핫-인코딩(multi-hot-encoding)이라 부른다. 다음 vectorize_sequences() 함수는 앞서 설명한 멀티-핫-인코딩을 모든 주어진 샘플에 대해 실행한다.

이처럼 각 샘플을 지정된 크기의 1차원 어레이로 변환하는 과정을 벡터화(vectorization)이라 한다.

이제 훈련셋와 테스트셋를 벡터화한다.

첫째 훈련 샘플의 변환 결과는 다음과 같다.

레이블 또한 정수 자료형에서 float32 자료형으로 변환해서 자료형을 일치시킨다.

모델 구성

입력 샘플의 특성이 벡터(1차원 어레이)로 주어지고 레이블이 스칼라(하나의 숫자)로 주어졌을 때 밀집층(densely-connected layer)과 Sequential 모델을 이용하면 성능 좋은 모델을 얻는다. 이때 사용하는 활성화 함수는 일반적으로 다음과 같다.

그림 출처: Deep Learning with Python(Manning MEAP)

몇 개의 층을 사용하는가와, 각 층마다 몇 개의 유닛(unit)을 사용하는가를 결정해야 하는데 이에 대해서 다음 장에서 자세히 설명한다. 여기서는 일단 다음과 같은 구성을 추천한다.

참고: 이항 분류 모델의 최상위 층은 스칼라 값을 출력하도록 하나의 유닛을 사용하는 Dense 밀집층을 사용한다. 또한 활성화 함수로 0과 1사이의 확률값을 계삲하는 sigmoid를 활성화 함수로 사용한다. 그러면 사이킷런의 로지스틱 회귀(logistic regression) 모델처럼 작동한다.

모델 컴파일링

모델 훈련 검증

훈련 중인 모델을 에포크마다 검증하려면 검증 세트를 따로 지정하면 된다. 여기서는 처음 10,000개의 샘플을 검증 세트로 활용한다.

훈련을 시작할 때 validation_data 옵션 인자를 지정하면 에포크마다 검증 세트를 이용하여 훈련중인 모델을 평가한다.

History 객체 활용

fit() 메서드가 반환하는 객체는 Callback 클래스를 상속하는 History 클래스의 인스턴이며, 케라스의 모든 모델 훈련과정 중에 발생하는 다양한 정보를 저장한다.

참고: 콜백(Callback) 클래스에 대해서는 나중에 자세히 살펴볼 예정이다.

예를 들어, history 속성에 저장된 정보를 이용하여 훈련셋와 검증 세트에 대한 에포크별 손실값과 정확도의 변화를 그래프로 그릴 수 있다.

과대적합

과대적합(overfitting)은 모델이 훈련셋에 익숙해진다는 의미이다. 시험에 비유하면 연습문제의 답을 외워버리는 것을 의미한다. 과대적합을 방지하기 위한 다양한 기법은 다음 장(chapter)에서 다룬다. 위 문제의 경우 넷째 또는 다섯째 에포크 정도만 훈련 반복을 진행하면 된다. 아래 코드는 다시 처음부터 네 번의 에포크만을 사용하여 훈련한 결과를 보여준다.

테스트셋에 대한 성능은 아래와 같이 88% 정도의 정확도를 보인다. 앞으로 보다 좋은 성능의 모델을 살펴볼 것이며, 현존하는 가장 좋은 모델의 정확도는 95% 정도이다.

모델 활용

훈련된 모델을 활용하려면 predict() 메서드를 이용한다.

아래처럼 데이터셋이 클 경우 배치 단위로 묶어서 예측할 수도 있다.

연습문제

  1. 두 개의 은닉층 대신 1 개 또는 3 개의 은닉층을 사용할 때 검증 세트와 테스트셋에 대한 평가지표의 변화를 확인하라.
  2. 각 은닉층에 사용된 유닛(unit)의 수를 8, 32, 64 등으로 변화시킨 후 검증 세트와 테스트셋에 대한 평가지표의 변화를 확인하라.
  3. binary_crossentropy 대신 mse를 손실함수로 지정한 후 검증 세트와 테스트셋에 대한 평가지표의 변화를 확인하라.
  4. relu 함수 대신 이전에 많이 사용됐었던 tanh 함수를 손실함수로 지정한 후 검증 세트와 테스트셋에 대한 평가지표의 변화를 확인하라.

4.2 뉴스 기사 분류: 다중 클래스 분류

로이터(Reuter) 데이터셋

데이터셋 적재

주제별 기사 수가 다르다. 훈련셋의 타깃에 사용된 값들의 빈도수를 확인하면 다음과 같다.

가장 많이 언급된 주제는 총 3159번, 자장 적게 언급딘 주제는 총 10번 기사로 작성되었다.

각 샘플은 정수들의 리스트이다.

각 샘플 리스트의 길이가 일반적으로 다르다.

각 샘플에 대한 레이블은 0부터 45까지의 정수로 표현된다. 예를 들어, 10번 기사의 주제는 3이다.

주제 3은 'earn'(이익)과 연관된다.

참고: 데이터 분석을 위해 반드시 필요한 사항은 아니지만 언급된 46개의 주제와 번호 사이의 관계는 GitHub: Where can I find topics of reuters dataset #12072를 참조할 수 있다.

실제로 10번 기사 내용을 확인해보면 'earn'과 관련되어 있어 보인다. 데이터를 해독(decoding)하는 방법은 IMDB 데이터셋의 경우와 동일하다.

10번 기사 내용은 다음과 같다.

데이터 전처리

데이터 벡터화

IMDB의 경우와 동일하게 길이가 10,000인 벡터로 모든 샘플을 변환한다.

타깃의 원-핫-인코딩

앞서 보았듯이 타깃은 0부터 45 사이의 값이다. 이런 경우 정수를 텐서로 변환해서 사용하는 것보다 원-핫-인코딩(one-hot-encoding) 기법을 적용하는 게 좋다.

원-핫-인코딩은 멀티-핫-인코딩 기법과 유사하다. 차이점은 1인 오직 한 곳에서만 사용되고 나머지 항목은 모두 0이 된다. 예를 들어, 3은 길이가 46인 벡터(1차원 어레이)로 변환되는데 3번 인덱스에서만 1이고 나머지는 0이 된다.

아래 함수는 원-핫-인코딩을 실행하는 함수이다. 입력값은 레이블 데이터셋 전체를 대상으로 함에 주의하라.

훈련셋의 레이블과 테스트셋의 레이블을 인코딩한다.

인코딩된 레이블 하나를 살펴보자.

to_categorical() 함수

원-핫-인코딩을 지원하는 함수를 케라스가 지원한다.

참고: 사용된 레이블의 최댓값에 1을 더한 값을 어레이의 길이로 사용한다.

참고: 원-핫-인코딩, 멀티-핫-인코딩 등 정수를 사용하는 데이터를 범주형 데이터로 변환하는 전처리 과정을 지원하는 층(layer)이 있다. 예를 들어 tf.keras.layers.CategoryEncoding 층은 원-핫-인코딩과 멀티-핫-인코딩을 지원한다.

모델 생성

모델 정의

IMDB 데이터셋의 경우와는 달리 3 개 이상의 클래스로 분류하는 다중 클래스 분류 모델의 최종 층은 분류해야 하는 클래스의 수 만큼의 유닛과 함께 각 클래스에 속할 확률을 계산하는 softmax 활성화 함수를 이용한다.

참고: 각 클래스에 속할 확률을 모두 더하면 1이 되며, 가장 높은 확률을 가진 클래스를 예측값으로 사용한다.

반면에 이항 분류의 경우보다 복잡한 문제이기에 은닉층의 유닛은 64개씩 정하여 보다 많은 정보를 상위 층으로 전달하도록 한다.

모델 컴파일

다중 클래스 분류 모델의 손실함수는 categorical_crossentropy을 사용한다. categorical_crossentropy는 클래스의 실제 분포와 예측 클래스의 분포 사이의 오차를 측정하는 손실함수이며, 자세한 내용은 생략한다.

모델 훈련 및 검증

검증 세트 지정

처음 1,000개의 샘플을 검증 세트 용도로 사용한다.

모델 훈련

훈련 방식은 이전과 동일하다.

손실값의 변화

정확도의 변화

모델 재훈련

9번 에포크를 지나면서 과대적합이 발생하는 것으로 보인다. 따라서 에포크 수를 9로 줄이고 처음부터 다시 훈련시켜보자. 모델 구성부터, 컴파일, 훈련을 모두 다시 시작해야 가중치와 편향이 초기화된 상태서 훈련을 시작한다.

훈련된 모델을 이용한 테스트셋에 대한 예측의 정확도는 80% 정도이다.

80%의 정확도가 얼마나 좋은지/나쁜지를 판단하려면 무작위로 찍을 때의 정확도를 계산해봐야 한다. 아래 코드가 이를 실천하며, 20% 정도의 정확도가 나온다. 따라서 80% 정도의 정확도는 상당히 좋은 편이다.

예측하기

훈련된 모델을 테스트셋에 적용한다.

예측값의 모두 길이가 46인 1차원 어레이다.

예측값은 46개 클래스에 들어갈 확률들로 이루어지며 합은 1이다.

가장 큰 확률값을 가진 인덱스가 모델이 예측하는 클래스가 된다. 예를 들어 테스트셋의 0번 샘플(로이터 기사)은 3번 레이블을 갖는다고 예측된다.

정수 레이블 사용법

정수 텐서 레이블(타깃)을 이용하여 훈련하려면 모델을 컴파일할 때 손실함수로 sparse_categorical_crossentropy를 사용하면 된다.

y_train = np.array(train_labels)
y_test = np.array(test_labels)

model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

은닉층에 사용되는 유닛 개수

은닉층에 사용되는 유닛은 마지막 층의 유닛보다 많아야 한다. 그렇지 않으면 정보전달 과정에 병목현상(bottleneck)이 발생할 수 있다. 아래 코드의 둘째 은닉층은 4 개의 유닛만을 사용하는데 훈련된 모델의 성능이 많이 저하된다.

테스트셋에 대한 정확도가 80% 정도에서 65% 정도로 낮아진다.

연습문제

  1. 은닉층의 유닛 개수를 32, 128 등 여러 값으로 실험해 보아라.
  2. 은닉층의 수를 1개 또는 3개로 바꿔 보아라.

4.3 주택가격 예측: 회귀

이항 분류, 다중 클래스 분류 모델은 지정된 숫자들로 이루어진 특정 클래스의 번호 하나를 예측한다. 반면에 임의의 수를 예측하는 문제는 회귀(regression)이라 부른다. 예를 들어 온도 예측, 가격 예측 등을 다루는 것이 회귀 문제이다. 여기서는 보스턴 시의 주택가격을 예측하는 회귀 문제를 예제로 다룬다.

주의사항: '로지스틱 회귀'(logistic regression) 알고리즘는 분류 모델임에 주의하라.

보스턴 주택가격 데이터셋

사용하는 데이터셋은 다음과 같다.

보스턴 주택가격 데이터셋 적재

훈련셋 샘플의 타깃은 아래처럼 범위가 지정되지 않은 부동소수점 값이다.

데이터 전처리

특성에 따라 사용되는 값들의 크기가 다르다. 어떤 특성은 0과 1사이, 다른 특성은 한 자리리부터 세 자리의 수를 갖기도 한다.

데이터 정규화

따라서 모든 특성의 값을 정규화 해주어야 모델 훈련이 더 잘된다. 모든 특성값들을 특성별로 표준 정규분포를 따르도록 한다. 즉, 평균값 0, 표준편차 1이 되도록 특성값을 특성별로 변환한다.

주의사항: 테스트셋의 정규화는 훈련셋의 평균값과 표준편차를 이용해야 한다. 이유는 테스트셋의 정보는 모델 훈련에 절대로 사용되지 않아야 하기 때문이다.

모델 구현

모델 정의

이전과는 달리 모델 구성과 컴파일을 동시에 진행하는 함수를 이용한다.

참고: 데이터셋이 클 수록 보다 많은 층과 보다 많은 유닛 사용하는 것이 일반적임.

K-겹 교차검증 활용

데이터셋이 작기에 훈련 중에 사용할 검증 세트를 따로 분리하는 것은 훈련의 효율성을 떨어뜨린다. 대신에 K-겹 교차검증을(K-fold cross-validation) 사용한다. 아래 이미지는 3-겹 교차검증을 사용할 때 훈련 중에 사용되는 훈련셋과 검증셋의 사용법을 보여준다.

그림 출처: Deep Learning with Python(Manning MEAP)

예제: 4-겹 교차검증

K-겹 교차검증 훈련 과정 그래프: 평가지표 기준

500번의 에포크마다 4 번의 교차 검증을 진행하였기에 에포크 별로 검증세트를 대상으로하는 평균절대오차의 평균값을 계산한다.

에포크별 평균절대오차의 평균값의 변화를 그래프로 그리면 다음과 같다.

첫 10개의 에포크의 성능이 매우 나쁘기에 그 부분을 제외하고 그래프를 그리면 보다 정확하게 변환 과정을 파악할 수 있다.

재훈련

재훈련된 모델의 테스트셋에 대한 성능을 평가하면 주택가격 예측에 있어서 평균적으로 2,500달러 정도의 차이를 갖는다.

모델 활용

연습문제

사이킷런의 KFold를 이용하면 봅다 간단하게 K-겹 교차검증을 진행할 수 있다.