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

紅色石頭發表於2020-05-07

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

專案地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/blob/master/%E4%B8%AD%E6%96%87%E7%89%88.md

本文將介紹特徵工程中的 Wrapper Methods 封裝方法。

目錄:

封裝方法將特徵選擇問題視作搜尋問題,即其目標為從特徵子集集合中搜尋出一個最佳的子集,而這一子集在模型中表現最佳。在每一步中,其在特徵子集上訓練模型,然後對其進行評估,並在下一步繼續調整特徵子集,重新訓練評估,直到找到最佳子集或達到最大迭代次數為止。窮盡搜尋在封裝方法中為NP-Hard,故人們提出了一些方法來降低封裝方法所需要的迭代次數,以便可以在有限的時間內達到一個較好的效果。

1.2.1 Deterministic Algorithms 確定性演算法

在不考慮模型隨機性的情況下,給定相同的資料輸入,確定性演算法將始終輸出相同的最優特徵子集。

順序向前選擇(SFS),順序向後選擇(SBS)均為確定性演算法。順序向前選擇(SFS)方法將從最優單變數模型開始,然後在迭代中,其會在上一步變數子集的基礎上,以窮舉的方法在現有變數子集中增加一個新變數,使得新增一個變數後的變數子集可以獲得最大的模型表現提升。迭代將持續直到所選變數的數量滿足要求為止。

順序向後選擇(SBS)則從適合一個包含所有變數的模型開始,然後在迭代中,其會在上一步變數子集的基礎上,以窮舉的方法在現有變數子集中刪除一個對模型負影響最低的變數,直到所選特徵的數量滿足要求為止。

但是順序向前選擇(SFS)方法和順序向後選擇(SBS)均為逐步(step-wise)的方法,都可能會陷入區域性最優狀態。

1.2.1.1 Recursive Feature Elimination (SBS) 遞迴式特徵消除

在sklearn中,它僅實現遞迴特徵消除(SBS)方法。其提供了兩個函式來實現這一方法,一個是RFE,另一個是RFECV。與RFE函式相比,REFCV使用交叉驗證的結果來選擇最優的特徵數量,而在RFE中,要選擇的特徵數量由使用者預定義。

# RFE函式 演示
import numpy as np
from sklearn.feature_selection import RFE

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示

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

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesRegressor # 使用ExtraTrees 模型作為示範
clf = ExtraTreesRegressor(n_estimators=25)
selector = RFE(estimator = clf, n_features_to_select = 4, step = 1) 
# 與RFECV不同,此處RFE函式需要使用者定義選擇的變數數量,此處設定為選擇4個最好的變數,每一步我們僅刪除一個變數

selector = selector.fit(train_set, train_y) # 在訓練集上訓練

transformed_train = train_set[:,selector.support_]  # 轉換訓練集
assert np.array_equal(transformed_train, train_set[:,[0,5,6,7]]) # 選擇了第一個,第六個,第七個及第八個變數

transformed_test = test_set[:,selector.support_] # 轉換訓練集
assert np.array_equal(transformed_test, test_set[:,[0,5,6,7]]) # 選擇了第一個,第六個,第七個及第八個變數
# RFECV 函式 演示
import numpy as np
from sklearn.feature_selection import RFECV

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示

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

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesRegressor # 使用ExtraTrees 模型作為示範
clf = ExtraTreesRegressor(n_estimators=25)
selector = RFECV(estimator = clf, step = 1, cv = 5) # 使用5折交叉驗證
# 每一步我們僅刪除一個變數
selector = selector.fit(train_set, train_y)

transformed_train = train_set[:,selector.support_]  # 轉換訓練集
assert np.array_equal(transformed_train, train_set) # 選擇了所有的變數

transformed_test = test_set[:,selector.support_] # 轉換訓練集
assert np.array_equal(transformed_test, test_set) # 選擇了所有的變數

1.2.2 Randomized Algorithms 隨機方法

與確定性演算法相比,隨機方法在搜尋最佳特徵子集時引入了一定程度的隨機性。因此,在相同資料輸入的情形下,它可能會輸出不同的最優特徵子集結果,但此方法中的隨機性將有助於避免模型陷入區域性最優結果。

1.2.2.1 Simulated Annealing (SA) 基於模擬退火特徵選擇

模擬退火是一種隨機最優化方法,近年來被引入到特徵選擇領域。在每一步中,我們將根據當前的最優特徵子集隨機選擇一個特徵子集。若新的特徵子集效果更好,那麼我們將採用它並更新當前最優特徵子集。若新特徵子集的表現不佳,我們仍會以一定的概率接受它,這個接受概率取決於當前的狀態(溫度)。

以一定的概率接受變現不佳的特徵子集對於模擬退火演算法至關重要,因為這有助於演算法避免陷入區域性最優狀態。隨著迭代的進行,模擬退火演算法可收斂為良好且穩定的最終結果。

由於未發現能較好實現SA演算法的函式,因此我編寫了一個python指令碼來實現SA演算法,以供您參考。其能夠很好地相容sklearn中的模型,支援分類及迴歸問題。它還提供了內建交叉驗證方法。

公式:

在每一步中,接受表現不佳的特徵子集的概率為:

Prob為接受表現不佳的特徵子集的概率, ????? 為新特徵子集的損失(loss), ????? 為新特徵子集建立前的最優(最低)損失(loss), ???_??????????? 為當前的溫度。模擬退火的虛擬碼為:

迴歸問題演示

import sys 
sys.path.append("..") 
from SA import Simulated_Annealing # 匯入我們撰寫的模組

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示

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

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesRegressor # 使用ExtraTrees 模型作為示範

# 選擇模擬退火中評價特徵子集的的損失函式
from sklearn.metrics import mean_squared_error # 迴歸問題我們使用MSE

clf = ExtraTreesRegressor(n_estimators=25)
selector = Simulated_Annealing(loss_func = mean_squared_error, estimator = clf, 
                               init_temp = 0.2, min_temp = 0.005, iteration = 10, alpha = 0.9)
# 在訓練集中訓練
# SA.py中有具體每個引數的含義,此處不贅述

selector.fit(X_train = train_set, y_train = train_y, cv = 5) # 使用5折交叉驗證

transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_test = selector.transform(test_set)  # 轉換測試集
selector.best_sol # 返回最優特徵的索引
selector.best_loss; # 返回最優特徵子集對應的損失

分類問題演示

import sys 
sys.path.append("..") 
import numpy as np
import random
from SA import Simulated_Annealing # 匯入我們撰寫的模組

from sklearn.datasets import load_iris  # 利用iris資料作為演示資料集

# 載入資料集
iris = load_iris()
X, y = iris.data, iris.target

# iris 資料集使用前需要被打亂順序
np.random.seed(1234)
idx = np.random.permutation(len(X))
X = X[idx]
y = y[idx]

# 選擇前100個觀測點作為訓練集
# 剩下的前20個觀測點作為驗證集,剩下的30個觀測作為測試集
train_set = X[0:100,:]
val_set = X[100:120,:]
test_set = X[120:,:]

train_y = y[0:100]
val_y = y[100:120]
test_y = y[120:]

# 重製隨機種子 
# 隨機方法需要隨機性的存在
random.seed()
np.random.seed()

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesClassifier # we use extratree as predictive model

# 選擇模擬退火中評價特徵子集的的損失函式
from sklearn.metrics import log_loss # 迴歸問題中,我們使用交叉熵損失函式

clf = ExtraTreesClassifier(n_estimators=25)
selector = Simulated_Annealing(loss_func = log_loss, estimator = clf, 
                               init_temp = 0.2, min_temp = 0.005, iteration = 10, 
                               alpha = 0.9, predict_type = 'predict_proba')
# 在訓練集中訓練
# SA.py中有具體每個引數的含義,此處不贅述

selector.fit(X_train = train_set, y_train = train_y, X_val = val_set, 
             y_val = val_y, stop_point = 15) 
# 此函式允許使用者匯入自己定義的驗證集,此處嘗試一下

transformed_train = selector.transform(train_set)  # 轉換訓練集
transformed_test = selector.transform(test_set)  # 轉換測試集
selector.best_sol # 返回最優特徵的索引
selector.best_loss; # 返回最優特徵子集對應的損失

1.2.2.2 Genetic Algorithm (GA) 基於基因演算法特徵選擇

遺傳演算法是一種基於進化生物學概念的最優化搜尋演算法。它借鑑了自然界中的進化過程,並通過允許個體候選解通過“交叉”和“變異”來進化得到更優的候選解及種群。其還結合了自然界中的競爭理念,即僅允許最合適或最優的幾個候選解“生存”下來並“繁殖”其後代。經過種群及個體候選解的持續迭代,基因演算法(GA)會收斂到優化解決方案。

與模擬退火類似,我也編寫了一個python指令碼來實現GA演算法,以供您參考。它提供了兩種演算法,包括“one-max”和“ NSGA2”。“one-max”為傳統的單目標GA演算法,“NSGA2”則為一個多目標GA演算法。在特徵選擇中,“one-max”的目標是減少模擬在驗證集上的損失,而“NSGA2”的目標一是減少損失,二是同時要最小化特徵子集中特徵的數量。

此python指令碼能夠很好地相容sklearn中的模型,支援分類及迴歸問題。它還提供了內建交叉驗證方法。

基因演算法的虛擬碼如下:

迴歸問題演示

import sys 
sys.path.append("..") 
from GA import Genetic_Algorithm # 匯入我們撰寫的模組

# 直接載入資料集
from sklearn.datasets import fetch_california_housing
dataset = fetch_california_housing()
X, y = dataset.data, dataset.target # 利用 california_housing 資料集來演示

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

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesRegressor # 使用ExtraTrees 模型作為示範

# 選擇模擬退火中評價特徵子集的的損失函式
from sklearn.metrics import mean_squared_error # 迴歸問題我們使用MSE

clf = ExtraTreesRegressor(n_estimators=25)
selector = Genetic_Algorithm(loss_func = mean_squared_error, estimator = clf, 
                             n_gen = 10, n_pop = 20, algorithm = 'NSGA2')
# 在訓練集中訓練
# GA.py中有具體每個引數的含義,此處不贅述

selector.fit(X_train = train_set, y_train = train_y, cv = 5) # 使用5折交叉驗證

transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_test = selector.transform(test_set)  # 轉換測試集
selector.best_sol # 返回最優特徵的索引
selector.best_loss; # 返回最優特徵子集對應的損失

分類問題演示

import sys 
sys.path.append("..") 
import numpy as np
import random
from GA import Genetic_Algorithm # 匯入我們撰寫的模組

from sklearn.datasets import load_iris  # 利用iris資料作為演示資料集

# 載入資料集
iris = load_iris()
X, y = iris.data, iris.target

# iris 資料集使用前需要被打亂順序
np.random.seed(1234)
idx = np.random.permutation(len(X))
X = X[idx]
y = y[idx]

# 選擇前100個觀測點作為訓練集
# 剩下的前20個觀測點作為驗證集,剩下的30個觀測作為測試集
train_set = X[0:100,:]
val_set = X[100:120,:]
test_set = X[120:,:]

train_y = y[0:100]
val_y = y[100:120]
test_y = y[120:]

# 重製隨機種子 
# 隨機方法需要隨機性的存在
random.seed()
np.random.seed()

# 選擇用於衡量子集表現的有監督的機器學習模型
from sklearn.ensemble import ExtraTreesClassifier # we use extratree as predictive model

# 選擇模擬退火中評價特徵子集的的損失函式
from sklearn.metrics import log_loss # 迴歸問題中,我們使用交叉熵損失函式

clf = ExtraTreesClassifier(n_estimators=25)
selector = Genetic_Algorithm(loss_func = log_loss, estimator = clf, 
                             n_gen = 15, n_pop = 10, predict_type = 'predict_proba')
# 在訓練集中訓練
# GA.py中有具體每個引數的含義,此處不贅述

selector.fit(X_train = train_set, y_train = train_y, X_val = val_set, 
             y_val = val_y, stop_point = 15) 
# 此函式允許使用者匯入自己定義的驗證集,此處嘗試一下

transformed_train = selector.transform(train_set)  # 轉換訓練集
transformed_test = selector.transform(test_set)  # 轉換測試集
selector.best_sol # 返回最優特徵的索引
selector.best_loss; # 返回最優特徵子集對應的損失

基於 Jupyter 的特徵工程手冊:特徵選擇:

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

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

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

中文版 Jupyter 地址:

https://github.com/YC-Coder-Chen/feature-engineering-handbook/tree/master/%E4%B8%AD%E6%96%87%E7%89%88


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

相關文章