vits语音合成

vits语音合成

y4ny4n

记录一次vits模型训练并部署到qq-bot的踩坑历程

lab1

梦开始的地方:https://www.bilibili.com/video/BV1w84y1n7Ei/

VITS原版:https://github.com/jaywalnut310/vits

VITS(CjangCjengh版):https://github.com/CjangCjengh/vits

详细教程:https://www.bilibili.com/read/cv21153903

符华模型:https://www.bilibili.com/video/BV1zy4y197Tz/

派蒙模型:https://www.bilibili.com/video/BV16G4y1B7Ey/

加载预训练模型:https://www.bilibili.com/video/BV1yj411N7rt/

零基础炼丹秘籍 - 为自己喜爱的角色训练TTS(文字转语音)模型:https://www.bilibili.com/read/cv17826415

零基础炼丹 - vits版补充: https://www.bilibili.com/read/cv18357171

预设加载预训练模型的vits:https://github.com/rotten-work/vits-mandarin-windows

MoeTTS:https://github.com/luoyily/MoeTTS

MoeGoe:

数据集准备

格式处理

wave查看采样率

1
2
3
4
5
6
7
import os
import wave
for file_name in os.listdir(file_path):
#wave查看采样率
with wave.open(file_path+file_name, "rb") as wave_file:
frame_rate = wave_file.getframerate()
print(frame_rate)

scipy查看采样率、声道数、pcm位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import numpy as np
import scipy.io.wavfile
for file_name in os.listdir(file_path):
rate, data = scipy.io.wavfile.read(file_path+file_name)
# 打印采样率和数据的形状
print("Sample rate:", rate)
print("Data shape:", data.shape)
# 检查数据的维度,如果是1,说明是单声道,如果是2,说明是立体声
if len(data.shape) == 1:
print("The wav file is mono.")
elif len(data.shape) == 2:
print("The wav file is stereo.")
else:
print("The wav file is invalid.")
# 检查dtype属性来确定它的位数。如果dtype是int16,那么wav文件就是16位pcm的
if data.dtype == 'int16':
print('The wav file is 16 bit pcm.')
else:
print('The wav file is not 16 bit pcm.')

pydub 修改声道数和采样率

1
2
3
4
5
6
7
8
9
from pydub import AudioSegment
# 读取wav文件,返回一个AudioSegment对象
sound = AudioSegment.from_wav("/path/to/file.wav")
# 将声道数设置为1,即单声道
sound = sound.set_channels(1)
# 将采样率调整为16000
sound = sound.set_frame_rate(16000)
# 导出新的wav文件
sound.export("/output/path.wav", format="wav")

整理所有音频数据符合单声道、22050Hz,PCM 16bit。

文本整理

提前使用腾讯云的接口实现了语音转文字 这里只需整理为固定输入格式

将数据集分为测试集和训练集分别整理

环境配置

image-20230302224501385

租好gpu后安装requirements中的依赖

配置文件

生成config.json

根据租的服务器显存选择合适的batch

修改cleaner为chinese_cleaner

进行预处理

image-20230302234847771

训练

首先进行预训练

使用中文女声标贝进行预训练300个epoch,再在该权重模型基础上进行微调

只需按原方法将标贝的语音数据集和文本进行替换(并重新预处理生成cleaned文件)开始训练即可

image-20230303012745585

RTX 3080 10GB 开始设置batch为16后内存不足 改为8

训练一个epoch大概5min

训练过程中发现2200个epoch的标贝预训练模型也无法正常说话?并且模型参数不匹配,怀疑是预训练模型的版本与MoeTTS及后面训练的模型版本冲突,故又租了个服务器,打算自己训练300epoch的标贝试试。(money—

TTS推理

MoeGoe

开始使用MoeGoe后报错,修改全英路径和删除config文件后无果,改用MoeTTS。

image-20230304180937189

MoeTTS

可以加载kilo的模型,不能加载预训练的标贝模型

RuntimeError: Error(s) in loading state_dict for SynthesizerTrn:
size mismatch for enc_p.emb.weight: copying a param with shape torch.Size([50, 192]) from checkpoint, the shape in current model is torch.Size([78, 192]).

vits-gradio

kilo和标贝都参数不匹配

RuntimeError: Error(s) in loading state_dict for SynthesizerTrn:
size mismatch for enc_p.emb.weight: copying a param with shape torch.Size([78, 192]) from checkpoint, the shape in current model is torch.Size([43, 192]).

类似报错:https://www.bilibili.com/video/BV1be4y1V7g6/

使用说明:https://www.bilibili.com/video/BV1DT41127wr/

MoeSS

教程专栏:https://www.bilibili.com/read/cv22051145

需要把pth转为onxx文件 先不尝试了

co-lab在线推理

github项目:https://github.com/rotten-work/vits-mandarin-windows

https://colab.research.google.com/drive/1VWBOp3PDGNO77_xOm20yRtc4CSmsbqtb#scrollTo=2z_JvLmivPEf

突然发现前几个tts貌似都打开错方式了….

同一个模型我放到唯一能用的MoeTTS中只能发出单音节词但是上传到colab上发现500epoch已经可以说话了且效果非常好(但是标贝模型还是会出参数不匹配的error)

尝试拉到本地试一下

本地windows环境需要安装MSVC和win10SDK

然后就可以推理了

服务器部署

部署踩坑

1.poetry安装pytorch问题

https://stackoverflow.com/questions/59158044/poetry-and-pytorch

在pyproject.toml中添加以下后并poetry install

1
2
torch = {url = "https://download.pytorch.org/whl/cpu/torch-1.8.0%2Bcpu-cp38-cp38-linux_x86_64.whl", markers = "sys_platform == 'linux'"}
torchvision = {url = "https://download.pytorch.org/whl/cpu/torchvision-0.9.0%2Bcpu-cp38-cp38-linux_x86_64.whl", markers = "sys_platform == 'linux'"}

2.重新build monotonic alignment search

3.OSError: cannot load library ‘libsndfile.so’: libsndfile.so: cannot open shared object file: No such file or directory

依赖库librosa报错 执行yum install libsndfile

4.No module named ..

路径问题 参考引用不同文件夹下的模块 https://www.jb51.net/article/269579.htm

5.依旧是ImportError

image-20230305020444178

这个一眼看出是vits模型里的文件夹和saya模块的文件夹重名了(犯过太多次错

改bot主程序saya加载部分和文件夹名

至此 可以实现vits模型tts推理接入qq-bot的模块 效果如图

并且可以随时上传新训练好的模型进行替换~

tensorboard

在autodl中使用tensorboard查看模型收敛情况

autodl默认的tensorboard的event文件保存路径为/root/tf-logs/

修改tf logs目录为模型文件夹下生成event的路径 再重启tensorboard

image-20230306132346778

在autopanel中查看loss曲线

模型的收敛条件

参考: https://www.bilibili.com/read/cv6372536/

通常模型的收敛条件可以有以下3个:

  • loss小于某个预先设定的较小的值

    模型的训练目的就是为了减少loss值,那么我们可以设定一个比较小的数值,每一次训练的时候我们都同时计算一下loss值的大小,当loss值小于某个预先设定的阈值,就可以认为模型收敛了。那么就可以结束训练。

  • 两次迭代之间权值的变化已经很小了

    每一次训练我们可以记录模型权值的变化,如果我们发现两次迭代之间模型的权值变化已经很小,那么说明模型已经几乎不需要做权值地调整了,那么就可以认为模型收敛,可以结束训练。

  • 设定最大迭代次数,当迭代超过最大次数就停止
    用得最多的方式。我们可以预先设定一个比较大的模型迭代周期,比如迭代100次,或者10000次,或者1000000次等(需要根据实际情况来选择)。模型完成规定次数的训练之后我们就可以认为模型训练完毕。如果达到我们设置的训练次数以后我们发现模型还没有训练好的话,我们可以继续增加训练次数,让模型继续训练就可以了。

tensorboard给出的loss曲线过多 一时没看懂

image-20230308213404345

只能翻一下vits中使用的loss学习一下

vits损失函数

https://zhuanlan.zhihu.com/p/419883319

https://blog.csdn.net/zzfive/article/details/127503913

VITS可以看作是VAE和GAN的联合训练,因此总体损失为:

image-20230306161858486

loss_gen_all = loss_gen + loss_fm + loss_mel + loss_dur + loss_kl

对抗训练过程中,判别器是常规的判别器损失结构,但是使用的是多周期判别器,由多个子判别器组成。生成器的损失,包括mel重建损失、KL散度、时长预测器损失、对抗训练生成损失以及特征图损失。

1.重建损失

image-20230306162112238

loss_mel = F.l1_loss(y_mel, y_hat_mel) * hps.train.c_mel

  1. KL散度

    image-20230306162237066

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 先验分布和后验分布之间的KL散度
    def kl_loss(z_p, logs_q, m_p, logs_p, z_mask):
    """
    z_p, logs_q: [b, h, t_t]
    m_p, logs_p: [b, h, t_t]
    """
    z_p = z_p.float()
    logs_q = logs_q.float()
    m_p = m_p.float()
    logs_p = logs_p.float()
    z_mask = z_mask.float()

    kl = logs_p - logs_q - 0.5
    kl += 0.5 * ((z_p - m_p)**2) * torch.exp(-2. * logs_p)
    kl = torch.sum(kl * z_mask)
    l = kl / torch.sum(z_mask)
    return l

    loss_kl = kl_loss(z_p, logs_q, m_p, logs_p, z_mask) * hps.train.c_kl

3.对抗训练

image-20230306164629488

1
2
3
4
5
6
7
8
9
10
# 生成器的对抗损失,就是将生成器生成的波形经过判别器后的输出与1计算距离损失,L2损失
def generator_loss(disc_outputs):
loss = 0
gen_losses = []
for dg in disc_outputs:
dg = dg.float()
l = torch.mean((1-dg)**2)
gen_losses.append(l)
loss += l
return loss, gen_losses

loss_gen, losses_gen = generator_loss(y_d_hat_g)

image-20230306164758952

1
2
3
4
5
6
7
8
9
# 计算对抗训练中生成波形和真实波形在判别器中间特征之间的距离损失
def feature_loss(fmap_r, fmap_g):
loss = 0
for dr, dg in zip(fmap_r, fmap_g):
for rl, gl in zip(dr, dg):
rl = rl.float().detach()
gl = gl.float()
loss += torch.mean(torch.abs(rl - gl))
return loss * 2

loss_fm = feature_loss(fmap_r, fmap_g)

4.时长预测器损失

时长预测器损失在模型forward函数中直接计算

loss_dur = torch.sum(l_length.float())

至此 可以根据tensorboard中给出的loss曲线分析模型的收敛情况


看到github vits下相关issue对loss的讨论

https://github.com/jaywalnut310/https://y4ny4nblog.oss-cn-beijing.aliyuncs.com/img/issues/14

找了别的佬训练vits时的loss参考

1.https://blog.csdn.net/qq_39182815/article/details/126312069

500条 57k步收敛

image-20230308213045539

2.https://www.bilibili.com/video/BV1dY411Q7q1/

300条 1500个epoch

image-20230308212904615

3.https://github.com/jaywalnut310/https://y4ny4nblog.oss-cn-beijing.aliyuncs.com/img/issues/14

心路历程

记录一下在训练过程中模型合成效果不好采取的一系列措施:

解决方案:

1.等机器1的2000个epoch跑完继续训练2000个(保存当前镜像

2.修改训练程序使之可以在更大显存机器上恢复训练 调高batch(打包vits文件夹

目前怀疑模型不收敛的原因是

  • batch太小,epoch还不够
  • 看Loss的方式有问题 了解GAN的loss变化趋势和收敛

2000epoch已跑完,效果还不是很好。租了一台3090 24G,调高了batch。但是出现了新的问题,由于我修改了bacth,global_step的值减小。模型保存文件的机制是根据global_step的值大小比较模型的新旧,导致新训练出的权重一直无法保存,且tensorboard中以global_step为横轴的折线图发生错误。

解决方案:

1.修改权重模型保存文件名,继续小batch时的计算方式保持一致(有点自欺欺人,不知道引发什么报错

2.修改参数should_auto_delete_old_checkpoints为False,保存适量的大batch模型后删除原有的(但tensorboard无法正常显示更新模型的效果,需要拉到本地进行tts推理

了解到tensorboard通过events.out.tfevents文件绘制图表。events.out.tfevents文件是训练时生成的日志文件,用于记录训练过程中的各种信息,如损失函数、准确率、图结构等。使用TensorBoard工具来可视化这些信息,分析训练效果和模型性能。

发现其命名方式中包含容器名,尝试分类之前机器和现在机器训练产生的文件后,果然更新后的折线图可以正常显示。同理新建文件夹存放之前机器的测试结果,并只在eval文件夹下存放当前机器的测试结果,测试音频也可以播放。

image-20230312013936188

就是两个batch的global step会有重叠而非延续,不过可以凑合着看,毕竟不会改源码T^T

image-20230312014052008

踩坑

由于原版vits没有加载预训练权重模型的模块

更换为https://github.com/rotten-work/vits-mandarin-windows

需要在config.json里增加一些字段如data_loader

  1. keyerror

image-20230303045551666

文本数据真的要好好处理,我哭死。从报错一层一层溯源发现是我的文本数据里由于包含阿拉伯数字没有解析所以出现Keyerror,已经把数据里的阿拉伯数字都改为中文了。。

2.预训练加载不进去

config配置路径问题

image-20230303051559316

原因是在config.json中配置的预训练权重路径没有写绝对路径(愚蠢的我

优化器数组长度问题

valueerror: loaded state dict contains a parameter group that doesn’t match

image-20230303051631964

没找到解决方法,只能先注释了

3.跨地区数据拷贝

现在很想知道微调预训练模型不加载优化器对预测结果的影响(在标贝预训练模型上训练了200个epoch只能发出单音节让我很害怕…

目前猜测是batch太小了(但显存10G不允许,所以尝试一下迁移到24G显存的实例上继续训练 对比模型收敛程度

由于租的两个实例分属不同地域(我恨,没有充分吸取阿里云服务器的教训…只能通过scp实现实例间的数据拷贝(同地域可以迁移实例以及/root/autodl-nas或/root/autodl-fs实现数据共享)

https://www.autodl.com/docs/env/

在控制台查看主机2的ssh登录命令及密码,在主机1输入

scp -rP 66666 /root/autodl-tmp/xxxxx region-3.autodl.com:/root/autodl-tmp/实现主动传输

4.在新机器上恢复训练中优化器缺少初始学习率参数

image-20230307014550288

https://blog.csdn.net/guls999/article/details/85695409

KeyError: “param ‘initial_lr’ is not specified in param_groups[0] when resuming an optimizer”

bing
1
2
3
4
5
6
这个错误是由于在恢复优化器时,没有正确地指定initial_lr参数或者last_epoch参数。可以尝试以下几种方法:
1.在创建优化器时,为每个参数组添加initial_lr参数
例如:optimizer = torch.optim.SGD([{'params': model.parameters(), 'initial_lr': 0.01}], lr=0.01)
initial_lr参数是用来指定优化器中每个参数组的初始学习率。如果你在创建优化器时没有指定这个参数,那么在恢复优化器时,它会从param_groups[0]中寻找这个参数,如果没有找到,就会报错。
你可以在创建优化器时,为每个参数组添加initial_lr参数,例如:optimizer = torch.optim.SGD([{'params': model.parameters(), 'initial_lr': 0.01}], lr=0.01)。这样,在恢复优化器时,它就可以从每个参数组中读取正确的初始学习率。
2.恢复优化器的时候使用last_epoch=epoch-1参数。这个参数用于指定上一次训练的轮数,以便优化器使用正确的学习率。例如,如果你上一次训练了10轮,那么你可以使用last_epoch=9参数来恢复优化器,并继续训练。

这个错误是之前注释掉优化器加载的错误造成的(utils load_checkpoint函数),

会导致每次载入检查点模型时都不加载优化器(导致模型不收敛的原因?训练过程没中断,不会重复载入模型。但可能导致中断后epoch从0开始,删除当时的注释。

lab2

视频链接:https://www.bilibili.com/video/BV1jo4y1e71H/

Github项目地址:https://github.com/Plachtaa/VITS-fast-fine-tuning

Google Colab在线训练:https://colab.research.google.com/drive/1omMhfYKrAAQ7a6zOCsyqpla-wU-QyfZn?usp=sharing

在训练lab1的过程中尝试一下另一个微调模型

上传了三百条语音数据 不需要上传标注,程序会自动标注并进行数据的预处理(去BGM 噪音 分割)

进行了30个epoch 出来一股大佐味 训练时间很短 效果不太行

(大佐味是因为多语言混训的原因和预训练模型带大佐味的问题)

解决方案:

1.拉源码到本地 修改底模为标贝 再放到服务器上跑

2.尝试增加上传数据集 并增加epoch

  • 本文标题:vits语音合成
  • 本文作者:y4ny4n
  • 创建时间:2023-03-10 11:47:31
  • 本文链接:https://y4ny4n.cn/2023/03/10/vits/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!