句子相似性计算

句子的相似性可以使用下面的一些方法来计算:

  1. 杰卡德相似度
  2. 余弦相似度
  3. 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

其他相似度计算方法后面再进行补充.

未经允许不得转载:一亩三分地 » 句子相似性计算