本文会介绍 Spark 核心社区开发的生态系统库,以及 ML MLlib 及 Spark Streaming 的 Spark 库的具体用法,对于企业的各种用例及框架也进行了说明。
spark拥有一个庞大的、不断增长的社区,还有在企业环境中不可或缺的生态系统。这些生态系统提供了不同生产环境案例所需的许多功能。一般来说,Spark 应用做的是机器学习算法、日志聚合分析或者商务智能相关的运算,因为它在许多领域都有广泛的应用,包括商务智能、数据仓库、推荐系统、反欺诈等。
数据仓库
对任何业务来说,数据分析都是一个核心环节。对分析型的应用来说,数据仓库系统就是其核心系统。Spark 有众多的框架和生态系统,所以它能作为核心组件为企业环境提供数据仓库功能,如图1所示。
图1 Spark 可以用作数据仓库核心组件
当然,与其他现有的工具相比,Spark 提供的功能有较大不同。SQL 是很多数据分析师、数据科学家和工程师使用的细粒度数据分析方法。Spark 也可以用作数据仓库框架,支持 SQL 处理,名为 SparkSQL。
Spark 内核已经集成到其他分布式文件系统中,例如 HDFS、S3。如果你的业务数据本来就保存在这样的系统中,很容易将现有业务流程转移到 Spark 环境,因为你只需要在数据存储系统上启动 Spark 集群即可。针对开发人员,Spark 还提供了一个友好的 API,可以用数据科学家们喜爱的 Python 和 R 来访问它。这个功能存在很长一段时间了。如果你习惯使用这些语言,那么选择 Spark 作为自己的数据仓库引擎还是很容易的。
你可以使用熟悉的接口在 Spark 上处理更大的数据集。SparkSQL 特有的接口是DataFrame(数据帧),这是受 R 语言启发而引入的。建议使用这个接口来访问结构化数据。我们将在下一节详细介绍 DataFrame。先来看一个纯 SQL 接口。Spark 大致提供了三种类型的 DW(数据仓库)功能:SparkSQL、DataFrame 及 Hive On Spark。如前所述,尽管 DataFrame 一开始是使用 SparkSQL 来开发的,已经为大家精心准备了大数据的系统学习资料,从Linux-Hadoop-spark-......,需要的小伙伴可以点击但它与机器学习管道的关联度更高。我们将把它与 ML / MLlib 放到一起介绍。本节介绍SparkSQL 和 Hive on Spark,重点关注怎样配置集群。在尝试 Spark 的这些 SQL 功能之前,需要下载带 Hive profile(配置)的预编译包,或者用 Hive profile 去构建这个包。如果你要自己创建,可以使用如下命令:
一定要安装 Zinc,它是一个长时运行的服务器程序,用于 sbt 的增量编译。如果你用的是 OS X 系统,可以用命令 brew install zinc 来安装它。
在运行这条命令后,可以得到一个带有 Hive 类的 Spark 二进制包。你或许会发现能用-P 配置及-DHadoop.version 环境变量轻松选择 Hadoop 版本。最好依据 Hadoop 集群及 Hive 功能选择你所需要的版本。换句话说,如果想在 Hadoop 2.7.0 上运行 Hive 0.13,可以使用如下命令:
Hive on Spark
Hive 是用于管理分布式存储(例如 HDFS)中的大型数据集的数据仓库软件。Hive 一开始被开发来作为生成 Hadoop MapReduce 数据处理任务的简单接口。Hive 有很长的历史,差不多跟 Hadoop 一样悠久。之后,一个更灵活、可靠的框架 Tez 被引入进来,它曾试图取代 MapReduce 框架。Apache Tez 是一个比 MapReduce 更复杂的通用执行引擎。由于 Tez 旨在成为通用的执行引擎,如果正确地创建了执行计划,我们就能用它作为 SQL 执行引擎。从 Hive 1.1 开始,Hive 也支持将 Spark 作为查询执行引擎。这意味着 Hive 目前支持三个执行引擎:Hadoop MapReduce、Tez和Spark。虽然 Hive 还没有全部完成,仍然在开发过程中(详情及进度可以查看 Hive-7292),但是现在 Hive 能充分利用 Spark 的速度及可靠性。下面是在本地机器上使用 Hive on Spark 的步骤。
首先,需要启动 Spark 集群。请注意,你必须下载不包含 Hive JAR 包的 Spark 版本。为了从 Spark 二进制包中排除 Hive JAR 包,输入下面的命令:
用这个命令可以编译你自己的不含 Hive JAR 的 Spark 二进制包。但是在 YARN 上启动 Spark 集群最简单的方法是使用 Spark 目录下的 ec2 脚本:
关于如何使用 spark-ec2 脚本,可参考 Spark 官方文档(https://Spark. apache.org/docs/latest/ec2-scripts.html)。这个脚本是为了在 EC2 实例上更容易地启动 Spark 集群而写的。要使用它的话,需要有 AWS 账户,以及从 AWS 控制台获得 AWS 密钥对。详情请阅读上述官方文档。
几分钟后,你就有一个运行在 YARN 上的 Spark 集群了。这个集群默认不含 Hive。你需要在此 Spark 集群上安装 Hive 包。可以将 Hive 下载到 Spark master 服务器上,然后通过 Hive CLI(命令行接口)来启动:
当你试着按照上述过程使用 Hive on Spark 的时候,可能会遇到麻烦。因为有一些情况下,当你自己启动 Hadoop 集群的时候,Hadoop 和 Hive 的版本之间会发生冲突。所以,应该考虑使用 CDH 及 HDP 这样的发行版,它们包含 Spark 和 Hive,而且所有组件之间的兼容性与功能都是经过测试的,这是最便捷的途径。但是这些系统还在不断发展,并且组件间会有比较复杂的依赖关系,因此有必要了解组件间的依赖关系。
机器学习
在大数据领域的下一代数据处理技术中,机器学习扮演了重要角色。当收集大量的数据时,对系统性能会有显著影响。这意味着,收集大量的关于处理能力的数据,可以使一个机器学习模型更加出色。通过提供一种简单而通用的分布式框架,Hadoop 及其生态系统实现了基本的环境(用大数据做机器学习)。Spark 进一步推动了这种趋势。所以,在本章中我们要关注的是,对机器学习算法的使用和创建流程的一些具体工作。当然,对机器学习而言,Spark 还有很多地方有待完善。但它的内存处理(on-memory processing)体系结构很适合解决机器学习问题。已经为大家精心准备了大数据的系统学习资料,从Linux-Hadoop-spark-......,需要的小伙伴可以点击本节我们的下一个案例将重点看一看 Spark 中的 ML(机器学习)。对开发者来说,机器学习本身需要一定的数学背景及复杂的理论知识,乍一看并不是那么容易。只有具备一些知识和先决条件,才能在 Spark 上高效地运行机器学习算法。
一些主要的机器学习概念包括:
DataFrame 框架:它使创建及操作现实中的结构化数据更简单。这个框架提供了一个先进的接口,有了它,我们就不用关心每一种机器学习算法及其优化机制之间的差异。由于这种固定的数据模式(data schema),DataFrame 能根据数据优化自己的工作负载。
MLlib 和 ML:集成到 Spark 内的核心机器学习框架。这些框架从本质上来说是 Spark 外部的框架,但是由于它们由Spark的核心提交者(committer)团队所维护,它们是完全兼容的,并且可以经由 Spark 内核无缝使用。
其他可用于 Spark 的外部机器学习框架:包括 Mahout 及 Hivemall。它们都支持目前的 Spark 引擎。在一些 MLlib 及 ML 无法满足的情况下,可以选择这些外部库。
外部的框架
Spark 社区提供了大量的框架和库。其规模及数量都还在不断增加。在本节中,我们将介绍不包含在 Spark 核心源代码库的各种外部框架。Spark 试图解决的问题涵盖的面很广,跨越了很多不同领域,使用这些框架能帮助降低初始开发成本,充分利用开发人员已有的知识。
Spark Package:要使用 Spark 库,你首先必须了解的东西是 Spark package。它有点像 Spark 的包管理器。当你给 Spark 集群提交 job 时,你可以到存放 Spark package 的网站下载任何 package。所有 package 都存放在这个站点。
XGBoost:XGBoost 是一个专用于分布式框架的优化库。这个框架由DMLC(Distributed Machine Learning Community,分布式机器学习社区)开发。顾名思义,在 DMLC 项目下有许多机器学习库,它们在 Hadoop 和 Spark 等已有资源上具有高扩展性。XGBoost 是基于 Gradient Boosting(梯度提升)算法的。
spark-jobserver:提交 job 的流程需要改进,因为对于非工程师来说,这项工作有点难。你需要理解如何用命令行或者其他 UNIX 命令去提交 Spark job。Spark 项目现在是使用 CLI 来提交 job 的。spark-jobserver 提供了一个 RESTful API 来管理提交到 Spark 集群的 job。因此,这意味着可以在企业内部环境中将 Spark 作为一个服务启动。
未来的工作
你可能对使用Spark 服务比较感兴趣。Spark 已经提供了很多功能,例如 SQL 执行、流处理以及机器学习。Spark 也有一个好用的界面,而且背后有强大的社区,开发者十分活跃,这也是人们对 Spark 寄予厚望的原因。下面我们将介绍一些当前正在进行中的 Spark 项目。
Spark 目前使用的主要数据结构是 RDD 和 DataFrame。RDD 是一个原创的概念,而 DataFrame 是后来引入的。RDD 相对灵活。你可以在 RDD 结构上运行许多类型的转换与计算。然而,因为它太灵活了,所以很难对其执行进行优化。另一方面,DataFrame 有一定的固定结构,能利用它来优化 DataFrame 数据集上的执行。但是,它不具备 RDD 的优点,主要是没有 RDD 的灵活性。RDD 与 DataFrame 的主要区别如表 2 所示。
表 2 RDD 与 DataFrame 的区别
与参数服务器集成
在介绍参数服务器的实现之前,有必要厘清分布式机器学习的相关概念,例如并行。参数服务器的目标与已有数据库是不同的,它们为大规模机器学习而开发。在大规模机器学习中有两种并行类型:数据并行(data parallelism)及模型并行(model parallelism)。
数据并行
数据并行侧重于把数据分发到集群不同的计算资源上。通常,用于机器学习的训练数据量非常庞大,仅仅单台节点机器在内存中是无法保存所有数据的,甚至在磁盘上也无法保存全部的数据。这是一种 SIMD(单指令多数据流)处理类型。包括 Spark MLlib 及 ML 在内的大多数分布式机器学习框架都实现了数据并行。虽然数据并行很简单且易于实现,但是数据并行的收集任务(在前面的例子中,就是指计算平均值)会导致性能瓶颈,因为这个任务必须等待分布在集群中的其他并行任务完成后才能执行。
模型并行
模型并行与数据并行差别很大。不同的机器用相同的数据训练。然而,一个模型分布在多台机器上。深度学习的模型往往很大,因为许多参数常常不是在一台机器上的。模型并行就是将单个模型分为多个分片。一个节点维护一个模型分片。另一方面,每个训练进程能异步更新模型。框架必须对此进行管理以便于保持模型的一致性。实现这个过程的框架,特别是在机器学习领域,叫作“参数服务器”(parameter server)。深度学习尤其要求实现模型并行,因为深度学习需要用到更多数据,而这意味着最终需要更多参数。
参数服务器与 Spark
如前所述,原始的参数服务器是为模型并行处理而开发出来的。Spark MLlib 的大部分算法当前在处理数据时仅仅是数据并行,而不是模型并行。为了以一种通用的方式实现模型并行,人们研究和开发出更高效的参数服务器架构。参数服务器是在 RAM(随机访问存储)上存放以及更新分布式集群中的模型的。而模型更新常常是分布式机器学习过程的瓶颈所在。SPARK-6932 是一个用于研究参数服务器潜在能力的 ticket,也是对各种实现的比较。此外,Spark 项目在尝试基于这项研究去实现它自己的“参数服务器”。已经有人提供了 Spark 上的参数服务器,参见 https://github.com/chouqin/spark/tree/ps-on-Spark-1.3。
深度学习
深度学习因其高准确率及通用性,成为机器学习中最受关注的领域。这种算法在 2011—2012 年期间出现,并超过了很多竞争对手。最开始,深度学习在音频及图像识别方面取得了成功。此外,像机器翻译之类的自然语言处理或者画图也能使用深度学习算法来完成。深度学习是自 1980 年以来就开始被使用的一种神经网络。神经网络被看作能进行普适近似(universal approximation)的一种机器。换句话说,已经为大家精心准备了大数据的系统学习资料,从Linux-Hadoop-spark-......,需要的小伙伴可以点击这种网络能模仿任何其他函数。深度学习可以看作是组合了许多神经网络的一种深度结构。
如前面提到的参数服务器,与其他已有的机器学习算法相比,深度学习需要大量参数及训练数据。这也是我们介绍能在 Spark 上运行的深度学习框架的原因。要想在企业环境中稳定地进行深度学习的训练,必须要有一个可靠而快速的分布式引擎。 Spark 被视为目前最适合运行深度学习算法的平台,是因为:
基于内存的处理架构对于使用机器学习的迭代计算,特别是深度学习,十分适合。
Spark 的几个生态系统如 MLlib 及 Tachyon 对于开发深度学习模型很有用。
下面是一些 Spark 能用的深度学习框架。这些框架和深度学习一样,都是比较新的库。
H2O:H2O 是用 h2o.ai 开发的具有可扩展性的机器学习框架,它不限于深度学习。H2O 支持许多 API(例如,R、Python、Scala 和 Java)。当然它是开源软件,所以要研究它的代码及算法也很容易。H2O 框架支持所有常见的数据库及文件类型,可以轻松将模型导出为各种类型的存储。
deeplearning4j:deeplearning4j 是由 Skymind 开发的,Skymind 是一家致力于为企业进行商业化深度学习的公司。deeplearning4j 框架是创建来在 Hadoop 及 Spark 上运行的。这个设计用于商业环境而不是许多深度学习框架及库目前所大量应用的研究领域。
SparkNet:这是本文介绍的最新的库。SparkNet 由加州大学伯克利分校 AMP 实验室于 2015 年 11 月发布。而 Spark 最早就是由 AMP 实验室开发的。因此,说 SparkNet 是“运行在 Spark 上的官方机器学习库”一点儿也不为过。此库提供了读取 RDD 的接口,以及兼容深度学习框架 Caffe(http://caffe.berkeleyvision.org/)的接口。SparkNet 通过采用随机梯度下降(Stochastic Gradient Descent)获得了简单的并行模式。SparkNet job 能通过 Spark-submit 提交。
Spark 在企业中的应用
在最后,我们想聊一聊遇到的一些企业实际用例。虽然有些内容属于公司机密不便公开,但是我们想解释清楚Spark能做什么以及怎样才能充分利用 Spark。以下都是我们公司的实际用例。
用 Spark 及 Kafka 收集用户活动日志
收集用户活动日志能帮助提高推荐的准确性以及将公司策略的效果以可视化形式呈现。Hadoop 和 Hive 主要就用在这个领域。Hadoop 是唯一能处理像活动日志这样的海量数据的平台。借助 Hive 接口,我们能交互式做一些分析。但是这个架构有三个缺点:
Hive 做分析很耗时。
实时收集日志有难度。
需要对每个服务日志分别进行烦琐的分析。
为了解决这些问题,这家公司考虑引进 Apache Kafka 及 Spark。Kafka 是用于大数据传送的队列系统(见图3)。Kafka 自己不处理或转换数据,它使大量的数据从一个数据中心可靠地传送到另一个数据中心成为可能。因此,它是构建大规模管道架构不可或缺的平台。
图3 Kafka 和 Spark Steaming 的体系结构概览
Kafka有一个叫作主题(topic)的单元,带有偏移量及复制管理功能。通过 topic 及一组名为 ConsumerGroup 的读取器,我们就能获得不同类型的日志单元。为了做实时处理,我们采用 Spark 的流处理模块 Spark Streaming。严格来说,Spark Streaming 是一个微批量框架。微批量框架将流分为小数据集,对这些小集合运行批量处理进程。因此就处理算法而言,批处理跟微批量处理没有什么不同。这是我们采用 Spark Streaming 而不是 Storm 或者 Samza 之类的其他流式处理平台的一个主要原因。我们能方便地把当前的逻辑转换为 Spark Streaming。由于引入了这个架构,我们能获得如下结果:
用 Kafka 管理数据的终结。Kafka 自动删除过期的不需要的数据。我们无须处理这些事情。
使数据保存到存储(HBase)上的时间缩到最短。我们可以把这个时间从 2 小时缩短到10~20秒。
由于将一些过程转换为 Spark Streaming,所以减少了可视化的时间。我们能使这个时间从 2 小时缩减到 5 秒。
Spark Streaming 很好用,因为它的 API 基本与 Spark 相同。因此,熟悉 Scala 的用户会很习惯 Spark Streaming,而且 Spark Streaming 也能非常容易地无缝用在 Hadoop 平台(YARN)上,不到 1 个小时就能创建一个做 Spark Streaming 的集群。但需要注意的是,Spark Streaming 与普通 Spark job 不一样,它会长期占用 CPU 及内存。为了在固定时间里可靠地完成数据处理,做一些调优是必要的。如果用 Spark Streaming 不能非常快地做流式处理(秒级以下的处理),我们推荐你考虑其他流式处理平台,比如 Storm 和 Samza。
用 Spark 做实时推荐
机器学习需求最旺盛的领域就是推荐。你可以看到许多推荐案例,比如电子商务、广告、在线预约服务等。我们用 Spark Streaming 和 GraphX 做了一个售卖商品的推荐系统。GraphX 是用于分布式图处理的库。这个库是在一个 Spark 项目下开发的。我们可以用一种称为弹性分布式属性图(resilient distributed property graph)的 RDD 来扩展原始 RDD。GraphX 提供了对这个图的基本操作,以及类似 Pregel 的 API。
我们的推荐系统如下。首先从 Twitter 收集每个用户的推文(tweet)数据。接着,用 Spark Streaming 做接下来的微批量处理,每 5 秒收集一次推文并进行处理。由于推文是用自然语言写的(在本例中为日语),所以需要用形态分析(morphological analysis)把每个单词分离开。在第二阶段,我们用 Kuromoji 去做这个分离。为了与我们的商品数据库建立关系,需要为 Kuromoji 创建用户定义字典。这是获取有意义的推荐最重要的一点(见图 4)。
图4 Spark Streaming
在第三阶段,我们根据每个单词与商品的关系计算出一个分值。我们还必须调整用户定义字典,使单词与商品之间的相关性更好。特别地,我们删除了非字母字符,并且增加特别的相关词汇。在这个阶段之后,我们就获得一个从每条推文中收集到的词的集合。但是这个集合中还有与我们的商品不相关的词。因此在第四阶段,我们用 SVM 过滤出与商品相关的词语,以有监督学习方式(supervised learning)训练 SVM:标签0表示不相关的推文;标签 1 表示相关的推文。创建了有监督学习的数据后,就开始训练模型。接着我们从原始数据提取出相关的推文。最后一步就是分析商品条目与单词的相关度。如果聚类成功,就能推荐相同聚类中的另一个商品给用户(见图 5)。
图5 Spark Steaming 分析单词的相关性
虽然主要的麻烦之处在于创建用户定义字典,但是关于 Spark Streaming 也有一些地方需要注意:
Map#filterKeys和Map#mapValues 不可序列化——在 Scala 2.10 中不能使用这些 transformation。由于 Spark 1.1依赖于 Scala 2.10,所以我们不能用这些函数。这个问题在 Scala 2.11 中已经解决。
DStream 的输出操作受限制——在目前的 DStream.print、saveAsText Files、saveAasObjectFiles、saveAsHadoopFiles 与 foreachRDD 中没有太多的输出操作。在其他方法中,什么操作都会有副作用。例如,println在 map 函数上就没有效果。这为调试带来了困难。
无法在 StreamContext 中创建新的 RDD——DStream 是 RDD 的连续序列。我们能轻松分离或者转换这个初始的 RDD,但是在 StreamContext 中创建一个全新的 RDD 则很难。
在这个系统中,我们使用了 Spark Streaming、GraphX 及 Spark MLlib。虽然也能用 Solr 作为搜索引擎,但是 Spark 库几乎提供了所有功能。这是 Spark 最强的特性之一,其他框架则达不到同样的效果。
Twitter Bots 的实时分类
这可能是一种关于兴趣爱好的项目。我们已经分析了游戏角色的 Twitter 聊天机器人(Twitter Bot),并且可视化了 Bot 账户之间的关系。与前面例子类似,我们用Spark Streaming 收集推文数据。游戏角色的名字可能有不同的拼写形式。因此我们用搜索引擎 Solr 转换推文中独特的名字。在这个例子中我们觉得 Spark Streaming 的主要优点是,它已经实现了机器学习算法(MLlib)及图算法(GraphX)。因此我们能立即分析推文,不用准备其他库或编写算法。但是我们缺少数据去显示有意义的可视化结果。除此之外,从每个推文内容中提取出有意义的特征也不容易。这可能是由于当前我们手动搜索 Twitter 账户,推文数据不足而导致的。具体来说,Spark Streaming 是一个可扩展的系统,能处理海量数据集。我们认为应该利用好 Spark 的可扩展能力。
总结
本文解释了 Spark 核心社区开发的生态系统库,介绍了 ML/MLlib 及 Spark Streaming 的 Spark 库的具体用法,对于企业的各种用例及框架也进行了介绍。希望对你的开发或日常的业务决策能有所帮助。Spark 拥有灵活的架构,其社区也提供了大量生态系统框架,这一切使得 Spark 有广泛的应用场景。我们能从 spark-packages 上注册的包数量中看到 Spark 社区的活跃度。截至 2017 年 2 月 3 日,注册的包的数量已达到 319 个。
图 6 发展中的 Spark
Spark 社区是一个欣欣向荣的开源社区,Spark 社区在不远的未来肯定会发生变化。本文节选并整理自《Spark:大数据集群计算的生产实践》一书,Ilya Ganelin(伊利亚·甘列林)等著。