Model governance ensures ML systems are developed, deployed, and monitored responsibly. It covers documentation, approval workflows, audit trails, and regulatory compliance. As ML becomes regulated (EU AI Act, FDA guidance, financial regulations), governance transforms from best practice to legal requirement.
Where It's Used
Governance is essential in regulated and high-stakes domains:
- Financial Services: Credit decisions, trading, fraud detection
- Healthcare: Diagnostic tools, treatment recommendations
- Insurance: Underwriting, claims processing
- Government: Benefits decisions, law enforcement
- HR: Hiring, performance evaluation
- Any Regulated Industry: Automotive, pharmaceuticals, energy
Regulations: EU AI Act, GDPR, ECOA, SR 11-7, FDA guidance, EEOC.
Governance Components
| Component | Purpose | Artefacts |
|---|---|---|
| Documentation | Record what model does and how | Model cards, datasheets |
| Approval workflows | Control deployment decisions | Sign-off processes |
| Audit trails | Track all changes and decisions | Logs, version history |
| Risk assessment | Evaluate potential harms | Risk matrices |
| Monitoring | Ongoing performance and fairness | Dashboards, alerts |
| Incident response | Handle failures and issues | Playbooks, escalation |
Typical Requirements
| Aspect | Common Requirements |
|---|---|
| Documentation | Model purpose, data, performance, limitations |
| Versioning | All model versions tracked and retrievable |
| Testing | Validation before deployment |
| Approval | Designated approvers for production |
| Monitoring | Ongoing performance and bias tracking |
| Retention | Models and data retained for N years |
What You Actually Do
- Define model risk tiers - High, medium, low based on impact
- Create documentation templates - Model cards, datasheets
- Build approval workflows - Who approves what tier
- Implement model registry - Central catalogue with metadata
- Set up version control - Models, data, code all versioned
- Establish testing requirements - Per-tier validation
- Deploy monitoring - Performance, fairness, drift
- Create audit trails - Immutable logs of all actions
- Plan incident response - Playbooks for failures
- Train teams - Governance processes and responsibilities
Model Cards
Standard documentation format for ML models:
# model_card.yaml
model_details:
name: "Credit Risk Scorer v2.3"
version: "2.3.0"
type: "Binary classification (XGBoost)"
owner: "Risk Analytics Team"
contact: "risk-ml@company.com"
created: "2026-01-15"
last_updated: "2026-01-30"
intended_use:
primary_uses:
- "Credit limit decisions for existing customers"
out_of_scope_uses:
- "New customer acquisition"
- "Fraud detection"
users:
- "Credit decision systems"
- "Risk analysts"
training_data:
description: "12 months of credit applications and outcomes"
size: "2.4M samples"
features: "145 features after engineering"
label: "90-day default indicator"
collection_period: "2025-01 to 2025-12"
known_limitations:
- "Under-represents rural customers"
- "Economic conditions may have shifted"
evaluation:
metrics:
- name: "AUC-ROC"
value: 0.82
confidence_interval: "[0.81, 0.83]"
- name: "Gini"
value: 0.64
datasets:
- name: "Holdout test set"
size: "240K samples"
fairness_evaluation:
- attribute: "Age group"
metric: "Equal opportunity difference"
value: 0.03
- attribute: "Gender"
metric: "Demographic parity difference"
value: 0.02
ethical_considerations:
risks:
- "May perpetuate historical lending biases"
- "Performance may degrade for demographic shifts"
mitigations:
- "Quarterly fairness audits"
- "Monthly performance monitoring by segment"
caveats_and_recommendations:
- "Not validated for customers under 21"
- "Requires manual review for edge cases"
- "Retrain if AUC drops below 0.78"
Model Registry
from dataclasses import dataclass
from datetime import datetime
from typing import List, Dict, Optional
import json
@dataclass
class ModelVersion:
version: str
created_at: datetime
created_by: str
model_path: str
metrics: Dict[str, float]
status: str # development, staging, production, retired
approved_by: Optional[str] = None
approved_at: Optional[datetime] = None
class ModelRegistry:
def __init__(self, storage_backend):
self.storage = storage_backend
self.models = {}
def register_model(self, name: str, version: ModelVersion):
"""Register a new model version."""
if name not in self.models:
self.models[name] = []
self.models[name].append(version)
self._log_event("register", name, version.version)
def promote_to_staging(self, name: str, version: str, approver: str):
"""Promote model to staging environment."""
model = self._get_version(name, version)
if model.status != "development":
raise ValueError(f"Can only promote from development, current: {model.status}")
model.status = "staging"
self._log_event("promote_staging", name, version, approver)
def promote_to_production(self, name: str, version: str, approver: str):
"""Promote model to production with approval."""
model = self._get_version(name, version)
if model.status != "staging":
raise ValueError("Must pass staging before production")
model.status = "production"
model.approved_by = approver
model.approved_at = datetime.now()
self._log_event("promote_production", name, version, approver)
def retire_model(self, name: str, version: str, reason: str):
"""Retire a model version."""
model = self._get_version(name, version)
model.status = "retired"
self._log_event("retire", name, version, reason=reason)
def get_production_model(self, name: str) -> Optional[ModelVersion]:
"""Get current production model."""
if name not in self.models:
return None
for model in reversed(self.models[name]):
if model.status == "production":
return model
return None
def _get_version(self, name: str, version: str) -> ModelVersion:
for model in self.models.get(name, []):
if model.version == version:
return model
raise ValueError(f"Model {name} version {version} not found")
def _log_event(self, event_type: str, model_name: str, version: str,
approver: str = None, reason: str = None):
"""Immutable audit log."""
event = {
"timestamp": datetime.now().isoformat(),
"event_type": event_type,
"model_name": model_name,
"version": version,
"approver": approver,
"reason": reason
}
self.storage.append_log(event)
Approval Workflows
from enum import Enum
from typing import List
class RiskTier(Enum):
LOW = "low" # No human approval needed
MEDIUM = "medium" # Team lead approval
HIGH = "high" # Committee approval
class ApprovalWorkflow:
def __init__(self):
self.approval_rules = {
RiskTier.LOW: [],
RiskTier.MEDIUM: ["team_lead"],
RiskTier.HIGH: ["team_lead", "risk_committee", "legal"]
}
def get_required_approvers(self, risk_tier: RiskTier) -> List[str]:
return self.approval_rules[risk_tier]
def check_approval_status(self, model_name: str, approvals: List[dict],
risk_tier: RiskTier) -> dict:
required = set(self.approval_rules[risk_tier])
approved = {a["role"] for a in approvals if a["decision"] == "approve"}
pending = required - approved
rejected = [a for a in approvals if a["decision"] == "reject"]
return {
"approved": len(pending) == 0 and len(rejected) == 0,
"pending_approvers": list(pending),
"rejected": len(rejected) > 0,
"rejection_reasons": [r["reason"] for r in rejected]
}
Audit Trail Requirements
from datetime import datetime
import hashlib
import json
class AuditLog:
def __init__(self, storage):
self.storage = storage
def log_model_event(self, event_type: str, model_id: str,
user: str, details: dict):
"""Create immutable audit entry."""
entry = {
"timestamp": datetime.utcnow().isoformat(),
"event_type": event_type,
"model_id": model_id,
"user": user,
"details": details,
"previous_hash": self._get_last_hash()
}
entry["hash"] = self._compute_hash(entry)
self.storage.append(entry)
def _compute_hash(self, entry: dict) -> str:
content = json.dumps(entry, sort_keys=True, default=str)
return hashlib.sha256(content.encode()).hexdigest()
def _get_last_hash(self) -> str:
last = self.storage.get_last()
return last["hash"] if last else "genesis"
def verify_chain(self) -> bool:
"""Verify audit log integrity."""
entries = self.storage.get_all()
for i, entry in enumerate(entries):
expected_prev = entries[i-1]["hash"] if i > 0 else "genesis"
if entry["previous_hash"] != expected_prev:
return False
stored_hash = entry["hash"]
entry_copy = {k: v for k, v in entry.items() if k != "hash"}
if self._compute_hash(entry_copy) != stored_hash:
return False
return True
Evaluation Metrics
| Metric | Description | Use Case |
|---|---|---|
| Documentation coverage | % models with complete docs | Compliance |
| Approval latency | Time from request to approval | Process efficiency |
| Audit completeness | % actions logged | Audit readiness |
| Monitoring coverage | % models with active monitoring | Risk management |
Common Mistakes & Checklist
- [ ] Documentation debt: Models deployed without cards
- [ ] Shadow models: Unregistered models in production
- [ ] Missing approvals: Deployed without proper sign-off
- [ ] Incomplete audit trails: Gaps in logging
- [ ] Stale documentation: Model cards not updated
Production Checklist
Before deployment: - [ ] Model card completed - [ ] Required approvals obtained - [ ] Testing requirements met - [ ] Monitoring configured
After deployment: - [ ] Audit trail verified - [ ] Monitoring active - [ ] Incident response plan in place - [ ] Periodic review scheduled
Tools & Stack
| Category | Tools |
|---|---|
| Model Registry | MLflow, Weights & Biases, Neptune |
| Documentation | Model Cards Toolkit, Datasheets |
| Workflow | Prefect, Airflow, custom approval systems |
| Audit | Immutable logs, blockchain (regulated) |
Mini Case Example
Scenario: Implementing governance for credit decisioning.
Approach: - Three-tier risk classification - Model cards for all production models - Automated testing gates - Committee approval for high-risk
Results: - 100% documentation coverage - Passed regulatory audit - 3-day average approval time (was 3 weeks)
Related Pages
- Index: ML Field Guide
- ML Fairness
- Model Monitoring
- MLOps Foundations
Further Reading
Try it live
Everything above runs in your browser — open AI Governance Policy Impact: Speed-Adjustable Model and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.
▶ Open AI Governance Policy Impact: Speed-Adjustable Model simulation