项目六教学文档:信用违约风险综合建模 ------ 高难度综合收官
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_违约标签分布.png、02_各列缺失值概况.png)
1.1 本步目标
完成以下四件事,为后续 9 步打地基:
- 创建
project_06_credit_risk/标准目录(与项目 1~5 一致) - 加载 Give Me Some Credit 数据集(约 15 万条客户记录)
- 理解 每一列的业务含义------尤其是标签 y 是什么
- 确认 数据规模、违约比例、缺失值概况,并保存中文图表与文本报告
做完本步,你应该能向别人讲清楚:我们在预测什么?数据长什么样?有什么明显问题(不平衡、缺失)?
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% 欺诈) |
| 缺失值 | 表格里空着的格子 | 填错会误导模型 | MonthlyIncome 缺 19.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 做法相同。
1.7.2 print_field_dictionary() ------ 数据字典
把 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 | MonthlyIncome 缺 29,731(19.82%) ;NumberOfDependents 缺 3,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,图见03~06_*.png)
2.1 本步目标
在步骤 1「知道有哪些列、多少缺失」的基础上,本步要深入看数据在说什么:
- 哪些特征与违约关系最强?(相关性排序)
- 违约的人 和不违约的人,关键特征长什么样?(箱线图对比)
- 有没有异常值 / 哨兵值(age=0、使用率>1、逾期次数=98)?
- 总结至少 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) | 逾期列 98 各 264 条 | 不能当真实「逾期 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 核心发现(报告原文)
- 目标严重不平衡:违约率 6.68%,不能只看 Accuracy。
- 与违约最相关 Top1:30-59 天逾期次数 (r=0.126)。
- MonthlyIncome 缺失 19.8%,Pipeline 需 Imputer。
- age=0 共 1 条,明显异常。
- 信用使用率 >1 有 3321 条,分布右偏、含极端值。
- 逾期 =98 各 264 条,疑为哨兵值,后续需处理策略。
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 万行 上看数据。从本步起,必须先把数据切成三份,并写清楚规矩:哪些操作只能在哪一份上做。
本步要完成四件事:
- 划分 train 60% / val 20% / test 20%,且用
stratify=y保持违约率一致 - 验收 三路样本量与正负比例接近总体(约 6.68% 违约)
- 打印并保存 「数据泄露检查清单」------项目六后续 7 步的铁律
- 生成中文对比图与
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,错在哪一步?
任务 D :stratify=y 去掉后,重新运行(可临时改代码),test 违约率可能偏离 6.68% 吗?说明原因。
任务 E:步骤 2 的 EDA 用了全量数据,算不算泄露?为什么?
任务 F:背诵泄露口诀,并写出 test 集在项目六哪一步才「开封」使用。
3.11 验收标准
- 能解释 train / val / test 各自职责
- 能说出划分比例 60/20/20 与样本量 90k/30k/30k
- 能解释
stratify=y与random_state=42的作用 - 能列举至少 3 条泄露清单并举例
- 知道本步未训练模型,只划分 + 定规则
- 成功生成
07_*.png与step3_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_idx 到 outputs/ 便于团队协作。
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_*.png、09_*.png)
4.1 本步目标
步骤 3 切好了数据、定好了规矩。本步要第一次真正训练模型 ,并搭好后续步骤共用的评估框架。
要完成四件事:
- 构建
Pipeline([SimpleImputer, StandardScaler, LogisticRegression]) - 在 train 上
fit,在 val 上评估(不用 test) - 封装
evaluate_model():统一输出 Accuracy / F1 / ROC-AUC + 混淆矩阵 - 与「全猜不违约(全 0) 」对照,证明基线 F1 明显优于瞎猜
本步是项目六建模的「起跑线」 ------步骤 5~8 的所有对比,都沿用同一套 Pipeline 结构与 evaluate_model()。
4.2 这一步在解决什么问题?
4.2.1 为什么不能直接 LogisticRegression().fit(X, y)?
步骤 2 我们发现:
- MonthlyIncome 缺 19.8%
- 各特征量纲不同(年龄 ~50,负债率可能 >100)
若跳过预处理直接 fit:
- 缺失值会让 sklearn 报错或行为异常
- 未缩放时,数值大的列(如 DebtRatio)会 dominate 逻辑回归
- 若在全量数据 上先填缺失、再划分 → 泄露(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_score、f1_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 均只用 val ;test 在步骤 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_*.png、11_*.png)
5.1 本步目标
步骤 4 基线 LR 在默认阈值 0.5 下 Recall 仅 4% ------模型几乎不预测违约。本步系统对比三种不平衡处理 方案,在 val 上比 F1、Recall、PR-AUC,并选定一种进入步骤 6。
要完成四件事:
- 策略一 :无 class_weight/SMOTE,但在 val 上调阈值(降低判违约门槛)
- 策略二 :
class_weight='balanced',阈值 0.5 - 策略三 :SMOTE 放入 Pipeline,仅 train fit 时过采样
- 对比表 + 说明为何不能对 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% 违约率
- 分布失真:test 违约率会被人为拉到 50%,不再代表真实业务
- 数据泄露:合成样本用了本属于 test 区域的信息,分数虚高
- 评估无效:上线后没有 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.Pipeline 在 predict 时自动跳过 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 上。本步要回答:哪种模型更稳、更强?
要完成四件事:
- 在 train 上做
StratifiedKFold(5)(val/test 不参与) - 对比 LR / RandomForest / HistGradientBoosting(均在 Pipeline 内)
- 报告各模型 CV F1 均值 ± 标准差 (阈值固定 0.13)
- 选定 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_*.png、14_*.png)
7.1 本步目标
步骤 6 选出 HistGradientBoosting ,但用的是默认超参 (max_iter=100 等)。本步用 GridSearchCV 在 train 内系统搜参,并在 val 上复核。
要完成四件事:
- 对 HGB Pipeline 做
GridSearchCV(12 组超参 × 5 折) - 输出
best_params_、best_score_ - 在 val 上评估调参后模型(阈值 0.13)
- 验收: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 动手任务
任务 A :classifier__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_*.png、16_*.png)
8.1 本步目标
步骤 7 的 调参 HGB 已是强单模型。本步尝试 Stacking 集成 :多个基学习器「各抒己见」,再由元学习器学如何组合。
要完成四件事:
- 选 3 个基学习器 (LR、RF、调参 HGB)+ LR 元学习器
- 使用
StackingClassifier(内层 CV 生成 OOF 概率) - 5 折 CV 对比:单模型 vs Stacking
- 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.txt,17_*.png,18_*.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.pkl,LEARNING_SUMMARY.md)
- train+val 重训 Stacking →
models/credit_risk_best.pkl score_applicants()批量打分- 复盘见
LEARNING_SUMMARY.md
项目六 10 步全部完成。
文档版本:v2.0 | 项目六全部 10 步教学已完成