机器学习流水线与特征选择实战:Pipeline、SelectKBest 与递归消除

机器学习流水线与特征选择实战:scikit-learn Pipeline 组合、ColumnTransformer 异构数据、特征选择三大类(过滤/包裹/嵌入)、SelectKBest 与统计检验、RFE 递归消除、正则化特征选择、特征选择防泄漏、Pipeline 与交叉验证、生产落地与调试。

引言

真实项目的数据是「脏的、异构的」:数值列要缩放、类别列要编码、缺失值要填充,然后才能训练。把这些步骤串成可复用的 Pipeline,并从中选出真正有用的特征,是机器学习从「单机实验」走向「工程落地」的分水岭。本文覆盖 sklearn 的 Pipeline 与 ColumnTransformer 用法、三大类特征选择方法(过滤/包裹/嵌入)、RFE 递归消除与正则化选特征,以及最关键的防泄漏纪律。

前置:/ml-feature-engineering/(清洗/构造/编码)、/ml-model-evaluation/(交叉验证)、/ml-supervised-regression/(回归流程)。


目录


1. 为什么需要 Pipeline

1.1 裸写流程的三大问题

# 反模式:散乱的预处理代码
X = impute(X)        # 填充
X = scale(X)         # 缩放
X = encode(X)        # 编码
model.fit(X, y)      # 训练
  • 容易漏步骤:上线时少做一步预处理,模型直接崩
  • 无法复用:换模型/换数据要重写一整套
  • 泄漏风险:手动在训练前对全量数据做预处理,验证折的信息被污染

1.2 Pipeline 是什么

Pipeline 把「一串变换 + 一个最终模型」打包成一个可整体 fit/预测的对象,内部步骤顺序执行、中间结果自动传递:

数据 → 填充 → 缩放 → 编码 → 模型 → 预测

记忆:Pipeline = 预处理链 + 模型打包成一个可复用对象——防漏步骤、防泄漏、可整体交叉验证。


2. Pipeline 基础:串起预处理与模型

2.1 最小 Pipeline

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

pipe = Pipeline([
    ("scale", StandardScaler()),
    ("clf", LogisticRegression(max_iter=1000)),
])

pipe.fit(X_train, y_train)       # 一步训练整条链
y_pred = pipe.predict(X_test)    # 一步预测(自动预处理)

2.2 参数访问与网格搜索

Pipeline 让超参搜索变得「参数路径化」:

from sklearn.model_selection import GridSearchCV

params = {"clf__C": [0.1, 1.0, 10.0]}   # 步骤名__参数名
gs = GridSearchCV(pipe, params, cv=5)
gs.fit(X_train, y_train)

记忆:Pipeline 一步 fit/一步 predict;超参用「步骤名__参数名」寻址,GridSearchCV 自动做带预处理的交叉验证。


3. ColumnTransformer:异构数据的分列处理

3.1 问题:不同列要不同处理

一个表里既有数值列(要缩放)、又有类别列(要编码)、还有可能要 drop 的 ID 列——不能对全表做同一种预处理。

3.2 分列处理

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler

num_cols = ["age", "income"]
cat_cols = ["city", "job"]

pre = ColumnTransformer([
    ("num", StandardScaler(), num_cols),
    ("cat", OneHotEncoder(handle_unknown="ignore"), cat_cols),
])

pipe = Pipeline([("pre", pre), ("clf", LogisticRegression())])

3.3 配合 DataFrame 的输出检查

X_out = pre.fit_transform(X_train)
print(X_out.shape)   # 确认编码后的列数,避免意外

记忆:ColumnTransformer 让「不同列走不同预处理管道」——数值列缩放、类别列独热、多余列 drop,一次 fit 全自动。


4. 特征选择三大类:过滤包裹嵌入

类别思路代表特点
过滤法与目标的相关统计SelectKBest、方差阈值快、与模型无关
包裹法按模型表现选子集RFE、前向搜索准、慢、易过拟合
嵌入法训练中学特征权重L1 回归、树重要性平衡、常用

三类各有取舍:过滤最快但忽略特征交互,包裹按模型效果选最贴合但贵,嵌入法性价比最高。

记忆:三类特征选择——过滤看统计快而糙、包裹看模型效果准而慢、嵌入在训练中学习权重最平衡。


5. 过滤法:SelectKBest 与统计检验

5.1 方差阈值:删掉「不变」的列

零方差的列毫无信息量,先删:

from sklearn.feature_selection import VarianceThreshold

sel = VarianceThreshold(threshold=0.0)   # 删零方差
X_sel = sel.fit_transform(X)

5.2 SelectKBest:按检验选 Top-K

from sklearn.feature_selection import SelectKBest, f_classif

# f_classif:单变量方差分析 F 检验(分类用)
sel = SelectKBest(f_classif, k=20)
X_sel = sel.fit_transform(X, y)

其他评分函数:mutual_info_classif(互信息,可捕捉非线性)、chi2(卡方,需非负特征)。

5.3 在 Pipeline 里用

pipe = Pipeline([
    ("scale", StandardScaler()),
    ("select", SelectKBest(f_classif, k=20)),
    ("clf", LogisticRegression()),
])

记忆:过滤法快而糙——VarianceThreshold 删零方差、SelectKBest 按 F 检验/互信息/卡方选 Top-K,适合高维数据的粗筛。


6. 包裹法:RFE 递归特征消除

6.1 RFE 的思路

RFE(Recursive Feature Elimination)反复训练模型,每次砍掉权重最小的特征,递归直到留下指定数量:

from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier

rfe = RFE(RandomForestClassifier(n_estimators=100), n_features_to_select=15)
rfe.fit(X_train, y_train)
print(rfe.support_)             # 每列是否被选中
print(rfe.ranking_)             # 每列排名(1 = 最优)

6.2 用交叉验证定特征数

RFECV 用交叉验证自动选「分数不再提升」的特征数,避免手选 n_features_to_select:

from sklearn.feature_selection import RFECV

rfecv = RFECV(RandomForestClassifier(n_estimators=100), cv=5)
rfecv.fit(X_train, y_train)
print(rfecv.n_features_)        # 自动选出的最优特征数

记忆:RFE 递归「训练→砍最小权重特征」直到目标数,RFECV 用交叉验证自动定最优特征数——准但慢,特征多时先粗筛再 RFE。


7. 嵌入法:正则化与树模型重要性

7.1 L1 正则让不重要权重归零

线性模型加 L1(Lasso)后,不重要的特征权重被压成 0——选特征即选非零权重:

from sklearn.linear_model import Lasso, LogisticRegression

# 回归用 Lasso,分类用 LogisticRegression(penalty='l1', solver='liblinear')
sel = Lasso(alpha=0.01).fit(X_scaled, y)
nonzero = (sel.coef_ != 0)       # True = 保留该特征

7.2 树模型特征重要性

随机森林/梯度提升自带特征重要性,按重要性阈值裁掉尾部特征:

from sklearn.ensemble import RandomForestClassifier
import numpy as np

rf = RandomForestClassifier(n_estimators=200).fit(X, y)
imp = rf.feature_importances_
keep = imp > np.quantile(imp, 0.3)   # 砍掉最低 30%

记忆:嵌入法在训练里学特征权重——L1 把不重要特征压成 0、树模型按重要性裁尾部;性价比最高,生产最常用。


8. 特征选择的最大坑:数据泄漏

8.1 泄漏的典型场景

在交叉验证之前就用全量数据做选择/缩放,让验证折的统计信息(均值/方差/选择结果)渗入训练——分数虚高、上线崩盘:

# 反模式:先对全量数据选特征,再交叉验证
X_all_sel = SelectKBest(k=20).fit_transform(X_all, y)   # 泄漏!选择用了全量 y
scores = cross_val_score(model, X_all_sel, y, cv=5)

8.2 正确做法:选择器放进 Pipeline

让选择器作为 Pipeline 的一步,交叉验证的每一折只在训练折上 fit 选择器:

# 正确:选择器在 Pipeline 里,每折只对训练部分 fit
pipe = Pipeline([
    ("scale", StandardScaler()),
    ("select", SelectKBest(k=20)),
    ("clf", LogisticRegression()),
])
scores = cross_val_score(pipe, X, y, cv=5)   # 无泄漏

记忆:特征选择/缩放的 fit 必须发生在每一折的训练集上——把选择器放进 Pipeline 让交叉验证自动防泄漏,这是铁的纪律。


9. Pipeline 与交叉验证的配合

9.1 完整落地模板

from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import classification_report

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)

pipe = Pipeline([
    ("pre", ColumnTransformer([...])),
    ("select", SelectKBest(...)),
    ("clf", RandomForestClassifier(n_estimators=200)),
])

gs = GridSearchCV(pipe, param_grid, cv=5, scoring="f1")
gs.fit(X_tr, y_tr)
print(classification_report(y_te, gs.predict(X_te)))

9.2 调试与扩展

# 想看中间结果:pipe.named_steps 拿到任一步
# 想加步骤:中间插入 estimators 即可
# 想序列化:joblib.dump(pipe, "pipe.pkl") 一键保存整条链
# 上线一致性:预测端加载同一个 pipe,预处理自动带上

记忆:Pipeline + 交叉验证是防泄漏的标准组合——train_test_split 切分、GridSearchCV 调参、named_steps 调试、joblib 一键上线,预处理随模型一起部署。


10. 速查表与一句话记忆

工具用途一句话
Pipeline串起预处理+模型一步 fit、一步预测
ColumnTransformer异构分列处理数值缩放/类别编码分流
VarianceThreshold删零方差无信息列先删
SelectKBest按检验选 Top-K过滤法代表
RFE/RFECV递归砍最小权重包裹法、准而慢
Lasso / 树重要性嵌入法选特征训练中自动学权重
GridSearchCVPipeline 调参步骤名__参数名

一句话记忆:Pipeline 让预处理+模型打包成一个可复用、可整体交叉验证的对象(ColumnTransformer 分列处理异构数据);特征选择三条路——过滤法按统计粗筛(SelectKBest)、包裹法按模型效果精选(RFE)、嵌入法在训练里学权重(Lasso/树重要性);最大的坑是数据泄漏——选择器和缩放器必须在每一折的训练集上 fit,放进 Pipeline 让交叉验证自动防泄漏;落地时 joblib 保存整条链,上线预测与训练用同一个预处理路径。


延伸阅读

  • /ml-feature-engineering/ — 特征清洗、构造、编码与防泄漏纪律
  • /ml-model-evaluation/ — 交叉验证与过拟合
  • /ml-supervised-classification/ — 分类任务全流程
  • /ml-model-interpretability/ — 特征重要性与 SHAP
  • [[ai-ml]] — 大规模特征工程与训练流水线
  • sklearn Pipeline 文档
  • sklearn 特征选择文档

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 模型压缩与推理优化实战:量化、剪枝与知识蒸馏
  2. 强化学习入门实战:MDP、Q-learning 与 DQN 概览
  3. 多分类与多标签学习实战:OvR、softmax 与多输出评估