NOTE: The `height` and `weight` depend on the inputs' size and its resulting size after being processed by the vision network.
| 194 | |
| 195 | |
| 196 | class SpatialBasis: |
| 197 | # TODO: Implement Spatial. |
| 198 | """ |
| 199 | NOTE: The `height` and `weight` depend on the inputs' size and its resulting size |
| 200 | after being processed by the vision network. |
| 201 | """ |
| 202 | |
| 203 | def __init__(self, height=27, width=20, channels=64): |
| 204 | h, w, d = height, width, channels |
| 205 | |
| 206 | p_h = torch.mul(torch.arange(1, h+1).unsqueeze(1).float(), torch.ones(1, w).float()) * (np.pi / h) |
| 207 | p_w = torch.mul(torch.ones(h, 1).float(), torch.arange(1, w+1).unsqueeze(0).float()) * (np.pi / w) |
| 208 | |
| 209 | # NOTE: I didn't quite see how U,V = 4 made sense given that the authors form the spatial |
| 210 | # basis by taking the outer product of the values. Still, I think what I have is aligned with what |
| 211 | # they did, but I am less confident in this step. |
| 212 | U = V = 8 # size of U, V. |
| 213 | u_basis = v_basis = torch.arange(1, U+1).unsqueeze(0).float() |
| 214 | a = torch.mul(p_h.unsqueeze(2), u_basis) |
| 215 | b = torch.mul(p_w.unsqueeze(2), v_basis) |
| 216 | out = torch.einsum('hwu,hwv->hwuv', torch.cos(a), torch.cos(b)).reshape(h, w, d) |
| 217 | self.S = out |
| 218 | |
| 219 | def __call__(self, X): |
| 220 | # Stack the spatial bias (for each batch) and concat to the input. |
| 221 | batch_size = X.size()[0] |
| 222 | S = torch.stack([self.S] * batch_size).to(X.device) |
| 223 | return torch.cat([X, S], dim=3) |
| 224 | |
| 225 | |
| 226 | def spatial_softmax(A): |