目录
一、特征构建:
1.1 第1个新特征:负债收入比
1.2 第2个新特征:工龄(月数)
1.3 第3个新特征:贷款金额占收入比重
1.4 补充一个关键点
二、特征选择:
2.1 主要概念
2.2 具体方法
2.2.1. 过滤法
2.2.2. 包裹法
2.3.3. 嵌入法
三、模型评估指标
3.1 第一把尺子:精确率(Precision)—— “抓得准不准”
3.2 第二把尺子:召回率(Recall)—— “捞得全不全”
3.3 第三把尺子:AUC(最牛的综合成绩单)
四、交叉验证
五、机器学习建模标准流程
本文要干什么?
从最原始的贷款数据出发,带你手把手走完特征构建、特征选择、模型评估和交叉验证的全流程。会用最接地气的大白话和代码示例,把机器学习建模这件事掰开揉碎讲明白。不用背公式,先理解“为什么这么做”和“到底发生了什么”,看完你就能自己照猫画虎建一套评分卡或风控模型。
一、特征构建:
原始数据(你手里拿到的烂数据,我们需要整理)
| A001 | 8000 | 50000 | 20000 | 2020-03-15 | 2026-08-06 |
| A002 | 15000 | 80000 | 50000 | 2024-01-01 | 2026-08-06 |
| A003 | 5000 | 45000 | 30000 | 2018-07-01 | 2026-08-06 |
1.1 第1个新特征:负债收入比
-
怎么造:负债总额 ÷ 月收入
-
算出来:
-
A001:50000 ÷ 8000 = 6.25
-
A002:80000 ÷ 15000 = 5.33
-
A003:45000 ÷ 5000 = 9.0
-
为啥有用:月收入8000欠5万,和月收入5万欠5万,风险能一样吗?原始数据里只有“负债”和“收入”两列,模型看不出来,你造出这个比值,模型一眼就能看出谁还款压力大。
1.2 第2个新特征:工龄(月数)
-
怎么造:本次申请日期 – 入职日期(算出干了多少个月)
-
算出来:
-
A001:2026年8月 – 2020年3月 = 77个月
-
A002:2026年8月 – 2024年1月 = 19个月
-
A003:2026年8月 – 2018年7月 = 97个月
-
为啥有用:工龄越长,工作越稳,越不容易违约。原始数据里只有两个日期,你把它相减变成“工龄”,模型就能用上了。
1.3 第3个新特征:贷款金额占收入比重
-
怎么造:申请贷款金额 ÷ 月收入
-
算出来:
-
A001:20000 ÷ 8000 = 2.5倍
-
A002:50000 ÷ 15000 = 3.33倍
-
A003:30000 ÷ 5000 = 6.0倍
-
为啥有用:一个月挣5000的人想贷3万(6个月工资),和挣1.5万贷5万(3个月工资),明显前者风险更高。这个新特征直接反映了“借钱是否超出承受能力”。
最终你造完的新数据(送给模型去训练的干净数据集)
| A001 | 8000 | 50000 | 20000 | 6.25 | 77 | 2.5 |
| A002 | 15000 | 80000 | 50000 | 5.33 | 19 | 3.33 |
| A003 | 5000 | 45000 | 30000 | 9.0 | 97 | 6.0 |
1.4 补充一个关键点
像 随机森林(RF) 和 XGBoost 这类模型,它们是“切豆腐块”的逻辑(不断二分数据),天生不擅长做“除法”和“加减日期”。如果你不亲手造出“负债收入比”和“工龄”这两个新特征,模型自己很难悟出来。你造得好,模型AUC直接涨一截;你懒得造,模型就只能用那几列原始数据,效果差一大截。
二、特征选择:
2.1 主要概念
-
特征构建 = 做加法(自己动脑子“造”出新列)
-
特征选择 = 做减法(从一大堆列里,把没用的、拖后腿的“扔掉”)
还用刚才那个贷款的例子,对比着看:
你现在手里经过“构建”之后,已经有了这些列: 月收入、负债总额、申请金额、负债收入比、工龄(月)、贷款收入比……可能还有几十上百个其他特征。
这时候问题来了:是不是所有列都要喂给模型?
当然不是!有些列是“垃圾”:
-
无效列:比如“用户ID”(A001、A002),这对判断违约毫无用处,留着只会让模型学歪。
-
重复列:你造了“负债收入比”,那原始的“负债总额”和“月收入”可能就没必要同时存在了(因为信息已经包含在新列里了)。
-
拖后腿列:有些列跟结果(是否违约),留着只会增加计算量,甚至导致模型“过拟合”。
“特征选择”要做的,就是把这些该扔的扔出去。
2.2 具体方法
“过滤法/包裹法/嵌入法”就是三种“扔东西”的策略:
| 过滤法 | 给每个特征打个分(比如算算它和“是否违约”的相关性),分数低的直接淘汰,不看模型脸色。 | 像过安检,用X光机扫一遍,看着有问题的直接挑出来扔掉。 |
| 包裹法 | 把特征当“队员”去组队比赛。每次加一个或减一个特征,跑一遍模型看成绩,成绩好就留下,成绩差就踢走,反复试。 | 像组篮球队,不知道谁行谁不行,就挨个换人上场打比赛,看实战效果定去留。 |
| 嵌入法 | 让算法边训练边自己打分(如随机森林自带“重要性排序”),算法觉得没用的特征,自动就把权重降成0了。 | 像教练边训练边观察,谁表现不好直接在训练过程中就被淘汰了。 |
2.2.1. 过滤法
代码逻辑:给每个特征和“标签(答案)”算一个相关分(比如卡方检验、皮尔逊系数),分太低的直接剔除。
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.datasets import make_classification
造个假数据:10个特征,100个样本
X, y = make_classification(n_samples=100, n_features=10, random_state=42)
【减法操作】:只保留分数最高的前 5 个特征(把另外5个直接砍掉)
selector = SelectKBest(score_func=chi2, k=5)
X_new = selector.fit_transform(X, y)
print(f"原始特征数: {X.shape[1]}") # 输出 10
print(f"过滤后特征数: {X_new.shape[1]}") # 输出 5
核心精髓:这里的 score_func 独立于模型,切完再喂给随机森林。
2.2.2. 包裹法
代码逻辑:把特征选择当成“寻宝游戏”。指定一个模型(比如随机森林),反复训练,穷举或贪心地加特征(看加了涨分没)或减特征(看减了掉分没)。
最经典的叫 RFE(递归特征消除),它干的事是:先全量训练一次,把最不重要的那个扔了,再训练,再扔最不重要的……直到剩下你指定的数量。
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
先定个模型(注意:包裹法必须依赖这个模型来当“裁判”)
estimator = RandomForestClassifier(random_state=42)
【减法操作】:目标只保留 3 个最重要的特征
selector = RFE(estimator, n_features_to_select=3, step=1)
selector = selector.fit(X, y) # 这一步在后台循环了很多次!
查看哪些列被留下了(True是留下,False是被减掉了)
print("特征保留掩码:", selector.support_)
比如输出: [False False True True False False False True False False]
减去没用的,得到新数据
X_new = selector.transform(X)
print(f"包裹法选择后特征数: {X_new.shape[1]}") # 输出 3
2.3.3. 嵌入法
代码逻辑:这类算法(Lasso回归、决策树、XGBoost)在训练时,内部自带惩罚项或分裂机制。训练完,没用的特征权重系数直接就是 0。
from sklearn.linear_model import LogisticRegression
from sklearn.feature_selection import SelectFromModel
用带 L1 惩罚项的逻辑回归(L1惩罚会把不重要的系数直接压成0)
这里的 C=0.1 控制惩罚力度,惩罚越狠,减掉的越多
estimator = LogisticRegression(penalty='l1', solver='liblinear', C=0.1, random_state=42)
【训练中进行减法】:训练的同时,自动把没用的特征权重减成 0
selector = SelectFromModel(estimator, threshold='mean') # threshold表示系数大于平均值才保留
selector = selector.fit(X, y)
查看权重系数,你会发现有些列是 0.0
print("特征权重系数:", selector.estimator_.coef_)
比如: [ 0. 0. 0.5 0.8 0. 0. 0.2 0. 0. 0.1] <- 有4个直接被归零了
X_new = selector.transform(X)
print(f"嵌入法选择后特征数: {X_new.shape[1]}")
三、模型评估指标
接下来:模型跑完了,怎么给它“打分”?精确率、召回率、AUC,就是三把不一样的“尺子”。
3.1 第一把尺子:精确率(Precision)—— “抓得准不准”
大白话: 你抓回来100个人,说他们是小偷,结果里面只有60个是真的,40个是冤枉的。那你的精确率就是 60%。
-
啥时候用? 比如给用户推送“降薪预警”,如果你误判了(冤枉好人),用户会骂娘,投诉你。这时候必须保证“抓回来的人里,尽量都是真的”,就得看精确率。
3.2 第二把尺子:召回率(Recall)—— “捞得全不全”
大白话: 商场里今天实际溜进来100个小偷,你带着保安一顿操作,只抓回来60个,漏掉了40个。那你的召回率就是 60%。
-
啥时候用? 比如“癌症早筛”或“信用卡盗刷检测”。漏掉一个真的病人(或盗刷),后果极其严重。这时候哪怕把正常人误判成病人,也得先把所有嫌疑人都捞出来,就得看召回率。
重点来了: 精确率和召回率是“跷跷板”——你调模型的时候,精确率高了,召回率通常会降;召回率高了,精确率通常会降。你要根据业务场景,决定优先保哪个。
3.3 第三把尺子:AUC(最牛的综合成绩单)
大白话: 前面两把尺子都是“偏科”分数,AUC看的是“综合排名能力”。
举个例子: 模型给10个人打分(分数越高越像小偷):
-
真正的小偷(3个)打了:95分、90分、85分。
-
普通好人(7个)打了:80分、70分、60分……
如果AUC=1.0(满分),说明所有小偷的分数,都比所有好人高,模型完美分开两类人。 如果AUC=0.5,说明模型纯属瞎蒙,跟抛硬币没区别(分数混在一起,分不出谁是小偷)。
四、交叉验证
把全部数据平均切成 N 份,轮流拿其中 1 份当“绝密高考卷”,剩下 N-1 份当“教材”,反复考 N 次,最后算平均分。
举例子—— K折交叉验证(K通常取5或10)。我拿 5折 给你拆解动作:
-
原始数据:你手上有1000个学生的全部资料(特征)和高考成绩(标签)。
-
切分:把这1000人随机平均分成5组,每组200人,编号A、B、C、D、E。
开始轮流传“绝密试卷”:
| 第1轮 | B + C + D + E 的数据喂给模型学 | A组(模型完全没见过) | 精确率/召回率/AUC = 85% |
| 第2轮 | A + C + D + E 的数据喂给模型学 | B组(模型完全没见过) | 精确率/召回率/AUC = 87% |
| 第3轮 | A + B + D + E 的数据喂给模型学 | C组(模型完全没见过) | 精确率/召回率/AUC = 82% |
| 第4轮 | A + B + C + E 的数据喂给模型学 | D组(模型完全没见过) | 精确率/召回率/AUC = 90% |
| 第5轮 | A + B + C + D 的数据喂给模型学 | E组(模型完全没见过) | 精确率/召回率/AUC = 86% |
最终该模型的真实成绩 = (85% + 87% + 82% + 90% + 86%)÷ 5 = 86%。
为什么必须搞这么麻烦?(回答你的“为何物”)
如果不做交叉验证,最容易犯的错叫 “拿全部数据又当教材又当考卷”——模型把1000个人的答案全背下来了,最后精确率100%。但拉到现实中模型直接懵逼,这叫过拟合。
故,交叉验证的三大硬核作用:
防作弊(最核心):保证每一份数据都当过“陌生人”,模型每轮都在做“没见过的题”,逼它学真本事。
更稳当:如果只分一次训练集/测试集,万一你手气差,分到的那200个测试题恰好全是“简单题”,成绩虚高;或者全是“难题”,成绩虚低。循环5次取平均,成绩更接近真实水平。
不浪费数据:如果你的数据本来就少(比如只有500条),你还硬抠出100条做测试、400条训练,模型会“吃不饱”。交叉验证让每一轮都有800人(4折)去训练,模型学得更扎实。
1> 千万注意: 做特征选择(比如用过滤法算分、用包裹法RFE)的时候,必须只在训练集(那4份)上算,算完再应用到测试集(那1份)上。
2> 错误做法:先用全部1000人的数据挑出最好的5个特征,再去做交叉验证。 3> 后果:你挑特征的时候,已经偷看了那200个“绝密考生”的信息,相当于考试前泄题,交叉验证白做了。
4.>正确流程: 每一轮交叉验证的内部,都要重新做一次特征选择——只拿800人的训练数据挑特征,再用这个标准去过滤那200人的测试数据。
五、机器学习建模标准流程
预处理 → 特征构建 → 进入交叉验证循环(内含:切分 → 特征选择 → 跑RF/XGBoost → 看精确率/召回率/AUC)→ 循环结束,算平均分 → 撰写模型评估报告

