jieba分詞學習筆記(二)

<!-- toc -->

分詞模式

jieba分詞有多種模式可供選擇。可選的模式包括:python

  • 全切分模式正則表達式

  • 精確模式算法

  • 搜索引擎模式函數

同時也提供了HMM模型的開關。搜索引擎

其中全切分模式就是輸出一個字串的全部分詞,spa

精確模式是對句子的一個機率最佳分詞,code

而搜索引擎模式提供了精確模式的再分詞,將長詞再次拆分爲短詞。索引

效果大抵以下:ip

# encoding=utf-8
import jieba

seg_list = jieba.cut("我來到北京清華大學", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式

seg_list = jieba.cut("我來到北京清華大學", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精確模式

seg_list = jieba.cut("他來到了網易杭研大廈")  # 默認是精確模式
print(", ".join(seg_list))

seg_list = jieba.cut_for_search("小明碩士畢業於中國科學院計算所,後在日本京都大學深造")  # 搜索引擎模式
print(", ".join(seg_list))

的結果爲utf-8

【全模式】: 我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學

【精確模式】: 我/ 來到/ 北京/ 清華大學

【新詞識別】:他, 來到, 了, 網易, 杭研, 大廈    (此處,「杭研」並無在詞典中,可是也被Viterbi算法識別出來了)

【搜索引擎模式】: 小明, 碩士, 畢業, 於, 中國, 科學, 學院, 科學院, 中國科學院, 計算, 計算所, 後, 在, 日本, 京都, 大學, 日本京都大學, 深造

其中,新詞識別即用HMM模型的Viterbi算法進行識別新詞的結果。

值得詳細研究的模式是精確模式,以及其用於識別新詞的HMM模型和Viterbi算法。

jieba.cut()

在載入詞典以後,jieba分詞要進行分詞操做,在代碼中就是核心函數jieba.cut(),代碼以下:

def cut(self, sentence, cut_all=False, HMM=True):
        '''
        The main function that segments an entire sentence that contains
        Chinese characters into seperated words.
        Parameter:
            - sentence: The str(unicode) to be segmented.
            - cut_all: Model type. True for full pattern, False for accurate pattern.
            - HMM: Whether to use the Hidden Markov Model.
        '''
        sentence = strdecode(sentence)

        if cut_all:
            re_han = re_han_cut_all
            re_skip = re_skip_cut_all
        else:
            re_han = re_han_default
            re_skip = re_skip_default
        if cut_all:
            cut_block = self.__cut_all
        elif HMM:
            cut_block = self.__cut_DAG
        else:
            cut_block = self.__cut_DAG_NO_HMM
        blocks = re_han.split(sentence)
        for blk in blocks:
            if not blk:
                continue
            if re_han.match(blk):
                for word in cut_block(blk):
                    yield word
            else:
                tmp = re_skip.split(blk)
                for x in tmp:
                    if re_skip.match(x):
                        yield x
                    elif not cut_all:
                        for xx in x:
                            yield xx
                    else:
                        yield x

其中,

docstr中給出了默認的模式,精確分詞 + HMM模型開啓。

第12-23行進行了變量配置。

第24行作的事情是對句子進行中文的切分,把句子切分紅一些只包含能處理的字符的塊(block),丟棄掉特殊字符,由於一些詞典中不包含的字符可能對分詞產生影響。

24行中re_han默認值爲re_han_default,是一個正則表達式,定義以下:

# \u4E00-\u9FD5a-zA-Z0-9+#&\._ : All non-space characters. Will be handled with re_han
re_han_default = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._]+)", re.U)

能夠看到諸如空格、製表符、換行符之類的特殊字符在這個正則表達式被過濾掉。

25-40行使用yield實現了返回結果是一個迭代器,即文檔中所說:

jieba.cut 以及 jieba.cut_for_search 返回的結構都是一個可迭代的 generator,可使用 for 循環來得到分詞後獲得的每個詞語(unicode)

其中,31-40行,若是遇到block是很是規字符,就正則驗證一下直接輸出這個塊做爲這個塊的分詞結果。如標點符號等等,在分詞結果中都是單獨一個詞的形式出現的,就是這十行代碼進行的。

關鍵在28-30行,若是是可分詞的block,那麼就調用函數cut_block,默認是cut_block = self.__cut_DAG,進行分詞

jieba.__cut_DAG()

__cut_DAG的做用是按照DAG,即有向無環圖進行切分單詞。其代碼以下:

def __cut_DAG(self, sentence):
        DAG = self.get_DAG(sentence)
        route = {}
        self.calc(sentence, DAG, route)
        x = 0
        buf = ''
        N = len(sentence)
        while x < N:
            y = route[x][1] + 1
            l_word = sentence[x:y]
            if y - x == 1:
                buf += l_word
            else:
                if buf:
                    if len(buf) == 1:
                        yield buf
                        buf = ''
                    else:
                        if not self.FREQ.get(buf):
                            recognized = finalseg.cut(buf)
                            for t in recognized:
                                yield t
                        else:
                            for elem in buf:
                                yield elem
                        buf = ''
                yield l_word
            x = y

        if buf:
            if len(buf) == 1:
                yield buf
            elif not self.FREQ.get(buf):
                recognized = finalseg.cut(buf)
                for t in recognized:
                    yield t
            else:
                for elem in buf:
                    yield elem

對於一個sentence,首先 獲取到其有向無環圖DAG,而後利用dp對該有向無環圖進行最大機率路徑的計算。
計算出最大機率路徑後迭代,若是是登陸詞,則輸出,若是是單字,將其中連在一塊兒的單字找出來,這些多是未登陸詞,使用HMM模型進行分詞,分詞結束以後輸出。

至此,分詞結束。

其中,值得跟進研究的是第2行獲取DAG第4行計算最大機率路徑第20和34行的使用HMM模型進行未登陸詞的分詞,在後面的文章中會進行解讀。

DAG = self.get_DAG(sentence)

    ...

self.calc(sentence, DAG, route)

    ...

recognized = finalseg.cut(buf)
相關文章
相關標籤/搜索