Au cours de ce calepin, nous allons voir comment générer des description de produits à l'aide de Réseaux Récurents et notamment grace aux structure LSTM (Long-Short Term Memory).
L'intérêt de cette application est limité. Les descriptions de textes de ce document sont trop pauvres syntaxiquement pour pouvoir juger réellement de la qualité du texte généré. L'intérêt réel de ce calepin est de voir comment les données doivent être mis en forme pour être utilisé dans un réseau recurrent dans un but de génération de texte.
In [ ]:
#Importation des librairies utilisées
import pandas as pd
import numpy as np
import pickle
import functools
from tqdm import tqdm
import keras.models as km
import keras.layers as kl
La Catégorie de Niveau 3 COQUE - BUMPER - FACADE TELEPHONE
est la catégorie le plus représenté du jeu de données originale Cdiscount avec 2.184.671 déscriptions présentent. Parmis ces descriptions, 1.761.637 sont composés d'exactement 197 caractères.
Nous allons nous servir de ces lignes (ou un sous ensemble de ces lignes, en fonction de la puissance de calcul disponible sur votre machine) pour apprendre un modèle de génération de texte qui permettra de généré automatiquement une nouvelle description de ce type de produit.
In [ ]:
N = 100000
DATA_DIR = ""
X = np.load(DATA_DIR+"data/description_coque.npy")[:N]
print(X.shape)
print(X[:3])
Exercice Vérifiez que toutes les séquences sont bien de tailles 197.
In [ ]:
Nd=197
La génération de texte implique de constuire un réseau Many-To-One
:
Ou la prédiction $y_t$ servira d'entrée au réseau au temps $t+1$, i.e : $y_t=x_{t+1}$.
Chaque $x_t$ représente ici un caractère de la déscription encodé en One-Hot encoding. Ainsi une description $x$ composé de $N_d$ caractères sera modélisé par une matrice de taille $(N_v\times N_d)$ $x=[x_1,x_2,...,x_{N_d}]$ ou $x_i \in \mathbb{R}^{N_v}$
In [ ]:
chars = list(functools.reduce(lambda x,y : x.union(y), [set(x) for x in X], set()))
print("Vocabulaire : " + str(chars))
Nous ajoutons à ce vocabulaire deux indicateur permettant de localiser le début et la fin de chaque description
In [ ]:
chars.extend(["start","end"])
In [ ]:
Nv = len(chars)
print("Taille du vocabulaire : %d" %Nv)
In [ ]:
int_to_char = {i:c for i,c in enumerate(chars)}
char_to_int = {c:i for i,c in int_to_char.items()}
I_START = char_to_int["start"]
I_END = char_to_int["end"]
La fonction suivante, permet d'encoder une matrice $X\in \mathbb{R}^{N \times N_d}$ constitués de N descriptions en une matrice $X_{vec} \in \mathbb{R}^{N \times N_d \times N_v}$ contenant les description encodées.
In [ ]:
def encode_input_output_sequence(x, length_sequence, size_vocab, char_to_int_dic, i_start, i_end):
n = x.shape[0]
x_vec = np.zeros((n,length_sequence, size_vocab))
y_vec = np.zeros((n,length_sequence, size_vocab))
x_vec[:,0,i_start] = 1
y_vec[:,-1,i_end] = 1
for ix,x in tqdm(enumerate(x)):
for ic,c in enumerate(x):
c_int = char_to_int_dic[c]
x_vec[ix,ic+1,c_int]=1
y_vec[:,:-1,:] = x_vec[:,1:,:]
return x_vec, y_vec
In [ ]:
X_vec, Y_vec = encode_input_output_sequence(X[:N], Nd+1, Nv, char_to_int,I_START,I_END)
In [ ]:
X_vec.shape
Exercice Retrouvez la phrase originale de la phrase test affiché ci-dessous à partir de la phrase encodé. Vérifiez que x et y sont bien les mêmes descriptions seulement décalées d'un index
In [ ]:
# %load solution/3_1.py
In [ ]:
nb_hidden = 32
epochs = 20
batch_size= 128
model = km.Sequential()
model.add(kl.LSTM(nb_hidden, input_shape=(None, Nv), return_sequences=True))
model.add(kl.TimeDistributed(kl.Dense(Nv)))
model.add(kl.Activation('softmax'))
model.summary()
In [ ]:
model.compile(loss="categorical_crossentropy", optimizer="rmsprop")
model.fit(X_vec, Y_vec, epochs=epochs, batch_size=batch_size)
model.save("data/generate_model.h5")
Q Pourquoi est-ce la categorical_crossentropy
qui est utilisée comme fonction de perte?
La celulle suivante permet de générer une description produit :
In [ ]:
x_pred = np.zeros((1, Nd+1, Nv))
print("step 0")
x_pred[0,0,I_START] =1
x_pred_str = decode_sequence(x_pred[0], int_to_char)
print(x_pred_str)
for i in range(Nd):
ix = np.argmax(model.predict(x_pred[:,:i+1,:])[0][-1,:])
x_pred[0,i+1,ix] = 1
x_pred_str=decode_sequence(x_pred[0], int_to_char)
print(x_pred_str)
Q Comment cette génération est-elle produite?
Exercice Effectuez une génération en choissisant la ou les premières lettres qui seront générées.
In [ ]:
#%load solution/3_2.py
Exercice Effectuez une génération en ajoutant de l'aléa. Vous pouvez par exemple faire en sorte que chaque lettre soit séléctionnée selon une loi multinomiale.
In [ ]:
#%load solution/3_3.py
In [ ]: