14 BERT 的 Masked Language Modeling 详解
现在我们单独拿出 BERT 的核心预训练任务之一来详细讲解:
Masked Language Modeling也就是:
掩码语言模型它通常简称为:
MLMMLM 是 BERT 区别于 GPT 的关键。GPT 使用的是 Causal Language Modeling,也就是:
给定前文,预测下一个 token而 BERT 使用的是 Masked Language Modeling,也就是:
遮住句子中的一部分 token,让模型根据左右上下文预测被遮住的 token例如原句是:
我 今天 去 学校 上课BERT 会把其中一部分 token 遮住:
我 今天 去 [MASK] 上课然后让模型预测:
学校这个任务看起来很简单,但它背后非常关键。因为它让 BERT 能够学习双向上下文表示。也就是说,BERT 在预测[MASK]时,不仅能看左边:
我 今天 去还能看右边:
上课这正是 BERT 使用 Transformer Encoder 的原因。本文我们重点讲清楚:
1. MLM 要解决什么问题? 2. MLM 和 GPT 的 CLM 有什么不同? 3. BERT 为什么不能直接像 GPT 一样预测下一个词? 4. BERT 的 15% mask 策略是什么? 5. 80% / 10% / 10% 替换规则是什么意思? 6. MLM 的 loss 到底在哪些位置计算? 7. MLM 如何用代码实现? 8. MLM 有什么优势和局限?一、为什么 BERT 需要 MLM?
BERT 的目标是学习一种适合理解任务的语言表示。什么叫理解任务?
例如:
情感分类:这句话是正面还是负面? 语义匹配:这两个句子意思是否相同? 自然语言推理:句子 B 是否可以由句子 A 推出? 命名实体识别:每个 token 是不是人名、地名、机构名? 阅读理解:答案在文章中的哪个位置?这些任务通常需要模型理解完整输入。
例如句子:
这个苹果手机很好用。如果模型只看左边:
这个苹果它可能不知道“苹果”是水果还是公司。但如果模型能看到右边:
手机很好用就能判断这里的“苹果”指的是 Apple 公司。所以,理解任务通常需要双向上下文。而 GPT 的训练方式是从左到右预测:
这个 苹果 手机 很 好用预测“苹果”时只能看到:
这个不能看到右边的“手机”。这对生成任务是合理的,因为生成时确实不能偷看未来。但对理解任务来说,右侧上下文非常重要。因此,BERT 没有使用 GPT 的 CLM,而是设计了 MLM。MLM 的核心目的就是:
让模型在训练时同时利用左右上下文,学习深层双向语言表示。
二、MLM 的基本思想
MLM 的基本流程很简单:
原始句子 ↓ 随机选择一部分 token ↓ 对这些 token 做遮挡或替换 ↓ 输入 BERT Encoder ↓ 只在被选中的位置预测原始 token例如原始句子:
我 今天 去 学校 上课随机选中:
学校构造输入:
我 今天 去 [MASK] 上课模型输入的是:
我 今天 去 [MASK] 上课模型目标是:
学校也就是说,模型不能直接看到“学校”这个 token,但可以通过上下文推断它。
因为左边有:
我 今天 去右边有:
上课所以模型可以学到:
去 ___ 上课中间很可能是:
学校 / 教室 / 培训班这就是 MLM 的训练信号。
三、MLM 的数学形式
假设输入序列是:
从中随机选择一部分位置作为 mask 集合:
例如:
M = {3, 7, 10}表示第 3、7、10 个 token 被选中作为预测目标。
模型看到的是被处理后的输入:
也就是部分 token 被[MASK]、随机 token 或原 token 替换后的序列。
MLM 的训练目标是:
MLM 不是对所有 token 都算 loss,而是只对被选中的 token 位置算 loss。
例如:
输入:我 今天 去 [MASK] 上课 标签:-100 -100 -100 学校 -100这里-100表示这个位置不参与 loss 计算。真正计算 loss 的只有[MASK]位置。
四、BERT 的 15% mask 策略
BERT 并不是把所有 token 都遮住。
如果把所有 token 都遮住,模型就看不到上下文了。
例如:
[MASK] [MASK] [MASK] [MASK] [MASK]这显然没有意义。
BERT 的做法是:
随机选择输入中 15% 的 token 作为预测目标。
例如一句话有 20 个 token,那么大约选择:
20 × 15% = 3 个 token作为 MLM 预测目标。
举个例子:
原句: 我 今天 去 学校 上课 , 老师 讲 了 Transformer 。随机选中 15% token 后,可能选择:
学校 Transformer然后构造 MLM 输入,让模型预测这两个原始 token。
为什么是 15%?
可以直观理解为一种折中。
如果 mask 比例太低,训练信号太少。
如果 mask 比例太高,上下文被破坏太严重。
15% 是 BERT 论文中采用的经验设置。
五、80% / 10% / 10% 替换规则
很多人以为:
BERT 选中的 token 全部替换成
[MASK]。
其实不是。
BERT 对被选中的 15% token 使用了三种处理方式:
80%:替换成 [MASK] 10%:替换成随机 token 10%:保持原 token 不变也就是说,假设某个 token 被选中作为预测目标,它不一定会变成[MASK]。
例如原句:
我 今天 去 学校 上课如果选中“学校”,那么可能有三种情况。
1. 80% 情况:替换成[MASK]
我 今天 去 [MASK] 上课模型需要预测:
学校这是最典型的 MLM。
2. 10% 情况:替换成随机 token
我 今天 去 苹果 上课这里“学校”被随机替换成“苹果”。
但模型的预测目标仍然是:
学校这会迫使模型不能只依赖表面输入,而要根据上下文判断当前位置应该是什么词。
3. 10% 情况:保持原 token 不变
我 今天 去 学校 上课输入中仍然是“学校”。
但这个位置仍然被选中参与 MLM loss。
模型目标仍然是预测:
学校这看起来像是模型直接看到了答案。
但这样做有一个重要作用:减少预训练和下游任务之间的不一致。
六、为什么不全部替换成[MASK]?
如果所有被选中的 token 都替换成[MASK],会有一个问题:
BERT 在预训练时经常看到
[MASK],但下游任务中几乎看不到[MASK]。
例如情感分类输入是:
[CLS] 这部电影很好看 [SEP]命名实体识别输入是:
小明 在 北京 上学这些下游任务不会出现[MASK]。
如果预训练时模型过度依赖[MASK]这个特殊符号,那么预训练和微调之间就会产生差异。
所以 BERT 使用 80/10/10 策略:
80% 用 [MASK] 提供明确填空任务 10% 用随机 token 增强鲁棒性 10% 保持原 token 缓解 [MASK] 不一致问题可以这样理解:
[MASK] 替换:让模型学会根据上下文填空 随机替换:让模型学会发现不合理 token 保持不变:让模型适应真实输入分布这就是 BERT MLM 数据构造的关键细节。
七、MLM 的输入和标签是什么样?
假设原始 token 是:
[CLS] 我 今天 去 学校 上课 [SEP]对应 token id:
[101, 2769, 791, 1343, 2110, 677, 102]假设选中“学校”做 MLM。
经过替换后,输入变成:
[CLS] 我 今天 去 [MASK] 上课 [SEP]对应 input_ids:
[101, 2769, 791, 1343, 103, 677, 102]其中103可能是[MASK]的 id。
标签 labels 是:
[-100, -100, -100, -100, 2110, -100, -100]这里:
2110 是原始 token “学校”的 id -100 表示忽略该位置,不计算 loss所以训练时模型只在第 5 个位置计算损失。
这种设计和 PyTorch 中的CrossEntropyLoss(ignore_index=-100)很契合。
八、MLM 和 Attention Mask 是一回事吗?
不是。
这里非常容易混淆。
BERT 中经常有两种 mask:
1. MLM mask 2. Attention mask它们完全不是一回事。
1. MLM mask
MLM mask 是训练任务的一部分。
它决定:
哪些 token 被选中做预测目标 哪些 token 被替换成 [MASK]、随机 token 或保持不变 哪些位置计算 loss例如:
我 今天 去 [MASK] 上课这里[MASK]就来自 MLM 数据构造。
2. Attention mask
Attention mask 是模型计算 attention 时使用的。
它通常用于屏蔽 padding token。
例如:
我 今天 去 学校 上课 [PAD] [PAD]其中[PAD]不是真实文本。
Attention mask 会告诉模型:
真实 token 可以关注 [PAD] 位置不要关注所以:
| 类型 | 作用 |
|---|---|
| MLM mask | 用于构造预训练任务,决定哪些 token 要预测 |
| Attention mask | 用于 attention 计算,屏蔽 padding 等无效位置 |
BERT 是 Encoder-only 模型,不需要 GPT 那种 causal mask。
因为 BERT 可以双向看上下文。
但 BERT 仍然需要 attention mask 来屏蔽 padding。
九、MLM 和 CLM 的区别
MLM 和 CLM 是两种不同的语言模型训练目标。
1. MLM:看左右上下文,预测被遮住 token
例如:
我 今天 去 [MASK] 上课模型可以看到:
左边:我 今天 去 右边:上课预测:
学校这种方式适合 BERT,因为 BERT 使用 Encoder,可以双向建模。
2. CLM:只看左边,预测下一个 token
例如:
我 今天 去模型只能看到左边上下文,预测:
学校不能看到右边的:
上课这种方式适合 GPT,因为 GPT 是自回归生成模型。
对比一下:
| 对比维度 | MLM | CLM |
|---|---|---|
| 代表模型 | BERT | GPT |
| 架构 | Encoder-only | Decoder-only |
| 可见上下文 | 左右都能看 | 只能看左边 |
是否使用[MASK] | 使用 | 不使用 |
| 是否需要 causal mask | 不需要 | 需要 |
| 适合任务 | 理解、分类、抽取 | 生成、对话、续写 |
| loss 位置 | 只在 mask 位置 | 通常每个位置都预测下一个 token |
一句话总结:
MLM 训练模型做双向理解,CLM 训练模型做自回归生成。
十、为什么 MLM 适合 Transformer Encoder?
Transformer Encoder 的 Self-Attention 是双向的。
在 Encoder 中,每个 token 都可以关注整个输入序列。
例如:
我 今天 去 [MASK] 上课[MASK]位置可以关注:
我 今天 去 上课因此它很适合根据完整上下文预测被遮住的 token。
如果使用 Decoder,就会有问题。
Decoder 的 causal self-attention 只能看左边。
那么[MASK]如果出现在中间,Decoder 不能自然利用右侧上下文。
所以 MLM 和 Encoder 是天然匹配的:
MLM 需要双向上下文 Encoder 提供双向 Self-Attention这就是 BERT 使用 Encoder-only 架构的重要原因。
十一、MLM 的模型输出是什么?
BERT 输入经过多层 Transformer Encoder 后,会得到每个 token 的隐藏状态:
对于 MLM,被选中的位置会接一个预测头。这个预测头会把 hidden state 映射到词表大小:
如果词表大小是 30,000,那么每个被预测位置都会输出:
30000 个 logits然后通过 softmax 得到每个 token 的概率。
最终模型希望真实 token 的概率尽可能高。
十二、MLM 的 loss 如何计算?
假设某个位置真实 token 是:
学校它的 token id 是:
2110模型在该位置输出 logits:
[词表中每个 token 的分数]损失函数会计算:
模型给真实 token id 2110 的概率是否足够高。
如果模型认为:
学校概率很高,loss 就小。
如果模型认为:
苹果概率很高,loss 就大。
对于整个序列,MLM 只在被选中的位置计算 loss:
在代码中,通常使用:
CrossEntropyLoss(ignore_index=-100)其中 labels 中不需要计算 loss 的位置设为:
-100十三、MLM 数据构造代码示例
下面给出一个简化版 MLM 数据构造代码。
这个代码不依赖 Hugging Face,主要用于理解原理。
import random import torch def create_mlm_inputs( input_ids, vocab_size, mask_token_id, special_token_ids, mlm_probability=0.15 ): """ 构造 BERT MLM 训练样本。 参数: input_ids: List[int],原始 token ids vocab_size: 词表大小 mask_token_id: [MASK] 的 token id special_token_ids: 特殊 token 的 id 集合,例如 [CLS], [SEP], [PAD] mlm_probability: 选中 token 的比例,BERT 中通常是 15% 返回: masked_input_ids: 被处理后的输入 labels: MLM 标签,非预测位置为 -100 """ masked_input_ids = input_ids.copy() # labels 默认全是 -100,表示不计算 loss labels = [-100] * len(input_ids) for i, token_id in enumerate(input_ids): # 跳过特殊 token if token_id in special_token_ids: continue # 以 15% 概率选中该 token if random.random() < mlm_probability: # labels 记录原始 token labels[i] = token_id prob = random.random() # 80% 替换成 [MASK] if prob < 0.8: masked_input_ids[i] = mask_token_id # 10% 替换成随机 token elif prob < 0.9: masked_input_ids[i] = random.randint(0, vocab_size - 1) # 10% 保持原 token 不变 else: masked_input_ids[i] = token_id return masked_input_ids, labels # ====================== # 测试示例 # ====================== # 假设词表: # [PAD]=0, [CLS]=101, [SEP]=102, [MASK]=103 # 我=2001, 今天=2002, 去=2003, 学校=2004, 上课=2005 input_ids = [101, 2001, 2002, 2003, 2004, 2005, 102] vocab_size = 3000 mask_token_id = 103 special_token_ids = {0, 101, 102, 103} masked_input_ids, labels = create_mlm_inputs( input_ids=input_ids, vocab_size=vocab_size, mask_token_id=mask_token_id, special_token_ids=special_token_ids, mlm_probability=0.15 ) print("原始 input_ids:") print(input_ids) print("MLM 输入 masked_input_ids:") print(masked_input_ids) print("MLM 标签 labels:") print(labels)输出可能是:
原始 input_ids: [101, 2001, 2002, 2003, 2004, 2005, 102] MLM 输入 masked_input_ids: [101, 2001, 2002, 2003, 103, 2005, 102] MLM 标签 labels: [-100, -100, -100, -100, 2004, -100, -100]这里说明:
学校 被替换成了 [MASK] 模型输入看到的是 [MASK] 模型目标是预测原始 token 2004也就是“学校”。
十四、MLM 模型训练代码示意
假设我们已经有一个 BERT 模型,它输出每个位置的 logits:
logits: [batch_size, seq_len, vocab_size]labels 是:
labels: [batch_size, seq_len]其中不需要计算 loss 的位置是-100。
训练代码通常类似:
import torch import torch.nn as nn criterion = nn.CrossEntropyLoss(ignore_index=-100) # logits: [batch_size, seq_len, vocab_size] # labels: [batch_size, seq_len] loss = criterion( logits.view(-1, logits.size(-1)), labels.view(-1) )为什么要 reshape?
因为CrossEntropyLoss需要输入形状:
[N, C]其中:
(N):样本数量;
(C):类别数量,也就是 vocab_size。
而原始 logits 是:
[batch_size, seq_len, vocab_size]所以要变成:
[batch_size * seq_len, vocab_size]labels 也要从:
[batch_size, seq_len]变成:
[batch_size * seq_len]这样每个 token 位置就变成一个分类任务。
但是因为大多数位置 label 是-100,所以它们不会参与 loss。
十五、MLM 为什么能学习上下文表示?
MLM 强迫模型根据上下文恢复被遮住的词。
例如:
医生 给 病人 开 了 [MASK]可能预测:
药再比如:
他 把 钱 存进 了 [MASK]可能预测:
银行模型要完成这个任务,必须学会很多语言规律:
词和词之间的搭配关系 句法结构 语义关系 实体类型 上下文约束 常识知识比如预测“银行”,模型要理解:
钱 存进这些词和“银行”高度相关。
所以 MLM 不是简单填空,而是在训练模型建立上下文语义表示。
这也是为什么经过 MLM 预训练后,BERT 可以迁移到很多理解任务上。
十六、MLM 的优势
MLM 的优势主要有三点。
1. 可以利用双向上下文
这是 MLM 最大的优势。
模型预测某个位置时,可以同时使用左边和右边信息。
这非常适合语言理解。
2. 适合 Encoder-only 架构
Transformer Encoder 本身就是双向 Self-Attention。
MLM 和 Encoder 的结构天然匹配。
这使得 BERT 可以学习每个 token 的深层上下文表示。
3. 下游任务迁移效果好
BERT 经过 MLM 预训练后,可以通过微调适配很多任务。
例如:
文本分类 自然语言推理 语义匹配 命名实体识别 阅读理解这是因为这些任务都依赖文本理解,而 MLM 正是在训练模型理解上下文。
十七、MLM 的局限
MLM 也有一些明显局限。
1. 预训练和下游任务存在[MASK]不一致
预训练时会出现[MASK]。
但下游微调和真实推理时通常没有[MASK]。
虽然 BERT 用 80/10/10 策略缓解了这个问题,但它仍然存在。
2. 训练效率不如 CLM
BERT 通常只选择 15% token 做预测。
也就是说,一句话中大部分 token 不参与 loss。
而 GPT 的 CLM 通常每个位置都预测下一个 token,训练信号更密集。
所以从训练效率角度看,MLM 有一定劣势。
3. 不适合直接生成长文本
BERT 学的是填空式理解,不是从左到右生成。
因此它不适合像 GPT 那样自然生成长文本。
虽然可以设计特殊方法让 BERT 做生成,但这不是它的强项。
4. 独立预测多个 mask 位置
如果一句话中有多个[MASK],BERT 通常是并行预测这些位置。
这意味着它不一定像自回归模型那样显式建模被 mask token 之间的生成依赖。
例如:
我 喜欢 [MASK] [MASK]两个 mask 位置之间的依赖不如 CLM 那样自然逐步建模。
这也是 MLM 的一个限制。
十八、MLM 和 RoBERTa、ALBERT、ELECTRA 的关系
BERT 之后,很多模型都围绕 MLM 或其缺点做改进。
1. RoBERTa
RoBERTa 认为 BERT 训练还不够充分。
它主要改进了训练策略,例如:
更大数据 更大 batch 训练更久 去掉 NSP 动态 maskRoBERTa 仍然使用 MLM,但对训练方式进行了优化。
2. ALBERT
ALBERT 主要解决 BERT 参数量大的问题。
它仍然使用 MLM,但通过参数共享、embedding factorization 等方式减少参数。
3. ELECTRA
ELECTRA 则对 MLM 的训练效率提出了更强的改进。
它不再只是让模型预测被 mask 的 token,而是让模型判断每个 token 是否被替换。
这个任务叫:
Replaced Token Detection相比 MLM 只在 15% 位置计算 loss,ELECTRA 可以在所有 token 位置获得训练信号。
所以 ELECTRA 在训练效率上很有优势。
这说明:
MLM 是 BERT 的关键起点,但后续很多工作都在改进它的效率和训练方式。
十九、MLM 的直观总结
可以用一个简单例子总结 MLM。
原始文本:
我 今天 去 学校 上课随机选中:
学校输入模型:
我 今天 去 [MASK] 上课目标标签:
学校模型通过 Encoder 看完整上下文:
我 今天 去 + 上课然后预测[MASK]位置最可能是什么词。
训练完成后,模型学到的是:
每个 token 在上下文中的语义表示所以 BERT 可以很好地用于理解任务。
