Qwen2.5-Omni -多模态端到端大模型

这是 Qwen 系列中全新的旗舰级多模态大模型,专为全面的多模式感知设计,可以无缝处理包括文本、图像、音频和视频的各种输入,同时支持流式的文本生成和自然语音合成输出

主要特点

  • 全能创新架构:我们提出了一种全新的Thinker-Talker架构,这是一种端到端的多模态模型,旨在支持文本/图像/音频/视频的跨模态理解,同时以流式方式生成文本和自然语音响应。我们提出了一种新的位置编码技术,称为TMRoPE(Time-aligned Multimodal RoPE),通过时间轴对齐实现视频与音频输入的精准同步。
  • 实时音视频交互:架构旨在支持完全实时交互,支持分块输入和即时输出
  • 自然流畅的语音生成:在语音生成的自然性和稳定性方面超越了许多现有的流式和非流式替代方案。Qwen2.5-Omni 支持修改输出语音的音色类型,目前支持2种音色类型。
  • 全模态性能优势:在同等规模的单模态模型进行基准测试时,表现出卓越的性能。Qwen2.5-Omni在音频能力上优于类似大小的Qwen2-Audio,并与Qwen2.5-VL-7B保持同等水平。
  • 卓越的端到端语音指令跟随能力:Qwen2.5-Omni在端到端语音指令跟随方面表现出与文本输入处理相媲美的效果,在MMLU通用知识理解和GSM8K数学推理等基准测试中表现优异。

摘要:

Qwen2.5-Omni,这是一种端到端的多模态模型,能够感知多种模态信息,包括文本、图像、音频和视频,同时以流式方式生成文本和自然语音响应。为了实现多模态信息输入的流式处理,Qwen2.5-Omni 的音频和视觉编码器采用了 分块处理(block-wise processing) 方法。该策略有效地解耦了长序列多模态数据的处理,将感知任务交由多模态编码器,而将长序列建模任务交由大语言模型(LLM),这种分工机制通过共享注意力机制增强了不同模态的融合。

为同步视频输入的时间戳与音频,我们采用 交错(interleaved) 方式对音视频数据进行序列化,并提出了一种新颖的位置编码方法——TMRoPE(Time-aligned Multimodal RoPE,时间对齐多模态旋转位置编码)ps:关于旋转位置编码

在同时生成文本和语音的过程中,为了避免两种模态之间的相互干扰,我们提出了 Thinker-Talker 架构。在该框架下:

  • Thinker 作为大语言模型,负责文本生成;
  • Talker 是一个 双轨自回归模型(dual-track autoregressive model),它直接利用 Thinker隐藏表示来生成音频标记(audio tokens)作为输出

ThinkerTalker 均以端到端方式进行训练和推理。此外,为了实现流式音频标记解码,我们引入了 滑动窗口 DiT(sliding-window DiT),通过限制感受野来减少初始数据包延迟。

Qwen2.5-Omni 的关键特性可总结如下:

  • Qwen2.5-Omni 是一个 统一多模态模型,能够感知所有模态信息,并以流式方式同时生成文本和自然语音响应。
  • 我们提出了一种新颖的位置编码算法 TMRoPE(Time-aligned Multimodal RoPE),该方法显式融入时间信息,以实现音视频的同步。
  • 我们设计了 Thinker-Talker 架构,以支持 实时理解语音生成
  • 在多模态基准测试中,Qwen2.5-Omni 展示了卓越的性能,相较于类似规模的单模态模型表现更优,尤其在语音指令跟随任务上,其能力可与纯文本输入任务相媲美。
  • 在需要整合多种模态的信息处理任务中(如 OmniBench 评测),Qwen2.5-Omni 达到了 最先进(state-of-the-art) 的性能。
  • 在语音生成方面,Qwen2.5-Omniseed-tts-eval 评测中表现出色,展现出强大的语音生成能力和稳健性。
Thinker 负责 文本生成
Talker 通过直接接收 Thinker 提供的高级表示,专注于 流式语音标记生成

Architecture:

图 2 所示,Qwen2.5-Omni 采用 Thinker-Talker 架构,其中:

  • Thinker 类似于大脑,负责处理和理解 文本、音频、视频 等模态输入,生成高级表示和对应的文本输出。
  • Talker 类似于人类的嘴巴,以流式方式接收 Thinker 生成的高级表示和文本,并顺畅地输出离散语音标记。

架构细节

  • Thinker 采用 Transformer 解码器,并配备 音频编码器图像编码器,以增强信息提取能力。
  • Talker 采用 双轨【同时接收文本token和语音token】自回归 Transformer 解码器 结构(受 Mini-Omni [Xie & Wu, 2024] 启发)。
  • 训练和推理 过程中,Talker 直接接收 Thinker 的高维表示,并共享其全部历史上下文信息,使整个架构作为一个统一模型进行 端到端训练和推理

感知:

Qwen2.5-Omni 通过 Thinker文本、音频、图像和视频(无音频) 进行处理,将它们转化为一系列隐藏表示作为输入。具体步骤如下:

  1. 文本输入
    • 对于文本输入,我们采用 Qwen 的分词器(Byte-level Byte-pair Encoding),词汇表包含 151,643 个常规标记。
  2. 音频输入和视频中的音频
    • 音频输入(包括视频中的音频部分)首先被重采样至 16kHz 的频率,然后将原始波形转化为 128 通道的梅尔频谱图(mel-spectrogram),窗口大小为 25ms,步幅为 10ms
    • 音频编码器采用 Qwen2-Audio 的音频编码器(Chu et al., 2024b),每一帧音频表示大约对应于 原始音频信号的 40ms 时长。
  3. 图像和视频输入
    • 对于图像输入,我们采用 Qwen2.5-VL 的视觉编码器(基于 Vision Transformer(ViT) 模型,约 6.75 亿个参数),能够有效处理图像和视频输入。
    • 视觉编码器使用混合训练方法,结合图像和视频数据,确保其在图像理解和视频理解上的高效表现。
    • 为了最大程度地保留视频信息并适应音频采样率,我们采用 动态帧率(dynamic frame rate)进行视频采样。此外,为保持一致性,每个图像都被视为两个相同的帧。

视频与TMRoPE

TMRoPE

我们提出了一种 音视频时间交错算法(time-interleaving),并引入了新的 位置编码方法 —— TMRoPE(Time-aligned Multimodal RoPE)。如 图 3 所示,TMRoPE 编码了多模态输入的三维位置,采用 多模态旋转位置编码(M-RoPE)ps: Qwen2-VL多模态旋转位置编码 多模位置编码】,并结合绝对时间位置。具体方法是将原始的旋转位置编码分解为三个组件:时间、图像高度和宽度。

  • 文本输入 使用相同的位置信息(位置 ID)来处理各组件,使得 M-RoPE 在文本输入中与 1D-RoPE 等效。
  • 音频输入 也使用相同的位置信息,并引入绝对时间位置编码,每 40ms 对应一个时间 ID
  • 图像输入,每个视觉标记的时间 ID 保持不变,而高度和宽度组件则根据标记在图像中的位置分配不同的 ID。
  • 音视频输入 情况下,音频依然使用相同的时间位置 ID 编码,每帧 40ms。视频则作为一系列图像处理,每帧对应的时间 ID 增量,同时高度和宽度组件的 ID 分配与图像一致。由于视频的帧率不是固定的,我们根据每帧实际时间动态调整时间 ID,以确保每个时间 ID 对应 40ms。

在多模态输入场景下,每种模态的位置信息初始化时会将前一模态的最大位置 ID 增加 1。

视频与音频时间交错方法

为了使模型能够同时接收视觉和听觉信息,我们采用 时间交错方法(time-interleaving) 对带有音频的视频进行特殊设计。具体做法是:

  • 将视频的音频表示按 实际时间2 秒 切分为块。
  • 在每个 2 秒块中,先安排视频的视觉表示,再安排音频表示,从而将视频和音频的表示交错排列。

生成:

文本生成由 Thinker 直接生成,其逻辑与广泛使用的大型语言模型(LLM)相同,文本通过基于词汇表的概率分布进行自回归采样生成。生成过程中可能会采用一些技术,如 重复惩罚(repetition penalty)top-p 采样以提高文本生成的多样性

语音生成中,Talker 接收 Thinker 生成的高维表示和采样的文本标记高维表示和离散采样标记的结合是这个过程中的关键。作为流式算法,语音生成需要在整个文本完全生成之前预测文本的语气和态度。Thinker 提供的高维表示隐含了这些信息,使得语音生成过程更自然。此外,Thinker 的表示主要体现语义相似性而非语音相似性,因此,即使是发音上差异较大的词,其高维表示可能非常相似,这就需要输入离散的采样标记来消除这种不确定性。

我们设计了一个高效的语音编解码器 qwen-tts-tokenizer,它能够高效地表示语音的关键信息,并通过因果音频解码器流式解码成语音。接收到信息后,Talker 开始自回归地生成音频标记和文本标记语音生成过程中不需要与文本进行逐字和逐时间戳的对齐,这大大简化了训练数据的要求和推理过程。

流式设计:

在音频和视频流式交互的背景下,初始包延迟是衡量系统流式性能的关键指标。这个延迟受到多个因素的影响:1)多模态信息输入处理引起的延迟;2)从接收到第一个文本输入到输出第一个语音标记之间的延迟;3)将第一个语音段转换为音频的延迟;4)架构本身的固有延迟,这与模型大小、计算 FLOP 数以及其他因素相关。本文将随后讨论在这四个维度上减少这些延迟的算法和架构改进。

支持预填充(Support Prefilling)

块状预填充(Chunked-prefills) 是现代推理框架中广泛使用的一种机制。为了支持模态交互中的预填充机制,我们修改了音频和视觉编码器,以支持沿时间维度的 块状注意力(block-wise attention)。具体而言,音频编码器从对整个音频的全局注意力改为对每个 2 秒 的音频块进行注意力计算。视觉编码器则使用 Flash Attention 来实现高效的训练和推理,并通过一个简单的 MLP 层 将相邻的 2×2 标记合并为一个标记。补丁大小设置为 14,允许不同分辨率的图像被打包成一个序列。

流式编解码器生成(Streaming Codec Generation)

为了促进音频的流式传输,特别是对于长序列的流式处理,我们提出了一种 滑动窗口块注意力机制(sliding window block attention),该机制限制了当前标记访问的上下文范围。具体来说,我们采用了 Flow-MatchingDiT 模型。输入的code通过 Flow-Matching 转换为 梅尔频谱图(mel-spectrogram),然后通过修改后的 BigVGAN 将生成的梅尔频谱图重建回波形。

DiT中用于编解码到波形生成的滑动窗口块注意力机制的示意图

预训练

Qwen2.5-Omni 由三个训练阶段组成。在第一阶段,我们锁定大型语言模型(LLM)的参数,专注于训练视觉编码器和音频编码器,利用大量的音频-文本和图像-文本对来增强 LLM 的语义理解能力。在第二阶段,我们解冻所有参数,并使用更广泛的多模态数据进行训练,以实现更全面的学习。在最后阶段,我们使用长度为 32k 的数据来提升模型理解复杂长序列数据的能力。

该模型在一个多样化的数据集上进行预训练,数据类型包括图像-文本、视频-文本、视频-音频、音频-文本和文本语料库。我们将层次标签替换为自然语言提示,遵循 Qwen2-Audio(Chu et al., 2024a)的方法,这可以提高模型的泛化能力和指令跟随能力。

在初始预训练阶段,Qwen2.5-Omni 的 LLM 组件使用 Qwen2.5(Yang et al., 2024b)中的参数初始化,视觉编码器与 Qwen2.5-VL 相同,音频编码器则使用 Whisper-large-v3(Radford et al., 2023)初始化。两个编码器分别在固定的 LLM 上进行训练,最初都专注于训练各自的适配器,然后再训练编码器。这个基础训练对装备模型具有坚实的视觉-文本和音频-文本关系和对齐的理解至关重要。

预训练的第二阶段标志着一个重要的进展,它增加了 8000 亿个图像和视频相关的数据标记,3000 亿个音频相关的数据标记,以及 1000 亿个视频带音频相关的数据标记。这一阶段引入了更多的混合多模态数据和更广泛的任务,增强了听觉、视觉和文本信息之间的互动,并加深了理解。加入多模态、多任务数据集对于培养模型同时处理多任务和多模态的能力至关重要,这是一项处理复杂现实世界数据集的关键能力。此外,纯文本数据在保持和提高语言能力方面也起着重要作用。

为了提高训练效率,我们在之前的阶段将最大标记长度限制为 8192 个标记。随后,我们引入了长音频和长视频数据,并将原始文本、音频、图像和视频数据扩展到 32,768 个标记进行训练。实验结果表明,我们的数据在支持长序列数据方面取得了显著的改进。

Post-training

数据格式:

Thinker

在后训练阶段,我们采用 ChatML 格式(OpenAI, 2022)进行指令跟随数据的微调。我们的数据集包括纯文本对话数据、视觉模态对话数据、音频模态对话数据以及混合模态对话数据。

Talker

我们为 Talker 引入了一个三阶段训练过程,使 Qwen2.5-Omni 能够同时生成文本和语音响应。在第一阶段,我们训练 Talker 学习上下文延续。在第二阶段,利用 DPO(Rafailov et al., 2023)增强语音生成的稳定性。在第三阶段,我们应用了多语者指令微调,以提高语音响应的自然性和可控性。

上下文学习(ICL) 训练阶段,除了像 Thinker 那样使用文本监督外,我们还通过下一标记预测执行语音延续任务,利用包含多模态上下文和语音响应的广泛对话数据集。Talker 学会了从语义表示到语音的单调映射,同时获得了根据上下文生成具有多样化属性(如韵律、情感和口音)的语音的能力。此外,我们还实施了音色解耦技术,以防止模型将特定的声音与不常见的文本模式关联。

为了扩大语者和场景的覆盖范围,预训练数据不可避免地包含标签噪声和发音错误,这可能导致模型产生幻觉。为了解决这个问题,我们引入了强化学习阶段来提高语音生成的稳定性。具体来说,对于每个请求和响应文本与参考语音配对的情况,我们构建了一个数据集 D,其中包含三元组数据 (x, yw, yl),其中 x 是输入序列的输入文本,ywyl 分别是良好和不良生成的语音序列。我们根据这些样本的奖励分数进行排名,奖励分数与 词错误率(WER)标点停顿错误率 相关。

最后,我们对上述基础模型进行了语者微调,使 Talker 能够采用特定的声音并提高其自然性。

Performance  

我们对 Qwen2.5-Omni 进行了全面评估,与类似大小的单模态模型和 Qwen2.5-VL-7B、Qwen2-Audio 和 Gemini-1.5-pro 等闭源模型相比,该模型在所有模态中均表现出色。在需要集成多种模态的任务(例如 OmniBench)中,Qwen2.5-Omni 实现了最佳性能。此外,在单模态任务中,它在语音识别(Common Voice)、翻译(CoVoST2)、音频理解(MMAU)、图像推理(MMMU、MMStar)、视频理解(MVBench)和语音生成(Seed-tts-eval 和主观自然度)等领域表现出色。

Paraformer-时间戳模型

参考论文:

  • FunASR: A Fundamental End-to-End Speech Recognition Toolkit   
  • 如图2(a)所示。Paraformer是一个单步非自回归(NAR)模型,结合了基于语言模型的快速采样模块,以增强NAR解码器捕捉标记之间依赖关系的能力。

    Paraformer由两个核心模块组成:预测器和采样器。预测器模块用于生成声学嵌入,捕捉输入语音信号中的信息。在训练过程中,采样器模块通过随机替换标记到声学嵌入中,结合目标嵌入生成语义嵌入。这种方法使得模型能够捕捉不同标记之间的相互依赖关系,并提高模型的整体性能。然而,在推理过程中,采样器模块处于非激活状态,声学嵌入仅通过单次传递输出最终预测结果。这种方法确保了更快的推理时间和更低的延迟。

    Timestamp Predictor:

    准确的时间戳预测是 ASR 系统的关键功能。然而,传统的工业 ASR 系统需要额外的混合模型来进行力对齐 (FA) 以实现时间戳预测 (TP),从而增加计算量和时间成本。FunASR 提供​​了一个端到端的 ASR 模型,通过重新设计 Paraformer 预测器的结构来实现准确的时间戳预测,如图2 (b) 所示。我们引入了一个转置卷积层和 LSTM 层来对编码器输出进行上采样,并通过后处理 CIF 权重 α2 来生成时间戳。我们将两个fireplaces 之间的帧视为前一个标记的持续时间,并根据α₂标出静音部分。此外,FunASR还发布了一个类似强制对齐的模型TP-Aligner,该模型包括一个较小的编码器和一个时间戳预测器。它接受语音和相应的转录作为输入,以生成时间戳。

    我们在AISHELL和60,000小时工业数据上进行了实验,以评估时间戳预测的质量。用于衡量时间戳质量的评估指标是累积平均偏移(AAS)。我们使用了一个包含5,549个手动标记时间戳的测试集,将提供的模型与使用Kaldi训练的FA系统进行时间戳预测性能比较。结果显示,Paraformer-TP在AISHELL上优于FA系统。在工业实验中,我们发现提出的时间戳预测方法在时间戳准确性方面与混合FA系统相当(差距小于10毫秒)。此外,这种单次解决方案对于商业使用非常有价值,因为它有助于减少计算和时间开销。

    语音理解模型—OSUM

    OSUM: Advancing Open Speech Understanding Models with Limited Resources in Academia

    大型语言模型(LLMs)在各种下游任务中取得了显著进展,启发了业界对语音理解语言模型(speech understanding language models, SULMs)的研发,以期实现基于语音情感、性别等副语言的高表现力交互。然而,大多数先进的SULM是由行业头部公司开发的,消耗大规模的数据和计算资源。而这些资源在学术界并不容易获得。此外,虽然训练好的模型和推理代码被开源了,但训练框架和数据处理流程依然缺乏透明度,这也为进一步研究产生了障碍。在本研究中,我们提出了OSUM,一个开放的语音理解模型,旨在探索在有限的学术资源下训练SLUM的潜力。OSUM模型将Whisper编码器与Qwen2 LLM相结合,支持广泛的语音任务,包括语音识别(ASR)、带时间戳的语音识别(SRWT)、语音事件检测(VED)、语音情感识别(SER)、说话风格识别(SSR)、说话者性别分类(SGC)、说话者年龄预测(SAP)和语音转文本聊天(STTC)。通过采用ASR+X训练策略,OSUM通过同时优化模态对齐和目标任务,实现了高效稳定的多任务训练。除了提供强大的性能,OSUM还强调透明度,提供公开可用的代码,并详细介绍了数据处理流程,以期为学术界提供有价值的参考,旨在加速先进SULM技术的研究和创新。

    方案设计 

    OSUM模型将Whisper编码器与Qwen2 LLM相结合,支持广泛的语音任务,包括语音识别(ASR)、带时间戳的语音识别(SRWT)、语音事件检测(VED)、语音情感识别(SER)、说话风格识别(SSR)、说话者性别分类(SGC)、说话者年龄预测(SAP)和语音转文本聊天(STTC)。通过采用ASR+X训练策略,OSUM通过同时优化模态对齐和目标任务,实现了高效稳定的多任务训练。

    模型结构

    模型的输入包括语音和自然语言提示。不同于 Whisper 和Qwen-Audio 依靠指令标签,Osum采用描述性文本,将所有八个支持任务转换为图2所示。当前,我们的模型仅支持基于文本的响应,但是音频输出功能正在积极开发。

    如图2所示,OSUM模型由一个Speech Encoder、一个Adaptor和一个LLM组成。在训练过程中,Speech Encoder和Adaptor中的所有参数都会更新,而大语言模型则使用LoRA方法进行微调。各部分具体配置如下:

    • Speech Encoder: Whisper-Medium (769M);
    • Adaptor: Conv1D * 3 + Transformer * 4,4倍下采样;
    • LLM: Qwen2-7B-Instruct带LoRA。LoRA hyperparameters-α, rank, and dropout ratio are set to 32, 8, and 0.1,

    多任务监督训练

    训练过程包括两个阶段:

    首先,在没有LLM的情况下,对原始的Whisper模型进行多任务监督微调,多任务数据微调了 Whisper ,以确保OSUM模型的更快收敛。此外,此阶段使我们能够验证多任务数据的可靠性。具体来说,我们扩展了Whisper的指示标签,以适应更多的任务,每个前向推理仅执行一个任务。

    其次,将微调后的Whisper编码器与Qwen2大语言模型相结合,构建出完整的OSUM系统,然后使用更大的数据集进行进一步的监督训练。

    OSUM模型的输入包括一段语音和一个自然语言描述的prompt,而输出在现阶段仅支持文本回复,音频输出功能正在开发中。为节省计算资源,OSUM的多任务训练引入了一种“ASR+X”范式,即同时训练ASR任务和一个附加任务X。这在加速训练的同时,允许执行X任务时参考文本和声学两种特征,从而提升性能和训练稳定性。“ASR+X”范式是在LLM的自回归框架内通过调整预测标签来实现的,无需对模型架构或损失函数进行修改。执行不同的X任务是通过给LLM不同的自然语言prompt来实现的,每个任务有5个候选prompt,训练时随机选择一个。prompt的示例如表1所示。

    训练数据

    OSUM旨在使用多样化的语音数据集进行多任务训练,目标是构建一个能够在对话场景中全面理解输入语音的统一模型。多任务训练过程使各个任务能够从共享学习中获益,从而提升模型的整体性能。有关用于训练的数据集的详细信息见表2所示,本版本模型的训练数据规模大约为5万小时。

    技术性能

    总览

    如图2所示,OSUM 模型和Qwen2-Audio 相比,在大多数任务中,尽管 OSUM 使用的计算资源和训练数据明显更少,但它的表现优于Qwen2-Audio。

    图2 OSUM与Qwen2-Audio各项任务性能对比的雷达图。雷达图中每个模型各项任务的值是基于公开测试集和内部测试集的平均结果得出的

    各项指标与性能演示

    ASR(语音识别):如表4所示,OSUM在中文ASR上表现优越,具体地,在WenetSpeech test meeting、3个AISHELL-2子测试集以及4个内部使用的SpeechIO测试集上优于其他模型。OSUM在英语测试集上性能也可与SenseVoice-S相媲美。值得注意的是,这些结果是在使用少得多的训练数据的情况下取得的。此外,我们发现,即使在训练过程中未纳入中英混语料数据集,OSUM在识别中英混语音方面也展现出了令人惊讶的出色能力。

    表4公开测试集和内部测试集上ASR任务的评估结果。加粗字体表示同一测试集中的最佳结果。所有内部测试结果均由我们自行推理得出

    表45公开测试集和内部测试集上多任务的评估结果。每个测试集的最佳结果都用粗体突出显示。蓝色字体显示的结果以及内部测试集的结果,均是我们使用原始发布的模型自行推理得出的

    SRWT(带时间戳的语音识别):如表5所示,OSUM模型在SRWT任务上的性能显著优于Whisper-Large-v3,相对优势达到了36.70%,并且也超过了Qwen-Audio。此外,OSUM的表现甚至略微超过了GMM-HMM模型,而后者在时间戳预测任务被广泛使用。另外,此功能不仅使得OSUM能够以端到端的方式预测时间戳,更重要的是,它引导OSUM模型理解了“时间”这一概念。在将来,我们将会利用这一能力继续开发更灵活的应用,例如判断音频中何时出现了语音事件,何时出现了说话人转换等。

    VED(语音事件检测):我们首先在公开测试集ESC-50和VocalSound上评估OSUM的性能。ESC-50包含大量的非人声音频事件,我们将它们归类为“其他”。表45示的实验结果表明,OSUM可以成功地将这些非人声音频事件归类为“其他”。此外,在VocalSound数据集上的结果显示,OSUM与Qwen2-audio相比虽然存在一定差距,但也取得了超过80%的准确率。值得注意的是,为更加符合真实使用场景,我们的训练数据是语音和音频事件拼接而成,但公开测试集只有孤立的音频事件而没有说话语音。即便存在这一不匹配的情况,OSUM模型的在公开测试集上的结果也证明了其有效性和泛化性。与公开测试集不同,我们人工录制了同时包含语音和声学事件的内部测试集。表45结果表明,PANNs由于其仅为孤立音频事件检测而设计,在我们内部测试集中基本处于不可用状态。Qwen2-audio的表现相对较好,但也出现了性能下降。相比之下,OSUM模型在公开测试集和内部测试集上都取得了较为均衡的结果,展现出了更强的泛化能力。

    SER(语音情感识别):如表45示,对于SER任务,使用公开数据集的实验中,OSUM在MER2023测试集上展现出了卓越的性能,超过了一些近期的公开基准模型。在MELD数据集上,OSUM的性能略低于SenseVoice-L模型,这很可能是因为后者在更大规模的语音情感数据集上进行了训练。此外,OSUM在内部测试集上的结果与EmoBox模型相当,显著优于其他对比方法。但是,我们也观察到,厌恶和恐惧这两种情感尤其难以识别,其归因于这两种情感的训练数据更加稀缺,也容易和其他情感混淆。

    SSR(说话风格识别):表5中实验表明,OSUM所采用的声学-文本双模态风格分类方法的表现显著优于GLM-4-9B-Chat所采用的单文本模态方法,这充分证明了“ASR+X”策略的价值。现阶段OSUM能够区分八种风格:“新闻科普”,“恐怖故事”,“童话故事”,“客服”,“诗歌散文”,“有声书”,“日常口语”以及“其他”。我们详细分析了测试集上各类别的准确率,发现OSUM在对“新闻科普”、“有声书”、“童话故事”以及“客服”风格类别上表现出色;然而,在“诗歌散文”、“恐怖故事”类别上仍有提升空间。有趣的是,我们发现从实际测试的主观体验上来说,OSUM风格分类正确率是超过测试集的,总体来说可以让人满意。

    SGC(说话者性别分类):在SGC公开测试集上的结果表明,OSUM在AISHELL-1测试集上达到了100%的准确率。这一结果在一定程度上表明该任务上存在说话人过拟合现象。此外,在Kaggle测试集上,我们的方法略优于Qwen2-Audio。但在我们的内部测试集上,OSUM的性能略低于Qwen2-Audio,但依然超过了95%。总之,OSUM在SGC任务上展现出了不错的性能,而且实测效果很少出现性别判断错误的情况。

    SAP(说话者年龄预测):在SAP任务上,由于我们发现青少年和成年人的声学相似度非常高,这使得有效区分他们变得很复杂。因此,我们将年龄分为三类:儿童、成年人和老年人。尽管我们努力调试了prompt,但Qwen2-Audio在Kaggle测试集和我们的内部测试集上,年龄分类准确率都较低。这可能是因为这些模型对年龄的分类过于细致,从而影响了Qwen2-Audio模型的最终效果。表4中结果显示,OSUM在Kaggle测试集上显著优于Qwen2-Audio,达到了76.52%的准确率。在我们的内部测试集上OSUM分类准确率虽然略有下降,但仍然超过了Qwen2-Audio。这表明OSUM在不同的数据上表现出了很强的泛化能力。

    STTC(语音转文本聊天):如表5所示,在STTC任务中,我们在所有测试集上都遵循了AirBench的评估协议。这包括提供音频查询的文本以及两个不同答案的文本,让基于文本的大语言模型(LLM)给出1到10的主观评分。这两个答案一个是真实回复,另一个是语音大语言模型(SULM)生成的答案。测试结果表明,在AirBench的官方speech子测试集上,OSUM的得分虽然低于Qwen2-Audio,但也处于一个合理范围。这主要是因为我们没有使用英语对话数据进行训练,目前的得分完全依赖于大语言模型自身的表现。反之,在我们内部的中文对话测试集上,OSUM的表现优于Qwen2-Audio,这充分证明了OSUM在中文对话任务上性能是不错的。总体而言,我们的OSUM模型在对话能力方面与Qwen2-Audio相当。

    更多功能

    OSUM理解大模型在将来会提供更多的功能,可作为通用语音打标工具使用。此外,我们正在开发的功能包括:

    1. 同时支持ASR+X和单X任务模式,在执行单X任务打标时推理速度更快。
    2. 同时输出ASR+X1+X2+..Xn的多任务打标模式,一次性提供几乎全部所需标签。
    3. 增加更多的理解任务。

    Step-Audio:产品级开源实时语音对话模型

    阶跃星辰:Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤),方言(如 粤语,四川话),可控制语速及韵律风格,支持RAP和哼唱等。其核心技术突破体现在以下四大技术亮点:

    • 1300亿多模态模型: 单模型能实现理解生成一体化完成语音识别、语义理解、对话、语音克隆、语音生成等功能,开源千亿参数多模态模型 Step-Audio-Chat
    • 高效数据生成链路: 基于130B 突破传统 TTS 对人工采集数据的依赖,生成高质量的合成音频数据,并同步开源首个基于大规模合成数据训练,支持 RAP 和哼唱的指令加强版语音合成模型 Step-Audio-TTS-3B ,该模型具有增强的指令遵循功能以控制语音综合的能力。
    • 精细语音控制: 支持多种情绪(如生气,高兴,悲伤)、方言(包括粤语、四川话等)和唱歌(包括 RAP、干声哼唱)的精准调控,满足用户对多样化语音生成的需求。
    • 扩展工具调用: 通过 ToolCall 机制和角色扮演增强,进一步提升其在 Agents 和复杂任务中的表现。
    端到端语音相互作用的人类评估。

    模型组成

    图2 采用了AQTA(音频输入,文本输出) + TTS框架 进行实时语音对话

    Step-Audio的体系结构。 Step-Adio主要由三个组成部分组成:语音令牌,LLM和语音解码器。语音令牌器负责将输入语音离散到令牌中。LLM模型接收文本和语音令牌,输出文本,而语音解码器生成波形输出。

    传统的语音对话系统通常采用包括ASR的级联建筑,LLM和TTS模块。但是,我们提出的模型在训练阶段进行了全面的多模式培训以及对文本和音频的一致性,已经具有端到端的语音对话功能。尽管对替代设计进行了广泛的探索,但我们最终采用了AQTA(音频输入,文本输出) + TTS框架 进行实时语音对话,如图2所示,这是由以下考虑的驱动的:

    • 高质量的纯净对话数据的稀缺性:纯净对话数据的可用性有限,再加上其受限的场景,限制了端到端语音对话模型的训练效率。
    • 输出语音的可控性和自定义:通过引入TTS模块,我们可以灵活地控制语音参数,例如音色和音调,以满足用户的个性化需求,同时不断增强模型的表现力能力。

    在Step-Audio系统中,音频流采用Linguistic tokenizer【语义】(码率16.7Hz,码本大小1024)与Semantice tokenizer【声学】(码率25Hz,码本大小4096)并行的双码本编码器方案,双码本在排列上使用了2:3时序交错策略。通过音频语境化持续预训练和任务定向微调强化了130B参数量的基础模型(Step-1),最终构建了强大的跨模态语音理解能力。为了实现实时音频生成,系统采用了混合语音解码器,结合流匹配(flow matching)与神经声码技术。此外,采用语音活动检测(VAD)模块提取声段。

    Tokenizer

    我们通过token级交错方法实现Linguistic token与Semantic token的有效整合。Linguistic tokenizer的码本大小是1024,码率16.7Hz;而Semantic tokenizer则使用4096的大容量码本来捕捉更精细的声学细节,码率25Hz。鉴于两者的码率差异,我们建立了2:3的时间对齐比例——每两个Linguistic token对应三个Linguistic token形成时序配对

    语言模型

    为了提升Step-Audio有效处理语音信息的能力,并实现精准的语音-文本对齐,我们在Step-1(一个拥有1300亿参数的基于文本的大型语言模型LLM)的基础上进行了音频持续预训练。

    在多轮对话系统中音频令牌和文本令牌之间的长度差异需要有效的处理策略。为了解决这个问题,历史信息最初是在系统输入之前使用ASR模型转录为文本格式的,从而优化了计算效率。但是,应注意的是,模型体系结构在需要时保持处理和使用音频令牌作为历史上下文的能力。

    语音解码器

    Step-Audio语音解码器主要是将包含语义和声学信息的离散标记信息转换成连续的语音信号。该解码器架构结合了一个30亿参数的语言模型、流匹配模型(flow matching model)和梅尔频谱到波形的声码器(mel-to-wave vocoder)。为优化合成语音的清晰度(intelligibility)和自然度(naturalness),语音解码器采用双码交错训练方法(dual-code interleaving),确保生成过程中语义与声学特征的无缝融合

    实时推理管线

    为了实现实时的语音交互,我们对推理管线进行了一系列优化。其中最核心的是控制模块(Controller),该模块负责管理状态转换、协调响应生成,并确保关键子系统间的无缝协同。这些子系统包括:

    • 语音活动检测(VAD):实时检测用户语音起止
    • 流式音频分词器(Streaming Audio Tokenizer):实时音频流处理。输入音频流是通过两个平行的令牌管道处理的,每个管道都采用固定持续分段。将所得令牌无缝合并为2:3交织比的单个序列。没有流音频令牌,根据音频输入的长度,推理时间将明显较慢。
    • Step-Audio语言模型与语音解码器:多模态回复生成
    • 上下文管理器(Context Manager):动态维护对话历史与状态。我们的系统利用文本转录而不是原始的音频令牌来实现历史上下文,因为它提供了更紧凑的表示(平均文本审计代币比率为1:14),提高性能,并启用更长的对话,对质量的影响最小的影响很小。 ASR异步将用户语音转录为文本,并保持准确,最新的对话历史记录。

    后训练细节

    在后训练阶段,我们针对自动语音识别(ASR)与文本转语音(TTS)任务进行了专项监督微调(Supervised Fine-Tuning, SFT)。对于音频输入-文本输出(Audio Question Text Answer, AQTA)任务,我们采用多样化高质量数据集进行SFT,并采用了基于人类反馈的强化学习(RLHF)以提升响应质量,从而实现对情感表达、语速、方言及韵律的细粒度控制。

    TTS模型:

    解决TTS任务中高质量语音数据的稀缺性

    Training Detail

    与传统的语音合成(TTS)系统注重对说话人特征、情感表达、语言特征和风格元素的精细控制不同,我们的方法采用了基于聊天的范式和大型语言模型(LLMs)的训练方法。这一战略对齐显著增强了系统的灵活性,同时建立了一个可扩展的框架,以支持未来模型和数据的扩展,从而解决了语音合成系统在可扩展性方面的关键挑战。

    监督的微调格式:

    SFT格式包括三个基本组成部分:系统提示、人类输入和助手回复,采用两轮对话结构。在这种格式中,系统提示作为指定说话人属性和定义支持的指令标签的基础元素。人类输入和助手回复部分则专门用于处理文本内容和双词典表示。第一轮的文本和音频标记可以用来保持领域内说话人的音色和风格一致性,同时也支持领域外的零样本克隆。

    指令标签

    指令标签分为两种不同的类别:描述性标签和比较性标签。描述性标签用于控制语言、方言、声音和风格等方面,而比较性标签则用于情感和语速控制的层次化区分。描述性标签的数据是通过Step-Audio模型克隆生成的,支持包括日语、韩语、粤语、四川方言、可爱声音、说唱和唱歌等语言和风格。比较性标签的数据则是通过Audio Edit模型生成的,支持诸如快乐、愤怒、悲伤等情感,以及快慢等语速变化,每种变化都被分为五个层级。

    我们使用第5.1.1节中概述的SFT数据,并采用一个具有30亿参数的模型,训练一个周期,初始学习率为 2×10−5。学习率采用余弦衰减策略进行调整,最低值设置为 2×10−6。

    AQTA:

    我们为AQTA任务应用了基于人类反馈的强化学习(RLHF),从而创建了Step-Audio-Chat模型,如图6所示。

    说明:

    用了AQTA(音频输入,文本输出) + TTS框架 情况下是如何实现多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤),方言(如 粤语,四川话),可控制语速及韵律风格,支持RAP和哼唱

    通过TTS【cosyvoice】代码可知,LLM的文本输出中会包含 {语言}【情感】 [语速] 这样的文本输出,然后TTS用于合成对应的音频: 使用[{}]的声音,根据这些情感标签的指示,调整你的情感、语气、语调和哼唱节奏

        self.sys_prompt_dict = {
            "sys_prompt_for_rap": "请参考对话历史里的音色,用RAP方式将文本内容大声说唱出来。",
            "sys_prompt_for_vocal": "请参考对话历史里的音色,用哼唱的方式将文本内容大声唱出来。",
            "sys_prompt_wo_spk": '作为一名卓越的声优演员,你的任务是根据文本中()或()括号内标注的情感、语种或方言、音乐哼唱、语音调整等标签,以丰富细腻的情感和自然顺畅的语调来朗读文本。\n# 情感标签涵盖了多种情绪状态,包括但不限于:\n- "高兴1"\n- "高兴2"\n- "生气1"\n- "生气2"\n- "悲伤1"\n- "撒娇1"\n\n# 语种或方言标签包含多种语言或方言,包括但不限于:\n- "中文"\n- "英文"\n- "韩语"\n- "日语"\n- "四川话"\n- "粤语"\n- "广东话"\n\n# 音乐哼唱标签包含多种类型歌曲哼唱,包括但不限于:\n- "RAP"\n- "哼唱"\n\n# 语音调整标签,包括但不限于:\n- "慢速1"\n- "慢速2"\n- "快速1"\n- "快速2"\n\n请在朗读时,根据这些情感标签的指示,调整你的情感、语气、语调和哼唱节奏,以确保文本的情感和意义得到准确而生动的传达,如果没有()或()括号,则根据文本语义内容自由演绎。',
            "sys_prompt_with_spk": '作为一名卓越的声优演员,你的任务是根据文本中()或()括号内标注的情感、语种或方言、音乐哼唱、语音调整等标签,以丰富细腻的情感和自然顺畅的语调来朗读文本。\n# 情感标签涵盖了多种情绪状态,包括但不限于:\n- "高兴1"\n- "高兴2"\n- "生气1"\n- "生气2"\n- "悲伤1"\n- "撒娇1"\n\n# 语种或方言标签包含多种语言或方言,包括但不限于:\n- "中文"\n- "英文"\n- "韩语"\n- "日语"\n- "四川话"\n- "粤语"\n- "广东话"\n\n# 音乐哼唱标签包含多种类型歌曲哼唱,包括但不限于:\n- "RAP"\n- "哼唱"\n\n# 语音调整标签,包括但不限于:\n- "慢速1"\n- "慢速2"\n- "快速1"\n- "快速2"\n\n请在朗读时,使用[{}]的声音,根据这些情感标签的指示,调整你的情感、语气、语调和哼唱节奏,以确保文本的情感和意义得到准确而生动的传达,如果没有()或()括号,则根据文本语义内容自由演绎。',
        }

    VITA-1.5:GPT-4o级别的实时视觉和语音交互模型

    [📖 VITA-1.5 Paper] [🤖 Basic Demo] [🍎 VITA-1.0]

    [📽 VITA-1.5 Demo Show! Here We Go! 🔥]

    引言

    近年来,多模态大语言模型(MLLMs)在视觉和文本的结合上取得了显著进展。然而,随着人机交互需求的增加,语音在多模态对话系统中的作用变得愈发重要。语音不仅是信息传递的关键媒介,还能显著提升交互的自然性和便捷性。因此,如何将视觉和语音模态高效整合,实现高性能的多模态交互,成为了当前研究的重点。

    VITA-1.5的提出正是为了解决这一挑战。通过精心设计的多阶段训练方法,VITA-1.5逐步训练大语言模型(LLM)理解视觉和语音信息,最终实现了流畅的视觉和语音交互。与现有模型相比,VITA-1.5不仅保留了强大的视觉-语言能力,还实现了高效的语音对话能力,显著加速了多模态端到端的响应速度。

    VITA-1.5

    模型架构

    图 2:VITA-1.5 的整体架构。输入端由视觉和音频编码器及其连接到 LLM 的适配器组成。输出端有一个端到端的语音生成模块,而不是像初始 VITA-1.0 版本那样直接使用外部 TTS 模型。

    VITA-1.5的整体架构如图2所示。输入侧与VITA-1.0版本相同,采用“多模态编码器-适配器-LLM”的配置。它将视觉/音频Transformer和多层连接器与LLM结合进行联合训练,旨在增强对视觉、语言和音频的统一理解。在输出侧,VITA-1.5拥有自己的端到端语音模块,而不是像原始VITA-1.0版本那样使用外部TTS模型。

    视觉模态

    视觉编码器:VITA-1.5采用InternViT-300M作为视觉编码器,输入图像大小为448×448像素,每张图像生成256个视觉标记。对于高分辨率图像,VITA-1.5采用动态分块策略捕捉局部细节,提高图像理解的准确性。

    视频处理:视频被视为一种特殊的多图像输入。如果视频长度短于4秒,则均匀采样4帧;对于4到16秒的视频,每秒采样一帧;对于超过16秒的视频,均匀采样16帧。视频帧不应用动态分块,以避免过多的视觉标记影响处理效率。

    视觉适配器:使用两层MLP将视觉特征映射到适合LLM理解的视觉标记。

    音频模态

    语音编码器:类似于[56],我们的音频编码模块由多个下采样卷积层(4倍下采样)和24个Transformer块(隐藏大小为1024)组成。下采样层有助于降低音频特征的帧率,提高LLM的处理速度。音频编码器约有350M参数,输出帧率为12.5Hz。使用Mel滤波器组特征作为音频编码器的输入,窗口大小为25ms,偏移为10ms。

    语音适配器:由多个2倍下采样的卷积层组成。

    语音解码器:使用TiCodec作为我们的编解码模型,定制了一个大小为1024的单码本。这种单码本设计简化了推理阶段的解码过程。编解码模型负责将连续语音信号编码为离散语音标记,频率为40Hz,同时能够将这些标记解码回采样率为24,000Hz的语音信号。

    当前的LLM只能输出文本标记,语音生成能力要求LLM能够输出语音标记。为此,我们在文本标记后添加了两个语音解码器:1)非自回归(NAR)语音解码器,全局处理文本标记并建模语义特征,旨在生成语音标记的初始分布;2)自回归(AR)语音解码器,基于NAR解码器生成的语音信息逐步生成更高质量的语音标记。最终的语音标记序列通过编解码模型的语音解码器解码为连续语音信号流(波形)。我们为NAR和AR语音解码器采用了4个LLaMA解码层,隐藏大小为896,参数大小约为120M。

    训练数据

    如表1所示,多模态指令微调的训练数据涵盖了广泛的类别,如描述数据和问答数据,包括中文和英文。在不同的训练阶段,从整体数据集中选择性地采样子集以服务于不同的目标。具体来说,数据集分类如下:

    • 图像描述数据:使用ShareGPT4V、ALLaVA-Caption、SharedGPT4o-Image和合成数据等数据集训练模型生成图像的描述性语言。
    • 图像问答数据:使用LLaVA-150K、LLaVA-Mixture-sample、LVIS-Instruct、ScienceQA、ChatQA和从LLaVA-OV采样的子集(如通用图像问答和数学推理数据集)等数据集训练模型回答基于图像的问题和执行视觉推理任务。
    • OCR和图表数据:支持模型理解OCR和图表内容,使用Anyword-3M、ICDAR2019-LSVT、UReader、SynDOG、ICDAR2019-LSVT-QA和从LLaVA-OV采样的相应数据等数据集。
    • 视频数据:使用ShareGemini和合成数据等数据集训练模型处理视频输入并执行诸如描述和基于视频的问答等任务。
    • 纯文本数据:增强模型理解和生成语言的能力,促进基于文本的问答任务。

    除了表1中列出的图像和视频数据外,还纳入了110,000小时的内部语音-转录配对ASR数据,涵盖中文和英文,用于训练音频编码器并将音频编码器与LLM对齐。此外,使用TTS系统生成的3,000小时文本-语音配对数据用于训练语音解码器。

    三阶段训练策略

    为了确保VITA-1.5在涉及视觉、语言和音频的任务中表现良好,我们必须面对一个关键挑战,即不同模态之间的训练冲突。例如,添加语音数据可能会对视觉数据的理解产生负面影响,因为语音的特征与视觉的特征显著不同,导致学习过程中的干扰。为了解决这一挑战,我们设计了一个三阶段训练策略,如图3所示。核心思想是逐步将不同模态引入模型,使其在增加新模态能力的同时保持现有模态的能力。

    VITA-1.5的训练管道。训练过程分为三个阶段,以逐步将视觉和音频纳入LLM同时缓解了形态冲突。第一阶段的重点是视觉训练,包括视觉对齐(阶段1.1,使用表1中的20%字幕数据),视觉理解(阶段1.2,使用100%的字幕数据)以及用于Visual QA的指令调整(阶段1.3,使用20%字幕数据和100%QA数据)。阶段2引入音频输入调整,并具有音频对齐(阶段2.1,使用11,000小时的语音转录对)和语音质量检查的指令调整(阶段2.2,采样4%字幕数据和20%的QA数据)。最后,第3阶段的重点是音频输出调整,包括对编解码器模型的训练(使用3,000个小时的文本语音数据)和语音解码器培训(阶段3.2)。图像中显示的百分比对应于表1中指定的数据采样率。

    阶段1:视觉训练

    阶段1.1 视觉对齐:在此阶段,我们的目标是弥合视觉和语言之间的差距。前者的特征从预训练的视觉编码器InternViT-300M中提取,后者通过LLM引入。我们使用表1中20%的描述性描述数据进行训练,其中只有视觉适配器是可训练的,而其他模块是冻结的。这种方法允许LLM初步对齐视觉模态。

    阶段1.2 视觉理解:在此阶段,我们的目标是教会LLM转录图像内容。为此,我们使用表1中所有的描述性描述数据。在此过程中,视觉模块的编码器和适配器以及LLM都是可训练的。重点是使模型通过学习关于图像的描述性文本,建立视觉和语言之间的强连接,使其能够通过生成自然语言描述来理解图像内容。

    阶段1.3 视觉SFT:在阶段1.2之后,模型已经获得了对图像和视频的基本理解。然而,指令跟随能力仍然有限,难以应对视觉问答任务。为了实现这一点,我们使用表1中所有的问答数据,同时保留20%的描述性描述数据以增加数据集的多样性和任务的复杂性。

    在训练过程中,视觉模块的编码器和适配器以及LLM都是可训练的。此阶段的关键目标是使模型不仅能够理解视觉内容,还能够根据指令回答问题。

    阶段2:音频输入微调

    阶段2.1 音频对齐:在完成阶段1的训练后,模型已经建立了强大的图像和视频理解基础。在此阶段,我们的目标是基于阶段1减少音频和语言之间的差异,使LLM能够理解音频输入。训练数据包括11,000小时的语音-转录对。我们采用两步方法:(a)语音编码器训练:我们采用常见语音识别系统中使用的训练框架,使用连接时序分类(CTC)损失函数[18]训练语音编码器。目的是使编码器从语音输入中预测转录文本。此步骤确保音频编码器能够提取语音特征并将其映射到文本表示空间。(b)语音适配器训练:在训练语音编码器后,我们将其与LLM集成,使用音频适配器将音频特征引入LLM的输入层。此阶段的训练目标是使LLM能够输出语音数据的转录文本。

    此外,在步骤(b)中,我们引入了特殊的可训练输入标记来指导语音理解过程。这些标记提供了额外的上下文信息,指导用于问答任务的LLM执行ASR任务。

    阶段2.2 音频SFT:此阶段的重点是引入语音问题和文本答案的问答功能。为此,我们从表1中采样4%的描述数据和20%的问答数据。在数据处理方面,大约一半的基于文本的问题被随机替换为其对应的语音版本,使用TTS系统生成。

    在此阶段,视觉编码器和适配器、音频编码器和适配器以及LLM都是可训练的,旨在提高模型对多模态输入的适应性。此外,我们在LLM的输出中添加了一个分类头。该头用于区分输入是来自语音还是文本。结果,模型可以更准确地解释语音输入,并高效灵活地处理不同模态。

    阶段3:音频输出微调

    在前两个训练阶段,VITA-1.5模型已经有效地发展了其多模态理解能力。然而,一个关键的能力,即语音输出,仍然缺失,这对于其作为交互助手的角色至关重要。为了在不影响模型基本能力的情况下引入语音输出功能,我们借鉴了[56]的策略,使用3,000小时的文本-语音数据,并采用两步训练方法(见图3)。

    阶段3.1 编解码训练:此步骤的目标是使用语音数据训练具有单码本的编解码模型。编解码模型的编码器能够将语音映射到离散标记,而解码器可以将离散标记映射回语音流。在VITA-1.5的推理阶段,仅使用解码器。

    阶段3.2 NAR + AR解码器训练:此阶段的训练使用文本-语音配对数据,其中文本被输入到LLM的分词器和嵌入层以获得其嵌入向量,语音被输入到编解码模型的编码器以获得其语音标记。文本嵌入向量被发送到NAR语音解码器以获得全局语义特征,然后将这些特征发送到AR语音解码器,预测相应的语音标记。请注意,在此阶段LLM是冻结的,因此多模态性能不受影响。

    评估

    视觉-语言评估

    基线:我们比较了一系列开源MLLMs,包括VILA-1.5、LLaVA-Next、CogVLM2、InternLM-XComposer2.5、Cambrian-1、MiniCPM-V-2.6、Ovis1.5、InternVL-Chat-1.5、InternVL-2、LLaVA-OV和Video-LLaVA、SilME和LongVA,以及5个闭源MLLMs,包括GPT-4V、GPT-4o、GPT-4o-mini、Gemini 1.5 Pro和Claude 3.5 Sonnet。

    评估基准:为了评估VITA-1.5的图像感知和理解能力,我们使用了多个评估基准,包括MME、MMBench、MMStar、MMMU、MathVista、HallusionBench、AI2D、OCRBench和MMVet。这些基准涵盖了广泛的方面,包括通用多模态能力(如MME、MMBench和MMMU)、数学推理(MathVista)、幻觉检测(HallusionBench)、图表(AI2D)和OCR(OCRBench)理解,提供了全面的评估结果。对于视频理解,我们使用了代表性的评估基准,包括Video-MME、MVBench和TempCompass。

    视觉-语言能力:表2展示了VITA-1.5的图像理解性能比较。在三个阶段的训练后,VITA-1.5的表现与最先进的开源模型相当,甚至超过了一些闭源模型,如GPT-4V和GPT-4o-mini。这一结果突显了VITA-1.5在图像-语言任务中的强大能力。如表3所示,VITA-1.5在视频理解评估中表现出与顶级开源模型相当的性能。与专有模型的显著差距表明,VITA-1.5在视频理解方面仍有显著的改进空间和潜力。请注意,在阶段2(音频输入微调)和阶段3(音频输出微调)的训练后,VITA-1.5几乎保留了其在阶段1(视觉-语言训练)中的原始视觉-语言能力。

    语音评估

    基线:以下三个基线模型用于比较:Wav2vec2-base、Mini-Omini2、Freeze-Omini和VITA-1.0。

    评估基准普通话评估集包括三个数据集:aishell-1、test net和test meeting。这些数据集用于评估模型在普通话语音上的表现。评估指标是字符错误率(CER)。英语评估集包括四个数据集:dev-clean、dev-other、test-clean和test-other,用于评估模型在英语语音上的表现。评估指标是词错误率(WER)。

    ASR性能:表4中的评估结果表明,VITA-1.5在普通话和英语ASR任务中均取得了领先的准确性。这表明VITA-1.5已成功集成了先进的语音能力,以支持多模态交互。

    结论

    本文介绍了VITA-1.5,这是一个通过精心设计的三阶段训练策略整合视觉和语音的多模态LLM。通过缓解模态之间的固有冲突,VITA-1.5在视觉和语音理解方面实现了强大的能力,无需依赖单独的ASR或TTS模块即可实现高效的语音到语音交互。广泛的评估表明,VITA-1.5在多模态基准测试中表现出色。我们希望VITA-1.5能够接过VITA-1.0的旗帜,继续推动开源模型在实时多模态交互领域的进步。

    ASR语音识别指标计算

    #coding=utf-8
    import os
    import sys
    import re
    from typing import List, Union
    import jiwer
    import pdb
    
    
    def cal_wer(path_ref, path_hyp, metric_type, output_detail, path_output):
    
        ref_text, hyp_text, ref_key = _read_file(path_ref, path_hyp, metric_type)
        
        cal_wer_from_list(ref_text, hyp_text, ref_key, metric_type, output_detail, path_output)
    
    
    def cal_wer_from_list(
        reference: Union[str, List[str]], 
        hypothesis: Union[str, List[str]], 
        key: Union[str, List[str]], 
        metric_type: str, 
        output_detail: bool, 
        path_output: str
    ):
        if isinstance(reference, str):
            reference = [reference]
        if isinstance(hypothesis, str):
            hypothesis = [hypothesis]
        if isinstance(key, str):
            key = [key]
    
        # 根据ref是否为空, 先分别计算wer指标再汇总
        ref_normal, hyp_normal, key_normal = [], [], []
        ref_empty, hyp_empty, key_empty = [], [], []
        for i in range(len(reference)):
            if len(reference[i]) != 0:
                ref_normal.append(reference[i])
                hyp_normal.append(hypothesis[i])
                key_normal.append(key[i])
            else:
                ref_empty.append(reference[i])
                hyp_empty.append(hypothesis[i])
                key_empty.append(key[i])
    
        res_normal, out_normal = _cal_wer_normal(ref_normal, hyp_normal, metric_type)
        res_empty, out_empty = _cal_wer_empty(hyp_empty, metric_type)
        _summary(ref_normal, hyp_normal, res_normal, out_normal.alignments, key_normal, 
                 hyp_empty, res_empty, out_empty, key_empty, 
                 metric_type, output_detail, path_output)
    
    
    def _read_file(path_ref, path_hyp, metric_type):
        ref_key, ref_text = _preprocess(path_ref, '\t', metric_type)
        hyp_key, hyp_text = _preprocess(path_hyp, '\t', metric_type)
    
        tmp_dict = {}
        tmp_text = []
        for i in range(len(hyp_key)):
            if hyp_key[i] not in tmp_dict.keys():
                tmp_dict[hyp_key[i]] = hyp_text[i]
            else:
                print ("repeated key")
        for i in range(len(ref_key)):
            if ref_key[i] in tmp_dict.keys():
                tmp_text.append(tmp_dict[ref_key[i]])
            else:
                tmp_text.append("")
    
        return ref_text, tmp_text, ref_key
    
    
    def _preprocess(path_in, sep, metric_type):
        res_key, res_text = [], []
    
        with open(path_in, "r", encoding="utf-8") as f_in:
            lines = f_in.readlines()
            for line in lines:
                line = line.strip().split(sep, 1)
                if len(line) == 2:
                    key, text = line
                    text = re.sub("<s>", "", text)
                    text = re.sub("</s>", "", text)
                    text = re.sub("<unk>", "", text)
                    text = re.sub("@@ ", "", text)
                    text = re.sub("@ ", "", text)
                    text = re.sub("@@", "", text)
                    text = re.sub("@", "", text)
                    #text = re.sub(" ", "", text)
                    text = text.lower()
                else:
                    key = line[0]
                    text = ""
    
                text = [x for x in text]
                text_tmp = ""
                if metric_type == "wer":
                    for ch in text:
                        if '\u4e00' <= ch <= '\u9fff':
                            text_tmp += " " + ch + " "
                        else:
                            text_tmp += ch
                    text = text_tmp.strip().replace("  ", " ")
                elif metric_type == "cer":
                    text_tmp = "".join(text)
                    text = text_tmp.strip().replace(" ", "")
                else:
                    assert False
    
                res_key.append(key)
                res_text.append(text)
    
        return res_key, res_text
    
    
    def _cal_wer_normal(reference, hypothesis, metric_type):
        if metric_type == "wer":
            out = jiwer.process_words(reference=reference, hypothesis=hypothesis)
            ERR = out.wer
        elif metric_type == "cer":
            out = jiwer.process_characters(reference=reference, hypothesis=hypothesis)
            ERR = out.cer
        else:
            assert False
    
        H = out.hits
        S = out.substitutions
        D = out.deletions
        I = out.insertions
        N = H + S + D
    
        res = [ERR, N, S, D, I]
    
        return res, out
    
    
    def _cal_wer_empty(hypothesis, metric_type):
        out = []
    
        I = 0
        for hyp in hypothesis:
            if hyp == "":
                i = 0
            else:
                if metric_type == "wer":
                    i = len(hyp.split(" "))
                elif metric_type == "cer":
                    i = len(hyp)
                else:
                    assert False
            I += i
            out.append(i)
    
        res = [0, 0, 0, 0, I]
    
        return res, out
    
    
    def _summary(ref_normal, hyp_normal, res_normal, out_normal, key_normal,
                 hyp_empty, res_empty, out_empty, key_empty, 
                 metric_type, output_detail, path_output):
        # wer/cer计算
        _, N, S, D, I = res_normal
        I += res_empty[-1]
        if N != 0:
            ERR = (S + D + I) / N
            SUB = S / N
            DEL = D / N
            INS = I / N
            N_WORD = N
        else:
            if I == 0:
                ERR = 0
            else:
                ERR = 1
            SUB, DEL, INS, N_WORD = 0, 0, I, 0
    
        # 句准计算 + 详细错误指标 + 详细错误统计
        utt_normal, alignments_normal, statistics_normal = _analyse_normal(
            ref_normal, hyp_normal, out_normal, key_normal, metric_type)
        utt_empty, alignments_empty, statistics_empty = _analyse_empty(
            hyp_empty, out_empty, key_empty, metric_type)
    
        utt = utt_normal + utt_empty
        alignments = alignments_normal + alignments_empty
        for key in statistics_empty['insert'].keys():
            if key not in statistics_normal['insert'].keys():
                statistics_normal['insert'][key] = statistics_empty['insert'][key]
            else:
                statistics_normal['insert'][key] += statistics_empty['insert'][key]
        N_SENT = len(out_normal) + len(out_empty)
        ACC_UTT = utt / N_SENT
        res = [ERR, SUB, DEL, INS, N_WORD, ACC_UTT, N_SENT]
    
        _format_output(res, alignments, statistics_normal, metric_type, output_detail, path_output)
    
    
    def _analyse_normal(ref_normal, hyp_normal, out_normal, key_normal, metric_type):
        utt_normal = 0
        alignments_normal = []
        statistics_normal = {'substitute' : {}, 'delete' : {}, 'insert' : {}}
    
        for i, alignment in enumerate(out_normal):
            err, n_hit, n_sub, n_del, n_ins = 0, 0, 0, 0, 0
            ref_align, hyp_align = "", ""
            sub_align, del_align, ins_align = "", "", ""
            for j, chunk in enumerate(alignment):
                if (metric_type == "wer" and (ref_align != "" or hyp_align != "")):
                    ref_align += " "
                    hyp_align += " "
                if chunk.type == 'equal':
                    n_hit += chunk.ref_end_idx - chunk.ref_start_idx
                    ref_align += _extract_string(ref_normal[i], chunk.ref_start_idx, chunk.ref_end_idx, metric_type)
                    hyp_align += _extract_string(hyp_normal[i], chunk.hyp_start_idx, chunk.hyp_end_idx, metric_type)
    
                elif chunk.type == 'substitute':
                    err += 1
                    n_sub += chunk.ref_end_idx - chunk.ref_start_idx
    
                    ref_sub = _extract_string(ref_normal[i], chunk.ref_start_idx, chunk.ref_end_idx, metric_type)
                    hyp_sub = _extract_string(hyp_normal[i], chunk.hyp_start_idx, chunk.hyp_end_idx, metric_type)
    
                    ref_align += ref_sub
                    hyp_align += hyp_sub
    
                    key_sub = "(" + ref_sub + ") --> (" + hyp_sub + ")"
    
                    sub_align += key_sub + "\t"
    
                    if key_sub not in statistics_normal['substitute'].keys():
                        statistics_normal['substitute'][key_sub] = 1
                    else:
                        statistics_normal['substitute'][key_sub] += 1
    
                elif chunk.type == 'delete':
                    err += 1
                    n_del += chunk.ref_end_idx - chunk.ref_start_idx
    
                    ref_del = _extract_string(ref_normal[i], chunk.ref_start_idx, chunk.ref_end_idx, metric_type)
                    hyp_del = "*"
    
                    ref_align += ref_del
                    hyp_align += hyp_del
    
                    key_del = ref_del
    
                    del_align += key_del + "\t"
    
                    if key_del not in statistics_normal['delete'].keys():
                        statistics_normal['delete'][key_del] = 1
                    else:
                        statistics_normal['delete'][key_del] += 1
    
                elif chunk.type == 'insert':
                    err += 1
                    n_ins += chunk.hyp_end_idx - chunk.hyp_start_idx
    
                    ref_ins = "*"
                    hyp_ins = _extract_string(hyp_normal[i], chunk.hyp_start_idx, chunk.hyp_end_idx, metric_type)
    
                    ref_align += ref_ins
                    hyp_align += hyp_ins
    
                    key_ins = hyp_ins
    
                    ins_align += key_ins + "\t"
    
                    if key_ins not in statistics_normal['insert'].keys():
                        statistics_normal['insert'][key_ins] = 1
                    else:
                        statistics_normal['insert'][key_ins] += 1
    
                else:
                    assert False
    
            if err == 0:
                utt_normal += 1
            alignments_normal.append((key_normal[i], ref_align, hyp_align, 
                                      sub_align, del_align, ins_align, 
                                      n_hit, n_sub, n_del, n_ins))
    
        return utt_normal, alignments_normal, statistics_normal
    
    
    def _analyse_empty(hyp_empty, out_empty, key_empty, metric_type):
        utt_empty = 0
        alignments_empty = []
        statistics_empty = {'insert' : {}}
    
        for i, ins in enumerate(out_empty):
            ref_align, hyp_align = "", ""
            sub_align, del_align, ins_align = "", "", ""
    
            if ins == 0:
                utt_empty += 1
            else:
                ref_ins = "*"
                hyp_ins = _extract_string(hyp_empty[i], 0, len(hyp_empty[i]), metric_type)
    
                ref_align += ref_ins
                hyp_align += hyp_ins
    
                key_ins = hyp_ins
    
                ins_align += key_ins + "\t"
    
                if key_ins not in statistics_empty['insert'].keys():
                    statistics_empty['insert'][key_ins] = 1
                else:
                    statistics_empty['insert'][key_ins] += 1
            alignments_empty.append((key_empty[i], ref_align, hyp_align, 
                                    sub_align, del_align, ins_align, 
                                    0, 0, 0, ins))
    
        return utt_empty, alignments_empty, statistics_empty
    
    
    def _extract_string(s, begin, end, metric_type):
        res = ""
        if metric_type == 'wer':
            res = ' '.join(s.split(' ')[begin:end])
        elif metric_type == 'cer':
            res = s[begin:end]
        else:
            assert False
        return res
    
    
    def _format_output(res, alignments, statistics, metric_type, output_detail, path_output):
        with open(path_output, "w", encoding="utf-8") as f_out:
            if output_detail == True:
                f_out.write("-"*100 + "\n")
                for i, sample in enumerate(alignments):
                    key, ref, hyp = sample[0:3]
                    sub_align, del_align, ins_align = sample[3:6]
                    n_hit, n_sub, n_del, n_ins = sample[6:]
    
                    f_out.write("KEY: " + key + "\n")
                    f_out.write("REF: " + ref + "\n")
                    f_out.write("HYP: " + hyp + "\n")
                    f_out.write("CNT: " + "H(" + str(n_hit) + ") " + \
                                          "S(" + str(n_sub) + ") " + \
                                          "D(" + str(n_del) + ") " + \
                                          "I(" + str(n_ins) + ")\n")
                    f_out.write("SUB: " + sub_align + "\n")
                    f_out.write("DEL: " + del_align + "\n")
                    f_out.write("INS: " + ins_align + "\n\n")
                f_out.write("-"*100 + "\n")
    
                f_out.write("-"*100 + "\n")
                lst_sub = list(sorted(statistics['substitute'].items(), key = lambda x : x[1], reverse=True))
                lst_del = list(sorted(statistics['delete'].items(), key = lambda x : x[1], reverse=True))
                lst_ins = list(sorted(statistics['insert'].items(), key = lambda x : x[1], reverse=True))
                f_out.write("\n替换错误统计: \n")
                for x in lst_sub:
                    f_out.write("\t" + x[0] + "(" + str(x[1]) + ")" + "\n")
                f_out.write("\n删除错误统计: \n")
                for x in lst_del:
                    f_out.write("\t" + x[0] + "(" + str(x[1]) + ")" + "\n")
                f_out.write("\n插入错误统计: \n")
                for x in lst_ins:
                    f_out.write("\t" + x[0] + "(" + str(x[1]) + ")" + "\n")
                f_out.write("-"*100 + "\n")
    
            f_out.write("-"*100 + "\n")
            f_out.write(metric_type.upper() + ": " + str(round(res[0] * 100.0, 2)) + '%\n')
            f_out.write("WORDS: " + str(res[4]) + "\t")
            f_out.write("SUB: " + str(round(res[1] * 100.0, 2)) + "%\t")
            f_out.write("DEL: " + str(round(res[2] * 100.0, 2)) + "%\t")
            f_out.write("INS: " + str(round(res[3] * 100.0, 2)) + "%\n")
            f_out.write("ACC_UTT: " + str(round(res[5] * 100.0, 2)) + '%\t')
            f_out.write("SENTS: " + str(res[6]) + '\n')
            f_out.write("-"*100 + "\n")
        
        print (metric_type + " calculation done")
        print ("saved to " + path_output)
    
    
    if __name__ == '__main__':
    
        '''
        # example of function cal_wer_from_list
        ref = ["今 天 天 气", "hello 我 ok 的", ""]
        hyp = ["今 天 天", "halo 我 ok 的 呀", "噪 声"]
        key = ["000", "001", "002"]
        path_output = "./example.wer"
        cal_wer(ref, hyp, key, "wer", True, path_output)
    
        ref = ["今天天气", "hello我ok的", ""]
        hyp = ["今天天", "halo我ok的呀", "噪声"]
        key = ["000", "001", "002"]
        path_output = "./example.cer"
        cal_wer_from_list(ref, hyp, key, "cer", True, path_output)
        '''
    

    InspireMusic–阿里通义开源音乐生成框架

    InspireMusic是由通义实验室开源的音乐生成技术,旨在打造一款集音乐生成、歌曲生成、音频生成能力为一体的开源AIGC工具包。

    为研究者和开发者提供音乐/歌曲/音频生成模型的训练和调优工具及模型,方便优化生成效果;同时为音乐爱好者提供一个易于使用的文本生成音乐/歌曲/音频创作工具,可通过文字描述或音频提示来控制生成内容。

    目前,InspireMusic已开源了音乐生成的训练和推理代码,支持通过简单的文字描述或音频提示,快速生成多种风格的音乐作品。

    InspireMusic的文生音乐创作模式涵盖了多种曲风、情感表达和复杂的音乐结构控制,提供了极大的创作自由度和灵活性。未来计划进一步开放歌唱生成和音频生成的基础模型,欢迎研究者、开发者及用户积极参与体验和研发。该开源工具包为社区开发者提供了丰富的技术资源,支持从学术研究到产品开发的广泛应用。

    🎶 主要特点

    • 统一的音频生成框架:基于音频大模型技术,InspireMusic支持音乐、歌曲及音频的生成,为用户提供多样化选择;
    • 灵活可控生成:基于文本提示和音乐特征描述,用户可精准控制生成音乐的风格和结构;
    • 简单易用:简便的模型微调和推理工具,为用户提供高效的训练与调优工具。

    🌟代码仓库

    核心模型

    InspireMusic由音频tokenizer、自回归Transformer模型、基于常微分方程的扩散模型即Conditional Flow Matching (CFM)模型、Vocoder所组成,可支持文本生成音乐、音乐续写等任务。通过具有高压缩比的单码本WavTokenizer将输入的连续音频特征转换成离散音频token,然后利用基于Qwen模型初始化的自回归Transformer模型预测音频token,再由CFM扩散模型重建音频的潜层特征,最终通过Vocoder输出高质量的音频波形。两种推理模式的设计:fast模型和高音质模型,为不同需求的用户提供了灵活的选择。

    工具包安装使用指南

    第一步:下载代码库

    git clone --recursive https://github.com/FunAudioLLM/InspireMusic.git
    # If you failed to clone submodule due to network failures, please run the following command until success
    cd InspireMusic
    git submodule update --init --recursive

    第二步:安装代码库

    conda create -n inspiremusic python=3.8
    conda activate inspiremusic
    cd InspireMusic
    # pynini is required by WeTextProcessing, use conda to install it as it can be executed on all platforms.
    conda install -y -c conda-forge pynini==2.1.5
    pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com
    # install flash attention to speedup training, support version 2.6.3
    pip install flash-attn --no-build-isolation

    第三步:下载模型

    InspireMusic-Base模型(https://www.modelscope.cn/iic/InspireMusic)
    # git模型下载,请确保已安装git lfs
    mkdir -p pretrained_models
    git clone https://www.modelscope.cn/iic/InspireMusic.git pretrained_models/InspireMusic-Base

    第四步:基本用法说明快速开始

    cd InspireMusic/examples/music_generation/
    bash run.sh

    训练LLM和flow matching模型样例脚本。

    torchrun --nnodes=1 --nproc_per_node=8 \
        --rdzv_id=1024 --rdzv_backend="c10d" --rdzv_endpoint="localhost:0" \
        inspiremusic/bin/train.py \
        --train_engine "torch_ddp" \
        --config conf/inspiremusic.yaml \
        --train_data data/train.data.list \
        --cv_data data/dev.data.list \
        --model llm \
        --model_dir `pwd`/exp/music_generation/llm/ \
        --tensorboard_dir `pwd`/tensorboard/music_generation/llm/ \
        --ddp.dist_backend "nccl" \
        --num_workers 8 \
        --prefetch 100 \
        --pin_memory \
        --deepspeed_config ./conf/ds_stage2.json \
        --deepspeed.save_states model+optimizer \
        --fp16
    
    torchrun --nnodes=1 --nproc_per_node=8 \
        --rdzv_id=1024 --rdzv_backend="c10d" --rdzv_endpoint="localhost:0" \
        inspiremusic/bin/train.py \
        --train_engine "torch_ddp" \
        --config conf/inspiremusic.yaml \
        --train_data data/train.data.list \
        --cv_data data/dev.data.list \
        --model flow \
        --model_dir `pwd`/exp/music_generation/flow/ \
        --tensorboard_dir `pwd`/tensorboard/music_generation/flow/ \
        --ddp.dist_backend "nccl" \
        --num_workers 8 \
        --prefetch 100 \
        --pin_memory \
        --deepspeed_config ./conf/ds_stage2.json \
        --deepspeed.save_states model+optimizer

    推理脚本

    cd InspireMusic/examples/music_generation/
    bash infer.sh

    带有CFM的推理模式

    pretrained_model_dir = "pretrained_models/InspireMusic/"
    for task in 'text-to-music' 'continuation'; do
      python inspiremusic/bin/inference.py --task $task \
          --gpu 0 \
          --config conf/inspiremusic.yaml \
          --prompt_data data/test/parquet/data.list \
          --flow_model $pretrained_model_dir/flow.pt \
          --llm_model $pretrained_model_dir/llm.pt \
          --music_tokenizer $pretrained_model_dir/music_tokenizer \
          --wavtokenizer $pretrained_model_dir/wavtokenizer \
          --result_dir `pwd`/exp/inspiremusic/${task}_test \
          --chorus verse \
          --min_generate_audio_seconds 8 \
          --max_generate_audio_seconds 30 
    done

    不带CFM的fast推理模式

    pretrained_model_dir = "pretrained_models/InspireMusic/"
    for task in 'text-to-music' 'continuation'; do
      python inspiremusic/bin/inference.py --task $task \
          --gpu 0 \
          --config conf/inspiremusic.yaml \
          --prompt_data data/test/parquet/data.list \
          --flow_model $pretrained_model_dir/flow.pt \
          --llm_model $pretrained_model_dir/llm.pt \
          --music_tokenizer $pretrained_model_dir/music_tokenizer \
          --wavtokenizer $pretrained_model_dir/wavtokenizer \
          --result_dir `pwd`/exp/inspiremusic/${task}_test \
          --chorus verse \
          --fast \
          --min_generate_audio_seconds 8 \
          --max_generate_audio_seconds 30 
    done

    FireRedASR -小红书语音识别大模型

    小红书 FireRed 团队正式发布并开源了基于大模型的语音识别模型 ——FireRedASR,在语音识别领域带来新突破。在业界广泛采用的中文普通话公开测试集上,FireRedASR 凭借卓越的性能取得了新 SOTA!FireRedASR 在字错误率(CER)这一核心技术指标上,对比此前的 SOTA Seed-ASR,错误率相对降低 8.4%,充分体现了团队在语音识别技术领域的创新能力与技术突破。

    FireredAsr,旨在满足各种应用程序中出色的性能和最佳效率的各种要求。 fireredasr包括两个变体:

    FireRedASR-LLM

    采用Encoder-Adapter-LLM,结合了文本预训练 LLM 的能力,为极致的 ASR 准确率而生,适用于对准确率要求极高的应用场景。在公共普通话基准上,fireredasr-LLM (8.3b参数)达到3.05%的平均字符错误率(CER),超过了3.33%的最新SOTA,相对CER(CERR)8.4%。它显示出优于工业级基线的卓越概括能力,在多源普通话ASR方案(例如视频,现场和智能助理)中,达到24%-40%的CERR。

    FireRedASR-AED

    基于经典的 Attention-based Encoder-Decoder 架构,FireRedASR-AED 通过扩展参数至 1.1B,成功平衡了 ASR 语音识别的高准确率与推理效率。适用于资源受限的应用程序。

    主要贡献

    • High-Accuracy Models with Efficiency: ASR识别准确率优于Seed-ASR[字节跳动],模型在保持效率的同时达到卓越精度的能力。
    • Robust Real-World Performance: 在各种实用的场景中,包括简短的视频,直播,字幕生成,语音输入和智能助手,我们的模型表现出了出色的功能,与相比的相对减少(CERR)相比实现了24%-40%流行的开源基线和领先的商业解决方案。
    • 多功能识别能力:支持方言/中文/英文/歌曲识别。而且在歌词识别中表现出色

    模型结构:

    FireRedASR-AED是基于注意的编码器-解码器 ASR模型。训练数据:包括大约70,000小时的音频数据,主要是高质量的普通话语音。与Whisper中使用的弱标记数据集不同,我们的大多数数据都是由专业注释者手动转录的,从而确保了高转录精度和可靠性。该数据集还包含大约11,000小时的英语语音数据,以增强英语ASR功能。

    Input Features: 输入25ms窗口的80-dimensional  log Mel filterbank (Fbank),10ms frame shifts,然后是全局均值和方差归一化。

    Encoder Structure:编码器由两个主要组件组成:一个下采样模块和Conformer  blocks堆叠。

    Decoder Structure:解码器遵循Transformer 体系结构。

    Tokenization:BPE编码英文文本, 1,000 English BPE tokens, 6,827 Chinese characters, and 5 special tokens.

    FireRedASR-LLM: Encoder-Adapter-LLM 架构。

    Input Features and Encoder: 训练数据和处理、encoder跟FireredAsr-AED相同。

    Adapter Structure:一个简单但有效的线性RELU线性网络组成,该网络投射了编码器的输出维度,以匹配输入LLM。在适配器的开头合并了一个额外的框架剪接操作。此操作进一步将时间分辨率从40ms降低到每个帧的80ms,从而降低了序列长度并提高了计算效率LLM。

    LLM初始化和处理:LLM用QWEN2-7B-INSTRUCT的预训练的重量初始化。训练数据格式:(prompt, speech, transcript)

    Training Strategy编码器和适配器是完全训练的,LLM采用lora微调,保证LLM的文本能力。此策略可确保编码器和适配器经过充分训练,以将语音特征映射到LLM的语义空间中,同时保留其预训练能力。训练目标基于交叉熵损失,损失仅在输入的转录部分上计算,忽略提示和语音嵌入。

    Evaluation

    缩放定律的观察

    LLMs 方面的最新研究表明,模型性能通常会随着模型尺寸的增加而提高,这称为缩放定律 。如表3所示,我们研究了具有不同模型大小的模型的缩放行为。对于 FireRedASR-AED,我们将模型大小逐步从 140M、413M、732M 扩展到 1.1B 参数。随着模型尺寸的增加,性能持续提高,从 XS 扩展到 S、从 S 扩展到 M 以及从 M 扩展到 L 配置时分别实现 6.1%、5.3% 和 5.6% 的 CERR。对于 FireRedASR-LLM,专注于扩展编码器,同时保持 LLM 主干不变。编码器大小从 86M 增加到 710M 参数,适配器参数的变化很小(17M 到 22M)。这表现出相似的扩展模式并带来一致的性能改进,从 XS(3.29%)到 L(3.05%)配置的总体 CERR 为 7.3%。这些结果证明了我们的扩展策略的有效性,并表明通过更大的模型容量可以进一步改进。

    下图是 FireRedASR 和其他 ASR 大模型的对比,在业界常用的中文普通话公开测试集上,FireRedASR-LLM(8.3B 参数量)取得了最优 CER 3.05%、成为新 SOTA!FireRedASR-AED (1.1B 参数量)紧随其后取得 3.18%,两者均比 Seed-ASR(12+B 参数量)的 3.33% 低、并且参数量更小。FireRedASR 也比 Qwen-Audio、SenseVoice、Whisper、Paraformer 取得了更优的 CER。

    FireRedASR 不仅在公开测试集上表现优异,在多种日常场景下,也展现了卓越的语音识别效果。
    如下图所示,在由短视频、直播、语音输入和智能助手等多种来源组成的 Speech 测试集上,与业内领先的 ASR 服务提供商(ProviderA)和 Paraformer-Large 相比, FireRedASR-LLM 的 CER 相对降低 23.7%~40.0%,优势十分明显。
    值得一提的是,在需要歌词识别能力的场景中,FireRedASR-LLM 也表现出极强的适配能力,CER 实现了 50.2%~66.7% 的相对降低,这一成果进一步拓宽了 FireRedASR 的应用范围,使其不仅能胜任传统语音识别需求,还能在创新性的多媒体场景中大放异彩。

    值得一提的是,FireRedASR 在中文方言和英语场景中同样表现不俗。在 KeSpeech(中文方言)和 LibriSpeech(英语)测试集上,FireRedASR 的 CER 显著优于此前的开源 SOTA 模型,使其在支持好普通话 ASR 的前提下,在中文方言和英语上也足够通用,进一步凸显了其鲁棒的语言适配能力。

    Discussion:

    FireredAsr模型优于竞争模型的原因:

    高质量和多样化的训练数据:语料库主要由从现实世界情景中收集的专业转录音频组成,该音频比在受控环境中提供的传统阅读式录音相比,它提供的训练信号明显更高。该数据集包括声音条件,扬声器,重音和内容域的广泛差异,总计数万小时。这种多样性和规模使我们的模型能够学习强大的语音表征和语言模式。

    实证研究表明,一千小时的高质量,人工标注的数据比一万小时的弱标记数据(例如,来自视频标题,OCR结果或其他ASR模型的输出)更好的结果,这解释了我们比Whisper的优势 。此外,在我们的语料库中包含唱歌数据为处理音乐内容时的基线模型的显着改进做出了贡献。

    优化的训练策略:将FireredAsr-A的扩展为140m到1.1b参数时,我们将正则化和学习率确定为影响模型收敛的关键因素。我们制定了一种渐进式正则化训练策略:最初没有正则化技术以实现快速收敛,然后逐渐引入更强的正则化,因为出现了过度拟合的趋势。此外,较大的模型需要降低学习率,这对于调整此参数的最佳性能至关重要。

    高效的ASR框架

    总结:提出了fireredasr-LLM和FireredAsr-AED,两种针对普通话优化的高性能ASR模型。通过全面的评估,我们证明了他们的体系结构,培训策略和高质量的数据集可以在保持计算效率的同时达到最先进的性能。

    Baichuan-Omni-1.5 多模态语音大模型

    Baichuan-Omni-1.5 🤗 | Baichuan-Omni-1.5-Base 🤗 | 技术报告 📖

    OpenMM-Medical 🤗 | OpenAudioBench 🤗

    Github: https://github.com/baichuan-inc/Baichuan-Omni-1.5/

    Baichuan-Omni-1.5 是 Baichuan-omni 系列的最新、性能一流模型。该模型通过端到端方式训练和推理。与其他开源模型相比,Baichuan-Omni-1.5 在文本、图像、音频和视频输入的理解能力有显著提升,并支持了可控的实时语音对话和多模态实时交互的新功能。此外,Baichuan-Omni-1.5 也是目前最出色的开源医疗多模态模型。Baichuan-Omni-1.5 的主要特性包括:

    • 多模态理解和交互能力: Baichuan-Omni-1.5 接受图像、视频、文本、音频作为输入,并生成高质量文本和语音输出,能够在不损害任何模态能力的情况下实现无缝的高质量跨模态交互,并和用户进行实时语音对话。在针对全模态理解的综合评测基准 OminiBench 中,Baichuan-Omni-1.5 取得开源社区一流水平,并超过了 GPT-4o-mini
    • 优秀的视觉能力: Baichuan-Omni-1.5 在 OpenCompass 常用的十个视觉评测集上平均得分 73.3,在7B量级的大小下,在图像理解方面超越了 GPT-4o-mini,比GPT-4o-mini平均高出6分,并且和GPT-4o的结果相近。此外,视频理解表现也优于GPT-4V
    • 出色的语音能力: Baichuan-Omni-1.5 通过一个 8 层 RVQ 音频Tokenizer(Baichuan-Audio-Tokenizer),在 12.5 Hz 帧率下实现了语义和声学信息捕获的最佳平衡,支持高质量可控制声音的中英双语实时对话。Baichuan-Omni-1.5 在语音理解任务优于 GLM-4-Voice,并在语音对话的语义和声学评估中展现了开源模型中最高的语音生成性能。同时,我们还开源了音频理解和生成基准(OpenAudio-Bench),以评估音频的端到端能力。
    • 领先的医疗图像理解能力: 我们从开源数据集中收集了一个比较全面的医学理解基准(OpenMM-Medical)用于评估模型的医学能力。Baichuan-Omni-1.5 在 GMAI-MMBench 以及 OpenMM-Medical 上取得了最佳的表现。在 OpenMM-Medical 上,Baichuan-Omni-1.5 仅使用 7B 的 LLM 取得了 83.8% 的高分,超过 Qwen2-VL-72B 的 80.7%。
    在图像、视频和音频模态上的评估。(左)Baichuan-Omni-1.5覆盖了比Qwen2 VL更多的模态,并且超越了当前领先的全模态模型VITA-1.5和MiniCPM-o 2.6。(右)各模态在所有基准测试中的平均得分。

    简介

    Baichuan-Omni-1.5是一款全模态模型,具备全模态理解能力,并提供端到端的音频生成能力。为了实现跨模态的流畅高质量互动,同时不妥协任何模态的能力,优先优化了三个关键方面。首先,建立了一个全面的数据清洗和合成管道,用于多模态数据处理,获得了约5000亿条高质量数据(包括文本、音频和视觉数据)。其次,设计了一种音频分词器(Baichuan-Audio-Tokenizer),能够同时捕捉音频中的语义和声学信息,从而实现无缝集成,并提升与多模态大语言模型(MLLM)的兼容性。最后设计了一个多阶段的训练策略,逐步整合多模态对齐和多任务微调,确保各模态之间的有效协同。Baichuan-Omni-1.5在全模态能力方面领先于当前的模型(包括GPT4o-mini和MiniCPM-o 2.6)。值得注意的是,在多个多模态医学基准测试中,它取得了与领先模型(如Qwen2-VL-72B)相媲美的成绩。

    与开源对手相比,Baichuan-Omni-1.5在文本、图像、音频和视频输入的理解能力上表现出显著的提升。特别地,该模型在可控实时语音互动和跨模态实时协同理解方面展示了令人印象深刻的能力。除了其通用能力外,Baichuan-Omni-1.5在医疗领域作为最出色的多模态大语言模型(MLLM)脱颖而出。这为人工智能在促进人类社会福祉方面开辟了令人兴奋的新可能性。Baichuan-Omni-1.5的架构如下图所示。根据评估结果,我们总结了Baichuan-Omni-1.5的关键优势和贡献:

    全模态互动:Baichuan-Omni-1.5旨在处理文本、图像、音频和视频输入,输出高质量的文本和语音。它能够实现无缝、高质量的跨模态互动,且不会妥协任何模态的能力。

    卓越的视觉-语言能力:Baichuan-Omni-1.5在十个图像理解基准测试中平均得分为73.3,超越GPT-4o-mini平均6分。

    统一且出色的语音能力:我们设计了一种8层的RVQ音频分词器(Baichuan-Audio-Tokenizer),在捕捉语义和声学信息之间达到了最佳平衡,帧率为12.5Hz,支持高质量的可控双语(中文和英文)实时对话。同时,我们还开源了音频理解和生成基准(OpenAudioBench),用于评估音频的端到端能力。

    领先的医学图像理解能力:我们收集了一个全面的医学理解基准:OpenMM-Medical,这是多个现有数据集的整合。我们的模型在GMAI-MMBench和OpenMM-Medical上取得了最先进的表现。具体来说,在OpenMM-Medical上,Baichuan-Omni-1.5使用7B大语言模型取得了83.8%的得分,超过了Qwen2-VL-72B的80.7%。

    模型架构:

    Baichuan-Omni-1.5 的架构。我们的模型旨在处理纯文本/音频输入以及视频/图像与文本/音频的组合。在生成音频时,Baichuan-Omni-1.5 LLM Decoder 交替预测文本标记和音频标记。然后,音频解码器对音频令牌进行解码,以生成最终音频。

    Baichuan-Omni-1.5

    高质量预训练数据

    Baichuan-Omni-1.5的预训练数据集展示:我们构建了一个广泛的全模态数据集,包括文本、图像-文本、视频-文本、音频-文本及它们之间的交互。我们的数据集还包含了交织的图像-音频-文本和视频-音频-文本数据。

    构建了全面且高质量的跨模态数据集,包含文本、图像-文本、视频-文本、音频-文本及它们之间的交互

    图像数据:我们将图像训练数据分为三种类型:交织的图像-文本数据、图像描述数据和问答数据。为了提高数据的多样性并提升模型性能,我们采用了以下两种图像数据合成策略:

    1)我们利用公司内收集的书籍和论文,并解析它们生成交织的图像-文本、OCR数据和图表数据。这些数据具有高度的完整性、专业性和知识密集性。

    2)我们根据[19]的方案,训练了一个专用的描述模型,能够生成所需的图像描述,如OCR提示。这些描述提供了图像内容的深入说明。

    3)目前,大量的开源数据集主要是英文数据。为了避免模型的中文能力下降,我们合成了大量的中文描述和交织数据。

    视频数据:视频数据集包含了广泛的公开资源,涵盖了视频分类 、动作识别 和时间定位等多种任务。视频-文本数据源可以分为视频描述数据和视频问答(QA)数据。

    音频数据:音频数据可以大致分为两种主要类型:音频理解数据和音频生成数据。音频理解数据包括自动语音识别(ASR)、音频问答(AQA)、语音转文本翻译和音频-文本交织数据。音频生成数据包括文本转语音(TTS)、交织的文本转语音数据和纯音频数据。交织数据由文本和音频模态交替组成,并以标点符号分隔,以促进跨模态知识的迁移。完全对齐的生成数据由完全对齐的文本和音频内容组成,旨在增强模型在文本监督下生成音频标记的能力。音频-文本配对数据(例如ASR和TTS数据)提高了基础语音任务的表现。纯音频数据则增强了独立处理音频模态的能力。

    文本数据:为了构建一个高质量的文本语料库,我们从各种来源聚合了数据,包括网页、书籍、学术论文、代码等。我们遵循之前研究中的数据处理指南,采用严格的选择方法,旨在提高文本语料库的多样性和质量。这种多样性确保训练语料库涵盖了广泛的主题和语言风格,适用于不同的应用。同时,我们的高质量处理技术旨在消除冗余和过滤噪声,从而丰富数据集的信息密度和整体效用。最终,我们获得了1.507亿条纯文本数据。

    跨模态交互数据:为了增强模型的跨模态交互能力,我们合成了一系列跨模态交互数据集,涵盖了图像-音频-文本视频-音频-文本格式。图像-文本数据来源包括两种类型:图像-文本描述数据和图像-文本交织数据。具体而言,文本数据首先在句子级别进行分割。然后,将四分之一的文本通过我们的内部文本转语音(TTS)接口转换为音频元素。随后,我们利用生成的音频元素替换原始图像-文本数据中的相应文本句子。这种方法通过将多样化的音频元素融入现有的文本内容,促进了丰富的跨模态交互框架。我们的音频数据包含44种不同的语音类型,确保了语调的多样性。此设置配有任务提示,如“请听以下音频,描述图像的内容。您的任务是在听完后结合音频和图像补充额外信息”,旨在预测剩余的三分之二文本描述。对于视频-文本数据集,音频成分直接从原始视频中提取,作为跨模态的音频元素。总计,我们生成了1000亿个用于跨模态交互的标记数据。

    模型结构:

    Baichuan-Omni-1.5是一个统一的全模态模型,由视觉分支、音频分支和一个预训练的大型语言模型(LLM)主干组成,支持文本、音频、视觉输入以及端到端的文本和音频输出。

    视觉分支
    像当前主流的MLLM一样,视觉分支旨在将图像和视频输入处理为视觉标记,并将其与文本标记一起输入到LLM中。我们使用Qwen2-VL的NaViT作为视觉编码器,该编码器能够动态处理任意分辨率和纵横比的图像和视频。然后,我们应用一个由两层MLP组成的视觉投影器,将视觉特征压缩为2×2的因子,从而在性能和效率之间取得平衡。

    音频分支
    音频分支扩展了LLM,使其能够支持端到端的语音输入和输出。这通过引入Baichuan-Audio-Tokenizer和基于流匹配的解码器来实现,前者负责将音频信号转换为离散标记,后者负责将音频标记解码为语音波形。我们在下图中展示了详细信息。

    Baichuan-Audio-Tokenizer基于残差向量量化(RVQ)和多目标训练,帧率为12.5 Hz。在使用Whisper Large Encoder 从Mel谱图特征中提取高级特征后,残差卷积网络执行下采样以获得低帧率序列特征。然后使用8层残差向量量化器对这些特征进行量化,生成音频标记。这些标记随后被输入到音频解码器和预训练的LLM中,分别执行Mel谱图重建和转录预测。音频解码器采用与Whisper编码器对称的结构,并使用多尺度Mel损失来增强声音重建的质量。在训练过程中,预训练LLM的参数保持不变,以确保音频标记器和文本空间之间的语义对齐。

    除了传统的任务如ASR、AQA和S2TT外,我们还将一定比例的交织文本-音频数据融入其中,以提高VQ模块建模复杂上下文场景的能力。

    为了进一步增强合成音频的质量和感知逼真度,音频解码器模块通过流匹配模型进行优化。借鉴Matcha-TTS 和CosyVoice 的设计,U-Net包括一个单独的下采样块、一个上采样块和12个中间块。具体而言,流匹配解码器在24 kHz音频数据上进行训练,以生成目标Mel谱图,然后使用HiFi-GAN 声码器将其转换为语音波形。

    多阶段模型训练:

    图像-文本预训练

    图像-文本预训练阶段扩展了LLM,使其能够处理和理解视觉输入,使用3000亿图像-文本样本,该阶段可以分为两个部分。

    第一阶段:在第一阶段,我们训练视觉投影器,利用开源图像描述数据(例如LAION-5B数据集),建立图像表示与文本之间的初步对齐。在此阶段,我们冻结LLM和视觉编码器,仅训练视觉投影器,学习率为1e−3。

    第二阶段:在第二阶段,我们解冻视觉编码器和LLM,以促进图像和文本表示之间更好的对齐。具体来说,我们以学习率1e−5训练LLM和视觉投影器,并以更低的学习率1e−6训练视觉编码器。我们使用公共和内部图像文本数据,包含交织数据和图像描述数据,以增强视觉-语言的表现力。具体来说,我们收集并标注高质量的OCR数据和图表数据,以增强文本/图表识别和理解能力。此外,我们还使用高质量的纯文本数据,这些数据占总数据的40%,以更好地保持语言模型的原始能力。

    图像-音频-文本预训练

    图像-音频-文本预训练阶段扩展了一个预训练在视觉数据上的LLM,使其能够以端到端的方式理解音频数据,使用887k小时的语音-文本数据,并结合我们的Baichuan-Audio-Tokenizer、新引入的音频嵌入层和独立音频头

    具体来说,Baichuan-Audio-Tokenizer生成的音频令牌首先通过音频嵌入层转化为音频嵌入。音频LLM交替生成对齐的文本令牌和音频令牌,使用一个特殊令牌实现文本与音频之间的模态切换。生成的音频令牌由独立的音频头处理,该音频头基于先前的工作设计,包含3层深度变换器和8个分类头。

    为了缓解语音和文本特征之间显著差异带来的冲突,我们参考了之前的工作,采用音频和文本数据交织的方法进行预训练。此外,采用了两阶段训练策略,以保持原始LLM的文本知识,同时有效地整合音频模态。

    第一阶段:在第一阶段,我们冻结LLM、视觉模块和音频标记器的参数,只有音频嵌入层和音频头的参数更新,学习率为1e−4。我们在这一阶段使用包括ASR、TTS、INTLV和ITTS数据的音频数据。

    第二阶段:在第二阶段,训练扩展到除视觉编码器和音频标记器之外的所有参数,学习率为1e−5。具体来说,我们使用音频数据、图像数据和纯文本数据,分别占比0.2、0.4和0.4,这可以更好地提升音频能力,同时保持视觉和语言能力。

    全模态预训练

    基于之前预训练阶段获得的视觉和音频能力,我们继续使用高质量的跨模态交互数据集进行训练,数据集涵盖图像-音频-文本和视频-音频-文本格式,并将最大序列长度扩展到64k,以支持长时间的语音和视频流。具体来说,输入的视频帧以每秒1帧的速率进行采样,每个视频最多包含32帧。每个输入帧被调整为最大分辨率560×1120像素,以保持最佳质量和细节。这一精心配置在性能和效率之间达到了平衡,促进了有效的模型训练,同时管理了计算负载。此训练过程使用4e−6的低学习率,以进一步优化与语言模态和跨模态交互的对齐。

    多模态监督微调

    在本节中,我们描述了全模态监督微调(SFT)阶段,旨在增强模型在各种任务中执行复杂全模态指令的能力。我们收集了包括开源、合成和内部注释数据在内的综合数据集。这些数据集涵盖了多个任务,包含大约1700万个数据对,跨越文本、音频、图像-文本、视频-文本和图像-音频组合等多种模态。关于这些数据类型和数量的详细信息见表4。

    实验

    如表6所示,Baichuan-Omni-1.5 在纯文本基准测试中表现出色,特别是在与仅专注于语言模态的开源LLM模型相比时。例如,在通用MMLU基准测试中,Llama3-Instruct的得分为67.1%,而Baichuan-Omni-1.5则达到了72.2%。Baichuan-Omni-1.5在语言模态上的成功,主要归功于我们在训练策略上的调整以及多模态训练数据的平衡比例,其中保持了适当比例的纯文本数据。这些结果表明,我们的数据合成与平衡方法,以及多阶段的训练策略,能够有效解决在多模态训练过程中纯语言任务性能下降的问题。此外,相较于最新的开源多模态模型MiniCPM-o 2.6,Baichuan-Omni-1.5在中文基准测试中展示了明显的优势,例如CMMLU(63.3%对75.5%)和C-Eval(61.5%对73.1%),并且在通用基准测试中也大大超过了MiniCPM-o 2.6,MMLU(65.3%对72.2%)和AGIEval(50.9%对54.4%)。这些结果表明,相较于当前的全模态模型,这些模型在训练非文本模态数据后可能会导致文本理解能力下降,而我们的模型在理解纯文本方面依然保持强大能力。

    如表7和表8所示,显然,我们的模型在大多数基准测试中优于最新的开源模型VITA-1.5和MiniCPM-o 2.6。例如,与最近的MiniCPM-o 2.6相比,我们的模型在包括MMBench、SEED-IMG、MME和MMMU在内的十个基准测试中的六个上表现更好,这些基准测试要求具备专家级的感知和推理能力。这表明,我们的全模态模型已经处于开源模型的前沿。此外,与其他非全模态模型相比,Baichuan-Omni-1.5也取得了相当或更优的表现。例如,与MiniCPM-Llama3-V 2.5相比,我们的模型在大多数视觉问答(VQA)任务中表现更好。总体而言,与Qwen2-VL-7B相比,我们的模型在各类图像理解基准测试中的表现相当。我们的模型在MMBench-CN(81.9%对83.6%)、MMMU(52.7%对53.9%)、MathVista-mini(58.2%对63.6%)和ChartQA(83.0%对84.9%)等方面取得了更好的表现。此外,值得注意的是,在MMBench-EN/CN和OCRBench上,我们的模型已经超越了像GPT4o这样的闭源模型。

    总结

    在这项工作中,我们介绍了Baichuan-Omni-1.5,一个全模态模型,代表了朝着开发一个涵盖所有人类感官的综合框架迈出的重要一步。通过使用高质量的多模态数据以及多阶段的全模态预训练和微调策略,Baichuan-Omni-1.5在处理视频、图像、文本和音频理解方面取得了优异的表现。Baichuan-Omni-1.5的关键特点包括:(1) 强大的纯文本和多模态理解能力;(2) 全模态输入(文本、图像、视频、文本)和双模态输出(文本和音频)的端到端并行处理;(3) 在医疗场景中的卓越表现;以及(4) 高质量的可控音频生成。

    尽管这些结果很有前景,但每种模态的基础能力仍有相当大的改进空间。即:(1) 增强文本理解能力;(2) 支持更长的视频帧理解;以及(3) 改进音频理解和生成,不仅能识别人类声音,还能识别自然环境中的声音,如流水声、鸟鸣声和碰撞声等。

    我们的未来研究将专注于完善这些领域,以确保开发出更复杂、更通用的模型,能够理解和与复杂环境互动。我们预计,在这些领域的持续进展将对实现人工通用智能(AGI)的更广泛目标做出重要贡献。

    Kokoro TTS:支持多语言的轻量级TTS模型

    最近,HuggingFace模型趋势榜上有一个很火的开源模型Kokoro-82M

    开源模型:https://huggingface.co/hexgrad/Kokoro-82M

    Kokoro-82M不是大模型,而是一个参数量只有82M的TTS(Text-to-Speech)模型。虽然模型不大,但是Kokoro-82M在TTS Arena榜单上排行第一!TTS Arena 是一个用于评估语音合成模型的平台,其灵感来源于 LMsys 的 Chatbot Arena。用户可以通过输入文本并对比两个模型的合成语音来投票选择更自然的结果,模型名称在投票后才会显示。该平台旨在解决语音合成领域缺乏有效质量评估方法的问题,通过公开排名使结果更透明且易于访问。虽然这个排行榜很有参考意义。

    在人工智能语音合成技术快速发展的今天,Kokoro TTS 以其轻量级设计和高效性能脱颖而出。作为一个仅有82M参数的文本转语音(TTS)模型,Kokoro 在 TTS Spaces Arena 中击败了许多参数规模更大的竞争对手,成为语音合成领域的一颗新星。

    模型架构与参数规模

    Kokoro TTS 基于 StyleTTS 2 架构,其参数规模仅为82M,远低于许多主流 TTS 模型(如 XTTS v2 的467M 参数和 MetaVoice 的1.2B 参数),但在单声道设置下表现卓越。

    支持的语音与语言

    Kokoro 最新版(0.23)支持多语言支持与声音克隆,包括:中、英、法、日、韩。每种语言支持多种音色以及男、女声,每种语音包都经过专业调校,确保音质清晰自然。英语支持美国英语和英国英语,并提供了10种独特的语音包,包括男声和女声(如 af_bella、af_sarah、am_adam 等)。

    不过还不支持中文或韩文中与英语混合的情况。

    性能优势与创新点

    Kokoro 的训练数据量不到100小时,远低于其他模型(如 XTTS v2 的10,000小时),但其在 TTS Spaces Arena 中排名第一,证明了其在参数效率上的优势。此外,Kokoro 采用 espeak-ng 进行字形到音素(g2p)转换,进一步提升了语音合成的自然度。

    本地部署步骤

    模型地址:https://huggingface.co/hexgrad/Kokoro-82M

    以下步骤为notebook中使用

    # 1️⃣ Install dependencies silently
    !git lfs install
    !git clone https://huggingface.co/hexgrad/Kokoro-82M
    %cd Kokoro-82M
    !apt-get -qq -y install espeak-ng > /dev/null 2>&1
    !pip install -q phonemizer torch transformers scipy munch
    
    # 2️⃣ Build the model and load the default voicepack
    from models import build_model
    import torch
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    MODEL = build_model('kokoro-v0_19.pth', device)
    VOICE_NAME = [
        'af', # Default voice is a 50-50 mix of Bella & Sarah
        'af_bella', 'af_sarah', 'am_adam', 'am_michael',
        'bf_emma', 'bf_isabella', 'bm_george', 'bm_lewis',
        'af_nicole', 'af_sky',
    ][0]
    VOICEPACK = torch.load(f'voices/{VOICE_NAME}.pt', weights_only=True).to(device)
    print(f'Loaded voice: {VOICE_NAME}')
    
    # 3️⃣ Call generate, which returns 24khz audio and the phonemes used
    from kokoro import generate
    text = "How could I know? It's an unanswerable question. Like asking an unborn child if they'll lead a good life. They haven't even been born."
    audio, out_ps = generate(MODEL, text, VOICEPACK, lang=VOICE_NAME[0])
    # Language is determined by the first letter of the VOICE_NAME:
    #    'a' => American English => en-us
    #    'b' => British English => en-gb
    
    # 4️⃣ Display the 24khz audio and print the output phonemes
    from IPython.display import display, Audio
    display(Audio(data=audio, rate=24000, autoplay=True))
    print(out_ps)

    API 接口与 Docker 化部署

    Kokoro-FastAPI 是一个基于 Docker 的 FastAPI 封装,支持 NVIDIA GPU 加速和队列处理功能。用户可以通过 API 接口发送文本转语音请求,并获取高质量的语音输出。

    Kokoro-FastAPI地址:https://github.com/remsky/Kokoro-FastAPI