Введение в анализ данных¶

Домашнее задание 5. Компьютерное зрение & генеративные модели¶

Правила, прочитайте внимательно:

  • Выполненную работу нужно отправить телеграм-боту @thetahat_ds25_bot. Для начала работы с ботом каждый раз отправляйте /start. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. Работы, присланные иным способом, не принимаются.
  • Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
  • Прислать нужно ноутбук в формате ipynb. Если вы строите интерактивные графики, их стоит прислать в формате html.
  • Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
  • Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.
  • Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
  • Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.
  • Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
  • Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
  • Комментарии к решению пишите в markdown-ячейках.
  • Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
  • Решение проверяется системой ИИ-проверки No description has been provided for this image ThetaGrader. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам.
  • Если код будет не понятен проверяющему, оценка может быть снижена.
  • Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
  • Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.

Важно!!! Правила заполнения ноутбука:

  • Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.
  • Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.
  • Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены <...>.
  • В markdown-ячейка, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.
  • При нарушении данных правил работа может получить 0 баллов.

Баллы за задание:

  • Задача 1 — 120 баллов
  • Задача 2 — 80 баллов
  • Задача 3 — 30 баллов

Баллы учитываются в факультативной части курса и не влияют на оценку по основной части.

In [ ]:
# Bot check

# HW_ID: fpmi_ad5
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.

# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше.
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.
# Никакие значения в этой ячейке не влияют на факт сдачи работы.
In [ ]:
# При необходимости установите библиотеки, например, раскомментируйте строку ниже
# !pip install torchinfo
In [ ]:
import warnings
from typing import Union

import seaborn as sns
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
from torchvision import transforms

sns.set(font_scale=1, style="darkgrid", palette="Set2")
warnings.simplefilter("ignore")

device = "cuda" if torch.cuda.is_available() else "cpu"
print(device)

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

❗ Весь код работы с библиотекой PyTorch необходимо написать самостоятельно, без использования ИИ-инструментов. Но можно использовать код из ноутбуков, рассказанных на занятии. Можно использовать ИИ-инструменты для написания технического кода (например, построение графиков), оформления ранее написанного кода и в качестве справки. Выводы необходимо делать самостоятельно.


Задача 1. Классификация MNIST¶

В данной задаче вам нужно провести сравнительный анализ сверточных нейросетей с различными параметрами на датасета рукописных цифр MNIST. С этим датасетом вы уже познакомились в предыдущем задании. Классификация в MNIST значительно проще, чем в CIFAR, рассмотренном на занятии, поэтому ваша задача — достичь на валидации хотя бы одной из моделей точности 97%. Вы можете свободно использовать код с занятия.

Использования слоев с семинара (свертка, pooling) более чем достаточно для достижения 97% точности на тесте. Также не делайте сеть глубокой.

Тестируйте работоспособность кода на CPU с небольшим количеством итераций. Если все работает, и хочется ускорить процесс, переходите на GPU.

Следуйте указаниям ниже.

Загрузим датаcет из torchvision.datasets.

In [ ]:
# Данные для обучения
train_dataset = torchvision.datasets.MNIST(
    root="./data", train=True, download=True, transform=transforms.ToTensor()
)
# Данные для тестирования
val_dataset = torchvision.datasets.MNIST(
    root="./data", train=False, download=True, transform=transforms.ToTensor()
)
# Классы объектов в датасете
num_classes = 10
classes_list = [str(i) for i in range(num_classes)]

Визуализируйте несколько картинок с соответствующими метками из датасета.

In [ ]:
...

Создайте генераторы батчей.

In [ ]:
...

Эксперимент 1. Создайте хотя бы 5 сверточных нейросетей с разным количеством линейных и сверточных слоев. Должен присутствовать хотя бы 1 сверточный слой и хотя бы 1 линейный слой. Для каждой посмотрите количество параметров с помощью torchinfo.summary.

In [ ]:
...

Обучите сети, используя функцию потерь nn.CrossEntropyLoss и оптимизатор torch.optim.SGD с дефолтными параметрами.

In [ ]:
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(simple_cnn.parameters())

...

Постройте график лосса и график accuracy, где сравниваются все модели (на train и на val). Нужная функция есть в семинаре.

In [ ]:
...

Сделайте вывод. Как количество разных слоев влияет на качество и время обучения?

Вывод по эксперименту 1:

...

Эксперимент 2. Теперь выберите лучший вариант, зафиксируйте это количество сверточных и линейных слоев и обучите хотя бы 4 сверточных нейросети, варьируя размеры ядер сверток. Например, в разном порядке поставьте ядра 3x3, 5x5.

In [ ]:
...

Постройте график лосса и график accuracy, где сравниваются все модели этого эксперимента (на train и на val).

In [ ]:
...

Сделайте вывод. Как размеры ядер влияют на качество и время обучения?

Вывод по эксперименту 2:

...

Анализ лучшей модели. Выберите лучшую конфигурацию из всех по accuracy на валидации. Она должна быть не меньше 97%.

In [ ]:
...

Проведите еще один проход валидации выбранной моделью по всему датасету. В нем посчитайте точность по каждому классу и соберите информацию о неправильных предсказаниях. Равномерна ли точность по отношению к классам? Покажите 10-20 примеров, на которых нейросеть выдала неправильную метку. Что можно о них сказать?

In [ ]:
...

Ответ:

...

Вывод по всей задаче:

...

На первом занятии мы рассматривали классификацию с помощью KNN, который показал высокую точность — 99%. Возникает вопрос: зачем нам изучать более сложные методы, если простой KNN может работать так же хорошо или даже лучше? Но здесь есть обман. В чем он заключается?

Для ответа на вопрос вспомните, с чем мы работали на первом занятии.

...


Задача 2. Визуализиция сверточных слоев¶

Обучите модель, состоящую как минимум из 4 сверточных слоев, на датасете MNIST. Количество выходных каналов в каждом сверточном слое должно быть равно 32, а точность на тестовых данных должна достигать 97%. Для создания модели будет удобно использовать torch.nn.Sequential.

In [ ]:
...

Визуализируйте веса свертки на первом слое. Поскольку веса имеют размерность $(32, 1, K_1, K_2)$, т.к. входное изображение имеет один канал, вам необходимо отобразить 32 изображения (по количеству выходных каналов) фильтрами размера $K_1 \times K_2$.

Совет. Здесь и далее серии из 32 картинок визуализируйте в виде сетки с помощью plt.subplots(4, 8, figsize=...). Не забывайте указывать параметр cmap='gray'.

In [ ]:
...

Какую функцию они могут нести? Для ответа на вопрос вспомните фильтры, рассмотренные на семинаре.

...

Для одного произвольного изображения из датасета MNIST визуализируйте выходы всех слоев нейросети, включая активацию и пуллинг, вплоть до операции Flatten, но не включая её. Каждый выход будет иметь размерность $(32, H_i, W_i)$. Таким образом, для каждого слоя необходимо создать $32$ изображения размером $H_i \times W_i$.

In [ ]:
...

Какие выводы можно сделать о том, как работает ваша сверточная сеть?

...

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

Ниже представлен шаблон функции, которая находит изображение, максимально усиливающее реакцию нейрона. Пример можно посмотреть в презентации с занятия.

In [ ]:
# При реализации запрещено пользоваться ИИ-инструментами.


def visualize_filter(
    model: nn.Module,
    layer_index: int,
    target_kernel: int,
    learning_rate: float = 1,
    steps: int = 10_000,
    image_size: int = 28,
    device: Union[str, torch.device] = "cpu",
) -> torch.Tensor:
    """Визуализирует фильтр сверточного слоя нейронной сети путем оптимизации
    входного изображения, которое максимизирует активацию заданного фильтра.

    Параметры:
        model (nn.Module): Модель нейронной сети, содержащая целевой сверточный слой.
        layer_index (int): Индекс целевого сверточного слоя в модели (по порядку в model.children()).
        target_kernel (int): Индекс фильтра в целевом слое для визуализации.
        learning_rate (float, optional): Скорость обучения для оптимизатора. По умолчанию 1.
        steps (int, optional): Количество шагов оптимизации. По умолчанию 10000.
        image_size (int, optional): Размер стороны квадратного входного изображения. По умолчанию 28.
        device (str|torch.device, optional): Устройство для вычислений ('cpu' или 'cuda'). По умолчанию "cpu".

    Возвращает:
        torch.Tensor: Оптимизированное изображение (тензор формы [1, C, H, W]),
                     которое максимизирует активацию целевого фильтра.
    """

    model.eval().to(device)  # Переводим модель в режим оценки

    # Инициализируем изображение случайным шумом
    input_img = torch.randn(..., requires_grad=True, device=device)
    # Обратите внимание, что в данном случае мы оптимизируем не модель, а входную картинку
    optimizer = optim.SGD(...)

    # Функция потерь: максимизируем активацию нужного ядра свертки
    for step in range(steps):

        # Пропускаем изображение через модель
        output = ...  # Выход заданного сверточного слоя
        loss = ...  # Минимизируемая функция

        # Обратное распространение
        ...

        # Ограничиваем значения пикселей в разумных пределах
        input_img.data = torch.clamp(input_img.data, -1, 1)

        # Логируем лосс
        if step % 500 == 0:
            print(f"Step {step}/{steps}, Loss: {loss.item():.4f}")

    # Преобразуем оптимизированное изображение
    img = ...

    return img
In [ ]:
...

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

...


Задача 3. Перенос стиля¶

Данную задачу, возможно, проще сделать в Google Colab.

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

Заметьте, что в примерах с семинара в качестве картинок стиля и контента использовались картинки среднего разрешения. Если возникают проблемы, например:

  • оптимизация останавливается на 0-й эпохе и не создает картинку;
  • loss в какой-то момент стал nan;
  • нехватка RAM;

то либо уменьшите разрешение ваших картинок, либо попробуйте картинку полегче.

In [ ]:
...

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

In [ ]:
...

Вывод:

...