一、前言

上节介绍了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. PS 图像调整算法——色调分离

    色调分离的原理就是将R, G, B每个通道 0-255 的色调区间进行强制划分到给定的区间里去,所以色调会合并,最终的图像看起来颜色就是一块一块的. clc; clear all; close all ...

  2. Mina源码阅读笔记(四)—Mina的连接IoConnector2

    接着Mina源码阅读笔记(四)-Mina的连接IoConnector1,,我们继续: AbstractIoAcceptor: 001 package org.apache.mina.core.rewr ...

  3. linux下如何查询未知库所依赖的包

    经常会遇到linux下安装软件时提示少文件,如何知道所缺少的文件属于哪个包?用什么命令查看? 例如:/lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录 ...

  4. cocos2dx 跨平台编译遇到的几个问题

    首先声明一下自己用的版本 vs2010   cocos2d-x_2.2    ndk_r9 1. 安装cygwin之后,也设置好了 ndk_root, 但是 cd $NDK_ROOT 进入不了, 只好 ...

  5. 恶补web之二:css知识(1)

    css指层叠样式表(Cascading Style Sheets)     样式定义如何显示html元素,样式通常存储在样式表里.把样式添加到html4.0中,是为了解决内容与表现分离的问题.外部样式 ...

  6. python JoinableQueue在生产者消费者项目中的简单应用

    class multiprocessing.JoinableQueue([maxsize]) JoinableQueue, a Queue subclass, is a queue which add ...

  7. 二、添加 Insert into

    文档目录 开始使用  初始化查询实例: LambdaToSql.SqlClient DB = new LambdaToSql.SqlClient(); 添加实体数据 ", IP = &quo ...

  8. java核心卷轴之集合

    1. Iterator 1.1 注意事项 接口的remove方法将删除上次调用next方式时返回的对象,即:remove之前,必须有next(先获取,再删除). 1.2 例一:删除字符串集合中的第一个 ...

  9. 7. Selenium的基本使用

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 32.0px "PingFang SC" } span.s1 { font: 32.0p ...

  10. JS基础:闭包和作用域链

    简介 一个定义在函数内部的函数与包含它的外部函数构成了闭包,内部函数可以访问外部函数的变量,这些变量将一直保存在内存中,直到无法再引用这个内部函数. 例如: var a = 0; function o ...