Mel-Band RoFormer Karaoke 모델 분석 — 리드 보컬만 골라내는 913MB의 비밀
RoPE + 멜 스케일 주파수 분해 + 계층적 시간·주파수 Transformer — 코드와 설정까지
모델 파일 정보
| 항목 | 값 |
|---|---|
| 파일명 | mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt |
| 크기 | 913 MB |
| 형식 | PyTorch pickle checkpoint (OrderedDict + FloatStorage) |
| 설정 파일 | config_mel_band_roformer_karaoke.yaml (1.72 kB) |
| 호스팅 | HuggingFace |
| SHA256 | 1de20d459332fe8869aeb01327a31df0032262706e1365114e852dc271779813 |
audio-separator 패키지가 첫 실행 시 UVR 모델 저장소에서 자동 다운로드한다.
RoFormer란 — Rotary Position Embedding
일반 Transformer는 위치 정보를 absolute position encoding이나 relative position encoding으로 처리한다. RoFormer는 RoPE(Rotary Position Embedding)를 쓴다.
RoPE는 Query와 Key 벡터를 위치에 따라 회전시킨다. 거리가 먼 토큰 간의 어텐션이 자연스럽게 감소한다. 학습된 위치 임베딩 없이도 가변 길이 시퀀스를 처리할 수 있다.
실제 코드 (mel_band_roformer.py):
from rotary_embedding_torch import RotaryEmbedding
time_rotary_embed = RotaryEmbedding(dim=64) # dim_head=64
freq_rotary_embed = RotaryEmbedding(dim=64)
# Attention.forward() 안에서:
q = self.rotary_embed.rotate_queries_or_keys(q)
k = self.rotary_embed.rotate_queries_or_keys(k)
시간 축과 주파수 축에 각각 별도의 RoPE를 쓴다.
Mel-Band vs Band-Split — 핵심 차이
기존 BS-RoFormer(Band-Split RoFormer)는 주파수를 62개의 비중복 밴드로 나눈다. Mel-Band RoFormer는 멜 스케일로 60개의 중복 밴드를 만든다.
| BS-RoFormer | Mel-Band RoFormer | |
|---|---|---|
| 밴드 분할 | 휴리스틱, 비중복, 62개 | 멜 스케일, 50% 중복, 60개 |
| 지각적 가중치 | 없음 | 있음 (인간 청각에 맞춤) |
| 파라미터 (L=6) | 72.2M | 84.2M |
멜 스케일은 인간이 저주파보다 고주파의 차이를 덜 느끼는 특성을 반영한다. 저주파 구간은 좁게, 고주파 구간은 넓게 나눈다. 50% 중복은 밴드 경계에서 발생하는 아티팩트를 줄여준다.
실제 코드:
# librosa로 멜 필터 뱅크 생성
mel_filter_bank_numpy = filters.mel(sr=44100, n_fft=2048, n_mels=60)
mel_filter_bank = torch.from_numpy(mel_filter_bank_numpy)
# 이진화: 각 주파수 빈이 어느 밴드에 속하는지
freqs_per_band = mel_filter_bank > 0
# 인덱스 매핑: 어떤 주파수가 어느 밴드로 가는지
repeated_freq_indices = repeat(torch.arange(freqs), 'f -> b f', b=num_bands)
freq_indices = repeated_freq_indices[freqs_per_band]
전체 추론 파이프라인
MelBandRoformer.forward()의 단계:
1단계 — STFT
원본 스테레오 오디오를 복소 스펙트로그램으로 변환.
입력: [batch, 2, samples] (스테레오 파형)
STFT: n_fft=2048, hop_length=441, win_length=2048
출력: [batch, 1025, time_frames, 2] (주파수 빈 × 시간 × 실수/허수)
441 hop은 44,100Hz에서 100fps. 1025개 주파수 빈은 0~22,050Hz를 커버한다.
2단계 — 멜 밴드 프로젝션
1025개 주파수 빈을 60개 멜 밴드로 분해한다. 각 밴드에 속하는 주파수 빈을 모아서 BandSplit 모듈의 밴드별 선형 레이어로 임베딩한다.
1025 주파수 빈 → 60개 멜 밴드로 그룹핑
각 밴드 → 선형 레이어 → [batch, time, 60, dim=384]
3단계 — 계층적 Transformer (6레이어)
각 레이어에서 시간 Transformer와 주파수 Transformer를 교대로 실행한다:
a) 시간 Transformer — 각 밴드 내에서 시간 축을 처리
[batch, 60, time, 384] → reshape → [batch×60, time, 384]
→ RoPE Attention + Gated Output + FFD
b) 주파수 Transformer — 각 시간 프레임 내에서 밴드 축을 처리
[batch, time, 60, 384] → reshape → [batch×time, 60, 384]
→ RoPE Attention + Gated Output + FFD
시간 Transformer가 "이 밴드에서 시간적으로 보컬이 어디 있는지"를 잡고, 주파수 Transformer가 "이 시점에서 밴드 간 관계(하모닉 구조)"를 잡는다.
4단계 — 마스크 추정
각 밴드에서 MLP(depth=2)로 복소 마스크를 생성한다:
384 → 1536 → freq_bins × 2 (GLU 활성화)
→ 중복 밴드 기여를 평균
→ 원본 STFT에 마스크를 곱함
5단계 — ISTFT
마스크된 스펙트로그램을 역 STFT로 시간 도메인 파형으로 복원.
카라오케 전용 동작의 비밀
학습 설정(YAML)이 핵심이다:
training:
instruments:
- karaoke # = 반주 + 백킹 보컬
- other # = 리드 보컬
target_instrument: karaoke
일반적인 음원 분리 모델의 학습 데이터:
vocals= 리드 + 백킹 전체instrumental= 보컬 없음
이 모델의 학습 데이터:
karaoke= 반주 + 백킹 보컬 + 코러스other= 리드 보컬만
모델이 출력하는 건 karaoke 스템이다. 잔차(원본 - karaoke)가 리드 보컬이 된다. 백킹 보컬은 karaoke 스템에 남아있으므로 반주가 자연스럽다.
SDR 10.1956의 의미
SDR(Signal-to-Distortion Ratio)은 분리 품질을 데시벨로 측정한다. 높을수록 좋다.
| 모델 | Vocal SDR | 비고 |
|---|---|---|
| Spleeter (2020) | ~5.91 dB | 오래된 베이스라인 |
| HTDemucs (2022) | ~9.20 dB | 4-스템 범용 분리 |
| 이 모델 | 10.20 dB | 리드 보컬만 (더 어려운 태스크) |
| Mel-RoFormer L=6 (논문) | 11.21 dB | 전체 보컬 (리드+백킹) |
| BS-RoFormer-Viperx | 12.98 dB | 전체 보컬, 최고 성능 |
리드 보컬만 분리하는 건 전체 보컬 분리보다 어렵다. 백킹 보컬과 리드 보컬의 주파수 대역이 겹치기 때문이다. 10.20 dB는 이 태스크에서 상당히 높은 수치다.
직접 테스트하는 방법
1. audio-separator로 간단 테스트:
pip install audio-separator[gpu] # GPU 가속
# 또는
pip install audio-separator[cpu] # CPU만
audio-separator song.mp3 \
--model_filename mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt
첫 실행 시 913MB 모델을 자동 다운로드한다. 출력:
song_(Vocals)_mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.wavsong_(Instrumental)_mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.wav
2. Python에서 직접:
from audio_separator.separator import Separator
sep = Separator(
model_file_dir="/tmp/models",
output_dir="/tmp/output"
)
sep.load_model(
"mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt"
)
output_files = sep.separate("song.mp3")
print(output_files)
# → ['song_(Vocals)_...wav', 'song_(Instrumental)_...wav']
3. 코드 레벨에서 분석하려면:
핵심 파일 3개:
audio_separator/separator/separator.py— 엔트리 포인트, 모델 로딩audio_separator/separator/architectures/mdxc_separator.py— 추론 오케스트레이션 (demix()메서드)audio_separator/separator/uvr_lib_v5/roformer/mel_band_roformer.py— 모델 아키텍처 구현 (471줄)
mel_band_roformer.py의 MelBandRoformer 클래스가 전체 아키텍처다. forward() 메서드가 위에 설명한 5단계 파이프라인을 구현한다.
모델 설정값 전체
audio:
chunk_size: 485100 # 441 × (1101-1) ≈ 11초
dim_f: 1024
dim_t: 1101
hop_length: 441 # 44100/441 = 100fps
n_fft: 2048
num_overlap: 4 # 추론 시 오버랩 수
sample_rate: 44100
stem_name: karaoke
num_stems: 1 # 출력 스템 1개 (나머지는 잔차)
model:
dim: 384 # 임베딩 차원
depth: 6 # Transformer 레이어 수
stereo: true
time_transformer_depth: 1 # 레이어당 시간 Transformer
freq_transformer_depth: 1 # 레이어당 주파수 Transformer
num_heads: 8
dim_head: 64
attn_dropout: 0.0
ff_dropout: 0.0
num_bands: 60 # 멜 밴드 수
mask_estimator_depth: 2
핵심 개념
STFT — 스테레오 오디오를 복소 스펙트로그램으로 변환 (n_fft=2048, hop=441)
멜 밴드 프로젝션 — 1025개 주파수 빈을 60개 중복 멜 밴드로 분해 + 선형 임베딩
계층적 Transformer ×6 — 시간 축 Transformer → 주파수 축 Transformer 교대 실행 (RoPE)
마스크 추정 — 밴드별 MLP로 복소 마스크 생성 → 원본 STFT에 곱셈
ISTFT — 마스크된 스펙트로그램을 시간 도메인 파형으로 복원
잔차 계산 — 원본 - karaoke(모델 출력) = 리드 보컬