Home
avatar

Qiu

分词器是什么

AI 总结

本文系统梳理分词器的起源与发展历程,用通俗语言详解传统分词算法与子词分词的核心原理,对比中英文分词的本质差异,介绍主流分词工具与应用场景,并探讨分词技术的未来趋势,适合零基础读者从零理解NLP的核心基础组件。

第一章:认识分词器——什么是分词,为什么重要

1.1 什么是分词器:用大白话解释

想象一下,你收到一封用外文写的信,但所有字母都连在一起,没有空格,也没有标点。比如英文写成”hellohowareyoutoday”,你能一眼看懂吗?可能要花点时间琢磨从哪里断字。

这就是计算机面对人类语言时的困境。

分词器(Tokenizer),简单来说就是帮计算机”断字”的工具。它把一长串连续的文本,切割成一个个有意义的最小语言单位——我们称之为”词”或者”Token”。

举个最直观的例子:

  • 输入:我爱自然语言处理
  • 分词输出:我 / 爱 / 自然 / 语言 / 处理

对于英文来说,这件事看起来简单一些,因为单词之间本来就有空格。比如”I love natural language processing”,按空格切一下就差不多了。但事情没那么简单——英文里有缩写”don’t”、连字符”state-of-the-art”、所有格”Tom’s”,这些都不能简单地按空格切。

而对于中文、日文、韩文等语言,问题就复杂多了。中文的字与字之间没有任何分隔符,一句话就是一串连续的汉字。人可以凭借语感和知识轻松断词,但计算机不行。它不知道”南京市长江大桥”应该切成”南京市 / 长江大桥”,还是”南京 / 市长 / 江大桥”。

分词器就是解决这个问题的核心技术。它是自然语言处理(NLP)的第一道大门,所有后续的语义理解、情感分析、机器翻译、搜索引擎,都建立在分词的基础之上。

1.2 为什么需要分词:计算机不懂”词”

很多人会问:计算机这么聪明,连围棋冠军都能赢,为什么连分词这么”简单”的事都做不好?

答案是:计算机根本不知道什么是”词”。

在计算机眼里,文本只是一串二进制编码。它看到”我爱你”,只知道这是三个Unicode字符,编号分别是U+6211、U+7231、U+4F60。至于这三个字合起来是什么意思,哪些字该放在一起组成词,计算机天生是不懂的。

那为什么不直接用”字”来处理呢?比如每个汉字作为一个单位?

原因有三个:

第一,语义粒度不对。 单个汉字承载的语义太少了。“自”是什么意思?“然”是什么意思?单独拿出来都很模糊。但合在一起的”自然”,意思就明确了。如果用字级别的单位来做语义理解,模型需要从单个字拼凑出词义,难度会大很多。

第二,序列太长。 一句话”我昨天去超市买了苹果和香蕉”,按字算有14个单位,按词算只有8个单位。越长的序列,模型处理起来越慢、越难。Transformer的自注意力机制计算量和序列长度的平方成正比,序列翻倍,计算量翻四倍。

第三,信息密度低。 常用汉字只有几千个,但常用词有几十万。词能承载更丰富、更精确的语义信息。

所以,人类语言处理的第一步,就是把连续的文本切成有意义的词语单元。这就是分词器存在的根本原因。

1.3 Token是什么:语言的最小单位

你可能经常听到”Token”这个词。它和”词”有什么区别?

词(Word) 是语言学概念,是能够独立运用的最小语言单位。 Token 是工程概念,是分词器切分后得到的最小处理单元。

两者有时候一致,有时候不一致。比如:

  • 在传统中文分词里,Token基本等于词
  • 在子词分词里,一个词可能被切成多个Token
  • 标点符号、数字也会成为独立的Token

打个比方:如果把语言比作乐高积木,那么”词”就是完整的乐高小人、乐高房子,而”Token”就是每一块积木零件。一个完整的组件可能由好几块积木拼成。

在大模型时代,Token的概念变得尤为重要。因为大模型处理的不是文字,而是Token ID——每个Token对应一个数字。模型的输入是一串数字,输出也是一串数字。

比如GPT-3.5的词汇表里有大约10万个Token,每个Token都有一个唯一的编号。当你输入”你好”时,分词器先把它转成Token,再映射成数字ID,模型才能处理。

1.4 分词器在NLP流水线中的位置

分词是几乎所有NLP任务的第一步。我们常说”垃圾进,垃圾出”(Garbage in, garbage out),分词如果错了,后面的所有步骤都会受影响。

一个典型的中文NLP流水线是这样的:

  1. 原始文本输入 → “工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作”
  2. 分词 → [“工信处”, “女干事”, “每月”, “经过”, “下属”, “科室”, “都”, “要”, “亲口”, “交代”, “24”, “口”, “交换机”, “等”, “技术性”, “器件”, “的”, “安装”, “工作”]
  3. 词性标注 → 标出每个词是名词、动词还是形容词
  4. 命名实体识别 → 找出人名、地名、机构名
  5. 句法分析 → 分析句子的语法结构
  6. 语义理解 → 最终理解句子的含义

可以看到,分词处在整个流水线的最上游。如果”女干事”被错误切成”女 / 干事”,或者”交换机”被切成”交换 / 机”,后面的词性标注、实体识别都会跟着错。

这就是为什么分词技术研究了几十年——它是地基,地基不稳,上面的楼再高也会塌。

1.5 通俗类比:分词器就像语言的”翻译官”

为了让你更好地理解,我们来做几个通俗类比:

类比一:分词器像语言的”断句师” 古人写文章是没有标点的,比如《论语》就是一整段文字。后人读书首先要学”句读”(jù dòu),也就是给文章断句加标点。同样一句话,断句不同,意思可能完全相反。分词器做的事情,本质上就是给计算机”断句”——只不过断的是词,不是句子。

类比二:分词器像食材处理师 厨师做菜之前,需要把一整块肉切成片、切成丝、切成丁。不同的菜需要不同大小的肉块。分词器就是NLP厨房的”食材处理师”,把整段文本切成合适大小的”语言肉块”,供后面的”厨师”(各种NLP模型)使用。切太大炒不熟,切太小容易碎。

类比三:分词器像电报编码员 以前发电报,要把文字转换成摩尔斯电码。分词器做的事情类似——把自然语言转换成模型能懂的”数字编码”。只不过摩尔斯电码是字母级的,而分词器是词级或子词级的。

理解了这些基本概念,接下来我们就穿越时空,看看分词器是怎样一步步发展到今天的。

第二章:分词器的起源与发展历史

分词器的发展史,本质上是一部在”词汇表大小”与”序列长度”之间寻找最优平衡点的进化史。它经历了从人工规则到统计模型,再到深度学习和子词智能的四次重大跃迁。

2.1 史前时代:人工分词与规则萌芽(1950s-1980s)

分词研究的源头,可以追溯到20世纪50年代的机器翻译探索。

1954年,美国乔治敦大学和IBM公司合作,进行了世界上第一次机器翻译实验——把俄语翻译成英语。这次实验虽然只处理了250个单词、6条语法规则,但开启了人类用计算机处理自然语言的序幕。

那时候的中文信息处理还处在萌芽期。中文打字机都还没普及,更别说中文分词了。

真正的中文分词研究起步于20世纪80年代。随着计算机进入中国,中文信息处理成为迫切需求。当时遇到的第一个拦路虎就是:计算机怎么理解中文句子?

最早的思路非常朴素——查字典。

研究者们把一本词典装进计算机,然后让程序扫描句子,遇到词典里有的词就切出来。这就是最早的”基于词典匹配”的分词方法,也叫机械分词法。

1983年,北京航空学院的研究者提出了”正向最大匹配法”,这是第一个系统化的中文分词算法。同期还有逆向最大匹配法、双向匹配法等变种。

这个时代的分词器,本质上就是个”高级查字典程序”。它没有任何智能,完全依赖人工编写的词典和规则。好处是简单、速度快;坏处是遇到词典里没有的词就傻眼,遇到歧义句子就乱切。

2.2 第一阶段:规则驱动时代(1980s-1990s)

整个80年代到90年代中期,分词研究都处在规则驱动时代。

核心思想

分词=查字典+人工规则。所有的分词逻辑都是人写死的。比如”最长匹配优先”、“单字词越少越好”等等。

代表算法

  • 正向最大匹配法(FMM):从左往右,每次尽量匹配最长的词
  • 逆向最大匹配法(BMM):从右往左,每次尽量匹配最长的词
  • 双向最大匹配法:正反各切一遍,选结果更好的那个
  • 最少切分法:哪种切法分出的词最少,就选哪种

典型系统

  • 1987年,中科院计算所的”书面汉语自动分词系统”(CDWS)
  • 90年代初,清华大学的SEG系统
  • 哈尔滨工业大学的分词系统

历史局限

这个时代的分词系统,准确率大概在90%-95%之间。听起来不低,但实际用起来问题很多:

  1. 未登录词完全无法处理:词典里没有的词,比如人名、新出现的词,一律切错
  2. 歧义消解能力弱:“南京市长江大桥”这种经典歧义句,基本靠碰运气
  3. 词典维护成本高:新词不断出现,人工更新词典永远追不上

但不管怎样,规则时代奠定了中文分词的基础。最大匹配法的思想直到今天还在被使用——很多轻量级分词工具的核心依然是它。

2.3 第二阶段:统计驱动时代(1990s-2015)

2.3.1 统计方法的兴起

90年代,统计学方法开始席卷整个NLP领域。人们发现,与其人工写无穷无尽的规则,不如让计算机从大量语料中自动学习规律。

1990年代初,隐马尔可夫模型(HMM)被引入中文分词。研究者把分词问题转化为”序列标注”问题——给每个汉字打标签,标记它是词首(B)、词中(M)、词尾(E)还是单字词(S)。

比如”我爱北京天安门”标注成:

  • 我/S 爱/S 北/B 京/E 天/B 安/M 门/E

这样一来,分词就变成了分类问题:给每个字分到B/M/E/S中的一类。

1999年,John Lafferty等人提出了条件随机场(CRF)模型,这是统计分词时代的巅峰之作。CRF比HMM更强大,能融合更多特征,准确率更高。很快,CRF就成为了工业界分词系统的标配。

2.3.2 里程碑:SIGHAN Bakeoff

2003年,在日本札幌举行的第一届ACL-SIGHAN国际中文分词竞赛,是中文分词发展史上的里程碑事件。

这次比赛首次统一了训练语料和测试标准,来自世界各地的研究团队同台竞技。比赛结果显示,基于统计机器学习的方法全面超越了纯规则方法。

这次竞赛之后,中文分词研究进入了快车道:

  • 2005年前后,CRF成为主流
  • 2007年,感知机和结构化学习方法兴起
  • 2010年代初,联合模型与半监督学习出现

统计时代的分词准确率提升到了95%-97%。更重要的是,模型对未登录词有了一定的识别能力——它能根据字的搭配规律,猜测哪些字可能组成一个新词。

2.3.3 统计时代的工具爆发

这个时期涌现了一大批优秀的开源分词工具:

  • ICTCLAS:中科院张华平博士开发,曾经的中文分词标杆
  • Jieba:2012年诞生,Python生态最流行的中文分词库
  • HanLP:何晗开发,功能最全面的Java/Python双语NLP工具
  • THULAC:清华大学出品,准确率与速度兼顾
  • IK Analyzer:搜索领域广泛使用的Java分词器

这些工具大多采用”词典+统计”的混合方案——先用词典匹配处理常见词,再用HMM或CRF处理未登录词,兼顾了速度和准确率。

2.4 第三阶段:子词智能时代(2015至今)

2.4.1 BPE:从数据压缩到NLP的传奇

子词分词的故事要从1994年说起。

美国软件工程师菲利普·盖奇(Philip Gage)在《C Users Journal》上发表了一篇文章《A New Algorithm for Data Compression》,提出了BPE(Byte Pair Encoding)算法。这本来是一个数据压缩算法——找出文本中出现最频繁的相邻字节对,用一个新字节替换它,反复操作就能压缩文件。

谁也没想到,20年后这个算法会彻底改变NLP领域。

2015年,英国爱丁堡大学的菲利普·森里奇(Philipp Sennrich)在论文《Neural Machine Translation of Rare Words with Subword Units》中,把BPE改造成了子词分词算法。

当时神经机器翻译(NMT)刚兴起,但遇到了一个头疼的问题——未登录词(OOV)。训练语料里没见过的词,模型完全不知道怎么翻译。比如人名、地名、专业术语,这些词不可能全部出现在训练数据里。

BPE的思路非常巧妙:不把整个词当单位,而是把词拆成更小的”子词”单元。比如”unhappiness”可以拆成”un + happy + ness”。这样即使模型没见过”unhappiness”这个完整词,也能通过子词组合猜出它的意思和翻译。

而且,子词共享还能大大压缩词汇表。英文有几十万个单词,但词根词缀就那么多。用子词的话,一个几万大小的词表就能表示几乎所有英文单词。

2.4.2 子词算法百花齐放

BPE成功之后,各种子词算法如雨后春笋般涌现:

  • 2016年:Google推出WordPiece,用于BERT等模型
  • 2018年:Google推出SentencePiece,支持多语言无空格分词
  • 2018年:Unigram算法被广泛应用于T5、ALBERT等模型

子词分词解决了困扰NLP多年的OOV问题,让模型能够处理任意文本。这为后来大语言模型的爆发铺平了道路——想象一下,如果GPT只能处理词汇表里有的那几万个词,它根本不可能和你自由对话。

2.4.3 大模型时代的分词器

2018年BERT问世,2020年GPT-3发布,大模型时代正式到来。

分词器的地位也发生了微妙的变化:

  • 以前,分词是一个独立的研究任务,有专门的评测和竞赛
  • 现在,分词成了大模型的一个内置组件,服务于预训练目标

词表大小也在不断膨胀:

  • BERT-base:约3万个Token
  • GPT-2:约5万个Token
  • GPT-3.5:约10万个Token
  • GPT-4:超过10万个Token
  • 国产大模型:普遍在10万-15万之间

词表越大,每个Token承载的语义越多,相同文本需要的Token数越少,模型推理越快。但词表也不是越大越好——词表越大,Embedding层参数越多,模型越难训练。

2.5 第四阶段:去分词化探索(2020至今)

有趣的是,当子词分词发展到顶峰时,学术界开始反思:我们真的需要分词器吗?

这个问题听起来有点叛逆,但理由很充分:

  1. 分词器是人为设计的预处理步骤,它的切分规则不一定最优
  2. 分词错误会向下游传播,造成误差累积
  3. 不同语言需要不同的分词器,跨语言处理麻烦
  4. 特殊字符、代码、混合文本处理困难

于是,一批”无分词器”的研究出现了:

  • ByT5(2021):Google提出,直接在UTF-8字节上建模,完全不需要分词。虽然效果略逊,但鲁棒性更强,对噪声不敏感。
  • MEGABYTE(2023):Meta提出,通过层次化设计解决字节序列过长的问题,用两级模型处理字节。
  • H-Net(2025):卡内基梅隆大学提出,动态分块机制,自适应地将字节流分割成语义单元。
  • ByteFlow(2025):亚马逊提出,基于压缩率的自适应分块策略。

这些研究还处在实验室阶段,目前子词分词在效率和性能上仍是最佳平衡点。但它们代表了一个重要的方向——也许未来的模型,会自己学会该怎么”断词”,不再需要人类预先设计分词器。

2.6 发展脉络总结

让我们用一张时间线来回顾分词器的进化之路:

时代时间核心方法代表技术准确率核心问题
规则时代1980s-1990s词典匹配+人工规则最大匹配法~90%未登录词、歧义
统计时代1990s-2015统计机器学习HMM、CRF95%-97%特征工程、数据依赖
子词时代2015-至今子词合并/剪枝BPE、WordPiece接近100%词表大小权衡
去分词时代2020-至今字节/字符级建模ByT5、MEGABYTE探索中计算效率

这条演进路线有一个清晰的趋势:人工干预越来越少,模型自动学习的成分越来越多。从人写规则,到人设计特征让统计模型学习,再到模型自动学习子词单元,最后可能连分词这个步骤都不需要了。

这正是整个AI领域的发展缩影——人类做的事情越来越抽象,机器自己学习的东西越来越多。

第三章:传统分词算法原理详解(小白通俗版)

了解了历史,我们来深入看看各种分词算法的原理。这一章我们聚焦传统分词方法——也就是大模型出现之前,中文分词领域的主流技术。

别担心,我会用最通俗的语言和例子来讲,保证小白也能看懂。

3.1 基于词典的分词方法

基于词典的分词,也叫”机械分词”,是最直观、最容易理解的一类方法。

核心思想就一句话:拿句子去查词典,能匹配上的就是词

但具体怎么匹配?从左往右还是从右往左?匹配长的还是短的?不同的策略衍生出了不同的算法。

3.1.1 正向最大匹配法(FMM)

核心思想:从句子左边开始,每次尽量取最长的、能在词典中找到的词。

通俗类比:就像你吃面条,从一头开始,每次夹起尽可能长的一筷子面。

算法步骤

  1. 确定词典中最长的词有几个字(比如4个字)
  2. 从句子开头取4个字,去词典里查
  3. 如果查到了,就把这4个字作为一个词切出来,指针后移4位
  4. 如果没查到,就减少一个字(取3个字),再查
  5. 重复直到匹配成功,然后继续处理剩下的句子
  6. 直到整个句子处理完

手把手例子

句子:“研究生命起源” 词典:{“研究”, “研究生”, “生命”, “起源”} 最长词长度:3

第一步:从左取3个字 → “研究生” 查词典 → 有! 切分出:“研究生” 剩余句子:“命起源”

第二步:从左取3个字 → “命起源” 查词典 → 没有 减到2个字 → “命起” 查词典 → 没有 减到1个字 → “命” 查词典 → 有(单字) 切分出:“命” 剩余句子:“起源”

第三步:取2个字 → “起源” 查词典 → 有! 切分出:“起源”

最终结果:研究生 / 命 / 起源

你看,问题来了——“研究生命起源”正确的切分应该是”研究 / 生命 / 起源”,但正向最大匹配切出了”研究生 / 命 / 起源”,这就错了。

这就是正向最大匹配的典型问题:因为从左往右贪心匹配,容易把前面的词切得太长,导致后面的词切错。

3.1.2 逆向最大匹配法(BMM)

既然从左往右容易错,那反过来从右往左呢?

核心思想:从句子右边开始,每次尽量取最长的、能在词典中找到的词。

还是刚才的例子:

句子:“研究生命起源” 词典:{“研究”, “研究生”, “生命”, “起源”} 最长词长度:3

第一步:从右取3个字 → “命起源” 查词典 → 没有 减到2个字 → “起源” 查词典 → 有! 切分出:“起源” 剩余句子:“研究生命”

第二步:从右取3个字 → “究生命” 查词典 → 没有 减到2个字 → “生命” 查词典 → 有! 切分出:“生命” 剩余句子:“研究”

第三步:取2个字 → “研究” 查词典 → 有! 切分出:“研究”

最终结果:研究 / 生命 / 起源

这次就对了!

统计表明,中文中逆向最大匹配的准确率确实比正向高一点点。因为中文的修饰语多在前面,中心词多在后面,从后往前匹配更容易抓住核心词。

但逆向匹配也不是万能的,它也有自己的易错场景。

3.1.3 双向最大匹配法

既然正向和逆向各有优劣,那不如两个都跑一遍,选结果更好的那个?

这就是双向最大匹配法。

选优规则通常是:

  1. 如果两种方法切出来的词数量不一样,选词数少的那个(词越少说明平均每个词越长,越可能正确)
  2. 如果词数一样,选单字词更少的那个

还是用刚才的例子:

  • 正向结果:[“研究生”, “命”, “起源”] → 3个词,1个单字词
  • 逆向结果:[“研究”, “生命”, “起源”] → 3个词,0个单字词

词数相同,逆向的单字词更少,所以选逆向结果。

双向匹配比单一方向的准确率高,但代价是要跑两遍分词,速度慢了一些。

3.1.4 词典匹配法的本质问题

词典匹配法简单、快速,但有两个绕不开的硬伤:

第一个硬伤:歧义

“南京市长江大桥”到底怎么切?

  • 南京市 / 长江大桥
  • 南京 / 市长 / 江大桥

两种切法词典里都有,匹配法根本不知道哪个对。它只知道”这些词都在词典里”,但不知道哪种组合符合语义。

第二个硬伤:未登录词

词典里没有的词,比如人名”张三”、新词”元宇宙”,匹配法肯定切不对。它会把”张三”切成”张 / 三”,把”元宇宙”切成”元 / 宇宙”。

这两个问题,光靠查字典是解决不了的。必须引入统计和概率,让模型学会”哪种切法更合理”。

3.2 基于统计的分词方法

统计分词的核心思想是:哪种分词结果出现的概率大,哪种就是对的

换句话说,不是看词典里有没有,而是看”人们会不会这么说话”。

3.2.1 N-gram语言模型:用概率说话

先介绍一个基础概念:语言模型。

语言模型就是计算一句话”像不像人话”的模型。一句话越符合语言习惯,概率就越高。

比如:

  • P(“我喜欢吃苹果”) → 概率很高,很像人话
  • P(“苹果吃喜欢我”) → 概率很低,不像人话

怎么计算一句话的概率呢?用链式法则: P(“我喜欢吃苹果”) = P(“我”) × P(“喜欢”|“我”) × P(“吃”|“我喜欢”) × P(“苹果”|“我喜欢吃”)

但这样计算太复杂了,因为前面的词越长,组合越多,根本统计不过来。

于是人们做了一个简化假设:一个词出现的概率,只和它前面的n个词有关。这就是N-gram模型。

  • 1-gram(一元模型):每个词的概率独立,P(w1,w2,w3) = P(w1)×P(w2)×P(w3)
  • 2-gram(二元模型):每个词只和前一个词有关,P(w3|w1,w2) ≈ P(w3|w2)
  • 3-gram(三元模型):每个词和前两个词有关

有了语言模型,分词就简单了:

  • 找出所有可能的分词方式
  • 用语言模型计算每种方式的概率
  • 选概率最大的那个

还是”研究生命起源”的例子:

  • 切法A:研究 / 生命 / 起源 → 计算P(研究)×P(生命|研究)×P(起源|生命)
  • 切法B:研究生 / 命 / 起源 → 计算P(研究生)×P(命|研究生)×P(起源|命)

显然P(生命|研究)远大于P(命|研究生),所以切法A概率更高,选A。

这就是统计方法的威力——它不是机械地匹配,而是根据语言规律判断哪种切分更合理。

3.2.2 隐马尔可夫模型(HMM)

N-gram模型虽然好用,但计算所有可能的切分方式太慢了。句子越长,可能的切法指数级增长。

有没有更高效的方法?有,那就是把分词转化为序列标注问题。

B/M/E/S四标签体系

  • B(Begin):词的第一个字
  • M(Middle):词中间的字
  • E(End):词的最后一个字
  • S(Single):单独成词的字

比如”我爱北京天安门”标注为: 我/S 爱/S 北/B 京/E 天/B 安/M 门/E

这样,分词问题就变成了:给每个字预测一个标签(B/M/E/S)。

隐马尔可夫模型(HMM)就是干这个的。HMM假设:

  1. 每个字(观测值)是由对应的标签(隐藏状态)生成的
  2. 标签之间有转移概率(比如B后面更可能接M或E,不太可能接B或S)

用HMM分词分两步:

  • 训练:在大量标注好的分词语料上,统计两个概率:
    • 发射概率:某个标签生成某个字的概率
    • 转移概率:某个标签转移到下一个标签的概率
  • 解码:对新句子,用Viterbi算法找出概率最大的标签序列

HMM的好处是计算快,而且能识别未登录词。比如一个人名,虽然词典里没有,但模型根据字的搭配规律,能猜出”这几个字应该组成一个词”。

3.2.3 条件随机场(CRF)

HMM有个局限:它假设每个位置的标签只和前一个位置有关,而且只能利用有限的特征。

条件随机场(CRF)解决了这个问题。CRF可以融合更多特征——比如当前字、前一个字、后一个字、字的偏旁部首、是不是数字等等。

CRF是统计分词时代的准确率天花板。在标准数据集上,纯CRF分词能达到96%-97%的F1值。

但CRF也有缺点:训练慢,特征工程麻烦。特征选得好不好,直接影响最终效果。而设计特征需要资深的NLP工程师。

这也是为什么后来深度学习火了——因为深度学习能自动学习特征,不用人手工设计。

3.2.4 序列标注法的通俗理解

可能B/M/E/S、HMM、CRF这些概念听起来有点抽象,我打个比方:

想象你在听一首外语歌,歌词是连在一起唱的,你不知道词和词之间从哪断开。但你听多了之后,会摸索出一些规律:

  • 某些音经常出现在词开头
  • 某些音经常出现在词结尾
  • 某些音经常连在一起出现

听得多了,即使遇到一首新歌,你也能大概猜出歌词该怎么断词。

统计模型做的就是这件事。它在大量标注语料上”听”了无数句子,学会了汉字在词中的位置规律,然后就能给新句子”断词”了。

3.3 基于深度学习的分词方法

2015年之后,深度学习开始统治NLP领域,分词也不例外。

3.3.1 BiLSTM-CRF架构

最经典的深度学习分词模型是BiLSTM-CRF。

这个架构分两层:

  1. BiLSTM层:双向LSTM网络,自动学习每个字的向量表示,捕捉上下文信息。它能看到每个字左边和右边的内容,理解”这个字在当前语境下应该是什么标签”。
  2. CRF层:在LSTM输出的基础上,学习标签之间的转移约束,保证输出的标签序列是合法的(比如B后面不能接S)。

BiLSTM-CRF的好处是:

  • 不需要人工设计特征,模型自动学习
  • 能捕捉更长距离的上下文依赖
  • 准确率比纯CRF更高

在标准数据集上,BiLSTM-CRF分词能达到97%-98%的F1值。

3.3.2 预训练模型时代

2018年BERT出来之后,分词又上了一个台阶。

用BERT做分词非常简单:在BERT输出的基础上加一个分类层,预测每个字的B/M/E/S标签就行了。

因为BERT已经在海量文本上预训练过,学到了丰富的语义知识,所以它做分词效果特别好。尤其是歧义词和未登录词,BERT凭借强大的语义理解能力,处理得比之前的模型都好。

但BERT也有缺点:速度慢、计算量大。所以工业界很多时候还是用CRF或更轻量的模型——毕竟分词只是第一步,后面还有更重的任务。

3.3.3 深度学习为什么更准

为什么深度学习分词比传统方法准?我再打个比方:

传统统计方法(CRF)就像一个经验丰富的老工人,他根据自己总结的几百条经验规则来判断怎么分词。经验很管用,但遇到超出经验的新情况就容易出错。

深度学习模型(BERT)就像一个读过亿万本书的学霸,它不仅记住了语言规律,还理解了语义。当遇到歧义句时,它能根据上下文的意思来判断该怎么切,而不是只看表面规律。

比如”结婚的和尚未结婚的”这个经典歧义:

  • 错误切法:结婚 / 的 / 和尚 / 未 / 结婚 / 的
  • 正确切法:结婚 / 的 / 和 / 尚未 / 结婚 / 的

传统模型可能会纠结,因为”和尚”确实是个常见词。但BERT能理解整句话的意思——这句话在说结婚的人和没结婚的人,所以”和尚”在这里说不通,应该是”和 / 尚未”。

这就是语义理解带来的质变。

3.4 传统分词方法总结

让我们对比一下三类传统分词方法:

方法类别核心思想代表算法准确率未登录词处理速度实现难度
基于词典查字典匹配最大匹配法~90%极快简单
基于统计概率最大化HMM、CRF95%-97%一般中等
深度学习自动学习特征BiLSTM-CRF、BERT97%-99%复杂

工业界的实际情况是:大多数系统采用”词典+统计”的混合方案。比如Jieba分词就是”前缀词典+最大概率路径+HMM未登录词识别”的组合——用词典保证速度和常见词的准确,用HMM处理未登录词。

这种混合方案在准确率和速度之间取得了很好的平衡,也是过去十几年中文分词的主流做法。

第四章:子词分词算法深度解析

如果说传统分词是中文NLP的特色话题,那么子词分词就是整个NLP领域的通用基础设施。

大模型时代,你听到的几乎所有模型——GPT、BERT、LLaMA、Qwen——都在使用子词分词。理解了子词分词,你就理解了大模型如何”读懂”文字。

4.1 为什么需要子词:OOV问题的痛点

在子词分词出现之前,NLP模型大多使用”词级分词”——也就是一个词对应一个Token。

比如词汇表里有”happy”、“sad”这些词,模型就能处理。但如果遇到”unhappiness”这个词,而词汇表里没有,那就麻烦了——这就是未登录词(Out-of-Vocabulary,简称OOV)

OOV问题在以下场景特别严重:

  1. 形态丰富的语言:英语、俄语、德语等,一个词根能变出几十种形式。比如”run”有runs、ran、running、runner等多种变形。如果每个变形都单独进词表,词表会爆炸。

  2. 专有名词:人名、地名、品牌名无穷无尽。比如”马斯克”、“特斯拉”,不可能全部收录。

  3. 新词:网络时代新词层出不穷。“内卷”、“躺平”、“元宇宙”,词典永远追不上。

  4. 低资源语言:小语种没有足够的语料来构建完整词表。

OOV问题的后果很严重:模型遇到不认识的词,只能用一个特殊标记[UNK]代替,等于这个词的信息完全丢失了。如果关键信息恰好是个未登录词,模型就完全理解错了。

怎么解决呢?研究者想到了一个朴素的思路:既然整词不认识,那就拆成小块认识

就像你不认识”unhappiness”这个词,但你认识”un-”(否定前缀)、“happy”(开心)、“-ness”(名词后缀),拼起来你也能大概猜出意思。

子词分词就是基于这个思想:把词拆成更小的单元(子词),用有限的子词组合出无限的词汇。

4.2 BPE(字节对编码):最经典的子词算法

BPE是目前应用最广泛的子词算法。GPT系列、LLaMA系列都在用它。

4.2.1 起源:从数据压缩到NLP

前面提到过,BPE最早是1994年提出的数据压缩算法。它的思路非常简单:

  1. 找出文本中出现最频繁的相邻字节对
  2. 用一个新字节替换这对字节
  3. 重复,直到达到压缩目标

比如文本里”ab”出现得最多,就用”z”代替所有”ab”。这样文本就变短了。

2015年,Sennrich等人把这个思想移植到了NLP领域。只不过压缩的目标换成了构建子词词表。

4.2.2 训练过程:手把手推演

BPE的训练过程就是”不断合并最高频相邻对”的过程。我用一个超简单的例子,带你一步步推演。

训练语料(4个单词,括号里是出现次数):

  • low (5次)
  • lower (2次)
  • newest (3次)
  • widest (1次)

目标:训练一个BPE词表

第一步:初始拆分 把每个单词拆成单个字符,末尾加特殊标记表示词结束。为什么要加结束标记?因为要区分”st”出现在词中还是词尾。

  • low → l o w (5次)
  • lower → l o w e r (2次)
  • newest → n e w e s t (3次)
  • widest → w i d e s t (1次)

初始词表就是所有单个字符:{l, o, w, n, e, r, i, d, s, t, }

第二步:统计所有相邻对的频率 我们来数一下每对相邻字符出现了多少次:

  • l-o:5+2 = 7次
  • o-w:5+2 = 7次
  • w-:5次
  • w-e:2+3 = 5次
  • e-r:2次
  • r-:2次
  • n-e:3次
  • e-s:3+1 = 4次
  • s-t:3+1 = 4次
  • t-:3+1 = 4次
  • w-i:1次
  • i-d:1次
  • d-e:1次

出现频率最高的是”l-o”和”o-w”,都是7次。我们随便选一个,比如”l-o”。

第三步:合并最高频对 把”l”和”o”合并成”lo”,更新所有单词:

  • low → lo w (5次)
  • lower → lo w e r (2次)
  • newest → n e w e s t (3次)
  • widest → w i d e s t (1次)

词表新增一个Token:lo

第四步:重复合并 继续统计相邻对频率,找到最高的,合并。 比如下一个最高频的可能是”lo-w”(出现7次),合并成”low”。 然后可能是”e-s”(出现4次),合并成”es”。 然后是”es-t”(出现4次),合并成”est”。 ……

就这样一轮轮合并下去,每轮新增一个Token,直到词表达到目标大小。

最终效果: 词表里既有单个字符(兜底用的,保证任何词都能被拆分),也有常见的字母组合,还有完整的高频词。

4.2.3 编码过程:最长匹配

训练好词表之后,怎么用它来分词呢?

BPE的编码过程很简单——最长匹配。从左往右扫描单词,每次匹配词表里最长的子词。

比如遇到”lowest”这个词:

  1. 看整个”lowest”在不在词表里?可能不在
  2. 看”lowe”在不在?不在
  3. 看”low”在不在?在!
  4. 切出”low”,剩下”est”
  5. 看”est”在不在?在!
  6. 切出”est”

最终结果:low + est

因为词表里有单个字符兜底,所以任何单词都能被切分,永远不会出现OOV。

4.2.4 通俗类比:搭积木

BPE的思想特别像搭积木:

  • 初始状态:你有一堆最小的积木块(单个字符)
  • 训练过程:把经常粘在一起的两个块粘成一个大块
  • 反复操作:最后你有各种大小的积木块
  • 使用时:用这些积木块去拼任何单词

积木块的总数就是词表大小。词表越大,积木块越大,拼一个词需要的块越少;词表越小,积木块越小,拼一个词需要的块越多。

这就是词表大小和序列长度的权衡。

4.2.5 BBPE:字节级BPE

原始BPE是基于字符的。但遇到生僻字、emoji、特殊符号怎么办?Unicode字符太多了,初始词表会很大。

GPT-2做了一个巧妙的改进:不用字符,用字节

UTF-8编码里,一个字节有256种可能。所以初始词表大小固定为256,不管是什么语言、什么符号,最终都能拆成字节。

这就是Byte-level BPE(BBPE),字节级BPE。

比如一个汉字”你”,UTF-8编码是3个字节:E4 BD A0。在BBPE看来,它初始就是3个Token。如果这个字出现频率很高,这3个字节可能被合并成一个Token。

BBPE的好处是:

  • 真正的”开放词汇表”——任何文本都能处理,没有[UNK]
  • 初始词表只有256,非常小
  • 对各种语言、符号、emoji一视同仁

现在主流大模型基本都用BBPE,比如GPT系列、LLaMA系列。

4.3 WordPiece:BERT的选择

WordPiece是Google提出的子词算法,主要用于BERT系列模型。

它和BPE非常像,都是从字符开始逐步合并。核心区别只有一点:合并选择策略不同

  • BPE:合并出现频率最高的相邻对
  • WordPiece:合并能最大化训练数据似然的相邻对

什么叫”最大化似然”?简单说就是:合并之后,整个语料的概率提升最多。

具体计算公式是:选择使 P(tok1,tok2) / (P(tok1) × P(tok2)) 最大的那一对。

这个值叫”互信息”。两个子词经常一起出现,但各自单独出现也很频繁,互信息就低;两个子词总是绑定在一起出现,互信息就高。

比如”的”和”是”经常相邻出现,但它们各自也经常和别的字搭配,互信息不高,不应该合并。 而”蜘”和”蛛”几乎总是一起出现,互信息很高,应该合并。

WordPiece还有一个特色标记:##前缀

  • 出现在词开头的子词,正常写
  • 出现在词中间的子词,前面加##

比如”unhappiness”可能被切成:un + ##happy + ##ness

这个##标记的作用是告诉模型:这个子词是接在前面的子词后面的,它们共同组成一个完整的词。

BPE vs WordPiece 通俗对比

  • BPE就像”谁出现次数最多就合并谁”,简单粗暴
  • WordPiece就像”谁最应该在一起就合并谁”,更讲究语义

实际使用中两者效果差别不大。BPE因为实现简单,用得更广泛一些。

4.4 Unigram:反向剪枝的概率模型

BPE和WordPiece都是”自底向上”的——从最小单元开始,一点点合并变大。

Unigram正好相反,它是”自顶向下”的——从一个很大的词表开始,一点点剪掉不重要的子词,直到达到目标大小。

4.4.1 训练过程:从大到小剪枝

Unigram的训练步骤:

  1. 初始化大词表:先生成一个很大的候选词表。怎么生成?可以把所有高频的字符串都放进去,也可以先用BPE生成一个较大的词表。

  2. 训练Unigram语言模型:假设每个子词独立出现,计算每个子词的出现概率。

  3. 评估每个子词的重要性:对于词表里的每个子词,计算”如果把它删掉,整个语料的困惑度会上升多少”。困惑度上升越少,说明这个子词越不重要。

  4. 剪枝:删掉最不重要的一批子词(比如删掉10%)。

  5. 重复:回到第2步,重新训练语言模型,再剪枝。直到词表缩小到目标大小。

  6. 保留基础字符:最后一定要保留所有单个字符,保证任何词都能被拆分。

4.4.2 编码过程:Viterbi求最优

分词的时候,Unigram不是用最长匹配,而是用Viterbi算法找出概率最大的切分路径

比如”unbelievable”有很多种切法:

  • u + n + b + e + l + i + e + v + a + b + l + e
  • un + believable
  • un + believ + able
  • un + be + lie + v + able

每种切法都有一个概率(所有子词概率的乘积),Unigram选概率最大的那种。

4.4.3 Unigram的独特优势

和BPE、WordPiece相比,Unigram有几个独特的优点:

  1. 全局最优倾向:因为是从大词表往下剪,避免了BPE早期错误合并导致的累积误差。BPE是贪心的,前面合错了后面没法改;Unigram有全局视角。

  2. 支持概率采样:Unigram可以输出多种可能的切分,每种切分有对应的概率。训练时可以随机采样不同的切分方式,起到数据增强的作用,提升模型鲁棒性。

  3. 天然支持无空格语言:Unigram不需要预分词,可以直接在原始字符流上工作。

SentencePiece默认就是用Unigram算法。T5、ALBERT、XLNet等模型也用的是Unigram。

4.4.4 通俗类比:修剪树木

如果说BPE是搭积木,那Unigram就是修剪树木:

  • 一开始有一棵枝繁叶茂的大树(大词表)
  • 你一根根剪掉最没用的树枝
  • 最后剩下的都是最有价值的枝干
  • 用这些枝干,也能组合出所有可能的形状

4.5 SentencePiece:语言无关的统一方案

前面讲的BPE和WordPiece,都有一个隐含前提——先按空格把文本切成单词,然后在单词内部做子词拆分。

这个前提对英文没问题,但对中文、日文这种没有空格的语言就麻烦了。你总不能先跑一遍中文分词,再做子词分词吧?那又回到传统分词的问题了。

Google的SentencePiece解决了这个问题。

4.5.1 核心创新:空格也是普通字符

SentencePiece最天才的设计是:它把空格也当作一个普通字符来处理

具体做法是:

  • 把输入文本中的空格替换成一个特殊符号”▁“(U+2581)
  • 然后直接在整个字符序列上训练子词模型,不做任何预分词
  • 解码的时候,再把”▁“换回空格

比如输入”hello world”:

  1. 变成”▁hello ▁world”
  2. 分词结果可能是:[“▁hello”, “▁world”]

对于中文”你好世界”:

  1. 变成”▁你好世界”
  2. 分词结果可能是:[“▁你好”, “世界”]

就这么简单!不需要区分中英文,不需要预先分词,所有语言一视同仁。

4.5.2 算法支持

SentencePiece本身不是一种算法,而是一个工具框架。它内部支持两种子词算法:

  • BPE
  • Unigram(默认)

4.5.3 为什么适合中日韩语言

传统的子词分词需要先做词级分词,而中日韩语言的词级分词本身就是个难题。

SentencePiece绕开了这个问题——它根本不关心什么是”词”,直接在原始字符流上做子词切分。对它来说,中文和英文没有本质区别,都是字符序列。

这也是为什么很多多语言大模型喜欢用SentencePiece——一套分词器处理所有语言。

4.6 四种子词算法对比总结

最后我们来总结对比一下四种子词分词方法:

算法构建方向合并/选择策略编码方式代表模型特点
BPE自底向上频率最高优先最长匹配GPT、LLaMA实现简单、应用最广
BBPE自底向上频率最高优先最长匹配GPT-2/3/4字节级、无OOV
WordPiece自底向上互信息最大优先最长匹配BERT系列更注重语义关联
Unigram自顶向下剪枝损失最小优先Viterbi概率最优T5、ALBERT有概率、可采样
SentencePiece框架级支持BPE/Unigram取决于算法多语言模型语言无关、无预分词

这几种算法没有绝对的优劣,各有适用场景。工业界目前用得最多的是BBPE(GPT系)和SentencePiece-Unigram(多语言系)。

第五章:中英文分词的本质区别

很多人学习分词的时候会困惑:为什么中文分词讲了那么多算法,英文分词好像很简单?大模型时代的分词器,对中英文的处理一样吗?

这一章我们就来聊聊中英文分词的本质区别。

5.1 语言结构差异

中英文分词难度不同,根源在于两种语言的文字系统完全不同。

5.1.1 文字系统对比

维度英文中文
基本单位字母(26个)汉字(常用约3500个)
词汇边界空格天然分隔无显式分隔符
构词方式字母组合成词汉字组合成词
形态变化丰富(时态、单复数、比较级)几乎无
最小表义单位字/词

打个比方:

  • 英文就像乐高积木,每块积木(字母)本身没意义,但拼在一起(单词)才有意义。积木之间有明显的缝隙(空格)。
  • 中文就像七巧板,每块板(汉字)本身就有意义,可以组合出更多图案(词语)。但拼好之后板和板之间没有缝隙,你得自己判断哪几块组成一个图案。

5.1.2 分词难度差异

因为有空格,英文的”词边界”大部分时候是明确的。英文分词的主要工作不是找词边界,而是处理一些特殊情况:

  • 缩写:don’t → do + n’t
  • 连字符:state-of-the-art → state + of + the + art
  • 所有格:Tom’s → Tom + ‘s
  • 数字和单位:100kg → 100 + kg

而中文分词的核心难题恰恰是找词边界。汉字连续排列,哪里是词的开始,哪里是词的结束,全靠算法判断。这是一个本质上更难的问题。

5.2 传统分词时代:两条完全不同的路线

在传统分词时代,中英文走的是完全不同的技术路线。

英文:词形还原是重点

英文因为有空格,分词本身不难。真正的难点是词形变化

比如”run”这个词,有runs、ran、running、runner、runners等多种形式。如果把它们当作不同的词,词表会非常大,而且模型无法共享它们之间的语义。

所以英文NLP有两个独特的步骤:

  1. 词干提取(Stemming):把词的后缀去掉,提取词干。比如running → run,flies → fli。简单粗暴,但不一定准确。
  2. 词形还原(Lemmatization):根据词性和词典,把词还原成原形。比如better → good,went → go。更准确,但需要词典和词性标注。

中文完全不需要这些——中文的”跑”就是”跑”,过去现在将来都是”跑”,没有形态变化。

中文:边界切分是重点

中文没有词形变化问题,但边界切分就够头疼了。

所以中文NLP的研究重点是:怎么切才对?歧义怎么消解?未登录词怎么识别?

整个中文分词研究了几十年,本质上都在解决边界问题。

通俗总结

  • 英文分词:边界天然存在,重点是”词形归一”
  • 中文分词:边界需要判断,重点是”边界识别”

5.3 子词时代的趋同与差异

大模型时代,子词分词成为主流。中英文分词的差异变小了,但依然存在。

5.3.1 预分词步骤不同

虽然都是子词分词,但第一步不一样:

英文子词分词流程

  1. 按空格和标点,把文本切成单词片段(预分词)
  2. 在每个单词内部做子词拆分

比如”unhappiness” → 先识别为一个单词 → 再拆成”un + happy + ness”

中文子词分词流程(以SentencePiece为例):

  1. 不做预分词,整个句子就是一串字符
  2. 直接在字符序列上做子词拆分

比如”自然语言处理” → 直接拆成”自然 + 语言 + 处理”

当然,如果用的是BBPE(字节级BPE),那预分词步骤更不重要——反正最后都拆成字节。

5.3.2 中文Token更”贵”

这是一个非常实际的问题:同样的信息量,中文消耗的Token数比英文多。

比如GPT-3.5的词表约10万Token,其中:

  • 英文常用词很多是整词Token,一个单词占1个Token
  • 中文常用词很多是两字或三字Token,但也有大量单字Token
  • 平均下来,一个英文Token约对应4个字符,一个中文Token约对应1.3个汉字

为什么会这样?因为GPT的词表是英文主导的,对中文优化不足。训练语料中英文占比高,常见英文组合都被合并成了整词Token;而中文出现频率不够高的组合,就停留在单字或两字级别。

这就是为什么大家说”中文比英文费Token”。同样的上下文窗口,中文能装的内容比英文少。

5.3.3 国产大模型的中文优化

国产大模型(比如通义千问、文心一言、DeepSeek等)普遍针对中文做了优化:

  1. 更大的词表:很多国产模型词表在12万-15万,比GPT的10万更大,放入了更多中文常用词
  2. 中文语料占比高:训练语料中中文比例高,中文子词合并更充分
  3. 常用词整词化:高频中文词尽量作为整词存在,减少Token数

效果就是:国产大模型处理中文时,Token利用率更高,同样的字数消耗的Token更少。

5.4 其他语言的分词特点

除了中英文,世界上还有很多语言,它们的分词各有特点:

  • 日语:汉字+假名混合,比中文还复杂。日语有专门的分词工具MeCab,分词+词性标注一体化。
  • 韩语:韩文是拼音文字,但单词之间也有空格,类似英文。但韩语的黏着语形态变化非常丰富。
  • 泰语:没有空格,类似中文,但字母是拼音文字。泰语分词也是专门的研究领域。
  • 德语:有空格,但喜欢把多个词拼成一个长复合词,比如”Donaudampfschifffahrtsgesellschaftskapitän”(多瑙河轮船公司船长)。这种词必须拆成子词。

可以说,每种语言都有自己的分词痛点。子词分词之所以成为主流,很大程度上是因为它能用一套统一的框架处理几乎所有语言。

5.5 本章总结

传统时代,中英文分词是两个完全不同的技术领域:

  • 英文:空格分词 + 词形还原
  • 中文:边界识别 + 歧义消解

子词时代,两者在技术框架上趋同了——都是把文本切成子词单元。但由于语言特性和词表优化的差异,实际表现仍然不同:

  • 英文:子词合并充分,Token利用率高
  • 中文:子词颗粒度偏细,Token利用率较低,但国产模型在不断优化

理解这些差异,能帮你更好地理解大模型的行为——比如为什么同样的问题,英文回答得更好,为什么中文感觉上下文窗口不够用。

第六章:主流分词工具全景对比

讲了这么多原理,我们来看看实际工作中能用哪些分词工具。工欲善其事,必先利其器。选择合适的分词工具,能让你的项目事半功倍。

6.1 中文分词工具

中文分词工具非常多,各有特色。我们从最常用的开始介绍。

6.1.1 Jieba:轻量之王

Jieba(结巴)应该是Python生态里最流行的中文分词库了。如果你做Python项目需要快速上手中文分词,Jieba几乎是首选。

核心特点

  • 纯Python实现,安装简单(pip install jieba)
  • 轻量级,内存占用小
  • 速度快,每秒能处理几十万字
  • 支持三种分词模式

三种分词模式

  1. 精确模式:最常用的模式,试图将句子最精确地切开,适合文本分析。

    • 输入:“我来到北京清华大学”
    • 输出:[“我”, “来到”, “北京”, “清华大学”]
  2. 全模式:把句子中所有可以成词的词语都扫描出来,速度非常快,但不能解决歧义。

    • 输入:“我来到北京清华大学”
    • 输出:[“我”, “来到”, “北京”, “清华”, “清华大学”, “华大”, “大学”]
  3. 搜索引擎模式:在精确模式的基础上,对长词再次切分,提高召回率,适合搜索引擎分词。

    • 输入:“我来到北京清华大学”
    • 输出:[“我”, “来到”, “北京”, “清华”, “华大”, “大学”, “清华大学”]

技术原理: Jieba采用”前缀词典+最大概率路径+HMM”的混合方案:

  • 基于Trie树结构构建前缀词典,实现高效的词典匹配
  • 用动态规划查找最大概率路径,找出最优切分
  • 对于未登录词,采用HMM模型和Viterbi算法识别

适用场景

  • 快速原型开发
  • 中小型项目
  • 对速度要求高、对准确率要求不是极致的场景

不足

  • 准确率不如HanLP、THULAC等专业工具
  • 对歧义和未登录词处理一般
  • 功能相对单一,只有分词和关键词提取

6.1.2 HanLP:全能选手

HanLP是由何晗开发的一站式NLP工具包,号称”面向生产环境的多语种自然语言处理工具包”。

核心特点

  • 功能极其全面:分词、词性标注、命名实体识别、依存句法分析、语义角色标注……几乎你能想到的NLP基础任务都有
  • 支持多种分词算法:最短路径分词、CRF分词、感知机分词、深度学习分词
  • 支持Java和Python双语言
  • 有完善的文档和社区
  • 支持自定义词典、用户词典

分词模式: HanLP提供了多种分词器供选择:

  • 标准分词:平衡速度与精度
  • NLP分词:用CRF模型,准确率更高
  • 索引分词:适合搜索引擎,对长词二次切分
  • 极速词典分词:纯词典匹配,速度极快

适用场景

  • 工业级生产环境
  • 需要完整NLP管线的项目
  • 对准确率要求较高的场景

不足

  • 比Jieba重,内存占用大
  • Python版比Java版功能少一些
  • 学习曲线稍陡

6.1.3 THULAC:清华出品

THULAC(THU Lexical Analyzer for Chinese)是清华大学自然语言处理实验室出品的中文词法分析工具包。

核心特点

  • 准确率高:在CTB5数据集上F1值可达97.3%
  • 速度快:只分词可达1.3MB/s,约每秒15万字
  • 分词和词性标注一体化
  • 训练语料规模大(5800万字人工标注语料)

适用场景

  • 学术研究
  • 对准确率要求高的项目

不足

  • 更新较慢
  • 功能相对HanLP少
  • Python版性能不如C++版

6.1.4 LAC:百度的词法分析

LAC(Lexical Analysis of Chinese)是百度开源的词法分析工具。

核心特点

  • 基于深度学习联合模型
  • 同时完成分词、词性标注、命名实体识别
  • 分词F1值超过91%(百度官方数据)
  • 支持Python、C++、Java等多种语言
  • 有移动端版本

适用场景

  • 百度生态项目
  • 需要同时做分词和实体识别的场景

6.1.5 IK Analyzer:搜索领域老兵

IK Analyzer是Java生态里最常用的中文分词器,广泛用于Elasticsearch等搜索引擎。

核心特点

  • 专门为搜索优化
  • 支持细粒度和智能两种切分模式
  • 支持自定义词典、停用词
  • 和Elasticsearch、Solr集成良好

适用场景

  • 搜索引擎(Elasticsearch)
  • Java后端项目

6.2 英文分词工具

英文分词工具就更多了,我们介绍几个最主流的。

6.2.1 NLTK:学术标配

NLTK(Natural Language Toolkit)是Python最经典的NLP库,几乎是NLP入门必学。

核心特点

  • 功能全面:分词、词性标注、句法分析、语义分析应有尽有
  • 学术资源丰富:附带大量语料库和数据集
  • 教学友好,文档完善

分词功能

  • word_tokenize:基于Penn Treebank分词规则
  • sent_tokenize:句子切分
  • 支持多种分词器

适用场景

  • 学习和研究
  • 学术项目
  • 快速原型

不足

  • 速度慢,不适合工业级大数据量
  • 分词效果一般,不如spaCy

6.2.2 spaCy:工业级首选

spaCy是目前工业界最流行的Python NLP库,以速度快、准确率高著称。

核心特点

  • 速度极快,专为生产环境设计
  • 准确率高,基于深度学习模型
  • API设计优雅,易用性好
  • 支持多语言
  • 内置词向量

分词功能: spaCy的分词器基于规则+统计的混合方案,处理缩写、连字符、特殊符号等都非常出色。

适用场景

  • 工业级生产环境
  • 英文NLP项目首选

6.2.3 Stanford CoreNLP

斯坦福大学出品的NLP工具包,Java语言,功能非常强大。

核心特点

  • 准确率高,学术认可度高
  • 功能极其全面
  • 支持多语言

不足

  • 速度慢,内存占用大
  • Java为主,Python调用不方便

6.3 大模型时代的分词器

大模型时代,分词器不再是独立工具,而是和模型绑定在一起。每个大模型都有自己的分词器和词表。

6.3.1 HuggingFace Tokenizers

HuggingFace的Tokenizers库是目前最流行的子词分词工具。

核心特点

  • 用Rust实现,速度极快
  • 支持BPE、WordPiece、Unigram等主流算法
  • 和Transformers库无缝集成
  • 支持训练自己的分词器

如果你用HuggingFace生态的模型,基本都是用这个库来做分词。

6.3.2 OpenAI tiktoken

OpenAI官方的分词器,专门用于GPT系列模型。

核心特点

  • 纯Rust实现,速度极快
  • 准确还原GPT模型的分词结果
  • 支持cl100k_base、p50k_base等多种词表
  • 支持Python、Rust等语言

如果你需要精确计算GPT API的Token消耗,tiktoken是最准确的工具。

6.3.3 SentencePiece

Google出品的子词分词工具,前面已经详细介绍过。

核心特点

  • 语言无关,不需要预分词
  • 支持BPE和Unigram两种算法
  • 适合多语言和中日韩语言
  • 速度快,轻量级

很多开源大模型(比如LLaMA、T5)都用SentencePiece来训练分词器。

6.4 工具选型指南

这么多工具,到底该怎么选?给你一个简单的决策树:

中文场景

  • 快速上手、Python项目 → Jieba
  • 工业级、功能全 → HanLP
  • 搜索场景、Java → IK Analyzer
  • 极致准确率 → THULAC
  • 百度生态 → LAC

英文场景

  • 学习研究 → NLTK
  • 工业生产 → spaCy
  • 学术高精度 → Stanford CoreNLP

大模型场景

  • HuggingFace生态 → Tokenizers库
  • GPT系列 → tiktoken
  • 自己训练分词器 → SentencePiece

通用建议

  1. 优先选社区活跃、文档完善的工具
  2. 准确率和速度根据场景权衡,不是越高越好
  3. 垂直领域一定要加自定义词典
  4. 不要重复造轮子,成熟工具已经足够好用

第七章:分词器的应用场景

分词器是NLP的基础组件,它本身不产生直接价值,但几乎所有NLP应用都建立在它之上。这一章我们来看看分词器在各个领域的具体应用。

7.1 搜索引擎:最经典的应用

搜索引擎是分词技术最大、最经典的应用场景。可以说,没有中文分词,就没有好用的中文搜索引擎。

7.1.1 搜索分词的两个阶段

搜索引擎里,分词发生在两个阶段:

第一阶段:建索引的时候 爬虫爬回来的网页,先要分词,然后建立倒排索引。比如”苹果手机多少钱”这篇文章,分词后得到[“苹果”, “手机”, “多少”, “钱”],索引里就记录”苹果”这个词出现在这篇文章里。

第二阶段:用户查询的时候 用户输入搜索词,搜索引擎先对查询词分词,然后去倒排索引里匹配。比如用户搜”苹果手机价格”,分词得到[“苹果”, “手机”, “价格”],然后找出包含这些词的文档。

7.1.2 搜索分词的特殊要求

搜索场景的分词和普通文本分词不一样,有自己的特殊要求:

  1. 多粒度切分:既要匹配长词也要匹配短词。比如”北京大学”,既要能作为整词匹配,也要能拆成”北京”和”大学”分别匹配。所以搜索引擎常用”全模式”或”搜索引擎模式”分词。

  2. 高召回优先:搜索场景下,漏结果比多结果更严重。所以分词倾向于切得更细一些,保证尽量多地召回相关结果,然后靠排序来保证质量。

  3. 实时性要求高:用户输入后要立刻出结果,分词必须非常快,通常毫秒级完成。

  4. 同义词扩展:分词之后通常还要做同义词扩展,比如”电脑”扩展出”计算机”,“手机”扩展出”移动电话”。

7.1.3 通俗例子

用户搜索”苹果14手机壳”:

  1. 分词器切分:[“苹果”, “14”, “手机壳”]
  2. 如果有自定义词典,可能识别为:[“苹果14”, “手机壳”]
  3. 同义词扩展:“手机壳” → 手机壳、保护套、手机保护壳
  4. 去索引里匹配包含这些词的商品

如果分词器把”苹果14”切成了”苹果”和”14”,搜索结果里就会混入大量苹果水果和无关的14相关商品,用户体验就差了。所以电商搜索非常看重品牌词、型号词的识别准确。

7.2 机器翻译:子词分词的发源地

机器翻译是子词分词技术的发源地。正是因为机器翻译饱受OOV困扰,才催生了BPE等子词算法。

7.2.1 传统词级翻译的问题

早期的神经机器翻译用的是词级分词。词汇表里有多少词,模型就能翻译多少词。遇到词汇表里没有的词,模型就傻了。

比如翻译”张三去了北京”,如果”张三”不在词表里,模型就不知道怎么翻,可能直接跳过,或者翻译成[UNK]。

专有名词、专业术语、新词,这些都是词级翻译的噩梦。

7.2.2 子词分词如何解决问题

有了子词分词之后,情况就不一样了。

比如”张三”这个人名,虽然词表里没有完整的”张三”,但有”张”和”三”这两个子词。模型可以分别翻译这两个字,再组合起来。虽然效果不如整词好,但至少不会完全丢失信息。

更重要的是,子词分词能利用词根词缀的规律。比如”unhappiness”虽然没见过,但”un-”、“happy”、“-ness”模型都认识,组合起来就能猜个大概。

BPE应用到机器翻译之后,翻译质量提升非常明显,尤其是稀有词和专有名词的翻译。

7.2.3 实际效果

根据Sennrich的原始论文,使用BPE子词分词后:

  • 稀有词的翻译准确率大幅提升
  • 整体BLEU值提升了1-2个点
  • 词汇表大小从几十万压缩到了几万

这在当时是非常大的突破。现在所有的机器翻译系统基本都用子词分词了。

7.3 文本分类与情感分析

文本分类、情感分析、主题识别这些任务,都需要先分词再提取特征。

7.3.1 特征提取的基础

传统的文本分类流程:

  1. 分词
  2. 去停用词(去掉”的”、“了”、“是”这些没意义的词)
  3. 特征提取(比如TF-IDF、词袋模型)
  4. 训练分类器

分词的质量直接影响特征质量。如果把”非小细胞肺癌”切成了”非 / 小 / 细胞 / 肺癌”,特征就散了,分类器可能识别不出这是一种癌症。

7.3.2 粒度对分类的影响

分词粒度和分类效果有密切关系:

  • 粒度太粗:特征太稀疏,很多文本没有共同特征
  • 粒度太细:噪声太多,无关特征干扰分类

一般来说,粗粒度的分词更适合分类任务。因为粗粒度的词语义更明确,区分度更高。

比如情感分析中,“非常满意”作为整词,情感倾向很明确;如果切成”非常”和”满意”,情感强度就弱了。

7.4 大语言模型:Tokenizer的核心作用

大模型时代,分词器(Tokenizer)的角色变得更加核心了。

7.4.1 文本到数字的转换

大模型本质上是一个处理数字序列的神经网络。它不认识文字,只认识数字。

Tokenizer就是负责转换的桥梁:

  1. 编码(Encoding):输入文本 → Tokenizer切分成Token → 每个Token映射成一个数字ID → 输入模型
  2. 解码(Decoding):模型输出数字ID → Tokenizer映射回Token → Token拼接成文本

没有Tokenizer,模型和人类语言之间就无法沟通。

7.4.2 词表大小的权衡

词表大小是大模型的一个核心超参数,它直接影响模型的多个方面:

词表大词表小
每个Token语义多,序列短,推理快每个Token语义少,序列长,推理慢
Embedding参数量大,训练难Embedding参数量小,训练易
少见的词也能整词表示,效果好少见的词拆成子词,效果差
词表稀疏,很多Token用得少词表紧凑,每个Token都常用

模型设计者需要在这些因素之间找到平衡点。GPT-3选了5万,GPT-3.5选了10万,国产模型很多选12万-15万。

7.4.3 特殊Token的功能

大模型的词表里除了普通的文本Token,还有很多特殊Token:

  • <|endoftext|>:文本结束标记
  • <|beginoftext|>:文本开始标记
  • <|padding|>:填充标记
  • <|unk|>:未知Token
  • <|system|><|user|><|assistant|>:对话角色标记
  • <|function_call|>:工具调用标记

这些特殊Token是模型控制逻辑的重要手段。比如看到<|endoftext|>,模型就知道该停止生成了。

7.5 垂直行业应用

通用分词器在专业领域往往不够用。医疗、金融、法律这些行业有大量专业术语,必须做领域适配。

7.5.1 医疗领域:病历分词

医疗是对分词准确度要求最高的领域之一。

核心痛点

  • 医学术语多且长:“急性下壁心肌梗死”、“头孢哌酮舒巴坦钠”
  • 缩写多:“房颤”(心房颤动)、“糖网”(糖尿病视网膜病变)
  • 拉丁药名、解剖部位名
  • 病历书写口语化、不规范

解决方案

  1. 构建医学专业词典,导入分词器
  2. 用医学语料微调分词模型
  3. 术语本体图谱辅助消歧

实际案例:某医疗问答系统,原始模型对”非小细胞肺癌靶向治疗”的实体召回率很低。引入Jieba加载医学术语词典后,实体召回率提升了18.7%。

7.5.2 金融领域:研报分析

金融领域有大量专业词汇:财报术语、金融产品名、股票代码、机构名称等等。

典型应用

  • 研报自动摘要
  • 舆情监控
  • 风险事件识别
  • 财报信息抽取

分词要求

  • 金融术语完整识别:“EBITDA”、“市盈率”、“量化宽松”
  • 股票代码和名称对应
  • 机构名完整切分

优化效果:专门的金融领域分词器,对专业术语的Token消耗比通用分词器少30%-40%,处理效率和准确率都更高。

7.5.3 法律领域:法条与案例检索

法律文本的特点是用词严谨、固定搭配多、专有名词密集。

典型应用

  • 法律文书检索
  • 法条引用识别
  • 判例相似度匹配
  • 合同信息抽取

分词难点

  • 法律术语:” habeas corpus”(人身保护令)、“certiorari”(调卷令)
  • 法条引用格式:“42 U.S.C. § 1983”
  • 案件名称、当事人名称

专业法律分词器能把复杂法律术语的Token数减少一半以上,显著提升检索和理解的效果。

7.5.4 电商领域:商品搜索与推荐

电商搜索是分词技术最成熟的商业应用之一。

核心挑战

  • 用户输入口语化、不规范
  • 品牌词、型号词层出不穷
  • 同义词、别称多(“手机壳”又叫”保护套”)
  • 中英混合、拼音混杂

电商分词的特殊技巧

  • 品牌词库实时更新
  • Query纠错(用户打错字也能识别)
  • 同义词扩展
  • 拼音分词支持

淘宝、京东的搜索系统背后,都有非常复杂的分词和Query理解模块。用户能搜到想要的商品,分词器功不可没。

7.6 其他应用场景

除了上面这些,分词器还活跃在很多地方:

  • 智能客服:理解用户问题的关键词
  • 舆情监控:从海量文本中提取话题和实体
  • 自动摘要:先分词再计算词重要性
  • 输入法:联想词、纠错都依赖分词
  • 语音识别后处理:语音转文字后,分词帮助断句和加标点

可以说,只要涉及中文文本处理,就几乎离不开分词器。它就像空气一样,平时你感觉不到它的存在,但没有它寸步难行。

第八章:分词的三大挑战与解决方案

分词看起来简单,实则困难重重。经过几十年的研究,还有几个核心难题没有被完全解决。这一章我们来聊聊分词的”三大敌人”——歧义、未登录词、领域差异。

8.1 歧义消解:分词最难的问题

歧义是中文分词最古老、最核心的难题。同样一串字,有多种合法的切法,人根据语境能判断,但机器很难。

8.1.1 歧义的类型

分词歧义主要分为两类:

第一类:交集型歧义

一个字串,既是前一个词的结尾,也是后一个词的开头,形成交叉。

经典例子:

  • “南京市长江大桥”

    • 切法A:南京市 / 长江大桥
    • 切法B:南京 / 市长 / 江大桥
    • “市”和”长”形成交集
  • “结婚的和尚未结婚的”

    • 切法A:结婚 / 的 / 和 / 尚未 / 结婚 / 的
    • 切法B:结婚 / 的 / 和尚 / 未 / 结婚 / 的
    • “和”和”尚”形成交集

交集型歧义是最常见的歧义类型,大约占歧义总量的80%以上。

第二类:组合型歧义

一个字串,可以作为一个整词,也可以拆成几个词。

经典例子:

  • “马上”

    • 副词:我马上就来 → 马上(立刻)
    • 名词+动词:马上有个人 → 马 / 上(马的上面)
  • “将来”

    • 名词:将来的日子 → 将来(未来)
    • 副词+动词:将来北京 → 将 / 来(将要来)

组合型歧义更难,因为它需要真正理解语义才能判断。

8.1.2 真歧义和伪歧义

还有一个概念:真歧义和伪歧义。

  • 伪歧义:虽然理论上有多种切法,但实际语言中只有一种是常用的。比如”乒乓球拍卖完了”,理论上可以切成”乒乓球 / 拍卖 / 完了”,但实际几乎没人这么说话。伪歧义词典里有,但语料中出现概率极低。
  • 真歧义:两种切法在实际语言中都很常见。比如”马上”两种用法都很多。真歧义必须结合上下文才能判断。

真歧义才是真正的难点。

8.1.3 消解方法的演进

歧义消解技术经历了几代演进:

第一代:规则法 人工写歧义消解规则。比如”的”后面不接B标签之类的。规则法能处理一些简单歧义,但覆盖不了所有情况,而且规则之间容易冲突。

第二代:统计法 用N-gram、HMM、CRF等统计模型,根据上下文概率判断哪种切法更合理。统计法比规则法强很多,能处理大部分常见歧义。但它本质上是在”搭概率积木”,没有真正理解语义,遇到需要常识的歧义就不行了。

第三代:语义法 用预训练语言模型(BERT等),基于深层语义理解来判断。BERT读过海量文本,学到了丰富的世界知识,很多需要常识的歧义它也能判断对。

比如”他骑在马上”,BERT知道”骑”这个动作是骑在动物背上,所以”马上”应该是”马 / 上”而不是副词”马上”。

8.1.4 歧义消解的极限

虽然技术在进步,但歧义问题永远不可能100%解决。因为:

  1. 语言本身就有歧义,有些句子人看了也会歧义
  2. 有些歧义需要外部知识和常识才能判断
  3. 有些歧义需要结合更大的上下文甚至背景知识

分词准确率从95%提升到99%容易,从99%提升到99.9%就非常难了。最后那零点几个百分点,都是各种疑难歧义。

8.2 未登录词识别:永远追不上的新词

未登录词(OOV)就是词典里没有、模型没见过的词。这是分词的第二大挑战。

8.2.1 什么是未登录词

未登录词主要包括:

  1. 命名实体:人名、地名、机构名、品牌名
  2. 新词:新出现的词,比如”内卷”、“躺平”、“元宇宙”
  3. 专业术语:各行业的专有名词
  4. 外来词:音译词、字母词,比如”咖啡”、“GDP”
  5. 网络用语:“yyds”、“emo”、“破防”

语言是活的,每天都有新词产生。词典的更新速度永远赶不上新词出现的速度。

8.2.2 传统方法:基于字的识别

传统分词系统怎么识别未登录词?主要靠序列标注模型。

比如HMM或CRF,它们不是只看词典,而是根据字的位置规律来判断。比如:

  • “张”、“李”、“王”这些字经常出现在人名开头
  • “伟”、“芳”、“明”这些字经常出现在人名结尾
  • 某些字搭配在一起很像人名

模型学多了之后,即使没见过的人名,也能大概猜出来这几个字应该组成一个词。

但这种方法准确率有限。遇到生僻人名、奇怪的品牌名,还是容易切错。

8.2.3 子词分词:从根本上缓解OOV

子词分词没有”解决”OOV问题,而是从根本上”消解”了OOV问题。

因为子词分词的词表里有最基础的字符/字节兜底,任何词都能被拆成子词,不存在”完全不认识”的情况。

当然,子词也不是完美的:

  • 整词认识的效果最好
  • 拆成子词也能懂,但效果打折扣
  • 拆得越碎,效果越差

但比起传统词级分词遇到OOV直接变成[UNK],子词分词已经是巨大进步了。

8.2.4 新词发现技术

除了被动识别,还有主动发现新词的技术——新词发现。

新词发现就是从未标注的海量文本中,自动找出可能是新词的字符串。基本思路是:

  1. 统计所有可能的字符串的出现频率
  2. 计算”凝固度”:字符串内部字之间的结合有多紧密。比如”苹果”凝固度高,“的苹”凝固度低
  3. 计算”自由度”:字符串左右能接的字有多少种。比如”苹果”左右能接很多种字,自由度高;“巧克”后面几乎总是”力”,自由度低
  4. 凝固度高且自由度高的字符串,很可能是一个新词

新词发现可以用来自动更新词典,让分词系统与时俱进。

8.3 领域适配:通用vs专业

第三个挑战是领域差异。通用分词器在通用文本上表现不错,但放到专业领域就不行了。

8.3.1 领域差异带来的问题

不同领域的语言差异非常大:

  • 词汇不同:医学有医学术语,金融有金融术语
  • 分词标准不同:有的领域倾向于整词,有的领域倾向于细分
  • 语言风格不同:法律文本严谨正式,网络文本口语随意

比如”关节”这个词:

  • 通用场景:是一个词,指身体部位
  • 机械领域:可能是”关 / 节”,指关闭阀门这个动作

通用分词器按照通用习惯切,到了专业领域就可能出错。

8.3.2 解决方案一:自定义词典

最简单也最有效的方法:加自定义词典。

把领域内的专业术语整理成词典文件,加载到分词器里。分词器遇到这些词就会优先按词典切。

比如医疗系统加载ICD-10疾病术语词典,法律系统加载法律术语词典,电商系统加载品牌商品词典。

自定义词典的优点是简单、见效快、可控性强;缺点是需要人工维护,新词加不进去。

8.3.3 解决方案二:领域微调

更进阶的方法:用领域语料重新训练或微调分词模型。

比如在通用CRF分词模型的基础上,用几千条标注好的医疗病历继续训练,模型就能学到医疗领域的分词规律。

微调比自定义词典效果好,能识别词典里没有的领域词汇。但需要标注数据,成本更高。

8.3.4 解决方案三:领域专属分词器

对分词质量要求极高的场景,可以直接训练领域专属的分词器。

比如:

  • 医疗分词器:专门在病历语料上训练
  • 法律分词器:专门在法律文书上训练
  • 代码分词器:专门处理程序代码

领域专属分词器在特定领域的表现肯定比通用分词器好,但通用性差,换个领域就不行了。

8.4 本章总结

分词的三大挑战——歧义、未登录词、领域差异,本质上对应了三个层面的困难:

  • 歧义:语义理解层面的困难
  • 未登录词:词汇开放性层面的困难
  • 领域差异:语言多样性层面的困难

这三个问题都没有终极解决方案,技术只能不断逼近完美。

从规则到统计到深度学习,每一代技术都在这三个问题上有所进步:

  • 深度学习在歧义消解上进步最大
  • 子词分词基本解决了未登录词问题
  • 领域微调能较好地解决领域适配问题

但离”完美分词”还有很长的路要走。也许只有当模型真正理解语言的时候,分词这个问题才会彻底消失。

第九章:分词器的评估与优化

怎么判断一个分词器好不好?怎么优化分词器的性能?这一章我们聊聊评估和优化。

9.1 评估指标

评估一个分词器,主要看三个维度:准不准、快不快、占多少内存。

9.1.1 准确率指标

最常用的三个指标:精确率(Precision)、召回率(Recall)、F1值。

  • 精确率:切出来的词里,有多少是切对的

    • 公式:正确切分的词数 / 切分出来的总词数
  • 召回率:应该切对的词里,有多少被切对了

    • 公式:正确切分的词数 / 标准答案的总词数
  • F1值:精确率和召回率的调和平均,综合评价

    • 公式:F1 = 2 × P × R / (P + R)

一般都用F1值来衡量分词准确率。

目前的水平:

  • 基于词典:~90%
  • CRF统计模型:95%-97%
  • 深度学习:97%-99%
  • 人类:约97%-98%(人与人之间也有分歧)

有意思的是,最好的分词模型准确率已经超过普通人了。因为普通人偶尔也会纠结某个地方该不该切。

9.1.2 速度指标

速度通常用”每秒处理多少字符”或”每秒处理多少词”来衡量。

不同算法速度差异很大:

  • 纯词典匹配:每秒几百万字
  • CRF:每秒几十万字
  • BiLSTM:每秒几万字
  • BERT:每秒几千字

速度和准确率往往是矛盾的。越准的模型越慢,越快的模型越不准。实际项目中要根据需求权衡。

9.1.3 内存指标

内存占用主要看词典大小和模型大小:

  • 轻量级词典:几MB
  • 统计模型:几十MB
  • 深度学习模型:几百MB到几GB

嵌入式设备、移动端对内存比较敏感,需要选轻量的分词器。

9.1.4 子词分词的特殊评估

子词分词的评估不太一样,因为它没有标准答案。子词分词没有绝对的”对”和”错”,只有”好”和”不好”。

子词分词通常用这些指标评估:

  • 压缩率:同样的文本,需要多少个Token。越少越好
  • 困惑度:语言模型在该词表下的困惑度。越低越好
  • 下游任务效果:最终翻译、生成等任务的效果。这是最根本的指标

毕竟子词分词是为下游任务服务的。下游任务效果好,分词就是好的。

9.2 常用评测数据集

中文分词有几个标准评测数据集,都是SIGHAN Bakeoff竞赛流传下来的:

  • MSR:微软亚洲研究院标注,新闻语料,约400万字
  • PKU:北京大学标注,新闻语料,约110万字
  • CTB:中文树库,宾夕法尼亚大学标注
  • AS:台湾中研院标注,繁体中文

这些数据集有标准的训练集和测试集,不同算法在上面跑,结果可以横向对比。

不过这些数据集都是新闻领域的,和实际业务场景可能有差异。最好的评估还是在自己的业务数据上测。

9.3 性能优化技巧

实际项目中,分词往往是性能瓶颈之一。尤其是搜索、推荐这些需要高并发的场景。

这里介绍一些常用的性能优化技巧。

9.3.1 算法层面优化

Trie树(字典树) 这是词典分词最经典的数据结构。Trie树把词典组织成一棵树,每个字是一个节点。查找的时候从根节点往下走,匹配非常快。

Trie树的好处:

  • 查找速度快,和词典大小关系不大
  • 前缀匹配天然支持最大匹配法
  • 内存占用比哈希表小

AC自动机 如果需要同时匹配大量模式串,AC自动机比Trie树更高效。IK Analyzer等搜索分词器常用。

动态规划优化 计算最大概率路径的时候,可以用各种动态规划优化技巧减少计算量。

9.3.2 工程层面优化

缓存 很多场景下,重复的文本会被多次分词。比如搜索系统里,热门查询每天被搜成千上万次。加个缓存,分词结果直接复用,性能提升巨大。

并行处理 分词是天然可并行的——文本切成好几段,每段同时分,最后拼起来。多核CPU上能线性提速。

Rust/C++实现 Python写的分词器再怎么优化也有天花板。用Rust或C++重写核心逻辑,速度能提升几倍到几十倍。HuggingFace Tokenizers、tiktoken都是Rust实现的。

流式处理 处理大文件时,不要一次性全部读进内存,边读边处理,降低内存峰值。

9.3.3 词典优化

裁剪词典 不是词越多越好。很多低频词几乎用不上,占内存还影响匹配速度。根据业务场景裁剪掉没用的词,又省内存又快。

热更新 业务词典经常需要更新。好的分词器支持词典热更新——不用重启服务,动态加载新词典。这对线上系统很重要。

分层词典 核心常用词放第一层,快速匹配;低频词放第二层,第一层匹配不到再查第二层。兼顾速度和覆盖率。

9.3.4 子词优化

词表大小选择 根据业务场景选择合适的词表大小。不是越大越好——太大了Embedding层参数多,模型训练慢;太小了序列太长,推理慢。

合并规则优化 BPE的合并顺序对最终效果有影响。可以针对特定语言或领域优化合并策略,让常用组合更早被合并。

9.4 避坑指南

最后说几个初学者常踩的坑:

坑1:只看准确率不看速度 很多人上来就选最准的模型,结果上线后扛不住并发。一定要先搞清楚性能要求。

坑2:用通用评测代替业务评测 标准数据集上分数高,不代表在你的业务数据上好。一定要用自己的数据测一遍。

坑3:词典加得越多越好 自定义词典不是越多越好。加得太多会引入错误,反而降低准确率。词典要精选,定期清理。

坑4:过度追求准确率 分词只是第一步,后面还有更重要的任务。分词准确率从97%提升到98%,可能花费巨大成本,但对最终业务效果提升很小。够用就好。

第十章:分词器的未来发展趋势

分词技术发展到今天,是不是已经到头了?未来会往哪个方向走?这一章我们来聊聊趋势。

10.1 语义感知与动态分词

现在的分词器都是”一刀切”——同一个词,不管什么上下文,切法都一样。但实际上,分词粒度应该根据语境动态调整。

10.1.1 固定粒度的局限

举个例子:“北京大学”

  • 在”我考上了北京大学”这句话里,作为一个整体理解更好
  • 在”北京大学校园很美”这句话里,切成”北京 / 大学”也能理解
  • 在搜索场景,既需要整词匹配也需要分词匹配

固定粒度的分词,无法同时满足所有需求。

10.1.2 上下文自适应分词

未来的分词器可能是动态的:

  • 理解当前任务是什么
  • 理解上下文语义
  • 自动决定该切多细

比如做文本分类时切粗一点,做机器翻译时切细一点;遇到常见词切粗一点,遇到生僻词切细一点。

这需要分词器和下游任务深度结合,而不是一个独立的预处理步骤。

10.2 多模态统一分词

多模态大模型兴起后,分词的概念正在扩展。

10.2.1 从文本Token到统一Token

以前,Token只针对文本。现在图像、音频也有了Token的概念:

  • 图像:把图片切成一个个Patch,每个Patch就是一个视觉Token
  • 音频:把音频切成一个个片段,每个片段就是一个音频Token

未来的多模态模型,可能会有一套统一的Token体系——文本、图像、音频都被编码到同一个Token空间里。

10.2.2 分词器的新使命

传统分词器只处理文本。未来的”分词器”可能要处理各种模态:

  • 文本怎么切
  • 图像怎么分块
  • 音频怎么分段
  • 不同模态的Token怎么对齐

这已经超出了传统分词的范畴,但本质思想是一样的——把连续的信号切成离散的单元,供模型处理。

10.3 低资源语言适配

现在的分词技术,对中文、英文这些高资源语言支持得很好,但对小语种、少数民族语言支持还很差。

10.3.1 少样本/零样本分词

未来的方向是:不需要大量标注数据,只用很少的样本甚至零样本,就能让分词器适应新语言。

比如用大模型的跨语言迁移能力——大模型已经学会了通用的语言规律,稍微给点提示就能做新语言的分词。

10.3.2 无监督分词

能不能完全不用标注数据,让模型自己从原始文本中学会分词?

这是一个很有吸引力的方向。人类小孩学说话,没人教他分词,听多了自然就知道哪里是词边界。模型能不能也这样?

目前已有一些无监督分词的研究,但效果还不如监督方法。随着技术进步,这可能是下一个突破点。

10.4 “去分词化”的探索

最激进的方向是:彻底取消分词器这个步骤。

10.4.1 为什么要去分词化

支持”去分词化”的理由:

  1. 分词是人为的预处理,不一定是最优的。模型也许有更好的切分方式,只是人类没想到。
  2. 误差传播:分词错了,后面全错。如果不分词,就没有分词错误。
  3. 语言公平:所有语言一视同仁,不用为每种语言设计分词器。
  4. 鲁棒性强:对噪声、乱码、特殊字符不敏感。

10.4.2 代表性研究

ByT5(Google,2021) 直接在UTF-8字节上做T5模型,完全不需要分词。输入就是字节序列,输出也是字节序列。

ByT5的特点:

  • 没有[UNK],任何文本都能处理
  • 对拼写错误、噪声更鲁棒
  • 在形态学、音译等任务上表现更好
  • 缺点:序列太长,计算量大,速度慢

MEGABYTE(Meta,2023) 针对字节序列太长的问题,MEGABYTE提出了层次化方案:

  • 全局模型:处理块级别的长距离依赖
  • 局部模型:处理块内的字节细节

用分而治之的思路,解决了纯字节模型计算量大的问题。

H-Net(CMU,2025) 通过动态分块机制,自适应地将字节流分割成语义单元。分块不是固定的,而是根据内容动态决定的。

这已经非常接近”模型自己学会分词”的理想了。

ByteFlow(亚马逊,2025) 基于压缩率的自适应分块策略。哪里信息密度高就切得细,哪里信息密度低就切得粗。

10.4.3 去分词化的挑战

去分词化虽然美好,但目前还有很多挑战:

  1. 计算效率:字节序列比Token序列长好几倍,计算量太大
  2. 训练难度:直接在字节上学语义更难,需要更多数据和算力
  3. 推理速度:生成速度慢,不实用

目前来看,子词分词在效率和效果的平衡上还是最优的。去分词化还处在实验室阶段。

10.5 分词技术的终极形态猜想

最后,我来大胆猜想一下分词技术的终极形态。

猜想一:分词器消失,融入模型 未来的模型可能不再有独立的分词步骤。输入原始字节流,模型内部自动学习合适的表示单元。分块是动态的、自适应的,甚至是不可解释的。

猜想二:统一的多模态Tokenizer 未来会有一个统一的Tokenizer,能处理文本、图像、音频、视频等所有模态。所有模态都被编码到同一个Token空间,模型在统一的表示上推理。

猜想三:语义Token取代符号Token 现在的Token是符号级的(字符、子词、词)。未来可能是语义级的——每个Token对应一个概念,而不是一串字符。模型直接在概念层面运算。

这些猜想听起来有点科幻,但技术发展往往超出想象。二十年前的人想不到今天的大模型,我们也很难想象二十年后的NLP会是什么样子。

但有一点可以确定:只要人类还在和机器用语言交流,“如何把语言切分成机器能处理的单元”这个问题就会一直存在。形式可能会变,但本质不变。

第十一章:动手实践:从零理解分词

光说不练假把式。这一章我们动手写点代码,实际感受一下分词器是怎么工作的。

11.1 手写一个最简单的正向最大匹配

我们来实现一个最基础的正向最大匹配分词器。十几行代码就能搞定。

def forward_max_match(text, word_dict, max_len=4):
    """
    正向最大匹配分词
    text: 待分词文本
    word_dict: 词典(集合)
    max_len: 词典中最长词的长度
    """
    result = []
    index = 0
    while index < len(text):
        # 取当前位置开始的max_len个字,如果不够就取到末尾
        end = min(index + max_len, len(text))
        # 从长到短尝试匹配
        while end > index:
            word = text[index:end]
            if word in word_dict:
                break
            end -= 1
        # 如果都没匹配上,就取单个字
        if end == index:
            result.append(text[index])
            index += 1
        else:
            result.append(text[index:end])
            index = end
    return result


# 测试一下
word_dict = {"研究", "研究生", "生命", "起源", "南京", "市长", "长江", "大桥", "南京市"}
text1 = "研究生命起源"
text2 = "南京市长江大桥"

print("正向最大匹配结果:")
print(f"{text1}{' / '.join(forward_max_match(text1, word_dict))}")
print(f"{text2}{' / '.join(forward_max_match(text2, word_dict))}")

运行结果:

正向最大匹配结果:
研究生命起源 → 研究生 / 命 / 起源
南京市长江大桥 → 南京市 / 长江 / 大桥

你看,“研究生命起源”切错了,但”南京市长江大桥”切对了。这就是正向最大匹配的特点——有时候对有时候错,完全看句子结构。

你可以试试把方向反过来,实现一个逆向最大匹配,看看”研究生命起源”是不是就能切对了。

11.2 Jieba分词入门实战

接下来我们试试工业界最常用的Jieba分词。

首先安装:

pip install jieba

然后体验三种模式:

import jieba

text = "我来到北京清华大学,喜欢自然语言处理技术"

# 精确模式
seg_list = jieba.lcut(text, cut_all=False)
print("精确模式:", " / ".join(seg_list))

# 全模式
seg_list = jieba.lcut(text, cut_all=True)
print("全模式:", " / ".join(seg_list))

# 搜索引擎模式
seg_list = jieba.lcut_for_search(text)
print("搜索引擎模式:", " / ".join(seg_list))

运行结果大概是这样:

精确模式: 我 / 来到 / 北京 / 清华大学 / , / 喜欢 / 自然语言 / 处理 / 技术
全模式: 我 / 来到 / 北京 / 清华 / 清华大学 / 华大 / 大学 / 喜欢 / 自然 / 自然语言 / 言语 / 处理 / 技术
搜索引擎模式: 我 / 来到 / 北京 / 清华 / 华大 / 大学 / 清华大学 / , / 喜欢 / 自然 / 言语 / 自然语言 / 处理 / 技术

可以看到三种模式的区别:

  • 精确模式最干净,每个词只出现一次
  • 全模式把所有可能的词都扫出来,有重叠
  • 搜索引擎模式介于两者之间,对长词做了二次切分

再试试加载自定义词典:

# 假设我们有一个专业领域的词
text = "非小细胞肺癌靶向治疗效果不错"

# 默认分词
print("默认:", " / ".join(jieba.lcut(text)))

# 加载自定义词典
jieba.add_word("非小细胞肺癌")
jieba.add_word("靶向治疗")

print("加词典后:", " / ".join(jieba.lcut(text)))

加了自定义词典后,专业术语就能被完整识别了。这在垂直领域非常有用。

11.3 用HuggingFace体验子词分词

我们来体验一下大模型的子词分词器。需要安装transformers库:

pip install transformers

以BERT的分词器为例:

from transformers import BertTokenizer

# 加载BERT-base中文分词器
tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")

text = "我爱自然语言处理"

# 分词
tokens = tokenizer.tokenize(text)
print("Token:", tokens)

# 转成ID
ids = tokenizer.convert_tokens_to_ids(tokens)
print("Token ID:", ids)

# 完整编码(自动加特殊标记)
encoded = tokenizer.encode(text)
print("完整编码:", encoded)
print("解码回来:", tokenizer.decode(encoded))

运行结果大概是:

Token: ['我', '爱', '自', '然', '语', '言', '处', '理']
Token ID: [2769, 4263, 5632, 4197, 6427, 6241, 1905, 4415]
完整编码: [101, 2769, 4263, 5632, 4197, 6427, 6241, 1905, 4415, 102]
解码回来: 我 爱 自 然 语 言 处 理

注意到了吗?BERT中文分词器把”自然”、“语言”、“处理”都拆成了单字。这是因为BERT-base中文词表比较小(约2.1万个Token),很多词没有整词,只能拆成单字。

我们再试试GPT的分词器,用tiktoken:

import tiktoken

# 加载GPT-3.5的分词器
enc = tiktoken.encoding_for_model("gpt-3.5-turbo")

text = "我爱自然语言处理"

# 编码
tokens = enc.encode(text)
print(f"Token数量: {len(tokens)}")
print(f"Token ID: {tokens}")

# 看看每个Token对应的文本
for token in tokens:
    print(f"  {token}{enc.decode([token])}")

你会发现,GPT处理中文时,很多是两字一个Token,比BERT的单字要好一些。这就是词表更大带来的好处。

11.4 可视化:看看一句话是怎么被切分的

为了更直观地理解,我们可以写个简单的可视化函数,把不同分词器的结果并排对比:

def compare_tokenizers(text, tokenizers_dict):
    print(f"原文: {text}")
    print("-" * 50)
    for name, tokenize_func in tokenizers_dict.items():
        tokens = tokenize_func(text)
        print(f"{name:15s}: {' | '.join(tokens)}")
    print()


# 定义各种分词函数
import jieba
import tiktoken
from transformers import BertTokenizer

enc = tiktoken.encoding_for_model("gpt-3.5-turbo")
bert_tokenizer = BertTokenizer.from_pretrained("bert-base-chinese")

def jieba_tokenize(text):
    return jieba.lcut(text)

def gpt_tokenize(text):
    return [enc.decode([t]) for t in enc.encode(text)]

def bert_tokenize(text):
    return bert_tokenizer.tokenize(text)

# 对比
compare_tokenizers("自然语言处理很有趣", {
    "Jieba分词": jieba_tokenize,
    "GPT子词": gpt_tokenize,
    "BERT子词": bert_tokenize,
})

运行之后,你就能清晰地看到不同分词器对同一句话的切分差异。

11.5 常见坑与避坑指南

最后分享几个实际使用中的常见坑:

坑1:中英文混合处理不好 很多分词器对中英混合的文本处理一般。比如”iPhone14手机”,可能切得乱七八糟。

  • 避坑:加自定义词典,把品牌型号都加进去
  • 或者用专门的混合语言分词器

坑2:数字和单位被切开 “100元”被切成”100”和”元”,“50kg”被切成”50”和”kg”。有时候你可能希望它们在一起。

  • 避坑:用正则后处理,把数字和单位合并
  • 或者自定义词典强制合并

坑3:停用词没处理好 分词之后通常要去停用词,但停用词表不是通用的。不同场景停用词不一样。

  • 避坑:根据业务场景定制停用词表
  • 不要随便用网上随便找的停用词表

坑4:繁体字、异体字 很多分词器对简体支持好,对繁体支持差。

  • 避坑:先做繁简转换,再分词
  • 或者用支持繁体的分词器

坑5:特殊符号、emoji 特殊符号、emoji处理不好常常导致分词错误。

  • 避坑:预处理时先做规范化
  • 子词分词器(BBPE)在这方面表现更好

第十二章:总结与展望

写到这里,我们的分词器之旅就要告一段落了。让我们最后总结一下。

12.1 核心要点回顾

  1. 分词是什么:把连续的文本切分成有意义的语言单元,是NLP的第一步。
  2. 为什么重要:计算机不认识词,只认识数字。分词是人类语言到机器表示的桥梁。
  3. 发展历程:从规则匹配→统计模型→深度学习→子词分词→去分词化探索。
  4. 传统中文分词:最大匹配、HMM、CRF、深度学习,核心是解决边界问题。
  5. 子词分词:BPE、WordPiece、Unigram、SentencePiece,核心是解决OOV问题。
  6. 中英文差异:英文有空格,重点是词形还原;中文无分隔,重点是边界识别。
  7. 三大挑战:歧义消解、未登录词、领域适配。
  8. 应用场景:搜索引擎、机器翻译、文本分类、大模型、各行各业。
  9. 未来趋势:语义感知、多模态统一、低资源适配、去分词化。

12.2 分词技术的本质

回顾整个发展史,你会发现一个有趣的规律:分词技术一直在”分”与”合”之间摇摆

  • 最早是字符级——每个字单独处理,太散
  • 然后是词级——整词处理,语义完整,但OOV严重
  • 现在是子词级——介于字和词之间,平衡了语义和开放性
  • 未来可能回到字节级——但这次是模型自动学习,不是人工切分

这不是简单的轮回,而是螺旋式上升。每一次回到更小的粒度,都是因为模型能力变强了,能处理更细粒度的信息了。

分词的本质是什么?我认为是在”表示粒度”和”处理能力”之间找平衡

  • 模型能力弱的时候,需要更大的粒度(整词),因为模型处理不了太细的单元
  • 模型能力强的时候,可以用更小的粒度(子词、字节),因为模型能自己组合语义

模型能力越来越强,分词粒度就越来越细。直到有一天,模型强大到可以直接处理原始字节流,分词器这个角色就可以退场了。

12.3 给学习者的建议

如果你正在学习NLP,关于分词我有几个建议:

  1. 理解原理,但不要死钻:分词是基础,但不是NLP的全部。理解核心思想和主流算法就够了,不用深入到每个细节。
  2. 善用工具,不要重复造轮子:工业界有大量成熟的分词工具,直接用就好。绝大多数场景下,你不需要自己写分词器。
  3. 关注业务效果,不要追求极致准确率:分词准确率97%还是98%,对最终业务影响可能很小。把精力放在更重要的地方。
  4. 子词分词是现在的重点:传统中文分词的重要性在下降,子词分词是大模型时代的核心,值得深入理解。
  5. 保持好奇,关注前沿:去分词化、多模态这些新方向很有意思,代表了未来的趋势。

12.4 最后的话

分词是一个非常经典的NLP问题。它看起来简单,似乎就是把句子切成词而已。但深入进去你会发现,这里面蕴含着语言的复杂性、人工智能的局限性,以及几代研究者的智慧。

从最早的查字典,到统计模型,到深度学习,再到子词分词,每一步前进都来之不易。每一个百分点的准确率提升,背后都是无数论文和实验。

也许有一天,分词技术会被更先进的方案取代,成为历史。但它在NLP发展史上的地位不会被忘记——它是计算机理解人类语言的第一道门槛,是无数NLP研究者的起点。

希望这篇长文能帮你真正理解分词器。当你下次使用搜索引擎、和AI对话的时候,能想起背后有这么一个默默工作的小角色,它把人类的语言切碎、编码,送进模型的世界。

这就是分词器的故事——平凡,却不可或缺。


分词器 自然语言处理 NLP 子词分词 BPE算法 大模型 零基础科普
Q-bot
hello!我是 Q-bot,我会唱、跳、rap,can i help you?