{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Frenar el sobreajuste\n",
    "\n",
    "Penalizar los pesos, apagar neuronas al azar y ensuciar las entradas. Uno funciona, uno ayuda a medias y uno casi no.\n",
    "\n",
    "Cuaderno de práctica del capítulo 8 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/frenar-el-sobreajuste/\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: \"ZGVjYXkgMC4wICAgIGVudHJlbmEgMC44ODQ3ICBwcnVlYmEgMC42MjEwICBicmVjaGEgKzAuMjYzNgpkZWNheSAwLjAwMyAgZW50cmVuYSAwLjc1MDIgIHBydWViYSAwLjcxMjIgIGJyZWNoYSArMC4wMzgwCmRlY2F5IDAuMDEgICBlbnRyZW5hIDAuNzE5NCAgcHJ1ZWJhIDAuNzIwNyAgYnJlY2hhIC0wLjAwMTMKZGVjYXkgMC4wMyAgIGVudHJlbmEgMC43MTQ2ICBwcnVlYmEgMC43MTc5ICBicmVjaGEgLTAuMDAzMgpkZWNheSAwLjEgICAgZW50cmVuYSAwLjcwNzIgIHBydWViYSAwLjcxNDcgIGJyZWNoYSAtMC4wMDc1CmRlY2F5IDAuMyAgICBlbnRyZW5hIDAuNTAwMCAgcHJ1ZWJhIDAuNTAwMCAgYnJlY2hhICswLjAwMDA=\",\n",
    "    2: \"ICAzMiBuZXVyb25hcywgZHJvcG91dCAwLjA6IGVudHJlbmEgMC44ODQ3ICBwcnVlYmEgMC42MjEwCiAgMzIgbmV1cm9uYXMsIGRyb3BvdXQgMC41OiBlbnRyZW5hIDAuNDk2NCAgcHJ1ZWJhIDAuNTE4NQogMTI4IG5ldXJvbmFzLCBkcm9wb3V0IDAuMDogZW50cmVuYSAwLjkzNjQgIHBydWViYSAwLjYzNjUKIDEyOCBuZXVyb25hcywgZHJvcG91dCAwLjU6IGVudHJlbmEgMC43OTczICBwcnVlYmEgMC42OTk3\",\n",
    "    3: \"bG9zIGRvcyBqdW50b3M6IGVudHJlbmEgMC43MTc5ICBwcnVlYmEgMC43MjA3CnNvbG8gZGVjYXkgZXJhOiBlbnRyZW5hIDAuNzE5NCAgcHJ1ZWJhIDAuNzIwNyAgIChkZSBsYSB0YWJsYSBkZSBhcnJpYmEp\",\n",
    "    4: \"ZGVjYXkgMC4wOiBwZXNvIG1lZGlvIDAuMjQxNSAgZWwgbcOhcyBncmFuZGUgMS4xNDE5CmRlY2F5IDAuMDE6IHBlc28gbWVkaW8gMC4wMjA4ICBlbCBtw6FzIGdyYW5kZSAwLjMwMTg=\",\n",
    "    5: \"Y29uIDExMjUgZmlsYXM6IDAuNTcwMQpjb24gMjI1MCBmaWxhczogMC42MjEw\",\n",
    "    6: \"Z3JhZGllbnRlIGNvbiBtw6FzY2FyYSwgbWVkaWE6IDAuMDAyMjg3CmdyYWRpZW50ZSBzaW4gbcOhc2NhcmEsIG1lZGlhOiAwLjAwMjIzNArCv3NvbiBpZ3VhbGVzPyBGYWxzZQ==\",\n",
    "    7: \"VmFsdWVFcnJvcjogSW5wdXQgY29udGFpbnMgTmFOLg==\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 7 la red memorizó las 2.250 filas y su prueba se hundió. Aquí\n",
    "vamos a impedírselo, con las tres herramientas que se usan de verdad 🛑\n",
    "\n",
    "Y te pregunto antes de empezar: **¿cuál de los tres frenos crees que funciona mejor?** Apuesta ahora, porque la respuesta medida no es la que yo esperaba 🎯\n",
    "\n",
    "Y las vamos a escribir nosotras, que son tres o cuatro líneas cada una."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El punto de partida, con el problema puesto a propósito"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from sklearn.compose import ColumnTransformer\n",
    "from sklearn.impute import SimpleImputer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import roc_auc_score\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import OneHotEncoder, StandardScaler\n",
    "\n",
    "URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv'\n",
    "\n",
    "def sigmoide(z):\n",
    "    return 1 / (1 + np.exp(-z))\n",
    "\n",
    "def carga_limpia(url):\n",
    "    v = pd.read_csv(url).drop_duplicates()\n",
    "    v['ciudad'] = (v['ciudad'].str.strip().str.lower()\n",
    "                   .str.normalize('NFKD')\n",
    "                   .str.encode('ascii', 'ignore').str.decode('utf-8'))\n",
    "    v['monto'] = pd.to_numeric(v['monto'].str.replace(',', '.'))\n",
    "    for col in ['fecha', 'fecha_ultima_compra']:\n",
    "        f = pd.to_datetime(v[col], format='%Y-%m-%d', errors='coerce')\n",
    "        falta = f.isna() & v[col].notna()\n",
    "        f[falta] = pd.to_datetime(v.loc[falta, col], format='%d/%m/%Y', errors='coerce')\n",
    "        v[col] = f\n",
    "    return v\n",
    "\n",
    "def prepara(v):\n",
    "    v = v.sort_values(['cliente_id', 'fecha']).copy()\n",
    "    v['sin_compra_previa'] = v['fecha_ultima_compra'].isna().astype(int)\n",
    "    v['sin_descuento'] = v['descuento'].isna().astype(int)\n",
    "    v['sin_satisfaccion'] = v['satisfaccion'].isna().astype(int)\n",
    "    v['precio_unitario'] = v['monto'] / v['unidades']\n",
    "    v['visita_numero'] = v.groupby('cliente_id').cumcount() + 1\n",
    "    return v\n",
    "\n",
    "NUMERICAS = ['unidades', 'monto', 'descuento', 'satisfaccion', 'precio_unitario',\n",
    "             'sin_compra_previa', 'sin_descuento', 'sin_satisfaccion', 'visita_numero']\n",
    "CATEGORICAS = ['ciudad', 'segmento', 'canal', 'categoria']\n",
    "\n",
    "datos = prepara(carga_limpia(URL))\n",
    "X_tr, X_te, y_tr, y_te = train_test_split(\n",
    "    datos[NUMERICAS + CATEGORICAS], datos['compro'],\n",
    "    test_size=0.25, random_state=42, stratify=datos['compro'])\n",
    "\n",
    "logistica = Pipeline([\n",
    "    ('pre', ColumnTransformer([\n",
    "        ('num', Pipeline([('r', SimpleImputer(strategy='median')),\n",
    "                          ('e', StandardScaler())]), NUMERICAS),\n",
    "        ('cat', Pipeline([('r', SimpleImputer(strategy='most_frequent')),\n",
    "                          ('c', OneHotEncoder(handle_unknown='ignore'))]), CATEGORICAS),\n",
    "    ])),\n",
    "    ('mod', LogisticRegression(max_iter=1000, random_state=42)),\n",
    "]).fit(X_tr, y_tr)\n",
    "\n",
    "T_tr = logistica.named_steps['pre'].transform(X_tr)\n",
    "T_te = logistica.named_steps['pre'].transform(X_te)\n",
    "objetivo = y_tr.values.astype(float).reshape(-1, 1)\n",
    "print('el listón:', round(roc_auc_score(y_te, logistica.predict_proba(X_te)[:, 1]), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Los tres frenos, en la misma función"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fíjate en lo poco que ocupa cada uno. Están señalados con comentarios:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def corre(ocultas=32, vueltas=2000, paso=0.5, semilla=0,\n",
    "          decay=0.0, dropout=0.0, ruido=0.0):\n",
    "    r = np.random.default_rng(semilla)\n",
    "    columnas = T_tr.shape[1]\n",
    "    W1 = r.normal(0, 0.1, (columnas, ocultas)); b1 = np.zeros(ocultas)\n",
    "    W2 = r.normal(0, 0.1, (ocultas, 1)); b2 = np.zeros(1)\n",
    "    n = len(objetivo)\n",
    "\n",
    "    for _ in range(vueltas):\n",
    "        # FRENO 3: ensuciar las entradas\n",
    "        A = T_tr + r.normal(0, ruido, T_tr.shape) if ruido else T_tr\n",
    "\n",
    "        h = np.tanh(A @ W1 + b1)\n",
    "\n",
    "        # FRENO 2: apagar neuronas al azar\n",
    "        if dropout:\n",
    "            mascara = (r.random(h.shape) > dropout) / (1 - dropout)\n",
    "            h = h * mascara\n",
    "\n",
    "        p = sigmoide(h @ W2 + b2)\n",
    "        d2 = (p - objetivo) / n\n",
    "        d1 = (d2 @ W2.T) * (1 - h ** 2)\n",
    "        if dropout:\n",
    "            d1 = d1 * mascara          # la culpa no pasa por las apagadas\n",
    "\n",
    "        # FRENO 1: encoger los pesos un poquito en cada vuelta\n",
    "        W2 -= paso * (h.T @ d2 + decay * W2); b2 -= paso * d2.sum(axis=0)\n",
    "        W1 -= paso * (A.T @ d1 + decay * W1); b1 -= paso * d1.sum(axis=0)\n",
    "\n",
    "    entrena = roc_auc_score(y_tr, sigmoide(np.tanh(T_tr @ W1 + b1) @ W2 + b2).ravel())\n",
    "    prueba = roc_auc_score(y_te, sigmoide(np.tanh(T_te @ W1 + b1) @ W2 + b2).ravel())\n",
    "    return entrena, prueba\n",
    "\n",
    "print(f\"{'freno':26} {'entrena':>8} {'prueba':>8} {'brecha':>8}\")\n",
    "for nombre, ajustes in [\n",
    "    ('nada', {}),\n",
    "    ('decay 0,001', dict(decay=0.001)),\n",
    "    ('decay 0,01', dict(decay=0.01)),\n",
    "    ('decay 0,1', dict(decay=0.1)),\n",
    "    ('dropout 0,2', dict(dropout=0.2)),\n",
    "    ('dropout 0,5', dict(dropout=0.5)),\n",
    "    ('ruido 0,5', dict(ruido=0.5)),\n",
    "]:\n",
    "    e, p = corre(**ajustes)\n",
    "    print(f'{nombre:26} {e:8.4f} {p:8.4f} {e - p:+8.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa tabla es el capítulo entero, así que vamos freno por freno 👇"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Freno 1: encoger los pesos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ltotal=L+λ2∑jwj2\n",
    "\n",
    "le sumas al error un castigo por tener pesos grandes, que es la forma más barata de que la red deje de memorizar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es una sola línea: `+ decay * W` dentro de la corrección. En cada\n",
    "vuelta, además de corregir el error, se le resta a cada peso una fracción de sí\n",
    "mismo.\n",
    "\n",
    "Lo que hace es **castigar los pesos grandes**, y los pesos\n",
    "grandes son justamente los que memorizan: para clavar una fila concreta hace\n",
    "falta un peso enorme en la columna que la distingue.\n",
    "\n",
    "Y funciona clarísimo. Sin nada, entrena 0,8847 y prueba 0,6210, con una brecha\n",
    "de +0,2636. Con `decay 0,01`, entrena 0,7194 y prueba\n",
    "**0,7207**, con una brecha de -0,0013 🎯\n",
    "\n",
    "Brecha negativa significa que le va *mejor* en la prueba que en el\n",
    "entrenamiento, o sea que ya no memoriza nada y lo poco que aprendió es real.\n",
    "\n",
    "Fíjate también en la dosis. Con 0,001 apenas hace nada (brecha +0,1881) y con\n",
    "0,1 empieza a apretar de más: entrena 0,7072, que ya es menos de lo que la\n",
    "logística consigue. Hay una ventana y hay que buscarla.\n",
    "\n",
    "Este freno es exactamente el Ridge del libro de machine learning, el mismo\n",
    "castigo a los pesos al cuadrado. En redes se le llama weight decay y en\n",
    "scikit-learn es el `alpha` de `MLPClassifier`, que en el\n",
    "capítulo 1 subió una red de 0,6584 a 0,7072 ⚖️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Freno 2: apagar neuronas al azar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "a~j=mj1−paj,mj~Bernoulli(1−p)\n",
    "\n",
    "apagas neuronas al azar durante el entrenamiento y escalas las que quedan, para que la suma siga valiendo lo mismo cuando en producción no apagas ninguna"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El dropout es más raro y más bonito. En cada vuelta, **apagas una parte\n",
    "de las neuronas al azar** y entrenas sin ellas.\n",
    "\n",
    "La idea es que ninguna neurona pueda confiarse: como en cualquier momento sus\n",
    "compañeras pueden desaparecer, todas tienen que aprender algo útil por su cuenta\n",
    "en vez de repartirse una regla frágil entre varias.\n",
    "\n",
    "La división por `(1 - dropout)` de la máscara es para que la suma\n",
    "total siga valiendo lo mismo aunque falte gente, y así al predecir con todas\n",
    "encendidas no haga falta ajustar nada.\n",
    "\n",
    "Con 0,2 la brecha baja de +0,2636 a +0,0889 y la prueba sube a 0,7032. Ayuda,\n",
    "y se queda por debajo del weight decay.\n",
    "\n",
    "Y con 0,5 pasa algo que hay que ver: entrena 0,4964 y prueba 0,5185.\n",
    "**El modelo se destruyó**, está por debajo del azar en\n",
    "entrenamiento 💀\n",
    "\n",
    "La razón es el tamaño. Apagar la mitad de 32 neuronas deja 16, y con tan\n",
    "pocas, la red no llega a formar nada estable entre tanto apagón. El dropout de\n",
    "0,5 es la receta estándar y está pensada para capas de cientos o miles de\n",
    "neuronas, no para esta.\n",
    "\n",
    "Es un buen ejemplo de por qué las recetas copiadas fallan: **0,5 no es\n",
    "un número mágico, es un número que funciona en redes grandes** 🎲"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Freno 3: ensuciar las entradas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El tercero es el más simple de entender: en cada vuelta le sumas ruido\n",
    "aleatorio a los datos, para que la red nunca vea dos veces exactamente la misma\n",
    "fila.\n",
    "\n",
    "Con ruido 0,5, que sobre columnas escaladas es muchísimo (media desviación\n",
    "típica), la brecha baja de +0,2636 a +0,1100 y la prueba sube a 0,6774.\n",
    "\n",
    "O sea que ayuda menos que los otros dos y hace falta bastante cantidad. En\n",
    "datos tabulares no suele valer la pena.\n",
    "\n",
    "Donde sí es la herramienta principal es en imágenes, y ahí tiene otro nombre:\n",
    "**aumento de datos**. Rotar la foto un poco, recortarla, cambiarle\n",
    "el brillo. Cada versión es una fila nueva que el modelo no puede memorizar, y en\n",
    "el capítulo 11 se entiende mejor por qué funciona tanto ahí y tan poco aquí 🖼️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que dice la tabla en conjunto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El mejor resultado de todo el capítulo es 0,7207, con weight decay 0,01. Y el\n",
    "listón de la logística es 0,7214.\n",
    "\n",
    "Entonces, otra vez lo mismo: **el freno que funciona es el que devuelve\n",
    "la red a la solución lineal**. No la mejora, la rescata 🙃\n",
    "\n",
    "Y eso no es un fracaso del capítulo. Es lo que necesitas saber hacer el día\n",
    "que trabajes con datos donde la red sí tenga algo que aportar, porque ahí la\n",
    "diferencia entre una red con freno y una sin freno son treinta puntos de AUC."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Buscar la dosis de weight decay\n",
    "\n",
    "Barre varios valores y encuentra la ventana."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Dropout en una red más grande\n",
    "\n",
    "Si 0,5 mató a la red de 32, prueba con 128 neuronas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Los dos frenos juntos\n",
    "\n",
    "Combina weight decay con dropout y mira si suman."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Qué le pasa a los pesos con el freno\n",
    "\n",
    "Mide cuánto valen los pesos con y sin weight decay."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Más datos también es un freno\n",
    "\n",
    "Entrena la red sin ningún freno pero con la mitad de las\n",
    "filas, y con todas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. El error de olvidar la máscara en la vuelta atrás\n",
    "\n",
    "Quita el `d1 = d1 * mascara` y compara el\n",
    "gradiente con el numérico."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El error de la dosis imposible\n",
    "\n",
    "Pon un dropout de 1, o sea apagarlo todo."
   ]
  },
  {
   "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",
    "Dropout 0,5 destruye una red de 32 neuronas (0,4964) y ayuda a una de 128. ¿Por qué?\n",
    "\n",
    "a) Porque apagar la mitad de 32 deja demasiado poco para representar nada\n",
    "\n",
    "b) Porque dropout solo funciona en redes grandes por definición\n",
    "\n",
    "c) Porque 0,5 es un valor mal elegido siempre\n",
    "\n",
    "d) Porque la red de 32 necesita más vueltas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y el freno que se queda puesto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Metes dropout, que es lo que todo el mundo recomienda, y al evaluar sale un número más bajo del que esperabas. Se prueba otra vez y sale distinto.\n",
    "\n",
    "```\n",
    "def predice(X):\n",
    "    h = relu(X @ W1 + b1)\n",
    "    h *= (np.random.rand(*h.shape) > 0.5)\n",
    "    return sigmoide(h @ W2 + b2)\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": [
    "- ⚖️ Weight decay es una línea (`+ decay * W`) y es el que\n",
    "funciona: la brecha pasa de +0,2636 a -0,0013 y la prueba sube a 0,7207.\n",
    "\n",
    "- 🎯 Y tiene dosis: con 0,001 no hace nada y con 0,1 aprieta de más.\n",
    "\n",
    "- 🎲 Dropout 0,2 ayuda a medias. Dropout 0,5 destruye una red de 32 neuronas\n",
    "(0,4964 en entrenamiento) y funciona bien con 128.\n",
    "\n",
    "- 🖼️ Ensuciar las entradas ayuda poco en tabla. En imágenes es la herramienta\n",
    "principal y se llama aumento de datos.\n",
    "\n",
    "- 🧱 Los frenos no se apilan por si acaso: decay más dropout da lo mismo que\n",
    "decay solo.\n",
    "\n",
    "- 📉 El freno hace lo que dice: el peso medio baja de 0,2415 a 0,0208.\n",
    "\n",
    "- 🚦 La brecha nunca se mira sola: con decay 0,3 sale +0,0000 perfecta y el\n",
    "modelo contesta 0,5000, o sea nada.\n",
    "\n",
    "- 🗄️ Más datos es el freno más eficaz y el único sin contraindicaciones.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Frenar la memorización no es lo mismo que aprender más. Casi siempre es solo lo primero."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El sobreajuste no es exclusivo de las redes: pasa igual con árboles y con regresiones, y ahí está medido en el [libro de machine learning desde cero](https://missyera.com/guias/machine-learning-desde-cero/) 🌳\n",
    "\n",
    "En el capítulo 11 cambiamos de tipo de dato: las convolucionales, y por fin un\n",
    "problema donde la red gana de calle.\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 8 de **Deep learning desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/deep-learning-desde-cero/frenar-el-sobreajuste/\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
}
