OpenAI Tiktoken 分词技术详解:原理、应用与最佳实践

·

在人工智能和自然语言处理的快速发展中,字符串分词技术是 AI 从业者、研究人员和爱好者必须掌握的核心环节。本文深入解析 OpenAI 的字符串分词机制,重点介绍其开源分词器 Tiktoken,探讨基础概念、实际应用和技术细节,帮助读者全面理解现代语言模型中的分词流程。

什么是字符串分词?

字符串分词是将文本转换为更小单元(称为“令牌”或“token”)的过程。这些令牌是大型语言模型(LLM)处理和生成类人文本的基本构建块。OpenAI 的语言模型(包括 GPT-3.5 和 GPT-4)严重依赖分词过程来解析输入并生成输出。

分词在大型语言模型中的关键作用

Tiktoken:OpenAI 的开源分词器

Tiktoken 是 OpenAI 开发的开源分词器,专门用于处理其模型的分词需求。它具有多项关键特性,使其成为使用 OpenAI 语言模型的重要工具。

Tiktoken 的核心特点

Tiktoken 的实际应用

以下通过 Python 代码示例展示 Tiktoken 的基本用法:

import tiktoken

# 获取特定模型的编码
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")

# 将字符串编码为令牌
text = "How long is the Great Wall of China?"
tokens = encoding.encode(text)
print(f"Tokens: {tokens}")
print(f"Number of tokens: {len(tokens)}")

# 将令牌解码回文本
original_text = encoding.decode(tokens)
print(f"Original text: {original_text}")

输出结果:

Tokens: [4438, 1317, 374, 279, 2294, 7147, 315, 5734, 30]
Number of tokens: 9
Original text: How long is the Great Wall of China?

此示例展示了使用 Tiktoken 进行文本编码、令牌计数和解码的基本流程。

模型特定的分词方案

需要注意的是,不同的 OpenAI 模型使用不同的分词方案。以下是一个对比不同模型令牌数量的示例:

def compare_encodings(example_string: str):
    encodings = ["r50k_base", "p50k_base", "cl100k_base"]
    results = {}
    for encoding_name in encodings:
        encoding = tiktoken.get_encoding(encoding_name)
        tokens = encoding.encode(example_string)
        results[encoding_name] = len(tokens)
    return results

text_samples = [
    "How long is the Great Wall of China?",
    "OpenAI's GPT models have revolutionized natural language processing.",
    "人工智能正在改变我们的世界。",  # 中文示例
]

for sample in text_samples:
    print(f"\nSample: '{sample}'")
    results = compare_encodings(sample)
    for encoding, count in results.items():
        print(f"{encoding}: {count} tokens")

输出结果:

Sample: 'How long is the Great Wall of China?'
r50k_base: 9 tokens
p50k_base: 9 tokens
cl100k_base: 9 tokens

Sample: 'OpenAI's GPT models have revolutionized natural language processing.'
r50k_base: 11 tokens
p50k_base: 11 tokens
cl100k_base: 10 tokens

Sample: '人工智能正在改变我们的世界。'
r50k_base: 10 tokens
p50k_base: 10 tokens
cl100k_base: 7 tokens

这一比较表明,虽然简单英文句子的令牌数量可能相似,但对于复杂或非英语文本,令牌数量可能会有显著差异,突显了为每个模型使用正确分词器的重要性。

分词与 API 调用

在向 OpenAI 模型发起 API 调用时,了解令牌计数对于管理成本和优化输入至关重要。以下是一个增强函数,用于计算聊天完成功能的令牌数量,支持多种模型:

def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
    """返回消息列表使用的令牌数量"""
    try:
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        print("Warning: model not found. Using cl100k_base encoding.")
        encoding = tiktoken.get_encoding("cl100k_base")
    
    if model in {
        "gpt-3.5-turbo-0613",
        "gpt-3.5-turbo-16k-0613",
        "gpt-4-0314",
        "gpt-4-32k-0314",
        "gpt-4-0613",
        "gpt-4-32k-0613",
    }:
        tokens_per_message = 3
        tokens_per_name = 1
    elif model == "gpt-3.5-turbo-0301":
        tokens_per_message = 4  # 每条消息格式:<|start|>{role/name}\n{content}<|end|>\n
        tokens_per_name = -1    # 如果有名称,则省略角色
    elif "gpt-3.5-turbo" in model:
        print("Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.")
        return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613")
    elif "gpt-4" in model:
        print("Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.")
        return num_tokens_from_messages(messages, model="gpt-4-0613")
    else:
        raise NotImplementedError(
            f"""num_tokens_from_messages() is not implemented for model {model}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens."""
        )
    
    num_tokens = 0
    for message in messages:
        num_tokens += tokens_per_message
        for key, value in message.items():
            num_tokens += len(encoding.encode(value))
            if key == "name":
                num_tokens += tokens_per_name
    num_tokens += 3  # 每个回复以 <|start|>assistant<|message|> 开头
    return num_tokens

# 示例用法
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "What's the weather like today?"},
    {"role": "assistant", "content": "I'm sorry, but I don't have access to real-time weather information. To get accurate weather details for your location, I recommend checking a reliable weather website or app, or looking outside if possible. Is there anything else I can help you with?"},
    {"role": "user", "content": "Thank you, that's all for now."}
]

print(f"Token count for gpt-3.5-turbo-0613: {num_tokens_from_messages(messages, 'gpt-3.5-turbo-0613')}")
print(f"Token count for gpt-4-0613: {num_tokens_from_messages(messages, 'gpt-4-0613')}")

输出结果:

Token count for gpt-3.5-turbo-0613: 84
Token count for gpt-4-0613: 84

此函数准确计算不同模型版本的令牌数量,考虑了分词方案和消息结构的差异。

令牌定价与模型考量

OpenAI 的定价模式直接与令牌使用量挂钩。了解输入和输出的令牌计数对于成本管理和高效 API 使用至关重要。以下是一些流行 OpenAI 模型的令牌定价概览:

模型输入令牌(每千个)输出令牌(每千个)
GPT-3.5-Turbo$0.0015$0.002
GPT-4 (8K上下文)$0.03$0.06
GPT-4 (32K上下文)$0.06$0.12

注意:价格可能变动,请始终参考 OpenAI 官方定价页面获取最新信息。

令牌使用和定价的关键考虑因素:

高级分词技术

随着语言模型的不断发展,分词技术也在持续演进。一些先进的方法包括:

字节对编码(BPE)

WordPiece

SentencePiece

一元语言模型

👉 查看实时分词工具与最新定价

分词技术的未来发展方向

随着 AI 和 NLP 技术的进步,我们可以期待分词领域的进一步发展:

常见问题

什么是字符串分词?

字符串分词是将文本转换为更小单元(令牌)的过程,这些令牌是语言模型处理文本的基本单位。它使模型能够理解和生成人类语言,是自然语言处理的核心技术之一。

Tiktoken 与其他分词器有何不同?

Tiktoken 是 OpenAI 专门为其语言模型开发的开源分词器,具有可逆性、高速性能和跨语言支持等特点。它与 GPT 系列模型深度集成,提供了优化的分词方案和更好的性能表现。

如何计算 API 调用中的令牌数量?

可以使用 Tiktoken 库的编码功能来计算文本的令牌数量。对于聊天完成 API,需要考虑消息结构、角色标识和模型特定的格式要求,使用专门的函数进行准确计数。

分词如何影响模型成本?

OpenAI 的定价基于令牌使用量,输入和输出令牌都计入成本。有效的分词策略可以帮助减少不必要的令牌使用,优化提示设计,从而降低 API 调用成本。

不同模型的分词方案有何差异?

较新的模型如 GPT-3.5 和 GPT-4 使用基于 cl100k_base 的分词器,而旧版模型可能使用 r50k_base 或 p50k_base。这些差异会影响令牌数量和文本处理方式,特别是对于非英语文本。

如何优化提示以减少令牌使用?

可以通过使用简洁的语言、避免冗余内容、合理组织消息结构等方式减少令牌使用。同时,了解不同语言和格式的令牌效率也有助于优化提示设计。

结语

理解字符串分词,特别是通过 OpenAI 的 Tiktoken,对于任何使用大型语言模型的人都至关重要。它影响着从模型性能到 API 使用成本的方方面面。随着 AI 的持续发展,保持对分词技术及其实际应用的了解,对于 AI 从业者和研究人员都将越来越重要。

通过掌握分词的复杂性,我们能够:

展望未来,分词无疑将继续作为 AI 领域的关键组成部分,与它服务的模型共同演进。无论您是经验丰富的 AI 研究人员还是好奇的开发人员,对 Tiktoken 等分词技术的深入理解,都将对充分利用大型语言模型的潜力和塑造 AI 驱动的自然语言处理的未来具有不可估量的价值。