Как рассчитать схожесть предложений, используя word2vec модель gensim с Python


125

Согласно Gensim Word2Vec , я могу использовать модель word2vec в пакете gensim для вычисления сходства между двумя словами.

например

trained_model.similarity('woman', 'man') 
0.73723527

Однако модель word2vec не может предсказать сходство предложений. Я обнаружил модель LSI с подобием предложений в gensim, но, похоже, это не может быть объединено с моделью word2vec. Длина корпуса каждого имеющегося у меня предложения не очень велика (короче 10 слов). Итак, есть ли простые способы достичь цели?


4
Этот вопрос обсуждается (среди прочего) в руководстве по ACL: youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Эмиель

7
Теперь вы можете использовать doc2vec от gensim и получать схожесть предложений из того же модуля
kampta

@kampta. Привет, не могли бы вы предложить какой-нибудь пост, показывающий реализацию?
Ian_De_Oliveira 02

1
Оформить заказ red-technologies.com/doc2vec-tutorial
kampta

Ответы:


86

На самом деле это довольно сложная проблема, которую вы задаете. Вычисление сходства предложений требует построения грамматической модели предложения, понимания эквивалентных структур (например, «он ходил в магазин вчера» и «вчера, он ходил в магазин»), обнаружения сходства не только в местоимениях и глаголах, но и в имена собственные, поиск статистических совпадений / отношений во множестве реальных текстовых примеров и т. д.

Самое простое, что вы могли бы попробовать - хотя я не знаю, насколько хорошо это будет работать и определенно не даст вам оптимальных результатов - это сначала удалить все "стоп-слова" (такие как "the", "an "и т. д., которые не добавляют особого смысла предложению), а затем запустите word2vec для слов в обоих предложениях, просуммируйте векторы в одном предложении, просуммируйте векторы в другом предложении, а затем найдите разницу между суммы. Суммируя их, вместо того, чтобы различать слова, вы, по крайней мере, не будете зависеть от порядка слов. Тем не менее, это приведет к неудаче по многим причинам и ни в коем случае не является хорошим решением (хотя хорошие решения этой проблемы почти всегда включают в себя некоторое количество НЛП, машинного обучения и других умений).

Итак, короткий ответ: нет, простого способа сделать это (по крайней мере, не делать хорошо) не существует.


4
Я думаю, вы правы. Самый простой метод - собрать все векторы слов в одном предложении и найти разницу между суммами. Кстати, будет ли этот простой метод влиять на подсчет слов? Потому что чем больше слов в одном предложении, тем больше будет суммировано гистограмма.
zhfkt 03

2
@zhfkt, скорее всего, да. Таким образом, вам может потребоваться разделить на количество слов или что-то подобное, чтобы попытаться исключить это. В любом случае, любая подобная эвристика будет иметь серьезные недостатки.
Майкл Аарон Сафян


75

Поскольку вы используете gensim, вам, вероятно, следует использовать его реализацию doc2vec. doc2vec - это расширение word2vec на уровне фраз, предложений и документов. Это довольно простое расширение, описанное здесь

http://cs.stanford.edu/~quocle/paragraph_vector.pdf

Gensim хорош тем, что он интуитивно понятен, быстр и гибок. Что здорово, так это то, что вы можете получить предварительно обученные вложения слов с официальной страницы word2vec, а слой syn0 модели Gensim Doc2Vec открыт, так что вы можете засеять вложения слов этими высококачественными векторами!

GoogleNews-vectors-negative300.bin.gz (как указано в Google Code )

Я думаю, что gensim - определенно самый простой (и пока что для меня лучший) инструмент для встраивания предложения в векторное пространство.

Существуют и другие методы преобразования предложения в вектор, кроме предложенного в статье Ле и Миколова выше. Сохер и Мэннинг из Стэнфорда, несомненно, являются двумя из самых известных исследователей, работающих в этой области. Их работа была основана на принципе композиции - семантика предложения происходит от:

1. semantics of the words

2. rules for how these words interact and combine into phrases

Они предложили несколько таких моделей (которые становятся все более сложными) того, как использовать композиционность для построения представлений на уровне предложений.

2011 - разворачивание рекурсивного автоэнкодера (относительно просто. Начните здесь, если интересно)

2012 - матрично-векторная нейронная сеть

2013 - нейронная тензорная сеть

2015 - Дерево LSTM

все его статьи доступны на socher.org. Некоторые из этих моделей доступны, но я все же рекомендую doc2vec от gensim. Во-первых, URAE 2011 года не особенно мощный. Кроме того, он поставляется с предварительно обученными весами, подходящими для перефразирования новостных данных. Предоставляемый им код не позволяет переобучать сеть. Вы также не можете поменять местами разные векторы слов, так что вы застряли на вложениях pre-word2vec 2011 года от Turian. Эти векторы, конечно же, не на уровне word2vec или GloVe.

Еще не работал с Tree LSTM, но он кажется очень многообещающим!

tl; dr Да, используйте doc2vec от gensim. Но есть и другие методы!


У вас есть дополнительная информация о том, как инициализировать модель doc2vec с предварительно обученными значениями word2vec?
Simon H

42

Если вы используете word2vec, вам необходимо вычислить средний вектор для всех слов в каждом предложении / документе и использовать косинусное сходство между векторами:

import numpy as np
from scipy import spatial

index2word_set = set(model.wv.index2word)

def avg_feature_vector(sentence, model, num_features, index2word_set):
    words = sentence.split()
    feature_vec = np.zeros((num_features, ), dtype='float32')
    n_words = 0
    for word in words:
        if word in index2word_set:
            n_words += 1
            feature_vec = np.add(feature_vec, model[word])
    if (n_words > 0):
        feature_vec = np.divide(feature_vec, n_words)
    return feature_vec

Рассчитайте сходство:

s1_afv = avg_feature_vector('this is a sentence', model=model, num_features=300, index2word_set=index2word_set)
s2_afv = avg_feature_vector('this is also sentence', model=model, num_features=300, index2word_set=index2word_set)
sim = 1 - spatial.distance.cosine(s1_afv, s2_afv)
print(sim)

> 0.915479828613

4
Не могли бы вы дать больше объяснений по index2word_set и model.index2word? Спасибо.
theteddyboy

3
Обратите внимание, что вычисление «среднего вектора» - такой же произвольный выбор, как и не вычисление его вообще.
gented

2
Я удивлен, почему это не лучший ответ, он работает достаточно хорошо и не имеет проблемы с последовательностью, как у метода усреднения.
Асим

Это ответ, который я искал. Моя проблема решена. Спасибо за решение
iRunner 02

25

вы можете использовать алгоритм Word Mover's Distance. вот простое описание ОМУ .

#load word2vec model, here GoogleNews is used
model = gensim.models.KeyedVectors.load_word2vec_format('../GoogleNews-vectors-negative300.bin', binary=True)
#two sample sentences 
s1 = 'the first sentence'
s2 = 'the second text'

#calculate distance between two sentences using WMD algorithm
distance = model.wmdistance(s1, s2)

print ('distance = %.3f' % distance)

Ps: если вы столкнулись с ошибкой при импорте библиотеки pyemd , вы можете установить ее, используя следующую команду:

pip install pyemd

2
Раньше я использовал WMD, он работает тихо, но при этом подавится большим корпусом. Попробуйте SoftCosineSimilarity. Также найдено в gensim ( twitter.com/gensim_py/status/963382840934195200 )
krinker

1
Однако WMD не очень быстр, когда вы хотите запросить корпус.
Amartya

18

После того, как вы вычислили сумму двух наборов векторов слов, вы должны взять косинус между векторами, а не разницу. Косинус можно вычислить, взяв скалярное произведение двух нормализованных векторов. Таким образом, количество слов не имеет значения.


1
не могли бы вы дать немного псевдокода о том, как это сделать (я не использую gensim / python)
dcsan 02

13

Существует функция из документации принимает список слов и сравнивая их сходство.

s1 = 'This room is dirty'
s2 = 'dirty and disgusting room' #corrected variable name

distance = model.wv.n_similarity(s1.lower().split(), s2.lower().split())

12

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

Шаг 1:

Загрузите подходящую модель с помощью gensim и вычислите векторы слов для слов в предложении и сохраните их как список слов.

Шаг 2: вычисление вектора предложения

Раньше вычисление семантического сходства между предложениями было трудным, но недавно была предложена статья под названием « ПРОСТАЯ, НО СЛОЖНАЯ БАЗА ВНЕДРЕНИЯ ПРЕДЛОЖЕНИЙ », которая предлагает простой подход: вычисление средневзвешенного числа векторов слов в предложении с последующим удалением проекции средних векторов на их первую главную составляющую. Здесь вес слова w равен a / (a ​​+ p (w)), где a - параметр, а p (w) - (оценочная) частота слова, называемая гладкой обратной частотой. .Этот метод работает значительно лучше.

Простой код для вычисления вектора приговора с помощью SIF (плавного обратной частоты) метода , предложенного в работе была дано здесь

Шаг 3: используя sklearn cosine_similarity, загрузите два вектора для предложений и вычислите сходство.

Это наиболее простой и эффективный метод вычисления сходства предложений.


2
очень хорошая бумага. примечание: ссылка на реализацию SIF требует написания метода get_word_frequency (), который можно легко выполнить, используя Python Counter () и вернув dict с ключами: уникальные слова w, значения: # w / # total doc len
Quetzalcoatl,

8

Я использую следующий метод, и он хорошо работает. Сначала вам нужно запустить POSTagger, а затем отфильтровать предложение, чтобы избавиться от стоп-слов (детерминанты, союзы, ...). Я рекомендую TextBlob APTagger . Затем вы создаете word2vec, взяв среднее значение каждого вектора слов в предложении. Метод n_similarity в Gemsim word2vec делает именно это, позволяя передавать два набора слов для сравнения.


В чем разница между взятием среднего значения векторов и их сложением для создания вектора предложения?
Καrτhικ 01

1
Разница в том, что размер вектора фиксирован для всех предложений
lechatpito

Нет никакой разницы, пока вы используете косинусное подобие. @lechatpito Ничего общего с размером вектора. Векторы суммируются, а не объединяются.
Wok

6

Существуют расширения Word2Vec, предназначенные для решения проблемы сравнения более длинных фрагментов текста, таких как фразы или предложения. Один из них - paragraph2vec или doc2vec.

«Распределенное представление приговоров и документов» http://cs.stanford.edu/~quocle/paragraph_vector.pdf

http://rare-technologies.com/doc2vec-tutorial/


2
Вкратце стоит упомянуть, как работает представленный алгоритм. По сути, вы добавляете уникальный «токен» к каждому высказыванию и вычисляете векторы word2vec. В конце вы получите векторы слов для каждого вашего слова в корпусе (при условии, что вы запрашиваете все слова, а также уникальные). Каждый уникальный «жетон» в высказывании будет представлять это высказывание. Есть некоторые разногласия по поводу результатов, представленных в документе, но это уже другая история.
Владислав Довгалец

5

Gensim реализует модель под названием Doc2Vec для встраивания абзацев .

В виде записных книжек IPython представлены различные учебные пособия:

Другой метод будет полагаться на Word2Vec и Word Mover's Distance (WMD) , как показано в этом руководстве:

Альтернативным решением было бы полагаться на средние векторы:

from gensim.models import KeyedVectors
from gensim.utils import simple_preprocess    

def tidy_sentence(sentence, vocabulary):
    return [word for word in simple_preprocess(sentence) if word in vocabulary]    

def compute_sentence_similarity(sentence_1, sentence_2, model_wv):
    vocabulary = set(model_wv.index2word)    
    tokens_1 = tidy_sentence(sentence_1, vocabulary)    
    tokens_2 = tidy_sentence(sentence_2, vocabulary)    
    return model_wv.n_similarity(tokens_1, tokens_2)

wv = KeyedVectors.load('model.wv', mmap='r')
sim = compute_sentence_similarity('this is a sentence', 'this is also a sentence', wv)
print(sim)

Наконец, если вы можете запустить Tensorflow, вы можете попробовать: https://tfhub.dev/google/universal-sentence-encoder/2


4

Я пробовал методы, указанные в предыдущих ответах. Это работает, но его главный недостаток заключается в том, что чем длиннее предложения, тем больше будет сходство (для вычисления сходства я использую косинусный балл двух средних вложений любых двух предложений), поскольку чем больше слов, тем больше положительных семантических эффектов. будет добавлено к предложению.

Я подумал, что мне следует передумать и вместо этого использовать вложение предложений, как описано в этой и этой статье .


3

Группа Facebook Research выпустила новое решение под названием InferSent Results, и код опубликован на Github, проверьте их репо. Это довольно круто. Планирую использовать. https://github.com/facebookresearch/InferSent

их статья https://arxiv.org/abs/1705.02364 Аннотация: Многие современные системы НЛП полагаются на вложения слов, ранее обученные неконтролируемым образом на больших корпусах, в качестве базовых функций. Однако попытки получить вложения для больших фрагментов текста, таких как предложения, не увенчались успехом. Несколько попыток обучения неконтролируемому представлению предложений не дали достаточных результатов, чтобы получить широкое распространение. В этой статье мы покажем, как универсальные представления предложений, обученные с использованием контролируемых данных из наборов данных Stanford Natural Language Inference, могут последовательно превосходить неконтролируемые методы, такие как векторы SkipThought, в широком диапазоне задач передачи. Подобно тому, как компьютерное зрение использует ImageNet для получения функций, которые затем могут быть переданы другим задачам, наша работа имеет тенденцию указывать на пригодность логического вывода на естественном языке для переноса обучения на другие задачи НЛП. Наш кодировщик находится в открытом доступе.


3

Если вы не используете Word2Vec, у нас есть другая модель, чтобы найти ее, используя BERT для встраивания. Ниже приведена справочная ссылка https://github.com/UKPLab/sentence-transformers

pip install -U sentence-transformers

from sentence_transformers import SentenceTransformer
import scipy.spatial

embedder = SentenceTransformer('bert-base-nli-mean-tokens')

# Corpus with example sentences
corpus = ['A man is eating a food.',
          'A man is eating a piece of bread.',
          'The girl is carrying a baby.',
          'A man is riding a horse.',
          'A woman is playing violin.',
          'Two men pushed carts through the woods.',
          'A man is riding a white horse on an enclosed ground.',
          'A monkey is playing drums.',
          'A cheetah is running behind its prey.'
          ]
corpus_embeddings = embedder.encode(corpus)

# Query sentences:
queries = ['A man is eating pasta.', 'Someone in a gorilla costume is playing a set of drums.', 'A cheetah chases prey on across a field.']
query_embeddings = embedder.encode(queries)

# Find the closest 5 sentences of the corpus for each query sentence based on cosine similarity
closest_n = 5
for query, query_embedding in zip(queries, query_embeddings):
    distances = scipy.spatial.distance.cdist([query_embedding], corpus_embeddings, "cosine")[0]

    results = zip(range(len(distances)), distances)
    results = sorted(results, key=lambda x: x[1])

    print("\n\n======================\n\n")
    print("Query:", query)
    print("\nTop 5 most similar sentences in corpus:")

    for idx, distance in results[0:closest_n]:
        print(corpus[idx].strip(), "(Score: %.4f)" % (1-distance))

Другая ссылка для перехода на https://github.com/hanxiao/bert-as-service

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.