| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 人工智能 -> Transformer详解 -> 正文阅读 |
|
[人工智能]Transformer详解 |
文章目录0. Transformer直观认识Transformer 和 LSTM 的最大区别,就是 LSTM 的训练是迭代的、串行的,必须要等当前字处理完,才可以处理下一个字。而 Transformer 的训练时并行的,即所有字是同时训练的,这样就大大增加了计算效率。Transformer 使用了位置嵌入 (Positional Encoding) 来理解语言的顺序,使用自注意力机制(Self Attention Mechanism)和全连接层进行计算。 Transformer 模型主要分为两大部分,分别是 Encoder 和 Decoder。Encoder 负责把输入(语言序列)隐射成隐藏层,然后解码器再把隐藏层映射为自然语言序列。 上图为 Transformer Encoder Block 结构图,注意:下面的内容标题编号分别对应着图中 1,2,3,4 个方框的序号 1. Positional Encoding如下图所示,Transformer模型对每个输入的词向量都加上了一个位置向量。这些向量有助于确定每个单词的位置特征,或者句子中不同单词之间的距离特征。词向量加上位置向量背后的直觉是:将这些表示位置的向量添加到词向量中,得到的新向量,可以为模型提供更多有意义的信息,比如词的位置,词之间的距离等。 现在定义一个位置嵌入的概念,也就是 Positional Encoding,位置嵌入的维度为 我们一般以字为单位训练 Transformer 模型。首先初始化字编码的大小为 原始论文中给出的位置编码信息的向量的设计表达式为: 上面表达式中的 p o s pos pos代表词的位置,取值范围是[0,max_sequence_length], d m o d e l d_{model} dmodel?代表位置向量的维度,i 代表位置 d m o d e l d_{model} dmodel?维位置向量第i维,指字向量的维度序号,取值范围[0,embedding_dimension/2]。 上面有 sin 和 cos 一组公式,也就是对应着 embedding_dimension 维度的一组奇数和偶数的序号的维度,分别用上面的 sin 和 cos 函数做处理,从而产生不同的周期性变化,而位置嵌入在 embedding_dimension维度上随着维度序号增大,周期变化会越来越慢,最终产生一种包含位置信息的纹理,就像论文原文中第六页讲的,位置嵌入函数的周期从 2π 到 10000?2π 变化,而每一个位置在 embedding_dimension维度上都会得到不同周期的 sin 和 cos 函数的取值组合,从而产生独一的纹理位置信息,最终使得模型学到位置之间的依赖关系和自然语言的时序特性。 在下图中,我们画出了一种位置向量在第4、5、6、7维度、不同位置的的数值大小。横坐标表示位置下标,纵坐标表示数值大小。 如果不理解这里为何这么设计,可以看这篇文章 Transformer 中的 Positional Encoding 2. Self Attention Mechanism对于输入的句子 X X X ,通过 WordEmbedding 得到该句子中每个字的字向量,同时通过 Positional Encoding 得到所有字的位置向量,将其相加(维度相同,可以直接相加),得到该字真正的向量表示。第 t t t个字的向量记作 x t x_t xt?。 先通过一个简单的例子来理解一下:什么是“self-attention自注意力机制”?假设一句话包含两个单词:Thinking Machines。自注意力的一种理解是:Thinking-Thinking,Thinking-Machines,Machines-Thinking,Machines-Machines,共 2 2 2^2 22种两两attention。那么具体如何计算呢?假设Thinking、Machines这两个单词经过词向量算法得到向量是 X 1 X_1 X1?, X 2 X_2 X2?, X 1 X1 X1, X 2 X2 X2: 下面,我们将self-attention计算的6个步骤进行可视化。 第1步:对输入编码器的词向量进行线性变换得到:Query向量: q 1 , q 2 , q 1 , q 2 q_1,q_2,q_1,q_2 q1?,q2?,q1?,q2?,Key向量: k 1 , k 2 , k 1 , k 2 k_1, k_2,k_1,k_2 k1?,k2?,k1?,k2?,Value向量: v 1 , v 2 , v 1 , v 2 v_1, v_2,v_1,v_2 v1?,v2?,v1?,v2?。这3个向量是词向量分别和3个参数矩阵相乘得到的,而这个矩阵也是是模型要学习的参数。attention计算的逻辑常常可以描述为:query和key计算相关或者叫attention得分,然后根据attention得分对value进行加权求和。 第2步:计算Attention Score(注意力分数)。假设我们现在计算第一个词Thinking 的Attention Score(注意力分数),需要根据Thinking 对应的词向量,对句子中的其他词向量都计算一个分数。这些分数决定了我们在编码Thinking这个词时,需要对句子中其他位置的词向量的权重。 Attention score是根据"Thinking" 对应的 Query 向量和其他位置的每个词的 Key 向量进行点积得到的。Thinking的第一个Attention Score就是 q 1 q_1 q1?和 k 1 k_1 k1?的内积,第二个分数就是 q 1 q_1 q1?和 k 2 k_2 k2?的点积。这个计算过程在下图中进行了展示,下图里的具体得分数据是为了表达方便而自定义的。 第3步:把每个分数除以 d k , d k \sqrt{d_k},d_{k} dk??,dk?是Key向量的维度。你也可以除以其他数,除以一个数是为了在反向传播时,求梯度时更加稳定。 第4步:接着把这些分数经过一个Softmax函数,Softmax可以将分数归一化,这样使得分数都是正数并且加起来等于1, 如下图所示。 这些分数决定了Thinking词向量,对其他所有位置的词向量分别有多少的注意力。 第5步:得到每个词向量的分数后,将分数分别与对应的Value向量相乘。这种做法背后的直觉理解就是:对于分数高的位置,相乘后的值就越大,我们把更多的注意力放到了它们身上;对于分数低的位置,相乘后的值就越小,这些位置的词可能是相关性不大的。 第6步:把第5步得到的Value向量相加,就得到了Self Attention在当前位置(这里的例子是第1个位置)对应的输出。 最后,在下图展示了 对第一个位置词向量计算Self Attention 的全过程。最终得到的当前位置(这里的例子是第一个位置)词向量会继续输入到前馈神经网络。注意:上面的6个步骤每次只能计算一个位置的输出向量,在实际的代码实现中,Self Attention的计算过程是使用矩阵快速计算的,一次就得到所有位置的输出向量。 self-attention矩阵运算将self-attention计算6个步骤中的向量放一起,比如
X
=
[
x
1
;
x
2
]
X=[x_1;x_2]
X=[x1?;x2?],便可以进行矩阵计算啦。下面,依旧按步骤展示self-attention的矩阵计算方法。 第2步:由于我们使用了矩阵来计算,我们可以把上面的第 2 步到第 6 步压缩为一步,直接得到 Self Attention 的输出。 Multi-Head Attention(多头注意力机制)Transformer 的论文通过增加多头注意力机制(一组注意力称为一个 attention head),进一步完善了Self-Attention。这种机制从如下两个方面增强了attention层的能力:
在多头注意力机制中,我们为每组注意力设定单独的 WQ, WK, WV 参数矩阵。将输入X和每组注意力的WQ, WK, WV 相乘,得到8组 Q, K, V 矩阵。 接着,我们把每组 K, Q, V 计算得到每组的 Z 矩阵,就得到8个Z矩阵。 由于前馈神经网络层接收的是 1 个矩阵(其中每行的向量表示一个词),而不是 8 个矩阵,所以我们直接把8个子矩阵拼接起来得到一个大的矩阵,然后和另一个权重矩阵 W O W^O WO相乘做一次变换,映射到前馈神经网络层所需要的维度。 以上就是多头注意力的全部内容。最后将所有内容放到一张图中: Attention代码下面的代码实现中,张量的第1维是 batch size,第 2 维是句子长度。代码中进行了详细注释和说明。
Padding Mask上面 Self Attention 的计算过程中,我们通常使用 mini-batch 来计算,也就是一次计算多句话,即
X
X
X 的维度是 但这时在进行 softmax 就会产生问题。回顾 softmax 函数
σ
(
z
i
)
=
e
z
i
∑
j
=
1
K
e
j
z
σ(z_i)=\frac {e^{z_i}}{∑_{j=1}^Ke^z_j}
σ(zi?)=∑j=1K?ejz?ezi??,
e
0
e^0
e0 是 1,是有值的,这样的话 softmax 中被 padding 的部分就参与了运算,相当于让无效的部分参与了运算,这可能会产生很大的隐患。因此需要做一个 mask 操作,让这些无效的区域不参与运算,一般是给无效区域加一个很大的负数偏置,即 3.残差连接和Layer Normalization到目前为止,我们计算得到了self-attention的输出向量。而单层encoder里后续还有两个重要的操作:残差连接、标准化。 编码器的每个子层(Self Attention 层和 FFNN)都有一个残差连接和层标准化(layer-normalization),如下图所示。 残差连接我们在上一步得到了经过 self-attention 加权之后输出,也就是 Self-Attention(Q, K, V),然后把他们加起来做残差连接。 Layer NormalizationLayer Normalization 的作用是把神经网络中隐藏层归一为标准正态分布,也就是
i
.
i
.
d
i.i.d
i.i.d 独立同分布,以起到加快训练速度,加速收敛的作用。 将 Self-Attention 层的层标准化(layer-normalization)和涉及的向量计算细节都进行可视化,如下所示: 编码器和和解码器的子层里面都有层标准化(layer-normalization)。假设一个 Transformer 是由 2 层编码器和两层解码器组成的,将全部内部细节展示起来如下图所示。 4.Transformer Encoder整体架构经过上面 3 个步骤,我们已经基本了解了 Encoder 的主要构成部分,下面我们用公式把一个 Encoder block 的计算过程整理一下: 1). 字向量与位置编码 5.Transformer Decoder整体架构我们先从 HighLevel 的角度观察一下 Decoder 结构,从下到上依次是:
和 Encoder 一样,上面三个部分的每一个部分,都有一个残差连接,后接一个 Layer Normalization。Decoder 的中间部件并不复杂,大部分在前面 Encoder 里我们已经介绍过了,但是 Decoder 由于其特殊的功能,因此在训练时会涉及到一些细节。 Masked Self-Attention传统 Seq2Seq 中 Decoder 使用的是 RNN 模型,因此在训练过程中输入 t t t 时刻的词,模型无论如何也看不到未来时刻的词,因为循环神经网络是时间驱动的,只有当 t t t 时刻运算结束了,才能看到 t+1 时刻的词。而 Transformer Decoder 抛弃了 RNN,改为 Self-Attention,由此就产生了一个问题,在训练过程中,整个 ground truth 都暴露在 Decoder 中,这显然是不对的,我们需要对 Decoder 的输入进行一些处理,该处理被称为 Mask 举个例子,Decoder 的 ground truth 为 " I am fine",我们将这个句子输入到 Decoder 中,经过 WordEmbedding 和 Positional Encoding 之后,将得到的矩阵做三次线性变换( W Q , W K , W V W_Q,W_K,W_V WQ?,WK?,WV?)。然后进行 self-attention 操作,首先通过 Q × K T d k \frac {Q×K^T}{\sqrt{d_k}} dk??Q×KT? 得到 Scaled Scores,接下来非常关键,我们要对 Scaled Scores 进行 Mask,举个例子,当我们输入 “I” 时,模型目前仅知道包括 “I” 在内之前所有字的信息,即 “” 和 “I” 的信息,不应该让其知道 “I” 之后词的信息。道理很简单,我们做预测的时候是按照顺序一个字一个字的预测,怎么能这个字都没预测完,就已经知道后面字的信息了呢?Mask 非常简单,首先生成一个下三角全 0,上三角全为负无穷的矩阵,然后将其与 Scaled Scores 相加即可。 之后再做 softmax,就能将 - inf 变为 0,得到的这个矩阵即为每个字之间的权重 Masked Encoder-Decoder Attention其实这一部分的计算流程和前面 Masked Self-Attention 很相似,结构也一摸一样,唯一不同的是这里的 K , V K,V K,V 为 Encoder 的输出, Q Q Q 为 Decoder 中 Masked Self-Attention 的输出 编码器和解码器协同工作编码器一般有多层,第一个编码器的输入是一个序列文本,最后一个编码器输出是一组序列向量,这组序列向量会作为解码器的 K 、 V K、V K、V输入,其中K=V=解码器输出的序列向量表示。这些注意力向量将会输入到每个解码器的Encoder-Decoder Attention层,这有助于解码器把注意力集中到输入序列的合适位置,如下图所示。 解码(decoding )阶段的每一个时间步都输出一个翻译后的单词(这里的例子是英语翻译),解码器当前时间步的输出又重新作为输入Q和编码器的输出K、V共同作为下一个时间步解码器的输入。然后重复这个过程,直到输出一个结束符。如下图所示: 解码器中的 Self Attention 层,和编码器中的 Self Attention 层的区别:
对于形形色色的NLP任务,OpenAI 的论文列出了一些列输入变换方法,可以处理不同任务类型的输入。下面这张图片来源于论文,展示了处理不同任务的模型结构和对应输入变换。 6.相关问题Transformer 为什么需要进行 Multi-head Attention?原论文中说到进行 Multi-head Attention 的原因是将模型分为多个头,形成多个子空间,可以让模型去关注不同方面的信息,最后再将各个方面的信息综合起来。其实直观上也可以想到,如果自己设计这样的一个模型,必然也不会只做一次 attention,多次 attention 综合的结果至少能够起到增强模型的作用,也可以类比 CNN 中同时使用多个卷积核的作用,直观上讲,多头的注意力有助于网络捕捉到更丰富的特征 / 信息。 Transformer 相比于 RNN/LSTM,有什么优势?为什么?
为什么说 Transformer 可以代替 seq2seq?这里用代替这个词略显不妥当,seq2seq 虽已老,但始终还是有其用武之地,seq2seq 最大的问题在于将 Encoder 端的所有信息压缩到一个固定长度的向量中,并将其作为 Decoder 端首个隐藏状态的输入,来预测 Decoder 端第一个单词 (token) 的隐藏状态。在输入序列比较长的时候,这样做显然会损失 Encoder 端的很多信息,而且这样一股脑的把该固定向量送入 Decoder 端,Decoder 端不能够关注到其想要关注的信息。Transformer 不但对 seq2seq 模型这两点缺点有了实质性的改进 (多头交互式 attention 模块),而且还引入了 self-attention 模块,让源序列和目标序列首先 “自关联” 起来,这样的话,源序列和目标序列自身的 embedding 表示所蕴含的信息更加丰富,而且后续的 FFN 层也增强了模型的表达能力,并且 Transformer 并行计算的能力远远超过了 seq2seq 系列模型 Transformer中的softmax计算为什么需要除以 d k d_k dk??论文中解释是:向量的点积结果会很大,将softmax函数push到梯度很小的区域,scaled会缓解这种现象。 1.为什么比较大的输入会使得softmax的梯度变得很小对于一个输入向量 X ? R d X\subseteq \mathbb{R}^d X?Rd ,softmax函数将其映射/归一化到一个分布 y ^ ? R d \hat y\subseteq \mathbb{R}^d y^??Rd 。在这个过程中,softmax先用一个自然底数 e e e 将输入中的元素间差距先“拉大”,然后归一化为一个分布。假设某个输入 X X X中最大的的元素下标是 k k k ,如果输入的数量级变大(每个元素都很大),那么 y ^ k \hat y_k y^?k?会非常接近1。 我们可以用一个小例子来看看 X X X的数量级对输入最大元素对应的预测概率的 y ^ k \hat y_k y^?k?影响。假定输入 X = [ a , a , 2 a ] T X=[a,a,2a]^T X=[a,a,2a]T ),我们来看不同量级的 a a a产生的 y ^ 3 \hat y_3 y^?3?有什么区别。
我们不妨把 a 在不同取值下, y ^ 3 \hat y_3 y^?3?对应的的全部绘制出来。代码如下:
得到的图如下所示:
然后我们来看softmax的梯度。不妨简记softmax函数为
g
(
?
)
g(·)
g(?),softmax得到的分布向量
y
^
=
g
(
X
)
\hat y=g(X)
y^?=g(X)对输入X的梯度为: 根据前面的讨论,当输入 X X X的元素均较大时,softmax会把大部分概率分布分配给最大的元素,假设我们的输入数量级很大,最大的元素是 x 1 x_1 x1?,那么就将产生一个接近one-hot的向量 y ^ ≈ [ 1 , 0 , ? ? ? , 0 ] T \hat y \approx [1,0,···,0]^T y^?≈[1,0,???,0]T,此时上面的矩阵变为如下形式:
2.维度与点积大小的关系是怎样的,为什么使用维度的根号来放缩?针对为什么维度会影响点积的大小,在论文的脚注中其实给出了一点解释: 将方差控制为1,也就有效地控制了前面提到的梯度消失的问题。 3.为什么在普通形式的 attention 时,使用非 scaled 的 softmax?最基础的 attention 有两种形式, 一种是 Add [1],一种是 Mul [2],写成公式的话是这样: 那么,极大的点积值是从哪里来的呢?对于 Mul 来说,如果 s s s 和 h h h 都分布在 [0,1],在相乘时引入一次对所有位置的 ∑ \sum ∑ 求和,整体的分布就会扩大到 [ 0 , d k ] [0,d_k] [0,dk?] 。反过来看 Add,右侧是被 t a n h ( ) tanh() tanh() 钳位后的值,分布在 [-1,1]。整体分布和 d k d_k dk? 没有关系。 4.为什么在分类层(最后一层),使用非 scaled 的 softmax?同上面一部分,分类层的 softmax 也没有两个随机变量相乘的情况。此外,这一层的 softmax 通常和交叉熵联合求导,在某个目标类别 i i i 上的整体梯度变为 y i ′ ? y i y_i^{'}-y_i yi′??yi? ,即预测值和真值的差。当出现某个极大的元素值,softmax 的输出概率会集中在该类别上。如果是预测正确,整体梯度接近于 0,抑制参数更新;如果是错误类别,则整体梯度接近于1,给出最大程度的负反馈。 也就是说,这个时候的梯度形式改变,不会出现极大值导致梯度消失的情况了。 7.参考文章 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/27 14:50:25- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |