[PYTHON] Calcul de la similitude entre les phrases à l'aide de Word2Vec (version simplifiée)

Il existe un moyen d'utiliser Doc2Vec, etc. pour calculer la similitude entre les phrases, mais c'est un peu gênant car je dois créer un modèle pour cela à partir de zéro. Il peut être plus polyvalent et plus facile d'utiliser le modèle Word2Vec tel quel si vous ne souhaitez obtenir qu'un certain degré de précision.

J'ai donc calculé la similitude entre les phrases en fonction de la moyenne des vecteurs de caractéristiques des mots contenus dans la phrase et de la similitude cosinus entre les phrases.

environnement

# OS
macOS Sierra

# Python(Utilisez Anaconda)
Python : Python 3.5.3 :: Anaconda custom (x86_64)
pip : 9.0.1 from /Users/username/anaconda/lib/python3.5/site-packages (python 3.5)

Cela ne fonctionnait pas bien avec python3.6, donc la version python3.5 d'Anaconda ([Anaconda 4.2.0 pour python3](https://repo.continuum.io/archive/Anaconda3-4.2.0-MacOSX-x86_64] .pkg)) est utilisé.

Obtenir et charger un modèle formé

Il a fallu trop de temps à mon MacBook Air pour générer un dictionnaire à partir du corpus

Le modèle entraîné de fastText a été publié

Nous avons utilisé un modèle entraîné plus publié. Cette fois, nous utiliserons un modèle (model_neologd.vec) dans lequel le texte de Wikipedia est divisé en utilisant NEologd de MeCab et entraîné par fastText. (Nombre de dimensions: 300)

Chargement du modèle entraîné

import gensim
word2vec_model = gensim.models.KeyedVectors.load_word2vec_format('model/model_neologd.vec', binary=False)

(Étant donné que le fichier est proche de 1 Go, sa lecture prend des dizaines de secondes)

En utilisant ce modèle, vous pouvez effectuer un calcul sémantique de mots à l'aide de vecteurs de caractéristiques.

Calcul de mots par Word2Vec (Exemple: Femme + King-Man)

import pprint
pprint.pprint(word2vec_model.most_similar(positive=['femme', 'Roi'], negative=['Homme']))

# => [('Reine', 0.7062159180641174),
# ('Royal', 0.6530475616455078),
# ('Royal', 0.6122198104858398),
# ('Prince', 0.6098779439926147),
# ('famille royale', 0.6084121465682983),
# ('Princesse', 0.6005773544311523),
# ('Reine', 0.5964134335517883),
# ('Roi', 0.593998908996582),
# ('Monsieur', 0.5929002165794373),
# ('Palais Royal', 0.5772185325622559)]

Calcul de la similitude entre les mots par Word2Vec

#Si vous souhaitez calculer la similitude entre des mots simples, modélisez.Peut être calculé par similitude
pprint.pprint(word2vec_model.similarity('Roi', 'Reine'))
# => 0.74155587641044496
pprint.pprint(word2vec_model.similarity('Roi', 'ramen'))
# => 0.036460763469822188

D'une certaine manière, le résultat est comme ça.

Charger MeCab

Utilisez MeCab pour décomposer le langage naturel en notes séparées. Spécifiez mecab-ipadic-neologd, qui est également utilisé pour générer des modèles entraînés sous forme de dictionnaire, et spécifiez la sortie sous forme de division.

import MeCab
mecab = MeCab.Tagger("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -Owakati")

mecab.parse("Il s'est cassé l'estomac hier")
# => 'Il s'est cassé l'estomac hier\n'

Le texte séparé est séparé par des espaces. Le saut de ligne étant inclus à la fin, il semble nécessaire de le supprimer au moment de la mise en œuvre. (Au fait, MeCab a été installé en utilisant mecab-python3. Il semble que cela ne fonctionne pas correctement avec la série python3.6, j'ai donc dû utiliser la série python3.5 à 2017/5)

Calculer la moyenne des vecteurs de caractéristiques des mots utilisés dans la phrase

Dans cette méthode, la moyenne des vecteurs de caractéristiques des mots utilisés dans la phrase est utilisée comme vecteur de caractéristiques de la phrase elle-même, donc une fonction pour cela est définie.

import numpy as np
def avg_feature_vector(sentence, model, num_features):
    words = mecab.parse(sentence).replace(' \n', '').split() #Ligne de rupture à la fin du mecab(\n)Est une sortie, alors supprimez-la
    feature_vec = np.zeros((num_features,), dtype="float32") #Initialiser le conteneur de vecteurs de fonctionnalités
    for word in words:
        feature_vec = np.add(feature_vec, model[word])
    if len(words) > 0:
        feature_vec = np.divide(feature_vec, len(words))
    return feature_vec

Il s'agit simplement de faire la moyenne des vecteurs de caractéristiques pour chaque mot. (Étant donné que le nombre de dimensions du modèle entraîné est de 300, spécifiez 300 pour num_features)

avg_feature_vector("Il s'est cassé l'estomac hier", word2vec_model, 300)
# => array([  6.39975071e-03,  -6.38077855e-02,  -1.41418248e-01,
#       -2.01289997e-01,   1.76049918e-01,   1.99666247e-02,
#             :                 :                 :
#       -7.54096806e-02,  -5.46530560e-02,  -9.14395228e-02,
#       -2.21335635e-01,   3.34903784e-02,   1.81226760e-01], dtype=float32)

Une fois exécuté, je pense qu'une quantité de caractéristiques de 300 dimensions sera sortie.

Calculez la similitude entre deux phrases

Ensuite, la fonction ci-dessus est utilisée pour calculer la similitude cosinus du vecteur moyen entre deux phrases.

from scipy import spatial
def sentence_similarity(sentence_1, sentence_2):
    #Le modèle Word2Vec utilisé cette fois est généré avec un vecteur de caractéristiques de 300 dimensions, donc num_caractéristiques également spécifiées comme 300
    num_features=300
    sentence_1_avg_vector = avg_feature_vector(sentence_1, word2vec_model, num_features)
    sentence_2_avg_vector = avg_feature_vector(sentence_2, word2vec_model, num_features)
    #Calculer la similitude cosinus en soustrayant la distance entre les vecteurs de 1
    return 1 - spatial.distance.cosine(sentence_1_avg_vector, sentence_2_avg_vector)

En utilisant cette fonction, vous pouvez facilement calculer la similitude entre les phrases. (La plage est de 0 à 1, et plus elle est proche de 1, plus elle est similaire.)

result = sentence_similarity(
    "Il a mangé un ramen épicé hier et a eu faim",
    "Hier, j'ai mangé une cuisine chinoise épicée et j'ai eu faim"
)
print(result)
# =>  0.973996032475

result = sentence_similarity(
    "Ce n'est pas bon ... je dois faire quelque chose rapidement ...",
    "Nous fournirons des informations d'emploi soigneusement sélectionnées"
)
print(result)
# => 0.608137464334

J'ai pu calculer une valeur numérique comme ça!

Problème de méthode ①

** Dans le cas de phrases longues, le degré de similitude est élevé. ** ** Puisque seule la moyenne des mots est prise et comparée, il devient difficile de faire une différence dans la valeur moyenne entre les phrases dans une longue phrase, et la similitude devient élevée même dans les phrases sans rapport.

result = sentence_similarity(
    "C'est enfin dans l'histoire de cette histoire. J'ai peur que d'autres en viennent au point où ils ne devraient pas avancer, et je m'en contente dans une certaine mesure.",
    "Même si je suis malade, c'est une bonne journée. En pensant à Gauche comme à une souris, votre visage a pressé le soupir de Doremifa et le prochain violoncelle de renard, et la différence entre eux est assez différente."
)
print(result)
# => 0.878950984671

Même si cela peut être fait, la comparaison entre des phrases de 10 mots est la limite.

Problème de méthode ②

** Impossible de gérer les mots inconnus. ** ** Puisqu'il n'est pas possible de sortir un vecteur de caractéristiques pour un mot inconnu qui n'est pas enregistré dans le modèle entraîné, il semble nécessaire de prendre des mesures telles que le remplissage du mot lui-même avec le vecteur de caractéristiques moyen des autres mots. (Cependant, dans ce cas, les mots inconnus ont souvent des caractéristiques sémantiques, ce qui réduit la précision de la similitude.)

>>> result = sentence_similarity(
...     "L'adoption par référence est devenue populaire ces dernières années",
...     "L'ère du recrutement par lots de nouveaux diplômés est révolue"
... )
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "<stdin>", line 5, in sentence_similarity
  File "<stdin>", line 6, in avg_feature_vector
  File "/Users/username/anaconda/lib/python3.5/site-packages/gensim/models/keyedvectors.py", line 574, in __getitem__
    return self.word_vec(words)
  File "/Users/username/anaconda/lib/python3.5/site-packages/gensim/models/keyedvectors.py", line 273, in word_vec
    raise KeyError("word '%s' not in vocabulary" % word)
KeyError: "word 'Référence' not in vocabulary"

Dans ce cas, je ne trouve pas le mot référence et il me manque.

Résumé

La méthode elle-même étant simple, je pense que les cas qui peuvent être utilisés sont assez limités. Au contraire, s'il ne faut traiter que des phrases courtes, il semble que cette méthode puisse également apporter une certaine précision. Lors de la recherche sérieuse de la similitude entre les phrases, c'est une approche simple d'utiliser une méthode comme Doc2Vec, de préparer un corpus qui convient à l'objectif du modèle lui-même et de créer le vôtre. ..

Code introduit dans l'article (tous)

import gensim
import MeCab
import numpy as np
from scipy import spatial

word2vec_model = gensim.models.KeyedVectors.load_word2vec_format('model/model_neologd.vec', binary=False)
mecab = MeCab.Tagger("-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -Owakati")

#Calculer la moyenne des vecteurs de caractéristiques des mots utilisés dans la phrase
def avg_feature_vector(sentence, model, num_features):
    words = mecab.parse(sentence).replace(' \n', '').split() #Ligne de rupture à la fin du mecab(\n)Est une sortie, alors supprimez-la
    feature_vec = np.zeros((num_features,), dtype="float32") #Initialiser le conteneur de vecteurs de fonctionnalités
    for word in words:
        feature_vec = np.add(feature_vec, model[word])
    if len(words) > 0:
        feature_vec = np.divide(feature_vec, len(words))
    return feature_vec

#Calculez la similitude entre deux phrases
def sentence_similarity(sentence_1, sentence_2):
    #Le modèle Word2Vec utilisé cette fois est généré avec un vecteur de caractéristiques de 300 dimensions, donc num_caractéristiques également spécifiées comme 300
    num_features=300
    sentence_1_avg_vector = avg_feature_vector(sentence_1, word2vec_model, num_features)
    sentence_2_avg_vector = avg_feature_vector(sentence_2, word2vec_model, num_features)
    #Calculer la similitude cosinus en soustrayant la distance entre les vecteurs de 1
    return 1 - spatial.distance.cosine(sentence_1_avg_vector, sentence_2_avg_vector)

result = sentence_similarity(
    "Il a mangé un ramen épicé hier et a eu faim",
    "Hier, j'ai mangé une cuisine chinoise épicée et j'ai eu faim"
)
print(result)
# => 0.973996032475

référence

Recommended Posts

Calcul de la similitude entre les phrases à l'aide de Word2Vec (version simplifiée)
Calculez la similitude entre les phrases avec Doc2Vec, une évolution de Word2Vec
Calcul de similarité entre les épisodes précurseurs à l'aide de la chronologie en direct et du modèle de sujet
Calcul de similitude par MinHash
Calcul du vecteur normal par convolution
[Python] Calcul de la similarité d'image (coefficient de dés)
[Version japonaise] Jugement de la similitude des mots pour les mots polynomiaux utilisant ELMo et BERT
Calcul de la machine à vecteurs de support (SVM) (en utilisant cvxopt)