Views
No views yet
Qwen/Qwen3-TTS-12Hz-1.7B-Base)과 함께
로드해야 하며, 화자 등록(speaker config/embedding)까지 필요합니다 — 아래 inference.py가
이 과정을 전부 처리합니다.inference.py 그대로 사용pip install qwen-tts peft soundfile huggingface_hub torchinference.py를 내려받아 같은 폴더에 두고:1from inference import generate_narration_audio
2
3generate_narration_audio(
4 narration_text="Careful, this one's spicy! Tteokbokki is chewy rice cakes simmered in a thick, spicy chili sauce.",
5 style="CAUTION", # 또는 "GUIDE"
6 output_wav="output.wav",
7)narration_text는 화면에 표시할 원문 그대로 넘기면 됩니다 — 음식명 리스펠링과 스타일별 톤
지시(instruct)는 내부에서 자동으로 처리됩니다.inference.py 없이 직접 통합하려면, 아래 3가지를 반드시 순서대로 적용해야 합니다.
LoRA 가중치만 병합하고 이 단계를 생략하면 화자 목소리가 등록되지 않아 오류가 나거나
의도한 화자로 생성되지 않습니다.1import json
2import torch
3from huggingface_hub import snapshot_download
4from peft import PeftModel
5from peft.tuners.lora import LoraLayer
6from safetensors.torch import load_file
7from qwen_tts import Qwen3TTSModel
8
9BASE_MODEL = "Qwen/Qwen3-TTS-12Hz-1.7B-Base"
10ADAPTER_REPO = "k-bite-team/k-menu-qwen3-tts-lora"
11SPEAKER_NAME = "speaker"
12LORA_SCALE = 0.3 # 1.0은 거의 항상 깨진 결과가 나옵니다 (0.2~0.5 권장 범위)
13
14adapter_dir = snapshot_download(repo_id=ADAPTER_REPO)
15
16tts = Qwen3TTSModel.from_pretrained(
17 BASE_MODEL, device_map="cuda:0", torch_dtype=torch.bfloat16, attn_implementation="sdpa",
18)
19
20# 1) LoRA를 scale 적용 후 병합
21peft_model = PeftModel.from_pretrained(tts.model, adapter_dir)
22for module in peft_model.modules():
23 if isinstance(module, LoraLayer):
24 module.set_scale(peft_model.active_adapter, LORA_SCALE)
25tts.model = peft_model.merge_and_unload()
26
27# 2) 화자 config 등록 (adapter_dir의 config.json에 저장된 talker_config를 병합)
28core = tts.model
29with open(f"{adapter_dir}/config.json", encoding="utf-8") as f:
30 talker_config = json.load(f)["talker_config"]
31core.config.talker_config.spk_id.update(talker_config.get("spk_id", {}))
32core.config.talker_config.spk_is_dialect.update(talker_config.get("spk_is_dialect", {}))
33core.config.tts_model_type = core.tts_model_type = "custom_voice"
34
35# 3) 화자 임베딩 주입 (adapter_dir의 speaker_embedding.safetensors)
36payload = load_file(f"{adapter_dir}/speaker_embedding.safetensors")
37spk_id = core.config.talker_config.spk_id[SPEAKER_NAME]
38w = core.talker.model.codec_embedding.weight
39w.data[spk_id] = payload["target_speaker_embedding"].to(device=w.device, dtype=w.dtype)
40core.eval()
41
42wavs, sr = tts.generate_custom_voice(
43 text="Careful, this one's spicy! Duck-bok-kee is chewy rice cakes simmered in a thick, spicy chili sauce.",
44 speaker=SPEAKER_NAME,
45 language="auto",
46 instruct="Say it in an urgent, warning tone, as if cautioning someone.",
47)위 텍스트에 "Duck-bok-kee"처럼 이미 리스펠링된 표기를 직접 쓴 것에 주의하세요 — 직접 로드하는 경우, 리스펠링 자동 치환(아래 표 참고)은inference.py의apply_respelling()을 그대로 가져다 써야 적용됩니다.
| 원본 표기 | TTS 입력용 리스펠링 |
|---|---|
| Tteokbokki | Duck-bok-kee |
| Bibimbap | Bee-bim-bahp |
| Nakji Bokkeum | Nahk-jee Boh-kum |
| Agujjim | Ah-goo-jim |
| Maeuntang | May-oon-tang |
| Bibim Naengmyeon | Bee-bim Nang-myun |
| Cheonggukjang | Chung-gook-jang |
| Doenjang Jjigae | Dwen-jang Jee-geh |
| Dongtae Jjigae | Dong-teh Jee-geh |
| Dubu Kimchi | Doo-boo Kim-chee |
| Jeyuk Bokkeum | Jeh-yook Bok-kum |
| Kimchi Bokkeumbap | Kim-chee Bok-kum-bap |
| Kimchi Jjigae | Kim-chee Jee-geh |
| Kongguksu | Kong-gook-soo |
| Mulhoe | Mul-hweh |
| Ojingeo Bokkeum | Oh-jing-uh Bok-kum |
| Sundae | Soon-deh |
| Sundubu Jjigae | Soon-doo-boo Jee-geh |
| Tteokgalbi | Duck-gal-bee |
| Tteokguk | Duck-gook |
| Yukhoe | Yook-hweh |
narration_text)는 원본 로마자 표기를 그대로 유지하고, TTS 입력
시에만 이 표로 치환합니다. 최종 학습 데이터에도 이 치환된 텍스트가 반영되어 있습니다.| style | instruct |
|---|---|
| GUIDE | Speak in a calm, informative tone, like a knowledgeable guide explaining something interesting. Keep a natural, steady pace, not slow. |
| CAUTION | Say it in an urgent, warning tone, as if cautioning someone. |
Qwen/Qwen3-TTS-12Hz-1.7B-Baseinstruct 없이는 GUIDE와의 톤 구분이
약함 (반드시 instruct 파라미터와 함께 사용 권장)