transformer編碼層-Bert底層介紹

內容抄自:視頻從中文Transformer到BERT的模型精講,以及基于BERT情感分類實戰
代碼文本來自:github講解和內容介紹

一、簡介

說到自然語言處理、語言模型、 命名實體識別、機器翻譯,可能很多人想到的LSTM等循環神經網絡, 但目前其實LSTM起碼在自然語言處理領域已經過時了, 在Stanford閱讀理解數據集(SQuAD2.0)榜單里, 機器的成績已經超人類表現,這很大程度要歸功于transformer的BERT預訓練模型。今天我們來講一下transformer模型,你不需要有很多深度學習和數學基礎,我來用簡單的語言和可視化的方法從零講起。transformer是谷歌大腦在2017年底發表的論文attention is all you need中所提出的seq2seq模型. 現在已經取得了大范圍的應用和擴展, 而BERT就是從transformer中衍生出來的預訓練語言模型。

在我們開始之前, 允許我簡單說一下目前自然語言處理領域的現狀, 目前transformer模型已經得到廣泛認可和應用, 而應用的方式主要是先進行預訓練語言模型, 然后把預訓練的模型適配給下游任務, 以完成各種不同的任務, 如分類, 生成, 標記(NER)等等, 預訓練模型非常重要, 預訓練的模型的性能直接影響下游任務的性能,我有信心讓小伙伴們充分理解transformer并具備一定衍生模型的設計和編寫能力。

本文主要從 transformer編碼器(理論部分)的各個組成結構開始介紹,如下所示。為了讓大家充分理解和初步使用transformer和訓練BERT, 并應用到自己的需求上:

  1. transformer模型的直觀認識;
  2. Positional encoding, 即位置嵌入(或位置編碼);
  3. Self attention mechanism , 即自注意力機制注意力矩陣可視化;
  4. Layer normalization和殘差連接.
  5. transformer encoder 整體結構.

二、transformer模型的直觀認識

在深度學習中,我們常用的模型是LSTM(長短時記憶神經網絡模型)。所以,我們首先聊一下transformer和LSTM的最大區別, 就是LSTM的訓練是迭代的, 是一個字接一個字的來, 當前這個字過完LSTM單元后, 才可以進下一個字;而transformer的訓練是并行了, 就是所有字是全部同時訓練的, 這樣就大大加快了計算效率, transformer使用了位置嵌入positional encoding來理解語言的順序, 使用自注意力機制和全連接層來進行計算,使得模型能有效對文本重點表示進行學習和改進。

如下圖所示,transformer模型主要分為兩大部分, 分別是編碼器解碼器, 編碼器負責把自然語言序列映射成為隱藏層(下圖中第2步用九宮格比喻的部分), 它是含有自然語言序列的數學表達。 然后,解碼器把隱藏層再映射為自然語言序列, 從而使我們可以解決各種問題, 如情感分類, 命名實體識別, 語義關系抽取, 摘要生成, 機器翻譯等等, 下面我們簡單闡述一下下圖的每一步都做了什么:

  1. 輸入自然語言序列到編碼器: Why do we work?(為什么要工作);
  2. 解析語言序列,編碼器輸出的隱藏層,再將其輸入到解碼器中;
  3. 輸入起始符號“<start>”到解碼器;
  4. 解碼器輸出得到第一個字"為";
  5. 將得到的第一個字"為"作為輸入,給解碼器;
  6. 得到第二個字"什";
  7. 將得到的第二字再作為輸入, 直到解碼器輸出 終止符“<end>”, 即序列生成完成。
transformer模型架構

本文的內容限于編碼器部分, 即把自然語言序列映射為隱藏層的數學表達的過程, 因為理解了編碼器中的結構后, 理解解碼器就非常簡單了,最重要的是BERT預訓練模型只用到了編碼器的部分, 也就是先用編碼器訓練一個語言模型, 然后再把它適配給其他五花八門的任務。因為,我們用編碼器就能夠完成一些自然語言處理中比較主流的任務, 如情感分類, 語義關系分析, 命名實體識別等, 解碼器的內容和序列到序列模型有機會我們會涉及到。

三、位置編碼

為方便理解, 本文將transformer編碼器(下圖)的內容分做了1, 2, 3, 4個方框的序號,便于我們理解位置編碼、自注意力、殘差鏈接等編碼器整體架構內容:


transformer編碼器

由于transformer模型沒有循環神經網絡(RNN)的迭代操作, 所以我們必須提供每個字的位置信息給transformer, 才能識別出語言中的順序關系。

現在定義一個位置嵌入的概念, 也就是positional encoding, 位置嵌入的維度為[max sexquenceLength, embedding dimension], 嵌入的維度(embedding dimension)同詞向量的維度(word embedding dimension), max sequenceLength屬于超參數, 指的是限定的最大單個句長。

注意, transformer模型一般以“字”為單位訓練, 也就是說我們不用分詞了。 首先我們要初始化字向量為[vocab size, embedding dimension],其中,wocab size為總共的字庫數量, embedding dimension為字向量的維度,也是每個字的數學表達。

在這里論文中使用了sin和cos函數的線性變換來提供模型的位置信息:

PE_{(pos,2i)}=sin(pos/10000^{2i/d_{model}})
PE_{(pos,2i+1)}=cos(pos/10000^{2i/d_{model}})

上式中pos指的是句中字的位置, 取值范圍是[0, \ max \ sequence \ Length), i指的是詞向量的維度, 取值范圍是[0, \ embedding \ dimension), 上面有sincos一組公式, 也就是對應著embedding \ dimension維度的一組奇數和偶數的序號的維度, 例如0, 1一組, 2, 3一組, 分別用上面的sincos函數做處理, 從而產生不同的周期性變化, 而位置嵌入在embedding \ dimension維度上隨著維度序號增大, 周期變化會越來越慢, 而產生一種包含位置信息的紋理, 就像論文原文中第六頁講的, 位置嵌入函數的周期從2 \pi10000 * 2 \pi變化, 而每一個位置在embedding \ dimension維度上都會得到不同周期的sincos函數的取值組合, 從而產生獨一的紋理位置信息, 模型從而學到位置之間的依賴關系和自然語言的時序特性。
下面畫一下位置嵌入, 可見縱向觀察, 隨著embedding \ dimension增大, 位置嵌入函數呈現不同的周期變化。

其所繪制的圖形如下所示,我們可以看到不同維度的跨度不同,隨著維度增大,維度周期越大:


不同維度的位置編碼,橫坐標是維度,縱坐標是字段長度
不同維度的位置編碼

四、自注意力self attention mechanism

我們現在有詞向量矩陣和位置嵌入,假設我們現在有一些句子X,X的維度是[batch size,sequence length],首先,我們再字向量里查到對應詞的嵌入,然后與位置嵌入元素相加,得到最終embedding的維度:
[batch size, sequence length, embedding dimension]
簡單數學表達解釋為:
X \in \Re^{batch size * seq.len}
X_{embedding} = EmbeddingLookup(X)+Positional Encoding
X_{embedding} \in \Re^{batch size * seq.len * embed.dim}

X: [batch_size, len, embedding_size]
W: [embedding_size, hidden_dimension]
XW = [batch_size, len, hidden_dimension]   # 消掉了embedding_size
多頭注意力機制

下一步,為了學到多重含義的表達,對X_{embedding}做線性映射,也就是分配三個權重,W_Q,W_K,W_V \in \Re^{embed.dim * embed.dim}
線性映射之后,形成三個矩陣,為Q,K,V和線性變換之前的維度一致.

Q=Linear(X_{embedding})=X_{embedding}W_Q
K=Linear(X_{embedding})=X_{embedding}W_K
V=Linear(X_{embedding})=X_{embedding}W_V

下面,進行多頭注意力機制,也就是multi head attention。
為什么是多頭的呢?因為我們要用注意力機制來提取多重語義的含義,我們首先定義一個超參數是h,也就是head的數量,注意embedding dimension必須整除于h,因為我們要把embedding .dimension分割成h份。

從上圖不難看出,我們把embedding .dimension分割成h份,也就是“頭”的個數份;
分割后,Q,K,V的維度為[batch size, sequence length, h , embedding dimension/h]
之后,我們把Q,K,V中的sequence length,h進行一下轉置,為了方便后續的計算:
轉置后,Q,K,V的維度為[batch size, h ,sequence length,embedding dimension/h]

自注意力機制計算

上圖中,我們拿出一組heads來解釋一下muti head attention的含義。一組heads也就是一組分割后的Q,K,V,它們的維度都是[sequence Length, embedding Dimension/h],我們先計算QK的轉置的點積。注意,上圖它們各自的維度,我們還記得點積的集合意義,兩個向量越相似(夾角越小),它們的點積就越大,否則就越小。

點積介紹

我們再這里首先用代表第一個字的c1行與c1列相乘,得到一個數值c1c1,它也就是位于注意力矩陣的第一行第一列的c1c1,這里的含義是第一個字與第一個字的注意力機制,然后依次求得c1c2,c1c3,...。

注意力矩陣的第一行就是指的是第一個字與這六個字(上圖的max sequence length大小)的哪個比較相關,通過softmax獲得對應的歸一化概率:
Attention(Q,K,V)=softmax( \frac{QK^T}{ \sqrt{d_k}} )V

上式中,就是自注意力機制,我們先求QK^T,也就是求注意力矩陣,然后用注意力矩陣給V加權,\sqrt{d_k} 是為了把注意力矩陣變成標準正態分布,使得softmax歸一化知乎的結果更加穩定,以便反向傳播的時候獲得平衡的梯度。

自注意力機制后,對V進行加權

目前,我們得到了注意力矩陣,并用softmax歸一化,使得每個字跟其他所有字的注意力權重的和為1。注意力矩陣的作用就是一個注意力權重的概率分布,我們要用注意力矩陣的權重給V進行加權。上圖中,我們從注意力矩陣去除一行(和為1),然后依次點乘V的列,矩陣V的每一行代表著每個字向量的數學表達。
我么上面的操作正是用注意力權重進行這些數學表達的加權線性組合,從而使每個字向量都含有當前句子內所有字向量的信息。注意,進行點乘運算后,V的維度沒有變化,仍為[batch size, h ,sequence length,embedding dimension/h]

Attention Mask(注意力遮蔽)

Attention Mask

注意, 在上面self \ attention的計算過程中, 我們通常使用mini \ batch來計算, 也就是一次計算多句話, 也就是X的維度是[batch \ size, \ sequence \ length], sequence \ length是句長,而一個mini \ batch是由多個不等長的句子組成的, 我們就需要按照這個mini \ batch中最大的句長對剩余的句子進行補齊長度, 我們一般用0來進行填充, 這個過程叫做padding.

但這時在進行softmax的時候就會產生問題, 回顧softmax函數\sigma (\mathbf {z} )_{i}={\frac {e^{z_{i}}}{\sum _{j=1}^{K}e^{z_{j}}}}, e^0是1(即padding過程為補0,所以softmax時它為1), 是有值的, 這樣的話softmax中被padding的部分就參與了運算, 就等于是讓無效的部分參與了運算, 會產生很大隱患。這時就需要做一個mask讓這些無效區域不參與運算, 我們一般給無效區域加一個很大的負數的偏置, 也就是:
z_{illegal} = z_{illegal} + bias_{illegal}
bias_{illegal} \to -\infty
e^{z_{illegal}} \to 0
經過上式的masking我們使無效區域經過softmax計算之后還幾乎為0, 這樣就避免了無效區域參與計算。

五. Layer Normalization和殘差連接

3.1 ) 殘差連接:
我們在上一步得到了經過注意力矩陣加權之后的V, 也就是Attention(Q, \ K, \ V), 我們對它進行一下轉置, 使其和X_{embedding}的維度一致, 也就是[batch \ size, \ sequence \ length, \ embedding \ dimension], 然后把他們加起來做殘差連接, 直接進行元素相加, 因為他們的維度一致:
X_{embedding} + Attention(Q, \ K, \ V)
在之后的運算里, 每經過一個模塊的運算, 都要把運算之前的值和運算之后的值相加, 從而得到殘差連接, 訓練的時候可以使梯度直接走捷徑反傳到最初始層:
X + SubLayer(X)

3.2) LayerNorm:
Layer \ Normalization的作用是把神經網絡中隱藏層歸一為標準正態分布, 也就是i.i.d獨立同分布, 以起到加快訓練速度, 加速收斂的作用:
\mu_{i}=\frac{1}{m} \sum^{m}_{i=1}x_{ij}
上式中以矩陣的行(row)為單位求均值;
\sigma^{2}_{j}=\frac{1}{m} \sum^{m}_{i=1} (x_{ij}-\mu_{j})^{2}
上式中以矩陣的行(row)為單位求方差;
LayerNorm(x)=\alpha \odot \frac{x_{ij}-\mu_{i}} {\sqrt{\sigma^{2}_{i}+\epsilon}} + \beta
然后用每一行每一個元素減去這行的均值, 再除以這行的標準差, 從而得到歸一化后的數值, \epsilon是為了防止除0;
之后引入兩個可訓練參數\alpha, \ \beta來彌補歸一化的過程中損失掉的信息, 注意\odot表示元素相乘而不是點積, 我們一般初始化\alpha為全1, 而\beta為全0.

normalization(目的是讓模型收斂的更快,如下右的梯度圖所示)

六. transformer encoder整體結構.

經過上面3個步驟(3,4,5節的位置編碼,自注意力,殘差和正規化等操作), 我們已經基本了解到來transformer編碼器的主要構成部分, 我們下面用公式把一個transformer \ block的計算過程整理一下:

1). 字向量與位置編碼:
X = EmbeddingLookup(X) + PositionalEncoding
X \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}

2). 自注意力機制:
Q = Linear(X) = XW_{Q}
K = Linear(X) = XW_{K}
V = Linear(X) = XW_{V}
X_{attention} = SelfAttention(Q, \ K, \ V)

3). 殘差連接與Layer \ Normalization
X_{attention} = X + X_{attention}
X_{attention} = LayerNorm(X_{attention})

4). 下面進行transformer \ block結構圖中的第4部分, 也就是FeedForward, 其實就是兩層線性映射并用激活函數激活, 比如說ReLU:
X_{hidden} = Linear(Activate(Linear(X_{attention})))

5). 重復3).:
X_{hidden} = X_{attention} + X_{hidden}
X_{hidden} = LayerNorm(X_{hidden})
X_{hidden} \in \mathbb{R}^{batch \ size \ * \ seq. \ len. \ * \ embed. \ dim.}

encoder of transformer

七、小結

本文到現在的位置已經講完了transformer的編碼器的部分, 了解到了transformer是怎樣獲得自然語言的位置信息的, 注意力機制是怎樣的, 其實舉個語言情感分類的例子, 我們已經知道, 經過自注意力機制, 一句話中的每個字都含有這句話中其他所有字的信息, 那么我們可不可以添加一個空白字符到句子最前面, 然后讓句子中的所有信息向這個空白字符匯總, 然后再映射成想要分的類別呢?
這就是BERT, 我們下次會講到. 在BERT的預訓練中, 我們給每句話的句頭加一個特殊字符, 然后句末再加一個特殊字符, 之后模型預訓練完畢之后, 我們就可以用句頭的特殊字符的hidden \ state完成一些分類任務了.

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容