克隆自己的声音——赛博分身必备技能

9cdcbb69c65c5599f0bb1ae067e19a71.gif

前言

语音合成的技术越来越成熟,大家已经不满足于传统千篇一律的 TTS,转而追求更多个性化的声音。复现整理了一下近期的几种主流实现方案,效果各有千秋,有些适合英语口吻有些适合中文语境,有些甚至模仿的惟妙惟肖,几乎能够乱真。给自己的视频配个音,或是在赛博空间里代替一下自己也毫无违和感了。

752c1ac7a0e76eb5bb92d8891d1997c0.jpeg

bark

bark 基于 hubert,采用 类似GPT 的结构,直接由文本生成语音,可以生成很多语气和情绪,也能通过标记加入不同的风格。自定义的语音需要的训练素材非常少,仅不到1分钟的语音就能模仿出相似的口吻。finetune 所需要的文件也很小,很方便通过 tts 库来加入自有的语音。

54749392f309fe95a20e3e84088c78e0.png
安装依赖
conda create -n bark python=3.10.0
activate bark

git clone https://github.com/suno-ai/bark
cd bark && pip install .
0ce39b4d33e5acacf12c8eebe79fa949.png

安装 Transformers 库

pip install git+https://github.com/huggingface/transformers.git
344ddea8597c8f1364bc375252299aa0.png

下载基础模型

新建一个目录 suno/bark,将以下链接的文件下载后放入其中(约20G左右,可在文末网盘中加速下载)

https://huggingface.co/suno/bark/tree/main

已有的音色库中,选择不同的语言和不同的人物风格

https://suno-ai.notion.site/8b8e8749ed514b0cbf3f699013548683?v=bc67cff786b04b50b3ceb756fd05f68c
c0a717c55c3cd1aa6e2befa741c92a93.png

在 voice_preset 中配置后,设置采样频率

from transformers import AutoProcessor, BarkModel
import scipy

processor = AutoProcessor.from_pretrained("./suno/bark")
model = BarkModel.from_pretrained("./suno/bark")

voice_preset = "v2/zh_speaker_4"

inputs = processor("克隆自己的声音,赛博分身必备技能", voice_preset=voice_preset)

audio_array = model.generate(**inputs)
audio_array = audio_array.cpu().numpy().squeeze()

sample_rate = model.generation_config.sample_rate
scipy.io.wavfile.write("bark_out.wav", rate=sample_rate, data=audio_array)

训练自有语音

训练自己的音色,我们需要引入另一个库来实现,TTS。由于bark架构的特殊性,只需要极少量的录音(1分钟左右)就能做到音色迁移。

安装 tts 库
https://github.com/coqui-ai/TTS
pip install TTS
96c9693d28bfc475f18397490c640b76.png
安装依赖
pip install ipython
faf94012f508060b63b7a25e0eda7ad4.png
生成音频

新建目录 bark_voices/speaker,在其中放入录制好的干净的原声,指定 speaker_id 为 speaker,voice_dirs 为 bark_voices

from TTS.tts.configs.bark_config import BarkConfig
from TTS.tts.models.bark import Bark
from scipy.io.wavfile import write as write_wav

# text = "克隆自己的声音,赛博分身必备技能。"  # Hello, my name is Manmay , how are you?
text = "Hello, my name is Manmay , how are you?"  # 

config = BarkConfig()
model = Bark.init_from_config(config)
model.load_checkpoint(config, checkpoint_dir="./suno/bark/", eval=True)

# with random speaker
# output_dict = model.synthesize(text, config, speaker_id="random", voice_dirs=None)

# cloning a speaker.
# It assumes that you have a speaker file in `bark_voices/speaker_n/speaker.wav` or `bark_voices/speaker_n/speaker.npz`
output_dict = model.synthesize(text, config, speaker_id="speaker", voice_dirs="bark_voices/")

sample_rate = 24000
write_wav("bark_out_c.wav", sample_rate, output_dict["wav"])

通过调整 BarkConfig 来配置基础模型的参数,bark_out_c.wav 就是最终新合成的音频。

https://huggingface.co/docs/transformers/main/en/model_doc/bark

由于基准模型多采用英语素材,采用其他语言的适配,会感觉一股老外的味道。要避免这种情况,则需要使用 CustomHuBERT 导入自己的预训练模型,进一步的资料可以参考:

https://github.com/gitmylo/bark-voice-cloning-HuBERT-quantizer/

不过预训练模型需要大量的基础语音素材,如果想省心省力的话,也可以考虑达摩院的 Sambert 模型。

Sambert

要说中文的个性化定制语音,最近达摩院在 KAN-TTS 基础上开源的 Sambert 模型,只需要录制20句话,经过几分钟的训练,就能够获得一个较好的个性化声音。

https://modelscope.cn/models/damo/speech_personal_sambert-hifigan_nsf_tts_zh-cn_pretrain_16k/summary
5949e30252a2d75ed05d4cfb899a26d4.png
安装依赖
sudo apt-get install ffmpeg
sudo apt-get install sox

要注意 pandas 和 numpy 的版本匹配,librosa 的 numpy 支持范围

pip install openai-whisper
pip install modelscope
pip install tts-autolabel -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html
pip install typeguard==2.3.1
pip install sox
pip install bitstring
pip install pysptk --no-build-isolation
pip install matplotlib
pip install kantts -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html
pip install pytorch_wavelets
pip install tensorboardX
pip install pandas==1.5.3

pip install librosa==0.10.0
pip install numpy==1.23.1

准备素材

这里我选用凯叔讲故事里西游记的一小段章节作为输入语音(大约时长 20 分钟)。

1a7cf4a23e646c524e229d67338ea493.png

先使用 whisper 模型来做长语音分割,将素材切割成 486 段短语音,存入 test_wavs 目录备用。

4907aac5d814d44d9ac53e8bfb899d92.png

import whisper
from pathlib import Path
import librosa
from scipy.io import wavfile
import numpy as np

import sox

whisper_size = "large"
whisper_model = whisper.load_model(whisper_size)

def split_long_audio(model, filepaths, save_dir="data_dir", out_sr=44100):
    if isinstance(filepaths, str):
        filepaths = [filepaths]

    for file_idx, filepath in enumerate(filepaths):

        save_path = Path(save_dir)
        save_path.mkdir(exist_ok=True, parents=True)

        print(f"Transcribing file {file_idx}: '{filepath}' to segments...")
        result = model.transcribe(filepath, word_timestamps=True, task="transcribe", beam_size=5, best_of=5)
        segments = result['segments']

        wav, sr = librosa.load(filepath, sr=None, offset=0, duration=None, mono=True)
        wav, _ = librosa.effects.trim(wav, top_db=20)
        peak = np.abs(wav).max()
        if peak > 1.0:
            wav = 0.98 * wav / peak
        wav2 = librosa.resample(wav, orig_sr=sr, target_sr=out_sr)
        wav2 /= max(wav2.max(), -wav2.min())

        for i, seg in enumerate(segments):
            start_time = seg['start']
            end_time = seg['end']
            wav_seg = wav2[int(start_time * out_sr):int(end_time * out_sr)]
            wav_seg_name = f"{file_idx}_{i}.wav"
            out_fpath = save_path / wav_seg_name
            wavfile.write(out_fpath, rate=out_sr, data=(wav_seg * np.iinfo(np.int16).max).astype(np.int16))

split_long_audio(whisper_model, "001石猴出世.mp3", "test_wavs")

自动标注

采用魔搭的自动标注工具,对原始数据预处理,期间工具会过滤掉背景声音,并将静音的片段删除,标注完成后的数据保存在 output_training_data 下,待训练。

https://modelscope.cn/models/damo/speech_ptts_autolabel_16k/summary
from modelscope.tools import run_auto_label
import os

os.makedirs("output_training_data", exist_ok=True)

input_wav = "./test_wavs/"
output_data = "./output_training_data/"
ret, report = run_auto_label(input_wav=input_wav, work_dir=output_data, resource_revision="v1.0.7")
eb055089e01782ab46f12f02797ac65f.png

训练模型

万事俱备,指定 sambert 模型的超参数后,就能开始训练了,11g以上的显卡应该都能顺利完成任务。

67ef4dd2e5cbc155db186f6aa2744228.jpeg
from modelscope.metainfo import Trainers
from modelscope.trainers import build_trainer
from modelscope.utils.audio.audio_utils import TtsTrainType

pretrained_model_id = 'damo/speech_personal_sambert-hifigan_nsf_tts_zh-cn_pretrain_16k'

dataset_id = "./output_training_data/"
pretrain_work_dir = "./pretrain_work_dir/"

os.makedirs("pretrain_work_dir", exist_ok=True)

# 训练信息,用于指定需要训练哪个或哪些模型,这里展示AM和Vocoder模型皆进行训练
# 目前支持训练:TtsTrainType.TRAIN_TYPE_SAMBERT, TtsTrainType.TRAIN_TYPE_VOC
# 训练SAMBERT会以模型最新step作为基础进行finetune
train_info = {
    TtsTrainType.TRAIN_TYPE_SAMBERT: {  # 配置训练AM(sambert)模型
        'train_steps': 202,               # 训练多少个step
        'save_interval_steps': 200,       # 每训练多少个step保存一次checkpoint
        'log_interval': 10               # 每训练多少个step打印一次训练日志
    }
}

# 配置训练参数,指定数据集,临时工作目录和train_info
kwargs = dict(
    model=pretrained_model_id,                  # 指定要finetune的模型
    model_revision = "v1.0.6",
    work_dir=pretrain_work_dir,                 # 指定临时工作目录
    train_dataset=dataset_id,                   # 指定数据集id
    train_type=train_info                       # 指定要训练类型及参数
)

trainer = build_trainer(Trainers.speech_kantts_trainer,
                        default_args=kwargs)

trainer.train()
2fc9c1bcf85733f43a197e4a7870f561.png

合成效果

from modelscope.models.audio.tts import SambertHifigan
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks

def infer(text):

  model_dir = os.path.abspath("./pretrain_work_dir")

  custom_infer_abs = {
      'voice_name':
      'F7',
      'am_ckpt':
      os.path.join(model_dir, 'tmp_am', 'ckpt'),
      'am_config':
      os.path.join(model_dir, 'tmp_am', 'config.yaml'),
      'voc_ckpt':
      os.path.join(model_dir, 'orig_model', 'basemodel_16k', 'hifigan', 'ckpt'),
      'voc_config':
      os.path.join(model_dir, 'orig_model', 'basemodel_16k', 'hifigan',
              'config.yaml'),
      'audio_config':
      os.path.join(model_dir, 'data', 'audio_config.yaml'),
      'se_file':
      os.path.join(model_dir, 'data', 'se', 'se.npy')
  }
  kwargs = {'custom_ckpt': custom_infer_abs}

  model_id = SambertHifigan(os.path.join(model_dir, "orig_model"), **kwargs)

  inference = pipeline(task=Tasks.text_to_speech, model=model_id)
  output = inference(input=text)

  filename = text + ".wav"

  with open(filename, mode='bx') as f:
      f.write(output["output_wav"])
  return filename


infer("克隆自己的声音,赛博分身必备技能")

听一下合成的语音,果然有凯叔那味儿了,KAN-TTS 在中文语音迁移上的效果已经很不错。

8fe2837402fabba468c24e2a9a3d5a58.png

VITS

原始语音素材的质量是模型能否提取特征的关键,某种程度上来说,预处理的好坏决定了后续语音合成的效果。

接下来,我们另一个语音合成神器 VITS,底模采用原神数据集和VCTK数据集,支持中日英三种语言,所以非常适合亚洲人的声线。

https://github.com/Plachtaa/VITS-fast-fine-tuning
安装依赖

由于环境的版本依赖,建议新建一个虚拟环境,并在 python3.8 下运行。其中 pyopenjtalk 我在 0.3.2 版本下复现成功,如遇到问题,可以参考我的 requirements_vits.txt 文件。

git clone https://github.com/Plachtaa/VITS-fast-fine-tuning.git VITS

conda create -n vits python=3.8
conda activate vits

pip install pyopenjtalk==0.3.2
pip install -r requirements.txt (修改 pyopenjtalk 版本)
e80aea69d175afd321d519700966b267.png

下载中文预训练模型

预训练模型非常的大,还需要墙外访问,有需求的朋友也可以在文末我的网盘中加速下载。

wget https://huggingface.co/datasets/Plachta/sampled_audio4ft/resolve/main/VITS-Chinese/D_0.pth -O ./pretrained_models/D_0.pth
wget https://huggingface.co/datasets/Plachta/sampled_audio4ft/resolve/main/VITS-Chinese/G_0.pth -O ./pretrained_models/G_0.pth
wget https://huggingface.co/datasets/Plachta/sampled_audio4ft/resolve/main/VITS-Chinese/config.json -O ./configs/finetune_speaker.json

下载原始素材

# download data for fine-tuning
wget https://huggingface.co/datasets/Plachta/sampled_audio4ft/resolve/main/sampled_audio4ft_v2.zip
unzip sampled_audio4ft_v2.zip

音频预处理

mkdir video_data
mkdir raw_audio
mkdir denoised_audio
mkdir custom_character_voice
mkdir segmented_character_voice

把原始音频放入 raw_audio 目录,需要 wav 格式,mp3的话先用 ffmpeg 转换一下。

ffmpeg -i "001石猴出世 凯叔.mp3" -acodec pcm_s16le -ac 1 -ar 24000 kai_input.wav

降噪处理,采用 demucs 移除背景声

python scripts/denoise_audio.py
9946fd5aa119b36e7842932a89efe51d.png

使用 whisper 将长音频分割为短音频,这里和前文 Sambert 的处理方式类似

python scripts/long_audio_transcribe.py --languages C --whisper_size large
93cb49538c6438c71fbe638bdc71d729.png bca5d8ccb065c27e283fcb0963106374.png

处理训练数据集

其中 add_auxiliary_data 则加入之前下载的 sampled_audio4ft_v2.zip 的素材进行 finetune 训练,可以避免新数据的影响过大。

python preprocess_v2.py --add_auxiliary_data True --languages C
2c1732f1d198b3ad76eb5f6e8a11e393.png

训练模型

由于我们输入的音频文件比较长,训练 200 个 epoch 之后,模型仍未过拟合,直接选用 G_latest.pth 就行。

python finetune_speaker_v2.py -m ./OUTPUT_MODEL --max_epochs 200 --drop_speaker_embed True
cbfa0977d779d5f08bdecc56fa45d124.png

生成音频

试听一下效果,完美!

python VC_inference.py --model_dir ./OUTPUT_MODEL/G_latest.pth --config_dir ./configs/modified_finetune_speaker.json --share True
e2704e9bc6931a36354604b5e771bc07.png

最终训练的结果都在 OUTPUT_MODEL 目录下,依据最小的g_losss保留最佳的权重文件即可。期间产生的临时文件,可以根据情况自行删除。

rm -rf ./custom_character_voice/* ./video_data/* ./raw_audio/* ./denoised_audio/* ./segmented_character_voice/* ./separated/* long_character_anno.txt short_character_anno.txt

另外除了文本到音频,还能直接采用语音到语音的方式,那么我们顺便试试替换歌手的效果。

RVC

864ec027506f900a3f309dc5fad98bd1.png
安装依赖
conda create -n chat python=3.10.0
activate chat

git clone https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI.git RVC
sudo apt install build-essential
pip install -r requirements.txt

预训练模型

下载预训练模型,由于我是N卡,直接下载 RVC0813Nvidia.7z 包含所有文件(也可在文末网盘下载)。

https://huggingface.co/lj1995/VoiceConversionWebUI/tree/main

将 hubert_base.pt 放置到 ./assets/hubert/ 下;

将基础模型放置到 ./assets/pretrained_v2 目录下;

将人声分离模型放置到 ./assets/uvr5_weights 目录下;

将人声音高提取模型放置到 ./assets/rmvpe 目录下。

准备素材

谈到让流行歌曲更换歌手的场景,我们首先要对歌曲做的预处理就是人声分离。我们选用王菲的《匆匆那年》来做测试,将 匆匆那年.mp3 放置到 ./song/ 目录下,先运行

python infer-web.py
b44f63ae77432c77118cd10a008452ee.png

人声分离

配置待处理音频文件夹和分割模型,内置的HP2兼容性比较好,其他的模型参考介绍即可。更细致的模型,可以参考后续番外篇 UVR 章节。

cea41e6f10a321fe7eb07bfb759bc588.png

顺利的话,在 opt 目录下,就能找到分割好的人声和旋律。


2a073180285d0c419c718dab613585fe.png

预处理目标语音

配置输入实验名,将凯叔的语音放入 ./assets/speaker/uncle_kai 目录下

50e4ba9672622e5b1dc7e6d91f0f5271.png

提取特征

使用 rmvpe_gpu 提取特征

48d237cd9ce49b0491ae774fc5a7cb8e.png

训练模型

设置超参数,训练 200 个 epoch,batch_size 为 40(RTX3090),显存小的可以相应改小些。

978a5ab12d11b53466e036143822702d.png

耐心等待一会儿,就能在 tensorboard 中看到整个训练过程

tensorboard --logdir=./logs/uncle_kai
943bb3e1bd007388ffe1d3245441c99d.png

训练完的权重文件 ./assets/weights/uncle_kai.pth

合成歌曲

切换到模型推理页,我们选择刚刚训练好的 uncle_kai.pth 模型;

由于凯叔的声音比较低沉,而王菲的女声比较高,这里设置变调 -20;

待处理的音频,选择刚刚分离的纯人声文件,./opt/vocal_匆匆那年.mp3_10.wav;

index 路径在 logs 目录下,点击转换合成歌曲。

f67e867612b6df037ca994e1df1f7d22.png

下载合成好的人声(凯叔版本),最后我们可以使用 Audacity 来合成人声和旋律(instrument_匆匆那年.mp3)

https://www.audacityteam.org/download/
088949e10a1907797ecfebf22bac6610.png

好了,男低音版本的凯叔匆匆那年有那味儿了,五音不全的朋友们也能自信地高歌起来啦。

番外篇 UVR

RVC 内置的人声分离模型比较简单,遇到有和声的歌曲则没法处理。要取得更好的效果,可以专门采用 UVR 来进一步优化。

git clone https://github.com/Anjok07/ultimatevocalremovergui.git UVR

下载模型,解压到 models 目录下

https://github.com/Anjok07/ultimatevocalremovergui/releases
e4d5f828885d0264f044eb83092e920e.png 26458cf6c799bfe4e3c31add1da687c0.png

匆匆那年_(Instrumental).mp3 为纯伴奏,匆匆那年_(Vocals) 为人声, 至此以后再也不用辛苦的找寻伴奏带了,这简直是卡拉OK爱好者的福音啊。

另外,懒得部署模型,也可以试试免费在线应用

https://vocalremover.org/

总结

本篇介绍了 Bark,Sambert,VITS 三种不同语音克隆的技术实现,并在 RVC(基于 VITS) 上克隆了歌曲。

整理一下整体思路,先将原始音频去噪,然后做人声分离,再将长音频进行标注,顺便分割为多段短音频,准备好原始训练素材;然后接入神经网络提取音频特征训练模型;最后将训练好的模型通过文本或语音(歌曲)渲染成特定的音色。

d2a78950d27e5cfafc1cc6e9e4e9d445.png

一直纠结是否要出一篇完整教程来介绍一下声音克隆项目,反复斟酌了好久。如果别有用心之人盗用了自己的声音,那不就间接做了电信诈骗的帮凶么?和朋友们深入地讨论后,仔细想来,恐怖分子都曾是技术专家,还是应该让更多人知道当前技术能实现的与应对策略,才会相对更安全。

一个有效的措施是,提前与家中老人孩子约定“密钥”,在电话中诉说重大事情的时候,对一下暗语,用以确定传递信息的真实性。

克隆声音对于社恐来说是一种福利,有效的解决了视频配音和直播的场景,加上翻译功能的加持,同声传译50种语言也不再是梦,而在动画人物和名人的模仿中,普通人更是可以轻松成为一个优秀的声优,会有很多的乐趣,科技点亮生活。

不多说了,批量导入喜爱的流行歌曲去除人声,进入K歌模式了!

源码下载

c2d57ebcb62ba9b492b08cbbc0903587.png

本期相关文件资料,可在公众号“深度觉醒”,后台回复:“voice01”,获取下载链接。涉及5个项目,推理的权重文件比较大,一共30个G左右,可以进入相应的目录按需下载。

8b792caf91a7f527aa227633e64f3c7e.gif

猜你喜欢

转载自blog.csdn.net/weixin_47479625/article/details/133004391
今日推荐