-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathlayers_common.py
89 lines (75 loc) · 2.75 KB
/
layers_common.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.modules.batchnorm as _BatchNorm
from functools import partial
import math
from typing import Tuple, List
class EMAU(nn.Module):
'''The Expectation-Maximization Attention Unit (EMAU).
Arguments:
c (int): The input and output channel number.
k (int): The number of the bases.
stage_num (int): The iteration number for EM.
'''
def __init__(self, c, k, stage_num=1, is_normalized=False):
super(EMAU, self).__init__()
self.stage_num = stage_num
self.is_normalized = is_normalized
mu = torch.Tensor(1, c, k)
mu.normal_(0, math.sqrt(2. / k)) # Init with Kaiming Norm.
mu = self._l2norm(mu, dim=1)
self.register_buffer('mu', mu)
self.conv1 = nn.Conv1d(c, c, 1)
self.conv2 = nn.Sequential(
nn.Conv1d(c, c, 1, bias=False),
nn.BatchNorm1d(c))
for m in self.modules():
if isinstance(m, nn.Conv1d):
n = m.kernel_size[0] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm1d):
m.weight.data.fill_(1)
if m.bias is not None:
m.bias.data.zero_()
def forward(self, x):
"""
:param x: BxCxN
:return: BxCxN
"""
idn = x
# The first 1x1 conv
x = self.conv1(x)
# The EM Attention
b, c, n = x.size()
mu = self.mu.repeat(b, 1, 1) # b * c * k
with torch.no_grad():
for i in range(self.stage_num):
x_t = x.permute(0, 2, 1) # b * n * c
z = torch.bmm(x_t, mu) # b * n * k
z = F.softmax(z, dim=2) # b * n * k
z_ = z / (1e-6 + z.sum(dim=1, keepdim=True))
mu = torch.bmm(x, z_) # b * c * k
mu = self._l2norm(mu, dim=1)
z_t = z.permute(0, 2, 1) # b * k * n
x = mu.matmul(z_t) # b * c * n
x = F.relu(x, inplace=True)
# The second 1x1 conv
x = self.conv2(x)
x = x + idn
# x = F.relu(x, inplace=True)
return x, mu
def _l2norm(self, inp, dim):
'''Normlize the inp tensor with l2-norm.
Returns a tensor where each sub-tensor of input along the given dim is
normalized such that the 2-norm of the sub-tensor is equal to 1.
Arguments:
inp (tensor): The input tensor.
dim (int): The dimension to slice over to get the ssub-tensors.
Returns:
(tensor) The normalized tensor.
'''
if self.is_normalized:
return inp / (1e-6 + inp.norm(dim=dim, keepdim=True))
else:
return inp