4.3 特征工程高级技巧


文档摘要

4.3 特征工程高级技巧 本节摘要:特征工程决定了模型效果的上限,模型本身只是在逼近这个上限。基础篇讲过标准化和独热编码,本节往高级走:特征选择用 SelectFromModel 和递归消除去掉冗余;多项式与交互特征让线性模型也能捕捉非线性;目标编码把高基数类别变量压成数值;主成分分析做降维;再补齐缺失值处理和时间特征这两个实战高频场景。掌握了这些,你就有了把脏数据加工成模型爱吃形态的完整工具箱。 本节导读 阅读完本节,你应当能够: 区分过滤式、包裹式、嵌入式三类特征选择,并说出各自代表方法。 用 SelectFromModel 和 RFECV 做特征选择,理解两者适用场景的差异。 说清多项式特征和交互特征各解决什么问题,以及维度爆炸的风险。 理解目标编码的原理与过拟合风险,知道何时该用。

4.3 特征工程高级技巧

本节摘要:特征工程决定了模型效果的上限,模型本身只是在逼近这个上限。基础篇讲过标准化和独热编码,本节往高级走:特征选择用 SelectFromModel 和递归消除去掉冗余;多项式与交互特征让线性模型也能捕捉非线性;目标编码把高基数类别变量压成数值;主成分分析做降维;再补齐缺失值处理和时间特征这两个实战高频场景。掌握了这些,你就有了把脏数据加工成模型爱吃形态的完整工具箱。

本节导读

阅读完本节,你应当能够:

  1. 区分过滤式、包裹式、嵌入式三类特征选择,并说出各自代表方法。
  2. 用 SelectFromModel 和 RFECV 做特征选择,理解两者适用场景的差异。
  3. 说清多项式特征和交互特征各解决什么问题,以及维度爆炸的风险。
  4. 理解目标编码的原理与过拟合风险,知道何时该用。
  5. 掌握缺失值填充的多种策略和时间特征抽取的常见套路。

一、先立个心法:特征比模型更值钱

有句流传很广的话——数据和特征决定了机器学习的上限,模型和算法只是在逼近这个上限。翻译成大白话:你费劲调半天的模型,可能还不如把某个特征换一种加工方式带来的提升大。这不是说模型不重要,而是说很多人把时间全花在模型上,忽略了特征这一头。

特征工程做的事,本质是"把人类对问题的理解翻译成模型能读懂的数字"。理解越深,翻译越准,模型越省力。下面按"选、造、编、降、补、挖"六个动作,把高级技巧串起来讲。

二、特征选择:砍掉拖后腿的列

特征不是越多越好。冗余特征浪费算力,噪声特征带偏模型,无关特征纯属干扰。特征选择就是挑出真正有用的子集。它分三派:

  • 过滤式:不看模型,直接用统计量筛。方差阈值砍掉"整列几乎不变的"列,单变量统计测每个特征与目标的相关性。快,但可能留下与模型不搭的特征。
  • 包裹式:拿模型当裁判,反复训练、反复删。递归特征消除(RFE)就是代表——训一次,删掉最不重要的,再训,直到剩下指定数量。慢,但选出来的最贴合这个模型。
  • 嵌入式:把选择塞进训练过程。SelectFromModel 让模型自己给特征打分,再按阈值截断;带正则化的线性模型天然会把无关特征的系数压到零。

动手看嵌入式最常用的一例:

from sklearn.feature_selection import SelectFromModel from sklearn.ensemble import RandomForestClassifier rf = RandomForestClassifier(random_state=42).fit(X_train, y_train) sel = SelectFromModel(rf, threshold="median") X_train_sel = sel.fit_transform(X_train, y_train) X_test_sel = sel.transform(X_test)

这四行干的事:先让随机森林算一遍特征重要性,再留下重要性高于中位数的特征,最后用筛选后的数据重训。如果不知道该留几个特征,用 RFECV——它加了一层交叉验证,自动挑出得分最高的特征数量。

💡 关键直觉:把特征选择想成"裁员"。过滤式是按简历初筛,包裹式是让主管一轮轮面,嵌入式是边干活边淘汰。简历筛最快但可能看走眼,一轮轮面最准但最贵。

三、特征组合:给线性模型装上限

线性模型又稳又可解释,但它只能表达直线关系,遇到"面积越大越值钱、但地段好时涨得更猛"这种交互,就抓瞎了。多项式特征和交互特征就是给线性模型补这个短板——把原始特征的高次项和乘积项显式造出来,让线性模型在扩展后的空间里也能拟合曲线。

from sklearn.preprocessing import PolynomialFeatures import numpy as np X = np.array([[1, 2], [3, 4]]) poly = PolynomialFeatures(degree=2, include_bias=False) X_poly = poly.fit_transform(X)

原始两列 [x1, x2] 会被扩展成 [x1, x2, x1平方, x1乘x2, x2平方]。线性回归拿到这五列,就等于拥有了一个二次曲面。

代价是维度爆炸。假设原始有 20 个特征,degree 设 2,特征数会从 20 涨到两百多;degree 设 3,直接上千。特征一多,线性模型开始过拟合,训练也变慢。所以多项式特征通常只用在特征数量本来就不多的场景,或者配合正则化一起用。

方式 生成什么 适用 风险
多项式特征 单特征的高次项 特征与目标有非线性关系 维度爆炸、过拟合
交互特征 多特征的两两乘积 特征间有协同效应 同上,且解释性下降
interaction_only 模式 只保留乘积项 只想抓交互,不要高次项 可能漏掉非线性主效应

四、目标编码:高基数类别变量的救星

类别变量如果取值很多(比如邮编、商品 ID,动辄上千种),独热编码会炸出一千多列稀疏矩阵,树模型嫌慢,线性模型嫌吵。目标编码的思路是:不用 0 和 1 表示类别,而是用"这个类别的目标均值"来替换它。

原理直观:如果一个邮编对应的房子普遍很贵,那这个邮编编码成的数值就应该偏大,模型一眼就能看出它与高房价的关系。但这里藏着个大坑——过拟合。如果某个类别在训练集里只有两三个样本,你用它们的均值去编码,等于把这个类别的稀有信息泄露给了模型,训练集看着很准,测试集就崩。

所以目标编码从来不是"直接算均值",而是要做平滑:把稀有类别的均值往全局均值拉一拉,样本越少,拉得越狠。Scikit-learn 里没有现成的目标编码器,但相关生态提供了带平滑的实现,核心参数就是平滑强度。用它时务必配合交叉验证,编码统计量只能从训练折里算。

⚠️ 常见坑:在全量数据上先算目标均值、再做编码,再切训练测试集。这就是标准的数据泄露——测试集的目标信息被提前缝进了特征里。正确顺序永远是先切分,编码统计量只从训练集算,再应用到测试集。

五、降维:把高维压成低维精华

降维不是偷工减料,而是去掉冗余维度、保留主要信息。主成分分析(PCA)是最常用的线性降维——它找到数据方差最大的几个方向,把数据投影上去。方差最大的方向往往就是信息最多的方向。

from sklearn.decomposition import PCA pca = PCA(n_components=0.95) # 保留 95% 的方差 X_reduced = pca.fit_transform(X_train)

n_components 可以给整数(留几个成分),也可以给浮点(保留多少比例的方差)。给浮点更省心——你不需要事先猜该降到几维,让算法按信息量自动决定。

降维的适用场景很明确:特征维度过高导致训练慢、噪声大,或者特征间高度相关、信息冗余。它的代价是可解释性——降维后的每个成分都是原始特征的线性组合,业务方看不懂"成分三"是什么。所以如果下游要解释模型,降维前先掂量清楚这笔账。

六、缺失值与时间特征:两个实战高频场景

6.1 缺失值:先搞清"为什么缺"

缺失不是无脑填均值。缺的原因不同,处理方式就该不同。完全随机缺失,均值中位数填充就行;非随机缺失——比如收入高的人偏偏不爱填收入——那"缺失"这件事本身就是个信号,应该专门造一列 0/1 特征标记"是否缺失",而不是把洞填平就完事。

from sklearn.impute import SimpleImputer imp = SimpleImputer(strategy="median") # 中位数填充 X_imputed = imp.fit_transform(X_train)

常用策略有均值、中位数、众数、常数。中位数对异常值更稳,众数适合类别变量,常数适合"缺失有特殊含义"的场景。进阶做法是拿其他特征去预测缺失值,但那要额外建模,成本高,只在缺失比例大且重要时用。

6.2 时间特征:把时间戳拆成零件

时间戳本身模型用不好,但拆开就值钱了。一个 2024-01-15 09:30 能拆出:年、月、日、星期几、第几周、是月初还是月末、是周末还是工作日、距某个基准日过了多少天。这些零件里往往藏着强信号——零售看星期几和节假日,风控看"距上次交易过了多久"。

import pandas as pd df["hour"] = df["ts"].dt.hour df["weekday"] = df["ts"].dt.weekday df["is_weekend"] = df["weekday"].isin([5, 6]).astype(int)

还有一个容易忽略的点:时间特征有周期性。23 点和 0 点只差一小时,但直接给数值,模型看到的是 23 和 0,会误以为差很远。用正弦余弦把小时、月份编码成角度,才能把"23 点挨着 0 点"这个关系告诉模型。

七、把六招串成一条管道

单看每一招都不难,真正的功力在于组合。一份真实数据,往往同时要补缺失、编类别、选特征、造交互。这时就该用管道把流程串起来,让每一步只在训练集上 fit、再一致地 apply 到测试集,从根上杜绝数据泄露。

from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.impute import SimpleImputer from sklearn.linear_model import LogisticRegression pipe = Pipeline([ ("impute", SimpleImputer(strategy="median")), ("scale", StandardScaler()), ("model", LogisticRegression()), ]) pipe.fit(X_train, y_train)

⚠️ 常见坑:把 StandardScaler 的 fit 用到整个数据集(训练加测试一起算均值和方差)。测试集的信息又漏进来了。管道的价值正在于此——它强制"先 fit 训练集、再 transform 测试集",你想漏都难。

💡 关键直觉:特征工程不是一次性做完的动作,而是一条要反复走的流水线。先用最简单的特征跑通基线,再一招一招往上加,每加一招看一次验证集有没有真提升。没提升的招就撤掉——能加会减,才算真的会用。

常见疑问

多项式特征 degree 设多少合适?
没有固定值,2 或 3 是常见起点。特征多时 degree 一高维度就爆,先用交叉验证看验证误差,再决定要不要往上加。多数实际问题里,degree 超过 3 的收益就很有限了。

目标编码和独热编码怎么选?
类别取值少、彼此独立,独热编码就够。类别取值多、且类别与目标有统计关联(比如邮编和房价),目标编码更省维度、更能体现这种关联。但目标编码一定要做平滑、且只能在训练集上算统计量,否则就是数据泄露,这点务必记住。

要点速记

  • 特征比模型更值钱:特征工程决定效果上限,模型只是逼近这个上限。
  • 特征选择三派:过滤式快但脱离模型,包裹式准但慢,嵌入式折中。
  • SelectFromModel 与 RFECV:前者按重要性阈值截断,后者用交叉验证自动定数量。
  • 多项式与交互特征:给线性模型补非线性能力,但会维度爆炸,需配合正则化。
  • 目标编码的过拟合:稀有类别要平滑,编码统计量只能从训练集算,否则数据泄露。
  • PCA 降维:按方差比例自动定维数,代价是丢可解释性。
  • 缺失值先问为什么缺:非随机缺失要把"是否缺失"本身当特征,而不是无脑填充。
  • 时间特征要拆更要处理周期:拆出星期几、小时等零件,并用正弦余弦编码周期性。

到这里,我们把"造零件、拼模型、喂数据"三条线都走通了。下一节把这条流水线放到数据量暴涨的现实里——大规模数据处理,看看当内存装不下时,前面学的这些招还能不能照常使。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U