0
$\begingroup$

In many NLP tasks, to handle batched processing, we pad the inputs in a given batch to match the length of the longest element in the batch. We also get a corresponding mask. Now, my point of confusion is that I am not sure how to handle the masks correctly. In BERT inspired implementations[0], I find it processed as following (self-attention):

def forward(input_tokens, mask):
    """
    input_tokens: batch_size x # input_tokens x hidden_dim
    mask: batch_size x # input_tokens
          The mask value is 1 for valid input and 0 for paddings
    """
    1. Expand the mask to batch_size x #input_tokens x hidden_dim
    2. Make the padded tokens 0: input_tokens[~expanded_mask] = 0
    
    3. input_tokens = input_tokens + positional_embeddings
    4. .. norm layer
    5. For each attention layer:
          input_tokens = attention(input_tokens, mask)

    6. return input_tokens

And then, inside the attention blocks:

def forward(query, key, value, mask):
    1. score = query x key / sqrt(dim)
    2. score = score.masked_fill(~mask, -float('Inf'))
    3. attention = softmax(score)
    4. context = attention * value
    
     ...

Now, in the above pseudo-code, I have applied masking to the input tokens before feeding it to the attention layers [also mentioned by 1]. And I am also applying it when calculating the scores. Is this the right way to do it?

My confusion arises when I see other implementations online where they don't mask the input tokens before processing and handle masks only during scoring [2].

Am kinda stuck on it. Any help is deeply appreciated. Thank you.

[0] https://github.com/huggingface/transformers/blob/v4.45.1/src/transformers/models/hubert/modeling_hubert.py#L1227 (Refer HubertAttention and HubertEncoder classes)

[1] https://ai.stackexchange.com/a/41133/87646

[2] https://pi-tau.github.io/posts/transformer/#multi-head-attention-layer

$\endgroup$

0

You must log in to answer this question.