Search

[머신러닝] 이진 분류(binary classification) - 영화 리뷰 분석 / 활성화 함수를 쓰는 이유

Created
2021/03/20 13:46
Tags
Created 1
Created 2
2021/03/20
Created 3
Created 4
subtitle
영화 리뷰 분석 / 활성화 함수를 쓰는 이유

데이터 처리

1. 데이터 불러오기
num_words = 10000 : 최빈 단어 순으로 1만 개만 사용하겠다는 의미이다.
test_data : 리뷰의 목록, 각 리뷰는 단어 인덱스의 리스트. [5, 2185, 3, ...]
train_labels, test_labes : 부정을 나타내는 0, 긍정을 나타내는 1의 리스트
from keras.datasets import imdb (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000) train_data[0] train_labels[0]
Python
복사
2. 데이터 처리
신경망에 리스트 타입을 주입할 수 없으니 텐서로 바꾼다.
리스트를 텐서로 바꾸는 방법은 2가지가 있다.
같은 길이가 되도록 리스트에 패딩 추가.
(samples, sequence_length) 크기의 정수 텐서로 변환
정수 텐서를 다룰 층을 신경망의 첫번째 층으로 사용(Embedding layer)
리스트를 원-핫 인코딩(one-hot encoding)하여 0과 1의 벡터로 변환
ex) [3, 5]를 인덱스 3과 5의 위치는 1이고 그 외는 모두 0인 10000차원의 벡터로 각각 변환
부동 소수 벡터 데이터를 다룰 Dense 층을 신경망의 첫번째 층으로 사용
아래는 두번째 방법.
import numpy as np def vectorize_sequence(seq, dimension=10000) : #크기가 (len(seq), dimension )이고 모든 원소가 0인 행렬 result = np.zeros((len(seq), dimension )) for i, s in enumerate(seq) : result[i, s] = 1. #특정 인덱스의 위치를 1로 만든다. return result # 훈련 데이터 벡터화 x_train = vectorize_sequence(train_data) x_test = vectorize_sequence(test_data) # 레이블 벡터화 y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32')
Python
복사

신경망 모델 만들기

1. Dense 층을 쌓을 때 고려할 것은 2가지이다.
층의 수 : 얼마나 많은 층을 사용할 것인가
유닛 수 : 각 층에서 얼마나 많은 유닛을 둘 것인가.
2. 신경망 모델 만들기
활성화 함수가 필요한 이유
활성화 함수가 없다면 신경망 층은 (선형 연산인) 점곱과 덧셈 2개로 구성된다. 선형 연산의 결과를 다시 다음 층의 입력값으로 주게 되면, 그 다음층의 결과값도 선형 연산이 되어 층을 여러 개로 구성하는 의미가 없어진다. 그래서 은닉층의 결과를 풍부하게 만들기 위해서는 활성화 함수가 필요하다.
중간 은닉 층 : 활성화 함수 relu를 사용
마지막층 : 확률을 출력하기 위해서 시그모이드 함수 사용. (1에 가까우면 리뷰가 긍정일 가능성이 높다)
※relu : 음수를 0으로 만드는 함수
※시그모이드 : 임의의 값을 [0,1] 사이로 압축한다.
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'))
Python
복사
3. 모델 컴파일
마지막으로 손실 함수(오차를 구하는 함수)와 옵티마이저를 선택한다.
여기서는 binary_crossentropy 손실이 적합하다. 확률을 출력하는 모델을 사용할 때는 크로스엔트로피가 최선의 선택이다.
※크로스엔트로피 : 확률 분포 간의 차이를 측정한다. 여기서는 원본 분포와 예측 분포 사이를 측정한다.
mean_squared_error, mean_absolute_error: 회귀 문제에 사용되는 대표적인 손실 함수
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
Python
복사

훈련 검증하기

너무 많은 훈련으로 특정(훈련) 데이터에 과적합(오버피팅)되는 것을 막기 위해 검증 과정을 거친다.
1. 검증 데이터 만들기
훈련 데이터에서 일부를 떼어내어 검증 데이터로 만든다.
x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:]
Python
복사
2. 모델 훈련
512개의 샘플 = 1개의 미니 배치 20번의 에포크(횟수)만큼 훈련시킨다.
동시에 따로 떼어놓은 1만 개의 샘플에서 손실, 정확도를 측정해야 하므로, validation_data 매개변수에 위에서 만든 검증 데이터를 전달한다.
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))
Python
복사
3. 훈련 과정의 히스토리를 변수에 저장한다.
history_dict = history.history history_dict.keys()
Python
복사
4. 그래프
훈련과 검증의 손실 그리기
import matplotlib.pyplot as plt %matplotlib inline history_dict = history.history loss = history_dict['loss'] val_loss = history_dict['val_loss'] epochs = range(1, len(loss)+1) plt.plot(epochs, loss, 'go', label='Training loss') #go : green dot plt.plot(epochs, val_loss, 'g', label='Validation loss') #g : green line plt.title('Training & Validation Loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
Python
복사
훈련, 검증 정확도 그리기
plt.clf() #그래프 초기화하기 acc = history_dict['acc'] val_acc = history_dict['val_acc'] plt.plot(epochs, acc, 'go', label='Training acc') #go : green dot plt.plot(epochs, val_acc, 'g', label='Validation acc') #g : green line plt.title('Training & Validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
Python
복사
Overfitting (과적합)
너무 많은 학습을 시켜서 훈련 데이터에 편향된 판단을 하게 하는 오류이다.
아래 그래프를 보면 훈련 데이터는 갈수록 손실(오차)이 줄어들고, 정확도는 높아지지만 검증 데이터는 반대이다. 훈련 데이터의 레이블에 편향된 판단을 해서 새로운 데이터를 일반화하여 판단하지 못하는 것이다.
위에서 보면 손실이 4번 에포크 정도에서 역전되고 있으므로 4번까지만 진행한다.

재훈련

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=['acc']) model.fit(x_train, y_train, epochs=4, batch_size=512) result = model.evaluate(x_test, y_test) prtin(result)
Python
복사

정리

데이터 전처리 : 원본 데이터를 신경망에 텐서로 주입하기 전 데이터 표현을 적절하게 바꾼다.
relu 활성화 함수와 Dense 층을 쌓은 네트워크는 앞으로 자주 사용하게 될 것이다.
이진 분류 문제(출력 클래스가 2개)의 네트워크는 하나의 유닛과 sigmoid 활성화 함수를 가진 Dense 층으로 끝나야 한다. 이 결과값(출력)은 확률이므로, 0과 1사이의 스칼라 값이다.
이진 분류 문제에서 이런 스칼라 시그모이드 출력에 대해 사용할 손실 한수는 binary_crossentropy이다.
rmsprop 옵티마이저 : 일반적인 경우에서 충분히 많이 쓰인다.
검증 데이터에서 overfitting 문제를 확인해야 한다.
활성화 함수를 쓰는 이유
생물학적 뉴런은 한 개의 신호(입력)가 아니라 여러 신호를 받는다. 그러나 뉴런은 신호를 받을 때마다 매번 반응(출력)할 수 없으니 여러 신호의 합들이 특정 분계점을 넘어야만 반응을 한다. 딥러닝의 신경망에서는 '활성화 함수'가 이러한 특성을 재현한다. 입력 값들의 합을 활성화 함수에 전달하면 이 입력을 활성화할지 말지 결정한다. 입력값의 합이 충분하지 않다면 활성화 함수는 다음 층에 0에 가깝거나 혹은 0 혹은 그 이하를 전달할 것이다(활성화 함수마다 다르다).
딥러닝에서 활성화 함수는 '비선형 함수'를 써야하는 이유
딥러닝에서 다층 신경망을 구성하는 이유를 생각해야 한다. a의 기울기를 가진 직선을 식으로 나타내면 y = ax이다.
두 데이터 셋이 양쪽으로 예쁘게 나눠져 있다면 직선을 가운데 두는 것만으로도 이 둘을 충분히 분류할 수 있을 것이다.
하지만 두 데이터 셋의 데이터 포인트들이 산발적으로 나뉘어져있거나 태극 문양처럼 휘어져서 나타나는 경우 직선으로는 분류하기 힘들다.
다층 신경망을 구성하면 선형 분류로는 풀기 어려웠던 문제들을 비선형적으로 풀 수 있다.
이때 활성화 함수가 위의 y = ax와 같은 선형 함수라고 가정하자.
다층 신경망을 구성해도 결국 a(a(ax)) = a3x (a는 상수)로 선형 함수와 같은 결과를 낳는다.
선형 함수의 한계를 극복하기 위해 다층 신경망을 구성했는데, 이렇게 되면 그 의미가 없다.
그러므로 비선형 함수의 활성화 함수를 다층 신경망 내에서 사용하여 선형 함수의 한계를 극복한다.
손실 함수 : mse, binary-crossentropy
이진 분류 문제에서는 binary-crossentropy를 사용한다.
옵티마이저 : 일반적으로 옵티마이저는 rmsprop 이라는 걸 많이 쓴다.
오버피팅 낮추는 법 : epochs를 너무 많이 주면, 훈련 데이터에 오버피팅 되어서 테스트 데이터의 예측률이 떨어진다.
오버피팅 : 훈련 데이터의 학습에 편향되는 것
오버피팅 확인 : 훈련 데이터셋의 일부를 떼어내서 검증 데이터로 활용하고, 손실과 정확도 측면에서 훈련 데이터와 검증 데이터의 history를 비교하여 오버피팅 여부를 확인한다. 만약 epoch가 거듭될 수록 훈련 데이터의 손실은 하락, 정확도는 높아진 반면 검증 데이터의 손실은 상승, 정확도는 낮아졌다면 오버피팅된 것이다.
epochs 조절 : 오버피팅되었다면 epochs를 낮추면서 훈련 데이터의 학습에 편향되지 않도록 한다.