# Copyright 2021 JD.com, Inc., JD AI
"""
@author: Jianjie Luo, Jingwen Chen
@contact: jianjieluo.sysu@gmail.com, chenjingwen.sysu@gmail.com
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
__all__ = ["SCAttention"]
class BasicAtt(nn.Module):
def __init__(
self,
mid_dims: list,
mid_dropout: float
):
super(BasicAtt, self).__init__()
sequential = []
for i in range(1, len(mid_dims) - 1):
sequential.append(nn.Linear(mid_dims[i - 1], mid_dims[i]))
sequential.append(nn.ReLU())
if mid_dropout > 0:
sequential.append(nn.Dropout(mid_dropout))
self.attention_basic = nn.Sequential(*sequential) if len(sequential) > 0 else None
self.attention_last = nn.Linear(mid_dims[-2], mid_dims[-1])
def forward(self, att_map, att_mask, value1, value2):
if self.attention_basic is not None:
att_map = self.attention_basic(att_map)
attn_weights = self.attention_last(att_map)
attn_weights = attn_weights.squeeze(-1)
if att_mask is not None:
attn_weights = attn_weights.masked_fill(att_mask.unsqueeze(1) == 0, -1e9)
attn_weights = F.softmax(attn_weights, dim=-1)
attn = torch.matmul(attn_weights.unsqueeze(-2), value2).squeeze(-2)
return attn
[docs]class SCAttention(BasicAtt):
[docs] def __init__(
self,
mid_dims: list,
mid_dropout: float
):
super(SCAttention, self).__init__(mid_dims, mid_dropout)
self.attention_last = nn.Linear(mid_dims[-2], 1)
self.attention_last2 = nn.Linear(mid_dims[-2], mid_dims[-1])
[docs] def forward(self, att_map, att_mask, value1, value2):
if self.attention_basic is not None:
att_map = self.attention_basic(att_map)
if att_mask is not None:
att_mask = att_mask.unsqueeze(1)
att_mask_ext = att_mask.unsqueeze(-1)
att_map_pool = torch.sum(att_map * att_mask_ext, -2) / torch.sum(att_mask_ext, -2)
else:
att_map_pool = att_map.mean(-2)
alpha_spatial = self.attention_last(att_map)
alpha_channel = self.attention_last2(att_map_pool)
alpha_channel = torch.sigmoid(alpha_channel)
alpha_spatial = alpha_spatial.squeeze(-1)
if att_mask is not None:
alpha_spatial = alpha_spatial.masked_fill(att_mask == 0, -1e9)
alpha_spatial = F.softmax(alpha_spatial, dim=-1)
if len(alpha_spatial.shape) == 4: # batch_size * head_num * seq_num * seq_num (for xtransformer)
value2 = torch.matmul(alpha_spatial, value2)
else:
value2 = torch.matmul(alpha_spatial.unsqueeze(-2), value2).squeeze(-2)
attn = value1 * value2 * alpha_channel
return attn