Coqui XTTS-v2 오류 해결 후기 - speaker_wav 적용부터 Python API 최종 성공까지
1편에서는 Windows 로컬 환경에서 Coqui TTS를 설치하고, XTTS-v2를 실행하는 단계까지 정리했습니다.
이번 2편에서는 실제로 막혔던 문제들을 어떻게 해결했는지, 그리고 최종적으로 어떤 코드로 한국어 음성 합성에 성공했는지 정리해보겠습니다.
두 번째 문제
PyTorch 2.6 계열에서 XTTS 체크포인트 로딩이 깨지는 문제
transformers 버전을 맞춘 뒤 다시 XTTS-v2를 실행했더니, 이번에는 다른 오류가 발생했습니다.
로그를 보면 핵심은 아래 메시지였습니다.
pickle.UnpicklingError: Weights only load failed...
이 문제는 PyTorch 2.6에서 torch.load 기본 동작이 바뀐 것과 연결되어 있습니다.
XTTS 체크포인트 로딩 과정에서 이 변경 사항과 충돌한 것입니다.
실무적으로 보면 이 단계에서 선택지는 둘 중 하나입니다.
- 로딩 코드를 직접 수정해서 우회한다
- XTTS가 안정적으로 동작하는 버전으로 PyTorch를 내린다
저는 두 번째를 선택했습니다.
그래서 torch, torchvision, torchaudio를 모두 제거하고 아래 조합으로 다시 설치했습니다.
pip uninstall -y torch torchvision torchaudio
pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu124
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'NO GPU')"
정리하고 나니 적어도 체크포인트 로딩 단계는 넘길 수 있었습니다.
[이미지: pickle.UnpicklingError 가 발생한 화면]

[이미지: torch 2.5.1 재설치 화면]

세 번째 문제
XTTS-v2는 speaker_wav 없이 바로 쓰는 모델이 아니었다
PyTorch 버전을 맞춘 뒤 다시 실행했더니, 이번에는 모델 사용 방식 자체에 대한 오류가 나왔습니다.
이건 XTTS-v2의 특성상 당연한 요구사항입니다.
XTTS-v2는 멀티스피커 모델이라서 텍스트만 넣는 방식으로는 안 되고, speaker_wav 또는 speaker_idx 가 필요합니다.
실제로는 speaker_idx 보다는 참조 음성 파일을 넘기는 speaker_wav 방식이 가장 직관적이고 빠릅니다.
[이미지: speaker_idx 또는 speaker_wav 가 필요하다는 오류 메시지 화면]

ref.wav 준비 조건
이제부터는 레퍼런스 음성 파일이 중요해집니다.
XTTS-v2는 이 파일 품질에 따라 결과 차이가 꽤 큽니다.
이번 테스트에서는 아래 기준으로 정리했습니다.
- 원하는 화자의 목소리일 것
- 길이는 6초~15초 정도
- 한 사람만 말할 것
- 배경 소음이 거의 없을 것
- wav 형식 권장
- 파일명은 ref.wav
- 위치는 F:\coqui-xtts\ref.wav
폴더 구조는 아래처럼 정리했습니다.
F:\coqui-xtts
┣ .venv
┣ output.wav
┣ output_ko.wav
┗ ref.wav
[이미지: .venv, output.wav, output_ko.wav, ref.wav 파일이 함께 있는 폴더 화면]

speaker_wav를 붙여서 다시 실행
레퍼런스 음성 파일을 준비한 뒤에는 아래 명령으로 다시 실행했습니다.
tts --model_name "tts_models/multilingual/multi-dataset/xtts_v2" --text "안녕하세요. 코퀴 TTS 테스트입니다." --speaker_wav ".\ref.wav" --language_idx "ko" --out_path output_ko.wav
이번에는 실제로 output_ko.wav 파일이 생성됐습니다.
즉, 설치와 실행 자체는 성공한 것입니다.
그런데 문제는 결과 음성 품질이었습니다.
한국어 문장을 넣었는데, 앞부분과 뒷부분에 이상한 발화가 섞이고, 전체적으로 어딘가 어색한 느낌이 있었습니다.
즉, 이 단계는 “안 된다”가 아니라 “그대로는 못 쓴다” 에 가까웠습니다.
CLI만으로는 한계가 있었다
Python API로 내려가서 직접 제어하기로 했다
여기서 판단을 바꿨습니다.
CLI는 빠른 테스트에는 좋지만, 출력 품질을 세밀하게 조절하기는 어렵습니다.
특히 XTTS-v2처럼 오토리그레시브 계열 모델은 아래 같은 요소 영향을 많이 받습니다.
- temperature
- repetition_penalty
- top_k
- top_p
- 텍스트 전처리
- 문장 분할 여부
그래서 CLI만 계속 붙잡지 않고 Python 코드로 넘어갔습니다.
처음에는 아래처럼 TTS.api 를 이용한 간단한 코드부터 테스트했습니다.
import torch
from TTS.api import TTS
device = "cuda" if torch.cuda.is_available() else "cpu"
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
tts.tts_to_file(
text="안녕하세요. 한국어 음성 합성 테스트입니다.",
speaker_wav="ref.wav",
language="ko",
file_path="output_ko_fix.wav"
)
이 방식으로도 음성 파일은 생성됩니다.
하지만 앞뒤 아티팩트를 줄이거나 출력 안정성을 높이기에는 제어 범위가 부족했습니다.

최종 해결 방법
저수준 XTTS 모델을 직접 호출했다
이후에는 TTS.api.TTS 대신 XTTS 모델을 직접 불러오는 방식으로 코드를 다시 작성했습니다.
핵심은 아래 세 가지였습니다.
첫째, 텍스트 전처리입니다.
마침표(.)를 느낌표(!)로 바꿔서 불필요한 아티팩트를 줄였습니다.
둘째, temperature 값을 낮췄습니다.
출력이 덜 흔들리고 더 안정적으로 들리게 만들기 위해서입니다.
셋째, repetition_penalty 값을 높였습니다.
앞뒤로 붙는 이상한 반복이나 잡음을 줄이기 위한 목적입니다.
아래는 실제로 최종 성공한 코드입니다.
import torch
import torchaudio
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
import os
import glob
REF_WAV = "ref.wav"
OUTPUT_WAV = "output_ko_fix.wav"
TEXT = "안녕하세요. 한국어 음성 합성 테스트입니다."
LANGUAGE = "ko"
MODEL_DIR = None
if MODEL_DIR is None:
candidates = glob.glob(
os.path.expanduser(
r"~\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2"
)
)
if not candidates:
raise FileNotFoundError(
"XTTS-v2 모델 폴더를 찾을 수 없습니다.\n"
"MODEL_DIR 변수에 직접 경로를 입력해주세요.\n"
r"예: MODEL_DIR = r'C:\Users\YourName\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2'"
)
MODEL_DIR = candidates[0]
print(f"[INFO] 모델 경로: {MODEL_DIR}")
def preprocess_text(text: str) -> str:
text = text.strip()
text = text.replace(". ", "! ")
text = text.replace(".", "!")
return text
processed_text = preprocess_text(TEXT)
print(f"[INFO] 원본 텍스트 : {TEXT}")
print(f"[INFO] 처리 후 텍스트: {processed_text}")
config = XttsConfig()
config.load_json(os.path.join(MODEL_DIR, "config.json"))
model = Xtts.init_from_config(config)
model.load_checkpoint(config, checkpoint_dir=MODEL_DIR, eval=True)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"[INFO] 사용 디바이스: {device}")
model.to(device)
print(f"[INFO] 레퍼런스 WAV: {REF_WAV}")
gpt_cond_latent, speaker_embedding = model.get_conditioning_latents(
audio_path=[REF_WAV]
)
print("[INFO] 추론 시작...")
out = model.inference(
processed_text,
LANGUAGE,
gpt_cond_latent,
speaker_embedding,
temperature=0.65,
repetition_penalty=10.0,
top_k=50,
top_p=0.85,
enable_text_splitting=True,
)
wav_tensor = torch.tensor(out["wav"]).unsqueeze(0)
torchaudio.save(OUTPUT_WAV, wav_tensor, 24000)
print(f"[INFO] 저장 완료: {OUTPUT_WAV}")
이 방식으로 돌렸을 때는 이전에 들리던 앞뒤 아티팩트가 확실히 줄었습니다.
완벽하다고 말하기는 어렵지만, 적어도 “바로 못 쓰는 상태”에서 “적용 검토가 가능한 상태” 까지는 올라왔습니다.
[이미지: test_xtts_ko_claude.py 실행 화면]

처리 시간까지 같이 확인해봤다
설치만 되는 것으로 끝내면 실무에서는 의미가 약합니다.
실제로는 아래 항목을 같이 봐야 합니다.
- 모델 로딩 시간
- 화자 임베딩 추출 시간
- 순수 추론 시간
- 총 처리 시간
- 실시간 배율(RTF)
그래서 타이머를 붙인 로그 버전도 따로 만들었습니다.
import torch
import torchaudio
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import Xtts
import os
import glob
import time
class Timer:
def __init__(self, label: str):
self.label = label
def __enter__(self):
self.start = time.perf_counter()
print(f"[TIME] ▶ {self.label} 시작...")
return self
def __exit__(self, *args):
self.elapsed = time.perf_counter() - self.start
print(f"[TIME] ■ {self.label} 완료 → {self.elapsed:.3f}s")
REF_WAV = "ref.wav"
OUTPUT_WAV = "output_ko_fix.wav"
TEXT = "안녕하세요. 한국어 음성 합성 테스트입니다."
LANGUAGE = "ko"
MODEL_DIR = None
total_start = time.perf_counter()
if MODEL_DIR is None:
candidates = glob.glob(
os.path.expanduser(
r"~\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2"
)
)
if not candidates:
raise FileNotFoundError(
"XTTS-v2 모델 폴더를 찾을 수 없습니다.\n"
"MODEL_DIR 변수에 직접 경로를 입력해주세요.\n"
r"예: MODEL_DIR = r'C:\Users\YourName\AppData\Local\tts\tts_models--multilingual--multi-dataset--xtts_v2'"
)
MODEL_DIR = candidates[0]
print(f"[INFO] 모델 경로 : {MODEL_DIR}")
def preprocess_text(text: str) -> str:
text = text.strip()
text = text.replace(". ", "! ")
text = text.replace(".", "!")
return text
processed_text = preprocess_text(TEXT)
print(f"[INFO] 원본 텍스트 : {TEXT}")
print(f"[INFO] 처리 후 텍스트: {processed_text}")
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"[INFO] 사용 디바이스: {device}")
print()
with Timer("모델 config 로드") as t_cfg:
config = XttsConfig()
config.load_json(os.path.join(MODEL_DIR, "config.json"))
with Timer("모델 초기화 및 체크포인트 로드") as t_ckpt:
model = Xtts.init_from_config(config)
model.load_checkpoint(config, checkpoint_dir=MODEL_DIR, eval=True)
model.to(device)
print(f"\n[INFO] 레퍼런스 WAV : {REF_WAV}")
with Timer("화자 임베딩 추출") as t_emb:
gpt_cond_latent, speaker_embedding = model.get_conditioning_latents(
audio_path=[REF_WAV]
)
print()
with Timer("음성 추론 (inference)") as t_inf:
out = model.inference(
processed_text,
LANGUAGE,
gpt_cond_latent,
speaker_embedding,
temperature=0.65,
repetition_penalty=10.0,
top_k=50,
top_p=0.85,
enable_text_splitting=True,
)
with Timer("WAV 파일 저장") as t_save:
wav_tensor = torch.tensor(out["wav"]).unsqueeze(0)
torchaudio.save(OUTPUT_WAV, wav_tensor, 24000)
total_elapsed = time.perf_counter() - total_start
audio_duration = wav_tensor.shape[-1] / 24000
print()
print("=" * 50)
print("[TIME] 처리 시간 요약")
print("=" * 50)
print(f" config 로드 : {t_cfg.elapsed:>8.3f}s")
print(f" 모델 초기화/체크포인트 : {t_ckpt.elapsed:>8.3f}s")
print(f" 화자 임베딩 추출 : {t_emb.elapsed:>8.3f}s")
print(f" 음성 추론 : {t_inf.elapsed:>8.3f}s")
print(f" 파일 저장 : {t_save.elapsed:>8.3f}s")
print("-" * 50)
print(f" 총 처리 시간 : {total_elapsed:>8.3f}s")
print("-" * 50)
print(f" 생성된 오디오 길이 : {audio_duration:>8.3f}s")
print(f" 실시간 배율 (RTF) : {t_inf.elapsed / audio_duration:>8.3f}x (1.0 미만이면 실시간 이상)")
print("=" * 50)
print(f"[INFO] 저장 완료: {OUTPUT_WAV}")
[이미지: 타이머 로그가 포함된 실행 화면]

실제 결과
로그 기준으로 보면 대략 아래 정도였습니다.
- 모델 초기화 및 체크포인트 로드: 약 8~11초
- 화자 임베딩 추출: 0.3~0.7초
- 순수 음성 추론: 1~3초대
- 총 처리 시간: 약 12초 전후
- 실시간 배율(RTF): 1.0 미만
여기서 중요한 건 순수 추론 시간만 보면 실시간 처리 가능성이 있다는 점입니다.
총 처리 시간은 모델 로딩 비용이 포함된 값이고, 실제 서비스에서는 모델을 메모리에 상주시켜 재사용하기 때문에 매번 같은 비용을 낼 필요가 없습니다.
즉, 실무 관점에서는 이렇게 해석할 수 있습니다.
- 단발성 CLI 실행은 느려 보일 수 있다
- 상주 프로세스 구조로 바꾸면 충분히 실용 가능성이 있다
최종 결론
이번 테스트 기준으로는 MeloTTS보다 XTTS-v2가 훨씬 실용적이었습니다.
MeloTTS는 재학습 기반 접근이라 시간이 많이 들고, 데이터와 하이퍼파라미터에 따라 결과 편차도 컸습니다.
반면 XTTS-v2는 설치 과정에서 호환성 문제만 정리하면, 레퍼런스 wav 하나로 훨씬 빠르게 결과를 확인할 수 있었습니다.
특히 아래 조합이 핵심이었습니다.
- Python 3.11
- venv 가상환경
- torch 2.5.1 / torchvision 0.20.1 / torchaudio 2.5.1
- transformers==4.46.2
- speaker_wav 필수
- Python API 직접 호출
- temperature=0.65
- repetition_penalty=10.0
- 마침표를 느낌표로 바꾸는 텍스트 전처리
이 조합으로 정리한 뒤에는 한국어 음성 합성이 실제로 돌아갔고, 앞뒤 아티팩트도 어느 정도 줄일 수 있었습니다.
아직 완성형이라고 말하기는 어렵지만, 적어도 “Windows 로컬 환경에서 설치가 되고, 한국어 테스트가 되고, 파라미터 조정으로 품질 개선이 가능한 상태” 까지는 확인한 셈입니다.
참고 링크
- XTTS-v2 Hugging Face: https://huggingface.co/coqui/XTTS-v2
- Coqui 공식 문서: https://docs.coqui.ai/en/latest/
- Coqui TTS GitHub 설치 문서: https://github.com/coqui-ai/TTS/tree/dev#installation
- Python 다운로드: https://www.python.org/downloads/windows/
- PyTorch 공식 설치 페이지: https://pytorch.org/get-started/locally/
- PyTorch 이전 버전 안내: https://pytorch.org/get-started/previous-versions/
이 글은 작성 과정에서 AI의 도움을 받아 정리했습니다.
'Programming > AI' 카테고리의 다른 글
| Qwen3-TTS Web UI가 바로 안 뜬 이유 (0) | 2026.04.02 |
|---|---|
| Windows에서 Qwen3-TTS 로컬 설치 시작 (0) | 2026.04.02 |
| Coqui XTTS-v2 설치 후기 - Windows 로컬 환경에서 한국어 TTS 테스트 시작하기 (0) | 2026.03.31 |
| Unity에서 OpenAI API로 AI 도슨트 만들기 (0) | 2026.03.26 |
| Unity에서 OpenAI API로 STT 붙이기 (0) | 2026.03.26 |