Open In Colab

데이터 다운로드

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from tensorflow import keras

fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()

케라스 내 대표적인 예제 데이터인 fashion_mnist 데이터를 사용했습니다.

X_train_full.shape
(60000, 28, 28)
X_test.shape
(10000, 28, 28)

데이터는 총 7만개이고, 28행 28열 값 입니다.

X_train_full.dtype
dtype('uint8')

데이터 내 0부터 255까지 픽셀값이 들어가고, 70000 28 28개로 데이터 양이 꽤 많습니다.

그러므로 메모리를 절약하기 위해 uint8 자료형을 사용합니다.

plt.imshow(X_train_full[0], cmap = 'binary')
plt.axis('off')
plt.show()

첫번째 샘플은 부츠인것 같군요.

Sequential Model 구축

model = keras.models.Sequential()
# Flatten은 입력데이터의 shape을 일렬로 변경하는 클래스입니다.(reshape(-1,1)과 유사)
model.add(keras.layers.Flatten(input_shape=[28,28]))
# Dence는 이전 뉴런과 완전 연결된 밀집뉴런층을 의미합니다. 뉴런 수와 활성화 함수를 정의합니다.
model.add(keras.layers.Dense(300, activation = 'relu'))
model.add(keras.layers.Dense(100, activation = 'relu'))
model.add(keras.layers.Dense(10, activation = 'softmax'))

딥러닝 모델을 케라스를 통해서 구축합니다.

분류기 모델이기 때문에 마지막 활성화 함수를 소프트멕스로 해서 0~1 사이로 값을 출력하게 합니다.

model.summary()
Model: "sequential_1"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten_1 (Flatten)         (None, 784)               0         
                                                                 
 dense_3 (Dense)             (None, 300)               235500    
                                                                 
 dense_4 (Dense)             (None, 100)               30100     
                                                                 
 dense_5 (Dense)             (None, 10)                1010      
                                                                 
=================================================================
Total params: 266,610
Trainable params: 266,610
Non-trainable params: 0
_________________________________________________________________

모델의 정보를 summary 함수를 통해서 출력했습니다.

model.layers[1].get_weights()
[array([[-0.02904556,  0.01724293,  0.00127908, ..., -0.03015002,
          0.02537476,  0.02381387],
        [-0.01983445,  0.04740578, -0.07353676, ..., -0.03010103,
         -0.05431781,  0.02538292],
        [-0.04257569,  0.00067744, -0.01834078, ..., -0.06939676,
         -0.04211871,  0.05452839],
        ...,
        [ 0.05041559,  0.01164866, -0.01733586, ..., -0.06081748,
          0.03213695,  0.01312949],
        [ 0.0624944 , -0.04938972, -0.03898798, ..., -0.01868138,
         -0.01819434,  0.04299803],
        [ 0.06238632, -0.05809005,  0.03214301, ...,  0.06427555,
          0.02793135, -0.03146121]], dtype=float32),
 array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)]

각 층의 있는 파라미터 값을 get_weights 함수를 이용해서 출력할 수 있습니다.

모델 학습하기

model.compile(loss = 'sparse_categorical_crossentropy', # 다중 분류 손실 함수. Y가 원핫인코딩이 아닐때
              optimizer = 'sgd', # 확률적 경사 하강법 사용.
              metrics = ['accuracy']) # 학습을 진행할 때 마다 출력할 평가방식.

모델을 컴파일합니다. 이때 손실함수, 옵티마이저, 테스트 셋으로 측청하는 것을 입력해줍니다.

X_valid, X_train = X_train_full[:5000] / 255.0, X_train_full[5000:] / 255.0
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
X_test = X_test/255.0

이후 출처를 밝히겠지만 인터넷 내 블로그 코드를 보고 따라했는데요. 이 부분이 중간에 생략되어있어서 많은 혼란이 있었습니다.

왜냐하면 딥러닝에서 가장 중요한 것은 X 데이터의 스케일링입니다. 한 개의 데이터당 28 * 28 = 786 개의 값이 입력됩니다.

이 786개의 값이 스케일이 물론 같지만, 0~255 값을 가지게 되면 밑에 코드의 학습이 전혀 되지 않습니다.(정확도가 계속 0.1에 머무름)

반드시! 255로 나누어줘서 X 값의 범위를 0~1로 만들어야합니다. 긴 시간 시행착오를 겪었지만 피와 살이 되는 경험이였고 잊지 않을 것 같아요.

# X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, test_size=0.1, random_state=34)

history = model.fit(X_train, y_train, epochs = 30, # 에포크는 학습반복수
                    validation_data = (X_valid, y_valid))
Epoch 1/30
1719/1719 [==============================] - 14s 8ms/step - loss: 0.7189 - accuracy: 0.7660 - val_loss: 0.5232 - val_accuracy: 0.8210
Epoch 2/30
1719/1719 [==============================] - 12s 7ms/step - loss: 0.4830 - accuracy: 0.8316 - val_loss: 0.4391 - val_accuracy: 0.8532
Epoch 3/30
1719/1719 [==============================] - 12s 7ms/step - loss: 0.4382 - accuracy: 0.8471 - val_loss: 0.4311 - val_accuracy: 0.8524
Epoch 4/30
1719/1719 [==============================] - 11s 6ms/step - loss: 0.4107 - accuracy: 0.8562 - val_loss: 0.4112 - val_accuracy: 0.8538
Epoch 5/30
1719/1719 [==============================] - 11s 6ms/step - loss: 0.3917 - accuracy: 0.8620 - val_loss: 0.3939 - val_accuracy: 0.8576
Epoch 6/30
1719/1719 [==============================] - 11s 7ms/step - loss: 0.3749 - accuracy: 0.8676 - val_loss: 0.3780 - val_accuracy: 0.8642
Epoch 7/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3615 - accuracy: 0.8722 - val_loss: 0.3638 - val_accuracy: 0.8702
Epoch 8/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3502 - accuracy: 0.8766 - val_loss: 0.3508 - val_accuracy: 0.8750
Epoch 9/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3404 - accuracy: 0.8789 - val_loss: 0.3416 - val_accuracy: 0.8782
Epoch 10/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3309 - accuracy: 0.8826 - val_loss: 0.3549 - val_accuracy: 0.8754
Epoch 11/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3223 - accuracy: 0.8858 - val_loss: 0.3391 - val_accuracy: 0.8788
Epoch 12/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.3145 - accuracy: 0.8877 - val_loss: 0.3287 - val_accuracy: 0.8810
Epoch 13/30
1719/1719 [==============================] - 7s 4ms/step - loss: 0.3073 - accuracy: 0.8912 - val_loss: 0.3309 - val_accuracy: 0.8818
Epoch 14/30
1719/1719 [==============================] - 7s 4ms/step - loss: 0.2999 - accuracy: 0.8929 - val_loss: 0.3256 - val_accuracy: 0.8840
Epoch 15/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.2933 - accuracy: 0.8942 - val_loss: 0.3213 - val_accuracy: 0.8864
Epoch 16/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.2877 - accuracy: 0.8968 - val_loss: 0.3121 - val_accuracy: 0.8884
Epoch 17/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.2820 - accuracy: 0.8977 - val_loss: 0.3247 - val_accuracy: 0.8810
Epoch 18/30
1719/1719 [==============================] - 7s 4ms/step - loss: 0.2765 - accuracy: 0.9009 - val_loss: 0.3157 - val_accuracy: 0.8874
Epoch 19/30
1719/1719 [==============================] - 11s 7ms/step - loss: 0.2710 - accuracy: 0.9027 - val_loss: 0.3118 - val_accuracy: 0.8868
Epoch 20/30
1719/1719 [==============================] - 10s 6ms/step - loss: 0.2660 - accuracy: 0.9040 - val_loss: 0.3133 - val_accuracy: 0.8876
Epoch 21/30
1719/1719 [==============================] - 12s 7ms/step - loss: 0.2614 - accuracy: 0.9063 - val_loss: 0.3317 - val_accuracy: 0.8800
Epoch 22/30
1719/1719 [==============================] - 10s 6ms/step - loss: 0.2565 - accuracy: 0.9075 - val_loss: 0.3060 - val_accuracy: 0.8890
Epoch 23/30
1719/1719 [==============================] - 9s 5ms/step - loss: 0.2524 - accuracy: 0.9091 - val_loss: 0.3061 - val_accuracy: 0.8886
Epoch 24/30
1719/1719 [==============================] - 9s 5ms/step - loss: 0.2482 - accuracy: 0.9097 - val_loss: 0.2999 - val_accuracy: 0.8904
Epoch 25/30
1719/1719 [==============================] - 10s 6ms/step - loss: 0.2441 - accuracy: 0.9123 - val_loss: 0.2960 - val_accuracy: 0.8912
Epoch 26/30
1719/1719 [==============================] - 7s 4ms/step - loss: 0.2397 - accuracy: 0.9134 - val_loss: 0.2991 - val_accuracy: 0.8922
Epoch 27/30
1719/1719 [==============================] - 6s 4ms/step - loss: 0.2358 - accuracy: 0.9148 - val_loss: 0.3040 - val_accuracy: 0.8914
Epoch 28/30
1719/1719 [==============================] - 8s 5ms/step - loss: 0.2317 - accuracy: 0.9157 - val_loss: 0.2910 - val_accuracy: 0.8932
Epoch 29/30
1719/1719 [==============================] - 10s 6ms/step - loss: 0.2281 - accuracy: 0.9179 - val_loss: 0.3280 - val_accuracy: 0.8824
Epoch 30/30
1719/1719 [==============================] - 8s 4ms/step - loss: 0.2244 - accuracy: 0.9203 - val_loss: 0.2989 - val_accuracy: 0.8900

테스트와 트레인 셋으로 나눈뒤 모델을 학습합니다. 에포크가 지날때 마다 반드시 개선되지는 않습니다.

학습 시각화하기

import pandas as pd

pd.DataFrame(history.history).plot(figsize = (8,5))
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()

학습데이터는 에포크가 지날때마다 계속 개선이 되는것으로 보이나 val 데이터는 개선이 되었다가 감소하는걸 반복합니다.

테스트 데이터로 평가와 예측하기

model.evaluate(X_test, y_test)
313/313 [==============================] - 1s 2ms/step - loss: 0.3289 - accuracy: 0.8798
[0.32892856001853943, 0.879800021648407]

테스트 데이터로 모델을 평가했습니다. 과적합 되지 않고 비슷한 성능을 보입니다.

X_new = X_test[:3]
y_proba = model.predict(X_new)
y_proba.round(2)
array([[0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.01, 0.  , 0.99],
       [0.  , 0.  , 0.99, 0.  , 0.01, 0.  , 0.  , 0.  , 0.  , 0.  ],
       [0.  , 1.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  , 0.  ]],
      dtype=float32)

predict 함수를 이용하면 범주마다의 확률을 예측해줍니다.

y_proba.argmax(axis=-1)
array([9, 2, 1])

이전에는 predict_classes로 범주를 뽑아낼 수 있었으나 케라스 버전 업그레이드 이후 이 함수가 사라졌습니다.

argmax 함수를 통해 가장 큰 확률을 가진 범주를 추출했습니다.

회귀 데이터 다운로드

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

housing = fetch_california_housing()

X_train_full, X_test, y_train_full, y_test = train_test_split(housing.data, housing.target, random_state = 42)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, random_state = 42)

scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

california_housing 데이터를 이용해 주택가격을 예측하는 모델을 케라스를 이용해 딥러닝으로 구현해보겠습니다.

우선 딥러닝 모델 적용시 가장 중요한 스케일링을 진행합니다. 이때 데이터 리키지가 일어나는것 항상 조심해야겠죠.

회귀 모델 학습하기

model = keras.models.Sequential([
    keras.layers.Dense(30, activation = 'relu', input_shape = X_train.shape[1:]),
    keras.layers.Dense(1)
])
model.compile(loss = 'mean_squared_error', optimizer = keras.optimizers.SGD(lr = 1e-3))
history = model.fit(X_train, y_train, epochs = 20, validation_data =(X_valid, y_valid))
/usr/local/lib/python3.7/dist-packages/keras/optimizer_v2/gradient_descent.py:102: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.
  super(SGD, self).__init__(name, **kwargs)
Epoch 1/20
363/363 [==============================] - 2s 4ms/step - loss: 2.0042 - val_loss: 1.5977
Epoch 2/20
363/363 [==============================] - 1s 3ms/step - loss: 0.7294 - val_loss: 0.6821
Epoch 3/20
363/363 [==============================] - 1s 3ms/step - loss: 0.6309 - val_loss: 0.5893
Epoch 4/20
363/363 [==============================] - 1s 2ms/step - loss: 0.5921 - val_loss: 0.5399
Epoch 5/20
363/363 [==============================] - 1s 2ms/step - loss: 0.5632 - val_loss: 0.5151
Epoch 6/20
363/363 [==============================] - 1s 3ms/step - loss: 0.5404 - val_loss: 0.4985
Epoch 7/20
363/363 [==============================] - 1s 2ms/step - loss: 0.5214 - val_loss: 0.4943
Epoch 8/20
363/363 [==============================] - 1s 2ms/step - loss: 0.5060 - val_loss: 0.4857
Epoch 9/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4932 - val_loss: 0.4814
Epoch 10/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4827 - val_loss: 0.4671
Epoch 11/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4734 - val_loss: 0.4758
Epoch 12/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4657 - val_loss: 0.4704
Epoch 13/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4588 - val_loss: 0.4573
Epoch 14/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4529 - val_loss: 0.4710
Epoch 15/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4478 - val_loss: 0.4527
Epoch 16/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4430 - val_loss: 0.4503
Epoch 17/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4386 - val_loss: 0.4426
Epoch 18/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4349 - val_loss: 0.4592
Epoch 19/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4313 - val_loss: 0.4514
Epoch 20/20
363/363 [==============================] - 1s 2ms/step - loss: 0.4281 - val_loss: 0.4557

우선 회귀모델이니 마지막 층에 소프트맥스 같은 활성화함수는 필요하지 않습니다. 또 손실함수는 MSE를 사용하였네요.

회귀 모델 평가와 예측

mse_test = model.evaluate(X_test, y_test)
162/162 [==============================] - 0s 1ms/step - loss: 0.4182

테스트 데이터로 모델을 평가했는데, 로스값이 더 좋은 모습입니다.

X_new = X_test[:3]
model.predict(X_new)
array([[0.54969954],
       [1.6468697 ],
       [3.592553  ]], dtype=float32)

모델을 이용해 새 데이터를 예측했습니다.

plt.plot(pd.DataFrame(history.history))
plt.grid(True)
plt.gca().set_ylim(0,1)
plt.show()

에포크가 진행될수록 트레인 데이터의 mse는 감소합니다. 하지만 valid 데이터는 일정수준이상이 되면 진동합니다.

신경망 하이퍼파라미터 튜닝하기

머신러닝의 다른 모델들과 다르게 신경망은 매우 많은 하이퍼 파라미터가 있습니다.

예시로 배치 사이즈, 옵티마이저, 학습률, 은닉층의 수, 활성화함수, loss, 드롭아웃 등 많습니다.

우선 배치 사이즈의 경우 크면 메모리 문제가 많이 생깁니다. 또 훈련 초기 종종 불안정하기 훈련되기도 합니다.

만약 너무 작으면 학습시간이 오래걸리고 노이즈도 커지는데요. gpu를 사용하기 때문에 2의 제곱수를 많이 사용합니다.(주로 32)

이 부분은 의견이 많이 갈리기도 하는 부분이에요.

다음은 옵티마이저 입니다. 간단하게 사진으로 대체했는데 사실 정확히 이해하기 힘듭니다. 따로 시간내서 공부해야합니다.

일반적으로 Adam이 성능이 좋다고 합니다.

옵티마이저.png

학습률은 디폴트가 0.01으로 크면 발산하고 작으면 지역 최저점에 머물 확률이 높아요. 옵티마이저 내부옵션으로도 사용됩니다.

활성화함수는 은닉층에서 주로 Relu를 주로 사용합니다. 모델에 따라 tanh등을 쓰기도 합니다.

은닉층은 많아지면 학습시간 문제, 과적합 문제가 많이 생깁니다.

느낀점

이번엔 텐서플로우 내 케라스라는 패키지를 이용해 딥러닝을 간단히 맛보았습니다.

케라스는 고수준 패키지로 쉽게 사용할 수 있으나 세밀한 작업은 하지 못하는데요.

간단한 예제 데이터를 통해 딥러닝이라는 것을 해봤습니다. 오늘 느낀것은 공부할 것이 참 많겠다 생각이 들었어요.

계속 발전하는 분야라서 인터넷 글마다 정보도 다르고 한데, 저도 발전되는 그 곳에 함께하고 싶네요.

더 열의를 다지는 시간이였던것 같아요.

참고