diff --git a/docs/procesamiento-de-lenguaje-natural/bag-of-words.md b/docs/procesamiento-de-lenguaje-natural/bag-of-words.md index 98b3c4b..0a54647 100644 --- a/docs/procesamiento-de-lenguaje-natural/bag-of-words.md +++ b/docs/procesamiento-de-lenguaje-natural/bag-of-words.md @@ -21,11 +21,13 @@ Es fundamental en el análisis de texto, ya que simplifica la complejidad al tra - Permite identificar términos clave en un texto de manera sencilla. ## ❌ Limitaciones + - Este modelo no considera el significado contextual de las palabras. - Puede haber problemas con palabras homónimas. - La dimensionalidad de los vectores puede afectar la eficacia en textos largos. ## ➡️ Pasos para construir el modelo BoW + El modelado de texto con Bag of Words implica: - Tokenizar el texto. @@ -119,7 +121,7 @@ for oracion in oraciones: for palabra in frecuencias.keys(): if palabra not in diccionario: diccionario[palabra] = [] - + # Añadir 1 si la palabra está en la oración, 0 si no está diccionario[palabra].append(1 if palabra in oracion else 0) ``` @@ -131,8 +133,9 @@ for oracion in oraciones: matriz = pd.DataFrame(diccionario) print(matriz) ``` -| Oración | perro | parque | marron | corre | días | juega | encuentra | interesante | veces | árbol | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| el perro marron corre por el parque todos los dias. | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | -| el perro juega con otros perros y siempre encuentra algo interesante en el parque. | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | -| a veces el perro se sienta bajo un árbol y observa a las personas que pasan por el parque. | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | + +| Oración | perro | parque | marron | corre | días | juega | encuentra | interesante | veces | árbol | +| ------------------------------------------------------------------------------------------ | ----- | ------ | ------ | ----- | ---- | ----- | --------- | ----------- | ----- | ----- | +| el perro marron corre por el parque todos los dias. | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | +| el perro juega con otros perros y siempre encuentra algo interesante en el parque. | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | +| a veces el perro se sienta bajo un árbol y observa a las personas que pasan por el parque. | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | diff --git a/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md b/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md index 456eb40..4b71829 100644 --- a/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md +++ b/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md @@ -1,6 +1,6 @@ --- sidebar_label: "🔎 Modelo TF-IDF" -sidebar_position: 8 +sidebar_position: 9 --- # 🔎 Modelo TF-IDF @@ -15,7 +15,7 @@ Ejemplo: en la frase "Ella es hermosa", la palabra "hermosa" tendrá más import ## 🔎 Pasos para construir el modelo TF-IDF en Python: 1. Cargar e importar las librerías necesarias. -2. Preprocesar los datos (tokenizar, eliminar stopwords, convertir a minúsculas). +2. Preprocesar los datos (tokenizar, eliminar stop words, convertir a minúsculas). 3. Calcular la frecuencia de términos (TF). 4. Calcular la frecuencia inversa de documentos (IDF). 5. Combinar TF e IDF para obtener la importancia de cada palabra. diff --git a/docs/procesamiento-de-lenguaje-natural/n-grams.md b/docs/procesamiento-de-lenguaje-natural/n-grams.md index 6eb98a1..a21e9ae 100644 --- a/docs/procesamiento-de-lenguaje-natural/n-grams.md +++ b/docs/procesamiento-de-lenguaje-natural/n-grams.md @@ -1,6 +1,6 @@ --- sidebar_label: '⛓️ N-Grams' -sidebar_position: 9 +sidebar_position: 10 --- # ⛓️ N-Grams diff --git a/docs/procesamiento-de-lenguaje-natural/sentiment-analysis.md b/docs/procesamiento-de-lenguaje-natural/sentiment-analysis.md index 1b5d1b7..11d5160 100644 --- a/docs/procesamiento-de-lenguaje-natural/sentiment-analysis.md +++ b/docs/procesamiento-de-lenguaje-natural/sentiment-analysis.md @@ -1,6 +1,6 @@ --- sidebar_label: '🔎 Sentiment analysis' -sidebar_position: 10 +sidebar_position: 11 --- # 🔎 Sentiment analysis diff --git a/docs/procesamiento-de-lenguaje-natural/spacy.md b/docs/procesamiento-de-lenguaje-natural/spacy.md new file mode 100644 index 0000000..3fe932e --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/spacy.md @@ -0,0 +1,148 @@ +--- +sidebar_label: '🤖 Spacy' +sidebar_position: 16 +--- + +# 🤖 Spacy + +**spaCy** es una biblioteca de código abierto para Procesamiento del Lenguaje Natural (PLN) en Python. Ofrece herramientas avanzadas como etiquetado POS, reconocimiento de entidades nombradas (NER), análisis de dependencias, clasificación de texto, entre otras vistas. + +## 🚀 Instalación e Importación de spaCy +Antes de utilizar spaCy, es necesario instalar la biblioteca y descargar los modelos de idioma correspondientes. A continuación, se muestra cómo hacerlo: + +1. Ejecuta el siguiente comando para instalar spaCy en tu entorno de Python: + ```bash title="Instalación de python" + pip install spacy + ``` + +2. Ejecuta el siguiente comando para instalar spaCy en tu entorno de Python: + ```bash title="Descarga de un modelo de idioma" + # Español + python -m spacy download es_core_news_sm + # Inglés + python -m spacy download en_core_web_sm + ``` + +3. Después de la instalación, puedes importar y cargar un modelo de idioma: + ```python title="Importación y carga de spaCy en tu proyecto" + import spacy + # Carga del modelo de español + pln = spacy.load("es_core_news_sm") + + # Procesar texto + doc = pln("Apple está buscando comprar una compañía por $1 millón de pesos en México") + ``` + + ## ⭐ Funcionalidades principales + | Nombre | Descripción | + |-----------------------------------|--------------------------------------------------------------------| + | Tokenización | Segmentación del texto en palabras, signos de puntuación, etc. | + | Etiquetado POS | Identificación de la categoría gramatical de las palabras. | + | Análisis de dependencias | Relación sintáctica entre las palabras (sujeto, objeto, etc.). | + | Serialización | Almacenamiento de objetos en archivos o cadenas de bytes. | + | Lematización | Conversión de palabras a sus formas base. | + | Detección de límites oracionales | Segmentación en oraciones. | + | Reconocimiento de entidades (NER) | Identificación de nombres de personas, empresas, ubicaciones, etc. | + | Clasificación de texto | Asignación de categorías o etiquetas a documentos. | + | Comparación de similitud | Evaluación de similitud entre palabras, frases o documentos. | + | Entrenamiento | Mejora de modelos estadísticos personalizados. | + + +### 🔑 Tokenización +spaCy segmenta el texto en tokens (palabras, signos de puntuación, etc.) según reglas específicas del idioma. + +```python title="Tokenización" +for token in doc: + print(token.text) +``` + +| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | +|--------|------|----------|---------|-----|----------|-----|---|---|--------|----|-------| +| Apple | está | buscando | comprar | una | compañía | por | $ | 1 | millón | de | pesos | + +### 🛑 Stopwords +Las stopwords se pueden consultar y modificar dinámicamente: +```python title="Stopwords" +# Verificar stopwords +print("hola".is_stop) + +# Agregar una nueva stopword +pln.Defaults.stop_words.add("computadora") +pln.vocab["computadora"].is_stop = True + +# Eliminar una stopword +pln.Defaults.stop_words.remove("último") +pln.vocab["último"].is_stop = False +``` + +### 🏷️ Etiquetado POS +El etiquetado POS en SpaCy identifica la categoría gramatical de cada palabra dentro del texto procesado. Esto es útil para análisis lingüísticos o para extraer patrones específicos. +```python title="Parts of speech" +for token in doc: + print(f"Texto: {token.text}, POS: {token.pos_}, Etiqueta: {token.tag_}, Explicación: {spacy.explain(token.tag_)}") +``` +``` output +Texto: Apple, POS: PROPN, Etiqueta: NNP, Explicación: nombre propio singular +Texto: está, POS: AUX, Etiqueta: VBZ, Explicación: verbo auxiliar en tercera persona singular +``` + +### 🏢 Reconocimiento de Entidades Nombradas (NER) +El Reconocimiento de Entidades Nombradas (NER) identifica y clasifica entidades dentro del texto, como nombres propios, ubicaciones, fechas, cantidades, entre otros. + +```python title="Mostrar las entidades reconocidas en el texto" +for ent in doc_en.ents: + print(ent.text, ent.start_char, ent.end_char, ent.label_) +``` + +```txt title="Output" +Entidad: Apple, Tipo: ORG, Explicación: Organización +Entidad: $1 millón, Tipo: MONEY, Explicación: Cantidad de dinero +Entidad: México, Tipo: GPE, Explicación: Entidad geopolítica +``` + + +### ⚙️ Procesamiento del texto +Los tokens tienen métodos para realizar las funciones fácilmente. + +```python +cols = ["texto", "lema", "POS", "stopword"] +renglones =[] + +for t in doc: + renglones.append([t.text,t.lemma_,t.pos_,t.is_stop]) + +pd.DataFrame(renglones, columns=cols) +``` + +| Texto | Lema | POS | Tag | Stop | +|-------------|------------|-----------|----------|-------| +| Apple | apple | PROPN | NNP | False | +| está | estar | AUX | VBZ | True | +| buscando | buscar | VERB | VBG | False | +| comprar | comprar | VERB | VBG | False | +| una | uno | DET | DT | True | +| compañía | compañía | NOUN | NN | False | +| por | por | ADP | IN | True | +| $ | $ | SYM | $ | False | +| 1 | 1 | NUM | CD | False | +| millón | millón | NUM | CD | False | +| de | de | ADP | IN | True | +| pesos | peso | NOUN | NNS | False | +| en | en | ADP | IN | True | +| México | méxico | PROPN | NNP | False | + +### 📊 Visualización +SpaCy proporciona herramientas para representar visualmente la estructura de las oraciones (análisis sintáctico) con ayuda de Displacy. +```python title="Visualización de dependencias" +from spacy import displacy +# Dependencias +displacy.render(doc, style="dep", options={"distance": 80}) + +``` +![Dependencias](/img/procesamiento-de-lenguaje-natural/spacy/dep-spacy.png "dependencias") + +```python title="Visualización de las entidades nombradas" +from spacy import displacy +displacy.render(doc_en, style="ent", jupyter=True) +``` +![Entidades](/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png "entidades") diff --git a/docs/procesamiento-de-lenguaje-natural/stop-words.md b/docs/procesamiento-de-lenguaje-natural/stop-words.md index 8310616..e5d70fb 100644 --- a/docs/procesamiento-de-lenguaje-natural/stop-words.md +++ b/docs/procesamiento-de-lenguaje-natural/stop-words.md @@ -1,6 +1,6 @@ --- sidebar_label: '🛑 Stop words' -sidebar_position: 7 +sidebar_position: 8 --- # 🛑 Stop words diff --git a/docs/procesamiento-de-lenguaje-natural/text-classification.md b/docs/procesamiento-de-lenguaje-natural/text-classification.md new file mode 100644 index 0000000..5b8eecf --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/text-classification.md @@ -0,0 +1,158 @@ +--- +sidebar_label: "📊 Text Classification" +sidebar_position: 15 +--- + +# 📊 Text Classification + +## 🚩 Introducción + +La clasificación de texto es una tarea fundamental en el procesamiento del lenguaje natural, cuyo objetivo es asignar etiquetas o categorías predefinidas a fragmentos de texto. + +Este proceso permite transformar texto en categorías predefinidas, facilitando la organización, la automatización de tareas y la reducción de costos operativos. Además, impulsa la toma de decisiones informadas al identificar patrones, como el análisis de sentimientos en opiniones públicas, y mejora experiencias personalizadas en motores de búsqueda, chatbots y sistemas de recomendación. + +## 🌟 Ejemplo + +En este ejemplo, veremos cómo implementar text classification desde cero en Python. + +```python title="Importación de librerías" +import pandas as pd +from sklearn.datasets import fetch_20newsgroups +from sklearn.model_selection import train_test_split +from sklearn.svm import SVC +from sklearn.metrics import accuracy_score, classification_report +import matplotlib.pyplot as plt +from sklearn.metrics import ConfusionMatrixDisplay +``` + +```python title="Procesamiento inicial de los datos" +# Cargamos el conjunto de datos con las categorías específicas de interés (deportes, espacio y autos) +# 'subset="all"' indica que cargamos todos los datos disponibles +newsgroups = fetch_20newsgroups(subset='all', categories=['rec.sport.baseball', 'sci.space', 'rec.autos'], shuffle=True, random_state=42) + +# Mostramos el número de muestras en el conjunto de datos (el tamaño de 'target') +len(newsgroups.target) + +# Imprimimos el primer mensaje de los datos cargados +newsgroups.data[0] + +# Mostramos el primer valor de la lista de etiquetas (target) que corresponde al primer mensaje +newsgroups.target[0] + +# Asignamos las variables X e y: +# X contiene los datos (mensajes) y y contiene las etiquetas (categorías) +X = newsgroups.data +y = newsgroups.target + +# Imprimimos las longitudes de X e y para verificar que coinciden (deben tener el mismo tamaño) +print(len(X), len(y)) + +# Creamos un DataFrame de pandas para facilitar la manipulación de los datos +# Cada fila contiene un mensaje (columna 'text') y su correspondiente etiqueta (columna 'label') +df = pd.DataFrame({'text': X, 'label': y}) + +# Mostramos las primeras filas del DataFrame para verificar que se ha creado correctamente +df.head() + +# Importamos el TfidfVectorizer desde sklearn.feature_extraction.text para convertir texto a características numéricas +from sklearn.feature_extraction.text import TfidfVectorizer + +# Inicializamos el vectorizador TF-IDF, excluyendo las palabras comunes del inglés (stop_words='english') +# y configurando un umbral para excluir palabras muy frecuentes (max_df=0.7) +vectorizer = TfidfVectorizer(stop_words='english', max_df=0.7) + +# Transformamos los mensajes de texto en vectores de características (matrices dispersas) +X_vect = vectorizer.fit_transform(df['text']) + +# Labels (las etiquetas ya las tenemos en df['label'], por lo que las asignamos nuevamente a y) +y = df['label'] + +# Importamos las funciones necesarias para dividir los datos en entrenamiento y prueba y para crear el clasificador SVM +from sklearn.model_selection import train_test_split +from sklearn.svm import SVC + +# Dividimos los datos en conjuntos de entrenamiento (70%) y prueba (30%) de manera aleatoria +# Esto nos ayuda a entrenar el modelo y luego evaluarlo con datos no vistos +X_train, X_test, y_train, y_test = train_test_split(X_vect, y, test_size=0.3, random_state=42) + +# Imprimimos las formas de X_train y X_test para asegurarnos de que la división se realizó correctamente +X_train.shape # Debería ser (2079, 34505) +X_test.shape # Debería ser (892, 34505) + +# Inicializamos un clasificador SVM con el kernel 'rbf' (radial basis function), que es común para clasificación de texto +clf = SVC(kernel='rbf') + +# Entrenamos el clasificador SVM usando los datos de entrenamiento +clf.fit(X_train, y_train) + +# Mostramos el clasificador entrenado +SVC() + +# Importamos las funciones para calcular la precisión y el reporte de clasificación +from sklearn.metrics import accuracy_score, classification_report + +# Realizamos predicciones sobre el conjunto de prueba +y_pred = clf.predict(X_test) + +# Mostramos las predicciones generadas para el conjunto de prueba +y_pred +``` + +Este código realiza una clasificación de textos en categorías específicas (deportes, ciencia, automóviles) utilizando un modelo SVM con características obtenidas mediante TF-IDF. + +```python title="Mostrar los datos" +# Inicializar el vectorizador TF-IDF +# 'stop_words' elimina palabras comunes en inglés que no aportan mucha información +# 'max_df=0.7' elimina las palabras que aparecen en más del 70% de los documentos, pues probablemente no son informativas +vectorizer = TfidfVectorizer(stop_words='english', max_df=0.7) + +# Transformar los textos a vectores numéricos utilizando el vectorizador +X_vect = vectorizer.fit_transform(df['text']) + +# Visualizar la forma de la matriz resultante +# 'X_vect' es una matriz dispersa con la representación TF-IDF de las noticias +# Las filas corresponden a los textos y las columnas a las palabras del vocabulario +print(X_vect.shape) # Muestra la forma de la matriz (número de documentos, número de palabras) + +# Las etiquetas de las noticias (target) +y = df['label'] + +# Importar las herramientas necesarias para dividir el conjunto de datos en entrenamiento y prueba +from sklearn.model_selection import train_test_split + +# Dividir los datos en entrenamiento y prueba (70% entrenamiento, 30% prueba) +X_train, X_test, y_train, y_test = train_test_split(X_vect, y, test_size=0.3, random_state=42) + +# Ver la forma de las matrices de entrenamiento y prueba +print(X_train.shape) # Debería mostrar (2079, 34505) +print(X_test.shape) # Debería mostrar (892, 34505) + +# Importar el clasificador SVM (Support Vector Machine) con un kernel radial (RBF) +from sklearn.svm import SVC + +# Inicializar el clasificador SVM +clf = SVC(kernel='rbf') + +# Entrenar el clasificador con los datos de entrenamiento +clf.fit(X_train, y_train) + +# Imprimir el tipo de clasificador utilizado +print(clf) + +# Importar las métricas para evaluar el rendimiento del modelo +from sklearn.metrics import accuracy_score, classification_report + +# Predecir las etiquetas para el conjunto de prueba +y_pred = clf.predict(X_test) + +# Mostrar las predicciones del modelo +print(y_pred) # Muestra las etiquetas predichas para el conjunto de prueba + +# Calcular la exactitud del modelo comparando las etiquetas predichas con las etiquetas reales +accuracy = accuracy_score(y_test, y_pred) +print(f'Accuracy: {accuracy}') # Muestra la exactitud del modelo + +# Mostrar un reporte detallado de las métricas de clasificación: precisión, recall y F1-score +report = classification_report(y_test, y_pred) +print(report) # Muestra el reporte de clasificación +``` diff --git a/docs/procesamiento-de-lenguaje-natural/text-similarity.md b/docs/procesamiento-de-lenguaje-natural/text-similarity.md new file mode 100644 index 0000000..1feb056 --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/text-similarity.md @@ -0,0 +1,98 @@ +--- +sidebar_label: "🔍 Similitud de Textos" +sidebar_position: 14 +--- + +# 🔍 Similitud de Textos + +El análisis de similitud de textos permite medir qué tan relacionados están dos o más textos. Existen varios enfoques para realizar este análisis, desde técnicas lingüísticas basadas en modelos de lenguaje, como spaCy, hasta métodos estadísticos, como TF-IDF con cosine similarity. + +--- + +## 🛠️ Métodos de Similitud + +### 🎨 Modelos Lingüísticos con spaCy + +Los modelos de lenguaje como spaCy calculan la similitud entre textos representándolos como vectores semánticos. Esto permite capturar no solo las palabras que comparten, sino también el contexto semántico de las mismas. + +#### ❓ ¿Cómo funciona? + +SpaCy utiliza embeddings de palabras preentrenados para representar los textos como vectores en un espacio multidimensional. La similitud se calcula como el coseno del ángulo entre estos vectores. + +#### ⭐ Ejemplo + +```python +import spacy + +# Carga del modelo de lenguaje en español +nlp = spacy.load("es_core_news_sm") + +# Textos de ejemplo +doc1 = nlp("Me gustan las manzanas") +doc2 = nlp("Me gustan las naranjas") + +# Similitud entre textos completos +print("Similitud entre doc1 y doc2:", doc1.similarity(doc2)) +``` + +#### 🔎 Análisis más detallado + +Puedes calcular similitudes entre diferentes porciones del texto: + +```python title="Similitud entre secciones específicas" +# División del texto en tokens +manzana = nlp(doc1) +naranja = nlp(doc2) + +# Similitud entre documentos +print("Similitud completa:", manzana.similarity(naranja)) + +# Similitud entre un documento y un token +print("Similitud entre texto completo y token:", manzana.similarity(manzana[0])) + +# Similitud entre secciones específicas +print("Similitud entre primeras tres palabras:", manzana[:3].similarity(naranja[:3])) +``` + +--- + +### 📐 Métodos Estadísticos: TF-IDF con Cosine Similarity + +El enfoque TF-IDF (Term Frequency-Inverse Document Frequency) mide qué tan importante es una palabra en un documento en relación con un conjunto de documentos. La similitud entre documentos se calcula con cosine similarity, que evalúa la similitud entre vectores TF-IDF. + +#### ❓ ¿Cómo funciona? + +1. Se transforman los textos en vectores TF-IDF. +2. Se calcula la similitud coseno entre estos vectores. + +#### ⭐ Ejemplo + +```python title="TF-IDF con Cosine Similarity" +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.metrics.pairwise import cosine_similarity +from nltk.corpus import stopwords +import pandas as pd + +# Textos de ejemplo +docs = ["Me gustan las manzanas", "Me gustan las naranjas"] + +# Creación del vectorizador con stopwords en español +vectorizer = TfidfVectorizer(stop_words=stopwords.words('spanish')) + +# Transformación de los textos a vectores TF-IDF +vectores = vectorizer.fit_transform(docs) + +# Visualización de las características +print(vectorizer.get_feature_names_out()) + +# Matriz TF-IDF como DataFrame +print(pd.DataFrame(vectores.toarray(), columns=vectorizer.get_feature_names_out())) + +# Cálculo de la similitud coseno +print("Similitud coseno: +", cosine_similarity(vectores)) +``` + +## 🚀 Conclusión + +Ambos métodos, spaCy y TF-IDF con Cosine Similarity, son herramientas poderosas para realizar análisis de similitud de textos, y la elección entre ellos depende de los objetivos específicos del análisis. Si necesitas captar relaciones semánticas profundas, opta por spaCy; para análisis rápidos basados en términos, TF-IDF es una opción eficaz. diff --git a/docs/procesamiento-de-lenguaje-natural/text-summarization.md b/docs/procesamiento-de-lenguaje-natural/text-summarization.md index ba29952..ebdd82d 100644 --- a/docs/procesamiento-de-lenguaje-natural/text-summarization.md +++ b/docs/procesamiento-de-lenguaje-natural/text-summarization.md @@ -1,6 +1,6 @@ --- sidebar_label: "📝 Text Summarization" -sidebar_position: 11 +sidebar_position: 12 --- # 📝 Text Summarization diff --git a/docs/procesamiento-de-lenguaje-natural/word-cloud.md b/docs/procesamiento-de-lenguaje-natural/word-cloud.md index ccea1ee..e4dd1a2 100644 --- a/docs/procesamiento-de-lenguaje-natural/word-cloud.md +++ b/docs/procesamiento-de-lenguaje-natural/word-cloud.md @@ -1,6 +1,6 @@ --- sidebar_label: "☁️ Word cloud" -sidebar_position: 12 +sidebar_position: 13 --- # ☁️ Word cloud diff --git a/static/img/procesamiento-de-lenguaje-natural/spacy/dep-spacy.png b/static/img/procesamiento-de-lenguaje-natural/spacy/dep-spacy.png new file mode 100644 index 0000000..27517d3 Binary files /dev/null and b/static/img/procesamiento-de-lenguaje-natural/spacy/dep-spacy.png differ diff --git a/static/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png b/static/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png new file mode 100644 index 0000000..f228762 Binary files /dev/null and b/static/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png differ