Мой PyTorch GAN переходит от создания случайного шума к темноте без конвергенции. Почему это?

#machine-learning #pytorch #generative-adversarial-network

#машинное обучение #pytorch #генеративная-состязательная-сеть

Вопрос:

Мой код очень простой, просто после первой лабораторной работы из DeepLearning.специализация GAN ии. Однако мой код не имеет такого же результата, в чем причина этого. Извините, если это просто глупая ошибка, это мой первый опыт работы с GAN. Я начинаю с создания классов генератора и дискриминатора, моей функции случайного шума и создания моих моделей. Затем я запускаю цикл обучения, но через 3 эпохи все выходные данные GAN становятся черными.

 import torch
from torch import nn
from tqdm.auto import tqdm
from torchvision import transforms
from torchvision.datasets import MNIST
from torchvision.utils import make_grid
from torch.utils.data import DataLoader, Dataset
import matplotlib.pyplot as plt

torch.manual_seed(0)

def show_tensor_images(image_tensor,num_images=25,size=(1,28,28)):
    image_unflat=image_tensor.detach().cpu().view(-1,*size)
    image_grid=make_grid(image_unflat[:num_images],nrow=5)
    plt.imshow(image_grid.permute(1,2,0).squeeze())
    plt.show()

class Generator(nn.Module):
    def __init__(self,z_dim):
        super(Generator,self).__init__()
        self.linear1=nn.Linear(z_dim,128)
        self.bn1=nn.BatchNorm1d(128)
        self.linear2=nn.Linear(128,256)
        self.bn2=nn.BatchNorm1d(256)
        self.linear3=nn.Linear(256,512)
        self.bn3=nn.BatchNorm1d(512)
        self.linear4=nn.Linear(512,1024)
        self.bn4=nn.BatchNorm1d(1024)
        self.linear5=nn.Linear(1024,784)
        self.relu=nn.ReLU(True)
        self.sigmoid=nn.Sigmoid()
    def forward(self,x):
        x=self.linear1(x)
        x=self.bn1(x)
        x=self.relu(x)
        x=self.linear2(x)
        x=self.bn2(x)
        x=self.relu(x)
        x=self.linear3(x)
        x=self.bn3(x)
        x=self.relu(x)
        x=self.linear4(x)
        x=self.bn4(x)
        x=self.relu(x)
        x=self.linear5(x)
        x=self.sigmoid(x)
        return(x)

def get_noise(n_samples,z_dim,device='cpu'):
    return torch.randn(n_samples,z_dim,device=device)

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator,self).__init__()
        self.linear1=nn.Linear(784,512)
        self.linear2=nn.Linear(512,256)
        self.linear3=nn.Linear(256,128)
        self.linear4=nn.Linear(128,1)
        self.relu=nn.LeakyReLU(0.2,True)
    def forward(self,x):
        x=self.linear1(x)
        x=self.relu(x)
        x=self.linear2(x)
        x=self.relu(x)
        x=self.linear3(x)
        x=self.relu(x)
        x=self.linear4(x)
        return(x)

criterion=nn.BCEWithLogitsLoss()
epochs=200
z_dim=64
display_step=500
batch_size=128
lr=0.00001
device='cuda'

dataloader=DataLoader(MNIST('.',download=True,transform=transforms.ToTensor()),batch_size=batch_size,shuffle=True)

gen=Generator(z_dim).to(device)
gen_opt=torch.optim.Adam(gen.parameters(),lr=lr)
disc=Discriminator().to(device)
disc_opt=torch.optim.Adam(disc.parameters(),lr=lr)

def get_disc_loss(gen,disc,criterion,real,num_images,z_dim,device):
    noise=get_noise(num_images,z_dim,device=device)
    gen_out=gen(noise)
    disc_fake_out=disc(gen_out.detach())
    fake_loss=criterion(disc_fake_out,torch.zeros_like(disc_fake_out))
    disc_real_out=disc(real)
    real_loss=criterion(disc_real_out,torch.zeros_like(disc_real_out))
    disc_loss=(fake_loss real_loss)/2
    return(disc_loss)

def get_gen_loss(gen,disc,criterion,num_images,z_dim,device):
    noise=get_noise(num_images,z_dim,device=device)
    gen_out=gen(noise)
    disc_out=disc(gen_out)
    loss=criterion(disc_out,torch.ones_like(disc_out))
    return loss

cur_step=0
mean_generator_loss=0
mean_discriminator_loss=0
gen_loss=False
error=False
for epoch in range(epochs):
    for x,_ in tqdm(dataloader):
        cur_batch_size=len(x)
        x=x.view(cur_batch_size,-1).to(device)

        disc_opt.zero_grad()
        disc_loss=get_disc_loss(gen,disc,criterion,x,cur_batch_size,z_dim,device)
        disc_loss.backward(retain_graph=True)
        disc_opt.step()

        gen_opt.zero_grad()
        gen_loss=get_gen_loss(gen,disc,criterion,cur_batch_size,z_dim,device)
        gen_loss.backward()
        gen_opt.step()

        mean_discriminator_loss =disc_loss.item()/display_step
        mean_generator_loss =gen_loss.item()/display_step

        if cur_step%display_step==0 and cur_batch_size>0:
            print(f"Step {cur_step}: Generator loss: {mean_generator_loss}, discriminator loss: {mean_discriminator_loss}")
            fake_noise = get_noise(cur_batch_size, z_dim, device=device)
            fake = gen(fake_noise)
            show_tensor_images(fake)
            show_tensor_images(x)
            mean_generator_loss = 0
            mean_discriminator_loss = 0
        cur_step  = 1
 

Ответ №1:

Ваша потеря дискриминатора неверна. Метки для реальных изображений должны быть 1 вместо 0 .

Обновленный код:

 def get_disc_loss(gen,disc,criterion,real,num_images,z_dim,device):
    noise=get_noise(num_images,z_dim,device=device)
    gen_out=gen(noise)
    disc_fake_out=disc(gen_out.detach())
    fake_loss=criterion(disc_fake_out,torch.zeros_like(disc_fake_out))
    disc_real_out=disc(real)
    real_loss=criterion(disc_real_out,torch.ones_like(disc_real_out))
    disc_loss=(fake_loss real_loss)/2
    return(disc_loss)
 

Для меня выходное изображение выглядит довольно хорошо:
введите описание изображения здесь

Комментарии:

1. Привет, можешь объяснить мне, почему генератор обучается после дискриминатора, а также обучается ли дискриминатор изображению, генерируемому генератором в каждую эпоху?