Transformer:从入门到精通

学习一个深度学习模型,我们首先需要从理论的角度理解它的构架,进而理解代码。

Transformer背景

首先我们知道,神经网络有一个巨大的家族,其中的CNN(卷积神经网络)源于视觉研究,目标是让机器自动学习图像特征,而RNN的出现是源于对记忆和序列建模的需求,目标是处理自然语言、语音等时序数据。循环神经网络(RNN)的关键思想是:不仅考虑当前输入,它还会记住之前的输入信息,把历史信息通过隐藏状态(hidden state)传递到下一步,从而实现“记忆”。所以,RNN可以用于自然语言处理(机器翻译、文本生成),语音处理(语音识别),时间序列预测(天气预测)等。

在RNN之前,语言模型主要是采用N-Gram,即预测当前词是什么的时候,我们只假设它和前面的N个词相关。显然这个模型并不靠谱,因为有时候关键信息藏在几句话之前。所以RNN出现了,它理论上可以向前/前后看任意多词:

双向循环神经网络结构

因此,RNN一定程度解决了N-Gram无法处理的问题(RNN详解见循环神经网络)。但是RNN也存在自己的不足:

1. RNN在处理长序列问题的时候,反向传播梯度会变得极小或极大,造成梯度消失或梯度爆炸

2. RNN难以捕捉长距离的依赖关系

3. RNN必须逐步处理输入,无法并行计算,训练速度缓慢

针对以上问题,科学家们想到了通过引入单元状态(cell state)的方法(长短时记忆网络)。LSTM的输入包括:当前时刻的输入值x _t,上一时刻LSTM的输出值h_{t-1},以及上一时刻的单元状态c_{t-1}。LSTM的输出包括:当前时刻输出值h_t,和当前时刻单元状态c_t。LSTM的关键就是怎样控制长期状态c。它引入了三个状态开关,即三个门:

1. 遗忘门:它决定了上一个时刻的单元状态c_{t-1}有多少保留到当前时刻单元状态c_t

2. 输入门:它决定当前时刻网络输入x _t有多少保留到单元状态c_t

3. 输出门:它控制单元状态c_t有多少输出到LSTM的当前输出值h_t

LSTM的前向计算

这样,遗忘门的控制可以保留很早期的信息,输入门的控制又可以避免当前无关紧要的信息进入记忆,输出门的控制又可以保证输出结果中包含早期的记忆。LSTM的引入使RNN能保留长期的依赖信息,缓解了梯度消失/爆炸的问题。后来在LSTM的基础上还发展出了GRU,Seq2Seq架构。但是,这些模型并没有本质上解决长距离依赖和训练效率低的问题。直到Transformer的出现。

(LSTM详解见长短时记忆网络(LSTM))

Transformer前言

一、独热编码(One-Hot Encoding)

计算机只能处理0/1编码,所以在面对文字的时候,我们需要一个词表来表示词(Token),即通过0/1将词表示为高维度稀疏向量(维度高,但大部分元素是0)。但是这样表示出来的句子无法包含上下文信息,词与词之间完全没有关系。因此在此基础上,我们引入了词嵌入。

二、词嵌入

所谓词嵌入,就是为词表中的每个词分配一个词向量,将其映射到语义空间中。这里语义空间维度表示词向量的维度,语义空间维度越高,语义表达能力就越强。词向量的每个值都是浮点数,词向量之间的距离也和词的语义距离有关。例如,我们可以将“红苹果”表示为 [0.13, 0.16, -0.89, 0.9] ,“青苹果”表示为 [0.12, 0.17, -0.88, 0.9] ,两者十分相似。常见的语义空间维度包括512(Transformer),12288(GPT3)。

三、嵌入矩阵

假设我们通过一个维度为D的语义空间表示了一个大小为V的词表,则其中的每个Token都是一个维度为D的浮点向量,该词表也就是一个\mathbb{R}^{V\times D}的矩阵。我们想要表示一个句子,那么在词表中选择我们用到的词组成一个嵌入矩阵即可。例如“红苹果不是青苹果”可以表示为:

X=\begin{bmatrix} 0.13 & 0.16 & -0.89 & 0.9 \\ 0.1 & -0.8 & 0.32 & 0.46 \\ 0.12 & 0.17 & -0.88 & 0.9 \end{bmatrix}

四、位置编码

在实现通过嵌入矩阵表示一个句子之后,我们如何把每个词在句子中的位置表示给模型呢,毕竟词之间的位置差异也会反映词向量的相关关系。这里,我们就需要用到位置编码。与词嵌入相似,位置编码也是一种将句中位置关系映射到一个向量中的方法。Transformer的位置编码为:

其中pos表示单词在句子中的位置(0, 1, 2...),i表示当前维度的下标,即一个词嵌入的第i个位置,d表示语义空间总维度。

这样,假设词向量维度D=4,则对于一个三个Token的句子,它的位置编码矩阵为:

最后,Transformer再将词嵌入矩阵与位置编码矩阵相加,得到文本序列的词和词序信息。

至于这个位置编码是如何设计出来的,可以参考博客:Transformer位置编码设计原理详解

自注意力机制

在图像分类任务中,我们知道判断图像类别是通过对图像的特征进行分析得到的,那么在序列中我们也可以通过观察句子中特定的词来理解句子的意思,这种对序列特征的观察就叫注意力。因此对于一个句子,其中的每个Token都应该有不同的注意力权重,当然,这个权重是相对于任意两个Token之间的关系来说的。

那么Transformer是怎么做的呢?

首先,假设我们得到了一个句子的两个词向量ij,该向量包含了位置编码信息。现在,我们通过每个Token x映射出三个向量,表示为:q,query;k,key和v,value。其中这个三个向量的维度可以和词向量维度不同,v的维度可以和qk不同。这三个向量是如何映射得到的呢:

q=xW_{q}

k=xW_{k}

v=xW_{v}

其中W_{q} W_{k}W{v}为三个可学习的权重矩阵。假设Token的维度为128(1*128),则通过128*64的权重矩阵,我们可以得到一个1*64的q,k,v

接下来,我们计算ij的注意力和ji的注意力:

a_{ij}=q_{i}^{T}k_{j}

a_{ji}=q_{j}^{T}k_{i}

这样,我们通过将两个64*1和1*64的向量相乘,会得到两个结果,当这两个向量维度较大时,点积的数值幅度会显著增大,导致训练时梯度消失。为了解决这个问题,Transformer对点积的结果进行缩放,缩放因子为:\frac{1}{\sqrt{D_{qk}}}D_{qk}为向量维度,即:

a_{ij}=\frac{q_{i}^{T}k_{j}}{\sqrt{D_{qk}}}

a_{ji}=\frac{q_{j}^{T}k_{i}}{\sqrt{D_{qk}}}

当一个序列中有S个Token时,我们两两计算其注意力,可以得到一个S\times S的注意力矩阵,表示为:

A=\frac{Q^{T}K}{\sqrt{D_{qk}}}

接下来,我们对这个注意力矩阵进行归一化,使其等同于概率分布。这里的归一化是针对注意力矩阵的每一行进行的,即一个句子中的第i个Token(第i行),这样,我们可以得到第i个Token相对于句子中其它Token的概率分布。

最后一步,我们会用到value矩阵V。假设一个句子有6个Token(S=6),则V矩阵会是一个6*64的结构,每一行表示一个Token的value,将其与A矩阵相乘(6*6):

Attention(Q,K,V) = Softmax(\frac{Q^{T}K}{\sqrt{D_{qk}}})V

可以得到一个6*64的矩阵,这个矩阵就是最终的注意力矩阵。

为什么Transformer要这样做呢?

首先,Q^{T}K计算了句子中一个Token与另一个Token的匹配程度(注意力分数),矩阵中第i行第j列即表示了iToken对jToken的注意力。Softmax后我们就可以知道哪些位置相对来说更重要。而为了结合具体的语义信息,我们还需要将这个归一化权重与句子的初始嵌入相乘得到加权词矩阵,这样,这个最后的矩阵就是“每个Token的新表示”矩阵,包括了上下文信息的矩阵。

部分内容参考:零基础入门深度学习(9) - Transformer (2/3)

多头注意力

多头注意力机制,按其字面信息,即表示多个参数向量组——多个q,k,v向量。单头注意力存在一定缺陷,每个序列的Token只能通过一个投影矩阵W_{q},W_{k},W_{v}去学习相关性和信息,但是语言的关系是多样的(比如语法结构和语义依赖关系是两种完全不同的语义空间),这样,我们就需要多个注意力头在不同的子语义空间计算注意力,关注一个序列的不同面。

为了不增加计算量和模型参数量,通常设置每个注意力头的维度是原先的1/h,即:D_{hq}=D_{q}/h,D_{hk}=D_{k}/h, D_{hv}=D_{v}/h

计算过程:

首先不同投影矩阵(W_{i}^{Q},W_{i}^{K},W_{i}^{V})把输入序列映射到多个子空间,每个子空间的维度是D_{hq},D_{hk},D_{hv}

在每个子空间分别计算注意力,得到多个head

拼接所有head的结果(n\times (h\cdot D))n为Token数,h为head数,D为子空间维度

W^{o}投影回原先语义空间维度

因果注意力

与普通的注意力不同,Transformer在生成文本时只能看到该位置之前的Token,因此与用于理解文本的双向注意力相比,单向注意力机制——因果注意力会有所不同。这种单向注意力机制可以通过掩码实现。

假设输入序列的Token数为n,掩码矩阵定义为:

不难发现,i\geqslant j表示允许当前位置和历史位置交互,i<j表示禁止与未来位置交互,其中i表示q向量的位置,j表示k向量的位置,即i位置的注意力只能由i位置之前的Token决定。掩码矩阵M实际上是一个上三角矩阵:

在计算注意力权重时,掩码矩阵直接通过相加融入注意力矩阵:

Transformer整体架构

Transformer总体上采用编码器+解码器的结构:

首先,编码器负责接收序列输入inputs,输出为编码到语义空间的一系列Token。具体来说,输入先转换为词嵌入,再引入位置嵌入,然后输入多头注意力层;接下来,残差网络的引入(Add)解决了梯度消失等问题(详见ResNet);层归一化(Norm)保证训练稳定,加速模型收敛;FFN提供非线性变化,同时为Token内各个维度提供了信息整合的渠道,即Token向量内部变换。上述子串构成了一个编码器层,一个完整的Transformer编码器包含N个编码器层。

解码器负责生成新的序列。解码序列中有两个特殊的Token:[start]和[end],表示生成序列的开始与结束。

解码器的结构和编码器非常相似,除了两点不同:第一点不同,解码器只能根据已经生成的Token去生成新的Token,因此需要使用因果注意力机制,即用Masked Multi-Head Attention替代了编码器中的Multi-Head Attention

第二点不同,解码器在生成每个Token时,需要将编码器的输出序列也作为输入序列的一部分,因此需要一个额外的Multi-Head Attention子层实现编码器和解码器的交叉注意力机制。具体来说,解码器根据上一子层Masked Multi-Head Attention的输出投影q向量,根据编码器对应层的输出投影kv向量,再根据注意力公式进行计算,从而使得解码器输出的Token可以融合编码器输出Token的信息。

一个完整的Transformer解码器包含N个解码器层,在Transformer的设计中,编码器和解码器层数相同。这样,每个解码器层都可以将对应的编码器层输出作为输入,从而可以同步顶层、中层和低层信息。

解码器最终如何输出词表中的词呢?当解码器的最后一个Transformer层输出后,这个输出经过一次线性变换Linear,将维度从词向量维度D变为词表的大小V,再经过Softmax归一化后,使之产生概率的意义。即最后的输出Output Probabilities是一个向量,其维度是词表大小V,这个向量的每个元素对应词表中的词的预测概率。对这个概率进行采样,就可以输出对应的词。

(以上内容参考零基础入门深度学习(10) - Transformer (3/3))

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

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

相关文章

FOC开环控制代码解读

这段代码实现了一个开环速度控制系统&#xff0c;用于控制电机转速。它通过PWM控制器输出电压信号&#xff0c;来驱动电机转动。具体来说&#xff0c;它在指定目标速度下&#xff0c;持续通过电压信号进行控制。下面是对该代码详细流程的逐步解析&#xff1a; 1. 宏定义与变量初…

Ansible Playbook 调试与预演指南:从语法检查到连通性排查

1&#xff1a;调试 playbook 时&#xff0c;最该先看哪一段输出&#xff1f; 答&#xff1a;先查看ansible-navigator run的 PLAY RECAP 段落&#xff0c;它能一次性给出每台受管主机的 ok、changed、unreachable、failed、skipped、rescued、ignored 等计数&#xff0c;快速定…

深入探讨可视化技术如何实现安全监测

可视化在安全监测中的作用&#xff0c;远超越了“美观的图表”这一表层概念。它是将抽象、混沌的安全数据转化为直观、可理解的视觉信息的过程&#xff0c;其核心价值在于赋能人类直觉&#xff0c;大幅提升认知与决策效率&#xff0c;从而实现对安全态势的深度感知和快速响应。…

Scikit-learn Python机器学习 - Scikit-learn加载数据集

锋哥原创的Scikit-learn Python机器学习视频教程&#xff1a; 2026版 Scikit-learn Python机器学习 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程主要讲解基于Scikit-learn的Python机器学习知识&#xff0c;包括机器学习概述&#xff0c;特征工程(数据…

如何在实际应用中选择Blaze或Apache Gluten?

Blaze 与 Apache Gluten 深入研究报告&#xff1a;技术实现、性能对比与选型指南 一、项目背景与技术演进 1.1 大数据处理性能瓶颈与 Native 引擎兴起 随着大数据量处理需求的不断增长&#xff0c;基于 JVM 的 Spark 在 CPU 密集型场景下的性能瓶颈日益凸显。从 Spark 2.4 版本…

Mysql 学习感悟 Day 1 Mysql架构

Mysql 学习感悟 Day 1简介具体流程如下&#xff1a;Server 层连接器查询缓存分析器优化器执行器存储引擎层更新语句是怎么执行的例子日志redo logbinlogmysql事务的二段提交Mysql官网 mysql安装教程 Navicat免费安装亲测有用 简介 大体来说&#xff0c;MySQL 服务端可以分为…

企业为什么需要部署数据防泄露系统?

在数字化转型的浪潮中&#xff0c;企业核心数据已成为商业竞争的“生命线”。然而&#xff0c;数据泄露事件频发&#xff0c;不仅可能导致巨额经济损失&#xff0c;更会严重损害企业信誉。据IBM《2023年数据泄露成本报告》显示&#xff0c;全球平均数据泄露成本已攀升至445万美…

CPU的MBR寄存器和MDR寄存器

在学习计算机组成原理&#xff0c;特别是学到CPU时&#xff0c;寄存器是必须了解的一些器件&#xff0c;比如说程序计数器&#xff08;PC&#xff09;,指令寄存器&#xff08;IR&#xff09;等寄存器&#xff0c;同时&#xff0c;了解MDR和MBR这两个寄存器也是必要的&#xff1…

QWidget和QML模式下阻止槽调用的方法总结

目录 1.背景 2.QWidget中阻止槽函数调用的方法 2.1.临时阻塞信号发射&#xff08;blockSignals()&#xff09; 2.2.断开特定信号与槽的连接&#xff08;disconnect()&#xff09; 2.3.在槽函数内通过标志位过滤 2.4.重写信号发射函数&#xff08;针对自定义信号&#xff…

序列化,应用层自定义协议

我们发的是一个结构化的数据OS内部&#xff0c;协议全部都是传递结构体对象。可以直接发送二进制对象吗&#xff1f;因为CS双方都能认识这个结构体!!!可以直接发送二进制对象&#xff0c;但是不建议1. 客户端和服务器说属于不同的OS&#xff0c;不同的结构体&#xff0c;在不同…

序列化和反序列的学习

一&#xff1a;重谈协议1 理解网络协议&#xff0c;可以把它想象成网络世界里的“交通规则”和“通用语言”。它是一套预先定义好的规则、标准和约定&#xff0c;使得不同设备、不同系统之间能够顺利地进行通信和数据交换。我们从TCP协议上面理解一下&#xff0c;首先TCP服务是…

计算机毕业设计 java 在线学习系统 基于 Java 的在线教育平台 Java 开发的学习管理系统

计算机毕业设计 java 在线学习系统fk01a40i &#xff08;配套有源码 程序 mysql数据库 论文&#xff09;本套源码可以先看具体功能演示视频领取&#xff0c;文末有联xi 可分享传统学习模式受时空限制&#xff0c;互动性不足&#xff0c;难以满足个性化学习需求。为打破限制&…

淘宝利用商品关键词获取商品信息指南

一、核心API接口选择接口名称功能描述适用场景taobao.items.search通过关键词搜索商品&#xff0c;支持分页、排序&#xff0c;返回商品列表&#xff08;含标题、价格、销量、图片等&#xff09;普通商品搜索、竞品监控、数据分析taobao.tbk.item.get淘宝客API&#xff0c;返回…

红黑树下探玄机:C++ setmultiset 的幕后之旅

目录 一、关联式容器 二、键值对 三、set 四、set的构造 五、set的iterator 六、set的Operations 七、multiset 一、关联式容器 序列式容器 &#xff1a; 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forwa…

Spring : 事务管理

1. 基本概念 事务&#xff08;Transaction&#xff09;是一组不可分割的操作单元&#xff0c;这些操作要么全部成功执行&#xff0c;要么全部失败回滚&#xff0c;不存在部分成功的情况。 事务具有ACID特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事…

C# 一个投资跟踪程序的设计与实现:面向对象与设计模式的深度解析

在现代金融应用开发中&#xff0c;如何高效、灵活地构建投资跟踪系统&#xff0c;是每一个金融软件工程师必须面对的挑战。本文将围绕一个投资跟踪程序的设计与实现过程&#xff0c;深入剖析其背后的设计理念、架构模式以及具体实现细节。我们将通过面向对象编程、设计模式&…

存储的未来之战:RustFS如何用ZK框架重构分布式协调?

本篇文章目录 一、导火索&#xff1a;当数据洪峰撞上分布式协调的天花板 二、技术密码&#xff1a;ZK框架的三大重构 2.1 一致性哈希环的量子级进化 2.2 动态负载均衡的"神经反射" 2.3 跨云数据同步的"时空折叠" 三、未来战争&#xff1a;2026年存储…

模拟实现STL中的list容器

list前言一、list的节点结构设计二、迭代器设计三、list类的实现3.1 类的成员变量和类型定义3.2 构造函数与析构函数3.3 元素访问与迭代器接口3.4 插入与删除操作3.5 其他常用操作四、总结每文推荐前言 在C STL中&#xff0c;list是一个非常常用的容器&#xff0c;它基于双向循…

Debug-039-el-date-picker组件手动输入时间日期的问题处理

图1-外输入框图2-内输入框图3问题描述&#xff1a;这两天在迭代功能的时候&#xff0c;基本上碰到的问题都是出自这个“时间日期选择框”&#xff0c;昨天的bug38也是解决这个组件。如上图1和2所示&#xff0c;可以把图1中的输入框叫外输入框&#xff0c;图2中的输入框叫内输入…

docker-runc not installed on system

问题 Docker build时Dockerfile有RUN命令执行报错shim error: docker-runc not installed on system&#xff0c;如下&#xff1a;解决方法 修改/etc/docker/daemon.json&#xff0c;添加正面内容 {"runtimes": {"docker-runc": {"path": "…