AutoML之自動化特徵工程

1. 引言

我的覺得,機器學習是朝着更高的易用性、更低的技術門檻、更敏捷的開發成本的方向去發展,且AutoML或者AutoDL的發展無疑是最好的證實。所以花費一些時間學習瞭解了AutoML領域的一些知識,並對AutoML中的技術方案進行概括整理。html

衆所周知,一個完整的機器學習項目可歸納爲以下四個步驟。python

其中,特徵工程(提取)每每是決定模型性能的最關鍵一步。而每每機器學習中最耗時的部分也正是特性工程和超參數調優。所以,許多模型因爲時間限制而過早地從實驗階段轉移到生產階段從而致使並非最優的。git

自動化機器學習(AutoML)框架旨在減小算法工程師們的負擔,以便於他們能夠在特徵工程和超參數調優上花更少的時間,而在模型設計上花更多的時間進行嘗試。github

本文將對AutoML中的自動化特徵工程模塊的現狀展開介紹,如下是目前主流的有關AUTOML的開源包。算法

2. 什麼是自動化特徵工程?

自動化特徵工程旨在經過從數據集中自動建立候選特徵,且從中選擇若干最佳特徵進行訓練的一種方式。數據庫

3. 自動化特徵工程工具包

3.1 Featuretools

Featuretools使用一種稱爲深度特徵合成(Deep Feature Synthesis,DFS)的算法,該算法遍歷經過關係數據庫的模式描述的關係路徑。當DFS遍歷這些路徑時,它經過應用於數據的操做(包括和、平均值和計數)生成綜合特徵。例如,對來自給定字段client_id的事務列表應用sum操做,並將這些事務聚合到一個列中。儘管這是一個深度操做,但該算法能夠遍歷更深層的特徵。Featuretools最大的優勢是其可靠性和處理信息泄漏的能力,同時能夠用來對時間序列數據進行處理。框架

例子:dom

假設有三張表,分別爲clients、loans、payments。機器學習

clients :有關信用合做社客戶的基本信息表。每一個客戶端在此數據框中只有一行。ide

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算法運行的步驟:

  1. 首先,它經過建立混合數據的全部特徵(即影子特徵)爲給定的數據集增長了隨機性。
  2. 而後,它訓練一個隨機森林分類的擴展數據集,並採用一個特徵重要性措施(默認設定爲平均減小精度),以評估的每一個特徵的重要性,越高則意味着越重要。
  3. 在每次迭代中,它檢查一個真實特徵是否比最好的影子特徵具備更高的重要性(即該特徵是否比最大的影子特徵得分更高)而且不斷刪除它視爲很是不重要的特徵。
  4. 最後,當全部特徵獲得確認或拒絕,或算法達到隨機森林運行的一個規定的限制時,算法中止。

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 。

相關文章
相關標籤/搜索