Ansj分词

这是一个基于n-Gram+条件随机场模型的中文分词的java实现.

分词速度达到每秒钟大约200万字左右(mac air下测试),准确率能达到96%以上

目前实现了.中文分词. 中文姓名识别 . 用户自定义词典

可以应用到自然语言处理等方面,适用于对分词效果要求高的各种项目.

源码:https://github.com/NLPchina/ansj_seg


四种分词模式

基本分词是什么

基本就是保证了最基本的分词.词语颗粒度最非常小的.所涉及到的词大约是10万左右.

基本分词速度非常快.在macAir上.能到每秒300w字每秒.同时准确率也很高.但是对于新词他的功能十分有限

基本分词具有什么功能

用户自定义词典 数字识别 人名识别 机构名识别 新词发现
Χ Χ Χ Χ

精准分词是什么

        精准分词是Ansj分词的推荐款

        它在易用性,稳定性.准确性.以及分词效率上.都取得了一个不错的平衡.

        如果你初次赏识Ansj如果你想开箱即用.那么就用这个分词方式是不会错的.

精准分词具有什么功能
用户自定义词典 数字识别 人名识别 机构名识别 新词发现
Χ Χ

NLP分词是什么

        nlp分词是总能给你惊喜的一种分词方式.

        它可以识别出未登录词.但是它也有它的缺点.速度比较慢.稳定性差.ps:我这里说的慢仅仅是和自己的其他方式比较.应该是40w字每秒的速度吧.

        个人觉得nlp的适用方式.1.语法实体名抽取.未登录词整理.只要是对文本进行发现分析等工作

NLP分词具有什么功能
用户自定义词典 数字识别 人名识别 机构名识别 新词发现

面向索引的分词是什么

面向索引的分词。故名思议就是适合在lucene等文本检索中用到的分词。 主要考虑以下两点

  • 召回率
    • 召回率是对分词结果尽可能的涵盖。比如对“上海虹桥机场南路” 召回结果是[上海/ns, 上海虹桥机场/nt, 虹桥/ns, 虹桥机场/nz, 机场/n, 南路/nr]
  • 准确率
    • 其实这和召回本身是具有一定矛盾性的Ansj的强大之处是很巧妙的避开了这两个的冲突 。比如我们常见的歧义句“旅游和服务”->对于一般保证召回 。大家会给出的结果是“旅游 和服 服务” 对于ansj不存在跨term的分词。意思就是。召回的词只是针对精准分词之后的结果的一个细分。比较好的解决了这个问题

分词具有什么功能

用户自定义词典 数字识别 人名识别 机构名识别 新词发现
Χ Χ


词性标注

    Ansj词性标注是基于HMM的。主要利用了ngram的方式,相对而言作的还是比较粗


分词实现步骤

    Ansj分词可以说是一个ictclas的Java版本,基本原理一致,只不过在分词优化算法上做了一些改进。

现分词有以下几个步骤: 


  1. 全切分,原子切分;
  2. N最短路径的粗切分,根据隐马尔科夫模型和viterbi算法,达到最优路径的规划;
  3. 人名识别;
  4. 系统词典补充;
  5. 用户自定义词典的补充;
  6. 词性标注(可选)


分词流程详解

    默认分词模式是ToAnalysis,以此为例来查看一下ansj的分词实现流程

示例:
    深圳鹏元征信有限公司 
     分词结果: 深圳/ns, 鹏/nr, 元/q, 征信/nz, 有限公司/n


先来看一下最基本的一些元素结构类。
Term类的成员:
private static final long serialVersionUID = 1L;
// 当前词
private String name;
//
private String realName;
// 当前词的起始位置
private int offe;
// 词性列表
private TermNatures termNatures = TermNatures.NULL;
// 词性列表
private AnsjItem item = AnsjItem.NULL;
// 同一行内数据
private Term next;
// 分数
private double score = 0;
// 本身分数
private double selfScore = 1;
// 起始位置
private Term from;
// 到达位置
private Term to;
// 本身这个term的词性.需要在词性识别之后才会有值,默认是空
private Nature nature = Nature.NULL;

private List<Term> subTerm = null;
进入Graph构建最短路径,Graph的类成员:
public char[] chars = null;
public String realStr = null;
public Term[] terms = null;
protected Term end = null;
protected Term root = null;
protected static final String E = "##";
protected static final String B = "##";
// 是否有人名
public boolean hasPerson;
// 是否有数字
public boolean hasNum;
Graph.chars=

查找第一个字:深
在core.dic中是
28145   156288 -1 2  {a=103, an=2, d=34, j=9, ng=0}
返回值为:

其他类似。

Item类:
protected String name;
protected byte status;
protected int base = 65536;
protected int index;
protected int check;
所有实词都是status是3的base都是65536.

分词逻辑如下:
default:
start = i;
end = i;
c = chars[start];
while (IN_SYSTEM[c] > 0) {
end++;
if (++i >= endOffe)
break;
c = chars[i];
}

if (start == end) {
gp.addTerm(new Term(String.valueOf(c), i, TermNatures.NULL));
continue;
}

gwi.setChars(chars, start, end);
while ((str = gwi.allWords()) != null) {
gp.addTerm(new Term(str, gwi.offe, gwi.getItem()));
}

/**
* 如果未分出词.以未知字符加入到gp
*/
if (IN_SYSTEM[c] > 0 || status(c) > 3 || Character.isHighSurrogate(chars[i]) ) {
i -= 1;
} else {
gp.addTerm(new Term(String.valueOf(c), i, TermNatures.NULL));
}

break;
}
具体体现是,第一轮,与词库core.dic匹配后,terms加载为:

第二轮,标准分词,
/**
* 干涉性增加相对权重
*
* @param relationMap
*/
public void walkPath(Map<String, Double> relationMap) {
Term term = null;
// BEGIN先行打分
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();
merger(term, to, relationMap);
term = term.next();
}
}
optimalRoot();
}
从前往后遍历打分,逻辑为
/**
* 具体的遍历打分方法
*
* @param i
* 起始位置
* @param j
* 起始属性
* @param to
*/
private void merger(Term fromTerm, int to, Map<String, Double> relationMap) {
Term term = null;
if (terms[to] != null) {
term = terms[to];
while (term != null) {
// 关系式to.set(from)
term.setPathScore(fromTerm, relationMap);
term = term.next();
}
} else {
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);
terms[to].setPathScore(fromTerm, relationMap);
}
}
计算的结果是:


取term.next!=null 的组合成词,计算成词分数时用到维特比算法,
// 维特比进行最优路径的构建
double score = MathUtil.compuScore(from, this, relationMap);
如“深”:next:深圳;  
   “有”:next:有限
这一轮计算后得到terms结果为:


详细成词过程图解:

第三轮,经过人名识别,用户自定义词典的识别,识别出专业名词“征信”/nz:

故,最终结果是  深圳/ns, 鹏/nr, 元/q, 征信/nz, 有限公司/n  。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐