Transformer導論之——Transformer


description: >-
傳統(tǒng)的RNN,GRU,LSTM他們都有一個問題,就是不能并行計算。同時雖然LSTM解決了長期依賴性的問題,但如果我們有一個很長很長的上萬字的文本要處理,這個時候LSTM還是顯得力不從心。Transformer模型很好的解決了上面的兩個問題,它在2017年于論文Attention
is All you Need [1]發(fā)表,之后用于Bert,GPT2,GPT3等模型中。


?? Transformer

Self-Attention 自注意力機制

Self-attention是一種機器學習中常用的技術,通常用于序列到序列的任務,如機器翻譯、文本摘要、問答等。Self-attention 的目的是根據輸入序列中各個位置之間的依賴關系,計算出每個位置的特征向量表示,從而得到一個表示整個序列的向量。在自然語言處理領域,self-attention被廣泛應用于Transformer模型中,取得了很好的效果。

Self-Attention的定義

Self-attention機制是一種將輸入序列的不同部分關聯(lián)起來的方法,可以在不引入循環(huán)或卷積結構的情況下學習到序列中的全局依賴關系。在self-attention中,每個輸入元素都會計算一個注意力得分,該得分衡量它與其他元素之間的相對重要性,并用這些得分來計算一個加權和。

Self-Attention的過程概覽

Self-attention的過程可以分為三個步驟:計算注意力得分、計算加權和、輸出。

  1. 計算注意力得分:對于輸入序列中的每個元素,都需要計算它與其他元素之間的相似度得分,從而確定其在計算加權和時的權重。這個相似度得分可以使用點積、加性或其他方式來計算。計算出的注意力得分可以看作是一個權重向量,它確定了每個輸入元素在計算加權和時的貢獻。
  2. 計算加權和:使用注意力得分來計算每個元素的加權和,以獲得對輸入序列的綜合表示。這個加權和可以表示為輸入序列中所有元素的線性組合,每個元素的權重由對應的注意力得分確定。
  3. 輸出:將加權和作為輸出,這個輸出可以作為后續(xù)層的輸入或輸出給到其他任務。

自注意力的計算公式

在自注意力模型(self-attention)中,給定一個輸入序列,我們需要計算該序列中每個元素與其他元素的相關度,以此來推斷其重要性。假設我們有一個輸入序列 x_1, x_2, ..., x_n,那么它的自注意力表示為A \in \mathbb{R}^{n \times n},其中 A_{i,j} 表示 x_ix_j的相關度。那么,自注意力的計算公式可以表示為:

A_{i,j}={\frac{\exp(e_{i,j})}{\sum_{k=1}^{n}\exp(e_{i,k})}},

其中, e_{i,j}是一個表示 x_ix_j 相關度的能量值。在使用自注意力時,通常會采用點積(dot-product)或加性(additive)的方式來計算 e_{i,j}。

在點積的情況下, e_{i,j} 的計算公式為:

e_{i,j}=x_{i}^{T}x_{j}

在加性的情況下, e_{i,j} 的計算公式為:

e_{i,j}=w_{2}^{T}\operatorname{tanh}(w_{1}x_{i}+w_{2}x_{j}),

其中 w_1w_2 是需要學習的權重矩陣。

自注意力計算的詳細過程

在自注意力機制中,每個輸入元素都可以被視為一個向量。對于每個向量,都可以通過一個矩陣變換來生成三個新向量:查詢向量、鍵向量和值向量。這些新向量可以表示不同的信息,例如查詢向量可以表示要查詢的內容,鍵向量可以表示文本中的單詞,值向量可以表示單詞的嵌入表示。

在計算自注意力時,我們首先將查詢向量與所有鍵向量進行點積運算,然后將結果除以一個可學習的縮放因子,得到一組分數(shù)。這些分數(shù)可以視為查詢向量與不同鍵向量之間的相似度分數(shù),用于衡量它們之間的相關性。接下來,我們可以使用分數(shù)對值向量進行加權匯聚,以獲得對查詢向量的響應表示。

在自注意力機制中,每個輸入元素都可以作為查詢向量、鍵向量和值向量的來源,因此每個元素都可以被視為自身與序列中所有其他元素之間的關系的表示。通過這種方式,自注意力可以有效地捕捉序列中元素之間的長程依賴關系,從而在各種自然語言處理任務中取得了很好的效果。

假設我們有一個輸入序列 x = {x_1, x_2, ..., x_n},其中每個 x_i 都是一個向量,維度為 d。我們可以通過一個線性變換來將每個向量映射到三個不同的向量,即查詢向量 q_i、鍵向量 k_i和值向量 v_i

q_i = W_q x_i, \ k_i = W_k x_i, \ v_i = W_v x_i

其中 W_q, W_k, W_v \in \mathbb{R}^{d \times d} 是可學習的權重矩陣。接下來,我們計算每對查詢向量和鍵向量之間的點積得分,然后對值向量進行加權求和,以得到對查詢向量的響應表示:

\mathrm{Attention}(Q, K, V) = \mathrm{softmax}(\frac{QK^T}{\sqrtu0z1t8os})V

其中 Q= [q_1, q_2, ..., q_n] \in \mathbb{R}^{d \times n}是查詢矩陣, K = [k_1, k_2, ..., k_n] \in \mathbb{R}^{d \times n}是鍵矩陣,V = [v_1, v_2, ..., v_n] \in \mathbb{R}^{d \times n}是值矩陣, \mathrm{softmax}是對每行進行 softmax 操作, \sqrtu0z1t8os是縮放因子,用于平衡點積得分的量級。

最終,對于輸入序列中的每個元素 x_i,我們都可以通過自注意力機制它和序列中其他元素的注意力。

多頭注意力

在自然語言處理中,多頭注意力通常用于機器翻譯和文本生成任務中。具體來說,輸入序列被分成多個頭部,每個頭部可以關注序列中的不同部分,并計算出一個對每個位置的表示。然后,這些表示被合并成一個整體表示,以便于后續(xù)的模型處理。

在計算機視覺中,多頭注意力可以用于圖像分類和目標檢測等任務中。具體來說,輸入圖像被分成多個頭部,每個頭部可以關注圖像中的不同區(qū)域,并計算出一個對每個區(qū)域的表示。然后,這些表示被合并成一個整體表示,以便于后續(xù)的模型處理。

總之,多頭注意力是一種強大的注意力機制,可以幫助模型更好地理解和表示輸入數(shù)據。

多頭注意力的計算過程

在多頭注意力中,輸入序列首先被分成若干個子序列,每個子序列都會經過一個獨立的注意力機制來計算其注意力權重。然后,每個子序列的輸出向量將被拼接在一起,形成最終的多頭注意力輸出向量。

下面是多頭注意力的計算過程:

將輸入序列 X \in \mathbb{R}^{n \times d}通過 h 個線性變換(稱為“頭”)轉換為 h個查詢 Q_1, Q_2, ..., Q_h、 h個鍵 K_1, K_2, ..., K_hh 個值 V_1, V_2, ..., V_h,其中每個頭的維度為 d/h。

Q_i = XW_i^Q,K_i = XW_i^K, V_i = XW_i^V, i=1,2,...,h

這里 W_i^Q \in \mathbb{R}^{d \times d/h}W_i^K \in \mathbb{R}^{d \times d/h} 、 W_i^V \in \mathbb{R}^{d \times d/h}分別是用于將輸入序列 X 轉換為查詢 Q_i、鍵 K_i 和值 V_i 的線性變換矩陣, d 是輸入序列的維度, h是頭的數(shù)量。每個頭的維度為 d/h ,因此每個頭可以關注輸入序列中的不同部分。

接下來,對于每個頭 i,計算其注意力權重 A_i,該權重表示該頭在輸入序列中關注的重要程度。這里采用前面說的點積注意力機制:

A_i = \text{softmax}(\frac{Q_iK_i^T}{\sqrt{d/h}})
\sqrt{d/h}是用于縮放點積的常數(shù),旨在避免點積過大或過小而導致的梯度問題。然后,將注意力權重 A_i與值 V_i 相乘并相加,得到頭 i 的輸出向量 O_i

O_i = A_iV_i

最后,將所有頭的輸出向量拼接在一起,得到多頭注意力的輸出向量 O \in \mathbb{R}^{n \times d}

O = \text{Concat}(O_1, O_2, ..., O_h)

多頭注意力的輸出向量 O 可以作為下一層模型的輸入,例如 Transformer 模型中的前饋神經網絡。多頭注意力機制可以幫助模型更好地理解序列數(shù)據中的信息,從而提高模型的性能。

image.png

Transformer

Transformer 是一種基于自注意力機制(self-attention mechanism)的深度神經網絡,它是自然語言處理領域中的一項重要技術。Transformer 最早由 Google 提出,已經被廣泛應用于機器翻譯、文本生成、語言模型等任務中。

Transformer 的核心思想是使用自注意力機制來實現(xiàn)對輸入序列的編碼和對輸出序列的解碼。自注意力機制可以讓模型對輸入序列中的不同位置進行關注,并將不同位置的信息整合起來。這種關注機制可以看作是一種在序列中進行“跨步”連接(skip connection)的方式,使得模型可以更好地捕捉序列中的長程依賴關系。

Transformer 包含兩個主要模塊:Encoder 和 Decoder。Encoder 模塊將輸入序列映射到一個高維空間中,而 Decoder 模塊則根據 Encoder 模塊生成的編碼信息,逐步生成目標序列。

Encoder 模塊由多個相同的層級組成,每個層級包含兩個子層級:多頭自注意力層(Multi-Head Self-Attention Layer)和全連接前饋層(Fully Connected Feedforward Layer)。多頭自注意力層用于對輸入序列進行編碼,全連接前饋層用于對編碼后的序列進行進一步處理。Decoder 模塊也由多個相同的層級組成,每個層級包含三個子層級:多頭自注意力層、編碼器-解碼器注意力層(Encoder-Decoder Attention Layer)和全連接前饋層。

在多頭自注意力層中,模型通過計算每個輸入序列元素與所有元素的相似度,得到每個元素對于其他元素的權重,然后使用這些權重進行加權平均,得到每個元素的向量表示。這樣,模型可以在不同層級中對輸入序列的不同部分進行關注。

在編碼器-解碼器注意力層中,模型使用編碼器層級生成的信息來計算每個目標序列元素與輸入序列元素的相似度,然后使用這些相似度進行加權平均,得到目標序列元素的向量表示。這樣,模型可以將輸入序列中相關的信息與目標序列中的信息相結合,從而更好地生成目標序列。

總之,Transformer 通過自注意力機制實現(xiàn)對序列的編碼和解碼,使得模型能夠更好地捕捉序列中的依賴關系,進而提高自然語言處理等任務的效果。

Encoder

Transformer 的 Encoder 模塊是由多個相同的層級組成的,每個層級包含兩個子層級:多頭自注意力層(Multi-Head Self-Attention Layer)和全連接前饋層(Fully Connected Feedforward Layer)。下面詳細介紹這兩個子層級的原理:

多頭自注意力層

多頭自注意力層是 Transformer 的核心部分,它通過對輸入序列中每個元素與所有元素的相似度進行計算,得到每個元素對于其他元素的權重,并使用這些權重進行加權平均,得到每個元素的向量表示。這個過程可以看做是將序列中的每個元素與其他元素進行“跨步”連接(skip connection),從而更好地捕捉序列中的長程依賴關系。 具體地,多頭自注意力層包含以下幾個步驟:

首先,將輸入序列經過三個線性變換(即,分別進行投影)得到 Q(Query)、K(Key)、V(Value)三個矩陣。

接著,計算 Q 和 K 的點積得到相似度矩陣,然后進行 softmax 歸一化,得到每個元素對于其他元素的權重。 將權重和 V 矩陣相乘并加權求和,得到每個元素的向量表示。

最后,將每個元素的向量表示通過一個線性變換得到最終輸出。

需要注意的是,多頭自注意力層中的 Q、K、V 矩陣都是由輸入序列經過不同的線性變換得到的,這些不同的線性變換分別對應不同的“頭”(Head),每個頭學習到不同的表示,從而可以捕捉序列中不同的特征。在計算相似度矩陣時,使用的是所有頭學習到的表示的點積,從而綜合考慮了不同頭的信息。更通俗的說,我們把每次計算注意力看作是一次循環(huán),每次循環(huán)就是一個頭。

全連接前饋層

全連接前饋層是多頭自注意力層的一個補充,它用于對編碼后的序列進行進一步處理,增強模型的表示能力。具體地,全連接前饋層包含兩個線性變換,中間使用激活函數(shù)(如 ReLU)進行非線性變換,從而生成更加復雜的特征表示。

總之,Transformer 的 Encoder 模塊通過多頭自注意力層和全連接前饋層對輸入序列進行編碼,從而捕捉序列中的依賴關系和特征表示。這些編碼信息可以傳遞給 Decoder 模塊,用于生成目標序列。

Add&Norm

Transformer中的Add & Norm是指在每個Multi-Head Attention和Feedforward層之后進行的一種規(guī)范化技術,目的是加快模型收斂速度并提高模型性能。這種想法來自于ResNet。

在Multi-Head Attention和Feedforward層中,模型進行一些線性變換和非線性變換,這些變換可能會導致梯度消失或梯度爆炸問題。為了解決這個問題,Transformer在每個層后添加了一個殘差連接(residual connection),將輸入和輸出相加。在殘差連接后,使用Layer Normalization對結果進行規(guī)范化。Layer Normalization是一種對數(shù)據進行歸一化的方法,通過對每個特征維度上的數(shù)據進行標準化,使得不同特征維度上的數(shù)據具有相同的分布。最后,將歸一化的結果與殘差連接相加,得到該層的最終輸出。

Add & Norm技術能夠有效地減輕梯度消失和梯度爆炸問題,同時也有助于加速模型的收斂速度。

Decoder

decoder 的輸入通常包括兩個部分:

  1. 前一個時間步的輸出:在解碼器中,前一個時間步的輸出是當前時間步的輸入。因此,Transformer decoder 的輸入包括前一個時間步的輸出向量,即上一次解碼器輸出的結果。
  2. 編碼器輸出的表示:編碼器會將輸入序列轉換成一個表示,該表示包含輸入序列中每個位置的信息。解碼器需要利用這個表示來指導自己生成正確的輸出。因此,Transformer decoder 的輸入還包括編碼器輸出的表示,通常是編碼器輸出的所有位置的向量的加權平均值(注意到這里權重的計算是通過注意力機制實現(xiàn)的)。

Masked Mult-Head Attention

Decoder的第一個子層是一個“masked”多頭自注意力層,這意味著在計算注意力時,只允許當前位置之前的位置作為查詢進行注意力計算,不允許當前位置之后的位置參與計算。這是因為在解碼器中,我們需要逐步生成輸出,而不是一次性生成所有輸出。如果允許當前位置之后的位置參與計算,那么就相當于我們在生成當前位置的輸出時使用了后面位置的信息,這會導致模型泄露未來信息,使得模型在生成輸出時產生錯誤。

具體來說,如果我們允許當前位置使用后面位置的信息進行計算,那么解碼器在生成當前位置的輸出時,就會知道未來位置的信息,這違背了解碼器的設計原則,也就是要根據之前生成的輸出來生成后續(xù)的輸出,而不是利用未來信息來影響當前輸出。

這種未來信息泄露會導致模型在生成輸出時產生錯誤,因為模型會過度依賴未來信息,而忽略當前位置及之前的信息,從而導致模型對輸入的理解出現(xiàn)偏差,輸出的結果也就不準確了。

因此,為了避免這種情況,解碼器中使用“masked”多頭自注意力層來限制只使用當前位置及其之前的信息進行計算,保證每個位置的輸出只受前面位置的影響,從而避免了未來信息的泄露。

image.png

位置編碼

在Transformer模型中,為了將序列的位置信息引入模型中,需要對輸入序列的每個位置進行編碼。這是通過在輸入序列中添加一個位置編碼向量來實現(xiàn)的。位置編碼向量可以被看作是一個與詞向量同樣維度的向量,其中每個元素的值是基于該位置以及每個維度的信息計算得到的。具體地,對于位置 pos和維度 i,位置編碼向量 PE_{pos, i} 的計算方式如下:

PE_{\mathrm{pos,}i}=\begin{cases}\sin\left(\frac{px}{10000^{2/4}\mathrm{medel}}\right)&i\text{is even}\\ \cos\left(\frac{pg^{2/4}\mathrm{med}}{10000^{2(i-1)/d}\mathrm{med}}\right)&i\text{is odd}\end{cases}

其中, PE_{pos, i} 表示位置編碼矩陣中位置 pos上的第 i 維元素, d_{\text{model}} 是詞向量和位置編碼向量的維度, pos 是當前位置的索引。公式中的 sin 和 $cos$$ 函數(shù)分別代表正弦函數(shù)和余弦函數(shù)。它們能夠給每個位置編碼向量賦予一個獨特的模式,從而區(qū)分不同位置的輸入。在計算中,位置編碼向量會被加到對應的詞向量中,從而產生最終的輸入向量。

需要注意的是,由于位置編碼向量是通過正弦和余弦函數(shù)進行計算的,所以在計算中不需要額外的訓練,也不需要對每個位置編碼向量進行更新。位置編碼向量只需要在模型的初始化階段計算一次,然后在每次輸入序列的編碼中使用即可。

Transformer的pytorch實現(xiàn)

首先,我們需要導入所需的庫和模塊:

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

然后,我們定義Transformer模型的主要組件,包括編碼器、解碼器和整個Transformer模型本身。

在編碼器和解碼器中,我們實現(xiàn)了多頭自注意力機制(multi-head self-attention)和前饋神經網絡(feed-forward network)這兩個核心組件。

在Transformer模型中,我們將編碼器和解碼器組合在一起,并添加一些額外的組件,如嵌入層(embedding layer)、位置編碼器(position encoding)和輸出層(output layer)。

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        super(MultiHeadAttention, self).__init__()
        self.d_model = d_model  # 模型的維度
        self.n_heads = n_heads  # 多頭注意力的頭數(shù)
        self.d_k = d_model // n_heads  # 每個頭的維度,保證能夠整除
        
        # 創(chuàng)建權重矩陣
        self.W_Q = nn.Linear(d_model, d_model)  # 查詢向量的權重矩陣
        self.W_K = nn.Linear(d_model, d_model)  # 鍵向量的權重矩陣
        self.W_V = nn.Linear(d_model, d_model)  # 值向量的權重矩陣
        
        # 最后的線性層
        self.W_O = nn.Linear(d_model, d_model)  # 輸出向量的權重矩陣
        
    def forward(self, Q, K, V, mask=None):
        batch_size = Q.size(0)  # 獲取輸入數(shù)據的批次大小
        
        # 通過線性層,分別計算 Q、K、V 的投影向量
        Q = self.W_Q(Q)
        K = self.W_K(K)
        V = self.W_V(V)
        
        # 將 Q、K、V 投影向量分裂為多個頭
        Q = Q.view(batch_size, -1, self.n_heads, self.d_k)
        K = K.view(batch_size, -1, self.n_heads, self.d_k)
        V = V.view(batch_size, -1, self.n_heads, self.d_k)
        
        # 計算注意力得分
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
        
        # ...


MultiHeadAttention類中,我們實現(xiàn)了多頭自注意力機制,其中包含了以下主要部分:

  1. __init__ 方法:初始化函數(shù),定義了模型的維度、多頭注意力的頭數(shù)、每個頭的維度,并創(chuàng)建了權重矩陣(查詢、鍵、值、輸出)。
  2. forward 方法:前向傳播函數(shù),用于計算多頭自注意力機制的輸出。在此函數(shù)中,我們首先通過線性層,將輸入的 Q、K、V 分別投影到 d_model 維度空間上。
  3. 接著,我們將 Q、K、V 投影向量分裂為多個頭,以便進行并行計算。
  4. 然后,我們計算注意力得分 scores,通過將 Q 與 K 轉置后相乘,再除以 math.sqrt(self.d_k)。注意力得分用于計算每個值向量的權重,以便對值向量進行加權求和。

在這里,我們只是計算了注意力得分,并沒有進行權重的計算。接下來,我們將使用 softmax 函數(shù)將注意力得分轉換為權重:

# 對 scores 進行縮放和掩碼操作
if mask is not None:
mask = mask.unsqueeze(1)
scores = scores.masked_fill(mask == 0, -1e9)

# 將注意力得分進行 softmax 計算
    attn_weights = F.softmax(scores, dim=-1)
    
    # 將權重與 V 向量相乘
    attn_output = torch.matmul(attn_weights, V)
    
    # 將多頭注意力向量拼接在一起
    attn_output = attn_output.view(batch_size, -1, self.d_model)
    
    # 通過最后的線性層,得到最終的多頭注意力向量
    attn_output = self.W_O(attn_output)
    
    return attn_output, attn_weights
class FeedForward(nn.Module):
def init(self, d_model, d_ff):
super(FeedForward, self).init()
 # 創(chuàng)建兩個線性層
    self.linear_1 = nn.Linear(d_model, d_ff)
    self.linear_2 = nn.Linear(d_ff, d_model)
    
def forward(self, x):
    # 通過 ReLU 激活函數(shù)
    x = F.relu(self.linear_1(x))
    x = self.linear_2(x)
    return x
class EncoderLayer(nn.Module):
def init(self, d_model, n_heads, d_ff, dropout=0.1):
super(EncoderLayer, self).init()
self.multihead_attn = MultiHeadAttention(d_model, n_heads)
self.ff = FeedForward(d_model, d_ff)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)

以上的代碼是 Transformer 模型的一部分,包括了 MultiHeadAttention、FeedForward 和 EncoderLayer 三個類。下面是代碼解釋:

  • MultiHeadAttention:多頭注意力機制,將輸入的 Q、K、V 矩陣分別通過線性變換得到 Q、K、V 的查詢矩陣、鍵矩陣和值矩陣,然后計算注意力得分,并通過 softmax 函數(shù)將注意力得分轉換為權重,最后將權重與 V 向量相乘得到多頭注意力向量。
  • FeedForward:前饋神經網絡,通過兩個線性層和 ReLU 激活函數(shù)對輸入進行變換。
  • EncoderLayer:編碼器層,包括多頭注意力機制、前饋神經網絡、LayerNormalization 和 Dropout 層,其中 LayerNormalization 是為了減少訓練過程中的內部協(xié)變量偏移,Dropout 是為了防止過擬合。

這三個類是 Transformer 模型的重要組成部分,可以用于語言建模、機器翻譯等任務中。其中 MultiHeadAttention 的思想也被廣泛應用于其他領域的深度學習模型中。

Refernce 引用

  1. Vaswani, Ashish et al. “Attention is All you Need.” ArXiv abs/1706.03762 (2017): n. pag.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容