本文共 5814 字,大约阅读时间需要 19 分钟。
(2018/2/6 更新:修改了部分名词的翻译)
与其说是教程类的科普,不如说是一篇经验向的个人笔记,所以细节上比较懒。其实,我更打算把这篇文章做成一个索引,能够引用原版文档的就引用文档,尽量不重复翻译,毕竟各类文档本身信息量最充足、更新最及时。简洁是本文的第一原则。
一些基础的暂时就不补充了,主要涉及一些奇技淫巧。纯粹是经验之谈,如有不准确之处,请多多包涵;如能帮忙指正,感激不尽。如果觉得内容少的话,等几个月,说不定就养肥了~
想到一点写一点,有思路才更新。有其他点子的话,可以在评论区补充或者私信。链接分享随意,全文转载前请私信。
特征工程(feature engineering):利用领域知识和现有数据,创造出新的特征,用于机器学习算法;可以手动(manual)或自动(automated)。神经网络的自动特征工程,常常不适用于现实中其他的复杂任务。因此,本文主要针对数据挖掘以及传统的机器学习,不会涉及图像识别、自然语言处理等深度学习领域。
俗话说:数据与特征工程决定了模型的上限,改进算法只不过是逼近这个上限而已。
特征工程(维基百科):
plot,plot,plot,重要的事情说三遍
我们需要将训练集表示为矩阵X(大小:n_samples * n_features)和矢量y(长度:n_samples)。矢量y可以被转换为矩阵Y(大小:n_samples * n_classes)。
scikit-learn完整的API reference:
常用函数:
类别特征,表示某个数据点属于某一个类别,或具有某一种类的特性。一列类别特征,默认用自然数表示(可以用LabelEncoder将字符串转化为自然数)。如果一列类别特征里有K种不同类别,其取值范围是。
例:颜色、性别、地址、血型、国籍、省、市、邮政编码。
类别特征(维基百科):
3.1.1. 自然数编码
默认的编码方式(见上,使用LabelEncoder可以得到),消耗内存小,训练时间快,但是特征的质量不高。
3.1.2. 独热编码(One-hot Encoding)
如果类别特征本身有顺序(例:优秀、良好、合格、不合格),那么可以保留单列自然数编码。如果类别特征没有明显的顺序(例:红、黄、蓝),则可以使用以下方法:
,用于类别特征的独热编码(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的类别合并到同一个新的类别下。
注意:如果特征列的频数需要被当做一个新的特征加入数据集,请在上述合并之前提取出频数特征。
数值特征(numerical feature),可以是连续的(continuous),也可以是离散的(discrete),一般表示为一个实数值。
例:年龄、价格、身高、体重、测量数据。
不同算法对于数值特征的处理要求不同。下文中的一些数据处理方法(3.2.1、3.2.2、3.2.3),因为是针对某一特征列的单调变换,所以不会对基于决策树的算法(随机森林、gbdt)产生任何影响。一般而言,决策树类算法不需要预处理数值特征。
3.2.1. 标准化(Standardization)
3.2.2. 归一化(Normalization)
,其中表示norm函数。
3.2.3. 区间缩放(scaling)
3.3. 其他特征工程(待补全)
时间特征:年、月、日、时、分、秒,这是一年的第n天,这是一年的第n周,这是一周的第n天,etc
地理特征:经度、纬度
用户特征:如用户名,每个种类对应一个不同的实体
3.4 缺失值处理
LightGBM和XGBoost都能将NaN作为数据的一部分进行学习,所以不需要处理缺失值。
其他情况下,我们需要使用
用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)
基于genetic programming的symbolic regression,具体的原理和实现参见文档。目前,python环境下最好用的基因编程库为gplearn。基因编程的两大用法:
在决策树系列的算法中(单棵决策树、gbdt、随机森林),每一个样本都会被映射到决策树的一片叶子上。因此,我们可以把样本经过每一棵决策树映射后的index(自然数)或one-hot-vector(哑编码得到的稀疏矢量)作为一项新的特征,加入到模型中。
具体实现:apply()以及decision_path()方法,在scikit-learn和xgboost里都可以用。
在数据挖掘中,数据泄露(leakage)指的是:本来不应该出现在X里的、和目标y有关的数据,出现在了X中。如此一来,机器学习算法就会有好到不真实的表现。
Kaggle Wiki链接:
6.1. 数据泄露的种类以及影响分析
6.2. 有效发现和利用数据泄露
数据泄露可以分为两大类:
避免第一种数据泄露的方法,可以参考kaggle的各类比赛。假设有大量数据,我们可以把未处理的数据分为训练集和测试集,其中,测试集包括Public LB和Private LB两部分。
交叉验证误差、public LB误差、private LB误差:如果后者的误差值显著高于前者,那么需要考虑过拟合或第一类数据泄露。
第二类的数据泄露,属于旁门左道。本质上,这相当于在模型训练阶段,干了数据收集阶段的工作。搜集原始数据,或是自己提供数据举办竞赛(试图避免他人利用数据泄露)时,可以参考这种思路。
这类数据可能会导致过拟合。
大家在收藏的时候也不要忘记点赞,你们的赞是我更新的动力。
觉得有用的话,可以把链接分享出去让更多人看见。
如果你们有其他特征工程的技巧,欢迎在评论区分享。
(顺便,厚颜无耻地求一个关注?我是 )