File size: 16,104 Bytes
ea0e222
52b0ede
ea0e222
52b0ede
ea0e222
52b0ede
ea0e222
46cc63a
ea0e222
 
 
 
 
 
 
 
 
 
52b0ede
ea0e222
52b0ede
 
 
ea0e222
52b0ede
ea0e222
46cc63a
ea0e222
46cc63a
ea0e222
46cc63a
ea0e222
46cc63a
ea0e222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46cc63a
ea0e222
46cc63a
ea0e222
46cc63a
ea0e222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52b0ede
 
ea0e222
46cc63a
ea0e222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46cc63a
ea0e222
46cc63a
ea0e222
 
 
 
 
52b0ede
ea0e222
52b0ede
 
 
ea0e222
52b0ede
ea0e222
52b0ede
ea0e222
 
 
 
 
 
 
46cc63a
ea0e222
52b0ede
 
ea0e222
 
46cc63a
52b0ede
ea0e222
52b0ede
 
ea0e222
46cc63a
ea0e222
52b0ede
ea0e222
46cc63a
ea0e222
52b0ede
 
ea0e222
 
52b0ede
 
ea0e222
 
 
 
 
 
 
 
 
 
 
52b0ede
ea0e222
52b0ede
ea0e222
52b0ede
ea0e222
52b0ede
ea0e222
 
52b0ede
 
ea0e222
52b0ede
ea0e222
 
 
52b0ede
 
ea0e222
52b0ede
ea0e222
46cc63a
ea0e222
 
 
 
52b0ede
 
ea0e222
 
 
 
 
 
46cc63a
ea0e222
46cc63a
ea0e222
52b0ede
ea0e222
 
 
 
 
46cc63a
ea0e222
46cc63a
ea0e222
52b0ede
 
ea0e222
 
 
46cc63a
ea0e222
 
46cc63a
ea0e222
 
 
46cc63a
 
 
 
ea0e222
52b0ede
 
 
ea0e222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52b0ede
 
 
ea0e222
46cc63a
ea0e222
46cc63a
ea0e222
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
<div align="center">

<img src="docs/assets/signalmod_logo.png" alt="SignalMod" width="520" />

### ModeraciΓ³n inteligente para comentarios de YouTube

🌐 [English](README.md) · **Español**

![Python](https://img.shields.io/badge/Python-3.12-3776AB?logo=python&logoColor=white)
![FastAPI](https://img.shields.io/badge/FastAPI-0.136-009688?logo=fastapi&logoColor=white)
![React](https://img.shields.io/badge/React-18-61DAFB?logo=react&logoColor=black)
![Vite](https://img.shields.io/badge/Vite-5-646CFF?logo=vite&logoColor=white)
![PyTorch](https://img.shields.io/badge/PyTorch-2.x-EE4C2C?logo=pytorch&logoColor=white)
![Transformers](https://img.shields.io/badge/Transformers-5.9-FFD21E?logo=huggingface&logoColor=black)
![scikit-learn](https://img.shields.io/badge/scikit--learn-1.8-F7931E?logo=scikitlearn&logoColor=white)
![Supabase](https://img.shields.io/badge/Supabase-DB-3ECF8E?logo=supabase&logoColor=white)
![Docker](https://img.shields.io/badge/Docker-compose-2496ED?logo=docker&logoColor=white)
![Render](https://img.shields.io/badge/Deploy-Render-46E3B7?logo=render&logoColor=white)

</div>

---

## 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-bert` congelado 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 [email protected]`      | [python.org/downloads](https://www.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](https://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

```bash
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 `cp` por `Copy-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)

```bash
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**
```bash
uv sync
uv run uvicorn src.api.main:app --reload --port 8000
```

**Terminal 2 β€” Frontend**
```bash
cd frontend
pnpm install
pnpm dev
```

#### OpciΓ³n C β€” Manual (PowerShell de Windows)

Dos terminales.

**Terminal 1 β€” API**
```powershell
uv sync
uv run uvicorn src.api.main:app --reload --port 8000
```

**Terminal 2 β€” Frontend**
```powershell
cd frontend
pnpm install
pnpm dev
```

> Si `uv` no se reconoce tras instalarlo, cierra y vuelve a abrir PowerShell para que se recargue el `PATH`.

### 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**:

```bash
# 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](docs/PIPELINE.es.md) para entrenamiento, [docs/API.es.md](docs/API.es.md) para endpoints, [docs/DEPLOY.md](docs/DEPLOY.md) para despliegue en Render.

---

## Colaboradores

<table>
  <tr>
    <td align="center" width="25%">
      <b>AndrΓ©s Torrez</b><br/>
      <sub>Backend Developer</sub>
    </td>
    <td align="center" width="25%">
      <b>Mirae Kang</b><br/>
      <sub>Scrum Master</sub>
    </td>
    <td align="center" width="25%">
      <b>Jonathan Brasales</b><br/>
      <sub>AI Developer</sub>
    </td>
    <td align="center" width="25%">
      <b>Roberto Molero</b><br/>
      <sub>Product Owner</sub>
    </td>
  </tr>
</table>

---

<div align="center">

**SignalMod** β€” Bootcamp IA P6 Β· Equipo 3 Β· 2026

</div>