Inference & WebUI

推理与交互

推理阶段要理解三件事:模型怎么加载、不同输入模式如何构造 prompt、音频 codes 如何被 Mimi 解码并流式播放。

命令行推理入口

`eval_omni.py` 支持原生 PyTorch 权重和 transformers 目录两种加载方式。它会加载 tokenizer、MiniMind-O 模型、SenseVoice/SigLIP2 和 Mimi decoder。

# 原生 PyTorch 权重
python eval_omni.py --load_from model --weight sft_omni --mode -1 --prompt_lang 2

# transformers 格式权重
python eval_omni.py --load_from minimind-3o --mode 0,2,4
`--mode`含义输入来源
`0`text → text/audio脚本内置中英文 prompt。
`1`multi-turn → text/audio脚本内置多轮历史。
`2`audio → text/audio`dataset/eval_omni/audio-*.mp3`。
`3`clone voice → text/audio`model/speaker/voices_unseen.pt`。
`4`image → text/audio`dataset/eval_omni/*.jpg`。
`5`image + audio → text/audio图片样例 + `img-*.mp3` 语音问题。
`-1`全部模式等价于 `0,1,2,3,4,5`。

文本与语音输出

`eval_sample` 会调用 `model.generate(..., stream=True, return_audio_codes=True)`。Thinker 的文本增量打印到终端;Talker 的 audio frame 收集起来后交给 `MimiModel.decode`,保存为 mp3。

chat template → input_ids
  ├─ MiniMindOmni.generate(stream=True)
  │   ├─ yield text token ids
  │   └─ yield 8-layer audio frame
  ├─ MimiModel.decode(codes)
  └─ save mp3 to output_audio/
语音不是每个 token 都立刻可播放:Talker 需要等 8 层 codebook 都对齐后才输出一个完整 audio frame,因此流式播放里会有短暂启动延迟。

语音与图像输入

语音输入

OmniDataset.process_audio 读取 mp3/wav,重采样到 16 kHz,生成 SenseVoice fbank。prompt 则由若干 <|audio_pad|> 占位符组成。

图像输入

PIL 读取图片,SigLIP processor 输出 pixel_values。prompt 在文本后拼接 64 个 <|image_pad|>

图像到语音样例
图像问答链路:视觉编码、文本生成、语音渲染在同一套推理流程里完成。

音色选择与克隆

发布资源里包含 `model/speaker/voices.pt`、`voices_unseen.pt` 和手工克隆文件 `voice_clone.pt`。每个音色通常包含 `ref_codes` 和 `spk_emb`,推理时作为 Talker 条件输入。

内置音色

来自 `voices.pt`,WebUI 中标为内置。

Unseen 音色

来自 `voices_unseen.pt`,适合观察零样本迁移。

手动克隆

`webui/web_demo.py` 可上传参考音频,生成 `voice_clone.pt`。

Gradio WebUI

`scripts/web_demo_omni.py` 是较直接的 Gradio demo。它会扫描 `scripts/` 目录下的 transformers 模型子目录,所以需要先把模型复制进去。

cp -r minimind-3o ./scripts/minimind-3o
cd scripts
python web_demo_omni.py --port 8888

这个界面适合快速体验文字、音频、图片和音色选择。

电话模式 WebUI

`webui/web_demo.py` 加上 `webui/web_demo.html` 提供更完整的实时交互:前端录音、WebSocket 传输、后端 VAD 判断、模型流式回复、Mimi 解码播放,以及用户打断。

实时交互示意
实时交互的状态在 listening、prefill、speaking、interrupt 之间切换。
cd webui
python web_demo.py --port 8888

实时打断如何工作

`RealtimeSession` 是纯工程层,不耦合模型权重。它用 Silero VAD 对 16 kHz 音频块打分:检测到足够长的语音后进入 speaking;静音持续到阈值后返回 speech_end;如果模型正在 generating 且用户又开始 speaking,就返回 interrupt。

listening
  ├─ speech probability > threshold → speaking
  ├─ silence long enough → speech_end
  └─ speaking while generating → interrupt