attention中Q,K,V的理解

news2025/1/19 8:25:47
  • 第一种

1.首先定义三个线性变换矩阵,query,key,value:

class BertSelfAttention(nn.Module):
    self.query = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
    self.key = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
    self.value = nn.Linear(config.hidden_size, self.all_head_size)

注意,这里的query, key, value只是一种操作(线性变换)的名称,实际的Q/K/V是它们三个的输出

 2.假设三种操作的输入都是同一个矩阵,这里暂且定长度为L的句子,每个token的特征维度是768,那输入就是(L,768),每一行就是一个字,像这样:

乘以上面三种操作就得到了Q、K、V,(L,768)*(768,768)=(L,768),维度其实没变,即此刻的Q、K、V分别为:

代码为:

class BertSelfAttention(nn.Module):
    def __init__(self, config):
        self.query = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
        self.key = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
        self.value = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
    
    def forward(self,hidden_states): # hidden_states 维度是(L, 768)
        Q = self.query(hidden_states)
        K = self.key(hidden_states)
        V = self.value(hidden_states)

3.然后实现这个操作:

① 首先是Q和K矩阵乘,(L, 768)*(L, 768)的转置=(L,L),看图:

首先用Q的第一行,即“我”字的768特征和K中“我”字的768为特征点乘求和,得到输出(0,0)位置的数值,这个数值就代表了“我想吃酸菜鱼”中“我”字对“我”字的注意力权重,然后显而易见输出的第一行就是“我”字对“我想吃酸菜鱼”里面每个字的注意力权重;整个结果自然就是“我想吃酸菜鱼”里面每个字对其它字(包括自己)的注意力权重(就是一个数值)了~

② 然后是除以根号dim,这个dim就是768,至于为什么要除以这个数值?主要是为了缩小点积范围,确保softmax梯度稳定性,具体推导可以看这里:Self-attention中dot-product操作为什么要被缩放

然后就是为什么要softmax,一种解释是为了保证注意力权重的非负性,同时增加非线性,还有一些工作对去掉softmax进行了实验,如PaperWeekly:线性Attention的探索:Attention必须有个Softmax吗?

③ 然后就是刚才的注意力权重V矩阵乘了,如图:

 注意力权重 x VALUE矩阵 = 最终结果

首先是“我”这个字对“我想吃酸菜鱼”这句话里面每个字的注意力权重,和V中“我想吃酸菜鱼”里面每个字的第一维特征进行相乘再求和,这个过程其实就相当于用每个字的权重对每个字的特征进行加权求和,然后再用“我”这个字对对“我想吃酸菜鱼”这句话里面每个字的注意力权重和V中“我想吃酸菜鱼”里面每个字的第二维特征进行相乘再求和,依次类推~最终也就得到了(L,768)的结果矩阵,和输入保持一致~

整个过程在草稿纸上画一画简单的矩阵乘就出来了,一目了然~最后上代码:

class BertSelfAttention(nn.Module):
    def __init__(self, config):
        self.query = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
        self.key = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
        self.value = nn.Linear(config.hidden_size, self.all_head_size) # 输入768, 输出768
    
    def forward(self,hidden_states): # hidden_states 维度是(L, 768)
        Q = self.query(hidden_states)
        K = self.key(hidden_states)
        V = self.value(hidden_states)
        
        attention_scores = torch.matmul(Q, K.transpose(-1, -2))
        attention_scores = attention_scores / math.sqrt(self.attention_head_size)
        attention_probs = nn.Softmax(dim=-1)(attention_scores)

        out = torch.matmul(attention_probs, V)
        return out

4. 为什么叫注意力网络?因为可以看到Q/K/V都是通过同一句话的输入算出来的,按照上面的流程也就是一句话内每个字对其它字(包括自己)的权重分配;那如果不是自注意力呢?简单来说,Q来自于句A,K、V来自于句B即可~

5. 注意,K/V中,如果同时替换任意两个字的位置,对最终的结果是不会有影响的,至于为什么,可以自己在草稿纸上画一画矩阵乘;也就是说注意力机制是没有位置信息的,不像CNN/RNN/LSTM;这也是为什么要引入位置embeding的原因。

  • 第二种

其实直接用邱锡鹏老师PPT里的一张图就可以直观理解——假设D是输入序列的内容,完全忽略线性变换的话可以近似认为Q=K=V=D(所以叫做Self-Attention,因为这是输入的序列对它自己的注意力),于是序列中的每一个元素经过Self-Attention之后的表示就可以这样展现:

也就是说,The这个词的表示,实际上是整个序列加权求和的结果——权重从哪来?点积之后Softmax得到——这里Softmax(QK)就是求权重的体现。我们知道,向量点积的值可以表征词与词之间的相似性,而此处的“整个序列”包括The这个词自己(再一次强调这是Self-Attention),所以最后输出的词的表示,其“主要成分”就主要地包含它自身和跟它相似的词的表示,其他无关的词的表示对应的权重就会比较低。

  • 第三种

首先附上链接:张俊林:深度学习中的注意力模型(2017版)这个几乎是我读到过的讲解Attention最为透彻的篇章之一了。

Q(Querry)代表查询值,对应Decoder的H(t-1)状态。这里要正确理解H(t-1),想要解码出t时刻的输出,你送入Decoder的必然有前一时刻计算出的隐状态。好了,所谓查询,就是你要拿着这个Decoder中的H(t-1)去和Encoder中各个时刻的隐状态[H(1), H(2), ... , H(T)](也就是各个Key)去比,也就是二者计算相似度(对应于文献中的各种energy函数)。最后算出来的结果用Softmax归一化,这个算出来的权重就是带有注意力机制的权重,其实在翻译任务中,Key和Value是相等的。在Transformer的实现源码中,Key和Value的初始值也是相等的。有了这个权重之后,就可以用这个权重对Value进行加权求和,生成的这个新的向量就是带有注意力机制的语义向量 Context vector,而这个语义向量会权衡Target与Source的token与token的关系,从而实现解码输出时,与Source中“真正有决定意义”的token关联。

姑且画蛇添足的再说几句:
首先,Attention机制是由Encoder-Decoder架构而来,且最初是用于完成NLP领域中的翻译(Translation)任务。那么输入输出就是非常明显的 Source-Target的对应关系,经典的Seq2Seq结构是从Encoder生成出一个语义向量(Context vector)而不再变化,然后将这个语义向量送入Decoder配合解码输出。这种方法的最大问题就是这个语义向量,我们是希望它一成不变好呢?还是它最好能配合Decoder动态调整自己,来使Target中的某些token与Source中的真正“有决定意义”的token关联起来好呢?

这就是为什么会有Attention机制的原因。说到底,Attention机制就是想生成会动态变化的语义向量来配合解码输出。而新贵 Self-Attention则是为了解决Target与Source各自内部token与token的关系。在Transformer中,这两种注意力机制得到了有机的统一,释放出了异常惊人的潜力。  

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

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

相关文章

python实验3 石头剪刀布游戏

实验3:石头剪刀布游戏 一、实验目的二、知识要点图三、实验1. 石头剪刀布2. 实现大侠个人信息 一、实验目的 了解3类基本组合数据类型。理解列表概念并掌握Python中列表的使用。理解字典概念并掌握Python中字典的使用。运用jieba库进行中文分词并进行文本词频统计。…

COGVLM论文解读(COGVLM:VISUAL EXPERT FOR LARGE LANGUAGE MODELS)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、摘要二、引言三、模型方法1、模型思路2、融合公式 四、训练方法总结 前言 2023年5月18日清华&智谱AI发布并开源VisualGLM-6B以来,清华KEG&…

竞赛选题 题目:基于深度学习的中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分:4.2 损失函数:4.3 搭建seq2seq框架:4.4 测试部分:4.5 评价NLP测试效果:4.6 梯度截断…

BUUCTF [GXYCTF2019]BabyUpload 1详解(.htaccess配置文件特性)

题目环境:查看题目源码 SetHandler application/x-httpd-php 通过源码可以看出这道文件上传题目主要还是考察.htaccess配置文件的特性 倘若不先上传.htaccess配置文件,那么后台服务器就无法解析php代码 这个是需要注意的 .htaccess配置文件特性 概述来说…

函数学习 PTA 1使用函数输出一个整数的逆序数;3判断满足条件的三位数;5使用函数求余弦函数的近似值

其实一共有五道题,但那两道实在太过简单,也不好意思打出来给大家看,那么这篇博客,就让我一次性写三道题吧!也当是个小总结,睡前深思。 6-1 使用函数输出一个整数的逆序数 本题要求实现一个求整数的逆序数的…

STM32F407-14.3.6-01输入捕获模式

输入捕获模式 在输入捕获模式下,当相应的 ICx⑦ 信号检测到跳变沿后,将使用捕获/比较寄存器 (TIMx_CCRx⑪) 来锁存计数器的值。发生捕获事件时,会将相应的 CCXIF⑬ 标志(TIMx_SR 寄存器)置 1, 并可发送中断…

云时空社会化商业 ERP 系统 Shiro 反序列化漏洞复现

0x01 产品简介 时空云社会化商业ERP(简称时空云ERP) ,该产品采用JAVA语言和Oracle数据库, 融合用友软件的先进管理理念,汇集各医药企业特色管理需求,通过规范各个流通环节从而提高企业竞争力、降低人员成本…

SpringMVC多种类型数据响应

SpringMVC多种类型数据响应入门 1.概念 RequestMapping 作用:用于建立请求URL和处理请求方法之间的对应关系 位置: 类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录 方法上,请求URL的第二级访问目…

【二叉树】Leetcode 199. 二叉树的右视图

力扣题目链接 解题思路 一开始&#xff0c;我以为只需要依次遍历最右边一列所有数即可&#xff0c;写出来的代码也通过了样例&#xff1a; class Solution { public:vector<int> rightSideView(TreeNode* root) {vector<int> ans;TreeNode* temp root;while(tem…

游戏缺少d3dx9_43.dll修复方法分享,快速解决dll缺失问题

在计算机使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“找不到d3dx9_43.dll文件”。这个错误通常出现在运行某些游戏或应用程序时&#xff0c;d3dx9_43.dll是一个动态链接库文件&#xff0c;它是DirectX 9的一部分&#xff0c;用于支持游戏中的3…

LeetCode 7 整数反转

题目描述 整数反转 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 示例…

U-boot(七):U-boot移植

本文主要探讨基于210官方U-boot源码移植。 移植基础 tar -jxvf android_uboot_smdkv210.tar.bz2cd u-boot-samsung-devrm -rf onenand_ipl onenand_bl1 lib_avr32 lib_blackfin lib_i386 lib_m68k lib_mips lib_microblaze lib_nios lib_nios2 lib_ppc lib_sh lib_sparccd bo…

Ubuntu防止休眠和挂起(笔记)

目录 1 动机2 禁用休眠3 解除休眠 1 动机 我要将 饿啊人制作成 noah-mp 的区域运行强迫&#xff0c;但是跑的慢&#xff0c;一晚上两天。后来发现是因为电脑自动 supend 了。Ubuntu 在电源那里最多只能设置 2 小时的防止休眠&#xff0c;2小时候又自动休眠&#xff0c;严重影响…

mongodb基本操作命令

mongodb快速搭建及使用 1.mongodb安装1.1 docker安装启动mongodb 2.mongo shell常用命令2.1 插入文档2.1.1 插入单个文档2.1.2 插入多个文档2.1.3 用脚本批量插入 2.2 查询文档 前言&#xff1a;本篇默认你是对nongodb的基础概念有了了解&#xff0c;操作是非常基础的。但是与关…

Opencv手势控制音量!附源码!

效果演示&#xff1a; 废话不多说&#xff01;直接上源码&#xff01;下面写有所有代码注释&#xff01;&#xff01; import cv2 import mediapipe as mp #它包含了各种预训练的机器学习模型&#xff0c;可以用于姿势估计、手势识别等任务 from ctypes import cast, POINTE…

P8649 [蓝桥杯 2017 省 B] k 倍区间(前缀和+优化(桶分类))

分析&#xff1a; &#xff08;1&#xff09;任意连续子序列可用两个前缀和的差来表示 &#xff08;2&#xff09;判断该子序列是否为k的倍数 p1-p2 模 0 (mod k) 等价于&#xff1a;前缀和模 k 是否同余 &#xff08;3&#xff09;同余的任意两前缀和组合的序列均满足…

[React] 2023年最新面试题

[React] 2023年最新面试题 1. class 组件与函数组件的区别2. react 18 新特性有那些?新增 createRoot API自动批处理过渡更新新的Hook 3. redux 和 react-redux 的区别4. redux 中间件的原理5. setState 发生了什么 &#xff0c;render 函数做了什么6. 虚拟DOM&#xff0c; Fi…

【面试】typescript

目录 为什么用TypeScript&#xff1f; TS和JS的区别 控制类成员可见性的访问关键字&#xff1f; public protected&#xff09;&#xff0c;该类及其子类都可以访问它们。 但是该类的实例无法访问。 私有&#xff08;private&#xff09;&#xff0c;只有类的成员可以访问…

JSON.stringify方法详解 后端接受JSON数据格式

1、方法定义&#xff1a;JSON.stringify(value, replacer, space) 参数说明&#xff1a; value&#xff1a;js对象 replacer&#xff1a;替换对象&#xff0c;可以是一个方法、对象或数组&#xff0c;将value按照替换规则展示。 space&#xff1a;填充参数&#xff0c;可以是数…

LVS+Keepalived实验

实验前准备 主DR服务器&#xff1a;(ens33)192.168.188.11 ipvsadm、keepalived (ens33:0)192.168.188.188 备DR服务器&#xff1a;(ens33)192.168.188.12 ipvsadm、keepalived (ens33:0)192.168.188.188 Wbe服务器1&#xff1a;(ens33)192.168.188.13 (lo:0)192.168.188.188 W…