音频数据介绍
本质上,声波是一种连续信号,这意味着它在给定时间内包含无限数量的信号值。这给期望有限数组的数字设备带来了问题。为了被数字设备处理、存储和传输,连续声波需要转换为一系列离散值,称为数字表示。
如果你查看任何音频数据集,你会发现包含声音片段的数字文件,例如文本叙述或音乐。你可能会遇到不同的文件格式,例如 .wav
(波形音频文件)、.flac
(免费无损音频编解码器)和 .mp3
(MPEG-1 音频层 3)。这些格式的主要区别在于它们如何压缩音频信号的数字表示。
让我们看看我们是如何从连续信号到这种表示的。模拟信号首先由麦克风捕获,麦克风将声波转换为电信号。然后,电信号通过模数转换器进行数字化,以通过采样获得数字表示。
采样和采样率
采样是在固定时间步长测量连续信号的值的过程。采样波形是离散的,因为它在均匀间隔内包含有限数量的信号值。
维基百科文章中的插图:采样(信号处理)
采样率(也称为采样频率)是在一秒钟内采集的样本数,以赫兹 (Hz) 为单位测量。举个例子,CD 质量音频的采样率为 44,100 Hz,这意味着每秒采集 44,100 个样本。相比之下,高分辨率音频的采样率为 192,000 Hz 或 192 kHz。训练语音模型常用的采样率是 16,000 Hz 或 16 kHz。
采样率的选择主要决定了可以从信号中捕获的最高频率。这也被称为奈奎斯特极限,正好是采样率的一半。人类语音中的可听频率低于 8 kHz,因此以 16 kHz 的频率对语音进行采样就足够了。使用更高的采样率不会捕获更多信息,只会导致处理此类文件的计算成本增加。另一方面,以过低的采样率对音频进行采样会导致信息丢失。以 8 kHz 采样率采样的语音听起来会很模糊,因为在这个速率下无法捕获更高的频率。
在执行任何音频任务时,务必确保数据集中所有音频示例都具有相同的采样率。如果你计划使用自定义音频数据来微调预训练模型,则数据的采样率应与模型预训练数据采样率相匹配。采样率决定了连续音频样本之间的时间间隔,这会影响音频数据的时间分辨率。举个例子:以 16,000 Hz 的采样率对 5 秒的声音进行采样将表示为 80,000 个值的序列,而以 8,000 Hz 的采样率对相同的声音进行采样将表示为 40,000 个值的序列。解决音频任务的 Transformer 模型将示例视为序列,并依靠注意力机制来学习音频或多模态表示。由于不同采样率的音频示例的序列不同,因此模型难以在采样率之间进行泛化。重采样是使采样率匹配的过程,并且是音频数据预处理的一部分。
振幅和位深
采样率告诉你样本采集的频率,但每个样本中的值到底是什么呢?
声音是由空气压力在人类可听频率范围内的变化产生的。声音的振幅描述了任何给定时刻的声压级,以分贝 (dB) 为单位测量。我们将振幅感知为响度。举个例子,正常的说话声在 60 dB 以下,摇滚音乐会的声音可能在 125 dB 左右,接近人类听力的极限。
在数字音频中,每个音频样本都会记录某个时间点音频波的振幅。样本的位深决定了可以描述此振幅值的精度。位深越高,数字表示越忠实地逼近原始连续声波。
最常见的音频位深是 16 位和 24 位。每个都是一个二进制术语,表示在将振幅值从连续转换为离散时,可以将其量化的可能步数:16 位音频为 65,536 个步长,24 位音频为 16,777,216 个步长。因为量化涉及将连续值舍入为离散值,所以采样过程会引入噪声。位深越高,这种量化噪声越小。在实践中,16 位音频的量化噪声已经小到无法被人耳察觉,并且通常不需要使用更高的位深。
你可能还会遇到 32 位音频。它将样本存储为浮点值,而 16 位和 24 位音频使用整数样本。32 位浮点值的精度为 24 位,使其与 24 位音频具有相同的位深。浮点音频样本预计位于 [-1.0, 1.0] 范围内。由于机器学习模型自然地在浮点数据上工作,因此音频必须首先转换为浮点格式才能用于训练模型。我们将在下一节关于预处理的内容中了解如何做到这一点。
与连续音频信号一样,数字音频的振幅通常以分贝 (dB) 表示。由于人类听觉本质上是对数的——我们的耳朵对安静声音中的小波动比对响亮声音中的小波动更敏感——如果振幅以分贝表示,则更容易解释声音的响度,分贝也是对数的。现实世界音频的分贝标度从 0 dB 开始,表示人类可以听到的最安静的声音,而更响亮的声音具有更大的值。但是,对于数字音频信号,0 dB 是可能的最大振幅,而所有其他振幅都是负数。作为经验法则:每 -6 dB 振幅减半,低于 -60 dB 的声音通常是听不到的,除非你真的把音量调大。
音频波形
你可能见过声音以**波形**的形式可视化,它将样本值随时间绘制出来,并展示了声音幅度的变化。这也被称为声音的时域表示。
这种可视化方法有助于识别音频信号的特定特征,例如单个声音事件的时间、信号的整体响度以及音频中存在的任何不规则或噪声。
为了绘制音频信号的波形,我们可以使用一个名为librosa
的 Python 库。
pip install librosa
让我们以库自带的“trumpet”(小号)音频为例。
import librosa
array, sampling_rate = librosa.load(librosa.ex("trumpet"))
该示例加载为音频时间序列(这里我们称之为array
)和采样率(sampling_rate
)的元组。让我们使用 librosa 的waveshow()
函数来查看该声音的波形。
import matplotlib.pyplot as plt
import librosa.display
plt.figure().set_figwidth(12)
librosa.display.waveshow(array, sr=sampling_rate)
这将信号的幅度绘制在 y 轴上,时间绘制在 x 轴上。换句话说,每个点对应于采样该声音时获取的单个样本值。另请注意,librosa 已经将音频作为浮点值返回,并且幅度值确实在 [-1.0, 1.0] 范围内。
将音频可视化并同时聆听它可以成为理解您正在处理的数据的有用工具。您可以看到信号的形状,观察模式,学习识别噪声或失真。如果您以某种方式预处理数据,例如归一化、重采样或滤波,则可以直观地确认预处理步骤已按预期应用。在训练模型后,您还可以可视化发生错误的样本(例如,在音频分类任务中)以调试问题。
频谱
可视化音频数据的另一种方法是绘制音频信号的**频谱**,也称为频域表示。频谱是使用离散傅里叶变换或 DFT 计算得到的。它描述了构成信号的各个频率及其强度。
让我们通过使用 numpy 的rfft()
函数进行 DFT 来绘制相同小号声音的频谱。虽然可以绘制整个声音的频谱,但查看一小部分区域更有用。这里我们将对前 4096 个样本进行 DFT,这大约是第一个音符播放的长度。
import numpy as np
dft_input = array[:4096]
# calculate the DFT
window = np.hanning(len(dft_input))
windowed_input = dft_input * window
dft = np.fft.rfft(windowed_input)
# get the amplitude spectrum in decibels
amplitude = np.abs(dft)
amplitude_db = librosa.amplitude_to_db(amplitude, ref=np.max)
# get the frequency bins
frequency = librosa.fft_frequencies(sr=sampling_rate, n_fft=len(dft_input))
plt.figure().set_figwidth(12)
plt.plot(frequency, amplitude_db)
plt.xlabel("Frequency (Hz)")
plt.ylabel("Amplitude (dB)")
plt.xscale("log")
这将绘制此音频片段中存在的各种频率成分的强度。频率值位于 x 轴上,通常以对数刻度绘制,而它们的幅度位于 y 轴上。
我们绘制的频谱显示了几个峰值。这些峰值对应于正在播放的音符的泛音,较高的泛音较弱。由于第一个峰值大约在 620 Hz,因此这是 E♭ 音符的频谱。
DFT 的输出是一个复数数组,由实部和虚部组成。使用np.abs(dft)
取模提取频谱图中的幅度信息。实部和虚部之间的角度提供所谓的相位谱,但在机器学习应用中通常会丢弃它。
你使用librosa.amplitude_to_db()
将幅度值转换为分贝尺度,从而更容易查看频谱中的更精细细节。有时人们使用**功率谱**,它测量能量而不是幅度;这只是一个幅度值平方后的频谱。
音频信号的频谱包含与其波形完全相同的信息——它们只是查看相同数据(此处,小号声音的前 4096 个样本)的两种不同方式。波形绘制音频信号的幅度随时间变化,而频谱则可视化固定时间点的各个频率的幅度。
声谱图
如果我们想查看音频信号中的频率如何变化呢?小号演奏了几个音符,它们都具有不同的频率。问题是频谱只显示了给定时刻的频率的冻结快照。解决方案是进行多次 DFT,每次覆盖一小段时间段,并将得到的频谱叠加在一起形成**声谱图**。
声谱图绘制了音频信号的频率内容随时间变化的情况。它允许您在一个图形上查看时间、频率和幅度。执行此计算的算法是 STFT 或短时傅里叶变换。
声谱图是您可以使用到的最具信息量的音频工具之一。例如,当处理音乐录音时,您可以看到各种乐器和人声轨迹以及它们如何共同构成整体声音。在语音中,您可以识别不同的元音,因为每个元音都以特定的频率为特征。
让我们使用 librosa 的stft()
和specshow()
函数绘制相同小号声音的声谱图。
import numpy as np
D = librosa.stft(array)
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
plt.figure().set_figwidth(12)
librosa.display.specshow(S_db, x_axis="time", y_axis="hz")
plt.colorbar()
在此图中,x 轴表示时间,如波形可视化中一样,但现在 y 轴表示频率(以 Hz 为单位)。颜色的强度表示每个时间点的频率成分的幅度或功率,以分贝 (dB) 为单位。
声谱图是通过获取音频信号的短片段(通常持续几毫秒)并计算每个片段的离散傅里叶变换以获得其频谱来创建的。然后将得到的频谱在时间轴上叠加在一起以创建声谱图。此图像中的每个垂直切片对应于一个单独的频谱,从顶部可以看到。默认情况下,librosa.stft()
将音频信号分成 2048 个样本的片段,这在频率分辨率和时间分辨率之间提供了良好的权衡。
由于声谱图和波形是相同数据的不同视图,因此可以使用逆 STFT 将声谱图转换回原始波形。但是,这除了需要幅度信息之外还需要相位信息。如果声谱图是由机器学习模型生成的,它通常只输出幅度。在这种情况下,我们可以使用经典的 Griffin-Lim 算法等相位重建算法,或使用称为声码器的神经网络,从声谱图重建波形。
声谱图不仅用于可视化。许多机器学习模型将以声谱图作为输入——而不是波形——并产生声谱图作为输出。
现在我们知道了什么是声谱图以及它是如何生成的,让我们看一下语音处理中广泛使用的一种变体:梅尔声谱图。
梅尔声谱图
梅尔声谱图是声谱图的一种变体,通常用于语音处理和机器学习任务。它与声谱图类似,都显示了音频信号的频率内容随时间变化的情况,但在不同的频率轴上。
在标准声谱图中,频率轴是线性的,以赫兹 (Hz) 为单位测量。但是,人类听觉系统对低频变化的敏感度高于高频变化,并且这种敏感度随着频率的增加而对数下降。梅尔刻度是一个感知刻度,它近似于人耳的非线性频率响应。
要创建梅尔声谱图,就像之前一样使用 STFT,将音频分成短片段以获得一系列频率谱。此外,每个频谱都通过一组滤波器(即梅尔滤波器组)进行处理,以将频率转换为梅尔刻度。
让我们看看如何使用 librosa 的melspectrogram()
函数绘制梅尔声谱图,该函数为我们执行所有这些步骤。
S = librosa.feature.melspectrogram(y=array, sr=sampling_rate, n_mels=128, fmax=8000)
S_dB = librosa.power_to_db(S, ref=np.max)
plt.figure().set_figwidth(12)
librosa.display.specshow(S_dB, x_axis="time", y_axis="mel", sr=sampling_rate, fmax=8000)
plt.colorbar()
在上面的示例中,n_mels
表示要生成的梅尔频带的数量。梅尔频带定义了一组频率范围,使用一组滤波器将频谱划分为感知上有意义的成分,这些滤波器的形状和间距的选择是为了模仿人耳对不同频率的响应方式。n_mels
的常用值为 40 或 80。fmax
指示我们关心的最高频率(以 Hz 为单位)。
就像普通声谱图一样,将梅尔频率成分的强度表示成分贝是很常见的做法。这通常称为对数梅尔声谱图,因为转换为分贝涉及对数运算。上面的示例使用了librosa.power_to_db()
,因为librosa.feature.melspectrogram()
创建的是功率声谱图。
创建梅尔声谱图是一个有损操作,因为它涉及到对信号进行滤波。将梅尔声谱图转换回波形比对普通声谱图进行转换更困难,因为它需要估计被丢弃的频率。这就是为什么像 HiFiGAN 声码器这样的机器学习模型需要从梅尔声谱图生成波形。
与标准声谱图相比,梅尔声谱图可以捕捉到音频信号中更多对人类感知有意义的特征,这使得它成为语音识别、说话人识别和音乐类型分类等任务的热门选择。
现在您已经知道如何可视化音频数据示例,请继续尝试查看您最喜欢的音频看起来是什么样子。 :)