一 序
本文分为两大部分,主要是NLP读书笔记,附带一段项目实际使用hanlp的策略。
还要做搜索,因此调研了下常见的NLP工具,看了下何晗《自然语言处理入门》
二 NLP自然语言处理入门
1. 新手上路
主要是围绕这些过程来实现。注意对于常见的应用搜索来说:NLP侧重于查询的理解,而不是信息检索。
历史的发展过程:基于规则的专家系统、基于统计( 语料库 )、机器学习
2. 词典分词
- 中文分词:指的是将一段文本拆分为一系列单词的过程,这些单词顺序拼接后等于原文本。
- 中文分词算法大致分为基于词典规则与基于机器学习这两大派。
切分算法
完全切分、正向最长匹配、逆向最长匹配、双向最长匹配
匹配算法的瓶颈之一在于如何判断集合(词典)中是否含有字符串。如果用有序集合TreeMap)的话,复杂度是o(logn) ( n是词典大小);如果用散列表( Java的HashMap. Python的dict )的话,账面上的时间复杂度虽然下降了,但内存复杂度却上去了。有没有速度又快、内存又省的数据结构呢?这就是字典树。
字典树:字符串集合常用宇典树(trie树、前缀树)存储,这是一种字符串上的树形数据结构。字典树中每条边都对应一个字, 从根节点往下的路径构成一个个字符串。字典树并不直接在节点上存储字符串, 而是将词语视作根节点到某节点之间的一条路径,并在终点节点(蓝色) 上做个标记“该节点对应词语的结尾”。字符串就是一 条路径,要查询一个单词,只需顺着这条路径从根节点往下走。如果能走到特殊标记的节点,则说明该字符串在集合中,否则说明不存在。
HanLP的词典分词实现
-
DoubleArrayTrieSegment
DoubleArrayTrieSegment分词器是对DAT最长匹配的封装,默认加载hanlp.properties中CoreDictionaryPath制定的词典。
其余参照git 来看,分词方式很多,ansj也是基于tire树的。
去掉停用词
3. 二元语法与中文分词
上一章是标准分词,对于给定两种分词结果“商品 和服 务”以及“商品 和 服务”,词典分词不知道哪种更加合理。我们人类确知道第二种更加合理,只因为我们从小到大接触的都是第二种分词,出现的次数多,所以我们判定第二种是正确地选择。这就是利用了统计自然语言处理。统计自然语言处理的核心话题之一,就是如何利用统计手法对语言建模,这一章讲的就是二元语法的统计语言模型。
3.1 语言模型
-
什么是语言模型
模型指的是对事物的数学抽象,那么语言模型指的就是对语言现象的数学抽象。准确的讲,给定一个句子 w,语言模型就是计算句子的出现概率 p(w) 的模型,而统计的对象就是人工标注而成的语料库。
然而 p(w) 的计算非常难:句子数量无穷无尽,无法枚举。即便是大型语料库。实际遇到的句子大部分都在语料库之外,意味着它们的概率都被当作0,这种现象被称为数据稀疏。
句子几乎不重复,单词却一直在重复。我们吧句子表示为单词列表w= w1*w2*....wk.
然而随着句子长度的增大,语言模型会遇到如下两个问题。
1 数据稀疏,指的是长度越大的句子越难出现,可能统计不到频次,导致
p(wk|w1w2...wk-1)=0,比如 p(商品 和 货币)=0。
2计算代价大,k 越大,需要存储的 p 就越多,即便用上字典树索引,依然代价不菲。
马尔可夫链与二元语法
为了解决以上两个问题,需要使用马尔可夫假设来简化语言模型,给定时间线上有一串事件顺序发生,假设每个事件的发生概率只取决于前一个事件,那么这串事件构成的因果链被称作马尔可夫链。
在语言模型中,第 t 个事件指的是 Wt 作为第 t 个单词出现。也就是说,每个单词出现的概率只取决于前一个单词:
P(Wt|W0W1...Wt-1)=P(Wt|Wt-1)
基于此假设,式子一下子变短了不少,此时的语言模型称为二元语法模型:
由于语料库中二元连续的重复程度要高于整个句子的重要程度,所以缓解了数据稀疏的问题,另外二元连续的总数量远远小于句子的数量,存储和查询也得到了解决。
n元语法
利用类似的思路,可以得到n元语法的定义:每个单词的概率仅取决于该单词之前的 n 个单词:公式不贴了。
3.2 中文分词语料库
语言模型只是一个函数的骨架,函数的参数需要在语料库上统计才能得到。为了满足实际工程需要,一个质量高、分量足的语料库必不可少。以下是常用的语料库:
- 《人民日报》语料库 PKU
3.3 训练与预测
训练指的是统计二元语法频次以及一元语法频次,有了频次,通过极大似然估计以及平滑策略,我们就可以估计任意句子的概率分布,即得到了语言模型
3.4 HanLP分词与用户词典的集成
词典往往廉价易得,资源丰富,利用统计模型的消歧能力,辅以用户词典处理新词,是提高分词器准确率的有效方式。HanLP支持 2 档用户词典优先级:
- 低优先级:分词器首先在不考虑用户词典的情况下由统计模型预测分词结果,最后将该结果按照用户词典合并。默认低优先级。
- 高优先级:分词器优先考虑用户词典,但具体实现由分词器子类自行决定
3.5 二元语法与词典分词比较
按照NLP任务的一般流程,我们已经完成了语料标注和模型训练。
相较于词典分词,二元语法在精确度、召回率及IV召回率上全面胜出,最终F1值提高了 2.5%,成绩的提高主要受惠于消歧能力的提高。
4. 隐马尔可夫模型与序列标注
要是说上一章还勉强看的话,本章直接就看不下去了。简单说下要点:
先说问题:第3章的n元语法模型从词语接续的流畅度出发,为全切分词网中的二元接续打分,进而利用维特比算法求解似然概率最大的路径。这种词语级别的模型无法应对 OOV(Out of Vocabulary,即未登录词) 问题: 00V在最初的全切分阶段就已经不可能进人词网了,更何谈召回。
例如下面一句:
头上戴着束发嵌宝紫金冠,齐眉勒着二龙抢珠金抹额
加粗的就是相对陌生的新词,之前的分词算法识别不出,但人类确可以,是因为读者能够识别“戴着”,这些构词法能让人类拥有动态组词的能力。我们需要更细粒度的模型,比词语更细粒度的就是字符。
一般而言,由字构词是序列标注模型的一种应用。 在所有“序列标注”模型中,隐马尔可夫模型是最基础的一种。
4.1 序列标注问题
序列标注指的是给定一个序列 X=X1X2...Xn,找出序列中每个元素对应标签 Y=Y1Y2...Yn 的问题。其中,y 所有可能的取值集合称为标注集。
相关的应用有:
序列标注与中文分词
分词标注集并非只有一种,为了捕捉汉字分别作为词语收尾(Begin、End)、词中(Middle)以及单字成词(Single)时不同的成词概率,人们提出了{B,M,E,S}这种最流行的标注集。
序列标注与词性标注
词性标注任务是一个天然的序列标注问题:x 是单词序列,y 是相应的词性序列。需要综合考虑前后的单词与词性才能决定当前单词的词.
序列标注与命名实体识别
所谓命名实体,指的是现实存在的实体,比如人名、地名和机构名,命名实体是 OOV 的主要组成部分。
总之,序列标注问题是NLP中最常见的问题之一。许多应用任务都可以变换思路,转化为序列标注来解决。所以一个准确的序列标注模型非常重要,直接关系到NLP系统的准确率。机器学习领域为NLP提供了许多标注模型,本着循序渐进的原则,本章介绍其中最基础的一个隐马尔可夫模型。
4.2 隐马尔可夫模型
隐马尔可夫模型( Hidden Markov Model, HMM)是描述两个时序序列联合分布 p(x,y) 的概率模型: x 序列外界可见(外界指的是观测者),称为观测序列(obsevation sequence); y 序列外界不可见,称为状态序列(state sequence)。比如观测 x 为单词,状态 y 为词性,我们需要根据单词序列去猜测它们的词性。隐马尔可夫模型之所以称为“隐”,是因为从外界来看,状 态序列(例如词性)隐藏不可见,是待求的因变量。从这个角度来讲,人们也称状态为隐状态(hidden state),而称观测为显状态( visible state)。隐马尔可夫模型之所以称为“马尔可夫模型”,”是因为它满足马尔可夫假设。
-
从马尔可夫假设到隐马尔可夫模型
马尔可夫假设:每个事件的发生概率只取决于前一个事件。
马尔可夫链:将满足马尔可夫假设的连续多个事件串联起来,就构成了马尔可夫链。
如果把事件具象为单词,那么马尔可夫模型就具象为二元语法模型。
隐马尔可夫模型:它的马尔可夫假设作用于状态序列,
假设 ① 当前状态 Yt 仅仅依赖于前一个状态 Yt-1, 连续多个状态构成隐马尔可夫链 y。有了隐马尔可夫链,如何与观测序列 x 建立联系呢?
隐马尔可夫模型做了第二个假设: ② 任意时刻的观测 x 只依赖于该时刻的状态 Yt,与其他时刻的状态或观测独立无关。如果用箭头表示事件的依赖关系(箭头终点是结果,依赖于起点的因缘),则隐马尔可夫模型可以表示为下图所示
状态与观测之间的依赖关系确定之后,隐马尔可夫模型利用三个要素来模拟时序序列的发生过程----即初始状态概率向量、状态转移概率矩阵和发射概率矩阵。
这些先不整理了,完全看不懂。
隐马尔可夫模型的三个基本用法
- 样本生成问题:给定模型,如何有效计算产生观测序列的概率?换言之,如何评估模型与观测序列之间的匹配程度?
- 序列预测问题:给定模型和观测序列,如何找到与此观测序列最匹配的状态序列?换言之,如何根据观测序列推断出隐藏的模型状态?
- 模型训练问题:给定观测序列,如何调整模型参数使得该序列出现的概率最大?换言之,如何训练模型使其能最好地描述观测数据?
前两个问题是模式识别的问题:1) 根据隐马尔科夫模型得到一个可观察状态序列的概率(评价);2) 找到一个隐藏状态的序列使得这个序列产生一个可观察状态序列的概率最大(解码)。第三个问题就是根据一个可以观察到的状态序列集产生一个隐马尔科夫模型(学习)。
****************************
分词问题:
hanlp 看到这里,已经理解了它是基于统计的,即使使用自定义词典高优先级也不一定按照自己想要的分词结果。
Segment shortestSegment = HanLP.newSegment();
CustomDictionary.insert("面肌痉挛","nz 100");
CustomDictionary.insert("三叉神经");
shortestSegment.enableCustomDictionaryForcing(true);
System.out.println(shortestSegment.seg("三叉神经痛和面肌痉挛"));
输出:[三叉/b, 神/n, 经痛/nz, 和面/v, 肌痉挛/nz]
所以分词还需要自行评估是否完全依赖于hanlp。
另外,关hanlp 看了git的首页、wiki\FAQ .已经确认过。
自定义词典动态插入的,不会持久化到词典文件中。项目实际中肯定涉及这个方面,
*****************
关于HMM,关于解码的一个应用场景是文本纠错,如果能熟悉这块,我想尝试一下。