最近看吳軍博士的《數學之美》。對自然語言處理非常感興趣,驚嘆於數學之美。於是自己抽空實現了乙個無庫取詞方法。下面針對實現方法和遇到的一些問題做一些分享。
首先要知道計算機不是人,並不了解中文語句中哪些是詞語。那我們如何利用計算機得到詞語呢?很簡單,用它最基本的功能-計算。廢話不多說,讓我們一起認識數學的吧。
語料
大家可以理解為取詞的文字,可以是一部書籍、一篇文章、一些使用者操作日誌等。
詞頻
詞頻就是乙個字在語料中出現的次數。往往作為取詞的乙個標準。但是光詞頻是不夠的,下面我來介紹下「魔法師」和「了一」兩個詞。
凝合度我將**《罪惡之城》免費章節部分做為語料,總共827973個字。其中「魔法師」一詞出現了479次。下面我們假設「魔」和「法師」兩個部分是偶然出現在一起的。「魔」字出現了3768次,「法師」一詞出現了1246次。求出他們的概率。
p(魔)=3768/827973≈0.004550873
p(法師)=1246/827973≈0.00150488
那麼理論上「魔法師」出現的概率為:
p(魔法師)=p(魔)*p(法師)≈0.00000685
但是「魔法師」的實際概率為:
p2(魔法師)=479/827973≈0.00057852
可以看出「魔法師」出現的實際概率是理論概率的84倍之多(通過p2/p得到)。這樣我們不得不考慮「魔法師」中的「魔」和「法師」兩部分並非偶然組合在了一起。他們是「魔法師」一詞的一部分,緊密的凝合在一起。p2/p得到的比值84,我們就稱之為-凝合度。
下面我們在看看乙個詞語「了一」,當然這裡是假設它是詞。「了一」一詞出現了1534次,其中「了」字出現了11868次,「一」字出現14946次,我們不難算出其凝合度數為7。這說明「了」和「一」是隨機組合在一起的可能性較大。我們可以給凝合度設定為乙個閥值,如果這個值比7大,那麼「了一」將不會出現在你的結果中。
以上發現「了一」的詞頻比「魔法師」更高,而「魔法師」更像乙個詞語。所以取詞不能完全靠詞頻來判斷,還需要凝合度。
相信大家對凝合度有了一定的了解。有些同學會問:「魔法師」也可以是「魔法」和「師」的隨機組合。對,我們不能忽略這種可能。按照這種組合,計算出來的凝合度為86。這種情況下,我們取較小的值,也就是84。
資訊熵每次買可樂,開啟瓶蓋的時候,如果是謝謝惠顧,我相信大家一定很淡定。我們可以理解為抽到謝謝惠顧的概率很高,所以給我們的資訊量很低,低到不足以使得我們激動。反過來如果是再來一瓶,這時候肯定會小激動一下,同理再來一瓶的概率比謝謝惠顧小的多,所以我們得到的資訊量就比較大,固然驚訝。這種反映乙個事件之後你能獲得多少資訊量的概念我們稱之為-資訊熵。
我們再來深層次的了解資訊熵,當乙個事件發生的概率是p,那麼如果這個事件真的發生了,那麼你得到的資訊量為:-log(p)。乙個封閉的箱子裡面有5個銅幣和1個金幣。你閉上眼睛去裡面取東西,如果你拿到了銅幣,那麼你得到的資訊量h(銅)=-log(5/6)≈0.182321。如果你運氣足夠好,拿到了金幣,那麼你得到的資訊量h(金)=-log(1/6)≈1.791759。可見你得到金幣的資訊量是得到銅幣資訊量的10倍左右。當然如果箱子裡面全都是銅幣,那麼你得到的資訊量是:-log(6/6)=0,因為你已經能知道自己拿到的是銅幣,沒什麼好驚訝的,也沒有得到資訊量。
說了這麼多資訊熵的概念與計算方法,那麼它對取詞有什麼用呢?我們來看乙個句子「吃葡萄不吐葡萄皮,不吃葡萄倒吐葡萄皮」。其中「葡萄」出現了4次,我們來看看葡萄左邊出現的字有哪些:「吃、吐、吃、吐」,右邊出現的字是:「不、皮、倒、皮」。我們可以算出葡萄左邊出現字的資訊量:h(吃)=-log(1/2)≈0.69314,h(吐)=-log(1/2)≈0.69314。因此我們可以得到葡萄左邊的資訊量h(左)=h(吃)/2+h(吐)/2≈0.69314。這其實很好理解,這裡葡萄左邊出現字就兩種情況,所以資訊量h(左)=-log(1/2)≈0.69314。和上面的計算相等。同理我們計算出右邊的資訊量h(右邊)=-log(1/2)/2-log(1/4)/4-log(1/4)/4≈1.04。由此可見葡萄右邊得到的資訊量大一些,為什麼呢?因為右邊的字更加豐富一些。說到這裡,我想大家應該明白資訊熵對取詞的意義。那就是分析乙個詞語的自由組合程度。乙個好的詞語,往往有很多左鄰字和右鄰字。作為評價乙個詞語的資訊熵,我們通常取左右資訊熵中較小的那個,然後將資訊熵設定乙個閥值,我們將會篩選出更優秀的詞語。
取詞方法
我們來看這個句子「一二三」,首先我們得窮舉出其所有組合可能(並按字典排序,更好的可以把有鄰詞都放在後面用空格隔開,方便統計):
一 二
一二 三
一二三二 一 三
二三 一
三 二
這樣從頭掃瞄到尾部,我們基本上就能算出每個詞的詞頻、資訊熵、以及部分凝合度(三個字以上的詞缺少一些組合情況的凝合度)。然後將每行文字倒序後按照字典排序,再次掃瞄。我們就得到了剩下的凝合度。這樣取詞步驟就完成了。
實現方法雖然簡單,但是過程中遇到了很多問題,其中比較麻煩的莫過於排序。大文字排序:如果純粹的使用記憶體來進行排序操作,處理1m以上文字的時候就會出現記憶體溢位。這時候我們可以採用分割排序方法,將文字分割成很多份,然後對每份進行排序。然後對排序好的所有檔案進行歸併排序,這樣基本上解決了大文字排序問題。效率方面:我的mac處理10m文字的速度大概是3分鐘左右。換成伺服器機器或者使用hadoop會有質的提公升,當然這些都是後話了。
洋洋灑灑寫了這麼多,只是希望以最簡單的方式讓大家明白取詞並不困難。不得不驚嘆於數學之美,統計之霸道。同時也要感謝給予我幫助的同事,以及一些激起我靈感的文章。
中文擷取無亂碼
實現中文字串擷取無亂碼的方法 肖巖utf 8中文擷取函式 在php中,substr 函式擷取帶有中文字串的話,可能會出現亂碼,這是因為中西文乙個位元組所占有的位元組數不一樣,而substr的長度引數是按照位元組去算的,在gb2312編碼時,乙個中文佔2個位元組,英文為1個位元組,而在utf 8編碼當...
php htmlentities導致中文無法查詢
在php中htmlspecialchars,將特殊字元轉成 html 格式,而htmlentities,將所有的字元都轉成 html 字串 了,下面我來分別簡單的介紹。htmlentities用法 str john adams echo htmlentities str,ent compat ech...
20個無庫無框架的小型web專案 中文版
前一陣子看到了大火的github專案vanillawebprojects,對其中的實現有些好奇,於是研究了寫法,但是對英文有些不太熟悉的我,無法忍受這樣的純英文專案,於是花了二周時間,潛心研究了專案的實現思路,然後自己實現了這20個web專案的中文版。雖然思路 於原專案,但是很多功能我做了擴充套件,...