信息檢索(IR)中最經常使用的一種文本關鍵信息表示法算法
基本信息:函數
詞頻TF:Term Frequency,衡量一個term在文檔中出現的有多頻繁spa
考慮到文章長度的差別,須要對詞頻作標準化code
逆文檔頻率IDF:Inverse Document Frequency,用於模擬在該語料的實際使用環境中,某一個term有多重要。orm
IDF的具體算法:blog
TF-IDF = TF * IDF(相乘)排序
缺點:token
單純以「詞頻」衡量一個詞的重要性,不夠全面,有時重要的詞可能出現次數並很少文檔
沒法考慮詞與詞之間的相互關係it
這種算法沒法體現詞的位置信息,出現位置靠前的詞語出現位置靠後的詞,都被視爲重要性相同,這是不正確的
一種解決方法是:對全文的第一段和每一段的第一句話,給與較大的權重。
TF(Term Frequency,縮寫爲TF)也就是詞頻啦,即一個詞在文中出現的次數,統計出來就是詞頻TF,顯而易見,一個詞在文章中出現不少次,那麼這個詞確定有着很大的做用,可是咱們本身實踐的話,確定會看到你統計出來的TF 大都是一些這樣的詞:‘的’,‘是’這樣的詞,這樣的詞顯然對咱們的分析和統計沒有什麼幫助,反而有的時候會干擾咱們的統計,固然咱們須要把這些沒有用的詞給去掉,如今有不少能夠去除這些詞的方法,好比使用一些停用詞的語料庫等。
假設咱們把它們都過濾掉了,只考慮剩下的有實際意義的詞。這樣又會遇到了另外一個問題,咱們可能發現"中國"、"蜜蜂"、"養殖"這三個詞的出現次數同樣多。這是否是意味着,做爲關鍵詞,它們的重要性是同樣的?
顯然不是這樣。由於"中國"是很常見的詞,相對而言,"蜜蜂"和"養殖"不那麼常見。若是這三個詞在一篇文章的出現次數同樣多,有理由認爲,"蜜蜂"和"養殖"的重要程度要大於"中國",也就是說,在關鍵詞排序上面,"蜜蜂"和"養殖"應該排在"中國"的前面。
因此,咱們須要一個重要性調整係數,衡量一個詞是否是常見詞。若是某個詞比較少見,可是它在這篇文章中屢次出現,那麼它極可能就反映了這篇文章的特性,正是咱們所須要的關鍵詞。
用統計學語言表達,就是在詞頻的基礎上,要對每一個詞分配一個"重要性"權重。最多見的詞("的"、"是"、"在")給予最小的權重,較常見的詞("中國")給予較小的權重,較少見的詞("蜜蜂"、"養殖")給予較大的權重。這個權重叫作"逆文檔頻率"(Inverse Document Frequency,縮寫爲IDF),它的大小與一個詞的常見程度成反比。
知道了"詞頻"(TF)和"逆文檔頻率"(IDF)之後,將這兩個值相乘,就獲得了一個詞的TF-IDF值。某個詞對文章的重要性越高,它的TF-IDF值就越大。因此,排在最前面的幾個詞,就是這篇文章的關鍵詞。
給出具體的公式:
考慮到文章有長短之分,爲了便於不一樣文章的比較,進行"詞頻"標準化。
或者
2.計算逆文檔頻率IDF
須要一個語料庫(corpus),用來模擬語言的使用環境。
若是一個詞越常見,那麼分母就越大,逆文檔頻率就越小越接近0。分母之因此要加1,是爲了不分母爲0(即全部文檔都不包含該詞)。log表示對獲得的值取對數。
3.計算TF-IDF
能夠看到,TF-IDF與一個詞在文檔中的出現次數成正比,與該詞在整個語言中的出現次數成反比。因此,自動提取關鍵詞的算法就很清楚了,就是計算出文檔的每一個詞的TF-IDF值,而後按降序排列,取排在最前面的幾個詞。
仍是以《中國的蜜蜂養殖》爲例,假定該文長度爲1000個詞,"中國"、"蜜蜂"、"養殖"各出現20次,則這三個詞的"詞頻"(TF)都爲0.02。而後,搜索Google發現,包含"的"字的網頁共有250億張,假定這就是中文網頁總數。包含"中國"的網頁共有62.3億張,包含"蜜蜂"的網頁爲0.484億張,包含"養殖"的網頁爲0.973億張。則它們的逆文檔頻率(IDF)和TF-IDF以下:
從上表可見,"蜜蜂"的TF-IDF值最高,"養殖"其次,"中國"最低。(若是還計算"的"字的TF-IDF,那將是一個極其接近0的值。)因此,若是隻選擇一個詞,"蜜蜂"就是這篇文章的關鍵詞。
除了自動提取關鍵詞,TF-IDF算法還能夠用於許多別的地方。好比,信息檢索時,對於每一個文檔,均可以分別計算一組搜索詞("中國"、"蜜蜂"、"養殖")的TF-IDF,將它們相加,就能夠獲得整個文檔的TF-IDF。這個值最高的文檔就是與搜索詞最相關的文檔。
TF-IDF算法的優勢是簡單快速,結果比較符合實際狀況。缺點是,單純以"詞頻"衡量一個詞的重要性,不夠全面,有時重要的詞可能出現次數並很少。並且,這種算法沒法體現詞的位置信息,出現位置靠前的詞與出現位置靠後的詞,都被視爲重要性相同,這是不正確的。(一種解決方法是,對全文的第一段和每一段的第一句話,給予較大的權重。)
jieba,NLTK,sklearn,gensim等程序包均可以實現TF-IDF的計算。除算法細節上有差別外,更多的是數據輸入/輸出格式上的不一樣。
輸出結果會自動按照TF-IDF值降序排列,而且直接給出的是詞條而不是字典ID,便於閱讀使用。
可在計算TF-IDF時直接完成分詞,並使用停用詞表和自定義詞庫,很是方便。(直接傳入句子,不須要提早切分詞)
有默認的IDF語料庫,能夠不訓練模型,直接進行計算
以單個文本爲單位進行分析。
jieba核心是拿到關鍵詞自己
jieba.analyse.extract_tags( sentence 爲待提取的文本 topK = 20 : 返回幾個 TF/IDF 權重最大的關鍵詞 withWeight = False : 是否一併返回關鍵詞權重值 allowPOS = () : 僅包括指定詞性的詞,默認值爲空,即不篩選 )
jieba.analyse.set_idf_path(file_name)
jieba.analyse.set_stop_words(file_name)
關鍵詞提取時使用自定義中止詞(Stop Words)語料庫
勞動防禦 13.900677652
生化學 13.900677652
奧薩貝爾 13.900677652
奧薩貝爾 13.900677652
考察隊員 13.900677652
jieba.analyse.TFIDF(idf_path = None)
新建 TFIDF模型實例 idf_path : 讀取已有的TFIDF頻率文件(即已有模型) 使用該實例提取關鍵詞:TFIDF實例.extract_tags()
# 使用jieba提取關鍵詞, import jieba import jieba.analyse # 注意:函數時在使用默認的TFIDF模型進行分析 t = jieba.analyse.extract_tags(chapter.txt[1]) print(t) # ['楊鐵心', '包惜弱', '郭嘯天', '顏烈', '丘處機', '武官', '楊二人', '官兵', ...] print("返回權重值") # 要求返回權重值 t = jieba.analyse.extract_tags(chapter.txt[1],withWeight=True) print(t) # [('楊鐵心', 0.21886511509515091), ('包惜弱', 0.1685852913570757), ('郭嘯天', 0.09908082913091291),...]
按照關鍵詞評分的重要性排序的結果。
t = jieba.analyse.extract_tags(chapter.txt[1])
print(t) # ['楊鐵心', '包惜弱', '郭嘯天', '顏烈', '丘處機', '武官', '楊二人', '官兵', ...]
若是想要進一步知道關鍵詞的具體評分值,加上withWeight=True
# 要求返回權重值 t = jieba.analyse.extract_tags(chapter.txt[1],withWeight=True) print(t) # [('楊鐵心', 0.21886511509515091), ('包惜弱', 0.1685852913570757), ('郭嘯天', 0.09908082913091291),...]
應用自定義詞典改善分詞效果
# 應用自定義詞典改善分詞效果 jieba.load_userdict('金庸小說詞庫.txt') # dict爲自定義詞典的路徑 # 在TFIDF計算中直接應用停用詞表 jieba.analyse.set_stop_words('停用詞.txt') Tfres = jieba.analyse.extract_tags(chapter.txt[1],withWeight=True) print(Tfres[:10]) # [('楊鐵心', 0.24787133516800222), ('包惜弱', 0.1909279203321098), ('郭嘯天', 0.11221202335308209)...]
使用自定義的TFIDF頻率文件
#------------------------------------------------------------------------------------------------- # 結巴分詞有一個默認的TFIDF權重表,或者說有一個現成的模型,能夠用該模型直接用於這個語料的計算中。可是這樣的準確率不高 # 正確的作法是:咱們應該把射鵰英雄傳全書拿來,作出一個TFIDF的權重頻率模型,而後把它讀進來, # 以下,咱們是用自定義的TFIDF頻率文件‘idf.txt.big’ # 使用自定義TFIDF頻率文件 jieba.analyse.set_idf_path('idf.txt.big') TFres1 = jieba.analyse.extract_tags(chapter.txt[1],withWeight=True) print('使用自定義TFIDF頻率文件') print(TFres1[:10])
# [('楊鐵心', 0.24787133516800222), ('包惜弱', 0.1909279203321098), ('郭嘯天', 0.11221202335308209)...]
sklearn輸出格式爲矩陣,直接爲後續的sklearn建模服務
須要先使用背景語料庫進行模型訓練。
結果給出的是字典ID而不是具體的詞條,直接閱讀比較困難
class sklearn.feature_extraction.text.TfidfTransformer()
參數基本和上面同樣
# 使用sklearn實現TD-IDF算法 ''' sklearn輸出格式爲矩陣,直接爲後續的sklearn建模服務 須要先使用背景語料庫進行模型訓練。 結果給出的是字典ID而不是具體的詞條,直接閱讀比較困難 class sklearn.feature_extraction.text.TfidfTransformer() 參數基本和上面同樣 ''' from sklearn.feature_extraction.text import TfidfTransformer # sklearn不能直接切中文句子,因此咱們須要提早作好分詞,用空格分開 ,(取前5章) txtlist = [" ".join(m_cut(w)) for w in chapter.txt.iloc[:5]] vectorizer = CountVectorizer() X = vectorizer.fit_transform(txtlist)# 將文本中的詞語轉換爲詞頻矩陣 transformer = TfidfTransformer() tfifd = transformer.fit_transform(X) #基於詞頻矩陣X計算TF-IDF值 print("*"*100) print(tfifd) ''' (0, 11621) 0.0056535238362054275 (0, 11614) 0.0056535238362054275 (0, 11613) 0.0056535238362054275 (0, 11612) 0.010775737599046372 ......... ''' print("*"*100) t = tfifd.toarray() print(t) ''' [[0. 0. 0. ... 0.00565352 0. 0. ] [0. 0. 0. ... 0. 0. 0. ] [0.01961759 0. 0. ... 0. 0. 0. ] [0. 0.00666239 0. ... 0. 0. 0. ] [0. 0. 0.00652369 ... 0. 0.00652369 0.00652369]] ''' print("*"*100) # 將稀疏矩陣轉換爲標準矩陣 t = tfifd.todense() print(t) ''' [[0. 0. 0. ... 0.00565352 0. 0. ] [0. 0. 0. ... 0. 0. 0. ] [0.01961759 0. 0. ... 0. 0. 0. ] [0. 0.00666239 0. ... 0. 0. 0. ] [0. 0. 0.00652369 ... 0. 0.00652369 0.00652369]] ''' print(t.shape) #(5, 11624) print("字典長度:",len(vectorizer.vocabulary_)) # 字典長度: 11624 print("字典:") print(vectorizer.vocabulary_) ''' {'第一回': 8722, '風雪': 11320, '驚變': 5284, '錢塘江': 10872, '浩浩': 7520, '江水': 7363, '日日夜夜': 6537, .... 顯然,直接閱讀的話,很難,這只是方便下一步的建模 '''
輸出格式爲list,目的也是爲後續的建模分析服務。
須要先使用背景語料庫進行模型訓練。
結果中給出的是字典ID,而不是具體的詞條(jieba給出的是具體詞條),直接閱讀結果比較困難。
# 使用gensim實現TF-IDF算法 ''' 輸出格式爲list, 目的也是爲了後續的建模服務 須要先使用背景語料庫進行模型訓練 結果中給出的是字典ID,而不是具體詞條,直接閱讀比較困難 ''' # 文檔分詞以及預處理 (取前5章) gensim能夠直接使用切好的分詞列表(list),而沒必要用空格隔開 chaplist = [m_cut(w) for w in chapter.txt.iloc[:5]] print("*"*100) print(chaplist) # [['第一回', '風雪', '驚變', '錢塘江', '浩浩', '江水', '日日夜夜', '無窮', '無休', '浙... #導入2個模塊,一個是語料庫,一個是建模的 from gensim import corpora, models # 生成文檔對應的字典和bow稀疏向量 dictionary = corpora.Dictionary(chaplist) # 語料庫 將chaplist中每一個成員都轉爲bow稀疏向量 corpus = [dictionary.doc2bow(text) for text in chaplist] #仍爲 list in list print("&"*100) print(corpus) # [詞ID,詞頻] 直接閱讀很難受 # [[(0, 1), (1, 32), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1),...]] # 建模 # 針對corpus創建TD-IDF模型,這是個總模型,包含了對整個文檔建模的背景信息 tfifd_model = models.TfidfModel(corpus) # 對所需文檔計算TF-IDF結果 用剛剛創建的tfidf_model模型對corpus語料進行計算 corpus_tfidf = tfifd_model[corpus] print("*"*100) print(corpus_tfidf) # <gensim.interfaces.TransformedCorpus object at 0x1F5554C0> # 列出所需文檔的TF-IDF計算結果 輸出第四章結果 t = corpus_tfidf[3] print(t) # List結果,,[詞ID,詞權重] # [(11, 0.00404720107824102), (12, 0.003535871261991013), (13, 0.0017679356309955065), # 列出字典內容 # 這樣能夠根據詞ID,來對應上面的TF-IDF計算結果 print(dictionary.token2id) # {'一兩天': 0, '一個': 1, '一個個': 2, '一個二十': 3, '一個多': 4, '一個月': 5, '一了百了': 6, '一事無成': 7, '一人': 8, '一件': 9,