[keras 뿌시기] Sequential API

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 메소드로 쉽게 구현할 수 있습니다.

img

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)

png

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)

png

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])

png

[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 포맷을 따릅니다.

다음 이미지를 통해 각 포맷이 이미지를 어떻게 설명하는지 알 수 있습니다.
img

이미지를 몇 장 확인해 볼까요?

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()

png

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 데이터셋을 통해 자연어처리를 활용하여 이진분류를 하는 모델을 만들어 보겠습니다.