From e56efa1fe2581911939027eb07588e1dcd3fcb2d Mon Sep 17 00:00:00 2001 From: Ximena58 <125088460+MenaRz58@users.noreply.github.com> Date: Mon, 21 Oct 2024 20:13:48 -0600 Subject: [PATCH 1/9] Update BoW.md --- docs/procesamiento-de-lenguaje-natural/BoW.md | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/docs/procesamiento-de-lenguaje-natural/BoW.md b/docs/procesamiento-de-lenguaje-natural/BoW.md index 7b9cc79..ccdac72 100644 --- a/docs/procesamiento-de-lenguaje-natural/BoW.md +++ b/docs/procesamiento-de-lenguaje-natural/BoW.md @@ -5,28 +5,13 @@ sidebar_position: 7 # 💰 Modelo Bag of Words -Es un método que se utiliza para representar documentos como un conjunto de palabras, ignorando el orden y la estructura, centrándose solo en la presencia de palabras. +Es un método que se utiliza para representar documentos como un conjunto de palabras, ignorando el orden y la estructura, enfocandose solo en la presencia de palabras. Con él se obtiene un vector numérico que representa la frecuencia de términos en el documento, permitiendo analizar la similitud entre distintos textos, por ejemplo, la clasificación de correos como spam según el conteo de palabras relevantes. ## ⭐ Importancia del modelado de texto Es fundamental en el análisis de texto, ya que simplifica la complejidad al tratar el texto como una colección de palabras únicas, permitiendo la comparación y clasificación de documentos. -## 📝 Detalles del proceso - -El modelado de texto con Bag of Words implica: - -- Tokenizar el texto. -- Construir un vocabulario. -- Representar el texto como un vector de frecuencias de palabras. -- Aplicar técnicas de NLP y aprendizaje automático. - -## 🔧 Ejemplo práctico - -**Resultados esperados**: se obtiene un vector numérico que representa la presencia de términos en el documento, permitiendo analizar la similitud entre distintos textos. - -**Aplicación en la industria**: un ejemplo real sería la clasificación de correos electrónicos como spam o no spam mediante el conteo de palabras clave utilizando el modelo Bag of Words. - -## ✅ Ventajas del modelo Bag of Words +## ✅ Ventajas - Permite una fácil implementación y comprensión. - Es eficiente para grandes conjuntos de datos textuales. @@ -42,6 +27,12 @@ El modelado de texto con Bag of Words implica: - 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. +- Construir un vocabulario. +- Representar el texto como un vector de frecuencias de palabras. +- Aplicar técnicas de NLP y aprendizaje automático. ### Paso 1: Recopilar y preparar los datos From 1f40a7e445e96f3f4c114904b92615abea95e49c Mon Sep 17 00:00:00 2001 From: Ximena58 <125088460+MenaRz58@users.noreply.github.com> Date: Mon, 21 Oct 2024 20:21:08 -0600 Subject: [PATCH 2/9] arreglo pasos --- docs/procesamiento-de-lenguaje-natural/BoW.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/procesamiento-de-lenguaje-natural/BoW.md b/docs/procesamiento-de-lenguaje-natural/BoW.md index ccdac72..d7d1990 100644 --- a/docs/procesamiento-de-lenguaje-natural/BoW.md +++ b/docs/procesamiento-de-lenguaje-natural/BoW.md @@ -21,7 +21,6 @@ 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. @@ -60,7 +59,6 @@ texto = re.sub(r"\s+", " ", oracion) ### Paso 2: Tokenización - Divide el texto en palabras individuales (tokens). -- Elimina palabras vacías (stopwords). ```python # Librería necesaria From d01e43f455b4933cc180e7fc6c70a0ef91db1305 Mon Sep 17 00:00:00 2001 From: Ximena58 <125088460+MenaRz58@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:11:04 -0600 Subject: [PATCH 3/9] spacy --- .../spacy.md | 147 ++++++++++++++++++ .../spacy/dep-spacy.png | Bin 0 -> 41349 bytes .../spacy/ent-spacy.png | Bin 0 -> 6286 bytes 3 files changed, 147 insertions(+) create mode 100644 docs/procesamiento-de-lenguaje-natural/spacy.md create mode 100644 static/img/procesamiento-de-lenguaje-natural/spacy/dep-spacy.png create mode 100644 static/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png diff --git a/docs/procesamiento-de-lenguaje-natural/spacy.md b/docs/procesamiento-de-lenguaje-natural/spacy.md new file mode 100644 index 0000000..a9f0e89 --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/spacy.md @@ -0,0 +1,147 @@ +--- +sidebar_label: '🤖 Spacy' +sidebar_position: 8 +--- + +# 🤖 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_) +``` +```yaml 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") \ No newline at end of file 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 0000000000000000000000000000000000000000..27517d352c3d7b6c8c610d4ce841e71f43c21d06 GIT binary patch literal 41349 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 S7@kB|ct=iEHv85+um20ZfII5| literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f228762224928bdc3ed06c9c87bf82ad81ec7f87 GIT binary patch literal 6286 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 Date: Fri, 22 Nov 2024 20:12:59 -0600 Subject: [PATCH 4/9] feat: adds similarity --- .../similarity.md | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 docs/procesamiento-de-lenguaje-natural/similarity.md diff --git a/docs/procesamiento-de-lenguaje-natural/similarity.md b/docs/procesamiento-de-lenguaje-natural/similarity.md new file mode 100644 index 0000000..d0a3b17 --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/similarity.md @@ -0,0 +1,98 @@ +--- +sidebar_label: "🔍 Similitud de Textos" +sidebar_position: 13 +--- + +# 🔍 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 + +### 1. **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 +# 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])) +``` + +--- + +### 2. **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 +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. From e922bfc0da891b5878d133238e1295274f627915 Mon Sep 17 00:00:00 2001 From: itaquito Date: Fri, 22 Nov 2024 20:27:08 -0600 Subject: [PATCH 5/9] fix: Bag of words file --- .../{BoW.md => bag-of-words.md} | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) rename docs/procesamiento-de-lenguaje-natural/{BoW.md => bag-of-words.md} (77%) diff --git a/docs/procesamiento-de-lenguaje-natural/BoW.md b/docs/procesamiento-de-lenguaje-natural/bag-of-words.md similarity index 77% rename from docs/procesamiento-de-lenguaje-natural/BoW.md rename to docs/procesamiento-de-lenguaje-natural/bag-of-words.md index d7d1990..0a54647 100644 --- a/docs/procesamiento-de-lenguaje-natural/BoW.md +++ b/docs/procesamiento-de-lenguaje-natural/bag-of-words.md @@ -1,11 +1,11 @@ --- -sidebar_label: '💰 Bag of Words' +sidebar_label: '💰 Bag of words' sidebar_position: 7 --- -# 💰 Modelo Bag of Words +# 💰 Modelo Bag of Words -Es un método que se utiliza para representar documentos como un conjunto de palabras, ignorando el orden y la estructura, enfocandose solo en la presencia de palabras. Con él se obtiene un vector numérico que representa la frecuencia de términos en el documento, permitiendo analizar la similitud entre distintos textos, por ejemplo, la clasificación de correos como spam según el conteo de palabras relevantes. +Es un método que se utiliza para representar documentos como un conjunto de palabras, ignorando el orden y la estructura, enfocandose solo en la presencia de palabras. Con él se obtiene un vector numérico que representa la frecuencia de términos en el documento, permitiendo analizar la similitud entre distintos textos, por ejemplo, la clasificación de correos como spam según el conteo de palabras relevantes. ## ⭐ Importancia del modelado de texto @@ -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. @@ -38,7 +40,7 @@ El modelado de texto con Bag of Words implica: - Reúne el texto que deseas analizar. - Limpia el texto eliminando caracteres especiales, convirtiendo a minúsculas, etc. -```python +```python title="Preparación de texto" texto = '''El perro marron corre por el parque todos los dias. El perro juega con otros perros y siempre encuentra algo interesante en el parque. A veces, el perro se sienta bajo un árbol y observa a las personas que pasan por el parque.''' # Convertir el texto a minúsculas @@ -60,7 +62,7 @@ texto = re.sub(r"\s+", " ", oracion) - Divide el texto en palabras individuales (tokens). -```python +```python title="Tokenización del texto" # Librería necesaria import nltk # Tokenizar la oración @@ -69,7 +71,7 @@ palabras = nltk.word_tokenize(texto) ### Paso 3: Eliminar palabras vacías (stopwords) -```python +```python title="Eliminar palabras vacías" # Librería necesaria from nltk.corpus import stopwords @@ -78,36 +80,33 @@ stop_words = stopwords.words("spanish") # Filtrar palabras vacías del texto palabras = [w for w in palabras if w not in stop_words] - ``` ### Paso 4: Construir el vocabulario - Crea una lista de todas las palabras únicas en el corpus. -```python +```python title="Construir el vocabulario" # Construir el vocabulario guardando una vez cada palabra restante vocabulario = set(palabras) - ``` ### Paso 5: Contar las frecuencias - Cuenta cuántas veces aparece cada palabra en cada documento. -```python +```python title="Crear las frecuencias" from collections import Counter # Obtener las frecuencias de las palabras frecuencias = dict(Counter(palabras)) - ``` ### Paso 6: Crear el vector BoW -- Representa cada documento como un vector de frecuencias de palabras. +- Representa cada documento como un vector de frecuencias de palabras. -```python +```python title="Crear el vector BoW" # Crear un diccionario para la matriz diccionario = {} @@ -122,21 +121,21 @@ 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) - ``` ### Extra: Visualizar en tabla -```python +```python title="Visualizar la matriz BoW" # Convertir el diccionario a un DataFrame 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 | From e0a8e55a778cbb70001c8a26f8a5662ca7a81ff7 Mon Sep 17 00:00:00 2001 From: DarthMayan <0250015@up.edu.mx> Date: Fri, 22 Nov 2024 21:10:04 -0600 Subject: [PATCH 6/9] commit Text Clasification --- .../stop-words.md | 2 +- .../text_clasification.md | 158 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 docs/procesamiento-de-lenguaje-natural/text_clasification.md diff --git a/docs/procesamiento-de-lenguaje-natural/stop-words.md b/docs/procesamiento-de-lenguaje-natural/stop-words.md index 5270685..775ae6a 100644 --- a/docs/procesamiento-de-lenguaje-natural/stop-words.md +++ b/docs/procesamiento-de-lenguaje-natural/stop-words.md @@ -1,5 +1,5 @@ --- -sidebar_label: '🛑 Stop Words' +sidebar_label: "🛑 Stop Words" sidebar_position: 5 --- diff --git a/docs/procesamiento-de-lenguaje-natural/text_clasification.md b/docs/procesamiento-de-lenguaje-natural/text_clasification.md new file mode 100644 index 0000000..4526b8a --- /dev/null +++ b/docs/procesamiento-de-lenguaje-natural/text_clasification.md @@ -0,0 +1,158 @@ +--- +sidebar_label: "📊 Text Clasification" +sidebar_position: 10 +--- + +# 📊 Text Clasification + +## 🌟 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 clasification 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 +``` From 50e6315d6ee06887f472a587beb8d2deaaea0e56 Mon Sep 17 00:00:00 2001 From: itaquito Date: Fri, 22 Nov 2024 22:02:33 -0600 Subject: [PATCH 7/9] fix: Fix order of pages in NLP --- docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md | 2 +- docs/procesamiento-de-lenguaje-natural/n-grams.md | 2 +- .../sentiment-analysis.md | 2 +- docs/procesamiento-de-lenguaje-natural/spacy.md | 4 ++-- docs/procesamiento-de-lenguaje-natural/stop-words.md | 2 +- .../{text_clasification.md => text-classification.md} | 8 ++++---- .../{similarity.md => text-similarity.md} | 2 +- .../text-summarization.md | 2 +- docs/procesamiento-de-lenguaje-natural/word-cloud.md | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) rename docs/procesamiento-de-lenguaje-natural/{text_clasification.md => text-classification.md} (97%) rename docs/procesamiento-de-lenguaje-natural/{similarity.md => text-similarity.md} (99%) diff --git a/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md b/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md index 456eb40..ba36b39 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 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 index a9f0e89..b790ec2 100644 --- a/docs/procesamiento-de-lenguaje-natural/spacy.md +++ b/docs/procesamiento-de-lenguaje-natural/spacy.md @@ -1,6 +1,6 @@ --- sidebar_label: '🤖 Spacy' -sidebar_position: 8 +sidebar_position: 16 --- # 🤖 Spacy @@ -144,4 +144,4 @@ displacy.render(doc, style="dep", options={"distance": 80}) from spacy import displacy displacy.render(doc_en, style="ent", jupyter=True) ``` -![Entidades](/img/procesamiento-de-lenguaje-natural/spacy/ent-spacy.png "entidades") \ No newline at end of file +![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_clasification.md b/docs/procesamiento-de-lenguaje-natural/text-classification.md similarity index 97% rename from docs/procesamiento-de-lenguaje-natural/text_clasification.md rename to docs/procesamiento-de-lenguaje-natural/text-classification.md index 4526b8a..d1d00bb 100644 --- a/docs/procesamiento-de-lenguaje-natural/text_clasification.md +++ b/docs/procesamiento-de-lenguaje-natural/text-classification.md @@ -1,9 +1,9 @@ --- -sidebar_label: "📊 Text Clasification" -sidebar_position: 10 +sidebar_label: "📊 Text Classification" +sidebar_position: 15 --- -# 📊 Text Clasification +# 📊 Text Classification ## 🌟 Introducción @@ -13,7 +13,7 @@ Este proceso permite transformar texto en categorías predefinidas, facilitando ## 🌟 Ejemplo: -En este ejemplo, veremos cómo implementar text clasification desde cero en Python. +En este ejemplo, veremos cómo implementar text classification desde cero en Python. ```python title="Importación de librerías" import pandas as pd diff --git a/docs/procesamiento-de-lenguaje-natural/similarity.md b/docs/procesamiento-de-lenguaje-natural/text-similarity.md similarity index 99% rename from docs/procesamiento-de-lenguaje-natural/similarity.md rename to docs/procesamiento-de-lenguaje-natural/text-similarity.md index d0a3b17..708452e 100644 --- a/docs/procesamiento-de-lenguaje-natural/similarity.md +++ b/docs/procesamiento-de-lenguaje-natural/text-similarity.md @@ -1,6 +1,6 @@ --- sidebar_label: "🔍 Similitud de Textos" -sidebar_position: 13 +sidebar_position: 14 --- # 🔍 Similitud de Textos 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 From 80894db00d57649386d84a93e23b0b3e4118ca2e Mon Sep 17 00:00:00 2001 From: itaquito Date: Fri, 22 Nov 2024 22:06:31 -0600 Subject: [PATCH 8/9] fix: Adds title to code blocks and fixes some tables --- .../modelo-tf-idf.md | 2 +- .../spacy.md | 73 ++++++++++--------- .../text-similarity.md | 4 +- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md b/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md index ba36b39..4b71829 100644 --- a/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md +++ b/docs/procesamiento-de-lenguaje-natural/modelo-tf-idf.md @@ -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/spacy.md b/docs/procesamiento-de-lenguaje-natural/spacy.md index b790ec2..3fe932e 100644 --- a/docs/procesamiento-de-lenguaje-natural/spacy.md +++ b/docs/procesamiento-de-lenguaje-natural/spacy.md @@ -5,47 +5,47 @@ 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 . +**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 -``` + ```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 -``` + ```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. | + ```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 @@ -56,9 +56,9 @@ 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 | +| 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: @@ -93,7 +93,8 @@ El Reconocimiento de Entidades Nombradas (NER) identifica y clasifica entidades for ent in doc_en.ents: print(ent.text, ent.start_char, ent.end_char, ent.label_) ``` -```yaml title="output" + +```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 diff --git a/docs/procesamiento-de-lenguaje-natural/text-similarity.md b/docs/procesamiento-de-lenguaje-natural/text-similarity.md index 708452e..7a8f01f 100644 --- a/docs/procesamiento-de-lenguaje-natural/text-similarity.md +++ b/docs/procesamiento-de-lenguaje-natural/text-similarity.md @@ -39,7 +39,7 @@ print("Similitud entre doc1 y doc2:", doc1.similarity(doc2)) Puedes calcular similitudes entre diferentes porciones del texto: -```python +```python title="Similitud entre secciones específicas" # División del texto en tokens manzana = nlp(doc1) naranja = nlp(doc2) @@ -67,7 +67,7 @@ El enfoque TF-IDF (Term Frequency-Inverse Document Frequency) mide qué tan impo #### Ejemplo: -```python +```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 From 4f49eeb90675b38fb038f53946be7f76b8c98f37 Mon Sep 17 00:00:00 2001 From: itaquito Date: Fri, 22 Nov 2024 22:09:50 -0600 Subject: [PATCH 9/9] fix: Adds emojis to missing pages --- .../text-classification.md | 4 ++-- .../text-similarity.md | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/procesamiento-de-lenguaje-natural/text-classification.md b/docs/procesamiento-de-lenguaje-natural/text-classification.md index d1d00bb..5b8eecf 100644 --- a/docs/procesamiento-de-lenguaje-natural/text-classification.md +++ b/docs/procesamiento-de-lenguaje-natural/text-classification.md @@ -5,13 +5,13 @@ sidebar_position: 15 # 📊 Text Classification -## 🌟 Introducción +## 🚩 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: +## 🌟 Ejemplo En este ejemplo, veremos cómo implementar text classification desde cero en Python. diff --git a/docs/procesamiento-de-lenguaje-natural/text-similarity.md b/docs/procesamiento-de-lenguaje-natural/text-similarity.md index 7a8f01f..1feb056 100644 --- a/docs/procesamiento-de-lenguaje-natural/text-similarity.md +++ b/docs/procesamiento-de-lenguaje-natural/text-similarity.md @@ -11,15 +11,15 @@ El análisis de similitud de textos permite medir qué tan relacionados están d ## 🛠️ Métodos de Similitud -### 1. **Modelos Lingüísticos con spaCy** +### 🎨 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? +#### ❓ ¿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: +#### ⭐ Ejemplo ```python import spacy @@ -35,7 +35,7 @@ doc2 = nlp("Me gustan las naranjas") print("Similitud entre doc1 y doc2:", doc1.similarity(doc2)) ``` -#### Análisis más detallado: +#### 🔎 Análisis más detallado Puedes calcular similitudes entre diferentes porciones del texto: @@ -56,16 +56,16 @@ print("Similitud entre primeras tres palabras:", manzana[:3].similarity(naranja[ --- -### 2. **Métodos Estadísticos: TF-IDF con Cosine Similarity** +### 📐 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? +#### ❓ ¿Cómo funciona? 1. Se transforman los textos en vectores TF-IDF. 2. Se calcula la similitud coseno entre estos vectores. -#### Ejemplo: +#### ⭐ Ejemplo ```python title="TF-IDF con Cosine Similarity" from sklearn.feature_extraction.text import TfidfVectorizer