Розмітка даних - це процес додавання міток (labels) до даних для supervised learning. Якість міток напряму впливає на якість моделі - "garbage in, garbage out". Ефективне маркування вимагає чітких guidelines, контролю якості, методів консенсусу та стратегічного вибору що маркувати.
Де використовується
Маркування даних критичне для більшості ML задач:
- Computer Vision: Bounding boxes, сегментація, класифікація зображень
- NLP: Named entities, sentiment, intent classification
- Speech: Транскрипція, speaker diarization
- Рекомендації: Релевантність, preference ratings
- Медицина: Діагнози, анотації зображень
- Автономні транспортні засоби: Об'єкти, дорожня розмітка, поведінка
Платформи: Label Studio, Labelbox, Scale AI, Amazon SageMaker Ground Truth.
Типи маркування
| Тип | Опис | Приклади |
|---|---|---|
| Classification | Присвоєння категорій | Spam/not spam, sentiment |
| Bounding box | Прямокутники навколо об'єктів | Object detection |
| Segmentation | Pixel-level маски | Medical imaging, autonomous driving |
| Sequence labeling | Мітки для послідовностей | NER, POS tagging |
| Relation extraction | Зв'язки між entities | Knowledge graphs |
| Ranking/Rating | Ordering або scoring | Search relevance |
| Transcription | Text із audio/image | Speech-to-text, OCR |
Типові дані
| Аспект | Типові значення |
|---|---|
| Dataset size | 1K до 1M+ зразків |
| Labels per sample | 1 до 100+ |
| Annotators per sample | 1 до 5 (для консенсусу) |
| Accuracy target | 95-99% |
| Cost per label | $0.01 до $10+ |
Що ви робите на практиці
- Визначте task та схему міток - Що маркуємо, які категорії
- Створіть annotation guidelines - Детальні інструкції з прикладами
- Оберіть annotation tool - Label Studio, Labelbox тощо
- Рекрутуйте анотаторів - In-house, crowdsource або hybrid
- Проведіть pilot - Невелика вибірка для валідації guidelines
- Встановіть QC процес - Gold standard, inter-annotator agreement
- Маркуйте з моніторингом - Відстежуйте якість в реальному часі
- Резолвіть disagreements - Експертний review, voting
- Ітеруйте guidelines - Уточнюйте на основі edge cases
- Інтегруйте з ML pipeline - Active learning, model-in-the-loop
Annotation Guidelines
Ефективні guidelines включають:
# Guidelines для маркування Spam
## Мета
Класифікувати emails як SPAM або NOT_SPAM.
## Визначення SPAM
- Небажана комерційна реклама
- Phishing спроби
- Scam пропозиції
## Визначення NOT_SPAM
- Легітимна ділова комунікація
- Newsletters на які підписались
- Транзакційні повідомлення
## Edge Cases
- Marketing від компанії з якою взаємодіяли → NOT_SPAM
- Newsletter без можливості unsubscribe → SPAM
- Автоматичні reply → NOT_SPAM
## Приклади
| Email | Label | Пояснення |
|-------|-------|-----------|
| "Win $1000000!!!" | SPAM | Scam offer |
| "Your order shipped" | NOT_SPAM | Transaction |
Контроль якості
Методи QC:
import numpy as np
from sklearn.metrics import cohen_kappa_score
def inter_annotator_agreement(annotations_a, annotations_b):
"""Обчислення Cohen's Kappa для двох анотаторів."""
return cohen_kappa_score(annotations_a, annotations_b)
def fleiss_kappa(annotations_matrix):
"""Fleiss' Kappa для багатьох анотаторів."""
# annotations_matrix: (n_samples, n_categories) - counts
n_samples = annotations_matrix.shape[0]
n_annotators = annotations_matrix.sum(axis=1)[0]
p_j = annotations_matrix.sum(axis=0) / (n_samples * n_annotators)
P_i = (annotations_matrix ** 2).sum(axis=1) - n_annotators
P_i = P_i / (n_annotators * (n_annotators - 1))
P_bar = P_i.mean()
P_e = (p_j ** 2).sum()
kappa = (P_bar - P_e) / (1 - P_e)
return kappa
class QualityControl:
def __init__(self, gold_standard_samples, gold_threshold=0.9):
self.gold_samples = gold_standard_samples
self.gold_threshold = gold_threshold
self.annotator_accuracy = {}
def check_gold_sample(self, annotator_id, sample_id, label):
"""Перевірка анотатора на gold sample."""
correct = label == self.gold_samples[sample_id]
if annotator_id not in self.annotator_accuracy:
self.annotator_accuracy[annotator_id] = []
self.annotator_accuracy[annotator_id].append(correct)
return correct
def get_annotator_quality(self, annotator_id):
"""Середня точність анотатора на gold samples."""
if annotator_id not in self.annotator_accuracy:
return None
return np.mean(self.annotator_accuracy[annotator_id])
def should_flag_annotator(self, annotator_id):
"""Чи потрібно флагнути анотатора для review."""
quality = self.get_annotator_quality(annotator_id)
if quality is None:
return False
return quality < self.gold_threshold
Методи консенсусу
Majority voting:
from collections import Counter
def majority_vote(annotations):
"""Просте majority voting."""
counts = Counter(annotations)
return counts.most_common(1)[0][0]
Weighted voting:
def weighted_vote(annotations, annotator_ids, annotator_weights):
"""Weighted voting на основі якості анотаторів."""
weighted_counts = {}
for label, annotator_id in zip(annotations, annotator_ids):
weight = annotator_weights.get(annotator_id, 1.0)
weighted_counts[label] = weighted_counts.get(label, 0) + weight
return max(weighted_counts, key=weighted_counts.get)
Dawid-Skene model:
# Ймовірнісна модель що оцінює true labels та annotator reliability
# Імплементовано в бібліотеках як crowd-kit
from crowdkit.aggregation import DawidSkene
ds = DawidSkene()
aggregated_labels = ds.fit_predict(annotations_df)
Active Learning для маркування
Пріоритизація зразків для маркування:
import numpy as np
from scipy.stats import entropy
class ActiveLearner:
def __init__(self, model):
self.model = model
def uncertainty_sampling(self, X_unlabeled, n_samples=100):
"""Вибір найбільш uncertain зразків."""
probs = self.model.predict_proba(X_unlabeled)
uncertainties = entropy(probs, axis=1)
indices = np.argsort(uncertainties)[-n_samples:]
return indices
def margin_sampling(self, X_unlabeled, n_samples=100):
"""Вибір з найменшим margin між top-2 predictions."""
probs = self.model.predict_proba(X_unlabeled)
sorted_probs = np.sort(probs, axis=1)
margins = sorted_probs[:, -1] - sorted_probs[:, -2]
indices = np.argsort(margins)[:n_samples]
return indices
def query_by_committee(self, X_unlabeled, models, n_samples=100):
"""Вибір де моделі найбільше не згодні."""
predictions = np.array([m.predict(X_unlabeled) for m in models])
disagreement = [len(set(predictions[:, i])) for i in range(len(X_unlabeled))]
indices = np.argsort(disagreement)[-n_samples:]
return indices
Типові помилки та чек-лист
- [ ] Неясні guidelines: Анотатори інтерпретують по-різному
- [ ] Недостатній QC: Погані мітки потрапляють в модель
- [ ] Annotator fatigue: Якість падає з часом
- [ ] Selection bias: Маркуємо нерепрезентативну вибірку
- [ ] Label leakage: Ground truth просочується в features
Чек-лист для продакшену
Перед маркуванням: - [ ] Детальні guidelines з прикладами - [ ] Pilot з невеликою вибіркою - [ ] QC процес визначено
Під час маркування: - [ ] Регулярні gold sample перевірки - [ ] Моніторинг inter-annotator agreement - [ ] Швидкий feedback анотаторам
Після маркування: - [ ] Аналіз disagreements - [ ] Валідація на holdout set - [ ] Документація edge cases
Інструменти та стек
| Категорія | Інструменти |
|---|---|
| Open Source | Label Studio, CVAT, Prodigy |
| Commercial | Labelbox, Scale AI, Amazon MTurk |
| Active Learning | Modal, Snorkel |
Приклад міні-кейсу
Сценарій: NER маркування для legal documents.
Підхід: - Label Studio з custom схемою - 3 анотатори + expert review - Active learning для hard cases
Результати: - Inter-annotator agreement: 0.87 Cohen's Kappa - 50% зменшення маркування через active learning - 94% F1 score для NER моделі
Пов'язані сторінки
- Індекс: ML Field Guide
- Active Learning
- Weak Supervision
Додаткові матеріали
Спробуйте наживо
Дослідіть пов'язану модель на практиці: відкрийте Dataset Label Quality Simulator і змінюйте параметри під час роботи. Усе лишається у вашому браузері.
▶ Відкрити симуляцію Dataset Label Quality Simulator