OpenAI Cookbook examples : API 使用方法 : tiktoken でトークンを数える方法 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/10/2023
* 本ページは、OpenAI Cookbook レポジトリの以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- examples : How to count tokens with tiktoken
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
OpenAI Cookbook : API 使用方法 : tiktoken でトークンを数える方法
tiktoken は OpenAI による高速なオープンソースのトークナイザーです。
テキスト文字列 (e.g., “tiktoken is great!”) とエンコーディング (e.g., “cl100k_base”) が与えられたとき、トークナイザーはテキスト文字列をトークンのリスト (e.g., [“t”, “ik”, “token”, ” is”, ” great”, “!”]) に分割することができます。
テキスト文字列をトークンに分割することは有用です、GPT モデルはテキストをトークンの形式で認識するからです。幾つのトークンがテキスト文字列にあるかを知ることは、(a) 文字列がテキストモデルが処理するのに長すぎるかどうか、そして (b) OpenAI API 呼び出しがいくらかかるか (使用量はトークンにより価格付けされるので)、を伝えます。
エンコーディング
エンコーディングはテキストがトークンに変換される方法を指定します。異なるモデルは異なるエンコーディングを使用します。
tiktoken は OpenAI モデルで使用される 3 つのエンコーディングをサポートしています :
エンコーディング名 | OpenAI モデル |
---|---|
cl100k_base | gpt-4, gpt-3.5-turbo, text-embedding-ada-002 |
p50k_base | Codex models, text-davinci-002, text-davinci-003 |
r50k_base (or gpt2) | GPT-3 models like davinci |
tiktoken.encoding_for_model() を使用して以下のようにモデルのエンコーディングを取得できます :
encoding = tiktoken.encoding_for_model('gpt-3.5-turbo')
p50k_base は r50k_base と本質的には重複していて、非コード・アプリケーションに対しては、それらは通常は同じトークンを与えることに注意してください。
言語によるトークナイザー・ライブラリ
cl100k_base と p50k_base エンコーディングに対しては :
- Python: tiktoken
- .NET / C#: SharpToken, TiktokenSharp
- Java: jtokkit
- Golang: tiktoken-go
r50k_base (gpt2) エンコーディングに対しては、トークナイザーは多くの言語で利用可能です。
- Python: tiktoken (or alternatively GPT2TokenizerFast)
- JavaScript: gpt-3-encoder
- .NET / C#: GPT トークナイザー
- Java: gpt2-tokenizer-java
- PHP: GPT-3-Encoder-PHP
- Golang: tiktoken-go
(OpenAI makes no endorsements or guarantees of third-party libraries.)
文字列は通常はどのようにトークン化されるか
英語では、トークンは一般的には 1 文字から 1 単語 (e.g., “t” or ” great”) の長さの範囲にありますが、幾つかの言語では、トークンは 1 文字よりも短かったり 1 単語よりも長い可能性があります。スペース (空白) は通常は単語の先頭でグループ化されます (e.g., “is ” や ” “+”is” の代わりに ” is”)。OpenAI トークナイザーではどのように文字列がトークン化されるか素早くチェックできます。
0. tiktoken のインストール
必要であれば、tiktoken を pip でインストールします :
%pip install --upgrade tiktoken
1. tiktoken のインポート
import tiktoken
2. エンコーディングのロード
tiktoken.get_encoding() を使用してエンコーディングを名前でロードします。
これを最初に実行する場合、ダウンロードするためにインターネット接続が必要です。その後の実行はインターネット接続を必要としません。
encoding = tiktoken.get_encoding("cl100k_base")
tiktoken.encoding_for_model() を使用して与えられたモデル名に対する正しいエンコーディングを自動的にロードします。
encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
3. encoding.encode() でテキストをトークンに変換する
.encode() メソッドはテキスト文字列をトークン整数のリストに変換します。
encoding.encode("tiktoken is great!")
[83, 1609, 5963, 374, 2294, 0]
.encode() により返されるリストの長さを数えてトークンをカウントします。
def num_tokens_from_string(string: str, encoding_name: str) -> int:
"""Returns the number of tokens in a text string."""
encoding = tiktoken.get_encoding(encoding_name)
num_tokens = len(encoding.encode(string))
return num_tokens
num_tokens_from_string("tiktoken is great!", "cl100k_base")
6
4. トークンを encoding.decode() でテキストに変換する
.decode() はトークン整数のリストを文字列に変換します。
encoding.decode([83, 1609, 5963, 374, 2294, 0])
'tiktoken is great!'
Warning: .decode() は単一トークンに適用できますが、utf-8 境界上にないトークンについては損失する可能性があることに注意してください。
単一トークンについては、.decode_single_token_bytes() が単一整数トークンをそれが表すバイトに安全に変換します。
[encoding.decode_single_token_bytes(token) for token in [83, 1609, 5963, 374, 2294, 0]]
[b't', b'ik', b'token', b' is', b' great', b'!']
(文字列の前の 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 ["gpt2", "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")
Example string: "antidisestablishmentarianism" gpt2: 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']
compare_encodings("2 + 2 = 4")
Example string: "2 + 2 = 4" gpt2: 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']
compare_encodings("お誕生日おめでとう")
Example string: "お誕生日おめでとう" gpt2: 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']
6. チャット補完 API 呼び出しのためにトークンを数える
gpt-3.5-turbo と gpt-4 のような ChatGPT モデルは古い補完モデルと同じ方法でトークンを使用しますが、メッセージベースのフォーマッティングのために、会話により幾つのトークンが使用されるかをカウントするのはより困難です。
以下は gpt-3.5-turbo または gpt-4 に渡されたメッセージのトークンをカウントするためのサンプル関数です。
トークンがメッセージからカウントされる正確な方法はモデルによって変わる可能性があることに注意してください。以下の関数からのカウントは推定値として考えてください、永久的な保証ではありません。
特に、オプションの関数入力を使用するリクエストは以下で計算される推定値に加えて追加のトークンを消費します。
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
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)
# 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 = openai.ChatCompletion.create(
model=model,
messages=example_messages,
temperature=0,
max_tokens=1, # we're only counting input tokens here, so let's not waste tokens on the output
)
print(f'{response["usage"]["prompt_tokens"]} prompt tokens counted by the OpenAI API.')
print()
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(). 127 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.
以上