DataWhale-9月组队学习打卡Day6

写在前面

感觉这个写在前面都快成我每天的日记本了(笑)。前天的时候终于第一次来了这边校区的图书馆,很大很漂亮。没想到我的卡竟然刷不进新校区图书馆,也是寄寄了。想着可能是因为校园卡被冻结了,于是昨天跑了一趟老图,没想到老图还能刷。很震惊。把校园卡解冻后还没尝试刷校园卡买东西,刷新图书馆还是刷不进来,图书馆管理员查询我信息也很正常。最后打电话给技术部,今天早上终于是给解决了。。不得不说学校技术部门还是给力。
我昨天在图书馆里发现了一本自然语言处理的书籍,上次有说到关于《数学之美》。其实那本书我还没有读完。上学年冬天我完成了一个词频统计的小课设,虽然是帮别人做的,但是自己学到了很多。昨天发现了一本讲如何进行汉语自然语言处理的书,是何晗老师写的,叫做《自然语言处理入门》。(图书馆的领导告诉我,这都是刚进的新书。说真的图书馆还蛮会进书的,但是希望它少进点PS相关的书来咱们研究生图书馆。多进点CS相关的外文书籍,乐。) 里面的内容怎么说呢,第一章就吊打了我上学期做的那个课设,这种感觉就跟我高考时候天天做的那几个函数,等到看高等数学时发现第一章就会介绍他们是典型函数一样。。觉得自己做的东西很浅显,也觉得自己很渺小。
正好今天的学习内容也和分词有关,可以说是很干货的一节。所以也就相当于顺带温习和学习。(话说ubuntu的鼠标指针是真的小啊,好想魔改。。
不说了,开始今天的学习。

7.1 大模型之模型概括

自然语言处理的专家弗莱德里克·贾里尼克教授说过:一个句子是否合理,就看它的可能性大小如何。统计语言模型(Statistical Language Model)即是用来描述词、语句乃至于整个文档这些不同的语法单元的概率分布的模型,能够用于衡量某句话或者词序列是否符合所处语言环境下人们日常的行文说话方式。

语言模型的一开始就可以被看做是一个黑箱,当前大规模语言模型的能力在于给定一个基于自身需求的prompt就可以生成符合需求的结果。形式可以表达为:
p r o m p t ⇝ c o m p l e t i o n prompt \leadsto completion promptcompletion
从数学的角度考虑就对训练数据 (traing data: ( x 1 , … , x L ) (x_{1},…,x_{L}) x1,,xL)的概率分布:
t r a i n i n g D a t a ⇒ p ( x 1 , . . . , x L ) . trainingData \Rightarrow p(x_{1},...,x_{L}). trainingDatap(x1,...,xL).
本节内容讨论大型语言模型是如何构建的。
其中着重讨论两个主题,分别是分词和模型架构:

  • 分词:即如何将一个字符串拆分成多个标记。

  • 模型架构:我们将主要讨论Transformer架构,这是真正实现大型语言模型的建模创新。

当讨论一个自然语言模型时,我们可以着重讨论其中的分词和模型架构。这两个方面是构建一个自然语言模型的基本要素。
分词是将连续的文本序列切分成有意义的词语或标记的过程。 在自然语言处理中,分词是一个重要的预处理步骤,它可以将一段连续的文本转化为离散的词语列表。常见的分词方法包括基于规则的分词、基于统计的分词和基于机器学习的分词。
基于规则的分词方法使用预定义的规则和词典来进行分词,例如基于特定的字符规则、词典匹配等。
(我之前做的就是基于预定义的规则/辞典进行词频统计,利用jieba库之类的)
基于统计的分词方法则利用统计模型(n-gram)和语言模型 来推断最可能的词语边界。
它的核心思想是:在上下文中,相邻的字同时出现的次数越多,就越有可能构成一个词。因此字与字相邻出现的概率或频率能较好地反映成词的可信度。 可以对训练文本中相邻出现的各个字的组合的频度进行统计,计算它们之间的互现信息。
互现信息体现了汉字之间结合关系的紧密程度。当紧密程度高于某一个阈值时,便可以认为此字组可能构成了一个词。 该方法又称为无字典分词。
基于机器学习的分词方法通过训练一个模型来进行分词,例如使用条件随机场(CRF)或循环神经网络(RNN)等。
本世纪初,最流行的统计语言模型当属 N-gram,其属于典型的基于稀疏表示(Sparse Representation)的语言模型;近年来随着深度学习的爆发与崛起,以词向量(Word Embedding)为代表的分布式表示(Distributed Representation)的语言模型取得了更好的效果,并且深刻地影响了自然语言处理领域的其他模型与应用的变革。
除此之外,还有

用于分词的模型还有很多/。如基于决策树的语言模型(Decision Tree Models)、最大熵模型以及自适应语言模型(Adaptive
Models)等。还可以利用维比特算法来求解HMM模型(这方面有点涉及到计算机语言学,有点感兴趣)。

模型架构:自然语言模型的架构是指模型的整体结构和组成方式。在自然语言处理中,常见的模型架构包括传统的n-gram模型、基于概率的语言模型、循环神经网络(RNN)和变种(如长短期记忆网络,LSTM)以及最近流行的预训练模型(如BERT、GPT等)。
n-gram模型是一种基于n个连续词语的概率模型,用于预测下一个词语的出现概率。它简单有效,但无法捕捉长距离依赖关系。RNN是一种递归神经网络,可以处理变长序列数据,如文本。它通过记忆之前的信息来建模上下文依赖关系。LSTM则是一种改进的RNN结构,可以更好地处理长距离依赖关系。
预训练模型是最近在自然语言处理领域取得重大突破的模型架构。它们通过在大规模语料库上进行预训练,学习到丰富的语言表示。这些模型可以在各种下游任务(如情感分析、命名实体识别等)上进行微调,以提高性能和泛化能力。

总之,分词和模型架构是构建自然语言模型的关键要素。分词决定了如何将文本转化为词语序列,而模型架构则决定了如何建模语言的规律和依赖关系。通过合理选择和设计分词方法和模型架构,我们可以构建出更准确、更强大的自然语言模型。

7.1.1 基于稀疏表示的语言模型,分布式表示的语言模型

基于稀疏表示的语言模型是一种传统的语言模型,它将每个单词表示为一个高维向量,其中大部分元素都是0。这种表示方法被称为“one-hot”表示,因为每个单词的向量只有一个元素是1,其他元素都是0。 这种表示方法的缺点是,它需要非常高维的向量来表示每个单词,因此会导致维度灾难问题。 此外,由于每个单词的向量都是独立的,因此无法捕捉到单词之间的语义关系。
分布式表示的语言模型则采用了一种更加紧凑和语义丰富的表示方法。在这种表示方法中,每个单词被表示为一个低维向量,其中每个元素都包含了该单词的一些语义信息。 这种表示方法可以通过训练神经网络来学习得到,因此也被称为神经网络语言模型。分布式表示的语言模型可以更好地捕捉到单词之间的语义关系,并且可以将这些关系应用于自然语言处理任务中。

7.2 分词

回顾:语言模型 p p p是一个对标记(token)序列的概率分布,其中每个标记来自某个词汇表 V V V

[ t h e , m o u s e , a t e , t h e , c h e e s e ] [the, mouse, ate, the, cheese] [the,mouse,ate,the,cheese]

然而,自然语言并不是以标记序列的形式出现,而是以字符串的形式存在(具体来说,是Unicode字符的序列),比如上面的序列的自然语言为“the mouse ate the cheese”。

分词器将任意字符串转换为标记序列:the mouse ate the cheese ⇒ [ t h e , m o u s e , a t e , t h e , c h e e s e ] \Rightarrow [the, mouse, ate, the, cheese] [the,mouse,ate,the,cheese]

这并不一定是语言建模中最引人注目的部分,但在确定模型的工作效果方面起着非常重要的作用。我们也可以将这个方式理解为自然语言和机器语言的一种显式的对齐。下面我对分词的一些细节进一步的讨论。

7.2.1 基于空格的分词

最简单的解决方案是使用text.split(' ')方式进行分词,这种分词方式对于英文这种按照空格,且每个分词后的单词有语义关系的文本是简单而直接的分词方式。然而,对于一些语言,如中文,句子中的单词之间没有空格

还有一些语言,比如德语,存在着长的复合词(例如Abwasserbehandlungsanlange)。即使在英语中,也有连字符词(例如father-in-law)和缩略词(例如don’t),它们需要被正确拆分。 例如,Penn Treebank将don’t拆分为do和n’t,这是一个在语言上基于信息的选择,但不太明显。因此,仅仅通过空格来划分单词会带来很多问题。

那么,什么样的分词才是好的呢?目前从直觉和工程实践的角度来说:

- 首先我们不希望有太多的标记(极端情况:字符或字节),否则序列会变得难以建模。其次我们也不希望标记过少,否则单词之间就无法共享参数(例如,mother-in-law和father-in-law应该完全不同吗?),这对于形态丰富的语言尤其是个问题(例如,阿拉伯语、土耳其语等)。每个标记应该是一个在语言或统计上有意义的单位。

7.2.2 Byte pair encoding

字节对编码算法(Byte Pair Encoding,BPE)是一种基于统计的数据压缩算法,常用于自然语言处理中的分词任务。它的核心思想是将原始文本中的字符序列逐步合并成更长的字符序列,直到达到预设的词表大小或者满足一定的停止条件(设定的阈值)为止。 BPE算法可以通过学习语料库中出现频率较高的字符序列来自适应地生成词表,从而避免了传统分词方法中需要使用人工构建的词典。 BPE算法在机器翻译、文本摘要、问答系统等多个自然语言处理任务中都有广泛应用。
首先,将所有字符序列视为单个字符,得到初始词表。
接下来,计算每个相邻字符序列出现的频率,并将出现频率最高的相邻字符序列合并成一个新字符
接下来,重复上述过程,直到达到预设的词表大小或者满足一定的停止条件设定的阈值)为止。

字节对编码BPE)算法应用于数据压缩领域,用于生成其中一个最常用的分词器。 BPE分词器需要通过模型训练数据进行学习,获得需要分词文本的一些频率特征。

学习分词器的过程,直觉上,我们将每个字符作为自己的标记,并组合那些经常共同出现的标记。整个过程可以表示为:

  • 输入:训练语料库(字符序列)。
  • 初始化词汇表 V V V为字符的集合。
  • 当我们仍然希望V继续增长时:
    **找到 V V V中共同出现次数最多的元素对 x , x ′ x,x' x,x
  • 用一个新的符号 x x ′ xx' xx替换所有 x , x ′ x,x' x,x的出现。将
  • x x ′ xx' xx添加到V中。**

这里举一个例子:

1 [ t , h , e , c , c , a , r ] , [ t , h , e , c , c , a , t ] , [ t , h , e , c , r , a , t ] 1[t, h, e, c, c, a, r],[t, h, e, c, c, a, t],[t, h, e, c, r, a, t] 1[t,h,e,c,c,a,r],[t,h,e,c,c,a,t],[t,h,e,c,r,a,t]
2 [th, e, , c , a , r ] , [ t h , e , u , c , a , t ] , [ t h , e , c , r , a , t ] , c, a, r],[t h, e, u, c, a, t],[t h, e, c, r, a, t] ,c,a,r],[th,e,u,c,a,t],[th,e,c,r,a,t] (th 出现了 3次)
3 [the, ≤ , c , a , r ] \leq, c, a, r] ,c,a,r], [the, ⊔ , c , a , t ] , [ \sqcup, c, a, t],[ ,c,a,t],[ the, u , r , a , t ] u, r, a, t] u,r,a,t] (the 出现了 3次)

7.2.2.1 Unicode的问题

Unicode(统一码)是一种字符编码标准,它为世界上大部分的文字系统提供了一个唯一的数字编码,不论是什么平台、不论是什么程序、不论是什么语言。Unicode最初由Xerox公司提出,后来由Unicode联盟(Unicode Consortium)负责维护。Unicode的目标是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。 Unicode包括字符集、编码方案等,其整理、编码了世界上大部分的文字系统。

Unicode(统一码)是当前主流的一种编码方式。但是Unicode字符非常多(共144,697个字符)。在训练数据中我们不可能见到所有的字符。
为了进一步减少多语言处理问题中数据的稀疏性,我们可以对字节而不是Unicode字符运行BPE算法Wang等人,2019年)。
以中文为例:
 今天 ⇒ [x62, x11, 4e, ca] \text { 今天} \Rightarrow \text {[x62, x11, 4e, ca]}  今天[x62, x11, 4e, ca]
BPE算法在这里的作用是为了进一步减少数据的稀疏性。通过对字节级别进行分词,可以在多语言环境中更好地处理Unicode字符的多样性,并减少数据中出现的低频词汇,提高模型的泛化能力。 通过使用字节编码,可以将不同语言中的词汇统一表示为字节序列,从而更好地处理多语言数据。

7.2.2.2 数据的稀疏性和稀疏表示的大模型

稀疏表示通常是指在高维空间中,数据点之间的距离非常大,而且大部分数据点都是零。这种情况下,我们可以使用一些技术来减少数据的稀疏性,例如使用稠密向量表示、BPE分词算法等方法。
BPE算法合成了一个新的标记。这并不是一个新的向量,而是一个新的token,它可以被视为与原始文本中出现频率较高的字符序列等价的字符。因此,BPE算法可以大大减少token列表的大小,并且可以更好地处理多语言编码方式不同的问题。
在BPE算法中,我们将文本中的每个字符视为一个token,并将其添加到初始token列表中。然后,我们计算每个相邻字符序列出现的频率,并将出现频率最高的相邻字符序列合并成一个新字符。这样,我们就得到了一个新的词表,其中包含了原始文本中出现频率较高的字符序列和新字符。在BPE算法中,token是指词表中的一个元素,可以是单个字符或多个字符组成的子序列。

7.2.3 Unigram model (SentencePiece)

Unigram model是一种基于统计的语言模型,它是N-gram模型的一种特殊情况,其中N=1。Unigram model假设每个单词出现的概率与其他单词出现的概率是独立的,因此可以将整个句子的概率表示为各个单词出现概率的乘积。Unigram model是一种简单但有效的语言模型,它在自然语言处理中得到了广泛应用,例如自动补全、拼写纠错、文本分类等任务。

与仅仅根据频率进行拆分不同,一个更“有原则”的方法是定义一个目标函数来捕捉一个好的分词的特征,这种基于目标函数的分词模型可以适应更好分词场景,Unigram model就是基于这种动机提出的。我们现在描述一下unigram模型(Kudo,2018年)。

这是SentencePiece工具(Kudo&Richardson,2018年)所支持的一种分词方法,与BPE一起使用。
它被用来训练T5和Gopher模型。给定一个序列 x 1 : L x_{1:L} x1:L,一个分词器 T T T p ( x 1 : L ) = ∏ ( i , j ) ∈ T p ( x i : j ) p\left(x_{1: L}\right)=\prod_{(i, j) \in T} p\left(x_{i: j}\right) p(x1:L)=(i,j)Tp(xi:j)的一个集合。

unigram 模型通过统计每个词汇在训练数据中的出现次数来估计其概率 。假设,𝖺𝖻 在训练数据中出现了两次,𝖼 出现了一次。因此,根据 unigram 模型的估计,p(𝖺𝖻)=2/3,p(𝖼)=1/3。 通过将各个词汇的概率相乘,我们可以得到整个训练数据的似然值为 4 / 9 4/9 4/9

似然值的计算是 unigram 模型中重要的一部分,它用于评估分词结果的质量。较高的似然值表示训练数据与分词结果之间的匹配程度较高,这意味着该分词结果较为准确或合理。

7.2.3.1 算法流程

EM算法(Expectation-Maximization Algorithm)是一种迭代优化算法,用于含有隐变量的概率模型的最大似然估计或极大后验概率估计。EM算法最初是为了解决数据缺失情况下的参数估计问题而提出的,其算法基础和收敛有效性等问题在Dempster、Laird和Rubin三人于1977年所做的文章《Maximum likelihood from incomplete data via the EM algorithm》中给出了详细的阐述。EM算法的基本思想是:先初始化模型参数,然后通过迭代的方式,不断地更新模型参数,直到收敛为止。在每次迭代中,EM算法分为两步:E步(Expectation Step)和M步(Maximization Step)。在E步中,根据当前的模型参数,计算隐变量的后验概率分布;在M步中,根据当前的隐变量后验概率分布,更新模型参数。通过不断地迭代E步和M步,可以逐渐提高模型的似然函数值,并得到最优的模型参数。

  • 从一个“相当大”的种子词汇表 V V V 开始。
  • 重复以下步骤:
    • 给定 V V V ,使用EM算法优化 p ( x ) p(x) p(x) T T T 分词。
    • 计算每个词汇 x ∈ V x∈V xV l o s s ( x ) loss(x) loss(x) ,衡量如果将 x x x V V V 中移除,似然值会减少多少。
    • 按照 l o s s loss loss 进行排序,并保留 V V V 中排名靠前的80%的词汇。

这个过程旨在优化词汇表,剔除对似然值贡献较小的词汇,以减少数据的稀疏性,并提高模型的效果。 通过迭代优化和剪枝,词汇表会逐渐演化,保留那些对于似然值有较大贡献的词汇,提升模型的性能。
减少数据的稀疏性。主要指的就是 降低维度。

7.3 模型架构

到目前为止,我们已经将语言模型定义为对标记序列的概率分布 p ( x 1 , … , x L ) p(x_{1},…,x_{L}) p(x1,,xL) 我们已经看到这种定义非常优雅且强大(通过提示,语言模型原则上可以完成任何任务,正如GPT-3所示)。然而,在实践中,对于专门的任务来说,避免生成整个序列的生成模型可能更高效。

在某些情况下,为了提高效率,我们可以使用一些不需要生成整个序列的生成模型。生成模型是一种常用的概率模型,它可以用来生成符合某种分布的样本。在自然语言处理中,生成模型通常用于文本生成、机器翻译、语音合成等任务。避免生成整个序列的生成模型意味着我们可以只生成部分序列或者不需要生成整个序列,从而减少计算量和内存占用。
上下文向量表征和分布式表示的语言模型都是将自然语言中的单词转换为向量的方法,并且都可以用于捕捉单词之间的语义关系。上下文向量表征是一种将自然语言中的单词转换为向量的方法,它可以将单词的语义信息编码为向量形式。在上下文向量表征中,每个单词被表示为一个低维向量,其中每个元素都包含了该单词的一些语义信息。

上下文向量表征 (Contextual Embedding): 作为先决条件,主要的关键发展是将标记序列与相应的上下文的向量表征:
[ t h e , m o u s e , a t e , t h e , c h e e s e ] ⇒ ϕ [ ( 1 0.1 ) , ( 0 1 ) , ( 1 1 ) , ( 1 − 0.1 ) , ( 0 − 1 ) ] . [the, mouse, ate, the, cheese] \stackrel{\phi}{\Rightarrow}\left[\left(\begin{array}{c} 1 \\ 0.1 \end{array}\right),\left(\begin{array}{l} 0 \\ 1 \end{array}\right),\left(\begin{array}{l} 1 \\ 1 \end{array}\right),\left(\begin{array}{c} 1 \\ -0.1 \end{array}\right),\left(\begin{array}{c} 0 \\ -1 \end{array}\right)\right]. [the,mouse,ate,the,cheese]ϕ[(10.1),(01),(11),(10.1),(01)].
正如名称所示,标记的上下文向量表征取决于其上下文(周围的单词);例如,考虑mouse的向量表示需要关注到周围某个窗口大小的其他单词。

  • 符号表示:我们将 ϕ : V L → R d × L ϕ:V^{L}→ℝ^{d×L} ϕ:VLRd×L 定义为嵌入函数(类似于序列的特征映射,映射为对应的向量表示)。

  • 对于标记序列 x 1 : L = [ x 1 , … , x L ] x1:L=[x_{1},…,x_{L}] x1:L=[x1,,xL] ϕ ϕ ϕ 生成上下文向量表征 ϕ ( x 1 : L ) ϕ(x_{1:L}) ϕ(x1:L)

7.3.1 语言模型分类

对于语言模型来说,编码-解码端 (Encoder-Decoder)的架构最初的起源来自于Transformer模型。但是当前对于语言模型的分类,将语言模型分为三个类型:编码端(Encoder-Only),解码端(Decoder-Only)和编码-解码端(Encoder-Decoder)。因此我们的架构展示以当前的分类展开。
这段话看着我觉得,很神秘……

自然语言处理的分类和回归任务

分类任务(也呗称为自然语言理解任务),自回归语言模型通常被称为自然语言生成任务。

7.3.1.1 编码端(Encoder-Only)架构

编码端架构的著名的模型如BERT、RoBERTa等。这些语言模型生成上下文向量表征,但不能直接用于生成文本。 可以表示为, x 1 : L ⇒ ϕ ( x 1 : L ) x_{1:L}⇒ϕ(x_{1:L}) x1:Lϕ(x1:L)。这些上下文向量表征通常用于分类任务(也呗称为自然语言理解任务)。任务形式比较简单,下面以情感分类/自然语言推理任务举例:
情感分析输入与输出形式: [ [ C L S ] , 他们 , 移动 , 而 , 强大 ] ⇒ 正面情绪 情感分析输入与输出形式:[[CLS], 他们, 移动, 而, 强大]\Rightarrow 正面情绪 情感分析输入与输出形式:[[CLS],他们,移动,,强大]正面情绪

自然语言处理输入与输出形式: [ [ C L S ] , 所有 , 动物 , 都 , 喜欢 , 吃 , 饼干 , 哦 ] ⇒ 蕴涵 自然语言处理输入与输出形式:[[CLS], 所有, 动物, 都, 喜欢, 吃, 饼干, 哦]⇒蕴涵 自然语言处理输入与输出形式:[[CLS],所有,动物,,喜欢,,饼干,]蕴涵

该架构的优势是对于文本的上下文信息有更好的理解,因此该模型架构才会多用于理解任务。该架构的有点是对于每个 x i x{i} xi上下文向量表征可以双向地依赖于左侧上下文 ( x 1 : i − 1 ) (x_{1:i−1}) (x1:i1) 和右侧上下文 ( x i + 1 : L ) (x_{i+1:L}) (xi+1:L)。但是缺点在于不能自然地生成完成文本,且需要更多的特定训练目标(如掩码语言建模)。

掩码语言建模(Masked Language Modeling,MLM)是一种自然语言处理任务,它的目的是训练语言模型来预测被遮盖的词语,以便在进行文本生成或其他任务时更准确地预测语言。
在 MLM 中,输入文本中的一些词语会被遮盖,然后用语言模型来预测这些被遮盖的词语。掩码语言建模最初是在 BERT 模型中提出的,它通过掩盖输入文本中的一些词语来训练模型,从而提高了模型的精度。掩码语言建模已经被广泛应用于自然语言处理领域,例如文本分类、情感分析、机器翻译等任务。

7.3.3.2 解码器(Decoder-Only)架构

解码器架构的著名模型就是大名鼎鼎的GPT系列模型。这些是我们常见的自回归语言模型,给定一个提示 x 1 : i x_{1:i} x1:i,它们可以生成上下文向量表征,并对下一个标记 x i + 1 x_{i+1} xi+1(以及递归地,整个完成 x i + 1 : L x_{i+1:L} xi+1:L)生成一个概率分布。 x 1 : i ⇒ ϕ ( x 1 : i ) , p ( x i + 1 ∣ x 1 : i ) x_{1:i}⇒ϕ(x_{1:i}),p(x_{i+1}∣x_{1:i}) x1:iϕ(x1:i),p(xi+1x1:i)。我们以自动补全任务来说,输入与输出的形式为, [ [ C L S ] , 他们 , 移动 , 而 ] ⇒ 强大 [[CLS], 他们, 移动, 而]⇒强大 [[CLS],他们,移动,]强大。与编码端架构比,其优点为能够自然地生成完成文本,有简单的训练目标(最大似然)。缺点也很明显,对于每个 x i xi xi,上下文向量表征只能单向地依赖于左侧上下文 ( x 1 : i − 1 x_{1:i−1} x1:i1)。

7.3.3.3 编码-解码端(Encoder-Decoder)架构

编码-解码端架构就是最初的Transformer模型,其他的还有如BART、T5等模型。这些模型在某种程度上结合了两者的优点:它们可以**使用双向上下文向量表征来处理输入 x 1 : L x_{1:L} x1:L,并且可以生成输出 y 1 : L y_{1:L} y1:L。**可以公式化为:
x 1 : L ⇒ ϕ ( x 1 : L ) , p ( y 1 : L ∣ ϕ ( x 1 : L ) ) 。 x1:L⇒ϕ(x1:L),p(y1:L∣ϕ(x1:L))。 x1:Lϕ(x1:L),p(y1:Lϕ(x1:L))
以表格到文本生成任务为例,其输入和输出的可以表示为:
[ 名称 : , 植物 , ∣ , 类型 : , 花卉 , 商店 ] ⇒ [ 花卉 , 是 , 一 , 个 , 商店 ] 。 [名称:, 植物, |, 类型:, 花卉, 商店]⇒[花卉, 是, 一, 个, 商店]。 [名称:,植物,,类型:,花卉,商店][花卉,,,,商店]

该模型的具有编码端,解码端两个架构的共同的优点,对于每个 x i x_{i} xi,上下文向量表征可以双向地依赖于左侧上下文 ( x 1 : i − 1 x_{1:i−1} x1:i1) 和右侧上下文 ( x i + 1 : L x_{i+1:L} xi+1:L),可以自由的生成文本数据。缺点就说需要更多的特定训练目标。

7.3.4 语言模型理论

下一步,我们会介绍语言模型的模型架构,重点介绍Transformer架构机器延伸的内容。另外我们对于架构还会对于之前RNN网络的核心知识进行阐述,其目的是对于代表性的模型架构进行学习。

深度学习的美妙之处在于能够创建构建模块,就像我们用函数构建整个程序一样。因此,在下面的模型架构的讲述中,我们能够像下面的函数一样封装,以函数的的方法进行理解:
T r a n s f o r m e r B l o c k ( x 1 : L ) TransformerBlock(x_{1:L}) TransformerBlock(x1:L)
为了简单起见,我们将在函数主体中包含参数,接下来,我们将定义一个构建模块库,直到构建完整的Transformer模型。

7.3.4.1 基础架构

首先,我们需要将标记序列转换为序列的向量形式。 E m b e d T o k e n EmbedToken EmbedToken函数通过在嵌入矩阵 E ∈ R ∣ v ∣ × d E∈ℝ^{|v|×d} ERv×d中查找每个标记所对应的向量,该向量的具体值这是从数据中学习的参数:

def EmbedToken(x_{1:L}:V^{L})→ℝ^{d×L}:

- 将序列$x_{1:L}$中的每个标记$xi$转换为向量。
- 返回[Ex1,…,ExL]。

以上的词嵌入是传统的词嵌入,向量内容与上下文无关。这里我们定义一个抽象的 S e q u e n c e M o d e l SequenceModel SequenceModel函数,它接受这些上下文无关的嵌入,并将它们映射为上下文相关的嵌入。

d e f S e q u e n c e M o d e l ( x 1 : L : R d × L ) → R d × L def SequenceModel(x_{1:L}:ℝ^{d×L})→ℝ^{d×L} defSequenceModel(x1:L:Rd×L)Rd×L

  • 针对序列 x 1 : L x_{1:L} x1:L中的每个元素xi进行处理,考虑其他元素。
  • [抽象实现(例如, F e e d F o r w a r d S e q u e n c e M o d e l FeedForwardSequenceModel FeedForwardSequenceModel S e q u e n c e R N N SequenceRNN SequenceRNN T r a n s f o r m e r B l o c k TransformerBlock TransformerBlock)]

最简单类型的序列模型基于前馈网络(Bengio等人,2003),应用于固定长度的上下文,就像n-gram模型一样,函数的实现如下:

def $FeedForwardSequenceModel(x_{1:L}:ℝ^{d×L})→ℝ^{d×L}$:

- 通过查看最后$n$个元素处理序列$x_{1:L}$中的每个元素$xi$。
- 对于每个$i=1,…,L$:
  - 计算$h_{i}$=$FeedForward(x_{i−n+1},…,x_{i})$。
- 返回[$h_{1},…,h_{L}$]。
7.3.4.2 递归神经网络

第一个真正的序列模型是递归神经网络(RNN),它是一类模型,包括简单的RNN、LSTM和GRU。基本形式的RNN通过递归地计算一系列隐藏状态来进行计算。

def S e q u e n c e R N N ( x : R d × L ) → R d × L SequenceRNN(x:ℝ^{d×L})→ℝ^{d×L} SequenceRNN(x:Rd×L)Rd×L

  • 从左到右处理序列 x 1 , … , x L x_{1},…,x_{L} x1,,xL,并递归计算向量 h 1 , … , h L h_{1},…,h_{L} h1,,hL
  • 对于 i = 1 , … , L i=1,…,L i=1,,L
    • 计算 h i = R N N ( h i − 1 , x i ) h_{i}=RNN(h_{i−1},x_{i}) hi=RNN(hi1,xi)
    • 返回 [ h 1 , … , h L ] [h_{1},…,h_{L}] [h1,,hL]

实际完成工作的模块是RNN,类似于有限状态机,它接收当前状态h、新观测值x,并返回更新后的状态:

def R N N ( h : R d , x : R d ) → R d RNN(h:ℝ^d,x:ℝ^d)→ℝ^d RNN(h:Rd,x:Rd)Rd

  • 根据新的观测值x更新隐藏状态h。
  • [抽象实现(例如,SimpleRNN,LSTM,GRU)]

有三种方法可以实现RNN。最早的RNN是简单RNN(Elman,1990),它将 h h h x x x的线性组合通过逐元素非线性函数 σ σ σ(例如,逻辑函数 σ ( z ) = ( 1 + e − z ) − 1 σ(z)=(1+e−z)−1 σ(z)=(1+ez)1或更现代的 R e L U ReLU ReLU函数 σ ( z ) = m a x ( 0 , z ) σ(z)=max(0,z) σ(z)=max(0,z))进行处理。

def S i m p l e R N N ( h : R d , x : R d ) → R d SimpleRNN(h:ℝd,x:ℝd)→ℝd SimpleRNN(h:Rd,x:Rd)Rd

  • 通过简单的线性变换和非线性函数根据新的观测值 x x x更新隐藏状态 h h h
  • 返回 σ ( U h + V x + b ) σ(Uh+Vx+b) σ(Uh+Vx+b)

正如定义的RNN只依赖于过去,但我们可以通过向后运行另一个RNN来使其依赖于未来两个。这些模型被ELMo和ULMFiT使用。

def B i d i r e c t i o n a l S e q u e n c e R N N ( x 1 : L : R d × L ) → R 2 d × L BidirectionalSequenceRNN(x_{1:L}:ℝ^{d×L})→ℝ^{2d×L} BidirectionalSequenceRNN(x1:L:Rd×L)R2d×L\:

  • 同时从左到右和从右到左处理序列。
  • 计算从左到右: [ h → 1 , … , h → L ] ← S e q u e n c e R N N ( x 1 , … , x L ) [h→_{1},…,h→_{L}]←SequenceRNN(x_{1},…,x_{L}) [h1,,hL]SequenceRNN(x1,,xL)
  • 计算从右到左: [ h ← L , … , h ← 1 ] ← S e q u e n c e R N N ( x L , … , x 1 ) [h←_{L},…,h←_{1}]←SequenceRNN(x_{L},…,x_{1}) [hL,,h1]SequenceRNN(xL,,x1)
  • 返回 [ h → 1 h ← 1 , … , h → L h ← L ] [h→_{1}h←_{1},…,h→_{L}h←_{L}] [h1h1,,hLhL]

注:

  • 简单RNN由于梯度消失的问题很难训练。
  • 为了解决这个问题,发展了长短期记忆(LSTM)和门控循环单元(GRU)(都属于RNN)。
  • 然而,即使嵌入h200可以依赖于任意远的过去(例如,x1),它不太可能以“精确”的方式依赖于它(更多讨论,请参见Khandelwal等人,2018)。
  • 从某种意义上说,LSTM真正地将深度学习引入了NLP领域。
7.3.4.3 Transformer

现在,我们将讨论Transformer(Vaswani等人,2017),这是真正推动大型语言模型发展的序列模型。正如之前所提到的,Transformer模型将其分解为Encoder-Only(GPT-2,GPT-3)、Decoder-Only(BERT,RoBERTa)和Encoder-Decoder(BART,T5)模型的构建模块。

关于Transformer的学习资源有很多:

强烈建议您阅读这些参考资料。该课程主要依据代码函数和接口进行讲解。

7.3.4.3.1 注意力机制

Transformer的关键是注意机制,这个机制早在机器翻译中就被开发出来了(Bahdananu等人,2017)。可以将注意力视为一个“软”查找表,其中有一个查询 y y y,我们希望将其与序列 x 1 : L = [ x 1 , … , x L ] x_{1:L}=[x_1,…,x_L] x1:L=[x1,,xL]的每个元素进行匹配。我们可以通过线性变换将每个 x i x_{i} xi视为表示键值对:
( W k e y x i ) : ( W v a l u e x i ) (W_{key}x_{i}):(W_{value}x_{i}) (Wkeyxi)(Wvaluexi)

并通过另一个线性变换形成查询:
W q u e r y y W_{query}y Wqueryy
可以将键和查询进行比较,得到一个分数:
s c o r e i = x i ⊤ W k e y ⊤ W q u e r y y score_{i}=x^{⊤}_{i}W^{⊤}_{key}W_{query}y scorei=xiWkeyWqueryy
这些分数可以进行指数化和归一化,形成关于标记位置 1 , … , L {1,…,L} 1,,L的概率分布:
[ α 1 , … , α L ] = s o f t m a x ( [ s c o r e 1 , … , s c o r e L ] ) [α_{1},…,α_{L}]=softmax([score_{1},…,score_{L}]) [α1,,αL]=softmax([score1,,scoreL])
然后最终的输出是基于值的加权组合:
∑ i = 1 L α i ( W v a l u e x i ) \sum_{i=1}^L \alpha_i\left(W_{value} x_i\right) i=1Lαi(Wvaluexi)
我们可以用矩阵形式简洁地表示所有这些内容:

def A t t e n t i o n ( x 1 : L : R d × L , y : R d ) → R d Attention(x_{1:L}:ℝ^{d×L},y:ℝ^d)→ℝ^d Attention(x1:L:Rd×L,y:Rd)Rd

  • 通过将其与每个 x i x_{i} xi进行比较来处理 y y y
  • 返回 W v a l u e x 1 : L softmax ⁡ ( x 1 : L ⊤ W k e y ⊤ W q u e r y y / d ) W_{value} x_{1: L} \operatorname{softmax}\left(x_{1: L}^{\top} W_{key}^{\top} W_{query} y / \sqrt{d}\right) Wvaluex1:Lsoftmax(x1:LWkeyWqueryy/d )

我们可以将注意力看作是具有多个方面(例如,句法、语义)的匹配。为了适应这一点,我们可以同时使用多个注意力头,并简单地组合它们的输出。

def M u l t i H e a d e d A t t e n t i o n ( x 1 : L : R d × L , y : R d ) → R d MultiHeadedAttention(x_{1:L}:ℝ^{d×L},y:ℝ^{d})→ℝ^{d} MultiHeadedAttention(x1:L:Rd×L,y:Rd)Rd:

  • 通过将其与每个xi与nheads个方面进行比较,处理y。
  • 返回 W o u t p u t [ [ Attention ⁡ ( x 1 : L , y ) , … , Attention ⁡ ( x 1 : L , y ) ] ⏟ n h e a d s t i m e s W_{output}[\underbrace{\left[\operatorname{Attention}\left(x_{1: L}, y\right), \ldots, \operatorname{Attention}\left(x_{1: L}, y\right)\right]}_{n_{heads}times} Woutput[nheadstimes [Attention(x1:L,y),,Attention(x1:L,y)]

对于自注意层,我们将用 x i x_{i} xi替换 y y y作为查询参数来产生,其本质上就是将自身的 x i x_{i} xi对句子的其他上下文内容进行 A t t e n t i o n Attention Attention的运算:

def S e l f A t t e n t i o n ( x 1 : L : R d × L ) → R d × L ) SelfAttention(x_{1:L}:ℝ_{d×L})→ℝ_{d×L}) SelfAttention(x1:L:Rd×L)Rd×L)

  • 将每个元素xi与其他元素进行比较。
  • 返回 [ A t t e n t i o n ( x 1 : L , x 1 ) , … , A t t e n t i o n ( x 1 : L , x L ) ] [Attention(x_{1:L},x_{1}),…,Attention(x_{1:L},x_{L})] [Attention(x1:L,x1),,Attention(x1:L,xL)]

自注意力使得所有的标记都可以“相互通信”,而前馈层提供进一步的连接:

def F e e d F o r w a r d ( x 1 : L : R d × L ) → R d × L FeedForward(x_{1:L}:ℝ^{d×L})→ℝ^{d×L} FeedForward(x1:L:Rd×L)Rd×L

  • 独立处理每个标记。
  • 对于 i = 1 , … , L i=1,…,L i=1,,L
    • 计算 y i = W 2 m a x ( W 1 x i + b 1 , 0 ) + b 2 y_{i}=W_{2}max(W_{1}x_{i}+b_{1},0)+b_{2} yi=W2max(W1xi+b1,0)+b2
  • 返回 [ y 1 , … , y L ] [y_{1},…,y_{L}] [y1,,yL]

对于Transformer的主要的组件,我们差不多进行介绍。原则上,我们可以只需将 F e e d F o r w a r d ∘ S e l f A t t e n t i o n FeedForward∘SelfAttention FeedForwardSelfAttention序列模型迭代96次以构建GPT-3,但是那样的网络很难优化(同样受到沿深度方向的梯度消失问题的困扰)。因此,我们必须进行两个手段,以确保网络可训练。

7.3.4.3.2 残差连接和归一化

残差连接:计算机视觉中的一个技巧是残差连接(ResNet)。我们不仅应用某个函数f:
f ( x 1 : L ) , f(x1:L), f(x1:L)
而是添加一个残差(跳跃)连接,以便如果 f f f的梯度消失,梯度仍然可以通过$x_{1:L}进行计算
x 1 : L + f ( x 1 : L ) 。 x_{1:L}+f(x_{1:L})。 x1:L+f(x1:L)
层归一化:另一个技巧是层归一化,它接收一个向量并确保其元素不会太大:normalization

def L a y e r N o r m ( x 1 : L : R d × L ) → R d × L LayerNorm(x_{1:L}:ℝ^{d×L})→ℝ^{d×L} LayerNorm(x1:L:Rd×L)Rd×L

  • 使得每个 x i x_{i} xi既不太大也不太小。

我们首先定义一个适配器函数,该函数接受一个序列模型 f f f并使其“鲁棒”:

def A d d N o r m ( f : ( R d × L → R d × L ) , x 1 : L : R d × L ) → R d × L AddNorm(f:(ℝd^{×L}→ℝ^{d×L}),x_{1:L}:ℝ_{d×L})→ℝ^{d×L} AddNorm(f:(Rd×LRd×L),x1:L:Rd×L)Rd×L

  • 安全地将f应用于 x 1 : L x_{1:L} x1:L
  • 返回 L a y e r N o r m ( x 1 : L + f ( x 1 : L ) ) LayerNorm(x_{1:L}+f(x_{1:L})) LayerNorm(x1:L+f(x1:L))

最后,我们可以简洁地定义Transformer块如下:

def T r a n s f o r m e r B l o c k ( x 1 : L : R d × L ) → R d × L TransformerBlock(x_{1:L}:ℝ^{d×L})→ℝ^{d×L} TransformerBlock(x1:L:Rd×L)Rd×L

  • 处理上下文中的每个元素 x i x_{i} xi
  • 返回 A d d N o r m ( F e e d F o r w a r d , A d d N o r m ( S e l f A t t e n t i o n , x 1 : L ) ) AddNorm(FeedForward,AddNorm(SelfAttention,x_{1:L})) AddNorm(FeedForward,AddNorm(SelfAttention,x1:L))
7.3.4.3.3 位置嵌入

最后我们对目前语言模型的位置嵌入进行讨论。您可能已经注意到,根据定义,标记的嵌入不依赖于其在序列中的位置,因此两个句子中的𝗆𝗈𝗎𝗌𝖾将具有相同的嵌入,从而在句子位置的角度忽略了上下文的信息,这是不合理的。

为了解决这个问题,我们将位置信息添加到嵌入中:

def $EmbedTokenWithPosition(x_{1:L}:ℝ^{d×L})$:

- 添加位置信息。
- 定义位置嵌入:
  - 偶数维度:$P_{i,2j}=sin(i/10000^{2j/dmodel})$
  - 奇数维度:$P_{i,2j+1}=cos(i/10000^{2j/dmodel})$
- 返回$[x_1+P_1,…,x_L+P_L]$。

上面的函数中, i i i表示句子中标记的位置, j j j表示该标记的向量表示维度位置。

最后我们来聊一下GPT-3。在所有组件就位后,我们现在可以简要地定义GPT-3架构,只需将Transformer块堆叠96次即可:
G P T − 3 ( x 1 : L ) = T r a n s f o r m e r B l o c k 96 ( E m b e d T o k e n W i t h P o s i t i o n ( x 1 : L ) ) GPT-3(x_{1:L})=TransformerBlock^{96}(EmbedTokenWithPosition(x_{1:L})) GPT3(x1:L)=TransformerBlock96(EmbedTokenWithPosition(x1:L))
架构的形状(如何分配1750亿个参数):

  • 隐藏状态的维度:dmodel=12288
  • 中间前馈层的维度:dff=4dmodel
  • 注意头的数量:nheads=96
  • 上下文长度:L=2048

这些决策未必是最优的。Levine等人(2020)提供了一些理论上的证明,表明GPT-3的深度太深,这促使了更深但更宽的Jurassic架构的训练。

不同版本的Transformer之间存在重要但详细的差异:

  • 层归一化“后归一化”(原始Transformer论文)与“先归一化”(GPT-2),这影响了训练的稳定性(Davis等人,2021)。
  • 应用了丢弃(Dropout)以防止过拟合。
  • GPT-3使用了sparse Transformer稀释 Transformer)来减少参数数量,并与稠密层交错使用。
  • 根据Transformer的类型(Encdoer-Only, Decoder-Only, Encdoer-Decoder),使用不同的掩码操作。

延伸阅读

分词:

模型架构:

Decoder-only 架构:

  • Language Models are Unsupervised Multitask Learners. Alec Radford, Jeff Wu, R. Child, D. Luan, Dario Amodei, Ilya Sutskever. 2019. Introduces GPT-2 from OpenAI.
  • Language Models are Few-Shot Learners. Tom B. Brown, Benjamin Mann, Nick Ryder, Melanie Subbiah, J. Kaplan, Prafulla Dhariwal, Arvind Neelakantan, Pranav Shyam, Girish Sastry, Amanda Askell, Sandhini Agarwal, Ariel Herbert-Voss, Gretchen Krueger, T. Henighan, R. Child, A. Ramesh, Daniel M. Ziegler, Jeff Wu, Clemens Winter, Christopher Hesse, Mark Chen, Eric Sigler, Mateusz Litwin, Scott Gray, Benjamin Chess, Jack Clark, Christopher Berner, Sam McCandlish, Alec Radford, Ilya Sutskever, Dario Amodei. NeurIPS 2020. Introduces GPT-3 from OpenAI.
  • Scaling Language Models: Methods, Analysis&Insights from Training Gopher. Jack W. Rae, Sebastian Borgeaud, Trevor Cai, Katie Millican, Jordan Hoffmann, Francis Song, J. Aslanides, Sarah Henderson, Roman Ring, Susannah Young, Eliza Rutherford, Tom Hennigan, Jacob Menick, Albin Cassirer, Richard Powell, G. V. D. Driessche, Lisa Anne Hendricks, Maribeth Rauh, Po-Sen Huang, Amelia Glaese, Johannes Welbl, Sumanth Dathathri, Saffron Huang, Jonathan Uesato, John F. J. Mellor, I. Higgins, Antonia Creswell, Nathan McAleese, Amy Wu, Erich Elsen, Siddhant M. Jayakumar, Elena Buchatskaya, D. Budden, Esme Sutherland, K. Simonyan, Michela Paganini, L. Sifre, Lena Martens, Xiang Lorraine Li, A. Kuncoro, Aida Nematzadeh, E. Gribovskaya, Domenic Donato, Angeliki Lazaridou, A. Mensch, J. Lespiau, Maria Tsimpoukelli, N. Grigorev, Doug Fritz, Thibault Sottiaux, Mantas Pajarskas, Tobias Pohlen, Zhitao Gong, Daniel Toyama, Cyprien de Masson d’Autume, Yujia Li, Tayfun Terzi, Vladimir Mikulik, I. Babuschkin, Aidan Clark, Diego de Las Casas, Aurelia Guy, Chris Jones, James Bradbury, Matthew Johnson, Blake A. Hechtman, Laura Weidinger, Iason Gabriel, William S. Isaac, Edward Lockhart, Simon Osindero, Laura Rimell, Chris Dyer, Oriol Vinyals, Kareem W. Ayoub, Jeff Stanway, L. Bennett, D. Hassabis, K. Kavukcuoglu, Geoffrey Irving. 2021. Introduces Gopher from DeepMind.
  • Jurassic-1: Technical details and evaluation. Opher Lieber, Or Sharir, Barak Lenz, Yoav Shoham. 2021. Introduces Jurassic from AI21 Labs.

Encoder-only 架构:

Encoder-decoder 架构:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值