|
from typing import Tuple |
|
|
|
import torch |
|
from torch import nn |
|
|
|
|
|
class AlignmentNetwork(torch.nn.Module): |
|
"""Aligner Network for learning alignment between the input text and the model output with Gaussian Attention. |
|
|
|
:: |
|
|
|
query -> conv1d -> relu -> conv1d -> relu -> conv1d -> L2_dist -> softmax -> alignment |
|
key -> conv1d -> relu -> conv1d -----------------------^ |
|
|
|
Args: |
|
in_query_channels (int): Number of channels in the query network. Defaults to 80. |
|
in_key_channels (int): Number of channels in the key network. Defaults to 512. |
|
attn_channels (int): Number of inner channels in the attention layers. Defaults to 80. |
|
temperature (float): Temperature for the softmax. Defaults to 0.0005. |
|
""" |
|
|
|
def __init__( |
|
self, |
|
in_query_channels=80, |
|
in_key_channels=512, |
|
attn_channels=80, |
|
temperature=0.0005, |
|
): |
|
super().__init__() |
|
self.temperature = temperature |
|
self.softmax = torch.nn.Softmax(dim=3) |
|
self.log_softmax = torch.nn.LogSoftmax(dim=3) |
|
|
|
self.key_layer = nn.Sequential( |
|
nn.Conv1d( |
|
in_key_channels, |
|
in_key_channels * 2, |
|
kernel_size=3, |
|
padding=1, |
|
bias=True, |
|
), |
|
torch.nn.ReLU(), |
|
nn.Conv1d(in_key_channels * 2, attn_channels, kernel_size=1, padding=0, bias=True), |
|
) |
|
|
|
self.query_layer = nn.Sequential( |
|
nn.Conv1d( |
|
in_query_channels, |
|
in_query_channels * 2, |
|
kernel_size=3, |
|
padding=1, |
|
bias=True, |
|
), |
|
torch.nn.ReLU(), |
|
nn.Conv1d(in_query_channels * 2, in_query_channels, kernel_size=1, padding=0, bias=True), |
|
torch.nn.ReLU(), |
|
nn.Conv1d(in_query_channels, attn_channels, kernel_size=1, padding=0, bias=True), |
|
) |
|
|
|
self.init_layers() |
|
|
|
def init_layers(self): |
|
torch.nn.init.xavier_uniform_(self.key_layer[0].weight, gain=torch.nn.init.calculate_gain("relu")) |
|
torch.nn.init.xavier_uniform_(self.key_layer[2].weight, gain=torch.nn.init.calculate_gain("linear")) |
|
torch.nn.init.xavier_uniform_(self.query_layer[0].weight, gain=torch.nn.init.calculate_gain("relu")) |
|
torch.nn.init.xavier_uniform_(self.query_layer[2].weight, gain=torch.nn.init.calculate_gain("linear")) |
|
torch.nn.init.xavier_uniform_(self.query_layer[4].weight, gain=torch.nn.init.calculate_gain("linear")) |
|
|
|
def forward( |
|
self, queries: torch.tensor, keys: torch.tensor, mask: torch.tensor = None, attn_prior: torch.tensor = None |
|
) -> Tuple[torch.tensor, torch.tensor]: |
|
"""Forward pass of the aligner encoder. |
|
Shapes: |
|
- queries: :math:`[B, C, T_de]` |
|
- keys: :math:`[B, C_emb, T_en]` |
|
- mask: :math:`[B, T_de]` |
|
Output: |
|
attn (torch.tensor): :math:`[B, 1, T_en, T_de]` soft attention mask. |
|
attn_logp (torch.tensor): :math:`[ßB, 1, T_en , T_de]` log probabilities. |
|
""" |
|
key_out = self.key_layer(keys) |
|
query_out = self.query_layer(queries) |
|
attn_factor = (query_out[:, :, :, None] - key_out[:, :, None]) ** 2 |
|
attn_logp = -self.temperature * attn_factor.sum(1, keepdim=True) |
|
if attn_prior is not None: |
|
attn_logp = self.log_softmax(attn_logp) + torch.log(attn_prior[:, None] + 1e-8) |
|
|
|
if mask is not None: |
|
attn_logp.data.masked_fill_(~mask.bool().unsqueeze(2), -float("inf")) |
|
|
|
attn = self.softmax(attn_logp) |
|
return attn, attn_logp |
|
|