[譯] Scikit-Learn 大變化:合併 Pandas
作者 | Ted Petrou
譯者 | 王天宇
編輯 | Jane
出品 | AI科技大本營
【導讀】近日,Scikit-Learn 釋出了 0.20 版本,這是近年來最大的一次更新。對許多資料科學家來說,一個典型的工作流會在使用 Scikit-Learn 進行機器學習之前,先通過 Pandas 對資料進行分析,而新的版本就將這一過程進行了簡化,並且功能更加多樣、穩定與標準。
今天,我們將通過 Ted Petrou 的一篇技術文章,為大家介紹如何完成從 Pandas 到 Scikit-Learn 這一令人興奮的工作流,並且作者基於 Kaggle 上入門級機器學習競賽之一:Housing Prices: Advanced Regression Techniques 作為案例實踐分析,讓大家可以更好地理解與使用這一工具。
全文概括及目標
-
通過本文,那些將 Scikit-Learn 作為機器學習庫,但依賴 Pandas 進行資料探索和準備工作的使用者一定可以收益良多。(假設你對 Scikit-Learn 和 Pandas 都有所瞭解)
-
我們會探索新的估計器 ColumnTransformer ,它使得我們可以對資料的不同子集單獨且並行地進行轉換,然後再把結果串接在一起。
-
用列中的字串資料來建立供 pandas 使用的資料框,這一過程應該更加標準化。
-
估計器 OneHotEncoder 在對列的字串資料編碼方面有所提升。
-
為了便於獨熱編碼 (one-hot encoding),我們使用新的估計器 SimpleImputer 來用常數填充缺失值。
-
我們會自定義一個估計器,該估計器將取代 Scikit-Learn 的內建工具,來執行對資料框的全部基本轉換操作。
-
最後,我們會基於新的估計器 KBinsDiscretizer 對數值進行二進位制轉換 。
注:作者是在 0.20 版本還沒有正式釋出前完成的這個教程,以後這個教程很可能會在某些方面內容有所更新。
前言
Scikit-Learn 的機器學習模型要求輸入必須是二維的數值資料結構。字串資料是不被接受的。但始終沒有提供一個處理字串列的標準方法,而字串資料在資料科學中是一種極為普遍的存在。這也致使相關教程都在探索用不同的方法來處理字串資料列。
有些解決方案傾向於用 Pandas 的 get_dummies 函式;一些使用 Scikit-Learn 的獨熱編碼方法 LabelBinarizer ,但此方法是為類別資料(目標變數)設計的,而非面向輸入資料;還有一些方案則建立了自定義的估計器;甚至還有整個的工具包,如 sklearn-pandas 就是為了解決這個問題創造出來的。對那些想要基於字串列來建機器學習模型的人來說,標準化方面的欠缺給他們帶來了糟糕的體驗;在轉換特定列而非整個資料集上的技術支援也比較薄弱。例如,將連續特徵進行標準化處理十分普遍,而對類別特徵來說卻很少見。如今這方面會變得容易得多。
sklearn-pandas:
https://github.com/scikit-learn-contrib/sklearn-pandas
▌ 升級至 0.20 版
幾天前,0.20 版的第一個候選版本釋出了。你可以用 conda 安裝它:
或者 pip 安裝:
▌ ColumnTransformer & OneHotEncoder( 升級版) 簡介
隨著版本更新至 0.20,從 Pandas 到 Scikit-Learn 的工作流應該越來越相似了。估計器 ColumnTransformer 會對 Pandas 資料框(或陣列)中列的特定子集執行轉換操作。
OneHotEncoder 並不是一個新的估計器,但它已更新至可以編碼字串資料列。此前,它只能對包含數值形式的類別資料進行編碼。
接下來讓我們看看,這些新特性將如何處理 Pandas 資料框中的字串資料列。
初體驗
通過 Kaggle 房屋資料集小試牛刀
Housing Prices: Advanced Regression Techniques 是 Kaggle 的入門級機器學習競賽之一。該競賽目標是基於給定的80個特徵,來預測房屋價格。特徵列是由連續特徵和類別特徵混雜成的。你可以從網站直接下載資料或使用他們的命令列工具。
參考連結:
https://www.kaggle.com/c/house-prices-advanced-regression-techniques
https://github.com/Kaggle/kaggle-api
▌ 觀察資料
首先我們來看看資料框,並輸出頭幾行資料。
▌ 將目標變數從訓練集中移除
我們要移除的目標變數是 SalePrice,然後將其以陣列的形式賦值給它本身。我們之後做機器學習時會用到它。
▌ 單個字串列的編碼
首先,我們對一個單獨的字串列 HouseStyle 進行編碼,該列含有關於房屋外部情況的資料。讓我們輸出一下每個不同字串的個數。
可見在這一列中,我們有8個不同的值。
▌ Scikit-Learn 必須基於 2D 資料
大多數 Scikit-Learn 估計器都要求資料為嚴格的二維形式。如果我們選擇上面提到的所有列作為 train['HouseStyle'] ,技術上來講,一維資料形式的 Pandas Series 就隨之產生了。我們可以通過將列表傳入空白資料框,來強制 Pandas 建立一個單列資料框:
▌ 轉換器三部曲 —— 匯入、例項化、除錯
Scikit-Learn 的 API 對於所有估計器都是一致的,即通過固定三個步驟的過程來訓練資料。
-
根據不同的模組,引入我們需要的估計器;
-
對估計器進行例項化,改變其預設配置;
-
基於資料對估計器進行除錯。如果需要,將資料轉換到新的地方。
下面我們引入 OneHotEncoder ,並將其例項化,確保我們得到的返回陣列中不存在缺失值,然後用 fit_transform 方法對我們的單列資料編碼。
和我們預期的一樣,它把所有不同的值都編碼成為了二進位制的列。
▌ 有了 NumPy 陣列,列名是什麼呢?
值得注意的一點是,我們輸出的是 NunPy 陣列而非 Pandas 資料框。起初 Scikit-Learn 並不是與 Pandas 可以直接整合的。所有的 Pandas 物件都被轉化成了 NumPy 陣列,NumPy 陣列都是由轉換操作生成的。
通過 get_feature_names 方法,我們仍可以從 OneHotEncoder 物件中獲得列名。
▌ 驗證第一行資料
驗證估計器的執行是否正常是很有必要的。讓我們看一下編碼後的資料的第一行:
這裡將陣列中的第6個值編碼成了1。我們用布林值作為索引調出特徵名字。
現在,我們來驗證初始資料框的列中第一個值是與之相同的。
▌ 利用 inverse_transform 自動化該過程
就像大多數轉換器物件一樣,方法 inverse_transform 方法可以幫你獲取原始資料。這裡我們把 row0 放進列表中,使其變成二維陣列。
我們可以通過將整個陣列進行反轉,來驗證所有的值。
▌ 將轉換器作用於測試集
無論我們對測試集使用什麼轉換器,我們必須對測試集也使用。讓我們來看看測試集,並獲取同樣的列,對其使用我們的轉換器。
我們又獲得了8個列。
這個例子證明了我們的觀點,但還有幾個我們可能遇到的問題,現在讓我們一一來看都有哪些。
難點
▌ 1. 測試集中不存在的類別
如果我們現有一座房屋,其特徵是測試集中不存在的,這種情況怎麼辦呢?比如現有一個特徵 3Story 。現在我們改變房屋特徵的第一個值,然後看看會產生什麼結果。
根據預設設定,我們的編碼器會生成一個錯誤。這是我們所期待的,因為我們要知曉是否有測試集中不存在的字串。如果你也存在這個問題,那麼你的探索可能需要更加深入了。現在我們通過將引數 handle_unknown 設定為 'ignore' 來忽略這一未知元素,並將這一行都編碼為0。
我們來驗證一下第一行均為0.
▌ 2 . 測試集中的缺失值
如果你的測試集中存在缺失值(NaN 或 None),只要將 handle_unknown 設定為 'ignore',缺失值就可以被忽略。現在我們為測試集的前兩個元素加入缺失值。
▌ 3.訓練集中的缺失值
訓練集中存在缺失值是更為嚴重的問題。到目前為止,估計器 OneHotEncoder 還無法很好地解決缺失值問題。
如果像測試集一樣,也有一個方法可以忽略訓練集中的缺失值就好了。目前這種方法並不存在,我們只能對缺失值進行填充。
現在,我們必須對這些缺失值加以填充。預處理模組中老的 Imputer 已經被棄用了。在同樣的位置建立了一個新模組 impute ,配合新的估計器 SimpleImputer 和新方案“常數”。若採用預設設定,這一方案會用字串 'missing_value' 來填充缺失值。我們可以通過引數 fill_value 來設定這個值。
到了這裡,我就可以像之前那樣進行編碼了。
要注意的一點是,現在我們有額外的一個列和特證名。
技能升級
實踐整個轉換工作
▌ 對測試集執行兩次轉換
我們可以手動依次執行上述兩個步驟,如下:
▌ Pipeline 的使用
Scikit-Learn 提供了 Pipeline 估計器,它包括一系列轉換操作,可以將它們一一執行。你可以把機器學習模型作為最終的估計器來執行。現在我們對缺失值進行簡單的填充並編碼。
每個步驟都是一個二元組,包括一個代表該步驟的字串和例項化的估計器。上個步驟的輸出是下個步驟的輸入。
只要將測試集傳入 transform 方法,它就可以基於 pipeline 的每一個步驟進行轉換。
▌ 多字串列的轉換
對含有多個字串的列進行轉換並不是難題。選中你要處理的列,然後基於同一個 pipeline 再傳入新的資料框。
▌ 獲取 pipeline 的片段
我們可以通過從屬性字典獲取名字,來獲取 pipeline 內每個單獨的轉換器。在這個例子中,我們獲取了一個獨熱編碼器,然後輸出特徵名字。
▌ 使用新的 ColumnTransformer 選擇列
全新的 ColumnTransformer 允許我們為不同的列選擇不同的轉換器。類別資料列比連續資料列往往更需要進行單獨轉換。
ColumnTransformer 目前還處於試驗階段,也就說未來可能有所變化。
ColumnTransformer 包括一個三元組列表。元組的第一個值是特徵名,第二個是例項化的估計器,第三個是一系列你想要執行轉換操作的列。該元組的形式如下:
此處的 columns 不一定非要是列名,你也可以用列的整數索引值來代替,或者一個布林陣列,甚至可以用一個函式(該函式要將整個資料框作為引數,並返回所選的列)。
你也可以選擇 NumPy 陣列與 ColumnTransformer 配合使用,但本教程著重於和 Pandas 的整合,所以我們仍採用資料框進行討論。
▌ 將 Pipeline 傳入 ColumnTransformer
我們甚至可以將一個含有多個轉換器的 pipeline 傳入列的轉換器,下面我們就要這麼做,因為我們的字串列需要多個轉換器。
接下來,我們重複上面的缺失值填充步驟,並用 ColumnTransformer 進行編碼。要注意的是 pipeline 和上面是相同的,只是每個變數名後加了個 cat。在下個部分中,我們會為數值列新增一個不同的 pipeline。
▌ 將整個資料框傳入 ColumnTransformer
ColumnTransformer 例項選擇了我們要使用的列,所以我們將整個資料框傳入 fit_transform 方法。
現在我們可以通過同樣的方式將測試集進行轉換。
▌ 獲取特徵名
我們必須花時間要做的一個事情就是去獲取特徵的名字。所有轉換器都儲存在屬性字典 named_transformers_ 中。接下來,我們可以通過名字以及三元組的第一個元素來選取特定的轉換器。下面就是我們選擇轉換器的過程(此處只有一個轉換器,即一個名為 'cat' 的 pipeline)。
接下來,我們從 pipeline 選取獨熱編碼器物件,並獲得特徵名。
▌ 數值列的轉換
數值列需要一組不同的轉換器。數值列中的缺失值通常由中位值或平均值來填充,而非一個常數。而且我們常通過減去每列平均值或除以標準差的方式對其進行標準化,而不是對它們的值直接編碼。這種方法有助於許多模型得到更好的效果,如嶺迴歸模型。
▌ 呼叫所有數值列
我們可以選擇所有的數值列,而非像上面那樣手動選擇一兩個字串列。我們通過根據 dtypes 屬性直接搜尋每一列的資料型別來實現這一目的,然後檢查每個 dtype 的 kind (型別)是否為 'O'。 dtypes 屬性會返回一系列 Numpy dtype 物件。其中每一個都包含 kind 屬性,這些屬性都由一個字元表示。我們可以基於此去搜尋數值或字串列。Pandas 將所有的字串列作為 object (物件)進行儲存,等同於 'O' 的型別。關於 kind 這個屬性的更多介紹可以參考 NumPy 文件。
NumPy 文件:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.kind.html
得到了類別,然後用一個字元來表示這些資料型別。
現假設所有數值列都不是物件。我們也可以通過這種方式來獲取特徵列。
一旦我們獲得了數值列的名字,我們就可以再次使用 ColumnTransformer 了。
▌ 將特徵列和數值列的轉換器相結合
基於 ColumnTransformer ,我們可以分別對資料框的每個部分進行單獨轉換。在這個例子中我們會使用每個單獨列。
接下來我們為特徵列和數值列共同建立一個單獨的 pipeline,然後用 ColumnTransformer 來對它們進行單獨轉換。這兩個轉換器是並行工作的,然後再把兩個結果整合在一起。
進行機器學習
這些工作的全部意義在於佈置我們的資料,以便於我們接下來的機器學習環節。我們可以建立最終版 pipeline,並新增一個機器學習模型作為最後的估計器。pipeline 的第一步是我們上面提到的整個轉換環節。我們將房屋的出售價格 SalePrice 設為 y 。這裡我們用 fit 方法來代替 fit_transform,因為最後一個環節是機器學習模型,不需要轉換操作。
我們可以用 score 方法來評估模型,返回的值為 R-square(確定係數):
模型的表現:交叉驗證
當然了,用訓練集本身來驗證模型是毫無意義的。讓我們做一下 K 折交叉驗證,來看看模型對於未見過的資料有什麼表現。我們設定一個隨機數,以確保在整個過程中都能按固定的隨機序列對原資料進行劃分。
網格搜尋的引數選擇
Scikit-Learn 的網格搜尋需要我們傳入引數名字典,與可能的數值相對映。當我們使用 pipeline 時,必須在每一步的名字後加兩個下劃線,然後再接引數名。如果你的 pipeline 有多個層,我們必須再繼續加兩個下劃線來再提升一個層,直到我們能獲取需要優化其引數的估計器。
▌ 在 Pandas 資料框中獲得網格搜尋的全部結果
網格搜尋的全部結果被儲存在 cv_results_ 中。為了便於展示,這是一個可以轉換為 Pandas 資料框的字典,而且它提供了更加便於手動掃描的結構。
一個完整 WorkFlow 還需要解決的問題
▌ 建立含有全部基礎操作的自定義轉換器
上述討論的工作流還存在一些限制。例如,在我們使用 fit方法時,若 OneHotEncoder 可以提供忽略缺失值的選項就好了。比如,它可以將缺失值編碼為一行0。現在它強制我們用字串來填充缺失值,並將這些字串編碼成一個單獨的列。
▌ 自定義估計器類
Scikit_Learn 的文件提供了許多關於如何寫估計器類方面的指導。base 模組中的 BaseEstimator 類為我們提供了 get_params 和 set_params 方法。做網格搜尋時, set_params 方法是必需的。你可以自己寫,或從 BaseEstimator 直接繼承。
Scikit_Learn 文件: http://scikit-learn.org/stable/developers/contributing.html#rolling-your-own-estimator
BasicTransformer 類可以執行下面一系列操作:
-
用平均值或中位值對數值列的缺失值進行填充
-
將全部數值列做標準化處理
-
對字串列採取獨熱編碼
-
不對特徵列的缺失值加以填充,而是將它們編碼為0
-
忽略測試集字串列中不存在的值
-
允許我們為字串列中一個值必須出現的次數設定閾值。該閾值以下的字串都被編碼為0
-
大多數操作都是基本的轉換,對許多資料集來說是必須執行的。
▌ BasicTransformer 的使用
BasicTransformer 估計器的使用和其它的 scikit-learn 估計器一樣。我們可以將其例項化,然後轉換資料。
▌ 在 pipeline 中使用自定義轉換器
我們可以將自定義的轉換器設定為 pipeline 的一部分。
我們也可以用它做交叉驗證,所得分數與之前的分數很相近。
我們可以把它用作網格搜尋的一部分。這證明了去掉低頻率字串對該模型幫助不大,即使對其它模型有明顯的提升。最佳分數有所提升,可能是因為使用了不同的編碼方式。
▌ 基於新的 KBinsDiscretizer 對數值列作二進位制轉換並編碼
存在多個列都含有關於年份的資訊,所以比起把它們當作特徵列,將這些列的值進行二進位制轉換更為合理。Scikit-Learn 開發了新的估計器 KBinsDiscretizer 來執行這一操作。它不僅將這些值轉換為二進位制碼,還會對其進行編碼。在此之前,你可以通過 Pandas 的 cut 和 qcut 函式手動完成這個過程。
讓我們看看它是如何工作的,以 YearBuilt 列為例。
每個二進位制結果包含的位數相等,我們將每一列相加來驗證這一點。
▌ 用 ColumnTransformer 分別處理所有年份列
我們可以採用 ColumnTransformer 對列的其它子集進行單獨處理。下面一段程式碼是我們之前轉換操作的後續步驟。我們也可以去掉 Id這一列,它的作用只是用來標記每一行。
接下來進行交叉驗證,計算得分,然後我們發現這一系列操作並沒有使結果提升。
改變每一列的二進位制串數目也許會優化我們的結果。但無論如何, KBinsDiscretizer 還是使二進位制化數值變數這一過程變得更加容易了。
Scikit-Learn 0.20 更多的有點
關於新版本還有許多本文未提到的新特性。檢視文件中的 What’s New 部分可以得到更多資訊。
What’s New: http://scikit-learn.org/dev/whats_new.html#version-0-20-0
總結
本文介紹了一種新型工作流,它有助於那些依賴於 Pandas 做資料分析與準備工作的 Scikit-Learn 使用者。
這是一個更加流暢且多功能的過程,包括納入 Pandas 資料框,並將其進行轉換,以便於後續的機器學習,這一系列操作都由全新且優化的估計器 ColumnTransformer 、 SimpleImpute ,、 OneHotEncoder 和 KBinsDiscretizer 來完成。
--【完】--
2018 AI開發者大會
AI熱潮下,技術和落地相輔而進。
2018 AI開發者大會以『 AI技術與應用 』為核心,力邀國內外一線大牛,帶你從專案中貫通AI。
即刻掃碼,搶購福利票!
點選 「閱讀原文」 ,精彩資訊搶先看