常用文本相似度的计算方法和代码
文本相似度的计算被广泛应用于信息检索、检索引擎、文档复制等:
因此,在各种情况和任务中,有不同的文本相似度计算。
最近正在处理搜索引擎的相关项目
介绍我们主要使用的相似度计算方式及其实现Github
余弦相似度:
余弦相似度是纯数学中的概念,首先,在进行计算的两个str中提取word,作为非重复词典使用。
遍历词典,使两个语句的表示向量化。 每个向量的长度是词典大小
import numpy as np
def cosine _ similarity (sentence 1: str,sentence2: str )- float:
''''
computenormalizedcosinesimilarity。
33660 param sentence 1:工程传感器。
33660 param sentence 2:工程传感器。
33660 return : normalizedsimilarityoftwoinputsentences。
''''
seg1=sentence1.strip ' ' ).split ' ' )
seg2=sentence2.strip ' ' ).split ' ' )
word _ list=list (set ([ wordforwordinseg1seg2] ) )
word_count_vec_1=[]
word_count_vec_2=[]
for word in word_list:
word _ count _ vec _1. append (se G1.count ) word ) )
word _ count _ vec _2. append (seg2. count ) word ) )
vec _1=NP.array (word _ count _ vec _1) )。
vec _2=NP.array (word _ count _ vec _2) )。
num=vec_1.dot(vec_2.t ) )
(denom=NP.LinaLG.norm(vec_1) NP.Lina LG.norm (vec _2) ) ) ) ) 652
cos=num/denom
sim=0.5(0.5*cos
返回sim
编辑距离:
编辑距离是指文本a成为文本b的处理次数
处理内容:
删除一个字符
增加一个字符
修改一个字
例如:
a :请求管理
b :请求
step 1:请求管理-请求
step 2:请求-请求
所以编辑距离是2
# usingdifflibispython 3.6默认软件包
导入差异列表
def compute _ levenshtein _ distance (sentence 1: str,sentence2: str )- int:
''''
计算级别距离。
''''
leven_cost=0
s=difflib.sequencematcher(none,sentence1,sentence2) )。
for tag,i1,i2,j1,j2 in s.get_opcodes () :
if tag=='replace':
Leven_cost=max(I2-I1,j2 - j1 ) ) ) ) ) ) ) ) )。
elif tag=='insert':
Leven_cost=(J2-J1 ) ) )。
elif tag=='delete':
Leven_cost=(I2-I1 ) )。
return leven_cost
利文斯顿相似度
利文斯顿距离是编辑距离
测量相似度所需的范围为0-1的浮点数
因此,需要将上述编辑距离整数值变换为normalize的分数
def compute _ levenshtein _ similarity (sentence 1: str,sentence2: str )- float:
' ' Compute the hamming similarity.' '
leven _ cost=compute _ levenshtein _ distance (sentence 1,sentence2) ) ) ) ) ) ) ) )。
return 1 - (leven_cost / len(sentence2))
Simhash & 汉明距离
计算字符串之间的相似度,步骤如下:
分词 :将字符串之间分词(中文需要分词,英文本身具有空格)
HASH :将每个单词进行hash,生成长度相同的01序列
加权 :计算每个单词本身的权重,例如 -4, 5 等等不同的权重,与0/1字符串相乘,1为正, 0为负, 例如:
W(清华) = 100101*4 = 4 -4 -4 4 -4 4
W(大学)=101011*5 = 5 -5 5 -5 5 5
合并 : 将上述各个词语,与权重相乘之后的hash数值相加
W(清华大学) = (4+5) (-4-5) (-4+5) (4-5) (-4+5) (4+5) = 9 -9 1 -1 1 9
降维: 将上述数值进行降维, ≥1ge 1≥1的转换为1, ≤0le 0≤0的转换为0
W(清华大学) = 1 0 1 0 1 1
则可以计算出 句子的代表数值,同样长度的句子0/1数值对应位置进行与运算,true值数量与句子总长度比例,则可以得出hamming distance.
此处代码使用了 python simHash package.
安装与使用tutorial
import re
from simhash import Simhash
def compute_simhash_hamming_similarity(sentence1: str, sentence2: str) -> float:
"""need to normalize after compute!"""
def get_features(s):
width = 3
s = s.lower()
s = re.sub(r'[^w]+', '', s)
return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]
hash_value1 = Simhash(get_features(sentence1)).value
hash_value2 = Simhash(get_features(sentence2)).value
return compute_levenshtein_similarity(str(hash_value1), str(hash_value2))
Jaccard系数
SaS_aSa 是sentence A
SbS_bSb 是sentence B
其单词交集与单词并集的比例即为杰卡德相似系数
(若遇见相同单词,则需要看数量重复是否占比很高,具体情况而定。两项处理均可。)
Sa∩SbSa∪Sbfrac{S_a cap S_b}{S_a cup S_b}Sa∪SbSa∩Sb
def compute_jaccard_similarity(sentence1: str, sentence2: str) -> float:
word_set1 = set(sentence1.strip(" ").split(" "))
word_set2 = set(sentence2.strip(" ").split(" "))
return len(word_set1 & word_set2) / len(word_set1 | word_set2)
TF-IDF
TF其实由两项组成:
TF: Term Frequency
IDF: Inverse Document Frequency
TF 代表了词语在文档中出现的频率,当进行索引的时候,词语出现频率较高的文本,匹配度也会较高,但是某些停止词,例如 to 在文本中会出现相当多的次数,但这对匹配并没有起到很好的索引作用,因此需要引入另一个度量值 IDF(逆文本频率)
IDF=logNN(x)IDF = logfrac{N}{N(x)}IDF=logN(x)N
其中NNN为语料库中文本的总数,N(x)N(x)N(x)为文本中出现单词xxx的文本数量。
次可以度量该单词的重要程度。
某些特殊情况下,xxx并未出现在语料库中(所有文本),则需要考虑将公式平滑为:
IDF=logN+1N(x)+1+1IDF = logfrac{N+1}{N(x)+1}+1IDF=logN(x)+1N+1+1
最终的TF-IDF值为:
TF−IDF(x)=TF(x)∗IDF(x)TF-IDF(x) = TF(x) * IDF(x)TF−IDF(x)=TF(x)∗IDF(x)
此处我们调用了nltk工具库来实现TF-IDF计算:
由于query可能有2-3个单词同时建立索引,此处采用了平均值。
from nltk.text import TextCollection
from nltk.tokenize import word_tokenize
def compute_tf_idf_similarity(query: str, content: str, type: str) -> float:
"""
Compute the mean tf-idf or tf
similarity for one sentence with multi query words.
:param query: a string contain all key word split by one space
:param content: string list with every content relevent to this query.
:return: average tf-idf or tf similarity.
"""
sents = [word_tokenize(content), word_tokenize("")] # add one empty file to smooth.
corpus = TextCollection(sents) # 构建语料库
result_list = []
for key_word in query.strip(" ").split(" "):
if type == "tf_idf":
result_list.append(corpus.tf_idf(key_word, corpus))
elif type == "tf":
result_list.append(corpus.tf(key_word, corpus))
else:
raise KeyError
return sum(result_list) / len(result_list)