Spark MLlib之线性回归源代码分析
1.理论基础
线性回归(Linear Regression)问题属于监督学习(Supervised Learning)范畴,又称分类(Classification)或归纳学习(Inductive Learning);这类分析中训练数据集中给出的数据类标是确定的。机器学习的目标是,对于给定的一个训练数据集,通过不断的分析和学习产生一个联系属性集合和类标集合的分类函数(Classification
Function)或预測函数(Prediction Function),这个函数称为分类模型(Classification Model)或预測模型(Prediction Model);通过学习得到的模型能够是一个决策树,规格集,贝叶斯模型或一个超平面。通过这个模型能够对输入对象的特征向量预測或对对象的类标进行分类。
回归问题中通常使用最小二乘(Least Squares)法来迭代最优的特征中每一个属性的比重,通过损失函数(Loss Function)或错误函数(Error Function)定义来设置收敛状态,即作为剃度下降算法的逼近參数因子。
2.矩阵向量运算库jblas介绍
因为spark MLlib中使用jlbas的线性代数运算库,因此学习和掌握jlbas库中主要的运算,对分析和学习spark中MLlib非常多算法非常有帮助。以下使用jlbas中DoubleMatrix矩阵对jlbas中基本运算进行简介:
val matrix1 = DoubleMatrix.ones(10, 1) //创建全部值为1的10*1矩阵
val matrix2 = DoubleMatrix.zeros(10, 1) //创建全部值为0的10*1矩阵
matrix1.put(1, -10)
val absSum = matrix1.norm1() //绝对值之和
val euclideanNorm = matrix1.norm2() //欧几里德距离
val matrix3 = (matrix1.addi(matrix2))
val matrix4 = new DoubleMatrix(1, 10, (1 to 10).map(_.toDouble): _*) //创建Double向量对象
println("print init value:matrix3=" + matrix3)
println("print init value:matrix4=" + matrix4)
println("matrix sub matrix:" + matrix3.sub(matrix4) + "," + matrix4.sub(10)) //减法运算
println("matrix add matrix:" + matrix3.add(matrix4) + "," + matrix4.add(10)) //加法运算
println("matrix mul matrix:" + matrix3.mul(matrix4) + "," + matrix4.mul(10)) //乘法运算
println("matrix div matrix:" + matrix3.div(matrix4) + "," + matrix4.div(10)) //除法运算
println("matrix dot matrix:" + matrix3.dot(matrix4)) //向量积 val matrix5 = DoubleMatrix.ones(10, 10)
println("N*M Vector Matrix sub OP:\n" + matrix5.subRowVector(matrix4) + "\n" + matrix5.subColumnVector(matrix4)) //多对象减法运算
println("N*M Vector Matrix add OP:\n" + matrix5.addRowVector(matrix4) + "\n" + matrix5.addColumnVector(matrix4)) //多对象加法运算
println("N*M Vector Matrix mul OP:\n" + matrix5.mulRowVector(matrix4) + "\n" + matrix5.mulColumnVector(matrix4)) //多对象乘法运算
println("N*M Vector Matrix div OP:\n" + matrix5.divRowVector(matrix4) + "\n" + matrix5.divColumnVector(matrix4)) //多对象除法运算
3.梯度下降(Gradient Descent)算法介绍
梯度下降算法用于在迭代过程中逐渐降阶,不断更新特征权重向量,从而得到无限接近或拟合的最优特征权重向量 ;梯度下降算法主要有两种,第一种是批量梯度下降(Batch Gradient Descent)算法。此种方式实现过程是对权重向量进行累加,然后批量更新的一种方式,一般不有用于大规模数据集处理;第二种是随机梯度下降(Stochastic
Gradient Descent)算法。这样的方式对给定训练数据集中每一个对象都进行权重计算和更新,在某些情况下easy收敛到局部最优解上。Spark MLlib库中主要使用随机梯度下降算法。为了更好的理解MLlib库中随机梯度算法(MLlib库中类后缀名以SGD结尾的全部算法)实现,
批量梯度下降算法的理论公式:
随机梯度下降算法的理论公式:
以下是使用随机梯度算法进行直线拟合的Demo:
def sgdDemo {
val featuresMatrix: List[List[Double]] = List(List(1, 4), List(2, 5), List(5, 1), List(4, 2))//特征矩阵
val labelMatrix: List[Double] = List(19, 26, 19, 20)//真实值向量
var theta: List[Double] = List(0, 0)
var loss: Double = 10.0
for {
i <- 0 until 1000 //迭代次数
if (loss > 0.01) //收敛条件loss<=0.01
} {
var error_sum = 0.0 //总误差
var j = i % 4
var h = 0.0
for (k <- 0 until 2) {
h += featuresMatrix(j)(k) * theta(k)
} //计算给出的測试数据集中第j个对象的计算类标签
error_sum = labelMatrix(j) - h //计算给出的測试数据集中类标签与计算的类标签的误差值
var cacheTheta: List[Double] = List() for (k <- 0 until 2) {
val updaterTheta = theta(k) + 0.001 * (error_sum) * featuresMatrix(j)(k)
cacheTheta = updaterTheta +: cacheTheta
} //更新权重向量
cacheTheta.foreach(t => print(t + ","))
print(error_sum + "\n")
theta = cacheTheta
//更新误差率
var currentLoss: Double = 0
for (j <- 0 until 4) {
var sum = 0.0
for (k <- 0 until 2) {
sum += featuresMatrix(j)(k) * theta(k)
}
currentLoss += (sum - labelMatrix(j)) * (sum - labelMatrix(j))
}
loss = currentLoss
println("loss->>>>" + loss / 4 + ",i>>>>>" + i)
}
}
4.MLlib线性回归源代码分析
MLlib中可用的线性回归算法有:LinearRegressionWithSGD。RidgeRegressionWithSGD,LassoWithSGD;MLlib回归分析中涉及到的主要类有,GeneralizedLinearAlgorithm。GradientDescent。以下以对LinearRegressionWithSGD实现进行主要分析。
第一步:在使用LinearRegressionWithSGD算法之前首先将输入数据解析成包括类标和特征向量的LabeledPoint对象的RDD弹性分布式数据集合。
第二步:调用LinearRegressionWithSGD伴生对象中的train方法传输第一步创建的RDD集合和最大迭代次数,在train中主要实现创建一个新的LinearRegressionWithSGD对象,初始化梯度下降算使用使用最小二乘梯度下降算法SquaredGradient以及更新权重向量使用SimpleUpdater。运行父类GeneralizedLinearAlgorithm中的run方法进行权重向量和拦截參数计算,并返回训练得到的模型属性权重向量
LinearRegressionWithSGD伴生对象中train方法实现
def train(
input: RDD[LabeledPoint],
numIterations: Int,
stepSize: Double,//默认步长为1
miniBatchFraction: Double)//每次跌打使用的batch因子。默觉得1
: LinearRegressionModel =
{
new LinearRegressionWithSGD(stepSize, numIterations, miniBatchFraction).run(input)
}
LinearRegressionWithSGD中run方法实现
def run(input: RDD[LabeledPoint], initialWeights: Array[Double]) : M = { // Check the data properties before running the optimizer
if (validateData && !validators.forall(func => func(input))) {//预验证输入数据的合法性,validators中存储验证的全部方法
throw new SparkException("Input validation failed.")
}
// Prepend an extra variable consisting of all 1.0's for the intercept.
val data = if (addIntercept) {//判读是否须要加入intercept
input.map(labeledPoint => (labeledPoint.label, 1.0 +: labeledPoint.features))
} else {
input.map(labeledPoint => (labeledPoint.label, labeledPoint.features))
}//对象转化为元组(类标签,特征) val initialWeightsWithIntercept = if (addIntercept) {
0.0 +: initialWeights
} else {
initialWeights
}//初始化权重特征 val weightsWithIntercept = optimizer.optimize(data, initialWeightsWithIntercept)//返回优化后的权重 val (intercept, weights) = if (addIntercept) {
(weightsWithIntercept(0), weightsWithIntercept.tail)
} else {
(0.0, weightsWithIntercept)
} logInfo("Final weights " + weights.mkString(","))
logInfo("Final intercept " + intercept) createModel(weights, intercept)//使用计算后的权重向量和截距创建模型
}
当中optimizer.optimize(data, initialWeightsWithIntercept)是LinearRegressionWithSGD实现的核心。oprimizer的类型为GradientDescent,optimize方法中主要调用GradientDescent伴生对象的runMiniBatchSGD方法,返回当前迭代产生的最优特征权重向量。
GradientDescentd对象中optimize方法实现
def optimize(data: RDD[(Double, Array[Double])], initialWeights: Array[Double])
: Array[Double] = {
//返回优化后的权重向量。和迭代过程中误差
val (weights, stochasticLossHistory) = GradientDescent.runMiniBatchSGD(
data,
gradient,
updater,
stepSize,
numIterations,
regParam,
miniBatchFraction,
initialWeights)
weights
}
GradientDescent伴生对象中runMiniBatchSGD方法实现
def runMiniBatchSGD(
data: RDD[(Double, Array[Double])],
gradient: Gradient,//SquaredGradient—平方剃度下降算法
updater: Updater,//SimpleUpdater
stepSize: Double,//1.0
numIterations: Int,//100
regParam: Double,//0.0
miniBatchFraction: Double,//1.0
initialWeights: Array[Double]) : (Array[Double], Array[Double]) = { val stochasticLossHistory = new ArrayBuffer[Double](numIterations) val nexamples: Long = data.count()
val miniBatchSize = nexamples * miniBatchFraction // Initialize weights as a column vector//创建一维向量,第一个參数为行数,第二个參数为列数,第三个參数開始为值
var weights = new DoubleMatrix(initialWeights.length, 1, initialWeights:_*)
var regVal = 0.0 for (i <- 1 to numIterations) {
/**
* 使用平方梯度下降算法
* gradientSum:本次选择迭代样本的梯度总和,
* lossSum:本次选择迭代样本的误差总和
*/
val (gradientSum, lossSum) = data.sample(false, miniBatchFraction, 42+i).map {
case (y, features) =>//(标签,特征)
val featuresCol = new DoubleMatrix(features.length, 1, features:_*)
val (grad, loss) = gradient.compute(featuresCol, y, weights)//(特征,标签。特征属性权重向量)
/**
* class SquaredGradient extends Gradient {
override def compute(data: DoubleMatrix, label: Double, weights: DoubleMatrix):
(DoubleMatrix, Double) = {
val diff: Double = data.dot(weights) - label//计算当前计算对象的类标签与实际类标签值之差
val loss = 0.5 * diff * diff//当前平方梯度下降值
val gradient = data.mul(diff)
(gradient, loss)
}
}
*/
(grad, loss)//当前训练对象的特征权重向量和误差
}.reduce((a, b) => (a._1.addi(b._1), a._2 + b._2)) //计算本次迭代所选训练数据新的特征权重向量之和和误差总和
/**
* NOTE(Xinghao): lossSum is computed using the weights from the previous iteration
* and regVal is the regularization value computed in the previous iteration as well.
*/
stochasticLossHistory.append(lossSum / miniBatchSize + regVal)//miniBatchSize=样本中对象数量*batch因子,regVal为回归因子
val update = updater.compute(
weights, gradientSum.div(miniBatchSize), stepSize, i, regParam)//weights:属性向量中设置的权重因子,regParam:为回归參数。stepSize:计算步长,i:当前迭代次数
/**
* class SimpleUpdater extends Updater {
* /**
* weihtsOld:上一次迭代计算后的特征权重向量
* gradient:本次迭代计算的特征权重向量
* stepSize:迭代步长
* iter:当前迭代次数
* regParam:回归參数
*/
override def compute(weightsOld: DoubleMatrix, gradient: DoubleMatrix,
stepSize: Double, iter: Int, regParam: Double): (DoubleMatrix, Double) = {
val thisIterStepSize = stepSize / math.sqrt(iter)//以当前迭代次数的平方根的倒数作为本次迭代趋近(下降)的因子
val normGradient = gradient.mul(thisIterStepSize)
(weightsOld.sub(normGradient), 0)//返回本次剃度下降后更新的特征权重向量
}
}
*
*/
weights = update._1
regVal = update._2//使用SimpleUpdater值为0
} logInfo("GradientDescent finished. Last 10 stochastic losses %s".format(
stochasticLossHistory.takeRight(10).mkString(", "))) (weights.toArray, stochasticLossHistory.toArray)
}
在MiniBatchSGD中主要实现对输入数据集进行迭代抽样,通过使用SquaredGradient作为梯度下降算法,使用SimpleUpdater作为更新算法,不断对抽样数据集进行迭代计算从而找出最优的特征权重向量解。
使用官方測试代码例如以下:
def linearRegressionAPITest(sc: SparkContext) {
val url = "/Users/yangguo/hadoop/spark/mllib/data/ridge-data/lpsa.data"
val data = sc.textFile(url)
val parseData = data.map { line =>
val parts = line.split(',')
LabeledPoint(parts(0).toDouble, parts(1).split(' ').map(x => x.toDouble).toArray)
}
val numIterations = 20
val model = LinearRegressionWithSGD.train(parseData, numIterations)
val valuesAndPreds = parseData.map { point =>
val prediction = model.predict(point.features)
(point, prediction)
}
valuesAndPreds.foreach { case (v, p) => print("[" + v.label + "," + p + "]"); v.features.foreach(base => print(base + "--")); println("\n") }
val isSuccessed = valuesAndPreds.map { case (v, p) => math.pow((p - v.label), 2) }.reduce(_ + _) / valuesAndPreds.count
println(isSuccessed)
}
參考:
[1].http://rdc.taobao.org/?p=2163
[2].http://cs229.stanford.edu/notes/cs229-notes1.pdf
[3].http://blog.sina.com.cn/s/blog_62339a2401015jyq.html
[4].http://blog.csdn.net/pennyliang/article/details/6998517
[5].http://en.wikipedia.org/wiki/Lasso_(statistics)#Lasso_method
Spark MLlib之线性回归源代码分析的更多相关文章
- Spark里边:Worker源代码分析和架构
首先由Spark图表理解Worker于Spark中的作用和地位: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYW56aHNvZnQ=/font/5a6L ...
- spark mllib 之线性回归
public static void main(String[] args) { SparkConf sparkConf = new SparkConf() .setAppName("Reg ...
- Spark SQL 源代码分析系列
从决定写Spark SQL文章的源代码分析,到现在一个月的时间,一个又一个几乎相同的结束很快,在这里也做了一个综合指数,方便阅读,下面是读取顺序 :) 第一章 Spark SQL源代码分析之核心流程 ...
- spark(1.1) mllib 源代码分析
在spark mllib 1.1加入版本stat包,其中包括一些统计数据有关的功能.本文分析中卡方检验和实施的主要原则: 一个.根本 在stat包实现Pierxunka方检验,它包括以下类别 (1)适 ...
- Spark MLlib LDA 基于GraphX实现原理及源代码分析
LDA背景 LDA(隐含狄利克雷分布)是一个主题聚类模型,是当前主题聚类领域最火.最有力的模型之中的一个,它能通过多轮迭代把特征向量集合按主题分类.眼下,广泛运用在文本主题聚类中. LDA的开源实现有 ...
- Spark MLlib回归算法------线性回归、逻辑回归、SVM和ALS
Spark MLlib回归算法------线性回归.逻辑回归.SVM和ALS 1.线性回归: (1)模型的建立: 回归正则化方法(Lasso,Ridge和ElasticNet)在高维和数据集变量之间多 ...
- Spark MLlib - Decision Tree源码分析
http://spark.apache.org/docs/latest/mllib-decision-tree.html 以决策树作为开始,因为简单,而且也比较容易用到,当前的boosting或ran ...
- Spark SQL 源代码分析之 In-Memory Columnar Storage 之 in-memory query
/** Spark SQL源代码分析系列文章*/ 前面讲到了Spark SQL In-Memory Columnar Storage的存储结构是基于列存储的. 那么基于以上存储结构,我们查询cache ...
- Spark SQL Catalyst源代码分析之TreeNode Library
/** Spark SQL源代码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心执行流程.SqlParser,和Analyzer,本来打算直接写Optimizer的,可是发 ...
随机推荐
- DCloud-MUI:代码块
ylbtech-DCloud-MUI:代码块 1.返回顶部 1. 怎么用? html 此底色代表最小触发字符 此底色代表非必要完整触发字符 *需HBuilder7.1+,或者下载m ...
- 此文章介绍vue-cli脚手架config目录下index.js配置文件
此配置文件是用来定义开发环境和生产环境中所需要的参数 关于注释 当涉及到较复杂的解释我将通过标识的方式(如(1))将解释写到单独的注释模块,请自行查看 上代码 // see http://vuejs- ...
- 如何让 vue 在 sublime 中变成彩色的
在 sublime 中编辑 vue 时,导入后是纯白色的文本,如下图: 想让其变成彩色的文本,需要安装插件,步骤如下: 1. 按住:Ctrl + Alt + P 2. 输入:install Packa ...
- Spring的AOP机制---- AOP环绕通知---- AOP环绕通知
323232三个人个地方司法发送哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
- css中background-clip属性的作用
background-clip属性的通俗作用就是指定元素背景所在的区域,有四种取值 1.border-box border-box是默认值,表示元素的背景从border区域(包括border)以内开始 ...
- Android App 开机启动画面和开机自动启动APP程序设置
1.当前比较成熟一点的应用基本上都会在进入应用之显示一个启动界面 如腾讯微博 2.准备元素 需要开机启动的图片一张 3.新建Activity AlphaAnimation动画:控制对象alpha水平 ...
- c++ 虚函数,纯虚函数的本质区别
转载博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fa ...
- Tinyxml2学习
转自http://www.360doc.com/content/13/1223/16/3684846_339528825.shtml,尊重原文 什么是XML? XML全称EXtensible Mark ...
- java 循环document 通用替换某个字符串或特殊字符
document 生成xml时 报错 XML-20100: (Fatal Error) Expected ';'. 查了半天发现是 特殊字符 & 不能直接转出,需要进行转换,因为是通用方法很 ...
- HTML 1.1页面js修改文字颜色
昨天的报告页面,想要实现根据不同文字内容改变字体颜色,效果图: 调试了半天出不来效果,最后请教了前端,上代码: <!DOCTYPE html> <html lang="en ...