所有之前:CodeByMySelf 系列之 Transformer 的实现记录,只有标题没有内容的是TODO,针对经典算法的变体和改进算法在其他文章。
Transformer 下面是一个最基本的Transformer实现,主要包含以下几个部分:
基本模块 多头注意力模块 Token和位置嵌入模块 LayerNorm 前馈神经网络FFN $EncoderLayer -> Encoder$ $DecoderLayer -> Decoder$ $Encoder+Decoder -> Transformer$ 多头注意力MHA 多头注意力机制的公式如下: $$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \ldots, \text{head}_h)W^O $$其中,每一个头的计算分别为: $$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V $$对于图像数据和序列数据,多头注意力机制的实现是不同,但是核心原理是相同的。对于序列数据,每个位置对应一个token;对于图像数据,每个位置对应一个patch块或者一个像素块,如果是$patch$块,需要展开到一维向量。这里实现的为序列数据的多头注意力机制,初始$shape$为$(batch,time,d_model)$
import torch from torch import nn import torch.functional as F import math class MultiHeadAttention(nn.Module): def __init__(self,d_model,n_head): super(MultiHeadAttention, self).__init__() assert d_model % n_head == 0 self.d_model = d_model self.n_head = n_head self.w_k = nn.Linear(d_model,d_model) self.w_q = nn.Linear(d_model,d_model) self.w_v = nn.Linear(d_model,d_model) self.w_conbine = nn.Linear(d_model,d_model,bias=False) # 对应W^O self.softmax = nn.Softmax(dim=-1) def forward(self,q,k,v,mask=None): batch,time,dimension = q.shape() # q,k,v shape: (batch,time,d_model) n_d = self.d_model // self.n_head q,k,v = self.w_q(q),self.w_k(k),self.w_v(v) print(q.shape, batch, time, self.n_head, n_d) q = q.view(batch,time,self.n_head,n_d).transpose(1,2) # self.n_head * n_d = d_model k = k.view(batch,time,self.n_head,n_d).transpose(1,2) # shape: (batch,n_head,time,n_d) v = v.view(batch,time,self.n_head,n_d).transpose(1,2) # compute attention by the formula score = q @ k.transpose(-2,-1) / math.sqrt(n_d) # (batch,n_head,time,time) if mask is not None: score = score.masked_fill(mask==0,float('-inf')) attn = self.softmax(score) @ v # (batch,n_head,time,n_d) attn = attn.transpose(1,2).contiguous().view(batch,time,dimension) # (batch,time,dimension) attn = self.w_conbine(attn) # (batch,time,dimension) return attn d_model = 512 n_head = 8 mha = MultiHeadAttention(d_model,n_head) x = torch.randn(2,10,d_model) y = mha(x,x,x) # self-multi-head-attention print(f"shape: {y.shape}\n输出:{y}") # torch.Size([2, 10, 512]) Token和位置嵌入模块 Token Embedding 是将离散的词(token)转换为连续的向量表示的过程。通常通过查找表(如 nn.Embedding)将每个词的索引映射为一个高维向量,使模型能够处理和学习词之间的语义关系。 公式表示: ...