Open-source MLOps framework for continuous monitoring and lifecycle management of AI models in medical imaging. Supports drift detection, embedding-based monitoring, MLflow integration, automated validation, and reproducible deployment pipelines for reliable clinical AI systems.
Install wsl (with the Ubuntu distribution) on your Windows dev machine or use Ubuntu instead. Make sure that you do a restart after you installed wsl.
To generate an ssh-key, use ssh-keygen -t ecdsa in the .ssh folder of your Windows user account, e.g., C:\Users\{USER}\.ssh.
Then open the Windows explorer and type \wsl.localhost\Ubuntu\home{USER} in the explorer. If there is not already a folder .ssh, create one. Then copy the generated ssh keys from C:\Users{USER}.ssh to the .ssh folder in the wsl file system.
Please add your public ssh key to your GitHub account.
Install pycharm and run it as admin. As you have previously installed wsl and restarted the computer, there should be an Ubuntu terminal based on wsl available in your Pycharm. If so, please open this terminal. If not, please check your wsl installation. You can check with pwd if the selected directory in your Ubuntu terminal session is /home/{USER}. If not, navigate there and perform the following steps.
- Clone the repository https://github.com/Akros-STM/mlops via git in the wsl filesystem, e.g., under
/home/{USER}. - Checkout the branch
develop. - Delete the folder
.vitualenvsin/home/{USER}if it exists. - Navigate to the root directory of your project
/home/{USER}/mlops. - Create a virtual environment via
python3.12 -m venv .venv. If step 4 does not work, you may need to install the venv extension for your python, i.e., bysudo apt install python3.X-venv. After the installation, try again to create a virtual environment. - Using wsl, activate the virtual environment by
source .venv/bin/activate. - Update pip via
pip install --upgrade pip. Also, install wheel viapip install wheeland the setuptools viapip install setuptools. - Install poetry in the virtual environment via
pip install poetry. - Install the dependencies via
poetry install --no-root --with dev. - If you have no GPU available, then install
pytorchviapoetry run pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu. If you have a GPU available, then execute the commandpoetry run pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121. - Go to the Pycharm Settings, then the Project: mlops, and then the Project Interpreter.
- Click on Add interpreter and select On WSL.
- Add an existing python interpreter and select the python in the previously created venv
/home/{USER}/mlops/venv/bin/python3.X. - Run the Python scripts from the root directory in the terminal or use the Pycharm Run/Debug functionality. Remember to set the Working directory in the Run/ Debug configuration to the project root directory.
- Go in the WSL Ubuntu terminal and execute
sudo apt updateandsudo apt install -y ca-certificates curl gnupg. - Create a keyrings directory with certain permissions via
sudo install -m 0755 -d /etc/apt/keyrings. - Download the Docker GPG key for Linux and store it in a format supported by
aptwithcurl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg - Set the permissions of the docker gpg file via
sudo chmod a+r /etc/apt/keyrings/docker.gpg. - Add the official docker packet repository for my Linux version and architecture
echo \ "deb [arch=$(dpkg --print-architecture) \ signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null - Update the packet information of all configured repositories via
sudo apt update - Install the docker packages via
sudo apt install -y \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin
- Clone the repository https://github.com/Akros-STM/mlops via git in the wsl filesystem, e.g., under
/home/{USER}. - Checkout the branch
develop. - Delete the folder
.vitualenvsin/home/{USER}if it exists. - Navigate to the root directory of your project
/home/{USER}/mlops. - Build the docker image via
docker build --build-arg TORCH_VARIANT=cpu -t mlops-cpu .if you do not have a GPU available. If you have a GPU available, then execute the commanddocker build --build-arg TORCH_VARIANT=cu121 -t mlops-gpu. - Run the docker container via
docker run -it --rm -v $(pwd):/workspace -p 8888:8888 -p 8000:8000 -p 5678:5678 mlops-cpuif you do not have a GPU available. If you have a GPU available, run the commanddocker run -it --rm -v $(pwd):/workspace -p 8888:8888 -p 8000:8000 -p 5678:5678 mlops-gputo run the container. - To connect your Pycharm environment to the docker container, go to the Pycharm Settings, navigate to "Build, Execution, Deployment" and select the Docker section. Then select your Docker instance and connect to Docker.
- Go to the Pycharm Settings, Python, Interpreter, click "Add interpreter" and select "On Docker". Then, "Pull or use existing", enter the name of your docker container, i.e., either
mlops-cpuormlops-gpudepending on whether you have a GPU available.
The preprocessing script prepares the NIH ChestX-ray8 (CXR8) dataset from https://nihcc.app.box.com/v/ChestXray-NIHCC for deep learning workflows. It automatically extracts all image .tar archives, loads the NIH metadata CSV, generates multi-label disease targets, and creates patient-level train/validation/test splits to avoid data leakage between splits. All chest X-ray images are converted to grayscale, resized to a configurable resolution (default: 224x224), and stored in structured output folders. Additionally, the script generates a manifest.csv file containing image paths, patient IDs, split assignments, and multi-hot encoded disease labels, which can be directly used in PyTorch or TensorFlow training pipelines for multi-label chest X-ray classification.
The current reference workflow implements the abstract's core MLOps loop: preprocessing, MLflow-tracked training, image/data drift monitoring, embedding drift, prediction drift, controlled shift evaluation, API deployment, alert generation, and retraining triggers.
Set PYTHONPATH before running the scripts outside Docker:
export PYTHONPATH=/workspace/srcOn Windows PowerShell from the repository root:
$env:PYTHONPATH="src"python src/dataset/dataset_preprocessing.py \
--data_dir /workspace/data/CXR8-selected \
--out_dir /workspace/data/cxr8_preprocessed \
--size 224 \
--seed 42This writes resized images into split folders and creates:
/workspace/data/cxr8_preprocessed/manifest.csv
python src/training/cxr8_training_with_augmentation_weighted_loss_mlflow.py \
--data_root /workspace/data/cxr8_preprocessed \
--manifest_csv /workspace/data/cxr8_preprocessed/manifest.csv \
--out_dir /workspace/data/reports \
--epochs 10 \
--batch_size 32 \
--use_pos_weight \
--use_mlflow \
--mlflow_tracking_uri http://localhost:5000 \
--mlflow_experiment cxr8-monitoring \
--mlflow_log_modelOutputs include best_model.pt, training_and_drift_report.json, whylogs profiles, Evidently HTML reports, image-feature CSVs, per-class metrics, embedding drift, prediction drift, and model-weight drift.
python src/evaluation/shift_evaluation.py \
--data_root /workspace/data/cxr8_preprocessed \
--manifest_csv /workspace/data/cxr8_preprocessed/manifest.csv \
--model_path /workspace/data/reports/best_model.pt \
--out_dir /workspace/data/shift_eval \
--split test \
--shifts brightness contrast noise \
--severities 0.1 0.2 0.3This simulates deployment shifts and writes shift_evaluation_summary.csv plus shift_evaluation_report.json.
For demonstrations, create small manifests that are not used for hyperparameter tuning. The most useful first experiment is to train on AP images only and monitor PA images as a held-out acquisition-view drift cohort.
python src/dataset/create_drift_demo_manifests.py \
--manifest_csv /workspace/data/cxr8_preprocessed/manifest.csv \
--metadata_csv /workspace/data/CXR8-selected/Data_Entry_2017.csv \
--out_dir /workspace/data/drift_demo_manifests \
--max_train_rows 5000 \
--max_eval_rows 1000 \
--seed 42Important outputs:
ap_only_training_manifest.csv # AP-only train/val/test for training an AP-domain model
ap_reference_train_manifest.csv # AP train reference cohort for monitoring
ap_id_test_manifest.csv # AP test cohort, in-distribution baseline
pa_view_drift_test_manifest.csv # PA test cohort, held-out view-position drift
drift_demo_dataset_report.json # counts and candidate drift cohorts
Train the AP-only model:
python src/training/cxr8_training_with_augmentation_weighted_loss_mlflow.py \
--data_root /workspace/data/cxr8_preprocessed \
--manifest_csv /workspace/data/drift_demo_manifests/ap_only_training_manifest.csv \
--out_dir /workspace/data/reports_ap_only \
--epochs 10 \
--batch_size 32 \
--use_pos_weight \
--use_mlflow \
--mlflow_tracking_uri http://localhost:5000 \
--mlflow_experiment cxr8-ap-pa-driftThen compare AP test as the in-distribution baseline and PA test as the drift cohort.
Run a combined domain-shift and concept-drift indicator experiment:
python src/evaluation/domain_concept_drift_experiment.py \
--data_root /workspace/data/cxr8_preprocessed \
--model_path /workspace/data/reports_ap_only/best_model.pt \
--reference_manifest /workspace/data/drift_demo_manifests/ap_id_test_manifest.csv \
--current_manifest /workspace/data/drift_demo_manifests/pa_view_drift_test_manifest.csv \
--reference_split test \
--current_split test \
--out_dir /workspace/data/domain_concept_ap_vs_paThe report separates domain-shift evidence from concept-drift indicators. Domain shift is measured with image-feature drift, embedding drift, and prediction drift. Concept drift is reported as a set of indicators using labeled data: AUC/AP deltas, Brier score delta, calibration error delta, threshold error rates, and label-prevalence changes.
The experiment also writes an Evidently prediction/target drift report from the model outputs:
prediction_target_drift_report.html
prediction_target_drift_report.reference_predictions.csv
prediction_target_drift_report.current_predictions.csv
Optional Arize logging uses Arize SDK v8 and can be enabled when ARIZE_API_KEY and ARIZE_SPACE_ID are available:
python src/evaluation/domain_concept_drift_experiment.py \
--data_root /workspace/data/cxr8_preprocessed \
--model_path /workspace/data/reports_ap_only/best_model.pt \
--reference_manifest /workspace/data/drift_demo_manifests/ap_id_test_manifest.csv \
--current_manifest /workspace/data/drift_demo_manifests/pa_view_drift_test_manifest.csv \
--reference_split test \
--current_split test \
--out_dir /workspace/data/domain_concept_ap_vs_pa \
--enable_arize \
--arize_space_id "$ARIZE_SPACE_ID" \
--arize_model_id cxr8-ap-pa-drift \
--arize_environment validation \
--arize_region EU_WEST_1A \
--arize_batch_id ap-vs-pa-demo \
--arize_max_rows_per_cohort 250python src/monitoring/monitor_batch.py \
--data_root /workspace/data/cxr8_preprocessed \
--reference_manifest /workspace/data/drift_demo_manifests/ap_reference_train_manifest.csv \
--baseline_manifest /workspace/data/drift_demo_manifests/ap_id_test_manifest.csv \
--current_manifest /workspace/data/drift_demo_manifests/pa_view_drift_test_manifest.csv \
--reference_split train \
--baseline_split test \
--current_split test \
--model_path /workspace/data/reports_ap_only/best_model.pt \
--out_dir /workspace/data/monitoring_ap_train_vs_pa_test \
--trigger_retraining_file /workspace/data/monitoring_ap_train_vs_pa_test/retrain_required.jsonThis writes monitoring_report.json, alerts.json, monitoring_report.html, and linked Evidently HTML reports into the output directory.
This creates image-feature drift statistics, Evidently reports, embedding drift, prediction drift, performance drift, alerts.json, and a retraining trigger file when a critical threshold is crossed.
When --model_path is provided, batch monitoring also creates an Evidently prediction/target drift report. Add --enable_arize plus Arize credentials to send the same labeled prediction cohorts to Arize.
Build the image:
docker build --build-arg TORCH_VARIANT=cpu -t mlops-cpu .Start MLflow and the inference API:
docker compose up mlflow apiThe API exposes:
GET http://localhost:8000/health
POST http://localhost:8000/predict
Predictions are appended to /workspace/data/prediction_log.csv, which can be used as the basis for future production-batch monitoring.
Run a one-shot monitoring job from a JSON config:
python -m operations.monitoring_job \
--config configs/monitoring_job.example.json \
--max_runs 1 \
--history_json /workspace/data/monitoring_job_history.jsonRun it as a scheduled container job. The example uses --interval_seconds 86400 for daily monitoring:
docker compose up monitoring-jobThe monitoring job can run three steps:
monitoring_command -> runs batch monitoring
summary_command -> writes monitoring_summary.md
retraining_command -> starts retraining when critical alerts exist
rollback_command -> optional rollback if retraining fails or critical alerts require it
For manual retraining from an alert file:
python -m operations.retraining_orchestrator \
--alerts_json /workspace/data/monitoring_ap_train_vs_pa_test/alerts.json \
--status_json /workspace/data/retraining/retraining_status.json \
--data_root /workspace/data/cxr8_preprocessed \
--manifest_csv /workspace/data/drift_demo_manifests/ap_only_training_manifest.csv \
--out_dir /workspace/data/retraining \
--epochs 10 \
--batch_size 32 \
--use_pos_weight \
--use_mlflow \
--mlflow_tracking_uri http://localhost:5000 \
--mlflow_experiment cxr8-retraining \
--mlflow_log_model \
--registered_model_name cxr8-densenet121 \
--promote_to_stage Staging \
--candidate_validation_json /workspace/data/retraining/candidate_validation_report.json \
--baseline_training_report /workspace/data/reports_ap_only/training_and_drift_report.json \
--monitoring_report /workspace/data/monitoring_ap_train_vs_pa_test/monitoring_report.jsonManage the MLflow model lifecycle:
python -m operations.mlflow_registry status \
--tracking_uri http://localhost:5000 \
--model_name cxr8-densenet121
python -m operations.mlflow_registry promote \
--tracking_uri http://localhost:5000 \
--model_name cxr8-densenet121 \
--version 1 \
--stage Production
python -m operations.mlflow_registry rollback \
--tracking_uri http://localhost:5000 \
--model_name cxr8-densenet121 \
--stage ProductionRun an explicit promotion gate before moving a candidate model to Production:
python -m operations.candidate_validation \
--candidate_training_report /workspace/data/retraining/training_and_drift_report.json \
--baseline_training_report /workspace/data/reports_ap_only/training_and_drift_report.json \
--monitoring_report /workspace/data/monitoring_ap_train_vs_pa_test/monitoring_report.json \
--prediction_log_validation_report /workspace/data/prediction_log_validation.json \
--output_json /workspace/data/retraining/candidate_validation_report.json \
--min_candidate_auc 0.70 \
--max_auc_drop_vs_baseline 0.03 \
--max_critical_alerts 0 \
--require_prediction_log_validCreate a human approval record and require it for Production promotion:
python -m operations.mlflow_registry approve \
--tracking_uri http://localhost:5000 \
--model_name cxr8-densenet121 \
--version 2 \
--stage Production \
--approver "clinical-reviewer" \
--validation_report /workspace/data/retraining/candidate_validation_report.json \
--approval_record /workspace/data/retraining/production_approval.json
python -m operations.mlflow_registry promote \
--tracking_uri http://localhost:5000 \
--model_name cxr8-densenet121 \
--version 2 \
--stage Production \
--require_approval \
--approval_record /workspace/data/retraining/production_approval.jsonValidate production prediction logs:
python -m deployment.validate_prediction_log \
--prediction_log /workspace/data/prediction_log.csv \
--output_json /workspace/data/prediction_log_validation.jsonServe either a local state-dict checkpoint or the current MLflow Production model:
python -m deployment.serve_model \
--model_uri models:/cxr8-densenet121/Production \
--mlflow_tracking_uri http://localhost:5000 \
--prediction_log /workspace/data/prediction_log.csvCreate a compact Markdown observability summary:
python -m operations.monitoring_summary \
--monitoring_report /workspace/data/monitoring_ap_train_vs_pa_test/monitoring_report.json \
--alerts_json /workspace/data/monitoring_ap_train_vs_pa_test/alerts.json \
--output_md /workspace/data/monitoring_ap_train_vs_pa_test/monitoring_summary.mdCreate a central HTML dashboard that combines training metrics, shift evaluation, AP-vs-PA drift, monitoring alerts, and poster figures:
python -m operations.central_dashboard \
--reports_dir /workspace/data/reports \
--domain_dir /workspace/data/domain_concept_ap_vs_pa \
--monitoring_dir /workspace/data/monitoring_ap_train_vs_pa_test \
--shift_eval_dir /workspace/data/shift_eval \
--figures_dir /workspace/data/poster_figures \
--output_html /workspace/data/central_dashboard.html