MSE广播:形状不同为何会把回归拉向均值
单输出回归中,预测形状是[N,1],标签形状是[N],不要因为它们各有N个数就直接相减。按广播规则,两者可能形成[N,N]的差值矩阵:每个预测与每个标签都比较一次,而不是每个样本只与自己的标签配对。
文章目录
这不只是多算了几项。错误目标会倾向于把每个输出拉向批内标签的共同均值。下面用“预测完全正确、错误损失却为2”的例子,推导损失与梯度为何改变,并给出能暴露错误的配对测试。Read in English。
从右侧对齐,形状到底变成什么
PyTorch广播说明要求从末维对齐尺寸;相等或有一方为1时可以扩展。[N]可视为[1,N],因此[N,1]与[1,N]相减的结果是[N,N]。行索引选择预测样本i,列索引选择标签样本j。
MSELoss文档要求目标与输入形状相同。这里用显式平方差推导广播的后果,不依赖某个版本是否发出警告;出现警告时不应把它当成无害噪声。公式里能执行的扩展,不等于任务需要这种配对。
正确契约可以统一为[N],也可以统一为[N,1],前提是每一个维度的含义一致。多输出回归[N,D]又是另一个契约,不能为了消除报错任意reshape成元素数相同的形状。
完全正确的预测,也能得到非零错误损失
取两条样本,预测p=[1,3],目标y=[1,3]。逐样本配对的平方误差是[0,0],均值0。广播成所有配对后,平方误差矩阵为[[0,4],[4,0]],四项均值是2。
如果把标签交换为[3,1],正确配对的均方误差变成4,因为每个预测都对应错了标签;所有配对的均值却仍是2。它只看到了同一组目标数值,没有保留“哪个标签属于哪个输入”的对应关系。
这说明,仅检查loss是有限标量、反向能运行或者训练曲线下降,都不足以证明目标正确。错误目标也有明确最优点,优化器可能稳定地朝那个点走。要检验的是任务想要的配对是否仍存在。
为什么错误目标偏向批次均值
正确目标L_pair=(1/N)Σᵢ(pᵢ−yᵢ)²,对pᵢ求导为2(pᵢ−yᵢ)/N。每个输出直接追逐自己的标签。所有配对目标L_all=(1/N²)ΣᵢΣⱼ(pᵢ−yⱼ)²,对pᵢ求导为2(pᵢ−ȳ)/N,其中ȳ是本批目标平均值。
若把这一批的每个pᵢ都看成可独立选择的数,L_all的最优点是全部等于ȳ。本例ȳ=2,输出[2,2]时所有配对损失为1,确实低于正确预测[1,3]得到的错误损失2。优化错误目标甚至会主动破坏原本正确的结果。
换一种看法,对任意固定pᵢ,有(1/N)Σⱼ(pᵢ−yⱼ)²=(pᵢ−ȳ)²+Var_batch(y),这里方差按N作分母。第二项不由预测决定,因此优化只会把pᵢ拉向均值,而不是找回原标签对应。
真实神经网络的输出共享参数,跨批次分布和优化过程也会影响最终模型,不能把这个逐批自由变量最优点当成训练结果的保证。它准确揭示的是:错误配对把监督信号从yᵢ换成了批次均值,模型容量再大也不会自动知道你的原意。
九项精确检查抓住配对语义
代码用Fraction分别实现逐样本与所有配对目标。它没有导入PyTorch,也不测试任何设备性能。两种函数都明确拒绝其自身不支持的空输入,避免把空均值问题混进本次推导。
# Exact paired and all-pairs objectives, not a PyTorch execution.
from fractions import Fraction as F
def paired(pred, target):
if not pred or len(pred)!=len(target):
raise ValueError("equal nonempty lengths required")
return sum((F(p)-t)**2 for p,t in zip(pred,target))/len(pred)
def all_pairs(pred, target):
if not pred or not target:
raise ValueError("nonempty inputs required")
return sum((F(p)-t)**2 for p in pred for t in target)/(len(pred)*len(target))
p=[F(1),F(3)]; y=[F(1),F(3)]
assert paired(p,y)==0
assert all_pairs(p,y)==2
assert paired(p,list(reversed(y)))==4
assert all_pairs(p,list(reversed(y)))==2
assert all_pairs([F(2),F(2)],y)==1
assert paired([F(2),F(2)],y)==1
assert all_pairs([F(1)],[F(1)])==paired([F(1)],[F(1)])
assert len([(a,b) for a in p for b in y])==4
assert paired(p,y)!=all_pairs(p,y)
print("9 MSE pairing checks passed")
本地九项断言通过,覆盖正确预测、标签置换、常数预测、单样本退化和配对数量。测试特意包含N=2:N=1时两个目标碰巧相同,若只测单条样本就会掩盖形状错误。所有配对函数允许两侧长度不同是数学定义选择,不代表生产回归接口应该接受不匹配标签数。
先统一语义,再选择squeeze方式
对于[N,1]的单输出预测,若标签契约为[N],可以明确只去掉最后那个已确认尺寸为1的输出维;若标签契约为[N,1],就给标签补上对应维。应先检查输出维确实代表单个回归量,而不是碰巧为1的序列长度或通道。
不带维度的squeeze会删除所有尺寸为1的轴。批大小恰为1时,它可能把批轴也去掉,让训练与评估路径得到不同秩。显式记录预测和标签的期望形状,并在损失前要求两者完全相等,比靠一个“总能消掉多余维”的操作更可靠。
也不要仅比较numel。一个[N,D]矩阵与[D,N]矩阵可以元素数相同,却交换了样本和输出语义。reshape不会替你验证样本顺序,转置和拼接后的对应关系也必须在数据管道中检查。
把标签置换做成有意的负测试
构造三条彼此不同的标签,并令预测逐项等于标签,正确损失应为零。然后只置换标签、保持输入与预测不变,损失应按具体值变化。若依旧不变,先看是否意外计算了所有配对,或数据对齐在更早的排序与拼接中丢失。
相反,如果同时按同一排列重排输入、预测与标签,逐样本平均损失应保持不变。这两个测试一个破坏对应,一个保留对应,目的不同。把它们放在同一组回归用例中,可以防止错误修复只迎合其中一个标量结果。
测试还应覆盖批大小1与大于1、最后一个不足批、多输出和合法空数据策略。不要为了通过测试而一律丢弃最后一批;那会把形状问题隐藏成数据选择变化。对比前先写清每条样本的标识与标签配对,但对外教学示例不需要放真实用户数据。
如果测试标签碰巧全都一样,破坏配对也可能不改变损失。因此负测试要主动选择彼此不同的合成标签,并手工核对预期数值;不能把“置换后不变”单独当作适用于任何数据的错误判据。
形状断言要靠近损失入口
在最接近loss的位置同时检查rank、各轴尺寸和标签含义,能把报错留在最容易解释的位置。若将所有张量一路广播到最后只剩一个标量,诊断信息已经损失很多。
这与梯度累积的损失归约不同:那里关注正确损失如何跨微批汇总;本篇先确认单次损失究竟比较了哪些样本。先修复配对,再检查平均与累积,顺序才能避免对错误目标做精确汇总。
若这组推导与标签置换测试帮你排除了“loss下降但回归变差”的一类错误,欢迎通过文末支持区自愿支持。可复用的收益,是把样本对应关系写成能执行的约束。
来源与补发说明
Broadcasting semantics、MSELoss
依据原计划时段前已有的PyTorch 2.8定义核对;推导、反例与图示为原创教学内容。保留2026年10月19日19:00文章日期,实际于10月19日晚补发。标准库代码已执行,没有运行PyTorch模型或测量设备性能。
支持
如果文中的解释或示例帮你解决了问题、节省了时间,欢迎自愿打赏支持本站。
二维码可点击放大。更多支持方式见支持页面。


