BigDL:一种面向 Apache Spark* 的分布式深度学习库

BigDL 是一种面向 Apache Spark* 的分布式深度学习库。利用 BigDL,您可以将深度学习应用编写为 Scala 或 Python* 程序,也可以利用可扩展 Spark 集群的功能。本文介绍了 BigDL,向您展示了如何在各种平台上创建库,并提供了实际应用的 BigDL 示例。

什么是深度学习?

深度学习是机器学习的一个分支,利用算法模拟高级别的数据抽象。这些方法基于人工神经网络拓扑,可以利用更大的数据集进行扩展。

图 1 显示了传统方法和深度学习方法的根本不同。传统方法注重特性提取,将其作为训练机器学习模型的一个阶段,深度学习通过在模型中创建特性提取器管线,引入了深度。通过分级特性提取,深度学习管线中的多个阶段提高了产出模型的整体预测准确性。


图 1. 作为特性提取器管线的深度学习

什么是 BigDL?

BigDL 是一款面向 Spark 的分布式深度学习库,在现有的 Spark 或 Apache Hadoop* 集群上直接运行。您可以将深度学习应用编写为 Scala 或 Python 程序。

  • 丰富的深度学习支持。BigDL 模仿 Torch ,为深度学习提供综合支持,包括数值计算(借助Tensor)和高级神经网络;此外,用户可以将预训练 Caffe* 或 Torch 模型加载至 Spark 框架,并使用 BigDL 库在数据中运行推断应用。
  • 高效的横向扩展。利用 Spark,BigDL 能够在 Spark 中高效地横向扩展,处理大数据规模的数据分析,高效实施随机梯度下降 (SGD),以及进行 all-reduce 通信。
  • 极高的性能。为了实现较高的性能,BigDL 在每个 Spark 任务中采用英特尔® 数学核心函数库(英特尔® MKL)和多线程编程。因此,相比现成的开源 Caffe、Torch 或 TensorFlow,BigDL 在单节点英特尔® 至强® 处理器上的运行速度高出多个数量级(与主流图形处理单元相当)。

什么是 Apache Spark*?

Spark 是一款极速的分布式数据处理框架,由加利福尼亚大学伯克利分校的 AMPLab 开发。Spark 可以以独立模式运行,也能以集群模式在 Hadoop 上的 YARN 中或 Apache Mesos* 集群管理器上运行(图 2)。Spark 可以处理各种来源的数据,包括 HDFS、Apache Cassandra* 或 Apache Hive*。由于它能够通过持久存储的 RDD 或 DataFrames 处理内存,而不是将数据保存至硬盘(如同传统的 Hadoop MapReduce 架构),因此,极大地提高了性能。


图 2. Apache Spark* 堆栈中的 BigDL

为什么使用 BigDL?

在以下情况下,您需要利用 BigDL 编写您的深度学习程序:

  • 您希望在存储数据的大数据 Spark 集群(如 HDFS、Apache HBase* 或 Hive )上分析大量数据;
  • 您希望在大数据 (Spark) 程序或工作流中添加深度学习功能(训练或预测);或者
  • 您希望利用现有的 Hadoop/Spark 集群运行深度学习应用,随后与其他工作负载轻松共享(例如提取-转换-加载、数据仓库、特性设计、经典机器学习、图形分析)。另一种使用 BigDL 的不常见的替代方案是与 Spark 同时引进另一种分布式框架,以实施深度学习算法。

安装 Toolchain

注:假如您没有安装任何 Linux* 工具,本文提供最新安装指令。如果您并非 Linux 新用户,可以跳过基本步骤,如安装 Git)。

在 Oracle* VM VirtualBox 上安装 Toolchain

如欲在 Oracle* VM VirtualBox 上安装 toolchain,请执行以下步骤:

  1. 在电脑的 BIOS 设置中启用虚拟化(图 3)。

    BIOS 设置通常在 SECURITY 菜单下。随后需要重启设备,并进入 BIOS 设置菜单。


    图 3. 在电脑的 BIOS 中启用虚拟化。

  2. 确定您的电脑是 64 位还是 32 位。

    多数现代计算机都是 64 位,但是为了进一步证实,请查看控制面板系统和安全小程序(图 4)。


    图 4. 确定您的电脑是 32 位还是 64 位。

    注:至少需要 4 GB 的 RAM。您的虚拟机 (VM) 将占用 2 GB,但是多数 BigDL 示例(LeNet 除外)无法正常运转(甚至停止运转)。本文认为最低需要 8 GB RAM。

  3. 利用 GuestAdditions 或 VirtualBox 网站上的另一个虚拟机客户端安装 VirtualBox。

    图 5 中的虚拟机配置与 BigDL 兼容。


    图 5.

    重要:

    • 将 35 到 50 GB 的硬盘空间分配给您的虚拟机,用于运行 Ubuntu*、Spark、BigDL 以及全部 DeepLearning 模型
    • 分配硬盘空间时,请选择“动态”分配,支持随后扩展分区(尽管非常重要)。如果您选择“静态”,然后用完了全部的硬盘空间,只能选择擦除整个安装,然后重新安装。
  4. Ubuntu 下载页面上安装 Ubuntu Desktop。
  5. 下载完成后,将您的虚拟机指向下载文件,便可以利用下载文件重启。(请转至 Ubuntu 常见问题和解答以获取指令):

    sudo apt-get update
    sudo apt-get upgrade

    从这里开始,所有指令都在 Ubuntu 或您的 Linux 版本 内运行;除非另有明确说明,虚拟机或本地 Linux 的指令相同。

    如果您在访问互联网时遇到困难,需要设置代理。(一般情况下使用虚拟专用网、特殊情况下使用 Cisco AnyConnect 修改虚拟机代理设置。)图 6 中的代理设置非常适用于 VirtualBox 5.1.12 版和 64 位 Ubuntu。


    图 6. 面向 Oracle* VM VirtualBox 5.1.12 版和 64 位 Ubuntu* 系统的代理设置

  6. 利用以下命令安装 Java*:

    sudo add-apt-repository ppa:webupd8team/java
    sudo apt-get update
    sudo apt-get install oracle-java8-installer

  7. 验证 Java 版本:

    java –version

  8. Scala 下载页面安装 Scala 2.11 版。

    下载 Scala 时,使用 Debian* 文件格式,默认下载至 Downloads 文件夹。在文件浏览器中打开 Scala,然后单击:

    sudo apt-get update

  9. Spark 下载页面上安装 Spark。

    以下指令面向 Spark-1.6。如果您安装了另一种版本,请根据您的偏好替换 1.6.x

    a. 利用下载的版本替换 Spark:

    $ cd Downloads
    $ tar -xzf spark-1.6.1-bin-hadoop2.6.tgz

    b. 将文件转移到合适的位置:

    $ sudo mkdir /usr/local/spark
    $ sudo mv spark-1.6.1-bin-hadoop2.6 /usr/local/spark

    c. 测试安装:

    $ cd /usr/local/spark
    $ cd spark-2.1.0-bin-hadoop2.6
    $ ./bin/spark-shell

    d. 安装 Git:

    $ sudo apt-get install git

下载 BigDL

利用 Git 源控制工具,可以在 GitHub* 中获取 BigDL。如下所示,复制 BigDL Git 存储库:

$ git clone https://github.com/intel-analytics/BigDL.git

复制完成后,出现了一个名为 BigDL 的新子目录,这个子目录包含 BigDL 存储库。如下所示,必须同时安装 Apache Maven,以编译 Scala 代码:

$ sudo apt-get install maven

创建 BigDL

本章节展示如何在您的 Linux 发行版上下载并创建 BigDL。创建 BigDL 的前提条件为:

  • Java 8(为了实现最佳性能);
  • Scala 2.10 或 2.11(如果您计划使用 Spark 2.0 或更高版本,需要 Scala 2.11);
  • Maven 3;和
  • Git。

Apache Maven 环境

需要利用 Maven 3 创建 BigDL。可从从 Maven 网站上下载 Maven 3。

Maven 3 安装完成后,将环境变量 MAVEN_OPTS 设置为以下形式:

$ export MAVEN_OPTS="-Xmx2g -XX:ReservedCodeCacheSize=512m"

利用 Java 7 编译时,必须添加 -XX:MaxPermSize=1G 选项。

开始创建

强烈建议您利用 make-dist.sh script 创建 BigDL。

下载完成后,利用以下命令创建 BigDL:

$ bash make-dist.sh

创建完成后,能够找到一个 dist 文件夹,该文件夹包含运行 BigDL 程序所需的全部文件。dist 文件夹中的文件包含:

  1. dist/bin/bigdl.sh. 利用这个脚本设置合适的环境变量,并启动 BigDL 程序。
  2. dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies.jar. 这个 Java 存档文件 (JAR) 包包含除 Spark 之外的全部关联组件。
  3. dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies-and-spark.jar. 这个 JAR 包包含了所有关联组件,如 Spark。

替代方案

如欲面向 Spark 2.0 创建 BigDL,需要针对 Scala 2.11 修改 bash 调用。如欲面向 Spark 2.0(默认使用 Scala 2.11)创建 BigDL,需要将 –P spark_2.0 传输至 make-dist.sh 脚本:

$ bash make-dist.sh -P spark_2.0

强烈建议您在运行 Spark 2.0 时使用 Java 8。否则,将会导致性能下降。

默认情况下,make-dist.sh 使用面向 Spark 1.5.x 或 1.6.x 版的 Scala 2.10 和面向 Spark 2.0 的 Scala 2.11。为了撤销默认行为,根据需要将 -P scala_2.10-P scala_2.11 传输至 make-dist.sh

入门教程

运行 BigDL 程序前,必须首先设置个别选项。本章节列出了入门的步骤。

设置环境变量

为了实现较高的性能,BigDL 利用英特尔 MKL 和多线程编程。因此,首先需要设置环境变量,运行在 PATH_To_BigDL/scripts/bigdl.sh 中提供的以下脚本:

$ source PATH_To_BigDL/scripts/bigdl.sh

您也可以利用 PATH_To_BigDL/scripts/bigdl.sh 启动 BigDL 程序。

利用交互式 Scala 外壳

您可以通过交互式 Scala 外壳试验 BigDL 代码。为了实现这一目标,请运行:

$ scala -cp bigdl-0.1.0-SNAPSHOT-jar-with-dependencies-and-spark.jar

然后,将显示类似的内容:

欢迎使用 Scala 2.11.8(Java 热点(TM) 64 位服务器虚拟机,Java 1.8.0_91)。
输入评估表达式,或尝试:help.

scala>

例如,为了在 BigDL 中试验 Tensor 应用编程接口 (API),您可以尝试以下命令:

scala> import com.intel.analytics.bigdl.tensor.Tensor
import com.intel.analytics.bigdl.tensor.Tensor

scala> Tensor[Double](2,2).fill(1.0)
res9: com.intel.analytics.bigdl.tensor.Tensor[Double] =
1.0     1.0
1.0     1.0
[com.intel.analytics.bigdl.tensor.DenseTensor of size 2x2]

如欲获取关于 BigDL API 的更多详情,请查看 BigDL 编程指南

运行本地 Java* 程序(单模)

您可以将 BigDL 程序(如 VGG 模型训练)运行为在单节点(机器)上运行的本地 Java 程序。要进行这一操作,请执行以下步骤:

  1. CIFAR-10 数据集页面上下载 CIFAR-10 数据。

    请记得选择二进制版本。

  2. 利用 bigdl.sh 脚本启动示例,作为一个本地 Java 程序启动:
    ./dist/bin/bigdl.sh -- \
    java -cp dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies-and-spark.jar \
    com.intel.analytics.bigdl.models.vgg.Train \
    --core core_number \
    --node 1 \
    --env local \
    -f cifar10_folder \
    -b batch_size
    	

    本命令利用以下参数:

    • --core.训练使用的物理内核数量
    • --node.训练使用的节点数量(机器)(由于本例作为本地 Java 程序运行,设将其设置为 1)。
    • --env.“local”或“spark”(在本例中设置为“local”)。
    • -f.放置 CIFAR-10 数据集的文件夹
    • -b.小批次尺寸(程序预计小批次尺寸是 node_number × core_number 的数倍。本示例中,node_number1,将小规模尺寸设置为 core_number × 4)。

运行 Spark 程序

您可以将 BigDL 程序(如VGG 模型训练)运行为标准 Spark 程序(在本地模式或集群模式中运行)。为了进行这一操作,请执行以下步骤:

  1. CIFAR-10 数据集页面中下载 CIFAR-10 数据。

    请记得选择二进制版本。

  2. 利用 bigdl.sh 脚本启动示例,启动为 Spark 程序:
    ./dist/bin/bigdl.sh -- \
    spark-submit --class com.intel.analytics.bigdl.models.vgg.Train \
    dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies.jar \
    --core core_number_per_node \
    --node node_number \
    --env spark \
    -f cifar10_folder/ \
    -b batch_size
    

    该命令使用以下参数:

    • --core.Spark 集群中每个执行器(或容器)使用的物理内核数量
    • --node.Spark 集群中执行器(或容器)数量(在 Spark local 模式下运行时,将数字设置为 1)。
    • --env.“local”或“spark”(在本示例中,设置为“spark”)。
    • -f.放置 CIFAR-10 数据集的文件夹(需要指出的是,在本示例中,它只是 Spark 硬盘上的一个本地文件夹。由于 CIFAR-10 的数据规模较小 [大约 120 MB],在本示例中,将数据从驱动程序直接传输到执行器)。
    • -b.小批次尺寸(预计小批次尺寸是 node_number × core_number_per_node 的数倍。本示例中,将小批次尺寸设置为 node_number × core_number_per_node × 4)。

调试常见问题

当前,BigDL 在模型训练中利用同步小批次 SGD,处理小批次任务时,将在每个执行器(或容器)中启动单个 Spark 任务(在多线程模式中运行):

  • Engine.nodeNumber 设置为 Spark 集群中执行器的数量。
  • 确保每一个 Spark 执行器拥有相同数量的内核 (Engine.coreNumber)。
  • 小批次尺寸必须为 nodeNumber × coreNumber 的数倍。

:利用 Java 7 运行面向 Spark 2.0 的 BigDL 时,性能可能会下降,因此,面向 Spark 2.0 创建并运行 BigDL 时,请使用 Java 8。

在 Spark 2.0 中使用默认的 Java 串行器,请勿使用 Kryo,因为 Kryo 存在 341 问题:“Stackoverflow error due to parentScope of Generics being pushed as the same object”(请查看 Kryo 问题页面以获取更多信息)。Kryo 4.0 中问题得以修复,但是 Spark 2.0 使用的是 Kryo 3.0.3。Spark 1.5 和 1.6 版不存在这个问题。

在 CentOS* 6 和 7 版中,将最大用户流程限度设置为更大的值,如 514585。否则,将会出现“unable to create new native thread”的错误。

当前,BigDL 在训练过程中,将全部的训练和验证数据加载至内存。BigDL 内存耗尽后,将收到错误消息。

BigDL 示例

:如果您正在使用虚拟机,鉴于虚拟机的开销和有限的资源,本地操作系统中的性能将低于预期。这种情况在意料之内,无法反映真实的 Spark 性能。

在 MNIST 上训练 LeNet

面向深度学习的“hello world”示例正在训练 LeNet(一种位于 MNIST 数据库上的卷积神经网络)。

图 7 显示 LeNet 的克隆示例。


图 7. LeNet 克隆示例

首先,向 BigDL 程序输入 com.intel.analytics.bigdl,然后初始化引擎,包括执行器节点的数量、每个执行器上的物理内核数量以及它是在 Spark 上运行还是作为本地 Java 程序运行:

  val sc = Engine.init(param.nodeNumber, param.coreNumber, param.env == "spark").map(conf => {
    conf.setAppName("Train Lenet on MNIST")
      .set("spark.akka.frameSize", 64.toString)
      .set("spark.task.maxFailures", "1")
    new SparkContext(conf)
  })
  

如果程序在 Spark 上运行,Engine.init() 返回安装了适当配置的 SparkConf,随后可以利用 SparkConf 创建 SparkContext。如果程序作为本地 Java 程序运行,Engine.init() 返回 None

初始化后,通过调用 LeNet5() 开始创建 LeNet 模型,将按照以下步骤创建 LeNet-5 卷积网络模型:

val model = Sequential()
model.add(Reshape(Array(1, 28, 28)))
    .add(SpatialConvolution(1, 6, 5, 5))
    .add(Tanh())
    .add(SpatialMaxPooling(2, 2, 2, 2))
    .add(Tanh())
    .add(SpatialConvolution(6, 12, 5, 5))
    .add(SpatialMaxPooling(2, 2, 2, 2))
    .add(Reshape(Array(12 * 4 * 4)))
    .add(Linear(12 * 4 * 4, 100))
    .add(Tanh())
    .add(Linear(100, classNum))
    .add(LogSoftMax())
    

然后创建 DataSet.scala 命令(根据它是否在 Spark 上运行,决定创建一个分布式命令或者本地命令),并应用一系列 Transformer.scala 命令(如 SampleToGreyImgGreyImgNormalizerGreyImgToBatch):

  val trainSet = (if (sc.isDefined) {
      DataSet.array(load(trainData, trainLabel), sc.get, param.nodeNumber)
    } else {
      DataSet.array(load(trainData, trainLabel))
    }) 
     -> SampleToGreyImg(28, 28) 
     -> GreyImgNormalizer(trainMean, trainStd) 
     -> GreyImgToBatch(param.batchSize)
     

通过指定数据集、模型和标准,创建一个 优化器(根据它是否在 Spark 上运行,决定创建分布式优化器或者本地优化器),提供了输入和目标后,优化器能够计算每个给定损失函数的梯度:

val optimizer = Optimizer(
    model = model,
    dataset = trainSet,
    criterion = ClassNLLCriterion[Float]())
    

根据需要指定优化器验证数据和方法后,通过调用 Optimizer.optimize() 训练模型:

optimizer
    .setValidation(
      trigger = Trigger.everyEpoch,
      dataset = validationSet,
      vMethods = Array(new Top1Accuracy))
    .setState(state)
    .setEndWhen(Trigger.maxEpoch(param.maxEpoch))
    .optimize()
    

以下命令从命令行中执行 LeNet 示例:

$ dist/bin/bigdl.sh -- \
  java -cp dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies-and-spark.jar \
  com.intel.analytics.bigdl.models.lenet.Train \
  -f ~/data/mnist/ \
  --core 1 \
  --node 1 \
  --env local \
  --checkpoint 
  

以下是虚拟机上的 LeNet Spark 本地模式示例命令:

:运行 Spark 本地模式前,导出 SPARK_HOME=your_spark_install_dirPATH=$PATH:$SPARK_HOME/bin

$ ./dist/bin/bigdl.sh \
  -- spark-submit \
  --master local[1] \
  --driver-class-path dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies.jar \
  --class com.intel.analytics.bigdl.models.lenet.Train \
    dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies.jar \
  -f ~/data/mnist/ \
  --core 1 \
  --node 1 \
  --env spark \
  --checkpoint ~/model/model_lenet_spark

图像分类

:如欲获取在预训练模型上运行图像推断的示例,请查看 BigDL README.md

将图像下载至目录 ILSVRC2012_img_val.tar。这是面向预训练模型的图像 .tar 文件。可以将它放在 ~/data/imagenet 中并解压文件。然后,下载模型 resnet-18.t7,将它放在 ~/model/resnet-18.t7 中。

从命令行中运行以下命令(本示例面向虚拟机,因此内存大小设置为 1g):

$ ./dist/bin/bigdl.sh --spark-submit --master local[1] \
  --driver-memory 1g \
  --executor-memory 1g \
  --driver-class-path dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies.jar \
  --class com.intel.analytics.bigdl.example.imageclassification.ImagePredictor \  
  dist/lib/bigdl-0.1.0-SNAPSHOT-jar-with-dependencies-and-spark.jar \
  --modelPath ~/model/resnet-18.t7/resnet-18.t7 \
  --folder ~/data/imagenet/predict_100 \
  --modelType torch -c 1 -n 1 \
  --batchSize 4 \
  --isHdfs false

程序输出一个表格,包含两列:*.JPEG 文件名和数值标签。为了检查预测的准确性,请参考 imagenet1000_clsid.txt 标签文件。(需要指出的是,文件标签基于 0,示例输出基于 1)。

图像文件的前几行如下所示:

{0:'tench, Tinca tinca',
 1:'goldfish, Carassius auratus',
 2:'great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias',
 3:'tiger shark, Galeocerdo cuvieri',
 4:'hammerhead, hammerhead shark',
 ...

示例输出的前几行如下所示:

[ILSVRC2012_val_00025162.JPEG,360]
[ILSVRC2012_val_00025102.JPEG,958]
[ILSVRC2012_val_00025113.JPEG,853]
[ILSVRC2012_val_00025153.JPEG,867]
[ILSVRC2012_val_00025132.JPEG,229]
[ILSVRC2012_val_00025133.JPEG,5]

图像 ILSVRC2012_val_00025133.JPEG (图 8)被正确识别为标签 5,对应 imagenet1000_clsid.txt 文件中的“4:‘hammerhead, hammerhead shark’”项。


图 8. 图像文件 ILSVRC2012_val_00025133.JPEG

为了在 IntelliJ IDE 中运行分类示例,如图 9 所示,在虚拟机中运行的自变量设置必须略有不同。


图 9. IntelliJ 集成开发环境中的分类示例

由于图 9 中的 IntelliJ IDE 不显示整行,以下是虚拟机选项:

-Dspark.master=local[1] 
-Dspark.executor.cores=1 
-Dspark.total.executor.cores=1 
-Dspark.executor.memory=1g 
-Dspark.driver.memory=1g 
-Xmx1024m -Xms1024m

以下是程序自变量:

ImageClassifier Program arguments: 
  --modelPath ~/model/resnet-18.t7/resnet-18.t7 
  --folder ~/data/imagenet/predict_100 
  --modelType torch 
  -c 1 
  -n 1 
  --batchSize 1 
  --isHdfs false

神经网络支持的宽度和深度

目前,BigDL 支持以下预配置神经网络模型:

  • 自动编码器
  • Inception
  • LeNet
  • Resnet
  • Rnn
  • VGG

如欲获取更多信息,请查看 BigDL 模型页面

此外,BigDL 支持超过 100 种标准神经网络“构建模块”,您可以配置自己的拓扑(图 10)。


图 10. BigDL 标准神经网络构建模块

如欲获取更多信息,请查看 BigDL 神经网络页面

此外,BigDL 提供面向 LeNet、ImageNet 和 TextClassification 的即购即用的端到端实施示例(图 11)。


图 11. BigDL 示例

如欲获取更多信息,请查看 BigDL 示例页面

缩略语

HDFSHadoop 分布式文件系统
IDE集成开发环境
MKL数学核心函数库
RDD弹性分布式数据集
RNN循环神经网络
SGD随机梯度下降
VM虚拟机
YARNYet Another Resource Negotiator
有关编译器优化的更完整信息,请参阅优化通知
附件大小
PDF icon BigDL_distributed_DL.pdf2.04 MB