首页
/ 基于 GLDv2 数据集的 DELF/DELG 图像特征模型完整训练指南

基于 GLDv2 数据集的 DELF/DELG 图像特征模型完整训练指南

2026-09-06 18:24:47作者:侯霆垣

导读

本文完整讲解 TensorFlow 官方模型仓库中 DELF(Deep Local Features)/DELG(Deep Local and Global Features)图像检索模型的训练流程,涵盖环境安装、GLDv2 数据集下载、TFRecord 数据准备、分布式训练、模型导出与端到端验证的每一个环节。读完本文,你将能够从零训练出用于图像检索的局部特征(DELF)或"局部 + 全局"特征(DELG)模型,并把它接入本仓库的特征提取与图像匹配管线完成实战验证。

本文全部操作与结论均以 training/README.md 为骨架,并对照 training 目录下的真实脚本与模型实现展开,所有命令均可直接复制执行。

概览:完整训练流水线

训练一个 DELF/DELG 模型需要依次完成以下四个阶段:

  1. 安装 DELF Python 库;
  2. 下载 Google Landmarks Dataset v2(GLDv2)原始图片;
  3. 将原始图片打包为 TFRecord 训练/验证数据;
  4. 启动分布式训练。

接下来各小节将逐步展开说明。

前置条件

训练代码位于本仓库 models/research/delf/delf/python/training 目录。克隆 TensorFlow Models 仓库并进入该目录即可:

git clone https://github.com/tensorflow/models.git
cd models/research/delf/delf/python/training

注意:本文所有命令默认在该 training 目录下执行,下文引用的 train.pybuild_image_dataset.pydownload_dataset.sh 等都位于此目录(即 research/delf/delf/python/training)。

安装 DELF 库

请严格按照 INSTALL_INSTRUCTIONS.md 完成 DELF Python 库的安装。该文档会指导你完成依赖安装(TensorFlow、TensorFlow Probability、absl、numpy、pandas、matplotlib 等)、delf 包的路径配置以及本地算子编译等步骤。训练/数据准备脚本还会用到两个关键依赖:

  • TensorFlow Probability(tfp):训练脚本中用于计算注意力分数的百分位统计,见 train.py 中的 _attention_summaries
  • pandasbuild_image_dataset.py 读取 GLDv2 的 CSV 元数据时依赖它。

此外仓库还提供了一键安装脚本 install_delf.sh,可用于自动完成环境准备。

下载 GLDv2 训练数据

GLDv2 的图片被划分为 TRAIN、INDEX、TEST 三个子数据集。每个子数据集的图片以 *.tar 压缩包组织,并由 *.csv 文件记录对应的训练元数据与版权信息。各子数据集对应的 tar 文件数量如下:

  • TRAIN:500 个文件;
  • INDEX:100 个文件;
  • TEST:20 个文件。

使用脚本下载

运行 download_dataset.sh 即可下载:

bash download_dataset.sh 500 100 20

脚本按顺序接收三个参数:

参数 含义 上限
第 1 个 从 TRAIN 数据集下载的图片文件数量 500
第 2 个 从 INDEX 数据集下载的图片文件数量 100
第 3 个 从 TEST 数据集下载的图片文件数量 20

下载目录结构

图片会按如下结构存放:

gldv2_dataset/
├── train/   # TRAIN 数据集的原始图片
├── index/   # INDEX 数据集的原始图片
└── test/    # TEST 数据集的原始图片

每个子文件夹(train/index/test/)中都会包含:

  • 下载好的 *.tar 图片压缩包;
  • 对应的 MD5 校验文件 *.txt
  • 解压后的图片内容(图片按文件名第 1、2、3 个字符组织到多级子目录中);
  • 与已下载图片对应的训练/版权元数据 CSV 文件。

download_dataset.sh 源码可以看到脚本的具体行为:

  • 所有文件从 https://s3.amazonaws.com/google-landmark(图片 tar 包)、.../md5sum(校验和)、.../metadata.../ground_truth(CSV 元数据)拉取;
  • 每个 download_image_file 会下载 images_XXX.tar(编号为 3 位数字)及其 md5 文件,并用 md5sum(Linux)/md5 -r(macOS)校验通过后才解压;
  • 脚本以 6 个并发进程批量下载并解压,中途校验失败会直接 exit 1 终止,避免留下损坏数据。

存储与带宽提示

由于 GLDv2 体量巨大,完整下载可能耗时长达 12 小时、占用近 1TB 磁盘空间。为节省带宽与磁盘,建议只下载训练必需的 TRAIN 子集——INDEX 与 TEST 合计约 95GB,可以全部跳过。进一步地,下载并解压后可删除 *.tar 压缩包以释放空间。

准备训练数据

数据准备阶段的目标是把 GLDv2 原始图片转换为 TFRecord 文件,并划分出 TRAIN 与 VALIDATION 两个 split。注意:训练集只保留 GLDv2 数据集中的 clean 子集——clean 子集过滤掉了识别/检索歧义与标注噪声过大的图片,其详细定义见引入 GLDv2 的 CVPR'20 论文。

运行 build_image_dataset.py

生成 clean 子集的 TRAIN/VALIDATION TFRecord 文件,需运行 build_image_dataset.py

python3 build_image_dataset.py \
  --train_csv_path=gldv2_dataset/train/train.csv \
  --train_clean_csv_path=gldv2_dataset/train/train_clean.csv \
  --train_directory=gldv2_dataset/train/*/*/*/ \
  --output_directory=gldv2_dataset/tfrecord/ \
  --num_shards=128 \
  --generate_train_validation_splits \
  --validation_split_size=0.2

参数含义如下(源自脚本中的 flags 定义,详见 build_image_dataset.py 源码):

参数 说明
--train_csv_path 训练集全量元数据 CSV(train.csv),包含 idlandmark_id
--train_clean_csv_path clean 子集 CSV(train_clean.csv),表头为 landmark_id;images;提供后将只保留本文件列出的图片
--train_directory 解压后 TRAIN 图片所在目录,通配符覆盖按文件名分层的多级子目录
--output_directory TFRecord 输出目录
--num_shards 输出分片数,默认 128
--generate_train_validation_splits 是否将训练集划分为 TRAIN 与 VALIDATION 两个 split(bool)
--validation_split_size VALIDATION split 占训练集的比例,默认 0.2,取值须在 (0, 1) 开区间内
--seed 划分 split 时洗牌用的随机种子,便于结果可复现

生成的 TFRecord 文件前缀如下:

  • TRAIN split:train-*
  • VALIDATION split:validation-*

clean 子集处理与重标注

从源码可以看清 clean 子集的转换逻辑(_get_clean_train_image_files_and_labels):

  1. 读取 train_clean.csv,该文件按 landmark_id 分组列出图片 id(images 列以空格分隔);
  2. train_directory 下实际存在的图片做交集匹配,跳过 CSV 中存在但未下载的图片;
  3. 将原始 landmark id 重映射为从 0 开始的连续标签relabeling = {label: index ...}),因为分类头 num_classes 需要的是密集整数标签;
  4. 重映射规则会被写入输出目录下的 relabeling.csv(表头 new_label,old_label),供后续评估对照。

在未启用 clean 过滤的情况下(不传 --train_clean_csv_path),脚本走 _get_all_image_files_and_labels 直接读取 train.csv 中每个图片 id 的 landmark_id

Train/Validation 划分的均衡策略

启用 --generate_train_validation_splits 后,脚本按 label 分层划分_build_train_and_validation_splits):先把每个 landmark 下的图片各自洗牌,再按 validation_split_size 比例切分出验证子集,最后把所有子集拼接并再次全局洗牌。这样能保证验证集与训练集在类别构成上尽量一致,避免小类别全部落入某一侧。

TEST split 的生成(可选)

同一脚本也可为训练后评估生成 TEST split 的 TFRecord,只需追加两个参数:

--test_csv_path=gldv2_dataset/train/test.csv \
--test_directory=gldv2_dataset/test/*/*/*/ \

此时 TEST split 的 TFRecord 文件名符合 test-* 模式。源码中 TEST split 的图片不包含 image/class/label 字段(测试集无公开标签),_convert_to_example 仅在 label 非空时才写入该特征。

TFRecord 内部结构

每个 TFRecord 记录是一个序列化的 Example proto,包含的字段(来自 _convert_to_example,即 build_image_dataset.py):

  • image/heightimage/width:图片像素尺寸;
  • image/colorspace:恒为 RGB
  • image/channels:恒为 3;
  • image/format:恒为 JPEG
  • image/id:图片唯一 id(如 97c0a12e07ae8dd5);
  • image/encoded:JPEG 编码的图片字节串;
  • image/class/label:整数地标标签,仅训练数据包含。

耗时与空间提示

同样由于 GLDv2 规模庞大,TFRecord 生成阶段最长可能耗时 12 小时、占用约 500GB 磁盘空间。建议预留足够的临时磁盘,并可在生成完成后删除原始图片以回收空间。

运行训练

为了让模型更快收敛,可以先用 ImageNet 预训练权重初始化 ResNet 主干。ImageNet checkpoint 从 http://storage.googleapis.com/delf/resnet50_imagenet_weights.tar.gz 获取,在 Linux 上解压:

curl -Os http://storage.googleapis.com/delf/resnet50_imagenet_weights.tar.gz
tar -xzvf resnet50_imagenet_weights.tar.gz

训练入口脚本的总体设计

训练主脚本为 train.py。从源码看,其核心设计是:

  • 使用 tf.distribute.MirroredStrategy 支持多 GPU 同步训练(脚本 docstring 明确说明使用分类损失 + MirroredStrategy);
  • 模型为 ResNet50 主干 + 注意力模块(Attention)+ 可选自动编码器,按 GLD 数据集版本确定分类头类别数(googlelandmarks.pynum_classes = {'gld_v1': 14951, 'gld_v2': 203094, 'gld_v2_clean': 81313});
  • 数据集由 googlelandmarks.pyCreateDataset 构建:读 TFRecord → 解码 JPEG → (像素 - 128) / 128 归一化 → 训练时做 ImageNet 风格随机裁剪(sample_distorted_bounding_box,面积占原图 0.08~1.0、宽高比 3/4~4/3)并 resize 到 321×321;
  • 学习率采用线性衰减调度:lr = initial_lr * (1 - global_step / max_iters)
  • 优化器为带 momentum 0.9 的 SGD,全局梯度范数裁剪到 10.0;
  • 每 1000 步做一次验证与 checkpoint 保存(CheckpointManager,保留最近 10 份、每 3 小时至少一份),并把 loss/accuracy/attention 统计写入 TensorBoard。

训练 DELF 局部特征模型

假设 TFRecord 已生成到 gldv2_dataset/tfrecord/ 目录,执行如下命令训练并在 gldv2_training 目录输出结果:

python3 train.py \
  --train_file_pattern=gldv2_dataset/tfrecord/train* \
  --validation_file_pattern=gldv2_dataset/tfrecord/validation* \
  --imagenet_checkpoint=resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 \
  --dataset_version=gld_v2_clean \
  --logdir=gldv2_training/

注意:--use_autoencoder 默认值为 True,因此默认将启用自动编码器联合训练。自动编码器把 block3 特征图压缩到低维(默认 128 维 autoencoder_dimensions)再扩展回去,其重建损失(默认权重 10.0 reconstruction_loss_weight)参与总损失,具体逻辑见 train.pytrain_step

训练 DELG 局部 + 全局特征模型

如需训练 DELG 论文中改进的全局特征头,在启动命令中追加 --delg_global_features 参数:

python3 train.py \
  --train_file_pattern=gldv2_dataset/tfrecord/train* \
  --validation_file_pattern=gldv2_dataset/tfrecord/validation* \
  --imagenet_checkpoint=resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 \
  --dataset_version=gld_v2_clean \
  --logdir=gldv2_training/ \
  --delg_global_features

delg_global_features 置真后,train.pycreate_model 会实例化 delg_model.py 中的 Delg 模型而非纯 DELF。从 delg_model.py 源码可见其与 DELF 的差异:

  • GeM Pooling:全局特征采用 Generalized Mean pooling(--delg_gem_power,默认 3.0),替代 DELF 的简单平均池化;
  • FC 白化/Embedding 层:GeM 输出后接一层 2048 维的全连接白化层(--delg_embedding_layer_dim,默认 2048);
  • ArcFace 余弦分类器:backbone 的全局分类头不再用普通 Dense,而是对 L2 归一化后的 embedding 与归一化类别权重计算余弦相似度,再乘可学习/固定的缩放因子(--delg_scale_factor_init,默认 45.25 ≈ sqrt(2048)),并在训练时对正确类别施加 ArcFace margin(--delg_arcface_margin,默认 0.1),见 cosine_classifier_logitsapply_arcface_margin
  • 在训练时 backbone 的 block3 输出会被 tf.stop_gradient 截断梯度(防止全局分支梯度干扰局部注意力分支),这一处理在 delf_model.pyglobal_and_local_forward_pass 中有明确注释,其依据即 DELG 论文。

训练参数速查

下表汇总了 train.py 中定义的、除数据路径外值得关注的核心 flags(默认值均取自源码):

Flag 默认值 说明
--debug False 调试模式:单机 eager 执行,batch_size=4、max_iters=100、每步保存
--logdir /tmp/delf TensorBoard 日志与 checkpoint 输出目录
--dataset_version gld_v1 枚举 gld_v1 / gld_v2 / gld_v2_clean,决定类别数
--seed 0 训练数据集洗牌种子
--initial_lr 0.01 初始学习率(线性衰减)
--batch_size 32 全局 batch size
--max_iters 500000 最大迭代步数
--block3_strides True 是否在 block3 输出增加 stride(影响特征图分辨率)
--use_augmentation True 是否使用 ImageNet 风格数据增强
--imagenet_checkpoint None ResNet 主干 ImageNet 预训练权重;None 则不加载
--attention_loss_weight 1.0 注意力损失在总损失中的权重
--delg_global_features False 是否训练 DELG(全局 + 局部)模型
--image_size 321 输入图片边长
--use_autoencoder True 是否联合训练自动编码器做降维
--reconstruction_loss_weight 10.0 自动编码器重建损失权重
--autoencoder_dimensions 128 自动编码器压缩后的通道数
--local_feature_map_channels 1024 局部特征来源层(block3)的通道数

损失函数方面,源码显示总损失 = desc_loss + attention_loss_weight * attn_loss + reconstruction_loss_weight * reconstruction_loss,其中全局分支的 desc_loss 使用 SparseCategoricalCrossentropy(from_logits=True)(DELG 模式为 ArcFace 余弦 logits),注意力分支使用独立的 Dense(num_classes) 分类器计算。

超参数建议

为提升收敛速度,以下超参数组合已在一组测试验证过的硬件环境中取得良好效果(其余 flags 保持默认值):

  • 8 张 Tesla P100 GPU:--batch_size=256 --initial_lr=0.01
  • 4 张 Tesla P100 GPU:--batch_size=128 --initial_lr=0.005

即 batch size 减半时初始学习率也应减半,以维持等效的梯度统计特性。

导出训练好的模型

假设训练产物(TensorFlow checkpoint)位于 gldv2_training 目录,训练脚本会同时保存两种产物:CheckpointManager 管理的 delf_tf2-ckpt 检查点,以及可直接 load_weights 的权重文件 gldv2_training/delf_weightssave_format='tf')。导出命令中的 --ckpt_path=gldv2_training/delf_weights 指向后者。

仅 DELF 局部特征模型

适用于只需要局部特征模型的场景:

python3 model/export_local_model.py \
  --ckpt_path=gldv2_training/delf_weights \
  --export_path=gldv2_model_local

仅 DELG 全局特征模型

适用于只需要全局特征模型的场景:

python3 model/export_global_model.py \
  --ckpt_path=gldv2_training/delf_weights \
  --export_path=gldv2_model_global \
  --delg_global_features

DELG 局部 + 全局联合模型

适用于需要同时提取局部与全局特征的场景:

python3 model/export_local_and_global_model.py \
  --ckpt_path=gldv2_training/delf_weights \
  --export_path=gldv2_model_local_and_global \
  --delg_global_features

export_global_model.py 的实现可以看到导出的本质:以 tf.Module 包装重建的模型结构(delg_model.Delgdelf_model.Delf),调用 load_weights(checkpoint_path) 载入权重后,用 tf.saved_model.save 导出,serving_default 签名提供 ExtractFeatures/ExtractFeaturesFixedScales 两个前向函数。导出过程关键点:

  • 导出时 block3_strides=False,与训练配置解耦;
  • 模型输入为 uint8[None, None, 3] 图像,可在服务端灵活处理任意分辨率,并通过 input_scales 支持多尺度金字塔。

Kaggle 兼容的全局特征模型(特殊格式)

如需导出符合 2020 Landmark Retrieval 挑战赛要求的全局特征模型,可执行:

python3 model/export_global_model.py \
  --ckpt_path=gldv2_training/delf_weights \
  --export_path=gldv2_model_global \
  --input_scales_list=0.70710677,1.0,1.4142135 \
  --multi_scale_pool_type=sum \
  --normalize_global_descriptor

注意:该命令对参赛很有帮助,但它导出的是 Kaggle 所需格式,与本代码库内部使用的格式不同——按此方式导出的模型无法直接用于 DELG 指令文档 中的命令。若要导出与代码库兼容的格式,请使用上文"仅 DELG 全局特征模型"一节中的命令。

此处三个参数的作用(依据 export_global_model.py 源码):

  • --input_scales_list:把多尺度输入缩放列表(1/√2 ≈ 0.7071、1.0、√2 ≈ 1.4142)硬编码进模型,导出 ExtractFeaturesFixedScales 签名,无需运行时动态传入尺度;
  • --multi_scale_pool_type:取值 None/average/sumNone 时每个尺度分别输出 global_descriptors;设为 sum(或 average)时对各尺度描述子池化,输出单一 1D 向量 global_descriptor
  • --normalize_global_descriptor:是否对最终全局描述子做 L2 归一化。

测试训练好的模型

测试训练好的局部特征模型

导出模型后,可用同一地标的两张图片做"提取特征 → 匹配验证"的冒烟测试,确认模型确实学到了可判别的地标特征。

第一步:下载 Oxford buildings 数据集

mkdir data && cd data
wget http://www.robots.ox.ac.uk/~vgg/data/oxbuildings/oxbuild_images.tgz
mkdir oxford5k_images oxford5k_features
tar -xvzf oxbuild_images.tgz -C oxford5k_images/
cd ../
echo data/oxford5k_images/hertford_000056.jpg >> list_images.txt
echo data/oxford5k_images/oxford_000317.jpg >> list_images.txt

第二步:改写 DELF 提取配置

复制 delf_config_example.pbtxt 作为提取配置的起点,然后做三处修改:

  1. model_path 指向导出模型所在目录(本例为 gldv2_model_local);
  2. 在根层级新增 is_tf2_exported 属性并置为 true
  3. delf_local_config 内的 use_pca 置为 false

修改后的文件应类似:

model_path: "gldv2_model_local"
image_scales: .25
image_scales: .3536
image_scales: .5
image_scales: .7071
image_scales: 1.0
image_scales: 1.4142
image_scales: 2.0
is_tf2_exported: true
delf_local_config {
  use_pca: false
  max_feature_num: 1000
  score_threshold: 100.0
}

配置项语义对照源码(delf_config.proto 与原始 delf_config_example.pbtxt):

  • model_path:导出模型(SavedModel)目录;
  • image_scales:图像金字塔多尺度列表,逐尺度提取特征可提升尺度鲁棒性;
  • is_tf2_exported: true:声明加载的是 TF2 导出模型;
  • delf_local_config.max_feature_num:每张图最多保留的特征点数量(默认 1000);
  • delf_local_config.score_threshold:特征点注意力分数阈值(默认 100.0),低于阈值的点被过滤;
  • use_pca: false:新训练的模型不做 PCA 降维,故需关闭该开关。

第三步:提取两幅图的 DELF 特征

python3 ../examples/extract_features.py \
  --config_path delf_config_example.pbtxt \
  --list_images_path list_images.txt \
  --output_dir data/oxford5k_features

第四步:特征匹配

python3 ../examples/match_images.py \
  --image_1_path data/oxford5k_images/hertford_000056.jpg \
  --image_2_path data/oxford5k_images/oxford_000317.jpg \
  --features_1_path data/oxford5k_features/hertford_000056.delf \
  --features_2_path data/oxford5k_features/oxford_000317.delf \
  --output_image matched_images.png

生成的 matched_images.png 效果应与下图类似:两幅展示同一地标(牛津 Hertford 学院)的图像,通过训练后模型提取的局部特征找到匹配点对并用连线标出,从而直观验证特征的正确性。

DELF 特征匹配演示输出图

测试训练好的全局(或全局 + 局部)特征模型

请遵循 DELG 指令文档 完成全局特征模型的检索测试,唯一需要修改的是:在特征提取时传入指向新训练模型delf_config_path。按照 delf_config.proto 中的定义,你需要根据实际使用哪种特征模态正确设置 use_local_featuresuse_global_features;同时记得将 is_tf2_exported 设为 true

关于纯全局特征(GeM + DELG 头)的训练与评估,仓库还提供了一套不依赖 GLDv2 分类监督的备选流程,可参考 global_features/README.md,其中覆盖了 Siamese/Triplet 式训练、多分辨率评估、Revisited Oxford/Paris 指标评测等进阶内容。

补充说明

  • DELF 与 DELG 的论文依据:DELF 模型对应论文 Large-Scale Image Retrieval with Attentive Deep Local Features,核心思路是用注意力对局部特征加权、丢弃背景区域;DELG 模型对应论文 Unifying Deep Local and Global Features for Image Search,在同一网络中以 GeM Pooling + 白化层 + ArcFace 训练全局头、以注意力机制训练局部头,从而统一两类特征。两篇论文的 arxiv 编号分别见 delf_model.pydelg_model.py 的 docstring。
  • 目录导航:本目录下的其他说明文档包括数据下载与转换细节 datasets/google_landmarks_dataset/README.md、训练后的实际检索流程 DELG_INSTRUCTIONS.md、以及特征提取/匹配最小示例脚本 examples/extract_features.pyexamples/match_images.py
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388