自然语言处理(25:(终章Attention 1.)Attention的结构​)

系列文章目录

终章 1:Attention的结构

终章 2:带Attention的seq2seq的实现

终章 3:Attention的评价

终章 4:关于Attention的其他话题

终章 5:Attention的应用


目录

系列文章目录

前言

Attention的结构

一.seq2seq存在的问题

二.编码器的改进

三.编码器的改进1

四.编码器的改进2

五.编码器的改进3


前言

上一章我们使用RNN生成了文本,又通过连接两个RNN,将一个时序数据转换为了另一个时序数据。我们将这个网络称为seq2seq,并用它成功求解了简单的加法问题。之后,我们对这个seq2seq进行了几处改进,几乎解决了这个简单的加法问题。 本章我们将进一步探索seq2seq的可能性(以及RNN的可能性)。这里,Attention这一强大而优美的技术将登场。Attention毫无疑问是近年来深度学习领域最重要的技术之一。本章的目标是在代码层面理解Attention的结构,然后将其应用于实际问题,体验它的奇妙效果。


Attention的结构

如上一章所述,seq2seq是一个非常强大的框架,应用面很广。这里我们将介绍进一步强化seq2seq的注意力机制(attention mechanism,简称 Attention)。基于Attention 机制,seq2seq 可以像我们人类一样,将“注意力”集中在必要的信息上。此外,使用Attention可以解决当前seq2seq 面临的问题。 本节我们将首先指出当前seq2seq存在的问题,然后一边说明Attention 的结构,一边对其进行实现(transformer中的核心就是注意力机制Attention,不过是多头注意力机制)

一.seq2seq存在的问题

seq2seq 中使用编码器对时序数据进行编码,然后将编码信息传递给解码器。此时,编码器的输出是固定长度的向量。实际上,这个“固定长度” 存在很大问题。因为固定长度的向量意味着,无论输入语句的长度如何(无论多长),都会被转换为长度相同的向量。以上一章的翻译为例,如下图所示,不管输入的文本如何,都需要将其塞入一个固定长度的向量中。

无论多长的文本,当前的编码器都会将其转换为固定长度的向量。就像 把一大堆西装塞入衣柜里一样,编码器强行把信息塞入固定长度的向量中。 但是,这样做早晚会遇到瓶颈。就像最终西服会从衣柜中掉出来一样,有用的信息也会从向量中溢出。

现在我们就来改进seq2seq。首先改进编码器,然后再改进解码器。

二.编码器的改进

到目前为止,我们都只将LSTM层的最后的隐藏状态传递给解码器, 但是编码器的输出的长度应该根据输入文本的长度相应地改变。这是编码器 的一个可以改进的地方。具体而言,如下图所示,使用各个时刻的LSTM 层的隐藏状态。

如上图所示,使用各个时刻(各个单词)的隐藏状态向量,可以获得和输入的单词数相同数量的向量。在上图的例子中,输入了5个单词,此时编码器输出5个向量。这样一来,编码器就摆脱了“一个固定长度的向量”的制约

上图中我们需要关注LSTM层的隐藏状态的“内容”。此时,各个时刻的LSTM层的隐藏状态都充满了什么信息呢?有一点可以确定的是,各个时刻的隐藏状态中包含了大量当前时刻的输入单词的信息。就上图的例子来说,输入“猫”时的LSTM层的输出(隐藏状态)受此时输入的单词“猫”的影响最大。因此,可以认为这个隐藏状态向量蕴含许多“猫的成分”。按照这样的理解,如下图所示,编码器输出的hs矩阵就可以视为各个单词对应的向量集合

以上就是对编码器的改进。这里我们所做的改进只是将编码器的全部时刻的隐藏状态取出来而已。通过这个小改动,编码器可以根据输入语句的长度,成比例地编码信息。那么,解码器又将如何处理这个编码器的输出呢? 接下来,我们对解码器进行改进。因为解码器的改进有许多值得讨论的地方,所以我们分3部分进行。

三.编码器的改进1

编码器整体输出各个单词对应的LSTM层的隐藏状态向量hs。然后, 这个hs被传递给解码器,以进行时间序列的转换

顺便说一下,在上一章的最简单的seq2seq中,仅将编码器最后的隐藏状态向量传递给了解码器。严格来说,这是将编码器的LSTM层的“最后” 的隐藏状态放入了解码器的LSTM层的“最初”的隐藏状态。用图来表示的话,解码器的层结构如下图所示

如上图所示,上一章的解码器只用了编码器的LSTM层的最后的隐藏状态。如果使用hs,则只提取最后一行,再将其传递给解码器。下面我 们改进解码器,以便能够使用全部hs。 我们在进行翻译时,大脑做了什么呢?比如,在将“吾輩は猫である” 这句话翻译为英文时,肯定要用到诸如“吾輩=I”“ 猫 =cat”这样的知识。 也就是说,可以认为我们是专注于某个单词(或者单词集合),随时对这个 单词进行转换的。那么,我们可以在seq2seq中重现同样的事情吗?确切地说,我们可以让seq2seq学习“输入和输出中哪些单词与哪些单词有关”这样的对应关系吗?

从现在开始,我们的目标是找出与“翻译目标词”有对应关系的“翻译 源词”的信息,然后利用这个信息进行翻译。也就是说,我们的目标是仅关 注必要的信息,并根据该信息进行时序转换。这个机制称为Attention,是本章的主题。

在介绍Attention的细节之前,这里我们先给出它的整体框架。我们要实现的网络的层结构如下图所示。

如上图所示,我们新增一个进行“某种计算”的层。这个“某种计算”接收(解码器)各个时刻的LSTM层的隐藏状态和编码器的hs。然后, 从中选出必要的信息,并输出到Affine层。与之前一样,编码器的最后的隐藏状态向量传递给解码器最初的LSTM层。 上图的网络所做的工作是提取单词对齐信息。具体来说,就是从hs 中选出与各个时刻解码器输出的单词有对应关系的单词向量。比如,当上图的解码器输出“I”时,从hs中选出“吾輩”的对应向量。也就是说, 我们希望通过“某种计算”来实现这种选择操作。不过这里有个问题,就是选择(从多个事物中选取若干个)这个操作是无法进行微分的。

可否将“选择”这一操作换成可微分的运算呢?实际上,解决这个问题的思路很简单(但是,就像哥伦布蛋一样,第一个想到是很难的)。这个思路就是,与其“单选”,不如“全选”。如下图所示,我们另行计算表示各个单词重要度(贡献值)的权重。

如下图所示,这里使用了表示各个单词重要度的权重(记为a)。此时,a像概率分布一样,各元素是0.0~1.0的标量,总和是1。然后,计算这个表示各个单词重要度的权重和单词向量hs的加权和,可以获得目标向量。这一系列计算如下图所示

如上图所示,计算单词向量的加权和,这里将结果称为上下文向量, 并用符号c表示。顺便说一下,如果我们仔细观察,就可以发现“吾輩”对应的权重为0.8。这意味着上下文向量c中含有很多“吾輩”向量的成分, 可以说这个加权和计算基本代替了“选择”向量的操作。假设“吾輩”对应 的权重是1,其他单词对应的权重是0,那么这就相当于“选择”了“吾輩” 向量。

下面,我们从代码的角度来看一下目前为止的内容。这里随意地生成编码器的输出hs和各个单词的权重a,并给出求它们的加权和的实现,代码如下所示,请注意多维数组的形状

import numpy as np
T, H = 5, 4
hs = np.random.randn(T, H)
a = np.array([0.8, 0.1, 0.03, 0.05, 0.02])ar = a.reshape(5, 1).repeat(4, axis=1)
print(ar.shape)
# (5, 4)t = hs * ar
print(t.shape)
# (5, 4)c = np.sum(t, axis=0)
print(c.shape)
# (4, )

设时序数据的长度T=5,隐藏状态向量的元素个数H=4,这里给出了加权和的计算过程。我们先关注代码ar = a.reshape(5, 1).repeat(4, axis=1)。 如下图所示,这行代码将a转化为ar。

如上图所示,我们要做的是复制形状为(5,)的a,创建(5,4)的数组。 因此,通过a.reshape(5, 1) 将a的形状从(5,)转化为(5,1)。然后,在第1 个轴方向上(axis=0)重复这个变形后的数组4次,生成形状为(5,4)的数组。

此外,这里也可以不使用repeat()方法,而使用NumPy的广播功能。 此时,令ar = a.reshape(5, 1),然后计算hs * ar。如下图所示,ar会自动扩展以匹配hs的形状。

为了提高执行效率,这里应该使用NumPy的广播,而不是repeat()方 法。但是,在这种情况下,需要注意的是,在许多我们看不见的地方多维数组的元素被复制了。这相当于计算图中的Repeat节点。 因此,在反向传播时,需要执行Repeat节点的反向传播。 如上图所示,先计算对应元素的乘积,然后通过c = np.sum(hs*ar, axis=0) 求和。这里,通过参数axis可以指定在哪个轴方向(维度)上求和。 如果我们注意一下数组的形状,axis的使用方法就会很清楚。比如,当x的形状为(X, Y, Z)时,np.sum(x, axis=1) 的输出(和)的形状为(X, Z)。这里的重点是,求和会使一个轴“消失”。在上面的例子中,hs*ar的形状为 (5,4),通过消除第0个轴,获得了形状为(4,)的矩阵(向量)。

下面进行批处理版的加权和的实现,具体如下所示

N, T, H = 10, 5, 4
hs = np.random.randn(N, T, H)
a = np.random.randn(N, T)
ar = a.reshape(N, T, 1).repeat(H, axis=2)
# ar = a.reshape(N, T, 1) # 广播机制t = hs * ar
print(t.shape)
# (10, 5, 4)c = np.sum(t, axis=1)print(c.shape)
# (10, 4)

这里的批处理与之前的实现几乎一样。只要注意数组的形状,应该很快就能确定repeat()和sum()需要指定的维度(轴)。作为总结,我们把加权 和的计算用计算图表示出来

如上图所示,这里使用Repeat节点复制a。之后,通过“×”节点 计算对应元素的乘积,通过Sum节点求和。现在考虑这个计算图的反向传播。其实,所需要的知识都已经齐备。这里重述一下要点:“Repeat的反向传播是Sum”“ Sum的反向传播是Repeat”。只要注意到张量的形状,就不难知道应该对哪个轴进行Sum,对哪个轴进行Repeat。

现在我们将上图的计算图实现为层,这里称之为Weight Sum层, 其实现如下所示

class WeightSum:def __init__(self):self.params, self.grads = [], []self.cache = Nonedef forward(self, hs, a):N, T, H = hs.shapear = a.reshape(N, T, 1).repeat(H, axis=2)t = hs * arc = np.sum(t, axis=1)self.cache = (hs, ar)return cdef backward(self, dc):hs, ar = self.cacheN, T, H = hs.shapedt = dc.reshape(N, 1, H).repeat(T, axis=1)dar = dt * hsdhs = dt * arda = np.sum(dar, axis=2)return dhs, da

以上就是计算上下文向量的Weight Sum层的实现。因为这个层没有要学习的参数,所以根据代码规范,此处为self.params = []。其他应该没有特别难的地方,我们继续往下看。

四.编码器的改进2

有了表示各个单词重要度的权重a,就可以通过加权和获得上下文向量。那么,怎么求这个a呢?当然不需要我们手动指定,我们只需要做好让 模型从数据中自动学习它的准备工作。 下面我们来看一下各个单词的权重a的求解方法。首先,从编码器的处理开始到解码器第一个LSTM层输出隐藏状态向量的处理为止的流程如下图所示

在上图中,用h表示解码器的LSTM层的隐藏状态向量。此时,我们的目标是用数值表示这个h在多大程度上和hs的各个单词向量“相似”。 有几种方法可以做到这一点,这里我们使用最简单的向量内积。顺便说一下,向量a=(a1,a2,···,an)和向量b =(b1,b2,···,bn)的内积为:

上式的含义是两个向量在多大程度上指向同一方向,因此使用内积作为两个向量的“相似度”是非常自然的选择。

下面用图表示基于内积计算向量间相似度的处理流程

如上图所示,这里通过向量内积算出h和hs的各个单词向量之间的相似度,并将其结果表示为s。不过,这个s是正规化之前的值,也称为得分。接下来,使用老一套的Softmax函数对s进行正规化(下图)

使用Softmax函数之后,输出的a的各个元素的值在0.0~1.0,总和为1,这样就求得了表示各个单词权重的a。现在我们从代码角度来看一下这些处理。

from common.layers import Softmax
import numpy as npN, T, H = 10, 5, 4
hs = np.random.randn(N, T, H)
h = np.random.randn(N, H)
hr = h.reshape(N, 1, H).repeat(T, axis=1)t = hs * hrprint(t.shape)
# (10, 5, 4)s = np.sum(t, axis=2)
print(s.shape)
# (10, 5)softmax = Softmax()
a = softmax.forward(s)
print(a.shape)
# (10, 5)

以上就是进行批处理的代码。如前所述,此处我们通过reshape()和 repeat() 方法生成形状合适的hr。在使用NumPy的广播的情况下,不需要 repeat()。此时的计算图如下图所示。

如上图所示,这里的计算图由Repeat节点、表示对应元素的乘积的 “×”节点、Sum节点和Softmax层构成。我们将这个计算图表示的处理实现为AttentionWeight 类

class AttentionWeight:def __init__(self):self.params, self.grads = [], []self.softmax = Softmax()self.cache = Nonedef forward(self, hs, h):N, T, H = hs.shapehr = h.reshape(N, 1, H).repeat(T, axis=1)t = hs * hrs = np.sum(t, axis=2)a = self.softmax.forward(s)self.cache = (hs, hr)return adef backward(self, da):hs, hr = self.cacheN, T, H = hs.shapeds = self.softmax.backward(da)dt = ds.reshape(N, T, 1).repeat(H, axis=2)dhs = dt * hrdhr = dt * hsdh = np.sum(dhr, axis=1)return dhs, dh

类似于之前的Weight Sum层,这个实现有Repeat和Sum运算。只要注意到这两个运算的反向传播,其他应该就没有特别难的地方。下面,我 们进行解码器的最后一个改进。

五.编码器的改进3

在此之前,我们分两节介绍了解码器的改进方案。上面分别实现了Weight Sum层和Attention Weight层。现在,我们将这两层组合起来,结果如下图所示。

上图显示了用于获取上下文向量c的计算图的全貌。我们已经分为 Weight Sum 层和Attention Weight 层进行了实现。重申一下,这里进行的计算是:Attention Weight 层关注编码器输出的各个单词向量hs,并计算各个单词的权重a;然后,Weight Sum层计算a和hs的加权和,并输出上下文向量c。我们将进行这一系列计算的层称为Attention层(如下图)

以上就是Attention技术的核心内容。关注编码器传递的信息hs中的重要元素,基于它算出上下文向量,再传递给上一层(这里,Affine层在上 一层等待)。下面给出Attention层的实现

class Attention:def __init__(self):self.params, self.grads = [], []self.attention_weight_layer = AttentionWeight()self.weight_sum_layer = WeightSum()self.attention_weight = Nonedef forward(self, hs, h):a = self.attention_weight_layer.forward(hs, h)out = self.weight_sum_layer.forward(hs, a)self.attention_weight = areturn outdef backward(self, dout):dhs0, da = self.weight_sum_layer.backward(dout)dhs1, dh = self.attention_weight_layer.backward(da)dhs = dhs0 + dhs1return dhs, dh

以上是Weight Sum层和Attention Weight层的正向传播和反向传播。 为了以后可以访问各个单词的权重,这里设定成员变量attention_weight, 如此就完成了Attention层的实现。我们将这个Attention层放在LSTM层 和Affine 层的中间,如下图

如上图所示,编码器的输出hs被输入到各个时刻的Attention层。 另外,这里将LSTM层的隐藏状态向量输入Affine层。根据上一章的解码器的改进,可以说这个扩展非常自然。如下图所示,我们将Attention信息“添加”到了上一章的解码器上。

如上图所示,我们向上一章的解码器“添加”基于Attention层的上下文向量信息。因此,除了将原先的LSTM层的隐藏状态向量传给 Affine 层之外,追加输入Attention层的上下文向量。

最后,我们将在上上个图的时序方向上扩展的多个Attention层整体实现为Time Attention 层,如下图所示。

由上图可知,Time Attention 层只是组合了多个Attention层,其实现如下所示

class TimeAttention:def __init__(self):self.params, self.grads = [], []self.layers = Noneself.attention_weights = Nonedef forward(self, hs_enc, hs_dec):N, T, H = hs_dec.shapeout = np.empty_like(hs_dec)self.layers = []self.attention_weights = []for t in range(T):layer = Attention()out[:, t, :] = layer.forward(hs_enc, hs_dec[:,t,:])self.layers.append(layer)self.attention_weights.append(layer.attention_weight)return outdef backward(self, dout):N, T, H = dout.shapedhs_enc = 0dhs_dec = np.empty_like(dout)for t in range(T):layer = self.layers[t]dhs, dh = layer.backward(dout[:, t, :])dhs_enc += dhsdhs_dec[:,t,:] = dhreturn dhs_enc, dhs_dec

这里仅创建必要数量的Attention层(代码中为T个),各自进行正向 传播和反向传播。另外,attention_weights列表中保存了各个Attention层 对各个单词的权重。

以上,我们介绍了Attention的结构及其实现。下一节我们使用Attention来实现seq2seq,并尝试挑战一个真实问题,以确认Attention的效果。

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

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

相关文章

Git 命令大全:通俗易懂的指南

Git 命令大全:通俗易懂的指南 Git 是一个功能强大且广泛使用的版本控制系统。对于初学者来说,它可能看起来有些复杂,但了解一些常用的 Git 命令可以帮助你更好地管理代码和协作开发。本文将介绍一些常用的 Git 命令,并解释它们的…

基于yolov11的棉花品种分类检测系统python源码+pytorch模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv11的棉花品种分类检测系统是一种高效、准确的农作物品种识别工具。该系统利用YOLOv11深度学习模型,能够实现对棉花主要品种,包括树棉(G. arboreum)、海岛棉(G. barbadense)、草棉&a…

论文:Generalized Category Discovery with Clustering Assignment Consistency

论文下载: https://arxiv.org/pdf/2310.19210 一、基本原理 该方法包括两个阶段:半监督表示学习和社区检测。在半监督表示学习中,使用了监督对比损失来充分地推导标记信息。此外,由于对比学习方法与协同训练假设一致,研究引入了…

Java高级JVM知识点记录,内存结构,垃圾回收,类文件结构,类加载器

JVM是Java高级部分,深入理解程序的运行及原理,面试中也问的比较多。 JVM是Java程序运行的虚拟机环境,实现了“一次编写,到处运行”。它负责将字节码解释或编译为机器码,管理内存和资源,并提供运行时环境&a…

MySQL 5.7 Online DDL 技术深度解析

14.13.1 在线DDL操作 索引操作主键操作列操作生成列操作外键操作表操作表空间操作分区操作 索引操作 下表概述了对索引操作的在线DDL支持情况。星号表示有附加信息、例外情况或依赖条件。有关详细信息,请参阅语法和使用说明。 操作原地执行重建表允许并发DML仅修…

kafka 报错消息太大解决方案 Broker: Message size too large

kafka-configs.sh --bootstrap-server localhost:9092 \ --alter --entity-type topics \ --entity-name sim_result_zy \ --add-config max.message.bytes10485880 学习营课程

HarmonyOS:ComposeTitleBar 组件自学指南

在日常的鸿蒙应用开发工作中,我们常常会面临构建美观且功能实用的用户界面的挑战。而标题栏作为应用界面的重要组成部分,它不仅承载着展示页面关键信息的重任,还能为用户提供便捷的操作入口。最近在参与的一个项目里,我就深深体会…

前端面试题之CSS中的box属性

前几天在面试中遇到面试官问了一个关于box的属性面试题,平时都是直接AI没有仔细去看过。来说说CSS中的常用box属性: 1. box-sizing box-sizing 属性定义了元素的宽度和高度是否包括内边距(padding)和边框(border&…

前端开发时的内存泄漏问题

目录 🔍 什么是内存泄漏(Memory Leak)?🚨 常见的内存泄漏场景1️⃣ 未清除的定时器(setInterval / setTimeout)2️⃣ 全局变量(变量未正确释放)3️⃣ 事件监听未清除4️⃣…

Java 基础-30-单例设计模式:懒汉式与饿汉式

在软件开发中,单例设计模式(Singleton Design Pattern)是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于管理共享资源(如数据库连接池、线程池等)或需要…

为 MinIO AIStor 引入模型上下文协议(MCP)服务器

Anthropic 最近宣布的模型上下文协议 (MCP) 将改变我们与技术交互的方式。它允许自然语言通信替换许多任务的复杂命令行语法。不仅如此,语言模型还可以总结传统工具的丰富输出,并以人类可读的形式呈现关键信息。MinIO 是世界领先的…

2023年12月电子学会青少年软件编程四级考级真题—新“跳7”游戏

此题可点下方去处查看,支持在线编程,获取源码: 新“跳7”游戏_scratch_少儿编程题库学习中心-嗨信奥https://www.hixinao.com/tiku/scratch/show-5109.html?_shareid3 程序演示可点击下方查看,支持源码查看:新“跳7…

3D 地图渲染-区域纹理图添加

引入-初始化地图&#xff08;关键代码&#xff09; // 初始化页面引入高德 webapi -- index.html 文件 <script src https://webapi.amap.com/maps?v2.0&key您申请的key值></script>// 添加地图容器 <div idcontainer ></div>// 地图初始化应该…

如何避免内存泄漏,尤其是在React中

在React中避免内存泄漏主要涉及到两个方面&#xff1a;组件的卸载清理和异步操作的正确管理。以下是几个关键的策略和最佳实践&#xff1a; 1. 清理组件中的事件监听器和定时器 当组件卸载时&#xff0c;确保清除所有绑定的事件监听器和定时器&#xff0c;否则它们会持续占用内…

如何学习C++以及C++的宏观认知

学习方法 首先可以给出一个论断&#xff1a;C的语法和各种组件的原理及使用可以说是所有编程语言里面比较难的 那么如何掌握所有东西&#xff0c;比如网络编程&#xff0c;文件读写&#xff0c;STL。 不要对语法记各种笔记&#xff0c;比如vector容器有什么什么方法什么什么…

Minimind 训练一个自己专属语言模型

发现了一个宝藏项目&#xff0c; 宣传是完全从0开始&#xff0c;仅用3块钱成本 2小时&#xff01;即可训练出仅为25.8M的超小语言模型MiniMind&#xff0c;最小版本体积是 GPT-3 的 17000&#xff0c;做到最普通的个人GPU也可快速训练 https://github.com/jingyaogong/minimi…

Spring Boot 与 Spring Integration 整合教程

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 Spring Boot 与 Spring Integration 整合教程 简介 Spring Integration 是 Spring 生态系统中用于实现企业集成模式&#xff08;Enterprise Integration Pa…

Nginx 核心配置详解与性能优化最佳实践

1.什么是 Nginx&#xff1f; Nginx 是一个高性能的 Web 服务器和反向代理服务器。它轻量、高效&#xff0c;被广泛用于现代 Web 开发中。 2.为什么前端需要了解 Nginx&#xff1f; ★ 了解 本地开发&#xff1a;可以模拟生产环境 部署前端项目&#xff1a;作为静态文件服务器…

LayaAir3.3.0-beta.3重磅更新!Spine4.2、2D物理、UI系统、TileMap等全面升级!

正式版推出前&#xff0c;说明3.3的功能还没开发完。所以&#xff0c;又一大波更新来了~ 下面对重点更新进行说明。 Spine的重要更新 3.3.0-beta.3版本开始&#xff0c;新增了Spine 4.2 的运行时库&#xff0c;Spine动画上可以支持物理特性了。例如&#xff0c;下图右侧女孩在启…

pip安装timm依赖失败

在pycharm终端给虚拟环境安装timm库失败&#xff08; pip install timm&#xff09;&#xff0c;提示你要访问 https://rustup.rs/ 来下载并安装 Rust 和 Cargo 直接不用管&#xff0c;换一条命令 pip install timm0.6.13 成功安装 简单粗暴