诸公可知目前最牛逼的TTS免费开源项目是哪一个?没错,是Bert-vits2,没有之一。它是在本来已经极其强大的Vits项目中融入了Bert大模型,基本上解决了VITS的语气韵律问题,在效果非常出色的情况下训练的成本开销普通人也完全可以接受。

BERT的核心思想是通过在大规模文本语料上进行无监督预训练,学习到通用的语言表示,然后将这些表示用于下游任务的微调。相比传统的基于词嵌入的模型,BERT引入了双向上下文信息的建模,使得模型能够更好地理解句子中的语义和关系。

BERT的模型结构基于Transformer,它由多个编码器层组成。每个编码器层都有多头自注意力机制和前馈神经网络,用于对输入序列进行多层次的特征提取和表示学习。在预训练阶段,BERT使用了两种任务来学习语言表示:掩码语言模型(Masked Language Model,MLM)和下一句预测(Next Sentence Prediction,NSP)。通过这两种任务,BERT能够学习到上下文感知的词嵌入和句子级别的语义表示。

在实际应用中,BERT的预训练模型可以用于各种下游任务,如文本分类、命名实体识别、问答系统等。通过微调预训练模型,可以在特定任务上取得更好的性能,而无需从头开始训练模型。

BERT的出现对自然语言处理领域带来了重大影响,成为了许多最新研究和应用的基础。它在多个任务上取得了领先的性能,并促进了自然语言理解的发展。

本次让我们基于Bert-vits2项目来克隆渣渣辉和刘青云的声音,打造一款时下热搜榜一的“青岛啤酒”鬼畜视频。

语音素材和模型

首先我们需要渣渣辉和刘青云的原版音频素材,原版《扫毒》素材可以参考:https://www.bilibili.com/video/BV1R64y1F7SQ/。

将两个主角的声音单独提取出来,随后依次进行背景音和前景音的分离,声音降噪以及声音切片等操作,这些步骤之前已经做过详细介绍,请参见:民谣女神唱流行,基于AI人工智能so-vits库训练自己的音色模型(叶蓓/Python3.10)。 囿于篇幅,这里不再赘述。

做好素材的简单处理后,我们来克隆项目:

git clone https://github.com/Stardust-minus/Bert-VITS2

随后安装项目的依赖:

cd Bert-VITS2  

pip3 install -r requirements.txt

接着下载bert模型放入到项目的bert目录。

bert模型下载地址:

中:https://huggingface.co/hfl/chinese-roberta-wwm-ext-large
日:https://huggingface.co/cl-tohoku/bert-base-japanese-v3/tree/main

语音标注

接着我们需要对已经切好分片的语音进行标注,这里我们使用开源库whisper,关于whisper请移步:闻其声而知雅意,M1 Mac基于PyTorch(mps/cpu/cuda)的人工智能AI本地语音识别库Whisper(Python3.10)

编写标注代码:

import whisper
import os
import json
import torchaudio
import argparse
import torch lang2token = {
'zh': "ZH|",
'ja': "JP|",
"en": "EN|",
}
def transcribe_one(audio_path):
# load audio and pad/trim it to fit 30 seconds
audio = whisper.load_audio(audio_path)
audio = whisper.pad_or_trim(audio) # make log-Mel spectrogram and move to the same device as the model
mel = whisper.log_mel_spectrogram(audio).to(model.device) # detect the spoken language
_, probs = model.detect_language(mel)
print(f"Detected language: {max(probs, key=probs.get)}")
lang = max(probs, key=probs.get)
# decode the audio
options = whisper.DecodingOptions(beam_size=5)
result = whisper.decode(model, mel, options) # print the recognized text
print(result.text)
return lang, result.text
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--languages", default="CJ")
parser.add_argument("--whisper_size", default="medium")
args = parser.parse_args()
if args.languages == "CJE":
lang2token = {
'zh': "ZH|",
'ja': "JP|",
"en": "EN|",
}
elif args.languages == "CJ":
lang2token = {
'zh': "ZH|",
'ja': "JP|",
}
elif args.languages == "C":
lang2token = {
'zh': "ZH|",
}
assert (torch.cuda.is_available()), "Please enable GPU in order to run Whisper!"
model = whisper.load_model(args.whisper_size)
parent_dir = "./custom_character_voice/"
speaker_names = list(os.walk(parent_dir))[0][1]
speaker_annos = []
total_files = sum([len(files) for r, d, files in os.walk(parent_dir)])
# resample audios
# 2023/4/21: Get the target sampling rate
with open("./configs/config.json", 'r', encoding='utf-8') as f:
hps = json.load(f)
target_sr = hps['data']['sampling_rate']
processed_files = 0
for speaker in speaker_names:
for i, wavfile in enumerate(list(os.walk(parent_dir + speaker))[0][2]):
# try to load file as audio
if wavfile.startswith("processed_"):
continue
try:
wav, sr = torchaudio.load(parent_dir + speaker + "/" + wavfile, frame_offset=0, num_frames=-1, normalize=True,
channels_first=True)
wav = wav.mean(dim=0).unsqueeze(0)
if sr != target_sr:
wav = torchaudio.transforms.Resample(orig_freq=sr, new_freq=target_sr)(wav)
if wav.shape[1] / sr > 20:
print(f"{wavfile} too long, ignoring\n")
save_path = parent_dir + speaker + "/" + f"processed_{i}.wav"
torchaudio.save(save_path, wav, target_sr, channels_first=True)
# transcribe text
lang, text = transcribe_one(save_path)
if lang not in list(lang2token.keys()):
print(f"{lang} not supported, ignoring\n")
continue
#text = "ZH|" + text + "\n"
text = lang2token[lang] + text + "\n"
speaker_annos.append(save_path + "|" + speaker + "|" + text) processed_files += 1
print(f"Processed: {processed_files}/{total_files}")
except:
continue

标注后,会生成切片语音对应文件:

./genshin_dataset/ying/vo_dialog_DPEQ003_raidenEi_01.wav|ying|ZH|神子…臣民对我的畏惧…
./genshin_dataset/ying/vo_dialog_DPEQ003_raidenEi_02.wav|ying|ZH|我不会那么做…
./genshin_dataset/ying/vo_dialog_SGLQ002_raidenEi_01.wav|ying|ZH|不用着急,好好挑选吧,我就在这里等着。
./genshin_dataset/ying/vo_dialog_SGLQ003_raidenEi_01.wav|ying|ZH|现在在做的事就是「留影」…
./genshin_dataset/ying/vo_dialog_SGLQ003_raidenEi_02.wav|ying|ZH|嗯,不错,又学到新东西了。快开始吧。

说白了,就是通过whisper把人物说的话先转成文字,并且生成对应的音标:

./genshin_dataset/ying/vo_dialog_DPEQ003_raidenEi_01.wav|ying|ZH|神子…臣民对我的畏惧…|_ sh en z i0 … ch en m in d ui w o d e w ei j v … _|0 2 2 5 5 0 2 2 2 2 4 4 3 3 5 5 4 4 4 4 0 0|1 2 2 1 2 2 2 2 2 2 2 1 1
./genshin_dataset/ying/vo_dialog_DPEQ003_raidenEi_02.wav|ying|ZH|我不会那么做…|_ w o b u h ui n a m e z uo … _|0 3 3 2 2 4 4 4 4 5 5 4 4 0 0|1 2 2 2 2 2 2 1 1
./genshin_dataset/ying/vo_dialog_SGLQ002_raidenEi_01.wav|ying|ZH|不用着急,好好挑选吧,我就在这里等着.|_ b u y ong zh ao j i , h ao h ao t iao x van b a , w o j iu z ai zh e l i d eng zh e . _|0 2 2 4 4 2 2 2 2 0 2 2 3 3 1 1 3 3 5 5 0 3 3 4 4 4 4 4 4 3 3 3 3 5 5 0 0|1 2 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 2 2 1 1
./genshin_dataset/ying/vo_dialog_SGLQ003_raidenEi_01.wav|ying|ZH|现在在做的事就是'留影'…|_ x ian z ai z ai z uo d e sh ir j iu sh ir ' l iu y ing ' … _|0 4 4 4 4 4 4 4 4 5 5 4 4 4 4 4 4 0 2 2 3 3 0 0 0|1 2 2 2 2 2 2 2 2 1 2 2 1 1 1
./genshin_dataset/ying/vo_dialog_SGLQ003_raidenEi_02.wav|ying|ZH|恩,不错,又学到新东西了.快开始吧.|_ EE en , b u c uo , y ou x ve d ao x in d ong x i l e . k uai k ai sh ir b a

最后,将标注好的文件转换为bert模型可读文件:

import torch
from multiprocessing import Pool
import commons
import utils
from tqdm import tqdm
from text import cleaned_text_to_sequence, get_bert
import argparse
import torch.multiprocessing as mp def process_line(line):
rank = mp.current_process()._identity
rank = rank[0] if len(rank) > 0 else 0
if torch.cuda.is_available():
gpu_id = rank % torch.cuda.device_count()
device = torch.device(f"cuda:{gpu_id}")
wav_path, _, language_str, text, phones, tone, word2ph = line.strip().split("|")
phone = phones.split(" ")
tone = [int(i) for i in tone.split(" ")]
word2ph = [int(i) for i in word2ph.split(" ")]
word2ph = [i for i in word2ph]
phone, tone, language = cleaned_text_to_sequence(phone, tone, language_str) phone = commons.intersperse(phone, 0)
tone = commons.intersperse(tone, 0)
language = commons.intersperse(language, 0)
for i in range(len(word2ph)):
word2ph[i] = word2ph[i] * 2
word2ph[0] += 1 bert_path = wav_path.replace(".wav", ".bert.pt") try:
bert = torch.load(bert_path)
assert bert.shape[-1] == len(phone)
except Exception:
bert = get_bert(text, word2ph, language_str, device)
assert bert.shape[-1] == len(phone)
torch.save(bert, bert_path)

模型训练

此时,打开项目目录中的config.json文件:

{
"train": {
"log_interval": 100,
"eval_interval": 100,
"seed": 52,
"epochs": 200,
"learning_rate": 0.0001,
"betas": [
0.8,
0.99
],
"eps": 1e-09,
"batch_size": 4,
"fp16_run": false,
"lr_decay": 0.999875,
"segment_size": 16384,
"init_lr_ratio": 1,
"warmup_epochs": 0,
"c_mel": 45,
"c_kl": 1.0,
"skip_optimizer": true
},
"data": {
"training_files": "filelists/train.list",
"validation_files": "filelists/val.list",
"max_wav_value": 32768.0,
"sampling_rate": 44100,
"filter_length": 2048,
"hop_length": 512,
"win_length": 2048,
"n_mel_channels": 128,
"mel_fmin": 0.0,
"mel_fmax": null,
"add_blank": true,
"n_speakers": 1,
"cleaned_text": true,
"spk2id": {
"ying": 0
}
},
"model": {
"use_spk_conditioned_encoder": true,
"use_noise_scaled_mas": true,
"use_mel_posterior_encoder": false,
"use_duration_discriminator": true,
"inter_channels": 192,
"hidden_channels": 192,
"filter_channels": 768,
"n_heads": 2,
"n_layers": 6,
"kernel_size": 3,
"p_dropout": 0.1,
"resblock": "1",
"resblock_kernel_sizes": [
3,
7,
11
],
"resblock_dilation_sizes": [
[
1,
3,
5
],
[
1,
3,
5
],
[
1,
3,
5
]
],
"upsample_rates": [
8,
8,
2,
2,
2
],
"upsample_initial_channel": 512,
"upsample_kernel_sizes": [
16,
16,
8,
2,
2
],
"n_layers_q": 3,
"use_spectral_norm": false,
"gin_channels": 256
}
}

这里需要修改的参数是batch_size,通常情况下,数值和本地显存应该是一致的,但是最好还是改小一点,比如说一块4060的8G卡,最好batch_size是4,如果写8的话,还是有几率爆显存。

随后开始训练:

python3 train_ms.py

程序返回:

[W C:\actions-runner\_work\pytorch\pytorch\builder\windows\pytorch\torch\csrc\distributed\c10d\socket.cpp:601] [c10d] The client socket has failed to connect to [v3u.net]:65280 (system error: 10049 - 在其上下文中,该请求的地址无效。).
[W C:\actions-runner\_work\pytorch\pytorch\builder\windows\pytorch\torch\csrc\distributed\c10d\socket.cpp:601] [c10d] The client socket has failed to connect to [v3u.net]:65280 (system error: 10049 - 在其上下文中,该请求的地址无效。).
2023-10-23 15:36:08.293 | INFO | data_utils:_filter:61 - Init dataset...
100%|█████████████████████████████████████████████████████████████████████████████| 562/562 [00:00<00:00, 14706.57it/s]
2023-10-23 15:36:08.332 | INFO | data_utils:_filter:76 - skipped: 0, total: 562
2023-10-23 15:36:08.333 | INFO | data_utils:_filter:61 - Init dataset...
100%|████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:00<?, ?it/s]
2023-10-23 15:36:08.334 | INFO | data_utils:_filter:76 - skipped: 0, total: 4
Using noise scaled MAS for VITS2
Using duration discriminator for VITS2
INFO:OUTPUT_MODEL:Loaded checkpoint './logs\OUTPUT_MODEL\DUR_4600.pth' (iteration 33)
INFO:OUTPUT_MODEL:Loaded checkpoint './logs\OUTPUT_MODEL\G_4600.pth' (iteration 33)
INFO:OUTPUT_MODEL:Loaded checkpoint './logs\OUTPUT_MODEL\D_4600.pth' (iteration 33)

说明没有问题,训练日志存放在项目的logs目录下。

随后可以通过tensorboard来监控训练过程:

python3 -m tensorboard.main --logdir=logs\OUTPUT_MODEL

当loss趋于稳定说明模型已经收敛:

模型推理

最后,我们就可以使用模型来生成我们想要听到的语音了:

python3 webui.py -m ./logs\OUTPUT_MODEL\G_47700.pth

注意参数为训练好的迭代模型,如果觉得当前迭代的模型可用,那么直接把pth和config.json拷贝出来即可,随后可以接着训练下一个模型。

结语

基于Bert-vits2打造的渣渣辉和刘青云音色的鬼畜视频已经上线到Youtube(B站),请检索:刘悦的技术博客,欢迎诸君品鉴和臻赏。

栩栩如生,音色克隆,Bert-vits2文字转语音打造鬼畜视频实践(Python3.10)的更多相关文章

  1. C# ms speech文字转语音例子

    最近突发奇想 想玩玩  文字转语音的东东   谷歌了一下 发现微软有一个TTS 的SDK   查了查相关资料  发现 还真不错  然后就开始玩玩Microsoft Speech SDK的 DEMO了 ...

  2. C# 语音识别(文字to语音、语音to文字)

    最近打算研究一下语音识别,但是发现网上很少有C#的完整代码,就把自己的学习心得放上来,和大家分享一下. 下载API: 1)SpeechSDK51.exe                   (67.0 ...

  3. Android文字转语音

    虽然视觉上的反馈通常是给用户提供信息最快的方式,但这要求用户把注意力设备上.当用户不能查看设备时,则需要一些其他通信的方法.Android提供了强大的文字转语音Text-to-Speech,TTS A ...

  4. Android实例-调用GOOGLE的TTS实现文字转语音(XE7+小米2)(无图)

    注意:在手机上必须选安装文字转语音引擎“google Text To Speech”地址:http://www.shouji56.com/soft/GoogleWenZiZhuanYuYinYinQi ...

  5. iOS语音识别,语音播报,文字变语音播报,语音变文字

    首先使用的是科大讯飞的sdk 1.语音识别部分 AppDelegate.m #import "AppDelegate.h" #import <iflyMSC/iflyMSC. ...

  6. 简单C#文字转语音

    跟着微软走妥妥的,C#文字转语音有很多参数我就不说了,毕竟我也是初学者.跟大家分享最简单的方法,要好的效果得自己琢磨喽: 先添加引用System.Speech程序集: using System; us ...

  7. Android技术分享-文字转语音并朗读

    Android技术分享-文字转语音并朗读 最近在做一个项目,其中有一个功能是需要将文本转换成语音并播放出来.下面我将我的做法分享一下. 非常令人开心的是,Android系统目前已经集成了TTS,提供了 ...

  8. TTS 文字转语音 ekho

    1.源码下载 使用svn客户端,执行如下命令下载 svn co https://svn.code.sf.net/p/e-guidedog/code/ 2.官方网站查看说明 http://www.egu ...

  9. Android文字转语音引擎(TTS)使用

    百度网盘下载地址 密码:3si0资源来源:https://blog.csdn.net/Sqq_yj/article/details/82460580?utm_source=blogxgwz4 简单比较 ...

  10. web端文字转语音的几种方案

    最近在开发一个微信排队取号的的系统,其中对于服务员端(管理端) 需要有呼叫功能,即点按钮 就播出"xxx号顾客请就座"的声音. 经过在网上一番搜索研究,web端实现指定文字的语音播 ...

随机推荐

  1. pip install lxml 总是失败

  2. Js中几种循环的使用

    在JavaScript中有五种常用的循环,现在来分别介绍一下五种循环的用法. 1.while 当满足条件时进入循环,进入循环后,当条件不满足时,跳出循环.while语句的一般表达式为:while(表达 ...

  3. Mac装机必备软件2023

        码农一枚,Mac作为生产力工具已经有10多年了. 用Mac的原因除了系统清爽,逼格高之外,最主要还是因为作为一个资深全栈,要做Apple相关开发,必须用MacOS系统. 与Windows不同, ...

  4. django执行makemigrations报AttributeError: 'str' object has no attribute 'decode'

    顺着报错文件点进去,找到query = query.decode(errors='replace')将decode修改为encode即可.

  5. Vue3 路由优化,使页面初次渲染效率翻倍

    3996 条路由? addRoute函数用了大约1s才执行完毕.通过观察,发现居然有3996条路由记录. 可是项目并没有这么多的页面啊~ 重复路由 let routes: Array<Route ...

  6. 一文理解GIT的代码冲突

    对于GIT,不知道有没有人和我一样,很长时间都是小心翼翼.紧张兮兮,生怕一不小心,自己辛苦写的代码没了. 特别是代码冲突,更是难到我无法理解,每次都要求助于百度,跟着人家的教程一步步解决,下一次还是这 ...

  7. Linux下后台运行Java程序

    1.背景描述 用Java编写了一个程序(可执行的jar),需要在Linux中启动并持续运行 1.1.直接执行程序 直接执行程序后,在程序执行期间,无法在当前会话中再执行其他操作 1.2.直接执行程序后 ...

  8. [ABC150E] Change a Little Bit

    2023-03-10 题目 题目传送门 翻译 翻译 难度&重要性(1~10):7 题目来源 AtCoder 题目算法 数学,贪心 解题思路 显然 \(C_i\) 越小的位越早被修改越好.所以我 ...

  9. 《SQL与数据库基础》08. 多表查询

    目录 多表查询 多表关系 一对多 多对多 一对一 多表查询概述 分类 内连接 外连接 自连接 联合查询 子查询 分类 标量子查询 列子查询 行子查询 表子查询 案例 本文以 MySQL 为例 多表查询 ...

  10. QEMU tap数据接收流程

    QEMU直接从tap/tun取数据 QEMU tap数据接收步骤: qemu从tun取数据包 qemu将数据包放入virtio硬件网卡. qemu触发中断. 虚拟机收到中断,从virtio读取数据. ...