NLP与LLM

NLP(Natural Language Processing)

Pre Norm与Post Norm

    • Pre Norm(Norm and add)
    • Post Norm(Add and Norm)
  • 在同一训练设置下,同一设置之下,Pre Norm结构往往更容易训练,但最终效果通常不如Post Norm:

    • Pre Norm更容易训练好理解,因为它的恒等路径更突出
    • Pre Norm的深度有“水分”!也就是说,一个L层的Pre Norm模型,其实际等效层数不如L层的Post Norm模型,而层数少了导致效果变差了。Post Norm每Norm一次就削弱一次恒等分支的权重,所以Post Norm反而是更突出残差分支的,因此Post Norm中的层数更加“足秤”,一旦训练好之后效果更优。
  • Post Norm的结构迁移性能更加好,也就是说在Pretraining中,Pre Norm和Post Norm都能做到大致相同的结果,但是Post Norm的Finetune效果明显更好

BN与LN

  • BN 与 LN 定义:Batch Normalization 是对这批样本的同一维度特征做规范化处理, Layer Normalization 是对这单个样本的所有维度特征做规范化处理

    • 区别:

      • LN中同层神经元输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差;BN中则针对不同神经元输入计算均值和方差,同一个batch中的输入拥有相同的均值和方差
    • 为什么 NLP 使用 LN 而不是 BN?

      • LN 不依赖于 batch 的大小和输入 sequence 的长度,因此可以用于 batchsize 为 1 和 RNN 中 sequence 的 normalize 操作
      • BN 不适用于 batch 中 sequence 长度不一样的情况,有的靠后面的特征的均值和方差不能估算;另外 BN 在 MLP 中的应用对每个特征在 batch 维度求均值方差,比如身高、体重等特征,但是在 NLP 中对应的是每一个单词,但是每个单词表达的特征是不一样的
      • 如果特征依赖于不同样本间的统计参数(比如 CV 领域),那么 BN 更有效(它抹杀了不同特征之间的大小关系,但是保留了不同样本间的大小关系);NLP 领域 LN 更合适(抹杀了不同样本间的大小关系,但是保留了一个样本内不同特征之间的大小关系),因为对于 NLP 或序列任务来说,一条样本的不同特征其实就是时序上字符取值的变化,样本内的特征关系是非常紧密的
    • 相同点:标准化技术目的是让每一层的分布稳定下来,让后面的层可以在前面层的基础上安心学习,加快模型收敛

Word2Vec

word2vec是一种将word转为向量的方法,其包含两种算法,分别是skip-gram和CBOW,它们的最大区别是skip-gram是通过中心词去预测中心词周围的词,而CBOW是通过周围的词去预测中心词。

one-hot编码在大量数据情况下会出现维度灾难,会导致数据非常稀疏(0多1少),于是采用分布式表示(通过训练,将每个词都映射到一个较Pre的词向量上)维度中的数字已经不是1和0了,而是一些其他的浮点数。

这种将高维度的词表示转换为低维度的词表示的方法,我们称之为词嵌入(word embedding)

分布式表示的词向量含有词语上下文信息(例:King-Man+Woman=Queen),CBOW就是通过当前中心词的上下文单词信息预测当前中心词。

通过一次周围词矩阵即可获得目标向量,常用词向量维度D为300。

skim-gram模型如下

skipgram

在训练结束后,对于词典中任一索引为i的词,我们都会得到两组词向量,在自然语言处理应用中,一般使用skip-gram模型的中心词向量作为词的表征向量。

词嵌入:句子中每个单词以Onehot形式作为输入,乘以学好的Word Embedding矩阵Q,直接取出单词对应的Word Embedding

遗留下的问题:word2vec最大的缺陷是无法解决多义词问题,即如果将一个词对应的向量定下来后,即使出现其他语义情况也无法再改变。

RNN(Recurrent Neural Network)

RNN:时序神经网络,拥有时间上的顺序性。能结合上下文考虑,弥补了全连接神经网络的不足。

遗留下的问题:会存在梯度消失,即模型难以学习到远距离的依赖关系。RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。

LSTM

相比于原始的RNN的隐层(hidden state), LSTM增加了一个细胞状态(cell state)。

遗留下的问题:表面上似乎没有什么问题了,但其实这个结合的方法是死板的,只结合前文而没有结合后文,依旧会出现考虑不全面的问题。

Seq2Seq(Attention)

RNN结构大多对序列的长度比较局限,对于类似于机器翻译的任务,输入和输出长度并不对等,为N to M的结构,简单的RNN束手无策,因此便有了新的模型,Encoder-Decoder模型,也就是Seq2Seq模型。

核心问题是当序列过长时,上述的Decoder输出的上下文向量 c 无法记住所有信息,会出现长序列梯度消失的问题。比如句子有100个词,那么c里面可能丢失了前几个词的信息。

Attention机制的Decoder结构如下:

首先计算上一个神经元隐藏状态h与Encoder每一个神经元隐藏状态的相似度,计算使用某种相似度计算函数。
“集中注意力”。对以上相似度使用softmax函数,得到Encoder每个隐藏状态在处理第 t 个词的时候的“注意力” α。计算得到各个上下文向量:
$$
c_t = \sum_{i=1}^N\alpha_{ti}h_i
$$
后面Decoder对 y 的计算与Decoder(将前一个神经单元的输出作为当前神经单元的输出。)的计算方式几乎一致,区别就在于上下文向量 c 的变化。

自注意力机制:

假设当前有输入信息 H=[ℎ1,ℎ2,…,ℎn] ,我们需要使用自注意力机制获取每个位置的输出context=[context1,context2,…,contextn] 。

首先,需要将原始输入映射到查询空间 Q、键空间 K 和值空间 V,相关计算公式如下:

Q=HW_q =[q_1,q_2,…,q_n] \ K=HW_k =[k_1,k_2,…,k_n]\ V=HW_v =[v_1,v_2,…,v_n]

接下来,我们将去计算每个位置的注意力分布,并且将相应结果进行加权求和:

其中 s(q_i,k_j) 是经过上述点积、缩放后分数值。

最后,为了加快计算效率,这里其实可以使用矩阵计算的方式,一次性计算出所有位置的的Attention输出向量:

Transformer

2017 年,Google 在论文 Attention is All you need 中提出了 Transformer 模型,其使用 Self-Attention 结构取代了在 NLP 任务中常用的 RNN 网络结构。Transformer 本质上是一个 Encoder-Decoder 架构。

编码组件由多层编码器(Encoder)组成(在论文中作者使用了 6 层编码器)。解码组件也是由相同层数的解码器(Decoder)组成(在论文也使用了 6 层)。

每个编码器由两个子层组成:Self-Attention 层(自注意力层)和 Position-wise Feed Forward Network(前馈网络,缩写为 FFN)。

解码器也有编码器中这两层,但是它们之间还有一个注意力层(即 Encoder-Decoder Attention),其用来帮忙解码器关注输入句子的相关部分(类似于 seq2seq 模型中的注意力)。

self-attention机制:scaled dot-product attention (缩放点积注意力)

多头注意力机制(Multi-head Attention)

首先,通过 h hh 个不同的线性变换对 Query、Key 和 Value 进行映射;然后,将不同的 Attention 拼接起来;最后,再进行一次线性变换。每一组注意力用于将输入映射到不同的子表示空间,这使得模型可以在不同子表示空间中关注不同的位置。

编码器结构中有一个需要注意的细节:每个编码器的每个子层(Self-Attention 层和 FFN 层)都有一个残差连接,再执行一个层标准化操作

**Mask **表示掩码,它对某些值进行掩盖,使其在参数更新时不产生效果。Transformer 模型里面涉及两种 mask,分别是 Padding Mask 和 Sequence Mask。其中,Padding Mask 在所有的 scaled dot-product attention 里面都需要用到,而 Sequence Mask 只有在 Decoder 的 Self-Attention 里面用到。

其中,Padding Mask,因为每个批次输入序列的长度是不一样的,所以我们要对输入序列进行对齐。具体来说,就是在较短的序列后面填充 0(但是如果输入的序列太长,则是截断,把多余的直接舍弃)。

具体的做法:把这些位置的值加上一个非常大的负数(负无穷),这样的话,经过 Softmax 后,这些位置的概率就会接近 0。

Sequence Mask 是为了使得 Decoder 不能看见未来的信息。也就是对于一个序列,在 t 时刻,我们的解码输出应该只能依赖于 t 时刻之前的输出,而不能依赖 t 之后的输出。因为我们需要想一个办法,把 t 之后的信息给隐藏起来。

具体的做法:产生一个上三角矩阵,上三角的值全为 0。把这个矩阵作用在每个序列上,就可以达到我们的目的。

解码器栈的输出是一个 float 向量。我们怎么把这个向量转换为一个词呢?通过一个线性层再加上一个 Softmax 层实现。线性层是一个简单的全连接神经网络,其将解码器栈的输出向量映射到一个更长的向量,这个向量被称为 logits 向量。现在假设我们的模型有 10000 个英文单词(模型的输出词汇表)。因此 logits 向量有 10000 个数字,每个数表示一个单词的分数。然后,Softmax 层会把这些分数转换为概率(把所有的分数转换为正数,并且加起来等于 1)。最后选择最高概率所对应的单词,作为这个时间步的输出。

在 Transformer 论文,提到一个细节:编码组件和解码组件中的嵌入层,以及最后的线性层共享权重矩阵。不过,在嵌入层中,会将这个共享权重矩阵乘以根号下d_model。

BERT

BERT的全称为Bidirectional Encoder Representation from Transformers,是一个预训练的语言表征模型。它强调了不再像以往一样采用传统的单向语言模型或者把两个单向语言模型进行浅层拼接的方法进行预训练,而是采用新的masked language model(MLM),以致能生成深度的双向语言表征。

该模型有以下主要优点:

1)采用MLM对双向的Transformers进行预训练,以生成深层的双向语言表征。

2)预训练后,只需要添加一个额外的输出层进行fine-tune,就可以在各种各样的下游任务中取得state-of-the-art的表现。在这过程中并不需要对BERT进行任务特定的结构修改。

以往的预训练模型的结构会受到单向语言模型(从左到右或者从右到左)的限制,因而也限制了模型的表征能力,使其只能获取单方向的上下文信息。而BERT利用MLM进行预训练并且采用深层的双向Transformer组件(单向的Transformer一般被称为Transformer decoder,其每一个token(符号)只会attend到目前往左的token。而双向的Transformer则被称为Transformer encoder,其每一个token会attend到所有的token。)来构建整个模型,因此最终生成能融合左右上下文信息的深层双向语言表征。

作者还在输入的每一个序列开头都插入特定的分类token([CLS]),该分类token对应的最后一个Transformer层输出被用来起到聚集整个序列表征信息的作用。

由于BERT是一个预训练模型,其必须要适应各种各样的自然语言任务,因此模型所输入的序列必须有能力包含一句话(文本情感分类,序列标注任务)或者两句话以上(文本摘要,自然语言推断,问答任务)。那么如何令模型有能力去分辨哪个范围是属于句子A,哪个范围是属于句子B呢?BERT采用了两种方法去解决:

1)在序列tokens中把分割token([SEP])插入到每个句子后,以分开不同的句子tokens。

2)为每一个token表征都添加一个可学习的分割embedding来指示其属于句子A还是句子B。

BERT的输入为每一个token对应的表征,实际上该表征是由三部分组成的,分别是对应的token分割位置 embeddings

C为分类token([CLS])对应最后一个Transformer的输出,T_i则代表其他token对应最后一个Transformer的输出。对于一些token级别的任务(如,序列标注和问答任务),就把T_i 输入到额外的输出层中进行预测。对于一些句子级别的任务(如,自然语言推断和情感分类任务),就把C输入到额外的输出层中,这里也就解释了为什么要在每一个token序列前都要插入特定的分类token。

BERT预训练任务

虽然NLP领域没有像ImageNet这样质量高的人工标注数据,但是可以利用大规模文本数据的自监督性质来构建预训练任务。因此BERT构建了两个预训练任务,分别是Masked Language ModelNext Sentence Prediction

MLM

MLM是BERT能够不受单向语言模型所限制的原因。简单来说就是以15%的概率用mask token ([MASK])随机地对每一个训练序列中的token进行替换,然后预测出[MASK]位置原有的单词。然而,由于[MASK]并不会出现在下游任务的微调(fine-tuning)阶段,因此预训练阶段和微调阶段之间产生了不匹配(这里很好解释,就是预训练的目标会令产生的语言表征对[MASK]敏感,但是却对其他token不敏感)。因此BERT采用了以下策略来解决这个问题:

首先在每一个训练序列中以15%的概率随机地选中某个token位置用于预测,假如是第i个token被选中,则会被替换成以下三个token之一

1)80%的时候是[MASK]。如,my dog is hairy——>my dog is [MASK]

2)10%的时候是随机的其他token。如,my dog is hairy——>my dog is apple

3)10%的时候是原来的token(保持不变,个人认为是作为2)所对应的负类)。如,my dog is hairy——>my dog is hairy

NSP

一些如问答、自然语言推断等任务需要理解两个句子之间的关系,而MLM任务倾向于抽取token层次的表征,因此不能直接获取句子层次的表征。为了使模型能够有能力理解句子间的关系,BERT使用了NSP任务来预训练,简单来说就是预测两个句子是否连在一起。具体的做法是:对于每一个训练样例,我们在语料库中挑选出句子A和句子B来组成,50%的时候句子B就是句子A的下一句(标注为IsNext),剩下50%的时候句子B是语料库中的随机句子(标注为NotNext)。接下来把训练样例输入到BERT模型中,用[CLS]对应的C信息去进行二分类的预测。

BERT预训练过程

Bert的预训练主要包含两个任务,MLM和NSP,Masked Language Model任务可以理解为完形填空,随机mask每一个句子中15%的词,用其上下文来做预测;Next Sentence Prediction任务选择一些句子对A与B,其中50%的数据B是A的下一条句子,剩余50%的数据B是语料库中随机选择的,学习其中的相关性。BERT 预训练阶段实际上是将上述两个任务结合起来,同时进行,然后将所有的 Loss 相加

LLM(Large Language Model)

GPT

利用无标注文本进行学习仍存在两个待解决的问题:1.目前尚不清楚哪种类型的优化目标在学习文本表征方面最有效。当下有很多研究在不同的任务中用了不同的优化目标而达到最优;2.如何将学习到的文本表征用于下游任务也不明确。现有的方法一般是针对下游任务更改模型结构等;

本文提出一种半监督的方法GPT,用于自然语言理解任务,GPT的整体流程结合了无监督的预训练和有监督的微调。目标旨在学习到一种通用的表征能够以极小的代价用于各种下游任务。本质上GPT1是基于Transformer的Decoder(变体)开发的。

训练过程整体上分为两步:1)在大规模文本数据上学习到一个高容量的语言模型;2)在标注数据上进行微调。

Decoder的修改(相较于Transformer decoder):

1.Transformer Decoder:Masked multi-head self-attention + encoder-decoder multi-head self-attention +feed forward

2.GPT Decoder:Masked multi-head self-attention+ feed forward

一个通用的语言模型应该能够处理任何字符,但是现有的语言模型通过包含各种预处理操作:lower-casing,tokenization,预设词汇表等。这些操作都会限制语言模型能够处理的字符范围。GPT-2综合考虑了OOV问题和词表过大的问题,使用了BPE算法,也能够使模型处理任何字符。