【发布时间】:2022-01-16 01:13:37
【问题描述】:
我要先说我在神经网络方面没有太多经验——一般而言,在 Pytorch 方面经验较少。我正在尝试在 WGAN-GP 实现(不是我的)中实现混合精度,以便我可以节省 GPU 内存并加快训练速度。
我从here 获得了代码,但我制作了自己的生成器/判别器模型,我将放在底部。
训练循环如下所示:
scaler1 = torch.cuda.amp.GradScaler()
scaler2 = torch.cuda.amp.GradScaler()
for epoch in range(EPOCHS):
# Target labels not needed! <3 unsupervised
for batch_idx, (real, _) in enumerate(loader):
real = real.to(device)
cur_batch_size = real.shape[0]
with torch.cuda.amp.autocast():
noise = torch.randn(cur_batch_size, LATENT_SIZE, 1, 1).to(device)
fake = gen(noise)
critic_real = critic(real).reshape(-1)
critic_fake = critic(fake).reshape(-1)
gp = gradient_penalty(critic, real, fake, device=device, scaler = scaler1)
loss_critic = (
-(torch.mean(critic_real) - torch.mean(critic_fake)) + LAMBDA_GP * gp
)
critic.zero_grad()
#loss_critic.backward(retain_graph=True)
#opt_critic.step()
scaler1.scale(loss_critic).backward(retain_graph = True)
scaler1.unscale_(opt_critic)
scaler1.step(opt_critic)
# Train Generator: max E[critic(gen_fake)] <-> min -E[critic(gen_fake)]
with torch.cuda.amp.autocast():
gen_fake = critic(fake).reshape(-1)
loss_gen = -torch.mean(gen_fake)
gen.zero_grad()
#loss_gen.backward()
#opt_gen.step()
scaler2.scale(loss_gen).backward(retain_graph = True)
scaler2.unscale_(opt_gen)
scaler2.step(opt_gen)
scaler1.update()
scaler2.update()
以及梯度惩罚函数:
def gradient_penalty(critic, real, fake, device="cpu", scaler = None):
BATCH_SIZE, C, H, W = real.shape
alpha = torch.rand((BATCH_SIZE, 1, 1, 1)).repeat(1, C, H, W).to(device)
interpolated_images = real * alpha + fake * (1 - alpha)
# Calculate critic scores
mixed_scores = critic(interpolated_images)
# Take the gradient of the scores with respect to the images
gradient = torch.autograd.grad(
inputs=interpolated_images,
outputs=mixed_scores if scaler is None else scaler.scale(mixed_scores),
grad_outputs=torch.ones_like(mixed_scores),
create_graph=True,
retain_graph=True,
)[0]
gradient = gradient / scaler.get_scale() if scaler is not None else gradient
gradient = gradient.view(gradient.shape[0], -1)
gradient_norm = gradient.norm(2, dim=1)
gradient_penalty = torch.mean((gradient_norm - 1) ** 2)
return gradient_penalty
我在没有混合精度的情况下对此进行了测试,它似乎做得很好,但是在我尝试实现混合精度之后,判别器损失在几批后变成了 NaN。生成器损失似乎是正常的(但它一开始是负数,我不确定这是否可以,但后来在不使用混合精度时变为正数)。
以下是我的生成器和鉴别器模型:
class Generator(nn.Module):
def __init__(self, targetSize, channels, features, latentSize):
super(Generator, self).__init__()
mult = int(np.log(targetSize)/np.log(2) - 3)
startFactor = 2**mult
self.network = nn.Sequential(
nn.ConvTranspose2d(latentSize, features * startFactor, 4, 1, 0, bias = False),
nn.BatchNorm2d(features * startFactor),
nn.LeakyReLU(0.2),
*sum([self.__block(int(features * startFactor / (2**i)), int(features * startFactor / (2**(i+1)))) for i in range(mult)], []),
nn.ConvTranspose2d(features, channels, 4, 2, 1, bias = False),
nn.Tanh(),
)
def __block(self, in_features, out_features):
layers = [nn.ConvTranspose2d(in_features, out_features, 4, 2, 1, bias = False)]
layers.append(nn.BatchNorm2d(out_features))
layers.append(nn.LeakyReLU(0.2))
return layers
def forward(self, inp):
return self.network(inp)
class Discriminator(nn.Module):
def __init__(self, targetSize, channels, features):
super(Discriminator, self).__init__()
mult = int(np.log(targetSize)/np.log(2) - 3)
startFactor = 2**mult
self.network = nn.Sequential(
nn.Conv2d(channels, features, 4, 2, 1, bias = False),
nn.LeakyReLU(0.2),
*sum([self.__block(int(features * (2**i)), int(features * (2**(i+1)))) for i in range(mult)],[]),
nn.Conv2d(features * startFactor, 1, 4, 2, 0, bias = False),
)
def __block(self, in_features, out_features):
layers = [nn.Conv2d(in_features, out_features, 4, 2, 1, bias = False)]
layers.append(nn.InstanceNorm2d(out_features, affine=True))
layers.append(nn.LeakyReLU(0.2))
return layers
def forward(self, inp):
return self.network(inp)
注意:FP16 图表在第 ~140 步结束,因为从该点开始它变为 NaN。
【问题讨论】:
-
您的判别器损失是否发散?你能分享一下损失输出吗
-
我已经用相关的损失图以及原始数据的链接更新了问题(批量大小为 32,学习率为 1e-4)。
标签: python pytorch generative-adversarial-network