我创建了一个简单的DCGAN
,具有6层,并在CelebA数据集上对其进行了训练(其中一部分包含3万张图像)。
我注意到我的网络生成的图像看起来是暗淡的,并且随着网络的训练越来越多,明亮的颜色逐渐变成暗淡的颜色!
这是一些示例:
这就是CelebA图片的样子(用于训练的真实图片):
这些是生成的数字,该数字显示纪元编号(它们最终被训练了30个纪元):
此现象的原因是什么?
我尝试完成所有与GAN
有关的技巧,例如在-1和1之间重新缩放输入图像,或者在BatchNorm
的第一层中不使用Discriminator
,并且Generator
的最后一层或
在LeakyReLU(0.2)
中使用Discriminator
,并在ReLU
中使用Generator
。但我不知道为什么图像太暗了!
这是因为训练图像减少了吗?
还是由网络缺陷引起的?如果是的话,这种缺陷的根源是什么?
这些网络的实现方法如下:
def conv_batch(in_dim, out_dim, kernel_size, stride, padding, batch_norm=True):
layers = nn.ModuleList()
conv = nn.Conv2d(in_dim, out_dim, kernel_size, stride, padding, bias=False)
layers.append(conv)
if batch_norm:
layers.append(nn.BatchNorm2d(out_dim))
return nn.Sequential(*layers)
class Discriminator(nn.Module):
def __init__(self, conv_dim=32, act = nn.ReLU()):
super().__init__()
self.conv_dim = conv_dim
self.act = act
self.conv1 = conv_batch(3, conv_dim, 4, 2, 1, False)
self.conv2 = conv_batch(conv_dim, conv_dim*2, 4, 2, 1)
self.conv3 = conv_batch(conv_dim*2, conv_dim*4, 4, 2, 1)
self.conv4 = conv_batch(conv_dim*4, conv_dim*8, 4, 1, 1)
self.conv5 = conv_batch(conv_dim*8, conv_dim*10, 4, 2, 1)
self.conv6 = conv_batch(conv_dim*10, conv_dim*10, 3, 1, 1)
self.drp = nn.Dropout(0.5)
self.fc = nn.Linear(conv_dim*10*3*3, 1)
def forward(self, input):
batch = input.size(0)
output = self.act(self.conv1(input))
output = self.act(self.conv2(output))
output = self.act(self.conv3(output))
output = self.act(self.conv4(output))
output = self.act(self.conv5(output))
output = self.act(self.conv6(output))
output = output.view(batch, self.fc.in_features)
output = self.fc(output)
output = self.drp(output)
return output
def deconv_convtranspose(in_dim, out_dim, kernel_size, stride, padding, batchnorm=True):
layers = []
deconv = nn.ConvTranspose2d(in_dim, out_dim, kernel_size = kernel_size, stride=stride, padding=padding)
layers.append(deconv)
if batchnorm:
layers.append(nn.BatchNorm2d(out_dim))
return nn.Sequential(*layers)
class Generator(nn.Module):
def __init__(self, z_size=100, conv_dim=32):
super().__init__()
self.conv_dim = conv_dim
# make the 1d input into a 3d output of shape (conv_dim*4, 4, 4 )
self.fc = nn.Linear(z_size, conv_dim*4*4*4)#4x4
# conv and deconv layer work on 3d volumes, so we now only need to pass the number of fmaps and the
# input volume size (its h,w which is 4x4!)
self.drp = nn.Dropout(0.5)
self.deconv1 = deconv_convtranspose(conv_dim*4, conv_dim*3, kernel_size =4, stride=2, padding=1)
self.deconv2 = deconv_convtranspose(conv_dim*3, conv_dim*2, kernel_size =4, stride=2, padding=1)
self.deconv3 = deconv_convtranspose(conv_dim*2, conv_dim, kernel_size =4, stride=2, padding=1)
self.deconv4 = deconv_convtranspose(conv_dim, conv_dim, kernel_size =3, stride=2, padding=1)
self.deconv5 = deconv_convtranspose(conv_dim, 3, kernel_size =4, stride=1, padding=1, batchnorm=False)
def forward(self, input):
output = self.fc(input)
output = self.drp(output)
output = output.view(-1, self.conv_dim*4, 4, 4)
output = F.relu(self.deconv1(output))
output = F.relu(self.deconv2(output))
output = F.relu(self.deconv3(output))
output = F.relu(self.deconv4(output))
# we create the image using tanh!
output = F.tanh(self.deconv5(output))
return output
# testing nets
dd = Discriminator()
zd = np.random.rand(2,3,64,64)
zd = torch.from_numpy(zd).float()
# print(dd)
print(dd(zd).shape)
gg = Generator()
z = np.random.uniform(-1,1,size=(2,100))
z = torch.from_numpy(z).float()
print(gg(z).shape)
答案 0 :(得分:2)
我认为问题在于架构本身,我会首先考虑生成图像的整体质量,而不是它们的亮度或暗度。当你训练更多的时期时,几代人显然会变得更好。我同意图像变暗,但即使在早期阶段,生成的图像也明显比训练样本中的图像暗。 (至少与您发布的相比。)
现在回到您的架构,30k 样本实际上足以获得非常令人信服的结果,正如人脸生成中最先进的模型所实现的那样。几代人确实变好了,但离“非常好”还差得很远。
我认为发电机肯定不够强大,是有问题的部分。(你的发电机损失猛增的事实也可以暗示这一点。)在发电机中,你要做的就是只是上采样和上采样。你应该注意到转置卷积更像是一种启发式方法,它没有提供太多的可学习性。这与问题的性质有关。当您进行卷积时,您拥有所有信息并且您正在尝试学习编码,但在解码器中,您正在尝试恢复以前丢失的信息:)。所以,在某种程度上,因为作为输入的信息是有限的和缺乏的,所以更难学习。
事实上,确定性双线性插值方法的性能确实与转置卷积相似甚至更好,而且这些方法完全基于缩放/扩展,零学习能力。({{3 }})
为了观察转置卷积的限制,我建议您将所有 Transposedconv2d
替换为 UpSampling2D
(https://arxiv.org/pdf/1707.05847.pdf),我声称结果不会有太大不同。 UpSampling2D 是我提到的确定性方法之一。
为了改进您的生成器,您可以尝试在上采样层之间插入卷积层。这些层将细化特征/图像并纠正上采样期间发生的一些错误。除了校正之外,下一个上采样层将需要更多信息输入。我的意思是尝试像 UNet 这样的解码,您可以在此链接 (https://www.tensorflow.org/api_docs/python/tf/keras/layers/UpSampling2D) 中找到它。当然,这将是探索的主要步骤。您可以尝试更多的 GAN 架构,它们的性能可能会更好。