當開發人員使用文本時,他們一般須要先清理它。有時是替換關鍵詞,就像用「JavaScript」替換「Javascript」同樣。其它時候,咱們只想知道文檔中是否提到了「JavaScript」。python
像這樣的數據清理是大多數處理文本的數據科學項目的標準任務。android
最近我有一項很是相似的工做。我在 Belong.co 擔任數據科學家,其中有一半工做是在作天然語言處理。ios
當我在咱們的文檔語料庫中訓練 Word2Vec 模型時,它最初會把同義詞當成近似術語給出。 好比「Javascripting」變成了「JavaScript」的近似術語。git
爲了解決這事,我編寫了一個正則表達式(Regex)來用標準化名稱替換全部已知的同義詞。好比將「JavaScripting」替換成「Javascript」。正則表達式解決了這一個問題,但創造了另外一個問題。github
有些人在面對一個問題時會想
「知道了,我該使用正則表達式。」如今他們有兩個問題了。正則表達式
以上引用來自 stack-exchange 的問題,說的就是我這種。算法
事實證實,若是要搜索和替換的關鍵詞數量在 100 個之內,正則表達式會很快。但個人語料庫有超過 2 萬個關鍵詞和 300 萬個文件。後端
當我對個人正則表達式代碼進行基準測試時,我發現它跑一次要花 5 天。bash
哦,恐怖
天然的解決方案是並行運行。可是,當咱們的數量級達到文檔千萬、關鍵詞十萬時,這將無濟於事。**必須有更好的方法!**我開始尋找......
我在辦公室和 Stack Overflow 上問了問 —— 收穫了一些建議。Vinay Pandey,Suresh Lakshmanan 和 Stack Overflow 指出了稱爲 Aho-Corasick 算法的美妙算法,以及 Trie 數據結構方法。我尋找已有的解決方案,但找不到多少。
因此我編寫了本身的實現,FlashText 誕生了。
在咱們瞭解什麼是 FlashText 以及它是如何工做的以前,讓咱們看看它的搜索性能:
底部的紅線是 FlashText 搜索所花費的時間
上面顯示的圖表是 1 個文檔時編譯過的正則表達式與 FlashText 的比較。隨着關鍵詞數量的增長,正則表達式所用的時間幾乎呈線性增加,但 FlashText 對此並不敏感。
這個好 :)
這是 FlashText 作替換時的計時:
底部的紅線是 FlashText 用於替換的時間
FlashText 是我在 GitHub 上開源的一個 Python 庫。它在提取關鍵詞和替換上都很高效。
要使用 FlashText,首先必須傳入一個關鍵詞列表。此列表將在內部用於構建 Trie 字典。而後傳入一個字符串,並說明是要替換仍是搜索。
**替換**
會建立一個替換了關鍵詞的新字符串。**搜索**
會返回字符串中找到的關鍵詞列表。在運行過程當中,輸入的字符串只會被掃描一遍。
如下是一位滿意的用戶對這庫的見解:
@RadimRehurek 是 @gensim_py 的建立者。
咱們試着用一個例子來理解這部分。假設咱們有一個句子,其中包含 3 個單詞 I like Python
,以及一個包含 4 個單詞 {Python,Java,J2ee,Ruby}
的語料庫。
若是咱們從語料庫中取出每一個單詞,並檢查它是否存在於句子中,則須要 4 次嘗試。
'Python' 在句子中嗎?
'Java' 在句子中嗎?
...
複製代碼
若是語料庫有 n
個詞,它就會重複 n
次。每一個搜索步驟 <word> 在句子中嗎?
都要獨自花費時間。這就是正則表達式匹配中發生的事情。
還有另外一種方法與第一種方法相反:對於句子中的每一個單詞,檢查它是否存在於語料庫中。
'I' 在語料庫中嗎?
'like' 在語料庫中嗎?
'Python' 在語料庫中嗎?
複製代碼
若是句子中有 m
個詞,它就會重複 m
次。在這種狀況下,它所花費的時間僅取決於句子中的單詞數量。而 <word> 在語料庫中嗎?
這一步可使用字典查找快速完成。
FlashText 算法基於第二種方法。其靈感來自 Aho-Corasick 算法和 Trie 數據結構。
它的工做方式是:
首先,使用語料庫建立 Trie 詞典。看起來有點像這樣:
Trie 詞典的語料庫。
用 start 和 EOT(End Of Term)表示像 空格
,標點
和 換行
這樣的單詞邊界。關鍵詞只有在其兩側都有單詞邊界時才匹配。這樣能夠防止 pineapple 匹配到 apple。
接下來咱們將輸入一個輸入字符串 I like Python
並逐個字符地搜索它。
第 1 步:<start>I<EOT> 在字典中嗎?不在。
第 2 步:<start>like<EOT> 在字典中嗎?不在。
第 3 步:<start>Python<EOT> 在字典中嗎?在。
複製代碼
<Start>Python<EOT>
在字典中。
由於這是逐個字符的匹配,因此咱們能夠很容易地在 <start>l
處就跳過 <start>like<EOT>
,由於詞典的 <start>
後面沒有 l
這個字母。這樣能夠快速跳過不在語料庫中的單詞。
FlashText 算法只遍歷輸入字符串 'I like Python' 的每一個字符。字典哪怕有高達百萬個關鍵詞,對運行時也沒有影響。這就是 FlashText 算法的真正力量。
簡單的答案:當關鍵詞數 > 500 時
對於搜索,FlashText 在大約超過 500 個關鍵詞後性能優於正則表達式。
複雜的答案:正則表達式能夠基於特殊字符搜索關鍵詞,如 ^,$,*,\d,.
,FlashText 不支持這個。
所以,若是您想像 word\dvec
這樣匹配部分詞,最好不要用 FlashText。但像 word2vec
這樣提取完整的單詞,就很是適合了。
# pip install flashtext
from flashtext.keyword import KeywordProcessor
keyword_processor = KeywordProcessor()
keyword_processor.add_keyword('Big Apple', 'New York')
keyword_processor.add_keyword('Bay Area')
keywords_found = keyword_processor.extract_keywords('I love Big Apple and Bay Area.')
keywords_found
# ['New York', 'Bay Area']
複製代碼
使用 FlashText 的簡單提取示例
您也能夠替換句子中的關鍵詞,而不是提取它。咱們用這做爲數據處理流程中的數據清理步驟。
from flashtext.keyword import KeywordProcessor
keyword_processor = KeywordProcessor()
keyword_processor.add_keyword('Big Apple', 'New York')
keyword_processor.add_keyword('New Delhi', 'NCR region')
new_sentence = keyword_processor.replace_keywords('I love Big Apple and new delhi.')
new_sentence
# 'I love New York and NCR region.'
複製代碼
使用 FlashText 的簡單替換示例
若是您認識誰在用文本數據、實體識別、天然語言處理或 Word2vec,請考慮與他們共享此博文。
這個庫對咱們很是有用,我相信它對其餘人也頗有用。
終於講完了,感謝捧場 😊
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。