BERT:双向编码器表示与预训练
一句话概述
BERT(Bidirectional Encoder Representations from Transformers)是Google于2018年提出的革命性预训练语言模型。它使用Transformer的编码器部分,通过两个创新的预训练任务——掩码语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)——在海量无标注文本上进行预训练,学习通用的语言表示。MLM任务随机遮盖输入中的15%的词,让模型根据上下文预测被遮盖的词,这迫使模型学习真正的双向上下文理解。预训练完成后,BERT可以通过微调(Fine-tuning)适配到各种下游NLP任务(文本分类、问答、命名实体识别等),在11项NLP基准上刷新了当时的最优结果。BERT的诞生标志着NLP进入了"预训练+微调"的范式时代,成为后续所有预训练模型(RoBERTa、ALBERT、ELECTRA等)的基石。
💡 核心要点:①BERT使用Transformer的编码器部分,实现真正的双向上下文理解 ②MLM任务随机遮盖15%的词,让模型预测被遮盖词,学习双向表示 ③NSP任务判断两个句子是否连续,学习句子间关系 ④BERT开启"预训练+微调"范式,在11项NLP任务上刷新SOTA
教学与演示
一、BERT的架构:双向Transformer编码器
是什么(定义):BERT的架构是Transformer编码器(Encoder-only)。BERT-base版本包含12层Transformer编码器(L=12),隐藏维度768(H=768),12个注意力头(A=12),总参数量1.1亿。BERT-large版本包含24层(L=24),隐藏维度1024(H=1024),16个注意力头(A=16),总参数量3.4亿。与GPT不同,BERT是双向的——每个位置可以同时看到左右两侧的上下文,这使得BERT在"理解"任务上优于单向的GPT。
大白话 BERT就像一个"完形填空专家"。它读文章时不是从左到右逐字读(像GPT那样),而是"一眼看全文"——看到每个词时,它同时参考了左边和右边的上下文。这就像中国人做英语完形填空:要填入一个词,不仅要看前面的句子,还要看后面的句子,才能准确判断。BERT的双向性让它特别擅长"理解"任务——比如判断两句话是否相似、提取文中的答案、判断文章的情感——这些都需要综合全文信息。
为什么(原理):BERT的双向性源于其使用Transformer编码器(而非解码器)。编码器的自注意力不需要掩码,每个位置可以无限制地关注所有其他位置。这解决了GPT等单向模型的根本局限——GPT只能利用上文信息,而BERT能同时利用上下文。但双向性也有代价:BERT不能直接用于文本生成(因为生成需要"因果性",不能看未来),因此BERT主要用于"理解"任务而非"生成"任务。
import numpy as np
# BERT的架构本质:双向Transformer编码器
# 对比GPT(单向)和BERT(双向)的注意力模式
class BERTEncoder:
"""BERT风格的编码器:无掩码,完全双向"""
def __init__(self, d_model=8, num_heads=2):
np.random.seed(42)
self.d_model = d_model
self.d_k = d_model // num_heads
self.num_heads = num_heads
# 自注意力权重(无掩码版本)
self.W_Q = np.random.randn(d_model, d_model) * 0.1
self.W_K = np.random.randn(d_model, d_model) * 0.1
self.W_V = np.random.randn(d_model, d_model) * 0.1
self.W_O = np.random.randn(d_model, d_model) * 0.1
def bidirectional_attention(self, X):
"""双向注意力:每个位置可以看到所有位置(BERT)"""
n = X.shape[0]
Q = X @ self.W_Q
K = X @ self.W_K
V = X @ self.W_V
# 关键:没有掩码!所有位置互相可见
scores = Q @ K.T / np.sqrt(self.d_k)
scores = scores - np.max(scores, axis=1, keepdims=True)
attn = np.exp(scores) / np.sum(np.exp(scores), axis=1, keepdims=True)
return attn
def causal_attention(self, X):
"""因果注意力:每个位置只能看到之前的位置(GPT)"""
n = X.shape[0]
Q = X @ self.W_Q
K = X @ self.W_K
V = X @ self.W_V
scores = Q @ K.T / np.sqrt(self.d_k)
# 关键:加上掩码,屏蔽未来位置
mask = np.triu(np.ones((n, n)) * -1e9, k=1)
scores = scores + mask
scores = scores - np.max(scores, axis=1, keepdims=True)
attn = np.exp(scores) / np.sum(np.exp(scores), axis=1, keepdims=True)
return attn
# 创建示例句子:"[CLS] 我 爱 北京 [SEP]"
words = ["[CLS]", "我", "爱", "北京", "[SEP]"]
X = np.random.randn(5, 8) * 0.5
bert = BERTEncoder(d_model=8, num_heads=2)
# BERT的双向注意力
bi_attn = bert.bidirectional_attention(X)
# GPT的因果注意力
causal_attn = bert.causal_attention(X)
print("=== BERT双向注意力 vs GPT因果注意力 ===\n")
print("BERT(双向):每个词可以看到所有其他词")
print("注意力矩阵:")
print(f"{'':>8}", end="")
for w in words:
print(f" {w:>6}", end="")
print()
for i, w1 in enumerate(words):
print(f"{w1:>8}", end="")
for j in range(len(words)):
print(f" {bi_attn[i][j]:.4f}", end="")
print()
print("\nGPT(因果/单向):每个词只能看到左侧的词")
print("注意力矩阵:")
print(f"{'':>8}", end="")
for w in words:
print(f" {w:>6}", end="")
print()
for i, w1 in enumerate(words):
print(f"{w1:>8}", end="")
for j in range(len(words)):
print(f" {causal_attn[i][j]:.4f}", end="")
print()
print("\n关键区别:")
print("- BERT:'北京'可以看到后面的'[SEP]'(双向)")
print("- GPT:'北京'看不到后面的'[SEP]'(单向,只能看到左侧)")
print("- 双向性使BERT在理解任务上更强,但无法用于生成")
大白话 BERT的输入是"三重信息叠加"。就像给每个词贴了三张标签:第一张是"我是谁"(词嵌入——这是什么词),第二张是"我在哪句话"(段嵌入——我是第一句还是第二句),第三张是"我排第几"(位置嵌入——我是第5个词)。三张标签叠在一起,形成一个"全息"的输入表示,然后送入12层Transformer进行深度理解。
什么用(应用):BERT的双向编码器使其特别擅长"理解"任务。在文本分类中,取[CLS]标记的最终输出做分类;在问答任务中,预测答案在原文中的起始和结束位置;在命名实体识别中,对每个词的输出做分类(人名、地名、组织名等);在语义相似度中,比较两个句子[CLS]向量的余弦距离。BERT发布后,几乎所有NLP任务都从"从零训练"转为"BERT微调",效果普遍大幅提升。
哪些坑(缺点):BERT不能直接用于文本生成(因为双向性违反了因果性)。BERT的输入长度限制为512个词(位置嵌入的最大长度),无法处理长文档。BERT的预训练成本高昂——BERT-base在16个TPU上训练了4天。此外,BERT模型体积大(1.1亿参数),推理速度慢,在移动端或实时场景下需要模型压缩。
二、预训练任务:MLM与NSP
是什么(定义):BERT通过两个预训练任务学习语言表示。①掩码语言模型(MLM):随机选择输入中15%的词,其中80%替换为[MASK]标记,10%替换为随机词,10%保持不变,然后让模型预测这些位置的原词。②下一句预测(NSP):输入两个句子A和B,50%的情况下B是A的下一句,50%的情况下B是随机句子,让模型判断B是否是A的下一句。
大白话 MLM就像"做填空题"。出题老师把文章中的15%的词涂黑(80%涂黑,10%换成别的词,10%保持原样),让你猜被涂黑的是什么词。为了猜对,你必须理解整篇文章的上下文。NSP就像"判断两段话是否连贯"——给你两段话,让你判断第二段是不是紧接第一段的。这训练了模型对句子间关系的理解能力。MLM教模型"理解词义",NSP教模型"理解句子关系"。
为什么(原理):MLM的设计有两个关键细节。为什么80/10/10的比例?如果全部替换为[MASK],模型在预训练时只见过[MASK],但微调时没有[MASK]——这会导致预训练和微调之间的不匹配。10%替换为随机词迫使模型不盲目信任输入,而是真正理解上下文来判断一个词是否合理。10%保持不变让模型也学习"这个词是对的,不需要改"的情况。NSP的设计训练了句子间关系理解,这对问答和自然语言推理等任务至关重要。
import numpy as np
# MLM(掩码语言模型)和NSP(下一句预测)的演示
# 这是BERT预训练的核心任务
class MLMDemo:
def __init__(self, vocab_size=100, d_model=8):
np.random.seed(42)
# 模拟词嵌入表
self.embeddings = np.random.randn(vocab_size, d_model) * 0.5
# 模拟BERT的预测头(将d_model映射到词表大小)
self.W_pred = np.random.randn(d_model, vocab_size) * 0.1
def mask_tokens(self, token_ids, mask_prob=0.15):
"""
模拟MLM的掩码策略
80%: 替换为[MASK](假设MASK token id=0)
10%: 替换为随机词
10%: 保持不变
"""
masked_ids = token_ids.copy()
mask_labels = np.full(len(token_ids), -1) # -1表示不需要预测
for i in range(len(token_ids)):
if np.random.random() < mask_prob:
mask_labels[i] = token_ids[i] # 记录原始词(用于计算损失)
rand = np.random.random()
if rand < 0.8:
masked_ids[i] = 0 # 80%: 替换为[MASK]
elif rand < 0.9:
masked_ids[i] = np.random.randint(1, len(self.embeddings)) # 10%: 随机词
# else: 10%保持不变
return masked_ids, mask_labels
def predict_masked(self, masked_ids, mask_labels):
"""模拟MLM预测:对被遮盖的位置预测原词"""
print("=== MLM 掩码语言模型演示 ===\n")
word_map = {1: "我", 2: "爱", 3: "北京", 4: "天安门",
5: "的", 6: "长城", 7: "很", 8: "美丽", 0: "[MASK]"}
sentence_ids = [1, 2, 3, 4, 5, 6, 7, 8] # "我 爱 北京 天安门 的 长城 很 美丽"
# 显示原始句子
print("原始句子:", " ".join(word_map.get(tid, str(tid)) for tid in sentence_ids))
# 应用掩码
masked_ids, mask_labels = self.mask_tokens(np.array(sentence_ids), mask_prob=0.25)
print("掩码后句子:", " ".join(word_map.get(tid, str(tid)) for tid in masked_ids))
# 展示掩码策略
print("\n掩码详情:")
for i, (orig, masked, label) in enumerate(zip(sentence_ids, masked_ids, mask_labels)):
if label != -1:
orig_word = word_map.get(orig, str(orig))
masked_word = word_map.get(masked, str(masked))
print(f" 位置{i}: '{orig_word}' → '{masked_word}' (需要预测回'{orig_word}')")
print("\nMLM的关键设计:")
print("- 80% [MASK]:让模型学会'填空'")
print("- 10% 随机词:迫使模型不盲目信任输入,真正理解上下文")
print("- 10% 不变:让模型学会'这个词是对的,不需要改'")
class NSPDemo:
def __init__(self):
pass
def demonstrate(self):
print("\n\n=== NSP 下一句预测演示 ===\n")
# 模拟句子对
sentence_A = "我 爱 北京 天安门"
sentence_B_positive = "天安门 上 太阳 升" # 真实的下一句
sentence_B_negative = "今天 天气 很 好" # 随机句子
print(f"句子A: {sentence_A}")
print(f"句子B₁(真实下一句): {sentence_B_positive}")
print(f"句子B₂(随机句子): {sentence_B_negative}")
print("\nBERT的输入格式:")
print(f"[CLS] {sentence_A} [SEP] {sentence_B} [SEP]")
print("\nNSP任务:")
print("- 50%的训练样本:B是A的真实下一句(标签=IsNext)")
print("- 50%的训练样本:B是随机句子(标签=NotNext)")
print("- [CLS]标记的最终输出用于二分类预测")
print("\nNSP训练了模型对句子间关系的理解能力")
print("这对问答(QA)和自然语言推理(NLI)任务至关重要")
# 运行演示
mlm = MLMDemo()
mlm.predict_masked(None, None)
nsp = NSPDemo()
nsp.demonstrate()
大白话 MLM就像"填写被涂掉的词"。模型看完整句话(除了被涂掉的部分),然后猜被涂掉的是什么词。猜错了要扣分(损失),猜对了加分。为了减少扣分,模型必须学会理解上下文——比如"我[MASK]北京",如果理解了上下文,应该猜"爱"而不是"恨"或"吃"。15%的遮盖比例是一个平衡:太少则学习信号不够,太多则上下文信息不足,模型无法合理推断。
什么用(应用):MLM和NSP的预训练策略对后续所有预训练模型产生了深远影响。RoBERTa去掉了NSP(发现NSP帮助不大),增加了数据量和训练步数,效果更好。ALBERT通过参数共享减小模型体积。ELECTRA使用判别器-生成器框架,判别所有词是否被替换(而非仅15%),效率更高。这些改进都基于BERT的MLM框架,验证了"预训练+微调"范式的强大。
哪些坑(缺点):MLM的15%遮盖比例意味着每轮训练只利用了15%的词作为学习信号,效率较低。后续研究(如ELECTRA)通过判别所有词来提高效率。NSP后来被证明对大多数下游任务帮助有限(RoBERTa去掉了NSP),但对句子对任务(如NLI)仍有帮助。此外,MLM引入了[MASK]标记,在预训练和微调之间造成了不匹配(微调时没有[MASK]),虽然80/10/10策略缓解了这一问题。
三、BERT的微调与下游任务
是什么(定义):微调(Fine-tuning)是BERT应用的核心范式。预训练完成后,BERT获得了通用的语言理解能力。在下游任务上,只需在BERT之上添加一个简单的任务特定层(如分类器),然后用少量标注数据对整个模型进行端到端微调。微调通常只需要几轮训练(3-5个epoch),在几分钟到几小时内完成,远快于从零训练。
大白话 微调就像"转岗培训"。BERT在大学(预训练)里学了通用的语言能力——读文章、理解句意、词汇知识。现在要去做具体工作(下游任务),比如做情感分析(判断评论是好评还是差评),只需要一个简短的"岗前培训"(微调)——用几百条标注好评/差评的数据训练几轮,BERT就能胜任了。因为它的语言基础已经很好,学新任务只需要"微调"一下,而不是从头学起。
为什么(原理):微调之所以高效,是因为预训练已经学到了通用的语言特征(词法、语法、语义知识),这些特征对大多数下游任务都有用。微调只需要对顶层特征进行任务特定的调整——底层特征(如词向量、语法结构)通常保持不变或微调幅度很小。这就是"迁移学习"的核心思想:将源任务的知识迁移到目标任务。
import numpy as np
# BERT微调演示:在预训练编码器上添加分类头
# 展示微调如何适配下游任务
class BERTFineTuning:
def __init__(self, d_model=8, num_classes=3):
np.random.seed(42)
self.d_model = d_model
# 模拟预训练好的BERT编码器(权重已冻结或有微小更新)
self.W_enc = np.random.randn(d_model, d_model) * 0.3
# 任务特定的分类头:将[CLS]的输出映射到类别
self.W_cls = np.random.randn(d_model, num_classes) * 0.1
self.b_cls = np.zeros(num_classes)
def encode(self, X):
"""模拟BERT编码器的前向传播(简化版)"""
# 模拟多层Transformer的计算
hidden = np.tanh(X @ self.W_enc) # 简化的编码过程
return hidden
def classify(self, X):
"""完整的微调前向传播:编码 + 分类"""
encoded = self.encode(X)
# 取[CLS]标记的输出(第一个位置)做分类
cls_output = encoded[0] # [CLS]的表示
# 分类头:线性变换 + softmax
logits = cls_output @ self.W_cls + self.b_cls
probs = np.exp(logits) / np.sum(np.exp(logits))
return probs, encoded
# 创建示例数据:模拟情感分类任务
# 3个句子,每个6维词向量
sentences = {
"这个电影太棒了": np.random.randn(4, 6) * 0.5, # 4个词
"一般般没什么意思": np.random.randn(5, 6) * 0.5, # 5个词
"太烂了浪费钱": np.random.randn(3, 6) * 0.5, # 3个词
}
bert_ft = BERTFineTuning(d_model=6, num_classes=3)
print("=== BERT微调:情感分类任务 ===\n")
labels = ["正面", "中性", "负面"]
for sentence, X in sentences.items():
probs, encoded = bert_ft.classify(X)
pred_label = labels[np.argmax(probs)]
print(f"句子: '{sentence}'")
print(f" 预测概率: 正面={probs[0]:.3f}, 中性={probs[1]:.3f}, 负面={probs[2]:.3f}")
print(f" 预测类别: {pred_label}")
print(f" [CLS]向量维度: {encoded[0].shape}")
print()
print("微调的关键特点:")
print("- 编码器参数(W_enc)在微调时也会更新,但幅度很小")
print("- 分类头(W_cls)从零开始学习,是任务特定的")
print("- 整个模型端到端训练,只需要少量标注数据")
大白话 微调就像"给大学生配一个专业导师"。BERT是已经大学毕业的通才(预训练),现在要去做"情感分析"这个专业工作。只需要在BERT上面加一个小小的"专业模块"(分类头),然后让专业导师(标注数据)带几天(微调几步),就能上岗了。因为这个大学生底子好(预训练学到的语言知识),所以学新专业特别快。
什么用(应用):BERT微调在NLP领域产生了革命性影响。文本分类(情感分析、垃圾邮件检测、新闻分类)只需在[CLS]上加一个分类器;命名实体识别在每词输出上加分类器;问答任务中,预测答案的起始位置和结束位置;文本相似度中,比较两个句子[CLS]向量的距离。BERT的微调范式使得NLP任务的开发门槛大幅降低——不再需要从零训练模型,只需要准备少量标注数据即可。
哪些坑(缺点):微调虽然高效,但需要为每个下游任务存储一份完整的BERT模型(1.1亿参数),存储成本高。此外,BERT的512长度限制意味着长文本需要截断或分段处理。微调也有"灾难性遗忘"(catastrophic forgetting)的风险——如果微调步数过多或学习率过大,可能破坏预训练学到的通用知识。Prompt Tuning和Adapter等参数高效微调方法试图解决这些问题。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| BERT | 双向编码器表示,通过MLM和NSP预训练 | 开启NLP预训练+微调范式 | Transformer编码器、MLM |
| MLM(掩码语言模型) | 随机遮盖词并预测,学习双向上下文 | BERT的核心预训练任务 | 完形填空、自监督学习 |
| NSP(下一句预测) | 判断两个句子是否连续 | 训练句子间关系理解 | 句子对任务、NLI |
| [CLS]标记 | 特殊标记,聚合整个输入序列的信息 | 用于分类任务的表示 | 句子表示、池化 |
| [SEP]标记 | 分隔两个句子的标记 | 标记句子边界 | 段嵌入、句子对 |
| 微调(Fine-tuning) | 在预训练模型上适配下游任务 | 使BERT适配各种NLP任务 | 迁移学习、Adapter |
| WordPiece | BERT使用的子词分词方法 | 将罕见词分解为常见子词 | BPE、SentencePiece |
| 预训练+微调范式 | 先在大数据预训练,再在小数据微调 | 改变NLP研究范式,降低任务门槛 | 迁移学习、自监督学习 |
重点答疑
Q1: BERT和GPT的核心区别是什么?
三个层面:①架构——BERT是编码器(双向),GPT是解码器(单向/因果);②预训练任务——BERT用MLM(填空),GPT用LM(预测下一个词);③适用场景——BERT擅长理解(分类、问答、NER),GPT擅长生成(文本续写、对话)。这导致了根本性的差异:BERT不能直接生成文本(因为双向性),GPT在理解任务上不如BERT(因为单向限制)。
Q2: MLM中为什么要有10%随机替换和10%不变?
如果100%都用[MASK]标记,模型会在预训练时学会"看到[MASK]就预测",但微调时没有[MASK]——这造成训练和推理不匹配。10%随机替换迫使模型检查每个词是否合理——即使不是[MASK],也可能是错的,模型不能盲目信任输入。10%不变让模型学习"这个词不需要改"——否则模型会倾向于把所有词都预测为别的词。
Q3: NSP后来被证明效果不大,为什么?
RoBERTa的研究发现,去掉NSP只使用MLM,在大多数下游任务上效果相同甚至更好。原因可能是:NSP任务太简单(50%概率判断随机句子),模型学到了浅层的主题特征而非深层的句子关系;MLM本身已经足够强,能学到大部分语言知识;NSP可能干扰了MLM的学习(因为输入格式变复杂了)。但NSP对需要句子对推理的任务(如NLI)仍有帮助。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| BERT | /bɜːrt/ | 双向编码器表示模型,Google的预训练语言模型 |
| Masked Language Model (MLM) | /mæskt ˈlæŋɡwɪdʒ ˈmɑːdəl/ | 掩码语言模型,随机遮盖词并预测 |
| Next Sentence Prediction (NSP) | /nekst ˈsentəns prɪˈdɪkʃən/ | 下一句预测,判断两句是否连续 |
| Fine-tuning | /faɪn ˈtjuːnɪŋ/ | 微调,在预训练模型上适配下游任务 |
| WordPiece | /wɜːrd piːs/ | 子词分词方法,将词分解为更小的子词单元 |
| Pre-training | /priː ˈtreɪnɪŋ/ | 预训练,在大规模无标注数据上训练通用表示 |
| [CLS] Token | /klæs ˈtoʊkən/ | 分类标记,BERT用于聚合整个序列信息 |
| [SEP] Token | /sep ˈtoʊkən/ | 分隔标记,分隔两个句子 |
| [MASK] Token | /mæsk ˈtoʊkən/ | 掩码标记,MLM中被遮盖词的位置标记 |
| Segment Embedding | /ˈseɡmənt ɪmˈbedɪŋ/ | 段嵌入,标记词属于句子A还是句子B |
面试练习
Q1 [单选] BERT使用的是什么架构?
- A. Transformer解码器
- B. Transformer编码器
- C. 完整的编码器-解码器
- D. LSTM
解答:BERT使用Transformer的编码器部分(Encoder-only),实现双向上下文理解。GPT使用解码器(Decoder-only),原始Transformer使用完整的编码器-解码器。
Q2 [单选] BERT的MLM任务中,多少比例的输入词会被遮盖?
- A. 5%
- B. 15%
- C. 50%
- D. 100%
解答:BERT随机选择15%的词进行遮盖。其中80%替换为[MASK],10%替换为随机词,10%保持不变。这个比例在"足够学习信号"和"足够上下文信息"之间取得了平衡。
Q3 [单选] BERT的[CLS]标记有什么作用?
- A. 标记句子的开始
- B. 聚合整个输入序列的信息,用于分类任务
- C. 标记被遮盖的词
- D. 标记句子的结束
解答:[CLS]是专门为分类任务设计的特殊标记。它位于输入序列的最开始,经过Transformer编码后,其输出向量聚合了整个序列的信息,用于下游分类任务。
Q4 [多选] 关于BERT的预训练,以下哪些说法是正确的?
- A. BERT使用MLM和NSP两个预训练任务
- B. MLM中80%的遮盖词替换为[MASK],10%为随机词,10%不变
- C. BERT在预训练时使用标注数据
- D. BERT-base有12层,1.1亿参数
- E. BERT可以用于文本生成任务
解答:BERT使用MLM和NSP两个预训练任务,MLM有80/10/10的替换策略,BERT-base有12层、1.1亿参数。BERT使用无标注数据进行预训练(自监督学习),且由于双向性,不能直接用于文本生成。
Q5 [单选] BERT-base中的"base"指的是什么参数规模?
- A. 8层,512维,8头
- B. 12层,768维,12头
- C. 24层,1024维,16头
- D. 6层,512维,8头
解答:BERT-base:L=12层,H=768维,A=12头,总参数1.1亿。BERT-large:L=24层,H=1024维,A=16头,总参数3.4亿。
Q6 [单选] 为什么BERT在微调时可以适配不同的下游任务?
- A. 因为BERT架构可以动态改变
- B. 因为预训练学到了通用的语言表示,只需在顶层添加任务特定层
- C. 因为BERT在微调时重新训练所有参数
- D. 因为BERT的参数量足够大
解答:BERT通过预训练学到了通用的语言表示(语法、语义知识),这些知识对大多数下游任务都有用。微调时只需在BERT之上添加简单的任务特定层(如分类器),用少量标注数据训练即可。
Q7 [多选] 关于BERT的输入表示,以下哪些是正确的?
- A. 输入由词嵌入、段嵌入、位置嵌入三部分组成
- B. 段嵌入用于区分句子A和句子B
- C. 位置嵌入是可学习的,最大支持512个位置
- D. BERT使用正弦位置编码
- E. 三部分通过逐元素相加合并
解答:BERT的输入 = 词嵌入 + 段嵌入 + 位置嵌入,三者相加。段嵌入区分两个句子(用于NSP),位置嵌入是可学习的(非正弦编码),最大512个位置。
Q8 [单选] 在BERT微调中,通常使用多大的学习率?
- A. 0.1-0.5
- B. 1e-3-5e-3
- C. 2e-5-5e-5
- D. 1e-7-5e-7
解答:BERT微调通常使用很小的学习率(2e-5到5e-5),这是为了避免破坏预训练学到的知识。太大的学习率会导致"灾难性遗忘"。
Q9 [多选] 以下哪些是BERT的局限性?
- A. 输入长度限制为512个词
- B. 不能直接用于文本生成
- C. 预训练成本高
- D. 推理速度慢,不适合实时场景
- E. 不支持中文
解答:BERT的局限包括:512长度限制(位置嵌入上限)、不能生成文本(双向性)、预训练成本高(16 TPU × 4天)、推理速度慢(1.1亿参数)。BERT支持中文(有BERT-Chinese版本)。
Q10 [单选] 以下哪个模型是BERT的改进版本,去掉了NSP任务?
- A. ALBERT
- B. RoBERTa
- C. ELECTRA
- D. DistilBERT
解答:RoBERTa(Facebook, 2019)去掉了NSP任务,使用更大的数据和更长的训练时间,证明NSP并非必要。ALBERT通过参数共享减小模型体积,ELECTRA使用判别器-生成器框架,DistilBERT是BERT的蒸馏版本。