Qwen3-TTS :从模型架构到 RKNN 板端部署

Qwen3-TTS :从模型架构到 RKNN 板端部署

  1. Qwen3-TTS 是怎么把一段文字变成一段语音的?
  2. Qwen3-TTS 模型内部长什么样?
  3. 以瑞芯微平台为例,我们是怎么把它搬到开发板上跑的,以及每一步为什么要那么做?

Qwen3-TTS 来源:https://modelscope.cn/models/Qwen/Qwen3-TTS-12Hz-1.7B-Base


1. TTS 是啥?

TTS 全称为 Text To Speech,文字转语音。

输入一句文本:“你好会上班呀~”,模型输出一段能播放的音频(wav 文件)。这个过程难点在哪?

  • 声音是连续的波形,一秒就有 24000 个采样点。直接逐点预测波形,太难了。
  • 同样一句话,可以说得温柔、可以说得严肃。模型得能控制。
  • 用户只想等零点几秒就听到声音,不能等半分钟。

Qwen3-TTS 的思路是:

1
先把声音压缩成一串编号,再让一个类似 ChatGPT 的语言模型像接龙一样逐个生成这些编号,最后用解码器把编号还原成声音。

2. 8 个基础概念

这些词后面会反复出现,先花两分钟认识它们。

概念 大白话解释
Token(词元) 模型眼里的最小单位。文字被切成 token,声音也被切成 token。模型只认编号,不认字,也不认声音。
Embedding(嵌入向量) 每个编号对应的一串数字(比如 2048 个小数),可以理解为这个编号的数字名片。模型真正计算的是这些向量,不是编号本身。
神经网络 一大堆可以调节的旋钮(参数/权重)。输入进来,经过层层计算,输出结果。训练就是反复拧旋钮,直到输出像样。
语言模型(LM) 专门做接龙的模型:看到前面的内容,预测下一个 token。ChatGPT 就是靠这个写文章的。
自回归(Autoregressive) 接龙的工作方式:每次只预测 1 个,把结果拼回去,再预测下一个,一步接一步。
Codebook(码本) 一本编号到向量的字典。声音被量化后,每一帧都变成字典里的一个编号。
NPU 专门做神经网络计算的芯片。开发板(比如 RK3588)里就有 NPU。
量化(Quantization) 把模型里的数字从精确的 32 位浮点,压缩成 8 位整数甚至 4 位整数。模型变小、变快,精度略降。

忘了没关系,用到时都会再解释一遍。


3. 核心思想一:把声音变成编号

概念备齐了,先讲两个核心思想里的第一个。

3.1 为什么不直接生成波形

一段 1 秒的音频,24kHz 采样率,就是 24000 个浮点数。

如果让模型直接输出这 24000 个数,就像让人用画笔一个像素一个像素地画照片。太慢了,也太难学。

聪明的办法是压缩。

把每秒 24000 个采样点,压缩成每秒两百个左右的编号;需要播放时,再用解码器把编号还原成波形。

这一对压缩器加还原器,合称 Speech Tokenizer(语音分词器)。

  • 压缩方向:音频 → 一串编号(codec tokens)
  • 还原方向:一串编号 → 音频

3.2 Qwen3-TTS 有两个 Tokenizer

Tokenizer-25Hz Tokenizer-12Hz
每秒出多少帧编号 25 帧 12.5 帧
每帧几路编号 1 路(一本码本) 16 路(16 本码本)
还原器 DiT(扩散模型)+ BigVGAN,重 轻量卷积网络(ConvNet),轻
特点 语义信息强,适合长文本 帧率低、延迟极低,适合实时
首包延迟 约 150 ms 约 97~101 ms

Hz 是什么意思?就是每秒切几帧。12.5 Hz = 每 80 毫秒一帧。

RKNN demo 用的是 12Hz 版本,因为延迟低、解码器轻,适合板端。下面只讲它。

3.3 12Hz Tokenizer 的 16 路编号是什么

想象你要向别人描述一幅画:

  • 第 1 条描述:画的是个人,在笑 —— 这是语义,决定内容是什么。
  • 第 2~16 条补充:光线偏暖、笔触细腻、左下角有点阴影 —— 这些是细节,对应音色、语气、韵律。

12Hz Tokenizer 就是这个思路:

1
2
3
4
5
6
每一帧声音(80 毫秒)
│
▼
16 个编号
├── 第 1 路:语义码(说了什么内容)
└── 第 2~16 路:声学码(用什么音色、什么语气说的)

技术上这叫 RVQ(残差向量量化):

  1. 先用第 1 本码本近似原始信号,记下误差(残差)。
  2. 再用第 2 本码本近似这个误差,再记残差。
  3. 一层层补,越往后越细。

每本码本有 2048 个条目,所以每路编号的取值范围是 0~2047。

一帧 = 16 个编号,每个编号 0~2047。
一秒 12.5 帧,也就是每秒 200 个编号。
对比原始音频一秒 24000 个数 —— 压缩了 100 多倍。

这个压缩比,就是语言模型能接管声音生成的前提:200 个 token/秒的接龙游戏,模型擅长。


4. 核心思想二:像接龙一样一个字一个字生成

编号的来路清楚了,接下来看这些编号是怎么被生成出来的。

4.1 说话这件事,变成了预测下一个编号

训练好的 Qwen3-TTS 主体(源码里叫 talker),本质是个语言模型。

它的工作和 ChatGPT 一模一样:

看到前面的所有输入 → 预测下一个 token。

区别只在预测对象:

  • ChatGPT 预测的下一个 token 是文字。
  • talker 预测的下一个 token 是声音的第 1 路编号。

4.2 双轨制:文字一条轨,声音一条轨

这里有个关键设计,叫 dual-track,双轨。

说话时,文字和声音是同步推进的:

1
2
3
4
5
6
时间 ──────────────────────────────────────────►

文字轨: 你 好 , 欢 迎 使 用 ...
│ │ │ │ │ │ │
声音轨: a1 b1 c1 d1 e1 f1 g1 ...
(每个 a1/b1 是一帧的 16 路编号)
  • 文字轨决定接下来说什么。
  • 声音轨决定用什么声音说出来。
  • 两条轨的向量在每个时间步相加,一起喂进模型。

好处是文字可以流式输入:上游大模型吐出一个字,TTS 就能开始配一帧声音,不用等全文到齐。这就是低延迟的来源。

4.3 每一步生成的完整动作

这是整个系统最核心的循环。每一步发生 4 件事:

1
2
3
4
5
6
7
第 N 步:
① talker 根据历史,预测本帧的第 1 路编号(语义码)
② code_predictor 拿着 talker 的内部状态 + 第 1 路编号,
一口气补齐第 2~16 路编号(声学码)
③ 16 路编号各自查表得到向量,加起来,作为下一步的输入
④ 同时,从文字轨取出当前位置的文字向量,也加进去
→ 进入第 N+1 步

其中:

  • talker 是主力,大模型(1.7B 或 0.6B 参数),负责全局决策:说什么、什么节奏、什么时候停。
  • code_predictor 是小助手(源码里也叫 MTP,Multi-Token Prediction 模块),小模型,只干一件事:已知第 1 路,补齐另外 15 路。

分开的理由:如果让 talker 一次预测 16 路编号,任务太难、序列太长。拆开之后,语义的骨架归 talker,细节的血肉归 code_predictor,各干各擅长的。

4.4 什么时候停

talker 每步都在预测下一个编号。当它预测出一个特殊的 EOS(结束)编号,整段生成停止。

4.5 声音从哪来:流式解码

声音轨每产出一帧编号,理论上就能立刻解码成 80ms 音频。

实际上为了避免太碎(调度开销大),每攒 4 帧(约 320ms 音频)解码一次。

所以用户体验是:说完话约 0.1 秒,第一小段声音就出来了。后面边生成边播,完全不用等整句合成完。


5. Qwen3-TTS 的整体架构

两个核心思想拼在一起,就是完整的架构。

架构图

几个要点:

  1. 文字侧:普通 Qwen 文本分词器,把文字切成编号,再查表成向量,投影到 talker 需要的尺寸。
  2. 音色侧:想要克隆谁的声音,就把谁的 3 秒音频喂给 Speaker Encoder,得到一个音色向量,拼在输入最前面。模型听到这个向量,就知道该用谁的嗓子说话。
  3. 生成侧:talker + code_predictor 循环接龙,产出 16 路编号。
  4. 还原侧:Speech Decoder 把编号流还原成波形。它是一个轻量因果卷积网络,因果的意思是只看过去、不看未来,所以能边收边解码,这是流式低延迟的关键。

模型家族有哪些

版本 大小 特点
12Hz-1.7B-Base 17 亿参数 支持 3 秒声音克隆,RKNN demo 用的就是它
12Hz-0.6B-Base 6 亿参数 更小更快,首包延迟 97 ms
12Hz-CustomVoice — 预设音色 + 指令控制风格
12Hz-VoiceDesign — 用一句自然语言描述,凭空定制新音色
25Hz 系列 — 长文本更稳,适合有声书类场景

训练数据:500 万小时以上语音,覆盖 10 种语言(中、英、日、韩、法、德、西、葡、意、俄)。


6. 模型内部拆开看

这一节把架构里的每个模块拆开看。稍微深入一点,但只讲结构,不讲公式。

6.1 talker:相同结构的层叠起来

talker 基于 Qwen3 语言模型。结构是标准 Transformer decoder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
输入向量
│
▼
┌─────────────────────┐
│ Decoder Layer × N │ ← 每层干两件事:
│ ① 注意力(Attention)│ 看看历史里哪些信息跟当前有关
│ ② 前馈网络(MLP) │ 做进一步的加工计算
└─────────────────────┘
│
▼
最终输出 → codec_head(一个线性层)→ 得到 3072 个候选的打分
│
▼
按打分采样出下一个编号

几个关键数字(来自源码与 RKNN 头文件):

项目 值 含义
隐藏层维度 2048 每个向量的长度
talker 词表 3072 talker 认识的编号总数
codec 词表 2048 每路声音编号的取值数
文本词表 151936 文字 token 数(Qwen 词表)
最大上下文 8192 一次能记住的历史长度

先说注意力机制。每生成一步,模型会回头扫一遍之前的所有输入,给每个历史位置打一个相关度分数,再按分数加权提取信息。就像你读到“他”这个字时,大脑自动回去找“他”指的是谁。

再看 KV Cache。既然每步都要回头看历史,每次都从头重算就太浪费了。于是把历史算好的中间结果(K 和 V)缓存起来,下次直接用,这就是 KV Cache。它很占内存,这一点在 RKNN 部署时会再提到。

6.2 code_predictor:缩小版的接龙模型

结构和 talker 类似,但小得多。

它的输入是:

  • talker 当前步的内部状态(past_hidden,可以理解为 talker 对前文的总结)
  • 第 1 路编号的向量

它的输出是:依次预测第 2、3、……、16 路编号。

一次生成循环里,talker 走 1 步,code_predictor 就要走 15 步。好在它很小,负担不重。

6.3 Speaker Encoder:音色提取器

结构是一个经典的声音识别网络(ECAPA-TDNN 风格:Res2Net + 注意力池化)。

工作方式:

1
2
3
4
3 秒参考音频
→ 切成梅尔频谱(128 维的声音指纹图)
→ 卷积层层层提取
→ 全段音频汇总成 1 个向量

这个向量就是说话人的音色档案。它跟具体说了什么无关,只跟是谁的声音有关。

6.4 Speech Decoder:编号还原器

12Hz 版本的解码器是个轻量因果卷积网络。

  • 卷积网络:擅长处理局部模式,相当于拿一个滑动窗口顺次扫过数据,计算量比注意力模型小得多。
  • 因果:每个输出只用过去和当前的输入,不等未来。所以音频能像流水一样边算边出。
  • 上采样:每帧编号(80ms)要放大成 1920 个采样点(24000Hz × 0.08s = 1920)。这个数字在 C++ 代码里就叫 kTotalUpsample = 1920。

7. 训练过程速览

结构看完了,再速览一下它是怎么练出来的。这一节了解即可,不影响理解部署。

预训练分三个阶段:

  1. 通才阶段:500 万小时多语言语音,学会文字和声音的基本对应。
  2. 提质阶段:筛选高质量数据继续训,去掉噪声数据带来的坏毛病。
  3. 长文本阶段:把上下文从 8K 拉到 32K,学会处理长内容,稳定生成 10 分钟以上。

后训练也分三个阶段:

  1. DPO(人类偏好对齐):让人类评判哪个合成更好听,让模型往好听的方向靠。
  2. 规则奖励强化学习(GSPO):进一步提升稳定性和指令跟随能力。
  3. 说话人微调:针对特定音色做精调,得到 CustomVoice 版本。

概括起来:学得多、学得精、学得听话。


8. RKNN 部分:为什么要搬上开发板

到这里,模型本身的原理讲完了。接下来换一个视角:怎么把它从服务器搬进开发板。

8.1 场景与动机

原版 Qwen3-TTS 跑在服务器 GPU 上。但很多场景需要端侧运行:

  • 车载座舱 —— 网络可能不好,也不想把语音数据传到云端(隐私)。
  • 智能音箱、机器人 —— 要离线可用,要响应快。
  • 成本 —— 一块开发板比 GPU 服务器便宜得多。

目标硬件是瑞芯微(Rockchip)的 SoC,比如 RK3588 / RK1820。这些芯片里有 NPU(神经网络处理器)。

RKNN 就是瑞芯微 NPU 的软件栈:

1
2
RKNN = Rockchip Neural Network
= 把模型转成 NPU 能吃的格式 + 在板子上运行的运行时库

8.2 三个现实困难

直接把原版模型搬过去是不行的,有三个拦路虎:

困难 解释
内存太小 1.7B 参数,按 16 位浮点存要 3.4 GB。板子内存常常只有几个 GB,还得分给系统和其他程序。
算力太弱 NPU 的算力远低于 GPU。浮点计算原样跑,一句话可能要等几十秒。
格式不通 NPU 不认识 PyTorch 模型。它只吃 RKNN 格式的模型,且喜欢形状固定的计算图。

对应的三板斧:拆模型、做量化、写 C++ 编排。下面逐一讲。

8.3 总体部署流水线

1
2
3
4
┌─────────────┐   导出    ┌────────┐   转换+量化  ┌─────────┐   部署   ┌──────────┐
│ PyTorch 模型 │ ───────► │ ONNX │ ───────────► │ RKNN │ ───────► │ 板端 C++ │
│(官方源码) │ │(中间态)│ │(NPU格式)│ │ 推理程序 │
└─────────────┘ └────────┘ └─────────┘ └──────────┘
  • ONNX:一种通用的模型交换格式,PyTorch 先导出成它,RKNN 工具链再从它转换。
  • 量化发生在 ONNX → RKNN 这一步。

先看量化。


9. RKNN 量化:怎么把模型变小

9.1 量化是什么

模型里存着几十亿个旋钮值,也就是权重。默认每个值用 32 位浮点数存(fp32),精确但占地方。

量化的思路,是用更少的位数近似表示这些数。

打个比方:

  • fp32 像用卷尺量到毫米。
  • int8 像用刻度到厘米的尺子。
  • int4 像只能精确到大约一拃。

大多数时候,厘米精度足够把活干好。

写法约定:

  • w4a16:权重(weight)用 4 位整数,激活值(activation,计算的中间结果)用 16 位浮点。
  • w8a16:权重 8 位,激活 16 位。

权重部署后就不变了,可以放心压得狠一点;激活值随输入变化,得保守一些。

效果:w4a16 相比 fp16,权重体积直接砍到约 1/4。1.7B 的 talker 从约 3.4 GB 压到 1 GB 以内,板子才装得下。

9.2 每个模块的量化方案不一样

RKNN demo 把整个 TTS 拆成了 5 块,每块用不同策略,这是本 demo 的核心设计:

模块 作用 量化方案 为什么这么选
embeds(3 张表) 编号 → 向量的查找表 不量化,直接存 fp16 裸文件 查表不需要计算,放内存里让 CPU 查就行,根本不进 NPU
text_projector 把文字向量投影到 talker 空间 不量化(fp16),单核运行 就是个两层小 MLP,小到无所谓,量化反而可能掉精度
talker 主力生成模型 w4a16 + GRQ 算法 + 真实数据校准 最大最重的模块,必须压到 4 位才装得下;用最好的算法和校准数据保精度
code_predictor 补齐 2~16 路编号 w8a16 中等大小;它每步要跑 15 次,误差会层层放大,所以比 talker 保守,保 8 位
speech_decoder 编号 → 波形 不量化(fp16) 直接决定听感,最敏感;好在它本身很轻,不量化也跑得动

总结出一条选型逻辑:

越大的越狠压,越影响最终观感的越不压,纯查表的干脆不进 NPU。

9.3 校准数据:量化的考前模拟

w4/w8 量化有个关键问题:每个数的取值范围压到多宽?

  • 范围定太小,大数被削顶,失真。
  • 范围定太大,小数挤在一起分不清,也失真。

做法是拿真实输入跑一遍,看看数值实际分布在什么范围。

这就是校准(calibration)。demo 里的流程:

1
2
3
4
5
6
7
8
9
10
11
input_cases.jsonl(一批真实文本 + 参考音频)
│
▼
prepare_talker_quant_data.py
用原始 PyTorch 模型,把每条输入拼成 talker 的输入张量,
一条条存成 npy 文件,生成 _dataset.txt 清单
│
▼
rknn.build(do_quantization=True, dataset=_dataset.txt)
RKNN 工具链把这些真实输入喂进模型,
统计每层激活值的分布,算出最优的量化范围

这也解释了为什么 talker 用 GRQ 算法(瑞芯微针对 LLM 的高质量量化算法)加真实校准,而 code_predictor 只用普通算法(normal)—— 后者输入形态简单,不需要大阵仗。

9.4 自回归模型的特殊处理:两种输入形状

talker 是接龙模型,一生中有两种时刻:

  1. Prefill(读题):第一次把整段输入(提示词、文字、音色向量)一次性喂进去,并行算完。形状是 [1, 128, 2048](128 是示例长度)。
  2. Decode(答题):之后每步只喂 1 个新 token。形状是 [1, 1, 2048]。

NPU 喜欢固定形状,所以 RKNN 转换时用 dynamic_input 显式声明这两套形状,让一张模型支持两种模式。

同时配置 KV Cache:

  • talker 的缓存长度开 2048,用 fp16 存 —— 因为上下文长。
  • code_predictor 的缓存只开 32 —— 它每轮只跑 15 步,够用了。

省下来的每一 MB 内存,对板子都宝贵。

9.5 Embedding 表为什么单独拎出来

三张表(文字表 151936×2048、talker 表 3072×2048、codec 表 15×2048)如果塞进 NPU 计算图:

  • 图变大,转换慢。
  • 查表本质上是按编号取一段内存,CPU 直接 memcpy 就行,根本用不上 NPU。

所以 demo 把它们导出成 fp16 裸二进制文件(*.fp16.bin),板端 C++ 用内存映射(mmap)直接读。简单、快、省 NPU。


10. 板端推理:C++ 是怎么跑起来的

模型文件就位,剩下的问题是怎么把它们组织成一条能实时出声的流水线。

10.1 运行时有哪些文件

1
2
3
4
5
6
7
8
9
10
model/
├── embeds/
│ ├── talker_text_embed.fp16.bin ← 文字查找表
│ ├── talker_input_embed.fp16.bin ← talker 查找表
│ ├── codec_embed.fp16.bin ← 第 2~16 路查找表
│ └── tokenizer.json ← 文字分词器
├── text_projector/*.rknn
├── talker/*.rknn
├── code_predictor/*.rknn
└── speech_decoder/*.rknn

10.2 一次合成的完整流程

以“你好,欢迎使用”为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
① 文字 → 编号
tokenizer.json 把文字切成 token 编号

② 编号 → 向量 → 投影
查 talker_text_embed 表得文字向量
text_projector(RKNN)把它投影到 2048 维

③ 拼输入
拼上控制标记、音色向量(默认音色预编译在
qwen3tts_speaker_embed.h 里)、特殊符号,
组成 prefill 输入

④ talker prefill
整段输入一次喂进 talker.rknn,
建立 KV Cache

⑤ 进入循环(decode)
┌────────────────────────────────────────┐
│ talker 预测第 1 路编号(top_k=50 采样) │
│ ↓ │
│ code_predictor 补齐第 2~16 路 │
│ ↓ │
│ 16 路编号通过回调函数送出 │
│ 同时查表求和 + 加上下一位文字向量 │
│ → 喂回 talker,进入下一步 │
└────────────────────────────────────────┘
直到预测出 EOS(编号 2150)

⑥ 音频解码(与 ⑤ 并行)
speech_decoder 按窗口解码 → 24kHz 音频

⑦ 写盘
每解出一块就追加写入 output.wav

采样参数也写在 C++ 里:talker 用 top_k=50, temperature=0.9, 重复惩罚=1.05;code_predictor 类似但不做重复惩罚。

10.3 双线程流水线:一边生成,一边出声

如果先生成完所有编号,再统一解码,用户要等很久才听到声音。

demo 用了生产者-消费者模式:

1
2
3
4
5
6
7
talker 线程(生产者)
每生成一帧 16 路编号,扔进缓冲队列
│
│ 条件变量唤醒
▼
decoder 线程(消费者)
攒够一批编号,就调 speech_decoder 解成音频,写 wav

两个线程同时干活。用户听到第一段声音时,talker 还在后面继续生成。

10.4 窗口解码:25 帧新货 + 25 帧旧账

speech_decoder 解码时按固定窗口进行:

1
2
3
4
5
6
窗口 = 25 帧旧上下文 + 25 帧新编号 = 50 帧
│
▼ 解码出 50 帧的音频
│
只取后 25 帧对应的音频(25 × 1920 个采样点 ≈ 2 秒)
前 25 帧属于重复劳动,只为保证边界连续

原因是:

  • 因果卷积也需要一点上下文才能出好声音。带上旧帧,新旧音频接缝处才听不出断点。
  • 固定窗口对 NPU 友好(形状固定,效率高)。
  • 每次只取新编号对应的音频,旧上下文只用来垫底,不重复发声。

尾部处理:talker 结束后,剩下的零头编号不够一个标准窗口,就直接一次性解完(drain),避免浪费。


11. 把整条链路串一遍

最后,用一张总表把所有东西串起来。左边是模型原理,右边是部署形态:

阶段 模型层面在做什么 RKNN 部署时变成了什么
1. 读文字 文本分词器切 token tokenizer.json(CPU)
2. 查表 查 embedding 得到向量 3 张 .fp16.bin 表(CPU 查表)
3. 投影 text_projection 映射空间 text_projection.rknn(fp16,不量化)
4. 主生成 talker 接龙,每步出第 1 路 talker.rknn(w4a16 + GRQ + 校准数据)
5. 补细节 code_predictor 补齐 2~16 路 code_predictor.rknn(w8a16)
6. 还原声音 因果卷积解码器 speech_decoder.rknn(fp16,不量化)
7. 编排 Python generate() 循环 C++ 双线程 + KV Cache + 窗口解码

三句话回顾:

  1. Qwen3-TTS 把语音压成 16 路编号,让语言模型用接龙的方式生成声音,再实时解码成音频。
  2. 双轨结构(文字轨带声音轨)让它能流式输入、流式输出,首包延迟约 100 毫秒。
  3. RKNN 部署的精髓是分而治之:拆开模块、按敏感度差异化量化、用真实数据校准、C++ 双线程流水线,最终让 1.7B 的模型在开发板 NPU 上跑起来。

12. 附录:术语表与文件索引

12.1 术语速查

术语 解释
Codec token 声音压缩后的编号
Codebook 编号对应的向量字典
RVQ 一层层编码剩余误差的量化方式,得到多路编号
语义码 / 声学码 第 1 路决定说了什么,第 2~16 路决定怎么说
MTP Multi-Token Prediction,即 code_predictor,一步补多个编号
双轨(dual-track) 文字向量和声音向量逐时间步相加,同步推进
Prefill / Decode 首次批量读入 / 后续逐个生成
KV Cache 缓存历史注意力中间结果,避免重复计算
EOS 结束标记,模型吐出它就停
首包延迟 从输入到第一段音频出来的时间
w4a16 权重 4 位整数、激活 16 位浮点的量化方案
校准(calibration) 用真实输入统计数值范围,确定量化参数
ONNX 模型交换格式,PyTorch 与 RKNN 之间的中转站
因果卷积 只依赖过去输入的卷积,支持边收边算

12.2 文件索引

原理相关(Qwen3-TTS-main/):

文件 看什么
qwen_tts/core/models/modeling_qwen3_tts.py talker、code_predictor、speaker encoder 的全部定义
qwen_tts/inference/qwen3_tts_model.py 对外推理接口(声音克隆 / 声音设计 / 预设音色)
qwen_tts/core/tokenizer_12hz/ 12Hz 语音分词器
examples/test_model_12hz_base.py 最简单的上手示例

技术报告(technical-report/):

文件 看什么
Qwen3-TTS_Technical_Report.md 官方报告:架构、训练、评测全貌

RKNN 部署相关(rknn-Qwen3-TTS-demo/Qwen3_TTS/):

文件 看什么
README.md 部署全流程说明与模块关系图
python/embeds/export_embeds.py 三张查找表怎么导出
python/text_projector/ 投影层 ONNX/RKNN 转换
python/talker/export_talker_onnx.py talker 导出(注意它把只返回最后一帧,改成了返回完整 hidden)
python/talker/export_talker_rknn.py talker 量化配置:w4a16、GRQ、group32、KV Cache 2048
python/talker/prepare_talker_quant_data.py 校准数据怎么生成
python/code_predictor/export_code_predictor_rknn.py w8a16 量化配置
python/speech_decoder/export_speech_decoder_rknn.py 解码器不量化配置
cpp/talker.h 词表大小、特殊编号、采样参数、预设音色表
cpp/main.cc 双线程流水线、窗口解码、wav 写盘

Qwen3-TTS :从模型架构到 RKNN 板端部署
http://www.horus-space.cloud/posts/aee6f67.html
作者
Horus
发布于
2026年8月27日
许可协议