把 Cosmos 3 后训练、VLM 行业推理和合成数据生成放在同一篇文章里,看起来是三个独立话题,实际是一条完整的数据生产链路。先由世界模型生成场景视频,再用视觉语言模型对视频做结构化理解,然后通过后训练把模型的输出格式和语义都稳定到目标领域;流水线跑通后,同一个流程既能处理智慧城市路口监控,也能为农业机器人生成合成训练数据。
标题中的[双语]我这样处理:关键模型名、专用术语尽量保留英文,正文用中文解释,方便对照英文文档。下面内容以“Cosmos 3 作为世界模型代号”展开,讲解后训练流程时使用开源 VLM 权重和通用工具链;实际项目中具体版本、仓库地址和导入路径都要以你手上的环境为准。读完这篇文章,你能拿到一套最小可复现的工作流:合成视频生成、VLM 数据标注、LoRA 后训练、结构化推理部署、农业场景数据回流。
这套技能适合做多模态模型微调、视觉问答落地、机器人仿真数据生产的工程师。它回答的核心问题不是“模型怎么下载”,而是“如何把通用 VLM 变成某个行业里稳定输出的 VLM”。
1. 先拆开技术主线:世界模型、VLM 后训练与合成数据生成如何串联
1.1 三个本来独立的技术,为什么能放进同一条流水线
世界模型解决“数据从哪里来”的问题。真实的城市监控视频和农业机器人操作视频采集成本高、涉及隐私、长尾场景少。Cosmos 3 这类以视频生成为目标的世界模型可以用文字描述生成一段带时间连续性的场景视频,虽然它不代表物理完全正确,但足以作为 VLM 标注和后续感知模型预训练的样本来源。
VLM 解决“视频怎么变成结构化语义”的问题。纯视频存储在工程上只是文件,模型要的是文本标签、目标框、事件描述和决策理由。视觉语言模型把图像或视频帧输入编码成视觉 token,再交给语言模型生成 JSON、自然语言或操作指令。
后训练解决“通用模型为什么在领域里不好用”的问题。通用 VLM 能回答常识问题,但未必知道“智慧城市路口应该关注哪些事件类型”“农业机器人抓取番茄时要输出怎样的姿态信息”。后训练用领域数据调整模型权重,让模型在该场景下既能稳定输出格式,也能抓住领域语义。
1.2 为什么这里不能只做提示词工程
可以用提示词让通用 VLM 输出一份 JSON,比如“你是一个城市路况分析助手”。在演示环境里可行,进入批量生产后会出现三类问题。
- 输出不稳定。提示词无法保证每次输出都能被
json.loads解析,模型可能多加一行解释、把false写成False。 - 领域知识不足。模型不知道车流量统计要考虑有效区域,不知道农业机器人末端执行器的坐标系约定。
- 上下文窗口限制。如果把长视频的每一帧都塞进提示词,处理和延迟都会失控。
后训练的价值就是把“输出的 schema”和“关键领域判断”写进权重,推理时只需要传少量上下文,而不是每次都重复一长段提示词。
1.3 整条流水线可以分成七步
实际项目的核心环节是固定的:场景生成、抽帧、预标注、数据清洗、后训练、推理部署、生成数据回流。完整步骤按下表执行。
| 序号 | 环节 | 输入 | 输出 | 关键工具 |
|---|---|---|---|---|
| 1 | 世界模型生成视频 | 文本提示词 | 场景视频 | Cosmos 3 视频生成流程 |
| 2 | 抽帧 | 视频文件 | 图像序列 | OpenCV / ffmpeg |
| 3 | VLM 预标注 | 图像序列 | 初版 JSON 标注 | 开源 VLM 权重 |
| 4 | 数据清洗与格式化 | 初版 JSON | 训练 JSONL | 校验脚本、人工抽检 |
| 5 | 后训练 | 训练 JSONL | LoRA 权重或合并权重 | peft、transformers |
| 6 | 推理部署 | 新场景视频或图片 | 结构化 JSON | vLLM 或原生推理脚本 |
| 7 | 合成数据回流 | 微调模型 | 新一批训练数据 | 过滤、去重、质量评分 |
这里要注意顺序:先用通用模型做预标注,再清洗,不要直接拿原始视频做训练。原始视频里 80% 的帧可能是重复或无信息量的,直接训练只会放大数据噪声。
1.4 两个业务方向只是同一套流程的两种配置
智慧城市 VLM 推理和农业机器人合成数据生成看起来行业差别很大,但抽象后完全一致:给定一段视觉输入,输出结构化判断。智慧城市需要判断“道路拥堵、事故、异常行人”;农业机器人需要判断“作物成熟度、障碍物位置、抓取姿态”。差异集中在数据 schema、目标类别、相机视角和任务粒度。后训练的数据格式不需要大改,只需要替换领域字段和标注说明。
因此,先把城市场景跑通,再迁移到农业场景,是降低风险的做法。两个方向共用代码库,出现问题排查路径一致,训练资源也能共享。
2. 环境与基础模型准备:先对齐版本,再谈后训练
2.1 硬件与软件环境要求
后训练 VLM 对显存不友好,因为视觉编码器和语言模型权重同时占用显存。按开源 7B 级 VLM 估算,学习环境使用 LoRA 至少准备 24 GB 显存;如果使用 14B 级权重,建议 2 块 24 GB 或 1 块 48 GB。全参微调 7B 需要 4 块 A100 级别的显卡,并且要配合 ZeRO-2 以上;不建议新入行者一开始做全参。
| 配置项 | 学习/实验环境 | 生产训练环境 |
|---|---|---|
| GPU 显存 | 单卡 24 GB 起步 | 2 到 8 卡 A100/H100 或同级别 |
| CPU 内存 | 64 GB | 256 GB 以上 |
| Python | 3.10 | 3.10 |
| CUDA | 12.1 以上 | 12.1 以上 |
| PyTorch | 2.1 以上 | 2.1 以上 |
| 加速组件 | peft、transformers、accelerate | 增加 DeepSpeed、flash-attn、vLLM |
数据库不需要,但训练数据需要放在快速磁盘,推荐 NVMe SSD。视频读取和图像解码是 IO 密集操作,不要把原始视频放在网络盘上直接训练。
2.2 Python 依赖安装
在虚拟环境中安装依赖。下面是一个可运行列表,版本是否锁定以你自己环境为准:
python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install torch==2.5.1 --index-url https://download.pytorch.org/whl/cu121 pip install transformers peft accelerate deepspeed pip install datasets opencv-python pillow pip install vllm如果需要加速注意力计算,再单独安装 flash-attn。这个库对 CUDA 版本敏感,如果编译失败,可以先不安装,训练任务仍然能跑,只是更慢。
2.3 基础模型选择与下载
后训练前先确认:基线 VLM 使用什么,以及对应权重文件名。不要只看“模型叫 Qwen2-VL”就假设 processor 配置一定匹配。下载前先确认config.json里model_type,processor_config.json是否存在,以及图像处理器是否与模型匹配。
常见项目中可以使用 Hugging Face 的snapshot_download下载权重:
from huggingface_hub import snapshot_download snapshot_download( repo_id="Qwen/Qwen2-VL-7B-Instruct", local_dir="./models/Qwen2-VL-7B-Instruct", allow_patterns=["*.json", "*.safetensors", "*.py", "tokenizer*"], )下载完成后,用transformers加载看一眼,确认能不能跑通一条最简单的推理,再进入后训练。这个检查点不能省:后训练无法修复一个加载都有问题的模型。
2.4 推荐目录结构
按下面结构组织,便于把数据、权重、日志分开:
project/ ├── configs/ # 训练、推理、数据生成配置 │ ├── train_lora.yaml │ └── infer_city.yaml ├── data/ │ ├── raw_videos/ # Cosmos 3 输出的原始视频 │ ├── frames/ # 抽帧结果 │ ├── labeled/ # 第一版 JSON 标注 │ └── train/ # 最终训练 JSONL ├── models/ # 基线权重和 LoRA 权重 │ ├── base_vlm/ │ └── lora_adapter/ ├── scripts/ │ ├── generate_scenes.py │ ├── extract_frames.py │ ├── label_videos.py │ ├── train_lora.py │ └── inference_city.py └── logs/ # 训练日志与错误日志把配置、数据、代码分离,是最基本的工程纪律。看到目录里混着.mp4和训练脚本时,大概率已经出了问题。
2.5 环境检查清单
动手跑训练前快速核对六项:
- 显卡驱动能看到 NVIDIA 设备,
nvidia-smi正常。 - PyTorch 能否在 GPU 上计算,
torch.cuda.is_available()返回True。 transformers与模型权重兼容,加载后跑通一次问答。- 数据目录有写入权限,磁盘剩余空间不少于模型权重 3 倍。
- 训练脚本与推理脚本使用同一个 Python 环境,避免命令行环境不一致。
- Cosmos 3 或视频生成流程能否稳定输出片段,先生成 3 秒测试视频确认格式。
这些检查看似基础,实际踩坑率最高。大部分训练失败不是代码问题,而是环境不一致或模型权重不完整。
3. 用世界模型生成场景视频,并转换成 VLM 后训练数据
3.1 用 Cosmos 3 生成智慧城市与农业场景视频
用世界模型生成视频,关键不是“生成”,而是“可控”。城市路口、农业无人机、机械臂作业都要写清楚相机视角、光照、物体类别和运动目标,否则模型会生成无关内容。
下面提示词用于说明思路,中文写给项目组看,英文写进生成接口:
城市路口示例: A 4K aerial view of a busy urban intersection at sunset. Traffic light turns red, vehicles stop, pedestrians cross the crosswalk. Camera stays at 30 meters height, slight drift to the left. 农业机器人示例: A first-person view from an agricultural robot arm in a tomato greenhouse. The gripper approaches a ripe tomato on the vine, background is softly blurred. Natural sunlight, no motion blur.生成时建议每次生成 5 到 15 秒视频。低于 5 秒事件不完整,超过 15 秒可能丢失一致性。实际调用方式取决于你的世界模型工具链,入口尽量统一封装成一个函数,输出统一.mp4文件。
有些项目会直接把世界模型生成的视频当真实数据用,这是高风险动作。合成视频在纹理、遮挡、目标比例上可能和真实场景不一致,用于训练 VLM 必须做人工抽检,并记录每个样本来自哪个生成提示词。
3.2 抽帧:把视频变成模型能处理的图像序列
视频是连续的,但当前大多数开源 VLM 处理的是图像或短视频 token。抽帧时要兼顾时间覆盖和存储成本。使用 OpenCV 每 0.5 秒取一帧即可;对于高动态场景可以提高到 1 秒 4 帧。
import cv2 from pathlib import Path video_path = Path("data/raw_videos/city_001.mp4") frame_dir = Path("data/frames/city_001") frame_dir.mkdir(parents=True, exist_ok=True) cap = cv2.VideoCapture(str(video_path)) fps = cap.get(cv2.CAP_PROP_FPS) interval = max(1, int(fps / 2)) # 每秒 2 帧 frame_id = 0 saved = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break if frame_id % interval == 0: out_path = frame_dir / f"{saved:06d}.jpg" cv2.imwrite(str(out_path), frame) saved += 1 frame_id += 1 cap.release()抽帧后立刻检查文件数量和平均大小。如果某一帧是黑屏或纯色,需要从数据里删除。注意不要用cv2.imwrite写中文路径,部分环境会出现静默失败。
3.3 用通用 VLM 生成第一版标注
第一版标注可以使用和后期不同的模型。例如先用较大规模的通用 VLM 生成初始 JSON,再用人工或规则校验。标注 prompt 要给出明确的输出 schema:
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor from PIL import Image model = Qwen2VLForConditionalGeneration.from_pretrained( "./models/Qwen2-VL-7B-Instruct", torch_dtype="bfloat16", device_map="auto", ) processor = AutoProcessor.from_pretrained("./models/Qwen2-VL-7B-Instruct") prompt = """分析这张城市路口图像,只输出 JSON: { "vehicles": [{"type": "car|bus|truck|bike", "lane_position": "left|middle|right"}], "pedestrians": {"count": 0, "crossing": true}, "traffic_light": "red|yellow|green|unknown", "events": ["queueing", "accident", "jaywalking", "none"], "reason": "一句话说明判断依据" } """ image = Image.open("data/frames/city_001/000000.jpg") inputs = processor( text=prompt, images=image, return_tensors="pt", ).to(model.device) output_ids = model.generate(**inputs, max_new_tokens=256) result = processor.batch_decode(output_ids, skip_special_tokens=True)[0] print(result)这段代码能跑通说明环境正常。批量标注时,要把它放进一个循环里,并在每次调用后捕获异常;不要因为一个坏