本文简要描述线性回归算法在spark mllib中的具体实现,涉及线性回归算法本身及线性回归并行处理的理论基础,然后对代码实现部分进行走读。
机器学习算法是的主要目的是找到最能够对数据做出合理解释的模型,这个模型是假设函数,一步步的推导基本遵循这样的思路
<code></code>
假设函数
为了找到最好的假设函数,需要找到合理的评估标准,一般来说使用损失函数来做为评估标准
根据损失函数推出目标函数
现在问题转换成为如何找到目标函数的最优解,也就是目标函数的最优化
具体到线性回归来说,上述就转换为:

那么如何求得损失函数的最优解,针对最小二乘法来说可以使用梯度下降法。
如何解决这些问题呢?可以采用收缩方法(shrinkage method),收缩方法又称为正则化(regularization)。 主要是岭回归(ridge regression)和lasso回归。通过对最小二乘估计加 入罚约束,使某些系数的估计为0。
上面讲述了一些数学基础,在将这些数学理论用代码来实现的时候,最主要的是把握住相应的假设函数和最优化算法是什么,有没有相应的正则化规则。
对于线性回归,这些都已经明确,分别为
y = a*x + b 假设函数
随机梯度下降法
岭回归或lasso法,或什么都没有
那么spark mllib针对线性回归的代码实现也是依据该步骤来组织的代码,其类图如下所示
函数调用路径
train->run,run函数的处理逻辑
利用最优化算法来求得最优解,optimizer.optimize
根据最优解创建相应的回归模型, createmodel
runminibatchsgd是真正计算gradient和loss的地方
上述代码中最需要引起重视的部分是aggregate函数的使用,先看下aggregate函数的定义
aggregate函数有三个入参,一是初始值zerovalue,二是seqop,三为combop.
seqop seqop会被并行执行,具体由各个executor上的task来完成计算
combop combop则是串行执行, 其中combop操作在jobwaiter的tasksucceeded函数中被调用
为了进一步加深对aggregate函数的理解,现举一个小小例子。启动spark-shell后,运行如下代码
仔细观察一下运行时的日志输出, aggregate提交的job由一个stage(stage0)组成,由于整个数据集被分成两个partition,所以为stage0创建了两个task并行处理。
讲完了aggregate函数的执行过程, 回过头来继续讲组成seqop的gradient.compute函数。
leastsquaregradient用来计算梯度和误差,注意cmopute中cumgraident会返回改变后的结果。这里计算公式依据的就是cost-function中的▽q(w)
在上述代码中频繁出现breeze相关的函数,你一定会很好奇,这是个什么新鲜玩艺。
说 开 了 其 实 一 点 也 不 稀 奇, 由 于 计 算 中 有 大 量 的 矩 阵(matrix)及 向量(vector)计算,为了更好支持和封装这些计算引入了breeze库。
breeze, epic及puck是scalanlp中三大支柱性项目, 具体可参数www.scalanlp.org
根据本次迭代出来的梯度和误差对权重系数进行更新,这个时候就需要用上正则化规则了。也就是下述语句会触发权重系数的更新
以岭回归为例,看其更新过程的代码实现。
计算出权重系数(weights)和截距intecept,就可以用来创建线性回归模型linearregressionmodel,利用模型的predict函数来对观测值进行预测
注意linearregression的构造函数需要权重(weights)和截距(intercept)作为入参,对新的变量做出预测需要调用predictpoint。
在spark-shell中执行如下语句来亲自体验一下吧。
再次强调,找到对应的假设函数,用于评估的损失函数,最优化求解方法,正则化规则。
本文就拟牛顿法l-bfgs的由来做一个简要的回顾,然后就其在spark mllib中的实现进行源码走读。
l-bfgs算法中使用到的正则化方法是squaredl2updater。
算法实现上使用到了由scalanlp的成员项目breeze库中的breezelbfgs函数,mllib中自定义了breezelbfgs所需要的difffunctions.
runlbfgs函数的源码实现如下:
costfun函数是算法实现中的重点