在機器學習的世界中,我常看到許多開發者直接將所有可用特徵丟入模型,期待演算法能自動找出重要的模式。然而,這種做法常導致模型過度複雜、訓練時間延長,甚至降低預測準確度。特徵選擇技術讓我們能夠在訓練前就識別並保留最有價值的特徵,為模型減輕不必要的負擔。

特徵選擇不僅能提升模型效能,還能降低計算資源需求,同時提高模型的可解釋性。在本文中,我將分享幾種實用的特徵選擇技術,幫助你最佳化器學習流程。

使用方差閾值過濾低訊息量特徵

數值特徵的方差閾值過濾

方差閾值過濾是最簡單的特徵選擇方法之一。這種技術根據一個簡單的假設:如果特徵的方差很小,那麼它可能對預測結果貢獻不大。當特徵的所有值都非常接近時,這個特徵可能不會提供模型所需的區分能力。

以下是如何使用方差閾值來過濾低變異的特徵:

# 載入必要的函式庫
from sklearn.feature_selection import VarianceThreshold
import numpy as np

# 建立特徵矩陣
features = np.array([
    [0, 2, 0, 3],
    [0, 1, 4, 3],
    [0, 1, 1, 3],
    [1, 1, 0, 2],
    [0, 1, 0, 1]
])

# 建立方差閾值選擇器
thresholder = VarianceThreshold(threshold=0.5)

# 套用選擇器並轉換特徵矩陣
features_var_threshold = thresholder.fit_transform(features)

# 檢視結果
print("原始特徵數量:", features.shape[1])
print("選擇後特徵數量:", features_var_threshold.shape[1])
print("選擇的特徵:", features_var_threshold)

這段程式碼示範瞭如何使用 scikit-learn 的 VarianceThreshold 類別來過濾低方差特徵。我們首先建立一個包含 5 個樣本、4 個特徵的矩陣,然後設定方差閾值為 0.5。這意味著任何方差低於 0.5 的特徵都會被移除。

執行後,我們可以檢視每個特徵的方差:

# 檢視各特徵的方差
thresholder.fit(features).variances_
# 輸出:array([ 0.68112222, 0.18675067, 3.09242489, 0.57853156])

從輸出可以看出,第二個特徵的方差僅為 0.18675067,低於我們設定的閾值 0.5,因此被過濾掉了。最終保留了三個方差較高的特徵。

在使用方差閾值技術時,有兩點需要特別注意:

  1. 方差並非中心化的,它使用特徵本身的單位的平方。這意味著當特徵集包含不同單位的特徵時(例如一個特徵以年為單位,而另一個以元為單位),方差閾值過濾可能不適用。

  2. 方差閾值需要手動選擇,這需要依靠我們的判斷或使用模型選擇技術來確定最佳閾值。

此外,如果特徵已經被標準化(均值為零,方差為一),那麼方差閾值過濾就失去了意義:

# 載入標準化工具
from sklearn.preprocessing import StandardScaler

# 標準化特徵矩陣
scaler = StandardScaler()
features_std = scaler.fit_transform(features)

# 計算標準化後各特徵的方差
selector = VarianceThreshold()
selector.fit(features_std).variances_
# 輸出:array([ 1., 1., 1., 1.])

標準化後,所有特徵的方差都變成了 1.0,這時方差閾值過濾就無法區分特徵的重要性了。

二元特徵的方差閾值過濾

對於二元特徵(即值只有 0 或 1 的特徵),我們可以使用伯努利隨機變數的方差公式來設定更有意義的閾值:

# 載入必要的函式庫rom sklearn.feature_selection import VarianceThreshold

# 建立特徵矩陣:
# 特徵 0:80% 為類別 0
# 特徵 1:80% 為類別 1
# 特徵 2:60% 為類別 0,40% 為類別 1
features = [[0, 1, 0],
            [0, 1, 1],
            [0, 1, 0],
            [0, 1, 1],
            [1, 0, 0]]

# 使用伯努利分佈的方差公式設定閾值
thresholder = VarianceThreshold(threshold=(.75 * (1 - .75)))
selected_features = thresholder.fit_transform(features)

print("選擇的特徵:", selected_features)

在處理二元特徵時,我們可以利用伯努利隨機變數的方差公式:Var(x) = p(1-p),其中 p 是類別 1 的比例。當絕大多數觀測值都屬於同一類別,該特徵的方差會非常低,可能不包含太多有用訊息。

在這個例子中,我們設定閾值為 0.75 * (1 - 0.75) = 0.1875。這意味著如果一個二元特徵的 0 或 1 的比例超過 75%(或低於 25%),該特徵將被移除。這種方法能有效過濾那些大多數值都相同的二元特徵。

實際應用中,我發現這種方法在處理稀疏特徵(如文字的詞袋模型)時特別有用,因為這些特徵通常有大量的 0 值和少量的 1 值。

處理高度相關的特徵

機器學習中的另一個常見問題是特徵間的高度相關性。當兩個特徵高度相關時,它們包含的訊息很相似,同時保留兩者可能是多餘的。處理高度相關特徵的簡單解決方案是:移除其中一個。

以下是識別和處理高度相關特徵的方法:

# 載入必要的函式庫
import pandas as pd
import numpy as np

# 建立包含兩個高度相關特徵的特徵矩陣
features = np.array([[1, 1, 1],
                     [2, 2, 0],
                     [3, 3, 1],
                     [4, 4, 0],
                     [5, 5, 1],
                     [6, 6, 0],
                     [7, 7, 1],
                     [8, 7, 0],
                     [9, 7, 1]])

# 將特徵矩陣轉換為 DataFrame
dataframe = pd.DataFrame(features)

# 建立相關矩陣(使用絕對值)
corr_matrix = dataframe.corr().abs()

# 選取相關矩陣的上三角部分
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape),
                                  k=1).astype(np.bool))

# 找出相關係數大於 0.95 的特徵欄位索引
to_drop = [column for column in upper.columns if any(upper[column] > 0.95)]

# 移除這些特徵
reduced_features = dataframe.drop(dataframe.columns[to_drop], axis=1)
print(reduced_features.head(3))

這段程式碼演示瞭如何處理高度相關的特徵。我們首先建立一個特徵矩陣,其中第 0 列和第 1 列高度相關。然後,我們計算特徵之間的相關係數矩陣,並使用絕對值來忽略正負相關的差異。

為了避免重複檢查同一對特徵,我們只選取相關矩陣的上三角部分。接著,我們找出相關係數大於 0.95 的特徵,並將其中一個從特徵集中移除。

檢查相關矩陣:

# 相關矩陣
dataframe.corr()
'''
輸出:
   0         1         2
0  1.000000  0.976103  0.000000
1  0.976103  1.000000 -0.034503
2  0.000000 -0.034503  1.000000
'''

可以看出,特徵 0 和特徵 1 之間的相關係數為 0.976103,超過了 0.95 的閾值。因此,我們移除了其中一個特徵(在這個例子中是特徵 1)。

這種方法在處理高維資料時特別有用,例如在金融分析或生物訊息學中,經常會遇到大量潛在相關的變數。透過移除高度相關的特徵,我們可以降低模型的複雜度,同時保留大部分有用的訊息。

移除分類別題中的無關特徵

當我們處理分類別題時,並非所有特徵都對預測目標類別有幫助。識別和移除那些與目標變數關聯性較低的特徵,可以簡化模型並可能提高其效能。

使用卡方檢驗選擇類別特徵

對於類別特徵,我們可以使用卡方檢驗來評估特徵與目標變數之間的相關性:

# 載入必要的函式庫
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2

# 載入資料
iris = load_iris()
features = iris.data
target = iris.target

# 將特徵轉換為整數型態(類別資料)
features = features.astype(int)

# 選擇卡方統計量最高的兩個特徵
chi2_selector = SelectKBest(chi2, k=2)
features_kbest = chi2_selector.fit_transform(features, target)

# 顯示結果
print("原始特徵數量:", features.shape[1])
print("選擇後特徵數量:", features_kbest.shape[1])

卡方檢驗是一種統計方法,用於檢驗兩個類別變數之間的獨立性。在特徵選擇中,我們計算每個特徵與目標變數之間的卡方統計量,然後選擇統計量最高的特徵。

卡方統計量的計算公式為:

χ² = Σ[(O₁ - E₁)² / E₁]

其中 O₁ 是類別 i 中觀測到的樣本數量,E₁ 是在特徵與目標變數獨立的假設下,預期在類別 i 中的樣本數量。

卡方統計量越高,表示特徵與目標變數之間的關聯性越強。在這個例子中,我們使用 SelectKBest 來選擇卡方統計量最高的兩個特徵。

需要注意的是,卡方檢驗只適用於類別特徵。在使用之前,我們需要確保特徵是型別的,或者將數值特徵轉換為類別特徵。此外,所有特徵值必須是非負的,這也是為什麼我們在範例中將特徵轉換為整數型態。

使用 F 值選擇數值特徵

對於數值特徵,我們可以使用 ANOVA F 值來評估特徵與目標變數之間的相關性:

# 載入必要的函式庫
from sklearn.feature_selection import f_classif

# 選擇 F 值最高的兩個特徵
fvalue_selector = SelectKBest(f_classif, k=2)
features_kbest = fvalue_selector.fit_transform(features, target)

# 顯示結果
print("原始特徵數量:", features.shape[1])
print("選擇後特徵數量:", features_kbest.shape[1])

ANOVA(方差分析)F 值是一種統計方法,用於比較不同組之間的均值差異。在特徵選擇中,我們計算每個特徵在不同目標類別下的 F 值,然後選擇 F 值最高的特徵。

F 值越高,表示該特徵在不同類別之間

特徵選擇與模型評估:提升機器學習效能的關鍵技術

在機器學習專案中,我常發現資料科學家過度關注複雜模型的建構,卻忽略了兩個能大幅提升效能的關鍵環節:特徵選擇與模型評估。在本文中,我將分享如何透過數值特徵的統計分析、遞迴特徵消除以及交叉驗證等技術,建立更精確、更高效的機器學習模型。

數值特徵的統計檢定:F值分析

當處理數值特徵時,我們需要判斷哪些特徵對於預測目標變數真正有幫助。F值統計(ANOVA F-value)是一種強大的工具,能檢測特徵與目標變數之間的關聯性。

F值分析的核心思想是檢測:當我們按目標變數分組時,各組的特徵平均值是否有顯著差異。舉例來說,假設我們有一個二元目標變數「性別」和一個數值特徵「測驗分數」,F值會告訴我們男性的平均測驗分數是否與女性的不同。如果沒有差異,那麼測驗分數對預測性別沒有幫助,這個特徵就不相關。

在實務中,我常使用scikit-learn的f_classif函式來計算F值,特別是處理分類別題時。這個簡單的統計檢定能快速篩選出對預測有實質幫助的特徵。

遞迴特徵消除:自動選擇最佳特徵集

在建構模型時,我們常面臨一個問題:到底應該保留哪些特徵?遞迴特徵消除(Recursive Feature Elimination, RFE)提供了一個系統化的答案。

遞迴特徵消除的核心原理

遞迴特徵消除的運作方式相當直觀:

  1. 使用所有特徵訓練一個模型
  2. 找出最不重要的特徵並移除
  3. 用剩餘的特徵重新訓練模型
  4. 重複這個過程,直到模型效能開始下降

結合交叉驗證(Cross-Validation, CV)的遞迴特徵消除(RFECV)更為強大,它能自動找出最佳的特徵數量。

讓我們看如何在Python中實作這個技術:

# 載入必要的函式庫
import warnings
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFECV
from sklearn import datasets, linear_model

# 忽略一些無害的警告
warnings.filterwarnings(action="ignore", module="scipy", message="^internal gelsd")

# 生成特徵矩陣、目標向量
features, target = make_regression(n_samples=10000,
                                  n_features=100,
                                  n_informative=2,
                                  random_state=1)

# 建立線性迴歸模型
ols = linear_model.LinearRegression()

# 遞迴消除特徵
rfecv = RFECV(estimator=ols, step=1, scoring="neg_mean_squared_error")
rfecv.fit(features, target)
selected_features = rfecv.transform(features)

這段程式碼展示瞭如何使用RFECV進行特徵選擇。首先,我們生成了一個有100個特徵的人薪水料集,但其中只有2個特徵真正包含資訊。接著,我們建立一個線性迴歸模型作為評估器,並設定RFECV使用均方誤差作為評分標準。

step=1引數表示每次迭代移除一個特徵。當RFECV完成後,我們可以使用transform()方法取得經過選擇的特徵子集。

執行完上述程式碼後,我們可以檢視RFECV為我們選出了多少個特徵:

# 最佳特徵數量
rfecv.n_features_
# 輸出: 5

我們還可以檢視哪些特徵被保留下來:

# 哪些特徵最佳
rfecv.support_

甚至可以檢視特徵的重要性排名:

# 特徵排名,從最佳(1)到最差
rfecv.ranking_

為何遞迴特徵消除如此有效?

在我多年的機器學習實踐中,發現遞迴特徵消除特別適合處理以下場景:

  1. 高維特徵空間:當特徵數量龐大時,手動選擇特徵幾乎不可能
  2. 多重共線性問題:當特徵之間高度相關時,RFE能幫助選出最具代表性的特徵
  3. 計算資源有限:減少特徵數量能顯著降低模型訓練與推論時間

RFE的核心思想是透過模型的引數(或權重)來判斷特徵重要性。對於線性模型,係數的絕對值大小直接反映了特徵的重要性。每次迭代中,RFE移除係數最小的特徵,這個過程持續到找到最佳特徵子集為止。

值得注意的是,在使用RFE之前,我通常會先對特徵進行標準化處理,確保所有特徵在相同的尺度上比較。

模型評估:交叉驗證的藝術

建立機器學習模型相對容易,但評估其真實效能卻是一門藝術。在探討各種學習演算法前,我們必須先理解如何正確評估模型品質。

為什麼需要交叉驗證?

初學者常犯的錯誤是在訓練資料上評估模型效能。這種方法存在根本性缺陷:我們的目標不是評估模型在已知資料上的表現,而是評估它在未曾見過的資料上的預測能力。

交叉驗證(Cross-Validation)提供了一個強大的框架,用於評估模型在新資料上的泛化能力。

實作交叉驗證評估

以下是如何使用scikit-learn實作完整的交叉驗證流程:

# 載入必要的函式庫
from sklearn import datasets
from sklearn import metrics
from sklearn.model_selection import KFold, cross_val_score
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# 載入數字資料集
digits = datasets.load_digits()

# 建立特徵矩陣與目標向量
features = digits.data
target = digits.target

# 建立標準化器
standardizer = StandardScaler()

# 建立邏輯迴歸物件
logit = LogisticRegression()

# 建立一個先標準化再執行邏輯迴歸的管道
pipeline = make_pipeline(standardizer, logit)

# 建立k折交叉驗證
kf = KFold(n_splits=10, shuffle=True, random_state=1)

# 進行k折交叉驗證
cv_results = cross_val_score(pipeline,  # 管道
                            features,    # 特徵矩陣
                            target,      # 目標向量
                            cv=kf,       # 交叉驗證技術
                            scoring="accuracy",  # 損失函式
                            n_jobs=-1)   # 使用所有CPU核心

# 計算平均值
mean_accuracy = cv_results.mean()
print(f"交叉驗證平均準確率: {mean_accuracy:.4f}")

這段程式碼展示了一個完整的模型評估流程。我們使用了手寫數字識別資料集,並建立了一個處理管道(pipeline),該管道先對資料進行標準化,再應用邏輯迴歸模型。

交叉驗證的核心在於KFold物件,它將資料集分割為10個折疊(folds)。在評估過程中,每次使用9個折疊訓練模型,然後在剩下的1個折疊上測試。這個過程重複10次,每個折疊都會作為測試集一次。

cross_val_score函式自動執行整個交叉驗證過程,並回傳每次評估的效能指標(這裡使用準確率)。最後,我們計算這些指標的平均值,得到模型的整體效能評估。

交叉驗證的深層原理

交叉驗證的核心思想可以概括為:

  1. 資料分割:將資料集分成訓練集和測試集
  2. 模型訓練:使用訓練集訓練模型
  3. 模型評估:在測試集上評估模型效能
  4. 重複過程:多次重複上述步驟,使用不同的資料分割

這個過程有效模擬了模型在實際應用中的表現,因為測試資料對模型來說是「未見過」的。

在我的實踐中,交叉驗證不僅用於評估最終模型,還用於:

  1. 超引數調整:找出最佳的模型引數設定
  2. 特徵選擇:如上文的RFECV
  3. 模型比較:客觀比較不同演算法的效能

模型評估指標的選擇

在交叉驗證過程中,選擇合適的評估指標至關重要。不同的問題需要不同的評估標準:

  • 分類別題:準確率(accuracy)、精確率(precision)、召回率(recall)、F1分數、AUC-ROC
  • 迴歸問題:均方誤差(MSE)、平均絕對誤差(MAE)、R²分數

在上面的例子中,我們使用了準確率作為評估指標,這適合於平衡的分類別題。但對於不平衡的分類別題,準確率可能會產生誤導性結果,此時應考慮使用F1分數或AUC-ROC。

綜合應用:特徵選擇與模型評估的結合

在實際專案中,我經常將特徵選擇和交叉驗證結合使用,以建立最佳模型。以下是這種組合方法的基本流程:

  1. 使用交叉驗證評估基準模型效能
  2. 應用RFECV等技術進行特徵選擇
  3. 使用選定的特徵子集重新訓練模型
  4. 再次使用交叉驗證評估最佳化的模型

這種方法確保了我們的特徵選擇過程不會導致過擬合,同時也提供了可靠的模型效能評估。

特徵選擇與模型評估的進階技巧

嵌入式特徵選擇

除了遞迴特徵消除外,還有一些模型本身就包含了特徵選擇機制,如Lasso迴歸和決策樹。這些模型在訓練過程中自動執行特徵選擇,被稱為「嵌入式特徵選擇」。

巢狀交叉驗證

當我們需要同時進行模型選擇和評估時,巢狀交叉驗證(Nested Cross-Validation)是一個強大的技術。它包含內層交叉驗證(用於模型選擇和超引數調整)和外層交叉驗證(用於無偏估計模型效能)。

時間序列資料的特殊考量

對於時間序列資料,標準的k折交叉驗證可能不適用,因為它打破了資料的時間順序。在這種情況下,時間序列交叉驗證(Time Series Cross-Validation)是更適合的選擇,它確保測試資料總是在時間上晚於訓練資料。

特徵選擇和模型評估是機器學習工作流程中的關鍵環節,它們對於建立高效、準確的預測模型至關重要。透過使用F值統計、遞迴特徵消除和交叉驗證等技術,我們可以系統地最佳化型,提高其在實際應用中的效能。

記住,建立模型很容易,但建立高品質的模型卻需要深思熟慮的特徵選擇和嚴謹的評估方法。在實際專案中,這些技術往往比選擇哪種學習演算法更為重要,因為它們直接影響模型的泛化能力和實用價值。

透過本文介紹的方法,你應該能夠更有信心地建立和評估機器學習模型,確保它們在實際應用中表現出色。無論是預測客戶行為、識

模型評估的藝術:超越表面資料

在機器學習的世界中,建立模型只是旅程的一半;另一半是正確評估這些模型的效能。當我深入研究模型評估技術時,發現許多開發者往往過於關注模型的複雜性,而忽略了評估的嚴謹性。一個複雜但評估不當的模型,就像一座建在沙灘上的城堡,表面華麗卻經不起實際應用的考驗。

讓我們探討幾種評估模型的核心方法,這些方法在我多年的實踐中被證明是最可靠的。

交叉驗證:模型評估的金標準

驗證集的侷限性

最直觀的模型評估方法是將資料分割成訓練集和測試集。在這種方法中,我們將一部分資料保留作為測試集,使用其餘資料訓練模型,然後評估模型在測試集上的表現。

然而,這種簡單的驗證方法存在兩個主要缺陷:

  1. 模型效能高度依賴於哪些觀測值被選入測試集
  2. 模型既沒有使用所有可用資料進行訓練,也沒有在所有可用資料上進行評估

k折交叉驗證:更強大的評估策略

k折交叉驗證(K-Fold Cross-Validation,KFCV)是一種能夠克服上述缺點的策略。在這種方法中,我們將資料分成k個部分(稱為「折」)。模型使用k-1個折進行訓練,然後在剩下的一折上進行測試。這個過程重複k次,每次使用不同的折作為測試集。最後,我們取k次迭代的平均效能作為模型的整體評估。

以下是使用scikit-learn進行10折交叉驗證的範例:

# 使用10折交叉驗證並輸出評估分數
cv_results = cross_val_score(model, features, target, cv=10)

# 檢視所有10折的分數
print(cv_results)
# 輸出結果類別於:
# array([0.97222222, 0.97777778, 0.95555556, 0.95, 0.95555556,
#        0.98333333, 0.97777778, 0.96648045, 0.96089385, 0.94972067])

這段程式碼執行了10折交叉驗證並列印了每一折的評估分數。cross_val_score函式接收模型、特徵矩陣、目標向量以及折數作為引數。輸出的陣列顯示了每一折的準確率,大多數折的準確率都超過了95%,表明模型表現相當穩定。

使用交叉驗證時的關鍵考量

在使用k折交叉驗證時,有三個重要的考慮因素:

  1. 獨立同分布假設:KFCV假設每個觀測值的建立獨立於其他觀測值。如果資料符合這一假設,在分配觀測值到不同折時進行隨機打亂是個好主意。在scikit-learn中,我們可以設定shuffle=True來實作這一點。

  2. 分層抽樣:當評估分類別時,通常希望每個折包含大致相同比例的各個目標類別的觀測值(稱為分層k折)。例如,如果我們的目標向量包含性別,與80%的觀測值是男性,那麼每個折也應該包含80%的男性和20%的女性觀測值。在scikit-learn中,我們可以使用StratifiedKFold替代KFold來實作分層k折交叉驗證。

  3. 正確的資料預處理:使用驗證集或交叉驗證時,必須根據訓練集進行資料預處理,然後將這些轉換應用於訓練集和測試集。例如,當我們擬合標準化物件時,只計算訓練集的均值和方差,然後使用這些引數來轉換訓練集和測試集。

以下是正確的資料預處理範例:

# 匯入函式庫
from sklearn.model_selection import train_test_split

# 建立訓練集和測試集
features_train, features_test, target_train, target_test = train_test_split(
    features, target, test_size=0.1, random_state=1)

# 只使用訓練集擬合標準化器
standardizer.fit(features_train)

# 將轉換應用於訓練集和測試集
features_train_std = standardizer.transform(features_train)
features_test_std = standardizer.transform(features_test)

這段程式碼展示了正確的資料預處理流程。首先,使用train_test_split函式將資料分為訓練集和測試集,測試集佔總資料的10%。然後,僅使用訓練集來擬合標準化器(計算均值和標準差)。最後,使用這個擬合好的標準化器來轉換訓練集和測試集。這種方法避免了測試集訊息洩漏到訓練過程中。

使用Pipeline簡化交叉驗證

scikit-learn的pipeline包使得在使用交叉驗證技術時正確預處理資料變得容易。我們首先建立一個包含資料預處理步驟和模型訓練的管道:

# 建立管道
pipeline = make_pipeline(standardizer, logit)

# 執行k折交叉驗證
cv_results = cross_val_score(
    pipeline,          # 管道
    features,          # 特徵矩陣
    target,            # 目標向量
    cv=kf,             # 交叉驗證技術
    scoring="accuracy", # 損失函式
    n_jobs=-1)         # 使用所有CPU核心

這段程式碼建立了一個處理管道,包含標準化器和邏輯迴歸模型。然後使用cross_val_score函式執行交叉驗證,其中pipeline是我們建立的管道,featurestarget是特徵矩陣和目標向量,kf指定了交叉驗證的方法,scoring引數定義了評估指標(這裡是準確率),n_jobs=-1告訴scikit-learn使用所有可用的CPU核心來加速操作。

cross_val_score還有一些值得注意的引數:

  • cv引數確定我們的交叉驗證技術。k折是最常見的,但還有其他方法,如留一交叉驗證(Leave-One-Out Cross-Validation),其中折數k等於觀測值的數量。
  • scoring引數定義了我們的成功指標,本章其他部分會討論許多不同的指標。
  • n_jobs=-1告訴scikit-learn使用每一個可用的核心。例如,如果你的電腦有四個核心(筆記型電腦常見的數量),那麼scikit-learn將同時使用所有四個核心來加速操作。