Antonio

Embedings: ¿Existe un universo de palabras?

Por Antonio Piedra Fernández | 5 de Diciembre de 2024

Estoy encantado de iniciar mi nuevo blog donde explicaré técnicamente el desarrollo de mis proyectos. También daré opiniones sobre tecnología y, sobre todo, IA.

En este caso, os traigo un explicación con código y demo de un juego de palabras utilizando el concepto de embeddings.

blog_icon
Aviso

Este post no pretende en ningún caso ser técnico, pretende dar una visión educativa para que el concepto se entienda. Es posible que haya pequeñas licencias que no sean 100% reales a cambio de dar una mejor explicación.

Introducción

El otro día, estaba cursando mi segunda clase semanal de inglés. Mi profesor, Bryan, para finalizar la clase, decidió hacer un juego de acertar la palabra. Ese juego, al decir una palabra, te decía a cuánta distancia te habías quedado de la palabra que debías acertar. En ese momento, me puse a pensar. ¿Cómo se hizo este juego? ¿qué tecnología hay detrás de este juego?

Por algún motivo este juego iba bastante mal. Por ejemplo, la palabra "colina" era muy cercana a la palabra "hospital". Todo esto me hizo pensar que tendrían algún tipo de matriz masiva que relacionaba las palabras. Pero, ¿cómo podríamos implementar este juego de forma sólida? ¿Es la matriz masiva la solución ideal?

Pues mi intención con esta publicación es mostrar como crear este juego utilizando el concepto de Embeddings.

Embeddings

¿Alguna vez te has preguntado como tecnologías como ChatGPT pueden entender las palabras que les dirigimos? Al final del día un procesador solo es capaz de entender datos numéricos. ¿Hay alguna forma de representar numéricamente un concepto para ser interpretado por un computador? ¿Cómo hace ChatGPT para que, cuando le preguntas como hacer una galletitas saladas entender qué es una galletita salada y pensar como cocinarla? La respuesta a estas preguntas radica en el concepto de embeddings

En la escuela nos enseñaron el concepto de espacio vectorial. En estos espacios puedes situar puntos y realizar operaciones sobre ellos. La tecnología embeddings propone transformar conceptos en puntos de un espacio vectorial determinado, es decir, propone crear un universo que contiene todos los conceptos, tanto ya existentes como por descubrir, solo tienes que encontrar una forma de calcular el punto del concepto que quieras.

Este espacio tiene algunas cualidades muy interesantes. En estos espacios puedes encontrar ejes que representan características de los objetos que contiene. Por ejemplo, imaginemos que encontramos un eje "temperatura", en este eje podríamos encontrar en un extremo el concepto de cero absoluto y en el otro el concepto de sol.

blog_icon
Embedding

Un embedding es una representación vectorial de elementos complejos en un espacio de dimensiones reducidas, diseñada para preservar sus relaciones semánticas o estructurales. Mediante técnicas de aprendizaje automático, los embeddings transforman entidades como palabras, imágenes o nodos en vectores que capturan sus características y similitudes relevantes, facilitando así su procesamiento y análisis en tareas de inteligencia artificial.

He preparado una representación en solo 3 dimensiones de lo que es un espacio vectorial de embeddings. En este caso, el espacio consta de 3 ejes:

Si pasas el ratón por uno de los puntos puedes ver que valor tiene el concepto en cada eje y su concepto más cercano (más adelante veremos como se calcula esto)

Representación de espacio de embeddings en 3 dimensiones.

En este punto, el lector se preguntará, muy bien entendido pero, ¿Cómo consigo una herramienta que pasé de una palabra a una representación vectorial? No desarrollaré esto en esta publicación, puesto que amerita una serie de publicaciones ello solo. Sí que mentaré que lo más utilizado en la actualidad son modelos basados en BERT, que mediante el famoso mecanismo de atención es capaz de "extraer el significado de una cadena de caracteres".

El estado del arte en embeddings open source es Snowflake's Arctic. En este proyecto decidí usar all-MiniLM-L6-v2 .

Similitud entre palabras

Una vez se obtuvo la forma de conseguir las representaciones embeddings, hay que obtener una forma de calcular la distancia entre palabras. Esta parte se realiza como en cualquier otro espacio vectorial.

La similitud se realizará mediante el producto punto entre las magnitudes de ambas palabras.

magnitud=iembeddingi2\text{magnitud} = \sqrt{\sum_{i} \, \text{embedding}_i^2}
productoPunto=iembedding1iembedding2i\text{productoPunto} = \sum_{i} \, \text{embedding1}_i \cdot \text{embedding2}_i
similitud=productoPunto(magnitud1magnitud2)\text{similitud} = \frac{\text{productoPunto}} {(\text{magnitud1} \cdot \text{magnitud2})}

El código en javascript sería el siguiente.

javascript
function similarity(embedding1, embedding2){
  const dotProduct = embedding1.reduce((sum, a, i) => sum + a * embedding2[i], 0);
  const magnitudeA = Math.sqrt(embedding1.reduce((sum, a) => sum + a * a, 0));
  const magnitudeB = Math.sqrt(embedding2.reduce((sum, b) => sum + b * b, 0));
  return dotProduct / (magnitudeA * magnitudeB);
}

Esta solución, en la práctica tuvo un problema, y es que la mayoría de palabras se situaban entre 0.3 y 0.7, por lo que, cuando un usuario se acercaba a la palabra solución no era tan evidente. Por lo que decidí aplicar una función softmax que apliase el valor de las palabras por encima de similitud 0.5 y redujese las que estaban por debajo.

Esta es una función muy utilizada en el ámbito de la IA, sobre todo como activación en las últimas capas de un sistema. En este caso, por la naturaleza de los embeddings, necesitaba que estuviese entre 0 y 1, por lo que no es la función softmax clásica, la cual genera resultados entre -1 y 1. A continuación se muestra la forma de esta función.

A continuación, se muestran tanto su ecuación matemática como su cálculo en javascript.

softmax(v)=e(v0.5)10e(v0.5)10+1\text{softmax}(v) = \frac{e^{(v - 0.5) \cdot 10}}{e^{(v - 0.5) \cdot 10} + 1}
javascript
function softmax(value) {
  const scaledValue = (value - 0.5) * 10;
  const expValue = Math.exp(scaledValue);
  const expBaseline = Math.exp(0); 
  return expValue / (expValue + expBaseline);
}

Implementación

Por último, queda la parte más sencilla. La implementación. Para implementar el juego decidí utilizar Astrojs con su módulo para utilizar islas, en concreto de Svelte.

Los embeddings son calculados por HuggingFace, que permite 1000 llamadas al día de forma gratuíta con el endpoint de all-MiniLM-L6-v2

Importante

Si se sigue este approach, es muy importante que la llamada al endpoint de HuggingFace se realize en serverside. Puesto que si no estarás compartiendo tu token con todo el mundo. En este caso la solución fue envolver esta llamada en la api de astrojs.

Todo el código fue publicado en el repositorio apfgijon/guessword (sueltame una estrellita si te mola porfa 🥲).

Este código fue puesto en producción con Vercel, puedes ver la demo en guess.antoniopiedra.es.

Conclusión

Realmente, una vez probado con mis compañeros de clase de inglés, esta solución fue bastante más coherente que la utilizada anteriormente. Desgraciadamente, al generar las pistas con ChatGPT, las palabras se hicieron demasiado evidentes y el juego terminó siendo demasiado fácil. Es posible que en el futuro dedique tiempo a generar bien las palabras para jugar.

Está claro que existe, no solo uno, si no infinitos universos donde todas las palabras están relacionadas entre sí. Solo encesitamos entrenar un nuevo modelo de embeddings para explorar como se relacionan todas las palabras en él.

¿Te gustó esta publicación? ¡Hazmelo saber en mi perfil de LinkedIn!