Spark MLlib 机器学习
本章導(dǎo)讀
機(jī)器學(xué)習(xí)(machine learning, ML)是一門涉及概率論、統(tǒng)計(jì)學(xué)、逼近論、凸分析、算法復(fù)雜度理論等多領(lǐng)域的交叉學(xué)科。ML專注于研究計(jì)算機(jī)模擬或?qū)崿F(xiàn)人類的學(xué)習(xí)行為,以獲取新知識、新技能,并重組已學(xué)習(xí)的知識結(jié)構(gòu)使之不斷改善自身。
MLlib是Spark提供的可擴(kuò)展的機(jī)器學(xué)習(xí)庫。MLlib已經(jīng)集成了大量機(jī)器學(xué)習(xí)的算法,由于MLlib涉及的算法眾多,筆者只對部分算法進(jìn)行了分析,其余算法只是簡單列出公式,讀者如果想要對公式進(jìn)行推理,需要自己尋找有關(guān)概率論、數(shù)理統(tǒng)計(jì)、數(shù)理分析等方面的專門著作。本章更側(cè)重于機(jī)器學(xué)習(xí)API的使用,基本能夠滿足大多數(shù)讀者的需要。
1.?機(jī)器學(xué)習(xí)概率
機(jī)器學(xué)習(xí)也屬于人工智能的范疇,該領(lǐng)域主要研究的對象是人工智能,尤其是如何在經(jīng)驗(yàn)學(xué)習(xí)中改善具體算法。機(jī)器學(xué)習(xí)是人工智能研究較為年輕的分支,它的發(fā)展過程大致可分為如下4個(gè)階段:
- ?第一階段:20世紀(jì)50年代中葉至60年代中葉,屬于熱烈時(shí)期。
- 第二階段:20世紀(jì)60年代中葉至70年代中葉,稱為冷靜時(shí)期。
- 第三階段:20世紀(jì)70年代中葉至80年代中葉,稱為復(fù)興時(shí)期。
- 第四階段:從1986年開始至今。
(1)?機(jī)器學(xué)習(xí)的組成
機(jī)器學(xué)習(xí)的基本結(jié)構(gòu)由環(huán)境、知識庫和執(zhí)行部分三部分組成。環(huán)境向?qū)W習(xí)部分(屬于知識庫的一部分)提供某些信息,學(xué)習(xí)部分利用這些信息修改知識庫,以增進(jìn)執(zhí)行部分完成任務(wù)的效能,執(zhí)行部分根據(jù)知識庫完成任務(wù),同時(shí)把獲得的信息反饋給學(xué)習(xí)部分。
(2)?學(xué)習(xí)策略
學(xué)習(xí)策略是指機(jī)器學(xué)習(xí)過程中所采用的推理策略。學(xué)習(xí)系統(tǒng)一般由學(xué)習(xí)和環(huán)境兩部分組成。環(huán)境(如書本或教師)提供信息,學(xué)習(xí)部分則實(shí)現(xiàn)信息轉(zhuǎn)換、存儲,并從中獲取有用的信息。學(xué)習(xí)過程中,學(xué)生(學(xué)習(xí)部分)使用的推理越少,他對教師(環(huán)境)的依賴就越大,教師的負(fù)擔(dān)也就越重。根據(jù)學(xué)生實(shí)現(xiàn)信息轉(zhuǎn)換所需推理的多少和難易程度,以從簡單到復(fù)雜,從少到多的次序可以將學(xué)習(xí)策略分為以下6種基本類型:
- 機(jī)械學(xué)習(xí)(rote learning):學(xué)習(xí)者不需要任何推理或轉(zhuǎn)換,直接獲取環(huán)境所提供的信息。屬于此類的如塞繆爾的跳棋程序。
- 示教學(xué)習(xí)(learning from instruction):學(xué)習(xí)者從環(huán)境獲取信息,把知識轉(zhuǎn)換成內(nèi)部可使用的表示形式,并將新知識和原有知識有機(jī)地合為一體。此種學(xué)習(xí)策略需要學(xué)生有一定程度的推理能力,但環(huán)境仍要做大量的工作。典型應(yīng)用是FOO程序。
- 演繹學(xué)習(xí)(learning by deduction):學(xué)習(xí)者通過推理獲取有用的知識。典型應(yīng)用是宏操作(macro-operation)學(xué)習(xí)。
- 類比學(xué)習(xí)(learning by analogy):學(xué)習(xí)者根據(jù)兩個(gè)不同領(lǐng)域(源域、目標(biāo)域)中的知識相似性,通過類比,從源域的知識推導(dǎo)出目標(biāo)域的相應(yīng)知識。此類應(yīng)用如盧瑟福類比。
- 基于解釋的學(xué)習(xí)(explanation-based learning, EBL):學(xué)習(xí)者根據(jù)教師提供的目標(biāo)概念和此概念的例子、領(lǐng)域理論及可操作準(zhǔn)則,首先給出解釋說明為什么該例子滿足目標(biāo)概念,然后將解釋推廣未目標(biāo)概念的一個(gè)滿足可操作準(zhǔn)則的充分條件。著名的EBL系統(tǒng)由迪喬恩(G.DeJong)的GENESIS等。
- 歸納學(xué)習(xí)(learning from induction):由環(huán)境提供某概念的一些實(shí)例或反例,讓學(xué)習(xí)者通過歸納推理得出該概念的一般描述。歸納學(xué)習(xí)是最基本的,發(fā)展也較為成熟的學(xué)習(xí)方法,在人工智能領(lǐng)域中已得到廣泛的研究和應(yīng)用。
學(xué)習(xí)策略還可以從所獲取知識的表示形式、應(yīng)用領(lǐng)域等維度分類。
(3)?應(yīng)用領(lǐng)域
目前,機(jī)器學(xué)習(xí)廣泛應(yīng)用于數(shù)據(jù)挖掘、計(jì)算機(jī)視覺、自然語言處理、生物特征識別、搜索引擎、醫(yī)學(xué)診斷、檢測信用卡欺詐、證券市場分析、DNA序列測序、語音和手寫識別、戰(zhàn)略游戲和機(jī)器人等領(lǐng)域。
2.?Spark MLlib總體設(shè)計(jì)
MLlib(machine learning library)是Spark提供的可擴(kuò)展的機(jī)器學(xué)習(xí)庫。MLlib中已經(jīng)包含了一些通用的學(xué)習(xí)算法和工具,如:分類、回歸、聚類、協(xié)同過濾、降維以及底層的優(yōu)化原語等算法和工具。
MLlib提供的API主要分為以下兩類:
- spark.mllib包中提供的主要API。
- spark.ml包中提供的構(gòu)建機(jī)器學(xué)習(xí)工作流的高層次的API。
3.?數(shù)據(jù)類型
MLlib支持存儲在一臺機(jī)器上的局部向量和矩陣以及由一個(gè)或多個(gè)RDD支持的分布式矩陣。局部向量和局部矩陣是提供公共接口的簡單數(shù)據(jù)模型。Breeze和jblas提供了底層的線性代數(shù)運(yùn)算。Breeze提供了一組線性代數(shù)和數(shù)字計(jì)算的庫,具體信息訪問http://www.scalanlp.org/。jblas提供了使用Java開發(fā)的線性代數(shù)庫,具體信息訪問http://jblas.org/。
3.1?局部向量
MLlib支持兩種局部向量類型:密集向量(dense)和稀疏向量(sparse)。密集向量由double類型的數(shù)組支持,而稀疏向量則由兩個(gè)平行數(shù)組支持。例如,向量(1.0,0.0,3.0)由密集向量表示的格式為[1.0,0.0,3.0],由稀疏向量表示的格式為(3,[0,2],[1.0,3.0])。
注意:這里對稀疏向量做些解釋。3是向量(1.0,0.0,3.0)的長度,除去0值外,其他兩個(gè)值的索引和值分別構(gòu)成了數(shù)組[0,2]和數(shù)組[1.0,3.0]。
有關(guān)向量的類如圖所示。
Vector是所有局部向量的基類,Dense-Vector和SparseVector都是Vector的具體實(shí)現(xiàn)。
Spark官方推薦使用Vectors中實(shí)現(xiàn)的工廠方法創(chuàng)建局部向量,就像下面這樣:
import org.apache.spark.mllib.linalg.{Vector, Vectors} //創(chuàng)建密集向量(1.0, 0.0, 3.0) val dv: Vector = Vectors.dense(1.0, 0.0, 3.0) //給向量(1.0, 0.0, 3.0)創(chuàng)建疏向量 val svl: Vector= Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)) //通過指定非0的項(xiàng)目,創(chuàng)建稀疏向量(1.0, 0.0, 3.0) val sv2: Vector = Vectors.sparse(3, Seq((0, 1.0), (2, 3.0)))注意: Scala默認(rèn)會導(dǎo)入scala.collection.immutable.Vector,所以必須顯式導(dǎo)入org.apache.spark.mllib.linalg.Vector才能使用MLlib才能使用MLlib提供的Vector。
上面例子中以數(shù)組為參數(shù),調(diào)用Vectors的sparse接口,見如下代碼。使用Seq創(chuàng)建稀疏向量,其本質(zhì)依然是使用數(shù)組,見如下代碼。
3.2?標(biāo)記點(diǎn)
標(biāo)記點(diǎn)是將密集向量或者稀疏向量與應(yīng)答標(biāo)簽相關(guān)聯(lián)。在MLlib中,標(biāo)記點(diǎn)用于監(jiān)督學(xué)習(xí)算法。MLlib使用double類型存儲標(biāo)簽,所以我們能在回歸和分類中使用標(biāo)記點(diǎn)。如果只有兩種分類,可以使用二分法,一個(gè)標(biāo)簽要么是1.0,要么是0.0。如果有很多分類,標(biāo)簽應(yīng)該從零開始:0、1、2....
標(biāo)記點(diǎn)由樣例類LabeledPoint來表示,其使用方式如下。
import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.regression.LabeledPoint //使用標(biāo)簽1.0和一個(gè)密集向量創(chuàng)建一個(gè)標(biāo)記點(diǎn) val pos = LabeledPoint(1.0, Vectors.dense(1.0, 0.0, 3.0)) //使用標(biāo)簽0.0和一個(gè)疏向量創(chuàng)建一個(gè)標(biāo)記點(diǎn) val neg = LabeledPoint(0.0, Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)))用稀疏的訓(xùn)練數(shù)據(jù)做練習(xí)是很常見的,好在MLlib支持讀取存儲在LIBSVM格式中的訓(xùn)練例子。LIBSVM格式是一種每一行表示一個(gè)標(biāo)簽稀疏特征向量的文本格式,其格式如下:
label index1:value1 index2:value2 ...LIBSVM是林智仁教授等開發(fā)設(shè)計(jì)的一個(gè)簡單、易用和快速有效的SVM模式識別與回歸的軟件包。MLlib已經(jīng)提供了MLUtils.loadLibSVMFile方法讀取存儲在LIBSVM格式文本文件中的訓(xùn)練數(shù)據(jù),見如下代碼:
import org.apache.spark.mllib.regression.LabeledPoint import org.apache.spark.mllib.util.MLUtils import org.apache.spark.rdd.RDDval examples: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")3.3?局部矩陣
MLlib支持?jǐn)?shù)據(jù)存儲在單個(gè)double類型數(shù)組的密矩陣。先來看這樣一個(gè)矩陣:
這個(gè)矩陣是如何存儲的?它只是存儲到一維數(shù)組[1.0, 3.0, 5.0, 2.0, 4.0, 6.0],這個(gè)矩陣的尺寸是3*2,即3行2列。
有關(guān)局部矩陣的類如下圖所示。局部矩陣的基類是Matrix,目前有一個(gè)實(shí)現(xiàn)類DenseMatrix。Spark官方推薦使用Matrices中實(shí)現(xiàn)的工廠方法創(chuàng)建局部矩陣,例如:
import org.apache.spark.mllib.linalg.{Matrix, Matrices} //創(chuàng)建密矩陣((1.0,2.0),(3.0, 4.0),(5.0, 6.0)) val dm: Matrix = Matrices.dense(3, 2, Array(1.0, 3.0, 5.0, 2.0, 4.0, 6.0))3.4?分布式矩陣
?分布式矩陣分布式地存儲在一個(gè)或者多個(gè)RDD中。如何存儲數(shù)據(jù)量很大的分布式矩陣?最重要的在于選擇一個(gè)正確的格式。如果將分布式矩陣轉(zhuǎn)換為不同格式,可能需要全局的shuffle,成本非常昂貴。
有關(guān)分布式矩陣的類如圖所示:
迄今為止,MLlib已經(jīng)實(shí)現(xiàn)了4種類型的分布式矩陣:
- RowMatrix:最基本的分布式矩陣類型,是面向行且行索引無意義的分布式矩陣。RowMatrix的行實(shí)際是多個(gè)局部向量的RDD,列受限于integer的范圍大小。RowMatrix適用于列數(shù)不大以便單個(gè)局部向量可以合理地傳遞給Driver,也能在單個(gè)節(jié)點(diǎn)上存儲和操作的情況。
下面展示了可以使用RDD[Vector]實(shí)例來構(gòu)建RowMatrix的例子。
import org.apache.spark.mllib.linalg.Vector import org.apache.spark.mllib.linalg.distributed.RowMatrix val rows: RDD[Vector] = ... val mat: RowMatrix = new RowMatrix(rows) val m = mat.numRows() val n = mat.numCols()- IndexedRowMatrix:與RowMatrix類似,但卻面向索引的分布式矩陣。IndexedRowMatrix常用于識別行或者用于執(zhí)行連接操作。可以使用RDD[IndexedRow]實(shí)例創(chuàng)建IndexedRowMatrix。IndexedRow的實(shí)現(xiàn)如下:
通過刪除IndexedRowMatrix的行索引,可以將IndexedRowMatrix轉(zhuǎn)換為RowMatrix。下面的例子演示了如何使用IndexedRowMatrix。
import org.apache.spark.mllib.linalg.distributed.{IndexedRow, IndexedRowMatrix, RowEntry} val rows: RDD[IndexedRow] = ... val mat: IndexedRowMatrix = new IndexedRowMatrix(rows) val m = mat.numRows() val n = mat.numCols() val rowMat: RowMatrix = mat.toRowMatrix()- CoordinateMatrix:使用坐標(biāo)列表(COO)格式存儲的分布式矩陣。支持CoordinateMatrix的RDD實(shí)際是(i: Long,j: Long, value: Double)這樣的三元組,i是行索引,j是列索引,value是實(shí)際存儲的值。CoordinateMatrix適用于行和列都很大且矩陣很稀疏的情況。
可以使用RDD[MatrixEntry]實(shí)例創(chuàng)建CoordinateMatrix。MatrixEntry的實(shí)現(xiàn)如下。
@Experimental case class MatrixEntry(i: Long, j: Long, value: Double)通過調(diào)用CoordinateMatrix的toIndexedRowMatrix方法,可以將CoordinateMatrix轉(zhuǎn)換為IndexedRowMatrix。下面的例子演示了CoordinateMatrix的使用。
import org.apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry} val entries: RDD[MatriEntry] = ... val mat: CoordinateMatrix = new CoordinateMatrix(entries) val m = mat.numRows() val n = mat.numCols() val indexedRowMatrix = mat.toIndexedRowMatrix()- BlockMatrix:由RDD[MatrixBlock]支持的分布式矩陣。MatrixBlock實(shí)際是((Int, Int), Matrix)這樣的二元組,(Int, Int)是Block的索引,Matrix是記錄塊大小的子矩陣。BlockMatrix支持與其他BlockMatrix的add和multiply,還提供validate方法用于校驗(yàn)當(dāng)前BlockMatrix是否恰當(dāng)構(gòu)建。
通過調(diào)用IndexedRowRowMatrix或者CoordinateMatrix的toBlockMatrix方法,可以方便轉(zhuǎn)換為BlockMatrix。toBlockMatrix方法創(chuàng)建的Block的默認(rèn)大小是1024 x 1024。可以使用toBlockMatrix(rowsPerBlock,colsPerBlock)方法改變Block的大小。下面的例子演示了BlockMatrix的使用。
import org.apache.spark.mllib.linalg.distributed.{BlockMatrix, CoordinateMatrix, MatrixEntry} val entries: RDD[MatrixEntry] = ... val coordMat: CoordinateMatrix = new CoordinateMatrix(entries) val matA: BlockMatrix = coordMat.toBlockMatrix().cache() matA.validate() val ata = matA.transpose.multiply(matA)注意:由于MLlib會緩存矩陣的大小,所以支持分布式矩陣的RDD必須要有明確的類型,否則會導(dǎo)致出錯(cuò)。
4.?基礎(chǔ)統(tǒng)計(jì)
MLlib提供了很多統(tǒng)計(jì)方法,包括摘要統(tǒng)計(jì)、相關(guān)統(tǒng)計(jì)、分層抽樣、假設(shè)校驗(yàn)、隨機(jī)數(shù)生成等。這些都涉及統(tǒng)計(jì)學(xué)、概率論的專業(yè)知識。
4.1?摘要統(tǒng)計(jì)
調(diào)用Statistics類的colStats方法,可以獲得RDD[Vector]的列的摘要統(tǒng)計(jì)。colStats方法返回了MultivariateStatisticalSummary對象,MultivariateStatisticalSummary對象包含了列的最大值、最小值、平均值、方差、非零元素的數(shù)量以及總數(shù)。下面的例子演示了如何使用colStats。
import org.apache.spark.mllib.linalg.Vector import org.apache.spark.stat.{MultivariateStatisticalSummary, Statistics} val observations: RDD[Vector] = ... val summary: MultivariateStatisticalSummary = Statistics.colStats(observations) println(summary.mean) //每個(gè)列值組成的密集向量 println(summary.variance) //列向量方差 println(summary.numNonzeros) //每個(gè)列的非零值個(gè)數(shù)?colStats實(shí)際使用了RowMatrix的computeColumnSummaryStatistics方法,見代碼如下:
4.2?相關(guān)統(tǒng)計(jì)
計(jì)算兩個(gè)序列之間的相關(guān)性是統(tǒng)計(jì)中通用的操作。MLlib提供了計(jì)算多個(gè)序列之間相關(guān)統(tǒng)計(jì)的靈活性。目前支持的關(guān)聯(lián)方法運(yùn)用了皮爾森相關(guān)系數(shù)(Pearson correlation coefficient)和斯皮爾森相關(guān)系統(tǒng)(Spearman's rank correlation coefficient)。
1.?皮爾森相關(guān)系數(shù)
皮爾森相關(guān)系數(shù)也稱為皮爾森積矩相關(guān)系數(shù)(Pearson product-moment correlation coefficient),是一種線性相關(guān)系數(shù)。皮爾森相關(guān)系數(shù)是用來反映兩個(gè)變量線性相關(guān)程度的統(tǒng)計(jì)量。
相關(guān)系數(shù)用r表示,其中n為樣本量,xi,yi,sx,sy?分別為兩個(gè)變量的觀測值和均值。r描述的是兩個(gè)變量間線性相關(guān)強(qiáng)弱的程度。r的取值在-1與+1之間,若r>0,表明兩個(gè)變量是正相關(guān),即一個(gè)變量的值越大,另一個(gè)變量的值也會越大;若r<0,表明兩個(gè)變量是負(fù)相關(guān),即一個(gè)變量的值越大另一個(gè)變量的值反而會越小。r的絕對值越大表明相關(guān)性越強(qiáng),要注意的是這里并不存在因果關(guān)系。若r=0,表明兩個(gè)變量間不是線性相關(guān),但有可能是其他方式的相關(guān)(比如曲線方式)。
2.?斯皮爾森秩相關(guān)系數(shù)
斯皮爾森秩相關(guān)系數(shù)也稱為Spearman的p,是由Charles Spearman命名的,一般用希臘字母ps(rho)或rs表示。Spearman秩相關(guān)系數(shù)是一種無參數(shù)(與分布無關(guān))的校驗(yàn)方法,用于度量變量之間聯(lián)系的強(qiáng)弱。在沒有重復(fù)數(shù)據(jù)的情況下,如果一個(gè)變量是另外一個(gè)變量的嚴(yán)格單調(diào)函數(shù),則Spearman秩相關(guān)系數(shù)就是+1或-1,稱變量完全Spearman秩相關(guān)。注意和Pearson完全相關(guān)的區(qū)別,只有當(dāng)兩變量存在線性關(guān)系時(shí),Pearson相關(guān)系數(shù)才為+1或-1。
Spearman秩相關(guān)系數(shù)為:
Statistics提供了計(jì)算序列之間相關(guān)性的方法,默認(rèn)情況下使用皮爾森相關(guān)系數(shù),使用方法如下:
import org.apache.spark.SparkContext import org.apache.spark.mllib.linalg._ import org.apache.spark.mllib.stat.Statisticsval sc: SparkContext = ... val seriesX: RDD[Double] = ... //a series val seriesY: RDD[Double] = ... //和seriesX必須有相同的分區(qū)和基數(shù) val correlation:Double = Statistics.corr(seriesX, seriesY, "pearson") val data: RDD[Vector] = ... //每個(gè)向量必須是行,不能是列 val correlMatrix: Matrix = Statistics.corr(data, "pearson")Statistics中相關(guān)性的實(shí)現(xiàn),見代碼如下:
其實(shí)質(zhì)是代理了Correlations,Correlations中相關(guān)性的實(shí)現(xiàn)見代碼如下:
4.3?分層抽樣
分層抽樣(Stratified sampling)是先將總體按某種特征分為若干次級(層),然后再從每一層內(nèi)進(jìn)行獨(dú)立取樣,組成一個(gè)樣本的統(tǒng)計(jì)學(xué)計(jì)算方法。為了對分層抽樣有更直觀的感受,請看下面的例子:
某市現(xiàn)有機(jī)動車共1萬輛,其中大巴車500輛,小轎車6000輛,中拔車1000輛,越野車2000輛,工程車500輛。現(xiàn)在要了解這些車輛的使用年限,決定采用分層抽樣方式抽取100個(gè)樣本。按照車輛占比,各類車輛的抽樣數(shù)量分別為5,60,10,20,5.
摘要統(tǒng)計(jì)和相關(guān)統(tǒng)計(jì)都集成Statistics中,而分層抽樣只需要調(diào)用RDD[(K,V)]的sampleByKey和sampleByKeyExact即可。為了分層抽樣,其中的鍵可以被認(rèn)為是標(biāo)簽,值是具體的屬性。sampleByKey方法采用擲硬幣的方式來決定是否將一個(gè)觀測值作為采樣,因此需要一個(gè)預(yù)期大小的樣本數(shù)據(jù)。sampleByKeyExact則需要更多更有效的資源,但是樣本數(shù)據(jù)的大小是確定的。sampleByKeyExact方法允許用戶采用符合[fk * nk] V k ∈ K,其中fk是鍵k的函數(shù),nk是RDD[(K,V)]中鍵為k的(K,V)對,K是鍵的集合。下例演示了如何使用分層抽樣。
import org.apache.spark.SparkContext import org.apache.spark.SparkContext import org.apache.spark.rdd.PairRDDFunctionsval sc: SparkContext = ... val data = ... //an RDD[(K,V)] of any key value pairs val fractions: Map[K. Double] = ... //specify the exact fraction desired from each key val exactSample = data.sampleByKeyExact(withReplacement = false, fractions)4.4?假設(shè)校驗(yàn)
假設(shè)校驗(yàn)(hypothesis testing)?是數(shù)理統(tǒng)計(jì)學(xué)中根據(jù)一定假設(shè)條件由樣本推斷總體的一種方法。
如果對總體的某種假設(shè)是真實(shí)的,那么不利于或不能支持這一假設(shè)的事件A(小概率事件)在一次試驗(yàn)中幾乎不可能發(fā)生;要是在一次試驗(yàn)中A竟然發(fā)生了,就有理由懷疑該假設(shè)的真實(shí)性,拒絕這一假設(shè)。小概率原理可以用圖表示。
H0表示原假設(shè),H1表示備選假設(shè)。常見的假設(shè)校驗(yàn)有如下幾種:
- 雙邊校驗(yàn):H0:u = u0,H1:u=/u0
- 右側(cè)單邊校驗(yàn):H0:u<=u0,H1:u>u0
- 左側(cè)單邊校驗(yàn):H0:u>=u0,H1:u<u0
假設(shè)校驗(yàn)是一個(gè)強(qiáng)大的工具,無論結(jié)果是否偶然的,都可以決定結(jié)果是否具有統(tǒng)計(jì)特征。MLlib目前支持皮爾森卡方測試。輸入數(shù)據(jù)的類型決定了是做卡方適合度檢測還是獨(dú)立性檢測。卡方適合度檢測的輸入數(shù)據(jù)類型應(yīng)當(dāng)是向量,而卡方獨(dú)立性檢測需要的數(shù)據(jù)類型是矩陣。RDD[LabeledPoint]可以作為卡方檢測的輸入類型。下列演示了如何使用假設(shè)校驗(yàn)。
import org.apache.spark.SparkContext import org.apache.spark.mllib.linalg._ import org.apache.spark.mllib.regression.LabeledPoint import org.apache.spark.mllib.stat.Statistics._val sc: SparkContext = ... val vec: Vector = ... //事件的頻率組成的vector val goodnessOfFitTestResult = Statistics.chiSqTest(vec) println(goodnessOfFitTestResult) val mat: Matrix = ... //偶然性matrix val independenceTestResult = Statistics.chiSqTest(mat) println(independenceTestResult) val obs:RDD[LabeledPoint] = ... //(feature, label) pairs val featureTestResults: Arra[ChiSqTestResult] = Statistics.chiSqTest(obs) var i = 1 featureTestResults.foreach{ result =>println(s"Column $i:\n result")i += 1 } //summary of the test4.5?隨機(jī)數(shù)生成
隨機(jī)數(shù)可以看做隨機(jī)變量,什么是隨機(jī)變量?將一枚質(zhì)地均勻的硬幣拋擲3次,記錄它的結(jié)果,有:
其中u代表正面朝上,d代表反面朝上,整個(gè)集合Ω是拋擲3次硬幣的樣本空間。正面朝上的次數(shù)可能是0,1,2,3。由于樣本空間Ω中的結(jié)果都是隨機(jī)發(fā)生的,所以出現(xiàn)正面的次數(shù)X是隨機(jī)的,X即為隨機(jī)變量。如果拋擲硬幣,直到出現(xiàn)正面的拋擲次數(shù)為Y,那么Y的取值可能是0,1,2,3...。如果隨機(jī)變量的取值是有限的(比如X)或者是可列的(比如Y),那么就稱為離散隨機(jī)變量。
剛才說的拋擲3次硬幣的情況下,使用P(X =?取值)的方式表達(dá)每種取值的概率,我們不難得出:
如果樣本空間上隨機(jī)變量的取值用x1,x2,x3,....表示,那么存在滿足p(x1) = P(X = xi)和Σip(xi) = 1?的函數(shù)p。這個(gè)函數(shù)p稱為隨機(jī)變量X的概率質(zhì)量函數(shù)或者頻率函數(shù)。
如果X取值累積某個(gè)范圍的值,那么其累積分布函數(shù)定義如下:
累積分布函數(shù)滿足:
同一樣本空間上兩個(gè)離散隨機(jī)變量X和Y的可能取值分別為x1,x2,x3,...和y1,y2,y3,...,如果對所有i和j,滿足:
則X和Y是獨(dú)立的。將此定義推廣到兩個(gè)以上離散隨機(jī)變量的情形,如果對所有i, j?和?k,滿足:
則X、Y和Z是相互獨(dú)立的。
剛才所說的X、Y的取值都是離散的,還有一種情況下取值是連續(xù)的。以人的壽命為例,可以是任意的正實(shí)數(shù)值。與頻率函數(shù)相對的是密度函數(shù)?(x)。?(x)有這些性質(zhì):?(x)?≥ 0,?分段連續(xù)且∫∞-∞?(x)dx = 1。如果X是具有密度函數(shù)?的隨機(jī)變量,那么對于任意的a < b,X落在區(qū)間(a, b)上的概率是密度函數(shù)從a到b的下方面積:
隨機(jī)數(shù)生成對于隨機(jī)算法、隨機(jī)協(xié)議和隨機(jī)性能測試都很有用。MLlib支持均勻分布、標(biāo)準(zhǔn)正態(tài)分布、泊松分布等生成隨機(jī)RDD。
MLlib有關(guān)隨機(jī)數(shù)的類如圖所示:
以泊松分布為例,先看看它的數(shù)學(xué)定義。參數(shù)為λ(λ>0)的泊松頻率函數(shù)是
當(dāng)λ = 0.1、1、5、10時(shí)的泊松分布如圖所示:
RandomRDDs提供了工廠方法創(chuàng)建RandomRDD和RandomVectorRDD。下面的例子中生成了一個(gè)包含100萬個(gè)double類型隨機(jī)數(shù)的RDD[double],其值符合標(biāo)準(zhǔn)正太分布N(0,1),分布于10個(gè)分區(qū),然后將其映射到N(1, 4)。
import org.apache.spark.SparkContext import org.apache.spark.mllib.random.RandomRDDs._val sc: SparkContext = ... val u = normalRDD(sc, 1000000L, 10) val v = u.map(x => 1.0 + 2.0 * x)5. 分類和回歸
MLlib支持多種多樣的分析方法,例如,二元分類、多元分類和回歸。表11-1列出了各類問題的支持算法。
5.1?數(shù)學(xué)公式
許多標(biāo)準(zhǔn)的機(jī)器學(xué)習(xí)方法都可以配制成凸優(yōu)化問題,即找到一個(gè)極小的凸函數(shù)?依賴于一個(gè)d項(xiàng)的可變向量w。形式上,我們可以寫為優(yōu)化問題minwεR d?(w),其中所述目標(biāo)函數(shù)的形式為:
這里的向量xi?ε Rd?是訓(xùn)練數(shù)據(jù),1?≤?i?≤ n?并且yi?ε??Rd?是想要預(yù)測數(shù)據(jù)的相應(yīng)的標(biāo)簽。如果L(w; xi, yi)能表示為?wτX和y的函數(shù),我們就說這個(gè)方法是線性的。幾個(gè)MLlib的分類和回歸算法都屬于這一類,并在這里討論。
目標(biāo)函數(shù)?有兩個(gè)部分:控制該模型的復(fù)雜的正則化部分和用于在訓(xùn)練數(shù)據(jù)上測量模型的誤差的損失部分。損失函數(shù)L(w)是典型的基于w的凸函數(shù)。固定的正則化參數(shù)λ?≥ 0定義了最小損失(即訓(xùn)練誤差)和最小化模型的復(fù)雜性(即避免過度擬合)這兩個(gè)目標(biāo)之間的權(quán)衡。
?(1)?損失函數(shù)
在統(tǒng)計(jì)學(xué),統(tǒng)計(jì)決策理論和經(jīng)濟(jì)學(xué)中,損失函數(shù)是指一種將一個(gè)事件(在一個(gè)樣本空間中的一個(gè)元素)映射到一個(gè)表達(dá)與其事件相關(guān)的經(jīng)濟(jì)成本和機(jī)會成本的實(shí)數(shù)上的一種函數(shù)。通常而言,損失函數(shù)由損失項(xiàng)和正則項(xiàng)組成。表11-2列出了常用的損失函數(shù)。
這里對表11-2中的一些內(nèi)容做些說明:
- Hinge loss:常用于軟間隔支持向量機(jī)的損失函數(shù);
- Logistic loss:常用于邏輯回歸的損失函數(shù);
- Squared loss:常用于最小二乘的損失函數(shù);
- Gradient or sub gradient:梯度與次梯度
(2)?正規(guī)化
正規(guī)化的目的是鼓勵(lì)簡單的模型,并避免過度擬合。MLlib支持以下正規(guī)化,如表11-3所示:
這里的sign(w)是由向量w中所有項(xiàng)的符號(±1)組成的向量。平滑度L2正規(guī)化問題一般比L1正規(guī)化容易解決。然而L1正規(guī)化能幫助促進(jìn)稀疏權(quán)重,導(dǎo)致更小、更可解釋的模型,其中后者于特征選擇是有用的。沒有任何正規(guī)化,特別是當(dāng)訓(xùn)練實(shí)例的數(shù)目是小的,不建議訓(xùn)練模型。
(3)?優(yōu)化
線性方法使用凸優(yōu)化來優(yōu)化目標(biāo)函數(shù)。MLlib使用兩種方法:新元和L-BFGS來描述優(yōu)化部分。目前,大多數(shù)算法的API支持隨機(jī)梯度下降(SGD),并有一些支持L-BFGS。
5.2?線性回歸
線性回歸是一類簡單的指導(dǎo)學(xué)習(xí)方法。線性回歸是預(yù)測定量響應(yīng)變量的有用工具。很多統(tǒng)計(jì)學(xué)習(xí)方法都是從線性回歸推廣和擴(kuò)展得到的,所以我們有必要重點(diǎn)理解它。
1.簡單線性回歸
簡單線性回歸非常簡單,只根據(jù)單一的預(yù)測變量X預(yù)測定量響應(yīng)變量Y。它假定X與Y之間存在線性關(guān)系。其數(shù)學(xué)關(guān)系如下:
≈表示近似。這種線性關(guān)系可以描述為Y對X的回歸。β0和β1是兩個(gè)未知的常量,被稱為線性模型的系數(shù),它們分別表示線性模型中的截距和斜率。
β0和β1怎么得到呢?通過大量樣本數(shù)據(jù)估算出估計(jì)值。假如樣本數(shù)據(jù)如下:
(x1, y1),(x2, y2),....,(x3, y3)
此時(shí)問題轉(zhuǎn)換為在坐標(biāo)中尋找一條與所有點(diǎn)的距離最大程度接近的直線問題,如圖11-7所示
使用最小二乘方法最終求得的估計(jì)值(β’0,β’1)。
實(shí)際情況,所有的樣本或者真實(shí)數(shù)據(jù)不可能真的都在一條直線上,每個(gè)坐標(biāo)都會有誤差,所以可以表示為如下關(guān)系:
上式也稱為總體回歸直線,是對X和Y之間真實(shí)關(guān)系的最佳線性近似。
2.多元線性回歸
相比簡單線性回歸,實(shí)踐中常常不止一個(gè)預(yù)測變量,這就要求對簡單線性回歸進(jìn)行擴(kuò)展。雖然可以給每個(gè)預(yù)測變量單獨(dú)建立一個(gè)簡單線性回歸模型,但無法做出單一的預(yù)測。更好的方法是擴(kuò)展簡單線性回歸模型,使它可以直接包含多個(gè)預(yù)測變量。一般情況下,假設(shè)有p個(gè)不同的預(yù)測變量,多元線性回歸模型為:
其中Xj代表第j個(gè)預(yù)測變量,βj代表第j個(gè)預(yù)測變量和響應(yīng)變量之間的關(guān)聯(lián)。
5.3?分類
5.2節(jié)的線性回歸模型中假設(shè)響應(yīng)變量Y是定量的,但很多時(shí)候,Y卻是定性的。比如杯子的材質(zhì)是定性變量,可以是玻璃、塑料或不銹鋼等。定性變量也叫分類變量。預(yù)測定性響應(yīng)值是指對觀測分類。
分類的目標(biāo)是劃分項(xiàng)目分類。最常見的分類類型是二元分類,二元分類有兩種分類,通常命名為正和負(fù)。如果有兩個(gè)以上的分類,它被稱為多元分類。MLlib支持兩種線性方法分類:線性支持向量機(jī)和邏輯回歸。線性支持向量機(jī)僅支持二元分類,而邏輯回歸對二元分類和多元分類都支持。對于這兩種方法,MLlib支持L1和L2正規(guī)化變體。MLlib中使用RDD[LabeledPoint]代表訓(xùn)練數(shù)據(jù)集,其中標(biāo)簽引從0開始,如0,1,2,...。對于二元標(biāo)簽γ在MLlib中使用0表示負(fù),使用+1表示正。
1.線性支持向量機(jī)
線性支持向量機(jī)(SVM)是用于大規(guī)模分類任務(wù)的標(biāo)準(zhǔn)方法。正是在介紹損失函數(shù)時(shí)提到的:
默認(rèn)情況下,線性支持向量機(jī)使用L2正規(guī)化訓(xùn)練。MLlib也支持選擇L1正規(guī)化,在這種情況下,問題就變成了線性問題。線性支持向量機(jī)算法輸出SVM模型。給定一個(gè)新的數(shù)據(jù)點(diǎn),記為X,該模型基于wτx的值做預(yù)測。默認(rèn)情況下,如果wτx?≥ 0則結(jié)果是正的,否則為負(fù)。
下例展示了如何加載樣本數(shù)據(jù)集,執(zhí)行訓(xùn)練算法。
import org.apache.spark.mllib.classification.{SVMModel,SVMWithSGD} import org.apache.spark.mllib.evaluation.BinaryClassificationMetrics import org.apache.spark.mllib.util.MLUtils //加載LIBSVM格式的訓(xùn)練數(shù)據(jù) val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsbvm_data.txt") //將數(shù)據(jù)切分為訓(xùn)練數(shù)據(jù)(60%)和測試數(shù)據(jù)(40%) val splits = data.randomSplit(Array(0.6,0.4),seed = 11L) val training = splits(0).cache() val test = splits(1) //運(yùn)行訓(xùn)練算法構(gòu)建模型 val numIterations = 100 val model = SVMWithSGD.train(training, numIterations) //在測試數(shù)據(jù)上計(jì)算原始分?jǐn)?shù) val scoreAndLabels = test.map{ point => val score = model.predict(point.features)(score, point.label) } //獲取評估指標(biāo) val metrics = new BinaryClassificationMetrics(scoreAndLabels) val auROC = metrics.areaUnderROC() println("Area under ROC = " + auROC) //保存和加載模型 model.save(sc, "myModelPath") val sameModel = SVMModel.load(sc, "myModelPath")SVMWithSGD.train默認(rèn)執(zhí)行L2正規(guī)化,可以設(shè)置正則化參數(shù)為1.0為執(zhí)行L1正規(guī)化。配置及優(yōu)化SVMWithSGD的代碼如下:
import org.apache.spark.mllib.optimization.L1Updater val svmAlg = new SVMWithSGD() svmAlg.optimizer.setNumIterations(200).setRegParam(0.1).setUpdater(new L1Updater) val modelL1 = svmAlg.run(training)2.邏輯回歸
?邏輯回歸被廣泛用于預(yù)測二元響應(yīng)。它正是在介紹損失函數(shù)時(shí)提到的:
對于二元分類問題,該算法輸出二元邏輯回歸模型。給定一個(gè)新的數(shù)據(jù)點(diǎn),記為X,該模型基于應(yīng)用邏輯函數(shù)
做預(yù)測,其中z =?wτx。默認(rèn)情況下,如果?(wτx) > 0.5,輸出為正,否則為負(fù)。雖然不像線性支持向量機(jī),邏輯回歸模型中,?(z)的原始輸出具有一概率解釋(即X是正概率)。
二元邏輯回歸可以推廣到多元邏輯回歸來訓(xùn)練和預(yù)測多元分類問題。對于多元分類問題,該算法將輸出一個(gè)多元邏輯回歸模型,其中包含K-1個(gè)二元邏輯回歸模型。MLlib實(shí)現(xiàn)了兩種算法來解決邏輯回歸分析:小批量梯度下降和L-BFGS。Spark官方推薦L-BFGS,因?yàn)樗刃∨荻认陆档氖諗扛臁?/p>
下例演示了如何使用邏輯回歸。
//運(yùn)行訓(xùn)練算法構(gòu)建模型 val model = new LogisticRegressionWithLBFGS().setNumClasses(10).run(training) //在測試數(shù)據(jù)上計(jì)算原始分?jǐn)?shù) val predictionAndLabels = test.map{ case LabeledPoint(label, features) => val prediction = model.predict(features)(prediction, label) } //獲取評估指標(biāo) val metrics = new MulticlassMetrics(predictionAndLabels) val precision =metrics.precision println("Precision = " + precision) //保存和加載模型 model.save(sc, "myModelPath") val samleModel = LogisticRegressionModel.load(sc, "myModelPath")5.4?回歸
1.線性最小二乘、套索和嶺回歸
線性最小二乘公式是回歸問題最常見的公式。在介紹損失函數(shù)時(shí)也提到過它的公式:
多種多樣的回歸方法通過使用不同的正規(guī)化類型,都派生自線性最小二乘。例如,普通最小二乘或線性最小二乘使用非正規(guī)化:嶺回歸使用L2正規(guī)化;套索使用L1正規(guī)化。對于所有這些模型的損失和訓(xùn)練誤差:
就是均方誤差。
下面的例子演示了如何使用線性回歸。
//加載解析數(shù)據(jù) val data = sc.textFile("data/mllib/ridge-data/lpsa.data") val parsedData = data.map { line => val parts = line.split(',')LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(' ').map(_.toDouble))).cache() //構(gòu)建模型 val numIterations = 100 val model = LinearRegressionWithSGD.train(parsedData, numIterations) //使用訓(xùn)練樣本計(jì)算模型并且計(jì)算訓(xùn)練誤差 val valueAndPreds = parsedData.map { point =>val prediction = model.predict(point.features)(point.label, prediction) } val MSE = valueAndPreds.map{case(v, p) => math.pow((v- p), 2)}.mean() println("training Mean Squared Error = " + MSE) //保存與加載模型 model.save(sc, "myModelPath") val sameModel = LinearRegressionModel.load(sc, "myModelPath") }2.流線性回歸
流式數(shù)據(jù)可以適用于線上的回歸模型,每當(dāng)有新數(shù)據(jù)到達(dá)時(shí),更新模型的參數(shù)。MLlib目前使用普通最小二乘法支持流線性回歸。除了每批數(shù)據(jù)到達(dá)時(shí),模型更新最新的數(shù)據(jù)外,實(shí)際與線下的執(zhí)行是類似的。
下面的例子,假設(shè)已經(jīng)初始化好了StreamingContext ssc來演示流線性回歸。
val numFeatures = 3 val model = new StreamingLinearRegressionWithSGD().setInitialWeights(Vectors.zeros(numFeatures)) model.trainOn(trainingData) model.predictOnValues(testData.map(lp => (lp.label, lp.features))).print() ssc.start() ssc.awaitTermination()6.?決策樹
決策樹是分類和回歸的機(jī)器學(xué)習(xí)任務(wù)中常用的方法。決策樹廣泛使用,因?yàn)樗鼈兒苋菀捉忉?#xff0c;處理分類的功能,延伸到多元分類設(shè)置,不需要縮放功能,并能捕捉到非線性和功能的交互。
MLlib使用連續(xù)和分類功能支持決策樹的二元和多元的分類和回歸。通過行實(shí)現(xiàn)分區(qū)數(shù)據(jù),允許分布式訓(xùn)練數(shù)以百萬計(jì)的實(shí)例。
6.1?基本算法
決策樹是一個(gè)貪心算法,即在特性空間上執(zhí)行遞歸的二元分割。決策樹為每個(gè)最底部(葉)分區(qū)預(yù)測相同的標(biāo)簽。為了在每個(gè)樹節(jié)點(diǎn)上獲得最大的信息,每個(gè)分區(qū)是從一組可能的劃分中選擇的最佳分裂。
1.節(jié)點(diǎn)不純度和信息增益
節(jié)點(diǎn)不純度是節(jié)點(diǎn)上標(biāo)簽的均勻性的量度。當(dāng)前實(shí)現(xiàn)提供了兩種分類不純度測量的方法(基尼不純度和嫡)和一種回歸不純度測量的方法(方差),如表11-4所示:
信息增益是父節(jié)點(diǎn)不純度與兩個(gè)子節(jié)點(diǎn)不純度的加權(quán)總和之間的差。假設(shè)將有s個(gè)分區(qū),大小為N的數(shù)據(jù)集D劃分為兩個(gè)數(shù)據(jù)集Dleft和Dright,那么信息增益為:
2.劃分候選人
(1)?連續(xù)特征
對于單機(jī)上實(shí)現(xiàn)的小數(shù)據(jù)集,給每個(gè)連續(xù)特征劃分的候選人在此特征上有唯一值。有些實(shí)現(xiàn)了對特征值排序,為了加速計(jì)算,使用這些有序的唯一值劃分候選人。對于大的分布式數(shù)據(jù)集,排序是很昂貴的。通過在樣本數(shù)據(jù)分?jǐn)?shù)上執(zhí)行位計(jì)算,實(shí)現(xiàn)了計(jì)算近似的劃分候選人集合。有序劃分創(chuàng)建了“箱”,可使用maxBins參數(shù)指定這樣的容器的最大數(shù)量。
(2)?分類特征
對于有M種可能值的分類特征,將會有2M-1-1個(gè)劃分候選人。對于二元(0/1)分類和回歸,我們可以通過平均標(biāo)簽排序的分類特征值,減少劃分候選人至M-1多得數(shù)據(jù)量。例如,對于一個(gè)有A、B和C三個(gè)分類的分類特征的二元分類問題,其相應(yīng)的標(biāo)簽1的比例是0.2,0.6和0.4時(shí),分類特征是有序的A、C、B。兩個(gè)劃分候選人分別是A|C,B和A,C|B,其中|標(biāo)記劃分。
在多元分類中共有2M-1-1種可能的劃分,無論何時(shí)都可能被使用。當(dāng)2M-1-1比參數(shù)maxBins大時(shí),我們使用與二元分類和回歸相類似的方法。M種分類特征用不純度排序,最終得到需要考慮的M-1個(gè)劃分候選人。
3.停止規(guī)則
遞歸樹的構(gòu)建當(dāng)滿足下面三個(gè)條件之一時(shí)會停在一個(gè)節(jié)點(diǎn)。
- 節(jié)點(diǎn)的深度與maxBins相等;
- 沒有劃分候選人導(dǎo)致信息增益大于minInfoGain;
- 沒有劃分候選人產(chǎn)生的子節(jié)點(diǎn)都至少有minInstancesPerNode個(gè)訓(xùn)練實(shí)例。
6.2?使用例子
下面的例子演示了使用基尼不純度作為不純度算法且樹深為5的決策樹執(zhí)行分類。
import org.apache.spark.mllib.tree.DecisionTree import org.apache.spark.mllib.tree.model.DecisionTreeModel import org.apache.spark.mllib.util.MLUtils val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt") val splits = data.randomSplit(Array(0.7, 0.3)) //訓(xùn)練決策樹模型 //空categoricalFeaturesInfo說明所有的特征是連續(xù)的 val numClasses = 2 val categoricalFeaturesInfo = Map[Int, Int]() val impurity = "gini" val maxDepth = 5 val maxBins = 32 // trainingData是訓(xùn)練數(shù)據(jù) val model = DecisionTree.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo, impurity, maxDepth, maxBins) //在測試實(shí)例上計(jì)算 val labelAndPreds = testData.map{ point =>val prediction = model.predict(point.features)(point.label, prediction) } val testErr = labelAndPreds.filter(r => r._1 != r._2).count.toDouble / testData.count() println("Test Error = " + testErr) println("Learned classification tree model: \n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = DecisionTreeModel.load(sc, "myModelPath")下面的例子演示了使用方差作為不純度算法且樹深為5的決策樹執(zhí)行分類
val categoricalFeaturesInfo = Map[Int, Int]() val impurity = "variance" val maxDepth =5 val maxBins = 32 val model = DecisionTree.trainRegressor(trainingData, categoricalFeaturesInfo, impurity, maxDepth, maxBins) val labelsAndPredictions = testData.map{ point =>val prediction = model.predict(point.features)(point.label, prediction) } val testMSE = labelsAndPredictions.map{ case(v, p) => math.pow((v - p), 2)}.mean() println("Test Mean Squared Error =" + testMSE) println("Learned regression tree model:\n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = DecisionTreeModel.load(sc, "myModelPath")7.?隨機(jī)森林
?合奏是一個(gè)創(chuàng)建由其他模型的集合組合而成的模型的學(xué)習(xí)算法。MLlib支持兩個(gè)主要的合奏算法:梯度提升決策樹和隨機(jī)森林,它們都使用決策樹作為其基礎(chǔ)模型。梯度提升決策樹和隨機(jī)森林雖然都是決策樹合奏的學(xué)習(xí)算法,但是訓(xùn)練過程是不同的。關(guān)于合奏有以下幾個(gè)權(quán)衡點(diǎn):
- GBT每次都要訓(xùn)練一顆樹,所以它們比隨機(jī)森林需要更長的時(shí)間來訓(xùn)練。隨機(jī)森林可以平行地訓(xùn)練多棵樹。另一方面,GBT往往比隨機(jī)森林更合理地使用更小(淺)的樹并且訓(xùn)練小樹會花費(fèi)更少的時(shí)間。
- 隨機(jī)森林更不易發(fā)生過度擬合。隨機(jī)森林訓(xùn)練更多的樹會減少多半的過度擬合,而GBT訓(xùn)練更多的樹會增加過度擬合。(在統(tǒng)計(jì)語言中,隨機(jī)森林通過使用更多的樹木減少方差,而GBT通過使用更多的樹木減少偏差。)
- 隨機(jī)森林可以更容易調(diào)整,因?yàn)樾阅芘c樹木的數(shù)量是單調(diào)增加的。但如果GBT樹木的數(shù)量增長過大,性能可能開始下降。
總之,兩種算法都是有效的,具體選擇應(yīng)取決于特定的數(shù)據(jù)集。
隨機(jī)森林是分類與回歸中最成功的機(jī)器學(xué)習(xí)模型之一。為了減少過度擬合的風(fēng)險(xiǎn),隨機(jī)森林將很多決策樹結(jié)合起來。和決策樹相似,隨機(jī)森林處理分類的功能,延伸到多元分類設(shè)置,不需要縮放功能,并能捕捉到非線性和功能的交互。
MLlib使用連續(xù)和分類功能支持隨機(jī)森林的二元和多元的分類和回歸。
7.1?基本算法
隨機(jī)森林訓(xùn)練一個(gè)決策樹的集合,所以訓(xùn)練可以并行。該算法隨機(jī)性注入訓(xùn)練過程,使每個(gè)決策樹會有一點(diǎn)不同。結(jié)合每棵樹的預(yù)測降低了預(yù)測的方差,改進(jìn)了測試數(shù)據(jù)的性能。
1.隨機(jī)注入
算法隨機(jī)性注入訓(xùn)練的過程包括:
1)?每次迭代對原始數(shù)據(jù)集進(jìn)行二次采樣獲得不同的訓(xùn)練集,即引導(dǎo)。
2)?考慮在樹的每個(gè)節(jié)點(diǎn)上將特征的不同隨機(jī)子集分割。
除了這些隨機(jī)性,每個(gè)決策樹個(gè)體都以同樣的方法訓(xùn)練。
2.預(yù)測
對隨機(jī)森林做預(yù)測,就必須聚合它的決策樹集合的預(yù)測。分類和回歸的聚合是不同的:
- 分類采用多數(shù)表決。每棵樹的預(yù)測作為對分類的一次投票,收到最多投票的分類就是預(yù)測結(jié)果。
- 回歸采用平均值。每棵樹都有一個(gè)預(yù)測值,這些樹的預(yù)測值的平均值就是預(yù)測結(jié)果。
7.2?使用例子
下面例子演示了使用隨機(jī)森林執(zhí)行分類。
//訓(xùn)練隨機(jī)森林模型 //空categoricalFeaturesInfo說明所有特征是連續(xù)的 val numClasses = 2 val categoricalFeaturesInfo = Map[Int, Int]() val numTrees = 3 //use more practice val featureSubsetStrategy = "auto" //Let the algorithm choose. val impurity = "gini" val maxDepth = 4 val maxBins = 32 val model = RandomForest.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo, numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins) val labelAndPreds = testData.map{ point => val prediction = model.predict(point.features)(point.label, prediction) }val testErr = labelAndPreds.filter(r => r._1 != r._2).count.toDouble / testData.count() println("Test Error = " + testErr) println("Learned classification forest model:\n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = RandomForestModel.load(sc, "myModelPath")下面例子演示了使用隨機(jī)森林執(zhí)行回歸。
val numClasses = 2 val categoricalFeaturesInfo = Map[Int, Int]() val numTrees = 3 //Use more in practice. val featureSubsetStrategy = "auto" //Let the algorithm choose val impurity = "variance" val maxDepth = 4 val maxBins = 32 val model = RandomForest.trainRegressor(trainingData, categoricalFeaturesInfo, numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins) val labelAndPredictions = testData.map{ point => val prediction = model.predict(point.features)(point.label, prediction) } val testMSE = labelAndPredictions.map{ case(v, p) => math.pow((v -p), 2)}.mean() println("Test Mean Squared Error = " + testMSE) println("Learned regression forest model:\n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = RandomForestModel.load(sc, "myModelPath")8.?梯度提升決策樹
GBT迭代訓(xùn)練決策樹,以便最小化損失函數(shù)。和決策樹相似,隨機(jī)森林處理分類的功能,延伸到多元分類設(shè)置,不需要縮放功能,并能捕捉到非線性和功能的交互。
MLlib使用連續(xù)和分類功能支持梯度提升決策樹的二元和多元的分類和回歸。
8.1?基本算法
GBT迭代訓(xùn)練一個(gè)決策樹的序列。在每次迭代中,算法使用當(dāng)前合奏來預(yù)測每個(gè)訓(xùn)練實(shí)例的標(biāo)簽,然后將預(yù)測與真實(shí)的標(biāo)簽進(jìn)行比較。數(shù)據(jù)集被重新貼上標(biāo)簽,將重點(diǎn)放在預(yù)測不佳的訓(xùn)練實(shí)例上。因此,在下一迭代中,決策樹將幫助糾正先前的錯(cuò)誤。重貼標(biāo)簽的具體機(jī)制是由損失函數(shù)定義的。隨著每次迭代,GBT進(jìn)一步減少訓(xùn)練數(shù)據(jù)上的損失函數(shù)。表11-5列出了MLlib中GBT支持的損失函數(shù)。請注意,每個(gè)損失只適用于分類或回歸之一。其中N表示實(shí)例數(shù)量,y1表示實(shí)例i的標(biāo)簽,xi表示實(shí)例i的特征,F(Xi)表示實(shí)例i的模型預(yù)測標(biāo)簽。
8.2?使用例子
下例演示了用LogLoss作為損失函數(shù),使用GBT執(zhí)行分類的例子。
//訓(xùn)練GradientBoostedTree模型 //默認(rèn)使用LogLoss val boostingStrategy = BoostingStrategy.defaultParams("Classification") boostingStrategy.numIterations = 3 //Note:Use more iterations in practice. boostingStrategy.treeStrategy.numClasses = 2 boostingStrategy.treeStrategy.maxDepth = 5 //空categoricalFeaturesInfo說明所有特征是連續(xù)的 boostingStrategy.treeStrategy.categoricalFeaturesInfo = Map[Int, Int]() val model = GradientBoostedTrees.train(trainingData, boostingStrategy) val labelAndPreds = testData.map{ point => val prediction = model.predict(point.features)(point.label, prediction) } val testErr = labelAndPreds.filter(r => r._1 != r._2).count.toDouble / testData.count() println("Test Error = " + testErr) println("Learned classification GBT model:\n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = GradientBoostedTreesModel.load(sc, "myModelPath")下例演示了用Squared Error?作為損失函數(shù),使用GBT執(zhí)行回歸的例子。
//訓(xùn)練GradientBoostedTrees模型 // defaultParams指定了Regression, 默認(rèn)使用SquaredError val boostingStrategy = BoostingStrategy.defaultParams("Regression") boostingStrategy.numIterations = 3 //Note: Use more iterations in practice. boostingStrategy.treeStrategy.maxDepth = 5 //空categoricalFeaturesInfo說明所有特征是連續(xù)的 boostingStrategy。treeStrategy.categoricalFeaturesInfo = Map [Int, Int]() val model = GradientBoostedTrees.train(trainingData, boostingStrategy) val labelsAndPredictions = testData.map{ point =>val prediction = model.predict(point.features)(point.label, prediction) } val testMSE = labelsAndPredictions.map{case(v, p) => math.pow((v - p), 2)}.mean() println("Test Mean Squared Error = " + testMSE) println("Learned regression GBT model:\n" + model.toDebugString) model.save(sc, "myModelPath") val sameModel = GradientBoostedTreesModel.load(sc, "myModelPath")9.?樸素貝葉斯
9.1?算法原理
j我們首先來介紹一些數(shù)學(xué)中的理論,然后來看樸素貝葉斯。
條件概率:A和B表示兩個(gè)事件,且P(B)?≠ 0(B事件發(fā)生的概率不等于0),則給定事件B發(fā)生的條件下事件A發(fā)生的條件概率定義為:
使用條件概率推導(dǎo)出乘法定律:A和B表示兩個(gè)事件,且P(B)?≠ 0(B事件發(fā)生的概率不等于0)。那么:
將乘法定律擴(kuò)展為全概率定律:事件B1,B2,...,Bn滿足Uni=1 Bi=Ω,Bi∩Bj=?,i?≠ j,且對所有的i,P(Bi) > 0。那么,對于任意的A,滿足:
貝葉斯公式:事件事件A, B1,B2,...,Bn,?其中Bi不相交,Uni=1?Bi=Ω,且對所有的i,P(Bi) > 0。那么:
樸素貝葉斯分類算法是一種基于每對特征之間獨(dú)立性的假設(shè)的簡單的多元分類算法。樸素貝葉斯的思想:對于給出的待分類項(xiàng),求解在此項(xiàng)出現(xiàn)的條件下各個(gè)類別出現(xiàn)的概率,哪個(gè)最大,就認(rèn)為此待分類項(xiàng)屬于哪個(gè)類別。樸素貝葉斯分類的定義如下:
1)?設(shè)x = {a1,a2,...,am}為待分類項(xiàng),每個(gè)ai為x的一個(gè)特征屬性;
2)類別集合C = {y1,y2,...,yn};
3)?計(jì)算P(y1|x),P(y2|x),...,P(yn|x) ;
4)?如果P(yk|x) = max{(y1|x),P(y2|x),...,P(yn|x)?},則x?ε?yk 。
關(guān)鍵在第三步:
1)?找到一個(gè)已知分類的待分類項(xiàng)集合,這個(gè)集合叫做訓(xùn)練樣本集。
2)?統(tǒng)計(jì)得到在各類別下各個(gè)特征屬性的條件概率估計(jì)。即P(a1|y1),P(a2|y1),...,P(am|y1);?P(a1|y2),P(a2|y2),... ,P(am|y2);...;P(a1|yn),P(a2|yn),...,P(am|yn) 。
3)?如果各個(gè)特征屬性都是獨(dú)立的,則根據(jù)貝葉斯公式可以得到以下推導(dǎo):
樸素貝葉斯能夠被非常有效的訓(xùn)練。它被單獨(dú)傳給訓(xùn)練數(shù)據(jù),計(jì)算給定標(biāo)簽特征的條件概率分布并給出觀察結(jié)果用于預(yù)測。
MLlib支持多項(xiàng)樸素貝葉斯和伯努利樸素貝葉斯。這些模型典型的應(yīng)用是文檔分類。在這方面,每個(gè)觀察是一個(gè)文檔,每個(gè)特征代表一個(gè)條件,其值是條件的頻率(在多項(xiàng)樸素貝葉斯中)或一個(gè)由零個(gè)或一個(gè)指示該條件是否在文檔中找到(在伯努利樸素貝葉斯中)。特征值必須是非負(fù)的。模型類型選擇使用可選的參數(shù)"多項(xiàng)"或“伯努利”。“多項(xiàng)”作為默認(rèn)模型。通過設(shè)置參數(shù)λ(默認(rèn)為1.0)添加劑平滑。為文檔分類,輸入特征向量通常是稀疏的,因?yàn)橄∈柘蛄磕芾孟∈栊缘膬?yōu)勢。因?yàn)橛?xùn)練數(shù)據(jù)只使用一次,所以沒有必要緩存它。
9.2?使用例子
下面的例子演示了如何使用多項(xiàng)樸素貝葉斯。
import org.apache.spark.mllib.classification.{NaiveBayes, NaiveBayesModel} import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.regression.LabeledPoint val data = sc.textFile("data/mllib/sample_naive_bayes_data.txt") val parsedData = data.map{ line => val parts = line.split(',')LabeledPoint(parts(0).toDouble, Vectors.dense(parts(1).split(' ').map(_.toDouble))) } val splits = parsedData.randomSplit(Array(0.6, 0.4), seed = 11L) val training = splits(0) val test = splits(1) val model = NaiveBayes.train(training, lambda = 1.0, modelType = "multinomial") val predictionAndLabel = test.map(p => (model.predict(p.features), p.label)) val accuracy = 1.0 * predictionAndLabel.filter(x => x._1 == x._2).count() / test.count() model.save(sc, "myModelPath") val sameModel = GradientBoostedTreesModel.load(sc, "myModelPath")10.?保序回歸
10.1?算法原理
保序回歸屬于回歸算法,其定義為:給定一個(gè)有限的實(shí)數(shù)集合Y =??{y1,y2,...,yn}?表示觀測響應(yīng),?X =??{x1,x2,...,xn}?表示未知的響應(yīng)值,進(jìn)行擬合找到一個(gè)最小化函數(shù):
?并使用x1≤x2≤...≤xn?對目標(biāo)排序,其中ωi?是大于0的權(quán)重。最終的函數(shù)被稱為保序回歸,并且它是唯一的。它可以看做是排序限制下的最小二乘問題。基本上保序回歸是擬合原始數(shù)據(jù)點(diǎn)最佳的單調(diào)函數(shù)。
MLlib支持PAVA,此算法使用一種辦法來平行化保序回歸。保序回歸有一個(gè)可選參數(shù)isotonic,默認(rèn)值是true。此參數(shù)指定保序回歸是保序的(單調(diào)增加)還是不保序的(單調(diào)減少)。
保序回歸的結(jié)果被視為分段線性函數(shù)。因此,預(yù)測的規(guī)則是:
1)?如果預(yù)測輸入能準(zhǔn)備匹配訓(xùn)練特征,那么返回相關(guān)預(yù)測。如果有多個(gè)預(yù)測匹配訓(xùn)練特征,那么會返回其中之一。
2)?如果預(yù)測輸入比所有的訓(xùn)練特征低或者高,那么最低和最高的訓(xùn)練特征各自返回。如果有多個(gè)預(yù)測比所有的訓(xùn)練特征低或者高,那么都會返回。
3)?如果預(yù)測輸入介于兩個(gè)訓(xùn)練特征,那么預(yù)測會被視為分段線性函數(shù)和從最接近的訓(xùn)練特征中計(jì)算得到的插值。
10.2?使用例子
下面的例子演示了如何使用保序回歸。
import org.apache.spark.mllib.regression.{IsotonicRegression, IsotonicRegressionModel} //省略數(shù)據(jù)加載及樣本劃分的代碼 val model = new IsotonicRegression().setIsotonic(true).run(training) val predictionAndLabel = test.map { point => val predictedLabel = model.predict(point._2)(predictedLabel, point._1) } val meanSquareError = predictionAndLabel.map{case(p, 1) => math.pow((p-1), 2)}.mean() println("Mean Squared Error = " + meanSquaredError) model.save(sc, "myModelPath") val sameModel = IsotonicRegressionModel.load(sc, "myModelPath")11.?協(xié)同過濾
協(xié)同過濾通常用于推薦系統(tǒng)。這些技術(shù)旨在填補(bǔ)用戶關(guān)聯(lián)矩陣的缺失項(xiàng)。MLlib支持基于模型的協(xié)同過濾,用戶和產(chǎn)品可以預(yù)測缺失項(xiàng)的潛在因素的小集合來描述。MLlib采用交替最小二乘算法來學(xué)習(xí)這些潛在的因素。
矩陣分解的標(biāo)準(zhǔn)方法基于協(xié)同過濾處理用戶項(xiàng)矩陣的條目是明確的。現(xiàn)實(shí)世界的用例只能訪問隱式反饋是更常見的(例如瀏覽、點(diǎn)擊、購買、喜歡、股份等)。
下面的例子演示了如何使用協(xié)同過濾。
import org.apache.spark.mllib.recommendation.ALS import org.apache.spark.mllib.recommendation.MatrixFactorizationModel import org.apache.spark.mllib.recommendation.Ratingval data = sc.textFile("data/mllib/als/test.data") val ratings = data.map(_.split(',') match { case Array(user, item, rate) =>Rating(user.toInt, item.toInt, rate.toDouble) }) //使用ALS構(gòu)建推薦模型 val rank = 10 val numIterations = 20 val model = ALS.train(ratings, rank, numIterations, 0.01) //模型計(jì)算 val usersProducts = ratings.map{ case Rating(user, product, rate) => (user, product) } val predictions = model.predict(usersProducts).map { case Rating(user, product, rate) =>((user, product), rate) } val ratesAndPreds = ratings.map { case Rating(user, product, rate) => ((user, product), rate) }.join(predictions) val MSE = ratesAndPres.map { case ((user, product), (r1, r2)) =>val err = (r1 - r2)err * err}.mean() println("Mean Squared Error = " + MSE) model.save(sc, "myModelPath") val sameModel = MatrixFactorizationModel.load(sc, "myModelPath")12.?聚類
聚類分析又稱群分析,它是研究(樣品或指標(biāo))分類問題的一種統(tǒng)計(jì)分析方法。聚類分析以相似性為基礎(chǔ),在一個(gè)聚類中的模式之間比不在同一聚類中的模式之間具有更多的相似性。MLlib支持的聚類算法如下:
- K-means;
- 高斯混合(Gaussian mixture);
- power iteration clustering(PIC);
- latent?Dirichlet allocation(LDA);
- 流式K-means.
12.1?K-means
K-means算法是硬聚類算法,是典型的基于原型的目標(biāo)函數(shù)聚類方法的代表,它是數(shù)據(jù)點(diǎn)到原型的某種距離作為優(yōu)化的目標(biāo)函數(shù),利用函數(shù)求極值的方法得到迭代運(yùn)算的調(diào)整規(guī)則。
聚類屬于無監(jiān)督學(xué)習(xí),以往的回歸、樸素貝葉斯、SVM等都是有類別標(biāo)簽y的,也就是說,樣本中已經(jīng)給出了樣本的分類。而聚類的樣本中卻沒有給定y,只有特征x,比如假設(shè)宇宙中的星星可以表示成三維空間中點(diǎn)集(x, y, z)。聚類的目的是找到每個(gè)樣本x潛在的類別y,并將同類別y的樣本x放在一起。
在聚類問題中,訓(xùn)練樣本X =??{x1,x2,...,xm},每個(gè)xi?ε Rn,K-means算法是將樣本聚類成k個(gè)簇,具體算法描述如下:
1)?隨機(jī)選取k個(gè)聚類質(zhì)心點(diǎn)為μ1,μ2,...,μk?ε Rn;
2)?重復(fù)下面過程直到收斂。
對每一個(gè)樣本i計(jì)算它應(yīng)該屬于的類
對于每一個(gè)類重新計(jì)算該類的質(zhì)心
k是我們事先給定的聚類數(shù),ci代表樣本i與k個(gè)類中距離最近的那個(gè)類,ci的值是1到k中的一個(gè)。質(zhì)心uj代表我們對屬于同一個(gè)類的樣本中心點(diǎn)的猜測,拿星團(tuán)模型來解釋就是要將所有的星星聚成k個(gè)星團(tuán),首先隨機(jī)選取k個(gè)宇宙中的點(diǎn)(或者k個(gè)星星)作為k個(gè)星團(tuán)的質(zhì)心,然后第一步對于每一個(gè)星星計(jì)算其到k個(gè)質(zhì)心中每一個(gè)的距離,接著選取距離最近的那個(gè)星團(tuán)作為ci,這樣經(jīng)過第一步每一個(gè)星星都有了所屬的星團(tuán);第二步對于每一個(gè)星團(tuán),重新計(jì)算它的質(zhì)心uj(對里面所有的星星坐標(biāo)求平均)。重復(fù)迭代第一步和第二步直到質(zhì)心不變或者變化很小。圖11-8演示了以上過程。
?
?下面的例子演示了K-means算法的使用。
import org.apache.spark.mllib.clustering.{KMeans, KMeansModel} import org.apache.spark.mllib.linalg.Vectorsval data = sc.textFile("data/mllib/kmeans_data.txt") val parsedData = data.map(s => Vectors.dense(s.split(' ').map(_.toDouble))).cache() val numClusters =2 val numIterations = 20 val clusters = KMeans.train(parsedData, numClusters, numIterations) val WSSSE = clusters.computeCost(parsedData) println("Within Set Sum of Squared Errors = " + WSSSE) clusters.save(sc, "myModelPath") val sameModel = KMeansModel.load(sc, "myModelPath")12.2?高斯混合
K-means的結(jié)果是每個(gè)數(shù)據(jù)點(diǎn)被分配到其中某一個(gè)cluster了,而高斯混合則給出這些數(shù)據(jù)點(diǎn)被分配到每個(gè)cluster的概率。高斯混合的算法與K-means算法類似。MLlib中高斯混合的使用例子
import org.apache.spark.mllib.clustering.GaussianMixture import org.apache.spark.mllib.clustering.GaussianMixtureModel import org.apache.spark.mllib.linalg.Vectorsval data = sc.textFile("data/mllib/gmm_data.txt") val parsedData = data.map(s => Vectors.dense(s.trim.split(' ').map(_.toDouble))).cache() val gmm = new GaussianMixture().setK(2).run(parsedData) gmm.save(sc, "myGMMModel") val sameModel = GaussianMixtureModel.load(sc, "myGMMModel") for(i <- 0 until gmm.k){println("weight=%f\nmu=%s\nsigma=\n%s\n" format(gmm.weights(i), gmm.gaussians(i).mu, gmm.gaussians(i).sigma)) }12.3?快速迭代聚類
快速迭代聚類是一種簡單可擴(kuò)展的圖聚類方法。其使用例子如下:
?
import org.apache.spark.mllib.clustering.{PowerIterationClustering, PowerIterationClusteringModel} import org.apache.spark.mllib.linalg.Vectors val similarities: RDD[(Long, Long, Double)] = ... val pic = new PowerIterationClustering().setK(3).setMaxIterations(20) val model = pic.run(similarities) model.assignments.foreach{ a => println(s"${a.id} -> ${a.cluster}") } model.save(sc, "myModelPath") val sameModel = PowerIterationClusteringModel.load(sc, "myModelPath")12.4?latent Dirichlet allocation
latent Dirichlet allocation(LDA)是一個(gè)三層貝葉斯概率模型,包含詞、主題和文檔三層結(jié)構(gòu)。文檔到主題服從Dirichlet分布,主題到詞服從多項(xiàng)式分布。
LDA是一種非監(jiān)督機(jī)器學(xué)習(xí)技術(shù),可以用來識別大規(guī)模文檔集或語料庫中潛藏的主題信息。它采用了詞袋的方法,這種方法將每一篇文檔視為一個(gè)詞頻向量,從而將文本信息轉(zhuǎn)化為了易于建模的數(shù)字信息。但是詞袋方法沒有考慮詞與詞之間的順序,這簡化了問題的復(fù)雜性,同時(shí)也為模型的改進(jìn)提供了契機(jī)。每一篇文檔代表了一些主題所構(gòu)成的一個(gè)概率分布,而每一個(gè)主題又代表了很多單詞所構(gòu)成的一個(gè)概率分布。由于Dirichlet分布隨機(jī)向量各分量間的弱相關(guān)性(之所以還有點(diǎn)“相關(guān)”,是因?yàn)楦鞣至恐捅仨殲?),使得我們假想的潛在主題之間也幾乎是不相關(guān)的,這與很多實(shí)際問題并不相符,從而造成了LDA的又一個(gè)遺留問題。
對于語料庫中的每篇文檔,LDA定義了如下生成過程:
1)?對每一篇文檔,從主題分布中抽取一個(gè)主題;
2)?從上述被抽到的主題所對應(yīng)的單詞分布中抽取一個(gè)單詞;
3)?重復(fù)上述過程直至遍歷文檔中的每一個(gè)單詞。
下例演示了LDA的使用。
12.5?流式K-means
當(dāng)數(shù)據(jù)流到達(dá),我們可能想要?jiǎng)討B(tài)地估算cluster,并更新它們。該算法采用了小批量的K-means更新規(guī)則。對每一批數(shù)據(jù),將所有的點(diǎn)分配到最近的cluster,并計(jì)算最新的cluster中心,然后更新每個(gè)cluster的公式為:
ci是前一次計(jì)算得到的cluster中心,ni是已經(jīng)分配到cluster的點(diǎn)數(shù),xi是從當(dāng)前批次得到的cluster的新中心,mi是當(dāng)前批次加入cluster的點(diǎn)數(shù)。衰減因子a可被用于忽略過去的數(shù)據(jù);a=1時(shí)所有數(shù)據(jù)都從一開始就被使用;a=0時(shí)只有最近的數(shù)據(jù)將被使用。這類似于一個(gè)指數(shù)加權(quán)移動平均值。
下面的例子演示了流失K-means的使用。
13.?維數(shù)減縮
維數(shù)減縮是減少所考慮變量的數(shù)量的過程。維數(shù)減縮有兩種方式:
- 奇異值分解;
- 主成分分析。
13.1?奇異值分解
奇異值分解將一個(gè)矩陣因子分解為三個(gè)矩陣U、Σ?和?V:
其中U是正交矩陣,其列被稱為左奇異向量;Σ是對角矩陣,其對角線是非負(fù)的且以降序排列,因此被稱為奇異值;V也是正交矩陣,其列被稱為右奇異向量。
對于大的矩陣,除了頂部奇異值和它的關(guān)聯(lián)奇異值,我們不需要完全分解。這樣可以節(jié)省存儲、去噪聲和恢復(fù)矩陣的低秩結(jié)構(gòu)。如果我們保持前7個(gè)頂部奇異值,那么最終的低秩矩陣為:
假設(shè)n小于m。奇異值和右奇異值向量來源于特征值和Gramian矩陣AτA的特征向量。矩陣存儲左奇異向量U,通過矩陣乘法U = A(VS-1)計(jì)算。使用的實(shí)際方法基于計(jì)算成本自動被定義:
1)?如果(n < 100)?或者(k > n/2),我們首先計(jì)算Gramian矩陣,然后再計(jì)算其頂部特征向量并將特征向量本地化到Driver。這需要單次傳遞,在每個(gè)Executor和Driver上使用O(n2)的存儲,并花費(fèi)Driver上O(n2k)的時(shí)間。
2)?否則,將使用分布式的方式計(jì)算AτA的值,并且發(fā)送給ARPACK計(jì)算頂部特征向量。這需要O(k)次傳遞,每個(gè)Executor上使用O(n)的存儲,在Driver上使用O(nk)的存儲。
下面的例子演示了SVD的使用。
13.2?主成分分析
主成分分析是一種統(tǒng)計(jì)方法,此方法找到一個(gè)旋轉(zhuǎn),使得第一坐標(biāo)具有可能的最大方差,并且每個(gè)隨后的坐標(biāo)都具有可能的最大方差。旋轉(zhuǎn)矩陣的列被稱為主成分。下面的例子演示了使用RowMatrix計(jì)算主成分。
14.?特征提取與轉(zhuǎn)型
14.1?術(shù)語頻率反轉(zhuǎn)
術(shù)語頻率反轉(zhuǎn)是一個(gè)反映文集的文檔中的術(shù)語的重要性,廣泛應(yīng)用于文本挖掘的特征矢量化方法。術(shù)語表示為t,文檔表示為d,文集表示為D。術(shù)語頻率TF(t,d)表示術(shù)語t在文檔d中出現(xiàn)的頻率,文檔頻率DF(t, D)表示包含術(shù)語t的文檔數(shù)量。如果我們僅使用術(shù)語頻率來測量重要性,則很容易過度強(qiáng)調(diào)術(shù)語出現(xiàn)的很頻繁,而攜帶的關(guān)于文檔的信息很少,例如a、the?和of等。如果術(shù)語非常頻繁地跨文集出現(xiàn),這意味著它并沒有攜帶文檔的特定信息。反轉(zhuǎn)文檔頻率是一個(gè)術(shù)語提供了多少信息的數(shù)值度量:
|D|?表示文集中的文檔總數(shù)。因?yàn)槭褂昧藢?shù),如果一個(gè)術(shù)語出現(xiàn)于所有的文檔中,它的IDF值變0。需要注意的是,應(yīng)用一個(gè)平滑項(xiàng),以避免被零除。TF-IDF方法基于TF與IDF,它的公式如下:
這里有一些術(shù)語頻率和文檔頻率定義的變種。在MLlib中,為了使TF和IDF更靈活,將它們分開了,MLlib實(shí)現(xiàn)術(shù)語頻率時(shí)使用了哈希。應(yīng)用歐冠哈希函數(shù)將原始特征映射到了索引(術(shù)語),術(shù)語頻率因此依賴于map的索引計(jì)算。這種方法避免了需要計(jì)算一個(gè)全局術(shù)語到索引圖,這對于大型語料庫開銷會很大。但由于不同的原始特征在哈希后可能變?yōu)橥瑯拥男g(shù)語,所以存在潛在的哈希沖突。為了降低碰撞的機(jī)會,我們可以增加目標(biāo)特征的維度(即哈希表中的桶數(shù))。默認(rèn)的特征維度是220 = 1048576.
下面的例子演示了HashingTF的使用。
14.2?單詞向量轉(zhuǎn)換
Word2Vec計(jì)算由單詞表示的分布式向量。分布式表征的主要優(yōu)點(diǎn)是,類似的單詞在矢量空間是接近的,這使得泛化小說模式更容易和模型估計(jì)更穩(wěn)健。分布式向量表示被證實(shí)在許多自然語言處理應(yīng)用中有用,例如,命名實(shí)體識別、消歧、解析、標(biāo)記和機(jī)器翻譯。
MLlib的Word2Vec實(shí)現(xiàn)采用了skip-gram模型。skip-gram的訓(xùn)練目標(biāo)是學(xué)習(xí)單詞的向量表示,其善于在同一個(gè)句子預(yù)測其上下文。給定了單詞序列w1,w2,...,wτ,skip-gram模型的目標(biāo)是最大化平均對數(shù)似然,公式如下:
其中k是訓(xùn)練窗口的大小。每個(gè)單詞w與兩個(gè)向量uw和vw關(guān)聯(lián),并且由單獨(dú)的向量來表示。通過給定的單詞wj正確預(yù)測wi的概率是由softmax模型決定的。softmax模型如下:
w表示詞匯量。
由于計(jì)算logp(wi|wj)的成本與V成正比(V很容易就達(dá)到百萬以上),所以softmax這種skip-gram模型是很昂貴的。為了加速Word2Vec計(jì)算,MLlib使用分級softmax,它可以減少計(jì)算logp(wi|wj)?復(fù)雜度到O(log(V))。
下面的例子演示了Word2Vec的使用,
14.3?標(biāo)準(zhǔn)尺度
通過縮放到單位方差和/或通過在訓(xùn)練集的樣本上使用列摘要統(tǒng)計(jì)溢出均值使特征標(biāo)準(zhǔn)化,這是常見的預(yù)處理步驟。例如,當(dāng)所有的特征都有單位方差和/或零均值時(shí),支持向量機(jī)的RBF核或者L1和L2正規(guī)化線性模型通常能更好地工作。標(biāo)準(zhǔn)化可以提高在優(yōu)化過程中的收斂速度,并且還可以防止在模型訓(xùn)練期間,非常大的差異會對特征發(fā)揮過大的影響。
StandardScaler的構(gòu)造器有兩個(gè)參數(shù):
- withMean:默認(rèn)false。用于縮放前求均值,這將建立一個(gè)密集的輸出,所以不能在稀疏輸入上正常工作,并將引發(fā)異常。
- withStd:默認(rèn)true。縮放數(shù)據(jù)到標(biāo)準(zhǔn)單位誤差。
以下例子演示了StandardScaler的使用。
14.4?正規(guī)化尺度
正規(guī)化尺度把樣本劃分為單位Lp范式,即維度。這是一種常見的對文本分類或集群化的操作。例如,兩個(gè)L2正規(guī)化TF-IDF向量的點(diǎn)積是這些向量的余弦近似值。
設(shè)二維空間內(nèi)有兩個(gè)向量a和b,它們的夾角為θ(0≤θ≤π),則點(diǎn)積定義為以下實(shí)數(shù):
MLlib提供Normalizer支持正規(guī)化,Normalizer有以下構(gòu)造參數(shù):
p:?正規(guī)化到Lp空間,默認(rèn)為2。
下面的例子演示了Normalizer的使用。
14.5?卡方特征選擇器
ChiSqSelector用于卡方特征選擇。它運(yùn)轉(zhuǎn)在具有分類特征的標(biāo)簽數(shù)據(jù)上。ChiSqSelector對基于分類進(jìn)行獨(dú)立卡方測試的特征排序,并且過濾(選擇)最接近標(biāo)簽的頂部特征。
ChiSqSelector有以下構(gòu)造器參數(shù):
numTopFeatures:選擇器將要過濾(選擇)的頂部特征數(shù)量。
下邊的例子演示了ChiSqSelector的使用。
14.6?Hadamard積
ElementwiseProduct采用逐個(gè)相乘的方式,使用給定的權(quán)重與每個(gè)輸入向量相乘。換言之,它采用一個(gè)標(biāo)量乘法器擴(kuò)展數(shù)據(jù)集的每一列。這表示Hadamard積對輸入向量v,使用轉(zhuǎn)換向量w,最終生成一個(gè)結(jié)果向量。Hadamard積可由以下公式表示:
ElementwiseProduct的構(gòu)造器參數(shù)為:
w:轉(zhuǎn)換向量。
下面代碼演示了ElementwiseProduct的使用。
15.?頻繁模式挖掘
分析大規(guī)模數(shù)據(jù)集的第一個(gè)步驟通常是挖掘頻繁項(xiàng)目、項(xiàng)目集、亞序列或其他子結(jié)構(gòu),這在數(shù)據(jù)挖掘中作為一個(gè)活躍的研究主題已多年了。其數(shù)學(xué)原理讀者可以取維基百科了解。MLlib提供了頻繁模式挖掘的并行實(shí)現(xiàn)——FP-growth算法。
FP-growth
給定一個(gè)交易數(shù)據(jù)集,FP-growth的第一步驟是計(jì)算項(xiàng)目的頻率,并確定頻繁項(xiàng)目。FP-growth雖然與Apriori類算法有相同的設(shè)計(jì)目的,但是FP-growth的第二步使用后綴樹(FP樹)結(jié)構(gòu)對交易數(shù)據(jù)編碼且不會顯式生成候選集(生成候選集通常開銷很大)。第二步之后,就可以從FP樹中抽取頻繁項(xiàng)目集。MLlib中實(shí)現(xiàn)了FP-growth的平行版本,叫做PFP。PFP可以將FP-growth的工作分發(fā)到其他機(jī)器,比單機(jī)運(yùn)行有更好的擴(kuò)展性。
FPGrowth有以下參數(shù):
- minSupport:項(xiàng)目集被確定為頻繁的最小數(shù)量。
- numPartitions:分發(fā)任務(wù)的數(shù)量。
下面的例子演示了FPGrowth的使用。
16.?預(yù)言模型標(biāo)記語言
預(yù)言模型標(biāo)記是一種基于XML的語言,它能夠定義和共享應(yīng)用程序之間的預(yù)測模型。
MLlib支持將模型導(dǎo)出為預(yù)言模型標(biāo)記語言。表11-6列出了MLlib模型導(dǎo)出為PMML的相應(yīng)模型。
下面的例子演示了將KMeamsModel導(dǎo)出為PMML格式。
17.?管道
Spark1.2增加了一個(gè)新包spark.ml,目的是提供一套高層次的API,幫助用戶創(chuàng)建、調(diào)試機(jī)器學(xué)習(xí)的管道。spark.ml的標(biāo)準(zhǔn)化API用于將多種機(jī)器學(xué)習(xí)算法組合到一個(gè)管道或工作流中。下面列出了Spark ML API的主要概念:
- ML?DataSet:由Hive table或者數(shù)據(jù)源的數(shù)據(jù)構(gòu)成的可容納各種數(shù)據(jù)類型的DataFrame作為數(shù)據(jù)集。例如,數(shù)據(jù)集可以由不同的列分別存儲文本、特征向量、標(biāo)簽和預(yù)測值。
- Transformer:是一種將DataFrame轉(zhuǎn)換為另一個(gè)DataFrame的算法。例如,ML模型是一個(gè)將特征RDD轉(zhuǎn)換為預(yù)測值RDD的Transformer。
- Estimator:適用于DataFrame,并生成一個(gè)Transformer。例如,學(xué)習(xí)算法是一個(gè)在數(shù)據(jù)集上訓(xùn)練并生成一個(gè)模型的Estimator。
- Pipeline:鏈接多個(gè)Transformer和Estimator,一起構(gòu)成ML的工作流。
- Param:所有Transformer和Estimator用于指定參數(shù)的通用API。
17.1?管道工作原理
機(jī)器學(xué)習(xí)中,運(yùn)行一系列的算法取處理數(shù)據(jù)或者從數(shù)據(jù)學(xué)習(xí)的場景是很常見的。例如,一個(gè)簡單的文本文檔處理工作流可能包含以下階段:
1)?將文檔文本切分成單詞;
2)?將文檔的單詞轉(zhuǎn)換為數(shù)字化的特征向量;
3)?使用特征向量和標(biāo)簽學(xué)習(xí)一個(gè)預(yù)測模型。
Spark ML以一系列按序運(yùn)行的PipelineStage組成的管道來表示這樣的工作流。這一系列的Stage要么是Transformer,要么是Estimator。數(shù)據(jù)集通過管道中的每個(gè)Stage都會被修改。比如Transformer的transform()方法將在數(shù)據(jù)集上被調(diào)用,Estimator的fit()方法被調(diào)用生成一個(gè)Transformer,然后此Transformer的transform()方法也將在數(shù)據(jù)集上被調(diào)用。圖11-9展示了簡單文本文檔工作流例子使用管道的處理流程。
圖11-9說明整個(gè)管道由3個(gè)Stage組成。Tokenizer和HashingTF都是Transformer,LogisticRegression是Estimator。每個(gè)圓柱體都說明它本身是一個(gè)DataFrame。整個(gè)處理流程如下:
1)?在由原生文本文檔構(gòu)成的原始數(shù)據(jù)集上應(yīng)用Pipeline.fit()方法。
2) Tokenizer.transform()將原生文本文檔切分為單詞,并向數(shù)據(jù)集增加單詞列。
3) HashingTF.transform()將單詞列轉(zhuǎn)換為特征向量,并向數(shù)據(jù)集增加向量列。
4)?因?yàn)長ogisticRegression是Estimator,所以管道第一次調(diào)用LogisticRegression.fit()生成了LogisticRegressionModel。如果管道中還有更多的Stage,將會傳遞數(shù)據(jù)集到下一個(gè)Stage之前在數(shù)據(jù)集上調(diào)用LogisticRegressionModel的transform()。
管道本身是一個(gè)Estimator。因此調(diào)用Pipeline的fit()方法最后生成了PipelineModel,PipelineModel也是一個(gè)Transformer。這個(gè)PipelineModel會在測試時(shí)間使用,測試過程如圖11-10所示。
圖11-10說明PipelineModel的測試過程與圖11-9的管道有相同的Stage數(shù)量。但是圖11-9的管道中的所有Estimator在此時(shí)都已經(jīng)變?yōu)門ransformer。當(dāng)在測試數(shù)據(jù)集上調(diào)用PipelineModel的transform()方法時(shí),數(shù)據(jù)在管道中按序通過。每個(gè)Stage的transform()方法都會更新數(shù)據(jù)集,并將數(shù)據(jù)集傳遞給下一個(gè)Stage。
剛才介紹的例子中,管道是線性的,即每個(gè)stage都使用由上一個(gè)stage生產(chǎn)的數(shù)據(jù)。只要數(shù)據(jù)流圖構(gòu)成了DAG,它就有可能不是線性的。如果管道構(gòu)成了DAG,那么這些Stage就必須指定拓?fù)漤樞颉?/p>
17.2?管道API介紹
Spark ML的Transformer和Estimator指定參數(shù)具有統(tǒng)一的API。有兩種方式指定參數(shù):
- 給實(shí)例設(shè)置參數(shù)。例如,Ir是LogisticRegression的實(shí)例,可以調(diào)用lr.setMaxIter(10)使得調(diào)用lr.fit()時(shí)最多迭代10次。
- 傳遞ParamMap給fit()或者transform()方法。通過這種方式指定的參數(shù)值將會覆蓋所有由set方式指定的參數(shù)值。
下面的例子演示了Estimator、Transformer和Param的使用。
17.3?交叉驗(yàn)證
?模型選擇是Spark ML中很重要的課題。通過對整個(gè)管道的調(diào)整,而不是對管道中的每個(gè)元素的調(diào)整,促成對管道模型的選擇。當(dāng)前spark.ml使用CrossValidator支持模型選擇。CrossValidator本身攜帶一個(gè)Estimator、一組ParamMap以及一個(gè)Evaluator。CrossValidator開始先將數(shù)據(jù)集劃分為多組,每組都由訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集組成。例如,需要?jiǎng)澐?組,那么CrossValidator將生成三個(gè)數(shù)據(jù)集對(訓(xùn)練,測試)。每一對都使用2/3的數(shù)據(jù)用于訓(xùn)練,1/3的數(shù)據(jù)用于測試。CrossValidator會迭代ParamMap的集合。對于每個(gè)ParamMap,它都會訓(xùn)練給定的Estimator并使用給定的Evaluator計(jì)算。ParamMap將會產(chǎn)出最佳的計(jì)算模型(對多個(gè)數(shù)據(jù)集對求平均),CrossValidator最終使用這個(gè)最佳的ParamMap和整個(gè)數(shù)據(jù)集擬合Estimator。下邊的例子演示了CrossValidator的使用。使用ParamGridBuilder構(gòu)造網(wǎng)格參數(shù):hashingTF.numFeatures有3個(gè)值,r.regParam有2個(gè)值。這個(gè)網(wǎng)格將會有3*2=6個(gè)參數(shù)設(shè)置供CrossValidator選擇。使用了2組數(shù)據(jù)集對,那么一共有(3*2)*2=12種不同的模型被訓(xùn)練。
下面的代碼演示了交叉驗(yàn)證的使用。
轉(zhuǎn)載于:https://www.cnblogs.com/swordfall/p/9456222.html
總結(jié)
以上是生活随笔為你收集整理的Spark MLlib 机器学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 其他格式数据与数组互转
- 下一篇: Microsoft Bot Framew