2026-06-03·5 min read·sota.io Team

EU AI Act Art.10 Dataset Diversity & Bias Testing: How to Audit Training Data for Compliance

Post #2 in the sota.io EU AI Act Data Governance Sprint Series

EU AI Act Art.10 dataset diversity and bias testing audit framework

The EU AI Act's Article 10 does not just require you to document your training data — it requires you to prove that the data is sufficiently representative, appropriately diverse, and free of statistical errors that could introduce bias into high-risk AI outputs.

For SaaS providers building or deploying high-risk AI systems, this creates a concrete engineering obligation: you need a systematic bias-testing process attached to your training data pipeline, and you need documentary evidence that it ran.

This post covers what Art.10(3) and Art.10(5) actually require, how to operationalize dataset diversity audits, and which automated gates should live in your CI/CD pipeline before the August 2026 deadline.


What Art.10(3) Actually Requires

Article 10(3) of Regulation (EU) 2024/1689 states:

"Training, validation and testing data sets shall be relevant, sufficiently representative, and to the best extent possible, free of errors and complete in view of the intended purpose."

Three obligations flow from this single sentence:

1. Relevance: Data must be appropriate for the specific task and deployment context. A credit-scoring model trained on data from one EU member state may not be "relevant" if deployed across multiple jurisdictions with different socioeconomic profiles.

2. Sufficient representativeness: The dataset must reflect the population of individuals the system will affect. This is not a statistical nicety — it is a compliance requirement. A facial recognition system trained predominantly on one demographic group fails this test.

3. Freedom from errors: Labelling errors, mislabelled protected attributes, and systematic data collection biases all constitute "errors" under Art.10(3). The phrase "to the best extent possible" acknowledges that zero errors is aspirational, but it requires documented efforts to find and correct them.


Art.10(4): The Contextual Diversity Requirement

Article 10(4) adds a geographic and behavioral dimension:

"Training, validation and testing data sets shall take into account, to the extent required by their intended purpose, the characteristics or elements that are particular to the specific geographical, contextual, behavioural or functional setting within which the high-risk AI system is intended to be used."

In practice this means:

This is not a one-time check — it must be re-evaluated whenever the deployment context changes.


Art.10(5): The Special Categories Exception for Bias Detection

Article 10(5) provides a narrow but important carve-out:

"To the extent strictly necessary for the purposes of ensuring bias monitoring, detection and correction in relation to the high-risk AI systems, the providers of such systems may process special categories of personal data referred to in Article 9(1) of Regulation (EU) 2016/679..."

This means you may process race, ethnicity, health data, or other special categories solely for bias detection — but only under strict conditions:

Document this processing in your DPIA and link it to your Art.10 technical documentation.


Building a Dataset Diversity Audit Framework

A compliant bias-testing process has four layers:

Layer 1 — Dataset Profiling

Before training, generate a statistical profile of your dataset:

import pandas as pd
from dataclasses import dataclass
from typing import Dict, List

@dataclass
class DatasetProfile:
    feature: str
    value_counts: Dict
    null_rate: float
    coverage_gap: str  # e.g. "underrepresented: age_group=65+"

def profile_protected_attributes(
    df: pd.DataFrame,
    protected_cols: List[str],
    threshold: float = 0.05
) -> List[DatasetProfile]:
    """
    Profiles distribution of protected attributes.
    Flags groups with < threshold representation.
    """
    profiles = []
    for col in protected_cols:
        if col not in df.columns:
            continue
        vc = df[col].value_counts(normalize=True).to_dict()
        gaps = [f"{k}={v:.1%}" for k, v in vc.items() if v < threshold]
        profiles.append(DatasetProfile(
            feature=col,
            value_counts=vc,
            null_rate=df[col].isna().mean(),
            coverage_gap=", ".join(gaps) if gaps else "none"
        ))
    return profiles

Store the output as a JSON artifact alongside your training run — this becomes part of your Art.10 technical documentation.

Layer 2 — Label Quality Audit

Label errors are the most common source of systematic bias. Run cross-validation to detect label inconsistency:

from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
import numpy as np

def detect_label_errors(X, y, classifier, cv=5):
    """
    Uses cross-validation predictions to surface likely mislabelled examples.
    High-confidence wrong predictions are candidate label errors.
    """
    y_pred_proba = cross_val_predict(
        classifier, X, y, cv=cv, method='predict_proba'
    )
    predicted_class = np.argmax(y_pred_proba, axis=1)
    confidence = np.max(y_pred_proba, axis=1)
    
    # High-confidence mismatches are probable label errors
    likely_errors = (predicted_class != y) & (confidence > 0.85)
    
    return {
        "total_samples": len(y),
        "likely_label_errors": int(likely_errors.sum()),
        "error_rate": float(likely_errors.mean()),
        "indices": np.where(likely_errors)[0].tolist()[:100]  # first 100
    }

A label error rate above 3-5% in a high-risk application warrants manual review before training proceeds.

Layer 3 — Statistical Bias Metrics

After training, measure bias across protected groups using established fairness metrics:

from typing import Tuple
import numpy as np

def compute_bias_metrics(
    y_true: np.ndarray,
    y_pred: np.ndarray,
    sensitive_attr: np.ndarray
) -> dict:
    """
    Computes demographic parity difference and equalized odds difference.
    Both are standard fairness metrics accepted in EU bias audits.
    """
    groups = np.unique(sensitive_attr)
    group_metrics = {}
    
    for g in groups:
        mask = sensitive_attr == g
        tp = ((y_pred[mask] == 1) & (y_true[mask] == 1)).sum()
        fp = ((y_pred[mask] == 1) & (y_true[mask] == 0)).sum()
        fn = ((y_pred[mask] == 0) & (y_true[mask] == 1)).sum()
        tn = ((y_pred[mask] == 0) & (y_true[mask] == 0)).sum()
        
        group_metrics[str(g)] = {
            "positive_rate": (tp + fp) / mask.sum() if mask.sum() > 0 else 0,
            "tpr": tp / (tp + fn) if (tp + fn) > 0 else 0,  # True positive rate
            "fpr": fp / (fp + tn) if (fp + tn) > 0 else 0,  # False positive rate
        }
    
    rates = [m["positive_rate"] for m in group_metrics.values()]
    tprs = [m["tpr"] for m in group_metrics.values()]
    
    return {
        "demographic_parity_difference": max(rates) - min(rates),
        "equalized_odds_tpr_gap": max(tprs) - min(tprs),
        "group_breakdown": group_metrics
    }

Thresholds for high-risk AI under EU AI Act (practical guidance):

These thresholds are not statutory — the Act does not specify numeric limits — but they align with what NCA auditors expect to see documented.

Layer 4 — Temporal Drift Detection

Training data collected over time may exhibit drift — early data may reflect historical biases that are no longer acceptable:

def check_temporal_bias_drift(
    df: pd.DataFrame,
    date_col: str,
    label_col: str,
    protected_col: str,
    window_months: int = 6
) -> dict:
    """
    Checks whether protected attribute distributions shifted over time.
    A significant shift suggests historical bias in older data.
    """
    df[date_col] = pd.to_datetime(df[date_col])
    df['period'] = df[date_col].dt.to_period('M')
    
    drift_report = {}
    for period, group in df.groupby('period'):
        dist = group[protected_col].value_counts(normalize=True).to_dict()
        drift_report[str(period)] = dist
    
    return drift_report

CI/CD Gates: Automated Bias Checks in Your Pipeline

A compliant Art.10 implementation requires that bias checks run automatically — not just before initial training, but on every significant dataset update.

GitHub Actions Gate

name: EU AI Act Art.10 Bias Gate

on:
  push:
    paths:
      - 'data/training/**'
      - 'data/validation/**'
  pull_request:
    paths:
      - 'data/training/**'

jobs:
  bias-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          lfs: true  # datasets often stored in Git LFS
      
      - name: Install bias audit tools
        run: pip install pandas scikit-learn fairlearn

      - name: Run dataset profile
        run: |
          python scripts/profile_dataset.py \
            --data data/training/train.csv \
            --protected-cols gender,age_group,region \
            --output reports/dataset-profile.json

      - name: Check demographic parity gate
        run: |
          python scripts/bias_gate.py \
            --profile reports/dataset-profile.json \
            --max-coverage-gap 0.05 \
            --max-demographic-parity 0.20
        # Fails pipeline if thresholds exceeded

      - name: Upload audit artifact
        uses: actions/upload-artifact@v4
        with:
          name: art10-bias-report
          path: reports/
          retention-days: 1825  # 5 years (Art.18 retention obligation)

What to Store for NCA Audits

Under Art.11 and Art.18, high-risk AI providers must retain technical documentation for 10 years after market placement. For your training data audit, that means storing:

  1. Dataset profile JSON — attribute distributions at training time
  2. Label error report — count and rate of detected mislabelled samples
  3. Bias metrics report — demographic parity and equalized odds per protected group
  4. CI/CD run logs — timestamps and pass/fail outcomes for each gate
  5. Human review records — any cases escalated from automated gates

Store these in an immutable log (append-only S3 bucket, Azure Immutable Blob Storage, or a WORM-enabled object store on EU infrastructure).


Practical Checklist: Art.10 Dataset Diversity Compliance

Before your August 2026 deadline, verify:

Data Profiling

Bias Metrics

Label Quality

CI/CD Integration

Special Categories (if applicable)


What Comes Next in This Series

This is post 2 of 5 in our EU AI Act Data Governance Sprint:

The August 2026 deadline applies to high-risk AI systems placed on the EU market. Providers who cannot demonstrate a documented, systematic bias-testing process face potential NCA audits and fines under Art.99 (up to €30M or 6% of global turnover).


sota.io is EU-native managed PaaS — 100% GDPR, Hetzner Germany, no CLOUD Act exposure. Deploy your AI compliance infrastructure on infrastructure that is itself compliant.

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.