GPU集群配置MXNet+CUDA
为方便控制集群,写了脚本cmd2all.sh
#!/bin/bash
if [ $# -lt 3 ]; thenecho "usage: $0 [type cmds hosts]"echo "for example: ./cmd2all.sh \"cmds\" \"touch t1.txt\" \"gpu1 gpu2\""echo "for example: ./cmd2all.sh \"path\" \"/home/gbxu/CUDA/" \"gpu1 gpu2\""exit -1;
fi
type=$1 # "cmds"
cmds_or_path=$2 # "touch test.txt"
#hosts=$3
hosts=(gpu10 gpu11 gpu12 gpu13 gpu14 gpu15 gpu16 gpu17 gpu18)
if [$type == "cmds"]
thenfor host in ${hosts[@]}dossh $host nohup $cmds_or_path &done
fiif [$type == "path"]
thenfor host in ${hosts[@]}donohup scp -r $cmds_or_path $host:~/ &done
fi
使用virtualenv
如果是python3的环境,需要virtualenv -p /usr/bin/python3 mxnetGPU
使用virtualenv,创建新的virtualenv,并修改.bashrc,使得在每次进入终端时activate虚拟环境(方便后期分布式运行)
hosts="gpu10 gpu11 gpu12 gpu13 gpu14 gpu15 gpu16 gpu17 gpu18 "
./cmd2all.sh "cmds" "sudo yum -y install epel-release && sudo yum -y install python-pip && sudo pip install virtualenv && virtualenv mxnetGPU" $hosts
./cmd2all.sh "cmds" "echo \"## gbxu MXnet-GPU\" >> .bashrc" $hosts
./cmd2all.sh "cmds" "echo \"source mxnetGPU/bin/activate\" >> .bashrc" $hosts
尝试在gpu10安装
Install NVIDIA Driver
本身已有驱动则该操作不必要。
lspci | grep -i nvidia #查看设备
modinfo nvidia #查看驱动
sudo yum -y remove nvidia-*
sudo sh NVIDIA-Linux-x86_64-390.25.run #安装驱动
Install CUDA:
see documents:
- offline安装,online版本可能出现依赖缺失。
- 所有版本
CUDA是NVIDIA推出的用于自家GPU的并行计算框架,只有当要解决的计算问题是可以大量并行计算的时候才能发挥CUDA的作用。
下载: 见offline安装
#copy installer && run# 若安装错误需要先卸载
sudo yum -y remove "cuda-*"
sudo rm -rf /usr/local/cuda*
sudo rpm -i cuda-repo-rhel7-9-2-local-9.2.148-1.x86_64.rpm
sudo yum clean all
sudo yum -y install cuda
gpu10利用yum local的安装出现问题,后来下载cuda_9.2.148_396.37_linux.run
sudo sh cuda_9.2.148_396.37_linux.run
安装
并且在安装(or not, just try)时同意nvidia驱动,并且一路yes和default。
or, add /usr/local/cuda-9.2/lib64 to /etc/ld.so.conf and run ldconfig as root
添加CUDA环境变量
# export LD_LIBRARY_PATH=/usr/local/cuda/lib64/:$LD_LIBRARY_PATH
echo -e "export LD_LIBRARY_PATH=/usr/local/cuda/lib64/:\$LD_LIBRARY_PATH" >> .bashrc
# export PATH=$PATH:/usr/local/cuda/bin
echo -e "export PATH=\$PATH:/usr/local/cuda/bin" >> .bashrc
测试CUDA
nvcc -V
nvidia-smi
cd /home/gbxu/NVIDIA_CUDA-9.2_Samples/1_Utilities/deviceQuery
make
./deviceQuery # 结果pass则安装成功
Install cuDNN:
see documents
cuDNN(CUDA Deep Neural Network library):是NVIDIA打造的针对深度神经网络的加速库,是一个用于深层神经网络的GPU加速库。如果你要用GPU训练模型,cuDNN不是必须的,但是一般会采用这个加速库。
tar -xzvf cudnn-9.2-linux-x64-v7.1.tgz
sudo cp cuda/include/cudnn.h /usr/local/cuda/include
sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn.h
安装Prerequisites
see documents
sudo yum -y install build-essential git lapack-devel openblas-devel opencv-devel atlas-devel
complie MXNet
see:documents
git clone --recursive https://github.com/apache/incubator-mxnet.git
cd incubator-mxnet
make clean_all
make -j $(nproc) USE_OPENCV=1 USE_BLAS=openblas USE_CUDA=1 USE_CUDA_PATH=/usr/local/cuda USE_CUDNN=1 USE_DIST_KVSTORE=1 USE_PROFILER=1
install MXNet in python
cd python
pip uninstall -y mxnet
pip install -e .
test MXNet in python
python
>>> import mxnet as mx
>>> a = mx.nd.zeros((2,3), mx.gpu())
install python lib
请根据最后运行MXNet任务时查缺补漏
pip install numpy requests
预设编译参数
cd到源代码主目录,在makefile文件中预设编译参数,
# vim incubator-mxnet/Makefile
cmpl:make -j $(nproc) USE_OPENCV=1 USE_BLAS=openblas USE_DIST_KVSTORE=1cmplgpu:make -j $(nproc) USE_OPENCV=1 USE_BLAS=openblas USE_DIST_KVSTORE=1 USE_CUDA=1 USE_CUDA_PATH=/usr/local/cuda USE_CUDNN=1
之后使用make指令编译更为便捷。
make cmplgpu
批量安装环境
在gpu11-gpu18批量安装环境
先用1.sh
将数据传到nodes,
1.sh
hosts=(gpu11 gpu12 gpu13 gpu14 gpu15 gpu16 gpu17 gpu18)
for host in ${hosts[@]}
doecho run 1.sh at $hostscp -r process_data gbxu@$host:~/
done
再用2.sh
在各nodes运行scripts_in_nodes.sh
脚本即可。
2.sh
hosts=(gpu12 gpu13 gpu14 gpu15 gpu16 gpu17 gpu18)
for host in ${hosts[@]}
doecho run 2.sh at $hostscp process_data/scripts_in_nodes.sh gbxu@$host:~/process_data/ssh gbxu@$host "cd process_data && nohup ./scripts_in_nodes.sh &"
done
scripts_in_nodes.sh
sudo yum -y remove "cuda-*"
sudo rpm -i cuda-repo-rhel7-9-2-local-9.2.148-1.x86_64.rpm
sudo yum clean all
sudo yum -y install cuda
echo -e "export LD_LIBRARY_PATH=/usr/local/cuda/lib64/:\$LD_LIBRARY_PATH" >> ~/.bashrc
echo -e "export PATH=\$PATH:/usr/local/cuda/bin" >> ~/.bashrctar -xzvf cudnn-9.2-linux-x64-v7.1.tgz
sudo cp cuda/include/cudnn.h /usr/local/cuda/include
sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn.hsudo yum -y install build-essential git lapack-devel openblas-devel opencv-devel atlas-develpip install numpy requests # 请根据最后运行MXNet任务时查缺补漏
编译、安装MXNet
之后只需在一台host上编译mxnet即可,余下用MXNet的同步机制即可。
在gpu10上启动训练
- 需要加库文件放到同步的文件夹下:
cd incubator-mxnet/example/image-classification
echo -e "gpu11\ngpu12\ngpu13\ngpu14\ngpu15\ngpu16\ngpu17\ngpu18\n" > hosts
rm -rf mxnet
cp -r ../../python/mxnet .
cp -r ../../lib/libmxnet.so mxnet
- 然后执行命令,该命令会同步文件夹cluster上启动8个worker,1个server
# export DMLC_INTERFACE='ib0'; # ib尚未配置好
python ../../tools/launch.py -n 8 -s 1 --launcher ssh -H hosts --sync-dst-dir /home/gbxu/image-classification_test/ python train_mnist.py --network lenet --kv-store dist_sync --num-epochs 1 --gpus 0
ENJOY
- multiple machines each containing multiple GPUs 的训练见docs
- 其中
dist_sync_device
替代dist_sync
。因为cluster为多GPU,见docs - mxnet-make-install-test.sh
cd incubator-mxnet
make clean_all
make -j $(nproc) USE_OPENCV=1 USE_BLAS=openblas USE_CUDA=1 USE_CUDA_PATH=/usr/local/cuda USE_CUDNN=1 USE_DIST_KVSTORE=1 USE_PROFILER=1
cd python
pip uninstall -y mxnet
pip install -e .
cd ../example/image-classification
echo -e "gpu11\ngpu12\ngpu13\ngpu14\ngpu15\ngpu16\ngpu17\n" > hosts
rm -rf mxnet # example/image-classification下的
cp -r ../../python/mxnet .
cp -r ../../lib/libmxnet.so mxnet
export DMLC_INTERFACE='ib0';
python ../../tools/launch.py -n 8 -s 1 --launcher ssh -H hosts --sync-dst-dir /home/gbxu/image-classification_test/ python train_mnist.py --network lenet --kv-store dist_sync --num-epochs 1 --gpus 0
MXNet 以数据并行的方式进行单机多卡训练
MXNet 支持在多 CPU 和 GPU 上进行训练。其中,这些 CPU 和 GPU 可以分布在不同的服务器上。
一台 GPU 机器上的每块 GPU 都有自己的编号(编号从 0 开始计数)。如果想使用某一块特定的 GPU 卡,即可以在代码中直接指定 context(ctx); 也可以在命令行中传递参数–gpus。
例如: 如果想在 python 中使用 GPU 0 和 GPU 2,可以使用下面的代码创建网络模型。
如果多个 GPU 的计算能力不同,那么可以根据它们的计算性能来划分工作负载。比如,如果 GPU 0 是 GPU 2 性能的 3 倍,那么可以提供一个额外的负载选项 work_load_list=[3, 1]。
如果所有其它超参都相同,在多个 GPU 上训练结果应该和单 GPU 上的训练结果相同。但在实际应用中,由于随机存取(随机顺序或其它 augmentations),使用不同的种子初始化权重和 CuDNN,结果可能不同。
我们可以控制梯度聚合和模型更新(如执行,训练过程)的位置,通过创建不同的 KVStore(数据通信模块)。即可以使用 mx.kvstore.create(type)来创建一个实例,也可以使用程序的参数–kv-store type 来实现功能。
两种常用功能
-
local: 所有的梯度都拷贝到CPU内存完成聚合,同时在CPU内存上完成权值的更新并拷贝回每个GPUworker。这种方式主要在于CPU与GPU,主要的性能负载在于CPU拷贝的负载。
-
device: 梯度聚合和权值更新都在GPU上完成。GPU之间的如果支持Peer to Peer通信(PCIe or NVLink),将避免CPU拷贝的负载,可以大大减轻CPU的负担,仅受限于通信带宽。PCIe 与NVLink通信带宽不同,NVLink具备告诉的Peer to Peer通信带宽。
注意: 如果有大量的 GPU(比如: >=4),建议使用 device 能获取更好的性能。
MXNet 以数据并行的方式进行多机多卡训练
在介绍 MXNet 分布式训练之前,先介绍几个关键性的概念方便理解 MXNet 的分布式训练:
三种进程类型
-
Worker: worker进程会对每一批次(batch_size)的数据样本进行训练。对批数据进行处理之前,workers会从servers服务器pull权重。对批处理数据处理完毕之后workers会聚合梯度数据发送给servers。(如果训练模型的工作负载比较高,建议最好不要把worker和server运行在相同的机器上)。
-
Server: 可以运行多个server服务进程,用于存储模型参数并与worker进行通讯。
-
Scheduler: 一个调度器,在集群中负责调度的角色。主要包含: 等待各个node节点数据的上报数据并让各个node节点知道彼此的存在并互相通讯。
进程之间的关系如下图所示:
工作流程:
1.worker, server 向 scheduler 注册,获取相关的信息。
2.worker 从 server 端 pull 参数 w。
3.worker 基于参数 w 和数据计算梯度,然后 push 梯度到 server。
4.server 更新参数 w。
5.反复执行 2-4 过程。
KVStore
KVStore 是 MXNet 提供的一个分布式 key-value 存储,用来进行数据交换。KVStore 本质的实现是基于参数服务器。
-
通过引擎来管理数据一致性,这使得参数服务器的实现变得简单,同时使得KVStore的运算可以无缝的与其他部分结合在一起。
-
使用两层的通讯结构,原理如下图所示。第一层的服务器管理单机内部的多个设备之前的通讯。第二层服务器则管理机器之间通过网络的通讯。第一层的服务器在与第二层通讯前可能合并设备之间的数据来降低网络带宽消费。同时考虑到机器内和外通讯带宽和延时的不同性,可以对其使用不同的一致性模型。例如第一层用强的一致性模型,而第二层则使用弱的一致性模型来减少同步开销。
通过调用 mxnet.kvstore.create()函数并传递 dist 关键字参数来开启分布式训练的 KVStore 模式:
kv = mxnet.kvstore.create(‘dist_sync’)
复制代码
分布式训练模式
当 KVStore 被创建并且包含 dist 关键参数就会开启分布式训练模式。通过使用不同类型的 KVStore,可以启用不同的分布式培训模式。具体如下:
-
dist_sync: 已同步的方式进行分布式训练,在处理每批次(batch)的数据时,所有的workers需要使用相同的模型参数集合。这意味着servers参数服务需要接收来自所有workers模型参数之后,才能进行下一个批次数据的处理。因此在使用这种分布式训练方式时,server参数服务需要等到所有的worker处理完毕之后,并且如果其中的某一个worker异常,会导致整个训练的过程halts。
-
dist_async: 已异步的方式进行分布式训练,server参数服务只要收到worker的计算梯度就会立即更新存储。这意味着哪个worker处理完当前的批次数据,就可以继续下一批次数据的处理。因此该种方式的分布式训练方式比dist_sync要快,但是需要花费更多的epochs去收敛。
-
dist_sync_device: 该分布式训练模式与dist_sync训练模式相同,只是dist_sync_device模式会在多GPUs上进行梯度聚合和更新权重,而dist_sync是在CPU上进行这些操作。这种模式比dist_sync要快,因为GPU和CPU之前的通信,但是会占用更多的GPU显存。
-
dist_async_device: 该模式和dist_sync_device类似,但是是已异步的方式进行的。
开启分布式训练
MXNet 为了用户方便的进行分布式训练提供了一个 tools/launch.py 脚本。并且支持对多种类型的集群资源进行管理,如: ssh,mpirun,yarn,sge。
如上图所示,将代码 clone 到本地后,进入 gluon 目录,使用 image_classification.py 和 CIFAR10 数据集来对 VGG11 模型进行分布式训练。
虽然 MXNet 实现了多机多卡的分布式训练,但是在资源隔离,资源调度,资源限制以及大规模训练时数据共享都是不能满足需求的,所以接下来介绍下 MXNet 基于 Kubeflow 的大规模分布式训练。
MXNet 结合 kubeflow 进行分布式训练
在将 MXNet 结合 kubeflow 进行分布式训练之前,首先需要安装 kubeflow 环境之前已经介绍了,这里就不在详细说明了。当 kubeflow 基础环境部署完成之后,需要针对 MXNet 安装 mxnet-operator。
安装 mxnet-operator
安装完成后,执行以下命令,检验 mxnet 是否安装成功:
kubectl get crd
复制代码
输出如下内容代表 mxnet-operator 安装成功:
基于 kubeflow 测试 MXNet 分布式训练
1 准备测试的训练镜像
示例代码: https://github.com/deepinsight/insightface
Dockerfile 文件内容:
2 创建分布式网络文件系统数据卷(cephfs)
由于我们是基于 kubernetes 的 pv 和 pvc 的方式使用数据卷,所有集群中需要事先安装好 storage-class install,这样当用户创建 pvc 时,会通过 storage-class 自动的创建 pv。
当创建好 pv 之后,用户可以将该数据卷 mount 到自己的开发机上,并将需要训练的数据集移到该数据卷。用于之后创建训练 worker pod 的时候,挂载到 worker 容器中,供训练模型使用。
3 创建 mxnet 分布式训练任务
4 创建训练任务
kubectl create -f insightface-train.yaml
复制代码
5 查看任务运行情况
6 查看训练日志的信息
登录到具体的 node 计算节点通过 docker logs 命令查看训练的日志:
docker logs -f fc3d73161b27
复制代码
更多资讯或疑问内容添加小编微信, 回复 “Python” ,领取更多资料哦
总结
虽然已经完成了 mxnet 结合 kubeflow 实现大规模的分布式训练,但是除了功能上的基本跑通,还存在很多因素影响分布式训练的性能,如: GPU 服务器的网络带宽,普通的我们使用的以太网因为通信延迟的原因,会大大影响多机扩展性。InfiniBand(IB)网络和 RoCE 网络因为支持 RDMA,大大降低了通信延迟,相比之下,20G 的以太网格延迟会大大提升。当然,对于现有的普通以太网络,也可以通过别的方法优化通信带宽的减少,比方说梯度压缩。通过梯度压缩,减少通信带宽消耗的同时,保证收敛速度和精度不会有明显下降。MXNet 官方提供了梯度压缩算法,按照官方数据,最佳的时候可以达到两倍的训练速度提升,同时收敛速度和精度的下降不会超过百分之一。还有如果使用分布式网络文件系统进行数据集的存储,如果解决吞吐量和网络延迟的问题。以及本地磁盘是否是 SSD,还是在训练时是否需要对大文件的数据集进行 record.io 文件格式的处理及训练前数据集的切分等等问题,都需要更进一步的处理。