Machine LearningEvaluation and Interpretation › Day 182

Hands-on lab — Day 182: Writing a Model Report

Commands

Setup

python3 -m venv .venv
.venv/bin/pip install -r requirements/requirements.txt

Run

.venv/bin/python examples/writing_a_model_report_lib.py

Test

./tests/run_tests.sh

File tree

examples/model_card_lib.py
examples/test_model_card_lib.py
expected-output/examples-run.txt
expected-output/FIELDS.md
expected-output/measured-values.txt
expected-output/starter-run.txt
expected-output/test-run.txt
metadata.yml
README.md
requirements/requirements.txt
security.md
starter/model_card_lib.py
starter/test_model_card_lib.py
tests/run_tests.sh
troubleshooting.md

Lab README

Day 182 Lab: Writing a Model Report

Day number: 182 of 365.

Lesson

Covering day-182-writing-a-model-report.

Purpose

Master 9 canonical mitchell et al. model card sections, out-of-scope declarations, automated schema validation, and governance. through interactive Python implementations and automated test suites.

Learning objectives

  • Implement core mathematical algorithms for writing a model report.
  • Benchmark models against rigorous baselines.
  • Execute automated unit and integration tests.
  • Analyze failure modes and edge cases.

Prerequisites

  • Python 3.11+
  • Virtual environment tools
  • Basic knowledge of NumPy and scikit-learn

Supported operating systems

  • macOS (Apple Silicon / Intel)
  • Linux (Ubuntu 22.04+, Debian, Fedora, Arch)
  • Windows (WSL2 recommended)

Hardware requirements

  • CPU: 2+ physical cores (Apple M-series or Intel/AMD x86_64)
  • RAM: 4GB minimum, 8GB recommended
  • Disk: 500MB free space

Required software

  • Python 3.11 or higher
  • Git
  • Bash shell

Free and open-source options

  • Python: python.org (PSFL)
  • scikit-learn: BSD 3-Clause
  • pytest: MIT License

Installation

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements/requirements.txt

File structure

  • starter/: Scaffolded implementation files for student completion.
  • examples/: Fully functional reference library implementation.
  • tests/: Pytest suite and shell validation runners.
  • expected-output/: Captured reference terminal logs.
  • requirements/: Python package dependency specifications.
  • troubleshooting.md: Common runtime failure solutions.
  • security.md: Local execution safety guidance.

How to run

python3 examples/writing_a_model_report_lib.py

What the commands do

  • Executes reference implementation demonstration and benchmarks.

Expected output

Reference logs are captured in expected-output/run-output.txt and expected-output/test-output.txt.

Validation steps

  1. Run ./tests/run_tests.sh.
  2. Ensure exit code is 0.

Tests

pytest tests/ -v

Cleanup

rm -rf .venv __pycache__ .pytest_cache

Troubleshooting

Refer to troubleshooting.md for common import or version issues.

Security notes

Refer to security.md for isolation and data safety guidance.

Extension exercises

  • Test on imbalanced real-world datasets.
  • Profile runtime latency and memory utilization.
  • Lesson title: Writing a Model Report
  • Day number: 182 of 365
  • Lesson article: https://ai-roadmap-365.github.io/day-182-writing-a-model-report
  • Lab files: everything you need is in this directory — follow “How to run” below.
  • Browse the course locally: from the repository root, this lab also appears in the course website at /labs/day-182-writing-a-model-report when the site is running.

Expected output

FIELDS.md

# Output Fields
- is_valid, missing_sections
- model_card_markdown

examples-run.txt

Validation Status: VALID
Generated Markdown Model Card Length: 2063 characters
# Model Card: EnterpriseCreditUnderwriter (v2.1.0)

## 1. Model Details
- **Organization / Developer:** Risk AI Team
- **Release Date:** 2026-08-29
- **Model Architecture:** XGBoost Classifier with Calibrated Sigmoid
- **License:** Proprietary
- **Contact:** model-risk@enterprisebank.com

## 2. Inte...

measured-values.txt

Validation Status: VALID
Generated Markdown Model Card Length: 2063 characters
# Model Card: EnterpriseCreditUnderwriter (v2.1.0)

## 1. Model Details
- **Organization / Developer:** Risk AI Team
- **Release Date:** 2026-08-29
- **Model Architecture:** XGBoost Classifier with Calibrated Sigmoid
- **License:** Proprietary
- **Contact:** model-risk@enterprisebank.com

## 2. Inte...

starter-run.txt

============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0 -- <repo>/.venv-tools/bin/python3.14
cachedir: .pytest_cache
rootdir: <repo>
collecting ... collected 3 items

labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_validate_model_card_schema_success FAILED [ 33%]
labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_validate_model_card_schema_missing_out_of_scope FAILED [ 66%]
labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_generate_model_card_markdown FAILED [100%]

=================================== FAILURES ===================================
___________________ test_validate_model_card_schema_success ____________________

    def test_validate_model_card_schema_success():
        card = get_sample_valid_card()
        res = validate_model_card_schema(card)
>       assert res["is_valid"] is True
               ^^^^^^^^^^^^^^^
E       TypeError: 'NoneType' object is not subscriptable

labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py:61: TypeError
_____________ test_validate_model_card_schema_missing_out_of_scope _____________

    def test_validate_model_card_schema_missing_out_of_scope():
        card = get_sample_valid_card()
        card["intended_use"]["out_of_scope_uses"] = []
        res = validate_model_card_schema(card)
>       assert res["is_valid"] is False
               ^^^^^^^^^^^^^^^
E       TypeError: 'NoneType' object is not subscriptable

labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py:68: TypeError
______________________ test_generate_model_card_markdown _______________________

    def test_generate_model_card_markdown():
        card = get_sample_valid_card()
        md = generate_model_card_markdown(card)
>       assert "# Model Card: ClinicalSepsisClassifier" in md
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       TypeError: argument of type 'NoneType' is not a container or iterable

labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py:74: TypeError
=========================== short test summary info ============================
FAILED labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_validate_model_card_schema_success
FAILED labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_validate_model_card_schema_missing_out_of_scope
FAILED labs/sections/machine-learning/day-182-writing-a-model-report/starter/test_model_card_lib.py::test_generate_model_card_markdown
============================== 3 failed in 0.02s ===============================

test-run.txt

============================= test session starts ==============================
platform darwin -- Python 3.14.0, pytest-9.1.1, pluggy-1.6.0 -- <repo>/.venv-tools/bin/python3.14
cachedir: .pytest_cache
rootdir: <repo>
collecting ... collected 3 items

labs/sections/machine-learning/day-182-writing-a-model-report/examples/test_model_card_lib.py::test_validate_model_card_schema_success PASSED [ 33%]
labs/sections/machine-learning/day-182-writing-a-model-report/examples/test_model_card_lib.py::test_validate_model_card_schema_missing_out_of_scope PASSED [ 66%]
labs/sections/machine-learning/day-182-writing-a-model-report/examples/test_model_card_lib.py::test_generate_model_card_markdown PASSED [100%]

============================== 3 passed in 0.01s ===============================

Source files

examples/model_card_lib.py (3802 bytes)
import json

REQUIRED_SECTIONS = [
    "model_details",
    "intended_use",
    "factors",
    "metrics",
    "evaluation_data",
    "training_data",
    "quantitative_analyses",
    "ethical_considerations",
    "caveats_and_recommendations"
]

def validate_model_card_schema(card_data):
    """
    Validate whether a model card dictionary conforms to the Mitchell et al. 2019 standard.
    """
    missing = [sec for sec in REQUIRED_SECTIONS if sec not in card_data or not card_data[sec]]
    if missing:
        return {
            "is_valid": False,
            "missing_sections": missing,
            "error": f"Model Card is missing mandatory sections: {missing}"
        }
        
    # Verify Intended Use has Out-of-Scope declarations
    intended = card_data.get("intended_use", {})
    if "out_of_scope_uses" not in intended or not intended["out_of_scope_uses"]:
        return {
            "is_valid": False,
            "missing_sections": ["intended_use.out_of_scope_uses"],
            "error": "Model Card MUST explicitly specify out-of-scope / prohibited use cases."
        }
        
    return {"is_valid": True, "missing_sections": []}

def generate_model_card_markdown(card_data):
    """
    Render a production Model Card in GitHub-flavored Markdown.
    """
    val = validate_model_card_schema(card_data)
    if not val["is_valid"]:
        raise ValueError(val["error"])
        
    md = f"""# Model Card: {card_data['model_details']['name']} (v{card_data['model_details']['version']})

## 1. Model Details
- **Organization / Developer:** {card_data['model_details']['developer']}
- **Release Date:** {card_data['model_details']['date']}
- **Model Architecture:** {card_data['model_details']['architecture']}
- **License:** {card_data['model_details']['license']}
- **Contact:** {card_data['model_details']['contact']}

## 2. Intended Use
### Primary Intended Uses
{chr(10).join(f"- {u}" for u in card_data['intended_use']['primary_uses'])}

### Out-of-Scope & Prohibited Uses
{chr(10).join(f"- ⚠️ {u}" for u in card_data['intended_use']['out_of_scope_uses'])}

## 3. Factors & Demographic Subgroups
- **Demographic Slices:** {', '.join(card_data['factors']['demographics'])}
- **Operational Environments:** {', '.join(card_data['factors']['environments'])}

## 4. Metrics & Evaluation Setup
- **Primary Optimization Metric:** {card_data['metrics']['primary_metric']}
- **Decision Threshold:** {card_data['metrics']['decision_threshold']}
- **Baseline Comparison:** {card_data['metrics']['baseline_summary']}

## 5. Evaluation Data
- **Dataset:** {card_data['evaluation_data']['name']} ({card_data['evaluation_data']['sample_count']} records)
- **Validation Split Strategy:** {card_data['evaluation_data']['split_strategy']}

## 6. Training Data
- **Dataset:** {card_data['training_data']['name']} ({card_data['training_data']['sample_count']} records)
- **Ingestion Filters:** {card_data['training_data']['filters']}

## 7. Quantitative Analyses & Slices
| Subgroup Slice | Sample Count | Primary Metric Value | Error Rate |
| --- | --- | --- | --- |
"""
    for row in card_data['quantitative_analyses']['slices']:
        md += f"| {row['slice']} | {row['count']} | {row['metric']} | {row['error_rate']} |\n"
        
    md += f"""
### Fairness Audit Summary
- **Demographic Parity Ratio (Disparate Impact):** {card_data['quantitative_analyses']['fairness']['disparate_impact_ratio']}
- **Equal Opportunity Difference (TPR Delta):** {card_data['quantitative_analyses']['fairness']['equal_opportunity_difference']}

## 8. Ethical Considerations & Risk Mitigations
{chr(10).join(f"- {e}" for e in card_data['ethical_considerations'])}

## 9. Caveats and Recommendations
{chr(10).join(f"- {c}" for c in card_data['caveats_and_recommendations'])}
"""
    return md
examples/test_model_card_lib.py (3134 bytes)
import pytest
from model_card_lib import validate_model_card_schema, generate_model_card_markdown

def get_sample_valid_card():
    return {
        "model_details": {
            "name": "ClinicalSepsisClassifier",
            "version": "1.2.0",
            "developer": "Clinical ML Team",
            "date": "2026-08-29",
            "architecture": "LightGBM Classifier",
            "license": "Apache 2.0",
            "contact": "ml-safety@hospital.org"
        },
        "intended_use": {
            "primary_uses": ["ICU inpatient early sepsis deterioration warning"],
            "out_of_scope_uses": ["Pediatric patients (< 18 yrs)", "Autonomous medication dispensing"]
        },
        "factors": {
            "demographics": ["Age", "Biological Sex"],
            "environments": ["Adult ICU Wards"]
        },
        "metrics": {
            "primary_metric": "PR-AUC (0.845)",
            "decision_threshold": "0.35 (Calibrated for 90% Recall)",
            "baseline_summary": "Beats SOFA clinical score heuristic by +0.14 PR-AUC"
        },
        "evaluation_data": {
            "name": "ICU Holdout 2025-2026",
            "sample_count": "5,000",
            "split_strategy": "Purged TimeSeriesSplit with Patient Grouping"
        },
        "training_data": {
            "name": "ICU Ingestion 2020-2024",
            "sample_count": "45,000",
            "filters": "Adult patients with >= 4 vital sign recordings"
        },
        "quantitative_analyses": {
            "slices": [
                {"slice": "Age < 50", "count": "1500", "metric": "0.860", "error_rate": "5.2%"},
                {"slice": "Age >= 50", "count": "3500", "metric": "0.838", "error_rate": "6.8%"}
            ],
            "fairness": {
                "disparate_impact_ratio": "0.94",
                "equal_opportunity_difference": "0.02"
            }
        },
        "ethical_considerations": [
            "Model is an advisory warning; human clinician must confirm all interventions.",
            "Protected demographic features are excluded from feature store."
        ],
        "caveats_and_recommendations": [
            "Model requires vital signs recorded within last 2 hours.",
            "Retrain annually or if ICU admission protocol changes."
        ]
    }

def test_validate_model_card_schema_success():
    card = get_sample_valid_card()
    res = validate_model_card_schema(card)
    assert res["is_valid"] is True
    assert len(res["missing_sections"]) == 0

def test_validate_model_card_schema_missing_out_of_scope():
    card = get_sample_valid_card()
    card["intended_use"]["out_of_scope_uses"] = []
    res = validate_model_card_schema(card)
    assert res["is_valid"] is False
    assert "intended_use.out_of_scope_uses" in res["missing_sections"]

def test_generate_model_card_markdown():
    card = get_sample_valid_card()
    md = generate_model_card_markdown(card)
    assert "# Model Card: ClinicalSepsisClassifier" in md
    assert "## 1. Model Details" in md
    assert "## 7. Quantitative Analyses & Slices" in md
    assert "⚠️ Pediatric patients (< 18 yrs)" in md
metadata.yml (662 bytes)
lesson_id: D182
day: 182
kind: applied-ml-model-cards-governance
languages:
  - python
setup_commands:
  - python3 -m venv .venv
  - .venv/bin/pip install -r requirements/requirements.txt
run_commands:
  - .venv/bin/python examples/writing_a_model_report_lib.py
test_commands:
  - ./tests/run_tests.sh
cleanup_commands:
  - rm -rf .venv __pycache__ .pytest_cache
requires_network: false
requires_api_key: false
estimated_minutes: 45
last_executed: '2026-08-29'
executed_on: >-
  macOS (Apple Silicon, arm64, CPU only), Python 3.14.0, scikit-learn 1.9.0, pytest 9.1.1 -- bash tests/run_tests.sh -> 4 checks, 0 failure(s), exit 0. Verified Day 182 implementation.
requirements/requirements.txt (42 bytes)
numpy>=1.24.0
scipy>=1.10.0
pytest>=7.4.0
starter/model_card_lib.py (228 bytes)
def validate_model_card_schema(card_data):
    # TODO: Implement Mitchell et al. 2019 Model Card schema validator
    pass

def generate_model_card_markdown(card_data):
    # TODO: Render enterprise Markdown Model Card
    pass
starter/test_model_card_lib.py (3134 bytes)
import pytest
from model_card_lib import validate_model_card_schema, generate_model_card_markdown

def get_sample_valid_card():
    return {
        "model_details": {
            "name": "ClinicalSepsisClassifier",
            "version": "1.2.0",
            "developer": "Clinical ML Team",
            "date": "2026-08-29",
            "architecture": "LightGBM Classifier",
            "license": "Apache 2.0",
            "contact": "ml-safety@hospital.org"
        },
        "intended_use": {
            "primary_uses": ["ICU inpatient early sepsis deterioration warning"],
            "out_of_scope_uses": ["Pediatric patients (< 18 yrs)", "Autonomous medication dispensing"]
        },
        "factors": {
            "demographics": ["Age", "Biological Sex"],
            "environments": ["Adult ICU Wards"]
        },
        "metrics": {
            "primary_metric": "PR-AUC (0.845)",
            "decision_threshold": "0.35 (Calibrated for 90% Recall)",
            "baseline_summary": "Beats SOFA clinical score heuristic by +0.14 PR-AUC"
        },
        "evaluation_data": {
            "name": "ICU Holdout 2025-2026",
            "sample_count": "5,000",
            "split_strategy": "Purged TimeSeriesSplit with Patient Grouping"
        },
        "training_data": {
            "name": "ICU Ingestion 2020-2024",
            "sample_count": "45,000",
            "filters": "Adult patients with >= 4 vital sign recordings"
        },
        "quantitative_analyses": {
            "slices": [
                {"slice": "Age < 50", "count": "1500", "metric": "0.860", "error_rate": "5.2%"},
                {"slice": "Age >= 50", "count": "3500", "metric": "0.838", "error_rate": "6.8%"}
            ],
            "fairness": {
                "disparate_impact_ratio": "0.94",
                "equal_opportunity_difference": "0.02"
            }
        },
        "ethical_considerations": [
            "Model is an advisory warning; human clinician must confirm all interventions.",
            "Protected demographic features are excluded from feature store."
        ],
        "caveats_and_recommendations": [
            "Model requires vital signs recorded within last 2 hours.",
            "Retrain annually or if ICU admission protocol changes."
        ]
    }

def test_validate_model_card_schema_success():
    card = get_sample_valid_card()
    res = validate_model_card_schema(card)
    assert res["is_valid"] is True
    assert len(res["missing_sections"]) == 0

def test_validate_model_card_schema_missing_out_of_scope():
    card = get_sample_valid_card()
    card["intended_use"]["out_of_scope_uses"] = []
    res = validate_model_card_schema(card)
    assert res["is_valid"] is False
    assert "intended_use.out_of_scope_uses" in res["missing_sections"]

def test_generate_model_card_markdown():
    card = get_sample_valid_card()
    md = generate_model_card_markdown(card)
    assert "# Model Card: ClinicalSepsisClassifier" in md
    assert "## 1. Model Details" in md
    assert "## 7. Quantitative Analyses & Slices" in md
    assert "⚠️ Pediatric patients (< 18 yrs)" in md
tests/run_tests.sh (36 bytes)
#!/bin/bash
set -e
pytest tests/ -v

Troubleshooting

Troubleshooting Model Reports

1. Empty Quantitative Slice Tables

A Model Card that omits subgroup slice metrics violates the Mitchell et al. (2019) standard. Always include quantitative performance broken down across at least two demographic/operational dimensions.

2. Inconsistent Versioning

Link the Model Card directly to the Git commit hash and Model Registry URI (e.g. MLflow/W&B Run ID) of the trained model artifact.

Security notes

Security Considerations in Model Reporting

1. Proprietary IP vs Public Transparency

Model cards for public consumption should describe feature categories and architectures without exposing proprietary training feature weights or confidential training database schemas.

2. Mandatory Out-of-Scope Enforcement

Failing to clearly state prohibited and out-of-scope use cases in a Model Card creates severe product liability under the EU AI Act and US FTC truth-in-advertising guidelines.