大模型原理:预训练目标-因果语言建模(CLM)-pytorch实现数据集

需要掌握:大模型原理:预训练目标-因果语言建模(CLM)-使用滑动窗口进行数据采样

介绍

为了实现高效的数据加载器,将使用PyTorch内置的Dataset类和DataLoader类。

安装

poetry add numpy==2.2
poetry add torch==2.9

实现

导入模块

from importlib.metadata import version
import tiktoken
import torch
from torch.utils.data import Dataset, DataLoader

print("查看 version 版本号:", version("tiktoken"))
print("查看所有支持的编码:", tiktoken.list_encoding_names())

定义Dataset类

该类创建对象时,传入文本、分词器、窗口大小、步长,然后对文本进行编码,根据窗口大小、步长提出 输入-目标对

class GPTDatasetV1(Dataset):
    """
    GPTDatasetV1类继承自PyTorch的Dataset类,并定义了如何从数据集中提取单行数据。
    每行数据包含多个词元ID(数量由max_length参数决定),这些词元ID被分配给input_chunk张量,
    而target_chunk张量包含相应的目标词元ID。
    """

    def __init__(self, txt, tokenizer, max_length, stride):
        """
        初始化
        :param txt: 文本
        :param tokenizer: 分词器
        :param max_length: 窗口大小
        :param 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:
        """
        return len(self.input_ids)

    def __getitem__(self, idx):
        """
        返回指定索引的数据,必须实现,否则传入数据加载器报错
        :param idx:
        :return:
        """
        return self.input_ids[idx], self.target_ids[idx]

创建数据加载器函数

定义函数 create_dataloader_v1,实现下面功能:

  1. 初始化分词器
  2. 创建数据集
  3. 创建、并返回数据加载器
def create_dataloader_v1(txt, batch_size=4, max_length=256, stride=128, shuffle=True, drop_last=True, num_workers=0):
    """
    数据加载器,用于批量生成输入-目标对
    :param txt:
    :param batch_size:
    :param max_length:
    :param stride:
    :param shuffle:
    :param drop_last:
    :param num_workers:
    :return:
    """
    # 初始化分词器,选择对应模型的编码,不同模型编码规则不同
    tokenizer = tiktoken.get_encoding("gpt2")
    # 创建数据集
    dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
    # 创建数据加载器
    dataloader = DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=shuffle,
        drop_last=drop_last,  # 如果drop_last为True且批次大小小于指定的batch_size,则会删除最后一批,以防止在训练期间出现损失剧增
        num_workers=num_workers  # 用于预处理的CPU进程数
    )
    return dataloader

执行

  1. 读取文本
  2. 调用数据加载器函数
  3. 打印前几张数据
# 读取文本
with open("the-verdict.txt", "r", encoding="utf-8") as f:
    raw_text = f.read()

dataloader = create_dataloader_v1(raw_text, batch_size=1, max_length=4, stride=1, shuffle=False)

data_iter = iter(dataloader)
first_batch = next(data_iter)
print("第1组输入目标对:", first_batch)
second_batch = next(data_iter)
print("第2组输入目标对:", second_batch)

执行结果:

查看 version 版本号: 0.12.0
查看所有支持的编码: ['gpt2', 'r50k_base', 'p50k_base', 'p50k_edit', 'cl100k_base', 'o200k_base', 'o200k_harmony']
第1组输入目标对: [tensor([[  40,  367, 2885, 1464]]), tensor([[ 367, 2885, 1464, 1807]])]
第2组输入目标对: [tensor([[ 367, 2885, 1464, 1807]]), tensor([[2885, 1464, 1807, 3619]])]

变量 first_batch包含两个张量:

  • 第一个张量存储输入词元ID
  • 第二个张量存储目标词元ID

由于 max_length 被设置为 4,因此这两个张量各自包含 4个词元ID。

注意:这里是为了简单,将输入大小设置为 4,在实际训练大语言模型时,输入大小通常 不小于 256

步长的含义

详见链接:
https://www.malaoshi.top/show_1GW2YXBPPimC.html

批次大小的含义

调用 create_dataloader_v1() 函数时,传值 batch_size=2,其他不变:

dataloader = create_dataloader_v1(raw_text, batch_size=2, max_length=4, stride=1, shuffle=False)
data_iter = iter(dataloader)
first_batch = next(data_iter)
print("第1组输入目标对:", first_batch)
second_batch = next(data_iter)
print("第2组输入目标对:", second_batch)

执行结果:

查看 version 版本号: 0.12.0
查看所有支持的编码: ['gpt2', 'r50k_base', 'p50k_base', 'p50k_edit', 'cl100k_base', 'o200k_base', 'o200k_harmony']
第1组输入目标对: [tensor([[  40,  367, 2885, 1464],
        [ 367, 2885, 1464, 1807]]), tensor([[ 367, 2885, 1464, 1807],
        [2885, 1464, 1807, 3619]])]
第2组输入目标对: [tensor([[2885, 1464, 1807, 3619],
        [1464, 1807, 3619,  402]]), tensor([[1464, 1807, 3619,  402],
        [1807, 3619,  402,  271]])]

每次读取 两组数据


原文出处:/show_1GW2YXCJgYVE.html