预训练语言模型
预训练语言模型
1.1Encoder-only PLM
Transformer结构主要由Encoder、Decoder组成,根据特点引入了ELMo的预训练思路。
ELMo(Embeddings from Language Models)是一种深度上下文化词表示方法,
- 该模型由一个**前向语言模型(预测句子中下一个词)和一个后向语言模型(预测句子中前一个词)**组成。
- 不同于传统的词嵌入方式(如Word2Vec或GloVe),ELMo不是为每个词提供一个固定不变的向量表示,而是根据词在其出现的具体上下文中动态地生成表示。ELMo模型的不同层能够捕捉到不同层次的语言特征,比如语法信息在较低层,语义信息则更多地体现在高层。
- 首先,ELMo会在一个大型语料库上进行无监督的预训练,以学习丰富的语言结构和模式。然后,在针对具体的下游任务时,可以通过微调的方式进一步优化模型参数,使其更好地适应特定任务的需求。
1.1.1 BERT
BERT使用Transformer架构,抛弃RNN而使用注意力机制,并在此基础上进行优化,通过将Encoder结构进行堆叠,扩大模型参数。
预训练+微调,ELMo的诞生标志着预训练+微调范式的诞生,ELMo基于双向LSTM架构
**双向LSTM:**是一种特殊的递归神经网络RNN变体,通过同时考虑时间序列数据的前后信息来增强模型的表现,传统的LSTM只能从一个方向处理序列数据(从前向后),这意味着它们在任一时刻的预测只能基于该时刻之前的信息。但在实际情况下,理解某个词的意思往往需要同时参考其给出的上下文信息。
尽管双向LSTM能有效地捕捉序列中的长期依赖关系,并且在很多任务上表现出色,但它仍存在一些局限性,比如训练速度较慢、难以并行化处理等。相比之下,Transformer架构通过自注意力机制(self-attention mechanism)能够高效地并行处理序列数据,同时也能捕捉到长距离依赖关系。
模型架构——Encoder Only
BERT 的模型架构是取了 Transformer 的 Encoder 部分堆叠而成,BERT模型的输入一般为文本序列,输出为该序列所包含的内容含义的标签(积极、消极)。与Transformer一样,使用Encoder堆叠的BERT模型也是一个Seq2Seq模型,只是没有Decoder。因此,为适配各种 NLU 任务,在模型的最顶层加入了一个分类头 prediction_heads,用于将多维度的隐藏状态通过线性层转换到分类维度(例如,如果一共有两个类别,prediction_heads 输出的就是两维向量)。
上图分为三列,第一列是模型的大概结构,对于输入的文本序列,首先会被tokenizer分词器进行分词,然后进入Embedding层进行向量化hidden_states,转化为模型可以识别的内容,然后进行Encoder层,
- 每个Encoder Layer都由多头自注意力机制(Multi-Head Self-Attention Mechanism)和前馈神经网络(Feed-Forward Neural Network)组成,它们共同作用以捕捉输入序列的深层次特征
经过Encoder编码后,进入prediction_heads后得到最后的类别概率,在经过Softmax计算出预测的类别。
prediction_heads 其实就是线性层加上激活函数,一般而言,最后一个线性层的输出维度和任务的类别数相等
每种类型的预测头部都是为了适应特定的任务需求而设计的,它们能够有效地利用BERT生成的深度语义信息,从而提升在各种自然语言处理任务上的性能表现。
BERT 所使用的激活函数是 GELU 函数,全名为高斯误差线性单元激活函数,这也是自 BERT 才开始被普遍关注的激活函数。GELU 的核心思路为将随机正则的思想引入激活函数,通过输入自身的概率分布,来决定抛弃还是保留自身的神经元。
激活函数
1. 在神经网络中,如果没有激活函数,每一层的输出都是上一层输入的线性组合。这意味着无论网络有多少层,最终的效果都可以通过一个单层的模型来实现(没有隐藏层的情况),这大大限制了模型的能力。激活函数通过向网络中引入非线性因素,使得模型能够学习和表示更复杂的数据模式。2. 激活函数根据输入信号的强度决定神经元是否应该被“激活”,即产生输出信号传递给下一层。例如,ReLU (Rectified Linear Unit) 激活函数会将所有负值变为0,而保留正值不变,**这样可以有效地过滤掉那些被认为不重要的信息。** 3. 激活函数的选择会影响这些梯度的计算方式,从而影响训练效率和效果。4. **归一化输出**
**BERT 的注意力计算过程:**观察上图,
- 首先对于输入序列的每个token,BERT会创建一个向量表示,然后对于这个向量,会被三个独立的线性变化转换为Q、K、V,准备开始计算注意力。
- 使用公式 Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dkQKT)V计算注意力分数。
- BERT采用的是多头注意力机制,上述的计算过程会在不同的子空间内并行 计算多次,最终拼接在一起,并通过另一个线性层整合这些信息。
- 然后就是残差链接(加上原始输出)和归一化。
- 最后进入一个前馈神经网络(包含线性变换和激活函数)
BERT与Transformer的注意力计算过程,论文链接:BERT
差异点 | Transformer | BERT |
---|---|---|
注意力类型 | 自注意力 + 编码器-解码器注意力 | 仅自注意力(双向) |
注意力掩码 | 解码器未来遮蔽 + 编码器填充掩码 | MLM任务中的遮蔽词处理 |
参数共享 | 通常不共享 | ALBERT等变体可能共享 |
位置编码 | 固定正弦编码 | 可学习的位置嵌入 |
注意力头目标 | 任务相关(如翻译对齐) | 语言内部关系(语法/语义) |
并行性 | 解码器受限 | 完全并行 |
预训练任务——MLM+NSP
这一部分宋博大佬描述的非常棒,下面直接引用原文(包含部分的整理总结)happy-llm/docs/chapter3/第三章 预训练语言模型.md at main · datawhalechina/happy-llm
相较于基本沿承 Transformer 的模型架构,BERT 更大的创新点在于其提出的两个新的预训练任务上——MLM 和 NSP(Next Sentence Prediction,下一句预测)。预训练-微调范式的核心优势在于,通过将预训练和微调分离,完成一次预训练的模型可以仅通过微调应用在几乎所有下游任务上,只要微调的成本较低,即使预训练成本是之前的数倍甚至数十倍,模型仍然有更大的应用价值 。因此,可以进一步扩大模型参数和预训练数据量,使用海量的预训练语料来让模型拟合潜在语义与底层知识,从而让模型通过长时间、大规模的预训练获得强大的语言理解和生成能力。
因此,预训练数据的核心要求即是需要极大的数据规模(数亿 token)。毫无疑问,通过人工标注产出的全监督数据很难达到这个规模。因此,预训练数据一定是从无监督的语料中获取。这也是为什么传统的预训练任务都是 LM 的原因——LM 使用上文预测下文的方式可以直接应用到任何文本中,对于任意文本,我们只需要将下文遮蔽将上文输入模型要求其预测就可以实现 LM 训练,因此互联网上所有文本语料都可以被用于预训练。
但是,LM 预训练任务的一大缺陷在于,其直接拟合从左到右的语义关系,但忽略了双向的语义关系 。虽然 Transformer 中通过位置编码表征了文本序列中的位置信息,但这和直接拟合双向语义关系还是有本质区别。例如,BiLSTM(双向 LSTM 模型 )在语义表征上就往往优于 LSTM 模型,就是因为 BiLSTM 通过双向的 LSTM 拟合了双向语义关系。
MLM
基于这一思想,Jacob 等学者提出了 MLM,也就是掩码语言模型作为新的预训练任务 。相较于模拟人类写作的 LM,MLM 模拟的是“完形填空”。MLM 的思路也很简单,在一个文本序列中随机遮蔽部分 token,然后将所有未被遮蔽的 token 输入模型,要求模型根据输入预测被遮蔽的 token。 例如,输入和输出可以是:
输入:I <MASK> you because you are <MASK>
输出:<MASK> - love; <MASK> - wonderful
由于模型可以利用被遮蔽的 token 的上文和下文一起理解语义来预测被遮蔽的 token,因此通过这样的任务,模型可以拟合双向语义,也就能够更好地实现文本的理解。同样,**MLM 任务无需对文本进行任何人为的标注,只需要对文本进行随机遮蔽即可,因此也可以利用互联网所有文本语料实现预训练。**例如,BERT 的预训练就使用了足足 3300M 单词的语料。
不过,MLM 也存在其固有缺陷。在下游任务微调和推理时,其实是不存在我们人工加入的 <MASK>
的,我们会直接通过原文本得到对应的隐藏状态再根据下游任务进入分类器或其他组件。预训练和微调的不一致,会极大程度影响模型在下游任务微调的性能。
在具体进行 MLM 训练时,会随机选择训练语料中 15% 的 token 用于遮蔽。但是这 15% 的 token 并非全部被遮蔽为 <MASK>
,而是有 80% 的概率被遮蔽,10% 的概率被替换为任意一个 token,还有 10% 的概率保持不变。
其中 10% 保持不变就是为了消除预训练和微调的不一致,而 10% 的随机替换核心意义在于迫使模型保持对上下文信息的学习。因为如果全部遮蔽的话,模型仅需要处理被遮蔽的位置,从而仅学习要预测的 token 而丢失了对上下文的学习。通过引入部分随机 token,模型无法确定需要预测的 token,从而被迫保持每一个 token 的上下文表征分布,从而具备了对句子的特征表示能力。且由于随机 token 的概率很低,其并不会影响模型实质的语言理解能力。
NSP
下一个句子预测。NSP 的核心思想是针对句级的 NLU 任务,例如问答匹配、自然语言推理等。
- 问答匹配是指,输入一个问题和若干个回答,要求模型找出问题的真正回答;
- 自然语言推理是指,输入一个前提和一个推理,判断推理是否是符合前提的。
输入:Sentence A:I love you.Sentence B: Because you are wonderful.
输出:1(是连续上下文)输入:Sentence A:I love you.Sentence B: Because today's dinner is so nice.
输出:0(不是连续上下文)
通过要求模型判断句对关系,从而迫使模型拟合句子之间的关系,来适配句级的 NLU 任务。同样,由于 NSP 的正样本可以从无监督语料中随机抽取任意连续的句子,而负样本可以对句子打乱后随机抽取(只需要保证不要抽取到原本就连续的句子就行),因此也可以具有几乎无限量的训练数据。
总结一下,BERT的核心思想是预训练+微调。
- 在预训练阶段的目标是,在大量的无监督预料(网上随便找的数据),训练一个通用的语言模型,使用MLM和NSP这两个任务进行训练,让模型学会语言的结构和含义。
- 在微调阶段,在少量有标签的数据上重新训练一下模型参数,目标是让模型将已经学会的知识运用到具体任务上,比如分类、问答等。
BERT模型是如何适配各种任务的呢
-
使用特殊的token的作用
-
在输入句子的最前面加上一个特殊的token叫做
-
经过Transformer编码后,这个位置的输出向量就代表整个句子的整体语义
-
比如在分类任务中,我们把这个向量输入一个分类器,就能判断这句话是正面还是负面。
-
输入:[CLS] 我今天很开心 [SEP] 输出:[CLS] 的向量 → 分类为 “正面”
-
-
输入格式统一化
- 所有的任务都采用类似的输入格式,包括
<CLS>
:表示开始<SEP>
:分隔句子(用于句子对的任务)
- 比如:
- 单句分类任务:
[CLS] 句子内容 [SEP]
- 句子对任务(如自然语言推理):
[CLS] 第一句 [SEP] 第二句 [SEP]
- 单句分类任务:
- 这样设计的好处是:不管是什么任务,都可以复用同一个 BERT 模型结构。
- 所有的任务都采用类似的输入格式,包括
-
微调 ≠ 从头训练,而是“轻度更新”
- 微调时,不改变 BERT 主体结构,只是:
- 使用少量标注数据
- 调整学习率较小
- 更新模型参数的幅度比较小
- 类似于“复习+微调”,而不是“重学一遍”。
- 微调时,不改变 BERT 主体结构,只是:
1.1.2 RoBERTa
对于每一个任务,都存在训练数据和模型拟合的问题,大参数量机的模型需要大量的训练数据才能拟合,这是传统的方法,但在预训练微调方法下,只需要一次训练模型,之后对于每一个任务,使用小规模数据集进行微调即可。
RoBERTa的出现是为了解决:大量的数据集的训练,能否使得参数量与训练集参数量不相符的模型有更好的性能提升。即更大的训练数据量和更长的训练时间能显著提升模型性能,即使模型架构本身(参数量)保持不变。
下面是RoBERTa如何解决的:
- 去掉NSP预训练任务
- 首先你需要知道,NSP是BERT中的Next Sentence Prediction,看英文你应该明白是干什么的。在预训练的时候,会提供给模型两个句子,一个是真实的文档的下一句,另一个是随机选出来的下一句,任务目标是判断这俩句子中,哪一个更符合上文,加上去文字更加连续。
- 但是,模型可能会直接判断两个句子的主题是否相关,从而作弊!仅仅使用单个长文本序列进行训练,让模型学习和理解句子的深层含义,更加有利于模型性能提升。
- 更大规模的预训练数据和预训练步长
- 一个“步长”指模型处理一个批次(batch)数据并更新一次参数。一个“epoch”指模型完整遍历一次整个训练数据集。总计算量 ≈ 数据量 * 训练步长 / 批次大小。
- 比如RERT base训练通常是100万步,等价于40个epochs在原始的数据集上。RoBERTa训练更加长的步长,在更加大的数据集上epochhs数相对减少,但总训练量更大。
- RoBERTa使用了更加大的数据集,验证更长的训练步长让模型有更多机会学习和优化其参数,充分挖掘模型架构的潜力。
- RoBERTa在数据预处理阶段采用了动态掩码(这是对BERT的一个重要优化,常被归入此点)。BERT在数据预处理时对每个训练样本静态地生成一次掩码模式,在整个训练周期中重复使用相同的掩码。RoBERTa改为在每次将训练样本输入模型时动态生成新的掩码模式。这相当于在相同数据上,模型看到了更多样化的掩码实例,
- 更大的bpe词表
- BPE byte-pair encoding 是一种子词切分算法,可以将单词拆分为更加常见的子单元。
- 首先你要明白,RoBERTa没有简单的增大词表的大小,他的词表大小和BERT其实是一样的,但分词的方法不一样了!
- 为什么这样呢,因为BERT的WordPiece分词器是基于Unicode字符的,在处理特殊字符、表情符号、拼写错误或罕见词时可能不够灵活或效率不高。
- Byte-level BPE 直接在字节(Byte) 级别进行操作,将基础字符定义为字节(0-255),然后应用标准的BPE算法。这使得词表构建完全基于数据驱动,不依赖于预定义的字符集,对多语言和特殊文本处理能力更强。
1.1.3 ALBERT
这是另外一种对BERT的优化模型,提出了降低模型的参数,以及新预训练任务SOP。他主要目的是,将大模型的参数量减少,但是不降低他的性能。
如何实现:
-
优化Embedding层
我们把词对应的向量进行压缩,等到模型真正的需要这个词的时候,再进行解压缩,比如说,原本词表里面有三万个词,每个词对应一个700多维度的一个向量,两者相乘进行计算,几乎有200万的参数。ALBERT的方法是,把这个维度降低,比如降低到128维,这样参数就小了非常多了,当模型需要这个词的信息的时候,把128升到原来的维度即可。
为什么这样可行呢?
Word2Vec早就证明,词本身的含义用100-300维的小向量就能表达得很好。768/2048维更多是给后面复杂的Transformer层做计算用的。ALBERT聪明地把这两个需求分开了。
-
跨层参数共享
BERT原本有24层的Encoder Layer,每层都有一套独立的参数,ALBERT只留下一层,但是,虽然参数量少了,计算量一点也没有少,还是要运行24遍,只是每一遍的参数都一样。
-
SOP任务
BERT用的NSP任务,新的模型将用SOP任务,让模型的考试题从北京卷换到了河南卷(●ˇ∀ˇ●)
至此,PLM就完了,他们主要是在训练方式上卷,或者是模型结构上卷,下一篇文章将会讲解最重要的T5