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

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

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

專案地址:

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

本文將介紹特徵工程中的 Embedded Methods 嵌入方法。

目錄:

過濾法的特徵選擇過程與後續的機器學習模型無關,因此過濾法可能導致較差的模型效能。

封裝方法利用預定義的有監督的機器學習模型來選擇最佳功能。但是,由於它們需要在大量可能的特徵子集上多次訓練模型,因此儘管它們通常會導致更好的效能,但它們同時也需要較長的處理時間。

嵌入式方法將特徵選擇過程嵌入到機器學習模型中,即利用機器學習來為每一個特徵打分。嵌入式方法在建立模型時即完成了對特徵子集的選擇。因此,與過濾法相比,它們往往具有更好的效能。與封裝方法相比,它們節省了大量的處理時間和計算能力。

三種方法的一個簡單對比.

1.3.1 基於正則化模型的方法

許多機器學習模型在其損失函式中引入了正則項(L1正則或L2正則),以防止過擬合問題。線性模型(例如線性向量支援機,邏輯迴歸,線性迴歸)中的L1正則項能夠有效地將某些特徵的特徵係數縮小為零,從而實現解的稀疏。因此,基於帶正則項線性模型的特徵係數,我們可以為特徵打分。係數越高,往往該特徵線上性模型中越重要。

我們可以使用sklearn SelectFromModel函式刪除特徵係數低或為零的特徵。

1.3.1.1 Lasso Regression (Linear Regression with L1 Norm) 套索迴歸

import numpy as np
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import Lasso # 我們也可以使用帶L2正則項的嶺迴歸

# 直接載入資料集
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]

clf = Lasso(normalize=True, alpha = 0.001)  
# 在進行線性迴歸前,我們需要先對變數進行縮放操作,否則迴歸係數大小無法比較
# alpha控制正則效果的大小,alpha越大,正則效果越強

clf.fit(train_set, train_y) # 在訓練集上訓練
np.round(clf.coef_ ,3)

array([ 0.346, 0.003, -0. , -0. , -0. , -0. , -0.033, 0. ])

selector = SelectFromModel(clf, prefit=True, threshold=1e-5)
# 閾值被設定為1e-5,因此絕對係數低於1e-5的特徵將被刪除
# 我們還可以設定max_features引數以選擇最重要的前幾個特徵

transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_test = selector.transform(test_set) #轉換測試集

assert np.array_equal(transformed_train, train_set[:,[0,1,6]]) 
# 選擇第一個,第二個及第七個變數
assert np.array_equal(transformed_test, test_set[:,[0,1,6]])

1.3.1.2 Logistic Regression (with L1 Norm) 邏輯迴歸

import numpy as np
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
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個觀測點作為訓練集
# 剩下的50個觀測點作為測試集
train_set = X[0:100,:]
test_set = X[100:,]
train_y = y[0:100]

# 在進行邏輯迴歸前,我們需要先對變數進行縮放操作,否則迴歸係數大小無法比較
from sklearn.preprocessing import StandardScaler
model = StandardScaler()
model.fit(train_set) 
standardized_train = model.transform(train_set)
standardized_test = model.transform(test_set)

clf = LogisticRegression(penalty='l1', C = 0.7, 
                         random_state=1234, solver='liblinear') 
# 我們也可以將正則項設定為 'l2'
# C控制正則效果的大小,C越大,正則效果越弱

clf.fit(standardized_train, train_y)
np.round(clf.coef_,3)

array([[ 0. , 1. , -3.452, -0.159],
[ 0. , -1.201, 0.053, 0. ],
[ 0. , 0. , 1.331, 3.27 ]])

selector = SelectFromModel(clf, prefit=True, threshold=1e-5)
# 閾值被設定為1e-5,因此絕對係數低於1e-5的特徵將被刪除
# 我們還可以設定max_features引數以選擇最重要的前幾個特徵

transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_test = selector.transform(test_set) #轉換測試集

assert np.array_equal(transformed_train, train_set[:,[1,2,3]]) 
# 選擇第2個, 第3個及第4個變數
assert np.array_equal(transformed_test, test_set[:,[1,2,3]])

1.3.1.3 LinearSVR/ LinearSVC 線性向量支援機

# LinearSVC 用於分類問題
# LinearSVR 用於迴歸問題
# 這裡以LinearSVR為例

import numpy as np
from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVR

# 直接載入資料集
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.preprocessing import StandardScaler
model = StandardScaler()
model.fit(train_set) 
standardized_train = model.transform(train_set)
standardized_test = model.transform(test_set)

clf = LinearSVR(C = 0.0001, random_state = 123) 
# C控制正則效果的大小,C越大,正則效果越弱

clf.fit(standardized_train, train_y)
np.round(clf.coef_,3)

array([ 0.254, 0.026, 0.026, -0.017, 0.032, -0.01 , -0.1 , -0.037])

selector = SelectFromModel(clf, prefit=True, threshold=1e-2)
# 閾值被設定為1e-2,因此絕對係數低於1e-2的特徵將被刪除
# 我們還可以設定max_features引數以選擇最重要的前幾個特徵

transformed_train = selector.transform(train_set) # 轉換訓練集
transformed_test = selector.transform(test_set) #轉換測試集

assert np.array_equal(transformed_train, train_set[:,[0,1,2,3,4,6,7]]) 
# 僅第6個變數被刪去
assert np.array_equal(transformed_test, test_set[:,[0,1,2,3,4,6,7]])

1.3.2 Tree Based Methods 基於樹模型的方法

機器學習的一大分支便是基於樹的機器學習模型,例如隨機森林,AdaBoost,Xgboost等。您可以在我的朋友和我撰寫的一系列部落格中找到有關這些基於樹的機器學習模型的更多介紹此處:

https://github.com/YC-Coder-Chen/Tree-Math

這些非參的樹狀模型在建立的過程中記錄了每一個變數如何在樹節點的分叉中逐步降低模型損失,並可以根據上述記錄分析每個特徵的特徵重要性。而我們可以基於這特徵重要性刪去一些不重要的變數。

# 我們以隨機森林為例
import numpy as np
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestRegressor

# 直接載入資料集
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]

# 在樹類機器學習模型中,我們無需縮放變數操作

clf = RandomForestRegressor(n_estimators = 50, random_state = 123)

clf.fit(train_set, train_y)
np.round(clf.feature_importances_, 3)

array([0.52 , 0.045, 0.031, 0.026, 0.027, 0.139, 0.106, 0.107])

# 視覺化特徵重要性
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
%matplotlib inline
importances = clf.feature_importances_
indices = np.argsort(importances)
plt.figure(figsize=(12,12))
plt.title('特徵重要性')
plt.barh(range(len(indices)), importances[indices], color='seagreen', align='center')
plt.yticks(range(len(indices)),np.array(dataset.feature_names)[indices])
plt.xlabel('特徵相對重要性');

selector = SelectFromModel(clf, prefit=True, threshold='median')
# 閾值被設定為'median', 即以特徵重要性的中位數作為閾值,大約為0.076
# 我們還可以設定max_features引數以選擇最重要的前幾個特徵

transformed_train = selector.transform(train_set)
transformed_test = selector.transform(test_set)
assert np.array_equal(transformed_train, train_set[:,[0,5,6,7]]) 
# 選擇來第1個,第6個, 第7個及第8個特徵
assert np.array_equal(transformed_test, test_set[:,[0,5,6,7]])

基於 Jupyter 的特徵工程手冊 – 特徵選擇系列歷史文章:

專欄 | 基於 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),歡迎關注!

相關文章