Итоговый поиск с примером можно найти здесь.
Это выглядит так:
Я хотел создать обратный поиск изображений для 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 для выполнения шагов сканирования, с некоторым ручным вмешательством для исправления ошибок по мере необходимости. У каждого шага была своя очередь, а затем задание следующего шага помещалось в следующую очередь. В конце концов, вызовы в Azure стоят около 50 долларов США, скажем, 10-20 миллионов вызовов функций, если я правильно помню.
Я пытался использовать базу данных Milvus с открытым исходным кодом, но на моем сервере DigitalOcean произошел сбой, поскольку на нем не хватило памяти. Затем я случайно и к счастью обнаружил ссылку на Pinecone в разделе комментариев Hacker News и решил использовать ее вместо этого, поскольку пробная версия была бесплатной, и мне не нужно было расширять память сервера, чтобы использовать 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-кодировке. Я рекомендую хранить просканированные файлы без кодирования URL. Я сожалею, что мои сценарии не столь высокого качества и отточены, например, в сценариях есть несколько шагов, и я меняю ситуацию, редактируя сценарий, вместо того, чтобы принимать аргументы командной строки. Мне не хочется публиковать фрагменты сценариев и объяснять их, поскольку они немного беспорядочны. Кроме того, я переместил файлы из хранилища Azure на свой сервер DigitalOcean на полпути, перед обработкой извлечения функций, поэтому возникла некоторая непоследовательная обработка местоположения данных.