Jak obliczyć podobieństwo zdań za pomocą modelu word2vec gensima z pythonem

125

Zgodnie z Gensim Word2Vec , mogę użyć modelu word2vec w pakiecie gensim do obliczenia podobieństwa między 2 słowami.

na przykład

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

Jednak model word2vec nie przewiduje podobieństwa zdań. Odnajduję model LSI z podobieństwem zdań w gensimie, ale wydaje się, że nie da się tego połączyć z modelem word2vec. Długość korpusu każdego zdania, które mam, nie jest zbyt długa (mniej niż 10 słów). Czy są więc jakieś proste sposoby na osiągnięcie celu?

zhfkt
źródło
4
Istnieje samouczek ACL omawiający ten problem (między innymi): youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be
Emiel
7
Możesz teraz skorzystać z doc2vec gensima i uzyskać podobieństwo zdań z tego samego modułu
kampta
@kampta. Cześć, czy mógłbyś zasugerować jakiś post pokazujący realizację?
Ian_De_Oliveira

Odpowiedzi:

86

W rzeczywistości jest to dość trudny problem, o który pytasz. Obliczenie podobieństwa zdań wymaga zbudowania gramatycznego modelu zdania, zrozumienia równoważnych struktur (np. „Wczoraj szedł do sklepu” i „wczoraj szedł do sklepu”), znalezienia podobieństwa nie tylko w zaimkach i czasownikach, ale także w rzeczowniki własne, znajdowanie statystycznych współwystępowań / zależności w wielu rzeczywistych przykładach tekstowych itp.

Najprostszą rzeczą, jaką możesz spróbować - chociaż nie wiem, jak dobrze by to działało iz pewnością nie dałoby optymalnych rezultatów - byłoby najpierw usunięcie wszystkich słów „stop” (takich jak „the”, „an "itp., które nie dodają zbyt wiele znaczenia do zdania), a następnie uruchom word2vec na słowach w obu zdaniach, zsumuj wektory w jednym zdaniu, zsumuj wektory w drugim zdaniu, a następnie znajdź różnicę między sumy. Podsumowując je, zamiast robić różnicę w słowach, przynajmniej nie będziesz podlegać kolejności słów. To powiedziawszy, zawiedzie to na wiele sposobów i nie jest w żaden sposób dobrym rozwiązaniem (chociaż dobre rozwiązania tego problemu prawie zawsze obejmują pewną ilość NLP, uczenia maszynowego i innego sprytu).

Krótka odpowiedź brzmi: nie, nie ma łatwego sposobu, aby to zrobić (przynajmniej nie zrobić tego dobrze).

Michael Aaron Safyan
źródło
4
Myślę, że masz rację. Najprostszą metodą jest zebranie wszystkich wektorów słów w jednym zdaniu i znalezienie różnicy między sumami. A propos, czy na tę prostą metodę wpłynie liczba słów? Ponieważ im więcej słów w jednym zdaniu, tym więcej histogramu zostanie podsumowany.
zhfkt
2
@zhfkt, najprawdopodobniej tak. Dlatego może być konieczne podzielenie przez liczbę słów lub kilka innych, aby spróbować to uwzględnić. Tak czy inaczej, każda taka heurystyka będzie miała poważne wady.
Michael Aaron Safyan
75

Ponieważ używasz gensim, prawdopodobnie powinieneś użyć jego implementacji doc2vec. doc2vec jest rozszerzeniem word2vec na poziomie frazy, zdania i dokumentu. To całkiem proste rozszerzenie, opisane tutaj

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

Gensim jest fajny, ponieważ jest intuicyjny, szybki i elastyczny. Wspaniałe jest to, że możesz pobrać wstępnie wytrenowane osadzenia słów z oficjalnej strony word2vec, a warstwa syn0 modelu Doc2Vec gensim jest odsłonięta, dzięki czemu możesz wysiewać osadzanie słów za pomocą tych wysokiej jakości wektorów!

GoogleNews-vectors-negative300.bin.gz (zgodnie z linkiem w Google Code )

Myślę, że gensim jest zdecydowanie najłatwiejszym (i jak dotąd dla mnie najlepszym) narzędziem do osadzania zdania w przestrzeni wektorowej.

Istnieją inne techniki zamiany zdania na wektor niż ta zaproponowana w powyższej pracy Le & Mikolov. Socher i Manning ze Stanford to z pewnością dwaj najbardziej znani badacze pracujący w tej dziedzinie. Ich praca została oparta na zasadzie kompozycyjności - semantyka zdania pochodzi z:

1. semantics of the words

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

Zaproponowali kilka takich modeli (coraz bardziej złożonych), jak używać kompozycyjności do budowania reprezentacji na poziomie zdań.

2011 - rozwijanie rekurencyjnego autoenkodera (bardzo stosunkowo proste. Jeśli jesteś zainteresowany, zacznij tutaj)

2012 - sieć neuronowa macierzowo-wektorowa

2013 - neuronowa sieć tensorowa

2015 - Drzewo LSTM

wszystkie jego artykuły są dostępne na socher.org. Niektóre z tych modeli są dostępne, ale nadal polecam doc2vec firmy Gensim. Po pierwsze, URAE 2011 nie jest szczególnie potężny. Ponadto jest wstępnie wytrenowany z wagami dostosowanymi do parafrazowania danych news-y. Dostarczony przez niego kod nie pozwala na przekwalifikowanie sieci. Nie możesz również zamieniać się różnymi wektorami słów, więc utkniesz z osadzeniami pre-word2vec 2011 z Turian. Te wektory z pewnością nie są na poziomie word2vec czy GloVe.

Jeszcze nie współpracowałem z Tree LSTM, ale wydaje się bardzo obiecujący!

tl; dr Tak, użyj doc2vec gensima. Ale istnieją inne metody!

Willie
źródło
Czy masz więcej informacji o tym, jak zainicjować model doc2vec z wstępnie wytrenowanymi wartościami word2vec?
Simon H
42

Jeśli używasz word2vec, musisz obliczyć średni wektor dla wszystkich słów w każdym zdaniu / dokumencie i użyć podobieństwa cosinusowego między wektorami:

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

Oblicz podobieństwo:

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
źródło
4
Czy mógłbyś podać więcej wyjaśnień na temat index2word_set i model.index2word? Dziękuję Ci.
theteddyboy
3
Zauważ, że obliczenie „średniego wektora” jest tak samo arbitralnym wyborem, jak w ogóle nie obliczanie go.
gented
2
Jestem zdumiony, dlaczego nie jest to najlepsza odpowiedź, działa całkiem dobrze i nie ma problemu z sekwencją, który ma metoda uśredniania.
Asim,
To jest odpowiedź, której szukałem. Rozwiązany mój problem. Dzięki za rozwiązanie
iRunner
25

możesz użyć algorytmu odległości programu Word Mover. tutaj jest prosty opis broni masowego rażenia .

#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: jeśli napotkasz błąd dotyczący importu biblioteki pyemd , możesz ją zainstalować za pomocą następującego polecenia:

pip install pyemd
Ehsan
źródło
2
Używałem wcześniej broni masowego rażenia i działa dobrze, ale dławiłby się dużym korpusem. Wypróbuj SoftCosineSimilarity. Znaleziono również w gensim ( twitter.com/gensim_py/status/963382840934195200 )
krinker
1
Jednak broń masowego rażenia nie jest zbyt szybka, gdy chcesz zapytać o korpus.
Amartya
18

Po obliczeniu sumy dwóch zbiorów wektorów słów należy wziąć cosinus między wektorami, a nie różnicę. Cosinus można obliczyć, znormalizując iloczyn skalarny dwóch wektorów. Dlatego liczba słów nie jest czynnikiem.

Rani Nelken
źródło
1
czy możesz podać trochę pseudokodu, jak to zrobić (nie używam gensim / python)
dcsan
13

Jest to funkcja z dokumentacją biorąc listę słów i porównywanie ich podobieństwa.

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
źródło
12

Chciałbym zaktualizować istniejące rozwiązanie, aby pomóc osobom, które będą obliczać podobieństwo semantyczne zdań.

Krok 1:

Załaduj odpowiedni model za pomocą gensim i oblicz wektory słów dla słów w zdaniu i zapisz je jako listę słów

Krok 2: Obliczanie wektora zdania

Obliczenie podobieństwa semantycznego między zdaniami było wcześniej trudne, ale ostatnio zaproponowano artykuł zatytułowanyProsta, ale trudna do pobicia linia bazowa dla osadzenia zdań ”, która sugeruje proste podejście polegające na obliczeniu średniej ważonej wektorów słów w zdaniu, a następnie usunięciu rzuty wektorów średnich na ich pierwszą składową główną, gdzie waga słowa w to a / (a ​​+ p (w)), gdzie a jest parametrem, a p (w) (szacowana) częstotliwość słowa zwana gładką częstotliwością odwrotną . ta metoda działa znacznie lepiej.

Podano tutaj prosty kod do obliczania wektora zdania za pomocą metody SIF (gładka częstotliwość odwrotna)

Krok 3: używając sklearn cosine_similarity załaduj dwa wektory dla zdań i oblicz podobieństwo.

Jest to najprostsza i najskuteczniejsza metoda obliczenia podobieństwa zdań.

Poorna Prudhvi
źródło
2
bardzo ładny papier. uwaga: link do implementacji SIF wymaga napisania metody get_word_frequency (), którą można łatwo wykonać używając Counter () Pythona i zwracając dykt z kluczami: unikalne słowa w, wartości: # w / # total doc len
Quetzalcoatl
8

Używam następującej metody i działa dobrze. Najpierw musisz uruchomić POSTagger, a następnie przefiltrować zdanie, aby pozbyć się słów pomijanych (determinanty, spójniki, ...). Polecam TextBlob APTagger . Następnie tworzysz word2vec, biorąc średnią każdego wektora słowa w zdaniu. Metoda n_similarity w Gemsim word2vec robi dokładnie to, zezwalając na przekazanie dwóch zestawów słów do porównania.

lechatpito
źródło
Jaka jest różnica między obliczaniem średniej wektorów a dodawaniem ich w celu stworzenia wektora zdań?
Καrτhικ
1
Różnica polega na tym, że rozmiar wektora jest stały dla wszystkich zdań
lechatpito
Nie ma różnicy, o ile używasz podobieństwa cosinusowego. @lechatpito Nie ma nic wspólnego z rozmiarem wektora. Wektory są sumowane, a nie łączone.
Wok
6

Istnieją rozszerzenia Word2Vec mające na celu rozwiązanie problemu porównywania dłuższych fragmentów tekstu, takich jak frazy lub zdania. Jednym z nich jest paragraph2vec lub doc2vec.

„Rozproszone reprezentacje zdań i dokumentów” http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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

Maks
źródło
2
Warto pokrótce wspomnieć, jak działa przedstawiony algorytm. Zasadniczo dodajesz unikalny „token” do każdej wypowiedzi i obliczasz wektory word2vec. Na końcu otrzymasz wektory słów dla każdego słowa w korpusie (pod warunkiem, że poprosisz o wszystkie słowa, także te unikalne). Każdy unikalny „symbol” w wypowiedzi będzie reprezentował tę wypowiedź. Wyniki przedstawione w artykule budzą pewne kontrowersje, ale to już inna historia.
Vladislavs Dovgalecs
5

Gensim implementuje model o nazwie Doc2Vec do osadzania akapitów .

Istnieją różne samouczki prezentowane jako notebooki IPython:

Inna metoda polegałaby na Word2Vec i Word Mover's Distance (WMD) , jak pokazano w tym samouczku:

Alternatywnym rozwiązaniem byłoby oparcie się na średnich wektorach:

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)

Wreszcie, jeśli możesz uruchomić Tensorflow, możesz spróbować: https://tfhub.dev/google/universal-sentence-encoder/2

Wok
źródło
4

Wypróbowałem metody podane w poprzednich odpowiedziach. To działa, ale jego główną wadą jest to, że im dłuższe zdania, tym większe będzie podobieństwo (do obliczenia podobieństwa używam wyniku cosinusowego dwóch średnich osadzeń dowolnych dwóch zdań), ponieważ im więcej słów, tym bardziej pozytywne efekty semantyczne zostanie dodany do zdania.

Pomyślałem, że powinienem zmienić zdanie i zamiast tego użyć osadzania zdań, jak studiowano w tym i tym artykule .

Lerner Zhang
źródło
3

Grupa badawcza Facebooka udostępniła nowe rozwiązanie o nazwie InferSent Wyniki, a kod jest publikowany na Github, sprawdź ich repozytorium. To jest niesamowite. Planuję to wykorzystać. https://github.com/facebookresearch/InferSent

ich artykuł https://arxiv.org/abs/1705.02364 Streszczenie: Wiele współczesnych systemów NLP opiera się na osadzaniu słów, wcześniej szkolonych bez nadzoru w dużych korpusach, jako cechach podstawowych. Wysiłki mające na celu osadzenie większych fragmentów tekstu, takich jak zdania, nie przyniosły jednak takiego sukcesu. Kilka prób uczenia się reprezentacji zdań bez nadzoru nie przyniosło wystarczających wyników, aby mogły zostać powszechnie przyjęte. W tym artykule pokazujemy, jak uniwersalne reprezentacje zdań trenowane przy użyciu nadzorowanych danych ze zbiorów danych Stanford Natural Language Inference mogą konsekwentnie przewyższać nienadzorowane metody, takie jak wektory SkipThought w szerokim zakresie zadań transferowych. Podobnie jak wizja komputerowa wykorzystuje ImageNet do uzyskiwania funkcji, które można następnie przenieść do innych zadań, nasza praca wskazuje na przydatność wnioskowania z języka naturalnego do przenoszenia nauki do innych zadań NLP. Nasz koder jest publicznie dostępny.

Ayman Salama
źródło
3

Jeśli nie korzystamy z Word2Vec, mamy inny model do znalezienia go przy użyciu BERT do osadzania. Poniżej znajduje się link referencyjny 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))

Inny link do śledzenia https://github.com/hanxiao/bert-as-service

kamran kausar
źródło