NeuroWhAI의 잡블로그

[Keras] GAN으로 MNIST 이미지 생성하기 본문

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

[Keras] GAN으로 MNIST 이미지 생성하기

NeuroWhAI 2018. 3. 26. 18:20


※ 이 글은 '코딩셰프의 3분 딥러닝 케라스맛'이라는 책을 보고 실습한걸 기록한 글입니다.


GAN으로 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from keras import layers, models, optimizers
from keras import datasets
from keras import backend as K
import matplotlib.pyplot as plt
import numpy as np
 
K.set_image_data_format('channels_first')
print(K.image_data_format())
 
class GAN(models.Sequential):
  def __init__(self, input_dim):
    super().__init__()
    
    self.input_dim = input_dim
    
    self.generator = self.make_G()
    self.discriminator = self.make_D()
    
    self.add(self.generator)
    self.discriminator.trainable = False
    self.add(self.discriminator)
    
    self.compile_all()
    
  def make_G(self):
    input_dim = self.input_dim
    
    model = models.Sequential()
    model.add(layers.Dense(1024, activation='tanh', input_dim=input_dim))
    model.add(layers.Dense(128 * 7 * 7, activation='tanh'))
    model.add(layers.BatchNormalization())
    model.add(layers.Reshape((12877), input_shape=(128 * 7 * 7,)))
    model.add(layers.UpSampling2D(size=(22)))
    model.add(layers.Conv2D(64, (55), padding='same', activation='tanh'))
    model.add(layers.UpSampling2D(size=(22)))
    model.add(layers.Conv2D(1, (55), padding='same', activation='tanh'))
    return model
  
  def make_D(self):
    model = models.Sequential()
    model.add(layers.Conv2D(64, (55), padding='same', activation='tanh',
                           input_shape=(12828)))
    model.add(layers.MaxPooling2D(pool_size=(22)))
    model.add(layers.Conv2D(128, (55), padding='same', activation='tanh'))
    model.add(layers.MaxPooling2D(pool_size=(22)))
    model.add(layers.Flatten())
    model.add(layers.Dense(1024, activation='tanh'))
    model.add(layers.Dense(1, activation='sigmoid'))
    return model
  
  def compile_all(self):
    opt_D = optimizers.SGD(lr=0.0005, momentum=0.9, nesterov=True)
    opt_G = optimizers.SGD(lr=0.0005, momentum=0.9, nesterov=True)
    
    self.compile(loss='binary_crossentropy', optimizer=opt_G)
    
    self.discriminator.trainable = True
    self.discriminator.compile(loss='binary_crossentropy', optimizer=opt_D)
    
  def get_z(self, ln):
    return np.random.uniform(-1.01.0, (ln, self.input_dim))
  
  def train_once(self, x):
    ln = x.shape[0]
    
    z = self.get_z(ln)
    gen = self.generator.predict(z, verbose=0)
    input_D = np.concatenate((x, gen))
    y_D = [1* ln + [0* ln
    loss_D = self.discriminator.train_on_batch(input_D, y_D)
    
    z = self.get_z(ln)
    self.discriminator.trainable = False
    loss_G = self.train_on_batch(z, [1* ln)
    self.discriminator.trainable = True
    
    return loss_D, loss_G
  
def get_x(x_train, index, batch_size):
  return x_train[index * batch_size:(index + 1* batch_size]
 
class MnistData():
    def __init__(self):
        (x_train, y_train), (x_test, y_test) = datasets.mnist.load_data()
 
        img_rows, img_cols = x_train.shape[1:]
 
        x_train = x_train.astype('float32'- 127.5
        x_test = x_test.astype('float32'- 127.5
        x_train /= 127.5
        x_test /= 127.5
 
        self.num_classes = 10
        self.x_train, self.y_train = x_train, y_train
        self.x_test, self.y_test = x_test, y_test
  
def main():
  batch_size = 100
  epochs = 30
  input_dim = 100
  sample_size = 6
  
  data = MnistData()
  x_train = data.x_train
  x_train = x_train.reshape((x_train.shape[0], 1+ x_train.shape[1:])
  
  gan = GAN(input_dim)
  
  for epoch in range(epochs):
    print("Epoch", epoch)
    
    for index in range(int(x_train.shape[0/ batch_size)):
      x = get_x(x_train, index, batch_size)
      loss_D, loss_G = gan.train_once(x)
      
    print('Loss D:', loss_D)
    print('Loss G:', loss_G)
      
    if epoch % 2 == 0 or epoch == epochs - 1:
      z = gan.get_z(sample_size)
      gen = gan.generator.predict(z, verbose=0)
      
      plt.figure(figsize=(202))
  
      for i in range(sample_size):
        ax = plt.subplot(1, sample_size, i + 1)
        plt.imshow(gen[i].reshape((2828)))
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
        
      plt.show()
 
if __name__ == '__main__':
  main()
cs

결과:
Using TensorFlow backend.
channels_first
Epoch 0
Loss D: 0.7338002
Loss G: 0.93040544


(중략)
Epoch 9
Loss D: 0.35202882
Loss G: 2.2948298
Epoch 10
Loss D: 0.46802875
Loss G: 1.4171509


(중략)
Epoch 29
Loss D: 0.2630578
Loss G: 1.8819554




생성망을 만드는데 새로운 레이어가 등장했습니다.
BatchNormalization 레이어입니다.
각 배치의 입력마다 평균을 0, 편차를 1로 변환해서 출력해주는 레이어입니다.
오버피팅을 방지하는 등 드롭아웃과 비슷한 효과를 낸다고 합니다.

생성망의 전체적인 구조는 별다를게 없습니다.
노이즈 입력을 변환해서 업스케일링과 합성곱을 거쳐 이미지를 생성합니다.

판별기는 전형적인 CNN 모습이니 넘어갑니다.

다음은 컴파일인데 책에서는 생성망도 compile 메소드를 호출해줬지만 생성망 자체는 train 작업이 없습니다.
생성망과 판별망을 합쳐서 train하기 때문이죠.
그래서 그 부분은 빼고 했는데 잘 되는거 같습니다.

이전 글에서 trainable 속성 설명은 했으니 넘어가고

최적화기는 SGD(Stochastic Gradient Descent)를 썼네요.
Loss를 계산할때 전체 Batch가 아니라 Mini-Batch를 가지고 계산한다고 합니다.

get_z 메소드는 생성망의 입력으로 쓸 노이즈 데이터를 생성합니다.

train_once 메소드에서 판별망과 생성망+판별망을 차례로 한번씩 학습해주고 있습니다.
이때 판별망의 학습 데이터는 (Input: 실제 이미지*n + 생성된 가짜 이미지*n, Target: 진짜*n+가짜*n) 입니다.
생성망은 판별망이 진짜(1)라고 출력하게 해야하므로 학습 데이터는 (Input: 노이즈 데이터*n, Target: 진짜*n) 입니다.

그 다음은 별거 없습니다.


끝!




Comments