Descripción del proyecto
SignalMod es un asistente de moderación inteligente para comentarios de YouTube. Clasifica automáticamente cada comentario como Seguro o Tóxico, devuelve una probabilidad entre 0 y 1 y etiqueta categorías de toxicidad (insulto, amenaza, odio identitario, contenido obsceno).
Está construido alrededor del modelo hybrid meta-feature stacking del equipo — embeddings de Toxic-BERT congelado combinados con metadatos y una regresión logística regularizada — que alcanza F1 = 0,805 con una brecha train–test de 2,54 pp sobre el split de 200 muestras del proyecto.
El producto se entrega como una API REST con FastAPI y una SPA React que imita la experiencia de YouTube Watch: eliges un vídeo, la API descarga los 50 comentarios más recientes vía la YouTube Data API, los puntúa y persiste cada predicción en Supabase para que cualquier visitante pueda ver el histórico completo.
Herramientas y lenguajes
Lenguajes
- Python 3.12 — backend, pipelines de ML, evaluación.
- TypeScript + React 18 — SPA del frontend.
- SQL (PostgreSQL vía Supabase) — persistencia de predicciones.
Backend
- FastAPI 0.136 — API REST, esquemas Pydantic, carga del modelo en lifespan.
- Uvicorn — servidor ASGI con hot reload.
- scikit-learn 1.8 — baseline TF-IDF + meta-learner LogisticRegression.
- Optuna — búsqueda de hiperparámetros del baseline TF-IDF.
- PyTorch 2.x + Transformers 5.9 —
unitary/toxic-bertcongelado para embeddings CLS. - spaCy + NLTK — lematización, stopwords, limpieza basada en regex.
- MLflow — tracking de experimentos.
- Supabase Python SDK — persistencia de predicciones con políticas RLS anónimas.
- google-api-python-client — integración con YouTube Data API v3.
Frontend
- React 18 + Vite 5 + TypeScript — SPA con hot module reload.
- CSS modules — tema oscuro estilo YouTube.
Tooling y operaciones
- uv — gestor de paquetes y entorno virtual de Python (
pyproject.toml+uv.lock). - pnpm — gestor de paquetes del frontend.
- Docker + Docker Compose — despliegue en un único contenedor sirviendo API + SPA construida.
- GNU Make —
make dev,make install,make build,make docker. - Render — despliegue gratuito vía blueprint
render.yaml. - Pytest — tests unitarios de contratos de API y preprocesado.
Arquitectura del proyecto
Project_9_Equipo3/
├── configs/ # Configs YAML para pipelines y catálogo de inferencia
│ ├── pipeline.yaml # Rutas de datos, target, folds de CV
│ ├── features.yaml # Preprocesado y ajustes de TF-IDF
│ ├── model_catalog.yaml # Catálogo de inferencia (3 modelos intercambiables)
│ ├── best_params.yaml # Ganador de Optuna para el baseline LR
│ ├── suggested_videos.yaml # IDs de YouTube del rail "Up next"
│ └── *_training.yaml # Perfiles de entrenamiento (golden, expert, hybrid, …)
├── data/ # Datasets crudos y procesados (git-ignored)
├── docs/ # API.md, PIPELINE.md, ARCHITECTURE.md, DEPLOY.md
│ └── assets/signalmod_logo.png # Activos de marca
├── frontend/ # SPA React + Vite
│ ├── public/signalmod_logo.png # Logo servido como activo estático
│ └── src/
│ ├── api/ # Cliente HTTP tipado
│ ├── components/ # Layout, CommentRow, SuggestedRail, ModelBanner
│ ├── context/ # Estado global (modelo activo, umbral)
│ ├── hooks/ # useDebouncedPredict
│ ├── pages/ # WatchPage, HubPage, SettingsPage
│ └── utils/ # toxicityColor, randomUsername, relativeTime
├── models/
│ ├── baseline/lr_tfidf.joblib # Baseline LR ajustado con Optuna
│ └── production_final/ # meta_stack_final.joblib — artefacto de producción
├── notebooks/
│ ├── 01–04 # EDA, preprocesado, TF-IDF, baseline LR
│ ├── 12 # Golden baseline (Toxic-BERT congelado)
│ ├── 14 # Meta-stacking final — artefacto de producción
│ └── archive_attempts/ # Experimentos anteriores conservados para reproducibilidad
├── reports/ # Métricas, gráficos, figuras EDA, summary.csv
├── src/
│ ├── api/ # App FastAPI
│ │ ├── main.py # Lifespan, CORS, montaje del SPA estático
│ │ ├── routes/ # health, models, predict (+ /predictions), videos
│ │ ├── schemas.py # Modelos Pydantic request/response
│ │ ├── services.py # predict_single, to_predict_response
│ │ ├── state.py # Estado compartido de la app
│ │ └── youtube.py # Fetch a YouTube Data API + metadatos sugeridos
│ ├── data/ # Loader, dual loader para pipelines híbridos
│ ├── db/ # Cliente Supabase + helpers save_prediction
│ ├── evaluation/ # Evaluator, threshold tuning, CV estable
│ ├── experiments/ # Versiones script de los notebooks 13 / 14
│ ├── features/ # text_preprocessor, vectorizer, metadata, augmentation
│ ├── models/ # baseline (LR/RF/XGBoost), hybrid_ensemble, metadata_lr
│ ├── pipeline/ # run_pipeline + variantes por estrategia
│ ├── service/ # ModelService, meta_stack_predictor, model_catalog
│ └── utils/ # Logger
├── supabase/predictions_setup.sql # SQL para crear la tabla predictions + políticas RLS
├── tests/ # Suite Pytest
├── Dockerfile # Build multi-stage (frontend + backend con uv)
├── docker-compose.yml # Despliegue de un contenedor (API + SPA)
├── render.yaml # Blueprint de Render (web service + static site)
├── Procfile # Declaración de proceso para Render
├── Makefile # make dev / install / build / docker / test
├── pyproject.toml + uv.lock # Dependencias Python fijadas con uv
└── README.md / README.es.md # Documentación en inglés / español
Flujo de datos
┌────────────────────────────────────────────────┐
│ SPA React (Vite) http://localhost:5173│
│ Layout · Watch · Hub · Settings │
└──────────────────┬─────────────────────────────┘
│ HTTP JSON (proxy Vite → :8000)
┌──────────────────▼─────────────────────────────┐
│ FastAPI http://localhost:8000│
│ /predict /predict-batch /predict-video │
│ /predictions (GET — histórico de Supabase) │
│ /models /models/select /model-info │
│ /videos/suggested /health │
└──────┬─────────────────────────────┬───────────┘
│ │
┌──────────────▼─────────────┐ ┌─────────────▼──────────────┐
│ ModelService │ │ YouTube Data API v3 │
│ · local joblib │ │ · metadatos de vídeo │
│ · hf_remote │ │ · 50 comentarios + nuevos │
│ · meta_stack (producción) │ │ │
└──────┬─────────────────────┘ └────────────────────────────┘
│
┌──────▼──────────────────────────────────────────────────┐
│ Supabase (PostgreSQL) │
│ tabla: predictions(id, created_at, text, video_id, │
│ probability, is_toxic, labels, …) │
│ RLS: insert anónimo + select anónimo │
└─────────────────────────────────────────────────────────┘
Catálogo de modelos (intercambiable desde la UI)
| Modelo | Tipo | F1 (test) | Brecha train–test | Umbral | Latencia | Default |
|---|---|---|---|---|---|---|
| Meta-Feature Stacking | Híbrido | 0,805 | 2,54 pp | 0,381 | ~400 ms | Sí |
| Frozen Toxic-BERT | Transformer | 0,790 | 0,16 pp | 0,120 | ~400 ms | No |
| LR + TF-IDF (Optuna) | sklearn | 0,758 | 4,76 pp | 0,500 | < 50 ms | No |
El modelo de producción concatena el embedding [CLS] congelado de unitary/toxic-bert (768-d) con metadatos hechos a mano (longitud, ratio de mayúsculas, densidad de emojis…), los escala con StandardScaler y los pasa por un meta-learner LogisticRegression(C=0,001).
Instalación y ejecución
1. Requisitos previos
| Herramienta | macOS / Linux | Windows |
|---|---|---|
| Python 3.12 | brew install python@3.12 |
python.org/downloads (marca Add Python to PATH) |
| uv | curl -LsSf https://astral.sh/uv/install.sh | sh |
powershell -c "irm https://astral.sh/uv/install.ps1 | iex" |
| Node.js 18+ | brew install node |
nodejs.org (LTS) |
| pnpm | npm i -g pnpm |
npm i -g pnpm |
| Make (opcional) | ya instalado | winget install GnuWin32.Make (o usa WSL) |
2. Clonar y configurar
git clone https://github.com/Bootcamp-IA-P6/Project_9_Equipo3.git
cd Project_9_Equipo3
cp .env.example .env
# Rellena: YOUTUBE_API_KEY, SUPABASE_URL, SUPABASE_KEY
PowerShell de Windows: sustituye
cpporCopy-Item .env.example .env.
Pega supabase/predictions_setup.sql en el editor SQL de Supabase antes del primer arranque (crea la tabla predictions + políticas RLS).
3. Arranque — tres opciones
Opción A — Con Makefile (recomendada en macOS / Linux / WSL)
make install # uv sync + pnpm install
make dev # FastAPI :8000 + Vite :5173
| Comando | Qué hace |
|---|---|
make install |
Instala deps de Python + frontend |
make dev |
Arranca API y UI en paralelo (Ctrl+C los para) |
make api |
Solo la API |
make ui |
Solo la UI |
make build |
Compila el SPA a frontend/dist |
make test |
Ejecuta Pytest |
make docker |
docker compose up --build |
make stop |
Mata procesos en los puertos 8000 / 5173 |
make clean |
Borra .venv, node_modules, dist |
Opción B — Manual (macOS / Linux)
Dos terminales.
Terminal 1 — API
uv sync
uv run uvicorn src.api.main:app --reload --port 8000
Terminal 2 — Frontend
cd frontend
pnpm install
pnpm dev
Opción C — Manual (PowerShell de Windows)
Dos terminales.
Terminal 1 — API
uv sync
uv run uvicorn src.api.main:app --reload --port 8000
Terminal 2 — Frontend
cd frontend
pnpm install
pnpm dev
Si
uvno se reconoce tras instalarlo, cierra y vuelve a abrir PowerShell para que se recargue elPATH.
4. Abrir la aplicación
| URL | Qué verás |
|---|---|
| http://localhost:5173 | SPA React — Watch / Hub / Settings |
| http://localhost:8000/docs | Swagger de FastAPI |
| http://localhost:8000/health | Health check |
5. Docker (un solo contenedor — API + SPA compilada)
Mismos comandos en macOS / Linux / Windows:
# Normal — deja imágenes y volúmenes para builds rápidos
docker compose up --build
# → http://localhost:8000 · Ctrl+C para parar · docker compose down
# Demo efímera — Ctrl+C borra contenedor + imagen + volúmenes
make docker-demo
# Limpieza manual completa
make docker-clean
# (equivale a: docker compose down --rmi local --volumes --remove-orphans)
Más detalle: docs/PIPELINE.es.md para entrenamiento, docs/API.es.md para endpoints, docs/DEPLOY.md para despliegue en Render.
Colaboradores
|
Andrés Torrez Backend Developer |
Mirae Kang Scrum Master |
Jonathan Brasales AI Developer |
Roberto Molero Product Owner |
SignalMod — Bootcamp IA P6 · Equipo 3 · 2026