File size: 5,643 Bytes
28e6f98 | 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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import math
from collections import deque
import numpy as np
import torch
from torch import nn
import torch.nn.functional as F
from torchvision import models
class SelfAttention(nn.Module):
"""
A vanilla multi-head masked self-attention layer with a projection at the end.
"""
def __init__(self, n_embd, n_head, attn_pdrop, resid_pdrop):
super().__init__()
assert n_embd % n_head == 0
# key, query, value projections for all heads
self.key = nn.Linear(n_embd, n_embd)
self.query = nn.Linear(n_embd, n_embd)
self.value = nn.Linear(n_embd, n_embd)
# regularization
self.attn_drop = nn.Dropout(attn_pdrop)
self.resid_drop = nn.Dropout(resid_pdrop)
# output projection
self.proj = nn.Linear(n_embd, n_embd)
self.n_head = n_head
def forward(self, x):
B, T, C = x.size()
# calculate query, key, values for all heads in batch and move head forward to be the batch dim
k = self.key(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
q = self.query(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
v = self.value(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
# self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
att = F.softmax(att, dim=-1)
att = self.attn_drop(att)
y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)
y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side
# output projection
y = self.resid_drop(self.proj(y))
return y
class Block(nn.Module):
""" an unassuming Transformer block """
def __init__(self, n_embd, n_head, block_exp, attn_pdrop, resid_pdrop):
super().__init__()
self.ln1 = nn.LayerNorm(n_embd)
self.ln2 = nn.LayerNorm(n_embd)
self.attn = SelfAttention(n_embd, n_head, attn_pdrop, resid_pdrop)
self.mlp = nn.Sequential(
nn.Linear(n_embd, block_exp * n_embd),
nn.ReLU(True), # changed from GELU
nn.Linear(block_exp * n_embd, n_embd),
nn.Dropout(resid_pdrop),
)
def forward(self, x):
B, T, C = x.size()
x = x + self.attn(self.ln1(x))
x = x + self.mlp(self.ln2(x))
return x
class TransFuse_layer(nn.Module):
""" the full GPT language model, with a context size of block_size """
def __init__(self, n_embd, n_head, block_exp, n_layer,
num_anchors, seq_len=1,
embd_pdrop=0.1, attn_pdrop=0.1, resid_pdrop=0.1):
super().__init__()
self.n_embd = n_embd
self.seq_len = seq_len
self.vert_anchors = num_anchors
self.horz_anchors = num_anchors
# positional embedding parameter (learnable), image + lidar
self.pos_emb = nn.Parameter(torch.zeros(1, 2 * seq_len * self.vert_anchors * self.horz_anchors, n_embd))
self.drop = nn.Dropout(embd_pdrop)
# transformer
self.blocks = nn.Sequential(*[Block(n_embd, n_head,
block_exp, attn_pdrop, resid_pdrop)
for layer in range(n_layer)])
# decoder head
self.ln_f = nn.LayerNorm(n_embd)
self.block_size = seq_len
self.apply(self._init_weights)
def get_block_size(self):
return self.block_size
def _init_weights(self, module):
if isinstance(module, nn.Linear):
module.weight.data.normal_(mean=0.0, std=0.02)
if module.bias is not None:
module.bias.data.zero_()
elif isinstance(module, nn.LayerNorm):
module.bias.data.zero_()
module.weight.data.fill_(1.0)
def forward(self, m1, m2):
"""
Args:
m1 (tensor): B*seq_len, C, H, W
m2 (tensor): B*seq_len, C, H, W
"""
bz = m2.shape[0] // self.seq_len
h, w = m2.shape[2:4]
# forward the image model for token embeddings
m1 = m1.view(bz, self.seq_len, -1, h, w)
m2 = m2.view(bz, self.seq_len, -1, h, w)
# pad token embeddings along number of tokens dimension
token_embeddings = torch.cat([m1, m2], dim=1).permute(0,1,3,4,2).contiguous()
token_embeddings = token_embeddings.view(bz, -1, self.n_embd) # (B, an * T, C)
# add (learnable) positional embedding for all tokens
x = self.drop(self.pos_emb + token_embeddings) # (B, an * T, C)
x = self.blocks(x) # (B, an * T, C)
x = self.ln_f(x) # (B, an * T, C)
x = x.view(bz, 2 * self.seq_len, self.vert_anchors, self.horz_anchors, self.n_embd)
x = x.permute(0,1,4,2,3).contiguous() # same as token_embeddings
m1_out = x[:, :self.seq_len, :, :, :].contiguous().view(bz * self.seq_len, -1, h, w)
m2_out = x[:, self.seq_len:, :, :, :].contiguous().view(bz * self.seq_len, -1, h, w)
print("modality1 output:", m1_out.max(), m1_out.min())
print("modality2 output:", m2_out.max(), m2_out.min())
return m1_out, m2_out
if __name__ == "__main__":
feature1 = torch.randn((4, 512, 20, 20))
feature2 = torch.randn((4, 512, 20, 20))
model = TransFuse_layer(n_embd=512, n_head=4, block_exp=4, n_layer=8, num_anchors=20, seq_len=1)
print("TransFuse_layer:", model)
feat1, feat2 = model(feature1, feature2)
print(feat1.shape, feat2.shape)
|