Softmax 多分类回归:参考答案#
说明#
以下答案与正文9道题逐题对应。前四题给出主要推导与计算;第 5--6 题给出可以运行的程序和检查方法;第 7--9 题说明比较时需要保持哪些条件相同,以及实验结果能够说明什么、不能说明什么。
分母为 \(e^2+e+1\),因此
\[\ba =\frac{1}{e^2+e+1}(e^2,e,1)\trans \approx(0.6652,0.2447,0.0900)\trans.\]真实类别为第 2 类时,
\[\mathcal L=-\log a_2\approx1.4076, \qquad \frac{\partial\mathcal L}{\partial\bz} =\ba-\by \approx(0.6652,-0.7553,0.0900)\trans.\]梯度分量和约为 0;精确地说,\(\bone\trans\cdot(\ba-\by)=1-1=0\)。近似数相加可能因四舍五入产生很小误差。
第 \(k\) 个分量为
\[\frac{\exp(z_k+c)}{\sum_j\exp(z_j+c)} =\frac{e^c\exp(z_k)}{e^c\sum_j\exp(z_j)} =\frac{\exp(z_k)}{\sum_j\exp(z_j)}.\]因此可取 \(c=-\max_k z_k\),使最大指数为 1 而不改变概率。任意两类概率比满足 \(a_i/a_j=\exp(z_i-z_j)\),只依赖差值;向 \(\bz\) 加 \(c\bone\) 无法从概率中识别。训练时若参数允许所有类别偏置共同平移,目标沿该方向保持不变。
对 \(a_k=e^{z_k}/S\)、\(S=\sum_r e^{z_r}\) 使用商法则:
\[\frac{\partial a_k}{\partial z_j} =a_k(\delta_{kj}-a_j).\]因而
\[\boldsymbol{J}_{\mathrm{softmax}} =\operatorname{diag}(\ba)-\ba\cdot\ba\trans.\]对 \(\mathcal L=-\sum_k y_k\log a_k\),
\[\frac{\partial\mathcal L}{\partial z_j} =-\sum_k\frac{y_k}{a_k}a_k(\delta_{kj}-a_j) =-y_j+a_j\sum_k y_k =a_j-y_j,\]其中 one-hot 标签满足 \(\sum_k y_k=1\)。若使用概率软标签,只要标签非负且和为 1,同一结论仍成立。
因
\[\mathcal L(\bz)=\log\sum_k e^{z_k}-\by\trans\cdot\bz,\]一阶梯度为 \(\ba-\by\),Hessian 为
\[\boldsymbol{H}=\operatorname{diag}(\ba)-\ba\cdot\ba\trans.\]对任意向量 \(\bv\),
\[\bv\trans\cdot\boldsymbol{H}\cdot\bv =\sum_k a_kv_k^2-\left(\sum_k a_kv_k\right)^2 =\operatorname{Var}_{k\sim\ba}(v_k)\geq0.\]因而 Hessian 半正定,损失为凸函数。取 \(\bv=\bone\) 时加权方差为 0,对应共同平移方向;所以关于未加约束的 \(\bz\) 不是严格凸函数。
一个稳定的批量实现为
import numpy as np def softmax_cross_entropy(scores, targets): scores = np.asarray(scores, dtype=np.float64) targets = np.asarray(targets) if scores.ndim != 2 or targets.shape != (scores.shape[0],): raise ValueError("scores 应为二维,targets 应为同批一维") if not np.isfinite(scores).all() or not np.issubdtype(targets.dtype, np.integer): raise ValueError("输入必须有限且标签必须为整数") if np.any((targets < 0) | (targets >= scores.shape[1])): raise IndexError("类别标签越界") maximum = scores.max(axis=1, keepdims=True) shifted = scores - maximum log_normalizer = np.log(np.exp(shifted).sum(axis=1, keepdims=True)) log_probability = shifted - log_normalizer probability = np.exp(log_probability) loss = -log_probability[np.arange(len(scores)), targets] return loss, float(loss.mean()), probability
减最大值后指数输入不大于 0。若允许空批次或零个类别,需要另行定义;本实现应将其视为非法输入。
对 \((1000,999)\),朴素
exp可能溢出,稳定实现先变为 \((0,-1)\);对 \((-1000,-1001)\) 同理。全部相等时概率应均匀;\(K=1\) 时概率为 1、损失和梯度为 0,这是没有分类选择的退化任务。大量类别测试检查正确类别轴和内存。按照 手算结果与可信实现对照 的原则,与可信库比较每样本损失、平均损失和梯度,并使用与数据类型匹配的容差。索引越界、
NaN、无类别或空批次应报错。朴素实现失败是数值算法问题,不是 Softmax 数学定义失效。三种算法接收同一随机数组,固定种子集合、维度、数值范围、数据类型、预先运行次数和调用次数。使用高精度稳定结果,或按照 手算结果与可信实现对照 的原则选择可信库作为参照,报告有限结果比例、损失与概率误差、固定批量时间、峰值内存和指数/对数调用次数。该任务没有训练阶段,应明确训练时间为“不适用”,模型前向调用次数为 0。
直接
exp在大正数时溢出,在极小真实类概率上先 Softmax 再log可能得到 \(-\infty\);直接 log-softmax 避免这一步精度损失。减最大值后再 Softmax 虽能稳定概率,交叉熵仍优先直接从 log-softmax 取得。对每个 \(K\) 固定数据生成规则与训练/验证/测试划分、输入维度、批量、种子、优化器、更新次数和每次更新样本数;若类别集合代表不同任务,质量指标只在各任务内部解释。输出层参数量约为 \((d+1)K\),保存分数和概率的内存为 \(O(nK)\)。报告测试交叉熵或 top-1 准确率、训练时间、固定批量预测时间、峰值内存、参数量和前向/后向次数。
类别数增加会提高输出投影和归一化成本,也使不同任务的随机准确率基线变化;不能把不同 \(K\) 的准确率直接当成纯效率指标。计时应保持设备、批量和数据类型一致。
三个模型共享数据划分、预处理、种子集合、最大更新次数、早停和评价程序;模型超参数只用验证集选择。报告测试准确率、macro-F1、交叉熵、训练时间、固定批量预测时间、峰值内存、参数量和前向调用次数,并汇总多种子波动。
Softmax 回归是线性决策基线,隐藏层模型能表达非线性但成本更高且可能过拟合。参数量更大不保证质量更好;若训练更新预算相同但每步运算不同,应同时报告墙钟时间和实际调用次数。