Dolphin -支持东方40语种+中国22方言的新SOTA语音大模型

在当今数字化时代,语音识别技术已成为人机交互的关键桥梁,广泛应用于智能客服、语音助手、会议转录等众多领域。然而,对于东方语言的识别如越南语、缅甸语等,现有模型往往表现不佳,难以满足用户的需求。为解决这一难题,海天瑞声携手清华大学电子工程系语音与音频技术实验室,共同推出了Dolphin —— 一款专为东方语言设计的语音大模型

Dolphin 采用的多任务格式,其主要沿用了 OpenAI Whisper的
格式。Dolphin 专注于自动语音识别 (ASR),不支持翻译任务。此外,Dolphin 引入了特定区域的标记,从而支持方言。

Dolphin 是由 Dataocean AI 与清华大学合作开发的多语言、多任务 ASR 模型。它支持东亚、南亚、东南亚和中东地区的 40 种东方语言,同时还支持 22 种中国方言。该模型基于超过 21 万小时的数据进行训练,其中包括 DataoceanAI 的专有数据集和开源数据集。该模型可以执行语音识别、语音活动检测 (VAD)、语音分割和语言识别 (LID)

二、创新技术架构 

  • 模型结构    

Dolphin网络结构基于CTC-Attention架构,E-Branchformer编码器和Transformer解码器,并引入了4倍下采样层,以实现高效的大规模多语言语音识别模型的训练。CTC-Attention架构结合了CTC的序列建模能力和注意力机制的上下文捕捉能力,能够有效提升模型的识别准确性和效率。E-Branchformer编码器采用并行分支结构,能够更有效地捕捉输入语音信号的局部和全局依赖关系,为模型提供了更丰富的特征表示。解码器部分则采用了在序列到序列任务中表现出色的Transformer,能够生成高质量的文本输出。为了进一步提高训练效率和性能,我们在模型中引入了4倍下采样层。这一层可以减少输入特征的序列长度,从而加速计算过程,同时保留关键的语音信息,确保模型的识别效果不受影响。

  • 多任务格式

Dolphin 借鉴了 Whisper 和 OWSM 的创新设计方法,但专注于ASR 进行了若干关键修改。Dolphin 不支持翻译任务,并且去掉了previous text及其相关标记的使用,这简化了输入格式并减少了潜在的复杂性。Dolphin引入了两级语种标签系统,以便更好地处理语言和地区的多样性。第一个标签指定语种(例如: <zh> 、 <ja>),第二个标签指定地区(例如 <CN> 、 <JP>)。这种分层方法使模型能够捕捉同一种语言内不同方言和口音之间的差异,以及同一地区内不同语言之间的相似性,从而提高了模型区分密切相关的方言的能力,并通过在语言和地区之间建立联系增强了其泛化能力。

三、强大的数据基础 Dolphin的训练数据集整合了海天瑞声的专有数据和多个开源数据集,总时长超过20万小时,涵盖40个东方语种。其中,海天瑞声数据集包含137,712小时的音频,覆盖38个东方语种。这些高质量、多样化的数据为模型的训练提供了坚实的基础,使其能够更好地适应不同语言和方言的语音特征。

四、卓越性能表现 通过精心设计的架构和大规模的训练数据,Dolphin在多种语言上的词错误率(WER)显著低于现有开源模型。

例如,在海天瑞声数据集上,Dolphin 模型的平均WER为31.5%,small模型为24.5%,medium模型为22.2%;在CommonVoice数据集上,Dolphin 模型的平均WER为37.2%,small模型为27.4%,medium模型为25.0%。即使与Whisper large-v3模型相比,Dolphin在模型规模更小的情况下,性能也更为出色。以中文为例,Dolphin中模型的WER仅为9.2%,而Whisper large-v3模型为27.9%。 在KeSpeech (包含一个普通话子集和八个中国方言子集)测试集上,Dolphin模型表现出了卓越的效果.

五、开源与社区贡献 为促进语音识别技术的进一步发展,Dolphin的训练模型和推理源代码已公开发布。这一举措不仅为研究人员提供了宝贵的研究基础,也为开源社区注入了新的活力,鼓励更多创新与合作。通过共享技术成果,我们希望能够吸引更多的开发者和研究机构参与到东方语言语音识别的研究中来,共同推动技术的进步。 

支持的语言列表:

Language code

Language CodeEnglish NameChinese Name
zhMandarin Chinese中文
jaJapanese日语
thThai泰语
ruRussian俄语
koKorean韩语
idIndonesian印度尼西亚语
viVietnamese越南语
ctYue Chinese粤语
hiHindi印地语
urUrdu乌尔都语
msMalay马来语
uzUzbek乌兹别克语
arArabic阿拉伯语
faPersian波斯语
bnBengali孟加拉语
taTamil泰米尔语
teTelugu泰卢固语
ugUighur维吾尔语
guGujarati古吉拉特语
myBurmese缅甸语
tlTagalog塔加洛语
kkKazakh哈萨克语
orOriya / Odia奥里亚语
neNepali尼泊尔语
mnMongolian蒙古语
kmKhmer高棉语
jvJavanese爪哇语
loLao老挝语
siSinhala僧伽罗语
filFilipino菲律宾语
psPushto普什图语
paPanjabi旁遮普语
kabKabyle卡拜尔语
baBashkir巴什基尔语
ksKashmiri克什米尔语
tgTajik塔吉克语
suSundanese巽他语
mrMarathi马拉地语
kyKirghiz吉尔吉斯语
azAzerbaijani阿塞拜疆语

Language Region Code

Language Region CodeEnglish NameChinese Name
zh-CNChinese (Mandarin)中文(普通话)
zh-TWChinese (Taiwan)中文(台湾)
zh-WUChinese (Wuyu)中文(吴语)
zh-SICHUANChinese (Sichuan)中文(四川话)
zh-SHANXIChinese (Shanxi)中文(山西话)
zh-ANHUIChinese (Anhui)中文(安徽话)
zh-TIANJINChinese (Tianjin)中文(天津话)
zh-NINGXIAChinese (Ningxia)中文(宁夏话)
zh-SHAANXIChinese (Shaanxi)中文(陕西话)
zh-HEBEIChinese (Hebei)中文(河北话)
zh-SHANDONGChinese (Shandong)中文(山东话)
zh-GUANGDONGChinese (Guangdong)中文(广东话)
zh-SHANGHAIChinese (Shanghai)中文(上海话)
zh-HUBEIChinese (Hubei)中文(湖北话)
zh-LIAONINGChinese (Liaoning)中文(辽宁话)
zh-GANSUChinese (Gansu)中文(甘肃话)
zh-FUJIANChinese (Fujian)中文(福建话)
zh-HUNANChinese (Hunan)中文(湖南话)
zh-HENANChinese (Henan)中文(河南话)
zh-YUNNANChinese (Yunnan)中文(云南话)
zh-MINNANChinese (Minnan)中文(闽南语)
zh-WENZHOUChinese (Wenzhou)中文(温州话)
ja-JPJapanese日语
th-THThai泰语
ru-RURussian俄语
ko-KRKorean韩语
id-IDIndonesian印度尼西亚语
vi-VNVietnamese越南语
ct-NULLYue (Unknown)粤语(未知)
ct-HKYue (Hongkong)粤语(香港)
ct-GZYue (Guangdong)粤语(广东)
hi-INHindi印地语
ur-INUrdu乌尔都语(印度)
ur-PKUrdu (Islamic Republic of Pakistan)乌尔都语
ms-MYMalay马来语
uz-UZUzbek乌兹别克语
ar-MAArabic (Morocco)阿拉伯语(摩洛哥)
ar-GLAArabic阿拉伯语
ar-SAArabic (Saudi Arabia)阿拉伯语(沙特)
ar-EGArabic (Egypt)阿拉伯语(埃及)
ar-KWArabic (Kuwait)阿拉伯语(科威特)
ar-LYArabic (Libya)阿拉伯语(利比亚)
ar-JOArabic (Jordan)阿拉伯语(约旦)
ar-AEArabic (U.A.E.)阿拉伯语(阿联酋)
ar-LVTArabic (Levant)阿拉伯语(黎凡特)
fa-IRPersian波斯语
bn-BDBengali孟加拉语
ta-SGTamil (Singaporean)泰米尔语(新加坡)
ta-LKTamil (Sri Lankan)泰米尔语(斯里兰卡)
ta-INTamil (India)泰米尔语(印度)
ta-MYTamil (Malaysia)泰米尔语(马来西亚)
te-INTelugu泰卢固语
ug-NULLUighur维吾尔语
ug-CNUighur维吾尔语
gu-INGujarati古吉拉特语
my-MMBurmese缅甸语
tl-PHTagalog塔加洛语
kk-KZKazakh哈萨克语
or-INOriya / Odia奥里亚语
ne-NPNepali尼泊尔语
mn-MNMongolian蒙古语
km-KHKhmer高棉语
jv-IDJavanese爪哇语
lo-LALao老挝语
si-LKSinhala僧伽罗语
fil-PHFilipino菲律宾语
ps-AFPushto普什图语
pa-INPanjabi旁遮普语
kab-NULLKabyle卡拜尔语
ba-NULLBashkir巴什基尔语
ks-INKashmiri克什米尔语
tg-TJTajik塔吉克语
su-IDSundanese巽他语
mr-INMarathi马拉地语
ky-KGKirghiz吉尔吉斯语
az-AZAzerbaijani阿塞拜疆语

GigaSpeech 2:三万小时东南亚多语种语音识别开源数据集发布

下载:https://huggingface.co/datasets/speechcolab/gigaspeech2

语言:泰语、印尼语、越南语
GigaSpeech 2 raw:30,000 小时的泰语、印尼语和越南语自动转录语音。
GigaSpeech 2 精炼:泰语 10,000 小时,印尼语和越南语各 6,000 小时。
GigaSpeech 2 DEV 和 TEST:每种语言的 DEV 时间为 10 小时,TEST 时间为 10 小时,由专业人工注释员转录,富有挑战性和现实性。

“Giga”一词源于“gigantic”[“巨大”],互联网上具有海量音频资源,但语音质量良莠不齐,高质量音频文本对数据十分稀缺且标注成本高昂,特别是在小语种领域。GigaSpeech 是一个非常成功的英文开源数据集,以 YouTube 和 Podcast 为音频来源,提供了上万小时的高质量文本标注语音数据集,获得了广泛关注和应用。针对多语言领域仍存在的语音识别性能较差、可用高质量标注数据缺乏等问题,我们提出了利用 in-the-wild 无标注音频,构建高质量大规模语音识别数据集的新范式,制作出面向真实场景的大规模、多领域、多语言的语音识别数据集 GigaSpeech 2基于Gigaspeech 2 数据集训练的语音识别模型在三个东南亚语种(泰语、印尼语、越南语)上达到了媲美商业语音识别服务的性能。我们怀揣着技术应当普惠大众的理念,致力于开源高质量语音识别数据集和模型,促进多语言文化沟通。

GigaSpeech 2 是一个持续扩展的、多领域多语言的大规模语音识别语料库,旨在促进低资源语言语音识别领域的发展和研究。GigaSpeech 2 raw拥有 30000 小时的自动转录音频,涵盖泰语、印尼语、越南语经过多轮精炼和迭代,GigaSpeech 2 refined拥有 10000 小时泰语、6000 小时印尼语、6000 小时越南语。我们也开源了基于 GigaSpeech 2 数据训练的多语种语音识别模型,模型性能达到了商业语音识别服务水平

数据集构建:

GigaSpeech 2 的制作流程也已同步开源,这是一个自动化构建大规模语音识别数据集的流程,面向互联网上的海量无标注音频,自动化地爬取数据、转录、对齐、精炼。这一流程包含利用 Whisper 进行初步转录,使用 TorchAudio 进行强制对齐,经过多维度过滤制作出 GigaSpeech 2 raw。随后,采用改进的 Noisy Student Training (NST) 方法,通过反复迭代精炼伪标签,持续提高标注质量,最终制作出GigaSpeech 2 refined。

GigaSpeech 2 在主题上涵盖了多样化话题领域,包括农业、艺术、商业、气候、文化、经济、教育、娱乐、健康、历史、文学、音乐、政治、两性关系、购物、社会、体育、科技和旅行。同时,在内容形式上涵盖了多种类型,包含声书、解说、讲座、独白、电影电视剧、新闻、访谈、视频博客。

GigaSpeech 2 raw: Automated Crawling and Transcription

音频收集

由于低资源语言中人工标注数据的稀缺性,我们的数据集采集策略仅关注音频内容,而不考虑是否存在或文本配对的质量。这种策略使我们能够收集更广泛范围的音频数据。考虑到低资源语言的资源稀缺性和分布不均,我们有策略地重点爬取 YouTube 频道中的视频,基于两个关键假设:

  1. 优先选择热门频道可以确保一致的领域特征和音频质量
  2. 不同频道之间没有说话人重叠,从而简化后续的数据划分。

数据收集流程首先由人工定义感兴趣的内容类别,所选主题包括:农业、艺术、商业、气候、文化、经济、教育、娱乐、健康、历史、文学、音乐、政治、人际关系、购物、社会、体育、科技和旅游。除了多样的主题外,我们还考虑了不同的内容格式,包括:有声书、评论、讲座、独白、电影、新闻、访谈和 vlog。这种广泛的选择确保了数据集在多个领域的全面性,可支持研究与分析。

在准备好 YouTube 频道列表后,我们使用 yt-dlp 工具下载所有音频文件,格式为 WebM。随后,这些文件被转换为单声道的 WAV 格式,并重采样为 16 kHz 的采样率。


训练 / 开发 / 测试集的划分:为确保各数据集之间没有说话人重叠,我们通过人工方式验证不同频道间无重叠说话人,并将来自不同 YouTube 频道的数据分配至不同的子集。数据集被划分为三个独立的子集:训练集(TRAIN)、开发集(DEV)和测试集(TEST)。

其中,DEV 和 TEST 集各包含 10 小时内容,均由专业人员手动转录,其余部分则分配至训练集。表1展示了这三种语言的数据量分布。更详细的分析见附录B。


使用 Whisper 进行转录:我们使用 OpenAI 的 Whisper large-v3 模型自动转录音频文件。对于每段音频,从中间选择一个 30 秒的片段进行语言识别,仅对与目标语言匹配的音频进行转录。


使用 TorchAudio 进行强制对齐:虽然 Whisper 可生成时间戳,但经过检验发现其精度不足。因此,我们采用了 TorchAudio 中的强制对齐模型【参考多语言数据的强制对齐 CTC 强制对齐 API 教程】,它能为嘈杂的转录文本提供可靠的对齐,支持在 GPU 上高效处理,并能更好地处理较长的音频序列。


文本标准化:对转录文本进行标准化处理,包括:

  • 应用 Unicode NFKC(兼容性分解与合成)规范;
  • 将所有字符转换为大写;
  • 去除标点符号;
  • 将阿拉伯数字映射为对应语言中的文本数字。

多维度过滤:为了排除质量较差的样本,我们在文本和音频两个模态上设计了一系列启发式的过滤规则:

  • 字符集过滤(Charset Filtering):仅保留那些只包含目标语言字符集内字符的片段。
  • 语言置信度过滤(Language Confidence Filtering):使用 fastText 提供的语言识别(LID)模型,根据语言识别的置信度分数进行过滤,仅保留那些置信度高于预设阈值的片段。该方法能有效排除无意义或重复的内容。需要注意的是,基于音频的语言识别在文本转录之前已经完成。
  • 音频时长过滤(Audio Duration Filtering):根据音频时长进行过滤,仅保留长度在预设的最短和最长时间阈值之间的片段。
  • 样本平衡(Balancing):我们对因频道特定内容造成的转录文本重复进行精细控制,同时尽可能保留自然的语言使用模式。

GigaSpeech 2 精炼:迭代标签优化(Iterative Label Refinement)

由于 Whisper 转录的不准确性以及强制对齐边界不精确,部分样本的质量仍然较低。为了解决这个问题,我们设计了一种改进的 神经自监督训练(NST) 方法。如图 1 右下角所示,该方法以一部分质量不佳的伪标签样本为起点训练一个教师模型,并通过迭代方式不断扩展训练集、生成新的伪标签,并对其进行过滤。随后训练一个与教师模型等大或更大的学生模型,使用优化后的伪标签进行训练,并将其作为新的教师模型。

在每次 NST 步骤中,我们引入了 SpecAugmentBypass特征遮盖(feature mask)来注入噪声。其中:

  • Bypass 是一种随机深度机制,它通过学习通道级的标量权重,在模块输入与输出之间进行加权组合;
  • Feature mask 在前馈层和卷积层的隐藏维度上执行 Dropout,但在时间维度上保持共享。

这种有意识地加入噪声的方式,可以使学生模型学习在有噪声扰动下仍能保持与教师模型一致的行为,而教师模型在生成伪标签时则不会受到这些扰动 。

通过这样的迭代过程,数据质量将逐步得到提升。详细的算法步骤见附录 A 中的算法 1。

数据集组成:

GigaSpeech 2 提供了两个版本的数据集,分别为 raw 和 refined 版本,适用于有监督训练任务。训练集时长详情如下表所示:

GigaSpeech 2 开发集和测试集由海天瑞声的专业人员对语音数据人工标注得到,时长详情如下表所示:

主题和内容分布详情如下图所示,外圈表示主题领域,内圈表示内容形式:

实验结果:

我们将使用 GigaSpeech 2 数据集训练的语音识别模型与业界领先的 OpenAI Whisper (large-v3、large-v2、base)、Meta MMS L1107、Azure Speech CLI 1.37.0 和 Google USM Chirp v2 模型在泰语、印尼语和越南语上进行比较。性能评估基于 GigaSpeech 2、Common Voice 17.0 以及 FLEURS 三个测试集,通过字符错误率(CER)或单词错误率(WER)指标进行评估。结果表明:

1)在泰语上,我们的模型展现出卓越的性能,全面超越了所有竞争对手,包括微软和谷歌商用接口。值得一提的是,我们的模型在达到这一显著成果的同时,参数量仅为 Whisper large-v3 的十分之一。

2)在印尼语和越南语上,我们的系统与现有的基线模型相比表现出具有竞争力的性能。

Baichuan-Auido端到端训练语音交互大模型

Baichuan-Auido 是Baichuan最新的端到端训练的语音交互大模型,无缝集成了音频理解和生成功能,支持高质量可控的中英双语实时对话。

  • Baichuan-Audio-Base: 为促进语音大模型发展,我们开源了使用高质量海量数据训练的端到端语音基座模型。该模型未经SFT指令微调,可塑性强。
  • Baichuan-Audio: 接受文本、音频作为输入,并生成高质量文本和语音输出,能够在保持预训练 LLM 智商能力下实现无缝的高质量语音交互,和用户进行实时语音对话

Introduction

Baichuan-Audio,这是一款为实时语音交互设计的端到端音频大语言模型。与 MoshiGLM-4-Voice [输出 文本+speech token,speech token 接入cosyvoice解码器合成音频 ] 类似,Baichuan-Audio 扩展了预训练的大语言模型(LLM),以支持端到端的音频输入和输出。该模型通过集成 Baichuan-Audio-Tokenizer 和流匹配解码器来实现上述功能,前者将音频信号离散化为音频标记,后者则将音频标记解码为语音波形。

Baichuan-Audio-Tokenizer 的运行帧率为 12.5 Hz,并采用多码本离散化技术,以保留语义和声学信息,从而支持 LLM 中语音模态的有效建模。Baichuan-Audio 还引入了独立的音频头(audio head),以提升模型处理和捕捉音频特征的能力。

我们在包含约 1000 亿个标记的音频-文本数据上进行了大规模预训练。基于包含 88.7 万小时的大型音频语料库,我们采用了交错数据处理方法,以在 LLM 框架内实现高效的知识迁移。

贡献:

  • 统一且卓越的语音能力:我们设计了一个 8 层 RVQ 音频分词器Baichuan-Audio-Tokenizer),在 12.5 Hz 帧率下实现了对语义和声学信息的最佳平衡,支持高质量、可控的中英文双语实时对话。
  • 端到端语音交互Baichuan-Audio 能够处理文本和音频输入,并生成高质量的文本和语音输出,支持无缝的高质量语音交互,同时保持智能响应能力。
  • 开源支持:我们已开源训练数据和基础模型,为语音交互领域的研究与创新提供了宝贵的资源与工具。

模型

该模型结构围绕三大核心组件构建:Baichuan-Audio分词器音频大语言模型(audio LLM),以及音频解码器

处理流程从音频分词器开始,它通过捕捉语义与声学信息,将原始音频输入转化为离散的token。这一步是通过 Whisper编码器残差矢量量化(RVQ)技术相结合实现的。

随后,音频LLM以交替方式生成对齐的文本与音频token,并借助一个特殊的token实现文本与音频模态间的无缝切换。接下来,这些音频token将由一个独立的音频head进一步处理。

最后,模型通过一个基于Flow-Matching的方法的音频解码器,从这些token中重建出高质量的Mel谱图,再通过 声码器(vocoder)将其转换为音频波形。

Audio Tokenization

当前音频分词器面临的主要挑战,在于如何在捕捉语音信号中的语义信息与声学信息之间取得最佳平衡。相较于像 HuBERT 这样的自监督学习方法,Baichuan-OmniQwen-Audio 这类模型在捕捉语义特征方面提供了更直接的路径。【ASR任务

与此同时,像 EncodecSpeechTokenizer这样的音频分词器则在完整重建音频特征方面表现尤为出色。【音频重建任务

为了结合这两类方法的优点,提出了 Baichuan-Audio-Tokenizer:一个基于 残差矢量量化(RVQ)多目标训练【重建音频任务以及ASR任务】 的音频分词器,如图2所示。Baichuan-Audio-Tokenizer 保留了来自 Baichuan-Omni音频编码器语言模型组件,并在编码器之后新增了一个 音频解码器结构,用于重建输入的 Mel 频谱图。该音频分词器采用多目标优化方法进行训练,从而能够有效捕捉输入语音中的语义信息与声学信息。

Baichuan-Audio-Tokenizer 采用了每秒 12.5 个 token 的帧率设计。高层次的音频特征首先通过 Whisper Large 编码器Mel 频谱图(Mel spectrogram) 中提取,随后通过一个 残差卷积网络 进行 4× 下采样,以获得低帧率的音频特征。

由于 Whisper 编码器输出的音频特征是高维的,在量化过程中需尽可能减少信息损失,因此我们使用了 8 层残差矢量量化(RVQ)。我们设计了逐层递减的码本大小,依次为:{8K, 4K, 2K, 1K, 1K, 1K, 1K, 1K}

音频解码器采用与 Whisper 编码器完全对称的结构,通过一个 反卷积模块(deconvolution module) 对输入进行 4× 上采样。之后,序列经过一系列 Transformer 层,并进一步进行 2× 上采样,最终得到每秒 100 个 token 的粗略 Mel 频谱表示。设计了一个 精细化网络,以提升 Mel 频谱重建的精度,最终获得高质量的精细 Mel 频谱特征。

在音频重建损失函数的设计中,我们,采用 L2 损失与 L1 损失的组合 作为重建损失。其形式定义如下:

为提升音频重建的质量,我们引入了一种 多尺度 Mel 频谱损失(multiscale Mel loss) 方法,使用了两种不同的 hop length(帧移)window size(窗长)。该方法有效缓解了在从解码器输出转换为 Mel 频谱过程中,由于降维和下采样插值所导致的信息损失。通过在多个尺度上进行优化,该方法能够保留更多细粒度的音频特征,从而提升重建的保真度和训练的稳定性。

对于预训练的语言模型(LLM),其目标是在音频理解任务中最大化文本输出的 softmax 概率。为了确保语义对齐,我们在训练过程中保持预训练语言模型参数不更新(冻结),仅最大化其在音频理解任务中预测文本的 softmax 概率。这种做法有助于保持音频分词器与文本LLM之间的语义对齐关系

在选择 LLM 的规模时,我们观察到,在音频理解模型的训练过程中,不同规模的 LLM 对于 ASR(自动语音识别)指标的影响很小。因此,我们最终选用了一个拥有 15亿参数(1.5B) 的预训练 LLM 进行持续训练。这个规模的模型与音频解码器在训练过程中表现出良好的匹配性,两者的梯度范数差距较小,有助于提升整体训练的稳定性。

在量化模块的训练中,我们采用了 指数移动平均(EMA) 策略来更新码本,并使用 直通估计器(STE, Straight-Through Estimator) 来反向传播梯度至编码器。此外,我们还使用了 向量量化承诺损失(VQ commitment loss),以确保编码器的输出能够紧密对齐至码本中的条目。

VQ 承诺损失定义如下:

总损失是多尺度重建损失、文本音频对齐损失(对于LLM)和 VQ 承诺损失的加权组合:

训练数据。 除了自动语音识别 (ASR)、音频查询回答 (AQA) 和语音到文本翻译 (S2TT) 等传统任务外,我们还将一定比例的音频文本交错数据纳入训练过程。此策略旨在增强 VQ 模块对复杂上下文场景进行建模的能力。具体来说,训练数据集包括 135k 小时的 ASR 数据、11k 小时的 AQA 数据、9k 小时的 S2TT 翻译数据和 52k 小时的音频文本交错数据。

Evaluation of Baichuan-Audio-Tokenizer. 我们使用相同的数据和基础模型训练了音频理解模型的非 VQ 版本作为基线。对于 VQ 和非 VQ 模型,LLM 的参数在训练期间保持冻结,以确保公平比较并隔离 VQ 机制对整体性能的影响。从表 1 中,我们可以看到 8 层 vq 更接近基线,并且语义内容损失最少。如表 2 所示,8 层 VQ 模型和基线在多个数据集上的 ASR 结果表明,训练后的 8 层 VQ 模型实现了具有竞争力的性能。

基于流匹配的音频解码器

为了提升合成音频的质量与保真度,我们对音频解码模块进行了增强,引入了 Flow-Matching 模型,在 24 kHz 采样率的音频数据上训练,用于生成目标 Mel 频谱图。该 Flow-Matching 解码器 包括两个主要部分:Pre-Net条件解码器(Conditional Decoder),如图 3 所示。

Pre-Net 负责将中间表示映射为供声码器(vocoder)使用的先验分布,结构上由一个 多层感知机(MLP) 和一个 12 层 Transformer 组成:MLP 将输入的 1280维、50 Hz 的音频特征压缩投影至 512维;接着,Transformer 对其进行精细建模;最后通过一个线性层转换为 80维的 Mel 频谱图

条件解码器(Flow-Matching Conditional Decoder)。该部分采用基于 U-Net 的结构,并结合 OT-CFM(Optimal Transport Conditional Flow Matching) 方法进行训练,灵感来源于 Matcha-TTSCosyVoice

U-Net 结构包括:一个下采样模块、一个上采样模块,以及 12 个中间模块,每个模块由一个 ResNet1D 层 和一个 Transformer 层(256维)组成。最终,再通过一个线性层将特征投影为 80维 Mel 频谱图

需要注意的是,由于模型已通过重建损失编码了声学信息(如说话人音色),因此 不需要额外的说话人嵌入(speaker embeddings)。生成的 Mel 频谱图将通过 HiFi-GAN 声码器转换为音频波形。

训练细节: 流匹配模型在约 27 万小时的音频上进行了训练,包括普通话、英语、各种方言和多语言数据。使用集成 ASR 和 MOS 过滤来改善数据质量。在训练期间,AudioEncoder、VQ 层和 AudioDecoder 是固定的,而流匹配 Pre-Net 和解码器则在 Pre-Net 中添加了先验损失进行训练。

重建性能评估

Audio LLM

Baichuan-Audio 通过整合新推出的 Baichuan-Audio-Tokenizer(包括音频嵌入层和独立的音频头)扩展了预训练的 LLM。具体来说,来自 Baichuan-Audio-Tokenizer 的音频标记首先通过音频嵌入层转换为音频嵌入。音频 LLM 交替生成对齐的文本标记和音频标记,并通过特殊标记实现文本和音频之间的模态切换。生成的音频标记由独立的音频头处理,该头由 3 层深度转换器和 8 个分类头组成。最后,音频嵌入通过音频编码器(例如基于流匹配的音频编码器和声码器)以重建音频波形。

音频嵌入。 首先,将 8 个离散音频标记通过相应数量的嵌入层相加以获得音频嵌入。由于包含一个额外的特殊标记来表示音频标记生成的结束,因此每个嵌入层的输入维度都比相应码本的大小大一。

音频头。生成的音频标记使用独立的音频头进行处理,该音频头由 3 层深度转换器和 8 个分类头组成。深度转换器的深度为 8,可预测 8 个码本的音频嵌入。最后,分类头用于获取与音频标记相对应的每个码本的逻辑值。

与纯文本大模型相比,语音语言模型(speech language models) 往往在生成语义连贯的输出方面存在困难。研究 [36] 指出,这一问题主要源于语音中引入了 时长信息(duration)副语言信息(paralinguistic information),例如语调、语气等。

为了解决这一问题,我们在预训练阶段引入了两种类型的交错数据(interleaved data)音频-文本交错数据(INTLV, Audio-Text Interleaved)文本到语音交错数据(ITTS, Interleaved Text-to-Speech)

这两种数据设计有助于提升模型的音频理解与生成能力

在推理阶段,离散音频 token 会被送入 LLM,模型随后以交替的方式生成对齐的 文本 token音频 token。我们引入了特殊 token,用于在文本与音频模态间切换。这种强制对齐策略(forced alignment approach)确保了模型会优先生成连贯完整的文本内容,再生成对应的音频 token,从而有效引导音频 token 的生成,缓解语义退化问题

Pre-training details

数据: 交错数据由交替的文本和音频模态组成,并用标点符号分隔以促进跨模态知识传输。交错对齐的生成数据由完全对齐的文本和音频内容组成,旨在增强模型在文本监督下生成音频标记的能力。音频-文本配对数据(例如,ASR 和 TTS 数据)提高了基本语音任务的性能。另一方面,纯音频数据增强了独立处理音频模态的能力。交错数据收集流程如图 4 所示,分为爬取数据和合成数据两种,共获取了 14.2 万小时的 ITTS 数据和 39.3 万小时的 INTLV 数据。交错数据采用LLM进行切分,即根据文本内容中的标点符号或自然停顿进行自然切分。对于合成数据的切分文本数据,我们还采用了大型语言模型进行文本归一化 。在预训练过程中,我们排除了音频文本交错数据中音频片段的损失计算,这一设计选择与 GLM-4-Voice 不同。 在当前约 50B 的训练音频数据规模下进行的经验观察表明,计算 INTLV 数据中音频片段的损失会导致性能下降。这一决定的合理性还在于音频和文本之间固有的模态冲突,以及推理过程中不需要文本到音频的延续。因此,我们省略了 INTLV 数据中音频片段的损失计算。对于 ITTS 数据,除了初始文本片段外,还计算了音频和文本片段的损失,以增强模型在文本引导音频生成方面的能力。

两阶段训练策略。 为了解决语音特征与文本特征的不同特征可能导致的LLM中原始文本知识的潜在破坏,我们提出了一种两阶段训练策略来缓解模态之间的训练冲突。在第一阶段,LLM的参数保持不变,只允许更新音频嵌入层和音频头的参数。在第二阶段,除文本嵌入层和 LM 头的参数外,所有参数都可训练

监督微调细节

监督微调阶段旨在增强模型在一系列任务中遵循复杂指令的能力。音频 SFT 数据来自大量文本指令。使用基于指令类型、多样性和整体质量的过滤策略选择高质量指令。使用 10,000 种不同语音音调的精选数据集合成音频指令。在自然对话停顿处生成和分割相应的文本响应,然后使用指定的语音音调转换为音频。这些数据集涵盖多项任务,包含大约 242k 个音频数据对。

为了确保合成音频的质量,自动语音识别 (ASR) 被应用于生成的音频文件。将 ASR 输出与原始文本进行比较以验证质量。此过程可创建高质量的端到端对话数据集。有错误的合成音频文件将添加到文本转语音 (TTS) 数据集,而有 ASR 错误的案例将合并到 ASR 训练数据集中。这种合并具有挑战性的示例的迭代方法可增强 TTS 和 ASR 的性能。

需要特别注意处理文本转音频导致原始文本响应不适合作为音频回复的情况。此问题是由于文本和音频之间的语调、速度和表达方式的差异而产生的。某些文本内容在转换为音频时可能无法传达预期含义或引入歧义。因此,在生成过程中仔细检查和调整此类情况至关重要。这可确保合成数据准确反映现实世界的语音交互场景,从而提高数据可靠性并提高模型的实际适用性。

Experiment

 综合智力测评

基于语音的对话模型面临的一大挑战是,与纯文本对话模型相比,其性能往往会下降。为了评估语音模型的“智能”,我们​​以文本到文本的建模能力为基准,评估预训练的语音到文本模型的性能。评估数据集包括两种类型:故事延续能力和常识推理能力。

Performance in ASR/TTS Tasks

Fleurs 、 WenetSpeech 和 KeSpeech 上的主要结果 

Performance in Audio Understanding Tasks

模型在音频理解基准测试中表现优异,超越了最新的开源模型。

两种不同的设置:1)非级联方式的语音到语音生成(表示为 S → S),其中输入是音频,输出是交错的文本和音频。然后合并输出文本并用于评估。2)语音到文本生成(表示为 S → T,其中输入是音频,输出是文本,用于评估。

总结

Baichuan-Audio,这是一种专为音频设计的端到端大型语言模型,集成了语音理解和生成功能。该模型通过预训练的 ASR 模型对 12.5 Hz 的语音信号进行多码本离散化,从而保留了语音标记中的语义和声学信息。此外,还专门设计了一个独立的音频头来高效处理这些标记。为了平衡音频建模和语言能力保留,采用了交错数据的两阶段预训练策略。所提出的框架通过文本引导的对齐语音生成来支持语音交互,从而进一步保留了模型的基础认知能力。

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 的高维表示,并共享其全部历史上下文信息,使整个架构作为一个统一模型进行 端到端训练和推理
  • Mini-Omni
  • 感知:

    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