fromscipy.statsimportchi2defhosmer_lemshow_test(y_true:pd.Series,y_pred:pd.Series,groups:int=10)->dict:data=pd.DataFrame({"y":y_true,"p":y_pred})data["bucket"]=pd.qcut(data["p"],groups,duplicates="drop")agg=data.groupby("bucket",observed=True).agg(n=("y","count"),observed=("y","sum"),expected=("p","sum"),)hl_stat=(((agg["observed"]-agg["expected"])**2)/(agg["expected"]*(1-agg["expected"]/agg["n"]))).sum()dof=len(agg)-2p_value=1-chi2.cdf(hl_stat,dof)return{"HL_statistic":round(hl_stat,4),"p_value":round(p_value,6),"calibrated":p_value>=0.05,# True = well calibrated"dof":dof,"groups_used":len(agg),}
p-value
判读
≥ 0.05
🟢 模型校准良好,无显著证据表明预测概率偏离实际频率
< 0.05
🔴 拒绝原假设,模型存在显著校准偏差
Limitations
分组方式敏感:不同分箱数量/方法导致不同结果,10 等分是惯例但非最优
样本量敏感:大样本下即使微小偏差也能导致显著 p-value(实际影响可能很小)
掩盖子群体问题:整体通过 HL 检验不等于所有子群体都校准良好
序贯分组问题:qcut 在重复值多时可能合并箱子,需检查 groups_used
Alternatives
Brier Score:无需分组,对样本量稳健,但只能给出误差量级而非定位
Spiegelhalter's Z-test:基于 Brier Score 的统计检验
Reliability Curves:可视化诊断,比 HL 检验提供更多信息
Expected Calibration Error (ECE):量化平均校准误差,解释性更强
Model QA 中的应用
Model QA Specialist 将 HL 检验用于:
模型上线前验证:新模型上线必须通过 HL 检验(p ≥ 0.05)
定期监控:在 OOT 窗口上重复执行,监控校准随时间恶化趋势
子群体分层测试:在关键子群体(高风险/低风险/新客户)上分别执行
Champion-Challenger:对比 champion model vs challenger model 的 HL 结果