5
votes

Mappage du vecteur de mots sur le mot le plus similaire / le plus proche à l'aide de spaCy

J'utilise spaCy dans le cadre d'une solution de modélisation de sujet et j'ai une situation où j'ai besoin de mapper un vecteur de mot dérivé au mot "le plus proche" ou "le plus similaire" dans un vocabulaire de vecteurs de mots.

Je vois que gensim a une fonction (WordEmbeddingsKeyedVectors.similar_by_vector) pour calculer cela, mais je me demandais si spaCy avait quelque chose comme ça pour mapper un vecteur à un mot dans son vocabulaire (nlp.vocab)?


0 commentaires

3 Réponses :


7
votes

Après un peu d'expérimentation, j'ai trouvé une fonction scikit (cdist dans scikit.spatial.distance) qui trouve un vecteur "proche" dans un espace vectoriel du vecteur d'entrée.

# Imports
from scipy.spatial import distance
import spaCy

# Load the spacy vocabulary
nlp = spacy.load("en_core_web_lg")

# Format the input vector for use in the distance function
# In this case we will artificially create a word vector from a real word ("frog")
# but any derived word vector could be used
input_word = "frog"
p = np.array([nlp.vocab[input_word].vector])

# Format the vocabulary for use in the distance function
ids = [x for x in nlp.vocab.vectors.keys()]
vectors = [nlp.vocab.vectors[x] for x in ids]
vectors = np.array(vectors)

# *** Find the closest word below ***
closest_index = distance.cdist(p, vectors).argmin()
word_id = ids[closest_index]
output_word = nlp.vocab[word_id].text
# output_word is identical, or very close, to the input word


0 commentaires

3
votes

Un mot d'avertissement sur cette réponse. Traditionnellement, la similarité de mots (dans gensim, spacy et nltk) utilise la similitude cosinus tandis que par défaut, le cdist de scipy utilise la distance euclidienne. Vous pouvez obtenir la distance cosinus qui n'est pas la même chose que la similitude, mais elles sont liées. Pour dupliquer le calcul de gensim, changez votre appel cdist comme suit:

import spacy
nlp = spacy.load("en_core_web_md")
x = nlp("man")
y = nlp("king")
print(x.similarity(y))
print(x.similarity(x))

Cependant, vous devriez également noter que scipy mesure la distance cosinus qui est "en arrière" par rapport à la similarité cosinus où "cosinus dist" = 1 - cos x (x est l'angle entre les vecteurs), donc pour faire correspondre / dupliquer les nombres de gensim, vous devez soustraire votre réponse de un (et bien sûr, prenez l'argument MAX - les vecteurs similaires sont plus proches de 1). C'est une différence très subtile mais qui peut causer beaucoup de confusion.

Des vecteurs similaires devraient avoir une grande similitude (près de 1), tandis que la distance est petite (proche de zéro).

La similarité cosinus peut être négative (ce qui signifie que les vecteurs ont des directions opposées) mais leur DISTANCE sera positive (comme la distance devrait l'être).

source: https://docs.scipy.org/ doc / scipy / reference / generated / scipy.spatial.distance.cdist.html

https://tedboy.github.io/nlps/generated/generated/gensim.models.Word2Vec.n_similarity.html# gensim.models.Word2Vec.n_similarity

également pour faire de la similitude dans spacy est le suivant:

distance.cdist(p, vectors, metric='cosine').argmin()


1 commentaires

x.similarity a été assez rapide pour que je puisse parcourir tous les mots du vocabulaire pour un petit nombre de cas.



3
votes

Oui, spacy a une méthode API pour le faire, tout comme KeyedVectors.similar_by_vector:

import numpy as np
import spacy

nlp = spacy.load("en_core_web_lg")

your_word = "king"

ms = nlp.vocab.vectors.most_similar(
    np.asarray([nlp.vocab.vectors[nlp.vocab.strings[your_word]]]), n=10)
words = [nlp.vocab.strings[w] for w in ms[0][0]]
distances = ms[2]
print(words)
['King', 'KIng', 'king', 'KING', 'kings', 'KINGS', 'Kings', 'PRINCE', 'Prince', 'prince']

(les mots ne sont pas correctement normalisés dans sm_core_web_lg , mais vous pourrait jouer avec d'autres modèles et observer une sortie plus représentative).


0 commentaires