使用 Training Hub 对 LLM 进行微调

training_hub 是一个 Python 库,它将多种 LLM 后训练算法——Supervised Fine-Tuning (SFT)、Orthogonal Subspace Fine-Tuning (OSFT)、LoRA / QLoRA,以及 continued pre-training (CPT)——封装在单个函数调用(sft(...)osft(...)lora_sft(...))之后,以统一方式处理单 GPU、多 GPU 和多节点训练。

  • 自动内存管理max_tokens_per_gpu 会限制 GPU 内存占用,并自动计算 micro-batch size 和 gradient accumulation,以满足你的目标 effective_batch_size
  • OSFT 实现了 Nayak et al., 2025 (arXiv:2504 .07097) —— 将权重更新限制在正交子空间中,可在没有 replay data 的情况下防止 catastrophic forgetting。
  • QLoRA 以 4-bit 方式加载冻结的 base model,并且只训练 LoRA adapters,使大型模型能够适配单个较小的 GPU 切片(Dettmers et al., 2023, arXiv:2305 .14314)。
  • Continued pre-training (CPT) 在原始文本语料上执行 next-token prediction,以便在 instruction tuning 之前注入领域知识。
  • 内置 checkpointing、experiment tracking 和 Liger kernel 支持。
设计背景

training_hub 是 RHOAI / Open Data Hub 用于通过单一 API 暴露后训练 算法的上游库,而 Alauda AI 则在 Kubeflow Trainer v2 (TrainJob / ClusterTrainingRuntime)上运行同一套代码。算法(此库) 与 distributed runtime(Kubeflow Trainer)之间的拆分遵循 Open Data Hub 的 architecture decision records (参见 distributed-workloadworkbenches 组件文档)——因此,这里的示例 在 workbench notebook 中运行,或作为集群 TrainJob 运行时,都会平滑映射到 相同的 sft / osft / lora_sft entrypoint。

方面SFTOSFTQLoRACPT
用例初始 instruction tuning已微调模型的持续领域适配节省内存的适配注入领域知识
数据Chat JSONLChat JSONLChat JSONL原始文本
可训练权重全部(bf16)正交子空间LoRA adapters(base 为 4-bit)全部(bf16)
关键参数标准 hyperparametersunfreeze_rank_ratio(0.0–1.0)load_in_4bit + lora_ris_pretraining + block_size
入口函数sft(...)osft(...)lora_sft(...)sft(..., is_pretraining=True)
后端instructlab-trainingmini-trainerunsloth / peft + bitsandbytesinstructlab-training

要求

  • 你的 cluster 中已安装 Alauda AI Workbench。
  • 一个具有互联网访问能力(或内部 PyPI 镜像)、至少一个 NVIDIA GPU,以及用于 checkpoint 的持久化存储的 workbench。
  • HuggingFace 模型名称或本地路径。
  • JSONL 格式的训练数据(见下文)。

数据格式

每一行都是一个对话:

{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is machine learning?"}, {"role": "assistant", "content": "Machine learning is a subset of AI..."}]}

角色:systemuserassistantpretraining。Masking:

  • SFT(默认)——只有 assistant 内容会对 loss 产生贡献。为某个 sample 添加 "unmask": true 可将所有非 system 内容都纳入训练。
  • OSFT——由 unmask_messages 控制(默认 False)。

如果数据集已预先 tokenized,并包含 input_ids / labels,则可以通过 use_processed_dataset=True 支持。

运行示例 notebooks

下载到你的 workbench 中,并逐 cell 执行:

NotebookAlgorithm下载
SFT comprehensive tutorialSFTsft-comprehensive-tutorial.ipynb
OSFT comprehensive tutorialOSFTosft-comprehensive-tutorial.ipynb
QLoRA comprehensive tutorialQLoRAqlora-comprehensive-tutorial.ipynb
CPT comprehensive tutorialCPTcpt-comprehensive-tutorial.ipynb

安装并配置:

pip install training-hub
# `training-hub` pulls a stray `attr` package that shadows `attrs.attr` and
# breaks `aiohttp`; uninstall it.
pip uninstall -y attr
NOTE

在预构建的 traininghub0.1-cu126-amd64:v0.1.0 runtime image 上,请在全新的 venv 中安装 training-hub ——因为 pip install --user training-hub 会将 transformers 升级到与随附 peft 不兼容的版本:

python -m venv /workspace/venv
source /workspace/venv/bin/activate
pip install training-hub
pip uninstall -y attr

编辑参数 cells:

model_path = "Qwen/Qwen2.5-7B-Instruct"        # HF name or local path
data_path       = "/path/to/your/training_data.jsonl"
ckpt_output_dir = "/path/to/checkpoints/my_experiment"
selected_distributed = "single_node_8gpu"      # or single_gpu_dev, multi_node_master, ...
# OSFT only:
unfreeze_rank_ratio = 0.25                      # 0.1–0.3 conservative, 0.3–0.5 balanced

内置的模型预设覆盖 Qwen 2.5 7B、Llama 3.1 8B、Phi 4 Mini,以及通用的 7B / 小型模型。

运行所有 cells。最后的训练 cell 会调用:

from training_hub import sft, osft

result = sft(model_path=model_path, data_path=data_path, ckpt_output_dir=ckpt_output_dir,
             effective_batch_size=128, max_tokens_per_gpu=20000, max_seq_len=16384,
             learning_rate=1e-5, num_epochs=3, nproc_per_node=8)

result = osft(model_path=model_path, data_path=data_path, ckpt_output_dir=ckpt_output_dir,
              unfreeze_rank_ratio=0.25,
              effective_batch_size=128, max_tokens_per_gpu=10000, max_seq_len=8192,
              learning_rate=5e-6, num_epochs=1, nproc_per_node=8)

checkpoint 会在每个 epoch 写入 ckpt_output_dir(由 checkpoint_at_epoch 控制)。

关键参数

通用(SFT 和 OSFT):

参数必需描述
model_pathHF 名称或本地路径
data_pathJSONL 训练数据
ckpt_output_dircheckpoint 目录
effective_batch_size全局有效 batch size
max_tokens_per_gpu每个 GPU 的 token 预算;自动计算 micro-batch size
max_seq_len最大序列长度
learning_rateoptimizer LR
num_epochs默认为 1
lr_scheduler, warmup_stepsLR 调度
use_ligerLiger kernels(OSFT 默认 True
seed默认为 42
data_output_dir处理后的数据缓存;RAM-disk 使用 "/dev/shm"
use_processed_dataset如果数据已包含 input_ids / labels,则跳过 tokenization
checkpoint_at_epoch, save_final_checkpoint默认为 True
nproc_per_node, nnodes, node_rank分布式拓扑
rdzv_id, rdzv_endpoint多节点 rendezvous

仅 OSFT:

参数必需描述
unfreeze_rank_ratio每个 weight matrix 中可更新的比例(0.0–1.0)。越低表示保留越多。
unmask_messages如果为 True,则在所有非 system 内容上训练
target_patterns用于将 OSFT 限定到特定层的子字符串模式

QLoRA(4-bit LoRA)

QLoRA 以 4-bit NormalFloat (NF4) 精度冻结 base model,并且只训练其上的小型 LoRA adapter 矩阵。对于完整 SFT 需要约 ~60 GiB 的 7B 模型,它可以在单个 16–24 GiB GPU(或 HAMI vGPU slice)上运行。只要你受限于 GPU 内存,就应当使用它; 代价是会有轻微的量化质量差距,以及稍慢的 step。

training_hub 通过 lora_sft(...) 提供 QLoRA——即 LoRA 加上 bitsandbytes 的 4-bit 选项:

from training_hub import lora_sft

result = lora_sft(
    model_path="Qwen/Qwen2.5-7B-Instruct",   # HF name or local path
    data_path="/path/to/training_data.jsonl", # same chat JSONL as SFT
    ckpt_output_dir="/path/to/checkpoints/qlora_run",
    # LoRA adapter
    lora_r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # or "all-linear"
    # 4-bit quantization — this is what makes it QLoRA
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype="bfloat16",
    bnb_4bit_use_double_quant=True,
    # core training (LoRA tolerates higher LRs than full SFT)
    num_epochs=3, effective_batch_size=16, learning_rate=2e-4, max_seq_len=4096,
    nproc_per_node=1,
)

QLoRA 专用参数:

参数必需描述
load_in_4bit是(用于 QLoRA)以 4-bit 加载冻结的 base model。若不设置,则 lora_sft 只是普通(16-bit)LoRA。
bnb_4bit_quant_type"nf4"(推荐)或 "fp4"
bnb_4bit_compute_dtype反量化计算 dtype,例如 "bfloat16"
bnb_4bit_use_double_quant对量化常量进行嵌套量化(额外节省内存)
lora_r, lora_alpha, lora_dropoutLoRA adapter 的 rank / scaling / dropout
target_modules要适配的 modules(attention projections,或 "all-linear"
load_in_8bit8-bit base(LoRA-8bit,而不是 QLoRA)

输出是一个 LoRA adapter,而不是完整 checkpoint。部署时,可使用 peft 加载 base + adapter, 或者调用一次 merge_and_unload() 将其合并并导出为独立模型。

NOTE

通过 bitsandbytes 实现的 4-bit QLoRA 需要具备计算能力 sm_75 或更高 (Turing / Ampere / Hopper)的 NVIDIA GPU。traininghub0.1-cu126-amd64 runtime 已经捆绑了 trlpeftbitsandbytes。默认的 lora_sft 后端是 unsloth; 如果需要完全控制,也可以在同一个 runtime image 上直接驱动 peft + bitsandbytes

NPU: bitsandbytes 4-bit 在 Huawei Ascend 上不可用。请在 llamafactory0.9-cann8.5-arm64 runtime 上使用不含 4-bit 的 LoRA(finetuning_type: lora) 作为参数高效路径——参见 Fine-tune and Pretrain on Ascend NPU

持续预训练 (CPT)

持续预训练 (CPT) 保持原始的 next-token prediction 目标,但将其运行在来自你的领域 (医疗、法律、代码、新语言)的 原始文本语料 上。它注入的是 知识和词汇——与 通过 chat 数据教授 行为 的 SFT/OSFT 不同。常见流水线是 CPT → SFT → alignment

training_hub 通过相同的 sft(...) entrypoint 运行 CPT,并设置 is_pretraining=True: loss 会覆盖 所有 token(不使用仅 assistant masking),并且文档会被打包到固定大小的 block_size window 中。

from training_hub import sft

result = sft(
    model_path="Qwen/Qwen2.5-7B",             # a BASE checkpoint
    data_path="/path/to/corpus.jsonl",         # raw text, one document per line
    ckpt_output_dir="/path/to/checkpoints/cpt_run",
    # continued pre-training
    is_pretraining=True,
    block_size=4096,                           # packed context-window length
    document_column_name="text",               # JSONL field holding the raw text
    # core training — keep the LR low to limit forgetting
    num_epochs=1, effective_batch_size=128, learning_rate=5e-6,
    max_seq_len=4096, max_tokens_per_gpu=20000,
    nproc_per_node=8,
)

CPT 数据是原始文本,不是 chat turns——在 document_column_name 下每行一个 document:

{"text": "A paragraph or document of raw domain text ..."}
{"text": "Another document ..."}

CPT 专用参数:

参数必需描述
is_pretraining切换到原始文本预训练(无 chat masking)
block_size打包后的上下文窗口长度(例如 1024–4096)
document_column_name保存原始文本的 JSONL 字段(默认 "text"

CPT 会更新 所有 权重并写出完整 checkpoint。它可能导致 general ability 的 catastrophic forgetting——可通过较低的 learning rate、混入一部分通用领域文本,以及随后执行一次 SFT/OSFT 来缓解(将 model_path 指向 CPT checkpoint)。如果 forgetting 是主要顾虑,请优先选择 OSFT

NOTE

NPU: 全参数持续预训练也可以通过 MindSpeed-LLM runtime(pretrain_gpt.py)在 Huawei Ascend 上运行。 参见 Fine-tune and Pretrain on Ascend NPUqwen25_pretrain_verify.ipynb 示例。

多节点

在每个节点上运行该 notebook(或脚本),并使用相同的 rdzv_id / rdzv_endpoint,以及不同的 node_rank

nproc_per_node = 8
nnodes         = 2
rdzv_id        = 42
rdzv_endpoint  = "10.0.0.1:29500"
node_rank      = 0   # 1 on the worker

在训练开始之前,所有节点都需要能够访问 rdzv_endpoint