读懂MiniMind-O数据格式:Mimi 8层codebook如何存储与重建语音
【免费下载链接】minimind-o🎙️ A 0.1B Omni model trained from scratch, capable of listening, speaking, and seeing!项目地址: https://gitcode.com/gh_mirrors/mi/minimind-o
MiniMind-O 是一个约 0.1B 参数的开源端到端 Omni 模型,单一权重同时支持文本、语音、图像三模态输入与文本、流式语音输出。本文带你读懂它的语音数据格式:所有语音在训练数据里都被 Mimi 编码器压缩成8 层 codebook 的整数序列(8 层 × 12.5 Hz × 24 kHz),而不是存成原始波形。搞懂这套"语音存成整数、解码回波形"的链路,你就掌握了 MiniMind-O 数据与训练的一半。
📌 上图展示了 MiniMind-O 的整体 IO 形态:Audio Input 最终要变成 Streaming Speech 输出,中间全靠 8 层 Mimi codes 传递。
Mimi 8层codebook:语音的"数字压缩包"
传统 TTS 数据存波形,动辄几 MB 一条;MiniMind-O 的做法是把语音先过一遍Mimi 神经音频编解码器(Mimi/Moshi 生态,冻结使用,约 96M 参数,权重见model/mimi/),压缩成整数码流:
| 参数 | 值 | 含义 |
|---|---|---|
| codebook 层数 | 8 层(L0~L7) | 每帧语音拆成 8 个整数,逐层细化声学细节 |
| 每层词表大小 | 2048 | 每个 code 是 0~2047 的整数 |
| 帧率 | 12.5 Hz | 每 80 ms 一帧,2 秒语音 = 25 帧 |
| 解码输出 | 24 kHz 波形 | 8 路 code 对齐后送入 Mimi 解码器还原波形 |
可以把它理解为"语音版的 BPE":第 0 层 code 承载最粗的语义/韵律骨架,后面 7 层逐层补充音色、细节。正因为只有 8 路整数序列,MiniMind-O 才能把语音当作 token 直接塞进 Transformer 训练,而不需要级联独立 TTS。
配置层面,音频词表共2112 = 2048 个 Mimi code + 64 个特殊 token,其中关键的三个定义在 model/model_omni.py:
audio_pad_token=2049(<|音频|>):序列中的占位符audio_stop_token=2050:告诉模型"这层说完了"audio_spk_token=2051:说话人(音色)条件占位符
Parquet训练数据:一条样本长什么样
训练数据是Parquet 表格(如sft_t2a_mini.parquet、sft_a2a_mini.parquet),每条样本的列结构与读取逻辑在 dataset/omni_dataset.py:
| 列名 | 内容 | 与 codebook 的关系 |
|---|---|---|
conversations | 对话文本(JSON 字符串) | Thinker 侧的文本序列 |
answer_audios | 每轮回复语音的Mimi codes | ⭐ 核心列:一维整数列表,每 8 个连续数字 = 1 帧 |
question_audios | 提问语音原始字节 | A2A 场景,运行时重采样到 16 kHz 过 SenseVoice 编码 |
ref_audios | 参考音色的 Mimi codes | 音色克隆条件,同样按 8 个一组平铺 |
spk_emb | 192 维说话人向量 | CAM++ 提取的音色 embedding |
注意一个关键细节:code 在 parquet 里是"帧优先"平铺的一维列表,即[帧0的8个code, 帧1的8个code, ...]。数据加载时按 8 个一组拆回 8 层,代码在 dataset/omni_dataset.py:
audio_codes_8layers = [[] for _ in range(8)] for i in range(0, len(tokens) - 7, 8): for j in range(8): audio_codes_8layers[j].append(tokens[i + j]) # 第 j 层追加第 i/8 帧的 code for layer in audio_codes_8layers: layer.append(self.audio_stop_token) # 每层末尾补 stop 标记也就是说,2 秒的语音在表格里只是约200 个整数,一行数据不到 2 KB——这就是"全语音数据统一转成 Mimi codes 存储"带来的空间优势,full 数据集约 1600+ 小时语音因此可以放进几份 parquet 文件。
8路code如何进入模型:9路输入与MTP延迟对齐
训练时,样本会被拼成一个9 路张量:前 8 路是音频 code 序列,最后 1 路是文本 token(dataset/omni_dataset.py)。8 层 code 不是同一时刻同时出现的,而是按MTP(Multi-Token Prediction)延迟调度阶梯式展开——第 L 层比第 0 层晚 L 个位置开始,整体形成斜向排布:
填充逻辑见 dataset/omni_dataset.py:第i层的目标 code 从assistant_start + i + 1位置开始写入,只覆盖最后一个 assistant 轮次(之前的轮次全部置-100不参与 loss)。配合 trainer/train_sft_omni.py 中的音频损失:8 层各自算 CrossEntropy,其中stop token 的 loss 被放大 10 倍(1 + stop_mask * 9),确保模型学会准时"闭嘴"。
生成侧对应同样的调度:model/model_omni.py 中,只有当 8 层都对齐(延迟 7 步后)才产出一个完整的 8 码帧;一旦某层采到>= 2048的特殊码就记为停止。
从codes回到波形:Mimi解码器重建24kHz语音
推理时,Talker 逐帧产出的 8 码帧最终交给 Mimi 解码器还原波形,完整流程在 eval_omni.py:
- 收集所有长度恰为 8 的帧,转成
(8, T)张量; torch.where(codes >= 2049, 0, codes)——把 pad/stop 等特殊码归零;model.mimi_model.decode(filtered)输出24 kHz波形,写 wav 后转 mp3。
解码器是transformers的MimiModel(eval_omni.py 加载model/mimi/),支持增量解码,所以语音可以边生成边播放,实现流式输出与实时打断。下面的 A2A 样例就是这条"语音进 → codes → 语音出"链路的重建结果:
音色克隆:ref_codes与spk_emb的双重注入
想换音色,MiniMind-O 不需要改权重,而是把参考音频的 codes 和说话人向量直接塞进序列(dataset/omni_dataset.py):
- ref_codes:参考音色的 8 层 Mimi codes(
ref_audios列),右对齐贴在回复起点前,相当于把"目标音色的开头"当上下文喂给 Talker; - spk_emb:192 维 CAM++ embedding 填到
audio_spk_token占位处,提供稳定说话人约束; - 50% 概率丢弃 ref_codes,让模型两条路都学会。
内置音色条件存放在 model/speaker/voices.pt(5 个)与 model/speaker/voices_unseen.pt(7 个训练未见音色),每份包含ref_codes+spk_emb,推理时直接加载替换即可换声线。
快速上手:三步验证你读懂了
- 看数据结构:
dataset/omni_dataset.py的__main__块可直接打印一条 parquet 样本的列名与首轮 code 长度(dataset/omni_dataset.py); - 跑文本问答:
python eval_omni.py --mode 0,观察生成的 audio frames 数量; - 跑音色克隆:
python eval_omni.py --mode 3,对比不同ref_codes下同一句回答的音色差异。
常见问题
Q:为什么是 8 层而不是 1 层 codebook?单层 code 压缩率不够,音色与音质细节会丢失;多层残差量化让第 0 层管"说什么、怎么停顿",高层管"听起来像谁、多自然",Talker 用 8 个轻量 adapter 共享主体预测这 8 路(model/model_omni.py)。
Q:code 和文本 token 在同一个序列里会冲突吗?不会。文本走 Thinker 词表,code 走独立的 2112 音频词表,两者只是位置对齐;Thinker 与 Talker 通过中间层 hidden bridge 通信,语音条件以 8 路 code 流形式注入 Talker。
Q:12.5 Hz 帧率意味着什么?每秒 12.5 帧、每帧 8 个 code,即 100 次 code 决策/秒——远慢于 24 kHz 采样率,让 0.1B 的 Talker 也能负担自回归生成。
小结
- MiniMind-O 的训练数据是 Parquet 表格,语音统一存成Mimi 8 层 codebook 的整数序列(帧优先平铺,每帧 8 个 code);
- 加载时按 8 个一组拆层,8 路 code 与 1 路文本拼成 9 路张量,按 MTP 延迟阶梯对齐后参与训练;
- 推理时 Talker 逐帧产出 8 码帧,Mimi 解码器增量还原24 kHz流式波形,音色由
ref_codes + spk_emb上下文注入。
读懂这套格式,你已具备修改数据、复现训练乃至替换 codec 的全部基础,建议配合 trainer/train.sh 把 mini 数据集完整跑一遍。
【免费下载链接】minimind-o🎙️ A 0.1B Omni model trained from scratch, capable of listening, speaking, and seeing!项目地址: https://gitcode.com/gh_mirrors/mi/minimind-o
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考