对一个学习任务来说,给定属性集,其中有些属性可能很关键、很有用,另一些属性则可能没什么用,我们将属性称为“特征”(feature),对当前学习任务有用的属性称为“相关特征”(relevant feature)、没什么用的属性称为“无关特征”(irrelavant feature)。从给定的特征集合中选择出相关特征子集的过程,称为“特征选择”(feature selection)。
【注意】:
特征选择是一个重要的“数据预处理”过程,在现实机器学习任务中,获得数据之后通常先进行特征选择,此后再训练学习器,那么为什么要进行特征选择呢?
【冗余特征(redundant feature)】:所包含的信息能从其他特征中推演出来。例如,考虑立方体对象,若已有特征“底面长”、“底面宽”,则“底面积”是冗余特征,去除它们会减轻学习过程的负担。但有时冗余特征会降低学习任务的难度,例如若学习目标是估算立方体的体积,则“底面积”这个冗余特征的存在将使得体积的估算更容易;更确切地说,若某个冗余特征恰好对应了完成学习任务所需的“中间概念”,则该冗余特征是有益的。
了解了上述基本概念后,我们该如何从初始的特征集合中选取一个包含所有重要信息的特征子集?
第三个方案涉及两个关键环节:
第一个环节是“子集搜索”(subset search)问题。
【前向搜索(forward)】:给定特征集合 { a 1 , a 2 , ⋯   , a d } \{a_1, a_2,\cdots, a_d\} { a1,a2,⋯,ad},我们可将每个特征看作一个候选子集,对这 d 个候选单特征子集进行评价,假定 { a 2 } \{a_2\} { a2} 最优,于是将 a 2 {a_2} a2 作为第一轮的选定集;然后,在上一轮的选定集中加入一个特征,构成包含两个特征的候选子集,假定在这 d - 1 个候选两特征子集中 a 2 , a 4 {a_2, a_4} a2,a4 最优,且优于 a 2 {a_2} a2,于是将 a 2 , a 4 {a_2, a_4} a2,a4 作为本轮的选定集。假定在第 k + 1 轮时,最优的候选 (k + 1) 特征子集步入上一轮的选定集,则停止生成候选子集,并将上一轮选定的 k 特征集合作为特征选择结果。
【后向搜索(backward)】:从完整的特征集合开始,每次尝试去掉一个无关特征。
【双向搜索(bidirectional)】:将前向和后向搜索结合起来,每一轮逐渐增加选定相关特征(这些特征在后续轮中将确定不会被去除)、同时减少无关特征。
【问题】:上述策略都是贪心的,因为它们仅考虑使本轮选定集最优,例如在第三轮假定选择 a5 优于 a6,于是选定集为 { a 2 , a 4 , a 5 } \{a_2, a_4, a_5\} { a2,a4,a5},然而在第四轮却可能是 { a 2 , a 4 , a 6 , a 8 } \{a_2, a_4, a_6, a_8\} { a2,a4,a6,a8} 比所有的 { a 2 , a 4 , a 5 , a i } \{a_2, a_4, a_5, a_i\} { a2,a4,a5,ai} 都更优。遗憾的是,若不进行穷举搜索,则这样的问题无法避免。
无论是前向、后向还是双向搜索都是贪心策略,这让我想起吴恩达老师的深度学习课程中所讲的集束搜索策略。集束搜索策略是指什么呢?我们在特征子集搜索的每一步过程中多选择几个特征,例如按照最优的结果降序排列,我们选择最前面的 3 个特征。假设选择的是 { a 2 } , { a 4 } , { a 5 } \{a_2\}, \{a_4\}, \{a_5\} { a2},{ a4},{ a5},然后分别以这三个子集作为起点出发,继续寻找最优的 3 个特征子集,那么此时可获得 9 个特征子集(有可能会重复,例如 { a 2 , a 4 } , { a 4 , a 2 } \{a_2, a_4\}, \{a_4, a_2\} { a2,a4},{ a4,a2}),再从这 9 个特征子集中选择最优的 3 个特征子集进入到下一轮搜索过程。也就是说,最终我们可以得到 3 个特征子集,而 3 就是集束搜索的集束宽,这个参数我们可以自行设置,参数越大计算开销也越大,但获取最优解的概率也越高。
关于集束搜索的详细内容可以参考吴恩达老师深度学习课程的笔记 传送门-3.3 集束搜索(Beam Search)
第二个环节是“子集评价”(subset evaluation)问题。
我们可以用决策树算法中的信息增益准则作为评价准则。其实,特征子集是对数据集的一个划分。因此除了信息熵之外,其他能判断划分差异的机制,例如不合度量、相关系数等,稍加调整即可用于特征子集评价。
此外,也可以直接用学习器训练当前特征子集的最终评分作为该特征子集的评价标准。后续要说明的包裹式以及嵌入式特征选择方法就是以学习器的性能作为特征子集的评价标准。
将特征子集搜索机制与子集评价机制相结合,即可得到特征选择方法。例如将前向搜索与信息熵相结合,这显然与决策树算法非常相似。其他的特征选择方法未必像决策树特征选择这么明显,但它们在本质上都是显式或隐式地结合了某种子集搜索机制和子集评价机制。
【常见的特征选择方法】:
过滤式方法先按照某种规则对数据集进行特征选择,然后再训练学习器,特征选择过程与后续学习器无关,这相当于先用特征选择过程对初始特征进行“过滤”,再用过滤后的特征来训练模型。
【某种规则】:按照发散性或相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,从而选择满足条件的特征。
计算各个特征的方差,然后根据阈值选择方差大于阈值的特征,或者指定待选择的特征数 k,然后选择 k 个最大方差的特征。
方差选择的依据是什么?举个极端的例子,在多分类问题中,如果某特征只有一个取值,那么该特征对分类结果没有任何意义,因为不管取什么值都为 1,单凭该特征是无法区分样本的分类。
需要注意的是,方差选择法只有在特征是离散型时才适用,如果是连续型则需要离散化后才能使用。此外,该方法在实际问题中效果并非很好,参考如下数据集:
A B Y
1 1 0
2 1 0
3 1 0
1 2 1
2 2 1
3 2 1
特征 A 的方差 4 要大于特征 B 的方差 1.5,但特征 A 对最终分类结果 Y 的区分度明显没有特征 B 好。单看这 6 条数据,特征 A 几乎没有办法区分 Y 是 0 还是 1。因此我们需要明确一个概念,特征值的方差越大不一定对分类结果有更好的区分。关键原因是特征值的方差仅仅只考虑自身的取值,而没有结合最终的分类结果。
【代码实现】:sklearn。
>>> from sklearn.preprocessing import VarianceThreshold
>>> X = [[0, 2, 0, 3], [0, 1, 4, 3], [0, 1, 1, 3]]
>>> selector = VarianceThreshold()
>>> selector.fit_transform(X)
array([[2, 0],
[1, 4],
[1, 1]])
若不传给 VarianceThreshold() 阈值,则默认移除方差为 0 的特征。同样,我们也可以给 VarianceThreshold() 传递阈值:
>>> X = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]
>>> sel = VarianceThreshold(threshold=(0.16))
>>> sel.fit_transform(X)
array([[0, 1],
[1, 0],
[0, 0],
[1, 1],
[1, 0],
[1, 1]])
关于 VarianceThreshold() 的实现可参考官方 API 传送门
不借助 sklearn 自行实现方差选择法,那么该如何编写代码呢?思路非常简单,先计算每一个特征的方差值,然后依次比对方差值与阈值,选择方差值大于阈值的特征。
>>> def variance_select(data, threshold=0):
... variance_list = np.var(data, axis=0)
... result, ind = [], 0
... for variance in variance_list:
... if variance > threshold:
... result.append(ind)
... ind += 1
... return np.array(data)[:, result]
...
>>> variance_select(X, 0.16)
array([[0, 1],
[1, 0],
[0, 0],
[1, 1],
[1, 0],
[1, 1]])
计算各个特征对目标值的相关系数及相关系数的 P 值。
在机器学习中我们一般采用皮尔逊相关系数来测量两个序列的线性关系,也就是说皮尔逊相关系数只能检测出线性关系,那么对于分类问题的适用性就远低于回归问题,因此相关系数法常用于回归问题。
为什么皮尔逊相关系数只能测量线性关系呢?具体解释可参考这篇博文 传送门。
【代码实现】:我们以 sklearn.datasets 中的波士顿房价数据集为例。
import numpy as np
import pandas as pd
from sklearn.datasets import load_boston
dataset_boston = load_boston()
dataset = dataset_boston.data
labels = dataset_boston.target
# 我们把 label 也加到 dataset 中
dataset_all = np.column_stack((dataset, labels))
columns = [name for name in dataset_boston.feature_names]
columns.append('label')
df_dataset = pd.DataFrame(data=dataset, columns=columns)
df_dataset.corr(method='pearson')
通过 df_dataset.corr(method=‘pearson’) 这句指令,我们可以看到各特征两两之间的皮尔逊相关系数。当然我们更关注的是特征与最终要预测的对象(房价)的相关系数。
除了使用 pandas 的 corr(method=‘pearson’) 方法之外,我们还可以使用 scipy.stats.pearsonr() 方法。
>>> from scipy.stats import pearsonr
>>> pearsonr(dataset[:, 0], labels)
(-0.38830460858681154, 1.1739870821945733e-19)
>>> pearsonr(dataset[:, 1], labels)
(0.3604453424505432, 5.713584153081686e-17)
上述代码分别计算 CRIM、ZN 和 label 之间的相关系数,可以看到输出结果的第一项与 corr(method=‘pearson’) 计算的结果相同,不同的是 pearsonr() 方法还多输出了一项 1.1739870821945733e-19 和 5.713584153081686e-17。
scipy.stats.pearsonr() 对给定两个数据序列会返回相关系数值和 p 值所组成的元组。也就是说 1.1739870821945733e-19 和 5.713584153081686e-17 就是这个 p 值,那么 p 值有什么用呢?p 值是该数据序列产生于一个不相关系统的概率,p 值越高,我们越不能信任这个相关系数。
不使用已有的方法自行实现相关系数的计算,依据相关系数的计算公式:
ρ = Cov ( X , Y ) σ X σ Y C o v ( X , Y ) = ∑ ( x − m x ) ( y − m y ) \rho=\frac{\operatorname{Cov}(X, Y)}{\sigma_{X} \sigma_{Y}} \quad Cov(X, Y) = \sum\left(x-m_{x}\right)\left(y-m_{y}\right) ρ=σXσYCov(X,Y)Cov(X,Y)=∑(x−mx)(y−my)
其中, m x m_x mx 和 m y m_y my 分别是向量 x 和 y 的均值。
【代码实现】:
def corr(vector_A, vector_B):
if vector_A.shape[0] != vector_B.shape[0]:
raise Exception('The Vector must be the same size')
vector_A_mean, vector_B_mean = np.mean(vector_A), np.mean(vector_B)
vector_A_diff, vector_B_diff = vector_A - vector_A_mean, vector_B - vector_B_mean
molecule = np.sum(vector_A_diff * vector_B_diff)
denominator = np.sqrt(np.sum(vector_A_diff**2) * np.sum(vector_B_diff**2))
return molecule / denominator
相关系数的取值在 -1 到 1 之间,-1 代表负相关、1 代表正相关、0 代表不相关。
>>> corr(np.array([1, 2, 3, 4, 5]), np.array([1, 4, 7, 10, 13]))
1.0
>>> corr(np.array([1, 2, 3, 4, 5]), np.array([13, 10, 7, 4, 1]))
-1.0
>>> corr(np.array([1, 2, 3, 4, 5]), np.array([7, 10, 4, 13, 1]))
-0.3
通过上述示例可以发现,特征与预测值的相关系数值越接近 -1 或 1 时,特征的变化趋势与预测值的变化趋势具有高度的一致性(反向或同向),也就是说这些特征对预测值产生的影响也越大,因此,我们优先选择相关系数绝对值大的特征。
检验定性自变量对定性因变量的相关性。关于卡方检验的介绍可参考这篇文章 卡方检验原理及应用。需要注意的是,卡方检验适用于分类问题。
【代码实现】:因为卡方检验适用于分类问题,因此以 sklearn.datasets 中的鸢尾花数据集为例。
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
dataset_iris = load_iris()
dataset = dataset_iris.data
labels = dataset_iris.target
model_sk = SelectKBest(score_func=chi2, k=3)
model_sk.fit(dataset, labels)
print(model_sk.scores_)
# 输出:array([ 10.81782088, 3.7107283 , 116.31261309, 67.0483602 ])
print(model_sk.pvalues_)
# 输出:array([4.47651499e-03, 1.56395980e-01, 5.53397228e-26, 2.75824965e-15])
卡方值越大,表明特征与预测结果的相关性也越大,同时 p 值也相应较小,因此我们优先选择卡方值大的特征。
评价定性自变量对定性因变量的相关性。
Relief(Relevant Features)是一种著名的过滤式特征选择方法,该方法设计了一个“相关统计量”来度量特征的重要性。该统计量是一个向量,其每个分量分别对应于一个初始特征,而特征子集的重要性则是由子集中每个特征所对应的相关统计量分量之和来决定。
【选择方式】:
【关键】:如何确定相关统计量。
给定训练集 { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x n , y n ) } \{(x_1, y_1), (x_2, y_2), \cdots, (x_n, y_n)\} { (x1,y1),(x2,y2),⋯,(xn,yn)},对每个实例 x i x_i xi,Relief 先在 x i x_i xi 的同类样本中寻找其最近邻 x i , n h x_{i,nh} xi,nh,称为“猜中近邻”(near-hit),再从 x i x_i xi 的异类样本中寻找其最近邻 x i , n m x_{i,nm} xi,nm 称为“猜错近邻”(near-miss),然后,相关统计量对应于特征 j 的分量为
δ j = ∑ i = 1 n − d i f f ( x i j , x i , n h j ) 2 + d i f f ( x i j , x i , n m j ) 2 \delta^j = \sum_{i=1}^n -diff(x_i^j, x_{i,nh}^j)^2 + diff(x_i^j, x_{i, nm}^j)^2 δj=i=1∑n−diff(xij,xi,nhj)2+diff(xij,xi,nmj)2
其中 x a j x_a^j xaj 表示样本 x a x_a xa 在特征 j 上的取值, d i f f ( x a j , x b j ) diff(x_a^j, x_b^j) diff(xaj,xbj) 取决于特征 j 的类型:
从上式中可以看出,若 ` x i x_i xi 与其猜中近邻 x i , n h x_{i,nh} xi,nh 在特征 j 上的距离小于 x i x_i xi 与其猜错近邻 x i , n m x_{i, nm} xi,nm 的距离,则说明特征 j 对区分同类与异类样本是有益的,于是增大特征 j 所对应的统计量分量;反之,若则说明特征 j 起负面作用,于是减小特征 j 所对应的统计量分量。
最后,对基于不同样本得到的估计结果进行平均,就得到各特征的相关统计量分量,分量值越大,则对应特征的分类能力就越强。
实际上 Relief 只需在数据集的采样上而不必在整个数据集上估计相关统计量。Relief 的时间开销随采样次数以及原始特征数呈线性增长,因此是一个运行效率很高的过滤式特征选择算法。
Relief 是为二分类问题设计的,其扩展变体 Relief-F 能处理多分类问题。
【Relief-F】:假定数据集 D 中的样本来自 |Y| 个类别。对实例 x i x_i xi,若它属于第 k 类,则 Relief-F 先在第 k 类的样本中寻找 x i x_i xi 的最近邻实例 x i , n h x_{i, nh} xi,nh 并将其作为猜中近邻,然后在第 k 类之外的每个类中找到一个 x i x_i xi 的最近邻实例走位猜错近邻,记为 x i , l , n m ( l = 1 , 2 , ⋯   , ∣ Y ∣ ; l ≠ k ) x_{i,l,nm}(l = 1, 2, \cdots, |Y|; l \neq k) xi,l,nm(l=1,2,⋯,∣Y∣;l̸=k)。于是,相关统计量对应于特征 j 的分量为
δ j = ∑ i = 1 n − d i f f ( x i j , x i , n h j ) 2 + ∑ l ≠ k ( p l × d i f f ( x i j , x i , l , n m j ) 2 ) \delta^j = \sum_{i=1}^n -diff(x_i^j, x_{i,nh}^j)^2 + \sum_{l \neq k}(p_l \times diff(x_i^j, x_{i,l,nm}^j)^2) δj=i=1∑n−diff(xij,xi,nhj)2+l̸=k∑(pl×diff(xij,xi,l,nmj)2)
其中, p l p_l pl 为第 l 类样本在数据集 D 中所占的比例。
包裹式特征选择与过滤式特征选择不考虑后续学习器不同,直接把最终将要使用的学习器的性能作为特征子集的评价准则。换言之,包裹式特征选择的目的就是为给定学习器选择最有利于其性能、“量身定做”的特征子集。
【与过滤式选择的区别】:
LVW 是一个典型的包裹式特征选择方法,它在拉斯维加斯(Las Vegas method)框架下使用随机策略来进行子集搜索,并以最终分类器的误差为特征子集评价准则。
【算法】:
【注意】:由于 LVW 算法中特征子集搜索采用了随机策略,而每次特征子集评价都需要训练学习器,计算开销很大,因此算法设置了停止条件控制参数 T。然而,整个 LVW 算法是基于拉斯维加斯方法框架,若初始特征数很多(即 |A| 很大)、T 设置较大,则算法可能运行很长时间都达不到停止条件。换言之,若有运行时间限制,则有可能给不出解。
另外还有一个经典的算法——蒙特卡罗方法。这两个以著名赌城名字命名的随机化方法的主要区别是:若有时间限制,则拉斯维加斯方法或者给出满足要求的解,或者不给出解;而蒙特卡罗方法一定会给出解,虽然给出的解未必满足要求;若无时间限制,则两者都能给出满足要求的解。
嵌入式特征选择是将特征选择过程与学习器训练过程融为一体,两者在同一个优化过程中完成,即在学习器训练过程中自动地进行了特征选择。
给定数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x n , y n ) } D = \{(x_1, y_1), (x_2, y_2), \cdots, (x_n, y_n)\} D={ (x1,y1),(x2,y2),⋯,(xn,yn)},其中 x ∈ R d , y ∈ R x \in R^d, y \in R x∈Rd,y∈R。我们考虑最简单的线性回归模型,以平方误差为损失函数,则优化目标为
m i n w ∑ i = 1 n ( y i − w T x i ) 2 min_w \sum_{i=1}^n(y_i - w^Tx_i)^2 minwi=1∑n(yi−wTxi)2
当样本特征很多,而样本数相对较少时,上式很容易陷入过拟合。为了缓解过拟合问题,可对上式引入正则化项。
m i n w ∑ i = 1 n ( y i − w T x i ) 2 + λ ∣ ∣ w ∣ ∣ 2 2 min_w \sum_{i=1}^n(y_i - w^Tx_i)^2 + \lambda ||w||_2^2 minwi=1∑n(yi−wTxi)2+λ∣∣w∣∣22
通过引入 L2 范数正则化,确能显著降低过拟合的风险。
m i n w ∑ i = 1 n ( y i − w T x i ) 2 + λ ∣ ∣ w ∣ ∣ 1 min_w \sum_{i=1}^n(y_i - w^Tx_i)^2 + \lambda ||w||_1 minwi=1∑n(yi−wTxi)2+λ∣∣w∣∣1
L1 范数和 L2 范数正则化都有助于降低过拟合风险,但 L1 范数比 L2 范数更易于获得“稀疏”(sparse)解,即它求得的 w 会有更少的非零向量。
同时使用 L1 范数和 L2 范数,即可避免过拟合,同时也实现了降维,并筛选出相应的特征。
m i n w ∑ i = 1 n ( y i − w T x i ) 2 + λ 1 ∣ ∣ w ∣ ∣ 1 + λ 2 ∣ ∣ w ∣ ∣ 2 2 min_w \sum_{i=1}^n(y_i - w^Tx_i)^2 + \lambda_1 ||w||_1 + \lambda_2||w||_2^2 minwi=1∑n(yi−wTxi)2+λ1∣∣w∣∣1+λ2∣∣w∣∣22
决策树可用于特征选择,树节点的划分特征所组成的集合就是选择出的特征子集。