Comment calculer la similitude des phrases en utilisant le modèle word2vec de gensim avec python


125

Selon le Gensim Word2Vec , je peux utiliser le modèle word2vec dans le package gensim pour calculer la similitude entre 2 mots.

par exemple

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

Cependant, le modèle word2vec ne parvient pas à prédire la similitude des phrases. Je découvre le modèle LSI avec la similitude des phrases dans gensim, mais, ce qui ne semble pas pouvoir être combiné avec le modèle word2vec. La longueur du corpus de chaque phrase que j'ai n'est pas très longue (moins de 10 mots). Alors, existe-t-il des moyens simples d'atteindre l'objectif?


4
Il y a un tutoriel ACL traitant de ce problème (entre autres): youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Emiel

7
Vous pouvez maintenant utiliser doc2vec de gensim et obtenir la similitude des phrases à partir du même module
kampta

@kampta. Salut, suggérez-vous un article qui montre la mise en œuvre?
Ian_De_Oliveira

Réponses:


86

C'est en fait un problème assez difficile que vous vous posez. Le calcul de la similitude des phrases nécessite la construction d'un modèle grammatical de la phrase, la compréhension des structures équivalentes (par exemple «il est allé au magasin hier» et «hier, il est allé au magasin»), la recherche de similitudes non seulement dans les pronoms et les verbes, mais aussi dans le noms propres, trouver des cooccurrences / relations statistiques dans de nombreux exemples textuels réels, etc.

La chose la plus simple que vous puissiez essayer - même si je ne sais pas dans quelle mesure cela fonctionnerait et que cela ne vous donnerait certainement pas les résultats optimaux - serait de commencer par supprimer tous les mots "d'arrêt" (des mots comme "le", "un ", etc. qui n'ajoutent pas beaucoup de sens à la phrase), puis exécutez word2vec sur les mots des deux phrases, additionnez les vecteurs dans une phrase, additionnez les vecteurs dans l'autre phrase, puis trouvez la différence entre les sommes. En les résumant au lieu de faire une différence mot-mot, vous ne serez au moins pas soumis à l'ordre des mots. Cela étant dit, cela échouera de nombreuses manières et n'est en aucun cas une bonne solution (bien que les bonnes solutions à ce problème impliquent presque toujours une certaine quantité de PNL, d'apprentissage automatique et d'autres intelligences).

Donc, la réponse courte est non, il n'y a pas de moyen facile de le faire (du moins de ne pas bien le faire).


4
Je pense que tu as raison. La méthode la plus simple est d'accumuler tous les vecteurs de mots dans une phrase et de trouver la différence entre les sommes. Au fait, cette méthode simple sera-t-elle effectuée par le nombre de mots? Parce que plus il y a de mots dans une phrase, plus l'histogramme sera résumé.
zhfkt

2
@zhfkt, très probablement, oui. Donc, vous devrez peut-être diviser par le nombre de mots ou d'autres pour essayer de le prendre en compte. Dans tous les cas, toute heuristique comme celle-ci sera gravement défectueuse.
Michael Aaron Safyan


75

Puisque vous utilisez gensim, vous devriez probablement utiliser son implémentation doc2vec. doc2vec est une extension de word2vec au niveau de la phrase, de la phrase et du document. C'est une extension assez simple, décrite ici

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

Gensim est agréable car il est intuitif, rapide et flexible. Ce qui est génial, c'est que vous pouvez récupérer les incorporations de mots pré-entraînées de la page officielle word2vec et la couche syn0 du modèle Doc2Vec de gensim est exposée afin que vous puissiez semer les incorporations de mots avec ces vecteurs de haute qualité!

GoogleNews-vectors-negative300.bin.gz (comme lié dans Google Code )

Je pense que gensim est certainement l'outil le plus simple (et jusqu'à présent pour moi, le meilleur) pour intégrer une phrase dans un espace vectoriel.

Il existe d'autres techniques phrase à vecteur que celle proposée dans l'article de Le & Mikolov ci-dessus. Socher et Manning de Stanford sont certainement deux des chercheurs les plus célèbres travaillant dans ce domaine. Leur travail a été basé sur le principe de la composition - la sémantique de la phrase vient de:

1. semantics of the words

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

Ils ont proposé quelques modèles de ce type (de plus en plus complexes) pour savoir comment utiliser la compositionnalité pour créer des représentations au niveau des phrases.

2011 - déploiement de l'autoencodeur récursif (très comparativement simple. Commencez ici si vous êtes intéressé)

2012 - réseau de neurones matrice-vecteur

2013 - réseau de tenseurs neuronaux

2015 - Arbre LSTM

ses papiers sont tous disponibles sur socher.org. Certains de ces modèles sont disponibles, mais je recommanderais toujours le doc2vec de gensim. D'une part, l'URAE 2011 n'est pas particulièrement puissante. De plus, il est pré-entraîné avec des poids adaptés pour paraphraser les données d'actualité. Le code qu'il fournit ne vous permet pas de recycler le réseau. Vous ne pouvez pas non plus échanger différents vecteurs de mots, vous êtes donc coincé avec les incorporations pré-word2vec de 2011 de Turian. Ces vecteurs ne sont certainement pas au niveau de word2vec ou GloVe.

Je n'ai pas encore travaillé avec le Tree LSTM, mais cela semble très prometteur!

tl; dr Ouais, utilisez doc2vec de gensim. Mais d'autres méthodes existent!


Avez-vous plus d'informations sur la façon d'initialiser le modèle doc2vec avec des valeurs word2vec pré-entraînées?
Simon H

42

Si vous utilisez word2vec, vous devez calculer le vecteur moyen pour tous les mots de chaque phrase / document et utiliser la similitude cosinus entre les vecteurs:

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

Calculez la similitude:

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
Pourriez-vous s'il vous plaît donner plus d'explications sur index2word_set et model.index2word? Je vous remercie.
theteddyboy

3
Notez que calculer le "vecteur moyen" est autant un choix arbitraire que de ne pas le calculer du tout.
gented

2
Je suis étonné de savoir pourquoi ce n'est pas la meilleure réponse, cela fonctionne très bien et n'a pas de problème de séquence avec la méthode de calcul de la moyenne.
Asim

C'est la réponse que je cherchais. J'ai résolu mon problème. Merci pour la solution
iRunner

25

vous pouvez utiliser l'algorithme de distance de Word Mover. voici une description simple de 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 vous rencontrez une erreur concernant l'importation de la bibliothèque pyemd , vous pouvez l'installer à l'aide de la commande suivante:

pip install pyemd

2
J'ai utilisé WMD avant et cela fonctionne bien, mais cela s'étoufferait sur un gros corpus. Essayez SoftCosineSimilarity. Aussi trouvé dans gensim ( twitter.com/gensim_py/status/963382840934195200 )
krinker

1
WMD n'est cependant pas très rapide lorsque vous souhaitez interroger un corpus.
Amartya

18

Une fois que vous avez calculé la somme des deux ensembles de vecteurs de mots, vous devez prendre le cosinus entre les vecteurs, pas le diff. Le cosinus peut être calculé en prenant le produit scalaire des deux vecteurs normalisés. Ainsi, le nombre de mots n'est pas un facteur.


1
pouvez-vous fournir un peu de pseudocode sur la façon de faire cela (je n'utilise pas gensim / python)
dcsan

13

Il y a une fonction de la documentation prenant une liste de mots et comparant leurs 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())

12

Je voudrais mettre à jour la solution existante pour aider les personnes qui vont calculer la similitude sémantique des phrases.

Étape 1:

Chargez le modèle approprié en utilisant gensim et calculez les vecteurs de mots pour les mots de la phrase et stockez-les sous forme de liste de mots

Étape 2: Calcul du vecteur de phrase

Le calcul de la similitude sémantique entre les phrases était difficile auparavant, mais récemment un article intitulé " UNE BASE SIMPLE MAIS TOUGH-TO-BEAT POUR LES INCORPORATIONS DE SENTENCE " a été proposé qui suggère une approche simple en calculant la moyenne pondérée des vecteurs de mots dans la phrase, puis en supprimant les projections des vecteurs moyens sur leur première composante principale Ici le poids d'un mot w est a / (a ​​+ p (w)) avec a étant un paramètre et p (w) la fréquence de mot (estimée) appelée fréquence inverse lisse . Cette méthode fonctionne nettement mieux.

Un code simple pour calculer le vecteur de phrase en utilisant SIF (smooth inverse frequency) la méthode proposée dans l'article a été donnée ici

Étape 3: en utilisant sklearn cosine_similarity, chargez deux vecteurs pour les phrases et calculez la similitude.

C'est la méthode la plus simple et la plus efficace pour calculer la similarité des phrases.


2
très beau papier. note: le lien vers l'implémentation SIF nécessite l'écriture de la méthode get_word_frequency () qui peut être facilement réalisée en utilisant Counter () de Python et en renvoyant un dict avec les clés: mots uniques w, valeurs: # w / # total doc len
Quetzalcoatl

8

J'utilise la méthode suivante et cela fonctionne bien. Vous devez d'abord lancer un POSTagger puis filtrer votre phrase pour vous débarrasser des mots vides (déterminants, conjonctions, ...). Je recommande TextBlob APTagger . Ensuite, vous construisez un mot2vec en prenant la moyenne de chaque vecteur de mot dans la phrase. La méthode n_similarity de Gemsim word2vec fait exactement cela en permettant de passer deux ensembles de mots à comparer.


Quelle est la différence entre prendre la moyenne des vecteurs et les ajouter pour créer un vecteur de phrase?
Καrτhικ

1
La différence est que la taille du vecteur est fixe pour toutes les phrases
lechatpito

Il n'y a pas de différence tant que vous utilisez la similitude cosinus. @lechatpito Rien à voir avec la taille du vecteur. Les vecteurs sont additionnés et non concaténés.
Wok le

6

Il existe des extensions de Word2Vec destinées à résoudre le problème de la comparaison de morceaux de texte plus longs comme des phrases ou des phrases. L'un d'eux est paragraph2vec ou doc2vec.

"Représentations distribuées de phrases et de documents" http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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


2
Il convient de mentionner brièvement comment fonctionne l'algorithme présenté. Vous ajoutez essentiellement un "jeton" unique à chaque énoncé et calculez les vecteurs word2vec. À la fin, vous obtiendrez les vecteurs de mots pour chacun de vos mots dans le corpus (à condition que vous demandiez tous les mots, même les uniques). Chaque «jeton» unique dans l'énoncé représentera cet énoncé. Il y a une certaine controverse sur les résultats présentés dans l'article, mais c'est une autre histoire.
Vladislavs Dovgalecs

5

Gensim implémente un modèle appelé Doc2Vec pour l' incorporation de paragraphes .

Il existe différents tutoriels présentés sous forme de notebooks IPython:

Une autre méthode s'appuierait sur Word2Vec et la distance de Word Mover (WMD) , comme indiqué dans ce didacticiel:

Une solution alternative serait de s'appuyer sur des vecteurs moyens:

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)

Enfin, si vous pouvez exécuter Tensorflow, vous pouvez essayer: https://tfhub.dev/google/universal-sentence-encoder/2


4

J'ai essayé les méthodes fournies par les réponses précédentes. Cela fonctionne, mais le principal inconvénient est que plus les phrases sont longues, plus la similitude sera grande (pour calculer la similitude, j'utilise le score cosinus des deux plongements moyens de deux phrases quelconques) car plus il y a de mots, plus les effets sémantiques positifs sont importants. sera ajouté à la phrase.

J'ai pensé que je devrais changer d'avis et utiliser la phrase incorporant à la place comme étudié dans cet article et ceci .


3

Le groupe de recherche Facebook a publié une nouvelle solution appelée InferSent Les résultats et le code sont publiés sur Github, vérifiez leur repo. C'est vraiment génial. Je prévois de l'utiliser. https://github.com/facebookresearch/InferSent

leur article https://arxiv.org/abs/1705.02364 Résumé: De nombreux systèmes PNL modernes s'appuient sur des imbrications de mots, préalablement entraînées de manière non supervisée sur de grands corpus, comme fonctionnalités de base. Les efforts pour obtenir des incorporations pour de plus gros morceaux de texte, tels que des phrases, n'ont cependant pas été aussi fructueux. Plusieurs tentatives d'apprentissage de représentations non supervisées de phrases n'ont pas atteint des performances suffisamment satisfaisantes pour être largement adoptées. Dans cet article, nous montrons comment les représentations de phrases universelles entraînées à l'aide des données supervisées des ensembles de données Stanford Natural Language Inference peuvent constamment surpasser les méthodes non supervisées telles que les vecteurs SkipThought sur un large éventail de tâches de transfert. Tout comme la vision par ordinateur utilise ImageNet pour obtenir des fonctionnalités, qui peuvent ensuite être transférées vers d'autres tâches, notre travail tend à indiquer la pertinence de l'inférence du langage naturel pour transférer l'apprentissage à d'autres tâches de la PNL. Notre encodeur est accessible au public.


3

Si vous n'utilisez pas Word2Vec, nous avons un autre modèle pour le trouver en utilisant BERT pour l'intégration. Vous trouverez ci-dessous le lien de référence 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))

Autre lien à suivre https://github.com/hanxiao/bert-as-service

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.