引言
最近两年,多模态AI技术迎来了爆发式增长——从OpenAI的GPT-4V到Google的Gemini,再到开源界的LLaVA、CogVLM,模型不仅能“看懂”图片,还能结合文本进行推理、问答甚至创作。然而,很多开发者面对这些庞然大物时却不知从何入手。其实,借助HuggingFace生态,我们完全可以用少量代码快速搭建自己的多模态应用。本文将以Salesforce的BLIP模型为例,带你完成一个图像描述生成 + 视觉问答的应用原型,所有代码均可直接运行。
核心概念:多模态与BLIP
多模态模型的核心任务是对齐不同模态的信息——将视觉信号(像素)与语义信息(文本)映射到同一个向量空间,从而实现跨模态理解。BLIP(Bootstrapping Language-Image Pre-training)是一种经典的视觉-语言预训练模型,它在一个统一的框架下支持三类任务:
- 图像文本匹配(Image-Text Matching)
- 图像描述生成(Image Captioning)
- 视觉问答(Visual Question Answering)
BLIP采用ViT(Vision Transformer)作为图像编码器,BERT作为文本编码器/解码器,并通过跨模态注意力机制融合信息。对于描述生成任务,它使用“图像-引导的语言模型”以自回归方式逐词生成标题;对于问答,则将图像特征与问题一起输入,在文本解码时生成答案。
选择BLIP的理由有三:轻量(base模型仅约990MB)、效果出色、代码接口极其友好。下面我们就用它来构建一个完整的多模态Demo。
实战示例:图像描述 + 视觉问答
环境准备
确保安装以下依赖(建议使用Python 3.8+,PyTorch根据CUDA版本选择):
pip install torch torchvision transformers pillow requests本文代码可在CPU上运行(速度较慢),推荐使用GPU环境。
完整代码
我们创建一个multimodal_app.py,包含三个核心功能:
- 从URL或本地路径加载图像
- 生成图像描述(Captioning)
- 针对图像进行问答(VQA)
```python
import torch
import requests
from PIL import Image
from io import BytesIO
from transformers import BlipProcessor, BlipForConditionalGeneration, BlipForQuestionAnswering
---------- 模型加载 ----------
图像描述模型(Captioning)
caption_model_name = "Salesforce/blip-image-captioning-base"
caption_processor = BlipProcessor.from_pretrained(caption_model_name)
caption_model = BlipForConditionalGeneration.from_pretrained(caption_model_name)
视觉问答模型(VQA)
vqa_model_name = "Salesforce/blip-vqa-base"
vqa_processor = BlipProcessor.from_pretrained(vqa_model_name)
vqa_model = BlipForQuestionAnswering.from_pretrained(vqa_model_name)
选择设备(有GPU则使用GPU)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
caption_model = caption_model.to(device)
vqa_model = vqa_model.to(device)
def load_image(image_source):
"""
从URL或本地文件路径加载PIL图像
image_source: str - 图片URL或本地路径
"""
if image_source.startswith("http://") or image_source.startswith("https://"):
response = requests.get(image_source, stream=True)
response.raise_for_status()
return Image.open(BytesIO(response.content)).convert("RGB")
else:
return Image.open(image_source).convert("RGB")
def generate_caption(image_source, max_length=20, num_beams=4):
"""
生成图像的自然语言描述
image_source: 图片URL或本地路径
max_length: 生成文本的最大长度
num_beams: beam search的束宽,越大越可能生成更优但计算更慢
"""
image = load_image(image_source)
# 预处理:将图像转为模型输入格式,并添加文本提示(caption任务通常不需要额外文本)
inputs = caption_processor(images=image, return_tensors="pt").to(device)
# 生成描述
with torch.no_grad():
out = caption_model.generate(
**inputs,
max_length=max_length,
num_beams=num_beams,
early_stopping=True
)
# 解码为文字,跳过特殊标记
caption = caption_processor.decode(out[0], skip_special_tokens=True)
return caption
def visual_question_answer(image_source, question, max_length=30):
"""
根据图像内容回答问题
image_source: 图片URL或本地路径
question: 关于图像的自然语言问题
max_length: 答案的最大长度
"""
image = load_image(image_source)
# 预处理:同时传入图像和问题
inputs = vqa_processor(image, question, return_tensors="pt").to(device)
with torch.no_grad():
out = vqa_model.generate(
**inputs,
max_length=max_length,
num_beams=1 # VQA通常用贪心解码较快
)
answer = vqa_processor.decode(out[0], skip_special_tokens=True)
return answer
ifname== "main":
# 示例图片:一只猫坐在沙发上(Unsplash免费图片)
test_image_url = "https://images.unsplash.com/photo-1507149833265-60c372d2b968?w=400"
print("