一、特徵選擇的重要性

機器學習

過程中,特徵選擇是非常重要的一步。可以說,選到了合適的特徵,那麼模型的訓練就成功了一半。

一方面,遺漏掉重要特徵肯定是我們無法接受的,這會導致我們的模型擬合能力不足;另一方面冗餘特徵也可能會造成嚴重的後果,比如:

  • 帶來額外的計算量,導致訓練時間過長;
  • 模型過擬合,對新樣本泛化能力不足;
  • 模型可讀性(解釋性)差。

所謂大浪淘沙始見金,在訓練模型的過程中,我們不光要考慮模型、參數等的選擇與調優,還要花足夠的時間來選取合適的特徵。

二、特徵選擇的方法

在現在的業界共識中,特徵選擇方法基本上可以歸類為三種:過濾法

、包裹法和嵌入法。

1. 過濾法

過濾法最好理解,就是通過一定的統計測量方法對每個特徵進行評分和排序,然後按照一定的標準過濾出最優的特徵子集。過濾法不用考慮後續的學習器,因此計算性能比較好。

2. 包裹法

包裹法的開銷會大一些,效果一般也更好一些,因為這種方法是針對特定的模型量身定製的。使用包裹法時,要先確定準備採納的學習器(模型),然後使用該學習器的性能指標作為特徵子集的評價準則。也就是說,包裹法的目標就是為了給特定學習器選擇最有利於其性能的特徵子集。

3. 嵌入法

嵌入法則更進一步,將特徵選擇和學習器(模型)訓練的過程合二為一,在訓練的過程中自動完成了特徵選擇。

三、scikit-learn中的特徵選擇

下邊兩張圖羅列了scikit-learn中提供的一些特徵選擇的方法,我先來為大家簡單介紹一下。

  • 方差閾值法隸屬於過濾法,這種方法最為簡單,但其效果並不是十分穩定、可靠。這種方法認為低方差的特徵所蘊含的信息量較少,應該丟棄;而高方差的特徵蘊含信息較多,應予以保留。但是在實踐過程中,我們會經常遇到刪除了低方差的特徵,但是模型性能不升反降的情況。因此這種方法僅適用於作為一個探索性的選擇;
  • sklearn中提供了一系列用於單變數選擇的方法,這些方法的原理是根據某些特定的統計指標來分別評測每一個特徵,並根據得分、排序或者p-value來完成篩選。根據IV、AUC、相關係數等來進行特徵選擇的方法需要我們自己來實現,sklearn中並沒有現成的工具可用(不用擔心,我會教大家的);
  • sklearn中也提供了包裹式的特徵選擇方法,比如SelectFromModel、RFE(遞歸消除法)、RFECV(RFE的交叉驗證版),它們都需要我們指定一個評估器(estimator,比如LogisticRegression),然後針對這個評估器,它們會自動選擇最佳的特徵子集。
  • sklearn中也有嵌入式的特徵選擇方法,如RandomizedLasso、RandomizedLogisticRegression等,它們在模型訓練的過程中會自動完成特徵選擇的過程。

後續我們會詳細探討它們各自的原理、實踐等更多細節,今天我們就拿最簡單的方差閾值法來為大家演示一下特徵選擇的過程。

四、一個方差閾值法的簡單例子

我們以sklearn中的iris數據集為例,該數據集一共有4個特徵。我們使用numpy.var()方法來計算每個特徵的方差並列印出來,並且通過數據的.shape屬性來獲取特徵數。

from sklearn.datasets import load_iris
from sklearn.feature_selection import VarianceThreshold
import numpy as np

# 載入數據集
iris = load_iris()
X = iris.data
y = iris.target

# 列印數據集中的特徵數和每個特徵的方差
print(原數據集中的特徵數:
, X.shape[1],
)
print(原數據集中不同特徵的方差:
, np.var(X, axis=0),
)

# 使用VarianceThreshold來過濾掉方差在0.6以下的特徵
selector = VarianceThreshold(threshold=0.6)
X_new = selector.fit_transform(X)

# 列印新數據集的特徵數
print(方差閾值法選擇的特徵數:
, X_new.shape[1])

輸出為:

原數據集中的特徵數:
4

原數據集中不同特徵的方差:
[0.68112222 0.18871289 3.09550267 0.57713289]

方差閾值法選擇的特徵數:
2

那麼接下來,我們以新老特徵集分別來訓練一個模型來看看效果。

首先看看原數據集:

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=101)
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(準確率:, acc)

輸出為:

準確率: 0.9736842105263158

然後我們看看新數據集:

X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=101)
model = LogisticRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(準確率:, acc)

輸出為:

準確率: 0.9473684210526315

可以看到,我們在僅保留了兩個特徵的情況下,僅僅犧牲了一點準確率。

事實上,在這個例子中,這種通過犧牲模型性能來換取計算性能的操作意義不大,因為這個數據集僅有150個樣本、4個特徵,但是當我們面臨成千上萬的特徵、數以億計的樣本時,我們就有必要進行權衡了。當然,特徵選擇的目的遠不止於此,在之後的探討中,我們會發現它的更多優點。


推薦閱讀:
相關文章