🚀 Experimente o Zilliz Cloud, o Milvus totalmente gerenciado, gratuitamente—experimente um desempenho 10x mais rápido! Experimente Agora>>

milvus-logo
LFAI
  • Home
  • Blog
  • O sistema de pesquisa por imagem de segunda geração

O sistema de pesquisa por imagem de segunda geração

  • Scenarios
August 11, 2020
Rife Wang

Este artigo é a segunda parte de A jornada para otimizar a pesquisa de imagens em escala de bilhões por UPYUN. Se não viu a primeira, clique aqui.

O sistema de pesquisa por imagem de segunda geração

O sistema de pesquisa por imagem de segunda geração escolhe tecnicamente a solução CNN + Milvus. O sistema baseia-se em vectores de caraterísticas e oferece um melhor apoio técnico.

Extração de caraterísticas

No domínio da visão por computador, a utilização da inteligência artificial tornou-se a corrente principal. Do mesmo modo, a extração de elementos do sistema de pesquisa por imagem de segunda geração utiliza a rede neural convolucional (CNN) como tecnologia subjacente

O termo CNN é difícil de compreender. Aqui concentramo-nos em responder a duas perguntas:

  • O que é que a CNN pode fazer?
  • Porque é que posso utilizar a CNN para uma pesquisa de imagens?

1-meme.jpg 1-meme.jpg

Existem muitos concursos no domínio da IA e a classificação de imagens é um dos mais importantes. A tarefa da classificação de imagens consiste em determinar se o conteúdo da imagem é sobre um gato, um cão, uma maçã, uma pera ou outros tipos de objectos.

O que é que a CNN pode fazer? Pode extrair caraterísticas e reconhecer objectos. Extrai caraterísticas de várias dimensões e mede a proximidade entre as caraterísticas de uma imagem e as caraterísticas de gatos ou cães. Podemos escolher as mais próximas como resultado da identificação, o que indica se o conteúdo de uma imagem específica é sobre um gato, um cão ou outra coisa qualquer.

Qual é a relação entre a função de identificação de objectos da CNN e a pesquisa por imagem? O que pretendemos não é o resultado final da identificação, mas o vetor de caraterísticas extraído de múltiplas dimensões. Os vectores de caraterísticas de duas imagens com conteúdo semelhante devem ser próximos.

Que modelo de CNN devo utilizar?

A resposta é VGG16. Porquê escolhê-lo? Em primeiro lugar, o VGG16 tem uma boa capacidade de generalização, ou seja, é muito versátil. Em segundo lugar, os vectores de caraterísticas extraídos pelo VGG16 têm 512 dimensões. Se houver muito poucas dimensões, a precisão pode ser afetada. Se houver demasiadas dimensões, o custo de armazenar e calcular estes vectores de caraterísticas é relativamente elevado.

A utilização de CNN para extrair caraterísticas de imagem é uma solução comum. Podemos utilizar o VGG16 como modelo e o Keras + TensorFlow para a implementação técnica. Eis o exemplo oficial do 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)

As caraterísticas extraídas aqui são vectores de caraterísticas.

1. Normalização

Para facilitar as operações subsequentes, é frequente normalizarmos as caraterísticas:

O que é usado posteriormente é também o normalizado norm_feat.

2. Descrição da imagem

A imagem é carregada utilizando o 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 facto, é o método TensorFlow chamado pelo Keras. Para mais pormenores, consulte a documentação do TensorFlow. O objeto de imagem final é, na realidade, uma instância de imagem PIL (o PIL utilizado pelo TensorFlow).

3. Conversão de bytes

Em termos práticos, o conteúdo da imagem é frequentemente transmitido através da rede. Por isso, em vez de carregar imagens a partir do caminho, preferimos converter dados de bytes diretamente em objectos de imagem, ou seja, Imagens PIL:

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)

A imagem acima é igual ao resultado obtido pelo método image.load_img. Há duas coisas a ter em atenção:

  • É preciso fazer a conversão RGB.
  • É preciso redimensionar (resize é o segundo parâmetro do load_img method).

4. Processamento do contorno preto

As imagens, como as capturas de ecrã, podem ocasionalmente ter alguns contornos pretos. Estas margens negras não têm qualquer valor prático e causam muita interferência. Por este motivo, a remoção dos limites pretos é também uma prática comum.

Um contorno preto é essencialmente uma linha ou coluna de pixéis onde todos os pixéis são (0, 0, 0) (imagem RGB). Para remover o contorno preto, é necessário encontrar essas linhas ou colunas e eliminá-las. Isto é, de facto, uma multiplicação de matriz 3-D em NumPy.

Um exemplo de remoção de margens pretas horizontais:

# -*- 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

Isto é basicamente o que eu quero falar sobre a utilização da CNN para extrair caraterísticas de imagens e implementar outro processamento de imagens. Agora vamos dar uma vista de olhos aos motores de pesquisa vectoriais.

Motor de pesquisa vetorial

O problema da extração de vectores de caraterísticas das imagens foi resolvido. Os restantes problemas são:

  • Como armazenar vectores de caraterísticas?
  • Como calcular a semelhança dos vectores de caraterísticas, ou seja, como pesquisar? O motor de pesquisa vetorial de código aberto Milvus pode resolver estes dois problemas. Até agora, tem estado a funcionar bem no nosso ambiente de produção.

3-milvus-logo.png 3-milvus-logo.png

Milvus, o motor de pesquisa vetorial

Extrair vectores de caraterísticas de uma imagem está longe de ser suficiente. Também precisamos de gerir dinamicamente estes vectores de caraterísticas (adição, eliminação e atualização), calcular a semelhança dos vectores e devolver os dados dos vectores no intervalo de vizinhança mais próximo. O motor de pesquisa de vectores de código aberto Milvus executa estas tarefas bastante bem.

O resto deste artigo descreverá práticas específicas e pontos a ter em conta.

1. Requisitos para a CPU

Para utilizar o Milvus, o seu CPU tem de suportar o conjunto de instruções avx2. Para sistemas Linux, use o seguinte comando para verificar quais conjuntos de instruções sua CPU suporta:

cat /proc/cpuinfo | grep flags

Então obterá 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

O que se segue às flags são os conjuntos de instruções que o seu CPU suporta. É claro que isso é muito mais do que eu preciso. Eu só quero ver se um conjunto de instruções específico, como avx2, é suportado. Basta adicionar um grep para filtrá-lo:

cat /proc/cpuinfo | grep flags | grep avx2

Se nenhum resultado for obtido, significa que esse conjunto de instruções específico não é suportado. Nesse caso, é preciso mudar de máquina.

2. Planeamento da capacidade

O planeamento da capacidade é a nossa primeira consideração quando concebemos um sistema. Qual a quantidade de dados que precisamos de armazenar? Qual a quantidade de memória e espaço em disco que os dados requerem?

Vamos fazer algumas contas rápidas. Cada dimensão de um vetor é float32. Um tipo float32 ocupa 4 Bytes. Assim, um vetor de 512 dimensões requer 2 KB de armazenamento. Da mesma forma:

  • Mil vectores de 512 dimensões requerem 2 MB de armazenamento.
  • Um milhão de vectores de 512 dimensões requerem 2 GB de armazenamento.
  • 10 milhões de vectores de 512 dimensões requerem 20 GB de armazenamento.
  • 100 milhões de vectores de 512 dimensões requerem 200 GB de armazenamento.
  • Mil milhões de vectores de 512 dimensões requerem 2 TB de armazenamento.

Se quisermos armazenar todos os dados na memória, o sistema precisa de ter, pelo menos, a capacidade de memória correspondente.

Recomenda-se a utilização da ferramenta oficial de cálculo de tamanho: Milvus sizing tool.

Na verdade, a nossa memória pode não ser assim tão grande (se não tivermos memória suficiente, não faz mal). O Milvus descarrega automaticamente os dados para o disco). Para além dos dados vectoriais originais, temos também de considerar o armazenamento de outros dados, como os registos.

3. Configuração do sistema

Para mais informações sobre a configuração do sistema, consulte a documentação do Milvus:

  • Configuração do servidor Milvus: https://milvus.io/docs/v0.10.1/milvus_config.md

4. Desenho da base de dados

Coleção e partição

  • A coleção é também conhecida como tabela.
  • A partição refere-se às partições dentro de uma coleção.

A implementação subjacente da partição é, na verdade, a mesma que a da coleção, exceto que uma partição está sob uma coleção. Mas com as partições, a organização dos dados torna-se mais flexível. Também podemos consultar uma partição específica numa coleção para obter melhores resultados de consulta.

Quantas colecções e partições podemos ter? A informação básica sobre colecções e partições encontra-se em Metadata. O Milvus utiliza o SQLite (integração interna do Milvus) ou o MySQL (requer uma ligação externa) para a gestão interna dos metadados. Se utilizar o SQLite por defeito para gerir os metadados, sofrerá uma grave perda de desempenho quando o número de colecções e partições for demasiado grande. Por conseguinte, o número total de colecções e partições não deve exceder 50 000 (o Milvus 0.8.0 limitará este número a 4 096). Se precisar de definir um número maior, recomenda-se que utilize o MySQL através de uma ligação externa.

A estrutura de dados suportada pela coleção e partição do Milvus é muito simples, ou seja, ID + vector. Por outras palavras, existem apenas duas colunas na tabela: ID e dados do vetor.

Nota:

  • O ID deve ser um número inteiro.
  • Temos de garantir que o ID é único dentro de uma coleção em vez de dentro de uma partição.

Filtragem condicional

Quando utilizamos bases de dados tradicionais, podemos especificar valores de campo como condições de filtragem. Embora o Milvus não filtre exatamente da mesma forma, podemos implementar uma filtragem condicional simples utilizando colecções e partições. Por exemplo, temos uma grande quantidade de dados de imagens e os dados pertencem a utilizadores específicos. Então, podemos dividir os dados em partições por utilizador. Assim, utilizar o utilizador como condição de filtragem é, na realidade, especificar a partição.

Dados estruturados e mapeamento vetorial

O Milvus apenas suporta a estrutura de dados ID + vetor. Mas em cenários empresariais, o que precisamos é de dados estruturados com significado empresarial. Por outras palavras, precisamos de encontrar dados estruturados através de vectores. Por conseguinte, é necessário manter as relações de mapeamento entre os dados estruturados e os vectores através do ID.

structured data ID <--> mapping table <--> Milvus ID

Seleção do índice

Pode consultar os seguintes artigos:

  • Tipos de índices: https://www.milvus.io/docs/v0.10.1/index.md
  • Como selecionar um índice: https://medium.com/@milvusio/how-to-choose-an-index-in-milvus-4f3d15259212

5. Processamento dos resultados da pesquisa

Os resultados de pesquisa do Milvus são uma coleção de ID + distância:

  • ID: o ID numa coleção.
  • Distância: um valor de distância de 0 ~ 1 indica o nível de semelhança; quanto menor for o valor, mais semelhantes são os dois vectores.

Filtragem de dados cujo ID é -1

Quando o número de colecções é demasiado pequeno, os resultados da pesquisa podem conter dados cujo ID é -1. Temos de os filtrar por nós próprios.

Paginação

A pesquisa de vectores é bastante diferente. Os resultados da consulta são ordenados por ordem decrescente de semelhança e os resultados mais semelhantes (topK) são selecionados (topK é especificado pelo utilizador no momento da consulta).

O Milvus não suporta a paginação. Temos de implementar a função de paginação por nós próprios se precisarmos dela para fins comerciais. Por exemplo, se tivermos dez resultados em cada página e só quisermos mostrar a terceira página, temos de especificar que topK = 30 e devolver apenas os últimos dez resultados.

Limiar de semelhança para negócios

A distância entre os vectores de duas imagens situa-se entre 0 e 1. Se quisermos decidir se duas imagens são semelhantes num cenário comercial específico, temos de especificar um limiar dentro deste intervalo. As duas imagens são semelhantes se a distância for inferior ao limiar, ou são bastante diferentes uma da outra se a distância for superior ao limiar. É necessário ajustar o limiar de acordo com as necessidades da sua empresa.

Este artigo foi escrito por rifewang, utilizador de Milvus e engenheiro de software da UPYUN. Se gostou deste artigo, venha dizer olá @ https://github.com/rifewang.

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started

Like the article? Spread the word

Continue Lendo