卷积神经网络如何学习音乐相似性

2026年01月29日 由 alex 发表 1945 0

为什么要用音频嵌入来推荐音乐?

流媒体平台(Spotify、Apple Music 等)需要能够向用户推荐新歌。推荐越好,听觉体验就越好。


这些平台可以通过多种方式构建推荐系统。现代系统会将不同的推荐方法融合成混合结构。


想想你刚加入Spotify时,会有人问你喜欢什么音乐类型。根据你选择的音乐类型,Spotify 会推荐一些歌曲。基于歌曲元数据的推荐被称为基于内容的过滤。还采用协同过滤,将行为相似的客户归为一组,然后在他们之间传递建议。


full_pipeline_diagram_small


上述两种方法很大程度上依赖用户行为。另一种越来越被大型流媒体服务采用的方法,是利用深度学习在已学习的嵌入空间中表示歌曲。这使得歌曲能够在高维嵌入空间中被表示,捕捉节奏、音色、质感和制作风格。这样歌曲之间的相似度就能轻松计算,这比传统的协作过滤方法更适合在考虑数亿用户和数千万曲目的情况下。


随着大型语言模型(LLM)的兴起,词语和短语嵌入已成为主流,并且相对被广泛理解。那么,歌曲嵌入是如何工作的?它们解决了什么问题?本文剩余部分将聚焦于音频如何成为模型输入,哪些架构选择编码了音乐特征,对比学习如何塑造嵌入空间的几何形态,以及使用嵌入的歌曲推荐系统在实际中可能如何工作。


音频是如何成为神经网络的输入?

像MP3这样的原始音频文件本质上是一种波形——一个快速变化的时间序列。从这些文件中学习是可能的,但通常数据消耗大且计算量大。我们可以将.mp3文件转换为mel-spectrogram,这更适合作为神经网络的输入。


Mel频谱图是一种表示音频文件频率随时间变化的方法,适应了人类对声音的感知方式。它是一种二维表示,x轴对应时间,y轴对应mel尺度频带,每个值代表该频段在该时间段的对数尺度能量。


audio_spect_diagram_small


我们在mel频谱图上看到的颜色和形状可以告诉我们有意义的音乐信息。明亮的颜色表示该频率和时间的能量较高,较暗的颜色表示能量较低。细的水平带表示持续音高,通常对应持续音符(人声、弦乐、合成器垫底音)。高而垂直的条纹表示能量跨多个频率,时间集中。这些可以代表鼓军鼓和拍手声。


现在我们可以开始思考卷积神经网络如何学习识别这些音频表现的特征。此时,关键挑战是:如何训练模型识别两个短音频片段属于同一首歌且没有标签?


分块与对比学习

在我们深入介绍我们所使用的CNN架构之前,先花点时间介绍如何将频谱图数据加载到网络中,以及如何在没有标签的情况下设置网络的丢失函数。


在非常高层次上,我们将频谱图输入CNN,内部进行大量矩阵乘法,然后我们得到一个128维的向量,这是音频文件物理特征的潜在表示。但我们如何设置批量处理和丢失功能,让网络能够评估相似歌曲呢?


我们先从批次分类开始。我们有一个歌曲数据集(来自FMA小型数据集),并将其转换为频谱图。我们使用 tensorflow.keras.utils.Sequence 类从数据集中随机选择 8 首歌曲。然后我们对每个频谱图进行随机“切块”,选择一个128 x 129的矩形,代表每首歌的一小部分,如下图所示。


chunking_diagram-1024x683


这意味着我们输入网络的每一批数据的形状为(8, 128, 129, 1)(批次大小、mel频率维数、时间块、信道维数)。通过输入歌曲的片段而非整首歌,模型会看到同一歌曲在不同训练时期的不同部分。这防止模型过度拟合到每条轨道的特定时刻。使用每首歌的短采样鼓励网络学习本地音乐质感(音色、节奏密度),而非长距离结构。


接下来,我们使用对比学习目标。对比损失由Chopra等人于2005年提出,旨在学习一个嵌入空间,其中相似的对(正对)欧几里得距离较低,而不同对(负对)之间至少有一定的间距。我们也采用了类似的概念,利用InfoNCE的损耗。


我们为每批数据创建两个随机“视图”。这意味着我们创建了两个批量的增强,每个都添加了随机、正态分布的噪声。作简单,使用以下函数:


@tf.function
def augment(x):
    """Tiny time-frequency noise."""
    noise = tf.random.normal(shape=tf.shape(x), mean=0.0, stddev=0.05)
    return tf.clip_by_value(x + noise, -80.0, 0.0)  
# mel dB range usually -80–0


同一音频采样的嵌入应比批次中任何其他采样的嵌入更相似。


对于大小为8的批次,我们计算第一个视图中每个嵌入和第二个视图中每个嵌入的相似度,得到一个8×8的相似度矩阵。


我们将两个L2归一化的增强批次定义为


屏幕截图2026-01-29155318


两批中的每一行(在本案中为128-D嵌入)均为L2归一化,即,


屏幕截图2026-01-29155323


然后我们可以计算第一个视图中每个嵌入和第二个视图中每个嵌入的相似度,得到一个NxN相似矩阵。该矩阵定义为:


屏幕截图2026-01-29155343


其中 S 的每个元素是歌曲 k 嵌入与歌曲 l 嵌入在两个增广上的相似性。这在元素层面可以定义为:


屏幕截图2026-01-29155355


其中τ是一个温度参数。这意味着对角线条目(同一首歌块之间的相似度)是正对,非对角线条目是负对。


然后对于相似矩阵的每一行 k,我们计算:


屏幕截图2026-01-29155406


这是一种软最大交叉熵损失,分子是正块之间的相似度,分母是所有相似度的总和。


最后我们平均整个批次的损失,得到完整的损失目标:


屏幕截图2026-01-29155423


最小化对比损失鼓励模型将匹配增强视图赋予最高的相似度,同时抑制与批次中其他所有样本的相似性。这同时使同一音频的表示更靠近,同时使不同音频的表示更远,形成结构化嵌入空间,而无需显式标签。


该损失函数可用以下 Python 函数简洁描述:


def contrastive_loss(z_i, z_j, temperature=0.1):
    """
    Compute InfoNCE loss between two batches of embeddings.
    z_i, z_j: (batch_size, embedding_dim)
    """
    z_i = tf.math.l2_normalize(z_i, axis=1)
    z_j = tf.math.l2_normalize(z_j, axis=1)

    logits = tf.matmul(z_i, z_j, transpose_b=True) / temperature
    labels = tf.range(tf.shape(logits)[0])
    loss = tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
    return tf.reduce_mean(loss)


现在我们已经对如何将批次加载到模型以及如何最小化损失函数如何将相似声音聚集成有一定的直观理解,我们可以深入探讨卷积神经网络的结构。


一个简单的CNN架构

我们为此任务选择了一种相当简单的卷积神经网络架构。CNN最早起源于Yann LeCun和团队,当时他们创建了LeNet,用于手写数字识别。CNN在学习理解图像方面非常出色,我们已经将每首歌曲转换成了与CNN相关的图像格式。


第一卷积层在频谱图上应用了32个小滤波器。此时,网络主要学习非常局部的模式:比如短暂的能量爆发、和声线条,或通常对应音符起始或打击乐的突然变化。批次归一化能让激活在训练过程中保持良好行为,最大池化则略微降低分辨率,避免模型对时间或频率的微小变化反应过度。


第二个模块将过滤器数量增加到64个,并开始将这些低层次模式组合成更有意义的结构。在这里,网络开始捕捉到更宽广的质感、重复的节奏模式和一致的音色特征。池化再次压缩表示,同时保留最重要的激活。


到了第三卷积层,模型已处理128个通道。这些特征映射往往反映声音的更高层次,如整体频谱平衡或乐器质感。在这个阶段,特征的具体位置不如它是否出现更重要。


image-145-1024x683


全局平均池通过将每个特征映射平均为单一值,去除剩余的时间-频率结构。这迫使网络总结块中存在的模式,而非它们出现的位置,并生成固定大小的向量,无论输入长度如何。


然后,一个稠密层将该摘要映射为128维嵌入。这正是学习相似性的地方:听起来相似的片段应该靠近,而不同的声音则被推开。


最后,嵌入经过L2归一化,使所有矢量都位于单位球面上。这使得余弦相似度易于计算,并在对比训练中保持嵌入空间中的距离一致。


在高层次上,这个模型学习音乐的方式与卷积神经网络学习图像的方式很相似。这里输入的不是按高度和宽度排列的像素,而是按频率和时间排列的mel-spectrogram。


我们怎么知道这个模型好用?

到目前为止,我们谈论的内容都相当抽象。我们怎么知道mel-spectrogram表示、模型架构和对比学习在创建有意义嵌入方面做得不错?


理解我们创造的嵌入空间的一个常见方法是将空间可视化为一个低维空间,人类实际上可以直观地看到。这种技术称为降维,在理解高维度数据时非常有用。


image-146-1024x874


我们可以采用的两种技术是PCA(主成分分析)和t-SNE(t分布随机邻居嵌入)。PCA是一种线性方法,保持全局结构,有助于理解嵌入空间中的整体形状和主要变化方向。t-SNE是一种非线性方法,优先考虑局部邻域关系,这使其更适合揭示相似点的小簇,但在解释全局距离时可靠性较低。因此,PCA更适合评估嵌入空间整体是否相干,而t-SNE则更适合检查相似项是否倾向于局部聚集。


如上所述,我用FMA小型数据集训练了这个CNN,该数据集包含每首歌曲的流派标签。当我们可视化嵌入空间时,可以将体裁归类,这有助于我们对嵌入空间的质量做出一些判断。


二维投影提供了对学习嵌入空间的不同但互补的视图。这两张图都没有显示完全分离的流派簇,这对音乐相似性模型来说是预期且实际上理想的。


在PCA投影中,体裁高度混合,形成平滑、连续的形状,而非独立的群体。这表明嵌入捕捉的是音乐特征(如音色和节奏)的渐进差异,而非记忆流派标签。由于PCA保持全局结构,这表明嵌入空间是连贯且有意义的组织的。


t-SNE投影侧重于局部关系。在这里,同一类型的曲目更可能彼此靠近,形成小而松散的群聚。与此同时,流派之间仍有显著重叠,反映出许多歌曲跨越流派界限具有共同特征。


image-147-1024x874


总体来看,这些可视化表明嵌入在基于相似性的任务中效果良好。PCA表明该空间具有整体结构良好,而t-SNE则表明本地相似歌曲往往会聚集在一起——这两者都是音乐推荐系统的重要属性。为了进一步评估嵌入的质量,我们还可以参考与推荐相关评估指标,如NDCG和recall@k。


将项目转变为一个可用的音乐推荐应用

最后,我们将花些时间讨论如何将这个训练好的模型真正转化为可用的东西。为了说明类似的CNN在实际应用中的应用,我创建了一个非常简单的歌曲推荐网页应用。该应用会根据余弦相似度返回上传的MP3文件,计算嵌入情况,并返回最相似的曲目列表。我没有孤立地对待模型,而是从端到端设计了流程:音频预处理、频谱图生成、嵌入推断、相似性搜索和结果展示。这与实际应用类似,模型必须可靠地依赖看不见的输入,而非策划的数据集。


FMA小型数据集的嵌入是预先计算并离线存储的,使得通过余弦相似度快速生成推荐,而无需反复运行模型。区块级嵌入被聚合成单一的歌曲级表示,确保不同长度轨道的行为一致。


最终成果是一个轻量级的网页应用,展示了如何将学习到的表示方式集成到真实的推荐工作流程中。


这只是嵌入如何在实际推荐系统中使用的一个非常简单的表示,但它并不能捕捉到全部情况。现代推荐系统将结合音频嵌入和协同过滤,正如本文开头所述。


音频嵌入捕捉声音,协作过滤捕捉谁喜欢什么。两者结合,加上额外的排名模型,可以创造出一种平衡声学相似性和个人品味的混合系统。



文章来源:https://towardsdatascience.com/how-convolutional-neural-networks-learn-musical-similarity/
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消