| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| class InstructTimeAttention(nn.Module): |
| def __init__( |
| self, |
| dim, |
| num_heads=8, |
| qkv_bias=False, |
| qk_norm=False, |
| attn_drop=0., |
| proj_drop=0., |
| norm_layer=nn.LayerNorm |
| ): |
| super().__init__() |
| assert dim % num_heads == 0, 'dim should be divisible by num_heads' |
| self.num_heads = num_heads |
| self.head_dim = dim // num_heads |
| self.scale = self.head_dim ** -0.5 |
| self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity() |
| self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity() |
| |
| self.query_proj = nn.Linear(dim, dim, bias=qkv_bias) |
| self.key_proj = nn.Linear(dim, dim, bias=qkv_bias) |
| self.value_proj = nn.Linear(dim, dim, bias=qkv_bias) |
| self.channel_query_proj = nn.Linear(dim, dim, bias=qkv_bias) |
| self.channel_key_proj = nn.Linear(dim, dim, bias=qkv_bias) |
| self.channel_value_proj = nn.Linear(dim, dim, bias=qkv_bias) |
|
|
| |
| self.attn_drop = nn.Dropout(attn_drop) |
| self.proj = nn.Linear(dim, dim) |
|
|
| def forward(self, query, memory, mask=None): |
| """ |
| Args: |
| query (torch.Tensor): Query tensor of shape (batch_size, L_q, d). |
| memory (torch.Tensor): Memory tensor of shape (batch_size, L_p, V, d). |
| Returns: |
| torch.Tensor: Output tensor of shape (batch_size, L_q, d). |
| """ |
| batch_size, L_q, d = query.size() |
| _, L_p, V, _ = memory.size() |
|
|
| |
| query_channel = self.channel_query_proj(query) |
| key_channel = self.channel_key_proj(memory).mean(dim=1) |
| value_channel = self.channel_value_proj(memory) |
|
|
| |
| query_channel = self.q_norm(query_channel.view(batch_size, self.num_heads, L_q, self.head_dim)) |
| key_channel = self.k_norm(key_channel.view(batch_size, self.num_heads, V, self.head_dim)) |
| value_channel = value_channel.view(batch_size, self.num_heads, L_p, V, self.head_dim) |
| |
|
|
| |
| channel_scores = torch.matmul(query_channel, key_channel.transpose(-2, -1)) / self.scale |
| channel_weights = F.softmax(channel_scores, dim=-1) |
| channel_output = torch.einsum('bnqv,bnlvd->bnld', channel_weights, value_channel) |
|
|
| |
| channel_output = channel_output.transpose(1, 2).contiguous() |
| memory_aggregated = channel_output.view(batch_size, L_p, d) |
|
|
| |
| Q = self.query_proj(query) |
| K = self.key_proj(memory_aggregated) |
| V = self.value_proj(memory_aggregated) |
|
|
| |
| Q = self.q_norm(Q.view(batch_size, L_q, self.num_heads, self.head_dim).transpose(1, 2)) |
| K = self.k_norm(K.view(batch_size, L_p, self.num_heads, self.head_dim).transpose(1, 2)) |
| V = V.view(batch_size, L_p, self.num_heads, self.head_dim).transpose(1, 2) |
|
|
| |
| attention_scores = torch.matmul(Q, K.transpose(-2, -1)) / self.scale |
| attention_weights = F.softmax(attention_scores, dim=-1) |
| attention_output = torch.matmul(attention_weights, V) |
|
|
| |
| attention_output = attention_output.transpose(1, 2).contiguous() |
| output = attention_output.view(batch_size, L_q, d) |
|
|
| |
| output = self.proj(output) |
|
|
| return output |
|
|
| if __name__ == '__main__': |
| |
| batch_size = 8 |
| seq_len = 10 |
| mem_len = 20 |
| mem_channels = 5 |
| dim = 64 |
| num_heads = 4 |
|
|
| |
| query = torch.rand(batch_size, seq_len, dim) |
| memory = torch.rand(batch_size, mem_len, mem_channels, dim) |
|
|
| |
| model = InstructTimeAttention(dim=dim, num_heads=num_heads, qkv_bias=True) |
|
|
| |
| output = model(query, memory) |
|
|
| |
| print("Output shape:", output.shape) |
|
|