이 포스팅은 혼자 공부하는 머신러닝 + 딥러닝 책을 공부하고 정리한것 입니다.
패션 MNIST
from tensorflow import keras
(train_input,train_target),(test_input,test_target) = keras.datasets.fashion_mnist.load_data()
keras.dataset.fashion_mnist 모듈 아래 load_data() 함수는 친절하게 훈련 데이터와 테스트 데이터를 나누어 반환한다. 이 데이터는 각각 입력과 타깃 쌍으로 구성되어 있다.
print(train_input.shape,train_target.shape)
훈련 데이터는 60,000 개의 이미지로 이루어져 있다. 각 이미지는 28 x 28 크기이다.
print(test_input.shape,test_target.shape)
훈련 데이터에서 몇 개의 샘플을 그림으로 출력해 보자
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()
print([train_target[i] for i in range(10)])
마지막으로 넘파이 unique() 함수로 레이블 당 샘플 개수를 확인해 보겠다.
import numpy as np
print(np.unique(train_target,return_counts=True))
로지스틱 회귀로 패션 아이템 분류하기
이 훈련 샘플은 60,000개나 되기 때문에 전체 데이터를 한꺼번에 사용하여 모델을 훈련하는 것보다 샘플을 하나씩 꺼내서 모델을 훈련하는 방법이 더 효율적으로 보인다. 이런 상황에 잘 맞는 방법이 확률적 경사 하강법이다.
SGDClassifier를 사용할 때 표준화 전처리된 데이터를 사용했다. 그 이유는 확률적 경사 하강법은 여러 특성 중 기울기가 가장 가파른 방향을 따라 이동한다. 만약 특성마다 값의 범위가 많이 다르면 올바르게 손실 함수의 경사를 내려 올 수 없다. 패션 MNIST의 경우 각 픽셀은 0~255 사이의 정숫값을 가진다. 이런 이미지의 경우 보통 255로 나누어 0~1 사이의 값으로 정규화 한다. 이는 표준화는 아니지만 양수 값으로 이루어진 이미지를 전처리할 때 널리 사용하는 방법이다.
SGDClassifer 는 2차원 입력을 다루지 못하기 때문에 각 샘플을 1차원 배열로 만들어야 한다.
train_scaled=train_input/255.0
train_scaled=train_scaled.reshape(-1,28*28)
print(train_scaled.shape)
784개의 픽셀로 이루어진 6만개의 샘플이 준비되었다.
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']))
여기에서는 SGDClassifier의 반복횟수(max_iter)를 5번으로 지정했다. 반복 횟수를 늘려도 성능이 크게 향상되지는 않는다.
만족할만한 수준이 아니다. 로지스틱 회귀 공식을 생각해보면
이 식을 패션 MNIST 데이터에 맞게 변형하면 다음과 같다.
바지와 티셔츠에 대한 선형 방정식은 배우 비슷하다. 동일하게 784개의 픽셀값을 그대로 사용하고 있다. 다만 바지에 대한 출력을 계산하기 위해 가중치와 절편은 다른 값을 사용해야 한다. 티셔츠와 같은 가중치를 사용한다면 바지와 티셔츠를 구분할 수 있을 리가 없다.
이런 식으로 나머지 클래스에 대한 선형 방정식을 모두 생각해 볼 수 있다. SGDClassifier 모델은 패션 MNIST 데이터의 클래스를 가능한 잘 구분할 수 있도록 이 10개의 방정식에 대한 모델 파라미터(가중치와 절편)을 찾는다.
z_티셔츠, z_바지와 같이 10개의 클래스에 대한 선형 방정식을 모두 계산한 다음에는 소프트맥스 함수를 통과하여 각 클래스에 대한 확률을 얻을 수 있다.
인공 신경망
가장 기본적인 인공 신경망은 확률적 경사 하강법을 사용하는 로지스틱 회귀와 같다. 그렇다면 어떻게 인공 신경망으로 성능을 높일 수 있을까?
클래스가 총 10개이므로 z10까지 계산한다. z1~z10을 계산하고 이를 바탕으로 클래스를 예측하기 때문에 신경망의 최종 값을 만든다는 의미에서 출력층(output layer) 라고 부른다.
인공 신경망에서는 z값을 계산하는 단위를 뉴런(neuron) 이라고 부른다. 하지만 뉴런에서 일어나는 일은 선형 계산이 전부이다. 이제는 뉴런이랑 표현 대신에 유닛(unit) 라고 부르는 사람이 더 많아지고 있다.
픽셀1, 픽셀2를 x1,x2 과 같이 바꾸었다. x1~x784까지를 입력층(input layer)라고 부른다.
z1을 만들기 위해 x1에 곱해지는 가중치는 w1,1 이라 쓰고 z2를 만들기 위해 픽셀1인 x1에 곱해지는 가중치는 w1,2라고 쓴다. 절편은 뉴런마다 하나씩이므로 순서대로 b1,b2와 같이 나타낸다.
확률적 경사 하강법을 사용한 로지스틱 회귀 모델이 가장 간단한 인공 신경망이라면 인공 신경망을 만들어도 성능이 좋아지지 않을 것 같다. 하지만 인공 신경망 모델을 만드는 최신 라이브러리들은 SGDClassifer에는 없는 몇 가지 기능을 제공한다.
import tensorflow as tf
from tensorflow import keras
인공 신경망으로 모델 만들기
여기에서는 앞서 로지스틱 회귀에서 만든 훈련 데이터 train_scaled 와 train_target를 사용하겠다. 로지스틱 회귀에서는 교차 검증을 사용해 모델을 평가했지만 인공 신경망에서는 교차 검증을 잘 사용하지 않고 검증 세트를 별도로 덜어내어 사용한다. 이렇게 하는 이유는 1. 딥러닝 분야의 데이터셋은 충분히 크기 때문에 검증 점수가 안정적이고, 2. 교차 검증을 수행하기전에는 훈련 시간이 너무 오래 걸리기 때문이다.
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)
print(train_scaled.shape,train_target.shape)
print(val_scaled.shape,val_target.shape)
먼저 훈련 세트로 모델을 만들고 검증 세트로 훈련한 모델을 평가해본다.
먼저 인공 신경망 그림의 오른쪽에 놓인 층을 만들어 보겠다. 이 층은 다음 그림처럼 10개의 패션 아이템을 분류하기 위해 10개의 뉴런으로 구성된다.
케라스의 레이어(keras.layer) 패키지 안에는 다양한 층이 준비되어 있다. 가장 기본이 되는 층은 밀집층(dense layer)이다. 왼쪽에 있는 784개의 픽셀과 오른쪽에 있는 10개의 뉴런이 모두 연결된 선을 생각해 보라. 정말 빽빽할 것이다.
이런 층을 양쪽의 뉴런이 모두 연결하고 있기 때문에 완전 연결층(fully connected layer)라고도 부른다.
케라스의 Dense 클래스를 사용해 밀집층을 만들어 보자. 필요한 매개변수는 뉴런 개수, 뉴런의 출력에 적용할 함수, 입력의 크기이다.
dense=keras.layers.Dense(10,activation='softmax',input_shape=(784,))
첫번째 매개변수로 뉴런 개수를 10개로 지정한다. 10개의 패션 아이템을 분류하기 때문이다. 10개의 뉴런에서 출력되는 값을 확률로 바꾸기 위해서는 소프트맥스 함수를 사용한다. 케라스층에서는 activation 매개변수에 이 함수를 지정한다. 만약 이진 분류라면 시그모이드 함수를 사용하기 위해 activation='sigmoid'와 같이 설정한다. 마지막으로 세 번째 매개변수는 입력값의 크기이다. 10개의 뉴런이 각각 몇개의 입력을 받는지 튜플로 지정한다. 여기에서는 784개의 픽셀 값을 받는다.
model=keras.Sequential(dense)
Sequential 클래스의 객체를 만들 때 앞에서 만든 밀집층의 객체 dense를 전달했다. 여기서 만든 model 객체가 바로 신경망 모델이다.
소프트맥스와 같이 뉴런의 선형 방정식 계산 결과에 적용되는 함수를 활성화함수 (activation function) 라고 부른다. 여기서는 이 값을 a로 표시한다.
인공 신경망으로 패션 아이템 분류하기
케라스 모델을 훈련하기 전에 설정 단계가 있다. 이런 설정을 model 객체의 compile() 메서드에서 수행한다. 꼭 지정해야 할 것은 손실 함수의 종류이다. 그다음 훈련 과정에서 계산하고 싶은 측정값을 지정한다.
model.compile(loss='sparse_categorical_crossentropy',metrics='accuracy')
패션 MNIST 데이터 셋과 같이 다중 분류일 경우에는
출력층은 10개의 뉴런이 있고 10개의 클래스에 대한 확률을 출력한다. 첫 번째 뉴런은 티셔츠일 확률이고 두 번째 뉴런은 바지일 확률을 출력한다. 이진 분류와 달리 각 클래스에 대한 확률이 모두 출력되기 때문에 타깃에 해당하는 확률만 남겨 놓기 위해서 나머지 확률에는 모두 0을 곱한다.
예를 들어 샘플이 티셔츠일 경우 첫 번째 뉴런의 활성화 함수 출력인 a1에 크로스 엔트로피 손실 함수를 적용하고 나머지 활성화 함수 출력 a2~a10 까지는 모두 0으로 만든다. 이렇게 하기 위해 티셔츠 샘플의 타깃값은 첫번쨰 원소만 1이고 나머지는 모두 0인 배열로 만들 수 있다.
결국 신경망은 티셔츠 샘플에서 손실을 낮추려면 첫 번째 뉴런의 활성화 출력 a1의 값을 가능한 1에 가깝게 만들어야 한다. 바로 이것이 크로스 엔트로피 손실 함수가 신경망에 원하는 바이다. 이와같이 타깃값을 해당 클래스만 1이고 나머지는 모두 0인 배열로 만드는 것을 원-핫 인코딩 (one-hot encoding) 이라고 부른다.
print(train_target[:10])
모두 정수로 되어 있지만 텐서플로에서는 정수로 된 타깃값은 원-핫 인코딩으로 바꾸지 않고 그냥 사용할 수 있다. 정수로된 타깃값을 사용해 크로스 엔트로피 손실을 계산한는 것이 바로 'sparse_categorical_crossentropy' 이다.
compile() 메서드의 두 번째 매개변수인 metrics에 대해 알아보자. 케라스는 모델이 훈련할때 기본으로 에포크마다 손실 값을 출력해 준다. 손실이 줄어드는 것을 보고 훈련이 잘되었다는 것을 알 수 있지만 정확도를 함께 출력하면 더 좋을것이다. 이를 위해 metrics 매개변수에 정확도 지표를 의미하는 'accuracy'를 지정했다.
model.fit(train_scaled,train_target,epochs=5)
성능이 조금 나아졌다. 케라스에서 모델의 성능을 평가하는 메서드는 evaluate() 메서드이다.
model.evaluate(val_scaled,val_target)
검증 세트의 점수는 훈련 세트 점수보다 조금 낮은 것이 일반적이다.
결론
이번 시간에는 28 x 28 크기의 흑백 이미지로 저장된 패션 아이템 데이터셋인 패션 MNIST 데이터셋을 사용했다. 먼저 로지스틱 손실 함수를 이용한 SGDClassifer 모델을 만들어 교차 검증 점수를 확인했다.
가장 인기 있는 딥러닝 라이브러리인 텐서플로와 케라스 API를 다루었다. 이 간단한 인공 신경망은 사실상 앞에서 만든 경사 하강법을 사용한 로지스틱 회귀 모델과 거의 비슷하다.
'Machine Learning > Basic' 카테고리의 다른 글
[7-3] 신경망 모델 훈련 (0) | 2021.04.13 |
---|---|
[7-2] 심층 신경망 (0) | 2021.04.13 |
[6-3] 주성분 분석 (0) | 2021.04.13 |
[6-2] k-평균 (0) | 2021.04.13 |
[6-1] 군집 알고리즘 (0) | 2021.04.13 |