文章概要
本文通过一个图像分类模型为实例,疏导您一步步实现在 Apache Spark 上利用 DJL 在大数据生产环境中部署 TensorFlow,PyTorch,以及 MXNet 等模型。
前言
深度学习在大数据畛域上的利用日趋宽泛,可是在 Java/Scala 上的部署计划却比比皆是。亚马逊开源我的项目团队另辟蹊径,利用 DJL 帮忙用户部署深度学习利用在 Spark 上。只需 10 分钟,你就能够轻松部署 TensorFlow,PyTorch,以及 MXNet 的模型在大数据生产环境中。
Apache Spark 是一个优良的大数据处理工具。在机器学习畛域,Spark 能够用于对数据分类,预测需要以及进行个性化举荐。尽管 Spark 反对多种语言,然而大部分 Spark 工作设定及部署还是通过 Scala 来实现的。尽管如此,Scala 并没有很好的反对深度学习平台。大部分的深度学习利用都部署在 Python 以及相干的框架之上,造成 Scala 开发者一个很头痛的问题:到底是全用 Python 写整套 spark 架构呢,还是说用 Scala 包装 Python code 在 pipeline 外面跑。这两个计划都会减少工作量和保护老本。而且,目前看来,PySpark 在深度学习多过程的反对上性能不如 Scala 的多线程,导致许多深度学习利用速度都卡在了这里。
明天,咱们会展现給用户一个新的解决方案,间接应用 Scala 调用 Deep Java Library (DJL) 来实现深度学习利用部署。DJL 将充沛开释 Spark 弱小的多线程解决性能,轻松提速 2 - 5 倍 * 现有的推理工作。DJL 是一个为 Spark 量身定制的 Java 深度学习库。它不受限于引擎,用户能够轻松的将 PyTorch, TensorFlow 以及 MXNet 的模型部署在 Spark 上。在本 blog 中,咱们通过应用 DJL 来实现一个图片分类模型的部署工作,你也能够在这里参阅残缺的代码。
图像分类:DJL + Spark
咱们将应用 Resnet50 的预训练图像分类模型来部署一个推理工作。为了简化配置流程,咱们只会在本地设置繁多 cluster 与多个虚构 worker node 的模式来进行推理。这是大抵的工作流程:
Spark 会产生多个 Executor 来开启每个 JVM 过程,而后每一个解决工作 (task) 都会发送給 Executor 执行。每一个 Excutor 领有独立调配的内核以及内存。具体任务执行将会齐全应用多线程来执行。在大数据处理中,这种架构能够帮忙每个 worker 调配到正当的数据量。
第一步 建设一个 Spark 我的项目
通过应用 sbt,咱们能够轻松构建 Scala 我的项目。想理解更多对于 sbt 的介绍,请参考这里。能够通过上面的模版轻松设定:
name := "sparkExample"
version := "0.1"
// DJL 要求 JVM 1.8 及以上
scalaVersion := "2.11.12"
scalacOptions += "-target:jvm-1.8"
resolvers += Resolver.mavenLocal
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.3.0"
libraryDependencies += "ai.djl" % "api" % "0.5.0"
libraryDependencies += "ai.djl" % "repository" % "0.5.0"
// 应用 MXNet 引擎
libraryDependencies += "ai.djl.mxnet" % "mxnet-model-zoo" % "0.5.0"
libraryDependencies += "ai.djl.mxnet" % "mxnet-native-auto" % "1.6.0"
我的项目应用 MXNet 作为默认引擎。你能够通过批改上面两行来更换应用 PyTorch:
// 应用 PyTorch 引擎
libraryDependencies += "ai.djl.pytorch" % "pytorch-model-zoo" % "0.5.0"
libraryDependencies += "ai.djl.pytorch" % "pytorch-native-auto" % "1.5.0"
第二步 配置 Spark
咱们应用上面的配置在本地运行 Spark:
// Spark 设置
val conf = new SparkConf()
.setAppName("图片分类工作")
.setMaster("local[*]")
.setExecutorEnv("MXNET_ENGINE_TYPE", "NaiveEngine")
val sc = new SparkContext(conf)
MXNet 多线程须要设置额定的 NaiveEngine 环境变量。如果应用 PyTorch 或者 TensorFlow,这一行能够删除:
.setExecutorEnv("MXNET_ENGINE_TYPE", "NaiveEngine")
第三步 设置输出数据
输出数据是一个内含多张图片的文件夹。Spark 会把这些图片读入而后分成不同的 partition。每个 partition 会被分发给不同的 Executor。那么咱们配置一下图片散发的过程:
val partitions = sc.binaryFiles("images/*")
第四步 设置 Spark job
在这一步,咱们将创立一个 Spark 计算图用于进行模型读取以及推理。因为每一张图片推理都会在多线程下实现,咱们须要在进行推理前设置一下 Executor:
// 开始散发工作到 worker 节点
val result = partitions.mapPartitions( partition => {
// 筹备深度学习模型:建设一个筛选器
val criteria = Criteria.builder
// 图片分类模型
.optApplication(Application.CV.IMAGE_CLASSIFICATION)
.setTypes(classOf[BufferedImage], classOf[Classifications])
.optFilter("dataset", "imagenet")
// resnet50 设置
.optFilter("layers", "50")
.optProgress(new ProgressBar)
.build
val model = ModelZoo.loadModel(criteria)
// 建设 predictor
val predictor = model.newPredictor()
// 多线程推理
partition.map(streamData => {val img = ImageIO.read(streamData._2.open())
predictor.predict(img).toString
})
})
DJL 引入了一个叫做 ModelZoo 的概念,通过 Criteria 来设置读取的模型。而后在 partition 内创立 Predictor。在图片分类的过程中,咱们从 RDD 中读取图片而后进行推理。这次应用的 Resnet50 模型是通过 ImageNet 数据集预训练的模型。
第五步 设置输入
当咱们实现了 Map 数据的过程,咱们须要让 Master 主节点收集数据:
// 打印推理的后果
result.collect().foreach(print)
// 存储到 output 文件夹
result.saveAsTextFile("output")
运行上述两行代码会驱动 Spark 开启工作,输入的文件会保留在 output 文件夹. 请参阅 Scala example 来运行残缺的代码。
如果你运行了示例代码,这个是输入的后果:
[
class: "n02085936 Maltese dog, Maltese terrier, Maltese", probability: 0.81445
class: "n02096437 Dandie Dinmont, Dandie Dinmont terrier", probability: 0.08678
class: "n02098286 West Highland white terrier", probability: 0.03561
class: "n02113624 toy poodle", probability: 0.01261
class: "n02113712 miniature poodle", probability: 0.01200
][
class: "n02123045 tabby, tabby cat", probability: 0.52391
class: "n02123394 Persian cat", probability: 0.24143
class: "n02123159 tiger cat", probability: 0.05892
class: "n02124075 Egyptian cat", probability: 0.04563
class: "n03942813 ping-pong ball", probability: 0.01164
][
class: "n03770679 minivan", probability: 0.95839
class: "n02814533 beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", probability: 0.01674
class: "n03769881 minibus", probability: 0.00610
class: "n03594945 jeep, landrover", probability: 0.00448
class: "n03977966 police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", probability: 0.00278
]
生产环境配置的倡议
在这个例子里,咱们用了 RDD 来进行任务分配,这个只是为了不便展现。如果思考到性能因素,倡议应用 DataFrame 来作为数据的载体。从 Spark 3.0 开始,Apache Spark 为 DataFrame 提供了 binary 文件读取性能。这样在将来图片读取存储将会大海捞针。
工业环境中 DJL 在 Spark 上的利用
Amazon Retail System (ARS) 通过应用 DJL 在 Spark 上运行了数以百万的大规模数据流推理工作。这些推理的后果用于推断用户对于不同操作的偏向,比方是否会购买这个商品,或者是否会增加商品到购物车等等。数以千计的用户偏向类别能够帮忙 Amazon 更好的推送相干的广告到用户的客户端与主页。ARS 的深度学习模型应用了数以千计的特色利用在几亿用户上,输出的数据的总量达到了 1000 亿。在宏大的数据集下,因为应用了基于 Scala 的 Spark 解决平台,他们已经始终在为没有好的解决方案而困扰。在应用了 DJL 之后,他们的深度学习工作轻松的集成在了 Spark 上。推理工夫从过来的很多天变成了只需几小时。咱们在之后将推出另一篇文章来深度解析 ARS 应用的深度学习模型,以及 DJL 在其中的利用。
对于 DJL
DJL 是亚马逊云服务在 2019 年 re:Invent 大会推出的专为 Java 开发者量身定制的深度学习框架,现已运行在亚马逊数以百万的推理工作中。如果要总结 DJL 的次要特色,那么就是如下三点:
- DJL 不设限度于后端引擎:用户能够轻松的应用 MXNet, PyTorch, TensorFlow 和 fastText 来在 Java 上做模型训练和推理。
- DJL 的算子设计有限趋近于 numpy:它的应用体验上和 numpy 根本是无缝的,切换引擎也不会造成后果扭转。
- DJL 优良的内存治理以及效率机制:DJL 领有本人的资源回收机制,100 个小时间断推理也不会内存溢出。
想理解更多,请参见上面几个链接:
https://djl.ai/
https://github.com/awslabs/djl
也欢送退出 DJL 的 slack 论坛。
[
](https://s3.cn-north-1.amazona…
*2- 5 倍基于 PySpark 在 PyTorch Python CPU 与 Spark 在 DJL PyTorch Scala CPU 上性能测试的后果。