{"id":132,"date":"2026-03-18T18:47:20","date_gmt":"2026-03-18T18:47:20","guid":{"rendered":"https:\/\/pulki.es\/blog\/?p=132"},"modified":"2026-03-18T18:47:20","modified_gmt":"2026-03-18T18:47:20","slug":"large-language-model-llm-from-scratch-embeddings","status":"publish","type":"post","link":"https:\/\/pulki.es\/blog\/index.php\/2026\/03\/18\/large-language-model-llm-from-scratch-embeddings\/","title":{"rendered":"Large Language Model (LLM) from scratch. Embeddings."},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">En la entrega pasada vimos como clasificar textos usando la regresi\u00f3n log\u00edstica. En la presente, discutiremos <strong>embeddings<\/strong>, que en t\u00e9rminos simples, se define como una representaci\u00f3n vectorial del significado de las palabras.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aprender <strong>embeddings<\/strong> en el contexto de este proyecto es importante, porque son una piedra angular en los LLM contempor\u00e1neos. Como es usual, usaremos como referencia, el cap\u00edtulo 5 de <strong>Jurafsky y Martin, 2025<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Lemma, wordform, y sense<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un LLM exitoso deber\u00eda entender c\u00f3mo se relacionan los significados de las palabras. Para eso, ser\u00eda esperable que capture <em>similitud<\/em>, <em>antonimia<\/em>, <em>connotaci\u00f3n<\/em>, <em>distintas perspectivas sobre un mismo evento<\/em>, y adem\u00e1s <em>permitir inferencias \u00fatiles para tareas como QA o di\u00e1logo<\/em>. A continuaci\u00f3n explicaremos en detalle cada una de ellas:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Similitud<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El modelo debe ser capaz de reconocer qu\u00e9 palabras tienen un significado parecido aunque no sean id\u00e9nticas. Por ejemplo, en <strong>Jurafsky y Martin, 2025<\/strong>, muestran que si bien, perro y gato no son sin\u00f3nimos, son parecidas, debido a que, por ejemplo, ambos son animales. Esta noci\u00f3n de similitud es muy \u00fatil porque ayuda a estimar cu\u00e1n parecidos son dos fragmentos m\u00e1s grandes, como frases u oraciones, algo central en tareas como question answering, par\u00e1frasis y summarization.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Antonimia<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La similitud nos permite identificar si dos palabras est\u00e1n relacionadas, pero no nos dice <strong>c\u00f3mo<\/strong> se relacionan. Por ejemplo, caliente y fr\u00edo est\u00e1n fuertemente relacionadas, pero no s\u00f3lo porque est\u00e9n asociadas a diferencias de temperatura, tambi\u00e9n porque son opuestos. Esto es importante porque una representaci\u00f3n pobre podr\u00eda tratar calor y frio como simplemente \u201cpalabras frecuentes del mismo tema\u201d, cuando sem\u00e1nticamente cumplen funciones opuestas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Connotaci\u00f3n<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed el punto es capturar la <strong>carga afectiva o valorativa <\/strong>que lleva la palabra, esto es \u00fatil para tareas como sentiment analysis, stance detection y an\u00e1lisis de lenguaje pol\u00edtico o rese\u00f1as.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Distintas perspectivas sobre un mismo evento<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Jurafsky y Martin (2025)<\/strong> dice que palabras como <em>buy<\/em>, <em>sell<\/em> y <em>pay<\/em> ofrecen perspectivas distintas sobre un mismo evento subyacente: una transacci\u00f3n. Si yo compro algo, alguien me lo vende, y probablemente yo le pago.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Esto quiere decir que el significado no est\u00e1 solo en objetos aislados, sino tambi\u00e9n en <strong>roles dentro de una situaci\u00f3n<\/strong>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>comprar<\/em> pone el foco en el comprador;<\/li>\n\n\n\n<li><em>vender<\/em> pone el foco en el vendedor;<\/li>\n\n\n\n<li><em>pagar<\/em> pone el foco en la transferencia de dinero.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">No son sin\u00f3nimos. Pero tampoco son eventos totalmente independientes. Son distintas \u201cvistas\u201d de la misma escena. Una teor\u00eda buena del significado deber\u00eda poder representar esa estructura.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Permitir inferencias<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Seg\u00fan los autores una buena representaci\u00f3n debe permitir inferencias para resolver tareas relacionadas con el significado. Por ejemplo:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Texto: \u201cMar\u00eda compr\u00f3 un libro a Pedro.\u201d<\/li>\n\n\n\n<li>Pregunta: \u201c\u00bfPedro vendi\u00f3 un libro?\u201d<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Un sistema realmente sem\u00e1ntico deber\u00eda responder algo como \u201cprobablemente s\u00ed\u201d, aunque la palabra <em>vendi\u00f3<\/em> no aparezca literalmente. \u00bfPor qu\u00e9? Porque entiende la relaci\u00f3n entre <em>comprar<\/em> y <em>vender<\/em> como dos perspectivas del mismo evento. Ese tipo de salto es una inferencia sem\u00e1ntica.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En resumen, el significado de una palabra no puede reducirse a una etiqueta. Una teor\u00eda \u00fatil debe modelar al menos parte de la red de relaciones que una palabra mantiene con otras palabras, con eventos, con valoraciones afectivas y con posibles inferencias.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como veremos en la siguiente secci\u00f3n, el significado de una palabra puede representarse como un vector, y ese vector se obtiene a partir de los contextos en los que la palabra aparece.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Sem\u00e1ntica vectorial<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En la secci\u00f3n anterior vimos qu\u00e9 aspectos del significado de una palabra deber\u00eda captar una buena teor\u00eda sem\u00e1ntica. En esta secci\u00f3n veremos una forma de representar ese significado en NLP.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La idea principal es definir el significado de una palabra a partir de su distribuci\u00f3n en el uso del lenguaje, es decir, de las palabras que suelen aparecer cerca de ella y de los entornos gramaticales en los que aparece. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Por ejemplo, supongamos que no sabemos el significado de la palabra <kbd>faze<\/kbd>, proveniente del castellano medieval. Est\u00e1 claro, que al extraer las siguientes frases del Libro de las animalias, de Moamyn,<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">E estos poluos q agora<br>diremos son pora la caletura q seles <code>faze<\/code> en las<br>bocas.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">los gusanos q seles <code>faze<\/code><br>en las moliellas.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">dela seqdad q seles<br><code>faze<\/code> dentro en los cuerpos<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">podemos ver que las oraciones sugieren que <kbd>faze<\/kbd> est\u00e1 relacionada con el verbo \u00abhacer\u00bb, en espa\u00f1ol moderno. En este sentido, podemos llevar este principio a la arena computacional, representando la palabra <code>faze<\/code> como un punto en un espacio sem\u00e1ntico multidimensional; derivado usando la distribuci\u00f3n de las palabras vecinas a ella. A esta representaci\u00f3n vectorial del significado de <code>faze<\/code>, lo llamamos embedding.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Computando embeddings: modelo basado en el conteo<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Empezaremos con el modelo vectorial de significado m\u00e1s simple. Para implementarlo, necesitamos llenar una matriz de <strong>co-occurrencia<\/strong>, que es una forma de representar cu\u00e1n seguido las palabras co-occurren. Un tipo particular de matriz de co-occurrencia es la <strong>word-context matrix<\/strong>, en la que cada fila es una palabra del vocabulario, y cada columna indica cu\u00e1n seguido aparecen las palabras vecinas de una fila particular. A modo de ejemplo, supongamos que disponemos de un corpus de dos oraciones, con una ventana de dos palabras vecinas: <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">q seles <code>faze<\/code> dentro en <\/p>\n<\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">q seles <code>faze<\/code> en las <\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">En base a esta informaci\u00f3n, una versi\u00f3n simplificada de la <strong>word-context matrix<\/strong> viene dada por:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><\/td><td>q<\/td><td>seles<\/td><td>dentro<\/td><td>en<\/td><td>las<\/td><\/tr><tr><td><code>faze<\/code><\/td><td>2<\/td><td>2<\/td><td>1<\/td><td>2<\/td><td>1<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Si bien la dimensi\u00f3n de esta matriz es 1&#215;5, una <strong>word-context matrix<\/strong> deber\u00eda tener dimensiones <math data-latex=\"|V|\\times|V|\"><semantics><mrow><mi>|<\/mi><mi>V<\/mi><mi>|<\/mi><mo>\u00d7<\/mo><mi>|<\/mi><mi>V<\/mi><mi>|<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">|V|\\times|V|<\/annotation><\/semantics><\/math>, esto, debido a que cada fila es una palabra del vocabulario, y cada columna, es una posible palabra vecina. Dicho esto, en nuestro caso, la matriz deber\u00eda tener dimensi\u00f3n <math data-latex=\"6\\times6\"><semantics><mrow><mn>6<\/mn><mo>\u00d7<\/mo><mn>6<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">6\\times6<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Como dijimos anteriormente, un <strong>embedding<\/strong> es una representaci\u00f3n vectorial del significado de una palabra. En el caso de nuestra <strong>word-context matrix<\/strong> simplificada, <code>faze<\/code> es representada como el vector [2,2,1,2,1] -aunque la word-context matrix completa deber\u00eda ser [2,2,1,2,1,0].<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Puesto que el objetivo inicial de esta entrada es analizar c\u00f3mo se relaciona el significado de dos palabras distintas, y ya sabemos c\u00f3mo representarlo de forma vectorial, el siguiente paso l\u00f3gico es medir qu\u00e9 tan similares son esas representaciones entre dos o m\u00e1s palabras. En la siguiente secci\u00f3n veremos c\u00f3mo hacerlo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Midiendo similitud entre vectores<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La m\u00e9trica que usaremos para medir similitud entre dos vectores, es la similitud coseno (puedes encontrar una definici\u00f3n acabada <a href=\"https:\/\/www.ibm.com\/es-es\/think\/topics\/cosine-similarity\">ac\u00e1<\/a>), cuya f\u00f3rmula viene dada por:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>s<\/mi><mi>i<\/mi><mi>m<\/mi><mi>_<\/mi><mi>c<\/mi><mi>o<\/mi><mi>s<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>v<\/mi><mo separator=\"true\">,<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mrow><mi>v<\/mi><mo>\u22c5<\/mo><mi>w<\/mi><\/mrow><mrow><mi>|<\/mi><mi>v<\/mi><mi>|<\/mi><mi>|<\/mi><mi>w<\/mi><mi>|<\/mi><\/mrow><\/mfrac><mo>=<\/mo><mfrac><mrow><msubsup><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>N<\/mi><\/msubsup><msub><mi>v<\/mi><mi>i<\/mi><\/msub><msub><mi>w<\/mi><mi>i<\/mi><\/msub><\/mrow><mrow><msqrt><mrow><msubsup><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>N<\/mi><\/msubsup><msubsup><mi>v<\/mi><mi>i<\/mi><mn>2<\/mn><\/msubsup><\/mrow><\/msqrt><msqrt><mrow><msubsup><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>N<\/mi><\/msubsup><msubsup><mi>w<\/mi><mi>i<\/mi><mn>2<\/mn><\/msubsup><\/mrow><\/msqrt><\/mrow><\/mfrac><\/mrow><annotation encoding=\"application\/x-tex\">sim\\_cos(v,w) = \\frac{v\\cdot w}{|v||w|} = \\frac{\\sum_{i=1}^{N}v_{i}w_{i}}{\\sqrt{\\sum_{i=1}^{N}v_{i}^{2}}\\sqrt{\\sum_{i=1}^{N}w_{i}^{2}}}<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">donde <math data-latex=\"v\"><semantics><mi>v<\/mi><annotation encoding=\"application\/x-tex\">v<\/annotation><\/semantics><\/math> y <math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math> son representaciones vectoriales de las palabras v y w.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En teor\u00eda, <math data-latex=\"sim\\_cos\"><semantics><mrow><mi>s<\/mi><mi>i<\/mi><mi>m<\/mi><mi>_<\/mi><mi>c<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">sim\\_cos<\/annotation><\/semantics><\/math> puede devolver valores entre -1 y 1, con -1 para vectores apuntando en direcci\u00f3n opuesta, 1 para vectores apuntando a la misma direcci\u00f3n, y 0 para vectores ortogonales. Pero, debido a que <math data-latex=\"v_{i}\"><semantics><msub><mi>v<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">v_{i}<\/annotation><\/semantics><\/math> y <math data-latex=\"w_{i}\"><semantics><msub><mi>w<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">w_{i}<\/annotation><\/semantics><\/math> nunca ser\u00e1n negativos (porque son frecuencias), en esta aplicaci\u00f3n,  <math data-latex=\"sim\\_cos\"><semantics><mrow><mi>s<\/mi><mi>i<\/mi><mi>m<\/mi><mi>_<\/mi><mi>c<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">sim\\_cos<\/annotation><\/semantics><\/math> siempre estar\u00e1 entre 0 y 1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para ilustrar esta idea, tomaremos 20 textos medievales, y calculamos la <strong>word-context matrix<\/strong> correspondiente con una ventana de contexto de <strong>dos<\/strong> palabras. En este ejercicio, producimos un vocabulario de 35.340 palabras, con un vector de 35.340 elementos para cada palabra. En este sentido, si analizamos <code>faze<\/code>, podemos ver que los diez elementos m\u00e1s grandes del vector son<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Palabra<\/strong><\/td><td><strong>Frecuencia<\/strong><\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>que<\/code><\/td><td>437<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>se<\/code><\/td><td>292<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>el<\/code><\/td><td>196<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>en<\/code><\/td><td>158<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>la<\/code><\/td><td>124<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>por<\/code><\/td><td>89<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>lo<\/code><\/td><td>77<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>las<\/code><\/td><td>75<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>seles<\/code><\/td><td>75<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><code>lo<\/code><\/td><td>74<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">indicando una buena se\u00f1al, puesto que <code>faze<\/code> (hoy <code>hace<\/code>, del verbo <code>hacer<\/code>), suele estar acompa\u00f1ada de <code>que<\/code> y <code>se<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Adem\u00e1s, al calcular <em>cosine similarity<\/em> de <code>faze<\/code> contra el resto de las palabras del vocabulario, las diez que presentan mayor similitud son:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Palabra<\/strong><\/td><td><strong>Cosine similarity<\/strong><\/td><\/tr><tr><td><code>fazie<\/code><\/td><td>0.904950<\/td><\/tr><tr><td><code>fazen<\/code><\/td><td>0.903953<\/td><\/tr><tr><td><code>mostraua<\/code><\/td><td>0.897187<\/td><\/tr><tr><td><code>tornasse<\/code><\/td><td>0.891229<\/td><\/tr><tr><td><code>yua<\/code><\/td><td>0.885668<\/td><\/tr><tr><td><code>entienden<\/code><\/td><td>0.885050<\/td><\/tr><tr><td><code>cumpliesse<\/code><\/td><td>0.878845<\/td><\/tr><tr><td><code>cumpla<\/code><\/td><td>0.876391<\/td><\/tr><tr><td><code>guardasse<\/code><\/td><td>0.874317<\/td><\/tr><tr><td><code>nunqua<\/code><\/td><td>0.871570<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Vale la pena recordar que las palabras que aparecen en esta tabla son aquellas que ocurren en contextos parecidos a <strong>faze<\/strong>, y no necesariamente sus sin\u00f3nimos. Por ejemplo, seg\u00fan estos resultados, <strong>fazie<\/strong> y <strong>fazen<\/strong> aparecen en entornos sint\u00e1cticos similares. Esto resulta razonable, pues son formas cercanas a <strong>faze<\/strong>: <strong>fazie<\/strong> sol\u00eda significar \u201chac\u00eda\u201d y <strong>fazen<\/strong>, \u201chacen\u201d. No obstante, aunque comparten contextos de uso similares, no son sin\u00f3nimos estrictos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para visualizar de forma gr\u00e1fica el concepto de similitud, utilizamos PCA para proyectar los vectores de palabras en dos dimensiones y, a continuaci\u00f3n, graficamos algunas de ellas.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"586\" src=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-4.png\" alt=\"\" class=\"wp-image-145\" srcset=\"https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-4.png 901w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-4-300x195.png 300w, https:\/\/pulki.es\/blog\/wp-content\/uploads\/2026\/03\/image-4-768x499.png 768w\" sizes=\"auto, (max-width: 901px) 100vw, 901px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Como podemos ver, palabras que cumplen una funci\u00f3n similar, como <code>fazia<\/code>, <code>fazie<\/code> y <code>fizo<\/code>, se encuentran cerca, y palabras con un significado distinto, no comparten una regi\u00f3n similar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Word2vec<\/em><\/strong>: <em>skip-gram con sampleo negativo<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Los vectores que hemos visto hasta el momento tienen una dimensi\u00f3n equivalente a la del tama\u00f1o del vocabulario. En nuestro ejemplo, <code>faze<\/code>, tiene dimensi\u00f3n 35.340, donde muchos componentes son cero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta secci\u00f3n presentaremos <strong>embeddings<\/strong> representados mediante vectores relativamente cortos y densos, cuyos valores pueden incluso ser negativos. Como se\u00f1alan <strong>Jurafsky y Martin (2025)<\/strong>, este tipo de representaci\u00f3n suele funcionar mejor que los vectores dispersos, es decir, aquellos con una gran cantidad de ceros. Una de las intuiciones principales es que, al tener menor dimensionalidad, el modelo aprende menos par\u00e1metros, lo que puede favorecer la generalizaci\u00f3n y reducir el riesgo de <strong>overfitting<\/strong>. Adem\u00e1s, los <strong>embeddings<\/strong> densos suelen capturar mejor la similitud entre palabras sem\u00e1nticamente cercanas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para ilustrarlo, supongamos que en nuestro corpus medieval encontramos dos contextos muy parecidos:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">dixo aquel que fezo el libro.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">dixo aql q fezo el libro.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Si representamos estos contextos mediante vectores dispersos, <em>aquel<\/em> y <em>aql<\/em> ocupar\u00e1n dimensiones distintas del vocabulario. As\u00ed, desde el punto de vista del vector, ambos contextos diferir\u00e1n en coordenadas distintas, aunque para nosotros resulten muy cercanos en contenido. En cambio, con <strong>embeddings<\/strong> densos ya no tenemos una dimensi\u00f3n por cada palabra del vocabulario, sino un vector corto de rasgos latentes aprendidos a partir de los contextos de uso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">La intuici\u00f3n del algoritmo que introduciremos ahora es la siguiente: en vez de limitarnos a contar cu\u00e1ntas veces aparece una palabra cerca de <code>dixo<\/code>, entrenaremos un clasificador que estime si una palabra candidata es o no un contexto probable de <code>dixo<\/code>. Los pesos aprendidos por ese clasificador ser\u00e1n precisamente los embeddings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>El clasificador<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El clasificador que queremos programar es uno que, dada una palabra objetivo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math> y una palabra candidata <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>c<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">c<\/annotation><\/semantics><\/math>, entregue la probabilidad de que <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>c<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">c<\/annotation><\/semantics><\/math> sea una palabra de contexto real de <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>. Por ejemplo, si consideramos la oraci\u00f3n <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><em>dixo aquel que fezo el libro<\/em> <\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">y usamos una ventana de contexto <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo>\u00b1<\/mo><mn>2<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\pm 2<\/annotation><\/semantics><\/math>, entonces el par <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mtext>fezo<\/mtext><mo separator=\"true\">,<\/mo><mtext>libro<\/mtext><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(\\text{fezo}, \\text{libro})<\/annotation><\/semantics><\/math> constituye un ejemplo positivo, ya que <code>libro<\/code> aparece realmente en la ventana de contexto de <code>fezo<\/code>. En cambio, si tomamos una palabra del vocabulario que no aparece en esa ventana, por ejemplo <code>aql<\/code>, entonces el par <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mtext>fezo<\/mtext><mo separator=\"true\">,<\/mo><mtext>aql<\/mtext><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(\\text{fezo}, \\text{aql})<\/annotation><\/semantics><\/math> puede utilizarse como ejemplo negativo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para formalizar esta tarea, definimos una variable binaria <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>D<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">D<\/annotation><\/semantics><\/math> tal que<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>D<\/mi><mo>=<\/mo><mrow><mo fence=\"true\" form=\"prefix\">{<\/mo><mtable><mtr><mtd class=\"tml-left\" style=\"padding-left:0em;padding-right:0em;\"><mn>1<\/mn><\/mtd><mtd class=\"tml-left\" style=\"padding-left:1em;padding-right:0em;\"><mrow><mtext>si&nbsp;<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mtext>&nbsp;es&nbsp;un&nbsp;par&nbsp;positivo<\/mtext><\/mrow><\/mtd><\/mtr><mtr><mtd class=\"tml-left\" style=\"padding-left:0em;padding-right:0em;\"><mn>0<\/mn><\/mtd><mtd class=\"tml-left\" style=\"padding-left:1em;padding-right:0em;\"><mrow><mtext>si&nbsp;<\/mtext><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mtext>&nbsp;es&nbsp;un&nbsp;par&nbsp;negativo<\/mtext><\/mrow><\/mtd><\/mtr><\/mtable><mo fence=\"true\" form=\"postfix\"><\/mo><\/mrow><\/mrow><annotation encoding=\"application\/x-tex\">D =\n\\begin{cases}\n1 &amp; \\text{si } (w,c) \\text{ es un par positivo} \\\\\n0 &amp; \\text{si } (w,c) \\text{ es un par negativo}\n\\end{cases}\n<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Entonces, el objetivo del clasificador es estimar la probabilidad<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>D<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>C<\/mi><mo>=<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(D=1|W=w,C=c),<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">es decir, la probabilidad de que el par observado <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>c<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c)<\/annotation><\/semantics><\/math> corresponda a una co-ocurrencia real de la palabra objetivo con una palabra de contexto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En t\u00e9rminos pr\u00e1cticos, para calcular esa probabilidad, el modelo asigna un vector denso para la palabra objetivo, y otro vector denso para la palabra candidata. Luego, para ver si ambos vectores son similares, se calcula el producto punto entre ambos, es decir,<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>w<\/mi><mo>\u22c5<\/mo><mi>c<\/mi><mo>=<\/mo><msub><mi>w<\/mi><mn>1<\/mn><\/msub><msub><mi>c<\/mi><mn>1<\/mn><\/msub><mo>+<\/mo><msub><mi>w<\/mi><mn>2<\/mn><\/msub><msub><mi>c<\/mi><mn>2<\/mn><\/msub><mo>+<\/mo><mi>.<\/mi><mi>.<\/mi><mi>.<\/mi><mo>+<\/mo><msub><mi>c<\/mi><mi>d<\/mi><\/msub><msub><mi>w<\/mi><mi>d<\/mi><\/msub><mo separator=\"true\">,<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">w\\cdot c = w_{1}c_{1} + w_{2}c_{2} + &#8230; + c_{d}w_{d},<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">y finalmente, para convertirlo en una probabilidad, se pasa por la sigmoide<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo>\u22c5<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mn>1<\/mn><mrow><mn>1<\/mn><mo>+<\/mo><msup><mi>e<\/mi><mrow><mo lspace=\"0em\" rspace=\"0em\">\u2212<\/mo><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo>\u22c5<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\" lspace=\"0em\" rspace=\"0em\">)<\/mo><\/mrow><\/msup><\/mrow><\/mfrac><mo>=<\/mo><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>D<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>C<\/mi><mo>=<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">\\sigma(w\\cdot c) = \\frac{1}{1+e^{-(w\\cdot c)}} = P(D=1|W=w,C=c).<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Recordar que la sigmoide es creciente en su argumento, por tanto un mayor producto punto -que indica una mayor similitud entre vectores-, implicar\u00e1 una mayor probabilidad de que la palabra de que <code>(w,c)<\/code> sea un par positivo. Al asumir que todas las palabras de contexto son independientes, tenemos:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>D<\/mi><mn>1<\/mn><\/msub><mo>=<\/mo><mn>1<\/mn><mo separator=\"true\">,<\/mo><msub><mi>D<\/mi><mn>2<\/mn><\/msub><mo>=<\/mo><mn>1<\/mn><mo separator=\"true\">,<\/mo><mi>.<\/mi><mi>.<\/mi><mi>.<\/mi><mo separator=\"true\">,<\/mo><msub><mi>D<\/mi><mi>L<\/mi><\/msub><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msub><mi>C<\/mi><mn>1<\/mn><\/msub><mo>=<\/mo><msub><mi>c<\/mi><mn>1<\/mn><\/msub><mo separator=\"true\">,<\/mo><msub><mi>C<\/mi><mn>2<\/mn><\/msub><mo>=<\/mo><msub><mi>c<\/mi><mn>2<\/mn><\/msub><mo separator=\"true\">,<\/mo><mi>.<\/mi><mi>.<\/mi><mi>.<\/mi><mo separator=\"true\">,<\/mo><msub><mi>C<\/mi><mi>L<\/mi><\/msub><mo>=<\/mo><msub><mi>c<\/mi><mi>L<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mrow><munderover><mo movablelimits=\"false\">\u220f<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>L<\/mi><\/munderover><\/mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msub><mi>c<\/mi><mi>i<\/mi><\/msub><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(D_{1}=1, D_{2} =1,&#8230;,D_{L} =1|W =w,C_{1} = c_{1}, C_{2} = c_{2}, &#8230;, C_{L} = c_{L}) = \\prod_{i=1}^{L}\\sigma(c_{i}\\cdot w)<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">que es la expresi\u00f3n para la probabilidad del contexto completo observado, dado el target <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>, bajo la hip\u00f3tesis de que las palabras de contexto son condicionalmente independientes, donde <math data-latex=\"L\"><semantics><mi>L<\/mi><annotation encoding=\"application\/x-tex\">L<\/annotation><\/semantics><\/math> es el tama\u00f1o la ventana de contexto. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Aprendiendo los par\u00e1metros del modelo<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El algoritmo de aprendizaje de skip-gram con sampleo negativo toma como entrada un corpus de texto y un tama\u00f1o de vocabulario <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>N<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">N<\/annotation><\/semantics><\/math>. A continuaci\u00f3n, asigna de manera aleatoria embeddings iniciales a las palabras del vocabulario. M\u00e1s precisamente, el modelo almacena dos vectores por palabra: uno cuando la palabra act\u00faa como palabra objetivo y otro cuando act\u00faa como palabra de contexto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Luego, a partir del corpus, extrae pares positivos formados por una palabra objetivo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math> y una palabra de contexto real <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">c^{pos}<\/annotation><\/semantics><\/math> dentro de una ventana dada. Para cada uno de estos pares, genera adem\u00e1s <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> pares negativos <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c^{neg})<\/annotation><\/semantics><\/math>, donde <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">c^{neg}<\/annotation><\/semantics><\/math> es una palabra de ruido muestreada aleatoriamente del l\u00e9xico.  A modo de ejemplo, consideremos la siguiente oraci\u00f3n:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">q fezo sus <code>cosas<\/code> bien copuestas.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">cuya palabra objetivo es <code>cosas<\/code> y ventana de contexto +-2 palabras, entonces, podemos construir la siguiente tabla:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>fezo<\/td><td>sus<\/td><td><code>cosas<\/code><\/td><td>bien<\/td><td>compuestas<\/td><\/tr><tr><td><math data-latex=\"c_{1}\"><semantics><msub><mi>c<\/mi><mn>1<\/mn><\/msub><annotation encoding=\"application\/x-tex\">c_{1}<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"c_{2}\"><semantics><msub><mi>c<\/mi><mn>2<\/mn><\/msub><annotation encoding=\"application\/x-tex\">c_{2}<\/annotation><\/semantics><\/math><\/td><td>w<\/td><td><math data-latex=\"c_{3}\"><semantics><msub><mi>c<\/mi><mn>3<\/mn><\/msub><annotation encoding=\"application\/x-tex\">c_{3}<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"c_{4}\"><semantics><msub><mi>c<\/mi><mn>4<\/mn><\/msub><annotation encoding=\"application\/x-tex\">c_{4}<\/annotation><\/semantics><\/math><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Luego, debemos seleccionar los ejemplos positivos y negativos. Para el primer grupo tenemos:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"c^{pos}\"><semantics><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">c^{pos}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>fezo<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>sus<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>bien<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>compuestas<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Para caracterizar el segundo grupo, debemos fijar el valor de <math data-latex=\"k\"><semantics><mi>k<\/mi><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math>. En este caso, con <math data-latex=\"k=2\"><semantics><mrow><mi>k<\/mi><mo>=<\/mo><mn>2<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">k=2<\/annotation><\/semantics><\/math> tenemos:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math><\/td><td><math data-latex=\"c^{neg}\"><semantics><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">c^{neg}<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>limpia<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>fablo<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>della<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>recibieron<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>altos<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>reyes<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>assi<\/td><\/tr><tr><td><code>cosas<\/code><\/td><td>dizen<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Cuando caracterizamos el segundo grupo en la tabla anterior, elegimos arbitrariamente las &#8216;palabas ruidosas&#8217;. No obstante, en la pr\u00e1ctica, deben ser tomadas de forma aleatoria desde el lexicon (cuya \u00fanica restricci\u00f3n es que no contengan la palabra objetivo). La principal funci\u00f3n que tienen estas palabras, es proporcionar un ejemplo negativo de entrenamiento. De este modo, el modelo aprende no solo a acercar la palabra objetivo a sus contextos reales, sino tambi\u00e9n a alejarla de palabras muestreadas como ruido en los ejemplos negativos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dichas palabras son elegidas de acuerdo a la siguiente probabilidad:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><msub><mi>P<\/mi><mi>\u03b1<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mfrac><mrow><mi>c<\/mi><mi>o<\/mi><mi>u<\/mi><mi>n<\/mi><mi>t<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mi>\u03b1<\/mi><\/msup><\/mrow><mrow><msub><mo movablelimits=\"false\">\u2211<\/mo><msup><mi>w<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msup><\/msub><mi>c<\/mi><mi>o<\/mi><mi>u<\/mi><mi>n<\/mi><mi>t<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msup><mi>w<\/mi><mo lspace=\"0em\" rspace=\"0em\" class=\"tml-prime\">\u2032<\/mo><\/msup><msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mi>\u03b1<\/mi><\/msup><\/mrow><\/mfrac><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">P_{\\alpha}(w) = \\frac{count(w)^{\\alpha}}{\\sum_{w&#8217;}count(w&#8217;)^{\\alpha}}.<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Donde <strong>count(w)<\/strong> cuenta cu\u00e1ntas veces aparece <math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math> en el corpus, y el denominador normaliza la suma de los conteos ponderados de todas las palabras del vocabulario. <math data-latex=\"\\alpha\"><semantics><mi>\u03b1<\/mi><annotation encoding=\"application\/x-tex\">\\alpha<\/annotation><\/semantics><\/math> es un par\u00e1metro que cuando es menor que uno, permite que los eventos raros sean mas probables que cuando <math data-latex=\"\\alpha=1\"><semantics><mrow><mi>\u03b1<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\alpha=1<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A modo de resumen parcial, hasta este punto tenemos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li> <strong>Conjunto inicial de embeddings<\/strong>: cada palabra del vocabulario tiene un vector para cuando act\u00faa como palabra objetivo y palabra de contexto, inicializados de forma aleatoria, con <math data-latex=\"d\"><semantics><mi>d<\/mi><annotation encoding=\"application\/x-tex\">d<\/annotation><\/semantics><\/math> elementos.<\/li>\n\n\n\n<li><strong>Conjunto de ejemplos positivos y negativos<\/strong>: el conjunto de ejemplos positivos se obtuvo del corpus, y el negativo se sample\u00f3 acorde a <math data-latex=\"P_{\\alpha}(w)\"><semantics><mrow><msub><mi>P<\/mi><mi>\u03b1<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P_{\\alpha}(w)<\/annotation><\/semantics><\/math>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Con esto en mente, la estrategia que debemos seguir, es hacer que el modelo aprenda la posici\u00f3n de las palabras objetivo en el espacio de <strong>embeddings<\/strong>, es decir, cu\u00e1les quedan \u201clejos\u201d, y cu\u00e1les quedan \u201ccerca\u201d. En este sentido, \u201ccerca\u201d o \u201clejos\u201d, depender\u00e1 de <math data-latex=\"P(D=1|W=w,C=c)\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>D<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>C<\/mi><mo>=<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(D=1|W=w,C=c)<\/annotation><\/semantics><\/math>, que a su vez, como vimos, depende del producto punto entre <math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math> y <math data-latex=\"c\"><semantics><mi>c<\/mi><annotation encoding=\"application\/x-tex\">c<\/annotation><\/semantics><\/math>. Espec\u00edficamente, si una palabra <math data-latex=\"c^{pos}\"><semantics><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><annotation encoding=\"application\/x-tex\">c^{pos}<\/annotation><\/semantics><\/math> apareci\u00f3 en la ventana real de la palabra objetivo (<math data-latex=\"w\"><semantics><mi>w<\/mi><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>), el modelo asignar\u00e1 pesos tal que el producto punto (<math data-latex=\"w\\cdot c^{pos}\"><semantics><mrow><mi>w<\/mi><mo>\u22c5<\/mo><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">w\\cdot c^{pos}<\/annotation><\/semantics><\/math>) sea <strong>alto<\/strong>, porque as\u00ed, <math data-latex=\"P(D=1|W=w,C=c)\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>D<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>C<\/mi><mo>=<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(D=1|W=w,C=c)<\/annotation><\/semantics><\/math> tambi\u00e9n dar\u00e1 un valor relativamente alto. Por otra parte, si <math data-latex=\"c_{neg}\"><semantics><msub><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msub><annotation encoding=\"application\/x-tex\">c_{neg}<\/annotation><\/semantics><\/math> fue muestreada como <strong>noise word<\/strong>, entonces, se asignar\u00e1n pesos tal que <math data-latex=\"w\\cdot c^{neg}\"><semantics><mrow><mi>w<\/mi><mo>\u22c5<\/mo><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">w\\cdot c^{neg}<\/annotation><\/semantics><\/math> sea bajo, para que el modelo considere improbable que ese par sea un contexto real. Este principio se puede formalizar con la siguiente <strong>loss function<\/strong>:<\/p>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><\/math><\/div>\n\n\n\n<div class=\"wp-block-math\"><math display=\"block\"><semantics><mrow><mi>L<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mtext>pos<\/mtext><\/msup><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mtext>neg<\/mtext><mo lspace=\"0em\" rspace=\"0em\">\u2217<\/mo><\/mrow><\/msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mrow><mo fence=\"true\" form=\"prefix\">[<\/mo><mrow><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msup><mi>c<\/mi><mtext>pos<\/mtext><\/msup><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>+<\/mo><mrow><munderover><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>k<\/mi><\/munderover><\/mrow><mrow><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mspace width=\"0.1667em\"><\/mspace><msup><mi>c<\/mi><msub><mtext>neg<\/mtext><mi>i<\/mi><\/msub><\/msup><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo fence=\"true\" form=\"postfix\">]<\/mo><\/mrow><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L(w, c^{\\text{pos}}, c^{\\text{neg}*}) = -\\left[\n\\log \\sigma(c^{\\text{pos}} \\cdot w)\n+\n\\sum_{i=1}^{k} \\log \\sigma(-\\,c^{\\text{neg}_i} \\cdot w)\n\\right].<\/annotation><\/semantics><\/math><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Finalmente, podemos resumir el algoritmo completo en los siguientes pasos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Se fija un tama\u00f1o de vocabulario <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>N<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">N<\/annotation><\/semantics><\/math> y una ventana de contexto, por ejemplo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo>\u00b1<\/mo><mn>2<\/mn><\/mrow><annotation encoding=\"application\/x-tex\">\\pm 2<\/annotation><\/semantics><\/math>.<\/li>\n\n\n\n<li>Inicializamos dos embeddings aleatorios por palabra.<\/li>\n\n\n\n<li><strong>Construimos ejemplos positivos<\/strong>: Recorremos el texto, y para cada palabra objetivo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>, tomamos las palabras que est\u00e1n dentro de su ventana de contexto como pares positivos <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c^{pos})<\/annotation><\/semantics><\/math>.<\/li>\n\n\n\n<li><strong>Construimos ejemplos negativos<\/strong>: Para cada par positivo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mi>p<\/mi><mi>o<\/mi><mi>s<\/mi><\/mrow><\/msup><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c^{pos})<\/annotation><\/semantics><\/math>, generamos <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>k<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">k<\/annotation><\/semantics><\/math> pares negativos <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c^{neg})<\/annotation><\/semantics><\/math>. Cada <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msup><mi>c<\/mi><mrow><mi>n<\/mi><mi>e<\/mi><mi>g<\/mi><\/mrow><\/msup><\/mrow><annotation encoding=\"application\/x-tex\">c^{neg}<\/annotation><\/semantics><\/math> es una <strong>noise word<\/strong>, es decir, una palabra aleatoria del vocabulario, con la \u00fanica restricci\u00f3n expl\u00edcita de no ser la palabra objetivo <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>. Esas palabras negativas no se eligen uniformemente, se muestrean seg\u00fan <math data-latex=\"P_{\\alpha}(w)\"><semantics><mrow><msub><mi>P<\/mi><mi>\u03b1<\/mi><\/msub><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P_{\\alpha}(w)<\/annotation><\/semantics><\/math>.<\/li>\n\n\n\n<li><strong>Definimos un clasificador binario<\/strong>: Dado un par <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mo stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>c<\/mi><mo stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">(w,c)<\/annotation><\/semantics><\/math>, el modelo quiere estimar la probabilidad de que <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>c<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">c<\/annotation><\/semantics><\/math> sea una palabra de contexto real de <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>w<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">w<\/annotation><\/semantics><\/math>. Para eso, usamos <math data-latex=\"P(D=1|W=w,C=c) = \\sigma(c \\cdot w)\"><semantics><mrow><mi>P<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>D<\/mi><mo>=<\/mo><mn>1<\/mn><mi>|<\/mi><mi>W<\/mi><mo>=<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><mi>C<\/mi><mo>=<\/mo><mi>c<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>c<\/mi><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">P(D=1|W=w,C=c) = \\sigma(c \\cdot w)<\/annotation><\/semantics><\/math>.<\/li>\n\n\n\n<li>Definimos la siguiente loss function: <br><math data-latex=\"L(w, c^{\\text{pos}}, c^{\\text{neg}*}) = -\\left[ \\log \\sigma(c^{\\text{pos}} \\cdot w) + \\sum_{i=1}^{k} \\log \\sigma(-\\,c^{\\text{neg}_i} \\cdot w) \\right].\"><semantics><mrow><mi>L<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>w<\/mi><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mtext>pos<\/mtext><\/msup><mo separator=\"true\">,<\/mo><msup><mi>c<\/mi><mrow><mtext>neg<\/mtext><mo lspace=\"0em\" rspace=\"0em\">\u2217<\/mo><\/mrow><\/msup><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>=<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mrow><mo fence=\"true\" form=\"prefix\">[<\/mo><mrow><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><msup><mi>c<\/mi><mtext>pos<\/mtext><\/msup><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo>+<\/mo><msubsup><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>i<\/mi><mo>=<\/mo><mn>1<\/mn><\/mrow><mi>k<\/mi><\/msubsup><mrow><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><mi>\u03c3<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mo form=\"prefix\" stretchy=\"false\">\u2212<\/mo><mspace width=\"0.1667em\"><\/mspace><msup><mi>c<\/mi><msub><mtext>neg<\/mtext><mi>i<\/mi><\/msub><\/msup><mo>\u22c5<\/mo><mi>w<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo fence=\"true\" form=\"postfix\">]<\/mo><\/mrow><mi>.<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">L(w, c^{\\text{pos}}, c^{\\text{neg}*}) = -\\left[ \\log \\sigma(c^{\\text{pos}} \\cdot w) + \\sum_{i=1}^{k} \\log \\sigma(-\\,c^{\\text{neg}_i} \\cdot w) \\right].<\/annotation><\/semantics><\/math><\/li>\n\n\n\n<li>Actualizamos los <strong>embeddings <\/strong>con descenso por gradiente estoc\u00e1stico: El modelo recorre el corpus y, en cada ejemplo, ajusta las filas relevantes de <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>W<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">W<\/annotation><\/semantics><\/math> y <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><mi>C<\/mi><\/mrow><annotation encoding=\"application\/x-tex\">C<\/annotation><\/semantics><\/math> para minimizar esa <strong>loss function<\/strong>.<\/li>\n\n\n\n<li>Repetir muchas veces.<\/li>\n\n\n\n<li>Finalmente, obtenemos el <strong>embedding<\/strong> final de cada palabra: Como el modelo aprendi\u00f3 dos vectores por palabra, algunos investigadores la representan como <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>w<\/mi><mi>i<\/mi><\/msub><mo>+<\/mo><msub><mi>c<\/mi><mi>i<\/mi><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">w_i+c_i<\/annotation><\/semantics><\/math>, o bien, solo como <math xmlns=\"http:\/\/www.w3.org\/1998\/Math\/MathML\"><semantics><mrow><msub><mi>w<\/mi><mi>i<\/mi><\/msub><\/mrow><annotation encoding=\"application\/x-tex\">w_i<\/annotation><\/semantics><\/math>.\u200b<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Como dijimos anteriormente, los vectores densos resultantes de este proceso suelen capturar mejor la similitud entre palabras sem\u00e1nticamente cercanas, y poseen menos dimensiones que los vectores calculados a partir del m\u00e9todo basado en el conteo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><em>Notas finales, y pr\u00f3xima entrada\u2026<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta entrada revisamos el concepto de <em>embeddings<\/em>, es decir, representaciones vectoriales que capturan aspectos del uso y significado de las palabras. Vimos que estas representaciones pueden construirse tanto con vectores dispersos como con vectores densos, y analizamos las principales propiedades de cada enfoque.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En la pr\u00f3xima entrada nos adentraremos en las redes neuronales, un componente fundamental en la construcci\u00f3n de los LLM. Stay tuned!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jos\u00e9 Miguel Mu\u00f1oz Urra \u2013\u00a0<a href=\"mailto:jmunozu@pulki.es\">jmunozu@pulki.es<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>En la entrega pasada vimos como clasificar textos usando la regresi\u00f3n log\u00edstica. En la presente, discutiremos embeddings, que en t\u00e9rminos simples, se define como una representaci\u00f3n vectorial del significado de las palabras. Aprender embeddings en el contexto de este proyecto es importante, porque son una piedra angular en los LLM contempor\u00e1neos. Como es usual, usaremos [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-132","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/132","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=132"}],"version-history":[{"count":19,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/132\/revisions"}],"predecessor-version":[{"id":156,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/posts\/132\/revisions\/156"}],"wp:attachment":[{"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=132"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=132"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pulki.es\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=132"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}