{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"\n# TALLER DE LABORATORIO – SEMANA 4\n## Machine Learning con datos estructurados y no estructurados de Kaggle\n\n**Curso:** Inteligencia Artificial  \n**Unidad I:** Machine Learning para el análisis inteligente de datos  \n**Semana:** 4  \n**Duración sugerida:** 100 minutos  \n**Docente:** Prof. Dr. Ivar Vargas Belizario  \n**Universidad Nacional de Juliaca – EPISS**\n\n### Tema de la semana\n**Random Forest y SVM. Matriz de confusión, accuracy, precision, recall, F1-score, ROC-AUC, validación cruzada, overfitting y underfitting.**\n\n### Propósito\nAl finalizar el taller, el estudiante implementa y compara **Random Forest (RF)** y **Support Vector Machine (SVM)** sobre dos tipos de datos reales:\n\n1. **Datos estructurados:** Titanic – Machine Learning from Disaster (Kaggle).\n2. **Datos no estructurados:** Dogs vs. Cats (Kaggle), usando imágenes.\n\n> **Idea central:** el algoritmo de ML recibe características numéricas. En datos tabulares estas características ya están organizadas en columnas; en imágenes primero debemos transformar el contenido visual en un vector de características.\n\n### Productos del taller\n- Notebook ejecutado.\n- Comparación RF vs. SVM para ambos datasets.\n- Matrices de confusión y métricas.\n- Validación cruzada.\n- Análisis breve de overfitting/underfitting.\n- Conclusiones técnicas.\n","metadata":{}},{"cell_type":"markdown","source":"\n## 0. Preparación del entorno\n\nEste notebook está diseñado para **Google Colab, Jupyter o Kaggle Notebooks**.\n\n### Datasets de Kaggle\n**Estructurado:** `Titanic - Machine Learning from Disaster`  \nhttps://www.kaggle.com/competitions/titanic/\n\n**No estructurado:** `Dogs vs. Cats`  \nhttps://www.kaggle.com/competitions/dogs-vs-cats\n\n### Si trabaja en Kaggle Notebook\nUse **Add Input** para añadir ambos datasets. Las rutas suelen quedar dentro de `/kaggle/input/`.\n\n### Si trabaja en Google Colab\nDescargue los archivos desde Kaggle y súbalos al entorno. Para Titanic necesitamos `train.csv`; para Dogs vs. Cats necesitamos `train.zip`.\n\n**Importante:** Dogs vs. Cats contiene 25 000 imágenes. Para que el taller pueda realizarse en 100 minutos, trabajaremos con una **muestra configurable**.\n","metadata":{}},{"cell_type":"code","source":"# Librerías\nimport os, glob, zipfile, random, warnings\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import train_test_split, cross_val_score\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.preprocessing import OneHotEncoder, StandardScaler\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.svm import SVC\nfrom sklearn.metrics import (\n    accuracy_score, precision_score, recall_score, f1_score,\n    roc_auc_score, ConfusionMatrixDisplay, classification_report\n)\n\nwarnings.filterwarnings(\"ignore\")\nRANDOM_STATE = 42\nnp.random.seed(RANDOM_STATE)\n\nprint(\"Entorno preparado correctamente.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:26.904186Z","iopub.execute_input":"2026-09-25T22:58:26.904496Z","iopub.status.idle":"2026-09-25T22:58:28.71142Z","shell.execute_reply.started":"2026-09-25T22:58:26.904468Z","shell.execute_reply":"2026-09-25T22:58:28.710585Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n# PARTE A — DATOS ESTRUCTURADOS: TITANIC\n\n## 1. Problema\nA partir de atributos de los pasajeros, construiremos un modelo que prediga la variable **Survived**:\n\n- `0`: no sobrevivió\n- `1`: sobrevivió\n\nTrabajaremos con variables numéricas y categóricas, por lo que será necesario realizar **preprocesamiento** antes de entrenar RF y SVM.\n","metadata":{}},{"cell_type":"code","source":"# 1. Localizar train.csv de Titanic.\n# Modifique esta ruta si fuera necesario.\ncandidate_paths = [\n    \"/kaggle/input/competitions/titanic/train.csv\",\n    #\"/kaggle/input/competitions/titanic/train.csv\",\n    #\"/content/train.csv\",\n    #\"train.csv\"\n]\n\ntitanic_path = next((p for p in candidate_paths if os.path.exists(p)), None)\n\nif titanic_path is None:\n    raise FileNotFoundError(\n        \"No se encontró train.csv. Descargue Titanic desde Kaggle y \"\n        \"coloque train.csv en el entorno, o modifique candidate_paths.\"\n    )\n\ndf = pd.read_csv(titanic_path)\nprint(\"Ruta:\", titanic_path)\nprint(\"Dimensión:\", df.shape)\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:59:59.87556Z","iopub.execute_input":"2026-09-25T22:59:59.875865Z","iopub.status.idle":"2026-09-25T22:59:59.90087Z","shell.execute_reply.started":"2026-09-25T22:59:59.875842Z","shell.execute_reply":"2026-09-25T22:59:59.900099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 2. Exploración inicial\ndisplay(df.info())\ndisplay(df.describe(include=\"all\").T)\n\nprint(\"\\nValores faltantes:\")\ndisplay(df.isnull().sum().sort_values(ascending=False))\n\nprint(\"\\nDistribución de la variable objetivo:\")\ndisplay(df[\"Survived\"].value_counts())\ndisplay(df[\"Survived\"].value_counts(normalize=True).rename(\"proporción\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:28.772889Z","iopub.execute_input":"2026-09-25T22:58:28.773236Z","iopub.status.idle":"2026-09-25T22:58:28.843196Z","shell.execute_reply.started":"2026-09-25T22:58:28.773203Z","shell.execute_reply":"2026-09-25T22:58:28.842324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. Visualización simple de la variable objetivo\ncounts = df[\"Survived\"].value_counts().sort_index()\nplt.figure(figsize=(6,4))\nplt.bar([\"No sobrevivió (0)\", \"Sobrevivió (1)\"], counts.values)\nplt.ylabel(\"Número de pasajeros\")\nplt.title(\"Distribución de clases – Titanic\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:28.844149Z","iopub.execute_input":"2026-09-25T22:58:28.844422Z","iopub.status.idle":"2026-09-25T22:58:29.121988Z","shell.execute_reply.started":"2026-09-25T22:58:28.844397Z","shell.execute_reply":"2026-09-25T22:58:29.120988Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n## 2. Selección de características\n\nPara mantener el taller enfocado en los conceptos de la semana, utilizaremos:\n\n- **Numéricas:** `Age`, `SibSp`, `Parch`, `Fare`\n- **Categóricas:** `Pclass`, `Sex`, `Embarked`\n\nNo utilizaremos `Survived` como entrada porque es la **etiqueta** que queremos predecir.\n","metadata":{}},{"cell_type":"code","source":"# 4. X e y\nfeatures = [\"Pclass\", \"Sex\", \"Age\", \"SibSp\", \"Parch\", \"Fare\", \"Embarked\"]\ntarget = \"Survived\"\n\nX = df[features].copy()\ny = df[target].copy()\n\nX_train, X_test, y_train, y_test = train_test_split(\n    X, y,\n    test_size=0.25,\n    stratify=y,\n    random_state=RANDOM_STATE\n)\n\nprint(\"Entrenamiento:\", X_train.shape)\nprint(\"Prueba:\", X_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:29.124369Z","iopub.execute_input":"2026-09-25T22:58:29.124667Z","iopub.status.idle":"2026-09-25T22:58:29.139293Z","shell.execute_reply.started":"2026-09-25T22:58:29.124641Z","shell.execute_reply":"2026-09-25T22:58:29.138336Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 5. Pipeline de preprocesamiento\nnumeric_features = [\"Age\", \"SibSp\", \"Parch\", \"Fare\"]\ncategorical_features = [\"Pclass\", \"Sex\", \"Embarked\"]\n\nnumeric_transformer = Pipeline([\n    (\"imputer\", SimpleImputer(strategy=\"median\")),\n    (\"scaler\", StandardScaler())\n])\n\ncategorical_transformer = Pipeline([\n    (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n    (\"onehot\", OneHotEncoder(handle_unknown=\"ignore\"))\n])\n\npreprocessor = ColumnTransformer([\n    (\"num\", numeric_transformer, numeric_features),\n    (\"cat\", categorical_transformer, categorical_features)\n])\n\nprint(\"Pipeline de preprocesamiento definido.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:29.140866Z","iopub.execute_input":"2026-09-25T22:58:29.141344Z","iopub.status.idle":"2026-09-25T22:58:29.16304Z","shell.execute_reply.started":"2026-09-25T22:58:29.141304Z","shell.execute_reply":"2026-09-25T22:58:29.162122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. Modelos\nrf_titanic = Pipeline([\n    (\"prep\", preprocessor),\n    (\"model\", RandomForestClassifier(\n        n_estimators=200,\n        random_state=RANDOM_STATE\n    ))\n])\n\nsvm_titanic = Pipeline([\n    (\"prep\", preprocessor),\n    (\"model\", SVC(\n        kernel=\"rbf\",\n        probability=True,\n        random_state=RANDOM_STATE\n    ))\n])\n\nrf_titanic.fit(X_train, y_train)\nsvm_titanic.fit(X_train, y_train)\n\nprint(\"Modelos entrenados.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:29.164284Z","iopub.execute_input":"2026-09-25T22:58:29.164625Z","iopub.status.idle":"2026-09-25T22:58:29.657906Z","shell.execute_reply.started":"2026-09-25T22:58:29.164597Z","shell.execute_reply":"2026-09-25T22:58:29.657155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. Función de evaluación\ndef evaluar_clasificador(nombre, modelo, Xtr, ytr, Xte, yte):\n    pred_train = modelo.predict(Xtr)\n    pred_test = modelo.predict(Xte)\n\n    if hasattr(modelo, \"predict_proba\"):\n        prob_test = modelo.predict_proba(Xte)[:, 1]\n    else:\n        prob_test = modelo.decision_function(Xte)\n\n    resultados = {\n        \"Modelo\": nombre,\n        \"Accuracy train\": accuracy_score(ytr, pred_train),\n        \"Accuracy test\": accuracy_score(yte, pred_test),\n        \"Precision\": precision_score(yte, pred_test, zero_division=0),\n        \"Recall\": recall_score(yte, pred_test, zero_division=0),\n        \"F1\": f1_score(yte, pred_test, zero_division=0),\n        \"ROC-AUC\": roc_auc_score(yte, prob_test)\n    }\n    return resultados, pred_test\n\nres_rf, pred_rf = evaluar_clasificador(\n    \"Random Forest\", rf_titanic, X_train, y_train, X_test, y_test\n)\nres_svm, pred_svm = evaluar_clasificador(\n    \"SVM\", svm_titanic, X_train, y_train, X_test, y_test\n)\n\nresultados_titanic = pd.DataFrame([res_rf, res_svm])\nresultados_titanic.round(4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:29.658996Z","iopub.execute_input":"2026-09-25T22:58:29.659368Z","iopub.status.idle":"2026-09-25T22:58:29.824653Z","shell.execute_reply.started":"2026-09-25T22:58:29.659346Z","shell.execute_reply":"2026-09-25T22:58:29.823988Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 8. Matrices de confusión\nfor nombre, pred in [(\"Random Forest\", pred_rf), (\"SVM\", pred_svm)]:\n    fig, ax = plt.subplots(figsize=(5,4))\n    ConfusionMatrixDisplay.from_predictions(y_test, pred, ax=ax)\n    ax.set_title(f\"Matriz de confusión – {nombre}\")\n    plt.show()\n\nprint(\"Reporte Random Forest\")\nprint(classification_report(y_test, pred_rf))\n\nprint(\"Reporte SVM\")\nprint(classification_report(y_test, pred_svm))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:29.825614Z","iopub.execute_input":"2026-09-25T22:58:29.82589Z","iopub.status.idle":"2026-09-25T22:58:30.124492Z","shell.execute_reply.started":"2026-09-25T22:58:29.825858Z","shell.execute_reply":"2026-09-25T22:58:30.123619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 9. Validación cruzada: 5 folds\ncv_rf = cross_val_score(rf_titanic, X, y, cv=5, scoring=\"f1\")\ncv_svm = cross_val_score(svm_titanic, X, y, cv=5, scoring=\"f1\")\n\ncv_table = pd.DataFrame({\n    \"Fold\": np.arange(1, 6),\n    \"RF_F1\": cv_rf,\n    \"SVM_F1\": cv_svm\n})\ndisplay(cv_table.round(4))\n\nprint(f\"RF  F1-CV: {cv_rf.mean():.4f} ± {cv_rf.std():.4f}\")\nprint(f\"SVM F1-CV: {cv_svm.mean():.4f} ± {cv_svm.std():.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:30.125742Z","iopub.execute_input":"2026-09-25T22:58:30.126412Z","iopub.status.idle":"2026-09-25T22:58:32.704092Z","shell.execute_reply.started":"2026-09-25T22:58:30.126378Z","shell.execute_reply":"2026-09-25T22:58:32.703199Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n### Actividad A — interpretación\nResponda en una celda Markdown:\n\n1. ¿Cuál modelo obtuvo mayor F1-score?\n2. ¿Accuracy y F1 conducen a la misma conclusión?\n3. Observe `Accuracy train` y `Accuracy test`. ¿Hay evidencia de overfitting?\n4. ¿Por qué es preferible observar los 5 folds y no una única partición train/test?\n5. A partir de la matriz de confusión, ¿qué tipo de error fue más frecuente: falso positivo o falso negativo?\n","metadata":{}},{"cell_type":"markdown","source":"\n# PARTE B — DATOS NO ESTRUCTURADOS: IMÁGENES DOGS vs. CATS\n\n## 3. ¿Por qué una imagen es un dato no estructurado?\nUna imagen no viene organizada naturalmente como una tabla de variables `edad`, `sexo`, `tarifa`, etc. Está formada por píxeles.\n\nPara utilizar **Random Forest y SVM**, transformaremos cada imagen en un vector numérico sencillo:\n\n1. leer la imagen;\n2. convertirla a escala de grises;\n3. redimensionarla a `32 × 32`;\n4. normalizar los píxeles;\n5. aplanar la matriz (`32 × 32 = 1024 características`).\n\n> Esta representación es deliberadamente simple. Más adelante, en Visión Computacional y Deep Learning, se estudiarán representaciones visuales más potentes.\n","metadata":{}},{"cell_type":"code","source":"# 10. Preparar Dogs vs. Cats\n# Buscamos train.zip o una carpeta con las imágenes.\n\nzip_candidates = [\n    \"/kaggle/input/competitions/dogs-vs-cats/train.zip\",\n    #\"/kaggle/input/dogs-vs-cats/train.zip\",\n    #\"/content/train.zip\",\n    #\"train.zip\"\n]\n\nimage_dirs = [\n    \"/kaggle/working/train\",\n    \"/content/train\",\n    \"train\"\n]\n\nzip_path = next((p for p in zip_candidates if os.path.exists(p)), None)\nimage_dir = next((p for p in image_dirs if os.path.isdir(p) and len(glob.glob(os.path.join(p, \"*.jpg\"))) > 0), None)\n\nif image_dir is None and zip_path is not None:\n    extract_base = \"/kaggle/working\" if os.path.isdir(\"/kaggle/working\") else \"/content\"\n    if not os.path.isdir(extract_base):\n        extract_base = \".\"\n    with zipfile.ZipFile(zip_path, \"r\") as z:\n        z.extractall(extract_base)\n    image_dir = os.path.join(extract_base, \"train\")\n\nif image_dir is None:\n    raise FileNotFoundError(\n        \"No se encontraron las imágenes. Añada Dogs vs. Cats desde Kaggle \"\n        \"o suba train.zip y modifique las rutas si fuera necesario.\"\n    )\n\nall_images = glob.glob(os.path.join(image_dir, \"*.jpg\"))\nprint(\"Carpeta:\", image_dir)\nprint(\"Imágenes disponibles:\", len(all_images))\nprint(\"Ejemplos:\", [os.path.basename(x) for x in all_images[:5]])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:32.70509Z","iopub.execute_input":"2026-09-25T22:58:32.705392Z","iopub.status.idle":"2026-09-25T22:58:39.553963Z","shell.execute_reply.started":"2026-09-25T22:58:32.70536Z","shell.execute_reply":"2026-09-25T22:58:39.553079Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 11. Mostrar ejemplos\nfrom PIL import Image\n\ncat_files = sorted(glob.glob(os.path.join(image_dir, \"cat.*.jpg\")))[:3]\ndog_files = sorted(glob.glob(os.path.join(image_dir, \"dog.*.jpg\")))[:3]\nexamples = cat_files + dog_files\n\nplt.figure(figsize=(12,6))\nfor i, f in enumerate(examples, 1):\n    img = Image.open(f).convert(\"RGB\")\n    plt.subplot(2, 3, i)\n    plt.imshow(img)\n    plt.title(\"Gato\" if os.path.basename(f).startswith(\"cat.\") else \"Perro\")\n    plt.axis(\"off\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:39.554986Z","iopub.execute_input":"2026-09-25T22:58:39.555294Z","iopub.status.idle":"2026-09-25T22:58:40.232847Z","shell.execute_reply.started":"2026-09-25T22:58:39.555261Z","shell.execute_reply":"2026-09-25T22:58:40.231706Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 12. Muestreo y extracción de características\n# Ajuste N_PER_CLASS según el tiempo y memoria disponibles.\nN_PER_CLASS = 1000\nIMG_SIZE = (32, 32)\n\ncat_files = glob.glob(os.path.join(image_dir, \"cat.*.jpg\"))\ndog_files = glob.glob(os.path.join(image_dir, \"dog.*.jpg\"))\n\nrng = random.Random(RANDOM_STATE)\ncat_sample = rng.sample(cat_files, min(N_PER_CLASS, len(cat_files)))\ndog_sample = rng.sample(dog_files, min(N_PER_CLASS, len(dog_files)))\n\nfiles = cat_sample + dog_sample\nlabels = [0] * len(cat_sample) + [1] * len(dog_sample)\n\ndef imagen_a_vector(path, size=IMG_SIZE):\n    img = Image.open(path).convert(\"L\").resize(size)\n    arr = np.asarray(img, dtype=np.float32) / 255.0\n    return arr.flatten()\n\nX_img = np.vstack([imagen_a_vector(f) for f in files])\ny_img = np.asarray(labels)\n\nprint(\"X imágenes:\", X_img.shape)\nprint(\"y:\", y_img.shape)\nprint(\"0 = gato | 1 = perro\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:40.234424Z","iopub.execute_input":"2026-09-25T22:58:40.23471Z","iopub.status.idle":"2026-09-25T22:58:44.047489Z","shell.execute_reply.started":"2026-09-25T22:58:40.234685Z","shell.execute_reply":"2026-09-25T22:58:44.046618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 13. Train/test\nX_img_train, X_img_test, y_img_train, y_img_test = train_test_split(\n    X_img, y_img,\n    test_size=0.25,\n    stratify=y_img,\n    random_state=RANDOM_STATE\n)\n\nprint(\"Train:\", X_img_train.shape)\nprint(\"Test:\", X_img_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:44.050266Z","iopub.execute_input":"2026-09-25T22:58:44.050552Z","iopub.status.idle":"2026-09-25T22:58:44.062893Z","shell.execute_reply.started":"2026-09-25T22:58:44.050526Z","shell.execute_reply":"2026-09-25T22:58:44.062014Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 14. Random Forest y SVM para imágenes\nrf_img = RandomForestClassifier(\n    n_estimators=150,\n    random_state=RANDOM_STATE,\n    n_jobs=-1\n)\n\nsvm_img = Pipeline([\n    (\"scaler\", StandardScaler()),\n    (\"model\", SVC(\n        kernel=\"rbf\",\n        probability=True,\n        random_state=RANDOM_STATE\n    ))\n])\n\nrf_img.fit(X_img_train, y_img_train)\nsvm_img.fit(X_img_train, y_img_train)\n\nprint(\"Modelos de imágenes entrenados.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:44.064148Z","iopub.execute_input":"2026-09-25T22:58:44.064435Z","iopub.status.idle":"2026-09-25T22:58:51.7587Z","shell.execute_reply.started":"2026-09-25T22:58:44.064402Z","shell.execute_reply":"2026-09-25T22:58:51.757987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 15. Evaluación\nres_rf_img, pred_rf_img = evaluar_clasificador(\n    \"Random Forest\", rf_img,\n    X_img_train, y_img_train, X_img_test, y_img_test\n)\nres_svm_img, pred_svm_img = evaluar_clasificador(\n    \"SVM\", svm_img,\n    X_img_train, y_img_train, X_img_test, y_img_test\n)\n\nresultados_imagenes = pd.DataFrame([res_rf_img, res_svm_img])\nresultados_imagenes.round(4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:51.759694Z","iopub.execute_input":"2026-09-25T22:58:51.760013Z","iopub.status.idle":"2026-09-25T22:58:54.726994Z","shell.execute_reply.started":"2026-09-25T22:58:51.759987Z","shell.execute_reply":"2026-09-25T22:58:54.726009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 16. Matrices de confusión\nfor nombre, pred in [(\"Random Forest\", pred_rf_img), (\"SVM\", pred_svm_img)]:\n    fig, ax = plt.subplots(figsize=(5,4))\n    ConfusionMatrixDisplay.from_predictions(\n        y_img_test, pred,\n        display_labels=[\"Gato\", \"Perro\"],\n        ax=ax\n    )\n    ax.set_title(f\"Dogs vs. Cats – {nombre}\")\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:54.728282Z","iopub.execute_input":"2026-09-25T22:58:54.72859Z","iopub.status.idle":"2026-09-25T22:58:55.120421Z","shell.execute_reply.started":"2026-09-25T22:58:54.728564Z","shell.execute_reply":"2026-09-25T22:58:55.119624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 17. Validación cruzada sobre el subconjunto de imágenes\n# 3 folds para controlar el tiempo de ejecución en laboratorio.\ncv_rf_img = cross_val_score(\n    rf_img, X_img, y_img, cv=3, scoring=\"f1\", n_jobs=-1\n)\ncv_svm_img = cross_val_score(\n    svm_img, X_img, y_img, cv=3, scoring=\"f1\"\n)\n\nprint(\"RF  F1-CV:\", np.round(cv_rf_img, 4),\n      \"Media =\", round(cv_rf_img.mean(), 4))\nprint(\"SVM F1-CV:\", np.round(cv_svm_img, 4),\n      \"Media =\", round(cv_svm_img.mean(), 4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:58:55.121586Z","iopub.execute_input":"2026-09-25T22:58:55.121912Z","iopub.status.idle":"2026-09-25T22:59:19.510253Z","shell.execute_reply.started":"2026-09-25T22:58:55.121882Z","shell.execute_reply":"2026-09-25T22:59:19.509292Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 18. Inspección visual de algunas predicciones\nidx = np.random.RandomState(RANDOM_STATE).choice(\n    len(X_img_test), size=min(12, len(X_img_test)), replace=False\n)\n\nplt.figure(figsize=(12, 8))\nfor pos, i in enumerate(idx, 1):\n    img = X_img_test[i].reshape(IMG_SIZE)\n    pred = pred_svm_img[i]\n    real = y_img_test[i]\n    plt.subplot(3, 4, pos)\n    plt.imshow(img, cmap=\"gray\")\n    plt.title(\n        f\"Real: {'Perro' if real else 'Gato'}\\n\"\n        f\"Pred: {'Perro' if pred else 'Gato'}\"\n    )\n    plt.axis(\"off\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-25T22:59:19.511325Z","iopub.execute_input":"2026-09-25T22:59:19.511673Z","iopub.status.idle":"2026-09-25T22:59:20.112536Z","shell.execute_reply.started":"2026-09-25T22:59:19.511645Z","shell.execute_reply":"2026-09-25T22:59:20.111813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n### Actividad B — interpretación\nResponda:\n\n1. ¿Qué modelo obtuvo mejor F1-score con imágenes?\n2. ¿La diferencia entre accuracy de entrenamiento y prueba sugiere overfitting?\n3. Identifique visualmente dos imágenes correctamente clasificadas y dos errores.\n4. ¿Qué información se pierde al convertir una imagen a `32 × 32` y escala de grises?\n5. ¿Por qué una representación basada solamente en píxeles puede ser insuficiente para reconocer objetos?\n","metadata":{}},{"cell_type":"markdown","source":"\n# PARTE C — COMPARACIÓN INTEGRADORA\n\nComplete la tabla con los resultados obtenidos.\n\n| Tipo de dato    | Dataset       | Modelo        |   Accuracy |  Precision |     Recall |         F1 |    ROC-AUC | ¿Overfitting?         |\n| --------------- | ------------- | ------------- | ---------: | ---------: | ---------: | ---------: | ---------: | --------------------- |\n| Estructurado    | Titanic       | Random Forest | **0.7444** | **0.6667** | **0.6744** | **0.6705** | **0.8162** | **Sí**                |\n| Estructurado    | Titanic       | SVM           | **0.7937** | **0.7703** | **0.6628** | **0.7125** | **0.8397** | **No evidente**       |\n| No estructurado | Dogs vs. Cats | Random Forest | **0.6280** | **0.6322** | **0.6120** | **0.6220** | **0.6758** | **Sí**                |\n| No estructurado | Dogs vs. Cats | SVM           | **0.6300** | **0.6182** | **0.6800** | **0.6476** | **0.6816** | **Sí, leve/moderado** |\n\n### Preguntas integradoras\n1. ¿Qué diferencia fundamental encontró entre preparar datos tabulares y preparar imágenes?\n2. ¿Por qué SVM requiere especial atención a la escala de las características?\n3. ¿Por qué no debemos seleccionar un modelo usando únicamente accuracy?\n4. Explique con sus palabras la función de la validación cruzada.\n5. ¿Qué evidencia usaría para afirmar que un modelo está sobreajustando?\n6. ¿El modelo con mejor desempeño en Titanic necesariamente debe ser también el mejor en Dogs vs. Cats? Justifique.\n","metadata":{}},{"cell_type":"markdown","source":"\n# RETO FINAL\n\nRealice **una** de las siguientes modificaciones y documente el resultado:\n\n### Opción 1 — Random Forest\nCambie `n_estimators` a `50`, `100` y `300`. Compare F1 y tiempo de ejecución.\n\n### Opción 2 — SVM\nCompare `kernel=\"linear\"` y `kernel=\"rbf\"`.\n\n### Opción 3 — Imágenes\nCompare imágenes de `16×16`, `32×32` y `64×64`. Analice el compromiso entre información, dimensionalidad y costo computacional.\n\n### Entrega\nEl estudiante debe entregar:\n- notebook completamente ejecutado;\n- tabla comparativa;\n- respuestas de las actividades A y B;\n- reto final;\n- **3 conclusiones técnicas propias**.\n\n**Criterio importante:** no basta con ejecutar código; se evaluará la capacidad de interpretar las métricas y justificar las conclusiones.\n","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# Referencias y recursos\n\n- Kaggle. *Titanic – Machine Learning from Disaster.*\n- Kaggle. *Dogs vs. Cats.*\n- Scikit-learn. *RandomForestClassifier, SVC, model evaluation and cross-validation.*\n- Géron, A. *Hands-On Machine Learning with Scikit-Learn, Keras & TensorFlow.*\n- Bishop, C. M. *Pattern Recognition and Machine Learning.*\n\n**Nota docente:** este taller está alineado con la Semana 4 de la Unidad I del sílabo: Random Forest, SVM, métricas de clasificación, validación cruzada y análisis de overfitting/underfitting.","metadata":{}}]}