CGAN-TensorFlow

CGAN-TensorFlow

[TOC]

CGAN-TensorFlow

原文链接:Conditional Generative Adversarial Nets in TensorFlow

We have seen the Generative Adversarial Nets (GAN) model in the previous post. We have also seen the arch nemesis of GAN, the VAE and its conditional variation: Conditional VAE (CVAE). Hence, it is only proper for us to study conditional variation of GAN, called Conditional GAN or CGAN for short.
我们在上一篇文章中看到了生成对抗网络(GAN)模型。我们还看到了 GAN 的死对头,VAE 及其条件变体:条件 VAE(CVAE)。因此,我们研究 GAN 的条件变体(称为条件 GAN 或简称 CGAN)是再合适不过的了。

CGAN: Formulation and Architecture

Recall, in GAN, we have two neural nets: the generator and the discriminator .
Now, as we want to condition those networks with some vector , the easiest way to do it is to feed into both networks. Hence, our generator and discriminator are now and respectively.
回想一下,在 GAN 中,我们有两个神经网络:生成器 和鉴别器
现在,由于我们想用某个向量 来调节这些网络,最简单的方法是将 输入两个网络。因此,我们的生成器和鉴别器现在分别是

We can see it with a probabilistic point of view. is modeling the distribution of our data, given and , that is, our data is generated with this scheme .
我们可以用概率的角度来看待它。给定正在对我们的数据分布进行建模,也就是说,我们的数据是用这个方案生成的。

Likewise for the discriminator, now it tries to find discriminating label for and , that are modeled with .
同样对于鉴别器来说,现在它试图为 找到鉴别标签,用 建模。

Hence, we could see that both and is jointly conditioned to two variables or and .
因此,我们可以看到 都与两个变量 联合相关。

Now, the objective function is given by:
现在,目标函数如下:

If we compare the above loss to GAN loss, the difference only lies in the additional parameter in both and .
如果我们将上述损失与 GAN 损失进行比较,差异仅在于 中的附加参数

The architecture of CGAN is now as follows (taken from [1]):
CGAN 的架构如下(取自[1]):

In contrast with the architecture of GAN, we now has an additional input layer in both discriminator net and generator net.
与 GAN 的架构相比,我们现在在鉴别器网络和生成器网络中都增加了一个输入层。

CGAN: Implementation in TensorFlow

I’d like to direct the reader to the previous post about GAN, particularly for the implementation in TensorFlow. Implementing CGAN is so simple that we just need to add a handful of lines to the original GAN implementation. So, here we will only look at those modifications.
我想引导读者阅读上一篇关于 GAN 的文章,特别是关于在 TensorFlow 中的实现。实现 CGAN 非常简单,我们只需要在原始 GAN 实现中添加几行代码即可。因此,我们在这里只讨论这些修改。

Generative Adversarial Nets in TensorFlow

The first additional code for CGAN is here:
CGAN 的第一个附加代码在这里:

1
y = tf.placeholder(tf.float32, shape=[None, y_dim])

We are adding new input to hold our variable we are conditioning our CGAN to.
我们正在添加新输入来保存我们用来调节 CGAN 的变量。

Next, we add it to both our generator net and discriminator net:
接下来,我们将其添加到我们的生成器网络和鉴别器网络中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def generator(z, y):
# Concatenate z and y
inputs = tf.concat(concat_dim=1, values=[z, y])

G_h1 = tf.nn.relu(tf.matmul(inputs, G_W1) + G_b1)
G_log_prob = tf.matmul(G_h1, G_W2) + G_b2
G_prob = tf.nn.sigmoid(G_log_prob)

return G_prob


def discriminator(x, y):
# Concatenate x and y
inputs = tf.concat(concat_dim=1, values=[x, y])

D_h1 = tf.nn.relu(tf.matmul(inputs, D_W1) + D_b1)
D_logit = tf.matmul(D_h1, D_W2) + D_b2
D_prob = tf.nn.sigmoid(D_logit)

return D_prob, D_logit

The problem we have here is how to incorporate the new variable into and . As we are trying to model the joint conditional, the simplest way to do it is to just concatenate both variables. Hence, in , we are concatenating and before we feed it into the networks. The same procedure is applied to .
我们这里的问题是如何将新变量 合并到 中。由于我们试图对联合条件进行建模,最简单的方法就是将两个变量连接起来。因此,在 中,我们在将其输入网络之前将 连接起来。同样的程序也适用于

Of course, as our inputs for and is now different than the original GAN, we need to modify our weights:
当然,由于我们对 的输入现在与原始 GAN 不同,我们需要修改我们的权重:

1
2
3
4
5
6
7
# Modify input to hidden weights for discriminator
# 修改鉴别器隐藏权重的输入
D_W1 = tf.Variable(shape=[X_dim + y_dim, h_dim]))

# Modify input to hidden weights for generator
# 修改生成器隐藏权重的输入
G_W1 = tf.Variable(shape=[Z_dim + y_dim, h_dim]))

That is, we just adjust the dimensionality of our weights.
也就是说,我们只是调整权重的维数。

Next, we just use our new networks:
接下来,我们只需使用我们的新网络:

1
2
3
4
5
# Add additional parameter y into all networks
# 在所有网络中添加附加参数 y
G_sample = generator(Z, y)
D_real, D_logit_real = discriminator(X, y)
D_fake, D_logit_fake = discriminator(G_sample, y)

And finally, when training, we also feed the value of into the networks:
最后,在训练时,我们还将 的值输入网络:

1
2
3
4
5
X_mb, y_mb = mnist.train.next_batch(mb_size)

Z_sample = sample_Z(mb_size, Z_dim)
_, D_loss_curr = sess.run([D_solver, D_loss], feed_dict={X: X_mb, Z: Z_sample, y:y_mb})
_, G_loss_curr = sess.run([G_solver, G_loss], feed_dict={Z: Z_sample, y:y_mb})

As an example above, we are training our GAN with MNIST data, and the conditional variable is the labels.
如上例,我们使用 MNIST 数据训练我们的 GAN,条件变量 是标签。

CGAN: Results

At test time, we want to generate new data samples with certain label. For example, we set the label to be 5, i.e. we want to generate digit “5”:
在测试时,我们希望生成具有特定标签的新数据样本。例如,我们将标签设置为 5,即我们希望生成数字“5”:

1
2
3
4
5
6
7
8
9
n_sample = 16
Z_sample = sample_Z(n_sample, Z_dim)

# Create conditional one-hot vector, with index 5 = 1
# 创建条件独热向量,索引 5 = 1
y_sample = np.zeros(shape=[n_sample, y_dim])
y_sample[:, 5] = 1

samples = sess.run(G_sample, feed_dict={Z: Z_sample, y:y_sample})

Above, we just sample , and then construct the conditional variables. In our example case, the conditional variables is a collection of one-hot vectors with value 1 in the 5th index. The last thing we need to is to run the network with those variables as inputs.
上面,我们只是对 进行采样,然后构造条件变量。在我们的示例中,条件变量是第 5 个索引中值为 1 的独热向量集合。我们需要做的最后一件事是使用这些变量作为输入来运行网络。

Here is the results:
结果如下:

Looks pretty much like digit 5, right?
看起来很像数字 5,对吧?

If we set our one-hot vectors to have value of 1 in the 7th index:
如果我们将独热向量设置为第 7 个索引中的值为 1:

Those results confirmed that have successfully trained our CGAN.
这些结果证实了我们的 CGAN 训练成功。

完整代码

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
135
136
137
138
139
140
141
142
143
144
145
# cgan_pytorch.py
import torch
import torch.nn.functional as nn
import torch.autograd as autograd
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os
from torch.autograd import Variable
from tensorflow.examples.tutorials.mnist import input_data


mnist = input_data.read_data_sets('../../MNIST_data', one_hot=True)
mb_size = 64
Z_dim = 100
X_dim = mnist.train.images.shape[1]
y_dim = mnist.train.labels.shape[1]
h_dim = 128
cnt = 0
lr = 1e-3


def xavier_init(size):
in_dim = size[0]
xavier_stddev = 1. / np.sqrt(in_dim / 2.)
return Variable(torch.randn(*size) * xavier_stddev, requires_grad=True)


""" ==================== GENERATOR ======================== """

Wzh = xavier_init(size=[Z_dim + y_dim, h_dim])
bzh = Variable(torch.zeros(h_dim), requires_grad=True)

Whx = xavier_init(size=[h_dim, X_dim])
bhx = Variable(torch.zeros(X_dim), requires_grad=True)


def G(z, c):
inputs = torch.cat([z, c], 1)
h = nn.relu(inputs @ Wzh + bzh.repeat(inputs.size(0), 1))
X = nn.sigmoid(h @ Whx + bhx.repeat(h.size(0), 1))
return X


""" ==================== DISCRIMINATOR ======================== """

Wxh = xavier_init(size=[X_dim + y_dim, h_dim])
bxh = Variable(torch.zeros(h_dim), requires_grad=True)

Why = xavier_init(size=[h_dim, 1])
bhy = Variable(torch.zeros(1), requires_grad=True)


def D(X, c):
inputs = torch.cat([X, c], 1)
h = nn.relu(inputs @ Wxh + bxh.repeat(inputs.size(0), 1))
y = nn.sigmoid(h @ Why + bhy.repeat(h.size(0), 1))
return y


G_params = [Wzh, bzh, Whx, bhx]
D_params = [Wxh, bxh, Why, bhy]
params = G_params + D_params


""" ===================== TRAINING ======================== """


def reset_grad():
for p in params:
if p.grad is not None:
data = p.grad.data
p.grad = Variable(data.new().resize_as_(data).zero_())


G_solver = optim.Adam(G_params, lr=1e-3)
D_solver = optim.Adam(D_params, lr=1e-3)

ones_label = Variable(torch.ones(mb_size, 1))
zeros_label = Variable(torch.zeros(mb_size, 1))


for it in range(100000):
# Sample data
z = Variable(torch.randn(mb_size, Z_dim))
X, c = mnist.train.next_batch(mb_size)
X = Variable(torch.from_numpy(X))
c = Variable(torch.from_numpy(c.astype('float32')))

# Dicriminator forward-loss-backward-update
G_sample = G(z, c)
D_real = D(X, c)
D_fake = D(G_sample, c)

D_loss_real = nn.binary_cross_entropy(D_real, ones_label)
D_loss_fake = nn.binary_cross_entropy(D_fake, zeros_label)
D_loss = D_loss_real + D_loss_fake

D_loss.backward()
D_solver.step()

# Housekeeping - reset gradient
reset_grad()

# Generator forward-loss-backward-update
z = Variable(torch.randn(mb_size, Z_dim))
G_sample = G(z, c)
D_fake = D(G_sample, c)

G_loss = nn.binary_cross_entropy(D_fake, ones_label)

G_loss.backward()
G_solver.step()

# Housekeeping - reset gradient
reset_grad()

# Print and plot every now and then
if it % 1000 == 0:
print('Iter-{}; D_loss: {}; G_loss: {}'.format(it, D_loss.data.numpy(), G_loss.data.numpy()))

c = np.zeros(shape=[mb_size, y_dim], dtype='float32')
c[:, np.random.randint(0, 10)] = 1.
c = Variable(torch.from_numpy(c))
samples = G(z, c).data.numpy()[:16]

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):
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')

if not os.path.exists('out/'):
os.makedirs('out/')

plt.savefig('out/{}.png'.format(str(cnt).zfill(3)), bbox_inches='tight')
cnt += 1
plt.close(fig)
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
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import os


mnist = input_data.read_data_sets('../../MNIST_data', one_hot=True)
mb_size = 64
Z_dim = 100
X_dim = mnist.train.images.shape[1]
y_dim = mnist.train.labels.shape[1]
h_dim = 128


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)


""" Discriminator Net model """
X = tf.placeholder(tf.float32, shape=[None, 784])
y = tf.placeholder(tf.float32, shape=[None, y_dim])

D_W1 = tf.Variable(xavier_init([X_dim + y_dim, h_dim]))
D_b1 = tf.Variable(tf.zeros(shape=[h_dim]))

D_W2 = tf.Variable(xavier_init([h_dim, 1]))
D_b2 = tf.Variable(tf.zeros(shape=[1]))

theta_D = [D_W1, D_W2, D_b1, D_b2]


def discriminator(x, y):
inputs = tf.concat(axis=1, values=[x, y])
D_h1 = tf.nn.relu(tf.matmul(inputs, D_W1) + D_b1)
D_logit = tf.matmul(D_h1, D_W2) + D_b2
D_prob = tf.nn.sigmoid(D_logit)

return D_prob, D_logit


""" Generator Net model """
Z = tf.placeholder(tf.float32, shape=[None, Z_dim])

G_W1 = tf.Variable(xavier_init([Z_dim + y_dim, h_dim]))
G_b1 = tf.Variable(tf.zeros(shape=[h_dim]))

G_W2 = tf.Variable(xavier_init([h_dim, X_dim]))
G_b2 = tf.Variable(tf.zeros(shape=[X_dim]))

theta_G = [G_W1, G_W2, G_b1, G_b2]


def generator(z, y):
inputs = tf.concat(axis=1, values=[z, y])
G_h1 = tf.nn.relu(tf.matmul(inputs, G_W1) + G_b1)
G_log_prob = tf.matmul(G_h1, G_W2) + G_b2
G_prob = tf.nn.sigmoid(G_log_prob)

return G_prob


def sample_Z(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):
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


G_sample = generator(Z, y)
D_real, D_logit_real = discriminator(X, y)
D_fake, D_logit_fake = discriminator(G_sample, y)

D_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_real, labels=tf.ones_like(D_logit_real)))
D_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.zeros_like(D_logit_fake)))
D_loss = D_loss_real + D_loss_fake
G_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logit_fake, labels=tf.ones_like(D_logit_fake)))

D_solver = tf.train.AdamOptimizer().minimize(D_loss, var_list=theta_D)
G_solver = tf.train.AdamOptimizer().minimize(G_loss, var_list=theta_G)


sess = tf.Session()
sess.run(tf.global_variables_initializer())

if not os.path.exists('out/'):
os.makedirs('out/')

i = 0

for it in range(1000000):
if it % 1000 == 0:
n_sample = 16

Z_sample = sample_Z(n_sample, Z_dim)
y_sample = np.zeros(shape=[n_sample, y_dim])
y_sample[:, 7] = 1

samples = sess.run(G_sample, feed_dict={Z: Z_sample, y:y_sample})

fig = plot(samples)
plt.savefig('out/{}.png'.format(str(i).zfill(3)), bbox_inches='tight')
i += 1
plt.close(fig)

X_mb, y_mb = mnist.train.next_batch(mb_size)

Z_sample = sample_Z(mb_size, Z_dim)
_, D_loss_curr = sess.run([D_solver, D_loss], feed_dict={X: X_mb, Z: Z_sample, y:y_mb})
_, G_loss_curr = sess.run([G_solver, G_loss], feed_dict={Z: Z_sample, y:y_mb})

if it % 1000 == 0:
print('Iter: {}'.format(it))
print('D loss: {:.4}'. format(D_loss_curr))
print('G_loss: {:.4}'.format(G_loss_curr))
print()

修改后代码

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# 导入必要库
# PyTorch用于深度学习
import torch
import torch.nn.functional as nn
import torch.autograd as autograd
import torch.optim as optim
# NumPy用于数值计算
import numpy as np
# Matplotlib用于绘图
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
# os用于文件操作
import os
from torch.autograd import Variable
# TensorFlow用于加载MNIST数据集
import tensorflow as tf
#from tensorflow.examples.tutorials.mnist import input_data
import csv

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 将图像数据标准化到 [0, 1] 范围
x_train = x_train.reshape(x_train.shape[0], 784).astype('float32') / 255.
x_test = x_test.reshape(x_test.shape[0], 784).astype('float32') / 255.

# 将标签转换为 one-hot 编码
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# 不再使用原来的mnist.train.next_batch()方法,我们需要创建一个函数来模拟这个功能:
def next_batch(num, data, labels):
idx = np.arange(0 , len(data))
np.random.shuffle(idx)
idx = idx[:num]
data_shuffle = [data[i] for i in idx]
labels_shuffle = [labels[i] for i in idx]
return np.asarray(data_shuffle), np.asarray(labels_shuffle)


# mnist = input_data.read_data_sets('../../MNIST_data', one_hot=True) # 加载MNIST数据集,旧的TensorFlow版本
mb_size = 64 #设置mini-batch大小为64
Z_dim = 100 #设置随机向量维度为100
X_dim = x_train.shape[1]
y_dim = y_train.shape[1]
#X_dim = mnist.train.images.shape[1] # 设置输入图像维度(784,因为MNIST图像是28x28=784像素)
#y_dim = mnist.train.labels.shape[1] # 设置标签维度(10,因为MNIST有10个类别)
h_dim = 128 #设置隐藏层维度为128
c = 0 #初始化计数器c
lr = 1e-3 #学习率lr


# Xavier初始化方法,用于初始化网络权重,它有助于解决深度网络中的梯度消失问题
def xavier_init(size):
in_dim = size[0]
xavier_stddev = 1. / np.sqrt(in_dim / 2.)
return Variable(torch.randn(*size) * xavier_stddev, requires_grad=True)


""" ==================== GENERATOR ======================== """
# 初始化了生成器的权重和偏置。Wzh和bzh是第一层的权重和偏置,Whx和bhx是第二层的。
Wzh = xavier_init(size=[Z_dim, h_dim])
bzh = Variable(torch.zeros(h_dim), requires_grad=True)

Whx = xavier_init(size=[h_dim, X_dim])
bhx = Variable(torch.zeros(X_dim), requires_grad=True)

# 生成器函数。它接受噪声z作为输入,通过两层网络生成假图像。第一层使用ReLU激活函数,第二层使用Sigmoid函数。
def G(z):
h = nn.relu(z @ Wzh + bzh.repeat(z.size(0), 1))
X = nn.sigmoid(h @ Whx + bhx.repeat(h.size(0), 1))
return X


""" ==================== DISCRIMINATOR ======================== """
# 初始化判别器的权重和偏置。Wzh和bzh是第一层的权重和偏置,Whx和bhx是第二层的。
Wxh = xavier_init(size=[X_dim, h_dim])
bxh = Variable(torch.zeros(h_dim), requires_grad=True)

Why = xavier_init(size=[h_dim, 1])
bhy = Variable(torch.zeros(1), requires_grad=True)

# 判别器函数。它接受图像X作为输入,输出一个0到1之间的数,表示图像是真实的概率。
def D(X):
h = nn.relu(X @ Wxh + bxh.repeat(X.size(0), 1))
y = nn.sigmoid(h @ Why + bhy.repeat(h.size(0), 1))
return y

# 将生成器和判别器的参数分别组织起来,方便后续优化。
G_params = [Wzh, bzh, Whx, bhx]
D_params = [Wxh, bxh, Why, bhy]
params = G_params + D_params


""" ===================== TRAINING ======================== """

# 重置所有参数的梯度。在每次更新参数之后调用,以准备下一次反向传播。
def reset_grad():
for p in params:
if p.grad is not None:
data = p.grad.data
p.grad = Variable(data.new().resize_as_(data).zero_())

# 为生成器和判别器分别创建了Adam优化器。
G_solver = optim.Adam(G_params, lr=1e-3)
D_solver = optim.Adam(D_params, lr=1e-3)

# 创建表示"真"和"假"的标签,用于计算损失。
ones_label = Variable(torch.ones(mb_size, 1))
zeros_label = Variable(torch.zeros(mb_size, 1))

# 在训练循环外创建两个列表来存储loss值
D_losses = []
G_losses = []

# 曲线平滑方法
def moving_average(data, window_size):
cumsum = np.cumsum(np.insert(data, 0, 0))
return (cumsum[window_size:] - cumsum[:-window_size]) / window_size

# 保存loss曲线
if not os.path.exists('out_gan_pytorch_1/'):
os.makedirs('out_gan_pytorch_1/')
csv_file = open('out_gan_pytorch_1/loss_data.csv', 'w', newline='')
csv_writer = csv.writer(csv_file)
csv_writer.writerow(['Iteration', 'D_loss', 'G_loss'])

# 主训练循环,总共进行100000次迭代。
for it in range(100000):
# Sample data
z = Variable(torch.randn(mb_size, Z_dim)) #生成一个随机噪声批次z作为生成器的输入。
# X, _ = mnist.train.next_batch(mb_size) #从MNIST数据集中获取一批真实图像X。
X, _ = next_batch(mb_size, x_train, y_train)
X = Variable(torch.from_numpy(X)) #将X转换为PyTorch的Variable。

""" ===================== 判别器训练 ======================== """
# Dicriminator forward-loss-backward-update
G_sample = G(z) #使用生成器G生成一批假图像G_sample
D_real = D(X) #判别器D对真实图像X进行评估,得到D_real
D_fake = D(G_sample) #判别器D对生成的假图像G_sample进行评估,得到D_fake

# 计算判别器的损失
D_loss_real = nn.binary_cross_entropy(D_real, ones_label) #D_loss_real是真实图像的损失, 目标是使D_real接近1。
D_loss_fake = nn.binary_cross_entropy(D_fake, zeros_label) #D_loss_fake是假图像的损失, 目标是使D_fake接近0。
D_loss = D_loss_real + D_loss_fake #总损失D_loss是这两部分之和。

# 对判别器进行反向传播和参数更新。
D_loss.backward()
D_solver.step()

# Housekeeping - reset gradient
# 重置所有参数的梯度,为下一步做准备。
reset_grad()

""" ===================== 生成器训练 ======================== """
# Generator forward-loss-backward-update
# 为训练生成器,我们再次生成假图像并用判别器评估
z = Variable(torch.randn(mb_size, Z_dim))
G_sample = G(z)
D_fake = D(G_sample)

# 计算生成器的损失。注意这里的目标是让D_fake接近1, 即欺骗判别器
G_loss = nn.binary_cross_entropy(D_fake, ones_label)

# 对生成器进行反向传播和参数更新。
G_loss.backward()
G_solver.step()

# Housekeeping - reset gradient
# 再次重置梯度。
reset_grad()

# 每次迭代都记录loss值
D_losses.append(D_loss.item())
G_losses.append(G_loss.item())

# Print and plot every now and then
# 每1000次迭代打印一次当前的损失值。
if it % 1000 == 0:
print('Iter-{}; D_loss: {}; G_loss: {}'.format(it, D_loss.data.numpy(), G_loss.data.numpy()))

samples = G(z).data.numpy()[:16] # 生成16个样本。

fig = plt.figure(figsize=(4, 4)) # 创建一个4x4的图像网格。
gs = gridspec.GridSpec(4, 4) # 将每个生成的样本绘制到网格中。
gs.update(wspace=0.05, hspace=0.05)

for i, sample in enumerate(samples):
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')

if not os.path.exists('out_gan_pytorch_1/'):
os.makedirs('out_gan_pytorch_1/')

plt.savefig('out_gan_pytorch_1/{}.png'.format(str(c).zfill(3)), bbox_inches='tight')
c += 1
plt.close(fig)

# 保存当前的loss数据
with open('out_gan_pytorch_1/loss_data_ongoing.csv', 'a', newline='') as file:
writer = csv.writer(file)
if it == 0: # 如果是第一次写入,添加表头
writer.writerow(["Iteration", "G_loss", "D_loss"])
writer.writerow([it, G_loss.item(), D_loss.item()])

print(f"Loss data at iteration {it} has been appended to 'out_gan_pytorch_1/loss_data_ongoing.csv'")

# 计算移动平均-曲线平滑
window_size = 1000
G_losses_avg = moving_average(G_losses, window_size)
D_losses_avg = moving_average(D_losses, window_size)

# 绘制loss曲线
plt.figure(figsize=(10, 5))
plt.title("Generator and Discriminator Loss During Training")
plt.plot(G_losses, label="G")
plt.plot(D_losses, label="D")
plt.xlabel("iterations")
plt.ylabel("Loss")
plt.legend()
plt.savefig('out_gan_pytorch_1/loss_curve.png')
plt.close()

# 保存loss数据到CSV文件
with open('out_gan_pytorch_1/loss_data.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(["Iteration", "G_loss", "D_loss"]) # 写入表头
for i, (g_loss, d_loss) in enumerate(zip(G_losses, D_losses)):
writer.writerow([i, g_loss, d_loss])

print("Loss data has been saved to 'out_gan_pytorch_1/loss_data.csv'")

# 计算移动平均
window_size = 1000
G_losses_avg = moving_average(G_losses, window_size)
D_losses_avg = moving_average(D_losses, window_size)

# 保存移动平均后的loss数据到CSV文件
with open('out_gan_pytorch_1/loss_data_avg.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(["Iteration", "G_loss_avg", "D_loss_avg"]) # 写入表头
for i, (g_loss, d_loss) in enumerate(zip(G_losses_avg, D_losses_avg)):
writer.writerow([i+window_size, g_loss, d_loss])

print("Averaged loss data has been saved to 'out_gan_pytorch_1/loss_data_avg.csv'")
# loss_data.csv:包含所有迭代的原始loss数据
# loss_data_avg.csv:包含移动平均后的loss数据
# loss_data_ongoing.csv:在训练过程中定期保存的loss数据
文章作者: HibisciDai
文章链接: http://hibiscidai.com/2024/07/10/CGAN-TensorFlow/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HibisciDai
好用、实惠、稳定的梯子,点击这里