一、核心概念
1. Token 化(分词)
- 本质:把文本拆成大模型能 "看懂" 的最小单位(Token),并给每个单位分配唯一 ID;
- 举例:"C++ LLM 学习" 拆为
C、+、+、(空格)、L、L、M、学、习,每个字符对应一个 ID; - 关键:字符级 Token 化是基础,BPE/WordPiece 是更高级的优化方式,核心都是 "文本→最小单位→ID"。
2. 词嵌入(Embedding)
- 本质:把 Token ID 转换成有 "语义" 的数字向量(如
[0.23, -1.56, 3.11]); - 维度逻辑:维度(如 768 维)是描述 Token 的 "特征数量",维度越高,能表达的语义越精细;
- 核心作用:让模型能通过 "数字相似度" 理解文字含义(比如 "学习" 和 "研习" 的向量数值相近)。
3. 预训练 / 微调 / 推理
|-----|------|-----------|--------------|
| 阶段 | 类比 | 目标 | 场景 |
| 预训练 | 通识教育 | 学习通用知识 | 大厂海量数据训练基础模型 |
| 微调 | 职业培训 | 适配具体场景 | 开发者用小数据优化模型 |
| 推理 | 上岗干活 | 处理新输入给出答案 | 实际使用模型 |
二、"文本→Token→Embedding" 完整转换流
-
文本转 Token:给文字 "贴数字标签"
- 拆分:把一句话拆成最小文字碎片(Token);
- 编码:给每个碎片分配唯一 ID(如
C=0、+=1、学=5); - 目的:让电脑能 "数数字" 理解文字。
-
Token 转 Embedding:给数字 "赋语义"
- 转换:把 Token ID 变成一组有规律的数字向量;
- 核心:语义相近的文字,对应的向量数值也相近;
- 目的:让模型能识别文字的实际含义。
总结:人类文字 → 电脑可计数的 ID → 电脑可理解的语义向量,是大模型 "看懂文字" 的核心步骤。
三、C++ 字符级 Token 化核心逻辑(处理中英文)
1. 核心问题
中文在 UTF-8 编码下占 2~4 字节(绝大多数 3 字节),直接按单字节遍历会拆成乱码,需先识别完整字符。
2. 关键代码逻辑
// 判断多字节字符的完整长度
unsigned char c = static_cast<unsigned char>(*it);
if (c >= 0xF0) bytes = 4; // 4字节字符
else if (c >= 0xE0) bytes = 3;// 3字节字符
else if (c >= 0xC0) bytes = 2;// 2字节字符
// 拼接完整字符
for (int i = 0; i < bytes && it != input_text.end(); ++i, ++it) {
current_char += *it;
}
完整代码:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
std::vector<int> characterTokenize(const std::string& input_text) {
// 存储字符到Token ID的映射(字典),初始为空
static std::unordered_map<std::string, int> char_to_id;
// 存储最终的Token ID列表
std::vector<int> token_ids;
// 下一个要分配的Token ID
static int next_id = 0;
// 遍历输入文本的每个字符
// 用迭代器遍历每个字符
for (auto it = input_text.begin(); it != input_text.end(); ) {
// 需要处理多字节中文
std::string current_char;
// 单字节
if (static_cast<unsigned char>(*it) < 128) {
current_char = *it;
++it;
}
else {
// 多字节
int bytes = 0;
unsigned char c = static_cast<unsigned char>(*it);
if (c >= 0xF0) bytes = 4;
else if (c >= 0xE0) bytes = 3;
else if (c >= 0xC0) bytes = 2;
else bytes = 1;
// 拼接字符
for (int i = 0; i < bytes && it != input_text.end(); ++i, ++it) {
current_char += *it;
}
}
// 如果当前字符还没有分配ID,就分配一个新的
if (char_to_id.find(current_char) == char_to_id.end()) {
char_to_id[current_char] = next_id++;
}
// 将当前字符的ID加入结果列表
token_ids.push_back(char_to_id[current_char]);
std::cout << "字符: \"" << current_char << "\" -> Token ID: " << char_to_id[current_char] << std::endl;
}
return token_ids;
}
// 测试函数
int main() {
std::string test_text = "C++ LLM学习";
std::cout << test_text << std::endl;
// 调用Token化函数
std::vector<int> result_ids = characterTokenize(test_text);
std::cout << "Token ID列表:";
for (int id : result_ids) {
std::cout << id << " ";
}
std::cout << std::endl;
// 额外测试
std::cout << "\n重复测试" << std::endl;
std::string test_text2 = "学习C++";
std::cout << "输入文本:" << test_text2 << std::endl;
std::vector<int> result_ids2 = characterTokenize(test_text2);
std::cout << "\nToken ID列表:";
for (int id : result_ids2) {
std::cout << id << " ";
}
std::cout << std::endl;
return 0;
}
3. 核心作用
- 识别中文等多字节字符的完整长度;
- 拼接零散字节为完整字符(如 "学" 的 3 个字节拼成一个 Token);
- 保证 Token 化结果正确,无乱码、错拆。
四、总结
- Token 化是 "文本→数字 ID" 的转换,核心是拆分为最小单位并编码,处理中文需适配 UTF-8 多字节规则;
- Embedding 是 "ID→语义向量" 的转换,让模型能理解文字含义;
- 预训练 / 微调 / 推理分别对应大模型的 "学习、适配、使用" 阶段,目标和场景不同。