Изучение основ работы с предварительно обученными моделями CLIP и библиотекой CLIP Python.

Блокнот Колаб:



Пространство для объятий лица:



вступление

Модель CLIP OpenAI и связанные с ней методы штурмом взяли область машинного обучения с тех пор, как группа опубликовала свой первый пост в блоге о модели в январе 2021 года. Я настоятельно рекомендую этот оригинальный пост в качестве введения в большие идеи CLIP, если вы еще не читал, но архитектура CLIP и учебный процесс выходят за рамки этого руководства.

Мы сосредоточимся на том, как использовать библиотеку clip Python для сравнения сходства текста и изображений с предварительно обученными моделями CLIP. Эта базовая функциональность лежит в основе популярных и очень сложных методов, основанных на CLIP. Примечательно, что модель была очень популярна в сфере ИИ-искусства/генеративных изображений, благодаря методам, изначально ставшим популярными благодаря художнику и исследователю Райан Мердок. В то время как официальный репозиторий CLIP содержит полезный учебник Colab Взаимодействие с CLIP, этот учебник начнет продвигать нас к использованию CLIP в качестве механизма визуального мышления для генеративной работы.

Мы собираемся использовать CLIP для сравнения отдельных сплошных цветов с текстовыми подсказками, а затем посмотрим, как изменится наша оценка сходства при интерполяции от одного цвета к другому.

Например, на обложке этого поста я интерполировал от желтого (RGB=(1,1,0)) до розового (RGB = (1,0,1)), и на каждом шаге имел CLIP сообщите о сходстве между этим цветом и фразой «Стакан лимонада».

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

Мы могли бы представить себе применение этой идеи для оптимизации цвета. Мы предоставляем исходный цвет — может быть, серый или случайно сгенерированный код RGB — и позволяем CLIP направлять нас в настройке этого цвета, чтобы он максимально соответствовал подсказке. Однако в этом уроке мы не будем этого делать. Вам придется остаться включенным для второй части этой серии!

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

Установка КЛИП

Давайте сначала рассмотрим установку CLIP. Показанный ниже код предназначен для ноутбука IPython, то есть Colab. Чтобы запустить его в оболочке bash, просто удалите ! в начале каждой строки.

!pip install --quiet ftfy regex tqdm
!pip install --quiet git+https://github.com/openai/CLIP.git

Основы работы с CLIP

Во-первых, нам нужно загрузить предварительно обученную модель CLIP. Библиотека clip предоставляет несколько моделей, каждая из которых соответствует немного отличающейся архитектуре. Как правило, мы имеем дело с двумя категориями: моделями, основанными на ResNet архитектурах, и моделями, основанными на VisualTransformer архитектурах.

# Should be one of ['RN50', 'RN101', 'RN50x4', 'RN50x16', 'ViT-B/32', 'ViT-B/16']
model_name = 'ViT-B/16' 
model, preprocess = clip.load(model_name)
# Set to "evaluation mode"
model.eval()

Кодировать текст

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

text = 'some text to encode'
tokenized_text = clip.tokenize(text)

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

encoded_text = model.encode_text(tokenized_text)

Изображения в PyTorch: порядок размеров

PyTorch придерживается соглашения, которое может быть незнакомым, если вы привыкли работать с изображениями с помощью PIL, NumPy, OpenCV или TensorFlow. Скажем, у нас есть цветное изображение RGB шириной 640 пикселей и высотой 480 пикселей. В то время как PIL, NumPy и т. д. обрабатывают это как массив формы (480, 640, 3), в PyTorch это будет (3, 480, 640).

Это означает, что если вы загружаете изображения, например, с помощью PIL, вы должны переставить размеры в правильном порядке. Этого можно добиться, используя метод .permute() для тензоров факелов. Например:

img = Image.open('/path/to/image.png')
arr = np.array(img)
# Rearrange the dimensions from (HWC) to (CHW)
x = torch.tensor(arr).permute((2,0,1))

Но это еще не все! Если бы у нас был стек из 10 изображений такого размера, мы бы сохранили их в тензоре формы (10, 3, 480, 640). Другими словами, PyTorch форматирует изображения как (N, канал, высота, ширина) или NCHW. Более того, многие модели предназначены для работы с пакетами изображений, и вам может понадобиться преобразовать тензор формы (C,H,W) в (1,C,H,W) . Это можно сделать с помощью метода .unsqueeze() следующим образом:

# Old x.shape = (C, H, W), new x.shape will be (1, C, H, W)
# because .unsqueeze is expanding dimension 0
x = x.unsqueeze(0)
# Similarly, we can do (?)
x = x.squeeze(0)

Кодировать изображение

С помощью CLIP наша цель — проводить мультимодальные сравнения — точнее, мы хотим измерить сходство между изображениями и текстом. CLIP изучает свои модели кодировщика изображений и текста вместе, и мы можем получить доступ к кодировщику изображений с помощью метода .encode_image обученной модели CLIP:

img = Image.open('/path/to/image.png')
# We can just use the model's associated preprocesser function
x = model.preprocess(img)
encoded_image = model.encode_image(x)

Кодировать один цвет как изображение

Конечно, для нашего примера в этом уроке мы не работаем с существующими изображениями. Мы создаем свои собственные!

Давайте напишем функцию, которая превращает цвет в пространстве RGB с плавающей запятой в правильно отформатированный тензор изображения. Другими словами, у нас может быть что-то вроде color = (1, 0, 0) для красного или color = (0.5, 0.5, 0.5) для 50% серого. На самом деле все сводится к созданию экземпляра тензорного объекта и преобразованию его в формат NCHW. Эта функция возвращает изображение размером 1x1 пиксель с каналами RGB. Точнее, он выводит стек, содержащий одно изображение.

def create_rgb_tensor(color):
  """color is e.g. [1,0,0]"""
  return torch.tensor(color, device=DEVICE).reshape((1, 3, 1, 1))
red_tensor = create_rgb_tensor((1, 0, 0))

Но мы пока не можем передать один из них в наш визуальный кодировщик. Напомним, что модель CLIP имеет предустановленное разрешение, которое можно узнать с помощью model.visual.input_resolution.

Для нас это входное разрешение — это высота и ширина (224,224). Итак, давайте изменим размер нашего изображения RGB. Есть множество способов сделать это, используя torch и библиотеки, совместимые с torch, но я буду использовать класс Resize из torchvision.transforms.

from torchvision.transforms import Resize
# Create the Resizer 
resolution = model.visual.input_resolution
resizer = torchvision.transforms.Resize(size=(resolution, resolution))
resized_tensor = resizer(red_tensor)
torch.encode_image(resized_tensor)

Сходство

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

similarity = torch.cosine_similarity(encoded_text, encoded_image)

Создание и интерполяция между цветами

Мы почти закончили… Мы можем кодировать текст. Мы можем генерировать изображения из цветов. Мы можем изменить размер этих цветов до нужного размера для CLIP. И мы можем кодировать эти изображения с помощью CLIP.

Теперь давайте напишем функцию для интерполяции между двумя цветами. Мы будем придерживаться линейной интерполяции, которая просто «рисует линию» между двумя конечными точками. Обратите внимание, что функция ниже называется lerp, что означает (L)inear int(ERP)olation. По моему опыту, эта аббревиатура является строгим соглашением, и вы, вероятно, увидите ее, когда и где увидите функцию линейной интерполяции, определенную в коде.

def lerp(x, y, steps=11):
  """Linear interpolation between two tensors """
	  
	weights = np.linspace(0, 1, steps)
	weights = torch.tensor(weights, device=DEVICE)
	weights = weights.reshape([-1, 1, 1, 1])
	
	interpolated = x * (1 - weights) + y * weights
  return interpolated
blue_tensor = create_rgb_tensor((0,0,1))
color_range = lerp(red_tensor, blue_tensor, 11)
color_range = resizer(color_range)
similarities = torch.cosine_similarity(encoded_text, color_range)

Нанесение цветов в Pandas

Есть еще несколько функций, которые нам нужны для превращения этих цветов в красивую гистограмму в Pandas, но я не буду подробно описывать их все здесь. Вот фрагмент кода, подробно описывающий этот процесс:

def rgb2hex(rgb):
"""Utility function for converting a floating point RGB tensor to a hexadecimal color code."""
    rgb = (rgb * 255).astype(int)
    r,g,b = rgb
    return "#{:02x}{:02x}{:02x}".format(r,g,b)
def get_interpolated_scores(x, y, encoded_text, steps=11):
  interpolated = lerp(x, y, steps)
  interpolated_encodings = model.encode_image(resizer(interpolated))
  scores = torch.cosine_similarity(interpolated_encodings,
encoded_text)
  scores = sc.detach().cpu().numpy()
  rgb = interpolated.detach().cpu().numpy().reshape(-1,  3)
  interpolated_hex = [rgb2hex(x) for x in rgb]  
  data = pd.DataFrame({
      'similarity': scores,
      'color': interpolated_hex
  }).reset_index().rename(columns={'index':'step'})
  return data
def similarity_plot(data, text_prompt):
  title = f'CLIP Cosine Similarity Prompt="{text_prompt}"'
  fig, ax = plt.subplots()
  plot = data['similarity'].plot(kind='bar',
                                 ax=ax,
                                 stacked=True,
                                 title=title,
                                 color=data['color'],
                                 width=1.0,
                                 xlim=(0, 2),
                                 grid=False)
  
  plot.get_xaxis().set_visible(False) ; 
  return fig

Развертывание нашей модели с градиентом и обниманием лицевых пространств

Мы можем заставить этот процесс работать в блокноте Colab, но давайте поговорим о развертывании этой модели в виде интерактивного приложения. Я буду использовать Gradio в качестве основы и пространства Hugging Face для развертывания.

Код градиента

Я не буду вдаваться в подробности написания приложения Gradio, но вот основы:

  1. Определите функцию для запуска приложения. Это происходит ниже с gradio_fn . Обратите внимание, что это всего лишь функция Python, и на самом деле она пока не требует каких-либо специфичных для Gradio вещей. Это просто то, что Gradio будет делать с входными данными, которые мы предоставляем.
  2. Определите входные данные для интерфейса Gradio. Gradio обрабатывает входные данные, используя классы из модуля gradio.inputs. В этом примере мы используем входные данные Textbox для записи значений RGB и Slider для выбора количества шагов. Простота Gradio — большая часть его привлекательности — приложения не только просты в настройке, но и имеют очень унифицированный интерфейс. Есть и другие вещи, с которыми Gradio отлично справляется за нас, например, развертывание временных общедоступных приложений из одной строки кода в блокноте Colab без необходимости регистрации! Если бы мы захотели однако создайте что-то более индивидуальное — например, если мы хотим использовать палитру цветов для выбора наших значений RGB — мы могли бы посмотреть на такую ​​​​инфраструктуру, как Streamlit. Streamlit также поддерживается Hugging Face Spaces и в целом превосходен, но на этот раз я выбрал Gradio, особенно потому, что его легко протестировать в Colab.
  3. Создайте и запустите интерфейс Gradio. У нас есть функция для запуска, и у нас есть входные компоненты для нашего интерфейса, поэтому теперь нам просто нужно создать объект gradio.Interface, который принимает в качестве аргументов нашу функцию, входные данные и тип вывода. Затем мы просто вызываем метод .launch()! Как я упоминал ранее, что действительно удивительно, так это то, что этот метод будет работать из ячейки Colab, выдавая общедоступную ссылку и открывая встроенный IFrame с нашим приложением. Но это также будет работать в нашем файле .py о пространствах для обнимающихся лиц.
def gradio_fn(rgb_start, rgb_end, text_prompt, steps=11, grad_disabled=True):
  rgb_start = [float(x.strip()) for x in rgb_start.split(',')]
  rgb_end =  [float(x.strip()) for x in rgb_end.split(',')]
  start = create_rgb_tensor(rgb_start)
  end = create_rgb_tensor(rgb_end)
  encoded_text = encode_text(text_prompt)
  data = get_interpolated_scores(start, end, encoded_text, steps)
  return similarity_plot(data, text_prompt)
gradio_inputs = [gr.inputs.Textbox(lines=1, default="1, 0, 0", label="Start RGB"),
                 gr.inputs.Textbox(lines=1, default="0, 1, 0", label="End RGB"),
                 gr.inputs.Textbox(lines=1, label="Text Prompt", default='A solid red square'),
                 gr.inputs.Slider(minimum=1, maximum=30, step=1, default=11, label="Interpolation Steps")]
iface = gr.Interface(fn=gradio_fn, inputs=gradio_inputs, outputs="plot")
iface.launch()

Следующие шаги

Что еще мы можем сделать теперь, когда мы можем сравнивать изображения и текст с помощью CLIP? Список длинный, поверьте мне, но вот несколько общих идей:

  1. Нулевая классификация: в духе этого проекта мы могли бы представить себе выбор из предопределенного набора цветов. Например, выбрав цвет Pantone, предоставив текстовую подсказку. Это руководство на самом деле проделало большую часть пути к классификации нулевого выстрела. После вычисления оценки сходства для каждого цвета в нашем наборе интерполированных цветов нам просто нужно сообщить о наилучшем совпадении.
  2. Непосредственное изменение изображения/текста/некоторого скрытого представления для максимизации (или минимизации) сходства. Это основная идея, лежащая в основе многих популярных генеративных проектов с поддержкой CLIP, таких как CLIP+VQGAN (кстати, вот отличная статья о VQGAN), CLIP Guided Diffusion и CLIP+StyleGAN3. На самом деле здесь можно обобщить. Мы могли бы взять этот процесс в обратном порядке и использовать CLIP для создания подписей, изменив скрытое текстовое представление, чтобы оно лучше соответствовало изображению.
  3. Некоторые проекты выводят идею CLIP+Generator на новый уровень. Посмотрите на Модель VQGAN-CLIP с прямой связью исследователя Мехди Черти, которая использует CLIP и VQGAN для создания набора данных ответов VQGAN на запросы CLIP, а затем выполняет контролируемое обучение на этом наборе данных запроса/изображения, чтобы научиться предсказывать скрытое пространство VQGAN напрямую, минуя итеративный процесс с интенсивными вычислениями, поддерживаемый стандартным подходом CLIP+Generator.

Далее в этой серии

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

Примечание редактора. Heartbeat — это интернет-издание и сообщество, созданное участниками и посвященное предоставлению лучших образовательных ресурсов для специалистов по науке о данных, машинному обучению и глубокому обучению. Мы стремимся поддерживать и вдохновлять разработчиков и инженеров из всех слоев общества.

Независимая от редакции, Heartbeat спонсируется и публикуется Comet, платформой MLOps, которая позволяет специалистам по данным и командам машинного обучения отслеживать, сравнивать, объяснять и оптимизировать свои эксперименты. Мы платим нашим авторам и не продаем рекламу.

Если вы хотите внести свой вклад, перейдите к нашему призыву к участию. Вы также можете подписаться на получение наших еженедельных информационных бюллетеней (Еженедельник глубокого обучения и Информационный бюллетень Comet), присоединиться к нам в Slack и следить за Comet в Twitter и LinkedIn, чтобы получать ресурсы, события и многое другое, что поможет вам быстрее создавать лучшие модели машинного обучения.