Qwen2.5Omni解析

5 分钟阅读

本文以Qwen2.5-Omni-7B为例解析该模型。

源码:modeling_qwen2_5_omni.py

Audio部分

1. 音频前处理

第一步:读取音频数据

第一步:比如20s的视频,原始采样率为44.100kHz。audio会用16kHz采样率重采样,得到335459个浮点数值,范围是[-1.0, 1.0]。为什么不是320000个浮点数值?这是因为原始采样率与16000存在四舍五入补帧情况,导致多采样部分。

第二步:特征提取

WhisperFeatureExtractor进行特征处理,主要是采用fbank (Filter Bank)特征提取,将音频数据转换成机器学习的特征向量,过程包括预加重、分帧、加窗、短时傅里叶变化(STFT)、梅尔滤波。函数参见_torch_extract_fbank_features

输入值[335459] float 
过程
reshape => [335459, 1] float
padding => [1, 4800000, 1] float  + [1, 4800000] int32 仅前335459为有效值
transpose => [1, 1, 4800000] float
extract_fbank_features => [1, 128, 30000] float

返回值
input_features: [1, 128, 30000] float
attention_mask: [1, 30000] int32 # 前2097为1,后全0

第三步:进一步预处理

audio_feature_lengths = sum(attention_mask) = 2097
input_features = permute(0,2,1) => [128, 2097] float
audio_feat_lengths = 1049

#进一步根据chunk = 200 进行分组
input_features = [11, 128, 200]
padded_mask = [11, 200] # 前10组全1,最后一组97个1

2. Qwen2_5OmniAudioEncoder

它是一个典型的transformers结构。

输入[11, 128, 200]
conv1d (d_model, 128, 3) => [11, d_model, 200]
conv1d (d_model, d_model, 3), stride = 2 => [11, d_model, 100]
position_embedding + => [11, d_model, 100]
permute              => [11, 100, d_model]
32x(attention+mlp)   => [11, 100, d_model]
# merge
dim 1 average        => [11,  50, d_model]
mm (d_model, hidden_size) => [11, 50, hidden_size]
reshape               => [550, hidden_size]

由于有mask存在,最后一组只有前97有效,最终输出为[524, hidden_size]

3. 音频Tokens数计算

这里忽略音频重采样补帧情况,做粗略计算。

2秒音频取32000个浮点数值,经过预处理得到[1, 128, 200],经过模型推理得到[50, hidden_size]

所以可以粗略计算为:每秒音频约占25 token

Vision部分

这部分与Qwen2.5VL相同vision部分

LLM 部分

Token如何摆放

以20s视频为例,音频占500 token;视频采用280x504分辨率,占1800 token;假定文本token占52;总token数为2352。通过pdb跟踪得到token摆放顺序如下:

内容 token数
文本 45
图像 180
音频 50
…… ……
图像 180
音频 50
文本 7

为何是如此摆放?这涉及到对replace_multimodal_special_tokens的解读。输入text如下:

['<|im_start|>system\nYou are Qwen, a virtual human developed by the Qwen Team, Alibaba Group, capable of perceiving auditory and visual inputs, as well as generat
ing text and speech.<|im_end|>\n<|im_start|>user\n<|vision_bos|><|VIDEO|><|vision_eos|><|im_end|>\n<|im_start|>assistant\n'] 

文本部分直接转成相应token,重点是VIDEO部分的处理。

  • 音频每秒25 token,视频每2秒180 token
  • seconds_per_chunk为2.0,表示每个chunk为2s。所以20s视频分成了10个chunk,180个token视频+50个token音频为一个chunk。

Position ID是如何排列

position id为3维,position_id_per_seconds为25,代表每秒间隔id为25。

id配置如下:

  • 时序id方面:文本id累增,音频id以25为单位增加;视频id以50为单位增加,与fps有关
  • hw维度:文本和音频与时序id一致;视频按照hw的顺利排布

具体ID排列如下:

内容 token数 id
文本 45 T: 0,1,2,3, … 43,43; H和W与T完全一致
图像 180 T: 44,44,44,…,44,44; H: 44(18个),45(18个),…,53(18个); W: [44, 61]共10个
音频 50 T: 44,45,46,…,92,93; H和W与T完全一致
…… ……  
图像 180 T: 494,494,494,…,494,494; H: 44(18个),45(18个),…,53(18个); W: [44, 61]共10个
音频 50 T: 494,495,496,…,542,543; H和W与T完全一致
文本 7 T: 544,544,545,546,547,548,549;H和W与T完全一致

推理过程

标准的embeding + num_layers x Block + lm_head结构

Talker部分

(待补充)