Назад
319

Grounding DINO: обучаем модель детекции понимать человеческий язык

319

Введение

Сегодня поговорим о Grounding DINO [GD] — популярной модели, описанной в статье “Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection” [гитхаб].

Давайте разбираться, почему эта модель так популярна и из каких основных компонентов она состоит 😊

Но перед этим — пара комментариев. Во-первых, мы рассмотрим компоненты GD не глубоко, а скорее обзорно (но общая картинка сложится). Во-вторых, сама модель основана на нескольких, довольно сложных моделях. Для более эффективного погружения в тему советуем прочитать и о них:

  1. Исходная модель «DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection» (не путать с известной фейсбуковсй статьей : ”Emerging Properties in Self-Supervised Vision Transformers” [гитхаб] про которую у нас есть пост)
  2. GLIP: ”Grounded Language-Image Pre-training” [гитхаб]. Отсюда было заимствовано много идей.
  3. Семейство моделей DETR: “End-to-End Object Detection with Transformers” [гитхаб, видео на русском].

Итак, приступим к обзору Grounding DINO.

Grounding DINO: что это за модель и чем она хороша?

Для ответа на этот вопрос нам нужно понять, что такое Open-Set Object Detection (OSOD).

Open-Set Object Detection (OSOD) — детектирование объектов, чьих классов нет в обучающей выборке.

Давайте разбираться на примере этой иллюстрации:

Рисунок 1. “Ехали медведи на велосипеде” [рисунок из детской книги “Тараканище” Корнея Чуковского]

Если вы когда-нибудь пользовались “классическими” сетками как Faster-RCNN или YOLO для нестандартных задачек, например, для детектирования медведей на велосипеде — скорее всего, вы замечали следующую проблему: детекция ограничена теми классами, которые уже есть в датасете (Closed-Set Object Detection). То есть медведя детектировать можно, велосипед — можно, а вот медведя на велосипеде (как единое целое) — нет. Тогда приходится либо вводить новый класс, договариваться с бродячим цирком на тему аренды животного и дообучать сетку, либо пилить условие по типу: “если расстояние между центрами медведя и велосипеда не больше, чем половина длины медведя … 😢”.

А что делать, если у нас задачка, как на рисунке ниже?

Рисунок 2. “Ехали медведи на велосипеде” [но уже по версии пикабу]

Интуитивно кажется, что решение есть — сетка же знает, что такое велосипед и что такое медведь!

И выход действительно есть. Оказывается, если добавить еще одну модальность — язык, то все соединится. Этот процесс и называется Grounding. Как упоминалось выше, авторы GD не первыми дошли до мультимодальности в задаче детекции (первыми были GLIP), но у них это, если верить метрикам, получилось лучше.

Как я представляю это у себя в голове (😇): классы (медведь, велосипед) из Closed-Set Object Detection — это векторы базиса (x, y). Язык дает коэффициенты перед векторами. Значит, наша сетка теперь способна детектировать не только x и y, но и любой вектор v = ax + by.

Примечание

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

Пусть в датасете нет класса “звездолет”. Но предобученный текстовый трансформер его знает как “нечто с соплами, крылышками и носом”. Поэтому он выдает разумный эмбеддинг. Таким образом, мы можем найти те классы, которых нет в обучающей выборке (например, в рамках моей задачки модель смогла детектировать ростки подсолнуха). Но тут важна удача 😊

Еще одно примечание

Редкая литература на русском переводит grounding как ‘заземление’, но, по моему мнению, корректнее будет переводить это слово как ‘цементирование’. По аналогии (кто знает, может быть, она и была у авторов GLIP): созданный для CSOD бекбон выдает кирпичики, а язык “цементирует” их и строит здание.

Как работает Grounding DINO?

Итак, переходим ко внутреннему устройству модели. Предупреждаем: в этой части статьи будет много технических подробностей 🙂

Примечание

В целом, можно, конечно, и сразу перейти в конец к ноутбукам. Но если вы хотите глубже во всем разобраться — лучше будет почитать про GLIP, DETR и DINO.

Общий вид

Рисунок 3. Общий вид архитектуры

На первый взгляд все может показаться устрашающим, но поверьте, это не страшнее китайского кода 😊

Давайте обо всем по порядку.

На вход подается пара Текст + Картинка. Текст идет на вход предобученного BERT-образного трансформера, а картинка — на вход предобученного Swin-образного трансформера (Swin [гитхаб] используется, чтобы хорошо работать с объектами разных размеров). В итоге из исходного текста и картинки получаются отдельные эмбеддинги. Теперь мы начнем их женить (см. название исходной статьи про GD). Для этого скормим пару фичероулучшателю.

Замечание

Строго говоря, эмбеддинги можно извлекать как угодно и из чего угодно. Лишь бы это были векторы. В статье экспериментально доказывается, что трансформеры хороши. И это пытаются представить как предсказуемый факт. Ну… мы все понимаем…

Фичероулучшатель

Рисунок 4. Вид фичероулучшателя

Его роль — сделать так, чтобы будущие супруги получше узнали друг друга.

Сначала они проходят через слои self-attention и разбираются в себе, а потом знакомятся друг с другом при помощи механизмов Image-to-Text и Text-to-Image кросс-внимания (в первом используются queries из изображения, а keys и values из текста, во втором наоборот). Механизм внимания помогает определить, какие части изображения релевантны каким частям текста, и наоборот. Таким образом, эмбеддинги взаимно обогащаются друг другом.

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

Language guide query selection

На этом моменте мой внутренний переводчик немного забуксовал 😊

Идея такая: в DETR на выходе получаются object queries, из которых потом строят классы обьектов и bounding boxes. Количество object queries — гиперпараметр. Важно, чтобы оно было больше ожидаемого числа объектов на картинке. Здесь мы хотим сделать нечто похожее, но теперь у нас есть еще и текст! Вспоминаем, что после фичероулучшателя скалярное производение эмбеддингов текста и кусков изображения имеет смысл. И отбираем несколько самых релевантных queries (количество которых — гиперпараметр).

Рисунок 5. Алгоритм выбора queries

Первая строчка в этом алгоритме — попарное скалярное произведение эмбеддингов кусочков изображения и эмбеддингов текста. Вторая — выбор для каждого кусочка изображения наиболее релевантного куска текста (с максимальным значением скалярного произведения). Третья — из полученных наиболее релевантных кусков текста выбор самых релевантных num_query и возвращение им индексов.

Самый просто способ понять эту часть — набрать код для какого-нибудь игрушечного примера и пройтись с дебагером. Мне помогло 😊

С помощью этого алгоритма получаются эмбеддинги анкорных боксов. С ними конкатенируются эмбеддинги содержания (content queries), которые просто выучиваются в процессе обучения.

Рисунок 6. Формирование queries на входе в кроссмодальный декодер

Это довольно тонкий момент, для полного погружения в который рекомендуем посмотреть статью «Conditional DETR for Fast Training Convergence» и особенно исходную статью по DINO: DETR.

Cross-Modality decoder, или кроссмодальный декодер

Рисунок 7. Кроссмодальный декодер

А теперь складываем все вместе. У нас есть отобранные вектора из предыдущей части. Их мы пропускаем через self-attention. Это будут queries для image cross attention, где keys и values — векторы эмбеддингов для кусочков изображений. Потом, используя полученные векторы как queries, мы делаем text-cross attention, где keys и values — эмбеддинги текста (над этой картинкой лучше немного помедитировать, чтобы понять, что это действительно брат знакомого нам декодера из трансформеров). Полученное мы, как и всегда, пропускаем через полносвязный слой. В результате снова получаем вектора queries, но уже улучшенные (и опять вспоминаем про object queries из DETR).

Последняя часть

Здесь мы еще раз очень рекомендуем посмотреть видео про DETR.

Полученные на предыдущем этапе вектора используются для двух целей: восстановить boudning boxes и узнать соответствующие классы. Кроме того, у нас есть эмбеддинги слов (или частей слов, в статье с этим экспериментируют) из исходного запроса.

Затем из всего этого нужно будет составить лосс. Как и в DETR для bounding boxes, здесь используется L1 и GIOU (Generalized Intersection over Union). Как и в GLIP, применяется также Contrastive loss, чтобы выяснить, какому вектору соответствсует какое слово из исходного запроса (вычисляется скалярное произведение между векторами слов после улучшателя и queries). Это дает логиты для каждого токена. И затем для этих логитов вычисляется focal loss. Далее, как и в DETR, венгерским алгоритмом решается задача о назначениях, чтобы сравнить предсказания классов обьектов с ground truth. Снова немного медитируем над картинкой — тогда все должно проясниться.

Еще один важный момент: для трейна можно использовать любой размеченный датасет. Все классы на изображении вмещаются в промт (медведь, велосипед, мяч), это и считается текстовым описанием картинки.

Пара слов о модификации DINO

Вы можете подумать: где grounding — это понятно, а где DINO? Есть и она.

Дело в том, что все предыдущие части статьи — описание модифицированной архитектуры DINO. Более того, авторы GD взяли веса из натренированной сетки, заморозили их и обучили только то, что не вошло в исходную DINO (как они сообщили, GD с нуля обучается довольно тяжело, и такая заморозка очень сильно помогает). Лучше всего вышесказанное представляется с помощью рисунка 8: это иллюстрация из статьи, где белым цветом выделены части исходного DINO, а синим — части, добавленные авторами.

Рисунок 8. Сравнение архитектур DINO и Grounding Dino

Заключение

Итак, давайте подытожим статью плюсами и минусами модели.

Плюсы

  1. Идея совместить разные модальности хороша. И на момент публикации статьи у авторов получилось сделать SOTA на многих датасетах (сейчас, в январе 2024, есть результаты получше, но все равно впечатляет). Кажется, что это только начало большой и плодотворной области.
  2. Легко прикрутить другие задачки, например, сегментацию (что уже сделано).
  3. Можно прикрутить диффузию и заменить обьекты на изображении с помощью промта (см. второй ноутбук чуть ниже).
  4. Как и в остальных DETR-подобных сетках, нет NMS и связанной с этим алгоритмом черной магии.
  5. Очень легко запустить и пощупать.
  6. Достаточно читабельный исходный код.

Минусы

  1. Когда она работает, она работает. А когда не работает — не очень понятно, что делать. Провести анализ ошибок, как минимум, очень нетривиально. Запихивать GD в прод я бы точно не стал.
  2. Не очень ясно, как это все файнтьюнить (здесь можно глянуть вот эту дискуссию в issues).
  3. По большому счету, пока не очень понятно, как это можно использовать в целях заработка. Есть мнение о применении для разметки датасетов.
  4. По опыту моих знакомых, оказалось, что если не обращать внимания на классы, GD очень хорошо находит баундинг боксы. А дальше их можно вырезать и пропускать через какие-нибудь обычные детекторы.

Полезные материалы

В завершение статьи мы оставляем два ноута.

Первый — найденный на просторах интернета туториал для скачивания и запуска GD, а также ее использования для zero shot разметки.

Ноутбук где скачивают, запускают и зерошотят

Второй — детекция и inpainting с помощью Stable Diffustion.

Grounded_sam_colab_demo_filnal.ipynb

Телеграм-канал

DeepSchool

Короткие посты по теории ML/DL, полезные
библиотеки и фреймворки, вопросы с собеседований
и советы, которые помогут в работе

Открыть Телеграм

Увидели ошибку?

Напишите нам в Telegram!