Prédire le prix d’un article d’e-commerce — Le Challenge Kaggle Mercari 1/3
Natural language processing avec python et Tensorflow pour l’estimation des prix des articles d’une plateforme d’e-commerce
Problématique
Prévision des prix des articles avec le texte de leur description
Le challenge Kaggle Mercari consiste à prédire au mieux le prix des articles d’une plateforme e-commerce en utilisant la description des articles et d’autres informations (catégorie, livraison offerte et état de l’article). Le dataset comporte 1 482 535 lignes, donc un échantillon largement assez grand pour utiliser des méthodes de deep-learning ! C’est donc une belle occasion de comparer différentes architectures de réseaux de neurones profonds, comparer leurs performances et utiliser des modèles de ce type pour les projets menés par Linkvalue. Il serait relativement simple d’utiliser les commentaires clients pour prédire des volumes de ventes sur une autre plateforme d’e-commerce en utilisant un modèle très proche par exemple.
Nous allons profiter de ce challenge pour montrer quelques techniques de traitement du langage avec Tensorflow. Pourquoi Tensorflow ? Parce qu’il permet d’implémenter efficacement et précisément des modèles complexes et qu’il semble s’imposer parmi les autres frameworks de deep learning.
Si vous souhaitez entraîner le réseau de neurones profond que nous allons présenter, une carte graphique récente est nécessaire. Sinon, les calculs risquent de durer quelques semaines (ou mois) !
Ce premier article sera centré sur le nettoyage des données, la présentation du modèle d’embedding et la récupération de l’un de ces modèles pré-entrainés.
Le second article rentrera dans le détail de l’implémentation d’un réseau de neurones convolutionnel et dans la conception d’un objet qui gérera l’entraînement du modèle.
Dans un troisième article, nous montrerons un exemple d’implémentation d’un réseau de neurones récurrent et les pistes d’améliorations qui pourraient être apportées.
Importation et traitement des données
Pandas est merveilleux
Pour importer et manipuler les données, nous utilisons (comme d’habitude) Pandas. Nous mélangeons les lignes du dataset avec la méthode sample, au cas où par exemple les lignes soient triées par date de création. Cela pourrait poser des problèmes d’optimisation par la suite, puisque le modèle apprend bloc par bloc : si les blocs au début du dataset sont très différents des blocs situés à la fin (par exemple), cela ne permettra pas à l’algorithme de converger de manière optimale. Ensuite, nous affichons les principales caractéristiques du dataset et nous fusionnons les colonnes name et description avant de les supprimer. Le challenge évalue l’erreur de ‘[log(prix prévu) — log(1+prix)]²’, donc nous regardons la distribution de ‘log(1+prix)’.
Code et sorties
import pandas as pd
import numpy as np
import matplotlib.pyplot as pp
import seaborn as sb
import tensorflow as tf
import re
from sklearn.preprocessing import StandardScaler
pp.style.use("fivethirtyeight")train_df = pd.read_csv("/Users/jeanbaptiste/Downloads/train.tsv",
sep = "\t")
train_df = train_df.sample(frac = 1.0)
print(train_df.columns)
print(train_df.shape[0])
train_df.loc[pd.isnull(train_df["item_description"]), "item_description"] = ""
train_df["total_text"] = train_df["item_description"] + " " + train_df["name"]
train_df.drop(["item_description", "name"], axis = 1, inplace = True)
pp.show(sb.kdeplot(np.log(1 + train_df["price"])))
Nettoyage du texte
Pour séparer les éléments de ponctuation des mots, nous utilisons une suite d’expressions régulières.
def clean_str(string):
"""
Tokenization/string cleaning for all datasets except for SST.
Original taken from https://github.com/yoonkim/CNN_sentence/blob/master/process_data.py
"""
string = re.sub(r"[^A-Za-z0-9(),!?\'\`]", " ", string)
string = re.sub(r"\'s", " \'s", string)
string = re.sub(r"\'ve", " \'ve", string)
string = re.sub(r"n\'t", " n\'t", string)
string = re.sub(r"\'re", " \'re", string)
string = re.sub(r"\'d", " \'d", string)
string = re.sub(r"\'ll", " \'ll", string)
string = re.sub(r",", " , ", string)
string = re.sub(r"!", " ! ", string)
string = re.sub(r"\.", " \. ", string)
string = re.sub(r"\(", " \( ", string)
string = re.sub(r"\)", " \) ", string)
string = re.sub(r"\?", " \? ", string)
string = re.sub(r"\s{2,}", " ", string)
return string.strip().lower()train_df["total_text"] = train_df["total_text"].apply(clean_str)
print(train_df["total_text"].head())
Le word embedding
Mais qu’est-ce que c’est ?
Le word embedding est une représentation vectorielle de mots. À chaque mot est associé un vecteur qui lui est propre. Ce vecteur défini le mot par son sens et son contexte dans l’espace vectoriel de grande dimension. Par exemple, le vecteur du mot « frog » a une distance faible avec les vecteurs associés aux noms de différentes espèces de grenouilles.
Pour créer un word embedding, la méthode classique est de récupérer un immense corpus de texte et d’estimer la probabilité que chaque mot apparaisse en présence d’un autre. Le calcul peut être très long, et pour éviter d’avoir à l’effectuer, nous allons utiliser un modèle déjà entraîné et généreusement partagé par l’université de Stanford. Il est disponible ici.
Pour plus de détails à propos du word embedding, nous vous recommandons vivement les cours en ligne de l’université de Stanford sur Youtube, et disponibles ci-dessous. Il y aura un peu de maths ;-)
Les fichiers mis à disposition par l’université sont au format .txt et nous devons les retraiter pour les placer dans un objet réutilisable par la suite. Nous le faisons avec le code suivant.
Le code pour retraiter les fichiers
class GloveModel :
def __init__(self, path) :
i = 1
self.index_word = []
self.embeddings = []
self.word_index = {}
with open(path, "r") as file :
row = file.readline().split(" ")
while row != [""] :
self.index_word.append(row[0])
self.embeddings.append(np.array(row[1:], dtype = np.float32))
self.word_index[row[0]] = i
i += 1
row = file.readline().split(" ")
self.index_words = [""] + self.index_word
self.embeddings = [np.zeros_like(self.embeddings[0])] + self.embeddingsstanford_glove = GloveModel("/Users/jeanbaptiste/Downloads/glove/glove.6B.300d.txt")
À chaque mot est attribué un entier unique qui correspond à l’index de sa représentation vectorielle. Nous avons attribué un vecteur nul à la valeur 0, nous verrons pourquoi par la suite.
Transformer le texte en séquences de nombres entiers
Nous allons maintenant transformer le texte en séquences de nombres entiers qui réfèrent à la représentation vectorielle des mots, pour ensuite envoyer ces séquences dans le word embedding. Pour que toutes nos séquences de mots aient la même longueur, nous utilisons pad_sequences de Keras. Il est indispensable que toutes les séquences aient la même longueur puisque le filtre de convolution demande des matrices de tailles fixes.
def text_to_sequence(string, dict_words) :
splitted = string.split(" ")
result = []
for word in splitted :
try :
result.append(dict_words[word])
except KeyError :
pass
return resultsequences = list(train_df["total_text"].apply(text_to_sequence,
args = (stanford_glove.word_index,)))
sequences = tf.keras.preprocessing.sequence.pad_sequences(sequences,
maxlen = 150)
sequences = np.array(sequences).astype(np.int32)
print(sequences[0])
Nous avons maintenant une matrice de 1 458 535 lignes et 150 colonnes. Si le texte associé à un produit est d’une longueur inférieure à 150 mots, la séquence est remplie avec des 0 sur la gauche, comme sur cet exemple. C’est pour cela que nous avions associé au chiffre 0 un vecteur nul. Si la longueur de la séquence est supérieure à 150 mots, elle est tronquée. C’est le framework tensorflow qui s’occupera de remplacer chaque entier par le vecteur correspondant à cet index.
Nous avons choisi la longueur de 150 mots pour deux raisons : par rapport à la médiane et à la moyenne de la longueur des phrases et parce que nous avons considéré qu’une phrase faisant en moyenne une vingtaine de mots, environs sept phrases seraient représentatives de la description.
La dernière ligne droite
Centrer et réduire la cible
Le deep learning pose de nombreux problèmes d’optimisation. L’une des étapes très fortement recommandées pour les éviter est de remettre les variables à la bonne échelle et de retraiter les valeurs extrêmes comme expliqué ici : https://visualstudiomagazine.com/articles/2014/01/01/how-to-standardize-data-for-neural-networks.aspx
Nous utilisons le StandardScaler de scikit-learn pour la mise à l’échelle et la méthode clip pour recadrer les valeurs trop importantes. L’outil demande une matrice en entrée, nous redimensionnons donc nos vecteurs en matrice d’une colonne.
Nous utilisons le premier million de lignes comme échantillon d’entraînement et les restantes comme échantillon de test.
target = np.log(1 + train_df["price"].values)
y_train = target[:1000000]
y_test = target[1000000:]
scaler = StandardScaler()
scaler.fit(y_train.reshape((-1, 1)))
y_train_scaled = scaler.transform(y_train.reshape((-1, 1))).flatten().clip(-2.5, 2.5)
y_test_scaled = scaler.transform(y_test.reshape((-1, 1))).flatten()
X_train = sequences[:1000000, :]
X_test = sequences[1000000:, :]
C’est enfin terminé ! Notre texte est bien retraité et transformé en séquences, notre variable cible est retraitée, et les vecteurs associés à chaque élément de vocabulaire sont dans un objet facilement utilisable. Le meilleur est maintenant à venir : l’implantation d’un réseau de neurones convolutionnel dans un prochain article et l’implémentation d’un réseau de neurones récurrent dans le dernier de la série !
Et pour découvrir le cheminement de notre collaborateur Daoud dans la prédiction de courses sportives, c’est par ici :
De plus, nous publions régulièrement des articles sur des sujets de développement produit web et mobile, data et analytics, sécurité, cloud, hyperautomatisation et digital workplace.
Suivez-nous pour être notifié des prochains articles et réaliser votre veille professionnelle.
Retrouvez aussi nos publications et notre actualité via notre newsletter, ainsi que nos différents réseaux sociaux : LinkedIn, Twitter, Youtube, Twitch et Instagram
Vous souhaitez en savoir plus ? Consultez notre site web et nos offres d’emploi.