2-1. 텐서플로를 이용한 GAN을 구현하는 방법
- 가중치 초기화하기
- GAN 구축
- GAN 학습
2-2. GAN : 오토인코더와 마찬가지로 대표적인 자율 학습 모델의 하나임
- 위폐범과 경찰이 연관된 위조화폐 제작과 감별을 GAN으로 구현
- 조건은 다음과 같음 :
- 위폐범은 위조화폐를 정교하게 만들고자 최선을 다함. 즉, 경찰이 위조화폐를 감별하지 못할 확률을 높이려고 노력함
- 경찰은 위폐를 감별하는데 최선을 다함. 즉, 자신이 실수할 확률을 낮추려고 노력함
- 위폐범(G)의 네트워크를 생성 네트워크(Generative Network)라고 함
- 경찰(D)의 네트워크를 식별 네트워크(Discriminator Network)라고 함
- G는 D가 실수할 확률을 높이려고 노력(maximize)하고, D는 자신의 실수 확률을 낮추려고 노력(minimize)하므로, 이는 미니맥스 문제(minimax problem)임
- GAN에서는 어려운 확률분포를 다루지 않고 확률을 통해 생성한 샘플을 다룸
- G는 Z를 입력받으므로 G(Z)라고 함. 이때 Z는 확률분포와 맵핑하는 prior라는 개념임. 무작위 노이즈(Random noise)가 됨
- G(Z)의 결과는 위폐화폐임
- D는 이미지 X를 입력받으므로 D(X)라고 함. D(X)의 결과물은 0~1 사이의 확률임
- 양자의 균형을 맞추는 평행 상태에 이르면 G는 진짜 화폐와 100% 같은 위조화폐를 만들어 D가 이를 감별할 확률은 0.5임
🟡 필요한 모듈 불러오기
# 필요한 모듈을 불러오기 MNIST 데이터를 저장함
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
mnist = tf.keras.datasets.mnist.load_data(
path='mnist.npz')
🟡 가중치 초기화
# 가중치의 초기화에 좋은 성능을 보이는 세이비어 초기화(xavier initialization)를 함수로 만들어서 이용함
def xavier_init(size):
in_dim = size[0]
xavier_stddev = 1. / tf.sqrt(in_dim / 2.)
return tf.random_normal(shape = size, stddev = xavier_stddev)
# compatibility mode를 적용해 1.x 버전의 기능을 그대로 사용할 수 있음
# 위와 같이 tf.disable_v2_behavior()를 실행해서 tf.placeholder를 그대로 사용할 수 있음
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
# 확률을 출력하는 식별 네트워크에서 사용할 가중치를 설정함
# 노드 수는 784 -> 128 -> 1의 순서로 줄임
# 식별 네트워크
X = tf.placeholder(tf.float32, shape = [None, 784], name = 'X')
W1_dis = tf.Variable(xavier_init([784, 128]), name = 'W1_dis')
b1_dis = tf.Variable(tf.zeros(shape=[128]), name = 'b1_dis')
W2_dis = tf.Variable(xavier_init([128, 1]), name = 'W2_dis')
b2_dis = tf.Variable(tf.zeros(shape=[1]), name = 'b2_dis')
theta_dis = [W1_dis, W2_dis, b1_dis, b2_dis]
# 생성 네트워크
Z = tf.placeholder(tf.float32, shape = [None, 100], name = 'Z')
W1_gen = tf.Variable(xavier_init([100, 128]), name = 'W1_gen')
b1_gen = tf.Variable(tf.zeros(shape=[128]), name = 'b1_gen')
W2_gen = tf.Variable(xavier_init([128, 784]), name = 'W2_gen')
b2_gen = tf.Variable(tf.zeros(shape=[784]), name = 'b2_gen')
theta_gen = [W1_gen, W2_gen, b1_gen, b2_gen]
def random_Z(z1, z2):
return np.random.uniform(-1., 1., size = [z1, z2])
🟡 생성 네트워크 구축
# 생성 네트워크를 구축함
# 이미지 출력
def gen(z):
h1_gen = tf.nn.relu(tf.matmul(z, W1_gen) + b1_gen)
log_prob_gen = tf.matmul(h1_gen, W2_gen) + b2_gen
prob_gen = tf.nn.sigmoid(log_prob_gen)
return prob_gen
🟡 식별 네트워크 구축
# 식별 네트워크를 구축함
# 확률을 출력하게 하는 것임
def dis(x):
h1_dis = tf.nn.relu(tf.matmul(x, W1_dis) + b1_dis)
logit_dis = tf.matmul(h1_dis, W2_dis) + b2_dis
prob_dis = tf.nn.sigmoid(logit_dis)
return prob_dis, logit_dis
# plot 함수는 16개의 이미지를 4x4 형태로 보여줌
# 그래프 구축을 완료한 이후 실행할 때, 사용하려고 미리 정의해둔 것임
def plot(samples):
fig = plt.figure(figsize = (4, 4))
grid = gridspec.GridSpec(4, 4)
grid.update(wspace = 0.1, hspace = 0.1)
for i, sample in enumerate(samples):
ax = plt.subplot(grid[i])
plt.axis('off')
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect('equal')
plt.imshow(sample.reshape(28, 28), cmap = 'gray')
return fig
🟡 GAN 학습하기
# GAN을 학습시키기 위한 Adversarial 과정을 선언함
sample_gen = gen(Z)
real_dis, logit_real_dis = dis(X)
fake_dis, logit_fake_dis = dis(sample_gen)
loss_real_dis = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits = logit_real_dis, labels = tf.ones_like(logit_real_dis)))
loss_fake_dis = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits = logit_fake_dis, labels = tf.zeros_like(logit_fake_dis)))
loss_dis = loss_real_dis + loss_fake_dis
loss_gen = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits = logit_fake_dis, labels = tf.ones_like(logit_fake_dis)))
solver_dis = tf.train.AdamOptimizer().minimize(loss_dis, var_list=theta_dis)
solver_gen = tf.train.AdamOptimizer().minimize(loss_gen, var_list=theta_gen)
# 그래프 실행
batch_size = 128
dim_Z = 100
sess = tf.Session()
sess.run(tf.global_variables_initializer())
i = 0
for j in range(100000):
if j % 2000 == 0:
samples = sess.run(sample_gen, feed_dict={Z: random_Z(16, dim_Z)})
fig = plot(samples)
plt.show()
i += 1
plt.close(fig)
X_batch, _ = mnist.train.next_batch(batch_size)
_, loss_curr_dis = sess.run([solver_dis, loss_dis],
feed_dict={X: X_batch, Z: random_Z(batch_size, dim_Z)})
_, loss_curr_gen = sess.run([solver_gen, loss_gen],
feed_dict={Z: random_Z(bath_size, dim_Z)})
if j % 2000 == 0:
print('Iteration: {}'.format(j))
print('Discriminator loss: {:.3}'. format(loss_curr_dis))
print('Generator loss: {:.3}'. format(loss_curr_gen))
print()