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


    dcef7e3015deee9de71aaee938411625.gif

    前言

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

    5cf7fea34af83e020a073348ec3ffa61.jpeg

    bark

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

    ebd70166e4a69985f3c99b28ed59af56.png
    安装依赖
    1. conda create -n bark python=3.10.0
    2. activate bark
    3. git clone https://github.com/suno-ai/bark
    4. cd bark && pip install .
    89b49de9472e18319a08b632bdbe0ec7.png

    安装 Transformers 库

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

    下载基础模型

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

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

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

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

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

    1. from transformers import AutoProcessor, BarkModel
    2. import scipy
    3. processor = AutoProcessor.from_pretrained("./suno/bark")
    4. model = BarkModel.from_pretrained("./suno/bark")
    5. voice_preset = "v2/zh_speaker_4"
    6. inputs = processor("克隆自己的声音,赛博分身必备技能", voice_preset=voice_preset)
    7. audio_array = model.generate(**inputs)
    8. audio_array = audio_array.cpu().numpy().squeeze()
    9. sample_rate = model.generation_config.sample_rate
    10. 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
    becd5210868403e0d87f154063c29dba.png
    安装依赖
    pip install ipython
    ff398bc335a4478ff11f5e492ddc3b6c.png
    生成音频

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

    1. from TTS.tts.configs.bark_config import BarkConfig
    2. from TTS.tts.models.bark import Bark
    3. from scipy.io.wavfile import write as write_wav
    4. # text = "克隆自己的声音,赛博分身必备技能。" # Hello, my name is Manmay , how are you?
    5. text = "Hello, my name is Manmay , how are you?" #
    6. config = BarkConfig()
    7. model = Bark.init_from_config(config)
    8. model.load_checkpoint(config, checkpoint_dir="./suno/bark/", eval=True)
    9. # with random speaker
    10. # output_dict = model.synthesize(text, config, speaker_id="random", voice_dirs=None)
    11. # cloning a speaker.
    12. # It assumes that you have a speaker file in `bark_voices/speaker_n/speaker.wav` or `bark_voices/speaker_n/speaker.npz`
    13. output_dict = model.synthesize(text, config, speaker_id="speaker", voice_dirs="bark_voices/")
    14. sample_rate = 24000
    15. 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
    c847b685f128be73a294d7524dfbeced.png
    安装依赖
    1. sudo apt-get install ffmpeg
    2. sudo apt-get install sox

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

    1. pip install openai-whisper
    2. pip install modelscope
    3. pip install tts-autolabel -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html
    4. pip install typeguard==2.3.1
    5. pip install sox
    6. pip install bitstring
    7. pip install pysptk --no-build-isolation
    8. pip install matplotlib
    9. pip install kantts -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html
    10. pip install pytorch_wavelets
    11. pip install tensorboardX
    12. pip install pandas==1.5.3
    13. pip install librosa==0.10.0
    14. pip install numpy==1.23.1

    准备素材

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

    09ccaa9768315f80d8ceef50b7ebba46.png

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

    a79cbb31fb534acbe5b0c25b2b7b2deb.png

    1. import whisper
    2. from pathlib import Path
    3. import librosa
    4. from scipy.io import wavfile
    5. import numpy as np
    6. import sox
    7. whisper_size = "large"
    8. whisper_model = whisper.load_model(whisper_size)
    9. def split_long_audio(model, filepaths, save_dir="data_dir", out_sr=44100):
    10. if isinstance(filepaths, str):
    11. filepaths = [filepaths]
    12. for file_idx, filepath in enumerate(filepaths):
    13. save_path = Path(save_dir)
    14. save_path.mkdir(exist_ok=True, parents=True)
    15. print(f"Transcribing file {file_idx}: '{filepath}' to segments...")
    16. result = model.transcribe(filepath, word_timestamps=True, task="transcribe", beam_size=5, best_of=5)
    17. segments = result['segments']
    18. wav, sr = librosa.load(filepath, sr=None, offset=0, duration=None, mono=True)
    19. wav, _ = librosa.effects.trim(wav, top_db=20)
    20. peak = np.abs(wav).max()
    21. if peak > 1.0:
    22. wav = 0.98 * wav / peak
    23. wav2 = librosa.resample(wav, orig_sr=sr, target_sr=out_sr)
    24. wav2 /= max(wav2.max(), -wav2.min())
    25. for i, seg in enumerate(segments):
    26. start_time = seg['start']
    27. end_time = seg['end']
    28. wav_seg = wav2[int(start_time * out_sr):int(end_time * out_sr)]
    29. wav_seg_name = f"{file_idx}_{i}.wav"
    30. out_fpath = save_path / wav_seg_name
    31. wavfile.write(out_fpath, rate=out_sr, data=(wav_seg * np.iinfo(np.int16).max).astype(np.int16))
    32. split_long_audio(whisper_model, "001石猴出世.mp3", "test_wavs")

    自动标注

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

    https://modelscope.cn/models/damo/speech_ptts_autolabel_16k/summary
    1. from modelscope.tools import run_auto_label
    2. import os
    3. os.makedirs("output_training_data", exist_ok=True)
    4. input_wav = "./test_wavs/"
    5. output_data = "./output_training_data/"
    6. ret, report = run_auto_label(input_wav=input_wav, work_dir=output_data, resource_revision="v1.0.7")
    4abb7f55f65136c312702147eda71148.png

    训练模型

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

    3a01c495c54a73a99693aebaf7211800.jpeg
    1. from modelscope.metainfo import Trainers
    2. from modelscope.trainers import build_trainer
    3. from modelscope.utils.audio.audio_utils import TtsTrainType
    4. pretrained_model_id = 'damo/speech_personal_sambert-hifigan_nsf_tts_zh-cn_pretrain_16k'
    5. dataset_id = "./output_training_data/"
    6. pretrain_work_dir = "./pretrain_work_dir/"
    7. os.makedirs("pretrain_work_dir", exist_ok=True)
    8. # 训练信息,用于指定需要训练哪个或哪些模型,这里展示AM和Vocoder模型皆进行训练
    9. # 目前支持训练:TtsTrainType.TRAIN_TYPE_SAMBERT, TtsTrainType.TRAIN_TYPE_VOC
    10. # 训练SAMBERT会以模型最新step作为基础进行finetune
    11. train_info = {
    12. TtsTrainType.TRAIN_TYPE_SAMBERT: { # 配置训练AM(sambert)模型
    13. 'train_steps': 202, # 训练多少个step
    14. 'save_interval_steps': 200, # 每训练多少个step保存一次checkpoint
    15. 'log_interval': 10 # 每训练多少个step打印一次训练日志
    16. }
    17. }
    18. # 配置训练参数,指定数据集,临时工作目录和train_info
    19. kwargs = dict(
    20. model=pretrained_model_id, # 指定要finetune的模型
    21. model_revision = "v1.0.6",
    22. work_dir=pretrain_work_dir, # 指定临时工作目录
    23. train_dataset=dataset_id, # 指定数据集id
    24. train_type=train_info # 指定要训练类型及参数
    25. )
    26. trainer = build_trainer(Trainers.speech_kantts_trainer,
    27. default_args=kwargs)
    28. trainer.train()
    8e6e2f5af48794d304366eaccf1acebd.png

    合成效果

    1. from modelscope.models.audio.tts import SambertHifigan
    2. from modelscope.pipelines import pipeline
    3. from modelscope.utils.constant import Tasks
    4. def infer(text):
    5. model_dir = os.path.abspath("./pretrain_work_dir")
    6. custom_infer_abs = {
    7. 'voice_name':
    8. 'F7',
    9. 'am_ckpt':
    10. os.path.join(model_dir, 'tmp_am', 'ckpt'),
    11. 'am_config':
    12. os.path.join(model_dir, 'tmp_am', 'config.yaml'),
    13. 'voc_ckpt':
    14. os.path.join(model_dir, 'orig_model', 'basemodel_16k', 'hifigan', 'ckpt'),
    15. 'voc_config':
    16. os.path.join(model_dir, 'orig_model', 'basemodel_16k', 'hifigan',
    17. 'config.yaml'),
    18. 'audio_config':
    19. os.path.join(model_dir, 'data', 'audio_config.yaml'),
    20. 'se_file':
    21. os.path.join(model_dir, 'data', 'se', 'se.npy')
    22. }
    23. kwargs = {'custom_ckpt': custom_infer_abs}
    24. model_id = SambertHifigan(os.path.join(model_dir, "orig_model"), **kwargs)
    25. inference = pipeline(task=Tasks.text_to_speech, model=model_id)
    26. output = inference(input=text)
    27. filename = text + ".wav"
    28. with open(filename, mode='bx') as f:
    29. f.write(output["output_wav"])
    30. return filename
    31. infer("克隆自己的声音,赛博分身必备技能")

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

    99930cceda008f942543a72792cfa133.png

    VITS

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

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

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

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

    1. git clone https://github.com/Plachtaa/VITS-fast-fine-tuning.git VITS
    2. conda create -n vits python=3.8
    3. conda activate vits
    4. pip install pyopenjtalk==0.3.2
    5. pip install -r requirements.txt (修改 pyopenjtalk 版本)
    7c53c96a964e908b2e8686f4c88e4158.png

    下载中文预训练模型

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

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

    下载原始素材

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

    音频预处理

    1. mkdir video_data
    2. mkdir raw_audio
    3. mkdir denoised_audio
    4. mkdir custom_character_voice
    5. 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
    ac5e3cfbbf19e8de01407ac35ca04faf.png

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

    python scripts/long_audio_transcribe.py --languages C --whisper_size large
    6c599bcf0cea4b2f9e017de0ba8da131.png 21d3b8cf20ed85e15f0b4c8e8cdcad22.png

    处理训练数据集

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

    python preprocess_v2.py --add_auxiliary_data True --languages C
    52834def66193d4afdc407ffb6ab905d.png

    训练模型

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

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

    生成音频

    试听一下效果,完美!

    python VC_inference.py --model_dir ./OUTPUT_MODEL/G_latest.pth --config_dir ./configs/modified_finetune_speaker.json --share True
    7846e740ee0df00ac3df43fa6dd8fb7d.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

    8bff7cf5b02a86820a122a4d699931f1.png
    安装依赖
    1. conda create -n chat python=3.10.0
    2. activate chat
    3. git clone https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI.git RVC
    4. sudo apt install build-essential
    5. 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
    78612e6a82b2c07e56a815325574bb5d.png

    人声分离

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

    20b9d6ba5f0763c53f092f49ba1f9f32.png

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


    fdfcdcd7c3db5d616c6df4c41b82efac.png

    预处理目标语音

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

    fe18974d6cb3c054791e774a5d7bc056.png

    提取特征

    使用 rmvpe_gpu 提取特征

    bdead3ba0b8c9841f28aa0e357fe8258.png

    训练模型

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

    3bc7d181c5fa2f963b986744fbe0f658.png

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

    tensorboard --logdir=./logs/uncle_kai
    d58a601b3efb31a6a45500d3606766fd.png

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

    合成歌曲

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

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

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

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

    677d56d1874823c8f3720d6012e812d2.png

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

    https://www.audacityteam.org/download/
    4488bfed4590a6262fbb43eb73d64613.png

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

    番外篇 UVR

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

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

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

    https://github.com/Anjok07/ultimatevocalremovergui/releases
    fe01aeb749d7ffd1e092f161bb3a67f9.png 2ed24514d7044e3567fad7bc48bf87f7.png

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

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

    https://vocalremover.org/

    总结

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

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

    aed0253b6ef9e596b7c7060473a43f9b.png

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

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

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

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

    源码下载

    f0f7f0b5fdaf007803147e6e89d8d25b.png

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

    6f693474c67e78d8fb6037f19de07afd.gif

  • 相关阅读:
    slice()与splice()的用法和区别你清楚吗?
    0.4 写Python前须知
    省重点实验室成果转化:24h在线的专业数字朋友,不用欠人情的网上解答神器~
    @Scope与@RefreshScope注解
    第二十三章 源代码文件 REST API 参考(五)
    Kestra:高性能、基于 Java 的编排平台
    abc 324 f ( 拓扑 + dp + 二分
    skywalking源码本地编译运行经验总结
    周赛371(模拟、哈希+排序+枚举)
    牛客网:NC54 三数之和
  • 原文地址:https://blog.csdn.net/weixin_47479625/article/details/133004391