NLP之中文分詞

Happy丶lazy發表於2020-10-12

中文分詞主要分為規則分詞、統計分詞、混合分詞

規則分詞

主要是通過人工設立詞庫,按照一定方式進行匹配切分,其實現簡單高效,但對新詞很難進行處理

基於規則的分詞是一種機械分詞方法,主要是通過維護詞典,在切分語句時,將語句的每個字串與詞表中的詞進行逐一匹配,找到則切分,否則不予切分。按照匹配切分的方式,主要有正向最大匹配法、逆向最大匹配法以及雙向最大匹配法三種方法。

正向最大匹配法

假定分詞詞典中的最長詞有i個漢字字元,則用被處理文件的當前字串中的前i個字作為匹配欄位,查詢
字典。若字典中存在這樣的一個i字詞,則匹配成功,匹配欄位被作為一一個詞切分出來。
如果詞典中找不到這樣的一個i字詞,則匹配失敗,將匹配欄位中的最後一個字去掉,
對剩下的字串重新進行匹配處理。如此進行下去,直到匹配成功,即切分出一個詞或剩
餘字串的長度為零為止。這樣就完成了一輪匹配,然後取下一個i字字串進行匹配處理,
直到文件被掃描完為止。

def getWordDic():
    """
    讀取詞典檔案
    載入詞典
    :return:
    """
    words_dic = []
    with open("dic.txt","r", encoding="utf8") as dic_input:
        for word in dic_input:
            words_dic.append(word.strip())
    return words_dic
#實現正向匹配演算法中的切詞方法
def cut_words(raw_sentence,words_dic):
    #統計詞典中最長的詞
    max_length = max(len(word) for word in words_dic)
    sentence = raw_sentence.strip()
    #統計序列長度
    words_length = len(sentence)
    #儲存切分好的詞語
    cut_word_list = []
    while words_length > 0:
        max_cut_length = min(max_length, words_length)
        subSentence = sentence[0 : max_cut_length]
        while max_cut_length > 0:
            if subSentence in words_dic:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[0:max_cut_length]
        sentence = sentence[max_cut_length:]
        words_length = words_length - max_cut_length
    #words = "/".join(cut_word_list)
    return cut_word_list
cut_words('很高興分享關於NLP的技術',getWordDic())
['很', '高興', '分享', '關於', 'NLP', '的', '技術']

逆向最大匹配演算法

逆向最大匹配法從被處理文件的末端開始匹配掃描,每次取最末端的i個字元(i為詞典中最長詞數)作為匹配欄位,若匹配失敗,則去掉匹配欄位最前面的一個字,繼續匹配。

def getWordDic():
    """
    讀取詞典檔案
    載入詞典
    :return:
    """
    words_dic = []
    with open("dic.txt","r", encoding="utf8") as dic_input:
        for word in dic_input:
            words_dic.append(word.strip())
    return words_dic
#實現逆向最大匹配演算法中的切詞方法
def cut_words(raw_sentence,words_dic):
    #統計詞典中詞的最長長度
    max_length = max(len(word) for word in words_dic)
    sentence = raw_sentence.strip()
    #統計序列長度
    words_length = len(sentence)
    #儲存切分出來的詞語
    cut_word_list = []
    #判斷是否需要繼續切詞
    while words_length > 0:
        max_cut_length = min(max_length, words_length)
        subSentence = sentence[-max_cut_length:]
        while max_cut_length > 0:
            if subSentence in words_dic:
                cut_word_list.append(subSentence)
                break
            elif max_cut_length == 1:
                cut_word_list.append(subSentence)
                break
            else:
                max_cut_length = max_cut_length -1
                subSentence = subSentence[-max_cut_length:]
        sentence = sentence[0:-max_cut_length]
        words_length = words_length -max_cut_length
    cut_word_list.reverse()
    #words = "/".join(cut_word_list)
    return  cut_word_list
cut_words('很高興分享關於NLP的技術',getWordDic())
['很', '高興', '分享', '關於', 'NLP', '的', '技術']

雙向最大匹配法

雙向最大匹配法( Bi-directction Matching method)是將正向最大匹配法得到的分詞
結果和逆向最大匹配法得到的結果進行比較,然後按照最大匹配原則,選取詞數切分最
少的作為結果。這正是雙向最大匹配法在實用中文資訊處理系統中得以廣泛使用的原因。

def getWordDic():
    """
    讀取詞典檔案
    載入詞典
    :return:
    """
    words_dic = []
    with open("dic.txt","r", encoding="utf8") as dic_input:
        for word in dic_input:
            words_dic.append(word.strip())
    return words_dic
#!/usr/bin/python
# -*- coding: UTF-8 -*-
#Author bruce
import FMM
import BMM
#實現雙向匹配演算法中的切詞方法
def cut_words(raw_sentence,words_dic):
    bmm_word_list = BMM.cut_words(raw_sentence,words_dic)
    fmm_word_list = FMM.cut_words(raw_sentence,words_dic)
    bmm_word_list_size = len(bmm_word_list)
    fmm_word_list_size = len(fmm_word_list)
    if bmm_word_list_size != fmm_word_list_size:
        if bmm_word_list_size < fmm_word_list_size:
            return bmm_word_list
        else:
            return fmm_word_list
    else:
        FSingle = 0
        BSingle = 0
        isSame = True
        for i in range(len(fmm_word_list)):
            if fmm_word_list[i] not in bmm_word_list:
                isSame = False
            if len(fmm_word_list[i])  == 1:
                FSingle = FSingle + 1
            if len(bmm_word_list[i]) == 1:
                BSingle = BSingle + 1
        if isSame:
            return fmm_word_list
        elif BSingle > FSingle:
            return fmm_word_list
        else:
            return bmm_word_list
cut_words('很高興分享關於NLP的技術',getWordDic())
['很', '高興', '分享', '關於', 'NLP', '的', '技術']

統計分詞

通過機器學習技術,應用於分詞任務上後,能夠較好應對新詞發現等特殊場景

把每個詞看做是由詞的最小單位的各個字組成的,如果相連的字在不同的文字中出現的
次數越多,就證明這相連的字很可能就是一個詞。因此我們就可以利用字與字相鄰出現的頻率
來反應成詞的可靠度,統計語料中相鄰共現的各個字的組合的頻度,當組合頻度高於某-一個臨界值時,我們便可認為此字組可能會構成-一個詞語。

基於統計的分詞一般要做如下兩步操作

1):建立統計語言模型

2):對句子進行單詞劃分,然後對劃分結果進行概率計算,獲得概率最大的分詞方式,如隱含馬爾科夫(HMM)、條件隨機場(CRF)

隱含馬爾可夫模型(HMM)

是將分詞作為字在字串中的序列標註任務來實現的。其基本思路是:每個字在構造一個特定的詞語時都佔據著–個確定的構詞位置(即詞位),
現規定每個字最多隻有四個構詞位置:即B (詞首) M (詞中)、E (詞尾)和S (單獨成詞), 那麼下面句子1)的分詞結果就可以直接表示成如2)所示的逐字標註形式:

1)中文/分詞/是/.文字處理/不可或缺/的/一步!

2)中/B文/E分/B詞/E是/S文/B本/M處/M理/E不/B可/M或/M缺/E的/S一//B步/E!/S

條件隨機場(CRF)

在隱含馬爾可夫中,有個很經典的假設,那就是每個狀態只與它前面的狀態有關。這樣的假設顯然是有偏差的,
於是學者們提出了條件隨機場演算法,使得每個狀態不止與他前面的狀態有關,還與他後
面的狀態有關

混合分詞

單純的統計分詞也有缺陷,那就是太過於依賴語料的質量,因此實踐中多是採用這兩種方法的結合,即混合分詞

目前不管是基於規則的演算法、還是基於HMM、CRF或者deep learning 等的方法,其分詞效果在具體任務中,其實差距並沒有那麼明顯。在實際工程應用中,多
是基於一種分詞演算法,然後用其他分詞演算法加以輔助。最常用的方式就是先基於詞典的
方式進行分詞,然後再用統計分詞方法進行輔助。如此,能在保證詞典分詞準確率的基
礎上,對未登入詞和歧義詞有較好的識別,Jieba 分詞工具便是基於這種方法的實現。

jieba

Jieba分詞結合了基於規則和基於統計這兩類方法。首先基於字首詞典進行詞圖掃
描,字首詞典是指詞典中的詞按照字首包含的順序排列,例如詞典中出現了“上”,之後
以“上”開頭的詞都會出現在這一部分,例如“上海”,進而會出現“上海市”,從而形
成一種層級包含結構。如果將詞看作節點,詞和詞之間的分詞符看作邊,那麼一種分詞
方案則對應著從第一個字到最後一個字的一-條分詞路徑。因此,基於字首詞典可以快速
構建包含全部可能分詞結果的有向無環圖,這個圖中包含多條分詞路徑,有向是指全部
的路徑都始於第一個字、止於最後一個字,無環是指節點之間不構成閉環。基於標註語
料,使用動態規劃的方法可以找出最大概率路徑,並將其作為最終的分詞結果。對於未
登入詞,Jieba使用了基於漢字成詞的HMM模型,採用了Viterbi 演算法進行推導。

▼精確模式:試圖將句子最精確地切開,適合文字分析。

▼全模式:把句子中所有可以成詞的詞語都掃描出來,速度非常快,但是不能解決
歧義。

▼搜尋引擎模式:在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於
搜尋引擎分詞。

import jieba
sent ="中文分詞是文字處理不可或缺的一步"
seg_list = jieba.cut(sent,cut_all=True)
print('全模式: ','/ '.join(seg_list))
seg_list = jieba.cut(sent,cut_all=False)
print('精確模式: ','/ '.join(seg_list))
seg_list = jieba.cut(sent)
print('預設精確模式: ','/ '.join(seg_list))
seg_list = jieba.cut_for_search(sent)
print('搜尋引擎模式','/'.join(seg_list) )
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\ADMINI~1\AppData\Local\Temp\jieba.cache
Loading model cost 1.155 seconds.
Prefix dict has been built succesfully.


全模式:  中文/ 分詞/ 是/ 文字/ 文字處理/ 本處/ 處理/ 不可/ 不可或缺/ 或缺/ 的/ 一步
精確模式:  中文/ 分詞/ 是/ 文字處理/ 不可或缺/ 的/ 一步
預設精確模式:  中文/ 分詞/ 是/ 文字處理/ 不可或缺/ 的/ 一步
搜尋引擎模式 中文/分詞/是/文字/本處/處理/文字處理/不可/或缺/不可或缺/的/一步

如需要程式碼或資料進群753035545


相關文章