FairScale 库测试实验(一)-- 大模型训练基础之模型并行

DDP的分布式训练方法采用数据并行方式,相当于通过增大数据的batch来加快训练。但对于大模型(LLM)来说,DDP已经不适用了。因为LLMs的模型本身太大,一块GPU都放不下怎么可能去复制从而实现数据并行呢。所以LLM的训练采用模型并行的方式来训练。

FairScale 是一个用于高性能和大规模训练的 PyTorch 扩展库。该库扩展了基本的 PyTorch 功能,同时添加了新的 SOTA 扩展技术。FairScale 以可组合模块和易于使用的 API 的形式提供最新的分布式训练技术。这些 API 是研究人员工具箱的基本组成部分,因为他们试图用有限的资源扩展模型。(来源官网)

本次熟悉一下其常用的设置。

目录

一、预先准备

二、使用 PIPELINE PARALLEL 进行模型分片

1、官网的教程 

2、实际应用举例

3、模型分片后,数据前向传播流程分析

总结


一、预先准备

随便准备一个模型作为例子。以下面模型为例(可以运行),简单的分类任务。

import torch
import torch.nn as nn

import random
import torchvision.datasets as data
import torchvision.transforms as transforms
import torch.optim as optim


class Classifier(nn.Module):
    def __init__(self):
        super(Classifier, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(in_channels=3,out_channels=32, kernel_size=4,stride=2, padding=1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1),
            nn.Conv2d(in_channels=32,out_channels=64, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1),
            nn.Conv2d(64, 128, 2, 2,1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1)
        )
        #
        self.classifier = nn.Sequential(

            nn.Linear(3*3*128,4096),
            nn.ReLU(),

            nn.Dropout(p=0.5),
            nn.Linear(4096,2048),
            nn.ReLU(),

            nn.Linear(2048,1024),
            nn.ReLU()
        )
        self.last_layer_input = nn.Sequential(nn.Linear(1024,10),
            nn.Softmax())

    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        x = self.last_layer_input(x)

        return x

if __name__ == '__main__':

    batchSize = 50
    nepoch = 45

    print("Random Seed: 88")
    random.seed(88)
    torch.manual_seed(88)
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    dataset = data.CIFAR10(root='/root/data/zjx/Datasets/cifar10',  # 这个路径自己改
                         train=True,
                         transform=transforms.Compose([transforms.ToTensor()]),
                         download=True
                         )

    dataloader = torch.utils.data.DataLoader(dataset,
                                             batch_size=batchSize,
                                             shuffle=True)

    Model = Classifier().to(device)
    Cross_entropy = nn.BCELoss().to(device)
    Optimizer = optim.Adam(Model.parameters(), lr=0.00001)

    for epoch in range(nepoch):
        for i, (data, label) in enumerate(dataloader, 0):
            data = data.to(device)
            label_onehot = torch.zeros((data.shape[0], 10)).to(device)
            label_onehot[torch.arange(data.shape[0]), label] = 1
            output = Model(data)
            loss = Cross_entropy(output, label_onehot)
            print('{}/{}: Loss is {}'.format(i, epoch, loss.data))
            Model.zero_grad()
            loss.backward()
            Optimizer.step()

二、使用 PIPELINE PARALLEL 进行模型分片

1、官网的教程 

官网的示例看这里,主要关键点包括切片的语法设置,以及设备对齐。

模型切片的要求格式

import fairscale
import torch
import torch.nn as nn

model = nn.Sequential(
            torch.nn.Linear(10, 10),
            torch.nn.ReLU(),
            torch.nn.Linear(10, 5)
        )

model = fairscale.nn.Pipe(model, balance=[2, 1])

可以看到要求必须 torch.nn.Sequential 格式。上述前两层放到cuda:0, 后一层放到cuda:1上。但是这里有个问题。如果我们自定义的模型不全是nn.Sequential的格式,那么它还能这样实现吗?

以 一中预准备的模型为例,它并不全是nn.Sequential的格式,而是分成了三个部分,前向传播过程中有一步拉直操作。(当然,一中例子也可以转换成全是nn.Sequntial的格式,拉直可以用nn.Flatten()来实现)这样的话使用这个设置还可以吗?来试一下


Model = Classifier().to(device)
# 在上面语句的下面添加
Model = fairscale.nn.Pipe(Model, balance=[6, 6, 6])

# 运行报错
TypeError: module must be nn.Sequential to be partitioned

显然,这样不行。报出错误:必须是nn.Sequential类型的。这样行不通,必须另想办法。

2、实际应用举例

当整个模型不是连续的nn.Sequential类型时,而是分成几部分单独定义时,我们可以把每部分分别分片放到不同的GPU上。这个过程可以在模型定义的时候在其内部实现。

以一中模型为例,将 模型中的 self.features 分片,放到三块GPU上。


self.features = nn.Sequential(
            nn.Conv2d(in_channels=3,out_channels=32, kernel_size=4,stride=2, padding=1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1),
            nn.Conv2d(in_channels=32,out_channels=64, kernel_size=3, stride=2, padding=1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1),
            nn.Conv2d(64, 128, 2, 2,1),
            nn.ReLU(),
            nn.MaxPool2d((2,2),1)
        )
# 在上面语句的下面添加
self.features = fairscale.nn.Pipe(self.features, balance=[3, 3, 3])

注意 ,我这里是根据我实际情况划分的,我采用的是三块GPU,一共9层,三等分了,你们按实际自行改动。下面的实例也是都用的三块GPU的基础上进行的

这样简单的设置,来执行一下看看行不行得通。

# 运行报错

RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:1 and cuda:0! (when checking argument for argument weight in method wrapper__cudnn_convolution)

报错了。因为官网中还有另一个关键的设备对其没有设置。所以,必须在整个过程中进行一下设备对齐,才能顺利运行。

首先,在定义模型时先不把模型放到 cuda上, 而是在模型内部去实现这一步。

# 将
Model = Classifier().to(device)

# 改为
Model = Classifier()

然后


self.features = fairscale.nn.Pipe(self.features, balance=[3, 3, 3])
# 在上面语句的下面添加
self.device = self.features.devices[0]

self.classifier = nn.Sequential(

            nn.Linear(3*3*128,4096),
            nn.ReLU(),

            nn.Dropout(p=0.5),
            nn.Linear(4096,2048),
            nn.ReLU(),

            nn.Linear(2048,1024),
            nn.ReLU()
        )
# 在上面的语句下面添加
self.classifier.to(self.device)
self.last_layer_input = nn.Sequential(nn.Linear(1024, 10),
            nn.Softmax())
# 在上面的语句下面添加
self.last_layer_input.to(self.device)
x = torch.flatten(x, 1)
# 在上面语句的下面添加
x = x.to(self.device)

这样就可以运行了。我们来看一下不同GPU显存占用的变化

模型未分片,只用一个GPU时

(base) root@3eaab89e2baa:~/data# nvidia-smi
Sat Mar  9 04:15:26 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.47.03    Driver Version: 510.47.03    CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:04:00.0 Off |                  N/A |
| 22%   33C    P2    71W / 250W |   1522MiB / 11264MiB |     31%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  NVIDIA GeForce ...  On   | 00000000:05:00.0 Off |                  N/A |
| 22%   27C    P8     3W / 250W |      3MiB / 11264MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  NVIDIA GeForce ...  On   | 00000000:09:00.0 Off |                  N/A |
| 22%   32C    P8     2W / 250W |      3MiB / 11264MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     28350      C                                    1519MiB |
+-----------------------------------------------------------------------------+

模型分片时的情况

(base) root@3eaab89e2baa:~/data# nvidia-smi
Sat Mar  9 04:17:00 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.47.03    Driver Version: 510.47.03    CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:04:00.0 Off |                  N/A |
| 22%   34C    P2    66W / 250W |   1546MiB / 11264MiB |     22%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  NVIDIA GeForce ...  On   | 00000000:05:00.0 Off |                  N/A |
| 22%   30C    P2    52W / 250W |   1210MiB / 11264MiB |      3%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  NVIDIA GeForce ...  On   | 00000000:09:00.0 Off |                  N/A |
| 22%   35C    P2    47W / 250W |   1190MiB / 11264MiB |      2%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     29306      C                                    1543MiB |
|    1   N/A  N/A     29306      C                                    1207MiB |
|    2   N/A  N/A     29306      C                                    1187MiB |
+-----------------------------------------------------------------------------+

可以看到其中的区别。未分片时只有一个GPU内存被占用,分片时三个GPU显存被占用。至于占用的大小并不是1+1=2的关系,因为其中不仅仅只是模型的参数被划分,在训练过程中还有其它参数的内存占用,比如中间过程生成的特征参数,计算保留的梯度参数等等。这里自行体会。

3、模型分片后,数据前向传播流程分析

debug,发现

模型被分片后,其被分到了三个设备上,具体的体现就是图中的devices包含三个cuda:1,2,3。 

在对其设备时,数据被放到了 cuda:0 上。所以,在送入 self.features 这个被分片的模型之前,数据的状态如下

当数据在分片的模型 self.features 前向流程走完后,发现

其在 cuda:2 设备上进行了输出。!!所以整个流程如下图所示

因此,模型分片的时候必须要对齐设备,所以,2中的例子才会有那老些的对齐设备步骤。

至此,整个过程以及需要注意的事项已经有了大概的了解,有了一定的视野。接下来,我们将会把一中的例子中的 self.classifier在之前的基础上也进行模型分片。有了上面的视图,这实现起来经不会太难。

具体地,在之前地基础上进行修改

self.classifier = nn.Sequential(

            nn.Linear(3*3*128,4096),
            nn.ReLU(),

            nn.Dropout(p=0.5),
            nn.Linear(4096,2048),
            nn.ReLU(),

            nn.Linear(2048,1024),
            nn.ReLU()
        )
# 在上面的语句下面添加
self.classifier = fairscale.nn.Pipe(self.classifier, balance=[2, 3, 2])
# 注释掉下面语句

# self.classifier.to(self.device)

注释掉是为了对齐进行模型分片后 ,模型会放到不同的设备上,如果再放到cuda:0上造成矛盾,从而出错。记住,模型分片后的设备cuda是个list,被放到了好几个cuda上。

x = self.classifier(x)
# 在上面的语句下面添加
x = x.to(self.device)

对齐设备,从之前的图中看到,x回在cuda:2上输出,所以必须把它放到cuda:0上才能进行下一步 

查看GPU显存占用情况

(base) root@3eaab89e2baa:~/data# nvidia-smi
Sat Mar  9 04:49:19 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 510.47.03    Driver Version: 510.47.03    CUDA Version: 11.6     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:04:00.0 Off |                  N/A |
| 22%   34C    P2    57W / 250W |   1326MiB / 11264MiB |     11%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   1  NVIDIA GeForce ...  On   | 00000000:05:00.0 Off |                  N/A |
| 22%   31C    P2    58W / 250W |   1420MiB / 11264MiB |     13%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
|   2  NVIDIA GeForce ...  On   | 00000000:09:00.0 Off |                  N/A |
| 22%   36C    P2    49W / 250W |   1250MiB / 11264MiB |      5%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A     13027      C                                    1323MiB |
|    1   N/A  N/A     13027      C                                    1417MiB |
|    2   N/A  N/A     13027      C                                    1247MiB |
+-----------------------------------------------------------------------------+

明显比之前的大了。

总结

到这里,通过一步步的简单的实践对模型的分片有了一定的了解。知道了怎么去实现模型分片。接下来会继续探索其中的奥秘, 望诸君共勉!嘿嘿嘿

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

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

相关文章

MySQL--优化(索引--索引失效场景)

MySQL–优化(索引–索引失效场景) 定位慢查询SQL执行计划索引 存储引擎索引底层数据结构聚簇和非聚簇索引索引创建原则索引失效场景 SQL优化经验 常见的索引失效场景 1、场景准备: 给 tb_user 表创建联合索引,字段为&#xff1…

对于SG90三线舵机的介绍

目录 一、什么是舵机 PWM 生成PWM波的原理 二、SG90工作原理 180度舵机 360度舵机 三、相关代码 main.c timer0.c timer0.h delay.h 一、什么是舵机 “舵机是一种直流微型伺服电机,它能够接收并响应控制信号,实现角度或位置的精确控制。舵机…

【中间件】RabbitMQ入门

📝个人主页:五敷有你 🔥系列专栏:中间件 ⛺️稳中求进,晒太阳 MQ的优劣: 优势 应用解耦:提升了系统容错性和可维护性异步提速:提升用户体验和系统吞吐量消峰填谷&#xff1…

高电平复位电路工作原理详解

单片机复位电路的作用是:使单片机恢复到起始状态,让单片机的程序从头开始执行,运行时钟处于稳定状态、各种寄存器、端口处于初始化状态等等。目的是让单片机能够稳定、正确的从头开始执行程序。一共分为:高电平复位,低…

SRC学习-成为赏金猎人

你是否对漏洞挖掘充满好奇?零基础或有基础但想更进一步?想赚取可观的漏洞赏金让自己有更大的自由度? 那么,不妨了解下土拨鼠的安全屋 这或许也是你成为漏洞赏金猎人的第一课。 逻辑漏洞挖掘手法与创新思路,带你突破…

漏洞复现-蓝凌LandrayOA系列

蓝凌OA系列 🔪 是否利用过 优先级从高到低 发现日期从近到远 公司团队名_产品名_大版本号_特定小版本号_接口文件名_漏洞类型发现日期.载荷格式LandrayOA_Custom_SSRF_JNDI漏洞 LandrayOA_sysSearchMain_Rce漏洞 LandrayOA_Custom_FileRead漏洞

【C++庖丁解牛】STL简介 | string容器初次见面

📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1. 什么是STL2. STL的…

Spring Boot工作原理

Spring Boot Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spr…

SpringMVC04、Controller 及 RestFul

4、Controller 及 RestFul 4.1、控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。控制器负责解析用户的请求并将其转换为一个模型。在Spring MVC中一个控制器类可以包含多个方法在Spring MVC中,对于Contr…

FPGA高端项目:FPGA基于GS2971的SDI视频接收+GTX 8b/10b编解码SFP光口传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收转HDMI输出应用本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放Video Mixer多路视频拼接应用本方案的SDI接收OSD动态字符叠加…

CorelDRAW Graphics Suite2024专业图形设计软件Windows/Mac最新25.0.0.230版

CorelDRAW Graphics Suite 2024是一款专业的图形设计软件,它集成了CorelDRAW Standard 2024和其他高级图形处理工具,为用户提供了全面的图形设计和编辑解决方案。 该软件拥有强大的矢量编辑功能,用户可以轻松创建和编辑矢量图形,…

JavaScript数组常见实例方法:forEach、filter、map、reduce、find、every等

博客背后的故事 其实我23年7月就学过这些数组方法了,但是为什么24年3月才做笔记呢?这就要讲一个小故事了(不想听故事的同学自行拖动滚动条) 24年年初我和两个队友合作开发一个小程序。JavaScript中数组的实例方法我已经学了很久…

集万千优点于一身的Haproxy集群,你还不了解?

一、HAProxy介绍 HAProxy是法国开发者威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件,是一款具备高并发(一万以上)、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统…

怎么做不限扫码次数的文件活码?文件可长期扫描展现下载

如何制作不限扫码次数的文件二维码呢?将文件转二维码后分享给其他人,是现在非常方便的一种文件传输方式。很多小伙伴在制作文件二维码的时候,比较担心的一个问题,就是二维码可以扫码的次数,担心达不到自己预期的效果&a…

C++ 打印输出十六进制数 指定占位符前面填充0

C 打印十六进制数据&#xff0c;指定数据长度&#xff0c;前面不够时&#xff0c;补充0. 代码如下&#xff1a; #include <iostream> #include <iomanip> #include <cmath>using namespace std;int main() {unsigned int id 0xc01;unsigned int testCaseId…

Docker部署SimpleMindMap结合内网穿透实现公网访问本地思维导图

文章目录 1. Docker一键部署思维导图2. 本地访问测试3. Linux安装Cpolar4. 配置公网地址5. 远程访问思维导图6. 固定Cpolar公网地址7. 固定地址访问 SimpleMindMap 是一个可私有部署的web思维导图工具。它提供了丰富的功能和特性&#xff0c;包含插件化架构、多种结构类型&…

【C++】inline内联函数 VS #define宏

文章目录 1. 内联概念2. 内联特点3. 宏的优缺点 1. 内联概念 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内联函数的地方展开&#xff0c;无函数建立栈帧的开销&#xff0c;内联函数提升程序运行的效率。 在release模式下&#xff0c;默认展开生效。 在…

时序报告Report_timing_summary之一步精通配置选项使用

目录 一、前言 二、配置选项概览图 三、配置选项 3.1 Options 3.1.1 report 3.1.2 path limits 3.1.3 path display 3.2 Advanced 3.2.1 report 3.2.3 miscellaneous 3.3 Timer Settings 3.4 共有部分 四、工程示例 4.1 工程设计代码 4.2 约束文件 4.3 Option…

Stable Diffusion WebUI 中英文双语插件(sd-webui-bilingual-localization)并解决了不生效的情况

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 大家好&#xff0c;我是水滴~~ 本文介绍一款中英文对照插件 sd-webui-bilingual-localization&#xff0c;该插件可以让你的 Stable Diffusion WebUI 界面同时显示中文和英文&#xff0c;让我…

【C++】手撕string类(超实用!)

前言 一、标准库中的string类 1.1 string类介绍 1.2 string的常用接口 1.2.1 常用的构造函数 1.2.2 容量操作接口 &#xff08;1&#xff09;size &#xff08;2&#xff09;capacity &#xff08;3&#xff09;empty &#xff08;4&#xff09;clear &#xff08…
最新文章