深度学习:MindSpore自动并行

随着模型规模的逐渐增大,需要的算力逐渐增强,但是算力需求增长速度远高于芯片算力增长速度。现在唯一的解决方案只有通过超大规模集群训练大模型。

大集群训练大模型的挑战

内存墙

200B参数量的模型,参数内存占用745GB内存,训练过程需要3500GB内存(参数+激活+梯度+优化器状态),一个模型需要128张卡才能放下。

性能墙

大模型切分到集群后,通信成为了主要的性能瓶颈。模型训练时,主要有几种并行策略:

  • 模型并行:模型的不同层或不同部分被分布在不同设备
  • Pipeline并行:不同部分的前向计算和反向计算被分配到不同设备
  • 数据并行:不同设备处理同一个模型的不同批次

设备通讯主要发生在激活函数或者梯度需要在不同集群间传递(模型、管道并行),参数在GPU间需要同步(数据并行)。

大模型数千亿的参数导致了巨大的激活值和梯度值。受有限带宽的影响,在不同的设备间转移这些数据成为瓶颈。同时,即使带宽足够,小信息间初始化通信的延迟也会显著增加。

效率墙

算法的分布式并行开发一直是一道难题。如何让用户高效编写分布式代码,超大规模AI训练需要怎样的编程范式?

调优墙

在数千节点集群上,要保证计算的正确性、性能、可用性,手工难以全面兼顾。

大集群快速故障恢复

千卡大集群训练时间长,故障不可避免,跨苏故障恢复可减少等待时间。MindSpore解决方法如下:

  1. 定期保存CKPT(CheckPoint)文件
  2. 故障检测隔离
  3. 资源重条度
  4. 加载故障时刻CKPT
  5. 恢复训练任务

基于HCCL集合通信库实现分布式训练

HCCL(Huawei Collective Communication Library)基于Ascend芯片的高性能集合通信库,提供单机多卡、多机多卡集合通信原语。 

数据并行

左右表示两个Ascend卡各自运行一个进程,每张卡上运行同一个模型、参数、优化器状态,复制n份,每张卡输入不同数据,进行一次反向传播后获得梯度, 随后进行一次allreduce,把每一张卡上得到的梯度聚合至一起,取平均后分发回各张卡。

存在问题:

要求单卡能放下整个模型

多卡训练时内存冗余(多存了n-1份模型参数)

MindSpore中以BERT与训练为例的数据并行

from mindspore.parallel._utils import _get_device_num, _get_gradients_mean

# 6. Pretrain
mean = _get_gradients_mean()
degree = _get_device_num()
grad_reducer = nn.DistributedGradReducer(optimizer.parameters, mean, degree)

def train_step(input_ids, input_mask, masked_lm_ids, masked_lm_positions, masked_lm_weghts, next_sentence_label, segment_ids):
    status = init_register()
    input_ids = ops.depend(input_ids, status)
    (total_loss, masked_lm_loss, next_sentence_loss), grads = grad_fn(input_ids, input_mask, segment_ids, masked_lm_ids, masked_lm_positions, masked_lm_weights, next_sentence_label)
    grads = clip_by_global_norm(grads, clip_norm=1.0)
    # 进行allreduce操作,分发梯度
    grads = grad_reducer(grads)
    status = all_finite(grads, status)
    
    if status:
        total_loss = loss_scaler.unscale(total_loss)
        grads = loss_scaler.unscale(grads)
        total_loss = ops.depend(total_loss, optimizer(grads))
    total_loss = ops.depend(total_loss, loss_scaler.adjust(status))
    return total_loss, masked_lm_loss, next_sentence_loss, status

模型并行

 模型并行是算子层面的并行,他利用某些算子的特性将算子拆分到多个设备上进行计算。因此不是网络中的所有算子都可以拆分计算。可以产分的算子需满足如下特性:

  • 可以并行计算的算子
  • 算子其中一个输入来自parameter

矩阵乘法(全连接层、attention核心)

左侧为一张卡进行矩阵乘法的示意图。

右侧为模型并行在两张卡上的示意图:X保持一样,W参数矩阵分为两份,分别和X进行矩阵乘法。最后将两张卡乘法的输出结果合并到一起。

即X.shape为(m, k),W1.shape为(k, n),拆分后,W1'.shape为(k, n/2),W1''.shape为(k, n/2),相乘输出为(m, n/2),合并后仍为(m, n) 。

但不是所有算子都适合这样的模型并行。

MindSpore算子级并行

MindSpore屏蔽了模型并行的前置和后置工作(通信、算子排布),开发者只需关心数据如何进行切分即可。

  • MindSpore对每个算子独立建模,用户可以设置正向网络中每个算子的切分策略(对于未设置的算子,默认按数据并行进行切分)。
  • 在构图阶段,框架将遍历正向图,根据算子的切分策略对每个算子及其输入张量进行切分建模,使得该算子的计算逻辑在切分前后保持数学等价。
  • 框架内部使用Tensor Layout来表达输入输出张量在集群中的分布状态,Tensor Layout中包含了张量和设备间的映射关系,用户无需感知模型各切片在集群中如何分布,框架将自动调度分配。
  • 框架还将遍历相邻算子间张量的Tensor Layout,如果前一个算子输出张量作为下一个算子的输入张量,且前一个算子输出张量的Tensor Layout与下一个算子输入张量的TensorLayout不同,则需要在两个算子之间进行张量重排布(Tensor Redistribution)
  • 对于训练网络来说,框架处理完正向算子的分布式切分之后,依靠框架的自动微分能力即能自动完成反向算子的分布式切分。

示例

用户在4卡计算两个连续的二维矩阵乘法Z=(X * W) * V,第一个矩阵Y = X * W,用户想把X按行切4fen(数据并行),第二个矩阵Z = Y * V,用户想把V按列切4份(模型并行)

import mindspore.nn as nn
from mindspore import ops
import mindspore as ms

ms.set_auto_parallel_context(parallel_mode="semi_autoparallel", device_num=4)

class DenseMatMulNet(nn.Cell):
    def __init__(self):
        super(DenseMatMulNet, self).__init__()
        # 切分只需要配置shard接口即可
        # 对于matmul1, 接受两个输入,第一个输入,第一维切成四份,第二维不进行切割
        # 第二个输入,均不切割
        self.matmul1 = ops.MatMul.shard((4, 1), (1, 1))
        # 对于matmul2,第二个输入的第二个维度切成四份
        self.matmul2 = ops.MatMul.shard((1, 1), (1, 4))
       
    def construct(self, x, w ,v):
        y = self.matmul1(x, w)
        z = self.matmul2(y, v)
        return z

# a simple example to demenstarte auto data parallel and model parallel on Mindspore
import sys
import numpy as np
import mindspore.nn as nn
import mindspore as ms
from mindspore.nn import Cell, Momentum
from mindspore.ops import operations as ops
from mindspore.nn import SoftmaxCrossEntropyWithLogits
from mindspore import train
import mindspore.dataset as ds
import mindspore.communication as D
from mindspore.common.initializer import initializer

# generate fake dataset
step_per_epoch = 4

def get_dataset(*inputs):
    def generate():
        for _ in range(step_per_epoch):
            yield inputs
    return generate

# define a simple net which will cut data into pieces for multi-npu training
class Net(Cell):
    def __init__(self):
        super().__init__()
        self.matmul = ops.MatMul().shard(((1, 2), (2, 1)))
        self.weight = ms.Parameter(initializer("normal", [32, 16]), "w1")
        self.relu = ops.ReLU().shard(((2, 1),))
        
    def construct(self, x):
        out = self.matmul(x, self.weight)
        out = self.relu(out)
        return out

# 设置运行环境的context。
# 在运行程序之前,应配置context。如果没有配置,默认情况下将根据设备目标进行自动设置。
# Compile the graph once and execute it multiple times.
ms.set_context(mode=ms.GRAPH_MODE, device_target="Ascend")

# Initialize distributed training environment
D.init()
# get rank of current device in the whole distributed computation
rank = D.get_rank()
# set semi-auto-parallel
ms.set_auto_parallel_context(parallel_mode="semi_auto_parallel", device_num=2, full_batch=True)

np.random.seed(1)

input_data = np.random.rand(16, 32).astype(np.float32)
label_data = np.random.rand(16, 16).astype(np.float32)
fake_dataset = get_dataset(input_data, label_data)

net = Net()

# define callback function while traing, including loss monitor and checkpoint file save path
callback = [train.LossMonitor(), train.ModelCheckpoint(directory="{}".format(rank))]
# generate dataset and define data column
dataset = ds.GeneratorDataset(fake_dataset, ["input", "label"])
# define loss function
loss = nn.SoftmaxCrossEntropyWithLogits()

lr = 1e-3
momentum=0.1
num_epochs = 5

optimizer = nn.Momentum(net.trainable_params(), lr, momentum)

model = ms.Model(net, loss_fn=loss, optimizer=optimizer)

model.train(num_epochs, dataset, callbacks=callback, dataset_sink_mode=False)

Pipeline并行

受Server间通信带宽低的影响,传统数据并行叠加模型并行的混合模式性能表现欠佳,需要引入流水线并行。流水线并行是将神经网络中的算子切分为多个阶段,再把阶段映射到不同设备上,使得不同设备去计算神经网络的不同部分。相当于每张卡上只放模型的其中基层或一层

如果Pipeline并行每次都知计算一批数据,不能充分利用多卡优势,因为总有卡处于空闲在等待数据。

wile提升效率,流水线并行进一步把mini-barch划分为micro-batch,在微批次中采用流水线式的执行序列,从而提升效率。

1F1B

一次个micro-batch进行正向后,立即进行一次反向计算。使得内存可以更早释放,进而确保内存使用峰值更低。1F1B是现在的主流方式。 

示例

MindSpore中,Pipeline的实现方式非常简单。通过调用pipeline_stage接口来指定每个layer要在哪个stage上去执行。pipeline接口的洗礼度微Cell,即只要继承了nn.Cell的实例,就要配置pipeline_stage,并且需要按照网络执行的先后顺序,从小到大进行配置。

...

class ResNet(nn.Cell):
    """ResNet"""

    def __init__(self, block, num_classes=100, batch_size=32):
        """init"""
        super(ResNet, self).__init__()
        self.batch_size = batch_size
        self.num_classes = num_classes
        self.head = Head()

        self.layer1 = MakeLayer0(block, in_channels=64, out_channels=256, stride=1)
        self.layer2 = MakeLayer1(block, in_channels=256, out_channels=512, stride=2)
        self.layer3 = MakeLayer2(block, in_channels=512, out_channels=1024, stride=2)
        self.layer4 = MakeLayer3(block, in_channels=1024, out_channels=2048, stride=2)

        self.pool = ops.ReduceMean(keep_dims=True)
        self.squeeze = ops.Squeeze(axis=(2, 3))
        self.fc = fc_with_initialize(512 * block.expansion, num_classes)

        # pipeline parallel config
        self.head.pipeline_stage = 0
        self.layer1.pipeline_stage = 0
        self.layer2.pipeline_stage = 0
        self.layer3.pipeline_stage = 1
        self.layer4.pipeline_stage = 1
        self.fc.pipeline_stage = 1

...

 Pipeline并行的结果,只会在一张卡上输出loss,因为loss是集合到最后一张卡上进行输出的,故其他卡没有loss。

内存优化

重计算

计算某些反向算子时,需要一些正向算子的计算结果,导致这些正向算子结果需要驻留在内存中,知道依赖他们的反向算子计算完,这些正向算子的计算结果占用的内存才会被复用。

重计算是一种时间换空间的方法。为了降低内存峰值,重计算技术可以不保存正向计算结果,在反向计算时,重新计算一遍正向结果,使得内存可以被重复利用。

虽然运算速度慢了,但是可以有更多可利用的内存,可以增大batch_size大小。

MindSpore中,重计算接口可以针对单个算子和Cell进行设置。当用户调用Cell的重计算接口时,整个Cell中的所有正向算子都会被设置为重计算。 

优化器并行(ZeRO)

进行数据并行训练时,模型的参数更新部份在各个卡之间存在冗余,优化器并行通过将优化器的计算量分散到数据并行维度的卡上,在大规模网络上可以有效减少内存消耗并提升网络性能。

Baseline为传统的数据并行,即每个一卡都存一份参数、梯度和优化器状态。优化器并行就是在每张卡上分发一份需要维护的optimizer status。

MindSpore的优化器并行,全局配置即可

ms.set_auto_parallel_context(parallel_mode=ms.ParallelMode.SEMI_AUTO_PARALLEL, enable_parallel_optimizer=True)

 MindSpore分布式并行模式

半自动:如数据并行配置shard,Pipeline并行需要配置Pipeline_config仍需要用户自己指定参数配置,所以这是半自动的。

(全)自动并行:框架自动配置一个并行配置策略,适合想要并行训练但是不知道如何配置策略的用户。只需配置ms.set_auto_parallel_context(parallel_mode=ms.ParallelMode.AUTO_PARALELL)即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/934553.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端成长之路:HTML(2)

HTML中有两个非常重要的标签——表格和表单,在介绍之前需要先了解表格和表单的区别:表格是用于展示数据的;表单是用于提交数据的。本文主要介绍表格。 表格标签 表格主要是用于显示、展示数据的,并非是页面布局。它可以使本来难…

如何使用WinCC DataMonitor基于Web发布浏览Excel报表文档

本文介绍使用 WinCC DataMonitor 的 "Excel Workbooks" 功能,通过 Excel 表格显示 WinCC 项目的过程值、归档变量值和报警归档消息。并可以通过 Web 发布浏览访问数据 1.WinCC DataMonitor是什么 ? DataMonitor 是 SIMATIC WinCC 工厂智能中…

Facebook广告突然无消耗?原因解析与解决方案。

在Facebook广告投放中,广告突然无消耗是很多广告主都会遇到的难题。这种情况不仅浪费时间,还可能导致营销活动停滞,影响业务发展。那么,广告无消耗的原因是什么?又该如何解决呢? 一、Facebook广告无消耗的…

安卓调试环境搭建

前言 前段时间电脑重装了系统,最近准备调试一个apk,没想到装环境的过程并不顺利,很让人火大,于是记录一下。 反编译工具下载 下载apktool.bat和apktool.jar 官网地址:https://ibotpeaches.github.io/Apktool/install…

shell基础知识4----正则表达式

一、文本搜索工具——grep grep -参数 条件 文件名 其中参数有以下: -i 忽略大小写 -c 统计匹配的行数 -v 取反,不显示匹配的行 -w 匹配单词 -E 等价于 egrep ,即启用扩展正则表达式 -n 显示行号 -rl 将指定目录内的文件打…

git branch -vv(显示本地分支与远程分支的最新状态和提交信息)(very verbose mode)

文章目录 字段说明下一步操作建议字段说明当前状态分析相关操作建议 -vv功能说明-vv 与单个 -v 的区别总结 出现如下状况,是因为我把本地的develop分支没有提交到gitlab上的develop分支。 而是把develop分支的内容提交到了gitlab上的master分支,这样是不…

树莓派4B android 系统添加led灯 Hal 层

本文内容需要用到我上一篇文章做的驱动,可以先看文章https://blog.csdn.net/ange_li/article/details/136759249 一、Hal 层的实现 1.Hal 层的实现一般放在 vendor 目录下,我们在 vendor 目录下创建如下的目录 aosp/vendor/arpi/hardware/interfaces/…

Apache DolphinScheduler 限制秒级别的定时调度

背景 Apache DolphinScheduler 定时任务配置采用的 7 位 Crontab 表达式,分别对应秒、分、时、月天、月、周天、年。 在团队日常开发工作中,工作流的定时调度一般不会细化到秒级别。但历史上出现过因配置的疏忽大意而产生故障时间,如应该配…

MTK Android12 开机向导

文章目录 需求-场景参考资料:博客资料官网参考资料:参考资料注意点 附件资料文件说明:推荐工具:配置定制的 声明叠加层 APK需求实现替换字符、删减开机向导界面、添加开机向导界面删除部分界面需求,官网说明如下更新部…

Text2SQL(NL2sql)对话数据库:设计、实现细节与挑战

Text2SQL(NL2sql)对话数据库:设计、实现细节与挑战 前言1.何为Text2SQL(NL2sql)2.Text2SQL结构与挑战3.金融领域实际业务场景4.注意事项5.总结 前言 随着信息技术的迅猛发展,人机交互的方式也在不断演进。…

长沙数字孪生工业互联网三维可视化技术,赋能新型工业化智能制造工厂

长沙正积极拥抱数字化转型的浪潮,特别是在工业互联网和智能制造领域,长沙数字孪生技术的广泛应用,为新型工业化智能制造工厂的建设与发展注入了强劲动力。 在长沙智能制造工厂内,三维可视化技术被广泛应用于产线设计仿真优化和产…

FPGA工作原理、架构及底层资源

FPGA工作原理、架构及底层资源 文章目录 FPGA工作原理、架构及底层资源前言一、FPGA工作原理二、FPGA架构及底层资源 1.FPGA架构2.FPGA底层资源 2.1可编程输入/输出单元简称(IOB)2.2可配置逻辑块2.3丰富的布线资源2.4数字时钟管理模块(DCM)2.5嵌入式块 …

计算机网络期末常见问答题总结

1、试说明为什么在运输建立时使用三报文握手,如果不采用三报文握手会出现什么情况? TCP三次握手的目的是确保客户端和服务器都能够接收对方的连接请求,并建立起可靠的连接。如果只进行两次握手,可能会导致以下情况的发生: - 如果客户端发送的SYN包在网…

Redis篇-4--原理篇3--Redis发布/订阅(Pub/Sub)

1、概述 Redis 发布/订阅(Publish/Subscribe,简称 Pub/Sub)是一种消息传递模式,允许客户端订阅一个或多个通道(channel),并接收其他客户端发布到这些通道的消息。 2、Redis 发布/订阅的主要概…

第六届地博会世界酒中国菜助力广州龙美地标美食公司推动地标发展

第六届知交会暨地博会:世界酒中国菜助力广州龙美地标美食公司推动地标产品创新发展 2024年12月9日至11日,第六届粤港澳大湾区知识产权交易博览会暨国际地理标志产品交易博览会在中新广州知识城盛大启幕。本届盛会吸引了全球众多知识产权领域的专业人士和…

【CSS in Depth 2 精译_074】第 12 章 CSS 排版与间距概述 + 12.1 间距设置(下):行内元素的间距设置

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 12.1.1 使用 em 还是 px12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 ✔️ 12.2 Web 字体12.3 谷歌字体 文章目…

基于FPGA的智能电子密码指纹锁(开源全免)

基于FPGA的智能电子密码指纹锁 一、功能描述硬件资源需求 二、整体框架知识准备AS608指纹模块4*4数字键盘模块 三、Verilog代码实现以及仿真验证1.AS608_data模块2.check_hand模块3.four_four_key模块4.check_mima模块5.change_mima模块6.seg_ctrl模块7.uart_top模块8.key_debo…

汽车网络安全 -- IDPS如何帮助OEM保证车辆全生命周期的信息安全

目录 1.强标的另一层解读 2.什么是IDPS 2.1 IDPS技术要点 2.2 车辆IDPS系统示例 3.车辆纵深防御架构 4.小结 1.强标的另一层解读 在最近发布的国家汽车安全强标《GB 44495》,在7.2节明确提出了12条关于通信安全的要求,分别涉及到车辆与车辆制造商云平台通信、车辆与车辆…

【C++】快速排序详解与优化

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 💯前言💯快速排序的核心思想1. 算法原理2. 算法复杂度分析时间复杂度空间复杂度 💯快速排序的代码实现与解析代码实现代码解析1. 递归终止条件2. 动态分配子数组3. 分区…

Redis从入门到进阶(总结)

以下内容均以CentOS7为背景。 一、Redis安装及启动 mysql(读:2000/s;写:600/s) redis(读:10w/s;写:8w/s)通过官方给出的数据单机并发可以达到10w/s&#xf…