Головна▸Статті▸Дані та анотації

Розмітка даних

Стратегії якісного маркування - workflow анотації, контроль якості, методи консенсусу та active learning для ефективного маркування.

mysimulator teamОновлено 2026-01≈ 5 хв читання▶ Open the simulation

Розмітка даних - це процес додавання міток (labels) до даних для supervised learning. Якість міток напряму впливає на якість моделі - "garbage in, garbage out". Ефективне маркування вимагає чітких guidelines, контролю якості, методів консенсусу та стратегічного вибору що маркувати.

Де використовується

Маркування даних критичне для більшості ML задач:

Платформи: 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+

Що ви робите на практиці

  1. Визначте task та схему міток - Що маркуємо, які категорії
  2. Створіть annotation guidelines - Детальні інструкції з прикладами
  3. Оберіть annotation tool - Label Studio, Labelbox тощо
  4. Рекрутуйте анотаторів - In-house, crowdsource або hybrid
  5. Проведіть pilot - Невелика вибірка для валідації guidelines
  6. Встановіть QC процес - Gold standard, inter-annotator agreement
  7. Маркуйте з моніторингом - Відстежуйте якість в реальному часі
  8. Резолвіть disagreements - Експертний review, voting
  9. Ітеруйте guidelines - Уточнюйте на основі edge cases
  10. Інтегруйте з 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 з прикладами - [ ] 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 моделі

Пов'язані сторінки

Додаткові матеріали

Спробуйте наживо

Дослідіть пов'язану модель на практиці: відкрийте Dataset Label Quality Simulator і змінюйте параметри під час роботи. Усе лишається у вашому браузері.

▶ Відкрити симуляцію Dataset Label Quality Simulator

Що ви знайшли?

Додати кроки відтворення (опційно)