오늘부터 딥러닝의 2일차입니다.
loss는 내 예측값을 무엇과 비교할려는지 선택하는 옵션입니다. optimizer는 에러값을 어떻게 줄여나가는지 방법을 선택하는 옵션입니다.
은닉층 또는 히든레이어라 부르는 해당 레이어는 입력층과 출력층 사이에 가려져 있다고 해서 은닉층이라고 불립니다. 은닉층을 사용하면 성능이 향상되지만 무조건 과하게 사용한다고 무조건 성능이 향상되는 것은 아닙니다.
은닉층의 특징은 레이어 즉, 은닉층이 쌓일수록 Low-Level에서 High-Level의 특징(feature)을 새롭게 뽑아냅니다. 레이어 내부에 있는 노드의 수는 해당 Level에서 추출하려는 특징(feature)의 수를 의미합니다. 여기서 Level은 레이어라고 이해하면 됩니다.
만약 레이어를 증가시켜서 성능이 유지가 됐다면 이미 feature를 충분히 추출했다는 것을 의미합니다.
결론적으로 다시 정리해보자면 만약 은닉층이 추가되어 레이어가 증가되었을 때, 연결된 노드로부터 기존에 없던 새로운 특징(feature)을 추출합니다. 이것을 Feature Reoresntation이라 합니다. 혹은 Feture Learning이라고도 합니다. 사람이 직접 feature를 만드는 것이 아니라 머신이 직접 학습하여 새로운 feture를 만드는 것이 딥러닝의 의의입니다.
※학습이 잘 되어있다는 의미는 에러를 줄여나가는 방향으로 가중치를 업데이트 한다는 것을 의미합니다.(업데이트는 기존의 것을 고쳐서 살려 쓰는 것 입니다.
▶실습
회귀와 분류 모두 은닉층에 대한 실습을 진행했지만 멀티분류에 대해서만 포스팅하겠습니다.
전반적인 사용방법은 똑같지만 모델을 쌓아주는 .add()
을 진행하는 단계에서 InputLayer와 OutputLayer를 쌓는 사이에 은닉층을 쌓아줍니다.
keras.backend.clear_session()
model = keras.models.Sequential()
model.add(keras.layers.Input(shape=(4,)))
model.add(keras.layers.Dense(32, activation='relu')) #은닉층
model.add(keras.layers.Dense(32, activation='relu')) #은닉층
model.add(keras.layers.Dense(3, activation='softmax'))
model.compile(loss=keras.losses.categorical_crossentropy, metrics=['accuracy'], optimizer='adam')
model.summary()
해당 코드를 보게 되면 기본 틀은 분류 모델을 만들 때와 같지만 입력층과 출력층 사이에 은닉층을 삽입한 코드를 볼 수 있습니다. 여기서 model.add(keras.layers.Dense(32, activation='relu'))
를 자세히 살펴보면 출력층과 같은 Dense()
함수를 사용하는 것을 알 수 있습니다만 노드수가 출력층과는 다른 것을 확인할 수 있습니다. 노드 수는 사용자가 설정할 수 있으며 은닉층의 갯수도 사용자가 선택해서 몇 개의 층으로 할 지 선택할 수 있습니다. 하지만 층이 많아진다고 혹은 노드가 많아진다고 성능이 좋아지는 것은 아닙니다.
나머지 과정은 멀티분류와 크게 다를 것이 없습니다.
Feature Enginerring은 주어진 데이터를 의사결정에 도움이 되도록 적절하게 만드는 것을 의미합니다. 머신러닝의 경우는 사람이 직접 만드는 것이고 딥러닝은 학습을 통해 진행합니다.
Deep Learning은 얼마나 깊이 층을 쌓냐에 따라 알고리즘이 알아서 돌아가기 때문에 제대로 올바르게 학습한다면 유용한 특징을 뽑아줍니다.
Feature Learning은 자동화가 되어 좋은 특징을 만들어 냅니다.
Tabular 데이터는 이미 잘 정제되어 있기 때문에 머신러닝이 딥러닝보다 성능이 더 좋지만 그림 혹은 텍스트 데이터에서는 특징을 뽑아내기 너무 어렵기 때문에 딥러닝이 우세합니다. 즉 딥러닝과 머신러닝의 차이는 무엇이 언제 어떻게 필요한지를 생각하면 됩니다.
MNIST는 딥러닝(이미지)에서 Hello World!!와 같이 첫걸음이라고 할 수 있습니다.
▶필요 패키지 불러오기
먼저 필요한 패키지를 불러옵니다.
import tensorflow as tf
from tensorflow import keras
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import random as rd
from sklearn.metrics import accuracy_score
▶데이터 가져오기
그리고 keras에서 mnist의 데이터를 제공해주기 때문에 해당 데이터를 불러옵니다.
(train_x, train_y), (test_x, test_y) = keras.datasets.mnist.load_data()
keras에서 데이터를 불러올 때는 머신러닝에서 split 데이터를 할 때와 다른 형태로 사용됩니다.
데이터를 불러왔다면 이제 데이터의 모양을 확인합니다.
train_x.shape, train_y.shape, test_x.shape, test_y.shape
해당 데이터의 모양을 확인한면 (60000,28,28) 이런식으로 표기가 되는데 28 * 28의 데이터가 총 60000장 있다라고 해석하면 됩니다.
▶데이터 확인하기
데이터들이 각각 어떤 모습을 하고 있는 데이터인지 확인해주기 위해 아래와 같이 작성해줍니다.
id = rd.randrange(0,10000)
print(f'id = {id}')
print(f'다음 그림은 숫자 {train_y[id]} 입니다.')
plt.imshow(train_x[id], cmap='gray')
plt.show()
해당 코드를 작성하면 아래와 같은 결과가 나옵니다.
즉, train_y 6227번은 데이터 trian_x의 6227번째의 그림을 보고 0이라고 규정했다라고 해석하면 됩니다.
▶데이터 조작
해당 데이터는 차원이 하나 더 있기 때문에 데이터에 대해서 reshape를 사용한 조작이 필요합니다.
train_x = train_x.reshape([train_x.shape[0],-1])
test_x = test_x.reshape([test_x.shape[0],-1])
위 처럼 데이터 조작을 하고 train_x에 대해 모양을 살펴보면 (60000, 784)라는 결과값이 나옵니다. 784라는 값은 28*28값에서 나온 값입니다.
다음으로는 Min-Max Scaling을 진행해줍니다.
max_n, min_n = train_x.max(), train_x.min() #max=255, min=0
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(train_x)
train_x = scaler.transform(train_x)
test_x = scaler.transform(test_x)
max_n은 255, min_n은 0이 나옵니다.(색상에 대한 숫자값이기 때문입니다.)
정규화를 통해 0~1사이의 값으로 맞춰줬습니다.
▶One-Hot Encoding
다음으로는 원-핫 인코딩을 위한 타겟 y값에 대해 변수를 확인해 몇 개의 변수가 있는지 확인해줍니다.
from tensorflow.keras.utils import to_categorical
set(train_y) # 0-9출력이 됩니다.
len_y = len(set(train_y))
train_y = to_categorical(train_y, len_y)
test_y = to_categorical(test_y, len_y)
노드는 한개이지만 0-9까지의 변수에 대해서 원-핫 인코딩을 진행합니다.
▶모델링 시작
이제 데이터의 준비는 모두 끝났으며 본격적인 딥러닝 모델링으로 들어갑니다. 히든레이어는 사용하지 않았습니다.
keras.backend.clear_session()
model = keras.models.Sequential()
model.add(keras.layers.Input(shape=(train_x.shape[1])) )
model.add(keras.layers.Dense(10, activation='softmax') )
model.compile(loss=keras.losses.categorical_crossentropy, metrics=['accuracy'],
optimizer='adam')
model.summary()
전반적인 모델링은 일반 멀티분류 모델링과 비슷합니다.
▶Early Stopping
여기서 우리는 epochs의 제한을 풀어줬을 때 생기는 과적합을 Early Stopping을 사용해서 방지하겠습니다.
from tensorflow.keras.callbacks import EarlyStopping #필요패키지 호출
es = EarlyStopping(monitor='val_loss',
min_delta=0,
patience=5,
verbose=1,
restore_best_weights=True)
제한을 풀었을 때 발생하는 과적합에 대해서 방지해주는 역할을 해줍니다.
monitor는 관측대상입니다. val_loss
를 관측대상으로 사용하는 것을 명시해주는 것입니다. 모델을 학습(fit)하는 과정에서 쉽게 적용할 수 있습니다.
min_delta는 일종의 Threshold입니다. val_loss
가 최소한 우리가 지정한 값만큼 낮아져야 그것을 학습이 됬다고 인지합니다. 0으로 설정한 것은 최소한 나빠지지만 말자고 제한을 걸어주는 것입니다.(loss로 잡았기 때문에 낮아지는 것이 성능향상이지만 accuracy로 잡았을 경우 올라가야된다는 것이 성능향상입니다.)
patience는 몇 번이나 성능이 개선되지 않아도 참을지에 대해 명시해주는 것입니다.
restore_best_weights=True는 가장 성능이 좋았던 epochs의 가중치를 사용해줍니다.
▶모델학습
위 작업이 모두 끝나면 다시 모델 학습을 시작합니다.
model.fit(train_x, train_y, validation_split=0.2, callbacks=[es], verbose=1, epochs=50)
모델학습에서도 처음보는 옵션들이 등장합니다.
validation_split=0.2
은 train_x, train_y를 자체적으로 20% 분할해서 검증용 데이터로 쓰겠다는 것을 명시해주는 것입니다.
callbacks=[es]
는 아까 위에 명시해준 es를 사용해서 모델링을 진행하게 해줍니다.
▶모델 예측
학습이 끝난 모델로 예측 값을 만들어줍니다.
pred_train = model.predict(train_x)
pred_test = model.predict(test_x)
single_pred_train = pred_train.argmax(axis=1)
single_pred_test = pred_test.argmax(axis=1)
logi_train_accuracy = accuracy_score(train_y.argmax(axis=1), single_pred_train)
logi_test_accuracy = accuracy_score(test_y.argmax(axis=1), single_pred_test)
print('트레이닝 정확도 : {:.2f}%'.format(logi_train_accuracy*100))
print('테스트 정확도 : {:.2f}%'.format(logi_test_accuracy*100))
해당 train과 test에 대해서 예측 값을 만들어줍니다.
▶모델 평가
마지막으로 모델 평가를 진행합니다.
model.evaluate(test_x, test_y)
evaluate를 사용해서 모델을 평가합니다.
딥러닝의 2일차로 가장 기본이라 불리는 MNIST의 데이터를 다뤄봤습니다. 생각보다 모르는 함수들이 많이 나와서 당황했습니다. 익숙해져야될 함수들이 많다고 생각합니다.
※공부하고 있어 다소 틀린점이 있을 수 있습니다. 언제든지 말해주시면 수정하도록 하겠습니다.
※용어에 대해 조금 공부 더 해서 수정하겠습니다.