OpenAI API:在发送API请求之前,我如何计算令牌数量?

51

OpenAI的文本模型有一个上下文长度,例如:Curie的上下文长度为2049个标记。

它们提供max_tokens和stop参数来控制生成序列的长度。因此,当获取停止标记或达到max_tokens时,生成过程会停止。

问题是,在生成文本时,我不知道我的提示包含多少标记。由于我不知道这一点,我无法设置max_tokens = 2049 - 提示中的标记数。

这使我无法根据其长度动态生成各种文本。我需要的是一直生成直到遇到停止标记。

我的问题是:

  • 如何在Python API中计算标记数量?以便我可以相应地设置max_tokens参数。
  • 是否有一种方式可以将max_tokens设置为最大值,这样我就不需要计算提示标记的数量了?
3个回答

61
正如在官方的OpenAI文章中所述:

为了进一步探索标记化,您可以使用我们的交互式Tokenizer工具,它允许您计算标记的数量并查看文本如何被分解成标记。 或者,如果您想以编程方式对文本进行标记化,请使用Tiktoken作为专门用于OpenAI模型的快速BPE标记器。您还可以探索其他类似的库,例如Python的transformers package或NodeJS的gpt-3-encoder package

一个分词器可以将文本字符串分割成一个标记列表,正如在关于使用Tiktoken计算标记的官方OpenAI示例中所述:

Tiktoken是OpenAI的一个快速开源分词器。

给定一个文本字符串(例如"tiktoken is great!")和一个编码(例如"cl100k_base"),分词器可以将文本字符串分割成一个标记列表(例如["t", "ik", "token", " is", " great", "!"])。

将文本字符串分割成标记是有用的,因为GPT模型以标记的形式看待文本。知道一个文本字符串中有多少个标记可以告诉您:

  • 字符串是否过长,无法由文本模型处理
  • OpenAI API调用的成本(使用按标记计费)
Tiktoken支持OpenAI模型使用的3种编码方式(source):
编码名称 OpenAI模型
cl100k_base gpt-4gpt-3.5-turbotext-embedding-ada-002
p50k_base text-davinci-003text-davinci-002
r50k_base GPT-3模型
对于cl100k_basep50k_base编码方式:

对于r50k_base编码,许多语言都提供了分词器:

Python: tiktoken(或者GPT2TokenizerFast
JavaScript: gpt4-tokenizergpt3-tokenizergpt-3-encoder .NET / C#: GPT Tokenizer Java: gpt2-tokenizer-java PHP: GPT-3-Encoder-PHP 请注意,gpt-3.5-turbogpt-4与其他模型一样使用标记,正如官方OpenAI文档所述:

gpt-3.5-turbogpt-4这样的聊天模型与其他模型一样使用标记,但由于其基于消息的格式,很难计算出一个对话将使用多少个标记。

如果一个对话的标记数量超过了模型的最大限制(例如gpt-3.5-turbo的4096个标记),您将不得不缩短、省略或以其他方式减少文本长度,直到符合要求。请注意,如果从消息输入中删除了一条消息,模型将完全失去对它的了解。

还要注意,非常长的对话更有可能收到不完整的回复。例如,一个长度为4090个标记的gpt-3.5-turbo对话将在仅有6个标记后被截断。

如何使用tiktoken?

  1. 安装或升级tiktoken: pip install --upgrade tiktoken

  2. 您有两个选项。

选项1:在上面的表格中搜索给定OpenAI模型的正确编码

如果您运行get_tokens_1.py,您将得到以下输出:

9

get_tokens_1.py

import tiktoken

def num_tokens_from_string(string: str, encoding_name: str) -> int:
    encoding = tiktoken.get_encoding(encoding_name)
    num_tokens = len(encoding.encode(string))
    return num_tokens

print(num_tokens_from_string("Hello world, let's test tiktoken.", "cl100k_base"))

选项2:使用tiktoken.encoding_for_model()自动加载给定OpenAI模型的正确编码

如果你运行get_tokens_2.py,你将得到以下输出:

9

get_tokens_2.py

import tiktoken

def num_tokens_from_string(string: str, encoding_name: str) -> int:
    encoding = tiktoken.encoding_for_model(encoding_name)
    num_tokens = len(encoding.encode(string))
    return num_tokens

print(num_tokens_from_string("Hello world, let's test tiktoken.", "gpt-3.5-turbo"))

注意:如果你仔细观察OpenAI API的响应中的“usage”字段,你会发现它报告了使用了10个标记的相同消息。这比Tiktoken多了1个标记。我还没有弄清楚为什么。我在过去进行了测试(请参阅我的past answer)。正如@Jota在下面的评论中提到的,OpenAI API响应报告的标记使用情况与Tiktoken之间似乎仍然存在不匹配。

2
有没有适用于NodeJS的Tiktok API? - Anshuman Kumar
1
@AnshumanKumar 是的:https://www.npmjs.com/package/@dqbd/tiktoken - Rok Benko
1
Chat GPT指定的函数计算出的令牌与响应中Chat GPT返回的令牌不匹配...无法计算max_tokens变量。 - Jota
2
为什么这个答案比tiktoken的文档更好? - Rami Awar
1
@RamiAwar 很高兴听到这个消息。:) 可能是因为我结合了tiktoken文档、OpenAI文档和我个人测试过的代码。 - Rok Benko
显示剩余3条评论

3
这是我用Python 3的方法。 然后你可以传递模型名称或编码字符串。你可以获取编码、标记或标记计数。

token_helper.py:

import tiktoken

def encoding_getter(encoding_type: str):
    """
    Returns the appropriate encoding based on the given encoding type (either an encoding string or a model name).
    """
    if "k_base" in encoding_type:
        return tiktoken.get_encoding(encoding_type)
    else:
        return tiktoken.encoding_for_model(encoding_type)

def tokenizer(string: str, encoding_type: str) -> list:
    """
    Returns the tokens in a text string using the specified encoding.
    """
    encoding = encoding_getter(encoding_type)
    tokens = encoding.encode(string)
    return tokens

def token_counter(string: str, encoding_type: str) -> int:
    """
    Returns the number of tokens in a text string using the specified encoding.
    """
    num_tokens = len(tokenizer(string, encoding_type))
    return num_tokens

这是如何工作的
>>> import token_helper
>>> token_helper.token_counter("This string will be counted as tokens", "gpt-3.5-turbo"))
7

1

根据注释中的信息,我制作了这个文件:https://gist.github.com/buanzo/7cdd2c34fc0bb25c71b857a16853c6fa

它是一个 count_tokens 实现,尝试使用 tiktoken、nltk 并回退到 .split()。

它还包括一个简单的 TokenBuffer 实现。

我们可以从 token_counter 模块中导入 count_tokens 函数,并按以下方式调用它,将文本字符串作为参数:

from token_counter import count_tokens
text = "The quick brown fox jumps over the lazy dog."
result = count_tokens(text, debug=True)
print(result)

如果所有必需的库都可用,则结果更好,但即使没有tiktoken或nltk,该函数也应返回一个包含标记数量和计数方法的字典。例如:
{'n_tokens': 9, 'method': 'tiktoken'}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接