專欄 | 基於 Jupyter 的特徵工程手冊:特徵選擇(一)

紅色石頭發表於2020-04-22

資料預處理後,我們生成了大量的新變數(比如獨熱編碼生成了大量僅包含0或1的變數)。但實際上,部分新生成的變數可能是多餘:一方面它們本身不一定包含有用的資訊,故無法提高模型效能;另一方面過這些多餘變數在構建模型時會消耗大量記憶體和計算能力。因此,我們應該進行特徵選擇並選擇特徵子集進行建模。

專案地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook

本文將介紹特徵工程第一種演算法:Filter Methods 過濾法(上)。

目錄:

1.1 Filter Methods 過濾法

過濾法通過使用一些統計量或假設檢驗結果為每個變數打分。得分較高的功能往往更加重要,因此應被包含在子集中。以下為一個簡單的基於過濾法的機器學習工作流(以最簡單的訓練-驗證-測試這種資料集劃分方法為例)。

1.1.1 Univariate Filter Methods 單變數特徵過濾

單變數過濾方法依據單變數統計量或統計檢驗選擇最佳特徵。其僅僅考慮單個變數與目標變數的關係(方差選擇法僅基於單個變數)。

1.1.1.1 Variance Threshold 方差選擇法

方差選擇法刪除變數方差低於某個閾值的所有特徵。例如,我們應刪除方差為零的特徵(所有觀測點中具有相同值的特徵),因為該特徵無法解釋目標變數的任何變化。

import numpy as np
import pandas as pd
from sklearn.feature_selection import VarianceThreshold

# 合成一些資料集用於演示
train_set = np.array([[1,2,3],[1,4,7],[1,4,9]]) # 可見第一個變數方差為0
# array([[1, 2, 3],
#        [1, 4, 7],
#        [1, 4, 9]])

test_set = np.array([[3,2,3],[1,2,7]]) # 故意將第二個變數方差設為0
# array([[3, 2, 3],
#        [1, 2, 7]])

selector = VarianceThreshold()
selector.fit(train_set) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
# 下面為返回結果,可見第一個變數已被刪除
# array([[2, 3],
#        [4, 7],
#        [4, 9]])

transformed_test = selector.transform(test_set) # 轉換測試集
# 下面為返回結果,可見第一個變數已被刪除
# array([[2, 3],
#        [2, 7]])
# 雖然測試集中第二個變數的方差也為0
# 但是我們的選擇是基於訓練集,所以我們依然刪除第一個變數

1.1.1.2 Pearson Correlation (regression problem) 皮爾森相關係數 (迴歸問題)

皮爾森相關係數一般用於衡量兩個連續變數之間的線性相關性,也可以用於衡量二元變數與目標變數的相關性。故可以將類別變數利用獨熱編碼轉換為多個二元變數之後利用皮爾森相關係數進行篩選。

公式:

import numpy as np
from scipy.stats import pearsonr
from sklearn.feature_selection import SelectKBest

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示
# 此資料集中,X,y均為連續變數,故此滿足使用皮爾森相關係數的條件

# 選擇前15000個觀測點作為訓練集
# 剩下的作為測試集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]

# sklearn 中沒有直接的方程可以使用
# 此處將用 scipy.stats.pearsonr方程來實現基於皮爾森相關係數的特徵過濾
# 注意 scipy.stats.pearsonr 計算的是兩個變數之間的相關係數
# 因sklearn SelectKBest需要,我們將基於scipy.stats.pearsonr 重寫允許多特徵同時輸入的方程 udf_pearsonr

def udf_pearsonr(X, y):
    # 將會分別計算每一個變數與目標變數的關係
    result = np.array([pearsonr(x, y) for x in X.T]) # 包含(皮爾森相關係數, p值) 的列表
    return np.absolute(result[:,0]), result[:,1]

# SelectKBest 將會基於一個判別方程自動選擇得分高的變數
# 這裡的判別方程為皮爾森相關係數
selector = SelectKBest(udf_pearsonr, k=2) # k => 我們想要選擇的變數數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(15000, 2), 其選擇了第一個及第七個變數 
assert np.array_equal(transformed_train, train_set[:,[0,6]])

transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[0,6]]);
# 可見對於測試集,其依然選擇了第一個及第七個變數


# 驗算一下我們的結果
for idx in range(train_set.shape[1]):
    pea_score, p_value = pearsonr(train_set[:,idx], train_y)
    print(f"第{idx + 1}個變數和目標的皮爾森相關係數的絕對值為{round(np.abs(pea_score),2)}, p-值為{round(p_value,3)}")
# 應選擇第一個及第七個變數

第1個變數和目標的皮爾森相關係數的絕對值為0.7, p-值為0.0

第2個變數和目標的皮爾森相關係數的絕對值為0.07, p-值為0.0

第3個變數和目標的皮爾森相關係數的絕對值為0.14, p-值為0.0

第4個變數和目標的皮爾森相關係數的絕對值為0.04, p-值為0.0

第5個變數和目標的皮爾森相關係數的絕對值為0.02, p-值為0.011

第6個變數和目標的皮爾森相關係數的絕對值為0.05, p-值為0.0

第7個變數和目標的皮爾森相關係數的絕對值為0.23, p-值為0.0

第8個變數和目標的皮爾森相關係數的絕對值為0.08, p-值為0.0

1.1.1.3 Distance Correlation (regression problem) 距離相關係數 (迴歸問題)

與皮爾森相關係數類似,距離相關係數也一般被用於衡量兩個連續變數之間的相關性。但與皮爾森相關係數不同的是,距離相關係數還衡量了兩個變數之間的非線性關聯。

公式:

首先,計算(n x n)距離矩陣dX。dX中的每一個元素為 。類似的,我們也可以計算距離矩陣dY,其中dY中的每個元素為 。 是為觀測點i與觀測點j之間的距離:

其次,我們計算如下雙中心距離並更新距離矩陣。其中, ¯ 為距離矩陣dX的第i行平均值, ¯ 為距離矩陣dX的第j列的平均值, ∑ ∑ 為全域性平均值:

隨後,我們便可以算出樣本距離協方差及距離方差:

最後,距離相關係數 ( , ) 便為如下:

import numpy as np
from dcor import distance_correlation
from dcor.independence import distance_covariance_test
from sklearn.feature_selection import SelectKBest

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示
# 此資料集中,X,y均為連續變數,故此滿足使用距離相關係數的條件

# 選擇前15000個觀測點作為訓練集
# 剩下的作為測試集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]

# sklearn 中沒有直接的方程可以使用
# 此處將用 dcor.distance_correlation方程來實現基於距離相關係數的特徵過濾
# 注意 dcor.distance_correlation 計算的是兩個變數之間的相關係數
# 因sklearn SelectKBest需要,我們將基於dcor.distance_correlation 重寫允許多特徵同時輸入的方程 udf_dcorr

def udf_dcorr(X, y):
    # 將會分別計算每一個變數與目標變數的關係
    result = np.array([[distance_correlation(x, y), 
                        distance_covariance_test(x,y)[0]]for x in X.T]) # 包含(距離相關係數, p值) 的列表
    return result[:,0], result[:,1]

# SelectKBest 將會基於一個判別方程自動選擇得分高的變數
# 這裡的判別方程為距離相關係數
selector = SelectKBest(udf_dcorr, k=2) # k => 我們想要選擇的變數數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(15000, 2), 其選擇了第一個及第三個變數 
assert np.array_equal(transformed_train, train_set[:,[0,2]])

transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[0,2]]);
# 可見對於測試集,其依然選擇了第一個及第三個變數
# 驗算一下我們的結果
for idx in range(train_set.shape[1]):
    d_score = distance_correlation(train_set[:,idx], train_y)
    p_value = distance_covariance_test(train_set[:,idx], train_y)[0]
    print(f"第{idx + 1}個變數和目標的距離相關係數為{round(d_score,2)}, p-值為{round(p_value,3)}")
# 應選擇第一個及第三個變數

第1個變數和目標的距離相關係數為0.66, p-值為1.0

第2個變數和目標的距離相關係數為0.07, p-值為1.0

第3個變數和目標的距離相關係數為0.31, p-值為1.0

第4個變數和目標的距離相關係數為0.12, p-值為1.0

第5個變數和目標的距離相關係數為0.08, p-值為1.0

第6個變數和目標的距離相關係數為0.29, p-值為1.0

第7個變數和目標的距離相關係數為0.25, p-值為1.0

第8個變數和目標的距離相關係數為0.19, p-值為1.0

1.1.1.4 F-Score (regression problem) F-統計量 (迴歸問題)

F統計量(F-Score)用於檢驗線性迴歸模型的整體顯著性。在sklearn中,其將對每一個變數分別建立一個一元的線性迴歸模型,然後分別報告每一個對應模型的F統計量。F-統計量的零假設是該線性模型係數不顯著,在一元模型中,該統計量能夠反映各變數與目標變數之間的線性關係。因此,我們應該選擇具有較高F統計量的特徵(更有可能拒絕原假設)。

公式:

SST為總平方和,SSR為迴歸平方和,p為線性迴歸自變數數(包括常數項,故在上述的一元線性模型中,p=2), 為自變數與因變數的線性相關係數,n為總觀測數。因上述線性模型為一元線性模型,故可證 2= 2 。

import numpy as np
from sklearn.feature_selection import f_regression
from sklearn.feature_selection import SelectKBest

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示
# 此資料集中,X,y均為連續變數,故此滿足使用F統計量的條件

# 選擇前15000個觀測點作為訓練集
# 剩下的作為測試集
train_set = X[0:15000,:]
test_set = X[15000:,]
train_y = y[0:15000]

# sklearn 中直接提供了方程用於計算F統計量
# SelectKBest 將會基於一個判別方程自動選擇得分高的變數
# 這裡的判別方程為F統計量
selector = SelectKBest(f_regression, k=2) # k => 我們想要選擇的變數數
selector.fit(train_set, train_y) # 在訓練集上訓練
transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_train.shape #(15000, 2), 其選擇了第一個及第七個變數 
assert np.array_equal(transformed_train, train_set[:,[0,6]])

transformed_test = selector.transform(test_set) # 轉換測試集
assert np.array_equal(transformed_test, test_set[:,[0,6]]);
# 可見對於測試集,其依然選擇了第一個及第七個變數
# 驗算一下我們的結果
for idx in range(train_set.shape[1]):
    score, p_value = f_regression(train_set[:,idx].reshape(-1,1), train_y)
    print(f"第{idx + 1}個變數的F統計量為{round(score[0],2)}, p-值為{round(p_value[0],3)}")
# 故應選擇第一個及第七個變數

第1個變數的F統計量為14111.79, p-值為0.0
第2個變數的F統計量為71.99, p-值為0.0
第3個變數的F統計量為317.04, p-值為0.0
第4個變數的F統計量為23.93, p-值為0.0
第5個變數的F統計量為6.54, p-值為0.011
第6個變數的F統計量為35.93, p-值為0.0
第7個變數的F統計量為846.61, p-值為0.0
第8個變數的F統計量為98.06, p-值為0.0

專欄系列:

專欄 | 基於 Jupyter 的特徵工程手冊:資料預處理(一)

專欄 | 基於 Jupyter 的特徵工程手冊:資料預處理(二)

專欄 | 基於 Jupyter 的特徵工程手冊:資料預處理(三)

專欄 | 基於 Jupyter 的特徵工程手冊:資料預處理(四)

目前該專案完整中文版已全部製作完成,請持續關注哦~

中文版 Jupyter 地址:


本文首發於公眾號:AI有道(ID: redstonewill),歡迎關注!

相關文章