餘弦相似度

講者:Chia

日期:Oct. 10th, 2020

目次

  • 什麼是「向量」、「向量內積」?

  • 「餘弦相似度」又是什麼?

  • 手刻程式碼

  • 應用範例:計算文章的相似度

  • 參考資料

什麼是「向量」?

同時兼具方向和距離大小的移動量

分量

分量

什麼是「向量內積」?

三角函數

什麼是「向量內積」?

透過向量內積,我們希望求得什麼?

計算出兩向量夾角的度數。

|| A ||

|| B ||

內積公式:            ab = |a| |b| cosθ

鄰邊的長度

「餘弦相似度」又是什麼?

  • Cosine Similarity
    • 一種相當常見,用來計算文本相似度的方法。
    • 目標:求得『兩個向量』之間的夾角 θ

夾角越小,即

cos( θ ) 接近 1

代表兩向量越相像。

= cos( θ )

「餘弦相似度」又是什麼?

總而言之,

 

餘弦相似度 (Cosine Similarity) 就是

計算兩個 n 維向量間的相似程度

特徵擷取演算法

(Ex: TF-IDF)

+    餘弦相似度計算

How to use?

手刻程式碼

# Vectors
vec_a = [1, 2, 3, 4, 5]
vec_b = [1, 3, 5, 7, 9]

# Dot and norm
dot = sum(a*b for a, b in zip(vec_a, vec_b))
norm_a = sum(a*a for a in vec_a) ** 0.5
norm_b = sum(b*b for b in vec_b) ** 0.5

# Cosine similarity
cos_sim = dot / (norm_a*norm_b)

# Results
print('My version:', cos_sim)
# My version: 0.9972413740548081

手刻程式碼

from sklearn.metrics.pairwise import cosine_similarity

# Vectors
vec_a = [1, 2, 3, 4, 5]
vec_b = [1, 3, 5, 7, 9]

# Results
print('Scikit-Learn:', cosine_similarity([vec_a], [vec_b]))
# Scikit-Learn: [[0.99724137]]

sklearn 版

應用範例:計算文章的相似度

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

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

應用範例:計算文章的相似度

  • 使用 jieba 斷詞,設置自定義的「停用詞」字典
  • 使用 TF-IDF 找出文章的關鍵詞,生成各文章的 TF-IDF 向量
  • 計算兩兩文章向量的餘弦相似度,值越大,就表示越相似

設計說明

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

def stopword(docs_seg_list):
    stopWords = []
    with open('stopWords.txt', 'r', encoding='UTF-8') as file:
        for data in file.readlines():
            data = data.strip()
            stopWords.append(data)

    new_docs_seg = []
    for doc in docs_seg_list:
        doc = doc.split()
        remainderWords = list(filter(lambda a: a not in stopWords and a != '\n', doc))
        # print(remainderWords)
        new_docs_seg.append(' '.join(remainderWords))
        # print(new_docs_seg)
    return new_docs_seg


# Step1: 已斷詞的文本
docs = ["圖書館有提供掃描服務嗎?", "圖書館有提供影印及列印服務嗎?", "圖書館裡面可以吃東西嗎?"]
docs_seg = []
for doc in docs:
    seg = jieba.cut(doc)
    # seg = stopword(seg)
    docs_seg.append(' '.join(seg))
print(docs_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)
tf_vector = text_count_vector.toarray()


# Step3: 計算TFIDF
tfidf_transfomer = TfidfTransformer()
docs_tfidf = tfidf_transfomer.fit_transform(text_count_vector)
df = pd.DataFrame(docs_tfidf.T.toarray(), index=vectorizer.get_feature_names())


# Step4: TF-IDF + 餘弦相似度
print('============TF-IDF + 餘弦相似度============')
print('文本:\n{}\t{}\nTF-IDF + 餘弦相似度:{}\n'.format(docs[0], docs[1], cosine_similarity([df[0]], [df[1]])))
print('文本:\n{}\t{}\nTF-IDF + 餘弦相似度:{}\n'.format(docs[0], docs[2], cosine_similarity([df[0]], [df[2]])))
print('文本:\n{}\t{}\nTF-IDF + 餘弦相似度:{}\n'.format(docs[1], docs[2], cosine_similarity([df[1]], [df[2]])))

應用範例:找出相似的文章

參考資料

Cosine Similarity

By BessyHuang

Cosine Similarity

  • 598