簡體   English   中英

使用Python在上位詞級別進行類比的文本語義相似度

[英]Text semantic similarity by analogy in the hypernym level using Python

我有一些很長的(50 行)段落,我想用 Python 來衡量它們的相似性。 我對這些文本在上位詞(語言學術語)層面的語義相似性更感興趣,重點是功能和過程。 為了進一步澄清,如果兩個文本都指代相同的功能或過程,無論它們使用什么詞,我都會稱它們為相似的。

這里有兩個例子:Similar_Sentences =(“用管子吸入蘇打水”,“使用泵和動脈將血液輸送到心臟”)。 Unsimilar_Sentences =(“用管子吸蘇打水”,“做一些編程以變得更好”)。

在第一個例子中,“管”~“動脈”、“蘇打水”~“血液”和“吸入”~“轉移到”。 我希望我對什么很感興趣。

根據我對 NLP 算法和工具的研究,Python 中的 NLTK 和 WordNet 似乎是完成這項任務的正確工具,但我不確定如何。

提前參考任何相關教程或學習資源,以及任何建議。

NLPForHackers 上一篇很棒的文章,描述了如何使用 wordnet 實現句子相似性。

他們的成分:

  1. 一個 POS 標記器,用於縮小每個單詞的同義詞列表(在此之后,作者只采用第一個同義詞集)
  2. path_similarity :一個指標,顯示兩個詞在分類圖上的距離
  3. 詞級相似度的聚合:最大值,然后是平均值。

這已經很有效了:對於正例,相似度得分為 0.29,而對於負例,得分僅為 0.20。

我會建議一些改進:

  1. 不僅使用第一個找到的同義詞集,而且使用一個單詞的所有同義詞集的最大值。 它使您的正面和負面示例的分數分別為 0.36 和 0.23 - 彼此之間的距離比以前更遠
  2. 使用詞移動距離來聚合詞的相似性,而不是 max-and-mean。 我通過公式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 Question Pairs

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');

您可以從圖像中看到,重復對的得分平均遠高於非重復對,但重疊仍然很大。

在此處輸入圖片說明

如果您想要一個定量指標,您可以評估例如 ROC AUC。 在這個數據集上,它是 70%,這遠非完美,但卻是一個不錯的基線。

from sklearn.metrics import roc_auc_score
print(roc_auc_score(sample.is_duplicate, sims))
# 0.7075210210273749

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM