文章目錄
- Week 22: 深度學習補遺:Transformer Encoder構建
- 摘要
- Abstract
- 1. Positional Encoding 位置編碼
- 1.1 概要
- 1.2 代碼實現
- 1.3 效果簡析
- 2. LayerNorm 層歸一化
- 2.1 概要
- 2.2 代碼實現
- 3. Feed Forward Network 前饋神經網絡
- 3.1 概要
- 3.2 代碼實現
- 4. Encoder Layer構建
- 總結
Week 22: 深度學習補遺:Transformer Encoder構建
摘要
本週主要完成Transformer Encoder的代碼構建,繼續深挖幾個主要組成部分的數學原理以及代碼實現之間的細節,將理論與實踐相結合。
Abstract
This week’s primary focus was on constructing the Transformer Encoder code, continuing to delve into the mathematical principles of several key components and the intricacies of their code implementation, thereby integrating theory with practice.
1. Positional Encoding 位置編碼
1.1 概要
位置編碼作為整個Transformer最有意思的部分之一,非常值得研究和深挖。
Transformer的位置編碼對於Token中奇偶位置的數字採取不同的位置編碼。
這樣設計的目的主要是由於三角函數的和差角公式的存在。
可以得到,
簡要的説,位置編碼通過三角函數的編碼,利用其和差角公式的特性,對其位置信息進行加性的嵌入,同時考慮到了序列長度的因素
1.2 代碼實現
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model) # Initialize positional encoding matrix
position = torch.arange(0, max_len).unsqueeze(1) # [max_len, 1]
div_term = torch.exp(torch.arange(0, d_model, 2) *
(-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term) # sin for even indices
pe[:, 1::2] = torch.cos(position * div_term) # cos for odd indices
self.register_buffer('pe', pe.unsqueeze(0)) # [1, max_len, d_model]
def forward(self, x):
return x + self.pe[:, :x.size(1)]
# [:, :x.size(1)] means to get all the rows, and the first x.size(1) columns
這一份代碼中有許多numpy計算中的基本技巧需要進行深挖。
比較重要的一點是計算中的溢出問題,為了避免溢出,通常使用和
組合替代冪運算。
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
即
通過這種變換,避免了類型溢出。
1.3 效果簡析
對位置編碼結果進行渲染,可以發現,10個位置均能得到不同的位置編碼,又因為在計算中加入了,實際上可以適用不同的序列長度。加性的將位置編碼附加到Token中的各位置上,實際上就完成了位置信息的嵌入。
2. LayerNorm 層歸一化
2.1 概要
層歸一化是一個由Transformer推廣的一項關鍵技術,主要是為了解決BatchNorm在時序模型中的侷限性問題,其數學基礎主要是將均值歸一、方差歸零。
其中,是均值,
是方差,
和
是可學習的縮放偏移參數,
是防止除零的小常數。
2.2 代碼實現
class LayerNorm(nn.Module):
def __init__(self, d_model, eps=1e-5):
super().__init__()
self.d_model = d_model
self.eps = eps
self.gamma = nn.Parameter(torch.ones(d_model))
self.beta = nn.Parameter(torch.zeros(d_model))
def forward(self, x):
# 計算均值和方差
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
# 歸一化
x_norm = (x - mean) / torch.sqrt(var + self.eps)
# 縮放和偏移
return self.gamma * x_norm + self.beta
3. Feed Forward Network 前饋神經網絡
3.1 概要
前饋神經網絡由兩層線性層和一個激活函數組成,是比較基本的網絡結構,但因為激活函數的存在,可以學習比較複雜的非線性特徵。
3.2 代碼實現
class FeedForward(nn.Module):
def __init__(self, d_model, hidden, dropout=0.1):
super().__init__()
self.linear1 = nn.Linear(d_model, hidden)
self.linear2 = nn.Linear(hidden, d_model)
self.dropout = nn.Dropout(dropout)
self.activation = nn.ReLU()
def forward(self, x):
x = self.linear1(x)
x = self.activation(x)
x = self.dropout(x)
x = self.linear2(x)
return x
4. Encoder Layer構建
對於整個Transformer Encoder Block而言,實際上就是Input Embedding、Positional、Multi-Head Attention、FFN的組合,同時需要注意進行殘差連接。
class TransformerEmbedding(nn.Module):
def __init__(self, vocab_size, d_model, max_len, dropout=0.1):
super().__init__()
self.token_embedding = nn.Embedding(vocab_size, d_model, padding_idx=1)
self.positional_encoding = PositionalEncoding(d_model, max_len)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
tok_emb = self.token_embedding(x)
pos_emb = self.positional_encoding(x)
return self.dropout(tok_emb + pos_emb)
class EncoderLayer(nn.Module):
def __init__(self, d_model, num_heads, hidden, dropout=0.1):
super().__init__()
self.attention = MultiHeadAttention(d_model, num_heads)
self.norm1 = LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.ffn = FeedForward(d_model, hidden, dropout)
self.norm2 = LayerNorm(d_model)
self.dropout2 = nn.Dropout(dropout)
def forward(self, x, mask=None):
attn_output = self.attention(x, x, x, mask)
x = x + self.dropout1(attn_output) # residual connection
x = self.norm1(x)
ffn_output = self.ffn(x)
x = x + self.dropout2(ffn_output)
x = self.norm2(x)
return x
這樣子就完成了Transformer Encoder部分的構建。
總結
本週完成了Transformer Encoder部分的完整構建,充分的理解了包括位置編碼等幾項Transformer關鍵技術的深層數學原理以及應用效果,藉助構建Transformer Encoder深入的理解Encoder乃至一個神經網絡構建的基本流程以及前向傳播、反向傳播的數學公式在PyTorch中的實現。下週預計繼續完成Decoder部分構建,繼續深入理解,後續繼續嘗試快速利用已構建模塊搭建經典論文的關鍵部分。