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 。
Auto-ML之自動化特征工程