今天寫篇技術硬文,看看大家喜不喜歡。網際網路每年甚至每時刻都會催生很多新詞(在新華詞典裡找不到的詞語),比如”雙一流”、”佛系”、”up主”、”大資料殺熟”等等,而做中文自然語言處理的基礎是中文分詞,中文分詞的精準度是做自然語言處理的基礎,比如(語音識別,文字傾向性分析,自動問答機器人,人工智慧寫自動文章等等)。
常用的中文分詞方法是基於詞庫性質(比如結巴分詞),如果一個詞語沒在這個詞庫裡,那分詞的時候,這個詞語是肯定分不到一起的。要保證基於詞庫方式的分詞效果好,就要持續的更新詞庫,也就是要有發現新詞的能力。
這篇就來討論發現新詞的一種演算法。
n-gram加詞頻
最原始的新詞演算法莫過於n-gram加詞頻了。簡單來說就是,從大量語料中抽取連續的字的組合片段,這些字組合片段最多包含n個字,同時統計每個字組合的頻率,按照詞頻並設定一個閾值來判斷一個字組合片段是否為詞彙。
該方法簡單處理速度快,它的缺點也很明顯,就是會把一些不是詞彙但出現頻率很高的字組合也當成詞了。
凝固度和自由度
這個演算法在文章《網際網路時代的社會語言學:基於SNS的文字資料探勘》 裡有詳細的闡述。
凝固度就是一個字組合片段裡面字與字之間的緊密程度。比如“琉璃”、“榴蓮”這樣的詞的凝固度就非常高,而“華為”、“組合”這樣的詞的凝固度就比較低。
自由度就是一個字組合片段能獨立自由運用的程度。比如“巧克力”裡面的“巧克”的凝固度就很高,和“巧克力”一樣高,但是它自由運用的程度幾乎為零,所以“巧克”不能單獨成詞。
Python實現
根據以上闡述,演算法實現的步驟如下:
1. n-gram統計字組合的頻率
如果文字量很小,可以直接用Python的dict來統計n-gram及其頻率。一段文字n-gram出來的字組合的大小大約是原始文字的(1+n)*n/2倍,字組合的數量也非常驚人。比如,“中華人民共和國”的首字n-gram是(n=5):
中
中華
中華人
中華人民
中華人民共
n-gram統計字組合頻率的同時還要統計字組合的左右鄰居,這個用來計算自由度。
如果文字量再大一些,Python的dict經常會碰到最好使用trie tree這樣的資料結構。雙陣列Trie Tree有很多非常好的開源實現,比如,cedar、darts等等。Trie Tree使用的好處是,它天然包含了字組合的右鄰居資訊,因為這個資料結構一般是字首樹。要統計左鄰居資訊時,只需要把字組合倒序放入另外一個Trie Tree即可。
使用cedar Trie Tree的時候,5-gram統計30M的文字大約使用6GB左右的記憶體。
如果文字量更大,這就要藉助硬碟了,可以使用leveldb這樣的key-value資料庫來實現。實驗下來,trie tree統計30M的文字用幾十秒,而同樣的用leveldb統計卻要6個多小時!!!應該還有比leveldb更合適的資料庫來做這件事情,有時間再說。
當然,為了發現新詞,幾十MB的文字文字足夠了。
2. 計算字組合的凝固度;
有了上述的統計結果,計算每個字組合的凝固度就比較簡單了。
首先,把字組合切分成不同的組合對,比如’abcd’可以拆成(‘a’, ‘bcd’), (‘ab’, ‘cd’), (‘abc’, ‘d’),
然後,計算每個組合對的凝固度:D(s1, s2) = P(s1s2) / (P(s1) * P(s2))
最後,取這些組合對凝固度中最小的那個為整個字組合的凝固度。
3. 計算字組合的自由度;
分別計算它的左鄰居資訊熵和右鄰居資訊熵,取其中較小的為該組合的自由度。
4. 閾值的選擇
整個過程涉及到三個閾值的選擇:
組合的詞頻:頻率很低的組合成詞的可能性很小
組合的凝固度:凝固度越大成詞的可能性越大
組合的自由度:自由度越大成詞的可能性越大
經驗值:30M文字,詞頻>200, 凝固度>10**(n-1), 自由度>1.5
小竅門:詞頻>30, 凝固度>20**(n-1)也能發現很多低頻的詞彙。
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***