可以在此处找到带有示例的最终搜索。
它看起来像这样:
我想为 OpenGameArt 构建一个反向图像搜索,因为 Google 图像搜索和 TinEye 没有给出好的结果。我之前生成了一个巨大的图块地图来概述 OpenGameArt 上的类似图像,但它在网络或图像浏览器上的资源不是很友好,必须分成更小的文件,而且它无法以任何方式搜索,只是可滚动。因此,我想要一种方式让人们探索 OpenGameArt 上提供了哪些类型的艺术,并最终使用相似性搜索来浏览图像空间。
我要做的第一件事是在 OpenGameArt 上检索我感兴趣的查询的搜索结果,主要是 2D 艺术。然后我必须检索搜索结果索引中的每个 HTML 页面并解析 HTML 以获取文件链接。 OpenGameArt 包含许多存档文件,例如 zip 和 rar 文件,因此我必须解压它们才能获取图像。
例如,下面的代码片段显示了如何解析内容页面并获取文件链接:
responseBody = await Common . ReadURIOrCache ( blob , Common . BaseURI + page , client ) ;
var htmlDoc = new HtmlDocument ( ) ;
htmlDoc . LoadHtml ( responseBody ) ;
var htmlBody = htmlDoc . DocumentNode . SelectSingleNode ( " //body " ) ;
foreach ( var nNode in htmlBody . Descendants ( " a " ) )
{
if ( nNode . NodeType == HtmlNodeType . Element &&
nNode . Attributes [ " href " ] != null &&
nNode . Attributes [ " href " ] . Value . Contains ( " /default/files/ " ) )
{
msg . Add ( HttpUtility . HtmlDecode ( nNode . Attributes [ " href " ] . Value . Replace ( Common . FileURI , " " ) ) ) ;
}
}
我使用 Azure Functions 执行爬网步骤,并根据需要进行一些来回手动干预来纠正问题。每个步骤都有自己的队列,然后将下一步的作业放入下一个队列中。最终,Azure 上的调用成本约为 50 美元,如果我没记错的话,假设有 10-2000 万次函数调用。
我尝试使用开源 Milvus 数据库,但它在我的 DigitalOcean 服务器上崩溃了,因为我没有足够的内存。然后,我偶然且幸运地在 Hacker News 评论部分发现了 Pinecone 的链接,并决定使用它,因为试用是免费的,而且我无需扩展服务器内存即可使用 Milvus。最终我还是扩展了我的服务器,但我没有再尝试 Milvus(至少现在还没有)。
为此,我在脚本中使用了 VGG16 特征提取。有关更多信息,请参阅该文章,但本质上,每个图像都是 4096 个 32 位浮点数,它们描述了图像的各种特征,例如以非常简单的方式表示它有多少条纹或正方形,或者它的绿色程度。但这些特征是基于VGG16神经网络中的神经元(通常用于图像分类),因此这些特征可能比简单特征标签描述的更复杂。我们需要这些向量的原因是,可以很容易地使用欧几里得距离或余弦相似度或两个向量的其他度量来查看它们是否相似,从而得出图像相似。此外,还有针对这些向量的搜索技术,可以对大量向量进行快速搜索。
下面是一个简化的 Python 脚本,用于展示如何进行特征提取:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim: ft=python ts=4 sw=4 sts=4 et fenc=utf-8
from tensorflow . keras . applications . vgg16 import VGG16
from tensorflow . keras . preprocessing import image
from tensorflow . keras . applications . vgg16 import decode_predictions , preprocess_input
from tensorflow . keras . models import Model
from tensorflow . compiler import xla
import numpy as np
import time
import os
import sys
import PIL
import json
import math
import multiprocessing
from glob import glob
from PIL import Image
from io import BytesIO
model = VGG16 ( weights = 'imagenet' , include_top = True )
feat_extractor = Model ( inputs = model . input , outputs = model . get_layer ( "fc2" ). output )
def prepImage ( img ):
x = np . array ( img . resize (( 224 , 224 )). convert ( 'RGB' ))
x = np . expand_dims ( x , axis = 0 )
x = preprocess_input ( x )
return x
def main ():
'entry point'
fname = 'demo.jpg'
dt = Image . open ( fname )
pimg = prepImage ( dt )
print ( "Computing feature vector" , fname )
features = feat_extractor . predict ( pimg )
print ( features )
if __name__ == '__main__' :
main ()
这是脚本的输出:
emh@frostpunk ~ /public_html/ogasearch 0% ./test.py (git)-[gh-pages]
2021-04-07 18:48:03.158023: W tensorflow/stream_executor/platform/default/dso_loader.cc:60] Could not load dynamic library ' libcudart.so.11.0 ' ; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2021-04-07 18:48:03.158082: I tensorflow/stream_executor/cuda/cudart_stub.cc:29] Ignore above cudart dlerror if you do not have a GPU set up on your machine.
2021-04-07 18:48:07.783109: I tensorflow/compiler/jit/xla_cpu_device.cc:41] Not creating XLA devices, tf_xla_enable_xla_devices not set
2021-04-07 18:48:07.783485: W tensorflow/stream_executor/platform/default/dso_loader.cc:60] Could not load dynamic library ' libcuda.so.1 ' ; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory
2021-04-07 18:48:07.783530: W tensorflow/stream_executor/cuda/cuda_driver.cc:326] failed call to cuInit: UNKNOWN ERROR (303)
2021-04-07 18:48:07.783580: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (frostpunk): /proc/driver/nvidia/version does not exist
2021-04-07 18:48:07.784058: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-04-07 18:48:07.784513: I tensorflow/compiler/jit/xla_gpu_device.cc:99] Not creating XLA devices, tf_xla_enable_xla_devices not set
2021-04-07 18:48:08.599925: W tensorflow/core/framework/cpu_allocator_impl.cc:80] Allocation of 411041792 exceeds 10% of free system memory.
2021-04-07 18:48:09.194634: W tensorflow/core/framework/cpu_allocator_impl.cc:80] Allocation of 411041792 exceeds 10% of free system memory.
2021-04-07 18:48:09.385612: W tensorflow/core/framework/cpu_allocator_impl.cc:80] Allocation of 411041792 exceeds 10% of free system memory.
2021-04-07 18:48:13.033066: W tensorflow/core/framework/cpu_allocator_impl.cc:80] Allocation of 411041792 exceeds 10% of free system memory.
Computing feature vector demo.jpg
2021-04-07 18:48:13.706621: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:116] None of the MLIR optimization passes are enabled (registered 2)
2021-04-07 18:48:13.717564: I tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2199995000 Hz
[[0. 3.1128967 1.5611947 ... 1.2625191 0.7709812 0. ]]
./test.py 12.20s user 4.66s system 132% cpu 12.731 total
我还想最后将所有图像 URL 放入一个 SQL 数据库中,并有一个标志来指示我是否进行了 VGG16 特征提取以及是否将其添加到矢量数据库(Milvus 或 Pinecone)中。能够在 Pineone 中使用的整数主键和 URL 以及可能属于图像的其他元数据之间来回映射,因为 [Pinecone](https://www.pinecone.io/ 不存储其他元数据比初级最后,我将 SQL 数据库转为制表符分隔的文本文件,并在查询服务器启动时加载它。
我想我总共花了一周的时间来运行所有代码来完成,每个步骤大约需要一两天的时间,爬行,计算特征向量。我不记得将向量插入 Pinecone 数据库花了多少时间,但我认为这不是最耗时的步骤。
最后,我还添加了一个快速修复,以删除具有相同分数的几乎重复的图像结果。我在使用“双重”URL 编码的搜索页面上遇到了一些麻烦,因为我在文件系统中使用 URL 编码存储了文件,但是当浏览器对文件进行双重编码时,我在前端使用一些检测代码解决了这个问题。 URL 编码的文件名。我建议存储爬取的文件而不进行 URL 编码。我很遗憾我的脚本质量不够高或不够精致,例如脚本中有多个步骤,我通过编辑脚本而不是采用命令行参数来更改内容。我不想发布脚本片段并进行解释,因为它们有点混乱。此外,在处理特征提取之前,我中途将文件从 Azure 存储移动到了 DigitalOcean 服务器,因此存在一些不一致的数据位置处理。