Wie berechnet man die Satzähnlichkeit mit dem word2vec-Modell von Gensim mit Python?


125

Laut Gensim Word2Vec kann ich das word2vec-Modell im Gensim-Paket verwenden, um die Ähnlichkeit zwischen zwei Wörtern zu berechnen.

z.B

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

Das word2vec-Modell kann jedoch die Satzähnlichkeit nicht vorhersagen. Ich finde das LSI-Modell mit Satzähnlichkeit in Gensim heraus, aber das scheint nicht mit dem word2vec-Modell kombiniert zu werden. Die Länge des Korpus jedes Satzes, den ich habe, ist nicht sehr lang (kürzer als 10 Wörter). Gibt es also einfache Möglichkeiten, um das Ziel zu erreichen?


4
Es gibt ein ACL-Tutorial, das dieses Problem (unter anderem) behandelt: youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Emiel

7
Sie können jetzt gensims doc2vec verwenden und Satzähnlichkeit aus demselben Modul erhalten
kampta

@kampta. Hallo, würden Sie einen Beitrag vorschlagen, der die Implementierung zeigt?
Ian_De_Oliveira

Antworten:


86

Dies ist eigentlich ein ziemlich herausforderndes Problem, das Sie fragen. Das Berechnen der Satzähnlichkeit erfordert das Erstellen eines grammatikalischen Modells des Satzes, das Verstehen äquivalenter Strukturen (z. B. "er ging gestern zum Laden" und "gestern ging er zum Laden") und das Finden von Ähnlichkeit nicht nur in den Pronomen und Verben, sondern auch in der Eigennamen, Auffinden statistischer Koexistenz / Beziehungen in vielen realen Textbeispielen usw.

Das Einfachste, was Sie versuchen könnten - obwohl ich nicht weiß, wie gut dies funktionieren würde und es Ihnen sicherlich nicht die optimalen Ergebnisse bringen würde - wäre, zuerst alle "Stopp" -Wörter (Wörter wie "das", "ein" zu entfernen "usw., die dem Satz nicht viel Bedeutung verleihen) und dann word2vec für die Wörter in beiden Sätzen ausführen, die Vektoren in dem einen Satz summieren, die Vektoren im anderen Satz aufsummieren und dann den Unterschied zwischen finden die Summen. Wenn Sie sie zusammenfassen, anstatt einen wortweisen Unterschied zu machen, unterliegen Sie zumindest nicht der Wortreihenfolge. Davon abgesehen wird dies in vielerlei Hinsicht scheitern und ist keineswegs eine gute Lösung (obwohl gute Lösungen für dieses Problem fast immer ein gewisses Maß an NLP, maschinellem Lernen und anderer Klugheit erfordern).

Die kurze Antwort lautet also: Nein, es gibt keine einfache Möglichkeit, dies zu tun (zumindest nicht, um es gut zu machen).


4
Ich glaube, Du hast recht. Die einfachste Methode besteht darin, alle Vektoren von Wörtern in einem Satz zu akkumulieren und den Unterschied zwischen den Summen zu ermitteln. Wird diese einfache Methode übrigens durch die Wortanzahl beeinflusst? Denn je mehr Wörter in einem Satz enthalten sind, desto mehr Histogramme werden zusammengefasst.
zhfkt

2
@zhfkt, höchstwahrscheinlich ja. Möglicherweise müssen Sie also durch die Anzahl der Wörter oder ähnliches dividieren, um dies herauszufinden. In jedem Fall wird eine solche Heuristik stark fehlerhaft sein.
Michael Aaron Safyan


75

Da Sie gensim verwenden, sollten Sie wahrscheinlich die doc2vec-Implementierung verwenden. doc2vec ist eine Erweiterung von word2vec auf Phrasen-, Satz- und Dokumentebene. Es ist eine ziemlich einfache Erweiterung, die hier beschrieben wird

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

Gensim ist nett, weil es intuitiv, schnell und flexibel ist. Was großartig ist, ist, dass Sie die vorab trainierten Worteinbettungen von der offiziellen word2vec-Seite abrufen können und die syn0-Ebene des Doc2Vec-Modells von gensim freigelegt wird, sodass Sie die Worteinbettungen mit diesen hochwertigen Vektoren erstellen können!

GoogleNews-vectors-negative300.bin.gz (wie in Google Code verlinkt )

Ich denke, Gensim ist definitiv das einfachste (und für mich bisher beste) Werkzeug, um einen Satz in einen Vektorraum einzubetten.

Es gibt andere Satz-zu-Vektor-Techniken als die oben in Le & Mikolovs Artikel vorgeschlagene. Socher und Manning aus Stanford sind sicherlich zwei der bekanntesten Forscher auf diesem Gebiet. Ihre Arbeit basiert auf dem Prinzip der Komposition - die Semantik des Satzes stammt aus:

1. semantics of the words

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

Sie haben einige solcher Modelle vorgeschlagen (die immer komplexer werden), um mithilfe der Komposition Kompositionen auf Satzebene zu erstellen.

2011 - Entfaltung eines rekursiven Autoencoders (sehr vergleichsweise einfach. Bei Interesse hier beginnen)

2012 - Matrix-Vektor-Neuronales Netzwerk

2013 - neuronales Tensornetzwerk

2015 - Baum LSTM

Seine Papiere sind alle auf socher.org erhältlich. Einige dieser Modelle sind verfügbar, aber ich würde trotzdem gensims doc2vec empfehlen. Zum einen ist die URAE 2011 nicht besonders leistungsstark. Darüber hinaus ist es mit Gewichten ausgestattet, die zum Paraphrasieren von Nachrichtendaten geeignet sind. Mit dem von ihm bereitgestellten Code können Sie das Netzwerk nicht neu trainieren. Sie können auch nicht in verschiedene Wortvektoren tauschen, sodass Sie bei den Einbettungen von Turian vor Word2vec aus dem Jahr 2011 nicht weiterkommen. Diese Vektoren befinden sich sicherlich nicht auf der Ebene von word2vec oder GloVe.

Ich habe noch nicht mit dem Tree LSTM gearbeitet, aber es scheint sehr vielversprechend!

tl; dr Ja, benutze gensims doc2vec. Es gibt aber auch andere Methoden!


Haben Sie weitere Informationen zum Initialisieren des doc2vec-Modells mit vorab trainierten word2vec-Werten?
Simon H

42

Wenn Sie word2vec verwenden, müssen Sie den Durchschnittsvektor für alle Wörter in jedem Satz / Dokument berechnen und die Kosinusähnlichkeit zwischen Vektoren verwenden:

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

Ähnlichkeit berechnen:

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
Könnten Sie bitte weitere Erklärungen zu index2word_set und model.index2word geben? Danke dir.
Theteddyboy

3
Beachten Sie, dass die Berechnung des "Durchschnittsvektors" ebenso eine willkürliche Wahl ist wie die Nichtberechnung.
Gented

2
Ich bin erstaunt, warum dies nicht die beste Antwort ist, es funktioniert ziemlich gut und hat kein Sequenzproblem, das die Mittelungsmethode hat.
Asim

Dies ist die Antwort, nach der ich gesucht habe. Mein Problem wurde gelöst. Vielen Dank für die Lösung
iRunner

25

Sie können den Distanzalgorithmus von Word Mover verwenden. Hier ist eine einfache Beschreibung über Massenvernichtungswaffen .

#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: Wenn beim Importieren der Pyemd- Bibliothek ein Fehler auftritt , können Sie ihn mit dem folgenden Befehl installieren:

pip install pyemd

2
Ich habe WMD schon einmal benutzt und es funktioniert ziemlich gut, aber es würde an einem großen Korpus ersticken. Probieren Sie SoftCosineSimilarity aus. Auch in gensim gefunden ( twitter.com/gensim_py/status/963382840934195200 )
krinker

1
WMD ist jedoch nicht sehr schnell, wenn Sie einen Korpus abfragen möchten.
Amartya

18

Sobald Sie die Summe der beiden Sätze von Wortvektoren berechnet haben, sollten Sie den Kosinus zwischen den Vektoren und nicht den Diff nehmen. Der Kosinus kann berechnet werden, indem das Punktprodukt der beiden normalisierten Vektoren genommen wird. Somit ist die Wortzahl kein Faktor.


1
Kannst du ein bisschen Pseudocode dazu geben (ich benutze kein Gensim / Python)
dcsan

13

Es gibt eine Funktion aus der Dokumentation, die eine Liste von Wörtern erstellt und deren Ähnlichkeiten vergleicht.

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

Ich möchte die vorhandene Lösung aktualisieren, um den Personen zu helfen, die die semantische Ähnlichkeit von Sätzen berechnen werden.

Schritt 1:

Laden Sie das geeignete Modell mit gensim, berechnen Sie die Wortvektoren für die Wörter im Satz und speichern Sie sie als Wortliste

Schritt 2: Berechnen des Satzvektors

Die Berechnung der semantischen Ähnlichkeit zwischen Sätzen war zuvor schwierig, aber kürzlich wurde ein Artikel mit dem Titel " EINFACHE, ABER SCHWERE BASELINE FÜR SENTENZEMBEDDINGS " vorgeschlagen, der einen einfachen Ansatz vorschlägt, indem der gewichtete Durchschnitt der Wortvektoren im Satz berechnet und dann entfernt wird die Projektionen der Durchschnittsvektoren auf ihre erste Hauptkomponente. Hier ist das Gewicht eines Wortes w a / (a ​​+ p (w)), wobei a ein Parameter ist und p (w) die (geschätzte) Wortfrequenz, die als glatte inverse Frequenz bezeichnet wird Diese Methode arbeitet deutlich besser.

Ein einfacher Code , um den Satz Vektor unter Verwendung von SIF (glatt inverser Frequenz) , um das vorgeschlagene Verfahren in dem Papier zu berechnen gegeben wurde hier

Schritt 3: Verwenden von sklearn cosine_similarity Laden Sie zwei Vektoren für die Sätze und berechnen Sie die Ähnlichkeit.

Dies ist die einfachste und effizienteste Methode zur Berechnung der Satzähnlichkeit.


2
sehr schönes Papier. Hinweis: Für den Link zur SIF-Implementierung muss die Methode get_word_frequency () geschrieben werden, die mit Pythons Counter () problemlos ausgeführt werden kann, und ein Diktat mit Schlüsseln zurückgegeben werden: eindeutige Wörter w, Werte: # w / # total doc len
Quetzalcoatl

8

Ich benutze die folgende Methode und es funktioniert gut. Sie müssen zuerst einen POSTagger ausführen und dann Ihren Satz filtern, um die Stoppwörter (Determinanten, Konjunktionen, ...) zu entfernen. Ich empfehle TextBlob APTagger . Dann erstellen Sie ein word2vec, indem Sie den Mittelwert jedes Wortvektors im Satz nehmen. Die Methode n_similarity in Gemsim word2vec macht genau das, indem sie zwei Sätze von Wörtern zum Vergleichen übergibt .


Was ist der Unterschied zwischen dem Mittelwert der Vektoren und dem Hinzufügen dieser Vektoren, um einen Satzvektor zu erstellen?
Καrτhικ

1
Der Unterschied ist, dass die Vektorgröße für alle Sätze festgelegt ist
lechatpito

Es gibt keinen Unterschied, solange Sie die Kosinusähnlichkeit verwenden. @lechatpito Nichts mit Vektorgröße zu tun. Die Vektoren werden summiert und nicht verkettet.
Wok

6

Es gibt Erweiterungen von Word2Vec, mit denen das Problem des Vergleichs längerer Textteile wie Phrasen oder Sätze gelöst werden soll. Eine davon ist Absatz2vec oder Doc2vec.

"Verteilte Darstellungen von Sätzen und Dokumenten" http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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


2
Es ist kurz zu erwähnen, wie der vorgestellte Algorithmus funktioniert. Grundsätzlich fügen Sie jeder Äußerung ein eindeutiges "Token" hinzu und berechnen die word2vec-Vektoren. Am Ende erhalten Sie die Wortvektoren für jedes Ihrer Wörter im Korpus (vorausgesetzt, Sie fragen nach allen Wörtern, auch nach den eindeutigen). Jedes eindeutige "Zeichen" in der Äußerung repräsentiert diese Äußerung. Es gibt einige Kontroversen über die in der Zeitung präsentierten Ergebnisse, aber das ist eine andere Geschichte.
Vladislavs Dovgalecs

5

Gensim implementiert ein Modell namens Doc2Vec zum Einbetten von Absätzen .

Es gibt verschiedene Tutorials, die als IPython-Notizbücher vorgestellt werden:

Eine andere Methode basiert auf Word2Vec und Word Mover's Distance (WMD) , wie in diesem Tutorial gezeigt:

Eine alternative Lösung wäre, sich auf durchschnittliche Vektoren zu stützen:

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)

Wenn Sie Tensorflow ausführen können, können Sie Folgendes versuchen: https://tfhub.dev/google/universal-sentence-encoder/2


4

Ich habe die Methoden der vorherigen Antworten ausprobiert. Es funktioniert, aber der Hauptnachteil davon ist, dass je länger die Sätze sind, desto größer die Ähnlichkeit ist (um die Ähnlichkeit zu berechnen, verwende ich den Cosinus-Score der beiden mittleren Einbettungen von zwei beliebigen Sätzen), da je mehr Wörter, desto positiver die semantischen Effekte wird dem Satz hinzugefügt.

Ich dachte, ich sollte meine Meinung ändern und stattdessen die Satzeinbettung verwenden, wie in diesem und diesem Artikel untersucht .


3

Die Facebook-Forschungsgruppe hat eine neue Lösung namens InferSent veröffentlicht. Ergebnisse und Code werden auf Github veröffentlicht. Überprüfen Sie deren Repo. Es ist ziemlich großartig. Ich plane es zu benutzen. https://github.com/facebookresearch/InferSent

ihr Papier https://arxiv.org/abs/1705.02364 Zusammenfassung: Viele moderne NLP-Systeme basieren auf Worteinbettungen, die zuvor unbeaufsichtigt auf großen Korpora trainiert wurden, als Basismerkmale. Die Bemühungen, Einbettungen für größere Textblöcke wie Sätze zu erhalten, waren jedoch nicht so erfolgreich. Mehrere Versuche, unbeaufsichtigte Darstellungen von Sätzen zu lernen, sind nicht zufriedenstellend genug, um weit verbreitet zu sein. In diesem Artikel zeigen wir, wie universelle Satzdarstellungen, die unter Verwendung der überwachten Daten der Stanford Natural Language Inference-Datensätze trainiert wurden, unbeaufsichtigte Methoden wie SkipThought-Vektoren bei einer Vielzahl von Übertragungsaufgaben durchweg übertreffen können. Ähnlich wie Computer Vision ImageNet verwendet, um Funktionen zu erhalten, die dann auf andere Aufgaben übertragen werden können. Unsere Arbeit zeigt tendenziell die Eignung der Inferenz natürlicher Sprache für den Transfer des Lernens auf andere NLP-Aufgaben. Unser Encoder ist öffentlich verfügbar.


3

Wenn Sie Word2Vec nicht verwenden, haben wir ein anderes Modell, um es mit BERT zum Einbetten zu finden. Unten finden Sie den Referenzlink 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))

Anderer Link folgt https://github.com/hanxiao/bert-as-service

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.