Encoding Layer: a learnable residual encoder. Input is of shape (batch_size, channels, height, width). Output is of shape (batch_size, num_codes, channels). Args: channels: dimension of the features or feature channels num_codes: number of code words
| 4 | |
| 5 | |
| 6 | class Encoding(nn.Module): |
| 7 | """Encoding Layer: a learnable residual encoder. |
| 8 | |
| 9 | Input is of shape (batch_size, channels, height, width). |
| 10 | Output is of shape (batch_size, num_codes, channels). |
| 11 | |
| 12 | Args: |
| 13 | channels: dimension of the features or feature channels |
| 14 | num_codes: number of code words |
| 15 | """ |
| 16 | |
| 17 | def __init__(self, channels, num_codes): |
| 18 | super(Encoding, self).__init__() |
| 19 | # init codewords and smoothing factor |
| 20 | self.channels, self.num_codes = channels, num_codes |
| 21 | std = 1. / ((num_codes * channels)**0.5) |
| 22 | # [num_codes, channels] |
| 23 | self.codewords = nn.Parameter( |
| 24 | torch.empty(num_codes, channels, |
| 25 | dtype=torch.float).uniform_(-std, std), |
| 26 | requires_grad=True) |
| 27 | # [num_codes] |
| 28 | self.scale = nn.Parameter( |
| 29 | torch.empty(num_codes, dtype=torch.float).uniform_(-1, 0), |
| 30 | requires_grad=True) |
| 31 | |
| 32 | @staticmethod |
| 33 | def scaled_l2(x, codewords, scale): |
| 34 | num_codes, channels = codewords.size() |
| 35 | batch_size = x.size(0) |
| 36 | reshaped_scale = scale.view((1, 1, num_codes)) |
| 37 | expanded_x = x.unsqueeze(2).expand( |
| 38 | (batch_size, x.size(1), num_codes, channels)) |
| 39 | reshaped_codewords = codewords.view((1, 1, num_codes, channels)) |
| 40 | |
| 41 | scaled_l2_norm = reshaped_scale * ( |
| 42 | expanded_x - reshaped_codewords).pow(2).sum(dim=3) |
| 43 | return scaled_l2_norm |
| 44 | |
| 45 | @staticmethod |
| 46 | def aggregate(assignment_weights, x, codewords): |
| 47 | num_codes, channels = codewords.size() |
| 48 | reshaped_codewords = codewords.view((1, 1, num_codes, channels)) |
| 49 | batch_size = x.size(0) |
| 50 | |
| 51 | expanded_x = x.unsqueeze(2).expand( |
| 52 | (batch_size, x.size(1), num_codes, channels)) |
| 53 | encoded_feat = (assignment_weights.unsqueeze(3) * |
| 54 | (expanded_x - reshaped_codewords)).sum(dim=1) |
| 55 | return encoded_feat |
| 56 | |
| 57 | def forward(self, x): |
| 58 | assert x.dim() == 4 and x.size(1) == self.channels |
| 59 | # [batch_size, channels, height, width] |
| 60 | batch_size = x.size(0) |
| 61 | # [batch_size, height x width, channels] |
| 62 | x = x.view(batch_size, self.channels, -1).transpose(1, 2).contiguous() |
| 63 | # assignment_weights: [batch_size, channels, num_codes] |