신경망(Neural Network)은 원시 파형(raw waveform)을 그대로 잘 소화하지 못합니다. 스펙트로그램(spectrogram)은 그보다 낫고, 멜 스펙트로그램(mel spectrogram)은 한층 더 잘 받아들입니다. 2026년의 모든 음성 인식(ASR), 음성 합성(TTS), 오디오 분류기(audio classifier)는 결국 이 전처리(preprocessing) 선택 하나에 따라 성패가 갈립니다.
유형: Build
언어: Python
선수 강의: Phase 6 · 01 (오디오 기초 — 파형, 샘플링, 푸리에 변환)
예상 시간: 약 45분
학습 목표
단시간 푸리에 변환(Short-Time Fourier Transform; STFT)이 파형을 시간 × 주파수 행렬(time × frequency matrix)로 변환하는 과정을 설명합니다.
멜 스케일(Mel scale)과 멜 필터뱅크(mel filterbank)가 인간의 청각 지각(human perception)에 맞춰 주파수 축을 휘게 만드는 방식을 이해합니다.
로그-멜 스펙트로그램(log-mel spectrogram)과 멜 주파수 켑스트럼 계수(Mel-Frequency Cepstral Coefficients; MFCC)의 차이를 구분합니다.
대상 모델(target model)에 맞춰 프레임(frame), 홉(hop), 멜 개수(mel count), 정규화(normalization)를 선택합니다.
문제
10초짜리 16 kHz 클립(clip)을 떠올려 봅시다. 이는 160,000개의 부동소수점(float)이며, 모두 [-1, 1] 범위에 들어 있지만, "dog barking"이나 "the word cat" 같은 레이블(label)과는 거의 무상관에 가깝습니다. 원시 파형은 정보를 담고 있지만, 모델이 쉽게 뽑아낼 수 있는 형태가 아닙니다. 같은 음소(phoneme)라도 100 ms 차이로 발화하면 원시 샘플(raw sample) 값은 완전히 달라집니다.
스펙트로그램은 이 문제를 줄여 줍니다. 사람이 듣지 못하는 마이크로초 수준의 미세한 흔들림(microsecond jitter)은 접어 버리고, 사람이 주목하는 구조, 즉 약 10–25 ms의 시간 창(time window) 동안 어떤 주파수가 강하게 나타나는지를 보존합니다.
멜 스펙트로그램은 한 걸음 더 나아갑니다. 사람은 음높이(pitch)를 로그적으로 인지합니다. 100 Hz와 200 Hz의 거리는 1000 Hz와 2000 Hz의 거리와 "같은 정도로" 느껴집니다. 멜 스케일은 이 지각에 맞도록 주파수 축을 휘게 만듭니다. 멜로 스케일링된 스펙트로그램(mel-scaled spectrogram)은 2010년부터 2026년에 이르기까지 음성 머신러닝(speech ML)에서 가장 중요한 피처(feature)입니다.
사전 테스트
2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요
1.멜 스케일(Mel scale)이 1 kHz 이상에서 주파수 축을 선형이 아닌 로그적으로 휘게 만드는 이유는 무엇인가요?
2.로그-멜 스펙트로그램(log-mel spectrogram)과 MFCC를 음성 모델의 입력 피처로 쓸 때 핵심 차이점은 무엇인가요?
0/2 답변 완료
개념
STFT(Short-Time Fourier Transform). 파형을 겹치는 프레임으로 잘라 냅니다. 16 kHz 기준으로 25 ms 윈도우(window)와 10 ms 홉(hop), 즉 400 샘플 / 160 샘플이 일반적입니다. 각 프레임에는 윈도우 함수를 곱합니다(기본은 Hann 윈도우이며, Hamming은 약간 다른 트레이드오프를 갖습니다). 그런 다음 각 프레임에 고속 푸리에 변환(Fast Fourier Transform; FFT)을 적용합니다. 크기 스펙트럼(magnitude spectrum)을 쌓으면 (n_frames, n_freq_bins) 형태의 행렬이 되며, 이것이 스펙트로그램입니다.
로그-크기(Log-magnitude). 원시 크기는 5~6자릿수의 동적 범위(dynamic range)를 갖습니다. log(|X| + 1e-6) 또는 20 * log10(|X|)로 압축합니다. 모든 프로덕션 파이프라인은 원시 크기가 아니라 로그-크기를 사용합니다.
멜 스케일(Mel scale). Hz 단위 주파수 f는 m = 2595 * log10(1 + f / 700)을 통해 멜 값 m으로 변환됩니다. 1 kHz 아래에서는 거의 선형(linear)이고 그 위에서는 거의 로그적(logarithmic)입니다. 0–8 kHz를 덮는 80개의 멜 빈(mel bin)이 ASR 입력의 표준입니다.
멜 필터뱅크(Mel filterbank). 멜 스케일에서 등간격으로 배치된 삼각 필터(triangular filter) 모음입니다. 각 필터는 인접한 FFT 빈의 가중합(weighted sum)입니다. STFT 크기에 필터뱅크 행렬을 곱하면 단 한 번의 행렬곱(matmul)으로 멜 스펙트로그램이 만들어집니다.
로그-멜 스펙트로그램(Log-mel spectrogram).log(mel_spec + 1e-10)입니다. Whisper의 입력이고, Parakeet의 입력이며, SeamlessM4T의 입력입니다. 2026년의 보편적인 오디오 프론트엔드(audio frontend)입니다.
MFCC(Mel-Frequency Cepstral Coefficients). 로그-멜 스펙트로그램에 이산 코사인 변환(Discrete Cosine Transform; DCT) Type II를 적용한 뒤 앞쪽 13개의 계수를 남깁니다. 피처를 서로 비상관화(decorrelate)하고 더 압축합니다. 2015년 무렵 CNN/Transformer가 원시 로그-멜을 잘 다루기 전까지 지배적인 피처였으며, 화자 인식(speaker recognition) 분야에서는 x-vectors와 ECAPA 같은 곳에서 지금도 쓰입니다.
해상도 트레이드오프(Resolution trade). FFT가 클수록 주파수 해상도(frequency resolution)는 좋아지지만 시간 해상도(time resolution)는 나빠집니다. 오디오 머신러닝의 기본은 25 ms / 10 ms이고, 음악(music)에는 50 ms / 12.5 ms, 트랜지언트 감지(transient detection; 드럼 타격, 파열음 등)에는 5 ms / 2 ms가 쓰입니다.
직접 만들기
Step 1: 파형을 프레임으로 자르기
defframe(signal, frame_len, hop):
n = 1 + (len(signal) - frame_len) // hop
return [signal[i * hop : i * hop + frame_len] for i inrange(n)]
import math
defhann(N):
return [0.5 * (1 - math.cos(2 * math.pi * n / (N - 1))) for n inrange(N)]
FFT 적용 전에 원소별(element-wise)로 곱합니다. 양 끝이 0이 아닌 지점에서 잘려 나가면서 발생하는 스펙트럼 누설(spectral leakage)을 줄여 줍니다.
Step 3: STFT 크기
defstft_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 inzip(win, f)])) for f in frames]
실제 프로덕션에서는 FFT 기반으로 벡터화된 torch.stft 또는 librosa.stft를 사용합니다. 여기에 있는 반복문은 학습 목적이며, code/main.py에 있는 짧은 클립에서 실행됩니다.
Step 4: 멜 필터뱅크
defhz_to_mel(f):
return2595.0 * math.log10(1.0 + f / 700.0)
defmel_to_hz(m):
return700.0 * (10 ** (m / 2595.0) - 1)
defmel_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 inrange(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 _ inrange(n_mels)]
for m inrange(n_mels):
for k inrange(bins[m], bins[m + 1]):
fb[m][k] = (k - bins[m]) / max(1, bins[m + 1] - bins[m])
for k inrange(bins[m + 1], bins[m + 2]):
fb[m][k] = (bins[m + 2] - k) / max(1, bins[m + 2] - bins[m + 1])
return fb
0–8 kHz를 덮는 80개의 멜 빈을 n_fft=400으로 만들면 (80, 201) 행렬이 됩니다. (n_frames, 201) STFT 크기에 이 행렬의 전치(transpose)를 곱하면 (n_frames, 80) 형태의 멜 스펙트로그램이 만들어집니다.
Step 5: 로그-멜
deflog_mel(mel_spec, eps=1e-10):
return [[math.log(max(v, eps)) for v in frame] for frame in mel_spec]
흔히 사용되는 대안으로는 librosa.power_to_db(기준값 기반 dB 정규화)와 10 * log10(power + eps)가 있습니다. Whisper는 클리핑과 정규화를 함께 적용하는 더 정교한 루틴을 사용합니다(Whisper의 log_mel_spectrogram 참고).
Step 6: MFCC
defdct_ii(x, n_coeffs):
N = len(x)
return [
sum(x[n] * math.cos(math.pi * k * (2 * n + 1) / (2 * N)) for n inrange(N))
for k inrange(n_coeffs)
]
각 로그-멜 프레임에 DCT를 적용한 뒤 앞쪽 13개의 계수를 남기면 MFCC 행렬이 됩니다. 보통 첫 번째 계수는 전체 에너지(overall energy)를 담기 때문에 떨어내고 사용합니다.
사용해 보기
2026년 기준 표준 스택은 다음과 같습니다.
작업
피처
ASR (Whisper, Parakeet, SeamlessM4T)
80개 로그-멜, 10 ms 홉, 25 ms 윈도우
TTS 음향 모델 (VITS, F5-TTS, Kokoro)
80개 멜, 정밀한 시간 제어를 위해 5–12 ms 홉
오디오 분류 (AST, PANNs, BEATs)
128개 로그-멜, 10 ms 홉
화자 임베딩 (ECAPA-TDNN, WavLM)
80개 로그-멜 또는 원시 파형 기반 SSL
음악 (MusicGen, Stable Audio 2)
EnCodec 이산 토큰(mel이 아님)
키워드 스포팅(keyword spotting)
초소형 디바이스용 40개 MFCC
경험 법칙(rule of thumb)은 단순합니다. 음악 작업이 아니라면 80개의 로그-멜에서 시작하십시오. 거기서 벗어나려면 그만한 근거가 있어야 합니다.
2026년에도 여전히 출하되는 함정들
멜 개수 불일치. 학습은 80개 멜로 했는데 추론은 128개 멜로 진행하면 조용히 실패합니다. 양쪽 끝에서 피처의 shape를 로깅하십시오.
상류단의 샘플링 레이트 불일치. 22.05 kHz에서 계산한 멜은 16 kHz의 멜과 다릅니다. 피처를 만들기 전에 샘플링 레이트(SR)를 맞추십시오.
dB vs log. Whisper는 dB-멜이 아니라 로그-멜을 기대합니다. 일부 Hugging Face 파이프라인은 자동으로 감지하지만, 직접 작성한 코드는 그렇게 해 주지 않습니다.
정규화 표류(Normalization drift). 학습에서는 발화 단위(per-utterance) 정규화를 쓰고, 추론에서는 전역(global) 정규화를 쓰면 WER(단어 오류율)이 두 배가 되는 프로덕션 버그가 됩니다.
패딩에서 누설. 클립 끝을 0으로 패딩하면 마지막 프레임에 평탄한 스펙트럼이 생깁니다. 대칭으로 패딩(pad symmetrically)하거나 복제(replicate)하십시오.
산출물 만들기
outputs/skill-feature-extractor.md로 저장합니다. 이 스킬은 주어진 대상 모델에 맞춰 피처 타입, 멜 개수, 프레임/홉, 정규화 전략을 골라 줍니다.
연습문제
쉬움.code/main.py를 실행합니다. 200 Hz에서 4000 Hz로 올라가는 처프(chirp)를 합성하고, 프레임별 argmax 멜 빈을 출력합니다. (선택) 그래프로 그려 스윕(sweep)과 일치하는지 확인합니다.
중간.n_mels를 {40, 80, 128}, frame_len을 {200, 400, 800}으로 바꿔 다시 실행합니다. 시간축에서 날카로운 피크의 대역폭(sharp-peak bandwidth)을 측정해, 어떤 조합이 처프를 가장 잘 분해하는지 확인합니다.
어려움.power_to_db를 구현하고, AudioMNIST에서 작은 CNN 분류기로 (a) 원시 로그-멜, (b) ref=max 기반 dB-멜, (c) MFCC-13 + delta + delta-delta의 top-1 정확도를 비교해 보고서로 정리합니다.