Всем привет, я бы хотел вам кое‑что рассказать. Когда я однажды наткнулся на тему, связанную с self‑supervised learning, меня удивила мысль о том, что для обучения нейросети, оказывается, не совсем обязательная разметка. Удивительная идея, которая, как кажется на первый взгляд, ломает все, что я знал об обучении нейросетей.

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

  1. Немного теории

  2. Contrastive learning

    1. Triplet Loss

    2. NT‑Xent Loss

  3. Self‑prediction

  4. Заключение

И ответить на главный вопрос: «А чему учить, если ты сам ничего не знаешь?»

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

Немножко теории

Эту часть я оформлю в небольшой справочник для создания академического налета и чтобы было удобно освежить знания.

  • Эмбеддинг — представление некого объекта (изображения, текста и тому подобное) в виде вектора в многомерном пространстве. В идеальном случае, эмбеддинги похожих объектов лежат очень близко, а непохожих — далеко.

  • Self‑supervised обучение — обучение, при котором у модели (а иногда и у нас) отсутствует какая‑либо разметка данных, в таком случае, основная цель модели — с��здание информативных представлений (эмбеддингов) для объектов.

  • Cosine similarity — она же косинусная схожесть. Метрика, показывающая схожесть направлений векторов. Находится в пределах [-1; 1], где:

    • 1 — Полное сходство(вектора направлены в одном направлении)

    • 0 — Вектора перпендикулярны

    • -1 — Вектора противоположны по направлению

Contrastive learning или "Свой, среди чужих".

Якоря, моря и Triplet Loss.

Triplet Loss — является одним из ранних и достаточно простых способов обучения представлений, но к self‑supervised обучению он не относится, потому что для его использования все‑таки необходима разметка данных.

Triplet loss основан на тройках примеров из данных:

  1. Якорь (Anchor) — тот, с кем мы сравниваем (своего рода главный)

  2. Positive — Объект того же класса, что и якорь (мы должны быть с ним близки)

  3. Negative — Тот, от кого мы должны быть подальше.

В этих тройках и кроется наша функция и, по‑совместительству, задача модели:

Сделай так, чтобы якорь и позитивный пример были как можно ближе, а негативный — как можно дальше

Демонстрация того, чего должен достичь Triplet Loss
Демонстрация того, чего должен достичь Triplet Loss
\mathbb{L} = \sum_{i=1}^{m} \max \left( \|f(A^{(i)}) - f(P^{(i)})\|_2^2 - \|f(A^{(i)}) - f(N^{(i)})\|_2^2 + \alpha, 0 \right)

Страшно? Сейчас все поясню.

  • ||f(A^{(i)}) - f(P^{(i)})||_2^2— евклидово расстояние от якоря до позитивного примера.

  • ||f(A^{(i)}) - f(N^{(i)})||_2^2— расстояние от якоря до негативного примера.

  • \alpha— абстрактный минимальный отступ между позитивным и негативным примерами(его так же называют margin).

Triplet loss равен 0 тогда, когда расстояния от якоря до негатива больше, чем расстояние до позитива, то есть ||f(A^{(i)}) - f(P^{(i)})||_2^2 - ||f(A^{(i)}) - f(N^{(i)})||_2^2 < 0

А отступ там откуда взялся? Зачем он тут?

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

Если попробовать подобрать числа, то мы выйдем на то, что если оба расстояния будут 0, то и функция будет давать 0. А ч��о это значит? Значит все 3 эмбеддинга находятся в одной точке, что нас категорически не устраивает, но функция показывает 0, получается, мы выполнили задачу минимизации функции, схитрив.

И именно так и поступает нейросеть при обучении, она понимает, что ей проще сместить все эмбеддинги в 1 точку, вместо обучения правильных представлений. Такое явление называется коллапсом эмбеддингов и он является одной из главных проблем в контрастном self‑supervised обучении.

Небольшое дополнение

Выше я упоминал, что Triplet loss — чисто supervised метод, но, на самом деле, существуют его адаптации, не требующие разметки, в них берутся 2 аугментации(якорь и позитив) какого‑то элемента и аугментация другого случайного элемента из набора данных.

Как найти своего среди чужих? NT-Xent Loss

Собственно, разметки нет от слова совсем. Вариантов у нас тут немного. Единственное, что мы знаем наверняка — элемент n похож на элемент n, как бы это странно не звучало. И да, такой информации вполне достаточно(почти). Так как мы имеем в распоряжении единственный верный позитивный пример, то единственный наш вариант — аугментации, то есть видоизменения оригинала с целью получения «соседа», а именно — второго позитивного примера(на практике делают 2 аугментации оригинала, сам оригинал при обучении не используют).

Как нам заставить модель учиться? Для этого надо поставить задачу немного иначе:

Создай такие эмбеддинги, чтобы вероятность найти для первой аугментации(якоря в каком‑то смысле) его «соседа», то есть вторую аугментацию среди всех эмбеддингов была максимальной.

Мы знаем одну аугментацию, а нам нужно найти вторую, исходя из имеющихся эмбеддингов
Мы знаем одну аугментацию, а нам нужно найти вторую, исходя из имеющихся эмбеддингов

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

\mathbb{L}_{i,j} = - \log \frac{\exp(\mathrm{sim}(\mathbf{z}_i, \mathbf{z}_j) / \tau)}{\sum_{k=1}^{2N} [k \neq i] \exp(\mathrm{sim}(\mathbf{z}_i, \mathbf{z}_k) / \tau)}

Что тут происходит? Сначала разберем кто тут кто.

  1. \mathrm{sim}(\mathbf{z}_i, \mathbf{z}_j)— косинусная схожесть 2 эмбеддингов

  2. \exp()— функция экспоненты

  3. \tau— параметр, называемый температурой

Теперь по порядку.

  1. \exp(\mathrm{sim}(\mathbf{z}_i, \mathbf{z}_j) / \tau)— Получаем некое число, показывающее схожесть 2 аугментаций исходного объекта, чтобы его вычислить мы считаем схожесть и делим ее на \tau — параметр, регулирующий то, насколько сильно модель должна быть уверена в 2ом соседе, чтобы получить низкий лосс. Чем температура выше, тем проще ей получить низкий лосс и тем более «халатно» она будет работать, но если \tau сделать низким, модель начнет запоминать шум в данных, а не обобщать их, поэтому нужно что‑то среднее (обычно ставят 0.1). Прогоняем схожесть через экспоненту и получаем ее уже в виде некого числа.

  2. N — число примеров в батче. Почему 2N? — потому что у нас для каждого примера 2 аугментации(2 позитива).

  3. \sum_{k=1}^{2N} [k \neq i] \exp(\mathrm{sim}(\mathbf{z}_i, \mathbf{z}_k) / \tau)— Считаем по аналогии схожесть якоря с всеми

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

  4. -log()— Т.к. дробь всегда будет < 1, то с помощью логарифма мы приводим вероятность в функцию, которую уже можно минимизировать (при уменьшении логарифма вероятность будет наоборот увеличиваться)

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

Self-prediction, или же: "Угадай, что спрятано"

Рассмотрим второй вариант того, как можно заставить модель создавать устойчивые и богатые информацией признаки изображения или текста. Тут у нас тоже нету никаких меток и единственный верный вариант — мы сами.

Подход self‑prediction несколько иной. Вместо того, чтобы учить модель «искать своего среди чужих» мы научим ее восстанавливать потерянную часть объекта. Логика проста — если мы сможем по какой‑то части, например, изображения понять что на ней изображено и восстановить, то тогда мы понимаем что в целом находится на изображении.

Цель: взять объект, намеренно убрать случайную часть и после научить модель восстанавливать потерянный кусочек

Рассмотрим пример self‑supervised обучения для создания текстовых эмбеддингов (подобие BERT):

Примерный pipeline для обучения BERT-подобной модели
Примерный pipeline для обучения BERT-подобной модели

Общей формулы так таковой тут нет, потому что для каждой модальности и модели она разные, но задача для всех моделей одинакова — восстановленная часть должна быть максимально близка к оригиналу. В Colab ноутбуке вы можете посмотреть пример реализации self‑prediction метода для изображений.

Важное архитектурное замечание: для получения эмбеддингов обычно берут выход одного из последних скрытых слоев.

Что же по итогу?

В заключении я бы хотел добавить, что описанные тут методы являются уже скорее устаревающими, но все еще активно используются. Я не стал включать сюда разбор таких способов, как DINO, который сейчас является SOTA в мире self‑supervised learning, но он ближе к скорее развитию названных технологий, возможно, я когда‑то сделаю подробный разбор этой идеи, потому что она полна идей и интересных решений.


А все‑таки — чему учить модель, если мы ничего не знаем?
Ответ прост — мы можем научить модель не выдавать определенный результат при некоторых входных данных, а понимать данные. И, как показывает практика, даже для supervised обучения, где уже есть разметка претрейн модели self‑supervised методом дает улучшение результатов и ускорение обучения.


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

Ссылка на ноутбук с кодом и демонстрацией работы каждого описанного метода — Google Colab

Всем пока и, надеюсь, до встречи :-)