{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Qué mide una derivada, con un caso tuyo\n",
    "\n",
    "La derivada sin límites ni epsilones: a cuánto sube el error por cada paso, calculada sobre el ajuste de un peso de verdad.\n",
    "\n",
    "Cuaderno de práctica del capítulo 13 de **Matemática para machine learning**, 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/matematica-para-machine-learning/que-mide-una-derivada/\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"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import urllib.request\n",
    "\n",
    "# Este capitulo abre el archivo por su nombre, sin URL, que es lo que se hace\n",
    "# cuando lo tienes al lado. Aqui se baja primero para que exista.\n",
    "for nombre in ['ventas-miss-yera.csv']:\n",
    "    urllib.request.urlretrieve(f\"https://missyera.com/static/datasets/{nombre}\", nombre)\n",
    "    print(nombre, \"lista\")"
   ]
  },
  {
   "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: \"cGFzbyAxIHwgdyA9IDAuMTg3OSB8IGVycm9yID0gMTIwOTkxOC4yNApwYXNvIDIgfCB3ID0gMC4zNzUzIHwgZXJyb3IgPSAxMjA2NDE1LjgzCnBhc28gMyB8IHcgPSAwLjU2MTkgfCBlcnJvciA9IDEyMDI5MzcuMTcKcGFzbyA0IHwgdyA9IDAuNzQ4IHwgZXJyb3IgPSAxMTk5NDgyLjEKcGFzbyA1IHwgdyA9IDAuOTMzNCB8IGVycm9yID0gMTE5NjA1MC40NQ==\",\n",
    "    2: \"aCA9IDEuMCAtPiBwZW5kaWVudGUgLTExNi4wNDAzCmggPSAwLjAxIC0+IHBlbmRpZW50ZSAtMTE2LjA0MDMKaCA9IDFlLTA1IC0+IHBlbmRpZW50ZSAtMTE2LjA0MDMKaCA9IDFlLTEwIC0+IHBlbmRpZW50ZSAtMTE1LjI1MTIKaCA9IDFlLTE0IC0+IHBlbmRpZW50ZSAwLjA=\",\n",
    "    3: \"IDQwIHwgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIDQ1IHwgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogNTAgfCAjIyMjIyMjIyMjCiA1NSB8IAogNjAgfCAjIyMjIyMjIwogNjUgfCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogNzAgfCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiA3NSB8ICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw==\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Empezamos por el problema, no por la definición 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 9 saqué el mejor peso de una regresión resolviendo un\n",
    "sistema. Salió 55.34 y salió de una vez, sin buscar.\n",
    "\n",
    "Eso solo se puede hacer con modelos lineales. En cuanto el modelo se\n",
    "complica un poco, no hay fórmula: hay que **buscar**. Y para\n",
    "buscar hace falta saber, desde donde estás parada, hacia dónde está el\n",
    "fondo.\n",
    "\n",
    "Eso es una derivada. No hace falta nada más para entenderla."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Volvemos a abrir el archivo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada capítulo arranca de cero, así que estas líneas se repiten. Son las\n",
    "mismas del capítulo 1 y las vas a ver en todos."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "df = pd.read_csv('ventas-miss-yera.csv')\n",
    "for c in ('unidades', 'monto', 'satisfaccion'):\n",
    "    df[c] = pd.to_numeric(df[c], errors='coerce')\n",
    "X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy()\n",
    "print('X.shape:', X.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una función que quiero minimizar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = X[:, 1]\n",
    "u = X[:, 0]\n",
    "\n",
    "def error(w):\n",
    "    return float(((y - w * u) ** 2).mean())\n",
    "\n",
    "for w in (0, 25, 50, 55, 60, 100):\n",
    "    print('  w =', w, '-> error', round(error(w), 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "E(w)=1n∑i=1n(yi−wxi)2\n",
    "\n",
    "el error es el promedio de las diferencias al cuadrado entre lo que pasó y lo que el modelo predijo con ese peso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mira la forma de esos números: baja, baja, baja, toca fondo entre 55 y 60, y\n",
    "vuelve a subir. Es un valle.\n",
    "\n",
    "Y el trabajo entero de entrenar un modelo es **encontrar el fondo de\n",
    "ese valle**. Nada más 🐣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La derivada, medida a lo bruto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si estoy parada en un punto y quiero saber hacia dónde baja, doy un pasito\n",
    "chiquito para cada lado y miro:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "h = 1e-5\n",
    "\n",
    "def pendiente(w):\n",
    "    return (error(w + h) - error(w - h)) / (2 * h)\n",
    "\n",
    "for w in (20.0, 55.0, 90.0):\n",
    "    print('  w =', w, '-> pendiente', round(pendiente(w), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "f′(w)=limh→0f(w+h)−f(w−h)2h\n",
    "\n",
    "la derivada es a cuánto sube la función por cada paso que das, midiendo con un paso tan pequeño que ya no cambia el resultado"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y ahora léelo, que es todo lo que hay que saber:\n",
    "\n",
    "- En 20 la pendiente es **muy negativa**. Estoy en la ladera\n",
    "izquierda, lejos, y el error baja fuerte si avanzo.\n",
    "\n",
    "- En 55 sigue negativa pero **chiquita**. Estoy cerca del fondo,\n",
    "casi plano.\n",
    "\n",
    "- En 90 es **positiva**. Me pasé, estoy en la otra ladera, y\n",
    "avanzar más me sube el error.\n",
    "\n",
    "**El signo dice la dirección y el tamaño dice la inclinación.**\n",
    "Eso es una derivada. Lo del límite y la h que tiende a cero es la forma\n",
    "matemática de decir \"un paso tan chiquito que ya da igual\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La misma derivada, calculada de verdad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lo de arriba funciona pero es caro: dos evaluaciones por cada peso, y un\n",
    "modelo tiene millones. Se puede sacar la fórmula:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pendiente_exacta(w):\n",
    "    return float((-2 * u * (y - w * u)).mean())\n",
    "\n",
    "for w in (20.0, 55.0, 90.0):\n",
    "    print('  w =', w, '| exacta', round(pendiente_exacta(w), 4),\n",
    "          '| numerica', round(pendiente(w), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Clavadas. Y esto que acabas de ver tiene nombre en el oficio: se llama\n",
    "**gradient checking**. Cuando programas una derivada a mano, la\n",
    "comparas contra la numérica para saber si te equivocaste. Es lo primero que se\n",
    "hace cuando una red no aprende y no sabes por qué 🌟"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El fondo es donde la derivada vale cero"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimo = float((u @ y) / (u @ u))\n",
    "print('minimo exacto        :', round(optimo, 4))\n",
    "print('pendiente ahi        :', round(pendiente_exacta(optimo), 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y fíjate de dónde salió ese 55.3417: es **exactamente** el\n",
    "coeficiente de proyección del ejercicio 2 del capítulo 9.\n",
    "\n",
    "Dos caminos completamente distintos, el mismo número. Por geometría es la\n",
    "sombra más cercana; por cálculo es el fondo del valle. Son la misma cosa mirada\n",
    "de dos maneras, y esa coincidencia es de las más bonitas que hay en este\n",
    "libro 💛\n",
    "\n",
    "Y ese menos cero de la pendiente es coma flotante otra vez, lo mismo del capítulo 6: el número no es cero exacto, es algo como diez a\n",
    "la menos catorce, y al redondear se queda el signo. Para lo que nos importa, es\n",
    "el fondo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- La derivada es a cuánto sube la función por cada paso.\n",
    "\n",
    "- Signo negativo: el error baja si avanzas. Positivo: sube.\n",
    "\n",
    "- El tamaño dice la inclinación, no cuánto hay que moverse.\n",
    "\n",
    "- Se puede medir a lo bruto con dos evaluaciones, y eso es gradient\n",
    "checking.\n",
    "\n",
    "- En el fondo del valle la derivada vale cero.\n",
    "\n",
    "- El fondo del valle y la proyección más cercana son el mismo punto."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Estás ajustando un peso y la derivada del error en ese punto vale -12002. ¿Qué haces?\n",
    "\n",
    "a) Subir el peso, porque pendiente negativa quiere decir que el error baja yendo hacia la derecha\n",
    "\n",
    "b) Bajar el peso, porque el número es negativo\n",
    "\n",
    "c) Dejarlo, porque el error ya está bajando solo\n",
    "\n",
    "d) Subir el peso 12002 unidades"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Baja el valle a mano\n",
    "\n",
    "Da cinco pasos en contra de la pendiente y mira qué pasa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. El paso demasiado chico también miente\n",
    "\n",
    "Prueba la derivada numérica con varias h."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Dibuja el valle sin dibujar nada\n",
    "\n",
    "Con la tabla de errores basta para ver la forma."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preguntas frecuentes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "¿Qué es la pendiente de una recta?Cuánto sube la recta por cada paso que avanza. Si vale 2, cada vez que avanzas uno hacia la derecha la recta sube dos.\n",
    "\n",
    "¿Cuál es la fórmula de la pendiente?La resta de las alturas dividida entre la resta de las posiciones: lo que sube entre lo que avanza. Con dos puntos ya se puede calcular.\n",
    "\n",
    "¿Cómo se calcula la pendiente entre dos puntos?Se resta la segunda altura menos la primera, se resta la segunda posición menos la primera, y se dividen. El orden no importa mientras sea el mismo arriba y abajo.\n",
    "\n",
    "¿Qué es una derivada?La pendiente de una curva en un punto. Como una curva cambia de inclinación todo el rato, la derivada es la pendiente de la recta que la toca justo ahí.\n",
    "\n",
    "¿Para qué sirve una derivada en machine learning?Para saber hacia dónde mover un parámetro para que el error baje. Un modelo entrenando es alguien bajando una cuesta a oscuras, y la derivada es tantear con el pie hacia dónde baja."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 13 de **Matemática para machine learning**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/matematica-para-machine-learning/que-mide-una-derivada/\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
}
