[譯] 使用 Python 進行自動化特徵工程

Python 中的特徵工程自動化

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

機器學習正在利用諸如 H20TPOTauto-sklearn 等工具愈來愈多地從手工設計模型向自動化優化管道遷移。以上這些類庫,連同如 random search 等方法一塊兒,目的是在不須要人工干預的狀況下找到適合於數據集的最佳模型,以此來簡化器學習的模型選擇和調優部分。然而,特徵工程,做爲機器學習管道中一個能夠說是更有價值的方面,幾乎所有是手工活。html

特徵工程,也稱爲特徵建立,是從已有數據中建立出新特徵而且用於訓練機器學習模型的過程。這個步驟可能要比實際使用的模型更加劇要,由於機器學習算法僅僅從咱們提供給他的數據中進行學習,建立出與任務相關的特徵是很是關鍵的(能夠參照這篇文章 "A Few Useful Things to Know about Machine Learning" —— 《瞭解機器學習的一些有用的事》,譯者注)。前端

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

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


特徵工程基礎

特徵工程意味着從分佈在多個相關表格中的現有數據集中構建出額外的特徵。特徵工程須要從數據中提取相關信息,而且將其放入一個單獨的表中,而後能夠用來訓練機器學習模型。ios

構建特徵的過程很是耗時,由於每獲取一項新的特徵都須要不少步驟才能構建出來,尤爲是當須要從多於一張表格中獲取信息時。咱們能夠把特徵建立的操做分紅兩類:轉換聚合。讓咱們經過幾個例子的實戰來看看這些概念。git

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

咱們能夠經過從 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/a/65380/157…)。

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

結論

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

請持續關注這篇文章,與此同時,閱讀關於這個競賽的介紹 this introduction to get started。我但願您如今可使用自動化特徵工程做爲數據科學管道中的輔助工具。咱們的模型將和咱們提供的數據同樣好,自動化的特徵工程能夠幫助使特徵建立過程更有效。

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

我一如既往地歡迎各位的反饋和建設性的批評,大家能夠在 Twitter @koehrsen_will 上與我進行交流。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


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

相關文章
相關標籤/搜索