1. 程式人生 > >Mask矩陣理解

Mask矩陣理解

是什麼

mask矩陣是什麼?是一個由0和1組成的矩陣。一個例子是,在自然語言處理(NLP)中,句子的長度是不等長的,但因為我們經常將句子組成mini-batch用以訓練,因此那些長度較短的句子都會在句尾進行填充0,也即padding的操作。一個mask矩陣即用以指示哪些是真正的資料,哪些是padding。如:圖片來源:Theano:LSTM原始碼解析

其中mask矩陣中1代表真實資料;0代表padding資料。

為什麼

為什麼要使用mask矩陣?使用mask矩陣是為了讓那些被mask掉的tensor不會被更新。考慮一個tensor T的size(a,b),同樣大小的mask矩陣M,相乘後,在反向回傳的時候在T對應mask為0的地方,0的梯度仍為0。因此不會被更新。

怎麼做

接下來介紹幾種(可能不全)使用mask的場景。

對輸入進行mask

考慮NLP中常見的句子不等長的情況。設我們的輸入的batch I:(batch_size,max_seqlen),我們在過一層Embedding層之前,在過了一層Embedding層,則有 E:(batch_size,max_seqlen,embed_dim),如果我們希望Embedding是更新的(比如我們的Embedding是隨機初始化的,那當然Embedding需要更新),但我們又不希望padding更新。一種方法即令E與M相乘。其中M是mask矩陣(batch_size,max_seqlen,1) (1是因為要broadcast),這樣在Embedding更新梯度時,因為mask矩陣的關係,padding位置上的梯度就是0。當然在Pytorch中還可以直接顯式地寫:

1
self.embedding = nn.Embedding(vocab_size, embed_dim,padding_idx=0)

而此時應當將padding顯式新增到詞典的第一個。

對模型中間進行mask

一個很經典的場景就是dropout。對於引數矩陣W:(h,w),同樣大小的mask矩陣M,在前向傳播時令W’=W*M,則在反向傳播時,M中為0的部分不被更新。當然,我們可以直接呼叫PyTorch中的包nn.Dropout()

123
m = nn.Dropout(p=0.2)input = torch.randn(20, 16)output = m(input)

對loss進行mask

考慮NLP中的language model,每個詞都需要預測下一個詞,在一個batch中句子總是有長有短,對於一個短句,此時在計算loss的時候,會出現這樣的場景:<pad>

詞要預測下一個<pad>詞。舉個例子:三個句子[a,b,c,d],[e,f,g],[h,i],在組成batch後,會變成X:

a b c d
e f g <pad>
h i <pad> <pad>

Y:

b c d <pad>
f g <eos> <pad>
i <eos> <pad> <pad>

X是輸入,Y是預測。那麼從第三行可以看出,<pad>在預測下一個<pad>。這顯然是有問題的。一種解決方案就是使用mask矩陣,在loss的計算時,將那些本不應該計算的mask掉,使得其loss為0,這樣就不會反向回傳了。具體實踐:在PyTorch中,以CrossEntropy為例:

12
class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction=’elementwise_mean’

如果reduction=None則會返回一個與輸入同樣大小的矩陣。在與mask矩陣相乘後,再對新矩陣進行mean操作。在PyTorch實踐上還可以可以這麼寫:

123
masked_outputs = torch.masked_select(dec_outputs, mask)masked_targets = torch.masked_select(targets, mask)loss = my_criterion(masked_outputs, masked_targets)

另一種更為簡單的解決方案是,直接在CrossEntropy中設ignore_index=0,這樣,在計算loss的時候,發現target=0時,會自動不對其進行loss的計算。其本質和mask矩陣是一致的。

總結

mask矩陣可以用在任何地方,只要希望與之相乘的tensor相對應的地方不更新就可以進行mask操作。