Реализовать active learning loop

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

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

Разработать и протестировать цикл активного обучения (active learning loop) для задачи классификации коротких текстов. Основная цель — научиться выбирать наиболее неопределённые для текущей модели примеры из пула неразмеченных данных, запрашивать для них метки (в симуляции — у «оракула») и дообучать модель на пополненном размеченном наборе. Ключевой результат итоговая модель, обученная с использованием active learning, должна достигать точности, сопоставимой с моделью, обученной на всех размеченных данных, но при этом используя не более 30% исходного объёма размеченных примеров (т.е. в ~3 раза меньше).

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

Что нужноОткуда взять
Неразмеченный набор текстов (1000–2000 примеров)Открытый датасет: sklearn.datasets.fetch_20newsgroups (категории: sci.space, rec.autos, comp.graphics, talk.politics.guns — 4 класса, ~500 документов на класс)
Базовая модель классификатораScikit-learn LogisticRegression + эмбеддинги из sentence-transformers/all-MiniLM-L6-v2 (384-мерные векторы)
Стратегия отбора примеровРеализовать uncertainty sampling: минимальная уверенность (least confidence), margin sampling, entropy — на выбор
Оракул (разметчик)Симулировать — истинные метки из того же датасета, «запрос» возвращает метку из скрытого массива
Метрики качестваAccuracy, F1 (macro), кривая обучения (число запросов vs точность)

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

  1. Загружаем датасет 20newsgroups с помощью sklearn.datasets.fetch_20newsgroups.
  2. Разбиваем на train/test (70/30). Train часть используем как пул неразмеченных данных — метки прячем.
  3. Случайно выбираем 10 примеров из пула как начальный размеченный набор (seed).
  4. Создаём массив y_pool с истинными метками пула — это «оракул».
  5. В цикле: модель предсказывает вероятности для всех неразмеченных, вычисляем метрику неопределённости, выбираем top-k (например, 10), «запрашиваем» метку из y_pool, добавляем в размеченный набор и удаляем из пула.

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

КомпонентИнструментыНазначение
Язык / средаPython 3.10+, Jupyter NotebookРазработка и выполнение
Эмбеддингиsentence-transformers, transformersПреобразование текстов в векторы
Классификацияscikit-learn (LogisticRegression)Базовая модель
Active learning логикаnumpy, collectionsРеализация цикла и стратегий
Визуализацияmatplotlib, pandasКривые обучения, сравнение
Валидацияsklearn.metrics (accuracy, f1_score)Оценка качества

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

Этап 1: Подготовка данных и baseline (1 час)

Действия

  1. Загрузить датасет: from sklearn.datasets import fetch_20newsgroups; подмножество из 4 классов: categories=['sci.space','rec.autos','comp.graphics','talk.politics.guns'].
  2. Разделить на train (70%) и test (30%). Удалить заголовки, подписи и цитаты (remove=('headers','footers','quotes')).
  3. Посчитать эмбеддинги для всех текстов с помощью SentenceTransformer('all-MiniLM-L6-v2'). Сохранить как numpy array.
  4. Обучить baseline на всех train данных (с метками) — записать accuracy / F1 на test.
  5. Создать класс DataPool: список pool_indices (все train), список labeled_indices (изначально 10 случайных), y_true (истинные метки), embeddings.

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

  • Загруженный и предобработанный датасет, файл embeddings.npy (опционально).
  • Baseline-метрики (accuracy, F1) записаны.
  • Класс DataPool готов для использования.

Этап 2: Реализация стратегии отбора (1 час)

Действия

  1. Реализовать функцию compute_uncertainty(probabilities, strategy='least_confidence'):
    • least_confidence: 1 - max(prob)
    • margin: prob[:, 0] - prob[:, 1] (разница между двумя самыми вероятными классами)
    • entropy: -sum(p * log(p))
  2. Реализовать функцию select_next_batch(pool, model, strategy, k=10), которая:
    • получает вероятности для всех неразмеченных примеров через model.predict_proba
    • вычисляет неопределённость по стратегии
    • возвращает индексы k образцов с наибольшей неопределённостью
  3. Протестировать на небольшом наборе (первые 50 примеров), убедиться, что отбор корректный.

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

  • Функции compute_uncertainty и select_next_batch.
  • Юнит-тест с ручной проверкой (например, пример с известными вероятностями).

Этап 3: Реализация цикла активного обучения (1.5 часа)

Действия

  1. Написать класс ActiveLearningLoop:
    • Инициализация: DataPool, модель (LogisticRegression), стратегия, batch_size, max_queries (например, 100).
    • Метод run():
      • Цикл до max_queries:
        • Обучить модель на labeled_indices (X = эмбеддинги[labeled_indices], y = y_true[labeled_indices]).
        • Выбрать batch_size примеров из пула.
        • Переместить их из пула в размеченный набор (через oracle).
        • Сохранить текущую точность на test (по желанию логировать).
    • Логирование: номер итерации, размер размеченного набора, accuracy на test.
  2. Обучить модель на каждом шаге только на размеченных данных.
  3. Остановиться, когда исчерпан лимит запросов или пул пуст.
  4. Запустить цикл с batch_size=10, max_queries=50 (т.е. максимум 10 + 500 = 510 размеченных — около 30% от train).

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

  • Реализация класса с методом run(), возвращающим историю (iteration, labeled_size, accuracy).
  • Вывод лога в консоль или файл.

Этап 4: Сравнение со случайным отбором и baseline (1 час)

Действия

  1. Реализовать второй цикл с той же инициализацией, но стратегия отбора — random (случайный выбор из пула).
  2. Запустить его с теми же параметрами (начальный seed, batch_size, max_queries).
  3. Построить график: ось X — количество размеченных примеров, ось Y — accuracy на test. Три кривые:
    • Baseline (обучение на всех данных — константная линия)
    • Active learning (uncertainty)
    • Random sampling
  4. На графике отметить точку, где active learning достигает 95% от baseline accuracy — показать, сколько данных потребовалось.
  5. Вычислить коэффициент экономии: (размер baseline набора) / (размер данных, при котором active learning достиг 95% accuracy).

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

  • График (сохранённый как learning_curve.png).
  • Численная оценка экономии: «Active learning достиг 95% baseline accuracy при N примерах (вместо M), что в X раз меньше».

Этап 5: Документация и чистка (0.5 часа)

Действия

  1. Добавить комментарии и docstrings во все функции.
  2. Упаковать код в один файл active_learning_loop.py и/или Jupyter notebook с ячейками, разделёнными по этапам.
  3. Написать краткий README.md с описанием задачи, как запустить, результаты.
  4. Убедиться, что код воспроизводим (зафиксирован random_state=42).

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

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

  • Реализован класс DataPool, корректно управляющий размеченными/неразмеченными данными.
  • Реализованы три стратегии неопределённости (least confidence, margin, entropy) — хотя бы одна используется, остальные опциональны.
  • Цикл active learning выполняет запросы к оракулу и дообучает модель на каждом шаге.
  • Итоговая модель active learning достигает accuracy ≥ 95% от baseline при использовании ≤ 30% размеченных данных.
  • Построен график сравнения active learning vs random vs baseline.
  • Код воспроизводим: random_state фиксирован, зависимости указаны в requirements.txt.
  • Присутствует лог процесса (количество итераций, точность на каждом шаге).
  • Результат представлен в виде Jupyter notebook или Python-скрипта с комментариями.

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

Основной артефакт

  • Файл active_learning.ipynb (или active_learning.py + README.md) с полным конвейером.

Содержание

  • Загрузка и эмбеддинги
  • Baseline
  • Реализация стратегий
  • Цикл active learning
  • Сравнение с random
  • Визуализация
  • Выводы (метрики экономии)

Дополнительные результаты (опционально):

  • Сравнение разных стратегий (least confidence vs entropy vs margin) в одной таблице.
  • Анализ влияния размера начального seed.
  • Эксперимент с другой моделью (например, Random Forest вместо LogisticRegression).

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

СложностьРешение
Медленное обучение модели на каждой итерацииИспользовать быструю линейную модель (LogisticRegression); можно инкрементально обновлять (SGDClassifier с partial_fit)
Несбалансированность классов в отобранных batchИспользовать стратифицированный отбор или комбинировать uncertainty с разнообразием (diversity sampling)
Выбор начального seed влияет на результатПовторить эксперимент с несколькими random seeds (3–5) и усреднить кривые
«Oracle query» замедляет симуляциюПросто брать метку из массива (O(1)), не имитировать задержку
График не показывает явного преимущества active learningУменьшить batch_size (например, до 5), увеличить количество итераций; возможно, эмбеддинги недостаточно информативны — попробовать более качественную модель (e.g., all-mpnet-base-v2)

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

ЭтапВремя (часов)
Этап 1: Подготовка данных и baseline1
Этап 2: Реализация стратегии отбора1
Этап 3: Реализация цикла активного обучения1.5
Этап 4: Сравнение со случайным отбором1
Этап 5: Документация и чистка0.5
Итого5

Примечание для первого раза: если вы впервые работаете с sentence-transformers или active learning, заложите дополнительно 1 час на установку библиотек и отладку.

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

ВопросТема
42Оценка неопределённости в классификации (uncertainty estimation)
83Методы активного обучения (strategies)
124Эмбеддинги текстов с sentence-transformers
201Скорость обучения vs количество размеченных данных
289Инкрементальное обучение (partial_fit)
314Работа с дисбалансом классов в обучении
425Сравнение стратегий отбора (empirical evaluation)
518Логирование и визуализация экспериментов
637Random seed и воспроизводимость
789Оценка экономии данных (data efficiency)

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

  • Я загрузил датасет и проверил, что классы сбалансированы.
  • Я вычислил baseline на всех размеченных данных и зафиксировал точность.
  • Я реализовал хотя бы одну стратегию uncertainty и убедился, что она выбирает разные примеры при разных вероятностях.
  • Мой цикл active learning корректно обновляет размеченный набор и пул, не утекая метки.
  • Я построил график и вижу, что active learning достигает высокой точности быстрее, чем случайный выбор.
  • Я зафиксировал random_state и получил воспроизводимые результаты.
  • Я добавил комментарии и docstrings к ключевым функциям.