外观
训练数据预处理流水线
约 986 字大约 3 分钟
2025-05-23
# -------------------------- 数据预处理模块 --------------------------
# 导入OpenAI官方分词库,支持GPT系列模型的快速分词
import tiktoken
# PyTorch核心库和数据集工具
import torch
from torch.utils.data import Dataset, DataLoader
# 自定义GPT训练数据集类(继承PyTorch Dataset)
class GPTDatasetV1(Dataset):
def __init__(self, txt, tokenizer, max_length, stride):
# 存储输入序列和目标序列的列表(每个元素为LongTensor)
self.input_ids = []
self.target_ids = []
# 将原始文本转换为token ID序列(例如:"hello" -> [31373])
token_ids = tokenizer.encode(txt)
# 滑动窗口采样:以stride为步长,生成连续的训练样本
# 例如:max_length=512时,每个样本包含512个token作为输入和对应的目标
for i in range(0, len(token_ids) - max_length, stride):
# 输入窗口:从i开始取max_length个token
input_chunk = token_ids[i:i + max_length]
# 目标窗口:相对输入窗口后移1位(实现next-token预测任务)
target_chunk = token_ids[i + 1: i + max_length + 1]
# 转换为Tensor并存储(后续自动批处理需要Tensor格式)
self.input_ids.append(torch.tensor(input_chunk, dtype=torch.long))
self.target_ids.append(torch.tensor(target_chunk, dtype=torch.long))
# 返回数据集总样本数(用于自动分批)
def __len__(self):
return len(self.input_ids)
# 获取单个训练样本(输入序列 + 目标序列)
def __getitem__(self, idx):
return self.input_ids[idx], self.target_ids[idx] # 返回元组形式
# -------------------------- 数据流水线构建 --------------------------
def create_dataloader_v1(txt, batch_size=4, max_length=256, stride=128,
shuffle=True, drop_last=True, num_workers=0):
# 初始化GPT-2官方分词器(适配模型预训练时的分词方式)
tokenizer = tiktoken.get_encoding("gpt2")
# 实例化数据集(完成滑动窗口切分和token转换)
dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
# 构造高效数据加载器(支持多线程加载和自动批处理)
dataloader = DataLoader(
dataset,
batch_size=batch_size, # 每批样本数(影响内存占用和并行效率)
shuffle=shuffle, # 是否随机打乱(训练时建议开启)
drop_last=drop_last, # 丢弃最后不完整的批次(保持批次形状一致)
num_workers=num_workers # 数据预加载线程数(加速大规模数据集处理)
)
return dataloader
# -------------------------- 数据加载 --------------------------
# 读取原始文本文件(假设文本为英文小说或文章)
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read() # 获取完整文本内容字符串
# 设置模型上下文窗口长度(即最大输入token数)
max_length = 4 # 示例使用小窗口便于调试,实际值通常为512/1024等
# 构建数据加载器(演示配置)
dataloader = create_dataloader_v1(
raw_text,
batch_size=8, # 每个批次包含8个独立样本序列
max_length=max_length, # 每个样本序列长度为4个token
stride=max_length, # 窗口滑动步长等于窗口长度(样本无重叠)
shuffle=False # 禁止打乱顺序(便于调试观察原始样本)
)
# -------------------------- 神经网络模块 --------------------------
# 创建数据迭代器(用于遍历数据批次)
data_iter = iter(dataloader)
# 获取第一个批次的数据(输入序列和目标序列)
inputs, targets = next(data_iter)
# 设置GPT模型的词汇表大小(GPT-2标准配置)
vocab_size = 50257 # 包含特殊token(如<|endoftext|>)的词汇总量
# 定义词向量维度(影响模型容量和计算复杂度)
output_dim = 256 # 每个token映射到256维的连续向量空间
# 构建可训练的词嵌入矩阵(核心组件:将离散token映射为连续向量)
token_embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
"""
数学形式:
embeddings = token_embeddings(input_ids) # (batch, seq_len) -> (batch, seq_len, dim)
其中:
input_ids中的每个整数索引对应嵌入矩阵中的一行向量
"""
# 将输入token转换为词嵌入向量
token_embeddings = token_embedding_layer(inputs)
print(f"词嵌入张量形状: {token_embeddings.shape}") # 预期 (8,4,256)
# 定义位置编码层(为序列中的每个位置生成嵌入向量)
context_length = max_length # 位置编码长度需匹配输入序列长度
pos_embedding_layer = torch.nn.Embedding(context_length, output_dim)
# 生成位置嵌入(为0到context_length-1的位置生成向量)
pos_embeddings = pos_embedding_layer(torch.arange(context_length))
print(f"位置嵌入形状: {pos_embeddings.shape}") # (4,256) 可广播到每个批次
# 融合词嵌入与位置信息(Transformer的经典输入处理方式)
input_embeddings = token_embeddings + pos_embeddings # 逐元素相加
print(f"最终输入表示形状: {input_embeddings.shape}") # 保持 (8,4,256)
版权所有
版权归属:NateHHX