Importaciones y carga de datos
Comenzamos importando algunas bibliotecas y módulos útiles.
import json
from transformers import CLIPProcessor, CLIPTextModelWithProjection
from torch import load, matmul, argsort
from torch.nn.functional import softmax
A continuación, importaremos fragmentos de texto e imágenes del LLM multimodales y Incrustaciones multimodales publicaciones de blogs. Estos se guardan en archivos .json, que se pueden cargar en Python como una lista de diccionarios.
# load text chunks
with open('data/text_content.json', 'r', encoding='utf-8') as f:
text_content_list = json.load(f)# load images
with open('data/image_content.json', 'r', encoding='utf-8') as f:
image_content_list = json.load(f)
Si bien no revisaré el proceso de preparación de datos aquí, el código que utilicé está en la repositorio de GitHub.
También cargaremos las incrustaciones multimodales (de CLIP) para cada elemento en lista_contenido_texto y lista_contenido_imagen. Estos se guardan como tensores de pytorch.
# load embeddings
text_embeddings = load('data/text_embeddings.pt', weights_only=True)
image_embeddings = load('data/image_embeddings.pt', weights_only=True)print(text_embeddings.shape)
print(image_embeddings.shape)
# >> torch.Size([86, 512])
# >> torch.Size([17, 512])
Al imprimir la forma de estos tensores, vemos que están representados mediante incrustaciones de 512 dimensiones. Y tenemos 86 fragmentos de texto y 17 imágenes.
Búsqueda multimodal
Con nuestra base de conocimientos cargada, ahora podemos definir una consulta para búsqueda de vectores. Esto consistirá en traducir una consulta de entrada a una incrustación usando CLIP. Hacemos esto de manera similar a los ejemplos del publicación anterior.
# query
query = "What is CLIP's contrastive loss function?"# embed query (4 steps)
# 1) load model
model = CLIPTextModelWithProjection.from_pretrained("openai/clip-vit-base-patch16")
# 2) load data processor
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch16")
# 3) pre-process text
inputs = processor(text=[text], return_tensors="pt", padding=True)
# 4) compute embeddings with CLIP
outputs = model(**inputs)
# extract embedding
query_embed = outputs.text_embeds
print(query_embed.shape)
# >> torch.Size([1, 512])
Al imprimir la forma, vemos que tenemos un único vector que representa la consulta.
Para realizar una búsqueda vectorial en la base de conocimientos, debemos hacer lo siguiente.
- Calcule las similitudes entre la incrustación de la consulta y todas las incrustaciones de texto e imágenes.
- Cambie la escala de las similitudes para que vayan de 0 a 1 mediante la función softmax.
- Ordene las similitudes escaladas y devuelva los k resultados principales.
- Finalmente, filtre los resultados para mantener solo los elementos por encima de un umbral de similitud predefinido.
Así es como se ve en el código de los fragmentos de texto.
# define k and simiarlity threshold
k = 5
threshold = 0.05# multimodal search over articles
text_similarities = matmul(query_embed, text_embeddings.T)
# rescale similarities via softmax
temp=0.25
text_scores = softmax(text_similarities/temp, dim=1)
# return top k filtered text results
isorted_scores = argsort(text_scores, descending=True)[0]
sorted_scores = text_scores[0][isorted_scores]
itop_k_filtered = [idx.item()
for idx, score in zip(isorted_scores, sorted_scores)
if score.item() >= threshold][:k]
top_k = [text_content_list[i] for i in itop_k_filtered]
print(top_k)
# top k results[{'article_title': 'Multimodal Embeddings: An Introduction',
'section': 'Contrastive Learning',
'text': 'Two key aspects of CL contribute to its effectiveness'}]
Arriba, vemos los resultados de texto superiores. Observe que solo tenemos un artículo, aunque k=5. Esto se debe a que los elementos 2.º a 5.º estaban por debajo del umbral de 0,1.
Curiosamente, este elemento no parece útil para nuestra consulta inicial de “¿Cuál es la función de pérdida contrastiva de CLIP?” Esto destaca uno de los desafíos clave de la búsqueda vectorial: Es posible que elementos similares a una consulta determinada no necesariamente ayuden a responderla..
Una forma de mitigar este problema es aplicar restricciones menos estrictas a nuestros resultados de búsqueda aumentando k y bajando la similitud límitey luego esperar que el LLM pueda determinar qué es útil y qué no.
Para hacer esto, primero empaquetaré los pasos de búsqueda vectorial en una función de Python.
def similarity_search(query_embed, target_embeddings, content_list,
k=5, threshold=0.05, temperature=0.5):
"""
Perform similarity search over embeddings and return top k results.
"""
# Calculate similarities
similarities = torch.matmul(query_embed, target_embeddings.T)# Rescale similarities via softmax
scores = torch.nn.functional.softmax(similarities/temperature, dim=1)
# Get sorted indices and scores
sorted_indices = scores.argsort(descending=True)[0]
sorted_scores = scores[0][sorted_indices]
# Filter by threshold and get top k
filtered_indices = [
idx.item() for idx, score in zip(sorted_indices, sorted_scores)
if score.item() >= threshold
][:k]
# Get corresponding content items and scores
top_results = [content_list[i] for i in filtered_indices]
result_scores = [scores[0][i].item() for i in filtered_indices]
return top_results, result_scores
Luego, establezca parámetros de búsqueda más inclusivos.
# search over text chunks
text_results, text_scores = similarity_search(query_embed, text_embeddings,
text_content_list, k=15, threshold=0.01, temperature=0.25)# search over images
image_results, image_scores = similarity_search(query_embed, image_embeddings,
image_content_list, k=5, threshold=0.25, temperature=0.5)
Esto da como resultado 15 resultados de texto y 1 resultado de imagen.
1 - Two key aspects of CL contribute to its effectiveness
2 - To make a class prediction, we must extract the image logits and evaluate
which class corresponds to the maximum.
3 - Next, we can import a version of the clip model and its associated data
processor. Note: the processor handles tokenizing input text and image
preparation.
4 - The basic idea behind using CLIP for 0-shot image classification is to
pass an image into the model along with a set of possible class labels. Then,
a classification can be made by evaluating which text input is most similar to
the input image.
5 - We can then match the best image to the input text by extracting the text
logits and evaluating the image corresponding to the maximum.
6 - The code for these examples is freely available on the GitHub repository.
7 - We see that (again) the model nailed this simple example. But let’s try
some trickier examples.
8 - Next, we’ll preprocess the image/text inputs and pass them into the model.
9 - Another practical application of models like CLIP is multimodal RAG, which
consists of the automated retrieval of multimodal context to an LLM. In the
next article of this series, we will see how this works under the hood and
review a concrete example.
10 - Another application of CLIP is essentially the inverse of Use Case 1.
Rather than identifying which text label matches an input image, we can
evaluate which image (in a set) best matches a text input (i.e. query)—in
other words, performing a search over images.
11 - This has sparked efforts toward expanding LLM functionality to include
multiple modalities.
12 - GPT-4o — Input: text, images, and audio. Output: text.FLUX — Input: text.
Output: images.Suno — Input: text. Output: audio.
13 - The standard approach to aligning disparate embedding spaces is
contrastive learning (CL). A key intuition of CL is to represent different
views of the same information similarly [5].
14 - While the model is less confident about this prediction with a 54.64%
probability, it correctly implies that the image is not a meme.
15 - [8] Mini-Omni2: Towards Open-source GPT-4o with Vision, Speech and Duplex
Capabilities
Impulsar MLLM
Aunque la mayoría de estos resultados de elementos de texto no parecen útiles para nuestra consulta, el resultado de la imagen es exactamente lo que estamos buscando. Sin embargo, dados estos resultados de búsqueda, veamos cómo responde LLaMA 3.2 Vision a esta consulta.
Primero estructuraremos los resultados de la búsqueda como cadenas bien formateadas.
text_context = ""
for text in text_results:
if text_results:
text_context = text_context + "**Article title:** "
+ text['article_title'] + "\n"
text_context = text_context + "**Section:** "
+ text['section'] + "\n"
text_context = text_context + "**Snippet:** "
+ text['text'] + "\n\n"
image_context = ""
for image in image_results:
if image_results:
image_context = image_context + "**Article title:** "
+ image['article_title'] + "\n"
image_context = image_context + "**Section:** "
+ image['section'] + "\n"
image_context = image_context + "**Image Path:** "
+ image['image_path'] + "\n"
image_context = image_context + "**Image Caption:** "
+ image['caption'] + "\n\n"
Tenga en cuenta los metadatos que acompañan a cada elemento de texto e imagen. Esto ayudará al LLaMA a comprender mejor el contexto del contenido.
A continuación, entrelazamos los resultados de texto e imagen en un mensaje.
# construct prompt template
prompt = f"""Given the query "{query}" and the following relevant snippets:{text_context}
{image_context}
Please provide a concise and accurate answer to the query, incorporating
relevant information from the provided snippets where possible.
"""
El mensaje final es bastante largo, por lo que no lo imprimiré aquí. Sin embargo, se muestra completamente en el cuaderno de ejemplo en GitHub.
Finalmente, podemos usar ollama para pasar este mensaje a LLaMA 3.2 Vision.
ollama.pull('llama3.2-vision')response = ollama.chat(
model='llama3.2-vision',
messages=[{
'role': 'user',
'content': prompt,
'images': [image["image_path"] for image in image_results]
}]
)
print(response['message']['content'])
The image depicts a contrastive loss function for aligning text and image
representations in multimodal models. The function is designed to minimize the
difference between the similarity of positive pairs (text-image) and negative
pairs (text-text or image-image). This loss function is commonly used in CLIP,
which stands for Contrastive Language-Image Pre-training.**Key Components:**
* **Positive Pairs:** Text-image pairs where the text describes an image.
* **Negative Pairs:** Text-text or image-image pairs that do not belong to
the same class.
* **Contrastive Loss Function:** Calculates the difference between positive
and negative pairs' similarities.
**How it Works:**
1. **Text-Image Embeddings:** Generate embeddings for both text and images
using a multimodal encoder (e.g., CLIP).
2. **Positive Pair Similarity:** Calculate the similarity score between each
text-image pair.
3. **Negative Pair Similarity:** Calculate the similarity scores between all
negative pairs.
4. **Contrastive Loss Calculation:** Compute the contrastive loss by
minimizing the difference between positive and negative pairs' similarities.
**Benefits:**
* **Multimodal Alignment:** Aligns text and image representations for better
understanding of visual content from text descriptions.
* **Improved Performance:** Enhances performance in downstream tasks like
image classification, retrieval, and generation.
El modelo capta correctamente que la imagen contiene la información que necesita y explica la intuición general de cómo funciona. Sin embargo, No entiende el significado de los pares positivos y negativos.pensando que un par negativo corresponde a un par de la misma modalidad.
Mientras repasábamos los detalles de implementación paso a paso, empaqueté todo en una interfaz de usuario agradable usando Gradio en este computadora portátil en el repositorio de GitHub.
Los sistemas RAG multimodales pueden sintetizar el conocimiento almacenado en una variedad de formatos, ampliando lo que es posible con la IA. Aquí, revisamos 3 estrategias simples para desarrollar dicho sistema y luego vimos una implementación de ejemplo de un asistente de control de calidad de blog multimodal.
Aunque el ejemplo funcionó bastante bien para esta demostración, existen claras limitaciones en el proceso de búsqueda. Algunas técnicas que pueden mejorar esto incluyen el uso de un reranker para refinar la búsqueda de similitudes resultados y mejorar la calidad de la búsqueda mediante incrustaciones multimodales afinadas.
Si quieres ver futuras publicaciones sobre estos temas, házmelo saber en los comentarios 🙂
Más sobre modelos multimodales 👇