3.4 영화 리뷰 분류: 이진 분류 예제

3.3 딥러닝 컴퓨터 셋팅 | 목차 | 3.5 뉴스 기사 분류: 다중 분류 문제

 

2종 분류two-class classification 또는 이진 분류binary classification는 아마도 가장 널리 적용된 머신 러닝 문제일 것입니다. 이 예제에서 리뷰 텍스트를 기반으로 영화 리뷰를 긍정positive과 부정negative으로 분류하는 방법을 배우겠습니다.16

3.4.1 IMDB 데이터셋

인터넷 영화 데이터베이스Internet Movie Database로부터 가져온 양극단의 리뷰 5만 개로 이루어진 IMDB 데이터셋을 사용하겠습니다.17 이 데이터셋은 훈련 데이터 25,000개와 테스트 데이터 25,000개로 나뉘어 있고 각각 50%는 부정, 50%는 긍정 리뷰로 구성되어 있습니다.

왜 훈련 데이터와 테스트 데이터를 나눌까요? 같은 데이터에서 머신 러닝 모델을 훈련하고 테스트해서는 절대 안 되기 때문입니다! 모델이 훈련 데이터에서 잘 작동한다는 것이 처음 만난 데이터에서도 잘 작동한다는 것을 보장하지 않습니다. 중요한 것은 새로운 데이터에 대한 모델의 성능입니다(사실 훈련 데이터의 레이블은 이미 알고 있기 때문에 이를 예측하는 모델은 필요하지 않습니다). 예를 들어 모델이 훈련 샘플과 타깃 사이의 매핑을 모두 외워 버릴 수 있습니다. 이런 모델은 처음 만나는 데이터에서 타깃을 예측하는 작업에는 쓸모가 없습니다.18 다음 장에서 이에 대해 더 자세히 살펴봅니다.

MNIST 데이터셋처럼 IMDB 데이터셋도 케라스에 포함되어 있습니다. 이 데이터는 전처리되어 있어 각 리뷰(단어 시퀀스)가 숫자 시퀀스로 변환되어 있습니다. 여기서 각 숫자는 사전19에 있는 고유한 단어를 나타냅니다.

다음 코드는 데이터셋을 로드합니다(처음 실행하면 17MB 정도의 데이터를 컴퓨터에 내려받습니다).20

코드 3-1 IMDB 데이터셋 로드하기

from keras.datasets import imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(
    num_words=10000)

num_words=10000 매개변수는 훈련 데이터에서 가장 자주 나타나는 단어 1만 개만 사용하겠다는 의미입니다. 드물게 나타나는 단어는 무시하겠습니다. 이렇게 하면 적절한 크기의 벡터 데이터를 얻을 수 있습니다.

변수 train_datatest_data는 리뷰의 목록입니다. 각 리뷰는 단어 인덱스의 리스트입니다(단어 시퀀스가 인코딩된 것입니다).21 train_labelstest_labels부정을 나타내는 0긍정을 나타내는 1의 리스트입니다.

>>> train_data[0]
1, 14, 22, 16, .. . 178, 32]

>>> train_labels[0]
1

가장 자주 등장하는 단어 1만 개로 제한했기 때문에 단어 인덱스는 10,000을 넘지 않습니다.

>>> max([max(sequence) for sequence in train_data])
9999

재미 삼아 이 리뷰 데이터 하나를 원래 영어 단어로 어떻게 바꾸는지 보겠습니다.22

word_index = imdb.get_word_index() # word_index는 단어와 정수 인덱스를 매핑한 딕셔너리입니다.
reverse_word_index = dict(
    # 정수 인덱스와 단어를 매핑하도록 뒤집습니다.
    [(value, key) for (key, value) in word_index.items()])
decoded_review = ' '.join(
    # 리뷰를 디코딩합니다. 0, 1, 2는 ‘패딩’, ‘문서 시작’, ‘사전에 없음’을 위한 인덱스이므로 3을 뺍니다.
    [reverse_word_index.get(i - 3, '?') for i in train_data[0]])

3.4.2 데이터 준비

신경망에 숫자 리스트를 주입할 수는 없습니다. 리스트를 텐서로 바꾸는 두 가지 방법이 있습니다.

  • 같은 길이가 되도록 리스트에 패딩padding을 추가하고 (samples, sequence_length) 크기의 정수 텐서로 변환합니다.23 그다음 이 정수 텐서를 다룰 수 있는 층을 신경망의 첫 번째 층으로 사용합니다(Embedding 층을 말하며, 나중에 자세히 다루겠습니다24).
  • 리스트를 원핫 인코딩one-hot encoding하여 01의 벡터로 변환합니다. 예를 들어 시퀀스 [3, 5]를 인덱스 35의 위치는 1이고 그 외는 모두 010,000차원의 벡터로 각각 변환합니다.25 그다음 부동 소수 벡터 데이터를 다룰 수 있는 Dense 층을 신경망의 첫 번째 층으로 사용합니다.

여기서는 두 번째 방식을 사용하고 이해를 돕기 위해 직접 데이터를 원핫 벡터로 만들겠습니다.

코드 3-2 정수 시퀀스를 이진 행렬로 인코딩하기

import numpy as np

def vectorize_sequences(sequences, dimension=10000):
    # 크기가 (len(sequences), dimension)이고 모든 원소가 0인 행렬을 만듭니다.
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1. # results[i]에서 특정 인덱스의 위치를 1로 만듭니다.
    return results

x_train = vectorize_sequences(train_data) # 훈련 데이터를 벡터로 변환합니다.
x_test = vectorize_sequences(test_data) # 테스트 데이터를 벡터로 변환합니다.

이제 샘플은 다음과 같이 나타납니다.

>>> x_train[0]
array([ 0., 1., 1., .. ., 0., 0., 0.])

레이블은 쉽게 벡터로 바꿀 수 있습니다.

y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')

이제 신경망에 주입할 데이터가 준비되었습니다.

3.4.3 신경망 모델 만들기

입력 데이터가 벡터고 레이블은 스칼라(1 또는 0)입니다. 아마 앞으로 볼 수 있는 문제 중에서 가장 간단할 것입니다. 이런 문제에 잘 작동하는 네트워크 종류는 relu 활성화 함수를 사용한 완전 연결 층(즉 Dense(16, activation=’relu’))을 그냥 쌓은 것입니다.

Dense 층에 전달한 매개변수(16)는 은닉 유닛hidden unit의 개수입니다. 하나의 은닉 유닛은 층이 나타내는 표현 공간에서 하나의 차원이 됩니다. 2장에서 relu 활성화 함수를 사용한 Dense 층을 다음 텐서 연산을 연결하여 구현했습니다.

output = relu(dot(W, input) + b)

16개의 은닉 유닛이 있다는 것은 가중치 행렬 W의 크기가 (input_dimension, 16)이라는 뜻입니다. 입력 데이터와 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영됩니다(그리고 편향 벡터 b를 더하고 relu 연산을 적용합니다). 표현 공간의 차원을 ‘신경망이 내재된 표현을 학습할 때 가질 수 있는 자유도’로 이해할 수 있습니다. 은닉 유닛을 늘리면(표현 공간을 더 고차원으로 만들면) 신경망이 더욱 복잡한 표현을 학습할 수 있지만 계산 비용이 커지고 원하지 않는 패턴을 학습할 수도 있습니다(훈련 데이터에서는 성능이 향상되지만 테스트 데이터에서는 그렇지 않은 패턴입니다).

Dense 층을 쌓을 때 두 가지 중요한 구조상의 결정이 필요합니다.

  • 얼마나 많은 층을 사용할 것인가?
  • 각 층에 얼마나 많은 은닉 유닛을 둘 것인가?

4장에서 이런 결정을 하는 데 도움이 되는 일반적인 원리를 배웁니다. 당분간은 필자를 믿고 선택한 다음 구조를 따라 주세요.

  • 16개의 은닉 유닛을 가진 2개의 은닉 층
  • 현재 리뷰의 감정을 스칼라 값의 예측으로 출력하는 세 번째 층

중간에 있는 은닉 층은 활성화 함수로 relu를 사용하고 마지막 층은 확률(01 사이의 점수로, 어떤 샘플이 타깃 ‘1’일 가능성이 높다는 것은 그 리뷰가 긍정일 가능성이 높다는 것을 의미합니다)을 출력하기 위해 시그모이드 활성화 함수를 사용합니다. relu는 음수를 0으로 만드는 함수입니다(그림 34 참고). 시그모이드는 임의의 값을 [0, 1] 사이로 압축하므로 출력 값을 확률처럼 해석할 수 있습니다(그림 35 참고).

108

그림 3-4 relu 함수

109_1

그림 3-5 시그모이드(sigmoid) 함수

109_2

그림 3-6 3개의 층으로 된 신경망

그림 36에서 이 신경망을 보여 줍니다. 다음은 이 신경망의 케라스 구현입니다. 이전에 보았던 MNIST 예제와 비슷합니다.

코드 3-3 모델 정의하기

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'))

활성화 함수가 무엇인가요? 왜 필요한가요?

relu와 같은 활성화 함수(또는 비선형성non-linearity이라고도 부릅니다)가 없다면 Dense 층은 선형적인 연산인 점곱과 덧셈 2개로 구성됩니다.

output = dot(W, input) + b

그러므로 이 층은 입력에 대한 선형 변환(아핀 변환)만을 학습할 수 있습니다. 이 층의 가설 공간은 입력 데이터를 16 차원의 공간으로 바꾸는 가능한 모든 선형 변환의 집합입니다. 이런 가설 공간은 매우 제약이 많으며, 선형 층을 깊게 쌓아도 여전히 하나의 선형 연산이기 때문에 층을 여러 개로 구성하는 장점이 없습니다. 즉 층을 추가해도 가설 공간이 확장되지 않습니다.

가설 공간을 풍부하게 만들어 층을 깊게 만드는 장점을 살리기 위해서는 비선형성 또는 활성화 함수를 추가해야 합니다. relu는 딥러닝에서 가장 인기 있는 활성화 함수입니다. 이름은 조금 이상하지만 prelu, elu 등 비슷한 다른 함수들도 많습니다.26

마지막으로 손실 함수와 옵티마이저를 선택해야 합니다. 이진 분류 문제고 신경망의 출력이 확률이기 때문에(네트워크의 끝에 시그모이드 활성화 함수를 사용한 하나의 유닛으로 된 층을 놓았습니다), binary_crossentropy 손실이 적합합니다. 이 함수가 유일한 선택은 아니고 mean_squared_error도 사용할 수 있습니다.27 확률을 출력하는 모델을 사용할 때는 크로스엔트로피가 최선의 선택입니다. 크로스엔트로피Crossentropy는 정보 이론Information Theory 분야에서 온 개념으로 확률 분포 간의 차이를 측정합니다. 여기에서는 원본 분포와 예측 분포 사이를 측정합니다.

다음은 rmsprop 옵티마이저와 binary_crossentropy 손실 함수로 모델을 설정하는 단계입니다. 훈련하는 동안 정확도를 사용하여 모니터링하겠습니다.

코드 3-4 모델 컴파일하기

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

케라스에 rmsprop, binary_crossentropy, accuracy가 포함되어 있기 때문에 옵티마이저, 손실 함수, 측정 지표를 문자열로 지정하는 것이 가능합니다. 이따금 옵티마이저의 매개변수를 바꾸거나 자신만의 손실 함수, 측정 함수를 전달해야 할 경우가 있습니다. 전자의 경우에는 코드 35와 같이 옵티마이저 파이썬 클래스를 사용해서 객체를 직접 만들어 optimizer 매개변수에 전달하면 됩니다. 후자의 경우는 코드 36과 같이 lossmetrics 매개변수에 함수 객체를 전달하면 됩니다.

코드 3-5 옵티마이저 설정하기

from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
              loss='binary_crossentropy',
              metrics=['accuracy'])

코드 3-6 손실과 측정을 함수 객체로 지정하기28

from keras import losses
from keras import metrics

model.compile(optimizer=optimizers.RMSprop(lr=0.001),
              loss=losses.binary_crossentropy,
              metrics=[metrics.binary_accuracy])

3.4.4 훈련 검증

훈련하는 동안 처음 본 데이터에 대한 모델의 정확도를 측정하기 위해서는 원본 훈련 데이터에서 10,000의 샘플을 떼어 검증 세트를 만들어야 합니다.

코드 3-7 검증 세트 준비하기

x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

이제 모델을 512개의 샘플씩 미니 배치를 만들어 20번의 에포크 동안 훈련시킵니다(x_trainy_train 텐서에 있는 모든 샘플에 대해 20번 반복합니다). 동시에 따로 떼어 놓은 1만 개의 샘플에서 손실과 정확도를 측정할 것입니다. 이렇게 하려면 validation_data 매개변수에 검증 데이터를 전달해야 합니다.

코드 3-8 모델 훈련하기29

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['acc'])

history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,
                    batch_size=512,
                    validation_data=(x_val, y_val))

CPU를 사용해도 에포크마다 2초가 걸리지 않습니다. 전체 훈련은 20초 이상 걸립니다. 에포크가 끝날 때마다 1만 개의 검증 샘플 데이터에서 손실과 정확도를 계산하기 때문에 약간씩 지연됩니다.

model.fit() 메서드는 History 객체를 반환합니다. 이 객체는 훈련하는 동안 발생한 모든 정보를 담고 있는 딕셔너리인 history 속성을 가지고 있습니다. 한번 확인해 보죠.

>>> history_dict = history.history
>>> history_dict.keys()
[u'acc', u'loss', u'val_acc', u'val_loss']

이 딕셔너리는 훈련과 검증하는 동안 모니터링할 측정 지표당 하나씩 모두 4개의 항목을 담고 있습니다. 이어지는 두 목록에서 맷플롯립을 사용하여 훈련과 검증 데이터에 대한 손실(그림 37 참고)과 정확도(그림 38 참고)를 그리겠습니다. 신경망의 무작위한 초기화 때문에 독자들의 결과와 조금 다를 수 있습니다.

코드 3-9 훈련과 검증 손실 그리기

import matplotlib.pyplot as plt

history_dict = history.history
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='Training loss')  # ‘bo’는 파란색 점을 의미합니다.
plt.plot(epochs, val_loss, 'b', label='Validation loss') # ‘b’는 파란색 실선을 의미합니다.
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()
113

그림 3-7 훈련과 검증 손실

코드 3-10 훈련과 검증 정확도 그리기

plt.clf() # 그래프를 초기화합니다.
acc = history_dict['acc']
val_acc = history_dict['val_acc']

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()

plt.show()
114

그림 3-8 훈련과 검증 정확도

여기에서 볼 수 있듯이 훈련 손실이 에포크마다 감소하고 훈련 정확도는 에포크마다 증가합니다. 경사 하강법 최적화를 사용했을 때 반복마다 최소화되는 것이 손실이므로 기대했던 대로입니다. 검증 손실과 정확도는 이와 같지 않습니다. 네 번째 에포크에서 그래프가 역전되는 것 같습니다. 이것이 훈련 세트에서 잘 작동하는 모델이 처음 보는 데이터에서는 잘 작동하지 않을 수 있다고 앞서 언급한 경고의 한 사례입니다. 정확한 용어로 말하면 과대적합overfitting되었다고 합니다. 두 번째 에포크 이후부터 훈련 데이터에 과도하게 최적화되어 훈련 데이터에 특화된 표현을 학습하므로 훈련 세트 이외의 데이터에는 일반화되지 못합니다.

이런 경우에 과대적합을 방지하기 위해서 세 번째 에포크 이후에 훈련을 중지할 수 있습니다. 일반적으로 4장에서 보게 될 과대적합을 완화하는 다양한 종류의 기술을 사용할 수 있습니다. 처음부터 다시 새로운 신경망을 네 번의 에포크 동안만 훈련하고 테스트 데이터에서 평가해 보겠습니다.30

코드 3-11 모델을 처음부터 다시 훈련하기

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.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=4, batch_size=512)
results = model.evaluate(x_test, y_test)

최종 결과는 다음과 같습니다.

>>> results
[0.3231545869159698, 0.87348]

아주 단순한 방식으로도 87%의 정확도를 달성했습니다. 최고 수준의 기법을 사용하면 95%에 가까운 성능을 얻을 수 있습니다.

3.4.5 훈련된 모델로 새로운 데이터에 대해 예측하기

모델을 훈련시킨 후에 이를 실전 환경에서 사용하고 싶을 것입니다. predict 메서드를 사용해서 어떤 리뷰가 긍정일 확률을 예측할 수 있습니다.31

>>> model.predict(x_test)
array([[ 0.98006207]
       [ 0.99758697]
       [ 0.99975556]
       ...,
       [ 0.82167041]
       [ 0.02885115]
       [ 0.65371346]], dtype=float32)

여기처럼 이 모델은 어떤 샘플에 대해 확신을 가지고 있지만(0.99 또는 그 이상, 0.01 또는 그 이 하) 어떤 샘플에 대해서는 확신이 부족합니다(0.6, 0.4).

3.4.6 추가 실험

다음 실험을 진행하면 여기에서 선택한 구조가 향상의 여지는 있지만 어느 정도 납득할 만한 수준 이라는 것을 알게 될 것입니다.

  • 여기에서는 2개의 은닉 층을 사용했습니다. 1개 또는 3개의 은닉 층을 사용하고 검증과 테 스트 정확도에 어떤 영향을 미치는지 확인해 보세요.
  • 층의 은닉 유닛을 추가하거나 줄여 보세요. 32개의 유닛, 64개의 유닛 등
  • binary_crossentropy 대신에 mse 손실 함수를 사용해 보세요.
  • relu 대신에 tanh 활성화 함수(초창기 신경망에서 인기 있었던 함수입니다)를 사용해 보세요.

3.4.7 정리

다음은 이 예제에서 배운 것들입니다.

  • 원본 데이터를 신경망에 텐서로 주입하기 위해서는 꽤 많은 전처리가 필요합니다. 단어 시 퀀스는 이진 벡터로 인코딩될 수 있고 다른 인코딩 방식도 있습니다.
  • relu 활성화 함수와 함께 Dense 층을 쌓은 네트워크는 (감성 분류를 포함하여) 여러 종류의 문제에 적용할 수 있어 앞으로 자주 사용하게 될 것입니다.
  • (출력 클래스가 2개인) 이진 분류 문제에서 네트워크는 하나의 유닛과 sigmoid 활성화 함수 를 가진 Dense 층으로 끝나야 합니다. 이 신경망의 출력은 확률을 나타내는 0과 1 사이의 스 칼라 값입니다.
  • 이진 분류 문제에서 이런 스칼라 시그모이드 출력에 대해 사용할 손실 함수는 binary_ crossentropy입니다.
  • rmsprop 옵티마이저는 문제에 상관없이 일반적으로 충분히 좋은 선택입니다. 걱정할 거리 가 하나 줄은 셈입니다.
  • 훈련 데이터에 대해 성능이 향상됨에 따라 신경망은 과대적합되기 시작하고 이전에 본적 없 는 데이터에서는 결과가 점점 나빠지게 됩니다. 항상 훈련 세트 이외의 데이터에서 성능을 모니터링해야 합니다.

 


 

16 역주 이 절과 다음 절에서는 텍스트 데이터셋에 기본 신경망을 적용하여 분류 작업을 합니다. 텍스트 데이터에 순환 신경망을 적용하는 방법 은 6장에서 소개됩니다. IMDB 리뷰 분류 문제에 토큰화, 형태소 분석 등을 적용하는 다양한 방법은 <파이썬 라이브러리를 활용한 머신러닝> (한빛미디어, 2017)의 7장을 참고하세요.
17 역주 이 데이터셋은 스탠포드 대학의 앤드류 마스(Andrew Maas)가 수집한 데이터셋입니다(https://stanford.io/2w2NUzz).
18 역주 훈련 데이터를 외우는 것이 전부인 학습 알고리즘을 사례 기반(instance-based) 학습이라고 하며, 대표적으로는 k-최근접 이웃 (k-Nearest Neighbor) 알고리즘이 있습니다. 새로운 데이터에 대해 예측을 할 때는 가장 가까운 훈련 데이터 몇 개의 타깃을 평균(회귀)하 거나 다수인 클래스를 선택(분류)합니다.
19 역주 데이터셋의 전체 문서에 나타난 모든 단어에 고유한 번호를 부여한 목록을 어휘 사전 또는 사전이라고 부릅니다. 이런 작업은 텍스트 데 이터를 다룰 때 기본적으로 수행하는 전처리 과정입니다.
20 역주 케라스에 포함된 데이터셋은 아마존 AWS S3와 (cifar10, cifar100 데이터셋의 경우) 토론토 대학에서 내려받도록 되어 있으므로 예 제를 따라 하려면 인터넷 연결이 필요합니다. 이어지는 코드에서 get_word_index() 메서드도 1.6MB 정도의 사전 데이터를 내려받습니 다. 내려받은 데이터셋은 유닉스 기준으로 ~/.keras/datasets/ 폴더에 저장됩니다.
21 역주 행마다 길이가 다른 배열을 만들 수 없기 때문에 IMDB 데이터셋과 3.5절의 로이터 데이터셋의 텍스트 데이터는 파이썬 리스트를 원소 로 갖는 넘파이 배열을 사용합니다.
22 역주 decoded_review의 결과는 “? this film was just brilliant casting location ……”과 같습니다. train_data[0]의 첫 번째 원소 가 문서 시작을 알리는 인덱스 1입니다. 여기에서 3을 뺀 값을 reverse_word_index 딕셔너리에서 찾지 못하므로 물음표로 바뀌어 나타 납니다.
23 역주 가장 긴 리뷰는 2,494개의 단어로 이루어져 있으므로 훈련 데이터를 변환한 텐서의 크기는 (25000, 2494)가 됩니다.
24 역주 Embedding 층은 6장에서 자세히 소개됩니다.
25 역주 리스트가 하나의 벡터로 변환되므로 훈련 데이터를 변환한 텐서의 크기는 (25000, 10000)이 됩니다.
26 역주 relu의 변종들은 대부분 음수 처리 방식이 다릅니다. elu 함수는 음수를 완전히 제거하지 않고 보통 최대 -1까지 출력합니다. selu는 elu의 변종으로 이 두 함수는 activation 매개변수에 ‘elu’, ‘selu’로 지정할 수 있습니다. leaky relu는 매개변수로 지정한 값에 비례 해서 음수 값을 통과시킵니다. prelu는 leaky relu의 변종으로 훈련하는 동안 비례의 정도를 학습합니다. 케라스에서 이 두 활성화 함수 는 layer.advanced_activations 패키지에 클래스로 제공됩니다. models.add(LeakyReLU()) 또는 models.add(LeakyReLU())처 럼 사용합니다. 자세한 그래프 모양은 위키피디아 문서를 참고하세요(https://bit.ly/2fE7id7).
27 역주 mean_squared_error와 mean_absolute_error는 회귀 문제에 사용되는 대표적인 손실 함수입니다.
28 역주 이 예에서는 케라스의 함수 객체를 전달했지만 y_true, y_pred 텐서를 입력으로 받는 자신만의 함수를 만들어 전달할 수 있습니 다. 예를 들어 기본 손실 함수를 조합하거나 케라스의 백엔드 함수(https://bit.ly/2rk5Yzp)를 사용하여 자신만의 손실 함수를 만들 수 있습니다.
29 역주 정확도를 지정할 때 ‘accuracy’ 또는 ‘acc’ 모두 가능합니다. 이렇게 하면 자동으로 이진 분류에는 metrics.binary_accuracy 함 수가 사용되고 다중 분류에는 metrics.categorical_accuracy 함수가 사용됩니다. history.history 딕셔너리에 들어 있는 측정 지표의 키는 정확도일 경우 ‘acc’, 크로스엔트로피일 경우(측정 지표로 손실 함수를 사용할 수도 있습니다) ‘ce’ 약어를 사용합니다. 그 외에 는 측정 함수의 이름이 키가 되고 검증 세트의 점수에는 ‘val_’ 접두사가 붙습니다.
30 역주 fit() 메서드를 재호출하면 학습된 가중치에서 훈련이 이어지므로 처음부터 다시 학습하려면 모델 객체를 새로 만들어야 합니다.
31 역주 이진 분류에서 레이블이 1인 경우를 양성(positive) 샘플, 0인 경우를 음성(negative) 샘플이라고 합니다. 이 예에서는 긍정(positive) 인 리뷰가 양성 샘플이지만 어떤 좋은 것이 양성 샘플이 되는 것이 아니고 예측하려는 대상이 양성이 됩니다. 예를 들어 암 진단에 관한 문제 에서는 악성 종양이 양성 샘플이 됩니다.

 

3.3 딥러닝 컴퓨터 셋팅 | 목차 | 3.5 뉴스 기사 분류: 다중 분류 문제

 

이 글은 도서출판 길벗에서 출간한  “케라스 창시자에게 배우는 딥러닝“의 1장~3장입니다. 이 책의 저작권은 (주)도서출판 길벗에 있으므로 무단 복제 및 무단 전제를 금합니다.

3.4 영화 리뷰 분류: 이진 분류 예제”에 대한 23개의 생각

  1. 권영진

    좋은 책 잘 읽고 있습니다.
    책의 내용중, “검증 손실과 정확도는 이와 같지 않습니다. 네 번째 에포크에서 그래프가 역전되는 것 같습니다.”
    그래프의 어느 부분을 이야기하는 지, 이 부분이 잘 이해가 되지 않습니다.
    혹시 추가 설명을 좀 부탁드려도 될 가요?

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 그림 3-7과 그림 3-8 그래프에서 실선이 검증 손실과 정확도를 나타냅니다. 이 두 그래프는 훈련 손실이나 정확도와 달리 계속 향상되지 않고 몇 번만의 에포크 후에 역전되어 나빠지기 시작합니다. 🙂

      좋아요

      응답
  2. 박시현

    안녕하세요 좋은 포스팅 감사합니다
    포스팅에 궁금한 점이 생겨서 질문드립니다
    문장 딥러닝을 할때 RNN이나 LSTM Layer를 사용하는게 좋다고 알고 있는데 Dense Layer를 사용하신 이유가 궁금합니다

    Liked by 1명

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 텍스트와 같은 시퀀스 데이터에는 RNN 계열을 적용하는 것이 좋은 효과를 냅니다. 이 에에서는 기본적인 Dense 층을 사용하는 데는 특별한 이유가 없습니다. 먼저 간단한 모델을 사용해서 주어진 문제에서 도달할 수 있는 성능을 가늠해 보는 것이 일반적입니다. 또 어떤 문제에는 이런 모델을 써야 한다는 고정 관념이 어그러지는 경우가 많습니다. 요즘엔 시퀀스 데이터에 CNN을 써서 좋은 성능을 내는 경우도 많습니다. 특히 텍스트 감성 분류와 같은 문제는 단어의 순서에 큰 영향이 없습니다. 🙂

      좋아요

      응답
  3. 장기정

    안녕하세요?
    글이 좋아서,, 책까지 사서 보고 있습니다 ^^.
    다름 아니고,, imdb.load_data 를,, 이미 다운로드 받은 data로 대치하려면,
    어느 부분을 어떻게 수정해야 할까요?
    참고로, data는 책에 기술되어 있는 github 에서 받았습니다 ( datasets/aclimdb )

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 이미 다운로드 받은 데이터를 사용하려면 imdb.load_data(path=…, num_words=10000)와 같이 써 주세요. path 매개변수에는 로컬 파일 경로를 써 주면 됩니다. 🙂

      좋아요

      응답
  4. 양기성

    안녕하세요 책으로 공부하다가 모르는 것이 있어서 이 책의 옮긴이의 블로그라 해서 들어왔습니다.

    영화 리뷰 분류라는 것이 새로운 리뷰가 input으로 들어왔을때 이것이 긍정리뷰인지 부정리뷰인지 판별하는 블랙박스를 만드는 것이라 이해했습니다.
    처음 train_data를 불러왔을때 decode_review를 통해 train_data의 숫자들이 각각 단어에 대응한다는 것을 알았습니다. 그래서 이것을 input으로해서 신경망을 학습할 줄 알았습니다.

    1. 하지만 신경망에 숫자 리스트를 주입할 수 없다라고 했습니다. 왜 숫자 리스트를 주입할 수 없는지 모르겠습니다.

    2. 정수 시퀀스를 이진 행렬로 인코딩하기(vectorize_sequences) 부분이 질문이 있습니다.
    enumerate함수가 반복문에 쓰이는데 range와의 차이점은 몇 번째 반복인지에 대한 정보가 필요할때 사용한다는 것을 알았습니다.
    results[i, sequence] = 1이것을 보면 (첫번째 반복일때) i=0이므로 results의 1행 sequence열에 해당하는 것을 1로 바꾼다는 것으로 생각했습니다.
    train_data의 data type이 ‘O’라는 것을 확인했습니다. (이 O가 object인것 같은데 int가 나올거라 예상 했는데 O가 나와서 이게 뭔지 사실 잘 모릅니다.)
    일단 results[0,train_data[0]].shape를 했는데 218이라고 나왔습니다.
    제가 생각하기에 이전에 선언한 results의 1열은 10000개의 요소가 있는데 train_data[0]이 218개를 선별 할 수 있게 한다고 생각했습니다. 이렇게 선별하려면 예를들어 results[0,[1,7,20,888]]이런식으로 해야 하는걸로 아는데 train_data는 data type이 O인데 어떻게 선별이 가능한지 궁금합니다.

    3. results[i, sequence] = 1의 과정을 거치면 데이터가 0또는 1밖에 없는데 이것으로 (이전에 decode_review처럼) 이것이 어떻게 학습이 되는지 궁금합니다. 이전에 1번 질문에서 숫자리스트를 직접 신경망에 학습시킨다면 input output이 서로 연관이 있겠다 라는 생각이 드는데 여기서는 0과 1밖에 없어서 연관이 어떻게 되는지 모르겠습니다. 다른말로 얘기하면 25000개의 sample이 있는데 이것이 의미 하는 것은 다 다르지않습니까? 근데 1과 0으로만 표현을 하면 다른 내용인데 겹치는 표현방식이 나올것 같습니다.

    4. 이전에 MNIST에서 input을 256으로 나눠서 0부터1사이의 숫자로 만드셨는데(영화 분류에서도 0~ 1) 신경망 훈련에 input data는 무조건 0과 1사이 숫자만 되는건가요?

    오늘 어린이 날인데 질문이 너무 많아서 죄송합니다.
    좋은 책을 번역해 주셔서 감사합니다.

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 신경망을 처음 접하시면 조금 혼동스러울 수 있습니다. 책을 여러번 혹은 여러 책을 자꾸 보시게 되면 전체 시스템을 조망할 수 있으실 것 같네요. 파이썬 리스트는 길이가 가변적이기 때문에 일반 신경망에 사용하지 못합니다. train_data는 넘파이 배열입니다. train_data[0]에는 정수 리스트가 들어 있습니다. 이 리스트는 리뷰 텍스트에 들어 있는 단어입니다. 신경망 입력은 무조건 0과 1사이가 아닙니다. 데이터마다 조금씩 다를 수 있습니다. 🙂

      좋아요

      응답
  5. 박상인

    입력2,출력1 3개의 danse를 사용할때는 결과값이 잘 나왔는데 danse를 하나씩 늘려 확인해보니 일쩡 danse에서 제대로된 결과값이 나오지 않습니다. 왜 그런 걸까요?

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. dance가 아니라 dense를 말하시는 것 같습니다. 이 예제에서 입력과 출력층은 한개씩입니다. 여러 개의 입력층과 출력층을 사용하는 신경망이 있지만 이 예제와는 맞지 않습니다. 감사합니다.

      좋아요

      응답
      1. 박상인

        답변감사합니다. 그러면 이 예제에서 danse를 계속해서 늘리면 값이 나오지 않는데 왜그런건가요 예제마다 정해진 danse의 수가 있는 건가요? 그렇다면 danse를 찾기위해 어떤 노력이 필요할 까요?

        좋아요

      2. 박해선 글의 글쓴이

        dance가 아니라 dense입니다. 🙂 어떤 문제에 적합한 층 개수를 시험해 보지 않고 미리 알 수 없습니다. 층 개수도 하이퍼파라미터죠. 시행착오를 통해 결정하셔야 합니다. 감사합니다.

        좋아요

  6. 박양정

    안녕하세요. 코드 3-2 이진 정수 행렬로 인코딩 하는 부분에서 문제가 생겨서 질문차 연락드려요.
    —————————————————————————
    TypeError Traceback (most recent call last)
    in
    —-> 1 x_train = vectorize_sequences(train_data)
    2 x_test = vectorize_sequences(test_data)

    in vectorize_sequences(sequences, dimension)
    4
    5 def vectorize_sequences(sequences, dimension=10000):
    —-> 6 results = np.zeros(len(sequences), dimension)
    7 for i, sequence in enumerate(sequences):
    8 results[i,sequence]=1.0

    TypeError: data type not understood

    이런 결과가 나오는데, np 어레이에 할당하기에 데이터 양이 너무 많아서 작동하지 않는 것 같습니다. len(sequences)와 dimension에 작은 숫자를 넣으면 정상작동하더라고요. geforce 1060 3gb에 램 16기가 사용중입니다. 해결하는 방법이 있을까요?

    좋아요

    응답
    1. 박해선 글의 글쓴이

      np.zeros((len(sequences), dimension)) 처럼 괄호가 하나 더 있습니다. 이 함수는 정수나 정수 튜플을 입력받거든요. 🙂

      좋아요

      응답
  7. riderngg

    은닉층 1 개 일 때와 3개일 때 둘 다 88%의 적중률이 나왔는데
    이 문제에서는
    은닉층의 수 보다 여기선 뉴런의 수가 중요한 건가요?

    좋아요

    응답
    1. 박해선 글의 글쓴이

      머신러닝 문제는 여러 가지 조건에 따라 복합적인 영향을 받습니다. 은닉층 개수가 또 다른 하이퍼파라미터에 영향을 받을 수 있습니다. 그래서 단순하게 우선을 말하기 어렵습니다. 🙂

      좋아요

      응답
  8. SHJ

    안녕하세여 model.predict 결과
    confidence로 나와서 1/0이 아닌 0~1 사이 값이 나오는데 이럴 경우 레이블을 0.5이상을 1로 보듯이 임의로 사용자가 지정해줘야 할까요? predict가 label에 맞게 나오게 할수는 없나요?

    좋아요

    응답
  9. SHJ

    안녕하세요
    model.predict 결과
    confidence로 나와서 1/0이 아닌 0~1 사이 값이 나오는데 이럴 경우 레이블을 0.5이상을 1로 보듯이 임의로 사용자가 지정해줘야 할까요? predict가 label에 맞게 나오게 할수는 없나요?

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 클래스 레이블을 출력하는 predict_classes 메서드가 있지만 향후 삭제될 예정입니다. 대신 np.argmax(model.predict(x_test), axis=-1) 처럼 사용하시면 가장 높은 확률의 레이블 인덱스를 얻을 수 있습니다. 감사합니다.

      좋아요

      응답
      1. SHJ

        답변 빠르게 해주셔서 정말 감사합니다.

        포스팅도 정말 유익하고 도움이 많이 되었습니다. 다시 한번 감사합니다.

        Liked by 1명

댓글 남기기

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