Текст семанти c сходство по аналогии в гиперном уровне с использованием Python - PullRequest
1 голос
/ 25 марта 2020

У меня есть несколько длинных (50 строк) абзацев, которые я хотел бы измерить их сходством, используя Python. Меня больше интересует сходство семанти c этих текстов на гиперном уровне (термин в лингвистике c) с акцентом на функции и процессы. Чтобы уточнить, я бы назвал два фрагмента текста одинаковыми, если оба ссылаются на одну и ту же функцию или процесс, независимо от используемых в них слов.

Вот два примера: Similar_Sentences = ("использовать трубку для сосания сода в "," передача крови к сердцу с помощью насоса и артерии "). Unsimil_Sentences = («использовать трубку, чтобы всасывать газировку», «сделать какое-то программирование, чтобы поправиться»).

В первом примере «трубка» ~ «артерия», «газировка» ~ «кровь», и "всосать" ~ "перевести на". Я надеюсь, что ясно, что меня интересует.

Основываясь на моих исследованиях алгоритмов и инструментов НЛП, NLTK и Wor dNet в Python кажутся подходящими инструментами для этой задачи, но я не уверен как.

Ссылаемся на любой соответствующий учебник или источник для обучения, а также любые предложения приветствуются заранее.

1 Ответ

1 голос
/ 25 марта 2020

На NLPForHackers есть отличный пост , описывающий, как реализовать сходство предложений с помощью wor dnet.

Их ингредиенты:

  1. POS-тегер для сужения списка наборов для каждого слова (после этого авторы просто берут первый набор)
  2. путь_схожесть : показатель c, показывающий, как далеко находятся два слова друг от друга на графе таксономии
  3. Агрегация сходств на уровне слов: макс., А затем среднее.

Это уже работает довольно хорошо: для вашего положительного примера оценка сходства равна 0,29, а для отрицательного - всего 0,20.

Я бы предложил несколько улучшений. :

  1. Используйте не только первый найденный набор, но и максимум всех наборов для слова. Это делает оценки для ваших положительных и отрицательных примеров 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

Если вам нужен количественный показатель c, вы можете оценить, например, RO C AU C. В этом наборе данных он составляет 70%, что далеко от совершенства, но дает неплохой базовый уровень.

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