当前位置: 首页 > news >正文

[nanoGPT] 数据预处理与分词 | BPE配合`tiktoken`

第一章:数据预处理与分词

想象你是一位厨师,目标是烤制美味的蛋糕。

不能直接把生鸡蛋、面粉和糖扔进烤箱。首先需要准备食材:打散鸡蛋、称量面粉、甚至过筛去除结块。

这些准备工作确保食材以正确的形态和比例进入烘焙流程。

在GPT这类大语言模型(LLM)的世界里,情况非常相似

我们的"厨师"是GPT模型,"食材"则是海量的人类书写文本。nanoGPT项目旨在构建一个迷你GPT模型,而在模型开始学习之前,我们需要专门的"厨房团队"来预处理原始文本数据——这正是数据预处理与分词的核心任务。

本章将完整展示nanoGPT如何将原始文本切分并转化为模型可消化的数字"餐点"。我们的核心目标是理解

如何将"Hello world!"这样的文本转换为训练所需的数字序列。

为什么需要数据预处理?

根本问题在于计算机(尤其是GPT这类神经网络)无法直接理解单词或字符,它们只认识数字

因此,我们的首要任务是将所有文本(故事、文章、诗歌等)转换为整数序列。

预处理还包含其他关键步骤:

  1. 获取数据:寻找并下载大规模文本集合
  2. 数据分割:将文本划分为"训练集"(模型学习素材)和"验证集"(用于检查学习效果的"模拟考试")
  3. 分词:将文本转化为数字标记的核心过程
  4. 高效存储:以模型能快速加载的方式保存这些数字

让我们通过实例了解nanoGPT如何处理这些步骤。

获取与分割文本数据

在文本转数字之前,我们需要原始文本

nanoGPT提供从网络下载或使用本地文件的脚本

data/shakespeare_char/prepare.py脚本为例,该脚本设计用于在字符级别处理莎士比亚作品小数据集:

import os
import requests  # 用于文件下载# 定义文本文件保存路径
input_file_path = os.path.join(os.path.dirname(__file__), 'input.txt')# 若文件不存在则下载
if not os.path.exists(input_file_path):data_url = 'https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt'with open(input_file_path, 'w') as f:f.write(requests.get(data_url).text)# 读取全部文本内容
with open(input_file_path, 'r') as f:data = f.read()
print(f"数据集字符长度: {len(data):,}")

这种脚本获取原数据操作在前文[项目详解][boost搜索引擎#1] 概述 | 去标签 | 数据清洗 | scp亦有用到

这段代码首先检查input.txt(微型莎士比亚数据集)是否存在,若不存在则从GitHub下载保存

随后将全部内容读入data变量,此时data即保存了待处理的原始文本。

接下来将data分割为两部分:大部分用于训练模型,小部分用于验证

# 创建训练集和验证集
n = len(data)
train_data = data[:int(n*0.9)]  # 90%训练集
val_data = data[int(n*0.9):]    # 10%验证集

防过拟合小tip

这里90%的莎士比亚文本进入train_data供模型学习,剩余10%进val_data用于定期检查模型在未见文本上的表现——这对防止模型死记硬背训练数据至关重要。

分词:文本转数字

这是最关键的步骤。分词即将文本拆分为称为**标记(token)**的小单元,并为每个标记分配唯一ID

nanoGPT演示了两种主要分词方法:

方法1:字符级分词(简单版)

字符级分词将每个独立字符(如’a’、‘b’、’ ‘、’!')视为一个标记。这是最基础的方法,适合理解核心概念。

继续看data/shakespeare_char/prepare.py。加载数据后,脚本识别所有独特字符:

# 获取文本中所有独特字符
chars = sorted(list(set(data)))
vocab_size = len(chars)  # 独特字符总数
print("全部独特字符:", ''.join(chars))
print(f"词汇表大小: {vocab_size:,}")# 创建字符到整数的映射(stoi = string to integer)
stoi = { ch:i for i,ch in enumerate(chars) }
# 创建整数到字符的逆向映射(itos = integer to string)
itos = { i:ch for i,ch in enumerate(chars) }# 文本与标记ID的转换函数
def encode(s):return [stoi[c] for c in s]  # 编码器:输入字符串,输出整数列表
def decode(l):return ''.join([itos[i] for i in l])  # 解码器:输入整数列表,输出字符串

这部分代码首先找出莎士比亚文本中的所有独特字符(如’a’、‘b’、‘c’、‘.’、’ ‘、’!'),然后创建两个"字典":

  • stoi(字符到整数)将每个字符映射为唯一数字(标记ID)。例如’a’对应0,'b’对应1
  • itos(整数到字符)是逆向映射

vocab_size表示数据集中独特字符(即独特标记ID)的数量。微型莎士比亚数据集通常约65个。

现在可以用encode函数将train_dataval_data转为数字列表:

# 将训练集和验证集编码为整数
train_ids = encode(train_data)
val_ids = encode(val_data)
print(f"训练集标记数: {len(train_ids):,}")
print(f"验证集标记数: {len(val_ids):,}")

此时train_idsval_ids就是模型所需的长整数列表

例如文本"Hello"可能变为[20, 10, 23, 23, 26](假设’H’=20,‘e’=10,‘l’=23,‘o’=26)。

方法2:字节对编码(BPE)配合tiktoken(进阶版,LLM常用)

字符级分词虽简单,但对大文本效率低

例如常见词"the"会被拆为3个标记(‘t’,‘h’,‘e’),尽管它们常一起出现。BPE通过为常见字符序列(甚至完整单词)创建标记来解决这个问题。

nanoGPT使用OpenAI的tiktoken库实现BPE处理更真实的数据集。查看data/shakespeare/prepare.py(注意与shakespeare_char的区别):

import tiktoken
import numpy as np  # 用于高效数值处理# ...(数据下载与分割逻辑同前)...# 使用tiktoken的gpt2 bpe编码器
enc = tiktoken.get_encoding("gpt2")
train_ids = enc.encode_ordinary(train_data)
val_ids = enc.encode_ordinary(val_data)
print(f"训练集标记数: {len(train_ids):,}")
print(f"验证集标记数: {len(val_ids):,}")

这里不再手动创建stoiitos,而是使用tiktoken.get_encoding("gpt2")加载OpenAI为GPT-2预训练的BPE分词器。该分词器已掌握将文本拆分为常见片段(子词)并分配ID的方法

encode_ordinary()方法用这个强大的BPE分词器将train_dataval_data转为整数列表。

这些标记ID通常更大(GPT-2最多50256),因为它们代表比单个字符更复杂的标记。

本质其实还是我们一层不够高,那就再套一层实现的思想

对于超大数据集(如网络文本集合"OpenWebText"),nanoGPTdata/openwebtext/prepare.py中结合使用tiktoken和Hugging Face的datasets库实现高效加载处理:

import tiktoken
from datasets import load_dataset  # huggingface数据集库enc = tiktoken.get_encoding("gpt2")if __name__ == '__main__':dataset = load_dataset("openwebtext", num_proc=8)  # 加载海量数据集# ...(分割逻辑)...def process(example):ids = enc.encode_ordinary(example['text'])  # 用tiktoken编码文本ids.append(enc.eot_token)  # 添加文本结束特殊标记out = {'ids': ids, 'len': len(ids)}return outtokenized = dataset["train"].map(  # 对整个数据集应用分词process,remove_columns=['text'],desc="正在分词",num_proc=8,)# ...(保存逻辑)...

这段代码显示即使对超大数据集,核心的enc.encode_ordinary()方法保持不变

datasetsmap函数帮助高效处理数百万文档。enc.eot_token是特殊标记(文本结束),用于区分不同文档内容。

保存为二进制文件

所有文本转为整数ID列表后,nanoGPT需要高效保存它们。相比纯文本文件(体积大加载慢),它们被存储为二进制文件.bin)。

import numpy as np  # 数值计算库# ...(分词完成,得到train_ids和val_ids)...# 导出为bin文件
train_ids = np.array(train_ids, dtype=np.uint16)  # 列表转NumPy数组
val_ids = np.array(val_ids, dtype=np.uint16)      # 使用uint16节省内存# 将数组直接保存为二进制文件
train_ids.tofile(os.path.join(os.path.dirname(__file__), 'train.bin'))
val_ids.tofile(os.path.join(os.path.dirname(__file__), 'val.bin'))

这段代码用numpytrain_idsval_ids列表转为高效数组。

  • dtype=np.uint16指定每个数字存为16位无符号整数(GPT-2的标记ID最大值50256在此范围内,因2^16=65536)。
  • .tofile()生成紧凑的二进制文件(train.binval.bin),训练时可快速加载。

字符级分词还会额外保存meta.pkl文件存储stoiitos映射,便于后续将标记ID转回可读字符:(memo)

import pickle  # 用于保存Python对象# ...(vocab_size, itos, stoi已定义)...# 保存元信息供后续编码/解码使用
meta = {'vocab_size': vocab_size,'itos': itos,'stoi': stoi,
}
with open(os.path.join(os.path.dirname(__file__), 'meta.pkl'), 'wb') as f:pickle.dump(meta, f)

meta.pkl相当于小型字典供模型参考。

如何使用数据预处理脚本

可通过终端运行这些预处理脚本。例如用字符级分词处理微型莎士比亚数据集:

python data/shakespeare_char/prepare.py

运行后将看到类似输出:

数据集字符长度: 1,115,394
全部独特字符:!$&',-.3:;?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
词汇表大小: 65
训练集标记数: 1,003,854
验证集标记数: 111,540

data/shakespeare_char/目录会生成train.binval.binmeta.pkl

处理BPE版微型莎士比亚数据集:

python data/shakespeare/prepare.py

将在data/shakespeare/生成train.binval.bin

处理更大的OpenWebText数据集:

python data/openwebtext/prepare.py

注意:这将下载超大数据集(54GB),耗时耗空间

最终在data/openwebtext/生成train.bin(17GB)和`val.bin`(8.5MB)。

技术原理:数据预处理流程

用序列图展示字符级莎士比亚数据处理的完整流程:

在这里插入图片描述

该图展示了原始文本从网络到Python处理,最终存储为高效二进制文件的完整旅程。

分词方法对比

以下是nanoGPT使用的两种分词方法对比:

特性字符级分词(如shakespeare_char)字节对编码(BPE)配合tiktoken(如shakespeare, openwebtext)
标记单元单个字符(如’H’,‘e’,‘l’,‘l’,‘o’)常见子词或完整单词(如"Hello",“world”,“the”)
词汇表大小较小(如微型莎士比亚约65)较大(如GPT-2的tiktoken约50,000)
复杂度更简单易懂更复杂,采用最优子词查找算法
输出长度相同文本产生更长标记序列相同文本产生更短标记序列(更高效)
适用场景小数据集/教学用途/需精细字符控制的特定任务大语言模型标准方案,高效处理多样化文本

本章小结

本章我们学习了nanoGPT如何为模型准备"食材":下载原始文本、分割为训练验证集、最重要的是将其转化为数字标记。

  • 我们探索了两种分词方法:简单的字符级方案和高效但更复杂的BPE方案(使用tiktoken)。

  • 最终这些标记ID被存入紧凑的二进制文件(.bin),为训练阶段的高速加载做好优化。

正如厨师需要精心准备的食材,我们的GPT模型需要妥善处理的数字数据。现在数据已就绪,接下来将认识"厨师"本身——GPT模型架构=。

下一章:GPT模型架构

http://www.dtcms.com/a/521182.html

相关文章:

  • 搜索引擎收录入口飞猪关键词排名优化
  • 酒店手机网站模板电子商务网站建设与实践
  • 中国公路建设招标网站开发软件网站
  • 开发做一个网站的流程客源引流推广app
  • 南通网站建设总结如何应用网络营销发挥作用
  • CSS 总结
  • Idea 查找未使用的代码
  • 网站二级域名查询wordpress详情页图片放大
  • 首钢建设公司网站wordpress如何开启gzip
  • 淘宝商品详情数据 API 技术解析与实战指南
  • C++ STL <algorithm>中泛型算法:查找、排序、修改、统计、生成
  • 中石化石油工程建设公司官方网站wordpress留言板页面怎么制作
  • 天津网站建设方案咨询动漫制作专业研究生考啥
  • FLUMINER福禄T3 115T:比特币挖矿的新选择,如何提升效率与稳定性?
  • html5手机网站适配电影网站带采集
  • 深圳品牌医疗网站建设医药电子商务网站建设
  • 备案域名指向一个网站保定网站设计公司
  • 麒麟信创系统安装pgsql-15.4 报错 popen failure: Cannot allocate memory
  • LeetCode 面试经典 150_链表_合并两个有序链表(58_21_C++_简单)
  • [优选算法专题四.前缀和——NO.31~32 连续数组、矩阵区域和]
  • 事业单位网站备案流程谷歌做不做网站
  • 做家居网站企业建设网站的目标
  • 福田网站改版安徽六安特产
  • XML 核心知识点
  • 图片类网站模板自己做网站是用什么软件
  • 全国通用工会考试真题及答案解析
  • 东莞寮步镇网站做网站需要自己研发吗
  • QLExpress 解析方程式
  • AI编程:结合MCP实现数据库连接与高效开发
  • 在线做网站有哪些平台wordpress二级