微调数据集格式转换,不同框架之间数据集互转脚本
微调数据集格式转换是大模型训练前绕不开的一步。
不同框架(比如 PyTorch + HuggingFace、PaddlePaddle、LLaMA Factory)对训练数据的格式要求并不一样,有的是 JSONL,有的是 JSON,还有 Alpaca、ShareGPT 这些对话模板。
本文直接用 Python 脚本演示几种常见格式的互转,并告诉你转换时最容易踩的坑。
先搞清楚常见的数据集格式结构
转换之前必须先认格式,否则脚本写出来也容易出错。
目前主流的微调数据集格式有这几种:
- Alpaca 格式:每条数据包含
instruction、input、output三个字段,input可以为空字符串。 - ShareGPT 格式:每条数据包含
conversations列表,每个元素是from和value,from取human或gpt。 - HuggingFace Dataset 格式:通常是一个 JSON/JSONL 文件,字段名按任务而定,也可能直接是 Arrow 格式。
- 纯对话格式:只有
prompt和response两个字段,常用于基础指令微调。
不同框架会读取不同字段,所以转换脚本的核心就是「字段映射」。
准备环境
不需要安装额外框架,只需要本机有 Python 3.7 以上。
为了读写 JSONL,建议用标准库 json。
如果你的原始数据是 CSV,还需要 pandas,但这里我们尽量用标准库实现,减少依赖。
创建一个工作目录,比如 dataset_convert,在里面放一个 convert.py 脚本。
实战:Alpaca 格式转 JSONL
假设你手里是一份 Alpaca 格式的 JSON 文件,内容如下:
[
{
"instruction": "用一句话描述春天",
"input": "",
"output": "春天是万物复苏的季节。"
}
]
现在要转成 HuggingFace 常用的 JSONL 格式,也就是每一行一个 JSON 对象,字段改成 prompt 和 response。
import json
# 读取 Alpaca 格式 JSON
with open('alpaca.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# 转成 JSONL
with open('train.jsonl', 'w', encoding='utf-8') as f:
for item in data:
# 这里把 input 拼接进 prompt,如果为空就直接用 instruction
prompt = item['instruction']
if item.get('input'):
prompt += "\n" + item['input']
new_item = {
"prompt": prompt,
"response": item['output']
}
f.write(json.dumps(new_item, ensure_ascii=False) + '\n')
运行后你会得到一个 train.jsonl,每行是一个完整的 JSON。
有些框架在训练时不需要 prompt 字段,而是要求 instruction 和 output,那只要把 new_item 改成对应字段名即可。
实战:ShareGPT 格式转 Alpaca
再举一个反过来的例子。
下面是一份 ShareGPT 风格的数据:
{
"conversations": [
{"from": "human", "value": "你好"},
{"from": "gpt", "value": "你好,有什么可以帮你?"},
{"from": "human", "value": "推荐一本Python书"},
{"from": "gpt", "value": "《Python编程:从入门到实践》不错。"}
]
}
要转成 Alpaca 格式,需要把多轮对话拆开或者合并成一条指令和输出。
通常更合理的做法是转为「多轮对话」结构,但 Alpaca 本身是单轮的。
如果强行转,建议只保留第一轮,或者把整个对话内容作为输出。
import json
with open('sharegpt.json', 'r', encoding='utf-8') as f:
data = json.load(f)
output = []
for conv in data['conversations']:
if conv['from'] == 'human':
instruction = conv['value']
elif conv['from'] == 'gpt':
output.append({
"instruction": instruction,
"input": "",
"output": conv['value']
})
with open('alpaca_converted.json', 'w', encoding='utf-8') as f:
json.dump(output, f, ensure_ascii=False, indent=2)
注意这个脚本会把多轮对话拆成多条单轮数据,可能丢失上下文。
如果框架支持 ShareGPT,保留 conversations 字段更安全。
通用互转脚本思路:字段映射 + 批量处理
与其每次手工改,不如写一个通用的转换脚本。
核心逻辑是:先定义一个映射字典,把当前字段名映射为目标字段名,再按目标格式重组数据。
import json
def convert(input_file, output_file, mapping):
"""
mapping 示例: {"instruction": "prompt", "output": "response"}
"""
with open(input_file, 'r', encoding='utf-8') as f:
content = f.read()
# 自动判断是 JSON 还是 JSONL
if content.lstrip()[0] == '[':
data = json.loads(content)
else:
data = [json.loads(line) for line in content.strip().split('\n') if line]
converted = []
for item in data:
new_item = {}
for src, dst in mapping.items():
if src in item:
new_item[dst] = item[src]
converted.append(new_item)
with open(output_file, 'w', encoding='utf-8') as f:
if output_file.endswith('.jsonl'):
for item in converted:
f.write(json.dumps(item, ensure_ascii=False) + '\n')
else:
json.dump(converted, f, ensure_ascii=False, indent=2)
使用时只要传入对应的映射关系,比如从 Alpaca 转 JSONL:
python convert.py
脚本内调用方式自己修改即可。
实际项目中还需要处理 input 字段的拼接,这里只是让你理解映射思路。
避坑指南:这些坑很容易导致训练时报错
转换数据集最大的问题不是脚本,而是字段不对称。
下面几个点一定要检查:
- 中文不会乱码:所有文件读写都要带
encoding='utf-8',否则 Windows 环境下容易报错。 - JSON 和 JSONL 别混用:JSON 是一个数组,JSONL 是每行一个独立 JSON 对象。读取方式完全不同,脚本里要先判断。
- 空字段要处理:Alpaca 的
input可以为空,但有些框架不允许None,必须转成空字符串。 - 多轮对话不要强行拆:如果原始数据是 ShareGPT,你的目标框架也支持多轮,就保留
conversations结构,不要拆成单轮。 - 字段名大小写敏感:
Instruction和instruction是不同字段,转换后务必用head -n 3检查一下。
转换后怎么验证
转换完不能直接拿去训练,先做两步验证。
第一步,用命令查看文件前几行是否有合法 JSON:
head -n 3 converted.jsonl
第二步,用 Python 快速校验每一行能否被解析:
import json
with open('converted.jsonl', 'r', encoding='utf-8') as f:
for i, line in enumerate(f):
try:
json.loads(line)
except Exception as e:
print(f'第 {i+1} 行错误:{e}')
如果没有任何报错,再确认字段名和目标框架要求是否一致。
例如 HuggingFace 的 Dataset.from_json 会直接读取 JSONL,字段名需要和训练脚本里 data_collator 中指定的对应上。
最后补充一点:不同框架对数据集格式的官方要求可能会更新,转换前最好先查一下当前框架的文档。
如果你在转换过程中遇到具体的报错,把报错信息贴出来,可以根据实际框架再调整字段映射。
微调数据集格式转换并不复杂,只要理清字段关系,写一个自己的互转脚本就能一劳永逸。