Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
M
mtbookv2
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
NiuTrans
mtbookv2
Commits
37309b46
Commit
37309b46
authored
Sep 20, 2020
by
xiaotong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
wording (sec 12, attention and fnn)
parent
db70bd68
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
35 行增加
和
37 行删除
+35
-37
Chapter12/Figures/figure-calculation-of-context-vector-c.tex
+2
-2
Chapter12/Figures/figure-self-att-vs-enco-deco-att.tex
+2
-2
Chapter12/chapter12.tex
+31
-33
没有找到文件。
Chapter12/Figures/figure-calculation-of-context-vector-c.tex
查看文件 @
37309b46
...
...
@@ -20,7 +20,7 @@
\node
[anchor=south west] (alpha1) at ([xshift=-1em]key1.north west)
{
\scriptsize
{$
\alpha
_
1
=
.
2
$}}
;
\node
[anchor=south west] (alpha2) at ([xshift=-1em]key2.north west)
{
\scriptsize
{$
\alpha
_
2
=
.
3
$}}
;
\node
[anchor=south west] (alpha3) at ([xshift=-1em]key3.north west)
{
\scriptsize
{$
\alpha
_
3
=
.
1
$}}
;
\node
[anchor=south west] (alpha4) at ([xshift=-1em]key4.north west)
{
\scriptsize
{$
\alpha
_
4
=
.
3
$}}
;
\node
[anchor=south west] (alpha4) at ([xshift=-1em]key4.north west)
{
\scriptsize
{$
\alpha
_
i
=
.
3
$}}
;
\vspace
{
0.5em
}
...
...
@@ -40,7 +40,7 @@
\node
[anchor=south west] (alpha5) at ([xshift=-1em]key6.north west)
{
\scriptsize
{$
\alpha
_
1
=
.
1
$}}
;
\node
[anchor=south west] (alpha6) at ([xshift=-1em]key7.north west)
{
\scriptsize
{$
\alpha
_
2
=
.
3
$}}
;
\node
[anchor=south west] (alpha7) at ([xshift=-1em]key8.north west)
{
\scriptsize
{$
\alpha
_
3
=
.
2
$}}
;
\node
[anchor=south west] (alpha8) at ([xshift=-1em]key9.north west)
{
\scriptsize
{$
\alpha
_
4
=
.
3
$}}
;
\node
[anchor=south west] (alpha8) at ([xshift=-1em]key9.north west)
{
\scriptsize
{$
\alpha
_
i
=
.
3
$}}
;
\end{scope}
\end{tikzpicture}
...
...
Chapter12/Figures/figure-self-att-vs-enco-deco-att.tex
查看文件 @
37309b46
...
...
@@ -4,7 +4,7 @@
\node
[rounded corners=1pt,minimum width=11.0em,minimum height=2.0em,fill=pink!30,draw=black]
(p1) at (0,0)
{
\small
{
Self-Attention
}}
;
\node
[anchor=north]
(word1) at ([xshift=0.0em,yshift=-2.0em]p1.south)
{
\small
\vectorn
{
\emph
{
K
}}}
;
\node
[anchor=west]
(word2) at ([xshift=2.2em]word1.east)
{
\small
\vectorn
{
\emph
{
Q
}}}
;
\node
[anchor=west]
(word2) at ([xshift=2.2em]word1.east)
{
\small
\vectorn
{
\emph
{
V
}}}
;
\node
[anchor=east]
(word3) at ([xshift=-2.2em]word1.west)
{
\small
\vectorn
{
\emph
{
Q
}}}
;
\draw
[->,thick]
(word1.north)--(p1.south);
...
...
@@ -22,7 +22,7 @@
\node
[anchor=west,rounded corners=1pt,minimum width=14.0em,minimum height=2.0em,fill=pink!30,draw=black]
(p2) at ([xshift=5.0em]p1.east)
{
\small
{
Encoder-Decoder Attention
}}
;
\node
[anchor=north]
(word1-2) at ([xshift=0.0em,yshift=-2.0em]p2.south)
{
\small
\vectorn
{
\emph
{
K
}}}
;
\node
[anchor=west]
(word2-2) at ([xshift=2.2em]word1-2.east)
{
\small
\vectorn
{
\emph
{
Q
}}}
;
\node
[anchor=west]
(word2-2) at ([xshift=2.2em]word1-2.east)
{
\small
\vectorn
{
\emph
{
V
}}}
;
\node
[anchor=east]
(word3-2) at ([xshift=-2.2em]word1-2.west)
{
\small
\vectorn
{
\emph
{
Q
}}}
;
\draw
[->,thick]
(word1-2.north)--(p2.south);
...
...
Chapter12/chapter12.tex
查看文件 @
37309b46
...
...
@@ -153,7 +153,7 @@
\end{figure}
%----------------------------------------------
\parinterval
图
\ref
{
fig:12-39
}
展示了
经典的Transformer结构。解码器由若干层组成(绿色虚线框就代表一层)。每一层(l
ayer)的输入都是一个向量序列,输出是同样大小的向量序列,而Transformer层的作用是对输入进行进一步的抽象,得到新的表示结果。不过这里的层并不是指单一的神经网络结构,它里面由若干不同的模块组成,包括:
\parinterval
图
\ref
{
fig:12-39
}
展示了
Transformer的结构。解码器由若干层组成(绿色虚线框就代表一层)。每一层(L
ayer)的输入都是一个向量序列,输出是同样大小的向量序列,而Transformer层的作用是对输入进行进一步的抽象,得到新的表示结果。不过这里的层并不是指单一的神经网络结构,它里面由若干不同的模块组成,包括:
\begin{itemize}
\vspace
{
0.5em
}
...
...
@@ -180,11 +180,11 @@
\end{figure}
%----------------------------------------------
\parinterval
此外,编码端和解码端都有输入的词序列。编码端的词序列输入是为了对其进行表示,进而解码端能从编码端访问到源语言句子的全部信息。解码端的词序列输入是为了进行目标语的生成,本质上它和语言模型是一样的,在得到前
$
n
-
1
$
个单词的情况下输出第
$
n
$
个单词。除了输入
的
词序列的词嵌入,Transformer中也引入了位置嵌入,以表示每个位置信息。原因是,自注意力机制没有显性地对位置进行表示,因此也无法考虑词序。在输入中引入位置信息可以让自注意力机制间接地感受到每个词的位置,进而保证对序列表示的合理性。最终,整个模型的输出由一个Softmax层完成,它和循环神经网络中的输出层是完全一样的。
\parinterval
此外,编码端和解码端都有输入的词序列。编码端的词序列输入是为了对其进行表示,进而解码端能从编码端访问到源语言句子的全部信息。解码端的词序列输入是为了进行目标语的生成,本质上它和语言模型是一样的,在得到前
$
n
-
1
$
个单词的情况下输出第
$
n
$
个单词。除了输入词序列的词嵌入,Transformer中也引入了位置嵌入,以表示每个位置信息。原因是,自注意力机制没有显性地对位置进行表示,因此也无法考虑词序。在输入中引入位置信息可以让自注意力机制间接地感受到每个词的位置,进而保证对序列表示的合理性。最终,整个模型的输出由一个Softmax层完成,它和循环神经网络中的输出层是完全一样的。
\parinterval
在进行更详细的介绍前,先利用图
\ref
{
fig:12-39
}
简单了解一下Transformer模型是如何进行翻译的。首先,Transformer将源语
“我
\
很
\
好”的
{
\small\bfnew
{
词嵌入
}}
\index
{
词嵌入
}
(Word Embedding)
\index
{
Word Embedding
}
融合
{
\small\bfnew
{
位置编码
}}
\index
{
位置编码
}
(Position Embedding)
\index
{
Position Embedding
}
后作为输入。然后,编码器对输入的源语句子进行逐层抽象,得到包含丰富的上下文信息的源语表示并传递给解码器。解码器的每一层,使用自注意力子层对输入解码端的表示进行加工,之后再使用编码-解码注意力子层融合源语句子的表示信息。就这样逐词生成目标语译文单词序列。解码器的每个位置的输入是当前单词(比如,“I”),而这个位置输出是下一个单词(比如,“am”),这个设计和标准的神经语言模型是完全一样的。
\parinterval
在进行更详细的介绍前,先利用图
\ref
{
fig:12-39
}
简单了解一下Transformer模型是如何进行翻译的。首先,Transformer将源语
言句子“我/很/好”的词嵌入
融合
{
\small\bfnew
{
位置编码
}}
\index
{
位置编码
}
(Position Embedding)
\index
{
Position Embedding
}
后作为输入。然后,编码器对输入的源语句子进行逐层抽象,得到包含丰富的上下文信息的源语表示并传递给解码器。解码器的每一层,使用自注意力子层对输入解码端的表示进行加工,之后再使用编码-解码注意力子层融合源语句子的表示信息。就这样逐词生成目标语译文单词序列。解码器的每个位置的输入是当前单词(比如,“I”),而这个位置输出是下一个单词(比如,“am”),这个设计和标准的神经语言模型是完全一样的。
\parinterval
了解到这里,可能大家还有很多疑惑,比如,什么是位置编码?Transformer的自注意力机制具体是怎么进行计算的,其结构是怎样的?Add
\&
LayerNorm
又是什么?等等。下面就一一展开介绍。
\parinterval
当然,这里可能还有很多疑惑,比如,什么是位置编码?Transformer的自注意力机制具体是怎么进行计算的,其结构是怎样的?层归一化
又是什么?等等。下面就一一展开介绍。
%----------------------------------------------------------------------------------------
% NEW SECTION
...
...
@@ -216,12 +216,8 @@
\parinterval
位置编码的计算方式有很多种,Transformer使用不同频率的正余弦函数:
\begin{eqnarray}
\textrm
{
PE
}
(pos,2i) =
\textrm
{
sin
}
(
\frac
{
pos
}{
10000
^{
2i/d
_{
model
}}}
)
\label
{
eq:12-43
}
\end{eqnarray}
\begin{eqnarray}
\textrm
{
PE
}
(pos,2i+1) =
\textrm
{
cos
}
(
\frac
{
pos
}{
10000
^{
2i/d
_{
model
}}}
)
\label
{
eq:12-44
}
\textrm
{
PE
}
(pos,2i)
&
=
&
\textrm
{
sin
}
(
\frac
{
pos
}{
10000
^{
2i/d
_{
model
}}}
)
\label
{
eq:12-43
}
\\
\textrm
{
PE
}
(pos,2i+1)
&
=
&
\textrm
{
cos
}
(
\frac
{
pos
}{
10000
^{
2i/d
_{
model
}}}
)
\label
{
eq:12-44
}
\end{eqnarray}
\noindent
式中PE(
$
\cdot
$
)表示位置编码的函数,
$
pos
$
表示单词的位置,
$
i
$
代表位置编码向量中的第几维,
$
d
_{
model
}$
是Transformer的一个基础参数,表示每个位置的隐层大小。因为,正余弦函数的编码各占一半,因此当位置编码的维度为512 时,
$
i
$
的范围是0-255。 在Transformer中,位置编码的维度和词嵌入向量的维度相同(均为
$
d
_{
model
}$
),模型通过将二者相加作为模型输入,如图
\ref
{
fig:12-43
}
所示。
...
...
@@ -235,19 +231,19 @@
\end{figure}
%----------------------------------------------
\parinterval
那么为什么通过这种计算方式可以很好的表示位置信息?有几方面原因。首先,正余弦函数是具有上下界的周期函数,用正余弦函数可将长度不同的序列的位置编码的范围都固定到
[-1,1]
,这样在与词的编码进行相加时,不至于产生太大差距。另外位置编码的不同维度对应不同的正余弦曲线,这为多维的表示空间赋予一定意义。最后,根据三角函数的性质:
\parinterval
那么为什么通过这种计算方式可以很好的表示位置信息?有几方面原因。首先,正余弦函数是具有上下界的周期函数,用正余弦函数可将长度不同的序列的位置编码的范围都固定到
$
[-
1
,
1
]
$
,这样在与词的编码进行相加时,不至于产生太大差距。另外位置编码的不同维度对应不同的正余弦曲线,这为多维的表示空间赋予一定意义。最后,根据三角函数的性质:
\begin{eqnarray}
\textrm
{
sin
}
(
\alpha
+
\beta
)
&
=
&
\textrm
{
sin
}
\alpha
\cdot
\textrm
{
cos
}
\beta
+
\textrm
{
cos
}
\alpha
\cdot
\textrm
{
sin
}
\beta
\nonumber
\\
\textrm
{
cos
}
(
\alpha
+
\beta
)
&
=
&
\textrm
{
cos
}
\alpha
\cdot
\textrm
{
cos
}
\beta
-
\textrm
{
sin
}
\alpha
\cdot
\textrm
{
sin
}
\beta
\label
{
eq:12-45
}
\end{eqnarray}
\parinterval
可以得到
“
$
pos
+
k
$
”的位置
编码为:
\parinterval
可以得到
第
$
pos
+
k
$
个位置的
编码为:
\begin{eqnarray}
\textrm
{
PE
}
(pos+k,2i)
&
=
&
\textrm
{
PE
}
(pos,2i)
\
times
\textrm
{
PE
}
(k,2i+1) +
\nonumber
\\
&
&
\textrm
{
PE
}
(pos,2i+1)
\
times
\textrm
{
PE
}
(k,2i)
\\
\textrm
{
PE
}
(pos+k ,2i+1)
&
=
&
\textrm
{
PE
}
(pos,2i+1)
\
times
\textrm
{
PE
}
(k,2i+1) -
\nonumber
\\
&
&
\textrm
{
PE
}
(pos,2i)
\
times
\textrm
{
PE
}
(k,2i)
\textrm
{
PE
}
(pos+k,2i)
&
=
&
\textrm
{
PE
}
(pos,2i)
\
cdot
\textrm
{
PE
}
(k,2i+1) +
\nonumber
\\
&
&
\textrm
{
PE
}
(pos,2i+1)
\
cdot
\textrm
{
PE
}
(k,2i)
\\
\textrm
{
PE
}
(pos+k ,2i+1)
&
=
&
\textrm
{
PE
}
(pos,2i+1)
\
cdot
\textrm
{
PE
}
(k,2i+1) -
\nonumber
\\
&
&
\textrm
{
PE
}
(pos,2i)
\
cdot
\textrm
{
PE
}
(k,2i)
\label
{
eq:12-46
}
\end{eqnarray}
...
...
@@ -259,7 +255,7 @@
\section
{
基于点乘的多头注意力机制
}
\parinterval
Transformer模型摒弃了循环单元和卷积等结构,完全基于注意力机制来构造模型,其中包含着大量的注意力计算。比如,可以通过自注意力机制对源语言和目标语言序列进行信息提取,并通过编码-解码注意力对双语句对之间的关系进行
提取
。图
\ref
{
fig:12-44
}
中红色方框部分是Transformer中使用自注意力机制的模块。而这些模块都是由基于点乘的多头注意力机制实现的。
\parinterval
Transformer模型摒弃了循环单元和卷积等结构,完全基于注意力机制来构造模型,其中包含着大量的注意力计算。比如,可以通过自注意力机制对源语言和目标语言序列进行信息提取,并通过编码-解码注意力对双语句对之间的关系进行
建模
。图
\ref
{
fig:12-44
}
中红色方框部分是Transformer中使用自注意力机制的模块。而这些模块都是由基于点乘的多头注意力机制实现的。
%----------------------------------------------
\begin{figure}
[htp]
...
...
@@ -276,11 +272,11 @@
\subsection
{
点乘注意力
}
\parinterval
在
\ref
{
sec:12.1
}
节中已经介绍,自注意力机制中至关重要的是获取相关性系数,也就是在融合不同位置的表示向量时各位置的权重。
不同于第十章介绍的注意力机制的相关性系数计算方式,Transformer模型采用了一种基于点乘的方法来计算相关性系数。这种方法也称为
{
\small\bfnew
{
点乘注意力
}}
\index
{
点乘注意力
}
(Scaled Dot-Product Attention)
\index
{
Scaled Dot-Product Attention
}
机制。它的运算并行度高,同时并不消耗太多的存储空间。
\parinterval
在
\ref
{
sec:12.1
}
节中已经介绍,自注意力机制中至关重要的是获取相关性系数,也就是在融合不同位置的表示向量时各位置的权重。
Transformer模型采用了一种基于点乘的方法来计算相关性系数。这种方法也称为
{
\small\bfnew
{
缩放的点乘注意力
}}
\index
{
缩放的
点乘注意力
}
(Scaled Dot-Product Attention)
\index
{
Scaled Dot-Product Attention
}
机制。它的运算并行度高,同时并不消耗太多的存储空间。
\parinterval
具体来看,在注意力机制的计算过程中,包含三个重要的参数,分别是Query,
\\
Key和Value。在下面的描述中,分别用
$
\vectorn
{
\emph
{
Q
}}$
,
$
\vectorn
{
\emph
{
K
}}$
,
$
\vectorn
{
\emph
{
V
}}$
对它们进行表示,其中
$
\vectorn
{
\emph
{
Q
}}$
和
$
\vectorn
{
\emph
{
K
}}$
的维度为
$
L
\times
d
_
k
$
,
$
\vectorn
{
\emph
{
V
}}$
的维度为
$
L
\times
d
_
v
$
。这里,
$
L
$
为序列的长度,
$
d
_
k
$
和
$
d
_
v
$
分别表示每个Key和Value的大小,通常设置为
$
d
_
k
=
d
_
v
=
d
_{
model
}$
。
\parinterval
在自注意力机制中,
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
、
$
\vectorn
{
\emph
{
V
}}$
都是相同的,对应着源语言或目标语言的表示。而在编码-解码注意力机制中,由于要对双语之间的信息进行建模,因此,将目标语每个位置的表示视为编码-解码注意力机制的
$
\vectorn
{
\emph
{
Q
}}$
,源语言句子的表示视为
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
。
\parinterval
在自注意力机制中,
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
、
$
\vectorn
{
\emph
{
V
}}$
都是相同的,对应着源语言或目标语言
序列
的表示。而在编码-解码注意力机制中,由于要对双语之间的信息进行建模,因此,将目标语每个位置的表示视为编码-解码注意力机制的
$
\vectorn
{
\emph
{
Q
}}$
,源语言句子的表示视为
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
。
\parinterval
在得到
$
\vectorn
{
\emph
{
Q
}}$
,
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
后,便可以进行注意力机制的运算,这个过程可以被形式化为:
\begin{eqnarray}
...
...
@@ -289,9 +285,11 @@
\label
{
eq:12-47
}
\end{eqnarray}
\noindent
首先,通过对
$
\vectorn
{
\emph
{
Q
}}$
和
$
\vectorn
{
\emph
{
K
}}$
的转置进行点乘操作,计算得到一个维度大小为
$
L
\times
L
$
的相关性矩阵,即
$
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
K
}}^{
T
}$
,它表示一个序列上任意两个位置的相关性。再通过系数1/
$
\sqrt
{
d
_
k
}$
进行放缩操作,放缩可以尽量减少相关性矩阵的方差,具体体现在运算过程中实数矩阵中的数值不会过大,有利于模型训练。
\noindent
首先,通过对
$
\vectorn
{
\emph
{
Q
}}$
和
$
\vectorn
{
\emph
{
K
}}$
的转置进行点乘操作,计算得到一个维度大小为
$
L
\times
L
$
的相关性矩阵,即
$
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
K
}}^{
T
}$
,它表示一个序列上任意两个位置的相关性。再通过系数1/
$
\sqrt
{
d
_
k
}$
进行放缩操作,放缩可以减少相关性矩阵的方差,具体体现在运算过程中实数矩阵中的数值不会过大,有利于模型训练。
\parinterval
在此基础上,通过对相关性矩阵累加一个掩码矩阵
$
\vectorn
{
\emph
{
Mask
}}$
,来屏蔽掉矩阵中的无用信息。比如,在编码端,如果需要对多个句子同时处理,由于这些句子长度不同意,需要对句子的补齐。再比如,在解码端,训练的时候需要屏蔽掉当前目标语位置右侧的单词,因此这些单词在推断的时候是看不到的。
\parinterval
在此基础上,通过对相关性矩阵累加一个掩码矩阵,来屏蔽掉矩阵中的无用信息。比如,在编码端对句子的补齐,在解码端则屏蔽掉未来信息,这一部分内容将在下一小节进行详细介绍。随后,使用Softmax函数对相关性矩阵在行的维度上进行归一化操作,这可以理解为对第
$
i
$
行进行归一化,结果对应了
$
\vectorn
{
\emph
{
V
}}$
中不同位置上向量的注意力权重。对于
$
\mathrm
{
value
}$
的加权求和,可以直接用相关性系数和
$
\vectorn
{
\emph
{
V
}}$
进行矩阵乘法得到,即
$
\textrm
{
Softmax
}
\parinterval
随后,使用Softmax函数对相关性矩阵在行的维度上进行归一化操作,这可以理解为对第
$
i
$
行进行归一化,结果对应了
$
\vectorn
{
\emph
{
V
}}$
中不同位置上向量的注意力权重。对于
$
\mathrm
{
value
}$
的加权求和,可以直接用相关性系数和
$
\vectorn
{
\emph
{
V
}}$
进行矩阵乘法得到,即
$
\textrm
{
Softmax
}
(
\frac
{
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
K
}}^{
T
}}
{
\sqrt
{
d
_
k
}}
+
\vectorn
{
\emph
{
Mask
}}
)
$
和
$
\vectorn
{
\emph
{
V
}}$
进行矩阵乘。最终得到自注意力的输出,它和输入的
$
\vectorn
{
\emph
{
V
}}$
的大小是一模一样的。图
\ref
{
fig:12-45
}
展示了点乘注意力计算的全过程。
%----------------------------------------------
...
...
@@ -303,7 +301,7 @@
\end{figure}
%----------------------------------------------
\parinterval
下面举个简单的例子介绍点乘注意力的具体计算过程。如图
\ref
{
fig:12-46
}
所示,用黄色、蓝色和橙色的矩阵分别表示
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
。
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
中的每一个小格都对应一个单词在模型中的表示(即一个向量)。首先,通过点乘、放缩、掩码等操作得到相关性矩阵,即粉色部分。其次,将得到的中间结果矩阵(粉色)的每一行使用Softmax激活函数进行归一化操作,得到最终的权重矩阵,也就是图中的红色矩阵。红色矩阵中的每一行都对应一个注意力分布。最后,按行对
$
\vectorn
{
\emph
{
V
}}$
进行加权求和,便得到了每个单词通过点乘注意力机制计算得到的表示。这里面,主要的计算消耗是两次矩阵乘法,即
$
\vectorn
{
\emph
{
Q
}}$
与
$
\vectorn
{
\emph
{
K
}}^{
T
}$
的乘法、相关性矩阵和
$
\vectorn
{
\emph
{
V
}}$
的乘法。这两个操作都可以在GPU上高效地完成,因此可以一次性计算出序列中所有单词之间的注意力权重,并完成所有位置表示的加权求和过程,这样大大提高了模型
的计算速
度。
\parinterval
下面举个简单的例子介绍点乘注意力的具体计算过程。如图
\ref
{
fig:12-46
}
所示,用黄色、蓝色和橙色的矩阵分别表示
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
。
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
和
$
\vectorn
{
\emph
{
V
}}$
中的每一个小格都对应一个单词在模型中的表示(即一个向量)。首先,通过点乘、放缩、掩码等操作得到相关性矩阵,即粉色部分。其次,将得到的中间结果矩阵(粉色)的每一行使用Softmax激活函数进行归一化操作,得到最终的权重矩阵,也就是图中的红色矩阵。红色矩阵中的每一行都对应一个注意力分布。最后,按行对
$
\vectorn
{
\emph
{
V
}}$
进行加权求和,便得到了每个单词通过点乘注意力机制计算得到的表示。这里面,主要的计算消耗是两次矩阵乘法,即
$
\vectorn
{
\emph
{
Q
}}$
与
$
\vectorn
{
\emph
{
K
}}^{
T
}$
的乘法、相关性矩阵和
$
\vectorn
{
\emph
{
V
}}$
的乘法。这两个操作都可以在GPU上高效地完成,因此可以一次性计算出序列中所有单词之间的注意力权重,并完成所有位置表示的加权求和过程,这样大大提高了模型
计算的并行
度。
%----------------------------------------------
\begin{figure}
[htp]
...
...
@@ -326,11 +324,11 @@
\begin{itemize}
\vspace
{
0.5em
}
\item
首先将
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
、
$
\vectorn
{
\emph
{
V
}}$
分别通过线性变换的方式映射为
$
h
$
个子集(机器翻译任务中,
$
h
$
一般为8)。即
$
\vectorn
{
\emph
{
q
}}_
i
=
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
W
}}_
i
^
Q
$
、
$
\vectorn
{
\emph
{
k
}}_
i
=
\vectorn
{
\emph
{
K
}}
\vectorn
{
\emph
{
W
}}_
i
^
K
$
、
$
\vectorn
{
\emph
{
v
}}_
i
=
\vectorn
{
\emph
{
V
}}
\vectorn
{
\emph
{
W
}}_
i
^
V
$
,其中
$
i
$
表示第
$
i
$
个头,
$
\vectorn
{
\emph
{
W
}}_
i
^
Q
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
k
}$
,
$
\vectorn
{
\emph
{
W
}}_
i
^
K
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
k
}$
,
$
\vectorn
{
\emph
{
W
}}_
i
^
V
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
v
}$
是参数矩阵;
$
d
_
k
=
d
_
v
=
d
_{
model
}
/
h
$
,对于不同的头采用不同的变换矩阵,这里
$
d
_{
model
}$
是Transformer的一个参数,
表示每个隐层向量的维度;
\item
首先,将
$
\vectorn
{
\emph
{
Q
}}$
、
$
\vectorn
{
\emph
{
K
}}$
、
$
\vectorn
{
\emph
{
V
}}$
分别通过线性(Linear)变换的方式映射为
$
h
$
个子集。即
$
\vectorn
{
\emph
{
q
}}_
i
=
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
W
}}_
i
^
Q
$
、
$
\vectorn
{
\emph
{
k
}}_
i
=
\vectorn
{
\emph
{
K
}}
\vectorn
{
\emph
{
W
}}_
i
^
K
$
、
$
\vectorn
{
\emph
{
v
}}_
i
=
\vectorn
{
\emph
{
V
}}
\vectorn
{
\emph
{
W
}}_
i
^
V
$
,其中
$
i
$
表示第
$
i
$
个头,
$
\vectorn
{
\emph
{
W
}}_
i
^
Q
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
k
}$
,
$
\vectorn
{
\emph
{
W
}}_
i
^
K
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
k
}$
,
$
\vectorn
{
\emph
{
W
}}_
i
^
V
\in
\mathbb
{
R
}^{
d
_{
model
}
\times
d
_
v
}$
是参数矩阵;
$
d
_
k
=
d
_
v
=
d
_{
model
}
/
h
$
,对于不同的头采用不同的变换矩阵,这里
$
d
_{
model
}$
表示每个隐层向量的维度;
\vspace
{
0.5em
}
\item
其次对每个头分别执行点乘注意力操作,并得到每个头的注意力操作的输出
$
\vectorn
{
\emph
{
head
}}_
i
$
;
\item
其次
,
对每个头分别执行点乘注意力操作,并得到每个头的注意力操作的输出
$
\vectorn
{
\emph
{
head
}}_
i
$
;
\vspace
{
0.5em
}
\item
最后
将
$
h
$
个头的注意力输出在最后一维
$
d
_
v
$
进行拼接(Concat)重新得到维度为
$
h
\times
d
_
v
$
的输出,并通过对其左乘一个权重矩阵
$
\vectorn
{
\emph
{
W
}}^
o
$
进行线性变换,从而对多头计算得到的信息进行融合,且将多头注意力输出的维度映射为模型的隐层大小(即
$
d
_{
model
}$
),这里参数矩阵
$
\vectorn
{
\emph
{
W
}}^
o
\in
\mathbb
{
R
}^{
h
\times
d
_
v
\times
d
_{
model
}}$
。
\item
最后,
将
$
h
$
个头的注意力输出在最后一维
$
d
_
v
$
进行拼接(Concat)重新得到维度为
$
h
\times
d
_
v
$
的输出,并通过对其左乘一个权重矩阵
$
\vectorn
{
\emph
{
W
}}^
o
$
进行线性变换,从而对多头计算得到的信息进行融合,且将多头注意力输出的维度映射为模型的隐层大小(即
$
d
_{
model
}$
),这里参数矩阵
$
\vectorn
{
\emph
{
W
}}^
o
\in
\mathbb
{
R
}^{
h
\times
d
_
v
\times
d
_{
model
}}$
。
\vspace
{
0.5em
}
\end{itemize}
...
...
@@ -343,7 +341,7 @@
\end{figure}
%----------------------------------------------
\parinterval
多头机制
具体的计算公式如下
:
\parinterval
多头机制
可以被形式化描述为如下公式
:
\begin{eqnarray}
\textrm
{
MultiHead
}
(
\vectorn
{
\emph
{
Q
}}
,
\vectorn
{
\emph
{
K
}}
,
\vectorn
{
\emph
{
V
}}
)
&
=
&
\textrm
{
Concat
}
(
\vectorn
{
\emph
{
head
}}_
1, ... ,
\vectorn
{
\emph
{
head
}}_
h )
\vectorn
{
\emph
{
W
}}^
o
\label
{
eq:12-48
}
\\
\vectorn
{
\emph
{
head
}}_
i
&
=
&
\textrm
{
Attention
}
(
\vectorn
{
\emph
{
Q
}}
\vectorn
{
\emph
{
W
}}_
i
^
Q ,
\vectorn
{
\emph
{
K
}}
\vectorn
{
\emph
{
W
}}_
i
^
K ,
\vectorn
{
\emph
{
V
}}
\vectorn
{
\emph
{
W
}}_
i
^
V )
...
...
@@ -358,20 +356,20 @@
\subsection
{
掩码操作
}
\parinterval
在公式
\ref
{
eq:12-47
}
中提到了
Mask(掩码),它的目的是对向量中某些值进行掩盖,避免无关位置的数值对运算造成影响。Transformer中的Mask主要应用在注意力机制中的相关性系数计算,具体方式是在相关性系数矩阵上累加一个Mask矩阵。该矩阵在需要Mask的位置的值为负无穷-inf(具体实现时是一个非常小的数,比如-1e-9),其余位置为0,这样在进行了Softmax归一化操作之后,被掩码掉的位置计算得到的权重便近似为0,也就是说对无用信息分配的权重为0,从而避免了其对结果产生影响。Transformer包含两种Mask
:
\parinterval
在公式
\ref
{
eq:12-47
}
中提到了
掩码(Mask),它的目的是对向量中某些值进行掩盖,避免无关位置的数值对运算造成影响。Transformer中的掩码主要应用在注意力机制中的相关性系数计算,具体方式是在相关性系数矩阵上累加一个掩码矩阵。该矩阵在需要掩码的位置的值为负无穷
$
-
$
inf(具体实现时是一个非常小的数,比如
$
-
$
1e-9),其余位置为0,这样在进行了Softmax归一化操作之后,被掩码掉的位置计算得到的权重便近似为0,也就是说对无用信息分配的权重为0,从而避免了其对结果产生影响。Transformer包含两种掩码
:
\begin{itemize}
\vspace
{
0.5em
}
\item
Padding Mask。在批量处理多个样本时(训练或解码),由于要对源语言和目标语言的输入进行批次化处理,而每个批次内序列的长度不一样,为了方便对批次内序列进行矩阵表示,需要进行对齐操作,即在较短的序列后面填充0来占位(padding操作)。而这些填充的位置没有意义,不参与注意力机制的计算,因此,需要进行Mask
操作,屏蔽其影响。
\item
{
\small\bfnew
{
句长补全掩码
}}
\index
{
句长补全掩码
}
(Padding Mask
\index
{
Padding Mask
}
)。在批量处理多个样本时(训练或解码),由于要对源语言和目标语言的输入进行批次化处理,而每个批次内序列的长度不一样,为了方便对批次内序列进行矩阵表示,需要进行对齐操作,即在较短的序列后面填充0来占位(padding操作)。而这些填充的位置没有意义,不参与注意力机制的计算,因此,需要进行掩码
操作,屏蔽其影响。
\vspace
{
0.5em
}
\item
Future Mask。对于解码器来说,由于在预测的时候是自左向右进行的,即第
$
t
$
时刻解码器的输出只能依赖于
$
t
$
时刻之前的输出。且为了保证训练解码一致,避免在训练过程中观测到目标语端每个位置未来的信息,因此需要对未来信息进行屏蔽。具体的做法是:构造一个上三角值全为-inf的Mask矩阵,也就是说,在解码端计算中,在当前位置,通过Future Mask把序列之后的信息屏蔽掉了,避免了
$
t
$
时刻之后的位置对当前的计算产生影响。图
\ref
{
fig:12-47
}
给出了一个具体的实例。
\item
{
\small\bfnew
{
未来信息掩码
}}
\index
{
未来信息掩码
}
(Future Mask
\index
{
Future Mask
}
)。对于解码器来说,由于在预测的时候是自左向右进行的,即第
$
t
$
时刻解码器的输出只能依赖于
$
t
$
时刻之前的输出。且为了保证训练解码一致,避免在训练过程中观测到目标语端每个位置未来的信息,因此需要对未来信息进行屏蔽。具体的做法是:构造一个上三角值全为-inf的Mask矩阵,也就是说,在解码端计算中,在当前位置,通过未来信息掩码把序列之后的信息屏蔽掉了,避免了
$
t
$
时刻之后的位置对当前的计算产生影响。图
\ref
{
fig:12-47
}
给出了一个具体的实例。
%----------------------------------------------
% 图3.10
\begin{figure}
[htp]
\centering
\input
{
./Chapter12/Figures/figure-mask-instance-for-future-positions-in-transformer
}
\caption
{
Transformer中对于未来位置进行的屏蔽的
Mask
实例
}
\caption
{
Transformer中对于未来位置进行的屏蔽的
掩码
实例
}
\label
{
fig:12-47
}
\end{figure}
%----------------------------------------------
...
...
@@ -387,7 +385,7 @@
\parinterval
Transformer编码器、解码器分别由多层网络组成(通常为6层),每层网络又包含多个子层(自注意力网络、前馈神经网络)。因此Transformer实际上是一个很深的网络结构。再加上前面介绍的点乘注意力机制,包含很多线性和非线性变换;另外,注意力函数Attention(
$
\cdot
$
)的计算也涉及多层网络,整个网络的信息传递非常复杂。从反向传播的角度来看,每次回传的梯度都会经过若干步骤,容易产生梯度爆炸或者消失。
\parinterval
解决这个问题的一种办法就是使用
{
\small\sffamily\bfseries
{
残差连接
}}
\index
{
残差连接
}
\upcite
{
DBLP:journals/corr/HeZRS15
}
。残差连接是一种用来训练深层网络的技术,其结构如图
\ref
{
fig:12-49
}
,即在子层之前通过增加直接连接的方式,将底层信息直接传递给上层。
\parinterval
解决这个问题的一种办法就是使用
{
\small\sffamily\bfseries
{
残差连接
}}
\index
{
残差连接
}
(Residual Connection
\index
{
Residual Connection
}
)
\upcite
{
DBLP:journals/corr/HeZRS15
}
。残差连接是一种用来训练深层网络的技术,其结构如图
\ref
{
fig:12-49
}
,即在子层之前通过增加直接连接的方式,将底层信息直接传递给上层。
%----------------------------------------------
\begin{figure}
[htp]
...
...
@@ -404,7 +402,7 @@ x_{l+1} = x_l + \mathcal{F} (x_l)
\label
{
eq:12-50
}
\end{eqnarray}
\noindent
其中
$
\mathcal
{
F
}
(
x
_
l
)
$
是子层运算。如果
$
l
=
2
$
,那么公式
\ref
{
eq:12-50
}
可以解释为,第3层的输出等于第2层的输出加上第二层的输入。图
\ref
{
fig:12-50
}
中的红色方框展示了Transformer
中残差连接的位置。
\noindent
其中
,
$
x
_
l
$
表示
$
l
$
层网络的输入向量,
$
\mathcal
{
F
}
(
x
_
l
)
$
是子层运算。如果
$
l
=
2
$
,那么公式
\ref
{
eq:12-50
}
可以解释为,第3层的输入(
$
x
_
3
$
)等于第2层的输出(
$
\mathcal
{
F
}
(
x
_
2
)
$
)加上第二层的输入(
$
x
_
2
$
)。图
\ref
{
fig:12-50
}
中的红色方框展示了Transformer
中残差连接的位置。
%----------------------------------------------
\begin{figure}
[htp]
...
...
@@ -451,7 +449,7 @@ x_{l+1} = x_l + \mathcal{F} (x_l)
\end{figure}
%----------------------------------------------
\parinterval
Transformer使用了全连接网络。全连接网络的作用主要体现在将经过注意力操作之后的表示映射到新的空间中,新的空间会有利于接下来的非线性变换等操作。实验证明,去掉全连接网络会对模型的性能造成影响。Transformer的全连接前馈神经网络包含两次线性变换和一次非线性变换(ReLU激活函数:ReLU
$
(
x
)=
\textrm
{
max
}
(
0
,x
)
$
),每层的前馈神经网络参数不共享,计算公式如下:
\parinterval
Transformer使用了全连接网络。全连接网络的作用主要体现在将经过注意力操作之后的表示映射到新的空间中,新的空间会有利于接下来的非线性变换等操作。实验证明,去掉全连接网络会对模型的性能造成
很大
影响。Transformer的全连接前馈神经网络包含两次线性变换和一次非线性变换(ReLU激活函数:ReLU
$
(
x
)=
\textrm
{
max
}
(
0
,x
)
$
),每层的前馈神经网络参数不共享,计算公式如下:
\begin{eqnarray}
\textrm
{
FFN
}
(x) =
\textrm
{
max
}
(0,
\vectorn
{
\emph
{
x
}}
\vectorn
{
\emph
{
W
}}_
1 +
\vectorn
{
\emph
{
b
}}_
1)
\vectorn
{
\emph
{
W
}}_
2 +
\vectorn
{
\emph
{
b
}}_
2
\label
{
eq:12-52
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论