语音转换之CycleGan-VC2:原理与实战

news/2024/5/4 4:56:46/文章来源:https://blog.csdn.net/qq_36002089/article/details/128361696

非平行语音转换CycleGAN

之前学习了传统统计学习里的经典的语音转换模型GMM。随着深度学习的发展,出现了更好的语音转换方法,今天学习较为经典的CycleGan。

平行语音转换一般流程

典型代表就是基于GMM的语音转换。平行数据就是说源语音和目标语音一一对应,这里对应就是指每句话的内容必须一样。非平行数据就是说话内容无需完全一样。

所以,平行语音转换中最关键的就是特征配准(特征对齐),对齐效果好坏直接影响最终的转换效果。平行语音一般用DTW算法基于最小距离原则进行对齐。
在这里插入图片描述

特征映射模型总结

根据输入数据的类型,可将特征映射模型分为基于平行和非平行数据的模型。

基于平行数据

最早期使用基于规则的方法,后来引入概率统计模型,典型的如GMM,到现在深度学习的方法,主要基于神经网络。

  • DNN:帧到帧
    在这里插入图片描述

  • RNN:帧到帧
    在这里插入图片描述

  • seq-seq
    在这里插入图片描述

基于非平行数据

  • 通过切分、聚类,来设法构造并行数据

  • 使用ASR系统,辅助VC。
    提取与说话人无关的信息,构造这些信息与目标说话人之间的关联,在文本层级上进行对齐。
    在这里插入图片描述

  • 使用变分自编码VAE与竞争网络
    Encoder可以看作特征降维。
    对齐:让源特征和目标特征经过Encoder后,在浅变量概率分布层级上进行对齐。
    在这里插入图片描述

训练方法:在这里插入图片描述

可以看到,这些非平行数据的VC算法中,还是离不开“对齐”操作。要么在音频特征切分后,在特征维度上进行对齐;要么利用ASR系统,在文本层级对齐;要么利用深度神经网络进行编码,在编码的潜变量或分布上进行对齐。

基于CycleGan的VC,真正意义上摆脱了“对齐”操作。

CycleGan最早用于图片风格转换。如下图CycleGAN 斑马与马转换。
在这里插入图片描述

2019年,有人将CycleGan用到语音转换,并作了一系列的工作。下面这篇是最经典的,第二代CycleGan-VC2,目前被引了两百多次了。
在这里插入图片描述

CycleGan-VC

Gan网络的原理,可以看这篇文章:GAN

下图是CycleGan结构图:
既可以A 到B,也可以B到A。
在这里插入图片描述

和上面图表达的类似,可以对比着看。
在这里插入图片描述

目标函数:
在这里插入图片描述

损失函数

1.对抗损失 Adversial loss
用来衡量如何区分由x生成的y和真实y
在这里插入图片描述
注意这个对抗损失是最大最小化,即训练G时,希望能骗过D,希望损失最小化;训练D时,希望D很厉害能识别生成的假数据,最大化损失。

2.循环不变性损失Cycle-consistency loss
用来保证GX-Y和GY-X得到(X,Y)对的文本内容相同。
在这里插入图片描述
即循环一圈后生成的和原来的越相似越好。

CycleGan-VC2的改进,多了如下。

3.映射一致性损失Identity-mapping loss
Gx-y的网络应该输入x,希望生成y,现在直接就输入y,希望它能不变的输出y,为了保证模型只转换风格,不转换内容。
在这里插入图片描述

4.对抗损失2 Adversial loss2
在这里插入图片描述

从下图可以看到,给生成的x又加了个判别器Dx。按理说刚才说的损失循环不变性损失Cycle-consistency loss,也就是下图的(a)已经能保证说话内容不变了,为什么还有这个损失函数?
因为Cycle-consistency loss中的L1范式的度量仍然会导致过平滑,为了减轻这种负面影响才提出这个损失函数。
在这里插入图片描述
实际使用中不需要重新建一个Dx,用已有的Dx就行。

总结上面4个损失函数:
1是为了输出的语音是一句目标语音的人的声音,而不是其他声音,至于说的内容它无法约束。
2和3损失就是为了保证说的内容和原说话人一致,如源说话人说你好,转换后也得内容是你好。
4是为了缓解过平滑,让循环一圈以后的声音仍和原声更接近。

为了损失函数更直观,把上面的这几个损失函数进行了规整,统一成min的,loss最小。
1.对抗损失 Adversial loss

在这里插入图片描述

2.循环不变性损失Cycle-consistency loss
在这里插入图片描述

3.映射一致性损失Identity-mapping loss
在这里插入图片描述

4.对抗损失2 Adversial loss2
在这里插入图片描述

网路结构

生成器G: 输入source的MCEP特征,输出改变音色的MCEP特征。
生成器使用1DCNN,以捕获总体特征之间的关系,同时保留时间结构。同时使用了下采样层,残差层和上采样层,以及Instance Normalization。
这里用到了门控CNN,模拟lstm的是否遗忘门,或者说判断权重的思想。再做一个和CNN卷积一样参数的filter, 取值0-1,判断这个序列的特征哪些应该被关注,哪些应该被忽略。在门控CNN中,门控线性单元(GLUs)被用作一个激活函数,GLU是一个数据驱动的激活函数,并且门控机制允许根据先前的层状态选择性地传播信息。

在这里插入图片描述

鉴别器D: 输入音频的MCEP等特征,进行二分类,输出是否经过转换
使用2D CNN鉴别器来基于2D频谱纹理鉴别数据。使用具有完全连接层作为最后一层的FullGAN,以根据整体输入结构来区分数据。

在这里插入图片描述

注意里面结构PatchGAN:
一般D输出就是一维的,PatchGAN就是输出多维,就还是1-x,取平均。
在这里插入图片描述

代码实现

在GitHub上就有官方代码:CycleGAN-VC2
里面data文件夹有两个人的语音,可以分别新建train和test文件夹,把数据分来。

一般工程代码主要包含下面部分:

在这里插入图片描述

相关参数

hparams.py:超参数

class hparams():def __init__(self):# 数据缓存self.train_dir_A = "data/S0913/train"self.train_dir_B = "data/gaoxiaosong/train"self.catch_A = "catch/train_A"self.catch_B = "catch/train_B"self.n_frames =128# 特征提取相关self.fs = 16000         # 采样率 self.frame_period = 5.0 # 帧移self.coded_dim = 36     # mcep 特征维度 # 训练相关参数self.g_lr = 2e-4self.d_lr = 1e-4self.train_steps = 2e4# learing rate 衰减self.start_decay = 1e4self.decay_G = self.g_lr/2e5self.decay_D = self.d_lr/2e5# 丢失 identity_lossself.step_drop_identity = 1e4# lamdaself.identity_loss_lambda = 10self.cycle_loss_lambda = 5#每个 step_log 进行一次保存self.step_save = 2000# 模型保存路径self.path_save = 'save'

数据预处理

提取语音常见特征:F0、MCEP(这里编码成coded-sp)、ap

import numpy as np
import pyworld
import glob
from hparams import hparams
import librosa
import os# 用world声码器特征提取
def feature_world(wav,para):fs = para.fswav = wav.astype(np.float64)f0, timeaxis = pyworld.harvest(wav, fs, frame_period=para.frame_period, f0_floor=71.0, f0_ceil=800.0)sp = pyworld.cheaptrick(wav, f0, timeaxis, fs)ap = pyworld.d4c(wav, f0, timeaxis, fs)coded_sp =pyworld.code_spectral_envelope(sp, fs, para.coded_dim)return f0,timeaxis,sp,ap,coded_sp# 信号正则 
def wav_normlize(wav):max_ = np.max(wav)min_ = np.min(wav)wav_norm = wav*(2/(max_ - min_)) - (max_+min_)/(max_-min_)return wav_norm# 处理文件夹中的每条语音
def processing_wavs(file_wavs,para):f0s = []coded_sps = []for file in file_wavs:print("processing %s"%(file))fs = para.fswav, _ = librosa.load(file, sr=fs, mono=True)wav = wav_normlize(wav)# 提取特征f0和coded_spf0,_,_,_,coded_sp=feature_world(wav,para)print(coded_sp.shape)f0s.append(f0)coded_sps.append(coded_sp)# 计算log_f0的 均值和stdlog_f0s = np.ma.log(np.concatenate(f0s))log_f0s_mean = log_f0s.mean()log_f0s_std = log_f0s.std()# 计算 coded_sp 的均值和 标准差coded_sps_array = np.concatenate(coded_sps,axis=0)  # coded_sp的维度  T * Dcoded_sps_mean = np.mean(coded_sps_array,axis=0,keepdims = True)coded_sps_std = np.std(coded_sps_array,axis=0,keepdims = True)# 利用 coded_sp 的均值和 标准差 对特征进行正则coded_sps_norm = []for coded_sp in coded_sps:coded_sps_norm.append(  (coded_sp- coded_sps_mean)/ coded_sps_std )return log_f0s_mean,log_f0s_std,coded_sps_mean,coded_sps_std,coded_sps_normif __name__ == "__main__":para = hparams()# 提取说话人A的特征dir_train_A = para.train_dir_Awavs = glob.glob(dir_train_A+'/*wav') f0_mean,f0_std,mecp_mean,mecp_std, mecps = processing_wavs(wavs,para)os.makedirs(para.catch_A,exist_ok = True)np.save(os.path.join(para.catch_A,'static_f0.npy'),np.array([f0_mean,f0_std],dtype=object))np.save(os.path.join(para.catch_A,'static_mecp.npy'),np.array([mecp_mean,mecp_std],dtype=object))np.save(os.path.join(para.catch_A,'data.npy'),np.array(mecps,dtype=object))# 提取说话人B的特征dir_train_B = para.train_dir_Bwavs = glob.glob(dir_train_B+'/*wav')f0_mean,f0_std,mecp_mean,mecp_std, mecps = processing_wavs(wavs,para)os.makedirs(para.catch_B,exist_ok = True)np.save(os.path.join(para.catch_B,'static_f0.npy'),np.array([f0_mean,f0_std],dtype=object))np.save(os.path.join(para.catch_B,'static_mecp.npy'),np.array([mecp_mean,mecp_std],dtype = object))np.save(os.path.join(para.catch_B,'data.npy'),np.array(mecps,dtype=object))

构造Dataloader

import os
import torch
import numpy as np
from torch.utils.data import Dataset,DataLoader
from hparams import hparamsclass VC_Dataset(Dataset):def __init__(self,para):self.path_A = para.catch_Aself.path_B = para.catch_Bself.n_frames = para.n_frames# 加载数据self.train_A = np.load(os.path.join(self.path_A,'data.npy'),allow_pickle=True).tolist()self.train_B = np.load(os.path.join(self.path_B,'data.npy'),allow_pickle=True).tolist()self.n_samples = min(len(self.train_A),len(self.train_B))# 生成随机数据对self.gen_random_pair_index()def gen_random_pair_index(self):train_data_A_idx = np.arange(len(self.train_A))train_data_B_idx = np.arange(len(self.train_B))np.random.shuffle(train_data_A_idx)np.random.shuffle(train_data_B_idx)train_data_A_idx_subset = train_data_A_idx[:self.n_samples].tolist()train_data_B_idx_subset = train_data_B_idx[:self.n_samples].tolist()self.index_pairs = [(i,j) for i,j in zip(train_data_A_idx_subset,train_data_B_idx_subset)]def __len__(self):return len(self.index_pairs)def __getitem__(self,idx):# 读取 A 和 B 的数据data_A = self.train_A[self.index_pairs[idx][0]]data_B = self.train_B[self.index_pairs[idx][1]]# 从 A 和 B 中 随机截取 n_frames 帧start_A = np.random.randint(len(data_A) - self.n_frames + 1)sub_data_A = data_A[start_A:start_A + self.n_frames]start_B = np.random.randint(len(data_B) - self.n_frames + 1)sub_data_B = data_B[start_B :start_B + self.n_frames]return torch.from_numpy(sub_data_A.T),torch.from_numpy(sub_data_B.T)if __name__ == "__main__":para = hparams()m_Dataset= VC_Dataset(para)m_DataLoader = DataLoader(m_Dataset,batch_size = 2,shuffle = True, num_workers = 2)m_Dataset.gen_random_pair_index()for n_epoch in range(3):for i_batch, sample_batch in enumerate(m_DataLoader):train_A = sample_batch[0]train_B = sample_batch[1]print(train_A.shape)print(train_B.shape)if i_batch>5:breakm_Dataset.gen_random_pair_index()

其他代码太多就不一一列出来了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_241663.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【 Tomcat服务器】

文章目录二、Web服务器2.1 概念2.1.1 什么是Web2.1.2 什么是Web服务器2.2 常见Web服务器2.3 Tomcat服务器2.3.1 Tomcat的下载2.3.2 Tomcat的安装2.3.3 Tomcat的目录结构2.3.4 Tomcat的启动2.3.5 Tomcat的停止运行所需jar包2.3.5 Tomcat的停止2.3.6 修改Tomcat端口号2.3.7 项目部…

美容门店信息化管理系统该如何搭建?不妨参考一下百数

随着人们的生活水平越来越高,人们在解决了温饱问题之后有了更多的追求。其中美容正在成为不少人新的必做项目, 迎合了人们对于爱美的需求。目前我国美容机构市场规模已超过4500亿元,行业从业人员超过3000万。据国家工商联统计数字显示&#x…

宝藏又小众的东方行走rpg制作大师素材网站分享

看到大家都在问东方行走rpg制作大师素材,既要免费又要质量好,数量还要多,小编好不容易挖到了宝藏素材网站哦,资源优质数量庞大,使用体验也很好,要是需要的话,赶紧看一看,小编会给大家…

Redis实现全局唯一id,实现优惠卷秒杀的下单功能

Redis实现全局唯一id public class RedisIdWorker {private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}//开始时间戳private static final long BEGIN_TIMEST…

怎么调图片分辨率?怎么改图片分辨率?

许多考生在提交报名证件照片的时候,都因为图片分辨率不符合规定导致上传失败,所以今天小编就来告诉大家如何调整图片分辨率,其实只要通过专业的图片修改分辨率工具就可以轻松解决。这里推荐一款在线修改分辨率的软件,不用下载即可…

Java重点源码回顾——HashMap1.7

1. 概述 public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, SerializableHashMap在我们的日常使用中非常多&#xff0c;所以今天来阅读下它的源码&#xff0c;了解它具体的设计思想&#xff0c;能够帮助我们扩宽视野。 H…

SpringSecurity(二十四)--OAuth2:使用JWT和加密签名(下)非对称密钥加密

一、前言 由于上文对称密钥涉及到的内容比较多&#xff0c;所以这一节的非对称密钥加密拆开成这一节单独讲解。 所以大家尽量先阅读完上一章的内容后再浏览这一章内容会更好。 二、使用通过JWT和非对称密钥签名的令牌 本节将实现OAuth2身份验证的一个示例&#xff0c;其中授…

用户手册编写的终极指南

用户手册对于寻求了解产品和流程的用户来说是非常重要的。有时&#xff0c;它们甚至是一个公司向客户销售其产品的法律要求。 客户往往会在联系你的客户支持团队之前查阅你的用户手册&#xff0c;所以你的手册有可能为你节省支持成本。 在你的用户手册上投入大量时间和精力是…

数据结构课设:迷宫问题

文章目录前言一、概要设计1、基本信息2、功能模块图3、功能描述4、调用关系图5、结果演示① 创建迷宫② 求解③ 清除多余路径二、完整代码前言 最近刚好在写自己的课设&#xff0c;匆匆忙忙写出来的课设系统&#xff0c;仍有不足&#xff0c;拿出来和大家分享一下&#xff0c;…

【HTML5】复习(二)

HTML5复习二1.代码一2.代码二3.CSS的引入方式4.选择器5.form表单的一些属性6.内联7. 音频视频8. 滑块、搜索、数字、URL9. 表单补充1.代码一 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head>&…

如何在匿名上位机中显示自定义数据波形

匿名上位机相信很多人都用过&#xff0c;以前在调飞控的时候使用过&#xff0c;可以很直观的显示数据的波形&#xff0c;比如飞机姿态等。 最近在调试foc。很多数据在调试过程中&#xff0c;仅仅使用串口打印出来显示是很不直观的&#xff0c;比如正弦波&#xff0c;经典的马鞍…

word文件损坏打不开如何修复?文件丢失怎么办?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来Word文件修复的方法&#xff0c;以及Word文件丢失如何恢复的方法&#xff01; 一、Word文件损坏怎么办 部分…

Mathorcup数学建模竞赛第五届-【妈妈杯】D题:图像去噪中几类稀疏变换的矩阵表示(附一等奖获奖论文和matlab代码实现)

赛题描述 假设一幅二维灰度图像 X 受到加性噪声的干扰:Y=X+N ,Y 为观察到的噪声图像, N 为噪声。通过对于图像 Y 进行稀疏表示可以达到去除噪声的目的。任务: 2. 利用 Cameraman 图像中的一个小图像块(见图 1)进行验证。 3. 分析稀疏系数矩阵,比较四种方法的硬阈值稀…

kali安装cobaltstrike详细教程

下载cobaltstrike-linux版本,此下载链接提供4.3,4.4,4.5三个版本https://download.csdn.net/download/weixin_59679023/87354658 xshell上传至kali,解压 unzip cobaltstrike 进入cobaltstrike目录,ls查看如下 ls 给cs的服务端teamserver和客户端start.sh执行权限 chmod …

Java中常见的文件操作

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaEE 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 操作文件 File类 属性 构造方法 常见方法 重要方法的操作演示 文件内容的读写 FileInputStream OutputStream 按照字符读入 按照字符写入…

指南解读:急性心力衰竭中国急诊管理指南(2022)

心力衰竭&#xff08;heart failure&#xff0c;HF 简称心衰&#xff09;是由于心脏结构和 / 或功能异常导致心室充盈和/或射血能力受损的一组临床综合征&#xff0c;其病理生理学特征为肺淤血和/或体循环淤血、伴或不伴有组织器官低灌注&#xff0c;主要临床表现为呼吸困难、乏…

ETL数据清洗

大多数据仓库的数据架构可以概括为&#xff1a; 数据源-->ODS(操作型数据存储)-->DW-->DM(data mart) ETL贯穿其各个环节。 ​一、数据抽取&#xff1a; 可以理解为是把源数据的数据抽取到ODS或者DW中。 1. 源数据类型&#xff1a; 关系型数据库&#xff0c;如Or…

程序员必学的编辑语法——Markdown

Markdown是一种纯文本格式的标记语言。通过简单的标记语法&#xff0c;它可以使普通文本内容具有一定的格式。能使博客笔记更易阅读。 优点:因为是纯文本&#xff0c;所以只要是支持Markdown的地方都能获得一样的编辑效果&#xff0c;可以让作者摆脱排版的困扰&#xff0c;专心…

Mybatis_Plus_@TableName,@TableField

思考一个问题:为啥继承BaseMapper< POJO >&#xff0c;能直接找到Mysql的表 默认情况下:mp根据BaseMapper泛型POJO类取数据库底下找与POJO类型一致的表 思考一个问题:如果把表user改成tb_user那么我们需要怎么解决 使用TableName注解 TableField 思考一个问题:我们新增…

电磁兼容测试整改

1.1 什么时候需要电磁兼容整改及对策 在设计阶段就应考虑电磁兼容性&#xff0c;将产品生产阶段出现电磁兼容问题可能性减小。最终要通过电磁兼容测试检验其电磁兼容标准的符合性。 由于电磁兼容的复杂性&#xff0c;即使电磁兼容设计问题考虑比较周全&#xff0c;在设计制造…