译者续:本文会持续更新。
MLlib 是spark 机器学习的库,它的目标是使机器学习算法能更容易上手。这个库包含通用学习算法和工具集,包括:分类,回归,聚类,协同过滤,降维,以及深层优化策略和上层管道API(pipeline).
分为两个包:
1 spark.mllib 包含基于RDD的原始API
2 spark.ml 包含上层操作DataFrame 的API, 可以构造机器学习管道,
推荐使用spark.ml 包,因为DataFrame API 在机器学习应用中更通用和灵活。但我们会持续支持spark.mllib 也配合spark.ml的开发。开发者可以提交新算法到spark.ml 包,但用户可以持续关注spark.mllib和使用spark.mllib中的特性。例如,特征抽取和特征变换。
一下列出机器学习包中主要的功能,并讲解细节。
spark.mllib: data types, algorithms, and utilities
ostreaming significance testing
·Classification and regression
olinear models (SVMs, logistic regression, linear regression)
oensembles of trees (Random Forests and Gradient-Boosted Trees)
oalternating least squares (ALS)
opower iteration clustering (PIC)
olatent Dirichlet allocation (LDA)
osingular value decomposition (SVD)
oprincipal component analysis (PCA)
·Feature extraction and transformation
一 数据类型 – MLlib
MLlib支持单个节点的本地向量和本地指标,同时也支持基于RDDs的分布式指标集。本地向量和本地指标可看做数据模型的对外接口,而底层的线性代数操作有Breeze 和 jblas提供。监督学习中的训练样本在MLlib中称为,“标签点”(本人注解,即有类别信息的样本点数据)
1.1本地向量
本地向量有两个关键数据:0开始在索引和双精度浮点型值。MLlib支持两类本地向量:紧致向量和稀松向量。紧致向量是一个双精度浮点型向量元素组成的数组,稀松向量是两个同长度的数据,一个是非0向量指标数组,另一个是非0向量元素数组。如, 向量(1.0,0.0,3.0) 的紧致向量为[1.0,0.0,3.0] ,而对应的稀松向量为 (3, [0, 2], [1.0, 3.0])
,
此处
,3代表向量长度(本人注解:[0,2] 是向量中非0数据的指标集,[1.0,3.0] 是对应非0.0数据的值)
本地向量的基类是
Vector,我们提供两个实现:DenseVector 和SparseVector ,建议用户使用Vectors的工厂方法创建本地向量。
Scala Vector API:
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.Vector
Scala Vectors API
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.Vectors
importorg.apache.spark.mllib.linalg.{Vector,Vectors}
// Create a dense vector (1.0, 0.0, 3.0).
val dv:Vector=Vectors.dense(1.0,0.0,3.0)
// Create a sparse vector (1.0, 0.0, 3.0) by specifying its indices and values corresponding to nonzero entries.
val sv1:Vector=Vectors.sparse(3,Array(0,2),Array(1.0,3.0))
// Create a sparse vector (1.0, 0.0, 3.0) by specifying its nonzero entries.
val sv2:Vector=Vectors.sparse(3,Seq((0,1.0),(2,3.0)))
注意:
Scala默认import scala.collection.immutable.Vector , 在运行spark ML时,需要手动引入importorg.apache.spark.mllib.linalg.Vector.
1.2
标签点
标签点是本地向量
,可以使紧致向量,也可以使稀松向量。在ML
lib中
,
标签点用于监督学习算法
,
但是绑定双精度浮点类别标签后
,
也可以应用于回归和分类算法
。
在两类分类中
,
类别标签可选
0 或 1 , 对于多分类,类别标签 从0 到(总类别数-
1
)。
标签类使用
case classs LabeledPoint .
Scala LabdledPoint
API
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.regression.LabeledPoint
importorg.apache.spark.mllib.linalg.Vectors
importorg.apache.spark.mllib.regression.LabeledPoint
// Create a labeled point with a positive label and a dense feature vector.
valpos=LabeledPoint(1.0,Vectors.dense(1.0,0.0,3.0))
// Create a labeled point with a negative label and a sparse feature vector.
valneg=LabeledPoint(0.0,Vectors.sparse(3,Array(0,2),Array(1.0,3.0)))
1.2.1
稀松数据
实践中经常会碰到需要训练稀松数据集,MLLib支持从LIBSVN格式直接读取训练数据,对于LIBSVN和LIBLINEAR的用户对这种格式并不陌生。这种格式是文本文件,每行是一个标签点,这个点标识一个稀松特征向量。
label index1:value1 index2:value2 ...
注意此处文件中向量的索引是从1开始,加载到spark 后自动转换为从0 开始。
MLUtils.loadLibSVMFile
读取按LIBSVN格式存储的训练测试数据
Scala MLUtils API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.util.MLUtils
importorg.apache.spark.mllib.regression.LabeledPoint
importorg.apache.spark.mllib.util.MLUtils
importorg.apache.spark.rdd.RDD
valexamples:RDD[LabeledPoint]=MLUtils.loadLibSVMFile(sc,"data/mllib/sample_libsvm_data.txt")
1.3本地矩阵
本地矩阵是单个主机上的矩阵,具有特性:整数的矩阵索引和(双精度)浮点矩阵元素。MLLib支持紧致矩阵,矩阵元素按列优先存储在数组中,稀松矩阵,矩阵非0元素按列优先存储在CSC格式(Compressed Sparse Column,压缩稀松列),如下面紧致矩阵:
(3,2)的矩阵存储在数组中为: [1.0, 3.0, 5.0, 2.0, 4.0, 6.0]
本地矩阵的基类是Matrix , 同时提供两种本地矩阵实现:DenseMatrix,和SparseMatrix 。 建议用户使用Matrices 类的工厂方法创建本地矩阵。再次提醒,矩阵是按列优先的数组存储。
Scala Matrix
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.Matrix
Matrices API :
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.Matrices
importorg.apache.spark.mllib.linalg.{Matrix,Matrices}
// Create a dense matrix ((1.0, 2.0), (3.0, 4.0), (5.0, 6.0))
valdm:Matrix=Matrices.dense(3,2,Array(1.0,3.0,5.0,2.0,4.0,6.0))
// Create a sparse matrix ((9.0, 0.0), (0.0, 8.0), (0.0, 6.0))
valsm:Matrix=Matrices.sparse(3,2,Array(0,1,3),Array(0,2,1),Array(9,6,8))
1.4 分布式矩阵
分布式矩阵是分布在一个或多个RDDs的矩阵,具有特征:长整型矩阵索引,双精度浮点矩阵元素。考虑到将分布式矩阵转换为其他形式需要全局shuffle, 这样很消耗时间,因此有必要仔细斟酌选择合适形式来存储分布式大矩阵。暂时支持三种类型的分布式矩阵。
第一类是RowMatrix .RowMatrix 是面向行存储的矩阵,因此忽略行索引。例如,特征向量。这种矩阵每一行是一个本地向量(RDD)。假设每行的数据并不多,这样本地矩阵可以在单节点的driver间自由通信,也可以在单节点上存储和操作。
第二类是IndexedRowMatrix
,它比RowMatrix多了行索引,这个行索引可以标记行并用于关联操作。
第三类是CoordinateMatrix
,这种举证按CCO链表(coordinate list, https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29 ) 格式存储, 链表每个元素是一个RDD。
注意:
分布式矩阵的RDD的行和列在cache时必须是确定的,否则会出错。
1.4.1 RowMatrix
因为每行是一个本地向量,因此矩阵的列数限制在integer的范围,在实际中不建议太大。
RowMatrix 可以由一个RDD[Vector]实例创建,然后可以做列统计和分解。QR分解的形式 A = QR , 此处Q是一个正交矩阵,而R是一个上三角矩阵。了解更多奇异值分解(SVD,https://en.wikipedia.org/wiki/Singular_value_decomposition)和主成分分析(PCA,https://en.wikipedia.org/wiki/Principal_component_analysis) ,请看降维章节, http://spark.apache.org/docs/latest/mllib-dimensionality-reduction.html。
Scala RowMatrix API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.RowMatrix
importorg.apache.spark.mllib.linalg.Vector
importorg.apache.spark.mllib.linalg.distributed.RowMatrix
val rows:RDD[Vector]=...// an RDD of local vectors
// Create a RowMatrix from an RDD[Vector].
val mat:RowMatrix=newRowMatrix(rows)
// Get its size.
val m= mat.numRows()
val n= mat.numCols()
// QR decomposition
val qrResult= mat.tallSkinnyQR(true)
1.4.2 IndexedRowMatrix
IndexedRowMatrix 可由RDD[IndexedRow] 实例创建,此处IndexedRow 封装为(Long, Vector) . IndexedRowMatrix 去掉行索引就变成了RowMatrix。
Scala IndexedRowMatrix API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.IndexedRowMatrix
importorg.apache.spark.mllib.linalg.distributed.{IndexedRow,IndexedRowMatrix,RowMatrix}
val rows:RDD[IndexedRow]=...// an RDD of indexed rows
// Create an IndexedRowMatrix from an RDD[IndexedRow].
val mat:IndexedRowMatrix=newIndexedRowMatrix(rows)
// Get its size.
val m= mat.numRows()
val n= mat.numCols()
// Drop its row indices.
val rowMat:RowMatrix= mat.toRowMatrix()
1.4.3 CoordinateMatrix ( 调和矩阵)
Coordinatematrix 是分布式矩阵,所有元素做成的RDD对象。其中Tuple3 形如( i : Long , j : Long, value : Double ) ,此处i 是行索引, j 是列索引, value 是元素的值。CoordinateMatrix 只在当矩阵行和列都很大时,同时矩阵非0 元素很稀松。
CoordinateMatrix 可以从RDD[MatrixEntry]实例创建,此处MatrixEntry 封装为(Long , Long, Double )。 CoordinateMatrix 调用toIndexeedRowMatrix 方法可以将CoordinateMatrix 矩阵转化为IndexedRowMatrix 矩阵,其他coordinateMatrix 的计算暂时还不支持。
Scala CoordinateMatrix API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.CoordinateMatrix
importorg.apache.spark.mllib.linalg.distributed.{CoordinateMatrix,MatrixEntry}
val entries:RDD[MatrixEntry]=...// an RDD of matrix entries
// Create a CoordinateMatrix from an RDD[MatrixEntry].
val mat:CoordinateMatrix=newCoordinateMatrix(entries)
// Get its size.
val m= mat.numRows()
val n= mat.numCols()
// Convert it to an IndexRowMatrix whose rows are sparse vectors.
val indexedRowMatrix= mat.toIndexedRowMatrix()
1.4.4 BlockMatrix (分块矩阵)
BlockMatrix是分布式矩阵RDD[MarixBlock],此处MatrixBlock是元组((Int, Int) , Matrix ), 其中(Int, Int) 是矩阵块的索引, Matrix 是给定矩阵块索引的子矩阵,矩阵维度(是数组的长度)rowsPerBlock
* colsPerBlock
。BlockMatrix矩阵支持
add 和 multiply 方法和另一个同维度的BlockMatrix 计算。
H
elper
函数
validate
可以校验
BlockMatrix 是否设置正确。
BlockMatrix 矩阵可以有IndexedRowMatrix 或 CoordinateMatrix 调用toBlockMatrix 方法得到, toBlockMatrix 方法默认创建 1024 *
1024 的块矩阵
。
用户可以调用接口
toBlockMatrix(rowsPerBlock , colsPerBlock ) 修改矩阵维度。
Scala BlockMatrix API :
http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.BlockMatrix
importorg.apache.spark.mllib.linalg.distributed.{BlockMatrix,CoordinateMatrix,MatrixEntry}
val entries:RDD[MatrixEntry]=...// an RDD of (i, j, v) matrix entries
// Create a CoordinateMatrix from an RDD[MatrixEntry].
val coordMat:CoordinateMatrix=newCoordinateMatrix(entries)
// Transform the CoordinateMatrix to a BlockMatrix
val matA:BlockMatrix= coordMat.toBlockMatrix().cache()
// Validate whether the BlockMatrix is set up properly. Throws an Exception when it is not valid.
// Nothing happens if it is valid.
matA.validate()
// Calculate A^T A.
val ata= matA.transpose.multiply(matA)
2 基本统计 – spark.mllib
2.1 统计概览
在Statistics类中提供基本列统计RDD[Vector]功能
colStats()返回MultivariateStatisticalSummary 的实例,这个实例可以按列计算最大,最小,均值,方差,非0个数统计,列的1范数。
Scala MultivariateStatisticalSummary API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.MultivariateStatisticalSummary
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.mllib.stat.{MultivariateStatisticalSummary, Statistics}
val observations: RDD[Vector] = ... // an RDD of Vectors
// Compute column summary statistics.
val summary: MultivariateStatisticalSummary = Statistics.colStats(observations)
println(summary.mean) // a dense vector containing the mean value for each column
println(summary.variance) // column-wise variance
println(summary.numNonzeros) // number of nonzeros in each column
2.2 相关统计
计算两个数据序列(可以使向量或矩阵)的相关系数。在spark.mllib中,我们提供成对计算相关系数,实现了Pearson’s相关和Spearman’s相关。相关统计的结果依赖于计算对象,如果是两个RDD[Double]的计算,结果是Double类型,如果是两个RDD[Vector]计算,结果是一个Matrix矩阵。
Scala Statistics API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.Statistics
import org.apache.spark.SparkContext
import org.apache.spark.mllib.linalg._
import org.apache.spark.mllib.stat.Statistics
val sc: SparkContext = ...
val seriesX: RDD[Double] = ... // a series
val seriesY: RDD[Double] = ... // must have the same number of partitions and cardinality as seriesX
// compute the correlation using Pearson's method. Enter "spearman" for Spearman's method. If a
// method is not specified, Pearson's method will be used by default.
val correlation: Double = Statistics.corr(seriesX, seriesY, "pearson")
val data: RDD[Vector] = ... // note that each Vector is a row and not a column
// calculate the correlation matrix using Pearson's method. Use "spearman" for Spearman's method.
// If a method is not specified, Pearson's method will be used by default.
val correlMatrix: Matrix = Statistics.corr(data, "pearson")
2.3 分层采样(Stratified sampling)
在spark.mllib中提供计算原始RDD 键值对的分层采样方法:sampleByKey 和 sampleByKeyExact 。在分层采样中,键可以看做标签类,相应的值可以看做属性。如,键可以使男人或女人,文档ID,相应的值可以使人的年龄或文档的单次。 sampleByKey 方法随机采样一系列观测值,过程就像逐个遍历所有样本点,通过抛银币决定取舍,因此只需要确定采样点个数。sampleByKeyExact 比分层随机采样方法sampleByKey需要更多地样本,才能保证采样点个数有99.99%的置信度,sampleByKeyExact暂不支持python.
sampleByKeyExact() 采样由[ f_k , n_k ] 完全决定, 对任意一个键k 属于 K 键集合,f_k是预期键对应采样点值得占比(分数),n_k 是这个键k在整个集合中值的个数。无放回采样(即采样的数据取走,不会出现重复) 方法需要一个参数(withReplacement默认是false) , 而又放回采样方法需要两个参数。
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.rdd.PairRDDFunctions
val 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
// Get an exact sample from each stratum
val approxSample = data.sampleByKey(withReplacement = false, fractions)
val exactSample = data.sampleByKeyExact(withReplacement = false, fractions)
2.4 假设检验
假设检验在统计上用于判定统计结果又多大统计意义,及统计结果有多大置信度。Spark.mllib 暂支持Pearson’s chi-squared 检验,检验结果的适用性和独立性。输入数据需要验证适用性和独立性。适用性检验需要输入Vector , 独立性需要数据Matrix 。
Spark.mllib 支持输入RDD[LabledPoint] ,使用chi-squared独立性来决定特征的选择。
Statistics 提供方法运行Pearson’s chi-squared 检验,下例用于假设检验。
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 = ... // a vector composed of the frequencies of events
// compute the goodness of fit. If a second vector to test against is not supplied as a parameter,
// the test runs against a uniform distribution.
val goodnessOfFitTestResult = Statistics.chiSqTest(vec)
println(goodnessOfFitTestResult) // summary of the test including the p-value, degrees of freedom,
// test statistic, the method used, and the null hypothesis.
val mat: Matrix = ... // a contingency matrix
// conduct Pearson's independence test on the input contingency matrix
val independenceTestResult = Statistics.chiSqTest(mat)
println(independenceTestResult) // summary of the test including the p-value, degrees of freedom...
val obs: RDD[LabeledPoint] = ... // (feature, label) pairs.
// The contingency table is constructed from the raw (feature, label) pairs and used to conduct
// the independence test. Returns an array containing the ChiSquaredTestResult for every feature
// against the label.
val featureTestResults: Array[ChiSqTestResult] = Statistics.chiSqTest(obs)
var i = 1
featureTestResults.foreach { result =>
println(s"Column $i:\n$result")
i += 1
} // summary of the test
Statistics 提供1-sample, 2-sided Kolmogorov-Smirnov检验概率分布是否相等。提供理论分布名称和理论分布参数,或者根据已知理论分布计算累计分布函数,用户可以检验样本点是否出自来验证概率分布。在特殊例子中,如正态分布,不用没有提供正态分布参数,则检验会使用标准正态分布参数。
Scala Statistics API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.Statistics
import org.apache.spark.mllib.stat.Statistics
val data: RDD[Double] = ... // an RDD of sample data
// run a KS test for the sample versus a standard normal distribution
val testResult = Statistics.kolmogorovSmirnovTest(data, "norm", 0, 1)
println(testResult) // summary of the test including the p-value, test statistic,
// and null hypothesis
// if our p-value indicates significance, we can reject the null hypothesis
// perform a KS test using a cumulative distribution function of our making
val myCDF: Double => Double = ...
val testResult2 = Statistics.kolmogorovSmirnovTest(data, myCDF)
2.5 流式显著性测试
Spark.mllib 提供在线测试实现,如A/B在线测试。此测试需要在spark streaming DStream[(Boolean, Double)] 上使用,每个流单元的第一个元素是逻辑真假,假代表对照组(false),而真代表实验组(true) , 第二个元素是观测值。
流式显著性检验支持这两个参数:
1 peacePeriod (平稳周期), 默认最初启动后可以忽略的数据组数。
1 windowSize (窗尺寸) , 每次假设检验使用的数据批次数,若设为0 , 则累计处理之前所有批次。
StreamingTest 支持流式假设检验。
val data = ssc.textFileStream(dataDir).map(line => line.split(",") match {
case Array(label, value) => BinarySample(label.toBoolean, value.toDouble)
})
val streamingTest = new StreamingTest()
.setPeacePeriod(0)
.setWindowSize(0)
.setTestMethod("welch")
val out = streamingTest.registerStream(data)
out.print()
完整例子代码见:examples/src/main/scala/org/apache/spark/examples/mllib/StreamingTestExample.scala
2.6 随机数发生器
随机数发生器在随机算法,随机模板和性能测试中很有用。Spark.mllib 的随机发生器RDD 带i.i.d. 随机数据来自给定分布:均匀分布, 标准正态, Possion (泊松分布)。
RandomRDDs 提供工厂方法来生成随机双精度浮点RDD 和 随机向量RDD。下例生辰随机双精度浮点RDD, 这些随机值来自标准正态分布N(0,1), 做平移和伸缩后映射到N(1,4)。
Scala RandomRDD API : http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.random.RandomRDDs
import org.apache.spark.SparkContext
import org.apache.spark.mllib.random.RandomRDDs._
val sc: SparkContext = ...
// Generate a random double RDD that contains 1 million i.i.d. values drawn from the
// standard normal distribution `N(0, 1)`, evenly distributed in 10 partitions.
val u = normalRDD(sc, 1000000L, 10)
// Apply a transform to get a random double RDD following `N(1, 4)`.
val v = u.map(x => 1.0 + 2.0 * x)
2.6 核密度估计
核密度估计在经验概率分布图中用处很大,这种分布图不需要假设观测值来自特定的某个分布。通过给定点集,来计算随机变量的概率密度函数。通过计算经验分布在特定点的PDF(偏导数),作为标准正态分布在每个采样点附近的PDF。
KernelDensity 提供方法计算RDD采样点集的核密度估计,见下例:
Scala KernelDensity API: http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.KernelDensity
import org.apache.spark.mllib.stat.KernelDensity
import org.apache.spark.rdd.RDD
val data: RDD[Double] = ... // an RDD of sample data
// Construct the density estimator with the sample data and a standard deviation for the Gaussian
// kernels
val kd = new KernelDensity()
.setSample(data)
.setBandwidth(3.0)
// Find density estimates for the given values
val densities = kd.estimate(Array(-1.0, 2.0, 5.0))
3 分类和回归—spark.mllib
Spark.mllib 实现以下ML问题: 两个标签类的分类, 多个标签类的分类,和回归分析。
下表列出每类问题的支持算法:
Problem Type | Supported Methods |
Binary Classification | linear SVMs, logistic regression, decision trees, random forests, gradient-boosted trees, naive Bayes 线性支持向量机,逻辑回归,决策树,随机森林,梯度提升决策树, 朴素贝叶斯决策 |
Multiclass Classification | logistic regression, decision trees, random forests, naive Bayes 逻辑回归,决策树,随机森林,朴素贝叶斯决策 |
Regression | linear least squares, Lasso, ridge regression, decision trees, random forests, gradient-boosted trees, isotonic regression 线性最小二乘法,最小化的绝对收缩和选择算子,岭回归, 决策树,随机森林,梯度提升决策树,保序回归 |
3.1 线性模型 –spark.mllib
o Linear Support Vector Machines (SVMs)
o Linear least squares, Lasso, and ridge regression
3.1.1 数学公式
许多标准机器学习问题可以转化为凸优化问题,如, 凸函数f 的最小值是依赖于d维向量w(称为权重向量),可以把问题转化为:
求 \min_{w \in R^d }{ f(x) } 问题。此处f 函数形如:
F(w) = \Lamda * R(w) + frac{1,n} * \Sum|_{i=1}|^{n} {L(w;x_i,y_i)|
此处向量 x_i \in R^d是训练测试数据, 1 <= I <= n , y_i \in R是相应的类标签,类标签在分类问题是需要预测的。如果方法是线性的,如果
L(w;x,y) 可以表示成w^{T} x 和 y 的函数, 下面会讲解不是凸优化问题的情况。
目标函数f 有两个点:正规化决定模型的复杂程度,损失决定模型的误差,损失函数L(w; . , . ) 是w 的凸函数,正规化参数 \Lamda >= 0 (名为regParam ) 来权衡两个目标:错误最小 和模型复杂度最低 (为了防止过拟合)。
3.1.1.1 损失函数
下表总结损失函数,集损失函数的梯度函数
3.1.1.2 正规化
正则化可以使模型处理相对简单,并且可以避免模型过拟合。支持以下正则化 spark.mllib
此处 sign(w) 是符号向量,每个元素是w 向量相应位置的符号函数 sign(x_i)
L2-正规化相对L1-正规化处理简单,是因为L2的正规函数是连续光滑函数,而L1的正规函数则不是。L1正规化可以使权向量中稀少的值变得不那么重要,使模型在特征选择上处理更容易理解。弹性网络(elastic net)是L1和L2正规化的组合。不建议训练模型时不适用正则化,特别是训练向本数很少时。
3.1.1.3 最优化
线性方法使用凸最优化方法优化目标函数。Spark.mllib使用两种方法SGD 和L-BFGS(见最优化章节)。当前,大多数算法APIs 支持随机梯度下降(SGD)和大部分支持L-BFGS。
3.1.2分类
分类算法的目标是把数据分门别类。最简单分类是两分类问题,即分成两类(正类和负类)。如果多余两类,一般称为多类别分类问题。Spark.mllib 支持两种线性分类:线性支持向量机(SVM)和逻辑回归。 线性SVN 只支持两分类,而逻辑回归支持两分类和多分类。这两种算法都支持L1和L2正规化。训练集为RDD[LabeledPoint] 在MLlib , 而类标签为 0, 1,2,… 。 注意,数学公式中,两分类的类标签表示为: +1 (正类) 和 -1 (负类)。
3.1.2.1 线性支持向量机(SVM)
线性SVN是处理大多数分类问题的首选,线性方法描述见上面表达式(1),其中损失函数形如:
L(w;x,y) := max{ 0, 1 – y w^t x }
默认,线性SVN训练集需要使用L2正规化。同时支持L1正规化,在此情况下, 变成线性算法。
线性SVN算法输出SVN模型。给定新数据点,表示为x , 模型基于w^T x 的 预测。 默认, 如果 w^T x >= 0 , 则归为正类,否则归为负类。
例子:
下例中展示如何加载测试数据,执行算法训练,并预测结果与训练集的错误。
Scala SVMWithSGD API : https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.classification.SVMWithSGD
Scala SVMModel API :
import org.apache.spark.mllib.classification.{SVMModel, SVMWithSGD}
import org.apache.spark.mllib.evaluation.BinaryClassificationMetrics
import org.apache.spark.mllib.util.MLUtils
// Load training data in LIBSVM format.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split data into training (60%) and test (40%).
val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0).cache()
val test = splits(1)
// Run training algorithm to build the model
val numIterations = 100
val model = SVMWithSGD.train(training, numIterations)
// Clear the default threshold.
model.clearThreshold()
// Compute raw scores on the test set.
val scoreAndLabels = test.map { point =>
val score = model.predict(point.features)
(score, point.label)
}
// Get evaluation metrics.
val metrics = new BinaryClassificationMetrics(scoreAndLabels)
val auROC = metrics.areaUnderROC()
println("Area under ROC = " + auROC)
// Save and load model
model.save(sc, "myModelPath")
val sameModel = SVMModel.load(sc, "myModelPath")
SVMWitSGD.train() 方法默认使用L2正规化,且使用正规参数1.0 。如果想修改此默认,需要创建新的SVMWithSGD 实例,并用setter方法重新配置。其他spark.mllib算法也支持setter方法重新配置,例如,下例给出L1正规化 且SVM正规化参数位0.1 ,训练样本迭代200次。
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)
3.1.2.2 逻辑回归
逻辑回归广泛用于预测两类别分类问题。它也符合等式(1) , 并且损失函数形如:
L(w;x,y) := log( 1 + exp(-y w^T x) )
对于两类别分类问题,算法输出两类别逻辑回归模型,给定新的测试点,记为x , 模型通过逻辑函数
F(z) = 1 / { 1 + e^(-z)}
此处 z = w^T x , 如果 f(w^T x) > 0.5 , 认为是正类, 否则认为是负类, 可以看到此分类方法分类和SVN不太一样,多了一个随机函数f( )
两类别分类逻辑回归可以推广到多类别逻辑回归,用来处理多类别分类问题。例如,假设有K可能的输出结果,选取其中一个作为对比值,剩下K-1个输出值分别去和对比值做两类别回归。在spark.mllib , 这个对比值就是类别0 ,详见 统计学习基础:http://statweb.stanford.edu/~tibs/ElemStatLearn/
对于多类别分类问题,算法会输出K-1个逻辑回归模型,给定一个新测试点,带入K-1个模型算出最大概率值得类别,记为预测结果。
我们实现两个算法解决逻辑回归:小批梯队下降法(mini-batch gradient descent)和L-BFGS , 我们建议优先选L-BFGS,它的收敛性更快一些。
例子:
下面例子将如何加载多类别数据集,将数据集分为训练和测试,使用LogisticRegressionWithLBFGS 做逻辑回归。模型再用测试数据集去评估优劣。
Scala LogisticRegressionWithLBFGS API : https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS
Scala LogisticRegressionModel API :
import org.apache.spark.SparkContext
import org.apache.spark.mllib.classification.{LogisticRegressionWithLBFGS, LogisticRegressionModel}
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.util.MLUtils
// Load training data in LIBSVM format.
val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
// Split data into training (60%) and test (40%).
val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L)
val training = splits(0).cache()
val test = splits(1)
// Run training algorithm to build the model
val model = new LogisticRegressionWithLBFGS()
.setNumClasses(10)
.run(training)
// Compute raw scores on the test set.
val predictionAndLabels = test.map { case LabeledPoint(label, features) =>
val prediction = model.predict(features)
(prediction, label)
}
// Get evaluation metrics.
val metrics = new MulticlassMetrics(predictionAndLabels)
val precision = metrics.precision
println("Precision = " + precision)
// Save and load model
model.save(sc, "myModelPath")
val sameModel = LogisticRegressionModel.load(sc, "myModelPath")
3.1.3 回归
3.1.3.1 线性最小二乘,Lasso , 岭回归
最小二乘在回归问题中经常使用。同样是线性算法符合公式(1) , 损失函数形为:
使用不同的正规化方法得到不同最小二乘法: 正交最小二乘法或线性最小二乘法(不适用正规化);岭回归使用L2正规化;Lasso使用L1正规化。对所有这些模型,平均损失(训练集错误率) 1/n \SUM|_(i=1) ^n| ( w^T x_i – y_i )^2 , 称为均方误差。
例子:
下例展示如何加载训练数据,转化成标签点的RDD,例子使用LinearRegressionWithSGD 构建线性模型来预测类标签。最后计算均方差错误来评估拟合优度。
Scala LinearRegressionWithSGD API : https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.regression.LinearRegressionWithSGD
Scala LinearRegressionModel API : https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.regression.LinearRegressionModel
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.regression.LinearRegressionModel
import org.apache.spark.mllib.regression.LinearRegressionWithSGD
import org.apache.spark.mllib.linalg.Vectors
// Load and parse the data
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()
// Building the model
val numIterations = 100
val model = LinearRegressionWithSGD.train(parsedData, numIterations)
// Evaluate model on training examples and compute training error
val valuesAndPreds = parsedData.map { point =>
val prediction = model.predict(point.features)
(point.label, prediction)
}
val MSE = valuesAndPreds.map{case(v, p) => math.pow((v - p), 2)}.mean()
println("training Mean Squared Error = " + MSE)
// Save and load model
model.save(sc, "myModelPath")
val sameModel = LinearRegressionModel.load(sc, "myModelPath")
RidgeRegressionWithSGD 和 LassoWithSGD 使用同LinearRegressionWithSGD
为了运行上例代码,需要查看spark 快速指南中 Self-Contained Applications 章节(https://spark.apache.org/docs/latest/quick-start.html#self-contained-applications)
3.1.3.2 流线性回归
当数据是以流的形式进入模型,最好选取在线回归模型,更新数据每批生成的周期。Spark.mllib 流线性回归暂支持正交最小二乘。除了拟合度是计算每批次数据的到,拟合度计算方法和离线是一样。
例子
下例展示如何从文件生成训练数据流和测试数据流。把流数据解释为标签点,拟合在线线性回归模型,预测下一个流的类标签。
首先,引入必须的输入数据和模型类
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.regression.StreamingLinearRegressionWithSGD
然后生成训练数据流和测试数据流。假设StreamingContext ssc 已经生成,详见Spark Streaming Programming Guide (https://spark.apache.org/docs/latest/streaming-programming-guide.html#initializing)
下例中我们使用标签点来代表训练和测试数据,实际中建议测试数据使用无标签向量。
val trainingData = ssc.textFileStream("/training/data/dir").map(LabeledPoint.parse).cache()
val testData = ssc.textFileStream("/testing/data/dir").map(LabeledPoint.parse)
初始化模型权重为0
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()
现在可以把训练和测试数据流保存在不同的文件夹下。每行记录的数据点格式( y , [ x1,x2,x3]) ,此处y 是类标签, x1,x2,x3是特征向量。训练数据文件只要保存在/training/data/dir, 模型就会随时更新,测试数据文件保存在/testing/data/dir 下就会计算类标签预测。注意,训练数据越多,预测结果越好。
3.1.4 实现(开发者)
Spark.mllib实现了简单分布式版本的SGD(stochastic gradient descent),这个SGD是基于(underlying) 梯度下降法。所有提供的算法接受正规化参数作为输入(regParam) , 同时还有其他SGD的各种参数(stepSize , numIterations , miniBatchFraction ) 。 罪域每个参数,我们提供三种可能的正规化(none , L1 , L2 )
逻辑回归,L-BFGS版本的实现基于LogisticRegressionWithLBFGS类,这个实现支持两类别逻辑回归和多类别逻辑回归,而SGD只支持两类别逻辑回归。尽管,L-BFGS不支持L1正规化, 单SGD只支持L1正规化。当L1正规化不是必选是, 强烈推荐L-BFGS算法, 因为它收敛更快,比SGD算法更精确的逼近逆 Hessian 矩阵,这个Hessian 矩阵通过拟牛顿法(quasi-Newton methond)
算法的Scala 实现:
Python 调用scala 实现: PythonMLLibAPI.
3.2 朴素贝叶斯
3.3 决策树
· Node impurity and information gain
· Problem specification parameters
· Scaling
· Examples
决策树算法常用于机器学习中分类和回归问题,由于以下优点,决策树得到广泛使用:
1 处理特征分类时,对分类结果容易直观解释
2 容易扩展到多类情况
3 不去要对特征向量进行规整()
4 可以处理非线性问题
5 可以直观观察特征的比对交互过程
决策树算法族,诸如随机森林和随机深林的提升算法在处理分类和回归问题是效率最高。
Spark.mllib的决策树使用连续特征和归类特征,应用于两类别分类和多类别分类,以及回归问题。决策树实现按行分片处理,最多允许分布式训练百万行数据。
随机森林和梯度提升树详见 Ensembles guide (https://spark.apache.org/docs/latest/mllib-ensembles.html)
3.3.1 基本算法
决策树是贪婪算法,它会按二分去遍历整个特征向量空间。算法预测相同标签类的叶节点集。每一种分片的结果都是在决策节点上,所有可能的划分方法中选取最优的方法,选取最优的依据是信息增益(information gain) 最大化。 换句话说,在每个决策节点,选取使 argmax(s) IG(D,s) , 信息增益最大化的参数s ,此处 , IG(D,s) 是在数据集D 上应用划分方法s 所得到的信息增益。
3.3.1.1 节点混杂度和信息增益
节点混杂度是测量节点上标签集均一性。当前实现两种分类混杂度(Gini 混杂度和熵), 一种回归混杂度。
信息增益不同于父节点的混杂度,以及子节点的混杂度带权重之和。假设划分s 将数据集D 划分为D1 和D2 ,其中D1有N1个元素,D2有N2个元素。
信息增益
IG(D,s) = Impurity(D) – N1/N Impurity(D1) – N2/N Impurity(D2)
3.3.1.2 拆分可选集
3.3.1.2.1 连续特征
在单机上小数据集上,给定特征向量,对连续特征的划分备选集
spark.ml: high-level APIs for ML pipelines
·Overview: estimators, transformers and pipelines
·Extracting, transforming and selecting features
·Classification and regression
很多降维算法还没有完全在spark.ml中实现,用户可以自己把spark.mllib的实现和spark.ml中算法结合,构造自己的降维算法。
依赖