Transformers 中 BLIP-2 模型全解析:架构、配置与图生文推理实践
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/ 目录下,核心文件包括:
- configuration_blip_2.py:三个配置类;
- modeling_blip_2.py:PyTorch 模型实现;
- processing_blip_2.py:
Blip2Processor; - convert_blip_2_original_to_pytorch.py:官方权重转换脚本。
从 Blip2Model 的构造函数可以看出整个模型是由四个子模块拼装而成:
Blip2VisionModel:由config.vision_config构建的图像编码器;query_tokens:一个形状为(1, num_query_tokens, qformer_config.hidden_size)的可学习参数,初始化全零,充当 Q-Former 的"查询";Blip2QFormerModel:由config.qformer_config构建的 Q-Former;language_projection:nn.Linear(qformer_config.hidden_size, text_config.hidden_size),把 Q-Former 输出投影到语言模型输入维度;language_model:根据config.use_decoder_only_language_model用AutoModelForCausalLM(自回归型,如 OPT)或AutoModelForSeq2SeqLM(编码器-解码器型,如 T5/Flan-T5)实例化。
三个基础组件(视觉编码器、Q-Former、语言模型)全部是冻结参数,训练时只更新 query_tokens 与 Q-Former 等轻量模块。
推理/训练前向流程(源码视角)
以 Blip2ForConditionalGeneration.forward 为例,数据流分为清晰的三步:
pixel_values送入视觉编码器得到图像嵌入image_embeds;- 用全 1 的
image_attention_mask让可学习query_tokens与图像嵌入做 Q-Former 交叉注意力,产出query_output(Q-Former 全程保持在 fp32,见_keep_in_fp32_modules = ["query_tokens", "qformer"]); query_output经language_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_config 用 AutoConfig,qformer_config 与 vision_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_id→image_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。
它的核心职责与关键行为如下:
- 初始化时向分词器注入
<image>特殊 token:若分词器没有image_token属性,则用tokenizer.add_tokens(..., special_tokens=True)添加AddedToken("<image>", normalized=False, special=True),并把num_query_tokens保存在 processor 上。 __call__支持仅图像、仅文本、图像 + 文本三种输入;两者都为None时抛出ValueError。- 图像占位符位于 BOS 之前:当同时传入图像与文本时,processor 会把
num_query_tokens个连续的<image>token 拼接到文本序列最前面(甚至先于 BOS),确保自回归语言模型在首个 token 位置之前就已看到被替换为视觉嵌入的占位符。 - 防止占位符被截断:如果用户指定了
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_tokens 个 image_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_tokens、num_beams)都可以按 generation/ 目录中通用的生成配置继续透传。
低精度与内存优化
BLIP-2 的完整参数通常高达数 GB(例如配 2.7B OPT 的版本),官方支持 bitsandbytes 的 8-bit 量化推理来大幅降低显存占用。加载方式与上面相同,只需把参数改为 load_in_8bit=True、device_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_projection、text_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 交叉注意力,返回融合后的查询特征。
此外还有 Blip2TextModelWithProjection 与 Blip2VisionModelWithProjection,分别给文本与视觉输出加上投影层,便于做对比学习的嵌入对齐。
官方 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_features 与 forward |
| 生成 | Blip2ForConditionalGeneration | 图文条件生成,重载 generate / get_image_features |
| 检索 | Blip2ForImageTextRetrieval | ITM/ITC 打分(含 forward) |
| 投影 | Blip2TextModelWithProjection / Blip2VisionModelWithProjection |
带投影层的文本/视觉塔 |
配套的测试用例位于 tests/models/blip_2/test_modeling_blip_2.py 与 tests/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.py、modeling_blip_2.py 与 processing_blip_2.py,再配合 docstring 中的完整示例跑一遍图像描述与 VQA,即可获得从原理到实践的完整体验。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00