Phase 06 - Lesson 02

Espectrogramas, Escala Mel e Features de Áudio

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Redes neurais não consomem bem formas de onda cruas. Elas consomem espectrogramas. Consomem espectrogramas mel ainda melhor. Todo ASR, TTS e classificador de áudio em 2026 vive ou morre por essa única escolha de pré-processamento.

Type: Build Languages: Python Prerequisites: Phase 6 · 01 (Audio Fundamentals) Time: ~45 minutes

O Problema

Pegue um clipe de 10 segundos a 16 kHz. São 160.000 floats, todos em [-1, 1], quase perfeitamente descorrelacionados do rótulo "cachorro latindo" ou "a palavra gato". A forma de onda crua tem a informação, mas em um formato que o modelo não consegue extrair facilmente. Dois fonemas idênticos pronunciados com 100 ms de diferença têm amostras cruas completamente diferentes.

Um espectrograma resolve isso. Ele descarta o detalhe temporal onde a percepção humana o ignora (jitter de microssegundos) e preserva a estrutura onde a percepção presta atenção (quais frequências são energéticas, ao longo de janelas de tempo de ~10–25 ms).

Espectrogramas mel vão mais longe. Os humanos percebem o tom (pitch) de forma logarítmica: 100 Hz vs 200 Hz soa "à mesma distância" que 1000 Hz vs 2000 Hz. A escala mel deforma o eixo de frequência para corresponder a isso. Um espectrograma em escala mel é a feature mais importante em ML de fala de 2010 até 2026.

O Conceito

Escada de forma de onda para STFT para espectrograma mel para MFCC

STFT (Short-Time Fourier Transform). Fatie a forma de onda em quadros (frames) sobrepostos (típico: janela de 25 ms, salto de 10 ms = 400 amostras / 160 amostras a 16 kHz). Multiplique cada quadro por uma função de janela (Hann é o padrão; Hamming tem um tradeoff levemente diferente). Aplique FFT em cada quadro. Empilhe os espectros de magnitude em uma matriz de formato (n_frames, n_freq_bins). Esse é o seu espectrograma.

Log-magnitude. Magnitudes cruas abrangem 5-6 ordens de grandeza. Aplique log(|X| + 1e-6) ou 20 * log10(|X|) para comprimir a faixa dinâmica. Todo pipeline de produção usa log-magnitude, não magnitude crua.

Escala mel. A frequência f em Hz mapeia para mel m por m = 2595 * log10(1 + f / 700). O mapeamento é aproximadamente linear abaixo de 1 kHz e aproximadamente logarítmico acima. 80 bins mel cobrindo 0–8 kHz é a entrada padrão de ASR.

Banco de filtros mel (mel filterbank). Um conjunto de filtros triangulares espaçados igualmente na escala mel. Cada filtro é uma soma ponderada de bins adjacentes da FFT. Multiplicar a magnitude da STFT pela matriz do banco de filtros dá o espectrograma mel em uma única multiplicação de matrizes (matmul).

Espectrograma log-mel. log(mel_spec + 1e-10). A entrada do Whisper. A entrada do Parakeet. A entrada do SeamlessM4T. O frontend de áudio universal de 2026.

MFCCs. Pegue o espectrograma log-mel, aplique uma DCT (tipo II), mantenha os primeiros 13 coeficientes. Descorrelaciona as features e comprime ainda mais. Feature dominante até cerca de 2015, quando CNNs/Transformers sobre log-mels cruos a alcançaram. Ainda usada em reconhecimento de locutor (x-vectors, ECAPA).

Tradeoff de resolução. FFT maior = melhor resolução de frequência, mas pior resolução temporal. 25 ms / 10 ms é o padrão de ML de áudio; 50 ms / 12,5 ms para música; 5 ms / 2 ms para detecção de transientes (batidas de bateria, plosivas).

Construa

Passo 1: dividir a forma de onda em quadros

def frame(signal, frame_len, hop):
    n = 1 + (len(signal) - frame_len) // hop
    return [signal[i * hop : i * hop + frame_len] for i in range(n)]

Um clipe de 10 segundos a 16 kHz com frame_len=400, hop=160 produz 998 quadros.

Passo 2: janela de Hann

import math

def hann(N):
    return [0.5 * (1 - math.cos(2 * math.pi * n / (N - 1))) for n in range(N)]

Multiplique elemento a elemento antes da FFT. Remove o vazamento espectral (spectral leakage) causado por truncar em extremidades diferentes de zero.

Passo 3: magnitude da STFT

def stft_magnitude(signal, frame_len=400, hop=160):
    win = hann(frame_len)
    frames = frame(signal, frame_len, hop)
    return [magnitudes(dft([w * s for w, s in zip(win, f)])) for f in frames]

A produção usa torch.stft ou librosa.stft (apoiados em FFT, vetorizados). O laço aqui é pedagógico; ele roda em clipes curtos em code/main.py.

Passo 4: banco de filtros mel

def hz_to_mel(f):
    return 2595.0 * math.log10(1.0 + f / 700.0)

def mel_to_hz(m):
    return 700.0 * (10 ** (m / 2595.0) - 1)

def mel_filterbank(n_mels, n_fft, sr, fmin=0, fmax=None):
    fmax = fmax or sr / 2
    mels = [hz_to_mel(fmin) + (hz_to_mel(fmax) - hz_to_mel(fmin)) * i / (n_mels + 1)
            for i in range(n_mels + 2)]
    hzs = [mel_to_hz(m) for m in mels]
    bins = [int(h * n_fft / sr) for h in hzs]
    fb = [[0.0] * (n_fft // 2 + 1) for _ in range(n_mels)]
    for m in range(n_mels):
        for k in range(bins[m], bins[m + 1]):
            fb[m][k] = (k - bins[m]) / max(1, bins[m + 1] - bins[m])
        for k in range(bins[m + 1], bins[m + 2]):
            fb[m][k] = (bins[m + 2] - k) / max(1, bins[m + 2] - bins[m + 1])
    return fb

80 mels cobrindo 0–8 kHz com n_fft=400 dá uma matriz (80, 201). Multiplique a magnitude da STFT (n_frames, 201) pela transposta para obter o espectrograma mel (n_frames, 80).

Passo 5: log-mel

def log_mel(mel_spec, eps=1e-10):
    return [[math.log(max(v, eps)) for v in frame] for frame in mel_spec]

Alternativas comuns: librosa.power_to_db (dB normalizado por referência), 10 * log10(power + eps). O Whisper usa uma rotina de clip + normalização mais elaborada (veja o log_mel_spectrogram do Whisper).

Passo 6: MFCCs

def dct_ii(x, n_coeffs):
    N = len(x)
    return [
        sum(x[n] * math.cos(math.pi * k * (2 * n + 1) / (2 * N)) for n in range(N))
        for k in range(n_coeffs)
    ]

Aplique a DCT a cada quadro log-mel, mantenha os primeiros 13 coeficientes. Essa é a sua matriz MFCC. O primeiro coeficiente costuma ser descartado (ele codifica a energia geral).

Use

A stack de 2026:

Tarefa Features
ASR (Whisper, Parakeet, SeamlessM4T) 80 log-mels, salto de 10 ms, janela de 25 ms
Modelo acústico de TTS (VITS, F5-TTS, Kokoro) 80 mels, salto de 5–12 ms para controle temporal fino
Classificação de áudio (AST, PANNs, BEATs) 128 log-mels, salto de 10 ms
Embedding de locutor (ECAPA-TDNN, WavLM) 80 log-mels ou SSL sobre forma de onda crua
Música (MusicGen, Stable Audio 2) Tokens discretos do EnCodec (não mels)
Detecção de palavra-chave (keyword spotting) 40 MFCCs para dispositivos minúsculos

Regra prática: se você não está trabalhando com música, comece com 80 log-mels. O ônus da prova recai sobre qualquer desvio.

Armadilhas que ainda chegam à produção em 2026

  • Incompatibilidade na contagem de mels. Treinar com 80 mels, inferir com 128 mels. Falha silenciosa. Logue o formato (shape) da feature nas duas pontas.
  • Incompatibilidade de sample rate upstream. Mels computados a 22,05 kHz parecem diferentes dos de 16 kHz. Corrija o SR antes da extração de features.
  • dB vs log. O Whisper espera log-mel, não dB-mel. Alguns pipelines do HF autodetectam; o seu código customizado não vai.
  • Deriva de normalização. Normalização por enunciado durante o treino, normalização global durante a inferência. Bug de produção que dobra o WER.
  • Vazamento por padding. Preencher o final de um clipe com zeros produz um espectro plano nos quadros finais. Faça padding simétrico ou replique.

Entregue (Ship It)

Salve como outputs/skill-feature-extractor.md. A skill escolhe o tipo de feature, a contagem de mels, o frame/hop e a normalização para um determinado modelo-alvo.

Exercícios

  1. Fácil. Rode code/main.py. Ele sintetiza um chirp (frequência varrida de 200 → 4000 Hz) e imprime o argmax do bin mel por quadro. Plote (opcional) e confirme que corresponde à varredura.
  2. Médio. Reexecute com n_mels em {40, 80, 128} e frame_len em {200, 400, 800}. Meça a largura de banda do pico agudo ao longo do eixo temporal. Qual combinação resolve melhor o chirp?
  3. Difícil. Implemente power_to_db e compare a acurácia de ASR de um pequeno classificador CNN no AudioMNIST usando (a) log-mel cru, (b) dB-mel com ref=max, (c) MFCC-13 + delta + delta-delta. Reporte a acurácia top-1.

Termos-Chave

Termo O que as pessoas dizem O que realmente significa
Frame Uma fatia Trecho de 25 ms da forma de onda alimentado em uma FFT.
Hop Stride Amostras entre quadros consecutivos; 10 ms é o padrão de ASR.
Window Coisa Hann/Hamming Multiplicador ponto a ponto que afina as bordas do quadro até zero.
STFT Gerador de espectrograma FFT enquadrada + janelada; produz matriz tempo × frequência.
Mel Frequência deformada Escala de log-percepção; m = 2595·log10(1 + f/700).
Filterbank A matriz Filtros triangulares que projetam a STFT em bins mel.
Log-mel A entrada do Whisper log(mel_spec + eps); padronizada em 2026.
MFCC Feature da velha guarda DCT do log-mel; 13 coeficientes, descorrelacionados.

Leitura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).