RAG(检索增强生成)的基础知识总结: 关于token

一、关于token

1.1 什么是token

token可以被认为是单词(词语)的片段, 在用户对大语言模型提出问题的时候,我们的输入(prompt)会被分解成一组token, 在英语的句子中单词之家存在空格,一些常见的单词可能会被切割成独立的token, 但并非所有的单词都会被切割成独立的token, 有时候一个单词可能会被切割成多个token, token可以包含尾随在单词后面空格甚至子单词。以下是一些帮助理解token长度的经验规则:

  • 1 个token ~= 4 个英文字符
  • 1 个token ~= 3/4 个单词
  • 100 个token ~= 75 个单词

​​​​​或者

  • 1-2 个句子 ~= 30 tokens
  • 1个段落 ~= 100 tokens
  • 1500个单词 ~= 2048 tokens

因此可以这么认为一个句子,或者一篇文章它们的token总数往往会大于句子或者文章的单词总数。

1.2 token的计数规则

Token的计数方式与所使用的语言有关。例如,西班牙语中的“Cómo estás”(意为"How are you") 包含5个Token(共10个字符)。对于非英语语言,更高的Token与字符比率可能会使得使用API的成本更高。下面我们看一个例子:

在上面这个例子中句子:"My favorite color is red." 被用不同颜色分割,不同单词所在的色块中有的单词前面包含了空格如" red",有的单词前后都不包含空格如"My",句子末尾的句点被单独分配了一个色块。那么整个句子可以被认为是被切割成了6个token,每个token的id由上图中的list表示如:[3666,4004,3124,318,2266,13]。再看下面的例子:

 在上面这个例子中我们发现句子中的单词" red" 由原来小写变成了大写的“ Red”, 因此它的tokenId也发生了变化(由原来的2266变成了2297), 这说明单词的大小写也会产生不通的token。再看下面的例子:

 在上面的例子中单词"Red" 前后都没有包含空格它的tokenId是7738,这与之前包含前导空格的" Red"的tokenId(2297)是不同的。

token出现的可能性/频率越高,分配给它的token编号就越低:

  • 在所有 3 个句子中,相同的tokenId只有一个("13")即句号的tokenId。 这是因为,从上下文来看,整个语料库数据中使用的句号方式非常相似。
  • 为“red”生成的token根据其在句子中的位置而变化:

句子中间小写的"red"(tokenId:“2266”)
句子中间大写的"Red" (tokenId:“2297”)
句子开头大写的"Red"(tokenId:“7738”)

1.4 token的限制

根据所使用的模型,调用OpenAI的模型可以使用最多128000个Token,包括模型在输入(prompt)和输出(completion)之间的共享。某些模型,如GPT-4 Turbo,对输入和输出Token有不同的限制。

二、token计数

tiktoken 是 OpenAI 推出的快速开源token计数器工具。比如给定一个文本字符串(例如,“tiktoken is great!”)和编码(encoding 比如,“cl100k_base”),分词器可以将文本字符串拆分为token列表(例如, ["t", "ik", "token", " is", " great", "!"])。将文本字符串拆分为token非常有用,因为 GPT 模型以token的形式理解文本。 了解文本字符串中有多少个token可以告诉您 (1) 该字符串是否太长而无法让文本模型处理,以及 (2) OpenAI API 调用的成本是多少(因为使用量是按token使用数量来定价的)。

编码(Encodings)
编码(Encodings)的作用是将文本转换为token的技术。 不同的模型使用不同的编码。tiktoken 支持 OpenAI 模型使用的三种编码:

您可以使用 tiktoken.encoding_for_model() 检索模型的编码,如下所示:

encoding = tiktoken.encoding_for_model('gpt-3.5-turbo')

请注意,p50k_base 与 r50k_base 基本重叠,对于非代码应用程序,它们通常会给出相同的token。

如何将字符串转换成token
在英语中,token的长度通常从一个字符到一个单词不等(例如,“t”或“great”),尽管在某些语言中,token的长度可以短于一个字符或长于一个单词。通常空格会被保留在一个单词的开头,比如: " is"(空格在单词开头),一般不会是"is "(空格在单词尾部) 或者" " + "is"(拆分成两个token,空格和"is")。您可以在OpenAI Tokenizer或第三方Tiktokenizer web应用程序中快速检查字符串是如何被转换成token的:

1.安装 tiktoken

pip install --upgrade tiktoken
pip install --upgrade openai

2.加载encoding

使用tiktoken.get_encoding()按名称加载编码。第一次运行时,它将需要互联网连接进行下载。后续运行不需要互联网连接。

import tiktoken

encoding = tiktoken.get_encoding("cl100k_base")

#使用tiktoken.encoding_for_model()函数可以自动加载给定模型名称的正确编码。
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")

3.将文本转为token

使用. encode()方法将文本字符串转换为token整数列表。

encoding.encode("tiktoken is great!")

通过计算.encode()返回的列表的长度来统计token数量。


def num_tokens_from_string(string: str, encoding_name: str) -> int:
    """返回文本字符串中的Token数量"""
    encoding = tiktoken.get_encoding(encoding_name)
    num_tokens = len(encoding.encode(string))
    return num_tokens


num_tokens_from_string("tiktoken is great!", "cl100k_base")

 4. 将token转换为文本

使用.decode()将一个token整数列表转换为字符串。

encoding.decode([83, 1609, 5963, 374, 2294, 0])

注意:虽然.decode()可以应用于单个token,但是,对于不在utf-8边界上的token,它可能是有损的。对于单个token使用 .decode_single_token_bytes()方法可以安全地将单个整数token转换为它所表示的字节。

[encoding.decode_single_token_bytes(token) for token in [83, 1609, 5963, 374, 2294, 0]]

(在字符串前面的b表示这些字符串是字节字符串。) 

5. 编码比较

不同的编码方式在分割单词、处理空格和非英文字符方面存在差异。通过上述方法,我们可以比较几个示例字符串在不同的编码方式下的表现。

def compare_encodings(example_string: str) -> None:
    """Prints a comparison of three string encodings."""
    # print the example string
    print(f'\nExample string: "{example_string}"')
    # for each encoding, print the # of tokens, the token integers, and the token bytes
    for encoding_name in ["r50k_base", "p50k_base", "cl100k_base"]:
        encoding = tiktoken.get_encoding(encoding_name)
        token_integers = encoding.encode(example_string)
        num_tokens = len(token_integers)
        token_bytes = [encoding.decode_single_token_bytes(token) for token in token_integers]
        print()
        print(f"{encoding_name}: {num_tokens} tokens")
        print(f"token integers: {token_integers}")
        print(f"token bytes: {token_bytes}")
        
        
compare_encodings("antidisestablishmentarianism")

compare_encodings("2 + 2 = 4")

 

compare_encodings("お誕生日おめでとう")

 6. 调用openai模型时的token计数

像gpt-3.5-turbo和gpt-4这样的ChatGPT模型使用token的方式与旧的补全模型(completions model)相同,但由于其基于消息的格式,很难准确计算对话中将使用多少个token。

下面是一个示例函数,用于计算传递给gpt-3.5-turbo或gpt-4的消息中的token数量。

请注意,从消息中计算token的确切方式可能因模型而异。请将下面函数中的计数视为估计值,并非一直正确。特别地,在使用可选功能输入(input)的请求上方会消耗额外的token。

def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
    """Return the number of tokens used by a list of messages."""
    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  # every message follows <|start|>{role/name}\n{content}<|end|>\n
        tokens_per_name = -1  # if there's a name, the role is omitted
    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  # every reply is primed with <|start|>assistant<|message|>
    return num_tokens
# let's verify the function above matches the OpenAI API response

from openai import OpenAI
import os

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))

example_messages = [
    {
        "role": "system",
        "content": "You are a helpful, pattern-following assistant that translates corporate jargon into plain English.",
    },
    {
        "role": "system",
        "name": "example_user",
        "content": "New synergies will help drive top-line growth.",
    },
    {
        "role": "system",
        "name": "example_assistant",
        "content": "Things working well together will increase revenue.",
    },
    {
        "role": "system",
        "name": "example_user",
        "content": "Let's circle back when we have more bandwidth to touch base on opportunities for increased leverage.",
    },
    {
        "role": "system",
        "name": "example_assistant",
        "content": "Let's talk later when we're less busy about how to do better.",
    },
    {
        "role": "user",
        "content": "This late pivot means we don't have time to boil the ocean for the client deliverable.",
    },
]

for model in [
    "gpt-3.5-turbo-0301",
    "gpt-3.5-turbo-0613",
    "gpt-3.5-turbo",
    "gpt-4-0314",
    "gpt-4-0613",
    "gpt-4",
    ]:
    print(model)
    # example token count from the function defined above
    print(f"{num_tokens_from_messages(example_messages, model)} prompt tokens counted by num_tokens_from_messages().")
    # example token count from the OpenAI API
    response = client.chat.completions.create(model=model,
    messages=example_messages,
    temperature=0,
    max_tokens=1)
    print(f'{response.usage.prompt_tokens} prompt tokens counted by the OpenAI API.')
    print()

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-派神-

感谢您慷慨解囊,我会更加努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值