当前位置:   article > 正文

大模型系列:OpenAI使用技巧_如何使用tiktoken计算标记数

tiktoken


tiktoken 是由OpenAI开发的快速开源分词器。

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

将文本字符串拆分为标记对于GPT模型非常有用,因为GPT模型以标记的形式处理文本。知道文本字符串中有多少个标记可以告诉您(a)字符串是否过长以至于无法被文本模型处理,以及(b)OpenAI API调用的成本(因为使用量是按标记计价的)。

编码

编码指定如何将文本转换为标记。不同的模型使用不同的编码。

tiktoken支持OpenAI模型使用的三种编码:

编码名称OpenAI模型
cl100k_basegpt-4, gpt-3.5-turbo, text-embedding-ada-002
p50k_baseCodex模型,text-davinci-002text-davinci-003
r50k_base(或gpt2GPT-3模型,如davinci

您可以使用以下方式通过tiktoken.encoding_for_model()获取模型的编码:

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

请注意,p50k_baser50k_base有很大的重叠,并且对于非代码应用,它们通常会给出相同的标记。

根据语言的分词器库

对于cl100k_basep50k_base编码:

对于r50k_basegpt2)编码,分词器在许多语言中都可用。

(OpenAI不对第三方库作任何认可或保证。)

字符串的常见分词方式

在英语中,标记通常的长度范围从一个字符到一个单词(例如,"t"" great"),尽管在某些语言中,标记可能比一个字符更短或比一个单词更长。空格通常与单词的开头分组(例如," is"而不是"is "" "+"is")。您可以在OpenAI Tokenizer或第三方的Tiktokenizer网页应用程序上快速检查字符串的分词方式。

0. 安装 tiktoken

如果需要,可以使用 pip 安装 tiktoken:

# 安装tiktoken库
%pip install --upgrade tiktoken

# 安装openai库
%pip install --upgrade openai
  • 1
  • 2
  • 3
  • 4
  • 5

1. 导入 tiktoken

# 导入tiktoken模块,用于分词和标记化处理
import tiktoken
  • 1
  • 2

2. 加载一个编码

使用 tiktoken.get_encoding() 按名称加载一个编码。

第一次运行时,需要连接到互联网进行下载。后续运行不需要连接到互联网。



# 获取名为"cl100k_base"的编码格式
encoding = tiktoken.get_encoding("cl100k_base")
  • 1
  • 2
  • 3
  • 4

使用 tiktok.encoding_for_model() 自动加载给定模型名称的正确编码。



# 为"gpt-3.5-turbo"模型选择编码方式
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
  • 1
  • 2
  • 3
  • 4

3. 使用encoding.encode()将文本转换为标记。

.encode()方法将一个文本字符串转换为一个令牌整数列表。



# 调用encode函数,将字符串"tiktoken is great!"进行编码
encoding.encode("tiktoken is great!")
  • 1
  • 2
  • 3
  • 4
[83, 1609, 5963, 374, 2294, 0]
  • 1

通过计算.encode()返回的列表的长度来计算令牌数。

# 定义一个函数,用于计算一个文本字符串中的标记数量
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
# 调用函数,计算给定文本的token数量
num_tokens = num_tokens_from_string("tiktoken is great!", "cl100k_base")
  • 1
  • 2
6
  • 1

4. 使用encoding.decode()将标记转换为文本。

.decode()将一个令牌整数列表转换为字符串。



# 调用编码模块中的decode函数,将列表[83, 1609, 5963, 374, 2294, 0]解码
# 解码后得到字符串"Sمرحبا",其中"S"表示英文字符,"مرحبا"表示阿拉伯语中的"你好"
encoding.decode([83, 1609, 5963, 374, 2294, 0]) 
  • 1
  • 2
  • 3
  • 4
  • 5
'tiktoken is great!'
  • 1

警告:虽然.decode()可以应用于单个标记,但请注意对于不在utf-8边界上的标记,它可能会有损失。

对于单个标记,.decode_single_token_bytes() 可以安全地将单个整数标记转换为其表示的字节。



# 使用循环遍历列表中的每个元素
# 对于每个元素,使用编码模块的decode_single_token_bytes函数进行解码
# 将解码后的结果存储在新的列表中
[encoding.decode_single_token_bytes(token) for token in [83, 1609, 5963, 374, 2294, 0]]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
[b't', b'ik', b'token', b' is', b' great', b'!']
  • 1

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

5. 比较编码方式

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

# 定义一个函数,比较三种字符串编码的差异
def compare_encodings(example_string: str) -> None:
    # 打印示例字符串
    print(f'\nExample string: "{example_string}"')
    # 针对每种编码,打印标记数量、标记整数和标记字节
    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}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
# 定义函数 compare_encodings,接收一个字符串参数 word
compare_encodings("antidisestablishmentarianism")

  • 1
  • 2
  • 3
Example string: "antidisestablishmentarianism"

r50k_base: 5 tokens
token integers: [415, 29207, 44390, 3699, 1042]
token bytes: [b'ant', b'idis', b'establishment', b'arian', b'ism']

p50k_base: 5 tokens
token integers: [415, 29207, 44390, 3699, 1042]
token bytes: [b'ant', b'idis', b'establishment', b'arian', b'ism']

cl100k_base: 6 tokens
token integers: [519, 85342, 34500, 479, 8997, 2191]
token bytes: [b'ant', b'idis', b'establish', b'ment', b'arian', b'ism']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
compare_encodings("2 + 2 = 4")

  • 1
  • 2
Example string: "2 + 2 = 4"

r50k_base: 5 tokens
token integers: [17, 1343, 362, 796, 604]
token bytes: [b'2', b' +', b' 2', b' =', b' 4']

p50k_base: 5 tokens
token integers: [17, 1343, 362, 796, 604]
token bytes: [b'2', b' +', b' 2', b' =', b' 4']

cl100k_base: 7 tokens
token integers: [17, 489, 220, 17, 284, 220, 19]
token bytes: [b'2', b' +', b' ', b'2', b' =', b' ', b'4']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
compare_encodings("お誕生日おめでとう")

  • 1
  • 2
Example string: "お誕生日おめでとう"

r50k_base: 14 tokens
token integers: [2515, 232, 45739, 243, 37955, 33768, 98, 2515, 232, 1792, 223, 30640, 30201, 29557]
token bytes: [b'\xe3\x81', b'\x8a', b'\xe8\xaa', b'\x95', b'\xe7\x94\x9f', b'\xe6\x97', b'\xa5', b'\xe3\x81', b'\x8a', b'\xe3\x82', b'\x81', b'\xe3\x81\xa7', b'\xe3\x81\xa8', b'\xe3\x81\x86']

p50k_base: 14 tokens
token integers: [2515, 232, 45739, 243, 37955, 33768, 98, 2515, 232, 1792, 223, 30640, 30201, 29557]
token bytes: [b'\xe3\x81', b'\x8a', b'\xe8\xaa', b'\x95', b'\xe7\x94\x9f', b'\xe6\x97', b'\xa5', b'\xe3\x81', b'\x8a', b'\xe3\x82', b'\x81', b'\xe3\x81\xa7', b'\xe3\x81\xa8', b'\xe3\x81\x86']

cl100k_base: 9 tokens
token integers: [33334, 45918, 243, 21990, 9080, 33334, 62004, 16556, 78699]
token bytes: [b'\xe3\x81\x8a', b'\xe8\xaa', b'\x95', b'\xe7\x94\x9f', b'\xe6\x97\xa5', b'\xe3\x81\x8a', b'\xe3\x82\x81', b'\xe3\x81\xa7', b'\xe3\x81\xa8\xe3\x81\x86']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6. 统计聊天完成API调用的令牌数

ChatGPT模型(如gpt-3.5-turbogpt-4)与旧的完成模型一样使用令牌,但由于其基于消息的格式,很难计算一个对话将使用多少个令牌。

下面是一个示例函数,用于计算传递给gpt-3.5-turbogpt-4的消息的令牌数。

请注意,从消息中计算令牌的确切方式可能因模型而异。请将下面函数中的计数视为估计,而不是永恒的保证。

特别是,使用可选的功能输入的请求将在下面计算的估计之上消耗额外的令牌。

# 定义函数num_tokens_from_messages,用于计算一组消息所使用的token数量
def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
    # 尝试获取指定模型的编码方式
    try:
        encoding = tiktoken.encoding_for_model(model)
    # 如果模型未找到,则使用cl100k_base编码方式
    except KeyError:
        print("Warning: model not found. Using cl100k_base encoding.")
        encoding = tiktoken.get_encoding("cl100k_base")
    # 根据模型不同,设置每条消息和每个名字所使用的token数量
    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.")
        # 如果模型为gpt-3.5-turbo,则返回gpt-3.5-turbo-0613模型的token数量
        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.")
        # 如果模型为gpt-4,则返回gpt-4-0613模型的token数量
        return num_tokens_from_messages(messages, model="gpt-4-0613")
    else:
        # 如果模型未实现,则抛出NotImplementedError异常
        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."""
        )
    # 初始化token数量为0
    num_tokens = 0
    # 遍历每条消息
    for message in messages:
        # 每条消息都会使用tokens_per_message个token
        num_tokens += tokens_per_message
        # 遍历消息中的每个键值对
        for key, value in message.items():
            # 计算值所使用的token数量,并加到num_tokens中
            num_tokens += len(encoding.encode(value))
            # 如果键为"name",则还需要加上tokens_per_name个token
            if key == "name":
                num_tokens += tokens_per_name
    # 每个回复都以<|start|>assistant<|message|>开头,需要再加上3个token
    num_tokens += 3
    # 返回总的token数量
    return num_tokens
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
# 首先,我们需要验证上面的函数是否与OpenAI API的响应匹配

import openai

# 定义一组示例对话消息
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)
    
    # 调用上面定义的函数计算示例消息中的令牌数量
    print(f"{num_tokens_from_messages(example_messages, model)} prompt tokens counted by num_tokens_from_messages().")
    
    # 调用OpenAI API计算示例消息中的令牌数量
    response = openai.ChatCompletion.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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
gpt-3.5-turbo-0301
127 prompt tokens counted by num_tokens_from_messages().
127 prompt tokens counted by the OpenAI API.

gpt-3.5-turbo-0613
129 prompt tokens counted by num_tokens_from_messages().
129 prompt tokens counted by the OpenAI API.

gpt-3.5-turbo
Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.
129 prompt tokens counted by num_tokens_from_messages().
129 prompt tokens counted by the OpenAI API.

gpt-4-0314
129 prompt tokens counted by num_tokens_from_messages().
129 prompt tokens counted by the OpenAI API.

gpt-4-0613
129 prompt tokens counted by num_tokens_from_messages().
129 prompt tokens counted by the OpenAI API.

gpt-4
Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.
129 prompt tokens counted by num_tokens_from_messages().
129 prompt tokens counted by the OpenAI API.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/677569
推荐阅读
相关标签
  

闽ICP备14008679号