中文短文字相似度:WMD
阿新 • • 發佈:2019-02-20
開篇
句子相似是目前我做問句匹配的基礎。
這是我嘗試使用詞向量,以一種無監督方法去計算兩個句子相似度的第二種方法。第一種方法,我嘗試使用詞向量的加權平均生成句向量來計算句子間的相似度,效果很一般,之後我會嘗試使用不同的加權方法再次計算。有機會我會連著程式碼一起放出來。
當然我還使用了三種不同的深度學習方法來計算相似度,之後都會以程式碼講解的方式呈現。本部落格沒有使用任何公司的資料,也未整合到公司的任何系統中,屬於學術型文章開源。
WMD
word mover’s distance 是一種計算句子之間距離的方法,距離越小,相似度越高。看了網上幾篇部落格,基本是這邊抄一下,那邊抄一下,內容非常得凌亂。這邊也放 一篇部落格,等我讀過原始論文,再來這篇部落格來補充相關的理論知識,現在這篇部落格希望通過程式碼讓你更快的構建自己的wmd計算函式。迅速實戰。
詞向量
首先我們還是需要藉助詞向量,你可以自己下載別人訓練好的詞向量,或者自己使用gensim訓練詞向量
關於詞向量的訓練,我這邊不再重複,使用gensim只要幾行程式碼,如果沒有相關的經驗,可以參考我之前的部落格。
WMDdistance
先放出相關的程式碼,部落格的最後我會放出完整的程式碼,如果有什麼疑問歡迎留言。資料是公司的,不會公開,但是有其他資料可以替換。不影響程式碼的執行。
def wmd(sent1, sent2): sent1 = word_cut(sent1) sent2 = word_cut(sent2) model = KeyedVectors.load_word2vec_format(os.path.join(data_dir, "Word2VecModel_small.vector")) model.init_sims(replace=True) #這邊是歸一化詞向量,不加這行的話,計算的距離資料可能會非常大 distance = model.wmdistance(sent1,sent2) return distance
詞向量是一定要載入的。
中文當然是要分詞的,這裡我使用的是哈工大的分詞工具,下面我放出我的程式碼,這是需要載入的一下模型和詞典。
data_dir = "C:\\Users\\d84105613\\PycharmProjects\\Ai_qa\\data" LTP_DATA_DIR = 'C:\\Users\\d84105613\\ltp_data' cws_model_path = os.path.join(LTP_DATA_DIR, 'cws.model') segmentor = Segmentor() # 初始化例項 segmentor.load_with_lexicon(cws_model_path, os.path.join(data_dir,'wordDictupper.txt')) # 載入模型 #停用詞載入 stopwords = [] stopword = open(os.path.join(data_dir,'stopwords_zh.txt'),'rt',encoding='utf-8') for line in stopword: stopwords.append(line.strip())
分詞的時候記得把停用詞也給去了
def word_cut(sentence):
words = segmentor.segment(sentence)
# segmentor.release()
words = list(words)
# print(len(set(words)))
key = [",", "?", "[", "]"]
words = [c for c in words if c not in punctuation]
words = [c for c in words if c not in key]
words = [c for c in words if c not in stopwords]
return words
搜尋相似句子
這邊就是問答系統一個簡單的功能元件了,給出一個你要查詢的句子,在語料庫裡搜尋出最相似的幾個句子。感謝gensim提供的庫函式。
def get_top_n(document,query,num_best):
corpus = [word_cut(i) for i in document]
model = KeyedVectors.load_word2vec_format(os.path.join(data_dir, "Word2VecModel_small.vector"))
instance = WmdSimilarity(corpus, model, num_best)
query = word_cut(query)
sims = instance[query]
for i in range(num_best):
print(sims[i][1])
print(document[sims[i][0]])
完整程式碼
import os
from pyltp import Segmentor
from zhon.hanzi import punctuation
from gensim.models import KeyedVectors
from gensim.similarities import WmdSimilarity
data_dir = "C:\\Users\\d84105613\\PycharmProjects\\Ai_qa\\data"
LTP_DATA_DIR = 'C:\\Users\\d84105613\\ltp_data'
cws_model_path = os.path.join(LTP_DATA_DIR, 'cws.model')
segmentor = Segmentor() # 初始化例項
segmentor.load_with_lexicon(cws_model_path, os.path.join(data_dir,'wordDictupper.txt')) # 載入模型
#停用詞載入
stopwords = []
stopword = open(os.path.join(data_dir,'stopwords_zh.txt'),'rt',encoding='utf-8')
for line in stopword:
stopwords.append(line.strip())
def read_data_sets(train_dir):
s1 = []
s2 = []
for line in open(train_dir, encoding='utf-8'):
l = line.strip().split("¥")
if len(l) < 2:
continue
s1.append(l[0])
s2.append(l[1])
return s1, s2
def word_cut(sentence):
words = segmentor.segment(sentence)
# segmentor.release()
words = list(words)
# print(len(set(words)))
key = [",", "?", "[", "]"]
words = [c for c in words if c not in punctuation]
words = [c for c in words if c not in key]
words = [c for c in words if c not in stopwords]
return words
def wmd(sent1, sent2):
sent1 = word_cut(sent1)
sent2 = word_cut(sent2)
model = KeyedVectors.load_word2vec_format(os.path.join(data_dir, "Word2VecModel_small.vector"))
model.init_sims(replace=True)
distance = model.wmdistance(sent1,sent2)
return distance
def get_top_n(document,query,num_best):
corpus = [word_cut(i) for i in document]
model = KeyedVectors.load_word2vec_format(os.path.join(data_dir, "Word2VecModel_small.vector"))
instance = WmdSimilarity(corpus, model, num_best)
query = word_cut(query)
sims = instance[query]
for i in range(num_best):
print(sims[i][1])
print(document[sims[i][0]])
輸出
0.8867670409833355
修改採購計劃當前處理人
0.856070758049195
修改BPA當前處理人
0.8339566600760685
缺陷修改當前處理人
0.8040727679959436
修改變更單當前處理人
0.7797357960999256
修改單據當前處理人
Process finished with exit code 0
相關部落格程式碼
補充
之前的程式碼跑一個批次的資料,大概是6萬條要40秒,後續做了一些優化,大概跑6萬11秒左右
def overlap(list1,list2):
temp = [i for i in list1 if i in list2]
return len(temp)/len(list1)
def fast_top_n(document, query, num_best):
doc = [word_cut(i) for i in document]
query = word_cut(query)
corpus=[]
document1 = []
for i,j in enumerate(doc):
if overlap(query,j) > 0.25:
corpus.append(doc[i])
document1.append(document[i])
model = KeyedVectors.load_word2vec_format(os.path.join(data_dir, "Word2VecModel_small.vector"))
instance = WmdSimilarity(corpus, model, num_best)
sims = instance[query]
for i in range(num_best):
print(sims[i][1])
print(document1[sims[i][0]])