Cómo calcular la similitud de la oración usando el modelo word2vec de gensim con python

125

De acuerdo con Gensim Word2Vec , puedo usar el modelo word2vec en el paquete gensim para calcular la similitud entre 2 palabras.

p.ej

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

Sin embargo, el modelo word2vec no puede predecir la similitud de la oración. Descubro el modelo LSI con similitud de oraciones en gensim, pero que no parece que pueda combinarse con el modelo word2vec. La longitud del corpus de cada oración que tengo no es muy larga (menos de 10 palabras). Entonces, ¿hay formas simples de lograr el objetivo?

zhfkt
fuente
44
Hay un tutorial de ACL sobre este tema (entre otras cosas): youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Emiel
77
Ahora puede usar doc2vec de gensim y obtener similitud de oración del mismo módulo
kampta
@kampta. Hola, ¿sugerirías alguna publicación que muestre la implementación?
Ian_De_Oliveira

Respuestas:

86

En realidad, este es un problema bastante desafiante que estás preguntando. Calcular la similitud de las oraciones requiere construir un modelo gramatical de la oración, comprender las estructuras equivalentes (por ejemplo, "caminó a la tienda ayer" y "ayer, caminó a la tienda"), encontrando similitud no solo en los pronombres y verbos, sino también en el sustantivos propios, encontrar coincidencias / relaciones estadísticas en muchos ejemplos textuales reales, etc.

Lo más simple que podría intentar, aunque no sé qué tan bien funcionaría esto y ciertamente no le daría los resultados óptimos, sería eliminar primero todas las palabras "stop" (palabras como "the", "an ", etc. que no agregan mucho significado a la oración) y luego ejecutan word2vec en las palabras en ambas oraciones, suman los vectores en una oración, suman los vectores en la otra oración y luego encuentran la diferencia entre las sumas Al resumirlos en lugar de hacer una diferencia entre palabras, al menos no estará sujeto al orden de las palabras. Dicho esto, esto fallará de muchas maneras y no es una buena solución de ninguna manera (aunque las buenas soluciones a este problema casi siempre involucran cierta cantidad de PNL, aprendizaje automático y otras habilidades).

Entonces, la respuesta corta es, no, no hay una manera fácil de hacer esto (al menos no hacerlo bien).

Michael Aaron Safyan
fuente
44
Creo que tienes razón. El método más simple es acumular todos los vectores de palabras en una oración y encontrar la diferencia entre las sumas. Por cierto, ¿este método simple se verá afectado por el recuento de palabras? Como cuantas más palabras haya en una oración, se resumirá más histograma.
zhfkt
2
@zhfkt, muy probablemente sí. Por lo tanto, es posible que deba dividir por la cantidad de palabras o algo así para tratar de factorizar eso. De cualquier manera, cualquier heurística como esta será severamente defectuosa.
Michael Aaron Safyan
75

Como estás usando gensim, probablemente deberías usar su implementación doc2vec. doc2vec es una extensión de word2vec al nivel de frase, oración y documento. Es una extensión bastante simple, descrita aquí

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

Gensim es agradable porque es intuitivo, rápido y flexible. ¡Lo mejor es que puedes tomar las incrustaciones de palabras pre-entrenadas de la página oficial de word2vec y la capa syn0 del modelo Doc2Vec de gensim está expuesta para que puedas sembrar las incrustaciones de palabras con estos vectores de alta calidad!

GoogleNews-vectors-negative300.bin.gz (como se vincula en el Código de Google )

Creo que gensim es definitivamente la herramienta más fácil (y hasta ahora la mejor) para incrustar una oración en un espacio vectorial.

Existen otras técnicas de oración a vector que la propuesta en el artículo de Le & Mikolov anterior. Socher y Manning de Stanford son sin duda dos de los investigadores más famosos que trabajan en esta área. Su trabajo se ha basado en el principio de composición: la semántica de la oración proviene de:

1. semantics of the words

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

Han propuesto algunos de estos modelos (cada vez más complejos) sobre cómo usar la composicionalidad para construir representaciones a nivel de oración.

2011: despliegue del codificador automático recursivo (muy comparativamente simple. Comience aquí si está interesado)

2012 - red neuronal matriz-vector

2013 - red de tensor neural

2015 - Tree LSTM

Sus documentos están disponibles en socher.org. Algunos de estos modelos están disponibles, pero aún así recomendaría doc2vec de gensim. Por un lado, el URAE 2011 no es particularmente poderoso. Además, viene pre-entrenado con pesos adecuados para parafrasear datos de noticias. El código que proporciona no le permite volver a entrenar la red. Tampoco puede intercambiar diferentes vectores de palabras, por lo que está atrapado con las incrustaciones pre-word2vec de 2011 de Turian. Estos vectores ciertamente no están en el nivel de word2vec o GloVe.

Todavía no he trabajado con el Tree LSTM, ¡pero parece muy prometedor!

tl; dr Sí, usa el doc2vec de gensim. ¡Pero existen otros métodos!

Willie
fuente
¿Tiene más información sobre cómo inicializar el modelo doc2vec con valores word2vec previamente entrenados?
Simon H
42

Si está usando word2vec, necesita calcular el vector promedio para todas las palabras en cada oración / documento y usar la similitud de coseno entre vectores:

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 similitud:

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
fuente
44
¿Podría dar más explicaciones sobre index2word_set y model.index2word? Gracias.
theteddyboy
3
Observe que calcular el "vector promedio" es una elección tan arbitraria como no calcularlo en absoluto.
Gented
2
Estoy asombrado de por qué esta no es la respuesta principal, funciona bastante bien y no tiene un problema de secuencia que tiene el método de promedio.
Asim
Esta es la respuesta que estaba buscando. Resolvió mi problema. Gracias por la solución
iRunner
25

puede usar el algoritmo de distancia de Word Mover. Aquí hay una descripción 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: si enfrenta un error sobre la importación de la biblioteca pyemd , puede instalarlo usando el siguiente comando:

pip install pyemd
Ehsan
fuente
2
Utilicé ADM antes y funciona bien silenciosamente, sin embargo, se atragantaría con un corpus grande. Prueba SoftCosineSimilarity. También se encuentra en gensim ( twitter.com/gensim_py/status/963382840934195200 )
krinker
1
Sin embargo, WMD no es muy rápido cuando desea consultar un corpus.
Amartya
18

Una vez que calcula la suma de los dos conjuntos de vectores de palabras, debe tomar el coseno entre los vectores, no la diferencia. El coseno se puede calcular tomando el producto escalar de los dos vectores normalizados. Por lo tanto, el recuento de palabras no es un factor.

Rani Nelken
fuente
1
se puede proporcionar un poco de pseudocódigo sobre cómo hacer esto (no estoy usando gensim / Python)
dcsan
13

Hay una función de la documentación que toma una lista de palabras y compara sus similitudes.

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
fuente
12

Me gustaría actualizar la solución existente para ayudar a las personas que van a calcular la similitud semántica de las oraciones.

Paso 1:

Cargue el modelo adecuado usando gensim y calcule los vectores de palabras para las palabras en la oración y almacénelas como una lista de palabras

Paso 2: Calcular el vector de oración

El cálculo de la similitud semántica entre las oraciones era difícil antes, pero recientemente se propuso un artículo llamado " UNA BASE SENCILLA PERO TOUGH TO BEAT-TO-BEAT FOR SENTENCE " que sugiere un enfoque simple al calcular el promedio ponderado de los vectores de palabras en la oración y luego eliminar las proyecciones de los vectores promedio en su primer componente principal. Aquí el peso de una palabra w es a / (a ​​+ p (w)) con un ser un parámetro y p (w) la frecuencia de palabra (estimada) llamada frecuencia inversa suave Este método funciona significativamente mejor.

Aquí se ha dado un código simple para calcular el vector de oración usando SIF (frecuencia inversa suave) el método propuesto en el documento

Paso 3: usando sklearn cosine_similarity, carga dos vectores para las oraciones y calcula la similitud.

Este es el método más simple y eficiente para calcular la similitud de la oración.

Poorna Prudhvi
fuente
2
Muy buen papel. nota: el enlace a la implementación SIF requiere escribir el método get_word_frequency () que se puede lograr fácilmente usando el Contador de Python () y devolviendo un dict con claves: palabras únicas w, valores: # w / # total doc len
Quetzalcoatl
8

Estoy usando el siguiente método y funciona bien. Primero debe ejecutar un POSTagger y luego filtrar la oración para eliminar las palabras de detención (determinantes, conjunciones, ...). Recomiendo TextBlob APTagger . Luego construyes un word2vec tomando la media de cada vector de palabra en la oración. El método n_similarity en Gemsim word2vec hace exactamente eso al permitir pasar dos conjuntos de palabras para comparar.

lechatpito
fuente
¿Cuál es la diferencia entre tomar la media de los vectores y sumarlos para crear un vector de oración?
Καrτhικ
1
La diferencia es que el tamaño del vector es fijo para todas las oraciones
lechatpito
No hay diferencia mientras uses la similitud del coseno. @lechatpito Nada que ver con el tamaño del vector. Los vectores se suman, no se concatenan.
Wok
6

Hay extensiones de Word2Vec destinadas a resolver el problema de comparar fragmentos de texto más largos como frases u oraciones. Uno de ellos es el párrafo2vec o doc2vec.

"Representaciones distribuidas de oraciones y documentos" http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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

Max
fuente
2
Vale la pena mencionar brevemente cómo funciona el algoritmo presentado. Básicamente agrega un "token" único a cada enunciado y calcula los vectores word2vec. Al final, obtendrá los vectores de palabras para cada una de sus palabras en el corpus (siempre que solicite todas las palabras, también las únicas). Cada "ficha" única en el enunciado representará ese enunciado. Existe cierta controversia sobre los resultados presentados en el documento, pero esa es otra historia.
Vladislavs Dovgalecs
5

Gensim implementa un modelo llamado Doc2Vec para incrustar párrafos .

Hay diferentes tutoriales presentados como cuadernos IPython:

Otro método dependería de Word2Vec y Word Mover's Distance (WMD) , como se muestra en este tutorial:

Una solución alternativa sería confiar en vectores promedio:

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)

Finalmente, si puede ejecutar Tensorflow, puede intentar: https://tfhub.dev/google/universal-sentence-encoder/2

Wok
fuente
4

He probado los métodos proporcionados por las respuestas anteriores. Funciona, pero el principal inconveniente es que cuanto más largas sean las oraciones, mayor será la similitud (para calcular la similitud utilizo el puntaje del coseno de las dos incrustaciones medias de cualquiera de las dos oraciones) ya que cuanto más son las palabras, los efectos semánticos más positivos será agregado a la oración.

Pensé que debería cambiar de opinión y usar la incrustación de oraciones en su lugar como se estudió en este documento y este .

Lerner Zhang
fuente
3

El grupo de investigación de Facebook lanzó una nueva solución llamada InferSent Results y el código se publica en Github, consulte su repositorio. Es bastante impresionante Estoy planeando usarlo. https://github.com/facebookresearch/InferSent

su artículo https://arxiv.org/abs/1705.02364 Resumen: Muchos sistemas modernos de PNL se basan en incrustaciones de palabras, previamente entrenadas de manera no supervisada en grandes corpus, como características básicas. Sin embargo, los esfuerzos para obtener incrustaciones para fragmentos de texto más grandes, como las oraciones, no han tenido tanto éxito. Varios intentos de aprender representaciones no supervisadas de oraciones no han alcanzado un rendimiento lo suficientemente satisfactorio como para ser ampliamente adoptado. En este artículo, mostramos cómo las representaciones de oraciones universales entrenadas usando los datos supervisados ​​de los conjuntos de datos de Inferencia de Lenguaje Natural de Stanford pueden superar constantemente los métodos no supervisados ​​como los vectores SkipThought en una amplia gama de tareas de transferencia. Al igual que la visión por computadora utiliza ImageNet para obtener funciones, que luego se pueden transferir a otras tareas, nuestro trabajo tiende a indicar la idoneidad de la inferencia del lenguaje natural para transferir el aprendizaje a otras tareas de PNL. Nuestro codificador está disponible públicamente.

Ayman Salama
fuente
3

Si no usa Word2Vec, tenemos otro modelo para encontrarlo usando BERT para incrustar. A continuación se encuentran los enlaces de referencia 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))

Otro enlace a seguir https://github.com/hanxiao/bert-as-service

kamran kausar
fuente