На этот раз я буду резким и кратким. Через 10 минут я укажу мелкие модификации архитектуры трансформера для классификации изображений.
Поскольку это следующая статья, не стесняйтесь советовать мои предыдущие статьи о Transformer и внимание, если вы не чувствуете себя комфортно с терминами.
Теперь, дамы и господа, можете запускать часы!
Трансформаторам не хватает индуктивных смещений сверточных нейронных сетей (CNN), таких как инвариантность к трансляции и локально ограниченное рецептивное поле. Вы, наверное, слышали это раньше.
Но что это на самом деле означает?
Хорошо, неизменность означает, что вы можете распознать объект (т.е. объект) на изображении, даже если его внешний вид или положение различаются. Перевод в компьютерном зрении подразумевает, что каждый пиксель изображения был перемещен на фиксированную величину в определенном направлении.
Более того, помните, что свертка — это линейный локальный оператор. Мы видим только соседние значения, указанные ядром.
С другой стороны, трансформатор по своей конструкции инвариант перестановки. Плохая новость заключается в том, что он не может обрабатывать данные в виде сетки. Нам нужны последовательности! С этой целью мы преобразуем пространственный непоследовательный сигнал в последовательность!
Посмотрим, как.
Кратко о том, как работает Vision Transformer
Общая архитектура называется Vision Transformer (сокращенно ViT). Давайте рассмотрим его шаг за шагом.
-
Разбить изображение на патчи
-
Сгладить патчи
-
Создавайте низкоразмерные линейные вложения из сглаженных участков.
-
Добавить позиционные вложения
-
Подайте последовательность в качестве входных данных для стандартного трансформационного энкодера.
-
Предварительно обучите модель с помощью меток изображений (полностью контролируется на огромном наборе данных)
-
Тонкая настройка нисходящего набора данных для классификации изображений
Патчи изображений — это в основном токены последовательности (например, слова). Фактически блок энкодера идентичен оригинальному преобразователю, предложенному Vaswani et al. (2017), как мы подробно описали:
Всем известный трансформаторный блок. Изображение Алексея Досовицкого и др. 2020. Источник:Изображение стоит 16×16 слов: трансформеры для распознавания изображений в масштабе
Единственное, что меняется, это количество этих блоков. С этой целью и для дальнейшего доказательства того, что с большим количеством данных они могут обучать более крупные варианты ViT, были предложены 3 модели:
Алексей Досовицкий и др. 2020. Источник:Изображение стоит 16×16 слов: трансформеры для распознавания изображений в масштабе
Головки относятся к многоголовому вниманию, а размер MLP относится к синему модулю на рисунке. MLP расшифровывается как многослойный персептрон, но на самом деле это набор слоев линейного преобразования.
Скрытый размер – размер вложения, который остается фиксированным во всех слоях. Зачем держать это фиксированным? Так что мы можем использовать короткие соединения остаточного пропуска.
Если вы пропустили это, есть нет декодер в игре. Просто дополнительный линейный слой для окончательной классификации, называемый головкой MLP.
Но достаточно ли этого?
Да и нет. На самом деле нам нужен огромный объем данных и, как следствие, вычислительные ресурсы.
Важные детали
В частности, если ViT обучен на наборах данных с более чем 14 миллионами (как минимум: P) изображений, он может приблизиться или превзойти современные CNN.
Если нет, вам лучше придерживаться ResNets или EfficientNets.
ViT предварительно обучается на большом наборе данных, а затем настраивается на малые. Единственная модификация состоит в том, чтобы отказаться от прогностической головки (MLP-головки) и присоединить новую. линейный слой, где K — количество классов небольшого набора данных.
Мне показалось интересным, что авторы утверждают, что лучше проводить тонкую настройку при более высоких разрешениях, чем предварительное обучение.
Для точной настройки в более высоких разрешениях, 2D интерполяция выполняется встраивание предварительно обученной позиции. Причина в том, что они моделируют позиционные вложения с обучаемыми линейными слоями. При этом ключевая инженерная часть этой статьи посвящена передаче изображения в преобразователь.
Представление изображения в виде последовательности патчей
Мне также было очень любопытно, как вы можете элегантно изменить изображение в патчах. Для входного изображения и размер патча мы хотим создать патчи изображения, обозначаемые как где . длина последовательности аналогична словам в предложении.
Если вы не заметили патч изображения, т.е. (16,16,3) сплющен до 16x16x3. Надеюсь, название уже имеет смысл 😉
я буду использовать эйнопс библиотека, которая работает над PyTorch. Вы можете установить его через pip:
$ pip install einops
А затем немного компактного кода Pytorch:
from einops import rearrange
p = patch_size
x_p = rearrange(img, 'b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = p, p2 = p)
Короче говоря, каждый символ или каждая скобка указывает на измерение. Для получения дополнительной информации об операциях einsum ознакомьтесь с нашим Сообщение блога на эйнсумовых операциях.
Обратите внимание, что для простоты патчи изображения всегда квадратные.
А как насчет перехода от патча к встраиванию? Это просто слой линейного преобразования, который принимает последовательность элементы и выходы .
patch_dim = (patch_size**2) * channels
patch_to_embedding = nn.Linear(patch_dim, dim)
Вы видите, чего не хватает?
Бьюсь об заклад, вы! Нам нужно обеспечить какой-то порядок.
Позиционные вложения
Несмотря на то, что было применено множество схем позиционного встраивания, существенной разницы обнаружено не было. Вероятно, это связано с тем, что кодер-трансформер работает на уровне патчей. Изучение вложений, которые фиксируют отношения порядка между патчами (пространственная информация), не так важно. Относительно легче понять взаимосвязь между фрагментами P x P, чем между высотой x шириной полного изображения.
Интуитивно вы можете представить решение головоломки из 100 частей (патчей) вместо 5000 частей (пикселей).
Следовательно, после низкоразмерной линейной проекции обучаемый вложение позиции добавляется к представлениям патчей. Интересно посмотреть, как выглядят эти эмбеддинги позиций после обучения:
Алексей Досовицкий и др. 2020. Источник:Изображение стоит 16×16 слов: трансформеры для распознавания изображений в масштабе
Во-первых, есть какая-то двумерная структура. Во-вторых, шаблоны в строках (и столбцах) имеют схожие представления. Для высоких разрешений использовалась синусоидальная структура.
Ключевые результаты
В первые дни конверсии мы визуализировали ранние слои.
Почему?
Поскольку мы считаем, что хорошо обученные сети часто показывают хорошие и гладкий фильтры.
Слева: Визуализация фильтров Alexnet. Источник:Курс Стэндфорда CS231n Справа: ViT выучил фильтры. Источник:Изображение стоит 16×16 слов: трансформеры для распознавания изображений в масштабе
Я позаимствовал изображение из Стэнфордского курса. CS231n: сверточные нейронные сети для визуального распознавания.
Как прекрасно сказано в CS231n:
«Обратите внимание, что веса первого слоя очень хорошие и гладкие, что указывает на хорошо сходящуюся сеть. Функции цвета/оттенков серого сгруппированы, поскольку AlexNet содержит два отдельных потока обработки, и очевидным следствием этой архитектуры является то, что один поток развивает высокочастотные функции оттенков серого, а другой — низкочастотные цветовые функции». ~ Стэнфордский курс CS231: Визуализация того, что узнают ConvNets
Для таких визуализаций СПС используется. Таким образом, автор показал, что ранние представления слоев могут иметь схожие черты.
Следующий вопрос, пожалуйста.
Как далеко находятся изученные нелокальные взаимодействия?
Короткий ответ: Для размера патча P, максимум P*P, что в нашем случае 128, даже с 1-го слоя!
Нам не нужны последовательные конв. слои, чтобы добраться до 128 пикселей больше. При извилинах без дилатации рецептивное поле увеличивается линейно. Используя само-внимание, мы имеем взаимодействие между представлениями пикселей в 1-м слое и парами представлений во 2-м слое и так далее.
Справа: изображение, созданное с использованием Калькулятор искусственного интеллекта Фоморо Слева: изображение Алексей Досовицкий и др. 2020
Исходя из диаграммы слева от ViT, можно утверждать, что:
-
Действительно, есть головы, которые занимаются всем патчем уже в ранних слоях.
-
Можно оправдать прирост производительности на основе взаимодействия пикселей раннего доступа. Кажется более важным, чтобы ранние слои имели доступ ко всему патчу (глобальная информация). Другими словами, головы, которые принадлежат верхней левой части изображения, могут быть основной причиной превосходной производительности.
-
Интересно, что дистанция внимания увеличивается с глубиной сети, подобно рецептивному полю локальных операций.
-
В нижних слоях также есть головы внимания с постоянно небольшой дистанцией внимания. Справа 24-слойный слой со стандартными извилинами 3×3 имеет рецептивное поле менее 50. Нам потребуется примерно 50 свёрнутых слоев, чтобы обслуживать примерно 100 рецептивных полей, без расширения или объединения слоев.
-
Чтобы реализовать эту идею высоко локализованных головок внимания, авторы экспериментировали с гибридными моделями, в которых ResNet применяется до Transformer. Как и ожидалось, они обнаружили менее сильно локализованные головы. Наряду с визуализацией фильтра предполагается, что он может выполнять ту же функцию, что и ранние сверточные слои в CNN.
Дистанция внимания и визуализация
Тем не менее, я считаю важным понять, как они измеряли среднюю дистанцию внимания. Оно аналогично рецептивному полю, но не совсем то же самое.
Дистанция внимания рассчитывалась как среднее расстояние между пикселем запроса и остальной частью патча, умноженный на вес внимания. Они использовали 128 примеров изображений и усреднили их результаты.
Пример: если пиксель находится на расстоянии 20 пикселей, а вес внимания равен 0,5, расстояние равно 10.
Наконец, модель обращает внимание на области изображения, которые семантически релевантны для классификации, как показано ниже:
Алексей Досовицкий и др. 2020. Источник:Изображение стоит 16×16 слов: трансформеры для распознавания изображений в масштабе
Выполнение
Проверьте наши хранилище найти модули внутреннего внимания для вычислительного зрения. Учитывая реализацию ванили Кодер трансформатораViT выглядит так просто:
import torch
import torch.nn as nn
from einops import rearrange
from self_attention_cv import TransformerEncoder
class ViT(nn.Module):
def __init__(self, *,
img_dim,
in_channels=3,
patch_dim=16,
num_classes=10,
dim=512,
blocks=6,
heads=4,
dim_linear_block=1024,
dim_head=None,
dropout=0, transformer=None, classification=True):
"""
Args:
img_dim: the spatial image size
in_channels: number of img channels
patch_dim: desired patch dim
num_classes: classification task classes
dim: the linear layer's dim to project the patches for MHSA
blocks: number of transformer blocks
heads: number of heads
dim_linear_block: inner dim of the transformer linear block
dim_head: dim head in case you want to define it. defaults to dim/heads
dropout: for pos emb and transformer
transformer: in case you want to provide another transformer implementation
classification: creates an extra CLS token
"""
super().__init__()
assert img_dim % patch_dim == 0, f'patch size {patch_dim} not divisible'
self.p = patch_dim
self.classification = classification
tokens = (img_dim // patch_dim) ** 2
self.token_dim = in_channels * (patch_dim ** 2)
self.dim = dim
self.dim_head = (int(dim / heads)) if dim_head is None else dim_head
self.project_patches = nn.Linear(self.token_dim, dim)
self.emb_dropout = nn.Dropout(dropout)
if self.classification:
self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
self.pos_emb1D = nn.Parameter(torch.randn(tokens + 1, dim))
self.mlp_head = nn.Linear(dim, num_classes)
else:
self.pos_emb1D = nn.Parameter(torch.randn(tokens, dim))
if transformer is None:
self.transformer = TransformerEncoder(dim, blocks=blocks, heads=heads,
dim_head=self.dim_head,
dim_linear_block=dim_linear_block,
dropout=dropout)
else:
self.transformer = transformer
def expand_cls_to_batch(self, batch):
"""
Args:
batch: batch size
Returns: cls token expanded to the batch size
"""
return self.cls_token.expand((batch, -1, -1))
def forward(self, img, mask=None):
batch_size = img.shape(0)
img_patches = rearrange(
img, 'b c (patch_x x) (patch_y y) -> b (x y) (patch_x patch_y c)',
patch_x=self.p, patch_y=self.p)
img_patches = self.project_patches(img_patches)
if self.classification:
img_patches = torch.cat(
(self.expand_cls_to_batch(batch_size), img_patches), dim=1)
patch_embeddings = self.emb_dropout(img_patches + self.pos_emb1D)
y = self.transformer(patch_embeddings, mask)
if self.classification:
return self.mlp_head(y(:, 0, :))
else:
return y
Заключение
Ключевой инженерной частью этой работы является постановка задачи классификации изображений в виде последовательной задачи с использованием фрагментов изображений в качестве токенов и их обработка преобразователем. Это звучит хорошо и просто, но для этого нужны массивные данные. К сожалению, предварительно обученный набор данных принадлежит Google, поэтому результаты невозможно воспроизвести. И даже если бы они были, вам нужно было бы иметь достаточную вычислительную мощность.
* Раскрытие информации: обратите внимание, что некоторые из приведенных выше ссылок могут быть партнерскими ссылками, и мы без дополнительных затрат для вас получим комиссию, если вы решите совершить покупку после перехода по ссылке.