DeepSpeed-Chat 打造类ChatGPT全流程 笔记二之监督指令微调

系列文章

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做一个解读。

  1. 首先是头文件导入部分,除了一些常规的需要用到的包还从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_modelload_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
  1. 接下来是参数解析部分,解析如下:
# 这段代码定义了一个名为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

  1. 接下来是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
  1. 接下来是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
  1. 接下来是一个简单的打印工具函数:
# gen_output:这是一个列表,其中包含了我们希望打印的内容,每一项都是一段文本。
def print_utils(gen_output):
    # 函数会遍历gen_output列表中的每一项,然后将每一项都打印出来。为了在不同项之间增加一些可视化的分隔,函数在每一项前后都额外打印了一个空行。
    for i in range(len(gen_output)):
        print()
        print(gen_output[i])
        print()
  1. 然后是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()
  1. 解析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/dataraw_dataset.pydata_utils.py,为了搞清楚这个函数,我们需要对这两个文件做一个解析。

首先解析一下raw_dataset.py。这里先定义了一个PromptRawDataset类:

# DeepSpeed Team
from datasets import load_dataset
from torch.utils.data import Subset
import re


# 这段代码定义了一个名为PromptRawDataset的类,这个类是一个模板类,用于处理和组织模型输入数据的格式。
# 如果有新的数据集需要进行处理,可以继承这个类并实现相应的方法来确保数据的统一格式和接口。
class PromptRawDataset(
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值