需要掌握: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}
链式求导推导
对 $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对 $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
总结
MSE Loss 的
backward()核心是通过链式求导,计算损失对模型参数的梯度,单样本下梯度公式为:对权重 w:2 \times (y_{\text{pred}} - y_{\text{true}}) \times x
对偏置 b:2 \times (y_{\text{pred}} - y_{\text{true}})
多样本且
reduction='mean'时,梯度会额外除以样本数 n;执行
loss.backward()前必须用optimizer.zero_grad()清空梯度,否则梯度会累加导致更新错误。