Jiayan分词核心原理详解:字符级HMM+kenlm语言模型如何实现无词典文言文分词
【免费下载链接】Jiayan甲言,专注于古代汉语(古汉语/古文/文言文/文言)处理的NLP工具包,支持文言词库构建、分词、词性标注、断句和标点。Jiayan, the 1st NLP toolkit designed for Classical Chinese, supports lexicon construction, tokenizing, POS tagging, sentence segmentation and punctuation.项目地址: https://gitcode.com/gh_mirrors/ji/Jiayan
Jiayan(甲言)是专注古代汉语(文言文)处理的NLP工具包。本文将拆解它最核心的能力——文言文分词:在没有分词语料、没有现成词库的情况下,如何用「字符级隐马尔可夫模型(HMM)+ kenlm 语言模型」把一句古文自动切成词,并对比通用分词工具为何在文言文上"失灵"。
一、通用分词工具为什么切不好文言文?
主流汉语 NLP 工具(如 LTP、HanLP)都是在现代汉语语料上训练的。古文与今汉语法差异大、词汇体系不同,直接套用就会出现明显"切偏"。
以《庄子·天下篇》这句为例:
是故内圣外王之道,暗而不明,郁而不发,天下之人各为其所欲焉以自为方。
| 工具 | 分词结果(节选) |
|---|---|
| Jiayan | 是 / 故 / 内圣外王 / 之 / 道…天下 / 之 / 人 / 各 / 为 / 其 / 所 / 欲 / 焉 / 以 / 自 / 为 / 方 |
| LTP(现代模型) | 是 / 故内 / 圣外王 / 之 / 道…各 / 为 / 其 / 所 / 欲 / 焉以自为方 |
| HanLP(现代模型) | 是故 / 内 / 圣 / 外 / 王之道…各为其所欲焉 / 以 / 自为 / 方 |
可以看到:现代工具会把故内、圣外王、焉以自为方这类古文中根本不存在的词切出来。这正是 Jiayan 要解决的问题。
二、Jiayan 的两条分词技术路线
Jiayan 内置两种分词器,对应两种思路:
| 分词器 | 原理 | 是否需要词典 | 适用场景 |
|---|---|---|---|
CharHMMTokenizer(推荐) | 字符级 HMM + kenlm 语言模型 | ❌ 无词典 | 古文长文、无词库语料 |
WordNgramTokenizer | 词典 + 有向无环词图(DAG)最大概率路径 | ✅ 需要词库 | 有自建词库时的精细切分 |
核心源码位置:
- 字符级 HMM 分词器:
jiayan/tokenizer/hmm_tokenizer.py - 词级最大概率路径分词器:
jiayan/tokenizer/ngram_tokenizer.py - 文言词库(供第二种方式使用):
jiayan/data/dict.txt - 配套词库自动构建(PMI+邻接熵):
jiayan/lexicon/pmi_entropy_constructor.py
本文重点讲第一种——它是最"硬核"的部分,也是"无词典文言文分词"的关键。
三、字符级 HMM:把"分词"变成"给每个字打标签"
1. 问题转化:字级标注代替词级切分
传统分词需要知道"哪些字组成一个词",而这正依赖词典。Jiayan 换了个角度:
不直接找词,而是判断每个字处于词的哪个位置。
假设最长词为 4 个字,那么每个字只可能有 4 种"角色":
| 标签 | 含义 | 概率计算(N元语法) |
|---|---|---|
b | 词首(第1字) | p(当前字) |
c | 词内第2字 | p(当前字 | 前1字) |
d | 词内第3字 | p(当前字 | 前2字) |
e | 词内第4字 | p(当前字 | 前3字) |
这样,"词级切分"就被拆解成了"字符级序列标注"——一个标准的 HMM 解码问题:给定一串字(观测序列),求最可能的一串标签(隐藏状态序列),再按b的位置断开切词即可。
2. 概率从哪来:kenlm 语言模型充当"发射概率"
普通 HMM 分词器需要大量标注语料来统计转移概率和发射概率,而古汉语没有公开的分词标注数据。Jiayan 的解法是用一个字符级 N 元语言模型(kenlm 格式,文件为jiayan.klm)来估计发射概率。
模型里有一个巧妙的"差分"技巧(见seg_prob方法):
- 想让字
k处于"词内第2字"位置,就查lm.score(前一字+当前字)减去lm.score(前一字)的差值; - 位置越靠后,参考的前文越长,最长回看 3 个字。
语言模型本身是从古文语料按字符切分后训练的(构建脚本在jiayan/utils.py的make_kenlm中),因此它"天生懂古文",能给出符合文言语感的局部搭配概率。
3. 转移概率手动设定:鼓励"短词优先"
HMM 三要素中,初始概率、发射概率都能算出来,唯独状态转移概率没有数据可统计。Jiayan 直接手动调参:
| 转移 | 概率 | 设计意图 |
|---|---|---|
| b→b | 0.85 | 词首之后大概率又是一个新词的开头 |
| b→c | 0.15 | 小概率继续成词 |
| c→b / c→d | 0.9925 / 0.0075 | 双字词远多于三字词 |
| d→b / d→e | 0.999 / 0.001 | 三字词已较少 |
| e→b / e→e | 0.9999 / 0.0001 | 四字词最少,但允许存在 |
规律很清晰:越往后越倾向跳回b(开新词),从而让分词结果自然偏向双字词为主——这恰好符合汉语(含文言)以二字词为主体的规律,也是作者反复微调出的"语感参数"。
4. Viterbi 解码 + 虚字兜底
- 用 Viterbi 算法(见
viterbi方法)找出整句联合概率最大的标签序列,按标签切出候选词; - 切出的词还要过一道"虚字校验":如果词里含
之、而、以、于、为、者、所等文言虚字(定义在jiayan/globals.py的stopchars中,共 50 余个),判定不是合法词,退回逐字输出,避免之于、所以之类被硬拼成词。
四、快速上手:3 步跑通文言文分词
1️⃣ 安装(两步,确保 kenlm 版本最新):
pip install jiayan pip install https://github.com/kpu/kenlm/archive/master.zip2️⃣ 下载预训练语言模型jiayan.klm(项目资料中提供,用于分词及句读、标点特征提取)。
3️⃣ 调用分词器(示例代码完整收录于jiayan/examples.py):
from jiayan import load_lm from jiayan import CharHMMTokenizer lm = load_lm('jiayan.klm') tokenizer = CharHMMTokenizer(lm) text = '是故内圣外王之道,暗而不明,郁而不发,天下之人各为其所欲焉以自为方。' print(list(tokenizer.tokenize(text)))输出:
['是', '故', '内圣外王', '之', '道', ',', '暗', '而', '不', '明', ',', '郁', '而', '不', '发', ',', '天下', '之', '人', '各', '为', '其', '所', '欲', '焉', '以', '自', '为', '方', '。']内圣外王、天下这类文言实词被准确切出,单字虚词自然独立——效果完全符合阅读古文的语感。
💡 如果后续用PMIEntropyLexiconConstructor(词库构建功能)从语料中自动产出了文言词库,也可以切换到WordNgramTokenizer:它参考了业界经典的 DAG 动态规划方案,在词图上用最大概率路径切分,适合需要固定词表、控制切分颗粒度的场景。
五、原理要点小结与常见疑问
📌为什么不用 BMES(B/M/E/S)标签?M(词中)语义模糊,无法精确定位字在词内的位置,发射概率难以用 N 元模型计算;b/c/d/e四标签一一对应词内位置,天然适配语言模型查表。
📌为什么"无监督"反而是优势?这套流程(HMM 分词 → 统计词频 → 构建词库)本身就是 Jiayan 生成文言词语料的手段:先分词建库,库再反哺词级分词与词性标注,形成闭环。
📌已知局限(源码注释中明确列出):
- 速度较慢:训练/统计阶段需多次遍历语料,Viterbi 逐句解码;
- 长词(四字及以上)效果弱于短词,需人工调转移概率平衡词长;
- 语料为分词产物,词频存在"假频",故词库构建时改用 N 元模型中的真实共现频次过滤。
📌支持范围:受训练语料限制,当前版本仅支持简体文言文输入。
六、写在最后
Jiayan 的分词方案,本质是用"语言模型补数据、手工参数补统计、HMM 补结构"三个替换,绕开了"古汉语没有标注语料和现成词库"这一最大障碍。理解了这个思路,你也能把它迁移到其他低资源语言的文本处理中。配合同工具包的词性标注、文言句读(断句)、自动标点功能,一套完整的古文处理流水线就此成型。
【免费下载链接】Jiayan甲言,专注于古代汉语(古汉语/古文/文言文/文言)处理的NLP工具包,支持文言词库构建、分词、词性标注、断句和标点。Jiayan, the 1st NLP toolkit designed for Classical Chinese, supports lexicon construction, tokenizing, POS tagging, sentence segmentation and punctuation.项目地址: https://gitcode.com/gh_mirrors/ji/Jiayan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考