wikiAudio01|WaveNet:原理、架构与实现02|因果卷积、膨胀卷积与感受野
11 分钟
1 次观看

02|因果卷积、膨胀卷积与感受野

这一篇回答两个问题:预测下一点时能读哪些输入;怎样在不降低时间分辨率的情况下看得更远。前提只是理解上一页的“振幅类别”。

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

最重要的约定: 输入 q[n]q[n] 预测 q[n+1]q[n+1]。先把这个关系对齐,再检查卷积是否偷看未来。

1. 输入序列与预测目标对齐

网络位置 nn 的输入是 q[n]q[n],标签是下一点 q[n+1]q[n+1]。 用序列 [12,18,25,20] 对齐三个预测任务:

网络位置 nn已知历史当前输入预测目标
00[12]12121818
11[12,18]18182525
22[12,18,25]25252020

因果可见性矩阵,每项预测只能读取其目标之前的输入

图 1|输入历史与下一点标签

逐行读图:绿色区域是允许读取的历史,红色区域含目标及其之后的波形。真正的有限感受野网络只能使用绿色区域的最近一部分。网络位置与波形目标索引差一格,后续所有公式和切片都沿用这个约定。

下面三行是数据语义约定,必须与模型一起检查。qq 为 [B,N],条件 cond 为 [B,H,N];BB 是批大小、NN 是原始点数、HH 是条件通道数。输出长度 TT=NN−1。

inp = q[:, :-1]               # q[0]…q[N-2]
target = q[:, 1:]             # q[1]…q[N-1]
cond_target = cond[:, :, 1:]  # 与被预测的目标对齐
输入处理对齐结果判断
inp=q[:,:-1],target=q[:,1:]当前输入对应下一点标签本教程约定
再把 inp 右移一格额外丢掉最近一个已知点不符合上述约定
直接用同一个 qq 作为输入和标签当前输入含有当前答案标签泄露

2. 左侧填充与因果卷积计算

先抛开通道,只看时间索引。核宽 kk=2、膨胀率 dd=2 时,每个输出用到相隔两个索引的输入。dd 是核取样间距;它不会把输出点数减半。

核宽二、膨胀率二的左补零卷积,展示输入索引和五个逐项输出

图 2|膨胀率为 2 的因果卷积

图的输入是 xx=[1,2,3,4,5],左侧补两个零。旧位置权重为 2、当前权重为 3、偏置为 1。通用的单通道表达式是:

y[n]=woldx[n−d]+wnowx[n]+b.y[n]=w_{\rm old}x[n-d]+w_{\rm now}x[n]+b.
因果卷积:符号与数值
数学记号含义、单位与示例
nn输出时间索引,本例为 0,…,40,\ldots,4;不是目标波形的索引。
x[n]x[n]当前层在位置 nn 的输入隐藏值;这里用单通道标量。
y[n]y[n]当前层在位置 nn 的卷积输出。
dd膨胀率;本例为 22。
woldw_{\mathrm{old}}旧位置输入 x[n−d]x[n-d] 的权重,本例为 22。
wnoww_{\mathrm{now}}当前位置输入 x[n]x[n] 的权重,本例为 33。
bb偏置,本例为 11。越界输入采用隐藏零填充。

代入权重后,选取三个位置核对左边界与历史读取:

位置旧位置输入当前位置输入卷积结果
n=0n=0越界,填 0011y[0]=2×0+3×1+1=4y[0]=2\times0+3\times1+1=4
n=2n=2x[0]=1x[0]=133y[2]=2×1+3×3+1=12y[2]=2\times1+3\times3+1=12
n=4n=4x[2]=3x[2]=355y[4]=2×3+3×5+1=22y[4]=2\times3+3\times5+1=22

完整输出为 [4,7,12,17,22],可直接作为实现的检查样例。膨胀改变取样间距,不减少输出位置。

多通道时,把两个标量权重换成矩阵即可。若输入隐藏通道 CC=24,为同时准备门控两支,矩阵形状都是 [48,24],偏置有 48 个数;相应卷积核张量是 [48,24,2]。

实际代码先把 [B,C,T] 的输入左补 dd 点,再做无额外 padding 的有效卷积。F.pad(x,(d,0)) 的两个数依次表示时间轴左侧与右侧;输出仍为 [B,2C,T]。

class CausalConv(nn.Module):
    def __init__(self, channels, dilation):
        super().__init__()
        self.dilation = dilation
        self.conv = nn.Conv1d(channels, 2 * channels, 2,
                              dilation=dilation)

    def forward(self, x):
        return self.conv(F.pad(x, (self.dilation, 0)))

一般核宽 kk、步长为 1 时,最远核位置距离当前 (k−1)d,所以左补同样多的位置可保长。这里 kk=2:补后长度 TT+dd,再被有效卷积扣掉 dd,得到 TT。PyTorch 的 Conv1d 按互相关顺序应用权重,缓存实现必须保持旧→当前的核顺序。Conv1d 文档

3. 多层膨胀卷积的感受野

感受野是结构上可能影响一个输出的输入范围。 单层 d=4d=4 只取两个位置;多层堆叠后,每个中间位置又读取自己的历史,因此要沿原图的连线逐层展开。

WaveNet 原论文图3:膨胀率1、2、4、8的因果依赖网络

来源:WaveNet 图 3(截取)

实线突出一条输出的依赖路径,虚线表示其余连接。 下表按输入到输出的顺序累计覆盖范围;各层核宽均为 22:

已经过的层本层膨胀率 dℓd_\ell新增最远距离(点)连续覆盖点数 RR
输入——11
第 1 层111122
第 2 层222244
第 3 层444488
第 4 层88881616

从图顶端反向追踪时,d=8d=8 先把路径分成两支,后续 4,2,14,2,1 的连接填满中间位置。覆盖扩大,输出序列的长度仍然不变。

在所有层步长为 1、没有池化、额外输入层与输出头不跨时间的前提下:

R=1+∑ℓ=1L(kℓ−1)dℓ.R=1+\sum_{\ell=1}^{L}(k_\ell-1)d_\ell.
感受野:符号与适用范围
数学记号含义、单位与示例
RR按输入采样点计的感受野大小。
LL卷积层数;原论文示意图取 L=4L=4。
ℓ\ell层索引,从 11 到 LL。
kℓk_\ell第 ℓ\ell 层的卷积核宽度;本例各层为 22。
dℓd_\ell第 ℓ\ell 层的膨胀率。
∑ℓ=1L\sum_{\ell=1}^{L}累加每层增加的最远历史距离。
11公式开头的常数项,计入当前位置的输入。

同一公式可以比较普通卷积、论文示意图和教学配置:

配置(核宽均为 22)膨胀率序列感受野计算
四层普通卷积1,1,1,11,1,1,1R=1+4=5R=1+4=5
论文图 31,2,4,81,2,4,8R=1+(1+2+4+8)=16R=1+(1+2+4+8)=16
本教程 12 层[1,2,4,8,16,32] 重复两轮R=1+2×63=127R=1+2\times63=127

在教学配置中,输出位置 n=126n=126 读取 q[0],…,q[126]q[0],\ldots,q[126],预测 q[127]q[127]。输入窗口和目标仍然相差一个索引。

感受野换算成时间时,要区分“点数折算长度”和“首尾时间差”。下面固定 fs=8000f_s=8000 Hz:

比较量计算结果
127 个点折算的长度127/8000127/8000 秒15.87515.875 ms
首尾输入时间戳之差(127−1)/8000(127-1)/8000 秒15.7515.75 ms
125 Hz 信号的一个周期1/1251/125 秒88 ms

本模型的窗口约覆盖两个周期,远短于一句语音。 长时内容需要上游条件等机制提供,不能靠这个小窗口凭空恢复。

最大跨度、实际可达位置和训练后的利用程度,是三个不同概念。

核宽为 22 的组合相对当前输入的可达历史偏移结论
两层 d=2,4d=2,40,2,4,60,2,4,6跨度为 7 点,中间有空洞
三层 d=1,2,4d=1,2,40,1,2,3,4,5,6,70,1,2,3,4,5,6,78 点连续覆盖

表中偏移 22 表示读取 x[n−2]x[n-2]。结构上可达,不保证训练后每一点都被有效利用。

4. 因果性的扰动验证

源码 verify() 用输入扰动检查因果性:

步骤操作预期结果
固定前缀保持输入与条件的索引 0,…,400,\ldots,40 不变两次前向的已知历史相同
修改后缀改动索引 4141 及之后的数据变化只发生在未来区域
比较输出对比输出索引 0,…,400,\ldots,40前缀输出应保持不变
若检查失败排查对称 padding、时间轴归一化等找到未来信息混入的位置

因果性测试不能替代输入与标签的对齐检查。 把当前 qq 错当标签时,网络仍可能通过结构上的因果性测试。

额外检查应得到的结果
打印四点序列的输入与标签与第 1 节的逐位置表一致
将膨胀率由 22 改为 44核宽为 22 时,左 padding 也从 22 改为 44,保持长度与索引正确

正在连接 GitHub 评论…