你以為走不出的淤泥,也遲早會云淡風輕
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?—— 25.5.31
引言 ——《Attention is all you need》
???????《Attention is all you need》這篇論文可以說是自然語言處理領域的一座里程碑,它提出的 Transformer 結構帶來了一場技術革命。
研究背景與目標
????????在 Transformer 出現之前,自然語言處理領域主要依賴循環神經網絡(RNN)及其變體,如長短期記憶網絡(LSTM)和門控循環單元(GRU),還有卷積神經網絡(CNN)來處理序列數據。然而,RNN 存在順序計算的限制,難以并行化,而且隨著序列長度增加,信息容易丟失,LSTM 等雖有所改進,但對于長期依賴問題仍不夠理想;CNN 在處理長序列數據時,不同位置信號關聯的操作數會隨距離增長,學習遠距離依賴較困難。因此,論文旨在尋找一種新的模型架構,既能有效處理長序列數據中的長期依賴關系,又能實現高度并行化,提高訓練效率。
核心創新點
- 拋棄傳統架構,引入注意力機制:Transformer 完全基于注意力機制,摒棄了 RNN 和 CNN。注意力機制就像一個聰明的 “篩選器”,能讓模型自動關注輸入序列中不同位置的重要信息,無論它們距離多遠,比如在處理 “The dog chased the cat” 這樣的句子時,能理解 “chased” 和 “dog”“cat” 的關系,而不受距離影響。
- 自注意力機制(self - attention):這是 Transformer 的核心組件。每個單詞會生成 Query、Key 和 Value 三個向量,通過計算 Query 與所有 Key 的相似度得分,對 Value 進行加權求和,得到該單詞的上下文相關表示。例如,在分析 “它是一只可愛的貓,它喜歡玩耍” 時,能確定兩個 “它” 都指代 “貓”。
- 多頭注意力機制(Multi - Head Attention):相當于多個不同的自注意力機制集成,每個頭關注數據的不同方面,能捕捉更豐富的語義信息。可以想象成多個不同的 “小眼睛”,從不同角度觀察數據,然后把結果綜合起來。
- 編碼器 - 解碼器架構(Encoder - Decoder):編碼器由多個堆疊的自注意力層和前饋神經網絡組成,負責將輸入序列編碼成一個固定長度的向量表示;解碼器除了自注意力層和前饋神經網絡外,還有一個編碼器 - 解碼器注意力層,用于在解碼時關注編碼器的輸出。就像一個 “翻譯官”,編碼器把源語言 “理解” 成一種中間表示,解碼器再根據這個中間表示生成目標語言。
- 位置編碼(Positional Encoding):由于 Transformer 本身沒有內置的順序信息,位置編碼被引入來給單詞添加位置信息,使模型能區分不同位置的單詞,比如 “我愛你” 和 “你愛我”,通過位置編碼能讓模型理解其中的差異。
二、手搓Transformer模型
1.導入相應包
torch:導入Pytorch包,PyTorch 是一個開源的深度學習框架,提供了張量操作和自動微分功能操作和自動微分功能,是代碼的核心依賴。
- 核心功能:
- 定義和操作多維張量(如矩陣、向量),支持 CPU 和 GPU 加速。
- 構建神經網絡模型,管理模型參數和計算圖。
- 提供優化器(如 SGD、Adam)和損失函數(如交叉熵)。
- 在代碼中的使用場景:
- 定義模型層(如
nn.Linear
、nn.Embedding
)。 - 實現張量運算(如矩陣乘法
torch.matmul
)。 - 執行前向傳播和反向傳播。
- 定義模型層(如
torch.nn:Pytorch神經網絡模塊,nn
?是 PyTorch 中構建神經網絡的高層接口,封裝了常用的層(Layer)和模塊(Module)。
- 核心功能:
- 定義神經網絡層,如全連接層(
nn.Linear
)、卷積層(nn.Conv2d
)、歸一化層(nn.LayerNorm
)等。 - 提供損失函數(如
nn.CrossEntropyLoss
)。 - 支持自定義神經網絡模塊(通過繼承
nn.Module
)。
- 定義神經網絡層,如全連接層(
- 在代碼中的使用場景:
- 定義 Transformer 的各個組件,如
MultiHeadAttention
、EncoderLayer
等(均繼承自nn.Module
)。 - 使用
nn.Embedding
實現詞嵌入層,nn.LayerNorm
實現層歸一化。
- 定義 Transformer 的各個組件,如
torch.nn.functional:PyTorch 神經網絡函數(torch.nn.functional
),包含了神經網絡中常用的函數式接口(非類接口),通常用于定義層的操作(如激活函數、池化等)。
- 核心功能:
- 激活函數:如 ReLU(
F.relu
)、Softmax(F.softmax
)。 - 張量操作:如 dropout(
F.dropout
)、歸一化(F.layer_norm
)。 - 損失函數:如均方誤差(
F.mse_loss
)。
- 激活函數:如 ReLU(
- 在代碼中的使用場景:
- 在
scaled_dot_product_attention
方法中使用F.softmax
計算注意力權重。 - 在位置前饋網絡(
PositionwiseFeedForward
)中使用F.relu
作為激活函數。
- 在
math:Python 標準庫math,
提供數學運算函數,用于實現一些數值計算邏輯。
- 核心功能:
- 基礎數學函數:如平方根(
math.sqrt
)、指數(math.exp
)、對數(math.log
)。 - 常數:如圓周率
math.pi
。
- 基礎數學函數:如平方根(
- 在代碼中的使用場景:
- 在位置編碼(
PositionalEncoding
)中計算頻率因子div_term
時,使用math.log
計算對數。 - 在縮放點積注意力中,使用
math.sqrt
計算縮放因子(1/sqrt(d_k)
)。
- 在位置編碼(
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
2.位置編碼層實現
Ⅰ、運行流程
① 類繼承與父類初始化 ——> ② 創建位置編碼矩陣 pe ——> ③ 生成位置索引 position
④ 生成頻率因子 div_term ——> ⑤ 填充正弦和余弦值 ——> ⑥ 添加批次維度
⑦ 注冊為緩沖區 ——> ⑦?輸入張量 x ——> ⑧?獲取位置編碼 ——> ⑨?輸出結果
輸入: d_model, max_len↓
┌───────────────────────────────────────────┐
│ 初始化位置編碼矩陣和位置索引 │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 計算頻率因子 (div_term) │
│ div_term = exp(-log(10000) * 2i/d_model) │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 生成位置編碼矩陣 │
│ pe[:, 0::2] = sin(position * div_term) │
│ pe[:, 1::2] = cos(position * div_term) │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 添加批次維度并注冊緩沖區 │
│ pe = pe.unsqueeze(0) │
│ self.register_buffer('pe', pe) │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 前向傳播方法 │
│ x = x + self.pe[:, :x.size(1)] │
└───────────────────────────────────────────┘↓
輸出: x + 位置編碼
Ⅱ、初始化
d_model:模型維度(即詞嵌入維度),決定位置編碼的列數(每個位置的特征維度)。
max_len:模型支持的最大序列長度,決定位置編碼的行數(最多編碼max_len
個位置)。
pe:創建一個形狀為?(max_len, d_model)
?的全零張量,用于存儲位置編碼。初始化位置編碼矩陣,后續通過正弦 / 余弦函數填充具體值。
torch.zeros():創建指定形狀的全零張量。
參數名 | 類型 | 描述 |
---|---|---|
size | int 或 tuple | 張量形狀,如?(3, 5) ?表示 3 行 5 列的矩陣。 |
out=None | Tensor | 輸出張量(可選),結果將寫入此張量。 |
dtype=None | torch.dtype | 數據類型(如?torch.float32 ),默認與當前張量庫一致。 |
layout=torch.strided | torch.layout | 內存布局(如?torch.sparse_coo ?表示稀疏張量)。 |
device=None | torch.device | 設備(如?'cuda' ?或?'cpu' )。 |
requires_grad=False | bool | 是否需要梯度(用于自動微分)。 |
position:為每個位置生成唯一的索引值,用于計算位置編碼的頻率和相位。
torch.arange(0, max_len)
:生成從 0 到max_len-1
的一維張量,表示序列中的位置索引(如第 0 個位置,第 1 個位置,…)。.unsqueeze(1)
:將一維張量轉換為形狀?(max_len, 1)
?的二維張量(添加列維度)。
div_term:通過控制不同頻率的正弦曲線,為不同位置生成唯一且有區分度的編碼向量,從而讓模型能夠學習到序列中的相對位置和絕對位置信息。
torch.arange(0, d_model, 2)
:生成偶數索引序列?[0, 2, 4, ..., d_model-2]
(若d_model
為偶數),對應位置編碼的偶數維度。math.log(10000.0)
:以 10 為底的對數,用于計算頻率的縮放因子。- 最終結果是形狀為?
(d_model//2,)
?的張量,每個元素代表對應維度的頻率參數。
torch.arange():生成指定范圍的連續整數張量(類似 Python 的?range
,但返回張量)。
參數名 | 類型 | 描述 |
---|---|---|
start | int 或 float | 起始值(包含),默認 0。 |
end | int 或 float | 結束值(不包含)。 |
step=1 | int 或 float | 步長(間隔),默認 1。 |
out=None | Tensor | 輸出張量(可選)。 |
dtype=None | torch.dtype | 數據類型,默認與當前張量庫一致。 |
layout=torch.strided | torch.layout | 內存布局。 |
device=None | torch.device | 設備。 |
requires_grad=False | bool | 是否需要梯度。 |
torch.exp():對輸入張量的每個元素計算自然指數?e^x。
參數名 | 類型 | 描述 |
---|---|---|
input | Tensor | 輸入張量。 |
out=None | Tensor | 輸出張量(可選)。 |
math.log():計算自然對數?\(\ln(x)\)?或指定底數的對數(Python 標準庫函數,非 PyTorch)。
參數名 | 類型 | 描述 |
---|---|---|
x | float | 輸入值(必須大于 0)。 |
base=math.e | float | 對數底數(可選),默認?e(自然對數)。若指定?base=10 ,則計算常用對數 log_{10}(x)。 |
torch.sin():對輸入張量中的每個元素計算正弦值(按元素操作),結果為弧度制的正弦值。
參數名 | 類型 | 說明 |
---|---|---|
input | Tensor | 輸入張量,數據類型需為浮點型(如?float32 、float64 )。 |
out | Tensor, 可選 | 輸出張量,用于存儲計算結果,形狀需與?input ?一致。 |
torch.cos():對輸入張量中的每個元素計算余弦值(按元素操作),結果為弧度制的余弦值。
參數名 | 類型 | 說明 |
---|---|---|
input | Tensor | 輸入張量,數據類型需為浮點型(如?float32 、float64 )。 |
out | Tensor, 可選 | 輸出張量,用于存儲計算結果,形狀需與?input ?一致。 |
unsqueeze():在指定維度上為張量添加一個大小為 1 的維度(即增加維度數),常用于調整張量形狀以適配模型輸入或廣播機制。
參數名 | 類型 | 說明 |
---|---|---|
dim | int | 要插入維度的位置(索引從 0 開始)。 若為負數,表示從最后一維向前數(如? -1 ?表示最后一維)。 |
self.register_buffer():?在 PyTorch 模型中注冊一個緩沖區(Buffer),用于存儲非訓練參數(如位置編碼、均值 / 方差統計量等)。特點:
- 不參與梯度更新;
- 隨模型一起保存和加載;
- 當模型調用?
.to(device)
?時,緩沖區會自動移動到對應設備(如 GPU)。
# 使用正弦和余弦函數生成不同頻率的編碼,使模型能夠學習序列中的相對位置關系。
class PositionalEncoding(nn.Module):def __init__(self, d_model, max_len=5000):super().__init__()# 創建位置編碼矩陣 (max_len, d_model)pe = torch.zeros(max_len, d_model)# 生成位置索引 (max_len, 1)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)# 生成頻率因子 (d_model//2,)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))# 偶數位置使用正弦函數:# 【:】:選取所有行 【0::2】:每行中取偶數索引的列(從0索引處出發,每隔兩個列)pe[:, 0::2] = torch.sin(position * div_term)# 奇數位置使用余弦函數:# 【:】:選取所有行 【1::2】:每行中取奇數索引的列(從1索引處出發,每隔兩個列)pe[:, 1::2] = torch.cos(position * div_term)# 使位置編碼能夠適配批次輸入并作為模型的靜態參數使用。# 添加批次維度 (1, max_len, d_model):適配 PyTorch 模型處理批量數據的要求。pe = pe.unsqueeze(0)# 注冊為緩沖區(非訓練參數):位置編碼是預計算的固定值,不需要在訓練過程中更新,因此不應該作為模型參數(如 nn.Parameter)。# 設備一致性:將 pe 注冊為緩沖區后,當模型調用 .to(device) 時,pe 會自動跟隨模型移動到指定設備(如 GPU),確保與輸入數據的設備一致性。# 模型保存與加載:緩沖區會隨模型一起保存和加載,確保推理時位置編碼可用。self.register_buffer('pe', pe)
Ⅲ、前向計算
x:輸入序列的嵌入表示:通常是詞向量(Word Embedding)或其他特征的張量。[batch_size, seq_len, d_model]
self.pe[:, : x.size(1)]:預計算的位置編碼矩陣:通過正弦 / 余弦函數生成,用于為模型提供序列的位置信息。[1, max_len, d_model]
def forward(self, x):# 截取與輸入序列等長的位置編碼x = x + self.pe[:, :x.size(1)]return x
Ⅳ、完整代碼
# 使用正弦和余弦函數生成不同頻率的編碼,使模型能夠學習序列中的相對位置關系。
class PositionalEncoding(nn.Module):def __init__(self, d_model, max_len=5000):super().__init__()# 創建位置編碼矩陣 (max_len, d_model)pe = torch.zeros(max_len, d_model)# 生成位置索引 (max_len, 1)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)# 生成頻率因子 (d_model//2,)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))# 偶數位置使用正弦函數:# 【:】:選取所有行 【0::2】:每行中取偶數索引的列(從0索引處出發,每隔兩個列)pe[:, 0::2] = torch.sin(position * div_term)# 奇數位置使用余弦函數:# 【:】:選取所有行 【1::2】:每行中取奇數索引的列(從1索引處出發,每隔兩個列)pe[:, 1::2] = torch.cos(position * div_term)# 使位置編碼能夠適配批次輸入并作為模型的靜態參數使用。# 添加批次維度 (1, max_len, d_model):適配 PyTorch 模型處理批量數據的要求。pe = pe.unsqueeze(0)# 注冊為緩沖區(非訓練參數):位置編碼是預計算的固定值,不需要在訓練過程中更新,因此不應該作為模型參數(如 nn.Parameter)。# 設備一致性:將 pe 注冊為緩沖區后,當模型調用 .to(device) 時,pe 會自動跟隨模型移動到指定設備(如 GPU),確保與輸入數據的設備一致性。# 模型保存與加載:緩沖區會隨模型一起保存和加載,確保推理時位置編碼可用。self.register_buffer('pe', pe)def forward(self, x):# 截取與輸入序列等長的位置編碼x = x + self.pe[:, :x.size(1)]return x
3.多頭注意力機制實現
Ⅰ、運行流程
① 初始化 ——> ② 線性投影變換 + 多頭分割 ——> ③ 計算縮放點積注意力 ——>
④ 合并多頭 + 最終投影
輸入: Q, K, V, mask(可選)↓
┌───────────────────────────────────────────┐
│ 初始化參數 │
│ d_k = d_model // num_heads │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 線性變換 │
│ Q = W_q(Q), K = W_k(K), V = W_v(V) │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 多頭分割 │
│ split_heads: [batch, seq_len, d_model] │
│ → [batch, num_heads, seq_len, d_k] │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 縮放點積注意力計算 │
│ 1. scores = Q·K^T / √d_k │
│ 2. 應用掩碼 (masked_fill) │
│ 3. attention = softmax(scores) │
│ 4. output = attention·V │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 合并多頭 │
│ combine_heads: [batch, num_heads, seq_len, d_k] │
│ → [batch, seq_len, d_model] │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 最終線性變換 │
│ output = W_o(output) │
└───────────────────────────────────────────┘↓
輸出: 注意力輸出
Ⅱ、初始化
d_model:模型的總維度(embedding 維度),表示輸入和輸出的特征向量長度。
num_heads:注意力頭的數量,將整個注意力機制分成多個 “頭” 并行計算。
super().__init__():調用父類的初始化方法,避免重復編寫代碼。
assert:在代碼中檢查某個條件是否為真。如果條件為假,assert
?會拋出?AssertionError
?異常,幫助開發者快速定位問題。
- condition:必須為真的表達式。
- error_message(可選):斷言失敗時顯示的自定義提示信息。
- 作用:
- 參數校驗:確保
d_model
可被num_heads
整除,保證每個頭的維度d_k
為整數(如d_model=512
,?num_heads=8
時,d_k=64
)。 - 線性層定義:創建 4 個線性層用于投影輸入到 Q/K/V 空間及輸出空間,權重可學習。
- 參數校驗:確保
self.W_q:將輸入的查詢(Query)投影到?d_model
?維度。
self.W_k:將輸入的鍵(Key)分別投影到?d_model
?維度。
self.W_v:將輸入的值(Value)分別投影到?d_model
?維度。
self.W_o:將多個注意力頭的輸出合并后,再投影回?d_model
?維度。
nn.Linear():實現全連接層(線性變換),公式為?\(y = xA^T + b\),用于將輸入特征映射到輸出特征。
參數 | 描述 |
---|---|
in_features | 輸入特征維度(必需)。 |
out_features | 輸出特征維度(必需)。 |
bias | 是否添加偏置項(默認?True )。 |
class MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads):super().__init__()assert d_model % num_heads == 0, "d_model必須能被num_heads整除"self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_heads# 定義線性變換層:Q, K, V投影矩陣和輸出投影矩陣self.W_q = nn.Linear(d_model, d_model) # Q投影self.W_k = nn.Linear(d_model, d_model) # K投影self.W_v = nn.Linear(d_model, d_model) # V投影self.W_o = nn.Linear(d_model, d_model) # 輸出投影
Ⅲ、分割頭
- 作用:
- 維度重塑:將輸入張量(形狀為
[batch, seq_len, d_model]
)拆分為多個頭,重塑為[batch, seq_len, num_heads, d_k]
。 - 維度轉置:通過
transpose(1, 2)
將頭維度提前,變為[batch, num_heads, seq_len, d_k]
,便于并行計算每個頭的注意力。
- 維度重塑:將輸入張量(形狀為
- 例子:?
- batch_size = 2(兩個樣本)
- seq_len = 4(每個樣本有 4 個 token)
- d_model = 8(每個 token 用 8 維向量表示)
- num_heads = 4(4 個頭)
- d_k = 2(每個頭的維度是 2)
- 那么輸入 x 的形狀是 [2, 4, 8],經過
view
后變成 [2, 4, 4, 2],再經過transpose
后形狀變為 [2, 4, 4, 2]。最終輸出的形狀是 [batch_size, num_heads, seq_len, d_k],這正是多頭注意力機制后續計算所需的格式。
x:輸入張量,通常為編碼器或解碼器的特征向量。
batch_size:一次訓練或推理中處理的樣本數量。
seq_len:序列長度,即輸入序列中 Token 的數量(如句子中的單詞數)。
d_model:模型維度,是輸入和輸出的特征維度。
size():返回張量的形狀
參數 | 描述 |
---|---|
dim | 可選參數,指定要返回的維度大小(若指定,則返回標量;否則返回元組)。 |
view():重塑張量的形狀,返回的新張量與原張量共享內存。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
*shape | 新形狀(必需,元組或多個整數參數)。 |
transpose():交換張量的兩個維度。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
dim0 | 第一個要交換的維度索引(必需)。 |
dim1 | 第二個要交換的維度索引(必需)。 |
def split_heads(self, x):# 將輸入分割成多個頭batch_size, seq_len, d_model = x.size()return x.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
Ⅳ、縮放點積注意力
- 作用:
- 相似度計算:通過矩陣乘法
Q·K^T
計算查詢與鍵的相似度,除以√d_k
防止梯度消失(縮放點積注意力的核心)。 - 掩碼處理:若存在掩碼(如填充掩碼或前瞻掩碼),將無效位置的分數設為
-1e9
,經 softmax 后權重趨近于 0。 - 加權聚合:用注意力權重對值向量
V
加權求和,得到頭的輸出。
- 相似度計算:通過矩陣乘法
Q:查詢矩陣,形狀通常為?[batch_size, num_heads, seq_len_q, d_k]
。
K:鍵矩陣,形狀通常為?[batch_size, num_heads, seq_len_k, d_k]
。
V:值矩陣,形狀通常為?[batch_size, num_heads, seq_len_k, d_k]
。
mask:可選的布爾掩碼,用于屏蔽某些位置的注意力計算(如填充位置或未來 token)。
scores:計算 Q 與 K 的轉置的矩陣乘法,除以縮放因子,防止梯度消失(當d_k
較大時,點積結果可能方差較大),得到注意力分數矩陣。
torch.matmul():張量矩陣乘法,支持多種維度組合(如向量點積、矩陣乘、批量矩陣乘)
參數 | 描述 |
---|---|
input | 第一個輸入張量(必需)。 |
other | 第二個輸入張量(必需)。 |
out | 輸出張量(可選)。 |
transpose():交換張量的兩個維度。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
dim0 | 第一個要交換的維度索引(必需)。 |
dim1 | 第二個要交換的維度索引(必需)。 |
math.sqrt():計算數值的平方根
參數 | 描述 |
---|---|
x | 輸入數值(必需,需為正數)。 |
masked_fill():根據掩碼(mask)對張量元素進行替換。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
mask | 布爾掩碼張量(必需,與input 形狀相同)。 |
value | 替換值(必需)。 |
attention:將注意力分數歸一化為概率分布(值在 [0,1] 且和為 1)。
softmax():將輸入張量在指定維度上歸一化為概率分布(值在 [0,1] 且和為 1)。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
dim | 要計算 softmax 的維度(必需)。 |
dtype | 輸出數據類型(可選)。 |
output:根據注意力權重對 V 進行加權求和。
torch.matmul():張量矩陣乘法,支持多種維度組合(如向量點積、矩陣乘、批量矩陣乘)。?
def scaled_dot_product_attention(self, Q, K, V, mask=None):# 計算注意力分數,縮放以穩定梯度scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)# 應用掩碼:將掩碼位置的分數設為負無窮,softmax后趨近于0if mask is not None:scores = scores.masked_fill(mask == 0, -1e9)# 應用softmax獲取注意力權重attention = F.softmax(scores, dim=-1)# 加權求和得到輸出output = torch.matmul(attention, V)return output
Ⅴ、合并頭
x:輸入張量,通常為編碼器或解碼器的特征向量。
batch_size:一次訓練或推理中處理的樣本數量。
num_heads:注意力頭的數量,用于將模型維度拆分為多個子空間進行并行計算。
seq_len:序列長度,即輸入序列中 Token 的數量(如句子中的單詞數)。
d_k:每個注意力頭的維度,通過d_model
除以num_heads
得到。
self.d_model:模型維度,是輸入和輸出的特征維度。
size():返回張量的形狀
參數 | 描述 |
---|---|
dim | 可選參數,指定要返回的維度大小(若指定,則返回標量;否則返回元組)。 |
transpose():交換張量的兩個維度。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
dim0 | 第一個要交換的維度索引(必需)。 |
dim1 | 第二個要交換的維度索引(必需)。 |
contiguous():重新排列張量的內存布局,使其在內存中連續存儲(某些操作要求輸入必須連續)。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
view():重塑張量的形狀,返回的新張量與原張量共享內存。
參數 | 描述 |
---|---|
input | 輸入張量(必需)。 |
*shape | 新形狀(必需,元組或多個整數參數)。 |
def combine_heads(self, x):# 將多個頭的輸出合并batch_size, num_heads, seq_len, d_k = x.size()return x.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)
Ⅵ、前向傳播
Q:查詢輸入,形狀通常為?[batch_size, nums_head, seq_len_q, d_model]
。
K:鍵輸入,形狀通常為?[batch_size, nums_head, seq_len_k, d_model]
。
V:值輸入,形狀通常為?[batch_size, nums_head, seq_len_k, d_model]
。
mask:可選的布爾掩碼,用于屏蔽某些位置的注意力計算(如填充或未來 token)。
self.split_heads():將張量分割為多個頭,實現多頭并行計算。
attn_output:縮放點積注意力(Scaled Dot-Product Attention)的輸出,形狀為?(batch_size, num_heads, seq_len, d_k)
。
self.scaled_dot_product_attention():實現縮放點積注意力的核心計算邏輯。
- 計算注意力分數:通過?
Q·K^T
?計算 Token 間的相關性,除以?√d_k
?防止梯度消失。 - 應用掩碼:通過?
mask
?屏蔽無效位置(如填充或未來 Token)。 - Softmax 歸一化:將分數轉化為概率分布(注意力權重)。
- 加權求和:用注意力權重對 Value 向量加權,得到注意力輸出。
output:多頭注意力的最終輸出,形狀為?(batch_size, seq_len, d_model)
。
self.W_o():多頭注意力的輸出投影層,是一個線性變換矩陣。
self.combine_heads():將多頭分離的特征((batch_size, num_heads, seq_len, d_k)
)合并為單張量。
def forward(self, Q, K, V, mask=None):# 線性變換 + 多頭分割Q = self.split_heads(self.W_q(Q))K = self.split_heads(self.W_k(K))V = self.split_heads(self.W_v(V))# 計算注意力attn_output = self.scaled_dot_product_attention(Q, K, V, mask)# 合并頭并應用最終線性變換output = self.W_o(self.combine_heads(attn_output))return output
Ⅶ、完整代碼
class MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads):super().__init__()assert d_model % num_heads == 0, "d_model必須能被num_heads整除"self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_heads# 定義線性變換層:Q, K, V投影矩陣和輸出投影矩陣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 scaled_dot_product_attention(self, Q, K, V, mask=None):# 計算注意力分數,縮放以穩定梯度scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)# 應用掩碼:將掩碼位置的分數設為負無窮,softmax后趨近于0if mask is not None:scores = scores.masked_fill(mask == 0, -1e9)# 應用softmax獲取注意力權重attention = F.softmax(scores, dim=-1)# 加權求和得到輸出output = torch.matmul(attention, V)return outputdef split_heads(self, x):# 將輸入分割成多個頭batch_size, seq_len, d_model = x.size()return x.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)def combine_heads(self, x):# 將多個頭的輸出合并batch_size, num_heads, seq_len, d_k = x.size()return x.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)def forward(self, Q, K, V, mask=None):# 線性變換 + 多頭分割Q = self.split_heads(self.W_q(Q))K = self.split_heads(self.W_k(K))V = self.split_heads(self.W_v(V))# 計算注意力attn_output = self.scaled_dot_product_attention(Q, K, V, mask)# 合并頭并應用最終線性變換output = self.W_o(self.combine_heads(attn_output))return output
4.編碼層
Ⅰ、運行流程
?① 初始化階段 ——> ② 自注意力計算 ——> ③ 殘差連接 ——> ④?Dropout與層歸一化
——> ⑤ 前饋網絡計算 ——> ⑥ 殘差鏈接 ——> ⑦ Dropout與層歸一化
輸入 x [batch, seq_len, d_model]↓
┌───────────────────────────────────┐
│ 步驟1:自注意力 + 殘差 + 層歸一化 │
└───────────────────────────────────┘↓
1. 自注意力計算:x → MultiHeadAttention(x, x, x, mask) → attn_output↓
2. 殘差連接:x + attn_output → residual_1↓
3. Dropout與層歸一化:LayerNorm(x + Dropout(residual_1)) → x_1↓
┌───────────────────────────────────┐
│ 步驟2:前饋網絡 + 殘差 + 層歸一化 │
└───────────────────────────────────┘↓
1. 前饋網絡計算:x_1 → PositionwiseFeedForward(x_1) → ff_output↓
2. 殘差連接:x_1 + ff_output → residual_2↓
3. Dropout與層歸一化:LayerNorm(x_1 + Dropout(residual_2)) → 輸出 x
Ⅱ、初始化
d_model:模型維度。
num_heads:注意力頭數量。
d_ff:前饋網絡隱藏層維度。
dropout:Dropout 概率,用于防止過擬合。
d_model:模型的總維度(embedding 維度),表示輸入和輸出的特征向量長度。
num_heads:注意力頭的數量,將整個注意力機制分成多個 “頭” 并行計算。
super().__init__():調用父類的初始化方法,避免重復編寫代碼。
MultiHeadAttention():將輸入特征分解到多個子空間(頭),并行計算注意力,最后合并結果。
PositionwiseFeedForward():對每個 Token 的特征進行逐位置的前饋變換。
nn.LayerNorm():對輸入的特征維度進行歸一化,使數據在訓練過程中保持穩定分布,加速收斂并提高模型泛化能力。
其中,μ?和 σ?是當前樣本的均值和方差,γ?和 β?是可學習的縮放和平移參數。
參數 | 描述 |
---|---|
normalized_shape | 輸入特征的形狀(如整數或元組),指定需要歸一化的維度。例如:512 ?或?(512,) 。 |
eps | 數值穩定性的小常數(默認?1e-5 ),防止分母為零。 |
elementwise_affine | 是否學習可訓練的仿射參數?\(\gamma\)?和?\(\beta\)(默認?True )。 |
nn.Dropout():在訓練過程中隨機將輸入的部分元素置為 0,減少神經元對特定特征的依賴,從而防止過擬合。
參數 | 描述 |
---|---|
p | 元素被置零的概率(默認?0.5 ),取值范圍 [0, 1]。 |
inplace | 是否直接在原張量上操作(默認?False )。 |
# 編碼層
class EncoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 自注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.dropout = nn.Dropout(dropout) # Dropout層
Ⅲ、前向傳播
x:編碼層的輸入和經過每層操作后的中間輸出,形狀為(batch_size, src_len, d_model)
?。
mask:源序列的填充掩碼,形狀為(batch_size, 1, 1, src_len)
?,用于屏蔽填充位置。
attn_output:自注意力模塊的輸出。
self.self_attn():實現自注意力機制(Self-Attention),計算輸入序列中各 Token 之間的依賴關系。
self.norm1():對自注意力的輸出進行層歸一化(Layer Normalization)。
self.dropout():在自注意力輸出后應用隨機失活(Dropout),按概率丟棄部分神經元激活值。
ff_output:前饋網絡模塊的輸出。
self.feed_forward():實現位置前饋網絡(Positionwise Feed-Forward Network),對每個 Token 的特征進行非線性變換。
self.norm2():對前饋網絡的輸出進行層歸一化。
self.dropout():在自注意力輸出后應用隨機失活(Dropout),按概率丟棄部分神經元激活值。
def forward(self, x, mask):# 自注意力 + 殘差連接 + 層歸一化attn_output = self.self_attn(x, x, x, mask)x = self.norm1(x + self.dropout(attn_output))# 前饋網絡 + 殘差連接 + 層歸一化ff_output = self.feed_forward(x)x = self.norm2(x + self.dropout(ff_output))return x
Ⅳ、完整代碼
# 編碼層
class EncoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 自注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, x, mask):# 自注意力 + 殘差連接 + 層歸一化attn_output = self.self_attn(x, x, x, mask)x = self.norm1(x + self.dropout(attn_output))# 前饋網絡 + 殘差連接 + 層歸一化ff_output = self.feed_forward(x)x = self.norm2(x + self.dropout(ff_output))return x
5.解碼層
Ⅰ、運行流程
① 初始化 ——> ② 掩蔽自注意力計算 ——> ③ 殘差鏈接與層歸一化 ——> ④ 跨注意力計算 ——> ⑤ 殘差鏈接與層歸一化 ——> ⑥ 前饋網絡計算 ——> ⑦ 殘差鏈接與層歸一化
輸入:
x [batch, tgt_len, d_model] ← 解碼器輸入(目標序列)
encoder_output [batch, src_len, d_model] ← 編碼器輸出(源序列)
src_mask [batch, 1, src_len] ← 源序列填充掩碼
tgt_mask [batch, tgt_len, tgt_len] ← 目標序列因果掩碼(三角矩陣)↓
┌───────────────────────────────────────────┐
│ 步驟1:掩蔽自注意力 + 殘差 + 層歸一化 │
└───────────────────────────────────────────┘↓
1. 掩蔽自注意力:x → MultiHeadAttention(x, x, x, tgt_mask) → attn_output1↓
2. 殘差與歸一化:x + Dropout(attn_output1) → LayerNorm → x_1↓
┌───────────────────────────────────────────┐
│ 步驟2:編碼器-解碼器注意力 + 殘差 + 層歸一化 │
└───────────────────────────────────────────┘↓
1. 跨注意力:x_1 → MultiHeadAttention(x_1, encoder_output, encoder_output, src_mask) → attn_output2↓
2. 殘差與歸一化:x_1 + Dropout(attn_output2) → LayerNorm → x_2↓
┌───────────────────────────────────────────┐
│ 步驟3:前饋網絡 + 殘差 + 層歸一化 │
└───────────────────────────────────────────┘↓
1. 前饋網絡:x_2 → PositionwiseFeedForward(x_2) → ff_output↓
2. 殘差與歸一化:x_2 + Dropout(ff_output) → LayerNorm → 輸出 x
Ⅱ、初始化
d_model:模型維度
num_heads:注意力頭數量。
d_ff:前饋網絡隱藏層維度。
dropout:Dropout 概率,用于防止過擬合。
super().__init__():調用父類的初始化方法,避免重復編寫代碼。
MultiHeadAttention():將輸入特征投影到多個子空間,并行計算多個注意力頭,然后合并結果。
PositionwiseFeedForward():捕獲不同子空間的語義關系,增強模型對復雜模式的表達能力。
nn.LayerNorm():對輸入的特征維度進行歸一化,使數據在訓練過程中保持穩定分布,加速收斂并提高模型泛化能力。
其中,μ?和 σ?是當前樣本的均值和方差,γ?和 β?是可學習的縮放和平移參數。
參數 | 描述 |
---|---|
normalized_shape | 輸入特征的形狀(如整數或元組),指定需要歸一化的維度。例如:512 ?或?(512,) 。 |
eps | 數值穩定性的小常數(默認?1e-5 ),防止分母為零。 |
elementwise_affine | 是否學習可訓練的仿射參數?\(\gamma\)?和?\(\beta\)(默認?True )。 |
nn.Dropout():在訓練過程中隨機將輸入的部分元素置為 0,減少神經元對特定特征的依賴,從而防止過擬合。
參數 | 描述 |
---|---|
p | 元素被置零的概率(默認?0.5 ),取值范圍 [0, 1]。 |
inplace | 是否直接在原張量上操作(默認?False )。 |
class DecoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 掩蔽自注意力self.cross_attn = MultiHeadAttention(d_model, num_heads) # 編碼器-解碼器注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.norm3 = nn.LayerNorm(d_model) # 第三層歸一化self.dropout = nn.Dropout(dropout) # Dropout層
Ⅲ、前向傳播
x:解碼層的輸入和經過每層操作后的中間輸出,形狀為(batch_size, tgt_len, d_model)
?。
encoder_output:編碼器的輸出,形狀為(batch_size, src_len, d_model)
?,作為解碼器中編碼器 - 解碼器注意力的輸入。
src_mask:源序列的填充掩碼,形狀為(batch_size, 1, 1, src_len)
?。
tgl_mask:目標序列的掩碼,形狀為(batch_size, 1, tgt_len, tgt_len)
?,組合了填充掩碼和三角掩碼。
attn_output1:編碼器的輸出,形狀為(batch_size, src_len, d_model)
?,作為解碼器中編碼器 - 解碼器注意力的輸入。
self.self_attn():實現掩碼自注意力(Masked Self-Attention),處理目標序列內部的依賴關系。
self.norm1():對自注意力的輸出進行層歸一化(Layer Normalization)。
self.dropout():在自注意力后隨機丟棄部分神經元輸出,防止過擬合。
attn_output2:編碼器 - 解碼器注意力(Cross-Attention)的輸出,融合目標序列與編碼器輸出的信息。
self.cross_attn():實現編碼器 - 解碼器注意力,查詢(Query)來自解碼器,鍵(Key)和值(Value)來自編碼器。
self.norm2():對編碼器 - 解碼器注意力的輸出進行層歸一化。
self.dropout():在交叉注意力后應用 Dropout,防止過擬合。
ff_output:位置前饋網絡(Positionwise Feed-Forward Network)的輸出,對特征進行非線性變換。
self.feed_forward():實現兩層線性變換(通常是ReLU
激活),對每個位置的特征獨立處理。
self.norm3():對前饋網絡的輸出進行層歸一化。
self.dropout():在前饋網絡后應用 Dropout。
def forward(self, x, encoder_output, src_mask, tgt_mask):# 掩蔽自注意力:只關注已生成的token,通過三角掩碼防止解碼器看到未來位置attn_output1 = self.self_attn(x, x, x, tgt_mask)x = self.norm1(x + self.dropout(attn_output1))# 編碼器-解碼器注意力:關注編碼器輸出,連接編碼和解碼過程。attn_output2 = self.cross_attn(x, encoder_output, encoder_output, src_mask)x = self.norm2(x + self.dropout(attn_output2))# 前饋網絡ff_output = self.feed_forward(x)x = self.norm3(x + self.dropout(ff_output))return x
Ⅳ、完整代碼
# 解碼層
class DecoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 掩蔽自注意力self.cross_attn = MultiHeadAttention(d_model, num_heads) # 編碼器-解碼器注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.norm3 = nn.LayerNorm(d_model) # 第三層歸一化self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, x, encoder_output, src_mask, tgt_mask):# 掩蔽自注意力:只關注已生成的token,通過三角掩碼防止解碼器看到未來位置attn_output1 = self.self_attn(x, x, x, tgt_mask)x = self.norm1(x + self.dropout(attn_output1))# 編碼器-解碼器注意力:關注編碼器輸出,連接編碼和解碼過程。attn_output2 = self.cross_attn(x, encoder_output, encoder_output, src_mask)x = self.norm2(x + self.dropout(attn_output2))# 前饋網絡ff_output = self.feed_forward(x)x = self.norm3(x + self.dropout(ff_output))return x
6.編碼器
Ⅰ、運行流程
① 初始化 ——> ② 詞嵌入 + 位置編碼 ——> ③ 逐層通過編碼器層 ——> ④ 輸出結果
輸入:
src [batch, src_len] ← 輸入序列token ID
src_mask [batch, 1, src_len] ← 填充掩碼(屏蔽填充token)↓
┌─────────────────────────────────────┐
│ 步驟1:詞嵌入 + 位置編碼 + Dropout │
└─────────────────────────────────────┘↓
1. 詞嵌入與縮放:src → Embedding → 向量 × √d_model → [batch, src_len, d_model]↓
2. 位置編碼:添加位置信息 → [batch, src_len, d_model]↓
3. Dropout:隨機丟棄部分元素 → src↓
┌─────────────────────────────────────┐
│ 步驟2:逐層通過編碼器層 │
└─────────────────────────────────────┘↓
for 每層編碼器層:┌─────────────────────────────────┐│ 1. 自注意力 + 殘差 + 層歸一化 │└─────────────────────────────────┘↓┌─────────────────────────────────┐│ 2. 前饋網絡 + 殘差 + 層歸一化 │└─────────────────────────────────┘↓
輸出:
src [batch, src_len, d_model] ← 編碼后的序列表示
Ⅱ、初始化
num_layers:編碼器層的數量。
d_model:模型維度。
num_heads:注意力頭數量。
d_ff:前饋網絡隱藏層維度。
input_vocab_size:輸入序列的詞匯表大小,用于初始化詞嵌入層。
super().__init__():調用父類的初始化方法,避免重復編寫代碼。
nn.Embedding():將離散的 token ID 映射為連續的向量表示。
參數 | 類型 | 描述 |
---|---|---|
num_embeddings | int | 詞匯表大小。 |
embedding_dim | int | 嵌入向量維度。 |
padding_idx | int (可選) | 填充標記的索引。 |
PositionalEncoding():實現位置編碼的具體類,通過特定的算法(如基于三角函數的方法)生成位置編碼向量,并添加到詞嵌入向量上。
EncoderLayer():Transformer 編碼器的核心組成單元,負責對輸入序列進行單層的特征變換。通過堆疊多個?EncoderLayer
,編碼器能夠逐層提取更復雜的語義表示,捕捉序列中的長距離依賴關系。
nn.ModuleList():存儲子模塊的列表,用于動態構建神經網絡。
參數 | 類型 | 描述 |
---|---|---|
modules | iterable (可選) | 初始化模塊列表。 |
nn.Dropout():在訓練過程中隨機將輸入的部分元素置為 0,減少神經元對特定特征的依賴,從而防止過擬合。
參數 | 描述 |
---|---|
p | 元素被置零的概率(默認?0.5 ),取值范圍 [0, 1]。 |
inplace | 是否直接在原張量上操作(默認?False )。 |
# 編碼器:處理輸入序列,提取特征表示。
class Encoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, input_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(input_vocab_size, d_model) # 詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個編碼器層self.dropout = nn.Dropout(dropout) # Dropout層
Ⅲ、前向傳播
src:編碼器的輸入序列,形狀為(batch_size, src_len)
?,元素為 token ID。
src_mask:源序列的填充掩碼,形狀為(batch_size, 1, 1, src_len)
?。
self.embedding():詞嵌入層,將輸入的 token ID 映射為d_model
維度的向量。
math.sqrt():計算一個數的平方根(Python 內置函數)。
參數 | 類型 | 描述 |
---|---|---|
x | float | 要計算平方根的數值。 |
self.pos_encoding():對經過詞嵌入后的目標序列添加位置編碼信息。在 Transformer 架構中,原始的詞嵌入本身不包含位置信息,而位置編碼可以讓模型感知到單詞在序列中的位置,這對于捕捉序列順序關系至關重要 。
self.dropout():按照設定的概率對輸入進行隨機失活(將神經元輸出置為 0)操作。在訓練過程中,它通過隨機丟棄部分神經元的輸出,來防止模型過擬合 。
self.layers:由多個EncoderLayer
組成的模塊列表,堆疊形成編碼器。
layer:每個單獨的EncoderLayer層
layer():Transformer 編碼器的核心處理單元,通過堆疊多個?EncoderLayer
,模型能夠逐層提取更抽象的語義表示,為解碼器提供豐富的上下文信息。
def forward(self, src, src_mask):# 詞嵌入并縮放(乘以√d_model)src = self.embedding(src) * math.sqrt(self.d_model)# 添加位置編碼src = self.pos_encoding(src)src = self.dropout(src)# 依次通過各編碼器層for layer in self.layers:src = layer(src, src_mask)return src
Ⅳ、完整代碼
# 編碼器:處理輸入序列,提取特征表示。
class Encoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, input_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(input_vocab_size, d_model) # 詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個編碼器層self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, src, src_mask):# 詞嵌入并縮放(乘以√d_model)src = self.embedding(src) * math.sqrt(self.d_model)# 添加位置編碼src = self.pos_encoding(src)src = self.dropout(src)# 依次通過各編碼器層for layer in self.layers:src = layer(src, src_mask)return src
7.解碼器
Ⅰ、運行流程
① 初始化 ——> ② 詞嵌入與位置編碼 ——> ③ 逐層通過解碼器層 ——> ④ 輸出結果
輸入:
tgt [batch, tgt_len] ← 目標序列token ID
encoder_output [batch, src_len, d_model] ← 編碼器輸出
src_mask [batch, 1, src_len] ← 源序列填充掩碼
tgt_mask [batch, tgt_len, tgt_len] ← 目標序列因果掩碼↓
┌─────────────────────────────────────┐
│ 步驟1:詞嵌入 + 位置編碼 + Dropout │
└─────────────────────────────────────┘↓
1. 詞嵌入與縮放:tgt → Embedding → 向量 × √d_model → [batch, tgt_len, d_model]↓
2. 位置編碼:添加位置信息 → [batch, tgt_len, d_model]↓
3. Dropout:隨機丟棄部分元素 → tgt↓
┌─────────────────────────────────────┐
│ 步驟2:逐層通過解碼器層 │
└─────────────────────────────────────┘↓
for 每層解碼器層:┌─────────────────────────────────┐│ 1. 掩蔽自注意力 + 殘差 + 層歸一化 │└─────────────────────────────────┘↓┌─────────────────────────────────┐│ 2. 編碼器-解碼器注意力 + 殘差 + 層歸一化 │└─────────────────────────────────┘↓┌─────────────────────────────────┐│ 3. 前饋網絡 + 殘差 + 層歸一化 │└─────────────────────────────────┘↓
輸出:
tgt [batch, tgt_len, d_model] ← 解碼后的目標序列表示
Ⅱ、初始化
num_layers:解碼器層的數量。
d_model:模型維度。
num_heads:注意力頭數量。
d_ff:前饋網絡隱藏層維度。
target_vocab_size:目標序列的詞匯表大小,用于初始化目標詞嵌入層。
dropout:Dropout 概率,用于防止過擬合。
super().__init__():調用父類的初始化方法,避免重復編寫代碼。
nn.Embedding():將離散的 token ID 映射為連續的向量表示。
參數 | 類型 | 描述 |
---|---|---|
num_embeddings | int | 詞匯表大小。 |
embedding_dim | int | 嵌入向量維度。 |
padding_idx | int (可選) | 填充標記的索引。 |
PositionalEncoding():實現位置編碼的具體類,通過特定的算法(如基于三角函數的方法)生成位置編碼向量,并添加到詞嵌入向量上。
DecorderLayer():定義了解碼器中的單個層結構,通常包含自注意力機制(用于處理目標序列自身的依賴關系)、編碼器 - 解碼器注意力機制(用于結合編碼器的輸出信息)以及前饋神經網絡等組件。它對輸入進行一系列的變換操作,更新目標序列的特征表示。
nn.ModuleList():存儲子模塊的列表,用于動態構建神經網絡。
參數 | 類型 | 描述 |
---|---|---|
modules | iterable (可選) | 初始化模塊列表。 |
nn.Dropout():在訓練過程中隨機將輸入的部分元素置為 0,減少神經元對特定特征的依賴,從而防止過擬合。
參數 | 描述 |
---|---|
p | 元素被置零的概率(默認?0.5 ),取值范圍 [0, 1]。 |
inplace | 是否直接在原張量上操作(默認?False )。 |
class Decoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, target_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(target_vocab_size, d_model) # 目標詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個解碼器層self.dropout = nn.Dropout(dropout) # Dropout層
Ⅲ、前向傳播
tgt:目標序列,形狀為(batch_size, tgt_len)
?。
encoder_output:編碼器的輸出,形狀為(batch_size, src_len, d_model)
?。
src_mask:源序列的填充掩碼,通過make_src_mask
方法生成。
tgt_mask:目標序列的掩碼,通過make_tgt_mask
方法生成。
self.embedding():將目標序列中的離散詞(通常是詞的索引)映射為連續的向量表示,即詞嵌入向量。這些向量能夠捕捉單詞的語義信息。
self.pos_encoding():對經過詞嵌入后的目標序列添加位置編碼信息。
math.sqrt():計算一個數的平方根(Python 內置函數)。
參數 | 類型 | 描述 |
---|---|---|
x | float | 要計算平方根的數值。 |
self.dropout():按照設定的概率對輸入進行隨機失活(將神經元輸出置為 0)操作。
self.layers:nn.ModuleList
類型的容器,存儲了多個堆疊的解碼器層(DecoderLayer
)。這些解碼器層按順序依次處理輸入數據,逐步對目標序列進行特征提取和轉換。
layer:每個單獨的DecoderLayer編碼器層
layer():DecoderLayer
類的實例對象的調用。它會對輸入(目標序列特征、編碼器輸出、源序列掩碼、目標序列掩碼等)進行處理,通過內部的多頭注意力機制和前饋網絡等組件,對目標序列的特征進行更新和轉換。
def forward(self, tgt, encoder_output, src_mask, tgt_mask):# 目標序列詞嵌入并縮放tgt = self.embedding(tgt) * math.sqrt(self.d_model)# 添加位置編碼tgt = self.pos_encoding(tgt)tgt = self.dropout(tgt)# 依次通過各解碼器層for layer in self.layers:tgt = layer(tgt, encoder_output, src_mask, tgt_mask)return tgt
Ⅳ、完整代碼
# 解碼器:根據編碼器輸出和已生成的輸出,逐步生成目標序列。
class Decoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, target_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(target_vocab_size, d_model) # 目標詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個解碼器層self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, tgt, encoder_output, src_mask, tgt_mask):# 目標序列詞嵌入并縮放tgt = self.embedding(tgt) * math.sqrt(self.d_model)# 添加位置編碼tgt = self.pos_encoding(tgt)tgt = self.dropout(tgt)# 依次通過各解碼器層for layer in self.layers:tgt = layer(tgt, encoder_output, src_mask, tgt_mask)return tgt
8.Transformer模型
Ⅰ、運行流程
① 初始化 ——> ② 創建掩碼 ——> ③ 編碼器處理 ——> ④ 解碼器處理 ——> ⑤ 輸出層映射
輸入:
src [batch, src_len] ← 源序列token ID
tgt [batch, tgt_len] ← 目標序列token ID(訓練時為已生成部分)↓
┌───────────────────────────┐
│ 步驟1:創建掩碼 │
└───────────────────────────┘↓
src_mask = make_src_mask(src) → [batch, 1, 1, src_len]
tgt_mask = make_tgt_mask(tgt) → [batch, 1, tgt_len, tgt_len]↓
┌───────────────────────────┐
│ 步驟2:編碼器處理 │
└───────────────────────────┘↓
src → 詞嵌入 + 位置編碼 → 多層編碼器層 → encoder_output↓
┌───────────────────────────┐
│ 步驟3:解碼器處理 │
└───────────────────────────┘↓
tgt → 詞嵌入 + 位置編碼 → 多層解碼器層(每層包含:├─ 掩蔽自注意力(使用tgt_mask)└─ 編碼器-解碼器注意力(使用src_mask)) → decoder_output↓
┌───────────────────────────┐
│ 步驟4:輸出層映射 │
└───────────────────────────┘↓
decoder_output → 線性層 → output [batch, tgt_len, target_vocab_size]
Ⅱ、初始化
nn.Linear():實現全連接層(線性變換),將輸入特征映射到輸出特征。
參數 | 類型 | 描述 |
---|---|---|
in_features | int | 輸入特征維度。 |
out_features | int | 輸出特征維度。 |
bias | bool (可選) | 是否添加偏置項(默認True )。 |
encoder:編碼器模塊,處理輸入序列,將其轉換為特征表示。
decoder:解碼器模塊,基于編碼器輸出和已生成的部分目標序列生成后續內容。
src_pad_idx:源語言序列中填充標記(如?<pad>
)的索引,用于創建掩碼。
tgt_pad_idx:目標語言序列中填充標記的索引,用于創建掩碼。
fc_out:線性輸出層,將解碼器輸出(維度為?encoder.d_model
)映射到目標詞匯表大小,以便預測下一個 token。
# Transformer模型:連接編碼器和解碼器,輸出預測序列。
class Transformer(nn.Module):def __init__(self, encoder, decoder, src_pad_idx, tgt_pad_idx):super().__init__()self.encoder = encoder # 編碼器self.decoder = decoder # 解碼器self.src_pad_idx = src_pad_idx # 源序列填充標記索引self.tgt_pad_idx = tgt_pad_idx # 目標序列填充標記索引# 輸出層:將解碼器輸出映射到目標詞匯表大小self.fc_out = nn.Linear(encoder.d_model, decoder.embedding.num_embeddings)
Ⅲ、源序列掩碼(src_mask)
src:輸入的源序列張量,形狀為?(batch_size, src_seq_len)
。
src_mask:源序列的填充掩碼,形狀為?(batch_size, 1, 1, src_seq_len)
。值為?True
?表示非填充位置,False
?表示填充位置。
.unsqueeze():在指定維度上插入一個新的維度,擴展張量的形狀。
參數 | 類型 | 描述 |
---|---|---|
dim | int | 要插入新維度的位置。 |
def make_src_mask(self, src):# 創建源序列的填充掩碼 (batch_size, 1, 1, seq_len)src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)return src_mask
Ⅳ、目標序列掩碼(tgt_mask)
tgt:目標序列,形狀為(batch_size, tgt_len)
?。
tgt_pad_mask:目標序列的填充掩碼,形狀為?(batch_size, 1, 1, tgt_seq_len)
。
seq_len:目標序列中填充標記對應的索引。
.size():返回張量的形狀,等同于?shape
?屬性。
參數 | 類型 | 描述 |
---|---|---|
dim | int (可選) | 指定要返回的維度大小。 |
tgt_sub_mask:三角掩碼(下三角矩陣),確保解碼器只關注已生成的 token。形狀為?(tgt_seq_len, tgt_seq_len)
。
torch.trill():返回矩陣的下三角部分,其余元素置零。
參數 | 類型 | 描述 |
---|---|---|
input | Tensor | 輸入矩陣。 |
diagonal | int (可選) | 對角線偏移量(默認 0)。 |
torch.ones():創建全為 1 的張量。
參數 | 類型 | 描述 |
---|---|---|
size | tuple | 張量形狀。 |
dtype | torch.dtype (可選) | 數據類型。 |
device | torch.device (可選) | 設備(CPU/GPU)。 |
tgt_mask:組合后的掩碼,同時考慮填充和未來信息屏蔽,形狀為?(batch_size, 1, tgt_seq_len, tgt_seq_len)
。
def make_tgt_mask(self, tgt):# 創建目標序列的填充掩碼 (batch_size, 1, 1, seq_len)tgt_pad_mask = (tgt != self.tgt_pad_idx).unsqueeze(1).unsqueeze(2)# 創建三角掩碼(下三角為1,上三角為0)seq_len = tgt.size(1)tgt_sub_mask = torch.tril(torch.ones((seq_len, seq_len), device=tgt.device)).bool()# 組合填充掩碼和三角掩碼tgt_mask = tgt_pad_mask & tgt_sub_maskreturn tgt_mask
Ⅴ、前向傳播
src:源序列,形狀為(batch_size, src_len)
?。
tgt:目標序列,形狀為(batch_size, tgt_len)
?。
src_mask:源序列的填充掩碼,通過make_src_mask
方法生成。
self.make_src_mask():生成源序列的填充掩碼(Padding Mask),用于在自注意力計算中忽略填充標記(如<pad>
)的影響。防止編碼器關注無意義的 padding 位置。
tgt_mask:目標序列的掩碼,通過make_tgt_mask
方法生成。
self.make_tgt_mask():生成目標序列的組合掩碼,同時處理填充和未來信息屏蔽(確保解碼器僅關注已生成的 token)。
def forward(self, src, tgt):# 創建掩碼src_mask = self.make_src_mask(src)tgt_mask = self.make_tgt_mask(tgt)# 通過編碼器encoder_output = self.encoder(src, src_mask)# 通過解碼器decoder_output = self.decoder(tgt, encoder_output, src_mask, tgt_mask)# 輸出層:映射到詞匯表大小output = self.fc_out(decoder_output)return output
Ⅵ、完整代碼
# Transformer模型:連接編碼器和解碼器,輸出預測序列。
class Transformer(nn.Module):def __init__(self, encoder, decoder, src_pad_idx, tgt_pad_idx):super().__init__()self.encoder = encoder # 編碼器self.decoder = decoder # 解碼器self.src_pad_idx = src_pad_idx # 源序列填充標記索引self.tgt_pad_idx = tgt_pad_idx # 目標序列填充標記索引# 輸出層:將解碼器輸出映射到目標詞匯表大小self.fc_out = nn.Linear(encoder.d_model, decoder.embedding.num_embeddings)def make_src_mask(self, src):# 創建源序列的填充掩碼 (batch_size, 1, 1, seq_len)src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)return src_maskdef make_tgt_mask(self, tgt):# 創建目標序列的填充掩碼 (batch_size, 1, 1, seq_len)tgt_pad_mask = (tgt != self.tgt_pad_idx).unsqueeze(1).unsqueeze(2)# 創建三角掩碼(下三角為1,上三角為0)seq_len = tgt.size(1)tgt_sub_mask = torch.tril(torch.ones((seq_len, seq_len), device=tgt.device)).bool()# 組合填充掩碼和三角掩碼tgt_mask = tgt_pad_mask & tgt_sub_maskreturn tgt_maskdef forward(self, src, tgt):# 創建掩碼src_mask = self.make_src_mask(src)tgt_mask = self.make_tgt_mask(tgt)# 通過編碼器encoder_output = self.encoder(src, src_mask)# 通過解碼器decoder_output = self.decoder(tgt, encoder_output, src_mask, tgt_mask)# 輸出層:映射到詞匯表大小output = self.fc_out(decoder_output)return output
9.完整模型
import torch
import torch.nn as nn
import torch.nn.functional as F
import math# 使用正弦和余弦函數生成不同頻率的編碼,使模型能夠學習序列中的相對位置關系。
class PositionalEncoding(nn.Module):def __init__(self, d_model, max_len=5000):super().__init__()# 創建位置編碼矩陣 (max_len, d_model)pe = torch.zeros(max_len, d_model)# 生成位置索引 (max_len, 1)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)# 生成頻率因子 (d_model//2,)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))# 偶數位置使用正弦函數:# 【:】:選取所有行 【0::2】:每行中取偶數索引的列(從0索引處出發,每隔兩個列)pe[:, 0::2] = torch.sin(position * div_term)# 奇數位置使用余弦函數:# 【:】:選取所有行 【1::2】:每行中取奇數索引的列(從1索引處出發,每隔兩個列)pe[:, 1::2] = torch.cos(position * div_term)# 使位置編碼能夠適配批次輸入并作為模型的靜態參數使用。# 添加批次維度 (1, max_len, d_model):適配 PyTorch 模型處理批量數據的要求。pe = pe.unsqueeze(0)# 注冊為緩沖區(非訓練參數):位置編碼是預計算的固定值,不需要在訓練過程中更新,因此不應該作為模型參數(如 nn.Parameter)。# 設備一致性:將 pe 注冊為緩沖區后,當模型調用 .to(device) 時,pe 會自動跟隨模型移動到指定設備(如 GPU),確保與輸入數據的設備一致性。# 模型保存與加載:緩沖區會隨模型一起保存和加載,確保推理時位置編碼可用。self.register_buffer('pe', pe)def forward(self, x):# 截取與輸入序列等長的位置編碼x = x + self.pe[:, :x.size(1)]return xclass MultiHeadAttention(nn.Module):def __init__(self, d_model, num_heads):super().__init__()assert d_model % num_heads == 0, "d_model必須能被num_heads整除"self.d_model = d_modelself.num_heads = num_headsself.d_k = d_model // num_heads# 定義線性變換層:Q, K, V投影矩陣和輸出投影矩陣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 scaled_dot_product_attention(self, Q, K, V, mask=None):# 計算注意力分數,縮放以穩定梯度scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)# 應用掩碼:將掩碼位置的分數設為負無窮,softmax后趨近于0if mask is not None:scores = scores.masked_fill(mask == 0, -1e9)# 應用softmax獲取注意力權重attention = F.softmax(scores, dim=-1)# 加權求和得到輸出output = torch.matmul(attention, V)return outputdef split_heads(self, x):# 將輸入分割成多個頭batch_size, seq_len, d_model = x.size()return x.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)def combine_heads(self, x):# 將多個頭的輸出合并batch_size, num_heads, seq_len, d_k = x.size()return x.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)def forward(self, Q, K, V, mask=None):# 線性變換 + 多頭分割Q = self.split_heads(self.W_q(Q))K = self.split_heads(self.W_k(K))V = self.split_heads(self.W_v(V))# 計算注意力attn_output = self.scaled_dot_product_attention(Q, K, V, mask)# 合并頭并應用最終線性變換output = self.W_o(self.combine_heads(attn_output))return output# 位置前饋網絡:對每個位置的特征進行非線性變換,增加模型表達能力。
class PositionwiseFeedForward(nn.Module):def __init__(self, d_model, d_ff):super().__init__()self.fc1 = nn.Linear(d_model, d_ff) # 第一層線性變換self.fc2 = nn.Linear(d_ff, d_model) # 第二層線性變換self.relu = nn.ReLU() # 非線性激活函數def forward(self, x):return self.fc2(self.relu(self.fc1(x)))# 編碼層
class EncoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 自注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, x, mask):# 自注意力 + 殘差連接 + 層歸一化attn_output = self.self_attn(x, x, x, mask)x = self.norm1(x + self.dropout(attn_output))# 前饋網絡 + 殘差連接 + 層歸一化ff_output = self.feed_forward(x)x = self.norm2(x + self.dropout(ff_output))return x# 解碼層
class DecoderLayer(nn.Module):def __init__(self, d_model, num_heads, d_ff, dropout):super().__init__()self.self_attn = MultiHeadAttention(d_model, num_heads) # 掩蔽自注意力self.cross_attn = MultiHeadAttention(d_model, num_heads) # 編碼器-解碼器注意力self.feed_forward = PositionwiseFeedForward(d_model, d_ff) # 前饋網絡self.norm1 = nn.LayerNorm(d_model) # 第一層歸一化self.norm2 = nn.LayerNorm(d_model) # 第二層歸一化self.norm3 = nn.LayerNorm(d_model) # 第三層歸一化self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, x, encoder_output, src_mask, tgt_mask):# 掩蔽自注意力:只關注已生成的token,通過三角掩碼防止解碼器看到未來位置attn_output1 = self.self_attn(x, x, x, tgt_mask)x = self.norm1(x + self.dropout(attn_output1))# 編碼器-解碼器注意力:關注編碼器輸出,連接編碼和解碼過程。attn_output2 = self.cross_attn(x, encoder_output, encoder_output, src_mask)x = self.norm2(x + self.dropout(attn_output2))# 前饋網絡ff_output = self.feed_forward(x)x = self.norm3(x + self.dropout(ff_output))return x# 編碼器:處理輸入序列,提取特征表示。
class Encoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, input_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(input_vocab_size, d_model) # 詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個編碼器層self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, src, src_mask):# 詞嵌入并縮放(乘以√d_model)src = self.embedding(src) * math.sqrt(self.d_model)# 添加位置編碼src = self.pos_encoding(src)src = self.dropout(src)# 依次通過各編碼器層for layer in self.layers:src = layer(src, src_mask)return src# 解碼器:根據編碼器輸出和已生成的輸出,逐步生成目標序列。
class Decoder(nn.Module):def __init__(self, num_layers, d_model, num_heads, d_ff, target_vocab_size, dropout):super().__init__()self.d_model = d_modelself.embedding = nn.Embedding(target_vocab_size, d_model) # 目標詞嵌入層self.pos_encoding = PositionalEncoding(d_model) # 位置編碼self.layers = nn.ModuleList([DecoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)]) # 堆疊多個解碼器層self.dropout = nn.Dropout(dropout) # Dropout層def forward(self, tgt, encoder_output, src_mask, tgt_mask):# 目標序列詞嵌入并縮放tgt = self.embedding(tgt) * math.sqrt(self.d_model)# 添加位置編碼tgt = self.pos_encoding(tgt)tgt = self.dropout(tgt)# 依次通過各解碼器層for layer in self.layers:tgt = layer(tgt, encoder_output, src_mask, tgt_mask)return tgt# Transformer模型:連接編碼器和解碼器,輸出預測序列。
class Transformer(nn.Module):def __init__(self, encoder, decoder, src_pad_idx, tgt_pad_idx):super().__init__()self.encoder = encoder # 編碼器self.decoder = decoder # 解碼器self.src_pad_idx = src_pad_idx # 源序列填充標記索引self.tgt_pad_idx = tgt_pad_idx # 目標序列填充標記索引# 輸出層:將解碼器輸出映射到目標詞匯表大小self.fc_out = nn.Linear(encoder.d_model, decoder.embedding.num_embeddings)def make_src_mask(self, src):# 創建源序列的填充掩碼 (batch_size, 1, 1, seq_len)src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)return src_maskdef make_tgt_mask(self, tgt):# 創建目標序列的填充掩碼 (batch_size, 1, 1, seq_len)tgt_pad_mask = (tgt != self.tgt_pad_idx).unsqueeze(1).unsqueeze(2)# 創建三角掩碼(下三角為1,上三角為0)seq_len = tgt.size(1)tgt_sub_mask = torch.tril(torch.ones((seq_len, seq_len), device=tgt.device)).bool()# 組合填充掩碼和三角掩碼tgt_mask = tgt_pad_mask & tgt_sub_maskreturn tgt_maskdef forward(self, src, tgt):# 創建掩碼src_mask = self.make_src_mask(src)tgt_mask = self.make_tgt_mask(tgt)# 通過編碼器encoder_output = self.encoder(src, src_mask)# 通過解碼器decoder_output = self.decoder(tgt, encoder_output, src_mask, tgt_mask)# 輸出層:映射到詞匯表大小output = self.fc_out(decoder_output)return output
三、模型創建
代碼運行流程
輸入參數 →
┌───────────────────────────────────────────┐
│ 創建編碼器 │
│ Encoder(
│ num_layers, d_model, num_heads, d_ff,
│ src_vocab_size, dropout
│ ) → encoder │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 創建解碼器 │
│ Decoder(
│ num_layers, d_model, num_heads, d_ff,
│ tgt_vocab_size, dropout
│ ) → decoder │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 創建完整Transformer模型 │
│ Transformer(encoder, decoder,
│ src_pad_idx, tgt_pad_idx) → model │
└───────────────────────────────────────────┘↓
┌───────────────────────────────────────────┐
│ 參數初始化 │
│ 遍歷model.parameters(): │
│ ├─ 若參數維度 > 1(如權重矩陣): │
│ │ ↓ │
│ │ 使用Xavier均勻分布初始化 │
│ │ nn.init.xavier_uniform_(p) │
│ └─ 否則(如偏置項): 跳過 │
└───────────────────────────────────────────┘↓
輸出: model(初始化后的Transformer模型)
變量名 | 類型 | 說明 |
---|---|---|
src_vocab_size | int | 源語言(如英文)詞匯表的大小,決定編碼器詞嵌入層的維度。 |
tgt_vocab_size | int | 目標語言(如中文)詞匯表的大小,決定解碼器詞嵌入層的維度。 |
src_pad_idx | int | 源語言中填充標記(<PAD> )的索引,用于生成源序列的填充掩碼。 |
tgt_pad_idx | int | 目標語言中填充標記(<PAD> )的索引,用于生成目標序列的填充掩碼。 |
d_model | int | Transformer 模型的隱藏層維度(詞嵌入維度),默認值為 512。 |
num_heads | int | 多頭注意力機制中的頭數,默認值為 8。要求?d_model ?能被?num_heads ?整除。 |
num_layers | int | 編碼器和解碼器中堆疊的層數,默認值為 6。 |
d_ff | int | 位置前饋網絡(FFN)的中間層維度,通常為?d_model ?的 4 倍,默認值為 2048。 |
dropout | float | dropout 概率,用于防止過擬合,默認值為 0.1。 |
encoder | Encoder 類實例 | 創建的編碼器對象,包含詞嵌入、位置編碼和多層編碼器層。 |
decoder | Decoder 類實例 | 創建的解碼器對象,包含詞嵌入、位置編碼和多層解碼器層(含跨注意力機制)。 |
model | Transformer 類實例 | 組裝后的完整 Transformer 模型,包含編碼器、解碼器、掩碼生成和輸出層。 |
p | torch.Tensor | 模型參數張量(如權重矩陣、偏置項),遍歷?model.parameters() ?得到。 |
Encoder():創建 Transformer 的編碼器模塊。
Decoder():創建 Transformer 的解碼器模塊。
Transformer():將編碼器和解碼器組合成完整的 Transformer 模型。
model.parameters():返回模型中所有可訓練參數的迭代器。
.dim():返回張量的維度數(即秩)。
nn.init_xavier_uniform_():使用 Xavier 均勻分布初始化參數。
????????Xavier 均勻化分布(Xavier Uniform Distribution)是一種用于神經網絡權重初始化的方法,由論文《Understanding the difficulty of training deep feedforward neural networks》(2010)提出,旨在解決深層網絡訓練中因權重初始化不當導致的梯度消失或爆炸問題。其核心思想是讓權重的初始分布滿足輸入和輸出的方差保持一致,從而使信號在網絡中更穩定地傳播。
假設模型中有一個線性層nn.Linear(10, 20)
,其參數包括:
weight
:形狀為[20, 10]
的二維張量(需要 Xavier 初始化)bias
:形狀為[20]
的一維張量(應保持默認初始化)
在 Transformer 模型中,對參數進行維度判斷后再應用 Xavier 初始化的核心目的是:只對需要的權重矩陣進行初始化,避免對偏置和其他特殊參數造成干擾,從而確保模型訓練的穩定性和有效性。
參數 | 類型 | 描述 |
---|---|---|
tensor | Tensor | 要初始化的張量。 |
gain | float (可選) | 縮放因子(默認 1) |
# 模型創建函數
def create_transformer_model(src_vocab_size, tgt_vocab_size, src_pad_idx, tgt_pad_idx,d_model=512, num_heads=8, num_layers=6, d_ff=2048, dropout=0.1):# 創建編碼器和解碼器encoder = Encoder(num_layers, d_model, num_heads, d_ff, src_vocab_size, dropout)decoder = Decoder(num_layers, d_model, num_heads, d_ff, tgt_vocab_size, dropout)# 創建完整的Transformer模型model = Transformer(encoder, decoder, src_pad_idx, tgt_pad_idx)# 使用Xavier均勻分布初始化參數# 確保參數矩陣的輸入和輸出的方差保持一致,從而讓信號在神經網絡中更穩定地傳播。for p in model.parameters():if p.dim() > 1:# 對輸入的參數張量(通常是權重矩陣)使用Xavier 均勻分布進行原地初始化(_后綴表示原地修改)。nn.init.xavier_uniform_(p)return model
四、模型文件及完整代碼
通過網盤分享的文件:Transformer源代碼和模型文件
鏈接: https://pan.baidu.com/s/147A4-T57M3NowRoIuv8Ziw?pwd=xwni 提取碼: xwni?
--來自百度網盤超級會員v3的分享