撰文|CPFLAME
大噶好,年更楼主今日想推的是,主打分布式练习的模型库_李白(LiBai) 。
github.com/Oneflow-Inc…
对于目前市面上的模型库来说,选择实在是太多了,换了一批又一批,眼睛都挑花了,为什么要用LiBai? (假如你觉得LiBai假如某天能用到,或许这篇文章读下来感觉比较开心,能够去GitHub上点赞,假如能三连就更好了。众所周知,GitHub点赞其实是个保藏夹功能)。
依照现在的趋势来说,模型越来越大了,大到一张GPU乃至装不下完好的模型,必须得上分布式并行技术,可是分布式代码在很多结构下都是高度定制化的,对于新手来说底子读不懂,也不知道应该怎样运用,导致咱们上手十分的困难,让自己珍贵的发际线显得愈加珍贵。
针对大模型上述存在的痛点,导致咱们必须上分布式(数据并行、模型并行、流水并行)才干跑起来一个大模型。
那么,LiBai有哪些特点呢? 你坐好,我要发功了。
需求具体分章介绍的优势(看上去还不错,用户也能够听得懂,也知道要干什么):
-
简略易用的分布式代码,单机代码和分布式代码根本共同
-
能够无缝运用PyTorch、HuggingFace的model权重,并且还能够在LiBai下进行多机多卡的分布式推理
-
开箱即用,所有的分布式并行装备(Grad Acc,AMP,Checkpointing,ZeRO,Auto Parallel)技术都只需求在config里边一键设置就能够收效,不需求在算法代码model.py中额外增加
-
支撑模型一键转化 ONNX
我搁这儿就要介绍完的优势(看上去咱们也有,很虚的帽子话),为了不让咱们觉得过于虚,在介绍的一起也会刺进相关的比如。
- 具有高度灵敏性和高效率,一起支撑动态图eager模式和静态图graph模式,支撑一键切换,在便利debug和高效性之间反复横跳。
- 对于分布式并行的支撑比较全面,咱们能够在里边尽情地组合各种分布式并行的组件。
- LiBai下面有内置的layers直接运用,防止重复造轮子,比方用LiBai下面的Linear层就能够快速地构建一个2D并行(数据并行+模型并行)的MLP。
- 选用LazyCall(学习自detectron2)的装备体系,基于Python语法构建相比于 argparse 和 yacs-based 方式更灵敏,并且每次练习都会序列化yaml文件,用户能够一键读取yaml文件来复现“上古时期”的试验结果。
-
具有丰富的Projects完成。因为LiBai的分布式并行规划与算法逻辑进行了解耦,使得在Projects下面的算法都能够享受到LiBai下面的分布式并行技术,并且跟着分布式并行技术的更新,Projects下面的算法代码不需求任何更新就能够享受到更新后的效果。
-
和业界翘楚Megatron比起来,具有不弱于它的吞吐,乃至稍占优势,完好的对比试验在LiBai tutorial(libai.readthedocs.io/en/latest/t… )和《大模型练习难于上青天?效率出众、易用的李白模型库来了》,这儿给一个GPT2的3D并行数据简略感受一下。
或许有人会问,怎样都和Megatron去比,你们各个同行之间有对比数据吗?主要原因有二:1) 只需吕布不说话辩驳,那么我邢道荣就有不下于吕布的勇武,人均小吕布,这很合理;2) 咱们都是国产结构,中国人不卷中国人,咱们薅着一个外国人可劲儿的打。
下面分章具体说说上述优点。
开源自助
LiBai最基础的一个功能: 开源自助。也便是除了LiBai练习出来的模型以外,咱们还能够加载PyTorch以及HuggingFace上面的模型进行分布式推理。
因为LiBai的底层是基于OneFlow来完成的,而OneFlow的算子绝大部分都现已和PyTorch进行了对齐,这能发挥出什么优势?
运用前还要看看准备常识。一个完好的模型由两个部分构成:模型结构,换种说法便是model.py模型权重,再换种说法便是model_best.pth。
假定咱们在结构A下面,有modelA.py和model_best_A.pth,咱们想在结构B上面跑起来这个结构A下面的模型,应该怎样做呢?
在结构B下面,用结构B的算子搭建出一个modelB.py,该modelB的参数姓名能够和modelA的不共同,可是前向推理的逻辑运算最好共同,然后去加载model_best_A.pth得到model_A_state_dict(),把model_A_state_dict()里边的参数格局悉数转化成结构B下面支撑的格局,其中能够运用中间格局进行转化。
举个比如,比方torch.tensor()->np.numpy()(中间格局)->oneflow.tensor()之前提到了modelB中的参数姓名能够和modelA中的不共同,假如不共同,那么需求把model_A_state_dict()中的key值改一下和modelB的共同。
做完了今后,直接加载咱们转化好的参数modelB.load_state_dict(model_A_state_dict),就能够在结构B下面进行推理。为了保证模型转化好今后的准确性,能够喂给modelA以及modelB相同的输入,检查一下是否能得到相同输出。
这个准备常识不仅限于LiBai,在任何模型复现或许模型迁移上面都适用。
有了准备常识今后怎样运用PyTorch或许HuggingFace下面的模型?简略来说分为以下几步:
把torch的算子替换为oneflow: 把torch_model.py下面的torch悉数替换为oneflow,得到oneflow_model.py.把oneflow_model.py中的layer尽或许地替换成LiBai中支撑的layer,只替换你想要的部分也能够(比方只替换Linear层),LiBai会自动把没有替换的layer 转化成分布式并行所需求的格局。
这一步是支撑分布式推理的要害承继LiBai供给好的分布式推理的基类basic.py,重载转化权重的函数,依照PyTorch那样写好预处理和后处理,就能够进行分布式推理了。
下面链接里边有极其具体的过程回答,看在作者不仅授人以鱼,还授人以渔,还做了程序员最讨厌的文档活儿,能够顺便给LiBai点个star保藏,并且保不齐今后假如有个什么复现的使命,这儿边的常识点也用得上,至少能够用别人release出来的预练习权重来验证自己复现的model.py是否正确。这叫什么?这叫call back!
LiBai分布式推理介绍:
http//:github.com/Oneflow-Inc/libai/discussions/386
以MT5使用HuggingFace model为比如,咱们在2机4卡下面进行模型并行2X流水并行2的分布式推理,跑起来的代码风格如下:
# test_inference.py
from libai.inference.text_generation import TextGenerationPipeline
from libai.utils import distributed as dist
if __name__ == "__main__":
pipeline = TextGenerationPipeline(
"projects/MT5/configs/t5_inference.py",
data_parallel=1,
tensor_parallel=2,
pipeline_parallel=2,
pipeline_stage_id=[0] * 12 + [1] * 12,
pipeline_num_layers=12 * 2,
model_path="data_test/t5_inference_model",
mode="huggingface",
)
text = ["summarize: She is a student, She is tall, She loves study"]
dict1 = pipeline(text)
if dist.is_main_process():
print(dict1)
那么多机多卡的分布式推理脚本
在node0上输入指令:
NODE=2 NODE_RANK=0 ADDR=192.168.0.1 PORT=12345 bash tools/infer.sh test_inference.py 2
在node1上输入指令:
NODE=2 NODE_RANK=1 ADDR=192.168.0.1 PORT=12345 bash tools/infer.sh test_inference.py 2
细心的朋友现已发现了,LiBai下面能够通过设置pipeline_stage_id, 来让用户自己设置每个stage上group的层数是多少,便利在某些极点情况下(比方你的机器0很强,可是机器1很拉胯,或许你的encoder核算量巨大,可是decoder核算量较小)手动完成负载均衡。
大模型练习
众所周知,咱们都喜爱做点”出格”的事情,比方在上班的时分摸鱼,在VScode上面炒股……
那么LiBai呢?你乃至能够拿它来练习模型!
在Projects(github.com/Oneflow-Inc… )下支撑的模型:
下面来谈谈模型之外,LiBai有什么不一样的地方,换句话说,也便是核心竞争力在哪里?
分布式装备和算法逻辑解耦
LiBai进行了模块化的规划,使得分布式的装备和算法逻辑解耦,这意味着什么?
这意味着用户只需求把大部分的注意力专注到算法逻辑上面,而不用再苦恼怎样刺进各种并行的代码了。
简略来说,下面这些模块都能够在config.py中进行一键装备。
# my_config.py
from libai.config import get_config
train = get_config("common/train.py").train
optim = get_config("common/optim.py").optim
graph = get_config("common/models/graph.py").graph
# set dist
train.dist.data_parallel_size = 2
train.dist.tensor_parallel_size = 2
train.dist.pipeline_parallel_size = 2
# set model layers for pipeline
train.dist.pipeline_num_layers = 24
# set pipeline_stage_id according to your own needs.
# if `None`, LiBai will use its own mode of distribution
train.dist.custom_pipeline_stage_id = [0]*14 + [1]*10
# set auto parallel in LiBai
graph.auto_parallel.enabled = True
# enable amp (fp16)
train.amp.enabled = True
# enable gradient clipping
optim.params.clip_grad_norm = 1.0
optim.params.clip_grad_norm_type = 2.0
# enable grad accumulation for 8 steps
train.num_accumulation_steps = 8
# enable activation checkpointing
train.activation_checkpoint.enabled = True
# enable zero for leval-2
train.zero_optimization.enabled = True
train.zero_optimization.stage = 2
单机和分布式代码简直共同
下面给一个简略的2D并行(数据并行+模型并行)的MLP比如, 比方你的Linear层在16384这个维度上面比较大, 需求把它切分在不同的卡上才干装下, 那么在LiBai下面只需求如下所示就能够完成了,简直跟单机代码没有差异。
from libai.layers.linear import Linear
from oneflow import nn
# write a Simple 2D Parallel MLP
class MLP_2D(nn.Module):
def __init__(self,):
super().__init__()
self.linear1 = Linear(in_features=1024, out_features=16384, parallel="col")
self.relu = nn.GELU()
self.linear2 = Linear(in_features=16384, out_features=1024, parallel="row")
self.dropout = nn.Dropout(p=0.5)
def forward(self, x):
x = self.linear1(x)
x = self.relu(x)
x = self.linear2(x)
x = self.dropout(x)
return x
支撑一键转化ONNX
本人对一键转ONNX的执念可谓是相当之深了。相同以MT5为比如,LiBai支撑了一键转化ONNX的功能,点击以下链接就能够体会:
github.com/Oneflow-Inc…
更具体的说明和教程会在LiBai中持续发布。假如这篇文章对你有启发,请不要吝惜手中的保藏按钮,欢迎去GitHub上Star、Fork、Watch三连,持续跟进最新进展。
GitHub地址:
github.com/Oneflow-Inc…
也欢迎你扫码进技术交流群探讨。
引证
1. github.com/facebookres…
2. github.com/Oneflow-Inc…
3. github.com/Oneflow-Inc…
4. github.com/NVIDIA/Mega…
欢迎下载体会 OneFlow v0.8.0 最新版本:
github.com/Oneflow-Inc…