深入理解 LLM Tokenization:从文本分词到语义向量化的完整旅程


🧠 深入理解 LLM Tokenization:从文本分词到语义向量化的完整旅程

📌 本文带你从零理解大模型如何处理文本 ------ 从 Token 分词到 Embedding 向量化,再到语义相似度计算,附完整可运行代码。


一、为什么理解 Tokenization 很重要?

当我们使用 ChatGPT、Claude 等大模型时,按 Token 计费是常态。但 Token 到底是什么?为什么 "Hello, world!" 不是 13 个 Token?为什么中文往往比英文消耗更多 Token?

理解 Tokenization(分词)能帮你:

  • 🪙 控制 API 成本(按 Token 计费)
  • 📐 合理设计 Prompt(模型有上下文窗口限制)
  • 🔍 理解模型处理语言的方式
  • 🛠️ 排查模型输出异常(某些词被奇怪地拆分)

二、Tokenization 全景架构

LLM 处理文本的完整流水线分为六个环节:

第一步:用户输入原始文本,进入分词器(Tokenizer),将自然语言文本拆解为模型能理解的最小单位------Token。

第二步:每个 Token 被映射为一个唯一的数字 ID,形成数字序列。模型不"读"文字,它只"读"数字。

第三步:Token ID 序列进入 Embedding 层,每个 ID 被映射为一个高维浮点数向量(如 1024 维)。这个向量承载了该 Token 的语义信息。

第四步:向量序列送入 Transformer 模型进行推理计算。

第五步:模型输出 logits(概率分布),解码器从中采样生成新的 Token ID 序列。

第六步:De-Tokenizer(分词器的反向操作)将 Token ID 序列还原为人类可读的文本。

核心要点:模型不"读"文字,它只"读"数字。Tokenization 是文本和数字之间的双向桥梁。


三、实战一:用 tiktoken 亲手体验分词

3.1 什么是 tiktoken?

tiktoken 是 OpenAI 开源的快速 BPE(Byte Pair Encoding)分词库,GPT-4/GPT-3.5 等模型直接使用它进行分词。

关键概念:

  • cl100k_base:GPT-4 和 GPT-3.5-turbo 使用的编码表,约 100,000 个 Token。它是基于大规模多语言语料训练出来的 BPE 词表。
  • encode:将文本转为 Token ID 序列(数字数组)
  • decode:将 Token ID 序列还原为文本

3.2 完整代码

javascript 复制代码
import { getEncoding } from "js-tiktoken";

// 获取 GPT 官方的 Token 编码表 cl100k_base
// 底层基于 UTF-8 编码
const enc = getEncoding("cl100k_base");

// 中英混合文本
const text = "Hello, tiktoken! 你好,世界!";

// 编码:文本 → Token ID 数组
const tokens = enc.encode(text);
console.log("Token IDs:", tokens);
console.log("Token 数量:", tokens.length);

// 解码:Token ID 数组 → 文本
const decodedText = enc.decode(tokens);
console.log("解码还原:", decodedText);

// 查看每个 Token 对应的文本片段
for (const token of tokens) {
  console.log(`Token ${token} → "${enc.decode([token])}"`);
}

3.3 运行结果分析

"Hello, tiktoken! 你好,世界!" 为例,运行后你会看到每个 Token ID 对应的文本片段。你会发现:

  • 英文单词 通常是 1~2 个 Token(如 Hello 是一个 Token)
  • 标点符号空格也是独立的 Token
  • 中文字符往往是 1~2 个 Token 一个汉字
  • 中文比英文"贵"的根本原因:英文单词本身就承载丰富语义,一个词就是一个 Token;而中文需要多个 Token 组合才能表达一个词

四、深入 BPE 分词算法原理

4.1 分词方式对比

在 BPE 出现之前,有几种传统的分词方式:

  • 按空格分词 :将 "I love NLP" 拆成 ["I", "love", "NLP"]。问题很明显------中文、日语等语言根本没有空格分隔。
  • 按字符分词 :将 "hello" 拆成 ["h", "e", "l", "l", "o"]。序列变得极长,Transformer 的计算复杂度是 O(n²),效率急剧下降。
  • 按词分词:维护一个巨大的词表,每个完整单词一个 ID。词表太大,且无法处理未见过的词(OOV 问题)。

BPE(Byte Pair Encoding) 是一个优雅的折中方案。它的核心思想是:从字符级别出发,反复合并出现频率最高的相邻符号对,直到达到目标词表大小

4.2 BPE 训练过程

假设我们有语料:"low low low low low lower lower newest newest newest" 代表词频。

初始状态:将每个字符视为独立符号(包括空格和单词边界标记)。

第一轮合并 :统计所有相邻符号对的出现频率,发现 "l" + "o" 组合出现次数最多(在 lowlower 中都出现了),于是将其合并为一个新符号 "lo"

第二轮合并"lo" + "w" 的组合出现频率最高(low 反复出现),合并为 "low"

第三轮合并"e" + "r" 的组合出现频率最高(lowernewest 中),合并为 "er"

持续迭代,直到达到预设词表大小(如 100,000)。最终词表中既有高频完整词(如 "the""low"),也有常见的子词片段(如 "ing""er""tion")。

4.3 BPE 的优势

维度 优势
🆕 OOV 处理 通过子词组合可表示任意未见词,没有"未知词"问题
📦 词表大小可控 通常在 30K~100K 之间,由训练者决定
🌍 多语言友好 无需针对不同语言设计不同的分词器
编解码高效 词表适中,查找和合并速度都很快

五、实战二:从 Token 到语义向量(Embedding)

5.1 为什么需要 Embedding?

了解 Token 之后,下一个问题是:模型怎么"理解" Token 的含义?答案是 Embedding(嵌入)

Token ID 只是一个整数标识符,本身不携带任何语义信息------ID 为 99061234 之间没有任何数值关系。Embedding 层的作用,就是将每个 Token ID 映射到一个高维稠密向量(如 1024 维的浮点数数组),让语义相似的词在向量空间中的距离也接近。

实际效果是:"猫""狗" 的向量在空间中距离很近(都是宠物),而 "猫""汽车" 的向量距离很远(无关概念)。更神奇的是,这种语义关系是跨语言的------中文的 "猫" 和英文的 "cat" 在空间中也非常接近。

5.2 调用 Embedding API

这里使用阿里云 DashScope(兼容 OpenAI SDK),选择 text-embedding-v4 模型输出 1024 维向量:

javascript 复制代码
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config();

const client = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
});

/**
 * 文本向量化封装
 * @param {string} text - 输入文本
 * @returns {number[]} - 1024维浮点数向量
 */
async function getEmbedding(text) {
  const res = await client.embeddings.create({
    model: "text-embedding-v4",  // 嵌入模型
    input: text,
    dimensions: 1024,            // 输出维度
  });
  return res.data[0].embedding;
}

Embedding API 参数详解:

参数 说明
model 嵌入模型名称。阿里云用 text-embedding-v4,OpenAI 用 text-embedding-3-small
input 要向量化的文本,支持批量传入字符串数组
dimensions 输出向量的维度。越高保留的语义信息越精细,但计算和存储开销也更大。常用 768、1024、1536

⚠️ 重要提示 :不同 Embedding 模型产出的向量不可混用!如果用 v3 模型向量化了知识库,用 v4 模型去做查询,得出的相似度毫无意义。务必保证编码和查询使用同一个模型。


六、实战三:余弦相似度 ------ 用数学衡量语义

6.1 算法原理

有了向量之后,如何衡量两个向量的相似程度?答案是 余弦相似度

它衡量两个向量在方向上的接近程度,值域为 [-1, 1]1 表示方向完全一致(语义相同),0 表示正交(无关),-1 表示方向完全相反(语义对立)。

公式很简单:两个向量的点积除以它们模长的乘积。

6.2 代码实现

ini 复制代码
/**
 * 余弦相似度计算
 * @param {number[]} vecA - 向量A
 * @param {number[]} vecB - 向量B
 * @returns {number} - 相似度 [-1, 1]
 */
function cosineSimilarity(vecA, vecB) {
  let dotProduct = 0;
  let magnitudeA = 0;
  let magnitudeB = 0;

  for (let i = 0; i < vecA.length; i++) {
    dotProduct += vecA[i] * vecB[i];   // 点积:对应位置相乘再求和
    magnitudeA += vecA[i] ** 2;        // A 向量模的平方
    magnitudeB += vecB[i] ** 2;        // B 向量模的平方
  }

  return dotProduct / (Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB));
}

6.3 语义对比实验

设计两组对照实验来验证效果:

ini 复制代码
async function run() {
  // 实验组1:中英文表达同一语义(Andrej Karpathy 讲解 LLM 分词)
  const text1 = "Andrej Karpathy LLM Tokenization 分词原理";
  const text2 = "卡帕西讲解大模型BPE字词分词";

  // 实验组2:完全不相关的语义
  const text3 = "今天天气晴朗,适合出门散步";

  const vec1 = await getEmbedding(text1);
  const vec2 = await getEmbedding(text2);
  const vec3 = await getEmbedding(text3);

  // 跨语言同语义对比
  const similarity_en_zh = cosineSimilarity(vec1, vec2);
  console.log("跨语言同语义相似度:", similarity_en_zh);
  // 预期:0.85+ ------ 高度相似

  // 不同语义对比
  const similarity_diff = cosineSimilarity(vec1, vec3);
  console.log("不同语义相似度:", similarity_diff);
  // 预期:0.3~0.5 ------ 明显更低
}

run();

6.4 结果解读

典型运行结果类似:

makefile 复制代码
跨语言同语义相似度: 0.876
不同语义相似度:     0.342

尽管 text1 是英文、text2 是中文,它们表达的是同一个主题,余弦相似度高达 0.87。而 text1text3 主题完全不搭边,相似度骤降到 0.34。

这揭示了一个重要特性:Embedding 向量天然具有跨语言语义对齐能力。无论你用哪种语言表达,只要语义相同,向量就会聚在一起。这正是语义搜索、跨语言检索等技术的基础。


七、完整流水线代码

将上述三部分串联------这就是 LLM 处理文本的完整前端流水线:

javascript 复制代码
import { getEncoding } from "js-tiktoken";
import OpenAI from "openai";
import dotenv from "dotenv";
dotenv.config();

// ==================== 阶段1: Tokenization ====================
const enc = getEncoding("cl100k_base");

function tokenize(text) {
  const tokens = enc.encode(text);
  console.log(`[Tokenize] "${text}" → ${tokens.length} tokens`);
  return tokens;
}

// ==================== 阶段2: Embedding ====================
const client = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
});

async function getEmbedding(text) {
  const res = await client.embeddings.create({
    model: "text-embedding-v4",
    input: text,
    dimensions: 1024,
  });
  console.log(`[Embedding] "${text.slice(0, 20)}..." → [${res.data[0].embedding.length}维向量]`);
  return res.data[0].embedding;
}

// ==================== 阶段3: 相似度计算 ====================
function cosineSimilarity(vecA, vecB) {
  let dot = 0, magA = 0, magB = 0;
  for (let i = 0; i < vecA.length; i++) {
    dot += vecA[i] * vecB[i];
    magA += vecA[i] ** 2;
    magB += vecB[i] ** 2;
  }
  return dot / (Math.sqrt(magA) * Math.sqrt(magB));
}

// ==================== 完整流程 ====================
async function fullPipeline(text1, text2) {
  console.log("========== LLM 文本处理流水线 ==========\n");

  const tokens1 = tokenize(text1);
  const tokens2 = tokenize(text2);

  const vec1 = await getEmbedding(text1);
  const vec2 = await getEmbedding(text2);

  const similarity = cosineSimilarity(vec1, vec2);

  console.log(`\n[Result] 余弦相似度: ${similarity.toFixed(4)}`);
  console.log(`[Result] 百分比:     ${(similarity * 100).toFixed(1)}%`);
  console.log("==========================================");
}

// 运行
fullPipeline(
  "LLM Tokenization explained by Andrej Karpathy",
  "Andrej Karpathy 讲解大模型分词技术"
);

八、应用场景

掌握了 Tokenization + Embedding + 相似度这条流水线,你可以落地很多实用功能:

应用场景 核心原理
🔍 语义搜索 将查询和文档都向量化,找余弦相似度最高的
📊 文本聚类 将相似文本的向量聚在一起(K-Means 等算法)
🏷️ 文本分类 基于向量距离做零样本分类,无需额外训练
🌐 跨语言检索 中英文问句用同一 Embedding 模型编码后直接匹配
🚫 内容去重 相似度超过阈值则视为重复内容
💬 RAG 问答 用户问题向量化 → 检索最相关文档片段 → 拼接后喂给 LLM

这些都是目前 AI 应用开发中非常成熟和常见的技术模式。


九、关键要点总结

Tokenization 是 LLM 的入口------它将人类文字翻译成模型能理解的数字,也是计费和上下文管理的基础。

三个环节的分工:

环节 核心工具/算法 输入 输出
分词 tiktoken (BPE, cl100k_base) 文本字符串 Token ID 数组
向量化 Embedding API (text-embedding-v4) 文本字符串 高维浮点向量
相似度 余弦相似度公式 两个向量 -1 到 1 的标量

记忆三件事:

  1. BPE 是主流分词算法:通过统计字符对频率迭代合并构建子词词表,平衡了效率和覆盖面。
  2. Embedding 是语义的数学表达:将语言映射到高维空间,让"意思相近"变成"距离相近",且天然支持跨语言。
  3. 余弦相似度是语义比较的标准工具:关注方向而非绝对大小,是向量检索的基石。

十、扩展阅读


相关推荐
拾光拾趣录3 小时前
为什么选择 ReAct 模式而不是 Plan-and-Execute?
人工智能
武子康4 小时前
调查研究-196 CEO-Bench:Agent 不再只是“做任务“,而是要学会“经营一个系统“
人工智能
用户329901675054 小时前
把AI返回的Markdown表格渲染成可排序表格
人工智能
还好还好不是吗4 小时前
MatrixMedia HTTP 发布接口:让 AI 工作流直接驱动多平台视频发布
人工智能
贵慜_Derek4 小时前
复杂系统没法一把梭重构:Semi-Autoresearch 怎么小步迁移还不掉功能
人工智能·agent·ai编程
ctxinf4 小时前
Vercel Eve 实际上手初探
人工智能
用户5191495848454 小时前
利用ShellcodePack实现DLL劫持与COM对象劫持技术详解
人工智能·aigc
武子康4 小时前
调查研究-195 从 AmEx 支付系统看 Cell-based Architecture:真正的高可用,不是无限重试,而是控制失败边界
人工智能·openai·agent
米小虾4 小时前
Prompt Engineering —— 意图的精确表达
人工智能·agent
IT_陈寒4 小时前
React状态更新总是慢半拍?你可能忘了这个默认行为
前端·人工智能·后端