使用 Horovod 的TensorFlow - 深度学习 AMI
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Horovod 的TensorFlow

本教程介绍如何在TensorFlow上使用 采用 Conda 的 Deep Learning AMI with Horovod。Horovod 已针对 TensorFlow 预安装在 Conda 环境中。 推荐使用 Python 3 环境。此处的说明假定您有一个正在运行的 DLAMI 实例,其中包含一个或多个 GPUs。 有关更多信息,请参阅如何开始使用 DLAMI

注意

仅支持 P3.*、P2.* 和 G3.* 实例类型。

注意

在两个位置有 mpirun(通过 OpenMPI)可用。它在 /usr/bin/home/ubuntu/anaconda3/envs/<env>/bin 中可用。env 是与框架对应的环境,例如 Tensorflow 和 Apache MXNet。 更新的 OpenMPI 版本在 Conda 环境中可用。我们建议使用 mpirun 二进制文件的绝对路径或 -- 前缀标志来运行 mpi 工作负载。例如,对于 Tensorflow python36 环境,请使用以下任一方式:

/home/ubuntu/anaconda3/envs/tensorflow_p36/bin/mpirun <args> or mpirun --prefix /home/ubuntu/anaconda3/envs/tensorflow_p36/bin <args>

使用 Horovod 激活和测试TensorFlow

  1. 验证您的实例是否具有活动GPUs。 NVIDIA 为此提供了一个工具:

    $ nvidia-smi
  2. 激活 Python 3 TensorFlow 环境:

    $ source activate tensorflow_p36
  3. 启动 iPython 终端:

    (tensorflow_p36)$ ipython
  4. 测试导入 TensorFlow with Horovod 以验证其是否正常运行:

    import horovod.tensorflow as hvd hvd.init()

    以下内容可能显示在您的屏幕上(可能出现在一些警告消息之后)。

    -------------------------------------------------------------------------- [[55425,1],0]: A high-performance Open MPI point-to-point messaging module was unable to find any relevant network interfaces: Module: OpenFabrics (openib) Host: ip-172-31-72-4 Another transport will be used instead, although this may result in lower performance. --------------------------------------------------------------------------

配置您的 Horovod 主机文件

您可以将 Horovod 用于单节点多 GPU 训练或多节点多 GPU 训练。如果您打算使用多个节点进行分布式训练,则必须向主机文件添加每个 DLAMI 的私有 IP 地址。您当前登录的 DLAMI 称为领导。作为集群的一部分的其他 DLAMI 实例称为成员。

在开始本节之前,请启动一个或多个 DLAMI,然后等待它们进入准备就绪状态。示例脚本需要一个主机文件,即使您计划只使用一个 DLAMI,也请创建仅具有一个条目的主机文件。如果您在训练开始后编辑主机文件,则必须重新启动训练以使已添加或删除的主机生效。

针对训练配置 Horovod

  1. 将目录更改为训练脚本所在的目录。

    cd ~/examples/horovod/tensorflow
  2. 使用 vim 编辑领导的主目录中的文件。

    vim hosts
  3. 在 Amazon Elastic Compute Cloud 控制台中选择其中一个成员,控制台的说明窗格将出现。找到 Private IPs (私有 IAM) 字段,复制 IP 并将其粘贴到文本文件中。在新行上复制每个成员的私有 IP。然后,在每个 IP 的旁边添加一个空格,然后添加文本 slots=8,如下所示。这表示每个实例具有的 GPUs 数。p3.16xlarge 实例具有 8 个 GPUs,因此,如果您选择了不同的实例类型,您将为每个实例提供实际的 GPUs 数。对于领导,您可以使用 localhost。 对于包含 4 个节点的集群,它应类似于以下内容:

    172.100.1.200 slots=8 172.200.8.99 slots=8 172.48.3.124 slots=8 localhost slots=8

    保存文件并退回到领导的终端。

  4. 将成员实例使用的 SSH 密钥添加到 ssh-agent。

    eval `ssh-agent -s` ssh-add <key_name>.pem
  5. 现在,您的领导知道如何联系每个成员。这一切都将在专用网络接口上发生。接下来,使用简短的 bash 函数来帮助将命令发送到每个成员。

    function runclust(){ while read -u 10 host; do host=${host%% slots*}; ssh -o "StrictHostKeyChecking no" $host ""$2""; done 10<$1; };
  6. 告知其他成员不执行“StrickHostKeyChecking”操作,因为这可能会导致训练停止响应。

    runclust hosts "echo \"StrictHostKeyChecking no\" >> ~/.ssh/config"

使用合成数据训练

DLAMI 附带一个可用于使用合成数据训练模型的示例脚本。这将测试您的领导是否能与集群的成员通信。需要主机文件。有关说明,请参阅 配置您的 Horovod 主机文件

使用示例数据测试 Horovod 训练

  1. ~/examples/horovod/tensorflow/train_synthetic.sh 默认为 8 GPUs,但您可以为其提供要运行的 GPUs 的数量。以下示例运行脚本,并将 4 作为 4 个 GPUs 的参数传递。

    $ ./train_synthetic.sh 4

    在显示一些警告消息后,您将看到以下输出以验证 Horovod 是否使用了 4 个 GPUs。

    PY3.6.5 |Anaconda custom (64-bit)| (default, Apr 29 2018, 16:14:56) [GCC 7.2.0]TF1.11.0Horovod size: 4

    然后,在显示一些其他警告后,您将看到表的开头和一些数据点。如果您不想查看 1000 次批处理,请退出训练。

    Step Epoch Speed Loss FinLoss LR 0 0.0 105.6 6.794 7.708 6.40000 1 0.0 311.7 0.000 4.315 6.38721 100 0.1 3010.2 0.000 34.446 5.18400 200 0.2 3013.6 0.000 13.077 4.09600 300 0.2 3012.8 0.000 6.196 3.13600 400 0.3 3012.5 0.000 3.551 2.30401
  2. Horovod 首先使用所有本地 GPUs,然后再尝试使用集群成员的 GPUs。因此,要确保跨集群的分布式训练正常工作,请试用您要使用的完整数量的GPUs。例如,如果您有 4 个属于 p3.16xlarge 实例类型的成员,则集群中会有 32 个 GPUs。这是您希望试用全部 32 个 GPU 的位置。

    ./train_synthetic.sh 32

    您的输出与之前的测试类似。Horovod 大小为 32,速度约为四倍。随着此实验完成,您已测试您的领导及其与成员通信的能力。如果您遇到任何问题,请查看 Troubleshooting 部分。

准备 ImageNet 数据集

在本节中,您将下载 ImageNet 数据集,然后从原始数据集生成 TFRecord 格式的数据集。在 DLAMI 上为 ImageNet 数据集提供了一组预处理脚本,您可以将其用于 ImageNet 或用作其他数据集的模板。还提供了为 ImageNet 配置的主训练脚本。以下部分假定您已启动一个 DLAMI,其中有一个带 8 个 GPUs 的 EC2 实例。 我们建议使用 p3.16xlarge 实例类型。

在 DLAMI 上的 ~/examples/horovod/tensorflow/utils 目录中,您将找到以下脚本:

  • utils/preprocess_imagenet.py - 使用此参数将原始 ImageNet 数据集转换为 TFRecord 格式。

  • utils/tensorflow_image_resizer.py - 使用此参数将 TFRecord 数据集的大小调整为 ImageNet 训练所建议的大小。

准备 ImageNet 数据集

  1. 访问 image-net.org,创建账户,获取访问密钥,然后下载数据集。image-net.org 托管原始数据集。要下载它,您需要有 ImageNet 账户和访问密钥。该账户是免费的,要获取免费访问密钥,您必须同意 ImageNet 许可证。

  2. 使用图像预处理脚本从原始 TFRecord 数据集生成 ImageNet 格式数据集。从 ~/examples/horovod/tensorflow/utils 目录中:

    python preprocess_imagenet.py \ --local_scratch_dir=[YOUR DIRECTORY] \ --imagenet_username=[imagenet account] \ --imagenet_access_key=[imagenet access key]
  3. 使用图像调整大小脚本。如果您调整图像大小,训练将更快地运行并更好地符合 ResNet 参考文件。从 ~/examples/horovod/utils/preprocess 目录中:

    python tensorflow_image_resizer.py \ -d imagenet \ -i [PATH TO TFRECORD TRAINING DATASET] \ -o [PATH TO RESIZED TFRECORD TRAINING DATASET] \ --subset_name train \ --num_preprocess_threads 60 \ --num_intra_threads 2 \ --num_inter_threads 2

在单个 ResNet 上训练 ImageNet-50 DLAMI 模型

注意

使用 Horovod 在 ResNet50 数据集上训练 ImageNet CNN

  1. 导航到 ~/examples/horovod/tensorflow 文件夹。

    cd ~/examples/horovod/tensorflow
  2. 验证您的配置并设置要在训练中使用的 GPUs 数量。首先,查看与脚本位于相同文件夹中的 hosts。如果您使用的是少于 8 个 GPUs 的实例,则必须更新此文件。 默认情况下,它显示 localhost slots=8。 将数字 8 更新为您要使用的 GPUs 的数量。

  3. 提供了一个 Shell 脚本,该脚本采用您计划使用的 GPUs 数量作为其唯一参数。运行此脚本以开始训练。以下示例对 4 个 GPUs 使用 4。

    ./train.sh 4
  4. 完成此操作需要几个小时。它使用 mpirun 在 GPUs 中分发训练。

在 ResNetImageNet 集群上训练 DLAMI-50 s 模型

注意

此示例将指导您跨 ResNet 集群中的多个节点在准备好的数据集上训练 DLAMI-50 模型。

  • 要获得更快的性能,建议您将数据集本地置于集群的每个成员中。

    使用此 copyclust 函数,用于将数据复制到其他成员。

    function copyclust(){ while read -u 10 host; do host=${host%% slots*}; rsync -azv "$2" $host:"$3"; done 10<$1; };

或者,如果您的文件位于 S3 存储桶中,请使用 runclust 函数,用于将文件直接下载到每个成员。

runclust hosts "tmux new-session -d \"export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY && export AWS_SECRET_ACCESS_KEY=YOUR_SECRET && aws s3 sync s3://your-imagenet-bucket ~/data/tf-imagenet/ && aws s3 sync s3://your-imagenet-validation-bucket ~/data/tf-imagenet/\""

使用允许您一次性管理多个节点的工具将节省大量时间。您可以等待每个步骤并单独管理每个实例,也可以使用 tmux 或 screen 等工具断开连接并恢复会话。

在复制操作完成后,您已准备好开始训练。运行脚本,将 32 作为我们用于此次运行的 32 GPUs 的参数传递。如果您担心断开连接并终止会话(这将结束训练运行),请使用 tmux 或类似工具。

./train.sh 32

以下输出是您在使用 32 个 ImageNet 在 GPUs 上运行训练时看到的内容。 32 个 GPUs 需要 90–110 分钟。

Step Epoch Speed Loss FinLoss LR 0 0.0 440.6 6.935 7.850 0.00100 1 0.0 2215.4 6.923 7.837 0.00305 50 0.3 19347.5 6.515 7.425 0.10353 100 0.6 18631.7 6.275 7.173 0.20606 150 1.0 19742.0 6.043 6.922 0.30860 200 1.3 19790.7 5.730 6.586 0.41113 250 1.6 20309.4 5.631 6.458 0.51366 300 1.9 19943.9 5.233 6.027 0.61619 350 2.2 19329.8 5.101 5.864 0.71872 400 2.6 19605.4 4.787 5.519 0.82126 ... 13750 87.9 19398.8 0.676 1.082 0.00217 13800 88.2 19827.5 0.662 1.067 0.00156 13850 88.6 19986.7 0.591 0.997 0.00104 13900 88.9 19595.1 0.598 1.003 0.00064 13950 89.2 19721.8 0.633 1.039 0.00033 14000 89.5 19567.8 0.567 0.973 0.00012 14050 89.8 20902.4 0.803 1.209 0.00002 Finished in 6004.354426383972

在训练运行完成后,脚本将跟进评估运行。它将在领导上运行,因为它运行得足够快,而不必将作业分配给其他成员。以下是评估运行的输出。

Horovod size: 32 Evaluating Validation dataset size: 50000 [ip-172-31-36-75:54959] 7 more processes have sent help message help-btl-vader.txt / cma-permission-denied [ip-172-31-36-75:54959] Set MCA parameter "orte_base_help_aggregate" to 0 to see all help / error messages step epoch top1 top5 loss checkpoint_time(UTC) 14075 90.0 75.716 92.91 0.97 2018-11-14 08:38:28

下面是在运行时介于 14 到 15 分钟之间的 256 个 GPUs 中运行此脚本时的示例输出。

Step Epoch Speed Loss FinLoss LR 1400 71.6 143451.0 1.189 1.720 0.14850 1450 74.2 142679.2 0.897 1.402 0.10283 1500 76.7 143268.6 1.326 1.809 0.06719 1550 79.3 142660.9 1.002 1.470 0.04059 1600 81.8 143302.2 0.981 1.439 0.02190 1650 84.4 144808.2 0.740 1.192 0.00987 1700 87.0 144790.6 0.909 1.359 0.00313 1750 89.5 143499.8 0.844 1.293 0.00026 Finished in 860.5105031204224 Finished evaluation 1759 90.0 75.086 92.47 0.99 2018-11-20 07:18:18

Troubleshooting

以下命令可能有助于解决您在使用 Horovod 进行实验时出现的错误。

  • 如果训练因某种原因发生崩溃,则 mpirun 可能无法清理每台计算机上的所有 python 进程。在这种情况下,在启动下一个作业之前,请按如下所示停止所有计算机上的 python 进程:

    runclust hosts "pkill -9 python"
  • 如果进程突然完成且没有错误,请尝试删除您的日志文件夹。

    runclust hosts "rm -rf ~/imagenet_resnet/"
  • 如果弹出其他无法解释的问题,请检查您的磁盘空间。如果您已退出,请尝试删除日志文件夹,因为其中包含所有检查点和数据。您也可以为每个成员增加卷的大小。

    runclust hosts "df /"
  • 作为最后的手段,您也可以尝试重新启动。

    runclust hosts "sudo reboot"

如果您尝试在不受支持的实例类型上使用 TensorFlow with Horovod,则可能会收到以下错误代码:

--------------------------------------------------------------------------- NotFoundError Traceback (most recent call last) <ipython-input-3-e90ed6cabab4> in <module>() ----> 1 import horovod.tensorflow as hvd ~/anaconda3/envs/tensorflow_p36/lib/python3.6/site-packages/horovod/tensorflow/__init__.py in <module>() ** *34* check_extension('horovod.tensorflow', 'HOROVOD_WITH_TENSORFLOW', __file__, 'mpi_lib') ** *35* ---> 36 from horovod.tensorflow.mpi_ops import allgather, broadcast, _allreduce ** *37* from horovod.tensorflow.mpi_ops import init, shutdown ** *38* from horovod.tensorflow.mpi_ops import size, local_size, rank, local_rank ~/anaconda3/envs/tensorflow_p36/lib/python3.6/site-packages/horovod/tensorflow/mpi_ops.py in <module>() ** *56* ** *57* MPI_LIB = _load_library('mpi_lib' + get_ext_suffix(), ---> 58 ['HorovodAllgather', 'HorovodAllreduce']) ** *59* ** *60* _basics = _HorovodBasics(__file__, 'mpi_lib') ~/anaconda3/envs/tensorflow_p36/lib/python3.6/site-packages/horovod/tensorflow/mpi_ops.py in _load_library(name, op_list) ** *43* """ ** *44* filename = resource_loader.get_path_to_datafile(name) ---> 45 library = load_library.load_op_library(filename) ** *46* for expected_op in (op_list or []): ** *47* for lib_op in library.OP_LIST.op: ~/anaconda3/envs/tensorflow_p36/lib/python3.6/site-packages/tensorflow/python/framework/load_library.py in load_op_library(library_filename) ** *59* RuntimeError: when unable to load the library or get the python wrappers. ** *60* """ ---> 61 lib_handle = py_tf.TF_LoadLibrary(library_filename) ** *62* ** *63* op_list_str = py_tf.TF_GetOpList(lib_handle) NotFoundError: /home/ubuntu/anaconda3/envs/tensorflow_p36/lib/python3.6/site-packages/horovod/tensorflow/mpi_lib.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _ZN10tensorflow14kernel_factory17OpKernelRegistrar12InitInternalEPKNS_9KernelDefEN4absl11string_viewEPFPNS_8OpKernelEPNS_20OpKernelConstructionEE

更多信息

有关实用工具和示例,请参阅 DLAMI 的主目录中的 ~/examples/horovod 文件夹。

有关更多教程和示例,请参阅 Horovod GitHub 项目