<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Ateliers
Step1:  Téléchargement des données
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.
Step2:  Exercice Vérifiez que toutes les séquences sont bien de tailles 197.
Step3:  Mise en forme  des données
La génération de texte implique de constuire un réseau Many-To-One 
Step4:  Nous ajoutons à ce vocabulaire deux indicateur permettant de localiser le début et la fin de chaque description
Step5:  Création des dictionnaires
Les dictionnaires char_to_int et int_to_char permettent respectivement d'encoder une description texte et de décoder un encodage `One-Hot``
Step6:  Encodage des Descriptions
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.
Step7:  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
Step8:  Apprentissage
Nous allons maintenant définir notre modèle récurrent afin de générer notre modèle de prédiction. 
Prenez le temps de bien comprendre toutes les fonctions et arguments utilisés pour construire ce modèle.
Step9:  Q Pourquoi est-ce la categorical_crossentropy qui est utilisée comme fonction de perte?
Génération de Texte
La celulle suivante permet de générer une description produit 
Step10:  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.
Step11:  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.
<ASSISTANT_TASK:>
Python Code:
#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
Explanation: Ateliers: Technologies de l'intelligence Artificielle
<center>
<a href="http://www.insa-toulouse.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/logo-insa.jpg" style="float:left; max-width: 120px; display: inline" alt="INSA"/></a> 
<a href="http://wikistat.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/wikistat.jpg" width=400, style="max-width: 150px; display: inline"  alt="Wikistat"/></a>
<a href="http://www.math.univ-toulouse.fr/" ><img src="http://www.math.univ-toulouse.fr/~besse/Wikistat/Images/logo_imt.jpg" width=400,  style="float:right;  display: inline" alt="IMT"/> </a>
</center>
Traitement Naturel du Langage (NLP) : Génération de Texte avec des Réseaux Récurrent.
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.
Librairies
End of explanation
N = 100000
DATA_DIR = ""
X = np.load(DATA_DIR+"data/description_coque.npy")[:N]
print(X.shape)
print(X[:3])
Explanation: Téléchargement des données
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.
End of explanation
Nd=197
Explanation: Exercice Vérifiez que toutes les séquences sont bien de tailles 197.
End of explanation
chars = list(functools.reduce(lambda x,y : x.union(y), [set(x) for x in X], set()))
print("Vocabulaire : " +  str(chars))
Explanation: Mise en forme  des données
La génération de texte implique de constuire un réseau Many-To-One :
<img src="https://raw.githubusercontent.com/wikistat/AI-Frameworks/master/slides/OneToMany.png" alt="drawing" width="400"/>
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}$
Création de la liste des caractères
Afin d'encoder les description sous format 'One-Hot encoding'  nous devons dans un premier temps retrouver la taille $Nv$ de notre vocabulaire constitué de tout les caractères présent dans la description.
End of explanation
chars.extend(["start","end"])
Nv = len(chars)
print("Taille du vocabulaire : %d" %Nv)
Explanation: Nous ajoutons à ce vocabulaire deux indicateur permettant de localiser le début et la fin de chaque description
End of explanation
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"]
Explanation: Création des dictionnaires
Les dictionnaires char_to_int et int_to_char permettent respectivement d'encoder une description texte et de décoder un encodage `One-Hot``
End of explanation
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
X_vec, Y_vec = encode_input_output_sequence(X[:N], Nd+1, Nv, char_to_int,I_START,I_END)
X_vec.shape
Explanation: Encodage des Descriptions
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.
End of explanation
# %load solution/3_1.py
Explanation: 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
End of explanation
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()
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")
Explanation: Apprentissage
Nous allons maintenant définir notre modèle récurrent afin de générer notre modèle de prédiction. 
Prenez le temps de bien comprendre toutes les fonctions et arguments utilisés pour construire ce modèle.
End of explanation
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)
Explanation: Q Pourquoi est-ce la categorical_crossentropy qui est utilisée comme fonction de perte?
Génération de Texte
La celulle suivante permet de générer une description produit :
End of explanation
#%load solution/3_2.py
Explanation: 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.
End of explanation
#%load solution/3_3.py
Explanation: 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.
End of explanation
<END_TASK>