基礎GAN代碼解析
運行教程
使用Tensorflow 1.14.0版本可以直接運行。若Mnist數據集因為網絡原因下載不下來,可以通過以下鏈接下載壓縮包,解壓到項目根目錄即可。
Mnist數據集壓縮包
訓練過程會創建兩個文件夾,一個【out】目錄,存放着生成的圖片,一個是【mnist_gan】目錄,存放着保存着的權重文件。
代碼中GAN網絡結構:
- 網絡沒有采用卷積神經網絡的結構,就是最最基礎的神經網絡結構。
- 生成器G輸入的初始維度為128x100,輸出維度為128x784。生成器網絡一共有三層,分別是輸入層、中間層、輸出層。中間層的激活函數是relu函數,輸出層則用的是sigmod函數。
- 判別器D輸入的初始維度是128x784,輸出維度為128x1。中間層的激活函數同樣是relu函數,輸出層則用的同樣是sigmod函數。
- 判別器D和生成器G的損失函數都采用交叉熵函數。
- 生成器G的目標是 max(D(fake))
- 判別器D的目標是 min(D(fake)) + max(D(real))
GAN的代碼如下:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
from tensorflow.examples.tutorials.mnist import input_data
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
sess = tf.InteractiveSession()
def weight_var(shape, name): #定義權重,傳入權重shape和name
return tf.get_variable(name=name, shape=shape, initializer=tf.contrib.layers.xavier_initializer())
def bias_var(shape, name):#定義偏置,傳入偏置shape和name
return tf.get_variable(name=name, shape=shape, initializer=tf.constant_initializer(0))
def generator(z): #定義G,傳入隨機噪聲z,返回G的輸出。
G_h1 = tf.nn.relu(tf.matmul(z, G_W1) + G_b1) #G_h1中間層經過激活函數后的輸出。
G_log_prob = tf.matmul(G_h1, G_W2) + G_b2 #G輸出層沒有經過激活函數的輸出。
G_prob = tf.nn.sigmoid(G_log_prob)#G輸出層經過激活函數后的輸出。
return G_prob
def discriminator(x):#定義D,傳入樣本x,返回D的輸出和沒有經過激活函數的輸出。
D_h1 = tf.nn.relu(tf.matmul(x, D_W1) + D_b1)#D_h1中間層經過激活函數后的輸出
D_logit = tf.matmul(D_h1, D_W2) + D_b2#D輸出層沒有經過激活函數的輸出
D_prob = tf.nn.sigmoid(D_logit)#D輸出層經過激活函數后的輸出
return D_prob, D_logit
def sample_Z(m, n):#隨機噪聲向量的生成,維度為m*n
return np.random.uniform(-1., 1., size=[m, n])
def plot(samples):#畫圖
fig = plt.figure(figsize=(4, 4))
gs = gridspec.GridSpec(4, 4)
gs.update(wspace=0.05, hspace=0.05)
for i, sample in enumerate(samples): # [i,samples[i]] imax=16
ax = plt.subplot(gs[i])
plt.axis('off')
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.set_aspect('equal')
plt.imshow(sample.reshape(28, 28), cmap='Greys_r')
return fig
mb_size = 128
Z_dim = 100
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)#mnist數據集 one_hot是為了讓標簽二元,即只有0和1.
# discriminater net
X = tf.placeholder(tf.float32, shape=[None, 784], name='X') #樣本x的shape是【batchsize】【784】
D_W1 = weight_var([784, 128], 'D_W1') #D的中間層的w1
D_b1 = bias_var([128], 'D_b1')
D_W2 = weight_var([128, 1], 'D_W2')#D的輸出層的w2
D_b2 = bias_var([1], 'D_b2')
theta_D = [D_W1, D_W2, D_b1, D_b2]#D的參數列表
# generator net
#隨機噪聲向量z的shape是【batchsize】【100】
Z = tf.placeholder(tf.float32, shape=[None, 100], name='Z')
G_W1 = weight_var([100, 128], 'G_W1')#D的中間層的w1
G_b1 = bias_var([128], 'G_B1')
G_W2 = weight_var([128, 784], 'G_W2')#D的輸出層的w2
G_b2 = bias_var([784], 'G_B2')
theta_G = [G_W1, G_W2, G_b1, G_b2]#G的參數列表
G_sample = generator(Z) #調用generator(z)生成G樣本
D_real, D_logit_real = discriminator(X)#discriminator(x)辨別樣本
D_fake, D_logit_fake = discriminator(G_sample)
# D_loss = -tf.reduce_mean(tf.log(D_real) + tf.log(1. - D_fake))
# G_loss = -tf.reduce_mean(tf.log(D_fake))
#使用交叉熵代價函數,D的目標:對於真實樣本,target=1
D_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_logit_real, labels=tf.ones_like(D_logit_real)))
tf.summary.scalar("D_loss_real", D_loss_real)
#使用交叉熵代價函數,D的目標:對於生成器生成的樣本,target=0
D_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_logit_fake, labels=tf.zeros_like(D_logit_fake)))
tf.summary.scalar("D_loss_fake", D_loss_fake)
#D最后的損失函數為D(真)+D(假)
D_loss = D_loss_real + D_loss_fake
#使用交叉熵代價函數,G的目標:對於生成器生成的樣本,target=1
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(
logits=D_logit_fake, labels=tf.ones_like(D_logit_fake)))
tf.summary.scalar("G_loss", G_loss)
#使用GradientDescentOptimizer優化器
D_optimizer = tf.train.GradientDescentOptimizer(0.002).minimize(D_loss, var_list=theta_D)
G_optimizer = tf.train.GradientDescentOptimizer(0.002).minimize(G_loss, var_list=theta_G)
# init variables
sess.run(tf.global_variables_initializer())
if not os.path.exists('out/'):
os.makedirs('out/')
i = 0
summary_op = tf.summary.merge_all()
writer = tf.summary.FileWriter(".\mnist_gan",sess.graph)
for it in range(1000000):
if it % 1000 == 0:
#生成一個維度為16*100的向量,其值是-1.——1.的隨機值。
samples = sess.run(G_sample, feed_dict={
Z: sample_Z(16, Z_dim)})
fig = plot(samples)
plt.savefig('out/{}.png'.format(str(i).zfill(3)), bbox_inches='tight')
i += 1
plt.close(fig)
#調用了mnist里的方法,返回x和label
X_mb, _ = mnist.train.next_batch(mb_size)
#run(D_optimizer),開始進行梯度下降。
#run(D_loss),獲得d_loss值
#D喂入x樣本和Z樣本
_, D_loss_curr = sess.run([D_optimizer, D_loss], feed_dict={
X: X_mb, Z: sample_Z(mb_size, Z_dim)})
#G喂入Z樣本
_, G_loss_curr = sess.run([G_optimizer, G_loss], feed_dict={
Z: sample_Z(mb_size, Z_dim)})
result = sess.run(summary_op, feed_dict={X: X_mb, Z: sample_Z(mb_size, Z_dim)})
writer.add_summary(result, i)
if it % 1000 == 0:
print('Iter: {}'.format(it)) #用format()里的數字來替換“{}”
print('D_loss: {:.4}'.format(D_loss_curr))
print('G_loss: {:.4}'.format(G_loss_curr))
print()
Tensorflow函數基礎整理:
initializer變量初始化
-
tf.constant_initializer(value)
功能:將變量初始化為給定的常量,初始化一切所提供的值。
-
tf.random_normal_initializer(mean,stddev)
功能:將變量初始化為滿足正態分布的隨機值,主要參數(正太分布的均值和標准差),用所給的均值和標准差初始化均勻分布。
-
tf.truncated_normal_initializer(mean,stddev,seed,dtype)
mean:用於指定均值;stddev用於指定標准差;
seed:用於指定隨機數種子;dtype:用於指定隨機數的數據類型。
功能:將變量初始化為滿足正態分布的隨機值,但如果隨機出來的值偏離平均值超過2個標准差,那么這個數將會被重新隨機,通常只需要設定一個標准差stddev這一個參數就可以。
-
tf.random_uniform_initializer(a,b,seed,dtype)
功能:從a到b均勻初始化,將變量初始化為滿足平均分布的隨機值,主要參數(最大值,最小值)
優化器構造
-
compute_gradients(loss,var_list=None,gate_gradients=GATE_OP,aggregation_method=None,colocate_gradients_with_ops=False,grad_loss=None)
作用:對於在變量列表(var_list)中的變量計算對於損失函數的梯度,這個函數返回一個(梯度,變量)對的列表,其中梯度就是相對應變量的梯度了。這是minimize()函數的第一個部分,
參數:
- loss: 待減小的值;
- var_list: 默認是在GraphKey.TRAINABLE_VARIABLES.
-
apply_gradients(grads_and_vars,global_step=None,name=None)
作用:把梯度“應用”(Apply)到變量上面去。其實就是按照梯度下降的方式加到上面去。這是minimize()函數的第二個步驟。 返回一個應用的操作。
參數:
-
grads_and_vars: compute_gradients()函數返回的(gradient, variable)對的列表
-
global_step: Optional Variable to increment by one after the variables have been updated.
-
-
minimize(loss,global_step=None,var_list=None,gate_gradients=GATE_OP,aggregation_method=None,colocate_gradients_with_ops=False,name=None,grad_loss=None)
TF初始化:
-
sess.run(tf.global_variables_initializer())
解析:函數中調用了 variable_initializer() 和 global_variables()
global_variables() 返回一個 Variable list ,里面保存的是 gloabal variables。variable_initializer() 將 Variable list 中的所有 Variable 取出來,將其 variable.initializer 屬性做成一個 op group。然后看 Variable 類的源碼可以發現, variable.initializer 就是一個 assign op。
所以: sess.run(tf.global_variables_initializer()) 就是 run了所有global Variable 的 assign op,這就是初始化參數的本來面目。