Las incrustaciones (opens new window) son representaciones numéricas de datos que capturan la esencia semántica de palabras o frases. Estas incrustaciones se codifican como vectores de alta dimensionalidad, lo que permite un procesamiento eficiente en diversas aplicaciones de datos. Las incrustaciones pueden variar según los modelos utilizados. El mismo texto puede tener una incrustación diferente si se genera utilizando diferentes modelos. Si bien los datos de texto son el enfoque principal, las incrustaciones no se limitan a la información textual; también se pueden aplicar a imágenes, gráficos y otros tipos de datos.
Las incrustaciones son aún más valiosas en el contexto de las bases de datos vectoriales (opens new window); podemos aplicar medidas básicas de similitud/distancia en ellas para verificar qué tan similares o diferentes son un par de textos (u otros datos), también podemos usarlas para encontrar las muestras más relacionadas, entre otras cosas. Su uso es de fundamental importancia sin requerir mucho conocimiento previo.
En este blog, exploraremos cómo generar y utilizar incrustaciones con modelos populares como OpenAI, Jina y Bedrock. La función EmbedText() de MyScale proporciona un enfoque simplificado para trabajar con incrustaciones, especialmente cuando se trata de datos a gran escala. Al aprovechar esta herramienta, puede integrar sin problemas la búsqueda basada en incrustaciones y otras capacidades de procesamiento de texto en sus aplicaciones, todo ello aprovechando el rendimiento y la rentabilidad de MyScale.
# Modelos de incrustación
Tradicionalmente, tenemos modelos de incrustación clásicos como Word2Vec (opens new window), GloVe (opens new window) y LSA (opens new window). Más tarde, fueron gradualmente reemplazados por modelos basados en RNN/LSTM. Las capacidades incomparables de los transformadores trajeron otro cambio de paradigma, ya que ahora los transformadores son los modelos de facto para las incrustaciones. Para generar incrustaciones, podemos emplear algunas formas:
- Entrenar un modelo: Si tiene buenos recursos informáticos o tal vez hay un problema de privacidad de datos, etc., entrenarlo localmente sería una buena opción. Aquí, entrenar no significa necesariamente entrenar un modelo desde cero, sino que también incluye el ajuste fino (como hacemos la mayor parte del tiempo). Una vez entrenado, podemos usar este modelo para generar incrustaciones.
- Utilizar un modelo preentrenado: Por lo general, no necesitamos entrenar ni ajustar un modelo para las incrustaciones. Existe un conjunto de modelos estándar que se utilizan para la generación de incrustaciones en la mayoría de los casos, como la serie GPT, algunos SLM como Phi-series, Titan, Llama y Falcon, etc.
Con los modelos preentrenados, tenemos algunas opciones. Los modelos de HuggingFace son una excelente opción si prefieres ejecutarlos localmente (la inferencia no requiere necesariamente GPUs, por lo que solo se necesita una computadora portátil normal). Por otro lado, también tenemos muchos servicios basados en la nube para estos modelos de incrustación. OpenAI, Cohere y Jina son algunos buenos ejemplos. Otra razón por la que estos servicios son útiles es la falta de disponibilidad de algunos modelos de última generación (como la familia GPT-4).
# Uso de modelos en HuggingFace
Varios modelos de transformadores se alojan en Hugging Face (opens new window), incluidos los utilizados específicamente para incrustaciones. Para utilizar un modelo de Hugging Face, debemos llamar al modelo y al tokenizador respectivos. Estos modelos pueden tardar un tiempo en descargarse. Una vez descargados, permanecen en la caché y se pueden gestionar utilizando la consola de Hugging Face de PyCharm.
from transformers import AutoModel, AutoTokenizer
nombreModelo = "TencentBAC/Conan-embedding-v1"
tokenizer = AutoTokenizer.from_pretrained(nombreModelo)
modelo = AutoModel.from_pretrained(nombreModelo)
texto = "Todos los seres humanos nacen libres e iguales en dignidad y derechos. Están dotados de razón y conciencia y deben comportarse fraternalmente los unos con los otros."
inputs = tokenizer(texto, return_tensors="pt")
incrustacionesTokens = modelo(**inputs).last_hidden_state
Sería útil convertir estas incrustaciones de tokens en incrustaciones de oraciones mediante su agrupación.
incrustacionOracion = incrustacionesTokens.mean(dim=1)
incrustacionOracion[0]
El uso de Hugging Face para las incrustaciones es bastante bueno, pero puede ser problemático en algunos casos, como:
- Recursos limitados: Por lo general, los modelos de Hugging Face no ocupan mucho espacio (en promedio 1 GB), pero si tiene poco espacio en disco o memoria, probablemente no sea una opción factible.
- Indisponibilidad de algunos modelos: Si bien muchos modelos se alojan en Hugging Face, todavía hay algunos modelos que se alojan en servicios dedicados (el GPT-4 viene rápidamente a la mente, pero también hay otros).
Para utilizar estos modelos de forma remota, utilizamos diferentes servicios como OpenAI, Amazon Bedrock, Cohere, entre otros. Podemos utilizarlos en conjunto con MyScale, como veremos a continuación.
# Obteniendo incrustaciones con MyScale
Como hemos discutido, las incrustaciones son fundamentales para las aplicaciones de bases de datos vectoriales. MyScale proporciona la capacidad, la función EmbedText, para calcular incrustaciones (solo texto hasta ahora) utilizando una variedad de servicios, incluidos OpenAI, Cohere, Jina, etc. Esta integración es muy útil, ya que simplifica la conversión de texto en vectores. Admite el agrupamiento automático para un alto rendimiento y es útil tanto para la búsqueda en tiempo real como para el procesamiento por lotes. Esta función toma algunos parámetros esenciales:
texto
: La cadena de texto cuya incrustación deseamos calcular. Por ejemplo,'No puedes ser falso con nadie.'
.proveedor
: Nombre del proveedor del modelo de incrustación (8 compatibles hasta ahora; no distingue mayúsculas y minúsculas). Por ejemplo,OpenAI
.api_key
: Clave de API para la cuenta del proveedor respectivo. Se proporciona directamente por el servicio y no tiene nada que ver con MyScale.
Nota: Todos estos servicios de incrustación cobran en función del uso de tokens, por lo que es crucial evitar calcular repetidamente las mismas incrustaciones. En su lugar, una vez generadas, las incrustaciones deben guardarse en una base de datos para su reutilización eficiente.
Además de estos parámetros, también tenemos base_url
como un parámetro opcional (no requerido para la mayoría de los proveedores).
# Uso de modelos de OpenAI
El siguiente fragmento de código muestra cómo utilizar las incrustaciones de OpenAI con EmbedText()
:
SELECT EmbedText('<texto-de-entrada>', 'openAI', '', '<Clave de acceso>', '{"model":"text-embedding-3-small", "batch_size":"50"}')
Aquí también tenemos los argumentos opcionales de URL de API, dimensiones de vector de salida e ID de usuario. OpenAI proporciona 3 modelos de incrustación, Ada-002 y Embedding-3 (pequeño y grande). Como puede ver, la versión grande produce un vector de incrustación de longitud 3072 que tiene una mejor resolución y puede ser muy útil al tratar con documentos más grandes.
Modelo | Descripción | Dimensión de salida |
---|---|---|
text-embedding-3-large | Modelo de incrustación más capaz para tareas en inglés y no inglés | 3,072 |
text-embedding-3-small | Mejora del rendimiento sobre el modelo de incrustación Ada de segunda generación | 1,536 |
text-embedding-ada-002 | Modelo de incrustación de segunda generación más capaz, reemplazando 16 modelos de primera generación | 1,536 |
Para mostrar el poder incluso de sus modelos más pequeños, tomemos un gran fragmento de texto (todo un capítulo) y generemos sus incrustaciones.
proveedor_servicio = 'OpenAI'
textoEntrada = 'Dejaremos a Danglars luchando con el demonio del odio y tratando de insinuar en el oído del propietario del barco algún mal...' # truncado aquí a propósito
parámetros = {'sampleString': textoEntrada, 'serviceProvider': proveedor_servicio}
x = client.query("""
SELECT EmbedText({sampleString:String}, {serviceProvider:String}, '', 'sk-xxxxxxxx', '{"model":"text-embedding-3-small", "batch_size":"50"}')
""", parameters=parameters)
print(x.result_rows[0][0])
Nota: EmbedText()
puede tomar un texto de hasta 5600 caracteres de longitud.
Aquí estamos tomando result_rows[0][0]
ya que usar result_rows
directamente trae una anidación innecesaria de lista y tupla.
# Uso de modelos de Bedrock
Amazon Bedrock brinda a los usuarios acceso a varios modelos fundamentales. Antes de utilizar cualquier modelo, primero debes obtener su acceso (es bastante sencillo). Luego, podemos llamarlo directamente desde MyScale de la siguiente manera:
SELECT EmbedText('<texto-de-entrada>', 'Bedrock', '', '<SECRET_ACCESS_KEY>', '{"model":"amazon.titan-embed-text-v1", "region_name":"us-east-1", "access_key_id":"ACCESS_KEY_ID"}')
El siguiente ejemplo lo ilustrará aún más.
client.command("""
CREATE TABLE IF NOT EXISTS EmbeddingsCollection (
id UUID,
sentences String, -- Campo de texto para almacenar sus datos de texto
embeddings Array(Float32),
--CONSTRAINT check_data_length CHECK length(embeddings) = 1536
) ENGINE = MergeTree()
ORDER BY id;
""")
EmbeddingsCollection
pronto será útil, ya que guardaremos las incrustaciones generadas allí. Tomaremos algunas muestras de texto aleatorias y calcularemos sus incrustaciones.
proveedor_servicio = 'Bedrock'
modeloIncrustacion = 'amazon.titan-embed-text-v1'
listaIncrustaciones = []
for i in range(500, 1000, 30):
lineaAleatoria = lineasHamlet[i]
parámetros = {'sampleString': lineaAleatoria, 'serviceProvider': proveedor_servicio, 'model': modeloIncrustacion}
if len(lineaAleatoria) < 30: # Ignorar pasajes demasiado pequeños.
continue
else:
x = client.query("""
SELECT EmbedText({sampleString:String}, {serviceProvider:String}, '', '12PXA8xxxxxxxxxxx', '{"model":"amazon.titan-embed-text-v1", "region_name":"us-east-1", "access_key_id":"AKIxxxxxxxx"}')
""", parameters=parameters)
_id = client.query("""SELECT generateUUIDv4()
""")
listaIncrustaciones.append((_id.result_rows[0][0], lineaAleatoria, x.result_rows[0][0]))
Como puedes ver, al agregar, estamos tomando los UUID de la misma manera que estamos extrayendo los vectores de incrustación (los UUID son objetos individuales). Ahora esta listaIncrustaciones
se puede insertar en la tabla y utilizar posteriormente para diversas tareas como la búsqueda de similitud, etc.
# Uso de modelos de Jina
Jina es un servicio relativamente nuevo. Para utilizar un servicio de Jina, podemos especificar el texto de entrada, la clave de acceso y el modelo. El modelo predeterminado en MyScale es jina-embeddings-v2-base-en
y devuelve incrustaciones de longitud 768, que tienen una resolución más baja en comparación con los otros modelos.
SELECT EmbedText('<texto-de-entrada>', 'jina', '', '<clave-de-acceso>', '{"model":"jina-embeddings-v2-base-en"}')
Aquí tienes un ejemplo. Como puedes ver, es casi lo mismo que hicimos utilizando otros proveedores. EmbedText()
nos permite encapsular los proveedores y se siente como si estuviéramos utilizando más o menos el mismo servicio.
proveedor_servicio = 'jina'
textoEntrada = "The Astronomer's Telegram (ATel) is an internet-based short-notice publication service for quickly disseminating information on new astronomical observations.[1][2] Examples include gamma-ray bursts,[3][4] gravitational microlensing, supernovae, novae, or X-ray transients, but there are no restrictions on content matter. Telegrams are available instantly on the service's website, and distributed to subscribers via email digest within 24 hours' # truncado aquí a propósito"
parámetros = {'sampleString': textoEntrada, 'serviceProvider': proveedor_servicio}
x = client.query("""
SELECT EmbedText({sampleString:String}, {serviceProvider:String}, '', 'jina_faxxxxxxx', '{"model":"jina-embeddings-v2-base-en"}')
""", parameters=parameters)
x.result_rows[0][0]
# Procesamiento de incrustaciones con MyScale
Hemos aprendido cómo obtener estas incrustaciones con EmbedText()
, ahora veamos cómo utilizarlas. Dado que generar estas incrustaciones tiene un costo, debemos guardarlas en la base de datos justo después de su generación. Es bastante sencillo con MyScale. Crea una tabla e inserta las incrustaciones allí.
client.command("""
CREATE TABLE IF NOT EXISTS EmbeddingsCollection (
id UUID,
sentences String, -- Campo de texto para almacenar sus datos de texto
embeddings Array(Float32),
--CONSTRAINT check_data_length CHECK length(embeddings) = 1536
) ENGINE = MergeTree()
ORDER BY id;
""")
Como ya tenemos el dataframe en el formato de ID con texto de entrada y las respectivas incrustaciones. Simplemente podemos llamar a itertuples()
e insertarlo utilizando el método correspondiente.
registros_df = list(df.itertuples(index=False, name=None))
client.insert("EmbeddingsCollection", registros_df, column_names=["id", "sentences", "embeddings"])
Ahora que tenemos los datos en la tabla, podemos agregar el índice vectorial. Este índice pronto será útil en la búsqueda de similitud.
client.command("""
ALTER TABLE EmbeddingsCollection
ADD VECTOR INDEX cosine_idx embeddings
TYPE MSTG
('metric_type=Cosine')
""")
# Eficiencia
Habiendo hecho el trabajo principal, es bastante fácil ver por qué las incrustaciones son tan útiles y qué tan eficiente es MyScale. Por ejemplo, después de almacenar las incrustaciones para todo un libro, podemos realizar algunas búsquedas.
proveedor_servicio = 'jina'
consulta = "
parámetros = {'sampleString': consulta, 'serviceProvider': proveedor_servicio}
x = client.query("""
SELECT EmbedText({sampleString:String}, {serviceProvider:String}, '', 'jina_faxxxxxxx', '{"model":"jina-embeddings-v2-base-en"}')
""", parameters=parameters)
incrustacionesConsulta = x.result_rows[0][0]
resultados = client.query(f"""
SELECT id, sentences, embeddings,
distance(embeddings, {incrustacionesConsulta}) as dist
FROM BookEmbeddings
ORDER BY dist LIMIT 3
""")
df = pd.DataFrame(resultados.result_rows)
Como resultado, obtenemos los 3 mejores resultados:
Con estos beneficios significativos: integración sin problemas de modelos de incrustación, rendimiento impresionante y ahorro de costos, MyScale demuestra ser una plataforma potente para cualquier aplicación basada en datos.
# Conclusión
Al utilizar la función EmbedText()
de MyScale, hemos demostrado cómo integrar sin problemas modelos de incrustación populares como OpenAI, Bedrock y Jina en sus aplicaciones. Generar y procesar incrustaciones directamente dentro de MyScale no solo simplifica su flujo de trabajo, sino que también ofrece un rendimiento impresionante, incluso al manejar datos a gran escala.
Por ejemplo, realizar una búsqueda de similitud en un libro de más de 800 páginas en solo un par de segundos muestra la eficiencia y velocidad de MyScale. Este procesamiento rápido sería un desafío con modelos tradicionales de procesamiento del lenguaje natural. Además, las capacidades de MyScale no se limitan al texto; puede manejar incrustaciones para imágenes y otros tipos de datos, abriendo una multitud de posibilidades de aplicación.
Lo que distingue a MyScale no es solo su rendimiento, sino también su rentabilidad. A diferencia de otras bases de datos vectoriales, MyScale ofrece una flexibilidad y características excepcionales sin el alto costo. Incluso al escalar para administrar 80 millones de vectores, los costos siguen siendo notablemente bajos, con 8 pods que se ejecutan a menos de $0.10 por hora cada uno.
Esta asequibilidad hace de MyScale una opción ideal tanto para startups como para empresas establecidas sin arruinar el presupuesto. En esencia, MyScale le permite aprovechar todo el potencial de las incrustaciones, proporcionando una solución potente, escalable y económica para aplicaciones modernas basadas en datos.