sklearn: TfidfVectorizer 中文處理及一些使用參數
- 2019 年 10 月 30 日
- 筆記
版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/blmoistawinde/article/details/80816179
TfidfVectorizer可以把原始文本轉化為tf-idf的特徵矩陣,從而為後續的文本相似度計算,主題模型(如LSI),文本搜索排序等一系列應用奠定基礎。基本應用如:
#coding=utf-8 from sklearn.feature_extraction.text import TfidfVectorizer document = ["I have a pen.", "I have an apple."] tfidf_model = TfidfVectorizer().fit(document) sparse_result = tfidf_model.transform(document) # 得到tf-idf矩陣,稀疏矩陣表示法 print(sparse_result) # (0, 3) 0.814802474667 # (0, 2) 0.579738671538 # (1, 2) 0.449436416524 # (1, 1) 0.631667201738 # (1, 0) 0.631667201738 print(sparse_result.todense()) # 轉化為更直觀的一般矩陣 # [[ 0. 0. 0.57973867 0.81480247] # [ 0.6316672 0.6316672 0.44943642 0. ]] print(tfidf_model.vocabulary_) # 詞語與列的對應關係 # {'have': 2, 'pen': 3, 'an': 0, 'apple': 1}
但是要把它運用到中文上還需要一些特別的處理,故寫此文分享我的經驗。
第一步:分詞
中文不比英文,詞語之間有著空格的自然分割,所以我們首先要進行分詞處理,再把它轉化為與上面的document類似的格式。這裡採用著名的中文分詞庫jieba進行分詞:
import jieba text = """我是一條天狗呀! 我把月來吞了, 我把日來吞了, 我把一切的星球來吞了, 我把全宇宙來吞了。 我便是我了!""" sentences = text.split() sent_words = [list(jieba.cut(sent0)) for sent0 in sentences] document = [" ".join(sent0) for sent0 in sent_words] print(document) # ['我 是 一條 天狗 呀 !', '我 把 月 來 吞 了 ,', '我 把 日來 吞 了 ,', '我 把 一切 的 星球 來 吞 了 ,', '我 把 全宇宙 來 吞 了 。', '我 便是 我 了 !']
PS:語料來自郭沫若《天狗》。另外,由於分詞工具的不完善,也會有一些錯誤,比如這邊錯誤地把"日來"分到了一起。
第二步:建模
理論上,現在得到的document的格式已經可以直接拿來訓練了。讓我們跑一下模型試試。
tfidf_model = TfidfVectorizer().fit(document) print(tfidf_model.vocabulary_) # {'一條': 1, '天狗': 4, '日來': 5, '一切': 0, '星球': 6, '全宇宙': 3, '便是': 2} sparse_result = tfidf_model.transform(document) print(sparse_result) # (0, 4) 0.707106781187 # (0, 1) 0.707106781187 # (2, 5) 1.0 # (3, 6) 0.707106781187 # (3, 0) 0.707106781187 # (4, 3) 1.0 # (5, 2) 1.0
沒有錯誤,但有一個小問題,就是單字的詞語,如「我」、「吞」、「呀」等詞語在我們的辭彙表中怎麼都不見了呢?為了處理一些特殊的問題,讓我們深入其中的一些參數。
第三步:參數
查了一些資料以後,發現單字的問題是token_pattern這個參數搞的鬼。它的默認值只匹配長度≥2的單詞,就像其實開頭的例子中的'I'也被忽略了一樣,一般來說,長度為1的單詞在英文中一般是無足輕重的,但在中文裡,就可能有一些很重要的單字詞,所以修改如下:
tfidf_model2 = TfidfVectorizer(token_pattern=r"(?u)bw+b").fit(document) print(tfidf_model2.vocabulary_) # {'我': 8, '是': 12, '一條': 1, '天狗': 7, '呀': 6, '把': 9, '月': 13, '來': 14, '吞': 5, '了': 2, '日來': 10, '一切': 0, '的': 15, '星球': 11, '全宇宙': 4, '便是': 3}
token_pattern這個參數使用正則表達式來分詞,其默認參數為r"(?u)bww+b",其中的兩個w決定了其匹配長度至少為2的單詞,所以這邊減到1個。對這個參數進行更多修改,可以滿足其他要求,比如這裡依然沒有得到標點符號,在此不詳解了。
當然有些時候我們還是要過濾掉一些無意義的詞,下面有些別的參數也可以幫助我們實現這一目的:
1.max_df/min_df: [0.0, 1.0]內浮點數或正整數, 默認值=1.0
當設置為浮點數時,過濾出現在超過max_df/低於min_df比例的句子中的詞語;正整數時,則是超過max_df句句子。
這樣就可以幫助我們過濾掉出現太多的無意義詞語,如下面的"我"就被過濾(雖然這裡「我」的排比在文學上是很重要的)。
# 過濾出現在超過60%的句子中的詞語 tfidf_model3 = TfidfVectorizer(token_pattern=r"(?u)bw+b", max_df=0.6).fit(document) print(tfidf_model3.vocabulary_) # {'是': 8, '一條': 1, '天狗': 5, '呀': 4, '月': 9, '來': 10, '日來': 6, '一切': 0, '的': 11, '星球': 7, '全宇宙': 3, '便是': 2}
2.stop_words: list類型
直接過濾指定的停用詞。
tfidf_model4 = TfidfVectorizer(token_pattern=r"(?u)bw+b", max_df=0.6, stop_words=["是", "的"]).fit(document) print(tfidf_model4.vocabulary_) # {'一條': 1, '天狗': 5, '呀': 4, '月': 8, '來': 9, '日來': 6, '一切': 0, '星球': 7, '全宇宙': 3, '便是': 2}
3.vocabulary: dict類型
只使用特定的辭彙,其形式與上面看到的tfidf_model4.vocabulary_相同,也是指定對應關係。
這一參數的使用有時能幫助我們專註於一些詞語,比如我對本詩中表達感情的一些特定詞語(甚至標點符號)感興趣,就可以設定這一參數,只考慮他們:
tfidf_model5 = TfidfVectorizer(token_pattern=r"(?u)bw+b",vocabulary={"我":0, "呀":1,"!":2}).fit(document) print(tfidf_model5.vocabulary_) # {'我': 0, '呀': 1, '!': 2} print(tfidf_model5.transform(document).todense()) # [[ 0.40572238 0.91399636 0. ] # [ 1. 0. 0. ] # [ 1. 0. 0. ] # [ 1. 0. 0. ] # [ 1. 0. 0. ]