Spark權威指南(中文版)----第25章 預處理和特徵工程

​Spark The Definitive Guide(Spark權威指南) 中文版。本書詳細介紹了Spark2.x版本的各個模塊,目前市面上最好的Spark2.x學習書籍!!!

掃碼關注公衆號:登峯大數據,閱讀中文Spark權威指南(完整版),系統學習Spark大數據框架!

如果您覺得作者翻譯的內容有幫助,請分享給更多人。您的分享,是作者翻譯的動力

任何稱職的數據科學家都知道,高級分析中最大的挑戰之一(和時間消耗)是預處理。這並不是因爲它是特別複雜的編程,而是因爲它需要對正在處理的數據有深入的瞭解,並且需要理解模型需要什麼,才能成功地利用這些數據。本章將詳細介紹如何使用Spark執行預處理和特徵工程。我們將介紹您需要滿足的核心需求,以便根據數據的結構來訓練MLlib模型。然後,我們將討論Spark爲執行這類工作提供的不同工具。

25.1.根據用例格式化模型

要爲Spark的不同高級分析工具預處理數據,您必須考慮您的最終目標。下面的列表介紹了MLlib中每個高級分析任務對輸入數據結構的要求:

在大多數分類和迴歸算法的情況下,您希望將數據放入Double類型的列來表示標籤,並將數據放入Vector類型的列(密集或稀疏)來表示特徵。

  • 對於推薦算法,您希望將數據放入用戶列、項目列(比如電影或書籍)和評級列中。

  • 在無監督學習的情況下,需要一列Vector類型(密集或稀疏)來表示特徵。

  • 在圖計算的情況下,您將需要頂點的DataFrame和邊的DataFrame。

 

 

 

以這些格式獲取數據的最佳方法是通過transformers(轉換器)。轉換器是接受一個DataFrame作爲參數並返回一個新的DataFrame作爲響應的函數。本章將重點介紹與特定用例相關的transformers,而不是試圖列舉所有可能的transformers。

注意

Spark內置了許多transformers作爲org.apache.spark.ml.feature包的一部分。Python中相應的包是pyspark.ml.feature。新的transformers不斷出現在Spark MLlib中,因此不可能在本書中包含一個明確的列表。最新的信息可以在Spark文檔站點上找到。

在我們繼續之前,我們將閱讀幾個不同的樣本數據集,每一個都有不同的屬性,我們將在本章操作:

除了這些真實的銷售數據,我們還將使用幾個簡單的合成數據集。FakeIntDF、simpleDF和scaleDF都只有很少的行。這將使您能夠專注於我們正在執行的精確數據操作,而不是任何特定數據集的各種不一致性。因爲我們要訪問銷售數據很多次,我們要緩存它這樣我們就能從內存中有效地讀取它而不是每次需要時從磁盤中讀取它。爲了更好地理解數據集中的內容,我們還檢查前幾行數據:

注意

需要注意的是,我們在這裏過濾了空值。此時,MLlib並不總是能很好地處理空值。這是導致問題和錯誤的常見原因,也是調試時的重要第一步。每個Spark版本都進行了改進,以改進對空值的算法處理。

25.2.Transformers

我們在前一章討論了transformers,但是在這裏有必要再次回顧它們。Transformers是以某種方式轉換原始數據的函數。這可能是創建一個新的交互變量(來自其他兩個變量),對一個列進行規範化,或者簡單地將它轉換爲一個Double以輸入到模型中。Transformers主要用於預處理或特徵生成。

Spark的transformer只包含轉換方法。這是因爲它不會根據輸入數據進行更改。圖25-1是一個簡單的說明。左邊是一個輸入DataFrame,其中包含要操作的列。右邊是輸入DataFrame,其中有一個表示輸出轉換的新列。

Tokenizer是transformer的一個例子。它標記一個字符串,在一個給定的字符上分裂,並且沒有從我們的數據中學到什麼;它只是應用一個函數。我們將在本章後面更深入地討論Tokenizer,但這裏有一個小代碼片段,展示了Tokenizer是如何構建來接受輸入列的,它如何轉換數據,然後從該轉換輸出的 :

25.3.用於預處理的Estimators

預處理的另一個工具是estimators評估器。當您希望執行的轉換必須使用有關輸入列的數據或信息初始化時(通常通過傳遞輸入列本身派生),estimators是必要的。例如,如果您想將列中的值縮放爲均值爲零和單位方差,則需要對整個數據執行一次遍歷,以便計算用於將數據標準化爲均值爲零和單位方差的值。實際上,estimator可以是根據特定輸入數據配置的transformer。簡單地說,您可以盲目地應用轉換(“常規”轉換器類型),也可以基於數據執行轉換(estimator類型)。圖25-2簡單地說明了一個適合於特定輸入數據集的estimator,生成一個transformer,然後將其應用於輸入數據集,以附加一個新的列(轉換後的數據)。

此類estimator的一個例子是StandardScaler,它根據列中的值範圍對輸入列進行縮放,使每個維度的均值爲零,方差爲1。因此,它必須首先執行數據傳遞來創建transformer。下面是顯示整個過程和輸出的示例代碼片段:

 

我們將在本章的後面使用estimators評估器和transformers轉換器,並對這些特定的estimators評估器進行更多的介紹(並在Python中添加示例)。

25.3.1.Transformer的屬性設置

所有transformers都要求至少指定inputCol和outputCol,它們分別表示輸入和輸出的列名。用setInputCol和setOutputCol設置這些。有一些缺省值(您可以在文檔中找到),但是爲了清晰起見,最好親自手動指定它們。除了輸入列和輸出列之外,所有transformers都有不同的參數,您可以對其進行調優(在本章中提到參數時,必須使用set()方法對其進行設置)。在Python中,我們還有另一種方法來使用對象構造函數的關鍵字參數設置這些值。爲了保持一致性,我們在下一章的例子中排除了這些。Estimators要求您將transformer匹配到特定的數據集,然後對生成的對象調用transform。

注意

Spark MLlib將關於它在每個DataFrame中使用的列的元數據存儲爲列本身的屬性。這允許它正確地存儲(並註釋)Double列實際上可能表示一系列分類變量,而不是連續值。但是,在打印schema或DataFrame時,不會顯示元數據。

25.4.高級Transformers

高級轉換,如我們在前一章中看到的RFormula,允許您在一個轉換中簡潔地指定多個轉換。這些操作在“高級”上運行,允許您避免逐個進行數據操作或轉換。一般來說,您應該儘量使用最高級別的轉換器,以便將錯誤風險降到最低,並幫助您關注業務問題,而不是實現的較小細節。雖然這不總是可能的,但這是一個很好的目標。

25.4.1.RFormula

當您有“常規”格式的數據時,RFormula是最容易使用的transfomer。Spark從R語言中借鑑了這個transformer,使以聲明方式爲數據指定一組轉換變得簡單。使用這個transformer,值可以是數值型的,也可以是分類的,您不需要從字符串中提取值或以任何方式操作它們。RFormula將通過執行所謂的one-hot encoding自動處理分類輸入(指定爲字符串)。簡而言之,one-hot編碼將一組值轉換爲一組二進制列,指定數據點是否具有每個特定的值(我們將在本章後面更深入地討論one-hot編碼)。使用RFormula,數值列將被強制轉換爲Double,但不會使用one-hot編碼。如果標籤列的類型是String,那麼它將首先使用StringIndexer轉換爲Double。

警告

自動將數字列強制轉換爲Double,而不進行one-hot encoding具有一些重要的含義。如果您有數值型的分類變量,它們將被強制轉換爲Double,隱式地指定一個順序。重要的是確保輸入類型與預期的轉換相對應。如果類別變量實際上沒有順序關係,則應該將它們轉換爲String。您還可以手動索引列(參見“處理分類特徵”)。

RFormula允許您使用聲明式語法指定轉換。一旦理解了語法,使用起來就很簡單。目前,RFormula支持有限的R操作符子集,這些操作符在實踐中非常適用於簡單的轉換。基本操作如下:

~

target目標和terms特徵分隔符

+

連接terms。”+0”的意思是移除截距(這意味着我們擬合的直線的y軸截距爲0)

-

刪除terms。”-1”的意思是移除截距(這意味着我們擬合的直線的y軸截距是0,這和+ 0是一樣的)

:

交互(數值相乘,或二值化分類值)

.

除目標/因變量外的所有列

RFormula輸出的標籤和特徵集合(用於監督機器學習)使用默認的label列和features列來標記。本章後面介紹的模型默認情況下需要這些列名,因此很容易將轉換後的DataFrame傳遞到用於訓練的模型中。如果此時你不是完全理解,不要擔心——一旦我們在後面的章節中真正開始使用模型,它就會變得清晰起來。

讓我們在一個例子中使用RFormula。在本例中,我們希望使用所有可用變量(.),然後指定value1和color之間的交互,value2和color之間的交互作爲要生成的附加特性:

25.4.2.SQL Transformer

SQLTransformer允許您像使用MLlib轉換一樣利用Spark的大量sql相關操作庫。可以在SQL中使用的任何SELECT語句都是有效的轉換。惟一需要更改的是,不使用表名,而只使用關鍵字THIS。如果您想將一些DataFrame操作作爲預處理步驟,或者在超參數調優期間嘗試不同的SQL表達式來獲得特性,那麼您可能需要使用SQLTransformer。還要注意,此轉換的輸出將作爲列附加到輸出DataFrame中。

您可能想要使用SQLTransformer,以便在最原始的數據形式上表示所有的操作,以便可以將不同的操作變體版本化爲transformer。這使您可以通過簡單地交換transformers來構建和測試不同的管道。下面是使用SQLTransformer的一個基本例子:

下面是輸出的一個示例:

有關這些轉換的詳細示例,請參閱第2部分。

25.4.3.VectorAssembler

VectorAssembler是一個工具,您幾乎可以在生成的每個pipeline中使用它。它幫助您將所有特性連接到一個大向量中,然後您可以將其傳遞給一個estimator。它通常在機器學習管道的最後一步中使用,並接受Boolean, Double,或者 Vector作爲輸入。如果您要使用各種transformers轉換器執行許多操作,並且需要收集所有這些結果,那麼這將非常有用。

以下代碼片段的輸出將清楚地說明這是如何工作的:

25.5.處理連續特徵

連續特徵就是數軸上的值,從正無窮到負無窮。連續特性有兩種常見的transformers。首先,您可以通過一個稱爲bucketing的過程將連續特性轉換爲分類特性,或者您可以根據幾個不同的需求對特徵進行伸縮和標準化。這些bucketing只適用於Double類型,所以請確保您已經將任何其他數值轉換爲Double:

25.5.1.Bucketing

bucketing 或者 binning最直接的方法是使用Bucketizer。這將把給定的連續特徵分成指定的幾個buckets。您可以指定如何通過Double數組或列表創建bucket。這很有用,因爲您可能希望簡化數據集中的特性,或者簡化它們的表示,以便稍後進行解釋。例如,假設您有一個列,它表示一個人的體重,您希望根據這個信息預測一些值。在某些情況下,創建“overweight(超重)”、“average(平均體重)”和“underweight(體重不足)”這三類buckets可能更簡單。

要指定bucket,請設置它的邊界。例如,將contDF上的split設置爲5.0、10.0和250.0實際上會失敗,因爲我們沒有覆蓋所有可能的輸入範圍。在指定bucket points時,傳遞給split的值必須滿足三個要求:

  • splits數組中的最小值必須小於DataFrame中的最小值。

  • splits數組中的最大值必須大於DataFrame中的最大值。

  • 如果創建兩個bucket,需要在split數組中指定至少三個值。

 

 

 

警告

由於我們通過split方法指定bucket邊界,所以Bucketizer可能會讓人感到困惑,因爲這些並不是真正的split。

要覆蓋所有可能的範圍,scala.Double.NegativeInfinity可能是另一個分割選項,用scala.Double.PositiveInfinity涵蓋所有可能的範圍以外的內部split。在Python中,我們用以下方式指定它:float(“inf”)、float(“-inf”)。

爲了處理null或NaN值,我們必須將handleInvalid參數指定爲某個值。我們可以保留這些值(keep)、error或null,或者跳過這些行。下面是一個使用bucketing的例子:

除了基於硬編碼值進行分割外,另一個選項是基於數據中的百分位數進行分割。這是使用QuantileDiscretizer完成的,它將把值存儲到用戶指定的buckets中,分割由近似的分位數值確定。例如,第90分位數是數據中90%的數據低於該值的點。通過使用setRelativeError爲近似分位數計算設置相對誤差,可以控制buckets的柔和分割程度。Spark允許您指定希望從數據中提取的buckets的數量,並相應地分割數據。下面是一個例子:

高級bucketing技術

這裏描述的技術是bucketing數據最常見的方法,但是Spark中還有許多其他方法。從數據流的角度來看,所有這些流程都是相同的:從連續數據開始,並將它們放在buckets中,以便進行分類。差異的產生取決於計算這些buckets的算法。我們剛纔看到的簡單示例很容易集成和使用,但是在MLlib中也可以使用更高級的技術,比如局部敏感性哈希(LSH)。

25.5.2.縮放和規範化

我們瞭解瞭如何使用bucketing從連續變量中創建分組。另一個常見的任務是對連續數據進行伸縮和規範化。雖然並非總是必要的,但這樣做通常是最佳實踐。當您的數據包含基於不同比例的許多列時,您可能希望這樣做。例如,假設我們有一個包含兩列的DataFrame:weight重量(盎司)和height高度(英尺)。如果不進行縮放或標準化,算法對高度變化的敏感度就會降低,因爲以英尺爲單位的高度值要比以盎司爲單位的重量值低得多。這是一個應該縮放數據的例子。

標準化的一個例子可能包括轉換數據,使每個點的值都表示其與該列平均值的距離。用之前的例子,我們可能想知道一個人的身高離平均身高有多遠。許多算法假設它們的輸入數據是標準化的。

正如您所想象的,有許多算法可以應用於我們的數據來縮放或標準化它。這裏沒有必要一一列舉它們,因爲它們包含在許多其他文本和機器學習庫中。如果您不熟悉這個概念的細節,請查閱前一章中提到的任何書籍。只要記住基本目標——我們希望我們的數據具有相同的scale,以便能夠以一種合理的方式輕鬆地相互比較。在MLlib中,這總是在Vector類型的列上完成。MLlib將遍歷給定列中的所有行(類型爲Vector),然後將這些列中的每個維度視爲自己的特定列。然後,它將在每個維度上分別應用縮放函數或歸一化函數。

一個簡單的例子可能是列中的以下向量:

當我們應用縮放(而不是歸一化)函數時,“3”和“1”將根據這兩個值進行調整,而“2”和“4”將根據彼此進行調整。這通常稱爲組件比較。

25.5.3.StandardScaler特徵縮放

StandardScaler將一組特徵標準化爲均值爲零,標準差爲1。withStd標誌將數據縮放到單位標準差,而withMean標誌(默認爲false)將在縮放數據之前將數據居中。

警告

對稀疏向量居中可能非常昂貴,因爲它通常會將它們轉換爲密集向量,所以在對數據居中之前要小心。

下面是一個使用StandardScaler的例子:

25.5.4.MinMaxScaler

MinMaxScaler 轉換由向量行組成的數據集,將每個特徵調整到一個特定的範圍(由setMin方法和setMax方法設置範圍,通常是 [0,1] )。

 

25.5.5.MaxAbsScaler

MaxAbsScaler通過將每個值除以該特性中的最大絕對值來縮放數據。因此,所有值都在- 1和1之間。該轉換器在處理過程中根本不移位或居中數據,因此不會破壞任何的稀疏性:

25.5.6.ElementwiseProduct元素智能乘積

ElementwiseProduct允許我們將向量中的每個值縮放一個任意值。例如,給定下面的向量和行“1,0.1,-1”,輸出將是“10,1.5,-20”。“縮放向量的維數自然必須與相關列內向量的維數相匹配:

25.5.7.Normalizer

normalizer允許我們使用通過參數“p”設置的幾個冪範數中的一個來縮放多維向量。例如,我們可以使用帶p = 1的曼哈頓範數(或曼哈頓距離),帶p = 2的歐氏範數,等等。曼哈頓距離是一種測量距離的方法,你只能沿着一條軸線(就像曼哈頓的街道)從一個點到另一個點。

下面是使用Normalizer的一個例子:

25.6.處理分類特徵

分類特徵最常見的任務是indexing索引。索引將列中的分類變量轉換爲可以插入機器學習算法的數值變量。雖然這在概念上很簡單,但是要記住一些重要的注意點,這樣Spark才能以穩定和可重複的方式完成此任務。

通常,爲了一致性,我們建議在預處理時重新索引每個分類變量。這對於長期維護您的模型很有幫助,因爲您的編碼實踐可能會隨着時間而改變。

請在公衆號中閱讀本章剩下的內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章