深度学习论文: Attention is All You Need及其PyTorch实现

news2024/9/23 1:40:57

深度学习论文: Attention is All You Need及其PyTorch实现
Attention is All You Need
PDF:https://arxiv.org/abs/1706.03762.pdf
PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks

大多数先进的神经序列转换模型采用编码器-解码器结构,其中编码器将输入符号序列转换为连续表示,解码器则基于这些表示逐个生成输出符号序列。在每个步骤中,模型采用自回归方式,将先前生成的符号作为额外输入来生成下一个符号。
在这里插入图片描述

1 Encoder and Decoder Stacks

1-1 Encoder 编码器

编码器采用N=6个结构相同的层堆叠而成。每一层包含两个子层,第一个是多头自注意力机制,第二个则是简单的位置全连接前馈网络。为提升性能,在每个子层之间引入了残差连接,并实施了层归一化。具体来说,每个子层的输出经过LayerNorm(x + Sublayer(x))计算得出,其中Sublayer(x)代表子层自身的功能实现。为了确保残差连接的顺畅进行,编码器中的所有子层以及嵌入层均生成维度为dmodel=512的输出。

def clones(module, N):
    "Produce N identical layers."
    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

class LayerNorm(nn.Module):
    "Construct a layernorm module (See citation for details)."
    def __init__(self, features, eps=1e-6):
        super(LayerNorm, self).__init__()
        self.a_2 = nn.Parameter(torch.ones(features))
        self.b_2 = nn.Parameter(torch.zeros(features))
        self.eps = eps

    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a_2 * (x - mean) / (std + self.eps) + self.b_2
        
class Encoder(nn.Module):
    "Core encoder is a stack of N layers"
    def __init__(self, layer, N):
        super(Encoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, mask):
        "Pass the input (and mask) through each layer in turn."
        for layer in self.layers:
            x = layer(x, mask)
        return self.norm(x)


class SublayerConnection(nn.Module):
    """
    A residual connection followed by a layer norm.
    Note for code simplicity the norm is first as opposed to last.
    """
    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        "Apply residual connection to any sublayer with the same size."
        return x + self.dropout(sublayer(self.norm(x)))


class EncoderLayer(nn.Module):
    "Encoder is made up of self-attn and feed forward (defined below)"
    def __init__(self, size, self_attn, feed_forward, dropout):
        super(EncoderLayer, self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 2)
        self.size = size

    def forward(self, x, mask):
        "Follow Figure 1 (left) for connections."
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))
        return self.sublayer[1](x, self.feed_forward)

1-2 Decoder解码器

解码器同样由N=6个结构相同的层堆叠而成。除了编码器层中的两个子层,解码器还额外引入了一个第三子层,专门用于对编码器堆叠的输出执行多头注意力机制。与编码器设计相似,解码器中的每个子层也采用残差连接与层归一化。此外,还对解码器堆叠中的自注意力子层进行了优化,确保在生成输出时,位置i的预测仅依赖于位置小于i的已知输出,这通过掩码和输出嵌入偏移一个位置的方式实现。

class Decoder(nn.Module):
    "Generic N layer decoder with masking."
    def __init__(self, layer, N):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = LayerNorm(layer.size)
        
    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        return self.norm(x)
        
class DecoderLayer(nn.Module):
    "Decoder is made of self-attn, src-attn, and feed forward (defined below)"
    def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
        super(DecoderLayer, self).__init__()
        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 3)
 
    def forward(self, x, memory, src_mask, tgt_mask):
        "Follow Figure 1 (right) for connections."
        m = memory
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))
        return self.sublayer[2](x, self.feed_forward)

2 Attention

在这里插入图片描述
Transformer巧妙地运用了三种不同的多头注意力机制:

  • 在“编码器-解码器注意力”层中,查询源于前一解码器层,而键和值则汲取自编码器的输出。这种设计使得解码器中的任意位置都能关注输入序列的每一个细节,完美模拟了序列到序列模型中典型的编码器-解码器注意力机制。

  • 编码器内部则嵌入了自注意力层。在这个自注意力层中,键、值和查询均来自编码器前一层的输出,确保编码器中的每个位置都能全面捕捉到前一层中的信息。

  • 解码器同样采用了自注意力层,允许解码器中的每个位置关注到包括该位置在内的解码器内部所有位置的信息。为了确保自回归属性的保持,我们精心设计了一个机制:在缩放点积注意力内部,将对应非法连接的softmax输入值设为−∞,从而有效屏蔽了向左的信息流动。

2-1 Scaled Dot-Product Attention

Scaled Dot-Product Attention 缩放点积注意力,输入包括维度为 d k d_{k} dk的查询和键,以及维度为 d v d_{v} dv的值。计算查询与所有键的点积,每个都除以 d k \sqrt{d_{k} } dk ,然后应用softmax函数以获得值的权重。
在这里插入图片描述

def attention(query, key, value, mask=None, dropout=None):
    "Compute 'Scaled Dot Product Attention'"
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-2, -1)) \
             / math.sqrt(d_k)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)
    p_attn = F.softmax(scores, dim = -1)
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn

2-2 Multi-Head Attention

与其仅使用具有dmodel维度的键、值和查询来执行单一的注意力函数,将查询、键和值分别通过不同的、学习得到的线性投影,各自线性投影 h h h次,分别映射到 d k d_{k} dk d k d_{k} dk d v d_{v} dv维度后效果更好。随后在这些投影后的查询、键和值上并行地执行注意力函数,从而得到 d v d_{v} dv维度的输出值。最后,我们将这些输出值进行拼接,并再次通过投影得到最终的值。这种方法能够更充分地利用输入信息,提升模型的性能。
在这里插入图片描述
在这项研究中,我们采用了h=8个并行的注意力层,也称之为注意力头。对于每一个注意力头,我们都设定其维度为 d k d_{k} dk = d v d_{v} dv = d m o d e l / h d_{model} / h dmodel/h=64。由于每个注意力头的维度有所降低,因此其整体计算成本与使用完整维度的单头注意力相近。

class MultiHeadedAttention(nn.Module):
    def __init__(self, h, d_model, dropout=0.1):
        "Take in model size and number of heads."
        super(MultiHeadedAttention, self).__init__()
        assert d_model % h == 0
        # We assume d_v always equals d_k
        self.d_k = d_model // h
        self.h = h
        self.linears = clones(nn.Linear(d_model, d_model), 4)
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
        
    def forward(self, query, key, value, mask=None):
        "Implements Figure 2"
        if mask is not None:
            # Same mask applied to all h heads.
            mask = mask.unsqueeze(1)
        nbatches = query.size(0)
        
        # 1) Do all the linear projections in batch from d_model => h x d_k 
        query, key, value = \
            [l(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
             for l, x in zip(self.linears, (query, key, value))]
        
        # 2) Apply attention on all the projected vectors in batch. 
        x, self.attn = attention(query, key, value, mask=mask, 
                                 dropout=self.dropout)
        
        # 3) "Concat" using a view and apply a final linear. 
        x = x.transpose(1, 2).contiguous() \
             .view(nbatches, -1, self.h * self.d_k)
        return self.linears[-1](x)

3 Position-wise Feed-Forward Networks

除了注意力子层,编码器和解码器的每一层还包括一个全连接前馈网络,它独立且相同地应用于每个位置,包含两个线性变换和一个ReLU激活函数。
在这里插入图片描述
这里的输入和输出的维度是 d m o d e l = 512 d_{model}=512 dmodel=512,而内层的维度是 d f f = 2048 d_{ff}=2048 dff=2048

class PositionwiseFeedForward(nn.Module):
    "Implements FFN equation."
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

4 Embeddings and Softmax

与其他序列转换模型相似,这里使用学习嵌入将输入标记和输出标记转换为 d m o d e l d_{model} dmodel维向量。解码器输出通过线性变换和softmax函数转换为预测概率。在我们的模型中,嵌入层和softmax前的线性变换共享权重矩阵,并在嵌入层中将权重乘以 d m o d e l \sqrt{d_{model}} dmodel

class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        super(Embeddings, self).__init__()
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model

    def forward(self, x):
        return self.lut(x) * math.sqrt(self.d_model)

5 Positional Encoding

在编码器和解码器堆栈底部的输入嵌入中添加“位置编码”。位置编码的维度 d m o d e l d_{model} dmodel与嵌入的维度相同,以便可以将两者相加。位置编码有很多选择,包括学习和固定的。

在这里,选用不同频率的正弦和余弦函数:
在这里插入图片描述
其中pos是位置,i是维度。也就是说,位置编码的每个维度都对应于一个正弦波。波长从2π到10000·2π形成几何级数。选择这个函数是因为它可以使模型容易学习通过相对位置进行关注,因为对于任何固定的偏移量k, P E p o s + k PE_{pos+k} PEpos+k都可以表示为 P E p o s PE_{pos} PEpos的线性函数。

此外,在编码器和解码器堆栈中都对嵌入和位置编码的和使用了Dropout。对于基础模型,使用的Dropout ratio 为 P d r o p P_{drop} Pdrop=0.1。

class PositionalEncoding(nn.Module):
    "Implement the PE function."
    def __init__(self, d_model, dropout, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        
        # Compute the positional encodings once in log space.
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(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)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)
        
    def forward(self, x):
        x = x + Variable(self.pe[:, :x.size(1)], 
                         requires_grad=False)
        return self.dropout(x)

参考资料:
1 Attention is All You Need
2 The Illustrated Transformer
3 The Annotated Transformer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1552553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣热门算法题 135. 分发糖果,146. LRU 缓存,148. 排序链表

135. 分发糖果,146. LRU 缓存,148. 排序链表,每题做详细思路梳理,配套Python&Java双语代码, 2024.03.28 可通过leetcode所有测试用例。 目录 135. 分发糖果 解题思路 完整代码 Python Java 146. LRU 缓存 …

Mybatis别名 动态sql语句 分页查询

给Mybatis的实体类起别名 给Mybatis的xml文件注册mapper映射文件 动态sql语句 1 if 2 choose 3 where 4 foreach 一&#xff09;if 查询指定名称商品信息 语法&#xff1a; SELECT * FROM goods where 11 <if test "gName!null"> and g.g_name like co…

|行业洞察·手机|《2024手机行业及营销趋势报告-18页》

报告的主要内容解读&#xff1a; 手机行业概述及品牌分布&#xff1a; 2022年&#xff0c;受疫情影响&#xff0c;中国国内手机市场出货量下降22.6%&#xff0c;总计2.72亿部。5G手机市场占有率中&#xff0c;苹果领先&#xff0c;其次是vivo、OPPO和华为。消费者换机时更注重性…

【MagicDrive环境配置】新手配俩星期版

1.创建一个新的环境conda create -n newdrive python3.8 2.激活该环境conda activate newdrive 3.下载MagicDrive源码 git clone --recursive https://github.com/cure-lab/MagicDrive.git&#xff0c;如果出现时间超时八成是网的问题&#xff0c;直接自己下载解压就好 3.我的…

【群智能算法改进】一种改进的同核分子优化算法 IHMO算法【Matlab代码#71】

文章目录 【获取资源请见文章第5节&#xff1a;资源获取】1. 原始HMO算法2. 改进HMO算法2.1 改进的距离减小因子2.2 黄金正弦策略扰动 3. 部分代码展示4. 仿真结果展示5. 资源获取说明 【获取资源请见文章第5节&#xff1a;资源获取】 1. 原始HMO算法 同核分子优化算法&#x…

HarmonyOS 应用开发之UIAbility组件生命周期

概述 当用户打开、切换和返回到对应应用时&#xff0c;应用中的UIAbility实例会在其生命周期的不同状态之间转换。UIAbility类提供了一系列回调&#xff0c;通过这些回调可以知道当前UIAbility实例的某个状态发生改变&#xff0c;会经过UIAbility实例的创建和销毁&#xff0c;…

网络原理-传输层-UDP报文结构

本文介绍UDP报文 有很多友友搞不清楚UDP报文的详细结构还有TCP的详细结构,所以专门分开来讲 以免弄混. 首先我们先看一下整个UDP结构,让大家有一个全方面的认识 下面我们来详细解释UDP报 16位源端口号(本机):就是2字节大小,16个二进制位. 16位目的端口号(目的机):也是2字节…

如何构建一台机器学习服务器

系统信息 系统安装 系统安装这里就不再赘述&#xff0c;推荐使用ventory作为PE盘&#xff0c;来安装系统&#xff0c;这样方便快捷&#xff0c;可同时包含多个镜像&#xff0c;无需重复制作&#xff0c;需要注意的是在安装系统的时候需要手动进行分区&#xff0c;我们可以看一下…

uniApp使用XR-Frame创建3D场景(3)光源投影的运用。

上一篇讲解了如何在uniApp中创建xr-frame子组件并创建简单的3D场景。 这篇我们讲解光源在场景中的运用以及相关属性。 在子组件 xr-start的index.wxml文件中我们加入如下代码 <xr-scene render-system"alpha:true" bind:ready"handleReady"><xr…

睿尔曼超轻量仿人机械臂之复合机器人底盘介绍及接口调用

机器人移动平台是一个包含完整成熟的感知、认知和定位导航能力的轮式机器人底盘产品级平台&#xff0c;产品致力于为各行业细分市场的商用轮式服务机器人提供一站式移动机器人解决方案&#xff0c;让合作伙伴专注在核心业务/人机交互的实现。以下是我司产品双臂机器人以及复合升…

主机安全-德迅卫士

什么是主机安全&#xff1f; 主机安全&#xff0c;其核心内容包括安全应用交付系统、应用监管系统、操作系统安全增强系统和运维安全管控系统。它的具体功能是指保证主机在数据存储和处理的保密性、完整性&#xff0c;可用性&#xff0c;它包括硬件、固件、系统软件的自身安全&…

HCIA-Datacom H12-811 题库补充(3/28)

完整题库及答案解析&#xff0c;请直接扫描上方二维码&#xff0c;持续更新中 OSPFv3使用哪个区域号标识骨干区域&#xff1f; A&#xff1a;0 B&#xff1a;3 C&#xff1a;1 D&#xff1a;2 答案&#xff1a;A 解析&#xff1a;AREA 号0就是骨干区域。 STP下游设备通知上游…

蓝桥杯_day6

文章目录 不同路径不同路径II拿金币珠宝的最高价值 不同路径 【题目描述】 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为…

啥也不会的大学生看过来,这8步就能系统入门stm32单片机???

大家好&#xff0c;今天给大家介绍啥也不会的大学生看过来&#xff0c;这8步就能系统入门stm32单片机&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 对于没有任何基础的大学生来…

GPT:多轮对话并搭建简单的聊天机器人

1 多轮对话 多轮对话能力至关重要&#xff0c;它不仅能深化交流&#xff0c;精准捕捉对方意图&#xff0c;还能促进有效沟通&#xff0c;增强理解。在智能客服、教育辅导等领域&#xff0c;多轮对话更是提升服务质量、增强用户体验的关键。 注意&#xff1a;大模型没有多轮对话…

JAVA使用POI实现Excel单元格合并-02

JAVA使用POI实现Excel单元格合并 实现效果 解释&#xff1a;只要是遇见与前一行相同的数据就合并 引入jar <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.2</version></depe…

OpenCV 如何使用 XML 和 YAML 文件的文件输入和输出

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;如何利用OpenCV4.9离散傅里叶变换 下一篇: 目标 本文内容主要介绍&#xff1a; 如何使用 YAML 或 XML 文件打印和读取文件和 OpenCV 的文本条目&#xff1f;如何对 OpenCV …

如何选择合适的充电桩主控制板

充电桩主控制板是充电桩不可或缺的核心元件之一&#xff0c;它的功能多样&#xff0c;包括控制、保护、监测、报警和记录等。随着充电基础设施的不断完善&#xff0c;选择合适的充电桩主控制板成为设计过程中至关重要的一环。然而&#xff0c;在市场上琳琅满目的产品中做出选择…

20221124 kafka实时数据写入Redis

一、上线结论 实现了将用户线上实时浏览的沉浸式视频信息&#xff0c;保存在Redis中这样一个功能。为实现沉浸式视频离线推荐到实时推荐提供了强有力的支持。目前只是应用在沉浸式场景&#xff0c;后续也能扩展到其他所有场景。用于两个场景&#xff1a;&#xff08;1&#xf…

一分钟开服 《幻兽帕鲁》游戏专属服务器by京东云主机

使用京东云服务器搭建幻兽帕鲁Palworld游戏联机服务器教程&#xff0c;非常简单&#xff0c;京东云推出幻兽帕鲁镜像系统&#xff0c;镜像直接选择幻兽帕鲁镜像即可一键自动部署&#xff0c;不需要手动操作&#xff0c;真正的新手0基础部署幻兽帕鲁&#xff0c;阿腾云atengyun.…