天天看點

特征選擇:8 種常見的特征過濾法

特征選擇:8 種常見的特征過濾法

特征抽取是資料挖掘任務最為重要的一個環節,一般而言,它對最終結果的影響要高過資料挖掘算法本身。

不幸的是,關于怎樣選取好的特征,還沒有嚴格、快捷的規則可循,其實這也是資料挖掘科學更像是一門藝術的所在。

建立好的規則離不開直覺,還需要專業領域知識和資料挖 掘經驗,光有這些還不夠,還得不停地嘗試、摸索,在試錯中前進,有時多少還要靠點運氣。

通常特征數量很多,但我們隻想選用其中一小部分。有如下幾個原因。

1、降低複雜度

随着特征數量的增加,很多資料挖掘算法需要更多的時間和資源。減少特征數量,是提高算法運作速度,減少資源使用的好方法。

2、降低噪音

增加額外特征并不總會提升算法的表現。額外特征可能擾亂算法的正常工作,這些額外特征間的相關性和模式沒有實際應用價值(這種情況在小資料集上很常見)。隻選擇合适的特征有助于減少出現沒有實際意義的相關性的幾率。

3、增加模型可讀性

根據成千上萬個特征建立的模型來解答一個問題,對計算機來說很容易,但模型對我們自己來說就晦澀無比。是以,使用更少的特征,建立我們自己可以了解的模型,就很有必要。

有些分類算法确實很強壯,能夠處理噪音問題,特征再多也不在話下,但是選用幹淨的資料,選取更具描述性的特征,對算法效果提升很有幫助。

根據特征選擇的形式又可以将特征選擇方法分為三種

Filter

:過濾法,按照發散性或者相關性對各個特征進行評分,設定門檻值或者待選擇門檻值的個數,選擇特征。

Wrapper

:包裝法,根據目标函數(通常是預測效果評分),每次選擇若幹特征,或者排除若幹特征。

Embedded

:嵌入法,先使用某些機器學習的算法和模型進行訓練,得到各個特征的權值系數,根據系數從大到小選擇特征。類似于Filter方法,但是是通過訓練來确定特征的優劣。

寫在前面

為了幫助更好地了解特征選擇,這裡使用我們常用的股市資料作為基礎資料。

由于文章較長,為友善閱讀,我将特征選擇與特征提取總結文章拆分為上下兩篇,上篇(本文)主要内容包括如下圖所示,主要介紹過濾法中常用的幾種特征選擇方法。

特征選擇:8 種常見的特征過濾法

導入相關子產品

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
import yfinance as yf
yf.pdr_override()
           

複制

擷取資料

仍然用騰訊股市資料,輔助了解本文内容。

symbol = 'TCEHY'
start = '2016-01-01'
end = '2021-01-01'
df = yf.download(symbol, start, end)
# df = df.reset_index()
# View columns 
df.head()
           

複制

特征選擇:8 種常見的特征過濾法

特征構造

dataset = df.copy()
['Increase_Decrease'] = np.where(dataset['Volume'].shift(-1) > dataset['Volume'],1,0)
dataset['Buy_Sell_on_Open'] = np.where(dataset['Open'].shift(-1) > dataset['Open'],1,0)
dataset['Buy_Sell'] = np.where(dataset['Adj Close'].shift(-1) > dataset['Adj Close'],1,0)
dataset['Returns'] = dataset['Adj Close'].pct_change()
dataset = dataset.dropna()
dataset.head()
           

複制

特征選擇:8 種常見的特征過濾法

資料準備

設定目标标簽為收盤價,研究哪些變量對收盤價影響加大。通過一定對方法剔除幾乎沒有影響的特征,選出影響較多對特征。特征選擇在次元較大時尤為重要。

features = dataset.drop(['Adj Close', 'Close', 'Returns'], axis=1)
array = features.values
X = array.astype(int)

Y = dataset['Adj Close'].values.astype(int)
           

複制

過濾法

過濾方法通常用作預處理步驟,特征選擇完全獨立于任何機器學習算法。它是根據各種統計檢驗中的分數以及相關性的各項名額來選擇特征。

方差過濾

這是通過特征本身的方差來篩選特征的類。比如一個特征本身的方差很小,就表示樣本在這個特征上基本沒有差異,可能特征中的大多數值都一樣,甚至整個特征的取值都相同,那這個特征對于樣本區分沒有什麼作用。

scikit-learn中的VarianceThreshold轉換器可用來删除特征值的方差達不到最低标準的特征。

from sklearn.feature_selection import VarianceThreshold
vt = VarianceThreshold()
vt.fit_transform(X)
var_thd = pd.DataFrame(vt.variances_, columns = ["Variance"], index=features.columns)
var_thd = var_thd.reset_index()
var_thd.sort_values('Variance',ascending=0)
           

複制

特征選擇:8 種常見的特征過濾法

無論什麼時候,拿到資料後,先做下類似簡單、直接的分析,對資料集的特點做到心中有數。方差為0的特征不但對資料挖掘沒有絲毫用處,相反還會拖慢算法的運作速度。

單變量選擇

單變量的特征選擇是通過基于一些單變量的統計度量方法來選擇最好的特征。屬于過濾法的一種。

scikit-learn提供了幾個用于選擇單變量特征的轉換器,其中

SelectKBest

傳回

k

個最佳特征,

SelectPercentile

傳回表現最佳的前

r%

個特征。這兩個轉換器都提供計算特征表現的一系列方法。都将得分函數作為輸入,傳回單變量的得分和p值。可作為輸入的評分函數有:

  • 對于回歸: f_regression , mutual_info_regression, 互資訊
  • 對于分類: chi2 , f_classif , mutual_info_classif, 皮爾森相關系數

SelectKBest 選擇出前k個與标簽最相關的特征,主要有兩個參數:

1、

score_func

: callable,函數取兩個數組X和y,傳回一對數組(scores, pvalues)或一個分數的數組。預設函數為f_classif,預設函數隻适用于分類函數。

2、

k

:int or "all", optional, default=10。所選擇的topK個特征。“all”選項則繞過選擇,用于參數搜尋。

卡方

單個特征和某一類别之間相關性的計算方法有很多。最常用的有卡方檢驗。經典的卡方檢驗是檢驗定性自變量對定性因變量的相關性。

卡方過濾是專門針對離散型标簽(即分類問題)的相關性過濾。卡方檢驗類

feature_selection.chi2

計算每個非負特征和标簽之間的卡方統計量,并依照卡方統計量由高到低為特征排名。

再結合

feature_selection.SelectKBest

這個可以輸入”評分标準“來選出前K個分數最高的特征的類,我們可以借此除去最可能獨立于标簽,與我們分類目的無關的特征。

from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

# 再使用SelectKBest轉換器類,用卡方函數打分,初始化轉換器。
test = SelectKBest(score_func=chi2, k=3)
test.fit(X, Y)

# 得分情況
np.set_printoptions(precision=3)
print(test.scores_)
           

複制

[6.365e+03 6.416e+03 6.286e+03 8.295e+08 
 2.949e+01 2.240e+01 3.059e+01]
           

複制

卡方檢驗的本質是推測兩組資料之間的差異,其檢驗的原假設是”兩組資料是互相獨立的”。卡方檢驗傳回卡方值和P值兩個統計量,其中卡方值很難界定有效的範圍,而p值,我們一般使用0.01或0.05作為顯著性水準,即p值判斷的邊界。

從特征工程的角度,我們希望選取卡方值很大,p值小于0.05的特征,即和标簽是相關聯的特征。而調用SelectKBest之前,我們可以直接從chi2執行個體化後的模型中獲得各個特征所對應的卡方值和P值。

F檢驗

另外類似的方法還有F檢驗,又稱ANOVA,方差齊性檢驗,是用來捕捉每個特征與标簽之間的線性關系的過濾方法。它即可以做回歸也可以做分類,是以包含

feature_selection.f_classif

(F檢驗分類)和

feature_selection.f_regression

(F檢驗回歸)兩個類。其中F檢驗分類用于标簽是離散型變量的資料,而F檢驗回歸用于标簽是連續型變量的資料。

F檢驗的本質是尋找兩組資料之間的線性關系,其原假設是”資料不存在顯著的線性關系“。它傳回F值和p值兩個統 計量。

和卡方過濾一樣,我們希望選取p值小于0.05或0.01的特征,這些特征與标簽時顯著線性相關的,而p值大于 0.05或0.01的特征則被我們認為是和标簽沒有顯著線性關系的特征,應該被删除。

互資訊

互資訊法是用來捕捉每個特征與标簽之間的任意關系(包括線性和非線性關系)的過濾方法。和F檢驗相似,它既可以做回歸也可以做分類,并且包含兩個類

feature_selection.mutual_info_classif

(互資訊分類)和

feature_selection.mutual_info_regression

(互資訊回歸)。這兩個類的用法和參數都和F檢驗一模一樣,不過互資訊法比F檢驗更加強大,F檢驗隻能夠找出線性關系,而互資訊法可以找出任意關系。

互資訊法不傳回p值或F值類似的統計量,它傳回“每個特征與目标之間的互資訊量的估計”,這個估計量在[0,1]之間取值,為0則表示兩個變量獨立,為1則表示兩個變量完全相關。

關于F檢驗和互資訊,可以參見官方例子:Comparison of F-test and mutual information[1]

提取簡化後的特征

# 調用transform()或直接使用fit_transform方法,
# 對相同的資料集進行預處理和轉換。
new_features = test.transform(X)
# 列印出特征提取前後特征數
print('原始特征數:', X.shape[1])
print('簡化的特征數:', new_features.shape[1])
           

複制

原始特征數: 7
簡化的特征數: 3
           

複制

chi_sq = pd.DataFrame(fit.scores_, columns = ["Chi_Square"], index=features.columns)
chi_sq = chi_sq.reset_index()
chi_sq.sort_values('Chi_Square',ascending=0)
           

複制

特征選擇:8 種常見的特征過濾法

皮爾遜相關系數

Pearsonr函數的接口幾乎與scikit-learn單變量轉換器接口一緻,該函數接收兩個數組 (目前例子中為x和y)作為參數,傳回兩個數組:每個特征的皮爾遜相關系數和p值,直接把它傳入到SelectKBest函數中。

SciPy的pearsonr函數參數為兩個數組,但要注意的是第一個參數x為一維數組。我們來實作一個包裝器函數,這樣就能像前面那樣處理多元數組。

from scipy.stats import pearsonr

def multivariate_pearsonr(X, Y): 
    # 建立scores和pvalues數組,周遊資料集的每一列。
    scores, pvalues = [], []
    for column in range(X.shape[1]):
    # 隻計算該列的皮爾遜相關系數和p值,并将其存儲到相應數組中。
        cur_score, cur_p = pearsonr(X[:,column], Y)
        scores.append(abs(cur_score))
        pvalues.append(cur_p)
#函數最後傳回包含皮爾遜相關系數和p值的元組。 
    return (np.array(scores), np.array(pvalues))
           

複制

該方法衡量的是變量之間的線性相關性,結果的取值區間為

[-1,1]

-1表示完全的負相關;

+1表示完全的正相關;

0表示沒有線性相關。

現在,就可以像之前那樣使用轉換器類,根據皮爾遜相關系數對特征進行排序。

m_pearsonr = SelectKBest(score_func=multivariate_pearsonr, k=3) 
X_pearson = m_pearsonr.fit_transform(X, Y) 
print(m_pearsonr.scores_)

           

複制

[9.989e-01 9.992e-01 9.994e-01 3.903e-01 
 9.078e-03 9.203e-04 2.001e-02]
           

複制

pearsonr = pd.DataFrame(m_pearsonr.scores_, columns = ["pearsonr"], index=features.columns)
pearsonr = pearsonr.reset_index()
pearsonr.sort_values('pearsonr',ascending=0)
           

複制

特征選擇:8 種常見的特征過濾法

多重共線性方差膨脹系數

方差膨脹系數(variance inflation factor,VIF)是衡量多元線性回歸模型中複 (多重)共線性嚴重程度的一種度量。它表示回歸系數估計量的方差與假設自變量間不線性相關時方差相比的比值。

多重共線性是指自變量之間存線上性相關關系,即一個自變量可以是其他一個或幾個自變量的線性組合。

通常以10作為判斷邊界。

當VIF<10,不存在多重共線性;

當10<=VIF<100,存在較強的多重共線性;

當VIF>=100, 存在嚴重多重共線性。

from statsmodels.stats.outliers_influence import variance_inflation_factor

def calculate_vif(features):
    vif = pd.DataFrame()
    vif["index"] = features.columns
    vif["VIF"] = [variance_inflation_factor(features.values, i) for i in range(features.shape[1])]    
    return(vif)
  
vif = calculate_vif(features)
while vif['VIF'][vif['VIF'] > 10].any():
    remove = vif.sort_values('VIF',ascending=0)['index'][:1]
    features.drop(remove,axis=1,inplace=True)
    vif = calculate_vif(features)
    
vif
           

複制

特征選擇:8 種常見的特征過濾法

過濾法總結

最後用一張表格将過濾法做個總結,友善大家查閱學習。

說明 超參數的選擇
VarianceThreshold 方差過濾,可輸入方差門檻值,傳回方差大于門檻值的新特征矩陣 看具體資料究竟是含有更多噪聲還是更多有效特征一般就使用0或1來篩選也可以畫學習曲線或取中位數跑模型來幫助确認
SelectKBest 用來選取K個統計量結果最佳的特征,生成看配合使用的統計量符合統計量要求的新特征矩陣 看配合使用的統計量
chi2 卡方檢驗,專用于分類算法,捕捉相關性 追求p小于顯著性水準的特征
f_classif F檢驗分類,隻能捕捉線性相關性 要求資料服從正态分布 追求p小于顯著性水準的特征
f_regression F檢驗回歸,隻能捕捉線性相關性 要求資料服從正态分布 追求p小于顯著性水準的特征
mutual_info_classif 互資訊分類,可以捕捉任何相關性 追求互資訊估計大于0的特征不能用于稀疏矩陣 追求互資訊估計大于0的特征
mutual_info_regression 互資訊回歸,可以捕捉任何相關性 不能用于稀疏矩陣 追求互資訊估計大于0的特征
pearsonr 皮爾遜相關系數,隻能捕捉線性相關關系 追求p小于顯著性水準的特征

左右滑動檢視更多

參考資料

[1]

Comparison of F-test and mutual information: https://scikit-learn.org/stable/auto_examples/feature_selection/plot_f_test_vs_mi.html