El camino hacia la optimización de la búsqueda de imágenes a escala de miles de millones (2/2)
Este artículo es la segunda parte de The Journey to Optimizing Billion-scale Image Search by UPYUN. Si te perdiste la primera, haz clic aquí.
El sistema de búsqueda por imagen de segunda generación
El sistema de búsqueda por imagen de segunda generación elige técnicamente la solución CNN + Milvus. El sistema se basa en vectores de características y proporciona un mejor soporte técnico.
Extracción de características
En el campo de la visión por computadora, el uso de la inteligencia artificial se ha convertido en la corriente principal. Del mismo modo, la extracción de características del sistema de búsqueda por imagen de segunda generación utiliza una red neuronal convolucional (CNN) como tecnología subyacente
El término CNN es difícil de entender. Aquí nos centramos en responder dos preguntas:
- ¿Qué puede hacer CNN?
- ¿Por qué puedo usar CNN para una búsqueda de imágenes?
Foto de memegenerator.net
Hay muchas competiciones en el campo de la IA y la clasificación de imágenes es una de las más importantes. El trabajo de la clasificación de imágenes consiste en determinar si el contenido de la imagen trata sobre un gato, un perro, una manzana, una pera u otros tipos de objetos.
¿Qué puede hacer CNN? Puede extraer características y reconocer objetos. Extrae características de múltiples dimensiones y mide qué tan cerca están las características de una imagen de las características de gatos o perros. Podemos elegir las más cercanas como nuestro resultado de identificación, que indica si el contenido de una imagen específica trata sobre un gato, un perro o algo más.
¿Cuál es la conexión entre la función de identificación de objetos de CNN y la búsqueda por imagen? Lo que queremos no es el resultado final de identificación, sino el vector de características extraído de múltiples dimensiones. Los vectores de características de dos imágenes con contenido similar deben estar cerca.
¿Qué modelo CNN debo usar?
La respuesta es VGG16. ¿Por qué elegirlo? Primero, VGG16 tiene una buena capacidad de generalización, es decir, es muy versátil. Segundo, los vectores de características extraídos por VGG16 tienen 512 dimensiones. Si hay muy pocas dimensiones, la precisión puede verse afectada. Si hay demasiadas dimensiones, el costo de almacenar y calcular estos vectores de características es relativamente alto.
Usar CNN para extraer características de imágenes es una solución común. Podemos usar VGG16 como modelo y Keras + TensorFlow para la implementación técnica. Aquí está el ejemplo oficial de Keras:
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np
model = VGG16(weights=’imagenet’, include_top=False)
img_path = ‘elephant.jpg’
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
features = model.predict(x)
Las características extraídas aquí son vectores de características.
1. Normalización
Para facilitar las operaciones posteriores, a menudo normalizamos la característica:
Lo que se usa posteriormente también es el norm_feat normalizado.
2. Descripción de la imagen
La imagen se carga usando el método image.load_img de keras.preprocessing:
from keras.preprocessing import image
img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
De hecho, es el método de TensorFlow llamado por Keras. Para obtener más detalles, consulta la documentación de TensorFlow. El objeto de imagen final es en realidad una instancia de PIL Image (el PIL utilizado por TensorFlow).
3. Conversión de bytes
En términos prácticos, el contenido de imagen a menudo se transmite a través de la red. Por lo tanto, en lugar de cargar imágenes desde una ruta, preferimos convertir los datos de bytes directamente en objetos de imagen, es decir, PIL Images:
import io
from PIL import Image
# img_bytes: 图片内容 bytes
img = Image.open(io.BytesIO(img_bytes))
img = img.convert('RGB')
img = img.resize((224, 224), Image.NEAREST)
La imagen anterior es la misma que el resultado obtenido por el método image.load_img. Hay dos cosas a las que prestar atención:
- Debes hacer la conversión RGB.
- Debes redimensionar (resize es el segundo parámetro del
load_img method).
4. Procesamiento de bordes negros
Las imágenes, como las capturas de pantalla, en ocasiones pueden tener bastantes bordes negros. Estos bordes negros no tienen valor práctico y causan mucha interferencia. Por esta razón, eliminar los bordes negros también es una práctica común.
Un borde negro es esencialmente una fila o columna de píxeles donde todos los píxeles son (0, 0, 0) (imagen RGB). Eliminar el borde negro consiste en encontrar estas filas o columnas y eliminarlas. En realidad, esto es una multiplicación de matrices 3-D en NumPy.
Un ejemplo de eliminación de bordes negros horizontales:
# -*- coding: utf-8 -*-
import numpy as np
from keras.preprocessing import image
def RemoveBlackEdge(img):
Args:
img: PIL image instance
Returns:
PIL image instance
"""
width = img.width
img = image.img_to_array(img)
img_without_black = img[~np.all(img == np.zeros((1, width, 3), np.uint8), axis=(1, 2))]
img = image.array_to_img(img_without_black)
return img
Esto es prácticamente lo que quería comentar sobre el uso de CNN para extraer características de imágenes e implementar otros procesamientos de imágenes. Ahora echemos un vistazo a los motores de búsqueda vectorial.
Motor de búsqueda vectorial
El problema de extraer vectores de características de las imágenes se ha resuelto. Entonces los problemas restantes son:
- ¿Cómo almacenar vectores de características?
- ¿Cómo calcular la similitud de los vectores de características, es decir, cómo buscar? El motor de búsqueda vectorial de código abierto Milvus puede resolver estos dos problemas. Hasta ahora, ha estado funcionando bien en nuestro entorno de producción.
Logotipo de Milvus.
Milvus, el motor de búsqueda vectorial
Extraer vectores de características de una imagen está lejos de ser suficiente. También necesitamos gestionar dinámicamente estos vectores de características (adición, eliminación y actualización), calcular la similitud de los vectores y devolver los datos vectoriales en el rango de vecinos más cercanos. El motor de búsqueda vectorial de código abierto Milvus realiza estas tareas bastante bien.
El resto de este artículo describirá prácticas específicas y puntos a tener en cuenta.
1. Requisitos para la CPU
Para usar Milvus, tu CPU debe admitir el conjunto de instrucciones avx2. Para sistemas Linux, usa el siguiente comando para comprobar qué conjuntos de instrucciones admite tu CPU:
cat /proc/cpuinfo | grep flags</code?
Entonces obtienes algo como:
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts
Lo que sigue a flags son los conjuntos de instrucciones que admite tu CPU. Por supuesto, estos son muchos más de los que necesito. Solo quiero ver si se admite un conjunto de instrucciones específico, como avx2. Simplemente agrega un grep para filtrarlo:
cat /proc/cpuinfo | grep flags | grep avx2
Si no se devuelve ningún resultado, significa que este conjunto de instrucciones específico no es compatible. Entonces necesitas cambiar de máquina.
2. Planificación de capacidad
La planificación de capacidad es nuestra primera consideración cuando diseñamos un sistema. ¿Cuántos datos necesitamos almacenar? ¿Cuánta memoria y espacio en disco requieren los datos?
Hagamos unos cálculos rápidos. Cada dimensión de un vector es float32. Un tipo float32 ocupa 4 Bytes. Entonces un vector de 512 dimensiones requiere 2 KB de almacenamiento. Del mismo modo:
- Mil vectores de 512 dimensiones requieren 2 MB de almacenamiento.
- Un millón de vectores de 512 dimensiones requieren 2 GB de almacenamiento.
- 10 millones de vectores de 512 dimensiones requieren 20 GB de almacenamiento.
- 100 millones de vectores de 512 dimensiones requieren 200 GB de almacenamiento.
- Mil millones de vectores de 512 dimensiones requieren 2 TB de almacenamiento.
Si queremos almacenar todos los datos en la memoria, entonces el sistema necesita al menos la capacidad de memoria correspondiente.
Se recomienda que use la herramienta oficial de cálculo de tamaño: Milvus sizing tool.
En realidad, nuestra memoria puede no ser tan grande. (Realmente no importa si no tiene suficiente memoria. Milvus vacía automáticamente los datos al disco.) Además de los datos vectoriales originales, también debemos considerar el almacenamiento de otros datos, como los registros.
3. Configuración del sistema
Para obtener más información sobre la configuración del sistema, consulte la documentación de Milvus:
- Configuración del servidor Milvus: https://milvus.io/docs/v0.10.1/milvus_config.md
4. Diseño de la base de datos
Colección y partición
- Collection también se conoce como tabla.
- Partition se refiere a las particiones dentro de una colección.
La implementación subyacente de partition es en realidad la misma que la de collection, excepto que una partición está dentro de una colección. Pero con las particiones, la organización de los datos se vuelve más flexible. También podemos consultar una partición específica en una colección para lograr mejores resultados de consulta.
¿Cuántas colecciones y particiones podemos tener? La información básica sobre collection y partition está en Metadata. Milvus utiliza SQLite (integración interna de Milvus) o MySQL (requiere conexión externa) para la gestión interna de metadatos. Si usa SQLite de forma predeterminada para gestionar Metadata, sufrirá una pérdida severa de rendimiento cuando el número de colecciones y particiones sea demasiado grande. Por lo tanto, el número total de colecciones y particiones no debe superar 50.000 (Milvus 0.8.0 limitará este número a 4.096). Si necesita establecer un número mayor, se recomienda que use MySQL mediante una conexión externa.
La estructura de datos admitida por la colección y la partición de Milvus es muy simple, es decir, ID + vector. En otras palabras, solo hay dos columnas en la tabla: ID y datos vectoriales.
Nota:
- ID debe ser un entero.
- Debemos asegurarnos de que el ID sea único dentro de una colección en lugar de dentro de una partición.
Filtrado condicional
Cuando usamos bases de datos tradicionales, podemos especificar valores de campo como condiciones de filtrado. Aunque Milvus no filtra exactamente de la misma manera, podemos implementar un filtrado condicional simple usando colecciones y particiones. Por ejemplo, tenemos una gran cantidad de datos de imágenes y los datos pertenecen a usuarios específicos. Entonces podemos dividir los datos en particiones por usuario. Por lo tanto, usar el usuario como condición de filtro es en realidad especificar la partición.
Datos estructurados y mapeo vectorial
Milvus solo admite la estructura de datos ID + vector. Pero en escenarios empresariales, lo que necesitamos son datos estructurados con significado empresarial. En otras palabras, necesitamos encontrar datos estructurados a través de vectores. En consecuencia, necesitamos mantener las relaciones de mapeo entre los datos estructurados y los vectores mediante ID.
ID de datos estructurados <--> tabla de mapeo <--> ID de Milvus
Selección de índice
Puede consultar los siguientes artículos:
- Tipos de índice: https://www.milvus.io/docs/v0.10.1/index.md
- Cómo seleccionar un índice: https://medium.com/@milvusio/how-to-choose-an-index-in-milvus-4f3d15259212
5. Procesamiento de resultados de búsqueda
Los resultados de búsqueda de Milvus son una colección de ID + distancia:
- ID: el ID en una colección.
- Distancia: un valor de distancia de 0 ~ 1 indica el nivel de similitud; cuanto menor sea el valor, más similares serán los dos vectores.
Filtrado de datos cuyo ID es -1
Cuando el número de colecciones es demasiado pequeño, los resultados de búsqueda pueden contener datos cuyo ID es -1. Debemos filtrarlos nosotros mismos.
Paginación
La búsqueda de vectores es bastante diferente. Los resultados de la consulta se ordenan en orden descendente de similitud, y se seleccionan los resultados más similares (topK) (topK lo especifica el usuario en el momento de la consulta).
Milvus no admite paginación. Necesitamos implementar la función de paginación nosotros mismos si la necesitamos para el negocio. Por ejemplo, si tenemos diez resultados en cada página y solo queremos mostrar la tercera página, debemos especificar que topK = 30 y devolver solo los últimos diez resultados.
Umbral de similitud para el negocio
La distancia entre los vectores de dos imágenes está entre 0 y 1. Si queremos decidir si dos imágenes son similares en un escenario de negocio específico, necesitamos especificar un umbral dentro de este rango. Las dos imágenes son similares si la distancia es menor que el umbral, o son bastante diferentes entre sí si la distancia es mayor que el umbral. Debe ajustar el umbral para satisfacer sus propias necesidades de negocio.
Este artículo está escrito por rifewang, usuario de Milvus e ingeniero de software de UPYUN. Si le gusta este artículo, le invitamos a venir a saludar @ https://github.com/rifewang.
Sigue leyendo

The Real Bottlenecks in Autonomous Driving — And How AI Infrastructure Can Solve Them
Autonomous driving faces a data bottleneck. Learn how AI-native vector databases like Zilliz solve scale, cost, and insight challenges across AV pipelines.

Announcing VDBBench 1.0: Open-Source VectorDB Benchmarking with Your Real-World Production Workloads
Discover VDBBench 1.0, an open-source tool for benchmarking vector databases with real-world production data, streaming ingestion, and concurrent workloads.

Democratizing AI: Making Vector Search Powerful and Affordable
Zilliz democratizes AI vector search with Milvus 2.6 and Zilliz Cloud for powerful, affordable scalability, cutting costs in infrastructure, operations, and development.



