你有没有遇到过这种情况:在OpenAI的API里输入一段中文,Token数比你预想的多出一大截;换成英文,同样意思的内容Token却少了很多。或者用DeepSeek和Claude处理同一段文本,算出来的Token数居然不一样。

这不是错觉,而是Token计算本身的复杂性造成的。

Token可以理解为大模型处理文本时的最小“积木块” 。它既不是字也不是词,而是介于两者之间的“子词(subword)”或“字节序列”。大模型看不懂原始文字,只能看懂数字。所以每一段文字输入模型之前,都要先被切分成Token,再映射成数字ID。

一、Token是怎么“切”出来的?——Tokenization的核心流程

把文本变成Token的过程叫Tokenization(分词) ,整个过程可以分为三步:

1.1 为什么不用“字”或“词”做最小单位?

如果用做单位(比如英文单词),会遇到生僻词问题——模型不可能认识所有单词,遇到不认识的词就不知道怎么办了。

如果用字/字符做单位,又会导致序列太长——一个英文句子平均5个字符才等于1个词,序列长度直接膨胀5倍,计算量暴涨。

所以主流方案取了个折中:用子词(Subword) 做单位。高频词保留为完整Token,低频词拆分成共享子词单元。这样既控制了词表大小,又能覆盖几乎所有文本。

举个直观的例子:英文单词"unhappiness"可能被拆成"un" + "happiness"两个Token,而不是一个完整的词,也不是一个一个字母。

二、三大主流分词算法:BPE、WordPiece、SentencePiece

不同模型用不同的分词算法,这是导致同样文本在不同模型里Token数不一样的根本原因。

2.1 BPE(Byte-Pair Encoding)——最主流

代表模型:GPT系列、Llama、Qwen2等

核心思想:从字母开始,不断合并出现频率最高的相邻字符对。

训练过程(简化):
初始词表:a, b, c, d, e, ...
第1步:发现 "ab" 出现次数最多 → 合并成 "ab"
第2步:发现 "ab" + "c" 出现最多 → 合并成 "abc"
第3步:继续合并,直到词表达到预定大小

特点

  • 纯数据驱动,只看统计频率

  • 可逆且无损,Token可以完美还原回原文

  • OpenAI的tiktoken就是基于BPE的快速实现

2.2 WordPiece——BERT的选择

代表模型:BERT

与BPE的区别:BPE选频率最高的合并,WordPiece选能最大提升似然概率的合并——简单说,BPE看“谁出现最多”,WordPiece看“谁合并后效果最好”。

2.3 SentencePiece——语言无关的解决方案

代表模型:LLaMA、T5、ALBERT等

最大特点:把空格也当成普通字符处理,不需要预先按空格分词。这让它天然支持中文、日文等没有空格的语言

三种算法对比总结

算法 代表模型 核心逻辑 中文支持
BPE GPT、Llama、Qwen2 合并频率最高的相邻对 一般
WordPiece BERT 合并后似然提升最大的对 一般
SentencePiece LLaMA、T5 把空格当字符,不依赖预分词 优秀

三、Token计算框架:模型是怎么算Token的?

3.1 OpenAI:tiktoken

OpenAI官方提供了一个叫tiktoken的Python库,专门用来计算OpenAI模型的Token数。

import tiktoken

# 根据模型名称获取对应的编码器
encoding = tiktoken.encoding_for_model("gpt-4o")

# 计算Token数
text = "Hello, how are you?"
tokens = encoding.encode(text)
print(f"Token数量: {len(tokens)}")
print(f"Token列表: {tokens}")

tiktoken的编码规则:不同模型用不同的编码(encoding)。比如cl100k_base是GPT-4系列用的编码,词汇表有10万个Token。

英文的估算规则

  • 1个Token ≈ 4个字符

  • 1个Token ≈ 0.75个单词

  • 100个Token ≈ 75个单词

3.2 DeepSeek:基于BPE的自研分词器

DeepSeek同样采用BPE算法,但词汇表和具体切分规则与OpenAI不同。这就是为什么同样一段中文,在DeepSeek和OpenAI里算出来的Token数不一样。

一个有意思的发现:在DeepSeek-V3和Qwen 3.6上,中文比英文更省Token——同样的内容,中文版的Token数只有英文版的0.65倍。这与OpenAI的情况正好相反(OpenAI里中文通常比英文更费Token)。

3.3 Hugging Face Tokenizers:统一框架

Hugging Face提供了tokenizers库,统一实现了BPE、WordPiece、Unigram等多种算法。

from transformers import AutoTokenizer

# 加载任何Hugging Face模型的tokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

# 分词并计算Token数
tokens = tokenizer.tokenize("Hello, how are you?")
print(f"Token列表: {tokens}")
print(f"Token数量: {len(tokens)}")

Tokenizer的完整流水线

  1. Normalizer:标准化(转小写、去重音等)

  2. Pre-tokenizer:预切分(按空格等规则初步拆分)

  3. Model:核心分词算法(BPE/WordPiece/Unigram)

  4. Post-processor:后处理(添加特殊标记如[CLS][SEP]等)

四、模型之间Token计算的差异:为什么同样文本在不同模型里不一样?

4.1 差异来源一:词表大小不同

每个模型训练时都会构建自己的词表。词表越大,一个Token能覆盖的文本就越多。

模型 词表大小(约)
GPT-2 50,257
GPT-4 (cl100k_base) 100,000
Llama 2 32,000
DeepSeek-V3 129,000

词表越大,生僻词越可能被保留为完整Token而不是拆成多个,Token数就越少

4.2 差异来源二:分词粒度不同

同样是中文"人工智能":

模型 可能的切分方式 Token数
模型A ["人工智能"] 1
模型B ["人工", "智能"] 2
模型C ["人", "工", "智", "能"] 4

没有一个“标准答案” ,全看模型训练时的分词策略。

4.3 差异来源三:特殊Token的添加

很多模型会在输入前后自动添加特殊Token:

  • [CLS](分类标记)—— BERT系列

  • [SEP](分隔标记)—— BERT系列

  • <|im_start|><|im_end|>(消息起止)—— 部分开源模型

  • <|endoftext|>(文本结束)—— GPT系列

这些特殊Token也会计入总数,而且不同模型加的数量不一样。

五、多模态Token计算:图像、音频是怎么变成Token的?

多模态模型的Token计算比纯文本复杂得多——不同模态的Token是分开计算的,而且计算方式完全不同

5.1 图像转Token:视觉编码器

多模态模型处理图像时,不是直接把图片“当作”Token,而是通过视觉编码器把图片转换成Token序列。

典型的转换流程

以ViT-L/14为例,处理224×224分辨率图像时,默认生成196个Patch Token。分辨率越高,Token越多。

不同平台的图像Token计算规则

平台/模型 图像Token计算方式
StepFun 默认每张图169 Token,开启detail模式后根据图片大小计算
通义千问VL 最小28×28像素=1 Token,每张图最少4 Token,最多1280 Token
Qwen3-Omni 图片/视频输入单独计价,与文本输入价格不同

5.2 音频转Token:声学特征提取

音频的处理方式又不一样——通过声学特征提取把声音信号转成Token序列。

不同模态的Token价格完全不同。以阿里云Qwen3-Omni-Flash为例:

模态 输入价格(元/百万Token)
文本 3.156
图片/视频 5.725
音频 27.962

音频Token的价格是文本的将近9倍。

5.3 多模态Token是“相互”还是“分开”计算的?

答案是:分开计算,但合并计费。

  • 计算上分开:文本走文本分词器,图像走视觉编码器,音频走声学编码器——各自独立转换成Token

  • 计费上合并:最终把所有模态的Token数加在一起,按各自的价格分别计费

计费公式

总费用 = (图像Token数 × 图像单价) + (音频Token数 × 音频单价) + (文本Token数 × 文本单价) + (输出Token数 × 输出单价)

5.4 多模态模型的“隐藏成本”

多模态调用有一个容易被忽略的成本:即使你只问了一个简单问题,模型也要先把整张图片转成几百个Token才能处理

比如你发一张高清图问“这是什么?”,模型可能先消耗了500个图像Token“读图”,再用10个文本Token生成回答。读图的成本远高于回答本身。 

六、VSCode实战:Token计算的完整代码示例

6.1 使用tiktoken计算OpenAI模型的Token

import tiktoken

def count_tokens_openai(text, model="gpt-4o"):
    """
    使用tiktoken计算OpenAI模型的Token数
    """
    try:
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        # 如果模型名不存在,使用默认编码
        encoding = tiktoken.get_encoding("cl100k_base")
    
    tokens = encoding.encode(text)
    return len(tokens), tokens

# 测试不同文本
test_texts = [
    "Hello, how are you?",           # 英文
    "你好,最近怎么样?",              # 中文
    "Artificial Intelligence",       # 英文单词
    "人工智能",                       # 中文词汇
]

for text in test_texts:
    count, tokens = count_tokens_openai(text)
    print(f"文本: {text}")
    print(f"  Token数: {count}")
    print(f"  Token IDs: {tokens[:5]}...")  # 只显示前5个
    print()

运行结果示例(实际数字可能因模型版本而异):

文本: Hello, how are you?
  Token数: 6
  Token IDs: [9906, 11, 703, 527, 499, 30]...

文本: 你好,最近怎么样?
  Token数: 7
  Token IDs: [44300, 106395, 16, 38412, 102, 450, 116]...

文本: Artificial Intelligence
  Token数: 3
  Token IDs: [21646, 12518]...

文本: 人工智能
  Token数: 2
  Token IDs: [44300, 106395]...

6.2 使用Hugging Face Tokenizer计算任意模型

from transformers import AutoTokenizer

def count_tokens_hf(text, model_name="Qwen/Qwen2.5-7B"):
    """
    使用Hugging Face加载指定模型的tokenizer并计算Token
    """
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    tokens = tokenizer.tokenize(text)
    token_ids = tokenizer.encode(text)
    
    return len(tokens), tokens, token_ids

# 测试
text = "人工智能正在改变世界"
count, tokens, ids = count_tokens_hf(text)
print(f"文本: {text}")
print(f"Token数: {count}")
print(f"Token列表: {tokens}")
print(f"Token IDs: {ids}")

6.3 对比不同模型的Token计算差异

import tiktoken
from transformers import AutoTokenizer

def compare_tokenizers(text, models):
    """
    对比不同模型对同一段文本的Token切分
    """
    results = {}
    
    # OpenAI模型
    for model in models.get("openai", []):
        try:
            enc = tiktoken.encoding_for_model(model)
            tokens = enc.encode(text)
            results[f"OpenAI-{model}"] = {
                "count": len(tokens),
                "tokens": tokens[:10]  # 只显示前10个
            }
        except:
            pass
    
    # Hugging Face模型
    for model in models.get("hf", []):
        try:
            tokenizer = AutoTokenizer.from_pretrained(model)
            tokens = tokenizer.tokenize(text)
            ids = tokenizer.encode(text)
            results[f"HF-{model}"] = {
                "count": len(tokens),
                "tokens": tokens[:10]
            }
        except:
            pass
    
    return results

# 测试文本
text = "大语言模型的Token计算方式因模型而异"

# 要对比的模型
models = {
    "openai": ["gpt-4o", "gpt-3.5-turbo"],
    "hf": ["Qwen/Qwen2.5-7B", "meta-llama/Llama-2-7b"]
}

results = compare_tokenizers(text, models)
for model_name, data in results.items():
    print(f"{model_name}: {data['count']} 个Token")
    print(f"  前10个Token: {data['tokens']}")
    print()

6.4 多模态Token估算(模拟)

def estimate_multimodal_tokens(image_resolution=(224, 224), text="", audio_seconds=0):
    """
    估算多模态请求的Token消耗(模拟)
    """
    total = 0
    details = {}
    
    # 图像Token估算(基于ViT风格)
    if image_resolution:
        patch_size = 16
        h, w = image_resolution
        image_tokens = (h // patch_size) * (w // patch_size)
        details["图像Token"] = image_tokens
        total += image_tokens
    
    # 文本Token估算(粗略)
    if text:
        # 粗略估算:中文约1.5 Token/字,英文约0.25 Token/字符
        chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff')
        other_chars = len(text) - chinese_chars
        text_tokens = int(chinese_chars * 1.5 + other_chars * 0.25)
        details["文本Token"] = text_tokens
        total += text_tokens
    
    # 音频Token估算(粗略:1秒≈50 Token)
    if audio_seconds > 0:
        audio_tokens = int(audio_seconds * 50)
        details["音频Token"] = audio_tokens
        total += audio_tokens
    
    details["总计"] = total
    return details

# 示例:一张图 + 一段文字 + 10秒音频
result = estimate_multimodal_tokens(
    image_resolution=(224, 224),
    text="请分析这张图片的内容",
    audio_seconds=10
)

print("多模态Token估算:")
for key, value in result.items():
    print(f"  {key}: {value}")

输出示例

多模态Token估算:
  图像Token: 196
  文本Token: 7
  音频Token: 500
  总计: 703
维度 核心要点
Token是什么 大模型处理文本的最小单元,介于字和词之间的“子词”
怎么计算 预处理 → 分词(BPE/WordPiece/SentencePiece)→ 映射为ID
三大算法 BPE(最主流)、WordPiece(BERT用)、SentencePiece(支持中文好)
为什么不同模型不一样 词表大小不同、分词粒度不同、特殊Token不同
多模态怎么算 图像→视觉编码器转Token,音频→声学编码器转Token,分开计算、合并计费
主要框架 OpenAI tiktoken、Hugging Face Tokenizers、Google SentencePiece

小编建议

  1. 不要用字符数估算Token数:中文1个字≈1.5-2 Token,英文1个词≈1-1.5 Token

  2. 不同模型用不同的计算工具:OpenAI用tiktoken,Hugging Face用AutoTokenizer

  3. 多模态调用前先估算图像Token:一张高清图可能消耗几百个Token,成本远超你的想象

  4. 中文场景优先选中文优化好的模型:DeepSeek和Qwen的中文Token效率优于OpenAI

  5. 生产环境务必在代码里精确计算Token:不要靠猜,用官方工具算

小编团自动研发  大型文件翻译、文本润色链接

[免费]翻译全文网页,你是否为翻译烦恼?-CSDN博客

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐