基於python的中文分詞的實現及應用

基於python的中文分詞的實現及應用

劉新亮 嚴姍姍
(北京工商大學計算機學院,100037)
 
    摘  要  中文分詞的實現及應用屬於自然語言處理範疇,完成的是中文分詞在Python語言環境下的實現,以及利用這個實現的一個應用程序接口和一箇中文文本處理的應用。設計共分爲五個部分,分別是:分詞模塊、包裝模塊、應用程序接口、Nonsense模塊,這個項目是爲了下一步開放源代碼的中文搜索引擎提供中文分詞功能,同時通過表現代碼的娛樂性達到促進公開源代碼的發展。
    關鍵詞  中文分詞;Python語言;程序接口
 

 

1 引言

    自然語言處理是研究實現人與計算機之間用自然語言進行有效通信的各種理論和方法的一個領域,是計算機科學領域和人工智能領域的重要發展方向之一。它大體包括自然語言理解和自然語言生成兩個部分,前者是使計算機理解自然語言文本,後者是使計算機以自然語言文本來表達給定的意圖、思想等。
    自然語言處理是目前比較前沿的學科,擁有廣闊的前景,但同時是非常有難度的。現在已經出現了能夠針對一定應用的實用系統,但是通用的、高質量的自然語言系統仍然是較長期的努力目標。這些實用系統已經具有相當的自然語言處理能力,其中有些已經商品化、甚至產業化。典型的例子有:各種數據庫和專家系統的自然語言接口、各種機器翻譯系統、全文信息檢索系統、自動文摘系統等[1]

2 中文分詞技術

    中文分詞技術屬於自然語言處理技術範疇,對於一句話,人可以通過自己的知識來明白哪些是詞,哪些不是詞,但如何讓計算機也能理解,其處理過程就是分詞算法。現有的分詞算法可分爲三大類:基於字符串匹配的分詞方法、基於理解的分詞方法和基於統計的分詞方法[2]
    (1)基於字符串匹配的分詞方法:這種方法又叫做機械分詞方法,它是按照一定的策略將待分析的漢字串與一個“充分大的”機器詞典中的詞條進行匹配,若在詞典中找到某個字符串,則匹配成功(識別出一個詞)。按照掃描方向的不同,串匹配分詞方法可以分爲正向匹配和逆向匹配;按照不同長度優先匹配的情況,可以分爲最大(最長)匹配和最小(最短)匹配;按照是否與詞性標註過程相結合,又可以分爲單純分詞方法和分詞與標註相結合的一體化方法。
    (2)基於理解的分詞方法:這種分詞方法是通過讓計算機模擬人對句子的理解,達到識別詞的效果。其基本思想就是在分詞的同時進行句法、語義分析,利用句法信息和語義信息來處理歧義現象。它通常包括三個部分:分詞子系統、句法語義子系統、總控部分。在總控部分的協調下,分詞子系統可以獲得有關詞、句子等的句法和語義信息來對分詞歧義進行判斷,即模擬人對句子的理解過程。
    (3)基於統計的分詞方法:從形式上看,詞是穩定的字的組合,因此在上下文中,相鄰的字同時出現的次數越多,就越有可能構成一個詞。
    到底哪種分詞算法的準確度更高,目前並無定論。對於任何一個成熟的分詞系統來說,不可能單獨依靠某一種算法來實現,都需要綜合不同的算法。筆者瞭解,海量科技的分詞算法就採用“複方分詞法”。所謂複方,相當於用中藥中的複方概念,即用不同的藥材綜合起來去醫治疾病。同樣,對於中文詞的識別,需要多種算法來處理不同的問題。

3 分詞中的難題

    有了成熟的分詞算法,是否就能容易的解決中文分詞的問題呢?事實遠非如此。中文是一種十分複雜的語言,讓計算機理解中文語言更是困難。在中文分詞過程中,有兩大難題一直沒有完全突破。
    (1)歧義識別:歧義是指同樣的一句話,可能有兩種或者更多的切分方法。例如:表面的,因爲“表面”和“面的”都是詞,那麼這個短語就可以分成“表面的”和“表 面的”。這種稱爲交叉歧義。
    (2)新詞識別:新詞,專業術語稱爲未登錄詞。也就是那些在字典中都沒有收錄過,但又確實能稱爲詞的那些詞。最典型的是人名,人可以很容易理解句子“王軍虎去廣州了”中,“王軍虎”是個詞,因爲是一個人的名字,但要是讓計算機去識別就困難了。如果把“王軍虎”作爲一個詞收錄到字典中去,全世界有那麼多名字,而且每時每刻都有新增的人名,收錄這些人名本身就是一項巨大的工程。即使這項工作可以完成,還是會存在問題,例如:在句子“王軍虎頭虎腦的”中,“王軍虎”還能不能算詞?新詞中除了人名以外,還有機構名、地名、產品名、商標名、簡稱、省略語等都是很難處理的問題,而且這些又正好是人們經常使用的詞,因此對於搜索引擎來說,分詞系統中的新詞識別十分重要。目前新詞識別準確率已經成爲評價一個分詞系統好壞的重要標誌之一。

4 軟件結構

    如圖1所示,軟件實現過程大體是:首先由分詞模塊用C語言實現分詞,這部分主要運用了中國科學院的ICTCLAS,對它進行研究及處理;然後用SWIG對分詞模塊進行包裝,使其能夠被Python語言調用,在Python語言環境下實現中文分詞

 

圖1  軟件結構圖

4.1 分詞模塊結構

    分詞模塊包括兩個部分,其一是中國科學院的公開源代碼軟件ICTCLAS(Institute of Computing Technology, Chinese Lexical Analysis System) 。該系統的功能有:中文分詞;詞性標註;未登錄詞識別。分詞正確率高達97.58%(973專家組評測結果),未登錄詞識別召回率均高於90%,其中中國人名的識別召回率接近98%,處理速度爲31.5Kbytes/s。ICTCLAS的特色還在於:可以根據需要輸出多個高概率結果,有多種輸出格式,支持北大詞性標註集,973專家組給出的詞性標註集合。該系統得到了專家的好評,並有多篇論文在國內外發表。[3][4][5]
    ICTCLAS的功能有:中文分詞,詞性標註,未登錄詞識別。中文分詞,即輸入“我是中國人。” ,得到處理結果爲“我/是/中國/人/。/” 。詞性標註,即對每個切分開的詞進行詞性說明,支持北大詞性標註集。如上例可得到結果爲:“我/r  是/v  中國/n  人/n  。/w  ” 。未登錄詞識別即解決新詞識別問題。分詞模塊還可以根據需要輸出多個高概率結果,例如,用戶輸入“我是中國人。”
    分詞模塊的第二個部分是對ICTCLAS的處理。通過對源代碼中ICTCLAS_WinDlg.cpp的函數On_button_run()的修改,得到seg.cpp,以便下一步對ICTCLAS進行包裝。
    Seg.h:
class Segment{
    CResult m_ICTCLAS;
public: char* process_paragraph(char* sSource,int type,int format);
};
seg.cpp:
//#include <ICTCLAS.h>
#include <string.h>
//#include "StdAfx.h"
//#include <iostream.h>
#include "seg.h"
char* Segment::process_paragraph(char* sSource,int type,int format)
{   
    char* sResult;//,*sSource;
    //CResult m_ICTCLAS;
    m_ICTCLAS.m_nOutputFormat=format;
    m_ICTCLAS.m_nOperateType=type;
    //clock_t start, finish;
    if(format!=2)
         sResult=new char [(strlen(sSource)+13)*3];
    else
         sResult=new char [(strlen(sSource)+13)*50];
    if (!m_ICTCLAS.ParagraphProcessing(sSource,sResult)){
         return NULL;
     }
    else{
         return sResult;
     }

4.2 包裝模塊

    具體過程是:① 根據seg.cpp編寫seg.i文件;②通過swig用seg.i生成seg.py 和seg_wrap.cpp,命令行爲:swig –c++ -python cpp1.i;③ 通過setup.py 把seg.cxx和seg_wrap.cxx編譯到一起,生成_seg1.pyd, a.編寫setup.py,b.輸入命令行setup.py build.ext;④ 把_seg.pyd拷貝到與其他文件同一目錄下;⑤ 在seg.py中import _seg.pyd;⑥ 用python調用此程序時import seg.py;
    具體算法:
seg.i:
 %module seg
%{
#include "seg.h"
%}
extern class Segment{
public: char* process_paragraph(char* sSource,int type,int format);
};
    整個分詞模塊相當於一個黑盒子,seg.py相當於是唯一與其聯繫的線,我們通過seg.py使用分詞模塊,體現包裝的作用。process_paragraph是seg.cpp中的函數,通過調用它,可以完成分詞工作。函數有三個參數,分別是中文文本,標示類型和輸出標準。
setup.py:
from distutils.core import setup, Extension
setup(name="_seg", version="1.0",
      ext_modules=[Extension("_seg", ["seg.cpp","Result\\Result.cpp","Segment\\Segment.cpp","Utility\\Dictionary.cpp",
"Segment\\DynamicArray.cpp","Segment\\NShortPath.cpp","Segment\\Queue.cpp","Segment\\SegGraph.cpp","Tag\\Span.cpp",
"StdAfx.cpp","Unknown\\UnknowWord.cpp","Utility\\Utility.cpp","Utility\\ContextStat.cpp","seg_wrap.cxx"],
include_dirs="Result\\","Segment\\","Dictionary\\","DynamicArray\\","NShortPath\\","Queue\\","SegGraph\\","Span\\","StdAfx\\",
"UnkownWord\\","Utility\\","ContextStat\\"])])

4.3 應用程序接口 

forexoweb.py:
import seg
import sys
import string
a=seg.Segment()
def tokenizer(filecontent):
    (type=1 一級標註;format=0 北大標準)
    b=a.process_paragraph(filecontent,1,0)
    list=b.split()
    (刪去標點,助詞,嘆詞)
    list = filter(lambda x: (not (x[-2:] in ('/w','/u','/e'))),
           list)
    list = map(lambda x: x.split('/')[0],
list)
    定義函數tokenizer,參數filecontent表示需要被分詞的中文文本。調用函數process_paragraph,選擇一級標註,並且以北大標準輸出。然後將分次結果轉換成列表形式,並刪去標點符號,助詞和嘆詞,刪除標註,以方便之後開發搜索引擎之用。
    例如,需要被分詞的中文文本爲“我是中國人嗎?” ,對其處理過程如下:
    分詞:“我/r  是/v  中國/n  人/n  嗎/y  ?/w  ”
    轉換成列表形式:
    ['我',’/r’,'是',’/v’,'中國',’/n’,'人',’/n’,'嗎',’/y’,'?',’/w’]
刪除標點符號,助詞和嘆詞,刪除標註:
    ['我', '是', '中國', '人']

4.4 Nonsense模塊

    基本算法:
    ①  對中文文本進行分詞處理
    ②  確定新文本第一個詞
    ③  確定新文本第二個詞
    ④  循環確定下一個詞,直到新文本字數等於原文本
import seg
import sys
import string
import random
class Myclass:
        def __init__(self):
        self.a = seg.Segment()
       def tokenizer(self,allLines):
        b = self.a.process_paragraph(allLines,0,0)
        words = b.split()
        self.first = self.find_firstword_dict(words)
        (self.word_dict, self.sum_dict) = self.change_ to_dict(words)
        self.word_length = len(words) 
        #return [ unicode(word, 'GBK') for word in words ]
    def find_firstword_dict(self,words):
            sentences = []
        current_sentence = []
        for word in words:
          if word in ["。","?","!"]:
             sentences.append(current_sentence)
                current_sentence = []
            else:
              current_sentence.append(word)
        if len(current_sentence) > 0:
             sentences.append(current_sentence)
        sum=0
        a=0
        first={}
        for current_sentence in sentences:
           a = current_sentence[0]
           first[a] = first.setdefault(a,0) + 1
           sum += 1
          for key in first.keys():
            first[key] = float(first[key]) / sum
        return first
        (first={'我':0.33,'你':0.33,'她':0.33}
        假設:first={'我':0.5,'你':0.2,'她':0.3})
    def find_first_word(self):
        first = self.first
        h = random.random()
        m=0
        firstword = ''
       values = []
        firstkeys = first.keys()
        start = 0
        for eachkey in firstkeys:
            values.append((eachkey,start,start+self.first[eachkey]))
            start += self.first[eachkey]
        (values = [('我',  0,0.5),
                  ('你',0.5,0,7),
                 ('她',0.7,  1)])
       for value in values:
          word,start,end = value
            if h >= start and h <= end:
                firstword = word
                break
        return firstword
    def change_to_dict(self,words):
        word_dict = {}
        sum_dict = {}
        sum = 0
        curr_word = words[0]
        for next_word in words[1:]:
           if word_dict.has_key(curr_word):
              if word_dict[curr_word].has_key(next_word):
                    word_dict[curr_word][next_word] += 1
                else:
                    word_dict[curr_word][next_word] = 1
                sum_dict[curr_word] += 1   
            else:
              word_dict[curr_word] = {next_word:1}
                sum_dict[curr_word] = 1
            curr_word = next_word
            sum_dict = {'我':1,'是':3})
        return (word_dict, sum_dict)
    def get_next_word(self,next_words,sum):
       h = random.randint(1,sum)
  m=0
        nextword = ''
        values = []
        nextkeys = next_words.keys()
        (nextkeys = ['我','你','她'])
        start = 0
           for eachkey in nextkeys:
            values.append((eachkey,start,start+next_words[eachkey]))
            start += next_words[eachkey]
        for value in values:
            word,start,end = value
            if h >= start and h <= end:
                nextword = word
                break
        return nextword       
 

5 結論

    在這個軟件中,我們在Python語言環境下實現了中文分詞,並進行了中文信息處理的應用。這對Python語言和中文信息處理來說,是雙向的擴展,都具有積極的意義:Python語言的優勢可以給中文信息處理帶來更好的效果,同時中文信息處理的廣闊前景也給Python語言以及與其相關的Zope等提供了更大的發展空間。有關中文分詞的應用,還可以繼續往人工智能方向發展,例如利用Python語言編寫的貝葉斯分類器,可以獲得更有實際意義的應用,比如垃圾郵件分類等。

參考文獻

    [1]《python核心編程》, (美)Wesley J.Chun(陳仲才)著, 楊濤 王建橋 楊曉雲 高文雅 等譯, 機械工業出版社, 2001.8
    [2]徐飛 孫勁光,基於一種粗切分的最短路徑中文分詞研究[J] 計算機與信息技術,2007(11)
    [3]Hua-Ping ZHANG Qun LIU Xue-Qi CHENG Hao Zhang Hong-KuiYu. Chinese Lexical Analysis Using Hierarchical Hidden Markov Model.
    [4]Remi Delon, CherryPy Tutorial, 12 May 2004, Release 0.10
    [5]Python Tutorial,Guido van Rossum Fred L. Drake, Jr., editor,PythonLabs ,Release 2.3.4,May 20, 2004
    收稿日期:9月10日       修改日期:9月12日

    作者簡介:劉新亮(1972-),男,北京工商大學計算機學院講師,研究方向:數據庫應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章