Изучение глубоких вложений

Визуализация моделей Pytorch с помощью программы просмотра вложений Tensorboard

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

Целью этой статьи будет изучить, как это векторное пространство выглядит для различных моделей, и создать инструмент, который позволит нам взять любую модель глубокого обучения и визуализировать ее векторное пространство с помощью Tensorboard's Embedding Projector, TensorboardX и Pytorch. .

* Весь код для этого руководства доступен в репозитории Github здесь *

Переосмысление моделей глубокого обучения

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

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

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

Сегодня мы будем отображать эти вложения, чтобы понять, в каком векторном пространстве они живут и как в нем разделяются разные данные.

Настройка данных

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

Этот загрузчик данных будет генерировать два элемента для каждого пакета: пакетный тензор изображений (BATCH_SIZE, 3, 221, 221) и пакетный тензор меток (BATCH_SIZE, 1). Нам нужно изменить размер изображений до 221x221, потому что это минимальный размер, приемлемый для предварительно обученных моделей torchvision. Поскольку по стандартам глубокого обучения это довольно много, мы изменим их размер до меньшего размера, прежде чем визуализировать их в Tensorboard.

Инициализация предварительно обученной модели

Загрузить указанную предварительно обученную архитектуру модели в Torchvision чрезвычайно просто:

import torchvision.models as models
vgg16 = models.vgg16(pretrained=True)
vgg16.eval() # Setup for inferencing

Теперь мы можем сгенерировать функции для набора изображений MNIST с помощью простого скрипта:

Это идеально подходит для понимания процесса использования предварительно обученной модели для создания встраиваний, но для анализа этих встраиваний для использования с программой просмотра встраивания Tensorboard нам понадобится несколько дополнительных компонентов. Мы собираемся создать класс, который будет делать следующее:

  1. Инициализируется моделью Pytorch (объект nn.module), которая может принимать пакет данных и выводить одномерные вложения некоторого размера.
  2. Записывает парные точки входных данных и их вложения в предоставленные папки в формате, который может быть записан в журналы Tensorboard.

Создание Tensorboard Writer

Классу потребуются методы для создания вложений с моделью, записи их в файлы (вместе с соответствующим элементом данных, который их создал), создания объекта записи Tensorboard и использования модуля записи для добавления встраиваний в Tensorboard. Мы составляем класс следующим образом:

Для простоты класс не будет инициализировать для вас конкретную модель или набор данных, поскольку в идеале он должен работать с любой моделью и любым набором данных, если выходные данные модели в наборе данных являются одномерными встраиваемыми объектами. Однако обратите внимание, что определенные методы в этом классе (например, create_tensorboard_log ()) необходимо будет изменить, если элементы данных не являются изображениями или двумерными массивами. Поскольку мы визуализируем вложения для изображений, мы можем воспользоваться параметром «label_img» в методе add_embedding () в TensorboardX, чтобы каждое вложение было представлено соответствующим изображением, которое его сгенерировало.

Наконец, мы будем хранить как вложения, так и элементы данных в виде файлов .npy (массив Numpy) с (сгенерированными случайным образом) уникальными идентификаторами для каждого элемента данных. Это позволит нам узнать, какие элементы данных сгенерированы, какие встраиваемые, и легко извлекать их в виде пар для записи в Tensorboard.

Вот класс:

Создание вложений

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

Одна вещь, о которой нам нужно знать, заключается в том, что удаление прогнозирующего заголовка сети не всегда является простым делом и отличается в зависимости от архитектуры модели. Например, мы можем удалить голову прогноза модели VGG-16, выполнив:

vgg16 = models.vgg16(pretrained=True)
vgg16.classifier = vgg16.classifier[0:4] # Remove the pred head

Однако для Реснет-152:

resnet152 = models.resnet152(pretrained=True)
class Identity(torch.nn.Module):
    def __init__(self):
        super(Identity, self).__init__()
        
    def forward(self, x):
        return x
resnet152.fc = Identity() # Remove the prediction head

В общем, разные модели имеют свои слои, описываемые разными ключами, поэтому вызов print (model) иногда может помочь нам понять структуру. В идеале мы хотим избавиться только от слоя, который сопоставляет функции с размером определенного набора данных (например, полностью связанный слой размером 1000 в конце большинства моделей torchvision, которые были предварительно обучены на ImageNet. 1000 категорий).

После успешного запуска записной книжки, приведенной выше, мы можем начать изучать структуру некоторых встраиваний.

От 1000+ измерений до 3

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

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

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

UMAP: используйте кросс-энтропию и градиентный спуск для создания низкоразмерного многообразия, которое разделяет топологические свойства с многомерным многообразием, индуцированным данными.

Параметр по умолчанию - PCA, который часто не позволяет создавать значимые кластеры в многомерных вложениях. Мы можем наблюдать, например, что в этом тесте (CIFAR10, встроенный с Resnet-152), PCA объясняет только ~ 26% дисперсии во встраиваниях (внизу слева):

Использование алгоритма T-SNE с некоторой корректировкой сложности и постепенным снижением скорости обучения приводит к стабильным кластерам

Наконец, алгоритм UMAP возвращает гораздо меньше кластеров, которые распределены шире:

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

или этот кластер, содержащий фотографии лодок:

Эта кластеризация классов также происходит с MNIST, здесь Resnet-152 с кластеризацией T-SNE:

В целом, вы найдете лучшее разделение с более мощными моделями (такими как Resnet-152 против VGG-16) и наборами данных, которые ближе к исходному набору обучающих данных (Imagenet).

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

Спасибо за чтение!