File size: 5,295 Bytes
2e552e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
PneumoOps β€” Hugging Face Model Hub Upload Script
=================================================
Uploads both model artifacts to the HF Model Hub for versioning.
Run this once after training, and again whenever you retrain.

Usage:
    huggingface-cli login   # one-time login
    python scripts/upload_to_hf.py

Environment variables (override defaults):
    HF_MODEL_REPO   your-username/pneumoops-chestmnist
    HF_TOKEN        your write token (if not logged in via CLI)
"""

import os
import json
from pathlib import Path
from huggingface_hub import HfApi, upload_file, create_repo

# ─── Config ───────────────────────────────────────────────────────────────────
ROOT = Path(__file__).resolve().parents[1]
MODEL_DIR = ROOT / "models" / "chestmnist_mobilenetv3"

HF_TOKEN = os.getenv("HF_TOKEN")  # optional if already logged in via CLI
MODEL_REPO = os.getenv("HF_MODEL_REPO", "")  # e.g. "your-username/pneumoops-chestmnist"

if not MODEL_REPO:
    print("\n⚠️  Please set HF_MODEL_REPO environment variable, e.g.:")
    print('   export HF_MODEL_REPO="your-hf-username/pneumoops-chestmnist"')
    raise SystemExit(1)

# ─── Files to upload ──────────────────────────────────────────────────────────
ARTIFACTS = [
    ("mobilenetv3_chestmnist.pth",    "Model A β€” Baseline PyTorch checkpoint"),
    ("mobilenetv3_chestmnist.onnx",   "Model B β€” Optimized ONNX artifact"),
    ("training_metrics.json",          "Training + evaluation metrics (AUROC, AUPRC, F1)"),
    ("baseline_stats.json",            "Pixel distribution stats for drift monitoring"),
    ("onnx_export_report.json",        "ONNX export configuration"),
]

# ─── Model Card ───────────────────────────────────────────────────────────────
MODEL_CARD = """---
license: mit
tags:
  - medical
  - image-classification
  - chest-xray
  - mlops
  - onnx
  - pytorch
  - mobilenetv3
datasets:
  - medmnist/chestmnist
metrics:
  - roc_auc
---

# PneumoOps β€” ChestMNIST MobileNetV3-small

This repository contains **two model versions** for the PneumoOps MLOps pipeline:
- **Model A (Baseline):** `mobilenetv3_chestmnist.pth` β€” Standard PyTorch checkpoint
- **Model B (Optimized):** `mobilenetv3_chestmnist.onnx` β€” ONNX-exported for faster inference

Both models are identical in architecture (MobileNetV3-small) and weights.
The ONNX version is used for inference time optimization in A/B testing.

## Dataset
**ChestMNIST** β€” 14-class multi-label chest X-ray classification  
78,468 training images, 224Γ—224 pixels, grayscale (converted to 3-channel).

## Classes (14)
Atelectasis, Cardiomegaly, Effusion, Infiltration, Mass, Nodule, Pneumonia,
Pneumothorax, Consolidation, Edema, Emphysema, Fibrosis, Pleural Thickening, Hernia

## Performance (Test Set)
| Metric | Score |
|--------|-------|
| Macro AUROC | **0.808** |
| Macro AUPRC | 0.210 |
| Micro F1 | 0.343 |

## Usage in PneumoOps
These artifacts are loaded by the FastAPI backend and selected via a weighted A/B router:
- 60% of requests β†’ PyTorch model
- 40% of requests β†’ ONNX model

The backend also computes a drift score using `baseline_stats.json` to detect
out-of-distribution inputs in real time.
"""

# ─── Main ─────────────────────────────────────────────────────────────────────

def main():
    api = HfApi(token=HF_TOKEN)

    print(f"\nπŸ“¦ Creating/verifying model repository: {MODEL_REPO}")
    create_repo(
        repo_id=MODEL_REPO,
        repo_type="model",
        exist_ok=True,
        token=HF_TOKEN,
    )

    # Write model card
    card_path = MODEL_DIR / "README.md"
    card_path.write_text(MODEL_CARD, encoding="utf-8")
    print("   Model card written.")

    # Upload model card first
    print(f"\n⬆️  Uploading artifacts to https://huggingface.co/{MODEL_REPO}")
    upload_file(
        path_or_fileobj=str(card_path),
        path_in_repo="README.md",
        repo_id=MODEL_REPO,
        repo_type="model",
        commit_message="Add model card",
        token=HF_TOKEN,
    )

    # Upload all artifacts
    for filename, description in ARTIFACTS:
        local_path = MODEL_DIR / filename
        if not local_path.exists():
            print(f"   ⚠️  Skipping {filename} β€” file not found")
            continue
        size_mb = local_path.stat().st_size / (1024 * 1024)
        print(f"   Uploading {filename} ({size_mb:.1f} MB) β€” {description} ...")
        upload_file(
            path_or_fileobj=str(local_path),
            path_in_repo=filename,
            repo_id=MODEL_REPO,
            repo_type="model",
            commit_message=f"Upload {filename}",
            token=HF_TOKEN,
        )

    print(f"\nβœ… All artifacts uploaded!")
    print(f"   View at: https://huggingface.co/{MODEL_REPO}")


if __name__ == "__main__":
    main()