nlp自然语言处理中句子相似度计算
在做自然语言处理的过程中,现在智能对话比较火,例如智能客服,智能家电,智能音箱等,我们需要获取用户说话的意图,方便做出正确的回答,这里面就涉及到句子相似度计算的问题,那么本节就来了解一下怎么样来用 Python 实现句子相似度的计算。
句子相似度常用的几种方法:
1、编辑距离
2、杰卡德系数计算
3、Word2Vec 计算
编辑距离,英文叫做 Edit Distance,又称 Levenshtein 距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数,
如果它们的距离越大,说明它们越是不同。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
例如我们有两个字符串:string 和 setting,如果我们想要把 string 转化为 setting,需要这么两步:
第一步,在 s 和 t 之间加入字符 e。
第二步,把 r 替换成 t。
所以它们的编辑距离差就是 2,这就对应着二者要进行转化所要改变(添加、替换、删除)的最小步数。
安装:pip3 install distance
import distance
str1 = "公司地址是哪里"
str2 = "公司在什么位置"
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
print(edit_distance(str1, str2))
想要获取相似的文本的话可以直接设定一个编辑距离的阈值来实现,如设置编辑距离为 2
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
strings = [
'你在干什么',
'你在干啥子',
'你在做什么',
'你好啊',
'我喜欢吃香蕉'
]
target = '你在干啥'
results = list(filter(lambda x: edit_distance(x, target) <= 2, strings))
print(results)
# ['你在干什么', '你在干啥子']
杰卡德系数计算
杰卡德系数,英文叫做 Jaccard index, 又称为 Jaccard 相似系数,用于比较有限样本集之间的相似性与差异性。Jaccard 系数值越大,样本相似度越高。
实际上它的计算方式非常简单,就是两个样本的交集除以并集得到的数值,当两个样本完全一致时,结果为 1,当两个样本完全不同时,结果为 0。
算法非常简单,就是交集除以并集,下面我们用 Python 代码来实现一下:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
def jaccard_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 将字中间加入空格
s1, s2 = add_space(s1), add_space(s2)
# 转化为TF矩阵
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 获取词表内容
ret = cv.get_feature_names()
print(ret)
# 求交集
numerator = np.sum(np.min(vectors, axis=0))
# 求并集
denominator = np.sum(np.max(vectors, axis=0))
# 计算杰卡德系数
return 1.0 * numerator / denominator
s1 = '你在干嘛呢'
s2 = '你在干什么呢'
print(jaccard_similarity(s1, s2))
Word2Vec,顾名思义,其实就是将每一个词转换为向量的过程。如果不了解的话可以参考:https://blog.csdn.net/itplus/article/details/37969519。
Word2Vec的词向量模型是训练的维基百科的中文语库,这里模型有250维和50维,向量维度越大模型越大,计算越复杂,正常使用时,需要小的模型,发现50维的也差不多,训练模型方式和模型下载请参考:之前文章。
流程:
01、对句子进行拆词
02、去除无用的分词
03、计算句子平均词向量
04、余弦相似度
对句子进行拆词:Python提供了很对可用库,自行选择
去除无用的分词:删除没用的语气词等,为的是减少对计算句子平均词向量的影响。
计算句子平均词向量用的是AVG-W2V,计算句子平均词向量,所以02步尤为重要
余弦相似度:
余弦相似度 np.linalg.norm(求范数)(向量的第二范数为传统意义上的向量长度
dist1=float(np.dot(vec1,vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2)))
def key_words_ask_method(sentence1, sentence2):
'''
因为无论是#1:AVG-W2V 2:AVG-W2V-TFIDF 都需要求得平均值,
除数:决定整个数据的大小 被除数:影响平均值
所以 分词的标准很重要,可通过自定义词典、停用词 和 语义分析进行适当处理
'''
vec1 = sentence_to_vec(sentence1)
vec2 = sentence_to_vec(sentence2)
# 零向量直接返回
if (vec1 == np.zeros(WORD_VECTOR_DIM)).all() == True or (vec2 == np.zeros(WORD_VECTOR_DIM)).all() == True:
return "不符合相似"
# score = cos(vec1, vec2)
# print(score)
# if score < COSINE_CRITICAL_VALUE:
# return "1"
# else:
# return "0"
# 余弦相似度 np.linalg.norm(求范数)(向量的第二范数为传统意义上的向量长度
dist1=float(np.dot(vec1,vec2)/(np.linalg.norm(vec1)*np.linalg.norm(vec2)))
print("score:", dist1)
if dist1 > 0.92:
return "两个句子相似"
else:
return "两个句子不相似"