机器学习工程实践——特征工程

目录

1.1 数据清洗​​​​​​​

1.1.1 直接删除缺失数据

1.1.2 固定值填充

1.1.3 均值/众数/中位数填充

1.1.4 相邻值填充

1.1.5 模型预测填充

1.2 特征处理

1.2.1 归一化

1.2.2 标准化

1.2.3 离散化

1.2.4 one-hot编码

1.3 特征交互

1.4 特征映射


1.1 数据清洗

数据清洗主要是对原始给定的数据进行规整化,目的是得到一份适合机器学习模型处理的基本数据集

从某网页中提取的一条原始数据如下:

这是一个.json文件,我们要完成的操作是根据commit message串对数据文件进行正样本标记,但是该文件还包含一些我们不需要的标签数据,这时候我们就要进行数据清洗操作,清洗后的数据示例如图:

 这是一个简单的例子,实际的数据清洗过程相对复杂得多。比如,不同类型的数据(如文本、图像)、不同格式的数据(如.xlsx、.txt、.csv、.json、.html、.jpg等)等,一般来说我们首先分析需求,然后借助各种工具(如各种python库、数据库等)去进行解析和处理,最终得到一份或多份比较规整的结构化数据表

一般情况下,我们获得的数据会存在缺失的情况,缺失数据如下图所示

缺失数据

大部分机器学习模型不能自动处理数据缺失,所以在正式训练模型前,我们需要确定各个特征缺失值情况,并处理。

假设原始数据名称为data,已经转化为Pandas熟悉的DataFrame(表格型数据结构) 格式

import pandas as pd
data = pd.read_excel("data.xlsx",header=0)   #读取excel表格中的数据
print(data) #输出

输出 data

填充后得到的新数据为data_new,也是DataFrame格式,下面是几种常用的缺失值处理方式

1.1.1 直接删除缺失数据

当数据量比较大而缺失情况不严重时,可以直接选择删除训练集中含有缺失值的数据(即含有缺失值的行)

data_new = data.dropna(axis=0)
print(data_new)

结果:

 优点:可以降低数据中的噪声,因为填补并非完全正确,不准确的填补会给数据带来额外的误差

缺点:会减少数据量,尤其是在数据量本身就少的情况下会造成较坏的影响,比如我们的示例数据;其次训练集中的样本是不能删除的,删除则可能造成测试集和训练集数据分布不一致,有时数据分布对模型的训练和预测影响较大

1.1.2 固定值填充

固定值填充是一种很简单的方式,比如直接用“0”填充缺失值

data_new = data.fillna(0)

结果:

这是一种不科学的方法,特别是当缺失值比例较大时,例如 id = 2 这条数据,强行用固定值填充,会给数据带来非常大的噪声,容易造成模型的过拟合 

1.1.3 均值/众数/中位数填充

使用均值、众数、中位数填充算是固定值填充的一个优化方案

(1)平均数填补:

data_new = data.fillna(data.mean())    #使用平均数填补

(2) 众数填补:

data_new = data.fillna(data.mode())     #使用众数填补

注意:第三行数据特征f292填补失败

mode()函数是寻找一列或一行中出现次数最多的数,所以结果可能不止一个,解决方法:

data_new = data.fillna(data.mode().iloc[0])     #得到的众数也是表的形式,用第一行进行填补

此外,根据mode()的参数,我们可以传入axis = 0或1,用于对列或者对行求众数,默认是 0;还可以对参数numeric_only进行设置:选择numeric_only=True,即只对数值型的列进行

(3)中位数填补:

data_new = data.fillna(data.median())   #使用中位数填补

以上三种填补,对于各个特征来说,本质上还是使用固定值填充,但是填充的值更接近实际情况,因为大部分数据的分布服从高斯分布,而高斯分布的中间部分占据了整体取值的较大比例

1.1.4 相邻值填充

相邻值填充,即使用空缺位置前面或后面的值进行缺失值的填充

data_new = data.fillna(method='pad')    #使用前一个数据代替NaN
print(data_new)

data_new = data.fillna(method='bfill')    #使用后一个数据代替NaN
print(data_new)

 优点:填充的值来源于特征取值的某个真实情况,并且不同缺失位置大多填充的是不同的值,数据的扰动比直接使用固定值或均值填充要好,因而模型不容易过拟合

缺点:采用邻接值替代,偶然性较大,可能会出现十分接近和相差很大两种结果,当相差较大时这种方式的填充是不如均值填充的

1.1.5 模型预测填充

以上述样本为例,特征 f1, f2, f3, f4, f6, f7 不存在缺失,我们构建模型预测 f5 的缺失部分

步骤如下:

第 1 步:将 f1 - f7 取出作为新的数据集 data

第 2 步:按照 f5 特征是否缺失将数据集划分为训练集和测试集两部分,则样本(0,2,4)作为训练集,样本(1,3)作为测试集

第 3 步:建立简单的预测模型,比如回归模型、随机森林模型等,并用训练集进行训练

第 4 步:用训练好的模型预测测试集 f5 的值,再将预测值填补到原来的缺失位置即可

好处:在尽量保证填充准确度的同时增加数据扰动,理论上优于上述其他方法

实际:操作较为复杂,需要分数据集以及构建模型,完成一个特征的填补就需要分一次数据集;若训练集不含缺失值的特征少,则训练出的模型会欠拟合

综上,建议直接采用均值或中位数填补,操作简单且效果较好

1.2 特征处理

结合所使用模型的特点(不同模型对输入数据的要求不同),将清洗后的数据进行相应的转化

1.2.1 归一化

将不同特征取值的量纲统一,下图是从某个数据项目中取出的5个样本对应特征的实际数据,可以看到,f5 和 f288 这两个特征的取值完全不在一个量级上

 假设我们直接训练线性回归模型,会得到表达式

h(x)=w_1x^{(1)}+...+w_Nx^{(N)}+b

式中,x^{(j)},j=1,2,...,N,表示样本数据中的 N 个特征,w_j 是特征 x^{(j)}对应的权重系数

由于 f5 和 f288 这两个特征的量纲不一样,因此会导致 f5 和 f288 这两个特征对应的权重系数的量纲也不一样

在大多数情况下,特征对应的权重系数值大小可以直接反映该特征在模型中的重要性

如果不对特征的量纲进行归一化,直接比较两个特征的权重系数是没有意义的

特征归一化的原理

x'=\frac{x-x_{min}}{x_{max}-x_{min}}

式中,x 表示某个特种的原始取值,{x}' 表示该特征被归一化处理后的取值,x_{max} 和 x_{min} 分别表示该特征原始取值中的最大值和最小值

将数据进行归一化处理后,该特征的所有取值都将被压缩至[0,1]区间

分别对训练集和测试集数据中的特征进行归一化处理,就可以得到一个统一量纲的特征数据集了

特征归一化会改变特征取值的分布

1.2.2 标准化

标准化是另一种统一特征量纲的方法,表达式为

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

式中,\mu 是某个特征的原始取值的均值,\sigma 是对应标准差

进行特征标准化处理后,特征数据的取值范围并不一定在[0,1]区间;

特征标准化就是将原始的特征数据转化成一个标准的正态分布,它的前提其实假设了原始特征数据的取值分布服从正态分布

特征标准化不会改变特征取值的分布

1.2.3 离散化

一种简单、灵活且精细的数值型特征处理方式

我们可以直接将某个数值型特征根据其取值大小做均分,比如1~100被均分成10等份,即特征取值1~10对应类别 “ 1 ”,11~20对应类别 “ 2 ”,……,91~100对应类别 “ 10 ”

实际情况下,会根据业务特征和数据的分布情况来决定离散化的划分区间

例如数据集年龄的取值为1~80岁,那么可能的划分为:0~18为类别 “ 1 ”,表示未成年人;19~35为类别 “ 2 ”,表示青壮年;36~48为类别 “ 3 ”,表示中年……以此类推

以建立用户血糖预测模型为例:

 

1.2.4 one-hot编码

对于类别型特征,有些模型是无法处理的

例如在线性回归模型或者Logistic回归模型中,模型的基本原理是赋予各个特征一个权重系数,即用该特征的取值乘以对应的权重系数得到的结果作为该特征在模型中的贡献值

但对于类别特征(例如性别 “ 男 ”和 “ 女 ”),就无法直接将其视为一个具体的数值,且要求该数值就能代表该特征的重要性,于是我们使用one-hot编码解决这一问题,示例如下

示例中有性别特征(取值为 “ 男 ” 和 “ 女 ”)和年龄特征(取值1~7共7个级别)

所以对于第一个样本,它的原始类别是 “ 年龄-3 ”“性别-男”,所以它进行 one-hot编码后对应的向量就是[0,1,0,0,1,0,0,0,0];第二个样本编码后的向量为[1,0,0,1,0,0,0,0,0]

即只有对应类别型的数值为 “ 1 ”,其他均为 “ 0 ”

1.3 特征交互

特征交互就是人为的或者通过构造模型自动将两个或两个以上的特征进行交互将不同特征组合在一起,形成新的特征,以提高模型的预测能力和准确性

交互方式:求和、求差、相乘、取对数等

假设原始特征为 f1 和 f2,则这两个特征之间的交互可以是 f1 + f2、f1 - f2 或 f1 × f2 等

简单的线性模型使用独立特征x^{(j)},j=1,2,...,N的线性组合来预测结果:

h(x)=w_1x^{(1)}+...+w_Nx^{(N)}+b

通过特征相乘(自己与自己,自己与其他人)对线性模型进行扩展

h(x)=w_1x^{(1)}+...+w_Nx^{(N)}+w_{1,1}x^{(1)}x^{(1)}+w_{1,2}x^{(1)}x^{(2)}+...+w_{N,N}x^{(N)}x^{(N)}+b

Python实现如下:

import numpy as np

#原始特征
train_array = np.arange(6).reshape(3,2)
print(train_array)
print("\n")

#交互特征
from sklearn.preprocessing import PolynomialFeatures
ploy1 = PolynomialFeatures(degree = 2, interaction_only = False, include_bias = True)
train_array2 = ploy1.fit_transform(train_array)
print(train_array2)

 结果:

 原始特征数据中有两个特征:x^{(1)}=\begin{bmatrix} 0 \\ 2 \\ 4 \end{bmatrix}x^{(2)}=\begin{bmatrix} 1 \\ 3 \\ 5 \end{bmatrix}

它的2次多项式的次数为[1,x^{(1)},x^{(2)},x^{(1)}x^{(1)},x^{(1)}x^{(2)},x^{(2)}x^{(2)}]

解释:

第一项 1 为:(x^{(1)})^0(x^{(2)})^0

第二项 x^{(1)} 为:(x^{(1)})^1(x^{(2)})^0

第三项 x^{(2)} 为:(x^{(1)})^0(x^{(2)})^1

……

PolynomialFeatures()函数有三个参数:

\bullet       degree:控制多项式的次数;
\bullet       interaction_only:默认为 False,如果指定为 True,那么就不会有特征自己和自己结合的项,组合的特征中没有 x^{(1)}x^{(1)}  和 x^{(2)}x^{(2)},结果如下

\bullet       include_bias:默认为 True ,如果为 False 的话,那么结果中就没有 0 次幂项,即全为 1 的一列,结果如下:

 若将interaction_only设置为True,include_bias设置为False,则结果如下:

 

1.4 特征映射

特征映射更高级,一般使用机器学习模型来实现,例如基于梯度提升树(GBDT)的高阶特征映射,暂不了解

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值