Module 5: GDPR for AI Systems

Data Minimization applied to AI

Description

GDPR Art. 5(1)(c): personal data must be "adequate, relevant and limited to what is necessary." That's the principle of data minimization. For AI, it clashes with the ML intuition that "more data = a better model."

The resolution: minimization isn't "less data." It's "only the data necessary for the specific purpose." For AI, this applies in two distinct contexts: training data and inference data.

By the end you'll be able to:

  • Distinguish data minimization in training vs. inference
  • Apply techniques: synthetic data, sampling, feature reduction, anonymization
  • Justify which data is necessary, with documentation

Training data vs. inference data: two distinct regimes

Training data

  • Processing: large volume, a single moment (the training run)
  • Risk: personal data can "leak" via the model's memorization
  • Mitigation: anonymization, synthetic data, federated learning, differential privacy

Inference data

  • Processing: continuous, every query
  • Risk: active exposure of personal data
  • Mitigation: minimal collection, anonymization in logs, strict retention

Techniques for minimization in training

Technique 1: Anonymization

You remove identifiers before training:

def anonymize_for_training(record):
    return {
        # Removed: name, email, phone, address, SSN
        "age_bucket": bucket_age(record["age"]),       # 25 → "20-29"
        "zip_prefix": record["zip"][:3],                # 12345 → "123"
        "purchase_history": record["purchases"],         # OK (generic)
        "preferences": record["preferences"],
    }

Caveat: anonymization is hard. K-anonymity, l-diversity, and differential privacy are the more serious techniques.

Technique 2: Synthetic data

You generate data that's statistically similar but contains no real people:

from sdv.lightweight_synthesizers import GaussianCopulaSynthesizer

synthesizer = GaussianCopulaSynthesizer(metadata)
synthesizer.fit(real_data)
synthetic_data = synthesizer.sample(num_rows=100000)

# Train on synthetic_data instead of the real thing
model.train(synthetic_data)

Pros: zero GDPR risk for training. Cons: model quality can suffer if the synthetic distribution doesn't capture the real patterns.

Technique 3: Sampling

If you have 10M records, do you really need all of them? Often, 100K is enough.

# Stratified sampling to preserve the distributions
from sklearn.model_selection import train_test_split

_, training_subset = train_test_split(
    full_data,
    test_size=0.01,  # 1% = 100K if the total is 10M
    stratify=full_data["target"]
)

Less data = less exposure = less GDPR risk.

Technique 4: Federated Learning

The model trains on the device (mobile, edge) without centralizing the data:

Client A: trains locally on data A → sends gradients
Client B: trains locally on data B → sends gradients
Server: aggregates the gradients, NEVER sees the data

Pros: personal data never leaves the device. Cons: significant technical complexity.

Technique 5: Differential Privacy

You add controlled noise to the data/gradients so no specific individual is identifiable.

# Sketch — DP-SGD adds noise to gradients
import opacus
privacy_engine = opacus.PrivacyEngine()
model, optimizer, data_loader = privacy_engine.make_private(
    module=model,
    optimizer=optimizer,
    data_loader=data_loader,
    noise_multiplier=1.1,
    max_grad_norm=1.0,
)

Pros: mathematical guarantees. Cons: model accuracy can drop significantly.


Techniques for minimization in inference

Technique 1: Collect only what you need

Your LLM doesn't need the user_id, email, or phone to answer a general question. Don't pass them in the prompt.

# Bad
prompt = f"User {user.full_name} ({user.email}) is asking: {question}"

# Good
prompt = f"User question: {question}"

Technique 2: Strip personal info from logs

def sanitize_for_logging(input_text):
    # Remove emails, phones, SSNs from logs
    sanitized = re.sub(r'[\w.+-]+@[\w-]+\.[\w.-]+', '[EMAIL_REDACTED]', input_text)
    sanitized = re.sub(r'\b\d{3}-\d{2}-\d{4}\b', '[SSN_REDACTED]', sanitized)
    sanitized = re.sub(r'\b\d{3}-\d{3}-\d{4}\b', '[PHONE_REDACTED]', sanitized)
    return sanitized

log.info(sanitize_for_logging(user_input))

Technique 3: Aggressive retention

Detailed logs with personal info: 30-90 days. After that, aggregate-only metrics.

@scheduled_task(every="daily")
def cleanup_old_logs():
    db.execute(
        "DELETE FROM detailed_logs WHERE created_at < NOW() - INTERVAL '90 days'"
    )

Technique 4: Tokenization / pseudonymization

Replace personal identifiers with reversible tokens:

# user_id → token via lookup
def tokenize(user_id):
    return f"USR-{hashlib.sha256(user_id.encode() + SECRET).hexdigest()[:16]}"

# In logs, prompts, analytics
log_entry["user"] = tokenize(real_user_id)

If you need to "de-tokenize" for support, you keep the mapping in a separate lookup table with stricter access controls.


The fundamental conflict: more data vs. the minimum

When more data IS necessary

  • A rare specific domain (specialized medicine) → you need broad coverage
  • Performance is safety-critical (autonomous vehicles) → more data = better
  • Long-tail outcomes matter → more samples required

When more data is NOT necessary

  • Your model has already plateaued on performance
  • You're adding similar data (no new information)
  • The additional data doesn't improve your eval metrics

The practical test: if removing 50% of your training data significantly degrades performance, it's necessary. If not, it isn't.


Documenting necessity

To defend minimization in an audit, document it:

## Data Necessity Justification — Customer Churn Model

### Data fields used in training:
- account_tenure: necessary because churn correlates with tenure
- usage_frequency: necessary because low frequency predicts churn
- support_tickets_count: necessary because a high count predicts churn
- last_login_date: necessary for the recency metric

### Data fields NOT used (available but excluded):
- name: NOT necessary for the prediction
- email: NOT necessary
- phone: NOT necessary
- exact_birthdate: NOT necessary; we use age_bucket instead

### Anonymization applied:
- email replaced with hashed_user_id
- exact_dates with month_year
- specific zipcodes with city_region

### Retention:
- Training data refreshed quarterly
- Old training datasets purged after 1 year

If an auditor asks "why do you have this data?", you have a documented answer.


Common traps

Trap 1 — Superficial "anonymization." You remove the name but keep ZIP + age + gender → re-identifiable. Decent k-anonymity requires at least k=5.

Trap 2 — The "more data is always better" mentality. Your ML team wants everything. Resist — every piece of data requires justification.

Trap 3 — Treating inference data like training data. Detailed logs that grow with no retention. The same position as training in terms of GDPR exposure.

Trap 4 — Synthetic data with no validation. You generate synthetic data and assume the quality is fine. Result: a bad model. Validate it against a real eval set.

Trap 5 — Federated learning as a silver bullet. "The data never leaves the device" sounds great, but gradients can still leak information. It needs DP on top for real guarantees.


Exercise

Your product: a recommendation system for e-commerce. Currently you collect:

  • user_id, name, email, phone
  • birthday (exact date), gender, address (full)
  • purchase history (all items, dates, amounts)
  • browsing history (every page view, every click)
  • inferred demographics (income bracket, family status)

Apply data minimization:

  1. Which data do you drop from training?
  2. Which data do you keep but anonymize/aggregate?
  3. What's the retention strategy?
See the solution

Dropped from training:

  • name: not necessary to recommend
  • email: not necessary
  • phone: not necessary
  • address (full): not necessary; we use zip_prefix or city

Kept but modified:

  • birthdayage_bucket (10-year buckets)
  • gender → kept (it can be relevant for apparel)
  • purchase_history → kept (item IDs, month/year dates, bucketed amounts)
  • browsing_history → aggregated into "categories visited" instead of every page
  • inferred demographics → kept but with documented consent

Retention:

  • Training datasets: 1 year, then refresh
  • Detailed browsing logs: 30 days
  • Aggregated patterns: indefinite (not identifiable)
  • Personal info for an active user: until account closure + 6 months

Summary

You learned:

  • ✅ Data minimization: training vs. inference (distinct regimes)
  • ✅ 5 training techniques: anonymization, synthetic, sampling, federated, DP
  • ✅ 4 inference techniques: collect the minimum, sanitize logs, retention, tokenization
  • ✅ Documenting necessity for an audit
  • ✅ The traps: superficial anonymization, the more-is-better mentality

Checkpoint: if you can justify every piece of data you collect with a documented necessity, you're ready.


Next capsule

05 — Granular consent for AI processing. Consent is the area where most sites fail GDPR. For AI, the requirements are even more specific.


Resources

  1. GDPR Art. 5(1)(c) — the minimization principle.
  2. Differential Privacy library (Opacus) — PyTorch + DP.
  3. Synthetic Data Vault — the generation library.
  4. Federated learning frameworks — an overview.