#!/usr/bin/env python # coding: utf-8 # # Procesamiento del Lenguaje Natural con Python # *Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](https://relopezbriega.com.ar/) en [Matemáticas, Analisis de datos y Python](https://relopezbriega.github.io). El contenido esta bajo la licencia BSD.* # NLP # > "El lenguaje sirve no sólo para expresar el pensamiento, sino para hacer posibles pensamientos que no podrían existir sin él." # # **[Bertrand Russell](https://es.wikipedia.org/wiki/Bertrand_Russell)** # ## Introducción # # El [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) es una de las herramientas centrales en nuestra vida social y profesional. Entre otras cosas, actúa como un medio para transmitir ideas, información, opiniones y sentimientos; así como para persuadir, pedir información, o dar ordenes. Asimismo, el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) humano es algo que esta en constante cambio y evolución; y que puede llegar a ser muy *ambiguo* y *variable*. Tomemos por ejemplo la frase *"comí una pizza con amigos"* comparada con *"comí una pizza con aceitunas"*; su estructura es la misma, pero su significado es totalmente distinto. De la misma manera, un mismo mensaje puede ser expresado de formas diferentes; *"comí una pizza con amigos"* puede también ser expresado como *"compartí una pizza con amigos"*. # # Los seres humanos somos muy buenos a la hora de producir e interpretar el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje), podemos expresar, percibir e interpretar significados muy elaborados en fracción de segundos casi sin dificultades; pero al mismo tiempo, somos también muy malos a la hora de entender y describir formalmente las reglas que lo gobiernan. Por este motivo, entender y producir el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) por medio de una *computadora* es un problema muy difícil de resolver. Éste problema, es el campo de estudio de lo que en [inteligencia artificial](https://iaarbook.github.io/inteligencia-artificial/) se conoce como [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) o [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) por sus siglas en inglés. # # ## ¿Qué es el Procesamiento del Lenguaje Natural? # # El [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) o [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es una disciplina que se encuentra en la intersección de varias ciencias, tales como las [Ciencias de la Computación](https://es.wikipedia.org/wiki/Ciencias_de_la_computaci%C3%B3n), la [Inteligencia Artificial](https://iaarbook.github.io/) y [Psicología Cognitiva](https://es.wikipedia.org/wiki/Psicolog%C3%ADa_cognitiva). Su idea central es la de darle a las máquinas la capacidad de leer y comprender los idiomas que hablamos los humanos. La investigación del [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) tiene como objetivo responder a la pregunta de cómo las personas son capaces de comprender el significado de una oración oral / escrita y cómo las personas entienden lo que sucedió, cuándo y dónde sucedió; y las diferencias entre una suposición, una creencia o un hecho. # # En general, en [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) se utilizan seis niveles de comprensión con el objetivo de descubrir el significado del discurso. Estos niveles son: # # * **Nivel fonético:** Aquí se presta atención a la [fonética](https://es.wikipedia.org/wiki/Fon%C3%A9tica), la forma en que las palabras son pronunciadas. Este nivel es importante cuando procesamos la palabra hablada, no así cuando trabajamos con texto escrito. # # * **Nivel morfológico:** Aquí nos interesa realizar un análisis [morfológico](https://es.wikipedia.org/wiki/Morfolog%C3%ADa_ling%C3%BC%C3%ADstica) del discurso; estudiar la estructura de las palabras para delimitarlas y clasificarlas. # # * **Nivel sintáctico:** Aquí se realiza un análisis de [sintaxis](https://es.wikipedia.org/wiki/Sintaxis), el cual incluye la acción de dividir una oración en cada uno de sus componentes. # # * **Nivel semántico:** Este nivel es un complemente del anterior, en el análisis [semántico](https://es.wikipedia.org/wiki/Sem%C3%A1ntica) se busca entender el significado de la oración. Las palabras pueden tener múltiples significados, la idea es identificar el significado apropiado por medio del contexto de la oración. # # * **Nivel discursivo:** El nivel discursivo examina el significado de la oración en relación a otra oración en el texto o párrafo del mismo documento. # # * **Nivel pragmático:** Este nivel se ocupa del análisis de oraciones y cómo se usan en diferentes situaciones. Además, también cómo su significado cambia dependiendo de la situación. # # Todos los niveles descritos aquí son inseparables y se complementan entre sí. El objetivo de los sistemas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es incluir estas definiciones en una *computadora* y luego usarlas para crear una oración estructurada y sin ambigüedades con un significado bien definido. # # ## Aplicaciones del Procesamiento del Lenguaje Natural # # Los algoritmos de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) suelen basarse en algoritmos de [aprendizaje automático](https://iaarbook.github.io/machine-learning/). En lugar de codificar manualmente grandes conjuntos de reglas, el [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) puede confiar en el [aprendizaje automático](https://iaarbook.github.io/machine-learning/) para aprender estas reglas automáticamente analizando un conjunto de ejemplos y haciendo una [inferencia estadística](https://es.wikipedia.org/wiki/Estad%C3%ADstica_inferencial). En general, cuanto más datos analizados, más preciso será el modelo. Estos algoritmos pueden ser utilizados en algunas de las siguientes aplicaciones: # # * **Resumir texto:** Podemos utilizar los modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para extraer las ideas más importantes y centrales mientras ignoramos la información irrelevante. # # * **Crear chatbots:** Podemos utilizar las técnicas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para crear [chatbots](https://iaarhub.github.io/capacitacion/2017/04/09/31-herramientas-que-te-ayudaran-a-crear-tu-propio-chatbot/) que puedan interactuar con las personas. # # * **Generar automáticamente [etiquetas de palabras clave](https://es.wikipedia.org/wiki/Etiquetado_gramatical)**: Con [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) también podemos realizar un análisis de contenido aprovechando el algoritmo de [LDA](https://es.wikipedia.org/wiki/Latent_Dirichlet_Allocation) para asignar palabras claves a párrafos del texto. # # * **[Reconocer entidades](https://es.wikipedia.org/wiki/Reconocimiento_de_entidades_nombradas)**: Con [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) podemos identificar a las distintas entidades del texto como ser una persona, lugar u organización. # # * **[Análisis de sentimiento](https://es.wikipedia.org/wiki/An%C3%A1lisis_de_sentimiento)**: También podemos utilizar [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) para identificar el *sentimiento* de una cadena de texto, desde muy negativo a neutral y a muy positivo. # # # ## Librerías de Python para Procesamiento del Lenguaje Natural # # Actualmente, [Python](https://www.python.org/) es uno de los lenguajes más populares para trabajar en el campo la [Inteligencia Artificial](https://iaarbook.github.io/). Para abordar los problemas relacionados con el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) [Python](https://www.python.org/) nos proporciona las siguientes librerías: # # * **[NLTK](https://www.nltk.org/):** Es la librería líder para el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Proporciona interfaces fáciles de usar a más de *[50 corpus](https://nltk.org/nltk_data/)* y recursos léxicos, junto con un conjunto de bibliotecas de procesamiento de texto para la clasificación, tokenización, el etiquetado, el análisis y el razonamiento semántico. # # # * **[TextBlob](http://textblob.readthedocs.io/en/dev/):** [TextBlob](http://textblob.readthedocs.io/en/dev/) simplifica el procesamiento de texto proporcionando una interfaz intuitiva a [NLTK](https://www.nltk.org/). Posee una suave curva de aprendizaje al mismo tiempo que cuenta con una sorprendente cantidad de funcionalidades. # # # * **[Stanford CoreNLP](https://stanfordnlp.github.io/CoreNLP/):** Paquete desarrollado por la universidad de [Stanford](http://web.stanford.edu/class/cs224n/), para muchos constituye el estado del arte sobre las técnicas tradicionales de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Si bien esta escrita en Java, posee una [interface con Python](https://github.com/stanfordnlp/python-stanford-corenlp). # # # * **[Spacy](https://spacy.io/):** Es una librería relativamente nueva que sobresale por su facilidad de uso y su velocidad a la hora de realizar el procesamiento de texto. # # # * **[Textacy](http://textacy.readthedocs.io/en/latest/):** Esta es una librería de alto nivel diseñada sobre [Spacy](https://spacy.io/) con la idea de facilitar aun más las tareas relacionadas con el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). # # # * **[Gensim](https://radimrehurek.com/gensim/intro.html):** Es una librería diseñada para extraer automáticamente los temas semánticos de los documentos de la forma más eficiente y con menos complicaciones posible. # # # * **[pyLDAvis](https://pyldavis.readthedocs.io/en/latest/readme.html):** Esta librería está diseñado para ayudar a los usuarios a interpretar los temas que surgen de un análisis de tópicos. Nos permite visualizar en forma muy sencilla cada uno de los temas incluidos en el texto. # # # Como veremos, el [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) también se está sumando a la popularidad del [Deep Learning](https://relopezbriega.github.io/blog/2017/06/13/introduccion-al-deep-learning/), por lo que muchos de los [frameworks](https://iaarbook.github.io/python/#frameworks-para-deep-learning) que se utilizan en [Deep Learning](https://relopezbriega.github.io/blog/2017/06/13/introduccion-al-deep-learning/) pueden ser aplicados para realizar modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). # # ## Corpus lingüístico # # Hoy en día, es indispensable el uso de buenos recursos lingüísticos para el desarrollo de los sistemas de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Estos recursos son esenciales para la creación de gramáticas, en el marco de aproximaciones simbólicas; o para llevar a cabo la formación de módulos basados en el [aprendizaje automático](https://iaarbook.github.io/machine-learning/). # # Un [corpus lingüístico](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) es un conjunto amplio y estructurado de ejemplos reales de uso de la lengua. Estos ejemplos pueden ser textos (los más comunes), o muestras orales (generalmente transcritas). Un [corpus lingüístico](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) es un conjunto de textos relativamente grande, creado independientemente de sus posibles formas o usos. Es decir, en cuanto a su estructura, variedad y complejidad, un [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) debe reflejar una lengua, o su modalidad, de la forma más exacta posible; en cuanto a su uso, preocuparse de que su representación sea real. La idea es que representen al lenguaje de la mejor forma posible para que los modelos de [NLP](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) puedan aprender los patrones necesarios para entender el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje). Encontrar un buen [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) sobre el cual trabajar no suele ser una tarea sencilla; un [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) que se suele utilizar para entrenar modelos es el que incluye la información extraída de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/). # # ## Procesamiento del Lenguaje Natural con Python # # Hasta aquí la introducción, ahora llegó el momento de ensuciarse un poco las manos y comenzar a explorar algunos ejemplos de las herramientas que nos ofrece [Python](https://www.python.org/) para trabajar con problemas de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales). Comencemos por ejemplo, descargando el [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico) de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/) en español. Esto lo podemos hacer fácilmente utilizando [Textacy](http://textacy.readthedocs.io/en/latest/). # In[1]: # # Importando las librerías que vamos a utilizar import pandas as pd import numpy as np import matplotlib.pyplot as plt import textacy from textacy.datasets import Wikipedia from collections import Counter, defaultdict import warnings; warnings.simplefilter('ignore') # graficos incrustados get_ipython().run_line_magic('matplotlib', 'inline') # función auxiliar def leer_texto(texto): """Funcion auxiliar para leer un archivo de texto""" with open(texto, 'r') as text: return text.read() # In[2]: # Descargando copus de wikipedia wp = Wikipedia(data_dir='/home/raul/Documents/data', lang='es', version='latest') wp.download() # In[3]: # Chequeando la información descargada wp.info # In[4]: for text in wp.texts(min_len=1000, limit=2): print(text[:375], "\n") # Como podemos ver, con la ayuda de [Textacy](http://textacy.readthedocs.io/en/latest/) es muy fácil descargar la información de [wikipedia](https://dumps.wikimedia.org/eswiki/latest/) para luego poder utilizarla de base para nuestro [corpus](https://es.wikipedia.org/wiki/Corpus_ling%C3%BC%C3%ADstico). Veamos otros problemas que también podemos resolver con la ayuda de [Textacy](http://textacy.readthedocs.io/en/latest/); como ser los casos de detectar el idioma o de procesar todo un texto y analizarlo. # In[5]: # Detectando el idioma con taxtacy saludos = ["Hola", "Hello", "Bonjour", "Guten Tag", "Buon giorno", "Bom dia"] for saludo in saludos: print(textacy.text_utils.detect_language(saludo)) # In[6]: # Cargando el modelo en español de spacy nlp = textacy.data.spacy.load('es_core_web_md') # In[7]: # detalle de stop words # las stop words son las palabras más comunes de un corpus que en general # queremos eliminar porque no son significativas para un análisis. # Ocurren muchas veces, pero aportan muy poca información stop = list(textacy.data.spacy.es.STOP_WORDS) stop[:15] # In[8]: # Procesando un texto # Procesando 1984 de George Orwell - mi novela favorita texto = leer_texto('ORWELL_1984.txt') texto_procesado = nlp(texto) # In[9]: # Cuántas sentencias hay en el texto? sentencias = [s for s in texto_procesado.sents] print(len(sentencias)) # In[10]: # imprimir las primeras 10 sentencias para verificar el texto print(sentencias[1:11]) # In[11]: # sentencias con las que aparece el Gran Hermano [sent for sent in texto_procesado.sents if 'Gran Hermano' in sent.string][-10:] # In[12]: # def encontrar_personajes(doc): """ Devuelve una lista de los personajes de un `doc` con su cantidad de ocurrencias :param doc: NLP documento parseado por Spacy :return: Lista de Tuplas con la forma [('winston', 686), ("o'brien", 135), ('julia', 85),] """ personajes = Counter() for ent in doc.ents: if ent.label_ == 'PERSON': personajes[ent.lemma_] += 1 return personajes.most_common() # In[13]: # Extrayendo los personajes principales del texto y contando cuantas veces # son nombrados. print(encontrar_personajes(texto_procesado)[:20]) # In[14]: # def obtener_adj_pers(doc, personaje): """ Encontrar todos los adjetivos relacionados a un personaje en un `doc` :param doc: NLP documento parseado por Spacy :param personaje: un objeto String :return: lista de adjetivos relacionados a un `personaje` """ adjetivos = [] for ent in doc.ents: if ent.lemma_ == personaje: for token in ent.subtree: if token.pos_ == 'ADJ': adjetivos.append(token.lemma_) for ent in doc.ents: if ent.lemma_ == personaje: if ent.root.dep_ == 'nsubj': for child in ent.root.head.children: if child.dep_ == 'acomp': adjetivos.append(child.lemma_) return adjetivos # In[15]: # Encontrar adjetivos que describen a algún personaje. print(obtener_adj_pers(texto_procesado, "winston")) # In[16]: # def personaje_verbo(doc, verbo): """ Encontrar los personajes que utilizan determinado `verbo` en `doc` :param doc: NLP documento parseado por Spacy :param verbo: un objeto String :return: lista de personajes que utilizan `verbo` """ contar_verbo = Counter() for ent in doc.ents: if ent.label_ == 'PERSON' and ent.root.head.lemma_ == verbo: contar_verbo[ent.text] += 1 return contar_verbo.most_common(10) # In[17]: # Encontrar personajes que utilizan determinado verbo personaje_verbo(texto_procesado, "dijo") # In[18]: # Trabajando con las entidades del texto # Una entidad nombrada es cualquier objeto del mundo real como una persona, # ubicación, organización o producto con un nombre propio. # tipos de entidades del texto set(ent.label_ for ent in texto_procesado.ents) # In[19]: # Entidades nombradas de tipo ORG [ent for ent in texto_procesado.ents if ent.label_ == 'ORG'][:10] # In[20]: # Partes de la oración (POS) # En las partes de la oración se etiquetan las palabras de acuerdo a lo # que significan segun su contexto. Algunas de estas etiquetas pueden # ser: Adjetivos, verbos, adverbios, conjunciones, pronombres, sustantivos. # Etiquetas del texto set(token.pos_ for token in texto_procesado) # In[21]: # Etiquetas de tipo ADJ [token.orth_ for token in texto_procesado if token.pos_ == 'ADJ'][1:11] # In[22]: # Etiquetas de tipo PROPN [token.orth_ for token in texto_procesado if token.pos_ == 'PROPN'][1:11] # Como demuestran estos ejemplos [Textacy](http://textacy.readthedocs.io/en/latest/) / [Spacy](https://spacy.io/) son herramientas muy poderosas que nos pueden ayudar a analizar y obtener información valiosa de un texto en forma rápida y sencilla. # # # ## Deep Learning y Procesamiento del Lenguaje Natural # # Durante mucho tiempo, las técnicas principales de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) fueron dominadas por métodos de [aprendizaje automático](https://iaarbook.github.io/machine-learning/) que utilizaron [modelos lineales](https://es.wikipedia.org/wiki/Modelo_lineal) como las [máquinas de vectores de soporte](https://es.wikipedia.org/wiki/M%C3%A1quinas_de_vectores_de_soporte) o la [regresión logística](https://es.wikipedia.org/wiki/Regresi%C3%B3n_log%C3%ADstica), entrenados sobre [vectores](https://es.wikipedia.org/wiki/Vector) de características de muy alta dimensional pero muy escasos. # Recientemente, el campo ha tenido cierto éxito en el cambio hacia modelos de [deep learning](https://iaarbook.github.io/deeplearning/) sobre entradas más densas. # # Las [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) proporcionan una poderosa maquina de aprendizaje que es muy atractiva para su uso en problemas de *lenguaje natural*. Un componente importante en las [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) para el [lenguaje](https://es.wikipedia.org/wiki/Lenguaje) es el uso de una capa de [word embedding](https://en.wikipedia.org/wiki/Word_embedding), una asignación de símbolos discretos a vectores continuos en un espacio dimensional relativamente bajo. Cuando se utiliza [word embedding](https://en.wikipedia.org/wiki/Word_embedding), se transforman los distintos símbolos en objetos matemáticos sobre los que se pueden realizar operaciones. En particular, la distancia entre vectores puede equipararse a la distancia entre palabras, facilitando la generalización del comportamiento de una palabra sobre otra. # Esta representación de palabras como vectores es aprendida por la red como parte del proceso de entrenamiento. # Subiendo en la jerarquía, la red también aprende a combinar los vectores de palabras de una manera que es útil para la predicción. Esta capacidad alivia en cierta medida los problemas de dispersión de los datos. # Hay dos tipos principales de arquitecturas de [redes neuronales](https://relopezbriega.github.io/category/redes-neuronales.html) que resultan muy útiles en los problemas de [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales): las [Redes neuronales prealimentadas](https://es.wikipedia.org/wiki/Red_neuronal_prealimentada) y las [Redes neuronales recurrentes](https://en.wikipedia.org/wiki/Recurrent_neural_network). # # Para ejemplificar, veamos como podemos utilizar [word embedding](https://en.wikipedia.org/wiki/Word_embedding) utilizando el modelo `word2vec` de [Gensim](https://radimrehurek.com/gensim/intro.html). # In[23]: # # importando gensim y TSNE para graficar import gensim from sklearn.manifold import TSNE # In[24]: # transformando el texto para pasarlo al modelo de gensim texto = [[str(palabra).lower() for palabra in sent if str(palabra) not in stop ] for sent in sentencias] # generando el diccionario diccionario = gensim.corpora.Dictionary(texto) # In[25]: # creando el modelo modelo = gensim.models.Word2Vec(texto, workers=4, size=100, min_count=5, window=10, sample=1e-3) # In[26]: # representación de la palabra hermano como vector. modelo['hermano'] # In[27]: # como convertimos las palabras en vectores y los vectores capturan muchas # regularidades lingüísticas, podemos aplicar operaciones vectoriales para # extraer muchas propiedades interesantes. # palabras similares a persona modelo.most_similar_cosmul('persona') # In[28]: # por último podemos graficar el modelo vocab = list(modelo.wv.vocab) X = modelo[vocab] # aplicamos TSNE tsne = TSNE(n_components=2) X_tsne = tsne.fit_transform(X) # transformamos en DataFrame df = pd.concat([pd.DataFrame(X_tsne), pd.Series(vocab)], axis=1) df.columns = ['x', 'y', 'palabra'] # In[29]: # creamos el gráfico fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(1, 1, 1) # reducimos el dataset a 15 palabras para el ejemplo df_e = df.head(15) ax.scatter(df_e['x'], df_e['y']) for i, txt in enumerate(df_e['palabra']): ax.annotate(txt, (df_e['x'].iloc[i], df_e['y'].iloc[i])) plt.show() # Aquí termina este artículo, obviamente el [Procesamiento del Lenguaje Natural](https://es.wikipedia.org/wiki/Procesamiento_de_lenguajes_naturales) es un campo muy amplio y quedaron muchas cosas sin desarrollar. Espero que esta introducción les haya sido de utilidad. # # Saludos! # # *Este post fue escrito por [Raúl e. López Briega](https://relopezbriega.github.io/) utilizando [Jupyter notebook](https://jupyter.org/). Pueden descargar este [notebook](https://github.com/relopezbriega/relopezbriega.github.io/blob/master/downloads/NLP.ipynb) o ver su version estática en [nbviewer](https://nbviewer.ipython.org/github/relopezbriega/relopezbriega.github.io/blob/master/downloads/NLP.ipynb).*