BERT用的LayerNorm可能不是你认为的那个Layer Norm?
?作者 | 王坤澤
單位 | 悉尼大學
研究方向 | NLP
有關 Batch norm 和 Layer norm 的比較可以算上是算法領域的八股文了,為什么 BERT 不用 batch norm 而用 layer norm 的問題都被問爛了,知乎上隨便一搜都有很多人講解 BN 和 LN 的區別。通常來說大家都會給這張圖:
▲ BN vs LN
大家會說,針對 CV 和 NLP 兩種問題,這里的三個維度表示的信息不同:
如果只看 NLP 問題,假設我們的 batch 是(2,3,4)的,也就是 batch_size = 2, seq_length = 3, dim = 4 的,假設第一個句子是 w1 w2 w3,第二個句子是 w4 w5 w6,那么這個 tensor 可以寫為:
[[w11,?w12,?w13,?w14],?[w21,?w22,?w23,?w24],?[w31,?w32,?w33,?w34] [w41,?w42,?w43,?w44],?[w51,?w52,?w53,?w54],?[w61,?w62,?w63,?w64]]我們發現,如果是 BN 的話,會對同一個 batch 里對應位置上的 token 求平均值,也就是說 (w11+w12+w13+w14+w41+w42+w43+w44)/8是其中一個 mean,一共會求出 3 個 mean,也就是上圖里 C 個(seq_length)個 mean。
但是如果是 LN 的話,看起來是對每個 sample 里的所有 feature 求 mean,也就是(w11+w12+w13+w14+w21+w22+w23+w24+w31+w32+w33+w34)/12,可以求出一共 2 個 mean,也就是圖里 N(batch_size)個 mean。
我一直對這個計算深信不疑,認為 BERT 里也是這樣的實現,但是有一天我在這個回答看到了?@猛猿?的這個回答:為什么 Transformer 要用 LayerNorm?[1]?其中作者給出了兩張圖:
▲ 都是 Layer norm 但是卻不一樣
左圖和我們認為的 LN 一致,也是我一直認為的 LN,但是右圖卻是在一個 token 上求平均,帶回我們原來的問題,對于一個(2,3,4)的 tensor,(w11+w12+w13+w14)/4 是一個 mean,一共會有 2*3=6 個 mean。
那到底,BERT 里是 batch_size個mean(左圖的計算方法),還是 batch_size*seq_length 個 mean(右圖的計算方法)呢?我們得看看源碼。
BERT 或者說 transformer encoder 的 pytorch 源碼比較著名的應該是 torch 自帶的 transformer encoder 和 hugging face 自己寫的,我們一個個看。
#?torch.nn.TransformerEncoderLayer #?https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/transformer.py #?412行 self.norm1?=?LayerNorm(d_model,?eps=layer_norm_eps,?**factory_kwargs)#?huggingface?bert_model #?https://github.com/huggingface/transformers/blob/3223d49354e41dfa44649a9829c7b09013ad096e/src/transformers/models/bert/modeling_bert.py#L378 #?382行 self.LayerNorm?=?nn.LayerNorm(config.hidden_size,?eps=config.layer_norm_eps)可以看到,無論是火炬自帶還是捧著臉復現的 transformer encoder 或者叫 bert layer,里面用的都是 torch 自己的 nn.LayerNorm,并且參數都是對應為 768 的 hidden dimension(變形金剛把它叫做 d_model,波特把它叫做 hidden_size)。
那我們看看 nn.LayerNorm(dim) 是一個什么效果,以下代碼修改自?Understanding torch.nn.LayerNorm in nlp [2]
import?torchbatch_size,?seq_size,?dim?=?2,?3,?4 embedding?=?torch.randn(batch_size,?seq_size,?dim)layer_norm?=?torch.nn.LayerNorm(dim,?elementwise_affine?=?False) print("y:?",?layer_norm(embedding))eps:?float?=?0.00001 mean?=?torch.mean(embedding[:,?:,?:],?dim=(-1),?keepdim=True) var?=?torch.square(embedding[:,?:,?:]?-?mean).mean(dim=(-1),?keepdim=True)print("mean:?",?mean.shape) print("y_custom:?",?(embedding[:,?:,?:]?-?mean)?/?torch.sqrt(var?+?eps))在以上代碼中,我先生成了一個 emb,然后使用 nn.LayerNorm(dim) 計算它 layer nrom 后的結果,同時,我手動計算了一個在最后一維上的 mean(也就是說我的 mean 的維度是 2*3,也就是一共 6 個 mean),如果這樣算出來的結果和我調 nn.LayerNorm(dim) 一致,那就說明,nn.LayerNorm(dim) 會給我們 (batch_size*seq_length) 個 mean,也就是剛才上圖里右邊的方法。計算后結果如下:
y:??tensor([[[-0.2500,??1.0848,??0.6808,?-1.5156],[-1.1630,?-0.7052,??1.3840,??0.4843],[-1.3510,??0.4520,?-0.4354,??1.3345]],[[?0.4372,?-0.4610,??1.3527,?-1.3290],[?0.2282,??1.3853,?-0.2037,?-1.4097],[-0.9960,?-0.6184,?-0.0059,??1.6203]]]) mean:??torch.Size([2,?3,?1]) y_custom:??tensor([[[-0.2500,??1.0848,??0.6808,?-1.5156],[-1.1630,?-0.7052,??1.3840,??0.4843],[-1.3510,??0.4520,?-0.4354,??1.3345]],[[?0.4372,?-0.4610,??1.3527,?-1.3290],[?0.2282,??1.3853,?-0.2037,?-1.4097],[-0.9960,?-0.6184,?-0.0059,??1.6203]]])確實一致,也就是說,至少在 torch 自帶和 hugging face 復現的 bert 里,layernorm 實際上和右圖一致是對每個 token 的 feature 單獨求 mean。
那么如果我們想像左圖里求出 batch_size 個 mean,怎么用 nn.LayerNorm 實現呢?只需要修改 nn.LayerNorm 的參數為 nn.LayerNorm([seq_size,dim]) 即可,代碼如下,大家可以跑一下,發現這樣和求 batch_size 個 mean 是一致的:
import?torchbatch_size,?seq_size,?dim?=?2,?3,?4 embedding?=?torch.randn(batch_size,?seq_size,?dim)layer_norm?=?torch.nn.LayerNorm([seq_size,dim],?elementwise_affine?=?False) print("y:?",?layer_norm(embedding))eps:?float?=?0.00001 mean?=?torch.mean(embedding[:,?:,?:],?dim=(-2,-1),?keepdim=True) var?=?torch.square(embedding[:,?:,?:]?-?mean).mean(dim=(-2,-1),?keepdim=True)print("mean:?",?mean.shape) print("y_custom:?",?(embedding[:,?:,?:]?-?mean)?/?torch.sqrt(var?+?eps))最后一個問題,按圖右這么求,那豈不是和 InstanceNorm 一樣了嗎?同樣我做了一個代碼實驗:
from?torch.nn?import?InstanceNorm2d instance_norm?=?InstanceNorm2d(3,?affine=False) x?=?torch.randn(2,?3,?4) output?=?instance_norm(x.reshape(2,3,4,1))?#InstanceNorm2D需要(N,C,H,W)的shape作為輸入 print(output.reshape(2,3,4))layer_norm?=?torch.nn.LayerNorm(4,?elementwise_affine?=?False) print(layer_norm(x))可以跑一下,發現確實是一致的。
結論:BERT 里的 layernorm 在 torch 自帶的? transformer encoder 和 hugging face 復現的 bert 里,實際上都是在做 InstanceNorm。
那么,最開始 Vaswani 在 attention is all you need 里提出的使用 layernorm 是什么呢?tf.tensor2tensor 的作者也是 Vaswani,那么我認為 tf.tensor2tensor 應該是符合作者最初的源碼設計的,通過翻閱源碼(看了無數的文件,大家可以試試,真的很多,各種 function 封裝...),我確認了作者自己的代碼里的 layernorm 使用的參數也是最后一維的 dimension,那么也就是說,原作者本質上也是用的 InstanceNorm。
最后想問問,InstanceNorm 是 LayerNorm 的一種嗎?為啥我沒看到相關的說法?
參考文獻
[1]?https://www.zhihu.com/question/487766088/answer/2309239401
[2]?https://stackoverflow.com/questions/70065235/understanding-torch-nn-layernorm-in-nlp
更多閱讀
#投 稿?通 道#
?讓你的文字被更多人看到?
如何才能讓更多的優質內容以更短路徑到達讀者群體,縮短讀者尋找優質內容的成本呢?答案就是:你不認識的人。
總有一些你不認識的人,知道你想知道的東西。PaperWeekly 或許可以成為一座橋梁,促使不同背景、不同方向的學者和學術靈感相互碰撞,迸發出更多的可能性。?
PaperWeekly 鼓勵高校實驗室或個人,在我們的平臺上分享各類優質內容,可以是最新論文解讀,也可以是學術熱點剖析、科研心得或競賽經驗講解等。我們的目的只有一個,讓知識真正流動起來。
📝?稿件基本要求:
? 文章確系個人原創作品,未曾在公開渠道發表,如為其他平臺已發表或待發表的文章,請明確標注?
? 稿件建議以?markdown?格式撰寫,文中配圖以附件形式發送,要求圖片清晰,無版權問題
? PaperWeekly 尊重原作者署名權,并將為每篇被采納的原創首發稿件,提供業內具有競爭力稿酬,具體依據文章閱讀量和文章質量階梯制結算
📬?投稿通道:
? 投稿郵箱:hr@paperweekly.site?
? 來稿請備注即時聯系方式(微信),以便我們在稿件選用的第一時間聯系作者
? 您也可以直接添加小編微信(pwbot02)快速投稿,備注:姓名-投稿
△長按添加PaperWeekly小編
🔍
現在,在「知乎」也能找到我們了
進入知乎首頁搜索「PaperWeekly」
點擊「關注」訂閱我們的專欄吧
·
總結
以上是生活随笔為你收集整理的BERT用的LayerNorm可能不是你认为的那个Layer Norm?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka send方法详解 (同步异步
- 下一篇: 再谈谷歌搜索引擎使用技巧