RVC-Boss / GPT-SoVITS

1 min voice data can also be used to train a good TTS model! (few shot voice cloning)
MIT License
35.25k stars 4.02k forks source link

在同参数下,多次测试,api比webui中的推理产生的音频噪音要大 #1480

Open OriX0 opened 2 months ago

OriX0 commented 2 months ago
       # 测试webui推理代码复制
cache = {}
def get_tts_wav6(ref_wav_path, prompt_text, prompt_language, text, text_language, top_k=20, top_p=1, temperature=1,speed=1):
    global cache
    t = []
    t0 = ttime()
    version = 2
    dtype = torch.float32
    prompt_text = prompt_text.strip("\n")
    if (prompt_text[-1] not in splits): prompt_text += "。" if prompt_language != "en" else "."
    print("实际输入的参考文本:", prompt_text)

    text = text.strip("\n")
    if (text[0] not in splits and len(get_first(text)) < 4): text = "。" + text if text_language != "en" else "." + text

    print("实际输入的目标文本:", text)
    zero_wav = np.zeros(
        int(hps.data.sampling_rate * 0.3),
        dtype=np.float16 if is_half == True else np.float32,
    )
    with torch.no_grad():
        wav16k, sr = librosa.load(ref_wav_path, sr=16000)
        if (wav16k.shape[0] > 160000 or wav16k.shape[0] < 48000):
            raise OSError("参考音频在3~10秒范围外,请更换!")
        wav16k = torch.from_numpy(wav16k)
        zero_wav_torch = torch.from_numpy(zero_wav)
        if is_half == True:
            wav16k = wav16k.half().to(device)
            zero_wav_torch = zero_wav_torch.half().to(device)
        else:
            wav16k = wav16k.to(device)
            zero_wav_torch = zero_wav_torch.to(device)
        wav16k = torch.cat([wav16k, zero_wav_torch])
        ssl_content = ssl_model.model(wav16k.unsqueeze(0))[
            "last_hidden_state"
        ].transpose(
            1, 2
        )  # .float()
        codes = vq_model.extract_latent(ssl_content)
        prompt_semantic = codes[0, 0]
        prompt = prompt_semantic.unsqueeze(0).to(device)

    t1 = ttime()
    t.append(t1-t0)

    text = cut2(text)

    while "\n\n" in text:
        text = text.replace("\n\n", "\n")
    print("实际输入的目标文本(切句后):", text)
    texts = text.split("\n")
    texts = process_text(texts)
    texts = merge_short_text_in_array(texts, 5)
    audio_opt = []
    phones1,bert1,norm_text1=get_phones_and_bert(prompt_text, prompt_language, version)
    audio_bytes = BytesIO()
    for i_text,text in enumerate(texts):
        # 解决输入目标文本的空行导致报错的问题
        if (len(text.strip()) == 0):
            continue
        if (text[-1] not in splits): text += "。" if text_language != "en" else "."
        print("实际输入的目标文本(每句):", text)
        phones2,bert2,norm_text2=get_phones_and_bert(text, text_language, version)
        print("前端处理后的文本(每句):", norm_text2)
        bert = torch.cat([bert1, bert2], 1)
        all_phoneme_ids = torch.LongTensor(phones1+phones2).to(device).unsqueeze(0)

        bert = bert.to(device).unsqueeze(0)
        all_phoneme_len = torch.tensor([all_phoneme_ids.shape[-1]]).to(device)

        t2 = ttime()
        # cache_key="%s-%s-%s-%s-%s-%s-%s-%s"%(ref_wav_path,prompt_text,prompt_language,text,text_language,top_k,top_p,temperature)

        with torch.no_grad():
            pred_semantic, idx = t2s_model.model.infer_panel(
                all_phoneme_ids,
                all_phoneme_len,
                prompt,
                bert,
                # prompt_phone_len=ph_offset,
                top_k=top_k,
                top_p=top_p,
                temperature=temperature,
                early_stop_num=hz * max_sec,
            )
            pred_semantic = pred_semantic[:, -idx:].unsqueeze(0)
            cache[i_text]=pred_semantic
        t3 = ttime()
        refers=[]
        if(len(refers)==0):refers = [get_spepc(hps, ref_wav_path).to(dtype).to(device)]
        audio = (vq_model.decode(pred_semantic, torch.LongTensor(phones2).to(device).unsqueeze(0), refers,speed=speed).detach().cpu().numpy()[0, 0])
        max_audio=np.abs(audio).max()#简单防止16bit爆音
        if max_audio>1:audio/=max_audio
        audio_opt.append(audio)
        audio_opt.append(zero_wav)
        t4 = ttime()
        t.extend([t2 - t1,t3 - t2, t4 - t3])
        t1 = ttime()
    cache = {}
    audio_bytes = pack_audio(audio_bytes, (np.concatenate(audio_opt, 0) * 32768).astype(np.int16), hps.data.sampling_rate)

    # # 将音频数据合并并转换为字节类型
    # audio_data = (np.concatenate(audio_opt, 0) * 32768).astype(np.int16)
    # yield audio_data.tobytes()
    if not stream_mode == "normal": 
        if media_type == "wav":
            audio_bytes = pack_wav(audio_bytes,hps.data.sampling_rate)
        yield audio_bytes.getvalue()
selfnat commented 2 months ago

是的,我测试过一样的结果, 同样疑惑

KamioRinn commented 2 months ago

有无样例?

KamioRinn commented 2 months ago

模型版本是否一致?

OriX0 commented 2 months ago

模型版本是否一致?

模型版本是一致的 都是v2版本 步数及参考音频都是一样的

OriX0 commented 2 months ago

有无样例?

已上传到123网盘,并开通了免登录流量包 可直接下载 https://www.123pan.com/s/501cVv-gLmWA 等待大佬解答

本模型原始样本电噪比较大 所以效果特别明显 其他的模型均有该情况

KamioRinn commented 2 months ago

webui和api的区别主要为两点:

  1. webui使用gradio进行音频打包(通过pydub库),api通过soundfile进行打包。
  2. webui为了防止精度转换问题进行了归一化处理,api无此处理。

针对这两点我对代码进行了改造测试:

  1. 在获得模型输出的音频数据后,一组归一化,一组不处理,所得到音频数据频谱、波形相同
  2. 在获得模型输出的音频数据后,一组pydub,一组soundfile,所得到音频数据频谱、波形相同
  3. 在获得模型输出的音频数据后,一组归一化后pydub输出,一组不处理后soundfile输出,所得到音频数据频谱、波形相同
  4. 在获得模型输出的音频数据后,一组归一化后由gradio输出,一组不处理后soundfile输出,所得到音频数据频谱、波形相同

结论: 暂无明显数据支持可以表明两者音质存在差别

OriX0 commented 2 months ago

webui和api的区别主要为两点:

  1. webui使用gradio进行音频打包(通过pydub库),api通过soundfile进行打包。
  2. webui为了防止精度转换问题进行了归一化处理,api无此处理。

针对这两点我对代码进行了改造测试:

  1. 在获得模型输出的音频数据后,一组归一化,一组不处理,所得到音频数据频谱、波形相同
  2. 在获得模型输出的音频数据后,一组pydub,一组soundfile,所得到音频数据频谱、波形相同
  3. 在获得模型输出的音频数据后,一组归一化后pydub输出,一组不处理后soundfile输出,所得到音频数据频谱、波形相同
  4. 在获得模型输出的音频数据后,一组归一化后由gradio输出,一组不处理后soundfile输出,所得到音频数据频谱、波形相同

结论: 暂无明显数据支持可以表明两者音质存在差别

辛苦大佬测试解答,麻烦问一下大佬测试所用的模型是我提供的吗还是大佬本身的。我的原始样本应该是没有你这边处理的这么好,本身的电噪比较大

KamioRinn commented 2 months ago

辛苦大佬测试解答,麻烦问一下大佬测试所用的模型是我提供的吗还是大佬本身的。我的原始样本应该是没有你这边处理的这么好,本身的电噪比较大

底模用你的参考音频直接推理,理论上来看模型好坏不会导致数据打包出来有差异吧

OriX0 commented 2 months ago

辛苦大佬测试解答,麻烦问一下大佬测试所用的模型是我提供的吗还是大佬本身的。我的原始样本应该是没有你这边处理的这么好,本身的电噪比较大

底模用你的参考音频直接推理,理论上来看模型好坏不会导致数据打包出来有差异吧

关于你说的,我进行了测试,测试结果确实如你所说在默认底膜去推理出来的基本没有差异。在我这边的测试下模型确实产生了数据下的差异

selfnat commented 2 months ago

辛苦大佬测试解答,麻烦问一下大佬测试所用的模型是我提供的吗还是大佬本身的。我的原始样本应该是没有你这边处理的这么好,本身的电噪比较大

底模用你的参考音频直接推理,理论上来看模型好坏不会导致数据打包出来有差异吧

我测试的差异是api和web在所有模型和参数一样的情况下,同样的文案出来的音频返回的正确率是web更稳定一些, 训练了几个模型, 都存在相同的问题,某些易错的的文字 , 同样的文字20次web 能有15次推理正确,api 5次正常, 而且是普遍性的

OriX0 commented 2 months ago

辛苦大佬测试解答,麻烦问一下大佬测试所用的模型是我提供的吗还是大佬本身的。我的原始样本应该是没有你这边处理的这么好,本身的电噪比较大

底模用你的参考音频直接推理,理论上来看模型好坏不会导致数据打包出来有差异吧

我测试的差异是api和web在所有模型和参数一样的情况下,同样的文案出来的音频返回的正确率是web更稳定一些, 训练了几个模型, 都存在相同的问题,某些易错的的文字 , 同样的文字20次web 能有15次推理正确,api 5次正常, 而且是普遍性的

这个我也碰到了,同样的参考,api推理吞字的概率大

KamioRinn commented 2 months ago

这个我也碰到了,同样的参考,api推理吞字的概率大

用你提供的模型测试了,同样测试方法下出来的音频文件是一致的。 吞字的问题,webui的topk默认15,api的topk默认10

OriX0 commented 2 months ago

这个我也碰到了,同样的参考,api推理吞字的概率大

用你提供的模型测试了,同样测试方法下出来的音频文件是一致的。 吞字的问题,webui的topk默认15,api的topk默认10

这个参数我做了修改,并不是默认值 方便提供一份您那边生成出来的音频吗?对了,api端我是通过本地接口请求,而非web页面输入url

einsqing commented 2 months ago

我也是同样的问题,api 效果没有 web 好,同样的模型

selfnat commented 2 months ago

直接在GPT_SoVITS/inference_webui.py的基础上修改为api.api 效果好很多 ,不知道两边具体哪里出入这么明显

XXXXRT666 commented 2 months ago

api.py可以设置模型,有特定的endpoint,建议在同模型同参考下对比

einsqing commented 2 months ago

直接在GPT_SoVITS/inference_webui.py的基础上修改为api.api 效果好很多 ,不知道两边具体哪里出入这么明显

我页直接重写了api,效果就跟webui一样

KamioRinn commented 2 months ago

直接在GPT_SoVITS/inference_webui.py的基础上修改为api.api 效果好很多 ,不知道两边具体哪里出入这么明显

我页直接重写了api,效果就跟webui一样

找到原因了吗?

einsqing commented 2 months ago

直接在GPT_SoVITS/inference_webui.py的基础上修改为api.api 效果好很多 ,不知道两边具体哪里出入这么明显

我页直接重写了api,效果就跟webui一样

找到原因了吗?

没找到原因,重写解决

selfnat commented 2 months ago

新更新的是定位到修复了这个问题吗?

KamioRinn commented 2 months ago

新更新的是定位到修复了这个问题吗?

针对其他问题的更新,顺便加了一个高动态范围的选项(在webui里并无这个功能)。这个”问题“在这个issues里并没有找到”问题“,建议更新后测试

roronoa-zoro-d commented 2 months ago

生成mel的时候是32k的采样率, 生成音色的时候是16k, 跟这个有没有关系。