'''
Description: GAN--study
Autor: 365JHWZGo
Date: 2021-11-12 22:06:12
LastEditors: 365JHWZGo
LastEditTime: 2021-11-13 18:24:05
'''
# 导包
import torch
import matplotlib.pyplot as plt
from torch.autograd import Variable
import numpy as np
# 超参数设置
BATCH_SIZE = 64 # 每一批所处理的数据集
LR_G = 0.0001 # generator的学习效率
LR_D = 0.0001 # discriminator的学习效率
N_IDEAS = 5 # generator的学习灵感
ART_COMPONENTS = 15 # generator创作的点数
# 一次处理中所产生的点数,二维矩阵
PAINT_POINTS = np.vstack([np.linspace(-1, 1, ART_COMPONENTS)
for _ in range(BATCH_SIZE)]) # shape(64,15)
# 著名画家的画
def artist_works():
#从[1,2)的正态分布中随机选取64个数字,并在第二维度新加一个维度
a = np.random.uniform(1, 2, size=BATCH_SIZE)[:, np.newaxis]
#生成这些点的纵坐标
paintings = a*np.power(PAINT_POINTS, 2)+(a-1) # shape(64,15)
# 将其转变为tensor数据
paintings = torch.from_numpy(paintings).float()
return paintings
# Generator network
# generator将自己的灵感变成15个点
G = torch.nn.Sequential(
torch.nn.Linear(N_IDEAS, 128),
torch.nn.ReLU(),
torch.nn.Linear(128, ART_COMPONENTS)
)
# Discriminator network
# 对画作进行鉴别,输出一个它判断该画作是否为著名画家画作的概率值,sigmoid()用于生成一个概率值
D = torch.nn.Sequential(
torch.nn.Linear(ART_COMPONENTS, 128),
torch.nn.ReLU(),
torch.nn.Linear(128, 1),
torch.nn.Sigmoid()
)
#当在class中才需要新建实例
opt_D = torch.optim.Adam(D.parameters(), lr=LR_D)
opt_G = torch.optim.Adam(G.parameters(), lr=LR_G)
plt.ion()
# training
if __name__ == '__main__':
for step in range(10000):
#著名画家的画作
artist_paintings = artist_works()
# 用于随机生成generator的灵感
G_ideas = torch.randn(BATCH_SIZE, N_IDEAS,requires_grad=True) # shape(64,5)
#根据生成的灵感来作画
G_paintings = G(G_ideas)
# discriminator判断这些画作【G自己的灵感画作】来自著名画家的概率为多少
prob_G = D(G_paintings)
#这种概率要越低越好,因为它是永远是在模仿
G_loss = torch.mean(torch.log(1.-prob_G))
#优化
opt_G.zero_grad() #将模型中的梯度清零
G_loss.backward() #求目标函数的梯度
opt_G.step() #梯度下降,更新G的参数
# discriminator判断这些画作【著名画家画作】来自著名画家的概率为多少,希望越高越好
prob_a = D(artist_paintings)
#G_paintings的梯度不会更新
prob_G = D(G_paintings.detach()) #锁住G的参数不求导
#我们是希望它越大越好,但是torch中只有减小误差才会提升
D_loss = -torch.mean(torch.log(prob_a)+torch.log(1.-prob_G))
opt_D.zero_grad()
#retain_graph是为了再次使用计算图纸
D_loss.backward(retain_graph=True)
opt_D.step()
if step % 150 == 0:
print(step)
plt.cla()
plt.plot(PAINT_POINTS[0], G_paintings.data.numpy(
)[0], c='r', lw=3, label='Generated painting')
plt.plot(PAINT_POINTS[0], 2 * np.power(PAINT_POINTS[0],
2) + 1, c='#74BCFF', lw=3, label='upper bound')
plt.plot(PAINT_POINTS[0], 1 * np.power(PAINT_POINTS[0],
2) + 0, c='#FF9359', lw=3, label='lower bound')
plt.text(-.5, 2.3, 'D accuracy = %.2f(0.5 for D to converage)' %
prob_a.data.numpy().mean(), fontdict={'size': 15, 'color': 'blue'})
plt.text(-.5, 2, 'D score = %.2f(-1.38 for G to converage)' % -
D_loss.data.numpy(), fontdict={'size': 15, 'color': 'blue'})
plt.ylim((0, 3))
plt.legend(loc='upper right', fontsize=10)
plt.draw()
plt.pause(0.01)
plt.ioff()
plt.savefig()
plt.show()
GAN
generator:生成器G的优化目标是生成尽可能真实的数据来“骗过”判别器,使判别器给生成的数据高分。
discriminator:判别器D的优化目标是使判别器对数据集中采集的真实数据判为高分,而对合成数据判为低分。
在这个GAN例子中,需要注意的点有:
- PAINT_POINTS = np.vstack([np.linspace(-1, 1, ART_COMPONENTS) for _ in range(BATCH_SIZE)])
写到这儿,我原来也好奇为什么不直接写 PAINT_POINTS = [np.linspace(-1, 1, ART_COMPONENTS) for _ in range(BATCH_SIZE)],而是非要再加上一个**np.vsatck()**来进行过垂直合并?它俩看上去作用是一样的呀。
后来我又进行测试
print([np.linspace(-1, 1, 4)for _ in range(3)])
print(np.vstack([np.linspace(-1, 1, 4)for _ in range(3)]))
到这里,应该可以看出差别了,如果直接用[]list生成的话,它俩是不兼容的,因为用np.linspace()生成的是numpy数据,而使用np.vstack()之后,它俩就都变成numpy可以进行运算了。
- 另一个比较迷惑的点是:下面两个loss究竟代表什么
D_loss = -torch.mean(torch.log(prob_a)+torch.log(1.-prob_G))
在gan中我们希望torch.mean(torch.log(prob_a)+torch.log(1.-prob_G))是越大越好,而神经网络中差值越小才有利于神经网络的提升,所以要加一个负号。
G_loss = torch.mean(torch.log(1.-prob_G))
在gan中要G_loss越小越好,所以用1.-prob_G