¿Cómo construir una aplicación de búsqueda multi-modal con Chroma?
¿Cómo crear una aplicación multi-modal de búsqueda utilizando Chroma?
Introducción
¿Alguna vez te has preguntado cómo nuestros intricados cerebros procesan el mundo? Si bien el funcionamiento interno del cerebro sigue siendo un misterio, podemos compararlo con una red neural versátil. Gracias a las señales electroquímicas, maneja diferentes tipos de datos: audio, visual, olores, sabores y tacto. A medida que la inteligencia artificial avanza, surgen modelos multi-modales, revolucionando las capacidades de búsqueda. Esta innovación abre posibilidades, mejorando la precisión y relevancia de la búsqueda. Descubre el fascinante mundo de la búsqueda multi-modal.
Objetivos de aprendizaje
- Comprender el término “Multi-modalidad en IA”.
- Obtener conocimientos sobre el modelo de imagen-texto CLIP de OpenAI.
- Aprender qué es una base de datos vectorial y entender el índice vectorial de manera breve.
- Utilizar CLIP y la base de datos vectorial Chroma para construir un recomendador de alimentos con una interfaz Gradio.
- Explorar otros casos de uso del mundo real de una búsqueda multi-modal.
Este artículo fue publicado como parte del Data Science Blogathon.
¿Qué es la Multi-modalidad en IA?
Si lo buscas en Google, encontrarás que multi-modal se refiere a involucrar múltiples modos o métodos en un proceso. En Inteligencia Artificial, los modelos multi-modales son aquellas redes neuronales que pueden procesar y entender diferentes tipos de datos. Por ejemplo, GPT-4 y Bard. Estos son modelos de lenguaje de lenguaje y visión que pueden entender texto e imágenes. Otros ejemplos podrían ser los autos Tesla con conducción automática que combinan datos visuales y sensoriales para entender el entorno, y Midjourney o Dalle, que pueden generar imágenes a partir de descripciones de texto.
Pre-entrenamiento Contrastivo de Lenguaje-Imagen (CLIP)
CLIP es una red neural multi-modal de código abierto desarrollada por OpenAI y entrenada con un gran conjunto de datos de pares imagen-texto. Esto asegura que CLIP aprenda a asociar conceptos visuales en imágenes con sus descripciones de texto. El modelo CLIP puede ser instruido en lenguaje humano para clasificar una amplia gama de datos de imágenes sin necesidad de entrenamiento específico.
- Explora las capacidades mejoradas de validación de datos de Pydantic V2
- Una guía universal para la ingeniería rápida el Marco de Andamios Contextuales (CSF)
- Las fronteras éticas de la IA generativa Introducción e importancia
La capacidad de “zero-shot” de CLIP es comparable a la de GPT 3. Por lo tanto, CLIP se puede utilizar para clasificar imágenes en cualquier conjunto de categorías sin necesidad de ser entrenado en esas categorías específicamente. Por ejemplo, para clasificar imágenes de perros vs. gatos, solo necesitamos comparar las puntuaciones logit de la imagen con la descripción de texto “una imagen de un perro” o “una imagen de un gato”; Una foto de un gato o un perro probablemente tendrá puntuaciones logit más altas con sus respectivas descripciones de texto.
Esto se conoce como clasificación “zero-shot” porque CLIP no necesita ser entrenado con un conjunto de datos de imágenes de perros y gatos para poder clasificarlos. Aquí tienes una presentación visual de cómo funciona CLIP.
CLIP utiliza un Vision Transformer (ViT) para imágenes y un modelo de texto para características de texto. Las codificaciones vectoriales luego se proyectan en un espacio vectorial compartido con dimensiones idénticas. El producto punto entre los dos se utiliza como un puntaje de similitud para predecir la similitud entre el fragmento de texto y la imagen. En otras palabras, CLIP puede clasificar imágenes en cualquier conjunto de categorías sin estar optimizado para ello. En este artículo, implementaremos CLIP de manera programática.
¿Por qué se requieren las bases de datos vectoriales?
Los algoritmos de aprendizaje automático no entienden los datos en su formato raw. Por lo tanto, para hacer que funcionen, debemos transformar los datos en su forma numérica. Los vectores o embeddings son las representaciones numéricas de diferentes tipos de datos como texto, imágenes, audio y videos. Sin embargo, las bases de datos tradicionales no son completamente capaces de consultar datos vectoriales de alta dimensionalidad. Para construir una aplicación que utilice millones de embeddings vectoriales, necesitamos una base de datos que pueda almacenar, buscar y consultarlos. Esto no es posible con bases de datos tradicionales. Para lograr esto, necesitamos bases de datos vectoriales, diseñadas específicamente para almacenar y consultar embeddings.
La siguiente imagen ilustra un flujo de trabajo simplificado de una base de datos vectorial.
Necesitamos modelos de incrustación especializados capaces de capturar el significado semántico subyacente de los datos. Los modelos son diferentes para diferentes tipos de datos. Utilice modelos de imagen como Resnet o Visual Transformers para datos de imágenes. Para textos, se utilizan modelos de texto como Ada y SentenceTransformers. Para la interacción multimodal, se utilizan modelos multimodales como Tortoise (Texto-A-Voz) y CLIP (Texto-A-Imagen). Estos modelos se utilizarán para obtener las incrustaciones de los datos de entrada. Las bases de datos de vectores suelen tener implementaciones personalizadas de modelos de incrustación, pero también podemos definir nuestros propios modelos para obtener las incrustaciones y almacenarlas en almacenes de vectores.
Indexación
Las incrustaciones suelen ser de alta dimensionalidad y las consultas a vectores de alta dimensionalidad suelen requerir mucho tiempo y capacidad de cálculo. Por lo tanto, las bases de datos de vectores emplean diversos métodos de indexación para realizar consultas eficientes. La indexación se refiere a organizar vectores de alta dimensionalidad de manera que se puedan realizar consultas eficientes de vectores vecinos más cercanos.
Algunos algoritmos de indexación populares son HNSW (Hierarchical Navigable Small World), Quantización de productos, Sistema de archivos invertido, Cuantización escalar, etc. De todos estos, HNSW es el algoritmo más popular y ampliamente utilizado en diversas bases de datos de vectores.
Para esta aplicación, utilizaremos la Base de Datos de Vectores Chroma. Chroma es una base de datos de vectores de código abierto. Le permite configurar rápidamente un cliente para almacenar y consultar vectores y metadatos asociados. Hay otros almacenes de vectores similares que puede utilizar, como Weaviate, Qdrant, Milvus, etc.
¿Qué es Gradio?
Gradio, escrito en Python, tiene como objetivo construir rápidamente una interfaz web para compartir modelos de Aprendizaje Automático como una herramienta de código abierto. Nos permite configurar una interfaz web de demostración utilizando Python. Proporciona flexibilidad para crear un prototipo decente para mostrar los modelos backend.
Para obtener más información sobre cómo construir, consulta este artículo.
Construyendo la Aplicación
Esta sección explicará los códigos para crear una aplicación simple de recomendación de platos de restaurante utilizando Gradio, Chroma y CLIP. Chroma aún no tiene soporte incorporado para modelos multimodales. Por lo tanto, esto será una solución alternativa.
Hay dos formas de utilizar CLIP en tu proyecto. Ya sea la implementación de CLIP de OpenAI o la implementación de CLIP de Huggingface. Para este proyecto, utilizaremos CLIP de OpenAI. Asegúrate de tener un entorno virtual con las siguientes dependencias instaladas.
cliptorchchromadbgradio
Esta es nuestra estructura de directorio.
├── app.py├── clip_chroma├── clip_embeddings.py├── __init__.py├── load_data.py
Incrustaciones de CLIP
Lo primero que debemos hacer es construir una clase para extraer las incrustaciones de imágenes y textos. Como sabemos, CLIP tiene dos partes para procesar textos e imágenes. Utilizaremos los modelos respectivos para codificar diferentes modalidades.
import clip import torchfrom numpy import ndarray from typing import List from PIL import Image class ClipEmbeddingsfunction: def __init__(self, model_name: str = "ViT-B/32", device: str = "cpu"): self.device = device # Almacena el dispositivo especificado para la ejecución del modelo self.model, self.preprocess = clip.load(model_name, self.device) def __call__(self, docs: List[str]) -> List[ndarray]: # Define un método que toma una lista de rutas de archivos de imagen (docs) como entrada lista_de_incrustaciones = [] # Crea una lista vacía para almacenar las incrustaciones de imagen for image_path in docs: image = Image.open(image_path) # Abre y carga una imagen desde la ruta proporcionada image = image.resize((224, 224)) # Procesa previamente la imagen y muévela al dispositivo especificado image_input = self.preprocess(image).unsqueeze(0).to(self.device) with torch.no_grad(): # Calcula las incrustaciones de imagen utilizando el modelo CLIP y conviértelas a matrices NumPy embeddings = self.model.encode_image(image_input).cpu().detach().numpy() lista_de_incrustaciones.append(list(embeddings[0])) return lista_de_incrustaciones def get_text_embeddings(self, text: str) -> List[ndarray]: # Define un método que toma una cadena de texto como entrada text_token = clip.tokenize(text) # Tokeniza el texto de entrada with torch.no_grad(): # Calcula las incrustaciones de texto utilizando el modelo CLIP y conviértelas a matrices NumPy text_embeddings = self.model.encode_text(text_token).cpu().detach().numpy() return list(text_embeddings[0])
En el código anterior, hemos definido una clase para extraer incrustaciones de textos e imágenes. La clase toma el nombre del modelo y el dispositivo como entradas. Si tu dispositivo es compatible con Cuda, puedes habilitarlo pasándolo con el dispositivo. CLIP admite varios modelos, como
clip.available_models()['RN50', 'RN101', 'RN50x4', 'RN50x16', 'RN50x64', 'ViT-B/32', 'ViT-B/16', 'ViT-L/14', 'ViT-L/14@336px']
El nombre del modelo por defecto está configurado como “ViT-B/32”. Puedes pasar cualquier otro modelo que desees.
El método __call__ toma una lista de rutas de imágenes y devuelve una lista de matrices numpy. El método get_text_embeddings toma una entrada de tipo string y devuelve una lista de incrustaciones.
Cargar Incrustaciones
Primero, necesitamos poblar nuestra base de datos de vectores. Así que, recopilé algunas imágenes de platos para agregar a nuestra colección. Crea una lista de rutas de imágenes y una lista de descripciones sobre ellas. Las rutas de las imágenes serán nuestros documentos, mientras que almacenaremos las descripciones de las imágenes como metadatos.
Pero primero, crea una colección Chroma.
import osfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunctionfrom typing import Listef = ClipEmbeddingsfunction()client = Client(settings = Settings(is_persistent=True, persist_directory="./clip_chroma"))coll = client.get_or_create_collection(name = "clip", embedding_function = ef)
Importamos la función de incrustación que definimos anteriormente y la pasamos como función de incrustación predeterminada para la colección.
Ahora, carga los datos en la base de datos.
coll.add(ids=[str(i) for i in range(len(img_list))], documents = img_list, #rutas de imágenes metadatas = menu_description,# descripción de los platos )
Eso es todo. Ahora estás listo para construir la parte final.
Aplicación Gradio
Primero, crea un archivo app.py, importa las siguientes dependencias e inicializa la función de incrustación.
import gradio as grfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunctionclient = Client(Settings(is_persistent=True, persist_directory="./clip_chroma"))ef = ClipEmbeddingsfunction()
Como interfaz gráfica, utilizaremos esto para crear una interfaz simple que reciba una consulta de búsqueda, ya sea un texto o una imagen, y muestre imágenes relevantes como salida.
with gr.Blocks() as demo: with gr.Row(): with gr.Column(): query = gr.Textbox(placeholder = "Ingresa una consulta") gr.HTML("O") photo = gr.Image() button = gr.UploadButton(label = "Cargar archivo", file_types=["imagen"]) with gr.Column(): gallery = gr.Gallery().style( object_fit='contain', height='auto', preview=True )
Ahora, definiremos eventos desencadenantes para la aplicación Gradio.
query.submit( fn = retrieve_image_from_query, inputs=[query], outputs= ) button.upload( fn = show_img, inputs=[button], outputs = [photo]).\ then( fn = retrieve_image_from_image, inputs=[button], outputs= )
En el código anterior, tenemos eventos desencadenantes. Procesamos una consulta de texto con la función retrieve_image_from_query. Primero mostramos imágenes en el objeto photo y luego invocamos retrieve_image_from_image(), mostrando la salida en el objeto Gallery.
Ejecuta el archivo app.py con el comando gradio y visita la dirección local que se muestra en la terminal.
Ahora, definiremos las funciones reales.
def retrieve_image_from_image(image): # Obtén una colección llamada "clip" utilizando la función de incrustación especificada (ef) coll = client.get_collection(name="clip", embedding_function=ef) # Extrae el nombre del archivo de imagen image = image.name # Consulta la colección utilizando el nombre del archivo de imagen como texto de consulta result = coll.query( query_texts=image, # Usa el nombre del archivo de imagen como texto de consulta include=["documents", "metadatas"], # Incluye tanto documentos como metadatos en los resultados n_results=4 # Especifica el número de resultados a recuperar ) # Obtén los documentos y sus metadatos recuperados docs = result['documents'][0] descs = result["metadatas"][0] # Crea una lista para almacenar pares de documentos y sus metadatos correspondientes lista_de_docs = [] # Itera a través de los documentos y metadatos recuperados for doc, desc in zip(docs, descs): # Agrega una tupla que contiene el documento y sus metadatos a la lista lista_de_docs.append((doc, list(desc.values())[0])) # Devuelve la lista de pares de documento-metadatos return lista_de_docs
También tenemos otra función para manejar consultas de texto.
def recuperar_imagen_desde_consulta(consulta: str): # Obtener una colección llamada "clip" utilizando la función de incrustación especificada (ef) coll = client.get_collection(name="clip", embedding_function=ef) # Obtener incrustaciones de texto para la consulta de entrada utilizando la función de incrustación (ef) emb = ef.get_text_embeddings(text=consulta) # Convertir las incrustaciones de texto a valores de punto flotante emb = [float(i) for i in emb] # Consultar la colección utilizando las incrustaciones de texto resultado = coll.query( query_embeddings=emb, # Utilizar las incrustaciones de texto como consulta include=["documents", "metadatas"], # Incluir tanto los documentos como los metadatos en los resultados n_results=4 # Especificar el número de resultados a recuperar ) # Obtener los documentos y sus metadatos recuperados docs = resultado['documents'][0] descs = resultado["metadatas"][0] # Crear una lista para almacenar pares de documentos y sus metadatos correspondientes lista_de_docs = [] # Iterar a través de los documentos y metadatos recuperados for doc, desc in zip(docs, descs): # Agregar una tupla que contenga el documento y sus metadatos a la lista lista_de_docs.append((doc, list(desc.values())[0])) # Devolver la lista de pares de documentos y metadatos return lista_de_docs
En lugar de pasar textos directamente en el código, extraímos las incrustaciones y luego las pasamos al método de consulta de Choma.
Entonces, aquí está el código completo para app.py.
# Importar las bibliotecas necesariasimport gradio as grfrom chromadb import Client, Settingsfrom clip_embeddings import ClipEmbeddingsfunction# Inicializar un cliente de chromadb con almacenamiento persistenteclient = Client(Settings(is_persistent=True, persist_directory="./clip_chroma"))# Inicializar la función ClipEmbeddingsfunctionef = ClipEmbeddingsfunction()# Función para recuperar imágenes a partir de una consulta de textodef recuperar_imagen_desde_consulta(consulta: str): # Obtener la colección "clip" con la función de incrustación especificada coll = client.get_collection(name="clip", embedding_function=ef) # Obtener las incrustaciones de texto para la consulta de entrada emb = ef.get_text_embeddings(text=consulta) emb = [float(i) for i in emb] # Consultar la colección para obtener documentos similares resultado = coll.query( query_embeddings=emb, include=["documents", "metadatas"], n_results=4 ) # Extraer los documentos y sus metadatos docs = resultado['documents'][0] descs = resultado["metadatas"][0] lista_de_docs = [] # Combinar documentos y descripciones en una lista for doc, desc in zip(docs, descs): lista_de_docs.append((doc, list(desc.values())[0])) return lista_de_docs# Función para recuperar imágenes a partir de una imagen cargadadef recuperar_imagen_desde_imagen(imagen): # Obtener la colección "clip" con la función de incrustación especificada coll = client.get_collection(name="clip", embedding_function=ef) # Obtener el nombre de archivo de la imagen cargada imagen = imagen.name # Consultar la colección con el nombre de archivo de la imagen resultado = coll.query( query_texts=imagen, include=["documents", "metadatas"], n_results=4 ) # Extraer los documentos y sus metadatos docs = resultado['documents'][0] descs = resultado["metadatas"][0] lista_de_docs = [] # Combinar documentos y descripciones en una lista for doc, desc in zip(docs, descs): lista_de_docs.append((doc, list(desc.values())[0])) return lista_de_docs# Función para mostrar una imagenef mostrar_imagen(imagen): return image.name# Crear una interfaz utilizando Blockswith gr.Blocks() as demo: with gr.Row(): with gr.Column(): # Entrada de texto para la consulta consulta = gr.Textbox(placeholder="Ingrese consulta") gr.HTML("O") # Entrada de imagen mediante carga de archivos foto = gr.Image() boton = gr.UploadButton(label="Cargar archivo", file_types=["image"]) with gr.Column(): # Mostrar una galería de imágenes galeria = gr.Gallery().style( object_fit='contain', height='auto', preview=True ) # Definir la entrada y salida para el envío de consultas consulta.submit( fn=recuperar_imagen_desde_consulta, inputs=[consulta], outputs= ) # Definir la entrada y salida para la carga de imágenes boton.upload( fn=mostrar_imagen, inputs=[boton], outputs=[foto]).\ then( fn=recuperar_imagen_desde_imagen, inputs=[boton], outputs= )# Ejecutar la interfaz Gradio si se ejecuta el script como programa principalif __name__ == "__main__": demo.launch()
Para empezar, ejecuta el archivo app.py en la terminal y visita la dirección local.
Repositorio de GitHub: https://github.com/sunilkumardash9/multi-modal-search-app
Casos de uso reales
La búsqueda multimodal puede tener muchas aplicaciones en diversas industrias.
- E-commerce: La búsqueda multimodal puede mejorar la experiencia de compra del cliente. Por ejemplo, puedes tomar una foto de un producto en una tienda física y buscarlo en línea para encontrar productos similares.
- Atención médica: Esto puede ayudar a diagnosticar enfermedades y encontrar tratamientos. Los médicos podrían usar una imagen para encontrar datos de investigación clínica en una base de datos médica.
- Educación: Las aplicaciones educativas habilitadas para la búsqueda multimodal pueden ayudar a los estudiantes y profesores a encontrar documentos relevantes de forma más rápida. Recuperar texto basado en imágenes y viceversa puede ahorrar mucho tiempo.
- Servicio al cliente: La búsqueda multimodal puede ayudar a encontrar respuestas relevantes a las consultas de los clientes en la base de conocimientos. Estas consultas pueden incluir imágenes o videos de productos.
Conclusión
La búsqueda multimodal será revolucionaria en el futuro. Poder interactuar en múltiples modalidades abre nuevas oportunidades de crecimiento. Así que este artículo trata sobre cómo utilizar la base de datos de vectores cromáticos y un modelo multimodal CLIP para construir una aplicación básica de búsqueda. Dado que la base de datos cromática no tiene soporte integrado para modelos multimodales, creamos una clase personalizada de incrustación CLIP para obtener incrustaciones de imágenes y ensamblamos diferentes partes para construir la aplicación de búsqueda de comida.
Puntos clave
- En IA, la multimodalidad implica la capacidad de interactuar con múltiples modos de comunicación como texto, imagen, audio y video.
- CLIP es un modelo de texto e imagen entrenado con miles de ejemplos de texto e imagen y capacidad de clasificación sin entrenamiento líder en su clase.
- Las bases de datos de vectores están diseñadas para almacenar, buscar y consultar vectores de alta dimensión.
- Los motores que permiten las tiendas de vectores son algoritmos ANN. HNSW es uno de los algoritmos ANN basados en gráficos más populares y eficientes.
Pregunta frecuente
Los medios mostrados en este artículo no son propiedad de Analytics Vidhya y se utilizan a discreción del autor.
We will continue to update Zepes; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles
- Revolucionando el ajuste fino del modelo de lenguaje logrando ganancias sin precedentes con las incrustaciones ruidosas de NEFTune
- ¿Cómo pueden las representaciones visuales pre-entrenadas ayudar a resolver la manipulación a largo plazo? Conoce Universal Visual Decomposer (UVD) Un método listo para usar para identificar submetas a partir de videos.
- Esta investigación de IA presenta ‘RAFA’ un marco de inteligencia artificial basado en principios para agentes LLM autónomos con eficiencia de muestra demostrable.
- Revolucionando el análisis de documentos conozca DSG, el primer sistema entrenable de principio a fin para la extracción de estructuras jerárquicas
- Despliega modelos de incrustación con los puntos finales de inferencia de Hugging Face
- Explora de forma interactiva tu conjunto de datos de Huggingface con una línea de código
- Un ‘mapa’ más preciso de las luces que ves cuando cierras los ojos puede mejorar los resultados de los ‘ojos biónicos’.