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操作。