learn编码器

news2024/11/15 16:59:49

目录

1、编码器的作用

2、编码器的结构图

3、代码实现如下


1、编码器的作用

编码器用于对输入进行指定的特征提取的过程,也称为编码,由 N 个编码器层堆叠而成

2、编码器的结构图

3、代码实现如下

import numpy as np
from torch.autograd import Variable
import copy
from torch import softmax
import math
import torch.nn as nn
import torch
# 构建Embedding类来实现文本嵌入层
class Embeddings(nn.Module):
    def __init__(self,vocab,d_model):
        """
        :param vocab: 词表的大小
        :param d_model: 词嵌入的维度
        """
        super(Embeddings,self).__init__()
        self.lut = nn.Embedding(vocab,d_model)
        self.d_model = d_model
    def forward(self,x):
        """
        :param x: 因为Embedding层是首层,所以代表输入给模型的文本通过词汇映射后的张量
        :return:
        """
        return self.lut(x) * math.sqrt(self.d_model)
class PositionalEncoding(nn.Module):
    def __init__(self,d_model,dropout,max_len=5000):
        """
        :param d_model: 词嵌入的维度
        :param dropout: 随机失活,置0比率
        :param max_len: 每个句子的最大长度,也就是每个句子中单词的最大个数
        """
        super(PositionalEncoding,self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        pe = torch.zeros(max_len,d_model) # 初始化一个位置编码器矩阵,它是一个0矩阵,矩阵的大小是max_len * d_model
        position = torch.arange(0,max_len).unsqueeze(1) # 初始一个绝对位置矩阵 max_len * 1
        div_term = torch.exp(torch.arange(0,d_model,2)*-(math.log(1000.0)/d_model)) # 定义一个变换矩阵,跳跃式的初始化
        # 将前面定义的变换矩阵进行奇数、偶数的分别赋值
        pe[:,0::2] = torch.sin(position*div_term)
        pe[:,1::2] = torch.cos(position*div_term)
        pe = pe.unsqueeze(0)  # 将二维矩阵扩展为三维和embedding的输出(一个三维向量)相加
        self.register_buffer('pe',pe) # 把pe位置编码矩阵注册成模型的buffer,对模型是有帮助的,但是却不是模型结构中的超参数或者参数,不需要随着优化步骤进行更新的增益对象。注册之后我们就可以在模型保存后重加载时,将这个位置编码与模型参数一同加载进来

    def forward(self, x):
        """
        :param x: 表示文本序列的词嵌入表示
        :return: 最后使用self.dropout(x)对对象进行“丢弃”操作,并返回结果
        """
        x = x + Variable(self.pe[:, :x.size(1)],requires_grad = False)   # 不需要梯度求导,而且使用切片操作,因为我们默认的max_len为5000,但是很难一个句子有5000个词汇,所以要根据传递过来的实际单词的个数对创建的位置编码矩阵进行切片操作
        return self.dropout(x)

def subsequent_mask(size):
    """
    :param size: 生成向后遮掩的掩码张量,参数 size 是掩码张量的最后两个维度大小,它的最后两个维度形成一个方阵
    :return:
    """
    attn_shape = (1,size,size) # 定义掩码张量的形状
    subsequent_mask = np.triu(np.ones(attn_shape),k = 1).astype('uint8') # 定义一个上三角矩阵,元素为1,再使用其中的数据类型变为无符号8位整形
    return torch.from_numpy(1 - subsequent_mask) # 先将numpy 类型转化为 tensor,再做三角的翻转,将位置为 0 的地方变为 1,将位置为 1 的方变为 0

def attention(query, key, value, mask=None, dropout=None):
    """
    :param query: 三个张量输入
    :param key: 三个张量输入
    :param value: 三个张量输入
    :param mask: 掩码张量
    :param dropout: 传入的 dropout 实例化对象
    :return:
    """
    d_model = query.size(-1)  # 得到词嵌入的维度,取 query 的最后一维大小
    scores = torch.matmul(query,key.transpose(-2,-1)) / math.sqrt(d_model)     # 按照注意力公式,将 query 和 key 的转置相乘,这里是将 key 的最后两个维度进行转置,再除以缩放系数,得到注意力得分张量 scores
    # query(2,8,4,64) key.transpose(-2,-1) (2,8,64,4) 进行矩阵乘法为 (2,8,4,4)
    if mask is not None:
        scores = torch.masked_fill(scores,mask == 0,-1e9)  # 使用 tensor 的 mask_fill 方法,将掩码张量和 scores 张量中每一个位置进行一一比较,如果掩码张量处为 0 ,则使用 -1e9 替换
        # scores = scores.masked_fill(mask == 0,-1e9)
    p_attn = softmax(scores, dim = -1) # 对 scores 的最后一维进行 softmax 操作,使用 F.softmax 方法,第一个参数是 softmax 对象,第二个参数是最后一个维度,得到注意力矩阵
    print('scores.shape ',scores.shape)
    if dropout is not None:
        p_attn = dropout(p_attn)
    return torch.matmul(p_attn,value),p_attn  # 返回注意力表示

class MultiHeadAttention(nn.Module):
    def __init__(self, head, embedding_dim , dropout=0.1):
        """
        :param head: 代表几个头的参数
        :param embedding_dim: 词向量维度
        :param dropout: 置零比率
        """
        super(MultiHeadAttention, self).__init__()
        assert embedding_dim % head == 0     # 确认一下多头的数量可以整除词嵌入的维度 embedding_dim
        self.d_k = embedding_dim // head  # 每个头获得词向量的维度
        self.head = head
        self.linears = nn.ModuleList([copy.deepcopy(nn.Linear(embedding_dim, embedding_dim)) for _ in range(4)])   # 深层拷贝4个线性层,每一个层都是独立的,保证内存地址是独立的,分别是 Q、K、V以及最终的输出线性层
        self.attn = None   # 初始化注意力张量
        self.dropout = nn.Dropout(p=dropout)
    def forward(self, query, key, value, mask=None):
        """
        :param query: 查询query [batch size, sentence length, d_model]
        :param key: 待查询key [batch size, sentence length, d_model]
        :param value: 待查询value [batch size, sentence length, d_model]
        :param mask: 计算相似度得分时的掩码(设置哪些输入不计算到score中)[batch size, 1, sentence length]
        :return:
        """
        if mask is not None:
            mask = mask.unsqueeze(1) # 将掩码张量进行维度扩充,代表多头中的第 n 个头
        batch_size = query.size(0)
        query, key, value = [l(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2) for l, x in zip(self.linears, (query, key, value))]  # 将1、2维度进行调换,目的是让句子长度维度和词向量维度靠近,这样注意力机制才能找到词义与句子之间的关系
        # 将每个头传递到注意力层
        x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)
        # 得到每个头的计算结果是 4 维的张量,需要形状的转换
        # 前面已经将1,2两个维度进行转置了,所以这里要重新转置回来
        # 前面已经经历了transpose,所以要使用contiguous()方法,不然无法使用 view 方法
        x = x.transpose(1, 2).contiguous() \
            .view(batch_size, -1, self.head * self.d_k)
        return self.linears[-1](x)  # 在最后一个线性层中进行处理,得到最终的多头注意力结构输出

class LayerNormalization(nn.Module):
    def __init__(self, features, eps=1e-6):
        """
        :param features: 词嵌入的维度
        :param eps: 出现在规范化公式的分母中,防止分母为0
        """
        super(LayerNormalization, self).__init__()
        # a 系数的默认值为1,模型的参数
        self.a = nn.Parameter(torch.ones(features))
        # b 系统的初始值为0,模型的参数
        self.b = nn.Parameter(torch.zeros(features))
        # 把 eps 传递到类中
        self.eps = eps

    def forward(self, x):
        # 在最后一个维度上求 均值,并且输出维度保持不变
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.a * (x - mean) / (std + self.eps) + self.b

class PositionFeedForward(nn.Module):
    def __init__(self, d_model, d_ff, dropout=0.1):
        """
        :param d_model: 输入维度,词嵌入的维度
        :param d_ff: 第一个的输出连接第二个的输入
        :param dropout: 置零比率
        """
        super(PositionFeedForward, self).__init__()
        self.linear1 = nn.Linear(d_model, d_ff)
        self.linear2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(p = dropout)
    def forward(self, x):
        return self.linear2(self.dropout(torch.relu(self.linear1(x))))

class SublayerConnection(nn.Module):
    def __init__(self, size, dropout=0.1):
        super(SublayerConnection,self).__init__()
        # 实例化了一个 LN 对象
        self.norm = LayerNormalization(size)
        self.dropout = nn.Dropout(p = dropout)
    def forward(self,x,sublayer):
        """
        :param x: 接受上一个层或者子层的输入作为第一个参数
        :param sublayer: 该子层连接中的子层函数胡作为第二个参数
        :return:
        """
        "首先对输出进行规范化,然后将结果交给子层处理,之后对子层进行 dropout处理" \
        "随机失活一些神经元,来防止过拟合,最后还有一个add操作" \
        "因此存在跳跃连接,所以是将输入x与dropout后的子层输出结果相加作为最终的子层连接输出"
        return x + self.dropout(sublayer(self.norm(x)))

class EncoderLayer(nn.Module):
    def __init__(self, size, self_attn, feed_forward, dropout):
        """
        :param size: 词嵌入的维度
        :param self_attn: 传入多头自注意力子层实例化对象,并且是自注意力机制
        :param feed_forward: 前馈全连接层实例化对象
        :param dropout: 置零比率
        """
        super(EncoderLayer,self).__init__()
        self.self_attn = self_attn
        self.feed_forward = feed_forward
        self.sublayer = nn.ModuleList([copy.deepcopy(SublayerConnection(size,dropout)) for _ in range(2)])
        self.size = size
    def forward(self, x, mask):
        """
        :param x: 上一层的输出
        :param mask: 掩码张量
        :return:
        """
        "首先通过第一个子层连接结构,其中包含多头自注意力子层"
        "然后通过第二个子层连接结构,其中包含前馈全连接子层,最后返回结果"
        x = self.sublayer[0](x,lambda x:self.self_attn(x,x,x,mask))
        return self.sublayer[1](x,self.feed_forward)
import torch.nn as nn
import copy
class Encoder(nn.Module):
    def __init__(self, layer, N):
        """
        :param layer: 编码器层
        :param N: 编码器的个数
        """
        super(Encoder,self).__init__()
        self.layers = nn.ModuleList(copy.deepcopy(layer) for _ in range(N))
        "初始化一个规范化层,用于最后的输出"
        self.norm = LayerNormalization(layer.size)
    def forward(self, x, mask):
        for layer in self.layers:
            x = layer(x,mask)
        return self.norm(x)
# 实例化参数
size = d_model = 512
head = 8
mask = torch.zeros(2,4,4)
d_ff = 62
dropout = 0.2
max_len = 60  # 句子最大长度
#-----------------------------------------词嵌入层
# 输入 x 是 Embedding层输出的张量,形状为 2 * 4 * 512
x = Variable(torch.LongTensor([[100,2,42,508],[491,998,1,221]]))
emb = Embeddings(1000,512)   # 嵌入层
embr = emb(x)
#-----------------------------------------位置编码
pe = PositionalEncoding(d_model, dropout,max_len)
pe_result = pe(embr)
x = pe_result
#-----------------------------------------多头注意力
self_attn = MultiHeadAttention(head,d_model)
#-----------------------------------------FFN
ff = PositionFeedForward(d_model, d_ff, dropout)
#-----------------------------------------编码器层
layer = EncoderLayer(size, copy.deepcopy(self_attn), copy.deepcopy(ff),dropout)
#-----------------------------------------编码器
N = 8
en = Encoder(layer, N)
en_result = en(x,mask)
print(en_result)
print(en_result.shape)

scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
scores.shape  torch.Size([2, 8, 4, 4])
tensor([[[ 0.8972,  1.1464, -2.2886,  ...,  0.4909,  0.5196,  0.0756],
         [-0.1428,  1.3504,  0.2368,  ..., -1.0005,  0.1516, -2.1761],
         [-0.0204,  0.1874, -0.7120,  ...,  1.0886,  0.5129,  1.1401],
         [-0.1354, -0.0835, -1.4364,  ...,  1.4079,  0.3481, -0.0844]],

        [[-1.5945, -0.1464,  0.3596,  ..., -1.7038,  1.0241, -0.0716],
         [-0.4506, -0.0832, -0.3198,  ...,  0.0378, -0.5991, -0.7635],
         [ 1.4974,  1.8504,  2.1817,  ..., -0.7692, -0.1072, -1.0367],
         [-0.2180, -0.8411,  0.1120,  ...,  1.3320, -0.3436, -0.3399]]],
       grad_fn=<AddBackward0>)
torch.Size([2, 4, 512])

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

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

相关文章

RabbitMQ开启消息跟踪日志(trace)

Trace 是Rabbitmq用于记录每一次发送的消息&#xff0c;方便使用Rabbitmq的开发者调试、排错。 1、启动Tracing插件 在RabbitMQ中默认是关闭的&#xff0c;需手动开启。此处rabbitMQ是使用docker部署的 ## 进入rabbitMq中 docker exec -it rabbitmq1 bash ## 启动日志插件 r…

基于图像的照明(IBL)简明教程

基于图像的照明基本上将图像中的所有像素视为光源。 通常&#xff0c;从全景高动态范围 (HDR) 图像创建的环境贴图&#xff08;通常是立方体贴图&#xff09;将用作纹理获取的源。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 假设阴影物体是不透明的&#xff0c;我们…

lazada商品评论数据接口,lazada商品评论API接口,lazadaAPI接口

lazada商品评论数据接口可以按照以下步骤获取&#xff1a; 注册Lazada开发者账号&#xff0c;创建API密钥和访问令牌。调用Lazada Open API中的Product Review API&#xff0c;提供商品的SKU或Seller SKU参数&#xff0c;即可获取该商品的所有评论。 Lazada商品评论数据接口封…

【Spring Boot从入门到实战】RabbitMQ消息队列 —— RabbitMQ入门

💠一名热衷于分享知识的程序员 💠乐于在CSDN上与广大开发者交流学习。 💠希望通过每一次学习,让更多读者了解我 💠也希望能结识更多志同道合的朋友。 💠将继续努力,不断提升自己的专业技能,创造更多价值。🌿欢迎来到@"衍生星球"的CSDN博文🌿 🍁本…

Web安全教程(超详细!从入门到精通!零基础可学!)

Web简介&#xff1a; Web 即全球广域网&#xff0c;也称为万维网&#xff0c;它是一种基于超文本和HTTP的、全球性的、动态交互和浏览信息提供了图形化的、易于访问的直观界面&#xff0c;其中的文档及超级链接将Internet上的信息节点组织成一个互为关联的网状结构。 万维网常…

2023年中国数控系统市场发展历程及趋势分析:数控系统市场规模将持续扩大[图]

数控系统是根据计算机存储器中存储的控制程序&#xff0c;执行部分或全部数值控制功能&#xff0c;并配有接口电路和伺服驱动装置的专用计算机系统。通过利用数字、文字和符号组成的数字指令来实现一台或多台机械设备动作控制&#xff0c;它所控制的通常是位置、角度、速度等机…

了解5个区别,FPmarkets用烛台和Renko图实现交易翻倍

很多投资者不知道日本烛台图表和Renko图表的区别&#xff0c;在交易中出现好的机会而把握不住&#xff0c;今天FPmarkets就和投资者一起了解烛台图表和Renko图表的区别&#xff0c;在今后的交易中能第一时间抓住机会&#xff0c;从而盈利。 首先&#xff0c;Renko图表是平滑的…

Linux系统卡顿处理记录(Debian)

问题现象描述 现象linux操作系统卡顿&#xff08;就是很慢&#xff09;&#xff0c;但是系统任然能够使用。 文章一步步的排查并且定位问题。 排查步骤 1. 使用top命令查看CPU是否占用过高。&#xff08;未发现&#xff09;排除问题 2. 使用df -h查看硬盘是否被占满。&#…

突破边界与持续技术创新,Doris Summit Asia 2023 主论坛亮点解读

峰会官网已上线&#xff0c;最新议程请关注&#xff1a;doris-summit.org.cn Doris Summit 是 Apache Doris 社区一年一度的技术盛会&#xff0c;由 SelectDB 联合 Apache Doris 社区的众多开发者、企业用户和合作伙伴共同发起&#xff0c;专注于传播推广开源 OLAP 与实时数据…

【如何查看Python安装了哪些包】

如何查看Python安装了哪些包 这篇文章主要给大家介绍了关于如何查看Python安装了哪些包的相关资料, Conda是另一种广泛使用的Python包管理工具,它用于安装、管理和升级软件包和其依赖项,需要的朋友可以参考下 目录 查找是否安装了具体的包怎么安装包&#xff1f;更新包总结 c…

C++ 使用Windows的API CreateDirectory 创建多层级文件夹

简介 使用Windows的API创建多层级文件夹 效果 代码 #include <windows.h> #include <direct.h> #include <iostream> #include <string> #include <sstream> #include <vector> //创建多层级文件夹 bool CreateDir(const std:…

web系统接口设计总结

一、前言 在前后端完全分离的开发模式或者说是架构模式下&#xff0c;后端开发者只需要编写后端接口&#xff0c;特别是restful风格接口更为常见。那么暴露给外面的接口大概有三个常见。1、给后台系统调用的接口&#xff0c;2、对客端应用的接口&#xff08;APP或者H5页面&…

Linux文件-内存映射mmap

mmap定义为&#xff1a;Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来&#xff0c;以初始化这个虚拟内存区域的内容&#xff0c;这个过程称为内存映射(memory mapping)。 在LINUX中我们可以使用mmap用来在进程虚拟内存地址空间中分配地址空间&#xff0c;创…

如何成为CISP信息安全专业人员

随着信息技术和经济社会的交汇融合&#xff0c;网络安全一跃成为国家最为关注的焦点之一。 对于网络安全行业的资质证书来说&#xff0c;CISP成为了目前从业人员最为关注和了解的证书之一&#xff0c;那么&#xff0c;在网络安全行业&#xff0c;CISP证书的价值究竟在哪里&…

MBBF展示的奇迹绿洲:5G的过去、此刻与未来

如果你来迪拜&#xff0c;一定不会错过全世界面积最大的人工岛项目&#xff0c;这是被称为世界第八大奇迹的棕榈岛。多年以来&#xff0c;这座岛从一片砂石、一棵棕榈树开始&#xff0c;逐步建成了整个波斯湾地区的地标&#xff0c;吸引着全世界游人的脚步。 纵观整个移动通信发…

国际前十伦敦金交易app软件最新排行榜(信息汇总)

随着科技的发展&#xff0c;移动交易已成为的趋势。伦敦金交易也不再局限于桌面&#xff0c;而是越来越多地转移到手机上。 为了更好地展现软件的信息&#xff0c;本文将为大家介绍国际前十的伦敦金交易app软件最新排行榜。 1.金荣中国 金荣中国是知名的伦敦金交易平台&…

Maven导入程序包jakarta.servlet,但显示不存在

使用前提&#xff1a;&#xff08;Tomcat10版本&#xff09;已知tomcat10版本之后&#xff0c;使用jakart.servlet。而tomcat9以及之前使用javax.servlet。 问题描述&#xff1a;在maven仓库有导入了Jakarta程序包&#xff0c;但是界面仍然显示是javax。&#xff08;下图&…

基于VCO的OTA稳定性分析的零交叉时差模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Linux编译FFmpeg

Linux编译FFmpeg 1. 下载FFmpeg源码 FFmpeg源码下载地址&#xff1a;http://ffmpeg.org/download.html 在下面选择版本 2. 解压并创建生成目录 tar xvf ffmpeg-snapshot.tar.bz2 // 解压下载的FFmpeg源码 makedir /root/ffmpeg // 创建生成目录3. 编译FFmpeg 进入FF…

详解Zookeeper(铲屎官)在众多中间件的应用和在Spring Boot业务系统中实现分布式锁和注册中心的解决方案

1.概述 1.1 Zookeeper是什么&#xff1f; Zookeeper: 直译过来是动物园管理员的意思&#xff0c;这里的动物表示的就是当下主流的众多框架组件(ps&#xff1a;现在的框架组件都喜欢用动物当图标)&#xff0c;而Zookeeper的图标如下图所示&#xff0c;是一个人拿着一个铲子(铲…