逻辑回归名称里有回归,输出却是分类概率;损失函数也不能随手换成均方误差。本文从对数几率推导模型,用数值稳定的 Sigmoid 和交叉熵训练一个 JavaScript 二分类器,并逐一核对溢出、阈值、特征尺度和收敛边界。
误区一:名字决定任务
“逻辑回归是不是预测连续值?”这是名字制造的第一个陷阱。模型确实先计算线性函数 z=w·x+b,但随后通过 Sigmoid 把它映射到零到一,并用阈值完成分类。真正需要警惕的反而是实现细节:z 绝对值很大时直接计算 exp(-z) 可能溢出,损失里直接取 log§ 也会遇到 log(0)。
概率来自对数几率
模型假设正类概率 p 的对数几率 log(p/(1-p)) 与特征线性相关。把等式反解,就得到 p=1/(1+exp(-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/(1+exp(-z)),在 z<0 时改写为 exp(z)/(1+exp(z)),避免计算巨大 exp(-z)。计算损失时把概率夹在 eps 与 1-eps 之间只用于日志,梯度仍使用真实概率,避免人为改变优化方向。
示例训练一个一维阈值数据集:0、1 属于负类,2、3 属于正类。train 同时学习 w 和 b,每五百轮记录损失;predictProba 与 predict 分开,提醒概率与标签是两层接口。训练后四个样本应全部分类正确,损失应低于初始值。额外断言用 z=1000 和 z=-1000 检查 Sigmoid 不产生 NaN 或无穷。
从零训练的完整程序
functionsigmoid(z){if(z>=0)return1/(1+Math.exp(-z));conste=Math.exp(z);returne/(1+e);}functiontrain(xs,ys,learningRate=0.2,epochs=3000){if(xs.length===0||xs.length!==ys.length)thrownewError("invalid data");letw=0,b=0;constlosses=[];for(letepoch=0;epoch<epochs;epoch++){letdw=0,db=0,loss=0;for(leti=0;i<xs.length;i++){constp=sigmoid(w*xs[i]+b);dw+=(p-ys[i])*xs[i];db+=p-ys[i];constsafe=Math.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%500===0||epoch===epochs-1)losses.push(loss/xs.length);}return{w,b,losses};}constxs=[0,1,2,3],ys=[0,0,1,1];constmodel=train(xs,ys);constprobabilities=xs.map(x=>sigmoid(model.w*x+model.b));constpredicted=probabilities.map(p=>p>=0.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、匹配的交叉熵梯度和合理特征尺度比背公式更重要。能跑出准确率只是起点,概率是否有限、损失是否下降、阈值是否符合代价才是完整验证。