问题描述
所以,我正在 pytorch 中对 celeba 数据集(人)训练 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,stride=(2,2),padding=(1,bias=False)
(4): Batchnorm2d(256,track_running_stats=True)
(5): ReLU(inplace=True)
(6): ConvTranspose2d(256,128,bias=False)
(7): Batchnorm2d(128,track_running_stats=True)
(8): ReLU(inplace=True)
(9): ConvTranspose2d(128,64,bias=False)
(10): Batchnorm2d(64,track_running_stats=True)
(11): ReLU(inplace=True)
(12): ConvTranspose2d(64,3,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,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)
只是一个简单的卷积层 + 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)
大部分步骤都是一样的,只是创建了一个新的transformer网络的实例。最后,对训练循环稍作修改,其中生成器不输入随机向量,而是输入新变压器网络的输出。
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[:,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 个 epochs 训练后得到的:
那么这个结果告诉我们什么?由于生成器的输入不是从正态分布中随机抽取的,因此生成器无法学习人脸的分布以创建不同范围的输出人脸。由于输入是一个条件特征向量,输出图像的范围是有限的。所以总而言之,即使生成器学会了删除补丁,它也需要随机输入:)