04a 向ChatGPT學Transformer - cccbook/py2gpt GitHub Wiki
ccc:
Transformer 的結構如下圖所示

這是由 Google 的團隊在
- Attention Is All You Need
ChatGPT:
當前的語言模型在自然語言處理任務中大放異彩,而 Transformer 是其中最受歡迎的架構之一。Transformer 是一種用於處理序列數據的神經網絡架構,它已被廣泛應用於機器翻譯、問答和文本生成等任務中。
下面是一個使用 PyTorch 內建模組實現的簡單 Transformer 模型:
import torch
import torch.nn as nn
class Transformer(nn.Module):
def __init__(self, input_size, output_size, hidden_size, num_layers, num_heads, dropout):
super().__init__()
self.embedding = nn.Embedding(input_size, hidden_size)
self.encoder = nn.TransformerEncoder(nn.TransformerEncoderLayer(hidden_size, num_heads, hidden_size, dropout), num_layers)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
x = self.embedding(x)
x = self.encoder(x)
x = x.mean(dim=0)
x = self.fc(x)
return x這個模型的構建使用了幾個重要的組件。首先,我們使用了一個嵌入層(Embedding)來將輸入序列轉換成向量表示。其次,我們使用了一個 TransformerEncoder 來對嵌入向量進行編碼。最後,我們使用一個全連接層(Linear)來將最終的 Transformer 輸出轉換為所需的輸出大小。
此外,我們還可以設置一些關鍵的超參數來控制模型的行為。這些超參數包括:
- input_size: 輸入序列的詞彙大小。
- output_size: 輸出序列的大小。
- hidden_size: Transformer 中隱藏層的維度大小。
- num_layers: Transformer 中堆疊的層數。
- num_heads: 注意力機制中的頭數。
- dropout: Dropout 的概率。
這個模型的意義在於,它提供了一個簡單而有效的方式來處理序列數據。Transformer 模型通過自注意力機制(self-attention)捕捉了輸入序列中的關聯性,從而能夠更好地處理長序列數據。此外,由於 Transformer 模型使用了堆疊的自注意力層和前向網絡層,因此它可以進一步提高模型的表現能力和泛化能力。
ChatGPT:
當前最流行的 Transformer 模型是由 Encoder 和 Decoder 兩部分構成的,因此在實現一個完整的 Transformer 模型時,需要同時考慮 Encoder 和 Decoder。
以下是一個使用 PyTorch 內建模組實現 Encoder-Decoder 架構的 Transformer 模型:
import torch
import torch.nn as nn
class Transformer(nn.Module):
def __init__(self, input_size, output_size, hidden_size, num_layers, num_heads, dropout):
super().__init__()
self.embedding = nn.Embedding(input_size, hidden_size)
self.encoder = nn.TransformerEncoder(nn.TransformerEncoderLayer(hidden_size, num_heads, hidden_size, dropout), num_layers)
self.decoder = nn.TransformerDecoder(nn.TransformerDecoderLayer(hidden_size, num_heads, hidden_size, dropout), num_layers)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x, y):
# Encoding
x = self.embedding(x)
x = self.encoder(x)
# Decoding
y = self.embedding(y)
y = self.decoder(y, x)
# Pooling over time dimension
y = y.mean(dim=0)
y = self.fc(y)
return y此模型與先前的模型相比,增加了一個 Decoder 部分,用於生成輸出序列。在這個模型中,Encoder 和 Decoder 都由一個或多個 Transformer 層堆疊而成。在 forward 方法中,我們首先對輸入序列進行編碼,然後將其傳遞到解碼器中進行解碼。最後,我們使用平均池化將解碼器的輸出平均成一個向量,並將其傳遞到一個全連接層中進行最終的輸出預測。
需要注意的是,解碼器的輸入 y 是目標序列,並且在嵌入層之後進行了位置編碼(position encoding)。這是因為在生成輸出序列時,模型需要考慮到目前已經生成的所有令牌,而位置編碼可以將這種位置信息引入模型中。
總的來說,這個模型提供了一個更加完整的 Transformer 實現,可以應用於各種序列數據的任務中。
ChatGPT:
Multi-Head Attention 是 Transformer 模型中的一個關鍵組件,用於在不同的表示空間中學習關鍵詞之間的相互作用。在 PyTorch 中,可以使用 torch.nn.MultiheadAttention 模組實現 Multi-Head Attention。不過,如果你想了解如何從頭開始實現 Multi-Head Attention,可以參考以下程式碼實現:
import torch
import torch.nn as nn
class MultiHeadAttention(nn.Module):
def __init__(self, hidden_size, num_heads, dropout):
super().__init__()
self.hidden_size = hidden_size
self.num_heads = num_heads
self.dropout = nn.Dropout(dropout)
assert hidden_size % num_heads == 0
self.head_size = hidden_size // num_heads
# Linear projections for Q, K, V
self.w_q = nn.Linear(hidden_size, hidden_size)
self.w_k = nn.Linear(hidden_size, hidden_size)
self.w_v = nn.Linear(hidden_size, hidden_size)
# Linear projection for the output of multi-head attention
self.w_h = nn.Linear(hidden_size, hidden_size)
def forward(self, q, k, v, mask=None):
# Linear projections for Q, K, V
q = self.w_q(q) # [batch_size, seq_len, hidden_size]
k = self.w_k(k) # [batch_size, seq_len, hidden_size]
v = self.w_v(v) # [batch_size, seq_len, hidden_size]
# Split the hidden size into multiple heads
q = q.view(-1, self.num_heads, q.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
k = k.view(-1, self.num_heads, k.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
v = v.view(-1, self.num_heads, v.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
# Compute the attention scores
scores = torch.matmul(q, k.transpose(-2, -1)) / (self.head_size ** 0.5) # [batch_size, num_heads, seq_len, seq_len]
if mask is not None:
mask = mask.unsqueeze(1).unsqueeze(2)
scores = scores.masked_fill(mask == 0, float('-inf'))
# Apply the softmax function to the attention scores
attn_weights = torch.softmax(scores, dim=-1) # [batch_size, num_heads, seq_len, seq_len]
attn_weights = self.dropout(attn_weights)
# Compute the weighted sum of the values
attn_output = torch.matmul(attn_weights, v) # [batch_size, num_heads, seq_len, head_size]
# Concatenate and linearly project the multi-head attention outputs
attn_output = attn_output.transpose(1, 2).contiguous().view(-1, q.size(-2), self.hidden_size) # [batch_size, seq_len, hidden_size]
attn_output = self.w_h(attn_output)
return attn_output, attn_weightsChatGPT:
Masked Multi-Head Attention 是一種 Multi-Head Attention,只是在計算 attention scores 時要考慮遮罩,避免模型在預測時能夠看到未來的信息。在 PyTorch 中,可以使用 torch.nn.MultiheadAttention 模組實現 Multi-Head Attention,但是沒有提供遮罩功能。如果要實現 Masked Multi-Head Attention,可以在 MultiHeadAttention 的基礎上進行修改,具體可以參考以下程式碼實現:
import torch
import torch.nn as nn
class MaskedMultiHeadAttention(nn.Module):
def __init__(self, hidden_size, num_heads, dropout):
super().__init__()
self.hidden_size = hidden_size
self.num_heads = num_heads
self.dropout = nn.Dropout(dropout)
assert hidden_size % num_heads == 0
self.head_size = hidden_size // num_heads
# Linear projections for Q, K, V
self.w_q = nn.Linear(hidden_size, hidden_size)
self.w_k = nn.Linear(hidden_size, hidden_size)
self.w_v = nn.Linear(hidden_size, hidden_size)
# Linear projection for the output of multi-head attention
self.w_h = nn.Linear(hidden_size, hidden_size)
def forward(self, q, k, v, mask=None):
# Linear projections for Q, K, V
q = self.w_q(q) # [batch_size, seq_len, hidden_size]
k = self.w_k(k) # [batch_size, seq_len, hidden_size]
v = self.w_v(v) # [batch_size, seq_len, hidden_size]
# Split the hidden size into multiple heads
q = q.view(-1, self.num_heads, q.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
k = k.view(-1, self.num_heads, k.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
v = v.view(-1, self.num_heads, v.size(-2), self.head_size) # [batch_size, num_heads, seq_len, head_size]
# Compute the attention scores
scores = torch.matmul(q, k.transpose(-2, -1)) / (self.head_size ** 0.5) # [batch_size, num_heads, seq_len, seq_len]
if mask is not None:
mask = mask.unsqueeze(1).unsqueeze(2)
scores = scores.masked_fill(mask == 0, float('-inf'))
# Apply the softmax function to the attention scores
attn_weights = torch.softmax(scores, dim=-1) # [batch_size, num_heads, seq_len, seq_len]
attn_weights = self.dropout(attn_weights)
# Compute the weighted sum of the values
attn_output = torch.matmul(attn_weights, v) # [batch_size, num_heads, seq_len, head_size]
# Concatenate and linearly project the multi-head attention outputs
attn_output = attn_output.transpose(1, 2).contiguous().view(-1, q.size(-2), self.hidden_size) # [batch_size, seq_len, hidden_size]
attn_output = self.w_h(attn_output)
return attn_output, attn_weightsChatGPT: 不客氣,有任何問題都歡迎再問我!