栩栩如生,音色克隆,Bert-vits2文字转语音打造鬼畜视频实践(Python3.10)
诸公可知目前最牛逼的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)的更多相关文章
- C# ms speech文字转语音例子
最近突发奇想 想玩玩 文字转语音的东东 谷歌了一下 发现微软有一个TTS 的SDK 查了查相关资料 发现 还真不错 然后就开始玩玩Microsoft Speech SDK的 DEMO了 ...
- C# 语音识别(文字to语音、语音to文字)
最近打算研究一下语音识别,但是发现网上很少有C#的完整代码,就把自己的学习心得放上来,和大家分享一下. 下载API: 1)SpeechSDK51.exe (67.0 ...
- Android文字转语音
虽然视觉上的反馈通常是给用户提供信息最快的方式,但这要求用户把注意力设备上.当用户不能查看设备时,则需要一些其他通信的方法.Android提供了强大的文字转语音Text-to-Speech,TTS A ...
- Android实例-调用GOOGLE的TTS实现文字转语音(XE7+小米2)(无图)
注意:在手机上必须选安装文字转语音引擎“google Text To Speech”地址:http://www.shouji56.com/soft/GoogleWenZiZhuanYuYinYinQi ...
- iOS语音识别,语音播报,文字变语音播报,语音变文字
首先使用的是科大讯飞的sdk 1.语音识别部分 AppDelegate.m #import "AppDelegate.h" #import <iflyMSC/iflyMSC. ...
- 简单C#文字转语音
跟着微软走妥妥的,C#文字转语音有很多参数我就不说了,毕竟我也是初学者.跟大家分享最简单的方法,要好的效果得自己琢磨喽: 先添加引用System.Speech程序集: using System; us ...
- Android技术分享-文字转语音并朗读
Android技术分享-文字转语音并朗读 最近在做一个项目,其中有一个功能是需要将文本转换成语音并播放出来.下面我将我的做法分享一下. 非常令人开心的是,Android系统目前已经集成了TTS,提供了 ...
- TTS 文字转语音 ekho
1.源码下载 使用svn客户端,执行如下命令下载 svn co https://svn.code.sf.net/p/e-guidedog/code/ 2.官方网站查看说明 http://www.egu ...
- Android文字转语音引擎(TTS)使用
百度网盘下载地址 密码:3si0资源来源:https://blog.csdn.net/Sqq_yj/article/details/82460580?utm_source=blogxgwz4 简单比较 ...
- web端文字转语音的几种方案
最近在开发一个微信排队取号的的系统,其中对于服务员端(管理端) 需要有呼叫功能,即点按钮 就播出"xxx号顾客请就座"的声音. 经过在网上一番搜索研究,web端实现指定文字的语音播 ...
随机推荐
- 手把手教你自定义自己SpringBoot Starter组件源码剖析
我们知道SpringBoot Starter也就是启动器.是SpringBoot组件化的一大优点.基于这个思想,基于这个思想SpringBoot 才变得非常强大,官方给我们提供很多开箱即用的启动器. ...
- 【Shell】ps 命令
ps 命令 Linux ps (英文全拼:process status)命令用于显示当前进程的状态,类似于 windows 的任务管理器. 1. ps 常用的命令 ps -aux #显示所有进程信息 ...
- 【VUE】 文件预览
[VUE] 文件预览 上传前预览 word文档:docx.doc 核心代码 import {renderAsync} from "docx-preview"; /** * 渲染do ...
- Hexo博客yilia主题首页添加helper-live2d模型插件
插件效果 插件的github地址 插件作者提供了较为详细的安装步骤,我结合自己操作和图示,提供大家. 效果展示:红框内为2d模型,可以随鼠标移动而变化 安装模块: hexo博客根目录选择cmd命令窗口 ...
- 模拟ArrayList(顺序表)的底层实现
模拟ArrayLIst的底层实现 package com.tedu.api04.list; import java.util.Objects; /** * @author LIGENSEN * Dat ...
- 想在golang里用好泛型还挺难的
golang的泛型已经出来了一年多了,从提案被接受开始我就在关注泛型了,如今不管是在生产环境还是开源项目里我都写了不少泛型代码,是时候全面得回顾下golang泛型的使用体验了. 先说说结论,好用是好用 ...
- 仪酷LabVIEW AI视觉工具包及开放神经网络交互工具包常见问题解答
前言 哈喽,各位朋友,好久不见~ 之前给大家分享了基于LabVIEW开发的AI视觉工具包及开放神经网络交互工具包,不少朋友私信说在安装和使用过程中会遇到一些问题,今天我们就集中回复一下大家问到最多的问 ...
- Django: You are trying to add a non-nullable field 'name' to mainnav without a default; we can't do that (the database needs something to populate existing rows).
错误原因: 语句中缺少默认值 class Main(models.Model): img = models.CharField(max_length=255) name = models.CharFi ...
- Java中的线程池使用及原理
开篇-为什么要使用线程池? Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来 3 个好处. 第一:降低 ...
- 跟运维学 Linux - 03
权限机制和性能指标 前面我们学完了操作文件和用户相关知识,本篇学习权限和性能相关知识. 文件的属性看起 看 linux 的权限,先从文件的属性看起 ls -l 加 -d 是只看这个文件夹: pjl@p ...