{"cells":[{"metadata":{},"cell_type":"markdown","source":"# **Proyecto 3 - Quora Insincere Questions**"},{"metadata":{},"cell_type":"markdown","source":"AJA, PROFESOR ARAQUE, AGARRESE PORQUE LO QUE VA A VER A CONTINUACIÓN ES ALTO PROYECTO, OK? LO SIENTO MUCHO PERO DESPUÉS DE VERLO \nUSTED NO ME VA A SALIR CON COSAS KIKE \"No y tal, les faltó un poco mejorar esto y explicar mejor aquello\" PORQUE ESTE ES LITERAL \nMI ÚLTIMO PROYECTO DE PROGRAMACIÓN DE LA CARRERA Y ME LO VOY A TRIPEAR COMO NO TIENE IDEA"},{"metadata":{},"cell_type":"markdown","source":"## 1) Preprocesamiento de los Datos"},{"metadata":{},"cell_type":"markdown","source":"Basado en el Kaggle de [Dieter](https://www.kaggle.com/christofhenkel) ([How to: Preprocessing when using embeddings](https://www.kaggle.com/christofhenkel/how-to-preprocessing-when-using-embeddings)) se hará un preprocesamiento al conjunto de acuerdo al embedding a utilizar, esto para poder acercar el vocubulario lo más cercano posible al embedding"},{"metadata":{},"cell_type":"markdown","source":"### 1.1) Importación de librerías"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import pandas as pd\nfrom tqdm import tqdm\ntqdm.pandas() # Esto es para poder ver progreso de los datos que se están procesando porque \n              # sino no ando cloro de que está pasando y me pieldo","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Cargamos el ganado:"},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"../input/quora-insincere-questions-classification/train.csv\")\ntest = pd.read_csv(\"../input/quora-insincere-questions-classification/test.csv\")\nprint(\"Train shape : \",train.shape)\nprint(\"Test shape : \",test.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1.2) Función auxiliar\n\nLa siguiente función trifásica es para mantener un seguimiento del vocabulario de entrenamiento, éste va a lo largo de todo el texto contando las ocurrencias las palabras que están contenido dentro de éste."},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_vocab(sentences, verbose =  True):\n    \"\"\"\n    :param sentences: list of list of words\n    :return: dictionary of words and their count\n    \"\"\"\n    vocab = {}\n    for sentence in tqdm(sentences, disable = (not verbose)):\n        for word in sentence:\n            try:\n                vocab[word] += 1\n            except KeyError:\n                vocab[word] = 1\n    return vocab","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 1.3) Primera cita\n\nEn la primera cita es cuando uno obtiene de verdad la primera impresión de la persona que lo encarpa a uno, esta es nuestra primera cita con el conjunto de datos, ay kemosión 🧡"},{"metadata":{"trusted":true},"cell_type":"code","source":"sentences = train[\"question_text\"].progress_apply(lambda x: x.split()).values\nvocab = build_vocab(sentences)\nprint({k: vocab[k] for k in list(vocab)[:5]})","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\"How did Quebec nationalists see\", el hecho de que sean nacionalistas no implican que sean ciegos 🙄"},{"metadata":{},"cell_type":"markdown","source":"### 1.4) Importando el embedding que tal\n\nBueno, ahora hay que importar el beta que realmente importa, embedding que se va a usar para el modelo."},{"metadata":{"trusted":true},"cell_type":"code","source":"from gensim.models import KeyedVectors\n\nnews_path = '../input/quora-insincere-questions-classification/embeddings/GoogleNews-vectors-negative300/GoogleNews-vectors-negative300.bin'\nembeddings_index = KeyedVectors.load_word2vec_format(news_path, binary=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 1.5) Hora de saber que tanto B!\"@-hit tiene el embedding \n\nLa función a continuación nos va a permitir ver la intersección que hay entre el vocabulario (o nuestros datos) y los embeddings, de manera tal de que se pueda limpiar y ajustar más el vocabulario a nuestras necesidades. \n\nSe denotará a \"oov\" como aquellas palabras que están fuera del vocabulario (out of vocabulary)"},{"metadata":{"trusted":true},"cell_type":"code","source":"import operator \n\ndef check_coverage(vocab,embeddings_index):\n    a = {}\n    oov = {}\n    k = 0\n    i = 0\n    for word in tqdm(vocab):\n        try:\n            a[word] = embeddings_index[word]\n            k += vocab[word]\n        except:\n\n            oov[word] = vocab[word]\n            i += vocab[word]\n            pass\n\n    print('Found embeddings for {:.2%} of vocab'.format(len(a) / len(vocab)))\n    print('Found embeddings for  {:.2%} of all text'.format(k / (k + i)))\n    sorted_x = sorted(oov.items(), key=operator.itemgetter(1))[::-1]\n\n    return sorted_x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"oov = check_coverage(vocab,embeddings_index)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Sólamente el 32% del vocabulario tiene embeddings, de manera que aproximadamente 21% de los datos que se tienen no sirven de nada. Esto no puede ser así vale, ¿Cómo en un proyecto tan arrecho como este podemos permitir esto? Nonono que vaina es pues.\n\nA continuación se muestran las primeras 10 palabras dentro de la variable oov:"},{"metadata":{"trusted":true},"cell_type":"code","source":"oov[:10]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Acá podemos ver como pequeñas palabras que no aportan mucho valor como \"to\", \"a\", \"of\" y \"and\" hay por coñazo en nuestro conjunto de datos pero está fuera de los embeddings de GoogleNews, esto evidentemente hay que eliminarlo ya que no aporta valor en absoluto para lo que se quiere. A su vez, podemos notar como hay varias palabras que incluyen signo de interrogación en ellas como si fuesen una sola, por lo que es momento de ver si este se encuentra en los embeddings, de manera tal que sea necesario mantenerlo o no."},{"metadata":{"trusted":true},"cell_type":"code","source":"'?' in embeddings_index","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'&' in embeddings_index","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Acá podemos ver que los embeddings de GoogleNews no contienen el signo de interrogación pero si inluyen el simbolo de ampersand. De hecho, luego de una larga y exhaustiva búsqueda en Mister Google, resulta que sus embeddings [no contienen signos de puntuación](https://www.reddit.com/r/MLQuestions/comments/aaha9g/can_word_embeddings_like_glove_fasttext_and/ecs115z/), entonces dado que sobran, a nadie les importa y Google siempre tiene la razón vamos a quitarlos xd \n\n*En realidad es para poder mejorar el vocabulario para que tenga más palabras en el conjunto de datos que concuerden con los embeddings*"},{"metadata":{},"cell_type":"markdown","source":"### 1.6) Se acerca el verano y las manos... A la obra porque hay que trabajar osi osi"},{"metadata":{"trusted":true},"cell_type":"code","source":"def clean_text(x):\n\n    x = str(x)\n    for punct in \"/-'\":\n        x = x.replace(punct, ' ')\n    for punct in '&':\n        x = x.replace(punct, f' {punct} ')\n    for punct in '?!.,\"#$%\\'()*+-/:;<=>@[\\\\]^_`{|}~' + '“”’':\n        x = x.replace(punct, '')\n    return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"La función anterior nos permitirá cumplir lo anterior descrito.\n\nAhora vamos a ver qlq con qlq:"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"question_text\"] = train[\"question_text\"].progress_apply(lambda x: clean_text(x))\nsentences = train[\"question_text\"].apply(lambda x: x.split())\nvocab = build_vocab(sentences)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"oov = check_coverage(vocab,embeddings_index)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![](https://i.ibb.co/ckLKc7j/descargar.jpg)VAMOOOOOOSSSSSS, así es que es vale nojombre, somos altos hackers y el que diga lo contrario es un nvidioso (osea un oso que trabaja en Nvidia).\n\nBueno, aquí lo que pasó es que se duplicó la cantidad de vocabulario que se encuentra en los embeddings arreglando los signos de puntuación. Veamos que tenemos ahora:"},{"metadata":{"trusted":true},"cell_type":"code","source":"oov[:10]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Bien, ahora sucede que hay varios números en el aire, veamos más detalladamente esto:"},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(10):\n    print(embeddings_index.index2entity[i])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Bien, lo que sucede es que los números que son mayores a 9 son transformados en numerales (#), vamos a arreglar eso porque de pana que así no se puede:"},{"metadata":{"trusted":true},"cell_type":"code","source":"import re\n\ndef clean_numbers(x):\n\n    x = re.sub('[0-9]{5,}', '#####', x)\n    x = re.sub('[0-9]{4}', '####', x)\n    x = re.sub('[0-9]{3}', '###', x)\n    x = re.sub('[0-9]{2}', '##', x)\n    return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"question_text\"] = train[\"question_text\"].progress_apply(lambda x: clean_numbers(x))\nsentences = train[\"question_text\"].progress_apply(lambda x: x.split())\nvocab = build_vocab(sentences)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"raw","source":"Probemos ahora a ver que tal:"},{"metadata":{"trusted":true},"cell_type":"code","source":"oov = check_coverage(vocab,embeddings_index)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Bien, no es una mejora tan sustancial como la anterior pero es algo vale, no se puede pedir tantos cambios tan radicales en esta vida, poco a poco, a paso de fracasadores... khé\n\n![](https://images7.memedroid.com/images/UPLOADED830/5ddbb05163918.jpeg)"},{"metadata":{},"cell_type":"markdown","source":"Vemos que nos quedó en las palabras que están fuera del vocabulario:"},{"metadata":{"trusted":true},"cell_type":"code","source":"oov[:20]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Bien, hay un problema y es que aun quedan palabras pequeñas como \"to\" que no están en los embeddings de GoogleNews y son un grupo generoso que evidentemente no está aportando nada en absoluto. Ya nos encargaremos de eso.\n\nMientras tanto la siguiente función multifásica radioactiva nos permite hacer varias cosas, como son quitar palabras mal escritas, acronimos, palabras de inglés británico, así como nombres de redes sociales que centraremos en \"social medium\":"},{"metadata":{"trusted":true},"cell_type":"code","source":"def _get_mispell(mispell_dict):\n    mispell_re = re.compile('(%s)' % '|'.join(mispell_dict.keys()))\n    return mispell_dict, mispell_re\n\n\nmispell_dict = {'colour':'color',\n                'centre':'center',\n                'didnt':'did not',\n                'doesnt':'does not',\n                'isnt':'is not',\n                'shouldnt':'should not',\n                'favourite':'favorite',\n                'travelling':'traveling',\n                'counselling':'counseling',\n                'theatre':'theater',\n                'cancelled':'canceled',\n                'labour':'labor',\n                'organisation':'organization',\n                'wwii':'world war 2',\n                'citicise':'criticize',\n                'instagram': 'social medium',\n                'whatsapp': 'social medium',\n                'snapchat': 'social medium'\n\n                }\nmispellings, mispellings_re = _get_mispell(mispell_dict)\n\ndef replace_typical_misspell(text):\n    def replace(match):\n        return mispellings[match.group(0)]\n\n    return mispellings_re.sub(replace, text)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Una vez hecho esto, vamos a quitar las palabras 'a','to','of' y 'and' de nuestro conjunto de datos porque no están en los embeddings de GoogleNews, entonces no es que estén aportando mucho ps"},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"question_text\"] = train[\"question_text\"].progress_apply(lambda x: replace_typical_misspell(x))\nsentences = train[\"question_text\"].progress_apply(lambda x: x.split())\nto_remove = ['a','to','of','and']\nsentences = [[word for word in sentence if not word in to_remove] for sentence in tqdm(sentences)]\nvocab = build_vocab(sentences)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"oov = check_coverage(vocab,embeddings_index)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![YEAAAAAAAAAH](https://i.ibb.co/Z1ydGW5/descargar-1.jpg)\n\n**LISTOOOOOOO, conseguimos que cerca del 99% del texto del conjunto de datos aparezca en los embeddings de GoogleNews**\n\nY si vemos la variable oov:"},{"metadata":{"trusted":true},"cell_type":"code","source":"oov[:20]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"No es que haya soluciones así como que bien rapidito para ellas pero bueno, se hace lo que se puede y pa' lante ps. \n\nHablando claro 99% es bien decente"},{"metadata":{},"cell_type":"markdown","source":"## 2) Preparando todo para que empiece la fiesta\n\n![](https://cdn.dopl3r.com//media/memes_files/quedan-pocos-dias-para-salvar-el-semestre-a-trabajar-kdkdkd-mematicnet-vw5RH.jpg)\n\n*Si profe, estamos haciendo este proyecto a última hora, por favor no nos juzgue.*\n\nUna vez habiendo procesado los datos para que se apreveche al máximo el uso de los embeddings, se procederá a hacer el setup de lo que será nuestra red neuronal recurrente para el analisis de las preguntas de Quora"},{"metadata":{},"cell_type":"markdown","source":"### 2.1) Importando las librerías que nos van a hacer todo juas juas"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.preprocessing.text import Tokenizer\nfrom keras.preprocessing.sequence import pad_sequences\n\nimport os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport math\nfrom sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.2) Obteniendo un conjunto de validación"},{"metadata":{},"cell_type":"markdown","source":"Sacaremos un pequeño conjunto de validación de nuestro conjunto de entrenamiento:"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df, val_df = train_test_split(train, test_size=0.1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Ahora cargaremos bien en un diccionario los embeddings de GoogleNews:"},{"metadata":{"trusted":true},"cell_type":"code","source":"word2vecDict = embeddings_index\nembeddings_index = dict()\nfor word in word2vecDict.wv.vocab:\n    embeddings_index[word] = word2vecDict.word_vec(word)\nprint('Loaded %s word vectors.' % len(embeddings_index))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Una vez cargado el diccionario con los vectores de las palabras, se convertiran cada una de las oraciones a un vector con los valores correspondientes del embedding de GoogleNews, al final lo que se tendrá, será una matriz que contenga todos los valores de las oraciones."},{"metadata":{},"cell_type":"markdown","source":"### 2.3) Funciones auxiliares"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Convert values to embeddings\ndef text_to_array(text):\n    empyt_emb = np.zeros(300)\n    text = text[:-1].split()[:30]\n    embeds = [embeddings_index.get(x, empyt_emb) for x in text]\n    embeds+= [empyt_emb] * (30 - len(embeds))\n    return np.array(embeds)\n\n# train_vects = [text_to_array(X_text) for X_text in tqdm(train_df[\"question_text\"])]\nval_vects = np.array([text_to_array(X_text) for X_text in tqdm(val_df[\"question_text\"][:3000])])\nval_y = np.array(val_df[\"target\"][:3000])\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"La siguiente función, es una mausqueherramienta misteriosa que nos ayudará más tarde, esta permitirá generar batch de tamaño de 128 para el entrenamiento de la red"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data providers\nbatch_size = 128\n\ndef batch_gen(train_df):\n    n_batches = math.ceil(len(train_df) / batch_size)\n    while True: \n        train_df = train_df.sample(frac=1.)  # Shuffle the data.\n        for i in range(n_batches):\n            texts = train_df.iloc[i*batch_size:(i+1)*batch_size, 1]\n            text_arr = np.array([text_to_array(text) for text in texts])\n            yield text_arr, np.array(train_df[\"target\"][i*batch_size:(i+1)*batch_size])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3) Definiendo el modelo que tal\n\n"},{"metadata":{},"cell_type":"markdown","source":"### 3.1) Importación de librerías"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import CuDNNLSTM, Dense, Bidirectional\nimport tensorflow as tf","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3.2) Creación del modelo que tal"},{"metadata":{"trusted":true},"cell_type":"code","source":"#inp = tf.keras.layers.Input(shape=(10,))\n#emb = tf.keras.layers.Embedding(20, 4)(inp)\n#x2 = tf.keras.layers.Bidirectional(tf.compat.v1.keras.layers.CuDNNGRU(64, return_sequences=True))(emb)\n#max_pl = tf.keras.layers.GlobalMaxPooling1D()(x2)\n#x = tf.keras.layers.Dense(16, activation=\"relu\")(max_pl)\n#x = tf.keras.layers.Dropout(0.1)(x)\n#output = tf.keras.layers.Dense(1, activation=\"sigmoid\")(x)\n\n#model = tf.keras.models.Model(inputs=inp, outputs=output)\n\nmodel = tf.keras.models.Sequential()\nx = tf.keras.layers.Bidirectional(tf.compat.v1.keras.layers.CuDNNLSTM(64, return_sequences=True),input_shape=(30, 300))\nmodel.add(x)\ny = tf.keras.layers.Bidirectional(tf.compat.v1.keras.layers.CuDNNLSTM(64))\nmodel.add(y)\nmodel.add(tf.keras.layers.Dense(1, activation=\"sigmoid\"))\n\nmodel.compile(loss='binary_crossentropy',\n              optimizer='adam',\n              metrics=['accuracy'])\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nmg = batch_gen(train_df)\n\n\n\nmodel.fit_generator(mg, epochs=20,\n                    steps_per_epoch=1000,\n                    validation_data=(val_vects, val_y),\n                    verbose=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Limpiaremos la RAM"},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\ndel val_vects, val_y, train_df, val_df, oov, sentences, to_remove, vocab, mispell_dict, news_path\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# prediction part\nbatch_size = 256\ndef batch_gens(test_df):\n    n_batches = math.ceil(len(test_df) / batch_size)\n    for i in range(n_batches):\n        texts = test_df.iloc[i*batch_size:(i+1)*batch_size, 1]\n        text_arr = np.array([text_to_array(text) for text in texts])\n        yield text_arr\n\n\nall_preds = []\nfor x in tqdm(batch_gens(test)):\n    all_preds.extend(model.predict(x).flatten())\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_te = (np.array(all_preds) > 0.5).astype(np.int)\n\nsubmit_df = pd.DataFrame({\"qid\": test[\"qid\"], \"prediction\": y_te})\nsubmit_df.to_csv(\"submission.csv\", index=False)\n\nimport matplotlib.pyplot as plt\nsubmit_df['prediction'].plot(kind='hist', bins=100)\nplt.xlabel('prediction')","execution_count":null,"outputs":[]}],"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":1}