cppjieba分詞學習筆記


cppjieba分詞包主要提供中文分詞、關鍵詞提取、詞性標注三種功能

一、分詞

  cppjieba分詞用的方法是最大概率分詞(MP)和隱馬爾科夫模型(HMM),以及將MP和HMM結合成的MixSegment分詞器。除此之外,cppjieba支持三種模式的分詞:

  • 精確模式,試圖將句子最精確地切開,適合文本分析;
  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;

我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學

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

小明, 碩士, 畢業, 於, 中國, 科學, 學院, 科學院, 中國科學院, 計算, 計算所, 后, 在, 日本, 京都, 大學, 日本京都大學, 深造

1.最大概率分詞(MP)

  • 默認基於jieba.dict.utf8生成前綴詞典,用戶可以添加自己的字典,並且按照一定權重比重一起生成前綴詞典。構建字典時將utf-8格式的輸入轉變為unicode格式
  • 分詞器中有一個類Prefilter,pre_fileter會將輸入的字符串轉變為unicode格式,根據某些特殊的字符symbols_,將輸入的字符串切分成一段一段,對每一段分別分詞
  • 構建DAG圖,從后往前的動態規划算法,回溯概率最大的切詞方法
    void Cut(RuneStrArray::const_iterator begin,
            RuneStrArray::const_iterator end, vector<WordRange>& words,
            size_t max_word_len = MAX_WORD_LENGTH) const {
        vector<Dag> dags;
        dictTrie_->Find(begin, end, dags, max_word_len);  //構建DAG圖
        CalcDP(dags);  //從后往前的動態規划算法
        CutByDag(begin, end, dags, words);//回溯
    }

2.隱藏馬爾科夫分詞(HMM)

參考:中文分詞之HMM模型詳解

cppjieba分詞中提供了HMM模型的參數文件,保存在hmm_model.utf8中。cppjieba的HMM分詞器,實際上就是加載HMM模型,然后根據輸入的句子(觀察序列),計算可能性最大的狀態序列。狀態空間由B(開始)、M(中間)、E(結束)、S(單個字)構成。下面是Viterbi算法的過程。

輸入樣例:

小明碩士畢業於中國科學院計算所

定義變量

二維數組 weight[4][15],4是狀態數(0:B,1:E,2:M,3:S),15是輸入句子的字數。比如 weight[0][2] 代表 狀態B的條件下,出現'碩'這個字的可能性。

二維數組 path[4][15],4是狀態數(0:B,1:E,2:M,3:S),15是輸入句子的字數。比如 path[0][2] 代表 weight[0][2]取到最大時,前一個字的狀態,比如 path[0][2] = 1, 則代表 weight[0][2]取到最大時,前一個字(也就是)的狀態是E。記錄前一個字的狀態是為了使用viterbi算法計算完整個 weight[4][15] 之后,能對輸入句子從右向左地回溯回來,找出對應的狀態序列。

使用InitStatus對weight二維數組進行初始化

已知InitStatus如下:

#B -0.26268660809250016 #E -3.14e+100 #M -3.14e+100 #S -1.4652633398537678 

且由EmitProbMatrix可以得出

Status(B) -> Observed(小) :  -5.79545 Status(E) -> Observed(小) :  -7.36797 Status(M) -> Observed(小) :  -5.09518 Status(S) -> Observed(小) :  -6.2475 

所以可以初始化 weight[i][0] 的值如下:

weight[0][0] = -0.26268660809250016 + -5.79545 = -6.05814 weight[1][0] = -3.14e+100 + -7.36797 = -3.14e+100 weight[2][0] = -3.14e+100 + -5.09518 = -3.14e+100 weight[3][0] = -1.4652633398537678 + -6.2475 = -7.71276 

注意上式計算的時候是相加而不是相乘,因為之前取過對數的原因。

遍歷句子計算整個weight二維數組

//遍歷句子,下標i從1開始是因為剛才初始化的時候已經對0初始化結束了 for(size_t i = 1; i < 15; i++) { // 遍歷可能的狀態 for(size_t j = 0; j < 4; j++) { weight[j][i] = MIN_DOUBLE; path[j][i] = -1; //遍歷前一個字可能的狀態 for(size_t k = 0; k < 4; k++) { double tmp = weight[k][i-1] + _transProb[k][j] + _emitProb[j][sentence[i]]; if(tmp > weight[j][i]) // 找出最大的weight[j][i]值 { weight[j][i] = tmp; path[j][i] = k; } } } } 

如此遍歷下來,weight[4][15] 和 path[4][15] 就都計算完畢。

確定邊界條件和路徑回溯

邊界條件如下:

對於每個句子,最后一個字的狀態只可能是 E 或者 S,不可能是 M 或者 B。

所以在本文的例子中我們只需要比較 weight[1(E)][14] 和 weight[3(S)][14] 的大小即可。

在本例中:

weight[1][14] = -102.492; weight[3][14] = -101.632; 

所以 S > E,也就是對於路徑回溯的起點是 path[3][14]

回溯的路徑是:

SEBEMBEBEMBEBEB

倒序一下就是:

BE/BE/BME/BE/BME/BE/S

所以切詞結果就是:

小明/碩士/畢業於/中國/科學院/計算/所

到此,一個HMM模型中文分詞算法過程就闡述完畢了。

也就是給定我們一個模型,我們對模型進行載入完畢之后,只要運行一遍Viterbi算法,就可以找出每個字對應的狀態,根據狀態也就可以對句子進行分詞。

3、MixSegment是MP和HMM的結合,首先使用MP分詞,然后對MP分詞的結果使用HMM分詞。其實,第二次使用HMM再分對原有分詞結果調整得並不多,只是將MP結果中單字順序收集再分詞。

    void Cut(RuneStrArray::const_iterator begin,
            RuneStrArray::const_iterator end, vector<WordRange>& res,
            bool hmm) const {
        if (!hmm) {
            mpSeg_.Cut(begin, end, res);
            return;
        }
        vector<WordRange> words;
        assert(end >= begin);
        words.reserve(end - begin);
        mpSeg_.Cut(begin, end, words);

        vector<WordRange> hmmRes;
        hmmRes.reserve(end - begin);
        for (size_t i = 0; i < words.size(); i++) {
            //if mp Get a word, it's ok, put it into result
            if (words[i].left != words[i].right
                    || (words[i].left == words[i].right
                            && mpSeg_.IsUserDictSingleChineseWord(
                                    words[i].left->rune))) {
                res.push_back(words[i]);
                continue;
            }

            // if mp Get a single one and it is not in userdict, collect it in sequence
            size_t j = i;
            while (j < words.size() && words[j].left == words[j].right
                    && !mpSeg_.IsUserDictSingleChineseWord(words[j].left->rune)) {
                j++;
            }

            // Cut the sequence with hmm
            assert(j - 1 >= i);
            // TODO
            hmmSeg_.Cut(words[i].left, words[j - 1].left + 1, hmmRes);
            //put hmm result to result
            for (size_t k = 0; k < hmmRes.size(); k++) {
                res.push_back(hmmRes[k]);
            }

            //clear tmp vars
            hmmRes.clear();

            //let i jump over this piece
            i = j - 1;
        }
    }

 二、關鍵詞提取

cpp結巴的提供了兩種關鍵詞提取方法,分別基於TF-IDF和TextRank算法,下面分別介紹

1.基於TF-IDF算法的關鍵詞抽取

TF(term frequency):一個詞語在單個文檔出現的次數
IDF(Inverse document frequency): 逆文檔頻率,是一個詞語普遍度的度量。某一特定詞語的IDF,有總文件數目除以包含該詞語的文件數目,再將取得商取對數的得到。
總的來說某一特定文件內的高頻率詞語,以及詞語在整個文件集合中的低文件頻率,可以產生出高權重的TF-IDF。
cppjieba中提供接近26萬詞語的IDF(idf.utf8),對於在idf.utf8中沒出現的詞語,idf權重取平局值。除此之外,有些詞語在文檔的出現的頻率TF很高,但是對於文檔來說沒什么實在的意義比如“的”,“是”等,因此cppjieba還提供了了一個1534個詞語的stop_words.utf8。
cppjieba中根據權重(tf*idf)排序,取出topN個關鍵詞。
partial_sort(keywords.begin(), keywords.begin() + topN, keywords.end(),Compare);

參考:http://blog.csdn.net/awj3584/article/details/18604901

2.基於TextRank的關鍵詞提取

  PageRank根據網頁之間的鏈接關系,建立網頁之間的有向圖,圖中的節點表示每個網頁,然后根據這個有向圖迭代計算出每個網頁的PER值,作為網頁重要性排名的依據。TextRank方法與PageRank的方法非常類似,當使用TextRank方法做文本的關鍵字提取時,首先在給定的共現窗口內(cppjieba中默認為5)根據詞語之間的共現關系建立鏈接關系圖,圖中的每個節點代表每個節點,每條邊的權重表示詞語共現的次數。然后根據詞語之間鏈接關系圖,迭代計算出每個詞語的權重值,根據權重值選出文本中權重值大的幾個關鍵詞。

  TextRank 一般模型可以表示為一個有向有權圖 G =(V, E), 由點集合 V和邊集合 E 組成, E 是V ×V的子集。圖中任兩點 Vi , Vj 之間邊的權重為 wji , 對於一個給定的點 Vi, In(Vi) 為 指 向 該 點 的 點 集 合 , Out(Vi) 為點 V指向的點集合。點 Vi 的得分定義如下:

  其中, d 為阻尼系數, 取值范圍為 0 到 1, 代表從圖中某一特定點指向其他任意點的概率, 一般取值為 0.85。使用TextRank 算法計算圖中各點的得分時, 需要給圖中的點指定任意的初值, 並遞歸計算直到收斂, 即圖中任意一點的誤差率小於給定的極限值時就可以達到收斂。cppjieba在實現默認迭代10次。

參考:http://www.cnblogs.com/clover-siyecao/p/5726480.html
參考:http://blog.sohu.com/s/MTAzMjM1NDY0/239636012.html(矩陣形式)

三、詞性標注

cppjieba中的詞性標注實現過程總的來說是基於詞典的查詢,詞典中存有大約35萬詞匯的詞性。單個詞語的詞性標注,直接查詢詞典,詞典中不存在一個詞語多個詞性的問題。詞典沒有的詞語,根據詞語的特點的簡單的標注為數字(m),英文(eng),以及x。對於整個句子的詞性標注是用分詞算法分詞,然后用上述單個詞語詞性標注的方法逐個標注詞性。由此可見cppjieba的詞性標注非常依賴詞典。對於句子的詞性標注,沒有考慮詞性之間的關系。

參考: CppJieba代碼詳解

          "結巴"(Jieba)中文分詞系列性能評測


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM