Несколько месяцев назад я был на конференции по розничной торговле. Несколькими неделями ранее мне очень понравилась модель CLIP OpenAI, которая объединяет модели компьютерного зрения с моделями естественного языка (не волнуйтесь, я объясню это позже).

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

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

Поиск сходства

Когда мы говорим о поиске по сходству, мы говорим о поиске похожих объектов в пуле объектов. В области компьютерного зрения нам нравится запускать некоторые нейронные сети для всего набора данных изображений и вычислять из него так называемое встраивание. Я предполагаю, что у вас есть некоторые базовые знания о нейронных сетях, иначе эта статья была бы намного длиннее.

Как вы можете видеть на этом изображении, мы запускаем сверточную нейронную сеть на входном изображении с n-мерным векторным выходом. Если бы мы хотели выполнить какую-то классификацию изображения, нам понадобилась бы дополнительная головка сверху, которая представляет собой просто полносвязный слой с N выходными каналами, каждый из которых представляет класс.

Теперь мы предполагаем, что это n-мерное вложение представляет наше входное изображение и что, если бы мы использовали несколько похожих изображений (например, тот же объект, но с другой точки зрения), мы получили бы подобное вложение. Итак, как мы будем тренировать что-то подобное? К счастью, мы можем сделать это несколькими способами. Во-первых, как упоминалось выше, мы можем просто обучить модель, выполнив классификацию (например, ResNet с классификацией ImageNet) и просто использовать выходные данные последнего слоя перед последним полносвязным слоем. Другой подход заключается в использовании некоторого типа контрастного обучения (например, SimCLR) или подхода неконтролируемого обучения (например, Дино), которые обучены выводить вложения (дополненные) одних и тех же изображений, насколько это возможно (хорошо, это было очень упрощено). , но просто проверьте газеты, если вы заинтересованы в этом, это потрясающе, что они там делают).

Мы только что говорили о сверточных нейронных сетях, но мы не ограничиваемся этой моделью. Другие модели, такие как Vision Transformers, делают то же самое. Мы вычисляем вложение, добавляем к нему голову и обучаем на ней классификатор изображений. Мы можем использовать это вложение, как мы использовали встраивание сверточной нейронной сети.

Но что мы можем сделать с этими вложениями? Как уже упоминалось, мы предполагаем, что эти вложения очень похожи друг на друга, если они представляют один и тот же объект, поэтому идея состоит в том, чтобы вычислить сходство между двумя вложениями, чтобы увидеть, насколько они похожи. Чаще всего мы используем косинусное подобие:

Итак, если мы можем вычислить встраивание изображения с помощью Vision Transformer, почему мы не можем вычислить встраивание текста с помощью Transformer Encoder? Что ж, получается, что можно (бумажный).

КЛИП

Теперь, когда мы знаем, как вычислять вложения и насколько похожи два вложения, мы можем начать понимать, как работает модель CLIP в целом. Идея очень проста, и, честно говоря, иногда я думаю: Почему я не додумался до этого первым :) Но потом я вспоминаю, что даже если бы я додумался до этого первым, я бы никогда не смог обучить базовую модель своим личные деньги.

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

Допустим, у нас есть 8 пар изображение-текст, а встраивание изображения и текста имеет размер 512 (да, оба должны быть одного размера, мы обсудим это позже). Получаем два тензора размером 8×512 (a=8×512, b=8×512). Если мы теперь умножим эти две матрицы путем перестановки второго тензора (a × bᵗ), мы получим матрицу размера 8 × 8. Каждый элемент этой матрицы представляет сходство между одной из заданных пар изображение-текст. Это также означает, что мы хотим, чтобы диагональ была как можно более похожей, а остальные — как можно более разными. Максимизируя диагональ и минимизируя остальные, мы можем одновременно обучать встраиватели изображений и текста проецировать вложения в одно и то же скрытое пространство.

Как мы можем быть уверены, что два встраивающих устройства будут выводить вложения одинаковой размерности (512-мерные в нашем примере)? Ну, мы не можем. Вот почему мы добавим дополнительный полносвязный слой к двум моделям, чтобы проецировать вложения в один и тот же каталог.

Поисковая система одежды

Первой большой проблемой было получить некоторые данные. К счастью, всегда есть бесплатный набор данных. В нашем случае это был некий Zalando Dataset [0], в котором ~8000 текстовых пар изображений изображений платьев и описание на немецком языке. Честно говоря, это был удачный момент, так как конференция проходила в Германии, что облегчило решение о том, что я хочу использовать многоязычную модель CLIP, которую я нашел на Hugging Face.

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

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

# Load necessary data

image_embedder = CLIP.load_image_embedder_model("path_to_weights")
text_embedder = CLIP.load_text_embedder_model("path_to_weights")

def get_embeddings(model, data, preprocess=None):
  if preprocess is not None:
    data = preprocess(data)
  embeddings = model(data)
  embeddings /= text_embedder.norm(2)
  return embeddings

def load_images(path):
  ...

def load_text(path):
  ...

images = load_images("path_to_images")
texts = load_text("path_to_texts")
preprocess_image = ...

# Compute embeddings

image_embeddings = get_embeddings(image_embedder, images, preprocess_image)
text_embeddings = get_embeddings(text_embedder, texts)

# Compute the prediction matrix (only needed when training)

prediction_matrix = image_embeddings @ text_embeddings.T

# Create the faiss Dataset with dim=512

index = faiss.IndexFlatIP(512) # Flat Inner Product
index.add(image_embeddings)

# Search for closest 5 matches

one_new_image = load_images("path_to_one_new_image")
image_one_new_embedding = get_embeddings(image_embedder, 
                                         one_new_image,
                                         preprocess_image)

distance, index = index.search(image_one_new_embedding, 5)

one_new_text = load_text("path_to_one_new_text")
text_one_new_embedding = get_embeddings(text_embedder, one_new_text)

distance, index = index.search(text_one_new_embedding, 5)

Как вы видели в псевдокоде, мы можем найти наиболее подходящее платье, выполнив поиск по изображению или встраиванию текста. Это означает, что мы можем создать наш демонстрационный интерфейс (я бы очень рекомендовал использовать Gradio для создания приложений, это очень просто, но я не буду вдаваться в подробности).

Этот интерфейс позволяет выполнять поиск, загружая изображение (или используя образец изображения). Когда мы нажимаем «Поиск с изображением!» мы можем, ну, выполнить поиск по изображению и вернуть наиболее близкое подходящее изображение или изображения. В моем случае он вернул 12 лучших изображений.

Вы также можете искать, используя только текст, как вы можете видеть на следующем изображении (я искал «Цветы»):

А теперь приятное дополнение. Помните, как мы говорили о том, как обучаются вложения, чтобы вложения текста и изображения проецировались в одно и то же скрытое пространство? Что ж, мы можем использовать этот факт для объединения обоих вложений. Мы просто вычисляем между ними линейную интерполяцию, которая должна вернуть некоторую комбинацию изображения и текста.

Ура! Он делает то, на что мы надеялись. Хорошо видно, что фасон, а иногда и цвет платья сохранены, но с добавлением цветов.

Но подождите, я упомянул некоторые проблемы с этим подходом. Как ни хорошо модель CLIP предварительно обучена, у нее были некоторые проблемы с сочетанием немецкого языка и платьев. Вот пример, когда я искал «Punkte» (англ. «точки») и получил очень плохие результаты:

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

Через несколько эпох мы получили лучшие результаты:

Но все равно не совсем устраивает. Но после дополнительных тренировок мы стали лучше:

Хотя это и не идеально, результаты улучшились уже после нескольких периодов тренировок. Чтобы еще больше улучшить результаты, я мог бы использовать лучшую метку, чем просто «Punkte», но мы уже видим, как легко мы можем получить лучшие результаты, просто настроив модель с данными, специфичными для предметной области.

Заключение

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

Рекомендации

[0] Лефакис, Леонидас, Алан Акбик и Роланд Воллграф. «Фейдеггер: мультимодальный корпус модных изображений и описаний на немецком языке». Материалы одиннадцатой Международной конференции по языковым ресурсам и оценке (LREC 2018). 2018.