文章目录
一、核心关系概述
分词工具(如Jieba、SentencePiece)与 AI大模型(如GPT、BERT)的关系可总结为:
- 分词工具是AI大模型的“前处理引擎”,为大模型提供数据预处理支持
- 大模型是任务的“智能大脑”,它利用分词结果可以进行更高级的语言理解和生成任务
二、分词工具的核心作用
- 文本标准化处理
- 中文分词示例:
"深度学习" → ["深度", "学习"]
- 解决英文子词问题:
"unhappiness" → ["un", "happiness"]
- 中文分词示例:
- 降低计算复杂度
- 长文本分割为词/子词单元,减少模型计算量
- 跨语言适配
- 针对不同语言特性设计(如中文需分词,英文需处理子词)
三、未登录词(OOV)问题
3.1 问题本质分析
分词工具(如 Jieba)分出的词汇后续由 Tokenizer(如 BERT)转换为词汇表中的索引时,若词汇表中不包含某些词,则会出现 OOV(Out-of-Vocabulary)问题,这会导致:
- 稀有词/专业术语被拆解为
<UNK>
(未知标记) - 语义信息丢失(如 “量子计算” →
[UNK]
) - 模型性能下降
3.2 解决方案
3.2.1 预对齐词汇表(最优解)
原理:强制分词工具只输出 Tokenizer 词汇表中存在的词汇
from transformers import BertTokenizer
import jieba
# 加载Tokenizer并提取词汇表
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")
vocab = set(tokenizer.get_vocab().keys()) # 获取所有合法token
# 定制化分词函数
def aligned_cut(text):
words = []
for word in jieba.lcut(text):
# 检查词汇是否存在(处理子词情况)
if word in vocab:
words.append(word)
else:
# 递归拆解未登录词(直到字符级)
for char in word:
if char in vocab:
words.append(char)
else:
words.append("[UNK]")
return words
# 测试
text = "量子计算是未来趋势"
print(aligned_cut(text)) # 输出保证在vocab中的分词
3.2.2 子词回退策略
原理:利用 Tokenizer 的子词分解能力处理未登录词
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
def safe_tokenize(text):
# 先用分词工具粗分
words = jieba.lcut(text)
# 再用Tokenizer的子词处理
tokens = []
for word in words:
tokens.extend(tokenizer.tokenize(word)) # 自动处理OOV
return tokens
# 测试
print(safe_tokenize("区块链技术")) # ['区', '块', '链', '技术']
3.2.3 词汇表扩展(适合专业领域)
步骤:
-
收集领域专有词汇(如医疗术语)
-
添加到分词工具用户词典
jieba.load_userdict("medical_terms.txt") # 每行格式: `术语 词频 词性`
-
微调 Tokenizer(需重新训练模型)
new_tokens = ["COVID-19", "mRNA疫苗"] tokenizer.add_tokens(new_tokens) # 扩展词汇表 model.resize_token_embeddings(len(tokenizer)) # 调整模型嵌入层
3.3 技术选型建议
场景 | 推荐方案 | 优点 | 缺点 |
---|---|---|---|
通用领域 | 子词回退策略 | 无需额外资源 | 可能拆解专业术语 |
专业领域(如医疗/法律) | 词汇表扩展+预对齐 | 保持术语完整性 | 需要领域词典和模型微调 |
多语言混合文本 | 使用SentencePiece分词 | 统一处理多种语言 | 需替换原有分词工具 |
3.4 关键验证方法
-
覆盖率测试
def check_coverage(texts): vocab = set(tokenizer.get_vocab().keys()) oov_rate = 0 for text in texts: words = jieba.lcut(text) oov_rate += sum(1 for w in words if w not in vocab) / len(words) print(f"OOV率: {oov_rate/len(texts):.2%}") check_coverage(["量子物理", "临床试验"]) # 示例输出: OOV率: 15.00%
-
可视化调试
from transformers import pipeline nlp = pipeline("ner", model="bert-base-chinese") text = "患者有COVID-19症状" print(nlp(text)) # 检查专业术语是否被正确识别
3.5 总结
通过 词汇表预对齐 + 子词回退 + 领域适配扩展 的组合策略,可确保:
✅ 分词结果 100% 被 Tokenizer 接受
✅ 专业术语完整性保留
✅ 避免 <UNK>
导致的语义损失
最终效果取决于分词工具与 Tokenizer 的协同设计,建议在预处理阶段加入 OOV检测模块 进行质量监控。
四、分词工具与Tokenizer的区别
特性 | jieba(传统分词工具) | 大模型Tokenizer(如BERT/GPT) |
---|---|---|
设计目标 | 针对特定语言(如中文)的词汇级分割 | 将文本转换为模型可处理的子词/字符级ID |
输出单元 | 词语(如[“深度学习”, “是”, “未来”]) | 子词(如[“深”, “度”, “学习”, “是”, “未”, “来”]) |
语言适应性 | 需针对不同语言训练专用模型 | 通过统一算法(如BPE/WordPiece)支持多语言 |
典型应用场景 | 搜索引擎、文本分析等传统NLP任务 | 大模型的输入预处理 |
问题:为什么大模型仍需自研Tokenizer?
- 子词平衡:Tokenizer通过算法(如BPE)解决OOV(未登录词)问题,而
jieba
无法动态生成子词。 - 多语言统一:大模型需处理混合语言文本(如中英混杂),
jieba
仅支持中文。 - 端到端训练:Tokenizer的分词方式与模型架构强相关(如BERT的WordPiece需与预训练一致)。
五、NLP 中的特殊标记
在自然语言处理(NLP)任务中,特殊标记(Special Tokens)用于处理文本输入和输出的特定需求。以下是常见的特殊标记及其作用:
5.1 特殊标记解释
-
[CLS]
(Classification Token)- 作用:用于分类任务的特殊标记。
- 位置:通常添加到输入文本的开头。
- 用途:
- 在 BERT 等模型中,
[CLS]
标记的最终隐藏状态(即模型输出的对应向量)通常用作整个输入序列的聚合表示,用于分类任务(如情感分析、文本分类)。 - 例如,在句子分类任务中,模型会根据
[CLS]
标记的向量输出分类结果。
- 在 BERT 等模型中,
-
[SEP]
(Separator Token)- 作用:用于分隔不同句子或文本段的特殊标记。
- 位置:
- 在单句任务中,通常添加到句子末尾。
- 在双句任务(如句子对分类、问答任务)中,用于分隔两个句子。
- 用途:
- 帮助模型区分不同的句子或文本段。
- 例如,在问答任务中,
[SEP]
标记用于分隔问题和上下文。
-
[MASK]
(Mask Token)- 作用:用于掩码语言模型(Masked Language Model, MLM)任务。
- 位置:替换输入文本中的某些词(通常随机选择)。
- 用途:
- 在 BERT 等模型的预训练过程中,
[MASK]
标记用于掩盖部分输入词,模型需要预测被掩盖的词。 - 例如,输入
"I love [MASK] learning."
,模型需要预测[MASK]
位置的实际词(如"deep"
)。
- 在 BERT 等模型的预训练过程中,
-
[PAD]
(Padding Token)- 作用:用于填充输入序列,使其达到固定长度。
- 位置:添加到输入序列的末尾。
- 用途:
- 在批处理(Batching)过程中,不同序列的长度可能不同,
[PAD]
标记用于将短序列填充到相同长度。 - 模型通常会忽略
[PAD]
标记的计算(通过注意力掩码实现)。
- 在批处理(Batching)过程中,不同序列的长度可能不同,
-
[UNK]
(Unknown Token)- 作用:用于表示词汇表中未包含的词(即未知词)。
- 位置:替换输入文本中的未知词。
- 用途:
- 当输入文本中的词不在模型的词汇表中时,模型会将其替换为
[UNK]
标记。 - 例如,如果词汇表中没有
"ChatGPT"
,输入"I use ChatGPT."
可能会被转换为"I use [UNK]."
。
- 当输入文本中的词不在模型的词汇表中时,模型会将其替换为
5.2 示例
以下是一个包含特殊标记的输入示例(以 BERT 为例):
[CLS] I love deep learning . [SEP] It is a fascinating field . [SEP] [PAD] [PAD]
六、大模型输入的核心组成部分
在自然语言处理(NLP)和大模型(如BERT、GPT等)中,input_ids
、attention_mask
和 token_type_ids
是模型输入的核心组成部分,用于将原始文本转换为模型可处理的数值化格式。以下是它们的详细解释和实际示例:
6.1 名词解释
-
input_ids
(文本的数值化表示)-
作用:将分词后的文本(Tokens)转换为模型词汇表中对应的整数ID。
-
生成方式:
- 分词器(Tokenizer)先将文本拆分为词/子词(如
"深度学习"
→["深", "度", "学", "习"]
)。 - 然后查询词汇表,将每个Token映射为对应的ID(如
"深"
→3918
)。
- 分词器(Tokenizer)先将文本拆分为词/子词(如
-
示例:
from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese") text = "深度学习很重要" inputs = tokenizer(text) print(inputs["input_ids"]) # 输出如:[101, 3918, 2428, 2110, 739, 2523, 7028, 6206, 102]
101
和102
是BERT的[CLS]
和[SEP]
特殊标记的ID。
-
-
attention_mask
(注意力掩码)-
作用:标识哪些Token是有效输入,哪些是填充(Padding)部分。
1
:真实Token(模型需处理)。0
:填充Token(模型忽略)。
-
为什么需要:批量训练时,不同文本长度不同,需填充到相同长度。
-
示例:
print(inputs["attention_mask"]) # 输出如:[1, 1, 1, 1, 1, 1, 1](无填充)
如果填充到长度10:
padded_inputs = tokenizer(text, padding="max_length", max_length=10) print(padded_inputs["attention_mask"]) # 输出如:[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
-
-
token_type_ids
(或segment_ids
,句子分段标识):-
作用:区分输入中的不同句子(如问答任务中的问题和上下文)。
0
:第一个句子。1
:第二个句子。
-
适用场景:BERT等模型处理句子对任务(如文本相似度、问答)。
-
示例:
text_pair = ("深度学习是什么?", "它是AI的一个分支") inputs = tokenizer(*text_pair) print(inputs["token_type_ids"]) # 输出如:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-
6.2 输入示例
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
# 处理单句
single_text = "你好,世界!"
inputs = tokenizer(single_text, return_tensors="pt")
print("单句输入:")
print(f"input_ids: {inputs['input_ids']}") # 输出 input_ids: tensor([[ 101, 872, 1962, 8024, 686, 4518, 8013, 102]])
print(f"attention_mask: {inputs['attention_mask']}") # 输出 attention_mask: tensor([[1, 1, 1, 1, 1, 1, 1, 1]])
print(f"token_type_ids: {inputs['token_type_ids']}") # 输出 token_type_ids: tensor([[0, 0, 0, 0, 0, 0, 0, 0]])
# 处理句子对
text_pair = ("今天天气如何?", "今天下雨了。")
inputs_pair = tokenizer(*text_pair, return_tensors="pt")
print("\n句子对输入:")
print(f"input_ids: {inputs_pair['input_ids']}") # 输出 input_ids: tensor([[ 101, 791, 1921, 1921, 3698, 1963, 862, 8043, 102, 791, 1921, 678,
7433, 749, 511, 102]])
print(f"attention_mask: {inputs_pair['attention_mask']}") # 输出 attention_mask: tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
print(f"token_type_ids: {inputs_pair['token_type_ids']}") # 输出 token_type_ids: tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]])
6.3 关键总结
input_ids
:文本的数字身份证,决定模型“看到”什么内容。attention_mask
:告诉模型“哪些部分需要关注”,优化计算效率。token_type_ids
:为模型标注“句子边界”,解决上下文依赖问题。
这些输入张量共同构成了大模型理解文本的基础,类似于人类阅读时需要的“文字+上下文+注意力焦点”。