Shortcuts

3. 基础模块

3.1   CoLLiE 的 Config 模块

3.2   CoLLiE 的 Dataset 和 Model

3.3   CoLLiE 的 Evaluator 和 Metric

3.4   CoLLiE 的 Trainer 模块

3.1   CoLLiE 的 Config 模块

    在tutorial-1的代码示例中,除了最开始的import部分,第一个用到的CoLLiE模块就是CollieConfig。CoLLie 的配置模块 CollieConfig 是整个 CoLLiE 的核心,CoLLie 的高度集成化归功于对 CollieConfig 配置文件的集中统一管理。CoLLiE 几乎所有的组件都受到 CollieConfig 的控制,包括模型架构(CollieConfig.model_config)、并行策略(CollieConfig.dp_size, CollieConfig.pp_size, CollieConfig.tp_size)、微调方法(CollieConfig.peft_config)等;CollieConfig 涉及的全部配置参数 以及 对应的功能描述 如下表所示。

名称

描述

seed

随机数种子,整数 默认 42

dp_size

数据并行粒度,整数 默认 1,详见 tutorial-6

pp_size

流水线并行粒度,整数 默认 1,详见 tutorial-6

tp_size

张量并行粒度,整数 默认 1,详见 tutorial-6

pp_partition_method

流水线并行切分方式,默认 'parameters',详见 tutorial-6.

train_epochs

训练 epoch 数量,整数 默认 100

eval_per_n_steps

多少 step 一次评测,整数 默认 0

eval_per_n_epochs

多少 epoch 一次评测,整数 默认 0

train_micro_batch_size

训练 batch 大小,整数 默认 1(流水线并行:作为 micro_batch 大小,详见 tutorial-6

gradient_accumulation_steps

多少 backward 一次 step,整数 默认 1(流水线并行:决定 train_batch 大小,详见 tutorial-6

eval_batch_size

测试 batch 大小,整数 默认 1

checkpointing

是否使用 activation checkpointing,默认 True

use_flash

是否使用 flash attention 进行自注意力加速,默认 True

dropout

dropout 大小,浮点数 默认 0.0

init_method

参数初始化方法,可选值 'none'(默认)、'normal'、'kaiming_normal'、'kaiming_uniform’等

low_cpu_mem_usage

是否在模型初始化阶段尝试减少 CPU 占用,默认 True

ds_config

指定 deepspeed 参数,字典型 或 json文件名,涉及零冗余优化器,详见 tutorial-6

model_config

指定模型架构相关的配置项,默认 transformers.PretrainedConfig()

peft_config

指定模型参数高效微调方法,默认 peft.PeftConfig()

quantization_config

指定模型量化方法,默认 transformers.BitsAndBytesConfig()

    由于在 CollieConfig 的所有的配置项中,模型架构相关的配置是较为繁琐但又相对固定的,因此 CollieConfig 提供了与 transformers.PretrainedConfig 类似的 from_pretrained 方法初始化 CollieConfig,并对 CollieConfig.model_config 赋值。例如,在tutorial-1中,首先明确模型架构为"meta-llama/Llama-2-7b-hf",接着使用 CollieConfig.from_pretrained 快速初始化,再定义并行相关参数(需要注意的是,由于是4卡运行,虽然三个并行粒度全部设为1,但数据并行粒度dp_size会自动设为4,详见 tutorial-6),最后定义训练相关参数,如 训练轮数、评测频率、batch大小。

from collie import CollieConfig

model_name_or_path = "meta-llama/Llama-2-7b-hf"

config = CollieConfig.from_pretrained(model_name_or_path)

config.dp_size = 4
config.pp_size = 1
config.tp_size = 1
config.train_epochs = 3
config.train_micro_batch_size = 32
config.gradient_accumulation_steps = 1
config.ds_config = {
    "fp16": {"enabled": True},
    "monitor_config": {
        "enabled": True,
        "tag": f"tutorial-lr_0.00001-epoch_3",
        "wandb": {
            "enabled": True,
            "team": "collie_exp",
            "project": "llama_alpaca",
            "group": f"llama2_7b",
        }
    },
    "zero_optimization": {"stage": 3},
}

3.2   CoLLiE 的 Dataset 和 Model

    在tutorial-1中,在通过 CollieConfig 制定配置参数之后,接下来就是依次加载 数据集 Dataset、初始化 模型 Model;二者也是本小节将要详细介绍的 CoLLiE 模块。

    首先介绍的是 CoLLiE 自定义的模型基类 CollieModelForCausalLM。我们知道 CoLLiE 最关键的功能是同时实现了对多种并行策略的支持,但是 张量并行 和 流水线并行 对模型架构的严格要求,普通的 torch.nn.Modul 或 transformers.PreTrainedModel 无法实现上述并行策略;因此,在 CoLLiE 中,我们自定义了支持多种并行方案的模型基类 CollieModelForCausalLM。使用 CoLLiE 训练或评测模型必须使用 CollieModelForCausalLM 或其子类 初始化模型;如果强行使用 torch.nn.Modul 或 transformers.PreTrainedModel 训练,仅支持 数据并行 和 零冗余优化器。 CollieModelForCausalLM 继承并重写了一些较为流行的模型,例如 LLaMA(LlamaForCausalLM)、MOSS(MossForCausalLM、Moss003MoonForCausalLM)、ChatGLM/ChatGLM2(ChatGLMForCausalLM、ChatGLM2ForCausalLM)、InternLM(InternLMForCausalLM),方便用户直接调用,如下表所示。在重写的过程中,我们保证接口尽可能和 transformers 一致,如下表所示,方便熟悉 transformers 的用户能快速上手使用。

名称

描述

from_config

根据config随机初始化模型,config可以是CollieConfig 或 字符串(hf或本地路径)

from_pretrained

根据config初始化模型,并通过model_name_or_path找到预训练模型参数权重加载

forward

前向传播函数,输入 input_ids、attention_mask、past_key_values 等字段

generate

生成函数,直接继承自 transformers.generation.utils,用法一致

main_input_name

属性,返回输入必须在forward函数中输入的字段

例如,在tutorial-1中,在指定模型类别并初始化 CollieConfig 后,即可以通过 CollieModelForCausalLM.from_pretrained,即这里的 LlamaForCausalLM.from_pretrained,初始化 LLaMA2 模型,并从 huggingface 下载预训练参数并完成加载,加载过程遵循传入的 CollieConfig。

from collie import LlamaForCausalLM

# model_name_or_path = "meta-llama/Llama-2-7b-hf"
# config = CollieConfig.from_pretrained(model_name_or_path, trust_remote_code=True)

model = LlamaForCausalLM.from_pretrained(model_name_or_path, config=config)

    在model中,forward函数必须要输入input_ids和attention mask,从而根据输出的logits和标签labels计算loss,然后反向传播、梯度更新。但在实际的训练过程当中,训练任务可以是简单的语言建模,同时也可以是指令微调;而对于后者,不是所有input_ids对应的logits都需要参与loss的计算。除此之外,在测试过程当中,模型的输入也存在一些特殊情况需要考虑。虽然对于生成任务,我们可以直接根据输入让模型续写,但是对于生成形式的分类任务,还需要让模型额外考虑选项等因素。

    对于上述两个问题,CoLLiE 提出了 自定义的数据集 CollieDataset实现自动的数据格式转换。在 CoLLiE 的使用过程中,可以使用 datasets.load_dataset,不一定涉及到 CollieDataset;但是无论如何,都必须要实现输入数据格式的转换,即 将每笔数据转化为字典,并且 该字典的键值 需要与 模型CollieModel 和 评测指标Metric的输入 一致。CoLLiE 定义了三种 CollieDataset,如下表所示:CollieDatasetForTraining/CollieDatasetForPerplexity、CollieDatasetForGeneration、CollieDatasetForClassfication;CollieDataset完整的初始化参数列表如下所示;无论哪种 CollieDataset,在初始化时都需要将数据以一个dict列表的形式输入(每个dict一笔数据),限定了输入数据的形式,从而在训练或测试过程中,CollieDataset向模型和评测指标传递格式对齐的数据dict。

名称

描述

dataset

数据,dict列表形式,dict形式取决于CollieDataset类型

tokenizer

PreTrainedTokenizer类型,可以为None,但一般要求输入

add_special_tokens

tokenize参数,是否在tokenize过程中加入特殊token,默认 True

max_length

tokenize参数,tokenize过程中截断序列最大长度,默认-1,即不截断

shuffle

dataset参数,是否在构建数据集时打乱顺序,默认 False

seed

随机数种子,默认 1024

style

CollieDatasetForClassfication特有参数,默认 "harness"

对于CollieDatasetForPerplexity输入的数据dict有两种形式输出的数据dict只有一种形式。一种输入形式,数据dict只包含一个字段“text”对应简单的语言建模,在“text”内根据前若干个token预测下一个token。此时输出的“input_ids”、“labels”就是“text”经过tokenizer处理过后的结果,没有错位;“input_ids”输入model计算“logits”,再和“labels”输入metric或者loss_fn。另一种输入形式,数据dict包含两个字段“prompt”和“output”对应指令微调任务,根据固定的“prompt”续写内容和“output”比较计算损失;此时输出的“input_ids”是“prompt”和“output”拼接后经过tokenizer处理过后的结果,而“labels”虽然与“input_ids”形状一样,但是仅后半部分对应tokenize的“output”,对应“prompt”的前半部分全部设为-100;由此实现了仅有生成部分参与loss_fn的计算,实现了对语言建模和指令微调两种任务形式的兼容。CollieDatasetForPerplexity主要用于训练,所以也命名为CollieDatasetForTraining;测试阶段,尤其是下游任务的评测主要使用后两种CollieDataset。

    对于针对生成任务的CollieDatasetForGeneration输入和输出的数据dict只有一种形式。dataset的输入数据dic包含两个字段“text”、“target”,意图让模型根据“text”输入实现“target”输出。此时dataset的输出数据dict包含“input_ids”、“labels”、“target”,其中,“input_ids”、“labels”是“text”经过tokenizer处理过后的结果,“target”是“target”经tokenizer处理过后的结果;这里的“labels”不在生成任务评测的metric中发挥作用。

4a82e2cb41374507b9f5f916489e084d

对于针对分类任务的CollieDatasetForClassfication,其输入的数据dict只有一种形式,包含输入问题“input”、候选答案列表“output”、答案序号“target”(即正确答案在候选答案列表中的序号),但是输出的数据dict有两种形式,分别对应两种处理方法,harness和helm。harness假设正确答案一定在候选答案中,那么对于这个正确的候选答案,将它和问题拼接得到的句子的困惑度会最小。也因此harness输出的数据dict包含三个字段:“input_ids”、“labels”、“target”,其中,“input_ids”、“labels”是输入数据dict中问题“input”和每个候选答案拼接后tokenize得到的列表,“input_ids”输入model计算“logits”结合“labels”得到每个选项的困惑度打分,“target”和输入数据dict的“target”一致。

而与harness相对,helm不认为正确答案一定存在于候选答案中,直接要求模型在给定问题“input”的条件下预测结果,如果不在候选答案列表“output”则预测结果设为-1。helm输出的数据dict包含四个字段:“input_ids”、“labels”、“output”、“target”,“input_ids”、“labels”只是输入数据dict中问题“input”经tokenizer处理过后的结果,“labels”同样不起作用,“output”是输入数据dict中候选答案列表“output”经tokenizer处理过后的列表,“target”仍然和输入数据dict的“target”一致。关于上述CollieDataset在评测中的具体应用详见下一节,下表简单梳理了不同CollieDataset的功能和输入输出注意事项。

名称

描述

CollieDatasetForTraining

所有CollieDataset的基类,行为与CollieDatasetForPerplexity完全相同

CollieDatasetForPerplexity

用于训练或测试LLM输出困惑度的CollieDataset,输入两种形式,输出一种形式

CollieDatasetForGeneration

用于测试LLM执行生成任务效果的CollieDataset,输入一种形式,输出一种形式

CollieDatasetForClassfication

用于测试LLM执行分类任务效果的CollieDataset,输入一种形式,输出两种形式

    在tutorial-1中,关于Dataset的使用如下所示。这里额外添加一行代码,表示CoLLiEDataset允许通过切片的方式进行数据集切分,切分得到的评测数据集可以传入Evaluator中,用于训练过程的评测。

tokenizer = LlamaTokenizer.from_pretrained(model_name_or_path, padding_side="left")

data = [{
        "intput": sample["prompt"].split("### Response:\n")[0] + "### Response:\n",
        "output": sample["prompt"].split("### Response:\n")[1] + tokenizer.eos_token
    } for sample in load_dataset("crumb/stanford_alpaca_full_prompts")["train"]]
train_dataset = CollieDatasetForTraining(data, tokenizer=tokenizer, max_length=2048)
eval_dataset = train_dataset[:32]

3.3   CoLLiE 的 Evaluator 和 Metric

    本节将介绍 CoLLiE 中的 评测指标 Metric、评测模块 Evaluator,以及至关重要的,CoLLiE 中 数据集 Dataset、评测指标 Metric、评测模块 Evaluator 之间紧密的对应关系。在CoLLiE中,不同的Evaluator对应不同类型的评测任务,继而匹配不同的Dataset和Metric,如下表所示。Evaluator的eval()对给定的Dataset进行迭代,每笔数据以字典形式通过eval_fn()喂给模型,包含“input_ids”等内容,接着Evaluator又根据模型输出的“logits”求得真正的预测结果“pred”,输入给对应的Metric进行指标计算。

名称

描述

对应

描述

Evaluator

所有Evaluator的基类

BaseMetric

所有Metric的基类

EvaluatorForPerplexity

评测LLM语言建模困惑度

PPLMetric

收集LLM语言建模困惑度

EvaluatorForClassfication

评测LLM执行分类任务

AccuracyMetric

计算LLM分类的正确率

ClassifyFPreRecMetric

计算LLM分类的查准查全率和F值

EvaluatorForGeneration

评测LLM执行生成任务

DecodeMetric

直接输出解码结果

BleuMetric

计算LLM生成的BLEU值

RougeMetric

计算LLM生成的Rouge值

    EvaluatorForClassification 针对分类任务对应数据集 CollieDatasetForClassfication。需要注意的是,虽然 CollieDatasetForClassfication 有两种输出,但是却在 EvaluatorForClassification 内部得到统一:即对于harness形式的处理,选择困惑度最小的候选答案序号作为“pred”,对于helm形式的处理,选择严格匹配的候选答案序号作为“pred”,如果预测答案不在候选答案列表中则“pred”设为-1。与模型预测结果“pred”相对的是数据dict中保存正确答案的字段“target”,Metric通过输入“pred”和“target”计算最终评测结果。EvaluatorForClassification 对应评测指标 AccuracyMetric和ClassifyFPreRecMetric,其含义无需赘述。

6ee4c86c21ec46709519df935b8cded4

    EvaluatorForGeneration 针对生成任务对应数据集 CollieDatasetForGeneration,通过对模型输出“logits”在词表维度上取最大值得到“pred”;对应评测指标 包括 DecodeMetric、BleuMetric、RougeMetric。DecodeMetric只需要输入“pred”即可,直接解码打印模型输出结果,或将之保存至指定文件。BleuMetric和RougeMetric则是NLP中根据单词匹配结果判断生成文本质量的评测指标,其含义在此不多赘述。

76d9442dcad34ad8a263317fca3789c7

    EvaluatorForPerplexity只与数据集CollieDatasetForPerplexity和评测指标PPLMetirc相对应,计算模型语言建模困惑度。这里有两点需要注意:一方面,对于指令微调数据,计算困惑度不考虑其指令部分,即 CollieDatasetForPerplexity第二种输入的“input”字段;另一方面,EvaluatorForPerplexity 在内部就已经根据“logits”和“target”计算完成困惑度,PPLMetric仅作为“ppl”的收集模块,无需接收“pred”参数。

289ad9d4c14548389bba21df4169af5e

    我们在这里给出在tutorial-1中省略的有关评测部分的示例代码。需要注意的是,初始化Evaluator时需要传入 模型model、配置类config(config无处不在)、评测数据集 dataset、监控器 monitor、对应具体指标字典 metrics;对于EvaluatorForGeneration,还需要传入tokenizer。对于评测指标字典 metrics,同一个Evaluator可以传入多个对应的Metric,只有传入metrics,evaluator才能够进行对应的评测;传入metrics时,可以通过字典的字段名称区分评测过程中打印的评测结果。

from collie import PPLMetric, DecodeMetric
from collie import EvaluatorForPerplexity, EvaluatorForGeneration

evaluator_ppl = EvaluatorForPerplexity(
    model=model, config=config, dataset=eval_dataset,
    monitors=[EvalMonitor(config), ], metrics={'ppl': PPLMetric(), }
)

evaluator_decode = EvaluatorForGeneration(
    model=model, config=config, tokenizer=tokenizer, dataset=eval_dataset,
    monitors=[EvalMonitor(config), ], metrics={'decode': DecodeMetric(), }
)

evaluator 既可以传入 trainer 来在训练中评测,也可以单独使用:

evaluator_ppl.eval()
evaluator_decode.eval()

3.4   CoLLiE 的 Trainer 模块

在上文介绍的Config、Evaluator等的基础上,我们可以完善tutorial-1中,trainer的示例代码;同时这里也将默认的loss,即经过GPTLMLoss封装的torch.nn.CrossEntropyLoss,呈现了出来。Trainer初始化之后,通过trainer.train()就可以实现这个训练,通过trainer.save_model()就可以把模型的权重等内容保存到对应文件夹目录下,参数权重保存在pytorch_model.bin中,兼容其他框架,可以直接用huggingface加载。

from collie import Trainer, GPTLMLoss

trainer = Trainer(
    model=model, config=config, loss_fn=GPTLMLoss(-100),
    optimizer=optimizer, lr_scheduler=lr_scheduler,
    train_dataset=train_dataset, evaluators=[evaluator_ppl, evaluator_decode],
    monitors=[LRMonitor(config), LossMonitor(config)],
)

trainer.train()

trainer.save_model("./result")

    除了上述应用到的初始化参数,其他较为重要的Trainer初始化参数如下表所示。除了model、config,其他的参数CoLLiE都允许默认为None;例如optimizer、 lr_scheduler,这些都可以在Trainer中设为None,并在配置类config中的“ds_config”进行设置,书写格式同deepspeed使用。此外,此处的optimizer可以使用pytorch定义的经典优化器,如Adam,也可以使用CoLLiE自定义的高效优化器,如AdaLomo,详见 tutorial-5

名称

描述

model

待训练模型,一般是 ColliModelForCausalLM 类型

config

训练配置参数,即初始化之后的 CollieConfig

tokenizer

模型对应分词器,若传入,要求是 PreTrainedTokenizerBase 类型

loss_fn

损失函数,默认 GPTLMLoss(),即 封装后的 nn.CrossEntropyLoss()

train_fn

训练函数,默认 None,允许传入自定义函数控制训练过程

eval_fn

评测函数,默认 None,允许传入自定义函数控制评测过程

optimizer

优化器,默认 None,CoLLiE 实现 LOMO 优化器 详见 tutorial-5

lr_scheduler

优化器控制,默认 None,参考 torch.optim.lr_scheduler

train_dataset

训练数据集,torch.utils.data.Dataset 或 CollieDatasetForTraining 类型

eval_dataset

训练数据集,torch.utils.data.Dataset 或 CollieDatasetForTraining 类型

callbacks

回调函数列表,自定义模型训练过程,详见 tutorial-4

train_dataset_collate_fn

负责训练数据padding的函数,默认 ColliePadder()

eval_dataset_collate_fn

负责训练测试padding的函数,默认 ColliePadder(padding_left=True)

data_provider

数据提供器,默认 None,BaseProvider 类型,详见 tutorial-4

monitors

监控器列表,默认 None,BaseMonitor 类型,详见 tutorial-4

metrics

评测指标字典,例如 {'acc': AccuracyMetric()}

evaluators

评测模块列表,例如 [EvaluatorForClassfication(), ]

其它版本