通常,我們使用Python 在文本中進行關鍵詞查找或替換時,會使用 re 模塊以正則的形式實現。在文本數量、文本內容、關鍵詞數量較小時,該方法能夠滿足我們程序的功能、性能需要。但當在大規模的文本或者對大量關鍵詞語料查找或者替換,re 實現方案的性能將成為瓶頸,本文我們將介紹一種新的關鍵詞搜索和替換的算法:Flashtext 算法,它是一個高效的字符搜索和替換算法。
有多高效呢?如下,是通過隨機生方式生成10000個單詞組成的文本,我們分別在該文本中查找由 0, 500, 1000, 5000, 10000, 50000, 100000, 200000, 400000 個關鍵詞組成的關鍵詞庫,我們來感受一下兩者的性能差異:
我們發現隨著關鍵詞查詢數量的增加,Flashtext 與 re 的時間消耗存在百倍乃至千倍以上的差異 。
為何存在這麼大的差異呢?Flashtext 算法的時間複雜度不依賴於查找或替換的字符的數量。如,對於一個文檔有 N 個字符,和一個有 M 個詞的關鍵詞庫,那麼時間複雜度就是 O(N) 。而正則匹配的時間複雜度是 O(M * N) 。這也是兩者在性能上的差異隨著關鍵詞數量增多而拉大的原因。
因此,在一些大數據下的內容檢索和替換,我們更傾向於選擇 Flashtext 算法 ,比如,自然語言處理領域中數據清洗是一項必須的操作。經常涉及使用標準的關鍵詞替換一些非標準的詞,如,將Javascript替換成JavaScript。或者我們需要判斷文本中是否存在JavaScript 關鍵詞等等。
接下來,就讓我們了解一下,如何使用Flashtext 實現關鍵詞的查找和替換。
FlashText
Flashtext 算法主要分為三部分,我們接下來將對每一部分進行單獨分析:
- 構建 Trie 字典
- 關鍵詞搜索
- 關鍵詞替換
構建 Trie 字典 (這部分不理解不影響我們使用Flashtext )
Flashtext 是一種基於 Trie 字典數據結構和 Aho Corasick 的算法。它的工作方式是,首先它將所有相關的關鍵詞作為輸入,使用這些關鍵詞建立一個 trie 字典。
為了構建 trie 字典,Flashtext 創建一個空的節點指向空字典。這個節點被用作所有關鍵詞的起點。我們在字典中插入一個關鍵詞。這個關鍵詞中的下一個字符在本字典中作為關鍵詞,並且這個指針需要再次指向一個空字典。這個過程不斷重複,直到我們達到單詞中的最後一個字符。當我們到達單詞的末尾時,我們插入一個特殊的字符(eot)來表示詞尾,如下:
start 和 eot 是兩個特殊的字符,用來定義關鍵詞的邊界,因此,也可知 Flashtext 只匹配完整的單詞,這個 trie 字典就是我們後面要用來搜索和替換的數據結構。
我們舉一個簡單的例子,假設我們有一個包含3個單詞的句子 「I like Python」,和一個有4個關鍵詞的語料庫 corpus = [Python,Java,J2ee,Ruby]。
Flashtext 算法將對於句子中的每一個單詞,檢查其是否在語料庫中出現,如下:
如果句子 N 個單詞,意味著需要做 N 次的循環操作。在這個例子中所需的時間步取決於句子中的單詞數。
如上,因為將文本中的每個字符串進行匹配,由於這是一個字符匹配過程,因為 start 並沒有和 l 相連,因此可以快速的跳過的I、like的匹配,這使得跳過缺失單詞的過程變得非常快。
因此,FlashText 算法不受 corpus 中關鍵詞數量的影響。
使用 Flashtext 進行搜索
我們對輸入文本中的字符進行逐個遍歷,當我們在文檔中的字符 word 匹配到字典中的
代碼示例如下:
使用 Flashtext 進行替換
Flashtext 對輸入文本中的字符進行逐個遍歷,Flashtext 先創建一個空的字符串,當字符序列中的 word 無法在 Trie 字典中找到匹配時,那麼Flashtext 就簡單的原始字符複製到返回字符串中。但當Flashtext 可以從 Trie 字典中找到匹配時,那麼Flashtext 將把匹配到的字符的標準字符複製到返回字符串中。因此,返回字符串是輸入字符串的一個副本,唯一的不同是替換了匹配到的字符序列,具體如下:
代碼示例如下:
性能比對
在本文開始,我們首先介紹了使用 re模塊與 flashtext 模塊在不同數量的關鍵詞語料庫下,兩者的耗時情況差異,具體性能比對實現的源碼如下:
輸出結果:
Flashtext 常用方法及參數說明
add_keyword
添加關鍵詞。
語法
參數
- keyword:檢索的詞。
- clean_name:顯示或要被替換為的詞(默認keywords本身),如果匹配到keyword,則會返回clean_name。
示例
add_keywords_from_dict
添加多個關鍵詞。
語法
參數
- keyword_dict:其中 key 類似於clean_name,value類似於keyword ,如果匹配到value,則會返回key。
示例:
add_keywords_from_list
添加多個關鍵詞。
語法
參數
- keyword_list:要添加的關鍵詞列表
示例
remove_keyword
刪除關鍵詞。
語法
參數
- keyword:刪除的關鍵詞。
示例
remove_keywords_from_list
刪除多個關鍵詞。
語法
參數
- keyword_list:要刪除的關鍵詞列表
示例
remove_keywords_from_dict
刪除多個關鍵詞。
語法
參數
- keyword_list:其中 key 類似於clean_name,value類似於keyword 。
示例