介绍
在因果语言建模(CLM)场景下,用滑动窗口(Sliding Window) 进行数据采样,目的是把 长文本切分成固定长度的、有重叠的样本,让模型能更好地学习序列依赖,同时充分利用数据。
滑动窗口采样过程
在CLM中,模型输入有 最大长度限制(比如GPT的512、1024),滑动窗口采样就是:
- 设定一个窗口长度(window_size)(等于模型最大输入长度);
- 设定一个步长(step_size)(每次窗口滑动的距离,小于window_size则样本有重叠);
- 从长文本的起始位置开始,按步长滑动窗口,切分出多个固定长度的子序列;
- 每个子序列作为CLM的训练样本(输入是前n-1个token,标签是第n个token)。
这种方式既符合CLM“用前文预测后文”的因果特性,又能让长文本的每个token都有机会作为预测目标,还能通过重叠区域保留上下文的连续性。
实现1
为了便于理解,使用滑动窗口(sliding window)方法,读取原文,而不是词元,像上图那样,提取一次 输入-目标对:
- 读取文本
- 进行分词
- 定义窗口大小、步长
- 提取一次
输入-目标对
import re
# 读取文本
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
# 分词
raw_sample = re.split(r'([,.:;?_!"()\']|--|\s)', raw_text)
raw_sample =[
item.strip() for item in raw_sample if item.strip()
]
# 定义上下文大小,即:窗口大小,每次读取4个词
context_size = 4
# 步长,下次移动1个词
stride = 1
# 输出词元
inputs = raw_sample[:context_size]
# 目标词元
targets = raw_sample[stride:context_size + stride]
print("输入词元:", inputs)
print("目标词元:", targets)
执行结果:
输入词元: ['I', 'HAD', 'always', 'thought']
目标词元: ['HAD', 'always', 'thought', 'Jack']
实现2
对 实现1 进行改进,提取所有 输入-目标对,前面步骤不变,只改第四步:
import re
# 读取文本
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
# 分词
raw_sample = re.split(r'([,.:;?_!"()\']|--|\s)', raw_text)
raw_sample =[
item.strip() for item in raw_sample if item.strip()
]
# 定义上下文大小,即:窗口大小,每次读取4个词
context_size = 4
# 步长,下次移动1个词
stride = 1
# 定义list,存放输入词元
inputs = []
# 定义list,存放目标词元
targets = []
for i in range(0, len(raw_sample) - context_size, stride):
input_chunk = raw_sample[i:i + context_size]
target_chunk = raw_sample[i + 1:i + context_size + 1]
inputs.append(input_chunk)
targets.append(target_chunk)
print("前5个输入词元:")
for item in inputs[:5]:
print(item)
print()
print("前5个目标词元:")
for item in targets[:5]:
print(item)
执行结果:
前5个输入词元:
['I', 'HAD', 'always', 'thought']
['HAD', 'always', 'thought', 'Jack']
['always', 'thought', 'Jack', 'Gisburn']
['thought', 'Jack', 'Gisburn', 'rather']
['Jack', 'Gisburn', 'rather', 'a']
前5个目标词元:
['HAD', 'always', 'thought', 'Jack']
['always', 'thought', 'Jack', 'Gisburn']
['thought', 'Jack', 'Gisburn', 'rather']
['Jack', 'Gisburn', 'rather', 'a']
['Gisburn', 'rather', 'a', 'cheap']
步长的含义
详见链接:
https://www.malaoshi.top/show_1GW2YXBPPimC.html
实现3
使用滑动窗口(sliding window)方法,提取 输入-目标对,这里是 提取词元:
- 读取文本
- 使用BPE分词器
- 定义窗口大小、步长
- 提取所有
输入-目标对
from importlib.metadata import version
import tiktoken
print("查看 version 版本号:", version("tiktoken"))
print("查看所有支持的编码:", tiktoken.list_encoding_names())
# 选择对应模型的编码,不同模型编码规则不同
tokenizer = tiktoken.get_encoding("gpt2")
# 读取文本
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
print("将文本转Token---------------------")
token_ids = tokenizer.encode(raw_text)
print("词元总数:", len(token_ids))
# 从数据集中移除前50个词元以便演示
# enc_sample = enc_text[50:]
# 定义上下文大小,即:窗口大小,每次读取4个词
context_size = 4
# 步长,下次移动1个词
stride = 1
# 定义list,存放输入词元
input_ids = []
# 定义list,存放目标词元
target_ids = []
for i in range(0, len(token_ids) - context_size, stride):
input_chunk = token_ids[i:i + context_size] # 输入词元
target_chunk = token_ids[i + 1:i + context_size + 1] # 目标词元
input_ids.append(input_chunk)
target_ids.append(target_chunk)
print()
print("前5个输入词元:")
for item in input_ids[:5]:
print(item)
print()
print("前5个目标词元:")
for item in target_ids[:5]:
print(item)
执行结果:
查看 version 版本号: 0.12.0
查看所有支持的编码: ['gpt2', 'r50k_base', 'p50k_base', 'p50k_edit', 'cl100k_base', 'o200k_base', 'o200k_harmony']
将文本转Token---------------------
词元总数: 5145
前5个输入词元:
[40, 367, 2885, 1464]
[367, 2885, 1464, 1807]
[2885, 1464, 1807, 3619]
[1464, 1807, 3619, 402]
[1807, 3619, 402, 271]
前5个目标词元:
[367, 2885, 1464, 1807]
[2885, 1464, 1807, 3619]
[1464, 1807, 3619, 402]
[1807, 3619, 402, 271]
[3619, 402, 271, 10899]
将词元转回文本
在上面的代码下面,加上下面代码:
# 将输入词元转回文本
print()
print("前5个输入词元:")
for item in input_ids[:5]:
text = tokenizer.decode(item)
print(text)
# 将目标词元转回文本
print()
print("前5个目标词元:")
for item in target_ids[:5]:
text = tokenizer.decode(item)
print(text)
执行结果:
前5个输入词元:
I HAD always -- 不足4个词,根据第3行可知,HAD无法识别
HAD always thought
AD always thought Jack -- HAD 识别成 H 和 AD 了
always thought Jack G
thought Jack Gis
前5个目标词元:
HAD always thought
AD always thought Jack
always thought Jack G
thought Jack Gis
Jack Gisburn

