Cómo implementar la búsqueda por similitud de imágenes en Elasticsearch

blog-thumb-website-search.png

Conoce cómo implementar la búsqueda por similitud de imágenes en Elastic en solo unos pocos pasos. Comienza a configurar el entorno de la aplicación, luego importa el modelo NLP y finalmente completa la generación de incrustaciones para tu conjunto de imágenes.

Obtén una descripción general de la búsqueda por similitud de imágenes con Elastic >> 

Cómo configurar tu entorno

El primer paso es configurar el entorno para tu aplicación. Los requisitos generales incluyen:

  • Git
  • Python 3.9
  • Docker
  • Cientos de imágenes

Es importante utilizar cientos de imágenes para garantizar los mejores resultados.

Ve a la carpeta de trabajo y consulta el código del repositorio creado. Luego navega a la carpeta del repositorio.

$ git clone https://github.com/radoondas/flask-elastic-image-search.git
$ cd flask-elastic-image-search

Debido a que utilizarás Python para ejecutar el código, debes asegurarte de que se cumplan todos los requisitos y que el entorno esté listo. Ahora crea el entorno virtual e instala todas las dependencias.

$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install -r requirements.txt

Cluster de Elasticsearch y modelo de incrustación

Inicia sesión en tu cuenta para activar un cluster de Elasticsearch. Configura un pequeño cluster con: 

  • Un nodo caliente con 2 GB de memoria
  • Un nodo de ML (machine learning) con 4 GB de memoria (el tamaño de este nodo es importante ya que el modelo NLP que importarás a Elasticsearch consume aprox. 1.5 GB de memoria).

Una vez que tu implementación esté lista, ve a Kibana y verifica la capacidad de tus nodos de machine learning. Verás un nodo de aprendizaje automático en la vista. No hay ningún modelo cargado en este momento.

Carga el modelo de incrustación CLIP desde OpenAI usando la biblioteca de Eland. Eland es un cliente Elasticsearch de Python para explorar y analizar datos en Elasticsearch y puede manejar tanto texto como imágenes. Utilizarás este modelo para generar incrustaciones a partir de la entrada de texto y la búsqueda de las imágenes coincidentes. Encuentra más detalles en la documentación de la biblioteca de Eland.

Para el siguiente paso, necesitarás el endpoint de Elasticsearch. Puedes conseguirlo desde la consola del cloud de Elasticsearch en la sección de detalles de despliegue.

Usando la URL del endpoint, ejecuta el siguiente comando en el directorio raíz del repositorio. El cliente de Eland se conectará al cluster de Elasticsearch y cargará el modelo en el nodo de machine learning. Haces referencia a tu URL de cluster real con el parámetro –url; por ejemplo, a continuación refiere a  "image-search.es.europe-west1.gcp.cloud.es.io" como URL de cluster.

--url https://elastic:<password>@image-search.es.europe-west1.gcp.cloud.es.io:443

Introduce el comando de importación de Eland.

$ eland_import_hub_model --url https://elastic:<password>@<URL>:443 \
  --hub-model-id sentence-transformers/clip-ViT-B-32-multilingual-v1 \
  --task-type text_embedding --ca-certs app/conf/ess-cloud.cer \
  --start

La salida será similar a la siguiente:

2022-12-12 13:40:52,308 INFO : Establishing connection to Elasticsearch
2022-12-12 13:40:52,327 INFO : Connected to cluster named 'image-search-8.6.1' (version: 8.5.3)
2022-12-12 13:40:52,328 INFO : Loading HuggingFace transformer tokenizer and model 'sentence-transformers/clip-ViT-B-32-multilingual-v1'
2022-12-12 13:41:03,032 INFO : Creating model with id 'sentence-transformers__clip-vit-b-32-multilingual-v1'
2022-12-12 13:41:03,050 INFO : Uploading model definition
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 129/129 [00:42<00:00,  3.01 parts/s]
2022-12-12 13:41:45,902 INFO : Uploading model vocabulary
2022-12-12 13:41:46,120 INFO : Starting model deployment
2022-12-12 13:41:52,825 INFO : Model successfully imported with id 'sentence-transformers__clip-vit-b-32-multilingual-v1'

La carga puede tardar unos minutos dependiendo de tu conexión. Cuando hayas terminado, consulta la lista de modelos entrenados en la página de machine learning de Kibana: Menu (Menú) -> Analytics (Analíticas) -> Machine Learning -> Model management (Manejo de modelos) ->Trained models (Modelos entrenados). Verifica que el modelo de Clip de NLP esté en el estado "iniciado".

Si recibes un mensaje en la pantalla (se requiere sincronización de trabajo de ML y modelo entrenado), haz clic en el enlace para sincronizar modelos.

Cómo crear incrustaciones de imágenes

Después de configurar el cluster de Elasticsearch e importar el modelo de incrustación, debes vectorizar los datos de tu imagen y crear incrustaciones de imágenes para cada imagen en tu conjunto de datos.

Para crear incrustaciones de imágenes, usa un script de Python simple. Puedes encontrar el script aquí: create-image-embeddings.py. El script recorrerá el directorio de tus imágenes y generará incrustaciones de imágenes individuales. Creará el documento con el nombre y la ruta relativa y lo guardará en un índice de Elasticsearch "my-image-embeddings" utilizando el mapeo proporcionado 

Pon todas tus imágenes (fotos) en la carpeta "app/static/images". Utiliza una estructura de directorios con subcarpetas para mantener las imágenes organizadas. Una vez que todas las imágenes estén listas, ejecuta el script con algunos parámetros.

Es crucial tener al menos unos cuantos cientos de imágenes para lograr resultados razonables. Tener muy pocas imágenes no dará los resultados esperados, ya que el espacio en el que buscarás será muy pequeño y las distancias para buscar vectores serán muy similares.

En la carpeta image_embeddings, ejecuta el script y usa tus valores para las variables.

$ cd image_embeddings
$ python3 create-image-embeddings.py \
  --es_host='https://image-search.es.europe-west1.gcp.cloud.es.io:443' \
  --es_user='elastic' --es_password=<password> \
  --ca_certs='../app/conf/ess-cloud.cer'

Dependiendo de la cantidad de imágenes, su tamaño, tu CPU y tu conexión de red, esta tarea llevará algún tiempo. Experimenta con una pequeña cantidad de imágenes antes de intentar procesar el conjunto de datos completo.
Una vez que se complete el script, puedes verificar si el índice my-image-embeddings existe y tiene los documentos correspondientes utilizando las herramientas de desarrollo de Kibana.

GET _cat/indices/my-image-embeddings?v

health status index               uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   my-image-embeddings vfA3wOheT1C79R-PceDyXg   1   1       1222            0     24.4mb         12.2mb

Mirando los documentos, verás objetos JSON muy similares (como el ejemplo). Verás el nombre de la imagen, la identificación de la imagen y la ruta relativa dentro de la carpeta de imágenes. Esta ruta se usa en la aplicación frontend para mostrar correctamente la imagen durante la búsqueda.
La parte más importante del documento JSON es "image_embedding" que contiene el vector denso producido por el modelo CLIP. Este vector se utiliza cuando la aplicación busca una imagen o una imagen similar.

{
   "_index": "my-image-embeddings",
   "_id": "_g9ACIUBMEjlQge4tztV",
   "_score": 6.703597,
   "_source": {
     "image_id": "IMG_4032",
     "image_name": "IMG_4032.jpeg",
     "image_embedding": [
       -0.3415695130825043,
       0.1906963288784027,
       .....
       -0.10289803147315979,
       -0.15871885418891907
       ],
     "relative_path": "phone/IMG_4032.jpeg"
   }
}

Utiliza la aplicación Flask para buscar imágenes

Ahora que tu entorno está completamente configurado, puedes dar el siguiente paso y buscar imágenes usando lenguaje natural para encontrar imágenes similares, usando la aplicación Flask que proporcionamos como prueba de concepto. La aplicación web tiene una UI simple que simplifica la búsqueda de imágenes. Puedes acceder al prototipo de la aplicación Flask en este repositorio de GitHub .

La aplicación en segundo plano realiza dos tareas. Después de ingresar el texto de búsqueda en el cuadro de búsqueda, el texto se vectorizará mediante el endpoint _infer de machine learning. Luego, la búsqueda con tu vector denso se ejecutará contra el índice my-image-embeddings con los vectores.

Puedes ver esas dos búsquedas en el ejemplo. La primera llamada a la API usa el endpoint _infer y el resultado es un vector denso.

POST _ml/trained_models/sentence-transformers__clip-vit-b-32-multilingual-v1/deployment/_infer
{
  "docs" : [
    {"text_field": "endless route to the top"}
    ]
}

En la segunda tarea, búsqueda de búsqueda, utilizaremos el vector denso y obtendremos las imágenes ordenadas por puntuación.

GET my-image-embeddings/_search
{
  "knn": {
    "field": "image_embedding",
    "k": 5,
    "num_candidates": 10,
    "query_vector": [
    -0.19898493587970734,
    0.1074572503566742,
    -0.05087625980377197,
    ...
    0.08200495690107346,
    -0.07852292060852051
  ]
  },
  "fields": [
    "image_id", "image_name", "relative_path"

  ],
  "_source": false
}

Para poner en marcha la aplicación Flask, ve a la carpeta raíz del repositorio y configura el archivo .env. Los valores en el archivo de configuración se utilizan para conectarse al cluster de Elasticsearch. Debes insertar valores para las siguientes variables. Estos son los mismos valores utilizados en la generación de incrustación de imágenes.

  • ES_HOST='URL:PORT'
  • ES_USER='elastic'
  • ES_PWD='password'

Cuando esté listo, ejecuta la aplicación Flask en la carpeta principal y espera hasta que se inicie.

# In the main directory 
$ flask run --port=5001

Si se inicia la aplicación, verás un resultado similar al siguiente, que al final indica qué URL debes visitar para acceder a la aplicación.

flask run --port=5001
 * Serving Flask app 'flask-elastic-image-search.py' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5001
Press CTRL+C to quit

¡Felicitaciones! Tu aplicación ahora debería estar en marcha y ser accesible en http://127.0.0.1:5001 a través del navegador de Internet.

Navega a la pestaña de búsqueda de imágenes e ingresa el texto que mejor describe tu imagen. Trata de usar un texto descriptivo o que no sea una palabra clave.

En el siguiente ejemplo, el texto ingresado fue "ruta sin fin hasta la cima". Los resultados se muestran desde nuestro conjunto de datos. Si a un usuario le gusta una imagen en particular en el conjunto de resultados, simplemente haz clic en el botón junto a ella y se mostrarán imágenes similares. Los usuarios pueden hacer esto infinitas veces y crear su propia ruta a través del conjunto de datos de la imagen.

La búsqueda también funciona simplemente subiendo una imagen. La aplicación convertirá la imagen en un vector y buscará una imagen similar en el conjunto de datos. Para hacer esto, navega a la tercera pestaña Imagen similar, carga una imagen del disco y presiona Buscar.

Debido a que el modelo NLP (sentence-transformers/clip-ViT-B-32-multilingual-v1) que usamos en Elasticsearch es multilingüe y admite la inferencia en muchos idiomas, intenta buscar las imágenes en tu propio idioma. Luego verifica los resultados usando también texto en inglés.

Es importante tener en cuenta que los modelos utilizados son modelos genéricos, que son bastante precisos, pero los resultados que obtengas variarán según el caso de uso u otros factores. Si necesitas una mayor precisión, tendrás que adaptar un modelo genérico o desarrollar tu propio modelo; el modelo CLIP solo pretende ser un punto de partida.

Resumen del código

Puedes encontrar el código completo en el repositorio de GitHub. Puedes estar inspeccionando el código en route.py, que implementa la lógica principal de la aplicación. Además de la definición de ruta obvia, debes centrarte en los métodos que definen los endpoints _infer y _search (infer_trained_model y knn_search_images). El código que genera incrustaciones de imágenes se encuentra en el archivo create-image-embeddings.py.

Resumen

Ahora que has configurado la aplicación Flask, ¡puedes buscar en tu propio conjunto de imágenes con facilidad! Elastic proporciona integración nativa de búsqueda de vectores dentro de la plataforma, lo que evita la comunicación con procesos externos. Obtienes la flexibilidad para desarrollar y emplear modelos de incrustación personalizados que puedes haber desarrollado con PyTorch.

La búsqueda semántica de imágenes ofrece los siguientes beneficios en comparación con otros enfoques tradicionales para la búsqueda de imágenes:

  • Mayor precisión: La similitud de vectores captura el contexto y las asociaciones sin depender de las metadescripciones textuales de las imágenes.
  • Mejoras en la experiencia del usuario: Describe lo que estás buscando o proporciona una imagen de muestra, en lugar de adivinar qué palabras clave pueden ser relevantes.
  • Categorización de bases de datos de imágenes: No te preocupes por catalogar tus imágenes: la búsqueda por similitud encuentra imágenes relevantes en una pila de imágenes sin tener que organizarlas.

Si tu caso de uso se basa más en datos de texto, puedes obtener más información sobre cómo implementar la búsqueda semántica y cómo aplicar el procesamiento del lenguaje natural al texto en blogs anteriores. Para los datos de texto, una combinación de similitudes vectoriales con puntuación tradicional de palabras clave presenta lo mejor de ambos mundos.
¿Listo para comenzar? Regístrate en un taller práctico de búsqueda de vectores en nuestro centro de eventos virtual e interactúa con la comunidad en nuestro foro de discusión en línea.