Spark快速大資料分析——機器學習
楔子
《Spark快速大資料分析》學習
11 基於MLlib的機器學習
MLlib是Saprk中提供機器學習函式的庫。它是專門在叢集上並行的情況而設計的。MLlib中包含許多機器學習演算法,可以在Spark支援的所有程式語言中使用。
11.1 概述
MLlib的設計裡面非常簡單:把資料已RDD的形式表示,然後在分散式資料集上呼叫各種演算法。MLlib引入了一些資料型別(例如點和向量),不過歸根結底,MLlib就是RDD上一系列可供呼叫的函式的集合。例如,如果使用MLlib來完成文字分類的任務,只需要按如下操作
1 首先用字串RDD來表示你的訊息
2 執行MLlib中的一個特徵提取演算法把文字資料轉換為數值特徵;該操作返回一個向量RDD。
3 對向量RDD呼叫分類演算法;這步會返回一個模型物件,可以使用該物件對新的資料點進行分類。
4 使用MLlib的評估函式在測試資料集上評估模型
需要注意的是,MLlib中只包含能夠執行在叢集上執行良好的並行演算法,這一點很重要。有些景點的機器學習演算法並沒有包含在其中,就是因為他們不能被並行執行。相反地,一些較新的研究得出的演算法因為使用與叢集,也被包含在MLlib中,例如分散式隨機森林演算法等。這樣的選擇使得MLlib中的每一個演算法都適用於大規模資料集。如果你要在許多小規模資料集生訓練各種機器學習模型,最好還是在各節點上使用單節點的機器學習演算法庫實現。比如可以使用Spark的Map操作在各節點生並行使用。類似地,我們在機器學習流水線中也常常用同一演算法的不同引數對小規模資料集分別訓練,來選出最好的一組引數。在 Spark 中,你可以通過把引數列表傳給 parallelize() 來在不同的節點上分別執行不同的引數,而在每個節點上則使用單節點的機器學習庫來實現。只有當你需要在一個大規模分散式資料集上訓練模型時,MLlib 的優勢才能突顯出來。
11.2 系統要求
首先,你需要為作業系統安裝 gfortran 執行庫。如果 MLlib 警告找不到 gfortran 庫的話,可以按 MLlib 網站(http://spark.apache.org/docs/latest/mllib-guide.html)上說明的步驟處理。其次,如果你要在 Python 中使用 MLlib,你需要安裝 NumPy(http://www.numpy.org/)。如果你的 Python 沒有安裝 NumPy(即你無法使用 import numpy ),最簡單的辦法就是使用 Linux 的包管理工具來安裝 python-numpy包或 numpy 包,或者使用第三方定製的 Python 版本,比如 Anaconda(
11.3 機器學習基礎
機器學習演算法嘗試根據訓練資料(training data)使得表示演算法行為的數學目標最大化,並以此來進行預測或作出決定。機器學習問題分為幾種,包括分類、迴歸、聚類,每種都有不一樣的目標。拿分類(classification)作為一個簡單的例子:分類是基於已經被標記的其他資料點(比如一些已經分別被標記為垃圾郵件或非垃圾郵件的郵件)作為例子來識別一個數據點屬於幾個類別中的哪一種(比如判斷一封郵件是不是垃圾郵件)。
所有的學習演算法都需要定義每個資料點的特徵(feature)集,也就是傳給學習函式的值。舉個例子,對於一封郵件來說,一些特徵可能包括其來源伺服器、提到 free 這個單詞的次數、字型顏色等。在很多情況下,正確地定義特徵才是機器學習中最有挑戰性的部分。例如,在產品推薦的任務中,僅僅加上一個額外的特徵(例如我們意識到推薦給使用者的書籍可能也取決於使用者看過的電影),就有可能極大地改進結果
大多數演算法都只是專為數值特徵(具體來說,就是一個代表各個特徵值的數字向量)定義的,因此提取特徵並轉化為特徵向量是機器學習過程中很重要的一步。例如,在文字分類中(比如垃圾郵件和非垃圾郵件的例子),有好幾個提取文字特徵的方法,比如對各個單詞出現的頻率進行計數。
11.4 資料型別
MLlib包含一些特有的資料型別,他們位於org.apache.spark.mlib或pyspark.mlib內。
-
Vector
一個數學向量。MLlib既支援稠密向量也支援稀疏向量,前者表示向量的每一位都儲存下來,後者則只儲存非零為以節約空間。向量可以通過mlib.linalg.Vectors類創建出來。
-
LabeledPoint
在諸如分類和迴歸這樣的監督式學習演算法中,LabeledPoint用來表示帶標籤的資料點。它包含一個特徵向量與一個標籤(由一個浮點數表示),位置在mlib.regression包中
-
Rating
使用者對一個產品的評分,在mlib.recommendation包中,使用者產品推介
-
各種Model類
每個Model都是訓練演算法的結果,一般有一個predict()方法可以用來對新的資料點或資料點組成的RDD應用模型進行預測
大多數演算法直接操作由 Vector 、 LabeledPoint 或 Rating 物件組成的 RDD。你可以用任意方式創建出這些物件,不過一般來說你需要通過對外部資料進行轉化操作來構建出RDD——例如,通過讀取一個文字檔案或者執行一條 Spark SQL 命令。接下來,使用map() 將你的資料物件轉為 MLlib 的資料型別。
操作向量
作為MLlib最常用的資料型別,Vector類有一些需要注意的地方。
第一,向量有2種:稠密向量和稀疏向量。稠密向量把所有維度的值存放在一個浮點型數陣列中。例如,一個100維的向量會儲存100個雙精度浮點數。相比之下,稀疏向量只把個維度中非0字儲存下來。當最多隻有 10% 的元素為非零元素時,我們通常更傾向於使用稀疏向量(不僅是出於對記憶體使用的考慮,也是出於對速度的考慮)。許多特徵提取技術都會生成非常稀疏的向量,所以這種方式常常是一種很關鍵的優化手段。
第二,建立向量的方式在各種語言中有一些細微的差別。 而在 Java 和 Scala 中,都需要使用 mllib.linalg.
Vectors 類。
//建立稠密向量<1.0,2.0,3.0>;
Vector dense = Vectors.dense(1.0,2.0,3.0);
//
Vector dense2 = Vectors.dense(new double[]{1.0,2.0,3.0});
//建立稀疏向量<1.0,0.0,2.0,0.0>
Vector sparse = Vectors.sparse(4, new int[]{0,2},new double[]{1.0,2.0});
11.5 演算法
11.5.1 特徵提取
mlib.feature包中包含一些用來進行常見特徵轉化的類。這些類中從文字(或其他表示)建立特徵向量的演算法,也對特徵向量正規化和伸縮變換的方法。
TF-IDF
詞頻——逆文件頻率是一種用來從文字文件中生成特徵向量的簡單方法。它為文件中的每個詞計算兩個統計值:一個是詞頻(TF),也就是每個詞在文件中出現的次數,另一個逆文件頻率(IDF),用來衡量一個詞在整個文件語料庫中出現的(逆)頻繁程度。這些值得積,也就是TF*IDF,展示了一個詞與特定文件的相關程式(比如這個詞在某文件中很常見,但在整個語料庫中卻很少見)。
MLlib有兩個演算法可以用來計算TF-IDF: HashingTF 和 IDF ,都在 mllib.feature 包內。HashingTF 從一個文件中計算出給定大小的詞頻向量。為了將詞與向量順序對應起來,它使用了雜湊法(hasing trick)。在類似英語這樣的語言中,有幾十萬個單詞,因此將每個單詞對映到向量中的一個獨立的維度上需要付出很大代價。而 HashingTF 使用每個單詞對所需向量的長度 S 取模得出的雜湊值,把所有單詞對映到一個 0 到 S-1 之間的數字上。由此我們可以保證生成一個 S 維的向量。在實踐中,即使有多個單詞被對映到同一個雜湊值上,演算法依然適用。MLlib 開發者推薦將 S 設定在 2^18到 2^20 之間。
11.5.2 統計
不論是在即時的探索中,還是在機器學習的資料理解中,基本的統計都是資料分析的重要部分。MLlib 通過 mllib.stat.Statistics 類中的方法提供了幾種廣泛使用的統計函式,這些函式可以直接在 RDD 上使用。一些常用的函式如下所列。
-
Statistics.colStats( rdd )
計算由向量組成的 RDD 的統計性綜述,儲存著向量集合中每列的最小值、最大值、平均值和方差。這可以用來在一次執行中獲取豐富的統計資訊。
-
Statistics.corr( rdd, method )
計算由向量組成的 RDD 中的列間的相關矩陣,使用皮爾森相關(Pearson correlation)或斯皮爾曼相關(Spearman correlation)中的一種(method 必須是 pearson 或 spearman
中的一個)。 -
Statistics.corr( rdd1, rdd2, method )
計算由 LabeledPoint 物件組成的 RDD 中每個特徵與標籤的皮爾森獨立性測試(Pearson’s independence test) 結 果。 返 回 一 個 ChiSqTestResult 對 象, 其 中 有 p 值(p-value)、測試統計及每個特徵的自由度。標籤和特徵值必須是分類的(即離散值)。
11.5.3 分類和迴歸
分類與迴歸是監督式學習的兩種主要形式。監督式學習指演算法嘗試使用有標籤的訓練資料(也就是已知結果的資料點)根據物件的特徵預測結果。分類和迴歸的區別在於預測的變數的型別:在分類中,預測出的變數是離散的(也就是一個在有限集中的值,叫作類別);比如,分類可能是將郵件分為垃圾郵件和非垃圾郵件,也有可能是文字所使用的語言。在迴歸中,預測出的變數是連續的(例如根據年齡和體重預測一個人的身高)。
分類和迴歸都會使用 MLlib 中的 LabeledPoint 類。這個類在 mllib.
regression 包中。一個 LabeledPoint 其實就是由一個 label ( label 總是一個 Double 值,不過可以為分類演算法設為離散整數)和一個 features 向量組成。
MLlib 包含多種分類與迴歸的演算法,其中包括簡單的線性演算法以及決策樹和森林演算法。
-
線性迴歸
線性迴歸是常用的方法之一,是指用特徵的線性組合來預測輸出值。MLlib 也支援 L1和 L2的正則的迴歸,通常稱為 Lasso 和 ridge 迴歸。
線性迴歸演算法可以使用的類包括 mllib.regression.LinearRegressionWithSGD 、 LassoWithSGD以及 RidgeRegressionWithSGD 。這遵循了 MLlib 中常用的命名模式,即對於涉及多個演算法的問題,在類名中使用“With”來表明所使用的演算法。這裡,SGD 代表的是隨機梯度下降法。
這些類都有幾個可以用來對演算法進行調優的引數
- numIterations 要執行的迭代次數(預設值: 100 )
- stepSize
梯度下降的步長(預設值: 1.0 )。 - • intercept
是否給資料加上一個干擾特徵或者偏差特徵——也就是一個值始終為 1 的特徵(預設值: false )。 - regParam
Lasso 和 ridge 的正規化引數(預設值: 1.0 )。
呼叫演算法的方式在不同語言中略有不同。在Java和Scala中,你需要建立一個LinearRegressionWithSGD物件,呼叫它的setter方法來設定引數,然後呼叫run()來訓練模型。在Python中,你需要使用類的方法LinearRegressionWithSGD.train(),並對其傳遞鍵值對引數。在這兩種情況中,你都需要傳遞一個由LabeledPoint組成的RDD,
-
邏輯迴歸
邏輯迴歸是一種二元分類方法,用來尋找一個分隔陰性和陽性示例的線性分割平面。在 MLlib 中,它接收一組標籤為 0 或 1 的 LabeledPoint ,返回可以預測新點的分類的LogisticRegressionModel 物件。
-
支援向量機
支援向量機(簡稱 SVM)演算法是另一種使用線性分割平面的二元分類演算法,同樣只預期 0 或者 1 的標籤。通過 SVMWithSGD 類,我們可以訪問這種演算法,它的引數與線性迴歸和邏輯迴歸的引數差不多。返回的 SVMModel 與 LogisticRegressionModel 一樣使用閾值的方式進行預測。
-
樸素貝葉斯
樸素貝葉斯(Naive Bayes)演算法是一種多元分類演算法,它使用基於特徵的線性函式計算將一個點分到各類中的得分。這種演算法通常用於使用 TF-IDF 特徵的文字分類,以及其他一些應用。MLlib 實現了多項樸素貝葉斯演算法,需要非負的頻次(比如詞頻)作為輸入特徵。
在 MLlib 中,你可以通過 mllib.classification.NaiveBayes 類來使用樸素貝葉斯演算法。它支援一個引數 lambda (Python 中是 lambda_ ),用來進行平滑化。你可以對一個由 LabeledPoint組成的 RDD 呼叫樸素貝葉斯演算法,對於 C 個分類,標籤值範圍在 0 至 C-1 之間
15.5 協同過濾推薦
協同過濾是一種根據使用者對各種產品的互動與評分來推薦新產品的推薦系統技術。協同過濾吸引人的地方就在於它只需要輸入一系列使用者 / 產品的互動記錄:無論是“顯式”的互動(例如在購物網站上進行評分)還是“隱式”的(例如使用者訪問了一個產品的頁面但是沒有對產品評分)互動皆可。僅僅根據這些互動,協同過濾演算法就能夠知道哪些產品之間比較相似(因為相同的使用者與它們發生了互動)以及哪些使用者之間比較相似,然後就可以作出新的推薦。
儘管 MLlib 的 API 使用了“使用者”和“產品”的概念,但你也可以將協同過濾用於其他應用場景中,比如在社交網路中推薦使用者,為文章推薦要新增的標籤,為電臺推薦歌曲等。
交替最小二乘
MLlib 中包含交替最小二乘(簡稱 ALS)的一個實現,這是一個協同過濾的常用演算法,可以很好地擴充套件到叢集上。 它位於 mllib.recommendation.ALS 類中
ALS 會為每個使用者和產品都設一個特徵向量,這樣使用者向量與產品向量的點積就接近於他們的得分。它接收下面所列這些引數。
- rank
使用的特徵向量的大小;更大的特徵向量會產生更好的模型,但是也需要花費更大的計算代價(預設值: 10 )。 - iterations
要執行的迭代次數(預設值: 10 )。 - lambda
正則化引數(預設值: 0.01 )。 - alpha
用來在隱式 ALS 中計算置信度的常量(預設值: 1.0 )。 - numUserBlocks , numProductBlocks
切分使用者和產品資料的塊的數目,用來控制並行度;你可以傳遞
-1來讓 MLlib 自動決定(預設行為)
要使用ALS演算法,你需要有一個由 mllib.recommendation.Rating 物件組成的 RDD,其中包含一個使用者ID,一個產品ID,一個評分。實現過程中的一個挑戰是每個ID都需要時一個32的整形值,如果ID是字串或者更大的數字,推薦在ALS中使用ID的雜湊值。還有一種辦法是broadcast() 一張從產品 ID 到整型值的表,來賦給每個產品獨特的 ID
ALS 返回一個 MatrixFactorizationModel 物件來表示結果,可以呼叫 predict() 來對一個由(userID, productID) 對組成的 RDD 進行預測評分。 8 你也可以使用 model.recommendProducts(userId, numProducts) 來為一個給定使用者找到最值得推薦的前 numProduct 個產品。注意,和 MLlib 中的其他模型不同, MatrixFactorizationModel 物件很大,為每個使用者和產品都儲存了一個向量。這樣我們就不能把它存到磁碟上,然後在另一個程式中讀取回來。不過,你可以把模型中生成的特徵向量 RDD,也就是 model.userFeatures 和 model.productFeatures 儲存到分散式檔案系統上。