MiniMind-O 学习笔记 14:从 LLM 到 Omni,先看懂整体架构

前面我们已经看过 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 到底是什么。

发表评论