ms-swift多模态数据准备:自定义数据集格式说明
在使用 ms-swift 进行多模态大模型微调时,数据是起点,更是成败的关键。你可能已经成功下载了 Qwen3-VL 或 InternVL3.5 这样的先进模型,也配置好了 A100 或 H100 环境,但一旦运行训练命令却报出KeyError: 'image'或ValueError: unsupported modality,问题往往不出在模型或硬件,而在于——你的数据集没有按 ms-swift 的“语言”说话。
这不是文档缺失导致的困惑,而是框架对多模态数据结构有明确、统一且可扩展的约定。它不强制你用某种数据库或云存储,也不要求你重写整个预处理流水线;它只要求你用一种清晰、稳定、机器可解析的方式描述:“这张图要配哪段文字”、“这段语音对应哪个答案”、“这个框选区域想表达什么”。
本文将完全聚焦于“如何组织自己的多模态数据”这一具体动作,不讲原理推导,不堆砌参数列表,只提供可立即验证、可直接复用、经生产环境反复打磨的数据格式规范。你会看到:
- 为什么 JSONL 是 ms-swift 多模态数据的事实标准;
- 图像、视频、音频、文本、坐标框等不同模态字段该怎么命名、怎么填值;
- 如何支持单模态输入、双模态组合、甚至四模态混合(图文+语音+视频);
- 怎样让同一个数据集既可用于 SFT,也能无缝切到 DPO 或 GRPO 训练;
- 常见陷阱与绕过方案:路径错误、编码异常、字段缺失、尺寸越界等真实问题。
读完后,你将能独立构建一个符合 ms-swift 要求的本地多模态数据集,并用一行命令启动训练。
1. 核心原则:字段驱动,路径为本,结构即协议
ms-swift 对自定义数据集的设计哲学非常务实:它不解析原始二进制数据,只解析数据描述。换句话说,框架从不直接打开一张 JPG 或 WAV 文件,而是读取你提供的 JSONL 行中关于该文件的“元信息”,再由内部加载器按需拉取、解码、归一化。
因此,所有数据准备工作的核心,就是写出一份字段语义准确、路径指向可靠、结构层次合理的文本描述。
1.1 为什么首选 JSONL(每行一个 JSON)
- 流式加载:训练时无需一次性载入全部数据到内存,适合百万级样本;
- 容错性强:某一行 JSON 格式错误,仅跳过该样本,不影响整体训练;
- 字段灵活:每行可包含不同模态组合(如第1行只有 image+text,第2行含 image+audio+boxes),框架自动适配;
- 工具链成熟:可用
jq、Pythonjsonlines、Pandasread_json(..., lines=True)快速校验与清洗; - 不推荐:JSON(单个大对象)、CSV(无法自然表达嵌套结构如 conversations)、Parquet(虽支持但调试成本高,初学者易卡在 schema 定义)。
1.2 字段命名不是随意的:必须与 ms-swift 内置解析器对齐
ms-swift 在swift.dataset模块中预定义了一组标准字段名。当你在数据中使用这些字段时,框架会自动触发对应模态的加载逻辑。命名错误 = 模态被忽略 = 训练退化为纯文本任务。
以下是必须掌握的 7 个核心字段(区分大小写,不可缩写):
| 字段名 | 类型 | 说明 | 示例值 |
|---|---|---|---|
image | str或list[str] | 单张图像路径(相对或绝对),支持本地路径、HTTP URL、ModelScope 资源 ID | "data/images/cat.jpg"或"https://example.com/dog.png" |
video | str | 视频文件路径(MP4/AVI/WebM),框架自动抽帧并采样关键帧 | "data/videos/meeting.mp4" |
audio | str | 音频文件路径(WAV/MP3/FLAC),支持单声道/立体声,自动重采样至 16kHz | "data/audio/question.wav" |
text | str | 主干文本输入,用于指令微调或作为上下文锚点 | "请描述这张图片的内容。" |
conversations | list[dict] | 多轮对话结构,每轮含role("user"/"assistant")和content(支持<img>等模态标记) | [{"role":"user","content":"<img>这是什么动物?"},{"role":"assistant","content":"这是一只橘猫。"}] |
boxes | list[list[float]] | 归一化边界框坐标[x1, y1, x2, y2](0~1 范围),用于 grounding 任务 | [[0.2, 0.3, 0.8, 0.7]] |
labels | list[str] | 分类标签或细粒度描述,常与boxes配合使用 | ["cat", "furry"] |
重要提示:
conversations是唯一支持内联模态标记的字段。你在content中写的<img>、<audio>不是字符串,而是 ms-swift 的“模态占位符”,会被自动替换为对应图像/音频的 embedding 序列。而image、audio等顶层字段,仅用于单模态任务或作为conversations的补充元信息。
1.3 路径规则:本地优先,绝对可靠
- 所有路径(
image/video/audio)默认以数据集文件所在目录为根。
例如你的数据文件是./my_mm_data.jsonl,其中一行写"image": "imgs/pic1.jpg",则框架会尝试加载./imgs/pic1.jpg。 - 支持
file://协议前缀(显式声明本地路径):"image": "file:///home/user/data/pic.jpg"; - 支持 HTTP/HTTPS:
"image": "https://cdn.example.com/photo.jpg"(需网络可达,首次加载稍慢); - 不支持:相对路径跨目录回溯(如
"../other/img.jpg")、Windows 驱动器盘符裸写("C:\data\img.jpg")、未转义空格("my pic.jpg"→ 应写为"my%20pic.jpg"或"my pic.jpg"并确保文件系统支持)。
2. 四种典型数据组织方式:从简单到复杂
你不需要一开始就设计最复杂的格式。ms-swift 允许你从最简结构起步,随着任务演进逐步增强。以下四种模式覆盖 95% 的实际场景。
2.1 单图 + 单文本:VQA / Captioning 基础版
适用于图像问答、图像描述生成等任务。结构最轻量,调试最快。
{ "image": "images/beach.jpg", "text": "这张图片展示了什么场景?" }或更贴近对话形式(推荐,兼容性更好):
{ "image": "images/beach.jpg", "conversations": [ {"role": "user", "content": "这张图片展示了什么场景?"}, {"role": "assistant", "content": "一片阳光明媚的海滩,有蓝色海水、白色沙滩和几把彩色遮阳伞。"} ] }优势:字段少,易生成,swift sft命令开箱即用
注意:若只用text字段而无conversations,则不会触发多模态对齐,模型仅将图像路径当作文本字符串处理
2.2 多图 + 多轮对话:图文交互式 Agent
适用于需要连续理解多张图像并进行多步推理的场景,如电商导购、医疗报告分析。
{ "image": ["images/xray1.jpg", "images/xray2.jpg"], "conversations": [ {"role": "user", "content": "<img><img>请对比这两张X光片,指出差异。"}, {"role": "assistant", "content": "第一张显示左肺下叶有模糊阴影,第二张该区域密度增高,提示炎症进展。"} ] }关键点:
image字段支持list[str],框架会依次加载并拼接 embedding;<img>标记数量必须与image列表长度一致(两个<img>→ 两张图);- 可混用:
"content": "<img>这是什么?<img>另一张呢?",实现精准位置绑定。
2.3 图文 + 音频 + 坐标框:细粒度指代定位(Grounding)
适用于需要模型精确定位并描述图像中特定区域的任务,如工业质检、教育辅导。
{ "image": "images/circuit.jpg", "audio": "audios/instruction.wav", "boxes": [[0.15, 0.22, 0.45, 0.68], [0.62, 0.10, 0.92, 0.40]], "labels": ["defective capacitor", "normal resistor"], "conversations": [ {"role": "user", "content": "<img><audio>请检查红色框内的元件状态。"}, {"role": "assistant", "content": "红色框内是一个失效的电容,顶部已鼓包;绿色框内电阻外观正常。"} ] }🔧技术细节:
boxes中每个子列表必须是 4 个浮点数,顺序为[x_min, y_min, x_max, y_max],值域0.0 ~ 1.0;- 若
boxes与labels长度不等,框架会截断或填充"",但强烈建议严格一一对应; <audio>标记会触发 Whisper 编码器,输出与图像 patch 同维度的序列,参与跨模态注意力。
2.4 四模态混合:视频 + 图像 + 文本 + 语音(高级研究场景)
面向前沿探索,如多传感器自动驾驶日志分析、沉浸式教育内容生成。
{ "video": "videos/driving.mp4", "image": "images/dashboard.jpg", "audio": "audios/voice_command.wav", "text": "当前车速60km/h,前方30米有施工区域,请减速并变道。", "conversations": [ {"role": "user", "content": "<video><img><audio>根据画面、仪表盘截图和语音指令,生成安全操作建议。"}, {"role": "assistant", "content": "请立即松开油门,轻踩刹车将车速降至40km/h以下,并观察左侧车道确认安全后平稳变道。"} ] }注意事项:
video和image可共存,但video会主导视觉输入(自动抽关键帧),image作为辅助参考;- 所有模态标记
<video><img><audio>必须在conversations.content中显式声明,否则不会参与建模; - 此类样本训练显存占用显著增加,建议搭配
--max_length 4096和梯度累积。
3. 数据集构建实操指南:三步完成验证
理论终需落地。下面以一个真实可运行的案例,带你走完从零创建到命令行验证的全过程。
3.1 第一步:准备最小可行数据集(3 行 JSONL)
创建文件demo_mm.jsonl,内容如下(请确保images/目录下存在对应图片):
{"image": "images/cat.jpg", "conversations": [{"role": "user", "content": "<img>这是什么动物?"}, {"role": "assistant", "content": "一只橘猫。"}]} {"image": "images/dog.jpg", "conversations": [{"role": "user", "content": "<img>这只狗在做什么?"}, {"role": "assistant", "content": "它正坐在草地上吐舌头。"}]} {"image": "images/bird.jpg", "conversations": [{"role": "user", "content": "<img>这只鸟的羽毛是什么颜色?"}, {"role": "assistant", "content": "主要是蓝绿色,翅膀边缘带黑色条纹。"}]}验证命令(检查 JSONL 格式):
# Linux/macOS jq -e '.image and .conversations' demo_mm.jsonl > /dev/null && echo " 格式正确" || echo " 有错误"3.2 第二步:编写训练命令并启动(单卡快速验证)
使用轻量模型 Qwen2-VL-2B(约 2GB 显存占用),避免资源瓶颈:
CUDA_VISIBLE_DEVICES=0 swift sft \ --model qwen-vl-chat \ --dataset ./demo_mm.jsonl \ --train_type lora \ --lora_rank 8 \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 4 \ --learning_rate 1e-4 \ --num_train_epochs 0.1 \ --max_length 2048 \ --output_dir ./output_demo \ --logging_steps 1 \ --save_steps 10 \ --eval_steps 10 \ --torch_dtype bfloat16关键参数说明:
--dataset ./demo_mm.jsonl:指定本地 JSONL 路径,框架自动识别为多模态;--model qwen-vl-chat:必须使用支持多模态的模型 ID(不能用纯文本模型如qwen2-7b);--max_length 2048:多模态输入 token 更长,需适当增大上限;--logging_steps 1:设为 1 便于第一时间观察是否成功加载图像。
成功标志:日志中出现类似Loading image from: ./images/cat.jpgLoaded 3 samples from ./demo_mm.jsonlStarting training...
3.3 第三步:排查常见失败原因(附解决方案)
| 现象 | 可能原因 | 快速诊断命令 | 解决方案 |
|---|---|---|---|
FileNotFoundError: images/cat.jpg | 路径相对于 JSONL 文件不正确 | ls -l ./images/cat.jpg | 使用realpath检查路径,或改用绝对路径"image": "/full/path/to/cat.jpg" |
KeyError: 'image' | JSONL 行中缺少image字段 | head -n1 demo_mm.jsonl | jq '.image' | 确保每行都含image(即使为空数组[],但不推荐) |
ValueError: invalid literal for int() | boxes中含非数字字符或空字符串 | jq -r '.boxes[] | join(",")' demo_mm.jsonl | 用 Python 脚本清洗:boxes = [[float(x) for x in box] for box in boxes] |
日志无Loading image提示 | 模型未启用多模态分支 | swift list-models | grep -i vl | 确认--model参数值正确,如qwen-vl-chat而非qwen2-7b |
| 训练 loss 不下降 | conversations中未使用<img>标记 | jq -r '.conversations[].content' demo_mm.jsonl | 将"content": "这是什么?"改为"content": "<img>这是什么?" |
4. 进阶技巧:让数据集更健壮、更高效
当你已能稳定运行基础训练,可引入以下技巧提升工程鲁棒性与实验效率。
4.1 数据分片与缓存:加速千万级数据加载
对超大数据集(>100 万样本),直接读 JSONL 仍可能成为 I/O 瓶颈。ms-swift 支持.parquet格式,其列式存储与内置压缩可提速 3~5 倍。
转换脚本(Python):
import pandas as pd import jsonlines # 读取 JSONL with jsonlines.open('large_mm.jsonl') as reader: records = [obj for obj in reader] # 转为 DataFrame(自动处理 list 字段) df = pd.DataFrame(records) # Parquet 会将 list 序列化为 string,但 ms-swift 加载器可自动反序列化 df.to_parquet('large_mm.parquet', index=False, compression='zstd')训练时直接使用:
swift sft --dataset ./large_mm.parquet ...优势:文件体积减小 60%,随机采样速度提升,支持 Spark/Dask 分布式预处理。
4.2 动态字段注入:同一份数据适配多种任务
你无需为 SFT、DPO、GRPO 分别准备三套数据。通过conversations结构微调,一份 JSONL 可复用:
SFT 格式(监督微调):
{"conversations": [{"role":"user","content":"<img>描述一下"},{"role":"assistant","content":"一只猫。"}]}DPO 格式(偏好学习):
{ "conversations": [{"role":"user","content":"<img>描述一下"}], "chosen": "一只橘猫蹲在窗台上,毛发蓬松。", "rejected": "猫。" }GRPO 格式(强化学习):
{ "conversations": [{"role":"user","content":"<img>描述一下"}], "response": "一只橘猫蹲在窗台上,毛发蓬松。", "reward": 0.92 }
ms-swift 的Dataset加载器会自动检测字段存在性,并路由到对应任务处理器。你只需在命令中指定--rlhf_type dpo或--rlhf_type grpo,数据逻辑完全解耦。
4.3 模态缺失容错:优雅降级策略
现实数据总有缺失。ms-swift 允许你定义 fallback 行为:
- 若
image字段为空或文件不存在,默认跳过该样本(--drop_invalid true,默认开启); - 若
audio缺失,但conversations.content含<audio>,则静音向量替代(不影响训练); - 最佳实践:预处理脚本中添加完整性检查:
from PIL import Image import os def validate_sample(sample): if 'image' in sample and isinstance(sample['image'], str): return os.path.exists(sample['image']) and Image.open(sample['image']).size[0] > 0 return True # 其他模态暂不强校验5. 总结:数据即契约,格式即生产力
在 ms-swift 的多模态工作流中,数据集不是训练的“输入”,而是你与框架之间的一份隐式契约。这份契约规定了:
- 你承诺提供哪些模态(
image/video/audio); - 你承诺如何组织它们(
conversations中的<img>顺序); - 你承诺字段值的语义(
boxes是归一化坐标,不是像素值); - 框架承诺据此加载、对齐、融合,并端到端优化。
遵守这份契约,你获得的是:
一次写好,多任务复用(SFT/DPO/GRPO);
本地调试与集群训练无缝切换;
新增模态(如 3D 点云、传感器信号)只需扩展字段与加载器;
团队协作时,数据格式即文档,无需口头解释。
反之,若绕过格式规范,试图用 hack 方式“骗过”框架,最终只会陷入无限 debug 循环——因为问题不在代码,而在你与框架的沟通失效。
所以,请把本文当作一份可执行的数据协议说明书。下次准备数据时,先打开 VS Code,新建my_dataset.jsonl,对照本文的字段表与示例,逐行填写。运行swift sft,看到Loading image...的日志那一刻,你就真正掌握了 ms-swift 多模态能力的第一把钥匙。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。