mirror of
https://github.com/RVC-Boss/GPT-SoVITS.git
synced 2026-07-08 15:51:09 +08:00
Compare commits
5 Commits
1296b485ce
...
8cdecd047c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8cdecd047c | ||
|
|
0be59c8043 | ||
|
|
b5a67e6247 | ||
|
|
b9211657d8 | ||
|
|
5867122df2 |
@ -356,7 +356,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
x = self.ar_text_embedding(x)
|
||||
x = x + self.bert_proj(bert_feature.transpose(1, 2))
|
||||
x = self.ar_text_position(x)
|
||||
x_mask = make_pad_mask(x_lens)
|
||||
x_mask = make_pad_mask_left(x_lens)
|
||||
|
||||
y_mask = make_pad_mask(y_lens)
|
||||
y_mask_int = y_mask.type(torch.int64)
|
||||
@ -420,7 +420,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
mask=xy_attn_mask,
|
||||
)
|
||||
x_len = x_lens.max()
|
||||
logits = self.ar_predict_layer(xy_dec[:, x_len:])
|
||||
logits = self.ar_predict_layer(xy_dec[:, x_len-1:])
|
||||
|
||||
###### DPO #############
|
||||
reject_xy_pos, reject_xy_attn_mask, reject_targets = self.make_input_data(
|
||||
@ -432,7 +432,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
mask=reject_xy_attn_mask,
|
||||
)
|
||||
x_len = x_lens.max()
|
||||
reject_logits = self.ar_predict_layer(reject_xy_dec[:, x_len:])
|
||||
reject_logits = self.ar_predict_layer(reject_xy_dec[:, x_len-1:])
|
||||
|
||||
# loss
|
||||
# from feiteng: 每次 duration 越多, 梯度更新也应该更多, 所以用 sum
|
||||
@ -455,7 +455,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
x = self.ar_text_embedding(x)
|
||||
x = x + self.bert_proj(bert_feature.transpose(1, 2))
|
||||
x = self.ar_text_position(x)
|
||||
x_mask = make_pad_mask(x_lens)
|
||||
x_mask = make_pad_mask_left(x_lens)
|
||||
|
||||
y_mask = make_pad_mask(y_lens)
|
||||
y_mask_int = y_mask.type(torch.int64)
|
||||
@ -502,7 +502,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
(xy_pos, None),
|
||||
mask=xy_attn_mask,
|
||||
)
|
||||
logits = self.ar_predict_layer(xy_dec[:, x_len:]).permute(0, 2, 1)
|
||||
logits = self.ar_predict_layer(xy_dec[:, x_len-1:]).permute(0, 2, 1)
|
||||
# loss
|
||||
# from feiteng: 每次 duration 越多, 梯度更新也应该更多, 所以用 sum
|
||||
loss = F.cross_entropy(logits, targets, reduction="sum")
|
||||
@ -578,7 +578,7 @@ class Text2SemanticDecoder(nn.Module):
|
||||
def pad_y_eos(self, y, y_mask_int, eos_id):
|
||||
targets = F.pad(y, (0, 1), value=0) + eos_id * F.pad(y_mask_int, (0, 1), value=1)
|
||||
# 错位
|
||||
return targets[:, :-1], targets[:, 1:]
|
||||
return targets[:, :-1], targets
|
||||
|
||||
def infer_panel_batch_infer(
|
||||
self,
|
||||
|
||||
@ -304,10 +304,10 @@ class TTS_Config:
|
||||
configs: dict = self._load_configs(self.configs_path)
|
||||
|
||||
assert isinstance(configs, dict)
|
||||
version = configs.get("version", "v2").lower()
|
||||
assert version in ["v1", "v2", "v3", "v4", "v2pro", "v2proplus"]
|
||||
self.default_configs[version] = configs.get(version, self.default_configs[version])
|
||||
self.configs: dict = configs.get("custom", deepcopy(self.default_configs[version]))
|
||||
configs_ = deepcopy(self.default_configs)
|
||||
configs_.update(configs)
|
||||
self.configs: dict = configs_.get("custom", configs_["v2"])
|
||||
self.default_configs = deepcopy(configs_)
|
||||
|
||||
self.device = self.configs.get("device", torch.device("cpu"))
|
||||
if "cuda" in str(self.device) and not torch.cuda.is_available():
|
||||
@ -315,11 +315,13 @@ class TTS_Config:
|
||||
self.device = torch.device("cpu")
|
||||
|
||||
self.is_half = self.configs.get("is_half", False)
|
||||
# if str(self.device) == "cpu" and self.is_half:
|
||||
# print(f"Warning: Half precision is not supported on CPU, set is_half to False.")
|
||||
# self.is_half = False
|
||||
if str(self.device) == "cpu" and self.is_half:
|
||||
print(f"Warning: Half precision is not supported on CPU, set is_half to False.")
|
||||
self.is_half = False
|
||||
|
||||
version = self.configs.get("version", None)
|
||||
self.version = version
|
||||
assert self.version in ["v1", "v2", "v3", "v4", "v2Pro", "v2ProPlus"], "Invalid version!"
|
||||
self.t2s_weights_path = self.configs.get("t2s_weights_path", None)
|
||||
self.vits_weights_path = self.configs.get("vits_weights_path", None)
|
||||
self.bert_base_path = self.configs.get("bert_base_path", None)
|
||||
@ -576,6 +578,10 @@ class TTS:
|
||||
if self.configs.is_half and str(self.configs.device) != "cpu":
|
||||
self.vits_model = self.vits_model.half()
|
||||
|
||||
self.configs.save_configs()
|
||||
|
||||
|
||||
|
||||
def init_t2s_weights(self, weights_path: str):
|
||||
print(f"Loading Text2Semantic weights from {weights_path}")
|
||||
self.configs.t2s_weights_path = weights_path
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
version: v2ProPlus
|
||||
custom:
|
||||
bert_base_path: GPT_SoVITS/pretrained_models/chinese-roberta-wwm-ext-large
|
||||
cnhuhbert_base_path: GPT_SoVITS/pretrained_models/chinese-hubert-base
|
||||
|
||||
@ -125,7 +125,8 @@ is_exist_s2gv4 = os.path.exists(path_sovits_v4)
|
||||
tts_config = TTS_Config("GPT_SoVITS/configs/tts_infer.yaml")
|
||||
tts_config.device = device
|
||||
tts_config.is_half = is_half
|
||||
tts_config.version = version
|
||||
# tts_config.version = version
|
||||
tts_config.update_version(version)
|
||||
if gpt_path is not None:
|
||||
if "!" in gpt_path or "!" in gpt_path:
|
||||
gpt_path = name2gpt_path[gpt_path]
|
||||
|
||||
212
api_model_manager.py
Normal file
212
api_model_manager.py
Normal file
@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
import re
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("gpt-sovits-api")
|
||||
|
||||
class ModelManager:
|
||||
"""
|
||||
GPT-SoVITS模型管理器
|
||||
用于管理GPT和SoVITS模型的映射关系
|
||||
"""
|
||||
def __init__(self):
|
||||
self.gpt_weights_dir = "GPT_weights"
|
||||
self.sovits_weights_dir = "SoVITS_weights"
|
||||
|
||||
# 扫描多个版本的模型目录
|
||||
self.gpt_dirs = [
|
||||
"GPT_weights",
|
||||
"GPT_weights_v2",
|
||||
"GPT_weights_v3",
|
||||
"GPT_weights_v4"
|
||||
]
|
||||
|
||||
self.sovits_dirs = [
|
||||
"SoVITS_weights",
|
||||
"SoVITS_weights_v2",
|
||||
"SoVITS_weights_v3",
|
||||
"SoVITS_weights_v4"
|
||||
]
|
||||
|
||||
# 模型映射缓存
|
||||
self.model_mapping = {}
|
||||
self.voice_info = {}
|
||||
|
||||
# 加载模型映射
|
||||
self.load_model_mapping()
|
||||
|
||||
def _extract_model_info(self, filename: str) -> Dict:
|
||||
"""
|
||||
从模型文件名中提取信息
|
||||
支持多种命名格式:
|
||||
1. 模型名_e迭代次数_s批次.pth
|
||||
2. 模型名-e迭代次数.ckpt
|
||||
|
||||
Args:
|
||||
filename: 模型文件名
|
||||
|
||||
Returns:
|
||||
Dict: 包含模型名称、迭代次数和批次的字典
|
||||
"""
|
||||
basename = os.path.basename(filename)
|
||||
name_parts = basename.split('.')
|
||||
base_name = name_parts[0]
|
||||
|
||||
# 尝试匹配迭代次数 (e参数),支持连字符(-)和下划线(_)
|
||||
e_match = re.search(r"[-_]e(\d+)", base_name)
|
||||
|
||||
# 尝试匹配批次 (s参数),主要在SoVITS模型中使用
|
||||
s_match = re.search(r"[-_]s(\d+)", base_name)
|
||||
|
||||
# 提取模型名称(去掉e和s参数部分)
|
||||
model_name = base_name
|
||||
|
||||
# 如果找到了e参数
|
||||
if e_match:
|
||||
# 获取e参数之前的部分作为模型名称
|
||||
e_pos = base_name.find(e_match.group(0))
|
||||
if e_pos > 0:
|
||||
separator = base_name[e_pos] # 获取分隔符 (- 或 _)
|
||||
model_name = base_name.split(f"{separator}e")[0]
|
||||
|
||||
# 提取扩展名
|
||||
ext = os.path.splitext(basename)[1].lower()
|
||||
|
||||
iteration = int(e_match.group(1)) if e_match else 0
|
||||
batch = int(s_match.group(1)) if s_match else 0
|
||||
|
||||
logger.debug(f"解析模型: {basename} -> 名称={model_name}, 迭代={iteration}, 批次={batch}")
|
||||
|
||||
return {
|
||||
"name": model_name,
|
||||
"iteration": iteration,
|
||||
"batch": batch,
|
||||
"filename": filename
|
||||
}
|
||||
|
||||
def load_model_mapping(self):
|
||||
"""
|
||||
扫描模型目录,创建模型映射关系
|
||||
将相同名称的GPT和SoVITS模型进行匹配
|
||||
"""
|
||||
# 扫描GPT模型
|
||||
gpt_models = {}
|
||||
for dir_path in self.gpt_dirs:
|
||||
if not os.path.exists(dir_path):
|
||||
continue
|
||||
|
||||
for file_path in glob.glob(f"{dir_path}/*.ckpt"):
|
||||
model_info = self._extract_model_info(file_path)
|
||||
model_name = model_info["name"]
|
||||
|
||||
# 使用更高迭代次数和批次的模型
|
||||
if model_name not in gpt_models or \
|
||||
(model_info["iteration"] > gpt_models[model_name]["iteration"] or \
|
||||
(model_info["iteration"] == gpt_models[model_name]["iteration"] and \
|
||||
model_info["batch"] > gpt_models[model_name]["batch"])):
|
||||
gpt_models[model_name] = model_info
|
||||
|
||||
# 扫描SoVITS模型
|
||||
sovits_models = {}
|
||||
for dir_path in self.sovits_dirs:
|
||||
if not os.path.exists(dir_path):
|
||||
continue
|
||||
|
||||
for file_path in glob.glob(f"{dir_path}/*.pth"):
|
||||
model_info = self._extract_model_info(file_path)
|
||||
model_name = model_info["name"]
|
||||
|
||||
# 使用更高迭代次数和批次的模型
|
||||
if model_name not in sovits_models or \
|
||||
(model_info["iteration"] > sovits_models[model_name]["iteration"] or \
|
||||
(model_info["iteration"] == sovits_models[model_name]["iteration"] and \
|
||||
model_info["batch"] > sovits_models[model_name]["batch"])):
|
||||
sovits_models[model_name] = model_info
|
||||
|
||||
# 创建映射关系
|
||||
for name in set(list(gpt_models.keys()) + list(sovits_models.keys())):
|
||||
gpt_model = gpt_models.get(name)
|
||||
sovits_model = sovits_models.get(name)
|
||||
|
||||
if gpt_model and sovits_model:
|
||||
self.model_mapping[name] = {
|
||||
"gpt_path": gpt_model["filename"],
|
||||
"sovits_path": sovits_model["filename"],
|
||||
"iteration": min(gpt_model["iteration"], sovits_model["iteration"]),
|
||||
"batch": min(gpt_model["batch"], sovits_model["batch"])
|
||||
}
|
||||
|
||||
self.voice_info[name] = {
|
||||
"id": name,
|
||||
"name": name,
|
||||
"iteration": min(gpt_model["iteration"], sovits_model["iteration"]),
|
||||
"batch": min(gpt_model["batch"], sovits_model["batch"])
|
||||
}
|
||||
|
||||
logger.info(f"已加载 {len(self.model_mapping)} 个模型映射")
|
||||
|
||||
def get_model_paths(self, voice_name: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""
|
||||
获取指定voice对应的GPT和SoVITS模型路径
|
||||
|
||||
Args:
|
||||
voice_name: 声音名称
|
||||
|
||||
Returns:
|
||||
Tuple[str, str]: (GPT模型路径, SoVITS模型路径)
|
||||
"""
|
||||
if voice_name in self.model_mapping:
|
||||
return (
|
||||
self.model_mapping[voice_name]["gpt_path"],
|
||||
self.model_mapping[voice_name]["sovits_path"]
|
||||
)
|
||||
return None, None
|
||||
|
||||
def get_all_voices(self) -> List[Dict]:
|
||||
"""
|
||||
获取所有可用的声音列表
|
||||
|
||||
Returns:
|
||||
List[Dict]: 声音信息列表
|
||||
"""
|
||||
return [self.voice_info[name] for name in self.voice_info]
|
||||
|
||||
def get_voice_details(self, voice_name: str) -> Optional[Dict]:
|
||||
"""
|
||||
获取指定声音的详细信息
|
||||
|
||||
Args:
|
||||
voice_name: 声音名称
|
||||
|
||||
Returns:
|
||||
Dict: 声音详细信息
|
||||
"""
|
||||
if voice_name in self.voice_info:
|
||||
info = self.voice_info[voice_name].copy()
|
||||
info.update({
|
||||
"gpt_path": self.model_mapping[voice_name]["gpt_path"],
|
||||
"sovits_path": self.model_mapping[voice_name]["sovits_path"]
|
||||
})
|
||||
return info
|
||||
return None
|
||||
|
||||
# 单例模式
|
||||
model_manager = ModelManager()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 测试代码
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
manager = ModelManager()
|
||||
voices = manager.get_all_voices()
|
||||
print(f"发现 {len(voices)} 个声音模型:")
|
||||
for voice in voices:
|
||||
print(f"- {voice['name']}, 迭代次数: {voice['iteration']}, 批次: {voice['batch']}")
|
||||
gpt_path, sovits_path = manager.get_model_paths(voice['name'])
|
||||
print(f" GPT: {gpt_path}")
|
||||
print(f" SoVITS: {sovits_path}")
|
||||
1789
api_openai_feature.py
Normal file
1789
api_openai_feature.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -59,7 +59,7 @@ top_html = """
|
||||
<a href="https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e" target="_blank">
|
||||
<img src="https://img.shields.io/badge/简体中文-阅读文档-blue?style=for-the-badge&logo=googledocs&logoColor=white" style="width: auto; height: 30px;">
|
||||
</a>
|
||||
<a href="https://github.com/RVC-Boss/GPT-SoVITS" target="_blank">
|
||||
<a href="https://lj1995-gpt-sovits-proplus.hf.space/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/免费在线体验-free_online_demo-yellow.svg?style=for-the-badge&logo=huggingface" style="width: auto; height: 30px;">
|
||||
</a>
|
||||
<a href="https://www.yuque.com/baicaigongchang1145haoyuangong/ib3g1e" target="_blank">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user