TF-IDF 程式學習紀錄

TF-IDF 是一種在文字分析領域中用來評估一個關鍵字在一組文檔集合中對一份文檔關聯程度的技術,很常用於資訊檢索的任務,找出與關鍵字最為匹配的文檔。其核心數學公式可分為兩部份:

  • TF (Term Frequency)
    表示詞頻,即待測量的關鍵詞在一個文檔中出現的次數除以該文檔中的所有的單詞數,公式如下所示。TF 的值越高表示該關鍵詞與該文檔越為匹配。其中,分母的部分除以文檔中所有字數是為了文檔的正規化,防止文字數過多的文檔主宰了主要的權重。

  • IDF (Inverse Document Frequency)
    即文檔的總數除以所有出現該關鍵詞的文檔數,公式如下。即關鍵詞越集中出現在某幾個文檔則關聯分數越高,若關鍵詞很普遍地出現在各個文檔中,則表示該關鍵詞的重要性越低。

最後以上兩者相乘就是 TF-IDF 的最終公式了,公式如下所示。

接下來是程式實作,以下就是我 python 程式碼關鍵部分。裡面第 4 行 IDF 分母加 1 是為了防止出現0而造成無窮大的情況。

1
2
3
4
5
def compute_tf_idf(self, word, document):
freq_dict = self._document_to_freq_dict(document)
tf = freq_dict.get(word, 0) / sum(freq_dict.values())
idf = math.log10(len(self.documents) / (1 + self._count_doc_freq_by_word(word)))
return tf * idf

輸入文檔的部分則以 python list 表示,每個元素表示一個文檔,先以簡單的英文文字示範,文檔如下。

1
2
3
4
5
6
7
documents = [
"I want to adopt the dog",
"a apple a day keeps doctor away",
"I have a pen I have an apple",
"who is your daddy",
"daddy daddy daddy daddy daddy"
]

這次要評估的關鍵詞為 daddy,很明顯的一定是第五個文檔的 TF-IDF 值最高,再來第四個文檔次之,以下就來執行主程式來驗證評估的成果,主程式如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import math

class TFIDFService:
def __init__(self):
self.documents = None

def set_documents(self, documents):
self.documents = documents

def _document_to_freq_dict(self, document):
return dict([(word, document.count(word)) for word in document])

def _count_doc_freq_by_word(self, word):
doc_count = 0
for document in self.documents:
if word in document:
doc_count += 1
return doc_count

def compute_tf_idf(self, word, document):
freq_dict = self._document_to_freq_dict(document)
tf = freq_dict.get(word, 0) / sum(freq_dict.values())
idf = math.log10(len(self.documents) / (1 + self._count_doc_freq_by_word(word)))
return tf * idf

def tokenize(documents):
documents = [document.split(" ") for document in documents]
return documents

def main():
documents = [
"I want to adopt the dog",
"a apple a day keeps doctor away",
"I have a pen I have an apple",
"who is your daddy",
"daddy daddy daddy daddy daddy"
]
# 分詞前處理
documents = tokenize(documents)

tf_idf = TFIDFService()
tf_idf.set_documents(documents)
for index, doc in enumerate(documents, 1):
tf_idf_value = tf_idf.compute_tf_idf("daddy", doc)
print("document{}'s tf-idf: {}".format(index, tf_idf_value))

if __name__ == "__main__":
main()

執行的結果如下,果然就如預期一樣,第五個文檔 TF-IDF 值是最高的,第四個文檔次之,而第 1 到第 3 個文檔沒有出現關鍵字所以是 0,以上就是 TF-IDF 的實作成果了。

1
2
3
4
5
document1's tf-idf: 0.0
document2's tf-idf: 0.0
document3's tf-idf: 0.0
document4's tf-idf: 0.0554621874040891
document5's tf-idf: 0.2218487496163564

reference

  1. https://zh.wikipedia.org/wiki/Tf-idf
  2. https://github.com/Mark1002/nlp-experiment