前面我们已经看过 MiniMind 语言模型和 MiniMind-V 视觉模型。MiniMind-O 是这个系列里的第三站。
如果说 MiniMind 是“会读写文本”,MiniMind-V 是“能看图再回答”,那么 MiniMind-O 想做的是更完整的交互:
能读文本 能听语音 能看图片 能输出文本 还能直接说话
README 里把它称为一个小规模端到端 Omni 模型。这里的 Omni 可以先简单理解成“多种输入、多种输出统一到一个模型链路里”。
MiniMind-O 的发布模型 minimind-3o 主干约 0.1B 参数,目标不是和大模型拼效果,而是让我们能从代码层面看懂一个完整的 Omni 链路。
1. 为什么不是 ASR + LLM + TTS
做语音助手最直接的办法是三段式:
用户语音 -> ASR 转文字 -> LLM 生成回答文本 -> TTS 把文本读出来
这条路工程上很好理解,也很容易拼起来。但它有几个问题:
1. 语音先被转成文字,语气、停顿、情绪会丢一部分。 2. 每段系统都有自己的延迟,整体交互更慢。 3. LLM 和 TTS 是分开的,声音输出只是后处理。
MiniMind-O 尝试走另一条路:
不要先把所有东西拆成独立模块, 而是在模型 hidden state 层面把文本、语音、图像接起来。
这就是 README 里强调的端到端 Omni 链路。
2. MiniMind-O 的三个外部工具
MiniMind-O 不是自己从原始图片、原始波形开始学所有东西。它用了几个冻结的外部模型作为前端或后端:
SenseVoice-Small: 把用户语音变成语音特征。 SigLIP2: 把用户图片变成视觉特征。 Mimi: 把语音 waveform 和离散 audio codes 互相转换。
可以先这样理解:
SenseVoice 负责“听”的前端特征 SigLIP2 负责“看”的前端特征 Mimi 负责“说”的音频编解码 MiniMind-O 负责把这些特征接进 LLM,并学习如何回答
这些外部模型在训练中通常是冻结的。真正训练的是 MiniMind-O 的主体,包括 Thinker、Talker 和 projector。
3. Thinker 和 Talker:一个负责想,一个负责说
MiniMind-O 的主体可以先分成两条路径:
Thinker: 负责理解文本、语音、图像,并生成文本层面的回答。 Talker: 负责把 Thinker 的语义状态变成 Mimi audio codes, 最后由 Mimi decoder 还原成声音。
整体图可以画成:
文本输入 --------------------+
|
用户语音 -> SenseVoice -> audio projector ----+
|
用户图片 -> SigLIP2 -> vision projector ---+-> Thinker
|
| text logits
v
文本回答
Thinker 中间层 hidden
|
v
Talker + 历史 audio codes
|
v
Mimi audio codes
|
v
Mimi decoder
|
v
语音输出
这里最重要的是:Talker 不是一个外置 TTS。它不是等 Thinker 生成完整文本后,再把字符串读出来。Talker 接收的是 Thinker 的中间层 hidden state,以及自己已经生成过的历史 audio codes。
所以更准确的说法是:
Talker 学会在 Thinker 的语义条件下,直接生成可解码的语音 token。
4. 多模态输入如何进入 Thinker
MiniMind 原本只能处理文本 token。文本 token 会先查 embedding table,变成 hidden states:
text token id -> text embedding -> Transformer
语音和图片没有办法直接查文本 embedding table,所以 MiniMind-O 使用占位 token:
<|audio_pad|> <|image_pad|>
流程是:
1. prompt 里先放一串 <|audio_pad|> 或 <|image_pad|> 2. tokenizer 把它们变成普通 token id 3. 模型先得到这些 token 的 embedding 4. 再用真实的语音/图像特征替换这些位置的 hidden states
这样做以后,Thinker 看到的仍然是一条序列:
文本 hidden + 语音 hidden + 图像 hidden
Transformer 不需要知道这些 hidden 原来来自哪里,只需要在同一条序列里建模。
5. 为什么要从 Thinker 中间层接给 Talker
代码里有一个 bridge_layer。默认情况下,Talker 不直接拿 Thinker 最后一层输出,而是拿中间层的 hidden state。
直觉上:
Thinker 中间层: 更像通用语义表示,已经融合了上下文和多模态信息。 Thinker 最后层: 更贴近文本 token 预测,准备接 lm_head。
Talker 要的是“这句话大概要表达什么”,而不是“下一个文本 token 的分布已经长什么样”。所以中间层更适合当语音生成条件。
6. MiniMind-O 学习链路的主线
把目前的代码学习主线压缩成一句话:
OmniDataset 构造 9 路训练输入; MiniMindOmni.forward 先跑 Thinker,再跑 Talker; 训练脚本同时优化 text_loss 和 audio_loss。
其中 9 路输入是:
前 8 路: Talker 的历史 Mimi audio codes 第 9 路: Thinker 的文本 token ids
训练时,Thinker 学文本回答,Talker 学语音 codes。推理时,模型一边生成文本,一边生成音频 codes,再由 Mimi 解码成声音。
7. 本文小结
MiniMind-O 的整体设计可以先记成三句话:
1. Thinker 负责理解和文本语义。 2. Talker 负责根据 Thinker hidden 生成语音 codes。 3. 语音/图像通过 projector 注入到文本序列的占位 hidden 中。
它不是简单的 ASR + LLM + TTS 串联,而是把语音输入、图像输入、文本输出、语音输出都放进一条可训练链路里。
下一篇我们先补上最重要的基础:音频为什么可以像文本一样变成 token,Mimi codebook 到底是什么。