Q-Former 跨模态对齐

论文:BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models

Q-Former 最开始被设计用于跨模态任务(如视觉-语言建模)的 transformer 模型。它由 BLIP-2(Bootstrapping Language-Image Pre-training 2)提出,目的是在视觉特征和语言模型之间建立高效的桥梁,从而提高大规模预训练语言模型在多模态任务中的表现。

后续也逐渐用于语音-文本模态的对齐任务中,比如: DeSTA2.5-Audio

BLIP-2 提出了一个通用且高效的预训练策略,能够从已存在的冻结图像编码器和大型语言模型中引导出强大的视觉-语言模型。核心思想是引入一个轻量级的Querying Transformer(Q-Former),在冻结的图像编码器和语言模型之间架起桥梁。Q-Former 被设计为一个信息瓶颈,负责从图像编码器中提取对文本生成最有用的视觉特征,并将其传递给大型语言模型。

Querying Transformer(Q-Former)

Q-Former 是一个轻量级的 Transformer 模型,包含一组可学习的查询向量(queries)。其架构由两个共享自注意力层的 Transformer 子模块组成:

  1. 图像 Transformer:与冻结的图像编码器交互,提取视觉特征。
  2. 文本 Transformer:同时可以作为文本编码器和解码器。

这些查询向量通过交叉注意力层与图像特征进行交互,提取出固定数量且对文本生成最有用的视觉特征。Q-Former 的输出是这些查询向量的表示,记为Z 。

由于 Q-Former 的输出维度比图像编码器的输出特征要小很多(例如,32×768 vs. 257×1024),因此它起到了信息瓶颈的作用,只保留了对后续文本生成最有用的视觉信息。这种设计减轻了大型语言模型在冻结参数的情况下与视觉特征进行对齐的负担。

两阶段预训练策略

BLIP-2 的预训练分为两个阶段,每个阶段都有特定的训练目标,以逐步引导模型实现视觉和语言的有效结合。

第一阶段:从冻结的图像编码器中引导视觉-语言表示学习

在第一阶段,将 Q-Former 与冻结的图像编码器连接,通过图像-文本对进行预训练。目标是训练 Q-Former,使其能够提取与文本最相关的视觉表示。

第二阶段:从冻结的大型语言模型中引导视觉到语言的生成学习

在第二阶段,将 Q-Former 输出的视觉表示连接到冻结的 LLM 上,训练 Q-Former 使其输出的视觉特征能够被 LLM 解释和利用。根据 LLM 的类型(解码器或编码器-解码器),采用不同的策略:

对于解码器类型的 LLM:利用语言模型的自回归特性,采用语言建模损失,让 LLM 在给定视觉特征的情况下生成文本。

对于编码器-解码器类型的 LLM:将视觉特征和前缀文本输入到 LLM 的编码器中,然后让解码器生成后续的文本,使用前缀语言模型损失进行训练。

在这一阶段,Q-Former 被训练使其输出的视觉特征能够被 LLM 有效地理解和利用,从而实现视觉到语言的高效生成。

SecoustiCodec 流式语音单码本编解码器

快手科技、天津大学、中科院自动化所与清华大学联合发布了SecoustiCodec,一种跨模态对齐的流式单码本语音Codec。支持语义信息、副语言信息、声学信息的独立编码与重构。可以应用于语音合成、语音对话模型等生成式语音任务。

背景动机

随着大型语言模型(Large Language Models,LLMs)在生成式人工智能领域的快速发展,基于语音的应用如文本到语音合成(TTS)、自动语音识别(ASR)以及语音对话系统等日益受到关注。语音编解码器作为连接原始语音信号与语言模型的重要桥梁,其作用类似于自然语言处理中的文本分词器,能够将连续的语音波形转化为离散的编码 token,便于与LLM等模型进行有效融合和交互。 

然而,当前主流的神经语音编解码器通常采用残差向量量化(RVQ)方法,通过将语音的语义信息与残差信息直接相加的方式重构语音。这种设计限制了语义编码的解耦能力及重构效果,不利于语义和声学信息的有效分离和高质量恢复。采用单一码本进行编码,这在低比特率条件下限制了编码的表达能力,难以兼顾语音的高保真度和压缩效率。同时,大多数现有模型采用非因果网络结构,导致处理延迟较高,难以满足实时语音翻译、在线会议和交互式对话系统等延迟敏感场景的需求。 

针对上述瓶颈,我们提出了SecoustiCodec,一种全新设计的神经语音编解码器框架。SecoustiCodec通过独立建模声学信息、语义信息以及副语言信息,实现了低码率、高保真度的实时流式语音编码与解码。该方法不仅提升了语义编码的可分离性和重构质量,还有效降低了延迟,显著增强了模型在实际实时应用中的适用性。

图2: SecoustiCodec 使用经过训练的声学表征(逐帧的连续值)来约束语义表征(逐帧的离散值)和副语言表征(全局的连续值)的联合训练。虽然某些副语言线索(如细微的情感变化)会呈现出难以完全由全局层面捕获的细粒度变化,但我们有意采用全局层面的副语言表征。这一设计能够在实现稳健语义解耦的同时,捕捉声学与语义表征之间的主要残差信息,例如说话人的音色和整体情感特征。我们认为,这种全局层面的表征能够高效地覆盖大部分副语言信息,并促进核心关系的建模:
语义 + 副语言 ≈ 声学

Contributions

  • 为了确保语义完整性和重建保真度,引入了副语言编码来弥合语义编码和声学编码之间的信息鸿沟。
  • 基于 VAE+FSQ 的纯语义高效量化方法,提高码本利用率以及缓解token的长尾分布。
  • 提出了一种基于对比学习的语义解缠方法,该方法在联合多模态帧级空间中对齐文本和语音。
  • 设计了一种声学约束的多阶段优化策略,通过逐步引入模块并调整不同损失成分的影响来确保模型的有效收敛。此外,采用因果架构来支持流式编码和解码。

方法

SecoustiCodec的整体架构包括三个主要的建模过程:声学建模(Acoustic Modeling)、语义建模(Semantic Modeling)和副语言建模(Paralinguistic Modeling)。这三个过程相互协作,共同实现语音信号的编码和解码。具体来说:

  • 声学建模:负责将输入的语音信号编码为连续的声学表示,并通过解码器重建语音信号。
  • 语义建模:通过对比学习和高效的量化方法,提取纯净的语义信息,并将其编码为离散的语义表示。
  • 副语言建模:提取语音中的副语言信息(如音色、情感等),并将其编码为全局级的副语言表示。

这三个表示之间的关系可以用公式S + G ≈ A 来描述,其中 S 是离散语义编码,G 是全局级副语言编码,A 是连续声学表示。通过这种方式,SecoustiCodec能够在单码本空间中实现语义和副语言信息的分离。

1.声学建模

声学建模部分包括语音编码器、声学投影和语音解码器。语音编码器将输入的语音信号Sin编码为连续的声学表示 A,然后通过声学投影模块进一步处理。语音解码器则负责将声学表示 A 重建为语音信号。

2. 语义建模

语义建模部分包括语义投影、变分自编码器(VAE)、有限标量量化(FSQ)和对比学习模块。其目标是从语音信号中提取纯净的语义信息,并将其编码为离散的语义表示 S。首先通过语义投影,将语音编码器的输出进一步映射到一个低维的语义空间。随后采用VAE + FSQ,通过VAE生成语义表示的均值 μ 和方差 σ,然后通过FSQ将这些连续的语义表示量化为离散的语义编码 S。这种方法不仅保留了语义信息,还进一步去除了副语言信息,同时提高了码本利用率。最后通过对比学习模块,将语音编码器生成的语义表示 S 和文本编码器生成的音素表示 P 对齐到一个联合帧级对齐的多模态空间中。通过最大化正样本对(S和对应的P)之间的相似度,同时最小化负样本对(S和不对应的P)之间的相似度,模型能够学习到纯净的语义表示。

其中 P 是音频表征,S 是语义表征,A是声学表征。

3. 副语言建模

副语言建模部分包括副语言编码器和语义连接器。其目标是从语音信号中提取副语言信息,并将其编码为全局级的副语言表示G。首先通过副语言编码器才从语音信号中提取副语言信息,如音色、情感等。随后经过语义连接器,将语义表示 S 和副语言表示 G 结合起来,预测声学表示 A^。通过这种方式,模型能够在解码阶段利用副语言信息来重建语音信号。

副语言语音 Gi​n 作为副语言编码器的输入:

G 表示副语言嵌入,其中 G 是固定长度的全局向量

4. 声学约束的多阶段优化策略

SecoustiCodec设计了一种声学约束的多阶段优化策略,逐步引入模块和调整损失函数的影响,确保模型的有效收敛。具体来说:

第一阶段(声学建模)Algorithm(Lines 3-4)仅训练语音编码器、声学投影和语音解码器,专注于学习声学表示A。

第二阶段(语义和副语言建模)Algorithm(Lines 5-13):冻结第一阶段的模块,训练语义投影、副语言编码器、语义连接器等模块,逐步引入对比学习损失和KL散度损失,优化语义和副语言表示。

实验结果

1.结构对比

SecoustiCodec在0.27kbps的比特率下实现了20Hz的帧率和20tokens/s的编码速率,仅次于MimiCodec。SecoustiCodec采用VAE+FSQ量化方法,同时支持单码本语义编码和连续值声学编码。SecoustiCodec使用1000小时的训练数据,与SpeechTokenizer、MimiCodec和BigCodec相当,但远少于FACodec的500000小时和TAAE的100000小时。此外,SecoustiCodec采用因果结构以支持流式编码和解码。

2. 语音重建性能

SecoustiCodec在0.27kbps和1kbps的比特率下实现了业界领先的重建质量。具体来说,SecoustiCodec(0.27kbps)在PESQ(感知语音质量评估)指标上达到了1.77,而SecoustiCodec(1kbps)的PESQ值更是高达2.58。这些结果不仅优于其他单码本模型,甚至在某些情况下超过了使用多个码本的模型。此外,SecoustiCodec在情感相似度(EmoSim)、说话人相似度(SpkSim)、对数谱距离(LSD)、基频误差(MSEP)、误配率(Mismatchrate)和词错误率(WER)等多个指标上也表现出色,证明了其在语义解耦和重建能力方面的优势。

3. 流式处理能力

SecoustiCodec的因果架构设计使其在流式处理方面表现出色。实验结果表明,SecoustiCodec的初始延迟为12.08ms,低于Encodec和MimiCodec等其他支持流式处理的模型。虽然Encodec在实时因子(RTF)方面表现更好,区别主要体现在解码阶段,SecoustiCodec在去掉声码器处理时间后的解码RTF仅为0.001,显著低于Encodec。

4. 消融实验

在不同量化方法的性能对比结果显示,VAE+FSQ量化方法在各项关键指标上均展现出优于VQ-VAE、SimVQ和FSQ等其他量化方法的表现。这一结果表明,VAE+FSQ在保留语义信息、去除副语言信息以及提高码本利用率方面具有显著优势。

对于因果结构的实验结果表明,具备因果结构的模型版本在多个指标上相较于不具备因果结构的版本有更佳表现,尤其是在实时性和低延迟场景中,这凸显了因果结构对于流式处理支持的重要作用。

在音高特征方面,实验结果表明,加入音高特征的模型在MSEP和WER等指标上的表现不如未加入音高特征的模型,这表明音高特征可能会对语义信息的提取产生干扰。

对于多阶段训练策略的实验结果表明,采用该策略的模型在所有指标上均优于未采用的版本,这表明多阶段训练策略对于模型的收敛和性能提升具有关键性影响。

声学嵌入维度的实验结果表明,较高的声学嵌入维度(256维)能够显著提升模型在多个评价指标上的表现。基于此,我们最终选择将256维作为SecoustiCodec的声学编码维度。

SecoustiCodec在单说话人场景下也表现出色,经过针对单一目标说话人进行微调后,模型在所有关键指标上均取得了高分。

SecoustiCodec的VAE + FSQ量化方法在码本利用率方面表现出色,达到了98.06%。这一结果显著高于其他量化方法,如VQ-VAE(9.7%)和SimVQ(8.61%)。高码本利用率意味着模型能够更有效地利用有限的码本资源,减少长尾分布问题,从而提高语言模型训练的效率和效果。

总结与未来展望

SecoustiCodec通过独立建模声学、语义和副语言信息,结合基于VAE + FSQ的高效量化方法、对比学习驱动的语义解耦技术和声学约束的多阶段优化策略,成功地在低比特率下实现了高保真、实时流式语音编解码。其在多个关键指标上的卓越表现证明了其在语音编解码领域的先进性和实用性。未来,我们计划探索无监督解耦方法,以减少对标注文本数据的依赖,并验证模型在其他语言上的适应性,以进一步提升其有效性和通用性。

S2SND 在线/离线统一推理的高精度说话人日志新框架

论文题目:Sequence-to-Sequence Neural Diarization with Automatic Speaker Detection and Representation

论文:https://arxiv.org/abs/2411.13849

摘自:https://mp.weixin.qq.com/s/s1JuYq5S2v2bfGnXPE0Qnw

研究动机

“谁在什么时候说话”是多说话人语音理解中的核心任务。然而,传统的说话人日志系统普遍依赖从完整音频提取说话人嵌入、聚类匹配或端到端建模等方法,往往只能离线运行,难以适应实时对话、会议转写等对系统延迟要求极高的实际场景。

为突破这些限制,武汉大学与昆山杜克大学研究团队提出了全新框架—— S2SND(Sequence-to-Sequence Neural Diarization,序列到序列的神经日志)。该方法基于序列到序列架构,创新性地融合了“说话人自动检测”与“嵌入动态提取”机制,无需聚类与先验的说话人嵌入,即可以在线推理的形式逐个识别新加入的未知说话人,并同步完成语音活动检测与说话人表示建模。

S2SND 无需聚类或排列不变训练(PIT),并具备统一支持在线推理与离线重解码等特性,在多个评估数据集的多种评测条件下均取得了领先性能,相关成果已发表于语音领域权威期刊 IEEE Transactions on Audio, Speech and Language Processing。

方法介绍

如图1所示,S2SND 框架采用序列到序列架构,主要包含三部分模块:基于ResNet-34的特征提取器(Extractor),基于Conformer的编码器(Encoder),和创新性设计的检测解码器(Detection Decoder)和表征解码器(Representation Decoder)两个部分,实现了说话人检测与嵌入提取的双向协同。首先,输入音频经过 ResNet 结构的帧级提取器生成时序说话人特征序列。随后送入基于 Conformer 架构的编码器建模长时依赖关系,输出作为下游解码器的主特征输入。在解码阶段,模型并行使用两个功能互补的解码器:检测解码器利用编码器的输出特征与目标说话人声纹嵌入作为参考信息,预测多个说话人的语音活动。反之,表征解码器则利用提取器的输出特征与目标说话人语音活动作为参考信息,提取多个说话人的声纹嵌入。

为了打通两条路径的协同优化,S2SND 引入一个可学习的全体说话人嵌入矩阵(Embedding Matrix),每一个行向量对应训练集中一个说话人的声纹嵌入。训练时,语音活动标签由标注数据提供,而目标说话人嵌入通过该矩阵查表获得(one-hot 标签与嵌入矩阵相乘),作为检测解码器的辅助查询;同时,该矩阵也作为表征解码器的监督目标,用于对其预测出的嵌入执行 ArcFace Loss监督。检测解码器输入的声纹嵌入和表征解码器输出的声纹嵌入为同个特征空间内联合优化所得。

为增强模型对新说话人的检测能力,训练中采用Masked Speaker Prediction机制:每个训练样本中会随机屏蔽一个已知说话人嵌入,使用一个可学习的“伪说话人嵌入”代替,并将其语音活动标签对应输出位置,促使模型学会发现“未注册说话人”的语音。另一方面,S2SND设计了Target-Voice Embedding Extraction机制:利用表征解码器从从多说话人混合音频中反向提取嵌入,实现检测与表征的闭环优化。整个框架端到端训练,联合优化语音活动预测的 BCE 损失与嵌入空间的 ArcFace 损失。 

图1. 序列到序列神经说话人日志(S2SND)框架示意图。其中,Det. 和 Rep. 分别表示检测(Detection)与表征(Representation)的缩写。

如图2所示,S2SND 支持低延迟的在线推理,采用块式(Blockwise)输入方式处理长音频。每次输入一个音频块,包含三部分:左上下文、当前块与右上下文,分别用于建模历史依赖、输出当前结果与提供未来参考。整个音频以滑动窗口方式逐块推进,当前块的结果实时输出,保证因果性。推理过程中,系统维护一个说话人嵌入缓冲区(Speaker-Embedding Buffer),用于存储当前已知说话人的嵌入表示。每一轮推理的输入说话人嵌入由三部分拼接组成:

  1. 固定在首位的训练过程中使用的伪说话人嵌入,用于检测是否有新说话人出现;
  2. 从缓冲区按顺序读取的已知说话人嵌入,用作特定人身份的参考信息;
  3. 用训练过程中使用的非语音嵌入作为填充,确保一个数据批次(Batch)的说话人数维度一致。

检测解码器以当前编码特征与上述嵌入序列为输入,预测每个说话人的语音活动(包括伪说话人);随后,表征解码器再以提取器特征与预测的语音活动作为输入,反向提取所有目标说话人的嵌入表示。模型通过评估每个预测出的语音活动的非重叠语音时长作为嵌入质量权重,对应生成的嵌入只有在超过设定阈值时才会写入缓冲区:

  1. 若伪说话人的语音活动质量满足阈值,说明存在新的未注册说话人,其嵌入将以新身份编号写入缓冲区;
  2. 若已有说话人的语音活动质量满足阈值,则对应嵌入将追加到该说话人的缓冲列表;
  3. 低质量嵌入则被舍弃,以避免污染已缓存的嵌入。

每次推理仅采用当前块对应的时间范围内的预测结果拼接到历史结果,确保输出的时间因果性。此外,当整个音频推理完成后,系统可利用缓冲区中聚集的高质量说话人嵌入,对全段音频进行一次离线重解码(Re-Decoding),进一步提升整体识别精度。由于模型结构在在线与离线模式下完全一致,无需额外转换或重新建模。

2. 序列到序列神经说话人日志(S2SND)推理流程图。其中,
Det. 和 Rep. 分别表示检测(Detection)与表征(Representation)的缩写。

流式推理伪代码:

Algorithm 1 Pseudocode of online inference in the Python-like style.
"""
- Extractor(), Encoder(), Det_Decoder(), Rep_Decoder(): neural network modules in S2SND models
- W(): calculating embedding weight
Inputs
- blocks: a sequence of input audio blocks
- e_pse/e_non: pseudo-speaker/non-speech embedding
- tau_1/tau_2: threshold for pseudo-speaker/enrolled-speaker embedding weight
- lc/lr: number of output VAD frames belonging to the current chunk / right context
- N: speaker capacity
- S: embedding dimension
Outputs
- dia_result: predicted target-speaker voice activities
- emb_buffer: extracted speaker embeddings
"""
dia_result = {}             # 初始化分离结果:字典,key=spk_id,value=拼接好的VAD帧序列
emb_buffer = {}             # 初始化“说话人嵌入缓冲区”:key=spk_id, value=[(embedding, weight), ...]
num_frames = 0              # 已经输出(拼接)的VAD帧计数,用于新出现的说话人补零对齐
for audio_block in blocks:  # 块式滑窗读取下一段音频(含左/当前/右三部分)

# 流式:逐块处理,保持低延迟与因果性。
    emb_list = [e_pse]                    # 输入给解码器的“说话人嵌入序列”首位固定放伪说话人嵌入
    spk_list = [len(emb_buffer) + 1]      # 并为它占一个新ID(“潜在新说话人”的候选ID)

# 伪说话人(pseudo-speaker):用于探测是否有新说话人。
# spk_list[0] 提前预留一个“新ID”,如果这次块里真的检测到新说话人且质量达阈值,就用这个 ID。

# 将每个“已知说话人”的汇总参考嵌入拼到 emb_list,与 spk_list 一一对应。这样解码器就能对“伪说话人 + 已知说话人们”同时进行语音活动预测。
    for spk_id in emb_buffer.keys():      # 遍历已知/已注册的说话人
        e_sum = torch.zeros(S)            # 为该说话人累积权重和
        w_sum = 0
        for e_i, w_i in emb_buffer[spk_id]:
            e_sum += w_i * e_i            # 质量加权的嵌入加和
            w_sum += w_i
        emb_list.append(e_sum / w_sum)    # 得到该说话人的“参考嵌入”(加权平均)
        spk_list.append(spk_id)           # 记录其对应的说话人ID(顺序与emb_list对应)

# 确保这次前向中,说话人维(batch)长度固定为 N,便于张量化与并行。
    while len(emb_list) < N:              # 若说话人槽位不够,用非语音嵌入 e_non 填满(batch 维度对齐)
        emb_list.append(e_non)

    emb_tensor = torch.stack(emb_list)    # 形状:N x S

# 提取器/编码器是声学前端,抽取时序表示。
X = Extractor(audio_block)            # 提取器:原波形 -> 帧级特征,形状 T x F
X_hat = Encoder(X)                    # 编码器:进一步编码,形状 T x D


# Y_hat[n]:第 n 个槽位(伪/已知/填充)的语音活动(帧级概率/0-1)。
#E_hat[n]:第 n 个槽位对应的说话人嵌入。
#两者联动:先“检测哪里在说话”,再“在这些区域提代表征”。
Y_hat = Det_Decoder(X_hat, emb_tensor)  # 检测解码器:对每个“说话人槽位”预测VAD,形状 N x T'
E_hat = Rep_Decoder(X, Y_hat)           # 表征解码器:反向提取每个槽位的嵌入,形状 N x S

y_pse = Y_hat[0]                       # 伪说话人的 VAD(T')
e_pse = E_hat[0]                       # 伪说话人的嵌入(S)
w_pse = W(y_pse)                       # 基于非重叠语音时长等指标计算质量权重(标量)

# 关键点:只拼当前块对应的 lc 帧,保证因果输出;右上下文只作参考不输出。
# 新说话人第一次出现,需要在历史时间线前边补零对齐
    if w_pse > tau_1:                      # 若伪说话人质量过阈,判定:出现新说话人
        elapsed_y = torch.zeros(num_frames)      # 为新ID补齐历史帧的0(过去都未说话)
        current_y = y_pse[-(lc+lr) : -lr]        # 只取当前块的有效帧(去掉右上下文)
        new_id = spk_list[0]                     # 取预留的新说话人ID
        dia_result[new_id] = torch.cat([elapsed_y, current_y])  # 拼成全局时间线
        emb_buffer[new_id] = [(e_pse, w_pse)]    # 把该新人的嵌入写入缓冲区


# 更正:循环上界不应是 len(S)(那是嵌入维),应该是 N 或 Y_hat.size(0)。
#已知说话人的 dia_result[spk_id] 直接在已有时间线上拼接本块的 lc 帧。
#当质量好时,往该说话人的 emb_buffer 追加一条 (embedding, weight),后续会参与加权平均,越滚越稳。
    for n in range(1, len(S)):             # 【这里有 Bug】应为 range(1, N) 或 Y_hat.shape[0]
        y_n = Y_hat[n]                     # 第 n 槽位的 VAD
        e_n = E_hat[n]                     # 第 n 槽位的嵌入
        w_n = W(y_n)                       # 质量
        spk_id = spk_list[n]               # 槽位对应的说话人 ID(已知)
        dia_result[spk_id] = torch.cat([dia_result[spk_id], y_n[-(lc+lr) : -lr]])  # 拼接本块有效帧
        if w_n > tau_2:                    # 若质量好,把新嵌入追加到该说话人的缓冲列表
            emb_buffer[spk_id].append((e_n, w_n))
    num_frames += lc                       # 全局时间线向前推进 lc 帧
#每处理完一个块,就“确认输出”了 lc 帧,时间线前移。

实验结果与性能分析

本研究构建了两类模型规模的 S2SND 系统:S2SND-Small 和 S2SND-Medium,最终模型参数量分别约为16+M和 46M+。为支撑大规模训练,我们分别采用了两个不同规模的说话人语料作为仿真素材。其中,VoxCeleb2数据库约 100 万条语音,覆盖 6112 名说话人;VoxBlink2数据库约 1000 万条语音,覆盖超过 11 万名说话人。此外,为提升中型模型对大语料的建模能力,本研究引入了以 ResNet-152 提取器作为教师模型的知识蒸馏策略,显著增强了说话人嵌入质量与下游推理性能。模型结构、训练细节与延迟设置等信息详见论文原文。所有实验均基于标准的 DER(Diarization Error Rate) 指标进行评估,不使用 Collar ,严格衡量系统在真实应用条件下的实际表现。同时,分别评测依赖和不依赖 Oracle VAD的情况,以满足和其他现有方法在不同评估标准下的公平对比。

如表1所示,在 DIHARD-II 测试集中,S2SND-Medium 模型结合 VoxBlink2 语料与知识蒸馏训练,在在线推理模式(0.64s 总延迟)下取得了 24.41% DER,刷新当前所有在线系统的最优结果。完成一次离线重解码后,DER 进一步降低至 21.95%,即便不依赖 Oracle VAD,也全面超越现有最强的 EEND-GLA 与 VBx 等方法。相比之下,传统 EEND-EDA + STB 方法的在线DER 仍在 30% 以上,EEND-GLA-Large 系统也仅在离线条件下达到 28.33%。S2SND 实现了更低延迟、更高准确率的双重突破。

1. S2SND 模型与其他方法在 DIHARD-II 测试集上的对比结果

如表2所示,在更新一代的 DIHARD-III 数据集上,S2SND 同样表现出色。在 0.80s 延迟设置下的在线模式,S2SND-Medium 达到 17.12% DER,明显优于各类在线系统。完成离线重解码后,DER 进一步降至 15.13%。值得一提的是,S2SND 的在线 DER 已逼近甚至优于许多离线系统(如 EEND-M2F 的 16.07%),体现出其在新说话人检测与时序建模方面的强大泛化能力。

2. S2SND 模型与其他方法在 DIHARD-III 测试集上的对比结果

如表2所示,在更新一代的 DIHARD-III 数据集上,S2SND 同样表现出色。在 0.80s 延迟设置下的在线模式,S2SND-Medium 达到 17.12% DER,明显优于各类在线系统。完成离线重解码后,DER 进一步降至 15.13%。值得一提的是,S2SND 的在线 DER 已逼近甚至优于许多离线系统(如 EEND-M2F 的 16.07%),体现出其在新说话人检测与时序建模方面的强大泛化能力。

除了识别“谁在什么时候说话”,说话人日志系统还应有效估计音频中“有多少人参与对话”。图3展示了不同系统在 DIHARD-III 测试集上的说话人数量预测混淆矩阵。相比 EEND 和聚类方法,S2SND 的预测结果更集中于主对角线,表明其在不同说话人数场景下具有更强的稳定性和泛化能力。这种能力得益于 S2SND 在训练阶段将说话人检测和表征联合优化的设计,使模型能够在无需先验聚类的情况下,自主发现新说话人的出现并持续跟踪,从而自然完成说话人数量的判断与更新。

图3. DIHARD-III 测试集上的说话人数量预测混淆矩阵。Pyannote.audio v3.1、VBx 和 DiaPer 的结果由各自作者提供。评估过程中未使用 Oracle VAD。

小米声音理解大模型 MiDashengLM-7B

首次提出了通过通用字幕进行音频文本对齐的方法,而无需依赖 ASR 或基于声音事件的模型:

  • 融合多模态音频描述:首次将语音转录文本、环境声音字幕和音乐字幕统一融合为一个通用的文本描述,打破了传统分别处理三类音频信息的局限。
  • 构建丰富语义的整体音频描述:通过统一的多模态文本表达,捕获更全面、更细致的音频场景信息,包括语义细节、环境声学特征等,而非仅仅停留在简单的标签或“有人讲话”等表面描述。
  • 促进音频与语言的深度对齐:提升音频理解模型对复杂、多样化音频内容的描述能力,推动声音和文本的跨模态融合与理解。
阶段一:使用ACAVCaps数据集进行全量训练,目标是训练一个encoder表征语音特征;阶段二:使用全量数据集,基于阶段一训练的encoder+初始化的LLM 进行lora微调;阶段三:使用任务均衡的高质量数据集进行SFT

MiDashengLM-7B 基于 Xiaomi Dasheng 作为音频编码器和 Qwen2.5-Omni-7B Thinker 作为自回归解码器,通过创新的通用音频描述训练策略,实现了对语音、环境声音和音乐的统一理解。

MiDashengLM 完整公开了 77 个数据源的详细配比,技术报告中详细介绍了从音频编码器预训练到指令微调的全流程。

MiDashengLM 集成了 Dasheng —— 一个专为高效处理多样化音频信息而设计的开源音频编码器。与以往主要依赖自动语音识别(ASR)进行音频与文本对齐的方法不同,该策略以通用音频字幕为核心,将语音、声音与音乐信息融合为统一的文本表达,从而能够对复杂音频场景进行整体性描述。

Motivation:

当前的音频理解研究通常将语音转录、音频字幕和音乐字幕分别处理。这种独立处理的方法限制了对听觉场景分析的深度和完整性。另一个关键限制来自现有的音频字幕,这些字幕往往只提供表面性的描述。例如,语音内容常被简化为“有人在说话”,忽略了语义细节。此外,这些数据集通常未能捕捉诸如房间声学特性(如混响)或信号质量等关键听觉要素

为克服这些限制,本文提出将语音转录、音频字幕和音乐字幕融合为一个统一的通用字幕。我们的目标是构建一个整体的文本表示,联合涵盖所有相关的音频信息,从而提供对听觉环境更为详细且语义丰富的描述。

基于 ASR 的预训练对于一般的音频语言理解而言,其效果有限,原因如下:

  • 数据利用效率低:仅使用ASR数据导致大量潜在有价值的数据被丢弃,如音乐、环境噪声,甚至静默片段。
  • 目标过于简单:基于文本的训练相比,模型从ASR数据中学到的有意义信息相对较少,型仅需建立局部对应关系,将口语词与文本对应,而无需理解更广泛的(全局)音频上下文。
  • 基于ASR预训练的局限性:超越语音内容。基于ASR的预训练主要关注语音内容,忽视了其他信息。这导致重要的语音元信息,如说话者的性别、年龄或情绪状态,无法在预训练过程中被捕获或整合。

Framework

图 2: MiDashengLM 框架提案。三个阶段均采用标准下一个标记预测损失进行训练。阶段 1 将音频编码器与文本模态对齐,之后提取音频编码器并进行初始化,以用于阶段 2。

当前,绝大多数工作都使用预训练的音频编码器,本文首次提出了通过通用字幕进行音频文本对齐的方法,而无需依赖 ASR 或基于声音事件的模型。此外,我们仅使用一个能够同时处理语音、声音和音乐的通用音频编码器。

架构设计:基于通用前缀的大型语言模型,通过多层感知机(MLP)将音频编码器的特征映射到语言模型的嵌入空间,实现音频与文本的对齐。

数据使用:只采用公开可用的音频-文本数据进行预训练和微调,数据来源多样且公开透明。

音频编码器选择:首次提出利用单一通用音频编码器联合处理语音、环境声音和音乐,无需依赖ASR或特定声音事件模型。

训练效率优化:支持可变长度输入,避免了大量无效的填充,提高计算效率;同时通过将音频序列下采样到5Hz,实现快速训练和推理。

Datasets

MiDashengLM 在预训练和监督微调阶段仅基于公开数据集进行训练。

ACAVCaps:基于ACAV100M制作通用字幕数据

字幕数据集不足,主要是因为缺乏语音理解,且其数据来源单一,主要来自 Audioset 、VGGSound  和 FSD50k 。就我们的目的而言,需要一个公开可用且内容丰富的数据集,包含多语言语音、不同类型的音乐和大量复杂的音频环境。我们认为 ACAV100M [https://acav100m.github.io/]是一个合理的候选源数据集,因为它之前没有被标记为用于音频字幕,并且与前面提到的数据集几乎没有重叠。

ACAV100M

由于 ACAV100M 缺乏标签,我们开发了一套高效的数据管理流程。我们首先使用 CED-Base 以 2 秒为单位预测 AudioSet 标签。我们使用这个更精细的 2 秒尺度,使我们的字幕能够捕捉时间关系。获得声音事件标签后,我们进一步使用大量不同的音频分类模型来处理数据,每个模型都针对特定任务量身定制。

  • 语音分析:该任务识别口语内容,区分不同说话者,对音频进行说话人分割(说话人分离),检测语音情感,分类说话者的性别和年龄,并使用Whisper推断转录文本。
  • 声纹分析:除了基本的语音内容外,该任务进一步细化声音情感检测,评估声音健康状况,分析音高和音色等独特声学特征。
  • 音乐分析:针对音乐内容,模型进行音乐风格分类、乐器识别、节奏检测、音乐情绪分析以及歌唱声音识别。
  • 环境声学:该流程部分对声学场景进行分类,评估音频质量,分析混响情况,并识别各种噪声类型。
数据整理流程。我们使用自动化流程过滤 ACAV100M,该流程可预测文本、声音事件、音质和其他元信息。然后,使用推理 LLM 根据提供的元信息生成字幕。最终整理的数据集被拆分为训练集 (ACAVCaps) 和全新的评估集——音频任务多专家链 (MECAT)。
通过pipline 合成的部分样例

在获得上述所有标签后,我们使用推理型大型语言模型(DeepSeek-R1)生成简短的音频字幕。生成的精编音频字幕数据集随后被划分为训练集(ACAVCaps)和测试集(用于细粒度音频理解任务的多专家构建基准,MECAT)。MECAT测试集是从精编数据集中筛选而来,首先依据视频授权类型过滤各来源视频,最终通过GLAP 方法对音频-文本一致性进行评分。

MECAT-QA:

MECAT-QA 中,每段音频配备了五个问答对,涵盖不同类别和难度等级,总计超过10万个问答对。问答任务分为三个主要认知类别:

a) 感知(Perception):包含一个子类别——直接感知(Direct Perception),侧重于对音频内容和事件的直接识别与命名。

b) 分析(Analysis):由两个子类别组成——声音特征(Sound Characteristics),用于考察声音的声学属性(如音高);质量评估(Quality Assessment),用于评估音频的技术质量(如噪声水平)。

c) 推理(Reasoning):涵盖更高级的认知能力,分为三个子类别——环境推理(Environment Reasoning),需要推断声音发生的声学场景;推断与判断(Inference & Judgement),基于音频内容进行逻辑推理和判断;应用场景(Application Context),测试对声音实际用途或情境的理解。

Training datasets and tasks

总计约110万小时的数据。其中约90%的训练数据来自公开的自动语音识别(ASR)数据集,其余数据集规模明显较小。如果不加以合理处理,模型在除ASR以外的任务上表现可能不足。数据采样情况如图6所示。

在音频-文本对齐阶段,我们使用前文介绍的ACAVCaps数据集,该数据集包含3.8万小时高质量的通用字幕。我们在ACAVCaps上训练3个epoch,以实现音频编码器与文本的对齐。

完成对齐后,我们对MiDashengLM模型在全量110万小时训练数据上进行预训练,约训练1.4个epoch。预训练结束后,在一个精选的预训练子集上进行一次监督微调,子集总时长约35.2万小时。

模型和三阶段训练:

音频编码器基于 Dasheng-0.6B构建,是一个帧级的视觉Transformer(ViT),使用掩码自动编码器(MAE)目标在主要的 ACAV100M 数据集上进行预训练。

MiDashengLM 仅支持16 kHz的音频输入,所有输入数据都会自动重采样到此采样率。音频波形被转换成64维的梅尔频谱图(mel-spectrogram),Dasheng-0.6B 通过提取32毫秒帧特征,步幅为10毫秒来处理这些频谱图。默认情况下,Dasheng 会将输入特征进一步下采样4倍,产生40毫秒间隔的高级特征。Dasheng 支持可变长度输入,最大输入长度为1008帧(约10.08秒)。对于超过该长度的输入,我们采用无重叠滑动窗口方法,将每个音频片段分别输入 Dasheng,随后拼接得到的帧级特征。

Audio-text alignment

MiDashengLM 的第一步是将音频编码器与文本数据对齐。在此对齐阶段,我们使用 ACAVCaps 数据集,对音频编码器和文本解码器组件进行端到端微调。对齐之后,我们提取训练好的音频编码器,以便在后续的预训练 SFT 阶段进行初始化。在模型开发期间,我们根据经验评估了两种替代方法:(1) 与冻结的大型语言模型 (LLM) 集成和 (2) 低秩自适应 (LoRA)。然而,这两种方法的音频编码器性能都不令人满意。音频文本对齐在 8 个 GPU 上以 256 的有效批次大小运行了一天。

解码器:

文本解码器使用 Qwen2.5-Omni-3B (一个公开的预训练语言模型)进行初始化。在预训练和监督微调阶段,我们都采用了 LoRA 来提升参数效率。训练目标是最小化标准交叉熵损失:

Training :

所有训练过程均采用线性学习率预热(warm-up),持续前1000次迭代,期间学习率从零线性上升至目标值。随后,学习率遵循余弦衰减策略,训练结束时降低至最大值的10%。预训练与监督微调(SFT)之间的主要区别包括:(1)SFT阶段采用较低的学习率;(2)通过LoRA技术扩展了可训练参数。

Results

音频编码器性能:

为了评估我们用通用音频字幕训练的音频文本对齐框架,我们将生成的音频编码器与 Whisper-Large V3 进行了比较。我们采用 X-Ares 基准 ,该基准通过轻量级 MLP 层评估冻结的编码器嵌入,涵盖三个核心音频领域:语音、音乐和(环境)声音。

基于 Dasheng 的编码器在各种音频分类任务中表现出色。比较分析表明,虽然 Whisper-Large v3 在 22 个任务中的 4 个任务上取得了更好的结果,但我们的编码器在其余 18 个任务上优于 Whisper。在某些严格与语音相关的任务上,如自动语音识别(ASR,WER 提高约 5%)、说话人数统计(LibriCount)、口语语言识别(VoxLingua33)以及关键词检测(Speech Commands V1),Whisper 的表现优于我们提出的编码器。

传统数据集基准

模型配置介绍:

Audio captioning results:

在音乐和音频(声音)字幕数据集上,MiDashengLM 的表现均优于基线模型。对于通用音频,性能提升尤为显著,我们的模型在 AutoACD 上的表现显著优于基线模型,而在音乐专用基准测试中,其性能提升则较为平缓。

 音频和副语言分类

在十项测试任务中的表现表明,MiDashengLM 在说话人验证(VoxCeleb1)、语言识别(VoxLingua107)、声音分类(VGGSound、FSD50k)和音乐分类(NSynth、FMA)方面的表现优于基准。

Automatic speech recognition:

音频令牌的帧率会显著影响 ASR 性能,更高的帧率会提高性能,但计算效率会有所下降。这些结果与我们之前发现一致,表明我们的编码器仍然落后于闭源 Whisper 模型——两个基线系统都采用的音频编码器。由于 MiDashengLM 首先是一个字幕模型,因此它的 ASR 性能在传统 LibriSpeech 数据集上与基线相比有所下降。然而,在更大的测试集(如 People’s Speech)上的性能优于 Qwen2.5-Omni 基线。Kimi-Audio 在英语和普通话语音识别方面表现最佳,这可能源于它使用大量英语和中文 ASR 数据进行预训练。然而,MiDashengLM 和 Qwen2.5-Omni 均能够对印尼语、越南语和泰语等不同语言进行自动语音识别。这表明,尽管我们的编码器没有经过专门的语音训练,却拥有令人惊讶的强大多语言能力。

问答结果:

MiDashengLM 在这些任务中保持领先地位,而 Kimi-Audio-Instruct 的性能最弱

Inference speed

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 数据上训练的统一系统覆盖语音、音乐与声音事件。同时,该模型采用简单的非扩散式编码器/解码器,实现快速批量推理

解析模块

  • 语义教师模型 (Semantic Teacher): 生成语义表示 S,用于指导语义编码器提取语义信息;
  • 语义编码器 (Semantic Encoder): 接收语义表示 S,提取语义特征 hS ;
  • 声学编码器 (Acoustic Encoder): 直接从输入音频 X 中提取声学特征 hA ;
  • 特征组合 (Concatenation): 将语义特征 hS 和声学特征 hA 进行特征组合,形成联合特征表示;
  • Dense 层: 对联合特征进行非线性变换,生成预量化特征 hpre ;
  • 残差向量量化 (RVQ): 对预量化特征进行hpre 量化,生成量化后的特征 hpost,并产生多个量化码本 Q1,Q2,…,Qm ;
  • Dense 层: 对量化后的特征进行非线性变换hpost,生成用于解码的特征表示;

解码过程:

  1. 语义解码器 (Semantic Decoder): 使用处理后的量化特征重建语义表示 S^ ;
  2. 声学解码器 (Acoustic Decoder): 使用处理后的量化特征重建音频信号 X^ ;

具体流程:

  1. 特征提取: 原始音频 X 输入后,通过语义教师模型生成语义表示 S,然后通过语义编码器和声学编码器分别提取语义特征和声学特征;
  2. 特征组合与量化: 提取的语义和声学特征进行组合,经过非线性变换后进行残差向量量化,得到量化后的特征表示;
  3. 解码与重建: 量化后的特征分别输入语义解码器和声学解码器,重建语义表示 S^ 和音频信号 X^ ;
  4. 损失计算与优化: 通过计算语义损失和声学损失,优化模型的参数,使重建的语义和音频尽可能接近原始输入;

音频分词器性能:

整体架构

HiggsAudio-V2 模型基于大型语言模型(LLM),并集成了音频适配器(Audio Adapter)和音频解码器(Audio Decoder),用于处理音频输入和输出。

模型基于 Llama-3.2-3B 构建。为了增强模型处理音频 token 的能力,引入了“DualFFN”架构作为音频 adapter。DualFFN 充当音频专家,以最小的计算开销提升 LLM 的性能。通过引入 DualFFN,我们的实现保留了原始 LLM 91% 的训练速度。

组件组成

  • Text Branch(浅蓝色):处理文本输入。
  • Understanding Variant(浅蓝色):用于理解文本和音频输入。
  • Generation Variant(黄色):用于生成文本和音频输出。
  • Audio Adapter – Dual FFN(虚线框):专门设计用于处理音频令牌的模块,包含两个前馈网络(FFN)和多头自注意力(MHA)模块。

文本输入

  • 文本输入通过 Text Tokenizer 转换为文本令牌(Text Token)。
  • 文本令牌通过 Text Branch 进行处理。

音频输入

  • 音频输入通过 Audio Tokenizer 转换为音频令牌(Audio Token)。
  • 音频令牌通过 Understanding Variant 进行处理。
  • 音频输入还通过 Semantic Encoder 提取语义信息。

文本和音频融合

  • 文本和音频令牌在 LLM 中进行融合处理。
  • Understanding Variant 和 Generation Variant 分别负责理解和生成任务。

音频适配器(Dual FFN)

  • Audio Adapter 包含两个并行的前馈网络(FFN),分别处理文本和音频特征。
  • 每个 FFN 之后都有一个归一化层(Norm)。
  • 处理后的特征通过多头自注意力(MHA)模块进行进一步处理。
  • 最终,处理后的文本和音频特征在音频适配器中融合。

为了提升模型处理音频令牌的能力,HiggsAudio-V2 引入了 “DualFFN” 架构作为音频适配器。DualFFN 作为音频专家模块,可以显著提升模型在音频任务上的性能,同时保持较低的计算开销。具体来说,DualFFN 在音频令牌处理过程中提供了额外的处理能力,使模型能够更有效地理解和生成音频数据。

延迟模式(Delay Pattern)

由于音频令牌化过程中涉及多个代码本,HiggsAudio-V2 采用了延迟模式(delay pattern)来实现跨代码本的并行代码生成。该模式通过在不同代码本之间引入偏移量,使得模型能够在保持音频质量的同时支持流式处理。延迟模式允许模型在生成音频时,同时处理多个代码本中的令牌,从而提高了生成效率。

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

由于版权问题,沐导没有使用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 模型经常无法提供准确的结果或给出错误的结果。此外,目前用于音频生成的开源视音频数据集在多样性和覆盖范围方面存在局限性,可能缺少稀有或特定文化的声音事件。未来,我们将继续探索更加多样化和全面的数据集,以提升模型的性能。此外,我们还将探索更有效的方法来提升生成音频的时间和空间对齐效果。