Python 为什么随着网络训练的增加,GAN生成的图像变得越来越暗?

Python 为什么随着网络训练的增加,GAN生成的图像变得越来越暗?,python,pytorch,generative-adversarial-network,Python,Pytorch,Generative Adversarial Network,我创建了一个具有6层的简单的DCGAN,并在CelebA数据集(其中一部分包含30K图像)上对其进行了训练。 我注意到我的网络生成的图像看起来很暗,随着网络训练的增加,明亮的颜色逐渐变暗 以下是一些示例: 这就是CelebA图像的外观(用于训练的真实图像): 这些是生成的,数字显示了历元数(他们最终被训练了30个历元): 这种现象的原因是什么? 我尝试了所有有关GANs的常规技巧,例如在-1和1之间重新缩放输入图像,或者在鉴别器的第一层和生成器的最后一层不使用BatchNorm,或者 在

我创建了一个具有6层的简单的
DCGAN
,并在CelebA数据集(其中一部分包含30K图像)上对其进行了训练。
我注意到我的网络生成的图像看起来很暗,随着网络训练的增加,明亮的颜色逐渐变暗

以下是一些示例:
这就是CelebA图像的外观(用于训练的真实图像):

这些是生成的,数字显示了历元数(他们最终被训练了30个历元):

这种现象的原因是什么?
我尝试了所有有关
GAN
s的常规技巧,例如在-1和1之间重新缩放输入图像,或者在
鉴别器的第一层和
生成器的最后一层不使用
BatchNorm
,或者 在
鉴别器中使用
LeakyReLU(0.2)
,在
生成器中使用
ReLU
。然而,我不知道为什么图像如此暗淡/黑暗
这仅仅是因为训练图像较少造成的吗?
还是由网络缺陷造成的?如果是,这些缺陷的来源是什么?
以下是这些网络的实施方式:

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)

我认为问题在于建筑本身,我首先要考虑生成的图像的整体质量,而不是亮度或黑暗。当你为更多的时代而训练时,这一代人显然会变得更好。我同意图像会变暗,但即使在早期,生成的图像也明显比训练样本中的图像暗。(至少与您发布的内容相比。)

现在回到您的架构,30k样本实际上足以获得非常令人信服的结果,正如最先进的模型在face世代中所取得的结果一样。这一代人确实变得更好了,但他们离“非常好”还很远

我认为发电机的强度肯定不够,是有问题的部分。(发电机损耗急剧上升的事实也可能是一个提示。)在发电机中,您所做的只是不断地进行上采样。您应该注意,转置卷积更像是一种启发式,它不提供太多的可学习性。这与问题的性质有关。当您进行卷积运算时,您拥有所有信息,并试图学习编码,但在解码器中,您试图恢复以前丢失的信息:)。因此,在某种程度上,它更难学习,因为作为输入的信息是有限和缺乏的

事实上,确定性双线性插值方法的性能与转置卷积类似,甚至更好,这些方法纯粹基于零可学习性的缩放/扩展()

为了观察转置卷积的极限,我建议您将所有的
Transposedconv2d
替换为
UpSampling2D
(),并且我声称结果不会有太大的不同。UpSampling2D是我提到的那些确定性方法之一


为了改进生成器,可以尝试在上采样层之间插入卷积层。这些层将细化特征/图像,并纠正上采样过程中出现的一些错误。除了校正外,下一个上采样层将采用更具信息性的输入。我的意思是尝试一个类似于UNet的解码,您可以在这个链接()中找到它。当然,这将是探索的首要步骤。您可以尝试更多的GAN体系结构,并且可能会表现得更好。

您是否尝试过对其进行更长时间的培训?比如100或500个时代?这可能只是因为网络尚未聚合。此外,我肯定不会在鉴别器的第一层使用
BatchNorm
。如果你没有提到它,这就是我的建议。我已经训练了它60个时代,结果是一样的,发电机的损失会像火箭一样!所以我把它减少到30个时代。我不使用
BatchNorm
作为第一层
conv
。你找到了解决这个问题的方法吗?一个可能的理论是:生成器“发现”通过使图像更暗(对比度越低,离数据集平均值越远等),鉴别器就越难区分真假示例