처음 Classification을 접할 때 한 번씩은 해본다는 손글씨 분류 문제입니다

간단하게 트레이닝하는 방법은 여기저기 많으니

조금 살을 붙여서 천천히 확인하면서 진행해보려 합니다

 

 

 

 

사용한 버전

  • tensorflow = 2.2.0
import tensorflow as tf
from tensorflow.keras.datasets.mnist import load_data
from tensorflow.keras.models import Sequential
from tensorflow.keras import models
from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.utils import plot_model


from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt

# mnist 불러오기
(x_train_full, y_train_full),(x_test, y_test) = load_data(path='mnist.npz')

# training data (70%), validation data (30%) 구분
x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full, test_size=0.3)

 

# data 갯수 확인
print('총 데이터: {}\t레이블: {}'.format(x_train_full.shape, y_train_full.shape))
print('학습 데이터: {}\t레이블: {}'.format(x_train.shape, y_train_full.shape))
print('검증 데이터: {}\t레이블: {}'.format(x_val.shape, y_val.shape))
print('테스트 데이터: {}\t레이블: {}'.format(x_test.shape, y_test.shape))

총 데이터: (60000, 28, 28) 레이블: (60000,)

학습 데이터: (42000, 28, 28) 레이블: (60000,)

검증 데이터: (18000, 28, 28) 레이블: (18000,)

테스트 데이터: (10000, 28, 28) 레이블: (10000,)

 

총 60000개의 28*28 이미지 데이터가 존재함을 확인할 수 있습니다

 

# 60000개의 데이터중 랜덤으로 5개 데이터 확인

plt.style.use('seaborn-white')
num_sample = 5

random_idxs = np.random.randint(60000, size=num_sample)

plt.figure(figsize=(num_sample*3, num_sample*2))
for i, idx in enumerate(random_idxs):
    img = x_train_full[idx, :]
    label = y_train_full[idx]
    
    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(img)
    plt.title("Index: {}, Label: {}".format(idx, label))

랜덤한 5개의 데이터

60000개의 데이터중 랜덤한 5개의 데이터를 출력해 보았고 셀을 반복하면 다른 5개의 데이터가 출력됩니다

출력되는 데이터의 개수를 조절하고 싶다면 num_sample을 조절해봅시다

 

 

 

 

# 데이터 전처리
x_train = x_train / 255.
x_val = x_val / 255.
x_test = x_test / 255.

y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
y_test = to_categorical(y_test)

x값의 전처리는 28*28 의 각 픽셀을 0~255의 범위에서 0~1의 범위로 변환합니다

(가독성을 위해 소숫점 한 자리로 잘랐습니다)

 

x_test[0] 의 전처리

 

y값의 전처리에 쓰인 to_categorical() 함수는

0으로 된 배열을 만들고 해당 위치에만 1을 넣는 함수입니다.

 

y_test[0] 의 전처리

 

 

 

# 모델 구성
model = Sequential([Input(shape=(28, 28), name='input'),
                  Flatten(input_shape=(28, 28), name='flatten'),
                  Dense(100, activation='relu', name='dense1'),
                  Dense(64, activation='relu', name='dense2'),
                  Dense(32, activation='relu', name='dense3'),
                  Dense(10, activation='softmax', name='output')])

 

Input layer 에서 flatten을 이용하여 1차원 배열로 변환 후

Dense를 이용하여 Fully connected layer를 구성하였습니다

 

활성화 함수는 중간층은 모두 relu를 이용하였고

다중 분류이므로 출력층은 softmax를 이용했습니다

 

 

 

model.summary()

summary() 함수를 사용하여 파라미터 수를 확인할 수 있습니다

 

 

plot_model(model)

plot_model() 함수를 사용하여 모델을 시각화하여 볼 수 있습니다

 

 

 

model.compile(loss='categorical_crossentropy',
             optimizer='sgd',
             metrics=['accuracy'])

모델을 어떻게 학습시키는지에 대한 설정입니다

 

loss (손실 함수)는 categorical_crossentropy

optimizer는 sgd

metrics (평가지표)는 accuracy를 이용하였습니다

 

loss는 개/고양이 같은 이중 분류의 경우 binary_crossentropy를 이용하기도 하며

optimizer는 모델에 따라 adam 등 다양한 다른 optimizer를 이용하기도 합니다

 

 

 

# 모델학습
history =  model.fit(x_train, y_train,
                    epochs=30,
                    batch_size=128,
                    validation_data=(x_val, y_val))

...

30 epochs를 트레이닝하였고

batch_size는 128로 설정하였습니다

 

진행중~~~

 

30초 멍때리면 끝

 

 

 

history.history.keys()

dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

history_dict = history.history

loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss)+1)
fig = plt.figure(figsize=(12, 6))

ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(epochs, loss, color='blue', label='train_loss')
ax1.plot(epochs, val_loss, color='red', label='val_loss')
ax1.set_title('Train ans Validation loss')
ax1.set_xlabel('Epochs')
ax1.set_ylabel('loss')
ax1.grid()
ax1.legend()

accuracy = history_dict['accuracy']
val_accuracy = history_dict['val_accuracy']

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, accuracy, color='blue', label='train_accuracy')
ax2.plot(epochs, val_accuracy, color='red', label='val_accuracy')
ax2.set_title('Train ans Validation Accuracy')
ax2.set_xlabel('Epochs')
ax2.set_ylabel('Accuracy')
ax2.grid()
ax2.legend()

plt.show()

validation인 빨간색 선을 주목해야합니다

loss는 계속 감소하고 accuracy는 계속 증가하는 것을 보아 완전히 학습되진 않은 것으로 추정됩니다 (underfitting 상태)

그래도 꽤 높은 값이니 그냥 진행해봅시다

 

 

 

# 모델 평가
model.evaluate(x_test, y_test) 
print(x_test.shape, y_test.shape)

313/313 [==============================] - 0s 1ms/step - loss: 0.1464 - accuracy: 0.9565 (10000, 28, 28) (10000, 10)

 

test data로 모델을 평가해보았을 때

loss : 0.1464  accuracy : 0.9565로 확인되었습니다

 

num_sample = 4

random_idxs = np.random.randint(10000, size=num_sample)


plt.figure(figsize=(num_sample*3, num_sample*2))
for i, idx in enumerate(random_idxs):
    img = x_test[idx, :]
    label = y_test[idx]

    pred_ysi = model.predict(x_test[random_idxs])
    arg_pred_yi = np.argmax(pred_ysi, axis=1)
    
    plt.subplot(1, len(random_idxs), i+1)
    plt.imshow(img)
    plt.title("Label: {}, predicted label: {}".format(np.argmax(label), arg_pred_yi[i]))

랜덤한 4개의 예측 데이터

이 셀을 반복적으로 run 하여 다른 랜덤 한 데이터를 볼 수도 있습니다

물론 틀린 값도 나올 수 있어요

 

# Confusin Maxtrix (혼동행렬)
from sklearn.metrics import confusion_matrix
import seaborn as sns
sns.set(style='white')

plt.figure(figsize=(9, 9))
pred_ys = model.predict(x_test)
arg_pred_y = np.argmax(pred_ys, axis=1)
cm = confusion_matrix(np.argmax(y_test, axis=-1), np.argmax(pred_ys, axis=-1))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('predicted Label')
plt.ylabel('True Label')
plt.show()

싸이킷런 (sklearn)을 import 해서 confusion martrix를 확인할 수도 있습니다

 

model.save('mnist_model.h5')

모델 저장 하기

+ Recent posts