Auto-ML之自動化特徵工程
1. 引言
個人以為,機器學習是朝著更高的易用性、更低的技術門檻、更敏捷的開發成本的方向去發展,且Auto-ML或者Auto-DL的發展無疑是最好的證明。因此花費一些時間學習瞭解了Auto-ML領域的一些知識,且對Auto-ML中的技術方案進行歸納整理。
一個完整的機器學習專案可概括為如下四個步驟。
其中,特徵工程(提取)往往是決定模型效能的最關鍵一步。而往往機器學習中最耗時的部分也正是特性工程和超引數調優。因此,許多模型並不是最優的,因為它們由於時間限制而過早地從實驗階段轉移到生產階段。
自動化機器學習(AutoML)框架旨在減少演算法工程師們的負擔,以便於他們可以在特徵工程和超引數調優上花更少的時間,而在模型設計上花更多的時間進行試驗。
本文將對Auto-ML中的自動化特徵工程模組的現狀展開介紹,以下是目前主流的有關AUTO-ML的開源包。
2. 什麼是自動化特徵工程?
自動化特徵工程旨在通過從資料集中自動建立候選特徵,且從中選擇最佳特徵進行訓練的一種方式。
3. 自動化特徵工程工具包
3.1 Featuretools
Featuretools使用一種稱為深度特徵合成(Deep Feature Synthesis,DFS)的演算法,該演算法遍歷通過關係資料庫的模式描述的關係路徑。當DFS遍歷這些路徑時,它通過應用於資料的操作(包括和、平均值和計數)生成綜合特徵。例如,它可能對來自給定客戶機id的事務列表應用sum操作,將這些事務聚合到一個列中。儘管這是一個深度操作,但該演算法可以遍歷更深層的特徵。特徵工具最大的優點是其可靠性和處理資訊洩漏的能力,同時使用時間序列資料。
Featuretools是一個執行自動化特性工程的框架。它擅長將時間和關係資料集轉換為機器學習的特徵。
例子:
假設有三張表,分別為clients、loans、payments。
clients :有關信用合作社客戶的基本資訊表。每個客戶端在此資料框中只有一行。
loans:向客戶提供的貸款表。每筆貸款在此資料框中只有自己的行,但客戶可能有多筆貸款。
payments:貸款償還表。每筆付款只有一行,但每筆貸款都有多筆付款。
以每個client_id為物件構造特徵:
傳統的特徵工程方案是利用Pandas對所需特徵做處理,例如下表中的獲取月份、收入值的對數。
同時,也可以通過與loans表關聯獲取新的特徵(每個client平均貸款額度、最大貸款額度等)。
而Featuretools通過基於一種稱為“ 深度特徵合成 ”的方法,即通過堆疊多個特徵來完成特徵工程。
深度特徵合成堆疊多個轉換和聚合操作(在 特徵工具 的詞彙中稱為 特徵基元 ),以通過分佈在許多表中的資料建立特徵。
Featuretools有兩個主要概念:
- 第一個是entities,它可被視為單個表。
- 第二個是entityset,它是實體(表)的集合,以及用來表示實體之間的關係。
首先,需要建立一個存放所有資料表的空實體集物件:
import featuretools as ft es = ft.EntitySet(id='clients')
現在需要新增實體:每個實體都必須有一個索引,索引是由實體中具有唯一元素值的列構成。也就是說,索引中的每個值必須只出現在表中一次。
es = es.entity_from_dataframe(entity_id='clients', dataframe=clients, index='client_id', time_index='joined') es = es.entity_from_dataframe(entity_id='loans', dataframe=loans, index='loans_id', time_index='joined')
而對於沒有唯一索引的表:需要傳入引數make_index = True並指定索引的名稱。
此外,雖然featuretools會自動推斷實體中每個列的資料型別,但仍可以通過將列型別的字典傳遞給引數variable_types來重新定義資料型別。例如對“missed”欄位我們定義為類別型變數。
es = es.entity_from_dataframe(entity_id='payments', dataframe=payments, variable_types={'missed': ft.variable_types.Categorical}, make_index=True, index='payment_id', time_index='payment_date')
在執行聚合計算時,要在featuretools中指定表之間的關係時,只需指定將兩個表關聯在一起的特徵欄位。clients和loans表通過client_id欄位關聯,loans和payments通過loan_id欄位關聯。
建立表之間關係並將其新增到entityset的程式碼如下所示:
# 'clients'表與loans表關聯 r_client_previous = ft.Relationship(es['clients']['client_id'], es['loans']['client_id']) # 將關係新增到實體集 es = es.add_relationship(r_client_previous) # loans表與payments表關聯 r_payments = ft.Relationship(es['loans']['loan_id'], es['payments']['loan_id']) # 將關係新增到實體集 es = es.add_relationship(r_payments)
在新增實體和形式化關係之後, entityset就完成了。
需要注意,featuretools 是通過以下兩種操作進行特徵構造:
- Aggregations:分組聚合
- Transformations:列之間計算
在 featuretools 中,可以使用這些原語自行建立新特性,也可以將多個原語疊加在一起。下面是featuretools中的一些功能原語列表:
此外,我們也可以定義自定義原語,詳見: https://docs.featuretools.com/guides/advanced_custom_primitives.html 。
接下來是進行特徵構造,這也是自動化特徵工程中最重要的一步:
features, feature_names = ft.dfs(entityset=es, target_entity='clients', agg_primitives=['mean', 'max', 'percent_true', 'last'], trans_primitives=['years', 'month', 'subtract', 'divide'])
當然,也可以讓 featuretools 自動為我們選擇特徵:
features, feature_names = ft.dfs(entityset=es, target_entity='clients', max_depth=2)
3.2 Boruta
Boruta主要是用來進行特徵選擇。所以嚴格意義上,Boruta並不是我們所需要的自動化特徵工程包。
Boruta-py是brouta特徵約簡策略的一種實現,在該策略中,問題以一種完全相關的方式構建,演算法保留對模型有顯著貢獻的所有特徵。這與許多特徵約簡演算法所應用的最小最優特徵集相反。boruta方法通過建立由目標特徵的隨機重排序值組成的合成特徵來確定特徵的重要性,然後在原始特徵集的基礎上訓練一個簡單的基於樹的分類器,在這個分類器中,目標特徵被合成特徵所替代。所有特性的效能差異用於計算相對重要性。
Boruta函式通過迴圈的方式評價各變數的重要性,在每一輪迭代中,對原始變數和影子變數進行重要性比較。如果原始變數的重要性顯著高於影子變數的重要性,則認為該原始變數是重要的;如果原始變數的重要性明顯低於影子變數的重要性,則認為該原始變數是不重要的。其中,原始變數就是我們輸入的要進行特徵選擇的變數;影子變數就是根據原始變數生成的變數
生成規則是:
- 先向原始變數中加入隨機干擾項,這樣得到的是擴充套件後的變數
- 從擴充套件後的變數中進行抽樣,得到影子變數
使用python來實現影子特徵,類似於:
# 從訓練資料集獲取特徵 z = train_df[f].values # Shuffle np.random.shuffle(z) # 影子特徵 train_df[f + "shadow"] = z
下面是Boruta演算法執行的步驟:
- 首先,它通過建立混合資料的所有特徵(即影子特徵)為給定的資料集增加了隨機性。
- 然後,它訓練一個隨機森林分類的擴充套件資料集,並採用一個特徵重要性措施(預設設定為平均減少精度),以評估的每個特徵的重要性,越高則意味著越重要。
- 在每次迭代中,它檢查一個真實特徵是否比最好的影子特徵具有更高的重要性(即該特徵是否比最大的影子特徵得分更高)並且不斷刪除它視為非常不重要的特徵。
- 最後,當所有特徵得到確認或拒絕,或演算法達到隨機森林執行的一個規定的限制時,演算法停止。
3.3 tsfresh
tsfresh是 基於可伸縮假設檢驗的時間序列特徵提取工具。該包包含多種特徵提取方法和魯棒特徵選擇演算法。
tsfresh可以自動地從時間序列中提取100多個特徵。這些特徵描述了時間序列的基本特徵,如峰值數量、平均值或最大值,或更復雜的特徵,如時間反轉對稱性統計量等。
這組特徵可以用來在時間序列上構建統計或機器學習模型,例如在迴歸或分類任務中使用。
時間序列通常包含噪聲、冗餘或無關資訊。因此,大部分提取出來的特徵對當前的機器學習任務沒有用處。為了避擴音取不相關的特性,tsfresh包有一個內建的過濾過程。這個過濾過程評估每個特徵對於手頭的迴歸或分類任務的解釋能力和重要性。它建立在完善的假設檢驗理論的基礎上,採用了多種檢驗方法。
需要注意的是,在使用tsfresh提取特徵時,需要提前把結構進行轉換,一般上需轉換為(None,2)的結構,例如下圖所示:
例子:
import matplotlib.pylab as plt from tsfresh import extract_features, select_features from tsfresh.utilities.dataframe_functions import impute from tsfresh.feature_extraction import ComprehensiveFCParameters from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report import pandas as pd import numpy as np if __name__ == '__main__': N = 500 df = pd.read_csv('UCI HAR Dataset/train/Inertial Signals/body_acc_x_train.txt', delim_whitespace=True, header=None) y = pd.read_csv('UCI HAR Dataset/train/y_train.txt', delim_whitespace=True, header=None, squeeze=True)[:N] # plt.title('accelerometer reading') # plt.plot(df.ix[0, :]) # plt.show() # extraction_settings = ComprehensiveFCParameters() master_df = pd.DataFrame({'feature': df[:N].values.flatten(), 'id': np.arange(N).repeat(df.shape[1])}) # 時間序列特徵工程 X = extract_features(timeseries_container=master_df, n_jobs=0, column_id='id', impute_function=impute, default_fc_parameters=extraction_settings) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) cl = DecisionTreeClassifier() cl.fit(X_train, y_train) print(classification_report(y_test, cl.predict(X_test))) # 未進行時間序列特徵工程 X_1 = df.ix[:N - 1, :] X_train, X_test, y_train, y_test = train_test_split(X_1, y, test_size=.2) cl = DecisionTreeClassifier() cl.fit(X_train, y_train) print(classification_report(y_test, cl.predict(X_test)))
此外,對於進行時間序列特徵工程後的資料集進行特徵選擇,進一步提高模型指標。
這裡,可以利用tsfresh.select_features方法進行特徵選擇,然而由於其僅適用於二進位制分類或迴歸任務,所以對於6個標籤的多分類,我們將多分類問題轉換為6個二元分類問題,故對於每一種分類,都可以通過二分類進行特徵選擇:
relevant_features = set() for label in y.unique(): y_train_binary = y_train == label X_train_filtered = select_features(X_train, y_train_binary) print("Number of relevant features for class {}: {}/{}".format(label, X_train_filtered.shape[1], X_train.shape[1])) relevant_features = relevant_features.union(set(X_train_filtered.columns)) X_train_filtered = X_train[list(relevant_features)] X_test_filtered = X_test[list(relevant_features)] cl = DecisionTreeClassifier() cl.fit(X_train_filtered, y_train) print(classification_report(y_test, cl.predict(X_test_filtered)))
注意:在Windows開發環境下,會丟擲“The "freeze_support()" line can be omitted if the program is not going to be frozen to produce an executable.”多程序的錯誤,導致無限迴圈,解決方法是在程式碼執行時引入” if __name__ == '__main__’:“ 。可參考: https://github.com/blue-yonder/tsfresh/issues/185 。
以下是分別使用 tsfresh進行特徵工程、未進行特徵工程以及使用 tsfresh進行特徵工程+特徵選擇後的模型效果:
4. 總結
自動化特徵工程解決了特徵構造的問題,但同時也產生了另一個問題:在資料量一定的前提下,由於產生過多的特徵,往往需要進行相應的特徵選擇以避免模型效能的降低。事實上,要保證模型效能,其所需的資料量級需要隨著特徵的數量呈指數級增長。
本文完整程式碼位於: https://github.com/wangkangdegithub/AutoML 。