ищем дубликаты изображений на основе Milvus с индексом FAISS внутри / Хабр
В user-generated проектах часто приходится бороться с дубликатами, а для нас это особенно актуально, так как основной контент мобильного приложения, которое я разрабатываю, — это изображения, которые постятся десятками тысяч ежедневно. Для поиска повторов мы написали отдельную систему, чтобы облегчить процесс и сэкономить море времени.
Под катом рассмотрим используемые инструменты, а потом перейдём к примеру реализации.
Свёрточная нейронная сеть (СNN)
Существует огромное количество различных алгоритмов поисков дубликатов, каждый со своими плюсами и минусами. Один из таких — поиск наиболее похожих (близких) векторов, полученных с помощью CNN-сетей.
После классификации изображения через CNN-сеть на выходе получается вектор «того, что увидела сеть на изображении». В теории такой способ должен быть менее чувствительным к кропу, но ложных похожих изображений будет больше в сравнении с более точными методами.
Есть и другой недостаток. На выходе классификации получается большой вектор (2048 float для resnet152), который где-то нужно хранить и иметь возможность за разумный промежуток времени найти все N похожих векторов для искомого — что само по себе уже непросто.
FAISS
Поиск наиболее близких векторов — частая задача, для решения которой уже есть отличные инструменты. Здесь лидером считается библиотека FAISS от Facebook. Она использует эффективную кластеризацию векторов, позволяя организовывать поиск даже для векторов, которые не помещаются в RAM.
Но с FAISS напрямую работать не очень удобно. Это не база данных, нельзя туда просто сохранить вектор и запросить похожий (к тому же, после создания индекса его можно только пересоздать). Поэтому для промышленной эксплуатации нужно строить свою обвязку вокруг системы индексации.
Milvus
Для этого есть весьма перспективный проект Milvus, который по дизайну сильно напоминает Elasticsearch. Отличие только в том, что Elasticsearch построен вокруг индекса lucene, а в Milvus вся архитектура выстроена вокруг индекса FAISS.
Структура коллекций тоже схожа:
Для каждой коллекции можно создать несколько партиций, по которым потом будет ограничиваться поиск. Партиция состоит из сегментов, которые представляют из себя простой набор файлов с id, исходными индексами и служебными данными.
Информация о коллекциях, партициях и сегментах хранится в отдельной SQL-базе. Для standalone-запуска используется встроенный SQLite, а ещё есть возможность использовать внешнюю MySQL-базу.
Проект Milvus находится в активной разработке (текущая версия 0.11.0). Пока что в нём нет репликации данных, как и возможности использовать другие SQL (или NoSQL) базы в качестве хранилища метаинформации. Поэтому пока для HA-решений можно использовать только схему с двумя экземплярами с общим хранилищем: один будет запущен, а другой — «спать». Для масштабирования можно будет использовать Mishards, но в 0. 11.0 он сломан.
Кроме того, в 0.11.0 появилась возможность вместе с самим вектором и id сохранять в коллекцию дополнительные данные. Правда, пока без дополнительных индексов для них, но с возможностью поиска.
С точки зрения использования, Milvus выглядит как обычная внешняя база данных. Есть API (gRPC-клиент и набор http-методов) для сохранения и поиска вектора, управления коллекциями и индексами, а также для получения информации обо всех сущностях.
При создании коллекции можно указать максимальное количество записей в сегменте (segment_row_limit). Если превысить этот лимит, то Milvus начнёт строить индекс FAISS. С этим связана одна из особенностей Milvus: для всех добавляемых векторов, по которым ещё не создан индекс, поиск будет работать на основе полного перебора. Поэтому при больших значениях segment_row_limit будет много записей, для которых индекс ещё не построен (он также влияет ещё и на то, сколько будет созданных сегментов для коллекции). Для поиска похожих векторов в коллекции необходимо сделать поиск в каждом сегменте — и чем их больше, тем дольше поиск.
Обратите внимание, новый созданный сегмент не набивается до лимита при добавлении новых записей. Вместо этого происходит постепенное объединение сегментов по принципу игры 2048 (до тех пор пока размер не превысит лимит). Поэтому при указании большего значения segment_row_limit может оказаться много меньших сегментов, для которых нет индекса, а значит поиск по ним будет медленным.
Несмотря на все особенности, поиск по векторам работает быстро. Архитектура индексов FAISS и самого Milvus позволяет за раз искать одновременно значения по нескольким векторам. И на практике последовательный поиск двух векторов будет существенно медленнее поиска обоих векторов за раз.
Реализация поиска дубликатов
Milvus можно запускать как в CPU-версии, так и в GPU. Первую лучше всего использовать на процессорах, которые поддерживают инструкцию AVX512. Для этого достаточно просто запустить контейнер:
docker run -d --rm --name milvusdb -p 19530:19530 -p 19121:19121 \ milvusdb/milvus:0. 11.0-cpu-d101620-4c44c0
В данном случае 19530 будет портом для gRPC-клиента, а 19121 — для http API.
В качестве CNN-сети можно взять любую из предобученных (или обучить самому) — результаты могут немного отличаться. В данном примере будем использовать предобученный resnet152:
model = models.resnet152(pretrained=True)
Вектор будем снимать со слоя `avgpool`:
layer = model._modules.get('avgpool')
А сам вектор получать с помощью hook:
vector = torch.zeros(2048) def copy_data(m, i, o): vector.copy_(torch.reshape(o.data, (1, 2048))[0]) hook = layer.register_forward_hook(copy_data) model(prepared_image) hook.remove()
Полный код получения вектора выглядит так:
import numpy as np import torch import torchvision.models as models import torchvision.transforms as transforms from torch. autograd import Variable from PIL import Image model = models.resnet152(pretrained=True) layer = model._modules.get('avgpool') model.eval() pipeline = [ transforms.Resize(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ] def _prepare_Image(img: Image) -> Variable: raw = img for action in pipeline: raw = action(raw) return Variable(raw.unsqueeze(0)) def image_vectorization(image_path: str) -> np.ndarray: img = Image.open(image_path) prepared_image = _prepare_Image(img) vector = torch.zeros(2048) def copy_data(m, i, o): vector.copy_(torch.reshape(o.data, (1, 2048))[0]) hook = layer.register_forward_hook(copy_data) model(prepared_image) hook.remove() # vector normalization norm_vector = vector / torch.norm(vector) return np.array(norm_vector)
Теперь понадобится клиент для работы с Milvus. Можно взять любой из поддерживаемых (Python, Java, Go, Rest, C++). Возьмём Java-клиент и напишем пример на Kotlin. Почему? А почему бы и нет.
Подключаем Milvus SDK:
implementation("io.milvus:milvus-sdk-java:0.9.0")
Создаём подключение к Milvus:
val connectParam = ConnectParam.Builder() .withHost("localhost") .withPort(19530) .build() val client = MilvusGrpcClient(connectParam)
Создаём коллекцию под 2048 вектор:
val collectionMapping = CollectionMapping.create(collectionName) .addVectorField("float_vec", DataType.VECTOR_FLOAT, 2048) //выключаем автосоздание id .setParamsInJson(JsonBuilder() .param("auto_id", false) .param("segment_row_limit", segmentRowLimit) .build() ) client.createCollection(collectionMapping)
Создаём IVF_SQ8 индекс:
Index. create(collectionName, "float_vec") .setIndexType(IndexType.IVF_SQ8) .setMetricType(MetricType.L2) .setParamsInJson(JsonBuilder() .param("nlist", 16384) .build() ) client.createIndex(index)
Сохраняем несколько векторов в коллекцию:
InsertParam.create(collectionName) .setEntityIds(listOf(1L, 2L)) .addVectorField("float_vec", DataType.VECTOR_FLOAT, listOf(vector1, vector2)) client.insert(insertParam) client.flush(collectionName) // чтобы сразу можно было найти вектор
Ищем ранее сохранённый вектор:
val dsl = JsonBuilder().param( "bool", mapOf( "must" to listOf( mapOf( "vector" to mapOf( "float_vec" to mapOf( "topk" to 10, "metric_type" to MetricType. L2, "type" to "float", "query" to listOf(vector1), "params" to mapOf("nprobe" to 50) ) ) ) ) ) ).build() val searchParam = SearchParam.create(collectionName) .setDsl(dsl) val result = client.search(searchParam) println(result.queryResultsList[0].map { it.entityId to it.distance })
Если всё работает и правильно настроено, то вернётся похожий результат:
[(1, 0.0), (2, 0.2)]
Для первого вектора расстояние L2 с самим собой будет 0, а с другим вектором — больше 0.
Всё вышеприведённое, конечно, только наброски, но этого достаточно, чтобы попробовать создать Python-сервис для классификации и получения вектора. И либо для него накрутить API для сохранения и поиска векторов, либо сделать в отдельном сервисе (например, на Kotlin), который будет получать вектор и сохранять его уже в Milvus самостоятельно.
Спасибо всем, кто дочитал до конца, надеюсь, вы нашли для себя что-то новое. А если вас заинтересовал проект Milvus, то можете поддержать его на Github.
Альбом «Milvus» (Hear & Now) в Apple Music
Альбом «Milvus» (Hear & Now) в Apple MusicBassa Marea
Levante
Coccobello
Baiadriatica
Milvus
Zanziblu
Abisso
- Algeria
- Angola
- Armenia
- Azerbaijan
- Bahrain
- Benin
- Botswana
- Cameroun
- Cape Verde
- Chad
- Côte d’Ivoire
- Congo, The Democratic Republic Of The
- Egypt
- Eswatini
- Gabon
- Gambia
- Ghana
- Guinea-Bissau
- India
- Iraq
- Israel
- Jordan
- Kenya
- Kuwait
- Lebanon
- Liberia
- Libya
- Madagascar
- Malawi
- Mali
- Mauritania
- Mauritius
- Morocco
- Mozambique
- Namibia
- Niger (English)
- Nigeria
- Oman
- Qatar
- Congo, Republic of
- Rwanda
- Saudi Arabia
- Senegal
- Seychelles
- Sierra Leone
- South Africa
- Sri Lanka
- Tajikistan
- Tanzania, United Republic Of
- Tunisia
- Turkmenistan
- United Arab Emirates
- Uganda
- Yemen
- Zambia
- Zimbabwe
- Australia
- Bhutan
- Cambodia
- 中国大陆
- Fiji
- 香港
- Indonesia (English)
- 日本
- Kazakhstan
- 대한민국
- Kyrgyzstan
- Lao People’s Democratic Republic
- 澳門
- Malaysia (English)
- Maldives
- Micronesia, Federated States of
- Mongolia
- Myanmar
- Nepal
- New Zealand
- Papua New Guinea
- Philippines
- Singapore
- Solomon Islands
- 台灣
- Thailand
- Tonga
- Turkmenistan
- Uzbekistan
- Vanuatu
- Vietnam
- Armenia
- Österreich
- Belarus
- Belgium
- Bosnia and Herzegovina
- Bulgaria
- Croatia
- Cyprus
- Czech Republic
- Denmark
- Estonia
- Finland
- France (Français)
- Georgia
- Deutschland
- Greece
- Hungary
- Iceland
- Ireland
- Italia
- Kosovo
- Latvia
- Lithuania
- Luxembourg (English)
- Malta
- Moldova, Republic Of
- Montenegro
- Nederland
- North Macedonia
- Norway
- Poland
- Portugal (Português)
- Romania
- Россия
- Serbia
- Slovakia
- Slovenia
- España
- Sverige
- Schweiz
- Turkey
- Ukraine
- United Kingdom
- Anguilla
- Antigua and Barbuda
- Argentina (Español)
- Bahamas
- Barbados
- Belize
- Bermuda
- Bolivia (Español)
- Brasil
- Virgin Islands, British
- Cayman Islands
- Chile (Español)
- Colombia (Español)
- Costa Rica (Español)
- Dominica
- República Dominicana
- Ecuador (Español)
- El Salvador (Español)
- Grenada
- Guatemala (Español)
- Guyana
- Honduras (Español)
- Jamaica
- México
- Montserrat
- Nicaragua (Español)
- Panamá
- Paraguay (Español)
- Perú
- St. Kitts and Nevis
- Saint Lucia
- St. Vincent and The Grenadines
- Suriname
- Trinidad and Tobago
- Turks and Caicos
- Uruguay (English)
- Venezuela (Español)
- Canada (English)
- Canada (Français)
- United States
- Estados Unidos (Español México)
- الولايات المتحدة
- США
- 美国 (简体中文)
- États-Unis (Français France)
- 미국
- Estados Unidos (Português Brasil)
- Hoa Kỳ
- 美國 (繁體中文台灣)
Введение Документация Milvus
На этой странице вы найдете обзор Milvus, ответив на несколько вопросов. Прочитав эту страницу, вы узнаете, что такое Milvus и как он работает, а также основные концепции, зачем использовать Milvus, поддерживаемые индексы и метрики, примеры приложений, архитектуру и соответствующие инструменты.
Что такое база данных векторов Milvus?
Milvus был создан в 2019 году с единственной целью: хранить, индексировать и управлять массивными векторами встраивания, сгенерированными глубокими нейронными сетями и другими моделями машинного обучения (ML).
Поскольку база данных специально разработана для обработки запросов к входным векторам, она способна индексировать векторы в триллионном масштабе. В отличие от существующих реляционных баз данных, которые в основном работают со структурированными данными в соответствии с заранее определенным шаблоном, Milvus разработан снизу вверх для обработки векторов внедрения, преобразованных из неструктурированных данных.
По мере роста и развития Интернета неструктурированные данные становились все более и более распространенными, включая электронные письма, документы, данные датчиков IoT, фотографии Facebook, белковые структуры и многое другое. Чтобы компьютеры могли понимать и обрабатывать неструктурированные данные, они преобразуются в векторы с использованием методов встраивания. Milvus хранит и индексирует эти векторы. Milvus может анализировать корреляцию между двумя векторами, вычисляя их расстояние подобия. Если два вектора встраивания очень похожи, это означает, что исходные источники данных также похожи.
Рабочий процесс Милвуса.
Ключевые понятия
Если вы новичок в мире баз данных векторов и поиска по сходству, прочитайте следующее объяснение ключевых понятий, чтобы лучше понять.
Узнайте больше о глоссарии Milvus.
Неструктурированные данные
Неструктурированные данные, включая изображения, видео, аудио и естественный язык, — это информация, которая не соответствует заранее определенной модели или способу организации. На этот тип данных приходится около 80% мировых данных, и его можно преобразовать в векторы с использованием различных моделей искусственного интеллекта (ИИ) и машинного обучения (МО).
Встраивание векторов
Встраивание вектора — это абстракция признаков неструктурированных данных, таких как электронные письма, данные датчиков IoT, фотографии Instagram, белковые структуры и многое другое. С математической точки зрения вектор встраивания представляет собой массив чисел с плавающей запятой или двоичных чисел. Современные методы встраивания используются для преобразования неструктурированных данных в векторы встраивания.
Поиск сходства векторов
Поиск сходства векторов — это процесс сравнения вектора с базой данных для поиска векторов, наиболее похожих на вектор запроса. Алгоритмы поиска приближенных ближайших соседей (ANN) используются для ускорения процесса поиска. Если два вектора встраивания очень похожи, это означает, что исходные источники данных также похожи.
Почему Мильвус?
- Высокая производительность при проведении векторного поиска по массивным наборам данных.
- Сообщество разработчиков, предлагающее многоязычную поддержку и набор инструментов.
- Облачная масштабируемость и высокая надежность даже в случае сбоя.
- Гибридный поиск достигается за счет объединения скалярной фильтрации с поиском по сходству векторов.
Какие индексы и показатели поддерживаются?
Индексы — это организационная единица данных. Вы должны объявить тип индекса и показатель подобия, прежде чем сможете искать или запрашивать вставленные объекты. Если вы не укажете тип индекса, Milvus по умолчанию будет использовать поиск методом полного перебора.
Типы индексов
Большинство типов векторных индексов, поддерживаемых Milvus, используют метод приближенного поиска ближайших соседей (ANNS), в том числе: небольшой миллионный набор данных.
Дополнительные сведения см. в разделе «Указатель векторов».
Метрики подобия
В Milvus метрики подобия используются для измерения сходства между векторами. Выбор хорошей метрики расстояния помогает значительно повысить производительность классификации и кластеризации. В зависимости от форм входных данных для оптимальной производительности выбирается конкретная метрика сходства.
Метрики, которые широко используются для встраивания с плавающей запятой, включают:
- Евклидово расстояние (L2) : Эта метрика обычно используется в области компьютерного зрения (CV).
- Внутренний продукт (IP) : Этот показатель обычно используется в области обработки естественного языка (NLP).
Метрики, которые широко используются для двоичных вложений, включают:
- Hamming : Эта метрика обычно используется в области обработки естественного языка (NLP).
- Jaccard : Этот показатель обычно используется в области поиска молекулярного сходства.
- Tanimoto : Этот показатель обычно используется в области поиска молекулярного сходства.
- Суперструктура : Этот показатель обычно используется для поиска подобной суперструктуры молекулы.
- Подструктура : Этот показатель обычно используется для поиска похожей подструктуры молекулы.
Дополнительную информацию см. в разделе Метрики сходства.
Примеры приложений
Milvus упрощает добавление поиска по сходству в ваши приложения. Примеры приложений Milvus включают:
- Поиск по сходству изображений: изображения доступны для поиска и мгновенно возвращают наиболее похожие изображения из огромной базы данных.
- Поиск по сходству видео: путем преобразования ключевых кадров в векторы и последующей передачи результатов в Milvus можно искать и рекомендовать миллиарды видео почти в реальном времени.
- Поиск по сходству аудио: быстрый поиск огромных объемов аудиоданных, таких как речь, музыка, звуковые эффекты и поверхностно похожие звуки.
- Поиск молекулярного сходства: молниеносно быстрый поиск сходства, поиск субструктуры или поиск суперструктуры для указанной молекулы.
- Система рекомендаций: Рекомендуйте информацию или продукты на основе поведения и потребностей пользователей.
- Система ответов на вопросы: интерактивный цифровой чат-бот QA, который автоматически отвечает на вопросы пользователей.
- Классификация последовательности ДНК: Точно определите классификацию гена за миллисекунды, сравнив аналогичную последовательность ДНК.
- Система поиска текста: Помогите пользователям найти нужную им информацию, сравнивая ключевые слова с базой данных текстов.
Дополнительные сценарии применения Milvus см. в руководствах Milvus и Milvus Adopters.
Являясь облачной векторной базой данных, Milvus по своему дизайну разделяет хранение и вычисления. Для повышения эластичности и гибкости все компоненты Milvus не имеют состояния.
Система разбита на четыре уровня:
- Уровень доступа: Уровень доступа состоит из группы прокси-серверов без сохранения состояния и служит передним уровнем системы и конечной точкой для пользователей.
- Служба координатора: Служба координатора назначает задачи рабочим узлам и функционирует как мозг системы.
- Рабочие узлы: рабочие узлы функционируют как руки и ноги и являются немыми исполнителями, которые следуют инструкциям службы координатора и выполняют инициируемые пользователем команды DML/DDL.
- Хранилище: Хранилище является основой системы и отвечает за сохранность данных. Он включает метахранилище, брокер журналов и хранилище объектов.
Дополнительные сведения см. в разделе Обзор архитектуры.
Архитектура Милвуса.
Инструменты разработчика
Milvus поддерживается богатыми API и инструментами для облегчения DevOps.
Доступ к API
У Milvus есть клиентские библиотеки, обернутые поверх Milvus API, которые можно использовать для программной вставки, удаления и запроса данных из кода приложения:
- PyMilvus
- Пакет SDK для Node.js
- Go SDK
- Пакет SDK для Java
Мы работаем над включением новых клиентских библиотек. Если вы хотите внести свой вклад, перейдите в соответствующий репозиторий проекта Milvus.
Инструменты экосистемы Milvus
Экосистема Milvus предоставляет полезные инструменты, включая:
- Milvus CLI
- Attu, графическая система управления для Milvus.
- MilvusDM (Milvus Data Migration), инструмент с открытым исходным кодом, разработанный специально для импорта и экспорта данных с помощью Milvus.
- Инструмент определения размера Milvus, помогающий оценить необработанный размер файла, объем памяти и стабильный размер диска, необходимые для определенного количества векторов с различными типами индексов.
Что дальше
- Начните с трехминутного руководства:
- Привет Милвус
- Установите Milvus для тестовой или производственной среды:
- Предварительные условия для установки
- Установка Milvus Standalone
- Установить кластер Milvus
- Если вы заинтересованы в более подробном изучении деталей конструкции Milvus:
- Читать об архитектуре Milvus
Установка Milvus Standalone с помощью Docker Compose Документация Milvus
Milvus OperatorHelmDocker Compose
В этом разделе описывается установка автономного Milvus с помощью Docker Compose.
Предварительные условия
Перед установкой проверьте требования к аппаратному и программному обеспечению.
Загрузите файл
YAML
Загрузите milvus-standalone-docker-compose.yml
и сохраните его как docker-compose.yml
вручную или с помощью следующей команды.
$ wget https://github.com/milvus-io/milvus/releases/download/v2.2.8/milvus-standalone-docker-compose.yml -O docker-compose.yml
Запустите Milvus
В том же каталоге, где находится файл docker-compose.yml
, запустите Milvus, выполнив:
$ sudo docker-compose up -d
Если в вашей системе установлен Docker Compose V2 вместо V1, используйте docker compose
вместо docker-compose
. Проверьте, так ли это с $ docker compose version
. Читайте здесь для получения дополнительной информации.
Создание milvus-etcd ... выполнено Создание milvus-minio... готово Создание milvus-standalone... сделано
Теперь проверьте, запущены ли контейнеры.
$ sudo docker-compose ps
После запуска автономной службы Milvus будут запущены три док-контейнера, включая автономную службу Milvus и две ее зависимости.
Имя Команда Состояние Порты -------------------------------------------------- -------------------------------------------------- ---------------- milvus-etcd etcd -advertise-client-url ... Up 2379/TCP, 2380/TCP milvus-minio /usr/bin/docker-entrypoint ... Up (здоровый) 9000/tcp milvus-standalone /tini -- milvus работает автономно Up 0.0.0.0:19530->19530/tcp, 0.0.0.0:9091->9091/tcp
Остановить Milvus
Чтобы остановить автономный Milvus, запустите:
sudo docker-compose down
Чтобы удалить данные после остановки Milvus, выполните:
sudo rm -rf volumes
Что дальше
Установив Milvus, вы сможете:
Проверьте Hello Milvus, чтобы запустить пример кода с различными SDK и посмотреть, что может сделать Milvus.
Изучите основные операции Milvus:
- Подключиться к серверу Milvus
- Создать коллекцию
- Создать раздел
- Вставить данные
- Провести векторный поиск
Изучите MilvusDM, инструмент с открытым исходным кодом, предназначенный для импорта и экспорта данных в Milvus.
Milvus: Vector database — Milvus