项目六教学文档:信用违约风险综合建模 —— 高难度综合收官

项目六教学文档:信用违约风险综合建模 ------ 高难度综合收官

https://gitee.com/ghaweiuptgb/machine-learning-AI.git (代码包)

使用说明 :本文档是项目六的完整教学记录。每完成一步,我会把该步骤的详细教学过程写入对应章节。

讲解要求 :假设读者是零基础小白 ------每个机器学习名词都要用大白话解释;流程用 Mermaid 图;每步提供动手任务与验收标准参考答案


目录

步骤 标题 状态
[步骤 1](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 项目搭建与数据加载 ✅ 已完成教学
[步骤 2](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 深度 EDA ✅ 已完成教学
[步骤 3](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 三路划分与泄露检查 ✅ 已完成教学
[步骤 4](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 基线 Pipeline ✅ 已完成教学
[步骤 5](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 不平衡策略对比 ✅ 已完成教学
[步骤 6](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) K 折 CV 多模型 ✅ 已完成教学
[步骤 7](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) GridSearchCV ✅ 已完成教学
[步骤 8](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) Stacking 集成 ✅ 已完成教学
[步骤 9](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 业务阈值与成本 ✅ 已完成教学
[步骤 10](#步骤 标题 状态 步骤 1 项目搭建与数据加载 ✅ 已完成教学 步骤 2 深度 EDA ✅ 已完成教学 步骤 3 三路划分与泄露检查 ✅ 已完成教学 步骤 4 基线 Pipeline ✅ 已完成教学 步骤 5 不平衡策略对比 ✅ 已完成教学 步骤 6 K 折 CV 多模型 ✅ 已完成教学 步骤 7 GridSearchCV ✅ 已完成教学 步骤 8 Stacking 集成 ✅ 已完成教学 步骤 9 业务阈值与成本 ✅ 已完成教学 步骤 10 部署与复盘 ✅ 已完成教学) 部署与复盘 ✅ 已完成教学

步骤 1:项目搭建与数据加载

状态 :✅ 已完成教学(报告见 reports/step1_data_inspection.txt,图见 01_违约标签分布.png02_各列缺失值概况.png

1.1 本步目标

完成以下四件事,为后续 9 步打地基:

  1. 创建 project_06_credit_risk/ 标准目录(与项目 1~5 一致)
  2. 加载 Give Me Some Credit 数据集(约 15 万条客户记录)
  3. 理解 每一列的业务含义------尤其是标签 y 是什么
  4. 确认 数据规模、违约比例、缺失值概况,并保存中文图表与文本报告

做完本步,你应该能向别人讲清楚:我们在预测什么?数据长什么样?有什么明显问题(不平衡、缺失)?

1.2 这一步在解决什么问题?

1.2.1 从项目五到项目六:我们在学什么?

项目五学的是:把一段英文评论变成数字,再判断正面/负面(文本分类)。

项目六学的是:把一位客户的「信用历史数字」变成预测------他未来 2 年会不会严重还不上钱(表格 + 不平衡 + 风控)。

可以把它想象成银行柜台前的场景:
#mermaid-svg-7WG5u1uQj1q8LqG3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7WG5u1uQj1q8LqG3 .error-icon{fill:#552222;}#mermaid-svg-7WG5u1uQj1q8LqG3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7WG5u1uQj1q8LqG3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .marker.cross{stroke:#333333;}#mermaid-svg-7WG5u1uQj1q8LqG3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7WG5u1uQj1q8LqG3 p{margin:0;}#mermaid-svg-7WG5u1uQj1q8LqG3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster-label text{fill:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster-label span{color:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster-label span p{background-color:transparent;}#mermaid-svg-7WG5u1uQj1q8LqG3 .label text,#mermaid-svg-7WG5u1uQj1q8LqG3 span{fill:#333;color:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .node rect,#mermaid-svg-7WG5u1uQj1q8LqG3 .node circle,#mermaid-svg-7WG5u1uQj1q8LqG3 .node ellipse,#mermaid-svg-7WG5u1uQj1q8LqG3 .node polygon,#mermaid-svg-7WG5u1uQj1q8LqG3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .rough-node .label text,#mermaid-svg-7WG5u1uQj1q8LqG3 .node .label text,#mermaid-svg-7WG5u1uQj1q8LqG3 .image-shape .label,#mermaid-svg-7WG5u1uQj1q8LqG3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7WG5u1uQj1q8LqG3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .rough-node .label,#mermaid-svg-7WG5u1uQj1q8LqG3 .node .label,#mermaid-svg-7WG5u1uQj1q8LqG3 .image-shape .label,#mermaid-svg-7WG5u1uQj1q8LqG3 .icon-shape .label{text-align:center;}#mermaid-svg-7WG5u1uQj1q8LqG3 .node.clickable{cursor:pointer;}#mermaid-svg-7WG5u1uQj1q8LqG3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .arrowheadPath{fill:#333333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7WG5u1uQj1q8LqG3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7WG5u1uQj1q8LqG3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7WG5u1uQj1q8LqG3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster text{fill:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 .cluster span{color:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7WG5u1uQj1q8LqG3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7WG5u1uQj1q8LqG3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7WG5u1uQj1q8LqG3 .icon-shape,#mermaid-svg-7WG5u1uQj1q8LqG3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7WG5u1uQj1q8LqG3 .icon-shape p,#mermaid-svg-7WG5u1uQj1q8LqG3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7WG5u1uQj1q8LqG3 .icon-shape .label rect,#mermaid-svg-7WG5u1uQj1q8LqG3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7WG5u1uQj1q8LqG3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7WG5u1uQj1q8LqG3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7WG5u1uQj1q8LqG3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 预测 不会
预测 会
客户申请贷款/信用卡
银行查看历史记录

年龄、收入、逾期次数...
机器学习模型打分
未来 2 年会严重逾期吗?
可能批准
可能拒绝或提高利率

本步还不训练模型。在写任何算法之前,必须先回答三个问题:

问题 本步要得到什么
数据从哪来? Kaggle 2011 竞赛官方训练集,15 万行
要预测什么? SeriousDlqin2yrs:0=不违约,1=严重逾期
有什么坑? 违约只占 6.68% (不平衡);月收入 约 20% 缺失
1.2.2 为什么不能跳过「加载与理解」?

机器学习里有一句老话:Garbage in, garbage out(垃圾进,垃圾出)。

如果搞错目标列、没发现缺失值、不知道正负样本比例,后面再高级的 Pipeline、Stacking 都会建立在错误前提上。项目六比前五个项目难, partly 就是因为步骤更多、更容易在某一处「悄悄泄露或搞错数据」------所以从步骤 1 就要养成「先看数据再写模型」的习惯。

1.3 本步在整体流程中的位置

#mermaid-svg-j4xlRcOgqPXoY03P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-j4xlRcOgqPXoY03P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-j4xlRcOgqPXoY03P .error-icon{fill:#552222;}#mermaid-svg-j4xlRcOgqPXoY03P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-j4xlRcOgqPXoY03P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-j4xlRcOgqPXoY03P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-j4xlRcOgqPXoY03P .marker.cross{stroke:#333333;}#mermaid-svg-j4xlRcOgqPXoY03P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-j4xlRcOgqPXoY03P p{margin:0;}#mermaid-svg-j4xlRcOgqPXoY03P .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-j4xlRcOgqPXoY03P .cluster-label text{fill:#333;}#mermaid-svg-j4xlRcOgqPXoY03P .cluster-label span{color:#333;}#mermaid-svg-j4xlRcOgqPXoY03P .cluster-label span p{background-color:transparent;}#mermaid-svg-j4xlRcOgqPXoY03P .label text,#mermaid-svg-j4xlRcOgqPXoY03P span{fill:#333;color:#333;}#mermaid-svg-j4xlRcOgqPXoY03P .node rect,#mermaid-svg-j4xlRcOgqPXoY03P .node circle,#mermaid-svg-j4xlRcOgqPXoY03P .node ellipse,#mermaid-svg-j4xlRcOgqPXoY03P .node polygon,#mermaid-svg-j4xlRcOgqPXoY03P .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-j4xlRcOgqPXoY03P .rough-node .label text,#mermaid-svg-j4xlRcOgqPXoY03P .node .label text,#mermaid-svg-j4xlRcOgqPXoY03P .image-shape .label,#mermaid-svg-j4xlRcOgqPXoY03P .icon-shape .label{text-anchor:middle;}#mermaid-svg-j4xlRcOgqPXoY03P .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-j4xlRcOgqPXoY03P .rough-node .label,#mermaid-svg-j4xlRcOgqPXoY03P .node .label,#mermaid-svg-j4xlRcOgqPXoY03P .image-shape .label,#mermaid-svg-j4xlRcOgqPXoY03P .icon-shape .label{text-align:center;}#mermaid-svg-j4xlRcOgqPXoY03P .node.clickable{cursor:pointer;}#mermaid-svg-j4xlRcOgqPXoY03P .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-j4xlRcOgqPXoY03P .arrowheadPath{fill:#333333;}#mermaid-svg-j4xlRcOgqPXoY03P .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-j4xlRcOgqPXoY03P .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-j4xlRcOgqPXoY03P .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j4xlRcOgqPXoY03P .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-j4xlRcOgqPXoY03P .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j4xlRcOgqPXoY03P .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-j4xlRcOgqPXoY03P .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-j4xlRcOgqPXoY03P .cluster text{fill:#333;}#mermaid-svg-j4xlRcOgqPXoY03P .cluster span{color:#333;}#mermaid-svg-j4xlRcOgqPXoY03P div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-j4xlRcOgqPXoY03P .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-j4xlRcOgqPXoY03P rect.text{fill:none;stroke-width:0;}#mermaid-svg-j4xlRcOgqPXoY03P .icon-shape,#mermaid-svg-j4xlRcOgqPXoY03P .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j4xlRcOgqPXoY03P .icon-shape p,#mermaid-svg-j4xlRcOgqPXoY03P .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-j4xlRcOgqPXoY03P .icon-shape .label rect,#mermaid-svg-j4xlRcOgqPXoY03P .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j4xlRcOgqPXoY03P .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-j4xlRcOgqPXoY03P .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-j4xlRcOgqPXoY03P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤1 加载理解

← 你在这里
步骤2 深度EDA
步骤3 三路划分
步骤4~9 建模调参
步骤10 交付复盘

1.4 名词解释(本步必须搞懂)

名词 大白话 为什么本步需要 在本项目中 易混淆
监督学习 有「标准答案标签 y」的学习 我们有 SeriousDlqin2yrs 这列历史结果 项目六仍是监督学习 项目四聚类没有 y
二分类 标签只有两类(0/1) 违约 vs 不违约 0=好,1=坏 不是多分类(如流失 Yes/No 也是二分类)
标签 y / 目标变量 模型要预测的那一列 必须明确哪一列是 y SeriousDlqin2yrs 不要把 MonthlyIncome 当 y
特征 X 用来预测 y 的输入列 10 个数值特征 年龄、逾期次数、负债率等 不含 y,不含无意义 Id
SeriousDlqin2yrs 未来 2 年是否「严重逾期」 竞赛官方定义的标签 1 约 10,026 人,0 约 139,974 「严重」= 逾期 ≥90 天
不平衡数据 两类样本数量差很多 93% vs 7%,模型容易「全猜不违约」也看似很准 步骤 5 专门处理 项目二更极端(0.17% 欺诈)
缺失值 表格里空着的格子 填错会误导模型 MonthlyIncome19.82% 步骤 4 Pipeline 里用 Imputer 填
EDA 探索性数据分析,先「看」再「建模」 步骤 2 会深入;步骤 1 做初览 本步看分布与缺失 不是正式建模
fetch_openml / CSV 两种下载数据的方式 脚本自动:先本地 CSV,再 GitHub,再 OpenML 保证你能一键跑通 与 Kaggle 手动下载等效
1.4.1 十个特征分别是什么意思?(通俗版)

可以把每一行想象成 一位曾借过钱的客户,各列描述其信用历史:

英文列名 中文理解 直觉
RevolvingUtilizationOfUnsecuredLines 信用卡「刷满程度」 已用额度 ÷ 总信用额度,越高越「紧」
age 年龄
NumberOfTime30-59DaysPastDueNotWorse 轻度逾期次数 过去 2 年曾晚还 30~59 天几次
DebtRatio 负债率 月还债 ÷ 月收入,越高压力越大
MonthlyIncome 月收入 美元;很多人没填
NumberOfOpenCreditLinesAndLoans 信贷账户数 信用卡 + 贷款一共几笔
NumberOfTimes90DaysLate 严重逾期次数 晚还 ≥90 天几次------强危险信号
NumberRealEstateLoansOrLines 房贷笔数 房子相关贷款
NumberOfTime60-89DaysPastDueNotWorse 中度逾期次数 晚还 60~89 天几次
NumberOfDependents 家属人数 要养几口人;少量缺失

1.5 项目目录结构(与前几项目一致)

复制代码
project_06_credit_risk/
├── 教学文档.md          # 本文件
├── README.md            # 你的学习笔记
├── data/
│   └── cs-training.csv  # 本地缓存(约 15MB,Git 不提交)
├── src/
│   └── train.py         # 主脚本(本步已可运行)
├── reports/
│   ├── step1_data_inspection.txt
│   └── figures/
│       ├── 01_违约标签分布.png
│       └── 02_各列缺失值概况.png
└── models/              # 步骤 10 保存 pkl

1.6 如何运行本步

在项目根目录下执行:

bash 复制代码
cd project_06_credit_risk
python src/train.py

首次运行 :若 data/cs-training.csv 不存在,脚本会从 GitHub 镜像下载(需网络;失败则自动用 OpenML)。

1.7 代码解读(按执行顺序)

1.7.1 load_data() ------ 三层 fallback 加载

#mermaid-svg-3IeridtDBLm1n3Gt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3IeridtDBLm1n3Gt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3IeridtDBLm1n3Gt .error-icon{fill:#552222;}#mermaid-svg-3IeridtDBLm1n3Gt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3IeridtDBLm1n3Gt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3IeridtDBLm1n3Gt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3IeridtDBLm1n3Gt .marker.cross{stroke:#333333;}#mermaid-svg-3IeridtDBLm1n3Gt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3IeridtDBLm1n3Gt p{margin:0;}#mermaid-svg-3IeridtDBLm1n3Gt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3IeridtDBLm1n3Gt .cluster-label text{fill:#333;}#mermaid-svg-3IeridtDBLm1n3Gt .cluster-label span{color:#333;}#mermaid-svg-3IeridtDBLm1n3Gt .cluster-label span p{background-color:transparent;}#mermaid-svg-3IeridtDBLm1n3Gt .label text,#mermaid-svg-3IeridtDBLm1n3Gt span{fill:#333;color:#333;}#mermaid-svg-3IeridtDBLm1n3Gt .node rect,#mermaid-svg-3IeridtDBLm1n3Gt .node circle,#mermaid-svg-3IeridtDBLm1n3Gt .node ellipse,#mermaid-svg-3IeridtDBLm1n3Gt .node polygon,#mermaid-svg-3IeridtDBLm1n3Gt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3IeridtDBLm1n3Gt .rough-node .label text,#mermaid-svg-3IeridtDBLm1n3Gt .node .label text,#mermaid-svg-3IeridtDBLm1n3Gt .image-shape .label,#mermaid-svg-3IeridtDBLm1n3Gt .icon-shape .label{text-anchor:middle;}#mermaid-svg-3IeridtDBLm1n3Gt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3IeridtDBLm1n3Gt .rough-node .label,#mermaid-svg-3IeridtDBLm1n3Gt .node .label,#mermaid-svg-3IeridtDBLm1n3Gt .image-shape .label,#mermaid-svg-3IeridtDBLm1n3Gt .icon-shape .label{text-align:center;}#mermaid-svg-3IeridtDBLm1n3Gt .node.clickable{cursor:pointer;}#mermaid-svg-3IeridtDBLm1n3Gt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3IeridtDBLm1n3Gt .arrowheadPath{fill:#333333;}#mermaid-svg-3IeridtDBLm1n3Gt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3IeridtDBLm1n3Gt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3IeridtDBLm1n3Gt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3IeridtDBLm1n3Gt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3IeridtDBLm1n3Gt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3IeridtDBLm1n3Gt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3IeridtDBLm1n3Gt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3IeridtDBLm1n3Gt .cluster text{fill:#333;}#mermaid-svg-3IeridtDBLm1n3Gt .cluster span{color:#333;}#mermaid-svg-3IeridtDBLm1n3Gt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3IeridtDBLm1n3Gt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3IeridtDBLm1n3Gt rect.text{fill:none;stroke-width:0;}#mermaid-svg-3IeridtDBLm1n3Gt .icon-shape,#mermaid-svg-3IeridtDBLm1n3Gt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3IeridtDBLm1n3Gt .icon-shape p,#mermaid-svg-3IeridtDBLm1n3Gt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3IeridtDBLm1n3Gt .icon-shape .label rect,#mermaid-svg-3IeridtDBLm1n3Gt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3IeridtDBLm1n3Gt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3IeridtDBLm1n3Gt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3IeridtDBLm1n3Gt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



load_data 开始
本地有 cs-training.csv?
read_csv
尝试 GitHub 下载
成功?
fetch_openml GiveMeSomeCredit
保存本地 CSV
_clean_raw_dataframe

去索引列 + y 变 0/1
返回 DataFrame

  • _clean_raw_dataframe :去掉 CSV 里多余的 Unnamed: 0 行号列;把目标统一成 0/1 整数(兼容 OpenML 的 Yes/No)。
  • 为何缓存到本地? 15 万行下载一次即可,之后秒开------与项目 2~5 做法相同。

把 Kaggle 英文列名翻译成业务语言 。做风控项目时,先懂业务再懂算法 ------否则不知道 NumberOfTimes90DaysLate 为何重要。

1.7.3 visualize_label_distribution() ------ 违约分布图

用柱状图直观看到:蓝色柱(不违约)远高于红色柱(违约)。这就是「不平衡」的第一眼证据。

1.7.4 save_step1_inspection_report() ------ 文本报告

把关键数字写入 reports/step1_data_inspection.txt,方便复习和对照验收。

1.8 实验结果(你的环境)

指标 数值
客户总数 150,000
特征数 10(不含目标列)
标签 0(不违约) 139,974(93.32%)
标签 1(严重逾期) 10,026(6.68%)
MonthlyIncome 缺失 29,731(19.82%)
NumberOfDependents 缺失 3,924(2.62%)
年龄中位数 52 岁
月收入中位数(非空) 约 5,400 美元

读图提示:

  • 01_违约标签分布.png:红色柱只有蓝色柱的大约 1/14------坏客户是少数。
  • 02_各列缺失值概况.png月收入缺失最多,步骤 4 以后要在 Pipeline 里专门处理。

1.9 输出文件

文件 说明
reports/step1_data_inspection.txt 数据检查报告
reports/figures/01_违约标签分布.png 0/1 样本数量
reports/figures/02_各列缺失值概况.png 缺失值条形图
data/cs-training.csv 本地缓存(首次运行后生成)

1.10 动手任务

任务 A :运行 python src/train.py,目标列 0 和 1 各有多少条?违约率是多少?

任务 B:哪两列有缺失?各缺多少比例?

任务 C:用一句话向非技术人员解释:「项目六要预测什么?」

任务 D:若模型「全部预测为 0(不违约)」,Accuracy 大约是多少?这样模型有用吗?

任务 E :打开 step1_data_inspection.txt,找到「NumberOfTimes90DaysLate」的含义------为什么它可能是强风险特征?

1.11 验收标准

  • 能说出数据集行数、特征数、目标列名
  • 能解释 SeriousDlqin2yrs 的业务含义(90 天严重逾期)
  • 能说出正类(违约)比例约 6~7%
  • 能指出 MonthlyIncome 有大量缺失
  • 知道本步只「看数据」,尚未训练模型
  • 成功生成 2 张中文 PNG 与 1 份 txt 报告

1.12 动手任务与验收标准参考答案

以下为教学标准答案,便于自学对照;README.md 学习记录仍建议用自己的话写。

动手任务答案
任务 参考答案
A 0(不违约)139,974 ;1(严重逾期)10,026 ;违约率 6.68%
B MonthlyIncome29,731(19.82%)NumberOfDependents3,924(2.62%)
C 示例:「根据客户过去的信用行为(年龄、收入、逾期记录等),预测他未来 2 年会不会出现超过 90 天还不上的严重逾期。」
D Accuracy 约 93.32% (因为 93% 本来就是 0),但完全没用------一个违约都抓不到,银行会亏大钱。所以不能只看 Accuracy(步骤 5 会学 F1、Recall)。
E 表示过去 2 年「逾期 ≥90 天」的次数;次数越多说明还款记录越差,与未来违约高度相关。
验收标准答案
验收项 参考答案
行数与特征 150,000 行;10 个特征 + 1 个目标列。
目标含义 未来 2 年是否严重逾期(≥90 天);0=否,1=是。
正类比例 6.68% ,属于不平衡分类。
缺失列 主要是 MonthlyIncome (约 20%)和 NumberOfDependents(约 2.6%)。
本步边界 只加载与初探; 划分 train/test,训练模型。
输出物 step1_data_inspection.txt + 2 张 figures 中文图。

1.13 常见问题

Q1:这和项目二「信用卡欺诈」有什么区别?

项目二 欺诈 项目六 信用违约
正类比例 0.17%(极少) 6.68%(少但没那么极端)
特征 匿名 V1~V28 有业务含义的 10 列
难度侧重 SMOTE + 阈值 + CV + GridSearch + Stacking 全流程

Q2:OpenML 的列名和 Kaggle 略有不同?

OpenML 目标列曾叫 FinancialDistressNextTwoYears,脚本已统一重命名为 Kaggle 常用的 SeriousDlqin2yrs,含义相同。

Q3:为什么删除 Unnamed: 0

GitHub 上的 CSV 有时带 pandas 导出的行号列,不是客户特征,参与建模会引入无意义噪声。

Q4:步骤 1 就要处理缺失值吗?

本步只统计 缺失;从步骤 4 起在 Pipeline 的 SimpleImputer 里系统处理,并保证不对测试集「偷看」统计量(防泄露)。

1.14 本步小结

要点 内容
数据集 Give Me Some Credit,150,000
任务类型 监督学习 · 二分类 · 不平衡
目标 y SeriousDlqin2yrs(0/1)
特征 X 10 个数值列
违约率 6.68%
主要缺失 MonthlyIncome ~20%
本步产出 2 图 + 1 报告 + 本地 CSV 缓存
下一步 步骤 2:深度 EDA(分布、相关性、异常值)

1.15 学习记录(请你填写)

  • 完成日期
  • 我用一句话描述项目六在做什么
  • 我最意外的发现(如:违约人这么少 / 收入缺这么多):
  • 疑问(如有):

文档版本:v1.0 | 最后更新:步骤 1 项目搭建与数据加载教学已写入


步骤 2:深度 EDA

状态 :✅ 已完成教学(报告见 reports/step2_eda.txt,图见 0306_*.png

2.1 本步目标

在步骤 1「知道有哪些列、多少缺失」的基础上,本步要深入看数据在说什么

  1. 哪些特征与违约关系最强?(相关性排序)
  2. 违约的人不违约的人,关键特征长什么样?(箱线图对比)
  3. 有没有异常值 / 哨兵值(age=0、使用率>1、逾期次数=98)?
  4. 总结至少 5 条 EDA 发现,写入报告------这些发现将指导步骤 3~5 的划分、Imputer、不平衡处理

本步仍然不训练模型。EDA 是「用眼睛和统计把问题摸清楚」,避免后面建模走弯路。

2.2 这一步在解决什么问题?

步骤 1 像「打开快递看清单」:15 万行、10 列、违约率 6.68%。

步骤 2 像「拆开试用」:

  • 哪些特征最值得放进模型?(不是 10 个都同等重要)
  • 违约客户有没有共同模式?(比如逾期次数明显更高)
  • 数据有没有脏点?(录入错误、特殊编码)

#mermaid-svg-67KELyecMTHs0iSF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-67KELyecMTHs0iSF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-67KELyecMTHs0iSF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-67KELyecMTHs0iSF .error-icon{fill:#552222;}#mermaid-svg-67KELyecMTHs0iSF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-67KELyecMTHs0iSF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-67KELyecMTHs0iSF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-67KELyecMTHs0iSF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-67KELyecMTHs0iSF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-67KELyecMTHs0iSF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-67KELyecMTHs0iSF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-67KELyecMTHs0iSF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-67KELyecMTHs0iSF .marker.cross{stroke:#333333;}#mermaid-svg-67KELyecMTHs0iSF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-67KELyecMTHs0iSF p{margin:0;}#mermaid-svg-67KELyecMTHs0iSF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-67KELyecMTHs0iSF .cluster-label text{fill:#333;}#mermaid-svg-67KELyecMTHs0iSF .cluster-label span{color:#333;}#mermaid-svg-67KELyecMTHs0iSF .cluster-label span p{background-color:transparent;}#mermaid-svg-67KELyecMTHs0iSF .label text,#mermaid-svg-67KELyecMTHs0iSF span{fill:#333;color:#333;}#mermaid-svg-67KELyecMTHs0iSF .node rect,#mermaid-svg-67KELyecMTHs0iSF .node circle,#mermaid-svg-67KELyecMTHs0iSF .node ellipse,#mermaid-svg-67KELyecMTHs0iSF .node polygon,#mermaid-svg-67KELyecMTHs0iSF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-67KELyecMTHs0iSF .rough-node .label text,#mermaid-svg-67KELyecMTHs0iSF .node .label text,#mermaid-svg-67KELyecMTHs0iSF .image-shape .label,#mermaid-svg-67KELyecMTHs0iSF .icon-shape .label{text-anchor:middle;}#mermaid-svg-67KELyecMTHs0iSF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-67KELyecMTHs0iSF .rough-node .label,#mermaid-svg-67KELyecMTHs0iSF .node .label,#mermaid-svg-67KELyecMTHs0iSF .image-shape .label,#mermaid-svg-67KELyecMTHs0iSF .icon-shape .label{text-align:center;}#mermaid-svg-67KELyecMTHs0iSF .node.clickable{cursor:pointer;}#mermaid-svg-67KELyecMTHs0iSF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-67KELyecMTHs0iSF .arrowheadPath{fill:#333333;}#mermaid-svg-67KELyecMTHs0iSF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-67KELyecMTHs0iSF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-67KELyecMTHs0iSF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-67KELyecMTHs0iSF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-67KELyecMTHs0iSF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-67KELyecMTHs0iSF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-67KELyecMTHs0iSF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-67KELyecMTHs0iSF .cluster text{fill:#333;}#mermaid-svg-67KELyecMTHs0iSF .cluster span{color:#333;}#mermaid-svg-67KELyecMTHs0iSF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-67KELyecMTHs0iSF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-67KELyecMTHs0iSF rect.text{fill:none;stroke-width:0;}#mermaid-svg-67KELyecMTHs0iSF .icon-shape,#mermaid-svg-67KELyecMTHs0iSF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-67KELyecMTHs0iSF .icon-shape p,#mermaid-svg-67KELyecMTHs0iSF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-67KELyecMTHs0iSF .icon-shape .label rect,#mermaid-svg-67KELyecMTHs0iSF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-67KELyecMTHs0iSF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-67KELyecMTHs0iSF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-67KELyecMTHs0iSF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤1

知道有什么列
步骤2 深度EDA

知道哪些有用/有问题
步骤3 划分数据
步骤4 Pipeline

知道要 Imputer 什么

若跳过 EDA 直接 fit() 模型,你可能:

  • 不知道 MonthlyIncome 缺 20%,Pipeline 漏了 Imputer;
  • 不知道 98 是哨兵值,模型被 264 条怪数据带偏;
  • 以为 Accuracy 93% 很好,其实模型啥也没学到。

2.3 名词解释(本步新术语)

名词 大白话 为什么本步需要 在本项目中 易混淆
EDA Exploratory Data Analysis,探索性数据分析 建模前「把数据摸透」 本步核心活动 不是正式训练
Pearson 相关系数 r 衡量两列线性一起变大/变小的程度,-1~1 看哪个特征与违约 0/1 最同步 Top1: 30-59天逾期 r=0.126 ≠ 因果关系
点二列相关 一列是 0/1 标签时,与数值特征的相关系数 目标 y 是 0/1,用 corrwith(y) 即可 与 Pearson 在此场景一致 不是分类专用神秘公式
正相关 r>0 特征越大,违约(1)越多 逾期次数多为正相关 30-59天逾期 +0.126
负相关 r<0 特征越大,违约越少 年龄 -0.115(略负,需结合业务) 不等于「年龄小一定违约」
箱线图 boxplot 看中位数、四分位、离群点;比只看均值更稳 对比违约/不违约两组分布 04_关键特征箱线图 不是直方图
异常值 outlier 明显不合理或极端的点 age=0 仅 1 步骤 3+ 决定是否删/截断
哨兵值 sentinel 用特殊数字表示「未知/缺失」(如 98) 逾期列 98264 不能当真实「逾期 98 次」 与 NaN 不同
右偏分布 大部分值很小,少数极大(拖长尾) 信用使用率、负债率 使用率>1 有 3321 对线性模型不友好
密度 histogram 纵轴是「占比密度」而非人数 月收入对比图 06_月收入分布
2.3.1 相关系数 r 怎么读?(小白必看)

NumberOfTimes90DaysLate 为例:

  • 不违约组均值 0.135(几乎没严重逾期过)
  • 违约组均值 2.091(平均约 2 次)

r=+0.117 表示:整体上「90 天逾期次数越多,越可能是违约客户」。但 r 只有 0.12 左右------说明单靠这一列不能完美分开,还需要组合其他特征 + 更好的模型。

重要 :相关性 因果。不能简单说「降低年龄就能降低违约」------年龄与违约的负相关可能混有其他因素。

2.4 EDA 流程(Mermaid)

#mermaid-svg-cppwAFN09SU9BqwI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cppwAFN09SU9BqwI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cppwAFN09SU9BqwI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cppwAFN09SU9BqwI .error-icon{fill:#552222;}#mermaid-svg-cppwAFN09SU9BqwI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cppwAFN09SU9BqwI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cppwAFN09SU9BqwI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cppwAFN09SU9BqwI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cppwAFN09SU9BqwI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cppwAFN09SU9BqwI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cppwAFN09SU9BqwI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cppwAFN09SU9BqwI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cppwAFN09SU9BqwI .marker.cross{stroke:#333333;}#mermaid-svg-cppwAFN09SU9BqwI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cppwAFN09SU9BqwI p{margin:0;}#mermaid-svg-cppwAFN09SU9BqwI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cppwAFN09SU9BqwI .cluster-label text{fill:#333;}#mermaid-svg-cppwAFN09SU9BqwI .cluster-label span{color:#333;}#mermaid-svg-cppwAFN09SU9BqwI .cluster-label span p{background-color:transparent;}#mermaid-svg-cppwAFN09SU9BqwI .label text,#mermaid-svg-cppwAFN09SU9BqwI span{fill:#333;color:#333;}#mermaid-svg-cppwAFN09SU9BqwI .node rect,#mermaid-svg-cppwAFN09SU9BqwI .node circle,#mermaid-svg-cppwAFN09SU9BqwI .node ellipse,#mermaid-svg-cppwAFN09SU9BqwI .node polygon,#mermaid-svg-cppwAFN09SU9BqwI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cppwAFN09SU9BqwI .rough-node .label text,#mermaid-svg-cppwAFN09SU9BqwI .node .label text,#mermaid-svg-cppwAFN09SU9BqwI .image-shape .label,#mermaid-svg-cppwAFN09SU9BqwI .icon-shape .label{text-anchor:middle;}#mermaid-svg-cppwAFN09SU9BqwI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cppwAFN09SU9BqwI .rough-node .label,#mermaid-svg-cppwAFN09SU9BqwI .node .label,#mermaid-svg-cppwAFN09SU9BqwI .image-shape .label,#mermaid-svg-cppwAFN09SU9BqwI .icon-shape .label{text-align:center;}#mermaid-svg-cppwAFN09SU9BqwI .node.clickable{cursor:pointer;}#mermaid-svg-cppwAFN09SU9BqwI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cppwAFN09SU9BqwI .arrowheadPath{fill:#333333;}#mermaid-svg-cppwAFN09SU9BqwI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cppwAFN09SU9BqwI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cppwAFN09SU9BqwI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cppwAFN09SU9BqwI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cppwAFN09SU9BqwI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cppwAFN09SU9BqwI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cppwAFN09SU9BqwI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cppwAFN09SU9BqwI .cluster text{fill:#333;}#mermaid-svg-cppwAFN09SU9BqwI .cluster span{color:#333;}#mermaid-svg-cppwAFN09SU9BqwI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cppwAFN09SU9BqwI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cppwAFN09SU9BqwI rect.text{fill:none;stroke-width:0;}#mermaid-svg-cppwAFN09SU9BqwI .icon-shape,#mermaid-svg-cppwAFN09SU9BqwI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cppwAFN09SU9BqwI .icon-shape p,#mermaid-svg-cppwAFN09SU9BqwI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cppwAFN09SU9BqwI .icon-shape .label rect,#mermaid-svg-cppwAFN09SU9BqwI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cppwAFN09SU9BqwI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cppwAFN09SU9BqwI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cppwAFN09SU9BqwI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 加载 15 万行数据
compute_correlation_with_target

各特征 vs 违约 r
detect_outlier_counts

age=0 使用率>1 等
build_eda_findings

至少 5 条结论
4 张中文图 + step2_eda.txt

2.5 代码解读

函数 作用
compute_correlation_with_target 对 10 个特征算与 y 的 Pearson r,按 |r| 排序
detect_outlier_counts 统计 age=0、使用率>1、逾期=98、DebtRatio>100 等
build_eda_findings 把数字变成 6 条可读结论
visualize_correlation_top10 水平条形图,红=正相关、蓝=负相关
visualize_key_features_boxplot 三组关键特征的违约 vs 不违约箱线图
visualize_outlier_counts 异常/哨兵条数一览
visualize_income_by_default 月收入分布(非缺失样本)

为何箱线图要「截断」极端值?

信用使用率原始最大可达 50708 ,若不做 clip,图会被几个点拉扁,看不出 99% 客户的分布。教学上对 可视化 截断,建模时 步骤 4 再决定 Winsorize/保留。

2.6 实验结果(你的环境)

2.6.1 与违约相关性 Top10
排名 特征(中文) r 直觉
1 30-59天逾期次数 +0.1256 轻度逾期越多,越易严重违约
2 90天严重逾期次数 +0.1172 历史严重逾期是强信号
3 年龄 -0.1154 略负:年长客户违约略少(线性趋势)
4 60-89天逾期次数 +0.1023 中度逾期同样危险
5 家属人数 +0.0460 弱正相关
... 信用使用率 -0.0018 几乎无线性相关(别忽视,非线性可能仍有)

结论逾期次数类 特征整体最强;单特征 r 都不高(<0.13),说明需要多特征组合模型。

2.6.2 分组对比(90 天逾期)
组别 NumberOfTimes90DaysLate 均值
不违约 (0) 0.135
严重逾期 (1) 2.091

违约组的严重逾期次数约为不违约组的 15 倍------业务上非常合理。

2.6.3 异常值 / 哨兵值
规则 条数 占比
age=0 1 0.001%
信用使用率 > 1 3,321 2.21%
信用使用率 > 10 241 0.16%
各逾期列 = 98 264 0.18%
DebtRatio > 100 24,380 16.25%
2.6.4 六条 EDA 核心发现(报告原文)
  1. 目标严重不平衡:违约率 6.68%,不能只看 Accuracy。
  2. 与违约最相关 Top1:30-59 天逾期次数 (r=0.126)。
  3. MonthlyIncome 缺失 19.8%,Pipeline 需 Imputer。
  4. age=01 条,明显异常。
  5. 信用使用率 >13321 条,分布右偏、含极端值。
  6. 逾期 =98264 条,疑为哨兵值,后续需处理策略。

2.7 输出文件

文件 说明
reports/step2_eda.txt 完整 EDA 报告
03_特征与违约相关性Top10.png 相关系数条形图
04_违约与不违约关键特征箱线图.png 三组箱线图
05_异常值初步识别.png 异常/哨兵统计
06_月收入分布按违约对比.png 月收入密度对比

2.8 动手任务

任务 A:与违约正相关最强的 3 个特征是什么?r 各约多少?

任务 B :不违约 vs 违约组,NumberOfTimes90DaysLate 均值各是多少?

任务 C:「信用使用率与违约 r 几乎为 0」是否说明使用率没用?请说理由。

任务 D:什么是哨兵值 98?为什么不应直接当「逾期 98 次」?

任务 E :打开箱线图 04_*,违约组(红色)在「90天严重逾期次数」上是否整体更高?

2.9 验收标准

  • 能解释 EDA 的目的(建模前探索,非训练)
  • 能解释 Pearson r 正负与绝对值含义
  • 能说出至少 3 条本步 EDA 发现(含数字)
  • 知道逾期类特征最重要,但单特征 r 不高
  • 知道 age=0、98 哨兵、Missing Income 等数据质量问题
  • 成功生成 4 张图 + step2_eda.txt

2.10 动手任务与验收标准参考答案

以下为教学标准答案;README.md 学习记录仍建议用自己的话写。

动手任务答案
任务 参考答案
A ①30-59天逾期 +0.126 ;②90天严重逾期 +0.117 ;③60-89天逾期 +0.102 (年龄 -0.115 为负相关最强)。
B 不违约 0.135 ;违约 2.091
C 不能 简单说没用。r 衡量线性关系;使用率与违约可能是非线性,或需与其他特征交互;且 r≈0 只表示「单独线性相关弱」。
D 98 在 Kaggle 该数据中常表示缺失/未知的编码方式,不是真逾期 98 次;直接当大数会误导模型,步骤 4+ 可讨论替换为 NaN 再 Imputer。
E 。红色(违约)箱体整体高于蓝色(不违约),与中位数/均值差异一致。
验收标准答案
验收项 参考答案
EDA 目的 建模前理解分布、相关、异常,指导特征处理与模型选择。
r 的含义 + 越大越易违约;- 越大越不易违约;|r| 越大单特征线性越有用。
三条发现 例:违约率 6.68%;Top 相关是逾期次数;Income 缺 19.8%;98 哨兵 264 条。
单特征 r 最高仅 ~0.13,需多特征 + 非线性模型(RF/HistGBR)。
数据质量 age=0×1;使用率>1×3321;DebtRatio>100×24380;98 哨兵。
产出 4 PNG + step2_eda.txt。

2.11 常见问题

Q1:为什么不用热力图画 10×10 全部相关?

可以,但本任务 y 是核心,特征 vs 违约 排序更直接;特征间相关(多重共线性)步骤 7 调 LR 时再关注。

Q2:年龄与违约负相关,是否说明年轻人更危险?

r=-0.115 只是弱线性趋势,且可能有混杂因素;箱线图/分箱 EDA 可进一步看,不能作因果结论。

Q3:DebtRatio>100 有 2.4 万条,是不是数据错了?

负债率定义是「月债务/月收入」,未填收入 或分母很小时比率会爆高;与 Missing Income 有关,步骤 4 Imputer 会部分缓解,也可能需 cap/删极端(进阶)。

Q4:EDA 完就要删 age=0 那 1 行吗?

本步只记录 ;步骤 3 划分后,在 train 上 决定清洗规则,再应用到 val/test,避免泄露。

2.12 本步小结

要点 内容
方法 相关分析 + 箱线图 + 异常扫描
最强特征族 历史逾期次数
Top1 r 30-59天逾期 0.126
90天逾期均值 不违约 0.14 vs 违约 2.09
主要数据坑 Income 缺失、98 哨兵、使用率极端值
产出 4 图 + step2_eda.txt
下一步 步骤 3:train/val/test 三路划分

2.13 学习记录(请你填写)

  • 完成日期
  • 我印象最深的一条 EDA 发现
  • 我还想问的一个特征
  • 疑问(如有):

文档版本:v1.1 | 最后更新:步骤 2 深度 EDA 教学已写入


步骤 3:三路划分与泄露检查

状态 :✅ 已完成教学(报告见 reports/step3_train_val_test_split.txt,图见 07_训练验证测试集对比.png

3.1 本步目标

步骤 1~2 我们是在全量 15 万行 上看数据。从本步起,必须先把数据切成三份,并写清楚规矩:哪些操作只能在哪一份上做。

本步要完成四件事:

  1. 划分 train 60% / val 20% / test 20%,且用 stratify=y 保持违约率一致
  2. 验收 三路样本量与正负比例接近总体(约 6.68% 违约)
  3. 打印并保存 「数据泄露检查清单」------项目六后续 7 步的铁律
  4. 生成中文对比图与 step3_train_val_test_split.txt 报告

本步仍然不训练模型。我们只切数据、定规则,为步骤 4 的 Pipeline 铺路。

3.2 这一步在解决什么问题?

3.2.1 为什么不能「全部数据拿来训练 + 同一批数据评估」?

想象你在准备高考:

  • 练习题和考卷是同一套,你背答案就能拿高分------但这不代表你真会。
  • 机器学习里,若用同一批数据既训练又评估,模型会「记住答案」,分数虚高,上线后对新客户预测很差。

所以需要留出从未参与训练的数据 ,专门用来「考试」------这就是 test 集(测试集)

但项目六还要调参、选模型、选阈值 ------若只用 train + test,你会反复在 test 上试,等于偷看考卷改答案 ,这叫 数据泄露(data leakage)

因此项目六采用 三路划分
#mermaid-svg-F92jD88DaPIJ6r6a{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F92jD88DaPIJ6r6a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F92jD88DaPIJ6r6a .error-icon{fill:#552222;}#mermaid-svg-F92jD88DaPIJ6r6a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F92jD88DaPIJ6r6a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F92jD88DaPIJ6r6a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F92jD88DaPIJ6r6a .marker.cross{stroke:#333333;}#mermaid-svg-F92jD88DaPIJ6r6a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F92jD88DaPIJ6r6a p{margin:0;}#mermaid-svg-F92jD88DaPIJ6r6a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-F92jD88DaPIJ6r6a .cluster-label text{fill:#333;}#mermaid-svg-F92jD88DaPIJ6r6a .cluster-label span{color:#333;}#mermaid-svg-F92jD88DaPIJ6r6a .cluster-label span p{background-color:transparent;}#mermaid-svg-F92jD88DaPIJ6r6a .label text,#mermaid-svg-F92jD88DaPIJ6r6a span{fill:#333;color:#333;}#mermaid-svg-F92jD88DaPIJ6r6a .node rect,#mermaid-svg-F92jD88DaPIJ6r6a .node circle,#mermaid-svg-F92jD88DaPIJ6r6a .node ellipse,#mermaid-svg-F92jD88DaPIJ6r6a .node polygon,#mermaid-svg-F92jD88DaPIJ6r6a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F92jD88DaPIJ6r6a .rough-node .label text,#mermaid-svg-F92jD88DaPIJ6r6a .node .label text,#mermaid-svg-F92jD88DaPIJ6r6a .image-shape .label,#mermaid-svg-F92jD88DaPIJ6r6a .icon-shape .label{text-anchor:middle;}#mermaid-svg-F92jD88DaPIJ6r6a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-F92jD88DaPIJ6r6a .rough-node .label,#mermaid-svg-F92jD88DaPIJ6r6a .node .label,#mermaid-svg-F92jD88DaPIJ6r6a .image-shape .label,#mermaid-svg-F92jD88DaPIJ6r6a .icon-shape .label{text-align:center;}#mermaid-svg-F92jD88DaPIJ6r6a .node.clickable{cursor:pointer;}#mermaid-svg-F92jD88DaPIJ6r6a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-F92jD88DaPIJ6r6a .arrowheadPath{fill:#333333;}#mermaid-svg-F92jD88DaPIJ6r6a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F92jD88DaPIJ6r6a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F92jD88DaPIJ6r6a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F92jD88DaPIJ6r6a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-F92jD88DaPIJ6r6a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F92jD88DaPIJ6r6a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-F92jD88DaPIJ6r6a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F92jD88DaPIJ6r6a .cluster text{fill:#333;}#mermaid-svg-F92jD88DaPIJ6r6a .cluster span{color:#333;}#mermaid-svg-F92jD88DaPIJ6r6a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F92jD88DaPIJ6r6a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-F92jD88DaPIJ6r6a rect.text{fill:none;stroke-width:0;}#mermaid-svg-F92jD88DaPIJ6r6a .icon-shape,#mermaid-svg-F92jD88DaPIJ6r6a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F92jD88DaPIJ6r6a .icon-shape p,#mermaid-svg-F92jD88DaPIJ6r6a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-F92jD88DaPIJ6r6a .icon-shape .label rect,#mermaid-svg-F92jD88DaPIJ6r6a .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F92jD88DaPIJ6r6a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-F92jD88DaPIJ6r6a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-F92jD88DaPIJ6r6a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤4~8 可用
步骤4~9 可用
步骤9 封箱后

只用一次
全量 150,000 条

违约率 6.68%
训练集 train 60%

90,000 条

fit 模型与预处理
验证集 val 20%

30,000 条

调参、选阈值、比模型
测试集 test 20%

30,000 条

最终评估一次
建模流程
最终成绩单

3.2.2 三路各自干什么?(一句话版)
数据集 占比 样本量 职责 比喻
train 60% 90,000 fit 模型、fit Imputer/Scaler、SMOTE、GridSearch 内层 CV 平时作业 + 课堂练习
val 20% 30,000 对比模型、调阈值、GridSearch 外层复核 模拟考(可多次)
test 20% 30,000 最终 评估,尽量只用 1 次 正式高考(不能反复改策略刷分)

关键纪律 :test 在步骤 4~8 不参与任何调参;步骤 9 业务阈值定好后,才打开 test 看「真实考分」。

3.3 本步在整体流程中的位置

#mermaid-svg-o8NKljkMii03Q0BZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o8NKljkMii03Q0BZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o8NKljkMii03Q0BZ .error-icon{fill:#552222;}#mermaid-svg-o8NKljkMii03Q0BZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o8NKljkMii03Q0BZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o8NKljkMii03Q0BZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o8NKljkMii03Q0BZ .marker.cross{stroke:#333333;}#mermaid-svg-o8NKljkMii03Q0BZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o8NKljkMii03Q0BZ p{margin:0;}#mermaid-svg-o8NKljkMii03Q0BZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-o8NKljkMii03Q0BZ .cluster-label text{fill:#333;}#mermaid-svg-o8NKljkMii03Q0BZ .cluster-label span{color:#333;}#mermaid-svg-o8NKljkMii03Q0BZ .cluster-label span p{background-color:transparent;}#mermaid-svg-o8NKljkMii03Q0BZ .label text,#mermaid-svg-o8NKljkMii03Q0BZ span{fill:#333;color:#333;}#mermaid-svg-o8NKljkMii03Q0BZ .node rect,#mermaid-svg-o8NKljkMii03Q0BZ .node circle,#mermaid-svg-o8NKljkMii03Q0BZ .node ellipse,#mermaid-svg-o8NKljkMii03Q0BZ .node polygon,#mermaid-svg-o8NKljkMii03Q0BZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o8NKljkMii03Q0BZ .rough-node .label text,#mermaid-svg-o8NKljkMii03Q0BZ .node .label text,#mermaid-svg-o8NKljkMii03Q0BZ .image-shape .label,#mermaid-svg-o8NKljkMii03Q0BZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-o8NKljkMii03Q0BZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-o8NKljkMii03Q0BZ .rough-node .label,#mermaid-svg-o8NKljkMii03Q0BZ .node .label,#mermaid-svg-o8NKljkMii03Q0BZ .image-shape .label,#mermaid-svg-o8NKljkMii03Q0BZ .icon-shape .label{text-align:center;}#mermaid-svg-o8NKljkMii03Q0BZ .node.clickable{cursor:pointer;}#mermaid-svg-o8NKljkMii03Q0BZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-o8NKljkMii03Q0BZ .arrowheadPath{fill:#333333;}#mermaid-svg-o8NKljkMii03Q0BZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o8NKljkMii03Q0BZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o8NKljkMii03Q0BZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o8NKljkMii03Q0BZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-o8NKljkMii03Q0BZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o8NKljkMii03Q0BZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-o8NKljkMii03Q0BZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o8NKljkMii03Q0BZ .cluster text{fill:#333;}#mermaid-svg-o8NKljkMii03Q0BZ .cluster span{color:#333;}#mermaid-svg-o8NKljkMii03Q0BZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-o8NKljkMii03Q0BZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-o8NKljkMii03Q0BZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-o8NKljkMii03Q0BZ .icon-shape,#mermaid-svg-o8NKljkMii03Q0BZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o8NKljkMii03Q0BZ .icon-shape p,#mermaid-svg-o8NKljkMii03Q0BZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-o8NKljkMii03Q0BZ .icon-shape .label rect,#mermaid-svg-o8NKljkMii03Q0BZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o8NKljkMii03Q0BZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-o8NKljkMii03Q0BZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-o8NKljkMii03Q0BZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤1 加载
步骤2 EDA
步骤3 三路划分

← 你在这里
步骤4 基线 Pipeline
步骤5~8 调参集成
步骤9 test 评估

EDA(步骤 2)可以在划分前 对全量做------那是「看数据」,不是「用标签调模型」。

划分之后,任何依赖标签 y 的建模决策都必须遵守泄露清单。

3.4 名词解释(本步新术语)

名词 大白话 为什么本步需要 在本项目中 易混淆
train_test_split sklearn 函数,随机把数据切成两份 本步用两次得到 60/20/20 random_state=42 可复现 不是只能切 train/test 两路
stratify=y 分层抽样:切完后各集违约率与总体接近 违约只有 6.68%,随机切可能某集变成 5% 或 8% 三路均为 6.68% 不设 stratify 则小概率失衡
random_state 随机种子,固定后每次划分结果相同 便于复现、对照实验 42(与前几项目一致) 不是「模型参数」
数据泄露 leakage 测试/验证信息「漏进」训练过程,分数虚高 项目六步骤多,最易在此翻车 test 反复调参 = 泄露 与「网络安全泄露」无关
fit / transform fit=在数据上 规则;transform=应用规则 Scaler 只能在 train 上 fit 步骤 4 Pipeline 强制执行 全量 fit Scaler 再划分 = 泄露
验证集 validation 调参用的「模拟考」集 没有 val 就会偷看 test 30,000 不是 K 折里的「某一折」
测试集 test 最终评估,尽量一次 步骤 9 才用 30,000 不等于 val
K 折 CV 把 train 再分成 K 份轮流验证 步骤 6~7 在 train 内部 不占用 test 本步尚未实现
3.4.1 为什么要 stratify?(数值直觉)

全量违约率 6.68% 。若不用 stratify,随机切 30,000 条 test,违约人数期望约 2004,但波动 可能让 test 违约率变成 6.2% 或 7.1%------对比模型时多 0.5% 可能是抽样噪声而非模型真的更好。

stratify=y 保证每一路都约 6.68% ,对比更公平。对极度不平衡数据(如项目二 0.17% 欺诈)更是必须。

3.4.2 两次 split 怎么得到 60/20/20?

#mermaid-svg-RVDSCEhV5lX7Yb5s{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RVDSCEhV5lX7Yb5s .error-icon{fill:#552222;}#mermaid-svg-RVDSCEhV5lX7Yb5s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RVDSCEhV5lX7Yb5s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .marker.cross{stroke:#333333;}#mermaid-svg-RVDSCEhV5lX7Yb5s svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RVDSCEhV5lX7Yb5s p{margin:0;}#mermaid-svg-RVDSCEhV5lX7Yb5s .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster-label text{fill:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster-label span{color:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster-label span p{background-color:transparent;}#mermaid-svg-RVDSCEhV5lX7Yb5s .label text,#mermaid-svg-RVDSCEhV5lX7Yb5s span{fill:#333;color:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .node rect,#mermaid-svg-RVDSCEhV5lX7Yb5s .node circle,#mermaid-svg-RVDSCEhV5lX7Yb5s .node ellipse,#mermaid-svg-RVDSCEhV5lX7Yb5s .node polygon,#mermaid-svg-RVDSCEhV5lX7Yb5s .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .rough-node .label text,#mermaid-svg-RVDSCEhV5lX7Yb5s .node .label text,#mermaid-svg-RVDSCEhV5lX7Yb5s .image-shape .label,#mermaid-svg-RVDSCEhV5lX7Yb5s .icon-shape .label{text-anchor:middle;}#mermaid-svg-RVDSCEhV5lX7Yb5s .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .rough-node .label,#mermaid-svg-RVDSCEhV5lX7Yb5s .node .label,#mermaid-svg-RVDSCEhV5lX7Yb5s .image-shape .label,#mermaid-svg-RVDSCEhV5lX7Yb5s .icon-shape .label{text-align:center;}#mermaid-svg-RVDSCEhV5lX7Yb5s .node.clickable{cursor:pointer;}#mermaid-svg-RVDSCEhV5lX7Yb5s .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .arrowheadPath{fill:#333333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVDSCEhV5lX7Yb5s .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RVDSCEhV5lX7Yb5s .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVDSCEhV5lX7Yb5s .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster text{fill:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s .cluster span{color:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RVDSCEhV5lX7Yb5s .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RVDSCEhV5lX7Yb5s rect.text{fill:none;stroke-width:0;}#mermaid-svg-RVDSCEhV5lX7Yb5s .icon-shape,#mermaid-svg-RVDSCEhV5lX7Yb5s .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVDSCEhV5lX7Yb5s .icon-shape p,#mermaid-svg-RVDSCEhV5lX7Yb5s .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RVDSCEhV5lX7Yb5s .icon-shape .label rect,#mermaid-svg-RVDSCEhV5lX7Yb5s .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVDSCEhV5lX7Yb5s .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RVDSCEhV5lX7Yb5s .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RVDSCEhV5lX7Yb5s :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 150,000 条
第1次 split

test_size=0.4
train 60%

90,000
temp 40%

60,000
第2次 split

test_size=0.5

stratify=y_temp
val 20%

30,000
test 20%

30,000

  • 第一次:留出 40% 临时集 → train 得 60%
  • 第二次:临时集 对半分 → val 20% + test 20%
  • 两次都 stratify=y,违约比例对齐

3.5 数据泄露检查清单(全文)

以下 7 条是项目六必须遵守的规范(脚本会打印并写入报告):

# 操作 允许的数据 说明
1 fit StandardScaler / Imputer 仅 train 均值、标准差、中位数不能偷看 val/test
2 SMOTE 过采样 仅 train 合成样本不能基于 val/test 分布
3 GridSearchCV 选超参 train(内层 CV) 用 val 复核;test 不参与
4 根据表现选分类阈值 val test 只在最终评估用一次
5 特征选择 / 删列决策 仅 train 不能用 test F1 反推删哪列
6 全量 EDA 划分前可看全量 划分后调参勿偷看 test
7 最终 test 评估 test 仅一次 反复调 test = 泄露

#mermaid-svg-y0ODAYmB1AwbcoPd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y0ODAYmB1AwbcoPd .error-icon{fill:#552222;}#mermaid-svg-y0ODAYmB1AwbcoPd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y0ODAYmB1AwbcoPd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y0ODAYmB1AwbcoPd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y0ODAYmB1AwbcoPd .marker.cross{stroke:#333333;}#mermaid-svg-y0ODAYmB1AwbcoPd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y0ODAYmB1AwbcoPd p{margin:0;}#mermaid-svg-y0ODAYmB1AwbcoPd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster-label text{fill:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster-label span{color:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster-label span p{background-color:transparent;}#mermaid-svg-y0ODAYmB1AwbcoPd .label text,#mermaid-svg-y0ODAYmB1AwbcoPd span{fill:#333;color:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd .node rect,#mermaid-svg-y0ODAYmB1AwbcoPd .node circle,#mermaid-svg-y0ODAYmB1AwbcoPd .node ellipse,#mermaid-svg-y0ODAYmB1AwbcoPd .node polygon,#mermaid-svg-y0ODAYmB1AwbcoPd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y0ODAYmB1AwbcoPd .rough-node .label text,#mermaid-svg-y0ODAYmB1AwbcoPd .node .label text,#mermaid-svg-y0ODAYmB1AwbcoPd .image-shape .label,#mermaid-svg-y0ODAYmB1AwbcoPd .icon-shape .label{text-anchor:middle;}#mermaid-svg-y0ODAYmB1AwbcoPd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y0ODAYmB1AwbcoPd .rough-node .label,#mermaid-svg-y0ODAYmB1AwbcoPd .node .label,#mermaid-svg-y0ODAYmB1AwbcoPd .image-shape .label,#mermaid-svg-y0ODAYmB1AwbcoPd .icon-shape .label{text-align:center;}#mermaid-svg-y0ODAYmB1AwbcoPd .node.clickable{cursor:pointer;}#mermaid-svg-y0ODAYmB1AwbcoPd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y0ODAYmB1AwbcoPd .arrowheadPath{fill:#333333;}#mermaid-svg-y0ODAYmB1AwbcoPd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y0ODAYmB1AwbcoPd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y0ODAYmB1AwbcoPd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y0ODAYmB1AwbcoPd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y0ODAYmB1AwbcoPd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y0ODAYmB1AwbcoPd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster text{fill:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd .cluster span{color:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y0ODAYmB1AwbcoPd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y0ODAYmB1AwbcoPd rect.text{fill:none;stroke-width:0;}#mermaid-svg-y0ODAYmB1AwbcoPd .icon-shape,#mermaid-svg-y0ODAYmB1AwbcoPd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y0ODAYmB1AwbcoPd .icon-shape p,#mermaid-svg-y0ODAYmB1AwbcoPd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y0ODAYmB1AwbcoPd .icon-shape .label rect,#mermaid-svg-y0ODAYmB1AwbcoPd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y0ODAYmB1AwbcoPd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y0ODAYmB1AwbcoPd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y0ODAYmB1AwbcoPd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ❌ 步骤4~8 禁止
用 test 调参
test 上 fit 任何东西
✅ 在 val 上决策
选阈值
比模型
✅ 只在 train 上 fit
Imputer
StandardScaler
SMOTE
特征选择

小白记忆口诀「fit 只 train,调参看 val,test 考一次。」

3.6 代码解读

函数 作用
split_train_val_test 两次 train_test_split,返回 X/y 六元组
summarize_split 统计各集样本数、违约数、违约率
get_leakage_checklist 返回 7 条泄露规则(供打印与报告复用)
visualize_three_way_split 左:样本数柱状图;右:违约率对比
save_step3_split_report 写入 step3_train_val_test_split.txt

核心代码逻辑(简化):

python 复制代码
# 特征 X 与标签 y
X = df[get_feature_columns(df)]
y = df[TARGET_COL]

# 60% train,40% temp
X_train, X_temp, y_train, y_temp = train_test_split(
    X, y, test_size=0.4, random_state=42, stratify=y)

# temp 对半 → 20% val,20% test
X_val, X_test, y_val, y_test = train_test_split(
    X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp)

注意 :本步划分结果在内存中返回;步骤 4 会在 main() 里再次调用 split_train_val_test(),因 random_state 固定,每次运行划分一致

3.7 如何运行本步

bash 复制代码
cd project_06_credit_risk
python src/train.py

控制台应看到三路样本量 90000 / 30000 / 30000 ,违约率均为 6.68%

3.8 实验结果(你的环境)

数据集 样本数 违约数 违约率
train 90,000 6,016 6.68%
val 30,000 2,005 6.68%
test 30,000 2,005 6.68%
全量(对照) 150,000 10,026 6.68%

验收通过:三路违约率与全量完全一致(分层抽样成功)。

3.9 输出文件

文件 说明
reports/step3_train_val_test_split.txt 划分参数、结果表、泄露清单
reports/figures/07_训练验证测试集对比.png 样本量 + 违约率双子图

3.10 动手任务

任务 A:train / val / test 各占多少比例?各多少条?

任务 B:为什么需要 val,不能只用 train + test?

任务 C :若在全量数据 上 fit StandardScaler 再划分 train/test,错在哪一步?

任务 Dstratify=y 去掉后,重新运行(可临时改代码),test 违约率可能偏离 6.68% 吗?说明原因。

任务 E:步骤 2 的 EDA 用了全量数据,算不算泄露?为什么?

任务 F:背诵泄露口诀,并写出 test 集在项目六哪一步才「开封」使用。

3.11 验收标准

  • 能解释 train / val / test 各自职责
  • 能说出划分比例 60/20/20 与样本量 90k/30k/30k
  • 能解释 stratify=yrandom_state=42 的作用
  • 能列举至少 3 条泄露清单并举例
  • 知道本步未训练模型,只划分 + 定规则
  • 成功生成 07_*.pngstep3_train_val_test_split.txt

3.12 动手任务与验收标准参考答案

动手任务答案
任务 参考答案
A 60% / 20% / 20%90,000 / 30,000 / 30,000 条。
B 调参、选模型、选阈值需要多次试错;若只用 test,会反复「偷看考卷」导致 test 分数不可信;val 是专门的模拟考。
C Scaler 的均值/方差用了 test 的信息 (全量 fit),test 不再「未见过的数据」,评估会乐观泄露 。正确:只在 train 上 fit,再 transform val/test。
D 可能。不用 stratify 时各集违约率是随机波动,小样本下可能偏离 6.68%(如 6.3% 或 7.2%),影响对比公平性。
E 不算 (在本项目规范下)。步骤 2 是探索性 看分布/相关,没有用 test 标签去训练或选超参;且 EDA 结论(如「Income 缺 20%」)是业务事实,不依赖划分。划分后则不能再根据 test 表现改 EDA 结论。
F 口诀:fit 只 train,调参看 val,test 考一次 。test 在 步骤 9(业务阈值与成本) 定好策略后,做最终一次评估。
验收标准答案
验收项 参考答案
三路职责 train=学习;val=调参/比模型/选阈值;test=最终评估一次。
比例与数量 60/20/20;90000/30000/30000。
stratify 保持各集违约率≈全量 6.68%。
random_state 固定随机性,实验可复现。
泄露举例 全量 fit Scaler;test 上 GridSearch;反复用 test 选阈值。
本步边界 只划分, fit 模型。
产出 step3 报告 + 图 07。

3.13 常见问题

Q1:项目 1 只有 train/test 两路,为什么项目六要三路?

项目六步骤多(CV、GridSearch、Stacking、业务阈值),调参次数多 ;两路时容易忍不住反复看 test。加 val 是竞赛/工业常见做法,把 test 封箱更干净。

Q2:val 和 K 折 CV 里的「验证折」冲突吗?

不冲突。步骤 6~7 会在 train 内部 再做 K 折------那是「训练集里的交叉验证」;对外的 val 仍是 hold-out,用于步骤 4~5 快速对比和步骤 9 前选阈值。

Q3:能否把 val 和 test 合并?

可以但不推荐:合并后只剩一次 hold-out,调阈值和最终评估混在一起,仍易泄露。教学项目坚持三路更清晰。

Q4:划分要不要先删掉 age=0 那 1 条?

步骤 2 只记录 异常。是否在 train 上定清洗规则并在 val/test 同样应用 ,放在步骤 4 Pipeline 或预处理里做------且规则只能来自 train 的统计

Q5:需要把划分索引保存到磁盘吗?

本步未强制保存;因 random_state=42 固定,每次运行一致。进阶可保存 train_idxoutputs/ 便于团队协作。

3.14 本步小结

要点 内容
划分 train 60% / val 20% / test 20%
样本量 90,000 / 30,000 / 30,000
违约率 三路均为 6.68%(stratify=y)
铁律 fit 只 train;调参看 val;test 步骤 9 考一次
产出 图 07 + step3 报告
下一步 步骤 4:基线 Pipeline + evaluate_model()

3.15 学习记录(请你填写)

  • 完成日期
  • 我用一句话解释 val 和 test 的区别
  • 我最容易犯的一条泄露错误(自评):
  • 疑问(如有):

文档版本:v1.2 | 最后更新:步骤 3 三路划分与泄露检查教学已写入


步骤 4:基线 Pipeline

状态 :✅ 已完成教学(报告见 reports/step4_baseline_pipeline.txt,图见 08_*.png09_*.png

4.1 本步目标

步骤 3 切好了数据、定好了规矩。本步要第一次真正训练模型 ,并搭好后续步骤共用的评估框架

要完成四件事:

  1. 构建 Pipeline([SimpleImputer, StandardScaler, LogisticRegression])
  2. trainfit,在 val 上评估(不用 test
  3. 封装 evaluate_model():统一输出 Accuracy / F1 / ROC-AUC + 混淆矩阵
  4. 与「全猜不违约(全 0) 」对照,证明基线 F1 明显优于瞎猜

本步是项目六建模的「起跑线」 ------步骤 5~8 的所有对比,都沿用同一套 Pipeline 结构与 evaluate_model()

4.2 这一步在解决什么问题?

4.2.1 为什么不能直接 LogisticRegression().fit(X, y)

步骤 2 我们发现:

  • MonthlyIncome19.8%
  • 各特征量纲不同(年龄 ~50,负债率可能 >100)

若跳过预处理直接 fit:

  1. 缺失值会让 sklearn 报错或行为异常
  2. 未缩放时,数值大的列(如 DebtRatio)会 dominate 逻辑回归
  3. 若在全量数据 上先填缺失、再划分 → 泄露(Imputer 偷看了 val/test 的中位数)

Pipeline 的作用 :把「填缺失 → 标准化 → 分类」串成一条流水线pipeline.fit(X_train, y_train) 时,Imputer 和 Scaler 只在 train 上学习 ,对 val 只做 transform------与步骤 3 泄露清单一致。
#mermaid-svg-Inegbq1nDTFy4Tnz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Inegbq1nDTFy4Tnz .error-icon{fill:#552222;}#mermaid-svg-Inegbq1nDTFy4Tnz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Inegbq1nDTFy4Tnz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Inegbq1nDTFy4Tnz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Inegbq1nDTFy4Tnz .marker.cross{stroke:#333333;}#mermaid-svg-Inegbq1nDTFy4Tnz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Inegbq1nDTFy4Tnz p{margin:0;}#mermaid-svg-Inegbq1nDTFy4Tnz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster-label text{fill:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster-label span{color:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster-label span p{background-color:transparent;}#mermaid-svg-Inegbq1nDTFy4Tnz .label text,#mermaid-svg-Inegbq1nDTFy4Tnz span{fill:#333;color:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz .node rect,#mermaid-svg-Inegbq1nDTFy4Tnz .node circle,#mermaid-svg-Inegbq1nDTFy4Tnz .node ellipse,#mermaid-svg-Inegbq1nDTFy4Tnz .node polygon,#mermaid-svg-Inegbq1nDTFy4Tnz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Inegbq1nDTFy4Tnz .rough-node .label text,#mermaid-svg-Inegbq1nDTFy4Tnz .node .label text,#mermaid-svg-Inegbq1nDTFy4Tnz .image-shape .label,#mermaid-svg-Inegbq1nDTFy4Tnz .icon-shape .label{text-anchor:middle;}#mermaid-svg-Inegbq1nDTFy4Tnz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Inegbq1nDTFy4Tnz .rough-node .label,#mermaid-svg-Inegbq1nDTFy4Tnz .node .label,#mermaid-svg-Inegbq1nDTFy4Tnz .image-shape .label,#mermaid-svg-Inegbq1nDTFy4Tnz .icon-shape .label{text-align:center;}#mermaid-svg-Inegbq1nDTFy4Tnz .node.clickable{cursor:pointer;}#mermaid-svg-Inegbq1nDTFy4Tnz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Inegbq1nDTFy4Tnz .arrowheadPath{fill:#333333;}#mermaid-svg-Inegbq1nDTFy4Tnz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Inegbq1nDTFy4Tnz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Inegbq1nDTFy4Tnz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Inegbq1nDTFy4Tnz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Inegbq1nDTFy4Tnz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Inegbq1nDTFy4Tnz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster text{fill:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz .cluster span{color:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Inegbq1nDTFy4Tnz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Inegbq1nDTFy4Tnz rect.text{fill:none;stroke-width:0;}#mermaid-svg-Inegbq1nDTFy4Tnz .icon-shape,#mermaid-svg-Inegbq1nDTFy4Tnz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Inegbq1nDTFy4Tnz .icon-shape p,#mermaid-svg-Inegbq1nDTFy4Tnz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Inegbq1nDTFy4Tnz .icon-shape .label rect,#mermaid-svg-Inegbq1nDTFy4Tnz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Inegbq1nDTFy4Tnz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Inegbq1nDTFy4Tnz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Inegbq1nDTFy4Tnz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} X_train

含缺失
Pipeline.fit
Imputer

学 train 中位数
Scaler

学 train 均值方差
LogisticRegression

学权重
X_val
Pipeline.predict

只 transform
y_pred / y_proba

4.2.2 为什么要封装 evaluate_model()?

项目六后面要对比:不平衡策略、随机森林、GridSearch、Stacking ......若每步手写一遍 accuracy_scoref1_score、打印格式,容易漏指标、格式不统一。

evaluate_model()标准考卷批改器:同一套指标、同一套打印、同一套返回值,步骤 5~8 直接调用即可。

4.3 本步在整体流程中的位置

#mermaid-svg-ZYL9Hk2yDXrzdLar{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZYL9Hk2yDXrzdLar .error-icon{fill:#552222;}#mermaid-svg-ZYL9Hk2yDXrzdLar .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZYL9Hk2yDXrzdLar .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .marker.cross{stroke:#333333;}#mermaid-svg-ZYL9Hk2yDXrzdLar svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZYL9Hk2yDXrzdLar p{margin:0;}#mermaid-svg-ZYL9Hk2yDXrzdLar .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster-label text{fill:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster-label span{color:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster-label span p{background-color:transparent;}#mermaid-svg-ZYL9Hk2yDXrzdLar .label text,#mermaid-svg-ZYL9Hk2yDXrzdLar span{fill:#333;color:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .node rect,#mermaid-svg-ZYL9Hk2yDXrzdLar .node circle,#mermaid-svg-ZYL9Hk2yDXrzdLar .node ellipse,#mermaid-svg-ZYL9Hk2yDXrzdLar .node polygon,#mermaid-svg-ZYL9Hk2yDXrzdLar .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .rough-node .label text,#mermaid-svg-ZYL9Hk2yDXrzdLar .node .label text,#mermaid-svg-ZYL9Hk2yDXrzdLar .image-shape .label,#mermaid-svg-ZYL9Hk2yDXrzdLar .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZYL9Hk2yDXrzdLar .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .rough-node .label,#mermaid-svg-ZYL9Hk2yDXrzdLar .node .label,#mermaid-svg-ZYL9Hk2yDXrzdLar .image-shape .label,#mermaid-svg-ZYL9Hk2yDXrzdLar .icon-shape .label{text-align:center;}#mermaid-svg-ZYL9Hk2yDXrzdLar .node.clickable{cursor:pointer;}#mermaid-svg-ZYL9Hk2yDXrzdLar .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .arrowheadPath{fill:#333333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZYL9Hk2yDXrzdLar .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZYL9Hk2yDXrzdLar .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZYL9Hk2yDXrzdLar .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster text{fill:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar .cluster span{color:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZYL9Hk2yDXrzdLar .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZYL9Hk2yDXrzdLar rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZYL9Hk2yDXrzdLar .icon-shape,#mermaid-svg-ZYL9Hk2yDXrzdLar .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZYL9Hk2yDXrzdLar .icon-shape p,#mermaid-svg-ZYL9Hk2yDXrzdLar .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZYL9Hk2yDXrzdLar .icon-shape .label rect,#mermaid-svg-ZYL9Hk2yDXrzdLar .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZYL9Hk2yDXrzdLar .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZYL9Hk2yDXrzdLar .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZYL9Hk2yDXrzdLar :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤3 划分
步骤4 基线Pipeline

← 你在这里
步骤5 不平衡策略
步骤6 多模型CV
步骤9 test评估

仍用 evaluate_model

4.4 名词解释(本步新术语)

名词 大白话 为什么本步需要 在本项目中 易混淆
Pipeline sklearn 把多步预处理+模型串成一条链 防泄露、一步 fit Imputer→Scaler→LR 不是 Linux 管道
SimpleImputer 用统计量(如中位数)填缺失 Income 缺 19.8% strategy='median' 不是删行
StandardScaler 减均值、除标准差,缩到相近尺度 LR 对量纲敏感 只在 train fit 不是 MinMaxScaler
LogisticRegression 逻辑回归,输出 0~1 概率的二分类器 可解释、训练快、作基线 max_iter=1000 名字带 Regression 但是分类
Accuracy 猜对的比例 基线约 93.37% 接近「全猜 0」的 93.32% 不平衡时误导
Precision(精确率) 预测为违约的人里,真违约占多少 基线 0.5548 误报成本高时重要 ≠ Accuracy
Recall(召回率) 真违约的人里,被抓住多少 基线仅 0.0404 漏报成本高时重要 也叫 TP rate
F1 Precision 与 Recall 的调和平均 违约类 F1=0.0753 步骤 5 主要优化目标 比 Acc 更公平
ROC-AUC 模型排序能力(0.5=随机,1=完美) 基线 0.7151 与阈值无关的整体分 不是 Accuracy
混淆矩阵 真/假 × 预测 0/1 的四格表 08_基线混淆矩阵 看漏报/误报数量
全猜不违约 所有人都预测 0 Acc 93.32% 但 F1=0 本步对照基线 步骤 1 提过
4.4.1 为什么基线 Acc 很高、F1 却很低?

验证集约 93.32% 是不违约。默认阈值 0.5 下,LR 仍偏向预测 0

  • Accuracy 0.9337 ≈ 全猜 0 的 0.9332 ------ 看起来「差不多」
  • 违约 F1 0.0753 vs 全 0 的 0.0000 ------ 说明模型确实抓到了一部分违约 ,但 Recall 只有 4%,大量违约被漏掉

这正是步骤 5 要做不平衡处理 + 调阈值的原因。

4.4.2 混淆矩阵四格怎么读?
复制代码
                 预测
              不违约    违约
真  不违约    TN       FP(误报)
实  违约      FN(漏报) TP
  • FN(漏报):真违约却预测不违约 ------ 银行可能放款给坏客户
  • FP(误报):假违约 ------ 可能误拒好客户

4.5 Pipeline 流程(Mermaid)

#mermaid-svg-z1EKKzeu51uTstNa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-z1EKKzeu51uTstNa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-z1EKKzeu51uTstNa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-z1EKKzeu51uTstNa .error-icon{fill:#552222;}#mermaid-svg-z1EKKzeu51uTstNa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z1EKKzeu51uTstNa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-z1EKKzeu51uTstNa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z1EKKzeu51uTstNa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z1EKKzeu51uTstNa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-z1EKKzeu51uTstNa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z1EKKzeu51uTstNa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z1EKKzeu51uTstNa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z1EKKzeu51uTstNa .marker.cross{stroke:#333333;}#mermaid-svg-z1EKKzeu51uTstNa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z1EKKzeu51uTstNa p{margin:0;}#mermaid-svg-z1EKKzeu51uTstNa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-z1EKKzeu51uTstNa .cluster-label text{fill:#333;}#mermaid-svg-z1EKKzeu51uTstNa .cluster-label span{color:#333;}#mermaid-svg-z1EKKzeu51uTstNa .cluster-label span p{background-color:transparent;}#mermaid-svg-z1EKKzeu51uTstNa .label text,#mermaid-svg-z1EKKzeu51uTstNa span{fill:#333;color:#333;}#mermaid-svg-z1EKKzeu51uTstNa .node rect,#mermaid-svg-z1EKKzeu51uTstNa .node circle,#mermaid-svg-z1EKKzeu51uTstNa .node ellipse,#mermaid-svg-z1EKKzeu51uTstNa .node polygon,#mermaid-svg-z1EKKzeu51uTstNa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z1EKKzeu51uTstNa .rough-node .label text,#mermaid-svg-z1EKKzeu51uTstNa .node .label text,#mermaid-svg-z1EKKzeu51uTstNa .image-shape .label,#mermaid-svg-z1EKKzeu51uTstNa .icon-shape .label{text-anchor:middle;}#mermaid-svg-z1EKKzeu51uTstNa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-z1EKKzeu51uTstNa .rough-node .label,#mermaid-svg-z1EKKzeu51uTstNa .node .label,#mermaid-svg-z1EKKzeu51uTstNa .image-shape .label,#mermaid-svg-z1EKKzeu51uTstNa .icon-shape .label{text-align:center;}#mermaid-svg-z1EKKzeu51uTstNa .node.clickable{cursor:pointer;}#mermaid-svg-z1EKKzeu51uTstNa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-z1EKKzeu51uTstNa .arrowheadPath{fill:#333333;}#mermaid-svg-z1EKKzeu51uTstNa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z1EKKzeu51uTstNa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z1EKKzeu51uTstNa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z1EKKzeu51uTstNa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-z1EKKzeu51uTstNa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z1EKKzeu51uTstNa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-z1EKKzeu51uTstNa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z1EKKzeu51uTstNa .cluster text{fill:#333;}#mermaid-svg-z1EKKzeu51uTstNa .cluster span{color:#333;}#mermaid-svg-z1EKKzeu51uTstNa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-z1EKKzeu51uTstNa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-z1EKKzeu51uTstNa rect.text{fill:none;stroke-width:0;}#mermaid-svg-z1EKKzeu51uTstNa .icon-shape,#mermaid-svg-z1EKKzeu51uTstNa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z1EKKzeu51uTstNa .icon-shape p,#mermaid-svg-z1EKKzeu51uTstNa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-z1EKKzeu51uTstNa .icon-shape .label rect,#mermaid-svg-z1EKKzeu51uTstNa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z1EKKzeu51uTstNa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-z1EKKzeu51uTstNa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-z1EKKzeu51uTstNa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} build_baseline_pipeline()
SimpleImputer(median)

填 Income/Dependents 缺失
StandardScaler

10 特征标准化
LogisticRegression

输出 P(违约)
pipeline.fit(X_train, y_train)
evaluate_model(pipeline, X_val, y_val)
指标 + 混淆矩阵 + 报告

4.6 代码解读

函数 作用
build_baseline_pipeline 返回 Imputer→Scaler→LR 三步 Pipeline
evaluate_model 对已 fit 模型算 Acc/P/R/F1/AUC,打印 report,返回 dict
compute_naive_all_zero_metrics 「全猜 0」对照指标
visualize_confusion_matrix 中文标签混淆矩阵热力图
visualize_baseline_vs_naive Acc vs F1 双柱对比图
save_step4_baseline_report 写入 step4_baseline_pipeline.txt

核心代码(简化):

python 复制代码
pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
    ("classifier", LogisticRegression(max_iter=1000, random_state=42)),
])
pipeline.fit(X_train, y_train)          # 只在 train 上 fit 整条链
val_metrics = evaluate_model(pipeline, X_val, y_val, "验证集 val")

为何 Imputer 用 median?

收入等右偏分布,中位数比均值更抗极端值;与项目三数值列策略一致。

4.7 如何运行本步

bash 复制代码
cd project_06_credit_risk
python src/train.py

4.8 实验结果(你的环境)

4.8.1 验证集 --- 基线 LR Pipeline
指标 数值 解读
Accuracy 0.9337 与全猜 0 几乎一样,不能说明模型好
Precision(违约) 0.5548 预测违约里约一半真违约
Recall(违约) 0.0404 只抓住约 4% 真违约
F1(违约) 0.0753 明显优于全 0 的 0
ROC-AUC 0.7151 排序能力尚可,优于随机 0.5
4.8.2 对照 --- 全猜不违约(全 0)
指标 数值
Accuracy 0.9332
F1(违约) 0.0000

验收通过 :基线 F1 0.0753 >> 0.0000,模型学到了信号;但 Recall 太低,步骤 5 需改进。

4.8.3 classification_report 摘要
类别 precision recall f1-score support
不违约(0) 0.9356 0.9977 0.9656 27,995
严重逾期(1) 0.5548 0.0404 0.0753 2,005

4.9 输出文件

文件 说明
reports/step4_baseline_pipeline.txt 完整评估报告
08_基线Pipeline验证集混淆矩阵.png 混淆矩阵热力图
09_基线模型与全猜不违约对比.png Acc / F1 对照

4.10 动手任务

任务 A:Pipeline 三步分别是什么?各解决什么问题?

任务 B :为何 pipeline.fit(X_train) 能防止 Imputer 泄露 val 信息?

任务 C:基线 Accuracy 0.9337 与全猜 0 的 0.9332 几乎相同,为何还说基线「更好」?

任务 D:Recall=0.0404 表示什么?对银行风控意味着什么?

任务 E:ROC-AUC=0.7151 说明什么?与 F1 低是否矛盾?

任务 F:打开混淆矩阵图,FN 和 FP 哪个数量更大?为什么?

4.11 验收标准

  • 能画出/说出 Pipeline 三步及顺序
  • 能解释 fit 只在 train、eval 在 val、test 未使用
  • 能解释 Accuracy 高但 F1 低的含义
  • 能说出基线 val 的 F1、AUC 约数
  • 知道 evaluate_model() 供后续步骤复用
  • 成功生成 step4 报告 + 图 08、09

4.12 动手任务与验收标准参考答案

动手任务答案
任务 参考答案
A SimpleImputer(median) 填缺失;② StandardScaler 标准化;③ LogisticRegression 二分类。
B fit 时 Pipeline 内部 Imputer 只从 X_train 计算各列中位数;对 val 预测时只用这些统计量 transform,未使用 val 的标签或特征分布来「学习」。
C 违约类 F1 :基线 0.0753 ,全 0 为 0;Acc 被 93% 多数类主导,F1 才反映「抓违约」能力。
D 4% 的真违约被识别;96% 违约被漏报------若直接用于拒贷,会放过大量坏客户。
E AUC 0.715 说明按概率排序时,违约客户整体排在不违约前面;F1 低是因为默认阈值 0.5 下预测违约太少------排序好 ≠ 阈值合适
F FN(漏报)远大于 FP;模型保守,很少预测 1,故漏掉大量真违约。
验收标准答案
验收项 参考答案
Pipeline Imputer(median) → StandardScaler → LogisticRegression
数据使用 fit train 90k;eval val 30k;test 未用
Acc vs F1 Acc≈93% 因多数类;F1≈0.075 反映违约识别弱
基线指标 F1≈0.075,AUC≈0.715,Recall≈0.04
evaluate_model 统一 Acc/P/R/F1/AUC + report
产出 step4 报告 + 08/09 图

4.13 常见问题

Q1:为什么不用 RandomForest 作基线?

步骤 4 刻意用最简 LR + 线性 Pipeline,便于理解泄露与评估;步骤 6 会系统对比 RF/HistGBR。

Q2:test 什么时候用?

本步及步骤 5~8 均只用 valtest 在步骤 9 定阈值后评估一次

Q3:Imputer 能换成 mean 吗?

可以,但 Income 右偏,median 更稳;步骤 7 GridSearch 可再搜 strategy。

Q4:F1 只有 0.075,模型很烂吗?

作为未处理不平衡的基线,属正常;AUC 0.72 说明有信号。步骤 5 的 class_weight/SMOTE/阈值会明显提升 Recall 和 F1。

Q5:与项目三 Pipeline 有何异同?

项目三 流失 项目六 信用
特征 数值+类别,ColumnTransformer 纯数值 10 列
Pipeline Imputer+Scaler+OneHot Imputer+Scaler(无 OneHot)
评估 test 本步 val(三路划分)

4.14 本步小结

要点 内容
Pipeline Imputer(median) → Scaler → LR
训练 fit train 90,000
评估 val Acc=0.9337, F1=0.0753, AUC=0.7151
对照 全 0:F1=0,证明基线有效
框架 evaluate_model() 供步骤 5~8 复用
产出 step4 报告 + 图 08、09
下一步 步骤 5:不平衡策略对比

4.15 学习记录(请你填写)

  • 完成日期
  • 我原来以为 Accuracy 高就好,本步改变了什么认识
  • 混淆矩阵里我最关心的一格(TN/FP/FN/TP)
  • 疑问(如有):

文档版本:v1.3 | 最后更新:步骤 4 基线 Pipeline 教学已写入


步骤 5:不平衡策略对比

状态 :✅ 已完成教学(报告见 reports/step5_imbalance_strategies.txt,图见 10_*.png11_*.png

5.1 本步目标

步骤 4 基线 LR 在默认阈值 0.5 下 Recall 仅 4% ------模型几乎不预测违约。本步系统对比三种不平衡处理 方案,在 val 上比 F1、Recall、PR-AUC,并选定一种进入步骤 6。

要完成四件事:

  1. 策略一 :无 class_weight/SMOTE,但在 val 上调阈值(降低判违约门槛)
  2. 策略二class_weight='balanced',阈值 0.5
  3. 策略三SMOTE 放入 Pipeline,仅 train fit 时过采样
  4. 对比表 + 说明为何不能对 test 做 SMOTE + 选定最优策略

5.2 这一步在解决什么问题?

5.2.1 步骤 4 的痛点
指标 步骤 4 基线(阈值 0.5) 问题
Recall 0.04 100 个真违约只抓到 4 个
F1 0.075 极低
Accuracy 0.93 被多数类「骗高」

风控里**漏报违约(FN)**代价往往很高。本步要在「多抓违约」与「少误报」之间找更好平衡。

5.2.2 三条路各干什么?

#mermaid-svg-nn3LGRKWPEFX8KzY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nn3LGRKWPEFX8KzY .error-icon{fill:#552222;}#mermaid-svg-nn3LGRKWPEFX8KzY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nn3LGRKWPEFX8KzY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nn3LGRKWPEFX8KzY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nn3LGRKWPEFX8KzY .marker.cross{stroke:#333333;}#mermaid-svg-nn3LGRKWPEFX8KzY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nn3LGRKWPEFX8KzY p{margin:0;}#mermaid-svg-nn3LGRKWPEFX8KzY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster-label text{fill:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster-label span{color:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster-label span p{background-color:transparent;}#mermaid-svg-nn3LGRKWPEFX8KzY .label text,#mermaid-svg-nn3LGRKWPEFX8KzY span{fill:#333;color:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY .node rect,#mermaid-svg-nn3LGRKWPEFX8KzY .node circle,#mermaid-svg-nn3LGRKWPEFX8KzY .node ellipse,#mermaid-svg-nn3LGRKWPEFX8KzY .node polygon,#mermaid-svg-nn3LGRKWPEFX8KzY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nn3LGRKWPEFX8KzY .rough-node .label text,#mermaid-svg-nn3LGRKWPEFX8KzY .node .label text,#mermaid-svg-nn3LGRKWPEFX8KzY .image-shape .label,#mermaid-svg-nn3LGRKWPEFX8KzY .icon-shape .label{text-anchor:middle;}#mermaid-svg-nn3LGRKWPEFX8KzY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nn3LGRKWPEFX8KzY .rough-node .label,#mermaid-svg-nn3LGRKWPEFX8KzY .node .label,#mermaid-svg-nn3LGRKWPEFX8KzY .image-shape .label,#mermaid-svg-nn3LGRKWPEFX8KzY .icon-shape .label{text-align:center;}#mermaid-svg-nn3LGRKWPEFX8KzY .node.clickable{cursor:pointer;}#mermaid-svg-nn3LGRKWPEFX8KzY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nn3LGRKWPEFX8KzY .arrowheadPath{fill:#333333;}#mermaid-svg-nn3LGRKWPEFX8KzY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nn3LGRKWPEFX8KzY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nn3LGRKWPEFX8KzY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nn3LGRKWPEFX8KzY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nn3LGRKWPEFX8KzY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nn3LGRKWPEFX8KzY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster text{fill:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY .cluster span{color:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nn3LGRKWPEFX8KzY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nn3LGRKWPEFX8KzY rect.text{fill:none;stroke-width:0;}#mermaid-svg-nn3LGRKWPEFX8KzY .icon-shape,#mermaid-svg-nn3LGRKWPEFX8KzY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nn3LGRKWPEFX8KzY .icon-shape p,#mermaid-svg-nn3LGRKWPEFX8KzY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nn3LGRKWPEFX8KzY .icon-shape .label rect,#mermaid-svg-nn3LGRKWPEFX8KzY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nn3LGRKWPEFX8KzY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nn3LGRKWPEFX8KzY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nn3LGRKWPEFX8KzY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 策略3 SMOTE
Pipeline 内 SMOTE

仅 fit 时造样本
阈值 0.5
策略2 class_weight
LR balanced

加重违约权重
阈值 0.5
策略1 无处理+调阈值
基线 LR 训练
val 扫描 0.01~0.99
选 F1 最高阈值

如 0.13

策略 机制 通俗理解
调阈值 不改训练,把判违约门槛从 0.5 降到 0.13 「更容易报警」
class_weight 训练时违约样本错误代价 × 权重 「漏掉违约更亏」
SMOTE 在 train 里合成更多违约样本 「让模型多见违约长什么样」

5.3 名词解释(本步新术语)

名词 大白话 在本项目中 易混淆
不平衡 违约 ~7%,不违约 ~93% 步骤 1 已知 项目二更极端
class_weight='balanced' 权重 ∝ 1/类别频率,少数类更重 策略二 不是 SMOTE
SMOTE 在两条真违约之间插值「造」新违约 策略三,仅 train 不是复制 test
阈值 threshold P(违约) ≥ 阈值 → 预测 1 策略一最优 0.13 默认 0.5
PR-AUC PR 曲线下面积,不平衡时比 ROC 更靠谱 class_weight 0.326 最高 ≠ ROC-AUC
Precision 预测违约里真违约比例 调阈值 0.35 最高 高阈值通常更高
Recall 真违约里被抓住的比例 SMOTE 0.68 最高 低阈值通常更高
F1 P 与 R 的调和平均 本步选优主指标 步骤 6 仍看 F1
imblearn Pipeline SMOTE 只在 fit 执行,predict 不过采样 防泄露写法 不是 sklearn Pipeline
5.3.1 F1 vs PR-AUC:为何可能「选优不一致」?
  • F1 依赖你选的阈值(0.13 还是 0.5)
  • PR-AUC所有阈值下 Precision-Recall 的整体表现,与单一阈值无关

本步按学习路线用 F1 选策略 ;class_weight 的 PR-AUC 最高,说明其概率排序更好,但 0.5 阈值下 F1 不如调阈值------步骤 9 还可再调阈值。

5.4 为何不能对 test 做 SMOTE?(必考)

#mermaid-svg-tzH2A61V75X6GoIH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tzH2A61V75X6GoIH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tzH2A61V75X6GoIH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tzH2A61V75X6GoIH .error-icon{fill:#552222;}#mermaid-svg-tzH2A61V75X6GoIH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tzH2A61V75X6GoIH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tzH2A61V75X6GoIH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tzH2A61V75X6GoIH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tzH2A61V75X6GoIH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tzH2A61V75X6GoIH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tzH2A61V75X6GoIH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tzH2A61V75X6GoIH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tzH2A61V75X6GoIH .marker.cross{stroke:#333333;}#mermaid-svg-tzH2A61V75X6GoIH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tzH2A61V75X6GoIH p{margin:0;}#mermaid-svg-tzH2A61V75X6GoIH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tzH2A61V75X6GoIH .cluster-label text{fill:#333;}#mermaid-svg-tzH2A61V75X6GoIH .cluster-label span{color:#333;}#mermaid-svg-tzH2A61V75X6GoIH .cluster-label span p{background-color:transparent;}#mermaid-svg-tzH2A61V75X6GoIH .label text,#mermaid-svg-tzH2A61V75X6GoIH span{fill:#333;color:#333;}#mermaid-svg-tzH2A61V75X6GoIH .node rect,#mermaid-svg-tzH2A61V75X6GoIH .node circle,#mermaid-svg-tzH2A61V75X6GoIH .node ellipse,#mermaid-svg-tzH2A61V75X6GoIH .node polygon,#mermaid-svg-tzH2A61V75X6GoIH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tzH2A61V75X6GoIH .rough-node .label text,#mermaid-svg-tzH2A61V75X6GoIH .node .label text,#mermaid-svg-tzH2A61V75X6GoIH .image-shape .label,#mermaid-svg-tzH2A61V75X6GoIH .icon-shape .label{text-anchor:middle;}#mermaid-svg-tzH2A61V75X6GoIH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tzH2A61V75X6GoIH .rough-node .label,#mermaid-svg-tzH2A61V75X6GoIH .node .label,#mermaid-svg-tzH2A61V75X6GoIH .image-shape .label,#mermaid-svg-tzH2A61V75X6GoIH .icon-shape .label{text-align:center;}#mermaid-svg-tzH2A61V75X6GoIH .node.clickable{cursor:pointer;}#mermaid-svg-tzH2A61V75X6GoIH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tzH2A61V75X6GoIH .arrowheadPath{fill:#333333;}#mermaid-svg-tzH2A61V75X6GoIH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tzH2A61V75X6GoIH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tzH2A61V75X6GoIH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tzH2A61V75X6GoIH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tzH2A61V75X6GoIH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tzH2A61V75X6GoIH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tzH2A61V75X6GoIH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tzH2A61V75X6GoIH .cluster text{fill:#333;}#mermaid-svg-tzH2A61V75X6GoIH .cluster span{color:#333;}#mermaid-svg-tzH2A61V75X6GoIH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tzH2A61V75X6GoIH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tzH2A61V75X6GoIH rect.text{fill:none;stroke-width:0;}#mermaid-svg-tzH2A61V75X6GoIH .icon-shape,#mermaid-svg-tzH2A61V75X6GoIH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tzH2A61V75X6GoIH .icon-shape p,#mermaid-svg-tzH2A61V75X6GoIH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tzH2A61V75X6GoIH .icon-shape .label rect,#mermaid-svg-tzH2A61V75X6GoIH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tzH2A61V75X6GoIH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tzH2A61V75X6GoIH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tzH2A61V75X6GoIH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ❌ 错误
全量 SMOTE
再划分
泄露 + 分布失真
✅ 正确
划分 train/val/test
仅 train fit 时 SMOTE
val/test 保持真实 6.68% 违约率

  1. 分布失真:test 违约率会被人为拉到 50%,不再代表真实业务
  2. 数据泄露:合成样本用了本属于 test 区域的信息,分数虚高
  3. 评估无效:上线后没有 SMOTE 过的客户,模型表现对不上

口诀先划分,再 SMOTE;只 SMOTE train,val/test 原样评估。

5.5 代码解读

函数 作用
build_baseline_pipeline(class_weight=...) 策略一/二共用结构
build_smote_pipeline Imputer→Scaler→SMOTE→LR
scan_thresholds_on_val val 上 0.01~0.99 扫 F1
fit_and_evaluate_strategy fit train + val 评估
select_best_strategy_by_f1 步骤 6 沿用最优

SMOTE Pipeline 关键imblearn.pipeline.Pipelinepredict自动跳过 SMOTE,只在 fit 时对训练集过采样。

5.6 实验结果(你的环境)

5.6.1 验证集对比
策略 阈值 F1 Recall PR-AUC Precision
无处理+调阈值 0.13 0.3355 0.3187 0.2514 0.3542
class_weight=balanced 0.50 0.2815 0.6658 0.3261 0.1785
SMOTE+LR 0.50 0.2675 0.6763 0.3134 0.1667

★ 选定策略无处理+调阈值 (val F1 最高 0.3355

对比步骤 4 基线 F1 0.075 → 调阈值后 0.336 ,提升约 4.5 倍

5.6.2 如何读这张表?
  • 调阈值 :F1、Precision 最好,Recall 中等------综合最均衡
  • class_weight :Recall 67% 高,但 Precision 仅 18%------误报多
  • SMOTE :Recall 最高 68%,F1 反而最低------合成样本 + 0.5 阈值在本数据上不占优

结论 :没有「万能策略」,需按业务看 FN/FP 代价;本教学项目按 F1 选「无处理+调阈值」 进入步骤 6。

5.7 输出文件

文件 说明
reports/step5_imbalance_strategies.txt 对比表 + SMOTE 防泄露说明 + 选定策略
10_三种不平衡策略验证集对比.png F1 / Recall / PR-AUC 柱状图
11_选定策略验证集混淆矩阵.png 最优策略混淆矩阵

5.8 动手任务

任务 A:三种策略分别改训练还是改预测?各一句话说明。

任务 B :策略一最优阈值为何是 0.13 而不是 0.5?

任务 C:class_weight 的 Recall 比调阈值高很多,为何 F1 反而更低?

任务 D:用两句话解释:为何不能对 test 做 SMOTE?

任务 E:PR-AUC 最高的是哪种策略?与 F1 最高为何不是同一种?

任务 F:步骤 6 将沿用哪种策略?阈值多少?

5.9 验收标准

  • 能说出三种策略的名称与核心机制
  • 能解释 SMOTE 只对 train、不对 val/test 的原因
  • 能读出对比表中 F1 / Recall / PR-AUC 的大致数值
  • 知道本步选优指标是 val F1
  • 知道选定策略为「无处理+调阈值」,阈值 0.13
  • 成功生成 step5 报告 + 图 10、11

5.10 动手任务与验收标准参考答案

任务 参考答案
A ①调阈值:只改预测 (门槛);②class_weight:改训练 权重;③SMOTE:改训练集样本量(仅 fit)。
B 违约是少数类,模型默认输出概率整体偏低;降到 0.13 才在 val 上达到 F1 最优,相当于「更积极报违约」。
C Recall 高但 Precision 很低(0.18)------预测了很多假违约;F1 是 P 和 R 的调和平均,一侧太低会拖垮 F1。
D ①test 过采样会改变真实违约比例,评估不代表上线;②合成过程会造成信息泄露,指标不可信。
E class_weight PR-AUC 0.326 最高;F1 依赖具体阈值,调阈值在 0.13 时 F1 更优。
F 无处理+调阈值 ,阈值 0.13

5.11 常见问题

Q1:能否三种策略都调阈值再比?

可以,但学习路线要求策略二/三用 0.5,突出机制差异;步骤 9 可对最终模型再统一调阈值。

Q2:SMOTE 在本项目为何不如项目二效果好?

项目二欺诈 0.17% 极不平衡;本项目违约 6.68%,SMOTE 收益较小,且易增加误报。

Q3:在 val 上调阈值算不算泄露?

本步允许 在 val 上选阈值(val 职责就是调参);test 仍封箱到步骤 9。

Q4:train 上 SMOTE 后多少条?

83,984 × 2 ≈ 168k(多数类数 ≈ 违约类被过采样到与多数类相同)。

5.12 本步小结

要点 内容
策略 调阈值 / class_weight / SMOTE
最优 无处理+调阈值 ,thr=0.13 ,val F1=0.3355
对比步骤 4 F1 从 0.075 → 0.336
铁律 SMOTE 仅 train fit;禁止 test/val 过采样
产出 step5 报告 + 图 10、11
下一步 步骤 6:K 折 CV 多模型

5.13 学习记录(请你填写)

  • 完成日期
  • 我最意外的结果(如:SMOTE 不是最好):
  • 若银行更怕漏报,我会选哪种策略
  • 疑问(如有):

文档版本:v1.4 | 最后更新:步骤 5 不平衡策略对比教学已写入


步骤 6:K 折 CV 多模型

状态 :✅ 已完成教学(报告见 reports/step6_cv_model_comparison.txt,图见 12_五折CV模型F1对比.png

6.1 本步目标

步骤 5 选定了不平衡策略(无处理+调阈值 0.13 ),但仍在单一 LR 上。本步要回答:哪种模型更稳、更强?

要完成四件事:

  1. train 上做 StratifiedKFold(5)(val/test 不参与)
  2. 对比 LR / RandomForest / HistGradientBoosting(均在 Pipeline 内)
  3. 报告各模型 CV F1 均值 ± 标准差 (阈值固定 0.13
  4. 选定 CV 最优模型,供 步骤 7 GridSearch 使用

6.2 这一步在解决什么问题?

6.2.1 单次划分可能「运气好」

步骤 5 在 val 上 LR F1=0.3355 。但这是一次 hold-out 的结果------若 val 里恰好「好猜」或「难猜」,分数会偏高或偏低。

交叉验证(CV) :把 train 切成 5 份,轮流当「小考卷」,考 5 次取平均------更稳定地估计泛化能力。

本步对照:

评估方式 LR F1
单次 val(步骤 5) 0.3355
5 折 CV 均值 0.3042 ± 0.012

val 比 CV 均值偏高------说明单次 val 可能略「运气好」;做模型选型应更信 CV。

6.2.2 为何要试三种模型?
模型 特点 适合
LogisticRegression 线性、可解释、快 基线
RandomForest 多棵树投票、非线性 特征交互
HistGradientBoosting 梯度提升、常很强 表格竞赛常用

三者共用 Imputer → Scaler → 分类器,保证预处理一致、对比公平。
#mermaid-svg-DEgCO4xm1lP35dBo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DEgCO4xm1lP35dBo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DEgCO4xm1lP35dBo .error-icon{fill:#552222;}#mermaid-svg-DEgCO4xm1lP35dBo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DEgCO4xm1lP35dBo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DEgCO4xm1lP35dBo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DEgCO4xm1lP35dBo .marker.cross{stroke:#333333;}#mermaid-svg-DEgCO4xm1lP35dBo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DEgCO4xm1lP35dBo p{margin:0;}#mermaid-svg-DEgCO4xm1lP35dBo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DEgCO4xm1lP35dBo .cluster-label text{fill:#333;}#mermaid-svg-DEgCO4xm1lP35dBo .cluster-label span{color:#333;}#mermaid-svg-DEgCO4xm1lP35dBo .cluster-label span p{background-color:transparent;}#mermaid-svg-DEgCO4xm1lP35dBo .label text,#mermaid-svg-DEgCO4xm1lP35dBo span{fill:#333;color:#333;}#mermaid-svg-DEgCO4xm1lP35dBo .node rect,#mermaid-svg-DEgCO4xm1lP35dBo .node circle,#mermaid-svg-DEgCO4xm1lP35dBo .node ellipse,#mermaid-svg-DEgCO4xm1lP35dBo .node polygon,#mermaid-svg-DEgCO4xm1lP35dBo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DEgCO4xm1lP35dBo .rough-node .label text,#mermaid-svg-DEgCO4xm1lP35dBo .node .label text,#mermaid-svg-DEgCO4xm1lP35dBo .image-shape .label,#mermaid-svg-DEgCO4xm1lP35dBo .icon-shape .label{text-anchor:middle;}#mermaid-svg-DEgCO4xm1lP35dBo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DEgCO4xm1lP35dBo .rough-node .label,#mermaid-svg-DEgCO4xm1lP35dBo .node .label,#mermaid-svg-DEgCO4xm1lP35dBo .image-shape .label,#mermaid-svg-DEgCO4xm1lP35dBo .icon-shape .label{text-align:center;}#mermaid-svg-DEgCO4xm1lP35dBo .node.clickable{cursor:pointer;}#mermaid-svg-DEgCO4xm1lP35dBo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DEgCO4xm1lP35dBo .arrowheadPath{fill:#333333;}#mermaid-svg-DEgCO4xm1lP35dBo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DEgCO4xm1lP35dBo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DEgCO4xm1lP35dBo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DEgCO4xm1lP35dBo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DEgCO4xm1lP35dBo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DEgCO4xm1lP35dBo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DEgCO4xm1lP35dBo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DEgCO4xm1lP35dBo .cluster text{fill:#333;}#mermaid-svg-DEgCO4xm1lP35dBo .cluster span{color:#333;}#mermaid-svg-DEgCO4xm1lP35dBo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DEgCO4xm1lP35dBo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DEgCO4xm1lP35dBo rect.text{fill:none;stroke-width:0;}#mermaid-svg-DEgCO4xm1lP35dBo .icon-shape,#mermaid-svg-DEgCO4xm1lP35dBo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DEgCO4xm1lP35dBo .icon-shape p,#mermaid-svg-DEgCO4xm1lP35dBo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DEgCO4xm1lP35dBo .icon-shape .label rect,#mermaid-svg-DEgCO4xm1lP35dBo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DEgCO4xm1lP35dBo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DEgCO4xm1lP35dBo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DEgCO4xm1lP35dBo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤7复核
步骤9
train 90,000
StratifiedKFold 5折
每折: fit Pipeline

predict_proba

阈值0.13 → F1
5折 F1 均值 ± 标准差
选最优 → 步骤7 GridSearch
val 30,000
test 封箱
不参与本步

6.3 名词解释(本步新术语)

名词 大白话 在本项目中 易混淆
K 折交叉验证 数据分 K 份,轮流 1 份验证、K-1 份训练 K=5 不是 K-Means 的 K
StratifiedKFold 每折保持违约率 ≈ 6.68% 不平衡必用 普通 KFold 可能某折失衡
fold 折 一次「训练+验证」轮回 共 5 次
mean ± std 5 次 F1 的平均与波动 HGB 0.4116±0.0045 std 小=更稳
RandomForest 很多决策树「投票」 n_estimators=100 不是单棵树
HistGradientBoosting 逐棵树修正前一棵的错误 max_iter=100 sklearn 新版 GBR
泛化 没见过的数据的表现 CV 估计泛化 不是 train 分数

6.4 CV 流程(Mermaid)

#mermaid-svg-JfcZQZ3dgbUF4cMS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JfcZQZ3dgbUF4cMS .error-icon{fill:#552222;}#mermaid-svg-JfcZQZ3dgbUF4cMS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JfcZQZ3dgbUF4cMS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .marker.cross{stroke:#333333;}#mermaid-svg-JfcZQZ3dgbUF4cMS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JfcZQZ3dgbUF4cMS p{margin:0;}#mermaid-svg-JfcZQZ3dgbUF4cMS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster-label text{fill:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster-label span{color:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster-label span p{background-color:transparent;}#mermaid-svg-JfcZQZ3dgbUF4cMS .label text,#mermaid-svg-JfcZQZ3dgbUF4cMS span{fill:#333;color:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .node rect,#mermaid-svg-JfcZQZ3dgbUF4cMS .node circle,#mermaid-svg-JfcZQZ3dgbUF4cMS .node ellipse,#mermaid-svg-JfcZQZ3dgbUF4cMS .node polygon,#mermaid-svg-JfcZQZ3dgbUF4cMS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .rough-node .label text,#mermaid-svg-JfcZQZ3dgbUF4cMS .node .label text,#mermaid-svg-JfcZQZ3dgbUF4cMS .image-shape .label,#mermaid-svg-JfcZQZ3dgbUF4cMS .icon-shape .label{text-anchor:middle;}#mermaid-svg-JfcZQZ3dgbUF4cMS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .rough-node .label,#mermaid-svg-JfcZQZ3dgbUF4cMS .node .label,#mermaid-svg-JfcZQZ3dgbUF4cMS .image-shape .label,#mermaid-svg-JfcZQZ3dgbUF4cMS .icon-shape .label{text-align:center;}#mermaid-svg-JfcZQZ3dgbUF4cMS .node.clickable{cursor:pointer;}#mermaid-svg-JfcZQZ3dgbUF4cMS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .arrowheadPath{fill:#333333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JfcZQZ3dgbUF4cMS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JfcZQZ3dgbUF4cMS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JfcZQZ3dgbUF4cMS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster text{fill:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS .cluster span{color:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JfcZQZ3dgbUF4cMS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JfcZQZ3dgbUF4cMS rect.text{fill:none;stroke-width:0;}#mermaid-svg-JfcZQZ3dgbUF4cMS .icon-shape,#mermaid-svg-JfcZQZ3dgbUF4cMS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JfcZQZ3dgbUF4cMS .icon-shape p,#mermaid-svg-JfcZQZ3dgbUF4cMS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JfcZQZ3dgbUF4cMS .icon-shape .label rect,#mermaid-svg-JfcZQZ3dgbUF4cMS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JfcZQZ3dgbUF4cMS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JfcZQZ3dgbUF4cMS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JfcZQZ3dgbUF4cMS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 每一折
80% train折

fit Pipeline
20% val折

predict_proba
阈值0.13 → F1
5折 F1 求 mean/std

关键 :每折 重新 new 一个 Pipeline ,Imputer/Scaler 只在该折训练部分 fit------无泄露

6.5 代码解读

函数 作用
build_rf_pipeline / build_hist_gbr_pipeline 步骤 6 新增两模型
cross_validate_f1_at_threshold 单模型 5 折 CV
run_multi_model_cv 三模型批量 CV
select_best_model_by_cv_f1 步骤 7 沿用

6.6 实验结果(你的环境)

模型 CV F1 均值 标准差 各折 F1
LogisticRegression 0.3042 0.0119 0.315, 0.319, 0.293, 0.289, 0.305
RandomForest 0.3721 0.0039 0.374, 0.373, 0.367, 0.368, 0.378
HistGradientBoosting 0.4116 0.0045 0.416, 0.414, 0.413, 0.412, 0.403

★ CV 最优HistGradientBoosting (F1 比 LR 高约 35% ,比 RF 高约 11%

稳定性 :HGB 与 RF 的 std < 0.005 ,五折很稳;LR std 0.012,波动稍大。

6.7 输出文件

文件 说明
reports/step6_cv_model_comparison.txt CV 对比表 + 最优模型
12_五折CV模型F1对比.png 三模型 F1 均值±标准差柱状图

6.8 动手任务

任务 A :为何本步 CV 只在 train 上,不用 val?

任务 B:StratifiedKFold 与普通 KFold 有何区别?

任务 C:LR 单次 val F1=0.3355,CV mean=0.3042,哪个更可信?为什么?

任务 D:三模型 Pipeline 前两步为何相同?

任务 E:步骤 7 将对哪个模型做 GridSearch?

任务 F:若某模型 5 折 F1 为 0.40, 0.10, 0.38, 0.39, 0.41,敢选它吗?

6.9 验收标准

  • 能解释 K 折 CV 的目的(稳定估计泛化)
  • 能说出三模型名称与 CV F1 大致排序
  • 知道 CV 在 train、val 留给步骤 7、test 仍封箱
  • 知道最优模型是 HistGradientBoosting
  • 理解「单次 val 可能运气好」
  • 成功生成 step6 报告 + 图 12

6.10 动手任务与验收标准参考答案

任务 参考答案
A val 留给步骤 7 GridSearch 外层复核;test 步骤 9;CV 在 train 内不浪费 hold-out。
B Stratified 保证每折违约率≈总体;普通 KFold 可能某折违约过少/过多。
C CV mean 更可信;val 只是一次抽样,本例 val 略高于 CV mean,可能略幸运。
D 公平对比:同一 Imputer+Scaler,差异只在分类器;且防泄露结构一致。
E HistGradientBoosting(CV F1 最高 0.4116)。
F 不敢------第二折 0.10 说明极不稳定,mean 会被误导,应看 std 和每折。

6.11 常见问题

Q1:为何不对 val+train 合并做 CV?

合并后 val 被吃进 CV,步骤 7 无独立 hold-out 复核;教学上保留 val 更清晰。

Q2:树模型还要 StandardScaler 吗?

不强制,但三模型统一 Pipeline 结构,步骤 7 换模型不用改预处理。

Q3:CV 里要不要再调阈值?

本步固定步骤 5 的 0.13 ,专注比模型;步骤 9 可再调阈值。

Q4:HGB 比 LR 好多少?

CV F1:0.4116 vs 0.3042,约 +35% 相对提升。

6.12 本步小结

要点 内容
CV train 5 折 StratifiedKFold
阈值 0.13(步骤 5)
最优 HistGradientBoosting F1=0.4116±0.0045
排序 HGB > RF > LR
对照 val LR F1=0.3355 > CV mean 0.3042
产出 step6 报告 + 图 12
下一步 步骤 7:GridSearchCV

6.13 学习记录(请你填写)

  • 完成日期
  • 我之前以为 LR 够用了,本步改变了什么
  • CV 五折分数里我最关注什么(mean 还是 std)
  • 疑问(如有):

文档版本:v1.5 | 最后更新:步骤 6 K 折 CV 多模型教学已写入


步骤 7:GridSearchCV

状态 :✅ 已完成教学(报告见 reports/step7_gridsearch_hgb.txt,图见 13_*.png14_*.png

7.1 本步目标

步骤 6 选出 HistGradientBoosting ,但用的是默认超参 (max_iter=100 等)。本步用 GridSearchCV 在 train 内系统搜参,并在 val 上复核。

要完成四件事:

  1. 对 HGB Pipeline 做 GridSearchCV(12 组超参 × 5 折)
  2. 输出 best_params_best_score_
  3. val 上评估调参后模型(阈值 0.13
  4. 验收:val F1 优于步骤 4 基线(0.0753)

7.2 这一步在解决什么问题?

7.2.1 超参数 vs 模型参数
类型 谁学 例子 能否用 GridSearch 在 test 上选
模型参数 算法 fit 学 LR 的权重 w
超参数 人要指定 max_depth、learning_rate ❌ 应在 train CV 内

GridSearchCV = 穷举(或网格)尝试多组超参,用 CV 分数选最好的一组------像「自动试穿搭,但只在训练集内部选秀」。

7.2.2 两层验证结构

#mermaid-svg-7pOXtv1xc6cWpfaR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7pOXtv1xc6cWpfaR .error-icon{fill:#552222;}#mermaid-svg-7pOXtv1xc6cWpfaR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7pOXtv1xc6cWpfaR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7pOXtv1xc6cWpfaR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7pOXtv1xc6cWpfaR .marker.cross{stroke:#333333;}#mermaid-svg-7pOXtv1xc6cWpfaR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7pOXtv1xc6cWpfaR p{margin:0;}#mermaid-svg-7pOXtv1xc6cWpfaR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster-label text{fill:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster-label span{color:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster-label span p{background-color:transparent;}#mermaid-svg-7pOXtv1xc6cWpfaR .label text,#mermaid-svg-7pOXtv1xc6cWpfaR span{fill:#333;color:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR .node rect,#mermaid-svg-7pOXtv1xc6cWpfaR .node circle,#mermaid-svg-7pOXtv1xc6cWpfaR .node ellipse,#mermaid-svg-7pOXtv1xc6cWpfaR .node polygon,#mermaid-svg-7pOXtv1xc6cWpfaR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7pOXtv1xc6cWpfaR .rough-node .label text,#mermaid-svg-7pOXtv1xc6cWpfaR .node .label text,#mermaid-svg-7pOXtv1xc6cWpfaR .image-shape .label,#mermaid-svg-7pOXtv1xc6cWpfaR .icon-shape .label{text-anchor:middle;}#mermaid-svg-7pOXtv1xc6cWpfaR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7pOXtv1xc6cWpfaR .rough-node .label,#mermaid-svg-7pOXtv1xc6cWpfaR .node .label,#mermaid-svg-7pOXtv1xc6cWpfaR .image-shape .label,#mermaid-svg-7pOXtv1xc6cWpfaR .icon-shape .label{text-align:center;}#mermaid-svg-7pOXtv1xc6cWpfaR .node.clickable{cursor:pointer;}#mermaid-svg-7pOXtv1xc6cWpfaR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7pOXtv1xc6cWpfaR .arrowheadPath{fill:#333333;}#mermaid-svg-7pOXtv1xc6cWpfaR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7pOXtv1xc6cWpfaR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7pOXtv1xc6cWpfaR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7pOXtv1xc6cWpfaR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7pOXtv1xc6cWpfaR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7pOXtv1xc6cWpfaR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster text{fill:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR .cluster span{color:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7pOXtv1xc6cWpfaR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7pOXtv1xc6cWpfaR rect.text{fill:none;stroke-width:0;}#mermaid-svg-7pOXtv1xc6cWpfaR .icon-shape,#mermaid-svg-7pOXtv1xc6cWpfaR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7pOXtv1xc6cWpfaR .icon-shape p,#mermaid-svg-7pOXtv1xc6cWpfaR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7pOXtv1xc6cWpfaR .icon-shape .label rect,#mermaid-svg-7pOXtv1xc6cWpfaR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7pOXtv1xc6cWpfaR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7pOXtv1xc6cWpfaR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7pOXtv1xc6cWpfaR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 步骤9
train 90k
GridSearchCV

5折CV选超参
best_estimator_

在全 train refit
val 30k

外层复核一次
test 封箱
不用

  • 内层 :GridSearch 的 5 折 CV → 得到 best_score_
  • 外层:val 复核 → 看调参后真实 hold-out 表现
  • test:仍不参与

7.3 名词解释

名词 大白话 在本项目中
GridSearchCV 网格搜索 + 交叉验证 12 组 × 5 折 = 60 次 fit
param_grid 要试的超参组合 max_iter, max_depth, learning_rate
classifier__前缀 Pipeline 里指定某步的参数 classifier__max_depth
best_params_ CV 最优那组超参 lr=0.05, depth=10, iter=100
best_score_ 最优组的 CV 平均 F1 0.4135
refit=True 选完超参后用全 train 再训一遍 GridSearch 默认
learning_rate 梯度提升每步「迈多大」 越小越稳、常需更多 iter
max_depth 单棵树最大深度 10 比 None 更防过拟合

7.4 参数网格

python 复制代码
{
    "classifier__max_iter": [100, 200],
    "classifier__max_depth": [None, 10, 15],
    "classifier__learning_rate": [0.05, 0.1],
}

2×3×2=12 组;评分函数用 违约 F1 @ 阈值 0.13(与步骤 5~6 一致)。

7.5 实验结果(你的环境)

7.5.1 GridSearch 最优
best_params_ learning_rate=0.05 , max_depth=10 , max_iter=100
best_score_ 0.4135(train 内 5 折 CV F1)
7.5.2 val 对比(阈值 0.13)
阶段 val F1
步骤 4 基线 LR 0.0753
HGB 调参前(步骤 6 默认) 0.4250
HGB 调参后 0.4270

验收通过 :0.4270 >> 0.0753(提升约 +0.35)。

7.5.3 调参后 val 完整指标
指标 数值
F1 0.4270
Recall 0.6284
Precision 0.3233
PR-AUC 0.4065
ROC-AUC 0.8659
7.5.4 诚实结论

调参后 F1 0.4270 vs 调参前 0.4250 ,仅提升 0.002 ------说明步骤 6 默认超参已接近最优 ;GridSearch 的价值在于系统化验证,而非必然大幅提升。

7.6 输出文件

文件 说明
reports/step7_gridsearch_hgb.txt 网格、最优参数、val 对比
13_GridSearch前后验证集F1对比.png 基线 / 调参前 / 调参后
14_GridSearch最优HGB验证集混淆矩阵.png 最优模型混淆矩阵

7.7 动手任务

任务 Aclassifier__max_depth 前缀为何必要?

任务 B:best_score_ 与 val F1 为何可能不同?

任务 C:本步为何不在 test 上选超参?

任务 D:最优 learning_rate=0.05 比 0.1 慢还是快?含义?

任务 E:调参提升很小说明什么?

7.8 验收标准

  • 能解释 GridSearchCV 与超参数含义
  • 能说出 best_params_ 与 best_score_
  • 知道超参在 train CV 内选、val 外层复核
  • 调参后 val F1 优于步骤 4 基线 0.0753
  • 成功生成 step7 报告 + 图 13、14

7.9 动手任务与验收标准参考答案

任务 参考答案
A Pipeline 多步同名参数会冲突;classifier__ 指定改 HistGradientBoosting 那一步。
B best_score_ 是 train 内 CV 平均 ;val 是另一次 hold-out,分布不同故可略有差异。
C test 选超参 = 偷看最终考卷,步骤 9 评估会虚高(泄露)。
D 0.05 更慢更稳(每棵树贡献更小),常配合更多 max_iter;本网格下 100 次已够。
E 默认超参合理;搜参确认无大幅遗漏;工业上仍应做 GridSearch 再下结论。

7.10 本步小结

要点 内容
对象 HistGradientBoosting Pipeline
best_params lr=0.05, depth=10, iter=100
CV best_score 0.4135
val F1 调参前 0.4250 → 调参后 0.4270
验收 远优于步骤 4 基线 0.0753
产出 step7 报告 + 图 13、14
下一步 步骤 8:Stacking 集成

7.11 学习记录(请你填写)

  • 完成日期
  • GridSearch 跑多久?best_params 是否符合直觉
  • 疑问(如有):

文档版本:v1.6 | 最后更新:步骤 7 GridSearchCV 教学已写入


步骤 8:Stacking 集成

状态 :✅ 已完成教学(报告见 reports/step8_stacking_comparison.txt,图见 15_*.png16_*.png

8.1 本步目标

步骤 7 的 调参 HGB 已是强单模型。本步尝试 Stacking 集成 :多个基学习器「各抒己见」,再由元学习器学如何组合。

要完成四件事:

  1. 3 个基学习器 (LR、RF、调参 HGB)+ LR 元学习器
  2. 使用 StackingClassifier(内层 CV 生成 OOF 概率)
  3. 5 折 CV 对比:单模型 vs Stacking
  4. val 复核;如实记录是否有提升

8.2 Stacking 是什么?(大白话)

#mermaid-svg-xrJLgUVobxjuQvif{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xrJLgUVobxjuQvif .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xrJLgUVobxjuQvif .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xrJLgUVobxjuQvif .error-icon{fill:#552222;}#mermaid-svg-xrJLgUVobxjuQvif .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xrJLgUVobxjuQvif .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xrJLgUVobxjuQvif .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xrJLgUVobxjuQvif .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xrJLgUVobxjuQvif .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xrJLgUVobxjuQvif .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xrJLgUVobxjuQvif .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xrJLgUVobxjuQvif .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xrJLgUVobxjuQvif .marker.cross{stroke:#333333;}#mermaid-svg-xrJLgUVobxjuQvif svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xrJLgUVobxjuQvif p{margin:0;}#mermaid-svg-xrJLgUVobxjuQvif .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xrJLgUVobxjuQvif .cluster-label text{fill:#333;}#mermaid-svg-xrJLgUVobxjuQvif .cluster-label span{color:#333;}#mermaid-svg-xrJLgUVobxjuQvif .cluster-label span p{background-color:transparent;}#mermaid-svg-xrJLgUVobxjuQvif .label text,#mermaid-svg-xrJLgUVobxjuQvif span{fill:#333;color:#333;}#mermaid-svg-xrJLgUVobxjuQvif .node rect,#mermaid-svg-xrJLgUVobxjuQvif .node circle,#mermaid-svg-xrJLgUVobxjuQvif .node ellipse,#mermaid-svg-xrJLgUVobxjuQvif .node polygon,#mermaid-svg-xrJLgUVobxjuQvif .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xrJLgUVobxjuQvif .rough-node .label text,#mermaid-svg-xrJLgUVobxjuQvif .node .label text,#mermaid-svg-xrJLgUVobxjuQvif .image-shape .label,#mermaid-svg-xrJLgUVobxjuQvif .icon-shape .label{text-anchor:middle;}#mermaid-svg-xrJLgUVobxjuQvif .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xrJLgUVobxjuQvif .rough-node .label,#mermaid-svg-xrJLgUVobxjuQvif .node .label,#mermaid-svg-xrJLgUVobxjuQvif .image-shape .label,#mermaid-svg-xrJLgUVobxjuQvif .icon-shape .label{text-align:center;}#mermaid-svg-xrJLgUVobxjuQvif .node.clickable{cursor:pointer;}#mermaid-svg-xrJLgUVobxjuQvif .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xrJLgUVobxjuQvif .arrowheadPath{fill:#333333;}#mermaid-svg-xrJLgUVobxjuQvif .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xrJLgUVobxjuQvif .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xrJLgUVobxjuQvif .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xrJLgUVobxjuQvif .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xrJLgUVobxjuQvif .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xrJLgUVobxjuQvif .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xrJLgUVobxjuQvif .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xrJLgUVobxjuQvif .cluster text{fill:#333;}#mermaid-svg-xrJLgUVobxjuQvif .cluster span{color:#333;}#mermaid-svg-xrJLgUVobxjuQvif div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xrJLgUVobxjuQvif .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xrJLgUVobxjuQvif rect.text{fill:none;stroke-width:0;}#mermaid-svg-xrJLgUVobxjuQvif .icon-shape,#mermaid-svg-xrJLgUVobxjuQvif .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xrJLgUVobxjuQvif .icon-shape p,#mermaid-svg-xrJLgUVobxjuQvif .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xrJLgUVobxjuQvif .icon-shape .label rect,#mermaid-svg-xrJLgUVobxjuQvif .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xrJLgUVobxjuQvif .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xrJLgUVobxjuQvif .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xrJLgUVobxjuQvif :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户特征 X
基模型1

LR Pipeline
基模型2

RF Pipeline
基模型3

HGB Pipeline
P(违约)₁
P(违约)₂
P(违约)₃
元学习器 LR

学如何加权组合
最终 P(违约)

专家委员会:三个专家分别给意见(三个概率),主席(元 LR)学习「听谁的更靠谱」。

多样性 很重要:三个模型越不一样,集成越可能增益;若三人意见总相同,加再多也没用。

8.3 名词解释

名词 大白话 在本项目中
Stacking 堆叠:基模型输出 → 元模型再学 sklearn StackingClassifier
基学习器 第一层模型 LR、RF、调参 HGB
元学习器 第二层,组合基模型预测 LogisticRegression
OOF Out-Of-Fold,折外预测 内层 5 折生成训练元特征
stack_method 基模型贡献什么 predict_proba → 违约概率
集成 多模型组合降方差/提稳健 本步 CV F1 +0.024

8.4 实验结果(你的环境)

8.4.1 Train 5 折 CV
方案 CV F1 均值 标准差
调参 HGB 单模型 0.4135 0.0031
Stacking 0.4371 0.0060
提升 +0.0236
8.4.2 验证集 val(阈值 0.13)
方案 val F1 Recall
调参 HGB 0.4270 0.6284
Stacking 0.4528 0.4988
Δ F1 +0.0258 Recall 下降

结论 :Stacking 在 CV 与 val 的 F1 均提升 ,集成有效

注意 :Stacking 的 Recall 更低 (0.50 vs 0.63)------F1 升来自 Precision/Recall 权衡变化;步骤 9 将按业务成本再选阈值。

8.5 输出文件

文件 说明
reports/step8_stacking_comparison.txt CV + val 对比与结论
15_Stacking与单模型CV及验证集对比.png CV / val F1 双图
16_Stacking验证集混淆矩阵.png Stacking val 混淆矩阵

8.6 动手任务

任务 A:Stacking 三层结构各是什么?

任务 B:为何基学习器要用不同算法(LR/RF/HGB)?

任务 C:本例 Stacking F1 提升但 Recall 下降,银行更怕漏报时应怎么办?

任务 D:若 Stacking 无提升说明什么?(预习:本例有提升)

8.7 验收标准

  • 能画出 Stacking 基模型 → 元模型流程
  • 能说出三基学习器 + 元学习器
  • 知道 CV 与 val 对比结果及是否提升
  • 理解「无提升也如实写」的验收要求
  • 成功生成 step8 报告 + 图 15、16

8.8 参考答案摘要

任务 参考答案
A 基:LR/RF/HGB Pipeline;元:LR 组合三份违约概率。
B 多样性:线性/树/提升树错误模式不同,集成才互补。
C 步骤 9 在 val 上按成本矩阵扫阈值,或选单模型若 Recall 更重要。
D 单模型已够强、基模型太相似、或数据噪声大时集成可能无效。

8.9 本步小结

要点 内容
结构 LR+RF+HGB → LR 元学习器
CV F1 0.4135 → 0.4371(+0.024)
val F1 0.4270 → 0.4528(+0.026)
结论 集成有效;Recall 需步骤 9 权衡
产出 step8 报告 + 图 15、16
下一步 步骤 9:业务成本 + test 一次评估

文档版本:v1.7 | 最后更新:步骤 8 Stacking 集成教学已写入


步骤 9:业务阈值与成本

状态 :✅ 已完成(step9_business_threshold_test_eval.txt17_*.png18_*.png

9.1 本步目标

业务成本 (非纯 F1)在 val 上选阈值,test 仅评估一次

9.2 成本矩阵

  • FN(漏放违约)× 10 + FP(误拒好客户)× 1
  • 总成本最小 → 业务最优阈值 0.06(低于 F1 阈值 0.13)

9.3 结果

阈值 val F1 test F1 test Recall
F1 参考 0.13 0.4528 --- ---
业务最优 0.06 0.4012 0.3955 0.674

test ROC-AUC=0.8652


步骤 10:部署与复盘

状态 :✅ 已完成(credit_risk_best.pklLEARNING_SUMMARY.md

  • train+val 重训 Stacking → models/credit_risk_best.pkl
  • score_applicants() 批量打分
  • 复盘见 LEARNING_SUMMARY.md

项目六 10 步全部完成。


文档版本:v2.0 | 项目六全部 10 步教学已完成