【Transformer系列(1)】encoder(编码器)和decoder(解码器)

news2024/10/2 19:26:44

前言

这个专栏我们开始学习transformer,自推出以来transformer在深度学习中占有重要地位,不仅在NLP领域,在CV领域中也被广泛应用,尤其是2021年,transformer在CV领域可谓大杀四方。

在论文的学习之前,我们先来介绍一些专业术语。本篇就让我们先来认识一下encoder和decoder吧!

目录

一、encoder

1.1 简介

1.2 代码实现

1.3 transformer中的使用

1.3.1 transformer中encoder的组成

1.3.2 每个Block的组成

1.3.3 每个Block 中的具体实现步骤

二、decoder

2.1 简介

2.2 代码实现

2.3 transformer中的使用

2.3.1transformer中decoder的组成

2.3.2 transformer中encoder和decoder的区别

2.3.3  Masked self attention模块

2.3.4  Cross attetion模块

 2.3.5  具体实现步骤

三、encoder-decoder

3.1 简介

3.2 代码实现

3.3 注意问题


 一、encoder

1.1 简介

encoder也就是编码器,负责将输入序列压缩成指定长度的向量,这个向量就可以看成是这个序列的语义,然后进行编码,或进行特征提取(可以看做更复杂的编码)。

简单来说就是机器读取数据的过程,将现实问题转化成数学问题。如下图所示:


1.2 代码实现

在编码器接口中,我们只指定长度可变的序列作为编码器的输入X。 任何继承这个encoder 基类的模型将完成代码实现。

class encoder(nn.Module):
    def __init__(self):
        super(Encoder, self).__init__()
        self.positional_encoding = Positional_Encoding(config.d_model)
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)
        self.feed_forward = Feed_Forward(config.d_model)

        self.add_norm = Add_Norm()

    def forward(self,x): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list

        x += self.positional_encoding(x.shape[1],config.d_model)
        # print("After positional_encoding: {}".format(x.size()))
        output = self.add_norm(x,self.muti_atten,y=x)
        output = self.add_norm(output,self.feed_forward)

        return output

Mutihead_Attention():多头注意力机制: 

class Mutihead_Attention(nn.Module):
    def __init__(self,d_model,dim_k,dim_v,n_heads):
        super(Mutihead_Attention, self).__init__()
        self.dim_v = dim_v
        self.dim_k = dim_k
        self.n_heads = n_heads

        self.q = nn.Linear(d_model,dim_k)
        self.k = nn.Linear(d_model,dim_k)
        self.v = nn.Linear(d_model,dim_v)

        self.o = nn.Linear(dim_v,d_model)
        self.norm_fact = 1 / math.sqrt(d_model)

    def generate_mask(self,dim):
        # 此处是 sequence mask ,防止 decoder窥视后面时间步的信息。
        # padding mask 在数据输入模型之前完成。
        matirx = np.ones((dim,dim))
        mask = torch.Tensor(np.tril(matirx))

        return mask==1

    def forward(self,x,y,requires_mask=False):
        assert self.dim_k % self.n_heads == 0 and self.dim_v % self.n_heads == 0
        # size of x : [batch_size * seq_len * batch_size]
        # 对 x 进行自注意力
        Q = self.q(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k
        K = self.k(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k
        V = self.v(y).reshape(-1,y.shape[0],y.shape[1],self.dim_v // self.n_heads) # n_heads * batch_size * seq_len * dim_v
        # print("Attention V shape : {}".format(V.shape))
        attention_score = torch.matmul(Q,K.permute(0,1,3,2)) * self.norm_fact
        if requires_mask:
            mask = self.generate_mask(x.shape[1])
            attention_score.masked_fill(mask,value=float("-inf")) # 注意这里的小Trick,不需要将Q,K,V 分别MASK,只MASKSoftmax之前的结果就好了
        output = torch.matmul(attention_score,V).reshape(y.shape[0],y.shape[1],-1)
        # print("Attention output shape : {}".format(output.shape))

        output = self.o(output)
        return output

Feed_Forward() : 两个Linear中连接Relu即可,目的是为模型增添非线性信息,提高模型的拟合能力。

class Feed_Forward(nn.Module):
    def __init__(self,input_dim,hidden_dim=2048):
        super(Feed_Forward, self).__init__()
        self.L1 = nn.Linear(input_dim,hidden_dim)
        self.L2 = nn.Linear(hidden_dim,input_dim)

    def forward(self,x):
        output = nn.ReLU()(self.L1(x))
        output = self.L2(output)
        return output

Add_Norm():残差连接以及LayerNorm

class Add_Norm(nn.Module):
    def __init__(self):
        self.dropout = nn.Dropout(config.p)
        super(Add_Norm, self).__init__()

    def forward(self,x,sub_layer,**kwargs):
        sub_output = sub_layer(x,**kwargs)
        # print("{} output : {}".format(sub_layer,sub_output.size()))
        x = self.dropout(x + sub_output)

        layer_norm = nn.LayerNorm(x.size()[1:])
        out = layer_norm(x)
        return out

1.3 transformer中的使用

1.3.1transformer中encoder的组成

transformer 中 encoder 由 6 个相同的层组成,每个层包含 2 个部分:

  • Multi-Head Self-Attention
  • Position-Wise Feed-Forward Network (全连接层)


1.3.2 每个Block的组成

自注意力机制 + 残差链接 + LayerNorm + FC + 残差链接 + layer Norm此时的输出 = 一个 Block 的输出;


1.3.3 每个Block 中的具体实现步骤

(1)原始的输入向量b 与输出向量a残差相加,得到向量a+b;
【注意】 b是原始的输入向量,下图中输出向量a是考虑整个序列的输入向量得到的结果

 (2)将向量 a+b 通过 Layer Normation 得到向量c ;

也就是下图左边部分:

(3)将向量c 通过 FC layer 得到向量d ;

(4)向量c 与向量d 残差相加 ,得到向量e ;

(5)向量e 通过 Layer Norm 输出 向量f;

(6)此时得到的输出向量f 才是 encoder中每个Block中的一个输出向量;
以上步骤就是下图右边部分:

上述步骤,便是原始论文transformer中encoder的设计啦~


二、decoder

2.1 简介

decoder,也就是解码器,负责根据encoder部分输出的语义向量c来做解码工作。以翻译为例,就是生成相应的译文。

简单来说,就是就数学问题,并转换为现实世界的解决方案。

【注意】生成的序列是不定长的。而且上一时刻的输出通常要作为下一时刻的输入。


2.2 代码实现

在上面1.2encoder的代码实现中,我们已经实现了大部分decoder的模块。

但是encoder和decoder实现还是有区别的:

  • decoder的Muti_head_Attention引入了Mask机制
  • decoder与encoder中模块的拼接方式不同
class Decoder(nn.Module):
    def __init__(self):
        super(Decoder, self).__init__()
        self.positional_encoding = Positional_Encoding(config.d_model)
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)
        self.feed_forward = Feed_Forward(config.d_model)
        self.add_norm = Add_Norm()

    def forward(self,x,encoder_output): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list
        # print(x.size())
        x += self.positional_encoding(x.shape[1],config.d_model)
        # print(x.size())
        # 第一个 sub_layer
        output = self.add_norm(x,self.muti_atten,y=x,requires_mask=True)
        # 第二个 sub_layer
        output = self.add_norm(output,self.muti_atten,y=encoder_output,requires_mask=True)
        # 第三个 sub_layer
        output = self.add_norm(output,self.feed_forward)

        return output

2.3 transformer中的使用

2.3.1transformer中decoder的组成

在transformer中decoder 也是由 6 个相同的层组成,每个层包含 3 个部分:

  • Multi-Head Self-Attention
  • Multi-Head Context-Attention
  • Position-Wise Feed-Forward Network


2.3.2 transformer中encoder和decoder的区别

我们先来看看这个图

(1)第一级中: 将self attention 模块加入了Masked模块,变成了 Masked self-attention, 这样以来就只考虑解码器的当前输入和当前输入的左侧部分, 不考虑右侧部分; ( 注意,第一级decoder的key, query, value均来自前一层decoder的输出,但加入了Mask操作,即我们只能attend到前面已经翻译过的输出的词语,因为翻译过程我们当前还并不知道下一个输出词语,这是我们之后才会推测到的。)

(2)第二级中:引入了 Cross attention 交叉注意力模块 在 masked self-attention 和全连接层 之间加入;

(3)Cross attention 交叉注意力模块的输入 Q,K,V 不是来自同一个模块,K,V 来自编码器的输出, Q来自解码器的输出;

【注意】 解码器的输出一个一个产生的


2.3.3  Masked self attention模块

举个栗子吧~以翻译为例:

  • 输入:我是路人贾
  • 输出: I am Jia

由上一节可知,输入“我是路人贾”这一步是在encoder中进行了编码,那么这里我们具体讨论decoder的操作,也就是加了 Masked self attention模块后如何得到输出(“I am Jia”)的过程。

第1步:

  • 初始输入: 起始符</s> + Positional Encoding(位置编码)
  • 中间输入:(我是路人贾)Encoder Embedding
  • 最终输出:产生预测“I”

第2步:

  • 初始输入:起始符</s> + “I”+ Positonal Encoding
  • 中间输入:(我是路人贾)Encoder Embedding
  • 最终输出:产生预测“am”

第3步:

  • 初始输入:起始符</s> + “I”+ “Love”+ Positonal Encoding
  • 中间输入:(我是路人贾)Encoder Embedding
  • 最终输出:产生预测“Jia”

其实这个原理很简单,主要想表达的就是因为变成了Masked self attention ,所以只考虑输入向量本身, 和输入向量的之前的向量,即左侧向量,而不去考虑后边(右侧)向量。

另外,求相关性的分数时,q,k ,v 同样的也只会考虑当前输入向量的左侧向量部分,而不去考虑输入向量后面的右侧部分。

这里介绍一下论文在Decoder的输入上,对Outputs的Shifted Right操作。

Shifted Right 实质上是给输出添加起始符/结束符,方便预测第1个Token/结束预测过程。

还是看看我们上一个栗子~

正常的输出序列位置关系如下:

  • 0-"I"
  • 1-"am"
  • 2-"Jia"

但在执行的过程中,我们在初始输出中添加了起始符</s>,相当于将输出整体右移1位(Shifted Right),所以输出序列变成如下情况:

  • 0-</s>【起始符】
  • 1-“I”
  • 2-“am”
  • 3-“Jia”

这样我们就可以通过起始符</s>预测“I”,也就是通过起始符预测实际的第1个输出啦。


2.3.4  Cross attetion模块

Cross attetion模块称为交叉注意力模块,是因为向量 q , k , v 不是来自同一个模块

而是将来自解码器的输出向量q 与来自编码器的输出向量 k , v运算。

具体讲来:
向量 q 与向量 k之间相乘求出注意力分数α1 '
注意力分数α1 '再与向量 v 相乘求和,得出向量 b (图中表示为向量 v ) ;

请添加图片描述


 2.3.5  具体实现步骤

(1)经过 Masked self attention:
解码器之前的输出作为当前解码器的输入,并且训练过程中真实标签的也会输入到解码器中,此时这些输入, 通过一个Masked self-attention ,得到输出q向量,注意到这里的q是由解码器产生的;

(2)经过 Cross attention:
将向量q 与来自编码器的输出向量 k , v 运算。具体讲来就是向量 q 与向量 k之间相乘求出注意力分数α1 ',注意力分数α1 '再与向量 v 相乘求和,得出向量 b  ;

(3)经过全连接层:
之后向量 b 便被输入到feed−forward 层, 也即全连接层, 得到最终输出;

上述步骤,便是原始论文transformer中decoder的设计啦~


三、encoder-decoder

刚才已经分别了解了encoder和decoder,接下来我们再来看看encoder-decoder这个框架吧。

3.1 简介

encoder-decoder 模型主要是 NLP 领域里的概念。它并不特值某种具体的算法,而是一类算法的统称encoder-decoder 算是一个通用的框架,在这个框架下可以使用不同的算法来解决不同的任务。

 其实整个过程我们可以看做是一个游戏——《你画我猜》。玩家1从系统中抽取拿到题卡,然后通过画画的方式描述该词。玩家2就通过画来猜出题目中的词是什么东东。我们拿目前应用最深入的机器翻译问题举个栗子:

(毕贾索已上线~)

就酱就酱~大家懂就行~


3.2 代码实现

encoder-decoder框架包含了一个编码器和一个解码器,并且还拥有可选的额外的参数。在前向传播中,编码器的输出用于生成编码状态,这个状态又被解码器作为其输入的一部分。

class EncoderDecoder(nn.Module):
    """
    A standard Encoder-Decoder architecture. Base for this and many 
    other models.
    """
    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):
        super(EncoderDecoder, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.src_embed = src_embed
        self.tgt_embed = tgt_embed
        self.generator = generator
        
    def forward(self, src, tgt, src_mask, tgt_mask):
        "Take in and process masked src and target sequences."
        return self.decode(self.encode(src, src_mask), src_mask,
                            tgt, tgt_mask)
    
    def encode(self, src, src_mask):
        return self.encoder(self.src_embed(src), src_mask)
    
    def decode(self, memory, src_mask, tgt, tgt_mask):
        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)

3.3 注意问题

  • 不论输入和输出的长度是什么,中间的“向量c”长度都是固定的(这是它的缺陷所在)。
  • 根据不同的任务可以选择不同的编码器和解码器(例如,CNN、RNN、LSTM、GRU等)
  • encoder-decoder的一个显著特征就是:它是一个end-to-end的学习算法。
  • 只要符合这种框架结构的模型都可以统称为encoder-decoder模型。

本文参考:

李宏毅机器学习 

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

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

相关文章

【C++】类和对象—日期类的实现

目录一、日期类的功能二、获取月的天数三、Date类中的默认成员函数构造函数析构函数拷贝构造赋值运算符重载取地址操作符重载和const取地址操作符重载四、运算符重载&#x1f312;、、-、-日期天数日期天数日期-天数日期-天数&#x1f312;、!、>、>、<、<日期-日期…

C++ :websocket 通讯下的五种 I/O 模型

目录 I/O 多路复用&#xff08;一种同步 I/O 模型&#xff09; 非阻塞与阻塞 select、poll、epoll 起因 改善 select 与 poll 的差别 I/O 模型 阻塞 I/O 模型 非阻塞 I/O 模型 I/O 多路复用模型 信号驱动 I/O 模型&#xff08;SIGIO&#xff09; 异步 I/O 模型&…

VirtualBox下Ubuntu系统磁盘扩容

1. 正确扩容虚拟硬盘&#xff1a;修改虚拟硬盘和快照的虚拟硬盘大小 打开VirtualBox所在目录&#xff0c;打开cmd&#xff0c;输入命令VBoxManage list hdds&#xff0c;这样能够列出所有的虚拟磁盘。找到你需要扩容的磁盘输入命令VBoxManage" modifyhd "D:\Pat\to\…

米尔STM32MP135核心板 又一款入门级嵌入式开发平台

自2007年意法半导体&#xff08;ST&#xff09;推出STM32首款Cortex-M内核 MCU,十几年来&#xff0c;ST在MCU领域的发展是飞速向前的。而2019年ST发布了全新的STM32MPU系列产品线&#xff0c;STM32MP1作为新一代 MPU 的典范&#xff0c;有着极富开创意义的异构系统架构兼容并蓄…

WMS智能仓储

子产品介绍篇--智能仓储 智能仓储 我们通常也称 WMS 系统。是一个实时的计算机软件系统&#xff0c;它能够按照运作的业务规则和运算法则&#xff0c;对信息、资源、行为、存货和分销运作进行更完美地管理&#xff0c;提高效率。 一. 仓储管理系统&#xff08;wms&#xff09;…

javaweb过滤器与监听器

一、过滤器程序的基本结构、web.xml文件的配置过程和过滤器的执行过程 <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&quo…

MobPush创建推送

功能说明 MobPush提供遵循REST规范的HTTP接口&#xff0c;适用各开发语言环境调用。 IP绑定 工作台可以绑定服务器IP地址&#xff0c;未绑定之前所有IP均可进行REST API的调用&#xff0c;绑定后进仅绑定的IP才有调用权限。 调用地址 POSThttp://api.push.mob.com/v3/push/c…

03.vue3的计算属性

文章目录1.计算属性1.get()和set()2.computed的简写3.computed和methods对比2.相关demo1.全选和反选2.todos列表1.计算属性 模板内的表达式非常便利&#xff0c;但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。所以&#xff0c;对于任何…

CRM系统是什么?它有什么作用?

CRM系统是什么&#xff1f; CRM是Customer Relationship Management&#xff08;客户关系管理&#xff09;的缩写&#xff0c;是一种通过对客户进行跟踪、分析和管理的方法&#xff0c;以增加企业与客户之间的互动和联系&#xff0c;提高企业与客户之间的互信&#xff0c;从而…

GoNote第一章 环境搭建

GoNote第一章 环境搭建 golang介绍 1. 语言介绍 Go 是一个开源的编程语言&#xff0c;它能让构造简单、可靠且高效的软件变得容易。 Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发&#xff0c;后来还加入了Ian Lance Taylor, Russ Cox等人&#xff0c…

oracle远程克隆pdb

使用远程克隆的先决条件是: oracle版本是12.2以上,开启归档模式以及本地undo. 这里是想从172.16.12.250将PRODPDB1克隆到172.16.12.251下&#xff0c;命名为PRODPDB1COPY。 1 确保源端数据库开启归档模式 备注&#xff1a;进cdb里开启归档。 2 在源数据库中&#xff0c;确保…

2023年环境工程与生物技术国际会议(CoEEB 2023)

会议简介 Brief Introduction 2023年环境工程与生物技术国际会议(CoEEB 2023) 会议时间&#xff1a;2023年5月19日-21日 召开地点&#xff1a;瑞典马尔默 大会官网&#xff1a;www.coeeb.org 2023年环境工程与生物技术国际会议(CoEEB 2023)将围绕“环境工程与生物技术”的最新研…

【教程】Unity 与 Simence PLC 联动通讯

开发平台&#xff1a;Unity 2021 依赖DLL&#xff1a;S7.NET 编程语言&#xff1a;CSharp 6.0 以上   一、前言 Unity 涉及应用行业广泛。在工业方向有着一定方向的涉足与深入。除构建数据看板等内容&#xff0c;也会有模拟物理设备进行虚拟孪生的需求需要解决。而 SIMATIC&a…

测评:腾讯云轻量4核8G12M服务器CPU内存带宽流量

腾讯云轻量4核8G12M应用服务器带宽&#xff0c;12M公网带宽下载速度峰值可达1536KB/秒&#xff0c;折合1.5M/s&#xff0c;每月2000GB月流量&#xff0c;折合每天66GB&#xff0c;系统盘为180GB SSD盘&#xff0c;地域节点可选上海、广州或北京&#xff0c;4核8G服务器网来详细…

02-参数传递+统一响应结果

1. 参数传递&#xff1a; -- 简单参数 如果方法形参数名称与请求方法名称不匹配&#xff0c;采用RequestParam注解 -- 实体参数 -- 数组集合参数 -- 日期参数 -- JSON参数 -- 路径参数 2. 统一响应结果 -- 1. 创建Result类&#xff08;放到pojo包中&#xff09; package dem…

centos8 源码安装 apache(内附图片超详细)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的绽放&#xff0…

Redis 如何实现库存扣减操作和防止被超卖?

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

《Rank-LIME: Local Model-Agnostic Feature Attribution for Learning to Rank》论文精读

文章目录一、论文信息摘要二、要解决的问题现有工作存在的问题论文给出的方法&#xff08;Rank-LIME&#xff09;介绍贡献三、前置知识LIMEFeature AttributionModel-AgnosticLocalLearning to Rank&#xff08;LTR&#xff09;单文档方法&#xff08;PointWise Approach&#…

工业相机标定(张正友标定法)

目录 相机标定的概念 a. 相机标定的定义 b. 相机标定的目的 相机标定的过程 a. 标定板选择 b. 标定板摆放及拍摄 c. 标定板角点提取 张正友标定法 a. 反解相机矩阵 b.反解畸变系数 使用Python进行相机标定 a. 安装OpenCV b. 准备标定板图片 c. 利用OpenCV进行角点…

HashMap、HashTable、ConcurrentHashMap 之间的区别

哈喽&#xff0c;大家好~我是保护小周ღ&#xff0c;本期为大家带来的是 HashMap、HashTable、ConcurrentHashMap 之间的区别&#xff0c;从数据结构到多线程安全~确定不来看看嘛~更多精彩敬请期待&#xff1a;保护小周ღ *★,*:.☆(&#xffe3;▽&#xffe3;)/$:*.★* ‘一、…