高級特徵工程和預處理的四個技巧

2019-08-25   人工智慧遇見磐創

可以說,開發機器學習模型的兩個最重要的步驟是特徵工程預處理。特徵工程包括特徵的創建,而預處理涉及清理數據。

折磨數據,它會坦白任何事情。 - 羅納德科斯

我們經常花費大量時間將數據精鍊成對於建模有用的東西。為了使這項工作更有效,我想分享四個技巧,可以幫助你進行特徵工程和預處理。

要注意到,領域知識是特徵工程時最重要的事情之一(陳詞濫調)。更好地了解你使用的特徵可以幫助防止陷入欠擬合和過擬合的情況

本連結是特徵工程分析的筆記(https://github.com/MaartenGr/feature-engineering)。

1. 重新採樣不平衡數據

實際上,我們經常會遇到不平衡的數據。如果目標數據只有輕微的不平衡,這並不一定是一個問題。可以通過對數據使用適當的驗證措施來解決這個問題,比如平衡精度(Balanced Accuracy)精確召回曲線(Precision-Recall Curves)F1分數(F1-score)

不幸的是,情況並非總是如此,目標變量可能非常不平衡(例如,10:1)。這種情況下,我們可以對該少數類(即樣本數少的類別)進行過採樣,以便使用一種稱為SMOTE的技術來引入平衡。

SMOTE

SMOTE(https://jair.org/index.php/jair/article/view/10302)是合成少數類過採樣技術 (Synthetic Minority Oversampling Technique)的縮寫,是一種用於增加少數類樣本的過採樣技術。

它通過觀察目標的特徵空間和檢測最近鄰來生成新的樣本。然後,在相鄰樣本的特徵空間內,簡單地選擇相似的樣本,每次隨機地改變一列。

要實現SMOTE的模塊可以在imbalanced-learn包(https://imbalanced-learn.readthedocs.io/en/stable/index.html)中找到。導入該包並使用fit_transform方法:

import pandas as pd
from imblearn.over_sampling import SMOTE

# 導入數據創建x和y
df = pd.read_csv('creditcard_small.csv')
X = df.iloc[:,:-1]
y = df.iloc[:,-1].map({1:'Fraud', 0:'No Fraud'})

# 重新採樣數據
X_resampled, y_resampled = SMOTE(sampling_strategy={"Fraud":1000}).fit_resample(X, y)
X_resampled = pd.DataFrame(X_resampled, columns=X.columns)

正如你所看到的,模型成功地對目標變量進行了過採樣。當使用SMOTE進行過採樣時,可以採用以下幾種策略:

  • 「少數類(minority)」:僅重採樣少數類;
  • 「非少數類(not minority)」:重新採樣除少數類以外的其他類;
  • 「非多數類(not majority)」:重新採樣除了多數類的其他類;
  • '所有(all)':重新採樣所有類;
  • "詞典(dict)":鍵為目標類,值對應於每個目標類所需的樣本數量。

我選擇使用字典來指定我想要在多大程度上過採樣數據。

附加提示1:如果數據集中有分類變量,那麼可能會為那些不能發生的變量創建值。例如,如果有一個名為isMale的變量,它只能是0或1,SMOTE可能會創建0.365作為值。

這種情況下,可以使用SMOTENC,它考慮到了分類變量的性質。這個版本也可以在imbalanced-learn包(https://imbalanced-learn.readthedocs.io/en/stable/index.html)中找到。

附加提示2:確保在訓練集與測試集分割之後進行過採樣,並且只對訓練數據進行過採樣。因為通常不在合成數據上測試模型的性能。

2. 創建新的特徵

為了提高模型的質量和預測能力,經常從現有變量中創建新特徵。我們可以在每對變量之間創建一些交互(例如,乘法或除法),希望找到一個有趣的新特徵。然而,這是一個漫長的過程,需要大量的編碼。幸運的是,這可以使用深度特徵綜合(Deep Feature Synthesis)自動完成。

深度特徵綜合

深度特徵綜合(DFS)是一種能夠快速創建具有不同深度的新變量的算法。例如,不僅可以對列進行相乘,你也可以選擇先將列A與列B相乘,然後再添加列C。

首先,讓我介紹將用於示例的數據。我選擇使用人力資源分析數據(https://www.kaggle.com/lnvardanyan/hr-analytics)分析數據,因為這些特徵很容易解釋:

簡單地根據我們的直覺,我們可以將 average_monthly_hours 除以number_project的值添加為一個新變量。然而,如果我們只跟著直覺,我們可能會錯過很多的關係。

如果你的數據如果是一個簡單的表格,你可以簡單地按照下面的代碼:

import featuretools as ft
import pandas as pd

# 創建實體
turnover_df = pd.read_csv('turnover.csv')
es = ft.EntitySet(id = 'Turnover')
es.entity_from_dataframe(entity_id = 'hr', dataframe = turnover_df, index = 'index')

# 指定trans_primitives參數以執行深度特徵綜合
feature_matrix, feature_defs = ft.dfs(entityset = es, target_entity = 'hr',
trans_primitives = ['add_numeric', 'multiply_numeric'],
verbose=True)

第一步是創建一個實體(entity),如果需要,可以從這個實體與其他表創建關係。接下來,我們可以簡單地運行ft.dfs來創建新變量。我們指定參數trans_primitives來表示以什麼方式創建變量。這裡我們選擇將數值變量相加或相乘。

正如你在上面的圖中所看到的,我們僅使用幾行代碼就創建了另外668個特徵。創建的一些特徵示例:

  • last_evaluate乘以satisfaction_level
  • left乘以promotion_last_5years
  • average_monthly_hours乘以satisfaction_level加上time_spend_company

附加技巧1:注意這裡的實現是比較簡單基礎的。DFS最大的優點是它可以進行表之間的聚合中創建新的變量。有關示例,請參見此連結(https://docs.featuretools.com/loading_data/using_entitysets.html)。

附加技巧2:運行ft.list_primitives(),以查看可以執行的聚合的完整列表。它甚至處理時間戳(timestamps)、空值(null values)和經緯度信息(long/lat information)。

3. 處理缺失值

像往常一樣,沒有一種處理缺失值的最佳方法。根據你的數據,只需用特定組的平均值或模式填充它們就足夠了。然而,有一些高級技術使用數據的已知部分來估算(impute)缺失的值。

其中一種方法來自Scikit-Learn中的一個新包叫做Iterative Imputer,它是基於R語言(MICE包)來估算缺失的變量。

Iterative Imputer(疊代輸入器)

雖然python是開發機器學習模型的一種很好的語言,但是仍然有很多方法在R中工作得更好。

Iterative Imputer是由Scikit-Learn開發的,它將每個缺失值的特徵作為其他特徵的函數來建模。它使用該函數的值作為估算。在每個步驟中,選擇一個特徵作為輸出y,其他所有特徵作為輸入的X。然後在X和y上訓練一個回歸器,用來預測y的缺失值。

讓我們看一個例子。我使用的數據是著名的titanic數據集。在這個數據集中,Age列缺少我們希望填充的值。代碼總是很簡單:

# 顯示的導入enable_iterative_imputer後
# 再從sklearn.impute 導入IterativeImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.ensemble import RandomForestRegressor
import pandas as pd
# 載入數據
titanic = pd.read_csv("titanic.csv")
titanic = titanic.loc[:, ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare']]
# 使用隨機森林估計器
imp = IterativeImputer(RandomForestRegressor(), max_iter=10, random_state=0)
titanic = pd.DataFrame(imp.fit_transform(titanic), columns=titanic.columns)

這種方法的優點是它允許使用你所選擇的估計器(estimator)。我使用隨機森林作為估計器來模擬在R中經常使用的missForest。

附加提示1:如果你有足夠的數據,那麼簡單地刪除缺少數據的示例可能是一個有吸引力的選項。但是,請記住,它可能會在你的數據中產生誤差,因為丟失的數據可能遵循了你錯過的模式。

附加提示2:Iterative Imputer允許使用不同的估計器。經過一些測試,我發現甚至可以使用Catboost作為估計器!不幸的是,LightGBM和XGBoost不能工作,因為它們的隨機狀態名(random state name)不同。

4. 異常值檢測

如果不能很好地理解數據,就很難檢測出異常值。如果你很了解數據,那麼你可以很容易地指定數據還具有意義的閾值。

有時這是不可能的,因為很難完全理解數據。這種情況下,你可以使用離群點檢測算法,比如流行的Isolation Forest

Isolation Forest

在Isolation Forest算法中,關鍵字是Isolation(孤立)。從本質上說,該算法檢查了樣本是否容易被分離。這樣樣本就產生了一個孤立編號,這個孤立編號由隨機決策樹中孤立該樣本所需的分割數來計算。這個孤立編號在所有生成的樹上進行平均。

如果算法只需要做幾次分割就能找到一個樣本,那麼該樣本更有可能是一個異常樣本。分割本身也是隨機劃分的,這樣異常樣本在樹中往往很淺(節點到根節點的路徑長度短)。因此,當孤立編號在所有樹中都很小時,該樣本很可能是異常的。

為了展示一個例子,我再次使用了我們之前使用的信用卡數據集:

from sklearn.ensemble import IsolationForest
import pandas as pd
import seaborn as sns
# 預測和可視化離群點
credit_card = pd.read_csv('creditcard_small.csv').drop("Class", 1)
clf = IsolationForest(contamination=0.01, behaviour='new')
outliers = clf.fit_predict(credit_card)
sns.scatterplot(credit_card.V4, credit_card.V2, outliers, palette='Set1', legend=False)

附加提示1:有一個擴展版的Isolation Forest(https://github.com/sahandha/eif)可用,它改進了一些缺點。然而,評論褒貶不一(https://towardsdatascience.com/outlier-detection-with-extended-isolation-forest-1e248a3fe97b)。

結論

希望本文中的技巧能夠幫助你設計和預處理這些特徵,Jupyter寫的筆記在本連結(https://github.com/MaartenGr/feature-engineering/blob/master/Engineering%20Tips.ipynb)