一、前言

上节介绍了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. 销售订单-修改量-高级定价关联sql

    修改量消耗明细 --修改量消耗明细 SELECT t.name, t.comments, t.version_no, cux_rebate_pub.get_hou_name(p_organizatio ...

  2. C/C++内存布局及对齐

    1.源文件转换为可执行文件 源文件经过以下几步生成可执行文件: 1.预处理(preprocessor):对#include.#define.#ifdef/#endif.#ifndef/#endif等进 ...

  3. Volley解析之表单提交篇

    要实现表单的提交,就要知道表单提交的数据格式是怎么样,这里我从某知名网站抓了一条数据,先来分析别人提交表单的数据格式.  数据包: Connection: keep-alive Content-Len ...

  4. mybatis配置开发

    以mysql为例: 一.需要的架包:mybatis.jar和mysql-connector-java.jar 二.一般会有两类配置文件:数据库配置文件和要执行的sql语句 数据库配置文件(配置文件中有 ...

  5. HI258摄像头旋转配置问题

    {0x28, 0x04}, //Full row start y-flip  {0x29, 0x01}, //Pre1 row start no-flip {0x2a, 0x02}, //Pre1 r ...

  6. obj-c中-fobjc-arc-exceptions的解释

    在开启ARC之后正常情况下一切和内存有关的申请和释放操作皆不用你关心了,ARC全全帮你包办了.但是还有极少数的情况下,编译器无法为你生成合适的ARC额外代码,比如obj-c异常就是这么一个例子. 话句 ...

  7. 云技术:负载均衡SLB

    什么是SLB? SLB是Server Load Balance(负载均衡)的简称,XX云计算有限公司提供的负载均衡服务,通过设置虚拟服务IP,将位于同一机房的多台云服务器资源虚拟成一个高性能.高可用的 ...

  8. Redis的集群配置

    如果我们redis的压力很大,如果我们的并发高到我们读数据和写数据都有了很大压力. 那么我们可能就需要把redis分开部署,并且配置为一个『主从』的状态. 在服务器上构筑Redis的集群配置: 1.切 ...

  9. AMDP + XLSX Workbench 报表开发模式

    本文介绍了我和同事通过使用AMDP + XLSX Workbench缩短报表开发周期.分离数据查询处理逻辑和前端展示工作的经验.欢迎讨论. 前言 最近接到了一套人力资源报表的开发需求,需要以EXCEL ...

  10. js中获取方法名

    var tmp = arguments.callee.toString(); var re = /function\s*(\w*)/i; var matches = re.exec(tmp);//方法 ...