迁移学习 - NSP 案例 - 自定义数据集对象
1 课程概览
本课讲解迁移学习 NSP(Next Sentence Prediction)案例的自定义数据集对象。NSP 是中文句子关系任务,判断第二句话是否是第一句话的下半句。需要自己写 Dataset,把一句话拆成两句话。筛选长度大于 44 的句子,截取前 44 个字符,前 22 个作为句子一,后 22 个作为句子二。
2 核心概念与定义
- NSP(Next Sentence Prediction):下一句预测,判断第二句是否是第一句的下半句。
- 自定义 Dataset:需要自己写 Dataset,把一句话拆成两句话。
- 正负样本:正样本(是下半句),负样本(不是下半句)。
3 模型与算法详解
NSP 任务
判断第二句话是否是第一句话的下半句。
- 输入:两句话
- 输出:二分类(是/不是下半句)
- 示例:"床前明月光" → "疑是地上霜"(是)
与前两个案例的区别
| 案例 | Dataset | 说明 |
|---|---|---|
| 中文分类 | load_dataset | 直接加载 |
| 中文填空 | load_dataset | 直接加载 |
| NSP | 自定义 Dataset | 需要拆句子 |
数据处理
筛选长度大于 44 的句子,截取前 44 个字符。
- 筛选长度大于 44 的句子
- 截取前 44 个字符
- 前 22 个字符作为句子一
- 后 22 个字符作为句子二
正负样本构建
随机生成 0 或 1,构建负样本。
- 随机数 = 0:构建负样本
- 从其他数据中随机抓一条数据
- 拿它的第二句话覆盖当前的第二句话
- 随机数 = 1:正样本(保持原样)
自定义 Dataset
继承
torch.utils.data.Dataset。
class MyDataset(Dataset):
def __init__(self, data):
...
def __getitem__(self, i):
...
def __len__(self):
...
4 数学原理与推导
NSP
$$P(\text{is_next} | \text{sentence}_1, \text{sentence}_2) = \text{softmax}(\text{BERT}(\text{sentence}_1, \text{sentence}_2))$$
其中:
- $\text{sentence}_1$ 是第一句话
- $\text{sentence}_2$ 是第二句话
- $\text{is_next}$ 是是否为下半句
句子拆分
$$\text{sentence}_1 = \text{text}[:22]$$
$$\text{sentence}_2 = \text{text}[22:44]$$
5 代码示例
import torch
from torch.utils.data import Dataset
import random
class MyDataset(Dataset):
"""自定义数据集:NSP 任务"""
def __init__(self, data):
"""
Args:
data: 数据列表,每个元素是 {'text': ..., 'label': ...}
"""
self.data = data
def __getitem__(self, i):
"""获取一条数据"""
# 1. 获取文本
text = self.data[i]['text']
# 2. 截取前 44 个字符
text = text[:44]
# 3. 拆分成两句话
sentence1 = text[:22] # 前 22 个字符作为句子一
sentence2 = text[22:44] # 后 22 个字符作为句子二
# 4. 构建正负样本
# 随机生成 0 或 1
if random.randint(0, 1) == 0:
# 负样本:从其他数据中随机抓一条数据的第二句话
random_idx = random.randint(0, len(self.data) - 1)
random_text = self.data[random_idx]['text'][:44]
sentence2 = random_text[22:44]
label = 0 # 不是下半句
else:
# 正样本:保持原样
label = 1 # 是下半句
return sentence1, sentence2, label
def __len__(self):
"""返回数据集大小"""
return len(self.data)
# 测试
if __name__ == "__main__":
print("=== 迁移学习:NSP 案例 - 自定义数据集对象 ===")
# 模拟数据
data = [
{'text': '床前明月光疑是地上霜举头望明月低头思故乡' * 2},
{'text': '春眠不觉晓处处闻啼鸟夜来风雨声花落知多少' * 2},
]
# 创建数据集
dataset = MyDataset(data)
# 测试
for i in range(len(dataset)):
sentence1, sentence2, label = dataset[i]
print(f"样本 {i+1}:")
print(f" 句子一: {sentence1}")
print(f" 句子二: {sentence2}")
print(f" 标签: {label} ({'是' if label == 1 else '不是'}下半句)")
代码说明
| 代码 | 说明 |
|---|---|
Dataset | 继承 PyTorch 数据集基类 |
text[:44] | 截取前 44 个字符 |
text[:22] | 前 22 个字符作为句子一 |
text[22:44] | 后 22 个字符作为句子二 |
random.randint(0, 1) | 随机生成 0 或 1 |
label = 0 | 负样本(不是下半句) |
label = 1 | 正样本(是下半句) |
6 重难点与易错提醒
- ❗重点:NSP 是判断第二句话是否是第一句话的下半句。
- ❗重点:需要自定义 Dataset,把一句话拆成两句话。
- ❗重点:筛选长度大于 44 的句子,前 22 作为句子一,后 22 作为句子二。
- ❗重点:随机生成 0 或 1 构建正负样本。
- ⚠️易错:负样本要从其他数据中随机抓一条数据的第二句话。
7 课堂问答精选
Q1:NSP 任务是什么?
A:NSP(Next Sentence Prediction)是下一句预测,判断第二句话是否是第一句话的下半句。是二分类问题。
Q2:为什么需要自定义 Dataset?
A:因为需要把一句话拆成两句话,构建句子对。前两个案例直接用 load_dataset 加载,NSP 需要自己写 Dataset。
Q3:如何拆分句子?
A:筛选长度大于 44 的句子,截取前 44 个字符,前 22 个作为句子一,后 22 个作为句子二。
Q4:如何构建正负样本?
A:随机生成 0 或 1。如果是 0,从其他数据中随机抓一条数据的第二句话覆盖当前的第二句话,构建负样本;如果是 1,保持原样,是正样本。
8 本课小结
- NSP:下一句预测,判断第二句是否是第一句的下半句。
- 需要自定义 Dataset,把一句话拆成两句话。
- 筛选长度大于 44 的句子,前 22 作为句子一,后 22 作为句子二。
- 随机生成 0 或 1 构建正负样本。
9 延伸思考
- 数据预处理如何进行?
- 如何使用 BERT 分词器处理句子对?