본문 바로가기

Python/DeepLearning

Tensorflow(keras)를 이용해 MNIST digits 분류해보기

0. 딥러닝을 시작하면서 가장 처음 접하게 되는 데이터 셋은 거의 MNIST 데이터베이스일 것이다. 어떤 사람들은 이 데이터베이스를 'Hello, Deep Learning!' 라고 한다. 

#include<stdio.h>

int main(void){
printf("Hello, World");
return 0;
}

아마 이런거 아닐까...? 아무튼 아직 역전파는 제대로 (이해도) 못하고 있으니 순전파로 MNIST를 분류해보았다. MNIST는 이렇게 생긴 귀여운 데이터베이스셋이다.

훈련케이스 6만개, 테스트케이스 1만개를 가지고 있는 이 친구는 28*28로 표현되고 각 값은 0~255의 값을 가진다. 하나 뽑아서 배열로 보면 눈에 들어온다. 

[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0  84 185 159 151  60  36   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0 222 254 254 254 254 241 198 198 198 198 198 198 198 198 170  52   0   0   0   0   0   0]
 [  0   0   0   0   0   0  67 114  72 114 163 227 254 225 254 254 254 250 229 254 254 140   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0  17  66  14  67  67  67  59  21 236 254 106   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  83 253 209  18   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  22 233 255  83   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0 129 254 238  44   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0  59 249 254  62   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0 133 254 187   5   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   9 205 248  58   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0 126 254 182   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0  75 251 240  57   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0  19 221 254 166   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   3 203 254 219  35   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0  38 254 254  77   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0  31 224 254 115   1   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0 133 254 254  52   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0  61 242 254 254  52   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 121 254 254 219  40   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0 121 254 207  18   0   0   0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0]]

무슨 글자일까요? 뭔지 모르겠으면 멀리서 봐보자. 난 눈이 나쁘니까 그냥 출력했다.

비슷하게 생겼나? 레딧에서 자주하는 유니코드로 그림그리기같이 생겼는데 아무튼.. 이 것들을 분류해본다.

1. 사용할 라이브러리는 Tensorflow안의 Keras이다. 먼저 단일 네트워크로 구성해보았다.

batch_size = 128 / epochs = 5 / Standard Gradient Descent / Cross Entropy 사용했다.

Input Node는 784개, Output은 10개 중간에 Hidden Layer는 기본적으로 32개 해주었다.

세대가 거듭될수록 정확도가 높아지는 것이 보인다. 하지만 traning과 validation의 간극이 존재한다. 여기에서 Hidden Layer의 노드 개수를 늘려주면 어떻게 될까?

정확도가 더 높아지는 것을 알 수 있다. 10개 준 애는 바닥을 긴다. 하지만 연산해야하는 개수가 늘어난 만큼 execute time을 무시할 수 없다.

아주 정직하게 같이 늘어난다. 이제 다중 네트워크로 구성해본다.

2. 다중 네트워크

그림은 마치 Sigmoid가 한 번에 여러번 처리되는 것 같지만 그게 아닌 input -> sig -> sig -> … -> ouput이다. 레이어마다의 결과값을 확인해본다.

Layer 1개
Layer 2개
Layer 3개
Layer 4개
레이어 개수에 따른 비교

 이상하게도 딥러닝을 맹신하는 일반적인 생각과 달리 Layer의 개수가 늘어날수록 정확도는 뚝뚝 떨어진다. 이게 어떻게 된 일일까? 이러한 현상을 과적합(overfitting)이라고 한다. 학습 데이터를 과하게 학습하여 실제 테스트 데이터에서는 제대로 분류하지 못하는 것이다.

사람도 아는것을 먼저 인식한다

 이 때의 해결방법은 epoch를 늘려 학습을 오~래 시키는 것이다. 가장 정확도가 낮았던 4개 레이어에 50epoch를 주어 결과를 본다.

 정확도가 올라가긴 했지만 많이 부족한 것을 알 수 있다. 이 점을 극복하기 위해 어떻게 해야할지는 더 공부하고 포스팅해야겠다.

import tensorflow as tf
from minist import load_mnist
import matplotlib.pylab as plt
from keras.layers import Dense
from keras.models import Sequential
import time

def get_data():
	(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False, one_hot_label=True)
	return (x_train, t_train), (x_test, t_test)
#x_train, t_train = 학습하기 위한 이미지(6만장)
#x_test, t_test = 테스트 용 이미지(1만장)

def one_layer(unit_num):
	startTime = time.time()
	x, t = get_data()
	#x = (x_train, t_train)
	#t = (x_test, t_test)
	
	image_size = 784
	num_classes = 10
	
	model = Sequential()
	
	model.add(Dense(units=unit_num, activation='sigmoid', input_shape=(image_size,)))
	model.add(Dense(units=num_classes, activation='softmax'))
	model.summary()
	
	model.compile(optimizer="sgd", loss='categorical_crossentropy', metrics=['accuracy'])
	history = model.fit(x[0], x[1], batch_size=128, epochs=5, verbose=False, validation_split=.1)
	loss, accuracy  = model.evaluate(t[0], t[1], verbose=False)

	# plt.plot(history.history['acc'])
	# plt.plot(history.history['val_acc'])
	# plt.title('model accuracy')
	# plt.ylabel('accuracy')
	# plt.xlabel('epoch')
	# plt.legend(['training', 'validation'], loc='best')
	# plt.show()
	# print(f'Test loss: {loss:.3}')
	# print(f'Test accuracy: {accuracy:.3}')
	endTime = time.time() - startTime
	return history.history['val_acc']

def create_dense(layer_sizes):
	image_size = 784
	num_classes = 10
	
	model = Sequential()
	model.add(Dense(layer_sizes[0], activation='sigmoid', input_shape=(image_size,)))
	
	for s in layer_sizes[1:]:
		model.add(Dense(units = s, activation = 'sigmoid'))
		
	model.add(Dense(units=num_classes, activation='softmax'))
	
	return model

def evaluate(model, batch_size=128, epochs=5):
	x, t = get_data()
	#x = (x_train, t_train)
	#t = (x_test, t_test)
	
	model.summary()
	model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'])
	history = model.fit(x[0], x[1], batch_size=batch_size, epochs=epochs, validation_split=.1, verbose=False)
	loss, accuracy  = model.evaluate(t[0], t[1], verbose=False)
	
	plt.plot(history.history['acc'])
	plt.plot(history.history['val_acc'])
	plt.title('model accuracy')
	plt.ylabel('accuracy')
	plt.xlabel('epoch')
	plt.legend(['training', 'validation'], loc='best')
	plt.show()
	
	print()
	print(f'Test loss: {loss:.3}')
	print(f'Test accuracy: {accuracy:.3}')
	
	return history.history['val_acc']
	
if __name__ == '__main__':
	units_list = [10, 32, 50, 100, 200, 300]
	accuracy_list = []
	# endTime_list = []
	
	# for n in units_list:
		# accuracy = one_layer(n)
		# accuracy_list.append(accuracy)
		
	# for layers in range(1, 5):
		# model = create_dense([32] * layers)
		# accuracy_list.append(evaluate(model))
	
	model = create_dense([32] * 4)
	evaluate(model, batch_size=128, epochs=50)

	# plt.plot(accuracy_list[0])
	# plt.plot(accuracy_list[1])
	# plt.plot(accuracy_list[2])
	# plt.plot(accuracy_list[3])
	# plt.plot(accuracy_list[4])
	# plt.plot(accuracy_list[5])
	# plt.ylabel('accuracy')
	# plt.xlabel('units')
	# plt.legend(['10', '32', '50', '100', '200', '300'], loc='best')
	# plt.show()

사용한 코드. 매우 더럽다.