Alyona 經營著一家總部在紐西蘭的 NLP 諮詢公司——Entopix,她有計算語言學碩士和計算機博士學位,是主題提取工具 Maui 的作者。
視訊簡介:https://www.youtube.com/watch?v=Chwm_AFZg-c
1 引言
在這個教程中,你將會學到如何用Python和Java自動提取關鍵詞,而且你將理解與之相關的任務,例如有控制詞表的關鍵短語提取(換句話說是把文字分類到各種可能類別的超大集合中),還有術語提取。
本教程如下組織:首先,我們討論要一點背景——什麼是關鍵詞,一個關鍵詞演算法如何工作?然後我們用一個叫做Rake的Python庫舉一個簡單但在很多情況下很有用的關鍵詞提取的例子。最後,我們展示一個叫做Maui的Java工具如何用機器學習方法提取關鍵詞。
1.1 為什麼提取關鍵詞
處理文件時,提取關鍵詞是最重要的工作之一。讀者受益於關鍵詞,因為他們可以快速判斷一篇文章是否值得一讀。網站創立者從中受益,因為他們可以根據話題集中相似的內容。演算法開發者從 關鍵詞受益,因為關鍵詞降低文字維度來顯出最重要的特徵。這只是一些有幫助的例子。
根據定義,關鍵詞是一篇文件中表達的主要話題。這個術語有點迷惑,所以下面的圖片在詞彙的源頭和被每個文件的話題數量方面比較了相關的任務。
這個教程中,我們會關注兩個具體的任務並且評價它們:
- 在給定文字中出現的最重要的詞和短語
- 從與給定文字匹配的預定詞表中,識別一系列主題
如何多個文件間詞彙一致性重要,我推薦你使用一個詞表——或學科詞表、分類詞典,除非由於某些原因做不到這點。
給對文字分類(另一個做文字工作時流行的任務)有興趣的人的幾句話:如果類別的數量很大,你將會很難收集足夠的訓練集用於有監督的分類。因此,如果有一百多個類別,並且你可以給出這些類別的名字(而不是抽象類別),那麼你面臨是細粒度的分類。我們可以把這個任務看作帶有受控詞彙表的關鍵詞提取,或者術語分配。所以,讀下去,這個教程也是針對你的!
2 關鍵詞提取如何工作?
通常關鍵詞提取演算法有三個主要成分:
- 候選詞選擇:這裡,我們提取所有可能是關鍵詞的詞,片語,術語或概念(取決於任務)。
- 特性計算:對於每個候選詞,我們需要計算表示它是否為關鍵詞的特性。比如,一個候選詞
- 對關鍵詞評分並選擇:所有候選詞可以通過把各性質結合進一個公式來評分,或者用機器學習技術來決定一個候選詞是一個關鍵詞的概率。然後一個分數或概率的閾值或者對關鍵詞數量的限制用來選擇最終的關鍵詞集合。
最終,像候選詞最小頻率的引數,它的最小和最大詞長,或者用來使候選詞標準化的詞幹提取器,都有助於調整演算法對於特定資料集的效能。
3 Python下用RAKE提取關鍵詞
對於Python使用者,有一個易用的關鍵詞提取庫叫做RAKE,其全稱是 Rapid Automatic Keyword Extraction。演算法本身表述在 Michael W. Berry的 《文字挖掘和理論》一書中(免費pdf)。這裡,我們使用已有的Python實現。這裡有個修改過的版本,它使用了自然語言處理工具NLTK處理一些計算。對於這個教程,我已經 fork了原始的RAKE倉庫並且將其擴充成了RAKE教程,來使用額外的引數評價它的效能。
3.1 配置RAKE
首先你需要從這個地址獲取RAKE倉庫 https://github.com/zelandiya/RAKE-tutorial.
1 |
$ git clone https://github.com/zelandiya/RAKE-tutorial |
然後,按照rake_tutorial.py中的步驟,import RAKE,並且為這個教程的”幕後”部分import operator:
1 2 |
import rake import operator |
3.2 對一小段文字使用RAKE
首先,我們用一個通向一個停止詞表的路徑初始化RAKE並且設定一些引數。
1 |
rake_object = rake.Rake("SmartStoplist.txt", 5, 3, 4) |
現在,我們有一個提取關鍵詞的RAKE物件,其中:
- 每個詞至少有5個字元
- 每個短語至少有3個詞
- 每個關鍵詞至少在文字中出現4次
這些引數取決與你手上的文字,並且仔細選擇這些引數是很關鍵的(試著用預設引數執行這個例子你就會明白)。更多資訊在下一節。
接下來,我們已經有了儲存在一個變數中的一段文字(在這個例子中,我們從一個檔案中讀取),我們可以應用RAKE並且列印關鍵詞。
1 2 3 4 |
sample_file = open("data/docs/fao_test/w2167e.txt", 'r') text = sample_file.read() keywords = rake_object.run(text) print "Keywords:", keywords |
輸出應該看起來像這樣:
1 |
Keywords: Keywords: [('household food security', 7.711414565826329), ('indigenous groups living', 7.4), ('national forest programmes', 7.249539170506913), ('wood forest products', 6.844777265745007)... |
這裡,我們沒有每個關鍵詞的名字和它對應於這個演算法的分數。
3.3 RAKE: 幕後
這一次,我們將會使用一個短文字片段,並且我們可以在這裡使用預設引數:
1 2 3 4 5 6 7 8 9 10 |
stoppath = "SmartStoplist.txt" rake_object = rake.Rake(stoppath) text = "Compatibility of systems of linear constraints over the set of natural numbers. Criteria of compatibility " \ "of a system of linear Diophantine equations, strict inequations, and nonstrict inequations are considered. " \ "Upper bounds for components of a minimal set of solutions and algorithms of construction of minimal generating"\ " sets of solutions for all types of systems are given. These criteria and the corresponding algorithms " \ "for constructing a minimal supporting set of solutions can be used in solving all the considered types of " \ "systems and systems of mixed types." |
首先,RAKE把文字分割成句子,並且生成候選詞:
1 2 3 |
sentenceList = rake.split_sentences(text) stopwordpattern = rake.build_stop_word_regex(stoppath) phraseList = rake.generate_candidate_keywords(sentenceList, stopwordpattern) |
這裡,各種標點符號將會被認為句子邊界。大多數情況這都很有用,但是對於標點是真實短語的一部分的情況(例,.Net或Dr. Who)沒有用。
列在停止詞檔案中的所有詞將會被認為是短語邊界。這幫助我們生成包含一個或更多非停止詞的候選詞,比如這篇文字中的“compatibility”、“systems”、“linear constraints”、“set”,、“natural numbers”和“criteria”。大多數候選詞將會是有效的,但是,對於停止詞是短語的一部分的情況不會有用。例如,“new”列在RAKE的停止詞中。這意味著“New York”或“New Zealand”都不會是一個關鍵詞。
第二,RAKE 計算每個候選詞的屬性,是各候選詞的分數的總和。候選詞是怎麼打分的?根據候選詞的出現頻率,以及典型長度。
1 2 |
wordscores = rake.calculate_word_scores(phraseList) keywordcandidates = rake.generate_candidate_keyword_scores(phraseList, wordscores) |
這裡的一個問題是,候選詞並沒有標準化。所有我們可能會有看起來完全一樣的關鍵詞,比如:「small scale production 和 small scale producers」,或者 「skim milk powder 和 skimmed milk powder」。在理想情況下,關鍵詞提取演算法應首先應用於詞幹和其他標準化的關鍵詞。
最後,我們根據RAKE的分數給候選關鍵詞排序。關鍵詞然後可以既是分數排名前五的候選詞,也可以是超過一個選定分數閾值的,或者排名第三的,如同下面的例子:
1 2 3 4 5 |
sortedKeywords = sorted(keywordcandidates.iteritems(), key=operator.itemgetter(1), reverse=True) totalKeywords = len(sortedKeywords) for keyword in sortedKeywords[0:(totalKeywords / 3)]: print "Keyword: ", keyword[0], ", score: ", keyword[1] |
正常的輸出資訊如下:
1 2 3 4 5 6 7 8 |
Keyword: minimal generating sets , score: 8.66666666667 Keyword: linear diophantine equations , score: 8.5 Keyword: minimal supporting set , score: 7.66666666667 Keyword: minimal set , score: 4.66666666667 Keyword: linear constraints , score: 4.5 Keyword: upper bounds , score: 4.0 Keyword: natural numbers , score: 4.0 Keyword: nonstrict inequations , score: 4.0 |
這還有兩個有用的指令碼。第一個用有文件和他們的手工分分配的關鍵詞的目錄,和應該評價的排名靠前的關鍵詞的數量,評價了 RAKE 的準確度。例如:
1 2 3 |
$ python evaluate_rake.py data/docs/fao_test/ 10 ... Precision 4.44 Recall 5.17 F-Measure 4.78 |
精度(Precision)告訴我們在這些被提取的關鍵詞中正確的百分比,回召(Recall)告訴我們在所有正確的關鍵詞中,正確提取的百分比,F量度是兩者的結合。
為了改善RAKE的效能,我們可以執行我為這個教程準備好的另一個指令碼。它迴圈運轉,每次使用不同的引數集,並且評估每次運轉的關鍵詞的質量。然後它返回在這個資料集上效能最好的引數。例如:
1 2 3 4 5 |
$ python optimize_rake.py data/docs/fao_test/ 10 Best result at 5.56 with min_char_length 3 max_words_length 5 min_keyword_frequency 6 |
這些值表示在這樣長的文件上,RAKE最好不包括超過5個詞的候選詞,並且只考慮出現少於6詞的候選詞。
總結一下,RAKE是一個簡單的關鍵詞提取庫,它主要解決找包含頻繁詞的多詞短語。它的強大之處在於它的易用性,它的缺點是它有限的準確度,引數配置的必須,還有它拋棄很多有效短語並且不歸一化候選詞。
4 用Java寫的Maui提取關鍵詞
Maui表示多用途自動主題索引。它是一個GPL許可用Java寫的庫,它的核心是機器學習工具包Weka。它是在多年的研究後,對關鍵詞提取演算法KEA的再現。跟RAKE相比,Maui支援:
- 不僅單從文字,還可以參照受控詞表,提取關鍵詞
- 通過用手工選擇的關鍵詞訓練Maui改善準確度
4.1 配置Maui
Maui的原始碼在Github和Maven Central可以下載,但是最簡單的方法是從Github下載Maui完整jar包,然後把jar複製到RAKE-tutorial工作目錄。
4.2 Mauti:從文字提取關鍵詞
為了比較,我們把Maui用於我們之前給RAKE使用的同一段文字。然而,因為Maui需要一個訓練模型,我們首先需要建立一個訓練模型。為了訓練Maui,我們執行下列命令:
1 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar train -l data/docs/fao_train/ -m data/models/keyword_extraction_model -v none -o 2 |
這些引數解釋如下,train用來表明我們在訓練一個模型
- -I 表示文件和他們的手工關鍵詞的路徑
- -m 表示模型輸出路徑,
- -v none表示沒有詞表或者執行關鍵詞提取,
- -o 2 表示拋棄任何候選詞出現少於兩次的候選詞。
因為訓練目錄非常大,我相應增加了Java的堆空間。
一旦這個命令完成(它需要幾分鐘),我們就有了一個訓練模型並且我們可以把它用在我們的RAKE例子中的同樣文件,如下:
1 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar run data/docs/fao_test/w2167e.txt -m data/models/keyword_extraction_model -v none -n 8 |
run命令既可以用於一個檔案路徑或者一個文字串。輸出應該像這樣:
1 2 3 4 5 6 7 8 |
Keyword: food security 0.4168253968253969 Keyword: household 0.361691628264209 Keyword: food production 0.3374377787854522 Keyword: nutrition 0.2319136508627857 Keyword: household food security and nutrition 0.21550774200419887 Keyword: rural development 0.1905299881996819 Keyword: forest resources 0.13882003874950102 Keyword: trees 0.13051174278199784 |
我們可以通過執行test命令評價關鍵詞質量,它會使用Maui內建的評價:
1 2 3 4 5 6 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar test -l data/docs/fao_test/ -m data/models/keyword_extraction_model -v none -n 8 ... INFO MauiTopicExtractor - Avg. number of correct keyphrases per document: 2 +/- 1.03 INFO MauiTopicExtractor - Precision: 25 +/- 12.91 INFO MauiTopicExtractor - Recall: 26.16 +/- 15.07 INFO MauiTopicExtractor - F-Measure: 25.57 |
你可以得到顯著提升的效能,如果你在整個手工標註文件集上訓練Maui。但是確保從訓練集中去掉你用於測試的檔案。
如果你對用Maui提取術語有興趣,僅用提高概率閾值。然後記數一個文件集合中最常用的一些詞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar test -l data/docs/fao_test/ -m data/models/keyword_extraction_model -v none -n 100 $ cd data/docs/fao_test/ $ cat *.maui | sort | uniq -c | sort -n -r | head -n 20 14 FAO 11 used 11 training 11 supply 11 environment 10 market 10 developing countries 9 important 9 government 9 consumption 9 Asia 8 world 8 species 8 services |
這給出了在這些文件中使用的術語種類的一個很好的指示。
4.3 Maui:用受控詞表做關鍵詞提取
假設我們在對一個文件集合的每個文件提取關鍵詞。如果我們從文件的文字中提取關鍵詞,他們受制於不一致性。一個作者可能會談論“栽培樹林”,另一個作者會說“人造樹林”。為了對兩個文件一致地使用相同關鍵詞,用受控詞表:一個術語列表、分類詞典或者分類系統,是個好主意。用詞表的另一個好處是它包含有助於提取過程的語義。比如,通過知道文件中“栽培樹林”和“人工樹林”等候選詞是相關的,演算法可以區分有聯絡的主題與聯絡少的主題。
Maui可以與任何RDF SKOS格式的詞表工作,並且還有很多各領域的這樣的可用詞表。
在Maui中使用一個詞表很容易。使用-v來指定詞表檔案的路徑並且用-f指定它的格式,比如“skos”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar train -l data/docs/fao_train/ -m data/models/term_assignment_model -v data/vocabulary/agrovoc_en.rdf.gz -f skos $ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar run data/docs/fao_test/w2167e.txt -m data/models/term_assignment_model -v data/vocabulary/agrovoc_en.rdf.gz -f skos Keyword: Food security 0.5300188323917138 Keyword: Foods 0.4959033690020568 Keyword: Forestry 0.2994235942964757 Keyword: Forest products 0.29543300574208564 Keyword: Forest management 0.2359568256207246 Keyword: Community forestry 0.211907148358341 Keyword: Food production 0.19937233666335424 Keyword: Households 0.19101089588377723 Keyword: Forest resources 0.18298670742855433 Keyword: Natural resources 0.15789472654988765 |
這些關鍵詞的兩個主要優勢是 a)它們與一個唯一的關聯於這些短語實際含義的ID繫結,並且 b)他們對於任何由同一詞表分析的文件會是一致的。
1 2 3 4 5 6 |
$ java -Xmx1024m -jar maui-standalone-1.1-SNAPSHOT.jar test -l data/docs/fao_test/ -m data/models/term_assignment_model -v data/vocabulary/agrovoc_en.rdf.gz -f skos ... INFO MauiTopicExtractor - Avg. number of correct keyphrases per document: 2.94 +/- 1.57 INFO MauiTopicExtractor - Precision: 29.38 +/- 15.69 INFO MauiTopicExtractor - Recall: 36.18 +/- 17.6 INFO MauiTopicExtractor - F-Measure: 32.43 |
這裡,在50個文件上訓練後,我們使用預設特徵和引數測試了Maui。在更多文件上訓練並且也調整了引數後,效能可以提升35%甚至更多。
5 接下來有什麼?
我們已經學到了關鍵詞提取的核心準則並且用專門為這個任務設計的Python和Java庫實驗了。不論你使用的庫或者程式語言,你現在可以開始把這些應用到你的專案中但是如果你想繼續學習,這有一些選項:
- 瞭解更多關於Maui如何工作的問題,可以參考我的博士論文,其中詳細解釋了各個方面。在Maui的網站上也有一個文件。
- 執行更多實驗,你可以下載並且嘗試不同的關鍵詞提取資料集和詞表。
- 對比最先進的演算法評估你自己的關鍵詞提取演算法,你可以在資料集上在SemEval的關鍵詞提取測試中對它進行效能測試
如果你在上面某個中遇到問題,儘管通過AirPair網站來聯絡我。