本博客是 <Machine Learing With Python> 一書 chapter 10 的讀書筆記
概述
特徵選擇即去掉對模型沒有價值的特徵, 根據手段不同可分爲三類:
- filter: 字面意思是過濾, 原理是根據統計特徵 (方差, 協方差, 等) 按照一定閾值或者排名直接選擇特徵; 這種方法比較簡單, 就和過濾一樣沒什麼技術含量.
- wrapper: 字面意思是封裝, 原理是用你的模型不斷嘗試特徵的各個子集, 直到找到一個特徵最少且性能最好的子集; 我認爲 wrapper 的意思就是說不像 filter 一樣用一個簡單的統計屬性, 而是用一個複雜的函數 (即你的模型的封裝) 來衡量特徵的好壞.
- 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的其中之一.
根據
檢驗 (針對離散特徵) 的邏輯就是對某一特徵與樣本標籤進行獨立性檢驗, 如果二者相互獨立, 那麼, 否則相關性與其值成正比.
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 庫中的函數往往只能作爲粗略的預處理步驟 (比如從上千個特徵中選取一百個較爲重要的出來), 更進一步的處理還需要依靠自己.