Python으로 배우는 DSP/Part 2. 주파수 영역 분석: Fourier & Wavelet

[12] STFT와 스펙트로그램: 시간에 따라 변하는 주파수 분석 (오디오 분석)

multimedia 2026. 4. 25. 10:00
반응형

들어가며

지금까지 배운 푸리에 변환(FFT)과 윈도우 함수를 이용하면 신호에 어떤 주파수가 들어있는지 명확하게 알 수 있습니다. 하지만 이 주파수 스펙트럼은 치명적인 단점이 있습니다. 바로 "그 주파수가 시간상 '언제' 발생했는지"를 알 수 없다는 것입니다. 악보에 '도, 미, 솔'이 있다는 것은 알지만, 순서대로 친 것인지 동시에 친 화음인지 알 길이 없습니다.

이를 해결하기 위해 신호 전체를 한 번에 분석하는 대신, 윈도우 함수를 이용해 신호를 짧은 시간 단위로 잘라가며 주파수 변화를 추적하는 기술이 등장합니다. 오늘 다룰 내용이자 오디오 및 음성 분석의 꽃이라 불리는 STFT(Short-Time Fourier Transform)와 그 결과를 시각화한 스펙트로그램(Spectrogram)을 통해 시간과 주파수를 동시에 꿰뚫어 보는 방법을 알아보겠습니다. 이러한 시각화를 통해서 악보에서의 음의 변화를 귀가 아닌 시각적인 그래프로 직접 확인할 수 있습니다. 

 

1. FFT의 한계: 시간 정보의 소멸

표준 FFT는 신호 전체를 하나의 블록으로 처리합니다. 따라서 분석 결과는 그 블록 전체 구간에 대해 "평균화된" 주파수 스펙트럼입니다. 아래 상황을 생각해 보면 문제가 명확해집니다.

  • 0~1초: 440 Hz (라, A4) 음이 울립니다.
  • 1~2초: 880 Hz (높은 라, A5) 음이 울립니다.

전체 2초 신호에 FFT를 적용하면 440 Hz와 880 Hz 두 피크가 나타나지만, "언제" 각 음이 울렸는지는 알 수 없습니다. 클래식 음악과 노이즈 음악이 동일한 스펙트럼을 가질 수도 있는 것입니다.

이 문제를 수학적으로 표현하면 다음과 같습니다. 신호 $x(t)$의 전역 푸리에 변환 $X(f)$는 아래와 같이 정의됩니다.

$$X(f) = \int_{-\infty}^{\infty} x(t) e^{-j2\pi ft} dt$$

이 식에서 시간 $t$는 적분에 의해 사라지고, 결과 $X(f)$는 오직 주파수 $f$만의 함수가 됩니다. 시간 정보가 완전히 소멸하는 것입니다.

2. STFT(Short-Time Fourier Transform)의 원리

STFT의 핵심 아이디어는 매우 직관적입니다. "시간이 변함에 따라 주파수도 변한다면, 시간이 거의 변하지 않는 짧은 구간(Short-Time)으로 나누어서 각각 푸리에 변환을 하자"는 것입니다. 긴 신호를 통째로 분석하는 대신, 짧은 윈도우를 시간 축을 따라 이동(슬라이딩)시키며 각 구간에 FFT를 반복 적용합니다. 각 위치에서의 FFT 결과를 시간 순서대로 나열하면, 시간에 따른 주파수 변화를 2차원 행렬로 얻을 수 있습니다.

수학적 정의는 다음과 같습니다.

$$\text{STFT}\{x[n]\}(m, k) = \sum_{n=-\infty}^{\infty} x[n] w[n - mH] e^{-j2\pi kn/N}$$

각 기호의 의미는 아래와 같습니다.

  • $x[n]$: 입력 이산 신호
  • $w[n]$: 윈도우 함수 (Hann, Hamming 등)
  • $m$: 프레임(Frame) 인덱스 — 윈도우의 위치
  • $H$: 홉 크기(Hop Size) — 윈도우 이동 간격
  • $N$: FFT 포인트 수 (= 윈도우 길이)
  • $k$: 주파수 빈(Bin) 인덱스 

STFT의 결과 $\text{STFT}(m, k)$는 복소수 행렬이며, 행 인덱스 $m$이 시간, 열 인덱스 $k$가 주파수에 대응합니다.

이전 11편에서 배운 윈도우 함수가 여기서 맹활약합니다. 신호의 일정 구간에 윈도우를 씌워 추출하고 FFT를 수행한 뒤, 윈도우를 일정 간격(Hop)만큼 옆으로 이동시키며 이 과정을 신호 끝까지 반복합니다. 결과적으로 1차원 형태였던 시간 영역의 신호가 시간과 주파수라는 두 개의 축을 가진 2차원 데이터로 변환됩니다.

 

3. 핵심 파라미터: 윈도우 길이, 홉 크기, 오버랩

STFT 품질을 결정하는 세 가지 핵심 파라미터가 있습니다.

3.1. 윈도우 길이(Window Length, $N$)

한 번의 FFT를 수행하는 프레임의 샘플 수입니다. 이 값은 시간-주파수 분해능 트레이드오프(Time-Frequency Resolution Trade-off)를 직접 결정합니다. 이것은 STFT에서 가장 중요한 개념 중 하나입니다.

  • $N$이 크면: 각 프레임이 긴 시간 구간을 포함하므로 주파수 분해능이 높아집니다(주파수 축이 세밀해집니다). 그러나 시간 분해능은 낮아져, 빠른 주파수 변화를 잡아내지 못할 수 있습니다. 
    • 장점: '어떤' 주파수인지 매우 정밀하게 찾아낼 수 있습니다. 
    • 단점: 윈도우 구간 내의 시간 정보가 뭉개져 버립니다. 
  • $N$이 작으면: 짧은 구간을 분석하므로 시간 분해능이 높아집니다(빠른 변화를 추적할 수 있습니다). 그러나 주파수 분해능은 낮아집니다. 
    • 장점: 타악기 소리, 갑작스러운 노이즈 등 트랜지언트 분석에 유리
    • 단점: 들어있는 데이터 포인트가 적어 주파수를 촘촘하게 구별해 내지 못합니다

이는 하이젠베르크의 불확정성 원리와 유사한 근본적인 한계입니다. 시간과 주파수를 동시에 완벽하게 알 수는 없습니다.

주파수 분해능과 시간 분해능은 다음과 같이 계산합니다.

$$\Delta f = \frac{f_s}{N}, \quad \Delta t = \frac{N}{f_s}$$

예를 들어 $f_s = 22050 \text{Hz}, N = 2048$이면: $\Delta f \approx 10.8 \text{Hz}, \Delta t \approx 93 \text{ms}$

3.2. 홉 크기(Hop Size, $H$)와 오버랩(Overlap)

홉 크기는 윈도우를 한 스텝 이동할 때의 샘플 수입니다. 오버랩 비율과의 관계는 다음과 같습니다.

$$H = N \times (1 - \text{Overlap Ratio})$$

오버랩 비율 75%일 때 $N = 2048$이면 $H = 512$입니다. 오버랩이 클수록 시간 축의 프레임 수가 늘어나 스펙트로그램이 부드럽게 이어지지만, 계산 비용이 증가합니다. 일반적으로 $50\% \sim 75\%$ 오버랩이 실용적인 선택입니다.

3.3. 파라미터 선택 가이드

아래 표는 분석의 대상에 따라 위에서 열거한 파라미터를 어떻게 설정할지에 대한 가이드를 제시하고 있습니다.

분석 대상 권장 N 권장 Hop 이유
음성(Speech) 512-1024 128-256 빠른 포먼트 변화 추적
음악(Music) 1024-4096 256-1024 음정 구별에 높은 주파수 분해능 필요
기계 진동 2048-8192 512-2048 저주파 성분의 정밀 분해
환경음(Bird song 등) 512-2048 128-512 빠른 음절 변화와 주파수 다양성 균형

STFT를 사용할 때 반드시 짚고 넘어가야 하는 가장 중요한 개념이 있습니다. 바로 시간 분해능(Time Resolution)과 주파수 분해능(Frequency Resolution) 사이의 트레이드오프(Trade-off)입니다. STFT를 설계할 때는 분석하고자 하는 신호의 특성에 맞춰 윈도우의 길이(NFFT, Window Size)와 겹치는 구간(Overlap, Hop Size)을 적절히 조율하는 것이 핵심 역량입니다.

 

4. 스펙트로그램(Spectrogram): 소리를 그리다

STFT를 거쳐 나온 결과물은 복소수 형태입니다. 우리가 눈으로 확인하기 위해서는 이 복소수의 크기(Magnitude)나 전력(Power)을 계산하여 컬러 맵으로 표현해야 합니다. 이렇게 STFT 결과를 2차원 이미지 형태로 시각화한 것을 스펙트로그램(Spectrogram)이라고 합니다.

$$S(m, k) = |\text{STFT}(m, k)|^2$$
  • X축: 시간(초)
  • Y축: 주파수(Hz)
  • 색상: 해당 시간·주파수 지점의 신호 에너지(밝을수록 강함)

스펙트로그램을 보면 시간의 흐름에 따라 어떤 주파수 대역이 강해지고 약해지는지를 마치 악보를 보듯 직관적으로 파악할 수 있습니다. 실무에서는 인간의 청각이 에너지를 로그 스케일로 인지하므로, dB 단위로 변환하여 표시합니다. 

$$S_{\text{dB}}(m, k) = 10 \log_{10}(S(m, k) + \epsilon)$$

 

5. Python 실습: 시간에 따라 주파수가 변하는 Chirp 신호 분석

5.1. 환경 준비 및 테스트 신호 생성

주파수가 시간에 따라 선형으로 증가하는 처프(Chirp) 신호를 생성하여 STFT의 특성을 확인합니다. 처프 신호는 시간 정보가 없는 FFT의 한계와 STFT의 강점을 가장 극적으로 보여주는 테스트 신호입니다.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from scipy import signal
from scipy.fft import fft, fftfreq

# 한국어 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

# 기본 파라미터
FS = 8000
DURATION = 4.0
t = np.linspace(0, DURATION, int(FS * DURATION), endpoint=False)

# 처프 신호: 0~4초 동안 200 Hz → 2000 Hz로 선형 증가
x_chirp = signal.chirp(t, f0=200, f1=2000, t1=DURATION, method='linear')

# 전역 FFT 계산 (비교용)
X = np.abs(fft(x_chirp))
freqs = fftfreq(len(t), 1 / FS)
pos_mask = freqs >= 0

fig, axes = plt.subplots(2, 1, figsize=(12, 6))

axes[0].plot(t, x_chirp, linewidth=0.5, color='steelblue')
axes[0].set_title('처프 신호 (시간 영역): 200 Hz → 2000 Hz', fontsize=12, fontweight='bold')
axes[0].set_xlabel('시간 [s]')
axes[0].set_ylabel('진폭')
axes[0].set_xlim(0, DURATION)
axes[0].grid(True, alpha=0.3)

axes[1].plot(freqs[pos_mask], X[pos_mask], linewidth=1.0, color='tomato')
axes[1].set_title('전역 FFT 결과: 시간 정보 소멸', fontsize=12, fontweight='bold')
axes[1].set_xlabel('주파수 [Hz]')
axes[1].set_ylabel('크기')
axes[1].set_xlim(0, FS / 2)
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('chirp_signal_fft.png', dpi=150, bbox_inches='tight')
plt.show()

위 실험 결과로부터 시간 영역에서 처프 신호는 왼쪽에서 오른쪽으로 갈수록 진동이 빨라지는 것을 시각적으로 확인할 수 있습니다. FFT 결과에서는 200~2000 Hz 구간에 걸쳐 에너지가 균등하게 분포하지만, 어느 주파수가 언제 발생했는지는 전혀 알 수 없습니다.

5.2. scipy.signal.stft로 STFT 계산

scipy.signal.stft를 사용하여 STFT를 계산하고 스펙트로그램을 시각화합니다.

# STFT 파라미터
N_FFT = 512
HOP = 128
WINDOW = 'hann'

# STFT 계산
freqs_stft, times_stft, Zxx = signal.stft(
    x_chirp,
    fs=FS,
    window=WINDOW,
    nperseg=N_FFT,
    noverlap=N_FFT - HOP
)

# 파워 스펙트로그램 (dB 변환)
S_power = np.abs(Zxx) ** 2
S_dB = 10 * np.log10(S_power + 1e-10)

fig, ax = plt.subplots(figsize=(12, 5))
img = ax.pcolormesh(times_stft, freqs_stft, S_dB,
                    shading='gouraud', cmap='inferno',
                    vmin=S_dB.max() - 60, vmax=S_dB.max())
cbar = fig.colorbar(img, ax=ax)
cbar.set_label('전력 [dB]', fontsize=10)
ax.set_title(f'스펙트로그램 (N={N_FFT}, Hop={HOP}, Overlap={100*(1-HOP/N_FFT):.0f}%)',
             fontsize=12, fontweight='bold')
ax.set_xlabel('시간 [s]')
ax.set_ylabel('주파수 [Hz]')
ax.set_ylim(0, 3000)
ax.grid(True, alpha=0.2, color='white', linestyle='--')
plt.tight_layout()
plt.savefig('spectrogram.png', dpi=150, bbox_inches='tight')
plt.show()

스펙트로그램에서 에너지가 집중된 밝은 선이 시간이 지남에 따라 아래에서 위로 선형으로 상승하는 것을 확인할 수 있습니다. 또한, 전역 FFT로는 알 수 없었던 주파수의 시간적 변화가 스펙트로그램에서는 한눈에 드러납니다.
 

5.3. 윈도우 길이에 따른 시간-주파수 분해능 비교

STFT의 핵심 트레이드오프를 직접 눈으로 확인합니다.

configs = [
    {'N': 128,  'hop': 32,  'label': 'N=128\n(높은 시간 분해능)'},
    {'N': 512,  'hop': 128, 'label': 'N=512\n(균형)'},
    {'N': 2048, 'hop': 512, 'label': 'N=2048\n(높은 주파수 분해능)'},
]

fig, axes = plt.subplots(1, 3, figsize=(16, 5), sharey=True)
fig.suptitle('윈도우 길이에 따른 시간-주파수 분해능 비교 (처프 신호)',
             fontsize=13, fontweight='bold')

for ax, cfg in zip(axes, configs):
    f, t_s, Zxx = signal.stft(x_chirp, fs=FS, window='hann',
                               nperseg=cfg['N'], noverlap=cfg['N'] - cfg['hop'])
    S_dB = 10 * np.log10(np.abs(Zxx) ** 2 + 1e-10)
    img = ax.pcolormesh(t_s, f, S_dB, shading='gouraud', cmap='inferno',
                        vmin=S_dB.max() - 60, vmax=S_dB.max())
    ax.set_title(cfg['label'], fontsize=11)
    ax.set_xlabel('시간 [s]')
    ax.set_ylim(0, 3000)
    ax.grid(True, alpha=0.2, color='white', linestyle='--')

axes[0].set_ylabel('주파수 [Hz]')
fig.colorbar(img, ax=axes[-1], label='전력 [dB]')
plt.tight_layout()
plt.savefig('stft_resolution_comparison.png', dpi=150, bbox_inches='tight')
plt.show()

 

위 실험 결과로부터 아래와 같은 내용을 관찰할 수 있습니다.

  • N=128: 시간 분해능이 높아 처프의 순간적인 위치 변화가 날카롭게 추적됩니다. 그러나 주파수 분해능이 낮아 에너지 띠의 수직 두께가 두껍습니다.
  • N=2048: 주파수 분해능이 높아 에너지 띠가 매우 얇고 선명합니다. 반면 시간 분해능이 낮아 처프 경사가 계단식으로 뭉개집니다.
  • N=512: 두 분해능 사이의 균형점으로, 대부분의 오디오 분석에서 무난하게 사용할 수 있습니다

 

6. Python 실습: 복합 오디오 신호 분석 - 멜로디 시뮬레이션 

6.1. 복합 오디오 신호 생성 및 스펙트로그램

실제 오디오 분석에 가까운 시나리오를 시뮬레이션합니다. 도(C4), 미(E4), 솔(G4) 음을 순서대로 연주한 뒤 동시에 화음(chord)으로 연주하는 신호를 생성합니다. 이렇게 생성된 신호를 바탕으로 스펙트로그램을 출력합니다.

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

# 한국어 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

FS2 = 22050
dur_note = 0.5
dur_chord = 1.0

def make_tone(freq, duration, fs, fade_ms=20):
    """단순 정현파 음 생성 (페이드 인/아웃 포함)"""
    t = np.linspace(0, duration, int(fs * duration), endpoint=False)
    tone = np.sin(2 * np.pi * freq * t)
    fade_samples = int(fs * fade_ms / 1000)
    fade_in  = np.linspace(0, 1, fade_samples)
    fade_out = np.linspace(1, 0, fade_samples)
    tone[:fade_samples]  *= fade_in
    tone[-fade_samples:] *= fade_out
    return tone

C4, E4, G4 = 261.6, 329.6, 392.0
silence = np.zeros(int(FS2 * 0.1))
signal_parts = [
    make_tone(C4, dur_note, FS2), silence,
    make_tone(E4, dur_note, FS2), silence,
    make_tone(G4, dur_note, FS2), silence,
    make_tone(C4, dur_chord, FS2) + make_tone(E4, dur_chord, FS2) + make_tone(G4, dur_chord, FS2),
]
x_melody = np.concatenate(signal_parts)
x_melody /= np.max(np.abs(x_melody))

N_FFT2 = 2048
HOP2   = 512
f2, t2, Zxx2 = signal.stft(x_melody, fs=FS2, window='hann',
                             nperseg=N_FFT2, noverlap=N_FFT2 - HOP2)
S_dB2 = 10 * np.log10(np.abs(Zxx2) ** 2 + 1e-10)

fig, axes = plt.subplots(2, 1, figsize=(13, 7))
t_melody = np.linspace(0, len(x_melody) / FS2, len(x_melody))

axes[0].plot(t_melody, x_melody, linewidth=0.5, color='steelblue')
axes[0].set_title('도-미-솔 멜로디 + 화음 (시간 영역)', fontsize=12, fontweight='bold')
axes[0].set_xlabel('시간 [s]')
axes[0].set_ylabel('진폭')
axes[0].set_xlim(0, len(x_melody) / FS2)
axes[0].grid(True, alpha=0.3)

img2 = axes[1].pcolormesh(t2, f2, S_dB2, shading='gouraud', cmap='magma',
                           vmin=S_dB2.max() - 60, vmax=S_dB2.max())
axes[1].set_title('스펙트로그램: 음표가 보인다', fontsize=12, fontweight='bold')
axes[1].set_xlabel('시간 [s]')
axes[1].set_ylabel('주파수 [Hz]')
axes[1].set_ylim(0, 1000)

for freq, name in [(C4, 'C4'), (E4, 'E4'), (G4, 'G4')]:
    axes[1].axhline(freq, color='cyan', linewidth=0.8, linestyle='--', alpha=0.7)
    axes[1].text(0.02, freq + 8, name, color='cyan', fontsize=9)

fig.colorbar(img2, ax=axes[1], label='전력 [dB]')
plt.tight_layout()
plt.savefig('melody_spectrogram.png', dpi=150, bbox_inches='tight')
plt.show()

상단의 그래프에 도(C4), 미(E4), 솔(G4) 음을 순서대로 연주한 뒤 동시에 화음(chord)으로 연주하는 신호가 시간 영역에서 표현되어 있습니다. 하단의 스펙트로그램에서는 도(C4), 미(E4), 솔(G4) 각 음에 해당하는 수평 에너지 줄기가 시간 순서대로 하나씩 나타났다가 사라집니다. 마지막 화음 구간에서는 세 줄기가 동시에 나타납니다. 우리는 스펙트로그램을 통해서 음표의 시작과 끝, 동시성(화음), 음높이를 모두 읽을 수 있습니다. 

6.2. librosa를 활용한 멜-스펙트로그램

오디오 AI 분야에서는 선형 주파수 축 대신 인간 청각의 로그적 주파수 인지를 반영한 멜 스케일(Mel Scale)로 변환한 멜-스펙트로그램(Mel Spectrogram)을 주로 사용합니다. 멜 스케일과 Hz의 관계는 다음과 같습니다.

$$m_{\text{mel}} = 2595 \log_{10} \left( 1 + \frac{f}{700} \right)$$

아래 실험에 사용되는 신호는 앞에서 만들어진 멜로디 복합신호를 사용하였습니다.

import librosa
import librosa.display

S_mel = librosa.feature.melspectrogram(
    y=x_melody,
    sr=FS2,
    n_fft=N_FFT2,
    hop_length=HOP2,
    n_mels=128,
    fmin=80,
    fmax=4000
)
S_mel_dB = librosa.power_to_db(S_mel, ref=np.max)

fig, ax = plt.subplots(figsize=(12, 5))
img = librosa.display.specshow(
    S_mel_dB,
    sr=FS2,
    hop_length=HOP2,
    x_axis='time',
    y_axis='mel',
    fmin=80,
    fmax=4000,
    cmap='magma',
    ax=ax
)
fig.colorbar(img, ax=ax, format='%+2.0f dB')
ax.set_title('멜-스펙트로그램 (Mel Spectrogram)', fontsize=12, fontweight='bold')
ax.set_xlabel('시간 [s]')
ax.set_ylabel('멜 주파수 [Hz]')
plt.tight_layout()
plt.savefig('mel_spectrogram.png', dpi=150, bbox_inches='tight')
plt.show()

사람의 귀는 저주파 대역의 변화에 훨씬 민감합니다. 멜 스케일은 이를 반영하여 저주파 영역을 촘촘하게, 고주파 영역을 성기게 배분합니다. 인간 청각 특성을 반영한 압축 표현이므로, 선형 스펙트로그램보다 음성·음악의 의미 있는 패턴이 더 넓은 영역에 분포하여 AI/ML 모델이 더 적은 파라미터로 효율적으로 학습할 수 있습니다. MFCC(Mel-Frequency Cepstral Coefficients)는 이 멜-스펙트로그램에서 한 단계 더 압축한 특징으로, 21편에서 자세히 다룹니다.

6.3. STFT 역변환(iSTFT): 신호 복원

STFT는 가역 변환입니다. 스펙트로그램에서 위상 정보가 보존되어 있다면, 역 STFT(iSTFT)로 원래 신호를 완벽하게 복원할 수 있습니다.

# iSTFT로 원신호 복원
_, x_reconstructed = signal.istft(
    Zxx2,
    fs=FS2,
    window='hann',
    nperseg=N_FFT2,
    noverlap=N_FFT2 - HOP2
)

# 길이 정렬
min_len = min(len(x_melody), len(x_reconstructed))
error = x_melody[:min_len] - x_reconstructed[:min_len]
rms_error = np.sqrt(np.mean(error ** 2))

print(f"복원 신호 RMS 오차: {rms_error:.2e}")
복원 신호 RMS 오차: 6.24e-17

오차가 $10^{-17}$ 수준으로, 부동소수점 연산의 정밀도 한계에 불과합니다. STFT-iSTFT 쌍은 완벽한 재구성(Perfect Reconstruction) 조건을 만족하며, 이는 오디오 이펙터, 음성 합성, 보코더 등의 기반이 됩니다.

 

7. STFT 주요 API 요약

STFT를 지원하는 주요 API를 아래 표에 정리하였습니다.

라이브러리 함수 특징
scipy.signal stft() / istft() 범용 STFT, 완벽한 재구성 지원
librosa stft() / istft() 오디오 특화, Mel 변환 등 편의 기능 풍부
librosa feature.melspectrogram() 멜-스펙트로그램 원스텝 계산
numpy / scipy.fft 직접 구현 학습 및 커스텀 목적

 

[Insight]

STFT와 스펙트로그램이 강력한 이유는 단순히 "FFT를 여러 번 수행한다"는 알고리즘적 단순함 때문이 아닙니다. 그 본질은 시간과 주파수라는 두 관점을 동시에 담아내는 표현을 만들어 냈다는 점에 있습니다. 핵심은 시간-주파수 분해능 트레이드오프를 이해하고 자신의 분석 목적에 맞게 윈도우 길이를 선택하는 것입니다. 음성 인식처럼 빠른 음소 변화를 추적해야 한다면 짧은 윈도우를, 음악의 음정을 정밀하게 구별해야 한다면 긴 윈도우를 선택해야 합니다. 이 선택 능력이 곧 실무 DSP 엔지니어의 역량입니다.

스펙트로그램은 단순히 신호를 분석하는 도구를 넘어, 최근 AI 분야에서 오디오 데이터를 처리하는 가장 핵심적인 관문입니다. 1차원의 시계열 음성 데이터를 2차원 이미지(스펙트로그램)로 변환하면, 영상 처리에서 압도적인 성능을 자랑하는 CNN(합성곱 신경망) 기반의 이미지 분류 모델(ResNet, YOLO 등)을 오디오 도메인에 그대로 적용할 수 있게 됩니다.

실제로 음성 인식, 음악 분류, 심지어 고장 진단 예측에 이르기까지 딥러닝 기반 오디오 분석 파이프라인의 90% 이상은 신호를 STFT로 변환하여 스펙트로그램 텐서를 만드는 전처리 과정에서 출발합니다. 윈도우 크기와 Overlap 비율을 어떻게 설정하느냐가 최종 AI 모델의 성능을 좌우하는 핵심 하이퍼파라미터가 되는 셈입니다.

 

다음 글 예고

오늘 배운 STFT는 신호를 짧게 잘라 분석하는 강력한 도구이지만, 분석 내내 고정된 크기의 윈도우를 사용해야 한다는 근본적인 한계가 있습니다. 저주파 신호는 천천히 진동하므로 긴 윈도우가 필요하고, 고주파 신호는 빠르게 진동하므로 짧은 윈도우가 필요한데, STFT는 이 둘을 동시에 만족시킬 수 없습니다.

이를 극복하기 위해, 주파수 대역에 따라 윈도우의 크기를 유연하게 고무줄처럼 늘렸다 줄였다 하며 다중 해상도로 신호를 쪼개보는 마법 같은 수학적 도구가 등장합니다. 다음 시간에는 신호 처리의 또 다른 혁명, [13. 웨이블릿 변환(Wavelet): CWT와 DWT 실습 및 다중 해상도 분석] 편을 통해 한계에 부딪힌 STFT를 넘어서는 방법을 알아보겠습니다.

 

📌 이전 연재 글 보기

반응형