python使用如下方法规范化数组_开源中文分词工具探析(三):Ansj

Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高分词准确度。虽然基本分词原理与ICTLAS的一样,但是Ansj做了一些工程上的优化,比如:用DAT高效地实现检索词典、邻接表实现分词DAG、支持自定义词典与自定义消歧义规则等。

【开源中文分词工具探析】系列:

1. 前言

Ansj支持多种分词方式,其中ToAnalysis为店长推荐款:

它在易用性,稳定性.准确性.以及分词效率上.都取得了一个不错的平衡.如果你初次尝试Ansj如果你想开箱即用.那么就用这个分词方式是不会错的.

因此,本文将主要分析ToAnalysis的分词实现(基于ansj-5.1.0版本)。

ToAnalysis继承自抽象类org.ansj.splitWord.Analysis,重写了抽象方法getResult。其中,分词方法的依赖关系:ToAnalysis::parse -> Analysis::parseStr -> Analysis::analysisStr。analysisStr方法就干了两件事:

按照消歧义规则分词;

在此基础上,按照核心词典分词;

analysisStr方法的最后调用了抽象方法getResult,用于对分词DAG的再处理;所有的分词类均重写这个方法。为了便于理解ToAnalysis分词,我用Scala 2.11重写了:

import java.util

import org.ansj.domain.{Result, Term}

import org.ansj.recognition.arrimpl.{AsianPersonRecognition, ForeignPersonRecognition, NumRecognition, UserDefineRecognition}

import org.ansj.splitWord.Analysis

import org.ansj.util.TermUtil.InsertTermType

import org.ansj.util.{Graph, NameFix}

/**

* @author rain

*/

object ToAnalysis extends Analysis {

def parse(sentence: String): Result = {

parseStr(sentence)

}

override def getResult(graph: Graph): util.List[Term] = {

// bigram分词

graph.walkPath()

// 数字发现

if (isNumRecognition && graph.hasNum)

new NumRecognition().recognition(graph.terms)

// 人名识别

if (graph.hasPerson && isNameRecognition) {

// 亚洲人名识别

new AsianPersonRecognition().recognition(graph.terms)

// 词黏结后修正打分, 求取最优路径

graph.walkPathByScore()

NameFix.nameAmbiguity(graph.terms)

// 外国人名识别

new ForeignPersonRecognition().recognition(graph.terms)

graph.walkPathByScore()

}

// 用户自定义词典的识别

new UserDefineRecognition(InsertTermType.SKIP, forests: _*).recognition(graph.terms)

graph.rmLittlePath()

graph.walkPathByScore()

import scala.collection.JavaConversions._

val terms = graph.terms

new util.ArrayList[Term](terms.take(terms.length - 1).filter(t => t != null).toSeq)

}

}

如果没看懂,没关系,且看下小节分解。

2. 分解

分词DAG

分词DAG是由类org.ansj.util.Graph实现的,主要的字段terms为org.ansj.domain.Term数组。其中,类Term为DAG的节点,字段包括:offe首字符在句子中的位置、name为词,next具有相同首字符的节点、from前驱节点、score打分。仔细看源码容易发现DAG是用邻接表(array + linked-list)方式所实现的。

Bigram模型

Bigram模型对应于一阶Markov假设,词只与其前面一个词相关,其对应的分词模型:

\begin{equation}

\arg \max \prod_{i=1}^m P(w_{i} | w_{i-1}) = \arg \min - \sum_{i=1}^m \log P(w_{i} | w_{i-1})

\label{eq:bigram}

\end{equation}

对应的词典为bigramdict.dic,格式如下:

始##始@和11

和@尚1

和@尚未1

世纪@末##末3

...

初始状态\(w_0\)对应于Bigram词典中的“始##始”,\(w_{m+1}\)对应于“末##末”。Bigram分词的实现为Graph::walkPath函数:

/**

* bigram分词

* @param relationMap 干涉性打分, key为"first_word \tab second_word", value为干涉性score值

*/

public void walkPath(Map relationMap) {

Term term = null;

// 给terms[0] bigram打分, 且前驱节点为root_term "始##始"

merger(root, 0, relationMap);

// 从第一个字符开始往后更新打分

for (int i = 0; i < terms.length; i++) {

term = terms[i];

while (term != null && term.from() != null && term != end) {

int to = term.toValue();

// 给terms[to] bigram打分, 更新最小非零score值对应的term为前驱

merger(term, to, relationMap);

term = term.next();

}

}

// 求解最短路径

optimalRoot();

}

对条件概率\(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-1})}{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=2079997\)为训练语料中的总词数,\(\lambda = \frac{1}{N}\)。上述平滑处理实现函数为MathUtil.compuScore。

求解式子\eqref{eq:bigram}的最优解等价于求解分词DAG的最短路径。Ansj采用了类似于Dijkstra的动态规划算法(作者称之为Viterbi算法)来求解最短路径。记\(G=(V,E)\)为分词DAG,其中边\((u,v) \in E\)满足如下性质:

\[v > u, \quad \forall \ (u,v) \in E

\]

即DAG顶点的序号的顺序与图流向是一致的。这个重要的性质确保了(按Graph.terms[]的index依次递增)用动态规划求解最短路径的正确性。用\(d_i\)标记源节点到节点\(i\)的最短距离,则有递推式:

\[d_i = \min_{(j,i) \in E} \ \{ d_j+ b_{(j,i)} \}

\]

其中,\(b_{(j,i)}\)为两个相邻词的条件概率的负log值-$ \log P(w_{i} | w_{j})$。上述实现请参照源码Graph::walkPath与Graph::optimalRoot。

自定义词典

Ansj支持自定义词典分词,是通过词黏结的方式——如果相邻的词黏结后正好为自定义词典中的词,则可以被分词——实现的。换句话说,如果自定义的词未能完全覆盖相邻词,则不能被分词。举个例子:

import scala.collection.JavaConversions._

val sentence = "倒模,替身算什么?钟汉良、ab《孤芳不自赏》抠图来充数"

println(ToAnalysis.parse(sentence).mkString(" "))

// 倒/v 模/ng ,/w 替身/n 算/v 什么/r ?/w 钟汉良/nr 、/w ab/en 《/w 孤芳/nr 不/d 自赏/v 》/w 抠/v 图/n 来/v 充数/v

DicLibrary.insert(DicLibrary.DEFAULT, "身算")

DicLibrary.insert(DicLibrary.DEFAULT, "抠图")

println(ToAnalysis.parse(sentence).mkString(" "))

// 倒/v 模/ng ,/w 替身/n 算/v 什么/r ?/w 钟汉良/nr 、/w ab/en 《/w 孤芳/nr 不/d 自赏/v 》/w 抠图/userDefine 来/v 充数/v

3. 参考资料

开源中文分词工具探析(四):THULAC

THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...

开源中文分词工具探析(五):FNLP

FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...

开源中文分词工具探析(五):Stanford CoreNLP

CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

开源中文分词工具探析(七):LTP

LTP是哈工大开源的一套中文语言处理系统,涵盖了基本功能:分词.词性标注.命名实体识别.依存句法分析.语义角色标注.语义依存分析等. [开源中文分词工具探析]系列: 开源中文分词工具探析(一):ICT ...

开源中文分词工具探析(六):Stanford CoreNLP

CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

中文分词工具探析(二):Jieba

1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...

中文分词工具探析(一):ICTCLAS &lpar;NLPIR&rpar;

1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...

基于开源中文分词工具pkuseg-python,我用张小龙的3万字演讲做了测试

做过搜索的同学都知道,分词的好坏直接决定了搜索的质量,在英文中分词比中文要简单,因为英文是一个个单词通过空格来划分每个词的,而中文都一个个句子,单独一个汉字没有任何意义,必须联系前后文字才能正确表达它 ...

11大Java开源中文分词器的使用方法和分词效果对比,当前几个主要的Lucene中文分词器的比较

本文的目标有两个: 1.学会使用11大Java开源中文分词器 2.对比分析11大Java开源中文分词器的分词效果 本文给出了11大Java开源中文分词的使用方法以及分词结果对比代码,至于效果哪个好,那 ...

随机推荐

获得ip地理信息的几种方法

目前已知的有腾讯.新浪.网易.搜狐和Google提供IP地址查询API,但是找得到的只有腾讯.新浪和网易的,Google的貌似要用Google Maps所以没有研究.看了下国内的几个腾讯提供的是Jav ...

&lbrack;JS&rsqb; 使用RequireJS引用UMeditor

UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码. 而UMeditor则是UEditor删减版. ...

yaourt&colon; a pacman frontend&lpar;pacman前端,翻译&rpar;

yaourt: 一个pacman前端 本文翻译自:https://archlinux.fr/yaourt-en 1 juin 2007 - admin 关于 简介 获取 示例 截图 链接 关于 Yao ...

WPF Image控件使用本地图片

BitmapImage bi = new BitmapImage(); // BitmapImage.UriSource must be in a BeginInit/EndInit block. b ...

CSS3与页面布局学习总结(三)——BFC、定位、浮动、垂直居中

一.BFC与IFC 1.1.BFC与IFC概要 BFC(Block Formatting Context)即“块级格式化上下文”, IFC(Inline Formatting Context)即行内格 ...

jquery上传控件个人使用

转了一篇jquery的上传控件使用博文,但是,经过测试貌似不行,自己研究了一下,效果实现.记下,以后使用. 下载“Uploadify”,官方版本为php的,很多文件不需要,删除带.php的文件. &l ...

django auth permission

django 提供内置view处理登陆和退出. 查看django.contrib.auth源码,主要查看三个函数authenticate,login,logout. authenticate(requ ...

未能正确加载&OpenCurlyDoubleQuote;EditorPackage”包(转)

打开vs2012加载项目的时候报如下的错误: 未能正确加载“Microsoft.VisualStudio.Editor.Implementation.EditorPackage”包.此问题可能是由配置 ...

【Ruby】【遇到的问题】

1 Error fetching https://gems.ruby-china.org/: certificate verify failed (https://gems.ruby-china.or ...

MySQL-查缺补漏

MySQL show create table tablename #查看表设计代码数据 show create table tablename/G #同上 desc tablename # 查看表数 ...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值