MFCC Python: совершенно другой результат от librosa vs python_speech_features vs tensorflow.signal

Я пытаюсь извлечь функции MFCC из аудио (файл .wav), и я пробовал python_speech_features и librosa, но они дают совершенно разные результаты:

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:  введите описание изображения здесь

python_speech_features:  введите описание изображения здесь

Я неправильно передал параметры для этих двух методов? Почему здесь такая огромная разница?

Обновление: Я также пробовал реализацию tensorflow.signal и вот результат:

введите описание изображения здесь

Сам сюжет ближе к таковому из librosa, но масштаб ближе к python_speech_features. (Обратите внимание, что здесь я вычислил 80 бинов Mel и взял первые 13; если я сделаю расчет только с 13 бинами, результат также будет совсем другим). Код ниже:

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?


person TYZ    schedule 02.03.2020    source источник
comment
Интересный. Имеет ли значение передача частоты дискретизации в версию psf как kwarg, то есть samplerate=sr? Или поменять dct_type в версии librosa?   -  person Hendrik    schedule 02.03.2020
comment
Версия @Hendrik psf уже содержит sr в качестве входных данных. Что касается dct_type, есть некоторые изменения, когда я установил значение 3, но все еще очень далеко от выходов psf (1 и 2 почти идентичны).   -  person TYZ    schedule 02.03.2020
comment
@Hendrik Тоже пробовал tenorflow, он ближе к librosa, но масштаб другой.   -  person TYZ    schedule 02.03.2020
comment
Имеет ли значение передача частоты дискретизации в версию psf как kwarg, то есть samplerate=sr? - ›Я имел в виду, что вы передаете его как позиционный аргумент, но это аргумент ключевого слова. Не уверен, если передача его в качестве аргумента kwarg имеет значение здесь, но я бы придерживался (явного) API.   -  person Hendrik    schedule 02.03.2020
comment
@Hendrik Нет, без разницы.   -  person TYZ    schedule 02.03.2020


Ответы (2)


Здесь есть как минимум два фактора, объясняющие, почему вы получаете разные результаты:

  1. Единого определения шкалы mel не существует. Librosa реализовать два способа: Slaney и HTK. Другие пакеты могут и будут использовать другие определения, что приведет к другим результатам. При этом общая картина должна быть похожей. Это подводит нас ко второй проблеме ...
  2. python_speech_features по умолчанию ставит энергию в качестве первого (нулевого индекса) коэффициента (appendEnergy по умолчанию True), что означает, что когда вы запрашиваете, например, 13 MFCC, вы фактически получаете 12 + 1.

Другими словами, вы сравнивали коэффициенты не 13 librosa и 13 python_speech_features, а скорее 13 librosa против 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)

спектрограмма 1

Как видите, масштаб другой, но в целом картина очень похожа. Обратите внимание, что мне нужно было убедиться, что количество параметров, передаваемых модулям, одинаковое.

person Lukasz Tracewski    schedule 02.03.2020
comment
Спасибо, что изучили это! Я попытался добавить все параметры один за другим, чтобы увидеть, какой из них вызывает разницу, и обнаружил, что это appendEnergy, после добавления этого график выглядит очень похожим. Что касается масштаба, я думаю, не будет особого значения, если мы сделаем масштабирование после этого. - person TYZ; 02.03.2020
comment
@TYZ Верно. При этом, учитывая то, что вы делаете, я бы подумал о том, чтобы выйти за рамки MFCC. - person Lukasz Tracewski; 03.03.2020
comment
Есть ли у вас предложения по поводу других вариантов? В настоящее время я читаю wav2vec от fairseq, но я определенно открыт для дополнительных предложений. - person TYZ; 03.03.2020
comment
@TYZ Поскольку это касается исходного вопроса, я свяжусь с вами напрямую. - person Lukasz Tracewski; 03.03.2020

Это из тех вещей, которые не дают мне уснуть по ночам. Этот ответ правильный (и чрезвычайно полезный!), но не полный, потому что он не объясняет большого различия между двумя подходами. . Мой ответ добавляет существенные дополнительные детали, но все еще не дает точных совпадений.

То, что происходит, сложно и лучше всего объясняется с помощью длинного блока кода ниже, который сравнивает librosa и python_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-го элемента все еще есть серьезные необъяснимые смещения, а более высокие элементы имеют тенденцию дрейфовать друг от друга. Это может быть что-то столь же простое, как различные реализации под капотом или немного разные числовые константы стабильности, или это может быть что-то, что можно исправить с помощью точной настройки, например, выбор заполнения или, возможно, ссылка в каком-либо преобразовании децибел. Я правда не знаю.

Вот пример кода:

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-й функции энергией, а не ее дополнением, и не имеет обычно используемой дельта-реализации.

  • Librosa по умолчанию нестандартна, без предупреждений и не имеет очевидного способа увеличения энергии, но имеет очень компетентную дельта-функцию в другом месте библиотеки.

  • Torchaudio будет имитировать и то, и другое, также имеет универсальную дельта-функцию, но по-прежнему не имеет чистого, очевидного способа получения энергии.

введите описание изображения здесь

person Novak    schedule 31.03.2020
comment
Спасибо за лишнюю милю! +1 - person Lukasz Tracewski; 14.12.2020