NeuroWhAI의 잡블로그

[TensorFlow] GAN으로 특정 숫자의 MNIST 이미지 생성하기 본문

개발 및 공부/라이브러리&프레임워크

[TensorFlow] GAN으로 특정 숫자의 MNIST 이미지 생성하기

NeuroWhAI 2018. 1. 24. 21:20


※ 이 글은 '골빈해커의 3분 딥러닝 텐서플로맛'이라는 책을 보고 실습한걸 기록한 글입니다.


넵 이전 예제에 이어서 또 GAN 입니다.
미리 스포(?) 해드리자면 이번 예제는 제가 이해를 덜했습니다... ㅠ


코드:
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
#-*- coding: utf-8 -*-
 
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
 
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./mnist/data/", one_hot=True)
 
total_epoch = 100
batch_size = 100
n_hidden = 256
n_input = 28*28
n_noise = 128
n_class = 10
 
D_global_step = tf.Variable(0, trainable=False, name='D_global_step')
G_global_step = tf.Variable(0, trainable=False, name='G_global_step')
 
= tf.placeholder(tf.float32, [None, n_input])
= tf.placeholder(tf.float32, [None, n_class])
= tf.placeholder(tf.float32, [None, n_noise])
 
def generator(noise, labels):
    with tf.variable_scope('generator'):
        inputs = tf.concat([noise, labels], 1)
        hidden = tf.layers.dense(inputs, n_hidden,
            activation=tf.nn.relu)
        output = tf.layers.dense(hidden, n_input,
            activation=tf.nn.sigmoid)
    return output
 
def discriminator(inputs, labels, reuse=None):
    with tf.variable_scope('discriminator') as scope:
        if reuse:
            scope.reuse_variables()
        inputs = tf.concat([inputs, labels], 1)
        hidden = tf.layers.dense(inputs, n_hidden,
            activation=tf.nn.relu)
        output = tf.layers.dense(hidden, 1,
            activation=None)
    return output
 
def get_noise(batch_size, n_noise):
    return np.random.uniform(-1.1., size=[batch_size, n_noise])
 
= generator(Z, Y)
D_real = discriminator(X, Y)
D_gene = discriminator(G, Y, True)
 
loss_D_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
    logits=D_real, labels=tf.ones_like(D_real)
))
loss_D_gene = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
    logits=D_gene, labels=tf.zeros_like(D_gene)
))
 
loss_D = loss_D_real + loss_D_gene
loss_G = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
    logits=D_gene, labels=tf.ones_like(D_gene)
))
 
vars_D = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
    scope='discriminator')
vars_G = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
    scope='generator')
 
train_D = tf.train.AdamOptimizer().minimize(loss_D,
    var_list=vars_D, global_step=D_global_step)
train_G = tf.train.AdamOptimizer().minimize(loss_G,
    var_list=vars_G, global_step=G_global_step)
 
tf.summary.scalar('loss_D', loss_D)
tf.summary.scalar('loss_G', loss_G)
 
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
 
    merged = tf.summary.merge_all()
    writer = tf.summary.FileWriter('./logs', sess.graph)
 
    total_batch = int(mnist.train.num_examples / batch_size)
 
    for epoch in range(total_epoch):
        loss_val_D, loss_val_G = 00
 
        batch_xs, batch_ys = None, None
        noise = None
 
        for i in range(total_batch):
            batch_xs, batch_ys = mnist.train.next_batch(batch_size)
            noise = get_noise(batch_size, n_noise)
 
            _, loss_val_D = sess.run([train_D, loss_D],
                feed_dict={X: batch_xs, Y: batch_ys, Z: noise})
            _, loss_val_G = sess.run([train_G, loss_G],
                feed_dict={Y: batch_ys, Z: noise})
 
        summary = sess.run(merged,
            feed_dict={X: batch_xs, Y: batch_ys, Z: noise})
        writer.add_summary(summary, global_step=sess.run(G_global_step))
 
        if epoch == 0 or (epoch + 1) % 10 == 0:
            print('Epoch:''%04d' % epoch,
                'D loss: {:.4}'.format(loss_val_D),
                'G loss: {:.4}'.format(loss_val_G))
 
            sample_size = 10
            noise = get_noise(sample_size, n_noise)
            samples = sess.run(G,
                feed_dict={Y: mnist.test.labels[:sample_size], Z: noise})
 
            fig, ax = plt.subplots(2, sample_size, figsize=(sample_size, 2))
 
            for i in range(sample_size):
                ax[0][i].set_axis_off()
                ax[1][i].set_axis_off()
 
                ax[0][i].imshow(np.reshape(mnist.test.images[i], (2828)))
                ax[1][i].imshow(np.reshape(samples[i], (2828)))
 
            plt.savefig('{}.png'.format(str(epoch).zfill(3)),
                bbox_inches='tight')
            
            plt.close(fig)
 
cs

결과:
Epoch: 0000 D loss: 0.004313 G loss: 7.136
Epoch: 0009 D loss: 0.1465 G loss: 5.191
Epoch: 0019 D loss: 0.7721 G loss: 3.282
Epoch: 0029 D loss: 0.5478 G loss: 3.197
Epoch: 0039 D loss: 0.7364 G loss: 2.399
Epoch: 0049 D loss: 0.7405 G loss: 2.027
Epoch: 0059 D loss: 0.6601 G loss: 2.435
Epoch: 0069 D loss: 0.6388 G loss: 2.324
Epoch: 0079 D loss: 0.6731 G loss: 2.147
Epoch: 0089 D loss: 0.6219 G loss: 2.051
Epoch: 0099 D loss: 0.7365 G loss: 1.9

(첫번째 줄은 실제 이미지, 두번째 줄은 생성된 가짜 이미지 입니다.)













이전 예제에서와는 다르게 특정 숫자만 생성하는게 가능해졌습니다.
제가 시간이 된다면(안 귀찮다면) '원하는' 숫자를 생성할 수 있도록 연습해보겠습니다.

이전 예제와 다르게 파라미터 변수들을 직접 생성하지 않고 layers API를 사용했습니다.
또한 이번엔 특정 숫자를 생성할 수 있도록 할것이므로 Y placeholder가 추가됬습니다.
그 외 자잘한게 많이 바뀌었는데 생략하도록 하겠습니다.

중요한 것은 Y를 생성기, 판별기의 입력 X와 생성기의 입력 Z에 concat(연쇄) 해주고 있다는 것입니다.
문제는 제가 이 방법이 실제로 효과가 있는 이유를 모르겠다는 겁니다!
정말 단순무식하게 이해해보자면 판별기가 실제 이미지 입력(X+Y)을 학습하면서 X와 Y의 관계?도 학습하게 되었고
따라서 생성기가 만든 이미지 G+Y에서 G에 해당하는 숫자와 Y가 다르면 가짜라고 결과가 나오고
이어서 생성기가 G와 Y가 같아지도록 학습되는게 아닐까... 싶습니다.
아니면 G+Y를 아무튼간에 가짜라고 판별해야 하므로 G+Y와 X+Y의 차이를 찾는데서 학습이 되는걸지도요.
누가 좀 가르쳐주세요 ㅠㅠ HELP!








Comments