wikiAudio01|WaveNet:原理、架构与实现04|PyTorch 实现:网络结构、训练与生成
12 分钟
0 次观看

04|PyTorch 实现:网络结构、训练与生成

这一篇把模型变成可执行的学习流程。先沿图走完一次前向,再精确划出损失范围,最后比较“已知真实历史的训练”和“靠自己继续写下去的生成”。完整脚本在源码附录,本页只保留需要解释的局部代码。

返回 WaveNet 导读 · 代码架构与完整源码

先分两件事: 训练更新参数;自由生成固定参数,持续更新历史与缓存。

1. 网络架构与张量流

教学 WaveNet 总架构:12层残差主干、每层条件注入、各层 skip 求和与四级输出头

图 1|教学模型架构与张量流

沿图中间蓝色主干读取:整数类别先查 embedding,得到连续特征;12 个块各自产生新的残差与一份 skip;残差继续往下,所有 skip 汇总后交给分类头。注意图中末层残差没有直接连到 head;head 读取的是所有 skip 的汇合结果。 同一组权重如何用于 step,在第 3 节的时间展开图中单独画出。

阶段实际形状应如何理解
原始 qq[4,513],long四条量化序列
inp / target[4,512],long相差一个采样点的输入与标签
cond_target[4,2,512],float每个目标的频率与振幅条件
查 embedding[4,512,24]256 个类别各有一个 24 维向量
转置后 / 各块残差[4,24,512]Conv1d 的批、通道、时间布局
门控卷积[4,48,512]为两支各准备 24 通道
各层 skip / 汇总结果[4,24,512]可逐元素相加的同形状特征
最终 logits[4,256,512]每个时间位置的 256 个类别分数
网络维度说明
数学记号含义、单位与示例
BB批大小,本例为 44。
TT网络时间长度,本例为 512512。
CC隐藏通道数,本例为 2424。
HH条件通道数,本例为 22。
QQ输出类别数,本例为 256256。

embedding 的 2424 是表示维度,不会把输出类别数从 256256 改为 2424。

各块 skip 汇合后送入输出头:S=1L∑ℓ=1LsℓS=\frac{1}{\sqrt L}\sum_{\ell=1}^{L}s_\ell。

Skip 汇合:符号说明
数学记号含义、单位与示例
LL残差块数;本例为 1212。
ℓ\ell残差块的索引。
sℓs_\ell第 ℓ\ell 层产生的 skip,形状为 [B,C,T]。
SS汇总并缩放后的张量,形状仍为 [B,C,T]。

除以 L\sqrt L 是本教学实现的尺度选择,不保证特征方差严格不变,因为各层输出并不独立。输出头依次是 ReLU、1×1 卷积 C→CC\to C、ReLU、1×1 卷积 C→QC\to Q。

以下代码与图逐项对应:x 保留主干,skips 收集支路,最后一次调用 head 得到 logits。这里不做 softmax,训练损失会处理它。

def forward(self, q_input, cond_target):
    x = self.embedding(q_input).transpose(1, 2)
    skips = []
    for block in self.blocks:
        x, skip = block(x, cond_target)
        skips.append(skip)
    return self.head(torch.stack(skips).sum(0) / math.sqrt(len(skips)))

2. 训练目标与损失计算

原始序列、目标索引和 warmup 的对应图,显示前126个位置不直接计分,后386个位置参与损失

图 2|warmup 与有效计分区间

读图中分界线:感受野 RR=127 时,输出 nn=126 第一次拥有完整的 127 个真实输入 q[0]q[0]…q[126]q[126],预测目标 q[127]q[127]。更早的输出含左补零上下文。本例保留这些位置的前向计算,但不直接把它们的分类损失计入平均。

令 ww=RR−1=126 为 warmup 长度,损失为:

L=−1B(T−w)∑b=0B−1∑n=wT−1ln⁡pb,n(qb,n+1).\mathcal L=-\frac{1}{B(T-w)}\sum_{b=0}^{B-1}\sum_{n=w}^{T-1}\ln p_{b,n}\big(q_{b,n+1}\big).
批平均交叉熵:符号与计分范围
数学记号含义、单位与示例
L\mathcal L一次 batch 上平均的交叉熵,单位为 nats/目标点。
BB批大小,本例为 44。
bbbatch 内的样本索引,从 00 到 B−1B-1。
TT网络时间长度,本例为 512512。
nn网络输出位置,参与计分的范围为 126,…,511126,\ldots,511。
ww不直接计分的前缀长度,本例为 126126。
RR感受野大小,本例为 127127,因此 w=R−1w=R-1。
qb,n+1q_{b,n+1}第 bb 条波形在目标位置 n+1n+1 的真实类别。
jj某个候选类别的编号。
pb,n(j)p_{b,n}(j)样本 bb 在输出位置 nn 对类别 jj 的预测概率。
ln⁡\ln自然对数。
∑b∑n\sum_b\sum_n在参与计分的样本与时间位置上累加。
B(T−w)B(T-w)参与平均的目标数,本例为 15441544。
核算项代入计算结果
每条样本的计分位置512−126512-126386386
一个 batch 的计分目标4×3864\times38615441544
假设各目标正确类别概率均为 0.250.25−ln⁡0.25-\ln0.251.38631.3863 nats/目标
上述相同损失再求平均1544×1.3863/15441544\times1.3863/1544仍为 1.38631.3863

真实训练应逐项取负对数,再平均。 各位置概率通常不同,“平均概率再取对数”会得到另一个目标。

代码的 cross_entropy 要求 logits 类别轴在 dim=1,目标是整数类别,不是 one-hot,也不是浮点振幅。它内部处理 log-softmax;先手工 softmax 再传进去会改变计算。CrossEntropyLoss 文档

logits = model(inp, cond_target)
warmup = model.receptive_field - 1
loss = F.cross_entropy(logits[:, :, warmup:], target[:, warmup:])
optimizer.zero_grad(set_to_none=True)
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
更新步骤作用
zero_grad清除上轮梯度
backward沿当前计算图计算梯度
梯度裁剪限制总梯度范数
优化器更新根据梯度修改参数

warmup 只屏蔽前缀的直接计分,不切断前缀对后续目标的影响。 变长真实语音还需要尾部有效长度 mask;本例四条数据等长,没有这一项。

3. 并行训练与自回归生成

训练与生成的展开图:真实输入的并行预测、抽样类别反馈和逐层缓存状态传递

图 3|并行训练与逐点生成

上半图的训练已经拿到真实序列 [12,18,25,20],一次前向同时预测三个目标。这称为 teacher forcing(教师强制):每项预测的历史来自真实数据,因果结构负责遮住尚不该读取的部分。

下半图只有种子 12 已知。若第一步抽到 17,第二步就必须使用 17,而不是偷偷换成训练数据中的 18。先前的小偏差会改变后续分布。这是低训练损失不足以证明长时间生成稳定的原因。

一次生成调用的输入是 previous[B]、当前目标的条件 [B,H],以及各层队列组成的 state;输出是 logits [B,Q] 和更新后的 state。以下三行负责预测与抽样,外层循环把新编号继续送回来:

logits, state = model.step(previous, cond_future[:, :, i], state)
probs = torch.softmax(logits / temperature, dim=1)
previous = torch.multinomial(probs, 1).squeeze(1)

i 是本次生成的步号,cond_future 的时间轴只覆盖要生成的新点。multinomial 按概率抽取类别,不等于取最大项。所有新点生成完后,才能用 μ-law 解码为振幅;脚本当前只执行短序列生成检查,不写 WAV。

温度通过 pj(τ)=exp⁡(ℓj/τ)∑k=0Q−1exp⁡(ℓk/τ)p_j(\tau)=\frac{\exp(\ell_j/\tau)}{\sum_{k=0}^{Q-1}\exp(\ell_k/\tau)} 改变抽样分布。

温度抽样:符号说明
数学记号含义、单位与示例
τ\tau必须大于 00 的无量纲温度参数。
ℓj\ell_j类别 jj 的 logit。
jj当前考察的类别编号。
kk分母的求和索引,遍历所有类别。
QQ类别总数。
pj(τ)p_j(\tau)在温度 au au 下,抽取类别 jj 的概率。

τ=1\tau=1 保持原分布;较小温度让分布更集中。

同一组四类 logits 在温度0.5、1、2时的概率柱图

图 4|温度对抽样分布的影响

固定图中的 logits 为 [0,ln⁡2,ln⁡3,ln⁡4][0,\ln2,\ln3,\ln4],只改变温度:

温度 τ\tau缩放后取指数的结果最高类别的概率
0.50.5[1,4,9,16][1,4,9,16]16/30≈0.533316/30\approx0.5333
11[1,2,3,4][1,2,3,4]4/10=0.44/10=0.4
22[1,2,3,2][1,\sqrt2,\sqrt3,2]约 0.32540.3254

降低温度使该分布更集中,但不能修复标签错位,也不保证音质更好。

自测:能否让每次生成都只调用 forward 而不写缓存?可以,重复计算已有前缀也能得到下一步分布,但浪费工作。下一篇解释怎样把已有中间结果保存下来,同时保持网络函数不变。

正在连接 GitHub 评论…