paint-brush
¿Quieres buscar algo con una imagen y una descripción de texto? Prueba un RAG multimodalpor@codingjaguar
Nueva Historia

¿Quieres buscar algo con una imagen y una descripción de texto? Prueba un RAG multimodal

por Jiang Chen16m2024/11/27
Read on Terminal Reader

Demasiado Largo; Para Leer

Una guía detallada sobre cómo construir un sistema RAG multimodal usando Milvus y cómo abrir varias posibilidades para los sistemas de IA.
featured image - ¿Quieres buscar algo con una imagen y una descripción de texto? Prueba un RAG multimodal
Jiang Chen HackerNoon profile picture
0-item
1-item

Este artículo proporciona una guía detallada sobre cómo construir un sistema RAG multimodal utilizando Milvus y cómo abrir varias posibilidades para los sistemas de IA.


Ya no basta con limitarse a un único formato de datos. A medida que las empresas dependen cada vez más de la información para tomar decisiones cruciales, necesitan la capacidad de comparar datos en distintos formatos. Afortunadamente, los sistemas de IA tradicionales limitados a un único tipo de datos han dado paso a sistemas multimodales que pueden comprender y procesar información compleja.


Los sistemas de búsqueda multimodal y recuperación multimodal-generación aumentada (RAG) han demostrado recientemente grandes avances en este campo. Estos sistemas procesan múltiples tipos de datos, incluidos texto, imágenes y audio, para proporcionar respuestas sensibles al contexto.


En esta publicación del blog, analizaremos cómo los desarrolladores pueden crear su propio sistema RAG multimodal con Milvus. También le explicaremos cómo crear un sistema que pueda manejar datos de texto e imágenes, en particular, realizar búsquedas de similitud y aprovechar un modelo de lenguaje para refinar el resultado. Comencemos.

¿Qué es Milvus?

Una base de datos vectorial es un tipo especial de base de datos que se utiliza para almacenar, indexar y recuperar incrustaciones vectoriales, que son representaciones matemáticas de datos que permiten comparar datos no solo para determinar su equivalencia sino también su similitud semántica. Milvus es una base de datos vectorial de código abierto y alto rendimiento diseñada para escalar. Puedes encontrarla en GitHub con una licencia Apache-2.0 y más de 30 000 estrellas.


Milvus ayuda a los desarrolladores a ofrecer una solución flexible para gestionar y consultar datos vectoriales a gran escala. Su eficiencia hace que Milvus sea una opción ideal para los desarrolladores que crean aplicaciones utilizando modelos de aprendizaje profundo, como recuperación de generación aumentada (RAG), búsqueda multimodal, motor de recomendaciones y detección de anomalías.


Milvus ofrece múltiples opciones de implementación para adaptarse a las necesidades de los desarrolladores. Milvus Lite es una versión liviana que se ejecuta dentro de una aplicación Python y es perfecta para crear prototipos de aplicaciones dentro de un entorno local. Milvus Standalone y Milvus Distributed son opciones escalables y listas para producción.

RAG multimodal: más allá del texto

Antes de construir el sistema, es importante comprender el RAG tradicional basado en texto y su evolución al RAG multimodal.


La generación aumentada de recuperación (RAG, por sus siglas en inglés) es un método para recuperar información contextual de fuentes externas y generar resultados más precisos a partir de modelos lingüísticos extensos (LLM, por sus siglas en inglés). La RAG tradicional es una estrategia muy eficaz para mejorar los resultados de los LLM, pero sigue limitada a los datos textuales. En muchas aplicaciones del mundo real, los datos se extienden más allá del texto: la incorporación de imágenes, gráficos y otras modalidades proporciona un contexto crítico.


El RAG multimodal aborda la limitación anterior al permitir el uso de diferentes tipos de datos, lo que proporciona un mejor contexto a los LLM.


En pocas palabras, en un sistema RAG multimodal, el componente de recuperación busca información relevante en diferentes modalidades de datos, y el componente de generación genera resultados más precisos basados en la información recuperada.

Comprensión de las incrustaciones vectoriales y la búsqueda de similitud

Las incrustaciones vectoriales y la búsqueda de similitud son dos conceptos fundamentales de RAG multimodal. Vamos a entenderlos.

Incrustaciones de vectores

Como se ha comentado, las incrustaciones vectoriales son representaciones matemáticas o numéricas de datos. Las máquinas utilizan esta representación para comprender el significado semántico de distintos tipos de datos, como texto, imágenes y audio.


Al utilizar el procesamiento del lenguaje natural (PLN), los fragmentos de documentos se transforman en vectores y las palabras semánticamente similares se asignan a puntos cercanos en el espacio vectorial. Lo mismo ocurre con las imágenes, donde las incrustaciones representan las características semánticas. Esto nos permite comprender métricas como el color, la textura y las formas de los objetos en un formato numérico.


El objetivo principal de utilizar incrustaciones vectoriales es ayudar a preservar las relaciones y similitudes entre diferentes piezas de datos.

Búsqueda de similitud

La búsqueda por similitud se utiliza para buscar y localizar datos en un conjunto de datos determinado. En el contexto de las incrustaciones de vectores, la búsqueda por similitud encuentra vectores en el conjunto de datos determinado que sean más cercanos al vector de consulta.


Los siguientes son algunos métodos que se utilizan comúnmente para medir la similitud entre vectores:

  1. Distancia euclidiana : mide la distancia en línea recta entre dos puntos en el espacio vectorial.
  2. Similitud de coseno : mide el coseno del ángulo entre dos vectores (centrándose en su dirección en lugar de en su magnitud).
  3. Producto escalar : una simple multiplicación de elementos correspondientes sumados.


La elección de la medida de similitud generalmente depende de los datos específicos de la aplicación y de cómo el desarrollador aborda el problema.


Al realizar búsquedas de similitud en conjuntos de datos a gran escala, la potencia de cálculo y los recursos necesarios son muy altos. Aquí es donde entran en juego los algoritmos de vecino más cercano aproximado (ANN). Los algoritmos ANN se utilizan para intercambiar un pequeño porcentaje o cantidad de precisión por una mejora significativa de la velocidad. Esto los convierte en una opción adecuada para aplicaciones a gran escala.


Milvus también utiliza algoritmos avanzados de ANN, incluidos HNSW y DiskANN, para realizar búsquedas de similitud eficientes en grandes conjuntos de datos de incrustación de vectores, lo que permite a los desarrolladores encontrar rápidamente puntos de datos relevantes. Además, Milvus admite otros algoritmos de indexación, como HSNW, IVF, CAGRA, etc., lo que lo convierte en una solución de búsqueda de vectores mucho más eficiente.


Construcción de RAG multimodal con Milvus

Ahora que hemos aprendido los conceptos, es momento de construir un sistema RAG multimodal utilizando Milvus. Para este ejemplo, utilizaremos Milvus Lite (la versión liviana de Milvus, ideal para experimentar y crear prototipos) para el almacenamiento y la recuperación de vectores, BGE para el procesamiento y la incrustación de imágenes precisas, y GPT-4o para la reclasificación avanzada de resultados.

Prerrequisitos

En primer lugar, necesitará una instancia de Milvus para almacenar sus datos. Puede configurar Milvus Lite con pip, ejecutar una instancia local con Docker o registrarse para obtener una cuenta Milvus alojada gratuita a través de Zilliz Cloud.


En segundo lugar, necesitas un LLM para tu canal de RAG, así que dirígete a IA abierta y obtener una clave API. El nivel gratuito es suficiente para que este código funcione.


A continuación, cree un nuevo directorio y un Python entorno virtual (o toma las medidas que uses para administrar Python).


Para este tutorial, también necesitarás instalar el pimilvus biblioteca, que es el SDK oficial de Python de Milvus, y un puñado de herramientas comunes.

Configurar Milvus Lite

 pip install -U pymilvus

Instalar dependencias

 pip install --upgrade pymilvus openai datasets opencv-python timm einops ftfy peft tqdm git clone https://github.com/FlagOpen/FlagEmbedding.git pip install -e FlagEmbedding

Descargar datos

El siguiente comando descargará los datos de ejemplo y los extraerá a una carpeta local “./images_folder”, que incluye:


  • Imágenes: Un subconjunto de Reseñas de Amazon 2023 contiene aproximadamente 900 imágenes de las categorías "Electrodomésticos", "Teléfonos móviles y accesorios" y "Electrónica".
  • Un ejemplo de consulta de imagen: leopard.jpg


 wget https://github.com/milvus-io/bootcamp/releases/download/data/amazon_reviews_2023_subset.tar.gz tar -xzf amazon_reviews_2023_subset.tar.gz

Cargar el modelo de incrustación

Utilizaremos el modelo BGE visualizado “bge-visualized-base-en-v1.5” para generar incrustaciones tanto de imágenes como de texto.


Ahora descarga el peso de HuggingFace.


 wget https://huggingface.co/BAAI/bge-visualized/resolve/main/Visualized_base_en_v1.5.pth


Entonces, construyamos un codificador.

 import torch from visual_bge.modeling import Visualized_BGE class Encoder:    def __init__(self, model_name: str, model_path: str):        self.model = Visualized_BGE(model_name_bge=model_name, model_weight=model_path)        self.model.eval()    def encode_query(self, image_path: str, text: str) -> list[float]:        with torch.no_grad():            query_emb = self.model.encode(image=image_path, text=text)        return query_emb.tolist()[0]    def encode_image(self, image_path: str) -> list[float]:        with torch.no_grad():            query_emb = self.model.encode(image=image_path)        return query_emb.tolist()[0] model_name = "BAAI/bge-base-en-v1.5" model_path = "./Visualized_base_en_v1.5.pth" # Change to your own value if using a different model path encoder = Encoder(model_name, model_path)

Generar incrustaciones y cargar datos en Milvus

Esta sección le guiará sobre cómo cargar imágenes de ejemplo en nuestra base de datos con sus incrustaciones correspondientes.


Generar incrustaciones


Primero, necesitamos crear incrustaciones para todas las imágenes en el conjunto de datos.


Cargar todas las imágenes del directorio de datos y convertirlas en incrustaciones.


 import os from tqdm import tqdm from glob import glob data_dir = (    "./images_folder" # Change to your own value if using a different data directory ) image_list = glob(    os.path.join(data_dir, "images", "*.jpg") ) # We will only use images ending with ".jpg" image_dict = {} for image_path in tqdm(image_list, desc="Generating image embeddings: "):    try:        image_dict[image_path] = encoder.encode_image(image_path)    except Exception as e:        print(f"Failed to generate embedding for {image_path}. Skipped.")        continue print("Number of encoded images:", len(image_dict))

Realizar una búsqueda multimodal y reordenar los resultados

En esta sección, primero buscaremos imágenes relevantes utilizando una consulta multimodal y luego usaremos un servicio LLM para reclasificar los resultados recuperados y encontrar el mejor con una explicación.


Ejecutar búsqueda multimodal


Ahora estamos listos para realizar la búsqueda multimodal avanzada con la consulta compuesta por instrucciones de imagen y texto.


 query_image = os.path.join(    data_dir, "leopard.jpg" ) # Change to your own query image path query_text = "phone case with this image theme" query_vec = encoder.encode_query(image_path=query_image, text=query_text) search_results = milvus_client.search(    collection_name=collection_name,    data=[query_vec],    output_fields=["image_path"],    limit=9, # Max number of search results to return    search_params={"metric_type": "COSINE", "params": {}}, # Search parameters )[0] retrieved_images = [hit.get("entity").get("image_path") for hit in search_results] print(retrieved_images)


El resultado se muestra a continuación:


 ['./images_folder/images/518Gj1WQ-RL._AC_.jpg', './images_folder/images/41n00AOfWhL._AC_.jpg'


Reordenar los resultados con GPT-4o


Ahora, utilizaremos GPT-4o para clasificar las imágenes recuperadas y encontrar los resultados que mejor se ajusten a las necesidades. El LLM también explicará por qué se clasifica de esa manera.


1. Crea una vista panorámica.


 import numpy as np import cv2 img_height = 300 img_width = 300 row_count = 3 def create_panoramic_view(query_image_path: str, retrieved_images: list) -> np.ndarray:    """    creates a 5x5 panoramic view image from a list of images    args:        images: list of images to be combined    returns:        np.ndarray: the panoramic view image    """    panoramic_width = img_width * row_count    panoramic_height = img_height * row_count    panoramic_image = np.full(        (panoramic_height, panoramic_width, 3), 255, dtype=np.uint8    )    # create and resize the query image with a blue border    query_image_null = np.full((panoramic_height, img_width, 3), 255, dtype=np.uint8)    query_image = Image.open(query_image_path).convert("RGB")    query_array = np.array(query_image)[:, :, ::-1]    resized_image = cv2.resize(query_array, (img_width, img_height))    border_size = 10    blue = (255, 0, 0) # blue color in BGR    bordered_query_image = cv2.copyMakeBorder(        resized_image,        border_size,        border_size,        border_size,        border_size,        cv2.BORDER_CONSTANT,        value=blue,    )    query_image_null[img_height * 2 : img_height * 3, 0:img_width] = cv2.resize(        bordered_query_image, (img_width, img_height)    )    # add text "query" below the query image    text = "query"    font_scale = 1    font_thickness = 2    text_org = (10, img_height * 3 + 30)    cv2.putText(        query_image_null,        text,        text_org,        cv2.FONT_HERSHEY_SIMPLEX,        font_scale,        blue,        font_thickness,        cv2.LINE_AA,    )    # combine the rest of the images into the panoramic view    retrieved_imgs = [        np.array(Image.open(img).convert("RGB"))[:, :, ::-1] for img in retrieved_images    ]    for i, image in enumerate(retrieved_imgs):        image = cv2.resize(image, (img_width - 4, img_height - 4))        row = i // row_count        col = i % row_count        start_row = row * img_height        start_col = col * img_width        border_size = 2        bordered_image = cv2.copyMakeBorder(            image,            border_size,            border_size,            border_size,            border_size,            cv2.BORDER_CONSTANT,            value=(0, 0, 0),        )        panoramic_image[            start_row : start_row + img_height, start_col : start_col + img_width        ] = bordered_image        # add red index numbers to each image        text = str(i)        org = (start_col + 50, start_row + 30)        (font_width, font_height), baseline = cv2.getTextSize(            text, cv2.FONT_HERSHEY_SIMPLEX, 1, 2        )        top_left = (org[0] - 48, start_row + 2)        bottom_right = (org[0] - 48 + font_width + 5, org[1] + baseline + 5)        cv2.rectangle(            panoramic_image, top_left, bottom_right, (255, 255, 255), cv2.FILLED        )        cv2.putText(            panoramic_image,            text,            (start_col + 10, start_row + 30),            cv2.FONT_HERSHEY_SIMPLEX,            1,            (0, 0, 255),            2,            cv2.LINE_AA,        )    # combine the query image with the panoramic view    panoramic_image = np.hstack([query_image_null, panoramic_image])    return panoramic_image


2. Combine la imagen de consulta y las imágenes recuperadas con índices en una vista panorámica.


 from PIL import Image combined_image_path = os.path.join(data_dir, "combined_image.jpg") panoramic_image = create_panoramic_view(query_image, retrieved_images) cv2.imwrite(combined_image_path, panoramic_image) combined_image = Image.open(combined_image_path) show_combined_image = combined_image.resize((300, 300)) show_combined_image.show() 


Resultados de búsqueda multimodal

3. Reordene los resultados y dé una explicación.


Enviaremos todas las imágenes combinadas al servicio LLM multimodal junto con las indicaciones adecuadas para clasificar los resultados obtenidos con una explicación. Nota: Para habilitar GPT-4o como LLM, debe preparar su Clave API de OpenAI por adelantado.


 import requests import base64 openai_api_key = "sk-***" # Change to your OpenAI API Key def generate_ranking_explanation(    combined_image_path: str, caption: str, infos: dict = None ) -> tuple[list[int], str]:    with open(combined_image_path, "rb") as image_file:        base64_image = base64.b64encode(image_file.read()).decode("utf-8")    information = (        "You are responsible for ranking results for a Composed Image Retrieval. "        "The user retrieves an image with an 'instruction' indicating their retrieval intent. "        "For example, if the user queries a red car with the instruction 'change this car to blue,' a similar type of car in blue would be ranked higher in the results. "        "Now you would receive instruction and query image with blue border. Every item has its red index number in its top left. Do not misunderstand it. "        f"User instruction: {caption} \n\n"    )    # add additional information for each image    if infos:        for i, info in enumerate(infos["product"]):            information += f"{i}. {info}\n"    information += (        "Provide a new ranked list of indices from most suitable to least suitable, followed by an explanation for the top 1 most suitable item only. "        "The format of the response has to be 'Ranked list: []' with the indices in brackets as integers, followed by 'Reasons:' plus the explanation why this most fit user's query intent."    )    headers = {        "Content-Type": "application/json",        "Authorization": f"Bearer {openai_api_key}",    }    payload = {        "model": "gpt-4o",        "messages": [            {                "role": "user",                "content": [                    {"type": "text", "text": information},                    {                        "type": "image_url",                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},                    },                ],            }        ],        "max_tokens": 300,    }    response = requests.post(        "https://api.openai.com/v1/chat/completions", headers=headers, json=payload    )    result = response.json()["choices"][0]["message"]["content"]    # parse the ranked indices from the response    start_idx = result.find("[")    end_idx = result.find("]")    ranked_indices_str = result[start_idx + 1 : end_idx].split(",")    ranked_indices = [int(index.strip()) for index in ranked_indices_str]    # extract explanation    explanation = result[end_idx + 1 :].strip()    return ranked_indices, explanation


Obtenga los índices de imagen después de la clasificación y el motivo del mejor resultado:


 ranked_indices, explanation = generate_ranking_explanation(    combined_image_path, query_text )


4. Muestra el mejor resultado con una explicación


 print(explanation) best_index = ranked_indices[0] best_img = Image.open(retrieved_images[best_index]) best_img = best_img.resize((150, 150)) best_img.show()


Resultados:


 Reasons: The most suitable item for the user's query intent is index 6 because the instruction specifies a phone case with the theme of the image, which is a leopard. The phone case with index 6 has a thematic design resembling the leopard pattern, making it the closest match to the user's request for a phone case with the image theme. 



Funda para móvil con estampado de leopardo - Mejor resultado


Consulta el código completo en este cuaderno . Para obtener más información sobre cómo iniciar una demostración en línea con este tutorial, consulta el Ejemplo de aplicación .

Conclusión

En esta publicación de blog, analizamos la construcción de un sistema RAG multimodal utilizando Milvus (una base de datos de vectores de código abierto). Explicamos cómo los desarrolladores pueden configurar Milvus, cargar datos de imágenes, realizar búsquedas de similitud y usar un LLM para reordenar los resultados obtenidos y obtener respuestas más precisas.


Las soluciones RAG multimodales abren varias posibilidades para los sistemas de IA que pueden comprender y procesar fácilmente múltiples formas de datos. Algunas posibilidades comunes incluyen motores de búsqueda de imágenes mejorados, mejores resultados basados en el contexto y más.