jieba 分詞簡單應用
關鍵詞抽取就是從文本里面把跟這篇文件意義最相關的一些詞抽取出來。這個可以追溯到文獻檢索初期,當時還不支援全文搜尋的時候,關鍵詞就可以作為搜尋這篇論文的詞語。因此,目前依然可以在論文中看到關鍵詞這一項。
除了這些,關鍵詞還可以在文字聚類、分類、自動摘要等領域中有著重要的作用。比如在聚類時將關鍵詞相似的幾篇文件看成一個團簇,可以大大提高聚類演算法的收斂速度;從某天所有的新聞中提取出這些新聞的關鍵詞,就可以大致瞭解那天發生了什麼事情;或者將某段時間內幾個人的微博拼成一篇長文字,然後抽取關鍵詞就可以知道他們主要在討論什麼話題。
總之,關鍵詞就是最能夠反映出文字主題或者意思的詞語。但是網路上寫文章的人不會像寫論文那樣告訴你本文的關鍵詞是什麼,這個時候就需要利用計算機自動抽取出關鍵詞,
關鍵詞抽取從方法來說大致有兩種:
- 第一種是關鍵詞分配,就是有一個給定的關鍵詞庫,然後新來一篇文件,從詞庫裡面找出幾個詞語作為這篇文件的關鍵詞;
- 第二種是關鍵詞抽取,就是新來一篇文件,從文件中抽取一些詞語作為這篇文件的關鍵詞;
目前大多數領域無關的關鍵詞抽取演算法(領域無關演算法的意思就是無論什麼主題或者領域的文字都可以抽取關鍵詞的演算法)和它對應的庫都是基於後者的。從邏輯上說,後者比前著在實際使用中更有意義。
從演算法的角度來看,關鍵詞抽取演算法主要有兩類:
- 有監督學習演算法,將關鍵詞抽取過程視為二分類問題,先抽取出候選詞,然後對於每個候選詞劃定標籤,要麼是關鍵詞,要麼不是關鍵詞,然後訓練關鍵詞抽取分類器。當新來一篇文件時,抽取出所有的候選詞,然後利用訓練好的關鍵詞抽取分類器,對各個候選詞進行分類,最終將標籤為關鍵詞的候選詞作為關鍵詞;
- 無監督學習演算法,先抽取出候選詞,然後對各個候選詞進行打分,然後輸出topK個分值最高的候選詞作為關鍵詞。根據打分的策略不同,有不同的演算法,例如TF-IDF,TextRank等演算法;
jieba分詞系統中實現了兩種關鍵詞抽取演算法,分別是基於TF-IDF關鍵詞抽取演算法和基於TextRank關鍵詞抽取演算法,兩類演算法均是無監督學習的演算法,下面將會通過例項講解介紹如何使用jieba分詞的關鍵詞抽取介面以及通過原始碼講解其實現的原理。
2 示例
下面將會依次介紹利用jieba分詞系統中的TF-IDF及TextRank介面抽取關鍵詞的過程。
2.1 基於TF-IDF演算法進行關鍵詞抽取
基於TF-IDF演算法進行關鍵詞抽取的示例程式碼如下所示,
from jieba import analyse
# 引入TF-IDF關鍵詞抽取介面
tfidf = analyse.extract_tags
# 原始文字
text = "執行緒是程式執行時的最小單位,它是程序的一個執行流,\ 是CPU排程和分派的基本單位,一個程序可以由很多個執行緒組成,\ 執行緒間共享程序的所有資源,每個執行緒有自己的堆疊和區域性變數。\ 執行緒由CPU獨立排程執行,在多CPU環境下就允許多個執行緒同時執行。\ 同樣多執行緒也可以實現併發操作,每個請求分配一個執行緒來處理。" # 基於TF-IDF演算法進行關鍵詞抽取 keywords = tfidf(text) print "keywords by tfidf:" # 輸出抽取出的關鍵詞 for keyword in keywords: print keyword + "/",
控制檯輸出,
keywords by tfidf:
執行緒/ CPU/ 程序/ 排程/ 多執行緒/ 程式執行/ 每個/ 執行/ 堆疊/ 區域性變數/ 單位/ 併發/ 分派/ 一個/ 共享/ 請求/ 最小/ 可以/ 允許/ 分配/
2.2 基於TextRank演算法進行關鍵詞抽取
基於TextRank演算法進行關鍵詞抽取的示例程式碼如下所示,
from jieba import analyse
# 引入TextRank關鍵詞抽取介面
textrank = analyse.textrank
# 原始文字
text = "執行緒是程式執行時的最小單位,它是程序的一個執行流,\ 是CPU排程和分派的基本單位,一個程序可以由很多個執行緒組成,\ 執行緒間共享程序的所有資源,每個執行緒有自己的堆疊和區域性變數。\ 執行緒由CPU獨立排程執行,在多CPU環境下就允許多個執行緒同時執行。\ 同樣多執行緒也可以實現併發操作,每個請求分配一個執行緒來處理。" print "\nkeywords by textrank:" # 基於TextRank演算法進行關鍵詞抽取 keywords = textrank(text) # 輸出抽取出的關鍵詞 for keyword in keywords: print keyword + "/",
控制檯輸出,
keywords by textrank:
執行緒/ 程序/ 排程/ 單位/ 操作/ 請求/ 分配/ 允許/ 基本/ 共享/ 併發/ 堆疊/ 獨立/ 執行/ 分派/ 組成/ 資源/ 實現/ 執行/ 處理/
3 理論分析
下面將會依次分析TF-IDF演算法及TextRank演算法的原理。
3.1 TF-IDF演算法分析
在資訊檢索理論中,TF-IDF是Term Frequency - Inverse Document Frequency的簡寫。TF-IDF是一種數值統計,用於反映一個詞對於語料中某篇文件的重要性。在資訊檢索和文字挖掘領域,它經常用於因子加權。
TF-IDF的主要思想就是:如果某個詞在一篇文件中出現的頻率高,也即TF高;並且在語料庫中其他文件中很少出現,即DF的低,也即IDF高,則認為這個詞具有很好的類別區分能力。
TF-IDF在實際中主要是將二者相乘,也即TF * IDF,TF為詞頻(Term Frequency),表示詞t在文件d中出現的頻率;IDF為反文件頻率(Inverse Document Frequency),表示語料庫中包含詞t的文件的數目的倒數。
TF公式:
TF計算公式為,
TF=count(t)count(di)
式中,count(t)表示文件di中包含詞t的個數;
count(di)表示文件di的詞的總數;
IDF公式:
IDF計算公式為,
IDF=num(corpus)num(t)+1
式中,num(corpus)表示語料庫corpus中文件的總數;
num(t)表示語料庫corpus中包含t的文件的數目;
應用到關鍵詞抽取:
1. 預處理,首先進行分詞和詞性標註,將滿足指定詞性的詞作為候選詞;
2. 分別計算每個詞的TF-IDF值;
3. 根據每個詞的TF-IDF值降序排列,並輸出指定個數的詞彙作為可能的關鍵詞;
3.2 TextRank演算法分析
類似於PageRank的思想,將文字中的語法單元視作圖中的節點,如果兩個語法單元存在一定語法關係(例如共現),則這兩個語法單元在圖中就會有一條邊相互連線,通過一定的迭代次數,最終不同的節點會有不同的權重,權重高的語法單元可以作為關鍵詞。
節點的權重不僅依賴於它的入度結點,還依賴於這些入度結點的權重,入度結點越多,入度結點的權重越大,說明這個結點的權重越高;
TextRank迭代計算公式為,
WS(Vi)=(1−d)+d∗∑Vj∈In(Vi)wji∑Vk∈Out(Vj)wjk∗WS(Vj)
節點i的權重取決於節點i的鄰居節點中i-j這條邊的權重 / j的所有出度的邊的權重 * 節點j的權重,將這些鄰居節點計算的權重相加,再乘上一定的阻尼係數,就是節點i的權重;
阻尼係數 d 一般取0.85;
演算法通用流程:
1. 標識文字單元,並將其作為頂點加入到圖中;
2. 標識文字單元之間的關係,使用這些關係作為圖中頂點之間的邊,邊可以是有向或者無向,加權或者無權;
3. 基於上述公式,迭代直至收斂;
4. 按照頂點的分數降序排列;
-
1.本模型使用co-occurrence關係,如果兩個頂點相應的語義單元共同出現在一個視窗中(視窗大小從2-10不等),那麼就連線這兩個頂點;
-
2.新增頂點到圖中時,需要考慮語法過濾,例如只保留特定詞性(如形容詞和名詞)的詞;
應用到關鍵短語抽取:
1. 預處理,首先進行分詞和詞性標註,將單個word作為結點新增到圖中;
2. 設定語法過濾器,將通過語法過濾器的詞彙新增到圖中;出現在一個視窗中的詞彙之間相互形成一條邊;
3. 基於上述公式,迭代直至收斂;一般迭代20-30次,迭代閾值設定為0.0001;
4. 根據頂點的分數降序排列,並輸出指定個數的詞彙作為可能的關鍵詞;
5. 後處理,如果兩個詞彙在文字中前後連線,那麼就將這兩個詞彙連線在一起,作為關鍵短語;
4 原始碼分析
jieba分詞的關鍵詞抽取功能,是在jieba/analyse目錄下實現的。
其中,__init__.py主要用於封裝jieba分詞的關鍵詞抽取介面;
tfidf.py實現了基於TF-IDF演算法抽取關鍵詞;
textrank.py實現了基於TextRank演算法抽取關鍵詞;
4.1 TF-IDF演算法抽取關鍵詞原始碼分析
基於TF-IDF演算法抽取關鍵詞的主調函式是TFIDF.extract_tags函式,主要是在jieba/analyse/tfidf.py中實現。
其中TFIDF是為TF-IDF演算法抽取關鍵詞所定義的類。類在初始化時,預設載入了分詞函式tokenizer = jieba.dt、詞性標註函式postokenizer = jieba.posseg.dt、停用詞stop_words = self.STOP_WORDS.copy()、idf詞典idf_loader = IDFLoader(idf_path or DEFAULT_IDF)等,並獲取idf詞典及idf中值(如果某個詞沒有出現在idf詞典中,則將idf中值作為這個詞的idf值)。
def __init__(self, idf_path=None):
# 載入 self.tokenizer = jieba.dt self.postokenizer = jieba.posseg.dt self.stop_words = self.STOP_WORDS.copy() self.idf_loader = IDFLoader(idf_path or DEFAULT_IDF) self.idf_freq, self.median_idf = self.idf_loader.get_idf()
然後開始通過TF-IDF演算法進行關鍵詞抽取。
首先根據是否傳入了詞性限制集合,來決定是呼叫詞性標註介面還是呼叫分詞介面。例如,詞性限制集合為["ns", "n", "vn", "v", "nr"],表示只能從詞性為地名、名詞、動名詞、動詞、人名這些詞性的詞中抽取關鍵詞。
1) 如果傳入了詞性限制集合,首先呼叫詞性標註介面,對輸入句子進行詞性標註,得到分詞及對應的詞性;依次遍歷分詞結果,如果該詞的詞性不在詞性限制集合中,則跳過;如果詞的長度小於2,或者詞為停用詞,則跳過;最後將滿足條件的詞新增到詞頻詞典中,出現的次數加1;然後遍歷詞頻詞典,根據idf詞典得到每個詞的idf值,併除以詞頻詞典中的次數總和,得到每個詞的tf * idf值;如果設定了權重標誌位,則根據tf-idf值對詞頻詞典中的詞進行降序排序,然後輸出topK個詞作為關鍵詞;
2) 如果沒有傳入詞性限制集合,首先呼叫分詞介面,對輸入句子進行分詞,得到分詞;依次遍歷分詞結果,如果詞的長度小於2,或者詞為停用詞,則跳過;最後將滿足條件的詞新增到詞頻詞典中,出現的次數加1;然後遍歷詞頻詞典,根據idf詞典得到每個詞的idf值,併除以詞頻詞典中的次數總和,得到每個詞的tf * idf值;如果設定了權重標誌位,則根據tf-idf值對詞頻詞典中的詞進行降序排序,然後輸出topK個詞作為關鍵詞;
def extract_tags(self, sentence, topK=20, withWeight=False, allowPOS=(), withFlag=False): # 傳入了詞性限制集合 if allowPOS: allowPOS = frozenset(allowPOS) # 呼叫詞性標註介面 words = self.postokenizer.cut(sentence) # 沒有傳入詞性限制集合 else: # 呼叫分詞介面 words = self.tokenizer.cut(sentence) freq = {} for w in words: if allowPOS: if w.flag not in allowPOS: continue elif not withFlag: w = w.word wc = w.word if allowPOS and withFlag else w # 判斷詞的長度是否小於2,或者詞是否為停用詞 if len(wc.strip()) < 2 or wc.lower() in self.stop_words: continue # 將其新增到詞頻詞典中,次數加1 freq[w] = freq.get(w, 0.0) + 1.0 # 統計詞頻詞典中的總次數 total = sum(freq.values()) for k in freq: kw = k.word if allowPOS and withFlag else k # 計算每個詞的tf-idf值 freq[k] *= self.idf_freq.get(kw, self.median_idf) / total # 根據tf-idf值進行排序 if withWeight: tags = sorted(freq.items(), key=itemgetter(1), reverse=True) else: tags = sorted(freq, key=freq.__getitem__, reverse=True) # 輸出topK個詞作為關鍵詞 if topK: return tags[:topK] else: return tags
4.2 TextRank演算法抽取關鍵詞原始碼分析
基於TextRank演算法抽取關鍵詞的主調函式是TextRank.textrank函式,主要是在jieba/analyse/textrank.py中實現。
其中,TextRank是為TextRank演算法抽取關鍵詞所定義的類。類在初始化時,預設載入了分詞函式和詞性標註函式tokenizer = postokenizer = jieba.posseg.dt、停用詞表stop_words = self.STOP_WORDS.copy()、詞性過濾集合pos_filt = frozenset(('ns', 'n', 'vn', 'v')),視窗span = 5,(("ns", "n", "vn", "v"))表示詞性為地名、名詞、動名詞、動詞。
首先定義一個無向有權圖,然後對句子進行分詞;依次遍歷分詞結果,如果某個詞i滿足過濾條件(詞性在詞性過濾集合中,並且詞的長度大於等於2,並且詞不是停用詞),然後將這個詞之後視窗範圍內的詞j(這些詞也需要滿足過濾條件),將它們兩兩(詞i和詞j)作為key,出現的次數作為value,新增到共現詞典中;
然後,依次遍歷共現詞典,將詞典中的每個元素,key = (詞i,詞j),value = 詞i和詞j出現的次數,其中詞i,詞j作為一條邊起始點和終止點,共現的次數作為邊的權重,新增到之前定義的無向有權圖中。
然後對這個無向有權圖進行迭代運算textrank演算法,最終經過若干次迭代後,演算法收斂,每個詞都對應一個指標值;
如果設定了權重標誌位,則根據指標值值對無向有權圖中的詞進行降序排序,最後輸出topK個詞作為關鍵詞;
def textrank(self, sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False): self.pos_filt = frozenset(allowPOS) # 定義無向有權圖 g = UndirectWeightedGraph() # 定義共現詞典 cm = defaultdict(int) # 分詞 words = tuple(self.tokenizer.cut(sentence)) # 依次遍歷每個詞 for i, wp in enumerate(words): # 詞i 滿足過濾條件 if self.pairfilter(wp): # 依次遍歷詞i 之後視窗範圍內的詞 for j in xrange(i + 1, i + self.span): # 詞j 不能超出整個句子 if j >= len(words): break # 詞j不滿足過濾條件,則跳過 if not self.pairfilter(words[j]): continue # 將詞i和詞j作為key,出現的次數作為value,新增到共現詞典中 if allowPOS and withFlag: cm[(wp, words[j])] += 1 else: cm[(wp.word, words[j].word)] += 1 # 依次遍歷共現詞典的每個元素,將詞i,詞j作為一條邊起始點和終止點,共現的次數作為邊的權重 for terms, w in cm.items(): g.addEdge(terms[0], terms[1], w) # 執行textrank演算法 nodes_rank = g.rank() # 根據指標值進行排序 if withWeight: tags = sorted(nodes_rank.items(), key=itemgetter(1), reverse=True) else: tags = sorted(nodes_rank, key=nodes_rank.__getitem__, reverse=True) # 輸出topK個詞作為關鍵詞 if topK: return tags[:topK] else: return tags
其中,無向有權圖的的定義及實現是在UndirectWeightedGraph類中實現的。根據UndirectWeightedGraph類的初始化函式__init__,我們可以發現,所謂的無向有權圖就是一個詞典,詞典的key是後續要新增的詞,詞典的value,則是一個由(起始點,終止點,邊的權重)構成的三元組所組成的列表,表示以這個詞作為起始點的所有的邊。
無向有權圖新增邊的操作是在addEdge函式中完成的,因為是無向圖,所以我們需要依次將start作為起始點,end作為終止點,然後再將start作為終止點,end作為起始點,這兩條邊的權重是相同的。
def addEdge(self, start, end, weight):
# use a tuple (start, end, weight) instead of a Edge object self.graph[start].append((start, end, weight)) self.graph[end].append((end, start, weight))
執行textrank演算法迭代是在rank函式中完成的。
首先對每個結點賦予相同的權重,以及計算出該結點的所有出度的次數之和;
然後迭代若干次,以確保得到穩定的結果;
在每一次迭代中,依次遍歷每個結點;對於結點n,首先根據無向有權圖得到結點n的所有
入度結點(對於無向有權圖,入度結點與出度結點是相同的,都是與結點n相連的結點),在前面我們已經計算出這個入度結點的所有出度的次數,而它對於結點n的權值的貢獻等於它本身的權值 乘以 它與結點n的共現次數 / 這個結點的所有出度的次數 ,將各個入度結點得到的權值相加,再乘以一定的阻尼係數,即可得到結點n的權值;
迭代完成後,對權值進行歸一化,並返回各個結點及其對應的權值。
def rank(self):
ws = defaultdict(float)
outSum = defaultdict(float)
wsdef = 1.0 / (len(self.graph) or 1.0) # 初始化各個結點的權值 # 統計各個結點的出度的次數之和 for n, out in self.graph.items(): ws[n] = wsdef outSum[n] = sum((e[2] for e in out), 0.0) # this line for build stable iteration sorted_keys = sorted(self.graph.keys()) # 遍歷若干次 for x in xrange(10): # 10 iters # 遍歷各個結點 for n in sorted_keys: s = 0 # 遍歷結點的入度結點 for e in self.graph[n]: # 將這些入度結點貢獻後的權值相加 # 貢獻率 = 入度結點與結點n的共現次數 / 入度結點的所有出度的次數 s += e[2] / outSum[e[1]] * ws[e[1]] # 更新結點n的權值 ws[n] = (1 - self.d) + self.d * s (min_rank, max_rank) = (sys.float_info[0], sys.float_info[3]) # 獲取權值的最大值和最小值 for w in itervalues(ws): if w < min_rank: min_rank = w if w > max_rank: max_rank = w # 對權值進行歸一化 for n, w in ws.items(): # to unify the weights, don't *100. ws[n] = (w - min_rank / 10.0) / (max_rank - min_rank / 10.0) return ws
4.3 使用自定義停用詞集合
jieba分詞中基於TF-IDF演算法抽取關鍵詞以及基於TextRank演算法抽取關鍵詞均需要利用停用詞對候選詞進行過濾。實現TF-IDF演算法抽取關鍵詞的類TFIDF和實現TextRank演算法抽取關鍵詞的類TextRank都是類KeywordExtractor的子類。而在類KeywordExtractor,實現了一個方法,可以根據使用者指定的路徑,載入使用者提供的停用詞集合。
類KeywordExtractor是在jieba/analyse/tfidf.py中實現。
類KeywordExtractor首先提供了一個預設的名為STOP_WORDS的停用詞集合。
然後,類KeywordExtractor實現了一個方法set_stop_words,可以根據使用者指定的路徑,載入使用者提供的停用詞集合。
可以將extra_dict/stop_words.txt拷貝出來,並在檔案末尾兩行分別加入“一個”和
“每個”這兩個詞,作為使用者提供的停用詞檔案,使用使用者提供的停用詞集合進行關鍵詞抽取的例項程式碼如下,
from jieba import analyse
# 引入TF-IDF關鍵詞抽取介面
tfidf = analyse.extract_tags
# 使用自定義停用詞集合
analyse.set_stop_words("stop_words.txt") # 原始文字 text = "執行緒是程式執行時的最小單位,它是程序的一個執行流,\ 是CPU排程和分派的基本單位,一個程序可以由很多個執行緒組成,\ 執行緒間共享程序的所有資源,每個執行緒有自己的堆疊和區域性變數。\ 執行緒由CPU獨立排程執行,在多CPU環境下就允許多個執行緒同時執行。\ 同樣多執行緒也可以實現併發操作,每個請求分配一個執行緒來處理。" # 基於TF-IDF演算法進行關鍵詞抽取 keywords = tfidf(text) print "keywords by tfidf:" # 輸出抽取出的關鍵詞 for keyword in keywords: print keyword + "/",
關鍵詞結果為,
keywords by tfidf:
執行緒/ CPU/ 程序/ 排程/ 多執行緒/ 程式執行/ 執行/ 堆疊/ 區域性變數/ 單位/ 併發/ 分派/ 共享/ 請求/ 最小/ 可以/ 允許/ 分配/ 多個/ 執行/
對比章節2.1中的關鍵詞抽取結果,可以發現“一個”和“每個”這兩個詞沒有抽取出來。
keywords by tfidf:
執行緒/ CPU/ 程序/ 排程/ 多執行緒/ 程式執行/ 每個/ 執行/ 堆疊/ 區域性變數/ 單位/ 併發/ 分派/ 一個/ 共享/ 請求/ 最小/ 可以/ 允許/ 分配/
實現原理 ,這裡仍然以基於TF-IDF演算法抽取關鍵詞為例。
前面已經介紹了,jieba/analyse/__init__.py主要用於封裝jieba分詞的關鍵詞抽取介面,在__init__.py首先將類TFIDF例項化為物件default_tfidf,而類TFIDF在初始化時會設定停用詞表,我們知道類TFIDF是類KeywordExtractor的子類,而類KeywordExtractor中提供了一個名為STOP_WORDS的停用詞集合,因此類TFIDF在初始化時先將類KeywordExtractor中的STOP_WORDS拷貝過來,作為自己的停用詞集合stop_words。
# 例項化TFIDF類
default_tfidf = TFIDF()
# 例項化TextRank類
default_textrank = TextRank()
extract_tags = tfidf = default_tfidf.extract_tags
set_idf_path = default_tfidf.set_idf_path
textrank = default_textrank.extract_tags
# 使用者設定停用詞集合介面
def set_stop_words(stop_words_path): # 更新物件default_tfidf中的停用詞集合 default_tfidf.set_stop_words(stop_words_path) # 更新物件default_textrank中的停用詞集合 default_textrank.set_stop_words(stop_words_path)
如果使用者需要使用自己提供的停用詞集合,則需要呼叫analyse.set_stop_words(stop_words_path)這個函式,set_stop_words函式是在類KeywordExtractor實現的。set_stop_words函式執行時,會更新物件default_tfidf中的停用詞集合stop_words,當set_stop_words函式執行完畢時,stop_words也就是更新後的停用詞集合。我們可以做個實驗,驗證在呼叫analyse.set_stop_words(stop_words_path)函式前後,停用詞集合是否發生改變。
from jieba import analyse
import copy
# 將STOP_WORDS集合深度拷貝出來
stopwords0 = copy.deepcopy(analyse.default_tfidf.STOP_WORDS)
# 設定使用者自定停用詞集合之前,將停用詞集合深度拷貝出來 stopwords1 = copy.deepcopy(analyse.default_tfidf.stop_words) print stopwords0 == stopwords1 print stopwords1 - stopwords0 # 設定使用者自定停用詞集合 analyse.set_stop_words("stop_words.txt") # 設定使用者自定停用詞集合之後,將停用詞集合深度拷貝出來 stopwords2 = copy.deepcopy(analyse.default_tfidf.stop_words) print stopwords1 == stopwords2 print stopwords2 - stopwords1
結果如下所示,
True
set([])
False
set([u'\u6bcf\u4e2a', u'\u8207', u'\u4e86', u'\u4e00\u500b', u'\u800c', u'\u4ed6\u5011', u'\u6216', u'\u7684', u'\u4e00\u4e2a', u'\u662f', u'\u5c31', u'\u4f60\u5011', u'\u5979\u5011', u'\u6c92\u6709', u'\u57fa\u672c', u'\u59b3\u5011', u'\u53ca', u'\u548c', u'\u8457', u'\u6211\u5011', u'\u662f\u5426', u'\u90fd'])
說明:
- 沒有載入使用者提供的停用詞集合之前,停用詞集合就是類KeywordExtractor中的STOP_WORDS拷貝過來的;
- 載入使用者提供的停用詞集合之後,停用詞集合在原有的基礎上進行了擴;