数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已,特征工程的本质就是一项工程活动,目的是最大限度地从原始数据中提取特征以供算法和模型使用。
特征工程主要分为三部分:
本文中使用sklearn中的RIS(鸢尾花)数据集来对特征处理能力进行说明,导入代码:
from sklearn.datasets import load_iris
# 导入IRIS数据集
iris = load_iris()
# 特征矩阵
iris.data
# 目标向量
iris.target
通过特征提取,我们能得到未处理的特征,这时的特征可能有以下问题:
sklearn中的preprocessing库来进行数据预处理
from sklearn.preprocessing import StandardScaler
std = StandardScaler()
# 标准化,返回值为标准化后的数据
std.fit_transform(iris.data)
# 标准化的特征目标向量(各种鸢尾花的名称)
std.fit_transform(iris.target.reshape(-1,1))
无量纲化使得不同规格的数据转换到同一规格
将服从正太分布的特征值转换为标准正态分布,标准化需要计算特征的均值和标准差,一般公式为: x ′ = x − X ‾ S x' = \frac{x - \overline{X}}{S} x′=Sx−X
使用preprocessing库的StandardScaler类对数据进行标准化的代码同上
区间缩放法的思路有多种,常见为最大最小值缩放,公式为:
x ′ = x − M i n M a x − M i n x' = \frac{x -Min}{Max - Min} x′=Max−Minx−Min
使用preprocessing库的MinMaxScaler类对数据进行区间缩放的代码如下:
from sklearn.preprocessing import MinMaxScaler
# 区间缩放,返回值为缩放到[0,1]的数值
MinMaxScaler().fit_transfrom(iris.data)
处理后的值最大值为1最小值为0,其他值按比例缩放
什么时候用标准化,什么时候用区间缩放比较好
归一化得目的在于样本向量在点乘运算或其他核函数计算相似性时,拥有统一的标准,也就是说都转换为"单位向量"。L2的归一化公式如下:
x ′ = x ∑ j m x [ j ] 2 x' =\frac{x}{\sqrt{\sum_{j}^{m}x[j]^2}} x′=∑jmx[j]2x
使用preprocessing库的Normalizer类对数据进行归一化的代码如下:
from sklearn.preprocessing import Normalizer
# 归一化,返回值为归一化后的数据
Normalizer().fit_transform(iris.data)
标准化(StandardScaler)和归一化(Normalizer)
StandardScaler就是尺寸缩放,将同一特征下的数值在一定范围内浮动,如将数值缩放在0~1范围内(MinMaxScaler),或者将数据标准化,变成均值为0,方差为1的数据(Z-score);
Normalizer就是将同一行数据的不同特征进行规范化,这样一个数据的不同特征具有相同的量纲或者表现力,比如说一个特征是身高1.7m,体重为150kg,那么两个特征之间差距太大,身高这个特征变化根本无法起到决定作用(在体重这个变化特征下),毕竟大家怎么长都是一米多,但是体重差距一下子拉开20多是很正常的事情
定性与定量的区别
定性:及格与不及格
定量:85分,39分
一般定性都会有相关的描述词,定量的描述一般都可以用数字量化处理
定量特征二值化的核心在于设定一个阈值,大于阈值的赋值为1,小于等于阈值的赋值为0
使用preprocessing库的Binarizer类对数据进行二值化的代码如下:
from sklearn.preprocessing import Binarizer
# 二值化,阈值设置为3,返回值为二值化后的数据
Binarizer(threshold=3).fit_transform(iris.data)
有些特征是用文字分类表达的,或者说将这些类转化为数字,但是数字与数字之间是没有大小关系的,纯粹的分类标记,这时候就需要用哑编码对其进行编码,IRIS数据集的特征皆为定量特征,使用其目标值进行哑编码(实际上是不需要的)。使用preprocessing库的OneHotEncoder类对数据进行哑编码的代码如下:
from sklearn.preprocessing import OneHotEncoder
# 哑编码,对IRIS数据集的目标值,返回值为哑编码后的数据
OneHotEncoder().fit_transform(iris.target.reshape(-1,1))
补充:但是从另一个角度来看,在标签需要被量化的时候就很有用了
由于IRIS数据集没有缺失值,故对数据集新增一个样本,4个特征均赋值为NaN,表示数据缺失,使用preprocessing库的Imputer类对数据进行缺失值计算的代码如下:
import numpy as np
from sklearn.preprocessing import Imputer
imp = Imputer(missing_values='NaN',strategy='mean',axis=0)
# 使用特征值的均值进行填充,其余还有众数(median)填充等
data = np.array([np.nan,2,6,np.nan,7,6]).reshape(3,2)
imp.fit_transform(data)
常见的数据变换有基于多项式的、基于指数函数的、基于对数函数的。4个特征,度为2的多项式转换公式如下:
(x1’,x2’,x3’,x4’,x5’.x6’,x7’,x8’,x9’,x10’,x11’.x12’,x13’,x14’.x15’)=(1,x1,x2,x3,x4, x12,x1*x2 , x1*x2 ,x1*x4 ,x22 ,x2 * x2,x2 * x4, x32 ,x3 * x4 ,x42)
使用preprocessing库的PolynomialFeatures类对数据进行多项式转换的代码如下:
from sklearn.preprocessing import PolynomialFeatures
# 多项式转换
# 参数degree为度,默认值为2
PolynomialFeatures().fit_transform(iris.data)
基于单变元函数的数据变换可以使用一个统一的方式完成,使用preprocessing库的FunctionTransformer对数据进行对数函数转换的代码如下:
from numpy import log1p # log(x+1)
from sklearn.preprocessing import FunctionTransformer
# 自定义转换函数为对数函数的数据变换
# 第一个参数是单变元函数
FunctionTransformer(log1p).fit_transform(iris.data)
类 | 功能 | 说明 |
---|---|---|
StandardScaler | 无量纲化 | 标准化,基于特征矩阵的列,将特征值转换至服从标准正态分布 |
MinMaxScaler | 无量纲化 | 区间缩放,基于最大值最小值,将特征值转换到[0,1]区间上 |
Normalizer | 归一化 | 基于特征矩阵的行,将样本向量转换为"单位向量" |
Binarizer | 二值化 | 基于给定阈值,将定量特征按阈值划分 |
OneHotEncoder | 哑编码 | 将定性数据编码为定量数据 |
Imputer | 缺失值计算 | 计算缺失值,缺失值可填充为均值等 |
PolynomialFeatures | 多项式数据转换 | 多项式数据转换 |
FunctionTransformer | 自定义单元数据转换 | 使用单变元的函数来转换数据 |
当数据预处理完成后,我们需要选择有意义的特征输入机器学习的算法和模型进行训练。通常来说,从两个方面考虑来选择特征:
根据特征选择的形式又可以将特征选择方法分为3种:
我们使用sklearn中的feature_selection库来进行特征选择
使用方差选择法,先要计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征。使用feature_selection库的VarianceThreshold类来选择特征的代码如下:
from sklearn.feature_selection import VarianceThreshold
# 方差选择法,返回值为特征选择后的数据
# 参数threshold为方差的阈值,为几就是过滤掉几个特征
VarianceThreshold(threshold=3).fit_transform(iris.data)
单变量特征选择能够对每一个特征进行测试,衡量该特征和目标变量之间的关系,根据得分扔掉不好的特征。对于回归和分类问题可以采用卡方检验等方式对特征进行测试。
方法简单,易于运行,易于理解,通常对于理解数据有较好的效果。(但对特征优化、提高泛化能力来说不一定有效);方法有很多改进版本、变种。
chi2卡方检验:适用于分类问题,y离散
经典的卡方检验(原理及应用)是检验定性自变量对定性因变量的相关性。假设自变量有N种取值,因变量有M种取值,考虑自变量等于i且因变量等于j的样本频数的观察值与期望的差距,构建统计量,其中A为实际数,E为理论值:
x 2 = ∑ ( A − E ) 2 E x^{2} = \sum{\frac{(A-E)^2}{E}} x2=∑E(A−E)2
检验特征对标签的相关性,选择其中K个标签最相关的特征。使用feature_selection库的SelectKBest类结合卡方检验来选择特征的代码如下:(这个就是自变量与因变量的相关性的取值,看到k的取值)
from sklearn.feature_selection import SelectKBest,chi2
# chi2就是卡方检验
# 选择k个最好的特征,返回选择特征后的数据
SelectKBest(chi2,k=2).fit_transform(iris.data, iris.target)
pearson相关系数:适用于回归问题(y连续)
皮尔森相关系数是一种最简单的,能帮助理解特征和目标变量之间关系的方法,该方法衡量的是变量之间的线性相关性,结果的取值区间为[-1, 1],-1表示完全的负相关,1表示完全正相关,0表示没有线性相关。
Pearson correlation速度快,易于计算,经常在拿到数据(经过清洗和特征提取之后)之后第一时间就之行。Scipy的pearsonr方法能够同时计算相关系数和p-value
import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = np.random.normal(0, 1, size)
# ↑ 创建300个均值为0,方差为1的高斯随机数
# pearsonr(x,y)的输入为特征矩阵和目标向量
print("Lower noise", pearsonr(x, x + np.random.normal(0,1,size)))
print("Higher noise", pearsonr(x, x + np.random.normal(0, 10, size)))
# 输出为二元组(sorce, p-value)的数组
Lower noise(0.71824836862138386, 7.3240173129992273e-49)
Higher noise (0.057964292079338148, 0.31700993885324746)
Mutual information and maximal information coefficient
后面讲EDA的时候再讲
距离相关系数是为了克服Peason相关系数的弱点而生的。在x和x2 这个例子中,即便pearson相关系数是0,我们也不能断定这两个变量是独立的(有可能是非线性相关的);但如果距离相关系数是0,那么我们可以说这两个变量是独立的。
R语言的energy包里提供了距离相关系数的实现,另外这是Python gist的实现
> x = runif(1000, -1, 1)
> dcor(x, x**2)
[1] 0.4943864
尽管有MIC和距离相关系数在,在变量之间的关系接近线性相关的时候,pearson相关系数仍然是不可替代的。
第一,pearson相关系数的计算速度快,在处理大规模数据的时候很重要
第二,pearson相关系数的取值区间是[-1, 1], 而MIC和距离相关系数都是[0, 1]。这个特点使得Pearson相关系数能够表征更丰富的关系,符号表示关系的正负,绝对值能够表示强度。当然pearson相关性有效的前提是两个变量的变化关系是单调的。
###3.2 Wrapper(包装法)
包装法的主要是思想是:根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征,也可以将特征子集的选择看作一个搜索寻优问题,生成不同的组合,对组合进行评价,再与其他的组合进行比较。这样就将自己的选择看作是一个优化问题。这里又很多的优化算法可以解决,尤其是一些启发式的优化算法,如GA,PSO,DE,ABC等。
递归消除特征法使用一个基模型来进行多轮训练,每轮训练后,消除若干权值系数的特征,再基于新的特征集进行下一轮训练,使用feature_selection库的RFE来选择特征的代码如下:
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
# 递归特征消除法,返回特征选择后的数据
# 参数 estimator为基模型
# 参数 n_feature_to_select为选择的特征个数
RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)
嵌入法主要思想是:使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于Filter方法,但是是通过训练来确定特征的优劣。其实是在确定模型的过程中,挑选出那些对模型的训练有重要意义的属性。
使用带惩罚项的基模型,除了筛选出特征外,同时也进行了降维,使用feature_selection库的SelectFromModel类结合带L1惩罚项的逻辑回归模型,来选择特征,代码如下:
from sklearn.feature_selection import SelectionFromModel
from sklearn,linear_model import LogisticRegression
# 带L1惩罚项的逻辑回归作为基模型的特征选择
SelectFromModel(LogisticRegression(penalty="l1", c=0.1)).fit_transform(iris.data, iris.target)
# 看一下例子,用的是支持向量机
from sklearn.svm import LinearSVC
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
iris = load_iris()
X, y = iris.data, iris.target
X.shape
# (150, 4)
lsvc = LinearSVC(0.01. penalty="l1", dual=False).fit(X, y)
model = SelectFromModel(lsvc, prefit=True)
X_new = model.transform(X)
X_new[:3,:]
#array([[ 5.1, 3.5, 1.4],
# [ 4.9, 3. , 1.4],
# [ 4.7, 3.2, 1.3]])
结合L2项下次看
from sklearn.feature_selection import SelectionFromModel
from sklearn.ensemble import GradientBoostingClassifier
# GBDT 作为基模型的特征选择
SelectFromModel(GradientBoostingClassifier()).fit_transform(iris.data, iris.target)
类 | 所属方式 | 说明 |
---|---|---|
VarianceThreshold | Filter | 方差选择法 |
SelectKBest | Filter | 可选关联系数、卡方校验、最大信息系数作为得分计算的方法 |
RFE | Wrapper | 递归地训练基模型,将权值系数较小的特征从特征集合中消除 |
SelectFromModel | Embedded | 训练基模型,选择权值系数较高的特征 |