文章目录
系列文章
- DeepSpeed-Chat 打造类ChatGPT全流程 笔记一
- 【DeepSpeed 教程翻译】三,在 DeepSpeed中使用 PyTorch Profiler和Flops Profiler
- DeepSpeed结合Megatron-LM训练GPT2模型笔记(上)
- 【DeepSpeed 教程翻译】二,Megatron-LM GPT2,Zero 和 ZeRO-Offload
- 【DeepSpeed 教程翻译】开始,安装细节和CIFAR-10 Tutorial
0x0. 前言
在 DeepSpeed-Chat 打造类ChatGPT全流程 笔记一 中跑通了DeepSpeed Chat的训练和推理流程,DeepSpeed Chat的训练流程包含监督指令微调(SFT),Reward模型微调,基于人类反馈的强化学习(RLHF)三个步骤。接着上面文章的todo,这篇文章主要是解析一下监督指令微调(SFT)阶段的代码实现。
0x1. 🐕 Supervised finetuning (SFT) 教程翻译
监督微调(SFT)与在自然语言任务(例如,WikiText-103)上的标准语言模型微调非常相似。主要的区别来自于数据集资源,SFT将收集高质量的查询-回答对来微调模型以达到人类更倾向的生成结果。
🏃 如何训练模型
我们提供了多个脚本用于在单个GPU(例如,单个A6000-48G,V100-32G,A100-40G等),单节点(例如,8/16x V100-32G,8 A100-40G/80G)和多节点设置(例如,64x A100-80G)上进行训练,这些可以在 training_scripts 目录中找到。例如,如果你有一个单独的A6000-48G,你可以简单地运行对应的脚本
training_scripts/single_gpu/run_1.3b.sh
来训练一个OPT-1.3b模型。我们的单节点脚本很容易扩展到多节点系统。
🏃 如何对SFT checkpoint进行评测?
一旦你使用上述代码完成训练,你可以简单地执行 bash evaluation_scripts/run_prompt.sh
它会要求用户提供两个模型的路径:(a) 原始预训练模型(即 --model_name_or_path_baseline facebook/opt-1.3b)和 (b) 微调后的模型(即 --model_name_or_path_finetune output/check_base)。“prompt_eval.py” 包含了几个可以根据你的喜好进行更新的提示。
💁 模型和数据
由于GPT3没有开源的checkpoint,我们使用了Meta OPT家族的预训练模型(即facebook/opt-1.3b)。你也可以使用其他预训练模型(如GPT-Neo,Bloom等)。至于数据集,我们也使用了来自Huggingface数据集的开源数据集,具体如下:
Dahoas/rm-static
Dahoas/full-hh-rlhf
Dahoas/synthetic-instruct-gptj-pairwise
yitingxie/rlhf-reward-datasets
openai/webgpt_comparisons
stanfordnlp/SHP
感谢DeepSpeed RLHF的数据抽象和融合技术,我们现在可以将多个数据源合并用于训练。然而,重要的是要注意,不同的数据集可能使用不同的提示词(例如,Dohas/rm-static使用"Human:"表示查询,"Assistant:"表示回答)。因此,用户必须自行对齐这些提示。在我们的例子中,我们一致使用了Dohas/rm-static的格式。通过我们的评估,我们发现整合多样化的数据集可以提高模型的质量。请参考下一节以获取不同查询-答案对的示例。
☀️来自OPT-1.3B及其SFT变体(使用不同微调数据)的提示示例

☀️ 一些参数解释和可训练的最大模型
main.py文件中使用的大多数参数都有清晰的解释,如果你有解码器模型微调的经验,通常很容易理解。然而,如果你对其中任何一个不清楚,请不要犹豫在GitHub问题上向我们求助。在这一部分,我们提供了一些具体的参数解释和它们的使用方法。
| 参数 | 解释 | 注意事项 |
|---|---|---|
| –data_path | 用于微调模型的数据 | 你可以指定多个数据资源来训练模型,例如:Dahoas/rm-static Dahoas/full-hh-rlhf |
| –data_split | 为三步训练切分数据 | 根据InstructGPT,我们提供了切分数据集的能力,使得每个分区只在一个步骤中使用。设置为"2,4,4"意味着我们分别使用20%,40%,40%的数据在每个步骤中。如果你只做SFT,或者你发现在不同步骤中使用重叠数据是可以的/有帮助的,你可以将它改为"10,0,0"。 |
| –sft_only_data_path | 用于微调模型的单响应数据 | 对于只在步骤1中使用的单响应数据,你应该将它们作为这个参数的一部分,而不是上面的data_path参数。这个参数中的数据集将不会被切分,而只在步骤1中全面使用。 |
| –gradient_checkpoint | 为模型启用梯度检查点(也称为激活检查点) | 这可以显著降低训练内存成本 |
| –offload | DeepSpeed特定功能。将模型卸载到CPT/NVME以节省内存 | 这可以在内存消耗较少的情况下训练更大的模型。但是它会减慢训练的速度。 |
| –zero_stage | DeepSpeed特定功能,适用于多GPU系统 | 这可以帮助将模型/优化器分布在多个GPU上。请参见https://www.deepspeed.ai/tutorials/zero/ |
| –lora_dim | 当它大于0时,将启用LoRA | 通常,LoRA需要更大的学习率才能更好地收敛 |
| –lora_module_name | 启用LoRA模块的范围。 | |
| –only_optimize_lora | 冻结所有其他参数,只优化LoRA相关参数 | |
| –gradient_checkpoint, --lora_dim, only_optimize_lora | 当启用LoRA和梯度检查点时,不能启用只优化LoRA | 如果全部启用,将影响梯度流(也就是由PyTorch支持的auto-grad系统后端) |
对于用户来说,一个重要的考虑是确定他们可以使用当前系统训练的最大模型大小。在这里,我们提供了一个估计这个限制的方法。假设你不使用卸载功能,并启用(i)零阶段3(如果使用多个GPU),(ii)梯度检查点,以及(iii)LoRA,那么你可以训练的大致最大模型大小(以十亿参数为单位)可以估计为"总GPU内存(GB)除以3"。例如,如果你有一台单一的A6000-48G GPU,你可能可以训练最多16十亿参数的模型。需要注意的是,这只是一个粗略的估计,你应该自己验证。
👀 其它
从InstructGPT的工作中,我们建议为了得到更好的人类偏好的答案,让模型过度拟合(即更长的训练周期)。通过我们的探索,我们发现这对于较小模型的微调,如OPT-1.3B,特别有帮助。值得注意的是,我们在脚本中提供的超参数并没有经过大量的调整。因此,我们鼓励用户和实践者自己找到最优的配置。此外,我们的系统可以很容易地扩展到其他语言,如中文和日语。为了证明这一点,我们在"training_scripts/other_language"目录下包含了两个例子。
0x2. 评测脚本解读
DeepSpeedExamples/applications/DeepSpeed-Chat/training/step1_supervised_finetuning/evaluation_scripts/run_prompt.sh这个脚本是对输入的模型进行评测,我们看一下内容:
# You can provide two models to compare the performance of the baseline and the finetuned model
export CUDA_VISIBLE_DEVICES=0
python prompt_eval.py \
--model_name_or_path_baseline XXX \
--model_name_or_path_finetune XXX
这表示我们可以调用prompt_eval.py来对baseline模型和finetune模型进行对比评测。所以评测的实现就是这里的prompt_eval.py,接下来对prompt_eval.py做一个解读。
- 首先是头文件导入部分,除了一些常规的需要用到的包还从DeepSpeed Chat的utils下面导入了2个函数。
# DeepSpeed Team
import argparse
import logging
import torch
import sys
import os
from transformers import (
AutoModelForCausalLM, )
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
from utils.model.model_utils import create_hf_model # 从utils下面导入自定义函数
from utils.utils import load_hf_tokenizer
logger = logging.getLogger(__name__)
接下来解析一下create_hf_model和load_hf_tokenizer两个函数的实现。首先是create_hf_model函数:
# 这段代码定义了一个名为create_hf_model的函数,该函数的作用是创建或加载一个预训练模型。该函数的主要参数包括:
# model_class:模型的类别,例如GPT-2、BERT等。
# tokenizer:用于模型的分词器。
# ds_config: DeepSpeed的配置参数。
# rlhf_training:一个标志,用来表示是否正在进行RLHF(Reinforcement Learning from Human Feedback)训练。
# disable_dropout:一个标志,用来表示是否禁用dropout。Dropout是一种防止过拟合的技术。
def create_hf_model(model_class,
model_name_or_path,
tokenizer,
ds_config=None,
rlhf_training=False,
disable_dropout=False):
# 根据model_name_or_path从预训练模型获取模型配置model_config。
model_config = AutoConfig.from_pretrained(model_name_or_path)
# 如果disable_dropout为真,则将模型配置中的dropout设为0.0。
if disable_dropout:
model_config.dropout = 0.0
# Note: dschf is defined in function scope to avoid global effects
# https://huggingface.co/docs/transformers/main_classes/deepspeed#nontrainer-deepspeed-integration
# 根据ds_config中的设置,创建DeepSpeed的配置对象dschf,以便进行DeepSpeed优化。
if ds_config is not None and ds_config["zero_optimization"]["stage"] == 3:
dschf = HfDeepSpeedConfig(ds_config)
else:
dschf = None
# 根据rlhf_training的值,确定是从配置中创建模型还是从预训练模型中加载模型。如果rlhf_training为真,则根据模型配置创建模型;否则,从预训练模型加载模型。
if rlhf_training:
# the weight loading is handled by create critic model
model = model_class.from_config(model_config)
else:
model = model_class.from_pretrained(
model_name_or_path,
from_tf=bool(".ckpt" in model_name_or_path),
config=model_config)
# 将模型的结束标记和填充标记设为分词器的结束标记id。
model.config.end_token_id = tokenizer.eos_token_id
model.config.pad_token_id = model.config.eos_token_id
# 调整模型的词汇表大小,使其为8的倍数。这样做的目的是为了在某些硬件(如GPU)上提高效率。
model.resize_token_embeddings(int(
8 *
math.ceil(len(tokenizer) / 8.0))) # make the vocab size multiple of 8
return model
然后是load_hf_tokenizer函数:
# 这段代码定义了一个名为load_hf_tokenizer的函数,该函数的作用是加载一个指定的分词器。这个函数的参数有:
# model_name_or_path:模型的名称或者预训练模型的路径。
# fast_tokenizer:是否使用Fast Tokenizer,这是一种更快的分词器实现。
def load_hf_tokenizer(model_name_or_path, fast_tokenizer=True):
# 检查model_name_or_path是否存在。如果存在,可能意味着这是一个本地的模型路径。
if os.path.exists(model_name_or_path):
# Locally tokenizer loading has some issue, so we need to force download
# 如果是本地模型,再检查在该路径下的"config.json"文件是否存在。如果存在,打开该文件,并从文件中读取"_name_or_path"字段,将其赋值给model_name,然后通过AutoTokenizer.from_pretrained函数,使用该模型名称和fast_tokenizer参数,从HuggingFace模型库下载并加载分词器。
model_json = os.path.join(model_name_or_path, "config.json")
if os.path.exists(model_json):
model_json_file = json.load(open(model_json))
model_name = model_json_file["_name_or_path"]
tokenizer = AutoTokenizer.from_pretrained(model_name,
fast_tokenizer=True)
else:
# 如果model_name_or_path不存在,直接使用AutoTokenizer.from_pretrained函数,使用model_name_or_path和fast_tokenizer参数,从HuggingFace模型库下载并加载分词器。
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path,
fast_tokenizer=True)
return tokenizer
- 接下来是参数解析部分,解析如下:
# 这段代码定义了一个名为parse_args的函数,该函数的作用是解析命令行参数。它使用Python的argparse库来完成这个工作。下面是每个参数的详解:
def parse_args():
parser = argparse.ArgumentParser(description="Eval the finetued SFT model")
# model_name_or_path_baseline:基线模型的路径,这是一个必须提供的参数(required=True)。
parser.add_argument(
"--model_name_or_path_baseline",
type=str,
help="Path to baseline model",
required=True,
)
# model_name_or_path_finetune:微调后模型的路径,这也是一个必须提供的参数。
parser.add_argument(
"--model_name_or_path_finetune",
type=str,
help="Path to pretrained model",
required=True,
)
# num_beams:用于指定集束搜索的集束宽度,其默认值为1。
parser.add_argument(
"--num_beams",
type=int,
default=1,
help='Specify num of beams',
)
# num_beam_groups:用于指定集束搜索的组数,其默认值为1。
parser.add_argument(
"--num_beam_groups",
type=int,
default=1,
help='Specify num of beams',
)
# top_k:用于指定在Top-K采样中考虑的最高可能性词汇的数量,其默认值为4。
parser.add_argument(
"--top_k",
type=int,
default=4,
help='Specify num of beams',
)
# penalty_alpha:惩罚因子,其默认值为0.6。
parser.add_argument(
"--penalty_alpha",
type=float,
default=0.6,
help='Specify num of beams',
)
# num_return_sequences:生成序列的数量,其默认值为1。
parser.add_argument(
"--num_return_sequences",
type=int,
default=1,
help='Specify num of return sequences',
)
# max_new_tokens:生成的最大新token数,其默认值为100。
parser.add_argument(
"--max_new_tokens",
type=int,
default=100,
help='Specify num of return sequences',
)
# language:语言类型,可以是"English"、"Chinese"或"Japanese",默认为"English"。
parser.add_argument("--language",
type=str,
default="English",
choices=["English", "Chinese", "Japanese"])
# parser.parse_args()这个函数将解析命令行参数,并将结果保存在一个Namespace对象中。这个对象被返回,可以在其他地方使用这些参数。
args = parser.parse_args()
return args
- 接下来是
generate函数的解析:
# 这个函数是用来利用训练好的模型生成文本的,它接受以下参数
# model:已经训练好的模型。
# tokenizer:用于将文本转换为模型可理解的输入的工具。
# inputs:模型的输入数据。
# num_beams:在使用束搜索算法时的束宽,其默认值为1。
# num_beam_groups:在使用分组束搜索时的组数,默认为1。
# do_sample:是否进行随机采样。如果设为True,则在生成过程中会随机选择下一个单词,而不是仅选择最可能的单词。默认为False。
# num_return_sequences:模型返回的序列数,默认为1。
# max_new_tokens:模型生成的最大新token数,即最大生成文本的长度,默认为100。
def generate(model,
tokenizer,
inputs,
num_beams=1,
num_beam_groups=1,
do_sample=False,
num_return_sequences=1,
max_new_tokens=100):
# 函数首先使用模型的generate方法,根据提供的参数生成文本。
generate_ids = model.generate(inputs.input_ids,
num_beams=num_beams,
num_beam_groups=num_beam_groups,
do_sample=do_sample,
num_return_sequences=num_return_sequences,
max_new_tokens=max_new_tokens)
# 使用tokenizer的batch_decode方法将生成的令牌ID解码为可读的文本。注意,这里跳过了特殊的令牌(如填充和开始/结束令牌),并且不会清理tokenize产生的额外空格。
result = tokenizer.batch_decode(generate_ids,
skip_special_tokens=True,
clean_up_tokenization_spaces=False)
return result
- 接下来是
generate_constrastive_search函数的解析:
# 这个函数叫做 generate_constrastive_search,它是用于利用训练好的模型进行对比搜索生成文本的。这个函数接受以下参数:
# model:已经训练好的模型。
# tokenizer:用于将文本转换为模型可理解的输入的工具。
# inputs:模型的输入数据。
# top_k:在每一步生成时,只考虑概率最高的top_k个候选项,然后进行随机抽样。默认为4。
# penalty_alpha:用于惩罚新生成的token与原始输入之间的差异,默认为0.6。
# num_return_sequences:模型返回的序列数,默认为1。
# max_new_tokens:模型生成的最大新token数,即最大生成文本的长度,默认为100。
def generate_constrastive_search(model,
tokenizer,
inputs,
top_k=4,
penalty_alpha=0.6,
num_return_sequences=1,
max_new_tokens=100):
# 函数首先使用模型的generate方法,根据提供的参数生成文本。注意这里使用了模型的一个特殊的生成方式,这种方式在每一步生成时,只考虑概率最高的top_k个候选项,然后进行随机抽样,同时使用了一个惩罚因子penalty_alpha来惩罚新生成的token与原始输入之间的差异。
generate_ids = model.generate(inputs.input_ids,
top_k=top_k,
penalty_alpha=penalty_alpha,
num_return_sequences=num_return_sequences,
max_new_tokens=max_new_tokens)
# 然后,使用tokenizer的batch_decode方法将生成的token ID解码为可读的文本。注意,这里跳过了特殊的token(如填充和开始/结束token),并且不会清理token化产生的额外空格。
result = tokenizer.batch_decode(generate_ids,
skip_special_tokens=True,
clean_up_tokenization_spaces=False)
return result
- 接下来是一个简单的打印工具函数:
# gen_output:这是一个列表,其中包含了我们希望打印的内容,每一项都是一段文本。
def print_utils(gen_output):
# 函数会遍历gen_output列表中的每一项,然后将每一项都打印出来。为了在不同项之间增加一些可视化的分隔,函数在每一项前后都额外打印了一个空行。
for i in range(len(gen_output)):
print()
print(gen_output[i])
print()
- 然后是
prompt_eval这个函数,这个函数prompt_eval的目的是评估和比较基线模型(model_baseline)和微调过的模型(model_fintuned)对于一组提示(prompts)的生成性能。让我们逐行进行解析:
# 输入参数包括:args(命令行参数)、model_baseline(基线模型)、model_fintuned(微调模型)、tokenizer(用于编码和解码的分词器)、device(指定运行模型的设备)、prompts(一组要评估的提示)。
def prompt_eval(args, model_baseline, model_fintuned, tokenizer, device,
prompts):
# 对于prompts中的每一个提示,我们都做以下操作:
for prompt in prompts:
# 使用分词器将提示转换为模型所需的输入格式,并将其移至指定的设备上。
inputs = tokenizer(prompt, return_tensors="pt").to(device)
# 打印一条消息表示我们现在正在进行基线模型的生成。
print("==========Baseline: Greedy=========")
# 然后,我们调用之前定义的generate函数使用贪婪搜索方法生成文本,并使用print_utils函数打印生成的结果。
r_base = generate(model_baseline,
tokenizer,
inputs,
num_beams=1,
num_return_sequences=args.num_return_sequences,
max_new_tokens=args.max_new_tokens)
print_utils(r_base)
# 打印一条消息表示我们现在正在进行微调模型的生成。
print("==========finetune: Greedy=========")
# 同样地,我们调用generate函数使用贪婪搜索方法生成文本,并使用print_utils函数打印生成的结果。
r_finetune_g = generate(model_fintuned,
tokenizer,
inputs,
num_beams=1,
num_return_sequences=args.num_return_sequences,
max_new_tokens=args.max_new_tokens)
print_utils(r_finetune_g)
# 注意:在此函数中,贪婪搜索被用作基线方法。然而,该函数还提供了其他几种搜索策略的例子,包括多项式采样、束搜索、束搜索多项式采样、多样性束搜索和对比搜索。这些策略在此函数中都被注释掉了,但你可以根据需要去掉注释,使用这些策略。
# print("==========finetune: Multinomial sampling=========")
# r_finetune_m = generate(model_fintuned, tokenizer, inputs,
# num_beams=1,
# do_sample=True,
# num_return_sequences=args.num_return_sequences,
# max_new_tokens=args.max_new_tokens)
# print_utils(r_finetune_m)
# print("==========finetune: Beam Search=========")
# r_finetune_b = generate(model_fintuned, tokenizer, inputs,
# num_beams=args.num_beams,
# num_return_sequences=args.num_return_sequences,
# max_new_tokens=args.max_new_tokens)
# print_utils(r_finetune_b)
# print("==========finetune: Beam-search multinomial sampling=========")
# r_finetune_s = generate(model_fintuned, tokenizer, inputs,
# num_beams=args.num_beams,
# do_sample=True,
# num_return_sequences=args.num_return_sequences,
# max_new_tokens=args.max_new_tokens)
# print_utils(r_finetune_s)
# print("==========finetune: Diverse Beam Search=========")
# r_finetune_d = generate(model_fintuned, tokenizer, inputs,
# num_beams=args.num_beams,
# num_beam_groups=args.num_beam_groups,
# num_return_sequences=args.num_return_sequences,
# max_new_tokens=args.max_new_tokens)
# print_utils(r_finetune_d)
# print("==========finetune: Constrastive Search=========")
# r_finetune_c = generate_constrastive_search(model_fintuned, tokenizer, inputs,
# top_k=args.top_k,
# penalty_alpha=args.penalty_alpha,
# num_return_sequences=args.num_return_sequences,
# max_new_tokens=args.max_new_tokens)
# print_utils(r_finetune_c)
# 最后,打印一条消息表示这个提示的处理已经结束。然后打印两个空行作为分隔。
print("====================prompt end=============================")
print()
print()
- 解析main函数:
# main函数负责解析命令行参数、准备模型和分词器、定义提示,然后使用这些来评估和比较基线模型和微调模型。
def main():
# 这个main函数是整个脚本的入口点。它首先通过parse_args函数解析命令行参数。然后它设置了运行模型的设备为第一个GPU。
args = parse_args()
device = torch.device("cuda:0")
# 接着,它使用load_hf_tokenizer函数加载分词器,然后使用create_hf_model函数创建基线模型(model_baseline)和微调模型(model_fintuned)
tokenizer = load_hf_tokenizer(args.model_name_or_path_baseline,
fast_tokenizer=True)
model_baseline = create_hf_model(AutoModelForCausalLM,
args.model_name_or_path_baseline,
tokenizer, None)
model_fintuned = create_hf_model(AutoModelForCausalLM,
args.model_name_or_path_finetune,
tokenizer, None)
# 然后,这些模型被移动到指定的设备上。
model_baseline.to(device)
model_fintuned.to(device)
# 在接下来的部分,函数定义了一组用于评估的提示。注意,这里特别指出,如果提示以空格结束,那么没有经过微调的原始模型有可能会陷入停滞并无法产生响应。微调过的模型在这方面表现得更好。因此,这里所有的提示都以冒号":"结束,以使得比较更有意义。
# 这个脚本支持英文、中文和日文的评估,它通过args.language参数判断用户选择的语言,并根据此选择加载对应的提示。
if args.language == "English":
prompts = [
"Human: Please tell me about Microsoft in a few sentence? Assistant:",
"Human: Explain the moon landing to a 6 year old in a few sentences. Assistant:",
"Human: Write a short poem about a wise frog. Assistant:",
"Human: Who was president of the United States in 1955? Assistant:",
"Human: How does a telescope work? Assistant:",
"Human: Why do birds migrate south for the winter? Assistant:"
]
elif args.language == "Chinese":
prompts = [
"Human: 请用几句话介绍一下微软? Assistant:",
"Human: 用几句话向6岁的孩子解释登月。 Assistant:",
"Human: 写一首关于一只聪明的青蛙的短诗。 Assistant:",
"Human: 谁是1955年的美国总统? Assistant:", "Human: 望远镜是如何工作的? Assistant:",
"Human: 鸟类为什么要南迁过冬? Assistant:"
]
elif args.language == "Japanese":
prompts = [
"Human: マイクロソフトについて簡単に教えてください。 Assistant:",
"Human: 6歳児に月面着陸を短い文で説明する。 Assistant:",
"Human: 賢いカエルについて短い詩を書いてください。 Assistant:",
"Human: 1955年のアメリカ合衆国大統領は誰? Assistant:",
"Human: 望遠鏡はどのように機能しますか? Assistant:",
"Human: 鳥が冬に南に移動するのはなぜですか? Assistant:"
]
# 最后,它调用prompt_eval函数,将所有的参数和提示传递给它,以进行模型评估。
prompt_eval(args, model_baseline, model_fintuned, tokenizer, device,
prompts)
0x3. 训练脚本解读
训练过程都集中在DeepSpeedExamples/applications/DeepSpeed-Chat/training/step1_supervised_finetuning/main.py这个脚本,我们逐行解析一下这个脚本。
0x3.1 头文件相关解析
# DeepSpeed Team
# 首先,它导入了Python的标准库,如argparse(用于解析命令行参数),os和math。
# 然后,它导入了PyTorch库,这是一个用于深度学习的开源库,同时也导入了一些PyTorch的辅助模块,如DataLoader(用于加载数据)、
# RandomSampler和SequentialSampler(用于数据抽样)以及DistributedSampler(用于在分布式设置中进行数据抽样)。
import argparse
import os
import math
import sys
import torch
from torch.utils.data import DataLoader, RandomSampler, SequentialSampler
from torch.utils.data.distributed import DistributedSampler
# 接下来,它导入了Hugging Face的transformers库的一些模块,包括用于因果语言建模的模型(AutoModelForCausalLM),优化器调度类型(SchedulerType),默认的数据整理函数(default_data_collator)和获取优化器调度器的函数(get_scheduler)。
from transformers import (
AutoModelForCausalLM,
SchedulerType,
default_data_collator,
get_scheduler,
)
# 然后,它导入了deepspeed库,这是一个为大规模模型训练优化的库。它也导入了deepspeed库中的一些模块,包括优化器类(DeepSpeedCPUAdam和FusedAdam)
import deepspeed
from deepspeed.ops.adam import DeepSpeedCPUAdam, FusedAdam
# 之后,它将当前脚本的父目录添加到系统路径中,以便可以从该目录下的utils目录导入一些自定义函数和模块。
sys.path.append(
os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
# 最后,它从utils目录中导入了一些自定义模块和函数,包括数据处理函数(create_prompt_dataset),打印和设备转换函数(print_rank_0和to_device),
# 模型保存函数(save_hf_format),随机种子设置函数(set_random_seed),求平均函数(get_all_reduce_mean),
# 获取优化器参数组的函数(get_optimizer_grouped_parameters),保存和加载模型的函数(save_zero_three_model和load_hf_tokenizer),
# 以及创建模型和处理模型的函数(create_hf_model)。这些函数在脚本中的后续部分都将被使用。
from utils.data.data_utils import create_prompt_dataset
from utils.utils import print_rank_0, to_device, save_hf_format, set_random_seed, get_all_reduce_mean, get_optimizer_grouped_parameters, save_zero_three_model, load_hf_tokenizer
from utils.ds_utils import get_train_ds_config
from utils.module.lora import convert_linear_layer_to_lora, convert_lora_to_linear_layer, only_optimize_lora_parameters
from utils.model.model_utils import create_hf_model
create_prompt_dataset解析
create_prompt_dataset这个函数实际上直接或者间接的用到了utils/data中raw_dataset.py和data_utils.py,为了搞清楚这个函数,我们需要对这两个文件做一个解析。
首先解析一下raw_dataset.py。这里先定义了一个PromptRawDataset类:
# DeepSpeed Team
from datasets import load_dataset
from torch.utils.data import Subset
import re
# 这段代码定义了一个名为PromptRawDataset的类,这个类是一个模板类,用于处理和组织模型输入数据的格式。
# 如果有新的数据集需要进行处理,可以继承这个类并实现相应的方法来确保数据的统一格式和接口。
class PromptRawDataset(

最低0.47元/天 解锁文章
2万+

被折叠的 条评论
为什么被折叠?



