【持续更新】机器学习特征工程实用技巧大全

【持续更新】机器学习特征工程实用技巧大全

(题图:Rene Magritte - The Empire of Light)

(2018/2/6 更新:修改了部分名词的翻译)

与其说是教程类的科普,不如说是一篇经验向的个人笔记,所以细节上比较懒。其实,我更打算把这篇文章做成一个索引,能够引用原版文档的就引用文档,尽量不重复翻译,毕竟各类文档本身信息量最充足、更新最及时。简洁是本文的第一原则。

一些基础的暂时就不补充了,主要涉及一些奇技淫巧。纯粹是经验之谈,如有不准确之处,请多多包涵;如能帮忙指正,感激不尽。如果觉得内容少的话,等几个月,说不定就养肥了~

想到一点写一点,有思路才更新。有其他点子的话,可以在评论区补充或者私信。链接分享随意,全文转载前请私信。


目录

  • 0. 概述
  • 1. 探索性数据分析(EDA)
  • 2. 预处理(Preprocessing)
  • 3. 特征工程:处理已有特征
  • 3.1. 类别特征
  • 3.2. 数值特征
  • 3.3. 时间特征
  • 3.4 缺失值处理
  • 4. 特征工程:创造新特征
  • 5. 特征选择
  • 6. 数据泄露(Leakage)
  • 7. 参考文献


0. 概述

特征工程feature engineering):利用领域知识现有数据,创造出新的特征,用于机器学习算法;可以手动(manual)或自动(automated)。神经网络的自动特征工程,常常不适用于现实中其他的复杂任务。因此,本文主要针对数据挖掘以及传统的机器学习,不会涉及图像识别、自然语言处理等深度学习领域。

俗话说:数据与特征工程决定了模型的上限,改进算法只不过是逼近这个上限而已。


特征工程(维基百科):Feature engineering


1. 探索性数据分析(EDA,Exploratory Data Analysis)

plot,plot,plot,重要的事情说三遍


2. 预处理(Preprocessing)

我们需要将训练集表示为矩阵X(大小:n_samples * n_features)和矢量y(长度:n_samples)。矢量y可以被转换为矩阵Y(大小:n_samples * n_classes)。

scikit-learn完整的API reference:API Reference - scikit-learn 0.18.1 documentation

常用函数:


3. 特征工程:处理已有特征

3.1. 类别特征

类别特征,表示某个数据点属于某一个类别,或具有某一种类的特性。一列类别特征,默认用自然数表示(可以用LabelEncoder将字符串转化为自然数)。如果一列类别特征里有K种不同类别,其取值范围是\{0, 1, 2, 3, ..., K-1\}

例:颜色、性别、地址、血型、国籍、省、市、邮政编码。

类别特征(维基百科):Categorical variable

3.1.1. 自然数编码

默认的编码方式(见上,使用LabelEncoder可以得到),消耗内存小,训练时间快,但是特征的质量不高。

3.1.2. 独热编码(One-hot Encoding)

如果类别特征本身有顺序(例:优秀、良好、合格、不合格),那么可以保留单列自然数编码。如果类别特征没有明显的顺序(例:红、黄、蓝),则可以使用以下方法:

sklearn.preprocessing.OneHotEncoder - scikit-learn 0.18.1 documentation,用于类别特征的独热编码(One-Hot Encoding)。运行结果与LabelBinarizer相似,不过在参数以及输入输出的格式上有细微的差别,参见文档。输出的矩阵是稀疏的,含有大量的0。

统计学中,独热编码的变种还有effects coding、contrast coding、nonsense coding等编码方式,但在数据挖掘中都不常用。

3.1.3. 聚类编码

和独热编码相比,聚类编码试图充分利用每一列0与1的信息表达能力。聚类编码时一般需要特定的专业知识(domain knowledge),例如ZIP码可以根据精确度分层为ZIP3、ZIP4、ZIP5、ZIP6,然后按层次进行编码。

(我个人尚未使用过这种方法,仅在读论文的时候看到了类似的思路,所以暂时不知道对于各种算法而言效果如何。)

3.1.4. 平均数编码(mean encoding)

平均数编码(mean encoding),针对高基数类别特征有监督编码。当一个类别特征列包括了极多不同类别时(如家庭地址,动辄上万)时,可以采用。优点:和独热编码相比,节省内存、减少算法计算时间、有效增强模型表现。

平均数编码:针对高基数类别特征(类别特征)的数据预处理/特征工程 - 知乎专栏

3.1.5. 只出现一次的类别

在类别特征列里,有时会有一些类别,在训练集和测试集中总共只出现一次,例如特别偏僻的郊区地址。此时,保留其原有的自然数编码意义不大,不如将所有频数为1的类别合并到同一个新的类别下

注意:如果特征列的频数需要被当做一个新的特征加入数据集,请在上述合并之前提取出频数特征。


3.2. 数值特征

数值特征(numerical feature),可以是连续的(continuous),也可以是离散的(discrete),一般表示为一个实数值。

例:年龄、价格、身高、体重、测量数据。

不同算法对于数值特征的处理要求不同。下文中的一些数据处理方法(3.2.1、3.2.2、3.2.3),因为是针对某一特征列的单调变换,所以不会对基于决策树的算法(随机森林、gbdt)产生任何影响。一般而言,决策树类算法不需要预处理数值特征。


3.2.1. 标准化(Standardization)

x' = \frac{x - \mu}{\sigma}

  • sklearn.preprocessing.RobustScaler - scikit-learn 0.18.1 documentation。如果数值特征列中存在数值极大或极小的outlier(通过EDA发现),应该使用更稳健(robust)的统计数据:用中位数而不是算术平均数,用分位数(quantile)而不是方差。这种标准化方法有一个重要的参数:(分位数下限,分位数上限),最好通过EDA的数据可视化确定。免疫outlier。


3.2.2. 归一化(Normalization)

\vec{x'} = \frac{\vec{x}}{l(\vec{x})},其中l表示norm函数。

3.2.3. 区间缩放(scaling)

x' = \frac{x}{max(|X|)}

x' = \frac{x - min(X)}{max(X) - min(X)}


3.3. 其他特征工程(待补全)

时间特征:年、月、日、时、分、秒,这是一年的第n天,这是一年的第n周,这是一周的第n天,etc

地理特征:经度、纬度

用户特征:如用户名,每个种类对应一个不同的实体

  • NA indicator column
  • 时间序列:把昨天的特征加入今天的特征,或者把和昨天相比,特征数值的改变量加入今天的特征。
  • 连续特征离散化(这对于决策树类型的模型没太多意义)。一种挺有趣的变种,就是限制浮点数特征的精度。
  • clipping:可以用pandas dataframe的.clip(low, upper)方法,把特征值的取值限制在一定范围内
  • 一开始就把所有的特征一股脑地扔进模型,费时费力,容易被一些没用的特征误导
  • 除非万不得已,不要用PCA或者LDA降维,直接减原始特征就行了。
  • 像lightgbm和xgboost这种gbdt,一般都自带cv,early stopping和feature importance,用来同时验证FE是坠吼的,省时间。


3.4 缺失值处理

LightGBM和XGBoost都能将NaN作为数据的一部分进行学习,所以不需要处理缺失值。

其他情况下,我们需要使用


4. 特征工程:创造新特征

4.1. 数值特征的简单变换

  1. 单独特征列乘以一个常数(constant multiplication)或者加减一个常数:对于创造新的有用特征毫无用处;只能作为对已有特征的处理。
  2. 任何针对单独特征列的单调变换(如对数):不适用于决策树类算法。对于决策树而言,XX^3X^5 之间没有差异, |X|X^2X^4 之间没有差异,除非发生了舍入误差。
  3. 线性组合(linear combination):仅适用于决策树以及基于决策树的ensemble(如gradient boosting, random forest),因为常见的axis-aligned split function不擅长捕获不同特征之间的相关性;不适用于SVM、线性回归、神经网络等。
  4. 多项式特征(polynomial feature):sklearn.preprocessing.PolynomialFeatures - scikit-learn 0.18.1 documentation
  5. 比例特征(ratio feature):X_1 / X_2
  6. 绝对值(absolute value)
  7. max(X_1, X_2)min(X_1, X_2)X_1 xor X_2


4.2. 类别特征与数值特征的组合

用N1和N2表示数值特征,用C1和C2表示类别特征,利用pandas的groupby操作,可以创造出以下几种有意义的新特征:(其中,C2还可以是离散化了的N1)

median(N1)_by(C1)  \\ 中位数
mean(N1)_by(C1)  \\ 算术平均数
mode(N1)_by(C1)  \\ 众数
min(N1)_by(C1)  \\ 最小值
max(N1)_by(C1)  \\ 最大值
std(N1)_by(C1)  \\ 标准差
var(N1)_by(C1)  \\ 方差
freq(C2)_by(C1)  \\ 频数

freq(C1) \\这个不需要groupby也有意义

仅仅将已有的类别和数值特征进行以上的有效组合,就能够大量增加优秀的可用特征

将这种方法和线性组合等基础特征工程方法结合(仅用于决策树),可以得到更多有意义的特征,如:

N1 - median(N1)_by(C1)
N1 - mean(N1)_by(C1)


4.3. 用基因编程创造新特征

Welcome to gplearn’s documentation!

基于genetic programming的symbolic regression,具体的原理和实现参见文档。目前,python环境下最好用的基因编程库为gplearn。基因编程的两大用法:

  • 转换(transformation):把已有的特征进行组合转换,组合的方式(一元、二元、多元算子)可以由用户自行定义,也可以使用库中自带的函数(如加减乘除、min、max、三角函数、指数、对数)。组合的目的,是创造出和目标y值最“相关”的新特征。这种相关程度可以用spearman或者pearson的相关系数进行测量。spearman多用于决策树(免疫单特征单调变换),pearson多用于线性回归等其他算法。
  • 回归(regression):原理同上,只不过直接用于回归而已。


4.4. 用决策树创造新特征

在决策树系列的算法中(单棵决策树、gbdt、随机森林),每一个样本都会被映射到决策树的一片叶子上。因此,我们可以把样本经过每一棵决策树映射后的index(自然数)或one-hot-vector(哑编码得到的稀疏矢量)作为一项新的特征,加入到模型中。

具体实现:apply()以及decision_path()方法,在scikit-learn和xgboost里都可以用。


决策树、基于决策树的ensemble

  • spearman correlation coefficient


线性模型、SVM、神经网络

  • 对数(log)
  • pearson correlation coefficient


6. 数据泄露(Data Leakage)

在数据挖掘中,数据泄露leakage)指的是:本来不应该出现在X里的、和目标y有关的数据,出现在了X中。如此一来,机器学习算法就会有好到不真实的表现。

Kaggle Wiki链接:Leakage | Kaggle

6.1. 数据泄露的种类以及影响分析

  • 测试集数据被泄露到训练集:过拟合,模型在现实中的表现远不如test accuracy;测试集失去意义。
  • 正确的预测(y)被泄露到测试集:严重过拟合,训练出的模型毫无用处,比赛组织者的极大失败
  • 未来的信息被泄露到过去:时间序列相关,现实中模型将无法有效根据过去情况预测未来。
  • 模型可以获得一些不该获得的信息,比如和目标变量有较大关系的变量、现实里接触不到的变量。例子:y是“病人是否患有癌症”,但是X包括了“病人是否接受肿瘤切除手术”。
  • 反向工程,去匿名化,去除数据集中的随机打乱操作,社会工程学。这种行为是数据比赛明令禁止的,而且在现实中也涉嫌侵犯隐私。例子:反向工程“随机的”用户编码,得出用户的真名。
  • 第三方信息。例子:已知坐标,利用geocoder类型的服务推出所在城市;在预测金融市场时加入第三方的政策新闻的特征。

6.2. 有效发现和利用数据泄露

数据泄露可以分为两大类:

  1. 由于自己的疏忽,在交叉验证、训练过程中,产生的数据泄露。这种情况属于失误,应当尽量避免。
  2. 在数据竞赛中,找到了理论上不能使用(但是也没有明令禁止)的额外数据,从而提升分数。

避免第一种数据泄露的方法,可以参考kaggle的各类比赛。假设有大量数据,我们可以把未处理的数据分为训练集测试集,其中,测试集包括Public LB和Private LB两部分。

  • 在模型的训练、选择和交叉验证时,我们只能接触训练集。
  • 在对自己的模型非常自信时,可以偶尔在Public LB上验证。
  • 只有模型即将被用于正式商业用途时,才能看模型在Private LB上的表现。

交叉验证误差、public LB误差、private LB误差:如果后者的误差值显著高于前者,那么需要考虑过拟合或第一类数据泄露。

第二类的数据泄露,属于旁门左道。本质上,这相当于在模型训练阶段,干了数据收集阶段的工作。搜集原始数据,或是自己提供数据举办竞赛(试图避免他人利用数据泄露)时,可以参考这种思路。

  • 文件夹的创造时间。
  • 看似乱码的字符串(如各类id)可能有统计分布的规律。
  • 地理位置信息:如果提供了坐标,则可反向地理编码,得出相关地理信息。

这类数据可能会导致过拟合。



大家在收藏的时候也不要忘记点赞,你们的赞是我更新的动力。

觉得有用的话,可以把链接分享出去让更多人看见。

如果你们有其他特征工程的技巧,欢迎在评论区分享。

(顺便,厚颜无耻地求一个关注?我是 @光喻

编辑于 2018-02-07

文章被以下专栏收录