Attention Is All You Need(Transformer)详解以及代码复现

一.背景。

在此模型之前,序列到序列的任务(如机器翻译、文本摘要等)通常采用循环神经网络(RNN)或卷积神经网络(CNN)。然而,RNN 在处理长距离依赖时存在一定的局限性(举个例子:处理第Kt个词时,需要用到K1到Kt-1的词的输出作为输入),训练时也比较耗时。而 CNN 在处理序列数据时难以捕捉到全局的依赖关系。然而这篇文章介绍的模型Transformer完全基于注意力机制,与CNN,RNN,LSTM模型对比更加简单并且高效。

二.模型架构。

Transformer 模型采用了编码器-解码器架构。先上一个论文里面的架构图,再逐步介绍其中的各个部分。
在这里插入图片描述

1.Embedding

在这里插入图片描述

Embedding是什么:

为了对字符进行计算,我们首先需要将字符(或单词)转换成一种数值表示形式。独热编码(One-Hot Encoding)是一种常用的方法之一,例如词汇表 {'猫': 0, '狗': 1, '苹果': 2} (此处的索引012一般是根据某个词典获得,即某词典0号索引处为单词‘猫’),‘ 猫’ 的独热编码就是 [1, 0, 0] ,‘狗’ 的独热编码就是 [0, 1, 0],但是这样的缺点就是向量维度高且稀疏,计算效率低。如果词汇表有 10,000 个单词,那么每个独热向量的维度就是 10,000,并且是稀疏的,即大部分元素都是 0,只有一个位置是 1。
所以在深度学习特别是自然语言处理(NLP)中,我们通常会采用更加高效的嵌入表示(Embedding)。通过Embedding,每个字符(或单词)被表示为一个低维的密集向量。这些向量是通过训练得到的,可以捕捉字符(或单词)之间的语义关系。例如通过嵌入层,单词“猫”可能被表示为一个 5 维(维数由我们定义)向量 [0.0376, -0.2343, 0.1655, -0.0053, 0.1353] 。可见其特点是维度低且密集,计算效率高,并且能够捕捉语义信息。

Embedding的例子:

在 PyTorch 中,有一个函数torch.nn.Embedding(num_embedings, embedding_dim),其中 num_embedding 表示词表总的长度,embedding_dim 表示单词嵌入的维度,此函数会创建一个嵌入矩阵(通常是随机的),其形状为 (num_embedding , embedding_dim),给定输入张量(通常是单词索引),其形状为(batch_size, sequence_length),该层会将每个索引映射到对应的嵌入向量,返回一个形状为 (batch_size, sequence_length, embedding_dim) 的张量。
以下是例子代码:

import torch
import torch.nn as nn

# 定义词汇表大小和嵌入向量维度
vocab_size = 10 #词汇表大小
embedding_dim = 5 #向量维度

# 创建嵌入层
embedding_layer = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)

# 输入张量(单词索引)
input_tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.long)  # 示例输入,形状为 (batch_size, sequence_length)

# 通过嵌入层得到嵌入向量
embedding_output = embedding_layer(input_tensor)

print(f"Input Tensor Shape: {
     
     input_tensor.shape}")
print(f"Embedding Output Shape: {
     
     embedding_output.shape}")
print(f"Embedding Output:\n{
     
     embedding_output}")
'''
输出:
Input Tensor Shape: torch.Size([2, 3])
Embedding Output Shape: torch.Size([2, 3, 5])
Embedding Output:
tensor([[[ 0.0069,  0.0465, -0.0205,  0.0080, -0.0114],
         [-0.0244,  0.0404,  0.0452, -0.0027, -0.0307],
         [ 0.0024, -0.0043,  0.0340,  0.0370, -0.0400]],

        [[ 0.0057, -0.0015, -0.0154, -0.0306, -0.0375],
         [ 0.0317, -0.0275,  0.0160,  0.0283,  0.0040],
         [-0.0331, -0.0061,  0.0452,  0.0484, -0.0350]]], grad_fn=<EmbeddingBackward0>)
'''

Transformer中的Embedding

论文原文:(在嵌入层中,我们将这些权重乘以√dmodel)
在这里插入图片描述

在Embedding中使用 math.sqrt(self.d_model)(即 d \sqrt[]{d} d ) 进行缩放,是在实际实现中的一种实践,可以保持数值稳定性,确保在随后的计算中,尤其是在与模型其他部分进行交互时,不会出现数值过大或过小的问题。

  • 数学解释:
    在这里插入图片描述
    以下是复现代码:(这里暂定input跟output的Embedding是一样的)
class Embedding(nn.Module):
    def __init__(self, vocab_size, d_model):
        # vocab_size:词表长度    d_model:嵌入维度
        super(Embedding, self).__init__()
        self.embedding = nn.Embedding(vocab_size, d_model)
        self.d_model = d_model

    def forward(self, x):
        # x:输入张量
        # 乘以 根号dk 保持数据稳定性 
        return self.embedding(x) * math.sqrt(self.d_model)

2.Positional Encoding

在这里插入图片描述

为什么需要Positional Encoding

论文原文:(由于我们的模型不包含递归和卷积,为了使模型利用序列的顺序,我们必须注入一些关于序列中标记的相对或绝对位置的信息)
在这里插入图片描述
也就是attention没有时序信息,需要我们自己加入。(RNN的做法是上一个时刻的输出作为此时刻的输入以此引入时序信息)

Positional Encoding的实现

论文原文:
在这里插入图片描述

其中,pos 即 position,意为 token 在句中的位置,i为向量的某一维度。借助此公式再结合三角函数的性质
在这里插入图片描述
可以得到:
在这里插入图片描述
可以看出,对于 pos+k 位置的位置向量某一维 2i 或 2i+1 而言,可以表示为,pos 位置与k位置的位置向量的2i与 2i+1维的线性组合,这样的线性组合意味着位置向量中蕴含了相对位置信息。具体可以参考视频讲解
以下是复现代码:

class PositionalEncoding(nn.Module):
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        '''初始化函数,三个参数分别是:
            d_model:词嵌入维度; dropout:置0比率(位置编码与输入嵌入相加后一起作为模型的输入。模型在学习过程中会学习如何利用这些位置信息。如果位置编码没有经过 dropout 的正则化处理,模型可能会过度依赖这些位置信息,从而对训练数据记忆过深,导致在处理未见数据时表现不佳。)
            max_len:每个句子的最大长度。
        '''
        super(PositionalEncoding, self).__init__()

        #实例化dropout层,并传入参数
        self.dropout = nn.Dropout(p=dropout)
        #初始化一个位置编码矩阵,全为0,大小是max_len * d_model
        pe = torch.zeros(max_len, d_model)
        #初始化一个绝对位置矩阵,词的绝对位置即索引位置
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)

        #接下来就是把位置信息加入到位置编码矩阵中去,也就是把max_len * 1的position绝对位置矩阵变换成max_len * d_model形状,然后覆盖初始矩阵
        #也就是max_len * 1 的矩阵去乘以一个 1 * d_modl 的变换矩阵div_term,然后再进行覆盖,这里因为位置编码可以分成奇数和偶数两部分,故可以将变换矩阵更改为 1 * (d_model / 2)的形状
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))

        #按照公式给位置编码进行赋值
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)

        #这样子就得到了位置编码矩阵pe,但是要和embedding的输出相加就必须拓展一个维度
        pe = pe.unsqueeze(0)

        #因为无论我们输入的是什么,这个位置编码都不会改变,也就是所有的输入是公用一个位置编码的,所以这边使用self.register_buffer,其是一个用于将张量注册为模型的一部分的方法。它的主要用途是注册一些不作为模型参数的持久状态,例如在训练和推理过程中不需要更新的固定数据。
        self.register_buffer('pe', pe)

    def forward(self,x):
        #因为一个句子有长有短,所以可以位置编码只截取到句子的实际长度即可。
        x = x + self.pe[:, :x.size(1)]
        #最后使用dropout防止过拟合,并返回结果。
        return self.dropout(x)
实际例子

通过一个超参数比较小的例子输出并展示还是比较容易理解每一步骤的做法的。

import torch
import torch.nn as nn
import math

max_len = 10
d_model = 6

# 初始化位置编码矩阵
pe = torch.zeros(max_len, d_model)
print
<think>嗯,用户让我介绍Transformer技术,并且希望以DeepSeek博士的身份带他入门。首先,我需要回顾一下Transformer的基本概念和结构,确保自己理解正确。Transformer是2017年由Vaswani等人在“Attention Is All You Need论文中提出的,主要用于自然语言处理任务,但现在应用范围更广了,比如计算机视觉。 用户可能已经了解过RNN或CNN,但想转向Transformer,所以需要解释为什么Transformer比之前的模型更好。比如,并行计算能力和长距离依赖处理。然后,我需要分解Transformer的核心组件:自注意力机制、位置编码、前馈网络、编码器和解码器结构。 自注意力机制是重点,得解释清楚Q、K、V矩阵的计算,以及如何通过缩放点积注意力得到权重。可能需要用简单的例子说明,比如句子中单词之间的关系。然后,多头注意力,为什么需要多个头,不同的注意力头可以捕捉不同方面的信息。 位置编码部分,因为Transformer没有递归或卷积结构,必须显式地加入位置信息。要解释正弦和余弦函数的位置编码方法,以及现在可能有的变体,比如可学习的位置编码。 编码器和解码器的结构差异,比如解码器有掩码自注意力,防止看到未来信息。每层的组成:自注意力、前馈网络、残差连接和层归一化。残差连接有助于梯度流动,层归一化稳定训练。 还需要提到训练细节,比如优化器、学习率预热,以及词嵌入和softmax前的线性变换共享权重。这些可能对实际应用有帮助。 用户可能需要知道应用场景,比如BERT、GPT都是基于Transformer,所以可以举这些例子。另外,Transformer不仅用于NLP,还可以用于CV,比如Vision Transformer。 可能的问题:自注意力的计算复杂度高,如何优化?比如稀疏注意力、局部窗口注意力等。或者Transformer训练需要大量数据,怎么处理小数据集的情况? 最后,确保语言通俗易懂,避免过多数学公式,但必要的公式(如softmax和点积)需要解释清楚。可能需要用比喻或示意图辅助理解,不过在文本中用文字描述。 需要检查是否有遗漏的部分,比如Transformer的变体或最新发展,但入门的话可能不需要太深入。总结步骤应该是先介绍背景和优势,再拆解结构,解释关键组件,最后应用和扩展。确保用户能跟随逻辑,逐步理解。</think>好的,我将带你逐步理解Transformer技术,尽量用通俗的语言和逻辑拆解核心概念。以下是入门指南: --- ### 一、Transformer解决了什么问题? **传统RNN/LSTM的缺陷**: - 无法高效并行计算(必须按时间步顺序处理) - 长距离依赖捕捉困难(信息传递会衰减) - 处理长文本时计算复杂度高 **Transformer的突破**: 1. **完全基于注意力机制**:抛弃循环结构 2. **并行计算**:所有位置同时处理 3. **全局依赖建模**:任意两个位置的直接关联 --- ### 二、Transformer核心结构(图示联想) 想象一个由编码器(左)和解码器(右)组成的机器: - **编码器**:理解输入内容(如将句子转化为特征) - **解码器**:生成输出内容(如翻译结果) ![Transformer结构简图](https://miro.medium.com/v2/resize:fit:720/format:webp/1*JZ2J6W0h3X4S2l4pD5oJNg.png) --- ### 三、五大核心组件详解 #### 1. 自注意力机制(Self-Attention) **作用**:让每个词与其他词直接“对话”,找出关联强度 **过程**: 1. 对每个词生成3个向量:Query(查询)、Key(键)、Value(值) 2. 计算词A对词B的注意力分数:`Q_A · K_B` 3. 缩放并Softmax归一化得到权重 4. 加权求和Value得到输出 **公式简化**: \[ \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V \] #### 2. 多头注意力(Multi-Head Attention) - 并行运行多个自注意力机制 - 每个“头”学习不同的关注模式(如语法/语义) - 结果拼接后通过线性层融合 #### 3. 位置编码(Positional Encoding) - 问题:自注意力本身不考虑词序 - 解决方案:给每个词添加位置信号 - 方法:使用正弦/余弦函数生成位置向量,与词向量相加 #### 4. 前馈网络(Feed Forward Network) - 每个位置独立通过全连接层 - 常用两层线性变换+激活函数(如ReLU) #### 5. 残差连接与层归一化 - 残差连接:输入+输出,缓解梯度消失 - 层归一化(LayerNorm):稳定训练过程 --- ### 四、编码器-解码器工作流程 1. **编码器**(N个相同层): - 输入:源序列 + 位置编码 - 每层:多头自注意力 → 前馈网络 - 输出:上下文感知的特征表示 2. **解码器**(N个相同层): - 输入:目标序列(右移) + 位置编码 - 核心变化: - **掩码自注意力**:防止看到未来信息 - **交叉注意力**:连接编码器输出 --- ### 五、关键代码片段(PyTorch伪代码) ```python # 自注意力计算 def self_attention(query, key, value, mask=None): scores = torch.matmul(query, key.transpose(-2, -1)) / sqrt(d_k) if mask is not None: scores = scores.masked_fill(mask == 0, -1e9) weights = F.softmax(scores, dim=-1) return torch.matmul(weights, value) # Transformer块 class TransformerBlock(nn.Module): def __init__(self): self.attention = MultiHeadAttention() self.norm1 = LayerNorm() self.ffn = PositionwiseFFN() self.norm2 = LayerNorm() def forward(self, x): attn_out = self.attention(x) x = self.norm1(x + attn_out) # 残差连接+归一化 ffn_out = self.ffn(x) return self.norm2(x + ffn_out) ``` --- ### 六、Transformer的现代变体 1. **BERT**:仅用编码器的双向预训练模型 2. **GPT**:仅用解码器的自回归生成模型 3. **Vision Transformer**:将图像分块处理 4. **高效改进**:Linformer(低秩近似)、Reformer(局部敏感哈希) --- ### 七、入门学习路径建议 1. **基础理论**: - 精读原始论文Attention Is All You Need》 - 理解矩阵维度变化(推荐Colab可视化教程) 2. **代码实践**: - 复现简单Transformer(字符级语言模型) - 使用HuggingFace库快速调用预训练模型 3. **延伸应用**: - 尝试机器翻译(Fairseq/OpenNMT) - 微调BERT进行文本分类 需要更深入的某个部分讲解,或具体代码示例,可以随时告诉我! 🚀
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值