LLaMA-Factory 微调实践手册

整理 LLaMA-Factory 的安装、训练命令、数据格式、评估、部署和故障排查。

GitHub链接: https://github.com/hiyouga/LLaMA-Factory

一、安装

git clone https://github.com/hiyouga/LLaMA-Factory.git  
conda create -n llama_factory python=3.10  
conda activate llama_factory  
cd LLaMA-Factory  
pip install -e .[metrics] #方式一
pip install -e ".[torch,metrics]" #方式二
#若遇到包冲突,可用pip install --no-deps -e .
pip install -e .[依赖项]

可选的额外依赖项:torch、torch-npu、metrics、deepspeed、liger-kernel、bitsandbytes、hqq、eetq、gptq、aqlm、vllm、sglang、galore、apollo、badam、adam-mini、qwen、minicpm_v、modelscope、openmind、swanlab、quality

二、校验

llamafactory-cli train -h

三、指令

llamafactory-cli

动作参数枚举 参数说明
version 显示版本信息
train 命令行版本训练
chat 命令行版本推理chat
export 模型合并和导出
api 启动API server,供接口调用
eval 使用mmlu等标准数据集做评测
webchat 前端版本纯推理的chat页面
webui 启动LlamaBoard前端页面,包含可视化训练,预测,chat,模型合并多个子页面<
model_name_or_path 参数的名称(huggingface或者modelscope上的标准定义,如“meta-llama/Meta-Llama-3-8B-Instruct”), 或者是本地下载的绝对路径,如/media/codingma/LLM/llama3/Meta-Llama-3-8B-Instruct
template 模型问答时所使用的prompt模板,不同模型不同,请参考https://github.com/hiyouga/LLaMA-Factory?tab=readme-ov-file#supported-models 获取不同模型的模板定义,否则会回答结果会很奇怪或导致重复生成等现象的出现。chat 版本的模型基本都需要指定,比如Meta-Llama-3-8B-Instruct的template 就是 llama3

其中参数model_name_or_path也可以提前把相关的参数存在yaml文件里,比如LLaMA-Factory/examples/inference/llama3.yaml at main · hiyouga/LLaMA-Factory, 本地位置是 examples/inference/llama3.yaml ,内容如下

model_name_or_path: /media/codingma/LLM/llama3/Meta-Llama-3-8B-Instruct  
template: llama3

训练指令

llamafactory-cli train 路径

转换为GGUF格式

安装llama.cpp并执行转换:

python llama.cpp/convert_hf_to_gguf.py 输入模型路径 --outfile 输出路径.gguf --outtype q8_0

脚本

基础训练配置

#!/bin/bash

CUDA_VISIBLE_DEVICES=0 llamafactory-cli train \
    --stage sft \
    --do_train \
    --do_eval \
    --model_name_or_path ./models/llama3-8b-instruct \
    --dataset my_dataset,alpaca_zh \
    --dataset_dir ./train_data \
    --template llama3 \
    --finetuning_type lora \
    --lora_target all \
    --output_dir ./saves/llama3-8b-lora \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 2048 \
    --preprocessing_num_workers 8 \
    --per_device_train_batch_size 2 \
    --per_device_eval_batch_size 1 \
    --gradient_accumulation_steps 8 \
    --lr_scheduler_type cosine \
    --logging_steps 50 \
    --save_steps 200 \
    --eval_steps 100 \
    --eval_strategy steps \
    --load_best_model_at_end \
    --learning_rate 1e-4 \
    --num_train_epochs 3.0 \
    --max_samples 5000 \
    --val_size 0.1 \
    --plot_loss \
    --fp16

高级训练配置(全参数微调)

#!/bin/bash

CUDA_VISIBLE_DEVICES=0,1,2,3 llamafactory-cli train \
    --stage sft \
    --do_train \
    --model_name_or_path ./models/llama3-8b-instruct \
    --dataset my_dataset \
    --dataset_dir ./train_data \
    --template llama3 \
    --finetuning_type full \
    --output_dir ./saves/llama3-8b-full \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 2048 \
    --preprocessing_num_workers 16 \
    --per_device_train_batch_size 1 \
    --gradient_accumulation_steps 16 \
    --lr_scheduler_type cosine \
    --logging_steps 10 \
    --save_steps 500 \
    --learning_rate 5e-6 \
    --num_train_epochs 2.0 \
    --max_grad_norm 1.0 \
    --warmup_ratio 0.1 \
    --lr_scheduler_type cosine \
    --bf16 \
    --deepspeed deepspeed_config.json

训练监控

#!/bin/bash

CUDA_VISIBLE_DEVICES=0,1,2,3 llamafactory-cli train \
    --stage sft \
    --do_train \
    --model_name_or_path ./models/llama3-8b-instruct \
    --dataset my_dataset \
    --dataset_dir ./train_data \
    --template llama3 \
    --finetuning_type full \
    --output_dir ./saves/llama3-8b-full \
    --overwrite_cache \
    --overwrite_output_dir \
    --cutoff_len 2048 \
    --preprocessing_num_workers 16 \
    --per_device_train_batch_size 1 \
    --gradient_accumulation_steps 16 \
    --lr_scheduler_type cosine \
    --logging_steps 10 \
    --save_steps 500 \
    --learning_rate 5e-6 \
    --num_train_epochs 2.0 \
    --max_grad_norm 1.0 \
    --warmup_ratio 0.1 \
    --lr_scheduler_type cosine \
    --bf16 \
    --deepspeed deepspeed_config.json

模型评估

自动评估
#!/bin/bash

echo "=== 开始模型评估 ==="

llamafactory-cli eval \
    --model_name_or_path ./models/llama3-8b-instruct \
    --adapter_name_or_path ./saves/llama3-8b-lora \
    --template llama3 \
    --finetuning_type lora \
    --task chat \
    --eval_dataset alpaca_zh \
    --dataset_dir ./train_data \
    --per_device_eval_batch_size 2 \
    --predict_with_generate \
    --max_samples 200 \
    --save_name quantitative_eval

llamafactory-cli train \
    --stage sft \
    --do_predict \
    --model_name_or_path ./models/llama3-8b-instruct \
    --adapter_name_or_path ./saves/llama3-8b-lora \
    --eval_dataset my_dataset \
    --dataset_dir ./train_data \
    --template llama3 \
    --finetuning_type lora \
    --output_dir ./saves/llama3-8b-lora/predict \
    --per_device_eval_batch_size 1 \
    --max_samples 20 \
    --predict_with_generate

echo "=== 评估完成 ==="
评估结果分析
import json
import matplotlib.pyplot as plt

def analyze_evaluation_results():
    """分析评估结果"""
    
    # 读取评估指标
    try:
        with open('./saves/llama3-8b-lora/quantitative_eval/eval_results.json', 'r') as f:
            results = json.load(f)
        
        print("=== 评估结果分析 ===")
        metrics = {
            "BLEU-4": results.get("predict_bleu-4", "N/A"),
            "ROUGE-1": results.get("predict_rouge-1", "N/A"),
            "ROUGE-2": results.get("predict_rouge-2", "N/A"), 
            "ROUGE-L": results.get("predict_rouge-l", "N/A")
        }
        
        for metric, value in metrics.items():
            print(f"{metric}: {value}")
            
        # 可视化指标
        if all(isinstance(v, (int, float)) for v in metrics.values()):
            plt.figure(figsize=(10, 6))
            plt.bar(metrics.keys(), metrics.values())
            plt.title("模型评估指标")
            plt.ylabel("分数")
            plt.xticks(rotation=45)
            plt.tight_layout()
            plt.savefig('./saves/llama3-8b-lora/evaluation_metrics.png')
            print("指标图表已保存")
            
    except FileNotFoundError:
        print("评估结果文件不存在")

if __name__ == "__main__":
    analyze_evaluation_results()

模型部署和使用

Web UI部署
llamafactory-cli webui \
    --model_name_or_path ./models/llama3-8b-instruct \
    --adapter_name_or_path ./saves/llama3-8b-lora \
    --template llama3 \
    --finetuning_type lora
API服务部署
llamafactory-cli api \
    --model_name_or_path ./models/llama3-8b-instruct \
    --adapter_name_or_path ./saves/llama3-8b-lora \
    --template llama3 \
    --finetuning_type lora \
    --port 8000
命令行测试
llamafactory-cli chat \
    --model_name_or_path ./models/llama3-8b-instruct \
    --adapter_name_or_path ./saves/llama3-8b-lora \
    --template llama3 \
    --finetuning_type lora

完整流程执行脚本

#!/bin/bash

set -e  # 遇到错误立即退出

echo "=== LLaMA-Factory 完整微调流程 ==="

echo "步骤1: 数据准备"
python data_prepare.py

echo "步骤2: 开始训练"
chmod +x basic_train.sh
./basic_train.sh &

echo "步骤3: 启动训练监控"
gnome-terminal -- python training_monitor.py

wait

echo "步骤4: 模型评估"
chmod +x evaluation.sh
./evaluation.sh

echo "步骤5: 结果分析"
python analyze_results.py

echo "步骤6: 部署模型"
echo "启动Web UI: llamafactory-cli webui --model_name_or_path ./models/llama3-8b-instruct --adapter_name_or_path ./saves/llama3-8b-lora"

echo "=== 流程完成 ==="

四、数据集

目前仅支持两种格式的数据集:alpaca 和 sharegpt。

alpaca

[
  {
    "instruction": "用户指令(必填)",
    "input": "用户输入(选填)",
    "output": "模型回答(必填)",
    "system": "系统提示词(选填)",
    "history": [
      ["第一轮指令(选填)", "第一轮回答(选填)"],
      ["第二轮指令(选填)", "第二轮回答(选填)"]
    ]
  }
]

在LLaMA-Factory项目中均使用dataset_info.json进行定义和管理,其存储位置在LLaMA-Factory/data目录下。 文件格式如下:

"数据集名称": {
  "hf_hub_url": "Hugging Face 的数据集仓库地址(若指定,则忽略 script_url 和 file_name)",
  "ms_hub_url": "ModelScope 的数据集仓库地址(若指定,则忽略 script_url 和 file_name)",
  "script_url": "包含数据加载脚本的本地文件夹名称(若指定,则忽略 file_name)",
  "file_name": "该目录下数据集文件的名称(若上述参数未指定,则此项必需)",
  "file_sha1": "数据集文件的 SHA-1 哈希值(可选,留空不影响训练)",
  "subset": "数据集子集的名称(可选,默认:None)",
  "folder": "Hugging Face 仓库的文件夹名称(可选,默认:None)",
  "ranking": "是否为偏好数据集(可选,默认:False)",
  "formatting": "数据集格式(可选,默认:alpaca,可以为 alpaca 或 sharegpt)",
  "columns(可选)": {
    "prompt": "数据集代表提示词的表头名称(默认:instruction)",
    "query": "数据集代表请求的表头名称(默认:input)",
    "response": "数据集代表回答的表头名称(默认:output)",
    "history": "数据集代表历史对话的表头名称(默认:None)",
    "messages": "数据集代表消息列表的表头名称(默认:conversations)",
    "system": "数据集代表系统提示的表头名称(默认:None)",
    "tools": "数据集代表工具描述的表头名称(默认:None)"
  },
  "tags(可选,用于 sharegpt 格式)": {
    "role_tag": "消息中代表发送者身份的键名(默认:from)",
    "content_tag": "消息中代表文本内容的键名(默认:value)",
    "user_tag": "消息中代表用户的 role_tag(默认:human)",
    "assistant_tag": "消息中代表助手的 role_tag(默认:gpt)",
    "observation_tag": "消息中代表工具返回结果的 role_tag(默认:observation)",
    "function_tag": "消息中代表工具调用的 role_tag(默认:function_call)",
    "system_tag": "消息中代表系统提示的 role_tag(默认:system,会覆盖 system 列)"
  }
}

所以对于alpaca格式的数据,dataset_info.json 中的 columns 应为:

"数据集名称": {
  "columns": {
    "prompt": "instruction",
    "query": "input",
    "response": "output",
    "system": "system",
    "history": "history"
  }
}

sharegpt

其标准形式如下:

[
  {
    "conversations": [
      {
        "from": "human",
        "value": "用户指令"
      },
      {
        "from": "gpt",
        "value": "模型回答"
      }
    ],
    "system": "系统提示词(选填)",
    "tools": "工具描述(选填)"
  }
]

关于sharegpt 格式,在dataset_info.json中的定义形式就是如下:

"数据集名称": {
  "columns": {
    "messages": "conversations",
    "system": "system",
    "tools": "tools"
  },
  "tags": {
    "role_tag": "from",
    "content_tag": "value",
    "user_tag": "human",
    "assistant_tag": "gpt"
  }
}

五、WebUI

模块一:模型与方法

参数 推荐值 说明
语言 zh 将界面切换为中文,方便操作。
模型名称 Qwen/Qwen2.5-VL-3B-Instruct LLaMA-Factory 会自动从 HuggingFace 或 ModelScope 下载。
模型路径 默认 若已有本地模型,可填写绝对路径。
微调方法 LoRA 低秩适应微调,在效果和资源消耗之间取得了最佳平衡,是目前的主流选择。
量化等级 none (不量化) 4-bit 量化可大幅节省显存,但对模型精度有轻微影响。初次训练建议不量化。
对话模板 qwen2_vl 至关重要。必须与模型(Qwen2.5-VL)严格匹配,否则模型无法正确理解输入。

模块二:训练设置

参数 推荐值 说明
训练阶段 Supervised Fine-Tuning 监督微调,适用于我们准备的“问答”式标注数据。
数据目录 ./pokemon_sharegpt 指向您准备好的数据集文件夹。
数据集 pokemon_multimodal 选中我们刚才在 dataset_info.json 中定义的数据集名称。
截断长度 4096 模型能处理的最大序列长度。对于图文模型,建议不低于 2048 以确保图像编码有足够空间。
学习率 2e-4 这是 LoRA 微调 3B 级别模型的黄金学习率。如果 Loss 不下降可升至 3e-4,若震荡则降至 1e-4。
训练轮数 3 对于中小规模数据集(< 10k 条),3-5 轮通常足够。过多轮次可能导致过拟合。
批处理大小 2 每张 GPU 一次处理的样本数。受显存限制,24GB 显存可尝试 2-4,16GB 建议 1-2。
梯度累积 8 “模拟”大批量训练的技巧。有效批量 = 批处理大小 × 梯度累积。这里有效批量为 16,是公认的稳定值。
计算类型 bf16 强烈推荐。适用于新架构显卡(A100, RTX 30/40系),数值稳定性优于 fp16。
学习率调节器 cosine 余弦退火调度器,能使学习率平滑下降,有助于模型收敛到更优的点。
验证集比例 0.1 从训练集中划分 10% 的数据用于验证,以监控模型是否过拟合。
输出目录 saves/qwen25-vl-pokemon-lora 保存 LoRA 权重、日志和训练图表的文件夹。
日志间隔 10 每训练 10 步在控制台和日志文件中输出一次 Loss 等信息。
保存间隔 500 每训练 500 步保存一次模型权重(checkpoint)。
LoRA 秩 64 LoRA 矩阵的维度。越大,可训练参数越多,拟合能力越强,但显存占用也越高。64 是一个很好的平衡点。
LoRA 缩放系数 128 通常设为 rank 的 2倍,这是一个广泛验证过的有效配置。
LoRA 随机丢弃 0.1 在 LoRA 模块中加入 Dropout,能有效防止过拟合,增强模型泛化能力。
LoRA 作用模块 all 将 LoRA 应用于模型的所有线性层。对于初学者来说,这是最简单且效果不错的选择。

训练过程监控与故障排除

监控显存

pip install nvitop
pip install nvdida-ml-py -u
nvitop -m auto
nvitop -m compact
nvitop -m full

监控关键指标

  • Loss:最重要的指标。你应该观察到 loss 值随着训练进行而持续下降,并在训练后期趋于平稳。
  • Learning Rate:会根据选择的 cosine 调度器从初始值 2e-4 平滑下降。
  • Loss 曲线图:训练完成后,在输出目录(saves/qwen25-vl-pokemon-lora)下会生成 training_loss.png。一条健康的曲线应平滑下降并收敛。

常见问题与解决方案

问题 可能原因 解决方案
CUDA out of memory 批量大小过大或截断长度过长。 1. 降低批处理大小 至 1。 2. 如仍溢出,降低 LoRA 秩 至 32。 3. 最终手段:降低截断长度。
Loss 不下降或上升 学习率过低或数据有问题。 1. 提高学习率 至 3e-4。 2. 仔细检查数据集格式和内容。
Loss 剧烈震荡 学习率过高。 降低学习率 至 1e-4。
训练速度过慢 硬件限制或配置问题。 1. 确认已安装 flash-attn。 2. 适当减少梯度累积步数。

模型评估与测试

训练完成后,切换到 Evaluate 标签页。

  1. 选择适配器:在 检查点路径 下拉框中,选择刚刚训练好的模型(位于 saves/... 目录下)。
  2. 配置评估:选择 pokemon_multimodoal 数据集,设置一个小的最大样本数(如 100)进行快速评估。

对话测试

  1. 切换到 Chat 标签页。
  2. 加载适配器:同样,在 适配器路径 中选中你的模型。
  3. 加载模型:点击 *加载模型 按钮,等待模型加载完成。
  4. 开始对话:

对比测试:要感受微调带来的提升,可以点击 卸载模型,然后不选择任何适配器,再次点击 加载模型 加载原始的 Qwen2.5-VL-3B-Instruct 模型,用同样的问题进行测试,对比效果差异。

进阶调优技巧

  • 混合数据集:在 WebUI 中可以同时选择多个数据集进行训练,这能提升模型的泛化能力。
  • 使用 LoRA+:在“高级设置”中勾选 使用 LoRA+ 并设置 LoRA+ 学习率比例 为 16.0。LoRA+ 是一种改进算法,理论上能以相同的成本提升模型性能,值得尝试。
  • 调整 LoRA 目标:如果发现模型在图像理解上出现问题,可以尝试仅微调语言部分。在 LoRA 作用模块 中手动填入 q_proj,v_proj,k_proj,o_proj 等,避免改动视觉编码器。

计算token

#在llamafactory目录中scripts/stat_utils/length_cdf.py
torchrun --nproc_per_node=1 scripts/stat_utils/length_cdf.py \
  --model_name_or_path 模型路径 \
  --dataset 文件名 \
  --dataset_dir path \
  --template 对话模板
例如
torchrun --nproc_per_node=1 scripts/stat_utils/length_cdf.py \
  --model_name_or_path /root/autodl-tmp/Qwen/Qwen2.5-7B-Instruct \
  --dataset hanjie \
  --dataset_dir /root/autodl-tmp/LLaMA-Factory/datasets/hanjie \
  --template qwen

外网访问llamafactory(在autodl平台上)

要使用公有连接要autodl-tmp/LLaMA-Factory/src/llamafactory/webui/interface.py文件下最后改成share=True

image-20260125191959880

使用webui前开启学术加速,好像因为它会下载一个包,我也忘记了,不开代理可能会下载失败

source /etc/network_turbo
llamafactory-cli webui
然后在
unset http_proxy && unset https_proxy
llamafactory-cli webui

如果不unset http_proxy && unset https_proxy会出现

image-20260125192407063

GitHub链接:https://github.com/InternLM/xtuner

GitHub链接:https://github.com/axolotl-ai-cloud/axolotl

GitHub链接:https://github.com/deepspeedai/DeepSpeed

GitHub链接:https://github.com/unslothai/unsloth?spm=a2c6h.12873639.article-detail.4.19413e9fDtDunu