MNIST 부숴버리기

MNIST를 부숴보도록 하자.

사실 이번 학기에 휴학을 하면서 방구석백수찐따상태라 github에 수행했던 프로젝트나 수업에서 진행했던 과제들을 올려보며 추억팔이 좀 하려고 한다.

방구석백수의깃허브주소
방구석백수의MNIST깃허브주소

작년에 CS492C 과목인 introduction to deep learning인가 하는 과목을 들었고 중간에서 평균보다 10점이 낮았는데도 불구하고 과제 점수가 거의 만점이라 매우 괜찮은 grade를 받았다.

역시 전산 과목은 이론보다 감각적인 실전! 인정하고 가는 바이다.

이 과목에서는 두 개의 과제를 줬는데, 두 과제 모두 MNIST에 관한 과제이다.


MNIST가 뭐냐하면 위와 같은 숫자 그림들이 주어지고 해당 숫자들에 대한 gold label이 주어진다. gold label이란 숫자 그림에 해당하는 정답으로 MNIST에서는 0~9까지 총 10가지의 gold label 종류가 존재한다.

문제는 일반적인 MNIST는 정확도가 매우 잘 나오는 편이지만 과제에서 주어진 MNIST 데이터들은 난잡하다.


위의 그림이 과제에서 주어진 MNIST 데이터이다. 일단 사람이 봐도 판별 불가한 데이터들이 많다.
심지어 training data의 갯수는 5만개라서 저런 데이터가 대부분일텐데 학습이 제대로 될 일이 있을까?
개쩌는 아키텍쳐를 뚝딱하고 만드는 사람이 아닌 이상 정확도가 높게 나오지는 않을 것으로 보인다.
사람들이 보기에도 정답을 모르겠는게 너무 많아보이는데...

암튼 내가 개쩌는 아키텍쳐를 뚝딱하고 만드는 사람이였으면 이 학교에 없지 않았을까? 버클리에 가서 텐서플로우로 뚝딱하고 있었을 것이다.

다행인 것은 skeleton code는 주어져서 모델의 input output을 건드리지 않고 딥러닝 아키텍쳐만 건드리면 됐다.

첫 번째 PA는 Fully-connected Network를 이용하여 과제를 부숴버리는 것이였다.

일단 fully-connected Network를 다음과 같이 작성하고 코드를 돌려보았다.

Acc가 0.4가 나온다.

기분이 개처참했다.

그래서 발악을 한답시고 다음과 같은 3가지의 방법을 총동원했다.

1. l2 regularization (weight이 엄청 커지지 않도록 loss에다가 추가)
2. Batch normalization (batch의 mean variance이용하여 batch를 정규화)
3. dropout (node들을 몇 개씩 dropout)

def dense_batch_relu(x, phase, unit, scope, dropout_rate=0.30):
with tf.variable_scope(scope):
reg = tf.contrib.layers.l2_regularizer(scale=0.005)
l1 = tf.layers.dense(x, unit, activation=None, kernel_regularizer=reg)
l2 = tf.contrib.layers.batch_norm(l1, center=True, scale=True)
l3 = tf.layers.dropout(l2, dropout_rate, training=phase)
return tf.nn.relu(l3, 'relu')

그랬더니 결과는 0.47

처참했따. 그래도 다른 학생들한테 물어보니까 0.43~0.48 왔다갔다 한다더라.
뭔가 좀 아쉬웠다.
다른 방법이 있을 것 같아 고민해보다가 데이터 수를 늘려보자는 느낌적인 느낌이 다가왔다.

training data가 저렇게 뒤집히고 돌려져있는 숫자 형태의 데이터면 당연히 test data도 뒤집히고 돌려져있는 숫자가 엄청나게 많을 것이다.

따라서 training data를 뒤집히고 돌려져있는 데이터들을 늘려 data augmentation을 조지면 꿀이 똑하고 떨어질 것만 같았다.

def augmentation(images, conversion=False, rotater=True):
transforms_list = []
c = tf.constant(28.0, dtype=tf.float32)
original = tf.constant([1,0,0,0,1,0,0,0], dtype=tf.float32)
if rotater == True:
angles = tf.random_normal([batch_size], -math.pi, math.pi)
transforms_list.append( tf.contrib.image.angles_to_projective_transforms(angles, c, c))
if conversion==True:
binomial_prob = tf.less(tf.random_uniform([batch_size], -1.0, 1.0), 0.0)
flip_transform = tf.convert_to_tensor( [1, 0, 0, 0, -1, c, 0, 0], dtype=tf.float32)
transforms_list.append( tf.where(binomial_prob, tf.tile(tf.expand_dims(flip_transform, 0), [batch_size, 1]),
tf.tile(tf.expand_dims(original, 0), [batch_size, 1])))
images = tf.contrib.image.transform(images, tf.contrib.image.compose_transforms(*transforms_list), interpolation='NEAREST')
return images

이게 답이였다

Acc가 0.67로 수직상승하면서 Top 3명에게만 주어지는 10점을 먹었다.
이 과목 수강생이 110명이였으니 저 몇 줄 안되는 코드로 3등안에 들었다고 생각하면
진심으로 개꿀이였따 이 말씀이다.
(이때 평균은 0.48이였고 최고 점수는 0.83이라고 한다. 0.83은 어떻게 한거냐?)

두 번째 PA는 CNN으로 MNIST를 조지는 것이였다.
그냥 바로 CNN layer를 꽂아버렸다.

def cnn(input_layer, filters, kernel_size, pool_size, strides, phase, dropout_rate=0.2):
conv1 = tf.layers.conv2d(
inputs=input_layer,
filters=filters,
kernel_size=kernel_size,
padding="same",
activation=tf.nn.relu)
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=pool_size, strides=strides)
dropout_layer = tf.layers.dropout(pool1, dropout_rate, training=phase)
return dropout_layer

바로 꽂아버렸더니 3-layer인데도 data augmentation있으니 0.81이 떠버렸다.
(평균은 0.77이다)

여기에다가 7-layer로 늘리고 parameter tuning 좀 만져주니

Acc가 0.94가 떴다.
(평균이 0.77이고 최고 Acc가 0.9289라는데 아마 다른 testdata set을 이용해서 acc가 다르게 나오지 않았나라는 생각이 든다)

결과는 Top2에 들고 10점만점 앙 개꿀

그래서 중요한 것은 이미지 처리할때 Data Augmentaion에 대한 기법이 되게되게 중요하다는 것을 뼈저리게 느꼈다.

데이터같은 경우 가지고 있는 픽셀의 종류들도 매우 다양하고 넓으니 이런 걸 다 커버하려면 무지막지한 데이터가 필요해보이는데 간단한 Data Augmentation 방법으로도 Acc를 저렇게 올릴 수 있으니...

이러한 방법에 대한 연구론들에 살짝 흥미가 갔다. (흥미만 갔다 이 말씀)

암튼 MNIST 정복 끝~

코드는 github 참조해연~!

댓글

가장 많이 본 글