成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Transformer 模型結(jié)構(gòu)詳解及代碼實現(xiàn)!

人工智能 架構(gòu)
Transformer 默認都是大模型,除了一些特例(如 DistilBERT)外,實現(xiàn)更好性能的一般策略是增加模型的大小以及預(yù)訓(xùn)練的數(shù)據(jù)量。

一、Transformer簡要發(fā)展史

以下是Transformer模型發(fā)展歷史中的關(guān)鍵節(jié)點:

Transformer架構(gòu)于2017年6月推出。原本研究的重點是翻譯任務(wù)。隨后推出了幾個有影響力的模型,包括:

時間

模型

簡要說明

2017 年 6 月

「Transformer」

Google 首次提出基于 Attention 的模型,用于機器翻譯任務(wù)

2018 年 6 月

「GPT」

第一個使用 Transformer 解碼器模塊進行預(yù)訓(xùn)練的語言模型,適用于多種 NLP 任務(wù)

2018 年 10 月

「BERT」

使用 Transformer 編碼器模塊,通過掩碼語言建模生成更強大的句子表示

2019 年 2 月

「GPT-2」

更大更強的 GPT 版本,由于潛在風(fēng)險未立即發(fā)布,具備出色的文本生成能力

2019 年 10 月

「DistilBERT」

BERT 的輕量化版本,在保留 97% 性能的同時,速度更快、內(nèi)存占用更低

2019 年 10 月

「BART、T5」

使用完整的 Encoder-Decoder 架構(gòu),在各種 NLP 任務(wù)中表現(xiàn)優(yōu)異

2020 年 5 月

「GPT-3」

超大規(guī)模語言模型,支持“零樣本學(xué)習(xí)”,無需微調(diào)即可完成新任務(wù)

這個列表并不全面,只是為了突出一些不同類型的 Transformer 模型。大體上,它們可以分為三類:

類別

構(gòu)成

特點

典型模型

「GPT-like」

(自回歸 Transformer)

只使用解碼器

自回歸方式預(yù)測下一個詞,適合文本生成任務(wù)

GPT、GPT-2、GPT-3

「BERT-like」

(自動編碼 Transformer)

只使用編碼器

掩碼機制學(xué)習(xí)上下文表示,適合理解類任務(wù)如問答、情感分析

BERT、RoBERTa、DistilBERT

「BART/T5-like」

(序列到序列 Transformer)

編碼器 + 解碼器

完整的 encoder-decoder 架構(gòu),適合翻譯、摘要等生成+理解結(jié)合的任務(wù)

BART、T5

Transformer 默認都是大模型,除了一些特例(如 DistilBERT)外,實現(xiàn)更好性能的一般策略是增加模型的大小以及預(yù)訓(xùn)練的數(shù)據(jù)量。其中,GPT-2 是使用「transformer 解碼器模塊」構(gòu)建的,而 BERT 則是通過「transformer 編碼器」模塊構(gòu)建的。

二、Transformer 整體架構(gòu)

論文中給出用于中英文翻譯任務(wù)的 Transformer 整體架構(gòu)如下圖所示:

可以看出Transformer架構(gòu)由Encoder和Decoder兩個部分組成:其中Encoder和Decoder都是由N=6個相同的層堆疊而成。Multi-Head Attention 結(jié)構(gòu)是 Transformer 架構(gòu)的核心結(jié)構(gòu),其由多個 Self-Attention 組成的。其中,

部件

結(jié)構(gòu)

層數(shù)

主要模塊

Encoder

編碼器層堆疊

N=6層

Self-Attention+Feed Forward

Decoder

解碼器層堆疊

N=6層

Self-Attention+Encoder-Decoder Attention+Feed Forward

Transformer 架構(gòu)更詳細的可視化圖如下所示:

1. 輸入模塊

(1) Tokenizer預(yù)處理

在基于Transformer的大模型LLM中,輸入通常為字符串文本。由于模型無法直接處理自然語言,因此需要借助Tokenizer對輸入進行預(yù)處理。具體流程如下:

  • 分詞(Tokenization):將輸入文本按規(guī)則切分為一個個詞元(token),如單詞、子詞或特殊符號。
  • 詞表映射(Vocabulary Mapping):每個 token 被映射到一個唯一的整數(shù) ID,該 ID 來自預(yù)訓(xùn)練模型所使用的詞匯表。
  • 生成 input_ids 向量(矩陣):最終輸出是一個由 token ID 構(gòu)成的向量(或矩陣),作為模型輸入。

以下是以 Hugging Face 的 transformers 庫為例,展示如何使用 BertTokenizer 和 BertModel 完成輸入文本的預(yù)處理和編碼:

from transformers import BertTokenizer, BertModel
import torch

# 1. 加載預(yù)訓(xùn)練的 BERT tokenizer 和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

# 2. 輸入文本
text = "A Titan RTX has 24GB of VRAM"

# 3. 分詞并映射為 token ID 序列
inputs = tokenizer(text, return_tensors="pt", truncatinotallow=True, padding=True)

# 輸出 token IDs
print("Token IDs:", inputs['input_ids'])

# 4. 傳入模型,獲取輸出
outputs = model(**inputs)

# 5. 獲取最后一層的隱藏狀態(tài)表示
last_hidden_states = outputs.last_hidden_state
print("Last hidden states shape:", last_hidden_states.shape)

原始輸入文本 "A Titan RTX has 24GB of VRAM" 通過 tokenizer 完成分詞和詞表映射工作,生成的輸入 ID 列表:

[101, 138, 28318, 56898, 12674, 10393, 10233, 32469, 10108, 74727, 36535, 102]

其中,

  • 101 表示 [CLS] 標記;
  • 102 表示 [SEP] 標記;
  • 其余為對應(yīng) token 在詞表中的索引。

在所有基于 Transformer 的 LLM 中,唯一必須的輸入是 input_ids,它是由 Tokenizer 映射后的 token 索引組成的整數(shù)向量,代表了輸入文本在詞表中的位置信息。

(2) Embedding 層

在基于 Transformer 的大型語言模型(LLM)中,嵌入層(Embedding Layer)是將輸入 token ID 映射為向量表示的核心組件。其作用是將離散的整數(shù)索引轉(zhuǎn)換為連續(xù)、稠密的向量空間表示,從而便于后續(xù)神經(jīng)網(wǎng)絡(luò)進行語義建模。

? 萬物皆可 Embedding:雖然最常見的是詞嵌入(Word Embedding),但圖像、語音等也可以通過嵌入層映射為向量形式,實現(xiàn)統(tǒng)一建模。

例如,mnist 數(shù)據(jù)集中的圖片,可以通過嵌入層來表示,如下圖所示,每個點代表一個圖片(10000*784),通過嵌入層,將圖片的像素點轉(zhuǎn)化為稠密的向量,然后通過 t-SNE/pca 降維,可以看到圖片的空間分布。

LLM 中,單詞 token 需要經(jīng)過 Embedding 層,Embedding 層的作用是將輸入的離散化表示(例如 token ids)轉(zhuǎn)換為連續(xù)的低維向量表示,其由單詞 Embedding 和位置 Embedding (Positional Encoding)相加得到,通常定義為 TransformerEmbedding 層。

① 單詞嵌入(Token Embedding)」

自然語言處理中,輸入文本通常是以符號形式存在的詞匯,而這些離散符號無法直接被神經(jīng)網(wǎng)絡(luò)處理。因此需要一個可學(xué)習(xí)的嵌入矩陣將每個 token 轉(zhuǎn)換為固定維度的向量。

工作原理:

a. 輸入是一個形狀為 [batch_size, seq_len] 的整數(shù)張量,表示每個 token 在詞表中的索引;

b. 輸出是一個形狀為 [batch_size, seq_len, d_model] 的三維張量,其中:

  • d_model 是嵌入維度(如 512 或 768);
  • 每個 token 對應(yīng)一個 d_model 維的向量;

c. 嵌入層權(quán)重矩陣大小為 [vocab_size, d_model],參數(shù)量為:

在 PyTorch 中,詞嵌入層通常使用 torch.nn.Embedding 模塊實現(xiàn),其作用是將 token 的索引轉(zhuǎn)換為低維語義向量表示。

? 輸入與輸出說明

類型

描述

輸入

一個整數(shù)張量,表示每個 token 在詞表中的索引

輸入形狀

(batch_size, sequence_length)
其中:
- batch_size:批次大小(即一次處理多少條文本)
- sequence_length:每條文本包含的 token 數(shù)量

輸出

每個 token 被映射到 embedding_dim 維度的稠密向量

輸出形狀

(batch_size, sequence_length, embedding_dim)

  • embedding_dim 是嵌入向量的維度,也稱為詞向量維度;
  • 它通常被設(shè)置為 d_model 或 h,即后續(xù) Transformer 層使用的隱藏層維度(如 512 或 768).

?? 示例代碼:構(gòu)建 Token Embedding 層

from transformers import BertTokenizer
import torch.nn as nn

## 1, 使用 BERT tokenizer 將批量輸入的字符串文本序列轉(zhuǎn)化為 input_ids
tokenizer = BertTokenizer.from_pretrained("bert-base-multilingual-cased") 
batch_text = ["A Titan RTX has 24GB of VRAM", "I have a dog and cat"]
inputs = tokenizer(batch_text, return_tensors="pt", truncation=True, padding=True)
input_ids = inputs["input_ids"]

# 2. 創(chuàng)建一個 nn.Embedding 層
vocab_size = tokenizer.vocab_size  # 詞表大小取決于你加載的具體 tokenizer 模型
embedding_dim = 512  # 嵌入向量的維度,參考 transformer 論文的大小
embedding_layer = nn.Embedding(vocab_size, embedding_dim)

# 3. 通過 nn.Embedding 層,將輸入的 IDs 映射到嵌入向量
embedded_output = embedding_layer(input_ids)

# 4. 輸出嵌入向量的形狀
print("嵌入向量的形狀:", embedded_output.shape)  # (batch_size, sequence_length, embedding_dim), torch.Size([2, 12, 512])

# 5. 打印嵌入向量
print(embedded_output)

程序運行后輸出結(jié)果如下所示:

② 位置嵌入(Positional Encoding)」

由于 Transformer 不依賴于 RNN 的順序性建模方式,它必須顯式地引入位置信息,以保留 token 在序列中的位置特征。

為此,Transformer 使用了 Sinusoidal Positional Encoding(正弦/余弦位置編碼):

其中:

  • pos:token 在序列中的位置;
  • i:維度索引;
  • d_model:嵌入維度。

③ TransformerEmbedding 層集成

transformer 輸入模塊有三個組成部分:文本/提示詞、分詞器(Tokenizer)和嵌入層(Embeddings)。輸入模塊的工作流程和代碼實現(xiàn)如下所示:

矩陣的每一列表示一個 token 的嵌入向量。

class PositionalEncoding(nn.Module):
    """
    compute sinusoid encoding.
    """
    def __init__(self, d_model, max_len, device):
        """
        constructor of sinusoid encoding class

        :param d_model: dimension of model
        :param max_len: max sequence length
        :param device: hardware device setting
        """
        super(PositionalEncoding, self).__init__()

        # same size with input matrix (for adding with input matrix)
        self.encoding = torch.zeros(max_len, d_model, device=device)
        self.encoding.requires_grad = False  # we don't need to compute gradient

        pos = torch.arange(0, max_len, device=device)
        pos = pos.float().unsqueeze(dim=1)
        # 1D => 2D unsqueeze to represent word's position

        _2i = torch.arange(0, d_model, step=2, device=device).float()
        # 'i' means index of d_model (e.g. embedding size = 50, 'i' = [0,50])
        # "step=2" means 'i' multiplied with two (same with 2 * i)

        self.encoding[:, 0::2] = torch.sin(pos / (10000 ** (_2i / d_model)))
        self.encoding[:, 1::2] = torch.cos(pos / (10000 ** (_2i / d_model)))
        # compute positional encoding to consider positional information of words

    def forward(self, x):
        # self.encoding
        # [max_len = 512, d_model = 512]

        batch_size, seq_len = x.size()
        # [batch_size = 128, seq_len = 30]

        return self.encoding[:seq_len, :]
        # [seq_len = 30, d_model = 512]
        # it will add with tok_emb : [128, 30, 512]         

class TokenEmbedding(nn.Embedding):
    """
    Token Embedding using torch.nn
    they will dense representation of word using weighted matrix
    """

    def __init__(self, vocab_size, d_model):
        """
        class for token embedding that included positional information
        :param vocab_size: size of vocabulary
        :param d_model: dimensions of model
        """
        super(TokenEmbedding, self).__init__(vocab_size, d_model, padding_idx=1)

class TransformerEmbedding(nn.Module):
    """
    token embedding + positional encoding (sinusoid)
    positional encoding can give positional information to network
    """

    def __init__(self, vocab_size, max_len, d_model, drop_prob, device):
        """
        class for word embedding that included positional information
        :param vocab_size: size of vocabulary
        :param d_model: dimensions of model
        """
        super(TransformerEmbedding, self).__init__()
        self.tok_emb = TokenEmbedding(vocab_size, d_model)
        # self.position_embedding = nn.Embedding(max_len, embed_size)
        self.pos_emb = PositionalEncoding(d_model, max_len, device)
        self.drop_out = nn.Dropout(p=drop_prob)

    def forward(self, x):
        tok_emb = self.tok_emb(x)
        pos_emb = self.pos_emb(x)
        return self.drop_out(tok_emb + pos_emb)

2. Multi-Head Attention 結(jié)構(gòu)

Encoder 和 Decoder 結(jié)構(gòu)中公共的 layer 之一是 Multi-Head Attention,其是由多個 Self-Attention 并行組成的。Encoder block 只包含一個 Multi-Head Attention,而 Decoder block 包含兩個 Multi-Head Attention (其中有一個用到 Masked)。

(1) Self-Attention 結(jié)構(gòu)

Self-Attention 中文翻譯為自注意力機制,論文中叫作 Scale Dot Product Attention,它是 Transformer 架構(gòu)的核心,使得每個 token 能夠關(guān)注整個序列中的其他 token,從而建立全局依賴關(guān)系。其結(jié)構(gòu)如下圖所示:

(2) Self-Attention 實現(xiàn)

? 在本文中,Self-Attention 層與論文中的 ScaleDotProductAttention 層意義一致,實現(xiàn)方式完全相同。

?? 數(shù)學(xué)定義

Self-Attention 的計算過程可以表示為:

其中:

  •  :Query 向量;
  • :Key 向量;
  • :Value 向量;
  • :Query 和 Key 的維度;
  • Softmax 對注意力分數(shù)按最后一個維度歸一化;
  • :用于縮放點積,防止 softmax 梯度消失;

輸入來源:

  • 輸入詞向量經(jīng)過 Embedding 層后,進入位置編碼層;
  • 再通過線性變換(Linear 層),分別生成 Query、Key 和 Value 向量;
  • 這三個向量的形狀通常為 [batch_size, seq_len, d_k] 或 [seq_len, d_k]。

計算步驟如下:

① 計算注意力分數(shù)矩陣

  • 其中 是 Key 張量的轉(zhuǎn)置;
  • 點積結(jié)果是一個 [seq_len, seq_len] 的注意力得分矩陣;
  • 使用 softmax 歸一化,得到注意力權(quán)重。

② 應(yīng)用掩碼(可選)

  • 在 Decoder 中使用 Masked Self-Attention,防止未來信息泄露;
  • 若傳入 mask,將對應(yīng)位置設(shè)為極小值(如 -1e9)以抑制其影響。

③ 加權(quán)聚合 Value 向量

  • 將 softmax 后的注意力權(quán)重與 Value 相乘,得到上下文感知的輸出張量;
  • 輸出維度保持與輸入一致:[batch_size, seq_len, d_v]。

?? 代碼實現(xiàn):

import torch
import math
import torch.nn as nn

class ScaleDotProductAttention(nn.Module):
    def __init__(self):
        """
        初始化 Self-Attention 層,僅包含一個 softmax 操作。
        """
        super(ScaleDotProductAttention, self).__init__()
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, Q: torch.Tensor, K: torch.Tensor, V: torch.Tensor, mask: torch.Tensor = None):
        """
        Self-Attention 前向傳播函數(shù)

        :param Q: Query 向量,形狀為 [batch_size, seq_len, d_k]
        :param K: Key 向量,形狀為 [batch_size, seq_len, d_k]
        :param V: Value 向量,形狀為 [batch_size, seq_len, d_v]
        :param mask: 掩碼張量,形狀為 [batch_size, seq_len, seq_len]
        :return: 
            output: 加權(quán)后的 Value 向量,形狀為 [batch_size, seq_len, d_v]
            attn_weights: 注意力權(quán)重矩陣,形狀為 [batch_size, seq_len, seq_len]
        """
        # 1. 計算 QK^T 得到注意力分數(shù)
        K_T = K.transpose(-1, -2)  # [batch_size, d_k, seq_len]
        scores = torch.matmul(Q, K_T) / math.sqrt(Q.size(-1))  # [batch_size, seq_len, seq_len]

        # 2. 如果有 mask,應(yīng)用掩碼(例如 Decoder 中防止看到未來詞)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)

        # 3. 應(yīng)用 softmax 得到注意力權(quán)重
        attn_weights = self.softmax(scores)  # [batch_size, seq_len, seq_len]

        # 4. 權(quán)重 × Value 得到最終輸出
        output = torch.matmul(attn_weights, V)  # [batch_size, seq_len, d_v]

        return output, attn_weights

?? 示例調(diào)用與輸出解析:

# 創(chuàng)建 Q、K、V 張量
Q = torch.randn(5, 10, 64)  # [batch_size=5, seq_len=10, d_k=64]
K = torch.randn(5, 10, 64)
V = torch.randn(5, 10, 64)

# 創(chuàng)建 Self-Attention 層
attention = ScaleDotProductAttention()

# 前向傳播
output, attn_weights = attention(Q, K, V)

# 打印輸出形狀
print(f"ScaleDotProductAttention output shape: {output.shape}")      # [5, 10, 64]
print(f"attn_weights shape: {attn_weights.shape}")                # [5, 10, 10]

變量

形狀

描述

Q, K, V

[5, 10, 64]

batch=5,序列長度=10,嵌入維度=64

scores

[5, 10, 10]

注意力得分矩陣,反映 token 之間的相似度

attn_weights

[5, 10, 10]

softmax 后的注意力權(quán)重,用于加權(quán)聚合 Value

output

[5, 10, 64]

最終輸出,融合了上下文信息的 Value 加權(quán)表示

(3) Multi-Head Attention

Multi-Head Attention(MHA)是在Self-Attention基礎(chǔ)上引入的一種增強機制。其核心理念是:將輸入向量空間劃分為多個子空間,在每個子空間中獨立計算Self-Attention,最后將多個子空間的輸出拼接在一起并進行線性變換,從而得到最終的輸出。

對于 MHA,之所以需要對 Q、K、V 進行多頭(head)劃分,其目的是為了增強模型對不同信息的關(guān)注。具體來說,多組 Q、K、V 分別計算 Self-Attention,每個頭自然就會有獨立的 Q、K、V 參數(shù),從而讓模型同時關(guān)注多個不同的信息,這有些類似 CNN 架構(gòu)模型的多通道機制。

下圖是論文中 Multi-Head Attention 的結(jié)構(gòu)圖。

從圖中可以看出, MHA 結(jié)構(gòu)的計算過程可總結(jié)為下述步驟:

  • 將輸入 Q、K、V 張量進行線性變換(Linear 層),輸出張量尺寸為 [batch_size, seq_len, d_model];
  • 將前面步驟輸出的張量,按照頭的數(shù)量(n_head)拆分為 n_head 子張量,其尺寸為 [batch_size, n_head, seq_len, d_model//n_head];
  • 每個子張量并行計算注意力分數(shù),即執(zhí)行 dot-product attention 層,輸出張量尺寸為 [batch_size, n_head, seq_len, d_model//n_head];
  • 將這些子張量進行拼接 concat ,并經(jīng)過線性變換得到最終的輸出張量,尺寸為 [batch_size, seq_len, d_model]。

?? 數(shù)學(xué)表達式

其中:

  • :第 i 個 head 的可學(xué)習(xí)參數(shù);
  • :最終輸出的線性變換矩陣;
  • Concat表示將各個 head 的輸出拼接在一起。

(4) Multi-Head Attention 實現(xiàn)

import torch
import math
import torch.nn as nn

class MultiHeadAttention(nn.Module):
    """Multi-Head Attention Layer"""
    
    def __init__(self, d_model, n_head):
        """
        Args:
            d_model: 模型嵌入維度(通常為 512 或 768);
            n_head: 注意力頭的數(shù)量(如 8);
        """
        super(MultiHeadAttention, self).__init__()
        
        # 初始化參數(shù)
        self.n_head = n_head
        self.attention = ScaleDotProductAttention()  # 使用前面定義的 Self-Attention
        
        # 線性變換層
        self.w_q = nn.Linear(d_model, d_model)       # Query 變換
        self.w_k = nn.Linear(d_model, d_model)       # Key 變換
        self.w_v = nn.Linear(d_model, d_model)       # Value 變換
        self.fc = nn.Linear(d_model, d_model)         # 輸出投影層

    def forward(self, q, k, v, mask=None):
        """
        Args:
            q: Query 張量,[batch_size, seq_len, d_model]
            k: Key 張量,[batch_size, seq_len, d_model]
            v: Value 張量,[batch_size, seq_len, d_model]
            mask: 掩碼張量,[batch_size, seq_len, seq_len]
        """
        # Step 1: 線性變換
        q, k, v = self.w_q(q), self.w_k(k), self.w_v(v)

        # Step 2: 拆分到多個 head
        q = self.split(q)   # [batch_size, n_head, seq_len, d_tensor]
        k = self.split(k)   # [batch_size, n_head, seq_len, d_tensor]
        v = self.split(v)   # [batch_size, n_head, seq_len, d_tensor]

        # Step 3: 計算每個 head 的 attention
        sa_output, attn_weights = self.attention(q, k, v, mask)

        # Step 4: 拼接所有 head 的輸出
        mha_output = self.concat(sa_output)  # [batch_size, seq_len, d_model]

        # Step 5: 最終線性變換
        mha_output = self.fc(mha_output)

        return mha_output, attn_weights

    def split(self, tensor):
        """
        拆分輸入張量為多個 head

        Args:
            tensor: [batch_size, seq_len, d_model]
        Returns:
            [batch_size, n_head, seq_len, d_tensor]
        """
        batch_size, seq_len, d_model = tensor.size()
        d_tensor = d_model // self.n_head  # 每個 head 的維度
        
        # reshape + transpose 實現(xiàn)拆分
        tensor = tensor.view(batch_size, seq_len, self.n_head, d_tensor)
        tensor = tensor.transpose(1, 2)  # [batch_size, n_head, seq_len, d_tensor]
        
        return tensor

    def concat(self, sa_output):
        """
        拼接多個 head 的輸出

        Args:
            sa_output: [batch_size, n_head, seq_len, d_tensor]
        Returns:
            [batch_size, seq_len, d_model]
        """
        batch_size, n_head, seq_len, d_tensor = sa_output.size()
        d_model = n_head * d_tensor
        
        # transpose + reshape 實現(xiàn)合并
        sa_output = sa_output.transpose(1, 2).contiguous().view(batch_size, seq_len, d_model)
        
        return sa_output

?? 示例調(diào)用與輸出解析:

# 定義參數(shù)
d_model = 512
n_head = 8
seq_len = 10
batch_size = 32

# 創(chuàng)建 Q、K、V 張量
Q = torch.randn(batch_size, seq_len, d_model)
K = torch.randn(batch_size, seq_len, d_model)
V = torch.randn(batch_size, seq_len, d_model)

# 構(gòu)建 MHA 層
mha_layer = MultiHeadAttention(d_model=d_model, n_head=n_head)

# 前向傳播
output, weights = mha_layer(Q, K, V)

# 打印輸出形狀
print("MHA Output Shape:", output.shape)      # [32, 10, 512]
print("Attn Weights Shape:", weights.shape)   # [32, 8, 10, 10]

變量

形狀

描述

Q, K, V

[32, 10, 512]

輸入張量,表示 batch=32,seq_len=10,d_model=512

q, k, v

[32, 8, 10, 64]

拆分后的 Q/K/V,每個 head 64 維

sa_output

[32, 8, 10, 64]

每個 head 的 attention 輸出

mha_output

[32, 10, 512]

拼接后的最終輸出

attn_weights

[32, 8, 10, 10]

每個 head 的注意力權(quán)重矩陣

3. Encoder結(jié)構(gòu)

Transformer 中的 Encoder 是整個模型中用于編碼輸入序列的部分。它由 N=6 個相同的 encoder block 堆疊而成。每個 encoder block 主要包含兩個子層(sub-layers):多頭自注意力機制(Multi-Head Self-Attention)和位置全連接前饋網(wǎng)絡(luò)(Position-wise Feed Forward Network)。

這兩個子層之間都使用了 殘差連接(Residual Connection) 和 層歸一化(Layer Normalization),以增強訓(xùn)練穩(wěn)定性和模型表達能力。

下圖中紅色框選部分表示一個標準的 Encoder Block,其內(nèi)部結(jié)構(gòu)如下:

由以下四個關(guān)鍵部分構(gòu)成。

模塊

描述

Multi-Head Attention

使用多個 attention head 并行提取序列中的不同特征

Add & Norm (1)

殘差連接(Residual Connection)+ 層歸一化(LayerNorm)

Position-wise FeedForward

兩層線性變換 + 激活函數(shù),對每個詞獨立建模

Add & Norm (2)

同樣應(yīng)用殘差連接和 LayerNorm

(1) 每一層的計算流程(以單個 encoder block 為例)

① 多頭自注意力機制(Multi-Head Self-Attention)

  • 輸入:嵌入后的張量 
  • 輸出:通過自注意力加權(quán)后的新張量 sa_output
sa_output, attn_weights = MultiHeadAttention(q=x, k=x, v=x, mask=src_mask)

其中,

  • Query、Key 和 Value 來自同一個輸入 ;
  • 可選 mask 通常用于屏蔽 padding token 或控制位置感知范圍。

② 殘差連接 + 層歸一化(Sublayer 1)

x = x + dropout(sa_output)
x = layer_norm(x)
  • 應(yīng)用殘差映射,緩解梯度消失問題;
  • 使用 LayerNorm 對每個 token 的向量進行標準化處理;
  • 整體目標:提升模型表達能力與訓(xùn)練穩(wěn)定性。

③ 位置全連接前饋網(wǎng)絡(luò)(Position-wise FeedForward)

定義為: 

nn.Sequential(
    nn.Linear(d_model, d_ff),
    nn.ReLU(),
    nn.Linear(d_ff, d_model),
    nn.Dropout(drop_prob)
)
  • d_model:模型隱層維度(如 512);
  • d_ff:FeedForward 網(wǎng)絡(luò)中間維度(如 2048);
  • ReLU 導(dǎo)致非線性更強的語義表達。

④ 再次殘差連接 + 層歸一化(Sublayer 2)

x = x + dropout(ffn_output)
x = layer_norm(x)
  • 保證模型在經(jīng)過復(fù)雜變換后仍能保留原始信息;
  • 達成對上下文感知表示的穩(wěn)定學(xué)習(xí)。

(2) 維度變化說明(輸入輸出保持一致)

無論經(jīng)過多少層 Encoder block,每個 block 的輸入與輸出形狀始終一致:

張量

形狀

描述

輸入

[batch_size, seq_len, d_model]

批次大小 × 序列長度 × 模型維度

MHA 輸出

[batch_size, seq_len, d_model]

注意力加權(quán)后的輸出

FFN 輸出

[batch_size, seq_len, d_model]

每個 Token 的前饋網(wǎng)絡(luò)輸出

最終輸出

[batch_size, seq_len, d_model]

經(jīng)過兩次 Sublayer 后仍然保持相同維度

(3) PyTorch 模塊封裝示例

import torch
import torch.nn as nn

class EncoderBlock(nn.Module):
    def __init__(self, d_model, n_head, d_ff, drop_prob=0.1):
        """
        Args:
            d_model: 嵌入維度(例如 512)
            n_head: 多頭數(shù)量(通常設(shè)為 8)
            d_ff: Feed Forward 網(wǎng)絡(luò)中間維度(通常為 2048)
            drop_prob: Dropout 概率
        """
        super(EncoderBlock, self).__init__()
        self.attention = MultiHeadAttention(d_model, n_head)
        self.norm1 = nn.LayerNorm(d_model)
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.ReLU(),
            nn.Linear(d_ff, d_model),
            nn.Dropout(drop_prob)
        )
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(drop_prob)

    def forward(self, x, src_mask=None):
        # Step 1: Multi-Head Self-Attention
        sa_output, _ = self.attention(x, x, x, src_mask)
        x = self.norm1(x + self.dropout(sa_output))

        # Step 2: Position-wise FeedForward
        ffn_output = self.ffn(x)
        x = self.norm2(x + self.dropout(ffn_output))

        return x

(4) 封裝整個 Encoder 模塊

有了 EncoderBlock 后,我們可以將它 重復(fù) N 次 構(gòu)建完整的 Encoder:

class TransformerEncoder(nn.Module):
    def __init__(self, num_layers, d_model, n_head, d_ff, drop_prob=0.1):
        """
        Args:
            num_layers: encoder block 堆疊層數(shù)(原論文為 6)
            d_model: 模型維度(如 512)
            n_head: 注意力頭數(shù)(如 8)
            d_ff: FeedForward 網(wǎng)絡(luò)維度(如 2048)
        """
        super(TransformerEncoder, self).__init__()
        self.blocks = nn.ModuleList([
            EncoderBlock(d_model, n_head, d_ff, drop_prob)
            for _ in range(num_layers)
        ])

    def forward(self, x, mask=None):
        for block in self.blocks:
            x = block(x, mask)
        return x

?? 示例調(diào)用

# 創(chuàng)建輸入張量
x = torch.randn(batch_size=32, seq_len=20, d_model=512)  # [32, 20, 512]

# 構(gòu)建 Encoder
encoder = TransformerEncoder(num_layers=6, d_model=512, n_head=8, d_ff=2048)
output = encoder(x)

print("Encoder 輸出形狀:", output.shape)  # [32, 20, 512]

4. Decoder結(jié)構(gòu)

Decoder是Transformer架構(gòu)中用于生成輸出序列的部分。與Encoder類似,它由N=6個相同的Decoder block堆疊而成,但結(jié)構(gòu)更為復(fù)雜。

(1) Decoder Block 的核心組件

一個標準的Decoder block包含三個主要子層:

  • Masked Multi-Head Self-Attention
  • Encoder-Decoder Attention
  • Position-wise Feed Forward Network

每個子層后面都跟隨 殘差連接(Residual Connection) 和 層歸一化(Layer Normalization)。

如下圖右側(cè)紅框表示一個標準的 Decoder Block。

①「Masked Multi-Head Self-Attention」

這是Decoder的第一個注意力機制,用于處理目標語言的輸入序列(即解碼器自身的輸入)。

  • 使用 masking 技術(shù) 防止在預(yù)測當前詞時看到未來的詞,保持因果關(guān)系。
  • 實現(xiàn)方式:通過 trg_mask 屏蔽未來信息。
x = self.mha1(q=dec_out, k=dec_out, v=dec_out, mask=trg_mask)

②「Encoder-Decoder Attention」

這是 Decoder 的第二個注意力機制,用于將 Encoder 的輸出信息融合到 Decoder 中。

  • Query (Q) 來自上一層 Decoder 的輸出;
  • Key (K) 和 Value (V) 來自 Encoder 的輸出;
  • 這樣 Decoder 在生成每個詞時都能關(guān)注到整個輸入句子的信息。
x = self.mha2(q=x, k=enc_out, v=enc_out, mask=src_mask)

③ 「Position-wise Feed Forward Network」

這是一個簡單的兩層全連接網(wǎng)絡(luò),對每個位置的向量進行非線性變換。

x = self.ffn(x)

④「殘差連接 + 層歸一化(Add & Norm)」

每個子層都應(yīng)用:

x = self.ln(x_residual + dropout(sublayer_output))
  • 提升訓(xùn)練穩(wěn)定性;
  • 緩解梯度消失問題。

⑤「Decoder的完整實現(xiàn)」

DecoderLayer類:

class DecoderLayer(nn.Module):
    def __init__(self, d_model, ffn_hidden, n_head, drop_prob):
        super(DecoderLayer, self).__init__()
        # 第一個 Multi-Head Attention: Masked Self-Attention
        self.mha1 = MultiHeadAttention(d_model, n_head)
        self.ln1 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(p=drop_prob)

        # 第二個 Multi-Head Attention: Encoder-Decoder Attention
        self.mha2 = MultiHeadAttention(d_model, n_head)
        self.ln2 = nn.LayerNorm(d_model)
        self.dropout2 = nn.Dropout(p=drop_prob)

        # 前饋網(wǎng)絡(luò)
        self.ffn = PositionwiseFeedForward(d_model, ffn_hidden)
        self.ln3 = nn.LayerNorm(d_model)
        self.dropout3 = nn.Dropout(p=drop_prob)

    def forward(self, dec_out, enc_out, trg_mask, src_mask):
        x_residual1 = dec_out
        # Step 1: Masked Self-Attention
        x = self.mha1(q=dec_out, k=dec_out, v=dec_out, mask=trg_mask)
        x = self.ln1(x_residual1 + self.dropout1(x))

        if enc_out is not None:
            # Step 2: Encoder-Decoder Attention
            x_residual2 = x
            x = self.mha2(q=x, k=enc_out, v=enc_out, mask=src_mask)
            x = self.ln2(x_residual2 + self.dropout2(x))

        # Step 3: Position-wise Feed Forward
        x_residual3 = x
        x = self.ffn(x)
        x = self.ln3(x_residual3 + self.dropout3(x))

        return x

Decoder 類:

class Decoder(nn.Module):
    def __init__(self, dec_voc_size, max_len, d_model, ffn_hidden, n_head, n_layers, drop_prob, device):
        super().__init__()
        # 輸入嵌入 + 位置編碼
        self.emb = TransformerEmbedding(
            d_model=d_model,
            drop_prob=drop_prob,
            max_len=max_len,
            vocab_size=dec_voc_size,
            device=device
        )

        # 堆疊多個 Decoder Layer
        self.layers = nn.ModuleList([
            DecoderLayer(
                d_model=d_model,
                ffn_hidden=ffn_hidden,
                n_head=n_head,
                drop_prob=drop_prob
            ) for _ in range(n_layers)
        ])

        # 最終輸出層:映射到目標詞匯表大小
        self.linear = nn.Linear(d_model, dec_voc_size)

    def forward(self, trg, src, trg_mask, src_mask):
        # trg: 目標序列 [batch_size, trg_seq_len]
        # src: Encoder 輸出 [batch_size, src_seq_len, d_model]
        trg = self.emb(trg)

        for layer in self.layers:
            trg = layer(trg, src, trg_mask, src_mask)

        # 輸出:[batch_size, trg_seq_len, dec_voc_size]
        output = self.linear(trg)
        return output

三、Transformer實現(xiàn)

基于前面實現(xiàn)的 Encoder 和 Decoder 組件,我們就可以實現(xiàn) Transformer 模型的完整代碼,如下所示:

import torch
from torch import nn

class Transformer(nn.Module):
    def __init__(self, src_pad_idx, trg_pad_idx, trg_sos_idx, enc_voc_size, dec_voc_size, 
                 d_model, n_head, max_len, ffn_hidden, n_layers, drop_prob, device):
        super().__init__()
        
        # 保存特殊標記的索引
        self.src_pad_idx = src_pad_idx  # 源語言填充索引
        self.trg_pad_idx = trg_pad_idx  # 目標語言填充索引
        self.trg_sos_idx = trg_sos_idx  # 目標語言起始符號索引
        self.device = device  # 設(shè)備信息
        
        # 構(gòu)建 Encoder
        self.encoder = Encoder(
            d_model=d_model,  # 模型維度
            n_head=n_head,  # 注意力頭數(shù)量
            max_len=max_len,  # 最大序列長度
            ffn_hidden=ffn_hidden,  # 前饋網(wǎng)絡(luò)隱藏層維度
            enc_voc_size=enc_voc_size,  # 源語言詞匯表大小
            drop_prob=drop_prob,  # Dropout 概率
            n_layers=n_layers,  # Encoder 層數(shù)
            device=device  # 設(shè)備信息
        )
        
        # 構(gòu)建 Decoder
        self.decoder = Decoder(
            d_model=d_model,  # 模型維度
            n_head=n_head,  # 注意力頭數(shù)量
            max_len=max_len,  # 最大序列長度
            ffn_hidden=ffn_hidden,  # 前饋網(wǎng)絡(luò)隱藏層維度
            dec_voc_size=dec_voc_size,  # 目標語言詞匯表大小
            drop_prob=drop_prob,  # Dropout 概率
            n_layers=n_layers,  # Decoder 層數(shù)
            device=device  # 設(shè)備信息
        )

    def forward(self, src, trg):
        # 創(chuàng)建源序列的 padding mask
        src_mask = self.make_pad_mask(src, src, self.src_pad_idx, self.src_pad_idx)
        
        # 創(chuàng)建目標序列到源序列的 mask
        src_trg_mask = self.make_pad_mask(trg, src, self.trg_pad_idx, self.src_pad_idx)
        
        # 創(chuàng)建目標序列的 padding mask 和因果mask的組合
        trg_mask = self.make_pad_mask(trg, trg, self.trg_pad_idx, self.trg_pad_idx) * \
                   self.make_no_peak_mask(trg, trg)

        # 編碼器前向傳播
        enc_src = self.encoder(src, src_mask)
        
        # 解碼器前向傳播
        output = self.decoder(trg, enc_src, trg_mask, src_trg_mask)
        
        return output

    def make_pad_mask(self, q, k, q_pad_idx, k_pad_idx):
        # 獲取輸入序列長度
        len_q, len_k = q.size(1), k.size(1)

        # 創(chuàng)建針對 key 的 mask
        # batch_size x 1 x 1 x len_k
        k_mask = k.ne(k_pad_idx).unsqueeze(1).unsqueeze(2)
        # batch_size x 1 x len_q x len_k
        k_mask = k_mask.repeat(1, 1, len_q, 1)

        # 創(chuàng)建針對 query 的 mask
        # batch_size x 1 x len_q x 1
        q_mask = q.ne(q_pad_idx).unsqueeze(1).unsqueeze(3)
        # batch_size x 1 x len_q x len_k
        q_mask = q_mask.repeat(1, 1, 1, len_k)
        
        # 組合兩個 mask
        mask = k_mask & q_mask
        
        return mask

    def make_no_peak_mask(self, q, k):
        # 創(chuàng)建因果mask,防止解碼器看到未來信息
        len_q, len_k = q.size(1), k.size(1)
        
        # 創(chuàng)建下三角矩陣,保證解碼器只能關(guān)注當前詞及之前的詞
        mask = torch.tril(torch.ones(len_q, len_k)).type(torch.BoolTensor).to(self.device)
        
        return mask
責任編輯:趙寧寧 來源: 小喵學(xué)AI
相關(guān)推薦

2025-01-02 15:40:23

2025-01-16 12:30:00

2020-11-12 18:53:34

代碼Transformer編程

2010-09-10 14:24:27

CSS盒狀模型

2024-08-15 11:37:05

2023-12-05 13:38:11

架構(gòu)模型

2023-01-03 10:06:08

模型計算

2023-06-05 14:04:59

模型AI

2021-10-11 09:38:46

模型Transformer代碼

2022-06-20 07:16:25

機器學(xué)習(xí)模型Codex

2022-07-22 07:18:53

代碼DeepMind

2024-01-30 01:12:37

自然語言時間序列預(yù)測Pytorch

2021-01-07 08:12:47

數(shù)據(jù)結(jié)構(gòu)二叉樹

2011-07-20 15:20:14

IPhone AVAudioRec

2024-08-19 02:35:00

模型量化深度學(xué)習(xí)

2024-10-22 17:24:32

2023-12-08 09:15:53

Java單表樹形結(jié)構(gòu)Tree

2009-08-13 15:41:50

C#結(jié)構(gòu)體指針

2025-01-16 08:30:00

LLMAI訓(xùn)練

2025-04-25 09:00:00

Transforme模型代碼
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 综合天天久久 | 久久综合爱 | 欧美啊v在线观看 | 国产一区二区三区 | 日韩欧美中文 | 国产一在线观看 | 国产我和子的乱视频网站 | 欧美1—12sexvideos | 国产毛片久久久久久久久春天 | 成人免费观看男女羞羞视频 | 亚洲免费大片 | 国产一级免费视频 | 国产成人99久久亚洲综合精品 | 欧美色影院 | 黄色成人免费在线观看 | 人人爽人人爽 | 狠狠久久 | 精产国产伦理一二三区 | 精品免费国产一区二区三区四区介绍 | av色噜噜| 精品一区在线免费观看 | 天天操天天摸天天爽 | 久草视频在线播放 | 国产我和子的乱视频网站 | 欧美黄在线观看 | 亚洲永久入口 | 黄页网址在线观看 | 日韩精品一区二区三区中文在线 | 日韩伦理一区二区三区 | 日本羞羞影院 | 黄色一级大片视频 | 国产精品久久久久久久久久三级 | 国产精品久久久久久久久久久免费看 | 亚洲图片一区二区三区 | 国产成人免费视频 | 日韩在线不卡 | 精品一级毛片 | 精品久久久久久亚洲精品 | 狠狠入ady亚洲精品经典电影 | 日韩精品在线视频免费观看 | 你懂的免费在线 |