Die finale Suche mit einem Beispiel finden Sie hier.
Es sieht so aus:
Ich wollte eine umgekehrte Bildersuche für OpenGameArt erstellen, da Google Image Search und TinEye dafür keine guten Ergebnisse liefern. Ich hatte zuvor eine riesige Kachelkarte erstellt, um einen Überblick über ähnliche Bilder auf OpenGameArt zu geben, aber sie war im Web oder im Bildbrowser nicht sehr ressourcenschonend und musste in kleinere Dateien aufgeteilt werden, außerdem ist sie überhaupt nicht durchsuchbar scrollbar. Deshalb wollte ich den Leuten eine Möglichkeit bieten, herauszufinden, welche Art von Kunst auf OpenGameArt verfügbar ist, und landete bei der Verwendung der Ähnlichkeitssuche, um den Bildraum zu durchsuchen.
Das erste, was ich tun musste, war, die Suchergebnisse für die Suchanfrage, die mich interessierte, auf OpenGameArt abzurufen, hauptsächlich die 2D-Kunst. Dann musste ich jede HTML-Seite abrufen, die sich im Suchergebnisindex befand, und den HTML-Code auf Links zu Dateien analysieren. OpenGameArt enthält viele Archivdateien wie ZIP- und RAR-Dateien, die ich dann entpacken musste, um an die Bilder zu gelangen.
Hier ist zum Beispiel ein Ausschnitt, der zeigt, wie man die Inhaltsseite analysiert und Dateilinks erhält:
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 , " " ) ) ) ;
}
}
Ich habe Azure Functions verwendet, um die Crawling-Schritte durchzuführen, mit einigen manuellen Eingriffen, um die Dinge nach Bedarf zu korrigieren. Jeder Schritt hatte seine eigene Warteschlange und dann wurde der Job für den nächsten Schritt in die nächste Warteschlange gestellt. Am Ende kosten die Aufrufe auf Azure etwa 50 USD, wenn ich mich richtig erinnere, sagen wir mal 10-20 Millionen Funktionsaufrufe.
Ich habe versucht, die Open-Source-Datenbank Milvus zu verwenden, aber sie stürzte auf meinem DigitalOcean-Server ab, weil ich nicht genügend Speicher darauf hatte. Dann entdeckte ich zufällig und glücklicherweise den Link zu Pinecone in einem Kommentarbereich von Hacker News und entschied mich, stattdessen diesen zu verwenden, da die Testversion kostenlos war und ich meinen Serverspeicher nicht erweitern musste, um Milvus zu verwenden. Am Ende habe ich meinen Server trotzdem erweitert, Milvus aber nicht noch einmal ausprobiert (zumindest noch nicht).
Ich habe dafür in meinem Skript die VGG16-Feature-Extraktion verwendet. Weitere Informationen finden Sie im Artikel, aber im Wesentlichen handelt es sich um 4096 32-Bit-Gleitkommazahlen für jedes Bild, die verschiedene Merkmale des Bildes beschreiben, beispielsweise auf sehr vereinfachte Weise, wie viele Streifen oder Quadrate es hat oder wie grün es ist . Diese Merkmale basieren jedoch auf Neuronen im neuronalen Netzwerk für VGG16 (das normalerweise zur Bildklassifizierung verwendet wird), sodass die Merkmale möglicherweise komplizierter sind als das, was durch einfache Merkmals-Tags beschrieben wird. Und der Grund, warum wir diese Vektoren brauchen, ist, dass es einfach ist, den euklidischen Abstand oder die Kosinusähnlichkeit oder ein anderes Maß für zwei Vektoren zu verwenden, um zu sehen, ob sie ähnlich sind, und folglich sind die Bilder ähnlich. Darüber hinaus gibt es für diese Vektoren eine Suchtechnologie, die eine schnelle Suche auf einer großen Anzahl von ihnen ermöglicht.
Hier ist ein vereinfachtes Python-Skript, das zeigt, wie die Feature-Extraktion durchgeführt wird:
#!/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 ()
Hier ist die Ausgabe des Skripts:
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
Ich wollte am Ende auch alle Bild-URLs in eine SQL-Datenbank einfügen und eine Markierung haben, ob ich die VGG16-Feature-Extraktion durchgeführt habe und ob sie zur Vektordatenbank (Milvus oder Pinecone) hinzugefügt wurde. Das ist wichtig Ordnen Sie zwischen einem ganzzahligen Primärschlüssel, der in Pineone verwendet wird, und der URL und möglicherweise anderen Metadaten, die zum Bild gehören, hin und her, da [Pinecone](https://www.pinecone.io/ diese nicht speichert andere Metadaten als der Primärschlüssel. Am Ende habe ich die SQL-Datenbank in eine tabulatorgetrennte Textdatei umgewandelt und sie beim Start des Abfrageservers geladen.
Ich glaube, ich habe insgesamt eine Woche damit verbracht, den gesamten Code bis zur Fertigstellung auszuführen, wobei jeder Schritt etwa ein oder zwei Tage gedauert hat, ich habe gecrawlt und Feature-Vektoren berechnet. Ich kann mich nicht erinnern, wie viel Zeit es gedauert hat, die Vektoren in die Pinecone-Datenbank einzufügen, aber ich denke, es war nicht der zeitaufwändigste Schritt.
Am Ende habe ich auch eine schnelle Lösung hinzugefügt, um nahezu doppelte Bildergebnisse mit identischer Bewertung zu entfernen. Auf der Suchseite hatte ich einige Probleme mit der „doppelten“ URL-Kodierung, weil ich die Dateien mit URL-Kodierung im Dateisystem gespeichert hatte, aber ich habe es mit einem Erkennungscode im Frontend umgangen, als der Browser sie doppelt kodierte URL-codierte Dateinamen. Ich empfehle, die gecrawlten Dateien ohne URL-Kodierung zu speichern. Ich bedaure, dass meine Skripte nicht so hochwertig oder ausgefeilt sind, zum Beispiel gibt es mehrere Schritte in Skripten und ich ändere Dinge, indem ich das Skript bearbeite, anstatt Befehlszeilenargumente zu verwenden. Ich habe keine Lust, Ausschnitte aus den Skripten zu posten und zu erklären, da sie etwas chaotisch sind. Außerdem habe ich die Dateien auf halbem Weg vom Azure-Speicher auf meinen DigitalOcean-Server verschoben, bevor die Feature-Extraktion verarbeitet wurde, sodass es zu einer inkonsistenten Datenspeicherortbehandlung kommt.