Sequential API
저는 지금 대학교에서 딥러닝 응용1 이라는 과목을 배우고 있는데, 강의에서 배운 내용을 복습도 하고 저만의 방식으로 배운 것을 기록을 하기 위해 이렇게 포스팅을 하게 되었습니다.
이미지 자료나 코드 모두 한경훈 교수님의 자료에서 참조하였으며, 한경훈 교수님의 블로그 링크를 첨부합니다.
블로그 링크 : https://sites.google.com/site/kyunghoonhan/keras?authuser=0
Mnist Data
keras는 딥 러닝 모델을 빌드하고 학습시키기 위한 TensorFlow의 상위 수준 API입니다.
keras는 3가지 종류의 API를 제공하는데 그 중에서 가장 유연성은 떨어지지만 그만큼 사용하기 쉬운 API가 Sequential API입니다. (Sequential API 이외에 Functional API, Subcalssing API 가 있습니다. 후에 포스팅 예정입니다.)
다음은 mnist 데이터를 예측하는 신경망의 예시입니다.
keras.layers 메소드로 쉽게 구현할 수 있습니다.
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
import tensorflow as tf
from tensorflow import keras
from keras import layers
import numpy as np
keras.__version__
'2.9.0'
from keras import Sequential
relu = layers.ReLU()
softmax = layers.Softmax()
dense1 = layers.Dense(50, input_shape=(784,))
dense2 = layers.Dense(10)
model = Sequential([dense1, relu, dense2, softmax])
Metal device set to: Apple M1
2023-06-06 19:20:36.556629: I tensorflow/core/common_runtime/pluggable_device/pluggable_device_factory.cc:305] Could not identify NUMA node of platform GPU ID 0, defaulting to 0. Your kernel may not have been built with NUMA support.
2023-06-06 19:20:36.556710: I tensorflow/core/common_runtime/pluggable_device/pluggable_device_factory.cc:271] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 0 MB memory) -> physical PluggableDevice (device: 0, name: METAL, pci bus id: <undefined>)
만든 모델을 확인해봅시다.
from tensorflow.keras.utils import plot_model
plot_model(model)
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 50) 39250
re_lu (ReLU) (None, 50) 0
dense_1 (Dense) (None, 10) 510
softmax (Softmax) (None, 10) 0
=================================================================
Total params: 39,760
Trainable params: 39,760
Non-trainable params: 0
_________________________________________________________________
위의 모델은 relu
층과 softmax
층을 분리하여 모델을 만들었지만
보통의 경우에는 다음과 같이 Dense
클래스의 인수로 설정해서 구성합니다.
model = Sequential([
layers.Dense(50, input_shape=(784,), activation="relu"),
layers.Dense(10, activation="softmax")
])
plot_model(model, show_layer_activations=True)
model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_2 (Dense) (None, 50) 39250
dense_3 (Dense) (None, 10) 510
=================================================================
Total params: 39,760
Trainable params: 39,760
Non-trainable params: 0
_________________________________________________________________
학습
model의 complie 메소드로 세부적인 학습설정을 할 수 있습니다.
아래의 모델은 optimizer는 SGD, 손실함수는 crossentropy, 평가지표는 accuracy를 사용하였습니다.
model = Sequential([
layers.Dense(50, input_shape=(784,), activation="relu"),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer="SGD",
loss="sparse_categorical_crossentropy",
metrics="accuracy")
손실함수의 경우 라벨이 정수일때는 sparse_categorical_crossentropy
, 라벨이 원 핫 인코딩 되어있을때는 categorical_crossentropy
를 사용합니다.
to_categorical
함수를 사용하여 라벨을 쉽게 원 핫 인코딩 할 수 있습니다.
from tensorflow.keras.utils import to_categorical
train_labels_one_hot = to_categorical(train_labels)
test_labels_one_hot = to_categorical(test_labels)
print(train_labels[0])
print(train_labels_one_hot[0])
5
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
학습률과 같은 옵티마이저의 세부사항을 조정하고싶다면 다음과 같이 해야 합니다.
optimizer = keras.optimizers.SGD(learning_rate=0.05)
model.compile(optimizer=optimizer,
loss="sparse_categorical_crossentropy",
metrics="accuracy")
저희가 다루는 데이터는 mnist 손글씨 데이터 입니다.
손글씨 이미지 한장은 28*28 의 크기를 가지고 있고 그 값은 0 ~ 255 사이의 픽셀의 밝기를 의미합니다.
따라서 데이터를 모두 255로 나누어주면 데이터는 0 ~ 1사이의 값을 가지게 됩니다.
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images / 255
학습 설정까지 마친 model은 fit
메소드로 학습을 시킬 수 있습니다.
이때 배치의 크기와 epochs를 설정합니다.
history = model.fit(train_images, train_labels, epochs=5, batch_size=128)
Epoch 1/5
2023-06-06 19:20:37.495256: W tensorflow/core/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 Hz
2023-06-06 19:20:37.612264: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
469/469 [==============================] - 2s 4ms/step - loss: 0.6483 - accuracy: 0.8295
Epoch 2/5
469/469 [==============================] - 2s 4ms/step - loss: 0.3337 - accuracy: 0.9055
Epoch 3/5
469/469 [==============================] - 2s 4ms/step - loss: 0.2904 - accuracy: 0.9175
Epoch 4/5
469/469 [==============================] - 2s 4ms/step - loss: 0.2618 - accuracy: 0.9253
Epoch 5/5
469/469 [==============================] - 2s 4ms/step - loss: 0.2401 - accuracy: 0.9318
fit
메소드는 학습과정이 기록된 객체를 리턴합니다.
이를 통해 각 epoch별 손실함수의 값과 평가지표의 값을 확인할 수 있습니다.
history.history
{'loss': [0.6483351588249207,
0.3336530327796936,
0.29039037227630615,
0.26177677512168884,
0.24005787074565887],
'accuracy': [0.8295000195503235,
0.9054833054542542,
0.9174500107765198,
0.9252833127975464,
0.9318000078201294]}
평가
모델에 대한 평가는 evaluate 메소드로 할 수 있습니다.
이때 학습데이터가 아닌 test 데이터 셋을 사용해야 합니다.
model = keras.Sequential([
layers.Dense(512, input_shape=(784,), activation="relu"),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer="RMSprop",
loss="sparse_categorical_crossentropy",
metrics="accuracy")
model.fit(train_images, train_labels, epochs=5, batch_size=128)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print("\n")
print(f"test_acc: {test_acc}")
Epoch 1/5
9/469 [..............................] - ETA: 3s - loss: 1.3705 - accuracy: 0.5946
2023-06-06 19:20:48.206735: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
469/469 [==============================] - 3s 6ms/step - loss: 0.2555 - accuracy: 0.9273
Epoch 2/5
469/469 [==============================] - 3s 6ms/step - loss: 0.1016 - accuracy: 0.9704
Epoch 3/5
469/469 [==============================] - 3s 6ms/step - loss: 0.0671 - accuracy: 0.9797
Epoch 4/5
469/469 [==============================] - 3s 6ms/step - loss: 0.0492 - accuracy: 0.9850
Epoch 5/5
469/469 [==============================] - 3s 6ms/step - loss: 0.0372 - accuracy: 0.9887
29/313 [=>............................] - ETA: 1s - loss: 0.0554 - accuracy: 0.9860
2023-06-06 19:21:02.880950: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
313/313 [==============================] - 1s 4ms/step - loss: 0.0722 - accuracy: 0.9795
test_acc: 0.9794999957084656
모델의 예측확률분포를 알고싶다면 predict 메소드를 사용합니다.
predictions = model.predict(test_images)
print(predictions[0])
102/313 [========>.....................] - ETA: 0s
2023-06-06 19:21:04.170892: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
313/313 [==============================] - 1s 2ms/step
[1.4570821e-09 1.7129452e-11 1.0004075e-06 3.3091777e-07 1.7591903e-12
3.3679076e-10 7.1158764e-15 9.9999857e-01 1.0977904e-09 1.3834070e-07]
predictions
을 통해 confusion matrix를 만들 수 있습니다.
confusion = np.zeros((10,10), dtype=int)
for k in range(len(test_images)):
i = test_labels[k] # k 번째 데이터의 실제 값
j = np.argmax(predictions[k]) # k 번째 데이터에 대한 예측 값(확률이 가장 높은 index)
confusion[i][j] += 1
print(confusion)
[[ 968 1 1 0 1 1 2 2 2 2]
[ 0 1129 3 0 0 1 2 0 0 0]
[ 3 1 1019 0 2 0 2 4 1 0]
[ 0 0 12 973 1 6 0 8 3 7]
[ 1 0 3 0 971 0 2 1 0 4]
[ 2 0 0 4 1 872 7 2 2 2]
[ 2 3 4 1 5 2 938 1 2 0]
[ 0 3 11 0 0 1 0 1009 0 4]
[ 7 0 16 2 5 5 1 5 925 8]
[ 1 2 0 2 8 1 0 4 0 991]]
sklearn을 사용하면 같은 결과를 쉽게 얻을 수 있습니다.
from sklearn.metrics import confusion_matrix
confusion = confusion_matrix(test_labels, np.argmax(predictions, axis=1))
print(confusion)
[[ 968 1 1 0 1 1 2 2 2 2]
[ 0 1129 3 0 0 1 2 0 0 0]
[ 3 1 1019 0 2 0 2 4 1 0]
[ 0 0 12 973 1 6 0 8 3 7]
[ 1 0 3 0 971 0 2 1 0 4]
[ 2 0 0 4 1 872 7 2 2 2]
[ 2 3 4 1 5 2 938 1 2 0]
[ 0 3 11 0 0 1 0 1009 0 4]
[ 7 0 16 2 5 5 1 5 925 8]
[ 1 2 0 2 8 1 0 4 0 991]]
CIFAR10 Data
CIFAR10 데이터는 케라스에서 불러올수 있습니다
from keras.datasets import cifar10
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 [==============================] - 62s 0us/step
Mnist Data 와 달리
데이터들은 3차원 텐서로 되어있고 그 안은 3차원 벡터들로 이루어져 있습니다.
print(train_images[0].shape)
(32, 32, 3)
plt.imshow
를 통해 이미지를 확인할 수 있습니다.
import matplotlib.pyplot as plt
plt.axis("off")
plt.imshow(train_images[0])
plt.show()
print(train_labels[0])
[6]
첫 번째 이미지 데이터의 라벨은 6이며 라벨은 다음과 같이 대응합니다.
개구리 이미지였네요
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'sheep', 'truck']
데이터가 3차원 텐서인 이유는 기존 2차원이였던 이미지에 채널이 추가되었기 때문입니다.
채널을 추가함으로서 이미지의 색상을 입힐 수 있습니다.
위의 train_images[0].shape
을 통해 데이터의 shape을 알 수 있었는데요, 순서대로 H(height),W(width),C(channel)을 뜻합니다.
케라스와 matplotlib 에서는 H*W*C 포맷을 따르지만 파이토치에서는 C*H*W 포맷을 따릅니다.
다음 이미지를 통해 각 포맷이 이미지를 어떻게 설명하는지 알 수 있습니다.
이미지를 몇 장 확인해 볼까요?
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
plt.xticks([])
plt.yticks([])
plt.imshow(train_images[i])
plt.xlabel(class_names[train_labels[i][0]])
plt.show()
input 데이터의 크기가 다르기 때문에 이전에 썼던 모델을 그대로 사용할 수 없습니다.
하지만 input_shape
만 조절하면 이전과 거의 동일한 방법으로 쉽게 모델을 만들 수 있습니다.
train_images = train_images.reshape((50000, 32 * 32 * 3))
train_images = train_images / 255
test_images = test_images.reshape((10000, 32 * 32 * 3))
test_images = test_images / 255
model = keras.Sequential([
layers.Dense(512, input_shape=(32 * 32 * 3,), activation="relu"),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer="RMSprop",
loss="sparse_categorical_crossentropy",
metrics="accuracy")
model.fit(train_images, train_labels, epochs=15, batch_size=128)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print("\n")
print(f"test_acc: {test_acc}")
Epoch 1/15
2023-06-06 22:18:30.067735: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
391/391 [==============================] - 4s 10ms/step - loss: 2.4656 - accuracy: 0.2397
Epoch 2/15
391/391 [==============================] - 4s 9ms/step - loss: 1.8256 - accuracy: 0.3436
Epoch 3/15
391/391 [==============================] - 4s 10ms/step - loss: 1.7243 - accuracy: 0.3855
Epoch 4/15
391/391 [==============================] - 4s 9ms/step - loss: 1.6619 - accuracy: 0.4084
Epoch 5/15
391/391 [==============================] - 4s 9ms/step - loss: 1.6190 - accuracy: 0.4248
Epoch 6/15
391/391 [==============================] - 4s 9ms/step - loss: 1.5835 - accuracy: 0.4384
Epoch 7/15
391/391 [==============================] - 4s 9ms/step - loss: 1.5578 - accuracy: 0.4483
Epoch 8/15
391/391 [==============================] - 4s 9ms/step - loss: 1.5311 - accuracy: 0.4589
Epoch 9/15
391/391 [==============================] - 4s 9ms/step - loss: 1.5109 - accuracy: 0.4669
Epoch 10/15
391/391 [==============================] - 4s 9ms/step - loss: 1.4930 - accuracy: 0.4732
Epoch 11/15
391/391 [==============================] - 4s 9ms/step - loss: 1.4745 - accuracy: 0.4789
Epoch 12/15
391/391 [==============================] - 4s 9ms/step - loss: 1.4564 - accuracy: 0.4828
Epoch 13/15
391/391 [==============================] - 3s 9ms/step - loss: 1.4473 - accuracy: 0.4881
Epoch 14/15
391/391 [==============================] - 4s 9ms/step - loss: 1.4324 - accuracy: 0.4941
Epoch 15/15
391/391 [==============================] - 4s 9ms/step - loss: 1.4169 - accuracy: 0.5008
13/313 [>.............................] - ETA: 1s - loss: 1.4993 - accuracy: 0.4712
2023-06-06 22:19:24.975474: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:113] Plugin optimizer for device_type GPU is enabled.
313/313 [==============================] - 2s 5ms/step - loss: 1.5103 - accuracy: 0.4766
test_acc: 0.4765999913215637
하지만 이런 기본적인 신경망으론 높은 정확도를 기대하긴 힘듭니다.
다음에는 IMDB 데이터셋을 통해 자연어처리를 활용하여 이진분류를 하는 모델을 만들어 보겠습니다.