本文说明在 LLaMA-Factory 中,使用 qwen3_omni_nothink 模板训练 Qwen3-Omni-30B-A3B-Instruct 时,视频与音频如何被编码成 token,以及如何从日志中的 grid_thw、时长等反推和验算 token 数量。
video_fps 抽帧 → 每帧按 video_max_pixels 做等比 resize → 送入 Qwen 的 video processor → 得到 3D 网格 grid_thw,再按 merge_size 合并得到 视频 token 数。audio_sampling_rate 读取 → 经 feature extractor(如 Whisper 编码器风格)→ 得到 音频 token 数。use_audio_in_video=True,同一段视频与对应音频会按时间轴 chunk 交叠,生成交织的 video/audio token 序列。下文分别说明视频 token、音频 token 以及交叠模式的计算方式。
video_fps(默认 2.0)和 video_maxlen(默认 128)计算采样帧数:
sample_frames = min(total_frames, video_maxlen, floor(duration × video_fps))sample_frames 帧。width × height > video_max_pixels:按面积等比缩小到 ≤ video_max_pixels。width × height < video_min_pixels:按面积等比放大到 ≥ video_min_pixels。int(w × factor)、int(h × factor),因此可能出现如 376×815 这种非 32 倍数;最终送入模型前的对齐由 transformers 的 video_processor 内部完成。Qwen 的 video processor 会输出 grid_thw,形状为 (T, H, W):
| 维度 | 含义 | 计算方式 |
|---|---|---|
| T | 时间维格点数 | 采样帧数 ÷ temporal_patch_size(一般为 2,即每 2 帧合并为一个时间格点) |
| H | 高度方向 patch 数 | resize 后的高度 ÷ patch_size(如 16),取整 |
| W | 宽度方向 patch 数 | resize 后的宽度 ÷ patch_size,取整 |
示例:132 帧、resize 后 376×815(宽×高)、temporal_patch_size=2、patch_size=16:
即 grid_thw = [66, 50, 24]。
空间上还有 merge(一般为 merge_size=2,即 2×2 patch 合并为 1 个 token):
展开代码video_tokens = T × ⌊H / merge_size⌋ × ⌊W / merge_size⌋
同上例:66 × (50/2) × (24/2) = 66 × 25 × 12 = 19800。
_regularize_audios(重采样到 audio_sampling_rate,默认 16000)。feature_attention_mask 的长度有一组固定换算(与 100、2 等池化步长相关)。audio_tokens 即为该段音频在模型中占用的 音频 token 数,大致与时长正相关(例如约 12–13 token/秒量级,以实际日志为准)。当 use_audio_in_video=True 且数据中同时提供与视频一一对应的音频时:
<video><audio> 占位符会被替换成 按时间 chunk 交叠 的 token 序列:vision_bos + audio_bos + [video_chunk_1_tokens + audio_chunk_1_tokens + video_chunk_2_tokens + audio_chunk_2_tokens + …] + audio_eos + vision_eos。get_chunked_index 与固定步长(如 50)决定,保证同一时间段内的视频 token 与音频 token 在序列中交替出现,便于模型做多模态对齐。video_tokens / audio_tokens 分别为该段中所有 video/audio token 的合计。开启训练后,前若干条多模态样本会打印类似:
展开代码[OmniPlugin] sample #1 multimodal token stats (use_audio_in_video=True): video[0]: duration=66.17s sampled_frames=132 video_tokens=19800 video frame size after resize: 376x815 px video[0] grid_thw (T,H_patches,W_patches)=[66, 50, 24] (T*H*W // merge_size^2 = video_tokens) audio[0]: duration=66.08s audio_tokens=860 TOTAL: video_tokens=19800 audio_tokens=860 mm_tokens=20660
验算建议:
sampled_frames / temporal_patch_size 应等于 grid_thw[0](如 132/2=66)。grid_thw[1]、grid_thw[2](如 815/16≈50,376/16≈24)。T × (H/merge_size) × (W/merge_size),如 66×25×12=19800。| 参数 | 默认 | 说明 |
|---|---|---|
video_max_pixels | 256×256 | 单帧 resize 后允许的最大像素数(面积) |
video_min_pixels | 16×16 | 单帧允许的最小像素数 |
video_fps | 2.0 | 抽帧时每秒采样帧数 |
video_maxlen | 128 | 单视频最多保留的帧数 |
use_audio_in_video | false | 是否启用音视频按时间交叠 |
以上参数在训练/推理的 yaml 中可直接设置,会通过 patch_processor 注入到 processor,进而影响抽帧、resize 以及 token 数量。
grid_thw=(T,H,W) 与 merge_size 决定,公式为video_tokens = T × (H/merge_size) × (W/merge_size)。use_audio_in_video=True 时,同一段视频与音频会按时间 chunk 交替排列成一段 token 序列,便于模型做细粒度音画对齐。理解上述关系后,即可根据日志中的 duration、resize 尺寸、grid_thw 和 video_tokens/audio_tokens 自行验算并调整 video_fps、video_max_pixels 等参数,以控制显存与效果。


本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!