笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLPpython
上一章中咱们实现了块儿不许的词典分词,词典分词没法消歧。给定两种分词结果“商品 和服 务”以及“商品 和 服务”,词典分词不知道哪一种更加合理。git
咱们人类确知道第二种更加合理,只由于咱们从小到大接触的都是第二种分词,出现的次数多,因此咱们断定第二种是正确地选择。这就是利用了统计天然语言处理。统计天然语言处理的核心话题之一,就是如何利用统计手法对语言建模,这一章讲的就是二元语法的统计语言模型。github
什么是语言模型算法
模型指的是对事物的数学抽象,那么语言模型指的就是对语言现象的数学抽象。准确的讲,给定一个句子 w,语言模型就是计算句子的出现几率 p(w) 的模型,而统计的对象就是人工标注而成的语料库。函数
假设构建以下的小型语料库:学习
商品 和 服务 商品 和服 物美价廉 服务 和 货币
每一个句子出现的几率都是 1/3,这就是语言模型。然而 p(w) 的计算很是难:句子数量无穷无尽,没法枚举。即使是大型语料库,也只能“枚举”有限的数百万个句子。实际遇到的句子大部分都在语料库以外,意味着它们的几率都被看成0,这种现象被称为数据稀疏。spa
句子几乎不重复,单词却一直在重复使用,因而咱们把句子表示为单词列表 \(w=w_1w_2...w_k\) ,每一个 \(w_t,t\in[1,k]\) 都是一个单词,而后定义语言模型:
\[ \begin{aligned} p(\boldsymbol{w}) &=p\left(w_{1} w_{2} \cdots w_{k}\right) \\ &=p\left(w_{1} | w_{0}\right) \times p\left(w_{2} | w_{0} w_{1}\right) \times \cdots \times p\left(w_{k+1} | w_{0} w_{1} w_{2} \dots w_{k}\right) \\ &=\prod_{t=1}^{k+1} p\left(w_{t} | w_{0} w_{1} \cdots w_{t-1}\right) \end{aligned} \]
其中,\(w_0=BOS\) (Begin Of Sentence,有时用<s>),\(w_{k+1}=EOS (End Of Sentence,有时也用</s>)\),是用来标记句子收尾的两个特殊“单词”,在NLP领域的文献和代码中常常出现。code
然而随着句子长度的增大,语言模型会遇到以下两个问题。对象
马尔可夫链与二元语法索引
为了解决以上两个问题,须要使用马尔可夫假设来简化语言模型,给定时间线上有一串事件顺序发生,假设每一个事件的发生几率只取决于前一个事件,那么这串事件构成的因果链被称做马尔可夫链。
在语言模型中,第 t 个事件指的是 \(w_t\) 做为第 t 个单词出现。也就是说,每一个单词出现的几率只取决于前一个单词:
\[p(w_t|w_0w_1...w_{t-1})=p(w_t|w_{t-1})\]
基于此假设,式子一会儿变短了很多,此时的语言模型称为二元语法模型:
\[ \begin{aligned} p(\boldsymbol{w}) &=p\left(w_{1} w_{2} \cdots w_{k}\right) \\ &=p\left(w_{1} | w_{0}\right) \times p\left(w_{2} | w_{1}\right) \times \cdots \times p\left(w_{k+1} | w_{k}\right) \\ &=\prod_{t=1}^{k+1} p\left(w_{t} | w_{t-1}\right) \end{aligned} \]
因为语料库中二元连续的重复程度要高于整个句子的重要程度,因此缓解了数据稀疏的问题,另外二元连续的总数量远远小于句子的数量,存储和查询也获得了解决。
n元语法
利用相似的思路,能够获得n元语法的定义:每一个单词的几率仅取决于该单词以前的 n 个单词:
\[ p(w)=\prod_{t=1}^{k+n-1} p\left(w_{t} | w_{t-n+1} \dots w_{t-1}\right) \]
特别地,当 n=1 时的 n 元语法称为一元语法 ( unigram);当 n=3 时的 n 元语法称为三元语法(tigam); n≥4时数据稀疏和计算代价又变得显著起来,实际工程中几乎不使用。
数据稀疏与平滑策略
对于 n 元语法模型,n 越大,数据稀疏问题越严峻。好比上述语料库中“商品 货币”的频次就为0。一个天然而然的解决方案就是利用低阶 n 元语法平滑高阶 n 元语法,所谓平滑,就是字面上的意思:使 n 元语法频次的折线平滑为曲线。最简单的一种是线性插值法:
\[ p\left(w_{t} | w_{t-1}\right)=\lambda p_{\mathrm{ML}}\left(w_{t} | w_{t-1}\right)+(1-\lambda) p\left(w_{t}\right) \]
其中,\(\lambda\in(0,1)\) 为常数平滑因子。通俗理解,线性插值就是劫富济贫的税赋制度,其中的 λ 就是我的所得税的税率。\(p_{ML}(w_t|w_{t-1})\) 是税前所得,\(p(w_t)\) 是社会福利。 经过缴税,高收人(高几率)二元语法的一部分收人 (几率)被移动到社会福利中。而零收入(语料库统计不到频次)的一元语法可以从社会福利中取得点低保金, 不至于饿死。低保金的额度与二元语法挣钱潜力成正比:二元语法中第二个词词频越高,它将来被统计到的几率也应该越高,所以它应该多拿一点。
相似地,一元语法也能够经过线性插值来平滑:
\[ p\left(w_{t}\right)=\lambda p_{\mathrm{ML}}\left(w_{t}\right)+(1-\lambda) \frac{1}{N} \]
其中,N 是语料库总词频。
语言模型只是一个函数的骨架,函数的参数须要在语料库上统计才能获得。为了知足实际工程须要,一个质量高、份量足的语料库必不可少。如下是经常使用的语料库:
语料库 | 字符数 | 词语种数 | 总词频 | 平均词长 |
---|---|---|---|---|
PKU | 183万 | 6万 | 111万 | 1.6 |
MSR | 405万 | 9万 | 237万 | 1.7 |
AS | 837万 | 14万 | 545万 | 1.5 |
CITYU | 240万 | 7万 | 146万 | 1.7 |
通常采用MSR做为分词语料的首选,有如下缘由:
训练指的是统计二元语法频次以及一元语法频次,有了频次,经过极大似然估计以及平滑策略,咱们就能够估计任意句子的几率分布,即获得了语言模型。这里以二元语法为例:
这里咱们选用上面本身构造的小型语料库:data/dictionnary/my_cws_corpus.txt
代码请见:code/ch03/ngram_segment.py
步骤以下:
加载语料库文件并进行词频统计。
对词频文件生成词网
词网指的是句子中全部一元语法构成的网状结构,是HanLP工程上的概念。好比“商品和服务”这个句子,咱们将句子中全部单词找出来,起始位置(offset)相同的单词写做一行:
0:[ ] 1:[商品] 2:[] 3:[和,和服] 4:[服务] 5:[务] 6:[ ]
其中收尾(行0和行6)分别对应起始和末尾。词网必须保证从起点出发的全部路径都会连通到钟点房。
词网有一个极佳的性质:那就是第 i 行的词语 w 与第 i+len(w) 行的全部词语相连都能构成二元语法。
词图上的维特比算法
上述词图每条边以二元语法的几率做为距离,那么中文分词任务转换为有向无环图上的最长路径问题。再经过将浮点数乘法转化为负对数之间的加法,相应的最长路径转化为负对数的最短路径。使用维特比算法求解。
这里仅做一下简述,详细过程参考书本第三章。
该模型代码输入是句子“货币和服务”,获得结果以下:
[' ', '货币', '和', '服务', ' ']
结果正确,可见咱们的二元语法模型具有必定的泛化能力。
词典每每廉价易得,资源丰富,利用统计模型的消歧能力,辅以用户词典处理新词,是提升分词器准确率的有效方式。HanLP支持 2 档用户词典优先级:
HanLP分词器简洁版:
from pyhanlp import * ViterbiSegment = SafeJClass('com.hankcs.hanlp.seg.Viterbi.ViterbiSegment') segment = ViterbiSegment() sentence = "社会摇摆简称社会摇" segment.enableCustomDictionary(False) print("不挂载词典:", segment.seg(sentence)) CustomDictionary.insert("社会摇", "nz 100") segment.enableCustomDictionary(True) print("低优先级词典:", segment.seg(sentence)) segment.enableCustomDictionaryForcing(True) print("高优先级词典:", segment.seg(sentence))
输出:
不挂载词典: [社会/n, 摇摆/v, 简称/v, 社会/n, 摇/v] 低优先级词典: [社会/n, 摇摆/v, 简称/v, 社会摇/nz] 高优先级词典: [社会摇/nz, 摆/v, 简称/v, 社会摇/nz]
可见,用户词典的高优先级未必是件好事,HanLP中的用户词典默认低优先级,作项目时请读者在理解上述说明的状况下根据实际需求自行开启高优先级。
按照NLP任务的通常流程,咱们已经完成了语料标注和模型训练,如今来比较一下二元语法和词典分词的评测:
算法 | P | R | F1 | R(oov) | R(IV) |
---|---|---|---|---|---|
最长匹配 | 89.41 | 94.64 | 91.95 | 2.58 | 97.14 |
二元语法 | 92.38 | 96.70 | 94.49 | 2.58 | 99.26 |
相较于词典分词,二元语法在精确度、召回率及IV召回率上全面胜出,最终F1值提升了 2.5%,成绩的提升主要受惠于消歧能力的提升。然而 OOV 召回依然是 n 元语法模型的硬伤,咱们须要更强大的语言模型。
HanLP何晗--《天然语言处理入门》笔记:
https://github.com/NLP-LOVE/Introduction-NLP
项目持续更新中......
目录
章节 |
---|
第 1 章:新手上路 |
第 2 章:词典分词 |
第 3 章:二元语法与中文分词 |
第 4 章:隐马尔可夫模型与序列标注 |
第 5 章:感知机分类与序列标注 |
第 6 章:条件随机场与序列标注 |
第 7 章:词性标注 |
第 8 章:命名实体识别 |
第 9 章:信息抽取 |
第 10 章:文本聚类 |
第 11 章:文本分类 |
第 12 章:依存句法分析 |
第 13 章:深度学习与天然语言处理 |