Como calcular a similaridade de sentenças usando o modelo word2vec de gensim com python

125

De acordo com o Gensim Word2Vec , posso usar o modelo word2vec no pacote gensim para calcular a semelhança entre duas palavras.

por exemplo

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

No entanto, o modelo word2vec falha ao prever a similaridade da sentença. Descobri o modelo LSI com semelhança de sentença no gensim, mas que não parece que possa ser combinado com o modelo word2vec. O comprimento do corpus de cada sentença que tenho não é muito longo (menor que 10 palavras). Então, existem maneiras simples de atingir a meta?

zhfkt
fonte
4
Há um tutorial ACL discutir esta questão (entre outras coisas): youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Emiel
7
Agora você pode usar doc2vec das gensim e obter frase semelhança do mesmo módulo
kampta
@kampta. Oi, você sugeriria algum post que mostre a implementação?
Ian_De_Oliveira

Respostas:

86

Este é realmente um problema bastante desafiador que você está perguntando. A computação da similaridade de sentenças requer a construção de um modelo gramatical da frase, a compreensão de estruturas equivalentes (por exemplo, "ele caminhou até a loja ontem" e "ontem, caminhou até a loja"), encontrando similaridade não apenas nos pronomes e verbos, mas também nos substantivos próprios, encontrando co-ocorrências / relações estatísticas em muitos exemplos reais de texto, etc.

A coisa mais simples que você poderia tentar - embora eu não saiba como isso funcionaria e certamente não lhe daria os melhores resultados - seria primeiro remover todas as palavras "stop" (palavras como "the", " ", etc. que não acrescentam muito significado à sentença) e, em seguida, execute word2vec nas palavras das duas frases, some os vetores em uma frase, some os vetores na outra frase e encontre a diferença entre as somas. Resumindo-os em vez de fazer uma diferença em termos de palavras, você pelo menos não estará sujeito à ordem das palavras. Dito isto, isso falhará de várias maneiras e não será uma boa solução de forma alguma (embora as boas soluções para esse problema quase sempre envolvam uma certa quantidade de PNL, aprendizado de máquina e outras esperanças).

Portanto, a resposta curta é que não, não há uma maneira fácil de fazer isso (pelo menos para não fazê-lo bem).

Michael Aaron Safyan
fonte
4
Eu acho que você está certo. O método mais simples é acumular todos os vetores de palavras em uma frase e encontrar a diferença entre as somas. A propósito, esse método simples será efetuado pela contagem de palavras? Porque quanto mais palavras em uma frase, mais histograma será resumido.
zhfkt
2
@ zhfkt, provavelmente, sim. Portanto, você pode precisar dividir pelo número de palavras ou algumas delas para tentar calcular isso. De qualquer forma, qualquer heurística como essa será severamente falha.
Michael Aaron Safyan
75

Como você está usando o gensim, provavelmente deve usar a implementação do doc2vec. doc2vec é uma extensão do word2vec para o nível de frase, sentença e documento. É uma extensão bastante simples, descrita aqui

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

Gensim é legal porque é intuitivo, rápido e flexível. O que é ótimo é que você pode pegar as combinações de palavras pré-treinadas na página oficial word2vec e a camada syn0 do modelo Doc2Vec da gensim é exposta para que você possa propagar as combinações de palavras com esses vetores de alta qualidade!

GoogleNews-vectors-negative300.bin.gz (conforme vinculado no Código do Google )

Acho que o gensim é definitivamente a ferramenta mais fácil (e até agora, a melhor) para incorporar uma frase em um espaço vetorial.

Existem outras técnicas de sentença para vetor que não a proposta no artigo de Le & Mikolov acima. Socher e Manning, de Stanford, são certamente dois dos pesquisadores mais famosos que trabalham nesta área. O trabalho deles foi baseado no princípio da composição - a semântica da frase vem de:

1. semantics of the words

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

Eles propuseram alguns desses modelos (cada vez mais complexos) de como usar a composicionalidade para criar representações no nível de sentenças.

2011 - desdobramento do autoencoder recursivo (muito comparativamente simples. Comece aqui se estiver interessado)

2012 - rede neural de vetores matriciais

2013 - rede de tensores neurais

2015 - Árvore LSTM

seus documentos estão disponíveis em socher.org. Alguns desses modelos estão disponíveis, mas eu ainda recomendaria o doc2vec do gensim. Por um lado, o URAE 2011 não é particularmente poderoso. Além disso, ele é pré-treinado com pesos adequados para parafrasear dados de notícias. O código que ele fornece não permite que você treine novamente a rede. Você também não pode trocar em diferentes vetores de palavras, por isso não se preocupa com as incorporações pré-word2vec de Turian em 2011. Esses vetores certamente não estão no nível dos word2vec ou GloVe.

Ainda não trabalhou com o Tree LSTM, mas parece muito promissor!

tl; dr Sim, use o doc2vec do gensim. Mas existem outros métodos!

Willie
fonte
Você tem mais informações sobre como inicializar o modelo doc2vec com valores word2vec pré-treinados?
Simon H
42

Se você estiver usando o word2vec, precisará calcular o vetor médio para todas as palavras em cada frase / documento e usar a semelhança de cosseno entre os vetores:

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

Calcular semelhança:

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
tbmihailov
fonte
4
Você poderia dar mais explicações sobre index2word_set e model.index2word? Obrigado.
precisa
3
Observe que calcular o "vetor médio" é tanto uma escolha arbitrária quanto não calculá-lo.
gented 16/08/19
2
Estou surpreso porque essa não é a resposta principal, ela funciona muito bem e não tem problema de sequência que o método de média tem.
Asim
Esta é a resposta que eu estava procurando. Resolvi meu problema. Obrigado pela solução
iRunner 02/12/19
25

você pode usar o algoritmo de distância do Word Mover. Aqui está uma descrição fácil sobre WMD .

#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: se você encontrar um erro sobre a importação da biblioteca pyemd , poderá instalá-lo usando o seguinte comando:

pip install pyemd
Ehsan
fonte
2
Eu usei WMD antes e funciona bem silenciosamente, no entanto, seria sufocado por corpus grande. Tente SoftCosineSimilarity. Também encontrado em gensim ( twitter.com/gensim_py/status/963382840934195200 )
krinker
1
WMD não é muito rápido, no entanto, quando você deseja consultar um corpus.
Amartya
18

Depois de calcular a soma dos dois conjuntos de vetores de palavras, você deve pegar o cosseno entre os vetores, e não o diff. O cosseno pode ser calculado tomando o produto escalar dos dois vetores normalizados. Assim, a contagem de palavras não é um fator.

Rani Nelken
fonte
1
você pode fornecer um pouco de pseudocódigo sobre como fazer isso (eu não estou usando gensim / python)
dcsan
13

Existe uma função na documentação que faz uma lista de palavras e compara suas semelhanças.

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

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

Gostaria de atualizar a solução existente para ajudar as pessoas que vão calcular a semelhança semântica das frases.

Passo 1:

Carregue o modelo adequado usando gensim e calcule os vetores das palavras na frase e armazene-os como uma lista de palavras

Etapa 2: Computando o vetor de sentença

O cálculo da semelhança semântica entre as frases era difícil antes, mas recentemente foi proposto um artigo chamado " UMA LINHA DE REFERÊNCIA SIMPLES, MAS INDEPENDENTE PARA EMBEDDINGS DE SENTENÇA ", que sugere uma abordagem simples, calculando a média ponderada dos vetores de palavras na frase e removendo As projeções dos vetores médios em seu primeiro componente principal. Aqui o peso de uma palavra w é a / (a ​​+ p (w)) com um parâmetro e p (w) a frequência (estimada) da palavra chamada frequência inversa suave .este método apresentando um desempenho significativamente melhor.

Um código simples para calcular o vetor de sentenças usando SIF (frequência inversa suave), o método proposto no artigo, foi apresentado aqui

Etapa 3: usando o sklearn cosine_similarity carregue dois vetores para as frases e calcule a similaridade.

Este é o método mais simples e eficiente para calcular a semelhança de sentenças.

Poorna Prudhvi
fonte
2
papel muito bom. note: o link para a implementação do SIF requer escrever o método get_word_frequency () que pode ser facilmente realizado usando o Counter () do Python e retornando um dict com as chaves: palavras únicas w, valores: # w / # total doc len
Quetzalcoatl
8

Estou usando o método a seguir e funciona bem. Você primeiro precisa executar um POSTagger e depois filtrar sua frase para se livrar das palavras de parada (determinantes, conjunções, ...). Eu recomendo o TextBlob APTagger . Em seguida, você cria um word2vec calculando a média de cada vetor de palavra na frase. O método n_similarity no Gemsim word2vec faz exatamente isso ao permitir passar dois conjuntos de palavras para comparar.

lechatpito
fonte
Qual é a diferença entre obter a média dos vetores e adicioná-los para criar um vetor de sentença?
Καrτhικ
1
A diferença é que o tamanho do vetor é fixado para todas as sentenças
lechatpito
Não há diferença desde que você use a semelhança de cosseno. @lechatpito Nada a ver com o tamanho do vetor. Os vetores são somados, não concatenados.
Wok
6

Existem extensões do Word2Vec destinadas a resolver o problema de comparar partes mais longas do texto, como frases ou sentenças. Um deles é o paragraph2vec ou doc2vec.

"Representações distribuídas de frases e documentos" http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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

Máx.
fonte
2
Vale mencionar em breve como o algoritmo apresentado funciona. Você basicamente adiciona um "token" exclusivo a cada enunciado e calcula os vetores word2vec. No final, você receberá os vetores de palavras para cada uma das suas palavras no corpus (desde que você solicite todas as palavras, também as únicas). Cada "token" exclusivo na expressão representará essa expressão. Há alguma controvérsia sobre os resultados apresentados no artigo, mas isso é outra história.
Vladislavs Dovgalecs
5

O Gensim implementa um modelo chamado Doc2Vec para incorporação de parágrafos .

Existem diferentes tutoriais apresentados como notebooks IPython:

Outro método dependeria do Word2Vec e da Distância do Movedor de Palavras (WMD) , conforme mostrado neste tutorial:

Uma solução alternativa seria confiar em vetores médios:

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)

Por fim, se você pode executar o Tensorflow, tente: https://tfhub.dev/google/universal-sentence-encoder/2

Wok
fonte
4

Eu tentei os métodos fornecidos pelas respostas anteriores. Funciona, mas a principal desvantagem é que, quanto mais longas forem as sentenças, maior será a similaridade (para calcular a similaridade, uso a pontuação de cosseno das duas combinações médias de duas sentenças), pois quanto mais as palavras, mais efeitos semânticos positivos será adicionado à frase.

Eu pensei que deveria mudar de idéia e usar a frase incorporada, como estudada neste artigo e neste .

Lerner Zhang
fonte
3

O grupo de pesquisa do Facebook lançou uma nova solução chamada InferSent Results e o código foi publicado no Github, verifique seu repositório. É bem legal. Estou planejando usá-lo. https://github.com/facebookresearch/InferSent

seu artigo https://arxiv.org/abs/1705.02364 Resumo: Muitos sistemas modernos de PNL contam com a incorporação de palavras, previamente treinadas de maneira não supervisionada em grandes corpora, como recursos básicos. Os esforços para obter incorporações para pedaços maiores de texto, como frases, não foram tão bem-sucedidos. Várias tentativas de aprender representações não supervisionadas de sentenças não alcançaram desempenho satisfatório o suficiente para serem amplamente adotadas. Neste artigo, mostramos como representações universais de frases treinadas usando os dados supervisionados dos conjuntos de dados de Inferência de Linguagem Natural de Stanford podem consistentemente superar métodos não supervisionados, como os vetores SkipThought, em uma ampla variedade de tarefas de transferência. Assim como a visão por computador usa o ImageNet para obter recursos, que podem ser transferidos para outras tarefas, nosso trabalho tende a indicar a adequação da inferência de linguagem natural para transferir o aprendizado para outras tarefas da PNL. Nosso codificador está disponível ao público.

Ayman Salama
fonte
3

Se não estiver usando o Word2Vec, temos outro modelo para encontrá-lo usando o BERT para incorporar. Abaixo estão o link de referência 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))

Outro link para seguir https://github.com/hanxiao/bert-as-service

kamran kausar
fonte