Transformer架构:手撸源码实践(附带仓库地址)

Transformer架构:手撸源码实践

1. 引言

Transformer架构自2017年在论文《Attention Is All You Need》中被提出以来,彻底改变了自然语言处理(NLP)领域。它摒弃了传统的循环神经网络(RNN)和卷积神经网络(CNN),完全基于注意力机制构建,成为现代大语言模型(如BERT、GPT系列)的基础架构。

本文将深入解析Transformer的核心组件,通过手写实现代码来帮助理解其内部工作机制。

2. Transformer整体架构

Transformer采用经典的编码器-解码器架构:

复制代码
输入序列 → 编码器 → 解码器 → 输出序列

2.1 编码器(Encoder)

编码器由6个相同的层堆叠而成,每层包含两个子层:

  1. 多头自注意力机制(Multi-Head Self-Attention)
  2. 位置前馈网络(Position-wise Feed-Forward Networks)

每层都使用残差连接和层归一化。

编码器可以表示为以下公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c L a y e r ( x ) = L a y e r N o r m ( x + S e l f A t t e n t i o n ( x ) ) \mathrm{EncLayer}(x) = \mathrm{LayerNorm}(x + \mathrm{SelfAttention}(x)) </math>EncLayer(x)=LayerNorm(x+SelfAttention(x))

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c L a y e r ( x ) = L a y e r N o r m ( x + F F N ( x ) ) \mathrm{EncLayer}(x) = \mathrm{LayerNorm}(x + \mathrm{FFN}(x)) </math>EncLayer(x)=LayerNorm(x+FFN(x))

完整的编码器层可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c o d e r L a y e r ( x ) = L a y e r N o r m ( L a y e r N o r m ( x + S e l f A t t e n t i o n ( x ) ) + F F N ( L a y e r N o r m ( x + S e l f A t t e n t i o n ( x ) ) ) ) \mathrm{EncoderLayer}(x) = \mathrm{LayerNorm}(\mathrm{LayerNorm}(x + \mathrm{SelfAttention}(x)) + \mathrm{FFN}(\mathrm{LayerNorm}(x + \mathrm{SelfAttention}(x)))) </math>EncoderLayer(x)=LayerNorm(LayerNorm(x+SelfAttention(x))+FFN(LayerNorm(x+SelfAttention(x))))

2.2 解码器(Decoder)

解码器也由6个相同的层堆叠而成,每层包含三个子层:

  1. 掩码多头自注意力机制(Masked Multi-Head Self-Attention)
  2. 编码器-解码器注意力机制(Encoder-Decoder Attention)
  3. 位置前馈网络(Position-wise Feed-Forward Networks)

同样,每层都使用残差连接和层归一化。

解码器层可以表示为以下公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c L a y e r ( x , e n c o u t ) = L a y e r N o r m ( x + M a s k e d S e l f A t t e n t i o n ( x ) ) \mathrm{DecLayer}(x, enc_out) = \mathrm{LayerNorm}(x + \mathrm{MaskedSelfAttention}(x)) </math>DecLayer(x,encout)=LayerNorm(x+MaskedSelfAttention(x))

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c L a y e r ( x , e n c o u t ) = L a y e r N o r m ( x + E n c o d e r A t t e n t i o n ( x , e n c o u t ) ) \mathrm{DecLayer}(x, enc_out) = \mathrm{LayerNorm}(x + \mathrm{EncoderAttention}(x, enc_out)) </math>DecLayer(x,encout)=LayerNorm(x+EncoderAttention(x,encout))

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c L a y e r ( x , e n c o u t ) = L a y e r N o r m ( x + F F N ( x ) ) \mathrm{DecLayer}(x, enc_out) = \mathrm{LayerNorm}(x + \mathrm{FFN}(x)) </math>DecLayer(x,encout)=LayerNorm(x+FFN(x))

3. 核心组件详解

3.1 注意力机制(Attention)

注意力机制是Transformer的核心,其数学表达式为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V </math>Attention(Q,K,V)=softmax(dk QKT)V

其中:

  • Q(Query):查询向量
  • K(Key):键向量
  • V(Value):值向量
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> d k d_k </math>dk:键向量的维度

缩放因子 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 d k \frac{1}{\sqrt{d_k}} </math>dk 1的作用是为了防止点积结果过大导致softmax函数梯度消失。

在我们的实现中,注意力机制的代码如下:

python 复制代码
def attention(q, k, v, mask=None, dropout=None):
    """
    计算注意力机制
    
    Args:
        q: 查询矩阵 [batch_size, heads, seq_len, d_k]
        k: 键矩阵 [batch_size, heads, seq_len, d_k]
        v: 值矩阵 [batch_size, heads, seq_len, d_k]
        mask: 掩码矩阵,用于屏蔽某些位置
        dropout: dropout层
    
    Returns:
        tuple: (注意力输出, 注意力权重)
    """
    # 自注意力公式三步走

    # 第一步: q* k的转置除以根号d_k

    # 第二部: 判断是否做掩码, softmax层计算

    # 第三部: 乘以V
    # [batch_size, sqen_len, embed_dim]
    embed_dim = k.size(-1)
    # 计算Q和K的点积 [batch_size, heads, seq_len, seq_len]
    q_k = torch.matmul(q, k.transpose(-1,-2)) # batch_size, sqen_len, sqen_len
    # 缩放点积,除以根号d_k,防止梯度消失
    attn_weight = q_k / math.sqrt(embed_dim)

    if mask is not None:
        # 使用mask屏蔽不需要关注的位置,将被屏蔽位置的权重设为极小值
        attn_weight = attn_weight.masked_fill(mask == 0, -1e9)

    # 对注意力权重进行softmax归一化
    attn_weight = nn.functional.softmax(attn_weight, dim=-1)

    if dropout is not None:
        # 应用dropout防止过拟合
        attn_weight = dropout(attn_weight)

    # 使用注意力权重对V进行加权求和
    res = torch.matmul(attn_weight , v)

    return res, attn_weight

3.2 多头注意力(Multi-Head Attention)

为了增强模型捕捉不同位置信息的能力,Transformer使用多头注意力机制:

<math xmlns="http://www.w3.org/1998/Math/MathML"> MultiHead ( Q , K , V ) = Concat ( head 1 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O </math>MultiHead(Q,K,V)=Concat(head1,...,headh)WO

其中每个头计算为: <math xmlns="http://www.w3.org/1998/Math/MathML"> head i = Attention ( Q W i Q , K W i K , V W i V ) \text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) </math>headi=Attention(QWiQ,KWiK,VWiV)

多头注意力机制允许模型在不同表示子空间中并行关注信息,增强了模型的表达能力。

MultiHead计算的完整过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . , h e a d h ) W O \mathrm{MultiHead}(Q, K, V) = \mathrm{Concat}(\mathrm{head_1}, ..., \mathrm{head_h})W^O </math>MultiHead(Q,K,V)=Concat(head1,...,headh)WO

<math xmlns="http://www.w3.org/1998/Math/MathML"> where h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) \text{where} \quad \mathrm{head_i} = \mathrm{Attention}(QW_i^Q, KW_i^K, VW_i^V) </math>whereheadi=Attention(QWiQ,KWiK,VWiV)

多头注意力机制结构图
python 复制代码
class MultiHeadAttention(nn.Module):
    """
    多头注意力机制
    将输入分割为多个头,分别计算注意力,然后合并结果
    """
    def __init__(self, heads, embed_dim, dropout_p=0.1):
        """
        初始化多头注意力
        
        Args:
            heads: 注意力头的数量
            embed_dim: 嵌入维度
            dropout_p: dropout概率
        """
        super().__init__()
        # 构建属性
        self.heads = heads
        self.embed_dim = embed_dim

        # 确保嵌入维度可以被头数整除
        assert embed_dim % heads  == 0
        # 计算每个头的维度
        self.d_k = embed_dim // heads
        # 构建4个线性层: 3个用于Q、K、V的线性变换,1个用于最终输出
        self.linears = clone_modules(nn.Linear(embed_dim, embed_dim), 4)
        # 实例化dropout
        self.dropout = nn.Dropout(dropout_p)

    def forward(self, q, k, v, mask=None):
        """
        前向传播
        
        Args:
            q: 查询 [batch_size, seq_len, embed_dim]
            k: 键 [batch_size, seq_len, embed_dim]
            v: 值 [batch_size, seq_len, embed_dim]
            mask: 掩码矩阵
        
        Returns:
            torch.Tensor: 多头注意力输出
        """
        if mask is not None:
            # 扩展mask的维度以匹配多头
            mask = mask.unsqueeze(0)
        batch_size = q.size(0)
        heads = self.heads
        embed_dim = q.size(-1)
        d_k = self.d_k
        # 拆分多头序列 + 做线性变换
        # 对Q、K、V分别应用线性变换,然后reshape为(batch_size, heads, seq_len, d_k)
        q, k, v = [linear(x)
                    .view(batch_size, -1, heads, d_k)
                    .transpose(1,2)
                    for linear,x in zip(self.linears,(q,k,v))]

        # 获取注意力机制输出
        output, attn_weight = attention(q, k, v, mask, self.dropout)
        # 合并多头到一个量中
        # 交换维度并合并heads和d_k维度
        res = output.transpose(1,2).contiguous().view(batch_size, -1, embed_dim)
        # 最后使用线性层做收尾处理
        return self.linears[-1](res)

3.3 位置编码(Positional Encoding)

由于自注意力机制本身不包含位置信息,Transformer引入位置编码来为模型提供序列中词的位置信息。

Transformer使用正弦和余弦函数生成位置编码:

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i)=sin(100002i/dmodelpos)

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i+1)=cos(100002i/dmodelpos)

其中:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> p o s pos </math>pos 是位置
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> i i </math>i 是维度
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> d model d_{\text{model}} </math>dmodel 是模型维度

这种设计使得模型能够学习到相对位置信息,因为对于任意固定的偏移 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k, <math xmlns="http://www.w3.org/1998/Math/MathML"> P E p o s + k PE_{pos+k} </math>PEpos+k 可以表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> P E p o s PE_{pos} </math>PEpos 的线性函数。

位置编码的完整公式可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i)=sin(100002i/dmodelpos)

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i+1)=cos(100002i/dmodelpos)

python 复制代码
class PositionalEmbedding(nn.Module):
    """
    位置编码层
    为序列中每个位置生成独特的编码,使模型能够利用序列顺序信息
    """
    def __init__(self, max_length, embed_dim, dropout_p=0.1):
        """
        初始化位置编码层
        
        Args:
            max_length: 序列最大长度
            embed_dim: 嵌入维度
            dropout_p: dropout概率
        """
        super().__init__()
        self.max_length = max_length
        self.embed_dim = embed_dim

        # Dropout层用于防止过拟合
        self.dropout = nn.Dropout(p=dropout_p)

        # 创建位置编码矩阵 [max_length, embed_dim]
        pe = torch.zeros(max_length, embed_dim)   # [60, 512]
        
        # 生成位置信息 [max_length, 1]
        pos = torch.arange(0, max_length).unsqueeze(1) # [60, 1] = [[0],[1],[2], ...[59]]
        # 生成维度索引 0, 2, 4, ..., embed_dim-2 (只取偶数索引)
        tow_i = torch.arange(0, embed_dim, 2) # [256] = 0 2 4 6 8 .. 510  , 一共256个数
        # 计算角度频率参数
        tmp_v = torch.exp((math.log(10000) * -tow_i / self.embed_dim)) # [256]

        # 计算角度值:pos * 角度频率
        fn_v = tmp_v * pos # [256] * [60, 1]

        # 偶数位置使用sin编码
        pe[:, 0::2] = torch.sin(fn_v)
        # 奇数位置使用cos编码
        pe[:, 1::2] = torch.cos(fn_v)

        # 增加batch维度 [1, max_length, embed_dim]
        pe = pe.unsqueeze(0) # [1, 60, 512]

        # 注册为buffer,不会被更新,但会随模型保存和加载
        self.register_buffer('pe', pe)

    def forward(self, x): # 输入形状:[batch_size, seq_len, embed_dim]
        """
        前向传播
        
        Args:
            x: 输入张量 [batch_size, seq_len, embed_dim]
        
        Returns:
            torch.Tensor: 加入位置编码的张量 [batch_size, seq_len, embed_dim]
        """
        # 截取与输入序列长度相等的位置编码
        p_x = self.pe[:, :x.size(1)] # [1, seq_len, embed_dim]
        # 将位置编码与输入相加,并应用dropout
        res = self.dropout(p_x + x) # [batch_size, seq_len, embed_dim]

        return res

3.4 词嵌入与缩放(Embedding and Scaling)

Transformer中的Embedding层负责将离散的词汇转换为连续向量表示。为了保持数值稳定性,Transformer在词嵌入后进行缩放操作:

<math xmlns="http://www.w3.org/1998/Math/MathML"> Embedding scaled = Embedding × d model \text{Embedding}{\text{scaled}} = \text{Embedding} \times \sqrt{d{\text{model}}} </math>Embeddingscaled=Embedding×dmodel

词嵌入缩放的目的是平衡词嵌入和位置编码的数值范围,确保两者在数值上处于相近的量级,有助于模型训练的稳定性。

python 复制代码
class Embeddings(nn.Module):
    """
    词嵌入层
    将词汇索引转换为密集向量表示
    """
    def __init__(self, input_size, embed_dim):
        """
        初始化词嵌入层
        
        Args:
            input_size: 词汇表大小
            embed_dim: 嵌入维度
        """
        super().__init__()
        self.embed_dim = embed_dim
        # 创建嵌入层,将input_size个单词映射到embed_dim维向量
        self.embed = nn.Embedding(input_size, embedding_dim=embed_dim)

    def forward(self, x):
        """
        前向传播
        
        Args:
            x: 输入张量,包含词汇索引 [batch_size, seq_len]
        
        Returns:
            torch.Tensor: 词嵌入向量 [batch_size, seq_len, embed_dim]
        """
        # 通过嵌入层获取向量表示,并除以sqrt(embed_dim)进行缩放
        # 这种缩放有助于稳定梯度,特别是在位置编码加入之后
        return self.embed(x) / math.sqrt(self.embed_dim)

3.5 层归一化(Layer Normalization)

层归一化用于稳定训练过程,加速收敛。其数学公式为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> μ = 1 H ∑ i = 1 H x i \mu = \frac{1}{H}\sum_{i=1}^{H} x_i </math>μ=H1∑i=1Hxi

<math xmlns="http://www.w3.org/1998/Math/MathML"> σ 2 = 1 H ∑ i = 1 H ( x i − μ ) 2 \sigma^2 = \frac{1}{H}\sum_{i=1}^{H} (x_i - \mu)^2 </math>σ2=H1∑i=1H(xi−μ)2

<math xmlns="http://www.w3.org/1998/Math/MathML"> y = x − μ σ 2 + ϵ ⊙ γ + β y = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta </math>y=σ2+ϵ x−μ⊙γ+β

其中:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> μ \mu </math>μ 是均值
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> σ 2 \sigma^2 </math>σ2 是方差
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> ϵ \epsilon </math>ϵ 是一个小常数,防止除零
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> γ \gamma </math>γ 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> β \beta </math>β 是可学习的缩放和偏移参数

层归一化的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> L a y e r N o r m ( x ) = x − μ σ 2 + ϵ ⊙ γ + β \mathrm{LayerNorm}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta </math>LayerNorm(x)=σ2+ϵ x−μ⊙γ+β

python 复制代码
class LayerNorm(nn.Module):
    """
    层归一化
    对每个样本的特征进行归一化,而不是对批次维度进行归一化
    """
    def __init__(self, embed_dim, eps=1e-6):
        """
        初始化层归一化
        
        Args:
            embed_dim: 嵌入维度
            eps: 防止除零的小值
        """
        super().__init__()
        # 创建可学习的缩放参数W,初始化为全1矩阵
        self.w = nn.Parameter(torch.ones(embed_dim))
        # 创建可学习的偏移参数B,初始化为全0矩阵
        self.b = nn.Parameter(torch.zeros(embed_dim))
        # 防止除零的小值
        self.eps = eps

    def forward(self, x):
        """
        前向传播
        
        Args:
            x: 输入张量 [batch_size, seq_len, embed_dim]
        
        Returns:
            torch.Tensor: 归一化后的张量
        """
        # 计算平均数
        x_mean = torch.mean(x, keepdim=True, dim=-1) # keepdim=True , 保持形状一致
        # 计算标准差
        x_std = torch.std(x, keepdim=True, dim=-1)
        # 计算 (x-平均数) / (标准差+eps) = 标准化后的x
        res = (x - x_mean) / (x_std + self.eps)  # 修正了原代码中的运算符优先级问题
        # 返回 W * x + b,应用可学习的缩放和偏移
        return self.w * res + self.b

3.6 前馈神经网络(Feed-Forward Networks)

前馈网络对每个位置的表示进行非线性变换。其数学公式为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> F F N ( x ) = max ⁡ ( 0 , x W 1 + b 1 ) W 2 + b 2 \mathrm{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 </math>FFN(x)=max(0,xW1+b1)W2+b2

其中:

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> W 1 ∈ R d m o d e l × d f f W_1 \in \mathbb{R}^{d_{model} \times d_{ff}} </math>W1∈Rdmodel×dff 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> W 2 ∈ R d f f × d m o d e l W_2 \in \mathbb{R}^{d_{ff} \times d_{model}} </math>W2∈Rdff×dmodel 是参数矩阵
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> b 1 b_1 </math>b1 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> b 2 b_2 </math>b2 是偏置向量
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> d f f = 2048 d_{ff} = 2048 </math>dff=2048 是前馈网络的隐藏层维度

前馈网络的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> F F N ( x ) = R e L U ( x W 1 + b 1 ) W 2 + b 2 \mathrm{FFN}(x) = \mathrm{ReLU}(xW_1 + b_1)W_2 + b_2 </math>FFN(x)=ReLU(xW1+b1)W2+b2

python 复制代码
class FeedForward(nn.Module):
    """
    前馈神经网络
    在Transformer中用于对每个位置的表示进行非线性变换
    """
    def __init__(self, embed_dim, hidden_size=2048, dropout_p = 0.1):
        """
        初始化前馈网络
        
        Args:
            embed_dim: 嵌入维度
            hidden_size: 隐藏层大小,默认为2048
            dropout_p: dropout概率
        """
        super().__init__()
        # 两个线性层, 外加ReLU激活函数和dropout随机失活
        # 第一个线性层将维度从embed_dim扩展到hidden_size
        self.linear1 = nn.Linear(embed_dim, hidden_size)

        # ReLU激活函数
        self.relu = nn.ReLU()

        # Dropout层用于防止过拟合
        self.dropout = nn.Dropout(p=dropout_p)

        # 第二个线性层将维度从hidden_size压缩回embed_dim
        self.linear2 = nn.Linear(hidden_size, embed_dim)

    def forward(self, x):
        """
        前向传播
        
        Args:
            x: 输入张量 [batch_size, seq_len, embed_dim]
        
        Returns:
            torch.Tensor: 前馈网络输出
        """
        # 第一层线性变换
        x = self.linear1(x)

        # ReLU激活
        x = self.relu(x)

        # Dropout防止过拟合
        x = self.dropout(x)

        # 第二层线性变换
        x = self.linear2(x)

        return x

3.7 子层连接(Sublayer Connection)

子层连接实现残差连接和层归一化。其数学公式为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> S u b l a y e r ( x ) = L a y e r N o r m ( x + S u b l a y e r ( x ) ) \mathrm{Sublayer}(x) = \mathrm{LayerNorm}(x + \mathrm{Sublayer}(x)) </math>Sublayer(x)=LayerNorm(x+Sublayer(x))

其中Sublayer(x)可以是自注意力层或前馈网络层。

子层连接的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> S u b l a y e r C o n n e c t i o n ( x , S u b l a y e r ) = L a y e r N o r m ( x + D r o p o u t ( S u b l a y e r ( x ) ) ) \mathrm{SublayerConnection}(x, \mathrm{Sublayer}) = \mathrm{LayerNorm}(x + \mathrm{Dropout}(\mathrm{Sublayer}(x))) </math>SublayerConnection(x,Sublayer)=LayerNorm(x+Dropout(Sublayer(x)))

python 复制代码
class SubLayers(nn.Module):
    """
    子层连接模块
    实现残差连接和层归一化
    """
    def __init__(self, embed_dim, dropout_p=0.1):
        """
        初始化子层连接
        
        Args:
            embed_dim: 嵌入维度
            dropout_p: dropout概率
        """
        super().__init__()
        # 实例化层归一化函数
        self.norm = LayerNorm(embed_dim)

        # Dropout层
        self.dropout = nn.Dropout(p=dropout_p)

    def forward(self, x, layer_fn):
        """
        前向传播
        
        Args:
            x: 输入张量
            layer_fn: 要应用的层函数(如注意力层或前馈网络)
        
        Returns:
            torch.Tensor: 经过残差连接和层归一化的输出
        """
        # 对输入应用指定的层函数,然后进行层归一化和dropout
        # 最后与原始输入进行残差连接(x + res)
        res  = self.dropout(self.norm(layer_fn(x)))
        return x + res

4. 编码器实现

编码器由多个编码器层堆叠而成:

编码器层的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c o d e r L a y e r ( x ) = S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( x , S e l f A t t e n t i o n ) , F F N ) \mathrm{EncoderLayer}(x) = \mathrm{SublayerConnection}(\mathrm{SublayerConnection}(x, \mathrm{SelfAttention}), \mathrm{FFN}) </math>EncoderLayer(x)=SublayerConnection(SublayerConnection(x,SelfAttention),FFN)

编码器的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c o d e r ( x ) = L a y e r N o r m ( E n c o d e r L a y e r N ( ⋯ ( E n c o d e r L a y e r 1 ( x ) ) ⋯   ) ) \mathrm{Encoder}(x) = \mathrm{LayerNorm}(\mathrm{EncoderLayer}_N(\cdots(\mathrm{EncoderLayer}_1(x))\cdots)) </math>Encoder(x)=LayerNorm(EncoderLayerN(⋯(EncoderLayer1(x))⋯))

python 复制代码
class EncoderLayer(nn.Module):
    """
    编码器层
    包含自注意力层和前馈神经网络层
    """
    def __init__(self, embed_dim, heads, dropout_p=0.1, hidden_size=2048):
        """
        初始化编码器层
        
        Args:
            embed_dim: 嵌入维度
            heads: 注意力头数
            dropout_p: dropout概率
            hidden_size: 前馈网络隐藏层大小
        """
        super().__init__()

        # 多头自注意力机制
        self.multi_head_attn = MultiHeadAttention(heads=heads, embed_dim=embed_dim, dropout_p=dropout_p)

        # 前馈神经网络
        self.feed_ward = FeedForward(embed_dim=embed_dim, hidden_size=hidden_size, dropout_p=dropout_p)

        # 两个子层连接模块:自注意力和前馈网络
        self.layers = clone_modules(SubLayers(embed_dim=embed_dim, dropout_p=dropout_p), N=2)

    def forward(self, x, mask=None):
        """
        前向传播
        
        Args:
            x: 输入张量 [batch_size, seq_len, embed_dim]
            mask: 掩码张量,用于屏蔽无效位置
        
        Returns:
            torch.Tensor: 编码器层输出
        """
        # 第一层:自注意力机制
        x = self.layers[0](x, lambda x: self.multi_head_attn(x, x, x, mask))

        # 第二层:前馈神经网络
        x = self.layers[1](x, self.feed_ward)
        
        return x

class Encoder(nn.Module):
    """
    编码器
    由多个编码器层堆叠组成
    """
    def __init__(self, embed_dim, layer, N):
        """
        初始化编码器
        
        Args:
            embed_dim: 嵌入维度
            layer: 编码器层模板
            N: 编码器层数
        """
        super().__init__()

        # 克隆N个相同的编码器层
        self.encoder_layers = clone_modules(layer, N)

        # 输出层归一化
        self.norm = LayerNorm(embed_dim=embed_dim)

    def forward(self, x, mask=None):
        """
        前向传播
        
        Args:
            x: 输入张量 [batch_size, seq_len, embed_dim]
            mask: 掩码张量
        
        Returns:
            torch.Tensor: 编码器输出
        """
        # 依次通过每个编码器层
        for layer in self.encoder_layers:
            x = layer(x, mask=mask)
        # 最终层归一化
        res = self.norm(x)
        return res

5. 解码器实现

5.1 解码器实现

解码器也由多个解码器层堆叠而成,但结构比编码器更复杂:

解码器层的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c o d e r L a y e r ( x , e n c o u t ) = S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( x , M a s k e d S e l f A t t e n t i o n ) , E n c o d e r A t t e n t i o n ) , F F N ) \mathrm{DecoderLayer}(x, enc_out) = \mathrm{SublayerConnection}(\mathrm{SublayerConnection}(\mathrm{SublayerConnection}(x, \mathrm{MaskedSelfAttention}), \mathrm{EncoderAttention}), \mathrm{FFN}) </math>DecoderLayer(x,encout)=SublayerConnection(SublayerConnection(SublayerConnection(x,MaskedSelfAttention),EncoderAttention),FFN)

解码器的完整计算过程可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c o d e r ( x , e n c o u t ) = L a y e r N o r m ( D e c o d e r L a y e r N ( ⋯ ( D e c o d e r L a y e r 1 ( x , e n c o u t ) ) ⋯   ) ) \mathrm{Decoder}(x, enc_out) = \mathrm{LayerNorm}(\mathrm{DecoderLayer}_N(\cdots(\mathrm{DecoderLayer}_1(x, enc_out))\cdots)) </math>Decoder(x,encout)=LayerNorm(DecoderLayerN(⋯(DecoderLayer1(x,encout))⋯))

python 复制代码
class DecoderLayer(nn.Module):
    """
    解码器层
    包含自注意力层、编码器-解码器注意力层和前馈网络
    """
    def __init__(self, embed_dim, heads, feed_forward_hidden_size, dropout_p=0.1):
        """
        初始化解码器层
        
        Args:
            embed_dim: 嵌入维度
            heads: 注意力头数
            feed_forward_hidden_size: 前馈网络隐藏层大小
            dropout_p: dropout概率
        """
        super().__init__()
        # 第一个自注意力机制:目标序列的自注意力(需要掩码)
        self.multi_head_attn = MultiHeadAttention(heads=heads, embed_dim=embed_dim, dropout_p=dropout_p)
        # 三个子层连接模块:自注意力、编码器-解码器注意力、前馈网络
        self.layers = clone_modules(SubLayers(embed_dim=embed_dim, dropout_p=dropout_p), N=3)

        # 前馈神经网络
        self.feed_forward = FeedForward(embed_dim=embed_dim, dropout_p=dropout_p, hidden_size=feed_forward_hidden_size)

    def forward(self, q, k, v, target_mask=None, source_mask=None):
        """
        前向传播
        
        Args:
            q: 查询向量(目标序列)
            k: 键向量(编码器输出)
            v: 值向量(编码器输出)
            target_mask: 目标序列掩码(防止未来信息泄露)
            source_mask: 源序列掩码
        
        Returns:
            torch.Tensor: 解码器层输出
        """
        # 第一层:目标序列的自注意力(使用target_mask防止看到未来信息)
        res_1 = self.layers[0](q, lambda x: self.multi_head_attn(x, x, x, target_mask))

        # 第二层:编码器-解码器注意力(查询来自前一层,键值来自编码器输出)
        res_2 = self.layers[1](res_1, lambda x: self.multi_head_attn(x, k, v, source_mask))

        # 第三层:前馈神经网络
        res_3 = self.layers[2](res_2, self.feed_forward)

        return res_3

class Decoder(nn.Module):
    """
    解码器
    由多个解码器层堆叠而成
    """
    def __init__(self, embed_dim, decoder_layer, n):
        """
        初始化解码器
        
        Args:
            embed_dim: 嵌入维度
            decoder_layer: 解码器层模板
            n: 解码器层数
        """
        super().__init__()
        # 输出层归一化
        self.norm = LayerNorm(embed_dim=embed_dim)

        # 克隆N个解码器层
        self.encoder_layers = clone_modules(decoder_layer, n)

    def forward(self, q, k, v, target_mask=None, source_mask=None):
        """
        前向传播
        
        Args:
            q: 查询向量(目标序列)
            k: 键向量(编码器输出)
            v: 值向量(编码器输出)
            target_mask: 目标序列掩码
            source_mask: 源序列掩码
        
        Returns:
            torch.Tensor: 解码器输出
        """
        # 初始输入为查询向量
        res = q
        # 依次通过每个解码器层
        for layer in self.encoder_layers:
            res = layer(q, k, v, target_mask, source_mask)

        # 最终层归一化
        res = self.norm(res)

        return res

5.2输出部分介绍

输出部分包含: * 线性层 * softmax层

5.2.1 线性层的作用
  • 通过对上一步的线性变化得到指定维度的输出, 也就是转换维度的作用.
5.2.2 softmax层的作用
  • 使最后一维的向量中的数字缩放到0-1的概率值域内, 并满足他们的和为1.
5.2.3 线性层和softmax层的代码分析
ruby 复制代码
# 解码器类 Generator 实现思路分析
# init函数 (self, d_model, vocab_size)
    # 定义线性层self.project
# forward函数 (self, x)
    # 数据 F.log_softmax(self.project(x), dim=-1)

class Generator(nn.Module):
    def __init__(self, d_model, vocab_size):
        # 参数d_model 线性层输入特征尺寸大小
        # 参数vocab_size 线层输出尺寸大小
        super(Generator, self).__init__()
        # 定义线性层
        self.project = nn.Linear(d_model, vocab_size)

    def forward(self, x):
        # 数据经过线性层 最后一个维度归一化 log方式
        x = F.log_softmax(self.project(x), dim=-1)
        return x
  • nn.Linear演示:
ini 复制代码
>>> m = nn.Linear(20, 30)
>>> input = torch.randn(128, 20)
>>> output = m(input)
>>> print(output.size())
torch.Size([128, 30])

6. 完整Transformer模型

将编码器和解码器组合成完整的Transformer模型:

完整的Transformer模型可以表示为:

<math xmlns="http://www.w3.org/1998/Math/MathML"> T r a n s f o r m e r ( X , Y ) = D e c o d e r ( E n c o d e r ( X ) , Y ) \mathrm{Transformer}(X, Y) = \mathrm{Decoder}(\mathrm{Encoder}(X), Y) </math>Transformer(X,Y)=Decoder(Encoder(X),Y)

python 复制代码
class MyTransform(nn.Module):
    """
    Transformer模型实现
    包含编码器和解码器两部分
    """
    def __init__(self, heads, embed_dim, feed_hidden_size, dropout_p=0.1):
        """
        初始化Transformer模型
        
        Args:
            heads: 注意力头数
            embed_dim: 嵌入维度
            feed_hidden_size: 前馈网络隐藏层大小
            dropout_p: dropout概率
        """
        super().__init__()

        # 创建编码器层模板
        encoder_layer = EncoderLayer(heads=heads, embed_dim=embed_dim,
                                     hidden_size=feed_hidden_size, dropout_p=dropout_p)
        # 创建包含6层的编码器
        self.encoder = Encoder(layer=encoder_layer, N=6, embed_dim=embed_dim)

        # 创建解码器层模板
        decoder_layer = DecoderLayer(heads=heads, embed_dim=embed_dim,
                                     feed_forward_hidden_size=feed_hidden_size, dropout_p=dropout_p)

        # 创建包含6层的解码器
        self.decoder = Decoder(decoder_layer=decoder_layer, n=6, embed_dim=embed_dim)

    def forward(self, x, y, target_mask, source_mask):
        """
        前向传播
        
        Args:
            x: 源序列输入 [batch_size, src_seq_len, embed_dim]
            y: 目标序列输入 [batch_size, tgt_seq_len, embed_dim]
            target_mask: 目标序列掩码(防止未来信息泄露)
            source_mask: 源序列掩码
        
        Returns:
            torch.Tensor: 解码器输出 [batch_size, tgt_seq_len, embed_dim]
        """
        # 编码器处理源序列
        encoder_output = self.encoder(x, source_mask)

        # 解码器处理目标序列,使用编码器输出作为键和值
        decoder_output = self.decoder(y, encoder_output, encoder_output, target_mask)

        return decoder_output

7. 公式与代码实现对照详解

为了更好地理解Transformer中各公式的实现,我们在此详细对照每个重要公式与其对应的代码实现。

7.1 注意力机制公式对照

注意力机制的核心公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V </math>Attention(Q,K,V)=softmax(dk QKT)V

对应的代码实现:

python 复制代码
def attention(q, k, v, mask=None, dropout=None):
    # 获取维度
    embed_dim = k.size(-1)
    
    # 计算 QK^T
    q_k = torch.matmul(q, k.transpose(-1,-2))
    
    # 缩放操作:除以 sqrt(d_k)
    attn_weight = q_k / math.sqrt(embed_dim)
    
    # 应用掩码(如果存在)
    if mask is not None:
        attn_weight = attn_weight.masked_fill(mask == 0, -1e9)
    
    # softmax归一化
    attn_weight = nn.functional.softmax(attn_weight, dim=-1)
    
    # 应用dropout(如果存在)
    if dropout is not None:
        attn_weight = dropout(attn_weight)
    
    # 乘以 V
    res = torch.matmul(attn_weight , v)
    
    return res, attn_weight

7.2 多头注意力公式对照

多头注意力机制的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> MultiHead ( Q , K , V ) = Concat ( head 1 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O </math>MultiHead(Q,K,V)=Concat(head1,...,headh)WO

<math xmlns="http://www.w3.org/1998/Math/MathML"> where h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) \text{where} \quad \mathrm{head_i} = \mathrm{Attention}(QW_i^Q, KW_i^K, VW_i^V) </math>whereheadi=Attention(QWiQ,KWiK,VWiV)

对应的代码实现(MultiHeadAttention类的forward方法):

python 复制代码
def forward(self, q, k, v, mask=None):
    if mask is not None:
        # 扩展mask的维度以匹配多头
        mask = mask.unsqueeze(0)
    
    batch_size = q.size(0)
    heads = self.heads
    embed_dim = q.size(-1)
    d_k = self.d_k
    
    # 对Q、K、V分别应用线性变换,然后reshape为(batch_size, heads, seq_len, d_k)
    q, k, v = [linear(x)
                .view(batch_size, -1, heads, d_k)
                .transpose(1,2)
                for linear,x in zip(self.linears,(q,k,v))]

    # 获取注意力机制输出
    output, attn_weight = attention(q, k, v, mask, self.dropout)
    
    # 合并多头到一个量中
    # 交换维度并合并heads和d_k维度
    res = output.transpose(1,2).contiguous().view(batch_size, -1, embed_dim)
    
    # 最后使用线性层做收尾处理
    return self.linears[-1](res)

7.3 位置编码公式对照

位置编码的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i)=sin(100002i/dmodelpos)

<math xmlns="http://www.w3.org/1998/Math/MathML"> P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d model ) PE_{(pos,2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) </math>PE(pos,2i+1)=cos(100002i/dmodelpos)

对应的代码实现(PositionalEmbedding类的__init__方法):

python 复制代码
def __init__(self, max_length, embed_dim, dropout_p=0.1):
    super().__init__()
    self.max_length = max_length
    self.embed_dim = embed_dim

    # Dropout层用于防止过拟合
    self.dropout = nn.Dropout(p=dropout_p)

    # 创建位置编码矩阵 [max_length, embed_dim]
    pe = torch.zeros(max_length, embed_dim)
    
    # 生成位置信息 [max_length, 1]
    pos = torch.arange(0, max_length).unsqueeze(1)
    
    # 生成维度索引 0, 2, 4, ..., embed_dim-2 (只取偶数索引)
    tow_i = torch.arange(0, embed_dim, 2)
    
    # 计算角度频率参数
    tmp_v = torch.exp((math.log(10000) * -tow_i / self.embed_dim))

    # 计算角度值:pos * 角度频率
    fn_v = tmp_v * pos

    # 偶数位置使用sin编码
    pe[:, 0::2] = torch.sin(fn_v)
    # 奇数位置使用cos编码
    pe[:, 1::2] = torch.cos(fn_v)

    # 增加batch维度 [1, max_length, embed_dim]
    pe = pe.unsqueeze(0)

    # 注册为buffer,不会被更新,但会随模型保存和加载
    self.register_buffer('pe', pe)

7.4 层归一化公式对照

层归一化的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> μ = 1 H ∑ i = 1 H x i \mu = \frac{1}{H}\sum_{i=1}^{H} x_i </math>μ=H1∑i=1Hxi

<math xmlns="http://www.w3.org/1998/Math/MathML"> σ 2 = 1 H ∑ i = 1 H ( x i − μ ) 2 \sigma^2 = \frac{1}{H}\sum_{i=1}^{H} (x_i - \mu)^2 </math>σ2=H1∑i=1H(xi−μ)2

<math xmlns="http://www.w3.org/1998/Math/MathML"> y = x − μ σ 2 + ϵ ⊙ γ + β y = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta </math>y=σ2+ϵ x−μ⊙γ+β

对应的代码实现(LayerNorm类的forward方法):

python 复制代码
def forward(self, x):
    # 计算平均数
    x_mean = torch.mean(x, keepdim=True, dim=-1)
    
    # 计算标准差
    x_std = torch.std(x, keepdim=True, dim=-1)
    
    # 计算 (x-平均数) / (标准差+eps) = 标准化后的x
    res = (x - x_mean) / (x_std + self.eps)
    
    # 返回 W * x + b,应用可学习的缩放和偏移
    return self.w * res + self.b

7.5 前馈网络公式对照

前馈网络的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> F F N ( x ) = max ⁡ ( 0 , x W 1 + b 1 ) W 2 + b 2 \mathrm{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 </math>FFN(x)=max(0,xW1+b1)W2+b2

对应的代码实现(FeedForward类的forward方法):

python 复制代码
def forward(self, x):
    # 第一层线性变换: xW_1 + b_1
    x = self.linear1(x)

    # ReLU激活: max(0, xW_1 + b_1)
    x = self.relu(x)

    # Dropout防止过拟合
    x = self.dropout(x)

    # 第二层线性变换: max(0, xW_1 + b_1)W_2 + b_2
    x = self.linear2(x)

    return x

7.6 子层连接公式对照

子层连接的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> S u b l a y e r C o n n e c t i o n ( x , S u b l a y e r ) = L a y e r N o r m ( x + D r o p o u t ( S u b l a y e r ( x ) ) ) \mathrm{SublayerConnection}(x, \mathrm{Sublayer}) = \mathrm{LayerNorm}(x + \mathrm{Dropout}(\mathrm{Sublayer}(x))) </math>SublayerConnection(x,Sublayer)=LayerNorm(x+Dropout(Sublayer(x)))

对应的代码实现(SubLayers类的forward方法):

python 复制代码
def forward(self, x, layer_fn):
    # 对输入应用指定的层函数,然后进行层归一化和dropout
    # 最后与原始输入进行残差连接(x + res)
    res  = self.dropout(self.norm(layer_fn(x)))
    return x + res

7.7 编码器层公式对照

编码器层的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> E n c o d e r L a y e r ( x ) = S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( x , S e l f A t t e n t i o n ) , F F N ) \mathrm{EncoderLayer}(x) = \mathrm{SublayerConnection}(\mathrm{SublayerConnection}(x, \mathrm{SelfAttention}), \mathrm{FFN}) </math>EncoderLayer(x)=SublayerConnection(SublayerConnection(x,SelfAttention),FFN)

对应的代码实现(EncoderLayer类的forward方法):

python 复制代码
def forward(self, x, mask=None):
    # 第一层:自注意力机制
    x = self.layers[0](x, lambda x: self.multi_head_attn(x, x, x, mask))

    # 第二层:前馈神经网络
    x = self.layers[1](x, self.feed_ward)
    
    return x

7.8 解码器层公式对照

解码器层的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> D e c o d e r L a y e r ( x , e n c o u t ) = S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( S u b l a y e r C o n n e c t i o n ( x , M a s k e d S e l f A t t e n t i o n ) , E n c o d e r A t t e n t i o n ) , F F N ) \mathrm{DecoderLayer}(x, enc_out) = \mathrm{SublayerConnection}(\mathrm{SublayerConnection}(\mathrm{SublayerConnection}(x, \mathrm{MaskedSelfAttention}), \mathrm{EncoderAttention}), \mathrm{FFN}) </math>DecoderLayer(x,encout)=SublayerConnection(SublayerConnection(SublayerConnection(x,MaskedSelfAttention),EncoderAttention),FFN)

对应的代码实现(DecoderLayer类的forward方法):

python 复制代码
def forward(self, q, k, v, target_mask=None, source_mask=None):
    # 第一层:目标序列的自注意力(使用target_mask防止看到未来信息)
    res_1 = self.layers[0](q, lambda x: self.multi_head_attn(x, x, x, target_mask))

    # 第二层:编码器-解码器注意力(查询来自前一层,键值来自编码器输出)
    res_2 = self.layers[1](res_1, lambda x: self.multi_head_attn(x, k, v, source_mask))

    # 第三层:前馈神经网络
    res_3 = self.layers[2](res_2, self.feed_forward)

    return res_3

7.9 完整Transformer模型公式对照

完整Transformer模型的公式:

<math xmlns="http://www.w3.org/1998/Math/MathML"> T r a n s f o r m e r ( X , Y ) = D e c o d e r ( E n c o d e r ( X ) , Y ) \mathrm{Transformer}(X, Y) = \mathrm{Decoder}(\mathrm{Encoder}(X), Y) </math>Transformer(X,Y)=Decoder(Encoder(X),Y)

对应的代码实现(MyTransform类的forward方法):

python 复制代码
def forward(self, x, y, target_mask, source_mask):
    # 编码器处理源序列
    encoder_output = self.encoder(x, source_mask)

    # 解码器处理目标序列,使用编码器输出作为键和值
    decoder_output = self.decoder(y, encoder_output, encoder_output, target_mask)

    return decoder_output

8. 辅助函数

一些辅助函数帮助构建模型:

python 复制代码
def clone_modules(module, N):
    """
    克隆N个相同的模块
    
    Args:
        module: 要克隆的模块
        N: 克隆的数量
    
    Returns:
        nn.ModuleList: 包含N个相同模块的列表
    """
    return nn.ModuleList(copy.deepcopy(module) for _ in range(N))

def get_mask(size):
    """
    生成上三角掩码矩阵,用于防止未来信息泄露(在解码器中使用)
    
    Args:
        size: 掩码矩阵的尺寸 (heads, seq_len, seq_len)
    
    Returns:
        torch.Tensor: 上三角为0,下三角和对角线为1的掩码矩阵
    """
    tensor = torch.ones(size=size, dtype=torch.long)
    # triu生成上三角矩阵,k=1表示对角线上方的元素为1,其余为0
    # 1-操作将其反转,使对角线和下三角为1,上三角为0
    return 1-torch.triu(tensor, 1)

9. 测试代码

测试模型各部分功能:

python 复制代码
def test_transform():
    """
    测试完整的Transformer模型
    """
    # 占位符,无实际作用
    pass

    # 创建测试源序列数据
    x = torch.tensor([
        [1, 2, 3, 4],
        [2, 4, 5, 6]
    ])
    embed_dim = 512
    # 创建嵌入序列模块
    embedding = EmbeddingSequential(1000, 512, 60)
    # 获取源序列嵌入表示
    embedding_x = embedding(x)
    print('EmbeddingSequential', embedding_x.shape)
    heads = 8
    # 生成源序列掩码
    source_mask = get_mask(size=(heads, x.size(-1), x.size(-1)))
    dropout_p = 0.1
    hidden_size = 2048

    # 创建目标序列数据
    y = torch.tensor([
        [1, 2, 3, 4, 5, 6],
        [2, 3, 4, 5, 6, 7]
    ])

    # 深拷贝嵌入模块处理目标序列
    embed_y = copy.deepcopy(embedding)(y)

    print('embed_y', embed_y)
    # 生成目标序列掩码
    target_mask = get_mask(size=(heads, y.size(-1), y.size(-1)))
    # 创建Transformer模型
    transformer = MyTransform(heads=heads, embed_dim=embed_dim, feed_hidden_size=hidden_size, dropout_p=dropout_p)

    # Transformer前向传播
    decoder_output = transformer(embedding_x, embed_y, target_mask, source_mask)

    # 创建生成器
    generator = Generator(embed_dim, output_size=2000)

    # 生成预测结果
    pre_v = generator(decoder_output)

    print('pre_v', pre_v)

10. 项目源码

本项目完整源码已开源,您可以在以下GitHub仓库中获取:

github.com/KobeCYL/tra...

源码包含了完整的Transformer实现,以及详细的中文注释,方便学习和理解。项目结构如下:

bash 复制代码
transformer-implementation/
├── common.py              # 公共组件:注意力机制、多头注意力、层归一化等
├── encoder_module.py      # 编码器模块实现
├── decoder_module.py      # 解码器模块实现
├── embeddings.py          # 词嵌入和位置编码实现
├── generator.py           # 输出生成器
├── transform.py           # Transformer模型主体
└── test.py               # 测试代码

您可以通过以下方式使用该项目:

bash 复制代码
git clone https://github.com/KobeCYL/transformer-implementation.git
cd transformer-implementation
python test.py

欢迎提交Issue和Pull Request,共同完善这个教学用途的Transformer实现。

11. 总结

通过手写Transformer的实现,我们可以更深入地理解其内部工作机制:

  1. 注意力机制:核心组件,允许模型关注输入序列的不同部分
  2. 多头注意力:并行计算多个注意力表示,增强模型的表达能力
  3. 位置编码:为模型提供序列顺序信息
  4. 残差连接和层归一化:稳定训练过程,加速收敛
  5. 前馈网络:对每个位置的表示进行非线性变换

Transformer的这些设计使其能够并行处理序列数据,有效捕获长距离依赖关系,成为现代NLP模型的基础架构。通过手动实现这些组件,我们不仅加深了对Transformer架构的理解,也为后续的模型改进和优化奠定了基础。

12. 关注我们

如果你觉得这篇文章对你有帮助,欢迎:

  • 点赞支持:如果内容对你有帮助,请不要吝啬你的赞👍
  • 分享传播:将文章分享给更多需要的朋友,让知识传递更远
  • 关注作者:关注我的GitHub和博客,获取更多深度学习和自然语言处理的干货内容
  • 评论交流:在评论区留下你的想法和问题,我们一起讨论学习

更多关于Transformer、BERT、GPT等前沿NLP技术的深度解析,敬请关注!

让我们一起在AI的道路上不断前行,探索更多技术的奥秘!🚀

相关推荐
小马过河R1 天前
小白沉浸式本地Mac小龙虾OpenClaw部署安装教程
人工智能·macos·大模型·nlp·agent·openclaw·龙虾
华农DrLai2 天前
什么是Prompt注入攻击?为什么恶意输入能操控AI行为?
人工智能·深度学习·大模型·nlp·prompt
华农DrLai2 天前
什么是Prompt模板?为什么标准化的格式能提高稳定性?
数据库·人工智能·gpt·nlp·prompt
华农DrLai2 天前
什么是自动Prompt优化?为什么需要算法来寻找最佳提示词?
人工智能·算法·llm·nlp·prompt·llama
华农DrLai3 天前
什么是Prompt工程?为什么提示词的质量决定AI输出的好坏?
数据库·人工智能·gpt·大模型·nlp·prompt
热爱生活的猴子3 天前
RoBERTa 分类模型正则化调优实验——即dropout和冻结层对过拟合的影响
人工智能·深度学习·分类·数据挖掘·nlp
数据智能老司机4 天前
精通 Hugging Face 自然语言处理——深度 Q 网络与 Atari 游戏
nlp
数据智能老司机4 天前
精通 Hugging Face 自然语言处理——Hugging Face Diffusers 中的调度器
nlp
数据智能老司机4 天前
精通 Hugging Face 自然语言处理——Hugging Face Diffusers 库导论
nlp
Learn Beyond Limits5 天前
循环神经网络的问题:梯度消失与梯度爆炸|Problems with RNNs: Vanishing and Exploding Gradients
人工智能·rnn·深度学习·神经网络·机器学习·自然语言处理·nlp