如今AI越来越火在和各类 AI 交互时你一定经常听到这个词“这段上下文消耗了 2000 个 Token”、“这个模型支持 100万 Token 的超长上下文比如近期的前沿模型”。那到底什么是Token直白地说Token词元是大模型眼中的“基本阅读和书写单位”。官方定义它的中文叫“词元”人类看到的是一段段连续的汉字或英文而大模型看到的是一串串数字Token ID。在 AI 时代Token 就是新的“计量单位”具有可计量、可定价、可交易的特征买黄金看克数交电费看度数买猪肉看斤两榨取AI算力看Token。今天我来从技术角度扒一扒token。字符表示Character-level如今大模型看起来非常的智能但它本质上是一个阅读了海量人类语料的超级概率计算器它依靠Transformer 架构理解上下文通过不断计算并预测“下一个最合理的词”Next-Token Prediction从而涌现出看似具备逻辑与思想的智能。你给它一句话它根据它学过的海量数据推测出最合理的下一句话是什么。其实对于我们人类来说一直有个难点怎么把人类的语言喂给只懂0和1的计算机最早大家觉得这还不简单把每个英文字母或每个汉字都当成 token不就行了比如a是1b是2“我”是300“你”是301。那么一句Hello拆开就是[H, e, l, l, o]。 这种方法有一个很明显的好处简单。英文基础字符就那么多中文虽然汉字多一点但常用汉字也就几千个全收录进字典基本就不用担心遇到“不认识的词”。这样一来不管是一句话还是一篇文章都可以先拆成一个个字符再变成一串数字丢给模型去处理。但现实很快就打了脸这种“按字切分”的方案实在太碎了直接暴露了两个致命缺陷首先就是字都认识却丢了语义。把Apple拆成A-p-p-l-e单独看这些字母它们没有什么实际含义。AI模型必须自己重新硬记这些组合学习原来这几个字母连在一起才表示“苹果”。另一个问题就是成本太高因为字切得越碎一句话转换成的数字串就越长。而大模型底层的注意力机制算力消耗是“平方级增长”的句子长度翻 2 倍计算压力直接暴增 4 倍如果不改变切分方法大模型的算力瞬间就会被庞大的文本长度烧光。text Hello我是AI # -------------------------------------------------- # 1. 构建字符级词表 # -------------------------------------------------- # set(text) 用于获取文本中所有不重复的字符 # sorted() 用于固定字符顺序保证每次运行结果一致 characters sorted(set(text)) # 给每个字符分配一个唯一的整数 ID # 额外预留两个特殊 Token # 0 表示填充字符 PAD # 1 表示未知字符 UNK char_to_id { PAD: 0, UNK: 1, } # 从编号 2 开始为普通字符分配 ID for token_id, character in enumerate(characters, start2): char_to_id[character] token_id # 构建反向映射用于将数字 ID 还原成字符 id_to_char { token_id: character for character, token_id in char_to_id.items() } # -------------------------------------------------- # 2. 编码把文字转换成数字 # -------------------------------------------------- # 如果字符不在词表中就使用 UNK 的编号 unknown_token_id char_to_id[UNK] token_ids [ char_to_id.get(character, unknown_token_id) for character in text ] # -------------------------------------------------- # 3. 解码把数字重新还原成文字 # -------------------------------------------------- decoded_text .join( id_to_char.get(token_id, UNK) for token_id in token_ids ) # -------------------------------------------------- # 4. 打印结果 # -------------------------------------------------- print(原始文本, text) print(字符列表, list(text)) print(字符词表, char_to_id) print(Token ID, token_ids) print(解码结果, decoded_text) # -------------------------------------------------- # 5. 演示序列越长注意力计算成本越高 # -------------------------------------------------- sequence_length len(token_ids) # 标准自注意力需要比较序列中每两个 Token 的关系 # 因此可以用 n² 粗略表示其计算量 attention_cost sequence_length ** 2 print(Token 数量, sequence_length) print(粗略注意力计算量, attention_cost) # 假设序列长度翻倍 double_length sequence_length * 2 double_attention_cost double_length ** 2 print(长度翻倍后的 Token 数量, double_length) print(长度翻倍后的计算量, double_attention_cost) print( 计算量增加倍数, double_attention_cost / attention_cost, )上面这段代码演示了字符级 Tokenization 的完整过程构建字符词表 → 字符转数字 → 数字还原文字 → 粗略计算注意力成本。所以按照字符去切虽然简单但是字太碎、AI 理解成本太高又贵又低效。词级表示Word-level既然“按字切”太碎直接“按词切”行不行聪明的你顺理成章就会想到直接按“词”切不就好了比如I love coding拆成[I, love, coding]我喜欢苹果拆成[我, 喜欢, 苹果]这就是所谓的词粒度表示Word-level即一个词一个 token它的思路很直接给每个词分配一个编号。这样一来一句话就可以先被拆成词再变成一串数字交给模型处理。这种方案还挺符合人类的直觉首先是语义更完整比如“葡萄”不再是两个毫无意义的单字而是一个完整的水果另外句子也变短了拼图的块数少了对大模型来说序列越短计算压力越小尤其是注意力机制处理起来会轻松不少。但问题也正是从这里开始的。词粒度最大的问题就是我们人类的词太多了而且还在时时刻刻地不断变多你今天刚把词表整理好明天网上就冒出来一堆新词。比如绝绝子、YYDS、Citywalk、鸡你太美等等。当遇到这次词时大模型就直接懵圈了因为词表里没有这个词在NLP里这叫OOV 问题也就是Out Of Vocabulary未登录词问题。另外英语这种语言有多种“词形变化”比如单单一个“跑”就有run、running、ran、runs。明明核心意思一样却要在词库里硬生生占掉好几个坑。种种问题就导致在大模型底层每个词都需要配备一个高维向量Embedding来记录它的特征。如果为了涵盖所有词汇把词表膨胀到几百万个撑爆显存。import re def tokenize(text: str) - list[str]: 对英文文本进行简单的词级切分。 注意 1. 这里只是教学示例不是完整的工业级分词器。 2. 中文文本需要先经过结巴分词等中文分词工具。 # 将文本统一转换为小写避免 Love 和 love 占据两个词表位置 normalized_text text.lower() # 提取英文单词和数字 tokens re.findall(r[a-z]|\d, normalized_text) # 返回词语列表 return tokens # -------------------------------------------------- # 1. 准备训练语料 # -------------------------------------------------- corpus [ I love coding, I love Python, Python makes coding interesting, I run every day, She runs every day, ] # -------------------------------------------------- # 2. 对训练语料进行词级切分 # -------------------------------------------------- tokenized_corpus [ tokenize(sentence) for sentence in corpus ] print(分词结果) for tokens in tokenized_corpus: print(tokens) # -------------------------------------------------- # 3. 统计语料中的所有词 # -------------------------------------------------- # 将每个句子的词放入一个集合中自动去除重复词 unique_words { word for sentence_tokens in tokenized_corpus for word in sentence_tokens } # 对词语排序保证程序每次运行时编号一致 sorted_words sorted(unique_words) # -------------------------------------------------- # 4. 构建词表 # -------------------------------------------------- # 预留几个特殊 Token word_to_id { PAD: 0, # 用于补齐不同长度的句子 UNK: 1, # 用于表示词表中不存在的新词 } # 从编号 2 开始为普通词语分配编号 for token_id, word in enumerate(sorted_words, start2): word_to_id[word] token_id # 构建反向词表用于将编号还原为词语 id_to_word { token_id: word for word, token_id in word_to_id.items() } print(\n词表) for word, token_id in word_to_id.items(): print(f{word:12} - {token_id}) # -------------------------------------------------- # 5. 将句子编码成 Token ID # -------------------------------------------------- def encode(text: str) - list[int]: 将一段文本转换为词级 Token ID。 # 对输入文本进行词级切分 tokens tokenize(text) # 获取未知词对应的编号 unknown_token_id word_to_id[UNK] # 将每个词转换成对应编号 token_ids [ word_to_id.get(word, unknown_token_id) for word in tokens ] # 返回编号列表 return token_ids # 训练语料中出现过的句子 known_sentence I love Python # 包含新词的句子 oov_sentence I love Rust and Citywalk # 编码两个句子 known_token_ids encode(known_sentence) oov_token_ids encode(oov_sentence) print(\n已知句子, known_sentence) print(切分结果, tokenize(known_sentence)) print(Token ID, known_token_ids) print(\n包含新词的句子, oov_sentence) print(切分结果, tokenize(oov_sentence)) print(Token ID, oov_token_ids) # -------------------------------------------------- # 6. 展示 OOV 问题 # -------------------------------------------------- print(\nOOV 详细情况) for word in tokenize(oov_sentence): # 判断当前词是否存在于训练词表中 if word in word_to_id: print(f{word:10} - {word_to_id[word]}) else: print(f{word:10} - UNK词表中不存在) # -------------------------------------------------- # 7. 将 Token ID 解码回词语 # -------------------------------------------------- def decode(token_ids: list[int]) - str: 将 Token ID 列表还原为词语。 # 将每个编号转换回对应词语 words [ id_to_word.get(token_id, UNK) for token_id in token_ids ] # 使用空格将词语拼接成句子 return .join(words) print(\n解码结果, decode(oov_token_ids)) # -------------------------------------------------- # 8. 估算 Embedding 参数量 # -------------------------------------------------- # 当前词表大小 vocabulary_size len(word_to_id) # 假设每个词使用 768 维向量表示 embedding_dimension 768 # Embedding 层的参数量约等于词表大小 × 向量维度 embedding_parameters vocabulary_size * embedding_dimension print(\n当前词表大小, vocabulary_size) print(Embedding 维度, embedding_dimension) print(Embedding 参数量, embedding_parameters) # -------------------------------------------------- # 9. 模拟词表膨胀后的显存占用 # -------------------------------------------------- # 假设词表中存在 100 万个词 large_vocabulary_size 1_000_000 # 计算对应的 Embedding 参数量 large_embedding_parameters ( large_vocabulary_size * embedding_dimension ) # 假设每个参数使用 float32占用 4 字节 bytes_per_parameter 4 # 计算 Embedding 矩阵占用的总字节数 embedding_memory_bytes ( large_embedding_parameters * bytes_per_parameter ) # 将字节转换成 GB embedding_memory_gb embedding_memory_bytes / 1024**3 print(\n假设词表大小, large_vocabulary_size) print(Embedding 参数量, large_embedding_parameters) print(f仅 Embedding 矩阵约占{embedding_memory_gb:.2f} GB)上面这段代码演示词级表示的核心流程按词切分 → 构建词表 → 词转数字 → 遇到新词映射为UNK→ 估算 Embedding 参数量。所以按词切分解决了“太碎”的问题却又亲手制造了“不认识新词”和“词表爆炸”的灾难。子词分词与BPE、BBPE经过上面2种方案我们可以发现按字拆太碎模型理解成本高按词拆词太多词表撑爆显存还认不全新词。那么有没有一种“折中”的方案有的有的兄弟这就是大模型时代的核心解法——子词分词法Subword Tokenization。所谓的子词就是介于“单个字符”和“完整单词”之间的小片段或者可以说是 常见词根/组合。如果是高频词直接整体保留比如dog、我们、中国不用拆一口气吞下。那么如果是低频词、复杂词拆成“通用小零件” 比如unbelievable难以置信不用当成一个生词硬记而是拆成[un, believ, able]。这样一来模型根本不需要死记硬背世界上所有的单词只需要掌握一批有高复用价值的小零件。以后不管遇到什么新词、生僻词大不了就把它拆开用手头的积木“拼”着去理解那该如何找到这些“小零件”字词呢子词分词里最经典的做法叫BPE字节对编码Byte Pair Encoding它的灵魂就是高频合并低频拆分。我们可以把它的训练过程看作是 AI 在“找规律”先拆散一开始AI 什么词都不认识只能把所有文本拆成最基本的零散字符。比如l o w、l o w e r。找高频它开始疯狂统计发现l和o这两个字符老是紧挨在一起出没。做合并把它们胶合在一起变成一个新的 Token——lo。不断循环接着发现lo和w也是连体婴儿那就继续合并成low往后还能把e和r合并成er。BPE 的成长轨迹就像拼图l o w e r ➡️ lo w er ➡️ low er随着合并的词逐渐增多一套既不过分庞大、又不会太碎片化的Token 词表就这样自然生长出来了。下面代码演示了BPE从字符开始合并from collections import Counter def merge_pair(tokens, pair): 合并指定的相邻 Token。 result [] index 0 while index len(tokens): if ( index 1 len(tokens) and (tokens[index], tokens[index 1]) pair ): result.append(tokens[index] tokens[index 1]) index 2 else: result.append(tokens[index]) index 1 return result # 初始语料每个单词先拆成字符 corpus [ list(low), list(lower), list(lowest), ] # 连续执行 5 次高频合并 for _ in range(5): pair_counts Counter() # 统计相邻 Token 对 for tokens in corpus: pair_counts.update(zip(tokens, tokens[1:])) # 找出出现次数最多的组合 best_pair pair_counts.most_common(1)[0][0] # 在所有单词中应用合并规则 corpus [ merge_pair(tokens, best_pair) for tokens in corpus ] print(合并, best_pair) print(结果, corpus)图片那BPE到底强在哪里解决了词形变化导致的浪费以前code、coding、coded、coder要占四个坑现在有了子词大家都共享词根cod只要再加上ing或er这些小后缀就行词表缩减无数倍。解决了新词不认识OOV 模型没见过Citywalk这个完整新梗没有关系BPE 可以把它拆解为City walk。只要认识这两个常见零件AI 就不会两眼一黑照样能猜出个八九不离十。现实世界远远没有这么简单传统 BPE 已经很强了但现实世界里不光有英文和汉字还有日文、阿拉伯文、甚至无穷无尽的Emoji表情像这种 ❤️。如今顶尖的大模型如 GPT、Claude、DeepSeek 等都使用了更底层的Byte-level BPE字节级 BPE简称 BBPE不管你是文字、符号还是 Emoji 表情对于计算机来说最终全都是由字节Byte组成的。相较于BPE,BBPE不再从“字符”开始合并而是从256 个基础字节0x00到0xFF开始合并那么理论上这个世界上的任何一段文本、任何一个神秘符号都有兜底方案下面代码演示了BBPE从 UTF-8 字节开始合并from collections import Counter def merge_pair(tokens, pair): 合并指定的相邻字节 Token。 result [] index 0 while index len(tokens): if ( index 1 len(tokens) and (tokens[index], tokens[index 1]) pair ): result.append(tokens[index] tokens[index 1]) index 2 else: result.append(tokens[index]) index 1 return result text 你好 # 每个基础 Token 都是一个 UTF-8 字节 tokens [ bytes([byte]) for byte in text.encode(utf-8) ] print(原始字节, [token.hex() for token in tokens]) # 简单演示多轮高频字节合并 for _ in range(3): pair_counts Counter(zip(tokens, tokens[1:])) if not pair_counts: break best_pair pair_counts.most_common(1)[0][0] tokens merge_pair(tokens, best_pair) print(BBPE Token, [token.hex() for token in tokens]) # 将所有字节 Token 拼接并还原文本 restored_text b.join(tokens).decode(utf-8) print(还原文本, restored_text)最差的情况无非就是退回到几个最基本的“字节”来处理永远不可能出现“词表里完全没法表示”的情况。所以Token 究竟是什么Token是大模型眼中的“基本阅读和书写单位它未必对于者人类语法里的一个“字”或一个“词”它的设计灵魂在于“折中与平衡”让大模型用有限的词表以最低的算力成本高效、精准地覆盖了这个世界上无限变化的语言。Token 会影响使用成本吗会而且非常直接。在 AI 世界里Token 就是计费单位。你输入的问题要消耗 TokenAI 回答的内容也一样要消耗 Token。我们可以简单记一个公式输入越长 回答越多 消耗越大 耗费越贵。对于在网页端免费聊天的普通用户来说这种成本可能没什么感觉。但如果用到付费 API或者把 AI 接入自己的软件里这些消耗都会实打实地变成账单上的数字。另外现在聪明的 AI 模型还有两类特殊消耗缓存记录那些反复发过的重复背景系统可能会打折计费内部思考逻辑推导能力强的模型在正式开口前会在肚子里“默默思考”这部分脑力推演同样要消耗资源和费用。懂一点 Token对咱们普通人有什么用千万别觉得Token只是程序员才要关心的代码概念。只要你每天都在用 AI懂这点底层逻辑就能让你用得更顺手、更聪明。在智能时代好的沟通表达本身就是生产力给足关键信息把背景说清、把要求提准、把限制列明。该详细的地方给足条件该简洁的地方果断砍掉。你给它的输入越干净清晰它还你的答案就越靠谱。学习资源推荐如果你想更深入地学习大模型以下是一些非常有价值的学习资源这些资源将帮助你从不同角度学习大模型提升你的实践能力。一、全套AGI大模型学习路线AI大模型时代的学习之旅从基础到前沿掌握人工智能的核心技能​因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获取二、640套AI大模型报告合集这套包含640份报告的合集涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师还是对AI大模型感兴趣的爱好者这套报告合集都将为您提供宝贵的信息和启示​因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获取三、AI大模型经典PDF籍随着人工智能技术的飞速发展AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型如GPT-3、BERT、XLNet等以其强大的语言理解和生成能力正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获取四、AI大模型商业化落地方案作为普通人入局大模型时代需要持续学习和实践不断提高自己的技能和认知水平同时也需要有责任感和伦理意识为人工智能的健康发展贡献力量。