File size: 4,975 Bytes
f664f3f
 
2460459
f664f3f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2460459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f664f3f
 
 
 
 
 
 
 
 
 
2460459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f664f3f
 
 
 
 
2460459
f664f3f
2460459
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# multimodal/ · 多模态

把「外部模态」(图像/音频)编码并投影到 LLM 隐藏空间,实现多模态理解与生成。

## 数据流

```
原始输入(图/音)
   └─ encoder      → 模态特征
        └─ projector → 投影到 hidden_size
             └─ 替换 input_ids 中的占位 token 位置的 embedding
                  └─ 进入共享 LM 主干
```

## 编码器(Encoders)

把外部模态的原始信号编码为模态特征,冻结预训练权重、不参与 LLM 主干训练。

| 文件 | 类 | 作用 |
| --- | --- | --- |
| `encoders/vision/siglip.py` | `SiglipVisionEncoder` | 图像 → patch 特征(SigLIP) |
| `encoders/audio/sensevoice.py` | `SenseVoiceAudioEncoder` | 语音 → 帧级别 fbank 特征(SenseVoice) |

### SigLIP 视觉编码器

- 基于 `transformers.SiglipVisionModel`(siglip2-base-p32-256)
- 图像被分割为 32×32 像素的 patch,输出 patch 特征序列
- `processor` 负责图像预处理(resize、normalize)
- 输出通过 `MMVisionProjector` 重采样到 `image_token_len=64` 个视觉 token

### SenseVoice 音频编码器

- 基于 FunASR 的 `SenseVoiceEncoderSmall`(阿里达摩院)
- 输入:16kHz 单声道音频 → fbank 特征
- 输出:**帧级别**声学特征(每帧对应约 10ms 音频)
- 编码器输出维度:`d_model=512``audio_hidden_size`- 内部包含 frontend(fbank 提取)+ encoder(Conformer 架构)
- 使用 `funasr.AutoModel` 加载,`trust_remote_code=True`

> 注意:FunASR 的 C 库初始化与 PyArrow 存在冲突,需确保 pyarrow 在 FunASR 模型创建后导入(VAMDataset 使用 lazy import)。

### 音频特征长度

音频经 SenseVoice 编码后的帧数取决于输入长度。由于帧数与文本 token 数不一致,需用 `audio_proj` 重采样到固定长度匹配占位 token:

```
音频 3 秒 @ 16kHz = 48000 采样点
  → fbank (80 维) × ~300 帧
  → audio_proj 重采样到目标帧数(占位 token 数)
  → 替换 input_ids 中的 <|audio_pad|> 位置
```

## 投影器(Projectors)

把 encoder 输出的模态特征投影到 LLM 隐藏维度(`hidden_size`),随后替换 `input_ids` 中的占位 token 位置。

| 文件 | 类 | 作用 |
| --- | --- | --- |
| `projectors/vision.py` | `MMVisionProjector` | 视觉特征 → LLM `hidden_size` |
| `projectors/audio.py` | `MMAudioProjector` | 音频特征 → LLM `hidden_size` |

### MMAudioProjector

```
输入: (B, T_audio, audio_hidden_size=512)
  └─ Linear(512 → hidden_size)
  └─ 2D 位置编码(补偿音频帧与文本 token 的位置差异)
  └─ 重采样到目标 token 数
输出: (B, target_tokens, hidden_size)
```

## VAM 双模态架构

VAM 同时处理视觉和音频:

```
VAM(LMForCausalLM)
  ├─ model: LM                    # 共享主干
  ├─ vision_encoder + proj        # 图像理解
  ├─ audio_encoder + proj         # 语音理解
  └─ talker: TalkerModule          # 语音生成
```

### 多模态特征注入`bridge_layer`(默认第 3 层)之后注入多模态特征:

```python
# Forward 中的多模态注入
hidden_states = self.model.embed_tokens(input_ids)

# Thinker 层
h = self.thinker(hidden_states)

# 注入视觉特征
if pixel_values is not None:
    vision_features = self.vision_proj(self.vision_encoder(pixel_values))
    h = inject_at_positions(h, vision_features, vision_positions)

# 注入音频特征
if audio_inputs is not None:
    audio_features = self.audio_proj(self.audio_encoder(audio_inputs))
    h = inject_at_positions(h, audio_features, audio_positions)

# Talker 层
h = self.talker(h)

# 双 head 输出
logits = self.lm_head(h)
audio_logits = self.talker.decode(h)  # 8 层 Mimi code
```

### 模态组合

VAM 支持灵活的模态输入组合:

| 输入模态 | 设置 | 用例 |
| --- | --- | --- |
| 文本 + 音频 | 提供 `audio_inputs``pixel_values=None` | 语音对话 |
| 文本 + 图像 | 提供 `pixel_values``audio_inputs=None` | 图像理解 |
| 文本 + 图像 + 音频 | 同时提供两者 | 全模态交互 |
| 纯文本 | 两者均为 None | 回退到 LM |

## 设计要点(面试)

- **encoder 冻结 + 只训 projector**:大模型主干易灾难性遗忘,冻结预训练视觉/音频 encoder、只训轻量投影层,是高效多模态对齐的常用做法。
- **`encoders/` 与 `projectors/` 分离**:encoder 负责「理解模态」,projector 负责「对齐到 LLM 空间」,二者职责清晰、可独立替换。
- **可扩展性**:加新模态 = 加一对 encoder+projector + 占位 token,主干不动(符合本仓库分层初衷)。
- 文本侧用特殊占位 token(`<|image_pad|>` / `<|audio_pad|>`)预留位置;
- `forward` 把投影特征写到这些位置,LLM 对文本/视觉/音频 token 一视同仁。
- **3 阶段 SFT**:T2A→audio_proj→full,逐步激活文本生成、音频理解、协同优化能力。