Contents

Chapter 1

Annotation Workflow Guide

Overview

This guide walks you through setting up and running a complete data labeling workflow β€” from raw unlabeled data to model-ready training labels.

The Labeling Lifecycle

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Raw Data    β”‚ β†’  β”‚ Task Queue   β”‚ β†’  β”‚ Annotation    β”‚ β†’  β”‚ Consensusβ”‚
β”‚ (unlabeled) β”‚    β”‚ (prioritised)β”‚    β”‚ (N annotators)β”‚    β”‚ Resolutionβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚ Training    β”‚ ←  β”‚ Export       β”‚ ←  β”‚ Quality Audit β”‚ β†β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ Dataset     β”‚    β”‚ (approved)   β”‚    β”‚ & Adjudicationβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step-by-Step Setup

1. Define Your Schema

Before anything else, decide what labels you need. Open configs/schema_config.yaml and define:

  • Categories: What are the possible labels?
  • Descriptions: How should annotators interpret each category?
  • Examples: What are clear-cut examples of each?
  • Hierarchy: Do labels have parent-child relationships?
python
from src.schema import LabelSchema, Category, LabelType

schema = LabelSchema(
    name="my_project",
    version="1.0",
    label_type=LabelType.CLASSIFICATION,
    categories=[
        Category(
            name="positive",
            description="Expresses satisfaction or approval",
            examples=["Great product!", "Highly recommend"],
            counter_examples=["It's okay (β†’ neutral)"],
        ),
        # ... more categories
    ],
)

Key decisions:

  • Start with fewer categories. You can always split later.
  • Write descriptions as if explaining to a new hire who's never seen your data.
  • Include counter-examples for every common mistake.

2. Configure the Workflow

Edit configs/labeling_config.yaml. The critical settings:

SettingRecommendationWhy
redundancy3 for classification, 5 for subjectiveMore raters = better agreement measurement
assignment_strategystratifiedRoutes hard items to experienced annotators
consensus.min_agreement0.67 (2/3)Balances quality vs. throughput
quality.golden_items.fraction0.10Catches random clicking without excessive cost
active_learning.strategyentropyBest general-purpose uncertainty measure

3. Prepare Your Data

Create Task objects from your unlabeled data:

python
from src.workflow import Task, WorkflowEngine

tasks = [
    Task(
        task_id=f"item_{i:04d}",
        data={"text": row["text"]},
        difficulty="normal",  # or "easy" / "hard"
    )
    for i, row in enumerate(your_data)
]

engine = WorkflowEngine(redundancy=3, min_agreement=0.67)
engine.add_tasks(tasks)

4. Create Batches and Assign

python
annotator_ids = ["ann_alice", "ann_bob", "ann_charlie"]

batch = engine.create_batch(
    batch_id="batch_001",
    annotator_ids=annotator_ids,
    strategy="round_robin",
    max_tasks=50,
)

5. Collect Labels

As annotators work (through your UI or the included templates/labeling_interface.html):

python
engine.submit_label(
    assignment_id="asgn_abc123",
    label="positive",
    duration_sec=4.2,
)

6. Resolve Consensus

Once all annotators have completed their assignments:

python
results = engine.resolve_all()

for result in results:
    if result.needs_adjudication:
        print(f"  {result.task_id} needs expert review")
    else:
        print(f"  {result.task_id} β†’ '{result.final_label}'")

7. Adjudicate Disagreements

Tasks that can't reach consensus go to a senior annotator:

python
for task in engine.tasks_needing_adjudication:
    # Show the task + all submitted labels to the expert
    engine.adjudicate(task.task_id, "neutral", adjudicator_id="expert_01")

8. Run Quality Audit

python
from src.quality_audit import generate_audit_report, Annotation

# Convert engine's internal data to Annotation objects for auditing
annotations = [...]  # build from engine._assignments
report = generate_audit_report(annotations)
print(report)

9. Export Training Data

python
approved_data = engine.export_approved()
# β†’ [{"task_id": ..., "data": {...}, "label": ..., "metadata": {...}}, ...]

Active Learning Loop

For maximum labeling efficiency, use the active learning module:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                                 β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
└─→ β”‚ Train    β”‚ β†’  β”‚ Score     β”‚ β†’  β”‚ Select β”‚ β”€β”˜
    β”‚ Model    β”‚    β”‚ Unlabeled β”‚    β”‚ Top-K  β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                          β”‚
                                          ↓
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                    β”‚ Label    β”‚
                                    β”‚ Selected β”‚
                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
python
from src.active_learning import entropy_sampling

# After training your model on the current labeled set:
probs = model.predict_proba(X_unlabeled)
result = entropy_sampling(probs.tolist(), budget=100)

# Queue the selected samples for labeling
selected_tasks = [all_tasks[i] for i in result.indices]
engine.add_tasks(selected_tasks)

Handling Schema Changes

When you need to add/rename/merge categories mid-project:

python
from src.schema import SchemaMigration, MigrationStep, MigrationAction

migration = SchemaMigration(
    from_version="1.0",
    to_version="2.0",
    steps=[
        MigrationStep(
            action=MigrationAction.ADD_CATEGORY,
            params={"name": "neutral"},
            reason="Annotators struggled with ambiguous reviews",
        ),
    ],
)

# Apply to existing labels
migrated_labels, n_changed = migration.apply_to_labels(existing_labels)

Common Pitfalls

ProblemSymptomSolution
Low agreementΞΊ < 0.4Rewrite category descriptions with more examples
Annotator fatigueSpeed increases, accuracy drops per batchLimit batch size to 50 items
Class imbalanceOne label dominates > 80%Use stratified sampling for batches
Schema creepToo many categoriesMerge rare categories, split only when needed
Gold item biasAnnotators recognise gold itemsRotate gold set regularly

Metrics to Track

1. Inter-annotator agreement (Cohen's ΞΊ / Fleiss' ΞΊ): target β‰₯ 0.6

2. Adjudication rate: should decrease over time as guidelines improve

3. Label velocity: items labeled per annotator per hour

4. Active learning efficiency: accuracy gain per labeled sample vs. random

5. Distribution drift: chi-squared p-value between weekly batches

Part of ML Engineer Toolkit

Chapter 2
πŸ”’ Available in full product

Quality Assurance Guide for Data Labeling

Chapter 3
πŸ”’ Available in full product

Annotation Workflow Guide

Chapter 4
πŸ”’ Available in full product

Quality Assurance Guide for Data Labeling

You’ve reached the end of the free preview

Get the full Data Labeling Pipeline and unlock everything.

All Chapters

Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.

Full Tool Suite

Access all interactive tools with complete data, all workload profiles, and the full scenario library.

Source Files

Downloadable source code, configuration files, and working examples from every chapter.

Lifetime Updates

Free updates for life. Every new chapter, tool, and improvement included.

Buy Now — $29 →
πŸ“¦ Free sample included — download another copy or visit the store for the full product.
Data Labeling Pipeline v1.0.0 β€” Free Preview