【李宏毅机器学习】Transformer 内容补充

视频来源:10.【李宏毅机器学习2021】自注意力机制 (Self-attention) (上)_哔哩哔哩_bilibili

发现一个奇怪的地方,如果直接看ML/DL的课程的话,有很多都是不完整的。开始思考是不是要科学上网。

本文用作Transformer - Attention is all you need 论文阅读-CSDN博客的补充内容,因为发现如果实操还是有不能理解的地方,所以准备看看宝可梦老师怎么说×

Self-attention

引入

到目前为止,我们的network的input都是一个向量,输出可能是一个数值(regression)或者类别(classification)。但是假设我们遇到更复杂的问题呢?如果输入是一排向量,并且输入向量的长度是会改变的呢?

(老师提到在做图像分类任务的时候,假设输入图像的大小是一致的,但是实际上,我们可以不指定卷积核的大小,而通过我们期待的feature map的大小和输入图片的大小来反推卷积核的大小,感兴趣的话可以参考

SPP(Spatial Pyramid Pooling)网络 - 知乎 (zhihu.com)
CNN 在分类图片时图片大小不一怎么办? - 知乎 (zhihu.com)
[1406.4729] Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition (arxiv.org)当然在SPP出现之前一般采用的策略是裁剪or缩放or填充padding)

假设我们每次输入sequence的数目or长度不一样,怎么办?

但是什么例子是输入的是sequence并且长度会改变呢

比如文字处理。假如我们现在要输入的是一个句子,因为每个句子的长度都不一样(句子中词汇的数目不一样),如果把句子里面的每一个词汇都描述成一个向量,那么model的输入就会是一个vector set,并且这个vector set的大小不一样。

怎么把词汇表示成一个向量呢?最简单的方法是one-hot encoding。搞一个很长很长的向量,向量的长度就是世界上所有的词的数目,每一个位置对应到一个词汇。但是,这种方法有一个问题->假设所有的词汇彼此之间是没有关系的,我们看不出来cat和dog都是动物,cat和Apple一个是动物一个是食物就不太相像->没有语义信息。

另一个方法是Word embedding,这里给每一个词汇的向量就包含了语义的信息,如果画出来的话,会看到相似的概念都团在一起。

一个句子就是一排长度不一的向量

还有一个例子就是声音信号。一段声音信号,取一个范围(Window),把这个Window里面的信息描述成一个向量(frame),这个Window的长度是25ms,有各种各样的方法把声音信号转成frame,然后移动window(通常为10ms)->1s的声音信号又100个frame

还有一个例子是,图。

比如social network就是一个graph,在social network上面每一个节点就是一个人,节点和节点之间的edge就是这两个人的关系,每一个节点都可以看做是一个向量(可以拿每个人的profile的信息用向量表示)

应用:drug discovery

把分子当做模型的输入,一个分子就是一个向量,用one-hot表示原子。

输出是什么?

1. 每一个向量都有一个label
        当我们的模型看到输入是4个向量的时候,就要输出4个label,如果label是数值,就是regression问题,如果是class就是classification。
        文字处理可能会遇到,比如词性标注,是名词、动词、形容词……
        语音,每一个vector都要决定是哪一个Phoneme
        给一个social network,决定每一个节点有什么特性,比如会不会买某个商品

2. 一个sequence(有很多的vector)只需要一个输出就好了
        比如sentiment analysis,情感分析,即给机器看一段话,让机器判断这一段话是积极地还是消极的->一整个句子只需要一个label。
       辨认说话的人
        graph,预测一个分子有没有毒性?亲水性怎么样?

3. 不知道输出多少个label,机器需要自己决定要输出多少个label(称为seq2seq)
        比如机器翻译,语音识别

本节只focus on 第一个类别,每个vector都输出一个label

这种输入和输出数目一样多的情况又称sequence labeling。

怎么解决这种sequence labeling的问题呢?比较直觉的方法是拿一个fully connected network,虽然输入是一个sequence,我们就直接各个击破,把每一个向量分别输入到fully connected network里面,然后产生正确的输出就OK了。

但是这么做有很大的问题。比如我们要做的是词性标注的任务,比如I saw a saw,对于fully connected network来说,两个saw完全一模一样啊,输出同样的词汇,没可能输出不同的结果啊,但是实际上第一个saw是动词,第二个saw是名词->有没有可能让fully connected network考虑更多的上下文?

有可能的。

把当前向量和前后几个向量都串起来(window),一起丢到fully connected network里面去。

但是这个方法还是有局限,如果我们现在的任务不是考虑一个window就可以解决的,而是要考虑整个sequence呢?可以直接把window开大一点,包含整个句子吗?但是sequence的长度是不确定的,有长有短的。同时开一个超大的window意味着我们的fully connected network需要很多的参数,运算量很大,说不准还容易overfitting。

(弹幕:rnn考虑的是前一个隐藏状态,但是隐藏状态又包含之前的信息

RNN本质上只有短时的记忆,其实也就是前几个输入的信息,再往前的信息根本记不住了已经)

更好的方法->self-attention

self-attention的运作方式是,会吃一整个sequence的信息,然后input几个vector就输出几个vector。比如图中input4个vector就output4个vector。输出的4个vector有什么特别的地方呢?这4个vector都是考虑整个sequence之后才得到的,再把这些考虑了整个sequence的向量丢到fully connected network,再决定最后的output。

现在fully connected network就不是只考虑一个非常小的范围,而是考虑整个sequence的信息再决定输出什么样的结果。

self-attention不是只能用一次,可以叠加很多次。比如之前self-attention的输出经过fully connected network之后的输出再当做输入给另一个self-attention吃,最后再丢给另一个fully connected network,得到输出。

->可以把fully connected network(FC)和self-attention交替使用。self-attention处理整个sequence的信息,FC专注于处理某一个位置的信息

有关self-attention最知名的文章《变形金刚》(×)

self-attention是如何运作的呢?

self-attention的input是一串的vector,这个vector可能是整个network的input,也可能是某个hidden layer的output(所以这里用a表示->表示前面可能已经做过一些处理了)

input一排a向量之后,self-attention要output一排b向量,每一个b都是考虑了所有的a以后才生成出来的。

举例说明,怎么产生b^1向量。

第一个步骤是,根据a^1找出这个sequence里面其他和a^1相关的向量(我们做self-attention的目的是为了考虑整个sequence,但是我们又不想把整个sequence的信息都包在一个window里面)->找出哪些部分对a^1是重要的。

每一个向量和a^1关联的程度,用一个数值α表示。现在给两个向量,比如a^1和a^4,怎么计算这两个有多相关?

(弹幕:注意力就是权重)

怎么计算attention

用两个向量当做输入,直接输出α。那么怎么计算α的数值呢?比较常见的做法是dot-product。

把输入的两个向量分别乘上两个不同的矩阵,图中就是左侧的向量乘以W^q这个矩阵,右侧的向量乘以W^k这个向量。

带入一下刚刚的例子就是:

a^1\cdot W^q

a^4 \cdot W^k

得到q和k两个向量:

q = a^1\cdot W^q

k = a^4 \cdot W^k

再把q和k两个向量做点乘(element)得到一个scalar,这个scalar就是α

\alpha = q \cdot k = (a^1 \cdot W^q) \cdot (a^4 \cdot W^k )

除此之外,还有其他的方式,比如右侧的additive方法,这里是把q和k加起来,过一个activation function,再过一个transform得到α

后续的讨论都focus on dot-product方法(常用)了。

知道单个的计算方法之后,我们就要拿a^1和后面的a^2 a^3 a^4都分别计算关联性(α)。

也就是我们先把W^q乘上a^1得到q^1,这个q^1我们叫做query,就像是我们使用搜索引擎的时候,用来搜的关键字。

q^1 = W^qa^1

剩下a^2 a^3 a^4都要给乘上W^k,得到key。

k^2 = W^ka^2

把q^1和k^2算inner product得到alpha

\alpha_{1,2}=q^1 \cdot k^2

这里用1,2表示query是1提供的,key是2提供的,这个α称为attention score。

同样的方法我们也要对a^3 a^4做,得到相应的key

在实际操作的时候a^1也会算和自己的关联性(还有k^1)

我们计算出a^1和每一个向量的关联性之后,会做一个softmax(这个softmax和分类的时候用的softmax是一模一样的)exp(α),然后在normalize(除以所有exp的和)

softmax的输出就是一排α'

为什么用softmax呢

不一定要用softmax,用别的激活函数也完全没问题,只是softmax很常用(常用就说明softmax肯定打败了其他的)

怎么抽取信息

得到α'以后就要根据α'去抽取sequence里面重要的信息。根据α,我们已经知道哪些向量和a^1是最相关的,接下来我们要根据关联性(根据attention的分数),来抽取重要的信息。

那么怎么抽取信息呢?

把a^1 a^2 a^3 a^4每一个向量乘上W^v,得到新的向量,即v^1 v^2 v^3 v^4

接下来,把得到的v^1 v^2 v^3 v^4每一个都分别乘对应的α值(attention的分数),然后再加起来

(弹幕:这不就解释了为啥用softmax了 比例均匀 好加权
所以是因为QVK都是来自自己,所以叫自注意力吗
基于注意力分数获取信息
之前Q,K是经过非线性变换的,主要为了学习不同位置下的关系)

可以想象到,如果某一个向量(比如a^2)得到的分数越高(与a^1关联性很大),那么我们在做weighted sum之后得到的结果就可能比较接近v^2

->谁的attention分数越高,谁的v就会dominate抽出来的结果

 

现在我们就从一排vector得到了b^1

注意b^1 b^2 b^3 b^4的计算并没有先后关系,是同时被计算出来的

完整的计算流程

现在以a^2为例。

a^2会乘上一个matrix变成q^2

接下来会根据q^2对a^1 a^2 a^3 a^4四个位置计算attention的score(方法,拿q^2和k^i做dot-product),得到四个分数α。

得到四个分数之后,做一个normalization(比如softmax),得到最后的attention score α'

得到attention score α'四个数值之后,分别乘上v^1 v^2 v^3 v^4

全部加起来得到b^2

(弹幕:qkv的值是乘上qkv分别对应的权重矩阵得到的,这些权重矩阵就是神经网络要训练的参数)

_______________________________________

以上是dot-product运算的过程

以下是从矩阵乘法的角度看dot-product

——————————————————————

现在我们已经知道a^1 a^2 a^3 a^4每个都要分别产生q k v,如果用矩阵运算表示这个操作:

q^i=W^qa^i

\begin{bmatrix} q^1 &q^2 &q^3 &q^4 \end{bmatrix} =W^q \begin{bmatrix} a^1 &a^2 &a^3 &a^4 \end{bmatrix}

这里W^q是矩阵的参数,等下会learn出来的

同理,我们就会得到K和 V

得到Q K V之后,就是每一个Q都会和每一个K去dot-product,计算attention的分数。

所以就是k^1和q^1做inner product得到α_{1,1}

\alpha_{1,1} = \begin{bmatrix} k_{0}^1 &k_{1}^1 &...... & k_{n}^1 \end{bmatrix} \cdot \begin{bmatrix} q_{0}^1\\ q_{1}^1\\ ...\\ q_{n}^1 \end{bmatrix}

(介绍一下annotation,这里:

\alpha_{1,1}:表示的是a^1的query和a^1的key算出来的attention score
k_{0}^1:表示的是这是k^1向量中的第一个数字
k_{n}^1:表示的是这是k^1向量中 到最后一个数字,并且指明k^1向量的长度为n
q_{0}^1:表示的是这是q^1向量中的第一个数字
q_{n}^1:表示的是这是q^1向量中 到最后一个数字,并且指明q^1向量的长度为n)

类似的,我们可以把四个分数都表示出来

因此就可以看做是把k^1 k^2 k^3 k^4拼起来和q^1相乘

同理,其他也要算attention

(弹幕:本质上是将所有过程转换成矩阵乘法,然后通过比如动态规划等算法进行加速,实现中通常是由gpu负责)

然后对分数做normalization

下一步就是得到b^1,就是拿出每个a对应的分数,乘以v的值

同理,得到所有的vector b

 

->其实一连串的操作就是一连串的矩阵乘法而已

只要learn三个矩阵就好啦

Multi-head Self-attention

例子

self-attention有一个进阶的版本叫做multi-head self-attention。有的时候多一点head能得到更好的结果(但是具体用几个head,这又是另外一个hyperparameter了,需要咱们自己调啦)

为什么需要比较多的head呢?

我们在做self-attention的时候,我们就是用q去找相关的k,但是相关有很多不同的定义。所以……也许我们不能只有一个q,用不同的q去负责不同种类的相关性。

所以在做multi-head的时候,我们先把a乘上一个矩阵得到q,接下来再把q成上另外两个矩阵,分别得到q^1和q^2(图中采用的是q^{i,1},这里i代表位置,1和2代表是这个位置的第几个q)。代表有两个head->认为这个问题里面有两种不同的相关性,来找两种不同的相关性。

同理,k和v也有两个(方法就是得到k v之后再分别乘以两个矩阵,得到k^{i,1} k^{i,2} v^{i,1} v^{i,2})

同理对另一个位置j我们也做相同的操作。

下一步是做self-attention。

现在的idea是属于类别1的一起做,属于类别2的一起做(看上标是1还是2)。比如q^{i,1}在计算的时候就只关注k^{i,1} k^{j,1},做dot-product,得到分数。

之后也不用管2,只看1类的v

同理对第2类也做相同的操作

接下来,我们可能会把b^{i,1} b^{i,2}接起来,乘上一个矩阵得到b^i,再送到下一层去

Positional encoding

到目前为止,这个self-attention的layer少了一个可能很重要的信息,位置信息。对一个self-attention layer而言,每一个input是出现在sequence的哪个位置是不知道的。对于self-attention layer来说它并不觉得a^1和a^4就差很远,a^2和a^3就挨着,对它来说所有位置之间的距离都是一样的。

但是位置信息也许很重要啊,比如在做词性标记的时候,可能动词不太容易出现在句首。

所以在做self-attention的时候,如果觉得位置的信息是有用的,可以把位置的信息塞进去。

这里采用的技术称为positional encoding。为每一个位置设定一个vector(称为positional vector,用 e^i表示,i代表位置),每一个不同的位置就有不同的vector。把这个e加到a^i上就可以了 

这个positional vector是人设的,所以可能有问题,比如我只设定到128,但是新进来的sequence的长度是129。不过在attention is all you need里面已经没有这个问题了,因为其vector是通过一个规则产生的(是一个sin cos的function)

不过具体怎么做好,还尚待研究(到课程的时间)

应用

也可以用在语音上,如果想把语音信号表示成一个向量,那么这个向量可能非常的长(指向量个数很多),可能会带来计算上的困难(矩阵太大啦)

所以可能需要改改。

这里采用的是truncated self-attention,就是说我们现在不看一整句话,而是看一个范围,具体范围多大,这个是人设定的

还可以被用在图像上。

把每一个位置上的pixel看成是一个三维的向量,所以这里,整个图片就是5×10个向量。

比如:

self-attention v.s. CNN

假设现在我们用self-attention来处理一张图片,假设现在上面的红框框的地方是我们要考虑pixel,那么这个红框产生query,其他(包括其本身)产生key,在做inner product的时候,考虑的就是整张图片。

但是我们在做CNN的时候,会画出一个receptive field,每一个neuron只考虑这个receptive field里面的信息。

->CNN可以看做是一个简化版本的self-attention,只考虑receptive field里面的信息

self-attention可以看做是复杂化的CNN,CNN中receptive field的范围和大小是人决定的,attention就像是receptive field是自己学出来的

CNN其实是self-attention的特例,只要设定合适的参数,self-attention就可以做到和CNN一样的事情(具体数学推理参考图片中的论文)

越flexible的model比较需要更多的data,如果data不够就容易造成overfitting。比较小的/有限制的model就比较适合在data少的时候,如果限制设置的好也会有不错的结果。

self-attention v.s. RNN

RNN基本可以被self-attention取代(什么前浪死在沙滩上的悲惨故事)

https://leemeng.tw/neural-machine-translation-with-transformer-and-tensorflow2.html
提供了一些很好的动图

一个非常显而易见的区别是RNN只考虑了左边的vector,没有看到右边的vector,但是实际上RNN也可以用双向的,这样RNN也可以看到右边的情况

但是对于RNN来说,如果想要最后一个vector还记得第一个输入的vector的信息,就要把这个输入的信息存在memory里面。但是self-attention就完全不需要,它可以自己抽取信息。

另外RNN是没办法平行处理所有的output的。想输出最后一个vector,就要等前面的vector都运算完。

self-attention for Graph

graph也可以看做是一堆vector。

在graph中,我们不止有node(每一个node都可以表示成一个向量),还有edge的信息,需要知道哪些向量是相连的。之前在做self-attention的时候,关联是network自己找出来的,那么这里可能就不需要自己去找。(什么邻接矩阵?)

所以现在完全可以只计算有相连的node的分数。

如果没有向量,就意味着没有关系,没有关系就不用算score了,直接设为0就好了。

self-attention的变形

Transformer

引入

Transformer就是一个seq2seq(input是一个sequence,output也是一个sequence,但是我们不知道output的长度->output的长度由model自己决定)的model

有哪些应用?

语音识别。输入是声音信号,输出是语音识别的结果(输入的声音信号所对应的文字)

机器翻译。读入一个语言的句子,输出另一种语言的句子

语音翻译。输入一段声音信号,直接输出另一种语言的文字
为什么要做语音翻译呢?直接吧上面的语音识别接上机器翻译不可以吗?实际上有很多语言是没有文字的,对于这些没有文字的语言根本没办法做语音识别。

语音合成(语音识别的反面)

聊天机器人(Chatbox)

事实上,seq2seq的model在NLP领域的应用十分广泛。其实很多NLP领域的任务都可以想想成是QA的任务(Question Answering)。所谓QA的任务就是给机器度一段文字,然后问机器一个问题,期待机器给一个正确的答案。比如如果是机器翻译任务,给机器读一篇英语文章,要翻译成德语,这个时候的问题就是这篇文章的德语翻译是什么?或者让机器自动做摘要,这个时候问题就是这篇文章的摘要是什么?或者想让机器做sentiment analysis,这个时候的问题就是这篇文章是正面还是负面的?

输入是问题和文章,输出是答案。

但是对多数NLP的任务而言,为这些任务量身定做的模型会得到更好的结果,不见得非要单独用seq2seq。

用seq2seq硬解的举例。

input 一个sequence,由encoder处理这个sequence,再把处理好的sequence丢给decoder,让decoder决定要输出什么样的sequence。

很早就有应用。

只是现在提到seq2seq基本上都想到的是Transformer

encoder

seq2seq中encoder要做的事情是:给一排向量,输出另一排向量。给一排向量输出另一排向量有很多模型都可以做到。比如前面提到的self-attention,比如RNN、CNN(都能够做到input一排向量,output另外一排同样长度的向量)

在Transformer里面,Transformer的encoder用的就是self-attention。

现在encoder里面会分成很多个block。每一个block都是输入一排向量输出一排向量,最后的一排向量会输出最终的vector sequence。这里每一个block并不是neural network的一层,每一个block在做的事情是好几个layer在做的事情。

在Transformer的encoder里面,每一个block做的事情大概是:先做一个self-attention,得到一排vector(考虑整个sequence的信息)之后,把这些vector丢到FC里,再output另外一排vector,这里output的vector才是block的输出。

实际上,做的事情更复杂。

在Transformer里面加入了一个设计,self-attention的输出是考虑所有input的结果,除了这样输出的vector,额外添加了input(input+self-attention的output得到新的输出)->residual链接

(很好,又是残差)

做了residual之后,再做normalization,这里使用的不是batch normalization(猜测老师提到的原因是因为resnet用的是batch normalization),而是layer normalization。

layer normalization在做的事情是:输入一个向量,输出另一个向量,不用考虑batch。计算输入向量的mean和standard deviation。注意batch normalization是对不同的example的不同的feature的同一个dimension去计算mean和standard deviation,而layer normalization是对同一个feature的同一个example的不同的dimension计算mean和variance

(弹幕:batch norm 是在nbatch降维,而layer norm在seq上)

纠错,这里的x_i'把上标prime拿掉->x_i

得到layer normalization的输出之后,才算得到FC的输入(见右下角)

在FC这边也有residual的架构

再把residual的结果再做一次layer normalization

这个normalization的结果才是这个block的输出

图中的Add & Norm就是residual+layer normalization的意思啦

为什么Transformer的encoder要这样设计,不这样设计可以吗?可以,不一定非要按照原始的Transformer的架构(顺序)。

decoder

autoregressive

decoder其实有两种,这里主要介绍autoregressive(AT)

autoregressive的decoder是怎么运作的呢?

以语音识别为例(什么是语音识别?输入一段声音,输出一串文字。所以这里encoder在做的就是输入一段声音,输出一排vector)。

接下来就轮到decoder出场了。decoder要做的事情就是产生输出。那么decoder怎么产生语音识别的结果呢?decoder就是先把encoder的输出读进去。

decoder怎么产生一段文字呢?首先要给decoder一个符号,代表开始(<BOS> begin of sentence),接着decoder会吐出一个向量,这个向量的长度=vocabulary的size(词典的长度)。

这里解释一下vocabulary。我们首先要想好decoder输出的单位是什么,假设现在做的是中文的语音识别,输出的是中文,那么这里的vocabulary的size可能就是中文的方块字的数目。常用的中文的方块字的数量大概是2000-3000个。不同的语言的vocabulary的size可能是不同的,比如如果是英语,我们可以用字母,也可以用词汇etc。

那么带入到例子里面,这个vector的长度就和我们希望中文可以输出的方块字的数目相等。这样,每一个方块字都会对应一个数值。在产生这个向量之前,通常都会跑一个softmax->这时这个向量其实就是一个distribution(sum=1)。也就是每一个方块字都有一个分数,分数最高的那个字就是最终的输出。

接下来,我们把“机”当做decoder新的input(开始decoder只有一个input<BOS>),这里“机”也被表示为一个one-hot的vector,所以现在decoder有两个输入:<BOS>和“机”。根据这两个输入,得到一个向量,根据这个向量给每一个方块字的打分,决定第二个输出是什么(“器”)。

接下来把“器”也当做decoder的输入,现在decoder有三个输入啦:<BOS> “机” “器”,再决定输出什么(“学”)

……

这里encoder也有输入,这个稍后讨论

注意这里,decoder看到的输入其实是decoder在前一个时间点自己的输出,decoder会把自己的输出当做接下来的输入。所以当decoder在产生一个句子的时候,有可能看到错误的东西。

decoder内部的结构。先忽略encoder那边。

在Transformer中decoder的结构如上图所示。

看起来比encoder要复杂一点。如果我们把decoder中间的部分盖上,看起来encoder和decoder两遍的结构差不多哎。->除了中间被遮住的部分,并没有太大的差异哎

还有一个区别是在decoder这边的multi-head attention这里还加了一个masked

我们原来的self-attention如上图。input一排vector,output一排vector。output的每一个vector都是看过完整的input以后才做决定的。所以输出b^1的时候实际上是看过a^1到a^4所有的信息才输出的b^1。

在masked attention的时候,我们不能再看全部的的信息啦。在产生b^1的时候只能考虑a^1的信息(不看a^2 a^3 a^4),在产生b^2的时候只能看a^1 a^2的信息

(弹幕:不完全是RNN,这个masked可以主动掩码,可以支持并行计算,RNN必须先计算出前一个才能计算后一个)

讲的更具体一点:

当我们要产生b^2的时候,只拿第二个位置的query去和第一个位置的key和第二个位置的key去计算attention,第三个位置和第四个位置就不去管了(不计算attention)。

(弹幕:因为bert就做了两件事,分别是mask和NSP,mask也就是类似于完形填空
因为后面的结果还没翻译出来啊,怎么计算和后面的相似度)

为什么要加masked呢?

回忆一下decoder的运作方式,输出是一个一个产生的,是先有a^1再有a^2再有a^3再有a^4,这和之前的self-attention是不一样的,之前的self-attention是a^1到a^4一起输进去的。但是在decoder这边,当我们要计算b^2的时候,我们是没有后面的a^3 a^4的,所以也没办法把a^3 a^4考虑进去

(弹幕:因为decoder是RNN模式,分步进行
mask的引入说白了就是避免数据泄露~也就是说,不能使用未来的数据进行预测
相当于 你能一眼看到一个句子的所有词汇,但是写的时候只能从前往后一个一个的写
被翻译可以一次获取所有,而翻译成功的要一点点的出
训练的时候你知道后面的a,但你预测的时候后面的a都还没有,你怎么计算?
这个网络的训练方法就是预测下一个会出现的词,如果网络本身就可以获得下一个词的话,那么就只会获得一个一一映射,无法得到训练的目的)

这相当于是告诉我们decoder输出的token是一个一个产生的。所以只能考虑左边的东西而不能考虑右边的东西。

(弹幕:训练的时候decoder的输入是完整的输入,测试的时候才是autoregressive的输入,训练时加了mask为了模拟测试的情况)

到目前为止,还有一个问题,decoder要自己决定输出的sequence的长度

但是到底输出的长度是多少呢?我们没办法轻易的从输入的长度知道输出的长度是多少。并不一定输入是4个向量输出就是4个向量。decoder运作的机制导致decoder并不清楚什么时候应该停下来,比如这里产生“习”之后,还可以继续重复同样的process,比如这里进来一个“习”,下面可能输出一个“惯”。

所以我们需要一个特别的标识符,来断开(END)也就是说除了所有的中文的方块字之外还要有<BOS>和END(在示例中其实<BOS>和END用的是同一个符号)

所以我们期待着这样就可以在decoder吃进“习”之后,能够吐出来END,换言之,当把“习”当做输入以后,decoder看到encoder输出的embedding还有<BOS> “机” “器” “学” “习”之后,decoder就知道这个语音识别的结果已经结束了,也就不需要产生更多的词汇了,这个时候产生END这个token的概率必须是最大的

(弹幕:训练的时候加end,预测的时候由模型决定,感觉是这样
就是说某一次迭代softmax 输出的分布里 end 对应的概率分数为最大值的时候)

Non-autoregressive(NAT)

NAT不同于AT(一个字一个字往外蹦),是一次把整个句子产生出来

吃一整排BEGIN的token,让一次产生一排token,接结束了

但是不是不知道应该输出多长吗?是的,确实没办法很直接的知道。idea1:另外learn一个classifier,吃encoder的input,输出数字,代表输出的长度,这样decoder就吃这个数字个数的BEGIN。idea2:不在乎。随便输入,但是忽略END之后的输出。

(弹幕:4个begin其实是不同的东西,比如不同的语音片段)

NAT的好处

1. 平行化(如果想输出100个token的句子,AT就需要做100次的decode,但是NAT不care句子的长度是多少,都是一下出来)

2. 比较能控制输出的长度。比如语音合成,比如现在想讲快一点,就把classifier的output除以2(假设用的是idea1的方法),那么这个时候的语速就是2倍快了,如果想讲慢一点就可以把classifier的output乘以2。

Encoder-Decoder

如图,红框框那里。这里叫做cross attention,是链接encoder和decoder之间的桥梁

从图中可以发现,encoder提供两个输入(来自encoder的那边有两个箭头,蓝色圈圈),decoder提供一个箭头(绿色圈圈)

decoder会先吃BEGIN(<BOS>)这个特殊的token,然后经过self-attention(含mask,不会吃到后面的输入,这里吃进去多少向量,吐出来多少向量),然后把输出的向量乘上一个矩阵,做transform,得到query(q),再把a^1 a^2 a^3产生key(k^1 k^2 k^3),用q和k计算attention的分数,得到α^1 α^2 α^3(做softmax->α'^1 α'^2 α'^3),接下来再把α'^1 α'^2 α'^3分别乘上v^1 v^2 v^3,再求和得到v,这个v接下来会被丢到FC做后续的处理。这个步骤(q来自decoder,k v来自encoder)称为cross attention。

(弹幕:decoder以某种形式和注意力转移,不断的观察原文,输出翻译后的)

后面也一样,假设现在已经产生了第一个中文的字“机”,现在decoder输入BEGIN “机”,产生一个向量q',一样和k^1 k^2 k^3计算分数,和v^1 v^2 v^3做weighted sum,得到v',交给后面的FC

Training

仍然以语音识别为例。做语音识别需要收集什么样的数据?要手机大量的声音信号,每一句声音信号都要找人给打标签(对应的词汇是什么)。那么,怎么让机器学到这件事呢?我们已经知道输入这一段声音信号,输出的第一个字应该是“机”,所以当我们把BEGIN丢给decoder之后得到的第一个输出应该和“机”越接近越好。什么叫和“机”越接近越好?“机”会被表示成一个one-hot vector,只有“机”对应的维度是1,其他都是0(见左上角),我们decoder的输出是一个distribution,是一个概率分布,我们会希望这个输出的概率分布和这个one-hot vector越接近越好。所以我们会去计算这个groundtruth和这个distribution的cross entropy,我们希望这个cross entropy的值越小越好->和分类很像。

所以实际上训练的时候,我们已经知道输出应该是“机器学习”这四个字。现在我们就告诉decoder说,每一次的输出分别应该是“机”“器”“学”“习”这四个字的one-hot vector。所以我们就希望输出和这四个字的one-hot vector越接近越好。

在训练的时候,每一个输出的one-hot vector和它对应的正确答案都会有一个cross entropy,我们希望所有的cross entropy的总和越小越好

不要忘了这里还有END,假设现在中文的字是4个,但是学的时候,要decoder输出的并不是只有这四个中文的方块字,还要叫decoder记住,输完这四个中文字之后,还要输出END这个特殊符号。->也就是说最终第五个位置输出的向量应该和END的one-hot vector的cross entropy越小越好。

(弹幕:一种损失函数,代表预测值和真实结果的差距。所以越小预测越准【这里在回答前面有人在弹幕中提问为什么要cross entropy越小越好,这里提一句题外话,为什么要用cross entropy我最近才get到一点intuition所以在下面大概写一下】)

这边有一个问题需要注意。decoder的在训练的时候会给看正确答案(输入ground truth),也就是说我们会告诉decoder在已经有BEGIN和“机”的情况下会输出“器”,有BEGIN“机”“器”的情况下会输出“学”……decoder在训练的时候会给输入正确答案->这种做法叫teacher forcing

(弹幕:可以理解成encoder是在找到输入向量之间的相关性,而decoder是借助这种相关性进行预测,再将预测结果和真实值做交叉熵作为训练的目标函数)

Tips

Copy Mechanism

对很多任务而言,我们都要求decoder自己产生输出,但是对某些任务而言,decoder不需要自己创造输出,也许decoder要做的是从输入的东西里面复制一些东西出来。

什么样的任务需要复制一些东西出来呢?一个例子是做聊天机器人。比如在上图的例子里面,“库洛洛”这个人名显然就没必要让decoder自己创造出来(老实说要是能创造不如让decoder自己画下去了)。但是现在decoder学的并不是要产生“库洛洛”,而是看到“我是xxx”就自动把xxx复制出来说“xxx你好”。

或者在做摘要的时候,可能更需要copy的技能。训练的方法就是收集大量的文章,每个文章都有人写的摘要,然后train一个seq2seq的model,这种model需要大量的文章(比如搞一万篇文章就很逊啦)。在做摘要的时候,有很多字直接就是从原来的文章中复制的。

附相关资料

Guided Attention

机器是个黑盒子,所以学到什么我们可能也不太清楚,所以有什么会犯很低级的错误。这里给出的例子是语音合成(TTS,text-to-speech)。完全可以用seq2seq的model,就搜集很多的文字和声音对应关系,告诉这个seq2seq model看到这个句子就输出这个声音。然后硬train一发。

怎么解决这个问题呢?

要求机器在做attention的时候有固定的方式。比如在TTS的时候我们认为应该按照从左到右去看句子。

->把这种限制放到training里面,要求机器学到attention就应该从左向右

Beam Search

有时候有用,有时候没用

sampling

加入一些随机性(加点噪声)可能效果更好

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

仅使用 Python 创建的 Web 应用程序(前端版本)第11章_订单详细

本章我们将实现订单列表页面。 完成后的图片如下(与订单列表相同)。 由于模型和服务是在订单列表页面创建的,因此本章将省略它们。 No分类内容1Page定义PageId并创建继承自BasePage的页面类2Application将页面 ID 和页面类对添加到 MultiPageApp 的页面中Page:定义PageId并…

QT tcp与udp网络通信以及定时器的使用 (7)

QT tcp与udp网络通信以及定时器的使用 文章目录 QT tcp与udp网络通信以及定时器的使用1、QT网络与通信简单介绍2、QT TCP通信1、 服务器的流程2、 客户端的流程3、服务器的编写4、客户端的编写 3、QT UDP通信1、客户端流程2、客户端编写3、UDP广播4、UDP组播 4、定时器的用法1、…

1 月 28日算法练习-前缀和

小郑的蓝桥平衡串 思路&#xff1a;把 L 看成 1&#xff0c;Q 看成 -1&#xff0c;利用前缀和来得到输入串的前缀子串中LQ 的和&#xff0c;利用前缀和差的性质得到子串&#xff0c;通过枚举看它是否平衡。 将L看做1&#xff0c;Q看做&#xff0d;1&#xff0c;只有当某个区间…

【JS进阶】ES6箭头函数、forEach遍历数组

文章目录 前言一、箭头函数1.1 基本语法1.2 带参数的箭头函数1.3 this指针指向谁&#xff1f; 二、forEach遍历数组总结 前言 随着JavaScript语言的不断发展&#xff0c;ES6&#xff08;ECMAScript 2015&#xff09;引入了许多新的语法和特性&#xff0c;其中箭头函数和forEac…

【Linux】Linux权限的概念 -- 详解

一、Linux 中的用户 Linux 下有两种用户&#xff1a; 超级用户&#xff08;root&#xff09;&#xff1a;可以在 Linux 系统下做任何事情&#xff0c;不受限制。普通用户&#xff1a;在 Linux 下做有限的事情。 超级用户的命令提示符是 “#”&#xff0c;普通用户的命令提示符…

【Javaweb程序】【C00155】基于SSM的旅游旅行管理系统(论文+PPT)

基于SSM的旅游旅行管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于SSM的旅游旅行管理系统 本系统分为前台系统模块、管理员模块、用户模块以及商家模块 其中前台系统模块的权限为&#xff1a;当游客打开系统的网址后…

javax.servlet.http包

javax.servlet.http包 javax.srvlet.http包是对javax.servlet包的扩展。该包的类和接口处理使用HTTP进行通信的servlet。这些servlet也称为HTTP Servlet。您需要扩展HttpServlet类来开发HTTP Servlet。javax.servlet.http包经常使用的接口包括: HttpServletRequest接口HttpSe…

Flutter之环境搭建(小白教程)

这个章节我们学习如何安装Flutter&#xff0c;配置 Flutter 、Android Studio 环境&#xff0c;做开发的前置工作。 环境搭建有点麻烦&#xff0c;特别是Android环境的安装&#xff0c;没有代理简直就是噩梦&#xff0c;要有耐心一起加油&#xff01; Flutter 官网地址 一. 操…

ATT汇编

指令后缀 AT&T格式的汇编指令有不同的后缀 其中 b表示byte&#xff0c;字节 w表示word&#xff0c;字/两字节 l表示long&#xff0c;32位系统下的long是4字节 q表示quad&#xff0c;意味四重&#xff0c;表示4个字/8字节 寄存器用途 参见 AT&T的汇编世界 - Gemfield…

(自用)learnOpenGL学习总结-高级OpenGL-立方体贴图

ok终于来到了立方体贴图了&#xff0c;在这里面我们可以加入好看的天空包围盒&#xff0c;这样的画我们的背景就不再是黑色的了&#xff01; 首先&#xff0c;立方体贴图和前面的sampler2D贴图一样&#xff0c;不过是6个2D组成的立方体而已。 那么为什么要把6个组合在一起呢&…

81.网游逆向分析与插件开发-背包的获取-装备栏数据结构的逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;自动化助手显示物品数据-CSDN博客 然后游戏中有弓箭&#xff0c;弓箭有数量&#xff0c;可以作为突破口&#xff0c;也可以使用物品id 获取弓的方式 获取弓箭的方式 然后搜索250 然后搜索出一个 然后…

【智能家居入门之微信小程序控制下位机】(STM32、ONENET云平台、微信小程序、HTTP协议)

实现微信小程序控制单片机外设动作 一、使用ONENET可视化组件控制单片机外设动作二、使用微信小程序控制单片机外设动作三、总结 本篇博客话接上文&#xff1a; https://blog.csdn.net/m0_71523511/article/details/135892908 上一篇博客实现了微信小程序接收单片机上传的数据…

《吐血整理》高级系列教程-吃透Fiddler抓包教程(30)-Fiddler如何抓取Android7.0以上的Https包-番外篇

1.简介 通过宏哥前边几篇文章的讲解和介绍想必大家都知道android7.0以上&#xff0c;有android的机制不在信任用户证书&#xff0c;导致https协议无法抓包。除非把证书装在系统信任的证书里&#xff0c;此时手机需要root权限。但是大家都知道root手机是非常繁琐的且不安全&…

开源大数据集群部署(七)Freeipa卸载

作者&#xff1a;櫰木 1、命令卸载 卸载FreeIPA服务器和客户端的命令&#xff0c;以及清理相关残留文件和卸载相关软件包。 ipa-server-install -U --uninstall #服务端卸载 ipa-client-install -U --uninstall #客户端卸载 #删除残留文件&#xff0c;避免二次安装失败 cd /va…

数据可视化工具JSON Crack结合内网穿透实现公网访问

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

Prometheus 监控系统的初步了解与系统搭建

目录 目录 目录 前言 Prometheus的相关知识 Prometheus特点 Prometheus的存储引擎&#xff1a;TSDB Prometheus的组件 1.核心组件&#xff1a;prometheus server Prometheus server又分为三个部分&#xff1a; 2.exports 3.client Library 4.cadvisor 5.blackbox-ex…

Java毕业设计-基于jsp+servlet的人事管理系统-第79期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于jspservlet的人事管理系统&#xff1a;有配套报告文档&#xff0c;前端 jsp、jquery&#xff0c;后端 servlet、jdbc&#xff0c;集成员工档案管理、公告浏览、请假信…

H5项目使用@microsoft/fetch-event-source

前言&#xff1a; 在microsoft/fetch-event-source 文档中&#xff0c;没有介绍在项目中使用script 引入&#xff0c;只介绍了通过npm 引入的方式&#xff1b;项目的接口又是 post 方式的流式接口。 解决方式&#xff1a; 借助 webpack 工具 实现步骤&#xff1a; 1、初始化…

vue预览pdf文件的几种方法

文章目录 vue预览pdf集中方法方法一&#xff1a;方法二&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a; 方法三&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a;自己调参数&#xff0c;选择符合自己的 vue预览pdf集中方法 我…

方差与协方差之间的区别?

方差和协方差都是用来衡量随机变量之间关系的统计量&#xff0c;但它们的计算方式和含义有所不同。 方差&#xff08;Variance&#xff09;&#xff1a;方差是描述数据集合离散程度的统计量&#xff0c;它衡量了数据点与均值之间的平均距离。 方差越大&#xff0c;表示数据点越…