🚀 Попробуйте Zilliz Cloud, полностью управляемый Milvus, бесплатно — ощутите 10-кратное увеличение производительности! Попробовать сейчас>

milvus-logo
LFAI
  • Home
  • Blog
  • Система поиска по изображениям второго поколения

Система поиска по изображениям второго поколения

  • Scenarios
August 11, 2020
Rife Wang

Эта статья - вторая часть статьи "Путешествие к оптимизации поиска изображений в миллиардных масштабах" от UPYUN. Если вы пропустили первую часть, нажмите здесь.

Система поиска по изображениям второго поколения

Система поиска по изображениям второго поколения технически выбирает решение CNN + Milvus. Эта система основана на векторах признаков и обеспечивает лучшую техническую поддержку.

Извлечение признаков

В области компьютерного зрения использование искусственного интеллекта стало мейнстримом. Так и в системе поиска по изображениям второго поколения для извлечения признаков используется конволюционная нейронная сеть (CNN).

Термин CNN сложен для понимания. Здесь мы сосредоточимся на ответах на два вопроса:

  • Что может делать CNN?
  • Почему я могу использовать CNN для поиска изображений?

1-meme.jpg 1-meme.jpg

В области искусственного интеллекта проводится множество соревнований, и классификация изображений - одно из самых важных. Задача классификации изображений - определить, о чем идет речь: о кошке, собаке, яблоке, груше или других типах объектов.

Что может делать CNN? Он может извлекать признаки и распознавать объекты. Он извлекает признаки из нескольких измерений и измеряет, насколько близки признаки изображения к признакам кошек или собак. Мы можем выбрать наиболее близкие из них в качестве результата идентификации, который показывает, о чем идет речь на конкретном изображении - о кошке, собаке или о чем-то еще.

Какая связь между функцией идентификации объектов в CNN и поиском по изображению? Нам нужен не конечный результат идентификации, а вектор признаков, извлеченный из множества измерений. Векторы признаков двух изображений с похожим содержанием должны быть близки.

Какую модель CNN следует использовать?

Ответ - VGG16. Почему стоит выбрать именно ее? Во-первых, VGG16 обладает хорошей способностью к обобщению, то есть она очень универсальна. Во-вторых, векторы признаков, извлекаемые VGG16, имеют 512 измерений. Если измерений очень мало, это может повлиять на точность. Если измерений слишком много, стоимость хранения и вычисления этих векторов признаков будет относительно высокой.

Использование CNN для извлечения признаков изображения является наиболее распространенным решением. В качестве модели мы можем использовать VGG16, а для технической реализации - Keras + TensorFlow. Вот официальный пример 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)

Извлеченные здесь признаки - это векторы признаков.

1. Нормализация

Чтобы облегчить последующие операции, мы часто нормализуем признаки:

То, что используется в дальнейшем, также является нормализованным norm_feat.

2. Описание изображения

Изображение загружается с помощью метода image.load_img из keras.preprocessing:

from keras.preprocessing import image
img_path = 'elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))

Фактически, это метод TensorFlow, вызываемый Keras. Подробнее см. документацию по TensorFlow. Конечный объект изображения на самом деле является экземпляром PIL Image (PIL, используемый TensorFlow).

3. Преобразование байтов

На практике содержимое изображений часто передается по сети. Поэтому вместо загрузки изображений из пути мы предпочитаем преобразовывать байтовые данные непосредственно в объекты изображений, то есть 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)

Приведенное выше изображение img совпадает с результатом, полученным методом image.load_img. Следует обратить внимание на два момента:

  • Вы должны выполнить преобразование RGB.
  • Необходимо изменить размер (resize - второй параметр load_img method).

4. Обработка черной границы

Изображения, например скриншоты, иногда могут иметь довольно много черных границ. Эти черные границы не имеют практической ценности и создают много помех. По этой причине удаление черных границ также является распространенной практикой.

Черная граница - это, по сути, строка или столбец пикселей, где все пиксели имеют значения (0, 0, 0) (RGB-изображение). Чтобы удалить черную границу, нужно найти эти строки или столбцы и удалить их. На самом деле это трехмерное умножение матрицы в NumPy.

Пример удаления горизонтальных черных границ:

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

Это практически все, о чем я хочу рассказать, используя CNN для извлечения особенностей изображений и других способов их обработки. Теперь давайте посмотрим на векторные поисковые системы.

Векторная поисковая система

Проблема извлечения векторов признаков из изображений решена. Остались следующие проблемы:

  • Как хранить векторы признаков?
  • Как вычислять сходство векторов признаков, то есть как искать? Векторная поисковая система Milvus с открытым исходным кодом может решить эти две проблемы. Пока что он хорошо работает в нашей производственной среде.

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

Milvus, векторная поисковая система

Извлечь векторы признаков из изображения далеко не достаточно. Нам также необходимо динамически управлять этими векторами признаков (добавлять, удалять и обновлять), вычислять сходство векторов и возвращать данные векторов в диапазоне ближайших соседей. Векторный поисковик с открытым исходным кодом Milvus неплохо справляется с этими задачами.

В остальной части статьи будут описаны конкретные практики и моменты, на которые следует обратить внимание.

1. Требования к процессору

Чтобы использовать Milvus, ваш процессор должен поддерживать набор инструкций avx2. Для систем Linux используйте следующую команду, чтобы проверить, какие наборы инструкций поддерживает ваш процессор:

cat /proc/cpuinfo | grep flags

В результате вы получите что-то вроде:

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

За флагами следуют наборы инструкций, поддерживаемые вашим процессором. Конечно, это гораздо больше, чем мне нужно. Я просто хочу узнать, поддерживается ли определенный набор инструкций, например avx2. Просто добавьте grep для фильтрации:

cat /proc/cpuinfo | grep flags | grep avx2

Если результат не возвращается, значит, этот конкретный набор инструкций не поддерживается. Тогда вам нужно сменить машину.

2. Планирование мощностей

Планирование емкости - это первое, на что мы обращаем внимание при проектировании системы. Сколько данных нам нужно хранить? Сколько памяти и дискового пространства требуется для этих данных?

Давайте сделаем несколько быстрых математических вычислений. Каждое измерение вектора - float32. Тип float32 занимает 4 байта. Значит, вектор из 512 измерений требует 2 КБ памяти. Аналогично:

  • Тысяча 512-мерных векторов требуют 2 МБ памяти.
  • Один миллион 512-мерных векторов требует 2 ГБ памяти.
  • 10 миллионов 512-мерных векторов требуют 20 ГБ памяти.
  • 100 миллионов 512-мерных векторов требуют 200 ГБ памяти.
  • Один миллиард 512-мерных векторов требует 2 ТБ памяти.

Если мы хотим хранить все данные в памяти, то системе нужен как минимум соответствующий объем памяти.

Рекомендуется использовать официальный инструмент для расчета размера: Milvus sizing tool.

На самом деле наша память может быть не такой уж и большой (это не имеет значения, если у вас недостаточно памяти. Milvus автоматически сбрасывает данные на диск). Помимо исходных векторных данных, нам также необходимо учитывать хранение других данных, таких как журналы.

3. Конфигурация системы

Более подробную информацию о конфигурации системы можно найти в документации по Milvus:

  • Milvus server configuration: https://milvus.io/docs/v0.10.1/milvus_config.md.

4. Проектирование базы данных

Коллекция и раздел

  • Коллекция также известна как таблица.
  • Под разделом понимаются разделы внутри коллекции.

Базовая реализация раздела фактически совпадает с реализацией коллекции, за исключением того, что раздел находится под коллекцией. Но с помощью разделов организация данных становится более гибкой. Мы также можем запрашивать конкретный раздел в коллекции, чтобы добиться лучших результатов запроса.

Сколько у нас может быть коллекций и разделов? Основная информация о коллекциях и разделах находится в метаданных. Для управления внутренними метаданными Milvus использует либо SQLite (внутренняя интеграция Milvus), либо MySQL (требуется внешнее подключение). Если вы используете SQLite по умолчанию для управления метаданными, то при слишком большом количестве коллекций и разделов производительность будет сильно снижена. Поэтому общее число коллекций и разделов не должно превышать 50 000 (в Milvus 0.8.0 это число ограничено 4 096). Если вам необходимо задать большее число, рекомендуется использовать MySQL через внешнее соединение.

Структура данных, поддерживаемая коллекцией и разделом Milvus, очень проста, а именно ID + vector. Другими словами, в таблице всего два столбца: ID и векторные данные.

Примечание:

  • ID должны быть целыми числами.
  • Нам нужно убедиться, что ID уникален в пределах коллекции, а не раздела.

Условная фильтрация

Когда мы используем традиционные базы данных, мы можем указывать значения полей в качестве условий фильтрации. Хотя Milvus не фильтрует точно так же, мы можем реализовать простую условную фильтрацию с помощью коллекций и разделов. Например, у нас есть большой объем данных об изображениях, и эти данные принадлежат определенным пользователям. Тогда мы можем разделить данные на разделы по пользователям. Таким образом, использование пользователя в качестве условия фильтрации фактически является указанием раздела.

Структурированные данные и векторное отображение

Milvus поддерживает только структуру данных ID + вектор. Но в бизнес-сценариях нам нужны структурированные данные, несущие бизнес-смысл. Другими словами, нам нужно найти структурированные данные через векторы. Соответственно, нам нужно поддерживать отношения отображения между структурированными данными и векторами через ID.

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

Выбор индекса

Вы можете обратиться к следующим статьям:

  • Типы индексов: https://www.milvus.io/docs/v0.10.1/index.md
  • Как выбрать индекс: https://medium.com/@milvusio/how-to-choose-an-index-in-milvus-4f3d15259212

5. Обработка результатов поиска

Результаты поиска в Milvus представляют собой коллекцию ID + расстояние:

  • ID: ID в коллекции.
  • Расстояние: значение расстояния 0 ~ 1 указывает на уровень сходства; чем меньше значение, тем более похожи два вектора.

Фильтрация данных, чей ID равен -1

Когда количество коллекций слишком мало, в результатах поиска могут оказаться данные, чей ID равен -1. Мы должны отфильтровать их самостоятельно.

Пагинация

Поиск по векторам осуществляется совершенно иначе. Результаты запроса сортируются в порядке убывания сходства, и выбираются наиболее похожие (topK) результаты (topK задается пользователем во время запроса).

Milvus не поддерживает пагинацию. Если нам нужна функция пагинации, мы должны реализовать ее самостоятельно. Например, если у нас есть десять результатов на каждой странице, а мы хотим отобразить только третью страницу, нам нужно указать topK = 30 и вернуть только последние десять результатов.

Порог сходства для бизнеса

Расстояние между векторами двух изображений находится в диапазоне от 0 до 1. Если мы хотим решить, похожи ли два изображения в конкретном бизнес-сценарии, нам нужно указать порог в этом диапазоне. Два изображения похожи, если расстояние меньше порога, или они сильно отличаются друг от друга, если расстояние больше порога. Вам необходимо настроить порог в соответствии с вашими потребностями.

Эта статья написана rifewang, пользователем Milvus и инженером-программистом UPYUN. Если вам понравилась эта статья, заходите поздороваться с нами на https://github.com/rifewang.

Like the article? Spread the word

Продолжить чтение