资讯动态

自底向上注意力实战:Faster R-CNN区域特征驱动VQA与图像字幕

发布时间:2026/10/25 1:27:45 来源:尧图企业网站定制
简介面向图像字幕与视觉问答研究者的自下而上注意力模型训练代码包基于Faster R-CNN与ResNet-101实现多GPU训练利用Visual Genome的对象和属性标注生成显著图像区域特征可作为注意力式字幕与VQA模型中CNN特征的直接替代并给出CIDEr 117.9、BLEU_4 36.9及总体准确率70.3%的参考结果。资源共981个文件约14.27MB以cpp、hpp、prototxt、py、cu、sh和ipynb为主涵盖Caffe自定义层与算子源码、网络与训练配置、数据与实验脚本以及Notebook示例便于梳理从区域特征提取到模型训练的完整链路。已有753人学习。需要留意包内主要提供自下而上注意力/Faster R-CNN训练部分第3.1节字幕模型代码位于另一仓库。适合具备Caffe与深度学习基础、希望复现显著区域特征或将其迁移到图像字幕、VQA任务中的研究者和工程人员用于理解多GPU训练配置、特征生成流程及关键实验参数。1. 从一张被答错的图说起前阵子帮一个做视觉问答的团队看模型测试集上问“左边桌子上的杯子是什么颜色”模型盯着整张图答“桌子”。问题不在分类头而在特征整图 CNN 把局部物体压没了。自底向上注意力工程就是解决这个先用 Faster R-CNN 在 Visual Genome 上跑一遍把图像里显著区域框出来每个框池化成 2048 维特征再把这些区域特征喂给 VQA 和图像字幕模型做注意力。它适合想复现区域级视觉推理、手里有 MSCOCO 和 Visual Genome 数据、愿意碰 Caffe 的开发者也适合已经跑通端到端 VQA、但被整图特征卡住的人。下面按目录、特征链路、模型、跑通和改造拆。2. 工程全貌三块代码和一条区域特征数据流2.1 目录结构特征提取、VQA、Captioning 三条线这套自底向上注意力工程不是单个训练脚本而是把“区域特征生产”和“下游任务消费”拆成了两条腿。我拿到目录后先看三处tools/放特征提取和数据处理脚本vqa/放视觉问答模型与配置captioning/放图像字幕模型。notebooks/里是 Jupyter Notebook适合先跑通单张图再批处理。caffe/是编译好的 Caffe 和自定义层models/放 Faster R-CNN 的 prototxt 与 caffemodeldata/放数据集和生成的区域特征文件。目录/文件作用我一般先看什么caffe/Caffe 源码、自定义 ROI 层、编译脚本Makefile.config和include/caffe/里的自定义层models/Faster R-CNN 的 deploy 配置和权重deploy.prototxt的输入尺寸、ROI 输出层tools/提取区域特征、格式转换、评估脚本extract_features.py的参数解析data/MSCOCO、Visual Genome 标注、HDF5/NPZ 特征特征文件里的boxes、features、num_boxesvqa/VQA 模型定义、训练/推理配置问题编码器、注意力层、分类头captioning/图像字幕模型、解码逻辑attention LSTM 的每个时间步notebooks/单图演示、特征可视化、跑通示例第一个 cell 的路径配置目录结构看起来平铺实际有一条硬约束tools/产出的特征文件字段名必须和vqa/、captioning/里的数据层完全对齐。常见做法是把每张图存成一个 HDF5 或 NPZ里面至少有三个数组boxes是[N, 4]的归一化坐标features是[N, 2048]的区域特征num_boxes是有效框数量。N 不固定一张图可能 10 个框也可能 100 个框所以下游不能写死维度。我见过有人把特征存成[100, 2048]的定长矩阵不足补零结果注意力把零向量也加权进去回答开始飘。这个工程的思路是保留变长在数据层里做 padding 和 mask。看目录时别只盯着模型文件先把data/里一个特征样本读出来打印形状和数值范围后面能少踩很多坑。2.2 技术栈Caffe、Faster R-CNN 和 Visual Genome 的分工这套代码的核心选型是 Caffe Faster R-CNN。Caffe 在今天不算新但它的优势是推理链路固定、prototxt 把网络结构写得死特征提取阶段不容易因为框架版本漂移导致输出维度变化。Faster R-CNN 在这里不是用来做检测比赛而是当“区域提案器 特征池化器”RPN 先给出候选框ROI pooling 把每个框里的卷积特征压成固定长度最后输出区域特征。Visual Genome 负责提供密集的物体和关系标注让模型学到的区域更偏向“物体级”而不是“整图级”。VQA 和 image-captioning 是下游消费者。VQA 走vqa/下的配置问题经过 LSTM 或 GRU 编码图像侧用区域特征做 bottom-up top-down attention然后把问题向量和 attended 图像向量融合接 softmax 分类。图像字幕走captioning/解码器每个时间步对区域特征做 attention再经过 LSTM 生成单词。两个任务共享同一份区域特征所以特征质量直接决定上限。MSCOCO 在这里有两个用途VQA 用mscoco的问答标注和图像captioning 用mscoco-dataset的字幕标注。captioning-images目录里通常放图像列表或软链接不是真实图片全量拷贝。Jupyter Notebook 负责把“单张图 - 特征 - 答案/字幕”这条链路可视化适合调参前先确认数据没接错。提示Caffe 编译是这套工程的第一道玄学。先把caffe/编过再谈模型编译不过时不要急着改模型配置先看Makefile.config里的 CUDA、cuDNN、Python 路径和WITH_PYTHON_LAYER。2.3 一条数据流从图像到区域特征再到答案完整链路可以拆成四步。第一步图像进入 Faster R-CNN 的 deploy 网络短边缩放到固定尺寸长边保持比例。第二步RPN 产生候选框经过 NMS 后保留分数最高的若干框常见配置是每张图 10 到 100 个。第三步ROI pooling 对每个框取特征展平后得到 2048 维向量同时保留框坐标。第四步特征文件被vqa/或captioning/的数据层读取问题或已生成单词与区域特征做注意力最后输出答案或下一个词。这条链路里最容易被忽略的是坐标归一化。Faster R-CNN 输出的框是原图坐标存特征时我一般会除以图像宽高变成 0 到 1 的相对坐标。下游如果要做空间注意力或位置编码归一化坐标比绝对像素坐标稳。另一个点是框的排序按检测分数从高到低排截断到最大框数这样num_boxes才有意义。下面这段配置是我在tools/里常见的参数组织方式不是框架自带文件但结构能直接照着改。# feature_config.py # 区域特征提取的常用参数按数据集实际情况改 FEATURE_CONFIG { model_prototxt: models/faster_rcnn/deploy.prototxt, model_weights: models/faster_rcnn/faster_rcnn.caffemodel, image_list: data/mscoco/val_images.txt, output_dir: data/features/mscoco_val, max_boxes: 100, # 每张图最多保留多少区域 min_score: 0.2, # 低于该分数的框直接丢 nms_threshold: 0.3, # 框去重阈值 feature_dim: 2048, # ROI pooling 后的通道数 batch_size: 1, # Caffe 推理通常单图跑 use_gpu: True, gpu_id: 0 }这段字典里max_boxes和min_score是一对需要一起调的参数。max_boxes太大小图会混进大量低分框注意力被噪声拉偏太小密集场景里目标被截断。min_score调高会减少框数但可能把远处小物体丢掉。我的习惯是先按 0.2 跑一遍统计每张图的有效框数分布再看num_boxes的中位数是否在 30 到 60 之间。nms_threshold控制重叠框合并0.3 偏严格适合物体分得开的场景如果画面里堆叠严重可以放到 0.4 到 0.5但别和min_score同时放松否则框数会爆。feature_dim不要凭感觉写。ROI pooling 的输出维度由 prototxt 里的pool6或roi_pool层决定常见是 2048但如果你换了 backbone这个值可能变。读特征文件时先打印features.shape和配置里的feature_dim对不上就停下来查 prototxt不要硬跑。3. 特征提取链路Faster R-CNN 在 Visual Genome 上到底输出什么3.1 RPN、ROI pooling 和 2048 维区域特征Faster R-CNN 在这套工程里承担两个角色。RPN 负责“哪里可能有物体”它在一个共享卷积特征图上滑窗输出每个位置的 objectness 分数和框回归偏移。ROI pooling 负责“把每个框里的特征压成定长向量”不管框大框小输出都是[C, H, W]展平后接全连接或直接当区域特征。Visual Genome 的作用是让 RPN 和分类头见过大量物体类别和关系区域提案更偏向语义物体而不是随机纹理。实际提取时deploy 网络会去掉训练阶段的损失层保留输入、卷积主干、RPN、ROI pooling 和特征输出层。输入一般是一个 batch但 Caffe 推理在 Python 里常按单图跑。输出通常有两个 blobrois是[N, 5]第一列是 batch index后四列是框坐标pool5或features是[N, 2048, 1, 1]squeeze 后变成[N, 2048]。这里最容易翻车的是 blob 名字。prototxt 里叫pool5脚本里写fc7跑起来直接报找不到 blob。我一般先跑一次net.blobs.keys()把输出 blob 名打印出来再改脚本。区域特征不是越多样越好。一张图 100 个框里可能 60 个是背景或重复物体。Visual Genome 的标注密度高模型会倾向于多提框所以 NMS 和分数阈值要配合。max_boxes100是上限不是目标值真正进模型的应该是num_boxes个有效框。下游做 attention 时mask 掉 padding 位置不然 softmax 会把概率分给补零区域。3.2 Caffe 推理脚本加载模型、预处理、前向传播下面这段 Python 是特征提取脚本的核心骨架基于 Caffe 的 Python 接口。不同环境里caffe模块的导入方式可能不同有的是import caffe有的是把pycaffe加进PYTHONPATH。代码里保留关键注释参数和上面的配置字典对应。# extract_features.py import os import numpy as np import cv2 import caffe def load_net(prototxt, weights, use_gpuTrue, gpu_id0): if use_gpu: caffe.set_mode_gpu() caffe.set_device(gpu_id) else: caffe.set_mode_cpu() return caffe.Net(prototxt, weights, caffe.TEST) def preprocess_image(img_path, target_short_side600, max_long_side1000): img cv2.imread(img_path) if img is None: raise FileNotFoundError(img_path) h, w img.shape[:2] scale target_short_side / min(h, w) if max(h, w) * scale max_long_side: scale max_long_side / max(h, w) new_w, new_h int(round(w * scale)), int(round(h * scale)) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) # Caffe 常见输入是 BGR均值按通道减 mean np.array([102.9801, 115.9465, 122.7717], dtypenp.float32) blob resized.astype(np.float32) - mean blob blob.transpose(2, 0, 1) # HWC - CHW blob blob[np.newaxis, :, :, :] return blob, (h, w), scale def extract_one(net, img_path, max_boxes100, min_score0.2): blob, orig_size, scale preprocess_image(img_path) net.blobs[data].reshape(1, 3, blob.shape[2], blob.shape[3]) net.blobs[data].data[...] blob out net.forward() # 不同 prototxt 的输出 blob 名可能不同先打印再取 rois out.get(rois) feats out.get(pool5) if rois is None or feats is None: raise KeyError(找不到 rois 或 pool5检查 deploy.prototxt 的输出层) rois rois.reshape(-1, 5) feats feats.reshape(-1, feats.shape[1]) scores rois[:, 1] # 有些配置第一列是 batch index分数列按实际调整 order np.argsort(-scores)[:max_boxes] rois rois[order] feats feats[order] keep scores[order] min_score rois, feats rois[keep], feats[keep] # 框坐标还原到原图并归一化 boxes rois[:, 1:5] / scale boxes[:, [0, 2]] / orig_size[1] boxes[:, [1, 3]] / orig_size[0] return boxes.astype(np.float32), feats.astype(np.float32)这段代码有几个参数需要对着自己的 prototxt 改。target_short_side和max_long_side是常见 Faster R-CNN 输入尺度短边 600、长边不超过 1000能覆盖大多数 MSCOCO 图像。mean是 Caffe 常用的 BGR 均值如果你用的模型训练时均值不同这里必须换否则特征整体偏移。net.blobs[data].reshape是因为 Caffe 的输入 blob 形状在 prototxt 里可能写死遇到不同尺寸要先 reshape。rois的列含义要确认。有的实现是[batch_idx, x1, y1, x2, y2, score]有的是[batch_idx, score, x1, y1, x2, y2]。上面代码按rois[:, 1]当分数只适用于后一种。第一次跑先打印rois[0]看第二列是不是 0 到 1 之间的分数不是就改索引。feats的维度也要打印reshape(-1, feats.shape[1])在[N, 2048, 1, 1]上能压成[N, 2048]但如果输出是[N, 2048, 7, 7]直接 reshape 会错需要先做 global average pooling 或取pool5层。3.3 特征落盘HDF5 字段和下游对齐特征提取完不要直接丢给模型先落盘。常见格式是 HDF5一个文件一张图键名固定。下面这段是写文件和读文件的示例重点是字段名和 shape 检查。# feature_io.py import h5py import numpy as np def save_feature(path, boxes, features): # boxes: [N, 4] 归一化坐标features: [N, 2048] assert boxes.ndim 2 and boxes.shape[1] 4 assert features.ndim 2 and features.shape[0] boxes.shape[0] with h5py.File(path, w) as f: f.create_dataset(boxes, databoxes, compressiongzip) f.create_dataset(features, datafeatures, compressiongzip) f.create_dataset(num_boxes, datanp.array([boxes.shape[0]], dtypenp.int32)) def load_feature(path, expected_dim2048): with h5py.File(path, r) as f: boxes f[boxes][:] features f[features][:] num_boxes int(f[num_boxes][0]) assert boxes.shape[0] num_boxes, num_boxes 与 boxes 行数不一致 assert features.shape[1] expected_dim, f特征维度 {features.shape[1]} ! {expected_dim} return boxes, features, num_boxessave_feature里的断言不是摆设。我见过框和特征行数对不上原因是排序后只截了rois没截feats或者keep掩码只作用在一个数组上。load_feature的expected_dim从配置里传进来不要写死。num_boxes单独存一个标量下游数据层读的时候不用先加载整个features就能知道长度做 batch 采样更快。HDF5 的压缩用 gzip 能省磁盘但读取会慢一点。如果训练时数据加载是瓶颈可以改成不压缩或者转成 NPZ。MSCOCO 验证集几万张图每张 30 到 60 个框特征文件体积不小磁盘规划要提前做。captioning-images里的图像列表和特征文件名要能一一对应常见做法是用图像 ID 当文件名比如000000123456.h5不要用原始路径拼路径里的斜杠会把目录打散。4. VQA 与图像字幕模型注意力到底加在哪一层4.1 VQA 模型问题编码加区域注意力VQA 模型在vqa/下输入是问题 token 序列和图像区域特征。问题侧通常先过 embedding再进 LSTM 或 GRU取最后隐藏状态当问题向量。图像侧不是全局池化而是对[N, 2048]的区域特征做 attention用问题向量和每个区域特征算相似度softmax 得到权重加权求和得到 attended 图像向量。最后把问题向量和 attended 向量拼接或相加接全连接分类输出答案词表上的分布。下面这段是 attention 的 NumPy 写法方便看清计算过程。实际 Caffe 配置里会用InnerProduct、Softmax、Eltwise等层拼出来但数学关系一致。# vqa_attention.py import numpy as np def softmax(x, axis-1): x x - np.max(x, axisaxis, keepdimsTrue) e np.exp(x) return e / np.sum(e, axisaxis, keepdimsTrue) def bottom_up_attention(region_feats, region_mask, question_vec, W_q, W_v, w_att): # region_feats: [N, 2048], region_mask: [N] 1 有效 0 padding # question_vec: [D], W_q: [D, H], W_v: [2048, H], w_att: [H] q_proj question_vec W_q # [H] v_proj region_feats W_v # [N, H] scores np.tanh(q_proj[None, :] v_proj) w_att # [N] scores scores * region_mask (1 - region_mask) * (-1e9) weights softmax(scores) # [N] attended weights region_feats # [2048] return attended, weightsregion_mask是必须的。没有 mask 时padding 的零特征也会参与 softmax虽然零向量投影后分数不一定高但框数少的时候会影响归一化。W_q、W_v、w_att是训练参数维度H常见取 512 或 1024。scores先加再 tanh 再点乘是 bottom-up top-down attention 的常见形式也有实现用双线性池化或直接点积效果差异要看数据集。VQA 的输出层是答案分类。MSCOCO 的 VQA 标注里答案词表通常取出现频率最高的若干词低频答案合并成unk。训练时问题长度要截断或补齐图像侧框数变长所以 batch 内要按最大框数 padding。Caffe 的数据层对变长支持不如 PyTorch 灵活常见做法是离线把每个样本的num_boxes存好训练时按固定最大框数采样或截断。别在推理时把num_boxes写成常量不同图框数不一样写死会让注意力权重错位。4.2 图像字幕模型每个时间步都对区域特征加权图像字幕在captioning/下结构是 encoder-decoder。图像侧还是区域特征decoder 是 LSTM每个时间步先用上一时刻隐藏状态和区域特征算 attention得到当前步的视觉上下文再和当前词嵌入一起输入 LSTM预测下一个词。和 VQA 的区别是 VQA 只做一次 attention字幕要做多次每个词都重新看一遍区域。# caption_attention.py import numpy as np def decoder_step(region_feats, region_mask, h_prev, word_emb, W_h, W_v, w_att, W_ctx): # h_prev: [H], word_emb: [E], region_feats: [N, 2048] h_proj h_prev W_h # [A] v_proj region_feats W_v # [N, A] scores np.tanh(h_proj[None, :] v_proj) w_att # [N] scores scores * region_mask (1 - region_mask) * (-1e9) weights softmax(scores) # [N] ctx weights region_feats # [2048] ctx_proj ctx W_ctx # [H] lstm_input np.concatenate([word_emb, ctx_proj], axis-1) return lstm_input, weightsW_h把隐藏状态投到 attention 空间W_v把区域特征投到同一空间w_att是共享向量。ctx_proj把 2048 维视觉上下文压到和词嵌入拼接后适合 LSTM 输入的维度。实际训练时还有sigmoid门控、dropout、teacher forcing代码里省略了。解码阶段用 beam search 还是 greedy取决于延迟要求greedy 快beam search 质量好但要多跑几次前向。字幕模型容易在开始几步生成“a”“the”这类高频词注意力还没聚焦到物体。常见做法是加长度归一化或者在损失里对早期时间步加权。captioning-images目录里的图像和 MSCOCO 字幕标注要对齐图像 ID 和 caption 文件里的 ID 不一致训练会直接对不上。我一般先写个小脚本统计图像数、字幕数、词表大小三个数对不上就先修数据不碰模型。4.3 训练与推理入口脚本和 Notebook 各管一段vqa/和captioning/下通常有train.py、inference.py或对应的 prototxt solver。训练入口吃特征文件列表和标注文件推理入口吃单张图或图像列表。Jupyter Notebook 适合做单图演示加载一张图跑 Faster R-CNN 提特征把框画在图上再把问题或字幕结果打印出来。下面这段 bash 是常见的启动方式路径按自己的目录改。# 启动 notebook先跑单图链路 cd /path/to/project export PYTHONPATH$PWD/caffe/python:$PYTHONPATH jupyter notebook --ip0.0.0.0 --port8888 --no-browser # 批量提特征 python tools/extract_features.py \ --config tools/feature_config.py \ --image_list data/mscoco/val_images.txt \ --output_dir data/features/mscoco_val # VQA 推理 python vqa/inference.py \ --features data/features/mscoco_val \ --questions data/vqa/val_questions.json \ --model models/vqa/vqa_model.caffemodelPYTHONPATH指向caffe/python是为了让import caffe能找到编译好的 pycaffe。--no-browser在远程机器上常用本地跑可以去掉。批量提特征前先拿 10 张图试跑确认输出文件数和图像列表行数一致再全量跑。VQA 推理的--questions文件格式要和数据层读的一致常见是 JSON 里每个问题带image_id和question字段少一个字段就会在 batch 组装时报 key error。5. 跑通与避坑从 Caffe 编译到 Notebook 出结果5.1 环境准备Caffe 先编过再谈模型这套工程对 Caffe 的依赖是硬性的特征提取和下游模型都在 Caffe 图里。环境准备顺序我一般这样走先确认 GPU 驱动和 CUDA 可用再编 Caffe再装 Python 依赖最后跑 Notebook。顺序反了后面报错会混在一起。下面命令里的版本号按机器实际情况改不要照抄。# 检查 GPU 和 CUDA nvidia-smi nvcc --version # 编译 Caffe先改 Makefile.config cd caffe cp Makefile.config.example Makefile.config # 打开 WITH_PYTHON_LAYER1取消 USE_CUDNN 注释填好 CUDA_DIR make -j8 make pycaffe # Python 依赖按环境实际补 pip install numpy opencv-python h5py jupyter protobufmake -j8的并行数按 CPU 核数改内存小的话降到-j4不然编译会因内存不足被杀。make pycaffe之后要确认caffe/python/caffe能导入python -c import caffe不报错再往下。protobuf版本是常见冲突点Caffe 编译时用的 protobuf 和 Python 环境里的版本不一致会出现Descriptors cannot be created directly之类的报错。解决方法是让 Python 的 protobuf 版本和编译时一致或者用虚拟环境隔离。5.2 数据和模型准备MSCOCO、Visual Genome 和特征文件数据侧要准备三样图像、标注、特征文件。图像用 MSCOCO 的train2014、val2014或train2017按代码里的路径约定放。VQA 标注用vqa/下的问题答案文件字幕标注用captioning/下的 caption 文件。Visual Genome 主要用在 Faster R-CNN 预训练权重里推理阶段不一定需要完整标注但如果你想微调检测器就要准备 Visual Genome 的物体和关系标注。特征文件不是必须从零跑。如果只想跑通 VQA 或 captioning可以先下载现成的区域特征确认字段名和维度对得上再决定要不要自己提。自己提的话先跑val子集别一上来全量。MSCOCO 训练集十几万张图单卡提特征要很久中间断一次没有断点续跑会很难受。常见做法是按图像列表分片每 1000 张存一个目录跑完再合并文件列表。注意特征文件和图像列表的顺序要一致。下游数据层如果按文件名排序读而你的列表是乱序图像和特征会错位模型表现会莫名其妙地差。5.3 常见问题排查五个我踩过的坑现象一import caffe报No module named caffe。原因通常是PYTHONPATH没包含caffe/python或者make pycaffe没成功。解决先ls caffe/python/caffe看有没有__init__.py没有就回编译目录看make pycaffe的输出有就export PYTHONPATH$PWD/caffe/python:$PYTHONPATH再在同一个 shell 里启动 Python 或 Notebook。别在 Notebook 里用sys.path.append临时加内核重启后就丢了。现象二推理时提示 blob 不存在比如找不到rois或pool5。原因是 deploy.prototxt 的输出层名字和脚本里写的不一致或者模型文件是训练版 prototxt输出 blob 被损失层吃掉了。解决用net.blobs.keys()和net.outputs打印实际名字改脚本里的out.get(...)。如果是训练版 prototxt换 deploy 版或者手动在末尾加特征输出层。现象三特征维度对不上features.shape[1]不是 2048。原因是 backbone 或 ROI pooling 配置改了输出通道数变化也可能是取错了层取到了fc6而不是pool5。解决在extract_one里打印feats.shape和feature_config.py里的feature_dim对比。改配置时不要只改一个地方vqa/和captioning/的数据层里如果写死了维度也要一起改。现象四GPU 显存溢出batch size 降到 1 还是爆。原因是图像分辨率太大或者max_boxes太大ROI pooling 后的特征占显存。解决先降max_long_side比如从 1000 降到 800再把max_boxes从 100 降到 50 试。Caffe 的显存释放不如动态图框架及时反复加载模型时可以在两次推理之间重建Net或者把batch_size设成 1 并限制每张图的框数。现象五VQA 答案全是高频词字幕全是“a picture of”。原因可能是特征和问题没对齐也可能是训练时答案词表不平衡。解决先抽 10 个样本把区域框画在图上确认框落在物体上而不是背景再检查问题 token 和图像 ID 是否错位最后看分类损失如果损失降得很快但验证集不涨大概率是过拟合到高频答案需要重采样或对低频答案加权。这个坑我翻过一次查了两天才发现是特征文件名和图像 ID 差了一个前导零。5.4 跑通标准单图链路能复现就算过跑通不是看训练 loss 降没降而是看单图链路能不能稳定复现。我的验收标准是同一张图、同一个问题连续跑三次区域框数量一致答案一致注意力权重最大的框落在正确物体上。Notebook 里可以把框和权重画出来肉眼确认。批量跑的时候再抽 20 张图做同样检查没问题再上全量。Caffe 的推理结果对输入尺寸和均值很敏感换一台机器跑出不同答案先查预处理不要先怀疑模型。preprocess_image里的 resize 插值方式、均值顺序、BGR/RGB 转换任何一项和训练时不一致特征都会偏。血泪经验是把预处理后的图存一张下来和训练时的输入图对比肉眼看不出来就用数值 diff。6. 二次开发改注意力之前先把特征契约固定住二次开发最容易犯的错是先改模型结构再回头发现特征文件字段对不上。我的做法是先固定“特征契约”boxes永远是[N, 4]归一化坐标features永远是[N, D]num_boxes永远是标量D 写在配置里。契约不变下游随便换注意力实现。想换数据集只改image_list和标注路径想换 backbone先重新提一小批特征确认 D 变了之后同步改 VQA 和 captioning 的输入层。改造目标先改哪里验证方法换数据集tools/feature_config.py的image_list、output_dir抽 10 张图跑特征检查框数和图像内容对得上换注意力vqa/或captioning/的 attention 层固定随机种子对比改动前后同一张图的权重分布导出特征给别的框架feature_io.py的save_feature用 NumPy 读回断言 shape 和 dtype调推理速度max_boxes、max_long_side、batch_size记录单图耗时和显存峰值别只看吞吐如果要把特征导给 PyTorch 模型我一般会多存一个image_id和image_size方便后面做坐标还原。image_size存原图高宽boxes存归一化坐标还原时乘回去就行。别在特征文件里存原始像素坐标换分辨率后就废了。从那以后我每次载入特征文件都先跑一遍形状断言再进模型改 attention 之前先把同一张图的权重存成 npy改完对比数值不对就回滚。这套习惯救过我几次尤其是 Caffe 和 Python 版本混用的时候。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑