Difference between nn.MSELoss and torch.mean((op-target)**2)

DuttaAbhigyan: <p>So I wanted to know is there any difference between the gradients produced by the following 2 pieces of code or in any other matter:</p>
<pre><code class="lang-auto">criterion = nn.MSELoss()
loss = criterion(outputs, labels)
</code></pre>
<p>and</p>
<pre><code class="lang-auto">loss = torch.mean((output-labels) ** 2)
</code></pre>
<p>NOTE: The output is a batch of outputs.</p>

acobobby: <p>From the source code of <code>torch.nn.MSELoss</code> you can see that the class is a wrapper for <code>torch.nn.functional.mse_loss</code>. The relevant part of the code for this one is (<a href="https://pytorch.org/docs/stable/_modules/torch/nn/functional.html#mse_loss" rel="nofollow noopener">source code link</a>):</p>
<pre><code class="lang-auto">if size_average is not None or reduce is not None:
    reduction = _Reduction.legacy_get_string(size_average, reduce)
if target.requires_grad:
    ret = (input - target) ** 2
    if reduction != 'none':
        ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret)
else:
    expanded_input, expanded_target = torch.broadcast_tensors(input, target)
    ret = torch._C._nn.mse_loss(expanded_input, expanded_target, _Reduction.get_enum(reduction))
    return ret
</code></pre>
<p>So, as you can see if target requires gradient the operations are exactly the same as your code (hence, the gradient is the same).<br>
If target does not require gradient the C module implementation of MSE is used. I don’t know how it is implemented in C module but I would say that it is the same calculation. Anyway, just wait for someone more informed about this.</p>

xuef: <p>Training outcome is consistently different in my problem. I wonder if anyone has an explanation for this. Does error is all put to input when <code>target.requires_grad=False</code>?</p>

Psca: <p>I’m also wondering why the output of  <code>nn.MSELoss</code> is different to <code>torch.mean((output-labels)**2)</code>.<br>
I tried both on my code and the results differ.<br>
I tried all kinds of mse loss calculations:</p>
<pre><code class="lang-auto">1. torch.mean(torch.square(output - labels))
2. torch.mean((output - labels)**2)
3. ((output - labels)**2).mean()
4. criterion = nn.MSELoss()
   loss = criterion(output, labels)
5. labels.requires_grad = True
   criterion = nn.MSELoss()
   loss = criterion(output, labels)
6. loss = F.mse_loss(output, labels)
</code></pre>
<p>It seems like that 1,2,3 all give the same result as do 4,5,6 but the results between those two groups differ. Why is this the case? I wonder if it has to do with the precision used in C since MSELoss calls the C api, could this be the case?</p>

ptrblck: <p>If the relative errors are in the range ~1e-6, the difference is most likely caused by the limited floating point precision and a different order of operations in these approaches.</p>

Psca: <p>Thanks. I have to correct myself after some extra study on the results. It is nog the loss which gives different results. I think, but am not sure, that the <code>backward()</code> function might run differently for both groups I talked about before.<br>
I ran the code while printing both version of the loss: 1. <code>torch.mean(torch.square(output - labels))</code> and using 2. <code>nn.MSELoss()</code>. Every iteration they give the exact same result.<br>
However, when using method 1. in my model to train on, the results (and following losses) differ from those while using method 2. to train on.<br>
Again, in both cases both loss-functions give the same result every iteration:<br>
Using 1. to train on gives a loss of 2.771e-1 (for both methods), my trainable parameter values are R = 1.120 and C = 8.881e-1.<br>
Using 2. to train on gives a loss of 3.639e-1 (for both methods), my trainable parameter values are R =  1.388 and 9.691e-1</p>
<p>My question is, is it expected to get different results while using method 1. instead of 2.? And why is this probably be the case?</p>

ptrblck: <p>The gradient calculation would also yield the same results as seen here:</p>
<pre><code class="lang-python">output = torch.randn(100, 100, requires_grad=True)
labels = torch.randn(100, 100)

torch.mean(torch.square(output - labels)).backward()
g0 = output.grad.clone()
print(output.grad)
output.grad = None

torch.mean((output - labels)**2).backward()
g1 = output.grad.clone()
print(output.grad)
output.grad = None

((output - labels)**2).mean().backward()
g2 = output.grad.clone()
print(output.grad)
output.grad = None

criterion = nn.MSELoss()
loss = criterion(output, labels)
loss.backward()
g3 = output.grad.clone()
print(output.grad)
output.grad = None

loss = F.mse_loss(output, labels)
loss.backward()
g4 = output.grad.clone()
print(output.grad)
output.grad = None

print(torch.allclose(g0, g1))
&gt; True
print(torch.allclose(g0, g2))
&gt; True
print(torch.allclose(g0, g3))
&gt; True
print(torch.allclose(g0, g4))
&gt; True
</code></pre>
<p>How reproducible is the difference, how many runs did you use for each approach, and what is the mean of the final loss as well as its stddev?</p>

Psca: <p>For each approach I used 20 000 epochs. I used random seeds to have the same output every time.<br>
I noticed that the first ~2000  epochs the values of R en C (parameters I added to the model to fit) are the same using both approaches (1. <code>torch.mean(torch.square(output - labels))</code> and 2. <code>criterion = nn.MSELoss()</code>) . After these epochs, the values start to differ. Here I show you the training timelines of Parameter C:<br>
<img src="https://discuss.pytorch.org/uploads/default/original/3X/0/4/04ec88a69500f917e0b7de9bc03a97835e057267.png" alt="afbeelding" data-base62-sha1="HyGevvEVXqJdYmCuc2P5Vi8nDp" width="339" height="256"> Using 1. <code>torch.mean(torch.square(output - labels))</code><br>
<img src="https://discuss.pytorch.org/uploads/default/original/3X/1/c/1c3a7a0cf04b3e506ebebdccbbd934fe5f4442ce.png" alt="afbeelding" data-base62-sha1="41IEC1rQWHkU7lQ2XGngswqcK3s" width="339" height="256"> Using 2. <code>criterion = nn.MSELoss()</code><br>
Again, the only thing I changed was the method I used to describe the loss, although they should be (and are) the same. Both losses give identical every epoch, but when using method 1. to learn on, the parameters are getting other values then when the model is learned on method 2. (as shown in the figures). Therefore, other losses are found between both methods.<br>
I am just wondering why this difference shows up. <img src="https://discuss.pytorch.org/images/emoji/apple/thinking.png?v=12" title=":thinking:" class="emoji" alt=":thinking:" loading="lazy" width="20" height="20"> Since the losses are the same during training, but different if it trains on the other loss-function, I thought it might be the case that the backward function works differently for both approaches.</p>

ptrblck: <p>Both approaches might create small rounding errors due to the different order of operations.<br>
Rerunning the scripts with different seeds multiple times using both approaches should show similar convergence pattern. I.e. the number of converged runs vs. non-converged ones should approx. be the same for an increasing number of reruns.</p>
<p>I would not expect that these small differences would create different results, but your general training might be sensitive to these small changes.</p>