第3讲:BERT——"完形填空"炼成的语言理解大师

一、从一个课堂游戏开始

1.1 你小时候玩过"完形填空"吗?

题目:
"今天____很好,我们一起去____玩。"

选项:
A. 天气 / 公园
B. 心情 / 游戏
C. 运气 / 赌场

正确答案:A

你是怎么选出A的?

  • “今天____很好” → "天气"最搭配("心情很好"也可以,但"天气"更常见)
  • “一起去____玩” → "公园"最合理("游戏"是动词,"赌场"不适合课堂语境)

关键能力:你同时看了整句话的前面和后面,综合判断中间该填什么。


1.2 这就是BERT的核心思想

BERT的全称是 Bidirectional Encoder Representations from Transformers

拆解:

  • Bidirectional = 双向(同时看左边和右边)
  • Encoder = 编码器(只理解,不生成)
  • Representations = 表示/向量(把文字变成数字)
  • from Transformers = 用Transformer架构

一句话:BERT是一个**只会"阅读理解",不会"写作文"**的模型。


二、BERT的诞生背景:为什么需要它?

2.1 2018年之前的问题

在BERT出现之前,NLP模型大多是**“从左到右”**阅读的:

输入:"今天天气很好"

模型处理过程:
  看到"今" → 预测下一个字是"天"
  看到"今天" → 预测下一个字是"气"
  看到"今天天" → 预测下一个字是"气"
  ...

问题:模型永远看不到右边的信息!

句子:"他很高兴,因为考试通过了。"

如果只能从左到右:
  看到"他很高兴"时,还不知道"为什么高兴"!
  必须等到看到"因为考试通过了",才能回头理解——但已经晚了。

这就像闭着眼睛走路,只能摸前面,不能看全貌。


2.2 BERT的革命:双向阅读

BERT的做法:把某些词遮住,让模型根据上下文猜出来

原始句子:今天 [MASK] 很 [MASK],我们一起去公园玩。

模型输入:今天 [MASK] 很 [MASK],我们一起去公园玩。
          ↑ 遮住"天气"和"好"

模型任务:猜 [MASK] 是什么?
  第1个[MASK]:根据"今天___很好" → 猜"天气"
  第2个[MASK]:根据"天气很___"和"我们一起去公园" → 猜"好"

关键:模型必须同时看左边和右边,才能猜对。

就像做填空题时,你自然会前后文对照着看。BERT通过这种方式,"被迫"学会了双向理解。


三、BERT的两大预训练任务

3.1 任务一:MLM(Masked Language Model)——完形填空

怎么"遮"?
原始句子:今天天气很好,我们一起去公园玩。

处理步骤:
1. 随机选15%的词
2. 对选中的词,按以下规则处理:
   - 80%概率 → 换成 [MASK]
   - 10%概率 → 换成随机词(如"香蕉")
   - 10%概率 → 保持不变

例子

原始:今天 天气 很 好 ,我们 一起 去 公园 玩 。

处理后(假设选中"天气"和"好"):
  今天 [MASK] 很 好 ,我们 一起 去 公园 玩 。
  ("天气"被换成[MASK],"好"保持不变——这是10%的情况)

或者:
  今天 [MASK] 很 香蕉 ,我们 一起 去 公园 玩 。
  ("天气"被换成[MASK],"好"被换成随机词"香蕉")

为什么要加随机词和保持不变?

防止模型"偷懒"!如果模型知道[MASK]位置一定是被遮住的词,它就不会好好学上下文。加入噪声,让模型必须认真对待每个位置。


模型怎么"猜"?
输入:[CLS] 今天 [MASK] 很 好 ,我们 一起 去 公园 玩 。[SEP]
       ↑BERT的特殊标记

处理流程:
1. 经过Transformer编码器 → 每个词变成一个向量
2. [MASK]位置的向量 → 输入一个分类层
3. 分类层输出:词汇表中每个词的概率
4. 目标:让"天气"的概率最高

可视化

[MASK]位置的向量(768维)
         ↓
    ┌─────────┐
    │ 分类层   │  → 输出:P("天气")=0.85, P("心情")=0.10, P("运气")=0.05, ...
    └─────────┘
         ↓
    损失函数:-log(P("天气"))  ← 让正确答案概率越大越好

3.2 任务二:NSP(Next Sentence Prediction)——句子关系判断

为什么要这个任务?

很多NLP任务需要理解两个句子的关系,比如:

  • 问答:问题和答案是否匹配?
  • 语义相似度:两句话意思一样吗?
  • entailment:第二句是第一句的推论吗?
怎么做?
从语料库中采样句子对:

正例(50%概率):
  句子A:今天天气很好。
  句子B:我们一起去公园玩。
  标签:IsNext(是连续的)

负例(50%概率):
  句子A:今天天气很好。
  句子B:量子力学是研究微观粒子的物理学分支。
  标签:NotNext(不相关)

输入格式

[CLS] 今天天气很好。[SEP] 我们一起去公园玩。[SEP]
  ↑                    ↑
  特殊标记              分隔两个句子

[CLS]位置的输出向量 → 二分类:IsNext or NotNext

[CLS]标记的输出被训练成"整个句子对的语义摘要",下游任务可以直接用它。


四、BERT的架构细节

4.1 Encoder-only:只理解,不生成

BERT的架构 = Transformer的Encoder部分 × 12层(或24层)

        输入嵌入 + 位置编码
              ↓
    ┌─────────────────┐
    │  Transformer    │  ← 第1层
    │  Encoder Block  │
    └─────────────────┘
              ↓
    ┌─────────────────┐
    │  Transformer    │  ← 第2层
    │  Encoder Block  │
    └─────────────────┘
              ↓
           ... × 12
              ↓
    ┌─────────────────┐
    │  Transformer    │  ← 第12层(最后一层输出用于预测)
    │  Encoder Block  │
    └─────────────────┘
              ↓
         [MASK]预测 / [CLS]分类

为什么不用Decoder?

组件 功能 BERT需要吗?
Encoder 双向理解上下文 ✅ 需要
Decoder 自回归生成(一个词一个词写) ❌ 不需要

BERT的目标是"理解",不是"写作"。Decoder的"因果掩码"(只能看左边)会妨碍双向理解。


4.2 BERT的两个版本

版本 层数 隐藏维度 参数量 用途
BERT-Base 12 768 1.1亿 研究、中小任务
BERT-Large 24 1024 3.4亿 高精度需求

对比GPT-3(1750亿参数),BERT算是"小模型"了。但BERT证明了:预训练+微调的范式威力巨大。


4.3 输入表示的三合一体

BERT的每个词向量 = 词嵌入 + 位置嵌入 + 句子嵌入

输入:[CLS] 我 喜欢 猫 [SEP] 它 很 可爱 [SEP]

词嵌入(Token Embedding):
  [CLS]→[0.1, 0.2, ...]  我→[0.3, -0.1, ...]  喜欢→[0.5, 0.8, ...]

位置嵌入(Position Embedding):
  位置0→[0.01, 0.02, ...]  位置1→[0.03, -0.01, ...]

句子嵌入(Segment Embedding):
  句子A→[0.1, 0.1, ...]  句子B→[0.2, 0.2, ...]

最终输入 = 三者相加

Segment Embedding的作用:告诉模型"这个词属于第1句还是第2句"。


五、BERT vs GPT:理解 vs 生成的根本分歧

5.1 架构对比

┌─────────────────────────────────────────────────────────┐
│  BERT(理解大师)                                        │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐             │
│  │ 词1     │ ←→ │ 词2     │ ←→ │ 词3     │  ← 双向箭头! │
│  └─────────┘    └─────────┘    └─────────┘             │
│       ↓              ↓              ↓                   │
│  每个词都能看到所有其他词(左边+右边)                      │
│  任务:完形填空(MLM)+ 句子关系(NSP)                   │
│  用途:文本分类、情感分析、命名实体识别、问答...            │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  GPT(生成天才)                                         │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐             │
│  │ 词1     │ ─→ │ 词2     │ ─→ │ 词3     │  ← 单向箭头! │
│  └─────────┘    └─────────┘    └─────────┘             │
│       ↓              ↓              ↓                   │
│  每个词只能看到左边的词(因果掩码)                        │
│  任务:预测下一个词(自回归)                             │
│  用途:写文章、聊天、代码生成、创意写作...                  │
└─────────────────────────────────────────────────────────┘

5.2 用一个例子说明区别

任务:理解句子"他很高兴,因为考试通过了。"

模型 处理"高兴"时能看到什么? 理解效果
BERT “他 [MASK] ,因为考试通过了” ✅ 看到"考试通过",知道高兴的原因
GPT “他 很” → 预测下一个词 ❌ 还没看到"考试通过",只能猜"开心?"

任务:续写句子"今天天气很好,"

模型 能力 输出
BERT 不擅长生成 只能给[MASK]位置填词,不能流畅续写
GPT 擅长生成 “我们一起去公园野餐吧。阳光洒在脸上…”

5.3 选择指南

你的需求 选BERT 选GPT
判断情感(正面/负面)
找句子中的实体(人名/地名)
判断两句话是否相似
写文章、聊天对话
代码自动补全
创意写作、头脑风暴

现代大模型(如GPT-4、Claude)已经融合了两者能力,但理解BERT和GPT的区别,对理解架构演进至关重要。


六、应用场景:BERT能做什么?

6.1 文本分类( sentiment analysis)

输入:"这部电影太精彩了,强烈推荐!"

BERT处理:
  [CLS] 这 部 电影 太 精彩 了 , 强烈 推荐 ! [SEP]
    ↓
  [CLS]位置的向量 → 分类层 → 正面(0.95) / 负面(0.05)

输出:正面情感,置信度95%

6.2 命名实体识别(NER)

输入:"李明在北京大学学习计算机科学。"

BERT输出每个词的标签:
  李(B-PER) 明(I-PER) 在(O) 北(B-ORG) 京(I-ORG) 大(I-ORG) 学(I-ORG) 
  学(O) 习(O) 计(B-FIELD) 算(I-FIELD) 机(I-FIELD) 科(I-FIELD) 学(I-FIELD) 。(O)

标签含义:
  B-PER = 人名开始  I-PER = 人名中间
  B-ORG = 机构开始  I-ORG = 机构中间
  O = 无关

6.3 语义相似度

句子A:"今天天气很好"
句子B:"今天的天气真不错"

BERT输出:[CLS]向量A vs [CLS]向量B 的余弦相似度 = 0.92(很相似)

句子C:"我喜欢吃苹果"
相似度 = 0.15(不相似)

七、动手实验:Hugging Face加载BERT做情感分析

7.1 环境准备

# 安装依赖
pip install transformers datasets torch

7.2 实验1:用预训练BERT做推理(零样本)

from transformers import BertTokenizer, BertForSequenceClassification
import torch

# ============================================
# 步骤1:加载预训练模型和分词器
# ============================================

# 使用中文BERT(bert-base-chinese)
# 如果是英文情感分析,可用 "nlptown/bert-base-multilingual-uncased-sentiment"
model_name = "bert-base-chinese"

print("正在加载模型,首次需要下载约400MB...")
tokenizer = BertTokenizer.from_pretrained(model_name)

# 加载用于序列分类的BERT(有额外的分类头)
# num_labels=2 表示二分类(正面/负面)
model = BertForSequenceClassification.from_pretrained(
    "bert-base-chinese", 
    num_labels=2
)

# 把模型设为评估模式(不更新参数)
model.eval()

print("✅ 模型加载完成!")

# ============================================
# 步骤2:准备输入文本
# ============================================

texts = [
    "这部电影太精彩了,演员演技炸裂!",  # 应该正面
    "浪费我两个小时,剧情烂透了",         # 应该负面
    "一般般吧,没什么特别的印象",         # 中性偏负
]

# BERT需要特殊格式:[CLS] + 文本 + [SEP]
# tokenizer会自动添加这些标记

print("\n" + "=" * 50)
print("【实验1】预训练BERT的零样本预测")
print("=" * 50)

for text in texts:
    # 分词并转换为ID
    inputs = tokenizer(
        text,
        padding=True,
        truncation=True,
        max_length=128,
        return_tensors="pt"  # 返回PyTorch张量
    )
    
    # 查看分词结果(帮助理解)
    tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
    print(f"\n原文:{text}")
    print(f"分词:{' '.join(tokens)}")
    
    # 模型推理
    with torch.no_grad():  # 不计算梯度,节省内存
        outputs = model(**inputs)
    
    # 获取预测结果
    logits = outputs.logits  # [batch_size, num_labels]
    probabilities = torch.softmax(logits, dim=-1)
    
    # 因为用的是随机初始化的分类头,结果可能不准
    # 但这演示了完整流程
    pred_label = torch.argmax(probabilities, dim=-1).item()
    confidence = probabilities[0][pred_label].item()
    
    label_name = "正面" if pred_label == 1 else "负面"
    print(f"预测:{label_name}(置信度:{confidence:.2%})")
    print(f"原始分数:{logits[0].tolist()}")

print("\n⚠️ 注意:上面结果可能不准,因为分类头是随机初始化的!")
print("   下面我们用微调后的模型,或者自己微调。")

7.3 实验2:加载已微调的英文情感分析BERT

from transformers import pipeline

print("\n" + "=" * 50)
print("【实验2】使用Hugging Face现成的情感分析管道")
print("=" * 50)

# 自动下载并加载已微调好的模型(约400MB)
# 这个模型在大量标注数据上训练过,效果可靠
sentiment_analyzer = pipeline(
    "sentiment-analysis",
    model="nlptown/bert-base-multilingual-uncased-sentiment"
)

test_texts = [
    "I love this product! It's amazing.",
    "This is the worst experience ever.",
    "Not bad, but could be better.",
    "Absolutely fantastic! Highly recommended.",
    "Terrible quality, complete waste of money.",
]

print("\n英文情感分析结果:")
for text in test_texts:
    result = sentiment_analyzer(text)[0]
    print(f"  文本:{text}")
    print(f"  结果:{result['label']}(置信度:{result['score']:.2%})")
    print()

# ============================================
# 实验3:自己微调BERT做中文情感分析
# ============================================

print("=" * 50)
print("【实验3】微调BERT做中文情感分析(完整流程)")
print("=" * 50)

import torch
from torch.utils.data import DataLoader
from transformers import (
    BertTokenizer, 
    BertForSequenceClassification,
    AdamW,
    get_linear_schedule_with_warmup
)
from datasets import load_dataset
import numpy as np
from sklearn.metrics import accuracy_score

# 设置设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备:{device}")

# 加载中文BERT
model_name = "bert-base-chinese"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
model = model.to(device)

# 准备模拟数据(实际应用时替换为真实数据集)
# 这里用简单的列表演示,真实场景可用 load_dataset("chinese_sentiment")
train_texts = [
    "这部电影太棒了", "演员演技出色", "剧情紧凑刺激", "视觉效果震撼",
    "导演功力深厚", "配乐恰到好处", "强烈推荐观看", "年度最佳影片",
    "烂片一部", "浪费生命", "演技尴尬", "剧情狗血",
    "特效五毛", "逻辑混乱", "看得想睡觉", "退票!",
] * 10  # 重复扩充数据量

train_labels = [1] * 8 * 10 + [0] * 8 * 10  # 1=正面, 0=负面

# 构建Dataset类
class SentimentDataset(torch.utils.data.Dataset):
    def __init__(self, texts, labels, tokenizer, max_len=128):
        self.texts = texts
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_len = max_len
    
    def __len__(self):
        return len(self.texts)
    
    def __getitem__(self, idx):
        text = str(self.texts[idx])
        label = self.labels[idx]
        
        encoding = self.tokenizer(
            text,
            max_length=self.max_len,
            padding='max_length',
            truncation=True,
            return_tensors='pt'
        )
        
        return {
            'input_ids': encoding['input_ids'].flatten(),
            'attention_mask': encoding['attention_mask'].flatten(),
            'label': torch.tensor(label, dtype=torch.long)
        }

# 创建数据加载器
train_dataset = SentimentDataset(train_texts, train_labels, tokenizer)
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)

# 训练配置
optimizer = AdamW(model.parameters(), lr=2e-5, weight_decay=0.01)
epochs = 3
total_steps = len(train_loader) * epochs
scheduler = get_linear_schedule_with_warmup(
    optimizer,
    num_warmup_steps=0,
    num_training_steps=total_steps
)

# 训练循环
print(f"\n开始训练({epochs}轮)...")
model.train()

for epoch in range(epochs):
    total_loss = 0
    correct = 0
    total = 0
    
    for batch_idx, batch in enumerate(train_loader):
        input_ids = batch['input_ids'].to(device)
        attention_mask = batch['attention_mask'].to(device)
        labels = batch['label'].to(device)
        
        # 清零梯度
        optimizer.zero_grad()
        
        # 前向传播
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            labels=labels
        )
        
        loss = outputs.loss
        logits = outputs.logits
        
        # 计算准确率
        preds = torch.argmax(logits, dim=-1)
        correct += (preds == labels).sum().item()
        total += labels.size(0)
        
        # 反向传播
        loss.backward()
        
        # 梯度裁剪(防止梯度爆炸)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
        
        # 更新参数
        optimizer.step()
        scheduler.step()
        
        total_loss += loss.item()
        
        if (batch_idx + 1) % 10 == 0:
            print(f"  Epoch {epoch+1}, Batch {batch_idx+1}, "
                  f"Loss: {loss.item():.4f}, Acc: {correct/total:.2%}")
    
    avg_loss = total_loss / len(train_loader)
    accuracy = correct / total
    print(f"Epoch {epoch+1} 完成:平均损失={avg_loss:.4f}, 准确率={accuracy:.2%}")

# 测试微调后的模型
print("\n" + "=" * 50)
print("【测试】微调后的模型预测")
print("=" * 50)

model.eval()

test_texts = [
    "这部电影真好看,感动哭了",      # 应该正面
    "太差了,完全看不下去",          # 应该负面
    "演员表演很到位",               # 应该正面
    "剧情莫名其妙",                 # 应该负面
]

for text in test_texts:
    inputs = tokenizer(
        text,
        padding=True,
        truncation=True,
        max_length=128,
        return_tensors="pt"
    ).to(device)
    
    with torch.no_grad():
        outputs = model(**inputs)
    
    logits = outputs.logits
    probabilities = torch.softmax(logits, dim=-1)
    pred_label = torch.argmax(probabilities, dim=-1).item()
    confidence = probabilities[0][pred_label].item()
    
    label_name = "正面😊" if pred_label == 1 else "负面😞"
    print(f"文本:{text}")
    print(f"预测:{label_name}(置信度:{confidence:.2%})")
    print(f"分数:{logits[0].tolist()}")
    print()

print("=" * 50)
print("✅ 实验完成!你刚刚亲手微调了一个BERT模型!")
print("=" * 50)

7.4 实验4:可视化BERT的注意力权重

from transformers import BertTokenizer, BertModel
import torch
import matplotlib.pyplot as plt
import numpy as np

print("=" * 50)
print("【实验4】可视化BERT的注意力权重")
print("=" * 50)

# 加载BERT(不带分类头,只取编码器输出)
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
model = BertModel.from_pretrained("bert-base-chinese", output_attentions=True)
model.eval()

# 输入句子
text = "猫坐在垫子上"
inputs = tokenizer(text, return_tensors="pt")

# 获取注意力权重
with torch.no_grad():
    outputs = model(**inputs)

# attentions是一个元组,12层,每层[batch, heads, seq, seq]
attentions = outputs.attentions  # 12层

# 查看形状
print(f"层数:{len(attentions)}")
print(f"每层注意力形状:{attentions[0].shape}")
# [1, 12, 6, 6] = [batch=1, heads=12, seq_len=6, seq_len=6]

# 获取分词结果
tokens = tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])
print(f"\n分词结果:{tokens}")
# ['[CLS]', '猫', '坐', '在', '垫', '子', '上', '[SEP]']

# 可视化第1层第1个头的注意力
layer = 0  # 第1层
head = 0  # 第1个头

attn_matrix = attentions[layer][0, head].numpy()  # [seq, seq]

plt.figure(figsize=(8, 6))
plt.imshow(attn_matrix, cmap='hot', interpolation='nearest')
plt.colorbar(label='Attention Weight')
plt.xticks(range(len(tokens)), tokens, rotation=45)
plt.yticks(range(len(tokens)), tokens)
plt.title(f'BERT Layer {layer+1}, Head {head+1} Attention')
plt.xlabel('Key (被关注的词)')
plt.ylabel('Query (当前处理的词)')

# 在每个格子里标注数值
for i in range(len(tokens)):
    for j in range(len(tokens)):
        text_color = "white" if attn_matrix[i, j] > 0.5 else "black"
        plt.text(j, i, f'{attn_matrix[i, j]:.2f}', 
                ha="center", va="center", color=text_color, fontsize=8)

plt.tight_layout()
plt.savefig('/mnt/agents/output/bert_attention_viz.png', dpi=150)
plt.show()

print("\n✅ 注意力可视化图已保存!")
print("""
解读热力图:
- 行 = Query(当前在处理哪个词)
- 列 = Key(在看哪些词)
- 颜色越亮 = 注意力权重越高

观察重点:
1. [CLS]通常关注所有词(收集全局信息)
2. 代词(如"它")会高度关注其指代对象
3. 不同头关注不同模式(语法 vs 语义)
""")

# 对比不同层的注意力模式
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
layers_to_plot = [0, 2, 5, 8, 10, 11]  # 第1,3,6,9,11,12层

for idx, layer in enumerate(layers_to_plot):
    ax = axes[idx // 3, idx % 3]
    attn_matrix = attentions[layer][0, 0].numpy()  # 取第1个头
    
    im = ax.imshow(attn_matrix, cmap='hot', interpolation='nearest')
    ax.set_xticks(range(len(tokens)))
    ax.set_yticks(range(len(tokens)))
    ax.set_xticklabels(tokens, rotation=45, fontsize=8)
    ax.set_yticklabels(tokens, fontsize=8)
    ax.set_title(f'Layer {layer+1}')
    
    # 添加数值标注
    for i in range(len(tokens)):
        for j in range(len(tokens)):
            text_color = "white" if attn_matrix[i, j] > 0.5 else "black"
            ax.text(j, i, f'{attn_matrix[i, j]:.2f}', 
                   ha="center", va="center", color=text_color, fontsize=6)

plt.suptitle('BERT Attention Evolution Across Layers', fontsize=14)
plt.tight_layout()
plt.savefig('/mnt/agents/output/bert_attention_layers.png', dpi=150)
plt.show()

print("\n✅ 多层对比图已保存!")
print("观察:浅层关注局部/语法,深层关注全局/语义")

八、核心总结

概念 一句话解释
BERT 双向Transformer编码器,专门做"理解"
MLM 完形填空——遮住词,让模型根据上下文猜
NSP 判断两句话是否连续,学习句子关系
[CLS] 特殊标记,输出代表整句语义
[SEP] 分隔标记,区分两个句子
预训练 在大语料上自监督学习通用语言表示
微调 在具体任务上用小数据调整,快速适配
BERT vs GPT 理解 vs 生成;双向 vs 单向

九、面试高频题

Q1:BERT和GPT的本质区别是什么?

:架构上,BERT是Encoder-only,双向注意力,适合理解任务;GPT是Decoder-only,单向因果注意力,适合生成任务。训练目标上,BERT用MLM(完形填空)和NSP,GPT用自回归预测下一个词。

Q2:为什么BERT用MLM而不是直接预测下一个词?

:预测下一个词只能利用左边的信息,是单向的。MLM遮住中间的词,迫使模型同时利用左右两边的上下文,实现真正的双向理解。

Q3:BERT的[CLS]标记有什么用?

:[CLS]位置的输出向量经过NSP任务训练,编码了整个输入序列的语义信息。下游分类任务可以直接用这个向量,无需对整个序列做额外处理。

Q4:BERT处理长文本有什么限制?

:BERT-base的最大序列长度通常是512个token。更长的文本需要截断或分段处理。这是Transformer O(n²)注意力复杂度的限制。


十、课后作业

作业1:修改实验代码

# 尝试:
# 1. 把MLM任务应用到英文句子,观察BERT如何填词
# 2. 比较BERT-base和BERT-large在同一任务上的表现
# 3. 用不同的随机种子,观察注意力权重的变化

作业2:思考题

问题:BERT的NSP任务在后续研究中被发现效果有限,
      RoBERTa等模型甚至去掉了NSP。为什么?

提示:可能的原因包括——
  1. NSP太简单(负样本随机采样,和正样本差异太大)
  2. 句子对任务不如更细粒度的词级别任务有效
  3. 现代模型用更大的数据和更好的预训练目标

十一、下讲预告

第4讲:GPT系列——"预测下一个词"的生成天才

我们将:

  • 理解GPT的自回归生成机制
  • 用Hugging Face实现文本续写
  • 探索Temperature、Top-p等生成参数的影响
  • 理解RLHF(人类反馈强化学习)如何让GPT"说人话"
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐