pytorch api文档:nn.MSELoss()的backward()方法-反向传播自动计算梯度(求导)

需要掌握:pytorch api文档:nn.MSELoss()均方差损失

MSE Loss 反向传播的数学原理

回顾 MSE 的公式(单样本、reduction=’mean’ 时):

\text{loss} = (y_{\text{pred}} - y_{\text{true}})^2

(注:单样本时 mean 和 sum 结果一致,多样本时 mean 会多一个 1/n 的系数,逻辑相同)

假设模型的简单线性结构为:y_{\text{pred}} = w \cdot x + b,要通过 loss.backward() 计算:

  • 损失对权重 w 的梯度:\frac{\partial \text{loss}}{\partial w}

  • 损失对偏置 b 的梯度:\frac{\partial \text{loss}}{\partial b}

链式求导推导

  1. 对 $b$ 的梯度:
    \frac{\partial \text{loss}}{\partial b} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot \frac{\partial y_{\text{pred}}}{\partial b} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot 1

  2. 对 $w$ 的梯度:
    \frac{\partial \text{loss}}{\partial w} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot \frac{\partial y_{\text{pred}}}{\partial w} = 2(y_{\text{pred}} - y_{\text{true}}) \cdot x

代码验证

代码验证 MSE Loss 的 backward() 计算

下面用极简的代码验证梯度计算是否和数学推导一致:

import torch
import torch.nn as nn

# 1. 手动设定模型参数(方便计算)
w = torch.tensor([2.0], requires_grad=True)  # 权重w=2
b = torch.tensor([1.0], requires_grad=True)  # 偏置b=1
x = torch.tensor([3.0])                      # 输入x=3
y_true = torch.tensor([8.0])                 # 真实值y=8

# 2. 前向计算预测值和MSE损失
y_pred = w * x + b  # 2*3+1=7
mse = nn.MSELoss()
loss = mse(y_pred, y_true)  # loss=(7-8)²=1

# 3. 执行反向传播
loss.backward()

# 4. 打印梯度并验证
print(f"预测值 y_pred: {y_pred.item()}")
print(f"MSE损失 loss: {loss.item()}")
print(f"\n手动计算的w梯度: 2*(7-8)*3 = {-6}")
print(f"backward()计算的w梯度: {w.grad.item()}")  # 应该输出-6
print(f"\n手动计算的b梯度: 2*(7-8)*1 = {-2}")
print(f"backward()计算的b梯度: {b.grad.item()}")  # 应该输出-2

输出结果:

预测值 y_pred: 7.0
MSE损失 loss: 1.0

手动计算的w梯度: 2*(7-8)*3 = -6
backward()计算的w梯度: -6.0

手动计算的b梯度: 2*(7-8)*1 = -2
backward()计算的b梯度: -2.0

可以看到,backward() 计算的梯度和我们手动推导的完全一致!

3. 多样本场景下的 MSE backward()

当有多个样本时,MSE 的 reduction='mean' 会对损失取平均,梯度也会对应除以样本数 $n$:

# 多样本示例(n=2)
w = torch.tensor([2.0], requires_grad=True)
b = torch.tensor([1.0], requires_grad=True)
x = torch.tensor([3.0, 4.0])          # 2个输入样本
y_true = torch.tensor([8.0, 10.0])    # 2个真实值

y_pred = w * x + b  # [7.0, 9.0]
loss = mse(y_pred, y_true)  # [(7-8)² + (9-10)²]/2 = (1+1)/2 = 1.0

# 清空之前的梯度,重新反向传播
w.grad.zero_()
b.grad.zero_()
loss.backward()

print(f"\n多样本MSE损失: {loss.item()}")
# 手动计算w梯度:[2*(7-8)*3 + 2*(9-10)*4]/2 = (-6-8)/2 = -7
print(f"多样本w梯度: {w.grad.item()}")  # 输出-7
# 手动计算b梯度:[2*(7-8)*1 + 2*(9-10)*1]/2 = (-2-2)/2 = -2
print(f"多样本b梯度: {b.grad.item()}")  # 输出-2

4. 完整模型中 MSE backward() 的使用

结合之前的线性回归模型,完整展示 MSE 损失反向传播的流程:

# 定义线性回归模型
class LinearModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1, 1)  # w和b是模型参数

    def forward(self, x):
        return self.linear(x)

# 初始化模型、损失函数、优化器
model = LinearModel()
# 手动初始化参数,方便验证(w=2, b=1)
with torch.no_grad():
    model.linear.weight.fill_(2.0)
    model.linear.bias.fill_(1.0)

criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 输入数据
x = torch.tensor([[3.0]])
y_true = torch.tensor([[8.0]])

# 训练一步
optimizer.zero_grad()  # 清空梯度
y_pred = model(x)      # 前向传播
loss = criterion(y_pred, y_true)  # 计算MSE损失
loss.backward()        # MSE损失反向传播,计算梯度
optimizer.step()       # 用梯度更新参数

# 查看更新后的参数
print(f"\n更新前w: 2.0, 更新后w: {model.linear.weight.item():.2f}")  # 2.0 - 0.01*(-6) = 2.06
print(f"更新前b: 1.0, 更新后b: {model.linear.bias.item():.2f}")      # 1.0 - 0.01*(-2) = 1.02

总结

  1. MSE Loss 的 backward() 核心是通过链式求导,计算损失对模型参数的梯度,单样本下梯度公式为:

    • 对权重 w:2 \times (y_{\text{pred}} - y_{\text{true}}) \times x

    • 对偏置 b:2 \times (y_{\text{pred}} - y_{\text{true}})

  2. 多样本且 reduction='mean' 时,梯度会额外除以样本数 n;

  3. 执行 loss.backward() 前必须用 optimizer.zero_grad() 清空梯度,否则梯度会累加导致更新错误。


原文出处:/show_1GW2ehiQEndH.html