{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Convertir palabras en números que signifiquen algo\n",
    "\n",
    "One-hot dice que bodega y minimarket no se parecen en nada. Los embeddings arreglan eso, y traen su propio problema.\n",
    "\n",
    "Cuaderno de práctica del capítulo 13 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/texto-y-embeddings/\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: \"IDIgZGltZW5zaW9uZXM6IGJvZGVnYS9taW5pbWFya2V0ICswLjk1NSAgYm9kZWdhL2Fycm96ICswLjY5MwogNSBkaW1lbnNpb25lczogYm9kZWdhL21pbmltYXJrZXQgKzAuODQ5ICBib2RlZ2EvYXJyb3ogKzAuMzAwCjEwIGRpbWVuc2lvbmVzOiBib2RlZ2EvbWluaW1hcmtldCArMC42ODYgIGJvZGVnYS9hcnJveiArMC4zNDY=\",\n",
    "    2: \"dmVudGFuYSAxOiBib2RlZ2EvbWluaW1hcmtldCArMC42MDkgIGJvZGVnYS9hcnJveiArMC40MzMKdmVudGFuYSAyOiBib2RlZ2EvbWluaW1hcmtldCArMC44NDkgIGJvZGVnYS9hcnJveiArMC4zMDAKdmVudGFuYSA1OiBib2RlZ2EvbWluaW1hcmtldCArMC44MjMgIGJvZGVnYS9hcnJveiArMC43ODE=\",\n",
    "    3: \"ZG9zIHF1ZWphcyBwYXJlY2lkYXM6IDAuOTY5CnF1ZWphIGNvbnRyYSBlbG9naW8gOiAwLjczMQ==\",\n",
    "    4: \"ICBib2RlZ2EgICAgICAgKzAuOTcxCiAgbmFkaWUgICAgICAgICswLjg5NwogIG1heW9yaXN0YSAgICArMC44NzcKICBtaW5pbWFya2V0ICAgKzAuNzg2\",\n",
    "    5: \"Ym9kZWdhICAgICAgIHkgbWluaW1hcmtldCAgOiArMS4wMDAKYm9kZWdhICAgICAgIHkgbGltYSAgICAgICAgOiArMC42NzYKbGltYSAgICAgICAgIHkgYXJlcXVpcGEgICAgOiArMS4wMDA=\",\n",
    "    6: \"ICAgICA0MSBwYWxhYnJhczogb25lLWhvdCAgICAgICAgICAxLDY4MSBuw7ptZXJvcyAgIGVtYmVkZGluZ3MgZGUgMTAwICAgICAgICA0LDEwMAogIDEsMDAwIHBhbGFicmFzOiBvbmUtaG90ICAgICAgMSwwMDAsMDAwIG7Dum1lcm9zICAgZW1iZWRkaW5ncyBkZSAxMDAgICAgICAxMDAsMDAwCiA1MCwwMDAgcGFsYWJyYXM6IG9uZS1ob3QgIDIsNTAwLDAwMCwwMDAgbsO6bWVyb3MgICBlbWJlZGRpbmdzIGRlIDEwMCAgICA1LDAwMCwwMDA=\",\n",
    "    7: \"S2V5RXJyb3I6ICdodWFuY2F5byc=\",\n",
    "}, lenguaje=\"python\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En el capítulo 11 el dato tenía forma de rejilla. Aquí tiene forma de\n",
    "**secuencia**, y antes de meterla en cualquier red hay un problema\n",
    "que resolver: una red multiplica números y \"bodega\" no se multiplica 🔤\n",
    "\n",
    "Antes de seguir, piénsalo tú: **¿cómo le explicarías a una máquina que bodega y minimarket se parecen, y que arroz no tiene nada que ver con ninguna de las dos?** 🔤"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que veníamos haciendo, y por qué no basta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En todo el libro de machine learning convertimos las palabras con\n",
    "`OneHotEncoder`: una columna por valor, un 1 en la que toca y ceros en\n",
    "las demás."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "palabras = ['bodega', 'minimarket', 'mayorista', 'arroz', 'aceite']\n",
    "posicion = {p: i for i, p in enumerate(palabras)}\n",
    "\n",
    "def one_hot(p):\n",
    "    v = np.zeros(len(palabras))\n",
    "    v[posicion[p]] = 1\n",
    "    return v\n",
    "\n",
    "print('bodega    :', one_hot('bodega'))\n",
    "print('minimarket:', one_hot('minimarket'))\n",
    "print()\n",
    "print('parecido bodega y minimarket:', float(one_hot('bodega') @ one_hot('minimarket')))\n",
    "print('parecido bodega y arroz     :', float(one_hot('bodega') @ one_hot('arroz')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los dos parecidos dan **0,0**. Exactamente el mismo número.\n",
    "\n",
    "O sea que para el modelo, una bodega se parece a un minimarket lo mismo que se\n",
    "parece a un saco de arroz: nada. Cada palabra es una isla 🏝️\n",
    "\n",
    "Con cuatro segmentos eso se aguanta, porque el modelo aprende cada uno por\n",
    "separado y hay datos de sobra. Con un vocabulario de 50.000 palabras de texto es\n",
    "imposible: no vas a tener ejemplos suficientes de cada una, y lo que aprendas\n",
    "sobre \"retraso\" no le servirá de nada a \"tardanza\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La idea: mirar con quién anda"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un **embedding** es una lista corta de números que representa una\n",
    "palabra. Y la idea para construirlo es de 1957, mucho antes que las redes: una\n",
    "palabra se define por las palabras que la rodean.\n",
    "\n",
    "Vamos con un puñado de comentarios de clientes. Los escribí yo para este\n",
    "capítulo, con la forma de los que llegan a una distribuidora:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "COMENTARIOS = [\n",
    "    'el pedido llego completo y a tiempo',\n",
    "    'el pedido llego incompleto y con retraso',\n",
    "    'la bodega pidio arroz y azucar',\n",
    "    'el minimarket pidio aceite y azucar',\n",
    "    'el mayorista pidio arroz en cantidad',\n",
    "    'el pedido de la bodega llego tarde',\n",
    "    'el pedido del minimarket llego tarde',\n",
    "    'la bodega reclamo por el retraso del pedido',\n",
    "    'el minimarket reclamo por el aceite roto',\n",
    "    'el mayorista reclamo por la factura',\n",
    "    'llego todo completo sin reclamo',\n",
    "    'el arroz llego en buen estado',\n",
    "    'el aceite llego en buen estado',\n",
    "    'el azucar llego roto y con retraso',\n",
    "    'la bodega pago la factura a tiempo',\n",
    "    'el minimarket pago la factura con retraso',\n",
    "    'el mayorista pago la factura a tiempo',\n",
    "    'el vendedor visito la bodega el lunes',\n",
    "    'el vendedor visito el minimarket el martes',\n",
    "    'el vendedor visito al mayorista el lunes',\n",
    "    'la bodega compro arroz el lunes',\n",
    "    'el minimarket compro aceite el martes',\n",
    "    'el mayorista compro arroz y aceite',\n",
    "    'nadie visito la bodega esta semana',\n",
    "    'el pedido de arroz llego completo',\n",
    "    'el pedido de aceite llego incompleto',\n",
    "]\n",
    "\n",
    "vocabulario = sorted({p for f in COMENTARIOS for p in f.split()})\n",
    "lugar = {p: i for i, p in enumerate(vocabulario)}\n",
    "print('frases:', len(COMENTARIOS), ' palabras distintas:', len(vocabulario))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Contar quién aparece al lado de quién"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "V = len(vocabulario)\n",
    "juntas = np.zeros((V, V))\n",
    "VENTANA = 2                      # dos palabras a cada lado\n",
    "\n",
    "for f in COMENTARIOS:\n",
    "    ps = f.split()\n",
    "    for i, p in enumerate(ps):\n",
    "        for j in range(max(0, i - VENTANA), min(len(ps), i + VENTANA + 1)):\n",
    "            if i != j:\n",
    "                juntas[lugar[p], lugar[ps[j]]] += 1\n",
    "\n",
    "for p in ['bodega', 'arroz']:\n",
    "    vecinas = np.argsort(-juntas[lugar[p]])[:4]\n",
    "    print(f'{p:10} aparece más al lado de:',\n",
    "          ', '.join(f'{vocabulario[i]} ({int(juntas[lugar[p], i])})' for i in vecinas))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa matriz es todo lo que sabemos del idioma: quién anda con quién. No hay\n",
    "gramática, ni diccionario, ni nadie que haya explicado qué es una bodega 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## De la matriz a los vectores cortos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La matriz tiene 41 por 41. Lo que queremos son listas de cinco números, y la\n",
    "forma clásica de comprimir una matriz conservando lo que importa es la\n",
    "**descomposición en valores singulares**, que numpy trae hecha:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "U, S, _ = np.linalg.svd(np.log1p(juntas))     # log1p suaviza las cuentas grandes\n",
    "E = U[:, :5] * S[:5]                          # cinco números por palabra\n",
    "\n",
    "def parecido(a, b):\n",
    "    x, y = E[lugar[a]], E[lugar[b]]\n",
    "    return float(x @ y / (np.linalg.norm(x) * np.linalg.norm(y) + 1e-12))\n",
    "\n",
    "print('bodega    :', np.round(E[lugar['bodega']], 3))\n",
    "print('arroz     :', np.round(E[lugar['arroz']], 3))\n",
    "print()\n",
    "for a, b in [('bodega', 'minimarket'), ('arroz', 'aceite'), ('bodega', 'arroz'),\n",
    "             ('lunes', 'martes'), ('completo', 'incompleto')]:\n",
    "    print(f'{a:12} y {b:12}: {parecido(a, b):+.3f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahí está lo que queríamos 🎉\n",
    "\n",
    "**bodega y minimarket dan 0,849**, cuando con one-hot daban 0,0.\n",
    "Y arroz y aceite 0,945, y lunes y martes 0,968.\n",
    "\n",
    "Y lo importante: **bodega y arroz dan 0,300**, o sea bajo. El\n",
    "método distinguió solo que hay palabras de \"tipo de cliente\" y palabras de\n",
    "\"producto\", sin que nadie se lo dijera. Solo mirando con quién andan.\n",
    "\n",
    "Ese número, el `parecido`, es la **similitud coseno**:\n",
    "mide si dos vectores apuntan en la misma dirección, sin importar su tamaño. Es la\n",
    "medida estándar para embeddings y va de -1 a 1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y ahora el problema, que es famoso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vuelve a mirar la última línea: **completo e incompleto dan\n",
    "0,975**. Casi idénticos.\n",
    "\n",
    "Y no es un fallo de mi corpus ni de mi código. Es que *completo* e\n",
    "*incompleto* aparecen exactamente en los mismos sitios: después de\n",
    "\"llego\", antes de \"y\". Su compañía es la misma, así que su vector es el mismo.\n",
    "\n",
    "Este es el agujero conocido de esta idea: **los embeddings de\n",
    "coocurrencia no distinguen antónimos**. Bueno y malo, subir y bajar, con y\n",
    "sin. Todos salen parecidísimos, y si construyes un detector de quejas encima,\n",
    "va a confundir el elogio con el reclamo 😬\n",
    "\n",
    "La solución no llegó hasta el capítulo 16: en vez de un vector fijo por\n",
    "palabra, un vector **que cambia según la frase**. Eso es la atención,\n",
    "y es la idea que hizo posible todo lo que usas hoy."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Buscar lo más parecido"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for p in ['bodega', 'arroz', 'retraso']:\n",
    "    puntajes = sorted(((parecido(p, w), w) for w in vocabulario if w != p),\n",
    "                      reverse=True)[:4]\n",
    "    print(f'{p:9} se parece a: ' +\n",
    "          ', '.join(f'{w} ({s:+.2f})' for s, w in puntajes))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para `bodega` saca mayorista, nadie, minimarket y vendedor. Tres de\n",
    "las cuatro son exactamente lo que esperarías 👏\n",
    "\n",
    "Y la cuarta explica el método: `nadie` aparece porque escribí\n",
    "\"nadie visito la bodega esta semana\", y en esa frase *nadie* ocupa el\n",
    "mismo hueco que ocuparía un vendedor. El método no sabe qué significa nadie: sabe\n",
    "en qué hueco cae.\n",
    "\n",
    "Con `arroz` pasa lo mismo: sale aceite (perfecto) y también\n",
    "`del` y `tarde`, que son ruido de tener solo 26 frases. Los\n",
    "embeddings de verdad se entrenan con miles de millones de palabras, y ahí el\n",
    "ruido se diluye 📚"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y si en vez de palabras fueran fotos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Acá está el salto que explica la palabra **multimodal**, que vas\n",
    "a oír mucho y que suena a más de lo que es.\n",
    "\n",
    "Un embedding no sabe que está hecho de palabras. Es una lista de números y un\n",
    "parecido que se mide con un ángulo. Nada de eso pide que el origen sea texto.\n",
    "**Si consigues meter una foto en el mismo espacio, la foto y la palabra se\n",
    "pueden comparar entre ellas.**\n",
    "\n",
    "Vamos con tres productos y unas fotos de mentira, que acá son rejillas de 6\n",
    "por 6. Lo que hace falta es aprender a pasar de los 36 números de la foto a los 5\n",
    "números de la palabra, y para eso basta un ajuste por mínimos cuadrados:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rng = np.random.default_rng(0)\n",
    "\n",
    "def foto(fila, columna):\n",
    "    im = np.zeros((6, 6))\n",
    "    im[fila] = 1.0\n",
    "    im[:, columna] = 1.0\n",
    "    return im\n",
    "\n",
    "FOTOS = {'arroz': foto(1, 1), 'aceite': foto(3, 3), 'azucar': foto(4, 0)}\n",
    "Xi = np.array([FOTOS[p].ravel() for p in FOTOS])\n",
    "Yt = np.array([E[lugar[p]] for p in FOTOS])\n",
    "W, *_ = np.linalg.lstsq(Xi, Yt, rcond=None)\n",
    "print('cada foto son', Xi.shape[1], 'numeros y cada palabra', Yt.shape[1])\n",
    "\n",
    "def coseno(x, y):\n",
    "    return float(x @ y / (np.linalg.norm(x) * np.linalg.norm(y) + 1e-12))\n",
    "\n",
    "for p in FOTOS:\n",
    "    sucia = (FOTOS[p] + rng.normal(0, .15, (6, 6))).ravel()\n",
    "    v = sucia @ W\n",
    "    print(f'foto de {p:8}',\n",
    "          sorted(((round(coseno(v, E[lugar[q]]), 3), q) for q in FOTOS), reverse=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cada foto, y encima ensuciada con ruido, cae más cerca de su propia palabra\n",
    "que de las otras dos. Nadie le enseñó a la foto qué es el arroz: se le enseñó a\n",
    "**aterrizar donde ya estaba la palabra** 🔤\n",
    "\n",
    "Mira los márgenes antes de emocionarte: 1,0 contra 0,943 es ganar por poco.\n",
    "Con tres ejemplos y una transformación lineal no da para más, y eso también es\n",
    "el mecanismo de verdad. Los modelos que hacen esto en serio usan una red por cada\n",
    "lado y millones de pares foto-texto, pero la idea que los sostiene es esta y cabe\n",
    "en veinte líneas.\n",
    "\n",
    "De ahí sale todo lo que llaman multimodal: buscar fotos escribiendo, describir\n",
    "una imagen, encontrar el producto del catálogo con una foto del cliente. No son\n",
    "tres inventos, es el mismo espacio compartido tres veces 📋"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Cuántas dimensiones conviene\n",
    "\n",
    "Prueba con 2, 5 y 10 números por palabra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 1\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. La ventana también decide\n",
    "\n",
    "Cuenta la coocurrencia con una ventana de 1 y de 5."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 2\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Sumar vectores para representar una frase\n",
    "\n",
    "La forma más simple de convertir una frase en números: sumar\n",
    "los de sus palabras."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 3\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. La aritmética famosa, a ver si sale\n",
    "\n",
    "Lo de \"rey menos hombre más mujer da reina\", con nuestras\n",
    "palabras."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 4\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Por qué esto no sirve para la tabla de ventas\n",
    "\n",
    "Arma el mismo procedimiento tratando cada fila del CSV como\n",
    "una frase y mira qué sale."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 5\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Cuántos números ocupa cada cosa\n",
    "\n",
    "Compara la memoria de one-hot contra embeddings para\n",
    "vocabularios reales."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%revisa 6\n",
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. El error de la palabra que no está\n",
    "\n",
    "Pídele el vector de una palabra que no salió en ninguna\n",
    "frase."
   ]
  },
  {
   "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",
    "Los embeddings de coocurrencia dan a completo e incompleto un parecido de 0,975. ¿Qué está pasando?\n",
    "\n",
    "a) Que aparecen en los mismos contextos, y eso es todo lo que el método mira\n",
    "\n",
    "b) Que el cálculo está mal\n",
    "\n",
    "c) Que hacen falta más dimensiones\n",
    "\n",
    "d) Que faltan datos de entrenamiento"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Y los vectores que vieron demasiado"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Entrenas los embeddings con todo el texto que tienes, que es lo lógico porque cuanto más texto mejor, y después repartes para evaluar.\n",
    "\n",
    "```\n",
    "vectores = entrena_embeddings(todo_el_texto)\n",
    "\n",
    "X = [vectores[p] for p in frases]\n",
    "X_tr, X_te, y_tr, y_te = train_test_split(\n",
    "    X, y, test_size=0.25)\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": [
    "- 🏝️ Con one-hot, bodega y minimarket se parecen 0,0, igual que bodega y\n",
    "arroz. Cada palabra es una isla.\n",
    "\n",
    "- 👀 Un embedding se construye contando con qué palabras aparece cada una. No\n",
    "hay gramática ni diccionario.\n",
    "\n",
    "- 🎉 Con cinco dimensiones, bodega y minimarket dan 0,849 y bodega y arroz\n",
    "0,300: distinguió tipos de palabra solo.\n",
    "\n",
    "- 😬 Y completo con incompleto dan 0,975. Los antónimos aparecen en los mismos\n",
    "sitios, así que salen iguales.\n",
    "\n",
    "- 🔍 La ventana decide qué agrupa: chica junta palabras que se sustituyen,\n",
    "grande junta palabras del mismo tema.\n",
    "\n",
    "- 🔀 Sumar los vectores de una frase pierde el orden: \"no llego el pedido\" se\n",
    "parece muchísimo a \"llego el pedido\".\n",
    "\n",
    "- 🧰 En la tabla de ventas, bodega y minimarket dan 1,000 y no se distinguen:\n",
    "cada columna se cruza con todas. Los embeddings necesitan contexto que varíe.\n",
    "\n",
    "- 🧩 Y solo saben las palabras que vieron: pedir una nueva revienta.\n",
    "\n",
    "Y si de todo el capítulo te llevas una sola frase, que sea esta:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para una máquina, dos palabras del mismo mundo son tan distintas como dos que no tienen nada que ver. Salvo que se lo enseñes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los embeddings son también lo que hay debajo de un buscador de documentos. Si eso es lo que te toca montar en tu empresa, en [soluciones de IA](https://missyera.com/consultoria-ia/) cuento cómo se aterriza 🏭\n",
    "\n",
    "En el capítulo 16 llega la atención, que arregla las tres cosas que quedaron\n",
    "mal aquí: los antónimos, el orden y el vector fijo.\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 13 de **Deep learning desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/deep-learning-desde-cero/texto-y-embeddings/\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
}
