一、前言

上节介绍了ansj的原子切分和全切分。切分完成之后,就要构建最短路径,得到分词结果。
以“商品和服务”为例,调用ansj的标准分词:
String str = "商品和服务" ;
Result result = ToAnalysis.parse(str);
System.out.println(result.getTerms());
先不管数字发现、人名识别、用户自定义词典的识别,暂时只考虑ToAnalysis类里面,构建最短路径的这行代码:
graph.walkPath();
上面这行代码执行前,已完成了全切分,构建了如下的有向无环图:

事实上,此时没有“务”这个节点

如上图所示,terms[4] = null。
不过这也没关系,后面给节点打分时,会填充这个null,这段代码位于Graph.merger(Term fromTerm, int to, Map<String, Double> relationMap):
char c = chars[to];
TermNatures tn = DATDictionary.getItem(c).termNatures;
if (tn == null || tn == TermNatures.NULL) {
tn = TermNatures.NULL;
}
terms[to] = new Term(String.valueOf(c), to, tn);
也就是说,给“和服”的后继节点打分时,发现其后继节点为null,那么就实例化一个Term,填充在terms[to]的位置。

二、理论基础

两个节点之间分之计算的代码位于MathUtil.compuScore(Term from, Term to, Map<String, Double> relationMap)
其中核心代码只有一行:
double value = -Math.log(dSmoothingPara * frequency / (MAX_FREQUENCE + 80000) + (1 - dSmoothingPara) * ((1 - dTemp) * nTwoWordsFreq / frequency + dTemp));
我们了探讨一下这行代码的理论基础。
首先,ansj使用二元语法模型(Bigram)进行分词。Bigram模型对应于一阶Markov假设,词只与其前面一个词相关,其对应的分词模型:
$arg\,max\prod_{m}^{i=1}P({w}_{i}|{w}_{i-1})\, =\,arg\,min-\sum_{m}^{i=1}logP({w}_{i}|{w}_{i-1})$
该等式将求解最大联合概率的问题转化为了求解有向无环图最短路径问题。
其中,数学符号arg表示使目标函数取最小值时的变量值。这里是指求解条件概率之积$\prod_{m}^{i=1}P({w}_{i}|{w}_{i-1})$取最大值时的分词结果。
对条件概率$P({w}_{i}|{w}_{i-1})$做如下的平滑处理:

\begin{aligned}
- \log P(w_{i} | w_{i-1}) & \approx - \log \left[ aP(w_{i-1}) + (1-a) P(w_{i}|w_{i-1}) \right] \\
& \approx - \log \left[ a\frac{f(w_i)}{N} + (1-a) \left( \frac{(1-\lambda)f(w_{i-1},w_i)}{f(w_{i-1})} + \lambda \right) \right]
\end{aligned}

其中,a = 0.1为平滑因子,N = 207997为训练语料中的总次数,$\lambda \,=\,\frac{1}{N}$。
第一个约等式是采用线性插值法(Linear Interpolation)(可参考自然语言处理:盘点一下数据平滑算法)进行平滑处理。
第二个约等式,我还没搞清楚是什么处理。

三、具体打分流程如下

代码位于Graph.walkPath(Map<String, Double> relationMap)。
Ansj采用了类似于Dijkstra的动态规划算法(作者称之为Viterbi算法)来求解最短路径。
如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点,那么(Vi...Vk)也必定是从i到k的最短路径。(可参考Dijkstra算法
1、从起始节点“始##始”开始,对其后继节点打分

设置“商”、“商品”的前驱节点(也就是Term类的from属性)为“始##始”。
2、计算“商”后继节点的分值

只有一个后继节点“品”。“商”和“品”的分值是13.509,因此从“始##始”到“品”的分值是19.56。
设置“品”的前驱节点为“商”。
3、计算“商品”后继节点分值

设置“和”、“和服”的前驱节点为“商品”。
4、计算“品”后继节点分值

以“和”为例,“和”有“商品”、“品”两个前驱节点。应该取分值最小的那个。因此,“和”的分值依然是8.92,前驱节点依然是“商品”。
同理,“和服”的前驱节点依然是“商品”。
对上图进行简化:

5、计算“和”后继节点分值

设置“服”、“服务”的前驱节点为“和”。
6、计算“和服”后继节点分值

设置“务”的前驱节点为“和服”。
对上图简化:

7、计算“服”后继节点分值

“务”以“服”为前驱,可以得到更小的分值。因此,更改“务”的前驱节点为“服”。
对上图简化:

8、计算“服务”后继节点分值

设置“末##末”的前驱节点为“服务”。
9、计算“务”后继节点分值

“末##末”以“服务”为前驱节点,分值更新。因此,“末##末”的前驱节点依然是“服务”。
对上图简化:

10、设置后继节点
目前已构建了最短路径,并且知道了每个节点的前驱节点。
例如,“末##末”的前驱节点是“服务”。但是并没有将“服务”的后继节点(也就是Term类的to属性)设置为“末##末”。
Graph.optimalRoot()就是设置后继节点的。执行完该方法后,terms被简化为了如下形式:

去掉null,就是分词结果了。

参考资料

ansj构造最短路径的更多相关文章

  1. ansj人名识别

    1.前言 ansj人名识别会用到两个字典,分别是:person/asian_name_freq.data.person/person.dic. 1.1.asian_name_freq.data 这是一 ...

  2. 关于Floyd-Warshall算法由前趋矩阵计算出的最短路径反映出了算法的执行过程特性的证明

    引言:Floyd-Warshall算法作为经典的动态规划算法,能够在O(n3)复杂度之内计算出所有点对之间的最短路径,且由于其常数较小,对于中等规模数据运行效率依然可观.算法共使用n此迭代,n为顶点个 ...

  3. 最短路径树:Dijstra算法

    一.背景 全文根据<算法-第四版>,Dijkstra算法.我们把问题抽象为2步:1.数据结构抽象   2.实现 二.算法分析 2.1 数据结构 顶点+边->图.注意:Dijkstra ...

  4. cocos2d-js版本A*算法

    [转]http://blog.csdn.net/realcrazysun1/article/details/43054229 A*算法的东西网上讲了很多~但还是不可避免的要去研究一下,cocos官网上 ...

  5. OSPF详解

    OSPF 详解 (1) [此博文包含图片] (2013-02-04 18:02:33) 转载 ▼ 标签: 端的 第二 以太 第一个 正在 目录 序言 初学乍练 循序渐进学习OSPF 朱皓 入门之前 了 ...

  6. 树链剖分-点的分治(点数为k且距离最长的点对)

    hdu4871 Shortest-path tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 130712/130712 K ( ...

  7. LeetCode--064--最小路径和

    给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小. 说明:每次只能向下或者向右移动一步. 示例: 输入:[  [1,3,1], [1,5,1], ...

  8. Dijkstra算法构造单源点最短路径

    迪杰斯特拉(Dijkstra)算法 是求从某个源点到其余各顶点的最短路径,即对已知图 G=(V,E),给定源顶点 s∈V,找出 s 到图中其它各顶点的最短路径. 我总结下核心算法,伪代码如下: Dij ...

  9. BZOJ 4016 最短路径树问题 最短路径树构造+点分治

    题目: BZOJ4016最短路径树问题 分析: 大家都说这是一道强行拼出来的题,属于是两种算法的模板题. 我们用dijkstra算法算出1为源点的最短路数组,然后遍历一下建出最短路树. 之后就是裸的点 ...

随机推荐

  1. Android JNI 使用的数据结构JNINativeMethod详解

    Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数.其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值.这 ...

  2. ITU-T Technical Paper: QoS的构建模块与机制

    本文翻译自ITU-T的Technical Paper:<How to increase QoS/QoE of IP-based platform(s) to regionally agreed ...

  3. Hadoop 的 TotalOrderPartitioner

    Partition所处的位置 Partition位置 Partition主要作用就是将map的结果发送到相应的reduce.这就对partition有两个要求: 1)均衡负载,尽量的将工作均匀的分配给 ...

  4. 【Linux 操作系统】阿里云服务器 操作实战 部署C语言开发环境(vim配置,gcc) 部署J2EE网站(jdk,tomcat)

    . 作者 :万境绝尘  转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835 . 博客总结 : 设置SecureCRT ...

  5. cocos2D v3.x 中action的回调block变化

    cocos2D v2.x中有带参数的回调block: id blk = [CCCallBlockN actionWithBlock:^(CCNode *node){ node.position = o ...

  6. Android群英传笔记——第三章:Android控件架构与自定义控件讲解

    Android群英传笔记--第三章:Android控件架构与自定义控件讲解 真的很久没有更新博客了,三四天了吧,搬家干嘛的,心累,事件又很紧,抽时间把第三章大致的看完了,当然,我还是有一点View的基 ...

  7. UnityEditor下文件操作方法汇总(Unity3D开发之二十四)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/50595585 ...

  8. Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!

    Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...

  9. 解决Android SDK Manager更新失败问题

    from:http://www.ztyhome.com/android-sdk-update/ 问题描述: 使用SDK Manager更新时无法完成更新ADT时无法解析https://dl-ssl.g ...

  10. es6(五):class关键字(extends,super,static)

    ES5中,生成对象通过构造函数: function A(name,age){ this.name=name; this.age=age } // 在A的prototype属性上定义一个test方法,即 ...