Source code for xmodaler.modeling.layers.scattention

# 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