在機器學習中,特徵選擇是選擇對您的預測最有用的特徵的過程。儘管聽起來很簡單,但這是創建新的機器學習模型時最複雜的問題之一。
在本文中,我將與您分享我在Fiverr領導的上一個項目期間研究的一些方法。
您將獲得有關我嘗試的基本方法以及更複雜的方法的一些想法,該方法獲得了最佳效果-刪除了60%以上的功能,同時保持了準確性並為我們的模型實現了更高的穩定性。我還將分享我們對該算法的改進。

為什麼進行特徵選擇如此重要?

如果您構建了機器學習模型,您就會知道識別哪些功能很重要而哪些僅僅是噪音是多麼困難。

刪除嘈雜的功能將有助於內存,計算成本和模型準確性。
此外,通過刪除功能部件,將有助於避免模型的過擬合。

有時,您具有一項具有業務意義的功能,但這並不意味着此功能將幫助您進行預測。
您需要記住,功能在一種算法(例如決策樹)中可能有用,而在另一種算法中的代表性可能不足(例如回歸模型),並非所有功能都是一樣的:)

不相關或部分相關的特徵可能會對模型性能產生負面影響。功能選擇和數據清理應該是設計模型的第一步,也是最重要的一步。

特徵選擇方法:

儘管有很多用於特徵選擇的技術,例如向後消除,套索回歸。在本文中,我將分享3種方法,這些方法被發現對完成更好的特徵選擇最有用,每種方法都有其自身的優勢。

“除了X”

在Fiverr中,將這種技術命名為“ All But X”。此技術很簡單,但很有用。

  1. 您可以反覆訓練和評估
  2. 在每次迭代中,都將刪除一個功能。
    如果您有大量功能,則可以刪除功能的“系列”,在Fiverr,我們通常會匯總不同時間,30天點擊次數,60天點擊次數等功能。這是一系列功能。
  3. 根據基準檢查評估指標。

該技術的目標是查看功能族中的哪些不影響評估,或者甚至刪除它也可以改善評估。

這種方法的問題在於,一次刪除一個要素,不會使要素彼此產生效果(非線性效果)。也許特徵X和特徵Y的組合正在產生噪聲,而不僅僅是特徵X。
運行全部流程的“除X之外的所有圖” —在運行所有迭代之後,我們進行了比較以檢查哪個迭代不影響模型的準確性。

這種方法的問題在於,一次刪除一個要素,不會使要素彼此產生效果(非線性效果)。也許特徵X和特徵Y的組合正在產生噪聲,而不僅僅是特徵X。

特徵重要性+隨機特徵

我們嘗試的另一種方法是使用大多數機器學習模型API具有的功能重要性。

我們所做的不僅僅是從功能重要性中獲取前N個功能。我們向數據添加了3個隨機特徵:

  1. 二進制隨機特徵(0或1)
  2. 在0至1個隨機特徵之間均勻
  3. 整數隨機特徵

在重要功能列表之後,我們僅選擇了高於隨機特徵的功能。

重要的是採用隨機特徵的不同分布,因為每種分布都會產生不同的影響。

在樹木中,模型“喜歡”連續要素(由於分割),因此這些要素將位於層次結構中的較高位置。因此,您需要將每個功能與其均布的隨機功能進行比較。

Boruta

Boruta是一種由華沙大學開發的功能分級和選擇算法。該算法基於隨機森林,但也可以用於XGBoost和不同的樹算法。

在Fiverr,我使用了該算法,並對XGBoost排名和分類器模型進行了一些改進,我將對此進行簡要介紹。

該算法是我上面提到的兩種方法的一種組合。

  1. 為數據集中的每個要素創建一個“陰影”要素,具有相同的要素值,但僅在各行之間隨機排列
  2. 循環運行,直到停止條件之一:
    2.1。我們不會刪除任何其他功能
    2.2。我們刪除了足夠的功能-可以說我們要刪除60%的功能
    2.3。我們進行了N次迭代-我們限制了迭代次數以免陷入無限循環
  3. 運行X次迭代-我們使用5來消除模式
    3.1 的隨機性。使用常規特徵和陰影特徵訓練模型
    3.2。保存每個特徵的平均特徵重要性得分
    3.3刪除所有低於其陰影特徵的特徵
def _create_shadow(x):
    """
    Take all X variables, creating copies and randomly shuffling them
    :param x: the dataframe to create shadow features on
    :return: dataframe 2x width and the names of the shadows for removing later
    """
    x_shadow = x.copy()
    for c in x_shadow.columns:
        np.random.shuffle(x_shadow[c].values) # shuffle the values of each feature to all the features
    # rename the shadow
    shadow_names = ["shadow_feature_" + str(i + 1) for i in range(x.shape[1])]
    x_shadow.columns = shadow_names
    # Combine to make one new dataframe
    x_new = pd.concat([x, x_shadow], axis=1)
    return x_new, shadow_names


# Set up the parameters for running the model in XGBoost
param = booster_params
df = pd.DataFrame() # initial empty dataframe

for i in range(1, n_iterations + 1):
    # Create the shadow variables and run the model to obtain importances
    new_x, shadow_names = _create_shadow(x)
    bst, df = _run_model(new_x, y, group, weights, param, num_boost_round, early_stopping_rounds, i == 1, df)
    df = _check_feature_importance(bst, df, i, importance_type)

df[MEAN_COLUMN] = df.mean(axis=1)
# Split them back out
real_vars = df[~df['feature'].isin(shadow_names)]
shadow_vars = df[df['feature'].isin(
)]

# Get mean value from the shadows
mean_shadow = shadow_vars[MEAN_COLUMN].mean() * (perc / 100)
real_vars = real_vars[(real_vars[MEAN_COLUMN] > mean_shadow)]

criteria = _check_stopping_crietria(delta, real_vars, x)

return criteria, real_vars['feature']
從創建陰影-訓練-比較-刪除特徵再返回的Boruta運行圖。
從創建陰影-訓練-比較-刪除特徵再返回的Boruta運行圖。

Boruta 2.0

這是本文的最佳部分,是對Boruta的改進。

我們使用原始模型的“簡短版本”運行了Boruta。通過獲取數據樣本和較少數量的樹(我們使用XGBoost),我們在不降低準確性的情況下改善了原始Boruta的運行時間。

另一個改進是,我們使用前面提到的隨機特徵運行了該算法。可以看出我們已經從數據集中刪除了所有隨機特徵,這是一個很好的條件。

有了改進,我們看不到模型準確性的任何變化,但是看到了運行時的改進。通過刪除,我們能夠將200多個要素轉換為少於70個要素。我們看到了該模型在樹木數量和訓練的不同階段的穩定性。

我們還看到訓練損失與驗證集之間的距離有所改善。

改進和Boruta的優勢在於您正在運行模型。在這種情況下,發現的有問題的特徵對您的模型有問題,而不是不同的算法。

總結

在本文中,您了解了3種不同的技術,這些技術如何對數據集進行特徵選擇以及如何建立有效的預測模型。您看到了我們對Boruta的實現,運行時的改進以及添加了隨機功能以幫助進行健全性檢查。

通過這些改進,我們的模型僅以原始功能的35%就能運行得更快,更穩定並保持了一定的準確性。

選擇最適合您的技術。請記住,功能選擇可以幫助提高準確性,穩定性和運行時間,並避免過度擬合。更重要的是,更少的功能使調試和解釋性變得更加容易。

本文轉自 medium,原文地址