การค้นหาขั้นสุดท้ายพร้อมตัวอย่างสามารถพบได้ที่นี่
ดูเหมือนว่านี้:
ฉันต้องการสร้างการค้นหารูปภาพแบบย้อนกลับสำหรับ OpenGameArt เนื่องจาก Google Image Search และ TinEye ไม่ได้ให้ผลลัพธ์ที่ดีนัก ก่อนหน้านี้ ฉันได้สร้างแผนที่ย่อยขนาดใหญ่เพื่อให้เห็นภาพรวมของรูปภาพที่คล้ายกันบน OpenGameArt แต่มันไม่เป็นมิตรกับทรัพยากรบนเว็บหรือเบราว์เซอร์รูปภาพมากนัก และต้องแบ่งออกเป็นไฟล์ขนาดเล็กลง แถมยังไม่สามารถค้นหาได้ในทางใดทางหนึ่ง เพียงแค่ เลื่อนได้ ดังนั้นฉันจึงต้องการวิธีให้ผู้คนได้สำรวจว่ามีงานศิลปะประเภทใดบ้างบน OpenGameArt และหันมาใช้การค้นหาที่คล้ายคลึงกันเพื่อเรียกดูพื้นที่รูปภาพ
สิ่งแรกที่ฉันต้องทำคือเรียกค้นผลการค้นหาสำหรับข้อความค้นหาที่ฉันสนใจใน OpenGameArt ซึ่งส่วนใหญ่เป็นงานศิลปะ 2 มิติ จากนั้น ฉันต้องดึงข้อมูลหน้า 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 เพื่อทำตามขั้นตอนการรวบรวมข้อมูล โดยมีการแทรกแซงด้วยตนเองกลับไปกลับมาเพื่อแก้ไขสิ่งต่าง ๆ ตามความจำเป็น แต่ละขั้นตอนมีคิวของตัวเอง จากนั้นจึงวางงานสำหรับขั้นตอนถัดไปในคิวถัดไป ในตอนท้ายการเรียกใช้ฟังก์ชันมีค่าใช้จ่ายประมาณ 50 USD บน Azure สมมติว่าการเรียกใช้ฟังก์ชัน 10-20 ล้านครั้งหากฉันจำถูกต้อง
ฉันพยายามใช้ฐานข้อมูลโอเพ่นซอร์ส Milvus แต่เกิดปัญหาบนเซิร์ฟเวอร์ DigitalOcean ของฉัน เนื่องจากฉันมีหน่วยความจำไม่เพียงพอ จากนั้นฉันก็บังเอิญและโชคดีที่ค้นพบลิงก์ไปยัง Pinecone ในส่วนความคิดเห็นของ Hacker News และตัดสินใจใช้ลิงก์นั้นแทน เนื่องจากการทดลองใช้ฟรี และฉันไม่จำเป็นต้องขยายหน่วยความจำเซิร์ฟเวอร์เพื่อใช้ Milvus ในที่สุดฉันก็ขยายเซิร์ฟเวอร์ของฉันต่อไป แต่ฉันไม่ได้ลองใช้ Milvus อีกเลย (อย่างน้อยก็ยังไม่ได้)
ฉันใช้การแยกคุณสมบัติ VGG16 ในสคริปต์ของฉันสำหรับสิ่งนี้ ดูบทความสำหรับข้อมูลเพิ่มเติม แต่โดยพื้นฐานแล้วมันคือตัวเลขทศนิยม 32 บิต 4096 สำหรับแต่ละภาพ ซึ่งอธิบายคุณสมบัติต่างๆ ของภาพ เช่น พูดง่ายๆ ว่ามีแถบหรือสี่เหลี่ยมกี่เส้นหรือสีเขียวแค่ไหน . แต่คุณลักษณะเหล่านี้จะขึ้นอยู่กับเซลล์ประสาทในโครงข่ายประสาทเทียมสำหรับ 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 ของฉันกลางคัน ก่อนที่จะประมวลผลการแยกฟีเจอร์ จึงมีการจัดการตำแหน่งข้อมูลที่ไม่สอดคล้องกัน