基于BLIP-2的视觉问答

大型语言模型 (LLM) 最近获得了很大的关注,出现了许多流行的模型,如 GPT、OPT、BLOOM 等。 这些模型擅长学习自然语言,非常适合构建聊天机器人、编码助手、决策助手或翻译系统。 然而,他们缺乏其他模式的知识—例如,他们无法处理图像、音频或视频。 这就是 BLIP 的用武之地,通过视觉理解来增强LLM的自然语言能力。

本质上,你可以向 BLIP 模型提供图像和文本对来执行各种任务,例如视觉问答 (VQA)、图像字幕或图像文本检索。

然而,用 BLIP 构建产品可能并不简单:你需要正确的模型服务框架和数据序列化库。 在 Jina AI,我们始终相信多模态 AI,因此我们为此目的优化了 Jina 和 DocArray。 为 BLIP-2 提供服务是这两个项目的典型用例。

让我们看看它是如何工作的!

1、BLIP-2

BLIP-2是论文BLIP-2:Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models中提出的视觉语言模型。

它利用冻结的预训练图像编码器和 LLM,通过在它们之间训练轻量级 12 层 Transformer 编码器,在各种视觉语言任务上实现最先进的性能。

2、Jina 和 DocArray

在深入研究代码之前,我们先简要介绍一下 Jina 和 DocArray,并解释一下这两个项目最近发生的情况。

Jina 是一个 MLOps 框架,用于构建和提供多模式 AI 服务,然后在云上扩展和部署它们。 但最近我们一直在重塑 Jina,以更有效地为模型提供服务。 在 3.14 版本中,我们允许用户使用 gRPC 协议将 Executor 作为独立服务提供服务。 在这篇文章中,我们将使用这些新发布的功能来服务于 BLIP-2 模型。

对于数据表示和序列化,Jina 使用 DocArray,这是一个用于非结构化、多模态数据的库。 它针对传输中的数据进行了优化,因为数据可以以多种格式(包括 protobuf)进行序列化。 换句话说,可以使用 DocArray 格式通过线路接收/发送模型输入和输出。

我们还重塑了 DocArray,以更好地适应多模式 AI 用例。 事实上,它正在经历大量重构,目标是通过 DocArray v2 提供灵活且强大的数据格式。

因此,我们将使用当前的 DocArray 和 DocArray v2 的 alpha 版本来表示模型查询和输出(这样做时,你将很快理解我们为什么重构 DocArray!)。

3、使用 DocArray 和 Jina 为 BLIP-2 提供服务

由于 BLIP-2 使用 LLM,因此它配备了不同的开源语言模型(即 flan-t5 和 opt)。 在本博客中,我们将使用 flan-t5-xl。 让我们从安装依赖项开始:

pip install jina torch git+https://github.com/huggingface/transformers pillow

然后,我们可以初始化一个加载模型的 Jina Executor:

from jina import Executor, Deployment, DocumentArray, requests
from transformers import Blip2Processor, Blip2ForConditionalGeneration
import torch

class Blip2Executor(Executor):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.processor = Blip2Processor.from_pretrained("Salesforce/blip2-flan-t5-xl")
        self.model = Blip2ForConditionalGeneration.from_pretrained(
            "Salesforce/blip2-flan-t5-xl", torch_dtype=torch.float16
        )
        self.model.to('cuda')

使用 BLIP-2 执行视觉问答任务非常简单:只需将图像和文本问题输入并生成文本输出即可。

让我们创建一个 Executor Endpoint 方法来启用 VQA 任务:

@requests
def vqa(self, docs: DocumentArray, **kwargs):
    for doc in docs:
        doc.load_uri_to_image_tensor()
        inputs = self.processor(images=doc.tensor, text=doc.tags['prompt'], return_tensors="pt").to('cuda', torch.float16)
        generated_ids = self.model.generate(**inputs)
        generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
        doc.tags['response'] = generated_text

DocArray文档可以在张量字段中存储图像,我们可以将提示和文本输出放在标签字段中。

最后,我们可以使用新发布的 Deployment 类为我们的 Executor 提供服务:

with Deployment(uses=Blip2Executor, timeout_ready=-1, port=12345) as dep:
    dep.block()`
Loading checkpoint shards: 100%|██████████████████████████████████| 2/2 [00:05<00:00,  2.92s/it]
─────────────────────────────── 🎉 Deployment is ready to serve! ───────────────────────────────
╭────────────────────── 🔗 Endpoint ──────────────────────╮
│  ⛓   Protocol                                     GRPC │
│  🏠     Local                            0.0.0.0:12345  │
│  🔒   Private                     192.168.178.31:12345  │
│  🌍    Public  2003:f1f:4a00:85b7:3950:81fa:952f:12345  │
╰─────────────────────────────────────────────────────────╯

现在我们的执行者应该准备好服务了。 只需使用 Jina Client 通过 gRPC 向 Executor 发送请求即可:

from jina import Client, Document
client = Client(port=12345)

docs = client.post(
        on='/', 
        inputs=[Document(
            uri='<http://images.cocodataset.org/val2017/000000039769.jpg>',
            tags={'prompt': 'Question: How many cats are there? Answer:'}
        )]
)
print(docs[0].tags['response'])
two

正如我们所看到的,BLIP-2 正确地回答了这个问题。 但这个答案的包装有点……笨拙。 虽然 DocArray 提供了标签和块等动态字段的灵活性,但这也带来了一些缺点:没有显式模式、奇怪的语法以及用于输入和输出的相同模式。

这就是我们在 DocArray v2 中重构内容的原因!

4、DocArray v2 :更好的语法

DocArray v2 背后的主要思想是用户可以使用类型提示定义输入和输出模式。 这意味着你在这些模式中定义所需的字段,而不是尝试将数据放入固定模式文档中。

这是 DocArray v1 表示数据的方式。 表示多模式数据意味着向文档添加更多块(子文档)。

现在,在 DocArray v2 中,模式不再将所有内容都转储到标签和块字段中,而是更加灵活和具有代表性。 你可以根据需要添加任意数量的字段,并确保严格输入数据验证:

这提供了两全其美的优点。 它使用户可以灵活地定义自己的模式,同时提供显式类型以实现更好的数据验证和转换。

Jina 提供了对 DocArray v2 的早期支持,并允许你在执行器端点中使用模式作为类型注释来定义服务的输入/输出格式。

让我们看看它是如何工作的。 首先从 v2 开发分支安装 DocArray:

pip install "git+https://github.com/docarray/docarray@feat-rewrite-v2#egg=docarray[common,torch,image]"

然后我们将通过指定输入和输出模式来重写执行器:

  • 输入模式包括图像和文本提示字段。
  • 输出架构仅包含一个文本字段(生成的响应)。
from jina import Executor, requests
from transformers import Blip2Processor, Blip2ForConditionalGeneration
import torch

from docarray import BaseDocument, DocumentArray
from docarray.typing import ImageUrl

class ImagePrompt(BaseDocument):
    img: ImageUrl
    prompt: str

class Response(BaseDocument):
    answer: str

class Blip2Executor(Executor):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.processor = Blip2Processor.from_pretrained("Salesforce/blip2-flan-t5-xl")
        self.model = Blip2ForConditionalGeneration.from_pretrained(
            "Salesforce/blip2-flan-t5-xl", torch_dtype=torch.float16
        )
        self.model.to('cuda')

    @requests
    def vqa(self, docs: DocumentArray[ImagePrompt], **kwargs) -> DocumentArray[Response]:
        response_docs = DocumentArray[Response]()
        for doc in docs:
            inputs = self.processor(images=doc.img.load(), text=doc.prompt, return_tensors="pt").to('cuda', torch.float16)
            generated_ids = self.model.generate(**inputs)
            generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
            response_docs.append(Response(answer=generated_text))
        return response_docs

现在,让我们为 Executor 提供服务并向其提交请求:

from jina import Deployment

with Deployment(uses=Blip2Executor) as dep:
    docs = dep.post(on='/bar', inputs=ImagePrompt(
        img='<http://images.cocodataset.org/val2017/000000039769.jpg>',
        prompt='Question: how many cats are there ? Answer:'
    ), return_type=DocumentArray[Response])
    print(docs[0].answer)
two

5、结束语

Jina 可让你构建基于 gRPC 的多模式 AI 服务。 它允许你以 Python 方式定义端点和请求/响应模式,从而消除了构建 gRPC 服务的复杂性。

DocArray 可让你高效地表示和序列化多模式数据。 为此,它提供了预定义的多模式数据类型和内置的 protobuf 序列化。

因此,Jina 和 DocArray 凭借高效的网络和序列化、Pythonic API 和富有表现力的数据类型等支柱,为构建多模态AI 服务提供了顶级的技术栈。


原文链接:Multimodal Visual Question Answering with BLIP-2 and Jina

BimAnt翻译整理,转载请标明出处