{"cells":[{"metadata":{},"cell_type":"markdown","source":"# **INTRODUCTION**\n\n\n*Définition du problème*\n\nDans le Défi de classification des questions de Quora Insincere, on a des questions et on nous demande de classer les questions comme sincères (0) ou non sincères (1). Le manque de sincérité dans ce cas comprend également des commentaires toxiques ou trompeurs, comme on peut le voir dans les échantillons ci-dessous. \nDans les données de formation, on a l’identificateur de question (qid), la question (question_text) et la catégorie (target). Dans l’ensemble de tests, on a seulement l’identificateur et la question, et on nous demande de les classer dans les deux catégories."},{"metadata":{},"cell_type":"markdown","source":"# *Importation des données*"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"scrolled":false},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport re\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport math\nimport os\nimport time\n\nfrom tensorflow.keras.preprocessing.text import Tokenizer\nfrom tensorflow.keras.preprocessing.sequence import pad_sequences\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense,Flatten,Embedding,Activation, Dropout , Input\nfrom tensorflow.keras.layers import Conv1D, MaxPooling1D, GlobalMaxPooling1D , LSTM, Bidirectional\n\nfrom keras.models import Model\nfrom keras import initializers, regularizers, constraints, layers\n\nfrom tensorflow.keras.optimizers import Adam\nfrom keras import optimizers, callbacks \nfrom sklearn.metrics import log_loss\nfrom sklearn.model_selection import train_test_split\nfrom keras.layers import Flatten\nimport sklearn.metrics as metrics\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# Lecture Data frame\ntrain_df = pd.read_csv('../input/quora-insincere-questions-classification/train.csv')\ntest_df  = pd.read_csv('../input/quora-insincere-questions-classification/test.csv')\n\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"print(train_df.columns)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train_df['question_text'].str.len().hist(color='y')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"f, ax = plt.subplots(nrows=1, ncols=1, figsize=(6, 4))\n\nsns.countplot(train_df.target, palette=['blue', 'salmon'], ax=ax)\n\nax.set_title(\"Distribution des categories\", fontsize=16)\nax.set_ylabel(ylabel='Count', fontsize=14)\nax.set_xticklabels(labels=['Sincere', 'Insincere'], fontsize=14)\nax.set_xlabel(xlabel='Category', fontsize=14)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train_df[\"quest_len\"] = train_df[\"question_text\"].apply(lambda x: len(x.split()))\n\nsincere = train_df[train_df[\"target\"] == 0]\ninsincere = train_df[train_df[\"target\"] == 1]\n\nplt.figure(figsize = (15, 8))\nsns.distplot(sincere[\"quest_len\"], hist = True, label = \"sincere\")\nsns.distplot(insincere[\"quest_len\"], hist = True, label = \"insincere\")\nplt.legend(fontsize = 10)\nplt.title(\"Longueur des questions par Classe\", fontsize = 12)\nplt.show()\n\ntrain_df[\"target\"].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"print(\"Train shape : \",train_df.shape)\nprint(\"Test shape : \",test_df.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Pré-traitement... une étape vers la vraie analyse\n\nNous sommes tous bien conscients que, avant que nous puissions utiliser des algorithmes de machine learning (ML) nous avons besoin de rendre notre dataset faisable pour l’analyse que on veut développer. Cette phase de pré-traitement des données est particulièrement importante lorsque on travaille avec des données en format texte. En effet, la plupart des mots qui constituent une phrase ne sont pas utiles au groupe de travail. L’objectif, à ce stade, est de rendre la vie de notre classeur aussi simple que possible afin d’en maximiser les performances. Voici quelques fonctions simples pour le nettoyage, non exhaustif, du texte :"},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# remove space\nspaces = ['\\u200b', '\\u200e', '\\u202a', '\\u202c', '\\ufeff', '\\uf0d8', '\\u2061', '\\x10', '\\x7f', '\\x9d', '\\xad', '\\xa0']\ndef remove_space(text):\n    \"\"\"\n    remove extra spaces and ending space if any\n    \"\"\"\n    for space in spaces:\n        text = text.replace(space, ' ')\n    text = text.strip()\n    text = re.sub('\\s+', ' ', text)\n    return text\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# replace strange punctuations and raplace diacritics\nfrom unicodedata import category, name, normalize\n\ndef remove_diacritics(s):\n    return ''.join(c for c in normalize('NFKD', s.replace('ø', 'o').replace('Ø', 'O').replace('⁻', '-').replace('₋', '-'))\n                  if category(c) != 'Mn')\n\nspecial_punc_mappings = {\"—\": \"-\", \"–\": \"-\", \"_\": \"-\", '”': '\"', \"″\": '\"', '“': '\"', '•': '.', '−': '-',\n                         \"’\": \"'\", \"‘\": \"'\", \"´\": \"'\", \"`\": \"'\", '\\u200b': ' ', '\\xa0': ' ','،':'','„':'',\n                         '…': ' ... ', '\\ufeff': ''}\ndef clean_special_punctuations(text):\n    for punc in special_punc_mappings:\n        if punc in text:\n            text = text.replace(punc, special_punc_mappings[punc])\n    \n    text = remove_diacritics(text)\n    return text","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Expression régulière**, ou regex est extrêmement puissant dans la recherche et la manipulation de chaînes de texte, en particulier dans le traitement des fichiers texte. Une ligne de regex peut facilement remplacer plusieurs dizaines de lignes de codes de programmation."},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def clean_number(text):\n    \n    text = re.sub(r'(\\d+)([a-zA-Z])', '\\g<1> \\g<2>', text)\n    text = re.sub(r'(\\d+) (th|st|nd|rd) ', '\\g<1>\\g<2> ', text)\n    text = re.sub(r'(\\d+),(\\d+)', '\\g<1>\\g<2>', text)\n    \n    return text","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"def decontracted(text):\n    # specific\n    text = re.sub(r\"(W|w)on(\\'|\\’)t \", \"will not \", text)\n    text = re.sub(r\"(C|c)an(\\'|\\’)t \", \"can not \", text)\n    text = re.sub(r\"(Y|y)(\\'|\\’)all \", \"you all \", text)\n    text = re.sub(r\"(Y|y)a(\\'|\\’)ll \", \"you all \", text)\n\n    # general\n    text = re.sub(r\"(I|i)(\\'|\\’)m \", \"i am \", text)\n    text = re.sub(r\"(A|a)in(\\'|\\’)t \", \"is not \", text)\n    text = re.sub(r\"n(\\'|\\’)t \", \" not \", text)\n    text = re.sub(r\"(\\'|\\’)re \", \" are \", text)\n    text = re.sub(r\"(\\'|\\’)s \", \" is \", text)\n    text = re.sub(r\"(\\'|\\’)d \", \" would \", text)\n    text = re.sub(r\"(\\'|\\’)ll \", \" will \", text)\n    text = re.sub(r\"(\\'|\\’)t \", \" not \", text)\n    text = re.sub(r\"(\\'|\\’)ve \", \" have \", text)\n    return text","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"import string\nregular_punct = list(string.punctuation)\nextra_punct = [\n    ',', '.', '\"', ':', ')', '(', '!', '?', '|', ';', \"'\", '$', '&','/', '[', ']', '>',\n    '%', '=', '#', '*', '+', '\\\\', '•',  '~', '@', '£','·', '_', '{', '}', '©', '^', '`',\n    '<', '→', '°', '€', '™', '›','♥', '←', '×', '§', '″', '′', 'Â', '½', 'à', '…', '“', '★',\n    '”','–', '●', 'â', '►', '−', '¢', '²', '¶', '↑', '±', '¿', '▾','—', '‹', '─', '：', '¼', \n    '▼', '■', '’', '▀', '¨', '♫', '☆','é', '¯', '♦', '¤', '▲','è', '¸', '¾', 'Ã', '⋅', '‘', \n    '∙', '）','↓', '、', '│', '（', '»','，', '♪', '³', '・', '❤', 'ï', 'Ø', '≤', '√', '«', '»',\n    '´', 'º', '¾', '¡', '§', '£', '₤']\nall_punct = list(set(regular_punct + extra_punct))\n# do not spacing - and .\nall_punct.remove('-')\nall_punct.remove('.')\n\ndef spacing_punctuation(text):\n    \"\"\"\n    add space before and after punctuation and symbols\n    \"\"\"\n    for punc in all_punct:\n        if punc in text:\n            text = text.replace(punc, f' {punc} ')\n    return text","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"train_df.question_text = train_df.question_text.apply(remove_space)\ntrain_df.question_text = train_df.question_text.apply(clean_special_punctuations)\ntrain_df.question_text = train_df.question_text.apply(clean_number)\ntrain_df.question_text = train_df.question_text.apply(decontracted)\ntrain_df.question_text = train_df.question_text.apply(spacing_punctuation)\n\ntrain_df.head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"test_df.question_text = test_df.question_text.apply(remove_space)\ntest_df.question_text = test_df.question_text.apply(clean_special_punctuations)\ntest_df.question_text = test_df.question_text.apply(clean_number)\ntest_df.question_text = test_df.question_text.apply(decontracted)\ntest_df.question_text = test_df.question_text.apply(spacing_punctuation)\n\ntest_df.head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"## split to train and validation\ntrain_df, val_df = train_test_split(train_df, test_size=0.2, random_state=1000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# Filling missing values in the text columns if any\ntrain_X = train_df['question_text'].fillna(\"_na_\").values\nval_X   = val_df['question_text'].fillna(\"_na_\").values\ntest_X  = test_df['question_text'].fillna(\"_na_\").values","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Maintenant, nous allons tokenize nos phrases et créer le vocabulaire. On peut définir un nombre max pour le nombre de mots dans le vocabulaire. "},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"max_features = 50000 # how many unique words to use\nmaxlen       = 50  # max number of words in a question to use\nembed_size   = 300 # how big is each word vector\n\n# Tokenizing words in our sentences using keras tokenizer\n\ntokenizer = Tokenizer(num_words = max_features)\ntokenizer.fit_on_texts(list(train_X))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pour prédire le prochain caractère, nous devons fournir au RNN une **séquence des caractères** précédents. Afin d’obtenir de nombreux exemples d’apprentissages, nous découpons des séquences dans les discours, en coulissant d’un certain pas entre chaque tranche."},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# converting each text in the dataset to a sequence of integers\n\ntrain_X = tokenizer.texts_to_sequences(train_X)\nval_X   = tokenizer.texts_to_sequences(val_X)\ntest_X  = tokenizer.texts_to_sequences(test_X)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"# Padding sequences \n\ntrain_X = pad_sequences(train_X, maxlen = maxlen)\nval_X   = pad_sequences(val_X, maxlen = maxlen)\ntest_X  = pad_sequences(test_X, maxlen = maxlen)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"#Target values\n\ntrain_y = train_df['target'].values\nval_y = val_df['target'].values","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"print(train_X[100])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Chargement de l'Embeddings \n\nGloVe (« Global Vectors for Word Representation ») comme son nom l’indique est meilleur pour préserver les contextes globaux car il crée une matrice globale de co-occurrence en estimant la probabilité qu’un mot donné se produise avec d’autres mots.\n\nD’abord il faut lire dans le fichier d'embedding dans un dictionnaire - chaque entrée est un mot, suivi du vecteur de nombres pour représenter ses valeurs."},{"metadata":{"trusted":true},"cell_type":"code","source":"import zipfile","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with zipfile.ZipFile(\"../input/quora-insincere-questions-classification/embeddings.zip\",\"r\") as z:\n    z.extractall(\".\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"embeddings = '../input/glove840b300dtxt/glove.840B.300d.txt'\n \ndef get_coefs(word,*arr):\n    return word, np.asarray(arr, dtype = 'float32')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(embeddings))\n\nall_embs = np.stack(embeddings_index.values())\nemb_mean,emb_std = all_embs.mean(), all_embs.std()\nembed_size = all_embs.shape[1]\n\nword_index = tokenizer.word_index\nnb_words = min(max_features, len(word_index))\nembedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))\nfor word, i in word_index.items():\n    if i >= max_features: continue\n    embedding_vector = embeddings_index.get(word)\n    if embedding_vector is not None: embedding_matrix[i] = embedding_vector\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"embedding_matrix[3]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Le model BLSTM \n\nIl est possible d'utiliser un réseau appelé BLSTM, qui consiste à dédoubler une couche LSTM, l'une étant apprise pour parcourir le signal de gauche à droite, et l'autre de droite à gauche :\nLes deux couches sont combinées afin de prendre les meilleures décisions locales en ayant \"vu\"... l'intégralité du signal !\n\nADAM est utile pour de l'optimisation stochastique. Elle utilise les EWA - Exponentially Weigthed Averages pour faire une estimation lissée des gradients à l'aide de moments\n\nLe dropout est une technique qui est destinée à empêcher le sur-ajustement sur les données de training en abandonnant des unités dans un réseau de neurones. En pratique, les neurones sont soit abandonnés avec une probabilité pp ou gardés avec une probabilité 1-p1−p.\n\nUn tel modèle s’implémente en quelques lignes avec Keras. On définit un modèle Sequential, sur lequel on ajoute une successions de couches qui correspondent directement aux étapes du schéma précédent. Les couches intermédiaires de dropout sont une technique de régularisation, pour éviter au modèle de trop coller aux données vues en entraînement et améliorer sa généralisation. Dans ce cas avec une probabilité d'etre abbandonné = 0.1"},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"inp = Input(shape=(maxlen,))\nx = layers.Embedding(max_features, embed_size, weights=[embedding_matrix])(inp)\nx = layers.Bidirectional(layers.LSTM(64, return_sequences=True))(x)\nx = layers.Bidirectional(layers.LSTM(64))(x)\nx = Dense(16, activation=\"relu\")(x)\nx = Dropout(0.1)(x)\nx = Dense(1, activation=\"sigmoid\")(x)\nmodel = Model(inputs=inp, outputs=x)\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])\nprint(model.summary())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Entraîner un RNN à prédire\nNotre objectif est de prédire, à partir d’une séquence de 50 caractères consécutifs, le caractère suivant. Il s’agit d’un problème d’apprentissage supervisé : à chaque itération, on fournit au modèle une séquence d’entrée encodée ainsi que le caractère encodé attendu en sortie. Le modèle effectue une prédiction, la compare à la cible attendue, et ajuste ses paramètres (aussi appelés poids du réseau) en cas d’erreur. \nNous avons préparé nos données et créé notre modèle, il ne reste  maintenant plus qu’à lancer l’apprentissage. Une ligne suffit pour démarrer cette étape."},{"metadata":{},"cell_type":"markdown","source":"**ModelCheckpoint** \nA chaque époque, le pointeur de contrôle voit si les paramètres du modèle se sont améliorés - s’ils l’ont fait, il les enregistre dans un fichier appelé weights.hdf5."},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"checkpointer = ModelCheckpoint(filepath=\"weights.hdf5\", monitor='loss', save_best_only=True, mode='min')\nhistory =model.fit(train_X, train_y, batch_size=512, epochs=5, callbacks=[checkpointer], validation_data=(val_X, val_y))\n\n#history = model.fit(train_X, train_y, batch_size=512, epochs=5, validation_data=(val_X, val_y))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"print(history.history['loss'])\nprint(history.history['accuracy'])\nprint(history.history['val_loss'])\nprint(history.history['val_accuracy'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Epochs est le nombre maximum d’itérations ; \nbatch_size correspond aux nombre d’observations que l’on fait passer avant de remettre à jour les poids synaptiques.\nL’évolution de l’apprentissage est affichée dans la console IPython. En ce qui me concerne, voici\nles valeurs finales de loss = 0.051 et accuracy = 0.955**"},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"import matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"plt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('model train vs validation loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'validation'], loc='upper right')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Performance du modèle : fonction de perte liée à la phase d’entraînement et de test pour chaque epoch.\n\nSur le graphique, vous pouvez voir comment le modèle atteint un bon niveau de performance pendant la première Epoch."},{"metadata":{},"cell_type":"markdown","source":"thresh:\tIl s’agit d’un entier qui spécifie le moins de valeurs non manquantes qui empêchent les lignes ou les colonnes de tomber.\n\nLa valeur de thresh est 2, ce qui signifie que nous évitons toute chute, au moins 2 valeurs non vides sont requises."},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"import sklearn.metrics as accuracy_score ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"pred_glove_val_y = model.predict([val_X], batch_size=1024, verbose=1)\nfor thresh in np.arange(0.1, 0.501, 0.01):\n    thresh = np.round(thresh, 2)\n    print(\"F1 score at threshold {0} is {1}\".format(thresh, metrics.f1_score(val_y, (pred_glove_val_y>thresh).astype(int))))\n\npred_glove_test_y = model.predict([test_X], batch_size=1024, verbose=1)\n\ndel word_index, embeddings_index, all_embs, embedding_matrix, model, inp, x\nimport gc; gc.collect()\ntime.sleep(10)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Score de soumission = 0,626 pas si mauvais !"},{"metadata":{"trusted":true,"scrolled":false},"cell_type":"code","source":"pred_test_y = pred_glove_test_y \npred_test_y = (pred_test_y>0.35).astype(int) \nout_df = pd.DataFrame({\"qid\":test_df[\"qid\"].values}) \nout_df['prediction'] = pred_test_y \nout_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Conclusions\n\nDans cette étude, une brève introduction a été offerte concernant l’utilisation de l’apprentissage profond inhérents à l’analyse du langage naturel. Tout d’abord, nous avons montré comment mettre en œuvre l’étape fondamentale du préprocessage des données. Dans cette section ont été discutées les principales techniques et méthodologies les plus communément utilisées, parmi lesquelles on cite le processus d'\"analyse\" afin de supprimer des parties de texte, de mots et de ponctuation qui ne sont pas utiles à l’accomplissement de la tâche de classement.\nIls suivent la procédure de \"tokenizzazione\" et de \"padding\" pour l’obtention d’un ensemble approprié de données utilisables par l’algorithme ML. Ensuite, la procédure d'\"embedding\" qui représente un pas fondamental et non négligeable pour l’amélioration et la représentation de l’ensemble de données dans un espace n-dimensionnel. À ce stade, le \"pourquoi\" de cette procédure est nécessaire et comment elle peut effectivement être mise en œuvre.\nLes connaissances fournies par Glove sont alors transmises à notre modèle de réseau neural qui, par l’intermédiaire de la couche d’embedding, associe à chaque mot présent dans le dictionnaire un vecteur de taille @n'. Pour ce faire, cependant, il est nécessaire d’imposer la condition de non-remorquage de cette couche (trainable=False).\nUne fois le modèle entraîné, on a procédé à l’évaluation du modèle et on a fourni le graphique relatif à la performance pendant la phase d’entraînement et le tableau relatif au rapport classification qui nous a permis d’évaluer la qualité de la méthodologie proposée."}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}