bert之我見-attention

2021-02-08 小小挖掘機

我想現在NLP領域中,不知道bert的已經少之又少了,而bert的講解文章也已經有了很多,這裡我談一下我最近學習得到的理解。事先說明,對bert和transformer完全不懂的人看這個完全不知道咋回事的,想要看下面這些請先對這兩個玩意有初步的理解。(風格依舊,不會重複別人寫過的東西)

今天給大家談的是bert中的attention,通篇可能不會有太多篇幅對著bert講,而是把attention講懂,然後再去看bert中的attention幹了什麼,這樣大家能對bert中的attention,甚至整個注意力機制有更加深刻的理解。

從機器翻譯開始

bert的核心之一在於使用了transformer中的encoder,而transformer的架構則來源於機器翻譯中的seq2seq,因此,要完全理解bert,還是要從機器翻譯開始理解。

首先我們看seq2seq。用圖來說比較容易,簡單地,由於我們只要知道基本結構,所以用RNN來解釋更為合適。

從上面的圖其實可以看到,整個seq2seq其實就是一個encoder-decoder的模式,這個就和transformer很像了,這個就是目前機器翻譯目前的一套主流架構。Encoder負責將原始信息進行編碼匯總,整理成模型能夠理解信息,後續如果有了attention之後還能提取關鍵型信息;Decoder則是將信息整合,輸出翻譯結果。

attention機制

我認為attention機制談的最清楚的應該是張俊林在2017年寫的《深度學習中的注意力模型》,據說被刊登在《程式設計師》上了,很厲害的亞子。

首先,大家這麼理解,何為注意力,在模型上,大家可以理解為,詞彙比較關鍵的對應位置,權重會比較高,相反不重要的位置權重就比較低。深入地,這個重要性的衡量,在機器翻譯裡,是依賴於翻譯結果中對應的位置的,例如現在翻譯到了一個名詞的位置,那重要性更高的應該就是原句中名詞的部分,因此對於預測句子中的每個的位置,其實都應該有這個位置針對原句所有詞彙的重要性衡量。

按照RNN的邏輯,預測應該是這樣預測的,每個預測點與前面的位置有關,而且在這裡看來是平權的,即C是固定的沒有重點的:

而如果是注意力機制,那就會是這樣的,C是變化的:

至於這個C1,C2,C3是怎麼來的,看這個:

Y1有自己的C1,Y2有自己的C2,於是就造就了注意力機制。

那麼下一個問題就是,怎麼去構造這個根據位置變化的權重向量C了。來看看這個圖:

我對於特定詞彙位置附近的詞進行attention計算,這裡使用的是RNN的輸出,用這個輸出計算了Attention scores之後進行歸一化形成分布。然後我們來看看公式的描述吧。

我們直接先從Decoder的隱含層公式看一下吧。

第i個位置的隱含層的輸出和前一個位置的隱含層輸出、前一個位置的預測結果以及encoder結果結合,然後我們從這個ci往前推。encoder的結果是基於attenton結果導出的權重向量以及encoder的隱含層向量求得的,可以理解為一個加權求和,所以是這樣的:

h是encoder的隱含層向量,這個就與你選用的模型有關了,所以問題就落到了這個alpha上了。然後我們知道這個alpha實質上是一個標準化向量,所以裡面肯定是包裹了一層標準化函數的,所以是這樣的:

一層一層解剖下來,就到了這個e的頭上了,值得注意的是,這裡面需要區分開e對應的兩個下角標,前者是decoder對應的位置,後者是encoder對應的位置。所以問題就到了這個e上了。

首先根據attention定義,對decoder特定位置衡量encoder各個位置的重要性,到了這裡其實就是decoder和encoder之間的相關性了,當然的越相關這裡就越重要對吧,所以說白了就是衡量相關性,硬要嚴謹一些,其實就是去構造兩者的一個得分函數。

這麼看說白了還是相似度吧。這個相似度描述其實就回到了很原始的幾大相似度衡量模型了,此處就不多談啦:

回過頭來,總結一下Attention的思想,就成了這樣:

衡量輸入和輸出兩者的相似度作為權重,做隱含層的加權平均,就這麼簡單。來看個直觀點的圖吧,這麼看大家是不是就知道怎麼回事了:

這裡就引出了attention的三個重要角色,query、key、value,key、value對應原句,query是翻譯句,value是隱含層向量。後續討論attention模型,就只需要搞清楚這三個是啥,這個模型你就理解了一大半了(額,其實我倒是感覺很多文章裡反而沒在各種應用,包括self attention,裡面把這三個角色分別是什麼說清楚)。

Transformer

Transformer就是BERT發明的一大功臣,這裡面,實際上就是使用了self-attetion,即自注意力機制。

何為自注意力機制,就是自己對自己,這個非常好理解,但是,自己對自己裡面的計算又是什麼樣的,大家有仔細想過嗎?是每個位點自己對自己,還是自己這句對應自己這句?很明顯,是後面的,用機器翻譯的方式理解,attention說白了就是把輸入句和輸出句都當做是自己,那麼這裡計算的重要性權重,就是每個單詞在整個句子中的重要性了(我的天這不就是term weighting嗎?)

然後現在回頭來看,k、q、v就很明確了。

k、q、v對應的其實都是一套,而不是一個,都是一個向量空間裡面的,只不過計算的時候取的不是一個位點而已。

這裡也可以看到,大家理解了k、q、v之後,attention模型的應用你就非常明白了。

這裡也告訴大家一個看k、q、v很快的技巧,那就是——看!源!碼!

tranformer的源碼中(https://github.com/Kyubyong/transformer/blob/master/model.py),對encodeer的attentiion是這樣的,非常一目了然。

enc = multihead_attention(queries=enc,

keys=enc,

values=enc,

key_masks=src_masks,

num_heads=self.hp.num_heads,

dropout_rate=self.hp.dropout_rate,

training=training,

causality=False)

而decoder的是這樣的。

dec = multihead_attention(queries=dec,

keys=dec,

values=dec,

key_masks=tgt_masks,

num_heads=self.hp.num_heads,

dropout_rate=self.hp.dropout_rate,

training=training,

causality=True,

scope="self_attention")


# Vanilla attention

dec = multihead_attention(queries=dec,

keys=memory,

values=memory,

key_masks=src_masks,

num_heads=self.hp.num_heads,

dropout_rate=self.hp.dropout_rate,

training=training,

causality=False,

scope="vanilla_attention")

可以看到這裡整了兩次,而這兩者的輸入是不同的,每層的decoder裡面實際上有兩個attention,第一個很明顯就是self-attention了,第二個的key和values是memory,至於這個memory是什麼,我們往前看。

# memory: encoder outputs. (N, T1, d_model)

這句話就在decoder的函數定義下的一行注釋裡,看到這個完全足夠了。由此你其實就非常明白transformer的attention機制是怎麼用的了,看看這圖是不是匹配的,而裡面怎麼整的是不是也更清楚了。

bert中的attention

終於談到bert了,這裡就可以開始談bert中的attention了,這裡用源碼來講更清楚,實際上,我們關注的就是這個代碼塊:

self.all_encoder_layers = transformer_model(

input_tensor=self.embedding_output,

attention_mask=attention_mask,

hidden_size=config.hidden_size,

num_hidden_layers=config.num_hidden_layers,

num_attention_heads=config.num_attention_heads,

intermediate_size=config.intermediate_size,

intermediate_act_fn=get_activation(config.hidden_act),

hidden_dropout_prob=config.hidden_dropout_prob,

attention_probs_dropout_prob=config.attention_probs_dropout_prob,

initializer_range=config.initializer_range,

do_return_all_layers=True)

它實際上就是引入了一個transformer_model。那麼transformer裡面有啥呢,繼續看:

attention_head = attention_layer(

from_tensor=layer_input,

to_tensor=layer_input,

attention_mask=attention_mask,

num_attention_heads=num_attention_heads,

size_per_head=attention_head_size,

attention_probs_dropout_prob=attention_probs_dropout_prob,

initializer_range=initializer_range,

do_return_2d_tensor=True,

batch_size=batch_size,

from_seq_length=seq_length,

to_seq_length=seq_length)

不多放,大部分代碼都是才處理各種輸入和輸出的參數,實質上我們就關注attention,它的應用就在這裡(這裡是構造multi-head attention中的其中一個)。於是我們就要看這個attention_layer是什麼了。可以看到,他這裡並沒有直接給出q、k、v是什麼,所以我們還要繼續往裡面去深挖。

# `query_layer` = [B*F, N*H]

query_layer = tf.layers.dense(

from_tensor_2d,

num_attention_heads * size_per_head,

activation=query_act,

name="query",

kernel_initializer=create_initializer(initializer_range))


# `key_layer` = [B*T, N*H]

key_layer = tf.layers.dense(

to_tensor_2d,

num_attention_heads * size_per_head,

activation=key_act,

name="key",

kernel_initializer=create_initializer(initializer_range))


# `value_layer` = [B*T, N*H]

value_layer = tf.layers.dense(

to_tensor_2d,

num_attention_heads * size_per_head,

activation=value_act,

name="value",

kernel_initializer=create_initializer(initializer_range))

找到了函數裡的這個,可以看到的是query用的是fromtensor2d,key和value用的是totensor2d,那我們回過頭來看這兩個是啥,其實就能看到他們都是layer_input,說白了就哈市self attention,而且沒有別的attention結構了,這也就印證了bert中的用的就是transformer中的encoder。

attention源碼

然後我們來看看attention的源碼吧其實不是很長:

def scaled_dot_product_attention(Q, K, V, key_masks,

causality=False, dropout_rate=0.,

training=True,

scope="scaled_dot_product_attention"):

'''See 3.2.1.

Q: Packed queries. 3d tensor. [N, T_q, d_k].

K: Packed keys. 3d tensor. [N, T_k, d_k].

V: Packed values. 3d tensor. [N, T_k, d_v].

key_masks: A 2d tensor with shape of [N, key_seqlen]

causality: If True, applies masking for future blinding

dropout_rate: A floating point number of [0, 1].

training: boolean for controlling droput

scope: Optional scope for `variable_scope`.

'''

with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):

d_k = Q.get_shape().as_list()[-1]


# dot product

outputs = tf.matmul(Q, tf.transpose(K, [0, 2, 1])) # (N, T_q, T_k)


# scale

outputs /= d_k ** 0.5


# key masking

outputs = mask(outputs, key_masks=key_masks, type="key")


# causality or future blinding masking

if causality:

outputs = mask(outputs, type="future")


# softmax

outputs = tf.nn.softmax(outputs)

attention = tf.transpose(outputs, [0, 2, 1])

tf.summary.image("attention", tf.expand_dims(attention[:1], -1))


# # query masking

# outputs = mask(outputs, Q, K, type="query")


# dropout

outputs = tf.layers.dropout(outputs, rate=dropout_rate, training=training)


# weighted sum (context vectors)

outputs = tf.matmul(outputs, V) # (N, T_q, d_v)


return outputs

點乘等各種操作,注釋其實寫的很好了,大家根據代碼翻譯為公式。

參考文獻

CS224N,Lecture Notes: Part VI, Neural Machine Translation, Seq2seq and Attention.

張俊林,深度學習中的attention機制:https://zhuanlan.zhihu.com/p/37601161

Attention機制詳解(二)——Self-Attention與Transformer:https://zhuanlan.zhihu.com/p/47282410

注意力機制在自然語言處理中的應用:https://www.cnblogs.com/robert-dlut/p/5952032.html

一文讀懂bert(原理篇):https://blog.csdn.net/sunhua93/article/details/102764783

【NLP】徹底搞懂BERT:https://www.cnblogs.com/rucwxb/p/10277217.html

transformer源碼:https://github.com/Kyubyong/transformer

bert源碼:https://github.com/google-research/bert


相關焦點

  • NLP.TM[26] | bert之我見-attention篇
    往期回顧:我想現在NLP領域中,不知道bert的已經少之又少了,而bert的講解文章也已經有了很多,這裡我談一下我最近學習得到的理解。事先說明,對bert和transformer完全不懂的人看這個完全不知道咋回事的,想要看下面這些請先對這兩個玩意有初步的理解。
  • bert之我見-positional encoding
    近期我會一連幾篇談談bert中的關鍵細節,這個position encoding是我看到的bert(實質上是transformer中提出的)中最為驚喜的但是卻被很多人忽略(可以理解為媒體鼓吹最少的)一個細節,這裡給大家談談。什麼是position encoding顧名思義,就是基於位置的一套詞嵌入方法,說得簡單點,就是對於一個句子,都有對應的一個向量。
  • 從 one-hot 到 BERT,帶你一步步理解 BERT
    我們會從one-hot、word embedding、rnn、seq2seq、transformer一步步逼近bert,這些是我們理解bert的基礎。Word Embedding首先我們需要對文本進行編碼,使之成為計算機可以讀懂的語言,在編碼時,我們期望句子之間保持詞語間的相似行,詞的向量表示是進行機器學習和深度學習的基礎。
  • 【BERT】如何訓練並使用Bert【持續更新】
    如果是self-attention,Q=K=V,如果是普通的attention,Q !=(K=V)。上次說過了Q和K就是兩個要比較的東西。在NLP中的attention中K和V一般是一樣的【或許可以修改V得到一種新的attention,應該有了類似的研究了吧】但是,不管用的是self-attention還是普通的attention,參數計算並不影響。
  • 【BERT】Sentence-Bert論文筆記
    如果使用bert模型,那麼每一次一個用戶問題過來,都需要與標準問庫計算一遍。在實時交互的系統中,是不可能上線的。而作者提出了Sentence-BERT網絡結構來解決bert模型的不足。簡單通俗地講,就是借鑑孿生網絡模型的框架,將不同的句子輸入到兩個bert模型中(但這兩個bert模型是參數共享的,也可以理解為是同一個bert模型),獲取到每個句子的句子表徵向量;而最終獲得的句子表徵向量,可以用於語義相似度計算,也可以用於無監督的聚類任務。對於同樣的10000個句子,我們想要找出最相似的句子對,只需要計算10000次,需要大約5秒就可計算完全。
  • 輕鬆玩轉BERT!Transformers快速上手
    ")model = AutoModel.from_pretrained("bert-base-uncased")inputs = tokenizer("Hello world!"", "I don't care for Pixar."]encoding = tokenizer(text_batch, return_tensors='pt', padding=True, truncation=True)input_ids = encoding['input_ids']attention_mask = encoding['attention_mask']labels = torch.tensor
  • Bert系列之句向量生成
    這當然是可以,但是在很多真實場景下,會涉及到一個向量搜索任務中。如果我把query跟每個doc都拼到一起輸入到Bert,那需要運行N次Bert才能計算query跟所有doc之間的相似度,在N比較大時這個時間顯然是不能被接受的。
  • Attention isn’t all you need!BERT的力量之源遠不止注意力
    選自medium作者:Damien Sileo機器之心編譯參與:Geek AI、路本文嘗試從自然語言理解的角度解釋為了說明這一點,我們使用一種可視化工具(來自這篇文章:https://medium.com/dissecting-bert/dissecting-bert-part2-335ff2ed9c73)深入研究注意力頭,並在預訓練 BERT-Base Uncased 模型(谷歌發布的 4 種BERT 預訓練模型中的一種)上測試了我們的假設。
  • BERT原理解讀及HuggingFace Transformers微調入門
    本文同時發布於我的個人網站,公式圖片顯示效果更好,歡迎訪問:https://lulaoshi.info/machine-learning/attention/bert自BERT(Bidirectional Encoder Representations from Transformer)[1]出現後,NLP界開啟了一個全新的範式
  • 文本挖掘從小白到精通(十六)--- 像使用scikit-learn一樣玩轉BERT
    每當像BERT這樣的新方法出現時,我就會為它建立一個transformer和estimator。然後我就可以很容易地將其納入到我現有的任何管道中,而不需要太多工作。因此,讓我們為BERT創建一個基於Sci-kit Learn的berttransformer,我們可以將其與任何estimator相連接。
  • 站在BERT肩膀上的NLP新秀們(PART II)
    再補一個THU的ERNIE報告PPT:ERNIE from THU (https://pan.baidu.com/s/1TjGOB2myXT-bln3OpFcbgA 提取碼:35fv)今天我們來看看另外幾個有意思的BERT新秀:XLMs from FacebookLASER from Facebook
  • 三分鐘帶你讀懂 BERT
    翻譯 | 胡瑛皓、stone豪         校對 | 醬番梨        審核 | 詹森·李加薪       整理 | 立魚王原文連結:https://towardsdatascience.com/bert-technology-introduced-in
  • 教你用BERT進行多標籤文本分類
    Bert-Base模型有12個attention層,所有文本都將由標記器轉換為小寫。我們在亞馬遜雲 p3.8xlarge EC2實例上運行此模型,該實例包含4個Tesla V100 GPU,GPU內存總共64 GB。
  • BERT/Transformer/遷移學習NLP資源大列表
    https://github.com/cedrickchee/awesome-bert-nlpGenerative Modeling with Sparse Transformers by OpenAI - an algorithmic improvement of the attention mechanism to extract patterns from sequences 30x longer than possible previously.
  • NLP中的詞向量對比:word2vec/glove/fastText/elmo/GPT/bert
    四、深入解剖bert(與elmo和GPT比較)1、為什麼bert採取的是雙向Transformer Encoder,而不叫decoder?2、elmo、GPT和bert在單雙向語言模型處理上的不同之處?3、bert構建雙向語言模型不是很簡單嗎?不也可以直接像elmo拼接Transformer decoder嗎?
  • NLP之文本分類:「Tf-Idf、Word2Vec和BERT」三種模型比較
    本教程比較了傳統的詞袋法(與簡單的機器學習算法一起使用)、流行的詞嵌入模型(與深度學習神經網絡一起使用)和最先進的語言模型(和基於attention的transformers模型中的遷移學習一起使用),語言模型徹底改變了NLP的格局。
  • AI基礎:一文看懂BERT
    我最近在編寫AI基礎系列,這個是NLP(自然語言處理)的非常重要的部分。將固定長度的字符串作為輸入,數據由下而上傳遞計算,每一層都用到了self attention,並通過前饋神經網絡傳遞其結果,將其交給下一個編碼器。
  • 中文NER任務實驗小結:BERT-MRC的再優化
    中的 query 元素(關於 attention 中的Q、K、V 可以參考我以前的 attention 文章介紹 Attention 機制簡單總結 [3],進行點乘模式的 attention 計算並得到 attention 分數。
  • BERT詳解:開創性自然語言處理框架的全面指南
    目前,關鍵之處在於——BERT是以Transformer結構為基礎的。第二,BERT的預訓練是在包含整個維基百科的無標籤號文本的大語料庫中(足足有25億字!) 和圖書語料庫(有8億字)中進行的。在BERT成功的背後,有一半要歸功於預訓練。
  • 手把手教你用BERT進行多標籤文本分類
    Bert-Base模型有12個attention層,所有文本都將由標記器轉換為小寫。我們在亞馬遜雲 p3.8xlarge EC2實例上運行此模型,該實例包含4個Tesla V100 GPU,GPU內存總共64 GB。