chapter2-处理文本数据
【README】
本文内容总结自《从零构建大模型》第二章,非常棒的一本书,墙裂推荐;
项目目录结构:
【1】在线使用BPE分词器
test0205_p29_bpe_tokenizer.py
from importlib.metadata import version
import tiktokenprint("tiktoken version=" , version("tiktoken"))print("\n\n=== 使用BPE分词器 ")
# 获取BPE分词器
bpeTokenizer = tiktoken.get_encoding("gpt2")
rawText = ("hello, do you like tea? <|endoftext|> in the sunlit terraces""of sumunknownPlace."
)tokenIds = bpeTokenizer.encode(rawText, allowed_special={"<|endoftext|>"})
print(tokenIds)
【打印效果】
=== 使用BPE分词器
[31373, 11, 466, 345, 588, 8887, 30, 220, 50256, 287, 262, 4252, 18250, 8812, 2114, 1659, 2160, 34680, 27271, 13]
【2】离线使用bpe分词器
安装bpe分词器所需的词汇表文件与编码json文件;
【test0205_p29_diy_bpe_tokenizer.py】
import os
import tiktoken
from pathlib import Pathtiktoken_cache_dir = str(Path("..", "tiktoken", ".tiktoken"))
os.environ["TIKTOKEN_CACHE_DIR"] = tiktoken_cache_dirprint("\n\n=== 使用BPE分词器 ")
# 获取BPE分词器
bpeTokenizer = tiktoken.get_encoding("gpt2")
rawText = ("hello, do you like tea? <|endoftext|> in the sunlit terraces""of sumunknownPlace."
)tokenIds = bpeTokenizer.encode(rawText, allowed_special={"<|endoftext|>"})
print(tokenIds)# 把tokenId转为字符串 -- 解码
print("\n\n===把tokenId转为字符串 -- 解码")
decodeResult = bpeTokenizer.decode(tokenIds)
print(decodeResult)
解决tiktoken库调用get_encoding时SSL超时-CSDN博客
【3】使用滑动窗口进行数据采样
test0206_p31_slide_window_data_sample.py
import os
from pathlib import Pathimport tiktokenfrom src.utils import BusiIoUtilstiktoken_cache_dir = str(Path("..", "tiktoken", ".tiktoken"))
os.environ["TIKTOKEN_CACHE_DIR"] = tiktoken_cache_dirprint("\n\n=== 使用BPE分词器 ")
# 获取BPE分词器
bpeTokenizer = tiktoken.get_encoding("gpt2")# 读取verdict小说
print("\n\n=== 读取verdict小说,并用自定义分词器分词")
with open(Path(BusiIoUtils.get_root_dir(), "..", "file", "the-verdict.txt")) as f:raw_text = f.read()
encoded_text = bpeTokenizer.encode(raw_text)
print(len(encoded_text))# 截取前50个词元
encode_sample = encoded_text[50:]# 创建下一个单词预测任务的输入-目标对
context_size = 4 # 滑动窗口大小=4
x = encode_sample[:context_size]
y = encode_sample[1:context_size + 1]
print(f"x:{x}")
print(f"y: {y}")# 处理多个预测任务
print("\n\n===处理多个预测任务,输出词元id")
for i in range(1, context_size + 1):context = encode_sample[:i]desired = encode_sample[i]print(context, "---->", desired)# 处理多个预测任务
print("\n\n===处理多个预测任务,输出词元id解码后的词元")
for i in range(1, context_size + 1):context = encode_sample[:i]desired = encode_sample[i]print(bpeTokenizer.decode(context), "---->", bpeTokenizer.decode([desired]))# 实现一个数据加载器
# 目标是: 返回两个张量, 一个包含大模型所见的文本输入的输入张量,另一个包含大模型需要预测的目标词元的目标张量
import torch
【打印效果】
=== 使用BPE分词器 === 读取verdict小说,并用自定义分词器分词
5145
x:[290, 4920, 2241, 287]
y: [4920, 2241, 287, 257]===处理多个预测任务,输出词元id
[290] ----> 4920
[290, 4920] ----> 2241
[290, 4920, 2241] ----> 287
[290, 4920, 2241, 287] ----> 257===处理多个预测任务,输出词元id解码后的词元and ----> establishedand established ----> himselfand established himself ----> inand established himself in ----> a
【4】高效数据加载器
为实现高效数据加载器,引入PyTorch内置的Dataset类和DataLoader类;
在把词元token转为嵌入向量前,还需要实现一个高效的数据加载器 DataLoader。
这个数据加载器会遍历输入数据集,并将输入和目标以PyTorch张量的形式返回,这些PyTorch张量可以被视为多维数组。
具体来说,我们的目标是返回两个张量:一个是包含大语言模型所见的文本输入的输入张量,另一个是包含大语言模型需要预测的目标词元的目标张量。
【4.1】定义数据集类GPTDatasetV1用于批处理输入和目标的数据集
【GPTDatasetV1.py】
import torch
from torch.utils.data import Dataset, DataLoaderclass GPTDatasetV1(Dataset):def __init__(self, txt, tokenizer, max_length, stride):self.input_ids = []self.target_ids = []# 对全部文本进行分词token_ids = tokenizer.encode(txt)# 使用滑动窗口把文本划分为长度为max_length的重叠序列for i in range(0, len(token_ids) - max_length, stride):input_chunk = token_ids[i:i + max_length]target_chunk = token_ids[i + 1: i + max_length + 1]self.input_ids.append(torch.tensor(input_chunk))self.target_ids.append(torch.tensor(target_chunk))# 返回数据集的总行数def __len__(self):return len(self.input_ids)# 返回数据集的指定行def __getitem__(self, idx):return self.input_ids[idx], self.target_ids[idx]
【5】用于批量生成输入-目标对的数据加载器
【test0206_p35_dataloader.py】
import os
from pathlib import Path
import tiktoken
from src.chapter02.GPTDatasetV1 import GPTDatasetV1 as Diy_GPTDatasetV1
from src.utils import BusiIoUtils
import torch
from torch.utils.data import Dataset, DataLoadertiktoken_cache_dir = str(Path("..", "tiktoken", ".tiktoken"))
os.environ["TIKTOKEN_CACHE_DIR"] = tiktoken_cache_dirprint("\n\n=== 使用BPE分词器 ")
# 获取BPE分词器
bpeTokenizer = tiktoken.get_encoding("gpt2")# 创建数据加载器
def create_data_loader(txt, batch_size=4, max_length=256, stride=128, shuffle=True, drop_last=True,num_workers=0):# 初始化分词器tokenizer = tiktoken.get_encoding("gpt2")# 创建数据集dataset = Diy_GPTDatasetV1(txt, tokenizer, max_length, stride)dataloader = DataLoader(dataset,batch_size=batch_size,shuffle=shuffle,# 如果drop_last为True,且批次大小小于指定的batch_size,则会删除最后一批,以防止在训练期间出现损失剧增drop_last=drop_last,# 用于预处理的cpu进程数num_workers=num_workers)return dataloader# 读取verdict小说
print("\n\n=== 读取verdict小说,并用自定义分词器分词")
with open(Path(BusiIoUtils.get_root_dir(), "..", "file", "the-verdict.txt")) as f:raw_text = f.read()print(raw_text[:10])data_loader = create_data_loader(raw_text, batch_size=1, max_length=4, stride=1, shuffle=False)
data_iter = iter(data_loader)
first_batch = next(data_iter)
print(first_batch)
【打印结果】
=== 使用BPE分词器 === 读取verdict小说,并用自定义分词器分词
I HAD alwa
[tensor([[ 40, 367, 2885, 1464]]), tensor([[ 367, 2885, 1464, 1807]])]
【5.2】设置批次大小(超参数)等于8,查看效果
# 使用大于1的批次大小(批次大小是超参数,这里设置为8)
data_loader2 = create_data_loader_v1(raw_text, batch_size=8, max_length=4, stride=4, shuffle=False)
data_iter2 = iter(data_loader2)
inputs, targets = next(data_iter2)
print("inputs = \n", inputs)
print("targets = \n", targets)
【打印结果】
inputs = tensor([[ 40, 367, 2885, 1464],[ 1807, 3619, 402, 271],[10899, 2138, 257, 7026],[15632, 438, 2016, 257],[ 922, 5891, 1576, 438],[ 568, 340, 373, 645],[ 1049, 5975, 284, 502],[ 284, 3285, 326, 11]])
targets = tensor([[ 367, 2885, 1464, 1807],[ 3619, 402, 271, 10899],[ 2138, 257, 7026, 15632],[ 438, 2016, 257, 922],[ 5891, 1576, 438, 568],[ 340, 373, 645, 1049],[ 5975, 284, 502, 284],[ 3285, 326, 11, 287]])