开源 AI 食谱文档

Smol 多模态 RAG:使用 Colab 免费层 GPU 上的 ColSmolVLM 和 SmolVLM 进行构建

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Open In Colab

Smol 多模态 RAG:使用 Colab 免费层 GPU 上的 ColSmolVLM 和 SmolVLM 进行构建

作者: Sergio Paniego

在本 Notebook 中,我们将变得更小🤏,并演示如何通过集成 ColSmolVLM 进行文档检索和 SmolVLM 作为视觉语言模型 (VLM) 来构建多模态检索增强生成 (RAG) 系统。这些轻量级模型使我们能够在消费级 GPU 甚至 Google Colab 免费层上运行功能齐全的多模态 RAG 系统。

本 Notebook 是多模态 RAG 配方系列的第三部分。如果您是该主题的新手或想探索更多,请查看以下之前的配方:

让我们深入探索并构建一个强大而紧凑的 RAG 系统!🚀

multimodal_rag_using_document_retrieval_and_smol_vlm (2).png

1. 安装依赖

让我们开始安装项目所需的基本库!🚀

对于本 Notebook,我们需要下载 byaldi正在进行中的 PR。一旦该 PR 合并,这些安装步骤将相应更新。

!pip install -q git+https://github.com/sergiopaniego/byaldi.git@colsmolvlm-support

2. 加载数据集 📁

在本 Notebook 中,我们将使用来自 Our World in Data 的图表和地图,这是一个提供丰富数据和可视化的开放访问平台。我们将重点关注预期寿命数据,它提供了全球预期寿命趋势的见解。

为了简化访问并保持“迷你”🤏,我们已将此数据的一个子集整理成一个托管在 Hugging Face 上的数据集。这个小集合非常适合演示,但在实际应用中,您可以扩展到更大的数据集以提高系统性能。

引用

Saloni Dattani, Lucas Rodés-Guirao, Hannah Ritchie, Esteban Ortiz-Ospina and Max Roser (2023) - “Life Expectancy” Published online at OurWorldinData.org. Retrieved from: 'https://ourworldindata.org/life-expectancy' [Online Resource]
from datasets import load_dataset

dataset = load_dataset("sergiopaniego/ourworldindata_example", split="train")

下载视觉数据后,我们将它本地保存,为 RAG(检索增强生成)系统做准备。此步骤至关重要,因为它使文档检索模型(ColSmolVLM)能够高效地索引、处理和操作视觉内容。正确的索引确保了系统执行期间的无缝集成和检索。

import os
from PIL import Image


def save_images_to_local(dataset, output_folder="data/"):
    os.makedirs(output_folder, exist_ok=True)

    for image_id, image_data in enumerate(dataset):
        image = image_data["image"]

        if isinstance(image, str):
            image = Image.open(image)

        output_path = os.path.join(output_folder, f"image_{image_id}.png")

        image.save(output_path, format="PNG")

        print(f"Image saved in: {output_path}")


save_images_to_local(dataset)

现在,让我们加载图像以探索数据集,并快速概览我们将要处理的视觉内容。此步骤有助于我们熟悉数据并确保一切都已正确设置,以进行后续阶段。

import os
from PIL import Image


def load_png_images(image_folder):
    png_files = [f for f in os.listdir(image_folder) if f.endswith(".png")]
    all_images = {}

    for image_id, png_file in enumerate(png_files):
        image_path = os.path.join(image_folder, png_file)
        image = Image.open(image_path)
        all_images[image_id] = image

    return all_images


all_images = load_png_images("/content/data/")

让我们可视化一些样本,以了解数据的结构!这将帮助我们掌握将要处理的内容的格式和布局。👀

>>> import matplotlib.pyplot as plt

>>> fig, axes = plt.subplots(1, 5, figsize=(20, 15))

>>> for i, ax in enumerate(axes.flat):
...     img = all_images[i]
...     ax.imshow(img)
...     ax.axis("off")

>>> plt.tight_layout()
>>> plt.show()

3. 初始化 ColSmolVLM 多模态文档检索模型 🤖

数据集准备好后,是时候初始化文档检索模型了,它将从原始图像中提取相关信息,并根据我们的查询返回相应的文档。该模型通过实现精确的信息检索,在增强系统对话能力方面发挥着关键作用。

对于此任务,我们将使用 Byaldi,一个旨在简化多模态 RAG 流水线的库。Byaldi 提供 API,用于集成多模态检索器和视觉语言模型,以实现高效的检索增强生成工作流程。

在本 Notebook 中,我们将特别关注 ColSmolVLM

ColPali architecture

此外,您可以探索 ViDore(视觉文档检索基准),了解顶级检索器如何运作。

首先,我们将从检查点加载模型。

from byaldi import RAGMultiModalModel

docs_retrieval_model = RAGMultiModalModel.from_pretrained("vidore/colsmolvlm-alpha")

接下来,我们将使用文档检索模型,通过指定图像存储的文件夹来索引文档。此过程允许模型高效地组织和处理文档,确保可以根据我们的查询快速检索它们。

docs_retrieval_model.index(
    input_path="data/", index_name="image_index", store_collection_with_index=False, overwrite=True
)

4. 使用文档检索模型检索文档 🤔

文档检索模型初始化完成后,我们可以通过提交问题并获取可能包含答案的相关文档来测试其功能。

模型将按相关性对结果进行排名,最相关的文档会首先返回。

让我们试一试,看看它的表现如何!

text_query = "What is the overall trend in life expectancy across different countries and regions?"

results = docs_retrieval_model.search(text_query, k=1)
results

让我们看看检索到的文档,并检查模型是否正确地将我们的查询与最佳结果匹配。

>>> result_image = all_images[results[0]["doc_id"]]
>>> result_image

5. 初始化用于问答的视觉语言模型 🙋

接下来,我们将初始化用于问答的视觉语言模型 (VLM)。为此任务,我们将使用 SmolVLM

SmolVLM architecture

请在此处查看 OpenVLMLeaderboard 了解开放视觉语言模型的最新进展。

首先,我们将从预训练检查点加载模型,并将其传输到 GPU 以获得最佳性能。您可以在此处探索完整的模型集合。

from transformers import Idefics3ForConditionalGeneration, AutoProcessor
import torch


model_id = "HuggingFaceTB/SmolVLM-Instruct"
vl_model = Idefics3ForConditionalGeneration.from_pretrained(
    model_id,
    device_map="auto",
    torch_dtype=torch.bfloat16,
    _attn_implementation="eager",
)
vl_model.eval()

接下来,我们将初始化视觉语言模型(VLM)处理器。

vl_model_processor = AutoProcessor.from_pretrained(model_id)

6. 组装 VLM 模型并测试系统 🔧

所有组件加载完毕后,我们就可以组装系统进行测试了。首先,我们将通过向系统提供检索到的图像和用户的查询来设置聊天结构。此步骤高度可定制,提供了根据您的需求调整交互的灵活性,并支持尝试不同的输入和输出。

chat_template = [
    {
        "role": "user",
        "content": [
            {
                "type": "image",
            },
            {"type": "text", "text": text_query},
        ],
    }
]

现在,让我们应用此聊天模板来设置系统,以便与模型进行交互。

text = vl_model_processor.apply_chat_template(chat_template, add_generation_prompt=True)

接下来,我们将处理输入,以确保它们格式正确,并可与视觉语言模型 (VLM) 一起使用。此步骤对于使模型能够根据提供的数据生成准确的响应至关重要。

inputs = vl_model_processor(
    text=text,
    images=[result_image],
    return_tensors="pt",
)
inputs = inputs.to("cuda")

现在我们准备好生成答案了!让我们看看系统如何使用处理后的输入来根据用户查询和检索到的图像提供响应。

generated_ids = vl_model.generate(**inputs, max_new_tokens=500)

模型生成输出后,我们会对其进行后处理以生成最终答案。

generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]

output_text = vl_model_processor.batch_decode(
    generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
>>> print(output_text[0])
The overall trend in life expectancy across different countries and regions is an increase over time.

如我们所见,SmolVLM 能够正确回答查询!🎉

现在,让我们来看看 SmolVLM 的内存消耗,以了解其资源使用情况。

>>> print(f"GPU allocated memory: {torch.cuda.memory_allocated() / 1024**3:.2f} GB")
>>> print(f"GPU reserved memory: {torch.cuda.memory_reserved() / 1024**3:.2f} GB")
GPU allocated memory: 8.32 GB
GPU reserved memory: 10.38 GB

7. 全部组装!🧑‍🏭️

现在,让我们创建一个包含整个管道的方法,以便将来可以轻松地重复使用它。

def answer_with_multimodal_rag(
    vl_model, docs_retrieval_model, vl_model_processor, all_images, text_query, retrival_top_k, max_new_tokens
):
    results = docs_retrieval_model.search(text_query, k=retrival_top_k)
    result_image = all_images[results[0]["doc_id"]]

    chat_template = [
        {
            "role": "user",
            "content": [{"type": "image"}, {"type": "text", "text": text_query}],
        }
    ]

    # Prepare the inputs
    text = vl_model_processor.apply_chat_template(chat_template, add_generation_prompt=True)
    inputs = vl_model_processor(
        text=text,
        images=[result_image],
        return_tensors="pt",
    )
    inputs = inputs.to("cuda")

    # Generate text from the vl_model
    generated_ids = vl_model.generate(**inputs, max_new_tokens=max_new_tokens)
    generated_ids_trimmed = [out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]

    # Decode the generated text
    output_text = vl_model_processor.batch_decode(
        generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
    )

    return output_text

让我们看看完整的 RAG 系统是如何运行的!

>>> output_text = answer_with_multimodal_rag(
...     vl_model=vl_model,
...     docs_retrieval_model=docs_retrieval_model,
...     vl_model_processor=vl_model_processor,
...     all_images=all_images,
...     text_query="What is the overall trend in life expectancy across different countries and regions?",
...     retrival_top_k=1,
...     max_new_tokens=500,
... )
>>> print(output_text[0])
The overall trend in life expectancy across different countries and regions is an increase over time.

🏆 我们现在拥有一个完全可操作的 smol RAG 流水线,它集成了 smol 文档检索模型smol 视觉语言模型,并经过优化以在单个消费级 GPU 上运行!这种强大的组合使我们能够根据用户查询和相关文档生成富有洞察力的响应,提供无缝的多模态体验。

8. 我们能变得更“小”吗?🤏

我们现在有一个完全可运行的系统,但我们能变得更吗?答案是肯定的!我们将使用 SmolVLM 模型的量化版本,以进一步降低系统的资源需求。

为了充分体验消耗上的差异,我建议重新初始化系统并运行所有单元格,除了那些实例化 VLM 模型的单元格。这样,您可以清楚地观察使用量化模型的影响。

让我们从安装 bitsandbytes 开始。

!pip install -q -U bitsandbytes

让我们创建 BitsAndBytesConfig 配置,以量化 int-4 配置加载我们的模型,这将有助于减少模型的内存占用并提高性能。

from transformers import BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
)

接下来,我们可以使用我们刚刚创建的量化配置来加载模型

from transformers import Idefics3ForConditionalGeneration, AutoProcessor

model_id = "HuggingFaceTB/SmolVLM-Instruct"
vl_model = Idefics3ForConditionalGeneration.from_pretrained(
    model_id, quantization_config=bnb_config, _attn_implementation="eager", device_map="auto"
)
vl_model_processor = AutoProcessor.from_pretrained(model_id)

最后,让我们测试我们量化模型的能力

>>> output_text = answer_with_multimodal_rag(
...     vl_model=vl_model,
...     docs_retrieval_model=docs_retrieval_model,
...     vl_model_processor=vl_model_processor,
...     all_images=all_images,
...     text_query="What is the overall trend in life expectancy across different countries and regions?",
...     retrival_top_k=1,
...     max_new_tokens=500,
... )
>>> print(output_text[0])
The overall trend in life expectancy across different countries and regions is an increase over time.

模型运行正常!🎉 现在,让我们来看看内存消耗。下面您可以看到结果——我们已成功进一步减少了内存使用!🚀

>>> print(f"GPU allocated memory: {torch.cuda.memory_allocated() / 1024**3:.2f} GB")
>>> print(f"GPU reserved memory: {torch.cuda.memory_reserved() / 1024**3:.2f} GB")
GPU allocated memory: 5.44 GB
GPU reserved memory: 7.86 GB

下表比较了 Cookboook 中其他两个多模态 RAG Notebook 和此处描述的两个版本的内存消耗。如您所见,这些系统在资源需求方面比其他系统小一个数量级。

笔记本 GPU 已分配内存 (GB) GPU 保留内存 (GB)
带量化的 Smol 多模态 RAG 5.44 GB 7.86 GB
Smol 多模态 RAG 8.32 GB 10.38 GB
带 ColQwen2、重排器
和消费级 GPU 量化 VLM 的多模态 RAG
13.93 GB 14.59 GB
带文档检索(ColPali)
和视觉语言模型(VLM)的多模态 RAG
22.63 GB 37.16 GB

8. 继续旅程 🧑‍🎓️

如果您渴望继续探索,请务必查看我们之前指南的结论和见解,使用文档检索(ColPali)和视觉语言模型(VLM)的多模态检索增强生成(RAG),以及重排器指南,在消费级 GPU 上使用 ColQwen2、重排器和量化 VLM 的多模态 RAG

祝您实验愉快!🧑‍🔬

< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.