大模型相关技术综述
本文综述了大模型技术的发展历程与核心架构。内容涵盖从 Word2Vec、ELMo 到 Transformer 的演进,详细解析了自注意力机制、位置编码及 Encoder-Decoder 结构。介绍了 BERT、GPT、T5 等主流模型系列及其变体,并探讨了多模态生成模型如 VAE、GAN、Flow 和 Diffusion 的核心概念与应用差异。文章梳理了国内外大模型发展现状及关键技术点,为理解大模型技术体系提供参考。

本文综述了大模型技术的发展历程与核心架构。内容涵盖从 Word2Vec、ELMo 到 Transformer 的演进,详细解析了自注意力机制、位置编码及 Encoder-Decoder 结构。介绍了 BERT、GPT、T5 等主流模型系列及其变体,并探讨了多模态生成模型如 VAE、GAN、Flow 和 Diffusion 的核心概念与应用差异。文章梳理了国内外大模型发展现状及关键技术点,为理解大模型技术体系提供参考。

Word2Vec 属于 NLP 领域无监督学习和比较学习的先驱。精髓在于可以用不带标签的文本语料输入神经网络模型,就可以学习到每个词的带语义的词向量表示。它背后原理其实就是人类讲出来的话已经是带有信息量的,只要通过神经网络对语料批量处理词和上下文映射关系,就能学习到人类对某个词的上下文语意。
用一个向量来表示每个词汇,语义比较相近的词汇,它们的向量会比较接近。
词嵌入技术过去经常用到,但是它有一个很大的缺点。就是一词多义问题。每个词只有唯一的向量表示,所以一词多义的话,词意思就是训练语料里这个词出现最多词上下文的表示,会把其它次要上下文语意直接忘记。
Word Embedding 本质上是个静态的方式,即训练好之后每个单词的表达就固定住了,以后使用的时候,不论新句子上下文单词是什么,这个单词的 Word Embedding 不会跟着上下文场景的变化而改变,所以对于比如 Bank 这个词,它事先学好的 Word Embedding 中混合了几种语义,在应用中来了个新句子,即使从上下文中(比如句子包含 money 等词)明显可以看出它代表的是'银行'的含义,但是对应的 Word Embedding 内容也不会变,它还是混合了多种语义。
ELMo 的本质思想是:先用语言模型学好一个单词的 Word Embedding,此时多义词无法区分,不过这没关系。在实际使用 Word Embedding 的时候,单词已经具备了特定的上下文了,这个时候我可以根据上下文单词的语义去调整单词的 Word Embedding 表示,这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义,自然也就解决了多义词的问题了。所以 ELMo 本身是个根据当前上下文对 Word Embedding 动态调整的思路。
ELMo 采用了典型的两阶段过程,第一个阶段是利用语言模型进行预训练;第二个阶段是在做下游任务时,从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。
ELMo 的网络结构采用了双层双向 LSTM,目前语言模型训练的任务目标是根据单词 Wi 的上下文去正确预测单词 Wi,Wi 之前的单词序列 Context-before 称为上文,之后的单词序列 Context-after 称为下文。图中左端的前向双层 LSTM 代表正方向编码器,输入的是从左到右顺序的除了预测单词外 Wi 的上文 Context-before 和下文 Context-after;右端的逆向双层 LSTM 代表反方向编码器,输入的是从右到左的逆序的句子上文和下文;每个编码器的深度都是两层 LSTM 叠加,而每一层的正向和逆向单词编码会拼接到一起。
预训练好网络结构后,如何给下游任务使用呢?
下图展示了下游任务的使用过程,比如我们的下游任务仍然是 QA 问题,此时对于问句 X,我们可以先将句子 X 作为预训练好的 ELMo 网络的输入,这样句子 X 中每个单词在 ELMo 网络中都能获得对应的三个 Embedding(单词的 Embedding,句法层面的 Embedding,语义层面的 Embedding),之后给予这三个 Embedding 中的每一个 Embedding 一个权重 a,这个权重可以学习得来,根据各自权重累加求和,将三个 Embedding 整合成一个。然后将整合后的这个 Embedding 作为 X 句在自己任务的那个网络结构中对应单词的输入,以此作为补充的新特征给下游任务使用。对于上图所示下游任务 QA 中的回答句子 Y 来说也是如此处理。因为 ELMo 给下游提供的是每个单词的特征形式,所以这一类预训练的方法被称为"Feature-based Pre-Training"。
ELMo 也存在一些缺点:
Contextualized Word Embedding 技术是动态的 embedding 技术,word2vec 只会根据训练语料高频出现上下文对每一个词 embedding 做取舍;而 Contextualized Word Embedding 则是在 pretrain 预训练阶段会把每个词的所有可能语意都记入下来,在 finetune 具体任务时候,会根据输入文本的上下文信息激活 pretrain 时候的不同语意权重来表示更适合当前这个多义词的语意。
主流的序列模型都是基于复杂的循环神经网络或者是卷积神经网络构造而来的 Encoder-Decoder 模型,并且就算是目前性能最好的序列模型也都是基于注意力机制下的 Encoder-Decoder 架构。由于传统的 Encoder-Decoder 架构在建模过程中,下一个时刻的计算过程会依赖于上一个时刻的输出,而这种固有的属性就限制了传统的 Encoder-Decoder 模型就不能以并行的方式进行计算。
Transformer 架构的优点在于它完全摈弃了传统的循环结构,取而代之的是只通过注意力机制来计算模型输入与输出的隐含表示,而这种注意力的名字就是大名鼎鼎的自注意力机制(self-attention)。
所谓自注意力机制就是通过某种运算来直接计算得到句子在编码过程中每个位置上的注意力权重;然后再以权重和的形式来计算得到整个句子的隐含向量表示。最终,Transformer 架构就是基于这种的自注意力机制而构建的 Encoder-Decoder 模型。
自注意力机制的核心过程就是通过 Q 和 K 计算得到注意力权重;然后再作用于 V 得到整个权重和输出。具体的,对于输入 Q、K 和 V 来说,其输出向量的计算公式为: $$\text{Attention}(Q, K, V) = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V$$ 其中 Q、K 和 V 分别为 3 个矩阵,且其维度分别为 $d_k$。之所以要进行缩放这一步是因为通过实验作者发现,对于较大的 $d_k$ 来说在完成后将会得到很大的值,而这将导致在经过 softmax 操作后产生非常小的梯度,不利于网络的训练。
如果仅仅只是看着结构以及公式中的计算过程显然是不那么容易理解自注意力机制的含义,例如初学者最困惑的一个问题就是图 1-3 中的 Q、K 和 V 分别是怎么来的?现在,假设输入序列'我 是 谁',且已经通过某种方式得到了 1 个形状为的矩阵来进行表示,那么通过图 1-3 所示的过程便能够就算得到 Q、K 以及 V。
Q、K、V 其实就是输入 X 分别乘以 3 个不同的矩阵计算而来。此处对于计算得到的 Q、K、V,你可以理解为这是对于同一个输入进行 3 次不同的线性变换来表示其不同的 3 种状态。在计算得到 Q、K、V 之后,就可以进一步计算得到权重向量。
如图 1-5 所示,在经过上述过程计算得到了这个注意力权重矩阵之后我们不禁就会问到,这些权重值到底表示的是什么呢?对于权重矩阵的第 1 行来说,0.7 表示的就是'我'与'我'的注意力值;0.2 表示的就是'我'与'是'的注意力值;0.1 表示的就是'我'与'谁'的注意力值。换句话说,在对序列中的'我'进行编码时,应该将 0.7 的注意力放在'我'上,0.2 的注意力放在'是'上,将 0.1 的注意力放在谁上。
同理,对于权重矩阵的第 3 行来说,其表示的含义就是,在对序列中'谁'进行编码时,应该将 0.2 的注意力放在'我'上,将 0.1 的注意力放在'是'上,将 0.7 的注意力放在'谁'上。从这一过程可以看出,通过这个权重矩阵模型就能轻松的知道在编码对应位置上的向量时,应该以何种方式将注意力集中到不同的位置上。
不过从上面的计算结果还可以看到一点就是,模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置(虽然这符合常识)而可能忽略了其它位置。因此,作者采取的一种解决方案就是采用多头注意力机制(MultiHeadAttention)。
经过上面内容的介绍,我们算是在一定程度上对于自注意力机制有了清晰的认识,不过在上面我们也提到了自注意力机制的缺陷就是:模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置,因此作者提出了通过多头注意力机制来解决这一问题。同时,使用多头注意力机制还能够给予注意力层的输出包含有不同子空间中的编码表示信息,从而增强模型的表达能力。
所谓的多头注意力机制其实就是将原始的输入序列进行多组的自注意力处理过程;然后再将每一组自注意力的结果拼接起来进行一次线性变换得到最终的输出结果。
在论文中,作者使用了 8 个并行的自注意力模块来构建一个注意力层,并且对于每个自注意力模块都限定了维度。从这里其实可以发现,论文中所使用的多头注意力机制其实就是将一个大的高维单头拆分成了多个多头。
在多头注意力中,对于初学者来说一个比较经典的问题就是,在相同维度下使用单头和多头的区别是什么?这句话什么意思呢?以示例为例,此时的自注意力中使用了两个头,每个头的维度为 $d_k$,即采用了多头的方式。另外一种做法就是,只是用一个头,但是其维度为 $2*d_k$,即采用单头的方式。那么在这两种情况下有什么区别呢?
首先,从论文中内容可知,作者在头注意力机制与多头个数之间做了如下的限制:单个头注意力机制的维度乘上多头的个数就等于模型的维度。
同时,从图中可以看出,这里使用的多头数量,即 $h=8$。此时,对于第 1 个头来说有: $$\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)$$ 最后,可以将所有 head 在横向堆叠起来进行一个线性变换得到最终的 Output。
说了这么多,终于把铺垫做完了。此时,假如有如图 1-15 所示的头注意力计算过程: 该计算过程采用了头注意力机制来进行计算,且头的计算过程还可通过图 1-16 来进行表示。
那现在的问题是图中的 Output 能够计算得到吗?答案是不能。为什么?因为我没有告诉你这里的 $d_k$ 等于多少。如果我告诉你多头 $h=2$,那么毫无疑问图 1-16 的计算过程就等同于图 1-14 的计算过程,即 $d_k = d_{model} / h$。
现在回到一开始的问题上,根据上面的论述我们可以发现,在固定的 $d_{model}$ 情况下,不管是使用单头还是多头的方式,在实际的处理过程中直到进行注意力权重矩阵计算前,两者之前没有任何区别。当进行注意力权重矩阵计算时,$d_k$ 越大那么就会被切分得越小,进而得到的注意力权重分配方式越多。
从图中可以看出,如果 $h=1$,那么最终可能得到的就是一个各个位置只集中于自身位置的注意力权重矩阵;如果 $h>1$,那么就还可能得到另外一个注意力稍微分配合理的权重矩阵;同理如此。因而多头这一做法也恰好是论文作者提出用于克服模型在对当前位置的信息进行编码时,会过度的将注意力集中于自身的位置的问题。
同时,当 $d_{model}$ 不一样时,$d_k$ 的取值也不一样,进而使得对权重矩阵的 scale 的程度不一样。例如,如果 $d_{model}=512$,那么当 $h=1$ 时,则 $d_k=512$;当 $h=8$ 时,则 $d_k=64$。
所以,当模型的维度确定时,一定程度上 $h$ 越大整个模型的表达能力越强,越能提高模型对于注意力权重的合理分配。
熟悉文本处理的读者可能都知道,在对文本相关的数据进行建模时首先要做的便是对其进行向量化。例如在机器学习中,常见的文本表示方法有 one-hot 编码、词袋模型以及 TF-IDF 等。不过在深度学习中,更常见的做法便是将各个词(或者字)通过一个 Embedding 层映射到低维稠密的向量空间。因此,在 Transformer 模型中,首先第一步要做的同样是将文本以这样的方式进行向量化表示,并且将其称之为 Token Embedding,也就是深度学习中常说的词嵌入(Word Embedding)。
如果是换做之前的网络模型,例如 CNN 或者 RNN,那么对于文本向量化的步骤就到此结束了,因为这些网络结构本身已经具备了捕捉时序特征的能力,不管是 CNN 中的 n-gram 形式还是 RNN 中的时序形式。但是这对仅仅只有自注意力机制的网络结构来说却不行。为什么呢?根据自注意力机制原理的介绍我们知道,自注意力机制在实际运算过程中不过就是几个矩阵来回相乘进行线性变换而已。因此,这就导致即使是打乱各个词的顺序,那么最终计算得到的结果本质上却没有发生任何变换,换句话说仅仅只使用自注意力机制会丢失文本原有的序列信息。
如图 2-2 所示,在经过词嵌入表示后,序列'我 在 看 书'经过了一次线性变换。现在,我们将序列变成'书 在 看 我',然后同样以中间这个权重矩阵来进行线性变换,过程如图 2-3 所示。
根据图 2-3 中的计算结果来看,序列在交换位置前和交换位置后计算得到的结果在本质上并没有任何区别,仅仅只是交换了对应的位置。因此,基于这样的原因,Transformer 在原始输入文本进行 Token Embedding 后,又额外的加入了一个 Positional Embedding 来刻画数据在时序上的特征。
Since our model contains no recurrence and no convolution, in order for the model to make use of the order of the sequence, we must inject some information about the relative or absolute position of the tokens in the sequence.
说了这么多,那到底什么又是 Positional Embedding 呢?数无形时少直觉,下面我们先来通过一幅图直观看看经过 Positional Embedding 处理后到底产生了什么样的变化。
如图 2-4 所示,横坐标表示输入序列中的每一个 Token,每一条曲线或者直线表示对应 Token 在每个维度上对应的位置信息。在左图中,每个维度所对应的位置信息都是一个不变的常数;而在右图中,每个维度所对应的位置信息都是基于某种公式变换所得到。换句话说就是,左图中任意两个 Token 上的向量都可以进行位置交换而模型却不能捕捉到这一差异,但是加入右图这样的位置信息模型却能够感知到。例如位置 20 这一处的向量,在左图中无论你将它换到哪个位置,都和原来一模一样;但在右图中,你却再也找不到与位置 20 处位置信息相同的位置。
下面,笔者通过两个实际的示例来进行说明。
如图 2-5 所示,原始输入在经过 Token Embedding 后,又加入了一个常数位置信息的 Positional Embedding。在经过一次线性变换后便得到了图 2-5 左右边所示的结果。接下来,我们再交换序列的位置,并同时进行 Positional Embedding 观察其结果。
如图 2-6 所示,在交换序列位置后,采用同样的 Positional Embedding 进行处理,并且进行线性变换。可以发现,其计算结果同图 2-5 中的计算结果本质上也没有发生变换。因此,这就再次证明,如果 Positional Embedding 中位置信息是以常数形式进行变换,那么这样的 Positional Embedding 是无效的。
在 Transformer 中,作者采用了如公式所示的规则来生成各个维度的位置信息,其可视化结果如图 2-4 右所示。 $$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$ $$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$ 其中 $PE$ 就是这个 Positional Embedding 矩阵,$pos$ 表示具体的某一个位置,$i$ 表示具体的某一维度。
最终,在融入这种非常数的 Positional Embedding 位置信息后,便可以得到对比结果。
从图 2-7 可以看出,在交换位置前与交换位置后,与同一个权重矩阵进行线性变换后的结果截然不同。因此,这就证明通过 Positional Embedding 可以弥补自注意力机制不能捕捉序列时序信息的缺陷。
说完 Transformer 中的 Embedding 后,接下来我们再来继续探究 Transformer 的网络结构。
如图 2-8 所示便是一个单层 Transformer 网络结构图。
如图 2-8 所示,整个 Transformer 网络包含左右两个部分,即 Encoder 和 Decoder。下面,我们就分别来对其中的各个部分进行介绍。
首先,对于 Encoder 来说,其网络结构如图 2-8 左侧所示(尽管论文中是以 6 个这样相同的模块堆叠而成,但这里我们先以堆叠一层来进行介绍,多层的 Transformer 结构将在稍后进行介绍)。
如图 2-9 所示,对于 Encoder 部分来说其内部主要由两部分网络所构成:多头注意力机制和两层前馈神经网络。
同时,对于这两部分网络来说,都加入了残差连接,并且在残差连接后还进行了层归一化操作。这样,对于每个部分来说其输出均为 $x + Sublayer(x)$,并且在都加入了 Dropout 操作。
进一步,为了便于在这些地方使用残差连接,这两部分网络输出向量的维度均为 $d_{model}$。
对于第 2 部分的两层全连接网络来说,其具体计算过程为: $$FFN(x) = max(0, xW_1 + b_1)W_2 + b_2$$ 其中输入的维度为 $d_{model}$,第 1 层全连接层的输出维度为 $d_{ff}$,第 2 层全连接层的输出为 $d_{model}$,且同时对于第 1 层网络的输出还运用了 Relu 激活函数。
到此,对于单层 Encoder 的网络结构就算是介绍完了,接下来让我们继续探究 Decoder 部分的网络结构。
同 Encoder 部分一样,论文中也采用了 6 个完全相同的网络层堆叠而成,不过这里我们依旧只是先看 1 层时的情况。对于 Decoder 部分来说,其整体上与 Encoder 类似,只是多了一个用于与 Encoder 输出进行交互的多头注意力机制。
不同于 Encoder 部分,在 Decoder 中一共包含有 3 个部分的网络结构。最上面的和最下面的部分(暂时忽略 Mask)与 Encoder 相同,只是多了中间这个与 Encoder 输出(Memory)进行交互的部分,作者称之为"Encoder-Decoder attention"。对于这部分的输入,Q 来自于下面多头注意力机制的输出,K 和 V 均是 Encoder 部分的输出(Memory)经过线性变换后得到。而作者之所以这样设计也是在模仿传统 Encoder-Decoder 网络模型的解码过程。
为了能够更好的理解这里 Q、K、V 的含义,我们先来看看传统的基于 Encoder-Decoder 的 Seq2Seq 翻译模型是如何进行解码的。
如图 2-11 所示是一个经典的基于 Encoder-Decoder 的机器翻译模型。左下边部分为编码器,右下边部分为解码器,左上边部分便是注意力机制部分。在图 2-11 中,$h_t$ 表示的是在编码过程中,各个时刻的隐含状态,称之为每个时刻的 Memory;$s_t$ 表示解码当前时刻时的隐含状态。此时注意力机制的思想在于,希望模型在解码的时刻能够参考编码阶段每个时刻的记忆。
因此,在解码第一个时刻"~"时,会首先同每个记忆状态进行相似度比较得到注意力权重。这个注意力权重所蕴含的意思就是,在解码第一个时刻时应该将 $s_t$ 的注意力放在编码第一个时刻的记忆上(其它的同理),最终通过加权求和得到 4 个 Memory 的权重和,即 context vector。同理,在解码第二时刻"我"时,也会遵循上面的这一解码过程。可以看出,此时注意力机制扮演的就是能够使得 Encoder 与 Decoder 进行交互的角色。
回到 Transformer 的 Encoder-Decoder attention 中,K 和 V 均是编码部分的输出 Memory 经过线性变换后的结果(此时的 Memory 中包含了原始输入序列每个位置的编码信息),而 Q 是解码部分多头注意力机制输出的隐含向量经过线性变换后的结果。在 Decoder 对每一个时刻进行解码时,首先需要做的便是通过 Q 与 K 进行交互(query 查询),并计算得到注意力权重矩阵;然后再通过注意力权重与 V 进行计算得到一个权重向量,该权重向量所表示的含义就是在解码时如何将注意力分配到 Memory 的各个位置上。
进一步,在得到这个解码向量并经过图 2-10 中最上面的两层全连接层后,便将其输入到分类层中进行分类得到当前时刻的解码输出值。
当第 1 个时刻的解码过程完成之后,解码器便会将解码第 1 个时刻时的输入,以及解码第 1 个时刻后的输出均作为解码器的输入来解码预测第 2 个时刻的输出。整个过程可以通过如图 2-14 所示的过程来进行表示。
如图 2-14 所示,Decoder 在对当前时刻进行解码输出时,都会将当前时刻之前所有的预测结果作为输入来对下一个时刻的输出进行预测。假设现在需要将"我 是 谁"翻译成英语"who am i",且解码预测后前两个时刻的结果为"who am",接下来需要对下一时刻的输出"i"进行预测,那么整个过程就可以通过图 2-15 和图 2-16 来进行表示。
如图 2-15 所示,左上角的矩阵是解码器对输入" ~who am"这 3 个词经过解码器中自注意力机制编码后的结果;左下角是编码器对输入"我 是 谁"这 3 个词编码后的结果(同图 2-12 中的一样);两者分别在经过线性变换后便得到了 Q、K 和 V 这 3 个矩阵。此时值得注意的是,左上角矩阵中的每一个向量在经过自注意力机制编码后,每个向量同样也包含了其它位置上的编码信息。
进一步,Q 与 K 作用和便得到了一个权重矩阵;再将其与 V 进行线性组合便得到了 Encoder-Decoder attention 部分的输出。
如图 2-16 所示,左下角便是 Q 与 K 作用后的权重矩阵,它的每一行就表示在对 Memory(这里指图 2-16 中的 V)中的每一位置进行解码时,应该如何对注意力进行分配。例如第 3 行的含义就是在解码当前时刻时应该将 $s_t$ 的注意力放在 Memory 中的"我"上,其它同理。这样,在经过解码器中的两个全连接层后,便得到了解码器最终的输出结果。接着,解码器会循环对下一个时刻的输出进行解码预测,直到预测结果为""或者达到指定长度后停止。
同时,这里需要注意的是,在通过模型进行实际的预测时,只会取解码器输出的其中一个向量进行分类,然后作为当前时刻的解码输出。例如图 2-16 中解码器最终会输出一个形状为 [3, tgt_vocab_len] 的矩阵,那么只会取其最后一个向量喂入到分类器中进行分类得到当前时刻的解码输出。具体细节见后续代码实现。
在介绍完预测时 Decoder 的解码过程后,下面就继续来看在网络在训练过程中是如何进行解码的。从 2.2.3 小节的内容可以看出,在真实预测时解码器需要将上一个时刻的输出作为下一个时刻解码的输入,然后一个时刻一个时刻的进行解码操作。显然,如果训练时也采用同样的方法那将是十分费时的。因此,在训练过程中,解码器也同编码器一样,一次接收解码时所有时刻的输入进行计算。这样做的好处,一是通过多样本并行计算能够加快网络的训练速度;二是在训练过程中直接喂入解码器正确的结果而不是上一时刻的预测值(因为训练时上一时刻的预测值可能是错误的)能够更好的训练网络。
例如在用平行预料"我 是 谁"<==>"who am i"对网络进行训练时,编码器的输入便是"我 是 谁",而解码器的输入则是" ~who am i",对应的正确标签则是"who am i "。
假设现在解码器的输入" ~who am i"在分别乘上一个矩阵进行线性变换后得到了 Q、K、V,且 Q 与 K 作用后得到了注意力权重矩阵(此时还未进行 softmax 操作)。
从图 2-17 可以看出,此时已经计算得到了注意力权重矩阵。由第 1 行的权重向量可知,在解码第 1 个时刻时应该将(严格来说应该是经过 softmax 后的值)的注意力放到"~"上,的注意力放到"who"上等等。不过此时有一个问题就是,在 2.2.3 节中笔者介绍到,模型在实际的预测过程中只是将当前时刻之前(包括当前时刻)的所有时刻作为输入来预测下一个时刻,也就是说模型在预测时是看不到当前时刻之后的信息。因此,Transformer 中的 Decoder 通过加入注意力掩码机制来解决了这一问题。
如图 2-18 所示,左边依旧是通过 Q 和 K 计算得到了注意力权重矩阵(此时还未进行 softmax 操作),而中间的就是所谓的注意力掩码矩阵,两者在相加之后再乘上矩阵 V 便得到了整个自注意力机制的输出,也就是图 2-10 中的 Masked Multi-Head Attention。
那为什么注意力权重矩阵加上这个注意力掩码矩阵就能够达到这样的效果呢?以图 2-18 中第 1 行权重为例,当解码器对第 1 个时刻进行解码时其对应的输入只有"~",因此这就意味着此时应该将所有的注意力放在第 1 个位置上(尽管在训练时解码器一次喂入了所有的输入),换句话说也就是第 1 个位置上的权重应该是 1,而其它位置则是 0。从图 2-17 可以看出,第 1 行注意力向量在加上第 1 行注意力掩码,再经过 softmax 操作后便得到了一个类似的向量。那么,通过这个向量就能够保证在解码第 1 个时刻时只能将注意力放在第 1 个位置上的特性。同理,在解码后续的时刻也是类似的过程。
到此,对于整个单层 Transformer 的网络结构以及编码解码过程就介绍完了,更多细节内容见后续代码实现。
在刚接触 Transformer 的时候,有的人会认为在 Decoder 中,既然已经有了 Attention mask 那么为什么还需要 Positional Embedding 呢?如图 2-18 所示,持这种观点的朋友认为,Attention mask 已经有了使得输入序列依次输入解码器的能力,因此就不再需要 Positional Embedding 了。这样想对吗?
根据 2.2.4 节内容的介绍可以知道,Attention mask 的作用只有一个,那就是在训练过程中掩盖掉当前时刻之后所有位置上的信息,而这也是在模仿模型在预测时只能看到当前时刻及其之前位置上的信息。因此,持有上述观点的朋友可能是把'能看见'和'能看见且有序'混在一起了。
虽然看似有了 Attention mask 这个掩码矩阵能够使得 Decoder 在解码过程中可以有序地看到当前位置之前的所有信息,但是事实上没有 Positional Embedding 的 Attention mask 只能做到看到当前位置之前的所有信息,而做不到有序。前者的'有序'指的是喂入解码器中序列的顺序,而后者的'有序'指的是序列本身固有的语序。
如果不加 Postional Embedding 的话,那么以下序列对于模型来说就是一回事:
→ 北 → 京 → 欢 → 迎 → 你 →
→ 北 → 京 → 迎 → 欢 → 你 →
→ 北 → 京 → 你 → 迎 → 欢 →
虽然此时 Attention mask 具有能够让上述序列一个时刻一个时刻的按序喂入到解码器中,但是它却无法识别出这句话本身固有的语序。
在 Transformer 中各个部分的 Q、K、V 到底是怎么来的一直以来都是初学者最大的一个疑问,并且这部分内容在原论文中也没有进行交代,只是交代了如何根据 Q、K、V 来进行自注意力机制的计算。虽然在第 2 部分的前面几个小节已经提及过了这部分内容,但是这里再给大家进行一次总结。
根据图 2-8(Transformer 结构图)可知,在整个 Transformer 中涉及到自注意力机制的一共有 3 个部分:Encoder 中的 Multi-Head Attention;Decoder 中的 Masked Multi-Head Attention;Encoder 和 Decoder 交互部分的 Multi-Head Attention。
① 对于 Encoder 中的 Multi-Head Attention 来说,其原始 q、k、v 均是 Encoder 的 Token 输入经过 Embedding 后的结果。q、k、v 分别经过一次线性变换(各自乘以一个权重矩阵)后得到了 Q、K、V(也就是图 1-4 中的示例),然后再进行自注意力运算得到 Encoder 部分的输出结果 Memory。
② 对于 Decoder 中的 Masked Multi-Head Attention 来说,其原始 q、k、v 均是 Decoder 的 Token 输入经过 Embedding 后的结果。q、k、v 分别经过一次线性变换后得到了 Q、K、V,然后再进行自注意力运算得到 Masked Multi-Head Attention 部分的输出结果,即待解码向量。
对于 Encoder 和 Decoder 交互部分的 Multi-Head Attention,其原始 q、k、v 分别是上面的带解码向量、Memory 和 Memory。q、k、v 分别经过一次线性变换后得到了 Q、K、V(也就是图 2-12 中的示例),然后再进行自注意力运算得到 Decoder 部分的输出结果。之所以这样设计也是在模仿传统 Encoder-Decoder 网络模型的解码过程。
在通过前面几部分内容详细介绍完 Transformer 网络结构的原理后,接下来就让我们来看一看如何借用 Pytorch 框架来实现 MultiHeadAttention 这一结构。同时,需要说明的一点是,下面所有的实现代码都是笔者直接从 Pytorch 1.4 版本中 torch.nn.Transformer 模块里摘取出来的简略版,目的就是为了让大家对于整个实现过程有一个清晰的认识。并且为了使得大家在阅读完以下内容后也能够对 Pytorch 中的相关模块有一定的了解,所以下面的代码在变量名方面也与 Pytorch 保持了一致。
在第 2 部分中,笔者详细介绍了单层 Transformer 网络结构中的各个组成部分。尽管多层 Transformer 就是在此基础上堆叠而来,不过笔者认为还是有必要在这里稍微提及一下。
如图 3-1 所示便是一个单层 Transformer 网络结构图,左边是编码器右边是解码器。而多层的 Transformer 网络就是在两边分别堆叠了多个编码器和解码器的网络模型。
如图 3-2 所示便是一个多层的 Transformer 网络结构图(原论文中采用了 6 个编码器和 6 个解码器),其中的每一个 Encoder 都是图 3-1 中左边所示的网络结构(Decoder 同理)。可以发现,它真的就是图 3-1 堆叠后的形式。不过需要注意的是其整个解码过程。
在多层 Transformer 中,多层编码器先对输入序列进行编码,然后得到最后一个 Encoder 的输出 Memory;解码器先通过 Masked Multi-Head Attention 对输入序列进行编码,然后将输出结果同 Memory 通过 Encoder-Decoder Attention 后得到第 1 层解码器的输出;接着再将第 1 层 Decoder 的输出通过 Masked Multi-Head Attention 进行编码,最后再将编码后的结果同 Memory 通过 Encoder-Decoder Attention 后得到第 2 层解码器的输出,以此类推得到最后一个 Decoder 的输出。
值得注意的是,在多层 Transformer 的解码过程中,每一个 Decoder 在 Encoder-Decoder Attention 中所使用的 Memory 均是同一个。
由于在实现多头注意力时需要考虑到各种情况下的掩码,因此在这里需要先对这部分内容进行介绍。在 Transformer 中,主要有两个地方会用到掩码这一机制。第 1 个地方就是在 2.2.4 节中介绍到的 Attention Mask,用于在训练过程中解码的时候掩盖掉当前时刻之后的信息;第 2 个地方便是对一个 batch 中不同长度的序列在 Padding 到相同长度后,对 Padding 部分的信息进行掩盖。下面分别就这两种情况进行介绍。
如图 3-3 所示,在训练过程中对于每一个样本来说都需要这样一个对称矩阵来掩盖掉当前时刻之后所有位置的信息。
从图 3-3 可以看出,这个注意力掩码矩阵的形状为 [tgt_len,tgt_len],其具体 Mask 原理在 2.2.4 节中笔者已经介绍过,这里就不再赘述。在后续实现过程中,我们将通过 generate_square_subsequent_mask 方法来生成这样一个矩阵。同时,在后续多头注意力机制实现中,将通过 attn_mask 这一变量名来指代这个矩阵。
在 Transformer 中,使用到掩码的第 2 个地方便是 Padding Mask。由于在网络的训练过程中同一个 batch 会包含有多个文本序列,而不同的序列长度并不一致。因此在数据集的生成过程中,就需要将同一个 batch 中的序列 Padding 到相同的长度。但是,这样就会导致在注意力的计算过程中会考虑到 Padding 位置上的信息。
如图 3-4 所示,P 表示 Padding 的位置,右边的矩阵表示计算得到的注意力权重矩阵。可以看到,此时的注意力权重对于 Padding 位置上的信息也会加以考虑。因此在 Transformer 中,作者通过在生成训练集的过程中记录下每个样本 Padding 的实际位置;然后再将注意力权重矩阵中对应位置的权重替换成负无穷,经 softmax 操作后对应 Padding 位置上的权重就变成了 0,从而达到了忽略 Padding 位置信息的目的。这种做法也是 Encoder-Decoder 网络结构中通用的一种办法。
如图 3-5 所示,对于"我 是 谁 P P"这个序列来说,前 3 个字符是正常的,后 2 个字符是 Padding 后的结果。因此,其 Mask 向量便为 [True, True, True, False, False]。通过这个 Mask 向量可知,需要将权重矩阵的最后两列替换成负无穷,在后续我们会通过 torch.masked_fill 这个方法来完成这一步,并且在实现时将使用 key_padding_mask 来指代这一向量。
到此,对于 Transformer 中所要用到 Mask 的地方就介绍完了,下面正式来看如何实现多头注意力机制。
根据前面的介绍可以知道,多头注意力机制中最为重要的就是自注意力机制,也就是需要前计算得到 Q、K 和 V,如图 3-6 所示。
然后再根据 Q、K、V 来计算得到最终的注意力编码,如图 3-7 所示。
同时,为了避免单个自注意力机制计算得到的注意力权重过度集中于当前编码位置自己所在的位置(同时更应该关注于其它位置),所以作者在论文中提到通过采用多头注意力机制来解决这一问题,如图 3-8 所示。
Transformer 结构第一次提出来是用于机器翻译任务,提出后自然有个想法:「Transformer 结构能不能用于训练语言模型?」答案是肯定的。RNN、LSTM 等网络结构训练语言模型的最大缺点在于容易梯度消失,难以学习长期依赖,但是 Transformer 没有合格缺点,因为它没有沿时间线进行横向传播的过程,这样来说,Transformer 很适合训练语言模型。
怎么训练?很简单,举个例子,假如现在我们有一句话包含 100 个 token,在预测第一个 token 的时候,我们把其它的 99 个 token 进行 attention mask 掉,只输入一个开始标记,让模型预测第一个 token;在预测第二个 token 的时候,把剩下的 98 个 token attention mask 掉,只让模型看见开始标记和第一个 token;在预测第三个 token 的时候,把剩下的 97 个 token attention mask 掉,只让模型看见开始标记和前两个 token。。。损失函数为真实 token 和预测 token 的交叉熵,这样模型不断学习,不断更新参数,就训练得到一个语言模型了。
想想这样有个什么问题?问题就是真实的文本可没有只包含 100 个 token 这么少。举个例子,假设我现在手头有个文章是包含 4000 个 token 的,那我就要把这 4000 个 token 都作为输入,原理上可以这么做,但实际上,输入的 token 很多的情况下,中间层 attention 的计算资源会爆炸。
Vanilla Transformer 的做法是「切片」,如把 4000 个 token 切成 8 段,每段包含 500 个 token,再对每段进行独立的训练,也即在第二段训练的时候,是看不到第一段的信息的,这样导致的问题就是「上下文碎片问题(context fragmentation problem)」,由于切片长度限制,模型并不能学习到足够常的依赖信息,这样会大大损害学习出来的语言模型的性能。
在评估时,为了能利用训练时的最长上下文信息,是每个时间步向右移动一个 token,导致的结构是评估耗费的计算资源很大。
「改进点一:循环机制」
在计算每个 segment 的时候,通过缓存上一个 segment 的信息,把前面 segment 的信息也考虑进正向传播过程中(上一个 segment 的信息虽然参与正向传播,但上一个 segment 是不会参与反向传播的)。具体的看,下面的式子, $$h_n^{(t)} = \text{SG}(h_{n-1}^{(t)}) + h_n^{(t-1)}$$ 这里的 $h$ 指的是 Transformer 第 n-1 层的第 t 个 segmetn 的 Encoder 的输出,把它与 Transformer 第 n 层的第 t 的 encoder 的输出沿着时间轴拼接在一起,SG 表示 stop-gradient,即前一个 segmetn 的信息只参与正向传播,并不会用到反向传。另外,在计算查询矩阵 q 时,只会用当前 segmetn 来计算得到;只有在计算键值向量时,才会用到拼接的 segmetn 向量。
这样的做法,能大大缓解上下文碎片的问题,举个例子,假设 Transformer 的 encoder 一共有 4 层,每个 segment 为 500 个 token。根据循环机制的原理,第 4 层的第 T 个 segmetn 的输出不仅考虑了第三层 encoder 的第 T 个 encoder 的输出,也考虑了第 T-1 个 encdoer 的输出,而第 T-1 个 encdoer 的输出,不仅考虑了第二层 encoder 的第 T-1 个 encdoer 的输出,也考虑了第 T-2 个 encdoer 的输出,也即,上下文的最大依赖长度是线性增加的,如 O(N*L),如这里所说的例子,虽然,一个 segmetn 是 500 个 token,但其实在最后输出时,足足考虑了 4 * 500 = 2000 个 token 这么多!上下文碎片的问题也就自然得到了大大的缓解了。
另外,在评估时,由于采用了循环机制,我们就不必每次只向右移动一步了,而且可以采用同训练时候差不多的片段机制,从而大大提高了评估效率。
「改进点二:相对位置编码」
上面所说的循环机制还有个问题待解决,就是位置编码,我们知道,原生的 Transformer 使用的是绝对位置编码,但绝对位置编码跟循环机制结合会导致一个问题,举个例子,我们在计算第 T 的输出时,不仅考虑了第 T 个片段的输入,还考虑了第 T-1 个片段的输入,假设我们采用绝对位置编码,那第 T 个片段和 T-1 个片段的第 1 个 token 的位置编码是一样的,但这是明显的不合理的。因此,作者提出了一种相对位置编码的思想。
具体的,原生的绝对位置编码在计算 attention 时,如下式所示, $$A_{ij} = \frac{(q_i)^T k_j}{\sqrt{d}}$$ 采用相对位置编码, $$A_{ij} = \frac{(q_i)^T (k_j + r_{j-i})}{\sqrt{d}}$$ 通常这样的变换,上面的每一项都有其相应的意义 (a)项为基于内容的寻址; (b)项为基于内容的位置偏置; (c)项为全局的内容偏置; (d)为全局的位置偏置。
把循环机制和相对位置编码的信息合并后,Transformer-XL 的最终形态。
相比起 Sparse Attention 需要用到稀疏算子而很难发挥 GPU、TPU 硬件性能的问题。Switch Transformer 不需要稀疏算子,可以更好的适应 GPU、TPU 等稠密硬件。主要的想法是简化稀疏路由。
即在自然语言 MoE(Mixture of experts)层中,只将 token 表征发送给单个专家而不是多个的表现会更好。模型架构如上图,中间蓝色部分是比价关键的部分,可以看到每次 router 都只把信息传给分数 p 最大的单个 FFN。而这个操作可以大大降低计算量。
然后另一方面,其成功的原因是有一个很秀的并行策略,其同时结合了数据并行 + 模型并行 + expert 并行。具体如下图:
其实对应模型架构来看,experts 并行是算子间的并行,其对应的 FFN 内部有算子级的模型并行,而整个 experts 在计算图上又是个多并行 FFN 分支,这是算子间模型并行,所以能够获得更低的通信开销,提高并行的效率。
Bert 模型是在 2018 年由 Google 在《Pre-training of Deep Bidirectional Transformers for Language Understanding(2018)》一文中提出。
在 Bert 模型的基础上,有很多针对 Bert 的改进模型,这里介绍几种常见的改进模型。
(1)RoBERTa RoBERTa 是 2019 年在《A Robustly Optimized BERT Pretraining Approach(2019)》一文中提出,在该文中细致的对 Bert 模型的训练方式进行了对比实验和分析,并基于此总结出了能够提升 Bert 效果的训练方法,主要包括以下几个方面:动态 mask 机制、移除了 NSP 任务、batch size 更大、训练时间更长、训练数据更多、训练序列更长。
(2)ALBERT ALBERT 是在 2020 年《A LITE BERT FOR SELF-SUPERVISED LEARNING OF LANGUAGE REPRESENTATIONS(2020)》一文中提出,主要提出了一个轻量级的 Bert 模型,以此降低 Bert 的运行开销。为了减少 Bert 资源开销,该文主要提出了两个优化:Factorized embedding parameterization 以及 Cross-layer parameter sharing。其中 Factorized embedding parameterization 将嵌入矩阵分解为两个小的矩阵,Cross-layer parameter sharing 是让跨 ALBERT 的每一层共享 transformer 权重,这将显着著减少参数。除了上述两个降低 Bert 运行开销的优化外,ALBERT 提出了 inter-senetnce loss 这一新的优化目标。
(3)ELECTRA ELECTRA 是在 2020 年《PRE-TRAINING TEXT ENCODERS AS DISCRIMINATORS RATHER THAN GENERATORS(ICLR 2020)》一文中提出,该方法的核心思路采用了 GAN 的思路。模型包括一个 generator 生成器 和一个 discriminator 判别器。生成器通常是一个小的 MLM,可以学习预测被 mask token 的原始单词。判别器被训练以鉴别输入 token 是否被生成器替换。通过对抗学习的方式,让模型从文本中提取信息的能力增强。
(4)ERNIE(THU) 清华版的 ERNIE(THU),把知识图谱的信息整合进 pre-train 的过程中,具体地,对于文本中的实体,在知识库中找到对应的 entity,利用 transE 来进行表示之后,在 BERT 的 text encoder 之上再加一层 knowledge encoder。文章主要的 contribution 就在于想到把 KG 融合到预训练 language model 的过程中去。
(5)ERNIE(Baidu) 百度版的 ERNIE(Baidu),重点在 mask 的 level 上对中文文本做了调整,英文 mask 单词是很直接的想法,而中文的处理一般是以词为单位,因此会有 phrase mask 以及 entity mask,例如人名、地名的 mask。通过添加两种 mask 机制,来让 language model 隐式地学习到 entity 之间的关联。比较有趣的一点是,作者还在 Pre-train 中加入了 Dialogue 语料,结果显示效果也会有一些提高。这指明了一个方向,在预训练阶段,除了修改模型、目标函数以外,选择高质量、特定 domain 的语料也是可行的方向。
基于 Bert 和 GPT 模型,后续研究者们又提出了很多改进模型,这里主要介绍 UniLM、XLNet、BART 这 3 种。
(1)UniLM UniLM 是 2019 年在《Unified Language Model Pre-training for Natural Language Understanding and Generation(NIPS 2019)》一文中提出,融合了 EMLo、BERT、GPT3 种语言模型优化目标,通过精巧的控制 mask 方式在一个模型中同时实现了 3 种语言模型优化任务,在 pretrain 过程交替使用 3 种优化目标。下图比较形象的描述了 UniLM 是如何利用 mask 机制来控制 3 种不同的优化任务,核心思路是利用 mask 控制生成每个 token 时考虑哪些上下文的信息。
(2)XLNet XLNet 是 2019 年在《Generalized Autoregressive Pretraining for Language Understanding(NIPS 2019)》一文中提出,文中将无监督语言模型分成两类,一类是 AR 自回归模型,如 GPT、ELMo 这种使用单向语言模型建模概率分布的方法;另一类是 AE 自编码模型,如 BERT 这种通过预测句子中 token 的方法。XLNet 融合了 AR 模型和 AE 模型各自的优点,既能建模概率密度,适用于文本生成类任务,又能充分使用双向上下文信息。XLNet 实现 AR 和 AE 融合的主要思路为,对输入文本进行排列组合,然后对于每个排列组合使用 AR 的方式训练,不同排列组合使每个 token 都能和其他 token 进行信息交互,同时每次训练又都是 AR 的。
(3)BART BART 是 2019 年在《Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension(2019)》一文中提出,其包括两个阶段:首先原文本使用某种 noise function 进行破坏,然后使用 sequence-to-sequence 模型还原原始的输入文本。下图中左侧为 Bert 的训练方式,中间为 GPT 的训练方式,右侧为 BART 的训练方式。首先,将原始输入文本使用某些 noise function,得到被破坏的文本。这个文本会输入到类似 Bert 的 Encoder 中。在得到被破坏文本的编码后,使用一个类似 GPT 的结构,采用自回归的方式还原出被破坏之前的文本。
Generative Pre-trained Transformer(GPT)系列是由 OpenAI 提出的非常强大的预训练语言模型,这一系列的模型可以在非常复杂的 NLP 任务中取得非常惊艳的效果,例如文章生成,代码生成,机器翻译,Q&A 等,而完成这些任务并不需要有监督学习进行模型微调。而对于一个新的任务,GPT 仅仅需要非常少的数据便可以理解这个任务的需求并达到接近或者超过 state-of-the-art 的方法。
当然,如此强大的功能并不是一个简单的模型能搞定的,GPT 模型的训练需要超大的训练语料,超多的模型参数以及超强的计算资源。GPT 系列的模型结构秉承了不断堆叠 transformer 的思想,通过不断的提升训练语料的规模和质量,提升网络的参数数量来完成 GPT 系列的迭代更新的。GPT 也证明了,通过不断的提升模型容量和语料规模,模型的能力是可以不断提升的。
这篇文章会依次介绍 GPT-1,GPT-2,GPT-3,并介绍它们基于上个版本的改进点,文章主要的介绍的包括四个主要方向:算法的思想和目标,使用的数据集和预处理方式,模型结构以及算法的性能。
在 GPT-1 之前(和 ELMo 同一年),传统的 NLP 模型往往使用大量的数据对有监督的模型进行任务相关的模型训练,但是这种有监督学习的任务存在两个缺点:
这里介绍的 GPT-1 的思想是先通过在无标签的数据上学习一个通用的语言模型,然后再根据特定热任务进行微调,处理的有监督任务包括 自然语言推理(Natural Language Inference 或者 Textual Entailment):判断两个句子是包含关系(entailment),矛盾关系(contradiction),或者中立关系(neutral); 问答和常识推理(Question answering and commonsense reasoning):类似于多选题,输入一个文章,一个问题以及若干个候选答案,输出为每个答案的预测概率; 语义相似度(Semantic Similarity):判断两个句子是否语义上市是相关的; 分类(Classification):判断输入文本是指定的哪个类别。
将无监督学习左右有监督模型的预训练目标,因此叫做通用预训练(Generative Pre-training,GPT)。
在第 1 节的时候,我们介绍了 GPT-1 处理的 4 个不同的任务,这些任务有的只有一个输入,有的则有多组形式的输入。对于不同的输入,GPT-1 有不同的处理方式,具体介绍如下: 分类任务:将起始和终止 token 加入到原始序列两端,输入 transformer 中得到特征向量,最后经过一个全连接得到预测的概率分布; 自然语言推理:将前提(premise)和假设(hypothesis)通过分隔符(Delimiter)隔开,两端加上起始和终止 token。再依次通过 transformer 和全连接得到预测结果; 语义相似度:输入的两个句子,正向和反向各拼接一次,然后分别输入给 transformer,得到的特征向量拼接后再送给全连接得到预测结果; 问答和常识推理:将 N 个选项的问题抽象化为 N 个二分类问题,即每个选项分别和内容进行拼接,然后各送入 transformer 和全连接中,最后选择置信度最高的作为预测结果。
GPT-1 使用了 BooksCorpus 数据集,这个数据集包含本没有发布的书籍。作者选这个数据集的原因有二:1. 数据集拥有更长的上下文依赖关系,使得模型能学得更长期的依赖关系;2. 这些书籍因为没有发布,所以很难在下游数据集上见到,更能验证模型的泛化能力。
GPT-1 使用了 12 层的 transformer,使用了掩码自注意力头,掩码的使用使模型看不见未来的信息,得到的模型泛化能力更强。
在有监督学习的 12 个任务中,GPT-1 在 9 个任务上的表现超过了 state-of-the-art 的模型。在没有见过数据的 zero-shot 任务中,GPT-1 的模型要比基于 LSTM 的模型稳定,且随着训练次数的增加,GPT-1 的性能也逐渐提升,表明 GPT-1 有非常强的泛化能力,能够用到和有监督任务无关的其它 NLP 任务中。GPT-1 证明了 transformer 对学习词向量的强大能力,在 GPT-1 得到的词向量基础上进行下游任务的学习,能够让下游任务取得更好的泛化能力。对于下游任务的训练,GPT-1 往往只需要简单的微调便能取得非常好的效果。
GPT-1 在未经微调的任务上虽然也有一定效果,但是其泛化能力远远低于经过微调的有监督任务,说明了 GPT-1 只是一个简单的领域专家,而非通用的语言学家。
GPT-2 的目标旨在训练一个泛化能力更强的词向量模型,它并没有对 GPT-1 的网络进行过多的结构的创新与设计,只是使用了更多的网络参数和更大的数据集。下面我们对 GPT-2 展开详细的介绍。
GPT-2 的学习目标是使用无监督的预训练模型做有监督的任务。因为文本数据的时序性,一个输出序列可以表示为一系列条件概率的乘积: $$P(y_1, y_2, ..., y_T) = \prod_{t=1}^{T} P(y_t | y_1, ..., y_{t-1})$$
GPT-2 训练了 4 组不同的层数和词向量的长度的模型,具体值见表 2。通过这 4 个模型的实验结果我们可以看出随着模型的增大,模型的效果是不断提升的。
截止编写此文前,GPT-3 是目前最强大的语言模型,仅仅需要 zero-shot 或者 few-shot,GPT-3 就可以在下游任务表现的非常好。除了几个常见的 NLP 任务,GPT-3 还在很多非常困难的任务上也有惊艳的表现,例如撰写人类难以判别的文章,甚至编写 SQL 查询语句,React 或者 JavaScript 代码等。而这些强大能力的能力则依赖于 GPT-3 疯狂的 1750 亿的参数量,45TB 的训练数据以及高达 1200 万美元的训练费用。
In-context learning 是这篇论文中介绍的一个重要概念,要理解 in-context learning,我们需要先理解 meta-learning(元学习)。对于一个少样本的任务来说,模型的初始化值非常重要,从一个好的初始化值作为起点,模型能够尽快收敛,使得到的结果非常快的逼近全局最优解。元学习的核心思想在于通过少量的数据寻找一个合适的初始化范围,使得模型能够在有限的数据集上快速拟合,并获得不错的效果。
这里的介绍使用的是 MAML(Model-Agnostic Meta-Learning)算法,正常的监督学习是将一个批次的数据打包成一个 batch 进行学习。但是元学习是将一个个任务打包成 batch,每个 batch 分为支持集(support set)和质询集(query set),类似于学习任务中的训练集和测试集。
而 GPT-3 中据介绍的 in-context learning(情境学习)则是元学习的内循环,而基于语言模型的 SGT 则是外循环。
而另外一个方向则是提供容量足够大的 transformer 模型来对语言模型进行建模。而近年来使用大规模的网络来训练语言模型也成为了非常行之有效的策略,这也促使 GPT-3 一口气将模型参数提高到 1750 亿个。
在 few-shot learning 中,提供若干个(10-100 个)示例和任务描述供模型学习。one-shot laerning 是提供 1 个示例和任务描述。zero-shot 则是不提供示例,只是在测试时提供任务相关的具体描述。作者对这 3 种学习方式分别进行了实验,实验结果表明,三个学习方式的效果都会随着模型容量的上升而上升,且 few shot > one shot > zero show。
从理论上讲 GPT-3 也是支持 fine-tuning 的,但是 fine-tuning 需要利用海量的标注数据进行训练才能获得比较好的效果,但是这样也会造成对其它未训练过的任务上表现差,所以 GPT-3 并没有尝试 fine-tuning。
GPT-3 共训练了 5 个不同的语料,分别是低质量的 Common Crawl,高质量的 WebText2,Books1,Books2 和 Wikipedia,GPT-3 根据数据集的不同的质量赋予了不同的权值,权值越高的在训练的时候越容易抽样到。
论文名称:Training language models to follow instructions with human feedback
模型结构:
图 9: Instruct GPT 框架。
由于 GPT 3.0 会生成一些不真实、带攻击歧视的答案。GPT 团队主要认为问题源于数据不行。因此, 第一步:他们雇佣了一个 40 个人的团队,针对 prompt,写一些高质量的答案来 finetune GPT 3.0 第二步:采集来自不同模型的答案,同样由标注团队来选择他们的顺序。以此来训练一个 reward model 第三步:GPT 3.0 与 reward model 采取强化学习的策略迭代优化。
最终,Instruct GPT 相对于 GPT 3 生成的答案更为真实、准确。
T5 全称是 Text-to-Text Transfer Transformer,是一种模型架构或者说是一种解决 NLP 任务的一种范式。
如下图,就是把所有任务,如分类、相似度计算、文本生成都用一个 Text-to-text(文本到文本)的框架里进行解决。
举个例子,现在要做一个英文转德文的机器翻译任务,输入"translate English to German: That is good.",目标输出是"Das ist gut.",再举个例子,现在做一个文本三分类任务,label 为【entailment、contradiction、neutral】,输入"mnli premise: I hate pigeons. hypothesis: My feelings towards pigeons are filled with animosity.",目标输出是"entailment",注意哦,都是用"生成"的方式得到输出类(可以理解成 sequence to sequence)。
这样的好处是可以把所有的问题都套进去一个统一的范式,从而可以采用同样的模型架构、同样的训练策略、同样的损失函数、同样的解码手段。
T5 模型采用 Transformer 的 encoder-decoder 结构,之前介绍过 GPT 采用的是 Transformer 的 decoder 结构。
如图所示,T5(Text-to-Text Transfer Transformer) 模型将翻译、分类、回归、摘要生成等任务都统一转成 Text-to-Text 任务,从而使得这些任务在训练 (pre-train 和 fine-tune) 时能够使用相同的目标函数,在测试时也能使用相同的解码过程。注意这里回归任务对应的浮点数会被转成字符串看待,从而可以 token by token 的预测出来。虽然感觉奇怪,but anyway, it works。
T5 模型和原始的 Transformer 结构基本一致,除了做了如下几点改动: remove the Layer Norm bias place the Layer Normalization outside the residual path use a different position embedding
在下游任务上 fine-tune,通过对各种对比实验的结果进行分析,作者最终确定了训练 T5 模型的较优方案,其中以下几点值得注意: 无监督训练目标:采用 span-corruption 目标,类似 SpanBERT 的做法。 预训练策略:采用 multi-task 预训练方式 (即无监督任务和有监督任务一起预训练),在对比迁移方法一小节中我们发现 Multi-task pretraining + fine-tuning 的效果和 Unsupervised pre-training + fine-tuning 的效果差不多,但是前者在预训练过程还能够监控下游任务的性能,因此作者最后采用 Multi-task pre-training。
Transformer architecture variants
如图所示为 Transformer 结构的几种变种,主要区别在于模型中 self-attention 机制的可见范围。 fully-visible attention mask:输出序列的每个元素可以看见输入序列的每个元素。 causal attention mask:输出序列的每个元素只能看对应位置及之前的输入序列的元素,无法看见未来的元素。 causal with prefix attention mask:输入序列的一部分前缀采用 fully-visible attention mask,其余部分采用 causal attention mask。
在最左侧的 Encoder-Decoder 结构中,Encoder 部分采用 fully-visible attention mask,而 Decoder 部分采用 causal attention mask。 中间的 Language model 结构中,采用 causal attention mask。 最右侧的 Prefix LM 结构中,采用 causal with prefix attention mask。比如在翻译任务中,给定训练样本 translate English to German: That is good. target: Das ist gut.,我们对 translate English to German: That is good. target:采用 fully-visible attention mask,对 Das ist gut.采用 causal attention mask。
前面章节已经详细介绍了 Transformer 的结构,包括了 ENcode 和 Decode 两大部分。同时也介绍了为了加快 training 速度 transformer 模型增加了 OutputEmbedding 输入头+attentionMask 来实现前向学习能力。所以可以看到 transfomer 可以作为一个很好的自监督、无监督的知识萃取器。实际上后续的更重大模型也就是如此操作。首先用 transfomer 架构批量的学习现有语料的得到一个强大的表现力极强的底座模型,你可以把这个阶段理解为一个人还没有出来做事前的博览群书增能力过程。然后又了一个底座模型后,用这个强大的底座模型在下游任务上做简单结构改造做 finetune,你可以把这个过程看成是就事锻炼增强自己实际解决问题能力;要能够成功这是有前提的,就是你得有足够知识量(也就是 pretraning 底座模型要够强。至于后续出现的 prompt、自动 prompt、以及 instruct 和 cot,其实都是 fintune 之后的或者平级事情,也就是既然这个底座模型如此博览群书牛逼无敌,那是否可以直接拿来用(zeroshot)或者我简单给他几个例子(fewshot)就可以为我所用,尽量减少 finetune 阶段出了学校还要经过公司实习半年一年的实习时间也是浪费,直接为我所用就好了。为了达到这个目的,就出现了 prompt(在学校时候就让他按公司模式学习,或者出公司前有个懂学习校那套语言的人做工作 - 学校指令转换),这个既要懂学校又要懂公司的人太少了所以人工 prompt 太难了,有没办法有个自动翻译机器所以出现自动 prompt 阶段。最后一个就到了 instruct 阶段,这个底座这么牛逼翻译过来就是这个学校科研能力这么强,有没办法我们产学研结合:你学校负责研发就好负责底座牛逼就行,我们公司层面和你多交互给你时不时下一些指令 instruct,经过这么个上下游一体化有反馈的结合就可以让学校科研为我所用,听懂人话了。对于一些复杂的任务如果只在一次研发任务很难一次性搞透,有没办法对任务拆解逐步推进得到更好效果,这对应的技术的技术就是 Cot(思维链)其实个人觉得这部分更多是在训练数据上和工程链路上做文章,模型架构本身基本没变化。
更学术解释:Fine-tuning 是使用大规模预训练语言模型来进行下游任务的流行范式,但需要更新和存储语言模型的全部参数。再运用到下游任务时,需要对每一个任务都需要存储一份修改后的参数。Lightweight fine-tuning 是尝试解决上述问题的方法,Lightweight fine-tuning 固定住绝大部分的预训练参数,修改预训练模型的小部分模块。但是该方法最困难的地方是识别模块中高表现的部分以其需要调节的预训练参数。另一种比较比较流行的方法是 Prompting, Prompting 在模型的输入前加上 instructions 和一些样本使模型输出任务需要的结果。
这些模型也称为双向或自动编码,仅在预训练期间使用编码器,这通常是通过屏蔽输入句子中的单词并训练模型进行重构来完成的。在预训练的每个阶段,注意力层都可以访问所有输入词。该系列模型最适用于需要理解完整句子的任务,例如句子分类或提取式问答。
GPT:仅使用上文进行编码 GPT2:仍然仅使用上文进行编码(因为要处理生成任务)。但是模型更大,数据量更多。 GPT3:超大规模
对于不同的微调任务,我们需要对数据进行如下处理:
由于 GPT 仍然是一个生成式的语言模型,因此需要采用 Mask Multi-Head Attention 的方式来避免预测当前词的时候会看见之后的词,因此将其称为单向 Transformer,这也是首次将 Transformer 应用于预训练模型,预测的方式就是将 position-wise 的前向反馈网络的输出直接送入分类器进行预测
此外整个 GPT 的训练包括预训练和微调两个部分,或者说,对于具体的下游任务,其模型结构也必须采用与预训练相同的结构,区别仅在于数据需要进行不同的处理
对于带有标签 y 的监督数据 [x1,…,xm],我们直接将其输入到已经完成预训练的模型中,然后利用最后一个位置的输出对标签进行预测,即 $$y = f(h_m)$$ 其中,Wy 为分类器的参数,hm 为最后一层最后一个位置的输出。则最大化优化目标即为: $$L2(C)=\sum_{(x,y)^T} log P(y|x_1,...,x_m)$$
解码器模型,通常称为自回归,在通常设计为强制模型预测下一个单词的预训练期间仅使用解码器。注意层只能访问句子中给定单词之前的单词。它们最适合涉及文本生成的任务。
Bert:同时使用上下文进行编码,通过 mask 方式实现 Roberta:相比 bert 主要是在训练参数上做了调整:batch size,adam 参数,训练数据、nsp loss、epoch 数,词表大小。
BERT 的输入表征由三种 Embedding 求和而成: Token Embeddings:即传统的词向量层,每个输入样本的首字符需要设置为 [CLS],可以用于之后的分类任务,若有两个不同的句子,需要用 [SEP] 分隔,且最后一个字符需要用 [SEP] 表示终止 Segment Embeddings:为 [0,1][0,1] 序列,用来在 NSP 任务中区别两个句子,便于做句子关系判断任务 Position Embeddings:与 Transformer 中的位置向量不同,BERT 中的位置向量是直接训练出来的
对于不同的下游任务,我们仅需要对 BERT 不同位置的输出进行处理即可,或者直接将 BERT 不同位置的输出直接输入到下游模型当中。具体的如下所示: 对于情感分析等单句分类任务,可以直接输入单个句子(不需要 [SEP] 分隔双句),将 [CLS] 的输出直接输入到分类器进行分类 对于句子对任务(句子关系判断任务),需要用 [SEP] 分隔两个句子输入到模型中,然后同样仅须将 [CLS] 的输出送到分类器进行分类 对于问答任务,将问题与答案拼接输入到 BERT 模型中,然后将答案位置的输出向量进行二分类并在句子方向上进行 softmax(只需预测开始和结束位置即可) 对于命名实体识别任务,对每个位置的输出进行分类即可,如果将每个位置的输出作为特征输入到 CRF 将取得更好的效果。
BERT 的预训练任务 MLM 使得能够借助上下文对序列进行编码,但同时也使得其预训练过程与中的数据与微调的数据不匹配,难以适应生成式任务 另外,BERT 没有考虑预测 [MASK] 之间的相关性,是对语言模型联合概率的有偏估计 由于最大输入长度的限制,适合句子和段落级别的任务,不适用于文档级别的任务(如长文本分类); 适合处理自然语义理解类任务 (NLU),而不适合自然语言生成类任务 (NLG)
编码器 - 解码器模型,也称为序列到序列,使用了 Transformer 架构的两个部分。编码器的注意层可以访问输入中的所有单词,而解码器的注意力层只能访问输入中给定单词之前的单词。预训练可以使用编码器或解码器模型的目标来完成,但通常涉及一些更复杂的东西。这些模型最适合围绕根据给定输入生成新句子的任务,例如摘要、翻译或生成式问答。
T5。encoder 的 hidden 层输出用 avgpooling,而不是像 bert 一样用的 cls-token
在训练模型时,我们需要为模型定义一个任务来学习。上面已经提到了一些典型的任务,例如预测下一个词或学习重建掩码词。''包括一个相当全面的预训练任务分类,所有这些都可以被认为是自我监督的: Language Modeling (LM): 预测下一个标记(在单向 LM 的情况下)或前一个和下一个标记(在双向 LM 的情况下) Masked Language Modeling (MLM):从输入句子中屏蔽掉一些标记,然后训练模型通过其余标记预测被屏蔽的标记 Permuted Language Modeling (PLM): 与 LM 相同,但基于输入序列的随机排列。一个排列是从所有可能的排列中随机抽取的。然后选择一些标记作为目标,训练模型来预测这些目标。 Denoising Autoencoder (DAE):采用部分损坏的输入(例如,从输入中随机抽取标记并将其替换为 [MASK] 元素。从输入中随机删除标记,或以随机顺序打乱句子)并旨在恢复原始未失真的输入. Contrastive Learning (CTL):文本对的得分函数是通过假设一些观察到的文本对在语义上比随机采样的文本更相似来学习的。它包括:Deep InfoMax (DIM):最大化图像表示和图像局部区域之间的互信息;Replaced Token Detection(RTD):根据周围环境预测令牌是否被替换;Next Sentence Prediction (NSP):训练模型区分两个输入句子是否是来自训练语料的连续片段;和 Sentence Order Prediction(SOP):类似于 NSP,但使用两个连续的片段作为正例,使用相同的片段但顺序交换作为负例
对 Huggingface 中可用的 63 个 transformer 模型做了统计,按模型名称、属于哪类预训练模型架构(只用 transformer 的 decode、只用 transformer 的 encode、transformer)、预训练的任务、属于那个家族系(bert、bart、gpt、t5、transformer…)。
2021 年 4 月,华为云联合循环智能发布盘古 NLP 超大规模预训练语言模型,参数规模达 1000 亿;联合北京大学发布盘古α超大规模预训练模型,参数规模达 2000 亿。 阿里达摩院发布 270 亿参数的中文预训练语言模型 PLUG,联合清华大学发布参数规模达到 1000 亿的中文多模态预训练模型 M6。 2021 年 6 月,北京智源人工智能研究院发布了超大规模智能模型'悟道 2.0',参数达到 1.75 万亿,成为当时全球最大的预训练模型。 2021 年 7 月,百度推出 ERNIE 3.0 知识增强大模型,参数规模达到百亿。10 月,浪潮发布约 2500 亿的超大规模预训练模型'源 1.0'。 2021 年 12 月,百度推出 ERNIE 3.0 Titan 模型,参数规模达 2600 亿。而达摩院的 M6 模型参数达到 10 万亿,将大模型参数直接提升了一个量级。 到 2022 年,大模型继续火热。最开始,大模型是集中在计算语言领域,但如今也已逐渐拓展到视觉、决策,应用甚至覆盖蛋白质预测、航天等等重大科学问题,谷歌、Meta、百度等等大厂都有相应的成果。一时间,参数量低于 1 亿的 AI 模型已经没有声量。 清华 GLM 清华 CPM-BEE 梳理各模型大小、参数、训练数据
| 模型名称 | 所属机构 | 模型大小 | 模型架构 | 训练数据 |
|---|---|---|---|---|
| 中文 gpt-3 | 达摩院 | 30B | transformer decode | 数据来源于和 |
| T5 | 元语智能 | 3B | transformer | 在 1000 亿 token 中文语料上预训练,累计学习 1.5 万亿中文 token |
| PALM | 达摩院 | 1B | encode(bert),decode(transformer decode) | |
| PLUG | 达摩院 | 27B | 先的 encoder,然后训练层的 encoder-decode | 数据来源于和 |
| GPT-MoE 中文 | 阿里云 | 75B | Switch Transformer decode | 130 亿广告 |
| bloom 中文 | 澜舟科技 | 6.4B | 未公布 | |
| GLM | 清华 | 130B | 混合 including autoencoding models (e.g., BERT), autoregressive models (e.g., GPT), and encoder-decoder models (e.g., T5) | 4000 亿个文本标识符(中文和英文各 2000 亿)进行了训练 |
| CPM-ANT+ | OPENBMB 清华 | 10B | transformer encode(类似 bert) | 悟道数据集合优化处理 |
| CPM-BEE | OPENBMB 清华 | 10B | transformer encode(类似 bert) | 悟道数据集合优化处理 |
| ERNIE 3.0 Titan | 百度 | 260B | transformer encode(类似 bert)预训练+transformer decode(类似 gpt)任务端 | 未公布 |
| 源 1.0 | 浪潮 | 13B | ||
| 盘古α | 华为 | 200B | transformer decode architectures of PLMs besides GPT and BERT. | 从开源开放数据集、common crawl 数据集、电子书等收集近 80TB 原始语料,构建了约 1.1TB 的高质量中文语料数据集、53 种语种高质量单、双语数据集 2TB |
| 悟道 2.0 | 北京智源 | 1750B | transformer decode | 0 多种规则从 100TB 原始网页数据中清洗得出最终数据集,注重隐私数据信息的去除,源头上避免 GPT-3 存在的隐私泄露风险;包含教育、科技等 50+ 个行业数据标签 |
生成模型的目标是在给定了数据集 D,并且假设这个数据集的底层分布 (underlying distribution) 是 Pdata,我们希望够近似出这个数据分布。如果我们能够学习到一个好的生成模型,我们就能用这个生成模型为下游任务做 inference 推理。
下面我们简单回顾一下生成模型和判别模型,读者可以自行选择跳过这小节。
对于判别模型 (discriminative model),像逻辑回归模型,是对给定的数据点预测一个标签 label,但是对于生成模型,它学习的是整个数据的联合分布 (joint distribution)。当然判别模型也可以理解为是给定了输入数据后,标签 label 的生成模型。但一般生成模型指的是高维数据。
那么如果我们的建模方式不一样,对应的模型也是不一样的。假设我们希望求 p(Y|X),对于左边的模型,我们需要用贝叶斯规则计算 p(Y) 和 p(X|Y)。而对于右边的模型,它已经可以直接用来计算 p(Y|X),因为 p(X) 是给定的,所以我们可以不用给它建模。
我们将随机变量在图上显示出来:
对于生成模型的联合分布:p(Y,X)=p(Y)p(X1|Y)p(X2|Y,X1)…p(Xn|Y,X1,…,Xn−1)我们需要考虑的是怎么确定 p(Xi|(X)pa(i),Y)的参数,这里的 pa(i)指的是指向随机变量 Xi 的随机变量集合。
对于判别式模型的联合分布:p(Y,X)=p(X1)p(X2|X1)p(X3|X1,X2)…p(Y|X1,…,Xn)
朴素贝叶斯 (Naive Bayes),它是生成模型的一个特例,它假设在给定了 label 之后,各个随机变量之间是独立的,这就是它 naive 的原因吧,如下图:
用训练数据估计参数,用贝叶斯规则做预测:
逻辑回归并不要求随机变量之间独立。
但是生成模型依然很有用,根据链式法则: p(Y,X)=p(X|Y)p(Y)=p(Y|X)p(X)
假设 X 的部分随机变量是可观测的,我们还是要计算 p(Y|Xevidence),那么我们就可以对这些看不到的随机变量 marginalize(积分求和)。
生成模型的学习 (learning) 是指在给定一个数据分布 pdata 和一个模型家族 M(model family) 的情况下,我们要从这个模型家族中找到一个近似分布 pθ,使得它和数据分布尽可能的近。
但是要怎么衡量这个近呢?我们用距离来衡量,写成数学表达式: minθ∈M d(pdata,pθ)
因此,我们自然而然会对三个问题感兴趣: 模型家族 M 的表达式是神马? 目标函数 d(⋅) 是什么样子的? 最小化 d(⋅) 的优化过程是什么?
一个生成模型应该是一个联合概率分布 p(x),假设这个概率分布是从一堆狗狗的图片上学习到的,那么这个概率分布应该可以: 生成 (Generation), 即采样 (sampling) xnew∼p(x),并且采样 xnew 图片应该很像狗狗。 密度估计 (Density estimation),如果给了一张狗狗的图片 x,那么这个概率分布 p(x) 的值应该很高。或者给了一张不相关的图片,p(x) 的值很低,这可以用来做异常检测。 无监督表示学习 (unsupervised representation learning), 我们可以学习到这些图片的一些公共信息,像是一些特征,耳朵,尾巴…
但我们也发现量化评估上面的任务 1 和任务 3 其实是很难的;其次,并不是所有的模型家族在这些任务上都推理速度都快且精准,也正是因为推理过程的折中和权衡,导致了各种不同的方法。
数学建模为随机分布,忽略了输入数据分布逐步转化到最后生成图片的随机过程,只关心输入数据分布到生成数据分布的转化分布。 自回归模型 变分自编码器 正则化流模型 生成对抗网络
数学建模为随机过程,输入数据分布经历了一个过程逐步转化成最终生成图片,对转化的随机过程也考虑进去,用 T 步随机噪声过程来模拟(扩散模型家族)。 1.DDIM 2.SDE 3.stable diffusion
深度隐变量模型 (Deep Latent Variable Models) 通常长什么样子呢?
假设隐变量 z 服从正态分布 z∼N(0,I) p(x|z)=N(μθ(z),Σθ(z)) 其中 μθ和 Σθ都是神经网络
我们希望在训练完之后,z 可以有一定的意义,能表示有意义的特征,这些特征可以用在无监督学习里面。
特征的计算可以通过 p(z|x)
![...]
当且仅当 q=p(z|x;θ) 的时候,等号成立。证明略,读者可以带入得到,也可以参考 VI 这节的证明。
因此,我们需要找到一个分布 q(z) 它必须和 p(z|x;θ)尽可能相近的时候,才能让等式的右边的证据下界和左边的 likelihood 越接近。另外左边的 likelihood,也被称之为 evidence。
熟悉 KL divergence 的读者应该已经发现,当我们把右边的式子移到左边,其实就是分布 q(z) 和 分布 p(z|x;θ) 的 KL 散度: DKL(q(z)||p(z|x;θ))=logp(x;θ)−∑zq(z)logp(x,z;θ)−H(q)≥0
当然,q=p(z|x;θ) 的时候,KL 散度为 0。
这个时候我们就将问题转到了如何找到这样的分布 q(z)?
在开始学习 VAE 的参数之前,我们看看这里有两组参数需要我们优化,一组是对应于分布 q 的参数 ϕ, 另一组是对应分布 p 的参数 θ,见下面的式子:
我们再来看一张学习 VAE,经常会看到的图,很好的解释了 ELBO,KL 散度和 likelihood 之间的关系:
注意上面图片中的 q 是 conditioned 在 x 上的,暂时可以理解为给了我们一些 x 的信息后,我们的分布 q 才能更容易接近 p(z|x;θ),或者理解为 q 的空间可以限定的小一些,这样更容易采样到有意义的样本。
从上面的推导中,我们可以看到最大化 likelihood 这个问题被转换为最大化证据下界 ELBO。接下来,我们来看一下要怎么联合优化参数 ϕ 和 θ来最大化 ELBO。
根据前面的论述,我们知道我们的目标是要让证据下界尽可能的大,这里再贴一下 ELBO:L
从这个式子中,我们可以看到如果我们想求 ∇θL(x;θ,ϕ)和 ∇ϕL(x;θ,ϕ)。如果这个期望函数有对应的 closed-form 解自然是很好的,比如我们在 VI-变分推理里举的那个例子,但是如果没有,对于前者我们求起来很方便,假设 q(z;ϕ) 是容易采样的,我们可以用采样的方法:
但是要怎么求 ∇ϕL(x;θ,ϕ) 呢?我们的期望是依赖 ϕ 的,这个求解过程瞬间就变复杂了,当然其实也是可以用蒙特卡洛采样的方法来求解的,这里先不讲了。我们来看看另一个方法先。
我们将上面的式子简化一下:𝔼q(z;ϕ)[r(z)]=∫q(z;ϕ)r(z)dz
把前面 ELBO 方括号中的东西全部用 r(⋅) 来表示。另外需要注意其中 z 是连续随机变量。接下来假设分布 q(z;ϕ)=N(μ,σ²I), 参数为 ϕ=(μ,σ)。那么下面两种方法的采样是相同的: 采样 z∼qϕ(z) 采样 ϵ∼N(0,I),z=μ+σϵ=g(ϵ;ϕ)
我们把第二种方法带入前面的式子:𝔼z∼q(z;ϕ)[r(z)]=𝔼ϵ∼N(0,I)[r(g(ϵ;ϕ))]=∫p(ϵ)r(μ+σϵ)dϵ
那我们再来对 ϕ 求梯度: ∇ϕ𝔼q(z;ϕ)[r(z)]=∇ϕ𝔼ϵ[r(g(ϵ;ϕ))]=𝔼ϵ[∇ϕr(g(ϵ;ϕ))]
这样我们就成功把求梯度的符号挪到了方括号里面去了,期望也不再依赖 ϕ 了。只要 r 和 g 对于 ϕ 是可导的,我们就很容易用蒙特卡洛采样来计算 ϕ 梯度了。
𝔼ϵ[∇ϕr(g(ϵ;ϕ))]≈1/k∑k∇ϕr(g(ϵk;ϕ))whereϵ1,…,ϵk∼N(0,I)
我们在后面也会看到,这个 trick 使得我们的神经网络可以反向传导。
回到我们最初的 ELBO 中,我们可以看大我们的 r(z,ϕ) 和这里的 r(z) 还有些差别,但是类似的,我们还是可以用 reparameterization 的方法将其转换为:
𝔼ϵ[∇ϕr(g(ϵ;ϕ)),ϕ]≈1/k∑k∇ϕr(g(ϵk;ϕ),ϕ)whereϵ1,…,ϵk∼N(0,I)
假设我们的数据样本集合为 D,那么我们可以把我们的 likelihood 和 ELBO 表达成:
值得注意的是,我们这里每个 xi 都有一个参数 ϕi与之对应,还记得我们的参数 ϕ是分布 q(z;ϕ) 的参数,而这个分布是为了近似地拟合真实分布 p(z|xi;θ),从这个真实分布中,我们都可以看到这个后验分布对于不同的数据点 xi 都是不同的,因此对于不同的数据点,这个近似分布对于每个数据点的分布也应该是不同的,于是我们用不同的 ϕi 来表示。
但是这样一来,如果数据集很大,参数量就炸了呀。于是我们用一个参数化函数 fλ把每个 x 映射到一组变分参数上:xi→ϕi,*。通常我们把 q(z;fλ(xi)) 写作 qϕ(z|x)。
Amortized inference: 也就是要学习怎么通过 q(z;fλ(xi))),把 xi 映射到一组好的参数上 ϕi。
于是我们的 ELBO 就变为:L
那我们的整个计算流程就是:
有了上面的式子之后,我们可以进一步的把上面的式子进行转换:
那这个式子就很有趣了。我们引入了 z 的先验,我们可以把它理解为: 首先,从数据集中拿出一个数据点 xi 用 qϕ(z|xi)(encoder) 采样 ẑ 用 p(x|ẑ ;θ) (decoder) 采样得到重建的 x̂
我们的训练目标 L(x;θ,ϕ),其中第一项是希望 x̂ ≈xi^,即 xi 要在 p(x|ẑ ;θ)下概率尽可能的大。第二项,是为了让 ẑ 尽可能的贴合先验 p(z)换句话说假设我们的先验是已知的,那么我们可以用它来代替 qϕ 直接进行采样ẑ ∼p(z),再用解码器生成分布 p(x|ẑ ;θ),进行采样得到样本。
一个最优的生成模型应该能够生成最好的样本质量 (sample quality) 和最高的测试集的 log-likelihood。但是对于不完美的模型,取得高的 log-likelihood 也许并不意味着好的样本质量,反之亦然。
给定了 S1=x∼P,S=x∼Q, 一个 two-sample test 指的是下面的两个假设 (hypothesis): Null hypothesis H0:P=Q Alternate hypothesis: H1:P≠Q
我们用统计指标 T 来比较 S1 和 S2,比如,T 可以是两个分布均值的不同或者方差的不同。如果 T 小于一个阈值 α,那么接受 H0 否则就拒绝。我们可以看到测试统计指标 T 是 likelihood-free 的 (likelihood-free),因为它并不直接考虑计算概率密度 P 或者 Q,它只比较了两个分布的样本集合 S1 和 S2。
先前我们假设可以直接获取到数据集 S1=D=x∼Pdata。另外,我们有模型分布 pθ,假设模型分布允许高效率采样,S2=x∼pθ。Alternate notion of distance between distributions (改变两个分布之间的距离定义): 训练一个生成模型从而最小化 S1 和 S2 之间的 two-sample test 目标。
到这里,我们可以发现,我们把从直接在概率密度分布上建模转换到了用一个考虑两个分布上的样本的统计指标来衡量两个分布是否相同。因此找到一个好的统计指标是至关重要的,它决定了我们是否能生成出高质量的样本。
在高维空间中找到这样的统计指标还是很难的,比如说两个高斯分布的方差相同,均值不同,那么它们的分布也是完全不同的。那在高维空间中更是如此,稍微某个维度有一些差异,那对应的分布可能就差很大。那么我们是不是能学习一个统计指标能找到这些差异,它最大化两个样本集 S1 和 S2 之间的距离定义呢?
到这里,我们就有了两个目标,一个目标是想最小化 S1 和 S2 之间的距离,使得 S2 的样本尽量逼近 S1;另一个目标是最大化 S1 和 S2 样本之间的距离,找到那些会让两个分布很不相同的差异。
生成对抗网络是两个玩家之间的零和游戏,这两个玩家是 生成器 和 判别器。
生成器 (generator) 是一个有向的隐变量模型,隐变量 z 和生成样本 x 之间的映射为 Gθ。它的目标为最小化 two-sample test。
判别器 (discriminator) 是一个函数比如神经网络,用来区分来自数据集的'真实'数据,和来自模型生成器的'伪造'数据。它的目标为最大化 two-sample test。
判别器的训练目标为:
上面的式子可以理解为,对于一个固定的生成器,判别器就是做了一个二分类,交叉熵为目标,当 x∼pdata,即样本来自真实数据集,那么概率赋值为 1,当 x∼pG,即样本来自生成器,那么概率赋值为 0.
最优的判别器有:
证明:对于给定的生成器 G,判别器 D 的训练目标就是要最大化 V(G,D)
对于任意的 (a,b)∈R2,(a,b)≠0,0(,函数 y→alog(y)+blog(1−y),在区间 [0,1][0,1] 上的取得最大值的地方在 aa+b。因此,令 y=D(x),即可得证。
对于生成器,我们的训练目标为:
也就是说我们希望能找到一个生成器,使得判别器的目标不能实现 (最小),因此也就产生了对抗。
这时,将最优判别器 D∗G(⋅) 带入,我们有: DJSD 表示为 2xJensen-Shannon Divergence,也叫做对称 KL 散度,写作:
我们来总结一下 GAN 的训练步骤: 从真实数据 D 中采样 m 个训练样本 x(1),x(2),…,x(m) 从噪声分布 pz 中采样 m 个噪声向量 z(1),z(2),…,z(m) 用随机梯度下降更新生成器参数 θ 因为我们这里是更新生成器,所以不关注 V 的第一项 (从真实数据采样)。 用随机梯度上升更新判别器参数 ϕ 以上步骤重复固定个 epochs
上面的训练过程是先训练生成器然后训练判别器,这个顺序也可以反过来。
GAN 的训练是比较困难的,许多论文提出了很多的 tricks,主要的困难有以下几个方面: 不稳定的优化过程 Unstable optimization Mode collapse 评估 Evaluation
理论上来说,在每个 step,判别器如果达到了最优,生成器在函数空间中更新,那么生成器是能够收敛到数据分布的。但是实际训练过程中,生成器和判别的损失函数值总是震荡着的。也不同于极大似然估计 MLE,应该什么时候停止训练也很难判断。
Mode Collapse 主要指的是 GAN 的生成器总是只生成固定的一个或几个样本,类似于 modes(众数)。事实上,笔者发现其它的一些生成模型也会有这个问题。
课件中给的例子是这样的:假设我们的真实分布是混合高斯 (mixture of Gaussians)
但是在生成过程中,我们的生成器分布总是在各个中心点之间跳来跳去。
对于众数问题,可以通过改变架构,增加正则项,引入噪音干扰等方法。这里有个,给了许多 GAN 的训练建议和 tricks。
自回归模型虽然似然 likelihood 很容易计算但是没有直接的办法来学习特征。变分自编码器虽然可以学习特征的表示 (隐变量 z 但是它的边缘似然函数很难求,所以我们取而代之用了最大化证据下界 ELBO 来近似求解。
我们有没有办法设计一个含有隐变量的模型并且它的似然函数也容易求呢?的确是有的。我们对这样的模型还有些其他要求,除了概率密度是可以计算的,还需要容易采样。许多简单的分布像是高斯和均匀分布都是满足这个要求的。但是实际的数据分布是很复杂的。那有什么办法可以用这些容易采样又容易计算概率密度的分布映射到复杂分布呢?有的,用换元法 (change of variables)
在后面的部分中,读者可以记住 z 来自于一个容易采样的分布,x 来自于实际的数据的复杂分布。
在介绍 Flow 模型前,我们先来看一些基本概念。
相信大家对这个概念都不陌生。如果读者对这个概念不熟悉,可以参考这个。
先来贴一下一维的公式:如果 X=f(Z),f(⋅) 是单调的并且它的逆函数 Z=f−1(X)=h(X),那么:
举个例子,假设 Z=1/4X 并且 Z∼U[0,2], 那么 pX(4) 为多少?带入公式 h(X)=X/4h,所以 pX(4)=pZ(1)h′(4)=1/2×1/4=1/8。
更一般的,假设 Z 是一个均匀随机向量 [0,1]^n,X=AZ,其中 A 是一个可逆的方阵,它的逆矩阵为 W=A−1,那么 X 是怎么分布的?
矩阵 A 是把一个单位超立方体 [0,1]^n 变成了一个超平行体。超立方体和超平行四边形是正方形和平行四边形的更一般式。下图中,我们看到这里的变换矩阵
把单位正方形变成了一个任意的平行四边形。
超平行四边形体的体积 (volume) 等于转换矩阵 A 的行列式的值。
因为 X 是均匀分布在超四边形上的,因此:
标准化流模型 Normalizing Flow Models 在一个标准流模型 (normalizing flow model),假设 Z 和 X 分别为隐变量和观测变量,并且他们之间的映射为函数 f:ℝn↦ℝn,并且它是可逆的,那么 X=f(Z),Z=f−1(X):
那么利用换元法,可以得到观测变量的似然值为:
这里的 f 转换函数我们加上了参数下标 θ,也就是我们的神经网络学习的参数。从这个公式中,我们可以看到,假设我们的隐变量来自一个简单分布,我们经过转换 fθ,可以把它隐射到复杂分布 pX 上,并且当我们想要计算观测变量的概率值的时候只需要把这个过程逆转过来,用 f−1 对 x 进行转换并代入到简单分布 pZ 中,乘以相应的行列式的值即可。当然,读者应该会有疑惑,如何保证这个转换是可逆的呢?这个我们后面再提。
这里插一张笔者从李宏毅老师的课件里摘来的图:
这里的 generator,也就是我们的 fθ,这里的正太分布 π(z) 即为简单分布 pZ。
标准化-Normalizing 换元法使得我们用一个可逆变换得到了标准化的概率密度。那么 流-Flow 的意思指的是一系列的可逆变换互相组合在一起:
首先 z0 来自于一个简单分布,例如,高斯分布 用 M 个可逆转换后得到了 x,即:x≜zM
通过换元,我们可以得到 (乘积的行列式等于行列式的乘积):
Planar flow 指的 flow 模型是它的可逆转换为:
具体的代码,可以参考这个,但是这里的代码中的分布是由 close-form 解的,loss 其实用的是 KL divergence,和我们所希望学习的任意的实际数据分布 (没有 close-formed) 是不一样的,之所以不能学习任意分布是因为论文中也没给出计算 Likelihoode 所需要的 f−1,论文参考。后面我们会写要怎么设计这个转换。
下图是 Planar flow 通过多次转换后把简单的高斯分布变成了复杂的一个分布:
我们把上面的 likelhood,加上 log,得到在数据集 D 上的极大似然为:
当我们训练完模型,想要采样的时候,可以通过前向转换 z↦x z∼pZ(z)x=fθ(z)
学习隐变量的表示可以通过逆转换得到,比如我们有一张图片,我们可以得到它的特征: z=f−1θ(x)
因为计算 Jacobian 的行列式值复杂度很高 O(n³),但是我们知道上三角/下三角矩阵的行列式的值为对角线元素相乘 O(n),所以复杂度一下子就降下来了。所以我们可以按照这个思路来设计我们的可逆转换函数。
NICE(Non-linear Independent Components Estimation),源自。它包含了两种类型的层,additive coupling layers 和 rescaling layers。
首先我们把隐变量 z 分成两个部分 z1:d 和 zd+1:n,1≤d<n1。
那么我们的前向映射:z↦x x1:d=z1:d (直接拷贝过去) xd+1:n=zd+1:n+mθ(z1:d),其中 mθ(⋅) 是一个神经网络,参数为 θ,输入为维度为 d,输出维度为 n−d。
逆映射:x↦z
因此,det(J)=1det(下三角矩阵,且对角线为 1),当行列式的值为 1 的时候,我们称这种变换为 Volume preserving transformation。
这些 coupling layers 可以任意叠加下去,所以是 additive 的。并且每一层的隐变量分隔 partition 可以是不一样的 (d 的取值每一层都可以不同)。
NICE 的最后一层用了一个 rescaling transformation。同样地, 前向映射:z↦x:xi=sizi 其中,si 是第 i 维的 scaling factor.
逆映射:x↦z:zi=xisi
前向映射的 Jacobian 矩阵为:
好了,我们的每种类型的曾都有前向映射和逆映射了,就可以开开心心的训练我们的神经网络了。
Real-NVP(Non-volume presreving extention of NICE) 是 NICE 模型的一个拓展,可以参考这篇。
如下图所示:
我们可以发现 z1:d 基本都是直接复制下去了,其实也可以让 z1:d 和 zd+1:n 反一下,如下图 (叠加了 3 个 coupling layers):
这一节中,我们尝试理解如何把自回归模型看成是流模型,接着我们介绍两种模型 Masked Autoregressive Flow (MAF) 和 Inverse Autoregressive Flow (IAF) 模型。
假设我们的自回归模型:
我们的 MAF(Masked Autoregressive Flow) 的前向映射:
那么它的前向映射:z↦x 让 x1=exp(α1)z1+μ1。并且计算 μ2(x1),α2(x1) 让 x2=exp(α2)z2+μ2。并且计算 μ3(x1,x2),α3(x1,x2)
采样依然是序列化的并且慢 (和自回归一样),需要 O(n) 的时间。
逆映射如下图:
逆映射:x↦z 所有的 μi 和 αi 都可以并行计算,因为 zi 互相没有依赖关系。比如我们可以用自回归文章中介绍的 MADE 模型来做。 z1=(x1−μ1)/exp(α1) z2=(x2−μ2)/exp(α2)
Jacobian 矩阵是下三角,因此行列式的值计算起来也很快。似然值评估 (likelihood estimation) 起来也很简单方便,并且是并行的。(因为 zi 可以并行计算)
前面的 MAF,我们发现采样是很慢的但是计算 likelihood 很快,而在 IAF(Inverse Autoregressive Flow) 中这种情况反了过来。同样地,我们分别来看看前向映射,逆映射和 Jacobian 值。
前向映射:z↦x (并行): 对 i=1,…,n 采样 zi∼N(0,1) 并行计算好所有的 μi 和 αi x1=exp(α1)z1+μ1 x2=exp(α2)z2+μ2
逆映射:x↦z (序列化计算): 让 z1=(x1−μ1)/exp(α1), 根据 z1 计算 μ2(z1), α2(z1) 让 z2=(x2−μ2)/exp(α2), 根据 z1,z2 计算 μ3(z1,z2),α3(z1,z2)
从上面的映射中可以看到采样很方便,计算数据点的 likelihood 很慢 (训练)。
注意,IAF 对给定的数据点 x 计算 likelihood 慢,但是评估计算它自己生成的点的概率是很快的。
如下图,我们发现在 MAF 的逆转换中,把 x 和 z 交换一下,就变成了 IAF 的前向转换。类似地。MAF 的前向转换是 IAF 的逆转换。
MAF 的 likelihood 计算很快,采样很慢。IAF 的采样很快,likelihood 计算很慢。所以 MAF 更适合基于极大似然估计 (MLE) 的训练过程以及概率密度计算,IAF 更适合实施的生成过程。那我们可以同时利用两个模型的优点吗?
接下来就看一下我们怎么同时利用 MAF 和 IAF 的特点,训练一个采样和训练都很快的模型。
Parallel Wavenet 分两个部分,一个老师模型,一个学生模型。其中老师模型是 MAF,用来做训练计算极大似然估计 MLE。一旦老师模型训练好了之后,我们再来训练学生模型。学生模型用 IAF,虽然学生模型不能快速计算给定的外部数据点的概率密度,但是它可以很快地采样,并且它也可以直接计算它的隐变量的概率密度。
概率密度蒸馏 (Probability density distillation): 学生模型的训练目标是最小化学生分布 s 和老师分布 t 之间的 KL 散度。 DKL(s,t)=Ex∼s[log s(x)−log t(x)]
计算流程为: 用学生模型 IAF 抽取样本 x (详见 IAF 前向转换) 获得学生模型的概率密度 (这里的概率密度直接用上一步中的 z 和相应的 Jacobian 矩阵的值 exp(∑ni=1αi) 得到) 用老师模型 MAF 计算,根据学生模型采样得到的 x 样本的概率密度。 计算 KL 散度
整体的训练过程就是: 用极大似然训练老师模型 MAF 用最小化和老师模型的分布的 KL 散度训练学生模型 IAF
测试过程:用学生模型直接测试,生成样本
这个流程的效率比原来的 Wavenet-自回归模型快了近 1000 倍!
Parallel Wavenet 我们来看一下这个模型的图:
首先呢,我们的老师模型 MAF 已经是训练好了,注意在训练过程中会加上一些语言学信息特征 linguistic features(通常大家用的都是 spectrogram 频谱,笔者不是很确定直接加像是文字特征行不行)。接着呢,我们让学生模型 IAF 进行采样,采样得到的样本 (紫色的圈圈) 放到老师模型中去评估概率密度。
我们还是回到 Glow 模型来看看另一个例子,
这里的 WN 模块指的是类似于 WaveNet 中的结构,实际上它可以是任意的神经网络结构,作者用了膨胀卷积 (dilated convolutions) 和 gated-tanh,invertible 1x1 convolution 可以参考这篇。稍微注意一下,我们前面都是从 z↦x,现在这张图我们是直接看的逆转换 x↦z(毕竟在训练的时候,我们就是在用 z 计算 likelihood)。
代码可以参考。
对于扩散模型技术正在蓬勃发展期,网上的资料也很多很全面,各种综述文章也不少。我就不在做更多阐述,会把一些我觉得整理不错的技术帖综合放进文章,减少大家文章检索的工作保持阅读流畅。
其实 diffusion 模型和上面一些模型的主要差异在于对生成过程的建模。相当于是通过增加了一个随机生成过程的仿真让生成内容质量可控。
扩散模型(diffusion models)是深度生成模型中新的 SOTA。扩散模型在图片生成任务中超越了原 SOTA:GAN,并且在诸多应用领域都有出色的表现,如计算机视觉,NLP、波形信号处理、多模态建模、分子图建模、时间序列建模、对抗性净化等。此外,扩散模型与其他研究领域有着密切的联系,如稳健学习、表示学习、强化学习。然而,原始的扩散模型也有缺点,它的采样速度慢,通常需要数千个评估步骤才能抽取一个样本;它的最大似然估计无法和基于似然的模型相比;它泛化到各种数据类型的能力较差。如今很多研究已经从实际应用的角度解决上述限制做出了许多努力,或从理论角度对模型能力进行了分析。
然而,现在缺乏对扩散模型从算法到应用的最新进展的系统回顾。为了反映这一快速发展领域的进展,我们对扩散模型进行了首个全面综述。我们设想我们的工作将阐明扩散模型的设计考虑和先进方法,展示其在不同领域的应用,并指出未来的研究方向。此综述的概要如下图所示:
尽管 diffusion model 在各类任务中都有着优秀的表现,它仍还有自己的缺点,并有诸多研究对 diffusion model 进行了改善。为了系统地阐明 diffusion model 的研究进展,我们总结了原始扩散模型的三个主要缺点,采样速度慢,最大化似然差、数据泛化能力弱,并提出将的 diffusion models 改进研究分为对应的三类:采样速度提升、最大似然增强和数据泛化增强。我们首先说明改善的动机,再根据方法的特性将每个改进方向的研究进一步细化分类,从而清楚的展现方法之间的联系与区别。在此我们仅选取部分重要方法为例,我们的工作中对每类方法都做了详细的介绍,内容如图所示:
在分析完三类扩散模型后,我们将介绍其他的五种生成模型 GAN,VAE,Autoregressive model, Normalizing flow, Energy-based model。考虑到扩散模型的优良性质,研究者们已经根据其特性将 diffusion model 与其他生成模型结合,所以为了进一步展现 diffusion model 的特点和改进工作,我们详细地介绍了 diffusion model 和其他生成模型的结合的工作并阐明了在原始生成模型上的改进之处。Diffusion model 在诸多领域都有着优异的表现,并且考虑到不同领域的应用中 diffusion model 产生了不同的变形,我们系统地介绍了 diffusion model 的应用研究,其中包含如下领域:计算机视觉,NLP、波形信号处理、多模态建模、分子图建模、时间序列建模、对抗性净化。对于每个任务,我们定义了该任务并介绍利用扩散模型处理任务的工作,我们将本项工作的主要贡献总结如下: l 新的分类方法:我们对扩散模型和其应用提出了一种新的、系统的分类法。具体的我们将模型分为三类:采样速度提升、最大似然提升、数据泛化提升。进一步地,我们将扩散模型的应用分为七类:计算机视觉,NLP、波形信号处理、多模态建模、分子图建模、时间序列建模、对抗性净化。 l 全面的回顾:我们首次全面地概述了现代扩散模型及其应用。我们展示了每种扩散模型的主要改进,和原始模型进行了必要的比较,并总结了相应的论文。对于扩散模型的每种类型的应用,我们展示了扩散模型要解决的主要问题,并说明它们如何解决这些问题 l 未来研究方向:我们对未来研究提出了开放型问题,并对扩散模型在算法和应用方面的未来发展提供了一些建议。
二.扩散模型基础 生成式建模的一个核心问题是模型的灵活性和可计算性之间的权衡。扩散模型的基本思想是正向扩散过程来系统地扰动数据中的分布,然后通过学习反向扩散过程恢复数据的分布,这样就了产生一个高度灵活且易于计算的生成模型。
A. Denoising Diffusion Probabilistic Models(DDPM) 一个 DDPM 由两个参数化马尔可夫链组成,并使用变分推断以在有限时间后生成与原始数据分布一致的样本。前向链的作用是扰动数据,它根据预先设计的噪声进度向数据逐渐加入高斯噪声,直到数据的分布趋于先验分布,即标准高斯分布。反向链从给定的先验开始并使用参数化的高斯转换核,学习逐步恢复原数据分布。用 x0 ~q(x0) 表示原始数据及其分布,则前向链的分布是可由下式表达:
这说明前向链是马尔可夫过程,xt 是加入 t 步噪音后的样本,βt 是事先给定的控制噪声进度的参数。当 ∏t1−βt趋于 1 时,xT 可以近似认为服从标准高斯分布。当 βt 很小时,逆向过程的转移核可以近似认为也是高斯的:
我们可以将变分下界作为损失函数进行学习:
B. Score-Based Generative Models(SGM) 上述 DDPM 可以视作 SGM 的离散形式。SGM 构造一个随机微分方程(SDE)来平滑的扰乱数据分布,将原始数据分布转化到已知的先验分布:
和一个相应的逆向 SDE,来将先验分布变换回原始数据分布:
因此,要逆转扩散过程并生成数据,我们需要的唯一信息就是在每个时间点的分数函数。利用 score-matching 的技巧我们可以通过如下损失函数来学习分数函数:
对两种方法的进一步介绍和两者关系的介绍请参见我们的文章。
原始扩散模型的三个主要缺点,采样速度慢,最大化似然差、数据泛化能力弱。最近许多研究都在解决这些缺点,因此我们将改进的扩散模型分为三类:采样速度提升、最大似然增强和数据泛化增强。在接下来的三、四、五节我们将对这三类模型进行详细的介绍。
三.采样加速方法 在应用时,为了让新样本的质量达到最佳,扩散模型往往需要进行成千上万步计算来获取一个新样本。这限制了 diffusion model 的实际应用价值,因为在实际应用时,我们往往需要产生大量的新样本,来为下一步处理提供材料。研究者们在提高 diffusion model 采样速度上进行了大量的研究。我们对这些研究进行了详细的阐述。我们将其细化分类为三种方法:Discretization Optimization,Non-Markovian Process,Partial Sampling。
A. Discretization Optimization 方法优化求解 diffusion SDE 的方法。因为现实中求解复杂 SDE 只能使用离散解来逼近真正的解,所以该类方法试图优化 SDE 的离散化方法,在保证样本质量的同时减少离散步数。SGM 提出了一个通用的方法来求解逆向过程,即对前向和后向过程采取相同的离散方法。如果给定了前向 SDE 的离散方式:
那么我们就可以以相同的方式离散化逆向 SDE:
这种方法比朴素 DDPM 效果略好一点。进一步,SGM 向 SDE 求解器中加入了一个矫正器,从而让每一步生成的样本都有正确的分布。在求解的每一步,求解器给出一个样本后,矫正器都使用马尔可夫链蒙特卡罗方法来矫正刚生成的样本的分布。实验表明向求解器中加入矫正器比直接增加求解器的步数效率更高。
B. Non-Markovian Process 方法突破了原有 Markovian Process 的限制,其逆过程的每一步可以依赖更多以往的样本来进行预测新样本,所以在步长较大时也能做出较好的预测,从而加速采样过程。其中主要的工作 DDIM,不再假设前向过程是马尔可夫过程,而是服从如下分布:
DDIM 的采样过程可以视为离散化的神经常微分方程,其采样过程更高效,并且支持样本的内插。进一步的研究发现 DDIM 可以视作流形上扩散模型 PNDM 的特例。
C. Partial Sampling 方法通过在 generation process 中忽略一部分的时间节点,而只使用剩下的时间节点来生成样本,直接减少了采样时间。例如,Progressive Distillation 从训练好的扩散模型中蒸馏出效率更高的扩散模型。对于训练好的一个扩散模型,Progressive Distillation 会从新训练一个扩散模型,使新的扩散模型的一步对应于训练好的扩散模型的两步,这样新模型就可以省去老模型一半的采样过程。具体算法如下:
不断循环这个蒸馏过程就能让采样步骤指数级下降。
四.最大似然估计加强 扩散模型在最大似然估计的表现差于基于似然函数的生成模型,但最大化似然估计在诸多应用场景都有重要意义,比如图片压缩,半监督学习,对抗性净化。由于对数似然难以直接计算,研究主要集中在优化和分析变分下界(VLB)。我们对提高扩散模型最大似然估计的模型进行了详细的阐述。我们将其细化分类为三类方法:Objectives Designing,Noise Schedule Optimization,Learnable Reverse Variance。
A. Objectives Designing 方法利用扩散 SDE 推倒出生成数据的对数似然与分数函数匹配的损失函数的关系。这样通过适当设计损失函数,就可以最大化 VLB 和对数似然。Song et al. 证明了可以设计损失函数的权重函数,使得 plug-in reverse SDE 生成样本的似然函数值小于等于损失函数值,即损失函数是似然函数的上界。分数函数拟合的损失函数如下:
我们只需将权重函数 λ(t) 设为扩散系数 g(t)即可让损失函数成为似然函数的 VLB,即:
B. Noise Schedule Optimization 通过设计或学习前向过程的噪声进度来增大 VLB。VDM 证明了当离散步数接近无穷时,损失函数完全由信噪比函数 SNR(t)的端点决定:
那么在离散步数接近无穷时,可以通过学习信噪比函数 SNR(t)的端点最优化 VLB,而通过学习信噪比函数中间部分的函数值来实现模型其他方面的改进。
C. Learnable Reverse Variance 方法学习反向过程的方差,从而较少拟合误差,可以有效地最大化 VLB。Analytic-DPM 证明,在 DDPM 和 DDIM 中存在反向过程中的最优期望和方差:
使用上述公式和训练好的分数函数,在给定前向过程的条件下,最优的 VLB 可以近似达到。
五.数据泛化增强 扩散模型假设数据存在于欧几里得空间,即具有平面几何形状的流形,并添加高斯噪声将不可避免地将数据转换为连续状态空间,所以扩散模型最初只能处理图片等连续性数据,直接应用离散数据或其他数据类型的效果较差。这限制了扩散模型的应用场景。数个研究工作将扩散模型推广到适用于其他数据类型的模型,我们对这些方法进行了详细地阐释。我们将其细化分类为两类方法:Feature Space Unification,Data-Dependent Transition Kernels。
A. Feature Space Unification 方法将数据转化到统一形式的 latent space,然后再 latent space 上进行扩散。LSGM 提出将数据通过 VAE 框架先转换到连续的 latent space 上后再在其上进行扩散。这个方法的难点在于如何同时训练 VAE 和扩散模型。LSGM 表明由于潜在先验是 intractable 的,分数匹配损失不再适用。LSGM 直接使用 VAE 中传统的损失函数 ELBO 作为损失函数,并导出了 ELBO 和分数匹配的关系:
该式在忽略常数的意义下成立。通过参数化扩散过程中样本的分数函数,LSGM 可以高效的学习和优化 ELBO。
B. Data-Dependent Transition Kernels 方法根据数据类型的特点设计 diffusion process 中的 transition kernels,使扩散模型可以直接应用于特定的数据类型。D3PM 为离散型数据设计了 transition kernel,可以设为 lazy random-walk,absorbing state 等。GEODIFF 为 3D 分子图数据设计了平移 - 旋转不变的图神经网络,并且证明了具有不变性的初分布和 transition kernel 可以导出具有不变性的边缘分布。假设 T 是一个平移 - 旋转变换,如:
那么生成的样本分布也有平移 - 旋转不变性:
六.和其他生成模型的联系 在下面的每个小节中,我们首先介绍其他五类重要的生成模型,并分析它们的优势和局限性。然后我们介绍了扩散模型是如何与它们联系起来的,并说明通过结合扩散模型来改进这些生成模型。VAE,GAN,Autoregressive model, Normalizing flow, Energy-based model 和扩散模型的联系如下图所示:
A. DDPM 可以视作层次马尔可夫 VAE(hierarchical Markovian VAE)。但 DDPM 和一般的 VAE 也有区别。DDPM 作为 VAE,它的 encoder 和 decoder 都服从高斯分布、有马尔科夫行;其隐变量的维数和数据维数相同;decoder 的所有层都共用一个神经网络。
B. DDPM 可以帮助 GAN 解决训练不稳定的问题。因为数据是在高维空间中的低维流形中,所以 GAN 生成数据的分布和真实数据的分布重合度低,导致训练不稳定。扩散模型提供了一个系统地增加噪音的过程,通过扩散模型向生成的数据和真实数据添加噪音,然后将加入噪音的数据送入判别器,这样可以高效地解决 GAN 无法训练、训练不稳定的问题。
C. Normalizing flow 通过双射函数将数据转换到先验分布,这样的作法限制了 Normalizing flow 的表达能力,导致应用效果较差。类比扩散模型向 encoder 中加入噪声,可以增加 Normalizing flow 的表达能力,而从另一个视角看,这样的做法是将扩散模型推广到前向过程也可学习的模型。
D. Autoregressive model 在需要保证数据有一定的结构,这导致设计和参数化自回归模型非常困难。扩散模型的训练启发了自回归模型的训练,通过特定的训练方式避免了设计的困难。
E. Energy-based model 直接对原始数据的分布建模,但直接建模导致学习和采样都比较困难。通过使用扩散恢复似然,模型可以先对样本加入微小的噪声,再从有略微噪声的样本分布来推断原始样本的分布,使的学习和采样过程更简单和稳定。
七.扩散模型的应用 在下面的每个小节中,我们分别介绍了扩散模型在计算机视觉、自然语言处理、波形信号处理、多模态学习、分子图生成、时间序列以及对抗学习等七大应用方向中的应用,并对每类应用中的方法进行了细分并解析。例如在计算机视觉中可以用 diffusion model 进行图像补全修复(RePaint):
在多模态任务中可以用 diffusion model 进行文本到图像的生成(GLIDE):
还可以在分子图生成中用 diffusion model 进行药物分子和蛋白质分子的生成(GeoDiff):
应用分类汇总见表:
现在我们通过一个比喻来说明它们之间的区别。我们把数据的生成过程,也就是从 Z 映射到 X 的过程,比喻为过河。河的左岸是 Z,右岸是 X,过河就是乘船从左岸码头到达右岸码头。船可以理解为生成模型,码头的位置可以理解为样本点 Z 或者 X 在分布空间的位置。不同的生成模型有不同的过河的方法,如下图所示(图中小圆点代表样本点,大圆圈代表样本分布,绿色箭头表示 loss),我们分别来分析。
不同生成模型的过河方式
从先验分布随机采样一个 Z,也就是在左岸随便找一个码头,直接通过对抗损失的方式强制引导船开到右岸,要求右岸下船的码头和真实数据点在分布层面上比较接近。
1)VAE 在过河的时候,不是强制把河左岸的一个随机点拉到河右岸,而是考虑右岸的数据到达河左岸会落在什么样的码头。如果知道右岸数据到达左岸大概落在哪些码头,我们直接从这些码头出发就可以顺利回到右岸了。 2)由于 VAE 编码器的输出是一个高斯分布的均值和方差,一个右岸的样本数据 X 到达河左岸的码头位置不是一个固定点,而是一个高斯分布,这个高斯分布在训练时会和一个先验分布(一般是标准高斯分布)接近。 3)在数据生成时,从先验分布采样出来的 Z 也大概符合右岸过来的这几个码头位置,通过 VAE 解码器回到河右岸时,大概能到达真实数据分布所在的码头。
1)Flow 的过河方式和 VAE 有点类似,也是先看看河右岸数据到河左岸能落在哪些码头,在生成数据的时候从这些码头出发,就比较容易能到达河右岸。 2)和 VAE 不同的是,对于一个从河右岸码头出发的数据,通过 Flow 到达河左岸的码头是一个固定的位置,并不是一个分布。而且往返的船开着双程航线,来的时候从什么右岸码头到达左岸码头经过什么路线,回去的时候就从这个左岸码头经过这个路线到达这个右岸码头,是完全可逆的。 3)Flow 需要约束数据到达河左岸码头的位置服从一个先验分布(一般是标准高斯分布),这样在数据生成的时候方便从先验分布里采样码头的位置,能比较好的到达河右岸。
1)Diffusion 也借鉴了类似 VAE 和 Flow 的过河思想,要想到达河右岸,先看看数据从河右岸去到左岸会在哪个码头下船,然后就从这个码头上船,准能到达河右岸的码头。 2)但是和 Flow 以及 VAE 不同的是,Diffusion 不只看从右岸过来的时候在哪个码头下船,还看在河中央经过了哪些桥墩或者浮标点。这样从河左岸到河右岸的时候,也要一步一步打卡之前来时经过的这些浮标点,能更好约束往返的航线,确保到达河右岸的码头位置符合真实数据分布。 3)Diffusion 从河右岸过来的航线不是可学习的,而是人工设计的,能保证到达河左岸的码头位置,虽然有些随机性,但是符合一个先验分布(一般是高斯分布),这样方便我们在生成数据的时候选择左岸出发的码头位置。 4)因为训练模型的时候要求我们一步步打卡来时经过的浮标,在生成数据的时候,基本上也能遵守这些潜在的浮标位置,一步步打卡到达右岸码头。 5)如果觉得开到河右岸一步步这样打卡浮标有点繁琐,影响船的行进速度,可以选择一次打卡跨好几个浮标,就能加速船行速度,这就对应 diffusion 的加速采样过程。
1)可以类比 Diffusion 模型,将 AR 生成过程 X0,X0:1,…,X0:t,X0:t+1,…,X0:T 看成中间的一个个浮标。从河右岸到达河左岸的过程就好比自回归分解,将 X0:T 一步步拆解成中间的浮标,这个过程也是不用学习的。 2)河左岸的码头 X0 可以看成自回归生成的第一个 START token。AR 模型河左岸码头的位置是确定的,就是 START token 对应的 embedding。 3)在训练过程中,自回归模型也一个个对齐了浮标,所以在生成的时候也能一步步打卡浮标去到河右岸。 4)和 Diffusion 不同的是,自回归模型要想加速,跳过某些浮标,就没有那么容易了,除非重新训练一个 semi-autoregressive 的模型,一次生成多个 token 跨过多个浮标。 5)和 Diffusion 类似的是,在训练过程中都使用了 teacher-forcing 的方式,以当前步的 ground-truth 浮标位置为出发点,预测下一个浮标位置,这也降低了学习的难度,所以通常来讲,自回归模型和 Diffusion 模型训练起来都比较容易。
综上所述,大模型技术正朝着更大规模、更高效推理及多模态融合的方向发展。掌握 Transformer 架构及其变体、理解生成模型原理,对于从事 AI 开发至关重要。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online