在Galvatron中添加新模型

本指南将教你如何在Galvatron中添加新模型。

目录结构

一个模型在Galvatron中的目录结构如下;

MyModel/
├── meta_configs/                              # 模型配置文件目录
│   ├── __init__.py                            
│   ├── config_utils.py                        # 配置工具函数
│   ├── MyModel-{MODEL_SIZE}b.json        # 模型配置
│   └── ...                                    # 其他规模模型配置
│
├── scripts/                                   # 运行脚本目录
│   ├── profile.sh                             # 性能分析脚本
│   ├── train.sh                               # 训练脚本
│   └── search.sh                              # 并行策略搜索脚本
│
├── __init__.py                                
├── arguments.py                               # 参数定义
├── dataloader.py                              # 数据加载实现
├── profiler.py                                # 性能分析入口
├── search_dist.py                             # 并行策略搜索入口
├── train.py                                   # 单机训练入口
├── train_dist.py                              # 分布式训练入口
├── train_dist_random.py                       # 随机数据训练入口
│
├── MyModelModel_checkpoint.py            # 检查点保存加载
├── MyModelModel_hybrid_parallel.py       # 混合并行实现
├── MyModelModel_sequential.py            # 序列化模型实现
└── MyModelModel_tensor_parallel.py       # 张量并行实现

Galvatron构建混合并行模型过程

在介绍如何加入新模型之前,我们先来了解一下Galvatron构建混合并行模型的大致过程。

Galvatron构建模型不需要手动定义模型整体结构,而是通过使用transformersflash attention中相应的模型结构,你可以在MyModel中添加hffa后缀来区分你所选择的模型结构后端。如果你不知道该选择什么样的模型结构后端,我们推荐你选择hf,因为Galvatron对hf的支持更加全面(fa模型不支持Ulysses-SP并行方法)。接着基于得到的模型结构构件混合并行模型的流程在construct_hybrid_parallel_model_api中。其具体的流程如下:

  1. 预处理配置:获取混合并行策略、模型配置等信息

  2. 通信组生成 (Step 0):生成各种并行策略需要的通信组

  3. 构建张量并行模型 (Step 1):使用模型特定的 TP 函数(定义在MyModelModel_tensor_parallel.py中)构建张量并行模型

  4. 构建序列模型 (Step 2):使用模型特定的序列化函数重构模型(定义在MyModelModel_sequential.py中)

  5. 包装重分布模块 (Step 3):为模型添加数据重分布功能,保证每层的数据分布和并行策略对应

  6. 构建流水线并行 (Step 4):构建流水线并行模型,将不同的stage放置在对应设备上

  7. 包装数据并行模块 (Step 5):基于FSDP库包装数据并行模块

  8. 添加检查点包装 (Step 6):根据检查点配置为模块添加检查点功能

其中,只有该API的调用,以及Step1和Step2实现需要使用模型特定的函数完成,其他步骤都是Galvatron的通用实现。

核心文件说明

添加新模型的核心是模型实现文件,这是开发者需要实现的最主要的部分,它定义了模型的结构和实现。

1 张量并行实现

张量并行实现通过MyModelModel_tensor_parallel.py文件实现,该文件定义了模型的张量并行实现,需要将Sequential中的模块替换成支持张量并行的模块,这里Galvatron根据不同的模型后端,提供了不同的张量并行实现,具体来说,hf使用Megatron-TP,fa使用flash-attn提供的TP。

对于hf,你需要实现MyModelLayer_tp类,并实现MyModelAttention_tpMyModelMLP_tp类,对于fa,则可以直接调用flash_attn的create_mixer_clscreate_mlp_cls方法。同时你还需要定义construct_tensor_parallel_model函数,用于将完整模型进行TP模型替换。这方面的详细例子可以参考gpt_hfgpt_fa

1.1 Transformer层 (hf模型格式)

Transformer层通过MyModelLayer_tp类实现:

class MyModelLayer_tp(nn.Module):
    def __init__(self, config, layer_number, tp_group=None, sp_group=None):
        """
        参数:
            config: 模型配置对象,TransformerConfig
            layer_number: 当前层的索引编号
            tp_group: 当前层张量并行通信组,CommGroup
            sp_group: 当前层序列并行通信组,CommGroup
        """
        super().__init__()
        self.attention = MyModelAttention_tp(config, layer_number, tp_group, sp_group)
        self.mlp = MyModelMLP_tp(config, tp_group)
        self.idx = layer_number
        
    def forward(self, hidden_states, attention_mask=None):
        # ...
        pass

该类主要负责定义一层Transformer的实现,包括注意力机制和前馈神经网络,需要注意的是self.idx的定义是必要的,这关乎后面如何区分层,config则直接使用创建Transformer库中的模型时使用的TransformerConfig类。

1.2 注意力层 (hf模型格式)

注意力层通过MyModelAttention_tp类实现:

class MyModelAttention_tp(nn.Module):
    def __init__(self, config, layer_number, tp_group=None, sp_group=None):
        """
        参数:
            config: 模型配置对象,TransformerConfig
            layer_number: 当前层的索引编号
            tp_group: 张量并行通信组,CommGroup
            sp_group: 序列并行通信组,CommGroup
        """
        super().__init__()
        # ...
        megatron_config = core_transformer_config_from_args(args)
        self.attention = ParallelAttention(megatron_config, ...)
        # ...
    def forward(self, hidden_states, attention_mask):
        # ...
        pass

ParallelAttention是Galvatron修改后的Megatron-TP中的注意力层实现,在原版Megatron-TP的注意力层实现中,增加了tp_group、sp_group、use_ulysses三个参数,分别表示张量并行通信组、序列并行通信组、是否使用Ulysses序列并行,通常来说你可以直接参考gpt_hf的例子实现这部分。

1.3 前馈神经网络层(hf模型格式)

前馈神经网络层通过MyModelMLP_tp类实现:

class MyModelMLP_tp(nn.Module):
    def __init__(self, config, tp_group=None):
        """
        参数:
            config: 模型配置对象,TransformerConfig
            tp_group: 张量并行通信组,CommGroup
        """
        super().__init__()
        # ...
        megatron_config = core_transformer_config_from_args(get_args())
        self.mlp = ParallelMLP(megatron_config, tp_group = self.tp_group)
        # ...
    def forward(self, hidden_states):
        # ...
        pass

ParallelMLP是Galvatron修改后的Megatron-TP中的前馈神经网络层实现,在原版Megatron-TP的注意力层实现中,增加了tp_group这个参数,用于表示张量并行通信组,通常来说你可以直接参考gpt_hf的例子实现这部分。

1.4 构造张量并行模型(hf模型格式)

构造张量并行模型通过construct_tensor_parallel_model函数实现:

def construct_tensor_parallel_model(model, config, tp_groups_enc, sp_groups_enc):
    """
    将模型转换为张量并行版本
    
    参数:
        model: 原始模型实例
        config: 模型配置对象,TransformerConfig
        tp_groups_enc: 每一层的张量并行通信组列表,List[CommGroup]
        sp_groups_enc: 每一层的序列并行通信组列表,List[CommGroup]
        
    返回:
        转换后的张量并行模型
    """
    # ...
    pass

该函数主要完成三件事:将模型中的Transformer Layer替换为MyModelLayer_tp,将模型中的embedding层替换为VocabParallelEmbedding,将模型中的lm_head替换为ColumnParallelLinearVocabParallelEmbeddingColumnParallelLinear是同样是Galvatron修改后的Megatron-TP中的嵌入层和线性层实现,增加了tp_group和sp_group这两个参数,用于表示张量并行通信组和序列并行通信组,你也可以直接参考gpt_hf的例子实现这部分。

注意:这些类和函数中用到的通信组是Galvatron自定义的CommGroup类,如果你想访问torch生成的通信组,请使用tp_group.groupsp_group.group

1.5 构造张量并行模型(fa模型格式)

对于fa,你只需要实现construct_tensor_parallel_model函数即可,在该函数中你需要将Transformer Layer中的attention和mlp模块分别替换为flash_attn的create_mixer_clscreate_mlp_cls方法,将embedding层替换为flash_attn的ParallelGPT2Embeddings方法,将lm_head替换为flash_attn的ColumnParallelLinear方法。详细的例子请参考gpt_fa

2 序列化模型实现

MyModelModel_sequential.py定义了模型的序列化实现,包括模型的前向传播和反向传播实现。

对于传统的Transformer模型,你需要实现MyModelEmbeddings_, MyModelLayers_, MyModelPreNorm_, MyModelCls_ 等类。

此外,还需要实现construct_sequential_model函数,用于将模型转换为序列化模型。以及MyModelModelInfo类,用于定义模型相关信息。

具体来说,每个类的定义和格式如下:

2.1 嵌入层

嵌入层通过MyModelEmbeddings_类实现:

class MyModelEmbeddings_(nn.Module):
    def __init__(self, model):
            """
            参数:
                model: 模型实例
            """
            super().__init__()
            # ...
        def forward(self, tokens, **kwargs):
            # ...
            pass

该类主要用于定义模型中的嵌入层,包括词嵌入、位置嵌入等。

这里__init__函数中需要传入的model是直接通过调用transformers或flash-attn获取到的模型(所有API中model都需要传入transformers或flash-attn获取到的模型)。

为了增强代码的健壮性,该函数还需要支持一些额外的特性:Megatron序列并行、Ulysses序列并行(fa不支持),这方面的详细例子可以参考gpt_hfgpt_fa

注意:当使用hf后端时,对于有多种Embedding类型的文件(比如GPT同时拥有Vocab和Position Embedding),需要额外定义不同的Embedding类以区分这两种不同的Embedding参数,gpt_hf中展示了这样的一个例子。

2.2 Transformer层

Transformer层通过MyModelLayers_类实现:

class MyModelLayers_(nn.Module):
    def __init__(self, model, layer_idx):
        """
        参数:
            model: 模型实例
            layer_idx: 当前层的索引编号
        """
        super().__init__()
        # ...
    def forward(self, hidden_states, **kwargs):
        # ...
        pass

该类主要用于定义模型中的Transformer层,包括自注意力层、前馈神经网络层等。

对于fa后端,需要根据代码中实际的模型结构,决定是否添加残差和dropout。

2.3 归一化层

归一化层通过MyModelPreNorm_类实现:

class MyModelPreNorm_(nn.Module):
    def __init__(self, model):
        """
        参数:
            model: 模型实例
        """
        super().__init__()
        # ...
    def forward(self, hidden_states, **kwargs):
        # ...
        pass

该类主要用于定义模型中输出层前的归一化层。

2.4 输出层

输出层通过MyModelCls_类实现:

class MyModelCls_(nn.Module):
    def __init__(self, model):
        """
        参数:
            model: 模型实例
        """
        super().__init__()
        # ...
    def forward(self, hidden_states, **kwargs):
        # ...
        pass

该类主要用于定义模型的输出层。

为了增强代码的健壮性,该函数还需要支持一些额外的特性:Megatron序列并行、Ulysses序列并行(fa不支持)、并行求loss(fa不支持),这方面的详细例子可以参考gpt_hfgpt_fa

注意:当使用hf后端时,获取logits_parallel需要直接引用原模型的.weight变量,这一点在FSDP中是不允许的,因此可以单独将获取logits_parallel的代码放在一个单独的函数中,用MyModelLoss_来表示,gpt_hf中展示了这样的一个例子。

在实现这些层时,需要特别注意,Transformer层中相同种类的层的forward函数输入张量(kwargs除外)和输出张量的格式和大小相同,这是为了方便更新模型信息,以保证流水线并行的正确性。例如在gpt_hf中,Transformer层的forward函数输入张量和输出张量的格式和大小相同,都是hidden_states。

2.5 构造序列化模型

构造序列化模型通过construct_sequential_model函数实现:

def construct_sequential_model(model, config):
    """
    将模型转换为序列化版本
    
    参数:
        model: 原始模型实例
        config: 模型配置对象,TransformerConfig
        
    返回:
        转换后的序列化模型
    """
    model_ = PipeSequential()
    # ...

这个函数将模型转化为PipeSequential 格式,它是一个特殊的序列容器,专门用于流水线并行。开发者只需要把模型按照顺序顺次通过add_module方法添加到PipeSequential中即可。

注意:如果使用了MyModelLoss_,还需要给其增加reset_parameters方法,以保证模型可以正确初始化。

2.6 模型信息

模型信息通过MyModelModelInfo类实现:

class MyModelModelInfo(ModelInfo):
    def __init__(self, config, args):
        super(MyModelModelInfo, self).__init__()
        # ...
        self.set_layernums(layernum_list)
        self.set_shapes(layer_shapes_list)
        self.set_dtypes(layer_dtypes_list)
        self.set_module_types(module_types)

在该类中,需要赋值四个变量:layernumsshapesdtypesmodule_types,分别表示每种不同类型的Transformer层数,每种类型层的输入输出张量形状、每种类型层输入输出张量的数据类型、模型每一层的模型名称。

对于layernums,需要赋值一个列表,列表中的每个元素表示每种类型Transformer层的数量,例如对于GPT,列表的长度为1,因为GPT只有一种Decoder层,但对于T5,列表的长度为2,因为T5同时包含Encoder和Decoder层,这两种层的结构是不同的。

对于shapes,需要赋值一个列表,列表中的每个元素表示每种类型Transformer层的输入输出张量形状,通常是一个大小为[x,y]的列表,x表示Transformer层的种类,y表示每层输入输出张量的数量,列表中的每个值存储的是输入输出张量的形状。

对于dtypes,需要赋值一个列表,列表中的每个元素表示每种类型Transformer层的输入输出张量的数据类型,通常是一个大小为[x,y]的列表,x表示Transformer层的种类,y表示每层输入输出张量的数量,列表中的每个值存储的是输入输出张量的数据类型。

对于module_types,需要赋值一个列表,列表中的每个元素顺次表示模型中每一层的名称。

3 混合并行实现

混合并行实现通过MyModelModel_hybrid_parallel.py文件实现,该文件是连接模型与Galvatron并行系统的桥梁,主要负责构建支持混合并行的模型实例。

该文件主要实现了四个函数:get_hybrid_parallel_configsconstruct_hybrid_parallel_modelget_mymodel_configmymodel_model_hp

3.1 获取混合并行配置

get_hybrid_parallel_configs函数用于获取混合并行策略,其实现格式如下:

def get_hybrid_parallel_configs(model_config, training_args):
    hybrid_parallel_configs = get_hybrid_parallel_configs_api(model_config, training_args, MyModelModelInfo)
    return hybrid_parallel_configs

该函数不需要任何改动,通过调用Galvatron的get_hybrid_parallel_configs_api函数获取混合并行策略,并返回一个字典,字典中包含混合并行策略信息。

3.2 构建混合并行模型

construct_hybrid_parallel_model函数用于构建混合并行模型,其实现格式如下:

def construct_hybrid_parallel_model(model, model_config, training_args, hybrid_parallel_configs):
    # ...
    hp_model = construct_hybrid_parallel_model_api(...)
    return hp_model

该函数通过调用Galvatron的construct_hybrid_parallel_model_api函数构建混合并行模型,并返回一个支持混合并行的模型实例。具体来说,该API函数具体需要的参数和格式如下:

def construct_hybrid_parallel_model_api(
    model, # 原始模型实例   
    model_config, # 模型配置对象
    training_args, # 训练参数
    hybrid_parallel_configs, # 混合并行配置
    model_info, # 模型信息类
    construct_sequential_model, # 构建序列化模型的函数
    construct_tensor_parallel_model, # 构建张量并行模型的函数
    wrap_block_name=None, # 需要包装FSDP的模块名称列��
    wrap_checkpoint_block_name=None, # 需要添加检查点的模块名称列表
    wrap_other_block_name=None, # 需要包装FSDP的其他模块名称列表
    tied_wte_attr_names=None, # 权重绑定的属性名称列表
    layernorm_name = [], # 层归一化的名称列表
    all_block_name = None, # 所有模块的名称列表
    load_module_func = None, # 加载模块的函数
):
    # ...
    pass

参数可以直接参考gpt_hfgpt_fa的实现。

在此,我们额外对一些可能感到疑惑的可选参数进行解释:

  • wrap_block_name:需要包装FSDP的Transfomer层模块类列表。

  • wrap_checkpoint_block_name:需要添加检查点的模块名称列表,通常是Transformer层。

  • wrap_other_block_name:需要包装FSDP的其他模块名称列表,通常是Transformer层以外的其它层,注意这里如果定义了多个Embedding类,需要将所有细粒度Embedding类都添加到列表中。

  • tied_wte_attr_names:权重绑定的属性名称列表,部分模型Vocab Embedding层和输出层的参数是相同的,对于需要这种需求的模型,开发者需要将模型第一层和最后一层中如何访问Vocab Embedding层的方式告诉Galvatron,例如对于gpt_hfGPTVocabEmbedding_类在Embedding层通过self.wte访问,而输出层在Cls层直接通过self访问即可,因此tied_wte_attr_names为['wte','']

  • layernorm_name:用于标识Galvatron在不同的层该如何访问Layernorm的名称列表(不需要完整名称,只需要知道后缀名词即可),例如对于gpt_hf,Layernorm在GPTAttention_tpGPTMLP_tp类中通过self.LayerNorm访问,在GPTPreNorm_中通过self.ln访问,因此layernorm_name['LayerNorm', 'ln']

  • all_block_name:所有模块的名称列表,通常是wrap_block_namewrap_other_block_name的并集。

  • load_module_func:加载模块的函数,通常是定义在MyModelModel_checkpoint.py文件中的load_MyModel_module函数。

注意:虽然wrap_block_namewrap_checkpoint_block_namewrap_other_block_nameall_block_name这些参数在construct_hybrid_parallel_model_api中是可选参数,但为了保证模型可以正确初始化,这些参数必须传入。

3.3 获取模型配置

get_mymodel_config函数用于获取模型配置,其实现格式如下:

def get_mymodel_config(args, overwrite_args=True):
    config = config_from_meta(args.model_size)
    config = set_model_config(config, args, overwrite_args)
    if hasattr(args, 'local_rank') and args.local_rank == 0:
        print(config)
    return config

3.4 构建混合并行模型

mymodel_model_hp函数用于构建混合并行模型,其实现格式如下:

def mymodel_model_hp(config, args):
    hybrid_parallel_configs = get_hybrid_parallel_configs(model_config=config, training_args=args)
    if args.local_rank == 0:
        print("Creating Model...")
    mymodel_model = MyModelModel_huggingface(config)
    model = construct_hybrid_parallel_model(
        model=mymodel_model, 
        model_config=config, 
        training_args=args, 
        hybrid_parallel_configs=hybrid_parallel_configs
    )
    return model

注意这里MyModelModel_huggingface是直接通过transformers获取到的模型,而不是Galvatron的模型。在huggingface中选择模型时,需要选择包含输出层的模型。

4 模型检查点保存加载实现(Experimental, 支持hf)

模型检查点保存加载实现通过MyModelModel_checkpoint.py文件实现,该文件定义了模型的检查点保存和加载实现,包括检查点的保存和加载函数。

该文件需要实现save_MyModel_moduleload_MyModel_module函数。用于实现模型检查点的保存和加载。

Galvatron是按层存储和加载模型检查点的,因此在实现时需要注意按层进行加载和存储。

llama_hf中展示了如何实现模型检查点的保存和加载。

辅助文件说明

1 模型配置文件

模型配置文件定义了模型的配置,包括模型的结构、参数量等。

1.1 模型配置存储文件

meta_configs/MyModel-{MODEL_SIZE}b.json:模型配置文件,用于存储模型配置信息。

1.2 模型配置处理文件

  • meta_configs/config_utils.py:该文件主要负责处理模型配置相关的功能,其主要包括三部分:

    • 获取模型配置信息:通过调用config_from_meta函数获取模型配置信息,并写入到TransformerConfig中。

    • 修改模型配置信息:通过调用set_model_config函数,根据传入的arguments修改模型配置信息,并通过overwrite_megatron_argsoverwrite_model_args函数修改arguments中的模型配置信息。

    • 获取模型相关信息:通过model_name函数获取模型名称,通过model_layer_configs函数获取模型每一层的配置信息。

2 训练文件

训练文件主要定义了训练相关的功能,包括数据加载、模型训练等。

2.1 训练主文件

  • train_dist.py:该文件主要负责分布式训练相关的功能。

一个完整的示例如下:

def train(args):
    # 初始化分布式训练环境
    local_rank = args.local_rank
    rank = torch.distributed.get_rank()
    torch.cuda.set_device(local_rank)
    device = torch.device("cuda", local_rank)
    world_size = torch.distributed.get_world_size()

    config = get_mymodel_config(args)
    model = mymodel_model_hp(config, args)

    # 创建数据集
    if local_rank == 0:
        print("Creating Dataset...")
    
    # 设置数据集相关参数    
    set_megatron_args_for_dataset(args, model, 
                                 model.sp_groups_whole[0] if args.vocab_sp else model.tp_groups_whole[0], 
                                 model.dp_groups_whole[0])
    if local_rank == 0:
        _print_args("arguments", args)

    # 获取数据迭代器
    train_data_iterator, valid_data_iterator, test_data_iterator = get_train_valid_test_data_iterators()
    
    # 创建优化器和学习率调度器
    optimizer, opt_param_scheduler = get_optimizer_and_param_scheduler(model, args)

    # 设置性能分析器
    path = os.path.dirname(os.path.abspath(__file__))
    profiler = GalvatronProfiler(args)
    profiler.set_profiler_dist(path, model_layer_configs(config), model_name(config), start_iter=0)
    
    # 记录模型创建后的内存使用情况
    profiler.profile_memory(0, "After creating model")
    if local_rank == 0:
        print("Start training...")

    # 训练循环
    for iter in range(args.iteration, args.train_iters):
        # 获取一个批次的数据
        tokens, kwargs, loss_func = get_batch(train_data_iterator)
        
        # 记录开始时间和内存使用
        profiler.profile_time_start(iter)
        profiler.profile_memory(iter, "Before Forward")

        # 准备输入数据
        input_ids = tokens
        batch = [input_ids]
        
        # 前向传播和反向传播
        loss = model.forward_backward(batch, iter, profiler, 
                                      loss_func=loss_func,
                                      **kwargs)
        
        # 记录反向传播后的内存使用
        profiler.profile_memory(iter, "After Backward")
        
        # 梯度裁剪
        total_norm = clip_grad_norm(model, args.clip_grad)
        
        # 优化器步骤
        optimizer.step()
        # 学习率调度器步骤
        opt_param_scheduler.step(increment=args.global_batch_size)
        
        # 记录优化器步骤后的内存使用
        profiler.profile_memory(iter, "After optimizer_step")
        
        # 清零梯度
        optimizer.zero_grad()

        # 更新性能统计信息
        profiler.post_profile_memory(iter)
        # 获取当前学习率
        for param_group in optimizer.param_groups:
            learning_rate = param_group['lr']
        # 记录本次迭代的性能指标
        profiler.profile_time_end(iter, loss, learning_rate, total_norm)
        
        # 同步所有进程
        torch.distributed.barrier()

        # 定期保存模型检查点
        if args.save != None and (iter + 1) % args.save_interval == 0:
            save_llama_module(args.save, model, optimizer, opt_param_scheduler, iter + 1, args)

if __name__ == '__main__':
    # 初始化Galvatron训练环境
    args = initialize_galvatron(model_args, mode='train_dist')
    # 设置随机种子以确保可重复性
    set_seed()
    # 开始训练
    train(args)
  • train_dist_random.py:该文件主要负责分布式训练相关的功能,与train_dist.py类似,但使用随机数据进行训练。

2.2 数据加载文件

  • dataloader.py:该文件主要负责数据加载相关的功能,其主要包括两部分:

    • 随机数据加载:创建生成随机token的dataset,并创建collate_fn函数,将随机token转换为模型输入。 如下是一个随机数据加载的示例:

    def random_get_ltor_masks_and_position_ids(data):
    """Build masks and position id for left to right model."""
        micro_batch_size, seq_length = data.size()
        att_mask_batch = 1
        attention_mask = torch.tril(torch.ones(
            (att_mask_batch, seq_length, seq_length), device=data.device)).view(
                att_mask_batch, 1, seq_length, seq_length)
        attention_mask = (attention_mask < 0.5)
    
        return attention_mask
    
    def random_collate_fn(batch):
        # 将batch中的数据堆叠,并返回对应格式的数据
        tokens_ = torch.stack(batch, dim=0)
        labels = tokens_[:, 1:].contiguous()
        tokens = tokens_[:, :-1].contiguous()
        args = get_args()
        if not args.use_flash_attn:
            attention_mask = random_get_ltor_masks_and_position_ids(tokens)
        else:
            attention_mask = None
        return tokens, {"attention_mask":attention_mask, "labels" : labels}, None
    
    class DataLoaderForMyModel(Dataset):
        def __init__(self, args, device, dataset_size = 2560 * 16):
            self.vocab_size = args.vocab_size
            self.sentence_length = args.seq_length
            self.dataset_size = dataset_size
            # 随机生成每个样本的实际长度(1到最大长度之间)
            self.data_length = np.random.randint(1,self.sentence_length+1,(self.dataset_size,))
            self.device = device
    
            # 生成随机输入数据
            self.input_ids = []
            for i in range(self.dataset_size):
                sentence = np.random.randint(0,self.vocab_size,(self.sentence_length,))
                sentence[self.data_length[i]:] = 0
                mask = np.ones((self.sentence_length,))
                mask[self.data_length[i]:] = 0
    
                padding_sentence = np.zeros(self.sentence_length + 1, dtype=sentence.dtype)
                padding_sentence[:self.sentence_length] = sentence
                self.input_ids.append(padding_sentence)
    
            self.input_ids = np.array(self.input_ids)
    
        def __len__(self):
            return self.dataset_size
    
        def __getitem__(self, idx):
            if idx >= self.dataset_size:
                raise IndexError
            input_ids = torch.LongTensor(self.input_ids[idx]).to(self.device)
            return input_ids
    

    具体的trainloader由以下代码创建:

    trainloader = distributed_dataloader(
        dataset=DataLoaderForGPT(args, device),
        global_bsz=args.global_train_batch_size,
        shuffle=True,
        args=args,
        group = model.dp_groups_whole[0].group,
        collate_fn = random_collate_fn
    )
    

    其中distributed_dataloader函数是Galvatron提供的分布式数据加载器,用于创建分布式数据加载器。

    • 真实数据加载:创建真实数据加载器,并设计loss计算函数。

    真实数据加载的实现基于Megatron dataset,主要包含train_valid_test_datasets_providerget_train_valid_test_data_iteratorsget_batchloss_func等函数。一个具体实现的例子可以参考gpt_hf

    主要注意的是,get_batch函数返回一个tuple,tuple中包含三个元素,分别是:

    • 输入数据:通常是一个token序列,torch.Tensor类型。

    • 其他输入数据:通常是字典类型,包含position_ids、attention_mask、labels等。

    • loss计算函数:通过调用loss_func(output_tensor)函数可以直接计算出loss。

    注意:这里的输入数据要和MyModelModel_sequential.py文件中Embedding层的输入数据格式保持一致。而其他数据则作为**kwargs在模型层之间传递。

2.3 性能分析文件

  • profiler.py:该文件主要负责性能分析相关的功能,其内容如下:

if __name__ == '__main__':
    # 初始化Galvatron性能分析环境
    args = initialize_galvatron(model_args, mode='profile')
    
    # 加载模型配置
    config = get_mymodel_config(args, overwrite_args=False)
    
    # 创建性能分析器实例
    profiler = GalvatronProfiler(args)
    
    # 获取当前文件的目录路径
    path = os.path.dirname(os.path.abspath(__file__))
    
    # 设置性能分析器启动器
    profiler.set_profiler_launcher(path, layernum_arg_names(), model_name(config))
    
    # 启动性能分析脚本
    profiler.launch_profiling_scripts()
    
    # 处理收集到的性能数据
    profiler.process_profiled_data()

2.4 策略搜索文件

  • search_dist.py:该文件主要负责策略搜索相关的功能,其内容如下:

if __name__ == '__main__':
    args = initialize_galvatron(model_args, mode='search')
    config = get_mymodel_config(args, overwrite_args=True)
    path = os.path.dirname(os.path.abspath(__file__))
    print(args)
    print(config)
    # 创建策略搜索引擎实例
    search_engine = GalvatronSearchEngine(args)
    
    # 设置搜索引擎的基本信息
    search_engine.set_search_engine_info(path, model_layer_configs(config), model_name(config))
    
    # 初始化搜索引擎
    search_engine.initialize_search_engine()

    # 进行策略搜索
    search_engine.parallelism_optimization()

3 脚本文件

scripst文件夹中主要包含一些脚本文件,用于实现模型训练、性能分析、策略搜索等功能。

主要包含五种不同的脚本:

  • profile_computation.sh:用于性能分析,计算模型在不同配置下的计算性能。

  • profile_memory.sh:用于性能分析,计算模型在不同配置下的内存使用情况。

  • search_dist.sh:用于策略搜索,搜索模型在不同配置下的最优策略。

  • train_dist.sh:用于模型训练,训练模型。

  • train_dist_random.sh:用于模型训练,使用随机数据训练模型。