机器学习任务主要分为两种:Supervised Machine Learning 和 Unsupervised Machine Learning。其中Supervised Machine Learning主要包括Classification和Regression,Unsupervised Machine Learning主要包括Clustering。除了这些核心的算法以外,还有一些辅助处理的模块,例如Preprocessing, Dimensionality Reduction, Model Selection等。
目前最新的Spark 1.1.0版本中MLlib主要还是对核心算法的支持,辅助处理模块还很不完善。源代码包和其功能的对应关系如下:
Classification/Clustering/Regression/Tree |
分类算法、回归算法、决策树、聚类算法 |
Optimization |
核心算法的优化方法实现 |
STAT |
基础统计 |
Feature |
预处理 |
Evaluation |
算法效果衡量 |
Linalg |
基础线性代数运算支持 |
Recommendation |
推荐算法 |
本文主要讨论是用MLlib进行Classification工作。分类是机器学习最基础的工作,典型的应用场景就是AD CTR Prediction,也就是大部分互联网公司的利润来源。据业余了解,广告CTR预估使用最多的基础算法还是L1正则化的Logistic Regression。
下面一步一步来看看使用MLlib进行Classification机器学习。
1、分类算法原理与MLlib的实现
首先需要了解机器学习和MLlib的基础知识和原理,大家可以参考 http://spark.apache.org/docs/latest/mllib-Linear-Methods.html 。本文主要从工程实践的角度讨论如何使用和调优。
分类问题主要包括Binary Classification 和 Multiclass Classification。目前的MLlib只支持Linear Classification问题,这里讨论的也都是线性分类问题,不涉及到Kernel Method等。目前MLlib里面的Classification算法最常用的就是LR,SVM和Tree/RF。其中LR和SVM目前只支持Binary Classification,Tree/RF支持Multiclass Classification。本文主要讨论使用LR和SVM进行线性Binary Classification问题的实践中遇到的一些问题。
抽象来看LR和SVM算法都是通过指定Loss Function和Gradient/SUB-Gradient,然后通过Optimization算法(SGD或LBFGS)求使得Loss Function最小的凸优化问题,最后得出的解是一个Weights向量。从代码中也可以看出,LR和SVM算法仅仅是指定的Loss Function和Gradient是不同的,其求解最小值的过程是通用的,所以求解最小值的过程抽象出了Optimization模块,目前主要有SGD和LBFGS两种实现。
为了防止过拟合,需要在Loss Function后面加入一个正则化项一起求最小值。正则化项相当于对Weights向量的惩罚,期望求出一个更简单的模型。 MLlib目前支持两种正则化方法L1和L2。 L2正则化假设模型参数服从高斯分布,L2正则化函数比L1更光滑,所以更容易计算;L1假设模型参数服从拉普拉斯分布,L1正则化具备产生稀疏解的功能,从而具备Feature Selection的能力。
2、两种Optimization方法SGD和LBFGS
有了上面的数学基础,现在就是求取一个函数的最小值问题了。MLlib里面目前提供两种方法SGD和LBFGS。关于这两种算法的原理,大家可以参考 http://spark.apache.org/docs/latest/mllib-Optimization.html 。这两种优化方法的核心都是RDD的aggregate操作,这个从Spark Job运行时的UI中可以看出,SGD/LBFGS每迭代一次,aggregate执行一次,Spark UI中出现一个stage。下面分别看看两种优化算法具体怎么实现的。
SGD:
核心实现在GradientDescent.runMiniBatchSGD函数中
- for (i <- 1 to numIterations) {
- val bcWeights = data.context.broadcast(weights)
- // Sample a subset (fraction miniBatchFraction) of the total data
- // compute and sum up the subgradients on this subset (this is one map-reduce)
- val (gradientSum, lossSum) = data.sample(false, miniBatchFraction, 42 + i)
- .treeAggregate((BDV.zeros[Double](n), 0.0))(
- seqOp = (c, v) => (c, v) match { case ((grad, loss), (label, features)) =>
- val l = gradient.compute(features, label, bcWeights.value, Vectors.fromBreeze(grad))
- (grad, loss + l)
- },
- combOp = (c1, c2) => (c1, c2) match { case ((grad1, loss1), (grad2, loss2)) =>
- (grad1 += grad2, loss1 + loss2)
- })
- /**
- * 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)
- val update = updater.compute(
- weights, Vectors.fromBreeze(gradientSum / miniBatchSize), stepSize, i, regParam)
- weights = update._1
- regVal = update._2
- }
每次根据MiniBatchFraction指定的比例随机采样相应数量的样本,然后调用Gradient.Compute逐个计算Gradient和Loss并累加在一起,得到这一轮迭代总的Gradient和Loss的变化。然后调用Updater.Compute更新Weights矩阵和RegVal(正则化项)。也就是说Gradient.Compute只是利用当前Weights向量计算Gradient和Loss的变化,而Updater.Compute则根据这个变化以及正则化项计算更新之后的Weights和RegVal。
SGD算法支持使用L1和L2正则化方法。
注意到这里Weights向量在下一轮计算的过程中每个参与计算的Executor都需要,所以使用了Broadcast变量把它分发到每个节点,提高了计算效率。
LBFGS:
LBFGS优化方法的核心实现在LBFGS.runLBFGS函数里面。LBFGS的实现比SGD更加依赖Breeze库,它的迭代框架都是使用的Breeze的LBFGS的实现,只是实现了自己的名为CostFun的DiffFunction。大家可以去LBGFS.CostFun函数中看看Loss Function和Gradient的计算方法与SGD算法如出一辙,也是利用了RDD的Aggregate操作。
和SGD不同,目前LBFGS只支持L2正则化,不支持L1正则化。 其实在Breeze库里面有LBFGS + L1正则化的实现OWLQN(OWLQN算法默认自带L1正则化,所以在传入的参数DiffFunction中不需要显示定义正则化项,只需要定义Loss Function即可)是可以把它引入MLlib里面完成LBFGS+L1的功能。 这个在社区也有讨论,DB Tsai等人正在做这方面的工作。等不及的同学也可以尝试下我们自己修改的版本,引入了LBFGS+L1的功能的代码。
SGD和LBFGS两种算法的比较:
网上的资料都告诉我们说LBFGS比SGD更容易收敛,效果更好,大家可以亲自尝试下。例如选择Logistic Regression算法,选取同一个数据集,在做Training和Test集合的分配的时候也要一致。然后把生成的Training和Test的RDD分别丢到LogisticRegressionWithSGD和LogisticRegressionWithLBFGS两种具体实现算法里。其他参数要一致(例如都选择L2正则化,regParam=1.0)然后比较效果。
怎么比较效果的好坏呢?分类问题就是那些指标Precision/Recall/F1-score/Area Under ROC等。这里面有个需要注意的问题,MLlib的实现里面,SGD优化的终止条件是通过指定NumIterations也就是迭代次数终止的;而LBFGS优化的终止条件是通过指定ConvergenceTol(两次迭代Loss Function变化的容忍度)和MaxnumIterations(最大迭代次数)来终止的。
为了达到比较这两种优化方法的目的,需要定义一个统一的指标。由于我们优化的目标是让Loss Function+正则化项 最小,所以这个就是统一的指标。MLlib的日志里面会打印出迭代的最后10次的Loss大小:
//GradientDescent
- logInfo("GradientDescent.runMiniBatchSGD finished. Last 10 stochastic losses %s".format(
- stochasticLossHistory.takeRight(10).mkString(", ")))
//LBFGS
- logInfo("LBFGS.runLBFGS finished. Last 10 losses %s".format(
- lossHistory.takeRight(10).mkString(", ")))
我在一个数据集上测试,其他参数都一致(L2正则化,RegParam=1.0,MiniBatchFraction=1.0)的情况下,LBFGS需要14次就可以达到收敛;而对于SGD(实际上MiniBatchFraction=1.0的已经不是纯种SGD了)算法大约需要10000次循环才能达到相似的Loss大小。而且最后两种算法得到的Weights(权重矩阵)是一样的。
3,如何解读和分析训练出来的模型
训练出来的模型实际上就是一个Weights向量,现在MLlib的GeneralizedLinearModel的成员变量Weights和Intercept都是Public的了。 在训练好GeneralizedLinearModel之后,可以直接把Weights和Intercept打印出来,或者进行一些计算找出权重最大的几个维度。
这里有个问题,如果我的Loss Function和Gradient是确定的,那么使用不同的优化方法求出的Weights是不是应该是一样的?例如对同一个数据集,使用LogisticRegressionWithSGD和LogisticRegressionWithLBFGS分别训练出的模型是否应该是一致的?怎么衡量效果好坏?
对于这个问题,我想首先要看几点:
1) 确认两种方法使用的正则化和其他参数是一致的。
2) 通过日志查看两种优化方法的Loss Function+正则化项最后是否收敛?是否收敛到接近的值?
3) 如果有某种优化算法的Loss不收敛,说明那种方法的迭代次数不够(SGD)或者ConvergenceTol设置的太大(LBFGS),调整参数重试。如果收敛到相差较大的值,十有八九你的算法有问题。
4) 如果两种优化方法的Loss Function+正则化项收敛且到接近的值,那么就要看看传统指标Precision/Recall/Area Under ROC。如果Area Under ROC接近1.0,说明你的数据完全线性可分或者过拟合了,这个时候打印出两种方法得到的Weights应该是类似倍数关系的;如果Area Under ROC不是接近1.0,那么说明你的数据是真实的数据,这个时候两种方法得到的Weights应该是一致的。
5) 接下来就可以使用上述的思路去调节参数优化Precision/Recall等指标了。
4,预处理
其实上面的工作还只是停留在学习算法的阶段,拿过来一些公开的Dataset或者Benchmark来跑跑,看看效果。这些数据集往往都是经过了别人的一些加工和处理,在实际工作中这一部分也是需要我们来做的,这就是数据预处理。数据预处理特别繁琐,但是对机器学习模型效果的好坏却非常重要。
预处理主要包括Normalization,Scale,Outlier-Detection,正负样本均衡等。例如遇到一个数据正负样本比例9:1,这样的数据直接丢到模型里面显然会让模型更偏向正例做预测。解决这个问题的方法挺多的,最简单的例子就是正例采样、负例冗余,使两者达到接近平衡。
MLlib现阶段主要精力还是在核心算法上,对预处理这部分做的不是很好,这也提高了使用门槛。在1.1.0版本开始增加了一个新的Package叫Feature,里面大多是Preprocssing函数,包括Normalizer和StandardScaler等。例如StandardScaler能够把Feature按照列转换成Mean=0,Standard deviation=1的正态分布。
数据输入支持普通文本文件和LIBSVM格式。在1.1.0版本之前,输入的Label可以是+1/-1会被自动映射成1.0/0.0,但是从1.1.0开始貌似只支持输入1.0/0.0的Label了,这个和我们以前常见的LIBSVM格式的数据集不一样,需要注意。这个改动应该主要考虑的是对以后多分类的支持吧。
5,MLlib中的Vector和线性代数运算
不知道大家有没有注意到一个问题,就是MLlib底层的矩阵运算使用了Breeze库,Breeze库提供了Vector/Matrix的实现以及相应计算的接口(Linalg)。但是在MLlib里面同时也提供了Vector和Linalg等的实现(目前只是对Breeze做了一层包装)。在所有的MLlib的函数里面的参数传递都是使用Mllib自己的Vector,而且在函数内的矩阵计算又通过ToBreeze.ToDenseVector变成Breeze的形式进行运算。这样做的目的一是保持自己函数接口的稳定性,不会因为Breeze的变化而变化;另外一个就是可以把Distributed Matrix作为一种Matrix的实现而被使用。
6,开发环境
Spark集群(Standalone、Yarn-Client、Yarn-Cluster、单机调试环境)。
我主要使用Scala开发,IDE为Intellij IDEA,安装Scala插件。
开发一个Project可以使用Maven或者SBT编译,都可以通过IDEA创建相应的工程。 Maven编译的话和Java的Maven工程没啥区别,主要是修改Pom.xml文件;使用SBT编译的话,主要是修改Build.sbt文件。
Build.sbt的格式网上有很多资料了,简单说下需要注意的问题:
1) 必须每隔一行写新的内容;
2)LibraryDependencies 后面%%和%的区别:ArtifactId后面带/不带版本号;
3)LibraryDependencies 后面可以使用”Provided”使其在Assembly打包的时候不被打入包中。
开发一个基于Spark和MLlib的机器学习Job,主要依赖的两个LibraryDependencies就是Spark-Core和Spark-Mllib。
其实使用Scala开发Spark程序最重要的一点就是要知道你写的代码中哪些是RDD的操作,哪些是在RDD内部的操作,哪些是Transform,哪些是Actions,哪个地方会形成一个Stage。这些搞清楚之后就明白了哪些Code是在Driver上执行的,哪些是在Executor上并行执行的。另外就是哪些资源相关的参数,像Executor-Memory和Num-Executors等。关于这方面的内容在后面的介绍Tree和Random Forest的博客中讨论。
原文链接:mllib实践经验 (责编/刘亚琼)
相关推荐
这篇文章为读者提供了机器学习的基本概念和实践经验,以及 Spark MLlib 的应用,可以帮助读者更好地理解机器学习和 Spark MLlib。 机器学习基本概念: * 机器学习是计算机系统学习和改进其性能的算法和数学模型的...
机器学习是一种人工智能科学,它涉及能够通过经验自动改进的计算机算法的研究。机器学习主要研究的是在经验学习中如何改善特定算法的性能。机器学习的核心在于数据通过算法构建模型,并对模型进行评估。评估的性能...
机器学习的主要目标是让计算机系统通过经验学习和改进,而无需显式编程。这一科学学科结合了统计学、概率论、计算理论等多个领域的知识。 在机器学习中,我们通常区分两种主要的学习方式:监督学习和无监督学习。...
对于学习者来说,熟悉Spark机器学习库(MLLib)是非常重要的,因为它是Spark生态系统中不可或缺的一部分,也是数据分析和机器学习实践中的重要工具。对于初学者而言,可以从了解MLLib中的各种算法和工具开始,包括但...
总的来说,这份《基于Spark的分布式大数据机器学习算法》是学习和应用分布式机器学习的宝贵资源,无论你是初学者还是经验丰富的开发者,都能从中获得深入的理解和实用的技巧,提升你在大数据和机器学习领域的专业...
这篇“大数据分析中机器学习研究”很可能深入探讨了如何利用机器学习技术处理和挖掘海量数据中的价值。 首先,机器学习可以分为监督学习、无监督学习和半监督学习三大类。监督学习是通过已有的输入-输出对训练模型...
标题中的“大数据和机器学习实验(1).rar_hadoop_大数据_机器学习”表明这是一个关于使用Hadoop框架进行大数据和机器学习实践的压缩文件。在这个实验中,我们将深入理解Hadoop在处理大规模数据集时的角色,以及如何...
Spark提供了MLlib库,这是一个包含大量机器学习算法的集合,包括监督和无监督学习,以及预处理和评估工具。书中会详细介绍如何利用这些API实现机器学习任务,包括模型训练、调参和评估。此外,Spark还支持多种编程...
标题“ML与Spark MLlib.pdf”指出了本文档是关于机器学习(ML)和Apache Spark的机器学习库(MLlib)的介绍与实践。描述中提到,本文档展示了阿里云关于这些技术的最佳实践操作,而标签“spark”强调了文档的关键词...
本文档是一篇关于大规模分布式机器学习实践的文档,重点在于利用Apache Spark平台进行机器学习处理,涵盖了移动安全、企业安全、APT攻击检测和业务风控等多个安全领域。 Apache Spark是一个开源的大数据处理框架,...
6. **Apache Spark**:大数据处理框架,可以快速处理大规模数据集,尤其在机器学习任务中,如Spark MLlib提供了各种机器学习算法。 7. **Jupyter Notebook**:交互式笔记本环境,支持Python和其他语言,方便编写、...
对于机器学习开发者而言,MLlib和ML是重要的研究对象。它们包含了许多常用的机器学习算法实现,如线性回归、逻辑回归、决策树、随机森林、梯度提升机、朴素贝叶斯等,同时也支持模型评估和调优工具。通过阅读源码,...
在本项目实践中,我们主要探讨的是如何利用Spring Boot与Spark框架进行机器学习应用的开发,这对于深入了解和掌握人工智能领域的核心技术至关重要。Spring Boot以其强大的微服务治理能力,为开发者提供了便捷的Java...
本资料集包含了PySpark在机器学习、自然语言处理以及推荐系统中的实践代码和相关数据集,适用于毕业设计、课程作业以及电商系统的开发与分析。 一、PySpark 机器学习 PySpark内置了丰富的机器学习库MLlib,涵盖了...
在大数据处理领域,Apache Spark 是一个非常流行且高效的分布式计算框架,尤其在...同时,通过性能调优和在线学习算法,可以有效应对内存限制和提高计算效率,使得Spark成为大数据时代下机器学习研究和实践的重要平台。
Spark MLlib 是Spark的核心组件之一,它提供了丰富的机器学习算法,包括分类、回归、聚类以及协同过滤等。在这个项目中,我们重点利用了其协同过滤功能,特别是ALS算法。ALS是一种用于矩阵分解的常用方法,常用于...
本项目以"毕业设计:基于Spark MLlib的森林植被类型预测分类系统"为主题,旨在利用Spark的机器学习库(MLlib)进行森林植被类型的预测,为生态学研究和环境保护提供科学依据。 项目的核心是运用机器学习算法对森林...
【标题】: 可扩展机器学习的...【总结】: 本文全面综述了可扩展机器学习中并行与分布式优化算法的最新进展,为研究者和实践者提供了丰富的理论基础和实践指导,有助于推动机器学习领域在大数据环境下更高效地训练模型。