Разметка данных — самая дорогая часть ML-проекта. Для NLP с сотнями тысяч примеров бюджет на аннотацию часто превышает бюджет на обучение. Мы сталкивались с проектами, где заказчик потратил 80% времени на ручную разметку, а прирост метрик составил всего 2%. Причина: 90% примеров модель распознает уверенно — размечать их бессмысленно. Active Learning решает это: алгоритм сам отбирает 'трудные' объекты, где точность модели низкая. Размечать нужно только их. Результат: 5-10-кратная экономия бюджета разметки при сохранении качества на уровне 90% от полной разметки. Наши инженеры внедряют Active Learning под ключ: от выбора стратегии до интеграции с платформами. В этой статье — основные стратегии, код и опыт внедрения.
Active Learning — метод машинного обучения, где алгоритм выбирает наиболее информативные примеры для разметки.
Какие стратегии работают лучше?
На практике мы используем три основные стратегии отбора:
| Стратегия | Принцип | Когда применять |
|---|---|---|
| Uncertainty Sampling | Выбирает примеры с максимальной неопределённостью модели (энтропия, margin, least confidence) | Классификация, регрессия — любые задачи с вероятностным выходом |
| Query by Committee | Ансамбль моделей голосует; отбираются примеры с максимальным разбросом голосов | Нет данных для калибровки вероятностей, нужна робастность |
| Core-Set Sampling | Выбирает примеры, максимально удалённые от уже размеченных (геометрическое покрытие) | Нужно разнообразие в датасете, избегание дублирования неопределённости |
Uncertainty Sampling
Классический подход — модель предсказывает вероятности, и мы отбираем примеры с наименьшей уверенностью. Реализация обычно через энтропию:
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.base import BaseEstimator
class UncertaintySampler:
def __init__(self, model: BaseEstimator, strategy='entropy'):
self.model = model
self.strategy = strategy
def query(self, X_unlabeled: np.ndarray, n_instances: int = 10) -> np.ndarray:
proba = self.model.predict_proba(X_unlabeled)
if self.strategy == 'entropy':
scores = -np.sum(proba * np.log(proba + 1e-10), axis=1)
elif self.strategy == 'margin':
sorted_proba = np.sort(proba, axis=1)
scores = 1 - (sorted_proba[:, -1] - sorted_proba[:, -2])
elif self.strategy == 'least_confident':
scores = 1 - proba.max(axis=1)
return np.argsort(scores)[-n_instances:]
Плюс: простая реализация, работает для любых моделей с predict_proba. Минус: не учитывает разнообразие — может отбирать похожие неопределённые примеры.
Query by Committee
Здесь мы обучаем не одну модель, а ансамбль (например, на бутстрап-выборках). Несогласие участников комитета — индикатор информативности:
from sklearn.base import clone
class CommitteeSampler:
def __init__(self, base_estimator, n_members=5):
self.committee = [clone(base_estimator) for _ in range(n_members)]
def fit_committee(self, X_labeled, y_labeled):
n = len(X_labeled)
for member in self.committee:
bootstrap_idx = np.random.choice(n, n, replace=True)
member.fit(X_labeled[bootstrap_idx], y_labeled[bootstrap_idx])
def query(self, X_unlabeled, n_instances=10):
predictions = np.array([
member.predict(X_unlabeled) for member in self.committee
])
vote_entropy = []
for sample_idx in range(X_unlabeled.shape[0]):
votes = predictions[:, sample_idx]
unique, counts = np.unique(votes, return_counts=True)
probs = counts / len(votes)
entropy = -np.sum(probs * np.log(probs + 1e-10))
vote_entropy.append(entropy)
return np.argsort(vote_entropy)[-n_instances:]
Эта стратегия устойчивее к переобучению и часто даёт лучший прирост качества.
Core-Set Sampling
Если мы хотим покрыть всё признаковое пространство, а не только границы решений — используем Core-Set. Он выбирает точки, максимально далёкие от уже размеченных:
from sklearn.metrics import pairwise_distances
def core_set_selection(X_labeled, X_unlabeled, n_instances):
selected_indices = []
labeled_pool = X_labeled.copy()
for _ in range(n_instances):
distances = pairwise_distances(X_unlabeled, labeled_pool)
min_distances = distances.min(axis=1)
best_idx = np.argmax(min_distances)
selected_indices.append(best_idx)
labeled_pool = np.vstack([labeled_pool, X_unlabeled[best_idx]])
return np.array(selected_indices)
Почему Active Learning не всегда выгоден?
Если датасет уже сбалансирован и содержит мало 'лёгких' примеров — выигрыш может быть небольшим. Мы всегда проводим предварительный анализ: строим кривую обучения на выборке, чтобы понять потенциал экономии. Например, для одного из проектов предварительный тест показал, что AL даст лишь 20% экономии, и мы рекомендовали не внедрять.
Когда стоит использовать Committee вместо Uncertainty?
Committee лучше работает с зашумлёнными данными и когда калибровка вероятностей модели неточна. На практике мы часто комбинируем обе стратегии в рамках одного цикла: на первых итерациях используем Committee для разнообразия, затем переключаемся на Uncertainty для точности.
Active Learning для NER
Для Sequence Labeling мы используем неопределённость на уровне токенов. Агрегируем энтропию по всем токенам предложения — выбираем самые неуверенные предложения:
import torch
from transformers import AutoModelForTokenClassification, AutoTokenizer
def ner_uncertainty_sampling(texts, model, tokenizer, n_instances=20):
sentence_uncertainties = []
for i, text in enumerate(texts):
inputs = tokenizer(text, return_tensors='pt', truncation=True, max_length=512)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1).squeeze()
token_entropy = -(probs * torch.log(probs + 1e-10)).sum(dim=-1)
sentence_uncertainty = token_entropy.max().item()
sentence_uncertainties.append((i, sentence_uncertainty))
sentence_uncertainties.sort(key=lambda x: x[1], reverse=True)
return [idx for idx, _ in sentence_uncertainties[:n_instances]]
Этот подход даёт 3-5-кратную экономию для NER-датасетов.
Полный цикл Active Learning
class ActiveLearningPipeline:
def __init__(self, model, sampler, labeling_budget):
self.model = model
self.sampler = sampler
self.budget = labeling_budget
self.labeled_count = 0
self.performance_history = []
def run(self, X_initial, y_initial, X_pool, batch_size=20):
X_labeled, y_labeled = X_initial.copy(), y_initial.copy()
X_unlabeled = X_pool.copy()
while self.labeled_count < self.budget and len(X_unlabeled) > 0:
self.model.fit(X_labeled, y_labeled)
current_metric = self.evaluate(X_labeled, y_labeled)
self.performance_history.append({
'n_labeled': len(X_labeled),
'metric': current_metric
})
query_idx = self.sampler.query(X_unlabeled, n_instances=batch_size)
new_y = get_labels_from_annotator(X_unlabeled[query_idx])
X_labeled = np.vstack([X_labeled, X_unlinked[query_idx]])
y_labeled = np.concatenate([y_labeled, new_y])
X_unlabeled = np.delete(X_unlabeled, query_idx, axis=0)
self.labeled_count += batch_size
return self.performance_history
Этот код иллюстрирует полный цикл — его мы адаптируем под вашу инфраструктуру.
Как мы внедряем Active Learning в ваш проект
Типовой процесс состоит из 5 этапов:
- Анализ данных и бизнес-задачи — определяем метрики, структуру датасета, доступные типы разметки.
- Выбор стратегии отбора — тестируем Uncertainty Sampling, Committee и Core-Set на вашей задаче; выбираем лучшую по кривой обучения.
- Разработка пайплайна — реализуем цикл: обучить → отобрать → разметить → дообучить. Интегрируем с платформами Label Studio или Scale AI.
- Пилотный запуск — на небольшом датасете (1000-5000 примеров) проверяем эффективность.
- Продакшн — автоматизируем процесс, настраиваем мониторинг метрик (latency p99, GPU utilization) и перезапуск циклов.
Сроки и стоимость
| Этап | Длительность |
|---|---|
| Базовый пайплайн (Uncertainty Sampling + интеграция с Label Studio) | 2-3 недели |
| Расширенный (Committee + Core-Set + NLP/NER) | 6-8 недель |
| Кастомная холодная стратегия под специфику данных | от 8 недель |
Стоимость базового пилота — от 150 000 рублей, полного внедрения — от 500 000 рублей. Итоговая сумма зависит от сложности задачи, необходимого стека (PyTorch, HuggingFace, LLM), объёма данных и требований к инфраструктуре. Экономия в среднем 1-2 млн рублей на крупных проектах. Оценим ваш проект бесплатно.
Что входит в результат внедрения
- Рабочий пайплайн Active Learning, интегрированный с вашей системой разметки
- Дашборд метрик: динамика качества модели, экономия бюджета, распределение выбранных примеров
- Документация и руководство по эксплуатации
- Поддержка 3 месяца после запуска
Мы реализовали Active Learning для 15+ проектов в NLP и Computer Vision. Один из последних кейсов: задача классификации отзывов — сократили объём разметки с 50 000 до 8 000 примеров при сохранении F1 на уровне 0.94.
Active Learning — проверенная методология, которая уже сэкономила нашим клиентам миллионы рублей. Гарантируем качество внедрения и прозрачный результат.
Свяжитесь с нами: расскажите о своей задаче — мы предложим оптимальную стратегию Active Learning. Получите консультацию инженера по машинному обучению.







