詞典的存儲和搜索

好久沒更新,這是一篇長文。也是一篇比較硬的文章,比較燒腦。在硬長文里,可能很難找到這么有趣的。在有趣的文章里,可能很難找到這么硬的。這就是大半夜在寫文章的價值,不是么?嗯,大概就是這樣吧。

在自然語言處理領域,分詞是最基本的任務。不管是傳統的基于詞典的分詞算法還是現代的基于統計語言模型的分詞算法,都需要詞典作為輸入。本文介紹 Trie 算法,用來存儲詞典,并提供高效的搜索功能。

詞典的格式

這里的詞典比你書架上的現代漢語詞典要簡單很多,因為沒有釋義,只有光溜溜的一個詞在那。這也好理解,釋義是給人看的,而計算機根本就看不懂釋義。計算機頂多只會“算”,但它算的真的很快很快,快到讓人感覺它真的有智能。比如下面就是一個分詞時用到的詞典:

阿
阿巴丹
阿爸
...
...
...
做賊心虛
做主
做作

當然,基于現代的統計語言模型的分詞算法,還需要存儲詞的常用程度。所以需要對詞典里的詞做一些標注,比如:

反   1205
作戰股 1
先人后己    1
傳媒者 1

由此可見,詞典的數據其實是很簡單的,就是一個詞加上與這個詞對應的一個整形數值。在實際應用中,需要非常頻繁地查找詞典,比如在分詞算法里,我們找到兩個字后,需要判斷這兩個字是否在詞典里,如果在,就說明這是一個整體的詞,如果不在,那么就不能組成一個詞。所以詞典的格式應該要滿足快速查找的要求。那么怎么樣保存詞典以便查找速度最快呢?直接把詞典按照線性放在數組里顯然是不行的。

Trie 數據結構

Trie 的讀音和 Tree 相同,也有人讀作 Try ,是為了和 Tree 區分開。因為在 Tree 也是數據結構的一種,容易讓人誤解。

話說,怎么樣存儲詞典呢?科學家們發明了一種叫做叫 Trie 的數據結構來保存詞典:

Trie
Trie

圖一:圖片來自 wikipedia

上圖展示了 "A", "t", "to", "tea", "ted", "ten", "i", "in", "inn"。在這樣的數據結構里,要查找某個詞時,基本上和詞典的大小無關,而只與要查找的詞的大小有關。查找的速度基本上達到 O(1)。比如,我們要找 "tea" 這個詞,從開始狀態起步,我們的第一個字母是 "t" 則沿著根節點最左邊的子樹上前進到達其對應的子節點,接著是字母 "e" ,找到對應的子節點,再接著字母 "a" 找到 "tea" 這個單詞。再如,我們要找 "too" 這個單詞,在到達 "to" 這個節點時,還剩下一個 "o" 沒有消化掉。所以 "too" 這個單詞就不在上圖表示的詞典里。

從另外一個角度看這個圖,實際上這也是個確定有限狀態機(DFA - Deterministic Finite Automaton)。實際上針對 Trie 算法的實現,就是基于 DFA 的的原理進行的,所以要理解 Trie 算法,本質上需要先理解 DFA。

確定有限狀態機

有限狀態機的定義是非常嚴謹的,它包含一個五元組 (Q, Σ, δ, q0, F),其中:

  • Q 是一個有限的狀態集合
  • Σ 是一個有限的輸入事件集合
  • δ 是一個狀態轉移函數,當某個狀態遇到某個事件時,會引起狀態轉換,跳到另外一個狀態 (δ : Q × Σ → Q)
  • q0 是一個起始狀態 (q0 ∈ Q)
  • F 是一組可接受的終止狀態 (F ? Q)

不得不說,數學家這個群體還是很令人佩服的。他們把很好理解的概念抽象抽象再抽象,抽象到我等智商平平的人看不懂的程度。當然,數學家的本意并非讓我等看不懂,而是為了計算方便。另外一些人和數學家干的事正好相反,他們把很復雜的數學原理和算法,解釋得通俗易懂,讓大部分資質平平,沒經過專業訓練的人也能感受到數學之美。比如吳軍老師的《數學之美》就是這類的典范。

跑題結束,我們說回 DFA。上文講過,我們可以把 Trie 數據結構看成是一個 DFA 。那么詞典和數學家定義的 DFA 有什么關系呢?

還是以圖一為例,我們看一下對應關系:

  • Q 是有限狀態集合。上圖所有的圓圈構成了一個有限狀態集合
  • Σ 是有限的輸入事件集合。上圖中,引起狀態轉移,標注在狀態轉移線段上的字母就是輸入事件集合,t, o, e, a, d, n, A, i, n 構成輸入事件集合
  • q0 是一個起始狀態。上圖中,根節點就是起始狀態。
  • F 是一組可接受的終止狀態。上圖中,有標注數字的圓圈就代表一個可接受的終止狀態,to, tea, ted, ten, A, i, in, inn。從詞典的角度考慮,所有構成合法單詞的狀態就是可接受的終止狀態。

上述對應關系里,我們漏了狀態轉移函數。確定有限狀態機的關鍵點在狀態轉移上,如果當前狀態遇到一個確定的事件時,最多只能轉移到一個確定的狀態上,那么這就是一個確定有限狀態機,簡稱 DFA。為什么有最多一個確定狀態這一說法?因為可能沒有下一個狀態,對詞典的例子來說,如果沒有下一個狀態,就說明要查找的詞不在詞典里。比如我們要查找 "too" 這個單詞,當消耗完 "t", "o" 兩個字母后,還剩下一個 "o" ,這個時候應該還要有下一個由 "o" 觸發的確定的狀態才對,但在上圖中找不到,說明 "too" 這個單詞不在上圖表示的詞典里。

聰明的你可能會問,如果一個輸入事件,導致可能轉移到不同的狀態去,每種狀態有不同的概率,這是什么?答案是不確定有限狀態機。感興趣的搜索一下馬爾可夫鏈。等你研究完馬爾可夫鏈會發現,下一步要看隱馬爾可夫模型了。咳咳,這段文字純屬裝逼,我們還是就此打住,繼續今天的課題吧。

狀態轉移表

我們終于把 Trie 和 DFA 聯系起來了。在 DFA 里,狀態轉移函數一般使用狀態轉移表來描述。這是個二維的表格,行用來表示所有的狀態集合,列用來表示輸入事件集合。

我們來看一個最簡單的使用 Trie 描述的詞典:

           t        o
begin(0) ---> t(1) ---> to(2:F)
                   |
                   | e          a
                   ----> te(3) ---> tea(4:F)
                               |
                               | d
                               ---> ted(5:F)
                               |
                               | n
                               ---> ten(6:F)

這是本文示例圖片的一小部分,這個詞典只包含四個單詞,分別是 to, tea, ted, ten。從 DFA 的角度來看,它總共有 7 個狀態,包含一個狀態為 0 的起始狀態。輸入事件集合是 [t, o, e, a, d, n]。可接受的終止狀態集合就是上圖中標注著 ":F" 字樣的狀態,就是四個有效的單詞。那么上述詞典所代表的 DFA 的的狀態轉移表長什么樣呢?

state t o e a d n
0 1 x x x x x
1 x 2 3 x x x
2 x x x x x x
3 x x x 4 5 6
4 x x x x x x
5 x x x x x x
6 x x x x x x

表一:狀態轉移表

這就是上述 DFA 的狀態轉移表。從表中,我們可以清晰地看到。[0, 1, 2, 3, 4, 5, 6] 表示我們的 DFA 中的 7 個狀態。而 [t, o, e, a, d, n] 表示我們的輸入事件。表格中的數字表示當前狀態遇到輸入事件后能正確轉移的下一個狀態,其中 x 表示出錯,即無法轉移到下一個有效狀態。比如當狀態為 0 時,遇到 t 即可轉移到狀態 1,而遇到其他的輸入事件,則無法轉移到有效狀態。聰明如你,試著畫一下圖一的狀態轉移表吧。

雙數組 Trie

狀態轉移表很好很強大,可以實現 O(1) 的搜索速度。但其不足非常明顯,如表一所示,表中 x 非常多。即針對詞典來說,其 DFA 的狀態轉移表中,有效的狀態轉移只占少數,大部分都是無效的狀態轉移。聰明的科學家們哪能放過這個揚名立萬的機會?特別是在計算機的早期,內存非常寶貴,往往以字節計算價格,哪像現在,各位的手機動不動就有幾個 G 的內存。

在 1985 年科學家發明了一個壓縮算法,可以用三個數組即可表達狀態轉移表。1989 年更進一步壓縮到只用兩個數組即可表達狀態轉移表。詳細信息可以閱讀 An Implementation of Double-Array Trie。這個頁面里包含兩個實現,一個是早期的使用 C++ 模板類的實現,稱為 midatrie ,網上比較著名的開源分詞算法庫 LibMMSeg 就使用這一實現。另外一個實現是使用 C 語言重寫的,稱為 libdatrie,它更通用,可讀性也比較強。

要使用兩個數組實現 Tire 并非簡單的事情,要理解這一過程沒有燒些腦細胞估計很難做到,除非你是個天才。除此之外,還有一些細節需要了解,對理解代碼不無益處:

  • 上文討論中,我們都使用英文作為詞典保存對象。輸入事件集合就是一個個字母。實際上,中文的處理方法類似,如果是 utf-8 編碼,中文會把一個字拆成三個字節,然后以字節為單位,作為輸入事件集合中的一個元素。比如“中文”這兩個字的 utf-8 編碼是 E4 B8 AD E6 96 87,其實際上被解讀為 6 個輸入事件。由此可見,對任何語言,輸入事件總數不會超過 255 個。可以使用一個字節來表示。
  • 輸入事件并非連續的,所以在實現 Tire 時,一般會有個映射關系,比如把 a 映射到 1,把 b 映射到 2 等。這是為了更進一步節省空調。

應用場景

Trie 構造的詞典除了在分詞算法里使用外,在自動完成 Auto Complete,拼寫糾正等領域都有非常廣泛的應用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容

  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,043評論 6 13
  • 層次化的隱馬爾可夫模型 在自然語言處理等應用中,由于處理序列具有遞歸特性,尤其當序列長度比較大時,HMM的復雜度將...
    我偏笑_NSNirvana閱讀 6,721評論 1 15
  • 常用概念: 自然語言處理(NLP) 數據挖掘 推薦算法 用戶畫像 知識圖譜 信息檢索 文本分類 常用技術: 詞級別...
    御風之星閱讀 9,226評論 1 25
  • 溢思得瑞集團成立于2013年,它是一個非常年輕而且充滿活力的企業。我加入溢思得瑞集團已經2年不到的時間,在大公司里...
    亮晶昌閱讀 509評論 1 1
  • 簡書連載風云錄薔薇小說目錄擇一世長安專題擇一世長安【目錄】 文丨薔薇下的陽光 上一章丨《第六十二章:迷之身份》 前...
    薔薇下的陽光閱讀 727評論 4 12