使用Python在上位词级别进行类比的文本语义相似性

问题描述 投票:1回答:1

我有一些长(50行)的段落,我想使用Python来衡量它们的相似性。我对这些在上位词(语言学的术语)级别的语义相似性更感兴趣,重点是功能和过程。为了进一步澄清,如果两个文本都引用相同的函数或过程,则无论它们使用的是什么单词,我都将其称为相似的两段文本。

以下是两个示例:相似句子=(“使用管子吸入苏打水”,“使用泵和动脉将血液输送到心脏”)。Unsimilar_Sentences =(“使用管子吸苏打水”,“做一些编程以获得更好的效果”)。

在第一个示例中,“管”〜“动脉”,“苏打”〜“血液”和“吸入”〜“转移到”。我希望我很感兴趣。

根据我对NLP算法和工具的研究,Python中的NLTK和WordNet似乎是完成此任务的正确工具,但我不确定如何做到。

请先参考任何相关的教程或学习源以及任何建议。

python text nlp semantics similarity
1个回答
0
投票

a great post on NLPForHackers描述了如何使用词网实现句子相似度。

他们的成分:

  1. 一个POS标记器,用于缩小每个单词的同义词集的列表(此后,作者仅使用第一个同义词集)
  2. path_similarity:在分类法图表上显示两个单词彼此有多远的度量标准
  3. 词级相似度的总和:最大值,然后取平均值。

这已经很好用:对于您的正面示例,相似度得分为0.29,而对于负面示例,相似度得分仅为0.20。

我会建议一些改进:

  1. 不仅使用第一个找到的同义词集,而且对一个单词的所有同义词集使用max。它使您的正例和负例的得分分别为0.36和0.23-比以前更远。
  2. 使用移词器距离来汇总单词相似度,而不是最大和平均值。我通过公式s=1-d^2/2在相似度和距离之间进行转换。这会将您的正样本和负样本的得分进一步拉开-分别达到0.41和0.19。

这是我最终版本的代码:

from nltk import word_tokenize, pos_tag
from nltk.corpus import wordnet as wn
import numpy as np
from pyemd import emd

import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
nltk.download('wordnet')

def penn_to_wn(tag):
    """ Convert between a Penn Treebank tag to a simplified Wordnet tag """
    if tag.startswith('N'):
        return 'n'
    if tag.startswith('V'):
        return 'v'
    if tag.startswith('J'):
        return 'a'
    if tag.startswith('R'):
        return 'r'
    return None

def tagged_to_synsets(word, tag):
    wn_tag = penn_to_wn(tag)
    if wn_tag is None:
        return []
    return wn.synsets(word, wn_tag)

def get_counts(sentence, vocab):
    weights = np.zeros(len(vocab))
    for w in sentence:
        if w not in vocab:
            continue
        weights[vocab.index(w)] += 1
    return weights / sum(weights)

def sim3(sentence1, sentence2):
    sentence1 = pos_tag(word_tokenize(sentence1))
    sentence2 = pos_tag(word_tokenize(sentence2))
    vocab = [pair for pair in sorted(set(sentence1).union(set(sentence2))) if penn_to_wn(pair[1])]

    w1 = get_counts(sentence1, vocab)
    w2 = get_counts(sentence2, vocab)

    synsets = [tagged_to_synsets(*tagged_word) for tagged_word in vocab]

    similarities = np.array([[
        max([s1.path_similarity(s2) or 0 for s1 in w1 for s2 in w2], default=0)
        for w2 in synsets] for w1 in synsets]
    )
    distances = np.sqrt(2*(1-similarities))
    distance = emd(w1, w2, distances)
    similarity = 1 - distance**2 / 2
    return similarity

print(sim3("use a tube to suck soda in","transfer blood to the heart using a pump and artery"))
print(sim3("use a tube to suck soda in","do some programming to get better"))
# 0.41046117311104957
# 0.19280421873943732

我们可以尝试在数据集上评估这种相似性方法-例如在Quora问题对上

import pandas as pd
from tqdm.auto import tqdm, trange
import matplotlib.pyplot as plt

df = pd.read_csv('http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv', sep='\t')
sample = df.sample(1000, random_state=1)
sims = pd.Series([sim3(sample.iloc[i].question1, sample.iloc[i].question2) for i in trange(sample.shape[0])], index=sample.index)

# produce a plot
sims[sample.is_duplicate==0].hist(density=True);
sims[sample.is_duplicate==1].hist(alpha=0.5, density=True);
plt.legend(['non-duplicates', 'duplicates'])
plt.title('distribution of wordnet-sentence-similarity\n on quora question pairs');

从图像中您可以看到,重复对的分数平均比不重复对的分数高得多,但是重叠仍然很大。

enter image description here

[如果您要使用定量指标,可以评估例如ROC AUC。在此数据集上,它是70%,虽然还不完美,但可以作为一个不错的基准。

from sklearn.metrics import roc_auc_score
print(roc_auc_score(sample.is_duplicate, sims))
# 0.7075210210273749
© www.soinside.com 2019 - 2024. All rights reserved.