跳到主要内容
大模型微调实战:LLaMA-Factory 源码解析与部署 | 极客日志
Python AI 算法
大模型微调实战:LLaMA-Factory 源码解析与部署 深入解析 LLaMA-Factory 大模型微调框架,涵盖预训练(PT)、指令微调(SFT)及强化学习(RLHF)阶段。介绍 Transformers 与 PEFT 库基础,分析源码中 tokenizer、dataset、model 加载及 Trainer 流程。通过 Qwen1.5-0.5B 模型实战演示数据集准备、命令配置、损失监控及评估指标解读,提供从理论到落地的完整微调指南。
晚风叙旧 发布于 2025/2/6 更新于 2026/6/2 34 浏览什么是 LLaMA-Factory?
LLaMA-Factory 是一个在 GitHub 上开源的,专为大模型训练设计的平台。项目提供中文说明,可以参考官方文档。
为什么要学习 LLaMA-Factory?
大模型技术发展到现在,企业想要真正利用大模型做些事情,一定需要懂得大模型微调的过程。注意,这里说的是过程,而不是原理,专业技术人员才需要懂原理,普通用户只要懂过程就可以完成对大模型的微调。
对于有微调大模型需求,却对大模型微调完全是一个门外汉的用户来说,通过学习 LLaMA-Factory 后,可以快速的训练出自己需要的模型。
对于想要了解微调大模型技术的技术人员,通过学习 LLaMA-Factory 后也能快速理解模型微调的相关概念。
所以,我认为 LLaMA-Factory 是走向大模型微调的一条捷径。
如何学习?
如果你只想了解如何利用 LLaMA-Factory 进行模型的微调,直接通过官方文档即可完成。无需阅读本文。
如果你想对大模型微调技术本身感兴趣,想要深入了解,可以继续阅读本专栏,笔者将通过阅读源码的方式,对大模型微调技术进行深入剖析,看到哪里,遇到不懂的概念再去理解,最终展现出大模型训练的全貌。
理解了微调技术后,再通过使用 LLaMA-Factory 进行模型的微调实践,即可掌握大模型微调技术。
基础知识
阅读源码之前,我们需要对模型微调相关概念有一定的认识,来协助我们理解源码。
模型训练阶段
在理解模型微调概念之前,我们先来理解大模型训练阶段有哪些。
Pre-Training
Pre-Training:预训练阶段。
这个阶段是用来训练基础模型的,是最消耗算力的阶段,也是大模型诞生的起始阶段。
Supervised Finetuning (SFT)
sft:指令微调/监督微调阶段
和预训练阶段相比,这个阶段最大的变化就是训练数据由"量多质低"变为"量少质高",训练数据主要由人工进行筛选或生成。这个阶段完成后其实已经能获得一个可以上线的大模型了
RLHF
RLHF:基于人类反馈的强化学习(Reinforcement Learning from Human Feedback,RLHF)
可以分成两个环节
奖励建模阶段(Reward Modeling)
在这一阶段,模型学习和输出的内容发生了根本性的改变。前面的两个阶段,预训练和微调,模型的输出是符合预期的文本内容;**奖励建模阶段的输出不仅包含预测内容,还包含奖励值或者说评分值,数值越高,意味着模型的预测结果越好。**这个阶段输出的评分,并不是给最终的用户,而是在强化学习阶段发挥重大作用。
强化学习阶段(Reinforcement Learning)
这个阶段非常'聪明'的整合了前面的成果:
针对特定的输入文本,通过 SFT 模型获得多个输出文本。
基于 RM 模型对多个输出文本的质量进行打分,这个打分实际上已经符合人类的期望了。
基于这个打分,为多个输出文本结果加入权重。这个权重其实会体现在每个输出 Token 中。
将加权结果反向传播,对 SFT 模型参数进行调整,就是所谓的强化学习。
常见的强化学习策略包括PPO 与DPO ,它们的细节我们不去研究,只要知道 DPO 主要用于分布式训练,适合大规模并行处理的场景,PPO 通常指的是单机上的算法就可以了。
模型训练模式
了解了模型训练阶段后,现在有个问题,我们应该在哪个阶段进行微调训练呢?
通常会有以下训练模式进行选择,根据领域任务、领域样本情况、业务的需求我们可以选择合适的训练模式。
模式一:基于 base 模型 + 领域任务的 SFT;
模式二:基于 base 模型 + 领域数据 continue pre-train + 领域任务 SFT;
模式三:基于 base 模型 + 领域数据 continue pre-train + 通用任务 SFT+ 领域任务 SFT;
模式四:基于 base 模型 + 领域数据 continue pre-train + 通用任务与领域任务混合 SFT;
模式五:基于 base 模型 + 领域数据 continue pre-train(混入 SFT 数据 + 通用任务与领域任务混合 SFT;
模式六:基于 chat 模型 + 领域任务 SFT;
模式七:基于 chat 模型 + 领域数据 continue pre-train + 领域任务 SFT
是否需要 continue pre-train
大模型的知识来自于 pre-train 阶段,如果你的领域任务数据集与 pre-train 的数据集差异较大,比如你的领域任务数据来自公司内部,pre-train 训练样本基本不可能覆盖到,那一定要进行 continue pre-train。
如果你的领域任务数据量较大(token 在 1B 以上),并只追求领域任务的效果,不考虑通用能力,建议进行 continue pre-train。
是选择 chat 模型 还是 base 模型 如果你有一个好的 base 模型,在 base 模型基础进行领域数据的 SFT 与在 chat 模型上进行 SFT,效果上差异不大。
基于 chat 模型进行领域 SFT,会很容导致灾难性遗忘,在进行领域任务 SFT 之后,模型通用能力会降低,如只追求领域任务的效果,则不用考虑。
如果你的领域任务与通用任务有很大的相关性,那这种二阶段 SFT 会提升你的领域任务的效果。
如果你既追求领域任务的效果,并且希望通用能力不下降,建议选择 base 模型作为基座模型。在 base 模型上进行多任务混合训练,混合训练的时候需要关注各任务间的数据配比。
其他经验
在资源允许的情况下,如只考虑领域任务效果,我会选择模式二;
在资源允许的情况下,如考虑模型综合能力,我会选择模式五;
在资源不允许的情况下,我会考虑模式六;
一般情况下,我们不用进行 RLHF 微调;
开发工具库 Transformers Transformers 是 Hugging Face 提供的 Python 库,Hugging Face 是什么这里就不介绍了。国内可以通过镜像站访问:
Transformers 库的文档地址:
我们要关注那些内容呢?下边将会列举一些关键内容,详细内容请查阅官方文档。
Pipeline Pipeline 是一个用于模型推理的工具,它与模型训练关系不大,它主要是将预训练好的模型加载,推理预测使用的,我们了解它是什么即可。
AutoClass AutoClass 是一个比较重要的角色,主要是用来加载预训练模型的,通过 from_pretrained() 方法可以加载任意 Hugging Face 中的预训练模型和本地模型。
AutoTokenizer 几乎所有的 NLP 任务都以 tokenizer 开始,用它来加载模型对应的分词器。
AutoModel 真正来加载模型实例的是 AutoModel,不同任务使用的 AutoModel 也不同,针对大语言模型一般使用 AutoModelForCausalLM。
模型量化 量化技术专注于用较少的信息表示数据,同时尽量不损失太多准确性。
Transformers 支持三种量化方法:AWQ、GPTQ、BNB。底层细节我们不必研究
GPTQ 是专为 GPT 模型设计的,AWQ 适用于多种模型和任务,包括多模态语言模型。
BNB 是将模型量化为 8 位和 4 位的最简单选择,4 位量化可以与 QLoRA 一起用于微调量化 LLM。
PEFT 库 PEFT 是 Hugging Face 提供的库,是一个为大型预训练模型提供多种高效微调方法的 python 库。
PEFT 文档地址:
PEFT 可以轻松与 Transformers 库集成,一起完成模型微调的工作。
微调方式包括 LoRA、AdaLoRA、P-tuning 等。
补充说明:QLoRA 是量化 LoRA 的缩写,需要把模型量化再进行训练,细节暂不研究。
LLaMA-Factory 源码分析
从 pt 预训练开始 首先从分析 pt 预训练过程开始研究。
根据官方文档可知,预训练执行命令如下:
CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \
--stage pt \
--do_train \
--model_name_or_path path_to_llama_model \
--dataset wiki_demo \
--finetuning_type lora \
--lora_target q_proj,v_proj \
--output_dir path_to_pt_checkpoint \
--overwrite_cache \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--lr_scheduler_type cosine \
--logging_steps 10 \
--save_steps 1000 \
--learning_rate 5e-5 \
--num_train_epochs 3.0 \
--plot_loss \
--fp16
参数说明
其中很多训练参数可能会看不懂,但没关系,先整体有个印象就行。
–stage pt:指定训练阶段为预训练
–do_train:指定是训练任务
–model_name_or_path:本地模型的文件路径或 Hugging Face 的模型标识符
–dataset:指定数据集
–finetuning_type lora:指定微调方法为 lora
–lora_target q_proj,v_proj:Lora 作用模块为 q_proj,v_proj 此参数后续详解
–output_dir: 保存训练结果的路径
–overwrite_cache: 覆盖缓存的训练集和评估集
–per_device_train_batch_size 4: 每个 gpu 的批处理大小,训练参数
–gradient_accumulation_steps 4:梯度累计的步数,训练参数
–lr_scheduler_type cosine:学习率调度器,训练参数
–logging_steps 10:每两次日志输出间的更新步数,训练参数
–save_steps 1000:每两次断点保存间的更新步数,训练参数
–learning_rate 5e-5:学习率,adamW 优化器的默认值为 5e-5,训练参数
–num_train_epochs 3.0:需要执行的训练轮数,训练参数
–plot_loss:是否保存训练损失曲线
–fp16:使用 fp16 混合精度训练,此参数后续详解
lora_target lora_target 被设置到 LoraConfig 中的 target_modules 参数中,LoraConfig 是 PEFT 库中提供的。
文档地址:
LLaMA-Factory 框架中通过 lora_target 进行了封装,说明如下:
lora_target: str = field(
default="all" ,
metadata={
"help" : """Name(s) of target modules to apply LoRA. \
Use commas to separate multiple modules. \
Use "all" to specify all the linear modules. \
LLaMA choices: ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], \
BLOOM & Falcon & ChatGLM choices: ["query_key_value", "dense", "dense_h_to_4h", "dense_4h_to_h"], \
Baichuan choices: ["W_pack", "o_proj", "gate_proj", "up_proj", "down_proj"], \
Qwen choices: ["c_attn", "attn.c_proj", "w1", "w2", "mlp.c_proj"], \
InternLM2 choices: ["wqkv", "wo", "w1", "w2", "w3"], \
Others choices: the same as LLaMA."""
},
目前细节我们还无法理解,但可以通过以上说明进行对应的设置。
注意:经调试结果观察,Qwen1.5 的 lora_target 与 LLaMA choices 相同。
混合精度训练 在深度学习中,混合精度训练是一种利用半精度浮点数(16 位)和单精度浮点数(32 位)混合计算的训练技术。传统上,神经网络训练过程中使用的是单精度浮点数,这需要更多的内存和计算资源。而混合精度训练通过将一部分计算过程转换为半精度浮点数来减少内存占用和加快计算速度。
FP16(半精度浮点数):FP16 通常用于混合精度训练,其中大多数计算操作使用 FP16 来减少内存占用和加快计算速度。
BF16(bfloat16):BF16 提供了更大的动态范围和更好的数值精度,相比于 FP16 更适合于保持梯度更新的稳定性。
FP32(单精度浮点数):传统神经网络训练中使用 FP32 来表示参数、梯度等数值,但相对于 FP16 和 BF16,它需要更多的内存和计算资源。
Pure BF16(纯 bfloat16):这个术语通常用于强调使用纯粹的 BF16 格式,而不是在混合精度环境中与其他精度混合使用。使用纯 BF16 意味着所有的计算和存储都使用 BF16 格式。
理解源码 请自行配合源码阅读以下内容,文中不会粘贴完整源码。
源码入口 根据运行命令,我们可以从 src/train_bash.py 部分看起。
会进入 src/llmtuner/train/pt/workflow.py 中的 run_pt 方法中,
run_pt 函数主要实现了以下功能:
加载 tokenizer 和 dataset:根据传入的参数,使用 load_tokenizer 函数加载 tokenizer,然后使用 get_dataset 函数根据 tokenizer、model_args、data_args 和 training_args 获取 dataset。
加载模型:使用 load_model 函数根据 tokenizer、model_args、finetuning_args 和 training_args 的 do_train 参数加载模型。
初始化 Trainer:使用 CustomTrainer 初始化一个 Trainer 实例,传入模型、参数、tokenizer、data_collator、callbacks 和其他额外的参数。data_collator 是通过调用 DataCollatorForLanguageModeling 类的实例化来创建一个数据整理器,主要用于自然语言处理任务中,将原始文本数据转换为模型可以输入的格式。
训练模型:如果 training_args.do_train 为 True,则调用 trainer 的 train 方法进行训练,根据需要恢复 checkpoint。训练完成后,保存模型、日志和状态。
评估模型:如果 training_args.do_eval 为 True,则调用 trainer 的 evaluate 方法进行评估,并计算 perplexity。然后记录和保存评估指标。
创建模型卡片:使用 create_modelcard_and_push 函数创建并推送模型卡片,其中包含了模型的相关信息和训练、评估结果。
tokenizer = load_tokenizer(model_args)
dataset = get_dataset(tokenizer, model_args, data_args, training_args, stage="pt" )
model = load_model(tokenizer, model_args, finetuning_args, training_args.do_train)
load_tokenizer def load_tokenizer (model_args: "ModelArguments" ) -> "PreTrainedTokenizer" :
r"""
Loads pretrained tokenizer. Must before load_model.
Note: including inplace operation of model_args.
"""
try_download_model_from_ms(model_args)
init_kwargs = _get_init_kwargs(model_args)
tokenizer = AutoTokenizer.from_pretrained(
model_args.model_name_or_path,
use_fast=model_args.use_fast_tokenizer,
split_special_tokens=model_args.split_special_tokens,
padding_side="right" ,
**init_kwargs,
)
patch_tokenizer(tokenizer)
return tokenizer
get_dataset 比较核心的方法其实是 get_dataset,因为要训练模型,最重要的部分是组织训练数据。
函数主要执行以下操作:
根据提供的 tokenizer、model_args、data_args 和 training_args 参数,获取数据集的模板并进行一些预处理。
检查是否需要从缓存路径加载数据集,如果是,则加载并返回数据集。
如果缓存路径不存在,则根据 data_args 参数获取数据集列表,并加载每个数据集。
将所有加载的数据集合并为一个数据集。
对数据集进行预处理,包括使用 tokenizer 对数据进行编码、根据指定的 stage 进行额外的预处理。
如果指定的 cache_path 不为空,则将预处理后的数据集保存到 cache_path 路径。
如果指定 should_log 参数,则打印数据集的一个样本
获取数据集模板 首先来分析一下获取数据集模板在做什么。可以进入以下路径查看代码。
src/llmtuner/data/template.py 的 get_template_and_fix_tokenizer 方法。
根据输入的 name,获取相应的模板,如果没有提供 name 或提供的 name 不存在,则使用默认模板 'vanilla'。
如果模板指示需要替换 EOS(End of String)标记,且模板还指定了停用词,则取出第一个停用词在分词器中替换 EOS 标记。,并将剩余的停用词保存起来。
如果分词器中没有定义 EOS 标记,则在分词器中添加一个空 EOS 标记"<|endoftext|>"。
如果分词器中没有定义 PAD 标记,则将 PAD 标记设置为与 EOS 标记相同的值,并在日志中记录。
如果模板指定了停用词,并且有剩余的停用词,则将这些停用词添加到分词器的特殊标记中,并在日志中记录。如果成功添加了新的特殊标记,则会发出警告提醒用户确认是否需要调整词汇表大小。
尝试将模板转换为 Jinja 模板,并将其与分词器相关联。
这段代码内容有点多,我们先不考虑模板的事,先来理解一下分词器中对应的概念。
概念理解
在自然语言处理(NLP)中,分词器(tokenizer)是一个将文本输入分割成单词、子词或符号序列的工具。这个过程称为分词或者标记化。在 NLP 中,文本通常以字符串的形式存在,而计算机需要将其转换成可以处理的结构化数据形式,例如单词序列或标记序列,以便进行后续的语言处理任务,如词嵌入、语言模型训练、序列标注等。
那分词器的 EOS 标记,PAD 标记,停用词分别代表什么呢?
EOS 标记通常用于表示序列的结束。在自然语言处理任务中,特别是序列到序列的任务(如机器翻译、文本生成等),需要在序列的末尾添加 EOS 标记以指示句子的结束。这有助于模型正确处理不同长度的输入序列。
在分词器中,EOS 标记用于标记化后的文本中表示句子结束的位置。
PAD 标记通常用于对不同长度的序列进行填充,使它们具有相同的长度。这在很多深度学习模型中是必要的,因为它们需要输入具有固定长度的序列。通过将序列填充到相同的长度,可以方便地将它们组成一个批次进行并行处理,提高训练效率。
在分词器中,PAD 标记用于填充序列,使其达到指定的最大长度。
停用词是在自然语言处理任务中通常会被忽略的常见词语,因为它们通常不携带太多的信息。在一些任务中,特别是文本生成任务,停用词可能会影响模型的生成结果,因此需要在预处理阶段将其去除。
在分词器中,停用词通常被用作特殊的标记,例如 EOS 或 PAD 标记的替代。在一些情况下,停用词可能还会被添加到词汇表中作为特殊标记,以便模型学习到如何处理它们。
至于 Jinja 模板,这里就不过多介绍了,后边我们会去查看源码,看看在做什么的。
获取模板 理解了以上内容,我们回过头来分析一下最开始的根据 name 获取相应模板是怎么做到的,它获取到的模板到底是什么。
通过阅读源码,我们可以看到,模板就是一个字典:
templates: Dict [str , Template] = {}
字典中的内容是通过_register_template 方法注册进去的。我们来分析一下_register_template 方法.
方法的入参比较多,我们可以分析各个参数的作用如下:
name: 模板的名称。
format_user: 用户对话部分的格式化器。
format_assistant: AI 助手对话部分的格式化器。
format_system: 系统对话部分的格式化器。
format_function: 函数对话部分的格式化器。
format_observation: 观察部分的格式化器。
format_tools: 工具部分的格式化器。
format_separator: 分隔符部分的格式化器。
default_system: 默认系统消息。
stop_words: 停用词列表。
efficient_eos: 是否使用高效 EOS 标记。
replace_eos: 是否替换 EOS 标记。
force_system: 是否强制使用系统。
函数的主要工作是根据提供的参数创建对应的格式化器,并使用这些格式化器创建一个新的对话模板(Template 对象或 Llama2Template 对象),然后将该模板注册到全局变量 templates 中。
注意,这里的 Template 对象或 Llama2Template 对象都是项目自定义的类。类中的内容我们先不看。
只要知道在初始化时,会调用此方法注册模板即可。
以 qwen 模板为例,在代码中我们可以看到如下注册模板的内容:
_register_template(
name="qwen" ,
format_user=StringFormatter(slots=["<|im_start|>user\n{{content}}<|im_end|>\n<|im_start|>assistant\n" ]),
format_system=StringFormatter(slots=["<|im_start|>system\n{{content}}<|im_end|>\n" ]),
format_separator=EmptyFormatter(slots=["\n" ]),
default_system="You are a helpful assistant." ,
stop_words=["<|im_end|>" ],
replace_eos=True ,
)
根据入参,我们再重新查看_register_template 方法的源码 (请自行查看源码往下看):
首先,efficient_eos 没有传参,默认值为 False,以至于 eos_slots 为 [],由此可以理解所谓的高效 EOS 标记,就是可能不需要额外的 EOS 标记,从而节省了内存和计算资源。
后续就是在实例化 Template 对象返回。
转换为 Jinja 模板
该函数_get_jinja_template 的作用是根据输入的模板和分词器生成一个 Jinja2 模板字符串。
首先,函数会判断模板中是否设置了默认系统消息,如果有,则将该消息添加到 Jinja2 模板中。
接着,函数会检查模板消息列表中是否存在系统消息,并将其内容赋值给变量 system_message。
然后,根据模板类型和是否强制显示系统消息,将 system_message 变量添加到 Jinja2 模板中。
接下来,函数会遍历模板消息列表,并根据消息的角色(用户或助手)将相应的内容添加到 Jinja2 模板中。
最后,函数返回生成的 Jinja2 模板字符串。
在处理过程中,函数会使用_convert_slots_to_jinja 函数将模板中的占位符转换为对应的 Jinja2 表达式,并使用 PreTrainedTokenizer 对模板内容进行分词处理。
可以看到,Jinja2 模板中支持 if else 和 for,不过这些都不重要,我们只要知道组织好模板后将模板赋值给了 tokenizer 的 chat_template 属性即可。
获取数据集列表 with training_args.main_process_first(desc="load dataset" ):
all_datasets = []
for dataset_attr in get_dataset_list(data_args):
all_datasets.append(load_single_dataset(dataset_attr, model_args, data_args))
dataset = merge_dataset(all_datasets, data_args, training_args)
这里先来关注 get_dataset_list 方法。
该函数用于获取数据集列表。根据输入的 data_args 参数中的 dataset 字段,将数据集名称列表进行处理并保存。然后从 data_args 参数中的 dataset_dir 目录下读取数据集配置文件 DATA_CONFIG,并解析其中的内容。实际文件路径为 data/dataset_info.json。
接下来,根据配置文件中定义的数据集信息,创建并填充 DatasetAttr 对象,并将其添加到 dataset_list 列表中。最后,返回 dataset_list 列表。
在创建 DatasetAttr 对象时,根据配置文件中的不同字段,选择不同的数据集类型和属性,并设置相应的属性值。
如果配置文件中定义了列名,则将其添加到 DatasetAttr 对象的属性中。
如果数据集格式为 sharegpt,并且配置文件中定义了标签信息,则将其添加到 DatasetAttr 对象的属性中。
如果在读取配置文件时发生异常,将抛出相应的异常。
现在我们知道 get_dataset_list 方法会返回数据集的一些元数据,load_single_dataset 方法就会根据元数据来加载真正的数据了。
get_dataset_list 根据给定的 dataset_attr、model_args 和 data_args 参数加载单个数据集。根据 dataset_attr.load_from 的值,函数从不同的来源加载数据集。支持的来源包括"Hugging Face Hub"、"ModelScope Hub"、脚本或文件。
当从"Hugging Face Hub"或"ModelScope Hub"加载数据集时,函数会使用相应的库加载数据集。
当从脚本或文件加载数据集时,函数会根据文件类型选择合适的方式加载数据。
函数还支持数据集的截断和对齐操作。
dataset = load_dataset(
path=data_path,
name=data_name,
data_dir=data_dir,
data_files=data_files,
split=data_args.split,
cache_dir=model_args.cache_dir,
token=model_args.hf_hub_token,
streaming=(data_args.streaming and (dataset_attr.load_from != "file" )),
**kwargs,
)
这部分使用的是 Hugging Face 的 datasets 库来进行加载的。具体使用方法可以参考官网。
这里就不介绍了。
后续使用 align_dataset 对数据集进行转换后返回单个数据集的结果
align_dataset 函数用于对给定的 dataset 进行格式转换,使其符合指定的 dataset_attr 属性要求。
该函数根据 dataset_attr 的格式要求选择不同的转换函数(convert_alpaca 或 convert_sharegpt),并为转换后的数据集定义了特定的特征字典(features)。
转换函数将对数据集中的每个样本进行处理,重新组织其字段,并添加额外的"prompt"、"response"、"system"和"tools"字段。
处理过程中,可以选择是否使用批处理,并可以指定并行处理的工作线程数、是否从缓存文件加载以及是否覆盖缓存文件等参数。最终返回转换后的数据集。
具体的转换逻辑我们先不用看了,知道是转换成一种方便训练的格式就可以了。
后续预处理的逻辑我们也先不用看,大体了解会使用 tokenizer 对数据进行处理就可以了。
load_model
函数首先通过 model_args.model_name_or_path 使用 AutoConfig 获取模型的配置和初始化参数,然后根据是否可训练和是否使用 unsloth 选择不同的模型加载方式。
如果可训练且使用 unsloth,则使用 FastLanguageModel.from_pretrained 加载模型;
否则,使用 AutoModelForCausalLM.from_pretrained 加载模型。
接着,函数会对模型进行一些修改和注册,然后根据是否添加值头(value head)来初始化或修改模型。
最后,函数将模型设置为相应的模式(可训练或不可训练),并返回模型。
参数说明:
tokenizer: 预训练的分词器。
model_args: 模型参数,包括模型名称、最大序列长度、计算数据类型等。
finetuning_args: 微调参数。
is_trainable: 模型是否可训练,默认为 False。
add_valuehead: 是否添加值头,默认为 False。
返回值:
model: 加载的预训练模型。
其中比较核心的就是对模型进行的一些修改和注册了。这部分代码如下:
patch_model(model, tokenizer, model_args, is_trainable)
register_autoclass(config, model, tokenizer)
model = init_adapter(model, model_args, finetuning_args, is_trainable)
概念理解 这里我们看到了新的概念,unsloth。所以我们先来理解一下新概念。
通过观察 LLaMA-Factory 的可视化页面中高级设置,可以看到加速方式。加速方式包括 flashattn 和 unsloth,那它们代表什么呢?
'unsloth' 和 'flashattn' 是两种不同的加速技术,通常用于优化神经网络模型的推理速度。
Unsloth: Unsloth 是一种基于量化的加速技术,它的主要思想是通过减少模型参数的精度来降低计算的复杂度,从而提高推理速度。在 Unsloth 中,通常会将模型参数从浮点数转换为低精度的整数或定点数。这样可以降低模型的存储需求,并且在推理时减少了浮点运算的开销,从而加快了模型的推理速度。不过,由于参数精度的降低可能会带来一定的精度损失,因此在选择使用 Unsloth 技术时需要权衡推理速度和模型精度之间的关系。
FlashAttn: FlashAttn 是一种用于加速注意力机制(attention mechanism)的技术。注意力机制在深度学习模型中广泛应用于处理序列数据,例如机器翻译、文本生成等任务。然而,由于注意力机制的计算量较大,它可能成为模型推理速度的瓶颈。FlashAttn 通过优化注意力计算的方式来加速模型推理过程。具体来说,FlashAttn 可能采用一些技巧,例如降低注意力矩阵的计算复杂度、减少注意力头的数量、或者采用特定的注意力结构等。这些技巧可以有效地减少模型推理时的计算量,从而加速推理过程。
简单来说,Unsloth 和 FlashAttn 都是用于加速神经网络模型推理过程的技术,但它们的具体实现和优化方式有所不同,适用于不同类型的模型和应用场景。
patch_model patch_model 函数用于根据不同的模型类型和参数,对模型和分词器进行一系列的修改和配置。
具体包括以下几个方面:
如果模型的 generate 方法不是 GenerationMixin 的子类,则将其替换为 PreTrainedModel.generate 方法。
如果模型配置中的 model_type 为"chatglm",则设置模型的 lm_head 为 transformer.output_layer,并设置保存模型时忽略 lm_head.weight。(chatglm 需要一些特殊处理,我们暂不关心)
如果 model_args.resize_vocab 为 True,则调用_resize_embedding_layer 函数来调整嵌入层的大小。
如果模型是可训练的,则调用_prepare_model_for_training 函数对模型进行训练前的准备。
如果模型配置中的 model_type 为"mixtral"且启用了 DeepSpeed 的 Zero3 优化器,则导入 set_z3_leaf_modules 和 MixtralSparseMoeBlock,并调用 set_z3_leaf_modules 函数将 model 中的叶子模块设置为 MixtralSparseMoeBlock。如果模型是可训练的,则调用 patch_mixtral_replace_moe_impl 函数。
尝试向模型添加标签"llama-factory",如果添加失败则打印警告信息。
这些修改和配置的目的是为了适应不同模型的需求,提高模型的性能和效率。
我们如果使用 qwen 模型,主要需要观察_prepare_model_for_training 函数对模型做了哪些准备。
该函数主要为模型训练做准备,具体包括以下操作:
如果 model_args.upcast_layernorm 为 True,则将模型中的层归一化(layernorm)权重转换为 float32 类型。
如果 model_args.disable_gradient_checkpointing 为 False 且模型支持梯度检查点(gradient checkpointing),则启用梯度检查点,并设置相关属性。
如果模型具有 output_layer_name 属性且 model_args.upcast_lmhead_output 为 True,则将语言模型头(lm_head)的输出转换为 float32 类型
register_autoclass def register_autoclass (config: "PretrainedConfig" , model: "PreTrainedModel" , tokenizer: "PreTrainedTokenizer" ):
if "AutoConfig" in getattr (config, "auto_map" , {}):
config.__class__.register_for_auto_class()
if "AutoModelForCausalLM" in getattr (config, "auto_map" , {}):
model.__class__.register_for_auto_class()
if "AutoTokenizer" in tokenizer.init_kwargs.get("auto_map" , {}):
tokenizer.__class__.register_for_auto_class()
就是字面意思,注册 Transformers 框架中的自动类,具体用处目前还不明确。
init_adapter init_adapter 函数用于初始化适配器,并支持全参数、冻结和 LoRA 训练。根据传入的模型、模型参数、微调参数和是否可训练,该函数将根据微调类型对模型进行相应的处理。此方法属于比较核心的方法
如果模型不可训练且没有指定适配器名称路径,则加载基本模型。
如果微调类型为"full"且模型可训练,则将模型参数转换为 float32 类型。
如果微调类型为"freeze"且模型可训练,则根据 num_layer_trainable 和其他参数来确定可训练的层,并将其他层的参数设置为不可训练。可训练的层可以是最后 n 层、前面 n 层或指定的层。
如果微调类型为"lora",则根据是否指定适配器名称路径和其他参数来加载、合并和恢复 LoRA 模型,并创建新的 LoRA 权重。
最终,该函数返回处理后的模型
这部分代码内容还是比较多的,full 和 freeze 我们不用关注,重点关注 lora 部分。由于这部分比较重要,我把 lora 部分代码放到下边,并用注释解释一下:
if finetuning_args.finetuning_type == "lora" :
logger.info("Fine-tuning method: {}" .format ("DoRA" if finetuning_args.use_dora else "LoRA" ))
adapter_to_resume = None
if model_args.adapter_name_or_path is not None :...
if is_trainable and adapter_to_resume is None :
if len (finetuning_args.lora_target) == 1 and finetuning_args.lora_target[0 ] == "all" :
target_modules = find_all_linear_modules(model)
else :
target_modules = finetuning_args.lora_target
if finetuning_args.use_llama_pro:
target_modules = find_expanded_modules(model, target_modules, finetuning_args.num_layer_trainable)
if finetuning_args.use_dora and getattr (model, "quantization_method" , None ) is not None :
if getattr (model, "quantization_method" , None ) != QuantizationMethod.BITS_AND_BYTES:
raise ValueError("DoRA is not compatible with PTQ-quantized models." )
peft_kwargs = {
"r" : finetuning_args.lora_rank,
"target_modules" : target_modules,
"lora_alpha" : finetuning_args.lora_alpha,
"lora_dropout" : finetuning_args.lora_dropout,
"use_rslora" : finetuning_args.use_rslora,
}
if model_args.use_unsloth:
from unsloth import FastLanguageModel
unsloth_peft_kwargs = {"model" : model, "max_seq_length" : model_args.model_max_length}
model = FastLanguageModel.get_peft_model(**peft_kwargs, **unsloth_peft_kwargs)
else :
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
inference_mode=False ,
modules_to_save=finetuning_args.additional_target,
use_dora=finetuning_args.use_dora,
**peft_kwargs,
)
model = get_peft_model(model, lora_config)
if not finetuning_args.pure_bf16:
for param in filter (lambda p: p.requires_grad, model.parameters()):
param.data = param.data.to(torch.float32)
通过阅读以上源码和注释,想要更好的理解,需要解决下边的问题。
lora_target 应该怎么设置比较合适?
dora 是什么?
LoraConfig 应该怎么配置更合适?
想要解决这些问题,我们应该去了解一下 LoraConfig。
解读 LoraConfig 首先,LoraConfig 属于 PEFT 库。所以可以阅读一下官方文档,来理解一下 Lora,地址如下:
通过阅读这部分文档,我们可以对 Lora 整体有了一个认识。
之后我们可以阅读这部分内容,来理解一下 LoraConfig 中每个参数的作用。
回到我们自己的代码中,现在可以解释一下这部分代码具体的含义了:
peft_kwargs = {
"r" : finetuning_args.lora_rank,
"target_modules" : target_modules,
"lora_alpha" : finetuning_args.lora_alpha,
"lora_dropout" : finetuning_args.lora_dropout,
"use_rslora" : finetuning_args.use_rslora,
}
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
inference_mode=False ,
modules_to_save=finetuning_args.additional_target,
use_dora=finetuning_args.use_dora,
**peft_kwargs,
)
task_type:此参数不是 LoraConfig 的参数,而是它的父类 PeftConfig 的参数,可选值为 TaskType 中的值,具体的含义是什么呢?我们可以直接看源码:
class TaskType (str , enum.Enum):
"""
Enum class for the different types of tasks supported by PEFT.
Overview of the supported task types:
- SEQ_CLS: Text classification.
- SEQ_2_SEQ_LM: Sequence-to-sequence language modeling.
- CAUSAL_LM: Causal language modeling.
- TOKEN_CLS: Token classification.
- QUESTION_ANS: Question answering.
- FEATURE_EXTRACTION: Feature extraction. Provides the hidden states which can be used as embeddings or features
for downstream tasks.
"""
SEQ_CLS = "SEQ_CLS"
SEQ_2_SEQ_LM = "SEQ_2_SEQ_LM"
CAUSAL_LM = "CAUSAL_LM"
TOKEN_CLS = "TOKEN_CLS"
QUESTION_ANS = "QUESTION_ANS"
FEATURE_EXTRACTION = "FEATURE_EXTRACTION"
可以看到,其实就是在指定任务类型是大语言模型。
inference_mode:此参数也是父类 PeftConfig 的参数,表示模型是否是推理模型,由于我们要进行训练,所以设置为 False
modules_to_save:除 LoRA 层以外的可训练模块名称,我们先不用管这里。
use_dora:使用权重分解的 LoRA。
r:lora 微调的维度,我们默认设置的是 8。
target_modules:就是要微调的模块,前边已经有介绍。
lora_alpha:LoRA 微调的缩放因子,默认为 r * 2。
lora_dropout:LoRA 微调的随机丢弃率。了解过深度学习的一定可以理解这个指标。
use_rslora:是否使用 LoRA 层的秩稳定缩放因子,阅读官网可以理解为:将适配器的缩放因子设置为 lora_alpha/math.sqrt® ,因为它被证明工作得更好。否则,它将使用原始的默认值 lora_alpha/r 。
至此,目前我们已经理解了项目中使用到的参数。
其他内容可根据官方文档理解。
模型训练部分 前边的内容只是训练的前提,接下来我们就来看一下训练部分的实现。
我们回到 src/llmtuner/train/pt/workflow.py 中的 run_pt 方法中继续往下看。这里我把核心源码直接放到下边。
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False )
trainer = CustomTrainer(
model=model,
args=training_args,
finetuning_args=finetuning_args,
tokenizer=tokenizer,
data_collator=data_collator,
callbacks=callbacks,
**split_dataset(dataset, data_args, training_args),
)
if training_args.do_train:
train_result = trainer.train(resume_from_checkpoint=training_args.resume_from_checkpoint)
trainer.save_model()
trainer.log_metrics("train" , train_result.metrics)
trainer.save_metrics("train" , train_result.metrics)
trainer.save_state()
if trainer.is_world_process_zero() and finetuning_args.plot_loss:
plot_loss(training_args.output_dir, keys=["loss" , "eval_loss" ])
首先我们先来理解一下 DataCollatorForLanguageModeling,可以阅读官网文档进行理解,地址如下:
简单来说就是转换成模型可以输入的格式。
CustomTrainer 是一个比较重要的内容,接下来我们将对它进行解读。
解读 CustomTrainer CustomTrainer 继承自 Trainer 类,并且重写了 create_optimizer_and_scheduler 方法。
create_optimizer_and_scheduler 方法用于创建自定义的优化器和学习率调度器。它首先调用 create_custom_optimzer 函数来创建优化器,该函数根据模型、参数和 finetuning_args 来决定如何创建优化器。如果 create_custom_optimzer 返回 None,则调用 Trainer 类的 create_optimizer 方法来创建优化器。
我们可以看一下 create_custom_optimzer 的底层实现,代码内容不多,直接放到下边:
def create_custom_optimzer (
model: "PreTrainedModel" ,
training_args: "Seq2SeqTrainingArguments" ,
finetuning_args: "FinetuningArguments" ,
max_steps: int ,
) -> Optional ["torch.optim.Optimizer" ]:
if finetuning_args.use_galore:
return _create_galore_optimizer(model, training_args, finetuning_args, max_steps)
if finetuning_args.loraplus_lr_ratio is not None :
return _create_loraplus_optimizer(model, training_args, finetuning_args)
如何创建自定义优化器我们先不研究,目前我们还用不到。
要研究的重点其实是 Trainer 类,Trainer 是 Transformers 库中比较重要的一部分。可以阅读官方文档进行了解:
接下来我们分析一下代码中传入的参数的含义:
model:这个不用太解释,就是传入之前加载的模型
args=training_args:这个 training_args 实际上是 Transformers 库中的 Seq2SeqTrainingArguments,这其中包含的参数就比较多了。
finetuning_args:这个参数是自定义的参数,我们不关注。
tokenizer、data_collator:这两个参数不再解释,你应该也能看懂了。
callbacks:指定回调函数,LLaMA-Factory 指定了一个打印日志的回调函数。
split_dataset:这是自定义的函数,用来做数据拆分,实现细节我们先不看,主要就是返回 split_dataset 参数和 eval_dataset 参数,分别用来表示训练的数据集和评估的数据集,是 Trainer 类自带的参数。
至此,对 CustomTrainer 我们已经有了整体的认识。
解读 Seq2SeqTrainingArguments 为了更进一步的了解训练参数,我们可以看一下 Seq2SeqTrainingArguments 中都有哪些参数。官方文档地址如下:
由于参数太多了,就不挨个参数解读了,只解释一些我们常用到的参数,其他参数详见官方文档:
output_dir:输出目录,将写入模型预测和检查点。
overwrite_output_dir:如果设置为 True,将覆盖输出目录中的内容。可以在 output_dir 指向检查点目录时使用此选项继续训练。
do_train:是否进行训练。这个参数不是直接由 [Trainer] 使用的
do_eval:是否在验证集上运行评估。如果 evaluation_strategy 不是 'no' ,将被设置为 True。这个参数不是直接由 [Trainer] 使用的
do_predict:是否在测试集上进行预测。这个参数不是直接由 [Trainer] 使用的
evaluation_strategy:训练过程中采用的评估策略。可能的取值有:
'no': 不进行评估。
'steps': 每隔 eval_steps 进行评估。
'epoch': 每个 epoch 结束时进行评估
per_device_train_batch_size (int,可选,默认为 8):每个 GPU/TPU 核心/CPU 的训练批次大小。
per_device_eval_batch_size (int,可选,默认为 8):每个 GPU/TPU 核心/CPU 的评估批次大小。
gradient_accumulation_steps (int,可选,默认为 1):在执行反向传播/更新步骤之前,累积梯度的更新步骤数。
模型评估部分 直接看剩余部分的代码,理解了之前训练部分的代码,评估部分代码很容易就能看懂。
if training_args.do_eval:
metrics = trainer.evaluate(metric_key_prefix="eval" )
try :
perplexity = math.exp(metrics["eval_loss" ])
except OverflowError:
perplexity = float ("inf" )
metrics["perplexity" ] = perplexity
trainer.log_metrics("eval" , metrics)
trainer.save_metrics("eval" , metrics)
总结 至此,我们已经打通了 pt 预训练这条通道,接下来我们就要开始查看 sft 指令微调部分的实现了。
sft 指令微调 首先我们还是查看官方文档提供的 sft 脚本,内容如下:
CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \
--stage sft \
--do_train \
--model_name_or_path path_to_llama_model \
--dataset alpaca_gpt4_zh \
--template default \
--finetuning_type lora \
--lora_target q_proj,v_proj \
--output_dir path_to_sft_checkpoint \
--overwrite_cache \
--per_device_train_batch_size 4 \
--gradient_accumulation_steps 4 \
--lr_scheduler_type cosine \
--logging_steps 10 \
--save_steps 1000 \
--learning_rate 5e-5 \
--num_train_epochs 3.0 \
--plot_loss \
--fp16
可以发现,只有–stage 被设置成了 sft,其他参数之前已经介绍过了。
所以我们直接开始查看源码。
理解源码 有了之前的经验,源码入口可以很快找到。
即 src/llmtuner/train/sft/workflow.py 中的 run_sft 方法中。这里我直接把完整代码放到下边:
def run_sft (
model_args: "ModelArguments" ,
data_args: "DataArguments" ,
training_args: "Seq2SeqTrainingArguments" ,
finetuning_args: "FinetuningArguments" ,
generating_args: "GeneratingArguments" ,
callbacks: Optional [List ["TrainerCallback" ]] = None ,
):
tokenizer = load_tokenizer(model_args)
dataset = get_dataset(tokenizer, model_args, data_args, training_args, stage="sft" )
model = load_model(tokenizer, model_args, finetuning_args, training_args.do_train)
if training_args.predict_with_generate:
tokenizer.padding_side = "left"
if getattr (model, "is_quantized" , False ) and not training_args.do_train:
setattr (model, "_hf_peft_config_loaded" , True )
data_collator = DataCollatorForSeq2Seq(
tokenizer=tokenizer,
pad_to_multiple_of=8 if tokenizer.padding_side == "right" else None ,
label_pad_token_id=IGNORE_INDEX if data_args.ignore_pad_token_for_loss else tokenizer.pad_token_id,
)
training_args.generation_max_length = training_args.generation_max_length or data_args.cutoff_len
training_args.generation_num_beams = data_args.eval_num_beams or training_args.generation_num_beams
trainer = CustomSeq2SeqTrainer(
model=model,
args=training_args,
finetuning_args=finetuning_args,
tokenizer=tokenizer,
data_collator=data_collator,
callbacks=callbacks,
compute_metrics=ComputeMetrics(tokenizer) if training_args.predict_with_generate else None ,
**split_dataset(dataset, data_args, training_args),
)
gen_kwargs = generating_args.to_dict()
gen_kwargs["eos_token_id" ] = [tokenizer.eos_token_id] + tokenizer.additional_special_tokens_ids
gen_kwargs["pad_token_id" ] = tokenizer.pad_token_id
gen_kwargs["logits_processor" ] = get_logits_processor()
if training_args.do_train:
train_result = trainer.train(resume_from_checkpoint=training_args.resume_from_checkpoint)
trainer.save_model()
trainer.log_metrics("train" , train_result.metrics)
trainer.save_metrics("train" , train_result.metrics)
trainer.save_state()
if trainer.is_world_process_zero() and finetuning_args.plot_loss:
plot_loss(training_args.output_dir, keys=["loss" , "eval_loss" ])
if training_args.do_eval:
metrics = trainer.evaluate(metric_key_prefix="eval" , **gen_kwargs)
if training_args.predict_with_generate:
metrics.pop("eval_loss" , None )
trainer.log_metrics("eval" , metrics)
trainer.save_metrics("eval" , metrics)
if training_args.do_predict:
predict_results = trainer.predict(dataset, metric_key_prefix="predict" , **gen_kwargs)
if training_args.predict_with_generate:
predict_results.metrics.pop("predict_loss" , None )
trainer.log_metrics("predict" , predict_results.metrics)
trainer.save_metrics("predict" , predict_results.metrics)
trainer.save_predictions(predict_results)
create_modelcard_and_push(trainer, model_args, data_args, training_args, finetuning_args)
发现了什么?
没错,代码结构基本与之前的预训练部分差不多。
只有在 get_dataset 处理数据部分,会有所差异,具体差异我们暂时不看,只要知道 sft 阶段数据预处理时,是需要增加指令、角色信息的就可以了。
实际上,如果选择使用 LLaMA-Factory 进行微调,我们按照 LLaMA-Factory 的数据集格式要求准备数据就可以了。
解读 CustomSeq2SeqTrainer 这阶段除了预处理数据部分有差别,最大的差别就是训练器与之前不同,使用的是 CustomSeq2SeqTrainer,
CustomSeq2SeqTrainer 是 Seq2SeqTrainer 的子类,而 Seq2SeqTrainer 是 Trainer 的子类。
Seq2SeqTrainer 的源码我们就不去仔细阅读了,简单阅读后发现,Seq2SeqTrainer 主要是重写了 Trainer 的评估和推理的方法,没有重写训练方法,所以与使用 Trainer 进行训练应该差别不大。
这里为什么使用 Seq2SeqTrainer,我们不必纠结。
阅读 qwen1.5 官方提供的 sft 示例,可以看到示例中使用的也是 Trainer 而不是 Seq2SeqTrainer。
示例地址:
总结 可以发现,理解了 pt 阶段后,再来理解 sft 阶段其实是很容易的,一通百通,sft 阶段我们就介绍到这里,如果你对哪部分感兴趣,可以自己去阅读源码,相信有了 pt 阶段的知识储备后,你可以很容易的阅读源码了。
另外,我们只要懂得 pt 与 sft 微调,就能实际上手微调了。所以后续的 RLHF 阶段就先不去查看了。
微调实践 我们已经理解了大模型微调的基本过程,但实践是检验真理的唯一标准,所以接下来将与大家一起对大模型微调进行实践,观察一下微调效果。
注意:UI 界面的使用请阅读官方文档,这里不会介绍 UI 如何使用。
数据集准备 根据 LLaMA-Factory 官方提供的数据准备文档,可以看到训练数据的格式。地址如下:
对于预训练数据集,仅 prompt 列中的内容会用于模型训练。
对于偏好数据集,response 列应当是一个长度为 2 的字符串列表,排在前面的代表更优的回答
下载后,将 json 文件存放到 LLaMA-Factory 的 data 目录下。
修改 dataset_info.json 文件。
直接增加以下内容即可:
"huanhuan" : {
"file_name" : "huanhuan.json"
}
添加后,在 UI 页面中直接可以看到新增加的数据集。
开始微调 为了减少资源消耗,本次选择测试的模型是 Qwen1.5-0.5B-Chat。
通过可视化页面配置后,可以得到微调命令如下:
CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \
--stage sft \
--do_train True \
--model_name_or_path /home/jqxxuser/model/Qwen1.5 -0.5 B-Chat \
--finetuning_type lora \
--template qwen \
--dataset_dir data \
--dataset huanhuan \
--cutoff_len 1024 \
--learning_rate 5e-05 \
--num_train_epochs 2.0 \
--max_samples 100000 \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--optim adamw_torch \
--output_dir saves/Qwen1.5 -0.5 B-Chat/lora/train_2024-03-28 -10 -54 -09 \
--fp16 True \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0.1 \
--lora_target q_proj,v_proj \
--plot_loss True
说明:SFT 阶段是最常用训练阶段,所以我们在 SFT 阶段进行微调,测试效果。
我们直接在 UI 中点击开始即可进行微调,可以观察到整个的过程,发现损失值在逐渐降低。
测试聊天效果 刷新适配器,可以看到我们刚刚训练完的模型,选择即可
评估模型 通过聊天观察效果是一种直观的感觉,我们可以在通过项目自带的评估功能做一下评估。
注意,如果报错,需要确保安装了以下库。
jieba
rouge-chinese
nltk
{
"predict_bleu-4" : 2.487403191204076 ,
"predict_rouge-1" : 16.790678761061947 ,
"predict_rouge-2" : 1.1607781979082865 ,
"predict_rouge-l" : 14.878193322606597 ,
"predict_runtime" : 900.9563 ,
"predict_samples_per_second" : 4.139 ,
"predict_steps_per_second" : 1.38
}
这些指标应该怎么看呢?首先我们来了解一下这些指标的概念。
BLEU(Bilingual Evaluation Understudy)是一种常用的用于评估机器翻译质量的指标。
BLEU-4 表示四元语法 BLEU 分数,它衡量模型生成文本与参考文本之间的 n-gram 匹配程度,其中 n=4。
值越高表示生成的文本与参考文本越相似,最大值为 100。
predict_rouge-1 和 predict_rouge-2:
ROUGE(Recall-Oriented Understudy for Gisting Evaluation)是一种用于评估自动摘要和文本生成模型性能的指标。
ROUGE-1 表示一元 ROUGE 分数,ROUGE-2 表示二元 ROUGE 分数,分别衡量模型生成文本与参考文本之间的单个词和双词序列的匹配程度。
值越高表示生成的文本与参考文本越相似,最大值为 100。
ROUGE-L 衡量模型生成文本与参考文本之间最长公共子序列(Longest Common Subsequence)的匹配程度。
值越高表示生成的文本与参考文本越相似,最大值为 100。
预测运行时间,表示模型生成一批样本所花费的总时间。
单位通常为秒。
predict_samples_per_second:
每秒生成的样本数量,表示模型每秒钟能够生成的样本数量。
通常用于评估模型的推理速度。
predict_steps_per_second:
每秒执行的步骤数量,表示模型每秒钟能够执行的步骤数量。
对于生成模型,一般指的是每秒钟执行生成操作的次数。
所以,单独看指标数据的话,模型的效果并不是太好,这就需要大家自行摸索如何让模型能力更好了。
导出模型 切换到 Export 选项卡,指定导出目录,点击开始导出,即可导出模型。
总结 至此,我们的一次模型微调的尝试就完成了。
可以发现,使用 LLaMA-Factory 进行微调基本上可以做到便捷式操作了。最大的工作量还是在组织训练数据这个阶段。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online