文章目錄
- VALL-E-X
- 簡介
- code
- vist
- 論文
- 解讀
- 代碼解讀
- 模塊
- loss
- 代碼
- valle
- 名詞解釋
VALL-E-X
簡介
微軟VALL-E-X:夸克在用
可以預訓練模型
端到端
code
code:https://github.com/Plachtaa/VALL-E-X/tree/master
報錯1:
File "/mnt/TTS/VALL-E-X/test.py", line 6, in <module>preload_models()File "/mnt/TTS/VALL-E-X/utils/generation.py", line 89, in preload_modelsvocos = Vocos.from_pretrained('charactr/vocos-encodec-24khz').to(device)File "/mnt/envs/tts_env/lib/python3.10/site-packages/vocos/pretrained.py", line 67, in from_pretrainedconfig_path = hf_hub_download(repo_id=repo_id, filename="config.yaml", revision=revision)File "/mnt/envs/tts_env/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py", line 118, in _inner_fn
報錯原因:沒有網,無法load遠程資料。需要手動下載,放在指定的位置。
下載鏈接:https://huggingface.co/charactr/vocos-encodec-24khz 頁面files and versions
下載的文件:config.yaml / pytorch_model.bin
linux系統放在:ls models–charactr–vocos-encodec-24khz/
注釋掉下載的代碼:/mnt/envs/tts_env/lib/python3.10/site-packages/vocos/pretrained.py
# config_path = hf_hub_download(repo_id=repo_id, filename="config.yaml", revision=revision)
# model_path = hf_hub_download(repo_id=repo_id, filename="pytorch_model.bin", revision=revision)
config_path = '/root/.cache/huggingface/hub/models--charactr--vocos-encodec-24khz/config.yaml'
model_path = '/root/.cache/huggingface/hub/models--charactr--vocos-encodec-24khz/pytorch_model.bin'
參考解決方案:https://github.com/Plachtaa/VALL-E-X/issues/89
vist
論文
Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-Speech
解讀
代碼解讀
模型結構:gan+flow+vae模型
大框架是vae,訓練時用gan,建模的時候用到了flow。
后驗分布就是高斯分布;先驗分布是高斯分布*flow
模塊
Text Encoder:先驗的文本的編碼器模塊,將獨立的字表征成上下文相關的特征,得到一個以文字和說話者為條件的先驗的分布:text->phonemet->先驗分布。
Linear Spectorgram:輸入wav,無參數訓練的一種傅立葉變換,得到頻譜。
posterior encoder:輸入頻譜,經過編碼,得到后驗分布。
loss
1、重構loss,輸入的wav的梅爾頻譜和生成的wav的梅爾頻譜計算loss。
2、KL loss:text的先驗分布和audio的后驗分布要對齊。audio的后驗分布是頻譜的長度,text的先驗分布顯然比audio的后驗要短,需要將先驗擴充一下。
怎么對齊:通過動態規劃的方式,找到每個因素持續的時長。先驗分布根據時長進行擴展,和后驗就在同一個維度了。
3、時長預測器:是一個flow模型,為什么需要時長預測器?infer的時候沒有頻譜可以來對齊text,需要預測。預測每個因素時長的分布,而不是預測因素時長的期望。有一個kl loss。
4、 gan的loss。
模型不是一個確定性的模型,即,每次預測的結果可能都不一樣。采樣的時候,是one to many的。
代碼
train.py
class TextAudioSpeakerLoader(torch.utils.data.Dataset):def _filter(self)#如果文本太短做過濾。 #dataset被調用的時候,會走getitem函數。text = self.get_text(text) #對phoneme進行離散化,將字符變成索引。
DistributedBucketSampler:#text的長度變化很大,不做桶排序的話,反向梯度的效率會很低。提升訓練效率。為什么效率不高,因為text短的話,需要pad較多的無效數據。def __init__(self, dataset, batch_size, boundaries, num_replicas=None, rank=None, shuffle=True):boundaries:#分桶的邊界。邊界是幀。num_replicas:#gpu卡數。def _create_buckets(self):返回桶,及每個桶的樣本量。indices.append(torch.randperm(len(bucket), generator=g).tolist()):shuffle
TextAudioSpeakerCollate_, ids_sorted_decreasing = torch.sort( #根據梅爾頻譜的長度進行排序torch.LongTensor([x[1].size(1) for x in batch]),dim=0, descending=True)
net_g = SynthesizerTrn( # 是生成器,從text到wavlen(symbols),hps.data.filter_length // 2 + 1,hps.train.segment_size // hps.data.hop_length,n_speakers=hps.data.n_speakers,**hps.model).cuda(rank)
net_d = MultiPeriodDiscriminator(hps.model.use_spectral_norm).cuda(rank) # 多周期的判別器
optim_g = torch.optim.AdamW( # 生成器的優化函數net_g.parameters(), hps.train.learning_rate, betas=hps.train.betas, eps=hps.train.eps)optim_d = torch.optim.AdamW(# 判別器的優化函數net_d.parameters(),hps.train.learning_rate, betas=hps.train.betas, eps=hps.train.eps)
net_g = DDP(net_g, device_ids=[rank]) # 分布式訓練
scheduler_g = torch.optim.lr_scheduler.ExponentialLR(optim_g, gamma=hps.train.lr_decay, last_epoch=epoch_str-2) # 學習率的衰減方案from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler(enabled=hps.train.fp16_run) # 混合訓練 fp16進行訓練,在效率和性能上進行平衡
x, x_lengths = x.cuda(rank, non_blocking=True), x_lengths.cuda(rank, non_blocking=True) # 把數據copy到gpu上面
y_hat, l_length, attn, ids_slice, x_mask, z_mask,\(z, z_p, m_p, logs_p, m_q, logs_q) = net_g(x, x_lengths, spec, spec_lengths, speakers)
# y_hat 預估波形;l_length 預估波形的長度;并不是所有的波形都參與訓練,而是對波形進行了采樣,減少內存的消耗。ids_slice:采樣后的頻譜的id。
spec:線性普
spec_to_mel_torch()線性普轉梅爾普
mel_spectrogram_torch() 波形到梅爾普
y_mel = commons.slice_segments(mel, ids_slice, hps.train.segment_size // hps.data.hop_length) # 對梅爾普也進行采樣。
y = commons.slice_segments(y, ids_slice * hps.data.hop_length, hps.train.segment_size) # 一個梅爾普對應256個波形點y_d_hat_r, y_d_hat_g, _, _ = net_d(y, y_hat.detach()) # y_d_hat_r真實的判別器的輸出,生成的判別器判別器的輸出,判別器
with autocast(enabled=False): # 不走fp16loss_disc, losses_disc_r, losses_disc_g = discriminator_loss(y_d_hat_r, y_d_hat_g)loss_disc_all = loss_disc
optim_d.zero_grad() #判別器梯度置0
model.py
self.dec = Generator(inter_channels, resblock, resblock_kernel_sizes, resblock_dilation_sizes, upsample_rates, upsample_initial_channel, upsample_kernel_sizes, gin_channels=gin_channels) # 波形生成器self.enc_q = PosteriorEncoder(spec_channels, inter_channels, hidden_channels, 5, 1, 16, gin_channels=gin_channels) # 后驗編碼器
PosteriorEncoder:def forward(self, x, x_lengths, g=None):#g是一個條件,以說話人為條件x_mask = torch.unsqueeze(commons.sequence_mask(x_lengths, x.size(2)), 1).to(x.dtype)x = self.pre(x) * x_maskx = self.enc(x, x_mask, g=g)stats = self.proj(x) * x_maskm, logs = torch.split(stats, self.out_channels, dim=1) # m分布的方差 # logs分布的標準差取logz = (m + torch.randn_like(m) * torch.exp(logs)) * x_mask return z, m, logs, x_maskself.flow = ResidualCouplingBlock(inter_channels, hidden_channels, 5, 1, 4, gin_channels=gin_channels)# 提高先驗分布的表達能力的,加flow效果會更好,是先驗的flowif use_sdp: #隨機時長預測器,(說話的韻律節奏)self.dp = StochasticDurationPredictor(hidden_channels, 192, 3, 0.5, 4, gin_channels=gin_channels)x, m_p, logs_p, x_mask = self.enc_p(x, x_lengths) # x 文本的encode,m_p分布的方差,logs_p分布的標準差,是fseitaz分布的u sigma
z_p = self.flow(z, y_mask, g=g) # 后驗經常一個逆flow;或者先驗經過一個flow# 動態規劃 單調對齊搜索
with torch.no_grad():# negative cross-entropys_p_sq_r = torch.exp(-2 * logs_p) # [b, d, t]neg_cent1 = torch.sum(-0.5 * math.log(2 * math.pi) - logs_p, [1], keepdim=True) # [b, 1, t_s]neg_cent2 = torch.matmul(-0.5 * (z_p ** 2).transpose(1, 2), s_p_sq_r) # [b, t_t, d] x [b, d, t_s] = [b, t_t, t_s]neg_cent3 = torch.matmul(z_p.transpose(1, 2), (m_p * s_p_sq_r)) # [b, t_t, d] x [b, d, t_s] = [b, t_t, t_s]neg_cent4 = torch.sum(-0.5 * (m_p ** 2) * s_p_sq_r, [1], keepdim=True) # [b, 1, t_s]neg_cent = neg_cent1 + neg_cent2 + neg_cent3 + neg_cent4l_length = l_length / torch.sum(x_mask) # l_length是似然attn_mask = torch.unsqueeze(x_mask, 2) * torch.unsqueeze(y_mask, -1)attn = monotonic_align.maximum_path(neg_cent, attn_mask.squeeze(1)).unsqueeze(1).detach() # attn是01矩陣,因為是硬對齊,attn shape[b,1,T_s,T_t] 文本長度,頻譜長度
w = attn.sum(2) #w的shape [b, 1,T_t] 每個文本有多少幀 T_t是文字
# 對齊之前 m_p 的shape [b,feat_dim,T_t]
# 對齊之前 logs_p 的shape [b,feat_dim,T_t]
m_p = torch.matmul(attn.squeeze(1), m_p.transpose(1, 2)).transpose(1, 2)# expand prior
# 對齊后擴展后 m_p 的shape [b,feat_dim,T_s]
# 對齊后擴展后 logs_p 的shape [b,feat_dim,T_s] z_slice, ids_slice = commons.rand_slice_segments(z, y_lengths, self.segment_size) # 為了減少內存,進入解碼器之前,需要進行采樣,segment_size決定了采樣的頻率;一個幀頻譜對應256個波形點
o = self.dec(z_slice, g=g) # 反卷積和參差網絡,一個幀頻譜對應256個波形點,如果想從頻譜產出波形,需要上采樣256,需要4個上采樣 8 8 2 2if use_sdp: # 隨機時長預測器self.dp = StochasticDurationPredictor(hidden_channels, 192, 3, 0.5, 4, gin_channels=gin_channels)else:self.dp = DurationPredictor(hidden_channels, 256, 3, 0.5, gin_channels=gin_channels)# x輸入 經過神經網絡或者transfrom 和 w計算f1 lossclass StochasticDurationPredictor(nn.Module): # 隨機 事表現力更強,預測每個因素餓時長分布,從分布中采樣就是隨機 通過重參數化 基于flow來設計的。
valle
名詞解釋
VITS: VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)是一種結合變分推理(variational inference)、標準化流(normalizing flows)和對抗訓練的高表現力語音合成模型
b站 bert+vits2:
https://www.bilibili.com/video/BV1oC4y1D7VX/?spm_id_from=333.337.search-card.all.click
Zero-Shot Learning簡稱ZSL
TTS:Text-to-Speech
飛槳 PaddlePaddle:百度旗下產品