用Python進行簡單的文本相似度分析

  • 2020 年 1 月 13 日
  • 筆記

學習目標:

  1. 利用gensim包分析文檔相似度
  2. 使用jieba進行中文分詞
  3. 了解TF-IDF模型

環境:

Python 3.6.0 |Anaconda 4.3.1 (64-bit)

工具:

jupyter notebook

註:為了簡化問題,本文沒有剔除停用詞「stop-word」。實際應用中應該要剔除停用詞。

首先引入分詞API庫jieba、文本相似度庫gensim

import jieba  from gensim import corpora,models,similarities

以下doc0-doc7是幾個最簡單的文檔,我們可以稱之為目標文檔,本文就是分析doc_test(測試文檔)與以上8個文檔的相似度。

doc0 = "我不喜歡上海"  doc1 = "上海是一個好地方"  doc2 = "北京是一個好地方"  doc3 = "上海好吃的在哪裡"  doc4 = "上海好玩的在哪裡"  doc5 = "上海是好地方"  doc6 = "上海路和上海人"  doc7 = "喜歡小吃"  doc_test="我喜歡上海的小吃"

分詞

首先,為了簡化操作,把目標文檔放到一個列表all_doc中。

all_doc = []  all_doc.append(doc0)  all_doc.append(doc1)  all_doc.append(doc2)  all_doc.append(doc3)  all_doc.append(doc4)  all_doc.append(doc5)  all_doc.append(doc6)  all_doc.append(doc7)

以下對目標文檔進行分詞,並且保存在列表all_doc_list中

all_doc_list = []  for doc in all_doc:      doc_list = [word for word in jieba.cut(doc)]      all_doc_list.append(doc_list)

把分詞後形成的列表顯示出來:

print(all_doc_list)

[[『我』, 『不』, 『喜歡』, 『上海』], [『上海』, 『是』, 『一個』, 『好』, 『地方』], [『北京』, 『是』, 『一個』, 『好』, 『地方』], [『上海』, 『好吃』, 『的』, 『在』, 『哪裡』], [『上海』, 『好玩』, 『的』, 『在』, 『哪裡』], [『上海』, 『是』, 『好』, 『地方』], [『上海』, 『路』, 『和』, 『上海』, 『人』], [『喜歡』, 『小吃』]]

以下把測試文檔也進行分詞,並保存在列表doc_test_list中

doc_test_list = [word for word in jieba.cut(doc_test)]  doc_test_list

[『我』, 『喜歡』, 『上海』, 『的』, 『小吃』]

製作語料庫

首先用dictionary方法獲取詞袋(bag-of-words)

dictionary = corpora.Dictionary(all_doc_list)

詞袋中用數字對所有詞進行了編號

dictionary.keys()

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]

編號與詞之間的對應關係

dictionary.token2id

{『一個』: 4, 『上海』: 0, 『不』: 1, 『人』: 14, 『北京』: 8, 『和』: 15, 『哪裡』: 9, 『喜歡』: 2, 『在』: 10, 『地方』: 5, 『好』: 6, 『好吃』: 11, 『好玩』: 13, 『小吃』: 17, 『我』: 3, 『是』: 7, 『的』: 12, 『路』: 16}

以下使用doc2bow製作語料庫

corpus = [dictionary.doc2bow(doc) for doc in all_doc_list]

語料庫如下。語料庫是一組向量,向量中的元素是一個二元組(編號、頻次數),對應分詞後的文檔中的每一個詞。

[[(0, 1), (1, 1), (2, 1), (3, 1)], [(0, 1), (4, 1), (5, 1), (6, 1), (7, 1)], [(4, 1), (5, 1), (6, 1), (7, 1), (8, 1)], [(0, 1), (9, 1), (10, 1), (11, 1), (12, 1)], [(0, 1), (9, 1), (10, 1), (12, 1), (13, 1)], [(0, 1), (5, 1), (6, 1), (7, 1)], [(0, 2), (14, 1), (15, 1), (16, 1)], [(2, 1), (17, 1)]]

以下用同樣的方法,把測試文檔也轉換為二元組的向量

doc_test_vec = dictionary.doc2bow(doc_test_list)  doc_test_vec

[(0, 1), (2, 1), (3, 1), (12, 1), (17, 1)]

相似度分析

使用TF-IDF模型對語料庫建模

tfidf = models.TfidfModel(corpus)

獲取測試文檔中,每個詞的TF-IDF值

tfidf[doc_test_vec]

[(0, 0.08112725037593049), (2, 0.3909393754390612), (3, 0.5864090631585919), (12, 0.3909393754390612), (17, 0.5864090631585919)]

對每個目標文檔,分析測試文檔的相似度

index = similarities.SparseMatrixSimilarity(tfidf[corpus], num_features=len(dictionary.keys()))  sim = index[tfidf[doc_test_vec]]  sim

array([ 0.54680777, 0.01055349, 0. , 0.17724207, 0.17724207, 0.01354522, 0.01279765, 0.70477605], dtype=float32)

根據相似度排序

sorted(enumerate(sim), key=lambda item: -item[1])

[(7, 0.70477605), (0, 0.54680777), (3, 0.17724207), (4, 0.17724207), (5, 0.013545224), (6, 0.01279765), (1, 0.010553493), (2, 0.0)]

從分析結果來看,測試文檔與doc7相似度最高,其次是doc0,與doc2的相似度為零。大家可以根據TF-IDF的原理,看看是否符合預期。 最後總結一下文本相似度分析的步驟:

  1. 讀取文檔
  2. 對要計算的多篇文檔進行分詞
  3. 對文檔進行整理成指定格式,方便後續進行計算
  4. 計算出詞語的詞頻
  5. 【可選】對詞頻低的詞語進行過濾
  6. 建立語料庫詞典
  7. 載入要對比的文檔
  8. 將要對比的文檔通過doc2bow轉化為詞袋模型
  9. 對詞袋模型進行進一步處理,得到新語料庫
  10. 將新語料庫通過tfidfmodel進行處理,得到tfidf
  11. 通過token2id得到特徵數 12、稀疏矩陣相似度,從而建立索引 13、得到最終相似度結果