Introduction au NLP avec Python : les IA prennent la parole

Introduction au NLP avec Python : les IA prennent la parole

Chatbots, moteurs de recherches, assistants vocaux, les IA ont énormément de choses à nous dire. Néanmoins, la compréhension du langage, qui est une formalité pour les êtres humains, est un challenge quasiment insurmontable pour les machines. C’est d’ailleurs un domaine entier du machine learning, on le nomme NLP.

Introduction au NLP avec Python : les IA prennent la parole

Ces dernières années ont été très riches en progrès pour le Natural Language Processing (NLP) et les résultats observés sont de plus en plus impressionnants. C’est vrai que dans mon article Personne n’aime parler à une IA, j’ai été assez sévère dans ma présentation des IA conversationnelles. Malgré que les systèmes qui existent sont loin d’être parfaits (et risquent de ne jamais le devenir), ils permettent déjà de faire des choses très intéressantes.

Introduction au NLP avec Python : les IA prennent la parole

Du mot au vecteur


Pour comprendre le langage le système doit être en mesure de saisir les différences entre les mots. Pour cela, l’idéal est de pouvoir les représenter mathématiquement, on parle d’encodage. De la même manière qu’une image est représentée par une matrice de valeurs représentant les nuances de couleurs, un mot sera représenté par un vecteur de grande dimension, c’est ce que l’on appelle le word embedding.

Ces vecteurs sont construits pour chaque langue en traitant des bases de données de textes énormes (on parle de plusieurs centaines de Gb). En comptant les occurrences des mots dans les textes, l’algorithme peut établir des correspondance entre les mots.

Les modèles de ce type sont nombreux, les plus connus sont Word2vec, BERT ou encore ELMO. Leurs utilisations est rendue simple grâce à des modèles pré-entrainés que vous pouvez trouver facilement.

Rien ne nous empêche de dessiner les vecteurs (après les avoir projeter en dimension 2), je trouve ça assez joli !

Introduction au NLP avec Python : les IA prennent la parole
Représentation graphique de mots avec Word2vec

Cette représentation est très astucieuse puisqu’elle permet maintenant de définir une distance entre 2 mots. Vous pouvez même écrire des équations de mots comme : Roi – Homme = Reine – Femme


Application du NLP : classification de phrases sur Python

Pré-requis : Installation de Python

Maintenant que l’on a compris les concepts de bases du NLP, nous pouvons travailler sur un premier petit exemple. Prenons une liste de phrases incluant des fruits et légumes. Nous allons construire en quelques lignes un système qui va permettre de les classer suivant 2 catégories.

Nous verrons que le NLP peut être très efficace, mais il sera intéressant de voir que certaines subtilités de langages peuvent échapper au système !

Je vous conseille d’utiliser Google Collab, c’est l’environnement de codage que je préfère.

Voici nos données de départ :

phrases = ["J'ai acheté des tomates",
          "La saison des oignons arrive",
          'Cette mangue est très bonne',
          'Ca sent les haricots',
          'Je crois que je vais tomber dans les pommes',
          'Penses à acheter des carottes',
          "Un petit jus orange?",
          "Ces fraises sont belles",
          "Prend des aubergines aussi"]


Avant de commencer nous devons importer les bibliothèques qui vont nous servir :

# import des bibliothèques utiles

from gensim.models import Word2Vec
import nltk
from gensim.models import KeyedVectors

from nltk.cluster import KMeansClusterer
import numpy as np 

from sklearn import cluster
from sklearn import metrics

Si elles ne sont pas installées vous n’avez qu’à faire pip install gensim, pip install sklearn, …

              Nettoyage des données


La première étape à chaque fois que l’on fait du NLP est de construire une pipeline de nettoyage de nos données. L’exemple que je vous présente ici est assez basique mais vous pouvez être amenés à traiter des données beaucoup moins structurées que celles-ci.

Et d’ailleurs le plus gros travail du data scientist ne réside malheureusement pas dans la création de modèle. Le nettoyage du dataset représente une part énorme du processus.

Pour nettoyage des données textuelles on retire les chiffres ou les nombres, on enlève la ponctuation, les caractères spéciaux comme les @, /, -, :, … et on met tous les mots en minuscules.

Pour cela on utiliser ce que l’on appelle les expressions régulières ou regex. Sur Python leur utilisation est assez simple, vous devez importer la bibliothèque ‘re’. Puis construire vos regex. Attention à l’ordre dans lequel vous écrivez les instructions.

Il n’y a malheureusement aucune pipeline NLP qui fonctionne à tous les coups, elles doivent être construites au cas par cas. Dans le cas qui nous importe cette fonction fera l’affaire :

import re

def nlp_pipeline(text):

    text = text.lower() # mettre les mots en minuscule

# Retirons les caractères spéciaux :

    text = re.sub(r"[,\!\?\%\(\)\/\"]", "", text)
    text = re.sub(r"\&\S*\s", "", text)
    text = re.sub(r"\-", "", text)
    
    return text

 
Installation d’un modèle Word2vec pré-entrainé :


Pour gagner du temps et pouvoir créer un système efficace facilement il est préférable d’utiliser des modèles déjà entraînés.

Pour cet exemple j’ai choisi un modèle Word2vec que vous pouvez importer rapidement via la bibliothèque Gensim. Voici le code à écrire sur Google Collab. Rien ne vous empêche de télécharger la base et de travailler en local.

# Import d'une base word2vec en francais deja entrainee

w2v = KeyedVectors.load_word2vec_format(
http://embeddings.net/frWac_non_lem_no_postag_no_phrase_200_cbow_cut100.bin,
    binary=True)

Update : ce lien semble obsolète, voici une alternative :

https://s3.us-east 2.amazonaws.com/embeddings.net/embeddings/frWac_non_lem_no_postag_no_phrase_200_cbow_cut100.bin

             
Encodage : la transformation des mots en vecteurs est la base du NLP


C’est l’étape cruciale du processus. Nous devons transformer nos phrases en vecteurs.

Pour cela, word2vec nous permet de transformer des mots et vecteurs. Je vais ensuite faire simplement la moyenne de chaque phrase. Sachez que pour des phrases longues cette approche ne fonctionnera pas, la moyenne n’est pas assez robuste. Si vous avez des phrases plus longues ou des textes il vaut mieux choisir une approche qui utilise TF-IDF.

# On commence par utiliser notre pipeline définie plus haut

phrases_propres = []

for phrase in phrases:
    phrases_propres.append(nlp_pipeline(phrase))

# Nous devons séparer les phrases en liste de mots

phrases_split = []

for phrase in phrases_propres:
    phrases_split.append(phrase.split(" "))

# C'est là que Word2vec intervient
X = []

for phrase in phrases_split:
    vec_phrase = []
    for mot in phrase:
        vec_phrase.append(w2v.wv[mot])
    X.append(np.mean(vec_phrase,0))

             
Classification par la méthode des k-means :


Maintenant que nous avons nos vecteurs, nous pouvons commencer la classification.

En classification il n’y a pas de consensus concernant la méthode a utiliser. Vous pouvez lire l’article 3 méthodes de clustering à connaitre.

Ici nous aller utiliser la méthode des k moyennes, ou k-means. Elle est d’autant plus intéressante dans notre situation puisque l’on sait déjà que nos données sont réparties suivant deux catégories.

Le code pour le k-means avec Scikit learn est assez simple :

kclusterer = KMeansClusterer(2, distance=nltk.cluster.util.cosine_distance, repeats=25)
clusters = kclusterer.cluster(X, assign_clusters=True)
print (clusters)

for index, phrase in enumerate(phrases):    
    print (str(clusters[index]) + ":" + str(phrase))

kmeans = cluster.KMeans(n_clusters=2)
kmeans.fit(X)
  
labels = kmeans.labels_
centroids = kmeans.cluster_centers_

Voici les résultats que l’on obtient :

1 : J'ai acheté des tomates
1 : La saison des oignons arrive
0 : Cette mangue est très bonne
1 : Ca sent les haricots
1 : Je crois que je vais tomber dans les pommes
1 : Penses à acheter des carottes
0 : Un petit jus orange?
0 : Ces fraises sont belles
1 : Prend des aubergines aussi

A part pour les pommes chaque phrase est rangée dans la bonne catégorie. Pour les pommes on a peut-être un problème dans la taille de la phrase. Comme je l’ai expliqué plus la taille de la phrase sera grande moins la moyenne sera pertinente.

Il peut être intéressant de projeter les vecteurs en dimension 2 et visualiser à quoi nos catégories ressemblent sur un nuage de points.

import matplotlib.pyplot as plt
from sklearn.manifold import TSNE

model = TSNE(n_components=2, random_state=0)
np.set_printoptions(suppress=True)

Y = model.fit_transform(X)
plt.scatter(Y[:, 0], Y[:, 1], c=clusters, s=290,alpha=.5)

plt.show()

Voilà ce que l’on obtient :

Nuages de points
Nuage de points représentant les phrases de notre corpus

Je suis fan de beaux graphiques sur Python, c’est pour cela que j’aimerais aussi construire une matrice de similarité. Elle nous permettra de voir rapidement quelles sont les phrases les plus similaires.

size = 10

# Construction de la matrice des distances

S = np.zeros((size,size))

for i in range(len(X)):
    for j in range(len(X)):
        S[i][j] = np.dot(X[i],X[j])/(np.linalg.norm(X[i])*np.linalg.norm(X[j]))

fig, ax = plt.subplots()
img = ax.imshow(S)

# Pour afficher les phrases

x_label_list = phrases
y_label_list = phrases

# Imposons qu'ils soient tous afficher, sinon par défaut vous n'en verrez #que la moitié

ax.set_xticks(np.arange(10))
ax.set_yticks(np.arange(10))

# Afficher les labels et le titre principal

ax.set_xticklabels(x_label_list,rotation='vertical',verticalalignment='top')
ax.set_yticklabels(y_label_list)
ax.set_title("Matrice de similarité")

# Ecrire la valeur de la similarité dans chaque case de la matrice

for i in range(len(x_label_list)):
    for j in range(len(y_label_list)):
        text = ax.text(j, i, round(S[j,i],3),fontsize=5,
                       ha="center", va="center", color="w")

# Imposer un angle de rotation pour faciliter la lecture des #labels en abscisses

plt.setp(ax.get_xticklabels(), rotation=45, ha="right",
         rotation_mode="anchor")

plt.show()


Voilà le résultat !

Introduction au NLP avec Python : les IA prennent la parole


A l’échelle d’un mot ou de phrases courtes la compréhension pour une machine est aujourd’hui assez facile (même si certaines subtilités de langages restent difficiles à saisir). Néanmoins, pour des phrases plus longues ou pour un paragraphe, les choses sont beaucoup moins évidentes. Et on utilise souvent des modèles de réseaux de neurones comme les LSTM.

L’algorithme doit être capable de prendre en compte les liens entre les différents mots. Il se trouve que le passage de la sémantique des mots obtenue grâce aux modèles comme Word2vec, à une compréhension syntaxique est difficile à surmonter pour un algorithme simple. Les chatbots qui nous entourent sont très souvent rien d’autre qu’une succession d’instructions empilées de façon astucieuse.

Néanmoins, le fait que le NLP soit l’un des domaines de recherches les plus actifs en machine learning, laisse penser que les modèles ne cesseront de s’améliorer. Peut-être que nous aurons un jour un chatbot capable de comprendre réellement le langage.

Ah et tant que j’y pense, n’oubliez pas de manger vos 5 fruits et légumes par jour !