MindSpore易点通·精讲系列--网络构建之LSTM算子--中篇
Dive Into MindSpore – Distributed Training With GPU For Model Train
MindSpore易点通·精讲系列–模型训练之GPU分布式并行训练
本文开发环境
- Ubuntu 20.04
- Python 3.8
- MindSpore 1.7.0
- OpenMPI 4.0.3
- RTX 1080Ti * 4
本文内容摘要
- 基础知识
- 环境搭建
- 单卡训练
- 多卡训练–OpenMPI
- 多卡训练–非OpenMPI
- 本文总结
- 遇到问题
- 本文参考
1. 基础知识
1.1 概念介绍
在深度学习中,随着模型和数据的不断增长,在很多情况下需要使用单机多卡或者多机多卡进行训练,即分布式训练。分布式训练策略按照并行方式不同,可以简单的分为数据并行和模型并行两种方式。
- 数据并行
- 数据并行是指在不同的 GPU 上都 copy 保存一份模型的副本,然后将不同的数据分配到不同的 GPU 上进行计算,最后将所有 GPU 计算的结果进行合并,从而达到加速模型训练的目的。
- 模型并行
- 与数据并行不同,分布式训练中的模型并行是指将整个神经网络模型拆解分布到不同的 GPU 中,不同的 GPU 负责计算网络模型中的不同部分。这通常是在网络模型很大很大、单个 GPU 的显存已经完全装不下整体网络的情况下才会采用。
1.2 MindSpore中的支持
1.1
中介绍了理论中的并行方式,具体到MIndSpore
框架中,目前支持下述的四种并行模式:
- 数据并行:用户的网络参数规模在单卡上可以计算的情况下使用。这种模式会在每卡上复制相同的网络参数,训练时输入不同的训练数据,适合大部分用户使用。
- 半自动并行:用户的神经网络在单卡上无法计算,并且对切分的性能存在较大的需求。用户可以设置这种运行模式,手动指定每个算子的切分策略,达到较佳的训练性能。
- 自动并行:用户的神经网络在单卡上无法计算,但是不知道如何配置算子策略。用户启动这种模式,
MindSpore
会自动针对每个算子进行配置策略,适合想要并行训练但是不知道如何配置策略的用户。 - 混合并行:完全由用户自己设计并行训练的逻辑和实现,用户可以自己在网络中定义
AllGather
等通信算子。适合熟悉并行训练的用户。
对于大部分用户来说,其实能够用到的是数据并行模式,所以下面的案例中,会以数据并行模式来展开讲解。
2. 环境搭建
2.1 MindSpore安装
略。可参考笔者之前的文章MindSpore入门–基于GPU服务器安装MindSpore 1.5.0,注意将文章中的MindSpore
版本升级到1.7.0
。
2.2 OpenMPI安装
在GPU
硬件平台上,MindSpore
采用OpenMPI
的mpirun
进行分布式训练。所以我们先来安装OpenMPI
。
本文安装的是4.0.3
版本,安装命令如下:
wget -c https://download.open-mpi.org/release/open-mpi/v4.0/openmpi-4.0.3.tar.gz
tar xf openmpi-4.0.3.tar.gz
cd openmpi-4.0.3/
./configure --prefix=/usr/local/openmpi-4.0.3
make -j 16
sudo make install
echo -e "export PATH=/usr/local/openmpi-4.0.3/bin:\$PATH" >> ~/.bashrc
echo -e "export LD_LIBRARY_PATH=/usr/local/openmpi-4.0.3/lib:\$LD_LIBRARY_PATH" >> ~/.bashrc
source ~/.bashrc
使用mpirun --version
命令验证是否安装成功,输出如下内容:
mpirun (Open MPI) 4.0.3
Report bugs to http://www.open-mpi.org/community/help/
2.3 环境验证
上面基础环境安装完成后,我们对环境进行一个初步验证,来看看是否搭建成功。
验证代码如下:
# nccl_allgather.py
import numpy as np
import mindspore.ops as ops
import mindspore.nn as nn
from mindspore import context, Tensor
from mindspore.communication import init, get_rank
class Net(nn.Cell):
def __init__(self):
super(Net, self).__init__()
self.allgather = ops.AllGather()
def construct(self, x):
return self.allgather(x)
if __name__ == "__main__":
context.set_context(mode=context.GRAPH_MODE, device_target="GPU")
init("nccl")
value = get_rank()
input_x = Tensor(np.array([[value]]).astype(np.float32))
net = Net()
output = net(input_x)
print(output)
将上面代码保存到文件nccl_allgather.py
中,运行命令:
命令解读:
-n
后面数字代表使用GPU的数量,这里使用了机器内全部GPU。如果读者不想使用全部,记得设置相应的环境变量。
mpirun -n 4 python3 nccl_allgather.py
输出内容如下:
[[0.]
[1.]
[2.]
[3.]]
[[0.]
[1.]
[2.]
[3.]]
[[0.]
[1.]
[2.]
[3.]]
[[0.]
[1.]
[2.]
[3.]]
至此,我们的环境搭建完成,且验证成功。
3. 单卡训练
为了能够后续进行对比测试,这里我们先来进行单卡训练,以此做为基准。
3.1 代码部分
代码说明:
- 网络结构采用的是
ResNet-50
,读者可以在MindSpore Models
仓库进行获取,复制粘贴过来即可,ResNet-50代码链接。- 数据集采用的是
Fruit-360
数据集,有关该数据集的更详细介绍可以参看笔者之前的文章MindSpore易点通·精讲系列–数据集加载之ImageFolderDataset。数据集下载链接- 读者注意将代码中的
train_dataset_dir
和test_dataset_dir
替换为自己的文件目录。
单卡训练的代码如下:
import numpy as np
from mindspore import context
from mindspore import nn
from mindspore.common import dtype as mstype
from mindspore.common import set_seed
from mindspore.common import Tensor
from mindspore.communication import init, get_rank, get_group_size
from mindspore.dataset import ImageFolderDataset
from mindspore.dataset.transforms.c_transforms import Compose, TypeCast
from mindspore.dataset.vision.c_transforms import HWC2CHW, Normalize, RandomCrop, RandomHorizontalFlip, Resize
from mindspore.nn.loss import SoftmaxCrossEntropyWithLogits
from mindspore.nn.optim import Momentum
from mindspore.ops import operations as P
from mindspore.ops import functional as F
from mindspore.train import Model
from mindspore.train.callback import CheckpointConfig, ModelCheckpoint, LossMonitor
from scipy.stats import truncnorm
# define reset50
def create_dataset(dataset_dir, mode="train", decode=True, batch_size=32, repeat_num=1):
if mode == "train":
shuffle = True
else:
shuffle = False
dataset = ImageFolderDataset(
dataset_dir=dataset_dir, shuffle=shuffle, decode=decode)
mean = [127.5, 127.5, 127.5]
std = [127.5, 127.5, 127.5]
if mode == "train":
transforms_list = Compose(
[RandomCrop((32, 32), (4, 4, 4, 4)),
RandomHorizontalFlip(),
Resize((100, 100)),
Normalize(mean, std),
HWC2CHW()])
else:
transforms_list = Compose(
[Resize((128, 128)),
Normalize(mean, std),
HWC2CHW()])
cast_op = TypeCast(mstype.int32)
dataset = dataset.map(operations=transforms_list, input_columns="image")
dataset = dataset.map(operations=cast_op, input_columns="label")
dataset = dataset.batch(batch_size=batch_size, drop_remainder=True)
dataset = dataset.repeat(repeat_num)
return dataset
def run_train():
context.set_context(mode=context.GRAPH_MODE, device_target="GPU")
set_seed(0)
train_dataset_dir = "/mnt/data_0002_24t/xingchaolong/dataset/Fruits_360/fruits-360_dataset/fruits-360/Training"
test_dataset_dir = "/mnt/data_0002_24t/xingchaolong/dataset/Fruits_360/fruits-360_dataset/fruits-360/Test"
batch_size = 32
train_dataset = create_dataset(dataset_dir=train_dataset_dir