DefectRisk 在 JM1 历史留出评测中报告,审查约 30% 的模块可覆盖 71.26% 的已知缺陷。项目按相同审查容量比较模型,将重复特征向量分组后划分数据,并在最终评测前冻结模型和策略。
审查约 30% 的模块 → 覆盖 71.26% 的已知缺陷模块。
在对冻结的原始 Random Forest 进行的历史单次评估中,2,177 个模块里有 652 个被标记,其中包含 421 个有缺陷记录的模块中的 300 个。这衡量的是审查优先级排序的效果;它既不能说明审查者找出了每一个 bug,也不能验证后来经过校准的系统。
我搭建了评估流程,在相同审查容量下比较了不同模型,并交付了冻结的模型产物和可实际运行的推理 CLI。DefectRisk 通过估计风险,帮助团队决定从哪里开始审查。
团队无法对每个模块投入同样多的审查精力。真正有用的问题是:如果我们只能审查部分代码库,ML 能否让更多缺陷集中在这份审查队列里?
有缺陷的模块属于少数类。在 JM1 上,把所有模块都预测为无缺陷,就能获得约 80.65% 的准确率,却无法覆盖任何缺陷。因此,产品决策要看召回率、精确率和审查工作量。
项目使用 JM1 / OpenML 1053,version 1:共 10,885 个模块、21 项静态代码指标,有缺陷的模块约占 19.35%。
最初的数据审计发现,随机划分后的训练集和测试集之间,有 299 个完整特征向量重复出现。数据中存在重复样本,也存在标签冲突组:指标完全相同,缺陷记录却不同。因此,评估可能有一部分衡量的是模型识别重复样本的能力,而非泛化能力。
我在划分数据前,先按原始特征向量分组。标签不参与组的身份判定;在每一次训练集与测试集划分、验证集划分以及交叉验证划分中,相同的特征向量都留在同一侧。我保留了所有数据行,包括存在标签冲突的行,并在拟合前增加了重叠审计。缺失值填补、数据变换和类别权重都只从用于拟合的数据分区中学习。
开发过程依次经过 Logistic Regression 基线 → 类别加权 → 阈值与审查工作量分析 → Random Forest → HGB → XGBoost,期间进行了有限的嵌套调参、特征工程和集成模型比较。
最关键的决策,是在相同审查容量下比较模型,尤其关注约 30% 的容量,而不是依赖默认的 0.50 阈值。外层采用带分组的五折交叉验证估计效果;内层采用三折交叉验证选择树模型配置,选择时不参考外层折的评估结果。
在仅使用训练数据的模型选择研究中,审查工作量相近时,RF 覆盖了 1,685 个缺陷模块中的 953 个,而 balanced Logistic Regression 覆盖了 906 个。HGB、XGBoost、衍生特征和小型集成模型都没有带来足以替换 RF 的实质性提升。最终选中的加权森林包含 200 棵树,深度为 8,每个叶节点至少包含三个样本。这些 CV 比较与下面的历史评估结果是分开的。
审查风险最高的约三分之一模块,就能把约七成有缺陷记录的模块集中到审查队列里。
在对留出集进行唯一一次评估之前,模型和策略都已冻结。策略只根据分数和容量选择截断点,不参考测试集标签,并将分数相同的模块放在一起处理。
队列中还包含 352 个记录为无缺陷的模块,也就是假阳性。这是在为人工审查排定优先级,并非自动检测 bug。71.26% 是本次测试的召回率,不是准确率,不代表概率估计的正确程度,也不是预期的生产环境表现。
这个结果属于冻结的原始 RF,不能作为后来开发的校准模型产物或拒绝判断策略的最终验证。
随后,我仅使用训练数据,通过带分组的嵌套 CV 评估了 sigmoid 和 isotonic 校准。对于固定的 RF 配置,sigmoid 将 Brier score 从 0.18692 改善到 0.13863,而 AP 基本保持稳定(0.40703 → 0.40619)。Brier score 衡量概率估计的质量;解读时还要结合可靠性曲线,以及各个分数区间内的样本量。
校准改善了概率的可解释性,但没有产生新的预测信息。我尝试以 ≥90% 的精确率自动判定 HIGH,但没有找到样本量足以支撑这一判断的高分尾部区间。保守策略将 98.44% 的模块留在 UNCERTAIN:136 个 LOW、零个 HIGH,以及 8,572 个 UNCERTAIN。即使在 LOW 模块中,也有七个存在缺陷记录。
产品仍然定位于风险排序和人工审查优先级安排。DefectRisk 预测的是风险与排序,而不是确定性。历史测试集没有被重新用于校准或策略设计;后来的系统仍然需要一个真正独立的外部留出集来验证。
交付内容包括冻结的校准模型产物 defectrisk-rf-sigmoid-v1、有序特征 schema、依赖版本、训练指纹和校验和。CLI 会验证 CSV,并加载经过校验的模型产物,不进行训练:
python -m pip install -e .
defectrisk rank examples/modules.csv
请在 DefectRisk 仓库根目录运行,并使用记录的依赖版本。处理流程为:包含静态指标的 CSV → 经过校验的冻结模型产物 → 校准后的概率 → 按风险降序排列。模块标识符不进入模型;支持表格和 JSON 输出。
Schema 和校验和检查验证的是一致性与完整性,而非来源真实性。使用 Joblib 加载时,只能使用可信的本地模型产物。复现已保存的 CV 证据,既不会重新启用历史测试集,也不能独立验证最终模型产物。
JM1 是历史数据;特征仅包含静态指标。
标签与快照的来源信息并不完善;存在含义模糊或相互冲突的标签。
无法保证模型能够跨项目、语言或版本泛化。
校准与策略目前只有训练数据和 CV 层面的证据,需要在设置冻结后,使用新的独立外部留出集验证。
概率不能证明单个模块的确定性或因果关系;分数低也不能免除标准测试或审查。
现有证据表明,当前特征已经成为主要瓶颈,但这并不能证明存在数学意义上的信息上限。建议下一步投入更好的预测时可用数据:代码变动量、缺陷历史、代码归属、测试覆盖率、提交与变更历史,以及审查历史。这些信号尚未实现;要使用它们,需要明确的数据快照和标签对应的时间范围。
文章《当更好的模型也不够用时》(When Better Models Are Not Enough)解释了我如何决定停止探索更多模型。
GitHub 上的 DefectRisk:代码、模型产物与使用说明
Model card 与预期用途限制
原始 RF 的历史单次评估
后续仅使用训练数据的校准与不确定性研究
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。