wnr-training / prepare_data.py
haejin1320's picture
Add WNR training scripts
2e81846 verified
Raw
History Blame Contribute Delete
10.3 kB
"""ν’μ†ŒμŒ ν•™μŠ΅ 데이터 μ€€λΉ„ 슀크립트
κΉ¨λ—ν•œ μŒμ„±κ³Ό ν’μ†ŒμŒ νŒŒμΌλ‘œλΆ€ν„° HDF5 ν•™μŠ΅ 데이터λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
이 μŠ€ν¬λ¦½νŠΈλŠ” λ‘œμ»¬μ—μ„œ μ‹€ν–‰ν•˜μ—¬ 데이터λ₯Ό μ€€λΉ„ν•œ ν›„ HF Hub에 μ—…λ‘œλ“œν•©λ‹ˆλ‹€.
μ‚¬μš©λ²•:
python prepare_data.py --speech-dir ./data/speech \
--noise-dir ./data/noise \
--output-dir ./data/hdf5 \
[--rir-dir ./data/rir] \
[--sr 48000]
"""
import argparse
import json
import os
from pathlib import Path
import numpy as np
import soundfile as sf
def find_audio_files(directory, extensions=(".wav", ".flac", ".ogg", ".mp3")):
"""λ””λ ‰ν† λ¦¬μ—μ„œ μ˜€λ””μ˜€ 파일 경둜 μˆ˜μ§‘"""
audio_files = []
for ext in extensions:
audio_files.extend(Path(directory).rglob(f"*{ext}"))
return sorted(audio_files)
def split_stereo_to_mono(audio_files, output_dir):
"""Stereo μ˜€λ””μ˜€ 파일의 L/R 채널을 각각 mono WAV둜 뢄리.
DeepFilterNet3은 mono λͺ¨λΈμ΄λ―€λ‘œ, stereo νŒŒμΌμ„ μ±„λ„λ³„λ‘œ λΆ„λ¦¬ν•˜μ—¬
ν•™μŠ΅ 데이터λ₯Ό 2배둜 ν™•λ³΄ν•˜λŠ” 효과λ₯Ό μ–»μŒ.
이미 mono인 νŒŒμΌμ€ κ·ΈλŒ€λ‘œ 볡사.
Returns:
λΆ„λ¦¬λœ mono 파일 경둜 리슀트
"""
mono_dir = Path(output_dir) / "mono_split"
mono_dir.mkdir(parents=True, exist_ok=True)
mono_files = []
for f in audio_files:
info = sf.info(str(f))
stem = f.stem
if info.channels >= 2:
data, sr = sf.read(str(f), always_2d=True)
# Left channel
left_path = mono_dir / f"{stem}_L.wav"
sf.write(str(left_path), data[:, 0], sr, subtype="PCM_16")
mono_files.append(left_path)
# Right channel
right_path = mono_dir / f"{stem}_R.wav"
sf.write(str(right_path), data[:, 1], sr, subtype="PCM_16")
mono_files.append(right_path)
else:
# Mono νŒŒμΌμ€ κ·ΈλŒ€λ‘œ 볡사
mono_path = mono_dir / f"{stem}.wav"
data, sr = sf.read(str(f))
sf.write(str(mono_path), data, sr, subtype="PCM_16")
mono_files.append(mono_path)
print(f" Stereo β†’ Mono 뢄리 μ™„λ£Œ: {len(audio_files)}개 β†’ {len(mono_files)}개 파일")
return sorted(mono_files)
def validate_sample_rate(files, target_sr=48000, max_check=50):
"""μƒ˜ν”Œλ§ 레이트 검증 (DeepFilterNet3은 48kHz ν•„μˆ˜)"""
issues = []
for f in files[:max_check]:
info = sf.info(str(f))
if info.samplerate != target_sr:
issues.append((str(f), info.samplerate))
return issues
def create_file_list(audio_files, output_path):
"""μ˜€λ””μ˜€ 파일 경둜 λͺ©λ‘ 생성 (HDF5 λ³€ν™˜μ— μ‚¬μš©)"""
with open(output_path, "w") as f:
for audio_file in audio_files:
f.write(str(audio_file.resolve()) + "\n")
print(f" 파일 λͺ©λ‘ 생성: {output_path} ({len(audio_files)}개 파일)")
return output_path
def create_dataset_config(output_dir, has_rir=False):
"""dataset.cfg 생성 (DeepFilterNet ν•™μŠ΅μ— ν•„μš”ν•œ JSON μ„€μ •)"""
config = {
"train": [
[os.path.join(output_dir, "train_speech.hdf5"), 1.0],
[os.path.join(output_dir, "train_noise.hdf5"), 1.0],
],
"valid": [
[os.path.join(output_dir, "valid_speech.hdf5"), 1.0],
[os.path.join(output_dir, "valid_noise.hdf5"), 1.0],
],
"test": [
[os.path.join(output_dir, "test_speech.hdf5"), 1.0],
[os.path.join(output_dir, "test_noise.hdf5"), 1.0],
],
}
if has_rir:
config["train"].append([os.path.join(output_dir, "train_rir.hdf5"), 1.0])
config["valid"].append([os.path.join(output_dir, "valid_rir.hdf5"), 1.0])
config_path = os.path.join(output_dir, "dataset.cfg")
with open(config_path, "w") as f:
json.dump(config, f, indent=2)
print(f" dataset.cfg 생성: {config_path}")
return config_path
def split_files(files, train_ratio=0.8, valid_ratio=0.1):
"""νŒŒμΌμ„ train/valid/test둜 λΆ„ν• """
n = len(files)
n_train = int(n * train_ratio)
n_valid = int(n * valid_ratio)
np.random.seed(42)
indices = np.random.permutation(n)
train_files = [files[i] for i in indices[:n_train]]
valid_files = [files[i] for i in indices[n_train:n_train + n_valid]]
test_files = [files[i] for i in indices[n_train + n_valid:]]
return train_files, valid_files, test_files
def print_summary(speech_files, noise_files, rir_files=None):
"""데이터 μš”μ•½ 좜λ ₯"""
print("\n" + "=" * 50)
print("데이터 μš”μ•½")
print("=" * 50)
total_speech_dur = 0
for f in speech_files[:100]:
info = sf.info(str(f))
total_speech_dur += info.duration
est_total = total_speech_dur / min(len(speech_files), 100) * len(speech_files)
print(f" μŒμ„± 파일: {len(speech_files)}개 (μ˜ˆμƒ 총 {est_total/3600:.1f}μ‹œκ°„)")
total_noise_dur = 0
for f in noise_files[:100]:
info = sf.info(str(f))
total_noise_dur += info.duration
est_total_n = total_noise_dur / min(len(noise_files), 100) * len(noise_files)
print(f" λ…Έμ΄μ¦ˆ 파일: {len(noise_files)}개 (μ˜ˆμƒ 총 {est_total_n/3600:.1f}μ‹œκ°„)")
if rir_files:
print(f" RIR 파일: {len(rir_files)}개")
print()
def main():
parser = argparse.ArgumentParser(description="DeepFilterNet3 νŒŒμΈνŠœλ‹μš© 데이터 μ€€λΉ„")
parser.add_argument("--speech-dir", required=True, help="κΉ¨λ—ν•œ μŒμ„± 디렉토리")
parser.add_argument("--noise-dir", required=True, help="ν’μ†ŒμŒ 디렉토리")
parser.add_argument("--rir-dir", default=None, help="RIR 디렉토리 (선택)")
parser.add_argument("--output-dir", required=True, help="좜λ ₯ 디렉토리 (HDF5 + μ„€μ •νŒŒμΌ)")
parser.add_argument("--sr", type=int, default=48000, help="μƒ˜ν”Œλ§ 레이트 (κΈ°λ³Έ: 48000)")
args = parser.parse_args()
os.makedirs(args.output_dir, exist_ok=True)
# 1. μ˜€λ””μ˜€ 파일 μˆ˜μ§‘
print("μ˜€λ””μ˜€ 파일 μˆ˜μ§‘ 쀑...")
speech_files = find_audio_files(args.speech_dir)
noise_files = find_audio_files(args.noise_dir)
rir_files = find_audio_files(args.rir_dir) if args.rir_dir else None
if not speech_files:
print(f"였λ₯˜: {args.speech_dir}μ—μ„œ μŒμ„± νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
return
if not noise_files:
print(f"였λ₯˜: {args.noise_dir}μ—μ„œ λ…Έμ΄μ¦ˆ νŒŒμΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
return
# 2. μƒ˜ν”Œλ§ 레이트 검증
print(f"\nμƒ˜ν”Œλ§ 레이트 검증 ({args.sr}Hz)...")
sr_issues = validate_sample_rate(speech_files, args.sr)
sr_issues += validate_sample_rate(noise_files, args.sr)
if sr_issues:
print(" κ²½κ³ : λ‹€μŒ νŒŒμΌλ“€μ˜ μƒ˜ν”Œλ§ λ ˆμ΄νŠΈκ°€ λ§žμ§€ μ•ŠμŠ΅λ‹ˆλ‹€:")
for path, sr in sr_issues[:10]:
print(f" {path}: {sr}Hz (ν•„μš”: {args.sr}Hz)")
print(" λ¦¬μƒ˜ν”Œλ§μ΄ ν•„μš”ν•©λ‹ˆλ‹€. librosaλ‚˜ ffmpeg둜 48kHz둜 λ³€ν™˜ν•˜μ„Έμš”.")
print(" 예: ffmpeg -i input.wav -ar 48000 output.wav")
return
print_summary(speech_files, noise_files, rir_files)
# 2.5. Stereo β†’ Mono 채널 뢄리 (DeepFilterNet3은 mono λͺ¨λΈ)
print("\nStereo β†’ Mono 채널 뢄리 쀑...")
print(" Speech 파일:")
speech_files = split_stereo_to_mono(speech_files, args.output_dir)
print(" Noise 파일:")
noise_files = split_stereo_to_mono(noise_files, args.output_dir)
if rir_files:
print(" RIR 파일:")
rir_files = split_stereo_to_mono(rir_files, args.output_dir)
# 3. Train/Valid/Test λΆ„ν• 
print("데이터 λΆ„ν•  쀑 (80/10/10)...")
train_speech, valid_speech, test_speech = split_files(speech_files)
train_noise, valid_noise, test_noise = split_files(noise_files)
print(f" Speech - train: {len(train_speech)}, valid: {len(valid_speech)}, test: {len(test_speech)}")
print(f" Noise - train: {len(train_noise)}, valid: {len(valid_noise)}, test: {len(test_noise)}")
# 4. 파일 λͺ©λ‘ 생성
print("\n파일 λͺ©λ‘ 생성 쀑...")
lists_dir = os.path.join(args.output_dir, "lists")
os.makedirs(lists_dir, exist_ok=True)
for split, files in [("train", train_speech), ("valid", valid_speech), ("test", test_speech)]:
create_file_list(files, os.path.join(lists_dir, f"{split}_speech.txt"))
for split, files in [("train", train_noise), ("valid", valid_noise), ("test", test_noise)]:
create_file_list(files, os.path.join(lists_dir, f"{split}_noise.txt"))
if rir_files:
train_rir, valid_rir, _ = split_files(rir_files)
create_file_list(train_rir, os.path.join(lists_dir, "train_rir.txt"))
create_file_list(valid_rir, os.path.join(lists_dir, "valid_rir.txt"))
# 5. dataset.cfg 생성
print("\ndataset.cfg 생성 쀑...")
create_dataset_config(args.output_dir, has_rir=rir_files is not None)
# 6. HDF5 λ³€ν™˜ μ•ˆλ‚΄
print("\n" + "=" * 50)
print("λ‹€μŒ 단계: HDF5 λ³€ν™˜")
print("=" * 50)
print()
print("DeepFilterNet의 prepare_data.py 슀크립트둜 HDF5λ₯Ό μƒμ„±ν•˜μ„Έμš”:")
print("(Docker μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€ λ˜λŠ” Linux ν™˜κ²½μ—μ„œ μ‹€ν–‰)")
print()
dfn_script = "python /opt/DeepFilterNet/DeepFilterNet/df/scripts/prepare_data.py"
for split in ["train", "valid", "test"]:
for dtype in ["speech", "noise"]:
txt_path = os.path.join(lists_dir, f"{split}_{dtype}.txt")
hdf5_path = os.path.join(args.output_dir, f"{split}_{dtype}.hdf5")
print(f" {dfn_script} --sr {args.sr} {dtype} {txt_path} {hdf5_path}")
print()
if rir_files:
for split in ["train", "valid"]:
txt_path = os.path.join(lists_dir, f"{split}_rir.txt")
hdf5_path = os.path.join(args.output_dir, f"{split}_rir.hdf5")
print(f" {dfn_script} --sr {args.sr} rir {txt_path} {hdf5_path}")
print()
print("HDF5 생성 ν›„ HF Hub에 μ—…λ‘œλ“œ:")
print(f" huggingface-cli upload YOUR_USERNAME/wind-noise-data {args.output_dir} --repo-type dataset")
print()
print("μ™„λ£Œ!")
if __name__ == "__main__":
main()