用Python寫一個簡單的中文分詞器

ybw8 10年前發布 | 29K 次閱讀 Python Python開發

解壓后取出以下文件:訓練數據:icwb2-data/training/pku training.utf8測試數據:icwb2-data/testing/pku test.utf8正確分詞結果:icw...

解壓后取出以下文件:

訓練數據:icwb2-data/training/pku_ training.utf8

測試數據:icwb2-data/testing/pku_ test.utf8

正確分詞結果:icwb2-data/gold/pku_ test_ gold.utf8

評分工具:icwb2-data/script/socre

2 算法描述

算法是最簡單的正向最大匹配(FMM):

用訓練數據生成一個字典

對測試數據從左到右掃描,遇到一個最長的詞,就切分下來,直到句子結束

注:這是最初的算法,這樣做代碼可以控制在60行內,后來看測試結果發現沒有很好地處理數字問題, 才又增加了對數字的處理。

3 源代碼及注釋

#! /usr/bin/env python

-- coding: utf-8 --

Author: minix

Date: 2013-03-20

import codecs import sys

由規則處理的一些特殊符號

numMath = [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9'] numMath_suffix = [u'.', u'%', u'億', u'萬', u'千', u'百', u'十', u'個'] numCn = [u'一', u'二', u'三', u'四', u'五', u'六', u'七', u'八', u'九', u'〇', u'零'] numCn_suffix_date = [u'年', u'月', u'日'] numCn_suffix_unit = [u'億', u'萬', u'千', u'百', u'十', u'個'] special_char = [u'(', u')']

def proc_num_math(line, start): """ 處理句子中出現的數學符號 """ oldstart = start while line[start] in numMath or line[start] in numMath_suffix: start = start + 1 if line[start] in numCn_suffix_date: start = start + 1 return start - oldstart

def proc_num_cn(line, start): """ 處理句子中出現的中文數字 """ oldstart = start while line[start] in numCn or line[start] in numCn_suffix_unit: start = start + 1 if line[start] in numCn_suffix_date: start = start + 1 return start - oldstart

def rules(line, start): """ 處理特殊規則 """ if line[start] in numMath: return proc_num_math(line, start) elif line[start] in numCn: return proc_num_cn(line, start)

def genDict(path): """ 獲取詞典 """ f = codecs.open(path,'r','utf-8') contents = f.read() contents = contents.replace(u'\r', u'') contents = contents.replace(u'\n', u'')

# 將文件內容按空格分開
mydict = contents.split(u' ')
# 去除詞典List中的重復
newdict = list(set(mydict))
newdict.remove(u'')

# 建立詞典
# key為詞首字,value為以此字開始的詞構成的List
truedict = {}
for item in newdict:
    if len(item)>0 and item[0] in truedict:
        value = truedict[item[0]]
        value.append(item)
        truedict[item[0]] = value
    else:
        truedict[item[0]] = [item]
return truedict

def print_unicode_list(uni_list): for item in uni_list: print item,

def divideWords(mydict, sentence): """ 根據詞典對句子進行分詞, 使用正向匹配的算法,從左到右掃描,遇到最長的詞, 就將它切下來,直到句子被分割完閉 """ ruleChar = [] ruleChar.extend(numCn) ruleChar.extend(numMath) result = [] start = 0 senlen = len(sentence) while start < senlen: curword = sentence[start] maxlen = 1

    # 首先查看是否可以匹配特殊規則
    if curword in numCn or curword in numMath:
        maxlen = rules(sentence, start)
    # 尋找以當前字開頭的最長詞
    if curword in mydict:
        words = mydict[curword]
        for item in words:
            itemlen = len(item)
            if sentence[start:start+itemlen] == item and itemlen > maxlen:
                maxlen = itemlen
    result.append(sentence[start:start+maxlen])
    start = start + maxlen
return result

def main(): args = sys.argv[1:] if len(args) < 3: print 'Usage: python dw.py dict_path test_path result_path' exit(-1) dict_path = args[0] test_path = args[1] result_path = args[2]

dicts = genDict(dict_path)
fr = codecs.open(test_path,'r','utf-8')
test = fr.read()
result = divideWords(dicts,test)
fr.close()
fw = codecs.open(result_path,'w','utf-8')
for item in result:
    fw.write(item + ' ')
fw.close()

if name == "main": main()</pre>

4 測試及評分結果

使用 dw.py 訓練數據 測試數據, 生成結果文件

使用 score 根據訓練數據,正確分詞結果,和我們生成的結果進行評分

使用 tail 查看結果文件最后幾行的總體評分,另外socre.utf8中還提供了大量的比較結果, 可以用于發現自己的分詞結果在哪兒做的不夠好

注:整個測試過程都在Ubuntu下完成

$ python dw.py pku_training.utf8 pku_test.utf8 pku_result.utf8

$ perl score pku_training.utf8 pku_test_gold.utf8 pku_result.utf8 > score.utf8

$ tail -22 score.utf8

INSERTIONS:     0

DELETIONS:      0

SUBSTITUTIONS:  0

NCHANGE:        0

NTRUTH: 27

NTEST:  27

TRUE WORDS RECALL:      1.000

TEST WORDS PRECISION:   1.000

=== SUMMARY:

=== TOTAL INSERTIONS:   4623

=== TOTAL DELETIONS:    1740

=== TOTAL SUBSTITUTIONS:        6650

=== TOTAL NCHANGE:      13013

=== TOTAL TRUE WORD COUNT:      104372

=== TOTAL TEST WORD COUNT:      107255

=== TOTAL TRUE WORDS RECALL:    0.920

=== TOTAL TEST WORDS PRECISION: 0.895

=== F MEASURE:  0.907

=== OOV Rate:   0.940

=== OOV Recall Rate:    0.917

=== IV Recall Rate:     0.966


基于詞典的FMM算法是非常基礎的分詞算法,效果沒那么好,不過足夠簡單,也易于入手,隨著學習的深入,我可能還會用Python實現其它的分詞算法。另外一個感受是,看書的時候盡量多去實現,這樣會讓你有足夠的熱情去關注理論的每一個細節,不會感到那么枯燥無力。


</div>

 本文由用戶 ybw8 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!