{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 🔧 Notebook 02 — Preprocesamiento de Texto\n", "\n", "Construimos y validamos el pipeline de limpieza de texto **paso a paso**.\n", "\n", "El texto crudo de YouTube tiene ruido que engaña al modelo: URLs, menciones, caracteres raros (`\\xa0`), contracciones rotas (`don t`).\n", "Antes de vectorizar necesitamos texto limpio y normalizado.\n", "\n", "### Herramientas\n", "- **`re`** → expresiones regulares para limpiar ruido estructural\n", "- **`NLTK`** → lista curada de 179 stopwords en inglés\n", "- **`spaCy`** → lematización con modelo de lenguaje real `en_core_web_sm`\n", "- **`MLflow`** → registrar qué configuración de preprocesamiento usamos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Imports y configuración\n", "\n", "Cargamos todo desde el YAML. Ningún valor hardcodeado en el notebook." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PROJECT_ROOT : /mnt/c/Users/under/Documents/F5/3_Projects/Project_9_Equipo3/Project_YT\n", "Python : 3.10.20\n" ] } ], "source": [ "import re\n", "import sys\n", "import yaml\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import mlflow\n", "import nltk\n", "import spacy\n", "from nltk.corpus import stopwords\n", "from pathlib import Path\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Ruta raiz — \n", "PROJECT_ROOT = Path.cwd().parent\n", "sys.path.insert(0, str(PROJECT_ROOT))\n", "\n", "plt.rcParams['figure.figsize'] = (12, 5)\n", "plt.rcParams['axes.spines.top'] = False\n", "plt.rcParams['axes.spines.right'] = False\n", "\n", "print(f'PROJECT_ROOT : {PROJECT_ROOT}')\n", "print(f'Python : {sys.version.split()[0]}')" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Configuracion de preprocesamiento:\n", " lowercase: True\n", " remove_urls: True\n", " remove_mentions: True\n", " remove_emojis: True\n", " remove_special_chars: True\n", " remove_stopwords: True\n", " lemmatize: True\n", " min_token_length: 2\n", " language: en\n", " custom_stopwords: ['youtube', 'video', 'watch', 'like', 'comment', 'channel', 'stefan', 'peggy', 'masri', 'ferguson', 'cigar', 'hubbard']\n" ] } ], "source": [ "# Carga de configuracion desde YAML\n", "CONFIG_PATH = PROJECT_ROOT / 'configs' / 'features.yaml'\n", "\n", "with open(CONFIG_PATH) as f:\n", " config = yaml.safe_load(f)\n", "\n", "prep_cfg = config['preprocessing']\n", "print('Configuracion de preprocesamiento:')\n", "for k, v in prep_cfg.items():\n", " print(f' {k}: {v}')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "spaCy : core_web_sm v3.8.0\n", "NLTK : 198 stopwords en ingles\n" ] } ], "source": [ "# Descargar recursos NLTK\n", "nltk.download('stopwords', quiet=True)\n", "nltk.download('punkt', quiet=True)\n", "\n", "# Cargar modelo spaCy\n", "# Si no lo tienes instalado: python -m spacy download en_core_web_sm\n", "nlp = spacy.load('en_core_web_sm', disable=['parser', 'ner'])\n", "\n", "print(f\"spaCy : {nlp.meta['name']} v{nlp.meta['version']}\")\n", "print(f'NLTK : {len(stopwords.words(\"english\"))} stopwords en ingles')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Carga de datos\n", "\n", "Las rutas vienen del YAML de pipeline, no se escriben a mano." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Dataset : (1000, 15)\n", "Texto : \"Text\"\n", "Target : \"IsToxic\"\n", "Sublabels: ['IsAbusive', 'IsProvocative', 'IsHatespeech', 'IsRacist', 'IsObscene']\n", "\n", "Muestra texto crudo:\n", " -> \"You call yourself an anarchist but defend a cop shooting an unarmed civilian. I'm highly disappointe\"\n", " -> 'My mother told me the same thing.\\xa0 God Bless this woman.'\n", " -> 'Love it I same the saem thing Go Peggy! #stupid \\xa0\\nYa Killing ya selves more quicker than\\xa0 STLPD cou'\n" ] } ], "source": [ "PIPELINE_PATH = PROJECT_ROOT / 'configs' / 'pipeline.yaml'\n", "\n", "with open(PIPELINE_PATH) as f:\n", " pipeline_cfg = yaml.safe_load(f)\n", "\n", "DATA_PATH = PROJECT_ROOT / pipeline_cfg['data']['raw_path']\n", "TEXT_COL = pipeline_cfg['data']['text_column']\n", "TARGET_BIN = pipeline_cfg['data']['target_binary']\n", "SUBLABELS = pipeline_cfg['data']['target_multilabel']\n", "\n", "df = pd.read_csv(DATA_PATH)\n", "print(f'Dataset : {df.shape}')\n", "print(f'Texto : \"{TEXT_COL}\"')\n", "print(f'Target : \"{TARGET_BIN}\"')\n", "print(f'Sublabels: {SUBLABELS}')\n", "print()\n", "print('Muestra texto crudo:')\n", "for t in df[TEXT_COL].sample(3, random_state=42):\n", " print(f' -> {repr(t[:100])}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Pipeline paso a paso\n", "\n", "Construimos cada función por separado para entender su efecto antes de encadenarlas.\n", "\n", "```\n", "texto raw\n", " → [1] lowercase\n", " → [2] regex: URLs, @menciones, \\xa0, contracciones rotas, números\n", " → [3] spaCy: tokenización + lematización\n", " → [4] NLTK: filtrado stopwords + tokens cortos + puntuación\n", " → texto limpio\n", "```" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PASO 1 — Lowercase\n", "-----------------------------------------------------------------\n", " ANTES : Stephan, Thank you for the video. It takes all the available\n", " DESPUES: stephan, thank you for the video. it takes all the available\n", "\n", " ANTES : Body cams should also air what the police run into day to da\n", " DESPUES: body cams should also air what the police run into day to da\n", "\n", " ANTES : This is a really sad story. Someone is killed for no reason,\n", " DESPUES: this is a really sad story. someone is killed for no reason,\n", "\n" ] } ], "source": [ "# ── PASO 1: Lowercase ──\n", "# Por que: 'BLACK' y 'black' son la misma palabra.\n", "# Sin esto el modelo aprende 'BLACK' y 'black' como features distintas.\n", "\n", "def to_lowercase(text: str) -> str:\n", " \"\"\"Convierte el texto a minusculas.\"\"\"\n", " return str(text).lower()\n", "\n", "# Validacion visual\n", "print('PASO 1 — Lowercase')\n", "print('-' * 65)\n", "for ex in df[TEXT_COL].sample(3, random_state=1).tolist():\n", " print(f' ANTES : {ex[:60]}')\n", " print(f' DESPUES: {to_lowercase(ex)[:60]}')\n", " print()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PASO 2 — Limpieza Regex\n", "-----------------------------------------------------------------\n", " ANTES : 'Check this out http://youtube.com/watch?v=abc123 ok?'\n", " DESPUES: 'Check this out ok?'\n", "\n", " ANTES : \"Hey @username you're so stupid\\xa0\\xa0 really\"\n", " DESPUES: 'Hey youre so stupid really'\n", "\n", " ANTES : 'dont\\nyou see?\\n\\nThis is wrong!!!'\n", " DESPUES: 'dont you see? This is wrong!!!'\n", "\n", " ANTES : 'He has 100 guns and 50 knives'\n", " DESPUES: 'He has guns and knives'\n", "\n" ] } ], "source": [ "# ── PASO 2: Limpieza con Regex ────────────────────────────────────────────\n", "# Por que regex: hay ruido sistematico en comentarios de YouTube.\n", "# El EDA mostro: \\xa0 embebidos, saltos de linea, URLs, @menciones.\n", "\n", "\n", "def clean_regex(text: str) -> str:\n", " \"\"\"Limpieza con expresiones regulares.\"\"\"\n", " # URLs completas\n", " text = re.sub(r'http\\S+|www\\.\\S+', '', text)\n", " # Menciones @usuario\n", " text = re.sub(r'@\\w+', '', text)\n", " # Saltos de linea y tabulaciones -> espacio\n", " text = re.sub(r'[\\n\\t\\r]', ' ', text)\n", " # Caracteres no-ASCII: \\xa0, emojis, caracteres especiales\n", " text = re.sub(r'[^\\x00-\\x7F]+', ' ', text)\n", " # Apostrofes: \"don't\" -> \"dont\", \"it's\" -> \"its\"\n", " text = re.sub(r\"'\", '', text)\n", " # Numeros solos sin contexto lexico util\n", " text = re.sub(r'\\b\\d+\\b', '', text)\n", " # Espacios multiples -> uno solo\n", " text = re.sub(r'\\s+', ' ', text)\n", " return text.strip()\n", "\n", "# Validacion con casos problematicos reales del dataset (ejemplo)\n", "print('PASO 2 — Limpieza Regex')\n", "print('-' * 65)\n", "test_cases = [\n", " 'Check this out http://youtube.com/watch?v=abc123 ok?',\n", " 'Hey @username you\\'re so stupid\\xa0\\xa0 really',\n", " 'dont\\nyou see?\\n\\nThis is wrong!!!',\n", " 'He has 100 guns and 50 knives',\n", "]\n", "for tc in test_cases:\n", " print(f' ANTES : {repr(tc)}')\n", " print(f' DESPUES: {repr(clean_regex(tc))}')\n", " print()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PASO 3+4 — Lematizacion (spaCy) + Filtrado (NLTK)\n", "-----------------------------------------------------------------\n", " ANTES : black people are being killed by cops\n", " DESPUES: black people kill cop\n", "\n", " ANTES : you are so stupid and racist thug\n", " DESPUES: stupid racist thug\n", "\n", " ANTES : running faster than the police officers\n", " DESPUES: run fast police officer\n", "\n", " ANTES : these cops are criminals and killers\n", " DESPUES: cop criminal killer\n", "\n", " ANTES : Hi, stefan\n", " DESPUES: hi\n", "\n" ] } ], "source": [ "# ── PASO 3 + 4: Lematizacion con spaCy + filtrado con NLTK ───────────────\n", "#\n", "# Por que spaCy para LEMATIZAR:\n", "# Conoce gramatica inglesa real. // 'running' -> 'run' | 'cops' -> 'cop' | 'better' -> 'good'\n", "\n", "# Por que NLTK para STOPWORDS:\n", "# Lista curada de 179 palabras funcionales (the, is, at, which...)\n", "# Mas explicita y facil de personalizar que la lista interna de spaCy\n", "\n", "\n", "STOP_WORDS = set(stopwords.words('english'))\n", "\n", "# Stopwords custom: palabras tematicas sin valor discriminante\n", "CUSTOM_STOPWORDS = set(config['preprocessing']['custom_stopwords'])\n", "STOP_WORDS = STOP_WORDS | CUSTOM_STOPWORDS\n", "\n", "MIN_TOKEN_LEN = prep_cfg.get('min_token_length', 2)\n", "\n", "def lemmatize_and_filter(text: str) -> str:\n", " \"\"\"Lematiza con spaCy y filtra stopwords con NLTK.\"\"\"\n", " doc = nlp(text) # Separación de palabras\n", " tokens = [\n", " token.lemma_\n", " for token in doc\n", " if not token.is_punct # sin puntuacion\n", " and not token.is_space # sin espacios\n", " and len(token.text) >= MIN_TOKEN_LEN # tokens de al menos 2 chars\n", " and token.lemma_ not in STOP_WORDS # sin stopwords\n", " ]\n", " return ' '.join(tokens)\n", "\n", "\n", "# Validacion: ver exactamente que hace la lematizacion (ejemplo)\n", "print('PASO 3+4 — Lematizacion (spaCy) + Filtrado (NLTK)')\n", "print('-' * 65)\n", "test_texts = [\n", " 'black people are being killed by cops',\n", " 'you are so stupid and racist thug',\n", " 'running faster than the police officers',\n", " 'these cops are criminals and killers',\n", " 'Hi, stefan'\n", "]\n", "for tt in test_texts:\n", " result = lemmatize_and_filter(tt)\n", " print(f' ANTES : {tt}')\n", " print(f' DESPUES: {result}')\n", " print()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PIPELINE COMPLETO — Ejemplos reales del dataset\n", "======================================================================\n", "[NO TOXICO ]\n", " CRUDO : You call yourself an anarchist but defend a cop shooting an unarmed civilian. I'm highly disappointe\n", " LIMPIO : call anarchist defend cop shoot unarmed civilian highly disappointed beginning refer square know blu\n", "\n", "[NO TOXICO ]\n", " CRUDO : My mother told me the same thing.  God Bless this woman.\n", " LIMPIO : mother tell I thing god bless woman\n", "\n", "[NO TOXICO ]\n", " CRUDO : Love it I same the saem thing Go Peggy! #stupid  \n", "Ya Killing ya selves more quicker than  STLPD cou\n", " LIMPIO : love saem thing go stupid ya kill ya self quick stlpd could ever wakeup\n", "\n", "[TOXICO ]\n", " CRUDO : Next time they do that, line up some cars and start making burnout smoke to \"riot gas\" them. Non let\n", " LIMPIO : next time line car start make burnout smoke riot gas non lethal eficient double check actualy non le\n", "\n", "[NO TOXICO ]\n", " CRUDO : He was Robbing the Store and Being a Big Man .\n", "If you Play with Fire you Will Get Burnt .?\n", "The Polic\n", " LIMPIO : rob store big man play fire get burn police job\n", "\n", "[NO TOXICO ]\n", " CRUDO : The crazy thing is I thought offices never do anything I want of random people over\n", " LIMPIO : crazy thing think office never anything want random people\n", "\n" ] } ], "source": [ "# ── PIPELINE COMPLETO ──\n", "\n", "def preprocess_text(text: str) -> str:\n", " \"\"\"\n", " Pipeline completo de preprocesamiento NLP.\n", "\n", " Pasos:\n", " 1. lowercase\n", " 2. limpieza regex (URLs, menciones, chars especiales)\n", " 3. lematizacion con spaCy\n", " 4. filtrado stopwords con NLTK\n", "\n", " \"\"\"\n", " text = to_lowercase(text)\n", " text = clean_regex(text)\n", " text = lemmatize_and_filter(text)\n", " return text\n", "\n", "# Verificacion con ejemplos reales del dataset\n", "print('PIPELINE COMPLETO — Ejemplos reales del dataset')\n", "print('=' * 70)\n", "for _, row in df.sample(6, random_state=42).iterrows():\n", " label = 'TOXICO ' if row[TARGET_BIN] else 'NO TOXICO '\n", " print(f'[{label}]')\n", " print(f' CRUDO : {row[TEXT_COL][:100]}')\n", " print(f' LIMPIO : {preprocess_text(row[TEXT_COL])[:100]}')\n", " print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Aplicar el pipeline al dataset completo\n", "\n", "Procesamos las 1000 filas y validamos que no haya pérdida de información crítica." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Procesando dataset completo...\n", "Completado: 1000 comentarios procesados\n" ] } ], "source": [ "print('Procesando dataset completo...')\n", "df['clean_text'] = df[TEXT_COL].apply(preprocess_text)\n", "print(f'Completado: {len(df)} comentarios procesados')" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " CRUDO LIMPIO REDUCCION\n", "----------------------------------------------------\n", " mean 33.8 16.5 51.2%\n", " median 19.0 9.0 52.6%\n", " min 1.0 1.0 0.0%\n", " max 815.0 373.0 54.2%\n", "\n", "Comentarios vacios tras limpieza : 0\n" ] } ], "source": [ "# Estadisticas antes vs despues\n", "df['tokens_raw'] = df[TEXT_COL].str.split().str.len()\n", "df['tokens_clean'] = df['clean_text'].str.split().str.len()\n", "\n", "print(f\"{'':20} {'CRUDO':>8} {'LIMPIO':>8} {'REDUCCION':>11}\")\n", "print('-' * 52)\n", "for stat in ['mean', 'median', 'min', 'max']:\n", " raw = getattr(df['tokens_raw'], stat)()\n", " clean = getattr(df['tokens_clean'], stat)()\n", " pct = (1 - clean / raw) * 100 if raw > 0 else 0\n", " print(f' {stat:18} {raw:8.1f} {clean:8.1f} {pct:10.1f}%')\n", "\n", "print()\n", "empty_after = (df['tokens_clean'] == 0).sum()\n", "print(f'Comentarios vacios tras limpieza : {empty_after}')" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "image/png": "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/tU0zaJ/r5L83QAAoKzxPPenkvYkvPXWW63jBZ8jnnjiCet448aNrePp6emF2g4NDbWOm6ZppqSkWGXjxo0zTbPonnAHDx60RqC4XC7zjz/+sNp46KGHrPoPPPCAaZqm+Y9//MM69v777/vUPxMF77tgz7n09HTrN5Fk7tmz56xjK/h92Gw288cffzyjmEryvF2wJ2FWVpY5ZswYs2nTpj73cGJr2bKlVfdEr8jmzZub3377rZmTk1PoOgXvOy4uzvoz/uWXX5p/+ctfrHY/+OCDM7o/4EJHT0IggP3+++86fPiwJCk0NFRNmjSxytq2bWt9/umnnwqde/DgQe3atUvS8Z5tHTt2tMr++OMPq928vDx16dJFV155pa688kpNmzbNqvfDDz8Uardhw4bWCnA2m816y5eenm7VGTx4sCTpt99+U2JiosLDw1WvXj3dddddRcZ6pgqee+21157ROe3bt1dMTEyJrylJ//vf/6zPl112mfW54G9QUME427VrV2T9030PYWFh1lveb775Rrt27dKSJUus7zklJcWqe/vtt8swDGVnZ6tLly6KiopS9erVddttt2nDhg2nv8ESKng/Bb/jNm3aWJ8rVapkfS74Z6TgefXq1SuyzYLf+8nO5e8GAADliee5s1MazxcNGzb0WT35TJ8vfv75Z2sewbp166pixYpFtnHi/lNSUqx5KG+66SZVqlRJVapU0Q033KAlS5YUf5NFKPjMGBUVpQYNGvjEfLaxFVS/fn2f9k6lJM/bBfXr109jxozR5s2blZ2dXai8qD9j3333nVq2bKmwsDA1atRIDz74oFJTUyUd72F54r7T0tKsP+NXXnmlz7yFRf05By5GJAkBSFKh4QqnG75wYliFJE2cOFH79+8/62sW1W2/YNd/6fgQ15P97W9/0+eff67+/furSZMmCgoK0s6dO/XKK6+oY8eORT7MlZUTD8AFFfzuTiz8ccKpFiM5VTtlUf/kIccnhhobhqFbb73VKuvatatWr16tO+64Qy1btlRoaKj27t2r2bNnq2PHjqd8GD4XBR++Cw49iYyMLLL+iQe8Uznb76ioc0rSBgAA5YHnudM7X54vzqSNJk2aaOPGjbr//vvVrl07RUVF6cCBA/roo4+UnJysNWvWlOr1Slq3qOfhsrBnzx598sknkqTw8HBNmzZNK1as0IoVK6w6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eOG8n06OJCEAACixypUr6/7775ck9evXT3/72980ePBgVaxYUZLUsWNHffPNN1aS0Ov1atKkSQoNDVXDhg01dOhQvfXWW4WShL/99puWLVumtLQ0hYeHKzw8XCNHjtSYMWNIEgIAAJSxsp4Kp6CSTosTHBysSZMm6a677lKFChXKKLrSExUV5e8QTovhxgAAoMSqVKlifQ4NDS3yWGZmprUfHBys2NhYa79mzZr67bffCrW7d+9eBQcH+7RVp04d7d27t1TjBwAAQNHKciqc0pgWp0uXLoqLi9OECRNOWe/DDz9U48aN5XK5VKtWLf3rX/8qtu7MmTM1duxYfffddzIMQ4ZhaObMmZKkPXv2qFevXgoPD1dkZKRuvvlm7d+/X5L0448/KjQ0VHPmzLHaeu+99xQSEqJt27ZJKjzc2Ov1avLkyapXr55cLpdq1Kihp556yirfvHmzOnXqpJCQEFWsWFF33nmnz3N1WSBJCAAAyk1OTo4OHDhg7e/Zs0fVqlUrVC8hIUE5OTnWg5ck7d69WwkJCeUSJwAAAM5vdrtdTz/9tF588cViXyRv3LhRN998s/r27avNmzdrzJgxeuKJJ6zE38luueUWPfTQQ2rcuLFSU1OVmpqqW265RV6vV7169dKhQ4e0cuVKLV68WP/73/90yy23SJIaNmyoZ555Rvfee6/27NmjvXv36u6779akSZPUqFGjIq81YsQITZw4UU888YS2bdumOXPmWC/Is7KylJycrAoVKujrr7/W+++/ryVLlmjo0KHn/sWdAsONAQBAkfLz85WTk2PtG4Zxzm3abDaNGDFCL730kvbs2aOpU6dqzJgxhepVq1ZNV199tf7+97/r5Zdf1sGDB/XUU08VOXchAAAAAtNf/vIXtWjRQqNHj9brr79eqPzZZ59V586d9cQTT0iSLrnkEm3btk3//Oc/NWjQoEL1Q0JCFB4eLofDobi4OOv44sWLtXnzZu3atUvVq1eXJM2aNUuNGzfW119/rcsuu0z33nuvFixYoNtuu01BQUG67LLLdN999xUZ99GjR/XCCy/opZdesp5v69atqw4dOkiS5syZo5ycHM2aNUthYWGSpJdeeknXXXedJk2a5DPapjSRJPQjw7Dp0uoNFGS3y274duq02QzVrV9Rdrtks5/7/5QBAM5/IXbnedX2ww8/rIcfftjar1mz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gwbJ7XZLOt4TsFevXoqNjVVUVJSuuuoqfffdd2d8v4ZhWHPjjRkzRtdee63uuusuRUVFqXbt2lqxYoXmzZunevXqqUKFCj73cuI+HnvsMVWsWFE1atTQtGnTrPKThzbv2LFDycnJiomJUd26dfX888+fcZwLFy5Uq1atdO2118pms+naa69V27ZtNWvWrDNuAwAAADgZSUI/MgxD1SrGq2blBNlkFCqrEheuqvERMviVAOCil5WbU+yW484747rH8oqve7aio6PVrVs3a5jsG2+8odtvv92nTnZ2tjp37qzOnTvrt99+05dffqm5c+dqxowZkqRly5Zp5MiReu+995SamipJ2rJlS7HXrFmzppYtW6aMjAy99tprevjhh7V69WqfOp9//rm+/fZbbdu2TUuXLtXs2bMlSV6vV7feeqt27dql/fv3q2XLlrr55ptP27OtOIsWLVJycrIOHTqk/v3767bbbtPHH3+s7777TqtXr9a//vUvffPNN1b9LVu2yDAMpaam6t1339U//vEPrVq1qlC7+fn5uvbaa9W8eXPt27dPH330kSZPnqw5c+ZYdaKjo/XVV18VGZfX6y10T16vV99//32J7hMAAACQGG7sVw67Qzdd2VtRLpdcziBJf/a6cDhs6tazgUJCbAoKsvsvSABAuaj2yMBiy7o2aqn37vqHtV//8TuVnZdbZN329Rrps/tGW/vNxg7VwayjkqT0F94967huv/12Pf744xo4cKA+/PBDbdmyRf/4x5+xfPbZZ6pQoYI1R2GNGjX0wAMPaM6cORo8eLBmz56tlJQUJSYmSjreo65g78CT9enTx/p89dVXKzk5WStWrFD79u2t46NGjVJERIQiIiLUrVs3bdy4UYMGDVJkZKRuueUWq97YsWM1ZcoU7du3T9WqVTvre2/durVuuOEGScd7VY4fP17/+Mc/FBYWpkaNGqlZs2b65ptv1KpVK0lSWFiYxowZI6fTqcTERKWkpGjWrFm66qqrfNpdv369UlNT9eSTTyooKEjNmjXT0KFDNXPmTN16662SpPT09GLjuuaaa/T3v/9d8+bN07XXXqv58+dr9erVSkpKOut7BAAAAE6gjxoAAChW586dlZqaqvHjxysxMVFxcXE+5bt379aWLVsUHR1tbQ899JDS0tIkSfv27VPNmjWt+k6nU1WrVi32erNnz1ar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TSEhNtnthp8iBAAAAAAAQCAgSehHdptdbeq3VJTLJafdIenP1YvtdpuaNItTdAWHHA46fAIAAAAAAKDs+DX7NGHCBF122WWKiIhQbGysevfure3bt/vUycnJ0ZAhQ1SxYkWFh4erT58+2r9/v0+dPXv2qGfPngoNDVVsbKwefvhh5efnl+etAAAAAAAAABcsvyYJV65cqSFDhmjdunVavHix3G63unbtqqysLKvO8OHD9emnn+r999/XypUrtW/fPt1www1WucfjUc+ePZWXl6c1a9bozTff1MyZMzVq1Ch/3NJZ8ZpepR3er98Opsnj9fqWeU398XuW0lIz5fWafooQAAAAAAAAgcCvScKFCxdq0KBBaty4sZo3b66ZM2dqz5492rhxoyTpyJEjev311/Xss8+qU6dOat26tWbMmKE1a9Zo3bp1kqRFixZp27Ztevvtt9WiRQt1795d48eP19SpU5WXl+fP2zstj8ejuSs/1GtL3lGex31SmVefffKj3p29VW63t5gWAAAAzj+rVq3Sddddp/j4eBmGoXnz5vmUm6apUaNGqWrVqgoJCVGXLl30888/+9Q5dOiQUlJSFBkZqejoaA0ePFiZmZnleBcAAACB5bya7O7IkSOSpJiYGEnSxo0b5Xa71aVLF6tOw4YNVaNGDa1du1aStHbtWjVt2lRVqlSx6iQnJysjI0Nbt24t8jq5ubnKyMjw2QAAAFA6srKy1Lx5c02dOrXI8smTJ2vKlCl6+eWXtX79eoWFhSk5OVk5OTlWnZSUFG3dulWLFy/W/PnztWrVKt15553ldQsAAAAB57xZuMTr9WrYsGFq3769mjRpIklKS0tTUFCQoqOjfepWqVJFaWlpVp2CCcIT5SfKijJhwgSNHTu2lO8AAAAAktS9e3d17969yDLTNPX888/r8ccfV69evSRJs2bNUpUqVTRv3jz17dtXP/zwgxYuXKivv/5abdq0kSS9+OKL6tGjh5555hnFx8eX270AAAAEivOmJ+GQIUO0ZcsWzZ07t8yvNWLECB05csTafv311zK/JgAAAKRdu3YpLS3NZ6RIVFSU2rVr5zNSJDo62koQSlKXLl1ks9m0fv36IttlpAgAAMC5OS+ShEOHDtX8+fO1fPlyJSQkWMfj4uKUl5en9PR0n/r79+9XXFycVefk1Y5P7J+oczKXy6XIyEif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8KAcPHhQAMhLL70kBw8elHPnzomIyPLlyyU6Olo++OADOXz4sEyZMkX69OkjtbW13n1kZ2fLiBEjZO/evbJr1y6588475ZFHHumoJt2Sn/70p9KtWzfJz8/3eY26zWbzlpk9e7YkJyfLp59+Kvv375eMjAzJyMjwrq9/zfq3v/1tKSoqkk2bNkmPHj26zGvWlyxZItu3b5fS0lI5fPiwLFmyRBRFkY8//lhEQr/9gTR8c1aox2HhwoWSn58vpaWlsnv3bsnMzJS4uDiprKwUkdBvv4hIYWGh6PV6ef7556WkpETefvttsVgs8tZbb3nLhPo5UeTGW16Tk5Nl8eLFjdZpoR/k5uZKr169ZP369VJaWirvv/++xMXFyb//+797y2ihH4S6UB/fBet2z/2hROtj4qY0FZvq6mp56qmnpKCgQEpLS+WTTz6Ru+++W+68806pq6vz7iNUY8NricCai82pU6fkueeek/3790tpaal88MEH0rdvX5kwYYJ3H6EaG15/BdZUbLTcZ4LFJGE7ee211yQ5OVmMRqOMHj1a9uzZ09FVajXbtm0TAI3+5ebmioiIx+ORX/7yl5KQkCAmk0kmTpwoxcXFPvu4fPmyPPLIIxIRESFRUVHy4x//WKqrqzugNbfOX9sByMqVK71lamtr5V//9V8lJiZGLBaLfOc735Hy8nKf/Zw9e1YmTZokZrNZ4uLiZOHCheJ0Otu5NS0zY8YMSUlJEaPRKD169JCJEyd6/0CJhH77A2mYJAz1OEybNk169uwpRqNRevXqJdOmTZNTp05514d6++v93//9n9x1111iMplk0KBB8qc//clnfaifE0VENm/eLAAatUtEG/2gqqpK5s+fL8nJyRIWFiZ9+/aVn//852K3271ltNAPtCCUx3fBao1zf6jQ+pi4KU3Fxmazybe//W3p0aOHGAwGSUlJkSeeeKJRwj1UY8NricCai01ZWZlMmDBBunfvLiaTSfr37y+LFi2S69ev++wnFGPD66/AmoqNlvtMsBQRkTa9VZGIiIiIiIiIiIg6Nc5JSEREREREREREpHFMEhIREREREREREWkck4REREREREREREQaxyQhERERERERERGRxjFJSEREREREREREpHFMEhIREREREREREWkck4REREREREREREQaxyQhERERERERERGRxjFJSETUyeTn50NR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iIiIKHgcd3Hc1Zxb/XzbCseTRG2DSUIi6vSWL1+O4cOHeyeprtejRw9UVFRARLxzkBQVFd328e6++26sWbMG8fHx3oFaMAYPHoxVq1ahpqbGO5DdvXs3VFVtVPebt/nwww99lu3Zs6dRfY4fP47+/fsHXRej0egzYXT9sc6fP4/z5897v9U+fvw4rl27hiFDhnjLmc1mrF+/Hg888ACysrLw8ccfIzIyEiNGjIDb7UZlZSXGjx8fdF38GTFiBEaMGIG8vDxkZGTgnXfe4aCOiIioE+C4i+OuttAwznv27MHgwYP9lg32s+0M7SIKNXzcmIg6vbS0NOTk5ODVV1/1Wf6Nb3wDly5dwu9+9zucPn0aK1aswEcffXTbx8vJyUFcXBymTJmCnTt3orS0FPn5+Zg3b16Tk3nn5OQgLCwMubm5OHr0KLZt24Z/+7d/w49+9CMkJCT43Wb27NkoKSnBokWLUFxcjHfeecf7Rrx6ixcvxt///nfMnTsXRUVFKCkpwQcffNDkxNypqakoLS1FUVERvvzyS9jtdmRmZnpj+dlnn6GwsBCPPfYY7r//fowaNcpn+/DwcGzYsAF6vR6TJk2C1WrFgAEDkJOTg8ceewzvv/8+SktLUVhYiGXLlmHDhg1Bxba0tBR5eXkoKCjAuXPn8PHHH6OkpCTgIJGIiIjaF8ddHHe1hd27d+N3v/sdTp48iRUrVuC9997D/Pnz/ZZt7rPtTO0iCjVMEhJRl/Dcc881emxh8ODB+M///E+sWLEC6enpKCws9L7N7XZYLBbs2LEDycnJeOihhzB48GA8/vjjqKura/IbbovFgs2bN+PKlSu455578N3vfhcTJ07EH/7wh4DbJCcn43//93+xbt06pKen44033sALL7zgU2bYsGHYvn07Tp48ifHjx2PEiBF45plnkJSUFHC/Dz/8MLKzs/HNb34TPXr0wLvvvgtFUfDBBx8gJiYGEyZMQGZmJvr27Ys1a9b43UdERAQ++ugjiAgmT56MmpoarFy5Eo899hgWLlyIgQMHYurUqdi3b1/Qj99YLBZ8/vnnePjhhzFgwADMnDkTc+bMwaxZs4LanoiIiNoex10cd7W2hQsXYv/+/RgxYgR+85vf4KWXXkJWVpbfss19tp2pXUShRpGGE0sQEREREREREbWC1NRULFiwAAsWLOjoqhBRM3gnIRERERERERERkcYxSUhERERERERERKRxfNyYiIiIiIiIiIhI43gnIRERERERERERkcYxSUhERERERERERKRxTBISERERERERERFpHJOEREREREREREREGsckIRERERERERERkcYxSUhERERERERERKRxTBISERERERERERFpHJOEREREREREREREGsckIRERERERERERkcb9P/dyopTKjJl0AAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "💾 Guardado en reports/07_tokens_comparativa.png\n" ] } ], "source": [ "# Visualizacion: distribucion tokens antes vs despues\n", "fig, axes = plt.subplots(1, 2, figsize=(13, 5))\n", "\n", "# Comparativa global\n", "axes[0].hist(df['tokens_raw'], bins=40, alpha=0.6, label='Crudo',\n", " color='#7F77DD', edgecolor='white')\n", "axes[0].hist(df['tokens_clean'], bins=40, alpha=0.6, label='Limpio',\n", " color='#5DCAA5', edgecolor='white')\n", "axes[0].axvline(df['tokens_raw'].median(), color='#534AB7',\n", " linestyle='--', lw=1.5, label=f'Mediana crudo: {df[\"tokens_raw\"].median():.0f}')\n", "axes[0].axvline(df['tokens_clean'].median(), color='#0F6E56',\n", " linestyle='--', lw=1.5, label=f'Mediana limpio: {df[\"tokens_clean\"].median():.0f}')\n", "axes[0].set_title('Tokens: crudo vs limpio', fontweight='bold')\n", "axes[0].set_xlabel('Numero de tokens')\n", "axes[0].set_ylabel('Frecuencia')\n", "axes[0].legend(fontsize=9)\n", "\n", "# Por clase\n", "for clase, color in [('Toxico', '#E8593C'), ('No toxico', '#5DCAA5')]:\n", " mask = df[TARGET_BIN] == (clase == 'Toxico')\n", " axes[1].hist(df.loc[mask, 'tokens_clean'], bins=30, alpha=0.6,\n", " label=clase, color=color, edgecolor='white')\n", "axes[1].set_title('Tokens limpios por clase', fontweight='bold')\n", "axes[1].set_xlabel('Numero de tokens limpios')\n", "axes[1].legend()\n", "\n", "plt.tight_layout()\n", "plt.savefig(PROJECT_ROOT / 'reports' / 'v2' / '07_tokens_comparativa.png',\n", " dpi=150, bbox_inches='tight')\n", "plt.show()\n", "print('💾 Guardado en reports/07_tokens_comparativa.png')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Comentarios con < 3 tokens tras limpieza: 106\n", "\n", " [NO TOXICO]\n", " CRUDO : I agree with the protestor.\n", " LIMPIO : \"agree protestor\"\n", "\n", " [TOXICO]\n", " CRUDO : I think ,he kill him\n", " LIMPIO : \"think kill\"\n", "\n", " [NO TOXICO]\n", " CRUDO : 1 word: Provocateur........\n", " LIMPIO : \"word provocateur\"\n", "\n", " [NO TOXICO]\n", " CRUDO : I LIKE TURTLES!\n", " LIMPIO : \"turtle\"\n", "\n", " [TOXICO]\n", " CRUDO : CNN is such Bullcrap!\n", " LIMPIO : \"cnn bullcrap\"\n", "\n", " [TOXICO]\n", " CRUDO : You all are some dumb as people\n", " LIMPIO : \"dumb people\"\n", "\n" ] } ], "source": [ "# Casos problematicos: comentarios con menos de 3 tokens tras limpieza\n", "# Son comentarios que casi desaparecen -> hay que revisarlos\n", "short_clean = df[df['tokens_clean'] < 3]\n", "print(f'Comentarios con < 3 tokens tras limpieza: {len(short_clean)}')\n", "print()\n", "for _, row in short_clean.head(6).iterrows():\n", " label = 'TOXICO' if row[TARGET_BIN] else 'NO TOXICO'\n", " print(f' [{label}]')\n", " print(f' CRUDO : {row[TEXT_COL][:80]}')\n", " print(f' LIMPIO : \"{row[\"clean_text\"]}\"')\n", " print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Registro en MLflow\n", "\n", "Guardamos exactamente qué configuración usamos.\n", "Si mañana cambiamos `min_token_length` o las custom stopwords,\n", "podremos comparar qué configuración produjo mejores métricas al modelar.\n", "\n", "> Para ver el dashboard: `mlflow ui` desde la raíz del proyecto.\n", "\n", "> mlflow ui --backend-store-uri file:./mlruns --port 5001 " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026/05/14 16:23:55 INFO mlflow.tracking.fluent: Experiment with name 'Youtube_project_data' does not exist. Creating a new experiment.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "MLflow run registrado\n", " Run ID : fe6b6538f89b490eb6f586ef555bc82f\n", " Experimento: Youtube_project_data\n", " Ver UI : mlflow ui --backend-store-uri file:///mnt/c/Users/under/Documents/F5/3_Projects/Project_9_Equipo3/Project_YT/mlruns\n" ] } ], "source": [ "# ── Configuración MLflow ──\n", "MLFLOW_DIR = PROJECT_ROOT / 'mlruns'\n", "EXPERIMENT_NAME = 'Youtube_project_data'\n", "\n", "mlflow.set_tracking_uri(f\"file:{MLFLOW_DIR}\")\n", "mlflow.set_experiment(EXPERIMENT_NAME)\n", "\n", "\n", "with mlflow.start_run(run_name='preprocessing_v2'):\n", "\n", " # Params: que configuracion usamos\n", " mlflow.log_param('spacy_model', 'en_core_web_sm')\n", " mlflow.log_param('nltk_stopwords', 'english')\n", " mlflow.log_param('custom_stopwords', list(CUSTOM_STOPWORDS))\n", " mlflow.log_param('min_token_length', MIN_TOKEN_LEN)\n", " mlflow.log_param('remove_urls', prep_cfg['remove_urls'])\n", " mlflow.log_param('remove_mentions', prep_cfg['remove_mentions'])\n", " mlflow.log_param('lemmatize', prep_cfg['lemmatize'])\n", "\n", " # Metrics: que produce este preprocesamiento\n", " reduction = (1 - df['tokens_clean'].mean() / df['tokens_raw'].mean()) * 100\n", " mlflow.log_metric('avg_tokens_raw', round(df['tokens_raw'].mean(), 2))\n", " mlflow.log_metric('avg_tokens_clean', round(df['tokens_clean'].mean(), 2))\n", " mlflow.log_metric('median_tokens_clean', float(df['tokens_clean'].median()))\n", " mlflow.log_metric('token_reduction_pct', round(reduction, 2))\n", " mlflow.log_metric('empty_after_clean', int(empty_after))\n", " mlflow.log_metric('short_texts_lt3', len(short_clean))\n", "\n", " # Artefactos: archivos que produce\n", " out_csv = PROJECT_ROOT / 'data' / 'processed' / 'v2' /'comments_preprocessed.csv'\n", " df[['CommentId', TEXT_COL, 'clean_text', TARGET_BIN] + SUBLABELS].to_csv(\n", " out_csv, index=False\n", " )\n", " mlflow.log_artifact(str(out_csv))\n", " mlflow.log_artifact(str(PROJECT_ROOT / 'reports' / 'v2' / '07_tokens_comparativa.png'))\n", " mlflow.log_artifact(str(CONFIG_PATH))\n", "\n", " run_id = mlflow.active_run().info.run_id\n", " print(f'MLflow run registrado')\n", " print(f' Run ID : {run_id}')\n", " print(f' Experimento: Youtube_project_data')\n", " print(f' Ver UI : mlflow ui --backend-store-uri file://{MLFLOW_DIR}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Conclusiones y decisiones" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "CONCLUSIONES — PREPROCESAMIENTO\n", "=======================================================\n", "Pipeline aplicado:\n", " 1. lowercase\n", " 2. regex: URLs, @menciones, \\xa0, apostrofes, numeros\n", " 3. spaCy: lematizacion (en_core_web_sm)\n", " 4. NLTK: stopwords english + custom\n", "\n", "Resultado:\n", " Reduccion tokens : 51.2%\n", " Tokens mediana : 9 (antes: 19)\n", " Comentarios vacios: 0\n", "\n", "Decisiones clave:\n", " NO quitados: black, white, police, cop\n", " -> EDA mostro que aparecen en ambas clases con contexto distinto\n", " -> el modelo necesita verlas para aprender por bigrams\n", " SI quitados: youtube, video, watch, like, comment, channel\n", " -> ruido tematico sin valor discriminante\n", " spaCy para lemma, no NLTK Stemmer\n", " -> stemmer corta letras, lemma entiende gramatica\n", "\n", "\n" ] } ], "source": [ "reduction = (1 - df['tokens_clean'].mean() / df['tokens_raw'].mean()) * 100\n", "\n", "print(f\"\"\"\n", "CONCLUSIONES — PREPROCESAMIENTO\n", "{'='*55}\n", "Pipeline aplicado:\n", " 1. lowercase\n", " 2. regex: URLs, @menciones, \\\\xa0, apostrofes, numeros\n", " 3. spaCy: lematizacion (en_core_web_sm)\n", " 4. NLTK: stopwords english + custom\n", "\n", "Resultado:\n", " Reduccion tokens : {reduction:.1f}%\n", " Tokens mediana : {df['tokens_clean'].median():.0f} (antes: {df['tokens_raw'].median():.0f})\n", " Comentarios vacios: {empty_after}\n", "\n", "Decisiones clave:\n", " NO quitados: black, white, police, cop\n", " -> EDA mostro que aparecen en ambas clases con contexto distinto\n", " -> el modelo necesita verlas para aprender por bigrams\n", " SI quitados: youtube, video, watch, like, comment, channel\n", " -> ruido tematico sin valor discriminante\n", " spaCy para lemma, no NLTK Stemmer\n", " -> stemmer corta letras, lemma entiende gramatica\n", "\n", "\"\"\")" ] } ], "metadata": { "kernelspec": { "display_name": "py310", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.20" } }, "nbformat": 4, "nbformat_minor": 4 }