欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

机器学习 (四): 特征选择

程序员文章站 2024-03-08 09:01:58
...


本博客是 <Machine Learing With Python> 一书 chapter 10 的读书笔记

概述

特征选择即去掉对模型没有价值的特征, 根据手段不同可分为三类:

  1. filter: 字面意思是过滤, 原理是根据统计特征 (方差, 协方差, χ2\chi^2等) 按照一定阈值或者排名直接选择特征; 这种方法比较简单, 就和过滤一样没什么技术含量.
  2. wrapper: 字面意思是封装, 原理是用你的模型不断尝试特征的各个子集, 直到找到一个特征最少且性能最好的子集; 我认为 wrapper 的意思就是说不像 filter 一样用一个简单的统计属性, 而是用一个复杂的函数 (即你的模型的封装) 来衡量特征的好坏.
  3. embedded: 有些学习算法, 如 AdaBoost 在训练过程中会自动修改各个特征的权重, embedded 方法即为将特征选择过程嵌入到模型训练过程中, 根据训练的结果再从中选择权重最大的几个特征 (由于本章中没有提及该方法的具体实现, 所以本博客也不会对该方法进行详细说明)

Filter 方法

根据方差

方差用来衡量一个随机变量的离散程度, 方差越小表示该变量越稳定, 一般认为 (但是我不赞成) 方差小的特征含的信息量较小

直接根据一个阈值来滤掉特征:

from sklearn.datasets import load_iris
from sklearn.feature_selection import VarianceThreshold
import numpy as np

data = load_iris().data
# 原本的特征数量
print('orig dim: ', data.shape[1])
# 每个特征的方差
print('var: ', data.var(axis=0))
# 以0.5为阈值过滤后的特征数量
print('new dim: ', VarianceThreshold(0.5).fit_transform(data).shape[1])

输出

orig dim:  4
var:  [0.68112222 0.18871289 3.09550267 0.57713289]
new dim:  3

我们发现第二个特征方差最小.
但是, 因为方差的大小和数据的scale成平方关系, 所以在计算方差前应将特征缩放到同一取值范围

from sklearn.datasets import load_iris
from sklearn.preprocessing import maxabs_scale
import numpy as np

data = maxabs_scale(load_iris().data)
print('var: ', data.var(axis=0))

输出

var:  [0.01091367 0.00974757 0.06501791 0.09234126]

我们发现缩放后四个特征的方差都差不多. 此时就很难找到一个好的阈值来进行过滤了.

我个人认为, 根据方差来选择特征是一个非常幼稚的想法, 特征本身的发散程度和target并没有相关性, 反而有时候恰恰是方差小的特征才是决定target的关键.

根据协方差

有些特征之间可能会彼此相关, 在统计上表现为二者之间的协方差很高, 此时需要去掉冗余的特征.

import pandas as pd
import numpy as np
import seaborn as sns

%matplotlib inline

a = np.stack([np.arange(10), np.arange(10) // 2 * 2, np.random.rand(10)]).T
a = pd.DataFrame(a)
a = np.triu(a.corr().abs())

sns.heatmap(a)

机器学习 (四): 特征选择
我们发现特征0和特征1的协方差很高 (接近1), 因此可以去掉2或者1的其中之一.

根据χ2\chi^2

χ2\chi^2检验 (针对离散特征) 的逻辑就是对某一特征与样本标签进行独立性检验, 如果二者相互独立, 那么χ2=0\chi^2=0, 否则相关性与其值成正比.

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

SelectKBest(chi2, 2).fit_transform(load_iris().data, load_iris().target)

Wrapper 方法

RFECV

全称 Recursive Feature Elimination using Cross Validation
逻辑很简单, 即用你想要训练的模型, 先用全部特征训练, 然后逐步减少特征, 每次训练通过交叉验证的方式来打分, 从而可以得到一个特征最少, 得分最高的方案.

from sklearn.datasets import make_regression
from sklearn.feature_selection import RFECV
from sklearn import datasets, linear_model

# 创建一个用于线性回归的数据集, 其中有100个特征, 但只有两个是真正有用的
features, target = make_regression( n_samples = 1000,
                                    n_features = 100,
                                    n_informative = 2
                                  )
model = linear_model.LinearRegression()

refcv = RFECV(estimator=model, step=1, scoring="neg_mean_squared_error", cv = 5)
refcv.fit(features, target)
refcv.transform(features)

print(refcv.n_features_)

输出

2

总结

特征选择这个工作是特征工程中最为重要的步骤, 但是这个步骤依靠的更多的是经验和深入挖掘, sklearn 提供的 feature_selection 库中的函数往往只能作为粗略的预处理步骤 (比如从上千个特征中选取一百个较为重要的出来), 更进一步的处理还需要依靠自己.