
# 创建多机多卡的分布式训练（DistributedDataParallel）
在深度学习领域，随着模型复杂度的不断提升和训练规模的不断扩大，传统的单机训练方式已难以满足实际需求。在这一背景下，如何在多机多卡环境下实现高效的分布式训练成为了亟待解决的关键问题。
针对这一问题，本章节将详细介绍基于PyTorch引擎的多机多卡数据并行训练方法。本文不仅提供具体的代码适配操作过程，还通过完整的代码示例帮助读者更好地理解和实践。特别地，本文以ResNet18在CIFAR10数据集上的图像分类任务为例，展示了如何进行分布式训练改造（DDP），为读者提供可直接参考和复现的实践方案。
#### 训练流程简述
相比于DP，DDP能够启动多进程进行运算，从而大幅度提升计算资源的利用率。可以基于torch.distributed实现真正的分布式计算，具体的原理此处不再赘述。大致的流程如下：
1. 初始化进程组。
2. 创建分布式并行模型，每个进程都会有相同的模型和参数。
3. 创建数据分发Sampler，使每个进程加载一个mini batch中不同部分的数据。
4. 网络中相邻参数分桶，一般为神经网络模型中需要进行参数更新的每一层网络。
5. 每个进程前向传播并各自计算梯度。
6. 模型某一层的参数得到梯度后会马上进行通讯并进行梯度平均。
7. 各GPU更新模型参数。
具体流程图如下：
图1多机多卡数据并行训练   
![](https://support.huaweicloud.com/usermanual-standard-modelarts/figure/zh-cn_image_0000001965775642.png "点击放大")
#### 代码改造点
- 引入多进程启动机制：初始化进程
- 引入几个变量：tcp协议，rank进程序号，worldsize开启的进程数量
- 分发数据：DataLoader中多了一个Sampler参数，避免不同进程数据重复
- 模型分发：DistributedDataParallel(model)
- 模型保存：在序号为0的进程下保存模型
```
import torch
class Net(torch.nn.Module):
pass
model = Net().cuda()
### DistributedDataParallel Begin ###
model = torch.nn.parallel.DistributedDataParallel(Net().cuda())
### DistributedDataParallel End ###
```
#### 多节点分布式调测适配及代码示例
- [示例：创建DDP分布式训练（PyTorch+GPU）](https://support.huaweicloud.com/usermanual-standard-modelarts/modelarts-distributed-0011.html)：提供了分布式训练调测具体的代码适配操作过程和代码示例。
- [示例：创建DDP分布式训练（PyTorch+NPU）](https://support.huaweicloud.com/usermanual-standard-modelarts/modelarts-distributed-0012.html)：针对Resnet18在cifar10数据集上的分类任务，给出了分布式训练改造(DDP)的完整代码示例，供用户学习参考。
 
#### 常见问题
1. **示例代码中如何使用不同的数据集？**
   - 上述代码如果使用cifar10数据集，则将数据集下载并解压后，上传至OBS桶中，文件目录结构如下：
     ```
     DDP
     |--- main.py
     |--- input_dir
     |------ cifar-10-batches-py
     |-------- data_batch_1
     |-------- data_batch_2
     |-------- ...
     ```
     其中"DDP"为创建训练作业时的"代码目录"，"main.py"为上文代码示例（即创建训练作业时的"启动文件"），"cifar-10-batches-py"为解压后的数据集文件夹（放在input_dir文件夹下）。
     
   
   - 如果使用自定义的随机数据，则将代码示例中的参数"custom_data"改为"true"，修改后内容如下：
     ```
     parser.add_argument('--custom_data', default='true')
     ```
     然后直接运行代码示例"main.py"即可，创建训练作业的参数与上图相同。
     
    
2. **为什么DDP可以不输入主节点ip？**
   "parser.add_argument('--init_method', default=None, help='tcp_port')"中的init method参数值会包含主节点的ip和端口，由平台自动入参，不需要用户输入主节点的ip和端口。
   
 
