如何将图像提供给 GAN Pytorch 中的生成器

How to feed a image to Generator in GAN Pytorch

所以,我正在 celeba 数据集(人)上用 pytorch 训练 DCGAN 模型。这是生成器的架构:

Generator(
  (main): Sequential(
    (0): ConvTranspose2d(100, 512, kernel_size=(4, 4), stride=(1, 1), bias=False)
    (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
    (3): ConvTranspose2d(512, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (5): ReLU(inplace=True)
    (6): ConvTranspose2d(256, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (7): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (8): ReLU(inplace=True)
    (9): ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (10): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (11): ReLU(inplace=True)
    (12): ConvTranspose2d(64, 3, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (13): Tanh()
  )
)

所以在训练之后,如果我像这样输入被遮挡的图像,我想检查生成器输出的内容: (尺寸:64X64)

但是你可能已经猜到图像有 3 个通道并且我的生成器在开始时接受 100 个通道的潜在向量,那么将此图像提供给生成器并检查输出的正确方法是什么。 (我希望生成器尝试仅生成图像的遮挡部分)。如果您想要参考代码,请尝试 this pytorch 的演示文件。我已经根据自己的需要修改了这个文件,所以作为参考,这个就可以了。

你不能那样做。如您所说,您的网络需要 100 维输入,通常从标准正态分布中采样:

所以生成器的工作是采用这个随机向量并生成与真实图像无法区分的 3x64x64 图像。输入是从标准正态分布中抽取的随机 100 维向量。我看不出有什么方法可以在不修改架构和重新训练新模型的情况下将您的图像输入当前网络。如果你想尝试一个新模型,你可以将输入更改为遮挡图像,应用一些转换。 / 线性层将维度减少到 100,然后保持网络的其余部分不变。通过这种方式,网络将尝试学习生成图像,而不是从潜在向量生成图像,而是从从遮挡图像中提取的特征向量生成图像。它可能有效也可能无效。

编辑 我决定试一试,看看网络是否可以使用这种类型的条件输入向量而不是潜在向量来学习。我使用了您链接的教程示例并添加了一些更改。首先是一个用于接收输入并将其减少到 100 维的新网络:

class ImageTransformer(nn.Module):
    def __init__(self):
        super(ImageTransformer, self).__init__()
        self.main = nn.Sequential(
            nn.Conv2d(3, 1, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2, inplace=True)
        )            

        self.linear = nn.Linear(32*32, 100)

    def forward(self, input):
        out = self.main(input).view(input.shape[0], -1)
        return self.linear(out).view(-1, 100, 1, 1)

只需一个简单的卷积层 + relu + 线性层即可在输出端映射到 100 维。请注意,您可以在这里尝试更好的网络作为更好的特征提取器,我只是想做一个简单的测试。

fixed_input = next(iter(dataloader))[0][0:64, :, : ,:]
fixed_input[:, :, 20:44, 20:44] = torch.tensor(np.zeros((24,24), dtype = np.float32))
fixed_input = fixed_input.to(device)

这就是我修改张量以在输入上添加黑色补丁的方式。刚刚对批次进行采样以创建固定输入来跟踪过程,就像在教程中使用随机向量完成的那样。

# Create the generator
netG = Generator().to(device)
netD = Discriminator().to(device)
netT = ImageTransformer().to(device)

# Apply the weights_init function to randomly initialize all weights
#  to mean=0, stdev=0.2.
netG.apply(weights_init)
netD.apply(weights_init)
netT.apply(weights_init)

# Print the model
print(netG)
print(netD)
print(netT)

大部分步骤都是一样的,只是创建了一个新的变压器网络的实例。最后,训练循环略有修改,其中生成器未被馈送随机向量,但它被赋予新的变压器网络的输出。

img_list = []
G_losses = []
D_losses = []
iters = 0

for epoch in range(num_epochs):
    for i, data in enumerate(dataloader, 0):

        ############################
        # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z)))
        ###########################
        ## Train with all-real batch
        netD.zero_grad()
        transformed = data[0].detach().clone()
        transformed[:, :, 20:44, 20:44] = torch.tensor(np.zeros((24,24), dtype = np.float32))
        transformed = transformed.to(device)
        real_cpu = data[0].to(device)
        b_size = real_cpu.size(0)
        label = torch.full((b_size,), real_label, dtype=torch.float, device=device)
        output = netD(real_cpu).view(-1)
        errD_real = criterion(output, label)
        errD_real.backward()
        D_x = output.mean().item()

        ## Train with all-fake batch
        fake = netT(transformed)
        fake = netG(fake)
        label.fill_(fake_label)
        output = netD(fake.detach()).view(-1)
        errD_fake = criterion(output, label)
        errD_fake.backward()
        D_G_z1 = output.mean().item()
        errD = errD_real + errD_fake
        optimizerD.step()

        ############################
        # (2) Update G network: maximize log(D(G(z)))
        ###########################
        netG.zero_grad()
        label.fill_(real_label)
        output = netD(fake).view(-1)
        errG = criterion(output, label)
        errG.backward()
        D_G_z2 = output.mean().item()
        optimizerG.step()

        # Output training stats
        if i % 50 == 0:
            print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f\tD(x): %.4f\tD(G(z)): %.4f / %.4f'
                  % (epoch, num_epochs, i, len(dataloader),
                     errD.item(), errG.item(), D_x, D_G_z1, D_G_z2))

        # Save Losses for plotting later
        G_losses.append(errG.item())
        D_losses.append(errD.item())

        # Check how the generator is doing by saving G's output on fixed_noise
        if (iters % 500 == 0) or ((epoch == num_epochs-1) and (i == len(dataloader)-1)):
            with torch.no_grad():
                fake = netT(fixed_input)
                fake = netG(fake).detach().cpu()
            img_list.append(vutils.make_grid(fake, padding=2, normalize=True))

        iters += 1

训练在减少损失等方面还不错。最后这是我训练 5 个 epoch 后得到的结果:

那么这个结果告诉我们什么?由于生成器的输入不是从正态分布中随机获取的,因此生成器无法学习人脸的分布来创建不同范围的输出人脸。并且由于输入是条件特征向量,输出图像的范围是有限的。所以总而言之,生成器需要随机输入,即使它学会了删除补丁:)