[譯] Python 中的特徵工程自動化

[譯] Python 中的特徵工程自動化

來自專欄掘金翻譯計劃4 人贊了文章

  • 原文地址:Automated Feature Engineering in Python
  • 原文作者:William Koehrsen
  • 譯文出自:掘金翻譯計劃
  • 本文永久鏈接:github.com/xitu/gold-mi
  • 譯者:mingxing47
  • 校對者:yqian1991 Park-ma

Python 中的特徵工程自動化

如何自動化地創建機器學習特徵

機器學習正在利用諸如 H20、TPOT 和 auto-sklearn 等工具越來越多地從手工設計模型向自動化優化管道遷移。以上這些類庫,連同如 random search 等方法一起,目的是在不需要人工干預的情況下找到適合於數據集的最佳模型,以此來簡化器學習的模型選擇和調優部分。然而,特徵工程,作為機器學習管道中一個可以說是更有價值的方面,幾乎全部是手工活。

特徵工程,也稱為特徵創建,是從已有數據中創建出新特徵並且用於訓練機器學習模型的過程。這個步驟可能要比實際使用的模型更加重要,因為機器學習演算法僅僅從我們提供給他的數據中進行學習,創建出與任務相關的特徵是非常關鍵的(可以參照這篇文章 "A Few Useful Things to Know about Machine Learning" —— 《了解機器學習的一些有用的事》,譯者注)。

通常來說,特徵工程是一個漫長的手工過程,依賴於某個特定領域的知識、直覺、以及對數據的操作。這個過程可能會非常乏味並且最終獲得的特性會被人類的主觀性和花在上面的時間所限制。自動特徵工程的目標是通過從數據集中創建許多候選特徵來幫助數據科學家減輕工作負擔,從這些創建了候選特徵的數據集中,數據科學家可以選擇最佳的特徵並且用來訓練。

在這篇文章中,我們將剖析一個基於 featuretools Python library 庫進行自動特徵工程處理的案例。我們將使用一個樣例數據集來展示基本信息(請繼續關注未來的使用真實數據的文章)。這篇文章最終的代碼可以在 GitHub 獲取。


特徵工程基礎

特徵工程意味著從分布在多個相關表格中的現有數據集中構建出額外的特徵。特徵工程需要從數據中提取相關信息,並且將其放入一個單獨的表中,然後可以用來訓練機器學習模型。

構建特徵的過程非常耗時,因為每獲取一項新的特徵都需要很多步驟才能構建出來,尤其是當需要從多於一張表格中獲取信息時。我們可以把特徵創建的操作分成兩類:轉換聚合。讓我們通過幾個例子的實戰來看看這些概念。

一次轉換操作僅作用於一張表,該操作能從一個或多個現有列中創建新特徵(比如說 Python 中,一張表就如同 Pandas 庫中的一個 DataFrame)。如下面的例子所示,假如我們有如下的一張客戶(clients)信息表:

我們可以通過從 joined 列中尋找出月份或者對 income 列取自然對數來創建特徵。這些都是轉換的範疇,因為他們都是使用了單張表中的信息。

另一方面,聚合 則是跨表執行的,其使用了一對多關係進行分組觀察,然後再計算統計數據。比如說,如果我們還有另外一張含有客戶貸款信息的表格,這張表裡可能每個客戶都有多種貸款,我們就可以計算出每位客戶端諸如貸款平均值、最大值、最小值等統計數據。

這個過程包括了根據客戶進行貸款表格分組、計算聚合、然後把計算結果數據合併到客戶數據中。如下代碼展示了我們如何使用 Python 中的 language of Pandas 庫進行計算的過程:

import pandas as pd# 根據客戶 id (client id)進行貸款分組,並計算貸款平均值、最大值、最小值stats = loans.groupby(client_id)[loan_amount].agg([mean, max, min])stats.columns = [mean_loan_amount, max_loan_amount, min_loan_amount]# 和客戶的 dataframe 進行合併stats = clients.merge(stats, left_on = client_id, right_index=True, how = left)stats.head(10)

這些操作本身並不困難,但是如果我們有數百個變數分布在數十張表中,手工進行操作則是不可行的。理想情況下,我們希望有一種解決方案,可以在多個表格當中進行自動轉換和聚合操作,最後將結果數據合併到一張表格中。儘管 Pandas 是一個很優秀的資源庫,但利用 Pandas 時我們仍然需要手工操作很多的數據!(更多關於手工特徵工程的信息可以查看如下這個傑出的著作 Python Data Science Handbook)。

Featuretools 框架

幸運的是, featuretools 正是我們所尋找的解決方案。這個開源的 Python 庫可以自動地從一系列有關聯的表格中創建出很多的特徵。 Featuretools 是基於一個被稱為 "Deep feature synthesis" (深度特徵合成)的方法所創建出來的,這個方法聽起來要比實際跑起來更加令人印象深刻。(這個名字是來自於多特徵的疊加,並不是因為這個方法使用了深度學習!)

深度特徵合成疊加了多個轉換和聚合操作(在 feautretools 中也被稱為 feature primitives (特徵基元))來從遍布很多表格中的數據中創建出特徵。如同絕大多數機器學習中的想法一樣,這是一種建立在簡單概念基礎上的複雜方法。通過一次學習一個構建模塊,我們可以很好地理解這個強大的方法。

首先,讓我們看看我們的數據。之前我們已經看到了一些數據集,完整的表集合如下所示:

  • clients : 客戶在信用社的的基本信息。每個客戶在這個 dataframe 中僅佔一行

  • loans: 給客戶的貸款。每個貸款在這個 dataframe 中僅佔一行,但是客戶可能會有多個貸款

  • payments: 貸款償還。每個付款只有一行,但是每筆貸款可以有多筆付款。

如果我們有一件機器學習任務,例如預測一個客戶是否會償還一個未來的貸款,我們將把所有關於客戶的信息合併到一個表格中。這些表格是相互關聯的(通過 client_idloan_id 變數),我們可以使用一系列的轉換和聚合操作來手工完成這一過程。然而,我們很快就將看到,我們可以使用 featuretools 來自動化這個過程。

實體和實體集

對於 featuretools 來說,最重要的兩個概念是實體實體集。一個實體就只是一張表(或者說一個 Pandas 中的 DataFrame) 。一個實體集是一系列表的集合以及這些表格之間的關係。你可以把實體集認為是 Python 中的另外一個數據結構,這個數據結構有自己的方法和參數。

我們可以在 featuretools 中利用下面的代碼創建出一個空的實體集:

import featuretools as ft# 創建新實體集 es = ft.EntitySet(id = clients)

現在我們必須添加一些實體。每個實體必須有一個索引,它是一個包含所有唯一元素的列。也就是說,索引中的每個值必須只出現在表中一次。clients dataframe 中的索引是 client_id ,因為每個客戶在這個 dataframe 中只有一行。我們使用以下語法向實體集添加一個已經有索引的實體:

# 從客戶 dataframe 中創建出一個實體# 這個 dataframe 已經有一個索引和一個時間索引es = es.entity_from_dataframe(entity_id = clients, dataframe = clients, index = client_id, time_index = joined)

loans datafram 同樣有一個唯一的索引,loan_id 以及向實體集添加 loan_id 的語法和 clients 一樣。然而,對於 payments dataframe 來說,並不存在唯一的索引。當我們向實體集添加實體時,我們需要把參數 make_index 設置為 True( make_index = True ),同時為索引指定好名稱。此外,雖然 featuretools 會自動推斷實體中的每個列的數據類型,我們也可以將一個列類型的字典傳遞給參數 variable_types 來進行數據類型重寫。

# 從付款 dataframe 中創建一個實體# 該實體還沒有一個唯一的索引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)

對於這個 dataframe 來說,即使 missed 是一個整型數據,這不是一個數值變數,因為它只能接受兩個離散值,所以我們告訴 featuretools 將它是為一個分類變數。在向實體集添加了 dataframs 之後,我們將檢查其中的任何一個:

我們指定的修改可以正確地推斷列類型。接下來,我們需要指定實體集中的表是如何進行關聯的。

表關係

考慮兩個表之間的關係的最佳方式是父親與孩子的類比。這是一對多的關係:每個父親可以有多個孩子。在表領域中,父親在每個父表中都有一行,但是子表中可能有多個行對應於同一個父親的多個孩子。

例如,在我們的數據集中,clients dataframe 是 loans dataframe 的父親。每個客戶在 clients 中只有一行,但在 loans 中可能有多行。同樣, loanspayments 的父親,因為每筆貸款都有多個支付。父親通過共享變數與孩子相連。當我們執行聚合時,我們將子表按父變數分組,並計算每個父表的子表的統計信息。

要在 featuretools 中格式化關係,我們只需指定將兩個錶鏈接在一起的變數。 clientsloans 表通過 loan_id 變數鏈接, loanspayments 通過 loan_id 聯繫在一起。創建關係並將其添加到實體集的語法如下所示:

# 客戶與先前貸款的關係r_client_previous = ft.Relationship(es[clients][client_id], es[loans][client_id])# 將關係添加到實體集es = es.add_relationship(r_client_previous)# 以前的貸款和以前的付款之間的關係r_payments = ft.Relationship(es[loans][loan_id], es[payments][loan_id])# 將關係添加到實體集es = es.add_relationship(r_payments)es

實體集現在包含三個實體(或者說是表)和連接這些實體的關係。在添加實體和對關係形式化之後,我們的實體集就準備完成了,我們接下來可以準備創建特徵。

特徵基元

在深入了解特性合成之前,我們需要了解特徵基元。我們已經知道它們是什麼了,但是我們只是用不同的名字稱呼它們!這些是我們用來形成新特徵的基本操作:

  • 聚合:通過父節點對子節點(一對多)關係完成的操作,並計運算元節點的統計信息。一個例子是通過 client_idloan 表分組,並為每個客戶機找到最大的貸款金額。
  • 轉換:在單個表上對一個或多個列執行的操作。舉個例子,取一個表中兩個列之間的差值,或者取列的絕對值。

新特性是在 featruetools 中創建的,使用這些特徵基元本身或疊加多個特徵基元。下面是 featuretools 中的一些特徵基元列表(我們還可以定義自定義特徵基元:

特徵基元

這些基元可以自己使用或組合來創建特徵。要使用指定的基元,我們使用 ft.dfs 函數(代表深度特徵合成)。我們傳入 實體集目標實體(這兩個參數是我們想要加入特徵的表)以及 trans_primitives 參數(用於轉換)和 agg_primitives 參數(用於聚合):

# 使用指定的基元創建新特徵features, feature_names = ft.dfs(entityset = es, target_entity = clients, agg_primitives = [mean, max, percent_true, last], trans_primitives = [years, month, subtract, divide])

以上函數返回結果是每個客戶的新特徵 dataframe (因為我們把客戶定義為目標實體)。例如,我們有每個客戶加入的月份,這個月份是一個轉換特性基元:

我們還有一些聚合基元,比如每個客戶的平均支付金額:

儘管我們只指定了很少一部分的特徵基元,但是 featuretools 通過組合和疊加這些基元創建了許多新特徵。

完整的 dataframe 有793列新特性!

深度特徵合成

現在,我們已經準備好了理解深度特徵合成(deep feature synthesis, dfs)的所有部分。事實上,我們已經在前面的函數調用中執行了 dfs 函數!深度特性只是將多個特徵基元疊加的特性,而 dfs 是生成這些特性的過程的名稱。深度特徵的深度是創建該特性所需的特徵數量。

例如,MEAN(payments.payment_amount) 列是一個深度為 1 的特徵,因為它是使用單個聚合創建的。深度為 2 的特徵是 LAST(loans(MEAN(payments.payment_amount)) ,這是通過疊加兩個聚合而成的: LAST(most recent) 在均值之上。這表示每個客戶最近一次貸款的平均支付金額。

我們可以將特徵疊加到任何我們想要的深度,但是在實踐中,我從來沒有超過 2 的深度。在這之後,這些特徵就很難解釋了,但我鼓勵有興趣的人嘗試「深入研究」。


我們不必手工指定特徵基元,而是可以讓 featuretools 自動為我們選擇特性。為此,我們使用相同的 ft.dfs 函數調用,但不傳遞任何特徵基元:

# 執行深度特徵合成而不指定特徵基元。features, feature_names = ft.dfs(entityset=es, target_entity=clients, max_depth = 2)features.head()

Featuretools 已經為我們構建了許多新的特徵供我們使用。雖然這個過程會自動創建新特徵,但它不會取代數據科學家,因為我們仍然需要弄清楚如何處理所有這些特徵。例如,如果我們的目標是預測客戶是否會償還貸款,我們可以查找與特定結果最相關的特徵。此外,如果我們有特殊領域知識,我們可以使用它來選擇具有候選特徵的特定特徵基元或種子深度特徵合成。

接下來的步驟

自動化的特徵工程解決了一個問題,但卻創造了另一個問題:創造出太多的特徵。雖然說在確定好一個模型之前很難說這些特徵中哪些是重要的,但很可能並不是所有的特徵都與我們想要訓練的任務相關。而且,擁有太多特徵可能會讓模型的表現下降,因為在訓練的過程中一些不太有用的特徵會淹沒那些更為重要的特徵。

太多特徵的問題被稱為維數的詛咒。隨著特徵數量的增加(數據的維數增加),模型越來越難以了解特徵和目標之間的映射。事實上,模型執行良好所需的數據量(與特性的數量成指數比例)(stats.stackexchange.com)。

可以化解維數詛咒的是特徵削減(也稱為特徵選擇):移除不相關特性的過程。這可以採取多種形式:主成分分析(PCA),使用 SelectKBest 類,使用從模型引入的特徵,或者使用深度神經網路進行自動編碼。當然,特徵削減則是另一篇文章的另一個主題了。現在,我們知道,我們可以使用 featuretools ,以最少的工作量從許多表中創建大量的特性!

結論

像機器學習領域很多的話題一樣,使用 feautretools 的自動特徵工程是一個建立在簡單想法之上的複雜概念。使用實體集、實體和關係的概念,feautretools 可以執行深度特性合成來創建新特徵。深度特徵合成反過來又將特徵基元堆疊起來 —— 也就是聚合,在表格之間建立起一對多的關係,同時進行轉換,在單表中對一列或者多列應用,通過這些方法從很多的表格中構建出新的特徵出來。

請持續關注這篇文章,與此同時,閱讀關於這個競賽的介紹 this introduction to get started。我希望您現在可以使用自動化特徵工程作為數據科學管道中的輔助工具。我們的模型將和我們提供的數據一樣好,自動化的特徵工程可以幫助使特徵創建過程更有效。

要獲取更多關於特徵工具的信息,包括這些工具的高級用法,可以查閱在線文檔。要查看特徵工具如何在實踐中應用,可以參見 Feature Labs 的工作成果,這就是開發 featuretools 這個開源庫的公司。

我一如既往地歡迎各位的反饋和建設性的批評,你們可以在 Twitter @koehrsen_will 上與我進行交流。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久鏈接 即為本文在 GitHub 上的 MarkDown 鏈接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。

推薦閱讀:

特徵工程簡介
【特徵工程】特徵選擇與特徵學習
特徵工程-Outliers
基於Python互聯網金融LeningClub貸款違約預測模型
OneHotEncoder獨熱編碼和 LabelEncoder標籤編碼

TAG:機器學習 | 特徵工程 | 數據挖掘 |