2.2 转换器 (Transformer) 本节摘要:转换器是一类特殊的估计器,专干"把原始数据加工成模型能吃的样子"这件事。它把一次转换拆成两段:fit 只在训练集上记规则(比如均值和标准差),transform 用这套规则对任何数据做变换。这个两段式设计是防数据泄露的关键。标准化、独热编码、缺失值填充、降维,本质都是转换器。掌握 fittransform 与 transform 的差别,你才算真正会用。
本节摘要:转换器是一类特殊的估计器,专干"把原始数据加工成模型能吃的样子"这件事。它把一次转换拆成两段:fit 只在训练集上记规则(比如均值和标准差),transform 用这套规则对任何数据做变换。这个两段式设计是防数据泄露的关键。标准化、独热编码、缺失值填充、降维,本质都是转换器。掌握 fit_transform 与 transform 的差别,你才算真正会用。
阅读完本节,你应当能够:
真实数据从来不是拿来就能喂模型的。数值列量纲不一——一个特征是收入、以万计,另一个特征是年龄、以十计;类别列还是文字——"红""绿""蓝";还有缺值的格子、离群的点。模型大多是数学公式,它不认文字,也扛不住量纲悬殊带来的倾斜。
转换器就是干这件事的。你可以把它想成一个翻译加配钥匙的师傅:fit 是师傅拿着原装钥匙在锁孔里量出齿形、把规律记在本子上,transform 是用本子上的齿形去复制一把把新钥匙。量齿形只能对着训练集量,复制却可以对任何一把钥匙做。这个类比里,"本子"就是转换器的内部状态,"齿形"就是它记住的规则。
先跑一个最小的标准化:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(X_train) # 只在训练集上记均值和标准差 X_train_scaled = scaler.transform(X_train) # 用这套规则加工训练集 X_test_scaled = scaler.transform(X_test) # 用同一套规则加工测试集
StandardScaler 把每列特征减均值、除以标准差,处理后每列均值约等于 0、标准差约等于 1。注意看:测试集用的是训练集量出来的均值,不是测试集自己的。这一步看起来不起眼,却是整节最要紧的纪律。
如果转换器只有一个"一步到位"的方法,问题就来了:测试集也要转换,而测试集在真实场景里代表"还没见过的未来数据"。你要是拿测试集的统计量去转换测试集,等于偷看了答案,评估成绩会虚高——这就是数据泄露。
所以 Scikit-learn 把转换拆成两段,逼你做出正确的姿势:
一句话概括:规则只能来自训练集,但可以应用到任何数据。 fit_transform 并不是第三种方法,它只是 fit 加 transform 的合写,专门在训练集上用,图个少打一行。测试集和新数据永远只用 transform。
有人会问:那为什么还要留着 fit 和 transform 分开,直接全用 fit_transform 不行吗?行,只要对象是训练集。但测试集一旦也 fit_transform,规则就被测试集"污染"了。两段式设计不是啰嗦,是故意把容易犯的错挡在接口外面。
转换器的家族很大,但日常最常用的可以归成四类。选哪个,取决于你手里的数据长什么样、模型要什么。
| 转换器 | 作用 | 学习到的规则 | 适用场景 | 注意 |
|---|---|---|---|---|
| StandardScaler | 减均值、除标准差 | 均值、标准差 | 特征近似正态、基于距离的模型 | 受离群点影响大 |
| MinMaxScaler | 线性缩到指定区间 | 最小值、最大值 | 需要固定范围、数据非正态 | 新数据超出范围会破界 |
| OneHotEncoder | 类别转成多列 0 或 1 | 每列的类别列表 | 无序类别特征 | 高基数会维度爆炸 |
| SimpleImputer | 填充缺失值 | 填充用的统计量 | 有缺值且不宜删行 | 策略要按列选 |
先说两个缩放器。StandardScaler 和 MinMaxScaler 都消除量纲,区别在"参照物":前者以均值、标准差为基准,后者以最小、最大值为基准。数据里离群点多时,最大值、最小值会被一个脏点带飞,MinMaxScaler 的整条区间就变形了,这时要么先清离群点,要么换成对离群点更稳的鲁棒缩放。
类别编码也分场合。OneHotEncoder 把"红绿蓝"拆成三列 0/1,模型不会误以为"蓝大于绿";但如果一个类别特征有上万个取值,独热会撑出上万个新列,内存和训练时间都吃不消,这时要考虑别的编码思路,比如目标编码或把低频类别合并成"其他"。
缺值填充更简单:SimpleImputer 用均值、中位数、众数或一个常数把洞填上。数值列填中位数往往比填均值更稳,类别列填众数最自然。但填之前先问一句——这些缺值是随机丢的,还是有含义的(比如"没填收入"代表失业)?后者的话,填成 0 或单列一列"是否缺失"可能更诚实。
降维也是转换器,只是"加工"的方向不同。主成分分析这类降维器把高维压成低维,fit 时算出投影方向,transform 时把数据投过去。它同样遵守"只在训练集 fit"的纪律。
把最常见的几步串起来看一遍,感受转换器的"积木"特性:
from sklearn.preprocessing import StandardScaler, OneHotEncoder scaler = StandardScaler() encoder = OneHotEncoder(handle_unknown='ignore') X_num_scaled = scaler.fit_transform(X_train_num) # 数值列标准化 X_cat_encoded = encoder.fit_transform(X_train_cat) # 类别列独热编码
真实项目里,数据往往是数值列和类别列混在一起,不能一个转换器打天下。这时要么分列各转各的、再拼回去,要么用第 2.6 节的管道把多个转换器串成一条流水线。前者灵活但容易漏,后者省心且不易错。
三个缩放器的取舍,再压成一张对比:
| 缩放器 | 基准 | 抗离群点 | 输出范围 |
|---|---|---|---|
| StandardScaler | 均值、标准差 | 弱 | 无固定范围 |
| MinMaxScaler | 最小、最大值 | 弱 | 固定在指定区间 |
| RobustScaler | 中位数、四分位距 | 强 | 无固定范围 |
转换器学到的规则,同样可以从属性里读出来核对:
scaler.fit(X_train) print(scaler.mean_) # 每列的均值 print(scaler.scale_) # 每列的标准差
核对规则很有用:标准化后你预期均值接近 0,实际跑出来偏差很大,多半是 fit 和 transform 的数据没对上,或者有人中途又 fit 了一次。规则可读、可核对,是转换器比黑盒函数更可靠的原因之一。
转换器是数据泄露的高发地带,坑要逐个看清楚。
⚠️ 常见坑:测试集用了
fit_transform。这是最经典也最隐蔽的错误——你在测试集上重新算了均值和标准差,等于让测试集的"未来信息"渗进了模型,交叉验证分数会好得不真实。还有一种坑是独热编码遇到没见过的新类别,默认会报错,上线前要决定是忽略还是报错。
💡 关键直觉:转换器记住的是"规则"而不是"数据"。它 fit 之后只留几个统计量,不会把你的原始数据背下来。所以它轻、可复用、能跟着模型一起保存——这正是它比手写预处理高明的地方。
两条实操建议。第一,把转换器和模型一起放进管道,让 fit 和 transform 自动按正确的数据集流转,从根上杜绝"测试集 fit"的笔误——管道会在第 2.6 节细讲。第二,转换器大多带 inverse_transform,能把加工后的数据还原回原始量纲,这在你想把预测结果解释回业务语言时特别有用。
还有个判断标准值得记:凡是"只跟训练数据有关、不跟预测目标有关"的加工,都应该走转换器,而不是手写代码。 手写预处理看着灵活,却很容易在测试集和新数据上漏掉同一套规则,埋下隐性 bug。转换器把规则和数据绑在一起,换数据时规则跟着走。
还有一条容易被忽略的纪律:转换器加工完,列的顺序和含义可能就变了。标准化不改变列数,但独热编码会把一列"颜色"炸成好几列"颜色是红""颜色是绿"。所以后续模型 fit 时见到的列,必须和转换器 transform 输出的列严格一致——这又回到了预测器那节说的"列数对齐"。转换器和预测器之间的这条"列契约",是串起整条流水线的隐形线索。
fit_transform 和 transform 到底哪个该在训练集上用?
训练集用 fit_transform,测试集和新数据只用 transform。一句话记:训练集是"教"规则,所以要 fit;测试集只是"用"规则,所以只 transform。一旦测试集也 fit_transform,规则就被未来数据污染,评估分数虚高。
转换器 fit 之后能改数据吗,比如中途换一个填充策略?
能改,但代价是规则要重算。换策略意味着重新 fit,旧规则被覆盖。所以转换器通常"一次性 fit、长期 transform",把它和模型一起保存下来,上线后对新数据只做 transform。
怎么判断一个转换器该不该用目标 y?
大多数转换器只用特征 X,少数(比如目标编码)会用到目标 y。用不用 y,看它是否"借助答案来加工特征"。一旦用了 y,数据泄露的风险更高,通常要配合交叉验证来 fit,别图省事直接全量 fit。
转换器和"特征工程"是什么关系?
特征工程是更大的筐,转换器是其中一种标准化实现。手工构造交叉特征、业务衍生字段这类动作,往往在转换器之外做;而缩放、编码、填充、降维这些"有明确规则、且规则必须随数据一致复用"的动作,交给转换器最合适。经验法则:能写成 fit 加 transform 的加工,就交给转换器。
能不能把所有预处理都塞进一个转换器里?
单个转换器一般只干一件事,职责单一。多个转换器要协作,靠的是第 2.6 节的管道把它们串起来,而不是自己堆一个"大而全"的转换器。拆得细,规则才好单独复用、单独核对、单独替换——这也是模块化设计在数据预处理上的体现。
转换器需要和模型一起保存吗?
需要。转换器记住的规则——均值、标准差、类别列表——是模型推理的一部分,缺了它,模型拿到的是没加工过的原始数据,预测会错得离谱。所以上线时,转换器和预测器要打包保存、一起部署,别只存模型忘了存规则。
数据加工干净了,下一步该让模型张嘴说话。下一节我们看预测器——它负责把加工后的数据变成预测结果,而"给结果"的方式不止 predict 一种。