Transformer 模型的核心在於注意力機制,它讓模型能同時關注輸入序列的不同部分,有效捕捉長距離依賴關係。這種機制尤其適用於處理自然語言這類別具有複雜結構和上下文關係的資料。在 Transformer 中,注意力機制以多頭注意力的形式出現,允許模型從多個角度理解輸入訊息。編碼器部分利用多頭自注意力機制提取輸入序列的特徵,而解碼器則結合編碼器的輸出和自身已生成的序列,逐步生成目標序列。每個編碼器和解碼器塊都包含前饋神經網路和殘差連線等結構,以增強模型的表達能力和訓練穩定性。理解這些元件的互動方式對於掌握 Transformer 模型至關重要。

多頭注意力機制的優點

多頭注意力機制的優點包括:

  • 可以同時關注多個方面的訊息
  • 可以提高模型的表達能力
  • 可以減少過擬合的風險

圖表翻譯:

  graph LR
    Q[Query] -->|W_i^Q|> QW_i^Q
    K[Key] -->|W_i^K|> KW_i^K
    V[Value] -->|W_i^V|> VW_i^V
    QW_i^Q -->|Attention|> head_i
    KW_i^K -->|Attention|> head_i
    VW_i^V -->|Attention|> head_i
    head_i -->|Concat|> MultiHead
    MultiHead -->|W^O|> Output

圖表展示了多頭注意力機制的過程,包括查詢、鍵和值的線性變換、注意力權重的計算和多頭注意力機制的輸出。

多頭注意力機制的實作

在 Transformer 模型中,多頭注意力機制是一個重要的組成部分。這個機制允許模型從不同的角度去關注輸入序列的不同部分。下面是多頭注意力機制的實作細節:

初始化

首先,我們需要初始化多頭注意力機制的相關引數。這包括設定模型的維度、頭數等。

self.d_k = d_model // h
self.h = h

線性層的建立

接下來,我們需要建立四個全連線層。其中三個用於查詢、鍵和值的投影,另一個用於合併所有頭的輸出。

self.fc_layers = clones(torch.nn.Linear(d_model, d_model), 4)

注意力機制的實作

然後,我們需要實作注意力機制的前向傳播方法。這個方法需要接收查詢、鍵和值三個引數,同時也可以接收一個遮罩引數。

def forward(self, query, key, value, mask=None):
    if mask is not None:
        # 對所有頭應用相同的遮罩
        mask = mask.unsqueeze(1)
    batch_samples = query.size(0)

多頭注意力的實作

在前向傳播方法中,我們需要實作多頭注意力的計算。這包括對查詢、鍵和值進行線性變換,然後計算注意力權重,最後合併所有頭的輸出。

# 查詢、鍵和值的線性變換
query_linear = self.fc_layers[0](query)
key_linear = self.fc_layers[1](key)
value_linear = self.fc_layers[2](value)

# 注意力權重的計算
attention_weights = torch.matmul(query_linear, key_linear.transpose(-1, -2)) / math.sqrt(self.d_k)

# 遮罩的應用
if mask is not None:
    attention_weights = attention_weights.masked_fill(mask == 0, -1e9)

# 注意力權重的正規化
attention_weights = torch.softmax(attention_weights, dim=-1)

# 注意力輸出的計算
attention_output = torch.matmul(attention_weights, value_linear)

# 合併所有頭的輸出
output = self.fc_layers[3](attention_output)

最終輸出

最後,我們需要傳回注意力機制的最終輸出。

return output

這樣,多頭注意力機制的實作就完成了。這個機制可以讓模型從不同的角度去關注輸入序列的不同部分,從而提高模型的表達能力和泛化能力。

轉換器架構與注意力機制

在深入探討轉換器架構之前,我們先來瞭解一下注意力機制的重要性。注意力機制是一種允許模型關注輸入序列的特定部分的技術,從而可以更好地捕捉長距離依賴關係和上下文訊息。轉換器架構正是根據這種注意力機制而設計的。

注意力機制的實作

注意力機制的實作可以分為三個步驟:

  1. 線性投影:首先,我們需要對輸入的query、key和value進行線性投影,以得到相應的投影向量。這可以透過以下程式碼實作:
projections = [
    l(x).view(batch_samples, -1, self.h, self.d_k).transpose(1, 2)
    for l, x in zip(self.fc_layers, (query, key, value))
]
query, key, value = projections
  1. 注意力運算:接下來,我們需要對投影後的向量進行注意力運算,以得到注意力權重。這可以透過以下程式碼實作:
x, self.attn = attention(
    query, key, value,
    mask=mask,
    dropout=self.dropout
)
  1. 拼接和最終線性變換:最後,我們需要將注意力權重與value進行拼接,並進行最終的線性變換,以得到輸出結果。這可以透過以下程式碼實作:
x = x.transpose(1, 2).contiguous().view(batch_samples, -1, self.h * self.d_k)
return self.fc_layers[-1](x)

轉換器架構

轉換器架構由兩個主要部分組成:編碼器(encoder)和解碼器(decoder)。編碼器負責將輸入序列轉換為一系列向量,而解碼器則負責將這些向量轉換為輸出序列。

編碼器

編碼器由多個相同的層組成,每個層包括兩個子層:自注意力機制和前向神經網路。自注意力機制允許模型關注輸入序列的不同部分,而前向神經網路則對輸入序列進行變換。

解碼器

解碼器也由多個相同的層組成,每個層包括三個子層:自注意力機制、編碼器-解碼器注意力機制和前向神經網路。自注意力機制和編碼器-解碼器注意力機制允許模型關注輸入序列和編碼器輸出的不同部分,而前向神經網路則對輸入序列進行變換。

轉換器架構的變體

轉換器架構有多種變體,包括post-ln和pre-ln(或post-normalization和pre-normalization)等。這些變體主要是在注意力機制和前向神經網路的順序上進行了更改。

post-ln轉換器

post-ln轉換器是原始的轉換器架構,其特點是先進行自注意力機制和編碼器-解碼器注意力機制,然後進行前向神經網路的變換。

pre-ln轉換器

pre-ln轉換器則是先進行前向神經網路的變換,然後進行自注意力機制和編碼器-解碼器注意力機制。

Transformer 架構與注意力機制

Transformer 模型的 encoder 部分始於輸入序列的 one-hot 編碼 tokens。這些 tokens 透過查表(矩陣)轉換為 $d_{model}$ 維的嵌入向量。嵌入向量隨後乘以 $\sqrt{d_{model}}$,並在訓練過程中隨機初始化和調整。

為了保留序列中的位置訊息,Transformer 對嵌入向量進行位置編碼。原始的 Transformer 實作使用靜態位置編碼,這是一個唯一的向量,根據序列中的位置增加到嵌入向量中。這個靜態編碼是預先計算好的,可以直接使用。

除了靜態位置編碼,還有一種相對位置表示法,透過在注意力機制的 key-value 矩陣中動態編碼位置訊息。這種方法根據每個 token 相對於序列中其他 token 的位置計算相對位置編碼。

Transformer 的 encoder 由多個相同的塊組成,每個塊包含多頭自注意力機制和簡單的前向神經網路(FFN)。多頭自注意力機制允許模型同時考慮多個注意力頭,從而捕捉序列中不同位置之間的複雜關係。

在多頭自注意力機制中,輸入序列的嵌入向量作為查詢(query),而其上下文的嵌入向量作為鍵值儲存(key-value)。注意力機制的輸出向量作為輸入,供後續的模型使用。

多頭自注意力機制

多頭自注意力機制(MHA)產生 $h$ 個注意力向量,每個注意力頭對應一個輸入 token。這些注意力向量隨後透過全連線層(FC)進行線性投影和組合。整個注意力塊不需要顯式的啟用函式,因為 softmax 和點積運算已經引入了非線性。

前向神經網路

簡單的前向神經網路(FFN)定義為:

$$ FFN(x) = ActivationFunc(W_1 x + b_1) W_2 $$

其中 $W_1$、$W_2$ 和 $b_1$ 分別是權重矩陣和偏置向量,$ActivationFunc$ 是啟用函式。

Transformer 架構

Transformer 模型的 encoder 部分由多個相同的塊組成,每個塊包含多頭自注意力機制和簡單的前向神經網路。這種架構允許模型捕捉序列中複雜的關係和模式,並在自然語言處理任務中取得優異的效能。

圖表翻譯:

  graph LR
    A[輸入序列] -->|one-hot編碼|> B[嵌入向量]
    B -->|位置編碼|> C[位置編碼嵌入向量]
    C -->|多頭自注意力機制|> D[注意力向量]
    D -->|全連線層|> E[輸出向量]
    E -->|簡單前向神經網路|> F[最終輸出]

此圖表展示了 Transformer 模型的 encoder 部分,從輸入序列到最終輸出。每個步驟都對應到上述描述的過程。

Transformer 架構中的編碼器實作

Transformer 模型的編碼器(Encoder)是其核心元件之一,負責處理輸入序列並生成相應的表示。編碼器由多個相同的層組成,每層包括兩個子層:自注意力機制(Self-Attention)和前饋神經網路(Feed Forward Network,FFN)。

編碼器的結構

編碼器的結構可以概括如下:

  • 編碼器由 N 個相同的編碼器塊(Encoder Block)組成。
  • 每個編碼器塊包含兩個子層:自注意力機制和前饋神經網路。
  • 自注意力機制和前饋神經網路之間使用殘差連線(Residual Connection)和層歸一化(Layer Normalization)。

實作編碼器

以下是使用 PyTorch 實作編碼器的示例程式碼:

import torch
import torch.nn as nn

class EncoderBlock(nn.Module):
    def __init__(self, size, self_attn, feed_forward):
        super(EncoderBlock, self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size), 2)
        
    def forward(self, x, mask):
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        return self.sublayer[1](x, self.feed_forward)

class Encoder(nn.Module):
    def __init__(self, block, N):
        super(Encoder, self).__init__()
        self.blocks = clones(block, N)
        self.norm = LayerNorm(block.size)
        
    def forward(self, x, mask):
        for layer in self.blocks:
            x = layer(x, mask)
        return self.norm(x)

class SublayerConnection(nn.Module):
    def __init__(self, size):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        
    def forward(self, x, sublayer):
        return x + sublayer(self.norm(x))

def clones(module, N):
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

編碼器的工作流程

編碼器的工作流程可以概括如下:

  1. 對輸入序列進行自注意力機制處理。
  2. 對自注意力機制的輸出進行前饋神經網路處理。
  3. 對前饋神經網路的輸出進行層歸一化和殘差連線。
  4. 對步驟 1-3 的結果進行層歸一化和殘差連線。

編碼器的優點

編碼器的優點包括:

  • 能夠有效地捕捉長距離依賴關係。
  • 能夠平行化處理輸入序列。
  • 能夠學習到複雜的語言模式和結構。

Transformer架構中的EncoderBlock實作

在Transformer架構中,EncoderBlock是一個核心元件,負責處理輸入序列的自注意力機制和前饋神經網路(FFN)。下面是EncoderBlock的實作細節:

EncoderBlock類別

class EncoderBlock(torch.nn.Module):
    def __init__(self, size, self_attn, ffn, dropout=0.1):
        super(EncoderBlock, self).__init__()
        self.self_attn = self_attn
        self.ffn = ffn
        self.sublayers = clones(SublayerConnection(size, dropout), 2)
        self.size = size

    def forward(self, x, mask):
        x = self.sublayers[0](x, lambda x: self.self_attn(x, x, x, mask))
        return self.sublayers[1](x, self.ffn)

SublayerConnection類別

class SublayerConnection(torch.nn.Module):
    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = torch.nn.LayerNorm(size)
        self.dropout = torch.nn.Dropout(dropout)

EncoderBlock的工作流程

  1. 初始化EncoderBlock例項,傳入size、self_attn、ffn和dropout等引數。
  2. 在forward方法中,首先將輸入x傳入self.sublayers[0],並將self.self_attn作為lambda函式的引數。
  3. self.sublayers[0]會將輸入x和self.self_attn的輸出傳入LayerNorm和dropout層。
  4. 然後,將self.sublayers[0]的輸出傳入self.sublayers[1],並將self.ffn作為lambda函式的引數。
  5. self.sublayers[1]會將輸入x和self.ffn的輸出傳入LayerNorm和dropout層。
  6. 最終,傳回self.sublayers[1]的輸出。

注意事項

  • self_attn是多頭自注意力機制的實作。
  • ffn是前饋神經網路的實作。
  • dropout是隨機丟棄神經元的機制,用於防止過度擬合。
  • LayerNorm是層歸一化的實作,用於穩定神經網路的輸出。

透過這個實作,EncoderBlock可以有效地處理輸入序列的自注意力機制和前饋神經網路,為Transformer架構的編碼器提供了強大的功能。

Transformer 中的 PositionwiseFFN 層實作

在 Transformer 架構中,PositionwiseFFN 層是一個重要的元件,負責對輸入序列進行全域性特徵提取和變換。這個層的實作涉及到兩個全連線(Fully Connected)層和一個啟用函式。

PositionwiseFFN 層的公式

PositionwiseFFN 層的輸出可以用以下公式表示:

FFN(x) = W2 * ActivationFunc(W1 * x + b1) + b2

其中,W1 和 W2 是權重矩陣,b1 和 b2 是偏置向量,ActivationFunc 是啟用函式。在這裡,我們使用 SiLU(Sigmoid Linear Unit)啟用函式。

PyTorch 實作

以下是 PositionwiseFFN 層在 PyTorch 中的實作:

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

class PositionwiseFFN(nn.Module):
    def __init__(self, d_model: int, d_ff: int, dropout=0.1):
        super(PositionwiseFFN, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.silu(self.w_1(x))))

在這個實作中,d_model 是輸入序列的維度,d_ff 是中間層的維度,dropout 是 dropout 率。forward 方法定義了 PositionwiseFFN 層的前向傳播過程。

SublayerConnection 層的實作

SublayerConnection 層是 Transformer 架構中的一個重要元件,負責將輸入序列傳遞給子層(sublayer)並進行殘差連線(residual connection)。以下是 SublayerConnection 層的實作:

class SublayerConnection(nn.Module):
    def __init__(self, sublayer):
        super(SublayerConnection, self).__init__()
        self.norm = nn.LayerNorm()
        self.sublayer = sublayer

    def forward(self, x):
        return x + self.sublayer(self.norm(x))

在這個實作中,sublayer 是子層的例項,可以是 MultiHeadedAttention 或 PositionwiseFFN 層。forward 方法定義了 SublayerConnection 層的前向傳播過程。

圖表翻譯:

  graph LR
    x -->|輸入|> SublayerConnection
    SublayerConnection -->|子層輸出|> x + sublayer
    sublayer -->|殘差連線|> x + sublayer
    x + sublayer -->|輸出|> output

這個圖表展示了 SublayerConnection 層的前向傳播過程,包括子層的輸入、子層的輸出、殘差連線和最終輸出。

Transformer 解碼器

Transformer 解碼器負責生成輸出序列,結合編碼器輸出和之前生成的序列。這是一個自迴歸模型,先將初始序列輸入編碼器,然後解碼器根據編碼器輸出和之前生成的序列生成輸出序列,一個token接一個token。

解碼器使用相同的嵌入向量和位置編碼,如編碼器,並包含六個相同的解碼器塊。每個塊由三個子層組成,每個子層使用殘差連線、dropout和正規化。子層包括:

  • 掩蔽多頭自注意力機制:解碼器的自注意力機制是單向的,只能注意之前的序列元素。
  • 解碼器的自注意力機制使用掩蔽來避免非法的前向注意力。

掩蔽是透過在注意力輸入的softmax上增加一個掩蔽矩陣來實作的,掩蔽矩陣將非法的連線遮蔽起來。

掩蔽多頭自注意力機制

掩蔽多頭自注意力機制是解碼器中的一個重要組成部分。它允許解碼器根據之前的序列元素生成下一個token。

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

class MaskedMultiHeadAttention(nn.Module):
    def __init__(self, num_heads, hidden_size):
        super(MaskedMultiHeadAttention, self).__init__()
        self.num_heads = num_heads
        self.hidden_size = hidden_size
        self.query_linear = nn.Linear(hidden_size, hidden_size)
        self.key_linear = nn.Linear(hidden_size, hidden_size)
        self.value_linear = nn.Linear(hidden_size, hidden_size)
        self.dropout = nn.Dropout(0.1)

    def forward(self, query, key, value, mask):
        # 將query、key和value分割成多個頭
        query = self.query_linear(query)
        key = self.key_linear(key)
        value = self.value_linear(value)
        query = query.view(-1, query.size(1), self.num_heads, -1).transpose(1, 2)
        key = key.view(-1, key.size(1), self.num_heads, -1).transpose(1, 2)
        value = value.view(-1, value.size(1), self.num_heads, -1).transpose(1, 2)

        # 計算注意力權重
        attention_weights = torch.matmul(query, key.transpose(-1, -2)) / math.sqrt(query.size(-1))
        attention_weights = attention_weights + mask

        # 過濾掉非法的注意力權重
        attention_weights = F.softmax(attention_weights, dim=-1)
        attention_weights = self.dropout(attention_weights)

        # 計算注意力輸出
        attention_output = torch.matmul(attention_weights, value).transpose(1, 2).contiguous()
        attention_output = attention_output.view(-1, attention_output.size(1) * attention_output.size(2))

        return attention_output

# 示例使用
query = torch.randn(1, 10, 128)
key = torch.randn(1, 10, 128)
value = torch.randn(1, 10, 128)
mask = torch.zeros(1, 1, 10, 10)

attention = MaskedMultiHeadAttention(num_heads=8, hidden_size=128)
output = attention(query, key, value, mask)

解碼器塊

解碼器塊是解碼器的基本組成單元,每個塊由三個子層組成:掩蔽多頭自注意力機制、編碼器-解碼器注意力機制和前向神經網路。

class DecoderBlock(nn.Module):
    def __init__(self, num_heads, hidden_size):
        super(DecoderBlock, self).__init__()
        self.self_attention = MaskedMultiHeadAttention(num_heads, hidden_size)
        self.encoder_attention = MultiHeadAttention(num_heads, hidden_size)
        self.feed_forward = nn.Linear(hidden_size, hidden_size)

    def forward(self, decoder_input, encoder_output, mask):
        # 自注意力機制
        self_attention_output = self.self_attention(decoder_input, decoder_input, decoder_input, mask)

        # 編碼器-解碼器注意力機制
        encoder_attention_output = self.encoder_attention(self_attention_output, encoder_output, encoder_output)

        # 前向神經網路
        feed_forward_output = self.feed_forward(encoder_attention_output)

        return feed_forward_output

# 示例使用
decoder_input = torch.randn(1, 10, 128)
encoder_output = torch.randn(1, 10, 128)
mask = torch.zeros(1, 1, 10, 10)

decoder_block = DecoderBlock(num_heads=8, hidden_size=128)
output = decoder_block(decoder_input, encoder_output, mask)

Transformer 解碼器

Transformer 解碼器由六個相同的解碼器塊組成,每個塊由三個子層組成:掩蔽多頭自注意力機制、編碼器-解碼器注意力機制和前向神經網路。

class TransformerDecoder(nn.Module):
    def __init__(self, num_heads, hidden_size, num_blocks):
        super(TransformerDecoder, self).__init__()
        self.blocks = nn.ModuleList([DecoderBlock(num_heads, hidden_size) for _ in range(num_blocks)])

    def forward(self, decoder_input, encoder_output, mask):
        for block in self.blocks:
            decoder_input = block(decoder_input, encoder_output, mask)
        return decoder_input

# 示例使用
decoder_input = torch.randn(1, 10, 128)
encoder_output = torch.randn(1, 10, 128)
mask = torch.zeros(1, 1, 10, 10)

transformer_decoder = TransformerDecoder(num_heads=8, hidden_size=128, num_blocks=6)
output = transformer_decoder(decoder_input, encoder_output, mask)

這個Transformer解碼器可以用於序列到序列的任務,例如機器翻譯、文字摘要等。

Transformer模型的Decoder實作

在瞭解了Transformer模型的Encoder結構後,我們現在將關注於Decoder的實作。Decoder的主要功能是根據Encoder的輸出序列生成目標序列。在這一節中,我們將詳細介紹Decoder的結構和實作過程。

從技術架構視角來看,Transformer模型的Decoder部分與Encoder部分既有相似之處,也有關鍵性的差異。Decoder同樣採用了多層堆積疊的架構,每層包含自注意力機制、編碼器-解碼器注意力機制和前饋神經網路。然而,Decoder的自注意力機制引入了掩碼機制,確保在生成目標序列時,每個時間步只能關注到其之前的序列訊息,避免了訊息洩露。此外,編碼器-解碼器注意力機制允許Decoder關注Encoder的輸出,從而有效地利用輸入序列的訊息。

分析Decoder的實作細節,可以發現其複雜性主要體現在掩碼多頭注意力機制的設計和編碼器-解碼器注意力機制的整合。掩碼機制需要精確地控制資訊流向,確保模型的因果關係。而編碼器-解碼器注意力機制則需要有效地融合來自Encoder和Decoder的訊息。這些機制的實作需要仔細考慮效能和效率的平衡,例如,如何有效地計算注意力權重和如何處理不同長度的序列。

展望未來,Transformer Decoder的發展方向可能集中在以下幾個方面:更高效的注意力機制,例如線性注意力;更靈活的解碼策略,例如非自迴歸解碼;以及更強的泛化能力,例如跨語言解碼。這些研究方向旨在進一步提升Transformer模型的效能和應用範圍。

對於實務應用而言,深入理解Transformer Decoder的架構和實作細節至關重要。這不僅有助於開發者更好地調優模型引數,還有助於設計更適合特定任務的Transformer變體。玄貓認為,隨著研究的深入和技術的發展,Transformer Decoder將在更多領域展現其強大的能力。