MFCC Python:与librosa、python_speech_features和tensorflow.signal完全不同的结果

11

我正在尝试从音频(.wav文件)中提取MFCC特征,我已经尝试了python_speech_featureslibrosa,但它们给出的结果完全不同:

audio, sr = librosa.load(file, sr=None)

# librosa
hop_length = int(sr/100)
n_fft = int(sr/40)
features_librosa = librosa.feature.mfcc(audio, sr, n_mfcc=13, hop_length=hop_length, n_fft=n_fft)

# psf
features_psf = mfcc(audio, sr, numcep=13, winlen=0.025, winstep=0.01)

以下是图表: librosa: enter image description here python_speech_features: enter image description here 我有没有为这两种方法传递错误的参数?为什么会有这么大的差异?
更新:我也尝试了tensorflow.signal实现,以下是结果: enter image description here 图表本身更接近于librosa,但比例更接近于python_speech_features。(请注意,这里计算了80个mel bin并选取了前13个;如果只使用13个bin进行计算,则结果看起来会非常不同)。下面是代码:
stfts = tf.signal.stft(audio, frame_length=n_fft, frame_step=hop_length, fft_length=512)
spectrograms = tf.abs(stfts)

num_spectrogram_bins = stfts.shape[-1]
lower_edge_hertz, upper_edge_hertz, num_mel_bins = 80.0, 7600.0, 80
linear_to_mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix(
    num_mel_bins, num_spectrogram_bins, sr, lower_edge_hertz, upper_edge_hertz)
mel_spectrograms = tf.tensordot(spectrograms, linear_to_mel_weight_matrix, 1)
mel_spectrograms.set_shape(spectrograms.shape[:-1].concatenate(linear_to_mel_weight_matrix.shape[-1:]))

log_mel_spectrograms = tf.math.log(mel_spectrograms + 1e-6)
features_tf = tf.signal.mfccs_from_log_mel_spectrograms(log_mel_spectrograms)[..., :13]
features_tf = np.array(features_tf).T

我认为我的问题是:哪一个输出更接近于MFCC的实际样貌?

有趣。将采样率作为关键字参数传递给psf版本,例如samplerate=sr,会有什么不同吗?或者在librosa版本中更改dct_type呢? - Hendrik
@Hendrik,psf版本已经将sr作为输入。在dct_type方面,当我将其设置为3时,有一些变化,但仍然与psf输出相差甚远(1和2几乎相同)。 - TYZ
@Hendrik 我也尝试过tensorflow,它更接近于librosa,但是它的比例差别很大。 - TYZ
将采样率作为关键字参数传递给psf版本,例如samplerate=sr,会有所不同吗?我的意思是,您正在将其作为位置参数传递,但它是一个关键字参数。不确定在这里将其作为kwarg参数传递是否有所不同,但我会遵守(显式)API。 - Hendrik
@Hendrik 没有区别。 - TYZ
3个回答

21
这里有至少两个因素可以解释为什么你会得到不同的结果:
  1. 梅尔频率尺度没有一个统一的定义。Librosa 实现了两种方法:SlaneyHTK。其他软件包可能会使用不同的定义,导致不同的结果。话虽如此,总体情况应该是相似的。这带我们来到第二个问题...
  2. python_speech_features 默认将能量作为第一个(索引零)系数(appendEnergy 默认为 True),这意味着当您请求例如 13 个 MFCC 时,您实际上得到的是 12 + 1。
换句话说,你并没有比较 13 个 librosa 与 13 个 python_speech_features 系数,而是比较了 13 与 12。能量可能具有不同的幅度,因此由于不同的色彩缩放产生完全不同的图片。
我现在将演示如何让这两个模块产生类似的结果:
import librosa
import python_speech_features
import matplotlib.pyplot as plt
from scipy.signal.windows import hann
import seaborn as sns

n_mfcc = 13
n_mels = 40
n_fft = 512 
hop_length = 160
fmin = 0
fmax = None
sr = 16000
y, sr = librosa.load(librosa.util.example_audio_file(), sr=sr, duration=5,offset=30)

mfcc_librosa = librosa.feature.mfcc(y=y, sr=sr, n_fft=n_fft,
                                    n_mfcc=n_mfcc, n_mels=n_mels,
                                    hop_length=hop_length,
                                    fmin=fmin, fmax=fmax, htk=False)

mfcc_speech = python_speech_features.mfcc(signal=y, samplerate=sr, winlen=n_fft / sr, winstep=hop_length / sr,
                                          numcep=n_mfcc, nfilt=n_mels, nfft=n_fft, lowfreq=fmin, highfreq=fmax,
                                          preemph=0.0, ceplifter=0, appendEnergy=False, winfunc=hann)

spectrogram 1

正如您所看到的,比例尺不同,但总体情况非常相似。请注意,我必须确保传递给模块的参数数量是相同的。


感谢您关注此事!我尝试逐个添加所有参数,以查看哪个参数导致了差异,并发现是appendEnergy,在添加该参数后,绘图看起来相当相似。至于比例,我想如果我们之后进行缩放,那么这不会太重要。 - TYZ
@TYZ 确实如此。话虽如此,考虑到你们正在做的事情,我会考虑超越MFCC。 - Lukasz Tracewski
你有什么其他的建议吗?我目前正在研究fairseq的wav2vec,但肯定会接受更多的建议。 - TYZ
@TYZ 由于这个问题已经偏离了原来的主题,我会直接联系您。 - Lukasz Tracewski

12
这是让我夜不能寐的事情。这个答案(链接)是正确的(并且非常有用!),但不完整,因为它没有解释两种方法之间的广泛差异。我的回答增加了重要的额外细节,但仍然无法实现完全匹配。
发生的事情很复杂,最好通过下面的一段代码块来解释,该代码将librosapython_speech_features与另一个包torchaudio进行比较。
  • 首先,注意torchaudio的实现具有一个参数log_mels,其默认值(False)模仿了librosa实现,但如果设置为True,则模仿python_speech_features。在两种情况下,结果仍然不完全相同,但相似之处很明显。

  • 其次,如果您深入研究torchaudio的实现代码,您会看到注释说明默认值不是“教科书实现”(torchaudio的话,但我信任他们),而是提供给Librosa兼容性; torchaudio中切换的关键操作之一是:

    mel_specgram = self.MelSpectrogram(waveform)
    if self.log_mels:
        log_offset = 1e-6
        mel_specgram = torch.log(mel_specgram + log_offset)
    else:
        mel_specgram = self.amplitude_to_DB(mel_specgram)
  • 第三个问题,你可能会合理地想知道是否可以强制librosa正确运行。答案是肯定的(或者至少看起来是),可以直接使用mel频谱图并取其自然对数,将其作为输入传递给librosa mfcc函数。详情请参见下面的代码。

  • 最后,请小心一些。如果您使用此代码,请检查查看不同特征时发生的情况。第0个特征仍然具有严重的未解释偏移,并且更高级别的特征 tend to drift away from each other.这可能是底层实现的不同或稍微不同的数值稳定常数之类的简单问题,也可以通过微调(例如,在填充方面做出选择或在分贝转换中引用参考)来解决。我真的不知道。

以下是一些示例代码:

import librosa
import python_speech_features
import matplotlib.pyplot as plt
from scipy.signal.windows import hann
import torchaudio.transforms
import torch

n_mfcc = 13
n_mels = 40
n_fft = 512 
hop_length = 160
fmin = 0
fmax = None
sr = 16000

melkwargs={"n_fft" : n_fft, "n_mels" : n_mels, "hop_length":hop_length, "f_min" : fmin, "f_max" : fmax}

y, sr = librosa.load(librosa.util.example_audio_file(), sr=sr, duration=5,offset=30)

# Default librosa with db mel scale 
mfcc_lib_db = librosa.feature.mfcc(y=y, sr=sr, n_fft=n_fft,
                                    n_mfcc=n_mfcc, n_mels=n_mels,
                                    hop_length=hop_length,
                                    fmin=fmin, fmax=fmax, htk=False)

# Nearly identical to above
# mfcc_lib_db = librosa.feature.mfcc(S=librosa.power_to_db(S), n_mfcc=n_mfcc, htk=False)

# Modified librosa with log mel scale (helper)
S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels, fmin=fmin, 
                                    fmax=fmax, hop_length=hop_length)

# Modified librosa with log mel scale
mfcc_lib_log = librosa.feature.mfcc(S=np.log(S+1e-6), n_mfcc=n_mfcc, htk=False)

# Python_speech_features 
mfcc_speech = python_speech_features.mfcc(signal=y, samplerate=sr, winlen=n_fft / sr, winstep=hop_length / sr,
                                          numcep=n_mfcc, nfilt=n_mels, nfft=n_fft, lowfreq=fmin, highfreq=fmax,
                                          preemph=0.0, ceplifter=0, appendEnergy=False, winfunc=hann)

# Torchaudio 'textbook' log mel scale 
mfcc_torch_log = torchaudio.transforms.MFCC(sample_rate=sr, n_mfcc=n_mfcc, 
                                            dct_type=2, norm='ortho', log_mels=True, 
                                            melkwargs=melkwargs)(torch.from_numpy(y))

# Torchaudio 'librosa compatible' default dB mel scale 
mfcc_torch_db = torchaudio.transforms.MFCC(sample_rate=sr, n_mfcc=n_mfcc, 
                                           dct_type=2, norm='ortho', log_mels=False, 
                                           melkwargs=melkwargs)(torch.from_numpy(y))

feature = 1 # <-------- Play with this!!
plt.subplot(2, 1, 1)

plt.plot(mfcc_lib_log.T[:,feature], 'k')
plt.plot(mfcc_lib_db.T[:,feature], 'b')
plt.plot(mfcc_speech[:,feature], 'r')
plt.plot(mfcc_torch_log.T[:,feature], 'c')
plt.plot(mfcc_torch_db.T[:,feature], 'g')
plt.grid()

plt.subplot(2, 2, 3)
plt.plot(mfcc_lib_log.T[:,feature], 'k')
plt.plot(mfcc_torch_log.T[:,feature], 'c')
plt.plot(mfcc_speech[:,feature], 'r')
plt.grid()

plt.subplot(2, 2, 4)
plt.plot(mfcc_lib_db.T[:,feature], 'b')
plt.plot(mfcc_torch_db.T[:,feature], 'g')
plt.grid()

说实话,这些实现都不令人满意:

  • Python_speech_features采用了一种难以理解的方法,将第0个特征替换为能量而不是添加它,并且没有常用的delta实现。

  • Librosa默认情况下非标准化且没有警告,并且缺乏明显的用于增强能量的方法,但在库的其他地方具有高度竞争力的delta函数。

  • Torchaudio将模拟任何一种方法,并且具有多功能的delta函数,但仍然没有干净,明显的获取能量的方法。

输入图像描述


0
关于与tf.signal的区别,对于仍在寻找答案的人:我之前也遇到过类似的问题:将librosa的mel滤波器组/ mel频谱图匹配到tensorflow实现。解决方案是为频谱图使用不同的窗口方法,并将librosa的mel矩阵作为常量张量。请参见此处此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接