架构演进 · 编码器
- 对比
- BERT-base vs GPT-2 small
- 层
- 12 GPT-2 12
- dmodel
- 768 GPT-2 768
- 头
- 12 GPT-2 12
- dff
- 3,072 GPT-2 3,072
- 词表
- 30,522 个 WordPiece GPT-2 50,257
- 参数
- 110M GPT-2 124M
全部步骤
同样的形状,两个方向
BERT-base 的形状和 GPT-2 small 一模一样:12 层,宽 768,12 个头。不同的是方向:没有因果掩码,所以每个词元都能看到整个句子;它被训练成填出被隐藏的词,而不是预测下一个词。和 2017 年的 Transformer 一样,它在每次残差相加之后归一化。
12 × 768 · 110M · 仅编码器双向看
两个真实的头。GPT-2 最明显的前一词元头只能往回看。BERT 的第 3 层头 1 是它的镜像:几乎所有注意力都给了下一个词元,而这是因果掩码所禁止的。把鼠标停在格子上看看。
因果三角 · 完整方阵输入了什么
BERT 一次读一到两个句子:[CLS] 在最前,每个句子后面跟一个 [SEP]。每个输入向量是三者之和:WordPiece 词元嵌入、片段嵌入(句子 A 或 B)和学习得到的位置嵌入。WordPiece 会切开罕见词:mailman → mail ##man。
词元 + 片段 + 位置完形填空
训练时,15% 的词元被选中并隐藏(大多换成 [MASK]),BERT 从两侧预测它们:这就是掩码语言建模。这些是 BERT-base 的真实预测,旁边是 GPT-2 只凭空格之前的词做出的猜测。用下方的箭头切换句子。
从两侧预测 [MASK]用来读,而不是写
BERT 是为读而建的,不是为写:没有从左到右的顺序,它没有自然的方式来生成文本。取而代之的是在上面训练一个小分类器:用最终的 [CLS] 向量给每个句子一个标签(例如情感),或用每个词元的向量做标注。
label = softmax(hCLS · W)
代码
# BERT: GPT-2 small's shape, no causal mask, LayerNorm after each add
x = layer_norm(tok_emb[ids] + seg_emb[segments] + pos_emb[positions])
for layer in layers: # 12 × (768 wide, 12 heads, 3,072)
x = layer_norm(x + self_attn(x)) # every token sees every token
x = layer_norm(x + mlp(x))
# pretraining: predict hidden tokens from both sides
logits = mlm_head(x[masked_positions]) # 30,522 WordPiece scores
loss = F.cross_entropy(logits, original_ids)
# using it: a classifier on the [CLS] vector
probs = F.softmax(x[:, 0] @ W_cls, dim=-1)