Go 语言版本的 OpenAI 的 tiktoken。 帮你把文本转换成 OpenAI 的模型可以识别的 token。 tiktoken的原项目地址tiktoken.
go get github.com/matthiasthomas/tiktoken-go
Tiktoken-go 和原始的 Tiktoken 库一样,具有相同的缓存机制。
您可以使用环境变量 TIKTOKEN_CACHE_DIR 来设置缓存目录。
一旦设置了该变量,tiktoken-go 将使用该目录来缓存令牌字典。
如果您未设置此环境变量,则 tiktoken-go 将在每次首次初始化编码时下载字典。
默认情况下,tiktoken-go 会在运行时下载字典,如果您不想使用缓存或每次下载字典,您可以使用替代 BPE 加载器。
只需在调用 tiktoken.GetEncoding
或 tiktoken.EncodingForModel
之前调用 tiktoken.SetBpeLoader
。
BpeLoader
是一个接口,您可以通过实现此接口来实现自己的 BPE 加载器。
离线 BPE 加载器从嵌入文件加载 BPE 字典。
由于 BPE 字典的文件较大,不适合包含在本项目中,故此加载器在其他项目中。
如果需要使用,请引用:tiktoken_loader
package main
import (
"fmt"
"github.com/matthiasthomas/tiktoken-go"
)
func main() {
text := "Hello, world!"
encoding := "cl100k_base"
// 如果你不想在运行时下载字典,你可以使用离线加载器
// tiktoken.SetBpeLoader(tiktoken_loader.NewOfflineLoader())
tke, err := tiktoken.GetEncoding(encoding)
if err != nil {
err = fmt.Errorf("getEncoding: %v", err)
return
}
// encode
token := tke.Encode(text, nil, nil)
//tokens
fmt.Println((token))
// num_tokens
fmt.Println(len(token))
}
package main
import (
"fmt"
"github.com/matthiasthomas/tiktoken-go"
)
func main() {
text := "Hello, world!"
encoding := "gpt-3.5-turbo"
tkm, err := tiktoken.EncodingForModel(encoding)
if err != nil {
err = fmt.Errorf("getEncoding: %v", err)
return
}
// encode
token := tkm.Encode(text, nil, nil)
// tokens
fmt.Println(token)
// num_tokens
fmt.Println(len(token))
}
这段代码根据官方示例编写
编写时间: 2023-06-28
请注意,消息的token计算方式可能随时会发生改变,以下代码并不一定在将来适用,如果您需要精确的计算,请关注官方文档。
如果您发现这段代码不再适用,欢迎您提PR或Issue。
package main
import (
"fmt"
"github.com/matthiasthomas/tiktoken-go"
"github.com/sashabaranov/go-openai"
)
func NumTokensFromMessages(messages []openai.ChatCompletionMessage, model string) (numTokens int) {
tkm, err := tiktoken.EncodingForModel(model)
if err != nil {
err = fmt.Errorf("encoding for model: %v", err)
log.Println(err)
return
}
var tokensPerMessage, tokensPerName int
switch model {
case "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":
tokensPerMessage = 3
tokensPerName = 1
case "gpt-3.5-turbo-0301":
tokensPerMessage = 4 // every message follows <|start|>{role/name}\n{content}<|end|>\n
tokensPerName = -1 // if there's a name, the role is omitted
default:
if strings.Contains(model, "gpt-3.5-turbo") {
log.Println("warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.")
return NumTokensFromMessages(messages, "gpt-3.5-turbo-0613")
} else if strings.Contains(model, "gpt-4") {
log.Println("warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.")
return NumTokensFromMessages(messages, "gpt-4-0613")
} else {
err = fmt.Errorf("num_tokens_from_messages() is not implemented for model %s. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens.", model)
log.Println(err)
return
}
}
for _, message := range messages {
numTokens += tokensPerMessage
numTokens += len(tkm.Encode(message.Content, nil, nil))
numTokens += len(tkm.Encode(message.Role, nil, nil))
numTokens += len(tkm.Encode(message.Name, nil, nil))
if message.Name != "" {
numTokens += tokensPerName
}
}
numTokens += 3 // every reply is primed with <|start|>assistant<|message|>
return numTokens
}
Encoding name | OpenAI models |
---|---|
cl100k_base |
gpt-4 , gpt-3.5-turbo , text-embedding-ada-002 , text-embedding-3-small , text-embedding-3-large |
p50k_base |
Codex models, text-davinci-002 , text-davinci-003 |
r50k_base (or gpt2 ) |
GPT-3 models like davinci |
Model name | OpenAI models |
---|---|
gpt-4 | cl100k_base |
gpt-4-* | cl100k_base |
gpt-3.5-turbo | cl100k_base |
gpt-3.5-turbo-* | cl100k_base |
text-davinci-003 | p50k_base |
text-davinci-002 | p50k_base |
text-davinci-001 | r50k_base |
text-curie-001 | r50k_base |
text-babbage-001 | r50k_base |
text-ada-001 | r50k_base |
davinci | r50k_base |
curie | r50k_base |
babbage | r50k_base |
ada | r50k_base |
code-davinci-002 | p50k_base |
code-davinci-001 | p50k_base |
code-cushman-002 | p50k_base |
code-cushman-001 | p50k_base |
davinci-codex | p50k_base |
cushman-codex | p50k_base |
text-davinci-edit-001 | p50k_edit |
code-davinci-edit-001 | p50k_edit |
text-embedding-ada-002 | cl100k_base |
text-embedding-3-small | cl100k_base |
text-embedding-3-large | cl100k_base |
text-similarity-davinci-001 | r50k_base |
text-similarity-curie-001 | r50k_base |
text-similarity-babbage-001 | r50k_base |
text-similarity-ada-001 | r50k_base |
text-search-davinci-doc-001 | r50k_base |
text-search-curie-doc-001 | r50k_base |
text-search-babbage-doc-001 | r50k_base |
text-search-ada-doc-001 | r50k_base |
code-search-babbage-code-001 | r50k_base |
code-search-ada-code-001 | r50k_base |
gpt2 | gpt2 |
与官方 tiktoken 的对比
你可以使用 test 目录下的文件执行基准测试。
name | time/op | os | cpu | text | times |
---|---|---|---|---|---|
tiktoken-go | 8795ns | macOS 13.2 | Apple M1 | UDHR | 100000 |
tiktoken | 8838ns | macOS 13.2 | Apple M1 | UDHR | 100000 |
看上去tiktoken-go的性能基本与原tiktoken一致。
也许在不同的机器上的测试结果会有所不同。也可能是我的测试方法并不恰当。
如果你有更好的测试方法,或者说你想添加在你机器上的测试结果,欢迎提PR。