공부/AI

Neural Network 공부했다. - Multi Layer Neural Network && Autoencoder

Swimming_Kim 2018. 12. 24. 10:37

 지난번에는 Single Layer Neural Network에 대해서 공부하였었다. 93%정도의 놀라운 정확도를 보였지만, 이해할 수 없는 실수들을 하기도 하였다. 오늘은 그 개선을 위해서 Multi Layer Neural Network에 대해서 배우고, Autoencoder를 배우면서 실습도 해본다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 아주 Deep하게 만들지는 않고 중간게 Hidden Layer하나를 추가해 보도록 하자. 이 Hidden Layer에서 Neuron의 개수는 500개로 하기로 한다.(그냥 적당하게 해준 것이라고 한다.)


Copyright © 2018. Alina Inc. All Rights Reserved.


 당연이 구해야 하는 매개변수의 값들도 엄청나게 많아진다. 지난  Single Layer Neural Network에서는 7850개를 구하였는데, 이번에는 무려 397,510개의 매개변수를 찾는다. 아마 연산에 상당한 시간이 걸리 것으로 생각이 된다. MNIST가 상당히 간단한 이미지 형식임에도 이렇게 연산할 것이 많다. 해상도가 더 좋은 사진이나, 컬러 이미지일 경우는 훨씬 더 많은 연산이 필요할 것이다.....

 그럼 실습 코드는 다음과 같다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import math
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
 
tf.set_random_seed(173)
np.random.seed(173)
 
def get_data():
    mnist = keras.datasets.mnist
    to_categorical = keras.utils.to_categorical
 
    (train_data, train_labels), (test_data, test_labels) = mnist.load_data()
 
    # uint8 data type을 float32 data type으로 변환해서 float_train_data에 저장합니다.
    float_train_data = train_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_train_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_train_data = np.reshape(float_train_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_train_labels에 저장합니다.
    onehot_train_labels = to_categorical(train_labels, num_classes=10)
 
    # uint8 data type을 float32 data type으로 변환해서 float_test_data에 저장합니다.
    float_test_data = test_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_test_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_test_data = np.reshape(float_test_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_test_labels에 저장합니다.
    onehot_test_labels = to_categorical(test_labels, num_classes=10)
 
    return float_train_data, onehot_train_labels, float_test_data, onehot_test_labels
 
def draw_mnist_image(image, title=''):
    # 그림에 title을 설정합니다.
    plt.title(title)
    # image가 28 * 28이라고 가정하고 그림을 그립니다.
    plt.imshow(np.reshape(image, [2828]), cmap='gray')
    # 그림을 화면에 보여줍니다.
    plt.show()
 
# x_data에 필기체 숫자 image를 저장합니다.
# y_data에 필기체 숫자 종류를 저장합니다.
# x_test_data에 필기체 숫자 image의 test set을 저장합니다.
# y_test_data에 필기체 숫자 종류의 test set을 저장합니다.
x_data, y_data, x_test_data, y_test_data = get_data()
x_input = tf.placeholder(tf.float32, [None, 28 * 28])
y_input = tf.placeholder(tf.float32, [None, 10])
 
weight_1_var = tf.Variable(tf.truncated_normal([28 * 28500], stddev=0.5))
bias_1_var = tf.Variable(tf.zeros([500]))
weight_2_var = tf.Variable(tf.truncated_normal([50010], stddev=0.05))
bias_2_var = tf.Variable(tf.zeros([10]))
 
fc_1 = bias_1_var + tf.matmul(x_input, weight_1_var)
relu_1 = tf.nn.relu(fc_1)
logit = bias_2_var + tf.matmul(relu_1, weight_2_var)
y_output = tf.nn.softmax(logit)
 
cross_entropy = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_input, logits=logit)
cost_output = tf.reduce_mean(cross_entropy)
learning_rate = 0.01
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost_output)
 
sess = tf.Session()
step = 0
cost = 0
mini_batch = 1024
max_epoch = 23
 
# variable(weight, bias)들을 초기화합니다.
sess.run(tf.global_variables_initializer())
# max_epoch만큼 반복하며 cost가 작아지는 weight, bias들을 찾습니다.
for epoch in range(max_epoch):
    # mini_batch단위만큼 잘라서 반복합니다.
    for i in range(int(math.ceil(len(x_data) / mini_batch))):
        # Gradient Descent를 사용하여 cost가 약간 작아지도록 weight, bias들을 변경합니다.
        _, cost = sess.run(
                [train_step, cost_output],
                feed_dict={
                        x_input: x_data[i * mini_batch:(i + 1* mini_batch],
                        y_input: y_data[i * mini_batch:(i + 1* mini_batch]})
        step += 1
    if epoch % 4 == 0:
        print('epoch: {}, train_step: {}, cost: {}'.format(epoch, step, cost))
 
y_pred = sess.run(y_output, feed_dict={x_input: x_test_data})
y_data_class = np.argmax(y_test_data, axis=1)
y_pred_class = np.argmax(y_pred, axis=1)
y_right_class = y_data_class == y_pred_class
accuracy = np.mean(y_right_class)
print('accuracy', accuracy)
 
y_wrong_class = y_data_class != y_pred_class
 
for i in range(10):
    title = 'ground truth: {}, prediction: {}'.format(
            y_data_class[y_wrong_class][i],
            y_pred_class[y_wrong_class][i])
    draw_mnist_image(x_test_data[y_wrong_class][i], title)
 
for i in range(3):
    print('y_data_class', y_data_class[y_wrong_class][i])
    print('y_test_data', y_test_data[y_wrong_class][i])
    print('y_pred_class', y_pred_class[y_wrong_class][i])
    print('y_pred', y_pred[y_wrong_class][i])
 
sess.close()
cs


 사실 지난번 코드와 비교해서 크게 달라진 것은 없다. 다만 계층이 하나 늘어나다 보니, 수정된 그에 해당하는 수정된 부분이 있는데,


1
2
3
4
5
6
7
8
9
10
weight_1_var = tf.Variable(tf.truncated_normal([28 * 28500], stddev=0.5))
bias_1_var = tf.Variable(tf.zeros([500]))
weight_2_var = tf.Variable(tf.truncated_normal([50010], stddev=0.05))
bias_2_var = tf.Variable(tf.zeros([10]))
 
 
fc_1 = bias_1_var + tf.matmul(x_input, weight_1_var)
relu_1 = tf.nn.relu(fc_1)
logit = bias_2_var + tf.matmul(relu_1, weight_2_var)
y_output = tf.nn.softmax(logit)
cs


 위 4줄의 코드는 늘어난 매개변수들에 해당되는 부분이다. 변수들이 많다보니, 전부 0으로 초기화하기보다는 가우시안을 활용해서 임의로 뿌려주는 것이 더 효율적이다. 


 아래의 4줄은 Activation Function에 대한 부분이다. 잘 보면 relu라는 부분이 있는데, 지난 시간에 보았던 그 relu를 사용한 것을 알 수가 있다. 나머지는 지난 실습과 동일하다. 









실행 결과의 일부를 살펴보자. 상당히 난해한 숫자들이 있다. 사실 인간이 보더라도 어떤 숫자인지 혼동이 올 수 있을 여지가 충분해 보인다! Single Layer 때와 같이 터무니없는 실수는 다소 줄어든 것이 확실하다!! 정확도도 98%정도로 많이 오른 것이 보인다.


Copyright © 2018. Alina Inc. All Rights Reserved.


MNIST Dataset이 아닌 사람의 얼굴을 학습시킨다면 더 깊고 넓은 Neural Network가 필요할 것이다. 중간에 위치한 Hidden Layer들이 더 많아질 것이고 이들은 이전 패턴에서 그 패턴의 패턴을 찾고 그 다음에는 그 패턴의 패턴을 찾고.... 이 과정이 반복될 것이다. 


 간단하게 말하자면, Input에서 멀어질수록 더 복잡한 패턴을 찾는다는 것이다!! 위 그림에서 보이는 바와 같이 처음에는 얼굴에서 Edge들을 인식하고, 그 다음에는 눈이나 입과 같이 얼굴의 일부를 인식하고, 마지막 쯤에 다다러서야 얼굴전체에 대한 패턴을 찾는 것을 볼 수 있다. 

 

 놀라운 것은 인간이 이렇게 작은 것부터 차근차근 학습시킬 목적으로 설계하지도 않았는데 알아서 이렇게 되었다는 것이다! 그리고 이 과정은 뇌가 작용하는 방식과 비슷하다고 한다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 불쌍하지만, 고양이 뇌에서 나오는 전기 신호를 관찰하면서 특정한 각도의 Edge를 보았을 때, 더 큰 반응이 왔다는 실험의 결과가 있다. 앞선 우리의 Neural Network에서도 초기에는 Edge를 패터으로 삼았는데 뇌와 유사한 작동 방식을 가진다는 것을 다시 한 번 알 수가 있었다.


Copyright © 2018. Alina Inc. All Rights Reserved.


 그렇다면, 무조건 큰 Neural Network가 좋은 것일까?? 과거 Overfit/Underfit 에서도 다룬 적이 있었는데 Neural Network가 클수록 Overfittting하는 경향이 크다. 기출문제를 외우려고 한다는 것이다. Data의 수를 충분히 많이 사용해서 Neural Network이 외우지 못하도록 겁(?)을 주고 적당히 넓고 깊은 Neural Network을 구성해 주는 것이 중요하다. 물론 이는 설계하는 우리가 설정해주어야 한다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 Multi Layer Neural Network를 사용하는 실습으로 Autoencode에 대해서 소개한다. 사실 사용한다기 보다는 유산한 형태를 가지는 것을 볼 수 있다. 신기한 점은, Input과 Output이 동일하다는 것이다. Neuron의 수를 줄여 나가다가, 다시 키운다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 MNIST Data로 알기 쉽게 정리하자면 다음과 같은데, Input도 이미지고, Output도 이미지이다. 다만 그 과정의 중간에서 Encoder와 Decoder가 학습되고, Compressed된 이미지가 도출된다. Autoencoder 도덕책 당신은.... 이런 녀석을 어디에 쓸 수 있는지 의문을 가질 것이다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 아하~!! 이렇게 쓰인다고 한다!! 생각해보면 학습된 Decoder는 쓸모가 있어 보인다. 적은 Data로 이미지를 생성해 낼 수가 있다. 이미지 압축에 대해서는 할 이야기가 많은데 (jpeg나 jpg 방식에 대한 차이 등등...) 이는 Computer Vision에서도 다룰 만큼 중요한 내용이기도 하다.


 그럼 이전에 사용했던 MNIST Dataset을 가지고 Autoencoder를 구축해보자!!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import math
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
 
tf.set_random_seed(173)
np.random.seed(173)
 
def get_data():
    mnist = keras.datasets.mnist
    to_categorical = keras.utils.to_categorical
 
    (train_data, train_labels), (test_data, test_labels) = mnist.load_data()
 
    # uint8 data type을 float32 data type으로 변환해서 float_train_data에 저장합니다.
    float_train_data = train_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_train_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_train_data = np.reshape(float_train_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_train_labels에 저장합니다.
    onehot_train_labels = to_categorical(train_labels, num_classes=10)
 
    # uint8 data type을 float32 data type으로 변환해서 float_test_data에 저장합니다.
    float_test_data = test_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_test_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_test_data = np.reshape(float_test_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_test_labels에 저장합니다.
    onehot_test_labels = to_categorical(test_labels, num_classes=10)
 
    return float_train_data, onehot_train_labels, float_test_data, onehot_test_labels
 
def draw_mnist_image(image, title=''):
    # 그림에 title을 설정합니다.
    plt.title(title)
    # image가 28 * 28이라고 가정하고 그림을 그립니다.
    plt.imshow(np.reshape(image, [2828]), cmap='gray')
    # 그림을 화면에 보여줍니다.
    plt.show()
 
# x_data에 필기체 숫자 image를 저장합니다.
# y_data에 필기체 숫자 종류를 저장합니다.
# x_test_data에 필기체 숫자 image의 test set을 저장합니다.
# y_test_data에 필기체 숫자 종류의 test set을 저장합니다.
x_data, y_data, x_test_data, y_test_data = get_data()
x_input = tf.placeholder(tf.float32, [None, 28 * 28])
 
weight_1_var = tf.Variable(tf.truncated_normal([28 * 28300], stddev=0.03))
bias_1_var = tf.Variable(tf.zeros([300]))
weight_2_var = tf.Variable(tf.truncated_normal([300300], stddev=0.03))
bias_2_var = tf.Variable(tf.zeros([300]))
weight_3_var = tf.Variable(tf.truncated_normal([3002], stddev=0.03))
bias_3_var = tf.Variable(tf.zeros([2]))
weight_4_var = tf.Variable(tf.truncated_normal([2300], stddev=0.03))
bias_4_var = tf.Variable(tf.zeros([300]))
weight_5_var = tf.Variable(tf.truncated_normal([300300], stddev=0.03))
bias_5_var = tf.Variable(tf.zeros([300]))
weight_6_var = tf.Variable(tf.truncated_normal([30028 * 28], stddev=0.03))
bias_6_var = tf.Variable(tf.zeros([28 * 28]))
 
 
fc_1 = bias_1_var + tf.matmul(x_input, weight_1_var)
relu_1 = tf.nn.relu(fc_1)
fc_2 = bias_2_var + tf.matmul(relu_1, weight_2_var)
relu_2 = tf.nn.relu(fc_2)
fc_3 = bias_3_var + tf.matmul(relu_2, weight_3_var)
feature = fc_3
fc_4 = bias_4_var + tf.matmul(feature, weight_4_var)
relu_4 = tf.nn.relu(fc_4)
fc_5 = bias_5_var + tf.matmul(relu_4, weight_5_var)
relu_5 = tf.nn.relu(fc_5)
fc_6 = bias_6_var + tf.matmul(relu_5, weight_6_var)
y_output = tf.nn.sigmoid(fc_6)
 
cost_output = tf.reduce_mean((x_input - y_output) ** 2)
 
learning_rate = 0.01
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost_output)
 
sess = tf.Session()
step = 0
cost = 0
mini_batch = 1024
max_epoch = 20
 
# variable(weight, bias)들을 초기화합니다.
sess.run(tf.global_variables_initializer())
# max_epoch만큼 반복하며 cost가 작아지는 weight, bias들을 찾습니다.
for epoch in range(max_epoch):
    # mini_batch단위만큼 잘라서 반복합니다.
    for i in range(int(math.ceil(len(x_data) / mini_batch))):
        # Gradient Descent를 사용하여 cost가 약간 작아지도록 weight, bias들을 변경합니다.
        _, cost = sess.run(
                [train_step, cost_output],
                feed_dict={
                        x_input: x_data[i * mini_batch:(i + 1* mini_batch]})
        step += 1
    if epoch % 3 == 0:
        print('epoch: {}, train_step: {}, cost: {}'.format(epoch, step, cost))
 
y_pred = sess.run(y_output, feed_dict={x_input: x_test_data})
mse = np.mean((x_test_data - y_pred) ** 2)
print('mse', mse)
 
y_data_class = np.argmax(y_test_data, axis=1)
 
for i in range(5):
    draw_mnist_image(x_test_data[i], '{} (input)'.format(y_data_class[i]))
    draw_mnist_image(y_pred[i], '{} (output)'.format(y_data_class[i]))
 
feature_val = [-1.00.0]
#feature_val = [1.0, 0.0]
#feature_val = [8.0, 16.0]
#feature_val = [8.0, 0.0]
#feature_val = [-8.0, 0.0]
#feature_val = [-8.0, -8.0]
#feature_val = [4.0, -1.0]
feature_output = sess.run(y_output, feed_dict={feature: [feature_val]})
draw_mnist_image(feature_output, feature_val)
 
sess.close()
cs


 전체의 소스는 이렇다. 사실 Multi Layer Neural Network와 큰 차이는 없다.


1
2
3
4
5
6
7
8
9
10
11
12
weight_1_var = tf.Variable(tf.truncated_normal([28 * 28300], stddev=0.03))
bias_1_var = tf.Variable(tf.zeros([300]))
weight_2_var = tf.Variable(tf.truncated_normal([300300], stddev=0.03))
bias_2_var = tf.Variable(tf.zeros([300]))
weight_3_var = tf.Variable(tf.truncated_normal([3002], stddev=0.03))
bias_3_var = tf.Variable(tf.zeros([2]))
weight_4_var = tf.Variable(tf.truncated_normal([2300], stddev=0.03))
bias_4_var = tf.Variable(tf.zeros([300]))
weight_5_var = tf.Variable(tf.truncated_normal([300300], stddev=0.03))
bias_5_var = tf.Variable(tf.zeros([300]))
weight_6_var = tf.Variable(tf.truncated_normal([30028 * 28], stddev=0.03))
bias_6_var = tf.Variable(tf.zeros([28 * 28]))
cs


 784 - 300 - 2 - 300 - 784 이렇게 Neuron을 나열하였는데 사실 이는 인간이 설정해 주어야 하는 부분이고 강의에서도 이렇게 하니 잘 되어서 했다고 한다. 


1
2
3
4
5
6
7
8
9
10
11
12
fc_1 = bias_1_var + tf.matmul(x_input, weight_1_var)
relu_1 = tf.nn.relu(fc_1)
fc_2 = bias_2_var + tf.matmul(relu_1, weight_2_var)
relu_2 = tf.nn.relu(fc_2)
fc_3 = bias_3_var + tf.matmul(relu_2, weight_3_var)
feature = fc_3
fc_4 = bias_4_var + tf.matmul(feature, weight_4_var)
relu_4 = tf.nn.relu(fc_4)
fc_5 = bias_5_var + tf.matmul(relu_4, weight_5_var)
relu_5 = tf.nn.relu(fc_5)
fc_6 = bias_6_var + tf.matmul(relu_5, weight_6_var)
y_output = tf.nn.sigmoid(fc_6)
cs


 이전과 동일하게 ReLU를 Activation Function으로 쓴 모습이 보인다. 


1
2
3
4
5
6
7
feature_val = [-1.00.0]
#feature_val = [1.0, 0.0]
#feature_val = [8.0, 16.0]
#feature_val = [8.0, 0.0]
#feature_val = [-8.0, 0.0]
#feature_val = [-8.0, -8.0]
#feature_val = [4.0, -1.0]
cs


 Encoder와 Decoder를 학습시켰다면 이제 2개의 Data만 넣어주면 MNIST 이미지를 얻을 수 있게 된 것이다. 여러가지 경우를 시험해 보라고 이렇게 해 놓았다.





 input과 output이미지가 다음과 같이 보이고(4는 솔직히 9같다.)

이 상황에서 Encoder와 Decoder가 학습이 되어 있을 것이다.




 임의의 값 -1 0을 넣어 보았더니 7같은 이런 MNIST Data를 만들어 낸 것을 알 수 있다. 이 밖에도

소스에서 다른 1*2 data들을 제시하고 있다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


이번에는 input 이미지에 잡음이 섞여 있는 상황에서 노이즈를 제거하는 Denoising Autoencoder를 만들어 보는 실습을 해 보도록 한다. Input은 노이즈가 있는 사진, Output은 노이즈가 없는 사진이 된다. 이런 Data들을 어디서 얻는가 하면, 사실 원본 이미지를 가지고 있다면 적절한 노이즈를 섞는 것은 어렵지 않다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 이렇게 왼쪽부터 차례로 원본 이미지, 노이즈가 낀 이미지, Denoising 된 이미지가 보인다. 위와 같이 새로운 Encoder와 Decoder를 학습시키고 노이즈가 있는 Test Data를 제시하였을 때 이를 복원해 낼 수 있는지 살펴보도록 하자. 


 2나 0같은 경우는 우리가 보아도 원래 어떤 수였는지 대충 알 수가 있지만, 오른쪽 제일 아래 4와 같은 경우는 솔직히 인간이 보아서서 원래 어떠한 모양이었는지 알기가 쉽지 않다. 그래도 우리의 인공지능은 잘 구별해준다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import math
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
 
tf.set_random_seed(173)
np.random.seed(173)
 
def get_data():
    mnist = keras.datasets.mnist
    to_categorical = keras.utils.to_categorical
 
    (train_data, train_labels), (test_data, test_labels) = mnist.load_data()
 
    # uint8 data type을 float32 data type으로 변환해서 float_train_data에 저장합니다.
    float_train_data = train_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_train_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_train_data = np.reshape(float_train_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_train_labels에 저장합니다.
    onehot_train_labels = to_categorical(train_labels, num_classes=10)
 
    # uint8 data type을 float32 data type으로 변환해서 float_test_data에 저장합니다.
    float_test_data = test_data.astype(np.float32)
    # 0.0~255.0범위의 pixel값을 0.0~1.0으로 변경합니다.
    float_test_data /= 255.0
    # 3차원 array을 2차원 array로 변경합니다.
    float_test_data = np.reshape(float_test_data, [-128 * 28])
    # label을 one hot encoding을 하여 onehot_test_labels에 저장합니다.
    onehot_test_labels = to_categorical(test_labels, num_classes=10)
 
    return float_train_data, onehot_train_labels, float_test_data, onehot_test_labels\
 
def draw_mnist_image(image, title=''):
    # 그림에 title을 설정합니다.
    plt.title(title)
    # image가 28 * 28이라고 가정하고 그림을 그립니다.
    plt.imshow(np.reshape(image, [2828]), cmap='gray')
    # 그림을 화면에 보여줍니다.
    plt.show()
 
def get_noise_image(image):
    # 평균 0.2, 표준편차 0.5의 normal distribution에서 random하게 image모양의 noise를 만들어서 noise에 저장합니다.
    noise = np.random.normal(0.20.5, image.shape)
    # 0.7이상의 noise만 남겨둡니다.
    noise[noise < 0.7= 0.0
    # noise를 image에 더해서 noise_image에 저장합니다.
    noise_image = noise + image
    # noise_image를 0.0~1.0이 되도록 clip합니다.
    np.clip(noise_image, 0.01.0, noise_image)
    return noise_image
 
x_data, y_data, x_test_data, y_test_data = get_data()
x_noise_data = get_noise_image(x_data)
x_test_noise_data = get_noise_image(x_test_data)
 
x_input = tf.placeholder(tf.float32, [None, 28 * 28])
y_input = tf.placeholder(tf.float32, [None, 28 * 28])
 
weight_1_var = tf.Variable(tf.truncated_normal([28 * 28300], stddev=0.005))
bias_1_var = tf.Variable(tf.zeros([300]))
weight_2_var = tf.Variable(tf.truncated_normal([300300], stddev=0.005))
bias_2_var = tf.Variable(tf.zeros([300]))
weight_3_var = tf.Variable(tf.truncated_normal([3002], stddev=0.005))
bias_3_var = tf.Variable(tf.zeros([2]))
weight_4_var = tf.Variable(tf.truncated_normal([2300], stddev=0.005))
bias_4_var = tf.Variable(tf.zeros([300]))
weight_5_var = tf.Variable(tf.truncated_normal([300300], stddev=0.005))
bias_5_var = tf.Variable(tf.zeros([300]))
weight_6_var = tf.Variable(tf.truncated_normal([30028 * 28], stddev=0.005))
bias_6_var = tf.Variable(tf.zeros([28 * 28]))
 
fc_1 = bias_1_var + tf.matmul(x_input, weight_1_var)
relu_1 = tf.nn.relu(fc_1)
fc_2 = bias_2_var + tf.matmul(relu_1, weight_2_var)
relu_2 = tf.nn.relu(fc_2)
fc_3 = bias_3_var + tf.matmul(relu_2, weight_3_var)
feature = fc_3
fc_4 = bias_4_var + tf.matmul(feature, weight_4_var)
relu_4 = tf.nn.relu(fc_4)
fc_5 = bias_5_var + tf.matmul(relu_4, weight_5_var)
relu_5 = tf.nn.relu(fc_5)
fc_6 = bias_6_var + tf.matmul(relu_5, weight_6_var)
y_output = tf.nn.sigmoid(fc_6)
 
cost_output = tf.reduce_mean((y_input - y_output) ** 2)
 
learning_rate = 0.01
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cost_output)
 
sess = tf.Session()
 
step = 0
cost = 0
mini_batch = 1024
max_epoch = 20
 
# variable(weight, bias)들을 초기화합니다.
sess.run(tf.global_variables_initializer())
# max_epoch만큼 반복하며 cost가 작아지는 weight, bias들을 찾습니다.
for epoch in range(max_epoch):
    # mini_batch단위만큼 잘라서 반복합니다.
    for i in range(int(math.ceil(len(x_data) / mini_batch))):
        # Gradient Descent를 사용하여 cost가 약간 작아지도록 weight, bias들을 변경합니다.
        _, cost = sess.run(
                [train_step, cost_output],
                feed_dict={
                        x_input: x_noise_data[i * mini_batch:(i + 1* mini_batch],
                        y_input: x_data[i * mini_batch:(i + 1* mini_batch]})
        step += 1
    if epoch % 3 == 0:
        print('epoch: {}, train_step: {}, cost: {}'.format(epoch, step, cost))
 
y_pred = sess.run(y_output, feed_dict={x_input: x_test_noise_data})
mse = np.mean((x_test_data - y_pred) ** 2)
print('mse', mse)
y_data_class = np.argmax(y_test_data, axis=1)
#10개의 noise가 섞인 필기체 숫자를 입력하여 출력을 그림으로 그려봅니다.
for i in range(10):
#for i in range(100, 110):
#for i in range(200, 210):
    draw_mnist_image(x_test_data[i], '{} (input)'.format(y_data_class[i]))
    draw_mnist_image(x_test_noise_data[i], '{} (noise)'.format(y_data_class[i]))
    draw_mnist_image(y_pred[i], '{} (output)'.format(y_data_class[i]))
 
sess.close()
cs

 

 사실 위에 있던 코드에서 Input과 Output에 대한 것만 살짝쿵 바뀐 것이기에 소스에 대한 설명은 생략한다. 아래에는 결과를 간단하게 보인다. 


 

 

 

이렇게 노이즈를 제거해 준 모습이다.


  


다만, 원래 가지고 있던 오류가 개선되지는 않은 모습이 보이기도 한다. output이 8같이 보인다.



 마지막으로 이번, 시간에 했던 실습들이 많았는데, 다 작성을 하고 보니 다소 헷갈리는 부분이 있어서 나름대로 정리를 해보았다.


◆ Multi Layer Neural Network 
    - Input / output : MNIST Data / 어떤 숫자인지 One-Hot Encoder형식으로 

- 학습시킨 것 : Encoder와 Decoder

- Compressed 된 벡터를 넣어 주면 MNIST Data를 내보내준다.


 Autoencoder
    - Input / output : 노이즈 이미지 / 원본 이미지

학습시킨 것 : 노이즈를 제거하는 Encoder와 Decoder

- 노이즈 이미지를 넣어 주면 깨끗하게 복원해 준다. 


Copyright © 2018. Alina Inc. All Rights Reserved.



 Neural Network가 쓰이는 예시에 대해서 살펴보자. Color Image들이 있을 때, 이들을 흑백으로 만들어서 Input으로 해주고, 흑백을 컬러로 만드는 학습을 시키면 다른 다른 흑백 이미지들도 컬러로 만들 수가 있다!! 사진에서 중간에 Conv라는 것들이 보이는데 이것들은 다음으로 배울 내용인인 Convolutional Neural Network에 대한 내용인 것 같다. 사실 이러한 것이 어떻게 가능하고 무엇이 어떤 색일지 인공지능이 어떻게 알 수 있나 싶지만,


Copyright © 2018. Alina Inc. All Rights Reserved.


 인공지능은, 이것은 사과네, 돌이네, 바다네, 이러한 것을 인식하고 그러면 돌은 갈색, 바다는 파란색, 사과는 빨간색, 이렇게 칠해 준다고 한다. 정확한 이해는 더 찾아봐야 하겠다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 다음으로 Super-Resolution이 있다. 이는 저해상도 이미지를 고해상도 이미지로 바꾸어 주는 인공지능이다. 이번에도 이미 있는 고해상도 이미지를 고의적으로 저해상도로 바꾼 뒤에 이들을 가지고 학습을 시켜 Encoder와 Decoder를 구성하고 test-set으로 기존에 없던 저해상도 이미지를 넣으면 고해상도로 복원시켜 주는 방식이라고 생각하면 되겠다.


Copyright © 2018. Alina Inc. All Rights Reserved.


 사람 얼굴에 대한 예제가 있는데, 제일 왼쪽과 비교하면 사실 놀라운 정도의 복원을 한 것이 보인다. 물론 원본과 다소 차이가 있기는 하지만 말이다. 


Copyright © 2018. Alina Inc. All Rights Reserved.


 손상된 이미지를 복원하는 Inpainting에도 쓰일 수 있다. 위에서는 깔끔하게 딱 직사각형으로 이미지가 잘려나갔지만... 하여튼, 주변에 어떠한 것들이 있었는지를 보고 이에 맞추어서 빈 부분을 복원해 준다.


Copyright © 2018. Alina Inc. All Rights Reserved.


 그렇다고 모든 것들을 완벽하게 복원하는가 하면, 그것은 아니다. 잘 보면 원래 사진에 무엇이 있었는지 우리도 충분히 상상할 수 있는 경우들이다. 잘려나간 부분 주위로 원래 어떠한 모양이 있었을지에 대한 단서들이 남겨져 있다. 그래서 아래 부분에 건물들이 있는 모습을 보면, 글자와 같이 해석이 필요한 경우는 제대로 복원이 되지 않았음이 보인다. 이를 어떻게 사용할 수 있을까?


 사진에서 사라지기를 원하는 부분이 있다면, 이것을 의도적으로 잘라내고 이를 복원시킨다. 그러면 배경으로 그 부분을 채워질 것이다. 


Copyright © 2018. Alina Inc. All Rights Reserved.

 


Copyright © 2018. Alina Inc. All Rights Reserved.


상당히 양이 많았다. 그래서 정리도 마지막에 해주었다.


다음 시간에는 아까도 살짝 언급되었던 Convolutional Neural Network (CNN)에 대해 배워본다.