Bick95:

I had a look at the source code associated with the aforementioned research paper and I saw that the authors combine both loss terms by passing the predictions of the discriminator on both real and fake data together to the loss function when computing the generator’s loss. I am assuming that this will indeed assure that the loss term resulting from the misclassification of real data will indeed be taken into account when computing the generator’s gradients.


If I understand the code correctly, get_dis_xy will return two tensors, which were not created by the generator (or if they were, then they are detached in these lines of code, since they are re-wrapped in the deprecated Variable class).
You could check for gradients in the generator after the backward() op was called in this line and should see no updates in the generator’s gradients.



 Bick95:

In this updated version of the code snippet, is it the case that actually the loss_real is taken into consideration when computing the gradients of the generator (since now it’s added to the loss_fake term that is going to be backpropagated through the generator)?


You are correct, that both losses are added. However, Autograd will use the backpropagation rules during the backward call, such that the generator still won’t get any information from the discriminator loss, if it wasn’t used in the creation of this loss.
Here is a very simple example, which mimics your workflow.
In the first part I’ll accumulate the losses and call loss.backward(), while in the second use case I call backward on each loss separately.
As you can see, the loss_real.backward() call does not create any gradients in the generator.
The final gradients are equal to the first approach, which means that the generator gets the gradient from the loss_fake.backward() call.
# Accumulate losses
torch.manual_seed(2809)

generator = nn.Linear(1, 1, bias=False)
discriminator = nn.Linear(1, 2, bias=False)

criterion = nn.CrossEntropyLoss()

x_real = torch.randn(1, 1)
y_pred_real = discriminator(x_real)
loss_real = criterion(y_pred_real, torch.zeros(y_pred_real.size(0)).long())

x_src = torch.randn(1, 1)
x_fake = generator(x_src)
y_pred_fake = discriminator(x_fake)
loss_fake = criterion(y_pred_fake, torch.ones(y_pred_fake.size(0)).long())

loss = loss_real + loss_fake
loss.backward()

print(generator.weight.grad)
print(discriminator.weight.grad)

# Separate calls
generator.zero_grad()
discriminator.zero_grad()

y_pred_real = discriminator(x_real)
loss_real = criterion(y_pred_real, torch.zeros(y_pred_real.size(0)).long())
loss_real.backward()

print(generator.weight.grad)
print(discriminator.weight.grad)

x_fake = generator(x_src)
y_pred_fake = discriminator(x_fake)
loss_fake = criterion(y_pred_fake, torch.ones(y_pred_fake.size(0)).long())
loss_fake.backward()

print(generator.weight.grad)
print(discriminator.weight.grad)