首页
/ Transformers 中 BLIP-2 模型全解析:架构、配置与图生文推理实践

Transformers 中 BLIP-2 模型全解析:架构、配置与图生文推理实践

2026-09-06 18:46:46作者:裘旻烁

BLIP-2(Bootstrapping Language-Image Pre-training)是视觉-语言预训练领域的重要工作,它通过在冻结的图像编码器冻结的大语言模型之间训练一个轻量级 Querying Transformer(Q-Former)来弥合模态鸿沟。本文以 BLIP-2 模型文档 为骨架,结合当前仓库中 transformers 的源码实现,系统讲解 BLIP-2 的架构组成、三大配置类、Processor 使用方法,以及如何用 generate 完成图像描述、视觉问答、图文匹配等实战任务。读完本文,你将掌握该模型在 Transformers 中的完整调用链路与底层原理,可直接用于基于图像的生成与检索场景。

BLIP-2 是什么:用冻结模型做两阶段引导式预训练

BLIP-2 于 2023 年 1 月 30 日发表于论文 BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models,并由社区于 2023 年 2 月 9 日贡献到 Hugging Face Transformers。

端到端联合训练大规模视觉-语言模型的开销日益高昂,BLIP-2 提出一种通用且高效的预训练策略:直接从现成的冻结图像编码器与冻结大语言模型上引导(bootstrap)视觉-语言预训练。它用轻量级的 Q-Former 桥接模态差异,并将预训练分为两个阶段:

  • 第一阶段:引导视觉-语言表示学习。在冻结的图像编码器之上,通过图文匹配、图文对比等任务让 Q-Former 学会把图片"蒸馏"成一组固定的可学习查询向量;
  • 第二阶段:引导视觉到语言的生成式学习。把冻结的大语言模型接入 Q-Former 输出,以语言建模损失让模型学会把视觉信息翻译成自然语言。

由于 Q-Former 极其轻量(文中描述为 12 层 Transformer 编码器),BLIP-2 能以远少于既有方法的可训练参数量在多种视觉-语言任务上达到当时领先效果。论文中给出的代表性结论是:在 zero-shot VQAv2 上,BLIP-2 比拥有 800 亿参数的 Flamingo 高出 8.7%,同时可训练参数量少 54 倍。此类结论源自论文原文表述,仅作背景引用。

模型整体结构与源码映射

在当前仓库中,BLIP-2 的实现分布在 src/transformers/models/blip_2/ 目录下,核心文件包括:

Blip2Model 的构造函数可以看出整个模型是由四个子模块拼装而成:

  1. Blip2VisionModel:由 config.vision_config 构建的图像编码器;
  2. query_tokens:一个形状为 (1, num_query_tokens, qformer_config.hidden_size) 的可学习参数,初始化全零,充当 Q-Former 的"查询";
  3. Blip2QFormerModel:由 config.qformer_config 构建的 Q-Former;
  4. language_projectionnn.Linear(qformer_config.hidden_size, text_config.hidden_size),把 Q-Former 输出投影到语言模型输入维度;
  5. language_model:根据 config.use_decoder_only_language_modelAutoModelForCausalLM(自回归型,如 OPT)或 AutoModelForSeq2SeqLM(编码器-解码器型,如 T5/Flan-T5)实例化。

三个基础组件(视觉编码器、Q-Former、语言模型)全部是冻结参数,训练时只更新 query_tokens 与 Q-Former 等轻量模块。

推理/训练前向流程(源码视角)

Blip2ForConditionalGeneration.forward 为例,数据流分为清晰的三步:

  1. pixel_values 送入视觉编码器得到图像嵌入 image_embeds
  2. 用全 1 的 image_attention_mask 让可学习 query_tokens 与图像嵌入做 Q-Former 交叉注意力,产出 query_output(Q-Former 全程保持在 fp32,见 _keep_in_fp32_modules = ["query_tokens", "qformer"]);
  3. query_outputlanguage_projection 投影后,通过 masked_scatter 按掩码替换输入 token 中 <image> 占位符对应的嵌入,最后交给语言模型做因果语言建模。

对于自回归语言模型,代码会在标签上手动执行 shift_logits/shift_labels 并计算交叉熵损失,因为必须扣除 query embedding 占据的序列长度(对应实现)。

三个配置类:掌握默认值与组合规则

配置层同样拆分为三个类,便于与三个子模块一一对应,全部位于 configuration_blip_2.py

Blip2VisionConfig(视觉编码器)

对应 model_type = "blip_2_vision_model",默认值即官方 Salesforce/blip2-opt-2.7b checkpoint 的 ViT 风格配置(源码定义):

参数 默认值 含义
hidden_size 1408 隐层维度
intermediate_size 6144 FFN 中间层维度
num_hidden_layers 39 Transformer 层数
num_attention_heads 16 注意力头数
image_size 224 输入图像尺寸
patch_size 14 Patch 尺寸(224/14 = 196 个 patch)
hidden_act "gelu" 激活函数
layer_norm_eps 1e-6 LayerNorm epsilon
attention_dropout 0.0 注意力 dropout
initializer_range 1e-10 初始化范围
qkv_bias True Q/K/V 是否带偏置

Blip2QFormerConfig(Q-Former)

对应 model_type = "blip_2_qformer",Q-Former 本质是一个带交叉注意力的 BERT 风格 Transformer(源码定义):

参数 默认值 含义
vocab_size 30522 词表大小(BERT 同款)
hidden_size 768 隐层维度
num_hidden_layers 12 Q-Former 层数("12 层轻量 Transformer"即由此而来)
num_attention_heads 12 注意力头数
intermediate_size 3072 FFN 中间层维度
hidden_act / hidden_dropout_prob / attention_probs_dropout_prob "gelu" / 0.1 / 0.1 激活与 dropout
max_position_embeddings 512 最大位置编码
layer_norm_eps 1e-12 LayerNorm epsilon
pad_token_id 0 填充 token
cross_attention_frequency 2 每隔多少层插入一次交叉注意力
encoder_hidden_size 1408 交叉注意力输入(图像)维度,构造总配置时会被覆盖为视觉编码器的 hidden_size
use_qformer_text_input False 是否使用 BERT 风格文本嵌入

Blip2Config(总配置)

model_type = "blip-2",通过 sub_configs 声明三个子配置的解析方式(text_configAutoConfigqformer_configvision_config 用各自配置类)。核心字段与默认值(源码定义):

参数 默认值 含义
vision_config / qformer_config / text_config 各自默认类 子模块配置,可为 dict 或配置实例
num_query_tokens 32 送入 Q-Former 的查询 token 数
image_text_hidden_size 256 图文融合层(图文检索投影)的隐层维度
image_token_index None 语言模型词表中 <image> 占位符的 token id
initializer_factor 1.0 初始化缩放因子
initializer_range 0.02 初始化范围

值得注意的 __post_init__ 行为:

  • 未显式给出 text_config 时,会默认使用 OPTConfig(并打印日志);也就是说裸 Blip2Config() 得到的是 "vision + Q-Former + OPT" 组合;
  • 总配置构造时强制 qformer_config.encoder_hidden_size = vision_config.hidden_size,保证 Q-Former 交叉注意力与图像编码器维度匹配;
  • use_decoder_only_language_model 依据文本模型类型是否属于因果 LM 映射自动推导,并据此把 is_encoder_decoder 同步给总配置;
  • attribute_map 提供了 image_token_idimage_token_index 的别名兼容。

从配置实例化模型时,建议像下面这样组合三个子配置(与配置类 docstring 中示例一致):

from transformers import (
    Blip2Config, Blip2QFormerConfig, Blip2VisionConfig,
    OPTConfig, Blip2ForConditionalGeneration,
)

vision_config = Blip2VisionConfig()
qformer_config = Blip2QFormerConfig()
text_config = OPTConfig()

config = Blip2Config(
    vision_config=vision_config,
    qformer_config=qformer_config,
    text_config=text_config,
)

# 使用随机权重初始化一个组合模型
model = Blip2ForConditionalGeneration(config)

Blip2Processor:图像与文本的统一预处理

Blip2Processor 继承自 ProcessorMixin,把图像处理器与分词器合并为一个入口(实现文件),实现位于 processing_blip_2.py

它的核心职责与关键行为如下:

  1. 初始化时向分词器注入 <image> 特殊 token:若分词器没有 image_token 属性,则用 tokenizer.add_tokens(..., special_tokens=True) 添加 AddedToken("<image>", normalized=False, special=True),并把 num_query_tokens 保存在 processor 上。
  2. __call__ 支持仅图像、仅文本、图像 + 文本三种输入;两者都为 None 时抛出 ValueError
  3. 图像占位符位于 BOS 之前:当同时传入图像与文本时,processor 会把 num_query_tokens 个连续的 <image> token 拼接到文本序列最前面(甚至先于 BOS),确保自回归语言模型在首个 token 位置之前就已看到被替换为视觉嵌入的占位符。
  4. 防止占位符被截断:如果用户指定了 max_length,processor 会自动把它减去 num_query_tokens,即 max_length -= num_query_tokens,避免分词器截断掉开头的图像占位符(对应代码)。

典型的处理器加载方式:

from transformers import AutoProcessor

processor = AutoProcessor.from_pretrained("Salesforce/blip2-opt-2.7b")

# 仅图像 -> 图像描述
inputs = processor(images=image, return_tensors="pt")

# 图像 + 问题 -> 视觉问答
inputs = processor(images=image, text="Question: how many cats are there? Answer:", return_tensors="pt")

在模型侧,Blip2ForConditionalGeneration.generate 的实现中也内建了同样的占位符逻辑:当不提供 input_ids 时,会自动构造 num_query_tokensimage_token_index 加上 bos_token_id 的起始序列,并把视觉特征通过 masked_scatter 写回 inputs_embeds 的占位位置(generate 实现)。因此手动调用时也要保证占位 token 数量与 query token 数量严格一致。

推理任务:图像描述、视觉问答与图文检索

条件生成(图像描述 + VQA)

文档给出的使用建议是:BLIP-2 适用于"给定图像 + 可选文本提示"的条件文本生成,推理时推荐使用 generate 方法,并用 Blip2Processor 完成图像预处理与生成 token 的解码。下述完整可运行示例取自 Blip2ForConditionalGeneration 的 docstring

from PIL import Image
import httpx
from io import BytesIO
from transformers import Blip2Processor, Blip2ForConditionalGeneration
import torch

device = "cuda" if torch.cuda.is_available() else "cpu"

processor = Blip2Processor.from_pretrained("Salesforce/blip2-opt-2.7b")
model = Blip2ForConditionalGeneration.from_pretrained(
    "Salesforce/blip2-opt-2.7b",
    device_map={"": 0},
    dtype=torch.float16,
)

url = "http://images.cocodataset.org/val2017/000000039769.jpg"
with httpx.stream("GET", url) as response:
    image = Image.open(BytesIO(response.read()))

# 场景一:图像描述(不提供文本提示)
inputs = processor(images=image, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)  # two cats laying on a couch

# 场景二:视觉问答(提示语即问题)
prompt = "Question: how many cats are there? Answer:"
inputs = processor(images=image, text=prompt, return_tensors="pt").to(device, torch.float16)
generated_ids = model.generate(**inputs)
generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0].strip()
print(generated_text)  # two

解码时建议开启 skip_special_tokens=True,从而丢弃 <image> 等特殊 token,只保留自然语言输出。generate 的额外参数(如 max_new_tokensnum_beams)都可以按 generation/ 目录中通用的生成配置继续透传。

低精度与内存优化

BLIP-2 的完整参数通常高达数 GB(例如配 2.7B OPT 的版本),官方支持 bitsandbytes 的 8-bit 量化推理来大幅降低显存占用。加载方式与上面相同,只需把参数改为 load_in_8bit=Truedevice_map={"": 0} 并配合 dtype=torch.bfloat16(模型 docstring 中即有该示例)。需要注意的是:Q-Former 与 query_tokens 被设计为始终在 fp32 中计算(见 _keep_in_fp32_modules),因此源码在每步 Q-Former 输出后都会做一次 dtype 还原(对应逻辑),这是实现层面的刻意取舍而非 bug。

图文检索与相似度打分

面向检索任务的入口是 Blip2ForImageTextRetrieval(官方示例 checkpoint 为 Salesforce/blip2-itm-vit-g)。它的结构在 Q-Former 基础上增加了 vision_projectiontext_projection(均投影到 image_text_hidden_size,默认 256)以及用于图文匹配的二分类头 itm_head(输出维度 2)。通过 use_image_text_matching_head 参数切换两种模式(docstring 示例):

import torch
from transformers import AutoProcessor, Blip2ForImageTextRetrieval

model = Blip2ForImageTextRetrieval.from_pretrained(
    "Salesforce/blip2-itm-vit-g", dtype=torch.float16
)
processor = AutoProcessor.from_pretrained("Salesforce/blip2-itm-vit-g")

# 图文匹配(Image-Text Matching):返回"图文是否匹配"的二分类概率
inputs = processor(images=image, text="two cats laying on a pink blanket", return_tensors="pt")
itm_out = model(**inputs, use_image_text_matching_head=True)
probs = itm_out.logits_per_image.softmax(dim=1)
print(f"{probs[0][0]:.1%} that image 0 is NOT the text")  # 不匹配概率
print(f"{probs[0][1]:.1%} that image 0 matches the text")  # 匹配概率

# 图文对比(Image-Text Contrastive):在一组候选中检索与图像最相关的文本
texts = ["a photo of a cat", "a photo of a dog"]
inputs = processor(images=image, text=texts, return_tensors="pt")
itc_out = model(**inputs, use_image_text_matching_head=False)
probs = itc_out.logits_per_image.softmax(dim=1)

use_image_text_matching_head=True 时使用 ITM 头输出匹配概率;为 False 时返回 ITC 对比相似度分数,可用于候选文本排序。

特征提取类模型

不参与文本生成、只做表征学习的基座模型是 Blip2Model,它面向检索/重排类下游任务提供三个独立方法(源码位置):

  • get_text_features(...):走 language_model.base_model(自回归型)或 language_model.get_encoder()(编码器-解码器型),返回文本侧 last_hidden_state
  • get_image_features(...):仅过 Blip2VisionModel,返回图像侧特征;
  • get_qformer_features(...):经过视觉编码器 + Q-Former 交叉注意力,返回融合后的查询特征。

此外还有 Blip2TextModelWithProjectionBlip2VisionModelWithProjection,分别给文本与视觉输出加上投影层,便于做对比学习的嵌入对齐。

官方 API 一览与仓库资源

模型文档中列出的公开类与当前仓库实现一一对应,可继续深入阅读:

类别 说明
配置 Blip2Config 总配置(vision + Q-Former + 语言模型)
配置 Blip2VisionConfig 视觉编码器配置
配置 Blip2QFormerConfig Q-Former 配置
处理器 Blip2Processor 图像 + 文本统一预处理(__call__
子模块 Blip2VisionModel 视觉 Transformer(含 forward
子模块 Blip2QFormerModel Q-Former(含 forward
基座 Blip2Model 提供 get_text_features / get_image_features / get_qformer_featuresforward
生成 Blip2ForConditionalGeneration 图文条件生成,重载 generate / get_image_features
检索 Blip2ForImageTextRetrieval ITM/ITC 打分(含 forward
投影 Blip2TextModelWithProjection / Blip2VisionModelWithProjection 带投影层的文本/视觉塔

配套的测试用例位于 tests/models/blip_2/test_modeling_blip_2.pytests/models/blip_2/test_processing_blip_2.py,前者覆盖各模型类的前向与生成行为,后者验证图像占位符拼接、文本编解码等 Processor 细节,是理解预期行为的补充参考。

关于 num_query_tokens 与 <image> 占位符的重要提醒

模型文档特别强调:在 v4.46 之后的版本中,若模型 checkpoint 的 processor 未声明 num_query_tokens、也未在模型词表中扩展 <image> 特殊 token,运行时会触发相关告警。官方强烈建议:

  • 若你持有该 checkpoint,请将 num_query_tokens 属性补充到 processor 中;
  • 若 checkpoint 属于他人,请提交 PR 修复;
  • 该属性可以从模型配置直接获得:model.config.num_query_tokens(默认值为 32,见上文配置表)。

补充这些属性的含义是:模型需要按"每张图片对应的 query token 数量"在文本序列里展开等量的 <image> 占位符(文档给出的经验量级约为每图数百个 token 量级,需以实际 checkpoint 为准)。由于占位符必须与视觉嵌入一一对应,务必保证文本在分词时不被截断,否则占位 token 数量对不上,合并嵌入(masked_scatter)时会直接失败。从 Blip2Processor 源码 可以看到,这一设计正是通过把占位符 token 置为不填充、不截断、不追加特殊 token 的方式在预处理阶段落实的。

结语

BLIP-2 的价值在于把昂贵的端到端多模态预训练问题,转化为"轻量 Q-Former 引导冻结的视觉塔与冻结 LLM"的可扩展方案。在 Transformers 仓库中,它被组织为三个配置类、一个处理器和若干任务模型类,从 Blip2Processor 的图像占位符拼接,到 Blip2ForConditionalGeneration.generate 的嵌入替换,再到 Q-Former 全程 fp32 的实现细节,都可以在本仓库源码中逐一追溯。如果你想动手实验,按本文第二部分的小节顺序分别阅读 configuration_blip_2.pymodeling_blip_2.pyprocessing_blip_2.py,再配合 docstring 中的完整示例跑一遍图像描述与 VQA,即可获得从原理到实践的完整体验。

登录后查看全文
热门项目推荐
相关项目推荐