15分鐘搞懂TFIDF

講者:土豆

時間:2020/09/27

大綱

  • 什麼是TFIDF
  • TFIDF實際使用案例
  • BM25
  • 參考資料

什麼是TFIDF

TFIDF = TF * IDF

TF (Term Frequency)

doc1: 新莊 的 輔仁 大學 在 新莊 的 中正路 上

doc2: 台北 的 臺灣 師範 大學 在 台北 的 和平東路 上

某個字出現在單一文件中的次數(頻率)

DF (Document Frequency)

某個字出現在所有文件中的次數(頻率)

IDF (Inverse Document Frequency)

IDF = DF 改倒數

為了讓提升常見字的重要性(e.g. 中正路、忠孝東路)

加上log讓數值比較平滑

TFIDF

TFIDF = TF * IDF

輪到你囉(^.<)

請計算以下三則文件的TFIDF

點此作答

doc1: 我 喜歡 喝 綠茶

doc3: 我 不 喜歡 喝 紅茶 但 喜歡 喝 奶茶

doc2: 我 喜歡 喝 奶茶

喜歡 綠茶 紅茶 奶茶
doc1
doc2
doc3

TFIDF實際使用案例

環境建置

(如果你要跟著跑的話)

# 安裝所需套件
pip install scikit-learn pandas

# 安裝jieba斷詞器台灣繁體特化版本
pip install git+https://github.com/APCLab/jieba-tw.git

關鍵字擷取

印出每份文件tfidf值前三高的字詞

import jieba
from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer
import pandas as pd

# Step1: 斷詞
# docs = ['我喜歡喝綠茶', '我喜歡喝奶茶', '我不喜歡喝紅茶但喜歡喝奶茶']
docs = [
    '新莊的輔仁大學在新莊的中正路上', 
    '台北的臺灣師範大學在台北的和平東路上', 
    '新竹的交通大學在新竹的大學路上', 
    '嘉義的嘉義大學在嘉義的學府路上', 
    '台北的政治大學在台北的指南路上', 
    '新竹的清華大學在新竹的光復路上'
]
docs_seg = []
for doc in docs:
    seg = jieba.cut(doc)
    docs_seg.append(' '.join(seg))


# Step2: 建立計算每個term在doc有多少個
# 加入tokenizer參數,覆寫原有的tokenize程序,否則單一個中文字會被自動審略掉
# 參考 https://github.com/scikit-learn/scikit-learn/issues/7251#issuecomment-242897897
vectorizer = CountVectorizer(tokenizer=lambda x: x.split()) 
text_count_vector = vectorizer.fit_transform(docs_seg)

print('============全部字詞=============')
print(vectorizer.get_feature_names())
print()
print('==========各文件字詞統計==============')
print(text_count_vector.toarray())
print()


# Step3: 計算TFIDF
tfidf_transfomer = TfidfTransformer()
docs_tfidf = tfidf_transfomer.fit_transform(text_count_vector)

print('==============IDF============')
print(tfidf_transfomer.idf_)
print()

df = pd.DataFrame(docs_tfidf.T.toarray(), index=vectorizer.get_feature_names())
print('============TFIDF============')
print(df)
print()


# Step4: 關鍵字萃取(印出各文件tfidf數值top 3)
print('============關鍵字萃取============')
for i, vector in enumerate(docs_tfidf.toarray()):
    doc_tfidf = list(zip(vectorizer.get_feature_names(), vector))
    doc_tfidf = sorted(doc_tfidf, key=lambda x: x[1], reverse=True)
    top3_keywords = [x[0] for x in doc_tfidf[:3]]
    print('doc' + str(i+1) + ": " + '、'.join(top3_keywords))

文件查詢

計算query的tfidf與所有文件之tfidf的cosine similarity

文件查詢

code

import jieba
from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import pandas as pd

# Step1: 斷詞
# docs = ['我喜歡喝綠茶', '我喜歡喝奶茶', '我不喜歡喝紅茶但喜歡喝奶茶']
docs = [
    '新莊的輔仁大學在新莊的中正路上', 
    '台北的臺灣師範大學在台北的和平東路上', 
    '新竹的交通大學在新竹的大學路上', 
    '嘉義的嘉義大學在嘉義的學府路上', 
    '台北的政治大學在台北的指南路上', 
    '新竹的清華大學在新竹的光復路上'
]
docs_seg = []
for doc in docs:
    seg = jieba.cut(doc)
    docs_seg.append(' '.join(seg))


# Step2: 建立計算每個term在doc有多少個
# 加入tokenizer參數,覆寫原有的tokenize程序,否則單一個中文字會被自動審略掉
# 參考 https://github.com/scikit-learn/scikit-learn/issues/7251#issuecomment-242897897
vectorizer = CountVectorizer(tokenizer=lambda x: x.split()) 
text_count_vector = vectorizer.fit_transform(docs_seg)

print('============全部字詞=============')
print(vectorizer.get_feature_names())
print()
print('==========各文件字詞統計==============')
print(text_count_vector.toarray())
print()


# Step3: 計算TFIDF
tfidf_transfomer = TfidfTransformer()
docs_tfidf = tfidf_transfomer.fit_transform(text_count_vector)

print('==============IDF============')
print(tfidf_transfomer.idf_)
print()

df = pd.DataFrame(docs_tfidf.T.toarray(), index=vectorizer.get_feature_names())
print('============TFIDF============')
print(df)
print()


# Step4: 計算query與文件們的相似度,並列出前三相似之文件
query = '台北'
query_seg = ' '.join(jieba.cut(query))
query_count_vector = vectorizer.transform([query_seg])
query_tfidf = tfidf_transfomer.transform(query_count_vector)
similarities = cosine_similarity(query_tfidf, docs_tfidf).flatten()
results = zip(docs, similarities)
results = sorted(results, key=lambda x: x[1], reverse=True)

print('============計算相似度============')
print('query:', query)
print()
for i in range(3):
    print('doc' + str(i+1) + ': ', results[i][1])
    print(results[i][0])
    print()

作為機器學習之特徵(feature)

model不能直接接受字串,需轉換為數字

 

Example:

使用TFIDF作為特徵訓練情感分析的model

https://www.kaggle.com/kyen89/1-sentiment-analysis-tf-idf

BM25

改良版TFIDF,考量到文件長度問題

來源:Okapi BM25

參考資料

15分鐘搞懂TFIDF

By Sam Yang

15分鐘搞懂TFIDF

  • 742