引言
在本文中,我们将探讨如何将扬声器的声音与现有的声音集进行匹配。你可以把它想象成一种生物识别系统,但使用的是人类的声音,而不是指纹或眼睛等物理感官。为了实现这一点,我们将利用向量嵌入和开源技术的魔力。
这种技术在 Google Assistant 或 Siri 中被广泛使用。当你购买一部新设备(如 Android 手机)时,在设置 Google 时,它会要求你录入声音以捕捉其模式和语音等信息,以确保只有你可以通过说"Ok, google"来访问 Google Assistant。
在深入细节之前,让我们先了解什么是向量嵌入,以及它是如何用于音频的。
音频的向量嵌入
向量嵌入是一种将对象(例如单词、句子或在我们案例中的音频数据)表示为数学空间中的向量的方式。音频数据可以表示为向量,其中音频的不同方面(如频率、幅度等特征)被映射到向量的特定位置。
在音频数据的上下文中,可以训练机器学习模型来学习这些嵌入。模型分析音频数据的模式和特征,以生成有意义的向量表示。一旦模型训练完成,它可以通过将音频数据转换为向量表示来编码音频数据。这个向量现在捕捉了音频内容和特征的重要信息。
音频内容的向量在嵌入空间中会彼此靠近。这允许进行音频相似性比较等任务,你可以通过测量各自嵌入之间的距离快速识别两个音频片段的相似程度。为了生成向量嵌入,我们将使用一个名为 Resemblyzer 的开源工具,并将这些向量存储在 Qdrant DB 中。
我们有一组一些名人(克里斯蒂亚诺·罗纳尔多、唐纳德·特朗普和霍默·辛普森)的音频片段(是的,他很有名)。
Resemblyzer 概述
Resemblyzer 允许我们通过深度学习模型获取声音的高级表示。它简化了开发人员的工作,使他们能够用几行代码将音频片段转换为向量,消除了对神经网络的需求。查看官方 GitHub 仓库。
安装适用于 Python(3.5+)的 Resemblyzer:
bash
pip install resemblyzer
我正在使用 Google Colab 和免费的 T4 GPU 进行此任务。你也可以使用 CPU,但可能需要很长时间。点击这里获取音频数据。
让我们开始编码部分:
python
from resemblyzer import preprocess_wav, VoiceEncoder
from pathlib import Path
from tqdm import tqdm
import numpy as np
from IPython.display import Audio
from itertools import groupby
import heapq
audio_sample = Audio('path-to-audio-folder/train/Trump.mp3', autoplay=True)
display(audio_sample)
播放: Trump.mp3
让我们从训练文件夹中获取音频片段。
python
wav_fpaths = list(Path("path-to-audio-folder/train").rglob("*.mp3"))
speakers = list(map(lambda wav_fpath: wav_fpath.stem, wav_fpaths))
print(speakers)
输出为: ['Ronaldo', 'Ronaldo2', 'Homer', 'Homer2', 'Trump']
现在,重要的部分,我们将释放 Resemblyzer 的力量,将音频片段转换为向量嵌入,只需几行代码。首先对所有音频片段进行预处理。
python
wavs = np.array(list(map(preprocess_wav, tqdm(wav_fpaths, "Preprocessing wavs", len(wav_fpaths)))), dtype=object)
speaker_wavs = {speaker: wavs[list(indices)] for speaker, indices in groupby(range(len(wavs)), lambda i: speakers[i])}
print(speaker_wavs)
在上述代码中,我们将这些声波转换为数值表示,仅用几行代码而无需使用任何神经网络。
现在,将这些数值表示转换为嵌入。
python
encoder = VoiceEncoder("cuda")
utterance_embeds = np.array(list(map(encoder.embed_utterance, wavs)))
print(utterance_embeds)
这个向量包含一个浮点对象,其中每一行代表每个音频片段。
对于此任务,我们使用 Qdrant DB 作为主要的向量数据库。因此,我们需要将这种表示转换为合适的格式。基本上,我们需要一个字典列表,其中每个字典包含键为 id 和向量的键。ID 将是递增的数值。
为了获取相似的向量,必须为向量分配唯一的 ID。
python
# 创建一个空列表以保存所需格式的嵌入
embeddings = []
# 遍历数组中的每个嵌入
for i, embedding in enumerate(utterance_embeds):
# 创建一个字典,包含"id"和"vector"键
embedding_dict = {"id": i + 1, "vector": embedding.tolist()} # 从 1 开始的 ID
# 将字典添加到嵌入列表中
embeddings.append(embedding_dict)
QdrantDB 概述
Qdrant DB 是最流行的向量数据库之一。使用 Qdrant DB,Web 开发人员可以无缝存储嵌入并检索它们。这里是 官方文档。
要开始使用 Qdrant DB,注册 他们的云服务以开始使用免费套餐,限制为每个集群最多 1GB。获取你的 API 密钥(本地安全地复制,复制后你将无法再次查看 API 密钥)。
对于 Python,Qdrant DB 有自己的 API qdrant_client,使用起来非常简单,代码行数较少。让我们设置 Qdrant DB。
通过 pip 安装 qdrant_client:
bash
pip install qdrant_client
python
import qdrant_client
qdrant_uri = 'paste-your-db-uri'
qdrant_api_key = 'paste-your-api-key'
让我们在数据库中创建一个集合;这里集合的含义与 MongoDB 中的集合相同。
python
vectors_config = qdrant_client.http.models.VectorParams(
size=256,
distance=qdrant_client.http.models.Distance.COSINE
)
现在,在初始化 QdrantDB 之后,我们将从 Resemblyzer 插入(或添加)嵌入。
python
# 插入嵌入
client.upsert('my-collection', embeddings)
到此为止,我们在 Qdrant DB 中存储了编码版本的音频样本。现在我们将使用新声音进行测试,该声音在数据库中有记录。
扬声器识别
识别新声音的用户就是要找到新声音与已存储声音集之间的相似性。例如,获取克里斯蒂亚诺·罗纳尔多的新声音并检查是否被识别。我们已经在数据库中有罗纳尔多的声音。
我正在使用罗纳尔多在赢得 UCL 后发表的标志性简短演讲:
"Muchas gracias afición esto para vosotros. Siuuuuuuuuu!"
播放: Siuu.mp3
将新声音转换为嵌入。
python
test_wav = preprocess_wav("/content/drive/MyDrive/audio_data_colab/Siuu.mp3")
test_embeddings = encoder.embed_utterance(test_wav)
python
results = client.search("my-collection", test_embeddings)
print(results)
输出示例:
python
[ScoredPoint(id=2, version=0, score=0.6956655, payload={}, vector=None, shard_key=None),
ScoredPoint(id=1, version=0, score=0.6705738, payload={}, vector=None, shard_key=None),
ScoredPoint(id=5, version=0, score=0.56731033, payload={}, vector=None, shard_key=None),
ScoredPoint(id=3, version=0, score=0.535391, payload={}, vector=None, shard_key=None),
ScoredPoint(id=4, version=0, score=0.42906034, payload={}, vector=None, shard_key=None)]
通过上述结果,你可以看到 ID 1 和 2 与罗纳尔多的片段相关(我们在嵌入代码中做了这个)。最高得分约为 70%,这很好,因为我们只有非常少量的数据。此外,片段的平均长度为 3-4 秒。你可以添加更多数据并尝试。
要获取前两个相似结果,只需运行以下代码(你也可以为前 1 或前 3 进行此操作,然后根据前 3 的模式进行决定)。
python
# 获取基于得分的前两个结果,处理潜在的平局
top_two_results = heapq.nlargest(2, results, key=lambda result: result.score)
# 提取和对齐 ID,考虑潜在的平局
top_two_ids = sorted({result.id - 1 for result in top_two_results}) # 删除重复项
# 获取对应的名称,检查有效 ID
top_two_names = []
for aligned_id in top_two_ids:
if 0 <= aligned_id < len(speakers):
top_two_names.append(speakers[aligned_id])
else:
print(f"遇到无效 ID {aligned_id + 1}.")
print("前两名扬声器:", top_two_names)
输出:
css
前两名扬声器: ['Ronaldo', 'Ronaldo2']
是的!这是一个匹配。我们成功地将新声音与现有的声音集进行了验证。
结论
在本文中,我们仅用几行代码实现了音频驱动的扬声器识别,使用了开源技术如 Resemblyzer 和 Qdrant DB。Resemblyzer 是处理音频数据并将其编码为嵌入的最简单方法。无需神经网络或变换器架构。另一方面,Qdrant DB 提供了一种高效的方式来存储和检索嵌入。