16 Softmax在YOLOv8中只做一件事——把16个Bin的分数变成概率

发布时间:2026/7/16 21:31:40
16 Softmax在YOLOv8中只做一件事——把16个Bin的分数变成概率 Softmax在YOLOv8中只做一件事——把16个Bin的分数变成概率一句话总结Softmax 在 YOLOv8 中只用于 DFL 解码——把 16 个 bin 的原始分数变成概率分布加起来 1然后才能加权平均算出坐标。分类分支用的是 Sigmoid不是 Softmax。目录16-Softmax在YOLOv8中只做一件事——把16个Bin的分数变成概率目录Softmax 在哪出场为什么需要 Softmax——原始分数不能当概率用代码演示16 个 bin 的完整 Softmax 过程可视化Softmax 前后对比Softmax 的两个关键特性特性 1赢家通吃特性 2概率总和 1为什么分类分支用 Sigmoid 而不是 Softmax自测一句话记住Softmax 在哪出场YOLOv8 内部用到 Softmax 的只有一处——DFL 解码链DFL 解码链: 16 个 bin 的原始分数 Softmax 后16 个概率 [2.0, 5.0, 1.0, 0.5, ...] → [0.046, 0.927, 0.017, 0.010, ...] ↑ ↑ 正负无穷都行 加起来 1.0 ↓ 加权平均Conv(dfl) ↓ 偏移量 → Sub → Div → 像素坐标分类分支80 个类别用的是 Sigmoid不是 Softmax。为什么需要 Softmax——原始分数不能当概率用模型输出的 16 个 bin 值是原始分数没有范围限制原始分数16 个 bin: [2.0, 5.0, 1.0, 0.5, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] 加起来 8.8 ← 不是 100%不能叫投票Softmax 的任务就是把打分变成投票——让 16 个值变成合法的概率分布。代码演示16 个 bin 的完整 Softmax 过程importnumpyasnpimportmatplotlib.pyplotasplt# 解决中文显示问题plt.rcParams[font.sans-serif][SimHei,Microsoft YaHei,DejaVu Sans]plt.rcParams[axes.unicode_minus]False# # 第1步给定 16 个 bin 的原始分数模拟 YOLOv8 的 DFL 输出# # 这是一组典型的 DFL 输出bin₁ 的分数最高说明偏移量大概率在 0.5 附近raw_scoresnp.array([2.0,5.0,1.0,0.5,0.2,0.1,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])print(*60)print(第1步原始分数16 个 bin)print(*60)fori,sinenumerate(raw_scores):bar█*int(s*5)print(f bin_{i:2d}(偏移{i*0.5:.1f}):{s:6.2f}{bar})# # 第2步Softmax 计算# defsoftmax(x):Softmax 函数e^x / sum(e^x)e_xnp.exp(x-np.max(x))# 减去最大值防止数值溢出returne_x/e_x.sum()probssoftmax(raw_scores)print(\n*60)print(第2步Softmax 后16 个概率)print(*60)print(f 概率总和:{probs.sum():.6f}← 验证加起来必须等于 1)print()fori,pinenumerate(probs):bar█*int(p*100)print(f bin_{i:2d}(偏移{i*0.5:.1f}):{p:.4f}({p*100:5.1f}%){bar})# # 第3步加权平均 → 偏移量# # Conv(dfl) 的权重 [0, 1, 2, ..., 15]dfl_weightsnp.arange(16,dtypenp.float32)offsetnp.dot(probs,dfl_weights)print(\n*60)print(第3步加权平均 → 偏移量)print(*60)print(f 权重: [0, 1, 2, ..., 15])print(f 计算: sum(prob_i × i) for i0..15)print(f 偏移量 {offset:.4f}个特征图像素)print(f 含义: 框的边界离格子左上角约{offset:.2f}个特征图像素)# # 第4步对比——如果不用 Softmax 直接加权平均# # 如果直接用原始分数加权平均不归一化raw_offsetnp.dot(raw_scores,dfl_weights)/raw_scores.sum()print(f\n 如果不用 Softmax直接加权平均:{raw_offset:.4f})print(f 用 Softmax正确做法:{offset:.4f})print(f ← Softmax 保证了概率的合法性)# # 可视化# fig,axesplt.subplots(2,2,figsize(14,10))# 图1原始分数ax1axes[0,0]colors_raw[#d4e6f1ifs1else#85c1e9ifs3else#2e86c1forsinraw_scores]bars1ax1.bar(range(16),raw_scores,colorcolors_raw,edgecolorwhite,linewidth0.5)ax1.set_title(原始分数Softmax 前,fontsize14,fontweightbold)ax1.set_xlabel(Bin 编号)ax1.set_ylabel(原始分数)ax1.set_xticks(range(16))ax1.set_xticklabels([f{i}\n({i*0.5})foriinrange(16)])ax1.axhline(y0,colorgray,linestyle-,linewidth0.5)fori,(bar,s)inenumerate(zip(bars1,raw_scores)):ifs0.5:ax1.text(bar.get_x()bar.get_width()/2,bar.get_height()0.1,f{s:.1f},hacenter,vabottom,fontsize8,fontweightbold)ax1.set_ylim(0,max(raw_scores)*1.2)# 图2概率分布Softmax 后ax2axes[0,1]colors_prob[#fadbd8ifp0.05else#f1948aifp0.3else#e74c3cforpinprobs]bars2ax2.bar(range(16),probs,colorcolors_prob,edgecolorwhite,linewidth0.5)ax2.set_title(概率分布Softmax 后,fontsize14,fontweightbold)ax2.set_xlabel(Bin 编号)ax2.set_ylabel(概率)ax2.set_xticks(range(16))ax2.set_xticklabels([f{i}\n({i*0.5})foriinrange(16)])fori,(bar,p)inenumerate(zip(bars2,probs)):ifp0.01:ax2.text(bar.get_x()bar.get_width()/2,bar.get_height()0.005,f{p:.3f},hacenter,vabottom,fontsize8,fontweightbold)ax2.set_ylim(0,max(probs)*1.2)# 图3加权平均过程ax3axes[1,0]bin_offsetsnp.arange(16)*0.5# 每个 bin 对应的实际偏移量ax3.bar(bin_offsets,probs,width0.4,color#e74c3c,alpha0.7,edgecolorwhite,label概率)ax3.axvline(xoffset*0.5,color#2e86c1,linewidth3,linestyle--,labelf加权平均 {offset*0.5:.2f}特征图像素)ax3.set_title(加权平均——框偏移量的由来,fontsize14,fontweightbold)ax3.set_xlabel(偏移量特征图像素)ax3.set_ylabel(概率)ax3.legend(fontsize10)ax3.grid(axisy,alpha0.3)# 图4对比 Sigmoid vs Softmaxax4axes[1,1]# 模拟分类分支的 6 个类别用 Sigmoidcls_scoresnp.array([9.5,0.1,0.2,0.3,0.0,0.1])cls_sigmoid1/(1np.exp(-cls_scores))x_posnp.arange(6)ax4.bar(x_pos,cls_sigmoid,color#27ae60,alpha0.7,edgecolorwhite,labelSigmoid分类)ax4.axhline(y1.0,colorgray,linestyle:,alpha0.5)ax4.set_title(Sigmoid 用于分类分支80 个类别,fontsize14,fontweightbold)ax4.set_xlabel(类别)ax4.set_ylabel(置信度)ax4.set_xticks(x_pos)ax4.set_xticklabels([person,bicycle,car,cat,dog,...])ax4.set_ylim(0,1.1)fori,(bar,v)inenumerate(zip(ax4.patches,cls_sigmoid)):ax4.text(bar.get_x()bar.get_width()/2,bar.get_height()0.02,f{v:.3f},hacenter,vabottom,fontsize9,fontweightbold)ax4.legend(fontsize10)plt.suptitle(Softmax 在 YOLOv8 中的作用——DFL 解码链,fontsize16,fontweightbold,y1.01)plt.tight_layout()plt.savefig(softmax_in_yolov8.png,dpi150,bbox_inchestight)plt.show()print(\n*60)print(总结)print(*60)print(f 原始分数 (bin₁): 5.0 → Softmax → 概率:{probs[1]:.4f}({probs[1]*100:.1f}%))print(f 原始分数 (bin₀): 2.0 → Softmax → 概率:{probs[0]:.4f}({probs[0]*100:.1f}%))print(f 偏移量 {offset:.4f}特征图像素)print(f 原图坐标 {offset:.4f}× stride)运行结果 第1步原始分数16 个 bin bin_ 0 (偏移 0.0): 2.00 ██████████ bin_ 1 (偏移 0.5): 5.00 █████████████████████████ bin_ 2 (偏移 1.0): 1.00 █████ bin_ 3 (偏移 1.5): 0.50 ██ bin_ 4 (偏移 2.0): 0.20 █ bin_ 5 (偏移 2.5): 0.10 bin_ 6 (偏移 3.0): 0.00 bin_ 7 (偏移 3.5): 0.00 bin_ 8 (偏移 4.0): 0.00 bin_ 9 (偏移 4.5): 0.00 bin_10 (偏移 5.0): 0.00 bin_11 (偏移 5.5): 0.00 bin_12 (偏移 6.0): 0.00 bin_13 (偏移 6.5): 0.00 bin_14 (偏移 7.0): 0.00 bin_15 (偏移 7.5): 0.00 第2步Softmax 后16 个概率 概率总和: 1.000000 ← 验证加起来必须等于 1 bin_ 0 (偏移 0.0): 0.0428 ( 4.3%) ████ bin_ 1 (偏移 0.5): 0.8604 ( 86.0%) ██████████████████████████████████████████████████████████████████████████████████████ bin_ 2 (偏移 1.0): 0.0158 ( 1.6%) █ bin_ 3 (偏移 1.5): 0.0096 ( 1.0%) bin_ 4 (偏移 2.0): 0.0071 ( 0.7%) bin_ 5 (偏移 2.5): 0.0064 ( 0.6%) bin_ 6 (偏移 3.0): 0.0058 ( 0.6%) bin_ 7 (偏移 3.5): 0.0058 ( 0.6%) bin_ 8 (偏移 4.0): 0.0058 ( 0.6%) bin_ 9 (偏移 4.5): 0.0058 ( 0.6%) bin_10 (偏移 5.0): 0.0058 ( 0.6%) bin_11 (偏移 5.5): 0.0058 ( 0.6%) bin_12 (偏移 6.0): 0.0058 ( 0.6%) bin_13 (偏移 6.5): 0.0058 ( 0.6%) bin_14 (偏移 7.0): 0.0058 ( 0.6%) bin_15 (偏移 7.5): 0.0058 ( 0.6%) 第3步加权平均 → 偏移量 权重: [0, 1, 2, ..., 15] 计算: sum(prob_i × i) for i0..15 偏移量 1.5896 个特征图像素 含义: 框的边界离格子左上角约 1.59 个特征图像素 如果不用 Softmax直接加权平均: 1.1136 用 Softmax正确做法: 1.5896 ← Softmax 保证了概率的合法性可视化Softmax 前后对比运行代码后生成 4 张图左上图原始分数——bin₁ 一枝独秀5.0其他 bin 也有一定分数。右上图Softmax 后的概率——bin₁ 占 92.7%其他 bin 被压到很低。e^x的放大效应让最大值赢家通吃。左下图加权平均过程——每个 bin 的概率红色柱乘以对应的偏移量加权平均线蓝色虚线就是最终偏移量。右下图分类分支用 Sigmoid——每个类别独立算置信度不互斥person 0.999 的同时 cat 也可以 0.574。Softmax 的两个关键特性特性 1赢家通吃Softmax 的e^x会让大的值更大、小的值更小原始分数: [2.0, 5.0, 1.0, 0.5] e^2.0 e^5.0 e^1.0 e^0.5 7.39 148.4 2.72 1.65 Softmax: [0.046, 0.927, 0.017, 0.010] ↑ ↑ 被压低了 被放大了特性 2概率总和 1Softmax 保证了 16 个 bin 的概率加起来等于 1这是概率分布的基本要求。只有这样加权平均才有数学意义。为什么分类分支用 Sigmoid 而不是 SoftmaxDFL 分支回归分类分支激活函数SoftmaxSigmoid通道数16 个 bin80 个类别互斥性互斥偏移量只有 1 个正确答案不互斥一只猫可以同时是 cat 和 animal总和 1各自独立不要求和分类分支Sigmoid: person: 0.9997 ← 可以同时很高 cat: 0.5743 ← 不互斥 dog: 0.0201 DFL 分支Softmax: bin₀: 0.0462 bin₁: 0.9267 ← 互斥加起来 1 bin₂: 0.0170 bin₃: 0.0103自测Q1Softmax 在 YOLOv8 中出现在哪点击查看答案只出现在 DFL 解码链中。分类分支用的是 Sigmoid。Q2为什么 DFL 需要 Softmax点击查看答案模型的原始输出是任意范围的分数不能直接加权平均。Softmax 把分数变成概率分布加起来 1然后每个 bin 的概率乘以权重求和得到合法的加权平均值。Q3Softmax 的e^x会导致什么现象点击查看答案“赢家通吃”——大的值被放大小的值被压低。比如原始分数 [2.0, 5.0]Softmax 后变成 [0.046, 0.927]差距从 3 倍变成了 20 倍。Q4分类分支为什么不用 Softmax点击查看答案分类分支的 80 个类别不互斥——一只猫可以同时是 cat 和 animal。Softmax 强制总和为 1会迫使模型选一个不适合多标签场景。Sigmoid 每个类别独立输出 0~1允许多个类别同时为高。一句话记住Softmax 把 16 个 bin 的原始分数变成概率分布加起来 1让加权平均有数学意义。e^x的放大效应让高分 bin 赢家通吃最终偏移量由最高分的 bin 主导。