多模态图文问答实战案例

《DeepSeek大模型高性能核心技术与多模态融合开发(人工智能技术丛书)》(王晓华)【摘要 书评 试读】- 京东图书

图像问答,作为一种基本的多模态模型形式,融合了视觉与语言处理的技术精髓。它不仅能够理解图像的视觉内容,还能根据用户提出的问题,准确地从图像中提取相关信息,并最终以自然语言的形式给出回答。这种模型的出现,极大地丰富了人机交互的方式,使得机器能够更自然地理解和回应人类的问题。

在图像问答系统中,多模态模型的运用至关重要。该模型能够同时处理图像和语言两种不同模态的数据,通过深度学习算法挖掘它们之间的内在联系。当用户提出一个问题时,系统首先会分析问题的语义,明确用户想要了解的信息点;然后,系统会利用视觉处理技术对图像进行解析,识别出图像中的关键元素;最后,结合问题语义和图像内容,系统生成简洁明了的回答。

随着技术的不断发展,图像问答系统将在更多领域得到应用。例如,在智能教育领域,它可以帮助学生更直观地理解复杂的概念;在智能家居领域,它可以为用户提供更加便捷的控制方式;在医疗领域,它还可以辅助医生进行疾病诊断等。

在本章中,我们将讲解一种基于多模态的图文问答实战,即将图像输入与文本内容进行拉平的操作,再嵌入后进行后续的处理和计算。

10.1.1  一种新的多模态融合方案

为了将图像输入到视觉多模态模型中,我们首先需要执行一个关键步骤:将图像的像素密集地嵌入到模型中。在ViT(即图像分块技术)问世之前,多数VLP[yx1] [晓王2] (Vision-and-Language Pre-training,视觉与语言预训练)方法都高度依赖于复杂的图像特征提取流程。这些流程通常涉及区域监督(例如物体检测)和卷积架构(如ResNet)的运用。因此,VLP研究的重点多集中在通过提升视觉嵌入器的性能来优化整体模型的效果。

研究人员渴望设计出功能更强大的视觉encoder,但这其中潜在着一个实际问题。在学术研究领域,视觉encoder所提取的区域特征可以预先缓存,从而加速研究进程。然而,在实际应用开发中,当面对全新的数据时,研究人员仍需经历区域特征的提取过程。这一过程往往耗时较长,因此,拥有高性能视觉嵌入器所带来的速度瓶颈问题,在实践中常常被忽视。

在这里,我们开发了一种新的多模态视觉模型ViLT。其总体架构如图10-1所示。Transformer Encoder使用预训练的 ViT来初始化,Image Embedding使用ViT中的Patch投影来实现,Word Embedding使用文本的tokenizer进行标记化,并学习一个全新的文本嵌入参数。

图10-1  一种新的多模态融合方案

具体来看,为了实现此种图像和文字特征融合的效果。我们可以使用如下代码:

token_embedding = self.embedding(input_token)
bs, seq_len, dim = token_embedding.shape
image_embedding = self.patch_embedding(image).to(token_embedding.device)

bs,seq_len,dim = token_embedding.shape
img_bs,image_len,dim = image_embedding.shape

position_ids = torch.concat((torch.zeros(size=(bs,image_len),dtype=torch.int), torch.ones(size=(bs,seq_len),dtype=torch.int)),dim=-1).to(token_embedding.device)
position_embedding = self.position_embedding(position_ids)
embedding = torch.cat((image_embedding,token_embedding),dim=1) + position_embedding

for i in range(self.num_layers):
     #residual = token_embedding
     #这里不能动,需要把信息融合进去
     embedding = self.layers[i](embedding, past_length = image_len)

从上面代码可以看到,我们分别提取了token_embedding与image_embedding,之后使用concat将两个embedding连接在一起。而past_length参数确保了模型在处理过程中能够区分并正确处理图像和文本数据。完整的ViLT代码如下所示。

import torch
import torch.nn as nn
import torch.nn.functional as F

import kan,mhsa
import timm

class ViLT(nn.Module):
    def __init__(self,vocab_size = 3120,num_layers = 6,d_model = 384,attention_head_num = 6,hidden_dropout = 0.1):
        """Full Mamba model."""
        super().__init__()

        self.num_layers = num_layers
        self.hidden_dropout = hidden_dropout
        self.embedding = nn.Embedding(3120, d_model)
        self.patch_embedding = timm.layers.PatchEmbed(28,patch_size=7,in_chans = 1,embed_dim=d_model)
        self.image_patch_num = int((28/7)**2)

        self.layers = torch.nn.ModuleList([mhsa.EncoderBlock(d_model, attention_head_num, hidden_dropout) for _ in range(num_layers)])

        self.lm_head = kan.KAN([d_model, vocab_size])

        "这里是我加上一个position_ids,目标是标注输入image与token_embedding方式"
        self.position_embedding = nn.Embedding(2, d_model)

    def forward(self, image,input_token):

        token_embedding = self.embedding(input_token)
        bs, seq_len, dim = token_embedding.shape
        image_embedding = self.patch_embedding(image).to(token_embedding.device)

        bs,seq_len,dim = token_embedding.shape
        img_bs,image_len,dim = image_embedding.shape

        position_ids = torch.concat((torch.zeros(size=(bs,image_len), dtype=torch.int), torch.ones(size=(bs,seq_len),dtype=torch.int)), dim=-1).to(token_embedding.device)
        position_embedding = self.position_embedding(position_ids)
        embedding = torch.cat((image_embedding,token_embedding),dim=1) + position_embedding

        for i in range(self.num_layers):
            #residual = token_embedding
            #这里不能动,需要把信息融合进去
            embedding = self.layers[i](embedding, past_length = image_len)

        x = torch.nn.functional.dropout(embedding,p=0.1)
        logits = self.lm_head(x)

        return logits

    @torch.no_grad()
    def generate(self,image, prompt=None,n_tokens_to_gen = 20,  temperature=1.,top_k = 3,sample = False,eos_token=2,device = "cuda"):
        """
        根据给定的提示(prompt)生成一段指定长度的序列。

        参数:
        - seq_len: 生成序列的总长度。
        - prompt: 序列生成的起始提示,可以是一个列表。
        - temperature: 控制生成序列的随机性。温度值越高,生成的序列越随机;温度值越低,生成的序列越确定。
        - eos_token: 序列结束标记的token ID,默认为2。
        - return_seq_without_prompt: 是否在返回的序列中不包含初始的提示部分,默认为True。

        返回:
        - 生成的序列(包含或不包含初始提示部分,取决于return_seq_without_prompt参数的设置)。
        """

        # 将输入的prompt转换为torch张量,并确保它在正确的设备上(如GPU或CPU)。]

        self.eval()
        #prompt = torch.tensor(prompt)
        prompt = prompt.clone().detach().requires_grad_(False).to(device)

        input_ids = prompt
        for token_n in range(n_tokens_to_gen):
            with torch.no_grad():
                indices_to_input = input_ids
                next_token_logits = self.forward(image,indices_to_input)[:, -1]

            probs = F.softmax(next_token_logits, dim=-1) * temperature
            (batch, vocab_size) = probs.shape

            if top_k is not None:
                (values, indices) = torch.topk(probs, k=top_k)
                probs[probs < values[:, -1, None]] = 0
                probs = probs / probs.sum(axis=1, keepdims=True)

            if sample:
                next_indices = torch.multinomial(probs, num_samples=1)
            else:
                next_indices = torch.argmax(probs, dim=-1)[:, None]

            input_ids = torch.cat([input_ids, next_indices], dim=1)

        return input_ids

在上面代码中,我们除了在模型中进行融合外,还整合了生成部分,结合原始的生成模型的生成方式,可以对文本内容进行预测。

10.1.2  数据集的设计与使用

本小节将完成图像问答模型的数据准备,此时我们依旧使用MNIST数据集来获取数据,不同的是我们在这例子中用来提问的文本内容,其相应的回答是MNIST手写所代表的数字,示例如下。

sample_texts = [
"这个数字或许是:",
"此数字可能等于:",
"该数字可能表示:",
"这个数字可能意味着:",
"或许这个数字是:",
……
]

此时对MNIST数据集处理的代码如下所示。

class MNIST(Dataset):
    def __init__(self, is_train=True):
        super().__init__()
        self.ds = torchvision.datasets.MNIST('../dataset/mnist/', train=is_train, download=True)
        self.img_convert = Compose([
            PILToTensor(),
        ])

    def __len__(self):
        return len(self.ds)

    def __getitem__(self, index):
        img, label = self.ds[index]

        #text = f"现在的数字是:{label}#"
        text = random.sample(sample_texts,1)[0][-10:]
        text = text + str(label) + "#"

        full_tok = tokenizer_emo.encode(text)[-12:]
        full_tok = full_tok + [1] * (12 - len(full_tok))

        inp_tok = full_tok[:-1]
        tgt_tok = full_tok[1:]

        inp_tok = torch.tensor(inp_tok)
        tgt_tok = torch.tensor(tgt_tok)

        """
        torch.Size([1, 28, 28])
        """
        return self.img_convert(img) / 255.0, inp_tok,tgt_tok

10.1.3  多模态融合数据集的训练

对于训练任务,我们需要根据需求设置特殊的label。观察此时的数据,我们的模型并不需要预测image部分,只需要预测文本生成部分。因此,我们在建立label时,可以额外添加一个不需要预测的image label部分,放置在文本内容之前,代码如下所示。

token_tgt_pad = torch.ones(size=(token_inp.shape[0],16),dtype=torch.int) * -100
token_tgt = torch.concat((token_tgt_pad,token_tgt),dim=-1)
token_tgt = token_tgt.to(device)

同样地,对于损失函数的计算,我们也需要做如下设计:

criterion = torch.nn.CrossEntropyLoss(ignore_index=-100)

这样做的目的是对于补充的部分,忽略了额外添补的image特征部分,从而只需要计算对应的文本内容。完整的训练代码如下所示。

from model import ViLT
import math
from tqdm import tqdm
import torch
from torch.utils.data import DataLoader

device = "cuda"
model = ViLT()
model.to(device)
save_path = "./saver/mamba_generator.pth"
#model.load_state_dict(torch.load(save_path),strict=False)

BATCH_SIZE = 640
seq_len = 49
import get_data_emotion
#import get_data_emotion_2 as get_data_emotion
train_dataset = get_data_emotion.MNIST()
train_loader = (DataLoader(train_dataset, batch_size=BATCH_SIZE,shuffle=True))

optimizer = torch.optim.AdamW(model.parameters(), lr = 2e-4)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max = 1200,eta_min=2e-5,last_epoch=-1)
criterion = torch.nn.CrossEntropyLoss(ignore_index=-100)

for epoch in range(12):
    pbar = tqdm(train_loader,total=len(train_loader))
    for image,token_inp,token_tgt in pbar:
        image = image.to(device)
        token_inp = token_inp.to(device)

        token_tgt_pad = torch.ones(size=(token_inp.shape[0],16),dtype=torch.int) * -100
        token_tgt = torch.concat((token_tgt_pad,token_tgt),dim=-1)
        token_tgt = token_tgt.to(device)
        logits = model(image,token_inp)

        loss = criterion(logits.view(-1, logits.size(-1)), token_tgt.view(-1))

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        lr_scheduler.step()  # 执行优化器
        pbar.set_description(f"epoch:{epoch +1}, train_loss:{loss.item():.5f}, lr:{lr_scheduler.get_last_lr()[0]*1000:.5f}")
    if (epoch + 1) % 7 == 0:
        torch.save(model.state_dict(), save_path)
torch.save(model.state_dict(), save_path)

读者可以自行运行代码并查看结果。

<think>好的,我现在需要回答用户关于Hugging Face中多模态数据处理方法、支持的模型和数据集的问题。用户提到他们参考的两篇文献,其中提到了多模态大模型如FLAVA,以及多模态数据的整合方法。首先,我需要回忆Hugging Face的文档和资源,特别是关于多模态的部分。 首先,Hugging Face的Transformers库支持多种多模态模型,比如Vision Transformer (ViT)、CLIP、FLAVA等。这些模型处理图像和文本的结合任务。例如,CLIP可以用于图像和文本的匹配,而FLAVA则支持更多跨模态的任务,如MMM(多模态掩码)和MLM(掩码语言建模)。用户提到的Kiela等人的工作,应该是在FLAVA模型中实现的,这需要确认Hugging Face是否有相关的模型实现。 其次,关于数据处理,Hugging Face的Datasets库可能包含多模态数据集,比如COCO、Flickr30k等,这些数据集通常包含图像和对应的文本描述。处理这些数据可能需要特定的加载和预处理方法,比如使用FeatureExtractor来处理图像,Tokenizer处理文本,然后将两者结合输入模型。 然后,需要检查Hugging Face的文档是否有关于多模态处理的教程或指南。例如,可能有用Pipeline来处理多模态任务,如图文匹配、图像描述生成等。此外,Hugging Face Hub可能托管了预训练的多模态模型,用户可以直接下载并使用。 需要注意的是,用户引用的资料中提到了多模态大模型的评测数据集,可能Hugging Face也提供了相关的数据集或评测工具。例如,COCO数据集用于图像描述和检测,VQA(视觉问答)数据集也是常见的多模态数据集。 可能还需要提到Hugging Face的社区贡献,比如用户自己上传的多模态模型或数据集,这些资源也可能对用户有帮助。另外,Hugging Face的课程或博文可能有关于多模态模型的使用案例,可以指导用户如何实际操作。 最后,要确保回答的结构清晰,分点列出数据处理方法、支持的模型、数据集,并给出示例代码或使用步骤。同时,根据用户提供的引用,正确标注参考文献,例如FLAVA模型的相关信息引用[^2],多模态大模型的背景引用[^1]。 现在需要验证一些细节,比如Hugging Face是否确实支持FLAVA模型。快速查阅Hugging Face模型库,发现确实有FLAVA的实现,属于Transformers库的一部分。因此,在回答中可以提到这一点,并引用相关文档。此外,确认CLIP和ViT是否在Hugging Face中有对应的处理工具,答案是肯定的,因此这些都可以作为支持的模型列出。 关于数据集,Hugging Face Datasets库中的COCO、Flickr30k等需要确认是否存在,并给出正确的名称和加载方式。例如,使用load_dataset("coco")来加载COCO数据集。预处理部分可能需要使用AutoProcessor,它结合了图像和文本的处理,比如CLIPProcessor,处理图像和文本的输入。 在代码示例方面,可以给出一个使用Pipeline进行图文匹配的例子,或者加载FLAVA模型进行多模态任务的代码片段。例如,使用AutoModel类加载模型,并使用处理器处理输入数据。 总结回答的结构应该是: 1. 多模态数据处理方法:使用Processor类处理图像和文本,Datasets库加载数据集。 2. 支持的模型:FLAVA、CLIP、ViLT、LayoutLM等。 3. 支持的数据集:COCO、Flickr30k、VQA等。 4. 示例代码:加载模型和处理器,处理数据并进行推理。 5. 相关资源链接和引用。 需要确保所有技术细节准确,比如正确的模型名称、处理器类名,以及数据集的正确调用方式。同时,按照用户的要求,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能用不到数学公式,所以可以忽略。最后生成相关问题,比如如何微调多模态模型,如何处理不同模态的对齐等。</think>### Hugging Face多模态数据处理及资源详解 Hugging Face通过Transformers库和Datasets库提供了完善的多模态数据处理工具及预训练模型支持,以下是核心要素的详细说明: --- #### 一、多模态数据处理方法 1. **统一特征处理器** 使用`AutoProcessor`自动加载适用于多模态模型的处理器,例如`CLIPProcessor`可同时处理图像和文本: ```python from transformers import AutoProcessor processor = AutoProcessor.from_pretrained("openai/clip-vit-base-patch32") ``` 2. **数据集加载与预处理** Hugging Face Datasets库内置多模态数据集(如COCO、Flickr30k),支持一键加载: ```python from datasets import load_dataset dataset = load_dataset("coco_captions") # 加载COCO图文数据集 ``` --- #### 二、支持的多模态模型 | 模型名称 | 功能特点 | 典型任务 | |-----------------|-----------------------------------|---------------------------| | **FLAVA** | 支持图像-文本联合掩码建模 | 多模态推理、视觉问答 | | **CLIP** | 图像-文本对比学习 | 跨模态检索、零样本分类 | | **ViLT** | 视觉-语言Transformer | 图文匹配、图像描述生成 | | **LayoutLM** | 文档图像与文本联合建模 | 文档理解、表格识别 | --- #### 三、常用多模态数据集 1. **COCO Captions** 包含12万张图片的5种文本描述,用于图像描述生成任务。 2. **Flickr30k** 3.1万张图片,每张图对应5句描述,常用于图文检索。 3. **VQA (Visual Question Answering)** 开放式视觉问答数据集,需结合图像与问题生成答案。 --- #### 四、实战示例:使用CLIP实现图文检索 ```python from transformers import CLIPModel, CLIPProcessor import torch # 加载模型与处理器 model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") # 处理输入 image = Image.open("cat.jpg") # 示例图片 texts = ["a photo of a cat", "a picture of a dog"] inputs = processor(text=texts, images=image, return_tensors="pt", padding=True) # 计算相似度 with torch.no_grad(): outputs = model(**inputs) logits_per_image = outputs.logits_per_image probs = logits_per_image.softmax(dim=1) # 图文匹配概率 ``` --- #### 五、扩展资源 1. **官方文档** - [Transformers多模态指南](https://huggingface.co/docs/transformers/multimodal) - [Datasets数据集目录](https://huggingface.co/datasets) 2. **论文与教程** - FLAVA论文《Multimodal Masked Autoencoders》 - Hugging Face博客《Zero-shot Image Classification with CLIP》 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值