TF-IDF具體算法和原理


TF-IDF算法

相關概念

  • 信息檢索(IR)中最常用的一種文本關鍵信息表示法

  • 基本信息:

    • 如果某個詞在一篇文檔中出現的頻率高,並且在語料庫中其它詞庫中其他文檔中很少出現,則認為這個詞具有很好的類別區分能力。
  • 詞頻TF:Term Frequency,衡量一個term在文檔中出現的有多頻繁

    • 平均而言,出現越頻繁的詞,其重要性可能就越高
  •  考慮到文章長度的差異,需要對詞頻做標准化

    • TF(t) = (t出現在文檔中的次數) / (文檔中的term總數)
    • TF(t) = (t出現在文檔中的次數) / (文檔中出現最多的term)
  • 逆文檔頻率IDF:Inverse Document Frequency,用於模擬在該語料的實際使用環境中,某一個term有多重要。

    • 有些詞到處都出現,但是明顯沒什么卵用。比如各種停用詞,過渡句用詞之類的。所以只看TF是沒什么用的。
    • 因此把罕見的詞的重要性(weight)提高,把常見詞的重要性降低。
  • IDF的具體算法:

    • IDF(t) = log(語料庫中的文檔總數 / (含有該term的文檔總數+1 ))
      • 加1是為了防止某term出現0次,導致結果無法計算。
  • TF-IDF = TF * IDF(相乘

    • TF-IDF與一個詞在文檔中的出現次數成正比
    • 與該詞在整個語料中的出現次數成反比

  • 缺點:
    • 簡單快速
    • 結果也比較符合實際情況
  • 缺點:

    • 單純以“詞頻”衡量一個詞的重要性,不夠全面,有時重要的詞可能出現次數並不多

    • 無法考慮詞與詞之間的相互關系

    • 這種算法無法體現詞的位置信息,出現位置靠前的詞語出現位置靠后的詞,都被視為重要性相同,這是不正確的

      • 一種解決方法是:對全文的第一段和每一段的第一句話,給與較大的權重。

  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算法的優點是簡單快速,結果比較符合實際情況。缺點是,單純以"詞頻"衡量一個詞的重要性,不夠全面,有時重要的詞可能出現次數並不多。而且,這種算法無法體現詞的位置信息,出現位置靠前的詞與出現位置靠后的詞,都被視為重要性相同,這是不正確的。(一種解決方法是,對全文的第一段和每一段的第一句話,給予較大的權重。)

TF-IDF的具體實現

  jieba,NLTK,sklearn,gensim等程序包都可以實現TF-IDF的計算。除算法細節上有差異外,更多的是數據輸入/輸出格式上的不同。

使用jieba實現TD-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實現TF-IDF算法

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, ....
顯然,直接閱讀的話,很難,這只是方便下一步的建模
'''

 

使用gensim實現TF-IDF算法

  輸出格式為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,

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM