大模型分词tiktoken、BPE、Sliding Window、Stride、DataLoader批次

tiktoken Python 库

介绍

OpenAI 官方推出的用于计算文本 token 数量的工具,也是各类大语言模型(LLM)计算上下文长度、计费的核心工具

tiktoken 是 OpenAI 开源的快速 BPE(字节对编码)token 编码 / 解码库

相比传统 GPT2Tokenizer 等库,它速度更快、内存占用更低

专门适配 OpenAI 全系列模型(如 gpt-3.5-turbo、gpt-4、gpt-4o 等)token 计算规则

Token:可以理解为大模型处理文本的 "最小单位",1 个 token 约等于英文 4 个字符 / 中文 1-2 个汉字

模型的上下文窗口(如 gpt-4o 的 128k)就是按 token 数量计算的

核心作用:计算一段文本会被模型拆分成多少个 token,这直接关系到 API 调用的费用和上下文是否超限

安装与基础使用

bash 复制代码
pip install tiktoken
python 复制代码
import tiktoken

# 1. 选择对应模型的编码(不同模型的编码规则不同)
# 常用编码映射:
# gpt-3.5-turbo/gpt-4/gpt-4o → "cl100k_base"
# gpt-3.5-turbo-0301 → "p50k_base"
# text-davinci-003 → "p50k_base"
# gpt-2/gpt-3 → "r50k_base"

# 方式1:直接指定编码名称
encoding = tiktoken.get_encoding("cl100k_base")

# 方式2:根据模型名称自动匹配编码(更推荐,适配模型更新)
def get_encoding_by_model(model_name):
    try:
        return tiktoken.encoding_for_model(model_name)
    except KeyError:
        print(f"未找到 {model_name} 的编码,默认使用 cl100k_base")
        return tiktoken.get_encoding("cl100k_base")

# 示例:计算 gpt-4o 模型的 token 数量
model_name = "gpt-4o"
encoding = get_encoding_by_model(model_name)

# 2. 编码(文本 → token 列表)
text = "你好,我是使用 tiktoken 计算 token 的示例。Hello, tiktoken!"
tokens = encoding.encode(text)
print(f"文本:{text}")
print(f"Token 列表:{tokens}")
print(f"Token 数量:{len(tokens)}")  # 输出:19(不同版本可能略有差异)

# 3. 解码(token 列表 → 文本)
decoded_text = encoding.decode(tokens)
print(f"解码后的文本:{decoded_text}")  # 输出原文本

# 4. 批量计算多段文本的 token 数
text_list = ["今天天气很好", "OpenAI tiktoken is fast", "1234567890"]
token_counts = [len(encoding.encode(t)) for t in text_list]
print(f"批量 token 数:{token_counts}")  # 输出:[7, 8, 6]

tiktoken.get_encoding(encoding_name):根据编码名称获取编码对象(如 cl100k_base)

tiktoken.encoding_for_model(model_name):根据模型名称自动匹配对应的编码(推荐,无需记编码名)

encoding.encode(text):将文本转换为 token 整数列表,返回列表长度即为 token 数

encoding.decode(tokens):将 token 列表还原为文本

encoding.encode_single_token(text):将单个 token 文本转换为对应的整数(如 encoding.encode_single_token("你") 返回 8214)

API 调用前校验:确保输入文本的 token 数不超过模型的上下文窗口(如 gpt-4o 最大 128000 token)

计费预估:OpenAI API 按输入 / 输出 token 数计费,提前计算可预估费用

文本截断:当文本过长时,按 token 数截断(而非字符数),避免模型处理失败

示例:文本截断(保留前 10 个 token)

python 复制代码
max_tokens = 10
truncated_tokens = tokens[:max_tokens]
truncated_text = encoding.decode(truncated_tokens)
print(f"截断后的文本:{truncated_text}")  # 输出:你好,我是使用 tiktoken 计算

中文 token 计算:中文每个字 / 标点通常会被拆分为 1-2 个 token,而非按字符数计算(比如 "你好" 是 2 个 token,"," 是 1 个 token)

编码兼容性:不同模型的编码规则不同,必须用对应编码(如 gpt-3.5-turbo 用 cl100k_base,不能用 r50k_base)

速度优势:tiktoken 是用 Rust 实现的核心逻辑,比纯 Python 的 tokenizer 快 10-100 倍,适合批量处理文本

总结

tiktoken 是 OpenAI 官方的高效 token 计算库,核心用于文本与 token 之间的编码 / 解码,是调用 OpenAI API 的必备工具

核心用法:通过 encoding_for_model() 匹配模型编码,用 encode() 计算 token 数,decode() 还原文本

关键注意点:不同模型对应不同编码规则,中文 token 数≠字符数,需按 token 数校验 / 截断文本

核心原理

介绍

BPE(Byte Pair Encoding,字节对编码) 是一种无损数据压缩和子词分词算法

核心思想:从数据中自动学习高频字符/子词对,合并成新符号,反复迭代以减少序列长度

广泛用于 LLM 的 tokenizer(如 GPT、BERT),平衡了词汇表大小和语义完整性

压缩文本序列:减少字符序列的长度,降低存储和计算成本

子词级分词:解决 OOV(未登录词)问题,例如将 unhappiness 拆分为 un + happy + ness,而非单个字符

适配 LLM 需求:让模型既能处理常见词,也能处理罕见词 / 新词

BPE 的执行分为 训练阶段(学习合并规则)和 推理阶段(应用合并规则分词):

训练阶段(学习合并规则)

输入:原始语料库 + 初始词汇表(所有单个字符) + 预设合并次数k

输出:合并规则列表(高频子词对 → 新子词)

步骤详解:

初始化词汇表

将语料中所有文本拆分为单个字符,并在每个词末尾添加结束符(区分词边界,例如low → l o w )

统计每个字符的频率

示例语料(简化):

"low", "low", "newer", "wider", "newer"

拆分后:

l o w , l o w , n e w e r , w i d e r , n e w e r

初始词汇表:{l, o, w, , n, e, r, i, d}

统计字符对频率

遍历所有词,统计相邻字符对的出现次数

示例统计结果:

字符对 频率
(l,o) 2
(o,w) 2
(w,) 2
(n,e) 2
(e,w) 2
(e,r) 2
(r,) 3

合并最高频字符对

找到频率最高的字符对,将其合并为新子词,并添加到词汇表。

示例:最高频对是(r,)(频率 3),合并为r

用新子词替换所有词中的对应字符对

替换后词序列:

l o w , l o w , n e w e r, w i d e r, n e w e r

重复迭代

重复步骤 2-3,共执行k次合并(k是超参数,决定词汇表大小)

每次合并都会生成一条合并规则(例如r + → r)

生成最终词汇表

合并k次后,词汇表包含初始字符 + k个新子词

推理阶段(分词)

输入:待分词文本 + 训练好的合并规则 + 词汇表

输出:子词序列

步骤:

将输入词拆分为单个字符 +

按合并规则的优先级(训练时的合并顺序),依次检查词中是否存在可合并的子词对,若存在则合并

直到无法再合并为止,最终的子词序列即为分词结果

示例:用训练好的规则分词newest

拆分:n e w e s t

应用规则合并e + w → ew → n ew e s t

继续应用规则合并ew + e → ewe → n ewe s t

最终分词:n ewe s t

BPE 关键特点

优点 缺点

解决 OOV 问题,适配新词 合并次数k需人工调参,影响分词效果

词汇表大小可控 无法处理语义无关的高频字符对

训练速度快,易于实现 对词边界依赖强(需标记)

在大模型作用

无需使用特殊词元 <|unk|> 来替换未知单词,解决 OOV 问题

滑动窗口(Sliding Window)进行数据采样

大模型训练使用滑动窗口(Sliding Window) 进行数据采样:是处理超长文本、适配模型上下文窗口(Context Window)的核心技术

能让模型高效学习长文本中的局部依赖关系,同时避免显存溢出

滑动窗口采样的核心逻辑

大模型的上下文窗口(如 GPT-4o 的 128k、Llama 3 的 8k)是固定的,而训练语料往往是远超窗口长度的超长文本(如整本书、长文档)

滑动窗口采样的核心:

将超长文本切割成多个重叠 / 不重叠的固定长度窗口(窗口长度 = 模型上下文窗口长度)

每个窗口作为一个独立的训练样本,输入模型进行训练

通过步长(Stride) 控制窗口间的重叠程度,平衡数据利用率和训练效率

stride straɪd

n. 大步,阔步;步态,步伐;步距,步幅;步速;进展,进步;<非正式>裤子(strides);钢琴跨越弹奏法

v. 大步走,阔步走;跨越,跨过;<文>跨坐在......上,跨立在......上

关键概念

概念 含义 示例(模型窗口长度 = 10)

窗口长度(Window Size) 单个样本的 token 数,等于模型上下文窗口长度 10 个 token

步长(Stride) 窗口滑动的 token 数 步长 = 5 → 相邻窗口重叠 5 个 token

重叠率 相邻窗口的重叠 token 比例 步长 = 5 → 重叠率 = 50%

重叠token数 = window_size - stride

重叠率 = (window_size - stride) / window_size × 100%

示例:

window_size=1024, stride=512 → 重叠 token 数 = 512 → 重叠率 = 50%

window_size=1024, stride=1024 → 重叠 token 数 = 0 → 重叠率 = 0%

window_size=1024, stride=256 → 重叠 token 数 = 768 → 重叠率 = 75%

滑动窗口采样的实现

基础版(无重叠) 和进阶版(带重叠),并兼容 tiktoken 计算 token 数:

python 复制代码
import tiktoken
from typing import List, Tuple

# 初始化tokenizer(适配GPT-4o)
tokenizer = tiktoken.encoding_for_model("gpt-4o")

def sliding_window_sampling(
    text: str,
    window_size: int = 1024,  # 模型上下文窗口长度
    stride: int = 512,        # 滑动步长
    add_eos: bool = True      # 是否在每个窗口末尾添加EOS token
) -> List[Tuple[List[int], List[int]]]:
    """
    对超长文本进行滑动窗口采样,生成(输入token, 目标token)训练对
    :param text: 原始超长文本
    :param window_size: 窗口长度(token数)
    :param stride: 滑动步长(token数)
    :param add_eos: 是否添加EOS token(标记文本结束)
    :return: 列表,每个元素是(输入token列表, 目标token列表),目标比输入右移一位(自回归训练)
    """
    # 1. 将文本编码为token序列
    tokens = tokenizer.encode(text)
    if add_eos:
        eos_token = tokenizer.encode_single_token("<|endoftext|>")  # OpenAI EOS token
        tokens.append(eos_token)
    
    # 2. 边界检查
    if len(tokens) <= window_size:
        # 文本长度≤窗口长度,直接作为单个样本
        input_tokens = tokens[:-1]  # 输入:最后一个token除外
        target_tokens = tokens[1:]  # 目标:右移一位(自回归训练)
        return [(input_tokens, target_tokens)]
    
    # 3. 滑动窗口切割
    train_pairs = []
    start_idx = 0
    max_idx = len(tokens) - 1  # 预留最后一个token作为目标
    
    while start_idx <= max_idx - window_size:
        # 截取当前窗口的token(输入窗口长度=window_size)
        end_idx = start_idx + window_size
        window_tokens = tokens[start_idx:end_idx + 1]  # +1是为了目标右移
        
        # 生成输入-目标对(自回归训练:输入→目标,目标比输入右移一位)
        input_tokens = window_tokens[:-1]
        target_tokens = window_tokens[1:]
        
        train_pairs.append((input_tokens, target_tokens))
        
        # 滑动窗口
        start_idx += stride
    
    # 处理最后一个窗口(避免尾部文本被截断)
    if start_idx < max_idx:
        window_tokens = tokens[-window_size - 1:]  # 取最后window_size+1个token
        input_tokens = window_tokens[:-1]
        target_tokens = window_tokens[1:]
        train_pairs.append((input_tokens, target_tokens))
    
    return train_pairs

# ---------------------- 示例使用 ----------------------
if __name__ == "__main__":
    # 超长文本示例(模拟书籍/长文档)
    long_text = """
    人工智能(AI)是一门旨在使机器模拟人类智能的技术科学。它涵盖了机器学习、自然语言处理、计算机视觉等多个领域。
    机器学习是AI的核心分支,通过数据训练模型,让模型自主学习规律。深度学习是机器学习的子集,基于神经网络实现复杂任务。
    大语言模型(LLM)是深度学习的应用之一,通过海量文本训练,能够理解和生成人类语言。GPT、Llama、文心一言等都是典型的LLM。
    长文本处理是LLM的关键挑战之一,滑动窗口采样是解决该问题的核心方法,能让模型高效学习长文本中的上下文依赖。
    模型的上下文窗口长度决定了其能处理的文本长度,滑动窗口则能将超长文本切割为适配窗口的样本,平衡训练效率和效果。
    """
    
    # 滑动窗口采样(窗口长度=20,步长=10,模拟小窗口模型)
    train_samples = sliding_window_sampling(
        text=long_text,
        window_size=20,
        stride=10
    )
    
    # 打印采样结果
    print(f"原始文本token总数:{len(tokenizer.encode(long_text))}")
    print(f"采样得到的训练样本数:{len(train_samples)}")
    print("\n第一个样本:")
    print(f"输入token(前10个):{train_samples[0][0][:10]}")
    print(f"输入文本:{tokenizer.decode(train_samples[0][0])}")
    print(f"目标token(前10个):{train_samples[0][1][:10]}")
    print(f"目标文本:{tokenizer.decode(train_samples[0][1])}")

代码关键解释

token 编码:使用tiktoken将文本转为 token 序列,适配 OpenAI 系模型的编码规则

自回归训练对:输入 token 是窗口内前 N 个,目标 token 是后 N 个(右移一位),符合大模型自回归生成的训练逻辑

滑动逻辑:

按stride步长滑动,直到覆盖整个文本

最后一个窗口强制截取尾部文本,避免丢失信息

重叠控制:

步长越小,窗口重叠越多(如步长 = 窗口长度→无重叠,步长 = 窗口长度 / 2→50% 重叠)

滑动窗口采样的进阶优化

动态窗口长度

针对文本中的自然边界(如段落、句子)调整窗口,避免切割语义完整的单元

python 复制代码
def split_by_sentence(text: str) -> List[str]:
    """按句子分割文本,避免窗口切割句子"""
    import re
    return re.split(r'[。!?;]', text)

def dynamic_window_sampling(text: str, window_size: int, stride: int):
    sentences = split_by_sentence(text)
    # 合并句子直到接近窗口长度,再滑动
    merged_texts = []
    current = ""
    for sent in sentences:
        if len(tokenizer.encode(current + sent)) < window_size:
            current += sent
        else:
            merged_texts.append(current)
            current = sent
    merged_texts.append(current)
    
    # 对合并后的文本进行滑动窗口采样
    all_samples = []
    for merged in merged_texts:
        samples = sliding_window_sampling(merged, window_size, stride)
        all_samples.extend(samples)
    return all_samples

随机滑动窗口(增强泛化性)

训练时随机调整窗口起始位置,避免模型过拟合固定窗口边界

python 复制代码
def random_sliding_window_sampling(
    text: str,
    window_size: int,
    num_samples: int = 5
) -> List[Tuple[List[int], List[int]]]:
    """随机采样窗口,而非顺序滑动"""
    tokens = tokenizer.encode(text)
    if len(tokens) <= window_size:
        return [(tokens[:-1], tokens[1:])]
    
    train_pairs = []
    for _ in range(num_samples):
        # 随机选择起始位置
        start_idx = random.randint(0, len(tokens) - window_size - 1)
        end_idx = start_idx + window_size
        window_tokens = tokens[start_idx:end_idx + 1]
        input_tokens = window_tokens[:-1]
        target_tokens = window_tokens[1:]
        train_pairs.append((input_tokens, target_tokens))
    return train_pairs

滑动窗口采样的适用场景与注意事项

适用场景

超长文本训练:如书籍、论文、代码库等远超模型上下文窗口的文本

长上下文模型预训练:提升模型对长文本的依赖关系捕捉能力

低显存训练:单个窗口样本显存占用可控,避免 OOM(显存溢出)

注意事项

步长选择:

步长 = 窗口长度:无重叠,训练效率高,但可能丢失跨窗口依赖

步长 = 窗口长度 / 2:50% 重叠,平衡效率和效果(主流选择)

语义完整性:尽量按自然边界(句子、段落)切割,避免窗口切割核心语义

窗口长度对齐:窗口长度需等于模型的上下文窗口长度,否则需 padding/truncation

总结

滑动窗口采样的核心是将超长文本切割为固定长度、可重叠的窗口样本,适配大模型的上下文窗口限制

核心参数是窗口长度(等于模型上下文长度) 和步长(控制重叠率),步长越小重叠越多,数据利用率越高

进阶优化可通过动态窗口(按语义分割) 和随机窗口(增强泛化) 提升训练效果,同时需注意语义完整性和显存占用

DataLoader中的批次的概念

DataLoader批次为1和4的区别,stride=4,以 in the heart of the city stood the old library, a relic from a bygone era.

DataLoader 加载文本时,通常会先把句子拆分为词 / Token 序列,再按 batch_size(批次大小)和 stride(步长)切分成批量的样本:

batch_size:每一批次加载的样本数量;

stride:滑动窗口每次移动的 Token 数(决定样本间的重叠 / 间隔);

先将例句拆分为 Token 序列(按空格拆分,忽略标点,共 16 个 Token):

plaintext 复制代码
[0:in, 1:the, 2:heart, 3:of, 4:the, 5:city, 6:stood, 7:the, 8:old, 9:library, 10:a, 11:relic, 12:from, 13:a, 14:bygone, 15:era]

场景 1:batch_size=1,stride=4

核心逻辑:每次只加载 1 个样本,滑动窗口每次移动 4 个 Token,样本间无重叠(因为 stride=4 等于窗口长度,这里假设窗口长度 = 4,是 NLP 中常见的 "固定窗口切分")。

具体切分过程(窗口长度 = 4):

批次序号 加载的样本(Token 索引) 样本内容 批次内样本数

1 [0,1,2,3] in the heart of 1

2 [4,5,6,7] the city stood the 1

3 [8,9,10,11] old library a relic 1

4 [12,13,14,15] from a bygone era 1

关键特点:

总共生成 4 个批次,每个批次只有 1 个样本;

每个样本是连续的 4 个 Token,且样本间完全不重叠(因为 stride=4 刚好跳过前一个样本的所有 Token);

数据加载次数多(4 次),每次处理的数据量小,适合显存 / 内存极小的场景,但效率低。

场景 2:batch_size=4,stride=4

核心逻辑:每次加载 4 个样本,滑动窗口仍移动 4 个 Token,一次性把所有切分好的样本加载完成(因为总样本数 = 4,刚凑成 1 批次)。

具体切分过程(窗口长度 = 4):

批次序号 加载的样本(Token 索引) 样本内容 批次内样本数

1 [0,1,2,3] in the heart of 4

4,5,6,7\] the city stood the \[8,9,10,11\] old library a relic \[12,13,14,15\] from a bygone era 关键特点: 总共只生成 1 个批次,一次性加载所有 4 个样本; 样本内容和 batch_size=1 时完全一致,但加载次数从 4 次减少到 1 次,效率大幅提升 要求显存 / 内存能容纳 4 个样本的数据量,是深度学习中更常用的配置(批量处理提升计算效率) 延伸:如果窗口长度 \> stride(比如窗口长度 = 6,stride=4) 若窗口长度大于步长,样本会出现重叠,两种批次大小的差异会更明显: batch_size=1:批次 1 加载 \[0-5\],批次 2 加载 \[4-9\](重叠 Token 4-5),批次 3 加载 \[8-13\],批次 4 加载 \[12-15\](补零),共 4 批次; batch_size=4:1 个批次直接加载 \[0-5\]、\[4-9\]、\[8-13\]、\[12-15\] 这 4 个重叠样本,一次性处理。 ### 核心区别总结 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5fb8bc334c0e4cf3b719e2e0bfc65eaa.png) ### 总结 batch_size 只影响 "每批加载的样本数",不改变样本本身的内容 / 切分逻辑(stride 固定时) stride=4 决定了样本间的间隔 / 重叠,和 batch_size 无关 实际开发中,batch_size=4 是更高效的选择(充分利用硬件并行能力),而 batch_size=1 仅用于调试或资源极度受限的场景 两者的核心差异是加载效率和资源占用,而非样本本身的内容:batch_size 越大,加载次数越少,计算效率越高,但对硬件的要求也越高 ## 过多重叠的问题 模型过拟合风险

相关推荐
美酒没故事°20 小时前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
鸿乃江边鸟20 小时前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
本旺20 小时前
【Openclaw 】完美解决 Codex 认证失败
ai·codex·openclaw·小龙虾·gpt5.4
张張40821 小时前
(域格)环境搭建和编译
c语言·开发语言·python·ai
乐鑫科技 Espressif21 小时前
使用 MCP 服务器,把乐鑫文档接入 AI 工作流
人工智能·ai·esp32·乐鑫科技
语戚21 小时前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
俊哥V21 小时前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
rrrjqy1 天前
什么是RAG?
ai
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai