查看全课大纲(第 52 / 93 节)
  1. 1.计算机视觉概述
  2. 2.计算机视觉应用场景
  3. 3.困难与挑战
  4. 4.开源库介绍
  5. 5.图像显示与存储
  6. 6.CLAHE
  7. 7.腐蚀膨胀与开闭运算
  8. 8.滤波与边缘填充
  9. 9.各种滤波
  10. 10.图像金字塔
  11. 11.傅里叶变换
  12. 12.短时傅里叶变换与小波变换
  13. 13.颜色特征与颜色直方图
  14. 14.几何特征_边缘
  15. 15.几何特征-角点与斑点
  16. 16.局部特征-SIFT特征描述子
  17. 17.局部特征-SURF特征描述子
  18. 18.局部特征-ORB特征描述子
  19. 19.其他特征描述子
  20. 20.传统图像分割技术
  21. 21.GraphCut
  22. 22.GrabCut
  23. 23.人脸检测的传统方法
  24. 24.行人检测的传统方法HOG+SVM
  25. 25.行人检测DPM
  26. 26.人工神经网络的架构
  27. 27.目标函数与梯度下降
  28. 28.误差反向传播
  29. 29.可手算的神经网络
  30. 30.改进目标函数与克服梯度消散
  31. 31.神经网络之传统神经网络与深度网络的差异
  32. 32.各种梯度下降算法
  33. 33.如何选择合适的梯度下降算法
  34. 34.避免过适应
  35. 35.CNN的架构
  36. 36.CNN的误差反向传播_卷积层
  37. 37.CNN的误差反向传播_池化层
  38. 38.分类所用的数据集
  39. 39.Alexnet详解
  40. 40.VGG详解
  41. 41.GoogLeNet_v1
  42. 42.BatchNormalization
  43. 43.GoogLeNet_v3v4
  44. 44.ResNet
  45. 45.ResNeXt
  46. 46.CNN设计准则
  47. 47.图像检索与推土机距离
  48. 48.基于颜色相似性
  49. 49.基于形状相似性
  50. 50.基于纹理相似性
  51. 51.基于局部特征相似性
  52. 52.DL的二值哈希编码
  53. 53.高维空间中检索KDTree
  54. 54.高维空间中检索LSH
  55. 55.目标识别任务综述
  56. 56.RCNN
  57. 57.SPP-Net
  58. 58.Fast R-CNN
  59. 59.Faster R-CNN
  60. 60.R-CNN系列模型回顾
  61. 61.R-FCN的设计思路
  62. 62.R-FCN的实现
  63. 63.YOLOv1
  64. 64.YOLOv2_9000
  65. 65.YOLOv3
  66. 66.图像分割应用场景与数据集
  67. 67.上采样的实现方式
  68. 68.FCN网络架构的由来
  69. 69.FCN构建细节
  70. 70.DeepLabv1
  71. 71.CRF
  72. 72.DeepLabv2
  73. 73.DeepLabv3
  74. 74.特定的医学影像分割任务
  75. 75.U-Net
  76. 76.3D U-Net
  77. 77.V-Net
  78. 78.DenseNet
  79. 79.病理切片分析任务及数据集
  80. 80.病理切片分析
  81. 81.图说任务及其传统处理方法
  82. 82.深度语言模型与RNN
  83. 83.LSTM及其变种
  84. 84.NIC模型
  85. 85.SAT模型
  86. 86.图说的性能指标
  87. 87.判别模型与生成模型
  88. 88.KL散度与JS散度
  89. 89.VAE
  90. 90.GAN的基本原理
  91. 91.DCGAN与CGAN
  92. 92.Wasserstein GAN
  93. 93.SRGAN

DL的二值哈希编码

基于深度学习的二值哈希编码

小象实战讲义 · 基于深度学习的计算机视觉

在学习了多种基于颜色、纹理、形状和局部特征的传统图像检索方法后,我们自然会问:深度学习在图像检索领域扮演什么角色?本节将解答这个问题,并介绍一种结合了深度学习强大特征提取能力与高效检索需求的实用技术——基于深度学习的二值哈希编码。学完本节,你将理解如何利用预训练的卷积神经网络生成紧凑的二值哈希码,并构建一个高效的“分桶-精排”两阶段检索系统。

💡 核心导读

  • 深度学习在检索中的角色:深度学习并非“放之四海而皆准”的唯一先进技术,但因其强大的特征学习能力,在图像检索领域不可或缺。
  • 自编码器的启示:通过“降维-升维”的自监督学习,自编码器的瓶颈层能学习到数据的关键特征,这启发了我们利用深度网络中间层作为特征。
  • 二值哈希编码的生成:在预训练网络(如AlexNet)的深层全连接层后,新增一个使用Sigmoid激活的隐层,通过微调(Fine-tuning)学习到低维的二值编码,用于快速“分桶”。
  • 两阶段检索流程:先用低维二值哈希码快速定位候选图像桶,再用高维的深度特征(如4096维向量)进行精确的相似度计算和重排序,兼顾速度与精度。

深度学习与图像检索

我们前面介绍了很多传统的图像检索技术。你可能会问:在图像检索领域,难道没有深度学习什么事吗?

首先,没有所谓的“放之四海而皆准”的绝对先进技术,只有适用的技术。就像我们用筷子已经用了4000年,它不一定是最“高明”的,但非常实用。

其次,你要相信,作为应用非常广泛的一种技术手段,深度学习在图像检索领域是不会缺席的。我们接下来要介绍的知识点——基于深度学习的二值哈希编码——正是深度学习在该领域的典型应用。

在深入细节之前,我们先回顾一下传统图像检索方案的整体架构:

  1. 图片知识库:相当于像百度那样的后台,收集了海量图片。
  2. 批量特征抽取:对知识库中的所有图片,批量抽取之前介绍过的纹理、颜色、形状、局部等特征。
  3. 在线检索:当新的查询图片到来时,同样进行预处理和特征抽取,然后在特征空间中进行相似度计算,根据计算结果查找索引并返回结果。

那么,深度学习用在其中的哪个环节呢?答案就在特征提取部分。传统的方案需要手工设计并抽取特征,而深度学习通过卷积等操作,能够自动学习到对于图像分类最有效的特征。既然这些特征对于分类最有效,那么它们也必然包含了图像的关键信息,自然可以作为图像检索的依据。

自编码器:特征提取的神经网络启示

在介绍具体的深度学习检索方法前,我们先看一种经典的神经网络特征提取思路——自编码器(Autoencoder)。

自编码器是一种通过神经网络进行特征提取的降维方法。它的核心思想是“自监督学习”:让网络的输出尽可能逼近输入,而标签就是数据本身。

设想一个简单的结构:输入一张图片(例如一个猫的图片,假设用100维向量表示),经过网络后,输出一张重构的图片(同样是100维)。关键在于网络中间有一个隐藏层(瓶颈层),它的维度(例如50维)要显著小于输入和输出的维度。

import numpy as np

# 模拟自编码器数据流:输入为100维,瓶颈层为50维,输出为100维
np.random.seed(42) # 固定随机种子,保证输出确定
original_input = np.random.randn(100) # 模拟一张图片的100维表示

# 模拟“编码”过程:从100维数据压缩到50维特征
W_encode = np.random.randn(50, 100) * 0.1
b_encode = np.random.randn(50) * 0.1
bottleneck_feature = np.tanh(np.dot(W_encode, original_input) + b_encode)

print("瓶颈层特征维度:", bottleneck_feature.shape)
print("前5个特征值:", bottleneck_feature[:5])
瓶颈层特征维度: (50,)
前5个特征值: [-0.79688258  0.95110198 -0.89354123 -0.84445605 -0.80274825]
# 模拟“解码”过程:从50维特征重构100维数据
import numpy as np
np.random.seed(42) # 必须再次设定种子,保证权重一致
original_input = np.random.randn(100) # 必须在块内重新定义输入
W_encode = np.random.randn(50, 100) * 0.1
b_encode = np.random.randn(50) * 0.1
bottleneck_feature = np.tanh(np.dot(W_encode, original_input) + b_encode)

W_decode = np.random.randn(100, 50) * 0.1
b_decode = np.random.randn(100) * 0.1
reconstructed_output = np.tanh(np.dot(W_decode, bottleneck_feature) + b_decode)

# 计算重构误差(均方误差)
mse = np.mean((original_input - reconstructed_output) ** 2)
print("重构均方误差 (MSE):", mse)
重构均方误差 (MSE): 0.9418511759068889

网络训练的目标是让重构输出与原始输入的误差最小。由于信息被强制通过一个很窄的“瓶颈”,为了能较好地还原输入,瓶颈层就必须学习并保存原始数据中最关键、最主要的信息。因此,瓶颈层的输出就可以作为原始数据的一个高质量的特征表示。

如果我们在瓶颈层处“打断”网络,直接将其输出作为特征,就完成了通过神经网络进行特征提取的过程。自编码器的这一思想,为我们利用深度学习模型中间层作为检索特征提供了直接的理论启示。

基于ImageNet预训练模型生成二值哈希编码

现在,我们将这一思路应用到实际的、强大的卷积神经网络上。以经典的AlexNet在ImageNet数据集上的预训练模型为例。

AlexNet的全连接层(在课程中常被称为第7层)有4096个神经元,其输出是一个4096维的向量。这个向量已经包含了用于图像分类的丰富信息。我们可以直接用它作为检索特征吗?可以,但4096维仍然较高,直接进行大规模相似度计算效率偏低。

我们的目标是获得一个更紧凑、更适合快速检索的表示。具体做法是:在AlexNet的第7层(4096维)和最终的输出层(1000维)之间,额外添加一个全连接隐层。

这个新加的隐层有几个关键设计:

  1. 低维度:其神经元数量远小于4096,例如128维。
  2. Sigmoid激活函数:使每个神经元的输出值在0到1之间。
  3. 二值化(仅用于检索):检索时对隐层输出设定一个阈值(例如0.5),把 Sigmoid 输出变换为0或1,从而形成一个二值检索向量(例如一个128位的0/1串)。注意二值化只是“从隐层输出派生哈希码”的后处理,隐层仍然直接连到输出层完成分类,因此它不在分类前向通路上,也不影响反向传播。
# 网络结构示意图 (基于AlexNet修改)
# ① 分类前向通路(训练与分类都走这条)
输入图片 -> [卷积层、池化层...] -> 第7层全连接(4096维) -> 新加隐层(128维,Sigmoid) -> 输出层(1000维)

# ② 检索分支(只在检索时使用,不参与分类前向)
新加隐层(128维) --阈值0.5二值化--> 128位0/1二值哈希码

如何训练这个新加的网络层呢?我们采用微调(Fine-tuning) 策略:

  1. 保留从ImageNet预训练好的前7层权重。
  2. 新添加的第8层(128维隐层)随机初始化;若目标类别数与原模型不同,输出层也需要重新初始化。
  3. 在一个新的目标数据集上,以较小的学习率对整个网络(包括新加层)进行训练。
  4. 训练目标仍然是图像分类(类别数与目标数据集一致;若沿用 ImageNet 则为 1000 类),但在这个过程中,网络会“学会”如何将4096维的丰富特征,压缩成一个低维的、同时又能保证分类正确的二值编码。

最终,我们得到了一个既能完成分类任务,又能产出二值哈希编码的网络。每次前向传播,我们都可以得到两个输出:

  • 第7层输出:4096维的浮点特征向量,表征图像的丰富信息,用于精确匹配。
  • 第8层输出:128维的浮点激活值,检索时阈值化为0/1后即128位二值哈希码,用于快速索引和分桶。这里的“第8层”指新加的128维隐层,不要与原来的1000维输出层混淆。

两阶段检索流程详解

基于以上模型,我们可以构建一个高效的图像检索系统,其流程如下图所示:

【两阶段检索流程】
1. 离线建库:
   知识库所有图片 -> 网络前向传播 -> 得到 (128位二值哈希码, 4096维深度特征) 对 -> 按哈希码分桶存储。

2. 在线检索:
   a. 分桶(粗筛):
      查询图片 -> 网络前向传播 -> 得到128位二值哈希码 -> 根据此码定位到对应的“桶”。
      (桶内存放的都是哈希码相同或相似的图片,因此语义上接近,例如都是“鹰”的图片)
   b. 精排(重排序):
      取出该桶内所有图片的4096维深度特征。
      计算查询图片的4096维特征与桶内每个特征的相似度(如余弦距离、欧氏距离)。
      根据相似度对桶内图片进行排序,返回最相似的若干张作为最终结果。

这个流程的优势非常明显:

  • 速度快:避免了查询图片与知识库中全部图片进行高维特征比对。只需要计算一次哈希码找到桶,然后仅与桶内(通常只占全集很小一部分)图片进行精细比较。
  • 精度高:粗筛阶段利用的是网络学习到的、与类别强相关的哈希码,保证了候选集的相关性。精排阶段使用的是信息更丰富的4096维深度特征,保证了排序的准确性。

检索效果与应用

采用基于深度学习的二值哈希编码进行图像检索,效果显著。例如,当查询图片是“飞机”或“马”时,系统能够返回语义一致且视觉上非常相似的图片。对于服装、箱包等商品的检索,也能达到很好的效果。

这印证了我们开头的观点:在图像检索这个应用广泛的领域,深度学习确实没有缺席,它提供了一种将强大特征学习能力与高效检索需求相结合的优秀解决方案。

📝 动手练一练

  1. 概念辨析:在基于深度学习的二值哈希编码检索系统中,“二值哈希码”(如128位)和“深度特征”(如4096维)分别承担什么角色?如果只用其中一种进行检索,可能会有什么问题?
👉 点击查看参考答案
  • 二值哈希码的角色:承担快速索引和粗筛(分桶) 的角色。它是一个低维、二值的紧凑表示,计算和比对速度极快(异或操作、汉明距离),用于将海量图像库划分为多个语义相似的子集(桶)。
  • 深度特征的角色:承担精确匹配和精排的角色。它是一个高维、稠密的浮点向量,包含了图像的丰富细节信息,用于在粗筛得到的少量候选图像中进行细致的相似度计算和最终排序。
  • 只用一种的问题:
    • 如果只用二值哈希码:虽然检索速度极快,但丢失了大量细节信息,检索精度会下降,可能无法区分视觉相似但属于不同类别的物体,或者无法对同类物体的细微差异进行排序。
    • 如果只用深度特征:需要对查询图片和库中所有图片计算高维向量的相似度,计算开销巨大,检索速度慢,无法应对大规模图像库的实时检索需求。
  1. 代码思考:假设我们有一个已经二值化的128位哈希码(用长度为128的0/1列表表示),以及一个包含10个桶的字典,每个桶的键是一个哈希码字符串,值是该桶内图片ID的列表。请编写伪代码,描述如何为一张查询图片找到其所属的桶。
👉 点击查看参考答案
# 伪代码:查询图片分桶过程
# 假设已有函数 extract_binary_hash(query_image) 能返回128位的0/1列表
# 假设 bucket_dict 的结构:{‘1010…’: [img_id1, img_id2, …], …}

def find_candidate_bucket(query_image, bucket_dict):
    # 1. 提取查询图片的二值哈希码
    query_hash_list = extract_binary_hash(query_image)
    query_hash_str = ''.join([str(bit) for bit in query_hash_list]) # 转换为字符串方便比对
    
    # 2. 精确匹配:查找哈希码完全相同的桶
    if query_hash_str in bucket_dict:
        candidate_bucket = bucket_dict[query_hash_str]
        return candidate_bucket
    else:
        # 3. (可选)如果精确匹配不到,可以查找汉明距离最近的桶
        # 这里简化处理,返回空列表或执行其他后备策略
        return []
        
# 使用示例
candidate_image_ids = find_candidate_bucket(my_query_image, bucket_dict)
print("候选图片ID列表:", candidate_image_ids)

在实际系统中,为了提高召回率,可能会查询汉明距离在某个阈值内的所有桶,而不仅仅是完全匹配的桶。

本章小结

本节我们探讨了深度学习在图像检索领域的应用,重点介绍了基于深度学习的二值哈希编码方法。该方法巧妙地利用了预训练卷积神经网络强大的特征学习能力,通过添加一个特殊的二值化隐层,生成了兼具语义信息与检索效率的紧凑哈希码。结合“分桶粗筛”与“特征精排”的两阶段流程,实现了在大规模图像库中既快又准的检索。

今天,这类基于分类监督的早期深度哈希方法,已大量被端到端深度哈希(如 DPSH、HashNet)以及 FAISS、hnswlib(HNSW)等向量检索库取代;但“低维紧凑码粗筛 + 高维特征精排”的两阶段思想,仍是现代大规模检索与向量数据库(如 IVF + 重排)的经典架构。

📋 行动清单

  • 理解自编码器通过“瓶颈层”学习数据关键特征的原理,并思考其与深度学习特征提取的关联。
  • 掌握在预训练网络(如AlexNet)上添加二值哈希层并进行微调(Fine-tuning)的整体流程。
  • 清晰描述基于二值哈希编码的两阶段检索(分桶与精排)流程,并能说明每个阶段使用的特征及其原因。

—— 小象教研组