想象一条快递分拣传送带,上面所有包裹都紧紧连成一串,没有贴标签,从外面只能看到一整条汉字长链。想要继续分拣,就必须先判断“哪个包裹到哪个包裹是一个完整物件”,然后插入隔板。中文分词做的正是这件事:在一串没有空格的汉字里,找出一个个词语的边界。 分拣站里有两位老师傅。老张手里有一本厚厚的“客户花名册”,他按花名册从左往右、从右往左找最长的名字来切包裹,这种做法很像 Jieba 使用的词典规则。小李没有完整花名册,但他看过几万条真实的包裹记录,会用“前面是什么字、后面是什么字”的经验来推断边界,这种做法很像 HanLP 的 CRF 序列标注。一个靠查表,一个靠经验;一个快而直白,一个慢但更懂上下文。理解了这两位师傅的差别,也就理解了 Jieba 与 HanLP 中文分词的核心差异。

岗位关联

掌握两种分词的底层差异,才能在做中文 NLP 项目时快速判断该用词典规则还是序列标注模型,避免被“分词不准”拖垮后续任务。

目录
  1. 可视化演示
  2. 分词任务基础
  3. 最大匹配原理
  4. Jieba 工程实现
  5. CRF 序列标注
  6. 双方法对比
  7. 工程选型调优
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示双向最大匹配词典分词、CRF序列标注分词及Jieba与HanLP性能对比。切换模式查看不同分词方法的核心机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

双向最大匹配分词 正向最大匹配从左到右扫描,取词典中最长匹配词 逆向最大匹配从右到左扫描,双向结果不一致时取词数少者 seg = argmin_len(FMM, BMM) 基于词典的规则方法无法处理未登录词

分词任务基础

分拣站的一串快递盒要切成几段,首先取决于你如何定义“一个包裹”。在中文里,词与词之间天然没有空格,“认真学习”到底算一个词还是两个词,往往要看使用场景。分词算法就是给这串汉字装隔板的人,隔板装得好,后续的搜索、翻译、问答才能正常工作。

分词难在三件事:第一是歧义,比如“研究生命科学”可以切出“研究/生命/科学”,也可能被误解成“研究生/命/科学”;第二是未登录词,也就是词典里根本没有的新词、人名、网络词;第三是词粒度,有时需要细粒度词,有时需要粗粒度词。衡量分词质量,不能只看“看起来对不对”,还要用标准答案算精确率和召回率。

回到分拣站,F1 就像“装对的隔板数”和“应该装的隔板数”之间的平衡。隔板装得太多,会把包裹切碎;装得太少,又会让两个包裹黏在一起。Jieba 和 HanLP 的差别,正是用不同方式决定这些隔板装在哪里。

F1=2×P×RP+R\displaystyle F_1 = \frac{2 \times P \times R}{P + R}
P 是切出来的词里有多少是对的,R 是标准答案里有多少被找回来了,F1 把两者合成一个综合分。
  1. 精确率 P模型切出的所有词中,和标准答案一致的比例。
    P=TPTP+FPP = \frac{TP}{TP + FP}
  2. 召回率 R标准答案中的词,被模型成功找回的比例。
    R=TPTP+FNR = \frac{TP}{TP + FN}
  3. F1 调和平均当 P 和 R 都高时,F1 才高;只追求其中一个会让另一个下降。
    F1=2×P×RP+RF_1 = \frac{2 \times P \times R}{P + R}

最大匹配原理

老张使用的方法是最大匹配。他手里有一本电话簿,里面记着所有见过的客户名。切包裹时,他从最左侧开始,先试着把最长的一截拿去电话簿里对,对不上就缩短一截,直到对上为止。这种方式叫正向最大匹配;反过来从右侧开始扫描,就是逆向最大匹配。两个方向都做一遍,再比较结果,就是双向最大匹配。

双向最大匹配之所以比单向稳,是因为它可以用“谁的隔板数量少”“谁的单字词更少”来消解部分歧义。它不学习,不训练,所有判断都来自词典,因此速度飞快。

老张的做法简单可靠,但电话簿再厚也有漏网之鱼。遇到“新零售”“元宇宙”这类新词,词典法很容易把它们拆成单字。这也是双向最大匹配最大的瓶颈:词典覆盖不到的地方,就是它的盲区。

wt=arg⁡max⁡w∈D, s[i:i+∣w∣]=w∣w∣\displaystyle w_t = \arg\max_{w \in \mathcal{D},\, s[i:i+|w|] = w} |w|
这句话的意思是:在词典 D 里,找到当前位置能匹配且长度最长的词,作为切分结果。
  1. 当前位置 i扫描指针停在第几个字上,正向从左往右,逆向从右往左。
    i=0,1,2,…i = 0, 1, 2, \dots
  2. 候选词 w从当前位置截取出一段字符串,去词典 D 里查找。
    s[i:i+∣w∣]=ws[i:i+|w|] = w
  3. 取最长在所有能匹配的候选中,选择长度最大的那个。
    arg⁡max⁡w∈D∣w∣\arg\max_{w \in \mathcal{D}} |w|
  4. 无匹配兜底如果词典里没有任何候选,就暂时把当前字切成单字词。
    ∣wt∣=1|w_t| = 1

Jieba 工程实现

Jieba 可以看成老张的升级版。它不再一个词一个词地死板匹配,而是先建了一本“前缀索引”,再用动态规划寻找整句最大概率路径。前缀索引能快速知道每个汉字后面可能跟着哪些词,比如“自然”后面可能有“语言”,也可能没有。这个词表让 Jieba 能高效地构建一张有向无环图,也叫 DAG。

DAG 中每个节点是一个字,每条边代表从当前位置到某个结束位置能组成一个词。Jieba 的任务是从所有路径中选一条总概率最高的切分路径。概率来自词频统计:常见词的路径得分更高,这样“自然语言”会被优先保留,而不是被切碎成“自然”“语言”或单字。

这套机制比单纯双向最大匹配更聪明,因为它综合考虑了整句的词频。Jieba 还额外用 HMM 来识别词典外的词,给未登录词一个“兜底”通道。对大部分通用场景来说,Jieba 已经足够快、足够准。

score(i)=max⁡j∈next(i)(score(j)+log⁡p(wi,j))\displaystyle \mathrm{score}(i) = \max_{j \in \mathrm{next}(i)} \big( \mathrm{score}(j) + \log p(w_{i,j}) \big)
score(i) 表示从第 i 个字到句尾能得到的最大对数概率,Jieba 用动态规划从右往左计算。
  1. next(i)从位置 i 出发,所有可能词尾位置的集合。
    next(i)={j∣s[i:j+1]∈D}\mathrm{next}(i) = \{ j \mid s[i:j+1] \in \mathcal{D} \}
  2. 词概率 p一个词在语料中出现的频率估计,词越常见,得分越高。
    p(wi,j)=count(wi,j)total_countp(w_{i,j}) = \frac{\mathrm{count}(w_{i,j})}{\mathrm{total\_count}}
  3. 取对数相加连乘概率容易下溢,所以转成对数后相加。
    log⁡p(w1)+log⁡p(w2)+⋯\log p(w_1) + \log p(w_2) + \cdots
  4. 递归取最大从当前位置出发,选择能带来最大累计得分的那条边。
    score(i)=max⁡j(score(j)+log⁡p(wi,j))\mathrm{score}(i) = \max_j ( \mathrm{score}(j) + \log p(w_{i,j}) )

CRF 序列标注

小李的方法完全不同。他不再死记电话簿,而是看大量已经切好的历史包裹。对他来说,每个汉字不是“能不能组成词”的问题,而是“这个字在词语中的角色”问题。他给每个字贴一个标签:B 表示词首,M 表示词中,E 表示词尾,S 表示单字词。比如“自然语言”会被标成“自 B、然 M、语 M、言 E”。

CRF 的全称是条件随机场,它可以看作一群特征函数的投票。每个特征函数会检查“当前字是不是常用词首字”“上一个标签是不是 B”“当前字是否常见于词语末尾”等线索。模型学到的权重会告诉小李哪些线索更可信,最后找到一条整体得分最高的标签序列。

小李的“经验”不是天生的,而是训练出来的。他用大量标注语料让权重 λ 不断调整,再用维特比算法快速搜索最优标签链。代价是训练时间长、推理也比查词典慢,但换来的是对上下文和未登录词更强的判断力。

P(y∣x)=1Z(x)exp⁡(∑t=1n∑kλkfk(yt−1,yt,x,t))\displaystyle P(y|x) = \frac{1}{Z(x)} \exp \left( \sum_{t=1}^{n} \sum_{k} \lambda_k f_k(y_{t-1}, y_t, x, t) \right)
这是在计算:给定句子 x,标签序列 y 出现的概率。所有特征分数相加,再做归一化。
  1. 输入 x 和输出 yx 是句子中的每个字,y 是每个字对应的 B/M/E/S 标签。
    x=(x1,x2,…,xn),y=(y1,y2,…,yn)x = (x_1, x_2, \dots, x_n), \quad y = (y_1, y_2, \dots, y_n)
  2. 特征函数 f_k检查某个局部线索是否成立,成立记 1,不成立记 0。
    fk(yt−1,yt,x,t)∈{0,1}f_k(y_{t-1}, y_t, x, t) \in \{0, 1\}
  3. 权重 λ_k表示这条线索有多重要,来自训练过程。
    λk∈R\lambda_k \in \mathbb{R}
  4. 累加与指数化把整条路径上的得分相加,再用指数变成正数。
    exp⁡(∑t∑kλkfk)\exp \left( \sum_t \sum_k \lambda_k f_k \right)
  5. 归一化 Z(x)除以所有可能标签序列的分数和,得到概率。
    Z(x)=∑y′exp⁡(∑t∑kλkfk(yt−1′,yt′,x,t))Z(x) = \sum_{y'} \exp \left( \sum_t \sum_k \lambda_k f_k(y'_{t-1}, y'_t, x, t) \right)

双方法对比

老张和小李的差异,在真实项目中非常明显。词典法像一辆轻便摩托车,启动快、油耗低,但遇到崎岖山路容易翻车;CRF 像一辆经过调校的越野车,准备时间长、成本高,但复杂路况下更稳。

| 维度 | 词典法 / Jieba | CRF / HanLP |

|---|---|---|

| 精度 | 对常见词很好,未登录词容易碎 | 对上下文敏感,未登录词更稳 |

| 速度 | 快,无需训练 | 较慢,需要特征计算 |

| 资源 | 词典小、内存低 | 需要标注语料和训练时间 |

| 词典依赖 | 强依赖词表 | 弱依赖词表 |

| 可解释性 | 高,容易排查 | 较低,特征权重不够直观 |

| 适用场景 | 日志、搜索、实时系统 | 专业文本、长尾新词多的场景 |

具体到项目里,“Jieba 一定比 HanLP 差”是不对的。如果领域词表非常全,文本又固定,Jieba 能以极低成本获得足够好的效果;如果语料开放、新词不断出现,CRF 的上下文建模能力才有用武之地。选型不是攀比算法,而是看谁更匹配当前的路况。

工程选型调优

分拣站的负责人最终要看业务需求。如果系统要求每毫秒处理大量文本,老张的查表法显然更合适;如果面对的是医疗、法律这种专业文本,宁可慢一点,也要用小李的 CRF 减少切错。更常见的做法是两者结合:先用 Jieba 快速初切,再用 CRF 对高风险片段重切。

调优也不是盲目加词典。给 Jieba 添加领域词典,能快速提升专业词召回;给 HanLP 补充领域标注语料,能让 CRF 学到更准的上下文规律。最终效果要用 F1 和延迟共同衡量。可以设计一个最简单的选择公式:在可接受的延迟范围内,选 F1 更高的模型。

老张和小李其实可以成为搭档:让老张负责快速粗切,让小李负责处理难分的长尾片段。中文分词没有银弹,只有对任务的理解、对数据的评估,以及对工具边界的清醒认识。

choose(m)=α×F1(m)−β×Latency(m)\displaystyle \mathrm{choose}(m) = \alpha \times \mathrm{F1}(m) - \beta \times \mathrm{Latency}(m)
这是一个概念示意:α 代表你有多看重准确率,β 代表你有多痛恨延迟,最终得分最高的模型胜出。
  1. 候选模型 m可以是 Jieba,也可以是 HanLP CRF。
    m∈{Jieba,HanLP}m \in \{\mathrm{Jieba}, \mathrm{HanLP}\}
  2. F1(m)模型在验证集上的综合准确率。
    F1(m)∈[0,1]\mathrm{F1}(m) \in [0, 1]
  3. Latency(m)模型处理一段文本的平均耗时。
    Latency(m)>0\mathrm{Latency}(m) > 0
  4. 权重 α 和 β由业务场景决定,日志系统 β 大,专业文本 α 大。
    α,β>0\alpha, \beta > 0

AI 应用场景

搜索引擎与大模型预处理

阶段:数据预处理

解决的问题:搜索 query 或大模型输入需要先切词,才能构建词表、做召回或 Prompt 切块。传统方法人工维护词表,成本高且更新慢;Jieba 基于词典和最大概率路径,可以快速完成大规模初切。

具体案例:用户搜索“自然语言处理入门”,Jieba 切出“自然语言 / 处理 / 入门”,搜索系统据此召回相关文档,比按单字匹配召回更准。

专业领域的 OOV 识别

阶段:特征工程

解决的问题:医疗、法律、金融文本充满新词和长术语,词典法经常把术语切碎。HanLP 的 CRF 利用上下文特征给每个字贴标签,能更稳地识别未登录词。

具体案例:病历文本“胃窦黏膜慢性炎”,CRF 可以根据“胃窦”“黏膜”等局部特征,把专业术语整体切出,避免后续实体识别把术语拦腰截断。

实时客服系统分流

阶段:模型推理与优化

解决的问题:实时客服需要低延迟,但口语化文本又有大量新词。单独用 CRF 太慢,单独用词典法又容易漏新词。先用 Jieba 快速分流,再对未登录词密度高的句子调用 CRF 重切,能在速度和精度之间取得平衡。

具体案例:客服机器人收到“咱家冰箱怎么老有异响”,Jieba 快速切出常规词,检测到“异响”可能不在词典后,用 CRF 重新判断并加进当日热词表。

Python 演示

python63 行
# -*- coding: utf-8 -*-class DualMaxMatcher:    def __init__(self, words, max_len=5):        self._dict = set(words)        self.max_len = max_len     def forward_match(self, text):        result = []        i = 0        n = len(text)        while i < n:            match = text[i]            for end in range(min(i + self.max_len, n), i, -1):                if text[i:end] in self._dict:                    match = text[i:end]                    break            result.append(match)            i += len(match)        return result     def backward_match(self, text):        result = []        i = len(text)        while i > 0:            match = text[i - 1]            for start in range(max(0, i - self.max_len), i):                if text[start:i] in self._dict:                    match = text[start:i]                    break            result.append(match)            i -= len(match)        return list(reversed(result))     def segment(self, text):        fwd = self.forward_match(text)        bwd = self.backward_match(text)        if len(fwd) == len(bwd):            return fwd        fwd_single = sum(1 for w in fwd if len(w) == 1)        bwd_single = sum(1 for w in bwd if len(w) == 1)        if fwd_single < bwd_single:            return fwd        if bwd_single < fwd_single:            return bwd        if len(fwd) <= len(bwd):            return fwd        return bwd  def main():    words = ["自然", "语言", "自然语言", "处理", "中文", "分词",             "学习", "深度学习", "研究"]    matcher = DualMaxMatcher(words, max_len=5)    samples = ["中文分词很有意义", "自然语言处理很有趣"]    for s in samples:        print("句子:", s)        print("正向:", " / ".join(matcher.forward_match(s)))        print("逆向:", " / ".join(matcher.backward_match(s)))        print("双向:", " / ".join(matcher.segment(s)))  if __name__ == "__main__":    main()
目的:用一个极简双向最大匹配分词器,演示词典规则法从左右两个方向找最长词,并用单字词数量消解歧义的基本过程。
  1. self._dict = set(words)把词表转成集合,判断“这个词在不在词典里”只需要 O(1) 时间。
  2. for end in range(min(i + self.max_len, n), i, -1)从最长窗口开始往短尝试,体现“最长优先”的匹配思路。
  3. if text[i:end] in self._dict命中词典就立刻切出一个词,不再继续缩短窗口。
  4. return list(reversed(result))逆向是从右往左收集的,最后要反转回正常顺序。
  5. if fwd_single < bwd_single:双向结果不一致时,优先选择单字词更少的方向,因为单字过多通常意味着切碎了。

常见误区

误区我以为 Jieba 分词器的核心就是双向最大匹配,只要把词典做大,效果就能无限接近 CRF。

正解Jieba 属于词典规则流派,但它内部使用前缀词典构建 DAG,再用动态规划寻找最大概率路径,并不是简单的前后各切一次。双向最大匹配是理解词典法思想的好起点,但不能完全等同于 Jieba 的实现。

为何常见:很多教程为了降低理解难度,会把 Jieba 简化成“词典匹配”,读者因此容易把简化版当成完整原理。

误区CRF 是机器学习方法,不依赖词典,所以任何新词都能识别,比 Jieba 强很多。

正解CRF 确实对未登录词更友好,但它依赖训练语料里的上下文特征。如果某个新词在训练语料中从未以类似模式出现过,CRF 同样很难切对。词典法有词表盲区,CRF 也有经验盲区。

为何常见:人们看到“机器学习建模”就觉得更聪明,忽略了模型同样受训练数据分布限制。

误区我只要把分词 F1 从 90% 提到 95%,下游任务肯定也会提升。

正解分词准确率是重要参考,但不是最终目标。搜索、翻译、问答等下游任务对“错误类型”的敏感度不同,有时候切词方式不符合下游习惯,反而伤害效果。应该用下游任务指标来验证分词方案。

为何常见:分词是第一步,人们容易把第一步的指标当成整个流程的成败指标,陷入局部最优。

误区分词不准就拼命往 Jieba 的自定义词典里加词,觉得词表越大越安全。

正解盲目加词会让词典互相冲突,比如把“自然”和“自然语言”同时加入,某些句子反而会产生错误的最长匹配或概率路径。词典需要按领域、按频率精心维护,而不是堆数量。

为何常见:词典法看起来“词多就不容易被拆”,忽略了词与词之间还有重叠和歧义关系。

面试题

Q1. 中文分词的核心任务是什么?

单选题 · easy

  • A.给句子加标点符号
  • B.在词与词之间插入分隔符← 正确答案
  • C.把汉字转成拼音
  • D.删除句子中的语气词

答案:B。中文分词的目标是给没有空格的中文文本确定词语边界,B 最准确。加标点、转拼音、删语气词都不是分词的核心任务。

Q2. 正向最大匹配从哪里开始匹配?

单选题 · easy

  • A.从句子左侧开始选择← 正确答案
  • B.从句子右侧开始选择
  • C.从句子中间随机选择
  • D.从标点符号两侧开始

答案:A。正向最大匹配从左往右扫描,逆向最大匹配才从右往左,这是两种方法最直观的区别。

Q3. 双向最大匹配通常如何消解歧义?

单选题 · mid

  • A.直接合并两个方向结果
  • B.选择切分词语数量更少的结果← 正确答案
  • C.随机选取一个方向结果
  • D.选择单字数量更多的结果

答案:B。双向最大匹配会把正反向结果比较,优先选词数更少或单字词更少的结果。合并、随机、选单字多都不是消解歧义的正确方向。

Q4. 下列哪些属于 Jieba 分词的组成步骤?

多选题 · mid

  • A.构建前缀词典← 正确答案
  • B.生成有向无环图← 正确答案
  • C.搜索最大概率路径← 正确答案
  • D.训练句法分析器

答案:ABC。Jieba 用前缀词典快速得到每个位置能组成的词,并构建 DAG,然后用动态规划搜索最大概率路径。它不需要训练句法分析器,D 是错误选项。

Q5. CRF 中 B/M/E/S 标签里的 E 通常表示什么?

单选题 · mid

  • A.单字词
  • B.词语开头
  • C.词语中间
  • D.词语结尾← 正确答案

答案:D。B 是 Begin 词首,M 是 Middle 词中,E 是 End 词尾,S 是 Single 单字词,因此 E 表示词语结尾。

Q6. 关于 CRF 与词典匹配,哪些说法正确?

多选题 · mid

  • A.CRF 利用上下文特征← 正确答案
  • B.CRF 需要训练语料← 正确答案
  • C.词典匹配训练成本为零← 正确答案
  • D.词典匹配能彻底解决歧义

答案:ABC。CRF 的核心优势是上下文特征,也需要标注语料训练权重。词典匹配不需要训练,但无法彻底解决歧义,D 说法过于绝对。

Q7. 线性链 CRF 的总分主要由哪两部分构成?

单选题 · hard

  • A.发射分数与转移分数之和← 正确答案
  • B.词频与逆文档频率之和
  • C.词典长度与文本长度之积
  • D.字符向量的余弦相似度

答案:A。CRF 建模时既要看当前字和标签的发射关系,也要看相邻标签的转移关系,总分由这两类特征分数累加得到。

Q8. 关于双向最大匹配,哪些说法正确?

多选题 · hard

  • A.正向可能把词切碎← 正确答案
  • B.逆向适合后缀明显的语言← 正确答案
  • C.双向能完全消除歧义
  • D.双向比单向更稳定← 正确答案

答案:ABD。正向和逆向都可能切碎词,逆向对后缀明显的词更敏感。双向通过比较减少错误,但不能完全消除歧义,因此 C 不选。

Q9. 分词评估中 F1 值高代表什么?

单选题 · hard

  • A.精确率和召回率都较高← 正确答案
  • B.运行速度一定更快
  • C.内存占用一定更小
  • D.不需要词典支持

答案:A。F1 是精确率和召回率的调和平均,数值高说明两者都较高。它不直接代表速度、内存或是否依赖词典。

Q10. 哪项场景最适合选择 HanLP CRF 而不是 Jieba 词典法?

单选题 · extreme

  • A.海量日志最低延迟
  • B.少量硬件的离线任务
  • C.长尾新词多的专业文本← 正确答案
  • D.固定领域且词典极全

答案:C。CRF 的上下文建模能力对未登录词和长尾新词更友好。海量日志低延迟更适合 Jieba,离线任务和固定词典场景不一定需要 CRF。