LLaMA-Factory Function Call训练完整指南
1. 同步上游和本地更新代码
1.1 同步上游代码
# 进入LLaMA-Factory目录
cd LLaMA-Factory
# 添加上游仓库(如果还没有添加)
git remote add upstream https://github.com/hiyouga/LLaMA-Factory.git
# 获取上游最新代码
git fetch upstream
# 合并上游代码到本地分支
git merge upstream/main
1.2 处理冲突
如果出现冲突,需要手动解决冲突后提交:
# 解决冲突后
git add .
git commit -m "Merge upstream changes"
2. Dataset使用说明
2.1 数据组织结构
自定义数据全部放在 LLaMA-Factory/data/dataset 目录下,按照训练日期分割:
LLaMA-Factory/data/dataset/
├── 8_29/ # 8月29日的数据
├── 8_30/ # 8月30日的数据
├── 9_10/ # 9月10日的数据
│ └── function_call_data/ # function call数据目录
├── 9_14/ # 9月14日的数据
└── preprocess_dara/ # 数据预处理脚本目录
2.2 Function Call数据拼接
分散的function_call数据拼接成为多轮对话格式:
数据位置:LLaMA-Factory/data/dataset/9_10/function_call_data/ 中的json数据
拼接脚本:LLaMA-Factory/data/dataset/preprocess_dara/convert_to_function_call_format.py
重要提示:
- 拼接的对话格式中,偶数轮的信息才能被学习到,奇数轮的信息不能被学习到
- 在构造hardmatch数据时需要特别注意这个规则
使用示例:
cd /home/ziqiang/LLaMA-Factory/data/dataset/preprocess_dara
python convert_to_function_call_format.py \
--input_dir ../9_10/raw_data \
--output_dir ../9_10/function_call_data \
--output_file function_call_train.json
2.3 数据合并和格式转换
2.3.1 混合训练数据合并
由于MCP function call使用多轮对话训练集结构,而价格服务部分使用单轮instruction input output格式,混训时需要统一格式。
脚本:LLaMA-Factory/data/dataset/preprocess_dara/merge_and_convert_data.py
功能:
- 将不同格式的数据转换为统一的sharegpt格式
- 自动在dataset_info.json中创建相应条目
- 支持多种数据格式的混合训练
使用示例:
python merge_and_convert_data.py \
--function_call_data ../9_10/function_call_data/function_call_train.json \
--price_service_data ../9_10/price_service_data.json \
--output_file ../9_14/mixed_training_data.json \
--dataset_name mixed_training_data
2.3.2 简单JSON文件合并
如果只需要合并不同的JSON文件:
脚本:LLaMA-Factory/data/dataset/preprocess_dara/merge_json_files.py
使用示例:
python merge_json_files.py \
--input_files file1.json file2.json file3.json \
--output_file merged_data.json
2.4 创建dataset_info.json条目
每次新建dataset进行训练时,需要在 LLaMA-Factory/data/dataset_info.json 中创建相应条目。
自动创建:merge_and_convert_data.py 脚本目前支持自动添加条目
手动创建示例:
{
"mixed_training_data": {
"file_name": "/home/ziqiang/LLaMA-Factory/data/dataset/9_14/mixed_training_data.json",
"formatting": "sharegpt",
"columns": {
"messages": "conversations",
"system": "system",
"tools": "tools"
}
}
}
3. 训练和评估命令
3.1 训练命令
3.1.1 基础训练命令
CUDA_VISIBLE_DEVICES=6 llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen3 \
--flash_attn auto \
--dataset_dir data \
--dataset mixed_training_data_09_17 \
--cutoff_len 8192 \
--learning_rate 5e-05 \
--num_train_epochs 5 \
--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 \
--packing False \
--enable_thinking False \
--output_dir /home/ziqiang/LLaMA-Factory/saves/Qwen3-8B/lora/train_$(date +%Y-%m-%d-%H-%M) \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0 \
--lora_target all
3.1.2 带调试日志的训练命令
# 将训练日志同时输出到控制台和文件
CUDA_VISIBLE_DEVICES=6,7 llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen3 \
--flash_attn auto \
--dataset_dir data \
--dataset mixed_training_data_09_17 \
--cutoff_len 8192 \
--learning_rate 5e-05 \
--num_train_epochs 5 \
--max_samples 100000 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 16 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--enable_thinking False \
--output_dir /home/ziqiang/LLaMA-Factory/saves/Qwen3-8B/lora/train_$(date +%Y-%m-%d-%H-%M) \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0.1 \
--lora_target all \
--gradient_checkpointing True \
2>&1 | tee token_debug_current.log
CUDA_VISIBLE_DEVICES=6 llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen3 \
--flash_attn auto \
--dataset_dir data \
--dataset mixed_training_data_09_17 \
--cutoff_len 8192 \
--learning_rate 5e-05 \
--num_train_epochs 1 \
--max_samples 100000 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 16 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--enable_thinking False \
--output_dir /home/ziqiang/LLaMA-Factory/saves/Qwen3-8B/lora/train_$(date +%Y-%m-%d-%H-%M) \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 4 \
--lora_alpha 8 \
--lora_dropout 0.1 \
--lora_target all \
--gradient_checkpointing True \
2>&1 | tee token_debug_current.log
3.1.3 调试Token限制问题的训练命令
# 使用较小的cutoff_len来观察截断情况
CUDA_VISIBLE_DEVICES=6 llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template qwen3 \
--flash_attn auto \
--dataset_dir data \
--dataset mixed_training_data_09_17 \
--cutoff_len 8192 \
--learning_rate 5e-05 \
--num_train_epochs 5 \
--max_samples 1000 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 1 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 1 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--enable_thinking False \
--output_dir /home/ziqiang/LLaMA-Factory/saves/Qwen3-8B/lora/debug_$(date +%Y-%m-%d-%H-%M) \
--bf16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0 \
--lora_target all
重要参数说明:
--dataset:指定在dataset_info.json中注册的条目名称--output_dir:LoRA模型保存位置--enable_thinking:是否训练模型的think模型--cutoff_len:Token长度限制,调试时建议使用2048观察截断情况--max_samples:调试时建议使用较小值(如1000)快速验证2>&1 | tee:将标准输出和错误输出同时显示在控制台和保存到文件
3.2 评估命令
CUDA_VISIBLE_DEVICES=6 llamafactory-cli train \
--stage sft \
--do_predict True \
--model_name_or_path /data/models/Qwen3-8B \
--adapter_name_or_path /home/ziqiang/LLaMA-Factory/saves/Qwen3-8B/lora/train_2025-09-14-10-48-context/checkpoint-430 \
--preprocessing_num_workers 8 \
--finetuning_type lora \
--template qwen3 \
--flash_attn auto \
--dataset_dir data \
--eval_dataset test_data \
--cutoff_len 2048 \
--per_device_eval_batch_size 8 \
--predict_with_generate True \
--max_new_tokens 1024 \
--do_sample False \
--temperature 0.0 \
--top_p 1.0 \
--bf16 True \
--trust_remote_code True \
--output_dir Qwen3-8B/eval_results/9_14
重要参数说明:
--eval_dataset:指定在dataset_info.json中注册的测试数据集名称--output_dir:评估结果保存位置
3.3 评估结果分析
评估后会生成 generated_predictions.jsonl 文件,包含每条数据的label和predict。
价格服务评估脚本:LLaMA-Factory/data/dataset/preprocess_dara/eval_by_field.py
使用示例:
python eval_by_field.py \
--predictions_file Qwen3-8B/eval_results/9_14/generated_predictions.jsonl \
--output_file eval_results_by_field.json
4. Function Call训练数据的Loss Mask机制详解
4.1 数据转换过程
原始对话数据:
{
"conversations": [
{"from": "system", "value": "你是一个智能助手..."},
{"from": "human", "value": "报表编号H20250611的收入类型分布情况能分析一下吗?"},
{"from": "function_call", "value": "{\"name\": \"analyze_revenue_by_type\", \"arguments\": {...}}"},
{"from": "observation", "value": "工具返回的结果..."},
{"from": "gpt", "value": "<answer>根据分析结果...</answer>"}
]
}
转换为Token序列:
<|im_start|>system
你是一个智能助手...
<|im_end|>
<|im_start|>user
报表编号H20250611的收入类型分布情况能分析一下吗?
<|im_end|>
<|im_start|>assistant
<tool_call>
{"name": "analyze_revenue_by_type", "arguments": {...}}
</tool_call>
<|im_end|>
<|im_start|>user
<tool_response>
工具返回的结果...
</tool_response>
<|im_end|>
<|im_start|>assistant
<answer>根据分析结果...</answer>
<|im_end|>
4.2 Loss Mask应用
Input IDs (完整序列):
[系统消息tokens] [用户查询tokens] [工具调用tokens] [工具结果tokens] [最终回答tokens]
Labels (用于计算loss):
[ IGNORE_INDEX ] [ IGNORE_INDEX ] [工具调用tokens] [ IGNORE_INDEX ] [最终回答tokens]
(不训练) (不训练) (训练) (不训练) (训练)
4.3 训练目标
模型学习的目标是:
- 根据用户查询生成正确的工具调用 - 从用户输入预测function_call部分
- 根据工具结果生成合适的回答 - 从工具返回结果预测最终答案
模型不会学习:
- 生成工具返回结果(observation部分被mask)
- 重复用户输入或系统提示
4.4 关键代码实现
Template中的格式定义:
# Qwen3模板
format_observation=StringFormatter(
slots=["<|im_start|>user\n<tool_response>\n{{content}}\n</tool_response><|im_end|>\n<|im_start|>assistant\n"]
)
Loss计算时的mask:
# 在supervised.py中
if self.data_args.train_on_prompt:
source_label = source_ids
else:
source_label = [IGNORE_INDEX] * source_len # 输入部分被mask
# observation部分会被自动识别并mask为IGNORE_INDEX
5. Token调试日志系统
5.1 概述
为了帮助开发者更好地理解和调试ShareGPT格式训练中的token限制问题,我们在关键代码位置添加了详细的调试日志系统。
5.2 调试日志类型
5.2.1 [TEMPLATE_DEBUG] - 模板编码阶段
位置: src/llamafactory/data/template.py 的 encode_multiturn 函数
记录内容:
- 输入messages数量
- 编码后messages数量
- 生成的pairs数量和每个pair的token长度
示例输出:
[TEMPLATE_DEBUG] encode_multiturn开始
[TEMPLATE_DEBUG] 输入messages数量: 6
[TEMPLATE_DEBUG] 编码后messages数量: 6
[TEMPLATE_DEBUG] 生成的pairs数量: 3
[TEMPLATE_DEBUG] Pair 1: source=120 tokens, target=80 tokens
[TEMPLATE_DEBUG] Pair 2: source=1300 tokens, target=50 tokens
[TEMPLATE_DEBUG] Pair 3: source=400 tokens, target=200 tokens
5.2.2 [TOKEN_DEBUG] - 数据编码阶段
位置: src/llamafactory/data/processor/supervised.py 的 _encode_data_example 函数
记录内容:
- 原始conversations长度
- 编码后的pairs数量
- 每个pair的原始长度和截断后长度
- 剩余预算和累计长度
- 最终结果和使用率
示例输出:
[TOKEN_DEBUG] 开始处理数据样本
[TOKEN_DEBUG] 原始conversations长度: 6 条消息
[TOKEN_DEBUG] 编码后的pairs数量: 3
[TOKEN_DEBUG] 初始total_length: 0
[TOKEN_DEBUG] cutoff_len: 2048
[TOKEN_DEBUG] === Pair 1 ===
[TOKEN_DEBUG] 原始长度: source=120, target=80
[TOKEN_DEBUG] 剩余预算: 2048
[TOKEN_DEBUG] 截断后长度: source=120->120, target=80->80
[TOKEN_DEBUG] 当前累计长度: 200/2048
[TOKEN_DEBUG] === Pair 2 ===
[TOKEN_DEBUG] 原始长度: source=1300, target=50
[TOKEN_DEBUG] 剩余预算: 1848
[TOKEN_DEBUG] 截断后长度: source=1300->1300, target=50->50
[TOKEN_DEBUG] 当前累计长度: 1550/2048
[TOKEN_DEBUG] === Pair 3 ===
[TOKEN_DEBUG] 原始长度: source=400, target=200
[TOKEN_DEBUG] 剩余预算: 498
[TOKEN_DEBUG] 截断后长度: source=400->298, target=200->200
[TOKEN_DEBUG] ⚠️ source被截断: 102 tokens
[TOKEN_DEBUG] 当前累计长度: 2048/2048
[TOKEN_DEBUG] === 最终结果 ===
[TOKEN_DEBUG] 最终input_ids长度: 2048
[TOKEN_DEBUG] 最终labels长度: 2048
[TOKEN_DEBUG] 最终total_length: 2048
[TOKEN_DEBUG] 使用率: 2048/2048 (100.0%)
[TOKEN_DEBUG] 处理完成
5.2.3 [INFER_SEQLEN] - 截断策略阶段
位置: src/llamafactory/data/processor/processor_utils.py 的 infer_seqlen 函数
记录内容:
- 截断策略的选择过程
- 输入输出参数
- 具体的截断逻辑
示例输出:
[INFER_SEQLEN] 输入: source_len=400, target_len=200, cutoff_len=498
[INFER_SEQLEN] 条件1: target_len*2 < cutoff_len (200*2=400 < 498)
[INFER_SEQLEN] 策略1: target完全保留,截断source
[INFER_SEQLEN] 输出: source_len=400->298, target_len=200->200
5.3 日志查看方法
5.3.1 运行训练并记录日志
CUDA_VISIBLE_DEVICES=0,1 llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--dataset your_dataset \
--cutoff_len 2048 \
--output_dir ./debug_output \
2>&1 | tee debug_train.log
5.3.2 过滤特定日志
# 查看所有token调试日志
grep 'TOKEN_DEBUG' debug_train.log
# 查看截断策略日志
grep 'INFER_SEQLEN' debug_train.log
# 查看模板编码日志
grep 'TEMPLATE_DEBUG' debug_train.log
# 查看截断事件
grep '⚠️' debug_train.log
# 查看使用率统计
grep '使用率:' debug_train.log
5.3.3 使用分析脚本
# 分析日志文件
python analyze_token_logs.py debug_train.log
5.4 关键指标解读
5.4.1 使用率 (Usage Rate)
- 100%: 完全使用cutoff_len,可能有截断
- <100%: 未完全使用,数据较短
- >100%: 不可能出现,检查日志
5.4.2 截断事件
- source被截断: 通常是observation内容过长
- target被截断: 通常是assistant回复被截断
- 预算耗尽: 后续pairs被完全丢弃
5.4.3 截断策略
- 策略1: target完全保留,截断source (target_len * 2 < cutoff_len)
- 策略2: source完全保留,截断target (source_len * 2 < cutoff_len)
- 策略3: 按比例截断source和target
5.5 优化建议
基于日志分析结果:
如果observation经常被截断:
- 增加cutoff_len到4096或8192
- 压缩observation内容长度
如果assistant回复被截断:
- 这是最严重的问题,必须解决
- 优先增加cutoff_len
如果使用率很低:
- 考虑减少cutoff_len以提高训练效率
如果经常预算耗尽:
- 数据过长,需要预处理压缩
5.6 日志文件位置
训练过程中的日志会输出到以下位置:
统一调试日志文件:
token_debug_YYYYMMDD_HHMMSS.log(自动生成时间戳)- 包含所有
[TOKEN_DEBUG]、[TEMPLATE_DEBUG]、[INFER_SEQLEN]日志 - 自动轮转:文件大小达到50MB时自动创建新文件
- 自动清理:保留3天的历史日志
- 异步写入:不影响训练性能
- 包含所有
控制台输出: 直接显示在终端(彩色格式)
重定向文件: 如果使用
tee命令,会保存到指定文件训练日志: 在
output_dir目录下的trainer_log.jsonl文件系统日志: 根据系统配置,可能输出到
/var/log/或其他位置
推荐做法:
# 将日志同时输出到控制台和文件
CUDA_VISIBLE_DEVICES=0,1 llamafactory-cli train [参数] 2>&1 | tee training_$(date +%Y%m%d_%H%M%S).log
# 或者只保存到文件
CUDA_VISIBLE_DEVICES=0,1 llamafactory-cli train [参数] > training.log 2>&1
查看统一调试日志:
# 实时查看调试日志
tail -f token_debug_*.log
# 过滤特定类型的调试信息
grep "TOKEN_DEBUG" token_debug_*.log
grep "TEMPLATE_DEBUG" token_debug_*.log
grep "INFER_SEQLEN" token_debug_*.log
# 查看截断事件
grep "截断" token_debug_*.log
# 统计使用率
grep "使用率" token_debug_*.log | tail -10
5.7 注意事项
- 性能影响: 调试日志会增加训练时间,建议只在调试时使用
- 日志量: 日志量较大,建议重定向到文件
- 生产环境: 生产环境建议移除或注释掉调试日志
- 存储空间: 长时间训练会产生大量日志,注意磁盘空间
6. 快速参考
6.1 常用调试命令
# 快速调试token截断问题
CUDA_VISIBLE_DEVICES=6 llamafactory-cli train \
--stage sft --do_train True \
--model_name_or_path /data/models/Qwen3-8B \
--dataset your_dataset --cutoff_len 2048 \
--max_samples 100 --num_train_epochs 1 \
--output_dir ./debug_output \
2>&1 | tee debug.log
# 查看截断事件
grep '⚠️' debug.log
# 查看使用率统计
grep '使用率:' debug.log
# 分析日志
python analyze_token_logs.py debug.log
6.2 日志文件位置总结
| 日志类型 | 位置 | 说明 |
|---|---|---|
| 统一调试日志 | token_debug_*.log |
包含所有[TOKEN_DEBUG]等标记的调试信息 |
| 控制台输出 | 终端 | 实时显示,训练时可见 |
| 重定向文件 | training_*.log |
使用tee命令保存的完整日志 |
| 训练日志 | output_dir/trainer_log.jsonl |
LLaMA-Factory生成的训练日志 |
6.3 关键文件路径
LLaMA-Factory/
├── src/llamafactory/data/
│ ├── processor/supervised.py # TOKEN_DEBUG日志
│ ├── processor/processor_utils.py # INFER_SEQLEN日志
│ └── template.py # TEMPLATE_DEBUG日志
├── configure_token_logs.py # 日志配置脚本
├── token_debug_*.log # 统一调试日志文件
├── test_token_debug.py # 测试脚本
├── analyze_token_logs.py # 日志分析脚本
└── TOKEN_DEBUG_README.md # 快速使用指南
6.4 故障排除
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 日志不显示 | 日志级别设置 | 检查logging配置 |
| 截断频繁 | cutoff_len太小 | 增加cutoff_len到4096或8192 |
| 使用率低 | 数据过短 | 考虑减少cutoff_len |
| 训练效果差 | assistant被截断 | 优先解决截断问题 |