textgen 训练指南:在 Training 标签页中完成 LoRA 微调、指令模板配置与 Loss 调优
本篇基于 textgen(开源本地 LLM 桌面应用,支持文本、视觉、工具调用与 OpenAI/Anthropic 兼容 API)的 Training 标签页文档与源码实现,系统讲解如何在本应用中训练自己的 LoRA 适配器:从数据集格式(OpenAI messages / ShareGPT / 纯文本)、指令模板(Jinja2 apply_chat_template())的两种来源,到 Rank、学习率、批大小等关键参数的取值依据、断点续训机制与 Loss 曲线的判读方法。读完本文,你可以独立完成一次 LoRA 训练任务,理解每个参数在源码中的实际作用,并能诊断训练异常。
前提与核心约束:LoRA 与模型架构绑定
一条最重要的规则:LoRA 是绑定特定模型架构的——在 Llama 3 8B 上训练的 LoRA 无法用在 Mistral 7B 上,必须在你计划使用的确切模型上训练。
从源码看,这个约束还有两个前置条件,modules/training.py 中的校验逻辑可以直接看到:
- 必须使用 Transformers 加载器。训练入口
do_train()首先检查加载器类型,若当前模型由 llama.cpp(GGUF)加载,会直接报错退出:"LoRA training requires a model loaded with the Transformers loader. GGUF models are not supported for training."。 - 训练前不应已挂载其他 LoRA。若模型已是
PeftModelForCausalLM,源码会给出警告("Training LoRA over top of another LoRA. May have unexpected effects.")并等待 5 秒让你按 Interrupt 中止(见 modules/training.py)。
此外,训练开始前源码会做一些隐式处理(modules/training.py):
- 梯度累积步数由
gradient_accumulation_steps = batch_size // micro_batch_size计算; - 若 tokenizer 没有
pad_token,会回退使用eos_token_id充当 pad token,并统一设置为右侧填充(padding_side = "right")。
Quick Start:五分钟完成一次 LoRA 训练
文档给出的快速上手流程如下,共五步:
- 用 Transformers 加载器加载基础模型(不加载任何 LoRA)。
- 打开 Training 标签页 > Train LoRA。
- 选择数据集并配置参数(见下文 Parameters 一节)。
- 点击 Start LoRA Training,监控 Loss。
- 训练完成后,在 Models 标签页加载该 LoRA 并测试。
点击开始按钮后,UI 事件会绑定到 do_train(),其完整签名接收 34 个参数(modules/training.py),覆盖模块选择、批参数、优化器、数据集与模板等全部配置。训练在独立线程中运行(trainer.train(resume_from_checkpoint=...)),主线程负责向 WebUI 推送进度(当前步数/总步数、it/s 速率、剩余时间估算),并响应 Interrupt。
训练完成后的注意事项
训练结束时源码会提示:在测试新 LoRA 之前必须先重新加载模型,因为基础模型已被本次训练修改(shared.model_dirty_from_training 标志会被置位,见 modules/training.py)。如果上一次训练(哪怕是失败的)改脏了模型,下一次训练前源码会自动先执行 reload_model() 重载基础模型(modules/training.py)。
Resuming Training:断点续训机制
文档说明:要从中断点恢复训练,使用相同的 LoRA 名称并取消勾选 Override Existing Files。若存在检查点(来自 Save every n steps),训练会自动从最新检查点恢复,完整保留优化器与调度器状态。注意:已创建 LoRA 的 Rank 不可更改。
源码实现印证了这一流程(modules/training.py):
- 当未勾选覆盖且目标目录已存在时,代码会按修改时间排序扫描
checkpoint-*目录,取最新者作为resume_checkpoint,传给 HuggingFace Trainer 的trainer.train(resume_from_checkpoint=resume_checkpoint),这就是"完整恢复优化器/调度器状态"的来源; - 若没有检查点目录,则走旧版回退路径:只加载裸适配器权重(
adapter_model.safetensors或adapter_model.bin),不恢复优化器状态。
文档还建议同时使用 UI 顶部的 Copy parameters from 下拉框,从上次运行复制 UI 设置(学习率、epochs 等)。其实现是读取该 LoRA 目录下自动保存的 training_parameters.json(由 do_copy_params() 完成),逐参数回填到界面——因此每次训练开始时,当前 34 个参数都会写入该文件备查(modules/training.py)。
另一个容易忽略的保护机制:取消勾选 Override Existing Files 且目录下已存在旧适配器时,backup_adapter() 会按适配器文件的创建日期建立 Backup-YYYY-MM-DD 子文件夹并整目录备份,避免旧成果被覆盖。
Instruction Templates:两种模板来源,同一条分词路径
所有指令/聊天训练都使用 apply_chat_template() 与 Jinja2 模板。Instruction Template 下拉框有两类选项(get_instruction_templates()):
- Chat Template:使用模型 tokenizer 自带的 chat template。适用于出厂即带聊天模板的 instruct/chat 模型(Llama 3、Qwen、Mistral 等)。若模型 tokenizer 没有
chat_template,源码会直接报错,提示改选命名模板或加载 instruct 模型(modules/training.py)。 - Named template(ChatML、Alpaca、Llama-v3 等):从
user_data/instruction-templates/加载 Jinja2 模板文件。适合没有内置模板的基础模型,或需要覆盖模型默认模板的场景。
两种方式在功能上完全等价——唯一区别是 Jinja2 模板字符串的来源。源码上,命名模板会被赋给 shared.tokenizer.chat_template(modules/training.py),随后两种情况走同一条分词路径。在两种情况下:
- 数据集经
apply_chat_template()分词; - labels 自动掩码,只对 assistant 回复部分计算损失;
- 原生支持多轮对话;
- 特殊 token 由模板正确处理。
assistant-only 掩码的具体实现
tokenize_conversation()(modules/training.py)的做法值得细看:先把完整消息序列分词得到 full_ids,labels 初始化为全 -100(即全部忽略);然后对每个 role == "assistant" 的消息,分别计算 apply_chat_template(messages[:i], add_generation_prompt=True)(该回复开始前的 token 数,即 start)与 apply_chat_template(messages[:i+1])(到该回复结束,即 end),把 full_ids[start:end] 原样写回 labels。这样 loss 只落在 assistant 生成的 token 上。注释指出该前缀假设对所有标准聊天模板(Llama、ChatML、Mistral 等)成立。
超长会话由 Excess length 参数控制,默认 drop(整条丢弃,推荐),可选 truncate(从右侧截断,可能产生不完整回复)。被丢弃的条数会以警告形式打印:Dropped {dropped}/{total} conversations exceeding cutoff length of {cutoff_len} tokens.(modules/training.py)。
模板文件从哪里来
user_data/instruction-templates/ 目录中预置了一批命名模板,例如 ChatML.yaml、Alpaca.yaml、Llama-v3.yaml、Mistral.yaml、Open Assistant.yaml、Vicuna-v1.1.yaml。以 ChatML 为例,其内容是一个标准 Jinja2 模板:
{%- for message in messages %}
{%- if message['role'] == 'system' -%}
{{- '系统\n' + message['content'].rstrip() + '\n' -}}
{%- elif message['role'] == 'user' -%}
{{-'用户\n' + message['content'].rstrip() + '\n'-}}
{%- else -%}
{{-'助手\n' + message['content'] + '\n' -}}
{%- endif -%}
{%- endfor %}
{%- if add_generation_prompt -%}
{{-'助手\n'-}}
{%- endif %}
(以上为示意;实际文件使用 `` / `
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00