티스토리 뷰
chapter.7 딥러닝을 시작합니다¶
07-1 인공 신경망¶
- 패션 MNIST¶
판매할 패션 상품의 데이터는 아직 없지만 chapter7과 chapter8에선 패션 MNIST 데이터셋을 사용하자. 이 데이터셋은 10종류의 패션 아이템으로 구성되어 있다.
- MNIST는 머신러닝과 딥러닝을 처음 배울 때 많이 사용된다. 딥러닝에서는 MNIST 데이터셋이 유명하다. 이 데이터는 손으로 쓴 0 ~ 9 까지의 숫자로 이루어져 있다. MNIST와 크기, 개수가 동일하지만 숫자 대신 패션 아이템으로 이루어진 데이터가 바로 패션 MNIST이다.
패션 MNIST 데이터는 워낙 유명하기 때문에 많은 딥러닝 라이브러리에서 이 데이터를 가져올 수 있는 도구를 제공한다. 여기서는 텐서플로를 사용해 데이터를 불러오자. 딥러닝이나 텐서플로에 대해 설명하기 전에 먼저 패션 MNIST가 어떤 데이터인지 확인해 보자. 다음 명령으로 텐서플로의 케라스 패키지를 임포트하고 패션 MNIST 데이터를 다운로드하자.
from tensorflow import keras
(train_input, train_target), (test_input, test_target) = keras.datasets.fashion_mnist.load_data()
keras.datasets.fashion_mnist 모듈 아래 load_data() 함수는 친절하게 훈련 데이터와 테스트 데이터를 나누어 반환한다. 이 데이터는 각각 입력과 타겟의 쌍으로 구성되어 있다.
전달 받은 데이터의 크기를 확인해 보자.
print(train_input.shape, train_target.shape)
(60000, 28, 28) (60000,)
훈련 데이터는 60000개의 이미지로 이루어져 있다. 각 이미지는 28x28 크기이다. 타깃도 60000개의 원소가 있는 1차원 배열이다.
테스트 세트의 크기도 확인해 보자.
print(test_input.shape, test_target.shape)
(10000, 28, 28) (10000,)
테스트 세트는 10000개의 이미지로 이루어져 있다.
앞서 과일을 출력했던 것처럼 훈련 데이터에서 몇 개의 샘플을 그림으로 출력해보자.
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1, 10, figsize=(10,10))
for i in range(10):
axs[i].imshow(train_input[i], cmap='gray_r')
axs[i].axis('off')
plt.show()
크기가 28 x 28이다 보니 꽤 작고 흐릿하다. 또 6장에서 다루었던 것처럼 반전된 흑백 이미지이다. 신발과 다양한 종류의 옷들이 보인다. 이 샘플들의 타깃값을 확인해보자. 파이썬의 리스트 내포를 사용해서 처음 10개 샘플의 타깃값을 리스트로 만든 후 출력해보자.
print([train_target[i] for i in range(10)])
[9, 0, 0, 3, 0, 2, 7, 2, 5, 5]
패션 MNIST의 타깃은 0 ~ 9까지의 숫자 레이블로 구성된다. 각 숫자의 의미는 아직 모르지만 마지막 2개의 샘플이 같은 레이블(5)를 가지고 있다. 패션 MNIST에 포함된 10개의 레이블의 의미는 다음과 같다.
- 0 : 티셔츠
- 1 : 바지
- 2 : 스웨터
- 3 : 드레스
- 4 : 코트
- 5 : 샌달
- 6 : 셔츠
- 7 : 스니커즈
- 8 : 가방
- 9 : 앵클 부츠
마지막으로 넘파이 unique() 함수로 레이블 당 샘플 개수를 확인해 보자.
import numpy as np
print(np.unique(train_target, return_counts=True))
(array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=uint8), array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000]))
0 ~ 9 까지 레이블마다 정확히 6000개의 샘플이 들어있는 것을 볼 수 있다.
- 로지스틱 회귀로 패션 아이템 분류하기¶
이 훈련 샘플은 60000개나 되기 때문에 전체 데이터를 한꺼번에 사용하여 모델을 훈련하는 것보다 샘플을 하나씩 꺼내서 모델을 훈련하는 방법이 더 효율적이다. 이런 상황에 잘 맞는 방법이 확률적 경사 하강법이다.
앞서 4장에서 SGDClassifier를 사용할 때 표준화 전처리된 데이터를 사용했다. 그 이유는 확률적 경사 하강법은 여러 특성중 기울기가 가장 가파른 방향을 따라 이동한다. 만약 특성마다 값의 범위가 다르면 올바르게 손실 함수의 경사를 내려올 수 없다. 패션 MNIST의 경우 각 픽셀은 0 ~ 255 사이의 정숫값을 가진다. 이런 이미지의 경우 보통 255로 나누어 0 ~ 1 사이의 값으로 정규화한다. reshape() 메서드를 사용해 2차원 배열인 각 샘플을 1차원 배열로 펼쳐보자.
SGDClassifier는 2차원 입력을 다루지 못하기 때문에 각 샘플을 1차원 배열로 만들어야 한다.
train_scaled = train_input / 255.0
train_scaled = train_scaled.reshape(-1, 28*28)
print(train_scaled.shape)
(60000, 784)
그리고 SGDClassifier 클래스와 cross_validate 함수를 사용해 이 데이터에서 교차 검증으로 성능을 확인해 보자.
from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss='log', max_iter=5, random_state=42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores['test_score']))
0.8195666666666668
위에선 반복 횟수를 5로 지정하였다. 반복 횟수를 늘려도 크게 향상 되지는 않는다.
sc = SGDClassifier(loss='log', max_iter=10, random_state=42)
scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)
print(np.mean(scores['test_score']))
0.8313333333333333
- 인공 신경망¶
가장 기본적인 인공 신경망은 확률적 경사 하강법을 사용하는 로지스틱 회귀와 같다. 그렇다면 어떻게 인공 신경망으로 성능을 높일 수 있는 걸까? 앞서 로지스틱 회귀를 표현한 그림과 매우 비슷하다. 클래스가 총 10개이기 때문에 z10 까지 계산한다. z1 ~ z10을 계산하고 이를 바탕으로 클래스를 예측하기 때문에 신경망의 최종 값을 만든다는 의미에서 출력층이라고 한다.
가장 기본적인 인공 신경망은 확률적 경사 하강법을 사용하는 로지스틱 회귀와 같다. 그럼 확률적 경사 하강법을 사용한 로지스틱 회귀 모델이 가장 간단한 인공 신경망이라면 인공 신경망을 만들어도 성능이 좋아지지 않을 것 같다. 하지만 인공 신경망을 만드는 최신 라이브러리들은 SGDClassifier에는 없는 몇 가지 기능을 제공한다. 이런 기능 덕택에 더 좋은 성능을 얻을 수 있다.
- 텐서플로와 케라스¶
텐서플로는 구글이 2015년 11월 오픈소스로 공개한 딥러닝 라이브러리이다. 아래와 같이 임포트를 해보자.
import tensorflow as tf
딥러닝 라이브러리가 다른 머신러닝 라이브러리와 다른 점 중 하나는 그래픽 처리 장치인 GPU를 사용하여 인공 신경망을 훈련한다는 것이다. GPU는 벡터와 행렬 연산에 매우 최적화되어 있기 때문에 곱셈과 덧셈이 많이 수행되는 인공 신경망에 큰 도움이 된다.
텐서플로에서 케라스를 사용하려면 다음과 같이 임포트 해 보자.
from tensorflow import keras
- 인공 신경망으로 모델 만들기¶
여기에서는 앞서 로지스틱 회귀에서 만든 훈련 데이터 train_scaled와 train_target을 사용해 보자. 로지스틱 회귀에서는 교차 검증을 사용해 모델을 평가했지만, 인공 신경망에서는 교차 검증을 잘 사용하지 않고 검증 세트를 별도로 덜어내어 사용한다.
from sklearn.model_selection import train_test_split
train_scaled, val_scaled, train_target, val_target = train_test_split(
train_scaled, train_target, test_size=0.2, random_state=42)
훈련 세트에서 20% 를 검증 세트로 덜어 내었다. 훈련 세트와 검증 세트의 크기를 알아보자.
print(train_scaled.shape, train_target.shape)
(48000, 784) (48000,)
print(val_scaled.shape, val_target.shape)
(12000, 784) (12000,)
60000개 중에 12000개가 검증 세트로 분리되었다.먼저 훈련 세트로 모델을 만들고 그 다음 검증 세트로 훈련한 모델을 평가해 보자.
케라스의 레이어 패키지 안에는 다양한 층이 준비되어 있다. 가장 기본이 되는 층은 밀집층이다. 픽셀이 총 784개 이고 출력층은 총 10개이다. 이것들이 모두 연결된 선을 생각해보면 784 x 10 = 7840 개의 연결된 선이 있다. 정말 빽빽하기 때문에 밀집층이라고 부른다.
그럼 케라스의 Dense 클래스를 사용해 밀집층을 만들어 보자. 필요한 매개변수는 뉴런 개수, 뉴런의 출력에 적용할 함수, 입력의 크기이다.
dense = keras.layers.Dense(10, activation='softmax', input_shape=(784,))
10개의 패션 아이템을 분류하기 때문에 매개변수는 10이고 함수는 softmax 함수를 사용한다. 10개의 뉴런이 각각 몇개의 입력을 받는지 튜플로 지정한다. 여기에선 784개의 픽셀값을 받는다. 이제 이 밀집층을 가진 신경망 모델을 만들어보자.
model = keras.Sequential([dense])
소프트맥스와 같이 뉴런의 선형 방정식 계산 결과에 적용되는 함수를 활성화 함수라고 한다.
- 인공 신경망으로 패션 아이템 분류하기¶
케라스 모델은 훈련하기 전에 설정 단계가 있다. 이런 설정을 model 객체의 compile() 메서드에서 수행한다. 꼭 지정해야 할 것은 손실 함수의 종류이다. 그다음 훈련 과정에서 계산하고 싶은 측정값을 지정한다.
model.compile(optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics = ["accuracy"])
앞서 4장의 이진 분류에서 이진 크로스 엔트로피 함수를 사용했었다. 다중 분류에서는 크로스 엔트로피 손실 함수를 사용한다. 이름으로 이진 분류와 다중 분류의 손실 함수가 명확히 구분된다. 그런데 sparse라는 단어는 왜 붙었을까? 이진 크로스 엔트로피 손실을 위해 -log(예측확률)에 타깃값(정답)을 곱했다.
이진 분류에선 출력층의 뉴런이 하나이다. 이 뉴런이 출력하는 확률값 a(시그모이드 함수의 출력값)를 사용해 양성 클래스와 음성 클래스에 대한 크로스 엔트로피를 계산한다. 이진 분류의 출력 뉴런은 오직 양성 클래스에 대한 확률(a)만 출력하기 때문에 음성 클래스에 대한 확률은 1-a로 구할 수 있다. 역시 이진 분류의 타깃값은 양성 샘플을 경우에는 1, 음성 샘플일 경우에는 0이다. 0을 곱하면 어떤 계산이든지 모두 0이 되기 때문에 특별히 음성 샘플일 경우 1로 바꾸어(1-타깃값) 계산한다.
그럼 패션 MNIST 데이터셋과 같이 다중 분류일 경우엔 어떻게 계산을 할까? 출력층은 10개의 뉴런이 있고 10개의 클래스에 대한 확률을 출력한다. 첫 번째 뉴런은 티셔츠일 확률이고 두 번째 뉴런은 바지일 확률을 출력하자. 이진 분류와 달리 각 클래스에 대한 확률이 모두 출력되기 때문에 타깃에 해당하는 확률만 남겨 놓기 위해서 나머지 확률에는 모두 0을 곱한다.
예를 들어 샘플이 티셔츠일 경우 첫 번째 뉴런의 활성화 함수 출력인 a1에 크로스 엔트로피 손실 함수를 적용하고 나머지 활성화 함수 출력 a2 ~ a10 까지는 모두 0으로 만든다. 결국 신경망은 티셔츠 샘플에서 손실을 낮추려면 첫 번째 뉴런의 활성화 출력 a1의 값을 가능한 1에 가깝게 만들어야 한다. 이것이 바로 크로스 엔트로피 손실 함수가 신경망에 원하는 바이다.
예를 하나더 들어보자. 샘플이 바지일 경우는 두 번째 뉴런의 활성화 출력인 a2만 남겨야 한다. 그러려면 두 번째 원소만 1이고 나머지는 모두 0으로 타깃값을 준비하면 된다. 바지 샘플을 정확하게 분류하려면 신경망이 a2의 출력을 가능한 한 높여야 한다. 이와 같이 타깃값을 해당 클래스만 1이고 나머지는 모두 0인 배열로 만드는 것을 원-핫 인코딩 이라고 부른다.
따라서 다중 분류에서 크로스 엔트로피 손실 함수를 사용하려면 0,1,2 와 같이 정수로 된 타깃값을 원-핫 인코딩으로 변환해야 한다.
print(train_target[:10])
[7 3 5 8 6 9 3 3 9 9]
패션 MNIST 데이터 타깃값은 위와 같이 모두 정수로 되어 있다. 하지만 텐서플로에서는 정수로 된 타깃값을 원-핫 인코딩으로 바꾸지 않고 그냥 사용할 수 있다. 정수로된 타깃값을 사용해 크로스 엔트로피 손실을 계산하는 것이 바로 'sparse_categorical_crossentropy'이다. 타깃값을 원-핫 인코딩으로 준비했다면 compile() 메서드에 손실 함수를 loss='categorical_crossentropy'로 지정한다.
이제 compile() 메서드의 두 번째 매개변수인 metrics에 대해 알아보자. 케라스는 모델이 훈련할 때 기본으로 에포크마다 손실 값을 출력해 준다. 손실이 줄어드는 것을 보고 훈련이 잘되었다는 것을 알 수 있지만 정확도를 함께 출력하면 더 좋다. 이를 위해 metrics 매개변수에 정확도 지표를 의미하는 'accuracy'를 지정했다. 이제 아래와 같이 모델을 훈련해 보자.
model.fit(train_scaled, train_target, epochs=5)
Epoch 1/5 48000/48000 [==============================] - 1s 24us/sample - loss: 0.6231 - acc: 0.7883 Epoch 2/5 48000/48000 [==============================] - 1s 21us/sample - loss: 0.4720 - acc: 0.84030s - loss: 0.4728 - acc: 0.840 Epoch 3/5 48000/48000 [==============================] - 1s 21us/sample - loss: 0.4438 - acc: 0.8470 Epoch 4/5 48000/48000 [==============================] - 1s 24us/sample - loss: 0.4287 - acc: 0.8506 Epoch 5/5 48000/48000 [==============================] - 1s 22us/sample - loss: 0.4168 - acc: 0.8559
<tensorflow.python.keras.callbacks.History at 0x7f11f82f5710>
케라스는 친절하게 에포크마다 걸린 시간과 손실 정확도를 출력해 준다. 5번 반복에 정확도가 85%를 넘었다. 그럼 앞서 따로 떼어놓은 검증 세트에서 모델의 성능을 확인해 보자. 케라스에서 모델의 성능을 평가하는 메서드는 evaluate() 메서드이다.
model.evaluate(val_scaled, val_target)
12000/12000 [==============================] - 0s 14us/sample - loss: 0.4267 - acc: 0.8509
[0.4267007879813512, 0.8509167]
evaluate() 메서드도 fit() 메서드와 비슷한 출력을 보여 준다. 검증 세트의 점수는 훈련 세트 점수보다 조금 낮은 것이 일반적이다.
출처 : 혼자 공부하는 머신러닝 + 딥러닝
'혼자공부하는 머신러닝+딥러닝' 카테고리의 다른 글
혼자 공부하는 머신러닝+딥러닝(ch7-3 신경망 모델 훈련) (0) | 2021.05.26 |
---|---|
혼자 공부하는 머신러닝+딥러닝(ch7-2 심층 신경망) (0) | 2021.05.25 |
혼자 공부하는 머신러닝+딥러닝(ch6-3 주성분 분석) (0) | 2021.05.07 |
혼자 공부하는 머신러닝+딥러닝(ch6-2 k-평균) (0) | 2021.05.06 |
혼자 공부하는 머신러닝+딥러닝(ch6-1 군집 알고리즘) (0) | 2021.05.03 |