9 분 소요

Code

[Notice] download here

Observing the dataset

MNIST 데이터셋에는 0부터 9까지 10종류의 숫자가 여러 2차원 이미지 데이터 형태로 저장되어 있으며, RGB 차원은 따로 없는 흑백사진이다. In the MNIST dataset, 10 types of numbers from 0 to 9 are stored in the form of several two-dimensional image data, and it is a black-and-white photograph without 3 RGB dimensions.

각 이미지 해상도는 28x28 픽셀 단위로 이루어져 있으며, 학습 데이터 6만장 테스트 데이터 1만장으로 구성되어 있다. Each image resolution consists of 28x28 pixel units, and consists of 60,000 training data sheets and 10,000 test data sheets.

해당 데이트셋을 활용하여 이미지 분류를 수행하는 모델을 학습시키는 것을 목표로 한다! It aims to train a model that performs image classification using the data set!

Loading the dataset

import tensorflow as tf

((X_train, y_train), (X_test, y_test)) = tf.keras.datasets.mnist.load_data()

X_train, X_test = X_train / 255.0, X_test / 255.0 # normalization

# reshape for image generator
X_train = X_train.reshape(60000, 28, 28, 1)
X_test = X_test.reshape(10000, 28, 28, 1)

print(X_test)

# print(X_train.shape, X_test.shape)
# print(y_train.shape, y_test.shape)
    [5 0 4 ... 5 6 8]

상기 종속변수 결과는 각 클래스에 해당하는 인덱스를 나타낸다. The result of the dependent variable indicates an index corresponding to each class.

가령, 맨 앞의 5는 모델이 새로운 데이터에 대하여 숫자 5일 것으로 예측하는 결과값일 것이다. For example, the first 5 will be the result value that the model predicts to be the number 5 for new data.

또한, 우리는 각 픽셀마다 [0, 255] 색상값이 할당된 이미지 데이터에 대하여 정규화(normalization)를 수행하여 학습 속도를 빠르게 한다. In addition, we perform normalization on the image data to which the [0, 255] color value is assigned to each pixel to fasten the learning speed.

그리고, 우리는 이미지 증강(image augmentation) 기법을 사용하여 학습 데이터 수를 늘려 더욱 정확한 모델 성능을 끌어내고자 한다. And, we use image augmentation to increase the number of training data to get more accurate model performance.

이를 위해, 주어진 데이터를 쓰임에 맞게 reshape 한다. To do this, the given data is reshaped for use.

이것은 RGB 열을 추가하는 작업으로, 흑백사진이기 때문에 3이 아닌 1을 할당한 모습이다. This is an operation to add an RGB column, and since it is a black-and-white photo, 1 is assigned instead of 3.

Visualizing the dataset

import matplotlib.pyplot as plt

figure = plt.figure()
figure.set_size_inches(15, 5)

axes = []
for i in range(1,6):
    axes.append(figure.add_subplot(1, 5, i))

for i in range(5):
    axes[i].matshow(x_train[i])

image

상기 이미지 결과표는 MNIST 데이터셋에서 랜덤하게 5가지 숫자 이미지를 나타낸다. The image result table shows five numerical images randomly from the MNIST dataset.

이러한 이미지와 같은 새로운 데이터들에 대한 다중 클래스 숫자 분류를 수행하는 CNN 모델을 구축해보자! Let’s build a CNN model that performs multi-class numeric classification on new data such as images!

Building the model

from keras.models import Sequential 
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D, Dense, Flatten, Dropout, Activation, BatchNormalization
from keras.optimizers import Adam
cnn_model = Sequential()

cnn_model.add(Conv2D(10, 3, strides = 1, padding = 'same', activation = 'relu', input_shape = (28, 28, 1)))
cnn_model.add(MaxPooling2D((2, 2)))

cnn_model.add(Conv2D(10, 3, strides = 1, padding = 'same', activation = 'relu'))
cnn_model.add(MaxPooling2D((2, 2)))

cnn_model.add(Flatten())

for i in range(3): # 3 hidden layers 
    cnn_model.add(Dense(100))
    cnn_model.add(BatchNormalization())
    cnn_model.add(Activation(activation = 'relu'))

cnn_model.add(Dropout(0.2))
cnn_model.add(Dense(10, activation = 'softmax')) # class: 10, multiclass --> softmax

본 과제에서 우리는 순차적(‘Sequential’) CNN을 구축한다:

  • Number of kernels(= filters): 10 (3x3)
  • Input_shape: 28x28 (RGB = 1)
  • Pooling: 2x2
    • 실험 결과 MaxPooling이 AveragePooling보다 더 좋은 정확도를 보여주었다. In this task, we build a ‘Sequential’ CNN.
  • Number of FCL(= hidden layers): 3 (# neurons=100, activation=relu)
  • Output Layer: softmax (multiclass classification task)
  • BatchNormalization(): internal covariate shift 문제 해결을 통한 과적합 개선 Improved overfitting by solving the internal covariate shift problem

FCL 층 개수, 뉴런 개수와 같은 피라미터는 자율적으로 작성한다. Parameters such as the number of FCL layers and the number of neurons are created autonomously.

피라미터 변화에 따른 모델 손실함수 시각화를 관찰하여 유의미한 감소를 보이지 않기 시작하는 지점에서의 수치로 피라미터를 설정하는 것이 바람직하다. By observing the visualization of the model loss function according to the parameter change, it is desirable to set the parameter to a value at the point where it does not show a significant decrease.

이 부분에 대한 보다 자세한 내용은 본 과제에서는 생략한다(TBD). Further details on this part will be omitted from this project (TBD).

Training the model

cnn_model.compile(loss = 'sparse_categorical_crossentropy', optimizer = Adam(learning_rate=0.001), metrics = ['accuracy'])
early_stop = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=1, verbose=1)

history = cnn_model.fit(X_train, y_train, epochs = 10, batch_size = 50, verbose = 1, validation_data = [X_test, y_test], callbacks=[early_stop])
    Epoch 1/10
    1200/1200 [==============================] - 16s 12ms/step - loss: 0.1787 - accuracy: 0.9496 - val_loss: 0.0658 - val_accuracy: 0.9794
    Epoch 2/10
    1200/1200 [==============================] - 14s 11ms/step - loss: 0.0674 - accuracy: 0.9797 - val_loss: 0.0839 - val_accuracy: 0.9747
    ...
    Epoch 9/10
    1200/1200 [==============================] - 15s 12ms/step - loss: 0.0222 - accuracy: 0.9929 - val_loss: 0.0481 - val_accuracy: 0.9860
    Epoch 10/10
    1200/1200 [==============================] - 14s 12ms/step - loss: 0.0217 - accuracy: 0.9932 - val_loss: 0.0518 - val_accuracy: 0.9866

자, 간식 타임 모델 훈련의 시간이다.

분류 문제를 다루고 있기에 크로스 엔트로피를 손실함수로, 가장 좋은 성능을 보이는 ‘Adam‘을 옵티마이저로, 그리고 모델 성능 척도는 ‘정확도‘로 설정했다. Since we are dealing with a classification problem, we set ‘Cross Entropy’ as the loss function, ‘Adam’ with the best performance as the optimizer, and ‘Accuracy’ as the model performance measure.

  • verbose: output에 정확도, validation 및 test 정확도를 표현한다. Expresses accuracy, validation, and test accuracy in the output.
  • epoch: 모델 학습의 순/역전파 과정에서 신경망 전체를 몇 번 통과할지 결정한다. Determines how many times to pass through the entire neural network in the forward/backward propagation process of model training.
  • batch_size: 한 epoch에 대하여 학습할 데이터 크기, 즉 mini batch 크기를 결정한다. Determines the size of the data to be trained for one epoch, that is, the mini batch size.
  • validation_data: 모델 예측성능 평가에 활용할 검증 데이터이다. verification data to be used for model prediction performance evaluation.

Early_stop‘을 활용하여 모델 학습과정에서 성능 변화가 일어나지 않으면 학습을 자동으로 멈추는 처리를 추가했다. By using ‘Early_stop’, we added a process that automatically stops learning if there is no performance change in the model training process.

  • patience: 몇 번의 성능 무변화를 용인해줄건지 설정한다. Sets how many times of no performance change to tolerate.

이 과정에서 모델의 테스트셋 정확도는 99.32%, 검증 데이터의 정확도는 98.66%로 도출되었다. In this process, the accuracy of the model (test set) was 99.32%, and the accuracy of the model (validation data) was 98.66%.

참고로, BatchNormalization()이 없을 경우, 정확도는 99%가 나오지 못했고, 학습 속도가 눈에 띄게 더딘 것이 확인되었다. For reference, in the absence of BatchNormalization(), the accuracy did not come out to 99%, and it was confirmed that the learning speed was noticeably slow.

Training the model with image augmentation

히딩크(Hiddink), “I’m still hungry.”

사람의 욕심은 끝이없다. There is no end to human greed.

우리는 여기서 그치지 않고 모델의 성능 더 끌어내고 싶다. We don’t want to stop there, but we want to bring out the performance of the model further.

그러기 위해서, 이미지 증강 기법을 활용하여 기존 인풋 이미지에 여러 변화를 주어 데이터 수를 증가시킨다. To do so, by using image augmentation techniques, various changes are made to the existing input image to increase the number of data.

학습 데이터의 증가는 모델의 과적합을 방지하여 더 높은 예측성능을 끌어낼 수 있다. Increasing training data can lead to higher predictive performance by avoiding overfitting the model.

from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
                            rotation_range = 50,
                            #width_shift_range = 0.1,
                            #horizontal_flip = True,
                            #vertical_flip = True,
                            #brightness_range=(.9, 1)
                            #zoom_range = 0.7,
                            #height_shift_range=0
                             )

datagen.fit(X_train)

n = 8
fig = plt.figure(figsize = (20,2))

# Creating the image flow with the training dataset
# Loading the certain number of adjusted images
for x_batch in datagen.flow(X_train, batch_size = n):
     for i in range(0,n):
            ax = fig.add_subplot(1, n, i+1)
            ax.imshow(x_batch[i])
     fig.suptitle('Augmented images (rotated 90 degrees)')
     plt.show()
     break;

cnn_model.fit_generator(datagen.flow(X_train, y_train, batch_size = 50), epochs = 10) # fit_generator() when using data made by image generator

score = cnn_model.evaluate(X_test, y_test)
print('Test accuracy', score[1])
 

image

    Epoch 1/10
    1200/1200 [==============================] - 20s 16ms/step - loss: 0.1346 - accuracy: 0.9613
    Epoch 2/10
    1200/1200 [==============================] - 18s 15ms/step - loss: 0.0799 - accuracy: 0.9756
    ...
    Epoch 9/10
    1200/1200 [==============================] - 18s 15ms/step - loss: 0.0521 - accuracy: 0.9843
    Epoch 10/10
    1200/1200 [==============================] - 18s 15ms/step - loss: 0.0481 - accuracy: 0.9850
    313/313 [==============================] - 1s 3ms/step - loss: 0.0461 - accuracy: 0.9860
    Test accuracy 0.9860000014305115

상기 이미지는 기존 이미지에 50도 안팎의 회전도를 부여하여 전혀 다른 새로운 이미지를 만들어낸 결과이다. The above image is the result of creating a completely different new image by giving a rotation of around 50 degrees to the existing image.

두 번째 이미지인 4와 같이 회전을 받아 기울어진 새로운 데이터가 주어졌을 때, 모델은 정확한 예측을 해내기 어려울 수 있다. When new data is rotated and tilted as in the second image 4, it may be difficult for the model to make accurate predictions.

우리는 이러한 변별력있는 데이터를 생성하여 모델의 성능을 더 끌어낸다. We generate these discriminative data to further drive the performance of our models.

구현 과정에서 하나 특별한 점은, 이미지 생성기를 통하여 모델을 학습할 경우 fit()이 아닌 fit_generator() 함수를 사용한다. One special thing in the implementation process is that when training a model through an image generator, use the fit_generator() function rather than fit().

최종적으로 모델의 성능은 0.9860000014305115, 즉 98.6%로 매우 높은 정확도를 보여준다. Finally, the performance of the model is 0.9860000014305115, that is, 98.6%, which shows a very high accuracy.

자, 이제 직관적인 시각화로 실제값과 예측값을 비교하고, 혼동 행렬로 표현하여 모델 평가를 진행해보자. Now, let’s compare the actual value and the predicted value with an intuitive visualization and evaluate the model by expressing it as a confusion matrix.

Evaluating the model

Comparison: Actual vs. Predicted

evaluation = cnn_model.evaluate(X_test, y_test)

predicted_classes = cnn_model.predict(X_test)
predicted_classes = np.argmax(predicted_classes, axis=1)

L = 7
W = 7
fig, axes = plt.subplots(L, W, figsize = (12, 12))
axes = axes.ravel()

for i in np.arange(0, L*W):
    axes[i].imshow(X_test[i])
    axes[i].set_title('Prediction = {}\n True = {}'.format(predicted_classes[i], y_test[i]))
    axes[i].axis('off')

plt.subplots_adjust(wspace = 1)

image

랜덤하게 선택된 테스트 데이터에 대한 실제값과 예측값을 보여주는 해당 결과표에서 ‘오분류’가 존재하지 않음을 발견했다. We found that there was no ‘misclassification’ in the corresponding result table showing the actual and predicted values ​​for the randomly selected test data.

이것은 99%에 육박하는 우리 모델의 강력한 성능에서 비롯된 결과일 것이다. This is probably the result of the strong performance of our model, which is close to 99%.

하지만, 우리는 오분류의 개수 역시 한 눈에 파악하고 싶다. However, we also want to know the number of misclassifications at a glance.

이럴 때, 혼동 행렬로써 성능 평가를 진행하면 매우 유용하다. In this case, it is very useful to perform performance evaluation with a confusion matrix.

Confusion matrix

from sklearn.metrics import confusion_matrix
import seaborn as sns

cm = confusion_matrix(y_test, predicted_classes)
cm
plt.figure(figsize = (10, 10))
sns.heatmap(cm, annot = True)

image

우리는 손쉽게 혼동행렬를 구현하고자 ‘씨본(seaborn)’ 라이브러리를 활용한다. We use the ‘seaborn’ library to easily implement the confusion matrix.

단 몇 줄만으로 혼동 행렬 구현이 가능하다. A confusion matrix can be implemented with just a few lines.

많은 데이터가 포함되있을 수록 밝은 색에 가까운 픽셀값을 가진다. The more data is included, the closer the pixel value is to a bright color.

직관적으로도 대각선, 즉 참분류 (TP, TN) 개수가 압도적인 것을 볼 수 있고, 그 외 검정색에 근사한 픽셀에 적힌 수치들은 모두 오분류이다. Intuitively, it can be seen that the diagonal, the number of true classifications (TP, TN), is overwhelming, and all other figures written in pixels that are close to black are misclassifications.

7만 개의 기존 이미지 데이터와 ‘이미지 증강’을 통해 추가된 새로운 이미지 7만 장, 도합 14만장의 인풋을 학습한 모델이 내놓은 오분류 치고 굉장히 낮은 오분류 개수를 확인할 수 있다. You can see a very low number of misclassifications, considering the input of 140,000 in total; 70,000 existing image data and 70,000 new images added through ‘image augmentation’.

댓글남기기