7 분 소요

Code

[Notice] download here

Learning Goals

  1. 합성곱 신경망 모델 설계하여 케라스로 이미지 분류 Building the CNN and Image classification using keras
  2. Adam 옵티마이저로 신경망 가중치 최적화 Optimizing weights using ‘Adam’
  3. 드롭아웃을 통한 과적합 개선 Drop-out
  4. 모델 평가 진행 (confusion matrix) Model evaluation
  5. Image Augmentation으로 신경망 일반화 성능 개선 Improving generalization error through image augmentation
  6. 훈련 신경망 가중치 조작 방법 How to adjust weights

Background knowledge

CIFAR-10

image

10가지 클래스로 나누어져 있는 6만 개의 컬러(RGB 채널) 이미지로 구성된다 (airplanes, cars, birds, cats, etc.). Ten classes; each includes 60,000 color images

  • 이미지 해상도가 32x32 픽셀로 매우 낮다. Image resolution = 32x32
  • 각 클래스마다 6천개의 이미지가 존재한다 (클래스 별 매우 균등한 이미지 분포). Uniform image distribution by class

이번 프로젝트에서 주어진 입력 이미지가 10개의 클래스 중 어디에 속하는지 분류 모델을 학습시켜보자. Training the model to put new image into certain class

Convolutional Neural Network (CNN)

image

CNN 관련 배경지식은 상기 링크를 통해 숙지해주세요! Please refer to the link above.

image

3x3 ‘sharpen’ 커널 필터를 적용하면 인풋 이미지의 3x3 픽셀 범위에 대해 합성곱마다 가운데 값에 가중치를 높게줘서 출력 이미지에서 가운데 픽셀을 뚜렷하게 강조한다 (= 명확하게 보이게 한다). Applying a 3x3 ‘sharpen’ kernel filter, the value in the center of convolution matrix takes a significant weight, which sharpens the image

성능지표: Key Performance Indicators (KPI)

image

Type I Error: 암이 없는 환자에게 있다고 오진할 확률 Probability of misdiagnosing a patient without cancer

Type II Error: 암이 있는 환자에게 없다고 오진할 확률 Probability of misdiagnosing a patient with cancer

Precision: 암이 없는 환자에게 없다고 예측할 확률 Probability of misdiagnosing a patient without cancer

Recall: 암이 있는 환자에게 있다고 예측할 확률 Probability of misdiagnosing a patient with cancer

image

Implementation

Observing the dataset

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn

from keras.datasets import cifar10
(X_train, y_train) , (X_test, y_test) = cifar10.load_data()
X_train.shape
    (50000, 32, 32, 3)
  • # Images: 50000개
  • Image resolution: 32x32
  • RGB: 3

Data visualization

i = 30009
plt.imshow(X_train[i])
print(y_train[i])
    1

image

클래스 리스트에서 인덱스가 1인 클래스, 즉 ‘Cars’에 속하는 이미지인 것을 확인할 수 있다. Index 1 represetns ‘Cars’

이제, 한 번에 여러 이미지를 배출하여 직관적으로 비교해보자. Displaying multiple images at once for better intuition

W_grid = 4 # Grid width
L_grid = 4 # Grid height

fig, axes = plt.subplots(L_grid, W_grid, figsize = (25, 25))
axes = axes.ravel() # 4x4 matrix --> 16 elements 
n_training = len(X_train)

for i in np.arange(0, L_grid * W_grid):
    index = np.random.randint(0, n_training) # pick a random number
    axes[i].imshow(X_train[index])
    axes[i].set_title(y_train[index])
    axes[i].axis('off') # hide axis
    
plt.subplots_adjust(hspace = 0.4) # space out images

image

Data Preprocessing

Formatting the image(float32)

X_train = X_train.astype('float32')
X_test = X_test.astype('float32')

number_cat = 10 # number of classes
y_train

    array([[6],
        [9],
        [9],
        ...,
        [9],
        [1],
        [1]], dtype=uint8)

클래스 인덱스로 종속변수인 2차원 배열이 표현된 것을 확인해볼 수 있다. expressed as a 2-dimensional array

각 숫자는 앞서 ‘데이터 시각화’ 예제에서 보았던 클래스 인덱스 번호이다. Each number represents the index of classes

이를 인덱스 번호가 아니라 ‘One-Hot Encoding’ 방법을 활용하여 범주화 시켜서 신경망 학습 인자로 만들어보자. Performing categorization to the indexes using ‘One-Hot Encoding’

신경망의 학습 데이터는 ‘numpy 2차원 배열’이여야 한다. Training dataset must be numpy 2-dimensional array

Categorization (Categorical Data)

import keras
y_train = keras.utils.to_categorical(y_train, number_cat) # one-hot encoding
y_test = keras.utils.to_categorical(y_test, number_cat)
    array([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 1.],
        [0., 0., 0., ..., 0., 0., 1.],
        ...,
        [0., 0., 0., ..., 0., 0., 1.],
        [0., 1., 0., ..., 0., 0., 0.],
        [0., 1., 0., ..., 0., 0., 0.]], dtype=float32)

정규화

독립변수들은 픽셀에 할당된 [0, 255] 사이의 수치이므로, [0, 1] 사이 값들로 정규화를 거칠 필요가 있다. Independent variables are in range [0, 255], so they need to be normalized

X_train = X_train/255
X_test = X_test/255

이렇게 255로 나누어주면 각 데이터들은 [0, 1] 사이 값을 가지게 된다. Dividing by 255 makes the data become a number in range [0, 1]

모델 훈련하기

from keras.models import Sequential 
from keras.layers import Conv2D, MaxPooling2D, AveragePooling2D, Dense, Flatten, Dropout
from keras.optimizers import Adam
from keras.callbacks import TensorBoard # TensorFlow Visualization Tool
cnn_model = Sequential()
cnn_model.add(Conv2D(filters = 64, kernel_size = (3,3), activation = 'relu', input_shape = Input_shape))
cnn_model.add(Conv2D(filters = 64, kernel_size = (3,3), activation = 'relu'))
cnn_model.add(MaxPooling2D(2,2))
cnn_model.add(Dropout(0.4))


cnn_model.add(Conv2D(filters = 128, kernel_size = (3,3), activation = 'relu'))
cnn_model.add(Conv2D(filters = 128, kernel_size = (3,3), activation = 'relu'))
cnn_model.add(MaxPooling2D(2,2))
cnn_model.add(Dropout(0.4))

cnn_model.add(Flatten())

cnn_model.add(Dense(units = 1024, activation = 'relu'))

cnn_model.add(Dense(units = 1024, activation = 'relu'))

cnn_model.add(Dense(units = 10, activation = 'softmax')) # 최종 출력 클래스 개수 10개 # final output class = 10

cnn_model.compile(loss = 'categorical_crossentropy', optimizer = keras.optimizers.rmsprop(lr = 0.001), metrics = ['accuracy'])

history = cnn_model.fit(X_train, y_train, batch_size = 32, epochs = 1, shuffle = True)

ReLU: 회귀 작업 관련 함수로 연속적인 출력값을 생성한다 Continuous output

Softmax: 분류에 사용된다. classification

CPU vs. GPU

CPU는 직렬, GPU는 병령 처리에 각각 능하다. 따라서, 하나의 학습이 오래 지속될 필요가 있는 경우 GPU가 아닌 CPU를 사용하면 보다 능률적인 처리가 가능하다. CPU <=> serialization, GPU <=> parellelism. Thus, if one training lasts for long, CPU is better than GPU

ANN 은닉층 뉴런개수와 CNN 필터개수를 증가시키면, 모델 복잡도가 증가하여 학습 시간이 늘어난다. The more neurons in the hidden layer of an ANN and more filters in the CNN, the more complex the model and the longer it takes to train.

Evaluating the model

evaluation = cnn_model.evaluate(X_test, y_test)

predicted_classes = cnn_model.predict_classes(X_test)

y_test = y_test.argmax(1) # one hot(binary) --> decimal

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

이미지는 결과의 한 행을 보여준다. Showing one row of the result

해당 행에 대하여 세 번째와 마지막 열을 제외하고, 분류기가 모두 올바른 예측을 해낸 것읋 확인해볼 수 있다. Excluding the third and last column on the row, the classifier performs prediction successfully

여기서는 정확도를 결과값으로 따로 보여주지 않았지만, 정확도는 ‘evaluation’ 변수에 담겨있다. The accuracy is in ‘evaluation’

이제 혼동 행렬을 사용하여 보다 직관적으로 평가지표를 분석해보자. How can we analyze the model performance using 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) # 해당 데이터가 많거나 높은 경우 색을 사용해 시각화하는 그래프 color visualization in case that data 多

image

대각선에 위치한 픽셀들은 ‘True Positive’과 ‘True Negative’로 모델이 실제값과 일치하는 데이터들 개수를 담고있다. ‘TP’ and ‘TN’ on the diagonal include the number of correct predictions

그 외 다른 모든 픽셀에 속한 수치들은 오분류(‘False Negative’, ‘False Positive’)로 취급한다. Other pixels (‘FN’, ‘FP’) are errors

Saving the model

import os 
directory = os.path.join(os.getcwd(), 'saved_models') # getcwd: get current working directory

if not os.path.isdir(directory):
    os.makedirs(directory)
model_path = os.path.join(directory, 'keras_cifar10_trained_model.h5')
cnn_model.save(model_path)

Image Augmentation

과적합 문제를 해소하고 정확도를 높이기 위해 기존 데이터에 변화를 적용하는 전처리 기법이다. Applying changes to existing data to solve overfitting problems and increase accuracy.

기존 인풋 이미지에 뒤집기, 회전 등 다양한 변화를 주어 학습하는 데이터량을 증가시키고, 데이터 차원이 모델 복잡도를 웃돌게 만든다. By applying various changes (i.e., flipping, rotation, etc.) to the existing input image, it increases the amount of learning data and makes the data dimension exceed the model complexity.

ImageDataGenerator 클래스를 통해 이미지내 여러 변화를 끌어낸다. various changes to images

이는 차후 과적합 방지에 효과가 있다. Avoiding overfitting

New dataset with image augmentation

import keras
from keras.datasets import cifar10
(X_train, y_train), (X_test, y_test) = cifar10.load_data()

X_train = X_train.astype('float32')
X_test = X_test.astype('float32')

n = 8 
X_train_sample = X_train[:n] # 8개의 샘플 이미지를 가져온다 Loading 8 samples
from keras.preprocessing.image import ImageDataGenerator

# dataget_train = ImageDataGenerator(rotation_range = 90) # rotation
# dataget_train = ImageDataGenerator(vertical_flip=True) # flipping/inversion
# dataget_train = ImageDataGenerator(height_shift_range=0.5) # 이미지 평면 위치 이동 move image plane position
dataget_train = ImageDataGenerator(brightness_range=(1,3)) # brightness

dataget_train.fit(X_train_sample) # applying the image genearter
from scipy.misc import toimage # array to image

fig = plt.figure(figsize = (20,2)) # 새로 생성될 이미지 사이즈 조정 adjusting the size of new image

# 훈련 샘플을 가져와 이미지 flow를 생성한다. Creating the image flow with the training dataset
# 변형된 이미지를 배치 단위로 불러올 수 있는 Generator(*flow()*)을 생성해 준다 (8개 요소 포함하는 리스트 형태로 한 번에 가져오게 된다) Loading the certain number of adjusted images
for x_batch in dataget_train.flow(X_train_sample, batch_size = n):
     for i in range(0,n):
            ax = fig.add_subplot(1, n, i+1)
            ax.imshow(toimage(x_batch[i]))
     fig.suptitle('Augmented images (rotated 90 degrees)')
     plt.show()
     break;

image

이미지 증감을 이용하여 기존 데이터에서 새로운 데이터로 개수를 부풀린 예시이다. This is an example of inflating the number from existing data to new data using image increase/decrease.

결과에서 보이는 것처럼 기존 인풋 이미지의 ‘밝기’를 수정하여 새로운 데이터를 만들어냈다! New data was created by modifying the ‘brightness’ of the existing input image!

Training the model with image augmentation

from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
                            rotation_range = 90,
                            width_shift_range = 0.1,
                            horizontal_flip = True,
                            vertical_flip = True
                             )

datagen.fit(X_train)

cnn_model.fit_generator(datagen.flow(X_train, y_train, batch_size = 32), epochs = 2) # 이미지 생성기로 만든 데이터를 학습에 이용할 때 fit_generator() 함수를 사용한다 fit_generator() when using data made by image generator

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

# save the model
directory = os.path.join(os.getcwd(), 'saved_models')

if not os.path.isdir(directory):
    os.makedirs(directory)
model_path = os.path.join(directory, 'keras_cifar10_trained_model_Augmentation.h5')
cnn_model.save(model_path)

댓글남기기