【转】datawhale
0x00 前言
数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。由此可见,特征工程在机器学习中占有相当重要的地位。在实际应用当中,可以说特征工程是机器学习成功的关键。
那特征工程是什么?
特征工程是利用数据领域的相关知识来创建能够使机器学习算法达到最佳性能的特征的过程。
特征工程又包含了Feature Selection(特征选择)、Feature Extraction(特征提取)和Feature construction(特征构造)等子问题,本章内容主要讨论特征选择相关的方法及实现。
在实际项目中,我们可能会有大量的特征可使用,有的特征携带的信息丰富,有的特征携带的信息有重叠,有的特征则属于无关特征,如果所有特征不经筛选地全部作为训练特征,经常会出现维度灾难问题,甚至会降低模型的准确性。因此,我们需要进行特征筛选,排除无效/冗余的特征,把有用的特征挑选出来作为模型的训练数据。
0x01 特征选择介绍
1.特征按重要性分类
2.特征选择的目的
对于一个特定的学习算法来说,哪一个特征是有效的是未知的。因此,需要从所有特征中选择出对于学习算法有益的相关特征。而且在实际应用中,经常会出现维度灾难问题。如果只选择所有特征中的部分特征构建模型,那么可以大大减少学习算法的运行时间,也可以增加模型的可解释性。
3.特征选择的原则
获取尽可能小的特征子集,不显著降低分类精度、不影响分类分布以及特征子集应具有稳定、适应性强等特点。
0x02 特征选择的方法
1.Filter方法(过滤式)
先进行特征选择,然后去训练学习器,所以特征选择的过程与学习器无关。相当于先对特征进行过滤操作,然后用特征子集来训练分类器。
主要思想:对每一维特征“打分”,即给每一维的特征赋予权重,这样的权重就代表着该特征的重要性,然后依据权重排序。
主要方法:
优点:运行速度快,是一种非常流行的特征选择方法。
缺点:无法提供反馈,特征选择的标准/规范的制定是在特征搜索算法中完成,学习算法无法向特征搜索算法传递对特征的需求。另外,可能处理某个特征时由于任意原因表示该特征不重要,但是该特征与其他特征结合起来则可能变得很重要。
2.Wrapper方法(封装式)
直接把最后要使用的分类器作为特征选择的评价函数,对于特定的分类器选择最优的特征子集。
主要思想:将子集的选择看作是一个搜索寻优问题,生成不同的组合,对组合进行评价,再与其他的组合进行比较。这样就将子集的选择看作是一个优化问题,这里有很多的优化算法可以解决,尤其是一些启发式的优化算法,如GA、PSO(如:优化算法-粒子群算法)、DE、ABC(如:优化算法-人工蜂群算法)等。
主要方法:递归特征消除算法。
优点:对特征进行搜索时围绕学习算法展开的,对特征选择的标准/规范是在学习算法的需求中展开的,能够考虑学习算法所属的任意学习偏差,从而确定最佳子特征,真正关注的是学习问题本身。由于每次尝试针对特定子集时必须运行学习算法,所以能够关注到学习算法的学习偏差/归纳偏差,因此封装能够发挥巨大的作用。
缺点:运行速度远慢于过滤算法,实际应用用封装方法没有过滤方法流行。
3.Embedded方法(嵌入式)
将特征选择嵌入到模型训练当中,其训练可能是相同的模型,但是特征选择完成后,还能给予特征选择完成的特征和模型训练出的超参数,再次训练优化。
主要思想:在模型既定的情况下学习出对提高模型准确性最好的特征。也就是在确定模型的过程中,挑选出那些对模型的训练有重要意义的特征。
主要方法:用带有L1正则化的项完成特征选择(也可以结合L2惩罚项来优化)、随机森林平均不纯度减少法/平均精确度减少法。
优点:对特征进行搜索时围绕学习算法展开的,能够考虑学习算法所属的任意学习偏差。训练模型的次数小于Wrapper方法,比较节省时间。
缺点:运行速度慢。
0x03 特征选择实现方法一:去掉取值变化小的特征 (Removing features with low variance)
该方法一般用在特征选择前作为一个预处理的工作,即先去掉取值变化小的特征,然后再使用其他特征选择方法选择特征。
考察某个特征下,样本的方差值,可以认为给定一个阈值,抛弃哪些小于某个阈值的特征。
1.实现原理
而且实际当中,一般不太会有95%以上都取某个值的特征存在,所以这种方法虽然简单但是不太好用。可以把它作为特征选择的预处理,先去掉那些取值变化小的特征,然后再从接下来提到的的特征选择方法中选择合适的进行进一步的特征选择。
2.实现代码
0x04 特征选择实现方法二:单变量特征选择
单变量特征选择方法独立的衡量每个特征与响应变量之间的关系,单变量特征选择能够对每一个特征进行测试,衡量该特征和响应变量之间的关系,根据得分扔掉不好的特征。该方法简单,易于运行,易于理解,通常对于理解数据有较好的效果(但对特征优化、提高泛化能力来说不一定有效);这种方法有许多改进的版本、变种。
1.Pearson相关系数(Pearson Correlation)
皮尔森相关系数是一种最简单的,能帮助理解特征和响应变量之间关系的方法,该方法衡量的是变量之间的线性相关性。
1)原理介绍
2)主要用于连续型特征的筛选,不适用于离散型特征的筛选。
3)优缺点
4)代码实现
import numpy as npfrom scipy.stats import pearsonrnp.random.seed(2019)size=1000x = np.random.normal(0, 1, size)# 计算两变量间的相关系数print('Lower noise {}'.format(pearsonr(x, x + np.random.normal(0, 1, size))))print('Higher noise {}'.format(pearsonr(x, x + np.random.normal(0, 10, size))))
2.互信息和最大信息系数(Mutual information and maximal information coefficient)
如果变量不是独立的,那么我们可以通过考察联合概率分布与边缘概率分布乘积之间的 Kullback-Leibler 散度来判断它们是否“接近”于相互独立。
1)互信息方法
熵H(Y)与条件熵H(Y|X)之间的差称为互信息,互信息与条件熵之间的关系:
其实,这就是ID3决策树的特征选择规则。
互信息法也是评价定性自变量对定性因变量的相关性的,但是并不方便直接用于特征选择:
2)最大信息系数方法
由于互信息法并不方便直接用于特征选择,因此引入了最大信息系数。最大信息数据首先寻找一种最优的离散方式,然后把互信息取值转换成一种度量方式,取值区间为[0,1]。
3)最大信息系数方法代码实现
3.距离相关系数(Distance correlation)
距离相关系数是为了克服Pearson相关系数的弱点而生的。
1)原理介绍
Pearson相关系数是0,我们也不能断定这两个变量是独立的(有可能是非线性相关)。
例如x和x^2之间的Pearson相关系数是0,但是两个变量并不是独立的。
2)代码实现
from scipy.spatial.distance import pdist, squareformimport numpy as npfrom numbapro import jit, float32def distcorr(X, Y): ''' Compute the distance correlation function >>> a = [1,2,3,4,5] >>> b = np.array([1,2,9,4,4]) >>> distcorr(a, b) 0.762676242417 ''' X = np.atleast_1d(X) Y = np.atleast_1d(Y) if np.prod(X.shape) == len(X): X = X[:, None] if np.prod(Y.shape) == len(Y): Y = Y[:, None] X = np.atleast_2d(X) Y = np.atleast_2d(Y) n = X.shape[0] if Y.shape[0] != X.shape[0]: raise ValueError('Number of samples must match') a = squareform(pdist(X)) b = squareform(pdist(Y)) A = a - a.mean(axis=0)[None, :] - a.mean(axis=1)[:, None] + a.mean() B = b - b.mean(axis=0)[None, :] - b.mean(axis=1)[:, None] + b.mean() dcov2_xy = (A * B).sum()/float(n * n) dcov2_xx = (A * A).sum()/float(n * n) dcov2_yy = (B * B).sum()/float(n * n) dcor = np.sqrt(dcov2_xy)/np.sqrt(np.sqrt(dcov2_xx) * np.sqrt(dcov2_yy)) return dcor
4.基于学习模型的特征排序(Model based ranking)
这种方法的思路是直接使用你要用的机器学习算法,针对每个单独的特征和响应变量建立预测模型。如果特征与响应变量之间的关系是非线性的,则有许多替代方案,例如基于树的方法(决策树,随机森林)、或者扩展的线性模型等。基于树的方法是最简单的方法之一,因为他们可以很好地模拟非线性关系,不需要太多的调整。但是要避免的主要是过度拟合,因此树的深度应该相对较小,并且应该应用交叉验证。
代码实现
5.卡方检验
卡方检验是一种用途很广的计数资料的假设检验方法,由卡尔·皮尔逊提出。卡方值描述两个事件的独立性或者描述实际观察值与期望值的偏离程度。卡方值越大,表名实际观察值与期望值偏离越大,也说明两个事件的相互独立性越弱。
1)原理介绍
CHI值(卡方值)用于衡量实际值与理论值的差异程度,除以T是为了避免不同观察值与不同期望之间产生的偏差因T的不同而差别太大,所以除以E以消除这种弊端。
2)实现流程
CHI值越大,说明两个变量越不可能是独立无关的,也就是说CHI值越大,两个变量的相关程度也越高。
a. 对于特征变量x1,x2,…,xn,以及分类变量y。只需要计算CHI(x1,y)、CHI(x2,y)、…、CHI(xn,y),并按照CHI的值从大到小将特征排序。
b. 选择合适的阈值,大于阈值的特征留下,小于阈值的特征删除。这样筛选出一组特征子集就是输入模型训练的特征。
3)只适用于分类问题中离散型特征筛选,不能用于分类问题中连续型特征的筛选,也不能用于回归问题的特征筛选。
4)代码实现
现实方法:
#导入sklearn库中的SelectKBest和chi2from sklearn.feature_selection import SelectKBest ,chi2#选择相关性最高的前5个特征X_chi2 = SelectKBest(chi2, k=5).fit_transform(X, y)X_chi2.shape输出:(27, 5)
0xFF 总结
去掉取值变化小的特征方法和单变量特征选择方法都属于过滤式类特征筛选方法,但是学习算法无法向特征搜索算法传递对特征的需求。为了真正关注的是学习问题本身,我们将在《特征工程系列:特征筛选的原理与实现(下)》中继续介绍Wrapper方法和Embedded方法的原理与实现。
0x00 前言
我们在《特征工程系列:特征筛选的原理与实现(上)》中介绍了特征选择的分类,并详细介绍了过滤式特征筛选的原理与实现。本篇继续介绍封装式和嵌入式特征筛选的原理与实现。
0x01 特征选择实现方法三:线性模型与正则化
1.主要思想
当所有特征在相同尺度上时,最重要的特征应该在模型中具有最高系数,而与输出变量不相关的特征应该具有接近零的系数值。即使使用简单的线性回归模型,当数据不是很嘈杂(或者有大量数据与特征数量相比)并且特征(相对)独立时,这种方法也能很好地工作。
2.正则化模型
正则化就是把额外的约束或者惩罚项加到已有模型(损失函数)上,以防止过拟合并提高泛化能力。损失函数由原来的E(X,Y)变为E(X,Y)+alpha||w||,w是模型系数组成的向量(有些地方也叫参数parameter,coefficients),||·||一般是L1或者L2范数,alpha是一个可调的参数,控制着正则化的强度。当用在线性模型上时,L1正则化和L2正则化也称为Lasso和Ridge。
1)L1正则化/Lasso regression
L1正则化将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法。
Lasso能够挑出一些优质特征,同时让其他特征的系数趋于0。当如需要减少特征数的时候它很有用,但是对于数据理解来说不是很好用。
2)L2正则化/Ridge regression
L2正则化将系数向量的L2范数添加到了损失函数中。
L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。所以L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。
3.原理介绍
多元线性回归,具有n个特征值,预测公式如下。
多元线性回归方程演变成求θ。
每个特征都有对应的权重系数coef,特征的权重系数的正负值代表特征与目标值是正相关还是负相关,特征的权重系数的绝对值代表重要性。
sklearn中 中LinearRegression的fit()方法就是通过训练集求出θ,LinearRegression的两个属性intercept和coef分别对应θ0和θ1-θn。
4.代码实现
1)普通线性模型
结果分析:
2)L1正则化线性模型
#A helper method for pretty-printing linear modelsdef pretty_print_linear(coefs, names = None, sort = False): if names == None: names = ['X%s' % x for x in range(len(coefs))] lst = zip(coefs, names) if sort: lst = sorted(lst, key = lambda x:-np.abs(x[0])) return ' + '.join('%s * %s' % (round(coef, 3), name) for coef, name in lst)from sklearn.linear_model import Lassofrom sklearn.preprocessing import StandardScalerfrom sklearn.datasets import load_bostonboston = load_boston()scaler = StandardScaler()X = scaler.fit_transform(boston['data'])Y = boston['target']names = boston['feature_names']lasso = Lasso(alpha=.3)lasso.fit(X, Y)print('Lasso model: {}'.format( pretty_print_linear(lasso.coef_, names, sort = True)))# 输出:Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO+ -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM+ 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX
许多特征具有系数0。L1正则化回归的稳定性与非正则化线性模型类似,这意味着当数据中存在相关特征时,系数(以及特征等级)即使在小数据变化时也会发生显着变化。
3)L2正则化线性模型
从示例中可以看出,线性回归的系数变化很大,具体取决于生成的数据。然而,对于L2正则化模型,系数非常稳定并且密切反映数据的生成方式(所有系数接近1)。
0x02 特征选择实现方法四:随机森林选择
随机森林具有准确率高、鲁棒性好、易于使用等优点,这使得它成为了目前最流行的机器学习算法之一。随机森林提供了两种特征选择的方法:mean decrease impurity和mean decrease accuracy。
1.平均不纯度减少(mean decrease impurity)
1)原理介绍
2)代码实现
from sklearn.datasets import load_bostonfrom sklearn.ensemble import RandomForestRegressorimport numpy as np#Load boston housing dataset as an exampleboston = load_boston()X = boston['data']Y = boston['target']names = boston['feature_names']# 训练随机森林模型,并通过feature_importances_属性获取每个特征的重要性分数。rf = RandomForestRegressor()rf.fit(X, Y)print('Features sorted by their score:')print(sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names), reverse=True))
2.平均精确度减少(mean decrease accuracy)
1)原理介绍
2)代码实现
0x03 特征选择实现方法五:顶层特征选择
顶层特征选择发建立在基于模型的特征选择方法基础之上的,例如线性回归和SVM等,在不同的子集上建立模型,然后汇总最终确定特征得分。
1.稳定性选择(Stability selection)
稳定性选择常常是一种既能够有助于理解数据又能够挑出优质特征的这种选择。
1)原理介绍
2)代码实现
from sklearn.linear_model import RandomizedLassofrom sklearn.datasets import load_bostonboston = load_boston()#using the Boston housing data.#Data gets scaled automatically by sklearn's implementationX = boston['data']Y = boston['target']names = boston['feature_names']rlasso = RandomizedLasso(alpha=0.025)rlasso.fit(X, Y)print('Features sorted by their score:')print(sorted(zip(map(lambda x: round(x, 4), rlasso.scores_), names), reverse=True))
2.递归特征消除(Recursive feature elimination,RFE)
1)原理介绍
2)代码实现
0xFF 总结
最后,特征筛选是为了理解数据或更好地训练模型,我们应该根据自己的目标来选择适合的方法。为了更好/更容易地训练模型而进行的特征筛选,如果计算资源充足,应尽量避免过度筛选特征,因为特征筛选很容易丢失有用的信息。如果只是为了减少无效特征的影响,为了避免过拟合,可以选择随机森林和XGBoost等集成模型来避免对特征过拟合。
联系客服