sklearn tfidf求余弦相似度_【基础算法 】文本相似度计算
在自然語言處理中,文本相似度是一種老生常談而又應用廣泛的基礎算法模塊,可用于地址標準化中計算與標準地址庫中最相似的地址,也可用于問答系統中計算與用戶輸入問題最相近的問題及其答案,還可用于搜索中計算與輸入相近的結果,擴大搜索召回,等等。
基于此,現將幾種常見的文本相似度計算方法做一個簡單總結,以便后續查閱,本文所有源碼均已上傳到github。
1.字符串相似度
字符串相似度指的是比較兩個文本相同字符個數,從而得出其相似度。 python為我們提供了一個difflib包用于計算兩個文本序列的匹配程度,我們可以將其視為兩個文本字符串的相似度,其代碼實現很簡單,如果需要得到兩個文本之間相同部分或不同部分,可參考Github
https://github.com/tianyunzqs/pynotes/tree/master/text_diff?github.comimport difflib difflib.SequenceMatcher(None, string1, string2).ratio()2.simhash相似度
simhash最早是由google在文章《detecting near-duplicates for web crawling》中提出的一種用于網頁去重的算法。simhash是一種局部敏感hash,計算速度快,對海量網頁文本可實現快速處理。 以下內容主要來源于:https://www.cnblogs.com/xlturing/p/6136690.html,介紹通俗易懂,故摘抄過來。
傳統的Hash算法只負責將原始內容盡量均勻隨機地映射為一個簽名值,原理上僅相當于偽隨機數產生算法。傳統的hash算法產生的兩個簽名,如果原始內容在一定概率下是相等的;如果不相等,除了說明原始內容不相等外,不再提供任何信息,因為即使原始內容只相差一個字節,所產生的簽名也很可能差別很大。所以傳統的Hash是無法在簽名的維度上來衡量原內容的相似度,而SimHash本身屬于一種局部敏感哈希算法,它產生的hash簽名在一定程度上可以表征原內容的相似度。我們主要解決的是文本相似度計算,要比較的是兩個文章是否相似,當然我們降維生成了hash簽名也是用于這個目的。看到這里估計大家就明白了,我們使用的simhash就算把文章中的字符串變成 01 串也還是可以用于計算相似度的,而傳統的hash卻不行。
我們可以來做個測試,兩個相差只有一個字符的文本串, “你媽媽喊你回家吃飯哦,回家羅回家羅” “你媽媽叫你回家吃飯啦,回家羅回家羅”。 通過simhash計算結果為: 1000010010101101111111100000101011010001001111100001001011001011 1000010010101101011111100000101011010001001111100001101010001011 通過傳統hash計算為: 0001000001100110100111011011110 1010010001111111110010110011101
通過上面的例子我們可以很清晰的發現simhash的局部敏感性,相似文本只有部分01變化,而hash值很明顯,即使變化很小一部分,也會相差很大。基本流程
整個過程的流程圖為:
simhash的主要思想是將高維的特征向量(文本可轉換為高維向量表示)映射成低維的特征向量,通過計算兩個向量的漢明距離(Hamming Distance)來確定文本的相似度。 其中,漢明距離,表示在兩個等長字符串中對應位置不同字符的個數。如,1011100 與 1001000 之間的漢明距離是 2。而字符串的編輯距離則是漢明距離的擴展。
根據以上simhash算法的流程描述,利用tfidf值表示詞語的權重,實現simhash計算文本相似度代碼如下
# -*- coding: utf-8 -*- # @Time : 2019/6/25 15:58 # @Author : tianyunzqs # @Description : import codecs import numpy as np import jieba.posseg as psegdef load_stopwords(path):return set([line.strip() for line in open(path, "r", encoding="utf-8").readlines() if line.strip()])stopwords = load_stopwords(path='stopwords.txt')def string_hash(source):if not source:return 0x = ord(source[0]) << 7m = 1000003mask = 2 ** 128 - 1for c in source:x = ((x * m) ^ ord(c)) & maskx ^= len(source)if x == -1:x = -2x = bin(x).replace('0b', '').zfill(64)[-64:]return str(x)def load_idf(path):words_idf = dict()with codecs.open(path, 'r', encoding='utf-8') as f:lines = f.readlines()for line in lines:parts = line.strip().split('t')if len(parts) != 2:continueif parts[0] not in words_idf:words_idf[parts[0]] = float(parts[1])return words_idfwords_idf = load_idf(path=r'idf.txt')def compute_tfidf(text):words_freq = dict()words = pseg.lcut(text)for w in words:if w.word in stopwords:continueif w.word not in words_freq:words_freq[w.word] = 1else:words_freq[w.word] += 1text_total_words = sum(list(words_freq.values()))words_tfidf = dict()for word, freq in words_freq.items():if word not in words_idf:continueelse:tfidf = words_idf[word] * (freq / text_total_words)words_tfidf[word] = tfidfreturn words_tfidfdef get_keywords(text, topk):words_tfidf = compute_tfidf(text)words_tfidf_sorted = sorted(words_tfidf.items(), key=lambda x: x[1], reverse=True)return [item[0] for item in words_tfidf_sorted[:topk]]def hamming_distance(simhash1, simhash2):ham = [s1 == s2 for (s1, s2) in zip(simhash1, simhash2)]return ham.count(False)def text_simhash(text):total_sum = np.array([0 for _ in range(64)])keywords = get_keywords(text, topk=2)for keyword in keywords:v = int(words_idf[keyword])hash_code = string_hash(keyword)decode_vec = [v if hc == '1' else -v for hc in hash_code]total_sum += np.array(decode_vec)simhash_code = [1 if t > 0 else 0 for t in total_sum]return simhash_codedef simhash_similarity(text1, text2):simhash_code1 = text_simhash(text1)simhash_code2 = text_simhash(text2)print(simhash_code1, simhash_code2)return hamming_distance(simhash_code1, simhash_code2)if __name__ == '__main__':print(simhash_similarity('在歷史上有著許多數學發現', '在歷史上有著許多科學發現'))而simhash算法已有對應python包——simhash,安裝即可實現simhash相似度計算
pip install simhash利用simhash包,計算文本相似度示例代碼
# -*- coding: utf-8 -*- # @Time : 2019/6/25 15:58 # @Author : tianyunzqs # @Description : from simhash import Simhashdef simhash_similarity(text1, text2):""":param text1: 文本1:param text2: 文本2:return: 返回兩篇文章的相似度"""aa_simhash = Simhash(text1)bb_simhash = Simhash(text2)max_hashbit = max(len(bin(aa_simhash.value)), (len(bin(bb_simhash.value))))# 漢明距離distince = aa_simhash.distance(bb_simhash)similar = 1 - distince / max_hashbitreturn similarif __name__ == '__main__':print(simhash_similarity('在歷史上有著許多數學發現', '在歷史上有著許多科學發現'))3.word2vec相似度
word2vec是對詞語進行向量化的一種無監督算法,具體介紹與tensorflow實現可參考:
【基礎算法】word2vec詞向量?mp.weixin.qq.comword2vec相似度是指利用word2vec算法將文本向量化,進而利用余弦距離計算兩個向量的余弦相似度作為兩字符串的相似度。
def sentence_similarity_word2vec(self, sentence1, sentence2):sentence1 = sentence1.strip()sentence2 = sentence2.strip()if sentence1 == sentence2:return 1.0vec1 = self.get_sentence_vector(sentence1)vec2 = self.get_sentence_vector(sentence2)return self.cosine_similarity(vec1, vec2)word2vec對文本的向量化是將文本分詞后,得到各詞語的向量化表示,然后對向量的每個維度進行加權相加,形成文本向量,進而可利用余弦距離計算文本的相似度。
def get_sentence_vector(self, sentence):words = self.text_segment(sentence)words_vec = [self.get_word_vector(word) for word in words]return np.mean(words_vec, axis=0)@staticmethod def cosine_similarity(vec1, vec2):tmp1, tmp2 = np.dot(vec1, vec1), np.dot(vec2, vec2)if tmp1 and tmp2:return np.dot(vec1, vec2) / (np.sqrt(tmp1) * np.sqrt(tmp2))return 0.0完整代碼,可參考
tianyunzqs/text_similarity?github.com4.神經網絡相似度
利用神經網絡進行相似度計算的一種思路是將輸入X編碼為中間向量V,然后對中間結果進行解碼得到輸出Y,其中損失函數的計算方式就是盡可能減少X與Y之間的偏差,理想情況就是中間向量V能完全解碼還原為原始輸入X。網絡訓練完成后,我們也就得到了輸入句子所表示的語義特征向量。
根據上述思路,我們自然可以聯想到最基礎的自編碼器(AutoEncoder, AE),當然還有其他更復雜的自編碼器( 如:棧式自編碼器(Stacked Autoencoder, SAE)、變分自編碼器(Variational auto-encoder, VAE)等)。
AutoEncoder能很好的編碼句子中所包含的語義信息,可以在一定程度上解決字符串相似度計算中所缺乏的語義理解問題和word2vec相似度計算中所缺乏的詞序問題。本文基于tensorflow實現了基礎版本的自編碼器,模型代碼如下,完整的項目代碼可參考
Github?github.comclass AutoEncoder(object):def __init__(self,embedding_size,num_hidden_layer,hidden_layers):assert num_hidden_layer == len(hidden_layers), 'num_hidden_layer not match hidden_layers'self.embedding_size = embedding_sizeself.num_hidden_layer = num_hidden_layerself.hidden_layers = hidden_layersself.input_x = tf.placeholder(tf.float32, [None, embedding_size])new_hidden_layers1 = [embedding_size] + hidden_layers + hidden_layers[::-1][1:]new_hidden_layers2 = hidden_layers + hidden_layers[::-1][1:] + [embedding_size]encoder_weights, encoder_biases, decoder_weights, decoder_biases = [], [], [], []for i, (hidden1, hidden2) in enumerate(zip(new_hidden_layers1, new_hidden_layers2)):if i < int(len(new_hidden_layers1) / 2.0):encoder_weights.append(tf.Variable(tf.random_normal([hidden1, hidden2])))encoder_biases.append(tf.Variable(tf.random_normal([hidden2])))else:decoder_weights.append(tf.Variable(tf.random_normal([hidden1, hidden2])))decoder_biases.append(tf.Variable(tf.random_normal([hidden2])))with tf.name_scope('output'):self.encoder_output = self.encoder_or_decode(self.input_x, encoder_weights, encoder_biases)self.decoder_output = self.encoder_or_decode(self.encoder_output, decoder_weights, decoder_biases)with tf.name_scope('loss'):self.loss = tf.reduce_mean(tf.pow(self.input_x - self.decoder_output, 2))self.global_step = tf.Variable(0, name="global_step", trainable=False)tf.summary.scalar('loss', self.loss)self.merge_summary = tf.summary.merge_all()self.saver = tf.train.Saver()@staticmethoddef encoder_or_decode(input_data, encoder_weights, encoder_biases):layer_output = [input_data]for weight, biase in zip(encoder_weights, encoder_biases):layer_output.append(tf.nn.sigmoid(tf.add(tf.matmul(layer_output[-1], weight), biase)))return layer_output[-1] 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的sklearn tfidf求余弦相似度_【基础算法 】文本相似度计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ThinkPHP6项目基操(4.拦截无效
- 下一篇: 使用hover后隐藏的图片不显示_持续分