海量数据场景下,通过大规模机器学习方法来充分挖掘数据集中的价值已经在很多公司里都有着非常多的应用了,相关的资料也已经很多,但仍谈不上非常的系统。
将门经本人许可,转载了大规模机器学习领域2位优秀的从业者——@白刚、@杨军的回答。希望可以把这个方向的知识体系梳理得更为清晰系统化,为这个领域的从业者提供一些导引性的帮助。
@白刚
机器学习研究的是从数据中自动的归纳逻辑或规则,并根据这个归纳的结果与新数据来进行预测的算法。这个方向是与数据相关的,而从前数据的收集过程效率往往很低成本很高,很少有大规模的问题需要解决。直到互联网发展起来,web服务器可以记录和收集大量的用户访问、交互信息,这类数据的收集成本非常低,并且有一定的价值。由此开始,“大规模机器学习”变得可行,并且越来越重要。
机器学习的应用上,我们接触到的各种问题、算法、技术看似复杂,但主要可以看成两个方面:
-
根据要建模的问题和应用的数据,确定模型的 representation 方案
-
在representation的无限种可能中去寻找最优的模型的 optimization 方法
“大规模机器学习”所涉及的就是从模型的representation和optimization这两个方面,去解决应用大规模的数据时的理论和工程上的问题。
为什么“大规模”是有益的,比“小规模”优势在哪里?
技术工作要以经世致用为则,而不是以fancy为则。
——Who Who
模型训练的目标是使得模型应用到新数据上时效果最好,也就是使得generalization error最小。而这个generalization error,对于supervised learning,理论上有两个来源:bias和variance。
-
High bias可以看做模型的建模能力不足,在训练集上的错误较大--并且在新数据上往往会更差,也就是under-fitting;
-
High variance则可以看做模型过于拟合训练集,而对新数据会效果很差,也就是over-fitting。
所以对于模型效果上,除了特征工程这类trick之外,“调得一手好参”——解决好bias和variance的tradeoff,也是部分算法工程师的核心竞争力。但“大规模机器学习”可以一定程度上同时解决这两个问题:
-
解决high variance/over-fitting:增大样本集。Variance的引入可以理解成样本集不够全面,训练样本的分布与实际数据的分布不一致造成的。扩大样本集,甚至使用全量的数据,可以尽量使得训练集与应用模型的数据集的分布一致,减少variance的影响。
-
解决high bias/under-fitting:提升模型的复杂性。通过引入更多的特征、更复杂的结构,模型可以更全面的描述概率分布/分界面/规则逻辑,从而有更好的效果。
所以当老板/技术委员会/隔壁团队的挑刺小能手/无知的围观群众提出几十上百计算节点/几TB数据吞吐不够绿色环保不要老想着搞个大新闻时,要以深邃的理论基础兼可观的业务收益说服众人:经世致用,而不是追求fancy。
模型的表示和优化
从模型的representation和optimization这两个方面,更形式化的定义机器学习的求解问题,learning的过程大概可以这么描述:
其中就是representation,或者说模型参数;D是数据;Σ(·)和Ω(·)分别是loss/risk/objective 和 prior/regularization/structural knowledge。
的过程即前面说的optimization,往往是迭代优化的过程。有些可以转化为最优化问题,通过数值优化的方法,比如batch的LBFGS,或者online的SGD/FTRL等等来进行参数估计;有些是无法构造成可解的最优化问题时,转化为概率分布的估计问题,通过probabilistic inference来解决--比如用Gibbs sampling来训练latent Dirichlet allocation模型。
无论是数值优化,还是sampling,都是不断迭代优化的过程:
每一步迭代做两件事。第一个是当前的模型在数据集上D的evaluation,得到一个“与好的模型相比可能存在的偏差”这个量;第二个是根据这个偏差量,去修正模型的过程。
大规模机器学习就是解决D和的规模非常大的时候所引入的理论上和工程上的问题。“知识体系”可以两个方面来整理,一方面是宏观上的架构,另一方面是无数微观上的trick。迭代中的那两个步骤决定了宏观上的架构应该是什么样子。两个步骤内的计算和之间的数据交互引入了解决多个特定问题的技术和trick。
首先,D和需要分布在多个计算节点上。
假设数据分布在n个data node节点上;模型分布在m个model node节点上。数据/样本集只与第一步有关,这部分计算应该在数据所在的节点上进行;第二部是对模型的更新,这一步应该在模型所在的结点上进行。也就是说,这个架构中有两个角色,每个都有自己的计算逻辑。并且在分布式的系统中,需要有一套replica的机制来容错。
几乎所有的大规模机器学习的平台、系统都可以看做这由这两个角色构成。在Spark MLLib里边,driver program是model node,executor所在的worker node是data node;在Vowpal Wabbit里边,mapper id为0的节点扮演了model node的角色,所有的mapper扮演了data node的角色;在Parameter Server里,server是model node,worker是data node。其中MLLib和VW的model node都是单个结点,而PS可以把模型扩展到多个结点。
其次,节点间需要传输数据。data node需要在计算前先获取当前的模型,model node需要得到更新量
。
MLLib通过RDD的treeAggregate接口,VW通过allReduce,这两者其实是相同的方式。结点被分配在一个树状的结构中,每个节点在计算好自己的这部分更新量后,汇总它的子节点的结果,传递给它的父节点。最终根节点(model node)获取到总的更新量
。DMLC中的Rabit对这类aggregation数据传输提供了更简洁的接口和更鲁棒的实现。我们应用Rabit实现了一套multi-task learning去训练不同广告位上的广告点击率模型的方案。
而PS则是通过worker(data node)去pull其需要的那部分,计算更新量并把自己的那部分
去push给server(model node),server去做对应的模型的更新。这个似乎比前边的aggregation要简单,但当每个data node进度差别太大时,又引入了新的问题。
所以第三点问题涉及到并行和一致性。分布式的系统中每个节点的状态都不相同,计算的进度也会不一样。当某个data node计算的更新量对应的是若干轮迭代之前的
时,它对优化不一定有贡献,甚至可能影响到收敛。
VW和MLLib上的迭代计算,可以看做是同步的。所有的model node获取了所有的data node的汇总起来,再更新,并广播给所有的data node。每次迭代model node获取的是完整的更新量,data node拿到的是一致的模型。所以这里相当于设置了barrier,等大家都到达这一轮的终点时,再放大家开启下一轮。
这里的tradeoff是:完全不设置barrier,也就是纯asynchronous processing,会让系统尽快的推进下去,但节点间进展程度差异太大会造成优化求解的收敛性不稳定;每一轮迭代都设置barrier,也就是bulk synchronous processing,会保证数据的一致,但是会在每一轮都等待慢的节点,造成效率不足的问题。
所以一个折衷的方案就是设置barrier,当不是每一轮都设置,而是限定一个时间窗口长度。计算的最快的节点也要等到所有的
都被应用到
的更新之后再开始下一轮pull&compute&push。这种机制又叫bounded delay asynchronous或者stale synchronous processing。
总的来说,解决好以上的几个问题,构建一套大规模的机器学习系统并不是太空科技。但这些需要严谨的工程能力和大量的巧妙的trick。这些都来自于对问题的深入理解和探索,以及充分的实践。前者帮助更合理的抽象结构、定义接口,后者则帮助解决实现中的障碍,得到更高效和稳定的系统。
我曾经尝试在Apache Spark上去实现一些大规模的机器学习算法:
https://github.com/BaiGang/spark_multiboost,并逐渐的发现Spark RDD这种data flow的抽象方式虽然可以支持机器学习算法实现上的一些需求,但它过于高级的接口封装限制了更底层的更有针对性的优化。
我后来也参与了一套分布式计算框架以及在其上的机器学习算法的开发:https://github.com/taskgraph/taskgraph。Go lang有不错的支持并行计算和网络的接口,很适合作为系统编程语言。但进行密集的纯占用CPU的数值计算时--机器学习算法程序的普遍情形--其性能还是有一定的差距。我想随着Go不断推出优化的新版本,TaskGraph仍然值得深入去做。
所以大规模机器学习的系统或平台应该是具有这样的特性:
-
较高的CPU、内存和网络性能
-
较灵活的模块抽象与接口
-
能够与现有的平台、工具、生态系统兼容
综合这几点,现有的若干种针对大规模机器学习的开源工具中,DMLC(http://dmlc.ml/)是值得使用的一个。
-
性能:由c++实现,引入大量的c++11特性来提升效率和增强代码可读性
-
模块抽象与接口:由若干个库组成
-
rabit(https://github.com/dmlc/rabit)实现了allreduce接口
-
ps-lite(https://github.com/dmlc/ps-lite)实现了parameter server,并包含大量工程上的优化和trick
-
-
结合现有生态系统:通过dmlc-core(http://dmlc.ml/)这个库来支持
-
YARN资源管理,可以直接在现有的Hadoop 2.0+/Spark集群上去运行
-
HDFS/S3等分布式文件系统的接口,可以直接读取现有的Hadoop集群上的数据,与ETL过程无缝的接合
-
@杨军
大规模机器学习的知识构成
本质上大规模机器学习可以说由三个Domain的知识构成:
-
机器学习系统
-
并行计算
-
分布式系统
其中机器学习系统的由来已久,从单机版的liblinear/libsvm到MaxEnt以及MSRA开源出来的OWLQN实现,交大的SVDFeature到现在的Caffe以及TensorFlow。变化的是具体的算法内核,线性模型/非线性模型,有监督学习/无监督学习,shallow model/deep model,不变的正如@白刚同学所说的,是迭代求优的过程,PLSA和K-Means求解所使用的EM、LR模型求解所使用的LBFGS/OWLQN、Online Learning所使用的FTRL、Deep Learning惯用的SGD本质上都是迭代求优过程的不同演绎。
并行计算则由来更久,从7、8十年代就已经源起,彼时主要应用的领域还是偏窄一些,高能物理模拟计算/军工/科学计算等等。
分布式系统相对来说要年轻一些,但也有十年左右的历史。例子在这里我想就不需要一一枚举了。
不同的Domain对于大规模机器学习的贡献是不一样的。
在我看来,机器学习系统是大规模机器学习的内核,单机版的机器学习系统在很多场景下已经足以满足业务需求,而并行计算在大规模机器学习的早、中期扮演了重要的角色(典型代表是MPI/Hadoop/Spark),随着某些应用场景下数据量越来越大,分布式系统的技术知识也变得重要起来(典型代表是Petuum/Peacock)。
大规模机器学习的核心本质论
说一门技术的”核心本质论“有些大言不惭的感觉,但是对”核心本质论“探讨梳理的过程往往有助于我们更深入地理解掌握一门技术的核心精要,所以在这里我只是share一下自己的理解认识,抛砖引玉。
在我个人看来,大规模机器学习的技术核心包括两个层面的内容:
-
基础技术
-
业务技术
其中基础技术就是并行计算、分布式系统这样的技术知识,而业务技术则是机器学习系统的知识。
所以,大规模机器学习也是一门业务,只不过这个业务更为基础一些。
跟其他的业务领域一样,整体意义上来说,业务技术的知识其实会要胜过基础技术,但是这种对比关系又不绝对,很多时候基础技术的知识又会对业务技术的演化带来制约和决定性因素。我来试着举几个例子:
从我个人了解到的情况来看,已经有不少工业界的公司,当他们的大规模机器学习集群在使用Batch Learning算法训练LR耗时过长时,会使用FTRL来进行离线训练,效果均跟Batch Learning算法持平。这个业务场景其实对于基础技术的选择是有着蛮重要的影响的。不考虑FTRL,只是基于"embarassingly parallel"的暴力并行来进行batch learning算法的性能加速,单位机器所带来的模型收益是不会提升的。
另一个例子,就是LDA主题模型的分布式训练了,从最传统的Gibbs Sampling到Sparse Sampling再到去年MSRA和CMU合作发表的LightLDA,本质上还是在业务层面作了大量的优化,从而起到花费更少规模的机器,获取评估数据集上相同甚至更高指标的效果。
而基础技术的发展对业务技术的演化带来促进的例子G家就是挺好的Case,只是做出一个分布式的机器学习系统和做出一个真正易用的机器学习系统,还是有着巨大的差异的,中间的差异主要还是由基础技术与业务技术的紧密衔接带来的。
但是我个人还是会认为,对于大规模机器学习领域来说,业务技术的重要性胜过了基础技术。业务技术领域的创新和技术进展,在很大程度上可以简化基础技术的投入和难度。只有在限定业务技术问题场景的情况下,基础技术的重要性会突显出来。有过一定业务系统经验的同学,对这一点的认识应该会有一些认识。
来源 : http://sanwen.net/a/xwozoqo.html
来源:将门创业
作者:白刚、杨军