深入解析 Kyutai Speech-To-Text:Transformers 中基于 Mimi 编解码与流式解码的语音识别模型实战指南
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
Kyutai Speech-To-Text(Kyutai STT)是 Kyutai 实验室于 2025 年 6 月正式并入 Hugging Face Transformers 的端到端语音识别模型系列,其核心是把 Mimi 神经音频编解码器 的流式离散音频表征与Moshi 风格的自回归语言模型解码器组合成一套统一的双模态 token 体系。本文以 Kyutai Speech-To-Text 官方模型文档为主体,结合仓库内该模型的完整实现,梳理其工作原理、配置参数、特征提取管线与流式generate机制,并给出可直接运行的单条与批量推理示例。
通过本文,你将掌握:如何在 Transformers 中加载并运行kyutai/stt-2.6b-en-trfs等检查点完成英文/法文语音转写;理解音频如何被实时切成 Mimi token 窗口并送入自回归解码器;以及KyutaiSpeechToTextConfig、KyutaiSpeechToTextProcessor、KyutaiSpeechToTextFeatureExtractor与KyutaiSpeechToTextForConditionalGeneration各自承担的角色与关键参数。
模型总览:一套双模态的"音频编解码 + 自回归解码"架构
根据模型文档与源码,Kyutai STT 不是传统的"编码器-注意力解码器"式 ASR,而是一条以音频 token 为第一公民的生成式语音转写链路:
- 前端编码:Mimi codec 的流式离散化。模型内置一个 Mimi 编解码器(在 HF Transformers 中另有独立模型支持,见 Mimi 文档)。Mimi 以流式方式把 24 kHz 的原始波形编码为离散 token 序列,为自回归解码器提供"逐帧音频表征"。
- 后端解码:Moshi 风格自回归解码器。解码器是一个类似 Moshi(见 Moshi 文档)的自回归 Transformer,逐帧预测文本 token 与 32 路 RVQ(残差向量量化)码本对应的音频 token。
这种"双模态 token 空间"设计的直接收益是:语音转写可以被建模为与 Moshi 一致的帧级对齐生成——每一帧同时包含文本与音频信息,解码器在帧序列上自回归地前进,从而实现低延迟的流式语音识别,而无需像传统 ASR 那样等待整段音频编码完毕。
官方文档声明当前发布了两档检查点:
kyutai/stt-1b-en_fr:约 1B 参数,支持英语与法语双语转写;kyutai/stt-2.6b-en:约 2.6B 参数,专注英文、以最大化转写准确率为目标。
本文所有示例与仓库测试均使用 HF 侧的镜像检查点kyutai/stt-2.6b-en-trfs(见 集成测试 setUp)。该模型于 2025-06-25 由 Eustache Le Bihan 贡献进入 Transformers。
快速上手:单条与批量推理
单条语音转写
模型文档给出的最小推理流程分为五步:加载模型与处理器 → 载入音频 → 预处理 →generate→ 解码。以下代码可直接运行(需要torch、datasets、transformers):
from datasets import Audio, load_dataset from transformers import KyutaiSpeechToTextForConditionalGeneration, KyutaiSpeechToTextProcessor # 1. 加载模型与处理器 model_id = "kyutai/stt-2.6b-en-trfs" processor = KyutaiSpeechToTextProcessor.from_pretrained(model_id) model = KyutaiSpeechToTextForConditionalGeneration.from_pretrained(model_id, device_map="auto") # 2. 加载音频样本(注意:模型期望 24000 Hz 采样率) ds = load_dataset( "hf-internal-testing/librispeech_asr_dummy", "clean", split="validation" ) ds = ds.cast_column("audio", Audio(sampling_rate=24000)) # 3. 准备模型输入 inputs = processor( ds[0]["audio"]["array"], ) inputs.to(model.device) # 4. 推理生成 output_tokens = model.generate(**inputs) # 5. 解码生成的 token print(processor.batch_decode(output_tokens, skip_special_tokens=True))几点实操细节:
- 采样率必须为 24000 Hz:处理器内部的特征提取器默认
sampling_rate=24000(源码见 feature_extraction_kyutai_speech_to_text.py)。集成测试中加载 LibriSpeech 后同样通过cast_column("audio", Audio(sampling_rate=24000))重采样(见 test_modeling_kyutai_speech_to_text.py)。 device_map="auto"依赖accelerate。若在 CPU 上运行可改传device_map="cpu",model.forward的 docstring 示例即采用此写法。- 该模型是条件生成任务,
inputs通常无需labels,直接generate即可。
批量推理:自动 padding
模型文档同时给出了批量推理版本,核心区别是向处理器传入音频数组列表并开启 padding:
from datasets import Audio, load_dataset from transformers import KyutaiSpeechToTextForConditionalGeneration, KyutaiSpeechToTextProcessor # 1. 加载模型与处理器 model_id = "kyutai/stt-2.6b-en-trfs" processor = KyutaiSpeechToTextProcessor.from_pretrained(model_id) model = KyutaiSpeechToTextForConditionalGeneration.from_pretrained(model_id, device_map="auto") # 2. 加载音频样本 ds = load_dataset( "hf-internal-testing/librispeech_asr_dummy", "clean", split="validation" ) ds = ds.cast_column("audio", Audio(sampling_rate=24000)) # 3. 收集多条音频并做批处理(返回 PyTorch 张量并按最长样本补零) audio_arrays = [ds[i]["audio"]["array"] for i in range(4)] inputs = processor(audio_arrays, return_tensors="pt", padding=True).to(model.device) # 4. 推理生成 output_tokens = model.generate(**inputs) # 5. 逐条解码 decoded_outputs = processor.batch_decode(output_tokens, skip_special_tokens=True) for output in decoded_outputs: print(output)从KyutaiSpeechToTextProcessorKwargs的默认值可以看到(processing_kyutai_speech_to_text.py),该处理器在音频侧默认注入sampling_rate=24000、公共侧默认return_tensors="pt",这正是示例中可以不显式传这两个参数的原因。
双模态 token 空间:解码器到底在预测什么
要正确理解generate的输出,需要先明白模型的输入/输出 token 组织方式。这是全模型最"反直觉"但最核心的设计,源码集中在两个位置:
- 可学习嵌入层按码本做偏移求和(modeling 文件)。词表维度定义为
vocab_size + num_codebooks * codebook_vocab_size + 1,默认即4001 + 32*2049 + 1。输入序列最后一维是1 + num_codebooks = 33:第 0 列是文本 token,其余 32 列分别对应 Mimi 的 32 个 RVQ 码本。嵌入前,每个非 padding 的音频 token 都会加上自己所属码本的偏移量(offsets[k] = vocab_size + k * codebook_vocab_size),随后在最后一维上求和,得到 (batch, seq, hidden) 的融合向量。 - 生成阶段逐帧拼接(
prepare_inputs_for_generation,modeling 文件)。解码器每个时间步的位置上,input_ids会被构造成(batch, seq, 2)的形态——文本 token 列拼接上当前帧的 32 路音频 token;当某帧使用起始标记时,该帧 32 路音频 token 全部被替换为audio_bos_token_id。
用配置文件中的默认 token id 可以更直观地理解这套体系(configuration_kyutai_speech_to_text.py):
| 字段 | 默认值 | 含义 |
|---|---|---|
vocab_size | 4001 | 文本侧词表大小 |
codebook_vocab_size | 2049 | 单个码本的音频 token 词表大小 |
num_codebooks | 32 | RVQ 码本数量(与 Mimi 对齐) |
audio_bos_token_id | 2048 | 音频流起始标记(每个码本内最后一个 id) |
audio_pad_token_id | 69569 | 音频填充标记(等于4001 + 32*2049,同时充当嵌入层padding_idx) |
bos_token_id | 48000 | 文本/整体序列 BOS 标记 |
pad_token_id | 3 | 文本侧 padding 标记 |
eos_token_id | None | 无显式 EOS,靠帧数约束生成长度 |
值得注意的一个推断:正因为vocab_size、codebook_vocab_size与num_codebooks共同决定嵌入表大小和偏移计算,这几个参数在加载官方检查点时不应随意改动,否则 token 语义会发生偏移。
解码器架构与生成机制源码解析
主干为标准的"深度" Transformer 解码器
KyutaiSpeechToTextModel是一个以hidden_size=2048、num_hidden_layers=48、num_attention_heads=32为主干的自回归解码器(configuration 默认值),并具备以下特征:
- 分组查询注意力(GQA):
num_key_value_heads默认None,在__post_init__中会回退为num_attention_heads(configuration)。 - 滑窗局部注意力:
sliding_window=375,配合max_position_embeddings=750,注意力被限制在较短的局部窗口内,从而压低流式生成时的缓存与算力开销。 - RMSNorm + SiLU 门控 MLP:归一化采用
KyutaiSpeechToTextRMSNorm,FFN 维度ffn_dim=11264,激活为hidden_act="silu"的门控结构(KyutaiSpeechToTextGatingMLP)。 - 旋转位置编码(RoPE):
KyutaiSpeechToTextRotaryEmbedding支持rope_parameters中配置的rope_type与rope_theta;默认实现遵循原始 RoPE 反频率公式(见 modeling 的 compute_default_rope_parameters)。 - 多种注意力后端:模型声明
_supports_flash_attn、_supports_sdpa、_supports_flex_attn(modeling),并支持梯度检查点。测试会对比 eager 与 sdpa / flash_attention_2 的输出等价性(见 test 中的注意力实现对比)。 - fp32 保精度的 codec 分支:
_keep_in_fp32_modules_strict = ["codec_model"],而解码器主网络可安全地以 fp16/bf16 运行(modeling)。集成测试明确断言加载后codec_model保持 fp32、主干与lm_head为 fp16(test)。
generate是如何把音频"喂"给自回归解码器的
KyutaiSpeechToTextForConditionalGeneration重写了多条生成钩子,构成了下图所示的流式链路:
_prepare_model_inputs(modeling):根据输入波形的实际采样长度,调用 codec 的get_encoded_length推算音频 token 窗口宽度audio_window_size,初始化形状为(batch, audio_window_size, num_codebooks)的全零audio_tokens,记录current_window窗口坐标,并为 Mimi 编解码器准备dynamic cache与各层一维因果卷积的padding cache(KyutaiSpeechToTextConv1dPaddingCache)。也就是说,codec 与解码器都持有独立缓存,是流式解码能够逐段推进的基础。prepare_inputs_for_generation(modeling):当生成的文本帧推进到当前窗口末尾时,以start * frame_size到(start + audio_window_size) * frame_size为界切出新的波形片段,调用codec_model.encode(...)在torch.no_grad()下产出该窗口的audio_codes,写回audio_tokens并滑动current_window;随后把"当前帧对应的 32 路音频 token"与文本 token 沿序列最后一维拼接后送入解码器。generate的自动长度约束(modeling):max_audio_frames = input_values.shape[-1] // codec_config.frame_size。若用户未显式给出max_new_tokens,或给出的值超过了最大音频帧数,会被自动钳制到音频帧总数——因此无需手动指定生成长度,模型天然"听多少、转写多少"。
from_pretrained/save_pretrained的重写(modeling)负责把codec_*前缀的生成配置在"模型 GenerationConfig"与"内置 codec 的 GenerationConfig"之间搬运,保证 codec 的流式缓存初始化参数(如sliding_window)在存取后不丢失。
上述流式生成属于实现层面的机制描述;仓库未对 "支持任意长度的实时流式音频输入" 提供文档级承诺,请以实际体验为准。另外测试注释指出:与原始 moshi 代码库相比,QKV 线性层的组织方式差异会使长上下文下的输出逐渐产生偏差,因此集成测试刻意使用较短的输入验证(见 test 注释)。
音频特征提取器:从波形到模型的预处理契约
KyutaiSpeechToTextFeatureExtractor继承自通用的SequenceFeatureExtractor,负责把float32波形整理成模型的input_values与padding_mask(feature_extraction_kyutai_speech_to_text.py)。其构造参数与语义如下:
| 参数 | 默认值 | 作用 |
|---|---|---|
feature_size | 1 | 特征维度,1 表示单声道,2 表示立体声 |
sampling_rate | 24000 | 音频数字化采样率(Hz) |
padding_value | 0.0 | 填充使用的常数值 |
chunk_length_s | None | 若设置,音频会按该秒数切片后再编码 |
overlap | None | 相邻 chunk 的重叠比例;用于计算chunk_stride = int((1.0 - overlap) * chunk_length) |
audio_delay_seconds | 0.0 | 在音频之后追加的延迟秒数(右侧补零) |
audio_silence_prefix_seconds | 0.0 | 在音频之前追加的静音秒数(左侧补零) |
在调用管线(__call__,见 feature_extraction 源码)内部还有几个容易被忽视但很重要的行为:
- 强制的采样率校验:若显式传入
sampling_rate且与self.sampling_rate不一致,会直接抛出ValueError;若未传则会打印日志警告。建议在调用处理器时始终带上 24 kHz 音频,避免静默错误。 - 默认开启 padding:
padding=None时被解释为True(补到 batch 内最长样本)。若同时开启truncation会报错。 - 输入形态约定:单声道要求波形是
(num_samples,)的一维数组,立体声要求(2, num_samples);每条样本内部会做转置与 float32 转换,非法维度会抛错。 - 右侧至少补 1 秒零:
pad_right = int((audio_delay_seconds + 1.0) * sampling_rate)(feature_extraction),即默认对每条音频右侧补满 24000 个采样点,左侧按audio_silence_prefix_seconds补零,且padding_mask同步扩展。这解释了为何输入会被模型"听到"得比实际语音更长——这是与 Moshi/Mimi 对齐的约定,解码器在这些帧上只产出空白或结束性文本。 - 分块预处理:当设置了
chunk_length_s与overlap时,max_length会被规整为 chunk 步长的整数倍边界((nb_step - 1) * chunk_stride + chunk_length),保证切片窗口对齐。
仓库内还提供了chunk_length与chunk_stride两个动态属性(feature_extraction),它们基于采样率把秒级参数换算为采样点数,并支持在运行时修改chunk_length_s后即时生效——设计意图正是服务于流式/分块场景。
Processor 与 Config:把部件粘合在一起
KyutaiSpeechToTextProcessor
KyutaiSpeechToTextProcessor是标准的ProcessorMixin组合(processing 源码),把一个feature_extractor(音频)与一个tokenizer(文本)绑在一起,__call__时默认把音频侧的采样率设为 24000、统一返回 PyTorch 张量。因此你只需要:
processor = KyutaiSpeechToTextProcessor.from_pretrained(model_id) # 之后 audio 波形会经由 feature_extractor 处理,解码文本则交给内置 tokenizerbatch_decode(..., skip_special_tokens=True)即走 tokenizer 路径把生成的 token id 还原为文本。
KyutaiSpeechToTextConfig
KyutaiSpeechToTextConfig(configuration 源码)是model_type="kyutai_speech_to_text"的配置类,除前文已列的 token 与解码器超参外,还需注意两点:
codec_config子配置:声明为sub_configs = {"codec_config": AutoConfig}(configuration)。若未提供,__post_init__会用AutoConfig.for_model("mimi")生成默认的 Mimi 音频编码器配置,并把frame_size等字段回填到模型配置中(configuration)。也就是说,Kyutai STT 在架构层面内嵌了一个完整的 codec 模型,这也是为什么单个类名同时承载了编码与解码两套网络。- 架构约束校验:
@strict装饰的validate_architecture要求ffn_dim必须为偶数,否则抛错(configuration)。
用配置类手工搭一个随机初始化模型的典型写法为:
from transformers import KyutaiSpeechToTextConfig, KyutaiSpeechToTextForConditionalGeneration configuration = KyutaiSpeechToTextConfig() model = KyutaiSpeechToTextForConditionalGeneration(configuration) configuration = model.config # 通过模型访问已补全的配置代码结构导航:从 modular 到生成文件的工程脉络
该模型在仓库中的源码组织遵循 HF Transformers 的"modular 单源生成"模式:
- modular_kyutai_speech_to_text.py:真正的维护源文件,所有手写改动应落在这里;
- modeling_kyutai_speech_to_text.py:由 modular 自动生成的主实现(文件头有显式警告,禁止手改);
- configuration_kyutai_speech_to_text.py:配置类;
- feature_extraction_kyutai_speech_to_text.py:音频特征提取器;
- processing_kyutai_speech_to_text.py:处理器组合;
- convert_kyutai_speech_to_text_to_hf.py:把原始 moshi/Kyutai 仓库权重(含 Mimi codec 权重)转换为 HF 格式的转换脚本,
write_model/write_processor分别产出模型权重与处理器配置。
模型内的关键子模块按职责划分:KyutaiSpeechToTextFlexibleLinear(为每个码本各维护一份线性层权重)、KyutaiSpeechToTextConv1dPaddingCache(因果卷积流式缓存)、KyutaiSpeechToTextEmbeddings(双模态偏移求和嵌入)、KyutaiSpeechToTextAttention/KyutaiSpeechToTextDecoderLayer(带 RoPE 与滑窗的注意力解码层)、KyutaiSpeechToTextModel(主干)、KyutaiSpeechToTextForConditionalGeneration(组合解码器 +lm_head+ 内嵌 codec 的顶层封装,见 modeling 类定义)。
验证与测试依据
想要验证本文描述的行为,可以关注仓库中针对该模型的测试与 fixture:
- 单元/质量测试文件 tests/models/kyutai_speech_to_text/test_modeling_kyutai_speech_to_text.py:覆盖 fp32 codec + fp16 主干的精度保持、eager/sdpa/flash_attention_2 输出等价性、缓存复用下多次
generate的一致性,以及集成测试(slow)中针对 LibriSpeech dummy 样本的精确 token 级输出对齐(见 EXPECTED_TOKENS 断言)。 - 该模型的配置与权重可从 HF Hub 的
kyutai/stt-2.6b-en-trfs检查点直接获取,处理器与模型共用同一个model_id加载。
运行集成测试需slow标记与 GPU 加速器;日常验证请优先使用 CPU/小样本的单元级测试路径。
小结:何时选择 Kyutai STT,以及进一步阅读
总结一下这套方案的关键画像:基于 Mimi 流式离散音频编码 + Moshi 风格双模态自回归解码,输入约定为 24 kHz 单声道波形,处理器默认右侧补 1 秒零、可配置 chunking 与 overlap;解码器在 48 层、32 码本的深度维度上逐帧生成,generate自动以音频帧数封顶生成长度。这套设计让语音转写天然具备流式、低延迟的自回归生成特性,适合希望把 ASR 纳入端到端生成式建模管线的研究与工程场景。
想进一步深入,建议按以下顺序阅读仓库内资料:
- 本模型的模型卡片原文档 docs/source/en/model_doc/kyutai_speech_to_text.md;
- 前后端依赖的 Mimi 文档 与 Moshi 文档;
- 配置、建模、特征提取与转换脚本的源码(路径见上文"代码结构导航"一节);
- 测试文件 test_modeling_kyutai_speech_to_text.py,其中有真实检查点的集成验证示例。
最终在代码侧,你只需要记住一条最简用法:
inputs = processor(audio, return_tensors="pt", sampling_rate=24000).to("cuda") tokens = model.generate(**inputs) text = processor.batch_decode(tokens, skip_special_tokens=True)【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考