pos_weight:为什么sigmoid分数不再等于原概率

黎 浩然/ 18 10 月, 2026/ 大语言模型/LARGELANGUAGEMODEL/LLM, 教程/TUTORIAL, 计算机/COMPUTER/ 0 comments

给BCEWithLogitsLoss设置pos_weight后,sigmoid输出仍在0到1之间,但不能因此把它直接当成原始数据分布下的正类概率。加权改变了优化目标。在理想的逐点最优情形,正类权重a会把真实条件概率q映射成p*=aq/(aq+1−q)。

文章目录
  1. pos_weight加在正类项上
  2. 固定一个输入,最优分数是什么
  3. 为什么0.5阈值的含义变了
  4. 十二项检查验证推导
  5. 反变换可以推导,校准仍要验证
  6. 训练、概率与决策分开验收
  7. 先检查正类定义和张量对应关系
  8. 把输出含义写进模型接口
  9. 来源与补发说明

这能解释一个常见困惑:加权后召回看起来变了,0.5阈值的含义却没有被重新检查。下面从损失推导映射,用十二项精确分数检查复现,再说明哪些结论可以用于排查,哪些不能当成真实模型校准保证。Read in English。

pos_weight加在正类项上

PyTorch官方文档给出的加权二元交叉熵,会用pos_weight缩放正类的对数损失项。本文只讨论一个二元标签、硬标签y∈{0,1}、恒定a>0,且没有额外样本权重的情形。多标签可逐类分析,但不能把不同类别的权重混成一个数。

记模型logit为z,p=sigmoid(z)。单样本损失为ℓ(p,y)=−ay log p−(1−y)log(1−p)。对实际API,输入应是logit;这里把损失改写成p,是为了方便看清最优点,不是建议先sigmoid再传入BCEWithLogitsLoss。

给整条样本损失乘一个共同常数,与只放大正类项不同。前者若对所有样本相同,不会改变无正则目标的数学最优点,但可能影响有限步优化;后者改变正负标签的相对代价。配置名里都有weight,并不代表统计含义相同。

固定一个输入,最优分数是什么

固定特征x,设q=P(y=1|x),0<q<1。模型在同样的x下输出同一个p。把标签随机性取期望,条件风险是R(p)=−aq log p−(1−q)log(1−p)。这一步把“正类项更重”转成了可求导的函数。

令一阶导数为零:R′(p)=−aq/p+(1−q)/(1−p)=0。移项得aq(1−p)=(1−q)p,于是p*=aq/(aq+1−q)。二阶导R″(p)=aq/p²+(1−q)/(1−p)²>0,因此在这个开区间里是唯一最小点。

当a=1,p*=q;当a>1且q在0到1之间,p*>q。分数上移不是公式失灵,而是你要求优化的目标已经偏向正类。它并没有证明有限数据训练出来的模型会精确落在这个解析解上。

a=1 → p*=0.2
a=4 → p*=0.5
a=9 → p*=9/13
原创总体风险示例:真实条件概率q=0.2不变;正类权重改变最优分数。图中是解析值,不是分类器实测准确率。

为什么0.5阈值的含义变了

在上述理想映射下,p*>0.5等价于q>1/(a+1)。如果a=4,分数0.5对应q=0.2;如果a=9,分数0.5对应q=0.1。因此继续用0.5截断加权模型,并不等于仍在真实概率0.5处做同一项决策。

例如合成情形中q=0.2,a从1改成4,最优分数从0.2变成0.5。输入条件下的标签分布并没有跟着变成一半正类。阈值恰好相等时还要规定使用大于还是大于等于,避免边界样本在不同实现中产生歧义。

若真实概率阈值希望为t,0<t<1,对应的理想加权分数阈值是at/(at+1−t)。例如a=4、t=0.5时,对应0.8。这个换算用于理解尺度,不能取代真实验证集上的阈值选择。

十二项检查验证推导

代码只用Python标准库Fraction。四组(q,a)各检查驻点导数和反变换,再检查两个具体分数及最小点两侧的导数符号,共十二项断言。

# Exact population-risk algebra, not a trained PyTorch model.
from fractions import Fraction as F

def optimal_score(q, a):
    return a*q / (a*q + 1-q)

def recover_q(p, a):
    return p / (a-(a-1)*p)

def derivative(p, q, a):
    return -a*q/p + (1-q)/(1-p)

for q,a in [(F(1,5),F(4)), (F(1,10),F(9)),
            (F(3,5),F(2)), (F(2,5),F(1))]:
    p = optimal_score(q,a)
    assert derivative(p,q,a) == 0
    assert recover_q(p,a) == q
assert optimal_score(F(1,5),F(4)) == F(1,2)
assert optimal_score(F(1,10),F(4)) == F(4,13)
assert derivative(F(2,5),F(1,5),F(4)) < 0
assert derivative(F(3,5),F(1,5),F(4)) > 0
print("12 weighted-BCE checks passed")

本地执行输出“12 weighted-BCE checks passed”。检查证明的是给定风险函数的代数关系;没有训练PyTorch模型,也没有测量准确率、召回率、校准误差或GPU性能。两侧导数符号与前面的严格凸性一起,解释了为什么p=0.5是该例的最优点。

反变换可以推导,校准仍要验证

由最优映射反解可得q=p*/[a−(a−1)p*]。等价地,在理想最优点,logit(p*)=logit(q)+log a。这个关系说明:恒定正类加权在理想条件下对应对数几率上的平移。

它也给了一个排查方向:若代码中改变a,却仍把输出解释成未加权概率,应重新审视输出契约。但不能对任意实测分数机械减去log a,就宣称已经完成概率校准。真实训练可能受模型容量、正则、优化误差、样本有限以及标签噪声影响。

采样还会改变问题。若训练时对正类过采样,q可能已经是采样后分布的条件概率;再乘pos_weight,得到的是对这个训练分布的加权目标。要恢复部署分布,必须知道采样与分布假设,不能只拿一个a修正所有偏移。

训练、概率与决策分开验收

scikit-learn阈值文档将分数学习与行动阈值区分开,并提醒不要用同一批数据既训练又调阈值。对实际项目,应先说明需要可比较的排序分数、可靠概率,还是某项约束下的分类标签,三者的验收方式不同。对理想分数施加同一个严格单调变换,会保留排序而改变数值含义;实际重新训练并不保证只发生这种变换,因此排序也可能变化。

拟合加权损失
检查分数校准
用验证数据选择阈值
在留出数据上最终评估
原创核验流程:训练、概率校准、阈值选择与最终评估回答不同问题。

训练阶段记录正类定义、采样方式、pos_weight及数据版本。验证阶段检查各阈值的混淆矩阵与目标指标;需要概率时,再用合适的留出或交叉验证方案评估校准。最终测试数据用于报告效果,不要反复据此挑选最漂亮的阈值。

不要仅凭召回上升就认定整体改进。阈值移动可能同时改变误报和精确率,应把这些变化放在同一份验证记录里。本文没有指定某个业务阈值,因为没有你的数据、约束或实测结果。

先检查正类定义和张量对应关系

实际排错时,先确认y=1到底表示什么,再看权重作用到哪个类别。多标签任务每类都可能有自己的a;如果类别顺序变化而权重顺序没变,损失仍能返回一个数,却优化了错误目标。

还要核对广播关系。一个形状能广播,不等于它沿着你想要的类别维度起作用。用很小的人工logit与标签,逐项计算未约简损失,比较预期的正类项和负类项,再接入完整训练;不要只看最终平均loss是否下降。

这与梯度累积的损失平均问题相通:数值正常并不能证明目标正确。前者要查样本归约,本篇要查正负项的相对权重,两者应分别核验。

把输出含义写进模型接口

如果下游把分数用于展示或跨版本比较,接口至少应记录它是原始logit、sigmoid分数,还是经过验证的概率估计,同时保存训练权重和阈值版本。只保留一个0到1的小数,会让后续使用者猜测它的语义。

pos_weight可以是有用的训练选择,但它改变了目标。先把这一点写清,再用独立数据判断排序、概率和决策表现。若本文的推导与复现代码帮你避免了分数解释错误,欢迎通过文末支持区自愿支持。

来源与补发说明

依据PyTorch 2.8官方损失定义与scikit-learn阈值文档核对。推导、十二项分数检查和两幅图为原创教学内容。本篇文章日期保留2026年10月18日14:00,实际于2026年10月19日补发;没有把文章日期当成此前已公开的证明。

支持

如果文中的解释或示例帮你解决了问题、节省了时间,欢迎自愿打赏支持本站。

微信支持二维码,点击查看大图
微信
支付宝支持二维码,点击查看大图
支付宝
Buy Me a Coffee,点击支持本站
Buy Me a Coffee

二维码可点击放大。更多支持方式见支持页面。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*