# Clasificador Multiclase — Dry Bean Dataset

**Curso:** Fundamentos de Machine Learning  
**Nombre:** Alejandro Lembke Barrientos | Carné: 12002840  
**Dataset:** Dry Bean Dataset (UCI ID 602) — 7 clases, 16 features morfológicas, 13,611 instancias  
**Repo:** https://gitea.p-lao.com/aleleba/Machine_Learning_Aplicado

## Objetivo

Clasificar automáticamente variedades de frijol seco (BARBUNYA, BOMBAY, CALI, DERMASON, HOROZ, SEKER, SIRA) a partir de 16 medidas morfológicas extraídas de imágenes. El problema replica un sistema de control de calidad agrícola automatizado.

Se comparan tres familias de modelos (Random Forest, XGBoost, Red Neuronal) con ≥5 experimentos cada una, y se construye un ensamble por majority voting con bootstrap.

## Setup

```bash
pip install -r requirements.txt
```

El dataset se descarga automáticamente desde UCI vía `ucimlrepo` — no requiere ningún archivo local.

## Estructura

```
proyecto-final/
├── dry_bean_classifier.ipynb          # único notebook entregable
├── outputs/
│   ├── bitacora_experimentos.csv      # log append de todos los experimentos
│   └── figures/                       # gráficas de feature importance
├── src/
│   └── utils.py                       # metricas(), registrar(), plot_importancia()
└── requirements.txt
```

## Secciones del Notebook

| # | Sección | Descripción |
|---|---------|-------------|
| 1 | Setup & Imports | Librerías, `RANDOM_STATE = 42`, `fetch_ucirepo` |
| 2 | Carga y Exploración | `fetch_ucirepo(id=602)`, distribución de clases, estadísticas |
| 3 | Preparación de Datos | Split 80/20 estratificado + `StandardScaler` para la NN |
| 4 | Bitácora — Funciones | `metricas()` y `registrar()` (helpers en `src/utils.py`) |
| 5 | Experimentos — Random Forest | ≥5 experimentos variando `n_estimators`, `max_depth`, `min_samples_leaf`, `max_features`, `criterion` |
| 6 | Experimentos — XGBoost | ≥5 experimentos variando `n_estimators`, `max_depth`, `learning_rate`, `subsample`, `colsample_bytree`, `reg_lambda` |
| 7 | Experimentos — Red Neuronal (Keras) | ≥5 experimentos variando capas, neuronas, `dropout`, `lr`, `batch_size`, `epochs` |
| 8 | Feature Importance | `mejor_rf` vs `mejor_xgb` — barplots + comparación de rankings |
| 9 | Ensamble | Majority voting + bootstrap; NN usa features escaladas |
| 10 | Tabla Comparativa | F1 train/test de los 4 modelos + Δ + diagnóstico |
| 11 | Conclusiones | Resumen automático basado en resultados reales |

## Archivos del Proyecto

| Archivo | Descripción |
|---------|-------------|
| [dry_bean_classifier.ipynb](dry_bean_classifier.ipynb) | Notebook principal con todo el código |
| [outputs/bitacora_experimentos.csv](outputs/bitacora_experimentos.csv) | Bitácora de todos los experimentos (append) |
| [outputs/figures/feature_importance__random_forest.png](outputs/figures/feature_importance__random_forest.png) | Feature importance — Random Forest |
| [outputs/figures/feature_importance__xgboost.png](outputs/figures/feature_importance__xgboost.png) | Feature importance — XGBoost |
| [src/utils.py](src/utils.py) | Helpers: `metricas()`, `registrar()`, `plot_importancia()` |
| [requirements.txt](requirements.txt) | Dependencias del proyecto |

---

## Tabla Comparativa Final

| Modelo | F1 train | F1 test | Δ | Diagnóstico |
|--------|----------|---------|---|-------------|
| Random Forest | 0.9739 | 0.9280 | 0.0459 | Bien ajustado |
| XGBoost | 0.9779 | 0.9327 | 0.0452 | Bien ajustado |
| **Red Neuronal (Keras)** | **0.9565** | **0.9404** | **0.0161** | **Bien ajustado** |
| Ensamble (majority voting) | 0.9754 | 0.9361 | 0.0393 | Bien ajustado |

**Mejor modelo individual:** Red Neuronal (Keras) con F1 test = **0.9404**

---

## Conclusiones

1. **Mejor modelo:** La Red Neuronal (Keras + StandardScaler) obtuvo el mayor F1 en test (0.9404), superando a XGBoost (0.9327) y Random Forest (0.9280).

2. **Ensamble:** El majority voting con bootstrap obtuvo F1 test = 0.9361, sin superar al mejor modelo individual (Δ = 0.0043). Los tres modelos votaron de forma coherente en la mayoría de casos.

3. **Diagnóstico:** Los cuatro modelos quedaron bien ajustados. La NN tuvo el menor Δ (0.0161), lo que indica muy buena generalización sin overfitting.

4. **Feature Importance:** ShapeFactor3 fue la feature más importante para RF y XGBoost. ShapeFactor4, Solidity y Extent coincidieron como las menos relevantes. Mayor discrepancia en Perimeter (RF rank 2 vs XGB rank 9).

5. **Aprendizaje clave:** La Red Neuronal (Keras) con `StandardScaler` superó a los modelos basados en árboles cuando las features están correctamente escaladas. El escalado es crítico para el desempeño de redes neuronales con datos de magnitudes muy distintas.
