GPU集群上分布式训练大模型

总结一下如何在超算系统上进行预训练大模型的分布式训练 / 微调,文中代码已上传至 github

实验环境

集群1:国家广州超算 星逸A800智能AI集群
GPU:8 * Nvdia Tesla-A800 80G显存
CPU:2 * 28核 Intel Xeon Gold 6348
内存:1024GB

集群2:并行科技 中国国家网格 N12 区(cngrid12)
GPU:4 * Nvdia Tesla-V100 16G显存
CPU:20 核 Intel® Xeon® CPU E5-2640 v4
内存:128GB

在超算分布式环境上和本地训练有几点不同:

  1. 超算环境无法科学上网,需要手动下载并上传:数据tokenizer模型模型参数,并在代码中作相应修改。
  2. 通过 slurm 进行作业管理,编写并提交 sbatch 脚本来运行作业。
  3. 每个集群的环境各不相同,移植时需要注意配置环境和预加载相关的库。
  4. 训练超大模型时,单个GPU显存有限,仅使用 torch.nn.parallel 数据并行常常无法加载完整模型无法完成训练,或只能小批次训练。因此需要用到分布式训练框架,常见的分布式训练框架有Horovod,Megatron-LM,DeepSpeed等。

1 举例:bert-large

1.1 本地单卡训练bert-large(假设GPU显存足够大)

Step1 编写训练代码 run_bert.py

import torch
from transformers import BertTokenizer, BertForMaskedLM, DataCollatorForLanguageModeling
from datasets import load_dataset
from torch.utils.data import DataLoader
from tqdm import tqdm# 加载预训练的tokenizer和模型
tokenizer = BertTokenizer.from_pretrained("bert-large-uncased")
model = BertForMaskedLM.from_pretrained("bert-large-uncased")# 加载WikiText数据集
dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="train")# 数据预处理
def tokenize_function(examples):return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=["text"])# 设置数据加载器
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)
train_loader = DataLoader(tokenized_dataset, batch_size=4, shuffle=True, collate_fn=data_collator)  # 根据显存调整batch_size# 设置设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)# 设置优化器
optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)# 设置训练参数
num_epochs = 3
gradient_accumulation_steps = 8  # 梯度累积步数,根据显存调整
model.train()# 手动实现训练循环
for epoch in range(num_epochs):print(f"Epoch {epoch + 1}/{num_epochs}")epoch_loss = 0for step, batch in enumerate(tqdm(train_loader)):# 将数据移到GPUbatch = {k: v.to(device) for k, v in batch.items()}# 前向传播outputs = model(**batch)loss = outputs.lossloss = loss / gradient_accumulation_steps  # 梯度累积# 反向传播loss.backward()# 更新参数并清空梯度if (step + 1) % gradient_accumulation_steps == 0:optimizer.step()optimizer.zero_grad()# 记录损失epoch_loss += loss.item()avg_loss = epoch_loss / len(train_loader)print(f"Epoch {epoch + 1} finished with average loss: {avg_loss:.4f}")print("Training complete.")

Step2 运行:

python run_bert.py

1.2 迁移到分布式训练(微调大模型)

按如下步骤转换为分布式训练代码,并移植到超算平台上完成训练(单节点多卡):

Step1 下载数据、tokenizer、模型
从huggingface官网或镜像网站(国内)下载对应文件,模型和tokenizer搜索bert-large-uncased,数据集搜索wikitext:

模型:config.json,pytorch_model.bin
数据:train-00000-of-00001.parquet
tokenizer:tokenizer.json,vocab.txt,tokenizer_config.json(可选)

Step2 修改训练代码 run_bert.py

  • 分布式训练框架我用微软提供的deepspeed框架进行训练。deepspeed支持3D并行训练,同时集成了一些优化,如ZeRO、CPU-offload、混合精度训练等,能够提供比原生pytorch更加高效的训练。
  • 修改内容:
  1. tokenizer、model、dataset 分别修改为从本地加载
  2. 添加 import deepspeed 和 import torch.distributed as dist
  3. 添加 deepspeed.init_distributed() 初始化分布式环境
  4. 添加 deepspeed.initialize() 将模型转换为deepspeed模型
  5. 获取 world_size 和 rank ,并将 .to(device) 替换为 .to(rank)
  6. 删除 device, optimizer, gradient_accumulation_steps 配置,转移到 ds_config.json 中
  7. 将所有的 model 替换为 model_engine ,即deepspeed模型
  8. 训练循环中只保留 model_engine(input_ids), model_engine.backward(), model_engine.step() 三个函数
  9. 添加 time.time() 函数用于计时
  10. 修改数据集加载和预处理逻辑,增加sampler,删除shuffle选项,使其符合分布式训练
  11. 对模型和数据添加 .contiguous() 以保证 tensor 的连续性
import torch
from transformers import BertTokenizer, BertForMaskedLM, DataCollatorForLanguageModeling
from datasets import load_dataset
from torch.utils.data import DataLoader, DistributedSampler
from tqdm import tqdm
import deepspeed
import torch.distributed as dist
import time# 初始化分布式环境
deepspeed.init_distributed()
world_size = dist.get_world_size()
rank = dist.get_rank()# 加载预训练的tokenizer和模型
tokenizer = BertTokenizer.from_pretrained("./tokenizer/")
model = BertForMaskedLM.from_pretrained("./model")# 加载WikiText数据集
dataset = load_dataset("parquet", data_files="./data/train-00000-of-00001.parquet")# 数据预处理
def tokenize_function(examples):return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512)tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=["text"])
train_dataset = tokenized_dataset["train"]# 使用分布式采样器
sampler = DistributedSampler(train_dataset, num_replicas=world_size, rank=rank)# 设置数据加载器
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)train_loader = DataLoader(train_dataset, batch_size=4, collate_fn=data_collator, sampler=sampler)  # 设置训练参数
epochs = 3# 确保模型参数的连续性
for param in model.parameters():param.data = param.data.contiguous()# 初始化deepspeed模型,将model转换成model_engine
model_engine, optimizer, _, _ = deepspeed.initialize(args=None,model=model,model_parameters=model.parameters(),config='./ds_config.json'
)model_engine.train()start_time = time.time()if rank == 0: print("Training start...")# 手动实现训练循环
for epoch in range(epochs):epoch_loss = 0for step, batch in enumerate(tqdm(train_loader, desc=f"Epoch {epoch+1}/{epochs}", disable=(rank != 0), mininterval=20)):  # 设置只有0号进程打印进度条且打印间隔为20s# 将数据移到GPUbatch = {k: v.to(rank) for k, v in batch.items()}# 前向传播outputs = model_engine(**batch)loss = outputs.loss# 反向传播model_engine.backward(loss)# 更新参数model_engine.step()# 记录损失epoch_loss += loss.item()end_time = time.time()elapsed_time = end_time - start_timeprint(f"\nRank {rank}, Epoch {epoch+1}/{epochs}, Batch {step+1}/{len(train_loader)}, Loss: {epoch_loss/len(train_loader)}, total_time_used: {elapsed_time / 60:.2f} mins")if rank == 0: print("Training complete.")
  • 上面代码是加载了预训练模型权重文件 pytorch_model.bin 然后对模型进行微调,若需要从头训练,只需要将 model = BertForMaskedLM.from_pretrained("./model") 改为:
from transformers import BertConfigconfig = BertConfig.from_pretrained('./model/config.json')
model = BertForMaskedLM(config)

Step3 编写运行脚本 sbatch.sh

#!/bin/bash#SBATCH --nodes=1                   # 节点数
#SBATCH --ntasks=4                  # 任务数
#SBATCH --partition=gpu             # 分区名称(根据集群修改)
#SBATCH --gres=gpu:4                # 设置使用的GPU数module load nvidia/cuda/12.2
module load mpich/3.4.1-gcc9.3      # 加载gcc-5版本以上deepspeed   --num_nodes=1          \--num_gpus=4           \--launcher slurm       \run_bert.py 

Step4 创建deepspeed的配置文件 ds_config.json

{"train_batch_size": 4,                // batch_size,必须等于 train_micro_batch_size_per_gpu * gradient_accumulation_steps * GPU数,且和训练代码中设置相同"train_micro_batch_size_per_gpu": 1,  // 每个GPU上micro_batch的数量"gradient_accumulation_steps": 1,     // 梯度累积多少个batch同步一次// 设置使用ZeRO-3优化"zero_allow_untested_optimizer": true,"zero_optimization":{"stage": 3},// 配置优化器"optimizer": {"type": "Adam","params": {"lr": 1e-4,"betas": [0.9, 0.999],"eps": 1e-8}} 
}

Step5 上传到服务器

  • 将所有文件打包上传到服务器,其中模型文件 pytorch_model.bin 比较大,可能需要单独上传。上传后解压,文件结构如下:
bert-large
│   run_bert.py
│   sbatch.sh   
│   ds_config.json  
│
└───data
│   │   train-00000-of-00001.parquet
│   
└───model│   config.json│   pytorch_model.bin
│
└───tokenizer
│   │   tokenizer.json
│   │   vocab.txt
│   

Step6 配置服务器运行环境

# 创建虚拟环境
$ conda create -n bert-large python==3.10
$ conda activate bert-large# 安装必要的库
$ pip install -r requirement.txt -i https://pypi.tuna.tsinghua.edu.cn/simple# 使用 conda 安装mpi4py,因为这个库需要的依赖太多了,pip很容易报错
$ conda install mpi4py

其中,requirement.txt

torch==2.4.1
transformers==4.46.0
deepspeed==0.15.2
datasets
tensorboard
fire==0.4.0
pytz==2021.1
loguru==0.5.3
sh==1.14.2
pytest==6.2.5
tqdm==4.62.3

Step7 命令行运行:

$ cd bert-large
$ sbatch sbatch.sh

运行结果(部分)
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

读取excel并且显示进度条

读取excel并且显示进度条 通过C#实现DataGridView加载EXCEL文件,但加载时不能阻塞UI刷新线程,且向UI显示加载进度条。 #region 左上角导入 private async void ToolStripMenuItem_ClickAsync(object sender, EventArgs e) { …

Java | Leetcode Java题解之第552题学生出勤记录II

题目: 题解: class Solution {static final int MOD 1000000007;public int checkRecord(int n) {long[][] mat {{1, 1, 0, 1, 0, 0},{1, 0, 1, 1, 0, 0},{1, 0, 0, 1, 0, 0},{0, 0, 0, 1, 1, 0},{0, 0, 0, 1, 0, 1},{0, 0, 0, 1, 0, 0}};long[][] re…

Oracle OCP认证考试考点详解082系列16

题记: 本系列主要讲解Oracle OCP认证考试考点(题目),适用于19C/21C,跟着学OCP考试必过。 76. 第76题: 题目 解析及答案: 以下哪三项活动会被记录在数据库的警报日志中? A. 块损坏错误 数据库…

103、Python并发编程:使用信号量Semaphore实现资源有限的并发场景

引言 在前面几篇文章的基础上,应对并发编程中现成同步的需求场景: 我们可以使用锁,作为多线程同步的几个核心基础,实现对临界资源的保护,确保满足基本的互斥访问逻辑。 使用条件变量Condition,实现有固定…

蛋奶烙饼:美味与温暖的邂逅

食家巷蛋奶烙饼,那金黄的色泽、浓郁的奶香和蛋香,光是看着就让人垂涎欲滴。它的制作过程并不复杂,却充满了生活的烟火气。将面粉、鸡蛋、牛奶等简单的食材混合在一起,搅拌成细腻的面糊。在平底锅中倒入少许油,舀一勺面…

Linux内核中IRQ Domain的结构、操作及映射机制详解

往期内容 本专栏往期内容,interrtupr子系统: 深入解析Linux内核中断管理:从IRQ描述符到irq domain的设计与实现 pinctrl和gpio子系统专栏: 专栏地址:pinctrl和gpio子系统 编写虚拟的GPIO控制器的驱动程序:…

cocos creator 3.8.3物理组件分组的坑

坑,坑的不行的大坑 group用的二进制的左移获取十进制的数值 目前是这样判断的,也不知道对不对,什么get、set Group没找到

如何解决“在ANACONDA prompt可以使用‘conda activate‘,但是在pycharm终端没办法使用该指令“”

一、设置好环境变量 此电脑(右键)-属性-高级系统设置-环境变量-在系统变量那一筐的PATH双击添加 二、完成后再测试conda activate 笔者测试完后,conda是可以用了,但是conda activate用不了。 显示该错误CommandNotFoundError:…

三菱MR-J4-B系列伺服参数一览

要点 与伺服系统控制器连接后,同服系统控制器的伺服参数的值即被写入各参数中。根据伺服系统控制器的机种和伺服放大器软件版本及MRConfigurator2的软件版本,存在无法设定的参数或范围。详细内容请参照伺服系统控制器的用户手册。请使用MR Configurator2…

Pattern program MPAT 详解

本文为VIP文章,主要介绍Pattern中元素与格式、常用指令、地址&数据产生指令等。 目录 一、pattern概述 二:Pattern构成元素 1、pattern构成元素:MPAT、END 2、pattern构成元素:pattern file name 3、pattern构成元素:SDEF 4、Pattern构成元素:REGISETR 5、Pa…

Qt编译lua库并调用

参考博客: 编译lua库 参考下面文章编译lua库文件 QT5.9学习笔记之QT编译lua库_qtluaintf.h-CSDN博客 https://blog.csdn.net/qq_23345187/article/details/112710677 Qt代码引用lua库文件 打开pro项目文件,右键空白处,点击添加库&#xff…

数据结构:直接插入排序

直接插入排序是数据结构中较为简单的插入排序法,基本思想是:把待排序的记录按照关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插完为止,得到一个新的有序序列。 实际上我们玩扑克时,就用了插入排…

Pr 视频过渡:溶解

效果面板/视频过渡/溶解 Video Transitions/Dissolve Adobe Premiere Pro 的视频过渡效果中,溶解 Dissolve效果组提供了多种平滑过渡方式,适用于不同的场景需求,从基础的淡入淡出到复杂的叠加溶解,帮助用户实现更具层次感的视觉过…

使用 Elasticsearch 构建食谱搜索(一)

作者:来自 Elastic Andre Luiz 了解如何使用 Elasticsearch 构建基于语义搜索的食谱搜索。 简介 许多电子商务网站都希望增强其食谱搜索体验。正确使用语义搜索可以让客户根据更自然的查询(例如 “something for Valentines Day - 情人节的礼物” 或 “…

prompt资料收集

1. LANGgpt模板 # Role: 知识探索专家 ## Profile: - - 即刻App即刻App,享受探索、表达和创造https://m.okjike.com/originalPosts/649801f1ba47fe581a0da471?seyJ1IjoiNjQyM2IwMDE4NDg5Njk1NGJjYzhkNWU1IiwiZCI6MX0%3D2. 好的prompt的标准 主观的说:…

大数据学习10之Hive高级

1.Hive高级 将大的文件按照某一列属性进行GROUP BY 就是分区,只是默认开窗存储; 分区是按行,如一百行数据,按十位上的数字分区,则有十个分区,每个分区里有十行; 分桶是根据某个字段哈希对桶数取…

前端Nginx的安装与应用

目录 一、前端跨域方式 1.1、CORS(跨域资源共享) 1.2、JSONP(已过时) 1.3、WebSocket 1.4、PostMessage 1.5、Nginx 二、安装 三、应用 四、命令 4.1、基本操作命令 4.2、nginx.conf介绍 4.2.1、location模块 4.2.2、反向代理配置 4.2.3、负载均衡模块 4.2.4、通…

Mit6.S081-实验环境搭建

Mit6.S081-实验环境搭建 注:大家每次做一些操作的时候觉得不太保险就先把虚拟机克隆一份 前言 qemu(quick emulator):这是一个模拟硬件环境的软件,利用它可以运行我们编译好的操作系统。 准备一个Linux系统&#xf…

AWS账号安全:如何防范与应对账号被盗风险

在云计算时代,Amazon Web Services(AWS)作为全球领先的云服务提供商,为企业和个人提供了强大的计算资源和灵活的服务。然而,随着云计算的普及,AWS账号被盗的风险也随之增加。我们九河云有多年用云经验&…

IPTABLE:Linux下的网络防火墙

IPTABLE:Linux下的网络防火墙 引言 在Linux系统中,IPtable是一种强大的网络防火墙工具,广泛应用于各种网络环境中。它不仅可以实现基本的包过滤功能,还能进行网络地址转换(NAT)、数据包记录、流量统计等高…