论文地址 论文代码
Motivation: 图卷积包含两个步骤, A ~ X \tilde{A}X A~X和 X W XW XW。 A ~ X \tilde{A}X A~X计算骨架关节点之间的聚合信息,称为空间聚合(spatial aggregation); X W XW XW计算不同通道之间的信息,称为通道关联(channel correlation)。
如下图所示,空间聚合可以分解为分别计算每个通道上的聚合。在当前基于GCN的骨架动作识别方法中,均采用耦合聚合的方式,所有通道共享一个邻接矩阵A。
作者受到CNN的启发,提出在GCN中,也可以像CNN一样使用解耦机制,在不同的通道具有不同的空间聚集核(即邻接矩阵),这种机制在文中被叫做解耦聚合机制。同时作者在文中将耦合聚合的GCN类比为CNN中的“退化深度卷积”,而这种退化深度卷积明显弱于标准深度卷积,所以作者认为当前GCN缺乏解耦机制。解耦机制的示意图如下:
DeCoupling GCN: 考虑到解耦图卷积会引入冗余的邻接矩阵。因此将通道分为g组,组中的通道共享一个可训练的邻接矩阵。g=C时,每个通道都有独立的邻接矩阵;g=1时,又重新退化为耦合图卷积。实验表明g设为8-16组最好,此时参数量只增加了5%-10%。
核心代码:
learn_A = self.DecoupleA.repeat(
1, self.out_channels // self.groups, 1, 1)
norm_learn_A = torch.cat([self.norm(learn_A[0:1, ...]), self.norm(
learn_A[1:2, ...]), self.norm(learn_A[2:3, ...])], 0)
x = torch.einsum('nctw,cd->ndtw', (x0, self.Linear_weight)).contiguous()
x = x + self.Linear_bias
x = self.bn0(x)
n, kc, t, v = x.size()
x = x.view(n, self.num_subset, kc // self.num_subset, t, v)
x = torch.einsum('nkctv,kcvw->nctw', (x, norm_learn_A))
x = self.bn(x)
x += self.down(x0)
x = self.relu(x)
Motivation: 在先前的方法中,GCN的性能并没有随着dropout层的增加而有明显提升。作者推测原因是在GCN中,图卷积混合了节点和邻居节点的特征,当只删除一个节点时,仍然能从其邻居节点中得到该节点的信息,最终导致过拟合。作者提出一种Attention-guided DropGraph机制增强正则化结果。
DropGraph: DropGraph的思想是,当删除某一节点的时候,其邻居节点也要被删除。引入两个参数 γ \gamma γ(节点被丢弃的概率)和K(被丢弃的邻居节点范围)。过程为,在input feature map上,使用概率为 γ \gamma γ的伯努利分布对节点进行随机采样得到Vroot,然后丢弃掉Vroot和距离Vroot最大为K步内的节点。伪代码如下:
实现上,作者设定超参数keep_prob,表示节点被保留的概率,则keep_prob与 γ \gamma γ和K都存在关系。
每个节点的平均度: d a v e = 2 e / n d_{ave}=2e/n dave=2e/n
采样到的节点的第i阶邻域中节点的数量期望: B i = d a v e × ( d a v e − 1 ) i − 1 B_i=d_{ave}\times(d_{ave}-1)^{i-1} Bi=dave×(dave−1)i−1
平均dropsize估计为:
最后计算出的 γ \gamma γ为:
代码中K的取值为1,根据数据集的不同,计算出的dropsize为1.92(25个node)和1.9(20个node)。
Attention-guided drop mechanism: 引入了注意力机制,让注意力权重高的地方有更高的采样概率Vroot。公式为:
这里作者说,有个常见的隐含假设,特征层中节点权重的绝对值(原话为the absolute value of an activation )代表了其重要性。于是通过平均通道维度上的绝对值得到 α \alpha α。
这部分的代码:
input_abs = torch.mean(torch.mean(
torch.abs(input), dim=2), dim=1).detach()
input_abs = input_abs / torch.sum(input_abs) * input_abs.numel()
if self.num_point == 25: # Kinect V2
gamma = (1. - self.keep_prob) / (1 + 1.92)
elif self.num_point == 20: # Kinect V1
gamma = (1. - self.keep_prob) / (1 + 1.9)
else:
gamma = (1. - self.keep_prob) / (1 + 1.92)
warnings.warn('undefined skeleton graph')
M_seed = torch.bernoulli(torch.clamp(
input_abs * gamma, max=1.0)).to(device=input.device, dtype=input.dtype)
M = torch.matmul(M_seed, A)
M[M > 0.001] = 1.0
M[M < 0.5] = 0.0
mask = (1 - M).view(n, 1, 1, self.num_point)
Spatial-temporal ADG: ADG的输入是一个时空特征,所以将ADG分别应用到时间和空间维度。图如下:
第一次写博客,错误之处难免,希望多多交流。