1. 程式人生 > >開源中文分詞工具探析(三):Ansj

開源中文分詞工具探析(三):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方法就幹了兩件事:

  1. 按照消歧義規則分詞;
  2. 在此基礎上,按照核心詞典分詞;

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實現的,主要的欄位termsorg.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<String, Double> 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::walkPathGraph::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. 參考資料

相關推薦

開源中文工具Ansj

Ansj是由孫健(ansjsun)開源的一箇中文分詞器,為ICTLAS的Java版本,也採用了Bigram + HMM分詞模型(可參考我之前寫的文章):在Bigram分詞的基礎上,識別未登入詞,以提高分詞準確度。雖然基本分詞原理與ICTLAS的一樣,但是Ansj做了一些工程上的優化,比如:用DAT高效地實現檢

開源中文工具Stanford CoreNLP

inf git deb seq 效果 analysis stream fix sps CoreNLP是由斯坦福大學開源的一套Java NLP工具,提供諸如:詞性標註(part-of-speech (POS) tagger)、命名實體識別(named entity recog

開源中文工具LTP

LTP是哈工大開源的一套中文語言處理系統,涵蓋了基本功能:分詞、詞性標註、命名實體識別、依存句法分析、語義角色標註、語義依存分析等。 【開源中文分詞工具探析】系列: 1. 前言 同THULAC一樣,LTP也是基於結構化感知器(Structured Perceptron, SP),以最大熵準則建模標註序列

開源中文工具THULAC

THULAC是一款相當不錯的中文分詞工具,準確率高、分詞速度蠻快的;並且在工程上做了很多優化,比如:用DAT儲存訓練特徵(壓縮訓練模型),加入了標點符號的特徵(提高分詞準確率)等。 【開源中文分詞工具探析】系列: 1. 前言 THULAC所採用的分詞模型為結構化感知器(Structured Percep

開源中文工具FNLP

FNLP是由Fudan NLP實驗室的邱錫鵬老師開源的一套Java寫就的中文NLP工具包,提供諸如分詞、詞性標註、文字分類、依存句法分析等功能。 【開源中文分詞工具探析】系列: 1. 前言 類似於THULAC,FNLP也是採用線性模型(linear model)分詞。較於對數線性模型(log-linea

中文工具ICTCLAS (NLPIR)

【開源中文分詞工具探析】系列: 1. 前言 ICTCLAS是張華平老師推出的中文分詞系統,於2009年更名為NLPIR。ICTCLAS是中文分詞界元老級工具了,作者開放出了free版本的原始碼(1.0整理版本在此). 作者在論文[1] 中宣稱ICTCLAS是基於HHMM(Hierarchical Hid

中文工具Jieba

【開源中文分詞工具探析】系列: 1. 前言 Jieba是由fxsjy大神開源的一款中文分詞工具,一款屬於工業界的分詞工具——模型易用簡單、程式碼清晰可讀,推薦有志學習NLP或Python的讀一下原始碼。與採用分詞模型Bigram + HMM 的ICTCLAS 相類似,Jieba採用的是Unigram +

數列塊入門九題LOJ6283~6285

freopen ans cstring 離散化 fin 利用 ace reg 有序 Preface 最後一題我一直覺得用莫隊是最好的。 數列分塊入門 7——區間乘法,區間加法,單點詢問 還是很簡單的吧,比起數列分塊入門 7就多了個區間乘。 類似於線段樹,由於乘法的優先級高

北大開源全新中文工具準確率遠超THULAC、結巴

選自GitHub,作者:羅睿軒、許晶晶、孫栩,機器之心編輯。 最近,北大開源了一箇中文分詞工具包,它在多個分詞資料集上都有非常高的分詞準確率。其中廣泛使用的結巴分詞誤差率高達 18.55% 和 20.42,而北大的 pkuseg 只有 3.25% 與 4.32%。 pkuseg 是由北京

在PyCharmPython整合開發環境中安裝jieba中文工具

PyCharm IDE中,可以直接引入各種工具包。jieba中文分詞工具包安裝非常方便。 1、開啟Pycharm,點選左上角  >>File  >>Settings。 2、在settings介面中點選Project :***(專案名稱)  >

PyNLPIR python中文工具

命名 hub 兩個 工具 ict mage ret wid tty 官網:https://pynlpir.readthedocs.io/en/latest/ github:https://github.com/tsroten/pynlpir NLPIR分詞系

7個優秀的開源中文庫推薦

中文分詞是中文文字處理的基礎步驟,也是中文人機自然語言互動的基礎模組。由於中文句子中沒有詞的界限,因此在進行中文自然語言處理時,通常需要先進行分詞。 縱觀整個開源領域,陸陸續續做中文分詞的也有不少,不過目前仍在維護的且質量較高的並不多。下面整理了一些個人認為比較優秀的中文分詞庫,以供大家

Hanlp等七種優秀的開源中文庫推薦

中文分詞是中文文字處理的基礎步驟,也是中文人機自然語言互動的基礎模組。由於中文句子中沒有詞的界限,因此在進行中文自然語言處理時,通常需要先進行分詞。 縱觀整個開源領域,陸陸續續做中文分詞的也有不少,不過目前仍在維護的且質量較高的並不多。下面整理了一些個人認為比較優秀的中文分

10大Java開源中文器的使用方法和效果對比

原文地址:http://my.oschina.net/apdplat/blog/412921 本文的目標有兩個: 1、學會使用10大Java開源中文分詞器 2、對比分析10 大Java開源中文分詞器的分詞效果 本文給出了10大Java開源中文分詞的使用方法以及分詞

【NLP】11大Java開源中文器的使用方法和效果對比

本文的目標有兩個: 1、學會使用11大Java開源中文分詞器 2、對比分析11大Java開源中文分詞器的分詞效果 本文給出了11大Java開源中文分詞的使用方法以及分詞結果對比程式碼,至於效果哪個好,那要用的人結合自己的應用場景自己來判斷。 11大Java開源中文分詞器,不同的分詞器

Solr與開源中文(ansj)整合

1. ansj分詞原始碼及jar包下載地址 原始碼: https://github.com/NLPchina/ansj_seg jar包: http://maven.nlpcn.org/org/ansj/ http://maven.nlpcn.org/org/nlpcn/n

中文工具thulac4j釋出

1. 介紹 thulac4j是THULAC的Java 8工程化實現,具有分詞速度快、準、強的特點;支援 自定義詞典 繁體轉簡體 停用詞過濾 若想在專案中使用thulac4j,可新增依賴: <dependency> <groupId>io.github.yizhiru</g

乾貨 | 史上最全中文工具整理

作者 | fendouai 一.中文分詞  分詞服務介面列表 二.準確率評測: THULAC:與代表性分詞軟體的效能對比 我們選擇LTP-3.2.0 、ICTCLAS(2015版) 、jieba(C++版)等國內具代表性的分詞軟體與THULAC做效能

中文工具

THULAC 四款python中中文分詞的嘗試。嘗試的有:jieba、SnowNLP(MIT)、pynlpir(大資料搜尋挖掘實驗室(北京市海量語言資訊處理與雲端計算應用工程技術研究中心))、thulac(清華大學自然語言處理與社會人文計算實驗室) 四款都

python中文工具結巴jieba

結巴分詞jieba特點    支援三種分詞模式:        精確模式,試圖將句子最精確地切開,適合文字分析;        全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;        搜尋引擎模式,在精確模式的基礎上,對長詞再次切分,提