1. 程式人生 > >深度解析中文分詞器演算法(最大正向/逆向匹配)

深度解析中文分詞器演算法(最大正向/逆向匹配)

中文分詞演算法概述: 

1:非基於詞典的分詞(nlp語義領域)

    相當於人工智慧領域計算。一般用於機器學習,特定領域等方法,這種在特定領域的分詞可以讓計算機在現有的規則模型中,

推理如何分詞。在某個領域(垂直領域)分詞精度較高。但是實現比較複雜。

2:基於詞典的分詞(最為常見)

     這類分詞演算法比較常見,也比較簡單粗暴,比如正向/逆向匹配和ik的最細粒度。例如: mmseg分詞器 就是一種基於詞典的分詞演算法。以最大正

向匹配為主,多種 消除歧義演算法為輔。但是不管怎麼分。該類分詞方法,分詞精度不高。由於中文比較複雜,不推薦採用正向最大匹配演算法的中文

分詞器。。逆向最大匹配演算法在處理中文往

往會比正向要準確。

    接下來分析第2種:基於詞典的分詞演算法(最長的詞優先匹配)。 先分析最大正向匹配演算法

   一: 具體流程圖如下:

                           


   一:以下程式碼片段為最大正向匹配演算法: 

package hhc.forwardAlgorithm;

import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @Description: 
 * @Date: 2015-2-7上午02:00:51
 * @Author 胡慧超
 * @Version 1.0
 */
@SuppressWarnings("unchecked")
public class TokenizerAlgorithm {
	private static final List<String> DIC=new ArrayList<String>();
	private static int MAX_LENGTH;
	
	/**
	 * 把詞庫詞典轉化成dic物件,並解析詞典資訊
	 */
	static {
		try {
			System.out.println("開始初始化字典...");
			int max=1;
			int count=0;
			//讀取詞典中的每一個詞
			URL url=TokenizerAlgorithm.class.getClassLoader().getResource("hhc/dic.txt");
			Path path=Paths.get(url.toString().replaceAll("file:/", ""));
			List<String> list=Files.readAllLines(path, Charset.forName("UTF-8"));
			System.out.println("讀取詞典檔案結束,詞總數為:"+list.size());
			for(String line:list){
				DIC.add(line);
				count++;
				//獲取詞庫中 ,最大長度的詞的長度
				if(line.length()>max){
					max=line.length();
				}
			}
			MAX_LENGTH=max;
			System.out.println("初始化詞典結束,最大分詞長度為:"+max+"  !!");
			System.out.println("------------------------------------------------------------------------");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 正向分詞演算法
	 * @param text 
	 * @return
	 */
	public static List forwardSeg(String text){
	    List result=new ArrayList();
		while(text.length()>0){
			int len=MAX_LENGTH;		
			if(text.length()<MAX_LENGTH){
				len=text.length();
			}
			//取指定的最大長度 文字去字典中匹配
			String tryWord=text.substring(0, len);
			while(!DIC.contains(tryWord)){//如果詞典中不包含該段文字
				//如果長度為1 的話,且沒有在字典中匹配,返回
				if(tryWord.length()==1){
					break;
				}
				//如果匹配不到,則長度減1,繼續匹配
				/**
				 * --這裡就是最關鍵的地方,把最右邊的詞去掉一個,繼續迴圈
				 */		
				tryWord=tryWord.substring(0, tryWord.length()-1);							
			}
			result.add(tryWord);
			//移除該次tryWord,繼續迴圈
			text=text.substring(tryWord.length());
		}
		return result;
	}
	


	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> lst=new ArrayList();
		lst.add("研究生命起源");
		lst.add("食物和服裝");
		lst.add("乒乓球拍賣完了");
		for(String str:lst){
			List<String> list=forwardSeg(str);
			String word="";
			for(String s:list){
				s+="/";
				word+=s;
			}
			System.out.println(word);
		}
	}
執行正向分詞結果:
                



二:最大逆向分詞演算法

考慮到逆向,為了 區分分詞的資料的連貫性。我們採用Stack(棧物件,資料結果,後進先出,不同於Queue和ArrayList有順序的先進先出) 這個物件來儲存分詞結果。。

	/**
	 * 逆向分詞演算法
	 * @param text 
	 * @return
	 */
	public static List reverseSeg(String text){
		Stack<String> result=new Stack();
		while(text.length()>0){
			int len=MAX_LENGTH;		
			if(text.length()<MAX_LENGTH){
				len=text.length();
			}
			//取指定的最大長度 文字去字典中匹配
			String tryWord=text.substring(text.length()-len);
			while(!DIC.contains(tryWord)){//如果詞典中不包含該段文字
				//如果長度為1 的話,且沒有在字典中匹配,返回
				if(tryWord.length()==1){
					break;
				}
				//如果匹配不到,則長度減1,繼續匹配
				/**
				 * --這裡就是最關鍵的地方,把最左邊的詞去掉一個,繼續迴圈
				 */		
				tryWord=tryWord.substring(1);							
			}
			result.add(tryWord);
			//移除該次tryWord,繼續迴圈
			text=text.substring(0,text.length()-tryWord.length());		
		}
		int size=result.size();
		List list =new ArrayList(size);
		for(int i=0;i<size;i++){
			list.add(result.pop());
		}
		return list;
	}

執行逆向分詞結果





以上程式碼實現了兩種正向和逆向的演算法,可以很明顯的比較中文分詞結果。

像之前介紹的採取正向最大匹配演算法的mmseg分詞器,內部設定了4個消除歧義的過濾演算法,這四個歧義解析規則表明是相當有效率

的。總體來講。mmseg的分詞精度還是值得推薦的。。。

還有一種不基於詞典的分詞器。簡單粗暴,ngram。