1. Tokenization入门:什么是,为什么需要Tokenization?

大家好,我是你们这门课的主讲工程师。在进入NLP的世界之前,我们必须先搞定一个最基础、也最容易被忽视的概念—— 和 Tokenization。

说白了,你把一段话交给计算机处理,计算机不认识「字」和「词」,它只认识数字。Tokenization就是那台「翻译机」,它把人类语言切成小块(),再把这些小块映射成计算机能算的编号。

我记得我第一次接触这个概念是在做一个中文情感分析项目。当时直接把一句话当作一个整体丢进模型,结果模型直接崩溃——因为它根本不知道「我喜欢」和「我不喜欢」之间的微妙差别。嗯,从那以后我才意识到:没有好的Tokenization,所有上层模型都是空中楼阁。

1.1 到底什么是?

一个,是你文本中一个最小的、有意义的单元。在英文里它可能是一个单词、一个标点,或是像"es"这样的词缀。在中文里,它可以是一个字、一个词,甚至是一个短语——取决于你怎么切。

核心观点:不是固定的物理单位,而是你选择的一种语义原子。

举个例子:

  • 句子:I love NLP
  • 按空格切:["I", "love", "NLP"] → 3个
  • 按子词切(比如BPE):["I", "lov", "e", "N", "L", "P"] → 6个

你会问:为什么不能直接按单词切?我在项目里踩过这个坑——英语单词动不动就几十万个,而很多单词其实很少出现。你如果给每个单词一个编号,词汇表会膨胀到百万级,模型根本训不动。

1.2 为什么我们需要Tokenization?

这个问题我曾经被实习生问过:「为什么不直接把汉字编码成Unicode丢进去?」答案是:模型需要捕捉「语义结构」,而不是字符的字节码。

你想想看:吃 和 吃饭 在Unicode里没有任何相似度,但语义上吃饭比吃更完整。Tokenizer帮我们做了一件事:把文本变成一串整数,同时保留尽可能多的语义信息。

必要性 说明
计算机只认数字 神经网络吃的是浮点数矩阵,不是文字
效率 一个=一个维度索引,计算远快于字级别操作
语义单元 词/子词比单字携带更强的句法语义信号
OOV处理 好的Tokenizer能处理未登录词(比如这种新词)

避坑指南:我曾经在项目里直接拿Python的.split()作为Tokenizer,结果模型遇到"don't"时把它切成["don", "'", "t"],导致"t"这种无意义反复出现,训练损失一直降不下去。后来才改用BPE解决。

1.3 Tokenization的核心流程

整个过程分为三步:切分 → 映射 → 编码。下面这张图帮你一秒理清脉络。

Tokenization 核心流程 原始文本 "I love NLP" 切分过程 按规则拆成子单元 列表 ["I","love","NLP"] 词汇表映射 I→105, love→230, NLP→3019 ID序列 [105, 230, 3019] 关键说明 • 切分策略决定的粒度:词级 vs 子词级 vs 字符级 • 词汇表大小一般在3万~5万,太大影响训练速度,太小OOV严重 • 最终输出是一个整数数组,模型只能看到这些数字

你看到没?切分这一步是整个流程里最需要动脑子、也最容易出错的环节。

1.4 主流切分策略一览

我根据经验给大家整理了一份对比,你在选Tokenizer时可以照着这张表来。

策略 典型代表 优点 缺点
字符级 Unicode字节编码 词汇表特小(几百个),无OOV 序列太长,丢失词义信息
词级 空格+标点拆分 语义准确,序列短 词汇表超大,OOV处理差
子词级 BPE、WordPiece、Unigram 平衡词汇表大小与语义保留 需要预先训练合并规则
多语言共享 SentencePiece 零门槛处理中文、日文等 对罕见语种可能效率不高

实战建议:我个人习惯先用预训练模型自带的Tokenizer(比如BERT用WordPiece,用BPE)。如果做垂直领域,再基于语料增量训练一个子词词表。千万别手写规则切分——我在医疗病历项目里试过,结果"心电监护"被切成了"心电/监/护",模型彻底蒙圈。

1.5 为什么Tokenizer的好坏直接决定模型效果?

你想想看:就是模型的基本输入单元。如果这个单元切得不对,模型再怎么调参都白搭。

举个例子。中文句子:「武汉市长江大桥」

  • 差的分法:武汉/市长/江大桥(意思全变了,变成「武汉的市长叫江大桥」)
  • 好的分法:武汉市/长江大桥

我踩过这个坑——曾经在一个舆情分析项目中,模型把「武汉市长江大桥」理解成「武汉市长」,结果所有和「武汉市长」有关的负面舆情全被误分到桥梁安全类别。嗯,那之后我再也不敢用简单最大匹配分词了。

重要提醒:Tokenization不是一次性的预处理步骤。很多现代模型把它和网络前向计算融合在一起(比如T5的Span Corruption里的随机掩码)。你要把Tokenizer当成模型的一个组件,而不是一个独立工具。

1.6 一个简单的代码侧写(伪代码版)

为了让你直观理解,我写一段极简的Python风格伪代码(仅示意流程,不涉及具体库)。

def tokenize(text, vocab):
    # 1. 切分(这里简单按空格分)
    tokens = text.split()              # ["I", "love", "NLP"]
    
    # 2. 查词汇表,若不在则替换为 [UNK]
    ids = []
    for  in tokens:
        if  in vocab:
            ids.append(vocab[])
        else:
            ids.append(vocab["[UNK]"])
    
    # 3. 加上特殊标记(CLS, SEP等)
    ids = [vocab["[CLS]"]] + ids + [vocab["[SEP]"]]
    
    return ids

# 示例
text = "I love NLP"
vocab = {"[CLS]":101, "[SEP]":102, "[UNK]":100, 
         "I":105, "love":230, "NLP":3019}
print(tokenize(text, vocab))
# 输出:[101, 105, 230, 3019, 102]

在实际项目里,你用的Tokenizer会复杂得多——比如BPE要维护合并规则、SentencePiece还有语言模型评分。但核心思想不变:先切,再映射,最后组装成特定格式的ID序列。

1.7 本章小结

Tokenization不是「切词」那么简单。它是一个从文本到整数序列的语义化编码过程。

  • 是模型能处理的最小语义单元
  • 好的Tokenizer能大幅减少OOV、控制序列长度、保留词义
  • 主流的子词级方案(BPE、WordPiece)是目前工业界标配
  • 务必把Tokenizer当成模型的一部分来维护和调优

一句话记住:
 没有合理的Tokenization,就没有可靠的NLP模型。

好了,这一章就到这里。你脑海中现在应该有一个清晰的图景:文本→切分→列表→ID序列。下一章我们专门拆解BPE(Byte Pair Encoding)——也是目前、BERT等主流模型都在用的子词切分算法。到时我会带上我在实际训练中遇到的坑和调参经验,咱们好好聊一聊。