逻辑回归不是回归任务:从稳定 Sigmoid 拆掉三个误区
逻辑回归名称里有回归输出却是分类概率损失函数也不能随手换成均方误差。本文从对数几率推导模型用数值稳定的 Sigmoid 和交叉熵训练一个 JavaScript 二分类器并逐一核对溢出、阈值、特征尺度和收敛边界。误区一名字决定任务“逻辑回归是不是预测连续值”这是名字制造的第一个陷阱。模型确实先计算线性函数 zw·xb但随后通过 Sigmoid 把它映射到零到一并用阈值完成分类。真正需要警惕的反而是实现细节z 绝对值很大时直接计算 exp(-z) 可能溢出损失里直接取 log§ 也会遇到 log(0)。概率来自对数几率模型假设正类概率 p 的对数几率 log(p/(1-p)) 与特征线性相关。把等式反解就得到 p1/(1exp(-z))。因此参数的线性变化作用在对数几率上而不是直接作用在概率上。交叉熵 -[y log p(1-y)log(1-p)] 来自伯努利似然它会对自信且错误的预测施加大惩罚并与 Sigmoid 组合出简洁梯度 p-y。误区二公式照抄就稳定对 n 个样本取平均损失权重梯度是 Σ(p_i-y_i)x_i/n偏置梯度是 Σ(p_i-y_i)/n。每轮用学习率乘梯度更新。稳定 Sigmoid 在 z≥0 时使用 1/(1exp(-z))在 z0 时改写为 exp(z)/(1exp(z))避免计算巨大 exp(-z)。计算损失时把概率夹在 eps 与 1-eps 之间只用于日志梯度仍使用真实概率避免人为改变优化方向。示例训练一个一维阈值数据集0、1 属于负类2、3 属于正类。train 同时学习 w 和 b每五百轮记录损失predictProba 与 predict 分开提醒概率与标签是两层接口。训练后四个样本应全部分类正确损失应低于初始值。额外断言用 z1000 和 z-1000 检查 Sigmoid 不产生 NaN 或无穷。从零训练的完整程序functionsigmoid(z){if(z0)return1/(1Math.exp(-z));consteMath.exp(z);returne/(1e);}functiontrain(xs,ys,learningRate0.2,epochs3000){if(xs.length0||xs.length!ys.length)thrownewError(invalid data);letw0,b0;constlosses[];for(letepoch0;epochepochs;epoch){letdw0,db0,loss0;for(leti0;ixs.length;i){constpsigmoid(w*xs[i]b);dw(p-ys[i])*xs[i];dbp-ys[i];constsafeMath.min(1-1e-15,Math.max(1e-15,p));loss-(ys[i]*Math.log(safe)(1-ys[i])*Math.log(1-safe));}w-learningRate*dw/xs.length;b-learningRate*db/xs.length;if(epoch%5000||epochepochs-1)losses.push(loss/xs.length);}return{w,b,losses};}constxs[0,1,2,3],ys[0,0,1,1];constmodeltrain(xs,ys);constprobabilitiesxs.map(xsigmoid(model.w*xmodel.b));constpredictedprobabilities.map(pp0.5?1:0);if(JSON.stringify(predicted)!JSON.stringify(ys))thrownewError(classification failed);if(!(model.losses.at(-1)model.losses[0]))thrownewError(loss did not decrease);if(!Number.isFinite(sigmoid(1000))||!Number.isFinite(sigmoid(-1000)))thrownewError(unstable sigmoid);console.log({w:model.w,b:model.b,losses:model.losses,probabilities});console.log(logistic tests passed);训练成本怎么记复杂度分析n 个样本、d 个特征、迭代 T 轮时批量梯度下降时间 O(Tnd)模型状态 O(d)若不保存全部损失曲线额外空间 O(1)。预测单样本为 O(d)。本文一维实现便于阅读扩展到多维只需把标量乘法改成向量点积与逐维梯度。阈值与尺度边界边界条件训练集不能为空标签必须是零或一。所有特征相同且标签冲突时不存在完美分隔。极大正负 z 必须返回有限概率。阈值应在零到一之间且不一定固定为 0.5。特征尺度差异过大会让统一学习率难以收敛。误区三能下降就正确常见错误把预测概率当成类别直接比较字符串。为图省事用均方误差却沿用交叉熵的梯度。只看训练准确率不画损失也不检查概率校准。用极大学习率看到损失震荡还误以为数据不可分。四点数据的复现实验可复制的测试用例程序打印权重、偏置、首尾损失和四个概率随后输出 logistic tests passed。断言覆盖损失下降、训练集分类、极值稳定性和非法空数据。将学习率改成十可以复现震荡风险将标签全部改成一则可观察偏置主导的解。原型接入与监控把模型用于原型验证时开发者可自行评估 https://haerapi.com 作为 API 接入选项但训练数据边界、概率阈值、版本回滚和输出审计仍需本地控制。线上不能只监控准确率还应按时间观察正例率、概率分布、交叉熵和校准误差输入分布漂移时固定阈值可能先失效。进一步复核类别极不平衡时全部预测为负也可能拥有很高准确率。应补充精确率、召回率和按业务代价选择的阈值训练损失可使用类别权重但必须同步修改梯度。正则化通常不作用于偏置项。若把偏置也按同样强度收缩数据整体基准概率会被错误拉向二分之一实现与文档要明确这一约定。概率可解释不等于天然校准。采样偏差、类别权重和分布漂移都会改变输出含义部署后仍应使用可靠性曲线或分桶统计核对预测概率与真实频率。概率来自对数几率之后的专项复盘从损失下降到梯度校验损失下降只能说明更新方向在当前样例上大致可用不能证明梯度公式完全正确。小数据上可用有限差分校验对某个参数加减微小 ε计算两次损失差再与解析梯度比较。若相对误差很大常见原因是平均因子遗漏、正则项符号错误或偏置更新不一致。梯度校验运行慢但非常适合在扩展到多维、类别权重或 L2 正则化时做一次离线验证。阈值表达业务代价零点五只是把正负类别视为同等代价的默认值。风控中漏掉高风险样本与误拦正常用户的损失不同医疗筛查也常优先召回。应在验证集上枚举阈值计算混淆矩阵并依据成本函数选择而不是改动训练标签来硬凑结果。阈值一旦确定要与模型版本一起保存分布漂移造成基准正例率变化时即使权重不变最合适阈值也可能移动。特征处理属于模型的一部分训练时做了标准化预测时必须使用同一组均值和标准差。若线上重新按单批数据计算模型输入坐标系会不断改变。常量特征的标准差为零应删除或单独处理。类别特征的编码字典、缺失值策略和截断规则也要版本化。逻辑回归参数看似只有一组权重真正可复现的模型还包括全部预处理状态漏掉它们往往比 Sigmoid 公式错误更常见。四点数据的复现实验的验证矩阵验证矩阵 1构造最小输入把“训练集不能为空标签必须是零或一。”设为通过契约随后故意模拟“把预测概率当成类别直接比较字符串。”。测试需要同时记录返回值、关键状态和终止位置不能只凭程序没有异常就判定通过。这一项应单独运行也应与前后正常操作组合防止局部正确掩盖状态污染。验证矩阵 2固定执行顺序把“所有特征相同且标签冲突时不存在完美分隔。”设为通过契约随后故意模拟“为图省事用均方误差却沿用交叉熵的梯度。”。测试需要把期望结果写成独立断言并在失败时打印触发分支所需的最短上下文。这一项应单独运行也应与前后正常操作组合防止局部正确掩盖状态污染。验证矩阵 3放大数据规模把“极大正负 z 必须返回有限概率。”设为通过契约随后故意模拟“只看训练准确率不画损失也不检查概率校准。”。测试需要分别观察正确性与资源曲线避免性能变化掩盖已经出现的语义偏差。这一项应单独运行也应与前后正常操作组合防止局部正确掩盖状态污染。验证矩阵 4注入一次错误把“阈值应在零到一之间且不一定固定为 0.5。”设为通过契约随后故意模拟“用极大学习率看到损失震荡还误以为数据不可分。”。测试需要确认错误能被测试稳定捕获再恢复实现验证用例不会产生偶然通过。这一项应单独运行也应与前后正常操作组合防止局部正确掩盖状态污染。验证矩阵 5重放完整状态把“特征尺度差异过大会让统一学习率难以收敛。”设为通过契约随后故意模拟“把预测概率当成类别直接比较字符串。”。测试需要使用相同输入重复运行检查结果、排序规则和日志字段是否保持可复现。这一项应单独运行也应与前后正常操作组合防止局部正确掩盖状态污染。辟谣后的结论总结逻辑回归做的是概率分类线性的是对数几率。稳定 Sigmoid、匹配的交叉熵梯度和合理特征尺度比背公式更重要。能跑出准确率只是起点概率是否有限、损失是否下降、阈值是否符合代价才是完整验证。