• 在百度云启动分布式训练
    • 创建集群模板
    • 创建集群
    • 编写集群任务启动脚本
    • 获取分布式训练结果
    • 关闭集群

    在百度云启动分布式训练

    PaddlePaddle Fluid分布式训练,可以不依赖集群系统(比如MPI,Kubernetes)启动分布式训练。本章节将会以 百度云 为实例,说明如何在云端环境,甚至云端GPU环境启动大规模分布式任务。

    创建集群模板

    登录到百度云控制台,选择BCC服务,点击“创建实例”。选择地域,注意,只有一些地域有GPU服务器可选,选择合适的地域之后,再选择对应型号,然后创建一个空的服务器,如下图:../../../_images/create_gpu_machine.png

    • 在操作系统选项中,可以根据需要选择对应的版本,注意根据实际情况选择CUDA版本,这里我们选择CUDA-9.2。
    • 示例中选择机器付费方式为后付费,表示随着机器的释放,收费也会对应停止,对运行一次性任务会比较划算。在机器创建成功之后,执行下面的命令安装paddlepaddle GPU版本和相关依赖。
    1. apt-get update && apt-get install -y python python-pip python-opencv
    2. # 注:百度云cuda-9.2镜像默认没有安装cudnn和nccl2,需要手动安装,如果自行安装,需要从官网下载
    3. wget -q "http://paddle-train-on-cloud.cdn.bcebos.com/libcudnn7_7.2.1.38-1+cuda9.2_amd64.deb"
    4. wget -q "http://paddle-train-on-cloud.cdn.bcebos.com/nccl_2.2.13-1+cuda9.0_x86_64.txz"
    5. dpkg -i libcudnn7_7.2.1.38-1+cuda9.2_amd64.deb
    6. ln -s /usr/lib/x86_64-linux-gnu/libcudnn.so.7 /usr/lib/libcudnn.so
    7. unxz nccl_2.2.13-1+cuda9.0_x86_64.txz
    8. tar xf nccl_2.2.13-1+cuda9.0_x86_64.tar
    9. cp -r nccl_2.2.13-1+cuda9.0_x86_64/lib/* /usr/lib
    10. # 注:可以选择是否使用下面的pip镜像加速下载
    11. pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib==2.2.3
    12. pip install -i https://pypi.tuna.tsinghua.edu.cn/simple paddlepaddle-gpu==0.15.0.post97

    完成安装后,使用下面的测试程序,测试当前机器是否可以正确运行GPU训练程序,如果遇到报错,请根据报错提示修复运行环境问题。为了方便启动GPU集群,测试程序执行成功之后,选择当前服务器,然后选择“创建自定义镜像”,后续创建GPU集群时即可选择配置好的镜像。../../../_images/create_image.png

    • 测试程序:
    1. from __future__ import print_function
    2.  
    3. import paddle.fluid.core as core
    4. import math
    5. import os
    6. import sys
    7.  
    8. import numpy
    9.  
    10. import paddle
    11. import paddle.fluid as fluid
    12.  
    13. BATCH_SIZE = 64
    14. PASS_NUM = 1
    15.  
    16. def loss_net(hidden, label):
    17. prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')
    18. loss = fluid.layers.cross_entropy(input=prediction, label=label)
    19. avg_loss = fluid.layers.mean(loss)
    20. acc = fluid.layers.accuracy(input=prediction, label=label)
    21. return prediction, avg_loss, acc
    22.  
    23. def conv_net(img, label):
    24. conv_pool_1 = fluid.nets.simple_img_conv_pool(
    25. input=img,
    26. filter_size=5,
    27. num_filters=20,
    28. pool_size=2,
    29. pool_stride=2,
    30. act="relu")
    31. conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)
    32. conv_pool_2 = fluid.nets.simple_img_conv_pool(
    33. input=conv_pool_1,
    34. filter_size=5,
    35. num_filters=50,
    36. pool_size=2,
    37. pool_stride=2,
    38. act="relu")
    39. return loss_net(conv_pool_2, label)
    40.  
    41.  
    42. def train(use_cuda):
    43. if use_cuda and not fluid.core.is_compiled_with_cuda():
    44. return
    45. img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32')
    46. label = fluid.layers.data(name='label', shape=[1], dtype='int64')
    47. prediction, avg_loss, acc = conv_net(img, label)
    48.  
    49. test_program = fluid.default_main_program().clone(for_test=True)
    50.  
    51. optimizer = fluid.optimizer.Adam(learning_rate=0.001)
    52. optimizer.minimize(avg_loss)
    53.  
    54. place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
    55. exe = fluid.Executor(place)
    56.  
    57. train_reader = paddle.batch(
    58. paddle.reader.shuffle(
    59. paddle.dataset.mnist.train(), buf_size=500),
    60. batch_size=BATCH_SIZE)
    61. test_reader = paddle.batch(
    62. paddle.dataset.mnist.test(), batch_size=BATCH_SIZE)
    63. feeder = fluid.DataFeeder(feed_list=[img, label], place=place)
    64.  
    65.  
    66. exe.run(fluid.default_startup_program())
    67.  
    68.  
    69. for pass_id in range(PASS_NUM):
    70. for batch_id, data in enumerate(train_reader()):
    71. acc_np, avg_loss_np = exe.run(fluid.default_main_program(),
    72. feed=feeder.feed(data),
    73. fetch_list=[acc, avg_loss])
    74. if (batch_id + 1) % 10 == 0:
    75. print(
    76. 'PassID {0:1}, BatchID {1:04}, Loss {2:2.2}, Acc {3:2.2}'.
    77. format(pass_id, batch_id + 1,
    78. float(avg_loss_np.mean()), float(acc_np.mean())))
    79.  
    80. if __name__ == '__main__':
    81. train(True)

    创建集群

    完成创建镜像之后,可以使用这个配置好的镜像创建一个GPU集群,根据您的实际需求创建足够数量的GPU服务器,作为示例,这里启动2台GPU服务器,包括上一步创建的服务器,所以这里再启动一台新的服务器。

    点击“创建实例”,在相同地域选择同样配置的GPU服务器,注意选择刚才创建的镜像作为操作系统。../../../_images/create_more_nodes.png

    编写集群任务启动脚本

    为了方便在更多的GPU服务器上启动分布式训练任务,我们将使用fabric作为集群任务启动管理工具,您可以选择其他熟悉的集群框架,比如MPI, Kubernetes,本示例演示的方法仅针对简单集群环境,而且服务器之间可以互相ssh登录。

    安装fabric,需要执行:

    1. pip install fabric

    假设我们创建了2台GPU服务器,ip分别是 172.16.0.5,172.16.0.6 ,然后在第一台服务器上,先创建训练程序文件 dist_train_demo.py ,从这里下载代码。然后编写 fabfile.py 脚本,用于控制在不同服务器上启动训练任务的parameter server和trainer:

    1. from fabric import Group, task
    2.  
    3. endpoints = "172.16.0.5:6173,172.16.0.6:6173"
    4. port = "6173"
    5. pservers = 2
    6. trainers = 2
    7.  
    8. hosts = []
    9. eps = []
    10. for ep in endpoints.split(","):
    11. eps.append(ep)
    12. hosts.append(ep.split(":")[0])
    13.  
    14. def start_server(c):
    15. current_endpoint = "%s:%s" % (c.host, port)
    16. trainer_id = hosts.index(c.host)
    17. cmd = "python /root/work/dist_train_demo.py pserver %s %s %d %d &> /root/work/server.log.%s &" % (
    18. endpoints, current_endpoint, trainer_id, trainers, c.host)
    19. c.run(cmd)
    20.  
    21. def start_trainer(c):
    22. current_endpoint = "%s:%s" % (c.host, port)
    23. trainer_id = hosts.index(c.host)
    24. cmd = "python /root/work/dist_train_demo.py trainer %s %s %d %d &> /root/work/trainer.log.%s &" % (
    25. endpoints, current_endpoint, trainer_id, trainers, c.host)
    26. c.run(cmd)
    27.  
    28. @task
    29. def start(c):
    30. c.connect_kwargs.password = "work@paddle123"
    31. c.run("mkdir -p /root/work")
    32. c.put("dist_train_demo.py", "/root/work")
    33. start_server(c)
    34. start_trainer(c)
    35.  
    36. @task
    37. def tail_log(c):
    38. c.connect_kwargs.password = "work@paddle123"
    39. c.run("tail /root/work/trainer.log.%s" % c.host)

    保存上述代码到 fabfile.py 之后,执行

    1. fab -H 172.16.0.5,172.16.0.6 start

    就可以开始一个分布式训练任务。这个任务会在两台GPU服务器分别启动2个pserver进程和2个trainer进程开始训练。

    获取分布式训练结果

    示例任务会在 /root/work 下记录日志,分别为pserver.log.[IP]trainer.log.[IP] 的形式,可以手动在服务器上查看这些日志文件观察结果,也可以使用fabric获取所有节点的日志信息,比如:

    1. fab -H 172.16.0.5,172.16.0.6 tail-log

    关闭集群

    任务执行完成后,不要忘记释放掉GPU集群资源,勾选选择需要释放的服务器,选择“释放”,则会关闭机器并释放资源。如果需要执行新的任务,可以直接使用之前保存的镜像,启动新的集群,并参照前面的步骤开始训练。../../../_images/release.png