fgg blog

BERT模型论文精读笔记

Table of Contents

(李沐-BERT论文精读的个人整理笔记)

BERT论文标题: Pre-training预训练 of Deep Bidirectional Transformers深度双向Transformer for Language Understanding语言理解

预训练(pre-training):先在一个大的数据集上训练一个模型(从零开始得到一组权重值W0),这个模
型的主要任务是被用在其他任务(或下游任务上)进行训练(training:以W0初始化模型然后训练)
(以解决下游任务问题)。

BERT本身含义:Bidirectional Encoder Representations from Transformer,使用了 Transformers 模 型(Transformer论文精读)的编码编码器组件,学习一个双向的嵌入表示。与 ELMo 和 Generative Pre-trained Transformer 不同:

  • BERT 从无标注的文本中(jointly conditioning 联合左右的上下文信息)预训练词嵌入的双向表征。
  • pre-trained BERT 可以通过加一个输出层来 fine-tune,不需要对特定任务的做架构上的修改就 可以在在很多任务(问答、推理)有很不错的、state-of-the-art 的效果
  • GPT unidirectional,使用左边的上下文信息预测未来;BERT bidirectional,使用左右侧的上下文信息
  • ELMo based on RNNs, down-stream 任务需要调整架构
  • GPT, based on Transformers decoder, down-stream 任务只需要改最上层
  • BERT based on Transformers encoder, down-stream 任务只需要调整最上层

NLP 任务主要分两类:

  • sentence-level tasks 句子级别的任务——情绪识别、语句相似计算、语义检索等;
  • token-level tasks 词级别的人物——NER (人名、街道名) 需要 fine-grained output

标准语言模型是 unidirectional 单向的,基于神经网络的 ELMo 和 GPT 也都是单向的,就是从左 到右的架构,只能将输入的一个句子从左看到右,然后是预测下一个单词/词元。然而一些语言理解 任务,比如情感分类、QA,则从左看到右、从右看到左 都应该是合法的。如果能从两个方向看信息, 能提升模型解决这类任务的性能。

BERT 通过 MLM(Masked language model) 带掩码的语言模型 作为预训练的目标,来减轻标准语言模 型的单向约束:

MLM 带掩码的语言模型做什么呢? 每次随机选输入的词源 tokens, 然后 mask 它们,目标函数是预测被 masked 的词;类似挖空 填词、完形填空。

BERT 除了 MLM 还有什么? NSP: next sentence prediction 判断两个句子是随机采样的 or 原文相邻,学习 sentence-level 的信息

总的来说: ELMo 用了 bidirectional 信息,但架构是 RNN 比较老(无法并行计算); GPT 架构 Transformer 新(注意力机制大法好),但只用了 unidirectional 信息; BERT = ELMo 的 bidirectional 信息 + GPT 的新架构 transformer。 毕竟语言任务很多并不是预测未来,而是完形填空。BERT结合这俩,并证明双向有用。

# BERT 模型

BERT 有两个任务:预训练 + 微调

pre-training: 使用 unlabeled data 训练

fine-tuning: 采用 Bert 模型,但是权重都是预训练期间得到的

  • 所有权重在微调的时候都会参与训练,用的是有标记的数据、
  • 每一个下游任务都会常见一个 新的 BERT 模型,(由预训练参数初始化),但每一个下游任务会 根据自己任务的 labeled data 来微调自己的 BERT 模型

IMG_BERT

下游任务:创建同样的 BERT 的模型,权重的初始化值来自于 预训练好 的权重。

MNLI, NER, SQuAD 下游任务有 自己的 labeled data, 对 BERT 继续训练,得到各个下游任务自己 的的 BERT 版本

## 模型架构,主要调了三个参数:

  • L: transform blocks 的个数
  • H: hidden size 隐藏层大小
  • A: 自注意力机制 multi-head 中 head 头的个数

主要两个 size:

  • 调了 BERT_BASE (学习 1 亿参数,L=12, H=768, A=12)
  • BERT_LARGE(3.4 亿参数, L=24, H=1024, A=16)

## 模型输入输出:

  • WordPiece 分词(tokenization)
  • 输入序列构成:
    • [CLS], [SEP]:特殊词元
    • 序列开头永远是[CLS],self-attn保证每个token都能联系到其他所有token
    • 第一个句子后是[SEP],用于区分两个句子

IMG_BERT_IO

预训练整体过程:

  1. 每一个 token 进入 BERT 得到 这个 token 的 embedding 表示。
  2. 整体进 BERT 然后输出一个结果序列
    • 最后一个 transformer 块的输出,表示 这个词元 token 的 BERT 的表示。在后面再添加额 外的输出层,来得到特定任务想要的结果

IMG_input_emb

在预训练第一步中得到token的embedding的过程:

  • 给定一个词元(token)
  • BERT input representation = token 本身的表示 + segment 句子的表示 + position embedding 位置表示
    • token embedding:每一个 token 有对应的词向量
    • Segment:即到底是 A 句还是 B 句,通过学习得到
    • Position:是 token 在这个序列 sequence 中的位置信息。从 0 开始 1 2 3 4 -> N。其最 终值也是通过学习得到的(transformer 是给定的)
    • 加起来的话每个向量的信息就会组合在一起
  • 从【一个词元的序列】得到【一个向量的序列】,最终可以进入 transformer 块

## 动态掩码策略:

BERT 模型中的 MLM(Masked Language Model) 任务确实引入了一个问题:在预训练阶段,输入序 列中的 15% 的单词被替换为 [MASK],而在微调阶段,模型接收的是完整的自然语言句子,不再有 [MASK] 标记。因此,预训练和微调阶段模型看到的数据分布不一致,这可能会影响微调阶段模型的 表现。

为了解决这个问题,BERT 设计中采取了一些措施:

BERT 的设计者意识到了这个问题,因此在预训练的过程中引入了一种动态掩码策略。虽然 15% 的单 词被标记为需要预测的目标,但并不是所有这些单词都被替换为 [MASK],具体的处理方式是:

80% 的情况下:将目标单词替换为 [MASK]。
10% 的情况下:将目标单词替换为一个随机的其他单词。
10% 的情况下:保持目标单词不变。

这种设计的目的是让模型不仅仅习惯于看到 [MASK] 标记,还可以在预训练时学到更多关于真实单词 的上下文信息。通过这种混合策略,BERT 能够学会在存在 [MASK] 标记时预测缺失单词,同时也能 应对训练过程中未出现 [MASK] 的情况。

# 损失函数

在 BERT 的预训练阶段,主要有两个任务,它们的损失函数与这两个任务紧密相关:

  • MLM任务中,模型的目标是根据上下文预测被随机掩盖(15%)的单词。这个任务的损失函数是交叉 熵损失函数(cross-entropy loss),它用来衡量模型预测的单词与真实单词之间的差距。

  • NSP任务中,模型的目标是判断两个句子之间是否具有逻辑上的连续性,即模型要预测第二个句子 是否是第一个句子的下一个句子。NSP 任务同样使用交叉熵损失函数,用于计算模型预测的二分类 结果(连续/不连续)与真实标签之间的误差。

因此,预训练阶段的总损失函数是 MLM 和 NSP 损失函数的加权和,模型通过这个损失函数学习到上 下文的语言信息。

在微调阶段,BERT 不再使用 MLM 或 NSP 任务,而是根据具体的下游任务定义新的损失函数。

  • 文本分类任务(如情感分析): 微调阶段的文本分类任务通常基于 [CLS] token 的输出,使用这个向量来预测类别。此时的损 失函数依然是交叉熵损失函数,用于计算模型预测的类别分布与真实类别之间的差异。

  • 序列标注任务(如命名实体识别 NER): 在序列标注任务中,BERT 的每个 token 会被映射到一个标签,这类任务的损失函数也是交叉熵 损失,但它是针对每个 token 预测结果和实际标签之间的差异计算的。

  • 问答任务(如 SQuAD): 在问答任务中,BERT 会生成两个标量,一个表示答案的起始位置,另一个表示答案的结束位置。 对于这些位置预测,同样使用交叉熵损失函数。

因此,微调阶段的损失函数完全取决于具体任务的性质,通常会选择合适的损失函数来优化任务的最终目标,而不再使用 MLM 和 NSP 的损失。