Architecture

架构精读

MiniMind-O 的核心是 Thinker/Talker 双路径。Thinker 负责理解和文本生成,Talker 在 Thinker 的中间表征上生成 8 路 Mimi audio codes。

总览

MiniMind-O 架构图
Thinker 接收文本、语音、图像;Talker 接收 bridge state、audio code history 和音色条件。
输入占位文本 token 中出现 <|audio_pad|><|image_pad|>
特征注入冻结 encoder 产生特征,projector 对齐到 hidden size。
ThinkerTransformer 主干融合上下文并输出文本 logits。
Talker根据 bridge state 预测 8 层 Mimi codes。

OmniConfig 的关键参数

参数默认值作用
`num_talker_hidden_layers`4Talker 的 Transformer block 数。
`talker_hidden_size`768Talker 隐藏维度,dense 版本与 Thinker 对齐。
`audio_vocab_size`21122048 个 Mimi codes 加特殊音频 token。
`audio_pad_token`2049音频输入流的 padding。
`audio_stop_token`2050Talker 停止输出音频的标志。
`audio_spk_token`2051speaker embedding 注入位置。
`image_token_len`64每张图对应的视觉 token 数。
`bridge_layer``num_hidden_layers // 2 - 1`把 Thinker 中间层表征送给 Talker。

源码入口:`model/model_omni.py` 的 `OmniConfig`。

Thinker:理解与文本生成

Thinker 本质上是 MiniMind 语言模型主干。它先把文本 token 变成 embedding;如果 prompt 中含有音频或图像占位符,就用 projector 后的真实模态特征替换对应位置的 embedding。

音频注入

encode_audio_inputs 调用 SenseVoice encoder,inject_audio_features 找到连续 <|audio_pad|> 段并替换。

图像注入

get_image_embeddings / encode_image_inputs 调用 SigLIP2,count_vision_proj 替换 <|image_pad|> 段。

文本 logits

Thinker 最后经过 norm 和 `lm_head`,用于 assistant 文本回复的 next-token prediction。

bridge state

循环 Thinker layers 时,在 `bridge_layer` 处保存中间 hidden states,供 Talker 使用。

Talker:从语义到声音

Talker 不是另一个 TTS 服务,而是一个小 Transformer。它同时看两类信息:Thinker 的 bridge states 和已经生成/给定的 8 路 audio code history。两者分别经 `embed_proj` 和 `codec_proj` 后相加,再进入 Talker blocks。

TalkerEmbedding

8 路 audio codes 使用共享 embedding 主体和轻量 adapter,减少参数重复。

TalkerHead

输出端同样是共享 base + 8 个 adapter,每层 codebook 各自得到 logits。

Speaker Condition

遇到 `audio_spk_token` 时,用 `spk_proj(spk_emb)` 替代该位置 embedding。

MiniMind-O 序列格式
训练样本中有 8 路 audio stream 和 1 路 text stream。Talker 的目标是预测 audio labels。

一次 forward 的路径

input_ids: (B, 9, T) 或 (B, T)
  ├─ text_ids = input_ids[:, 8, :]
  ├─ audio_ids = input_ids[:, :8, :]
  ├─ Thinker embed text
  ├─ inject audio / image features at special-token spans
  ├─ run Thinker layers, save bridge_states
  ├─ Talker embed audio codes
  ├─ add speaker embedding at audio_spk_token
  ├─ combine embed_proj(bridge_states) + codec_proj(talker_emb)
  ├─ run Talker layers
  └─ return text logits + 8 audio logits
为什么用中间层 bridge:embedding 层语义不足,最后一层更贴近文本 next-token 目标;中间层通常已经融合上下文和模态信息,又没有被 LM head 过度塑形。

流式生成

`stream_generate` 每轮先采样文本 token,再根据延迟调度补齐各层 audio code。由于一帧音频需要 8 层 codebook,代码用 `audio_step` 和 `step - 7 + i` 把不同层对齐,只有 8 层都活跃时才向外 yield 一个 audio frame。

文本停止

文本遇到 `eos_token_id` 后进入 finished 状态,但音频还会继续补齐到 stop token。

思考模式

open_thinking=1 时等待 </think>\n\n 后再启动音频输出,避免把思考过程读出来。