Вычислить подобие косинуса с учетом 2 строк предложения - программирование
Подтвердить что ты не робот

Вычислить подобие косинуса с учетом 2 строк предложения

Из Python: tf-idf-cosine: чтобы найти сходство документов, можно вычислить сходство документа с помощью tf-idf cosine. Без импорта внешних библиотек, есть ли какие-либо способы вычисления косинусного сходства между двумя строками?

s1 = "This is a foo bar sentence ."
s2 = "This sentence is similar to a foo bar sentence ."
s3 = "What is this string ? Totally not related to the other two lines ."

cosine_sim(s1, s2) # Should give high cosine similarity
cosine_sim(s1, s3) # Shouldn't give high cosine similarity value
cosine_sim(s2, s3) # Shouldn't give high cosine similarity value
4b9b3361

Ответ 1

Простая реализация pure-Python будет следующей:

import re, math
from collections import Counter

WORD = re.compile(r'\w+')

def get_cosine(vec1, vec2):
     intersection = set(vec1.keys()) & set(vec2.keys())
     numerator = sum([vec1[x] * vec2[x] for x in intersection])

     sum1 = sum([vec1[x]**2 for x in vec1.keys()])
     sum2 = sum([vec2[x]**2 for x in vec2.keys()])
     denominator = math.sqrt(sum1) * math.sqrt(sum2)

     if not denominator:
        return 0.0
     else:
        return float(numerator) / denominator

def text_to_vector(text):
     words = WORD.findall(text)
     return Counter(words)

text1 = 'This is a foo bar sentence .'
text2 = 'This sentence is similar to a foo bar sentence .'

vector1 = text_to_vector(text1)
vector2 = text_to_vector(text2)

cosine = get_cosine(vector1, vector2)

print 'Cosine:', cosine

Печать

Cosine: 0.861640436855

Используемая здесь косинусная формула описана здесь.

Это не включает взвешивание слов с помощью tf-idf, но для использования tf-idf вам нужно иметь достаточно большой корпус, из которого можно оценить весы tfidf.

Вы также можете развить его дальше, используя более сложный способ извлечь слова из фрагмента текста, основывать его или леммитировать и т.д.

Ответ 2

Короткий ответ: "Нет, это невозможно сделать принципиально, что работает даже отдаленно". Это нерешенная проблема в исследованиях обработки естественного языка, а также, оказывается, является предметом моей докторской работы. Я очень кратко обобщу, где мы находимся, и укажем на несколько публикаций:

Значение слов

Самое важное предположение здесь состоит в том, что можно получить вектор, который представляет каждое слово в предложении в quesion. Этот вектор обычно выбирается для захвата контекстов, в которые может появиться слово. Например, если мы рассматриваем только три контекста "есть", "красный" и "пушистый", слово "кошка" может быть представлено как [98, 1, 87], потому что, если бы вы читали очень длинный фрагмент текста (несколько миллиардов слов не редкость по сегодняшнему стандарту), слово "кошка" будет появляться очень часто в контексте "пушистых" и "съедающих", но не так часто в контексте "красного". Точно так же "собака" может быть представлена ​​как [87,2,34], а "зонтик" может быть [1,13,0]. Воображая эти векторы как точки в трехмерном пространстве, "кошка" явно ближе к "собаке", чем к "зонтику", поэтому "кошка" также означает нечто более похожее на "собаку", чем на "зонтик".

Эта линия работы была исследована с начала 90-х годов (например, этот работа Greffenstette) и принесла некоторые удивительно хорошие результаты. Например, вот несколько случайных записей в тезаурусе, который я недавно создал, когда мой компьютер читал wikipedia:

theory -> analysis, concept, approach, idea, method
voice -> vocal, tone, sound, melody, singing
james -> william, john, thomas, robert, george, charles

Эти списки похожих слов были получены полностью без вмешательства человека - вы вставляете текст и возвращаетесь через несколько часов.

Проблема с фразами

Вы можете спросить, почему мы не делаем то же самое для более длинных фраз, таких как "лисицы-лисицы любят фрукты". Это потому, что у нас недостаточно текста. Для того чтобы мы могли достоверно установить, что X похож на, нам нужно увидеть много примеров использования X в контексте. Когда X - это одно слово, например "голос", это не слишком сложно. Однако по мере того, как X становится длиннее, шансы найти естественные вхождения X экспоненциально медленнее. Для сравнения, у Google есть около 1B страниц, содержащих слово "лиса", а не одна страница, содержащая "лисицы-лисицы", несмотря на то, что это совершенно правильное английское предложение, и все мы понимаем, что это значит.

Состав

Чтобы решить проблему разреженности данных, мы хотим выполнить композицию, т.е. взять векторы для слов, которые легко получить из реального текста, и объединить их таким образом, чтобы фиксировать их значение. Плохая новость заключается в том, что пока никто не смог сделать это хорошо.

Самый простой и очевидный способ - добавить или размножить отдельные векторы слов вместе. Это приводит к нежелательному побочному эффекту, что "кошки преследуют собак" и "собачьи кошки-кошки" означают то же самое для вашей системы. Кроме того, если вы умножаетесь, вы должны быть особенно осторожны или все предложения будут представлены в виде [0,0,0,..., 0], который побеждает точку.

Дальнейшее чтение

Я не буду обсуждать более сложные методы композиции, которые были предложены до сих пор. Я предлагаю вам прочитать Katrin Erk "векторные космические модели значения слова и фразы: обзор" . Это очень хороший обзор высокого уровня, чтобы вы начали. К сожалению, на веб-сайте издателя не имеется свободного доступа, напишите автору напрямую, чтобы получить копию. В этой статье вы найдете ссылки на многие более конкретные методы. Более понятными являются Митчел и Лапата (2008) и Барони и Зампарелли (2010).


Редактировать после комментария @vpekar: Суть этого ответа состоит в том, чтобы подчеркнуть тот факт, что, хотя существуют и наивные методы (например, добавление, умножение, сходство с поверхностью и т.д.), Они в корне ошибочны и в целом не следует ожидать от них большой производительности.

Ответ 3

Спасибо @vpekar за вашу реализацию. Это очень помогло. Я просто обнаружил, что он пропускает вес tf-idf при вычислении сходства косинусов. Счетчик (слово) возвращает словарь, который имеет список слов вместе с их появлением.

cos (q, d) = sim (q, d) = (q · d)/(| q || d |) = (sum (qi, di)/(sqrt (sum (qi2))) * (sqrt (сумма (vi2))), где я = 1 - v)

  • qi - вес tf-idf термина я в запросе.
  • di - это tf-idf
  • вес термина я в документе. | Д | и | d | - длины q и d.
  • Это косинус-подобие q и d.,,, или, эквивалентно, косинус угла между q и d.

Пожалуйста, не стесняйтесь просматривать мой код здесь. Но сначала вам нужно будет скачать пакет anaconda. Он автоматически установит вам путь python в Windows. Добавьте этот интерпретатор python в Eclipse.

Ответ 4

Что ж, если вам известны вложения слов, такие как Glove/Word2Vec/Numberbatch, ваша работа наполовину выполнена. Если нет, позвольте мне объяснить, как это можно решить. Преобразуйте каждое предложение в жетоны слова и представляйте каждый из этих жетонов как векторы высокой размерности (используя предварительно обученные вложения слов, или вы могли бы даже обучить их самих!). Итак, теперь вы просто не фиксируете их поверхностное сходство, а скорее извлекаете значение каждого слова, составляющего предложение в целом. После этого вычислите их косинусное сходство и вы настроены.

Ответ 5

Попробуй это. Загрузите файл 'numberbatch-en-17.06.txt' из https://conceptnet.s3.amazonaws.com/downloads/2017/numberbatch/numberbatch-en-17.06.txt.gz и распакуйте его. Функция 'get_sentence_vector' использует простую сумму векторов слов. Однако его можно улучшить, используя взвешенную сумму, где веса пропорциональны Tf-Idf каждого слова.

import math
import numpy as np

std_embeddings_index = {}
with open('path/to/numberbatch-en-17.06.txt') as f:
    for line in f:
        values = line.split(' ')
        word = values[0]
        embedding = np.asarray(values[1:], dtype='float32')
        std_embeddings_index[word] = embedding

def cosineValue(v1,v2):
    "compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
    sumxx, sumxy, sumyy = 0, 0, 0
    for i in range(len(v1)):
        x = v1[i]; y = v2[i]
        sumxx += x*x
        sumyy += y*y
        sumxy += x*y
    return sumxy/math.sqrt(sumxx*sumyy)


def get_sentence_vector(sentence, std_embeddings_index = std_embeddings_index ):
    sent_vector = 0
    for word in sentence.lower().split():
        if word not in std_embeddings_index :
            word_vector = np.array(np.random.uniform(-1.0, 1.0, 300))
            std_embeddings_index[word] = word_vector
        else:
            word_vector = std_embeddings_index[word]
        sent_vector = sent_vector + word_vector

    return sent_vector

def cosine_sim(sent1, sent2):
    return cosineValue(get_sentence_vector(sent1), get_sentence_vector(sent2))

Я баллотировался на заданные предложения и нашел следующие результаты

s1 = "This is a foo bar sentence ."
s2 = "This sentence is similar to a foo bar sentence ."
s3 = "What is this string ? Totally not related to the other two lines ."

print cosine_sim(s1, s2) # Should give high cosine similarity
print cosine_sim(s1, s3) # Shouldn't give high cosine similarity value
print cosine_sim(s2, s3) # Shouldn't give high cosine similarity value

0.9851735249068168
0.6570885718962608
0.6589335425458225