当前位置: 首页 > article >正文

深度学习:基于MindSpore NLP的数据并行训练

什么是数据并行?

数据并行(Data Parallelism, DP)的核心思想是将大规模的数据集分割成若干个较小的数据子集,并将这些子集分配到不同的 NPU 计算节点上,每个节点运行相同的模型副本,但处理不同的数据子集。

1. 将数据区分为不同的mini-batch

将数据集切分为若干子集,每个mini-batch又不同的设备独立处理。如你有4个GPU,可以把数据集分为4分,每个GPU处理一个字数据集。

2. 模型参数同步

可以通过某一进程初始化全部模型参数后,向其他进程广播模型参数,实现同步。

3. 前向运算

每个设备独立计算前向运算。

4. 反向运算

每个设备计算损失的梯度。

5. 梯度聚合

当所有设备计算完各自的梯度后,对所有设备的梯度取平均,每个设备的模型参数根据平均梯度进行更新。

6. 参数更新

数据并行的参数更新是在数据切分、模型参数同步后进行的。更新前,每个进程的参数相同;更新时,平均梯度相同;故更新后,每个进程的参数也相同。

数据并行的基本操作

Reduce 归约

归约是函数式编程的概念。数据归约包括通过函数将一组数字归约为较小的一组数字。

如sum([1, 2, 3, 4, 5])=15, multiply([1, 2, 3, 4, 5])=120。

AllReduce

等效于执行Reduce操作后,将结果广播分配给所有进程。

MindSpore AllReduce

import numpy as np
from mindspore.communication import init
from mindspore.communication.comm_func import all_reduce
from mindspore import Tensor

init()
input_tensor = Tensor(np.ones([2, 8]).astype(np.float32))
output = all_reduce(input_tensor)

数据并行的主要计算思想

Parameter-Server

主要思想:所有node被分为server node和worker node

Server node:负责参数的存储和全局的聚合操作

Worker node:负责计算 

Parameter-Server的问题:

  • 假设有N=5张卡,GPU0作为Server,其余作为Worker
  • 将大小为K的数据拆分为N-1份,分给每个Worker GPU
  • 每个GPU计算得到local gradients
  • N-1块GPU将计算所得的local gradients发送给GPU 0
  • GPU 0对所有local gradients进行all reduce操作得到全局梯度,参数更新
  • 将该新模型的参数返回给每张GPU

假设单个Worker到Server的通信开销为C,那么将local gradients送到GPU 0上的通信成本为C * (N - 1)。收到GPU 0通信带宽的影响,通信成本随着设备数的增加而线性增长。

Pytorch DataParallel 

Pytorch DP在Parameter-Server的基础上,把GPU 0即当作Server也当作Worker。

1. 切分数据,但不切分Label

每个GPU进行正向计算之后,将正向计算结果聚合回GPU 0计算Loss,GPU 0计算完Loss的gradient之后,将梯度分发回其他worker GPU。随后各个GPU计算整个模型的grad,再将grad聚合回GPU 0,进行AllReduce。

2. 切分数据,同时切分Label 

每张卡自己计算Loss即可,减少一次聚合操作。

Pytorch DataParallel 问题:

1. 为摆脱Parameter-Server模式,性能差。

2. 需要额外的GPU进行梯度聚合/ GPU 0需要额外的显存。GPU 0限制了其他GPU的上限。

Ring AllReduce

每张卡单向通讯,通讯开销一定。

每张卡占用的显存相同。

第一步:Scatter-Reduce 

假设每张卡上各自计算好了梯度。

每张GPU依次传值: 

重复直至: 

第二步:All-Gather

将每一个累计值a / b / c逐个发送至个张卡

直至每张卡都有每层的梯度累计值。

两步分别做了四次通讯,便可以实现并行计算。

Ring AllReduce计算开销

  • N-1次Scatter-Reduce
  • N-1次All-Gather
  • 每个GPUGPU一次通讯量为:K/N,K为总数据大小
  • 每个GPU通信次数为:2(N-1)

总通信量为:2(N-1)*(K/N)

当N足够多时,通信量为一个常数2K。

Gradient Bucketing

集合通信在大张量上更有效。因此,可以在短时间内等待并将多个梯度存储到一个数据桶(Bucket),然后进行AllReduce操作。而不是对每个梯度立刻启动AllReduce操作。

MindSpore数据并行 

def forward_fn(data, target):
    logits = net(data)
    loss = loss_fn(logits, target)
    return loss, logits

grad_fn = ms.value_and_grad(forward_fn, None, net_trainable_param(), has_aux=True)
# 初始化reducer
grad_gather = nn.DistributedGradReducer(optimizer.parameters)

for epoch in range(10):
    i = 0
    for image, label in data_set:
        (loss_value, _), grads = grad_fn(image, label)
        # 进行通讯
        grads = grad_reducer(grads)
        optimizer(grads)
        # ...

MindNLP数据并行

def update_gradient_by_distributed_type(self, model: nn.Module) -> None:
    '''update gradient by distributed_type'''
    if accelerate_distributed_type == DistributedType.NO:
        return
    if accelerate_distributed_type == DistrivutedType.MULTI_NPU:
        from mindspore.communication import get_group_size
        from mindspore.communication.comm_func iport all_reduce
        rank_size = get_group_size()
        for parameter in model.parameters():
            # 进行all_reduce
            new_grads_mean = all_reduce(parameter.grad) / rank_size
            parameter.grad = new_grads_mean

数据并行的局限性

要求单卡可以放下模型

多卡训练时内存冗余,相同模型参数复制了多份。

MindSopre中的数据并行

1. 在启智社区创建云脑任务或华为云创建notebook

环境选择:mindspore==2.3.0, cann==8.0,昇腾910 * 2

 2. 更新MindSpore框架版本

pip install --upgrade mindspore

同时可以查看NPU信息:

npu--smi info

3. 配置项目环境

克隆mindnlp项目

git clone https://github.com/mindspore-lab/mindnlp.git

下载mindnlp

cd mindnlp
bash scripts/build_and_reinstall.sh

下载完成后,卸载mindformers、soundfile

pip uninstall mindformers

4. 运行训练脚本

cd mindnlp/llm/parallel/bert_imdb_finetune_dp
msrun --worker_num=2 --local_worker_num=2 --master_port=8118 bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py 

发现两个NPU都被占用 

日志文件开始记录模型训练进度 

成功实现数据并行! 

基于MindSpore微调Roberta+数据并行

数据集:imdb影评数据集

微调代码:roberta.py

#!/usr/bin/env python
# coding: utf-8
"""
unset MULTI_NPU && python bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py
bash bert_imdb_finetune_npu_mindnlp_trainer.sh
"""
import mindspore.dataset as ds
from mindnlp.dataset import load_dataset

# loading dataset
imdb_ds = load_dataset('imdb', split=['train', 'test'])
imdb_train = imdb_ds['train']
imdb_test = imdb_ds['test']
 
imdb_train.get_dataset_size()
 
import numpy as np
 
def process_dataset(dataset, tokenizer, max_seq_len=512, batch_size=4, shuffle=False):
    is_ascend = mindspore.get_context('device_target') == 'Ascend'
    def tokenize(text):
        if is_ascend:
            tokenized = tokenizer(text, padding='max_length', truncation=True, max_length=max_seq_len)
        else:
            tokenized = tokenizer(text, truncation=True, max_length=max_seq_len)
        return tokenized['input_ids'], tokenized['attention_mask']
 
    if shuffle:
        dataset = dataset.shuffle(batch_size)
 
    # map dataset
    dataset = dataset.map(operations=[tokenize], input_columns="text", output_columns=['input_ids', 'attention_mask'])
    dataset = dataset.map(operations=transforms.TypeCast(mindspore.int32), input_columns="label", output_columns="labels")
    # batch dataset
    if is_ascend:
        dataset = dataset.batch(batch_size)
    else:
        dataset = dataset.padded_batch(batch_size, pad_info={'input_ids': (None, tokenizer.pad_token_id),
                                                             'attention_mask': (None, 0)})
 
    return dataset

from mindnlp.transformers import AutoTokenizer
import mindspore
import mindspore.dataset.transforms as transforms
# tokenizer
tokenizer = AutoTokenizer.from_pretrained('roberta-base')

dataset_train = process_dataset(yelp_ds_train, tokenizer, shuffle=True)
from mindnlp.transformers import AutoModelForSequenceClassification

# set bert config and define parameters for training
model = AutoModelForSequenceClassification.from_pretrained('AI-ModelScope/roberta-base', num_labels=2, mirror='modelscope')

from mindnlp.engine import TrainingArguments

training_args = TrainingArguments(
    output_dir="./",
    save_strategy="epoch",
    logging_strategy="epoch",
    num_train_epochs=3,
    learning_rate=2e-5
)

training_args = training_args.set_optimizer(name="adamw", beta1=0.8)

from mindnlp.engine import Trainer

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=dataset_train
)

print('start training')
trainer.train()

运行命令:

msrun --worker_num=2 --local_worker_num=2 --master_port=8118 roberta.py


http://www.kler.cn/a/457518.html

相关文章:

  • docker compose部署kafka集群
  • 混合合并两个pdf文件
  • 【Kafka 消息队列深度解析与应用】
  • 小程序租赁系统开发的优势与应用探索
  • uniapp:微信小程序文本长按无法出现复制菜单
  • chatwoot 开源客服系统搭建
  • 玩转树莓派Pico(20): 迷你气象站6——软件整合改进
  • Unity3D仿星露谷物语开发9之创建农场Scene
  • Facebook广告优化新知:如何使用即时体验
  • C# dynamic 类型详解
  • 鸿蒙next RCP网络请求工具类进阶版来了
  • 【机器学习】Transformer
  • 代码随想录算法训练营第六十天 | 图 | A星算法
  • Bash语言的并发编程
  • 算法排序算法
  • 【每日学点鸿蒙知识】worker线程数量、判断用户是否进行权限决定、图片上传类型错误、request锁释放时机、H5问题
  • Zynq PS端外设之中断控制器
  • FFmpeg来从HTTP拉取流并实时推流到RTMP服务器
  • 自学记录鸿蒙API 13:实现智能文本识别Core Vision Text Recognition
  • Django中使用 `formfield_for_choice_field` 和 `get_form` 方法自定义管理后台表单
  • 26、使用StreamPark管理Flink作业中,关于flink on k8s部分的提交处理
  • driftingblues6靶机
  • Oracle数据库高级应用与优化策略
  • 2-194基于matlab的四足机器人行走程序设计
  • [ffmpeg]编译 libx264
  • FFmpeg:详细安装教程与环境配置指南