{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "m4-s12-01",
   "metadata": {},
   "source": "# M4 · Sesión 12 — Evaluación del módulo y defensa\n\n**Deep Learning y Visión Artificial** — 6 ECTS  \nMáster en Inteligencia Artificial, Automatización y Agentes Digitales · Next Educación · Próxima edición\n\n---\n\n## Práctica de la sesión: Prueba práctica y defensa\n\nEvaluación sumativa del módulo.\n\n| | |\n|---|---|\n| **Stack** | Entorno del máster |\n| **Entregable** | Prueba resuelta y defensa realizada. |\n\n### Objetivos que se evalúan\n\n1. Defender oralmente las decisiones de arquitectura y entrenamiento.\n2. Demostrar competencia práctica en deep learning aplicado.\n3. Cerrar el módulo con el modelo listo para el siguiente.\n\n> **Cómo se corrige.** No se evalúa solo el resultado: se evalúan la justificación escrita de cada decisión, la honestidad de la validación y que el cuaderno se ejecute de principio a fin en un equipo distinto al tuyo."
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-02",
   "metadata": {},
   "source": "---\n## 0. Entorno\n\nEjecuta esta celda antes que ninguna otra. Fija la semilla y el estilo de figuras que se usa en todo el máster, de modo que los resultados sean comparables entre alumnos."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-03",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "import platform\nimport numpy as np\nimport pandas as pd\nimport matplotlib\nimport matplotlib.pyplot as plt\n\nRANDOM_STATE = 42\nrng = np.random.default_rng(RANDOM_STATE)\nnp.random.seed(RANDOM_STATE)\n\npd.set_option(\"display.width\", 118)\npd.set_option(\"display.max_columns\", 40)\nplt.rcParams.update({\n    \"figure.figsize\": (9, 4.2), \"figure.dpi\": 110, \"axes.grid\": True,\n    \"grid.alpha\": .25, \"axes.spines.top\": False, \"axes.spines.right\": False,\n    \"font.size\": 10, \"axes.titlesize\": 12, \"axes.titleweight\": \"bold\",\n    \"axes.titlecolor\": \"#12395e\", \"axes.labelcolor\": \"#34495e\",\n})\nAZUL, OSCURO, ROJO = \"#2a76dd\", \"#12395e\", \"#e74c3c\"\n\nprint(\"Python\", platform.python_version(), \"| pandas\", pd.__version__,\n      \"| numpy\", np.__version__, \"| matplotlib\", matplotlib.__version__)"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-04",
   "metadata": {},
   "source": "---\n## 1. Datos de la sesión: imágenes sintéticas etiquetadas\n\nSe generan formas geométricas de 32×32 píxeles con ruido controlado. El conjunto permite entrenar y depurar sin descargas, y traslada los mismos problemas que un dataset real: desbalanceo, ruido y ambigüedad entre clases."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-05",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "TAM, N = 32, 1200\nCLASES = [\"circulo\", \"cuadrado\", \"franja\"]\n\ndef dibuja(clase, ruido=.16):\n    img = np.zeros((TAM, TAM), dtype=float)\n    yy, xx = np.mgrid[0:TAM, 0:TAM]\n    cx, cy = rng.integers(11, 21, 2)\n    if clase == \"circulo\":\n        img[(xx - cx) ** 2 + (yy - cy) ** 2 <= rng.integers(5, 9) ** 2] = 1\n    elif clase == \"cuadrado\":\n        l = rng.integers(9, 15); img[cy - l // 2:cy + l // 2, cx - l // 2:cx + l // 2] = 1\n    else:\n        g = rng.integers(4, 8); img[:, cx - g // 2:cx + g // 2] = 1\n    return np.clip(img + rng.normal(0, ruido, (TAM, TAM)), 0, 1)\n\netiquetas = rng.choice(CLASES, N, p=[.45, .35, .20])       # desbalanceo deliberado\nX = np.stack([dibuja(c) for c in etiquetas])\ny = pd.Series(etiquetas, name=\"clase\")\n\nprint(\"X:\", X.shape, \"| rango [%.2f, %.2f]\" % (X.min(), X.max()))\ny.value_counts().rename(\"imágenes\").to_frame()"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-06",
   "metadata": {},
   "source": "### 1.1 Perfilado\n\nAntes de tocar nada, mirar el dato. Este bloque es el mínimo exigible en cualquier entrega del módulo."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-07",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "estad = pd.DataFrame({\n    \"media_intensidad\": X.reshape(len(X), -1).mean(1),\n    \"desv_intensidad\": X.reshape(len(X), -1).std(1),\n    \"pixeles_activos\": (X > .5).reshape(len(X), -1).sum(1),\n    \"clase\": y,\n})\nprint(\"Estadísticos por clase (útiles para detectar fuga por atajo visual):\")\nestad.groupby(\"clase\").agg([\"mean\", \"std\"]).round(3)"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-08",
   "metadata": {},
   "source": "### 1.2 Lectura visual\n\nUna figura que responde a una pregunta concreta, no un gráfico decorativo."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-09",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "fig, axes = plt.subplots(3, 6, figsize=(10, 5.4))\nfor fila, clase in enumerate(CLASES):\n    idx = np.flatnonzero(y == clase)[:6]\n    for col, i in enumerate(idx):\n        axes[fila, col].imshow(X[i], cmap=\"gray\", vmin=0, vmax=1)\n        axes[fila, col].set_xticks([]); axes[fila, col].set_yticks([]); axes[fila, col].grid(False)\n    axes[fila, 0].set_ylabel(clase, color=OSCURO, fontweight=\"bold\")\nfig.suptitle(\"Muestra del conjunto por clase\", color=OSCURO, fontweight=\"bold\")\nplt.tight_layout(); plt.show()"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-10",
   "metadata": {},
   "source": "---\n## 2. Ejemplo trabajado: una línea base sin red neuronal\n\nAntes de entrenar nada profundo, conviene saber cuánto se consigue con estadísticos elementales. Ese número es la referencia contra la que se juzga después la red."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-11",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "from collections import Counter\n\ncarac = np.c_[X.reshape(len(X), -1).mean(1), (X > .5).reshape(len(X), -1).sum(1)]\ncorte = int(.7 * len(X))\nXtr, Xte, ytr, yte = carac[:corte], carac[corte:], y[:corte].to_numpy(), y[corte:].to_numpy()\n\ncentroides = {c: Xtr[ytr == c].mean(0) for c in CLASES}\npred = np.array([min(centroides, key=lambda c: np.linalg.norm(x - centroides[c])) for x in Xte])\n\ntrivial = Counter(ytr).most_common(1)[0][0]\nprint(f\"Línea base trivial (siempre '{trivial}') : {(yte == trivial).mean():.1%}\")\nprint(f\"Centroide sobre 2 características        : {(pred == yte).mean():.1%}\")\nprint(\"\\nCualquier modelo posterior tiene que superar claramente estas dos cifras para justificarse.\")"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-12",
   "metadata": {},
   "source": "---\n## 3. Tu turno: Prueba práctica y defensa\n\nA partir de aquí trabajas tú. Cada paso tiene una celda de código y una celda de justificación: **las dos se corrigen**. Si adaptas la práctica a tu propio proyecto, indícalo en el primer paso."
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-13",
   "metadata": {},
   "source": "### 3.1 Resolver la prueba práctica."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-14",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 1: Resolver la prueba práctica.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-15",
   "metadata": {},
   "source": "> **Justificación del paso 1.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-16",
   "metadata": {},
   "source": "### 3.2 Defender el sistema de visión artificial."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-17",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 2: Defender el sistema de visión artificial.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-18",
   "metadata": {},
   "source": "> **Justificación del paso 2.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-19",
   "metadata": {},
   "source": "### 3.3 Recibir devolución."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-20",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "# Paso 3: Recibir devolución.\n# TODO\n"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-21",
   "metadata": {},
   "source": "> **Justificación del paso 3.** _(dos o tres frases)_ Qué has decidido, con qué criterio, y qué alternativa has descartado y por qué."
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-22",
   "metadata": {},
   "source": "---\n## 4. Cierre y entrega\n\n**Entregable:** Prueba resuelta y defensa realizada.\n\nAntes de subir el cuaderno al Campus Virtual, repasa la lista. La celda de abajo verifica la parte que puede verificarse sola; el resto lo marcas tú al completarlo."
  },
  {
   "cell_type": "code",
   "id": "m4-s12-23",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": "comprobaciones = {\n    \"Semilla aleatoria fijada\": \"RANDOM_STATE\" in dir(),\n    \"Sin rutas absolutas del equipo\": True,\n    \"Justificacion escrita en todos los pasos\": False,\n    \"Ejecutado de arriba abajo sin errores\": False,\n    \"Sin datos personales ni credenciales\": True,\n}\nfor k, v in comprobaciones.items():\n    print((\"  OK   \" if v else \" FALTA \") + \"| \" + k)\n\nprint()\nprint(\"Listo para entregar.\" if all(comprobaciones.values())\n      else \"Completa lo que falta antes de entregar.\")"
  },
  {
   "cell_type": "markdown",
   "id": "m4-s12-24",
   "metadata": {},
   "source": "---\n*M4 · Sesión 12 — Evaluación del módulo y defensa · Máster en Inteligencia Artificial, Automatización y Agentes Digitales · Next Educación*  \n*Material docente elaborado por Sami Halawa Ribas. Pendiente de validación por la Dirección del programa.*"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11",
   "mimetype": "text/x-python",
   "file_extension": ".py",
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}