他的方法是通过随机生成的维度为1000的向量，生成大小为64*64的狗狗图。但经过较长时间的训练，设置了多种超参数进行调试，仍感觉效果不理想，总是在训练到一定程度之后，生成器的loss就不再改变，成为一个固定值，生成的图片也看不出狗的样子。

1.对TensorFlow的卷积、反卷积、全连接等操作进行封装，使其变量名称规整且方便调用。

 1 def conv2d(name, tensor,ksize, out_dim, stddev=0.01, stride=2, padding='SAME'):
2     with tf.variable_scope(name):
3         w = tf.get_variable('w', [ksize, ksize, tensor.get_shape()[-1],out_dim], dtype=tf.float32,
4                             initializer=tf.random_normal_initializer(stddev=stddev))
6         b = tf.get_variable('b', [out_dim], 'float32',initializer=tf.constant_initializer(0.01))
8
9 def deconv2d(name, tensor, ksize, outshape, stddev=0.01, stride=2, padding='SAME'):
10     with tf.variable_scope(name):
11         w = tf.get_variable('w', [ksize, ksize, outshape[-1], tensor.get_shape()[-1]], dtype=tf.float32,
12                             initializer=tf.random_normal_initializer(stddev=stddev))
14         b = tf.get_variable('b', [outshape[-1]], 'float32', initializer=tf.constant_initializer(0.01))
16
17 def fully_connected(name,value, output_shape):
18     with tf.variable_scope(name, reuse=None) as scope:
19         shape = value.get_shape().as_list()
20         w = tf.get_variable('w', [shape[1], output_shape], dtype=tf.float32,
21                                     initializer=tf.random_normal_initializer(stddev=0.01))
22         b = tf.get_variable('b', [output_shape], dtype=tf.float32, initializer=tf.constant_initializer(0.0))
23         return tf.matmul(value, w) + b
24
25 def relu(name, tensor):
26     return tf.nn.relu(tensor, name)
27
28 def lrelu(name,x, leak=0.2):
29     return tf.maximum(x, leak * x, name=name)

relu()和lrelu()函数是两个激活函数，lrelu()其中LeakyRelu激活函数的实现，它能够减轻RELU的稀疏性。

2.对判别器进行构建。

 1 def Discriminator(name,inputs,reuse):
2     with tf.variable_scope(name, reuse=reuse):
3         output = tf.reshape(inputs, [-1, pic_height_width, pic_height_width, inputs.shape[-1]])
4         output1 = conv2d('d_conv_1', output, ksize=5, out_dim=DEPTH) #32*32
5         output2 = lrelu('d_lrelu_1', output1)
6
7         output3 = conv2d('d_conv_2', output2, ksize=5, padding="VALID",stride=1,out_dim=DEPTH) #28*28
8         output4 = lrelu('d_lrelu_2', output3)
9
10         output5 = conv2d('d_conv_3', output4, ksize=5, out_dim=2*DEPTH) #14*14
11         output6 = lrelu('d_lrelu_3', output5)
12
13         output7 = conv2d('d_conv_4', output6, ksize=5, out_dim=4*DEPTH) #7*7
14         output8 = lrelu('d_lrelu_4', output7)
15
16         output9 = conv2d('d_conv_5', output8, ksize=5, out_dim=6*DEPTH) #4*4
17         output10 = lrelu('d_lrelu_5', output9)
18
19         output11 = conv2d('d_conv_6', output10, ksize=5, out_dim=8*DEPTH) #2*2
20         output12 = lrelu('d_lrelu_6', output11)
21
22         chanel = output12.get_shape().as_list()
23         output13 = tf.reshape(output12, [batch_size, chanel[1]*chanel[2]*chanel[3]])
24         output0 = fully_connected('d_fc', output13, 1)
25         return output0

3.对生成器进行构建。

 1 def generator(name, reuse=False):
2     with tf.variable_scope(name, reuse=reuse):
3         noise = tf.random_normal([batch_size, 128])#.astype('float32')
4
5         noise = tf.reshape(noise, [batch_size, 128], 'noise')
6         output = fully_connected('g_fc_1', noise, 2*2*8*DEPTH)
7         output = tf.reshape(output, [batch_size, 2, 2, 8*DEPTH], 'g_conv')
8
9         output = deconv2d('g_deconv_1', output, ksize=5, outshape=[batch_size, 4, 4, 6*DEPTH])
10         output = tf.nn.relu(output)
11         # output = tf.reshape(output, [batch_size, 4, 4, 6*DEPTH])
12
13         output = deconv2d('g_deconv_2', output, ksize=5, outshape=[batch_size, 7, 7, 4* DEPTH])
14         output = tf.nn.relu(output)
15
16         output = deconv2d('g_deconv_3', output, ksize=5, outshape=[batch_size, 14, 14, 2*DEPTH])
17         output = tf.nn.relu(output)
18
19         output = deconv2d('g_deconv_4', output, ksize=5, outshape=[batch_size, 28, 28, DEPTH])
20         output = tf.nn.relu(output)
21
22         output = deconv2d('g_deconv_5', output, ksize=5, outshape=[batch_size, 32, 32, DEPTH],stride=1, padding='VALID')
23         output = tf.nn.relu(output)
24
25         output = deconv2d('g_deconv_6', output, ksize=5, outshape=[batch_size, OUTPUT_SIZE, OUTPUT_SIZE, 3])
26         # output = tf.nn.relu(output)
27         output = tf.nn.sigmoid(output)
28         return tf.reshape(output,[-1,OUTPUT_SIZE,OUTPUT_SIZE,3])

4.数据预处理。

 1 def load_data(path):
2     X_train = []
3     img_list = glob.glob(path + '/*.jpg')
4     for img in img_list:
6         _img = cv2.resize(_img, (pic_height_width, pic_height_width))
7         X_train.append(_img)
8     print('训练集图像数目：',len(X_train))
9     # print(X_train[0],type(X_train[0]),X_train[0].shape)
10     return np.array(X_train, dtype=np.uint8)
11
12 def normalization(input_matirx):
13     input_shape = input_matirx.shape
14     total_dim = 1
15     for i in range(len(input_shape)):
16         total_dim = total_dim*input_shape[i]
17     big_vector = input_matirx.reshape(total_dim,)
18     out_vector = []
19     for i in range(len(big_vector)):
20         out_vector.append(big_vector[i]/256)    # 0~256值归一化
21     out_vector = np.array(out_vector)
22     out_matrix = out_vector.reshape(input_shape)
23     return out_matrix
24
25 def denormalization(input_matirx):
26     input_shape = input_matirx.shape
27     total_dim = 1
28     for i in range(len(input_shape)):
29         total_dim = total_dim*input_shape[i]
30     big_vector = input_matirx.reshape(total_dim,)
31     out_vector = []
32     for i in range(len(big_vector)):
33         out_vector.append(big_vector[i]*256)    # 0~256值还原
34     out_vector = np.array(out_vector)
35     out_matrix = out_vector.reshape(input_shape)
36     return out_matrix

normalization()函数和denormalization()函数用来对图像数据进行归一化和反归一化，每个像素在单个通道中的取值在[0~256]之间，我们需要将其归一化到[0,1]范围内，否则在模型训练过程中loss会出现较大波动；在使用生成器得到生成结果之后，每个像素内的数值都在[0,1]之间，需要将其反归一化到[0~256]。这一步也必不可少，最开始的几次试验没有对数据进行归一化，导致loss巨大，且难以收敛。

5.模型训练。

 1 def train():
2     with tf.variable_scope(tf.get_variable_scope()):
3         real_data = tf.placeholder(tf.float32, shape=[batch_size,pic_height_width,pic_height_width,3])
4         with tf.variable_scope(tf.get_variable_scope()):
5             fake_data = generator('gen',reuse=False)
6             disc_real = Discriminator('dis_r',real_data,reuse=False)
7             disc_fake = Discriminator('dis_r',fake_data,reuse=True)
8         """获取变量列表，d_vars为判别器参数，g_vars为生成器的参数"""
9         t_vars = tf.trainable_variables()
10         d_vars = [var for var in t_vars if 'd_' in var.name]
11         g_vars = [var for var in t_vars if 'g_' in var.name]
12         '''计算损失'''
13         gen_cost = -tf.reduce_mean(disc_fake)
14         disc_cost = tf.reduce_mean(disc_fake) - tf.reduce_mean(disc_real)
15
16         alpha = tf.random_uniform(
17             shape=[batch_size, 1],minval=0.,maxval=1.)
18         differences = fake_data - real_data
19         interpolates = real_data + (alpha * differences)
22         gradient_penalty = tf.reduce_mean((slopes - 1.) ** 2)
23         disc_cost += LAMBDA * gradient_penalty
24         """定义优化器optimizer"""
25         with tf.variable_scope(tf.get_variable_scope(), reuse=None):
26             gen_train_op = tf.train.RMSPropOptimizer(
27                 learning_rate=1e-4,decay=0.9).minimize(gen_cost,var_list=g_vars)
28             disc_train_op = tf.train.RMSPropOptimizer(
29                 learning_rate=1e-4,decay=0.9).minimize(disc_cost,var_list=d_vars)
30         saver = tf.train.Saver()
31         sess = tf.InteractiveSession()
32         coord = tf.train.Coordinator()
34         """初始化参数"""
35         init = tf.global_variables_initializer()
36         sess.run(init)
37         '''获得数据'''
39         dog_data = normalization(dog_data)
40         for epoch in range (1, EPOCH):
41             for iters in range(IDXS):
42                 if(iters%4==3):
43                     img = dog_data[(iters%4)*batch_size:]
44                 else:
45                     img = dog_data[(iters%4)*batch_size:((iters+1)%4)*batch_size]
46                 for x in range(1):           # TODO 在对一批数据展开训练时，训练几次生成器
47                     _, g_loss = sess.run([gen_train_op, gen_cost])
48                 for x in range(0,3):        # TODO 训练一次生成器，训练几次判别器...
49                     _, d_loss = sess.run([disc_train_op, disc_cost], feed_dict={real_data: img})
50                 print("[%4d:%4d/%4d] d_loss: %.8f, g_loss: %.8f"%(epoch, iters, IDXS, d_loss, g_loss))
51
52             with tf.variable_scope(tf.get_variable_scope()):
53                 samples = generator('gen', reuse=True)
54                 samples = tf.reshape(samples, shape=[batch_size,pic_height_width,pic_height_width,3])
55                 samples=sess.run(samples)
56                 samples = denormalization(samples)  # 还原0~256 RGB 通道数值
57                 save_images(samples, [8,8], os.getcwd()+'/img/'+'sample_%d_epoch.png' % (epoch))
58
59             if epoch%10==9:
60                 checkpoint_path = os.path.join(os.getcwd(),
61                                                './models/WGAN/my_wgan-gp.ckpt')
62                 saver.save(sess, checkpoint_path, global_step=epoch)
63                 print('*********    model saved    *********')
64         coord.request_stop()
66         sess.close()

这次实验的代码在github上进行了保存：https://github.com/NosenLiu/Dog-Generator-by-TensorFlow   除了这个WGAN模型的训练代码外，还有对进行模型、加载、再训练的相关内容。有兴趣的朋友可以关注(star☆)一下。

.总结感悟

通过这次实践，对WGAN模型有了一定程度的理解，尤其是对生成器的训练是十分到位的，比较容易出效果。但是由于它的判别器最后一层没有sigmoid函数，单独应用这个判别器对一个图像进行计算，根据计算结果，很难直接的得到这张图片中是狗图的概率。

https://blog.csdn.net/LEE18254290736/article/details/97371930

https://zhuanlan.zhihu.com/p/25071913

https://blog.csdn.net/xg123321123/article/details/78034859