快速串联 RNN / LSTM / Attention / transformer / BERT / GPT未完待续


0. 背景:序列数据及相关任务

  • 序列数据是由一组相互关联的样本组成的数据,其中任意样本对应的标记是由其自身和其他样本共同决定的;序列数据任务 是输入或输出为序列数据的机器学习任务,用传统机器学习模型处理他们是困难的,比如 序列模型(1)—— 难处理的序列数据 中第 3 节的例子
  • 传统方法的局限性在于其问题建模,这些模型不是针对可变长度的输入输出设计的,无法体现序列数据的特点,具体而言
    1. 传统的 MLP、CNN 这类模型都是 one-to-one 模型,即一个输入一个输出。这种模型会把序列数据作为一个整体来考虑,其输入输出的尺寸必须是固定的,比如输入一张固定尺寸的图像输出其类别,或者输入一段固定长度的句子预测下一个词

      Note:借助一些手段,可以强行用传统模型处理变长的输入输出问题,如 各种监督学习范式(强监督、半监督、多标记、偏标记、多示例、多示例多标记、标记分布…) 中的多示例多标记问题,但是这些模型本质上还是要将输入输出处理成固定的长度(比如用固定长度的 0/1 向量选出不同数量的样本作为输入输出)。至于样本间的关系,虽然可以通过网络自己学出来,但由于缺乏考虑这种关系的显式结构,所以很难学得好(可以参考 从模型容量的视角看监督学习

    2. 一个良好的序列模型应该是 many-to-many 模型,即支持可变长度的输入输出,并且最好能对序列样本间的关系进行显式建模,注意这样的模型也可以直接用来处理 one-to-manymany-to-one 甚至 one-to-one 问题。语音识别、本文情感分析、序列预测等等序列任务都能被这种模型更好地处理

      RNN 就是一种良好的序列模型,下图给出了各类输入输出情况下的 RNN 结构

      在这里插入图片描述

1. 早期序列模型

  • RNN 和 LSTM 等早期序列模型模仿人类处理序列数据的过程,人类阅读文本时每次看一个词,逐渐在大脑中积累文本信息,这些模型也是如此,其内部有一个隐状态代表目前积累的信息,每次读入一个序列样本就将其更新,如下图所示

    在这里插入图片描述


    注意这里的每一列都是不同时刻下的同一个模型,可见,模型的每个隐状态都表示 “当前位置之前的 ‘已见序列片段’ 的特征”,具体应用时
    1. 对于 “文本情感分析” 这类 传统监督学习任务(many-to-one),常将序列模型作为 “特征提取器”,只使用最后一个隐状态 h t \pmb{h}_t hhht 作为整个序列的特征向量,用它接一个分类头或回归头作为整个模型的输出。在训练时,只要像图像分类等普通监督学习任务一样训练即可
    2. 对于 “文本生成” 这类 标准语言模型任务 (one-to-many),即不断根据之前序列样本预测下一个样本值的任务,通常会如下图所示做 Autoregress,这时我们会增加一个分类头或回归头将隐状态 h \pmb{h} hhh 变换为输出 x \pmb{x} xxx,推断时不断地将上一步模型输出合并到下一步模型的输入中。在训练时,会构造很多以连续的 n 个样本作为输入,紧接着第 n+1 个样本作为标签的自监督样例,详见下文 1.4.1 节
    3. 对于 “文本翻译” 这类 Seq2Seq 任务(many-to-many),通常使用 Encoder-Decoder 结构。这时 Encoder 就是类似 1 中的 many-to-one 序列特征提取器,Decoder 就是类似 2 中的 one-to-many 序列生成器,Encoder 提取的特征作为 Decoder 的初始 seed,二者结合就能做 many-to-many 了。训练时通常用 teacher-forcing 形式,详见下文 1.4.2 节

1.1 循环神经网络 RNN

  • RNN 是实现第 1 节中 “信息积累” 概念的最简单模型,其结构图如下所示

    在这里插入图片描述


    具体而言,设隐藏层激活函数为 ϕ \phi ϕ t t t 时刻输入批量大小为 n n n 样本维度为 d d d 的小批量样本 X t ∈ R n × d \pmb{X}_t\in \mathbb{R}^{n\times d} XXXtRn×d,设隐藏变量维度为 h h h,前一个时刻的小批量隐层变量为 H t − 1 ∈ R n × h \pmb{H}_{t-1}\in\mathbb{R}^{n\times h} HHHt1Rn×h。引入模型的权重参数 W x h ∈ R d × h,W h h ∈ R h × h \pmb{W}_{xh}\in \mathbb{R}^{d\times h},\pmb{W}_{hh}\in \mathbb{R}^{h\times h} WWWxhRd×h,WWWhhRh×h 和偏置参数 b h ∈ R 1 × h \pmb{b}_h\in \mathbb{R}^{1\times h} bbbhR1×h,则该步的隐藏变量 H t ∈ R n × h \pmb{H}_t\in\mathbb{R}^{n\times h} HHHtRn×h 可以如下计算
    H t = ϕ ( X t W x h + H t − 1 W h h + b h ) = ϕ ( [ X t H t − 1 ] [ W x h W h h ] + b h ) \begin{aligned} \pmb{H}_t &= \phi(\pmb{X}_t\pmb{W}_{xh}+\pmb{H}_{t-1}\pmb{W}_{hh}+\pmb{b}_h) \\ &= \phi\left({\left[ \begin{array}{ccc} \pmb{X}_t & \pmb{H}_{t-1} \end{array} \right ]} {\left[ \begin{array}{ccc} \pmb{W}_{xh}\\ \pmb{W}_{hh} \end{array} \right ]}+\pmb{b}_h\right) \\ \end{aligned} HHHt=ϕ(XXXtWWWxh+HHHt1WWWhh+bbbh)=ϕ([XXXtHHHt1][WWWxhWWWhh]+bbbh) 再设输出维度为 m m m,用于输出的 FC 层参数为 W h m ∈ R h × m,b m ∈ R 1 × m \pmb{W}_{hm}\in\mathbb{R}^{h\times m},\pmb{b}_m\in\mathbb{R}^{1\times m} WWWhmRh×m,bbbmR1×m,该步的输出变量 O t ∈ R n × m \pmb{O}_t\in\mathbb{R}^{n\times m} OOOtRn×m 可以如下计算
    O t = H t W h m + b m \pmb{O}_t = \pmb{H}_t \pmb{W}_{hm} + \pmb{b}_m OOOt=HHHtWWWhm+bbbm 注意在不同的时间步使用的都是相同的模型参数 W,b \pmb{W},\pmb{b} WWW,bbb,因此 RNN 的参数开销不会随着时间步的增加而增加

1.2 长短期记忆网络 LSTM

  • LSTM 是对 RNN 模型的改进,可以有效缓解 RNN 的梯度消失(见下文 1.3.2 节)问题,其结构如下所示

    在这里插入图片描述


    这个结构看上去很复杂,不过我们可以从先从宏观角度来理解:相比 RNN,LSTM 针对序列数据性质增加了对数据处理过程的限制,从而减少了模型的弹性/容量,使它能在使得相同样本量下更好地提取序列信息,这和 CNN 比 MLP 能更好地处理图像数据是一个道理

  • 现在来仔细看一下这个结构,LSTM 的设计灵感来自于计算机的逻辑门,它的输出和 RNN 一样仍然是隐状态 H \mathbf{H} H,只是生成过程更复杂。相比 RNN,LSTM 引入了记忆单元cell C \mathbf{C} C,它和隐状态 H \mathbf{H} H 具有相同的形状,用于记录附加的信息并产生输出 H \mathbf{H} H,其他的所有门都是为了控制这个记忆单元服务的。具体而言,设隐藏层维度为 h h h,batch size 为 n n n,样本维度为 d d d,则批量输入为 X t ∈ R n × d \mathbf{X}_t \in \mathbb{R}^{n \times d} XtRn×d,前一时刻隐状态为 H t − 1 ∈ R n × h \mathbf{H}_{t-1} \in \mathbb{R}^{n \times h} Ht1Rn×h

    1. 候选记忆 C ~ \pmb{\tilde{C}} C~C~C~ 和 RNN 中的隐状态 H \mathbf{H} H 完全一致,只是指定了使用激活函数 ϕ = tanh ⁡ \phi=\tanh ϕ=tanh,将值压倒 ( − 1,1 ) (-1,1) (1,1)
    2. 遗忘门 F t ∈ R n × h \mathbf{F}_t \in \mathbb{R}^{n \times h} FtRn×h 用来控制要保留记忆单元 C \mathbf{C} C 中的过去信息的程度/比例,用 sigmoid 激活的 FC 层压倒 (0,1)
    3. 输入门 I t ∈ R n × h \mathbf{I}_t \in \mathbb{R}^{n \times h} ItRn×h 用来控制候选记忆 C ~ \mathbf{\tilde{C}} C~ 被合并到记忆单元的程度/比例,用 sigmoid 激活的 FC 层压倒 (0,1)
    4. 输出门 O t ∈ R n × h \mathbf{O}_t \in \mathbb{R}^{n \times h} OtRn×h 用来控制记忆单元 C \mathbf{C} C 中作为输出 H \pmb{H} HHH 的程度/比例,用 sigmoid 激活的 FC 层压倒 (0,1)

    列一下公式,有
    C ~ t = tanh ( X t W x c + H t − 1 W h c + b c ),I t = σ ( X t W x i + H t − 1 W h i + b i ),F t = σ ( X t W x f + H t − 1 W h f + b f ),O t = σ ( X t W x o + H t − 1 W h o + b o ),\begin{aligned} \tilde{\mathbf{C}}_t &= \text{tanh}(\mathbf{X}_t \mathbf{W}_{xc} + \mathbf{H}_{t-1} \mathbf{W}_{hc} + \mathbf{b}_c),\\ \mathbf{I}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xi} + \mathbf{H}_{t-1} \mathbf{W}_{hi} + \mathbf{b}_i),\\ \mathbf{F}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xf} + \mathbf{H}_{t-1} \mathbf{W}_{hf} + \mathbf{b}_f),\\ \mathbf{O}_t &= \sigma(\mathbf{X}_t \mathbf{W}_{xo} + \mathbf{H}_{t-1} \mathbf{W}_{ho} + \mathbf{b}_o),\end{aligned} C~tItFtOt=tanh(XtWxc+Ht1Whc+bc),=σ(XtWxi+Ht1Whi+bi),=σ(XtWxf+Ht1Whf+bf),=σ(XtWxo+Ht1Who+bo), 这里的 W,b \pmb{W},\pmb{b} WWW,bbb 都是要学习的参数,相比 RNN 多了三组。另外,所有信息选取都是通过对应位置乘以 (0,1) 间小数的方式进行的,如图可见有 “遗忘门 F \mathbf{F} F 去除部分老记忆 C t − 1 \mathbf{C}_{t-1} Ct1”、“输入门 I \mathbf{I} I 合并部分候选记忆 C ~ \mathbf{\tilde{C}} C~” 和 “输出门 O t \mathbf{O}_t Ot 保留部分新记忆 C t \mathbf{C}_t Ct” 三处,公式表示为
    C t = F t ⊙ C t − 1 + I t ⊙ C ~ t H t = O t ⊙ tanh ⁡ ( C t ) . \begin{aligned} \mathbf{C}_t &= \mathbf{F}_t \odot \mathbf{C}_{t-1} + \mathbf{I}_t \odot \tilde{\mathbf{C}}_t\\ \mathbf{H}_t &= \mathbf{O}_t \odot \tanh(\mathbf{C}_t). \end{aligned} CtHt=FtCt1+ItC~t=Ottanh(Ct).

  • 直观地看这个结构,其实就是每次 batch 输入先产生和 RNN 中 H \mathbf{H} H 完全一样的 C ~ t \mathbf{\tilde{C}}_t C~t 作为 “当前步记忆”,然后用遗忘和输入门控制它和 “历史序列记忆” C t − 1 \mathbf{C}_{t-1} Ct1 混合得到 “最新序列记忆” C t \mathbf{C}_{t} Ct,最后使用输出门将 C t \mathbf{C}_{t} Ct 衰减后输出为 H t \mathbf{H}_t Ht

    1. 如果遗忘门始终为 1 且输入门始终为 0, 则过去的记忆元 C t − 1 \mathbf{C}_{t-1} Ct1 将随时间被保存并传递到当前时间步,缓解梯度消失问题,更好地捕获序列中的长距离依赖关系。这有点类似残差网络通过残差连接避免梯度消失的思想
    2. 只要输出门接近 1,我们就能够有效地将所有记忆信息传递给预测部分, 而对于输出门接近 0,我们只保留记忆元内的所有信息,而不需要更新隐状态
  • 最后再回到宏观来看,LTSM 通过学习四组系数 & 偏置参数,要求模型按照上述逻辑构造记忆并从中提取输出,相比 RNN 直接一个 FC 加激活函数,它通过模型结构引导其做出更符合序列性质的行为,从而缓解了 RNN 的梯度消失和梯度爆炸问题,大幅提升了 RNN 的性能

1.3 改善 RNN/LSTM 的三个技巧

1.3.1 通过堆叠扩展为深度模型

  • 如果模型只有一层,其容量是有限的,无法表示太复杂的映射。但是注意到第 1 节这种序列模型对每一个输入都可以有一个输出代表对当前时刻之前的序列内容的聚合,而所有这些输出可以组合成一个新的序列,这样一来我们其实可以再把这个新序列输入参数相同的模型,这样每个输出将会是对当前时刻之前的序列内容的进一步聚合,如此反复堆叠就得到了 Stacked RNN/Stacked LSTM,其容量更大,能表示的映射关系也更加复杂,如下图所示

    在这里插入图片描述


    需要注意的是,对于更大容量的模型,需要提供更多的训练样本以避免过拟合

1.3.2 使用双向模型避免遗忘

  • 无论 RNN 还是 LSTM,模型都只能利用隐藏状态间接地获取之前序列的信息,由于隐藏状态的维度一定远远小于之前的变长序列所有样本的连接维度,这种做法无可避免地会损失一些信息,体现在微观层面上就是两个经典问题

    1. 梯度消失:参数更新梯度被近期样本主导(和 MLP 里的梯度消失不太一样)
    2. 梯度爆炸:参数更新梯度梯度趋近 ∞ \infin

    在 RNN 中这两个问题尤其严重(可以参考 RNN梯度消失和爆炸的原因),以梯度消失为例,近期的序列样本主导了优化方向,这会导致模型快速忘记相隔时间比较久的早期序列输入,比如下例

    这个 RNN 的任务是根据先前序列预测下一个单词,在 x 1 x_1 x1 处输入了 “China”,但由于梯度消失这个信息几乎没法被记住,模型难以在相隔较远的 h t + 1 h_{t+1} ht+1 处给出 “Chinese” 的预测

    在这里插入图片描述


    这里模型大概能学到应该输出一个语言,但是具体是什么语言会被近期序列的倾向所主导

    LSTM 通过引入 “记忆单元” 缓解了这些问题,但依然无法完全解决。一个简单粗暴的优化方案是直接同时从两个方向训练 RNN 或 LSTM,这样得到的 Bidirectional RNN/Bidirectional LSTM 结构如下

    在这里插入图片描述


    这样一来,一个方向的早期样本就成了另一个方向的近期样本,可以缓解 RNN/LSTM 的遗忘问题。另外这里输出的 y y y 是两个模型输出的向量拼接,它也可以像上面那样进行 stack 从而扩展容量

1.3.3 使用预训练模型

  • 处理序列数据时往往要做一步 embedding,使得序列样本变得可以处理。比如常见的文本模型,你没法直接向网络输入一个单词,这时有几个做法

    1. 直接给每个单词编一个数字,但是这种简单做法存在问题,比如 “car” 编码为 1,“cat” 编码为 2,“red” 编码为 3,那么你会发现 “car” + “cat” = “red”,这是不合理的
    2. 更合理一点的做法是把单词编码成 one-hot 向量,但是这样一来如果单词数量多的话,输入的维度就会特别大,导致处理困难
    3. 目前通常的做法是先编码成 one-hot,然后接一个 FC 层把它降维,而且我们希望降维之后的词向量能够体现语义上的远近关系,比如 “汽车” 和 “跑车” 应该在嵌入空间接近;“汽车” 和 “苹果” 应该在嵌入空间远离
  • 一个问题是,FC 嵌入层往往很大,有时甚至比模型参数还多不少,如果直接把它接在 RNN 或 LSTM 的输入之前一起训练,很容易导致嵌入层过拟合,影响模型性能。这时我们可以先用一个别的任务专门训练这个 FC 嵌入层,这就是所谓的 预训练pre-train 过程,几个注意点是

    1. 预训练任务必须要有对相同对象做嵌入的嵌入层,而且最好有大数据集
    2. 预训练任务可以是不同的任务,但是和目标任务越相关越好,因为学到的词向量会受到任务性质影响,越相关的任务,后期的调优过程越好做
    3. 预训练任务可以使用不同的模型,只要有 FC 嵌入层就行

    完成预训练后,直接用预训练模型得到的 FC 嵌入层参数初始化目标模型的 FC 嵌入层,然后在目标任务训练过程中有时将其固定住只训练模型的其他部分,有时也带着这个 FC 层一起训练,这称为 微调fine-turn 过程

  • 所有涉及预处理的部分都可以用类似思路进行预训练,可以有效提高模型性能

1.4 传统序列模型任务

  • 虽然 RNN 和 LSTM 都能对每个输入给出一个输出,但通常还是会用最后一个样本对应的输出作为整个序列的特征用于具体任务,因此这些传统模型自身是倾向 many-to-one 形式的,但是通过巧妙的应用,其能处理的问题涵盖了 one-to-manymany-to-oneone-to-one 所有情况,下面简单举两个例子进行说明

1.4.1 自动文本生成(Autoregress 结构)

  • 这个任务只需要一大段文本作为材料,就能生成出类似风格的句子
    1. 训练阶段,使用自监督方式构造样本,每个样本都是以一段固定长度文本作为输入序列,其后的词或字符作为预测标记,直接做多分类监督学习即可。样本的具体构造方式可以是随机截取,也可以是用一个滑动窗口以一定的偏移量扫过输入文本材料。注意到这样的输入输出符合 many-to-one 的形式,另外由于输入长度必须固定,某种程度上也能看做 one-to-one 形式

      换个角度看,此模型也可理解为先用序列模型提取前驱序列的特征(RNN/LSTM 视角下就是隐变量),再用这个特征做多分类来选择生成下一个 token,直接把这两件事放在一起进行 end-to-end 的训练

    2. 预测阶段,使用 Autoregress 的方式反复生成单词或字符,如下图所示

      在这里插入图片描述


      随便给定一个种子向量代表前驱序列特征,再给一个起始 token,模型就能输出后继 token 和新的隐状态,再将后继 token 作为输入就能继续生成,不断重复下去就能生成无限制长度的序列,这就是所谓的 “Autoregress”。总体上看属于 one-to-many 形式
  • 自动文本生成任务的意义在于,通过特定的结构安排,可以让只会做 to-one 任务的模型完成 to-many 任务。监督学习模型基本都是 to-one 的,所以借助这个结构,我们甚至可以用 MLP 等八竿子打不着的模型来做文本生成,但由于这些网络缺乏对于序列任务的归纳偏置,提取序列特征的能力差,效果通常很差。可以参考 序列模型(1)—— 难处理的序列数据

1.4.2 机器翻译(Encoder-Decoder 结构)

  • 机器翻译的输入和输出都是变长度序列,是典型的 many-to-many 问题,机器学习中也称这种任务为 “Seq2Seq” 任务。为了让 RNN/LSTM 有能力处理这类问题,模型要设计成特殊的 “编码器-解码器架构”。正如其名,这种架构含有两个组件

    1. 编码器Encoder:接受一个长度可变的序列作为输入, 并将其转换为具有固定形状的编码状态,即从输入序列中提取一个特征向量。原始的 RNN/LSTM 可以完成这种 many-to-one 的任务,但特征向量中不可避免地会损失长跨度样本的信息,这也是后面出现注意力机制的重要原因
    2. 解码器Decoder:将固定形状的编码状态映射到长度可变的序列。这恰好是上面 1.4.1 节那种 one-to-many 任务,所以 Decoder 也可以看做使用 Encoder 编码特征作为初始种子特征向量的自动文本生成任务。为了能自动控制输出长度,通常要增加 “起始” 和 “终止” 两个特殊 token

      在这里插入图片描述

  • 机器翻译模型设计得很巧妙,通过连接 many-to-oneone-to-many 的两个组件真正实现了 many-to-many 任务

    1. 训练阶段,Encoder 和 Decoder 连在一起作为一个 many-to-many 模型训练。以文本翻译任务为例,对于数据库中一个形如 ( 样 本 序 列,标 签 序 列 ) (样本序列,标签序列) (,) 的样本,如下操作

      1. Encoder 输入完整的样本序列,将 “起始” token 输入 Decoder,预测目标序列第1个 token p 1 \pmb{p}_1 ppp1,它和标签序列第1个真实 token y 1 \pmb{y}_1 yyy1 计算损失

        在这里插入图片描述

      2. Encoder 输入完整的样本序列,将 “起始” token 和标签序列第1个 token 输入 Decoder,预测目标序列第2个 token p 2 \pmb{p}_2 ppp2,它和标签序列第2个真实 token y 2 \pmb{y}_2 yyy2 计算损失
      3. 重复直到标签序列最后一个 token

      每次计算的损失梯度会从 Decoder 一直回传到 Encoder,同时更新两个组件的参数

    2. 测试阶段,Encoder 输入待翻译的句子,Decoder 输入 “起始” token,让它如 1.4.1 节一样自回归地生成序列,直到输出 “结束” token 未知

  • 本节介绍的 “编码器-解码器结构” 是序列学习领域的一个重要模型,后面的 transformer 其实也是基于这个架构设计的

2. 注意力机制

2.1 注意力机制 (Attention)

  • 正如上文 1.3.2 节的讨论,RNN 和 LSTM 这种传统序列模型对长跨度序列样本的特征提取能力有限,很容易出现梯度消失等问题,这个问题即使用双向模型也无法完全解决。Attention 是针对这个遗忘问题设计的,以机器翻译任务为例

    在这里插入图片描述


    可见当句子长度超过 20 词时,传统序列模型翻译的 BLEU 评分会迅速下降,增加 attention 机制后问题解决

  • Attention 机制最早提出于 ICLR 2015 的文章 Neural machine translation by jointly learning to align and translate,这篇文章基于 Encoder-Decoder 结构做机器翻译任务,它的思想很简单,Decoder 输出每个词时不要再只依赖一个隐状态特征向量 s \pmb{s} sss,而是去看完整地看一遍要翻译的原句,从原句的所有样本中提取信息汇聚成上下文向量 c \pmb{c} ccc 作为 Decoder 的附加输入,而所谓 attention,本质就是汇聚上下文向量时的权重

    Note: c \pmb{c} ccc 可以作为附加信息和 s \pmb{s} sss 一起作为解码器输入;也可以完全不用 s \pmb{s} sss 只依靠 c \pmb{c} ccc 和输入 token x ′ \pmb{x}' xxx 进行解码,因为 c \pmb{c} ccc 中已经包含了从来自原始序列所有样本的特征

    将这样的 attention 机制加入 RNN/LSTM Encoder-Decoder 结构,如下

    在这里插入图片描述


    相比原始 RNN/LSTM Encoder-Decoder,差别仅在于多了一个上下文向量 c i \pmb{c}_i ccci 作为解码的附加信息(下图显示了注意力汇聚过程和附加输入结构,忽略了注意力计算过程)

    在这里插入图片描述

  • 下面不严谨地歇写一下上下文向量的计算过程

    1. 利用 注意力评分函数 a a a,计算输入 Decoder 的隐状态 s j \pmb{s}_j sssj 和 Encoder 的所有样本输出 h 1,. . .,h m \pmb{h}_1,...,\pmb{h}_m hhh1,...,hhhm注意力得分 α 1,α m \alpha_1,\alpha_m α1,αm,这个得分体现的是 s j \pmb{s}_j sssj 和 Encoder 各个输出 h i \pmb{h}_i hhhi 的相关性
      α i = a ( h i,s j ) \alpha_i = a(\pmb{h}_i,\pmb{s}_j) αi=a(hhhi,sssj)
    2. 对注意力得分进行 softemax,转为权重形式
      α 1,. . .,α m ← softmax ( α 1,α m ) \alpha_1,\alpha_m \leftarrow \text{softmax}(\alpha_1,\alpha_m) α1,αmsoftmax(α1,αm)
    3. 利用权重对 Encoder 各个输出进行汇聚,得到解码当前 token 所需的 上下文向量,直接将其作为解码 s \pmb{s} sss 时输入 RNN Decoder 的附加信息即可
      c j = ∑ k α k h k \pmb{c}_j = \sum_{k}\alpha_k \pmb{h}_k cccj=kαkhhhk

    上述过程总结成一句话就是:利用 s \pmb{s} sss h \pmb{h} hhh 的相关性对 h \pmb{h} hhh 加权求和得到 c \pmb{c} ccc。这里有一个问题,直接用原始的 s \pmb{s} sss h \pmb{h} hhh 向量往往不够灵活,比如二者维度可能不同,或者我们想在更高维度计算相关性,又或者想在更高维度汇聚 h \pmb{h} hhh 中的信息,于是我们可以加一步抽象化

    1. 使用矩阵 Q \pmb{Q} QQQ s \pmb{s} sss 的维度进行变换,得到查询向量query q \pmb{q} qqq
    2. 使用矩阵 K \pmb{K} KKK h \pmb{h} hhh 的维度进行变换,得到键向量key k \pmb{k} kkk
    3. 使用矩阵 V \pmb{V} VVV h \pmb{h} hhh 的维度进行变换,得到值向量key v \pmb{v} vvv

    这里的 Q,K,V \pmb{Q},\pmb{K},\pmb{V} QQQ,KKK,VVV 矩阵都是自己学出来的,在汇聚上下文向量 c \pmb{c} ccc 时,通过 q,k \pmb{q},\pmb{k} qqq,kkk 计算相关性,对 v \pmb{v} vvv 加权求和进行汇聚,如下图所示

    在这里插入图片描述

  • 注意力评分函数可以有多种设计

    1. 原始论文中使用的是 加性注意力additive attention,它允许 q \pmb{q} qqq k \pmb{k} kkk 是不同长度的向量(事实上原始论文中的 q \pmb{q} qqq k \pmb{k} kkk 就是 s \pmb{s} sss h \pmb{h} hhh 本身),给定 q ∈ R q,k ∈ R k \pmb{q}\in\mathbb{R}^q,\pmb{k}\in\mathbb{R}^k qqqRq,kkkRk,注意力得分为
      a ( q,k ) = w v ⊤ tanh ( W q q + W k k ) ∈ R,a(\mathbf q,\mathbf k) = \mathbf w_v^\top \text{tanh}(\mathbf W_q\mathbf q + \mathbf W_k \mathbf k) \in \mathbb{R},a(q,k)=wvtanh(Wqq+Wkk)R, 其中可学习的参数包括 W q ∈ R h × q,W k ∈ R h × k,w v ∈ R h \mathbf W_q\in\mathbb R^{h\times q},\mathbf W_k\in\mathbb R^{h\times k},\mathbf w_v\in\mathbb R^{h} WqRh×q,WkRh×k,wvRh
    2. 现在主流用的是 transformer 使用的 缩放点积注意力scaled dot-product attention,它的计算效率更高,但要求 q \pmb{q} qqq k \pmb{k} kkk 是相同长度的向量。假设查询和键的所有元素都是独立的 d d d 维随机变量, 且都是均值0方差1,那么两个向量的点积的均值为0方差为 d d d,将点积除以 d \sqrt{d} d 使其方差为1,注意力得分为
      a ( q,k ) = q ⊤ k d a(\mathbf q,\mathbf k) = \frac{\mathbf{q}^\top \mathbf{k}}{\sqrt{d}} a(q,k)=d qk
  • 引入 attention 机制,要求模型显式地考虑输出 token 和输入句子各个 token 间的相关性,可以解决传统 Seq2Seq 模型的遗忘问题。通过可视化训练后的 attention 向量,发现机器确实能够学到输入句子和输出句子各个 token 间合理的相关性

    在这里插入图片描述


    但这种能力是有代价的,设输入序列长 m m m,输出序列长 t t t,对传统模型引入 attention 机制会使计算复杂复从 Q ( m + t ) Q(m+t) Q(m+t) 大幅上升到 Q ( m × t ) Q(m\times t) Q(m×t)

  • 值得注意的是,这种和传统序列模型结合的 attention 在 transformer 语境中被称为 cross attention,用来强调 attention 计算发生在 Decoder 和 Encoder 之间

2.2 自注意力机制 (Self-Attention)

  • Self-Attention 机制提出于 2016 年的论文 Long Short-Term Memory-Networks for Machine Reading,原始的 attention 机制仅能用于 Seq2Seq 模型,而 self-attention 解除了这个限制,可以用于所有的 RNN/LSTM 结构的模型上
  • Self-Attention 的思想也很简单,其实和 2.1 节的在计算上没有任何区别,只是原始 attention 机制计算的是输出句子各个 token 和输入句子各个 token 之间的 attention;Self-Attention 只考虑一个句子,它计算自己的各个 token 和该 token 位置之前的其他 token 的 attention,然后基于这个从之前的所有 token 中汇聚上下文 c \pmb{c} ccc,直接代替传统 RNN 中的隐状态 s \pmb{s} sss,或者作为附加信息和 s \pmb{s} sss 拼在一起使用,Self-Attention 和 SimpleRNN 结合的一个简单示例如下

    在这里插入图片描述


    这里 h \pmb{h} hhh 代表隐状态,而且在更新隐状态计算时直接用汇聚向量 c \pmb{c} ccc 代替 h \pmb{h} hhh 使用
  • 直观地看,Self-Attention 就是在更新 RNN/LSTM 隐状态时显式地要求模型重新看一遍之前的所有历史序列样本,从而避免遗忘,思想本质和 Attention 机制没有区别,不同之处在于 Self-Attention 的主要目的是用文本中的其它 token 来增强目标 token 的语义表示。从 attention 向量的可视化结果看,Self-Attention 可以避免遗忘,还能帮助传统 RNN/LSTM 更加关注有相关性的 token

    在这里插入图片描述

  • 值得注意的是,这种和传统序列模型结合的 self-attention 在 transformer 语境中被称为 masked self-attention,它其实只关注了句子中该 token 之前的部分

3. Attention + Encoder-Decoder

  • 累了,这块待续…

3.1 Transformer

  • 完全利用 Self-attention、Masked Self-attention、Cross-attention 和 MLP 搭建的 Encoder-Decoder 结构

3.2 GPT

  • Transformer 的 Decoder 部分,Autoregress 预训练,主要用来做文本生成任务,有特征提取能力但是不如 BERT

3.3 BERT

  • Transformer 的 Encoder 部分,完形填空+判断上下句预训练,主要用来学特征(动态词嵌入),利用这个特征可以做很多下游任务

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读773次,点赞6次,收藏9次。【代码】c# json字符串转Oracle的insert into的小程序。
文章浏览阅读8.7k次,点赞2次,收藏17次。此现象一般定位到远端的监听服务来找问题,在远端查看监听服务状态(具体看下面的解决方案会详细呈现),服务是否开启,另外查看监听端点概要是否存在host未指向到计算名的,如无直接进入监听配置文件listener.ora内添加指向即可。2、查看监听服务状态 lsnrctl status,右边为远端端点状态,未添加host指向到计算名;1、本地及远端安装好Oracle并配置好连接,Oracle服务和监听已启动;1、远程Oracle数据库:Oracle11g R2。或者进入下述服务手动重启。,再进行远程连接即可。_ora-12541:tns:无监听程序
文章浏览阅读2.8k次。mysql脚本转化为oracle脚本_mysql建表语句转oracle
文章浏览阅读2.2k次。cx_Oracle报错:cx_Oracle DatabaseError: DPI-1047: Cannot locate a 64-bit Oracle Client library_cx_oracle.databaseerror: dpi-1047: cannot locate a 64-bit oracle client libr
文章浏览阅读1.1k次,点赞38次,收藏35次。本文深入探讨了Oracle数据库的核心要素,包括体系结构、存储结构以及各类参数。通过解析Oracle数据库的体系结构,读者可以深入了解其内部组成和工作原理。存储结构部分介绍了数据在Oracle中的存储方式,从表空间到数据文件的层层逻辑。最后,我们深入探讨了Oracle数据库中各类参数的作用和配置方法,帮助读者更好地理解和优化数据库性能。本文旨在帮助读者全面理解Oracle数据库的运作机制,为其在实践中的应用提供基础和指导。
文章浏览阅读1.5k次。默认自动收集统计信息的时间为晚上10点(周一到周五,4个小时),早上6点(周六,周日,20个小时)由于平时默认每天只收集4小时,时间有点短了,改成每天可收集8小时。oracle 18c中默认是打开的。查看当前自动收集统计信息的时间。_oracle自动收集统计信息
文章浏览阅读929次,点赞18次,收藏20次。只有assm(Automatic Shared Memory Management)模式可以使用大页,需要关闭amm(Memory Manager Process)HugePages_Free: 306 (空闲306页,已使用306-306=0页)防止oracle使用的内存交换,所以设置的大小与oracle配置的sga、pga相关。HugePages_Rsvd: 0 (操作系统承诺给oracle预留的页数)HugePages_Total: 306 (总共306页)_oracle11g 大页
文章浏览阅读801次。例如:10046:0,1,4,8,12。默认redo日志有三个,大小为50M,循环覆盖使用。redo log再覆盖之前,会被归档,形成归档日志。答:不同事件,不同级别。trace的不同级别?_oracle 日志
文章浏览阅读4.2k次,点赞84次,收藏77次。主要讲解MySQL中SQL的DDL语句,其中包括对数据库和表的一系列操作。_sql ddl 新增字段 mysql
文章浏览阅读1.1k次。ON DEMAND:仅在该物化视图“需要”被刷新了,才进行刷新(REFRESH),即更新物化视图,以保证和基表数据的一致性;ON COMMIT:一旦基表有了COMMIT,即事务提交,则立刻刷新,立刻更新物化视图,使得数据和基表一致。Method =>'C',物化视图有三种刷新方式:COMPLETE、FAST和FORCE。物化视图会占用空间,一半可用于大量数据查询时,减缓主表的查询压力使用。例如创建一个物化视图,让对接单位查询。_oracle物化视图定时刷新
文章浏览阅读713次,点赞21次,收藏18次。1.背景介绍在当今的大数据时代,数据量越来越大,传统的关系型数据库已经无法满足业务需求。因此,NoSQL数据库技术迅速崛起,成为企业和开发者的首选。Oracle NoSQL Database是Oracle公司推出的一款分布式NoSQL数据库产品,具有高性能、高可用性和易于扩展等特点。在本文中,我们将深入了解Oracle NoSQL Database的集成与开发者工具,帮助您更好地掌握这款产品的...
文章浏览阅读2.5k次,点赞2次,收藏4次。今天遇见一个问题需要将字段中包含中文字符串的筛选出来。_oracle查询包含中文字符
文章浏览阅读802次。arcmap 在oracle删除表重新创建提示表名存在解决放啊
文章浏览阅读4.3k次,点赞2次,收藏4次。Oracle连接数据库提示 ORA-12638:身份证明检索失败_ora-12638
文章浏览阅读3.4k次,点赞6次,收藏25次。etc/profile是一个全局配置文件,所有用户登录都会使用该文件构建用户环境。与windows配置环境变量是一个道理。选择Linux系统,找到适合自己系统的安装包,我的是CentOS 8 x64。接下来需要登陆Oracle账户才能下载,无账户的可以自己注册一个。Linux中export 命令用于设置或显示环境变量。模式,利用上下键到文档最后,添加以下代码。出现如图所示版本号字样,则说明安装成功。点击下载,勾选1,点击2。记住完整路径用于后面配置。找到Java并点击进去。往下翻,找到Java8。_linux安装jdk1.8
文章浏览阅读2.4w次,点赞26次,收藏109次。JDK 是的简称,也就是 Java 开发工具包。JDK 是整个 Java 的核心,其中JDK包含了 Java 运行环境(Java Runtime Envirnment,简称 JRE),Java 工具(比如 javac、java、javap 等等),以及 Java 基础类库(比如 rt.jar)。最主流的 JDK 是Oracle公司发布的 JDK,除了 Oracle JDK(商业化,更稳定)之外,还有很多公司和组织开发了属于自己的 JDK,比较有名的有IBM JDK(更适合 IBM) 和OpenJDK。_jdk安装教程
文章浏览阅读7.5w次。出现 “java.sql.SQLNonTransientConnectionException:Could not create connection to database server” 的错误通常是由于无法连接到数据库服务器引起的。_java.sql.sqlnontransientconnectionexception: could not create connection to
文章浏览阅读849次,点赞7次,收藏10次。在ClickHouse中创建用户、数据库并进行权限分配是一个重要的管理任务,它涉及到安全性和访问控制。下面是一个基本的指南来帮助你完成这些操作:1. 创建数据库首先,需要创建一个数据库。使用以下命令:CREATE DATABASE IF NOT EXISTS your_database_name;将 your_database_name 替换为你想要的数据库名。2. 创建用户接下来,创建一个新用户。使用以下命令:CREATE USER your_username IDENTIFIED WIT_在clickhouse中如何创建用户 赋权
文章浏览阅读1.2k次,点赞53次,收藏39次。本文是一篇关于Oracle数据库安装和使用的博文摘要。作者以轻松幽默的笔调介绍了自己在实验中掌握的Oracle数据库基本操作,包括使用组件查看命令、配置数据库监听器等。作者也分享了在实验中遇到的一些有趣问题,如SQL语句缺少分号导致的意外错误。此外,作者还强调了登录sys用户和启动实例加载数据库的注意事项,并鼓励读者面对挑战时保持乐观,不断提升自己的能力。整体风格风趣严谨,引人入胜。
文章浏览阅读820次,点赞17次,收藏16次。KingbaseES、xml、dbms_xmlgen、SETSKIPROWS、人大金仓、KingbaseES兼容Oracle包dbms_xmlgen的功能是通过SQL查询将关系表中数据转化为XML文档。转化方式一共有两种:(1)通过查询字符串直接转化。(2)通过上下文句柄转化。对于通过查询字符串直接转化的方式,无法跳过若干行进行查询,只能直接将表格中的所有数据转化为XML文档。