找到相似的你——使用深度学习和局部敏感哈希来查找类似图像!
使用FastAI和Pytorch的ResNet 34图像嵌入查找类似图像的简单演练。还在巨大的图像嵌入集合中进行快速语义相似性搜索。
在这篇文章中,我们试图实现上述结果,即给定图像,我们应该能够从Caltech-101数据库中找到类似的图像。帖子指导了我如何构建端到端流程,复制项目的整个代码库都在我的GitHub存储库中。实现上述结果的过程可以通过以下几个步骤进行细分 -
- 从ResNet-34模型(在ImageNet上训练)迁移学习,使用FastAI和Pytorch检测Caltech-101数据集中的101个类。
- 从训练有素的ResNet 34模型中获取第二个完全连接层的输出,以嵌入所有9,144个Caltech-101图像。
- 使用局部敏感哈希为我们的图像嵌入创建LSH哈希,从而实现近似最近邻查找。
- 然后给出一个图像,我们可以使用训练的模型将其转换为图像嵌入,然后使用Caltech-101数据集上的近似最近邻查找相似的图像。
第1部分 - 数据理解和迁移学习
正如我上面提到的,对于这个项目,我的目标是查询任何给定的图像,并在Caltech-101数据库中找到语义相似的图像。该数据库包含9,144个图像,分为101个类别。每个类别中约有50-800个图像。
来自Caltech-101数据库的图像示例
我们项目的第一个练习是获得一个深度学习网络,可以准确地对这些类别进行分类。对于此任务,我们将使用经过预先训练的ResNet 34网络,该网络在ImageNet数据库上进行训练并迁移学习,使用Pytorch 1.0和FastAI库对101类Caltech-101数据库进行分类。正如我在前一篇博客中所写的关于如何使用任何给定数据集并进行迁移学习的文章,我将在本博客中概述该过程。你可以参考这个笔记本来找到做同样的代码。在下面找到进行迁移学习以分类Caltech-101图像的步骤 -
- 使用FastAI库使用Pytorch的数据集加载器加载数据
- 采用预先训练好的网络,在这种情况下,使用ResNet 34并移除它最后完全连接的一个层
- 在网络末端添加新的完全连接的图层,并使用Caltech-101图像仅训练这些图层,同时保持所有其他图层冻结
- 通过解冻所有层来训练整个网络
第2部分 - 使用Pytorch Hooks提取图像嵌入
现在我们有一个预先训练好的网络,我们需要从这个网络中提取所有Caltech-101图像的嵌入。在N维向量中嵌入一个对象的表示。在这种情况下,图像嵌入是N维图像的表示。基本思想是给定图像越接近另一图像,它们的嵌入也将在空间维度上相似且接近。
图像嵌入可视化
您可以在上面从该博客中获取的图像中看到,图像嵌入是矢量化形式的图像的空间表示,其中相似的图像也在空间维度上接近。
我们可以通过获取其第二个完全连接层的尺寸为512的输出来从ResNet-34获得图像嵌入。为了检查在Pytorch中的深度学习模型中保存中间计算,或者在我们的情况下提取嵌入,我们使用Pytorch Hooks。挂钩可以有两种类型:前进和后退。前向钩子用于保存在网络中向前传递的信息以进行推理,而后向钩子用于在反向传播期间收集有关梯度的信息。在我们的例子中,我们需要在推理阶段输出我们的第二个完全连接的层,这意味着我们需要使用前向钩子。让我们看一下创建钩子的代码(也在我笔记本的“Extracting Feature”部分)
上面的代码是创建前向钩子所需的全部代码。 SaveFeatures类从torch.nn模块调用register_forward_hook函数,并给定任何模型层,它将中间计算保存在numpy数组中,该数组可以使用SaveFeatures.features函数进行检索。让我们看看使用这个类的代码 -
第1-2行:使用模型层引用引用第二个完全连接层的输出作为输入,调用类SaveFeatures。
第4-6行:传递Caltech-101数据以获得预测。请注意,我们对保存预测不感兴趣,这就是我们使用“_”的原因。在这种情况下,第二个最后一层的中间输出保存在名为“sf”的变量中,该变量是SaveFeatures类的一个实例。
第8-10行:创建一个python字典,其中图像路径是关键,图像嵌入是值。
现在我们在字典中将每个图像的表示嵌入到Caltech-101中。
第3部分 - 用于快速近似最近邻查找的局部敏感哈希
我们可以使用新生成的Caltech 101图像嵌入并获得一个新图像,将其转换为嵌入以计算新图像的距离和所有Caltech 101数据库以查找类似图像。该过程本质上计算是昂贵的,并且因为新的图像嵌入必须与Caltech 101数据库中的所有9K +图像嵌入进行比较以找到最相似的图像(最近邻居),其在计算复杂度表示法中是O(N 2)问题。并且随着图像数量的增加,将花费指数更多的时间来检索相似的图像。
为了解决这个问题,我们将使用局部敏感散列(LSH),这是一种近似最近邻算法,它将计算复杂度降低到O(log N)。本博客在时间复杂性和实施方面详细解释了LSH。简而言之,LSH为图像嵌入生成哈希值,同时牢记数据的空间性;特别是;高维度相似的数据项将有更高的机会获得相同的哈希值。
以下是LSH如何在大小为K-的散列中转换嵌入的步骤
- 在嵌入维度中生成K个随机超平面( random hyperplanes)
- 检查特定嵌入是否在超平面之上或之下并分配1/0
- 对每个K超平面执行步骤2以获得哈希值
橙色点的哈希值为101,因为它:1)在紫色超平面上方; 2)蓝色超平面下方; 3)黄色超平面上方
现在让我们看看LSH将如何执行ANN查询。给定一个新的图像嵌入,我们将使用LSH为给定图像创建一个哈希,然后比较共享相同哈希值的Caltech-101数据集图片的图像嵌入距离。通过这种方式,我们不会对整个Caltech-101数据库进行相似性搜索,而只会对图像子集进行相似性搜索,这些图像与输入图像共享相同的哈希值。对于我们的项目,我们使用lshash3包进行近似最近邻搜索。让我们看一下代码来做同样的事情(你可以在我的笔记本上找到“使用局部敏感哈希查找近似的图像”部分的代码) -
上面的代码采用图像嵌入字典并将其转换为LSH表。要查询LSH表,我们可以使用下面的代码 -
第4部分 - 全部放在一起
现在我们创建了LSH表,让我们编写一个脚本,该脚本可以将图像URL作为输入,并从CalTech 101数据库中为我们提供N(用户定义的)类似图像。这部分的代码可以在我的Github上找到(https://github.com/aayushmnit/Deep_learning_explorations/blob/master/8_Image_similarity_search/find_similar_image.py)。
查找类似图像脚本的处理流程
该脚本执行以下任务 -
- 加载LSH表和我们的ResNet 34模型(load_model函数)
- 从用户调用获取图像URL并下载图像(download_img_from_url函数)
- 从ResNet-34传递图像以获得512维图像嵌入(image_to_vec函数)
- 使用LSH表查询它以查找N(用户定义的)类似图像及其路径(get_similar_images函数)
- 在所需的输出路径返回输出,可选择使用Open CV(get_similar_images函数)显示它
我们可以在各种应用中使用类似的概念,例如在我们的照片库中查找类似的图像,类似项目的项目推荐,对图像进行网络搜索,查找近似重复的图像等。
总结(TL; DR)
在文章中,我们看到了深度学习在寻找语义相似图像方面的应用,以及如何使用局部敏感散列(LSH)来进行近似最近邻查找,以加快大型数据集的查询时间。此外,重要的是要注意我们并没有在原始特征上使用LSH(图像),而是嵌入有助于在大型集合中进行快速相似性搜索的嵌入。
编译出品
来源:https://towardsdatascience.com/finding-similar-images-using-deep-learning-and-locality-sensitive-hashing-9528afee02f5
作者:Aayush Agrawal