特征选择的目标:
1.减少特征的数目来避免过拟合并提高模型的泛化能力
2.加深对特征与目标变量间的关系的理解
下面得到例子中,主要是对回归数据集,但是大多数的讨论和例子同样适用于分类数据集及方法。
在做回归前,为了减少特征,对于近似线性相关的模型,我们偏向用皮尔森相关系数,但还有MIC,distance correlation,添加高次项等方法。对于非线性的模型,我们偏向用基于树的方法(决策树,随机森林),基于树的方法用起来简单,也不用调参,但对这类方法做回归时要注意避免过拟合,可以通过剪枝和交叉检验来避免过拟合。还有一些机器学习模型中就有对特征的固定排名,像一些回归模型,SVM,决策树,随机森林等。
但是像单纯的线性回归模型,对于特征的选择又是有误差的,下面将通过实验介绍。
另外还有PCA等方法。
单变量特征选择
分别检查每个特征和目标变量间的关系。有以下几种选择
- 皮尔森相关系数
它描述了两个变量间的线性关系。
例子:比较一个参数在线性叠加不同程度的噪音下与其自身的线性相关性。
import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = np.random.normal(0, 1, size)
print "Lower noise", pearsonr(x, x + np.random.normal(0, 1, size))
print "Higher noise", pearsonr(x, x + np.random.normal(0, 10, size))
结果:
Lower noise (0.71824836862138386, 7.3240173129992273e-49)
Higher noise (0.057964292079338148, 0.31700993885324746)
pearsonr返回的第一个值是皮尔森相关系数,第二个值是p值。p值越小,表示相关系数越显著,一般p值在500个样本以上时有较高的可靠性。
结果表明噪音越低的参数与其自身的线性相关度越高。但不能检出非线性关系。最好还是先画图。
还有个缺点是不能推断出特征和特征间是否独立。
f_regression和pipeline
2. maximal information coefficient(MIC)最大互信息系数
互信息系数是可以度量变量相互关系(线性与非线性)的统计量
定义为:
I
(
X
,
Y
)
=
p
(
x
,
y
)
l
o
g
(
p
(
x
,
y
)
p
(
x
)
p
(
y
)
,
∑
y
∈
Y
,
∑
x
∈
X
I(X,Y) = p(x,y)log(p(x,y)p(x)p(y) ,\sum{y}\in {Y},\sum{x} \in {X}
I(X,Y)=p(x,y)log(p(x,y)p(x)p(y),∑y∈Y,∑x∈X
from minepy import MINE
m = MINE()
x = np.random.uniform(-1, 1, 10000)
m.compute_score(x, x**2)
print m.mic()
还需检验其统计检验力(怎么检验?)
3. distance correlation 距离相关性
距离相关性能清楚地解决皮尔森相关系数的缺点。
距离相关性能反应x与x^2两个参数是不独立的
#R-code
> x = runif (1000, -1, 1)
> dcor(x, x**2)
[1] 0.4943864
但当参数和目标参数的关系近于线性的时候,我们更偏向于使用皮尔森相关系数,其一是因为算起来快,其二是它还能带来别的信息,即是正相关还是负相关,除了pearson correlation 以为的统计量如MIC, distance correlation 的范围都在[0,1]
基于随机森林
- 随机森林
原文:http://blog.datadive.net/selecting-good-features-part-iii-random-forests/ - 按impurity(基尼系数或者信息熵这类)来排序特征(Mean decrease impurity)
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np
#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
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)
不过这种排序存在这样的偏差:分类越多的变量排名越高;
当数据集中有两个或更多的相关的特征时,那么这些特征对目标的解释程度应该是差不多的,但是按照随机森林处理后的结果与之矛盾。原文有做一个实验来说明(http://blog.datadive.net/selecting-good-features-part-iii-random-forests/)
除了基于随机森林的特征选择外,大多数基于模型的特征选择方法都存在对相关变量的排名的解释有问题的情况。所以在训练模型的时候不一定会因为我们丢弃了某个特征而导致模型效果大幅下降,因为还有其他的向边特征
- mean decrease accuracy
通过排列组合特征看每次对准确度减少了多少,从而体现每个特征对模型准确度的重要性。
from sklearn.cross_validation import ShuffleSplit
from sklearn.metrics import r2_score
from collections import defaultdict
X = boston["data"]
Y = boston["target"]
rf = RandomForestRegressor()
scores = defaultdict(list)
#crossvalidate the scores on a number of different random splits of the data
for train_idx, test_idx in ShuffleSplit(len(X), 100, .3):
X_train, X_test = X[train_idx], X[test_idx]
Y_train, Y_test = Y[train_idx], Y[test_idx]
r = rf.fit(X_train, Y_train)
acc = r2_score(Y_test, rf.predict(X_test))
for i in range(X.shape[1]):
X_t = X_test.copy()
np.random.shuffle(X_t[:, i])
shuff_acc = r2_score(Y_test, rf.predict(X_t))
scores[names[i]].append((acc-shuff_acc)/acc)
print "Features sorted by their score:"
print sorted([(round(np.mean(score), 4), feat) for
feat, score in scores.items()], reverse=True)
总结:随机森林来做特征选择的优点:只用做很少的特征工程,参数调整。但缺点有两点:1.对于相关的特征,强特征得到的分很低;2. 所分种类越多的特征得分越高
基于线性回归模型的特征选择
http://blog.datadive.net/selecting-good-features-part-ii-linear-models-and-regularization/
做了两个实验,说明如果两个特征的相关性较高,就会导致它们的系数判断误差大。(就是多重共线性)
岭回归和lasso回归通过引入不同范式表示的罚项来避免过拟合并提高泛化能力。
逐步回归的基本思想:将变量一个个引入,每引入一个变量时,要对已选入的变量进行逐个检验。当原引入的变量由于后面变量的引入而变得不再显著时,将其剔除。这个过程反复进行,直到既无显著的变量选入方程,也无不显著自变量从回归方程中剔除为止。
- lasso regression
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston
boston = 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: ", pretty_print_linear(lasso.coef_, names, sort = True)
- ridge regression
from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score
size = 100
#We run the method 10 times with different random seeds
for i in range(10):
print "Random seed %s" % i
np.random.seed(seed=i)
X_seed = np.random.normal(0, 1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X3 = X_seed + np.random.normal(0, .1, size)
Y = X1 + X2 + X3 + np.random.normal(0, 1, size)
X = np.array([X1, X2, X3]).T
lr = LinearRegression()
lr.fit(X,Y)
print "Linear model:", pretty_print_linear(lr.coef_)
ridge = Ridge(alpha=10)
ridge.fit(X,Y)
print "Ridge model:", pretty_print_linear(ridge.coef_)
print
岭回归的罚项是基于L2范式的,对同一模型的随机点多次回归,回归的系数相对稳定,且泛化能力强。
岭回归可以用于插值。
参考:
http://blog.datadive.net/
https://www.jianshu.com/p/744032f2f722