transformer--解码器

在编码器中实现了编码器的各种组件,其实解码器中使用的也是这些组件,如下图:

解码器组成部分:

  1. 由N个解码器层堆叠而成
  2. 每个解码器层由三个子层连接结构组成
  3. 第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
  4. 第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
  5. 第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接 

解码器层code

# 解码器层的类实现
class DecoderLayer(nn.Module):
    def __init__(self, size, self_attn, src_attn, feed_forward,dropout) -> None:
        """
        size : 词嵌入维度
        self_attn:多头自注意对象,需要Q=K=V
        src_attn:多头注意力对象,这里Q!=K=V
        feed_forward: 前馈全连接层对象
        """
        super(DecoderLayer,self).__init__()

        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        # 根据论文图使用clones克隆三个子层对象
        self.sublayer = clones(SublayerConnection(size,dropout), 3)

    def forward(self, x, memory, source_mask, target_mask):
        """
        x : 上一层的输入
        memory: 来自编码器层的语义存储变量
        source_mask: 源码数据掩码张量,针对就是输入到解码器的数据
        target_mask: 目标数据掩码张量,针对解码器最后生成的数据,一个一个的推理生成的词
        """

        m = memory

        # 将x传入第一个子层结构,第一个子层结构输入分别是x和self_attn函数,因为是自注意力机制,所以Q=K=V=x
        # 最后一个参数是目标数据掩码张量,这时要对目标数据进行掩码,因为此时模型可能还没有生成任何目标数据,
        # 比如在解码器准备生成第一个字符或词汇时,我们其实已经传入第一个字符以便计算损失
        # 但是我们不希望在生成第一个字符时模型能利用这个信息,因为我们会将其遮掩,同样生成第二个字符或词汇时
        # 模型只能使用第一个字符或词汇信息,第二个字符以及以后得信息都不允许被模型使用
        x = self.sublayer[0](x, lambda x: self.self_attn(x,x,x,target_mask))

        # 紧接着第一层的输出进入第二个子层,这个子层是常规的注意力机制,但是q是输入x;k、v是编码层输出memory
        # 同样也传入source_mask, 但是进行源数据遮掩的原因并非是抑制信息泄露,而是遮蔽掉对结果没有意义的的字符而产生的注意力
        # 以此提升模型的效果和训练速度,这样就完成第二个子层的处理
        x = self.sublayer[1](x, lambda x: self.src_attn(x,m,m,source_mask))

        # 最后一个子层就是前馈全连接子层,经过他的处理后就可以返回结果,这就是解码器层的结构
        return self.sublayer[2](x,self.feed_forward)

测试代码全放到最后

测试结果:

embr.shape =  torch.Size([2, 4, 512])
pe_result.shape =  torch.Size([2, 4, 512])
en_result.shape :  torch.Size([2, 4, 512])
en_result :  tensor([[[-1.0392, -1.2399, -0.0508,  ..., -0.0731,  0.0161,  0.1734],
         [ 0.4218, -0.8372,  0.0657,  ...,  1.1024, -0.0273,  0.0458],
         [ 1.1038,  0.7187, -0.4767,  ...,  0.0396,  0.4021, -0.2545],
         [-0.4050,  0.2746,  0.2608,  ..., -0.0969,  0.1556,  0.7639]],

        [[ 1.1785,  0.7174, -0.4660,  ..., -0.7642,  0.2084, -0.2262],
         [-0.2988,  0.7209,  0.1552,  ..., -0.4515,  0.2163, -0.5891],
         [-0.6027, -0.3825, -0.2690,  ...,  2.3163,  0.3059, -1.7363],
         [-0.5485, -1.7348,  0.5710,  ..., -1.8011, -3.2616,  0.6475]]],
       grad_fn=<AddBackward0>)
dl_result.shape =  torch.Size([2, 4, 512])
dl_result =  tensor([[[-23.0521, -28.3426,  -0.5458,  ...,  -6.1061,  -0.1419,   5.0221],
         [ 11.1962, -23.1371,  -0.3318,  ...,  24.4704,  -0.2596,   0.3329],
         [ 22.7772,  15.4876, -13.8883,  ...,  -0.7536,   6.2517,  -6.4530],
         [ -8.6034,   5.9488,   4.5170,  ...,  -1.7604,   3.1385,  18.6994]],

        [[ 20.9259,  18.1934, -13.7914,  ..., -18.0120,   0.2210,  -6.3908],
         [ -9.2162,  19.0768,  -0.3693,  ..., -11.8371,   5.5636, -15.0215],
         [-14.9818,  -8.8418,  -8.3098,  ...,  61.9500,   3.2425, -43.5170],
         [-16.1407, -38.8550,  10.6465,  ..., -44.7966, -83.8235,  12.7915]]],
       grad_fn=<AddBackward0>)

解码器

解码器的作用:根据编码器的结果以及上一次预测的结果,对下一次可能出现的值进行特征表示

# 解码器
class Decoder(nn.Module):
    def __init__(self,layer,N) -> None:
        """ layer: 解码器层, N:解码器层的个数"""
        super(Decoder,self).__init__()
        self.layers = clones(layer,N)
        self.norm = LayerNorm(layer.size)

    def forward(self, x, memory,source_mask, target_mask):
        # x:目标数据的嵌入表示
        # memory:编码器的输出
        # source_mask: 源数据的掩码张量
        # target_mask: 目标数据的掩码张量

        for layre in self.layers:
            x = layer(x,memory,source_mask,target_mask)
        
        return self.norm(x)

测试代码放到最后代码

结果:

de_result.shape :  torch.Size([2, 4, 512])
de_result :  tensor([[[-0.0569,  0.3506, -0.4071,  ..., -1.0797,  0.4819,  1.5599],
         [ 0.2342,  0.0497,  0.8868,  ...,  1.8162,  0.1724, -0.0384],
         [-0.0438, -0.8501,  1.2952,  ...,  0.5489,  0.1530,  1.2819],
         [-2.7741,  0.4939,  1.5461,  ..., -0.7539,  0.6964, -0.4137]],

        [[ 1.1773, -0.7767,  1.2400,  ...,  0.4109, -0.0105,  1.3137],
         [ 0.0067, -0.5182,  0.1695,  ..., -1.0328, -1.6252,  1.3039],
         [-0.8350, -0.8536, -0.4261,  ..., -1.2965,  0.1531,  0.2299],
         [-0.2015,  0.5470, -0.9219,  ..., -0.1534,  1.3922, -0.2488]]],
       grad_fn=<AddBackward0>)

输出部分

线性层的作用:

通过对上一步的线性变化得到指定维度的输出,也就是转换维度的作用,

softmax层的作用:

使最后一维的向量中的数字缩放到0-1的概率值域内,并满足他们的和为1 

 code

# 输出
class Generator(nn.Module):
    def __init__(self,d_mode, vocab_size) -> None:
        """
         d_mode: 词嵌入
         vocab_size: 词表大小
        """
        super(Generator,self).__init__()

        self.project = nn.Linear(d_mode, vocab_size)

    def forward(self, x):
        return F.log_softmax(self.project(x),dim=-1)

输出:

gen_result.shape : torch.Size([2, 4, 1000])
gen_result:  tensor([[[-7.3236, -6.3419, -6.6023,  ..., -6.8704, -6.2303, -6.9161],
         [-7.3549, -7.2196, -8.2483,  ..., -6.5249, -6.9905, -6.4151],
         [-6.7272, -6.5778, -7.1534,  ..., -6.3917, -7.4114, -6.7917],
         [-6.7106, -7.3387, -7.4814,  ..., -6.7696, -6.8284, -7.5407]],

        [[-7.0403, -6.6602, -6.6994,  ..., -6.5930, -7.5068, -7.0125],
         [-6.4951, -7.2265, -7.4753,  ..., -7.0645, -7.2771, -7.2495],
         [-7.5860, -7.3894, -8.1477,  ..., -6.7407, -6.4232, -8.4255],
         [-7.4713, -6.9773, -7.0890,  ..., -7.6705, -7.1161, -7.3006]]],
       grad_fn=<LogSoftmaxBackward0>)

测试代码 


import numpy as np
import torch
import torch.nn.functional as F
import torch.nn as nn
import matplotlib.pyplot as plt
import math
import copy 
from inputs import Embeddings,PositionalEncoding
from encode import  subsequent_mask,attention,clones,MultiHeadedAttention,PositionwiseFeedForward,LayerNorm,SublayerConnection,Encoder,EncoderLayer
# encode 代码在前面几节

# 解码器层的类实现
class DecoderLayer(nn.Module):
    def __init__(self, size, self_attn, src_attn, feed_forward,dropout) -> None:
        """
        size : 词嵌入维度
        self_attn:多头自注意对象,需要Q=K=V
        src_attn:多头注意力对象,这里Q!=K=V
        feed_forward: 前馈全连接层对象
        """
        super(DecoderLayer,self).__init__()

        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        # 根据论文图使用clones克隆三个子层对象
        self.sublayer = clones(SublayerConnection(size,dropout), 3)

    def forward(self, x, memory, source_mask, target_mask):
        """
        x : 上一层的输入
        memory: 来自编码器层的语义存储变量
        source_mask: 源码数据掩码张量,针对就是输入到解码器的数据
        target_mask: 目标数据掩码张量,针对解码器最后生成的数据,一个一个的推理生成的词
        """

        m = memory

        # 将x传入第一个子层结构,第一个子层结构输入分别是x和self_attn函数,因为是自注意力机制,所以Q=K=V=x
        # 最后一个参数是目标数据掩码张量,这时要对目标数据进行掩码,因为此时模型可能还没有生成任何目标数据,
        # 比如在解码器准备生成第一个字符或词汇时,我们其实已经传入第一个字符以便计算损失
        # 但是我们不希望在生成第一个字符时模型能利用这个信息,因为我们会将其遮掩,同样生成第二个字符或词汇时
        # 模型只能使用第一个字符或词汇信息,第二个字符以及以后得信息都不允许被模型使用
        x = self.sublayer[0](x, lambda x: self.self_attn(x,x,x,target_mask))

        # 紧接着第一层的输出进入第二个子层,这个子层是常规的注意力机制,但是q是输入x;k、v是编码层输出memory
        # 同样也传入source_mask, 但是进行源数据遮掩的原因并非是抑制信息泄露,而是遮蔽掉对结果没有意义的的字符而产生的注意力
        # 以此提升模型的效果和训练速度,这样就完成第二个子层的处理
        x = self.sublayer[1](x, lambda x: self.src_attn(x,m,m,source_mask))

        # 最后一个子层就是前馈全连接子层,经过他的处理后就可以返回结果,这就是解码器层的结构
        return self.sublayer[2](x,self.feed_forward)



# 解码器
class Decoder(nn.Module):
    def __init__(self,layer,N) -> None:
        """ layer: 解码器层, N:解码器层的个数"""
        super(Decoder,self).__init__()
        self.layers = clones(layer,N)
        self.norm = LayerNorm(layer.size)

    def forward(self, x, memory,source_mask, target_mask):
        # x:目标数据的嵌入表示
        # memory:编码器的输出
        # source_mask: 源数据的掩码张量
        # target_mask: 目标数据的掩码张量

        for layre in self.layers:
            x = layer(x,memory,source_mask,target_mask)
        
        return self.norm(x)

# 输出
class Generator(nn.Module):
    def __init__(self,d_mode, vocab_size) -> None:
        """
         d_mode: 词嵌入
         vocab_size: 词表大小
        """
        super(Generator,self).__init__()

        self.project = nn.Linear(d_mode, vocab_size)

    def forward(self, x):
        return F.log_softmax(self.project(x),dim=-1)




if __name__ == "__main__":
    # 词嵌入
    dim = 512
    vocab  =1000
    emb = Embeddings(dim,vocab)
    x = torch.LongTensor([[100,2,321,508],[321,234,456,324]])
    embr  =emb(x)
    print("embr.shape = ",embr.shape)
    # 位置编码
    pe = PositionalEncoding(dim,0.1) # 位置向量的维度是20,dropout是0
    pe_result = pe(embr)
    print("pe_result.shape = ",pe_result.shape)

    

    # 编码器测试
    size = 512
    dropout=0.2
    head=8
    d_model=512
    d_ff = 64

    c = copy.deepcopy
    x = pe_result
    

    self_attn = MultiHeadedAttention(head,d_model,dropout)
    ff = PositionwiseFeedForward(d_model,d_ff,dropout)
    # 编码器层不是共享的,因此需要深度拷贝
    layer= EncoderLayer(size,c(self_attn),c(ff),dropout)

    N=8
    mask = torch.zeros(8,4,4)

    en = Encoder(layer,N)
    en_result = en(x,mask)
    print("en_result.shape : ",en_result.shape)
    print("en_result : ",en_result)


    # 解码器层测试
    size = 512
    dropout=0.2
    head=8
    d_model=512
    d_ff = 64

    self_attn = src_attn = MultiHeadedAttention(head,d_model,dropout)
    ff = PositionwiseFeedForward(d_model,d_ff,dropout)
    
    x = pe_result
    mask = torch.zeros(8,4,4)
    source_mask = target_mask = mask
    memory = en_result

    dl = DecoderLayer(size,self_attn,src_attn,ff,dropout)
    dl_result = dl(x,memory,source_mask,target_mask)
    print("dl_result.shape = ", dl_result.shape)
    print("dl_result = ", dl_result)

    # 解码器测试

    size = 512
    dropout=0.2
    head=8
    d_model=512
    d_ff = 64
    memory = en_result
    c = copy.deepcopy
    x = pe_result
    

    self_attn = MultiHeadedAttention(head,d_model,dropout)
    ff = PositionwiseFeedForward(d_model,d_ff,dropout)
    # 编码器层不是共享的,因此需要深度拷贝
    layer= DecoderLayer(size,c(self_attn),c(self_attn),c(ff),dropout)

    N=8
    mask = torch.zeros(8,4,4)
    source_mask = target_mask = mask


    de = Decoder(layer,N)
    de_result = de(x,memory,source_mask, target_mask)
    print("de_result.shape : ",de_result.shape)
    print("de_result : ",de_result)


    # 输出测试
    d_model = 512
    vocab =1000
    x = de_result

    gen = Generator(d_mode=d_model,vocab_size=vocab)
    gen_result = gen(x)
    print("gen_result.shape :", gen_result.shape)
    print("gen_result: ", gen_result)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

<专利>机器人3D视觉快速定位抓取方法及系统

摘要&#xff0c;此专利无可用的关键技术信息&#xff0c;基本都是下面几句话反复说。。。 本发明提供了一种机器人3D快速定位抓取方法及系统&#xff0c; 包括&#xff1a; 通过高速的3D结构光成像对目标物体的表面轮廓进行扫描&#xff0c; 形成点云数据&#xff1b;对所述点…

浅谈排序算法(冒泡,插入,归并)

对于数据的排序&#xff0c;有多种方法&#xff0c;对应这不同的时间复杂度&#xff08;效率不同&#xff09;。 ​一、冒泡排序&#xff08;Bubble Sort&#xff09; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法。 算法思路&#xff1a; 1. 从第一对相…

利用 Python 抓取数据探索汽车市场趋势

一、引言 随着全球对环境保护意识的增强和技术的进步&#xff0c;新能源汽车作为一种环保、高效的交通工具&#xff0c;正逐渐受到人们的关注和青睐。在这个背景下&#xff0c;对汽车市场的数据进行分析和研究显得尤为重要。 本文将介绍如何利用 Python 编程语言&#xff0c;结…

扭蛋机小程序开发,线上扭蛋机成为市场发展主流?

近几年以来&#xff0c;潮玩市场一直处于领先状态&#xff0c;市场规模逐渐扩大。在潮玩行业中&#xff0c;除了盲盒&#xff0c;受到各大群体喜欢的就是扭蛋机了&#xff0c;它因为价格低、品类多样、收藏价值高的优势吸引了各个群体的消费者。 当下&#xff0c;线上用户体量…

基于springboot + vue实现的前后端分离-在线旅游网站系统(项目 + 论文)

项目介绍 本旅游网站系统采用的数据库是MYSQL &#xff0c;使用 JSP 技术开发&#xff0c;在设计过程中&#xff0c;充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。 技术选型 后端: SpringBoot Mybatis 数据库 : MyS…

★【二叉搜索树】【中序遍历+前后指针】Leetcode 530. 二叉搜索树的最小绝对差

★【二叉搜索树】【中序遍历前后指针】Leetcode 530. 二叉搜索树的最小绝对差 解法1 笨方法 中序遍历转化为有序数组之后遍历解法2 记忆一下&#xff01;&#xff01;&#xff01;需要用一个pre节点记录一下cur节点的前一个节点 遇到在二叉搜索树上求什么最值&#xff0c;求差…

一篇教会你升级GPT-4,内附详细步骤(24年3月最新)

先介绍一下 GPT 升级 第一种: 支付宝购买礼品卡给美区 Apple ID 充值 第二种&#xff1a;3分钟快速升级方法&#xff08;一键升级&#xff09; GPT4的作用非常强大&#xff0c;还可以使用DALL进行绘画&#xff0c;比如我画一个小王子的插画&#xff1a; 平时用DALL绘画是比较…

事物

概述&#xff1a; 数据库的事务&#xff08;Transaction&#xff09;是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令。 事务把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么同时成功&#xff0c;要么同时失败。 事…

SpringCloud搭建微服务之Consul服务配置

1. 概述 前面有介绍过Consul既可以用于服务注册和发现&#xff0c;也可以用于服务配置&#xff0c;本文主要介绍如何使用Consul实现微服务的配置中心&#xff0c;有需要了解如何安装Consul的小伙伴&#xff0c;请查阅SpringCloud搭建微服务之Consul服务注册与发现 &#xff0c…

Ubuntu系统使用Docker搭建Jupyter Notebook并实现无公网ip远程连接

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

用GGUF和Llama.cpp量化Llama模型

用GGUF和Llama .cpp量化Llama模型 什么是GGML如何用GGML量化llm使用GGML进行量化NF4 vs. GGML vs. GPTQ结论 由于大型语言模型&#xff08;LLMS&#xff09;的庞大规模&#xff0c;量化已成为有效运行它们的必要技术。通过降低其权重的精度&#xff0c;您可以节省内存并加快推理…

JavaScript数据类型 检测数据类型 数据类型转换 数值相等比较

数值相等比较 JavaScript 提供三种不同的值比较运算&#xff1a; ——严格相等&#xff08;三个等号&#xff09; ——宽松相等&#xff08;两个等号&#xff09; 8种数据类型 前七种为基础数据类型。 Object类型为引用数据类型。 数据类型概念以及存储方式 let a {name:…

MySQL:常用的SQL语句

提醒&#xff1a;设定下面的语句是在数据库名为 db_book执行的。 一、创建表 1. 创建t_booktype表 USE db_book; CREATE TABLE t_booktype(id INT AUTO_INCREMENT, bookTypeName VARCHAR(20),bookTypeDesc varchar(200),PRIMARY KEY (id) );2. 创建t_book表 USE db_book; C…

开源BLHELI-S 代码详细解读(五)

我们继续来看calc_next_comm_timing, 每次操作完换相之后&#xff0c;这里都会调用&#xff0c;同时会设置timer3去等advance timing. 总体思想是根据电机运行状态计算前4次换相时间&#xff0c;然后根据前4次换相时间计算15度和7.5度电角度时间&#xff0c;换相之后延时7.5度…

MYSQL C++链接接口编程

使用MYSQL 提供的C接口来访问数据库,官网比较零碎,又不想全部精读一下,百度CSDN都是乱七八糟的,大部分不可用 官网教程地址 https://dev.mysql.com/doc/connector-cpp/1.1/en/connector-cpp-examples-connecting.html 网上之所以乱七八糟,主要是MYSQL提供了3个接口两个包,使用…

输入一个字符串,将其中的数字字符移动到非数字字符之后

输入一个字符串&#xff0c;将其中的数字字符移动到非数字字符之后&#xff0c;并保持数字字符贺非数字字符输入时的顺序。 代码&#xff1a; #include <cstdio> #include <queue> using namespace std; int main() {char str[200];fgets(str, 200, stdin);//读入…

【详识JAVA语言】输入输出

输出到控制台 基本语法 System.out.println(msg); // 输出一个字符串, 带换行System.out.print(msg); // 输出一个字符串, 不带换行System.out.printf(format, msg); // 格式化输出 println 输出的内容自带 \n, print 不带 \n printf 的格式化输出方式和 C 语言的 printf 是基…

UE蓝图 编译过程详解

系列文章目录 UE蓝图 Get节点和源码 UE蓝图 Set节点和源码 UE蓝图 Cast节点和源码 UE蓝图 分支(Branch)节点和源码 UE蓝图 入口(FunctionEntry)节点和源码 UE蓝图 返回结果(FunctionResult)节点和源码 UE蓝图 函数调用(CallFunction)节点和源码 UE蓝图 序列(Sequence)节点和源…

BUUUCTF---LSB1

1.题目描述&#xff08;提示lsb&#xff09; 2.下载附件是一张图片 3.在010编辑器中查看没有发现什么信息&#xff0c;再看属性也没有什么有用的信息&#xff0c;根据题目提示lsb想到用Stegsolve这个工具 4.在该工具中打开图片 5.先将红绿蓝三种颜色的通道设置成0&#xff0c;…

如何使用Potplayer远程访问本地群晖NAS搭建的WebDAV中的本地资源

文章目录 本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是&#xff1a;1 使用环境要求&#xff1a;2 配置webdav3 测试局域网使用potplayer访问webdav3 内网穿透&#xff0c;映射至公网4 使用固定地址在potplayer访问webdav ​ 国内流媒体平台的内…
最新文章