Accéder à la page principale du cours

Séance 2 - Opérations sur les représentations vectorielles des mots

Adrien Guille, Université Lyon 2

Aucun package additionnel n’est nécessaire pour cette séance.

L’objectif est de récupérer des vecteurs pré-entraînés et de réaliser diverses opérations, basées sur la mesure de la similarité entre eux.

Chargement des représentations vectorielles

On charges les représentations apprises avec l’extension morphologique de Skip-Gram avec échantillonnage négatif (source : https://fasttext.cc/docs/en/crawl-vectors.html ; fichier utilisé ici : http://mediamining.univ-lyon2.fr/people/guille/word_embedding/fr_50k.vec.zip). On se limite aux 50 000 mots les plus fréquents dans la version francophone de Wikipedia :

embeddings <- read.csv('../Data/fr_50k.vec', skip = 1, sep=' ', quote='', stringsAsFactors=FALSE, header=FALSE)
dim(embeddings)
## [1] 49999   302

La première colonne liste les mots, les 300 suivantes décrivent les représentations vectorielles des mots, la dernière colonne est un artefact, on ne la considère donc pas :

words <- embeddings[ ,1]
vectors <- as.matrix(embeddings[ ,2:301])
length(words)
## [1] 49999
dim(vectors)
## [1] 49999   300

Implémentation de la similarité cosinus

La similarité cosinus entre deux vecteurs est données par la formule suivante :

\[ \text{cos}(v_1, v_2) = \frac{v_1 \cdot v_2}{||v_1|| \times ||v_2||} \]

cosine_similarity <- function(v1, v2){
  dot_product <- v1 %*% v2
  norm_prod <- sqrt(sum(v1**2)) * sqrt(sum(v2**2))
  return(as.numeric(dot_product / norm_prod))
}

On mesure la similarité entre des paires de mots de moins en moins sémantiquement proches :

cosine_similarity(vectors[match('voiture', words), ], vectors[match('camion', words), ])
## [1] 0.663517
cosine_similarity(vectors[match('voiture', words), ], vectors[match('bicyclette', words), ])
## [1] 0.5571083
cosine_similarity(vectors[match('voiture', words), ], vectors[match('bateau', words), ])
## [1] 0.4056555
cosine_similarity(vectors[match('voiture', words), ], vectors[match('table', words), ])
## [1] 0.1720994

Implémentation de la recherche des vecteurs les plus similaires

On mesure la similarité entre le vecteur \(v\) et tous les vecteurs connus, puis on trie le vocabulaire en fonction, et on retourne les \(n\) premiers mots :

find_closest_words <- function(v, n=5){
  similarity <- numeric(nrow(vectors))
  for(i in 1:nrow(vectors)){
    similarity[i] <- cosine_similarity(v, vectors[i, ])
  }
  ordered_words <- words[order(-similarity)]
  return(ordered_words[1:n])
}

On affiche les mots les plus proches de “voiture” :

find_closest_words(vectors[match('voiture', words), ])
## [1] "voiture"         "voitures"        "camionnette"     "v\303\251hicule"
## [5] "camion"

Résolution d’analogies

Le mot \(a\) est au mot \(b\) ce que le mot \(c\) est au mot \(d\) :

\[ a:b ~::~ c : d \]

On traduit l’analogie sous la forme d’une équation vectorielle : \[ v_a - v_b = v_c - v_d \] Si les mots \(a\), \(b\) et \(c\) sont connus, on peut tenter de deviner le mot \(d\) en résolvant le problème suivant :

\[ \text{mot } b = \underset{v}{\mathrm{argmax}} \big(\text{cos}(v, v_d) \big) \] Avec \(v_d = v_b - v_a + v_c\).

resolve_analogy <- function(word_a, word_b, word_c, n=1){
  word_d <- vectors[match(word_b, words), ] - vectors[match(word_a, words), ] + vectors[match(word_c, words), ]
  return(find_closest_words(word_d, n))
}

On résoud deux analogies. Par exemple, le modèle trouve que le mot qui complète l’analogie “Le père est à la mère ce que le frère est à la ?” est le mot “sœur” :

resolve_analogy('père','mère', 'frère')
## [1] "sœur"
resolve_analogy('frère','sœur', 'père')
## [1] "mère"

On voit que dans les deux cas, le vecteur résultant de la différence, soit entre “père” et “mère”, soit entre “frère” et “sœur”, semble encoder la différence de genre (masculin vers féminin). Ainsi, en ajoutant ce vecteur, soit à frère, soit à père, on obtient l’équivalent féminin.