1. 理解Llama Factory的微调数据集格式限制
当我在实际项目中首次接触Llama Factory时,发现它对微调数据集的格式要求确实比较严格。官方文档明确指出,目前仅支持Alpaca和ShareGPT两种标准格式。这种限制主要源于框架设计时的架构决策——通过规范化的输入格式简化数据处理流程,确保训练过程的稳定性。
Alpaca格式的特点是结构清晰,每个样本包含instruction(指令)、input(输入)、output(输出)三个核心字段,适合单轮指令微调场景。而ShareGPT格式则更灵活,采用对话式的conversations数组,支持多角色交互(如human、gpt、function等),适合复杂的多轮对话微调。
关键区别:Alpaca格式的样本是独立的结构化指令对,而ShareGPT格式保留了完整的对话上下文关系。
2. GLM4数据集格式解析
GLM4作为另一个主流大模型,其推荐的微调格式确实与Llama Factory存在差异。典型的GLM4数据集格式如下例所示:
json复制{
"prompt": "解释量子计算的基本原理",
"response": "量子计算利用量子比特的叠加态和纠缠效应...",
"history": [
["什么是量子比特?", "量子比特是量子计算的基本单位..."],
["与传统比特有何不同?", "传统比特只能是0或1..."]
]
}
这种格式与Alpaca的主要差异在于:
- 使用prompt/response而非instruction/output的字段命名
- history字段的结构是字符串二元组而非Alpaca的对象数组
- 缺少system字段的系统提示支持
在实际测试中,直接将GLM4格式数据导入Llama Factory会导致解析错误,因为框架无法自动识别这些字段映射关系。
3. 格式转换的三种实战方案
3.1 使用Python脚本进行格式转换
这是我在项目中采用的主要方法。以下是一个完整的转换脚本示例,可将GLM4格式转为Alpaca格式:
python复制import json
def convert_glm4_to_alpaca(input_file, output_file):
with open(input_file, 'r', encoding='utf-8') as f:
glm4_data = json.load(f)
alpaca_data = []
for item in glm4_data:
new_item = {
"instruction": item["prompt"],
"input": "",
"output": item["response"],
"history": []
}
if "history" in item:
for hist in item["history"]:
new_item["history"].append([hist[0], hist[1]])
alpaca_data.append(new_item)
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(alpaca_data, f, ensure_ascii=False, indent=2)
# 使用示例
convert_glm4_to_alpaca("glm4_data.json", "alpaca_data.json")
这个脚本处理了几个关键点:
- 将prompt映射为instruction
- 保留response作为output
- 转换history的数组结构
- 处理可能的字符编码问题
3.2 修改dataset_info.json配置文件
Llama Factory允许通过修改dataset_info.json来扩展格式支持。以下是适配GLM4格式的配置示例:
json复制{
"glm4_dataset": {
"file_name": "glm4_data.json",
"columns": {
"prompt": "prompt",
"response": "response",
"history": "history"
},
"formatting": "custom",
"preprocessor": "path/to/custom_preprocessor.py"
}
}
需要配套编写自定义预处理脚本,这种方法适合需要频繁使用GLM4格式的场景。我在一个长期项目中采用此方案,减少了每次转换的时间成本。
3.3 使用中间格式转换工具
对于不想写代码的用户,可以考虑使用格式转换工具链:
- 先将GLM4格式转为JSONL
- 使用jq命令行工具处理字段映射
- 最后转换为目标格式
示例命令:
bash复制cat glm4_data.json | jq -c '.[] | {instruction: .prompt, input: "", output: .response}' > alpaca_data.jsonl
这种方法适合在CI/CD流水线中自动化执行,但灵活性不如Python脚本。
4. 多轮对话数据的特殊处理
当GLM4数据包含多轮对话时(history字段),需要特别注意对话轮次的完整性。我的经验是:
- 确保每轮对话的提问和回答成对出现
- 对于中断的对话轮次,建议补全或删除不完整数据
- 在转换时保留对话顺序标记
一个处理多轮对话的增强版转换逻辑:
python复制def convert_multi_turn_glm4(input_file, output_file):
with open(input_file, 'r') as f:
data = json.load(f)
processed = []
for item in data:
if not item.get("history"):
# 单轮对话直接转换
processed.append({
"instruction": item["prompt"],
"input": "",
"output": item["response"]
})
else:
# 多轮对话需要拆分为多个训练样本
for i in range(len(item["history"])):
new_item = {
"instruction": item["history"][i][0],
"input": "",
"output": item["history"][i][1],
"history": item["history"][:i]
}
processed.append(new_item)
# 添加当前prompt作为最后一轮
processed.append({
"instruction": item["prompt"],
"input": "",
"output": item["response"],
"history": item["history"]
})
with open(output_file, 'w') as f:
json.dump(processed, f, indent=2)
5. 质量检查与验证
完成格式转换后,必须进行数据验证。我通常采用三级检查:
-
结构检查:使用JSON Schema验证器确保格式合规
python复制from jsonschema import validate schema = { "type": "array", "items": { "type": "object", "properties": { "instruction": {"type": "string"}, "input": {"type": "string"}, "output": {"type": "string"} }, "required": ["instruction", "output"] } } validate(instance=converted_data, schema=schema) -
内容抽样:人工检查5%的样本,确保语义转换正确
-
训练试运行:用少量数据测试能否正常开始训练
6. 性能优化技巧
处理大型数据集时,我总结了几个提升效率的方法:
-
流式处理:对于超大规模数据,使用ijson库进行流式解析
python复制import ijson def stream_convert(input_file): with open(input_file, 'rb') as f: for item in ijson.items(f, 'item'): yield convert_item(item) -
并行处理:利用multiprocessing加速转换
python复制from multiprocessing import Pool with Pool(8) as p: results = p.map(convert_func, data_chunks) -
内存映射:对于超大型文件,使用mmap减少内存占用
7. 常见问题排查
在实际项目中遇到过几个典型问题:
问题1:转换后训练时出现"KeyError: 'instruction'"
- 原因:字段映射不正确
- 解决:检查dataset_info.json中的columns配置
问题2:多轮对话的顺序错乱
- 原因:history字段的时间顺序错误
- 解决:在转换时添加轮次编号并排序
问题3:特殊字符导致解析失败
- 原因:JSON中包含控制字符
- 解决:使用json.dumps的ensure_ascii=False参数
问题4:训练时loss异常高
- 原因:可能是格式转换导致语义失真
- 解决:检查样本中的instruction-output对应关系
8. 高级应用:自定义预处理钩子
对于需要复杂转换的场景,可以实现自定义预处理钩子。在Llama Factory中注册预处理函数:
python复制from llama_factory.data import register_preprocessor
@register_preprocessor("glm4")
def glm4_preprocessor(item):
return {
"instruction": item["prompt"],
"output": item["response"],
"metadata": {
"source": "glm4",
"original_data": item
}
}
然后在配置中指定:
json复制{
"preprocessor": "glm4"
}
这种方法保持了原始数据的可追溯性,适合需要AB测试的场景。
