|
@@ -3,34 +3,61 @@
|
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import argparse
|
|
import argparse
|
|
|
|
|
+import sys
|
|
|
from pathlib import Path
|
|
from pathlib import Path
|
|
|
from typing import Any
|
|
from typing import Any
|
|
|
|
|
|
|
|
import pandas as pd
|
|
import pandas as pd
|
|
|
|
|
+import torch
|
|
|
from tqdm.auto import tqdm
|
|
from tqdm.auto import tqdm
|
|
|
|
|
|
|
|
-from .analysis_modules import (
|
|
|
|
|
- run_calibration,
|
|
|
|
|
- run_longitudinal,
|
|
|
|
|
- run_performance,
|
|
|
|
|
- run_physician,
|
|
|
|
|
-)
|
|
|
|
|
-from .data_access import load_backend_evaluation, load_clinical_table
|
|
|
|
|
-from .dataset_summary import run_dataset_summary
|
|
|
|
|
-from .defaults import (
|
|
|
|
|
- DEFAULT_BACKENDS,
|
|
|
|
|
- DEFAULT_BAYESIAN_MC_PASSES,
|
|
|
|
|
- DEFAULT_CALIBRATION_BINS,
|
|
|
|
|
- DEFAULT_DECISION_THRESHOLD,
|
|
|
|
|
- DEFAULT_POSITIVE_CLASS_INDEX,
|
|
|
|
|
- noise_factor_grid,
|
|
|
|
|
- threshold_grid,
|
|
|
|
|
-)
|
|
|
|
|
-from .holdout_evaluation import ensure_backend_netcdf
|
|
|
|
|
-from .longitudinal_audit import run_longitudinal_breakdown_audit
|
|
|
|
|
-from .noise_analysis import run_noise_analysis
|
|
|
|
|
-from .noise_correlation import run_noise_accuracy_uncertainty_analysis
|
|
|
|
|
-from .runtime import backend_dir, init_runtime_paths, load_config, write_json
|
|
|
|
|
|
|
+if __package__ in (None, ""):
|
|
|
|
|
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
|
+ from analysis.analysis_modules import (
|
|
|
|
|
+ run_calibration,
|
|
|
|
|
+ run_longitudinal,
|
|
|
|
|
+ run_performance,
|
|
|
|
|
+ run_physician,
|
|
|
|
|
+ )
|
|
|
|
|
+ from analysis.data_access import load_backend_evaluation, load_clinical_table
|
|
|
|
|
+ from analysis.dataset_summary import run_dataset_summary
|
|
|
|
|
+ from analysis.defaults import (
|
|
|
|
|
+ DEFAULT_BACKENDS,
|
|
|
|
|
+ DEFAULT_BAYESIAN_MC_PASSES,
|
|
|
|
|
+ DEFAULT_CALIBRATION_BINS,
|
|
|
|
|
+ DEFAULT_DECISION_THRESHOLD,
|
|
|
|
|
+ DEFAULT_POSITIVE_CLASS_INDEX,
|
|
|
|
|
+ noise_factor_grid,
|
|
|
|
|
+ threshold_grid,
|
|
|
|
|
+ )
|
|
|
|
|
+ from analysis.holdout_evaluation import ensure_backend_netcdf
|
|
|
|
|
+ from analysis.longitudinal_audit import run_longitudinal_breakdown_audit
|
|
|
|
|
+ from analysis.noise_analysis import run_noise_analysis
|
|
|
|
|
+ from analysis.noise_correlation import run_noise_accuracy_uncertainty_analysis
|
|
|
|
|
+ from analysis.runtime import backend_dir, init_runtime_paths, load_config, write_json
|
|
|
|
|
+else:
|
|
|
|
|
+ from .analysis_modules import (
|
|
|
|
|
+ run_calibration,
|
|
|
|
|
+ run_longitudinal,
|
|
|
|
|
+ run_performance,
|
|
|
|
|
+ run_physician,
|
|
|
|
|
+ )
|
|
|
|
|
+ from .data_access import load_backend_evaluation, load_clinical_table
|
|
|
|
|
+ from .dataset_summary import run_dataset_summary
|
|
|
|
|
+ from .defaults import (
|
|
|
|
|
+ DEFAULT_BACKENDS,
|
|
|
|
|
+ DEFAULT_BAYESIAN_MC_PASSES,
|
|
|
|
|
+ DEFAULT_CALIBRATION_BINS,
|
|
|
|
|
+ DEFAULT_DECISION_THRESHOLD,
|
|
|
|
|
+ DEFAULT_POSITIVE_CLASS_INDEX,
|
|
|
|
|
+ noise_factor_grid,
|
|
|
|
|
+ threshold_grid,
|
|
|
|
|
+ )
|
|
|
|
|
+ from .holdout_evaluation import ensure_backend_netcdf
|
|
|
|
|
+ from .longitudinal_audit import run_longitudinal_breakdown_audit
|
|
|
|
|
+ from .noise_analysis import run_noise_analysis
|
|
|
|
|
+ from .noise_correlation import run_noise_accuracy_uncertainty_analysis
|
|
|
|
|
+ from .runtime import backend_dir, init_runtime_paths, load_config, write_json
|
|
|
|
|
|
|
|
|
|
|
|
|
def _plot_description(filename: str) -> str:
|
|
def _plot_description(filename: str) -> str:
|
|
@@ -95,6 +122,29 @@ def _write_backend_plot_report(backend: str, out_dir: Path) -> Path:
|
|
|
return report_path
|
|
return report_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _print_device_info(config: dict[str, Any]) -> None:
|
|
|
|
|
+ device = torch.device(str(config["training"]["device"]))
|
|
|
|
|
+ print(f"Analysis device: {device}")
|
|
|
|
|
+
|
|
|
|
|
+ if device.type == "cuda":
|
|
|
|
|
+ if not torch.cuda.is_available():
|
|
|
|
|
+ print("CUDA is not available in this environment.")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ index = device.index if device.index is not None else torch.cuda.current_device()
|
|
|
|
|
+ props = torch.cuda.get_device_properties(index)
|
|
|
|
|
+ print(f"GPU index: {index}")
|
|
|
|
|
+ print(f"GPU name: {torch.cuda.get_device_name(index)}")
|
|
|
|
|
+ print(f"GPU capability: {props.major}.{props.minor}")
|
|
|
|
|
+ print(f"GPU total memory: {props.total_memory / (1024 ** 3):.2f} GiB")
|
|
|
|
|
+ print(f"CUDA runtime: {torch.version.cuda or 'unknown'}")
|
|
|
|
|
+ print(f"Visible CUDA devices: {torch.cuda.device_count()}")
|
|
|
|
|
+ elif device.type == "mps":
|
|
|
|
|
+ print("Apple Metal Performance Shaders backend selected.")
|
|
|
|
|
+ else:
|
|
|
|
|
+ print("CPU backend selected.")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _parse_args() -> argparse.Namespace:
|
|
def _parse_args() -> argparse.Namespace:
|
|
|
parser = argparse.ArgumentParser(
|
|
parser = argparse.ArgumentParser(
|
|
|
description=(
|
|
description=(
|
|
@@ -328,6 +378,7 @@ def main() -> None:
|
|
|
analysis_dir = Path(__file__).resolve().parent
|
|
analysis_dir = Path(__file__).resolve().parent
|
|
|
paths = init_runtime_paths(analysis_dir=analysis_dir, run_name=args.run_name)
|
|
paths = init_runtime_paths(analysis_dir=analysis_dir, run_name=args.run_name)
|
|
|
config = load_config(paths.root_dir)
|
|
config = load_config(paths.root_dir)
|
|
|
|
|
+ _print_device_info(config)
|
|
|
clinical_df = load_clinical_table(config=config, root_dir=paths.root_dir)
|
|
clinical_df = load_clinical_table(config=config, root_dir=paths.root_dir)
|
|
|
|
|
|
|
|
manifest: dict[str, Any] = {
|
|
manifest: dict[str, Any] = {
|