CIFAR-10 이미지 분류를 위한 CNN을 구성해보자! (Keras)
Overview
이 문서에서는 CIFAR-10 dataset에 대한 이미지 분류를 Keras를 사용한 CNN(Convolution Neural Network)로 구현해보도록 하겠습니다.
본문에서 사용한 코드는 이곳
CIFAR-10
발음을 조심해야하는 이름을 가진 CIFAR-10 dataset은 32x32픽셀의 60000개 컬러이미지가 포함되어있으며, 각 이미지는 10개의 클래스로 라벨링이 되어있습니다.
또한, MNIST와 같이 머신러닝 연구에 가장 널리 사용되는 dataset중 하나입니다.
60000개 중, 50000개 이미지는 트레이닝 10000개 이미지는 테스트용도로 사용됩니다.
CNN Archtecture
[참고]
CNN이란?
본문에서 사용한 CNN의 각 레이어 구조는 다음과 같습니다.
[용어 설명]
3channel : color이미지
ReLu : 0이상의 값들은 그대로 출력하게하는 활성화함수
Padding(Same) : 입력크기==출력크기 [INFO]
Dropout : 무작위로 뉴런들을 버림 [INFO]
Pooling : 크기도 줄이고 특정 feature를 강조 [INFO]
Softmax : classification [INFO]
Source Code
지금부터는 소스코드를 보도록 하겠습니다.
본문에서 사용한 full source는 이곳에서 받을 수 있습니다.
구글 colab을 이용해서 테스트하였습니다.
노트 설정에서 GPU를 선택하고 돌리는 것을 추천드립니다.
설정한 다음 Restart Kernel을 해주세요
Load CIFAR-10 Dataset
#cifar10에서 데이터를 로드
#from keras.datasets import cifar10 이 필요
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
print ("Training data:")
print ("Number of examples: ", X_train.shape[0])
print ("Number of channels:",X_train.shape[3])
print ("Image size:", X_train.shape[1], X_train.shape[2])
print
print ("Test data:")
print ("Number of examples:", X_test.shape[0])
print ("Number of channels:", X_test.shape[3])
print ("Image size:", X_test.shape[1], X_test.shape[2])
트레이닝에 사용할 5만장의 이미지와 테스트에 사용할 1만장의 이미지(32x32, 3channel)가 로드된 것을 확인할 수 있습니다.
일부 이미지를 실제로 출력해보면 제대로 다운로드받았는지 확인할 수 있습니다.
plt.subplot(141)
plt.imshow(X_train[0], interpolation="bicubic")
plt.grid(False)
plt.subplot(142)
plt.imshow(X_train[4], interpolation="bicubic")
plt.grid(False)
plt.subplot(143)
plt.imshow(X_train[8], interpolation="bicubic")
plt.grid(False)
plt.subplot(144)
plt.imshow(X_train[12], interpolation="bicubic")
plt.grid(False)
plt.show()
Data Normalization
코드를 보기 전에 잠시 데이터 정규화에 대해서 설명하고 넘어가도록 하겠습니다.
머신러닝에서의 핵심중 하나는 cost
를 구하고 Gradient Descent
와 같은 알고리즘을 사용하여 cost
를 최소화 시키는 것입니다. 그렇게 하기 위해 적당한 Learning Rate
값을 찾는 것이 중요합니다.
근데, Learning Rate
와 데이터에 대한 정규화가 어떤 상관이 있을까요?
다음 그림을 보면 좀 더 직관적으로 이해할 수 있습니다.
정규화되지 않은 데이터셋은 타원모양으로 길게 늘어져 Learning Rate
를 매우 작게 해야지만 학습이 될 수 있다는 것을 확인할 수 있습니다. 만약 충분히 작지 않다면 수평으로 이동할 때와 수직으로 이동할 때 불균형이 발생하여 Gradient Descent
알고리즘을 적용하기 어려울 수 있습니다.
반면에 정규화된 데이터셋은 구의 모양을 띄고 있습니다. 때문에 Gradient Descent
알고리즘을 적용하여 쉽고 빠르게 최적화 지점을 찾을 수 있습니다.
앞선 이유 때문에 최적의 머신러닝을 하기 위해선 데이터에 대한 정규화작업을 거치는 것이 반드시 필요합니다.
Data Normalization에는 크게 두가지 방법이 있습니다. (하나 더있는데 추후 서술)
1. Normalization(Min/Max)
전체 구간을 [0,1]로 맞춰줍니다.
2. Standardization
original data : 정규화 하기 전의 데이터 분포
zero-centered data : 원 데이터에 평균을 뺌. 이로써 데이터의 분포가 가운데에 모이게 됩니다.
normalized data : 표준편차를 나눠줌으로써 데이터의 분포가 일정해지는 효과를 얻게 됩니다. (가로 세로 길이가 같아짐)
+) 반드시 정규화를 하기 위한 평균, 표준편차, min, max등의 값들은 train set에 있는 값만 사용하여 구하고 validation, test set에 적용하여야 합니다.
참고링크 : cs231n
내가 몰라서 적는 표준편차 구하는 법)
표준편차는 자료의 관찰값이 얼마나 흩어져 있는지를 나타내는 값
관측값에서 평균을 뺀 모든 값의 제곱을 구하고 그 값의 평균을 제곱근하면 된다.
Data Preprocessing -code
다시 본론으로 돌아와서 소스코드를 보겠습니다.
아래 소스코드는 위의 설명 중 Standardization
을 사용해서 정규화를 진행하였습니다.
print ("mean before normalization:", np.mean(X_train))
print ("std before normalization:", np.std(X_train))
mean=[0,0,0]
std=[0,0,0]
newX_train = np.ones(X_train.shape)
newX_test = np.ones(X_test.shape)
#train set에 있는 데이터로만 평균과 표준편차를 구함
for i in range(3):
mean[i] = np.mean(X_train[:,:,:,i])
std[i] = np.std(X_train[:,:,:,i])
#train과 test셋 모두 정규화 작업
for i in range(3):
newX_train[:,:,:,i] = X_train[:,:,:,i] - mean[i]
newX_train[:,:,:,i] = newX_train[:,:,:,i] / std[i]
newX_test[:,:,:,i] = X_test[:,:,:,i] - mean[i]
newX_test[:,:,:,i] = newX_test[:,:,:,i] / std[i]
X_train = newX_train
X_test = newX_test
print ("mean after normalization:", np.mean(X_train))
print ("std after normalization:", np.std(X_train))
print(X_train.max())
Training
데이터 정규화까지 마쳤으니 이제 본격적으로 트레이닝을 해보도록 하겠습니다.
각 중요 파라미터는 다음과 같습니다.
batchSize = 512 #-- Training Batch Size
num_classes = 10 #-- Number of classes in CIFAR-10 dataset
num_epochs = 50 #-- Number of epochs for training
learningRate= 0.001 #-- Learning rate for the network
lr_weight_decay = 0.95 #-- Learning weight decay. Reduce the learn rate by 0.95 after epoch
img_rows = 32 #-- input image dimensions
img_cols = 32
Y_train = np_utils.to_categorical(y_train, num_classes)
Y_test = np_utils.to_categorical(y_test, num_classes)
Keras의 to_categorical
input : [1,0,4,3] output : [0,1,0,0,0] [1,0,0,0,0] [0,0,0,0,1] [0,0,0,1,0]
모델 구현은 다음과 같습니다.
from keras import initializers
import copy
result = {}
y = {}
loss = []
acc = []
dropouts = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
for dropout in dropouts:
print ("Dropout: ", (dropout))
model = Sequential()
#-- layer 1
model.add(Conv2D(64, 3, 3,
border_mode='same',
activation='relu'
input_shape=(img_rows, img_cols,3)))
model.add(Dropout(dropout))
model.add(Conv2D(64, 3, 3, activation='relu',border_mode='same'))
model.add(Dropout(dropout))
model.add(MaxPooling2D(pool_size=(2, 2)))
##--layer 2
model.add(Conv2D(128, 3, 3, activation='relu',border_mode='same'))
model.add(Dropout(dropout))
model.add(MaxPooling2D(pool_size=(2, 2)))
##--layer 3
model.add(Conv2D(256, 3, 3, activation='relu',border_mode='same'))
model.add(Dropout(dropout))
model.add(MaxPooling2D(pool_size=(2, 2)))
##-- layer 4
model.add(Flatten())
model.add(Dense(512, activation='relu'))
#-- layer 5
model.add(Dense(512, activation='relu'))
#-- layer 6
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
model_cce = model.fit(X_train, Y_train, batch_size=batchSize, nb_epoch=num_epochs, verbose=1, shuffle=True, validation_data=(X_test, Y_test))
score = model.evaluate(X_test, Y_test, verbose=0)
y[dropout] = model.predict(X_test)
print('Test score:', score[0])
print('Test accuracy:', score[1])
result[dropout] = copy.deepcopy(model_cce.history)
loss.append(score[0])
acc.append(score[1])
Dropout을 0.0부터 0.9까지 바꿔가며 테스트를 마친 뒤 Dropout별 Accuracy와 Loss입니다.
Dropout이 0.5일때 Acc가 가장 높고, Loss가 가장 낮습니다.
여담으로 Acc와 Loss를 결정짓는 요소는 굉장히 다양하니 뭘 어떻게 바꿔야 성능이 눈에 띄게 좋아지는지 지금 제 수준으로는 판단하기 굉장히 어려운 것 같습니다. 이래서 데이터사이언티스트가 몸값이 높은가…생각이 듭니다. ( ._.)
번외. Optimizer=Adadelta
완전히 동일한 과정이고 optimizer만 Adadelta
로 바꾼 결과 입니다. 그래프만 봐도 Adadelta
가 좀 더 성능이 좋은것 같습니다.
여기도 마찬가지로 Dropout 0.5에서 Acc가 제일 높네요!
대부분은 optimizer function을 adam
이나 adadelta
를 사용한다고 하는데 아직 정확히 뭐가 다른지는 모르겠습니다.
추후에 optimizer에 대한 내용도 공부해서 따로 포스팅을 해야겠습니다.
-끝-
댓글남기기