博客專欄

        EEPW首頁 > 博客 > 特征選擇:11 種特征選擇策略總結

        特征選擇:11 種特征選擇策略總結

        發(fā)布人:數據派THU 時間:2022-05-15 來源:工程師 發(fā)布文章

        來源DeepHub IMBA

        太多的特征會增加模型的復雜性和過擬合,而太少的特征會導致模型的擬合不足。將模型優(yōu)化為足夠復雜以使其性能可推廣,但又足夠簡單易于訓練、維護和解釋是特征選擇的主要工作。


        “特征選擇”意味著可以保留一些特征并放棄其他一些特征。本文的目的是概述一些特征選擇策略:

        1. 刪除未使用的列
        2. 刪除具有缺失值的列
        3. 不相關的特征
        4. 低方差特征
        5. 多重共線性
        6. 特征系數
        7. p 值
        8. 方差膨脹因子 (VIF)
        9. 基于特征重要性的特征選擇
        10. 使用 sci-kit learn 進行自動特征選擇
        11. 主成分分析 (PCA)


        該演示的數據集在 MIT 許可下發(fā)布,來自 PyCaret——一個開源的低代碼機器學習庫。
        數據集相當干凈,但我做了一些預處理。請注意,我使用此數據集來演示不同的特征選擇策略如何工作,而不是構建最終模型,因此模型性能無關緊要。
        首先加載數據集:





        import pandas as pddata = 'https://raw.githubusercontent.com/pycaret/pycaret/master/datasets/automobile.csv'df = pd.read_csv(data)
        df.sample(5)
        圖片
        該數據集包含 202 行和 26 列——每行代表一個汽車實例,每列代表其特征和相應的價格。這些列包括:




        df.columns
        >> Index(['symboling', 'normalized-losses', 'make', 'fuel-type', 'aspiration', 'num-of-doors', 'body-style', 'drive-wheels', 'engine-location','wheel-base', 'length', 'width', 'height', 'curb-weight', 'engine-type', 'num-of-cylinders', 'engine-size', 'fuel-system', 'bore', 'stroke', 'compression-ratio', 'horsepower', 'peak-rpm', 'city-mpg', 'highway-mpg', 'price'], dtype='object')


        現(xiàn)在讓我們深入研究特征選擇的 11 種策略。


        刪除未使用的列


        當然,最簡單的策略是你的直覺。雖然是直覺,但有時很有用的,某些列在最終模型中不會以任何形式使用(例如“ID”、“FirstName”、“LastName”等列)。如果您知道某個特定列將不會被使用,請隨時將其刪除。在我們的數據中,沒有一列有這樣的問題所以,我在此步驟中不刪除任何列。

        刪除具有缺失值的列


        缺失值在機器學習中是不可接受的,因此我們會采用不同的策略來清理缺失數據(例如插補)。但是如果列中缺少大量數據,那么完全刪除它是非常好的方法。
































        # total null values per columndf.isnull().sum()
        >>symboling             0normalized-losses   35make                 0fuel-type             0aspiration           0num-of-doors         2body-style           0drive-wheels         0engine-location       0wheel-base           0length               0width                 0height               0curb-weight           0engine-type           0num-of-cylinders     0engine-size           0fuel-system           0bore                 0stroke               0compression-ratio     0horsepower           0peak-rpm             0city-mpg             0highway-mpg           0price                 0dtype: int64

        不相關的特征


        無論算法是回歸(預測數字)還是分類(預測類別),特征都必須與目標相關。如果一個特征沒有表現(xiàn)出相關性,它就是一個主要的消除目標。可以分別測試數值和分類特征的相關性。
        數值變量




        # correlation between target and features(df.corr().loc['price'].plot(kind='barh', figsize=(4,10)))
        圖片
        在此示例中,peak-rpm, compression-ratio, stroke, bore, height , symboling 等特征與價格幾乎沒有相關性,因此我們可以刪除它們。

        可以手動刪除列,但我更喜歡使用相關閾值(在本例中為 0.2)以編程方式進行:






        # drop uncorrelated numeric features (threshold <0.2)corr = abs(df.corr().loc['price'])corr = corr[corr<0.2]cols_to_drop = corr.index.to_list()df = df.drop(cols_to_drop, axis=1)

        分類變量
        可以使用箱線圖查找目標和分類特征之間的相關性:




        import seaborn as sns
        sns.boxplot(y = 'price', x = 'fuel-type', data=df)
        圖片
        柴油車的中位價高于汽油車。這意味著這個分類變量可以解釋汽車價格,所以應放棄它。可以像這樣單獨檢查每個分類列。

        低方差特征


        檢查一下我們的特征的差異:








        import numpy as np
        # variance of numeric features(df.select_dtypes(include=np.number).var().astype('str'))
        圖片
        這里的“bore”具有極低的方差,雖然這是刪除的候選者。在這個特殊的例子中,我不愿意刪除它,因為它的值在2.54和3.94之間,因此方差很低:

        df['bore'].describe()

        圖片


        多重共線性


        當任何兩個特征之間存在相關性時,就會出現(xiàn)多重共線性。在機器學習中,期望每個特征都應該獨立于其他特征,即它們之間沒有共線性。高馬力車輛往往具有高發(fā)動機尺寸。所以你可能想消除其中一個,讓另一個決定目標變量——價格。
        我們可以分別測試數字和分類特征的多重共線性:
        數值變量
        Heatmap 是檢查和尋找相關特征的最簡單方法。











        import matplotlib.pyplot as plt
        sns.set(rc={'figure.figsize':(16,10)})sns.heatmap(df.corr(),          annot=True,          linewidths=.5,          center=0,          cbar=False,          cmap="PiYG")plt.show()
        圖片
        大多數特征在某種程度上相互關聯(lián),但有些特征具有非常高的相關性,例如長度與軸距以及發(fā)動機尺寸與馬力。

        可以根據相關閾值手動或以編程方式刪除這些功能。我將手動刪除具有 0.80 共線性閾值的特征。


        # drop correlated featuresdf = df.drop(['length', 'width', 'curb-weight', 'engine-size', 'city-mpg'], axis=1)


        還可以使用稱為方差膨脹因子 (VIF) 的方法來確定多重共線性并根據高 VIF 值刪除特征。我稍后會展示這個例子。


        分類變量
        與數值特征類似,也可以檢查分類變量之間的共線性。諸如獨立性卡方檢驗之類的統(tǒng)計檢驗非常適合它。
        讓我們檢查一下數據集中的兩個分類列——燃料類型和車身風格——是獨立的還是相關的。



        df_cat = df[['fuel-type', 'body-style']]df_cat.sample(5)
        圖片
        然后我們將在每一列中創(chuàng)建一個類別的交叉表/列聯(lián)表。



        crosstab = pd.crosstab(df_cat['fuel-type'], df_cat['body-style'])crosstab
        圖片
        最后,我們將在交叉表上運行卡方檢驗,這將告訴我們這兩個特征是否獨立。



        from scipy.stats import chi2_contingency
        chi2_contingency(crosstab)
        圖片
        輸出依次是卡方值、p 值、自由度和預期頻率數組。

        p 值 <0.05,因此我們可以拒絕特征之間沒有關聯(lián)的原假設,即兩個特征之間存在統(tǒng)計上顯著的關系。
        由于這兩個特征之間存在關聯(lián),我們可以選擇刪除其中一個。
        到目前為止,我已經展示了在實現(xiàn)模型之前應用的特征選擇策略。這些策略在第一輪特征選擇以建立初始模型時很有用。但是一旦構建了模型,就可以獲得有關模型性能中每個特征的適應度的更多信息。根據這些新信息,可以進一步確定要保留哪些功能。
        下面我們使用最簡單的線性模型展示其中的一些方法。

























        # drop columns with missing valuesdf = df.dropna()from sklearn.model_selection import train_test_split# get dummies for categorical featuresdf = pd.get_dummies(df, drop_first=True)# X featuresX = df.drop('price', axis=1)# y targety = df['price']# split data into training and testing setX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
        from sklearn.linear_model import LinearRegression# scalingfrom sklearn.preprocessing import StandardScalerscaler = StandardScaler()X_train = scaler.fit_transform(X_train)X_test = scaler.fit_transform(X_test)# convert back to dataframeX_train = pd.DataFrame(X_train, columns = X.columns.to_list())X_test = pd.DataFrame(X_test, columns = X.columns.to_list())# instantiate modelmodel = LinearRegression()# fitmodel.fit(X_train, y_train)

        現(xiàn)在我們已經擬合了模型,讓我們進行另一輪特征選擇。

        特征系數


        如果正在運行回歸任務,則特征適應度的一個關鍵指標是回歸系數(所謂的 beta 系數),它顯示了模型中特征的相對貢獻。有了這些信息,可以刪除貢獻很小或沒有貢獻的功能。









        # feature coefficientscoeffs = model.coef_
        # visualizing coefficientsindex = X_train.columns.tolist()
        (pd.DataFrame(coeffs, index = index, columns = ['coeff']).sort_values(by = 'coeff').plot(kind='barh', figsize=(4,10)))
        圖片
        某些特征beta 系數很小,對汽車價格的預測貢獻不大。可以過濾掉這些特征:









        # filter variables near zero coefficient valuetemp = pd.DataFrame(coeffs, index = index, columns = ['coeff']).sort_values(by = 'coeff')temp = temp[(temp['coeff']>1) | (temp['coeff']< -1)]
        # drop those featurescols_coeff = temp.index.to_list()X_train = X_train[cols_coeff]X_test = X_test[cols_coeff]

        p 值


        在回歸中,p 值告訴我們預測變量和目標之間的關系是否具有統(tǒng)計顯著性。statsmodels 庫提供了帶有特征系數和相關 p 值的回歸輸出的函數。
        如果某些特征不顯著,可以將它們一個一個移除,然后每次重新運行模型,直到找到一組具有顯著 p 值的特征,并通過更高的調整 R2 提高性能。




        import statsmodels.api as smols = sm.OLS(y, X).fit()print(ols.summary())
        圖片


        方差膨脹因子 (VIF)


        方差膨脹因子 (VIF) 是衡量多重共線性的另一種方法。它被測量為整體模型方差與每個獨立特征的方差的比率。一個特征的高 VIF 表明它與一個或多個其他特征相關。根據經驗:

        • VIF = 1 表示無相關性
        • VIF = 1-5 中等相關性
        • VIF >5 高相關


        VIF 是一種消除多重共線性特征的有用技術。對于我們的演示,將所有 VIF 高于10的刪除。










        from statsmodels.stats.outliers_influence import variance_inflation_factor
        # calculate VIFvif = pd.Series([variance_inflation_factor(X.values, i) for i in range(X.shape[1])], index=X.columns)
        # display VIFs in a tableindex = X_train.columns.tolist()vif_df = pd.DataFrame(vif, index = index, columns = ['vif']).sort_values(by = 'vif', ascending=False)vif_df[vif_df['vif']<10]
        圖片

        基于特征重要性選擇


        決策樹/隨機森林使用一個特征來分割數據,該特征最大程度地減少了雜質(以基尼系數雜質或信息增益衡量)。找到最佳特征是算法如何在分類任務中工作的關鍵部分。我們可以通過 feature_importances_ 屬性訪問最好的特征。
        讓我們在我們的數據集上實現(xiàn)一個隨機森林模型并過濾一些特征。







        from sklearn.ensemble import RandomForestClassifier
        # instantiate modelmodel = RandomForestClassifier(n_estimators=200, random_state=0)# fit modelmodel.fit(X,y)

        現(xiàn)在讓我們看看特征重要性:









        # feature importanceimportances = model.feature_importances_
        # visualizationcols = X.columns(pd.DataFrame(importances, cols, columns = ['importance']).sort_values(by='importance', ascending=True).plot(kind='barh', figsize=(4,10)))
        圖片
        上面的輸出顯示了每個特征在減少每個節(jié)點/拆分處的重要性。

        由于隨機森林分類器有很多估計量(例如上面例子中的 200 棵決策樹),可以用置信區(qū)間計算相對重要性的估計值。









        # calculate standard deviation of feature importancesstd = np.std([i.feature_importances_ for i in model.estimators_], axis=0)
        # visualizationfeat_with_importance = pd.Series(importances, X.columns)fig, ax = plt.subplots(figsize=(12,5))feat_with_importance.plot.bar(yerr=std, ax=ax)ax.set_title("Feature importances")ax.set_ylabel("Mean decrease in impurity")
        圖片
        現(xiàn)在我們知道了每個特征的重要性,可以手動(或以編程方式)確定保留哪些特征以及刪除哪些特征。

        使用 Scikit Learn 自動選擇特征


        sklearn 庫中有一個完整的模塊,只需幾行代碼即可處理特征選擇。
        sklearn 中有許多自動化流程,但這里我只展示一些:



        # import modulesfrom sklearn.feature_selection import (SelectKBest, chi2, SelectPercentile, SelectFromModel, SequentialFeatureSelector, SequentialFeatureSelector)


        基于卡方的技術


        基于卡方的技術根據一些預定義的分數選擇特定數量的用戶定義特征 (k)。這些分數是通過計算 X(獨立)和 y(因)變量之間的卡方統(tǒng)計量來確定的。在 sklearn 中,需要做的就是確定要保留多少特征。如果想保留 10 個功能,實現(xiàn)將如下所示:








        # select K best featuresX_best = SelectKBest(chi2, k=10).fit_transform(X,y)
        # number of best featuresX_best.shape[1]
        >> 10

        如果有大量特征,可以指定要保留的特征百分比。假設我們想要保留 75% 的特征并丟棄剩余的 25%:








        # keep 75% top featuresX_top = SelectPercentile(chi2, percentile = 75).fit_transform(X,y)
        # number of best featuresX_top.shape[1]
        >> 36

        正則化
        正則化減少了過擬合。如果你有太多的特征,正則化控制它們的效果,或者通過縮小特征系數(稱為 L2 正則化)或將一些特征系數設置為零(稱為 L1 正則化)。
        一些模型具有內置的 L1/L2 正則化作為超參數來懲罰特征。可以使用轉換器 SelectFromModel 消除這些功能。
        讓我們實現(xiàn)一個帶有懲罰 = 'l1' 的 LinearSVC 算法。然后使用 SelectFromModel 刪除一些功能。


















        # implement algorithmfrom sklearn.svm import LinearSVCmodel = LinearSVC(penalty= 'l1', C = 0.002, dual=False)model.fit(X,y)# select features using the meta transformerselector = SelectFromModel(estimator = model, prefit=True)
        X_new = selector.transform(X)X_new.shape[1]
        >> 2
        # names of selected featuresfeature_names = np.array(X.columns)feature_names[selector.get_support()]
        >> array(['wheel-base', 'horsepower'], dtype=object)

        序貫法
        序貫法是一種經典的統(tǒng)計技術。在這種情況下一次添加/刪除一個功能并檢查模型性能,直到它針對需求進行優(yōu)化。
        序貫法有兩種變體。前向選擇技術從 0 特征開始,然后添加一個最大程度地減少錯誤的特征;然后添加另一個特征,依此類推。
        向后選擇在相反的方向上起作用。模型從包含的所有特征開始并計算誤差;然后它消除了一個可以進一步減少誤差的特征。重復該過程,直到保留所需數量的特征。














        # instantiate modelmodel = RandomForestClassifier(n_estimators=100, random_state=0)
        # select featuresselector = SequentialFeatureSelector(estimator=model, n_features_to_select=10, direction='backward', cv=2)selector.fit_transform(X,y)
        # check names of features selectedfeature_names = np.array(X.columns)feature_names[selector.get_support()]
        >> array(['bore', 'make_mitsubishi', 'make_nissan', 'make_saab',      'aspiration_turbo', 'num-of-doors_two', 'body style_hatchback', 'engine-type_ohc', 'num-of-cylinders_twelve', 'fuel-system_spdi'], dtype=object)


        主成分分析 (PCA)


        PCA的主要目的是降低高維特征空間的維數。原始特征被重新投影到新的維度(即主成分)。最終目標是找到最能解釋數據方差的特征數量。















        # import PCA modulefrom sklearn.decomposition import PCA# scaling dataX_scaled = scaler.fit_transform(X)# fit PCA to datapca = PCA()pca.fit(X_scaled)evr = pca.explained_variance_ratio_
        # visualizing the variance explained by each principal componentsplt.figure(figsize=(12, 5))plt.plot(range(0, len(evr)), evr.cumsum(), marker="o", linestyle="--")plt.xlabel("Number of components")plt.ylabel("Cumulative explained variance")
        圖片
        20 個主成分解釋了超過 80% 的方差,因此可以將模型擬合到這 20 個成分(特征)。可以預先確定方差閾值并選擇所需的主成分數量。

        總結


        這是對可應用于特征選擇的各種技術的有用指南。在擬合模型之前應用了一些技術,例如刪除具有缺失值的列、不相關的列、具有多重共線性的列以及使用 PCA 進行降維,而在基本模型實現(xiàn)之后應用其他技術,例如特征系數、p 值、 VIF 等。雖然不會在一個項目中完全使用所有策略,這些策略都是我們進行測試的方向。
        本文代碼:https://github.com/mabalam/feature_selection作者:Mahbubul Alam


        *博客內容為網友個人發(fā)布,僅代表博主個人觀點,如有侵權請聯(lián)系工作人員刪除。



        關鍵詞: AI

        相關推薦

        技術專區(qū)

        關閉
        主站蜘蛛池模板: 白河县| 信丰县| 彰武县| 封丘县| 日土县| 双辽市| 余姚市| 安义县| 青冈县| 高安市| 蒙阴县| 贡嘎县| 剑川县| 姚安县| 天峻县| 岫岩| 沙田区| 会昌县| 大英县| 广昌县| 临武县| 封丘县| 荔浦县| 信宜市| 镇远县| 新安县| 宜黄县| 侯马市| 佛冈县| 洛阳市| 长白| 滨州市| 石屏县| 阳泉市| 鹿邑县| 健康| 大兴区| 磴口县| 鄂托克旗| 罗江县| 安宁市|