Higgs Audio V2-语音大模型

Higgs Audio V2模型,不仅能处理文本,还能同时理解并生成语音。除了一些常规语音任务外,这个模型还具备一些较为罕见的能力,比如生成多种语言的自然多说话人对话、旁白过程中的自动韵律调整、使用克隆声音进行旋律哼唱以及同时生成语音和背景音乐。

整个过程堪称“大力出奇迹”,直接将1000万小时的语音数据整合到LLM的文本训练,

Higgs Audio v2 采用上图架构图中所示的“generation variant”。其强劲的性能源于三项关键技术创新:

  • 开发了一套自动化注释流程,该流程充分利用了多个 ASR 模型、声音事件分类模型以及我们内部的音频理解模型借助该流程,我们清理并注释了 1000 万小时的音频数据,并将其命名为 AudioVerse 。该内部理解模型在 Higgs Audio v1 Understanding 的基础上进行了微调,并采用了架构图中所示的“理解变体”。
  • 从零开始训练了一个统一的音频分词器,它可以同时捕捉语义和声学特征。
  • 提出了 DualFFN 架构,它增强了 LLM 以最小计算开销建模声学 token 的能力。

Higgs Audio V2 在音频 AI 能力上实现了重大飞跃:

  • 多说话人对话自然流畅:多说话人对话往往难以处理,尤其是在角色无法匹配彼此的情绪和语气时。而借助 Higgs Audio V2,这种对话轻松自然,仿佛现场交流,充满生命力。
  • 支持长音频生成:生成长音频时需要保持声音的一致性,同时兼顾真实感、吸引力和生动性。Higgs Audio 提供条件控制与提示机制,使长音频表现出色。
  • 高保真音质:为了在高品质扬声器和耳机上实现逼真音效,V2 将音频处理管线从 16kHz 升级至 24kHz,带来更佳音质。
  • 高效推理,资源友好:无论是个人项目还是商用部署,推理效率都很重要。我们最小的模型可以在 Jetson Orin Nano 上运行;最新的 3B Audio Generation V2 模型则至少需要 RTX 4090 才能高效推理。
  • 生成真实、有情感的语音表现领先:在 EmergentTTS-Eval 基准测试中,其胜率超过 75%,超越 ChatGPT 4o。
  • 开源:模型开源。
  • 训练数据超千万小时:为实现更高音质与更逼真的语音效果,模型在超过1000万小时的音频上训练,并依托精细的处理与标注流程自动生成训练数据

内容:

传统的语音和文本模型之间相互独立,李沐老师就想,欸,能不能将两者结合起来,直接让LLM用语音进行沟通。那么首先就要知道文本语言模型的本质是用给定的一段指令去生成预测结果,就是将任务先拆解为系统指令(system)用户输入(user)模型回复(assistant)三个部分。system告诉模型,需要做什么事情,例如回答该问题、写一段文字或者其他,user就是告知事情的详细内容,例如问题具体是什么、文字要什么风格。

所以如果要让模型支持语音,就需要为模型增加一个系统命令,在user里输入要转录为语音的文字,让模型从system里输出对应语音数据。这样语音任务就能转换成相同的处理格式,直接打通语音和文本之间的映射,通过追加更多的数据和算力,直接scaling law“大力出奇迹”。

这就引出了新的问题,语音信号本质是连续的,要如何才能在离散的文本token中表示呢?

现有的方法是将一秒的语音信号裁切成多段(如100毫秒一段),为每一段匹配最相似的预定义模板(如45个模板),然后将其表示为长度为10的编号序列,也就是一个个token。

但这样做,虽然可以将一小时的音频从60兆压缩到0.16兆,但质量相当糟糕,所以需要优先保留语音的语义信息而声学信号只保留少量部分,后续再通过其他手段还原

于是他们训练了一个统一的离散化音频分词器,以每秒25帧 [40ms/帧] 的速度运行,同时保持甚至提高音频质量,以捕获语义和声学特征。

新的离散化音频分词器运行速度仅为每秒25帧,同时在音质上保持甚至优于码率翻倍的分词器。该模型是首个在 24 kHz 数据上训练的统一系统覆盖语音、音乐与声音事件。同时,该模型采用简单的非扩散式编码器/解码器,实现快速批量推理

音频分词器性能:

然后要让模型很好地理解和生成声音,就需要利用模型的文本空间,将语音的语义尽量地映射回文本,当中需要大量的数据支持。

由于版权问题,沐导没有使用B站或YouTube这类公开视频网站数据,而是购买或从允许抓取的网站获取。这样得到的数据质量参差不齐,需要删除其中的90%才能满足1000万小时的训练数据需求。

其次,将语音对话表示为相应的system(场景描述、声学特征、人物特征等)、user(对话文本)、assistant(对应音频输出)的形式。由于OpenAI和谷歌一向禁止使用他们的模型输出再训练,且训练成本过高,为了实现这种标注,他们利用相同的模型架构额外训练出一个语音模型AudioVerse

该模型接收用户语音输入,分析并输出场景、人物、情绪、内容等信息,再将输出反过来作为生成模型的system提示和user输入,实现模型的共同进步。

举个例子就是,如果想要教一个徒弟同时会拳脚功夫,但师傅一次又教不了,那就同时教两个徒弟,一个学打拳,一个学踢腿,然后让他们俩天天互相打,打着打着两个就都会拳脚功夫了。

最终,这个多模态模型就完成了,不仅可以完成简单的文本转语音,还能实现更复杂的任务,比如让它写一首歌并唱出来,再加上配乐。

还能根据语音分析场景、人物(性别、年龄、情绪状态)、环境音(室内外),并进行复杂的理解和推理。

在实时语音聊天上,还可实现低延迟、理解情绪并表达情绪的自然语音交互,而不仅仅是机械的问答。

EmergentTTS-Eval基准上,相较于其他模型,性能可以说是遥遥领先,尤其是在“情绪”和“问题”类别中,相比GPT-4o-mini-tts高出了75.7%和55.7%的胜率。

此外,它在Seed-TTS Eval和情感语音数据集 (ESD) 等传统TTS基准测试中也取得了最佳性能。

Evaluation:

Seed-TTS Eval & ESD

我们使用参考文本、参考音频和目标文本对 Higgs Audio v2 进行零样本语音合成(TTS)测试。评估采用 Seed-TTS Eval 和 ESD 中的标准评估指标。【SIM 指标一般是指 Speaker Similarity

EmergentTTS-Eval (“Emotions” and “Questions”):根据 EmergentTTS-Eval 论文,我们报告了在使用 “alloy” 音色时,相较于 “gpt-4o-mini-tts” 的胜率。评判模型为 Gemini 2.5 Pro。

多说话人评估:我们还设计了一个多说话人评估基准,用于评估 Higgs Audio v2 在多说话人对话生成方面的能力。该基准集包含三个子集:

  • two-speaker-conversation:包含1000条双人合成对话。我们固定两段参考音频,用以评估模型在双人语音克隆方面的能力,对话轮数在4到10轮之间,角色随机选择。
  • small talk(无参考音频):包含250条合成对话,生成方式与上类似,但特点是发言简短、轮数较少(4–6轮)。本集合未提供参考音频,旨在评估模型自动为角色分配合适声音的能力。
  • small talk(有参考音频):同样包含250条合成对话,发言更短。该集合在上下文中包含参考音频片段,类似于 two-speaker-conversation,用于评估基于参考音频的表现。

我们在这三个子集上报告了词错误率(WER)和说话人内相似度与说话人间差异度的几何平均值。除 Higgs Audio v2 外,我们还评估了 MoonCast 和 nari-labs/Dia-1.6B-0626 这两个当前最受欢迎、支持多说话人对话生成的开源模型。结果总结在下表中。由于 nari-labs/Dia-1.6B-0626 对话语长度及输出音频的严格限制,我们未能在 “two-speaker-conversation” 子集上运行该模型。

Seed LiveInterpret 2.0 端到端同声传译大模型

!!!总结:必须认识到数据在模型训练的重要性。模型经过数十万小时语音数据的训练,数据质量中的任何瑕疵都可能在最终效果中被显著放大,这些潜在问题包括口音差异、准确读音、时间戳的准确预测,以及句子衔接的流畅度等关键要素。良好的性能正是建立在海量优质训练数据之上。

Seed LiveInterpret 2.0 是首个延迟&准确率接近人类水平的产品级中英语音同传系统,在中英同传翻译质量达到业界 SOTA 的同时,实现了极低的语音延迟水平。

它基于全双工端到端语音生成理解框架,支持中英互译,可实时处理多人语音输入,像人类同传译员一样以极低的延迟 “边听边说”,一边接收源语言语音输入,一边直接输出目标语言的翻译语音。同时,Seed LiveInterpret 2.0 还支持 0 样本声音复刻,让沟通更加流畅自然。

  • 接近真人同传的翻译准确率 精准的语音理解能力保障了翻译准确度,在多人会议等复杂场景中英双向翻译准确率超 70%,单人演讲翻译准确率超 80%,接近真人专业同传水平。
  • 极低延迟的 “边听边说” 能力 采用全双工语音理解生成框架,翻译延迟可低至 2-3 秒,较传统机器同传系统降低超 60%,实现了真正的 “边听边说” 翻译。
  • 零样本声音复刻,音色真实自然 只需采样实时语音信号,便能提取声音特征,用说话人的音色特质实时 “说出” 外语,提升交流的沉浸感和亲和力。
  • 智能平衡翻译质量、延迟和语音输出节奏 可根据语音清晰度、流畅度、复杂程度,调整输出节奏,并适配不同语言特性。面对超长信息,依然能保证传译语音节奏的自然流畅。
同声传译系统评估:左右两图比较了人工评估的翻译质量分数和响应效率  对于语音转文本 (S2T) 和语音转语音 (S2S) 模式,响应效率是相对于人工翻译延迟来衡量的。人工评估准确度反映了翻译输出对说话者原始意图的忠实程度

框架:

系统会克隆每位说话者的声音,并以相应的语调将其翻译为另一种语言
全双工流式端到端模型架构: Hibiki ,模型架构和数据相关可参考该论文

提出一种端到端的语音到语音同步翻译模型,在一个统一框架内无缝整合了同步语音翻译和语音克隆功能。

  • 语言模型预训练:使用 Seed LLM 系列的方法对初始语言模型进行预训练,建立基础的文本生成与理解能力。
  • 多模态扩展:集成一个预训练的音频编码器,使模型能够接受流式音频输入,扩展为具备音频处理能力的多模态 LLM。
  • 多任务持续学习训练:在大规模多任务数据上进行自回归训练,生成包括文本 token(可选)和音频 token 的输出,实现语音合成。
  • 高质量数据微调:使用人工标注的高质量数据进行微调,进一步优化模型在指令理解、多说话人识别、翻译策略关键能力上的表现。

问题:面临严格延迟约束下的同步翻译优化难题,需要在翻译质量时序控制之间权衡。

核心思路:优化两个互补目标

  • 片段内一致性确保每个翻译片段自身准确、流畅
  • 片段间连贯性确保不同翻译片段之间逻辑衔接自然

奖励机制设计

  • 多维单轮奖励(step-level):为每一步生成即时反馈,评估翻译准确性与时序控制,实现片段内部一致性优化
  • 统一多轮奖励(sequence-level):从全局角度评估整个翻译段落的连贯性,优化跨片段一致性

两阶段训练策略

  • 第一阶段:单轮奖励训练
    • 仅使用 step-level 奖励,学习人类翻译的先验知识,确保训练稳定
  • 第二阶段:联合优化训练
    • 引入 sequence-level 奖励,与 step-level 奖励联合优化,平衡过程指标(每步表现)与结果指标(整体输出质量)

主要贡献包括:统一的语音到语音架构、跨语言的语音克隆机制,以及接近人类水平的翻译性能。

Training

Continual Training and Supervised Fine-tuning

为实现文本与语音之间的有效模态对齐,并提升跨语言能力,我们采用了全面的多任务多模态持续训练(CT)策略。该策略有效促进了语音与文本模态之间的对齐,并强化了模型的跨模态与跨语言泛化能力

具体措施如下:

  1. 多模态多任务训练数据
    • CT 数据集涵盖约 1000 亿 tokens,任务类型包括:
      • 语音转文本(Audio-to-Text Transcription)
      • 文本转语音(Text-to-Audio Synthesis)
      • 纯文本处理(Text-Only Tasks)
  2. 数据质量控制
    • 为提升训练效率并确保数据质量,我们引入了基于语音质量指标的严格过滤流程,对语音数据进行筛选。

在持续训练之后,我们在高质量的人类标注数据上进行有监督微调,以激活同步语音传译所需的关键能力。该过程使模型能够建立以下数据驱动能力:

  1. 读-写策略(read-write policy)
  2. 多说话人区分能力
  3. 语音翻译能力
  4. 声音克隆能力

有监督微调显著提升了模型的指令跟随能力以及在核心传译任务上的整体表现。经过微调后的模型为后续的强化学习阶段提供了强大基础,使得后续优化更具针对性有效性

Reinforcement Learning

现代同声传译系统采用双工处理,将输入流分割成连续的音频块。形式上,我们将输入输出序列表示为:

每个音频片段(audioₜ)对应一个增量翻译 yₜ。我们将(audioₜ, yₜ)表示为序列中的第 t 个片段,并将 audio :=(audio₁, audio₂, …, audioₜ)表示为从 1 到 T 的聚合音频。在每个 t 片段中,我们有 yₜ :=(yₜ₁, yₜ₂, …, yₜₙ, …, yₜₙ),其中 N 是输出的长度。该模型利用当前音频片段(audioₜ)和之前的上下文 x<t,通过策略生成翻译 yₜ。

其中 πθ 是具有参数 θ 的策略 决定翻译策略。完整的轨迹概率定义为:

我们将 rtn 表示为 t 个块中第 n 个 token 的奖励。强化学习的目标是最大化每条轨迹上的累积奖励,即:

其中 𝒟 是训练数据集。以下部分详细说明了 rtn  的设计方式。

 奖励设计:平衡单轮反馈和多轮反馈

  • 单轮奖励(Single-turn rewards):在每个决策点提供即时反馈,用于评估中间的推理或生成步骤。
  • 多轮奖励(Multi-turn rewards):评估整个输出序列的质量,反映多个决策步骤的长期、累积效果。

同步翻译系统尤其具有独特挑战,因此需要精细化的奖励设计。该任务需同时优化两个互补目标:

片段内一致性(Intra-segment consistency):要求模型在逐步输出时保持语义与时间上的准确性和完整性,适合采用单轮奖励(single-turn reward)进行即时评估。

片段间连贯性(Inter-segment coherence):确保整个翻译序列在语义和时间上的连续性与一致性,适合采用多轮奖励(multi-turn reward),从全局角度评估累积的序列质量。

基于上述考量,我们提出了一种新颖的框架,将多维单轮奖励统一多轮奖励相结合。

Single-turn Reward:方法利用了每个增量步骤的细粒度反馈,我们通过实证研究发现,这些反馈与人类的评估指标高度相关。

给定一个音频序列 {audiot}1T 和相应的真实值 {yt}1T ,沿着五个派生维度定义段内奖励:

检测准确性奖励(rl​):该奖励旨在鼓励模型在翻译前进行充分“倾听”,避免过早输出,从而提升语义单元完整性。

I(⋅) 为指示函数,条件成立时取值为 1,否则为 0;∣yt​∣ 表示模型在第 t 步生成的 token 数量;∣yt∗​∣ 表示参考翻译在第 t 步应生成的 token 数量。当模型和参考翻译在当前步都没有输出(token 数为 0)时,奖励为 1,否则为 0该设计鼓励模型在语义信息尚不完整时保持“静默”,从而提升翻译的延迟-准确性权衡表现。

翻译主动奖励 ( rs ):通过奖励尽快生成已确认的语义单元来鼓励语音翻译:鼓励模型在语义单元一旦可用时立即翻译

翻译质量奖励(rq:衡量当前步生成内容与参考翻译的相似度(可通过 BLEU、BERTScore 等):

时序匹配奖励(rc​):鼓励模型生成的语音时长与参考时长一致,惩罚过长或过短:

格式一致性奖励(rf):保证输出结构正确,如标点、格式符号等符合预设正则表达式:

最终单轮奖励定义如下:

多轮奖励:单轮奖励机制提供了详细的、逐步的反馈,能够在每一步的递增中平衡延迟和翻译质量,但它未能完全捕捉同声传译中固有的长期依赖关系和累积效应。尤其是,当生成的目标音频逐渐落后于源音频时,会导致破坏性延迟,从而降低用户体验。为了解决这些全局序列级的动态问题,我们设计了一个互补的多轮奖励机制,可以整体评估整个输出序列。

延迟惩罚(rL​):惩罚翻译滞后,鼓励更及时的输出:

  • l:可接受的最大等待阈值
  • K:翻译片段数
  • dk​:第 k 个翻译片段前等待的片段数量

序列级翻译质量奖励(rQ​):衡量整个翻译序列与参考的匹配度(例如通过全局对齐算法):

多轮奖励定义为:

最终奖励融合与正则项

  • 每个子奖励在 batch 中进行标准化(均值为 0,方差为 1),提高数值可比性。
  • 总奖励为标准化后的单轮与多轮奖励之和,融合了局部细粒度指导全局一致性目标
  • 引入 KL 散度正则项:

用于鼓励当前策略 πθ​ 向参考策略靠拢,提升训练稳定性与可控性。

稳定强化学习训练:通过近端策略优化 (PPO)来优化定义的目标,该算法通过修剪的目标函数实现稳定高效的策略更新。训练目标公式如下:

 audio={audiot}1T 表示输入的音频序列, y={yt}1T 表示从旧策略 πθo⁢l⁢d 采样的翻译响应。优势估计 Atn 使用广义优势估计 (GAE)计算。由于这些奖励之间紧密耦合且差异化,调整它们各自的权重颇具挑战性,而且通常效果不佳。为了解决这些问题并稳定训练,我们采用了两种主要策略:自适应 KL 惩罚 和两阶段强化学习训练方案。

 Adaptive KL

对于包含音频和文本 token 的序列,由于其长度较长,控制 KL 散度会更加困难,这自然会导致更高的累积 KL 散度。因此,KL 惩罚系数 β 必须设置为高于传统 RLHF 的设置。

采用对数空间中的比例控制器来自适应地调整 β ,以确保 KL 散度始终接近预定目标。

两阶段强化学习训练方案:在第一阶段,通过仅优化多维单轮奖励来预热模型,使其内化人类先验知识并实现稳定的学习动态。在第二阶段,使用结合过程和结果成分的多轮奖励对模型进行进一步训练,使其能够有效地优化和平衡延迟与翻译质量。、

Experiments

 评估指标:

对于文本翻译质量评估,我们主要依赖于人工评估指标——有效信息比例 (VIP),该指标衡量翻译输出对每个语义片段传达说话者原始意图的准确程度,与人工翻译的判断高度一致。

在语音到语音评估中,我们提出了“语音有效信息比例”(SVIP)作为一种全面的人类评估指标。该指标建立在已有的“有效信息比例”(VIP)框架[6]之上,用于衡量完整语音会话中有效语义片段所占的比例。

当一个语音语义片段能够有效传达源语音的核心信息、准确表达说话者的原始意图、在可接受的延迟范围内完成传递、保持适合听众理解的语速,并达到清晰和易懂的声音质量标准时,即被视为有效。

在延迟评估方面,我们采用“首字母出现延迟”(FLAL)指标来衡量系统在段落级别输出第一个确定翻译所需的时间。在句子级别,我们使用广泛应用的“平均延迟”(AL)和“长度自适应平均延迟”(LAAL)指标,以比较不同方法之间的延迟表现。

在延迟表现上,Seed LiveInterpret 2.0 在语音到文本场景中,输出首字平均延迟仅 2.21 秒,在语音到语音场景中,输出延时仅 2.53 秒,做到了对翻译质量以及时延的均衡。

针对 Seed LiveInterpret 2.0 中译英和英译中两个方向的表现进行了客观评估,与其他翻译系统在翻译质量(BLEURT/ COMET)和延迟(AL/ LAAL/FLAL)等指标上进行对比。

结果显示,Seed LiveInterpret 2.0 在两个数据集上均表现出最高的翻译质量。在延迟方面,Seed LiveInterpret 2.0 在英到中方向上实现了语音到语音翻译的最低平均滞后(AL),在中到英方向上也表现出竞争力,展现了速度与准确度的良好平衡。

总体来看,Seed LiveInterpret 2.0 在句子级基准测试中,有效平衡了翻译质量与延迟。这不仅缓解了传统同传中 “译得准则慢,译得快则偏” 的痛点,配合音色复刻能力,让中英跨语言交流首次具备自然对话般的流畅感。

总结与展望

在本研究中,团队进一步认识到数据对模型训练的重要性。模型经过数十万小时语音数据的训练,数据质量中的任何瑕疵都可能在最终效果中被显著放大,这些潜在问题包括口音差异、准确读音、时间戳的准确预测,以及句子衔接的流畅度等关键要素。Seed LiveInterpret 2.0 良好的性能正是建立在海量优质训练数据之上。

Seed LiveInterpret 2.0 已初步展现出一定优势,其边界仍有拓展空间。比如,在语言覆盖方面,目前模型主要支持中英互译,其他语种尚未较好支持。此外,其声音复刻的稳定性、语音表现力、情绪复刻能力、极复杂情况下的翻译准确性等仍有进步空间。

在未来研究中,我们希望进一步挖掘模型潜力,通过优化算法、增强数据及改进训练策略等方式,逐步拓展同传模型的能力边界,提升其在复杂场景下的适应性和性能表现。

高效 LLM 训练方法:Packed samples和 sorted batching

 要让大型语言模型更有效地处理长文本上下文,需要在相似长度的输入序列上进行指令微调。LongAlign 方法,它可以帮助大型语言模型有效处理长达 64k 的长上下文,并展现出强大的长文本理解和生成能力。

LongAlign :

动机:

  • 目前缺乏用于有监督微调(SFT)的长文本指令跟随数据集,更缺乏构建此类数据的方法。
  • 长上下文数据的长度分布差异较大,在多GPU环境中严重降低了传统批处理方法的训练效率——处理较短输入的GPU必须等待处理较长输入的GPU完成任务后才能继续运行。
  • 亟需一个强健的基准评估体系,用于衡量大型语言模型在面对真实世界长文本查询时的处理能力。

贡献:

LongAlign 方法,分别从数据构建、高效训练和评估三个方面入手:

在数据方面,为构建一个多样化的长文本指令跟随数据集,从九个来源收集了长文本序列,并使用 Self-Instruct生成了 1 万条长度在 8k 到 64k 之间的指令数据。

在训练方面,为应对不均匀批处理导致的效率问题,采用了 packing 策略,即在将数据分发到 GPU 之前,将多个序列打包为接近最大长度的组合。但我们发现这种打包训练中的损失计算存在偏差:不同数量序列的打包在最终损失计算中被赋予相同权重。为缓解这一问题,我们提出了“损失加权策略”,对每条文本的损失进行加权平均,以平衡不同序列对整体损失的贡献。此外,我们还引入了“排序批处理”方法,将长度相近的序列分组,从而减少批内空闲时间

在评估方面,开发了 LongBench-Chat 基准测试,它包含长度为 10k-100k 的开放式问题,这些问题由博士生进行标注。评估内容涵盖推理、编程、摘要以及多语种翻译等多种长文本指令跟随能力。使用 GPT-4(OpenAI,2023b)结合人工标注结果和少量示例,对模型生成的回答进行评分。

结论:

数据量与多样性的影响:长文本指令数据的数量和多样性都会显著影响模型处理长上下文的能力,最终性能差异最高可达 30%。

长文本指令数据的益处:增加长文本指令数据有助于提升模型在长上下文任务中的表现,同时不会削弱其处理短上下文任务的能力。

训练策略的有效性采用的打包和排序批处理策略可将训练速度提升超过 100%,且不影响模型性能。此外,提出的损失加权技术还能将长文本任务的性能提升 10%。

数据集构建:

构建了一个包含10,000条长度在8k-64k之间的长文指令跟随数据集,这些数据来自于9个不同的数据源,包括学术论文、书籍、百科全书等,覆盖了多样化的任务类型。

高效训练方法:

为了确保模型在有监督微调(SFT)后依然具备处理长文本和短文本(即通用能力)的能力,将长文本指令数据与通用指令数据集混合用于训练。这种训练策略使得大量通用短文本数据与相对较少的长指令数据结合,从而形成了一个“长尾”式的数据长度分布。探索了两种训练方法:packingsorted batching

Packing(打包)

该方法通过将不同长度的数据拼接,直至达到最大长度,生成的打包数据整体长度接近最大限值。这些打包后的数据再进行批处理并在多 GPU 上处理,有效减少了每个批次中的空转时间。

此外,为防止同一 pack 中的不同序列在自注意力计算中发生“交叉污染”,我们传入了每个序列的起始与结束位置列表,并使用了 FlashAttention 2 中的 flash_attn_varlen_func 该方法支持高效的块对角注意力计算,计算量与 IO 时间均优于传统的二维注意力掩码。

Packing 策略存在的偏差

不过我们注意到,packing 会带来对长序列目标 token 较多的序列偏向。这是因为:不同的打包组(pack)在最终损失计算中被赋予相同权重,而每个打包组中包含的序列数量和每个序列的目标 token 数量却不同。

因此,在对每个批次求平均损失时,包含序列较少(通常是较长序列)或目标 token 较多的 pack,会对最终损失产生更大影响。

形式上,设将 M 个序列打包成 K 个 pack,第 i 个 pack 包含索引区间为 [Pi−1,Pi)的序列,其中 P0=1,PK=M+1。设 Li 为第 i个序列在其 Ni​ 个目标 token 上的总损失。如果我们希望对每个序列赋予相等的权重[ SFT中算loss ],则损失应当为:

而在 packing 情况下计算得到的损失为:

(3)与公式 (2) 相比,在 packing 情况下,相当于为第 j个序列分配了一个权重:

也就是说,损失更偏向于目标 token 数较多的序列,以及位于pack 较小的组中的序列。

为了解决这种不公平,我们提出对第 i 个序列的损失进行缩放,缩放因子为:K/(NiM),然后对每个 pack 中缩放后的损失求和,这样得到的总损失将与公式 (2)(即平均每个序列损失)保持一致,从而消除了不同序列在损失计算中所受到的偏倚。

损失加权策略在下游任务中带来了约 10% 的性能提升

Sorted Batching(排序批处理)

还提出了一种高效的 排序批处理策略。为确保每个 batch 中的序列长度相近,我们先按照序列长度对数据进行排序,然后在每轮训练中从中随机选取一段连续的数据组成一个 batch,且不重复使用。

不过,该方法不可避免地会引入 批次间数据分布的不均衡某些 batch 可能全部由长序列组成,另一些则全是短序列。这种偏差可能对 SGD(随机梯度下降)优化过程造成严重影响。

尽管如此,我们在实验中发现,排序批处理显著加快了训练速度,且几乎不会对模型性能产生负面影响。这可能得益于我们使用了较大的梯度累积步数(gradient accumulation steps)和优化器本身较强的适应能力。

训练方法细节

这里介绍 packing 策略与损失加权的具体实现方式。

Packing 策略实现

在打包训练过程中,每个数据批次会传入一个特殊的一维注意力掩码。在该掩码中,第 i个元素表示第 i 个序列在该批次中的起始位置。掩码的第一个元素为 0,最后一个元素等于 batch_size × seq_len

在注意力计算时,我们使用 FlashAttention 2 提供的 flash_attn_varlen_func 函数,并将该掩码传入其参数 cu_seqlens_qcu_seqlens_k。该函数会根据掩码中相邻元素表示的起始和结束位置,在每个序列内部进行注意力计算因此,每个序列的 Query 只能与自身的 Key 进行注意力操作,实现了“序列内独立注意”。

损失加权策略实现

在实现损失加权策略时,首先对训练数据进行预处理:为每个 pack 中的序列生成一个加权的一维掩码。该掩码中,对应目标 token 的位置权重为 1/N(其中 N 是当前序列的目标 token 数),其他位置为 0。

训练时,根据当前配置动态设置 M 和 K,表示即当前批次中序列的数量和 pack 的数量。然后,损失计算方法为:对每个 token 的交叉熵损失乘以比例系数 K/(MN),再求和得到最终损失值。

Packing 加权loss代码实现:

SFT中算loss通常来讲都是样本内作token-level mean,样本间作sequence-level mean,也就是等式(2)的计算方式。如果不同样本间作token-level mean,则会使target token数量多的样本更受重视(相当于被upsample),从而引入不同样本间的不平衡。

### Support loss weighting for packing ###
        loss = None
        if labels is not None:
            lm_logits = lm_logits.to(torch.float32)
            # Shift so that tokens < n predict n
            shift_logits = lm_logits[..., :-1, :].contiguous()
            if isinstance(labels, tuple) or isinstance(labels, list):
                labels, weights = labels
            shift_labels = labels[..., 1:].contiguous()
            if self.pack_loss:
                shift_weights = weights[..., 1:].contiguous()
                loss_fct = CrossEntropyLoss(ignore_index=-100, reduction='none')
                loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
                loss = (loss * shift_weights).sum()
            else:
                loss_fct = CrossEntropyLoss(ignore_index=-100)
                loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))

            lm_logits = lm_logits.to(hidden_states.dtype)
            loss = loss.to(hidden_states.dtype)
        ### -------------------------------------- ###

Mistral Voxtral-语音理解大模型

论文:Voxtral https://arxiv.org/abs/2507.13264

法国 AI 巨头发布了他们首个开源语音模型系列——Voxtral,推出了 Voxtral Mini [4.7B]和 Voxtral Small[24.3B] 两款多模态音频聊天模型。

Voxtral 的两个模型不仅仅是转录工具,还具备以下功能:

  • 长文本上下文处理:支持最长 32,000 个 token 的上下文长度,可处理最长 30 分钟的转录音频,或最长 40 分钟的语义理解任务。
  • 内置问答与总结功能:无需串联 ASR(自动语音识别)和语言模型,即可直接就音频内容提问或生成结构化摘要。
  • 原生多语言支持:自动语言识别,在全球主流语言(如英语、西班牙语、法语、葡萄牙语、印地语、德语、荷兰语、意大利语等)中表现优异,帮助团队使用单一系统服务全球用户。
  • 语音直接调用函数功能:可根据用户语音中的意图,直接触发后端函数、工作流或 API 调用,无需中间解析步骤,使语音交互可直接转化为系统指令。
  • 强大的文本处理能力:延续了其语言模型基础——Mistral Small 3.1 的文本理解能力。

这些功能使 Voxtral 模型非常适合真实世界中的交互场景和下游任务,如摘要生成、问题回答、内容分析和洞察提取。在对成本敏感的应用场景中,Voxtral Mini Transcribe 的性能优于 OpenAI Whisper,价格却不到一半;在高端应用中,Voxtral Small 的表现媲美 ElevenLabs Scribe,同样价格不到一半。

Modeling

Voxtral 基于 Transformer 架构,由三个组件组成:用于处理语音输入的音频编码器、用于对音频嵌入进行下采样的适配器层,以及用于推理和生成文本输出的语言解码器。其整体架构如图 1 所示:

input:log-Mel spectrogram[128Mel-bins and 160 hop-length]
Audio Encoder:Whisper large-v3 50Hz
Adapter:MLP downsampling factors of 4x
LLM: Ministral 3B && Mistral Small 3.1 24B

Audio Encoder

音频编码器基于 Whisper large-v3。原始音频波形首先被转换为 log-Mel 频谱图,其参数为 128 个 Mel 频段(Mel-bins)和 160 的步长(hop-length)。在 Whisper 编码器内部,频谱图首先通过一个卷积层(convolutional stem),将时间分辨率下采样一半,然后输入一系列双向自注意力层(bidirectional self-attention layers)。最终得到的音频嵌入帧率为 50 Hz。

Mel-bins 指的是将频率轴按照 Mel 标度划分的“通道”数。Mel 标度是模拟人耳感知音高的非线性频率标度,低频分得更密,高频分得更疏。128 Mel-bins 就是将频率范围(通常是从 0 到某个最大频率,比如 8000Hz)映射成 128 个“频带”或“维度”,每一维代表一个 Mel 频段的能量。 hop-length 表示相邻两个短时傅里叶变换(STFT)窗口之间的间隔(帧移)。单位是 样本数(samples),不是时间。每 10 毫秒提取一帧频谱(帧移),这使得频谱图具有较高的时间分辨率。

Whisper 的感受野固定为 30 秒。为了处理超过该时长的音频序列,系统会先对整段音频计算 log-Mel 频谱图,但编码器会将其划分为每段 30 秒,分别独立处理。每个分段的绝对位置编码都会重置,并在同一个 batch 维度中组织这些分段。
在编码器的注意力层中,这种做法在功能上等同于 chunk-wise attention ,可有效降低长音频输入的计算开销,同时增强对不同长度输入的泛化能力。每个分段计算得到的嵌入(embedding)会在输出阶段拼接在一起,最终形成对整段音频的统一表示。

由于感受野固定,Whisper 也会将较短的音频填充到 30 秒。然而,实证结果显示,即使对编码器进行了适应性调整,性能仍有所下降。因此,我们仍保留将所有音频输入填充到下一个 30 秒整数倍的做法。

Whisper 将短音频填充至 30 秒。我们研究了在预训练过程中,在编码器权重已训练以适应新配置的前提下,这种填充约束是否必要。禁用填充几乎不会对 FLEURS 英语造成任何惩罚,但法语的 WER 会降低 0.5%。在两次运行的训练过程中,Llama QA 的 3 次准确率相当。为了在不影响语音理解的情况下获得最佳的语音识别分数,我们选择在音频编码器中保留填充。

Adapter Downsampling

基准音频编码器的运行帧率为 50 Hz。为了减少解码器的计算量和内存占用,我们插入了一个 MLP 适配器层,用于沿时间轴对音频嵌入进行下采样。我们分别以 50、25、12.5 和 6.25 Hz 的目标帧率进行实验,对应的下采样倍数分别为 1 倍、2 倍、4 倍和 8 倍。

对于 25 Hz 和 12.5 Hz,ASR 基准测试中几乎没有下降。然而,对于 6.25 Hz,FLUERS 法语的损失超过 1%。在 Llama QA 上,12.5 Hz 超过了 50 Hz 的基准,得分高出 1.5%。我们假设,在 12.5 Hz 下,每个音频嵌入编码的信息量与语言解码器主干中的文本嵌入相似,从而带来卓越的理解性能。基于序列长度、ASR 和语音理解性能之间的权衡,我们选择 12.5 Hz 作为 Voxtral 的最佳帧率。

Language Decoder

 Voxtral 的两个版本:Mini 和 Small。Voxtral Mini 构建于 Ministral 3B 之上,这是一个专注于边缘计算的模型,在较小的内存占用下提供极具竞争力的性能。Voxtral Small 则利用了 Mistral Small 3.1 24B 主干模型  ,在一系列知识和推理任务中表现出色。

Methodology

Pretraining:introduce speech to the language decoder

Voxtral 的预训练阶段旨在将语音引入语言解码器,与现有的文本模态互补。我们定义了两种将音频和文本结合到模型训练样本中的模式: 音频到文本重复跨模态延续 

音频转文本的重复模式定义为一个音频片段 An ,后跟相应的转录 Tn 。训练样本由单个音频-文本对 (An,Tn) 组成。此公式模拟语音识别,用于明确地训练模型进行语音转文本对齐。

另一方面,跨模态连续模式旨在通过模态不变的上下文建模,隐式地对齐语音和文本模态。具体来说,对于每个音频片段 An ,对应的文本是序列 Tn+1 中的前一个文本片段。此外,训练样本由多个连续片段的音频和文本交织而成: (A1,T2,A3,T4,…,AN−1,TN) 。这种结构类似于问答或对话等任务,其中模型必须保持跨模态的话语连续性。

由于我们使用两种不同的数据模式,因此给定音频片段的后续文本片段具有歧义性;重复和延续都是有效的。为了消除歧义,我们引入了两个特殊标记来指定预期输出: <repeat> 表示重复, <next> 表示延续。这些标记用于在训练期间指示模式,并在推理期间作为提示的一部分来控制模型行为。

Balancing the two tasks with equal ratios achieves ASR and Llama QA performance comparable。

我们在预训练过程中利用了两种数据模式:音频到文本的重复和跨模态延续。图 9 演示了改变这两种模式的比例如何影响 ASR 和语音理解。为了更好地理解跨模态延续模式对 ASR 的潜在能力,我们在 FLEURS ASR 任务的 3-Shot 版本上对其进行了评估,该版本与训练期间呈现的多轮模式更加一致。

仅包含音频转文本重复模式可获得出色的 ASR 性能,但 Llama QA 的性能几乎为零。相反,仅使用跨模态延续模式进行训练可获得出色的 Llama QA 性能,但 ASR 的字错误率 (WER) 接近 60%。以相同的比率平衡这两个任务,可实现与使用单一模式的运行相当的 ASR 和 Llama QA 性能。因此,我们在预训练期间以相同的概率对每个模式进行采样。

为了保留文本能力,我们还在数据混合中包含了文本预训练数据。

Supervised Finetuning

在训练后阶段,我们的主要目标是保留或略微提升预训练期间建立的转录能力,同时扩展模型在一系列语音理解任务中的熟练程度。我们还开发了强大的指令遵循行为,无论用户输入是音频还是文本形式。

我们的语音理解数据分为两类:一类是以音频作为语境,助手响应文本查询的任务;另一类是助手直接响应音频输入的任务。这两类任务都高度依赖于合成数据。

Audio Context, Text Query:为了创建涉及音频上下文与文本查询配对任务的合成数据,我们利用长格式音频数据(最长约 40 分钟的片段)及其对应的转录本和语言识别元数据。转录本与定制的提示配对,并输入到 LLM (Mistral Large) 中,然后 LLM 生成与音频内容相关的问答对。提示明确指示 LLM 将问题和答案构建为听觉理解而非文本分析的结果,从而鼓励下游音频模型做出自然的响应。为了实现数据的多样性和丰富性,我们采用了多种问题类型,包括简单的事实查询、“大海捞针”式的检索任务以及推理密集型问题。此外,为了最大限度地减少重复的问题风格,LLM 会为每个音频片段生成多个候选问答对,并从中抽取一对纳入训练后数据集。虽然我们通常确保问答对与原始音频和文字记录的语言相匹配,但我们偶尔会指示 Mistral Large 生成不同语言的问答对,以便对用户不会说的语言的音频进行 QA。

此外,我们还分配了另一部分长音频数据用于合成摘要和翻译数据。对于翻译任务,我们利用语言识别元数据来选择不同于原始音频语言的目标语言。为了避免过度拟合于狭窄范围的用户消息模式,我们从大量手动整理的合理用户请求中进行了采样。

Audio-Only Input:对于用户仅提供音频输入的场景,我们调整了现有的文本监督微调数据(包括函数调用数据集),通过使用文本转语音 (TTS) 模型将文本用户消息转换为合成音频。然而,仅仅依赖 TTS 生成的音频会导致对真实人类语音(尤其是带口音的声音)的泛化能力较差,最常见的表现是对话提示的转录错误而不是适当的延续。为了解决这一限制,我们从长篇 ASR 数据中提取了可以通过一般世界知识充分回答的问题,因此不需要额外的音频上下文。然后,我们分离包含这些问题的音频摘录,并使用 Mistral Large 生成相应的文本答案。此过程生成由真实人类语音问题与文本答案配对组成的数据集。

语音识别是一个独特的用例,其特点是任务明确,因此文本提示显得多余。为了解决这个问题,我们引入了专用的“转录模式”,并通过一个新的特殊标记发出信号。此模式明确指示模型执行转录任务,从而无需文本提示。

Preference Alignment

选择了 直接偏好优化 (DPO) 和 在线 (DPO) 。对于在线DPO,从当前策略中采样两个温度为 T=0.5 的候选响应。为了对响应进行排名,我们获取整个对话,用其转录替换音频,并利用基于文本的奖励模型。虽然奖励模型只能访问音频转录 – 而不是原始音频本身 – 但它能够从这些信息中捕捉语义、风格和事实一致性,这些属性会迁移到生成的文本响应。

虽然 DPO 和在线 DPO 都有助于提高响应质量,但在线版本更有效。

Voxtral Mini 在线 DPO 变体能够提供更清晰的接地气、更少的幻觉,并且通常能提供更有帮助的响应。
对于 Voxtral Small,我们发现其语音理解基准测试的响应质量得分显著提升,但在英语短句基准测试中却略有下降。

Results

语音识别:各任务的平均 WER 结果。Voxtral Small 在英语短格式和 MCV 上的表现优于所有开源和闭源模型。Voxtral Mini Transcribe 在每项任务中均胜过 GPT-4o mini Transcribe 和 Gemini 2.5 Flash。

Tables 4, 5 and 6 show the per-language breakdown of WER scores for the FLEURS, Mozilla Common Voice and Multilingual LibriSpeech benchmarks, respectively.

Speech Translation:在 FLEURS 语音翻译基准上对 Voxtral 进行了评估。图 4 展示了部分源/目标对的 BLEU 得分。Voxtral Small 在所有源/目标组合中都取得了最佳的翻译得分。

Speech Understanding:在三个语音理解基准和三个合成语音文本子集基准上的准确率。Voxtral Small 与闭源模型相比具有竞争力,在七个基准中的三个上超越了 GPT-4o mini Audio。

Speech Understanding Benchmark:

Text Benchmarks:

将 Voxtral Mini 和 Small 与纯文本 Mistral Small 3.1 模型的性能进行了比较。Voxtral Small 在文本基准测试中保持了良好的性能,使其成为文本和音频任务的理想替代品。

图解OpenRLHF中基于Ray的分布式训练流程

摘自:图解OpenRLHF中基于Ray的分布式训练流程

本文着重分析OpenRLHF中的PPO-Ray训练架构设计,不了解Ray的朋友也可以通过本文快速上手,全文共分成五块:

  1. 为什么用Ray
  2. 使用图例抽象出整体训练流程
  3. Ray核心知识速过
  4. 使用图例进一步抽象出核心代码细节,包括:
  • 训练入口
  • 部署PPO-Actor/Ref/Critic/RM实例
  • 部署vllm_engines实例
  • PPO-Actor与vllm_engines之间的通讯
  • PPO-Actor/Critic训练

5. RLHF-PPO算法细节介绍

Ray介绍

https://github.com/ray-project/ray

Ray 是一个用于扩展 AI 和 Python 应用程序的统一框架。Ray 由一个核心分布式运行时和一组用于简化 ML 计算的 AI 库组成:

一站式平台
包含多个模块(Ray Core, Ray Data, Train, Tune, RLlib, Serve)支持从数据加载、并行任务调度,到分布式训练、超参调优、强化学习、模型部署的完整 AI 工作流。

Python 原生,门槛低
采用装饰器 @ray.remote 标记函数或 Actor,即可并行执行,无需了解底层分布式原理。

高性能 & 容错能力
利用分布式调度、共享内存对象存储、层级调度机制,实现低延迟、高吞吐、故障恢复。

一、为什么要使用Ray

对于通常的rlhf框架,在训练时会在单卡上同时部署actor/ref/reward/critic四类模型,这种单一的部署方式可能存在如下问题:

  • 难以突破单卡显存的限制。
  • 无法实现更多的并行计算。例如在收集exp阶段,拿到(prompt, responses)结果的四类模型其实可以做并行推理;在训练阶段,拿到exp的actor和critic也可以做并行训练。但受到单卡显存等因素影响,通常的rlhf框架中使用更多的是串行。
  • 无法独立优化训练和推理过程。诸如vllm之类的框架,是可以用来提升actor生成(prompt, responses)的速度的,而对于其它模型,我们也可能会视算法需要有不同的推理需求。因此我们期望能更加灵活地设计训练、推理过程


而解决以上问题,需要开发者能设计一套较为灵活的分布式计算框架,能够实现资源定制化分配、分布式调度、节点内外通信等目标,同时相关的代码不能太复杂,能够让使用者更专注于算法部分的研发。而Ray天然可以帮我们做这件事:我们只需提供自己的资源分配方案,告诉Ray我想怎么部署这些模型,不管是分开还是合并部署Ray都可以帮我们实现。而复杂的调度策略和通信等事项,就由Ray在后台去做,我们无需关心这个过程。

二、整体流程

本节我们将提供2个例子,帮助大家更好理解使用Ray可以做什么样的“定制化”部署。注意,这些例子只做讲解用,不代表它们一定是训练的最优配置。

2.1 非共同部署


这个例子展示如何完全独立部署各个模型。假设我们有3台node,每台node 8张卡。以下展示其中一种可行的部署方式:

(1)部署4类模型

在这个例子中,4类模型分开部署在node0和node1上。以Actor为例,它分布在“node0的gpu0/1 + node1的gpu0/1”上。这一点是由Ray实现的:我们自己定制化资源分配的方案,进而管控模型的分配方式

而当实际训练时,我们还可进一步引入Deepspeed zero做优化:以Actor为例,上图中的4个Actor构成zero中的数据并行组(world_size = 4),根据zero的配置,我们可以在这4张卡间做optimizer/gradients/weights的切片

(2)部署vllm_engines

前文说过,对于Actor模型,在收集exp阶段我们可以采用vllm之类的框架加速(prompt, responses)的生成。在这个例子中:

  • 1个vllm_engine维护着一个vllm实例,每个vllm实例下维护一个完整的Actor模型,这里我们还假设一个vllm实例按tp_size = 2的方法切割模型。
  • 在node2中,共有4个vllm_engines(也即4个vllm实例),这种分配方式是通过Ray实现的。而每个vllm实例内的分布式推理则是由vllm自己管控。


(3)Actor与vllm_engines之间的通讯

我们称:

  • vllm_engines中的actor为vllm_actor
  • node0/1中的actor为ds_actor

在整个训练过程中,vllm_actor需要和ds_actor保持权重一致。我们来看这个一致性是如何维护的:

  1. 初始化阶段

假设pretrain路径下存储着sft模型,当我们首次开始训练时,ds_actor和vllm_actor都直接从pretrain中加载权重,两者互不影响,独立加载。


2. 训练中

在1个step结束后,ds_actor需要把更新后的权重broadcast给vllm_actor,具体步骤如下:

  • 首先,对ds_rank0 + all_vllm_ranks创建一个通讯组。在本例中:
    • node0/gpu0上的actor是ds_rank0
    • node2中所有的gpu构成all_vllm_ranks。
    • 我们就是把这两者纳入一个通讯组内,这个通讯组的world_size = 9。如果我们多一台node3来做vllm_engines,那么这个通讯组的world_size = 17,以此类推。
  • 假设我们使用ds_zero1/2,则ds_rank0上维护的是完整的actor权重,我们把ds_rank0上的权重broadcast到每一个vllm_rank,如有设置tp,vllm会自动帮我们完整接下来的模型切割。
  • 假设我们使用ds_zero3,则ds_rank0上只维护部分actor权重,那么:
    • ds_rank0先从ds_actor组内all gather回完整的模型权重
    • 再将完整的模型权重brocast给每一个vllm_rank

3. 从检查点恢复训练(load_checkpoint)

当我们需要从检查点恢复训练时,ds_actor会负责把检查点权重broadcast给vllm_actor,方式同2。

(4)整体运作流程

结合2.1开头的图例,我们来简述一下整体运作流程。

  • 首先明确一些表达。例如,node0中的Actor0/1 + node1中的Actor0/1属于相同的数据并行组,所以接下来我们会用它们在dp组中的rank来描述它们,也就是分别改称Actor0/1/2/3。对于其余三类模型也是同理。
  • 接着进行分组:
    • Actor0 / Ref0 / RM0 / Critic0 / vllm_engine0为一组
    • Actor1 / Ref1 / RM1 / Critic1 / vllm_engine1为一组
    • Actor2 / Ref2 / RM2 / Critic2 / vllm_engine2为一组
    • Actor3 / Ref3 / RM3 / Critic3 / vllm_engine3为一组
    • 你可以把每一组想象成原来的一张单卡,那么它的作用就是负责一个micro_batch的训练,这样我们就能大致想象到它们之间是如何配合运作的了。需要注意的是,在我们的例子中,这些实例都是一一对应的(各自有4个实例),但在实际操作中,根据不同用户的资源配置,不一定存在这个一一对应的关系。例如你可能用4卡部署Actor,2卡部署Critic,8个vllm_engines…以此类推。不管怎样,我们应该尽量在处理micro_bathes的各个组间均匀分配负载,在代码里相关的操作如下:

为每个actor分配其对应的critic/reward/ref,并启动每个分组的训练:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/launcher.py#L278-L299
为每个actor分配对应的vllm_engine,并使用vllm_engine进行推理:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_utils/experience_maker.py#L627

2.2 共同部署


同样,我们可以按照自己的需求,选择性地在单卡上部署不同种类的模型,例如下面的例子中,actor/ref共部署,critic/reward共部署,图例如下,运作流程和2.1相似,这里不赘述:

三、Ray的核心概念

在传统的编程中,我们经常使用到2个核心概念:function和class。而在分布式系统中,我们希望可以分布式并行执行这些function和class。Ray使用装饰器@ray.remote来将function包装成Ray task,将class包装成Ray actor,包装过后的结果可以在远端并行执行。接下来我们就来细看task/actor(注意,这里的actor是ray中的概念,不是rlhf-ppo中actor模型的概念)

请大家一定仔细看3.1和3.2节代码中的注释。

3.1 Ray Task

import ray
ray.init()

@ray.remote
def f(x):
    return x * x
# ===================================================================
# 创建driver进程,运行main
# ===================================================================
if __name__ == "__main__":
    # ===================================================================
    # 创建4个worker进程,可以在远端并行执行。
    # 每执行1次f.remote(i),会发生以下事情:
    # - 创建1个worker进程,它将在远端执行函数f(i)
    # - 在driver进程上立刻返回一个引用(feature),该引用指向f(i)远程计算的结果
    # ===================================================================
    futures = [f.remote(i) for i in range(4)]
    # ===================================================================
    # 阻塞/同步操作:等待4个worker进程全部计算完毕
    # ===================================================================
    results = ray.get(futures)) 
    # ===================================================================
    # 确保全部计算完毕后,在driver进程上print结果
    # ===================================================================
    print(f"The final result is: {results}") # [0, 1, 4, 9]



3.2 Ray Actor

import ray
ray.init()

@ray.remote
class Counter(object):
    def __init__(self):
        self.x = 0
    
    def inc(self):
        self.x += 1
    
    def get_value(self):
        return self.x

# ===================================================================
# 创建driver进程,运行main
# ===================================================================
if __name__ == "__main__":
    # ===================================================================
    # 创建1个worker进程,具体做了以下事情:
    # - 在远端创建Counter实例
    # - 在driver端即刻返回对该实例的引用c(称为actor handler)
    # - 我们可以在Ray集群的任何节点上传递和使用这个actor handler。即在任何地方,
    #   我们可以通过c来invoke它对应Counter实例下的各种方法
    # ===================================================================
    c = Counter.remote()

    # ===================================================================
    # 阻塞/同步:通过c来invoke远端Counter实例的get_value()方法,并确保方法执行完毕。
    # 执行完毕后才能接着执行driver进程上剩下的代码操作
    # ===================================================================
    print(ray.get(c.get_value.remote()))  # 0
    
    # ===================================================================
    # Increment the counter twice and check the value again.
    # 道理同上,不赘述
    # ===================================================================
    c.inc.remote()
    c.inc.remote()
    print(ray.get(c.get_value.remote()))  # 2

3.3 Ray cluster架构简图


现在我们已经通过以上例子对Ray运作原理有了一些基本感知,我们来进一步探索一个ray cluster的组成

  • 在一个ray cluster中,会有一台head node和若干worker node
  • Driver process是一种特殊的worker process,它一般负责执行top-level application(例如python中的__main__),它负责提交想要执行的任务,但却不负责实际执行它们。理论上driver process可以运行在任何一台node内,但默认创建在head node内。
  • Worker process负责实际任务的执行(执行Ray Task或Ray Actor中的方法)。
  • 每台node中还有一个Raylet process,它负责管控每台node的调度器和共享资源的分配。
  • Head node中的GCS将会负责维护整个ray cluster的相关服务。

四、代码细节


本章将解读更多代码实践上的重要细节。我们通过图例的方式抽象出代码运行的过程,而具体代码可参考文中给出的相关链接

4.1 训练入口

ppo_ray相关的训练入口在:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/cli/train_ppo_ray.py


在main中我们启动了driver进程,并执行训练函数train(args),这里主要做了如下几件事:

  • 在ray集群上部署Actor/Ref/Critic/RM实例
  • 在ray集群上部署vllm_engines实例
  • 配置Actor和vllm_engines之间的通讯,用于传递权重
  • 训练Actor和Critic模型

我们依次来解读这几个关键步骤。同时为了在表述上消除歧义,我们接下来谈到“Actor”时,会使用Ray-Actor和PPO-Actor来做区分,从之前的介绍中可知,Ray-Actor是指部署在Ray集群中的远端class,PPO-Actor/Ref/Critic/RM都属于Ray-Actor。

4.2 部署Actor/Ref/Critic/RM实例


(1)非共同部署

针对图2.1的情况,我们以PPO-Actor为例,看代码是如何将其部署到Ray集群上的。

  • PPORayActorGroup创建在driver进程上,可将它理解成一种部署方案,专门负责部署PPO中的4类模型
    • PPORayActorGroup中维护着self._actor_handlers,它是一个List[ray.actor.ActorHandle],列表中每个元素表示某个远端Ray-Actor的引用,而这个远端Ray-Actor可以是PPO-Actor/Ref/Critic/RM实例。如前文所说,我们可以在ray集群中的任何位置调用这个handler,来对相应的远端Ray-Actor执行操作。
    • 在本例中,我们创建了4个Ray-Actor(1个master-actor,3个worker_actor)。每个Ray-Actor都运行在一个worker进程中。在创建Ray-Actor的同时,我们也会去修改worker进程的环境变量。后续当我们在这些worker进程中启动ds_zero相关的分布式配置时,ds会读取这些环境变量信息,这样我们就知道哪些Ray-Actor同时又构成ds中的数据并行组。
    • 使用PPORayActorGroup部署模型实例的代码如下:
model = PPORayActorGroup(
        # 为部署该模型的全部实例,我们想用多少台node,例如本例中为2
        args.actor_num_nodes,
        # 为部署该模型的全部实例,我们每台node上想用多少gpu,例如本例中为2
        args.actor_num_gpus_per_node,
        # Actor/Critic/Reward/ReferenceRayActor
        ActorModelRayActor, 
        # pg可理解为,在ray cluster中锁定/预留一片资源,然后只在这片资源上部署该模型全部实例。
        # (pg维护在Head Node的GCS上,参见3.3)
        # 例如本例中,pg锁定的资源为node0 gpu0/1, node1 gpu0/1,
        # 我们只在上面部署ActorModelRayActor全部实例
        pg=pg,
        # 当我们在pg指向的预留资源中分配模型实例时,再进一步指定每个实例占据一张gpu的多少部分
        # 等于1说明每个实例占满一张gpu,即“非共同部署”
        # 小于1说明每个实例只占部分gpu,即“共同部署”,例如PPO-Actor/Ref共同部署在一张卡上
        num_gpus_per_actor=0.75 if pg else 1,
    )
  • ActorModelRayActor创建在远端worker进程上,是Ray-Actor。它包含了设置ds_zero分布式环境、加载模型权重、数据集准备、optimizer/scheduler准备、训练等一系列操作。

PPORayActorGroup代码参见:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/launcher.py#L143
根据这份代码,大家可自行去找Actor/Critic/Reward/ReferenceRayActor的相关实现。

(2)共同部署

针对图2.2的情况,我们以PPO-Actor为例,看代码是如何将其部署到Ray集群上的。

  • PPORayActorGroup:在driver进程上创建2个PPORayActorGroup,分别管理PPO-Actor,PPO-Ref的部署
  • 使用actor_model = PPORayActorGroup(..., pg = pg, num_gpus_per_actor=0.75)创建PPO-Actor部署方案实例;使用ref_model = PPORayActorGroup(..., pg = pg, num_gpus_per_actor=0.25)创建PPO-Ref部署方案实例
  • 这里,两个方案实例使用的pg都是同一个,即这个pg都指向“1台node,每台node 8张卡”这片预留好的资源。
  • num_gpus_per_actor = 0.75/0.25是一种创建trick,虽然我们的最终目的是为了让PPO-Actor和PPO-Ref对半分一张卡(对半=共享,不是指显存上对半分),但是:
    • 假设设置为0.5,当我们实际部署ActorModelRayActor时,Ray先在单卡上部署1个ActorModelRayActor实例,当它准备部署第二个ActorModelRayActor实例时,它发现由于每个实例只占0.5块卡,因此完全可以把第2个实例接着第1个实例在同一张卡上部署,这样就导致最终无法让PPO-Actor和PPO-Ref共享一张卡
    • 假设设置0.75,当我们在单卡上部署完1个ActorModelRayActor实例后,ray发现单卡剩下的空间不足以部署第2个ActorModelRayActor实例,所以就会把第二个实例部署到别的卡上,这样最终实现PPO-Actor和PPO-Ref共享一张卡
    • 所以,这个设置是为了达到不同类型模型的实例共享一张卡的目的,而并非真正指模型实际占据的单卡显存空间。
  • 最后,在这一步中,我们对全部ActorModelRayActor共创建8个worker进程,对全部RefenreceModelRayActor共创建8个worker进程,一共创建16个工作进程。

相关代码依然在:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/launcher.py#L143

4.3 部署vllm_engines实例

  • create_vllm_engines:在driver端,我们通过运行该函数来创建vllm_engines,过程相似于4.2节中的介绍,信息都在图中,这里不赘述。
  • LLMRayActor:worker端Ray-Actor,它主要是把vllm实例进行了一些包装,包装的目的是为了让ds_rank0和all vllm ranks间可以进行PPO-Actor的权重通讯(参见2.1(3))
  • 在上面的例子中,我们会创建4个worker进程(不占gpu资源,只占cpu资源),用于运行管理4个vllm_engine。在每个worker进程内,vllm实例还会创建属于自己的worker进程做分布式运行(这些worker进程会实际占据gpu资源)。

相关代码参见:
https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/vllm_engine.py
https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/vllm_worker_wrap.py



4.4 ds_rank0与vllm_ranks之间的通讯

在2.2中,我们说过,PPO-Actor的ds_rank0需要和all_vllm_ranks进行通讯,传递最新的PPO-Actor权重,例如以下ds_rank0要把完整的权重broadcast给16个vllm_ranks:


我们分成如下几步实现这个目标:

(1)创建通信组



Step1:

代码来自:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/ppo_actor.py#L58
这段代码执行在PPO-Actor0(ds_rank0)所在的worker进程中。这个worker进程将通过handler引用,触发远端每个vllm_engine上的init_process_group操作,并将ds_rank0纳入通讯组

        # Create torch group with deepspeed rank 0 and all vllm ranks
        # to update vllm engine's weights after each training stage.
        #
        # Say we have 3 vllm engines and eache of them has 4 GPUs,
        # then the torch group is:
        # [    0,      1, 2, 3, 4,  5, 6, 7, 8,  9, 10, 11, 12]
        # |ds rank 0 |  engine-0  |  engine-1  |   engine-2   |
        #
        # For ZeRO-1/2:
        #   1. Broadcast parameters from rank 0 to all vllm engines
        # For ZeRO-3:
        #   1. AllGather paramters to rank 0
        #   2. Broadcast parameters from rank 0 to all vllm engines
        if self.vllm_engines is not None and torch.distributed.get_rank() == 0:
            ...
            # world_size = num_of_all_vllm_ranks + 1 ds_rank0
            world_size = vllm_num_engines * vllm_tensor_parallel_size + 1
            ...
            # =====================================================================
            # 遍历每个vllm_engines,将其下的每个vllm_rank添加进通讯组中,这里又分成两步:
            # 1. engine.init_process_group.remote(...):
            #    首先,触发远程vllm_engine的init_process_group方法
            # 2. 远程vllm_engine是一个包装过的vllm实例,它的init_process_group
            #    方法将进一步触发这个vllm实例下的各个worker进程(见4.4图例),
            #    最终是在这些worker进程上执行“将每个vllm_rank"添加进ds_rank0通讯组的工作
            # =====================================================================
            refs = [
                engine.init_process_group.remote(
                    # ds_rank0所在node addr
                    master_address, 
                    # ds_rank0所在node port
                    master_port,
                    # 该vllm_engine的第一个rank在"ds_rank0 + all_vllm_ranks“中的global_rank,
                    # 该值将作为一个offset,以该值为起点,可以推算出该vllm_engine中其余vllm_rank的global_rank
                    i * vllm_tensor_parallel_size + 1, 
                    world_size,
                    "openrlhf",
                    backend=backend,
                )
                for i, engine in enumerate(self.vllm_engines)
            ]
            # =====================================================================
            # 将ds_rank0添加进通讯组中
            # =====================================================================
            self._model_update_group = init_process_group(
                backend=backend,
                init_method=f"tcp://{master_address}:{master_port}",
                world_size=world_size,
                rank=0,
                group_name="openrlhf",
            )
            # =====================================================================
            # 确保all_vllm_ranks都已添加进通讯组中
            # =====================================================================
            ray.get(refs)



Step2:

代码来自:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/vllm_worker_wrap.py#L11
这段代码实际运行在每个vllm_engine(即每个包装后的vllm实例)下的worker进程内。例如tp_size=2,那么每个vllm实例下就有2个worker进程,这两个worker进程都会运行这段代码。

class WorkerWrap(Worker):
    def init_process_group(self, master_address, master_port, rank_offset, world_size, group_name, backend="nccl"):
        """Init torch process group for model weights update"""
        assert torch.distributed.is_initialized(), f"default torch process group must be initialized"
        assert group_name != "", f"group name must not be empty"
        # =====================================================================
        # torch.distributed.get_rank(): 在当前vllm_engine内部的rank,
        #                               例如在tp_size = 2时,这个值要么是0,要么是1
        # rank_offset:当前vllm_engine中的第一个rank在“ds_rank0 + all_vllm_ranks"中的global_rank
        # 两者相加:最终得到当前rank在“ds_rank0 + all_vllm_ranks"中的global_rank
        # =====================================================================
        rank = torch.distributed.get_rank() + rank_offset
        self._model_update_group = init_process_group(
            backend=backend,
            init_method=f"tcp://{master_address}:{master_port}",
            world_size=world_size,
            rank=rank,
            group_name=group_name,
        )
        ...




(2)_broadcast_to_vllm

构建好通讯组,我们就可以从ds_rank0广播PPO-Actor权重到all_vllm_ranks上了,这里也分成两步。


Step1:PPO-Actor ds_rank0发送权重

代码在:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/ppo_actor.py#L146
这段代码运行在ds_rank0对应的worker进程中

      def _broadcast_to_vllm(self):
        # avoid OOM
        torch.cuda.empty_cache()
        model = self.actor.model.module
        count, num_params = 0, len(list(model.named_parameters()))
        for name, param in model.named_parameters():
            count += 1  # empty_cache at last param

            # Fire all vllm engines for broadcast
            if torch.distributed.get_rank() == 0:
                shape = param.shape if self.strategy.args.zero_stage != 3 else param.ds_shape
                refs = [
                    # 远端vllm_engine的每个rank上,初始化一个尺寸为shape的empty weight张量,
                    # 用于接收广播而来的权重
                    engine.update_weight.remote(name, dtype=param.dtype, shape=shape, empty_cache=count == num_params)
                    for engine in self.vllm_engines
                ]

            # For ZeRO-3, allgather sharded parameter and broadcast to all vllm engines by rank 0
            # ds_rank0发出权重(视是否使用zero3决定在发出前是否要做all-gather)
            with deepspeed.zero.GatheredParameters([param], enabled=self.strategy.args.zero_stage == 3):
                if torch.distributed.get_rank() == 0:
                    torch.distributed.broadcast(param.data, 0, group=self._model_update_group)
                    ray.get(refs) # 确保所有vllm_ranks接收权重完毕




Step2: 各个vllm_ranks接收权重

代码在:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/vllm_worker_wrap.py#L29
代码运行在每个vllm_engine(即每个包装后的vllm实例)下的各个worker进程中。例如tp_size = 2,那么每个vllm实例下有2个worker进程,这2个worker进程都会运行这段代码。

    def update_weight(self, name, dtype, shape, empty_cache=False):
        """Broadcast weight to all vllm workers from source rank 0 (actor model)"""
        if torch.distributed.get_rank() == 0:
            print(f"update weight: {name}, dtype: {dtype}, shape: {shape}")

        assert dtype == self.model_config.dtype, f"mismatch dtype: src {dtype}, dst {self.model_config.dtype}"
        # 创建同尺寸空张量用于接收ds_rank0广播来的权重
        weight = torch.empty(shape, dtype=dtype, device="cuda")
        # 接收权重
        torch.distributed.broadcast(weight, 0, group=self._model_update_group)
        # 使用接收到的权重进行更新
        self.model_runner.model.load_weights(weights=[(name, weight)])

        del weight




4.5 PPO-Actor/Critic Training


正如2.1(4)中所说,我们将部署在ray集群上的PPO-Actor/Ref/Critic/RM实例们进行分组,每组分别负责一份micro-batch的训练,上图刻画了某个组内的训练流程。一组内的训练流程发起自PPO-Actor实例(fit方法),注意不同颜色的worker0表示的是不同工作进程。共分成如下步骤执行。


Step1:发送prompts,并从vllm_engine上收集(prompt, response)。

代码参见:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_utils/experience_maker.py#L627



Step2:从Ref/Reward/Critic上收集并处理exps

代码参见:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_utils/experience_maker.py#L492



Step3: 确保将处理后的exps传送给Critic,并行执行Actor和Critic的训练

将exps传送给Critic:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_utils/experience_maker.py#L470
Actor训练:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/ppo_actor.py#L125
Critic训练:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/ppo_actor.py#L122
我们在Actor实例所在的worker进程上出发Actor和Critic的训练。以上代码只给出了训练入口,更多细节需要顺着入口去阅读。



Step4:vllm_engine权重更新。

代码参见:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ray/ppo_actor.py#L130

五、RLHF-PPO算法细节

到目前为止,我们已将Ray分布式训练部分介绍完毕。有了对工程架构的认识,很多朋友可能想进一步探索算法上的细节:例如在4.5图例中,我们收集的是什么样的exps?又是如何对PPO-Actor/Critic进行更新的?

我们知道整个RLHF-PPO训练过程大致分成2步:

  • Stage1:收集exps
  • Stage2:使用收集到的exps计算actor_loss和critic_loss,用于训练actor和critic

在OpenRLHF中的核心代码为:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_trainer.py#L19

下面我们分别解读这2个stage的过程

5.1 Stage1:collect exps


假设大家已读过上述说的【代码篇】和【理论篇】,那么对这张图的大部分细节应该不难理解。这里我们对优势(假设选择的是GAE)再做一些补充说明


在理论篇8.3(2)中,我们详细介绍过GAE定义和使用它的原因,第t步GAE的定义为:

由上式可知,如果我们想要计算出每个t时刻的GAE,我们需要从头到尾遍历2次序列:第1次计算 δt ;第2次计算 At 。我们期望尽可能减少计算复杂度,仅在1次遍历中同时计算出 δt,At ,那么我们可以按如下步骤改写

在OpenRLHF中,还设计到“advantage norm”,“reward clip & norm”等细节操作,篇幅限制不展开,大家可以自行阅读。

核心代码在:https://github.com/OpenRLHF/OpenRLHF/blob/bb46342711a203c457df2fbca5967fd0549557e0/openrlhf/trainer/ppo_utils/experience_maker.py

5.1 Stage2:Training

大家可以对照理论篇8.5和8.6节对actor_loss和critic_loss的介绍来理解上面的流程图~另外,ybq佬写过一篇对OpenRLHF中的loss分析,也可以对照阅读:ybq:OpenRLHF学习笔记-loss篇

六、参考

1、OpenRLHF
2、Ray official document
3、Ray official architecture whitepaper(要梯子,建议想看ray架构的朋友,直接看这个最新的官方白皮书,不要看2018年的那篇paper了,那个比较老了)
4、推荐一篇快速了解Ray 应用层核心概念的blog(要梯子)
5、Ray
6、vllm

ThinkSound:多模态大语言模型中的链式思维推理,用于音频生成与编辑

ThinkSound 是一个统一的 Any2Audio 生成框架,通过链式思维(Chain-of-Thought, CoT)推理进行流匹配指导

基于 PyTorch 的多模态音频生成与编辑实现:可基于视频、文本、音频及其组合,生成或编辑音频,底层由多模态大语言模型(MLLMs)逐步推理驱动。

主要特性

  • Any2Audio:支持任意模态(视频、文本、音频或其组合)生成音频。
  • 视频转音频 SOTA:在多个 V2A 基准上取得最新最优结果。
  • CoT 驱动推理:基于链式思维推理,实现可组合、可控的音频生成。
  • 交互式面向对象编辑:通过点击视觉对象或文本指令,细化或编辑特定声音事件。
  • 统一框架:单一基础模型,支持生成、编辑与交互式工作流。

Abstract

ThinkSound 将音频生成与编辑分为三个交互式阶段均由基于 MLLM链式思维(CoT)推理指导

  1. 拟音生成(Foley Generation): 从视频生成基础、语义与时序对齐的声景。
  2. 面向对象的细化: 通过点击或选择视频中的对象区域,对用户指定对象的声音进行细化或添加。
  3. 定向音频编辑: 使用高级自然语言指令对生成音频进行修改。

在每个阶段,一个多模态大语言模型都会生成与上下文相符的 CoT 推理内容,用以指导统一的音频基础模型。此外,我们还引入了 AudioCoT,一个包含结构化推理标注的综合数据集,用于建立视觉内容、文本描述与声音合成之间的联系。

带有链式思维(CoT)的 ThinkSound:(1) 由 CoT 驱动的拟音合成,捕捉语义与时间细节;(2) 面向对象的交互式精细化处理,实现用户控制;(3) 有针对性的音频编辑。

为视频生成真实的声音不仅仅是识别物体,它还需要对复杂的视觉动态和上下文进行推理,比如判断一只猫头鹰是在鸣叫还是在拍打翅膀,识别树枝轻微的摆动,并在一个场景中同步多个声音事件。

ThinkSound——在技术上,提出了三个关键创新:

  • a) 对 MLLM 进行 AudioCoT 微调,使其能生成结构化、面向音频的推理链,明确捕捉时间依赖关系、声学属性与复杂音频事件的分解过程;
  • b) 设计了一个基于 flow matching 的统一音频基础模型,支持所有三个阶段,能够从任意组合的输入模态(视频、文本、音频)中合成高保真音频。该模型直接受益于 MLLM 提供的细致 CoT 推理,将复杂音频场景分解为可控组件,在保证整体连贯性的同时实现重点声音事件的精确合成;
  • c) 引入了一个新颖的基于点击的交互界面,使用户能够选择特定视觉对象进行音频精修,CoT 推理机制将视觉关注转化为语境合理的声音合成过程。

AudioCoT Dataset for CoT-Guided Generation and Editing

Multimodal Data Sources

AudioCoT 数据集包含视频-音频和音频-文本对。对于视频-音频数据,我们利用 VGGSound 和 AudioSet中精选的非语音子集,以确保广泛覆盖现实世界的视听事件。对于音频-文本数据,我们聚合了来自 AudioSet-SL 、Freesound 、AudioCaps和 BBC Sound Effects数据对,从而构建了一个用于训练多模态模型的多样化且具有代表性的语料库。

首先移除静默的音频-视频片段,仅保留含有效内容的素材。针对AudioSet子集,根据标签信息进一步剔除了含有人声的片段,以专注于非语音音频。随后将所有音视频片段统一分割为9.1秒的固定时长,舍弃较短片段以保证数据 uniformity(统一性)。为实现数据平衡,保持音乐与音效样本的比例约为1:1,确保两类别的均衡表征。

自动化 CoT 生成流程:

 AudioCoT 数据集构建流程概览

第一阶段:基础拟音思维链生成

  • 视频-音频对处理
    1. 使用VideoLLaMA2通过差异化提示策略提取视频的时序与语义信息
    2. 结合Qwen2-Audio生成音频描述
    3. 将视频描述与音频描述通过GPT-4.1-nano整合为完整思维链
  • 纯音频-文本对处理
    采用简化流程(无需VideoLLA2),直接生成音频描述后与现有文本标注整合
    该阶段生成的思维链能捕捉内容条件与对应音频元素的复杂关联,确保两类数据共同促进音频生成推理的全面理解

第二阶段:交互式对象中心思维链生成
为实现对象聚焦的音频生成,开发基于Grounded SAM2的ROI提取框架:

  1. 对象定位:以音频描述为提示,生成潜在发声物体的边界框
  2. 时序追踪:跨视频帧持续跟踪坐标变化
  3. 语义增强:VideoLLA2为每个ROI片段提供详细语义描述
  4. 复杂操作处理
    • 建立分层推理结构,合并目标视频CoT 与参考视频的思维链CoT 以构建全局上下文
    • 结合ROI特定生成信息,通过GPT-4.1-nano生成连贯的操作逻辑

第三阶段:基于指令的音频编辑思维链生成
针对指令引导的音频编辑任务,基于四类核心(扩展、修复、添加和移除)分析并整合来自第一阶段的 CoT 信息。这些操作涵盖从扩展序列到删除不需要的片段的各种场景。GPT-4.1-nano 处理这些整合的信息,生成特定于指令的 CoT 推理链,同时执行相应的音频操作,创建(指令-CoT、输入音频、输出音频)三元组,用于模型训练和评估。

ThinkSound

 ThinkSound 架构概览。 左图: 我们的多模态 LLM 框架,其中经过微调的 VideoLLaMA 2 模型生成用于音频生成和编辑的 CoT 推理。 右图: 我们增强型多模态 Transformer 架构,该架构以 MM-DiT 为骨干,具有用于处理多模态输入的专用路径和 CoT 驱动的条件反射,从而实现高保真、基于情境的音频生成。

Overview

ThinkSound 引入了一个新颖的分步式交互式音频生成和编辑框架,该框架由 CoT 推理引导。我们的方法将复杂的 V2A 任务分解为三个直观的阶段:(1) 基础拟音生成,创建语义和时间匹配的音景;(2) 通过用户点击进行基于区域的交互式细化;以及 (3) 基于高级指令的定向音频编辑。在每个阶段,MLLM 都会生成 CoT 推理,引导统一的音频基础模型制作和细化音轨。

使用多模态 LLM 进行 CoT 推理

为了实现分步式、情境感知的音频生成,我们利用 VideoLLaMA2 作为核心多模态推理引擎。VideoLLaMA2 之所以被选中,是因为其在融合视频、文本和音频模态方面拥有领先的能力,而其先进的时空建模对于捕捉视觉事件与其对应听觉表现之间的微妙相互作用至关重要。

通过AudioCoT数据集对VideoLLA2进行微调,使其适配音频推理领域。该数据集包含专为视听任务定制的丰富标注推理链,通过微调过程使模型具备三大核心能力:

(1)音频中心化理解能力

  • 声学特性推断(如材料属性、空间混响等)
  • 声音传播建模
  • 视听对应关系推理(包括音频事件间的时序与因果关系,例如”脚步声先于开门声,随后出现对话声”)

(2)结构化思维链分解能力
将复杂音频生成/编辑任务拆解为明确可执行的步骤序列

(3)多模态指令跟随能力
可靠地解析并执行跨模态的多样化生成/编辑指令

如图3所示,微调目标采用标准的下一个token预测交叉熵损失。通过这种针对性适配,VideoLLA2被转化为专用音频推理模块,能够生成上下文精确的思维链指令,驱动ThinkSound流程的每个阶段。

CoT 引导的统一音频基础模型

ThinkSound 的核心是我们统一的音频基础模型,它能将 CoT 推理无缝转换为高质量音频,具体细节见图 3 右侧部分。使用预训练的 VAE将音频编码为潜在表示,并采用条件流匹配对模型进行训练,其中速度场预测以多模态上下文为条件,包括视觉内容、CoT 推理、文本描述和音频上下文。为了支持任意组合的输入模态,我们在训练过程中引入了无分类器引导的随机丢弃方法。通过以概率 p_drop 随机丢弃不同模态的组合,使模型在推理阶段能够处理任意输入配置——这对于我们的交互式框架至关重要。我们还结合了策略性音频上下文遮蔽,以支持音频修补和扩展等高级编辑操作

在文本处理方面,我们采用了双通道编码策略:MetaCLIP对视觉字幕进行编码,提供场景级上下文;而 T5-v1-xl则处理结构化的 CoT 推理,以捕捉详细的时间和因果关系。这两种互补的表示被有效融合,MetaCLIP 的特征作为全局条件信号,而 T5 的输出则支持基于推理的精细控制。

我们改进的 MM-DiT 架构基于多模态生成建模领域的最新进展,包含三大关键组件:(1)采用混合型 Transformer 主干网络,在模态专用与共享处理之间交替进行。多流 Transformer 块为每个模态维护独立参数,同时共享注意力机制,从而高效处理多样输入,同时兼顾跨模态学习。(2)设计了自适应融合模块,通过门控机制对视频特征进行上采样并与音频潜变量融合。这不仅能够突出显著的视觉线索、抑制无关信息,还确保视频信息直接参与后续的单流 Transformer 块。通过将视频整合到音频潜变量空间,模型可以更好地捕捉细微视觉细节及其对声景的微妙影响,实现比仅依赖音频潜变量更丰富的跨模态推理。(3)通过对字幕和视频的 CLIP 特征进行均值池化,实现全局条件控制,并借鉴 MMAudio,引入同步特征以提升音视频时间对齐效果。最终得到的全局条件被添加到时间步嵌入中,并通过自适应层归一化(AdaLN)注入多流与单流块。

 逐步 CoT 引导的音频生成和编辑

通过支持输入模式与 CoT 的灵活组合,ThinkSound 支持将音频生成分解为图 1 所示的三个直观阶段。该三阶段流程通过直观的交互式工作流程,实现了逐步精细化、高度定制化的音频生成,CoT 推理在每个步骤中将用户意图与音频合成连接起来。

阶段 1:基于 CoT 的拟音生成
在第一阶段,系统分析整段视频以识别声学要素及其关系。经过微调的 MLLM 生成详细的 CoT 推理,明确识别主要声事件、环境元素、声学属性以及它们的时间依赖关系——确定物体何时发声及声音间的相互作用。这种结构化推理指导音频基础模型生成高保真音频,精准匹配视觉场景的语义内容与时间动态。借助 CoT 推理将复杂音频场景拆解为显式声源,模型能够生成多样且连贯的声景,捕捉微妙视觉线索与运动动态,实现逼真的音频合成。

阶段 2:交互式对象聚焦音频生成
第二阶段引入交互框架,让用户通过关注特定视觉元素来优化初步声景。借助简单的点击界面,用户可以选择感兴趣的物体进行音频强化。不同于第一阶段的整体生成方式,此阶段采用基于目标区域(ROI)的局部细化,利用分割出的目标区域指导定向音频合成。经过微调的 MLLM 针对所选 ROI 生成专门的 CoT 推理,关注该物体在全局背景下的声学特性。模型在这些结构化推理引导下生成物体专属声音,与第一阶段生成的音轨自然融合。值得注意的是,此阶段的基础模型将已有音频上下文作为附加条件信号纳入考虑。

阶段 3:基于指令的音频编辑
在最后阶段,用户可通过高层次的编辑指令来优化音质或修改特定元素。MLLM 将自然语言指令转译为具体的音频处理操作,利用 CoT 推理综合视觉内容和当前音频状态。基础模型在此推理及现有音频上下文条件下执行定向修改,同时保持整体连贯性。通过自然语言控制,非专业用户也可以完成复杂的音频操作,包括添加声音、移除声音、音频修补以及音频延展。

Results

虽然目前的 MLLM 模型能够很好地理解和推理语义信息,但它们在理解视频的精确时间和空间信息方面仍然存在局限性。例如,在定位声音事件的精确时间戳时,MLLM 模型经常无法提供准确的结果或给出错误的结果。此外,目前用于音频生成的开源视音频数据集在多样性和覆盖范围方面存在局限性,可能缺少稀有或特定文化的声音事件。未来,我们将继续探索更加多样化和全面的数据集,以提升模型的性能。此外,我们还将探索更有效的方法来提升生成音频的时间和空间对齐效果。

SoulX-DuoVoice 端到端全双工语音通话大模型

Soul App 正式升级 自研端到端全双工语音通话大模型

SoulX-DuoVoice 包含一个负责对话理解与生成的 Dialogue Model 和一个负责语音生成的 Speech Model。

新模型摒弃了传统语音交互中依赖的 VAD(话音激活检测)机制与延迟控制逻辑,打破行业中普遍存在的“轮次对话”模式,赋予 AI 自主决策对话节奏的能力。AI可实现主动打破沉默、适时打断用户、边听边说、时间语义感知、并行发言讨论等。同时,模型具备多维度感知(包括时间感知、环境感知、事件感知等),口语化表达(如语气词、结巴、明显情绪起伏),音色复刻等能力,让AI更具“真人感”,支持打造更沉浸、类现实交互的语音互动新体验。

SoulX-DuoVoice 核心技术架构

SoulX-DuoVoice 结构示意图

为了尽可能保证全双工对话模型的基础对话能力,避免模型 “降智”,SoulX-DuoVoice 采用了双 LLMs 的结构。具体而言,SoulX-DuoVoice 包含一个负责对话理解与生成的 Dialogue Model 和一个负责语音生成的 Speech Model。

  • Dialogue Model
  • 接收用户侧的语音 Tokens 作为输入;
  • 负责生成机器人对话的文本 Token 序列;
  • 向下游 Speech Model 传递高维语义上下文表示[应该会比语音token方法效果更好],确保跨模态信息的完整传递
  • Speech Model
  • 接收来自 Dialogue Model 的高维上下文表示和文本 Token 序列
  • 生成机器人侧的语音 Tokens 输出
  • 通过显式的高维上下文传递机制,有效缓解了文本与语音模态间的信息干扰问题。

数据及对话训练策略

  • 精准的说话时机建模
  • 模型通过建模预测静音(Silence)和响应(Response)Token 任务,实现机器人的完全自主决策能力,精准控制对话节奏中的沉默与发声时机。
  • 沉默持续时间建模(Silence Duration Token Prediction)
  • 训练阶段:显式建模静音持续时长预测任务显著提升模型训练效率与收敛速度;
  • 推理阶段:沉默持续时长建模有效规避了 LLM 解码过程中的重复问题,无需依赖复杂的解码惩罚机制,提升了输出语音的时序稳定性,同时显著减少解码步数,降低推理延迟。
  • 可控对话风格建模
  • 支持 Prompt 驱动的个性化对话生成,通过文本和音频 Few-Shot 学习机制,实现特定对话风格驱动。
  • 安全性对齐
  • 采用融合对齐算法与监督微调的混合损失函数设计(KTO-loss 与 SFT-loss 联合优化),显著提升机器人输出的安全性和可靠性。

此次Soul技术升级便重点聚焦在全双工实时语音通话能力在陪伴场景的交互突破。具体来看,升级后AI能力特点包括:

一、全双工语音交互,AI具备自主决策反应能力

新模型支持响应(Response)、倾听(Listen)与打断(Interrupt)流式预测,AI自主决定发言时机,实现完全端到端的全双工交互——AI 与用户可以同时说话(如辩论、吵架、合唱)、适宜打断用户/被用户打断、AI主动打破沉默发起话题。

当AI拥有自主决策反应能力,在边听边说中,掌握互动时机、互动内容的“主动性”,将极大提升人机对话的自然度,并且在较长时间、多轮对话的交互中,实现沉浸的类真实交互体验。

二、日常表达口语化和情感化,情绪更鲜明的人机交互

让AI更具“真人感”,这包括在情绪表达、发音特点、对话内容等多维度的综合指标提升,更加接近现实表达。例如,情绪表达方面,除了具备笑、哭、生气等情绪特色外,新模型的声音情绪起伏更加明显,并能结合对话推进实现同步变化。在发音特点上,具备语气词、结巴、常用口头禅、咳嗽等日常语音元素。此外,AI对话的内容更加口语化、社交化,而非书面语言。

三、时间、事件、环境感知能力,互动更具沉浸感

Soul的新模型基于纯自回归模型架构,统一文本和音频生成(Unified Model),充分利用大语言模型强大的学习能力,让AI发言深度整合人设、时间、环境及上下文对话等信息。这意味着,具备感知、理解能力的AI能够更好塑造“数字人格”,形成丰富的AI故事线,让人机交互真正成为“情感与信息的双向交流”。

值得关注的是,Soul AI团队正在积极推进全双工语音通话模型在多人交互场景的创新应用。该技术突破使得AI在多人语音对话中能够基于智能决策算法,精准把握发言时机,有效引导话题讨论并实现深度延伸,从而在复杂社交场景中构建更自然的交互体验。

Recent Advances in Speech Language Models: A Survey

本文对语音语言模型 (SpeechLM) 领域进行了综述。

想象一下,如果 AI 能够像人类一样自然地进行语音对话,不再需要传统的「语音转文字(ASR)- 文本大模型处理(LLM)- 文字转语音(TTS)」的繁琐流程,而是直接理解和生成语音,那将是怎样的体验?这就是语音大模型(语音语言模型,SpeechLM)要解决的核心问题。

传统的语音交互系统存在三大痛点:信息丢失、延迟严重、错误累积。当语音转换为文字时,音调、语气、情感等副语言信息完全丢失;多个模块串联导致响应延迟明显;每个环节的错误会层层累积,最终影响整体效果。

SpeechLM 的出现彻底改变了这一局面。它能够端到端地处理语音,既保留了语音中的丰富信息,又大幅降低了延迟,为真正自然的人机语音交互铺平了道路。

技术架构:三大核心组件构建语音智能

本论文深入剖析了 SpeechLM 的技术架构,发现其由三个关键组件构成:语音分词器、语言模型和声码器。

  • 语音分词器(Speech Tokenizer)负责将连续的音频信号转换为离散或连续的 token 表示。根据建模目标的不同,可以分为语义理解型、声学生成型和混合型三类。语义理解型专注于提取语音的语义内容,声学生成型则重点保留音频的声学特征,而混合型试图兼顾两者的优势。
  • 语言模型(Language Model)是整个系统的核心,主要采用基于 Transformer 的自回归架构。通过扩展词汇表实现同时处理文本和语音 token,实现真正的多模态建模能力。
  • 声码器(Token-to-wav Synthesizer,Vocoder)则将语言模型生成的 token 转换回可听的音频波形,完成从抽象表示到具体语音的最后一步转换。

训练策略:从预训练到指令微调的完整流程

训练一个高质量的 SpeechLM 需要精心设计的训练策略。我们的综述详细梳理了当前主流的训练方法,包括三个关键阶段。

预训练阶段是基础,可以选择冷启动或继续预训练两种方式。冷启动从零开始训练,而继续预训练则基于已有的文本语言模型进行适配,后者通常能获得更好的效果。关键在于如何有效对齐文本和语音的表示空间,使模型能够充分利用两种模态的共同信息与互补信息。

指令微调阶段让模型学会遵循各种指令执行不同任务。研究者们通过构建大规模的指令跟随数据集,让 SpeechLM 具备了处理多样化语音任务的能力。

后对齐阶段则通过人类反馈强化学习等技术,进一步优化模型的输出质量和安全性,确保生成的语音既自然又符合人类偏好。

交互范式:实现真正自然的语音对话

语音交互的未来不仅在于理解和生成,更在于如何实现真正自然的对话体验。传统的语音交互采用「你说完我再说」的模式,但真实对话中人们经常会打断对方或同时说话。为了让 AI 具备这种自然对话能力,研究者们正在开发具有实时交互能力的 SpeechLM。

关键突破在于全双工建模技术,它包括两个核心特性:用户中断能力,即模型可以被用户打断并适当响应;同时响应能力,即模型能够在处理输入的同时生成输出。通过流式处理和全双工架构,SpeechLM 能够支持真正的双向同时通信,让人机交互变得更加自然流畅。

应用场景:重新定义人机交互的边界

SpeechLM 的应用潜力远超我们的想象。在语义相关应用方面,它能够进行自然的语音对话、语音翻译、自动语音识别、关键词检测等任务。更重要的是,这些任务都可以在统一的框架下完成,无需针对每个任务训练专门的模型。

在说话人相关应用中,SpeechLM 展现出了强大的说话人识别、验证和分离能力,甚至可以根据指令生成特定音色的语音。这为个性化语音助手和多人对话系统开辟了新的可能性。

最令人兴奋的是副语言学应用,SpeechLM 能够理解和生成带有特定情感、语调和风格的语音。它不仅能识别说话者的情绪状态,还能根据指令生成相应情感色彩的语音回应,让人机交互变得更加生动自然。

评估体系:多维度衡量模型性能

如何科学评估 SpeechLM 的性能是一个重要课题。我们的论文系统梳理了当前的评估方法,包括自动评估和人工评估两大类。

自动评估涵盖了表示质量、语言学能力、副语言学特征、生成质量和多样性、实时交互能力以及下游任务性能等多个维度。每个维度都有相应的指标和基准测试,为模型比较提供了客观标准。

人工评估则主要通过平均意见分数(MOS)等主观指标,从人类感知的角度评估语音的自然度、韵律质量和音色相似度等特征。

挑战与未来:通往通用语音智能的道路

尽管 SpeechLM 取得了显著进展,但仍面临诸多挑战。组件选择的最优化、端到端训练、实时语音生成、安全风险防控以及稀有语言支持等问题都需要进一步研究。

特别值得关注的是安全性问题。SpeechLM 可能生成有害内容或泄露隐私信息,如何建立有效的安全防护机制是当务之急。同时,如何让 SpeechLM 更好地服务于资源稀缺的语言和方言,也是推动技术普惠的重要方向。

Hibiki- 流式语音翻译[Kyutai]

[Read the paper] [Samples] [HuggingFace]

Hibiki——一款 支持实时、高保真、设备端运行的语音到语音翻译模型。它基于 Moshi 所构建的核心思想和架构,借助自研的合成数据实现高效训练,并支持在移动端进行推理。Hibiki 能忠实传递原说话者的声音特性和语流,其质量和自然度在现有模型中最贴近人工翻译的效果。 Hibiki 的推理代码与模型权重开源,同时在研究论文中公开了所有训练细节。

什么是 Hibiki? Hibiki 是一款用于流式语音翻译的模型(也称为同声传译模型)。与传统的离线翻译不同(离线翻译需等到说话人完整表达完毕后才开始翻译),Hibiki 能动态地积累刚好足够的上下文,并实时逐段输出准确的翻译内容。当用户说话时,Hibiki 会一边生成目标语言的自然语音(含声音迁移),一边输出对应的文字翻译。

架构:Hibiki 是一个仅包含解码器的同声传译模型。Hibiki 利用 Moshi 的多流架构,同时建模源语音和目标语音。这使得 Hibiki 能够在生成目标语音的同时持续处理输入音频流。Hibiki 以恒定的 12.5Hz 帧率生成文本和音频标记,从而实现连续的音频输出流,并附带带时间戳的文本翻译。Hibiki 的主干模型包含 20 亿个参数。我们还训练了一个移动版本 Hibiki-M,具有 10 亿个参数,用于设备端推理。

训练:Hibiki 依赖于对来自同一说话人的源语音与目标语音及文本之间对齐数据的监督训练。由于此类数据的实际数量非常有限,我们依赖于合成数据生成。在源语言和目标语言的转录文本之间,通过一种上下文对齐的弱监督方法进行词级匹配,该方法利用了一个现成的 MADLAD 机器翻译系统。由此得出的对齐规则是:一个词应当仅在可以根据源语言预测出来时才出现在目标语言中。这一规则通过插入静音或使用具备语音控制和对齐感知能力的语音合成系统(TTS)生成目标语音来实现。

推理:Hibiki 会持续编码源语音并生成目标语音。Hibiki 依赖简单的温度采样,因此兼容批处理,不同于依赖复杂推理策略的模型。此外,Hibiki 的语音转换保真度可以通过调整无分类器引导(Classifier-Free Guidance)的系数来控制:系数越大,语音相似度越高,但系数过大会导致翻译质量下降。Hibiki 目前仅支持法语到英语的翻译。得益于其仅解码器架构,Hibiki 可在单个 H100 GPU 上批处理最多 320 条并行翻译(使用无分类器引导时为 160 条)。其更小的替代模型 Hibiki-M 可以在智能手机硬件上本地运行。当前模型训练时支持最长 120 秒的序列,使用 40 秒的上下文窗口。

  • High-Fidelity Simultaneous Speech-To-Speech Translation
  • 摘要:

    Hibiki 利用多流语言模型同步处理源语音和目标语音,并联合生成文本和音频标记,以实现语音到文本和语音到语音的翻译。还解决了同步传译中的挑战,这与其顺序式翻译不同—后者在源语句结束后才开始翻译,而同步传译需要在实时过程中根据上下文的积累逐步生成准确的翻译。为此,我们引入了一种弱监督方法,该方法利用现成文本翻译系统的困惑度,按词识别最优延迟,并构造对齐的合成数据。在监督训练之后,Hibiki 可通过标准的温度采样方法实现自适应的同步语音翻译。在法语-英语同步语音翻译任务中,Hibiki 在翻译质量、说话人一致性和自然度方面展现了当前最先进的性能。

    Introduction

    为了训练 Hibiki,我们通过对单语音频的转录文本进行翻译与再合成,生成合成的平行数据。尽管这提供了在序列层面对齐的输入输出对,但无法学习细粒度的对齐信息。为此,我们引入了“上下文对齐”方法,这是一种基于现成机器翻译系统困惑度的简单方法,用于推导词级对齐。随后通过在目标语音中适当插入静音,使 Hibiki 能够在不依赖复杂推理策略的前提下,实现实时自适应的翻译流程。

    困惑度(Perplexity)是一种衡量语言模型预测样本的好坏的指标,常用于自然语言处理中。如果一个模型预测得越准确,则其困惑度越低。
    
    想象你在玩一个猜词游戏。你的朋友正在说一个句子,说到一半突然停下来,让你猜下一个词是什么。比如:
    
    "今天天气真..."
    "我想吃一碗..."
    如果你能很容易地猜出下一个词(比如"好"或者"面"),说明这个句子对你来说"困惑度很低"。 如果你完全猜不到下一个词会是什么,那么这个句子对你来说"困惑度很高"。
    
    在人工智能和语言模型中,困惑度就是用来衡量模型对文本的预测能力:
    
    困惑度越低 = 模型越自信 = 预测越准确
    就像你很容易猜到"今天天气真好"中的"好"一样
    困惑度越高 = 模型越困惑 = 预测越不确定
    就像面对"今天我遇到了一只..." 这样的句子,下一个词可能是"猫"、"狗"、"兔子"等很多可能,很难准确预测

    此外,鉴于训练数据中说话人相似度差异较大,我们提出为训练样本标注说话人相似度类别。该方法避免了对训练数据的过滤,同时在推理阶段通过无分类器引导(classifier-free guidance)倾向生成说话人高度相似的语音输出。

    方法

    我们考虑一个源语言的语音话语,将其表示为单声道波形X∈Rfs​⋅d,采样率为 fs=24kHz,时长为 d。类似地,其目标语言的翻译表示为 Y∈Rfs​⋅d。我们假设对 X 进行了填充,以确保 X 和 Y 拥有相同的时长。我们的目标是建模条件概率 P[Y∣X]。此外,我们增加了一个约束:在已知 X 的情况下对 Y 的建模应具有因果性,并且相对于源语音具有最小延迟,例如与人工同声传译员在实时翻译场景中所面临的约束相同。

    为了通过监督学习学习这一约束,目标语音 Y 本身必须构建为满足因果性约束。我们首先假设 Y 满足这一约束,并介绍如何对其分布进行建模。随后,我们引入一个信息论准则,用以验证 Y 相对于 X 是否具有因果性并进一步将一个非因果的翻译转换为一个因果的翻译

    模型

    以 Moshi框架为基础,对从神经音频编解码器中获得的多个离散标记序列进行联合建模。

    Neural audio codec

    我们使用预先训练的因果和流式 Mimi 编解码器将 X 和 Y 编码为低帧率的离散标记序列。

    编码器将持续时间为 d 的输入波形转换为一个潜在向量 U∈RC×fr⋅d,其中 C是潜在空间的维度,帧率 fr​=12.5 Hz。随后,U被投影到其在一个包含NA​ 个条目的码本中的最近邻。该投影的残差接着被投影到另一个具有相同大小的码本中,如此重复,直到完成 Q 次投影。最后一次的残差被舍弃,解码器则被训练为从这些投影张量的总和中重构原始输入波形。

    在语言建模任务中,我们关注的不是量化后的潜在向量及其残差,而是其在码本中投影对应的离散索引。我们将这些索引表示为 (At,q)∈{1,…,NA}fr⋅d×Q。在 Mimi 中,帧率为 fr=12.5 Hz,投影次数 Q 最多为 32,但我们实际使用不超过 16 个。

    第一层量化输出被训练用于复现来自 WavLM 自监督音频模型中获得的语义信息。我们将 At,1​ 称为语义标记(semantic tokens),而将 At,q≥2​ 称为声学标记(acoustic tokens)

    这些声学标记按从粗到细的层级排列:前几层承载最重要的音频信息,后续层则建模更精细的音频细节,从而确保感知上的平滑与自然性。

    Joint modeling of discrete audio tokens

    音频流的离散标记无法轻易地被压缩为一个具有合理基数和帧率的单一离散序列。因此,我们采用 RQ-Transformer在时间轴 t 和量化器轴 q上联合建模 At,q。

    该模型由一个大型的 时序 Transformer(Temporal Transformer) 组成,其运行帧率与编解码器相同,即 fr,并接收至今为止生成的所有标记作为输入,即所有 t≤fr的标记

    A0 被定义为指示生成开始的确定性标记。然后,较小规模的深度变换器在量化器轴上对标记 At,1,…,At,Q 进行自回归建模,例如,对于所有 t≤fr⋅d 和 q≤Q :

    At,0 也是一个特殊令牌,其目标是:

    我们进一步引入了2个时间步长的声学延迟,这意味着我们建模的是 τ(A)t,q而非直接的 At,q。

    0 为特殊标记。在使用编解码器解码音频之前,会移除延迟。

    生成“内心独白”(即与生成音频内容对齐的填充文本标记)有助于提升生成音频的质量和稳定性

    Translation as multistream modeling

    我们已经介绍了方程(1)和(2)中的 RQ-Transformer 如何实现对多路离散标记流的联合建模。我们将该框架改编用于联合语音到语音与语音到文本的同步翻译任务。具体做法是将目标译文 Y 的音频标记 AY 与源语音 X的标记 AX 在量化器维度 q 上进行拼接,即:

    Hibiki 还预测一个文本流 Wt​,对应于输出 Y 的转录文本,并在词与词之间加入足够的填充以保证其与音频保持对齐。需要注意的是,与以往多任务翻译工作不同,Hibiki 在推理阶段主动利用了这一能力。这里我们用 Wt表示文本流,其基数为 NW​,且帧率与音频流相同,均为 fr​。

    Alignment and synthetic interpretation data

    我们假设对 (X,Y) 尊重同声传译的限制。 我们现在引入一个无监督的标准来估计和执行因果关系 源语句和目标语句之间的依赖关系。

     文本域比齐

    我们首先在文本域中形式化地表达这些约束。设 S=(S1,…,Sn)表示源语句 X中的词序列,T=(T1,…,Tm) 表示目标语句 Y 中的词序列。

    理想对齐(Ideal alignment):我们希望定义一个理想的对齐序列 (ajideal​)∈{1,…,n}m,其中 ajideal​ 表示第 j个目标词 Tj 在生成前应等待的源词 Si​ 的索引,以最小化对 Tj​ 的不确定性。

    若训练使用的对齐比 aideal 激进(即目标词提前生成),则模型在推理时可能出现幻觉现象(hallucination);而若对齐更保守(即目标词延后生成),则模型依然保持因果性,但会引入额外的延迟

    上下文对齐(Contextual alignment)

    我们引入一个标准来估计  aideal  。我们将其表示为条件对数似然:

    我们预期 logpj,i​ 随着 i 的增加而上升,因为更多的上下文信息通常更有利于生成正确的翻译。我们的假设是,对于某个目标词 Tj​,增量 δj,i=log⁡(pj,i)−log⁡(pj,i−1) i=aj​ 时达到最大值。也就是说,j个目标词的生成在该位置获得了最大的上下文收益

    为估计 log⁡(pj,i),我们使用现成的文本翻译语言模型 MADLAD-3B,将其输入截断为前 i 个源词,并计算预测第 j个目标词的对数概率log(p^​j,i)。据此,我们定义了一种上下文对齐方法,用以估算每个目标词最优的等待位置,并以图 3 的形式加以示意。

    图3:我们使用一个预训练的文本翻译模型,计算目标词 “into” 在不同输入截断条件下的对数似然(log-likelihood)。当对应的源词 “en” 出现在输入中时,我们观察到“into”的对数似然显著上升(详见公式(6))。这表明该源词提供了关键的上下文信息,从而支持了我们对最优对齐点的判定方法。

     音频域对齐

    给定一对语音对齐样本 (X,Y),我们使用 Whisper 模型对其进行转录并提取时间戳,然后应用公式(6)计算对齐位置。如果目标语句 Y 中第 j 个词的时间戳在源语句 X 中第 ajctx​ 个词之后,则认为该对齐 ( ajctx​ ) 是被遵守的。

    为了降低对齐错误的影响,我们要求目标语音 Y 相比上下文对齐结果至少滞后 2 秒;同时,我们会排除局部延迟中高于滑动窗口(5 个词)平均延迟 25% 的“尖峰”异常【某个词的时间延迟相对于其上下文明显偏高,高出周围词平均延迟的 25% 以上】。

    静音插入(Silence insertion)

    若 Y 不满足对齐要求,可通过在目标词前插入适量静音段来调整其时序,如图 1 所示。然而该方法存在两点限制:

    1. 当时间戳不准确或词之间没有自然停顿时,静音插入可能造成生硬的语音切断;
    2. 调整后的 Y 可能相对理想对齐严重滞后,例如当 Y的语速慢于 X 时。

    该方法用于语音翻译训练阶段的样本对齐。

    对齐感知的语音合成(Alignment-aware TTS)

    为了获得更加自然的对齐语音数据,我们使用具备硬性与软性位置控制能力的 TTS 模型对 Y 进行(重新)合成,同时保留对说话人风格的准确建模。这种方法不仅可以生成对齐更好的训练数据,还可提升词错误率(WER)和说话人相似度。

    我们训练一个 TTS 模型,其输出同时包括音频和与之同步的文本序列,并在输入中加入说话人条件。文本流被强制与目标文本完全一致,模型仅允许插入填充标记。音频输出相对于文本是滞后的,以便其内容受文本控制,不论是内容还是时间戳。

    当 TTS 提前于对齐点 actx 时,填充标记会被强制插入以延迟下一个词的生成;当 TTS 滞后于目标时,会在填充标记的 logits 上施加惩罚,惩罚值随着滞后时间从 1 秒增加到 2 秒时线性从 0 增加到 -2。这样能平滑提升语速,从而追上源语音的节奏。

    我们对每个输入生成 6 到 8 个候选样本,优先根据词错误率选出最佳结果,其次考虑说话人相似度。该方法仅应用于语音翻译微调数据集的构建。

    声音迁移(Voice Transfer)

    改进语音迁移数据:在训练带有声音迁移功能的语音翻译模型时,通常采用同一说话人的合成配对序列进行监督训练。然而,图 4 显示,该数据集中源语音和目标语音的说话人相似度(以说话人嵌入的余弦相似度衡量)平均仅为 0.23,相当低。作为参考,当前最先进的跨语种声音迁移系统的平均说话人相似度约为 0.40。因此,我们使用对齐感知的 TTS 重新生成了 CVSS-T 数据,这使得迁移语音可以更好地保留说话人特征。如图 4 所示,重新合成后的 CVSS-T 数据的平均说话人相似度提升至 0.47。尽管如此,我们的训练数据混合了合成数据与重新合成的 CVSS-T,整体相似度仍分布较广泛,其中仍有大量样本低于 0.40。

    条件训练(Conditional Training)如果直接筛选出说话人相似度高的数据用于训练,确实可以提高声音迁移效果,但会导致训练样本显著减少,从而可能损害翻译质量。例如,若仅保留说话人相似度大于 0.40 的样本,将导致 训练数据减少约 45%。因此我们采用条件训练(conditional training),在训练过程中告知生成模型每个样本在声音迁移方面的可靠性。我们为每个训练样本打上一个离散的“声音迁移评分”,其标签来自以下集合:

    { very_bad, bad, neutral, good, very_good }
    

    评分依据是说话人相似度的分位数划分,每个评分标签对应一个可学习的嵌入(embedding),在模型的每个时间步加入输入中。值得注意的是,这些分位点是在合成数据和 CVSS-T 数据合并前计算的,以确保模型学习的是实际的说话人相似度,而不是将某标签“误绑定”到某特定数据集。在推理阶段,我们始终传入 “very_good” 标签,以期生成具有良好说话人保持能力的语音。

    无分类器引导(Classifier-Free Guidance)我们采用**无分类器引导(classifier-free guidance)**来增强条件训练的效果。具体做法是:分别使用 very_goodvery_bad 条件下计算输出 logits,然后结合两者以调整采样过程,从而增强模型在推理时对说话人风格的控制能力。

    这与实时推理兼容,因为它能以批大小为 2 同时生成两组 logits。结果表明,这种方法能显著提升语音转换效果。

    Experiments

    训练策略

    通过以下步骤训练一个法语-英语的语音翻译系统:

    文本预训练。 我们首先在多语言的纯文本数据上,从头开始预训练 Temporal Transformer,采用下一个词预测任务

    音频预训练。 在预训练好的文本模型基础上,使用非平行的法语和英语数据,在单流设置下进行音频预训练

    语音翻译训练。 我们构建了一个约包含 4 万小时法语和英语语音的翻译数据集。首先从一批富有表现力的法语音频中提取约 230 万条单说话人的语音片段,每段时长约 60 秒。我们使用 Whisper的 large-v3 模型对这些片段进行转录,并借助 PySBD将转录文本分句,然后使用 MADLAD-3B分别翻译每个句子,最后重新拼接成英文翻译文本。我们利用 TTS 系统合成语音,条件是原始法语说话人的身份(使用一段 10 秒的语音)。我们应用静音插入技术,以获得同声传译的语音对。

    我们进行基于说话人相似度的条件训练,并对源语音频施加噪声增强。在每对训练数据中,我们在源语音流中语音结束后首帧加入一个特殊的输入 EOS 标记,在文本流中也加入另一个特殊 EOS 标记,指示模型生成语音的结束。

    语音翻译微调。 我们使用引入的对齐感知 TTS 技术,构建了一个包含长句式的合成数据集,并改进了 CVSS-T/train 数据集,具有自然停顿和较高的说话人相似度,总计约 900 小时。

    Hibiki-M 的训练。 其训练流程与 Hibiki 相同,先进行文本和音频预训练。在语音翻译训练阶段,通过软蒸馏从 Hibiki 获得知识,再进行相同的微调步骤(不再进行蒸馏)。

    推理:

    我们使用流式编解码器对音频进行编码,并将生成的 token 输入 Hibiki,同时解码输出 token 以获得流式翻译。在输入结束时,我们向模型发送一个 EOS(结束)标记,并持续采样,直到模型自行生成一个 EOS。推理参数通过对 Audio-NTREX 的保留 8% 数据和 CVSS-C 的验证集分别进行交叉验证来确定。对于 Audio-NTREX,最优参数为 γ = 3.0,温度为 0.8,audio token 的 top-k 为 250,text token 的 top-k 为 50。在 CVSS 上,除了对 text token 使用温度为 0.1 的采样外,其余配置相同。我们推测,较低的文本采样温度通常有助于提升翻译质量,但可能导致模型过早生成 EOS 标记

    Results

    表 1: 与离线基线系统的对比结果。我们还报告了一个闭源流式模型(*)的性能,因为它采用了相同的评估协议。

    表 1 将 Hibiki 与在翻译时可以访问完整源音频的离线基线模型进行了比较。尽管 Hibiki 进行的是同声传译,但它的表现优于所有模型,包括 StreamSpeech 的离线版本。表 2 将 Hibiki 与可用的同声传译基线模型进行了对比。在短格式设置中,我们的模型优于 StreamSpeech 和 Seamless,但平均延迟时间增加了 0.7 秒。长格式数据集的挑战更大,因为 StreamSpeech 无法生成清晰易懂的翻译。Hibiki 的表现优于 Seamless,但延迟时间平均高出 0.8 秒。

    音频保真度。

    如表 2 所示,关于说话人相似度的客观评估结果表明,Hibiki 在语音转换方面显著优于 Seamless(我们未评估 StreamSpeech,因为它不执行语音转换)。表 3 中的人类评估结果进一步验证了这一点,并显示 Hibiki 在音质和自然度方面远高于 Seamless,接近专业人工口译音频的真实水平。

    这意味着 Hibiki 不仅能够生成高质量的音频,还能在语流中插入流畅且自然的停顿。

    消融实验:对齐策略。
    我们将所提出的上下文对齐方法与其他方案进行比较。表 4 显示,在训练时对目标语音不施加延迟会导致翻译质量非常低,这是可以预期的,因为模型缺乏足够的上下文来生成翻译。为训练样本添加延迟能够提升 ASR-BLEU 分数,其中 10 秒的延迟表现为一个合理的选择;但平均延迟(以 LAAL 表示)比使用上下文对齐差得多,因为模型无法根据上下文自适应调整生成节奏。“句子对齐”作为常量延迟与上下文对齐之间的折中方案,将每个输出句子的起始时间对齐到相应源语句子的结束时间。这种做法提高了翻译质量,但延迟反而更严重。

    总体而言,上下文对齐在翻译质量与延迟之间提供了最佳平衡。


    消融实验:无分类器引导(Classifier-free guidance)。
    表 5 显示,使用“very good”标签时,说话人相似度为 0.42,与 Seamless(0.43)相当。采用无分类器引导(γ = 3.0)可以显著提升说话人相似度,同时不会明显损害翻译质量。但如果权重设得过高,模型性能会下降,表现为生成的语音不可理解。

    附录中有趣地展示了:将 γ 增大到极端值时,会导致生成的语音出现夸张的法语口音(即我们的实验中使用的源语言),我们认为这是由于用于标注数据的说话人模型存在偏差所致。


    消融实验:通用消融。
    同时预测文本 token 如何作为语音生成的框架。表 4 验证了这一点:将 Hibiki 作为单模态模型训练(即不预测文本输出),会导致性能大幅下降;同样地,从一个预训练文本语言模型出发,直接进行语音到语音翻译(S2ST)训练,效果也很差。

    推理能力

    批量推理。Hibiki 的推理采用恒定帧率下的温度采样,这使得流式的无分类器引导和多个语音源的并行处理变得非常简单。这一点不同于 Seamless 和 StreamSpeech,它们的推理策略更复杂,需对每个序列做出动态且不规则的决策,因而难以批量处理。图 5 显示,即便同时处理 320 条语音序列(或在无分类器引导下处理 160 条),Hibiki 在 H100 上仍能保持快于实时的推理速度。

    端侧推理。我们蒸馏得到的 Hibiki-M 在短文本和长文本翻译任务上都能与 Seamless 相媲美,如表 2 所示。我们将其在长音频上的较低说话人相似度归因于其建模的量化器数量较少(8 个而非 16 个),这使得音频比特率降低了一半。图 6 展示了 Hibiki-M 在 iPhone 16 Pro 上的推理轨迹。即使使用支持无分类器引导所需的批量大小为 2,Hibiki-M 在一分钟的推理过程中仍能保持快于实时的速度。若使用滑动窗口注意力对 Hibiki-M 进行训练,还可进一步提升其实时性能。


    局限性

    本研究仅聚焦于一个翻译任务(法语到英语),若扩展到更多语言,可能需要借助 MADLAD 这类大规模多语言模型,但这也意味着需为更多语言训练相应的 TTS 系统。此外,虽然 Hibiki 在与 CVSS-C 的真实目标对比时能达到 35.5 的 ASR-BLEU 分数,但若将其输出与 MADLAD 的文本翻译对比,则可达到 47.9 的 ASR-BLEU。这表明 Hibiki 非常擅长生成与 MADLAD 类似的翻译结果;若使用更优或更丰富的伪目标(pseudo-target)对其进行训练,Hibiki 有望进一步提升相对于真实目标的翻译质量。

    Kyutai’s STT and TTS:Delayed Streams Modeling framework

    关于Kyutai:Kyutai 是一家位于法国巴黎的非营利性人工智能研究实验室,成立于 2023 年 11 月。该实验室致力于推动人工智能的开放研究,特别关注多模态大模型的开发与创新算法的研究,旨在提升 AI 的能力、可靠性和效率,并促进其民主化。

    延迟流建模 (DSM) :一种用于解决多种流式 X 到 Y 任务的技术(其中 X 和 Y 可以是语音或文本),它将我们在 MoshiHibiki 中采用的方法形式化了。

    Kyutai STT

    Kyutai STT 是一种流式语音转文本模型架构,在延迟与准确率之间实现了无与伦比的平衡,非常适合交互式应用。它支持批处理,可在单个 GPU 上处理数百条并发对话。发布了两个模型:

    • kyutai/stt-1b-en_fr:一个低延迟模型,支持英语和法语,并内置语义级语音活动检测器
    • kyutai/stt-2.6b-en:一个更大的仅支持英语的模型,经过优化以实现尽可能高的准确率

    Streaming and accurate: 在英文测评集上的WER指标

    Word error rate, lower is better.

    Kyutai STT 是一个流式模型,这意味着它在接收音频的同时实时转录,而不是假设音频在开始处理前已全部可用。这使其非常适合诸如 Unmute [Make any LLM listen and speak using Kyutai’s speech-to-text and text-to-speech.] 之类的实时应用。

    它输出格式良好的转录文本,包含标点符号,同时还提供词级时间戳

    在准确性方面,它的表现仍可与那些能够一次性访问完整音频的最先进非流式模型相媲美

    语义语音活动检测器

    对于像 Unmute [Make any LLM listen and speak using Kyutai’s speech-to-text and text-to-speech.] 这样基于串联式语音对话的应用程序,需要一种方式来判断用户是否已经说完,以便生成回应。最常见的方法是使用一个独立的语音活动检测(VAD)模型,判断用户是否在说话,并在用户说完之后等待一个固定的时间。但在实际应用中,不可能找到一个适用于所有情况的等待时间。人们在说话时经常会出现长时间停顿,这会导致这种朴素方法出现误判。

    为了解决这个问题,Kyutai STT 不仅预测文本,还预测用户是否已经说完话的概率。对于停顿的判断延迟会根据用户说话的内容和语调自适应调整。

    低延迟

    kyutai/stt-1b-en_fr延迟设定为 500 毫秒,也就是说,每个词会在说出后约 500 毫秒被转录。而 kyutai/stt-2.6b-en 的延迟为 2.5 秒

    在 Unmute [Make any LLM listen and speak using Kyutai’s speech-to-text and text-to-speech.]中,我们使用一种称为 “flush trick”(刷新技巧)的方法来进一步降低响应延迟。当语音活动检测器预测用户已经说完时,我们还需再等 500 毫秒(即 STT 模型的延迟),以确保不会截断转录的结尾部分。

    为了减少这段延迟,我们利用了语音转文本服务器“比实时更快”的处理能力。当检测到说话结束时,我们请求 STT 服务器尽可能快地处理此前已发送的音频。服务器的运行速度约为实时的 4 倍,因此可以在大约 125 毫秒内处理这段 500 毫秒的音频(即 500ms ÷ 4 = 125ms)。通过这种方式,我们“扭曲时间”,只需等待这 125 毫秒,即可确保所有内容已被转录。

    高吞吐量

    Kyutai STT 非常适合在生产环境中部署:在一块 H100 GPU 上,它可以同时转录 400 路实时音频流。这得益于其采用的延迟流建模架构(见下文),该架构允许我们在无需任何额外“粘合代码”的情况下,以大批量运行模型,并实现流式处理。

    相比之下,将 Whisper 改造成流式模型需要一个完全独立的研究项目——Whisper-Streaming。该系统会反复在最近几秒的音频上运行 Whisper,并将重叠的转录结果拼接在一起。

    Whisper-Streaming 在技术上令人印象深刻,但它不支持批处理,这导致吞吐量大大降低。若目标延迟更低,其吞吐量会进一步下降,因为需要更频繁地重新运行 Whisper。

    延迟流建模

    Kyutai STT 的主要创新是一种在 Kyutai 开发的技术,称为延迟流建模(delayed streams modeling),最早在 Moshi 项目中率先使用了这一方法。

    传统的语音转文本方法通常是将整段音频一次性输入模型,并由模型逐步(自回归地)生成文本。例如,Whisper 就是这样做的,它使用的是编码器-解码器结构的 Transformer 模型。

    在 Kyutai STT 中,将数据表示为时间对齐的文本流和音频流。本质上,音频和文本是“并列”存在的,而不是一个接着另一个。文本流会进行填充,以确保文本的时间与音频对齐。只是将文本流延迟了几个帧,以便语音转文本模型能够进行一定程度的前瞻。

    使用这种表示方式的文本-音频数据来训练 Kyutai STT,教它同时建模音频流和文本流。在推理阶段,我们保持音频流固定,实时输入接收到的音频,利用模型预测文本流

    这种方法的另一个巧妙之处在于其对称性。我们可以通过延迟音频流而非文本流,并保持文本固定(教师强制),来实现文本到语音的转换,进而预测音频为使模型能够预测填充符号以对齐文本流和音频流的时间,需要一些技巧

    Kyutai TTS

    Kyutai 文本到语音(TTS)最初是我们在 Moshi 开发过程中使用的内部工具。作为我们对开放科学的承诺的一部分,我们现在向公众发布了改进版的 TTS:kyutai/tts-1.6b-en_fr,一个拥有 16 亿参数的模型。该模型包含多项创新,使其特别适合实时使用。

    State of the art

    Kyutai TTS 在文本到语音领域树立了新的技术水平。词错误率(WER)衡量 TTS 未能准确遵循文本脚本的频率。说话人相似度则是评估在语音克隆时生成音频与原始样本声音相似程度的指标。我们在 NTREX 数据集中,选取了 15 篇英文和 15 篇法文新闻文章,对 Kyutai TTS 与其他模型进行了对比。除 Kyutai TTS 和 ElevenLabs 外,所有模型均按句子逐句生成,因为我们观察到这样能获得最佳效果。

    适合大语言模型(LLM)

    Kyutai TTS 无需提前获取完整文本,从接收到第一个文本标记到生成第一段音频的延迟为 220 毫秒。在 Unmute.sh 部署环境中,我们通过批处理同时处理最多 32 个请求,使用 L40S GPU 测得的延迟约为 350 毫秒。

    其他被描述为流式的 TTS 模型仅在音频上实现流式处理,仍然需要提前知道完整文本。

    Kyutai TTS 是首个同时支持文本流式处理的文本到语音模型。你可以边由大语言模型生成文本边输入,Kyutai TTS 会即时开始处理,从而实现超低延迟。这在大语言模型生成耗时较长的情况下尤为有用,比如资源受限环境或生成长文本段落时。

    文本流式处理得益于下文详细介绍的延迟流建模技术。

    语音克隆

    为了指定语音,我们使用一段 10 秒的音频样本。TTS 能够匹配源音频的声音、语调、说话习惯和录音质量。

    长时段生成

    大多数基于 Transformer 的 TTS 模型针对生成少于 30 秒的音频进行了优化,通常不支持更长时间的生成,或者在处理较长音频时表现不佳。Kyutai TTS 在生成更长时段音频方面没有任何问题。

    词级时间戳

    除了音频本身,Kyutai TTS 还会输出生成词语的精确时间戳。这对于为 TTS 提供实时字幕非常有用。

    延迟流建模

    Kyutai TTS 独特的能力,如文本流式处理、批处理和时间戳功能,均得益于 Kyutai 开发的一项技术——延迟流建模,该技术最早由我们在 Moshi 项目中开创。

    传统使用语言模型进行文本到语音转换的方法,是基于输入文本与音频的分词输出拼接在一起进行训练:

    这意味着这些模型是流式的,但仅限于音频:在已知完整文本的情况下,它们开始生成音频,并允许你在生成过程中访问部分结果。然而,完整文本仍需提前获知。

    而在 Kyutai TTS 中,我们将问题建模为时间对齐的文本流和音频流。本质上,音频和文本是“并列”存在的,而不是一个接着另一个。我们只是将音频流延迟了几个帧,以便文本到语音模型能够进行一定程度的前瞻:

    这意味着一旦知道了前几个文本标记,无论最终文本多长,我们都可以开始流式输出音频。

    在输入端,我们接收到一个没有时间信息的词语流,但模型需要使用填充符使文本流与音频对齐。这就是动作流(action stream)的作用:当它预测到 [word] 时,表示“我已完成当前词的发音,请给我下一个词”,此时我们将下一个词输入到文本流中