Фаза 04 · урок 17

Самообучение в компьютерном зрении — SimCLR, DINO, MAE

Цель урока: В ImageNet с обучением с учителем 1,3 млн размеченных изображений; их разметка, по оценкам, обошлась в $10M. Медицинские и промышленные наборы данных меньше, а их разметка ещё дороже. Каждая команда компьютерного зрения задаётся…

Текущий релиз AlexBred.com: первые 100 уроков русскоязычной программы.

Курс
AI Engineering from Scratch
Фаза
Компьютерное зрение
Чтение
11 мин.
Проверено
Содержание урока
  1. Цели обучения
  2. Проблема
  3. Концепция
  4. Три семейства методов
  5. Контрастивное обучение (SimCLR)
  6. «Учитель—ученик» (DINO)
  7. Реконструкция по маске (MAE)
  8. Почему 75 %, а не 15 %
  9. Оценка линейным зондированием
  10. Соберите
  11. Шаг 1: конвейер аугментирования двух представлений
  12. Шаг 2: функция потерь InfoNCE
  13. Шаг 3: быстрая проверка InfoNCE
  14. Шаг 4: маскирование в стиле MAE
  15. Используйте
  16. Внедрите
  17. Упражнения
  18. Ключевые термины
  19. Дополнительное чтение

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

Тип: Изучите + соберите Языки: Python Предварительные требования: Фаза 4, урок 04 (Классификация изображений), фаза 4, урок 14 (ViT) Время: ~75 минут

Цели обучения

  • Проследить три основные группы методов самообучения — контрастивные (SimCLR), «учитель—ученик» (DINO), реконструкцию по маске (MAE) — и объяснить, что оптимизирует каждая из них
  • Реализовать функцию потерь InfoNCE с нуля и объяснить, почему пакет из 512 примеров работает, а пакет из 32 — нет
  • Объяснить, почему коэффициент маскирования MAE в 75 % не произволен и чем он отличается от 15 % у BERT для текста
  • Использовать контрольные точки DINOv2 или MAE для ImageNet для линейного зондирования и поиска без обучения на целевой задаче

Проблема

В ImageNet с обучением с учителем 1,3 млн размеченных изображений; их разметка, по оценкам, обошлась в $10M. Медицинские и промышленные наборы данных меньше, а их разметка ещё дороже. Каждая команда компьютерного зрения задаётся вопросом: можно ли предобучаться на дешёвых неразмеченных данных — кадрах YouTube, результатах веб-обходов, записях веб-камер, спутниковых съёмках — а затем дообучать модель на небольшом размеченном наборе?

Ответ — самообучение. Современный ViT с самообучением, обученный на LAION или JFT, после дообучения достигает точности ImageNet с учителем или превосходит её. Он также лучше переносится на последующие задачи (детекцию, сегментацию, оценку глубины), чем предобучение с учителем. DINOv2 (Meta, 2023) и MAE (Meta, 2022) — нынешние производственные стандарты для переносимых признаков компьютерного зрения.

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

Концепция

Три семейства методов

Диаграмма к уроку «Самообучение в компьютерном зрении — SimCLR, DINO, MAE»

Контрастивное обучение (SimCLR)

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

Loss for positive pair (z_i, z_j) among 2N views per batch:

   L_ij = -log( exp(sim(z_i, z_j) / tau) / sum_k in batch \ {i} exp(sim(z_i, z_k) / tau) )

sim = cosine similarity
tau = temperature (0.1 standard)

Это функция потерь InfoNCE. Ей нужно много отрицательных примеров на каждый положительный, поэтому размер пакета важен: SimCLR нужны 512–8192. MoCo ввёл очередь моментов из прошлых пакетов, чтобы отделить число отрицательных примеров от размера пакета.

«Учитель—ученик» (DINO)

Две сети с одинаковой архитектурой: ученик и учитель. Учитель представляет собой экспоненциальное скользящее среднее (EMA) весов ученика. Обе сети видят аугментированные представления изображения. Выход ученика обучается совпадать с выходом учителя — без явных отрицательных примеров.

loss = CE( student_output(view_1),  teacher_output(view_2) )
     + CE( student_output(view_2),  teacher_output(view_1) )

teacher_weights = m * teacher_weights + (1 - m) * student_weights   (m ≈ 0.996)

Почему метод не схлопывается к «предсказывай константу»: выход учителя центрируется (из каждого измерения вычитается среднее) и заостряется (делится на малую температуру). Центрирование не даёт одному измерению доминировать; заострение не даёт выходу схлопнуться к равномерному распределению.

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

Реконструкция по маске (MAE)

Замаскируйте 75 % патчей входа ViT. Пропустите через кодировщик только видимые 25 %. Небольшой декодер получает выход кодировщика вместе с токенами маски в замаскированных позициях и обучается восстанавливать пиксели замаскированных патчей.

Encoder:  visible 25% of patches -> features
Decoder:  features + mask tokens at masked positions -> reconstructed pixels
Loss:     MSE between reconstructed and original pixels on masked patches only

Ключевые проектные решения, благодаря которым MAE работает:

  • Коэффициент маскирования 75 % — высокий. Он заставляет кодировщик изучать семантические признаки; восстанавливать 25 % было бы почти тривиально (соседние пиксели настолько коррелированы, что CNN справилась бы без труда).
  • Асимметричные кодировщик и декодер — большой кодировщик ViT видит только видимые патчи; небольшой декодер (8 слоёв, 512 измерений) занимается реконструкцией. Предобучение в 3 раза быстрее, чем у наивного BEiT.
  • Цель реконструкции в пространстве пикселей — она проще, чем токенизированная цель BEiT, и лучше работает с ViT.

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

Почему 75 %, а не 15 %

BERT маскирует 15 % токенов. MAE маскирует 75 %. Разница обусловлена плотностью информации.

  • Естественный язык имеет высокую энтропию на токен. Предсказать 15 % токенов всё ещё трудно, потому что у каждой замаскированной позиции много правдоподобных продолжений.
  • Патчи изображений имеют низкую энтропию: незамаскированное окружение часто почти точно определяет пиксели замаскированного патча. Чтобы предсказание требовало семантического понимания, нужно маскировать агрессивно.

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

Оценка линейным зондированием

После предобучения с самообучением стандартная оценка — линейный зонд: зафиксируйте кодировщик и обучите поверх него единственный линейный классификатор на метках ImageNet. Сообщается точность top-1.

  • SimCLR ResNet-50: ~71 % (2020)
  • DINO ViT-S/16: ~77 % (2021)
  • MAE ViT-L/16: ~76 % (2022)
  • DINOv2 ViT-g/14: ~86 % (2023)

Линейный зонд — чистая мера качества признаков; дообучение обычно добавляет 2–5 пунктов, но также смешивает в оценке эффект переобучения головы.

Соберите

Шаг 1: конвейер аугментирования двух представлений

import torch
import torchvision.transforms as T

two_view_train = lambda: T.Compose([
    T.RandomResizedCrop(96, scale=(0.2, 1.0)),
    T.RandomHorizontalFlip(),
    T.ColorJitter(0.4, 0.4, 0.4, 0.1),
    T.RandomGrayscale(p=0.2),
    T.ToTensor(),
])


class TwoViewDataset(torch.utils.data.Dataset):
    def __init__(self, base):
        self.base = base
        self.aug = two_view_train()

    def __len__(self):
        return len(self.base)

    def __getitem__(self, i):
        img, _ = self.base[i]
        v1 = self.aug(img)
        v2 = self.aug(img)
        return v1, v2

Каждый __getitem__ возвращает два аугментированных представления одного изображения; метки не нужны.

Шаг 2: функция потерь InfoNCE

import torch.nn.functional as F

def info_nce(z1, z2, tau=0.1):
    """
    z1, z2: (N, D) L2-normalised embeddings of paired views
    """
    N, D = z1.shape
    z = torch.cat([z1, z2], dim=0)  # (2N, D)
    sim = z @ z.T / tau              # (2N, 2N)

    mask = torch.eye(2 * N, dtype=torch.bool, device=z.device)
    sim = sim.masked_fill(mask, float("-inf"))

    targets = torch.cat([torch.arange(N, 2 * N), torch.arange(0, N)]).to(z.device)
    return F.cross_entropy(sim, targets)

Перед вызовом нормализуйте эмбеддинги по L2. tau=0.1 — значение SimCLR по умолчанию; меньшее значение делает функцию потерь более резкой и требует больше отрицательных примеров.

Шаг 3: быстрая проверка InfoNCE

z1 = F.normalize(torch.randn(16, 32), dim=-1)
z2 = z1.clone()
loss_same = info_nce(z1, z2, tau=0.1).item()
z2_random = F.normalize(torch.randn(16, 32), dim=-1)
loss_random = info_nce(z1, z2_random, tau=0.1).item()
print(f"InfoNCE with identical pairs:  {loss_same:.3f}")
print(f"InfoNCE with random pairs:     {loss_random:.3f}")

Идентичные пары должны дать малую потерю (близкую к 0 для большого пакета и низкой температуры). Случайные пары должны дать log(2N-1) = ~log(31) = ~3.4 при пакете из 16 пар.

Шаг 4: маскирование в стиле MAE

def random_mask_indices(num_patches, mask_ratio=0.75, seed=0):
    g = torch.Generator().manual_seed(seed)
    n_keep = int(num_patches * (1 - mask_ratio))
    perm = torch.randperm(num_patches, generator=g)
    visible = perm[:n_keep]
    masked = perm[n_keep:]
    return visible.sort().values, masked.sort().values


num_patches = 196
visible, masked = random_mask_indices(num_patches, mask_ratio=0.75)
print(f"visible: {len(visible)} / {num_patches}")
print(f"masked:  {len(masked)} / {num_patches}")

Просто, быстро и детерминированно для заданного seed. Реальные реализации MAE выполняют это пакетно и хранят отдельные маски для каждого примера.

Используйте

DINOv2 — производственный стандарт в 2026 году:

import torch
from transformers import AutoImageProcessor, AutoModel

processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
model = AutoModel.from_pretrained("facebook/dinov2-base")
model.eval()

# Per-image embeddings for zero-shot retrieval
with torch.no_grad():
    inputs = processor(images=[pil_image], return_tensors="pt")
    outputs = model(**inputs)
    embedding = outputs.last_hidden_state[:, 0]  # CLS token

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

Для эмбеддингов «изображение—текст» эквивалентом служат SigLIP или OpenCLIP; для дообучения в стиле MAE репозиторий timm предоставляет каждую контрольную точку MAE.

Внедрите

Этот урок создаёт:

  • outputs/prompt-ssl-pretraining-picker.md — промпт, выбирающий SimCLR / MAE / DINOv2 с учётом размера набора данных, вычислительных ресурсов и последующей задачи.
  • outputs/skill-linear-probe-runner.md — навык, который пишет оценку линейным зондированием для любого зафиксированного кодировщика и размеченного набора данных.

Упражнения

  1. (Легко) Проверьте, что потеря InfoNCE уменьшается при снижении температуры для хорошо согласованных эмбеддингов и растёт при снижении температуры для случайных эмбеддингов. Постройте график tau in [0.05, 0.1, 0.2, 0.5] в зависимости от потери.
  2. (Средне) Реализуйте буфер центра в стиле DINO. Покажите, что без центрирования ученик за несколько эпох схлопывается к постоянному вектору.
  3. (Сложно) Обучите MAE на CIFAR-100, используя TinyUNet из урока 10 как основу. Сообщите точность линейного зонда на 10, 50 и 200 эпохах. Покажите, что линейный зонд с предобучением MAE превосходит линейный зонд с обучением с учителем с нуля на том же подмножестве из 1 000 изображений.

Ключевые термины

Термин Что обычно говорят Что это на самом деле означает
Самообучение «Без меток» Предтекстовая задача, создающая полезные представления из неразмеченных данных
Предтекстовая задача «Искусственная задача» Цель, используемая во время SSL (восстанавливать патчи, сопоставлять представления); отбрасывается после предобучения
Линейный зонд «Зафиксированный кодировщик + линейная голова» Стандартная оценка SSL: обучается только линейный классификатор поверх зафиксированных признаков
InfoNCE «Контрастивная функция потерь» softmax по косинусным сходствам; положительная пара — целевой класс, все остальные — отрицательные примеры
Учитель EMA «Учитель со скользящим средним» Учитель, чьи веса являются экспоненциальным скользящим средним весов ученика; используется в BYOL, MoCo, DINO
Коэффициент маскирования «% скрытых патчей» Доля патчей, маскируемых при MAE; 75 % для зрения, 15 % для текста
Схлопывание представлений «Постоянный выход» Сбой SSL, при котором кодировщик выдаёт постоянный вектор для всех входов; предотвращается центрированием, заострением или отрицательными примерами
DINOv2 «Производственная основа SSL» Самообучаемый ViT Meta 2023 года; наиболее сильные универсальные признаки изображений в 2026 году

Дополнительное чтение

  • SimCLR (Chen et al., 2020) — справочный материал по контрастивному обучению
  • DINO (Caron et al., 2021) — «учитель—ученик» с моментом, центрированием и заострением
  • MAE (He et al., 2022) — предобучение маскированного автокодировщика для ViT
  • DINOv2 (Oquab et al., 2023) — масштабирование ViT с самообучением до производственных признаков

Источник: Self-Supervised Vision — SimCLR, DINO, MAE 04.16 — Полный конвейер компьютерного зрения: итоговый проект · Фаза 04 — Компьютерное зрение · 04.18 — Зрение с открытым словарём — CLIP · Полный каталог