中文翻译暂不可用,显示俄语原文。

Реализовать curriculum learning

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать curriculum learning

1. Цель задачи

Разработать и протестировать стратегию curriculum learning — обучение модели на данных, отсортированных по возрастанию сложности (от простого к сложному). Цель — ускорить сходимость нейросети минимум на 30% по сравнению со стандартным случайным порядком подачи данных при сохранении финального качества.
Ключевой результат конвергенция (достижение целевой метрики, например 90% accuracy на валидации) происходит не менее чем на 30% быстрее по числу эпох.

2. Исходные данные

Что нужноОткуда взять
Датасет изображений (MNIST или CIFAR-10)torchvision.datasets – загрузить через datasets.MNIST / datasets.CIFAR10
Базовая модель (простая CNN)Написать самостоятельно (пример кода ниже)
Метрика сложности примераРеализовать один из вариантов: длина градиента, уверенность предобученного оракула, размер объекта (для MNIST)
Стратегия curriculumРазработать: разделить обучающую выборку на K уровней, подавать их в порядке возрастания, постепенно добавляя новые уровни

Если нет реального инструмента — симулируем:

  1. Используем встроенные датасеты PyTorch (MNIST доступен всегда).
  2. В качестве метрики сложности для простоты берём среднюю интенсивность пикселей (чем меньше чёрных пикселей, тем пример сложнее) — для MNIST это разумно.
  3. Реализацию curriculum делаем через кастомный DataLoader / Dataset, который на каждой эпохе выдает batch из первых t уровней, где t растёт от 1 до K.

3. Технологический стек

КомпонентИнструментыНазначение
Язык программированияPython 3.10+Основной язык
Фреймворк глубокого обученияPyTorch 2.xПостроение и обучение модели
Работа с даннымиtorchvision, numpyЗагрузка и предобработка датасетов, сортировка
Визуализацияmatplotlib, seabornГрафики сходимости, loss/accuracy
ЭкспериментыJupyter Notebook / Python scriptВоспроизводимость
Управление зависимостямиrequirements.txtФиксация версий

4. Этапы выполнения

Этап 1: Подготовка данных и определение метрики сложности (≈1 час)

Действия

  1. Загрузить датасет MNIST (или CIFAR-10) через torchvision.datasets. Разделить на train/val (50000/10000 для CIFAR-10; для MNIST 60000/10000).
  2. Реализовать метрику сложности для каждого обучающего примера:
    • Для MNIST: средняя яркость пикселя (нормализованная от 0 до 1). Чем меньше яркость (тёмные изображения), тем "сложнее" — т.к. цифра меньше контрастирует.
    • Альтернативно: длина градиента после одного прохода через маленькую обученную модель-оракул (но это требует дополнительного обучения, поэтому для простоты берём яркость).
  3. Создать функцию compute_difficulty(dataset), возвращающую массив вещественных значений сложности для каждого образца.
  4. Отсортировать индексы обучающей выборки по возрастанию сложности (от самых простых к самым сложным).
  5. Разделить отсортированные данные на K=5 равных (или по количеству) уровней (buckets) — каждый уровень содержит ровно len(train)/K примеров.
import torchvision
import numpy as np

def compute_difficulty(dataset):
    difficulties = []
    for img, label in dataset:
        # img: Tensor [1,28,28] для MNIST
        difficulties.append(img.mean().item())  # средняя яркость
    return np.array(difficulties)

full_train = torchvision.datasets.MNIST(root='./data', train=True, download=True)
difficulties = compute_difficulty(full_train)
sorted_indices = np.argsort(difficulties)  # от меньшей яркости (просто) к большей (сложно)

Ожидаемый результат этапа

  • Отсортированный список индексов обучающей выборки по возрастанию сложности.
  • Создан массив sorted_indices и разбивка на 5 уровней (например, level_indices = np.array_split(sorted_indices, 5)).

Этап 2: Создание стратегии curriculum (≈1 час)

Действия

  1. Реализовать класс CurriculumDataset, наследующий torch.utils.data.Dataset.
    • На вход принимает исходный датасет, список уровней (каждый уровень — список индексов), текущий номер эпохи (current_epoch) и максимальное количество эпох (max_epochs).
    • Метод __len__ возвращает сумму длин уровней от 0 до current_level, где current_level = min( int(current_epoch / max_epochs * K), K-1 ).
    • Метод __getitem__ возвращает пример из одного из активных уровней (равномерно или пропорционально).
  2. Реализовать функцию get_curriculum_loader(epoch, ...), которая создаёт DataLoader с правильным CurriculumDataset.
  3. Подготовить baseline: обычный DataLoader с полным перемешиванием (shuffle=True).
  4. Убедиться, что на первой эпохе в curriculum попадают только самые простые примеры (уровень 0), на последней — все уровни.
class CurriculumDataset(Dataset):
    def __init__(self, base_dataset, level_indices, current_epoch, max_epochs, K=5):
        self.base = base_dataset
        self.levels = level_indices
        self.current_level = min(int(current_epoch / max_epochs * K), K-1)
        # объединяем индексы всех уровней до current_level
        self.active_indices = []
        for i in range(self.current_level + 1):
            self.active_indices.extend(self.levels[i])
    def __len__(self): return len(self.active_indices)
    def __getitem__(self, idx):
        real_idx = self.active_indices[idx]
        return self.base[real_idx]

Ожидаемый результат этапа

  • Реализованы классы и функции для генерации батчей как в режиме curriculum, так и baseline.
  • Проверено, что на первой эпохе используется только уровень 0, на последней — все 5.

Этап 3: Базовая модель и обучение (≈1.5 часа)

Действия

  1. Определить простую свёрточную сеть:
class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.pool = nn.MaxPool2d(2,2)
        self.fc1 = nn.Linear(64*7*7, 128)
        self.fc2 = nn.Linear(128, num_classes)
    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))
        x = x.view(x.size(0), -1)
        x = torch.relu(self.fc1(x))
        return self.fc2(x)
  1. Зафиксировать гиперпараметры:

    • Оптимизатор: Adam, lr=1e-3
    • Batch size: 64
    • Число эпох: 20 (или до достижения accuracy 90% на валидации)
    • Функция потерь: CrossEntropyLoss
  2. Обучить baseline (случайный shuffle) и сохранить лоссы/accuracy на валидации каждую эпоху.

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

Рекомендация Сделайте равное количество градиентных шагов. Задайте общее количество шагов (например, 5000). Для baseline — 5000 шагов на всех данных (shuffle). Для curriculum — те же 5000 шагов, но на первых шагах используются только простые примеры, а затем постепенно добавляются сложные.

Ожидаемый результат этапа

  • Два файла с логами обучения (loss на train, accuracy на val) для baseline и curriculum.
  • Код обучения в виде функций train_baseline() и train_curriculum().

Этап 4: Анализ сходимости (≈1 час)

Действия

  1. Построить на одном графике validation accuracy по числу шагов/эпох для обеих стратегий.
  2. Определить порог accuracy (например, 90%). Найти номер шага (или эпохи), когда этот порог был впервые достигнут каждой стратегией.
  3. Вычислить относительное ускорение:
    speedup = (steps_baseline - steps_curriculum) / steps_baseline * 100%
  4. Если ускорение ≥ 30% — задача выполнена. Если нет — проанализировать причины (метрика сложности, количество уровней, гиперпараметры) и при необходимости скорректировать.
import matplotlib.pyplot as plt

steps = range(len(baseline_acc))
plt.plot(steps, baseline_acc, label='baseline')
plt.plot(steps, curriculum_acc, label='curriculum')
plt.axhline(90, color='gray', linestyle='--')
plt.legend()
plt.show()

Ожидаемый результат этапа

  • График сходимости (png/pdf).
  • Численное значение ускорения.
  • Вывод о достижении цели.

Этап 5: Документирование и отчет (≈0.5 часа)

Действия

  1. Оформить код в виде Python-скрипта (или ноутбука) с комментариями.
  2. Написать краткий отчет (README или раздел в ноутбуке), содержащий:
    • описание метрики сложности;
    • параметры curriculum (K, закон роста);
    • полученные графики;
    • значение ускорения;
    • анализ: почему curriculum помог (или не помог).
  3. Зафиксировать зависимости в requirements.txt (torch, torchvision, matplotlib, numpy).

Ожидаемый результат этапа

  • Архив с кодом, результатами, графиками и отчетом.
  • Четкое заключение о применимости curriculum learning для данного датасета.

5. Критерии приемки (Definition of Done)

  • Определена и реализована воспроизводимая метрика сложности для датасета.
  • Данные отсортированы по возрастанию сложности и разбиты на уровни.
  • Реализована стратегия curriculum с динамическим расширением набора данных по мере обучения.
  • Baseline (случайный порядок) и curriculum обучены на одинаковом количестве градиентных шагов с одними гиперпараметрами.
  • Получены графики сходимости (loss/accuracy vs steps) для обеих стратегий.
  • Ускорение конвергенции (по достижении порога accuracy) составляет не менее 30%.
  • Код задокументирован, содержит README и может быть повторно запущен.
  • В отчете объяснены причины ускорения или его отсутствия.

6. Ожидаемый результат

Основной результат — папка с файлами:

  • curriculum_learning.py — основной скрипт (или curriculum_learning.ipynb).
  • requirements.txt — список зависимостей.
  • results/ — папка с логами обучения (baseline_log.npy, curriculum_log.npy).
  • plots/convergence.png — график сходимости.
  • README.md — отчет с выводами и инструкцией по запуску.

Дополнительно (опционально): анализ чувствительности к количеству уровней K, выбору метрики сложности, закону роста (линейный, логарифмический).

7. Возможные сложности и их решение

СложностьРешение
Метрика сложности (средняя яркость) может не коррелировать с реальной сложностью для модели.Использовать альтернативную метрику: длина градиента после одного шага на небольшой предобученной сети (оракул). Реализовать как отдельную утилиту.
Количество данных в эпоху различается между baseline и curriculum, что усложняет сравнение по эпохам.Сравнивать по количеству градиентных шагов (итераций). Для этого задать общее число шагов, а curriculum подаёт батчи из активного пула.
Переобучение на простых примерах на ранних этапах.Добавить регуляризацию (dropout, weight decay) или медленнее увеличивать сложность (например, добавлять новый уровень не каждую эпоху, а через определённое число шагов).
Выбор количества уровней K.Провести эксперимент с K=3,5,10. В отчёте указать, какое K дало наилучшее ускорение.
Воспроизводимость результатов.Зафиксировать seed для PyTorch и NumPy, указать в коде.

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Подготовка данных и метрика сложности1 час
Этап 2: Создание стратегии curriculum1 час
Этап 3: Базовая модель и обучение1.5 часа
Этап 4: Анализ сходимости1 час
Этап 5: Документирование и отчет0.5 часа
Итого5 часов

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

9. Связанные вопросы из базы знаний

ВопросТема
12Data Augmentation: методы и реализация
45Transfer Learning: тонкая настройка предобученных моделей
128Learning Rate Scheduling: ReduceLROnPlateau, CosineAnnealing
201Curriculum Learning: теоретические основы
273Практика: Curriculum Learning (данное ТЗ)
350Функции потерь: CrossEntropy, Focal Loss
512Оценка модели: accuracy, precision, recall
650Трюки обучения: Gradient Clipping, Batch Normalization
789Batch Normalization: влияние на сходимость
890Градиентный спуск: Adam, SGD, Momentum

10. Чек-лист самопроверки

  • Я выбрал датасет и реализовал метрику сложности (средняя яркость или альтернативу).
  • Я отсортировал обучающую выборку и разбил её на уровни.
  • Я создал класс CurriculumDataset, который на каждой эпохе использует только часть уровней.
  • Я обучил baseline (shuffle) и curriculum при одинаковых гиперпараметрах и одинаковом количестве градиентных шагов.
  • Я построил график сходимости и убедился, что ускорение составляет ≥30% (или объяснил, почему не получилось).
  • Я задокументировал код, оформил результаты и написал отчет.