句子的相似性可以使用下面的一些方法来计算:
- 杰卡德相似度
- 余弦相似度
- TextRank 相似度
1. 杰卡德相似度

杰卡德相似度需要计算两个句子的交并集,然而这里没有说明使用什么粒度来计算。所以我们可以基于词粒度来计算,也可以基于字的粒度来计算。
如果以词的粒度来计算的话,我们可以先将句子A和句子B分词,计算两个句子词的并集减去两个句子词的交集,并除以两个句子词的并集,该值的取值范围为 [0, 1], 值越接近于 1,则说明两个句子越相似。
如果以字的粒度的话,就不需要分词了,也可以以字的粒度来计算两个句子的叫并集,从而计算出杰卡德相似度。
import jieba
import numpy as np
def jaccard_similarity(sentence1, sentence2):
# 计算句子并集
sentence_union = np.union1d(sentence1, sentence2)
# 计算句子交集
sentence_inter = np.intersect1d(sentence1, sentence2)
print('交集:', sentence_union)
print('并集:', sentence_inter)
# 计算并集和交集的差集
sentence_diff = np.setdiff1d(sentence_union, sentence_inter)
print('差集:', sentence_diff)
# 计算杰卡德系数
jaccard_similarity = 1 - len(sentence_diff) / len(sentence_union)
print('杰卡德相似度: %.2f' % jaccard_similarity)
# 1. 字的粒度
def test01():
sentence1 = '我爱北京天安门'
sentence2 = '我爱北京大前门'
sentence1_words = list(sentence1)
sentence2_words = list(sentence2)
print('句子1:', sentence1_words)
print('句子2:', sentence2_words)
jaccard_similarity(sentence1_words, sentence2_words)
# 2. 词的粒度
def test02():
# 添加自定义词
# jieba.load_userdict()
jieba.add_word('大前门')
sentence1 = '我爱北京天安门'
sentence2 = '我爱北京大前门'
sentence1_words = jieba.lcut(sentence1)
sentence2_words = jieba.lcut(sentence2)
print('句子1:', sentence1_words)
print('句子2:', sentence2_words)
jaccard_similarity(sentence1_words, sentence2_words)
if __name__ == '__main__':
test01()
print('-' * 30)
test02()
程序运行结果:
句子1: ['我', '爱', '北', '京', '天', '安', '门'] 句子2: ['我', '爱', '北', '京', '大', '前', '门'] 交集: ['京' '前' '北' '大' '天' '安' '我' '爱' '门'] 并集: ['京' '北' '我' '爱' '门'] 差集: ['前' '大' '天' '安'] 杰卡德相似度: 0.56 ------------------------------ 句子1: ['我', '爱', '北京', '天安门'] 句子2: ['我', '爱', '北京', '大前门'] 交集: ['北京' '大前门' '天安门' '我' '爱'] 并集: ['北京' '我' '爱'] 差集: ['大前门' '天安门'] 杰卡德相似度: 0.60
2. 余弦相似度

通过余弦相似度计算两个句子的相似性,其关键是将句子转换为向量表示。我们可以将其转换为词频向量,也可以通过 word2vec 将每个词的词向量相加取均值作为句子向量,也可以用其他的方法。余弦相似度取值范围为[-1, 1],越接近 1 说明两个句子越相似。
import jieba
import numpy as np
import torch
from sklearn.feature_extraction.text import CountVectorizer
if __name__ == '__main__':
jieba.add_word('大前门')
s1 = '我爱北京天安门'
s2 = '我爱北京大前门'
# 句子分词
s1 = ' '.join(jieba.lcut(s1))
s2 = ' '.join(jieba.lcut(s2))
# 词频向量
transfer = CountVectorizer()
sentence_vector = transfer.fit_transform([s1, s2]).toarray()
s1, s2 = sentence_vector
# 余弦相似度
cosine_similarity = (s1 @ s2) / (np.sqrt(s1 @ s1) * np.sqrt(s2 @ s2))
print('cosine_similarity:', cosine_similarity)
s1, s2 = torch.tensor([s1]).float(), torch.tensor([s2]).float()
# 参数为两个二维向量 [batch_size, dim]
cosine_similarity = torch.cosine_similarity(s1, s2)
print('cosine_similarity:', cosine_similarity.item())
程序输出结果:
cosine_similarity: 0.4999999999999999 cosine_similarity: 0.5
3. TextRank 句子相似性

公式可理解为句子A和句子B交集词的数量除以句子A的长度对数和句子B的长度对数。
import jieba
import numpy as np
import torch
from sklearn.feature_extraction.text import CountVectorizer
if __name__ == '__main__':
jieba.add_word('大前门')
s1 = jieba.lcut('我爱北京天安门')
s2 = jieba.lcut('我爱北京大前门')
# 句子交集
intersect = np.intersect1d(s1, s2)
# TextRank 相似度
similarity = len(intersect) / (np.log(len(s1)) + np.log(len(s2)))
print('similarity:', similarity)
程序输出结果:
similarity: 1.0820212806667227
其他相似度计算方法后面再进行补充.

冀公网安备13050302001966号