← 返回 无痛入门音乐科技

「MIR-06」打破砂锅问到底,不同python库做音频预处理的区别在哪里?

2021年9月4日 16:37 · 无痛入门音乐科技

音频预处理通常是将原始波形处理为时频谱,即各种spectrograms,这一步可使用librosa或essentia,涉及到深度学习模型也可直接用torchaudio或tensorflow。但即使输入同个音频,采用同样的参数,得出的时频谱还是有细微差别。

♬ 本文为MIR音乐信息检索系列的第12篇文章 ♬
因为代码块较多,手机上看有些麻烦,建议网页端浏览(之后我会整理详细代码,并同步到github)。
本文将大致讲解,使用librosa、essentia、torchaudio、tensorflow时:

『通用参数设置』

安装在Linux机器下,各个python库均使用截止到2021年8月的最新稳定版本,分别为:

每个库中对音频预处理的默认参数不同,所以我们需要统一一下。鉴于tensorflow中可改的选项最不灵活,所以参数设置都向它对齐 (比如,tensorflow从linear到mel尺度进行转换时,依赖Hidden Markov Model Toolkit (HTK),所以需统一NORM设置为None,且MEL_SCALE设置为'htk')。

下表列出我们统一设定的参数值,以及几个库中的参数名和默认值 (若有):

参数值统一设定为 librosa默认值
torchaudio默认值
tensorflow默认值
SAMPLE_RATE=44100 sr=22050 sample_rate=16000 sample_rate
N_FFT=2048 n_fft=2048 n_fft=400

fft_length=frame_length

HOP_LENGTH=512 hop_length=512

hop_length=win_length//2

frame_step
WIN_LENGTH=N_FFT  win_length=n_fft win_length=n_fft frame_length
WINDOW='hann' window='hann'

window_fn=

torch.hann_window

window_fn=

tf.signal.hann_window

CENTER=False center=True center=True 分帧无法center
PAD_END=False pad_mode='reflect' pad_mode='reflect' pad_end=False
F_MIN=0 fmin=0.0 f_min=0 lower_edge_hertz

F_MAX=SAMPLE_RATE/2

fmax=sr/2 f_max=None upper_edge_hertz
N_FREQS=N_FFT//2+1 - n_freqs num_spectrogram_bins
N_MELS= 128 n_mels=128 n_mels=128
num_mel_bins
NORM=None norm='slaney' norm=None 依赖HTK
MEL_SCALE='htk' htk=False mel_scale='htk' 依赖HTK

本文中用到的事例音频可从librosa下载,并只用其40秒到45秒之间重采样的片段数据。

import numpy as npimport pandas as pdimport librosafrom essentia.standard import *import torch, torchaudioimport tensorflow as tf
filename = librosa.util.example_audio_file()audio, _ = librosa.load(filename, sr=SAMPLE_RATE, mono=MONO, offset=OFFSET, duration=LOAD_DURATION)

『Spectrogram』

分别用四种不同的库,计算示例5秒音频片段的magnitue spectrogram和log power spectrogram。

使用librosa得到的维度是1025 x 427:

# magnitude spectrogramspec_librosa = np.abs(librosa.stft(y=audio,                                    n_fft=N_FFT,                                    hop_length=HOP_LENGTH,                                    win_length=WIN_LENGTH,                                   window=WINDOW,                                   center=CENTER))# log power spectrogramlpspec_librosa = librosa.power_to_db(spec_librosa ** 2)

 使用essentia得到的维度是427 x 1025

# magnitude spectrogramspec_essentia = []es_win_func = Windowing(normalized=False, size=WIN_LENGTH, type='hann')es_spec_func = FFT(size=N_FFT)for frame in FrameGenerator(audio, frameSize=N_FFT, hopSize=HOP_LENGTH,                             startFromZero=True, validFrameThresholdRatio=1.0, lastFrameToEndOfFile=False):    spec_complex = es_spec_func(es_win_func(frame))    spec_essentia.append(np.abs(spec_complex))  spec_essentia = essentia.array(spec_essentia)# log power spectrogramlpspec_essentia = librosa.power_to_db(spec_essentia ** 2)

 使用torchaudio得到的维度是1025 x 427

# magnitude spectrogramspec_torch = torchaudio.transforms.Spectrogram(    n_fft=N_FFT,    win_length=WIN_LENGTH,    hop_length=HOP_LENGTH,    window_fn=torch.hann_window,    power=None,    center=CENTER,    return_complex=True)(torch.Tensor(audio))spec_torch = spec_torch.numpy()spec_torch = np.abs(spec_torch)# log power spectrogramlpspec_torch = librosa.power_to_db(spec_torch ** 2)

 使用tensorflow得到的维度是427 x 1025

# magnitude spectrogramspec_tf = tf.abs(tf.signal.stft(    signals=tf.convert_to_tensor(audio, dtype=tf.float32),    frame_length=N_FFT,    frame_step=HOP_LENGTH,    fft_length=N_FFT,    window_fn=tf.signal.hann_window,    pad_end=PAD_END))spec_tf = spec_tf.numpy()# log power spectrogramlpspec_tf = librosa.power_to_db(spec_tf ** 2)

将以上代码得到的log power spectrogram都画出来,也不会看到非常明显的不同。但是在数值上确实都是有区别的,区别的大小我们可以用Mean Square Error (MSE) 衡量,值越大则区别越大。下方表格的区别从小到大排列,统一设定参数的情况下,librosa和torchaudio得出的log power spectrogram在数值上最为接近。


kernel compare_kernel mse
0 librosa torchaudio 1.153476e-09
1 librosa tensorflow 9.368608e-07
2 tensorflow torchaudio 9.407697e-07
3 essentia torchaudio 2.041789e-04
4 essentia librosa 2.042010e-04
5 essentia tensorflow 2.050108e-04

『Mel Filter Bank』

鉴于上文中essentia与其他区别相对较大,之后我们主要检查其余三个python库之间的区别。为了得到melspectrogram,可将上文得到的"linear频带的spectrogram"与"可转换到mel尺度的矩阵"做矩阵乘法。所以这里我们先来看一下不同python库得到的linear to mel matrix。

使用librosa得到的维度是128 x 1025:

l2m_librosa = librosa.filters.mel(sr=SAMPLE_RATE,                                   n_fft=N_FFT,                                   n_mels=N_MELS,                                   fmin=F_MIN,                                  fmax=F_MAX,                                   htk=MEL_SCALE=='htk',                                   norm=NORM)

使用torchaudio得到的维度是1025 x 128:

l2m_torch = torchaudio.functional.create_fb_matrix(    n_freqs=N_FREQS,     f_min=F_MIN,     f_max=F_MAX,     n_mels=N_MELS,     sample_rate=SAMPLE_RATE,     norm=NORM,     mel_scale=MEL_SCALE)

使用tensorflow得到的维度是1025 x 128:

l2m_tf = tf.signal.linear_to_mel_weight_matrix(    num_mel_bins=N_MELS,    num_spectrogram_bins=N_FREQS,    sample_rate=SAMPLE_RATE,    lower_edge_hertz=F_MIN,    upper_edge_hertz=F_MAX)

以上三者之间的区别如下表所示,max_abs_diff表示,两两相减时得到的最大绝对差值。


kernel compare_kernel max_abs_diff mse
0 librosa torchaudio 0.000011 2.611765e-13
1 librosa tensorflow 0.003384 9.346046e-08
2 tensorflow torchaudio 0.003392 9.356785e-08

依然是librosa和torchaudio之间差别最小。


『Mel Spectrogram』

最后得magnitude melspectrogram, power melspectrogram, log power melspectrogram三者,可按以下代码运算。

# librosamelspec_librosa = np.dot(l2m_librosa, spec_librosa)pmelspec_librosa = np.dot(l2m_librosa, spec_librosa ** 2)lpmelspec_librosa = librosa.power_to_db(pmelspec_librosa)# torchaudiomelspec_torch = torch.matmul(torch.Tensor(spec_torch.T), l2m_torch).numpy().Tpmelspec_torch = torch.matmul(torch.Tensor(spec_torch.T ** 2), l2m_torch).numpy().Tlpmelspec_torch = librosa.power_to_db(pmelspec_torch)# tensorflowmelspec_tf = tf.tensordot(spec_tf, l2m_tf, 1).numpy().Tpmelspec_tf = tf.tensordot(spec_tf ** 2, l2m_tf, 1).numpy().Tlpmelspec_tf = librosa.power_to_db(pmelspec_tf)

melspec_librosa和pmelspec_librosa,也可以直接通过librosa.feature.melspectrogram()输入audio或spec_librosa得到,需要注意其中相应power参数值的设定。

同理,torchaudio中也有直接得melspectrogram的功能:

pmelspec_torch_by_audio = torchaudio.transforms.MelSpectrogram(    sample_rate=SAMPLE_RATE,    n_fft=N_FFT,    win_length=WIN_LENGTH,    hop_length=HOP_LENGTH,    f_min=F_MIN,    f_max=F_MAX,        n_mels=N_MELS,    window_fn=torch.hann_window,    power=2.0,    normalized=False,    center=CENTER,    norm=NORM,    mel_scale=MEL_SCALE)(torch.Tensor(audio))pmelspec_torch_by_audio = pmelspec_torch_by_audio.numpy()lpmelspec_torch_by_audio = librosa.power_to_db(pmelspec_torch_by_audio)

可是,目前我得到的pmelspec_torch_by_audio和前文中的pmelspec_torch,在数值上非常接近,但并非完全一致。

对以上log power melspectrogram之间的区别用MSE衡量可得:


kernel compare_kernel mse
0 librosa torchaudio_l2m 7.132773e-10
1 librosa torchaudio_direct 7.133121e-10
2 librosa tensorflow 1.559776e-04
3 torchaudio_direct torchaudio_l2m 1.416461e-13
4 tensorflow torchaudio_l2m 1.560096e-04
5 tensorflow torchaudio_direct 1.560096e-04

画出最小 (librosa vs. torchaudio_l2m) 和最大 (tensorflow vs. torchaudio_direct) 对应在log power melspectrogram上的差值:

Image



目前来看,librosa (0.8.0版本) 和torchaudio (0.9.0版本) 在参数设置相互对齐的前提下,可以得到数值上相对接近的音频预处理结果。最后强调一些引起数值差别的主要参数:

相关文章回顾:「MIR-01」要把音乐画出来,总共分几步?