{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Una red que reconoce dígitos\n",
    "\n",
    "Todo el libro en un archivo, sin importar ninguna librería de deep learning. Y por fin la red gana.\n",
    "\n",
    "Cuaderno de práctica del capítulo 21 de **Deep learning desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/deep-learning-desde-cero/proyecto-final/\n",
    "\n",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Antes de empezar\n",
    "\n",
    "Esta celda baja el ayudante que corrige tus ejercicios. Después, en cada\n",
    "ejercicio que se pueda corregir solo, vas a ver `%%revisa` arriba de la celda:\n",
    "escribe tu respuesta debajo, ejecuta, y te digo si te salió 💛"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# El ayudante de los cuadernos. Trae la corrección de los ejercicios y, en los\n",
    "# capítulos de consola, la celda mágica que ejecuta los comandos. Se baja en\n",
    "# vez de venir pegado aquí para que siempre sea el último.\n",
    "urllib.request.urlretrieve(\n",
    "    \"https://missyera.com/static/cuadernos/revisa.py\", \"revisa.py\")\n",
    "import revisa\n",
    "revisa.carga({\n",
    "    1: \"cGVzYSAzOC41IEtCCnByZWRpY2UgaWd1YWw6IFRydWU=\",\n",
    "    2: \"IDE2IG5ldXJvbmFzOiAwLjk1NTYKIDY0IG5ldXJvbmFzOiAwLjk3NTYKMTI4IG5ldXJvbmFzOiAwLjk3MTEKCiAgMjAwIHZ1ZWx0YXM6IDAuOTYyMgogMTAwMCB2dWVsdGFzOiAwLjk3NTYKIDMwMDAgdnVlbHRhczogMC45NzEx\",\n",
    "    3: \"ZXNjYWxhIDAuMDEwMDogMC45NjQ0CmVzY2FsYSAwLjEwMDA6IDAuOTY4OQplc2NhbGEgMC4xNzY4OiAwLjk3NTY=\",\n",
    "    4: \"ZXJhIHVuIDggeSBkaWpvIDEgY29uIGNvbmZpYW56YSAwLjg4MAogICAuLisjIysuLgogICAuLisjIyMuLgogICAuLisjIy4uLgogICAuLisjKy4uLgogICAuLisjLi4uLgogICAuLisjKy4uLgogICAuLiMjIy4uLgogICAuLisjIy4uLgoKZXJhIHVuIDAgeSBkaWpvIDQgY29uIGNvbmZpYW56YSAwLjgzOQogICAuLi4jIy4uLgogICAuLi4jIysuLgogICAuLiMjLiMuLgogICAuLiMrLiMuLgogICAuLiMjLiMuLgogICAuLiMrIyMuLgogICAuLiMjIysuLgogICAuLi4jIy4uLg==\",\n",
    "    5: \"IGNvcnRlICAgY29udGVzdGEgICBhY2llcnRhICAgcmV2aXNhbiAgIGVycm9yZXMKIDAuMDAwICAgICAxLjAwMDAgICAgMC45NzU2ICAgICAgICAgMCAgICAgICAgMTEKIDAuODAwICAgICAwLjk1MTEgICAgMC45ODYwICAgICAgICAyMiAgICAgICAgIDYKIDAuOTUwICAgICAwLjg3NTYgICAgMC45OTQ5ICAgICAgICA1NiAgICAgICAgIDIKIDAuOTkwICAgICAwLjc4NjcgICAgMC45OTcyICAgICAgICA5NiAgICAgICAgIDEKIDAuOTk5ICAgICAwLjU2NDQgICAgMS4wMDAwICAgICAgIDE5NiAgICAgICAgIDA=\",\n",
    "    6: \"Y2Fww610dWxvICAyOiBsYSBuZXVyb25hOiBtdWx0aXBsaWNhciwgc3VtYXIsIGFwbGFzdGFyCmNhcMOtdHVsbyAgMzogUmVMVSBlbiBsYSBjYXBhIG9jdWx0YQpjYXDDrXR1bG8gIDQ6IGxhIGNhcGEgb2N1bHRhIHF1ZSBkb2JsYQpjYXDDrXR1bG8gIDU6IGxhIHDDqXJkaWRhIHkgZWwgZGVzY2Vuc28gZGUgZ3JhZGllbnRlCmNhcMOtdHVsbyAgNjogbGEgcmV0cm9wcm9wYWdhY2nDs24gY29uIChQIC0gWSkKY2Fww610dWxvICA3OiBtaXJhciBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhIGEgbGEgdmV6CmNhcMOtdHVsbyAgODogZWwgd2VpZ2h0IGRlY2F5LCBhcXXDrSBzaW4gbmVjZXNpdGFybG8KY2Fww610dWxvIDExOiBsb3MgcMOteGVsZXMgY29tbyBkYXRvIGNvbiBmb3JtYQpjYXDDrXR1bG8gMTM6IGVsIG9uZS1ob3QgZGVsIG9iamV0aXZvCmNhcMOtdHVsbyAxNzogbGEgc29mdG1heCBlc3RhYmxl\",\n",
    "    7: \"VmFsdWVFcnJvcjogb3BlcmFuZHMgY291bGQgbm90IGJlIGJyb2FkY2FzdCB0b2dldGhlciB3aXRoIHNoYXBlcyAoNDUwLDEwKSAoNDUwLCkg\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Trece capítulos diciendo dónde la red pierde. Este es donde gana 🎉\n",
    "\n",
    "Y una pregunta antes de empezar: **¿en qué dígito crees que se va a equivocar más?** Apuesta ahora, porque al final vamos a mirar los once errores uno por uno 🔢\n",
    "\n",
    "Vamos a escribir una red completa, de principio a fin, sin importar ni una\n",
    "librería de deep learning. Y va a reconocer números escritos a mano."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El encargo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1.797 imágenes de dígitos de 8 por 8, las mismas del capítulo 11. Hay que\n",
    "decir qué número es cada una, de 0 a 9.\n",
    "\n",
    "Y ojo con eso último, porque es nuevo: **hasta ahora todo el libro fue\n",
    "sí o no**. Aquí hay diez respuestas posibles, y eso cambia dos piezas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from sklearn.datasets import load_digits\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import accuracy_score, confusion_matrix\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "digitos = load_digits()\n",
    "X = digitos.images.reshape(len(digitos.images), -1) / 16.0\n",
    "y = digitos.target\n",
    "\n",
    "i_tr, i_te = train_test_split(np.arange(len(X)), test_size=0.25,\n",
    "                              random_state=42, stratify=y)\n",
    "X_tr, X_te = X[i_tr], X[i_te]\n",
    "y_tr, y_te = y[i_tr], y[i_te]\n",
    "\n",
    "Y = np.zeros((len(y_tr), 10))              # el objetivo, una columna por dígito\n",
    "Y[np.arange(len(y_tr)), y_tr] = 1\n",
    "\n",
    "print('entreno con', len(X_tr), 'imágenes de', X.shape[1], 'píxeles')\n",
    "print('el objetivo pasa de', y_tr.shape, 'a', Y.shape)\n",
    "print('la primera imagen es un', y_tr[0], 'y su fila objetivo es', Y[0].astype(int))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese `Y` con una columna por dígito es el one-hot del capítulo 13,\n",
    "ahora aplicado a la *respuesta* en vez de a las entradas. La red va a\n",
    "sacar diez números y compararlos con esa fila 🔟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Las dos piezas nuevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para diez clases cambia la activación de salida y cambia la pérdida:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def softmax(z):\n",
    "    z = z - z.max(axis=-1, keepdims=True)\n",
    "    e = np.exp(z)\n",
    "    return e / e.sum(axis=-1, keepdims=True)\n",
    "\n",
    "def perdida(P, Y):\n",
    "    return float(-np.mean(np.sum(Y * np.log(np.clip(P, 1e-12, 1)), axis=1)))\n",
    "\n",
    "ejemplo = softmax(np.array([[2.0, 0.5, -1.0]]))\n",
    "print('tres puntajes se vuelven:', np.round(ejemplo, 4), ' suman', round(float(ejemplo.sum()), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La **softmax** del capítulo 3 en vez de la sigmoide, para que las\n",
    "diez salidas sumen 1 y se puedan leer como probabilidades que compiten.\n",
    "\n",
    "Y la **entropía cruzada categórica**, que es la del capítulo 5\n",
    "generalizada: en vez de mirar una probabilidad, mira la de la clase correcta.\n",
    "\n",
    "Y aquí está lo bonito: aunque las dos fórmulas cambien,\n",
    "**`P - Y` sigue siendo el gradiente**, exactamente igual\n",
    "que con una sola salida. La pareja softmax y entropía cruzada se simplifica igual\n",
    "de bien que sigmoide y entropía cruzada. Por eso van siempre juntas 🎯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La red entera"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "L=−∑kyklogpk\n",
    "\n",
    "solo mira la probabilidad que el modelo le dio a la clase correcta, y la castiga con un logaritmo que se dispara cuando esa probabilidad es chica"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def entrena(ocultas=64, vueltas=1000, paso=0.5, semilla=0, decay=0.0):\n",
    "    r = np.random.default_rng(semilla)\n",
    "    entradas = X_tr.shape[1]\n",
    "    # arranque con la escala de He, que es la que va con ReLU\n",
    "    W1 = r.normal(0, np.sqrt(2 / entradas), (entradas, ocultas))\n",
    "    b1 = np.zeros(ocultas)\n",
    "    W2 = r.normal(0, np.sqrt(2 / ocultas), (ocultas, 10))\n",
    "    b2 = np.zeros(10)\n",
    "\n",
    "    for _ in range(vueltas):\n",
    "        h = np.maximum(0, X_tr @ W1 + b1)          # ReLU, capítulo 3\n",
    "        P = softmax(h @ W2 + b2)\n",
    "        d2 = (P - Y) / len(Y)                      # capítulo 5\n",
    "        d1 = (d2 @ W2.T) * (h > 0)                 # capítulo 6\n",
    "        W2 -= paso * (h.T @ d2 + decay * W2)       # capítulo 8\n",
    "        b2 -= paso * d2.sum(axis=0)\n",
    "        W1 -= paso * (X_tr.T @ d1 + decay * W1)\n",
    "        b1 -= paso * d1.sum(axis=0)\n",
    "    return W1, b1, W2, b2\n",
    "\n",
    "def predice(Z, W1, b1, W2, b2):\n",
    "    return softmax(np.maximum(0, Z @ W1 + b1) @ W2 + b2)\n",
    "\n",
    "W1, b1, W2, b2 = entrena()\n",
    "p_tr = predice(X_tr, W1, b1, W2, b2).argmax(axis=1)\n",
    "p_te = predice(X_te, W1, b1, W2, b2).argmax(axis=1)\n",
    "print('acierto entrenando:', round(accuracy_score(y_tr, p_tr), 4))\n",
    "print('acierto en prueba :', round(accuracy_score(y_te, p_te), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está, y con eso ya terminó el libro en lo esencial 💛\n",
    "\n",
    "Una red de una capa oculta, escrita en veinte líneas, reconoce números escritos\n",
    "a mano con casi 98% de acierto. Sin TensorFlow, sin PyTorch, sin nada.\n",
    "\n",
    "Fíjate en el arranque de los pesos, que es lo único que no habíamos visto:\n",
    "`sqrt(2 / entradas)`. Se llama inicialización de He y está pensada para\n",
    "ReLU. Si arrancas con la escala 0,1 de los capítulos anteriores, con 64 entradas\n",
    "por neurona la suma sale demasiado chica y la red tarda mucho más en arrancar."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora sí: contra la logística"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "logistica = LogisticRegression(max_iter=3000).fit(X_tr, y_tr)\n",
    "print('logística:', round(accuracy_score(y_te, logistica.predict(X_te)), 4))\n",
    "print('nuestra red:', round(accuracy_score(y_te, p_te), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "0,9756 la red contra 0,9622 la logística 🏆\n",
    "\n",
    "Después de trece capítulos perdiendo, gana. Y gana en el sitio donde el\n",
    "capítulo 1 dijo que ganaría: **cuando el dato tiene estructura**.\n",
    "\n",
    "Es un punto y tres décimas de diferencia, que parece poco y no lo es: sobre 450\n",
    "imágenes son seis errores menos. En un sistema que procesa cheques o formularios,\n",
    "eso es la diferencia entre revisar seis cosas a mano o no.\n",
    "\n",
    "Y ahora fíjate en algo que en el capítulo 7 era la señal de alarma: el\n",
    "acierto en entrenamiento es **1,0000**, o sea perfecto. Allá eso\n",
    "venía con una prueba de 0,5870 y significaba memorización pura.\n",
    "\n",
    "Aquí viene con 0,9756 en prueba. La red se aprendió las 1.347 imágenes\n",
    "*y además* generaliza.\n",
    "\n",
    "Eso es lo que cambia cuando el dato tiene estructura: no es que memorizar deje\n",
    "de ocurrir, es que lo que aprende de memoria **también sirve para las\n",
    "imágenes nuevas**. Por eso aquí no hicieron falta ni parada temprana ni\n",
    "frenos 🧠"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## En qué se equivoca"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "M = confusion_matrix(y_te, p_te)\n",
    "print('errores:', int((p_te != y_te).sum()), 'de', len(y_te))\n",
    "print()\n",
    "for i in range(10):\n",
    "    for j in range(10):\n",
    "        if i != j and M[i, j] >= 2:\n",
    "            print(f'   confundió el {i} con el {j}: {M[i, j]} veces')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once errores en 450 imágenes. Y los que repite son el 8 con el 1 (tres veces)\n",
    "y el 0 con el 4 (dos veces).\n",
    "\n",
    "El 8 con el 1 tiene sentido: en 8 por 8 píxeles, un 8 mal escrito es un trazo\n",
    "vertical con dos bultitos, y un 1 es un trazo vertical. El 0 con el 4 es más\n",
    "raro, y es lo que yo iría a mirar imagen por imagen antes de entregar\n",
    "esto 🔍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ¿Sabe cuándo duda?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La pregunta del capítulo 13 del libro de machine learning, ahora aquí:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "probabilidades = predice(X_te, W1, b1, W2, b2)\n",
    "confianza = probabilidades.max(axis=1)\n",
    "fallo = p_te != y_te\n",
    "\n",
    "print('confianza media cuando acierta:', round(float(confianza[~fallo].mean()), 4))\n",
    "print('confianza media cuando falla  :', round(float(confianza[fallo].mean()), 4))\n",
    "print()\n",
    "for corte in [0.5, 0.9, 0.99]:\n",
    "    seguros = confianza >= corte\n",
    "    print(f'si solo contesto con confianza >= {corte}: '\n",
    "          f'contesto {seguros.mean():.4f} de las veces y acierto '\n",
    "          f'{accuracy_score(y_te[seguros], p_te[seguros]):.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sí sabe 🎯\n",
    "\n",
    "Cuando acierta, su confianza media es 0,9742. Cuando falla, 0,7159. Son\n",
    "veintiséis puntos de diferencia, y eso convierte la confianza en una señal\n",
    "utilizable.\n",
    "\n",
    "Y la tabla de abajo es el producto de verdad: **pidiéndole confianza de\n",
    "al menos 0,9 contesta el 91,78% de las veces y en esas acierta el\n",
    "99,52%**. El 8% restante se manda a una persona.\n",
    "\n",
    "Ese es exactamente el diseño de un sistema de lectura de formularios que\n",
    "funciona en producción. No uno que acierta siempre: uno que sabe cuáles pasar a\n",
    "revisión 🤝"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y esto para qué le sirve a una distribuidora"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Porque el libro empezó con las ventas de una distribuidora peruana y termina\n",
    "con dígitos, y conviene cerrar el círculo 🔄\n",
    "\n",
    "En el capítulo 1 medimos que sobre esa tabla de 3.000 ventas la red sacaba\n",
    "0,5834 y la regresión logística 0,7214. Aquí sobre imágenes la red saca 0,9756 y\n",
    "la logística 0,9622. **El mismo tipo de modelo, resultados invertidos**,\n",
    "y la diferencia no está en el modelo: está en si el dato tiene forma.\n",
    "\n",
    "Así que la pregunta útil no es \"¿usamos deep learning?\", es **\"¿qué\n",
    "tenemos que no entre en una tabla?\"**. En una distribuidora eso suele\n",
    "existir y suele estar tirado:\n",
    "\n",
    "- 📝 Pedidos anotados a mano por el vendedor, que alguien transcribe.\n",
    "\n",
    "- 🧾 Facturas y guías escaneadas de las que hay que sacar números.\n",
    "\n",
    "- 📸 Fotos de anaquel para ver si el producto está donde debería.\n",
    "\n",
    "- 💬 Comentarios y reclamos de clientes en texto libre.\n",
    "\n",
    "Los tres primeros son este capítulo con imágenes más grandes. El cuarto son\n",
    "los capítulos 13, 16 y 18.\n",
    "\n",
    "Y para lo que sí entra en una tabla, o sea a quién visitar y quién va a\n",
    "comprar, la respuesta sigue siendo el libro anterior: una regresión logística,\n",
    "que se entrena en dos décimas de segundo y se explica leyendo sus\n",
    "coeficientes 💛"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que se entrega"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Como en el proyecto final del libro de machine learning, esto no se entrega en\n",
    "un cuaderno:\n",
    "\n",
    "- 💾 Los cuatro arrays (W1, b1, W2, b2) guardados con\n",
    "`np.savez`. Pesan poco y no dependen de ninguna librería.\n",
    "\n",
    "- 📄 Una página con qué predice, con qué datos, cuánto acierta (0,9756), contra\n",
    "qué se compara (0,9622) y en qué se equivoca (8 con 1, 0 con 4).\n",
    "\n",
    "- 🚦 El umbral de confianza elegido y qué porcentaje manda a revisión.\n",
    "\n",
    "- 📐 La lista de tamaños de entrada, porque el capítulo 11 nos enseñó que ahí es\n",
    "donde revientan las cosas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Guardar el modelo y volver a cargarlo\n",
    "\n",
    "Guarda los pesos y comprueba que predice igual."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Cuántas neuronas y cuántas vueltas\n",
    "\n",
    "Barre las dos cosas y busca el punto."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Sin la inicialización de He\n",
    "\n",
    "Arranca con la escala 0,1 de los capítulos anteriores y\n",
    "compara."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Ver los errores uno por uno\n",
    "\n",
    "Imprime en texto las imágenes que falló."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. El umbral que decide cuánto revisa una persona\n",
    "\n",
    "Arma la tabla de decisión con el costo de cada opción."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Todo lo que usamos, capítulo por capítulo\n",
    "\n",
    "Repasa qué pieza salió de dónde."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El error del one-hot al revés\n",
    "\n",
    "Compara la predicción con las etiquetas sin convertirlas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 7\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "La red del proyecto final saca resultados parecidos a la logística sobre la misma tabla. ¿Qué se entrega?\n",
    "\n",
    "a) Los dos, con lo que cuesta mantener cada uno al lado\n",
    "\n",
    "b) La red, porque es lo que se pidió\n",
    "\n",
    "c) La logística, y se tira el trabajo de la red\n",
    "\n",
    "d) Un promedio de los dos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El 97,56% que se cae al salir a la calle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "La red saca 97,56% con los dígitos del conjunto, así que se conecta a la aplicación del celular para que la gente fotografíe los albaranes.\n",
    "\n",
    "```\n",
    "foto = camara.capturar()\n",
    "digito = red.predice(foto)\n",
    "\n",
    "# en el conjunto: 97,56%\n",
    "# en las fotos reales: 40 y pico\n",
    "```\n",
    "\n",
    "**¿Qué está mal?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 🏆 Una red escrita entera en numpy clasifica dígitos al 97,56% contra el\n",
    "96,22% de una regresión logística. Por fin gana, y gana donde el capítulo 1\n",
    "dijo.\n",
    "\n",
    "- 🔟 Para diez clases cambian dos piezas: softmax en la salida y entropía\n",
    "cruzada categórica. Y el gradiente sigue siendo `P - Y`.\n",
    "\n",
    "- 🧠 El acierto en entrenamiento es 1,0000 y aquí eso no es alarma: con\n",
    "estructura de verdad, lo que aprende de memoria también sirve para lo nuevo.\n",
    "\n",
    "- 🎬 La inicialización de He vale un punto entero de acierto, y es de lo que\n",
    "menos se habla.\n",
    "\n",
    "- 🎯 Su confianza media es 0,9742 cuando acierta y 0,7159 cuando falla: el\n",
    "modelo sabe cuándo duda.\n",
    "\n",
    "- 🤝 Con confianza mínima de 0,9 contesta el 91,78% de las veces y acierta el\n",
    "99,52%. Y con 0,999 no pasa ni un error, revisando 196 de 450.\n",
    "\n",
    "- 💾 Se entrega en cuatro arrays de numpy, sin dependencias que se rompan.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un modelo no falla cuando se equivoca. Falla cuando se equivoca con toda la confianza del mundo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y para seguir, el paso natural no es más deep learning: es saber evaluar bien lo que construyes, que es el [libro de machine learning desde cero](https://missyera.com/guias/machine-learning-desde-cero/) 🎯\n",
    "\n",
    "En el capítulo 22 te dejo dónde seguir, qué librerías tocan ahora y qué cosas\n",
    "este libro no cubre.\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## De entender la red a poner una a trabajar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sabes qué hace una capa, por qué la retropropagación funciona y cuándo un transformer es exagerado para tu problema. Ese criterio es la parte difícil y ya la tienes 🐣\n",
    "\n",
    "La parte que queda es la menos glamorosa y la que decide si el proyecto sirve: medir si el sistema hace lo que prometió, con qué se compara, y qué haces el día que empieza a responder cualquier cosa. En [el curso de ingeniería de IA en producción](https://missyera.com/cursos/ingenieria-de-ia/) eso es el hilo de las cuatro semanas.\n",
    "\n",
    "Y ojo con algo que veo seguido: mucha gente sale de un libro como este queriendo entrenar su propia red desde cero, y casi nunca es lo que toca. Elegir bien qué modelo usar y saber medirlo rinde mucho más que entrenar el tuyo, y esa decisión también es parte del curso."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 21 de **Deep learning desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/deep-learning-desde-cero/proyecto-final/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
