基於MLlib的機器學習


《Spark快速大數據分析》
11.1 概述

MLlib的設計理念非常簡單:把數據以RDD的形式表示,
然后在分布式數據集上調用各種算法。MLlib引入了一些數據類型,
比如點和向量,不過歸根結底,MLlib就是RDD上一系列可供調用的函數的集合。
比如,如果要用MLlib來完成文本分類的任務,例如識別垃圾郵件,
你只需要按如下步驟操作:
(1) 首先用字符串RDD來表示你的文本數據。
(2) 運行MLlib中的特征提取(feature extraction)算法來把文本數據
    轉換為數值特征(適合機器學習算法處理);該操作會產生一個向量RDD。
(3) 對向量RDD調用分類算法,比如邏輯回歸;這步會產生一個模型對象,
    可以使用該對象來對新的待分類的數據點進行分類。
(4) 使用MLlib的評估函數在測試數據集上評估模型。
 
MLlib中只包含能夠在集群上運行良好的並行算法。
有些經典的機器學習算法並沒有包含在其中,就是因為它們不能並行執行。
一些心的研究得出的算法因為適用於集群,也被包含在MLlib中,例如
分布式隨機森林算法(distributed random forests),K-means||聚類,
交替最小二乘法(alternating least squares)等。
如果要在小規模數據集上訓練各機器學習模型,最好還是在各個節點上
使用單節點的機器學習方法庫實現,例如Weka,SciKit-Learn。
例如可以用Spark的map()操作在各個節點上並行使用。
類似的,我們在機器學習流水線中也常常用同一算法的不同參數對小規模數據集分別訓練,
來選出最好的一組參數。在Spark中,你可以通過把參數列表傳給parelleize()來在不同的
節點上分別運行不同的參數,而在每個節點上則使用單節點的機器學習庫來實現。
只有當你需要在一個大規模分布式數據集上訓練模型時,MLlib的優勢才能突顯出來。
 
 
11.3 機器學習基礎
機器學習算法嘗試根據訓練數據(training data),使得表示算法行為的數學目標最大化,
並以此來進行預測或做出決定。機器學習問題分為幾種,包括分類,回歸,聚類,每種
都有不一樣的目標。以分類(classification)為例來說明,分類是基於已經被標記的
其他數據點(比如一些已經分別被標記為垃圾郵件和非垃圾郵件的郵件)作為例子來識別
哪些尚未被標記的數據屬於已知類型的哪一種。
 
所有的學習算法都需要定義每個數據點的特征集(feature),也就是傳給學習函數的值。
舉個栗子,對於一封郵件來說,一些特征可能包括來源機器,提到free這個單詞的次數,
字體顏色等。在很多情況下,正確定義特征才是機器學習中最有挑戰性的部分。
例如,在產品推薦的任務中,僅僅加上一個額外的特征(例如我們意識到推薦給用戶的書籍
可能取決於用戶看過的電影),就有可能極大的改進推薦效果。
 
大多數算法都只是為數值特征(具體來說,就是一個代表各個特征值的數字向量)定義的,
因此提取特征並轉化為特征向量是機器學習過程中很重要的一步。
例如在文本分類中(比如垃圾郵件和非垃圾郵件的例子),有好幾個提取文本特征的方法,
比如對各個單詞出現的頻率進行計數。
當數據已經成為特征向量的形式后,大多數機器學習算法都會
根據這些向量優化一個定義好的數學函數。例如,某個分類算法可能會在
特征向量的空間中定義出一個平面,使得這個平面能"最好"的分隔垃圾郵件和非垃圾郵件。
這里需要為"最好"給出定義,比如大多數據點都能被正確分類。
算法會在運行結束時返回一個代表學習決定的模型(比如這個選定的平面),而這個模型就
可以用來對新的數據點進行預測。
 
最后,大多數機器學習算法都有多個會影響結果的參數,所以現實中的機器學習流水線
會訓練出多個不同版本的模型,然后分別對其進行評估。
所以通常需要把數據分為"訓練數據集"和"測試數據集",並且之使用前者進行訓練,
這樣就可以使用后者來檢驗模型是否會過度擬合(overfit)了訓練數據。
 
def testLogisticRegressionWithSGD = {
    val spam = sc.textFile("src/main/resources/mllib/spam.txt", 1)
    val normal = sc.textFile("src/main/resources/mllib/normal.txt", 1)
    
    //創建一個HashingTF實例來把郵件文本映射為包含一個10000個特征的向量
    val tf = new HashingTF(numFeatures = 10000)
    //各郵件都被切分為單詞,每個單詞被映射為一個特征
    val spamFeatures = spam.map { email => tf.transform(email.split(" ")) }
    val normalFeatures = normal.map { email => tf.transform(email.split(" ")) }
    
    //創建LabeledPoint數據集分別存放陽性(垃圾郵件)和陰性(正常郵件)的例子
    val positiveExamples = spamFeatures.map { features => LabeledPoint(1, features) }
    val negativeExamples = normalFeatures.map { features => LabeledPoint(0, features) }
    val trainingData = positiveExamples.union(negativeExamples)
    trainingData.cache()
    println(trainingData.toDebugString)
    
    //使用SGD算法運行邏輯回歸
    val model = new LogisticRegressionWithSGD().run(trainingData)
    //以陽性(垃圾郵件)和陰性(正常郵件)的例子分別進行測試
    val posTest = tf.transform("O M G get cheap stuff by sending money to .".split(" "))
    val negTest = tf.transform("hello, i started studying Spark ".split(" "))
    println(s"prediction for positive tset example: ${model.predict(posTest)}")
    println(s"prediction for negitive tset example: ${model.predict(negTest)}")
    
    Thread.sleep(Int.MaxValue)
  }

  

 
11.4 數據類型
MLlib包含一些特有的數據類型,它們位於org.apache.spark.mllib包。
    Vector
        一個數學向量。MLlib既支持稠密向量也支持稀疏向量,前者表示向量
        的每一位都存儲下來,后者則只存儲非零位以節約空間,
    LabeledPoint
        在諸如分類和回歸這樣的監督式學習(supervised learning)算法中,
        LabeledPoint用來表示帶標簽的數據點。它包含一個特征向量和
        一個標簽(由一個浮點數表示)
    Rating
        用戶對一個產品的評分,用於產品推薦。
    各種Model類
        每個Model都是訓練算法的結果,一般有一個predict()方法可以用來對新的數據點或
        數據點組成的RDD應用該模型進行預測。
 
大多數算法直接操作由Vector,LabeledPoint或Rating對象組成的RDD。可以用任意方式來創建
出這些對象,不過一般來說需要通過外部數據進行轉化操作來構建出RDD。
例如,通過讀取一個文本文件或者運行一條SparkSQL命令。
 
 
操作向量
第一,向量有兩種,稠密向量和稀疏向量。稠密向量把所有維度的值存放在一個浮點數
    數組中。例如,一個100維的向量存儲100個雙精度浮點數。
    相比之下,稀疏向量只是把各維度中的非零值存儲下來。當最多只有10%的元素為非零元素時,
    我們通常傾向於使用稀疏向量。許多特征向量提取技術都會生成非常稀疏的向量。
第二,創建向量的方式在各種語言中有一些細微的差別。
 
 
11.5 算法
11.5.1 特征提取
mllib.feature包中包含一些用來進行常見特征轉化的類。
這些類中有從文本(或者其他表示)創建特征向量的算法,
也有特征向量進行正規化和伸縮變換的方法。
 
TF-IDF
詞頻-逆文檔頻率是一種用來從文本文檔中生成特征向量的簡單方法。
它為文檔中的每個詞計算兩個統計值:
一個是詞頻(TF),也就是每個詞在文檔中出現的次數,
另一個是逆文檔頻率(IDF),用來衡量一個詞在整個文檔語料庫中出現的(逆)頻繁程度。
這些值的積,也就是TF * IDF,展示了一個詞與特定文檔的相關程度(比如
這個詞在某個文檔中很常見,但是在整個語料庫中卻很少見)。
MLlib有兩個算法可以用來計算TF-IDF:HashingTF和IDF,都在mllib.feature包內。
HashingTF從一個文檔中計算給定大小的詞頻向量。為了將詞與向量順序對應起來,
它使用了哈希法。在類似英語這樣的語言中,有幾十萬個單詞,
因此將每個單詞映射到向量中的一個獨立的維度上需要付出很大代價。
而HashingTF使用每個單詞對所需向量的長度s取模得出的哈希值,把所有單詞
映射到一個0到s-1直接的數字上。由此我們可以保證生成一個s維的向量。
在實踐中,即使有多個單詞被映射到同一個哈希值上,算法依然適用。
MLlib開發者推薦將s設置在2^18到2^20之間。
HashingTF可以一次只運行與一個文檔中,也可以運行與整個RDD中。
它要求每個"文檔"都使用對象的可迭代序列來表示。
 
當構建好詞頻向量后,就可以使用IDF來計算逆文檔頻率,然后將它們
與詞頻相乘來計算TF-IDF。首先要對IDF對象調用fit()方法來獲取一個IDFModel,
它代表語料庫中的逆文檔頻率。接下來對模型調用transform()來把TF向量轉為IDF向量。
 
1.縮放
大多數機器學習算法都要考慮特征向量中各元素的幅值,並且在特征縮放調整為
平等對待時表現得最好。例如所有的特征平均值為0,標准差為1。當構建好特征向量后,
可以使用MLlib中的StandardScaler類來進行的縮放,同時控制均值和標准差。
需要創建一個StandardScaler,對數據集調用fit()函數來獲取一個StandardScalerModel(
也就是為每一列計算平均值和標准差),然后使用這個模型對象的transform()方法來縮放一個數據集。
 
2.正規化
在一些情況下,在准備輸入數據時,把向量正規化為長度1也是有用的。
是用Normalizer類可以實現,只要使用Normalizer.transform(rdd)就可以了。
默認情況下,Normalizer使用L^2范式(也即是歐幾里德距離),不過可以給Normalizer
傳遞一個參數p來使用L^2范式。
 
11.5.2 統計
 
11.5.3 分類與回歸
分類與回歸是監督式學習的兩種主要形式。監督式學習指算法嘗試使用有
標簽的訓練數據(也就是已知結果的數據點)根據對象的特征預測結果。
分類和回歸的區別在於預測的變量的類型:
在分類中,預測出的變量是離散的;也就是一個在有限集中的值,叫做類別,
比如分類可能是將郵件分為垃圾郵件和非垃圾郵件,也有可能是文本所使用的語言。
在回歸中,預測出的變量是連續的,例如根據年齡和體重預測一個人的身高。
 
分類和回歸都會使用MLlib的LabeledPoint類。一個LabeledPoint對象其實就是由
一個label(label總是一個Double值,不過可以分為分類算法設為離散整數)和
一個features向量組成。
 
對於一個二元分類,MLlib預期的標簽為0或1。在某些情況下可能會使用-1和1,
但是這回導致錯誤的結果。對於多元分類,MLlib預期標簽范圍是從0到C-1,
其中C表示類別數量。
MLlib包含多種分類與回歸的算法,其中包含簡單的線性算法以及決策樹和森林算法。
 
1 線性回歸
線性回歸是回歸中最常用的方法之一,是指用特征的線性組合來預測輸出值。
MLlib也支持L^1和L^2的正則的回歸,通常稱為Lasso和ridge回歸。
線性回歸算法可以使用的類包括mllib.regression.LinearRegressionWithSGD,
LassoWithSGD以及RidgeRegressionWithSGD。這遵循了MLlib中常用的命名模式,
即對於涉及多個算法的問題,在類名中使用With來表明所使用的算法。
SGD代表的是隨即梯度下降法。
 
有幾個可以對算法進行調優的參數:
    numIterations
        要運行的迭代次數(默認值100)
    stepSize
        梯度下降的步長(默認值1.0)
    intercept
        是否給數據加上一個干擾特征或者偏差特征--也就是一個值始終為1的特征(默認值false)
    regParam
        Lasso和ridge的正規化參數(默認值1.0)
 
一旦訓練完成,所有語言返回的LinearRegressionModel都會包含一個predict()函數,
可以用來對單個特征向量預測一個值。RidgeRegressionWithSGD和LassonWithSGD的
行為類似,並且會返回一個類似的模型類。
 
2 邏輯回歸
邏輯回歸是一種二元分類方法,用來尋找一個分隔陰性和陽性示例的線性分割平面。
在MLlib中,它接收一組標簽為0或1的LabeledPoint,
返回可以預測新點的分類的LogisticRegressionModel對象。
 
邏輯回歸算法的API和線性回歸相似。兩個算法:SGD,LBFGS。
這兩個算法中得出的LogisticRegressionModel可以為每個點求出一個
在0到1之間的得分,之后會基於一個閾值返回0或1:默認情況下,對於0.5
它會返回1。可以通過setThreshold()改變閾值,也可以通過clearThreshold()
去除閾值,這樣的話predict()就會返回原始得分。對於陰性陽性示例各半的均衡數據集,
推薦保留0.5作為閾值。對於不均衡的數據集,可以通過提升閾值來
減少假陽性數據的數據量(也就是提高精准率,但是也降低了召回率),
也可以通過降低閾值來減少假陰性數據的數量。
 
3 支持向量機
支持向量機(SVM)算法是另一種使用線性分割平面的二元分類算法,
同樣只預期0或1的標簽。通過SVMWithSGD類,可以訪問這種算法,
它的參數與線性回歸和邏輯回歸的參數差不多。
返回的SVMModel與LogisticRegressionModel一樣使用閾值的方式進行預測。
 
4 朴素貝葉斯
朴素貝葉斯(Naive Bayes)算法是一種多元分類算法,它使用基於
特征的線性函數計算將一個點分到分類中的得分。這種算法通常用於
使用TF-IDF特征的文本分類,以及其他一些應用。
MLlib實現了多項朴素貝葉斯算法,需要非負的頻次(比如詞頻)作為輸入特征。
在MLlib中,可以通過mllib.classification.NaiveBayes類來使用朴素貝葉斯算法。
它支持一個參數(lamda),用來進行平滑化。可以對一個由LabeledPoint組成的RDD
調用朴素貝葉斯算法,對於C個分類,標簽值在0到C-1之間。
返回的NavieBayesModel可以使用predict()預測對某個點最合適的分類,也可以訪問
訓練好的模型的兩個參數:各特征與各分類的可能性矩陣theta(對於C個分類和D個特征
的情況,矩陣大小為C*D),以及表示先驗概率的C維向量pi。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM