機器學習的模型訓練越來越自動化,但特徵工程還是一個漫長的手動過程,依賴於專業的領域知識,直覺和資料處理。而特徵選取恰恰是機器學習重要的先期步驟,雖然不如模型訓練那樣能產生直接可用的結果。本文作者將使用Python的featuretools庫進行自動化特徵工程的示例。
機器學習越來越多地從手動設計模型轉變為使用H20,TPOT和auto-sklearn等工具來自動優化的渠道。這些庫以及隨機搜尋等方法旨在通過查詢資料集的最優模型來簡化模型選擇和轉變機器學習的部分,幾乎不需要人工干預。然而,特徵工程幾乎完全是人工,這無疑是機器學習管道中更有價值的方面。
特徵工程也稱為特徵建立,是從現有資料構建新特徵以訓練機器學習模型的過程。這個步驟可能比實際應用的模型更重要,因為機器學習演算法只從我們提供的資料中學習,然而建立與任務相關的特徵絕對是至關重要的。
通常,特徵工程是一個漫長的手動過程,依賴於專業的領域知識,直覺和資料處理。這個過程可能非常繁瑣,而且最終的特徵將受到人類主觀性和時間的限制。自動化特徵工程旨在通過從資料集中自動建立許多候選特徵來幫助資料科學家,並從中可以選擇最佳特徵用於訓練。
在本文中,我們將使用Python 的featuretools庫進行自動化特徵工程的示例。我們將使用示例資料集來演示基礎知識。
完整程式碼:
https://github.com/WillKoehrsen/automated-feature-engineering/blob/master/walk_through/Automated_Feature_Engineering.ipynb
特徵工程基礎
特徵工程意味著從現有資料中構建額外特徵,這些資料通常分佈在多個相關表中。特徵工程需要從資料中提取相關資訊並將其放入單個表中,然後可以使用該表來訓練機器學習模型。
構建特徵的過程非常地耗時,因為每個特徵的構建通常需要一些步驟來實現,尤其是使用多個表中的資訊時。我們可以將特徵建立的步驟分為兩類:轉換和聚合。讓我們看幾個例子來了解這些概念的實際應用。
轉換作用於單個表(從Python角度來看,表只是一個Pandas 資料框),它通過一個或多個現有的列建立新特徵。
例如,如果我們有如下客戶表。
我們可以通過查詢joined列的月份或是獲取income列的自然對數來建立特徵。這些都是轉換,因為它們僅使用來自一個表的資訊。
import pandas as pd
# Group loans by client id and calculate mean, max, min of loans
stats = loans.groupby('client_id')['loan_amount'].agg(['mean', 'max', 'min'])
stats.columns = ['mean_loan_amount', 'max_loan_amount', 'min_loan_amount']
# Merge with the clients dataframe
stats = clients.merge(stats, left_on = 'client_id', right_index=True, how = 'left')
stats.head(10)
另一方面,聚合作用於多個表,並使用一對多的關係對觀測值進行分組,然後計算統計資料。例如,如果我們有另一個包含客戶貸款的資訊表格,其中每個客戶可能有多筆貸款,我們可以計算每個客戶的貸款的平均值,最大值和最小值等統計資料。
此過程包括通過客戶資訊對貸款表進行分組,計算聚合,然後將結果資料合併到客戶資料中。以下是我們如何使用Pandas庫在Python中執行此操作。
這些操作本身並不困難,但如果我們有數百個變數分佈在幾十個表中,那麼這個過程要通過手工完成是不可行的。理想情況下,我們需要一種能夠跨多個表自動執行轉換和聚合的解決方案,並將結果資料合併到一個表中。儘管Pandas庫是一個很好的資源,但通過我們手工完成的資料操作是有限的。
手動特徵工程的更多資訊:
https://jakevdp.github.io/PythonDataScienceHandbook/05.04-feature-engineering.html
Featuretools
幸運的是,featuretools正是我們正在尋找的解決方案。這個開源Python庫將自動從一組相關表中建立許多特徵。Featuretools基於一種稱為“深度特徵合成”的方法,這個名字聽起來比實際的用途更令人印象深刻
深度特徵合成實現了多重轉換和聚合操作(在featuretools的詞彙中稱為特徵基元),通過分佈在許多表中的資料來建立特徵。像機器學習中的大多數觀念一樣,它是建立在簡單概念基礎上的複合型方法。通過一次學習一個構造塊的示例,我們就會容易理解這種強大的方法。
首先,我們來看看我們的示例資料。 我們已經看到了上面的一些資料集,完整的表集合如下:
客戶:即有關信貸聯盟中客戶的基本資訊。每個客戶在此資料框中只有一行。
貸款:即客戶貸款。每項貸款在此資料框中只有自己單獨一行的記錄,但客戶可能有多項貸款。
付款:即支付貸款。 每筆支付只有一行記錄,但每筆貸款都有多筆支付記錄。
如果我們有機器學習目標,例如預測客戶是否將償還未來貸款,我們希望將有關客戶的所有資訊組合到一個表中。這些表是相關的(通過client_id和loan_id變數),目前我們可以手動完成一系列轉換和聚合過程。然而,不久之後我們就可以使用featuretools來自動化該過程。
實體和實體集
featuretools的前兩個概念是實體和實體集。實體只是一個表(如果用Pandas庫的概念來理解,實體是一個DataFrame(資料框))。
EntitySet(實體集)是表的集合以及它們之間的關係。可以將實體集視為另一個Python資料結構,該結構具有自己的方法和屬性。)
我們可以使用以下命令在featuretools中建立一個空實體集:
import featuretools as ft
# Create new entityset
es = ft.EntitySet(id = 'clients')
現在我們新增實體。每個實體都必須有一個索引,該索引是一個包含所有唯一元素的列。也就是說,索引中的每個值只能出現在表中一次。
clients資料框中的索引是client_id,因為每個客戶在此資料框中只有一行。 我們使用以下語法將一個現有索引的實體新增到實體集中:
# Create an entity from the client dataframe
# This dataframe already has an index and a time index
es = es.entity_from_dataframe(entity_id = 'clients', dataframe = clients, index = 'client_id', time_index = 'joined')
loans資料框還具有唯一索引loan_id,並且將其新增到實體集的語法與clients相同。但是,對於payments資料框,沒有唯一索引。當我們將此實體新增到實體集時,我們需要傳入引數make_index = True並指定索引的名稱。此外,雖然featuretools會自動推斷實體中每列的資料型別,但我們可以通過將列型別的字典傳遞給引數variable_types來覆蓋它。
# Create an entity from the payments dataframe
# This does not yet have a unique index
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')
對於這個資料框,即使missed 的型別是一個整數,但也不是一個數字變數,因為它只能取2個離散值,所以我們告訴featuretools將缺失資料視作是一個分類變數。將資料框新增到實體集後,我們檢查它們中的任何一個:
使用我們指定的修改模型能夠正確推斷列型別。接下來,我們需要指定實體集中的表是如何相關的。
資料表之間的關係
考慮兩張資料表之間關係的最佳方式是用父對子的類比 。父與子是一對多的關係:每個父母可以有多個孩子。在資料表的範疇中,父表的每一行代表一位不同的父母,但子表中的多行代表的多個孩子可以對應到父表中的同一位父母。
例如,在我們的資料集中,clients客戶資料框是loan 貸款資料框的父級,因為每個客戶在客戶表中只有一行,但貸款可能有多行。
同樣,貸款loan資料是支付payments資料的父級,因為每筆貸款都有多筆付款。父級資料表通過共享變數與子級資料表關聯。當我們執行聚合操作時,我們通過父變數對子表進行分組,並計算每個父項的子項之間的統計資料。
我們只需要指明將兩張資料表關聯的那個變數,就能用featuretools來建立表格見的關係 。
客戶clients資料表和貸款loans資料表通過變數client_id
相互關聯,而貸款loans資料表和支付payments資料表則通過變數loan_id相互關聯。以下是建立關聯並將其新增到entiytset的語法:
# Relationship between clients and previous loans
r_client_previous = ft.Relationship(es['clients']['client_id'],
es['loans']['client_id'])
# Add the relationship to the entity set
es = es.add_relationship(r_client_previous)
# Relationship between previous loans and previous payments
r_payments = ft.Relationship(es['loans']['loan_id'],
es['payments']['loan_id'])
# Add the relationship to the entity set
es = es.add_relationship(r_payments)
es
現在,在entityset中包含了三張資料表,以及三者間的關係。在新增entities並建立關聯後,我們的entityset就算完成了,可以開始建立特徵量了。
特徵基元
在我們完全深入進行特徵合成之前,我們需要了解特徵基元。我們已經知道它們是什麼了,但我們剛剛用不同的名字來稱呼它們!這些只是我們用來形成新功能的基本操作:
聚合:基於父表與子表(一對多)關係完成的操作,按父表分組,並計運算元表的統計資料。一個例子是通過client_id對貸款loan表進行分組,並找到每個客戶的最大貸款額。
轉換:在單個表上對一列或多列執行的操作。一個例子是在一個表中取兩個列之間的差異或取一列的絕對值。
在featuretools中使用這些基元本身或堆疊多個基元,來建立新功能。下面是featuretools中一些特徵基元的列表(我們也可以定義自定義基元)
這些原語可以單獨使用,也可以組合使用來建立特徵量。要使用指定的基元製作特徵,我們使用ft.dfs函式(代表深度特徵合成)。我們傳入entityset,target_entity,這是我們要新增特徵的表,選擇的trans_primitives(轉換)和agg_primitives(聚合):
# Create new features using specified primitives
features, feature_names = ft.dfs(entityset = es, target_entity = 'clients',
agg_primitives = ['mean', 'max', 'percent_true', 'last'],
trans_primitives = ['years', 'month', 'subtract', 'divide'])
結果是每個客戶端的新特徵資料框(因為我們使客戶端成為target_entity)。例如,我們有每個客戶加入的月份,這是由轉換特徵基元生成的:
我們還有許多聚合基元,例如每個客戶的平均付款金額:
儘管我們只指定了一些特徵基元,但featuretools通過組合和堆疊這些基元建立了許多新特徵。
深度特徵合成
我們現在已經做好準備來理解深度特徵合成(dfs)。實際上,我們已經在之前的函式呼叫中執行了dfs!深度特徵僅僅是堆疊多個基元的特徵,而dfs是製作這些特徵的過程名稱。深度特徵的深度是製作特徵所需的基元的數量。
例如,MEAN(payments.payment_amount)列是深度為1的深層特徵,因為它是使用單個聚合建立的。深度為2的特徵是LAST(貸款(MEAN(payments.payment_amount))這是通過堆疊兩個聚合來實現的:最後一個(最近的)在MEAN之上。這表示每個客戶最近貸款的平均支付額。
我們可以將功能堆疊到我們想要的任何深度,但在實踐中,我從未用過超過2的深度。在此之後,生成的特徵就很難解釋,但我鼓勵任何有興趣的人嘗試“更深入” 。
我們不必手動指定特徵基元,而是可以讓featuretools自動為我們選擇特徵。我們可以使用相同的ft.dfs函式呼叫,但不傳入任何特徵基元:
# Perform deep feature synthesis without specifying primitives
features, feature_names = ft.dfs(entityset=es, target_entity='clients',
max_depth = 2)
features.head()
Featuretools為我們構建了許多新特徵。雖然此過程會自動建立新特徵,但仍需要資料科學家來弄清楚如何處理所有這些特徵。例如,如果我們的目標是預測客戶是否會償還貸款,我們可以尋找與指定結果最相關的特徵。此外,如果我們有領域知識,我們可以使用它來選擇特定的特徵基元或種子深度特徵合成候選特徵。
下一步
自動化特徵工程雖然解決了一個問題,但又導致了另一個問題:特徵太多。雖然在擬合模型之前很難說哪些特徵很重要,但很可能並非所有這些特徵都與我們想要訓練模型的任務相關。此外,特徵太多可能會導致模型效能不佳,因為一些不是很有用的特徵會淹沒那些更重要的特徵。
特徵過多的問題被稱為維度詛咒 。隨著特徵數量的增加(資料的維度增加),模型越來越難以學習特徵和目標之間的對映。實際上,模型執行所需的資料量隨著特徵數量呈指數級增長。
維度詛咒與特徵縮減(也稱為特徵選擇)相對應:刪除不相關特徵的過程。特徵選擇可以採用多種形式:主成分分析(PCA),SelectKBest,使用模型中的特徵重要性,或使用深度神經網路進行自動編碼。但是,減少功能是另一篇文章的另一個主題。目前,我們知道我們可以使用featuretools以最小的努力從許多表建立許多功能!
結論
與機器學習中的許多主題一樣,使用featuretools的自動化特徵工程是一個基於簡單想法的複雜概念。使用實體集,實體和關係的概念,featuretools可以執行深度特徵合成以新建特徵。
聚合就是將深度特徵合成依次將特徵基元堆疊 ,利用了跨表之間的一對多關係,而轉換是應用於單個表中的一個或多個列的函式,從多個表構建新特徵。
在以後的文章中,我將展示如何使用這種技術解決現實中的問題,也就是目前正在Kaggle上主持的Home Credit Default Risk競賽。請繼續關注該帖子,同時閱讀此介紹以開始參加比賽!我希望您現在可以使用自動化特徵工程作為資料科學管道的輔助工具。模型的效能是由我們提供的資料所決定的,而自動化功能工程可以幫助提高建立新特徵的效率。
有關featuretools的更多資訊,包括高階用法,請檢視線上文件:
https://docs.featuretools.com
要了解功能工具在實踐中的使用方式,請閱讀開源庫背後的公司Feature Labs的工作:
https://www.featurelabs.com