MFCC: классические признаки речи

Финальный классический признак речи, десятилетиями бывший стандартом ASR.

MFCC (Mel-Frequency Cepstral Coefficients) — компактный набор коэффициентов, описывающих огибающую мел-спектра; долгое время это был главный признак для распознавания речи.

Мел-спектрограмма уже неплоха, но у неё есть проблема: соседние мел-каналы сильно скоррелированы (энергия «течёт» между ними), и данных всё ещё много. MFCC решают это, выжимая из мел-спектра самое существенное — форму огибающей — в 13–20 чисел на кадр.

Путь к MFCC

MFCC — это финал длинной цепочки, в которой собрано всё, что мы прошли. Каждый шаг убирает лишнее и оставляет полезное.

сигнал
  --> окна (STFT)
  --> спектр мощности
  --> мел-фильтры (мел-спектр)
  --> логарифм (лог-мел)
  --> DCT (косинусное преобразование)
  --> MFCC: 13-20 коэффициентов на кадр

Зачем DCT в конце

Последний шаг — DCT (дискретное косинусное преобразование). Оно делает две вещи: убирает корреляцию между каналами (коэффициенты становятся почти независимыми) и концентрирует информацию в первых нескольких коэффициентах. Поэтому хватает первых 13: они описывают общую форму спектра (форманты), а высшие — мелкие детали, которые часто отбрасывают. Это та же идея, что в сжатии JPEG.

MFCC и мел-спектрограмма: что выбрать

Мел-спектрограммаMFCC
Размер40–80 на кадр13–20 на кадр
Корреляциявысокаянизкая
Для нейросетейлучше (есть детали)избыточно сжато
Для классики (HMM/GMM)хужестандарт

Важный нюанс: в эпоху глубокого обучения часто возвращаются к мел-спектрограмме, потому что нейросеть сама найдёт нужные комбинации, и лишнее сжатие DCT ей мешает. MFCC же остаются королём для лёгких классических моделей и задач вроде определения говорящего.

Имитация DCT-сжатия

Покажем суть DCT на пальцах: возьмём лог-мел-«вектор» и посчитаем несколько косинусных коэффициентов. Первый отражает общий уровень, следующие — форму.

import math

log_mel = [2.0, 2.1, 1.8, 1.2, 0.9, 0.7]  # лог-энергии мел-каналов
N = len(log_mel)

def dct_coef(k):
    s = sum(log_mel[n] * math.cos(math.pi * k * (n + 0.5) / N) for n in range(N))
    return round(s, 3)

mfcc = [dct_coef(k) for k in range(4)]
print("Первые 4 MFCC:", mfcc)

Вывод:

Первые 4 MFCC: [8.7, 2.26, -0.26, -0.354]

Первый коэффициент большой (общая энергия), остальные малы — информация сконцентрировалась в начале. Именно поэтому хватает 13 коэффициентов вместо всех мел-каналов.

Чтобы понять, зачем вообще нужен финальный шаг DCT, полезно увидеть проблему мел-спектрограммы наглядно. Соседние мел-каналы почти никогда не бывают независимыми: если в речи усилилась некая полоса частот, подсветятся сразу несколько соседних фильтров — их треугольники перекрываются и ловят одну и ту же энергию. Для классических моделей вроде GMM это была беда: они предполагали, что признаки не коррелируют между собой, и «спотыкались» на дублирующей информации. DCT решает это одним движением — переупаковывает скоррелированные каналы в набор почти независимых коэффициентов, на которых старым моделям дышалось куда легче.

Связь MFCC с JPEG — не просто красивое сравнение, а буквально одна и та же математика. И там, и там сигнал раскладывают косинусным преобразованием и обнаруживают, что почти вся энергия скапливается в первых, низкочастотных коэффициентах, а хвост описывает мелкие детали, которыми можно пожертвовать. JPEG отбрасывает хвост, чтобы сжать картинку; MFCC отбрасывают высшие коэффициенты, чтобы оставить только общую форму спектра — огибающую, несущую форманты. Поэтому 13 чисел на кадр — это не магическая константа, а тот же принцип «80% смысла в 20% коэффициентов», что лежит в основе половины алгоритмов сжатия.

Полезно понимать, почему вокруг MFCC и мел-спектрограммы регулярно ломают копья — ответ зависит от того, кто потребляет признак. Жёсткое DCT-сжатие — это решение, принятое за модель человеком: «вот тебе готовая огибающая, остальное лишнее». Для слабой классической модели это подарок. Но глубокая нейросеть сама прекрасно находит нужные комбинации мел-каналов и даже учитывает их корреляции как полезный сигнал, поэтому навязанное DCT лишь обедняет вход. Отсюда практическое правило эпохи Whisper: лёгким моделям и задачам вроде верификации диктора давайте MFCC, а большим сетям — сырую лог-мел-спектрограмму и не мешайте им учиться.

Как работает под капотом

К статичным MFCC обычно добавляют их производные — дельты (как быстро меняются) и дельта-дельты (ускорение). Это даёт модели представление о динамике речи между кадрами. В классических ASR вектор признаков выглядел как 13 MFCC + 13 дельт + 13 дельта-дельт = 39 чисел на кадр. Эти 39 чисел и были входом HMM/GMM, о которых — следующий раздел.

Частые ошибки

  • Брать MFCC для глубоких сетей по инерции. Часто лог-мел-спектрограмма работает лучше — сеть сама извлечёт нужное.
  • Оставлять все коэффициенты. Высшие MFCC шумны; обычно берут первые 13.
  • Забывать дельты. Без производных теряется динамика речи между кадрами.

Итоги

  • MFCC — сжатые коэффициенты огибающей мел-спектра (обычно 13 на кадр).
  • DCT убирает корреляцию каналов и концентрирует информацию в начале.
  • Для классики MFCC — стандарт; для глубоких сетей часто лучше мел-спектрограмма.
  • К MFCC добавляют дельты и дельта-дельты, чтобы захватить динамику.
Проверьте себя
1. Что такое MFCC?
AСырая волна
BКомпактные коэффициенты огибающей мел-спектра (≈13 на кадр)
CЧастота сэмплирования
DТип микрофона
2. Зачем после мел-спектра применяют DCT?
AЧтобы сделать звук громче
BЧтобы убрать корреляцию каналов и сконцентрировать информацию в первых коэффициентах
CЧтобы перевести в другой язык
DЭто случайный шаг
3. Почему для глубоких нейросетей часто предпочитают мел-спектрограмму, а не MFCC?
AMFCC слишком большие
BСеть сама найдёт нужные комбинации, а сжатие DCT отбрасывает полезные детали
CМел-спектрограмма быстрее считается
DMFCC не работают