准备工作
作业六是使用GAN生成动漫人物脸,需要准备助教代码,运行代码过程中保持联网可以自动下载数据集,已经有数据集的情况可关闭助教代码中的下载数据部分。关注微信公众号,可获得代码、数据集和答案(文末有方法)。
提交地址
这次作业在NTU上面提交,没在册的学生可能提交不了,关于作业有想讨论的可进QQ群:156013866。
Simple Baseline (score>14.58)
方法:直接运行助教代码。
Medium Baseline (score>18.04)
方法:修改n_epoch+n_critic。运行更多的epoch,在config中设置epoch=50。每更新一次generator,更新两次discriminator,即设置n_critic=2。
运行后发现生成的图片先逐渐变好平稳,直到epoch 27后开始突然变坏,看不出人形。原因是loss_G在第27epoch后突然增大,loss_D接近于0,这说明后续的训练discriminator相对generator表现的太好,这与GAN的训练背道而驰,GAN训练最好的结果是loss_G小,loss_D大,也就是discriminator无法分辨generator的结果。第23epoch表现相对较好,如下图所示,不过还是有很多瑕疵,特别是眼睛大小不一。
config = {
......
"n_epoch": 50,
"n_critic": 2,
......
}
Strong Baseline (score>25.20)
这里使用了两种方法,WGAN weight clipping和WGAN-GP。
方法一:WGAN weight clipping,修改discriminator+RMSprop optimizer+loss函数+weight clipping+train longer。因为WGAN的思路是将discriminator训练为距离函数,所以discriminator不需要最后的非线性sigmoid层,需要将其从原始代码中删除。第二是WGAN weight clipping的optimizer,一般使用RMSprop效果较好。第三是将generator和discriminator中的loss函数改为距离。第四是做weight clipping操作,将助教代码中相应位置的注释去掉就好。第五需要在config中设置clip_value项,为了跟meidium baseline的DCGAN形成对比,设置n_epoch=50,n_cirtic=2。
# 去掉discriminator最后一个sigmoid层,这里使用注释的方式。
# nn.Sigmoid()
# 修改optimizer为RMSprop
self.opt_D = torch.optim.RMSprop(self.D.parameters(), lr=self.config["lr"])
self.opt_G = torch.optim.RMSprop(self.G.parameters(), lr=self.config["lr"])
# 修改loss函数,loss_D, loss_G
loss_D = -torch.mean(r_logit) + torch.mean(f_logit)
......
loss_G = -torch.mean(self.D(f_imgs))
# 参数更新后进行weight clipping操作,此操作需要config中添加clip_value项
for p in self.D.parameters():
p.data.clamp_(-self.config["clip_value"], self.config["clip_value"])
# config 中添加clip_value项,同时n_epoch=50, n_critic=2
config = {
......
"n_epoch": 50,
"n_critic": 2,
"clip_value": 1,
......
}
运行后发现结果不是很稳定,某一个epoch的生成图看起来不错,下一个epoch就不行了,然后再下一个又好了,猜测是因为weight clipping的原因,参数更新不稳定。这里放一个相对较好的结果,第39个epoch的图片,相对DCGAN略好。
方法二:WGAN-GP,修改discriminator+loss函数+gradient penalty+train longer。第一是WGAN-GP的discriminator仍然不需要最后的非线性sigmoid层,并且需要用nn.InstanceNorm2d层代替nn.BatchNorm2d层。第二需要将generator和discriminator中的loss函数改为距离,并且在训练generator的时候,loss函数加入graident penalty项。第三为了跟meidium baseline的DCGAN形成对比,设置n_epoch=50,n_cirtic=2。注意WGAN-GP的optimizer是Adam,而不是WGAN weight clipping使用的RMSprop。
# 去掉discriminator最后一个sigmoid层。
# nn.Sigmoid()
# 使用nn.InstanceNorm2d层
def conv_bn_lrelu(self, in_dim, out_dim):
return nn.Sequential(
nn.Conv2d(in_dim, out_dim, 4, 2, 1),
#nn.BatchNorm2d(out_dim),
nn.InstanceNorm2d(out_dim),
nn.LeakyReLU(0.2),
)
# 写gp函数并添加进loss_D
def gp(self, r_imgs, f_imgs):
Tensor = torch.cuda.FloatTensor
alpha = Tensor(np.random.random((r_imgs.size(0), 1, 1, 1)))
interpolates = (alpha*r_imgs + (1 - alpha)*f_imgs).requires_grad_(True)
d_interpolates = self.D(interpolates)
fake = Variable(Tensor(r_imgs.shape[0]).fill_(1.0), requires_grad=False)
gradients = autograd.grad(
outputs=d_interpolates,
inputs=interpolates,
grad_outputs=fake,
create_graph=True,
retain_graph=True,
only_inputs=True,
)[0]
gradients = gradients.view(gradients.size(0), -1)
gradient_penalty = ((gradients.norm(1, dim=1) - 1)**2).mean()
return gradient_penalty
# 修改loss函数,loss_D, loss_G
gradient_penalty = self.gp(r_imgs, f_imgs)
loss_D = -torch.mean(r_logit) + torch.mean(f_logit) + gradient_penalty
......
loss_G = -torch.mean(self.D(f_imgs))
# n_epoch=50, n_critic=2
config = {
......
"n_epoch": 50,
"n_critic": 2,
......
}
运行后发现结果逐步变好趋于稳定,比DCGAN和WGAN-WC的各个epoch“过山车”一样出结果好太多了。这里放一个相对较好的结果,第43个epoch生成的图片。
Boss Baseline (acc>29.13)
方法:使用stylegan2库+kaggle平台。styelegan2的使用方法详情见 https://github.com/lucidrains/stylegan2-pytorch。具体的训练设置为: image_size=64,batch_size16, num-trian-steps=20000。最开始我先测试了下每个step需要的时间,接近2秒,而默认的train steps是150000,按默认跑的话时间太长了,经过粗略计算,我选择了train steps=20000,总训练时间接近10个小时。作业中的建议训练时间是5个小时,这对应的是train steps=10000,我们总的图片数目是7万多个,这意味着10000个step只能将整个图片集合跑2遍多一点,为了平衡时间和结果,我决定让train steps=20000,不过跑完程序后,发现其实10000个step的结果已经相当好,与20000个step相差不多。
stylegan2虽然对算力要求高,但是出来的图片确实很好,文章最开头的插值图就是训练完后生成的(方法见代码),模型最后生成的图片如下图所示,效果比前面几个模型好了很多。毕竟是显卡厂商英伟达出品,研究人员的GPU算力管饱。最近stylegan3也开源了,对算力的要求更高,该模型的研究人员在GPU集群上跑一遍模型需要的电量是核电站运行15分钟所产生的能量,大概225M瓦。
# 安装stylegan2库
!pip install stylegan2_pytorch
#训练模型
!stylegan2_pytorch --data faces --image-size 64 --batch-size 16 --num-train-steps=20000
#训练结束后,利用模型生成图片,可选择使用哪个epoch的模型来生成
!stylegan2_pytorch --generate --num_generate=4 --num_image_tiles=8
#生成转换动画
!stylegan2_pytorch --generate-interpolation --interpolation-num-steps 100
作业六答案获得方式:
关注微信公众号 “机器学习手艺人”
后台回复关键词:202206