Keras
English
remote-sensing
hyperspectral
uncertainty-quantification
conformal-prediction
mamba
foundation-models
Instructions to use DaneshSelwal/Classification-DIAS-65Bands with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use DaneshSelwal/Classification-DIAS-65Bands with Keras:
# !pip install -U keras tensorflow huggingface_hub # Keras needs TensorFlow installed to read "hf://" paths, so the tensorflow backend is selected here; # "jax" and "torch" also work for computation once TensorFlow is installed. import os os.environ["KERAS_BACKEND"] = "tensorflow" import keras model = keras.saving.load_model("hf://DaneshSelwal/Classification-DIAS-65Bands") - Notebooks
- Google Colab
- Kaggle
Clean up codebase files to make it a strict Model Registry
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- adapt_k014.py +0 -252
- agent.md +0 -109
- baseline/Model_training.ipynb +0 -0
- baseline/Model_uncertainty_comparison.ipynb +0 -0
- baseline/results/AlexNet_CNN_classification_report.json +0 -63
- baseline/results/GFNet_classification_report.json +0 -63
- baseline/results/ViT_UNet_classification_report.json +0 -63
- baseline/results/classification_summary.csv +0 -4
- baseline/results/scene_visualizations/AlexNet_CNN_initial_classification.png +0 -3
- baseline/results/scene_visualizations/GFNet_initial_classification.png +0 -3
- baseline/results/scene_visualizations/ViT_UNet_initial_classification.png +0 -3
- baseline/results/scene_visualizations/combined_initial_classification_overview.png +0 -3
- baseline/results/scene_visualizations/ground_truth_label_map.png +0 -3
- baseline/results/scene_visualizations/initial_classification_maps.xlsx +0 -3
- baseline/results/scene_visualizations/scene_rgb.png +0 -3
- baseline/results/training_plots/AlexNet_CNN_training_curves.png +0 -3
- baseline/results/training_plots/GFNet_training_curves.png +0 -3
- baseline/results/training_plots/ViT_UNet_training_curves.png +0 -3
- baseline/results/training_plots/confusion_matrices_side_by_side.png +0 -3
- baseline/results/training_plots/model_comparison_metrics.png +0 -3
- baseline/results/training_plots/uncertainty_proxy_metrics.png +0 -0
- baseline/results/uncertainty_results/conformal_reports_all_models.xlsx +0 -3
- baseline/results/uncertainty_results/per_class_coverage.csv +0 -121
- baseline/results/uncertainty_results/run_config.json +0 -20
- baseline/results/uncertainty_results/summary_metrics.csv +0 -16
- baseline/test_m2_challenger.py +0 -261
- baseline/verify_m1_dataset.py +0 -375
- credit/Model_training_credit.ipynb +0 -2065
- credit/results/AlexNet_CNN_CREDIT_spatial_maps.png +0 -3
- credit/results/CREDIT_Results.xlsx +0 -3
- credit/results/GFNet_CREDIT_spatial_maps.png +0 -3
- credit/results/ViT_UNet_CREDIT_spatial_maps.png +0 -3
- credit/results/credit_confusion_matrices.png +0 -3
- credit/results/credit_evaluation_summary.csv +0 -4
- credit/results/credit_train_uncertainty_summary.csv +0 -4
- ensemble/Model_training_ensembles.ipynb +0 -2006
- ensemble/Model_uncertainty_CreDE.ipynb +0 -1365
- ensemble/results/AlexNet_CNN_CreDE_spatial_maps.png +0 -3
- ensemble/results/AlexNet_CNN_classification_report.json +0 -63
- ensemble/results/AlexNet_CNN_training_curves.png +0 -3
- ensemble/results/CreDE_Master_Summary.csv +0 -4
- ensemble/results/CreDE_Results.xlsx +0 -3
- ensemble/results/GFNet_CreDE_spatial_maps.png +0 -3
- ensemble/results/GFNet_classification_report.json +0 -63
- ensemble/results/GFNet_training_curves.png +0 -3
- ensemble/results/ViT_UNet_CreDE_spatial_maps.png +0 -3
- ensemble/results/ViT_UNet_classification_report.json +0 -63
- ensemble/results/ViT_UNet_training_curves.png +0 -3
- ensemble/results/classification_summary.csv +0 -16
- ensemble/results/combined_initial_classification_overview.png +0 -3
adapt_k014.py
DELETED
|
@@ -1,252 +0,0 @@
|
|
| 1 |
-
import ast
|
| 2 |
-
import json
|
| 3 |
-
from pathlib import Path
|
| 4 |
-
|
| 5 |
-
BASE_DIR = Path("/Users/danesh/Documents/AGY/agy/classification")
|
| 6 |
-
source_nb_path = BASE_DIR / "ensemble" / "Model_uncertainty_CreDE.ipynb"
|
| 7 |
-
staged_nb_path = BASE_DIR / "ensemble" / "staging_crede" / "k014.ipynb"
|
| 8 |
-
|
| 9 |
-
with open(source_nb_path, "r", encoding="utf-8") as f:
|
| 10 |
-
nb = json.load(f)
|
| 11 |
-
|
| 12 |
-
print(f"Original cells: {len(nb['cells'])}")
|
| 13 |
-
|
| 14 |
-
# Cell 2 adaptation
|
| 15 |
-
cell_2_code = """# Environment detection & optional Colab mount
|
| 16 |
-
import sys
|
| 17 |
-
if 'google.colab' in sys.modules:
|
| 18 |
-
from google.colab import drive
|
| 19 |
-
drive.mount('/content/drive', force_remount=False)
|
| 20 |
-
|
| 21 |
-
# Standard library
|
| 22 |
-
import os
|
| 23 |
-
import json
|
| 24 |
-
import time
|
| 25 |
-
import random
|
| 26 |
-
import io
|
| 27 |
-
import gc
|
| 28 |
-
import glob
|
| 29 |
-
import subprocess
|
| 30 |
-
from pathlib import Path
|
| 31 |
-
|
| 32 |
-
from scipy.io import loadmat
|
| 33 |
-
|
| 34 |
-
# Defensive package verification / installation
|
| 35 |
-
for pkg in ['openpyxl', 'pillow']:
|
| 36 |
-
try:
|
| 37 |
-
__import__(pkg)
|
| 38 |
-
except ImportError:
|
| 39 |
-
subprocess.run([sys.executable, '-m', 'pip', 'install', '-q', pkg], check=True)
|
| 40 |
-
|
| 41 |
-
# Numerical / data
|
| 42 |
-
import numpy as np
|
| 43 |
-
import pandas as pd
|
| 44 |
-
|
| 45 |
-
# Visualisation (headless backend configured before importing pyplot)
|
| 46 |
-
import matplotlib
|
| 47 |
-
matplotlib.use('Agg')
|
| 48 |
-
import matplotlib.pyplot as plt
|
| 49 |
-
from matplotlib.colors import ListedColormap
|
| 50 |
-
from matplotlib.patches import Patch
|
| 51 |
-
import seaborn as sns
|
| 52 |
-
|
| 53 |
-
# Deep learning
|
| 54 |
-
import tensorflow as tf
|
| 55 |
-
from tensorflow import keras
|
| 56 |
-
from tensorflow.keras import layers
|
| 57 |
-
|
| 58 |
-
# GPU Dynamic Memory Growth Configuration
|
| 59 |
-
gpus = tf.config.list_physical_devices('GPU')
|
| 60 |
-
if gpus:
|
| 61 |
-
for gpu in gpus:
|
| 62 |
-
try:
|
| 63 |
-
tf.config.experimental.set_memory_growth(gpu, True)
|
| 64 |
-
except RuntimeError as e:
|
| 65 |
-
print(f"Memory growth error: {e}")
|
| 66 |
-
print(f"GPU Active: {len(gpus)} device(s) detected: {[g.name for g in gpus]}")
|
| 67 |
-
else:
|
| 68 |
-
print("WARNING: No physical GPU detected!")
|
| 69 |
-
|
| 70 |
-
# Enable Keras unsafe deserialization for custom layer loading
|
| 71 |
-
try:
|
| 72 |
-
keras.config.enable_unsafe_deserialization()
|
| 73 |
-
print("Keras unsafe deserialization enabled.")
|
| 74 |
-
except Exception as e:
|
| 75 |
-
print(f"Notice: keras.config.enable_unsafe_deserialization(): {e}")
|
| 76 |
-
|
| 77 |
-
# Scikit-learn
|
| 78 |
-
from sklearn.model_selection import train_test_split
|
| 79 |
-
from sklearn.metrics import (
|
| 80 |
-
accuracy_score,
|
| 81 |
-
cohen_kappa_score,
|
| 82 |
-
confusion_matrix,
|
| 83 |
-
classification_report,
|
| 84 |
-
f1_score,
|
| 85 |
-
log_loss,
|
| 86 |
-
)
|
| 87 |
-
|
| 88 |
-
# Excel export
|
| 89 |
-
from openpyxl import Workbook
|
| 90 |
-
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
| 91 |
-
from openpyxl.utils import get_column_letter
|
| 92 |
-
from openpyxl.drawing.image import Image as XLImage
|
| 93 |
-
|
| 94 |
-
sns.set_style('whitegrid')
|
| 95 |
-
print('Python: ', sys.version.split()[0])
|
| 96 |
-
print('TensorFlow:', tf.__version__)
|
| 97 |
-
print('Keras: ', keras.__version__)"""
|
| 98 |
-
|
| 99 |
-
nb["cells"][2]["source"] = [line + "\n" for line in cell_2_code.split("\n")]
|
| 100 |
-
nb["cells"][2]["source"][-1] = nb["cells"][2]["source"][-1].rstrip("\n")
|
| 101 |
-
|
| 102 |
-
# Cell 7 adaptation
|
| 103 |
-
cell_7_code = """# Reproducibility
|
| 104 |
-
SEED = 42
|
| 105 |
-
np.random.seed(SEED)
|
| 106 |
-
tf.random.set_seed(SEED)
|
| 107 |
-
|
| 108 |
-
IN_KAGGLE = Path('/kaggle').exists() or Path('/kaggle/working').exists()
|
| 109 |
-
IN_COLAB = 'google.colab' in sys.modules
|
| 110 |
-
|
| 111 |
-
if IN_KAGGLE:
|
| 112 |
-
k001_primary = Path('/kaggle/input/dias-dataset-k01/k001.mat')
|
| 113 |
-
k002_primary = Path('/kaggle/input/dias-dataset-k01/k002.mat')
|
| 114 |
-
if k001_primary.exists() and k002_primary.exists():
|
| 115 |
-
DATA_FILE = k001_primary
|
| 116 |
-
LABEL_FILE = k002_primary
|
| 117 |
-
DATA_DIR = k001_primary.parent
|
| 118 |
-
else:
|
| 119 |
-
dias_candidates = (
|
| 120 |
-
list(Path('/kaggle/input').glob('**/k001.mat')) or
|
| 121 |
-
list(Path('/kaggle/input').glob('**/DIAS.mat'))
|
| 122 |
-
)
|
| 123 |
-
ref_candidates = (
|
| 124 |
-
list(Path('/kaggle/input').glob('**/k002.mat')) or
|
| 125 |
-
list(Path('/kaggle/input').glob('**/DIAS_ref.mat'))
|
| 126 |
-
)
|
| 127 |
-
DATA_FILE = dias_candidates[0] if dias_candidates else k001_primary
|
| 128 |
-
LABEL_FILE = ref_candidates[0] if ref_candidates else k002_primary
|
| 129 |
-
DATA_DIR = DATA_FILE.parent
|
| 130 |
-
|
| 131 |
-
ensemble_candidates = (
|
| 132 |
-
list(Path('/kaggle/input/dias-ensemble-k13').glob('**/ensembles')) or
|
| 133 |
-
list(Path('/kaggle/input/dias-ensemble-k13').glob('**/models/ensembles')) or
|
| 134 |
-
list(Path('/kaggle/input').glob('**/ensembles'))
|
| 135 |
-
)
|
| 136 |
-
if ensemble_candidates:
|
| 137 |
-
MODEL_DIR = ensemble_candidates[0]
|
| 138 |
-
else:
|
| 139 |
-
MODEL_DIR = Path('/kaggle/input/dias-ensemble-k13/results/models/ensembles')
|
| 140 |
-
|
| 141 |
-
ROOT_DIR = Path('/kaggle/working')
|
| 142 |
-
CREDE_OUT_DIR = Path('/kaggle/working/results')
|
| 143 |
-
|
| 144 |
-
elif IN_COLAB:
|
| 145 |
-
ROOT_DIR = Path('/content/drive/My Drive/Classification')
|
| 146 |
-
MODEL_DIR = ROOT_DIR / 'ensemble' / 'models' / 'ensembles'
|
| 147 |
-
DATA_DIR = ROOT_DIR / 'data'
|
| 148 |
-
DATA_FILE = DATA_DIR / 'DIAS.mat'
|
| 149 |
-
LABEL_FILE = DATA_DIR / 'DIAS_ref.mat'
|
| 150 |
-
CREDE_OUT_DIR = ROOT_DIR / 'ensemble' / 'results'
|
| 151 |
-
|
| 152 |
-
else:
|
| 153 |
-
ROOT_DIR = Path.cwd()
|
| 154 |
-
DATA_DIR = Path('data') if Path('data').exists() else ROOT_DIR / 'data'
|
| 155 |
-
DATA_FILE = DATA_DIR / 'DIAS.mat' if (DATA_DIR / 'DIAS.mat').exists() else DATA_DIR / 'k001.mat'
|
| 156 |
-
LABEL_FILE = DATA_DIR / 'DIAS_ref.mat' if (DATA_DIR / 'DIAS_ref.mat').exists() else DATA_DIR / 'k002.mat'
|
| 157 |
-
MODEL_DIR = Path('ensemble/results/models/ensembles') if Path('ensemble/results/models/ensembles').exists() else ROOT_DIR / 'ensemble' / 'models' / 'ensembles'
|
| 158 |
-
CREDE_OUT_DIR = Path('ensemble/results')
|
| 159 |
-
|
| 160 |
-
CREDE_OUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 161 |
-
|
| 162 |
-
# Scene geometry — H, W, B are inferred automatically from the .mat cube shape
|
| 163 |
-
# once loaded in Section 3.1, so this notebook works unchanged for any scene
|
| 164 |
-
# size / band count.
|
| 165 |
-
H, W, B = None, None, None
|
| 166 |
-
PERCENTILE_CLIP = (2.0, 98.0) # robust per-band stretch range (2nd-98th percentile)
|
| 167 |
-
PATCH_SIZE = 9
|
| 168 |
-
|
| 169 |
-
# Uncertainty thresholds used for masking
|
| 170 |
-
AU_THRESH = 0.5
|
| 171 |
-
EU_THRESH = 0.2
|
| 172 |
-
TU_THRESH = 0.7
|
| 173 |
-
|
| 174 |
-
print('Environment :', 'Kaggle' if IN_KAGGLE else ('Colab' if IN_COLAB else 'Local'))
|
| 175 |
-
print('Data file :', DATA_FILE, f'(exists: {DATA_FILE.exists()})')
|
| 176 |
-
print('Label file :', LABEL_FILE, f'(exists: {LABEL_FILE.exists()})')
|
| 177 |
-
print('Model dir :', MODEL_DIR, f'(exists: {MODEL_DIR.exists()})')
|
| 178 |
-
print('CreDE out dir:', CREDE_OUT_DIR)"""
|
| 179 |
-
|
| 180 |
-
nb["cells"][7]["source"] = [line + "\n" for line in cell_7_code.split("\n")]
|
| 181 |
-
nb["cells"][7]["source"][-1] = nb["cells"][7]["source"][-1].rstrip("\n")
|
| 182 |
-
|
| 183 |
-
# Cell 9 adaptation
|
| 184 |
-
cell_9_code = """CREDE_OUT_DIR.mkdir(parents=True, exist_ok=True)
|
| 185 |
-
print(f"CreDE output directory verified: {CREDE_OUT_DIR}")"""
|
| 186 |
-
|
| 187 |
-
nb["cells"][9]["source"] = [line + "\n" for line in cell_9_code.split("\n")]
|
| 188 |
-
nb["cells"][9]["source"][-1] = nb["cells"][9]["source"][-1].rstrip("\n")
|
| 189 |
-
|
| 190 |
-
# Cell 29 adaptation
|
| 191 |
-
cell_29_code = '''def get_ensemble_paths(model_name):
|
| 192 |
-
"""
|
| 193 |
-
Locate all saved `.keras` checkpoint files for a given architecture.
|
| 194 |
-
Searches MODEL_DIR, subdirectories, and recursively across /kaggle/input/.
|
| 195 |
-
"""
|
| 196 |
-
search_patterns = [
|
| 197 |
-
str(MODEL_DIR / f"{model_name}_ens_*_final.keras"),
|
| 198 |
-
str(MODEL_DIR / "ensembles" / f"{model_name}_ens_*_final.keras"),
|
| 199 |
-
f"/kaggle/input/dias-ensemble-k13/**/{model_name}_ens_*_final.keras",
|
| 200 |
-
f"/kaggle/input/**/{model_name}_ens_*_final.keras",
|
| 201 |
-
str(Path("ensemble/results") / "**" / f"{model_name}_ens_*_final.keras"),
|
| 202 |
-
]
|
| 203 |
-
for pattern in search_patterns:
|
| 204 |
-
matches = sorted(glob.glob(pattern, recursive=True))
|
| 205 |
-
valid_matches = [p for p in matches if not p.endswith('.tmp')]
|
| 206 |
-
if valid_matches:
|
| 207 |
-
print(f" Found {len(valid_matches)} models for {model_name} matching pattern: {pattern}")
|
| 208 |
-
return valid_matches
|
| 209 |
-
|
| 210 |
-
print(f" WARNING: No ensemble models found for {model_name} across searched patterns.")
|
| 211 |
-
return []'''
|
| 212 |
-
|
| 213 |
-
nb["cells"][29]["source"] = [line + "\n" for line in cell_29_code.split("\n")]
|
| 214 |
-
nb["cells"][29]["source"][-1] = nb["cells"][29]["source"][-1].rstrip("\n")
|
| 215 |
-
|
| 216 |
-
# Cell 34 adaptation
|
| 217 |
-
cell_34_src = "".join(nb["cells"][34]["source"])
|
| 218 |
-
assert "plt.show()" in cell_34_src, "plt.show() not found in cell 34!"
|
| 219 |
-
cell_34_adapted = cell_34_src.replace("plt.show()", "plt.close(fig)")
|
| 220 |
-
assert "plt.show()" not in cell_34_adapted, "plt.show() still found in cell 34!"
|
| 221 |
-
assert "plt.close(fig)" in cell_34_adapted, "plt.close(fig) not found in cell 34!"
|
| 222 |
-
nb["cells"][34]["source"] = [line + "\n" for line in cell_34_adapted.split("\n")]
|
| 223 |
-
nb["cells"][34]["source"][-1] = nb["cells"][34]["source"][-1].rstrip("\n")
|
| 224 |
-
|
| 225 |
-
# Hygiene: outputs: [], execution_count: None across all code cells
|
| 226 |
-
code_cell_count = 0
|
| 227 |
-
for i, cell in enumerate(nb["cells"]):
|
| 228 |
-
if cell["cell_type"] == "code":
|
| 229 |
-
code_cell_count += 1
|
| 230 |
-
cell["outputs"] = []
|
| 231 |
-
cell["execution_count"] = None
|
| 232 |
-
# AST validate
|
| 233 |
-
src = "".join(cell["source"])
|
| 234 |
-
try:
|
| 235 |
-
ast.parse(src)
|
| 236 |
-
except SyntaxError as e:
|
| 237 |
-
print(f"SyntaxError in cell {i}: {e}")
|
| 238 |
-
raise
|
| 239 |
-
|
| 240 |
-
print(f"Total code cells validated: {code_cell_count}")
|
| 241 |
-
|
| 242 |
-
# Root metadata
|
| 243 |
-
if "colab" in nb.get("metadata", {}):
|
| 244 |
-
del nb["metadata"]["colab"]
|
| 245 |
-
nb["metadata"]["accelerator"] = "GPU"
|
| 246 |
-
if "kernelspec" not in nb.get("metadata", {}):
|
| 247 |
-
nb["metadata"]["kernelspec"] = {"display_name": "Python 3", "name": "python3"}
|
| 248 |
-
|
| 249 |
-
with open("/tmp/k014.ipynb", "w", encoding="utf-8") as f:
|
| 250 |
-
json.dump(nb, f, indent=1)
|
| 251 |
-
|
| 252 |
-
print(f"Successfully wrote staged notebook to /tmp/k014.ipynb")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agent.md
DELETED
|
@@ -1,109 +0,0 @@
|
|
| 1 |
-
# MASTER CONTEXT & WORKFLOW: Hyperspectral Image Classification & UQ (DIAS 65-Bands)
|
| 2 |
-
|
| 3 |
-
**Last Updated:** 2026-09-16
|
| 4 |
-
**Role:** This is the single authoritative "Master File" for any agent, developer, or automated system working in this workspace. It dictates the architectural setup, pipeline execution process, storage strategy, and historical context of the DIAS 65-bands Uncertainty Quantification (UQ) project.
|
| 5 |
-
|
| 6 |
-
---
|
| 7 |
-
|
| 8 |
-
## 1. Project Domain & Objective
|
| 9 |
-
This project implements a generalized framework for **Hyperspectral Remote Sensing Image Classification and Uncertainty Quantification**.
|
| 10 |
-
Specifically, this repository is dedicated to processing the **DIAS agricultural dataset (65 spectral bands)**. The goal is to train deep learning models (AlexNet, GFNet, ViT-UNet) and apply various UQ methodologies (Conformal Prediction, Ensembles, Distillation) to measure epistemic and aleatoric uncertainty, yielding reliable spatial maps and metrics.
|
| 11 |
-
|
| 12 |
-
---
|
| 13 |
-
|
| 14 |
-
## 2. Workspace Architecture & Storage Strategy
|
| 15 |
-
The environment is split across three primary locations to balance local Mac memory limitations with the need for persistent cloud backups.
|
| 16 |
-
|
| 17 |
-
1. **Active Local Workspace (`/Users/danesh/Documents/AGY/agy/classification/`)**
|
| 18 |
-
- The primary staging and development ground.
|
| 19 |
-
- **Git Tracking:** Tracks the remote GitHub repository `DaneshSelwal/classification_dias.git`.
|
| 20 |
-
- **Constraint:** Do not store redundant `.zip` or unzipped caches here. Clean up `dl_temp/` folders after processing.
|
| 21 |
-
|
| 22 |
-
2. **Google Drive Cold Storage (`/Users/danesh/Documents/AGY/classification_65bands/`)**
|
| 23 |
-
- The ultimate local mirror to the Google Drive cloud backup.
|
| 24 |
-
- **Syncing Rule:** This folder must strictly mirror the finalized artifacts from the Local Workspace using `rsync`. It is never to be used as a working directory.
|
| 25 |
-
|
| 26 |
-
3. **GitHub Repository (`classification_dias`)**
|
| 27 |
-
- **Git LFS:** All heavy `.keras` and `.h5` model weights MUST be tracked via Git Large File Storage (LFS).
|
| 28 |
-
- Before pushing, always run `git lfs track "folder/models/*.keras"`.
|
| 29 |
-
|
| 30 |
-
---
|
| 31 |
-
|
| 32 |
-
## 3. The Orchestration Pipeline (How Work Gets Done)
|
| 33 |
-
Due to computational constraints on the local Mac, **all model training is orchestrated autonomously on Kaggle GPUs**. Any new pipeline must follow this strict 9-step execution workflow:
|
| 34 |
-
|
| 35 |
-
### Step 1: Notebook Adaptation
|
| 36 |
-
The local Jupyter notebook must be adapted for Kaggle. File paths must point to `/kaggle/input/...` and outputs must be written to `/kaggle/working/...`.
|
| 37 |
-
|
| 38 |
-
### Step 2: Obfuscation Strategy (Crucial)
|
| 39 |
-
To prevent Kaggle automated bans/flags when uploading datasets:
|
| 40 |
-
- Every dataset file and model weight must be renamed to a generic alias (e.g., `k001.mat`, `k016.keras`).
|
| 41 |
-
- The mapping must be registered in `/Users/danesh/Documents/AGY/agy/classification/kaggle_name_mapping.json`.
|
| 42 |
-
|
| 43 |
-
### Step 3: Kaggle Upload
|
| 44 |
-
- **Data/Models:** Large files must be uploaded as a Kaggle Dataset (`kaggle datasets create`).
|
| 45 |
-
- **Code:** The notebook is pushed as a Kaggle Kernel (`kaggle kernels push`).
|
| 46 |
-
|
| 47 |
-
### Step 4: Execution & Monitoring
|
| 48 |
-
Run `kaggle kernels status <slug>` and poll until the status is `COMPLETE`.
|
| 49 |
-
|
| 50 |
-
### Step 5: Download Outputs
|
| 51 |
-
Use `kaggle kernels output <slug> -p <local_temp_folder> -o` to pull down the generated `.keras` weights and evaluation CSV/Excel files.
|
| 52 |
-
|
| 53 |
-
### Step 6: Local Organization
|
| 54 |
-
Move downloaded artifacts from the temp folder to their permanent homes (e.g., `baseline/models/` and `baseline/results/`).
|
| 55 |
-
|
| 56 |
-
### Step 7: Local Cache Cleanup
|
| 57 |
-
**Immediately delete** the local `dl_temp/` caches to free up Mac storage memory.
|
| 58 |
-
|
| 59 |
-
### Step 8: Git Commit & Push
|
| 60 |
-
Commit the models and results. **Agents must use `BypassSandbox: true`** when executing `git push` to GitHub.
|
| 61 |
-
|
| 62 |
-
### Step 9: Google Drive Sync
|
| 63 |
-
Run `rsync -av /Users/danesh/Documents/AGY/agy/classification/ /Users/danesh/Documents/AGY/classification_65bands/ --exclude '.git'` to update the cold storage.
|
| 64 |
-
|
| 65 |
-
---
|
| 66 |
-
|
| 67 |
-
## 4. Completed Pipelines & Methodologies
|
| 68 |
-
*All items listed here have been fully trained, evaluated, pushed to GitHub (via Git LFS), and synced to Google Drive.*
|
| 69 |
-
|
| 70 |
-
1. **Baseline (`baseline/`)**
|
| 71 |
-
- **Method:** Softmax, MC Dropout, Temperature Scaling.
|
| 72 |
-
- **Models:** AlexNet, GFNet, ViT-UNet (both `_best` and `_final`).
|
| 73 |
-
2. **Self-Adaptive Conformal Prediction (`sacp/`)**
|
| 74 |
-
- **Method:** Post-hoc calibration evaluated across spatial windows (3, 5, 7, 9).
|
| 75 |
-
3. **Multi-Head CP (`multicp/`)**
|
| 76 |
-
- **Method:** Architectural modification outputting multi-head predictions.
|
| 77 |
-
- **Models:** 6 multi-head `.keras` variants.
|
| 78 |
-
4. **Deep Ensembles & CreDE (`ensemble/`)**
|
| 79 |
-
- **Method:** 15 distinct teacher models trained (5 seeds per architecture).
|
| 80 |
-
- **Evaluation:** CreDE spatial uncertainty maps generated.
|
| 81 |
-
- **Kaggle Dependency:** These 15 models were uploaded as a dedicated Kaggle Dataset (`daneshselwal/dias-ensemble-models`, obfuscated as `k016-k030`) so other kernels could access them.
|
| 82 |
-
|
| 83 |
-
## 5. Active / Pending Pipelines
|
| 84 |
-
*All pipelines are now COMPLETE as of 2026-09-16. No active Kaggle kernels running.*
|
| 85 |
-
|
| 86 |
-
### ✅ CREDIT Distillation (`credit/`) — COMPLETED 2026-09-16
|
| 87 |
-
- **Method:** Conformalized Regularized Distillation Training (Student learning from the 15 Ensembles).
|
| 88 |
-
- **Models:** AlexNet_CNN_CREDIT_best.keras (260MB), GFNet_CREDIT_best.keras (125MB), ViT_UNet_CREDIT_best.keras (187MB)
|
| 89 |
-
- **Results:** CREDIT_Results.xlsx, credit_evaluation_summary.csv, credit_train_uncertainty_summary.csv, 3 spatial maps PNGs, confusion matrices PNG
|
| 90 |
-
- **GitHub:** Pushed via Git LFS (commit dcb2cfc)
|
| 91 |
-
- **Drive:** Synced to classification_65bands/credit/results/
|
| 92 |
-
- *Note for future agents:* Kaggle kernels cannot read large `.keras` files from other kernels via `kernel_sources`. Fix: mount ensemble teachers explicitly as `dataset_sources` (dias-ensemble-models dataset, k016-k030).
|
| 93 |
-
|
| 94 |
-
## 6. Future Skeletons (To Be Implemented)
|
| 95 |
-
The original research repository contains several advanced methodologies that currently exist only as empty "skeleton" directories in this workspace. Future iterations of this project will port and execute these:
|
| 96 |
-
- `dapm/` (Deep Adaptive Predictive Modeling)
|
| 97 |
-
- `dofa/` (Dynamic Wavelength Tokenization)
|
| 98 |
-
- `georsclip/` (Vision-Language Model)
|
| 99 |
-
- `mambahsi/` (State Space Models)
|
| 100 |
-
- `spatialgcn/` (Graph Convolutional Networks)
|
| 101 |
-
- `multicp_sacp/` (Spatial Multi-Head CP)
|
| 102 |
-
|
| 103 |
-
---
|
| 104 |
-
|
| 105 |
-
## 7. Hard Rules for Autonomous Agents
|
| 106 |
-
1. **Never delete `.keras` files** from Git-tracked folders unless you are intentionally removing them from the GitHub repository using `git rm`.
|
| 107 |
-
2. **Always use `BypassSandbox: true`** when communicating with network interfaces (Git push, Kaggle CLI commands, Rsync).
|
| 108 |
-
3. **Protect the Google Drive:** Only write to `/Users/danesh/Documents/AGY/classification_65bands/` using `rsync` from the local workspace. Do not treat it as a scratch pad.
|
| 109 |
-
4. **Update this Document:** If a new pipeline is executed, a new Kaggle dataset is created, or a core architectural rule changes, this `agent.md` file MUST be updated to reflect the new state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/Model_training.ipynb
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
baseline/Model_uncertainty_comparison.ipynb
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
baseline/results/AlexNet_CNN_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 0.9982363315696648,
|
| 16 |
-
"recall": 1.0,
|
| 17 |
-
"f1-score": 0.999117387466902,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 1.0,
|
| 23 |
-
"f1-score": 1.0,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 0.9996854356715948,
|
| 28 |
-
"recall": 0.9993710691823899,
|
| 29 |
-
"f1-score": 0.9995282277087593,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 0.9967074317968015,
|
| 34 |
-
"recall": 1.0,
|
| 35 |
-
"f1-score": 0.9983510011778564,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 1.0,
|
| 40 |
-
"recall": 0.9824561403508771,
|
| 41 |
-
"f1-score": 0.9911504424778761,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 0.9984399375975039,
|
| 46 |
-
"recall": 0.9984399375975039,
|
| 47 |
-
"f1-score": 0.9984399375975039,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 0.9992578298946119,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 0.9991336420794457,
|
| 53 |
-
"recall": 0.9975333933913464,
|
| 54 |
-
"f1-score": 0.9983233745536122,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 0.9992596479947695,
|
| 59 |
-
"recall": 0.9992578298946119,
|
| 60 |
-
"f1-score": 0.999255974726486,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/GFNet_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 1.0,
|
| 16 |
-
"recall": 1.0,
|
| 17 |
-
"f1-score": 1.0,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 1.0,
|
| 23 |
-
"f1-score": 1.0,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 1.0,
|
| 28 |
-
"recall": 1.0,
|
| 29 |
-
"f1-score": 1.0,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 0.9995283018867924,
|
| 34 |
-
"recall": 1.0,
|
| 35 |
-
"f1-score": 0.9997640953054966,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 1.0,
|
| 40 |
-
"recall": 0.9974937343358395,
|
| 41 |
-
"f1-score": 0.998745294855709,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 1.0,
|
| 46 |
-
"recall": 1.0,
|
| 47 |
-
"f1-score": 1.0,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 0.9999257829894612,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 0.9999410377358491,
|
| 53 |
-
"recall": 0.99968671679198,
|
| 54 |
-
"f1-score": 0.9998136737701507,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 0.999925817997485,
|
| 59 |
-
"recall": 0.9999257829894612,
|
| 60 |
-
"f1-score": 0.9999257451832994,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/ViT_UNet_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 1.0,
|
| 16 |
-
"recall": 1.0,
|
| 17 |
-
"f1-score": 1.0,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 0.9992671308171491,
|
| 23 |
-
"f1-score": 0.999633431085044,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 0.9990571967316153,
|
| 28 |
-
"recall": 0.9996855345911949,
|
| 29 |
-
"f1-score": 0.9993712668972021,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 0.9990543735224586,
|
| 34 |
-
"recall": 0.997168475696083,
|
| 35 |
-
"f1-score": 0.9981105337742088,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 0.9900744416873449,
|
| 40 |
-
"recall": 1.0,
|
| 41 |
-
"f1-score": 0.9950124688279302,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 1.0,
|
| 46 |
-
"recall": 1.0,
|
| 47 |
-
"f1-score": 1.0,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 0.9993320469051507,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 0.9985232514926774,
|
| 53 |
-
"recall": 0.9995151426380534,
|
| 54 |
-
"f1-score": 0.9990159625730481,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 0.9993348527040135,
|
| 59 |
-
"recall": 0.9993320469051507,
|
| 60 |
-
"f1-score": 0.9993325262204305,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/classification_summary.csv
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
model,capacity_tag,test_accuracy,kappa,macro_f1,weighted_f1,val_nll,test_nll,val_brier,test_brier,test_ece_15bin,epochs_configured,epochs_ran,train_time_sec,best_model_path,final_model_path
|
| 2 |
-
GFNet,legacy_arch,0.9999257829894612,0.9999105961705329,0.9998136737701507,0.9999257451832994,0.007947924362124414,0.008106300741815049,0.00013942152948862152,0.00022818487861826534,0.007952897975472044,100,100,1130.2998793399997,/kaggle/working/models/GFNet_best.keras,/kaggle/working/models/GFNet_final.keras
|
| 3 |
-
ViT_UNet,legacy_arch,0.9993320469051507,0.9991954066321567,0.9990159625730481,0.9993325262204305,0.04282111958386762,0.04173323245097391,0.0031020371728503766,0.002701069443341945,0.03891381961762844,100,100,2168.4684350519997,/kaggle/working/models/ViT_UNet_best.keras,/kaggle/working/models/ViT_UNet_final.keras
|
| 4 |
-
AlexNet_CNN,legacy_arch,0.9992578298946119,0.999105915989467,0.9983233745536122,0.999255974726486,0.0025646521673203223,0.0025646521673203223,0.001327359502401775,0.001327359502401775,0.0004946586948412895,100,100,1132.121482828,/kaggle/working/models/AlexNet_CNN_best.keras,/kaggle/working/models/AlexNet_CNN_final.keras
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/scene_visualizations/AlexNet_CNN_initial_classification.png
DELETED
Git LFS Details
|
baseline/results/scene_visualizations/GFNet_initial_classification.png
DELETED
Git LFS Details
|
baseline/results/scene_visualizations/ViT_UNet_initial_classification.png
DELETED
Git LFS Details
|
baseline/results/scene_visualizations/combined_initial_classification_overview.png
DELETED
Git LFS Details
|
baseline/results/scene_visualizations/ground_truth_label_map.png
DELETED
Git LFS Details
|
baseline/results/scene_visualizations/initial_classification_maps.xlsx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:cc625a90b81f4bedd73daada1387bf53d113a18f75b0bbe2e7f370025e25b3fa
|
| 3 |
-
size 7448141
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/scene_visualizations/scene_rgb.png
DELETED
Git LFS Details
|
baseline/results/training_plots/AlexNet_CNN_training_curves.png
DELETED
Git LFS Details
|
baseline/results/training_plots/GFNet_training_curves.png
DELETED
Git LFS Details
|
baseline/results/training_plots/ViT_UNet_training_curves.png
DELETED
Git LFS Details
|
baseline/results/training_plots/confusion_matrices_side_by_side.png
DELETED
Git LFS Details
|
baseline/results/training_plots/model_comparison_metrics.png
DELETED
Git LFS Details
|
baseline/results/training_plots/uncertainty_proxy_metrics.png
DELETED
|
Binary file (71.9 kB)
|
|
|
baseline/results/uncertainty_results/conformal_reports_all_models.xlsx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:7eb1fdbbc6e271c23705080d3e11b04deb82fe68e6c3c031593db00cfd478857
|
| 3 |
-
size 16777908
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/uncertainty_results/per_class_coverage.csv
DELETED
|
@@ -1,121 +0,0 @@
|
|
| 1 |
-
class_id,class_coverage,support_count,model_name,method
|
| 2 |
-
0,1.0,1216,AlexNet,SplitConformal
|
| 3 |
-
1,1.0,704,AlexNet,SplitConformal
|
| 4 |
-
2,0.8869257950530035,283,AlexNet,SplitConformal
|
| 5 |
-
3,0.9641025641025641,1365,AlexNet,SplitConformal
|
| 6 |
-
4,0.9679245283018868,1590,AlexNet,SplitConformal
|
| 7 |
-
5,0.8895184135977338,1059,AlexNet,SplitConformal
|
| 8 |
-
6,0.77,200,AlexNet,SplitConformal
|
| 9 |
-
7,0.8875,320,AlexNet,SplitConformal
|
| 10 |
-
0,0.9481907894736842,1216,AlexNet,ClassConditionalConformal
|
| 11 |
-
1,0.9985795454545454,704,AlexNet,ClassConditionalConformal
|
| 12 |
-
2,0.9752650176678446,283,AlexNet,ClassConditionalConformal
|
| 13 |
-
3,0.9443223443223443,1365,AlexNet,ClassConditionalConformal
|
| 14 |
-
4,0.9452830188679245,1590,AlexNet,ClassConditionalConformal
|
| 15 |
-
5,0.9480642115203022,1059,AlexNet,ClassConditionalConformal
|
| 16 |
-
6,0.985,200,AlexNet,ClassConditionalConformal
|
| 17 |
-
7,0.959375,320,AlexNet,ClassConditionalConformal
|
| 18 |
-
0,0.9539473684210527,1216,AlexNet,RC3P
|
| 19 |
-
1,0.9985795454545454,704,AlexNet,RC3P
|
| 20 |
-
2,0.9752650176678446,283,AlexNet,RC3P
|
| 21 |
-
3,0.9509157509157509,1365,AlexNet,RC3P
|
| 22 |
-
4,0.9553459119496855,1590,AlexNet,RC3P
|
| 23 |
-
5,0.9546742209631728,1059,AlexNet,RC3P
|
| 24 |
-
6,0.99,200,AlexNet,RC3P
|
| 25 |
-
7,0.959375,320,AlexNet,RC3P
|
| 26 |
-
0,0.9481907894736842,1216,AlexNet,ClusteredConformal
|
| 27 |
-
1,0.9985795454545454,704,AlexNet,ClusteredConformal
|
| 28 |
-
2,0.9363957597173145,283,AlexNet,ClusteredConformal
|
| 29 |
-
3,0.9692307692307692,1365,AlexNet,ClusteredConformal
|
| 30 |
-
4,0.9716981132075472,1590,AlexNet,ClusteredConformal
|
| 31 |
-
5,0.9480642115203022,1059,AlexNet,ClusteredConformal
|
| 32 |
-
6,0.825,200,AlexNet,ClusteredConformal
|
| 33 |
-
7,0.909375,320,AlexNet,ClusteredConformal
|
| 34 |
-
0,1.0,1216,AlexNet,RAPS
|
| 35 |
-
1,1.0,704,AlexNet,RAPS
|
| 36 |
-
2,1.0,283,AlexNet,RAPS
|
| 37 |
-
3,1.0,1365,AlexNet,RAPS
|
| 38 |
-
4,1.0,1590,AlexNet,RAPS
|
| 39 |
-
5,1.0,1059,AlexNet,RAPS
|
| 40 |
-
6,1.0,200,AlexNet,RAPS
|
| 41 |
-
7,1.0,320,AlexNet,RAPS
|
| 42 |
-
0,0.9161184210526315,1216,GFNet,SplitConformal
|
| 43 |
-
1,0.9985795454545454,704,GFNet,SplitConformal
|
| 44 |
-
2,0.7526501766784452,283,GFNet,SplitConformal
|
| 45 |
-
3,0.9728937728937729,1365,GFNet,SplitConformal
|
| 46 |
-
4,0.9773584905660377,1590,GFNet,SplitConformal
|
| 47 |
-
5,0.9490084985835694,1059,GFNet,SplitConformal
|
| 48 |
-
6,0.93,200,GFNet,SplitConformal
|
| 49 |
-
7,0.940625,320,GFNet,SplitConformal
|
| 50 |
-
0,0.9449013157894737,1216,GFNet,ClassConditionalConformal
|
| 51 |
-
1,0.9588068181818182,704,GFNet,ClassConditionalConformal
|
| 52 |
-
2,0.9646643109540636,283,GFNet,ClassConditionalConformal
|
| 53 |
-
3,0.9443223443223443,1365,GFNet,ClassConditionalConformal
|
| 54 |
-
4,0.9534591194968554,1590,GFNet,ClassConditionalConformal
|
| 55 |
-
5,0.9716713881019831,1059,GFNet,ClassConditionalConformal
|
| 56 |
-
6,0.92,200,GFNet,ClassConditionalConformal
|
| 57 |
-
7,0.928125,320,GFNet,ClassConditionalConformal
|
| 58 |
-
0,0.946546052631579,1216,GFNet,RC3P
|
| 59 |
-
1,0.9630681818181818,704,GFNet,RC3P
|
| 60 |
-
2,0.9717314487632509,283,GFNet,RC3P
|
| 61 |
-
3,0.9457875457875458,1365,GFNet,RC3P
|
| 62 |
-
4,0.9572327044025157,1590,GFNet,RC3P
|
| 63 |
-
5,0.9716713881019831,1059,GFNet,RC3P
|
| 64 |
-
6,0.92,200,GFNet,RC3P
|
| 65 |
-
7,0.95625,320,GFNet,RC3P
|
| 66 |
-
0,0.9827302631578947,1216,GFNet,ClusteredConformal
|
| 67 |
-
1,0.9971590909090909,704,GFNet,ClusteredConformal
|
| 68 |
-
2,0.8127208480565371,283,GFNet,ClusteredConformal
|
| 69 |
-
3,0.9194139194139194,1365,GFNet,ClusteredConformal
|
| 70 |
-
4,0.9572327044025157,1590,GFNet,ClusteredConformal
|
| 71 |
-
5,0.9707271010387157,1059,GFNet,ClusteredConformal
|
| 72 |
-
6,0.94,200,GFNet,ClusteredConformal
|
| 73 |
-
7,0.928125,320,GFNet,ClusteredConformal
|
| 74 |
-
0,1.0,1216,GFNet,RAPS
|
| 75 |
-
1,1.0,704,GFNet,RAPS
|
| 76 |
-
2,1.0,283,GFNet,RAPS
|
| 77 |
-
3,1.0,1365,GFNet,RAPS
|
| 78 |
-
4,1.0,1590,GFNet,RAPS
|
| 79 |
-
5,1.0,1059,GFNet,RAPS
|
| 80 |
-
6,0.995,200,GFNet,RAPS
|
| 81 |
-
7,1.0,320,GFNet,RAPS
|
| 82 |
-
0,0.9983552631578947,1216,ViT,SplitConformal
|
| 83 |
-
1,1.0,704,ViT,SplitConformal
|
| 84 |
-
2,0.9293286219081273,283,ViT,SplitConformal
|
| 85 |
-
3,0.9194139194139194,1365,ViT,SplitConformal
|
| 86 |
-
4,0.9534591194968554,1590,ViT,SplitConformal
|
| 87 |
-
5,0.939565627950897,1059,ViT,SplitConformal
|
| 88 |
-
6,0.765,200,ViT,SplitConformal
|
| 89 |
-
7,0.890625,320,ViT,SplitConformal
|
| 90 |
-
0,0.9605263157894737,1216,ViT,ClassConditionalConformal
|
| 91 |
-
1,0.9801136363636364,704,ViT,ClassConditionalConformal
|
| 92 |
-
2,0.9328621908127208,283,ViT,ClassConditionalConformal
|
| 93 |
-
3,0.9340659340659341,1365,ViT,ClassConditionalConformal
|
| 94 |
-
4,0.9421383647798742,1590,ViT,ClassConditionalConformal
|
| 95 |
-
5,0.95561850802644,1059,ViT,ClassConditionalConformal
|
| 96 |
-
6,0.94,200,ViT,ClassConditionalConformal
|
| 97 |
-
7,0.946875,320,ViT,ClassConditionalConformal
|
| 98 |
-
0,0.962171052631579,1216,ViT,RC3P
|
| 99 |
-
1,0.9801136363636364,704,ViT,RC3P
|
| 100 |
-
2,0.9469964664310954,283,ViT,RC3P
|
| 101 |
-
3,0.9355311355311355,1365,ViT,RC3P
|
| 102 |
-
4,0.9477987421383648,1590,ViT,RC3P
|
| 103 |
-
5,0.9612842304060434,1059,ViT,RC3P
|
| 104 |
-
6,0.94,200,ViT,RC3P
|
| 105 |
-
7,0.95625,320,ViT,RC3P
|
| 106 |
-
0,0.9975328947368421,1216,ViT,ClusteredConformal
|
| 107 |
-
1,1.0,704,ViT,ClusteredConformal
|
| 108 |
-
2,0.9328621908127208,283,ViT,ClusteredConformal
|
| 109 |
-
3,0.8974358974358975,1365,ViT,ClusteredConformal
|
| 110 |
-
4,0.9440251572327044,1590,ViT,ClusteredConformal
|
| 111 |
-
5,0.9178470254957507,1059,ViT,ClusteredConformal
|
| 112 |
-
6,0.94,200,ViT,ClusteredConformal
|
| 113 |
-
7,0.946875,320,ViT,ClusteredConformal
|
| 114 |
-
0,0.9991776315789473,1216,ViT,RAPS
|
| 115 |
-
1,1.0,704,ViT,RAPS
|
| 116 |
-
2,1.0,283,ViT,RAPS
|
| 117 |
-
3,0.9970695970695971,1365,ViT,RAPS
|
| 118 |
-
4,1.0,1590,ViT,RAPS
|
| 119 |
-
5,1.0,1059,ViT,RAPS
|
| 120 |
-
6,1.0,200,ViT,RAPS
|
| 121 |
-
7,1.0,320,ViT,RAPS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/uncertainty_results/run_config.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"seed": 42,
|
| 3 |
-
"data_file": "/kaggle/input/datasets/daneshselwal/dias-dataset-k01/k001.mat",
|
| 4 |
-
"label_file": "/kaggle/input/datasets/daneshselwal/dias-dataset-k01/k002.mat",
|
| 5 |
-
"model_dir": "/kaggle/input/datasets/daneshselwal/dias-baseline-models",
|
| 6 |
-
"output_dir": "/kaggle/working/results/uncertainty_results",
|
| 7 |
-
"results_dir": "/kaggle/working/results/uncertainty_results",
|
| 8 |
-
"h": 512,
|
| 9 |
-
"w": 512,
|
| 10 |
-
"b": 65,
|
| 11 |
-
"patch_size": 9,
|
| 12 |
-
"train_percent": 0.75,
|
| 13 |
-
"calib_fraction_of_test": 0.5,
|
| 14 |
-
"alpha": 0.05,
|
| 15 |
-
"raps_lam": 0.01,
|
| 16 |
-
"raps_k_reg": 1,
|
| 17 |
-
"n_clusters": 4,
|
| 18 |
-
"batch_size": 128,
|
| 19 |
-
"timestamp_utc": "2026-09-10T01:08:25.151909+00:00"
|
| 20 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/results/uncertainty_results/summary_metrics.csv
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
model_name,method,target_coverage,empirical_coverage,avg_set_size,median_set_size,singleton_rate,empty_set_rate,runtime_sec,alpha,lam,n_clusters,mean_per_class_coverage
|
| 2 |
-
AlexNet,ClassConditionalConformal,0.95,0.9547276235713226,0.9547276235713226,1.0,0.9547276235713226,0.04527237642867745,2.292357693999975,0.05,,,0.9630099909133306
|
| 3 |
-
GFNet,ClassConditionalConformal,0.95,0.9517589431497699,0.9517589431497699,1.0,0.9517589431497699,0.04824105685023007,2.339258706999999,0.05,,,0.9482437871058172
|
| 4 |
-
ViT,ClassConditionalConformal,0.95,0.9496808668546831,0.9496808668546831,1.0,0.9496808668546831,0.050319133145316906,2.224427326999944,0.05,,,0.9490249937297599
|
| 5 |
-
AlexNet,ClusteredConformal,0.95,0.9572510019296423,0.9572510019296423,1.0,0.9572510019296423,0.04274899807035772,3.0594818560000476,0.05,,4.0,0.9383167735755203
|
| 6 |
-
GFNet,ClusteredConformal,0.95,0.9525011132551581,0.9525011132551581,1.0,0.9525011132551581,0.047498886744841914,5.474895679999918,0.05,,4.0,0.9385136158723342
|
| 7 |
-
ViT,ClusteredConformal,0.95,0.9455247142645095,0.9455247142645095,1.0,0.9455247142645095,0.054475285735490576,10.67419527800007,0.05,,4.0,0.9470722707142394
|
| 8 |
-
AlexNet,RAPS,0.95,1.0,1.0,1.0,1.0,0.0,0.4984293269999398,0.05,0.01,,1.0
|
| 9 |
-
GFNet,RAPS,0.95,0.9998515659789223,1.0,1.0,1.0,0.0,0.4831613420000167,0.05,0.01,,0.999375
|
| 10 |
-
ViT,RAPS,0.95,0.9992578298946119,1.0,1.0,1.0,0.0,0.46974008299991965,0.05,0.01,,0.999530903581068
|
| 11 |
-
AlexNet,RC3P,0.95,0.9606649844144278,0.9606649844144278,1.0,0.9606649844144278,0.03933501558557221,2.347965410000029,0.05,,,0.9672628519215065
|
| 12 |
-
GFNet,RC3P,0.95,0.955321359655633,0.955321359655633,1.0,0.955321359655633,0.04467864034436693,2.365494427000044,0.05,,,0.9540359151881321
|
| 13 |
-
ViT,RC3P,0.95,0.9535401514027015,0.9535401514027015,1.0,0.9535401514027015,0.0464598485972985,2.2882030419999637,0.05,,,0.9537681579377318
|
| 14 |
-
AlexNet,SplitConformal,0.95,0.9508683390233041,0.9508683390233041,1.0,0.9508683390233041,0.04913166097669586,2.4034767729999658,0.05,,,0.9207464126318985
|
| 15 |
-
GFNet,SplitConformal,0.95,0.9505714709811489,0.9505714709811489,1.0,0.9505714709811489,0.04942852901885112,2.2923555589999296,0.05,,,0.9296542381536252
|
| 16 |
-
ViT,SplitConformal,0.95,0.9477512245806738,0.9477512245806738,1.0,0.9477512245806738,0.05224877541932611,2.25247833800006,0.05,,,0.9244684439909617
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/test_m2_challenger.py
DELETED
|
@@ -1,261 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Milestone 2 Empirical Challenge Harness
|
| 3 |
-
Executed by teamwork_preview_challenger_m2_2
|
| 4 |
-
"""
|
| 5 |
-
import os
|
| 6 |
-
import sys
|
| 7 |
-
import json
|
| 8 |
-
import ast
|
| 9 |
-
import hashlib
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
|
| 12 |
-
REPO_ROOT = Path('/Users/danesh/Documents/AGY/agy/classification')
|
| 13 |
-
ORIGINAL_NB_PATH = REPO_ROOT / 'baseline' / 'Model_uncertainty_comparison.ipynb'
|
| 14 |
-
STAGED_NB_PATH = REPO_ROOT / 'baseline' / 'staging_uncertainty' / 'k004.ipynb'
|
| 15 |
-
BASELINE_NB_PATH = REPO_ROOT / 'baseline' / 'k004.ipynb'
|
| 16 |
-
KERNEL_META_PATH = REPO_ROOT / 'baseline' / 'staging_uncertainty' / 'kernel-metadata.json'
|
| 17 |
-
MAPPING_PATH = REPO_ROOT / 'kaggle_name_mapping.json'
|
| 18 |
-
|
| 19 |
-
results = {}
|
| 20 |
-
|
| 21 |
-
def log_test(name, passed, details=None):
|
| 22 |
-
results[name] = {"passed": passed, "details": details}
|
| 23 |
-
status = "PASS" if passed else "FAIL"
|
| 24 |
-
print(f"[{status}] {name}")
|
| 25 |
-
if details:
|
| 26 |
-
print(f" Details: {details}")
|
| 27 |
-
|
| 28 |
-
def compute_sha256(filepath):
|
| 29 |
-
h = hashlib.sha256()
|
| 30 |
-
with open(filepath, 'rb') as f:
|
| 31 |
-
while chunk := f.read(8192):
|
| 32 |
-
h.update(chunk)
|
| 33 |
-
return h.hexdigest()
|
| 34 |
-
|
| 35 |
-
def test_files_exist_and_hashes():
|
| 36 |
-
assert STAGED_NB_PATH.exists(), f"Missing {STAGED_NB_PATH}"
|
| 37 |
-
assert BASELINE_NB_PATH.exists(), f"Missing {BASELINE_NB_PATH}"
|
| 38 |
-
assert KERNEL_META_PATH.exists(), f"Missing {KERNEL_META_PATH}"
|
| 39 |
-
assert MAPPING_PATH.exists(), f"Missing {MAPPING_PATH}"
|
| 40 |
-
assert ORIGINAL_NB_PATH.exists(), f"Missing {ORIGINAL_NB_PATH}"
|
| 41 |
-
|
| 42 |
-
staged_hash = compute_sha256(STAGED_NB_PATH)
|
| 43 |
-
baseline_hash = compute_sha256(BASELINE_NB_PATH)
|
| 44 |
-
|
| 45 |
-
staged_size = STAGED_NB_PATH.stat().st_size
|
| 46 |
-
baseline_size = BASELINE_NB_PATH.stat().st_size
|
| 47 |
-
|
| 48 |
-
is_identical = (staged_hash == baseline_hash and staged_size == baseline_size)
|
| 49 |
-
log_test("Staged and Baseline k004.ipynb Byte Parity", is_identical,
|
| 50 |
-
f"staged_sha={staged_hash[:12]}..., baseline_sha={baseline_hash[:12]}..., size={staged_size}")
|
| 51 |
-
return staged_hash, staged_size
|
| 52 |
-
|
| 53 |
-
def test_notebook_json_and_cell_count():
|
| 54 |
-
with open(ORIGINAL_NB_PATH, 'r', encoding='utf-8') as f:
|
| 55 |
-
orig_nb = json.load(f)
|
| 56 |
-
with open(STAGED_NB_PATH, 'r', encoding='utf-8') as f:
|
| 57 |
-
staged_nb = json.load(f)
|
| 58 |
-
|
| 59 |
-
orig_cells = orig_nb.get('cells', [])
|
| 60 |
-
staged_cells = staged_nb.get('cells', [])
|
| 61 |
-
|
| 62 |
-
count_match = (len(orig_cells) == 44 and len(staged_cells) == 44)
|
| 63 |
-
log_test("Notebook Cell Count Matches Expected (44)", count_match,
|
| 64 |
-
f"Original: {len(orig_cells)}, Staged: {len(staged_cells)}")
|
| 65 |
-
|
| 66 |
-
orig_types = [c.get('cell_type') for c in orig_cells]
|
| 67 |
-
staged_types = [c.get('cell_type') for c in staged_cells]
|
| 68 |
-
types_match = (orig_types == staged_types)
|
| 69 |
-
|
| 70 |
-
md_count = staged_types.count('markdown')
|
| 71 |
-
code_count = staged_types.count('code')
|
| 72 |
-
log_test("Cell Types Parity (22 markdown, 22 code)", types_match and md_count == 22 and code_count == 22,
|
| 73 |
-
f"Markdown: {md_count}, Code: {code_count}")
|
| 74 |
-
|
| 75 |
-
return orig_nb, staged_nb
|
| 76 |
-
|
| 77 |
-
def test_cell_diff_strictness(orig_nb, staged_nb):
|
| 78 |
-
orig_cells = orig_nb['cells']
|
| 79 |
-
staged_cells = staged_nb['cells']
|
| 80 |
-
|
| 81 |
-
modified_cells = []
|
| 82 |
-
modified_code_cells = []
|
| 83 |
-
modified_md_cells = []
|
| 84 |
-
for idx, (orig_c, staged_c) in enumerate(zip(orig_cells, staged_cells)):
|
| 85 |
-
orig_src = "".join(orig_c.get('source', []))
|
| 86 |
-
staged_src = "".join(staged_c.get('source', []))
|
| 87 |
-
if orig_src != staged_src:
|
| 88 |
-
modified_cells.append(idx)
|
| 89 |
-
if orig_c.get('cell_type') == 'code':
|
| 90 |
-
modified_code_cells.append(idx)
|
| 91 |
-
else:
|
| 92 |
-
modified_md_cells.append(idx)
|
| 93 |
-
|
| 94 |
-
# Strict check across all cells
|
| 95 |
-
strictly_only_1_4_6 = (modified_cells == [1, 4, 6])
|
| 96 |
-
code_only_1_4_6 = (modified_code_cells == [1, 4, 6])
|
| 97 |
-
|
| 98 |
-
log_test("Modified Cells - Code Cells Strictly 1, 4, 6", code_only_1_4_6,
|
| 99 |
-
f"Modified code cells: {modified_code_cells}")
|
| 100 |
-
log_test("Modified Cells - All Cells Strictly 1, 4, 6 (Empirical Challenge)", strictly_only_1_4_6,
|
| 101 |
-
f"All modified cells: {modified_cells} (Markdown Cell 0 was modified: {modified_md_cells})")
|
| 102 |
-
|
| 103 |
-
# Check that remaining cells have identical source content
|
| 104 |
-
remaining_identical = True
|
| 105 |
-
non_matching = []
|
| 106 |
-
for idx in range(44):
|
| 107 |
-
if idx not in modified_cells:
|
| 108 |
-
o_src = "".join(orig_cells[idx].get('source', []))
|
| 109 |
-
s_src = "".join(staged_cells[idx].get('source', []))
|
| 110 |
-
if o_src != s_src:
|
| 111 |
-
remaining_identical = False
|
| 112 |
-
non_matching.append(idx)
|
| 113 |
-
log_test("All 40 Unmodified Cells Are 100% Identical", remaining_identical,
|
| 114 |
-
f"Non-matching cells: {non_matching}")
|
| 115 |
-
|
| 116 |
-
return modified_cells
|
| 117 |
-
|
| 118 |
-
def test_execution_hygiene(staged_nb):
|
| 119 |
-
cells = staged_nb['cells']
|
| 120 |
-
pollution = []
|
| 121 |
-
for idx, cell in enumerate(cells):
|
| 122 |
-
if cell.get('cell_type') == 'code':
|
| 123 |
-
ec = cell.get('execution_count')
|
| 124 |
-
outputs = cell.get('outputs', [])
|
| 125 |
-
if ec is not None:
|
| 126 |
-
pollution.append(f"Cell {idx} execution_count={ec}")
|
| 127 |
-
if len(outputs) > 0:
|
| 128 |
-
pollution.append(f"Cell {idx} has {len(outputs)} outputs")
|
| 129 |
-
meta = cell.get('metadata', {})
|
| 130 |
-
if 'execution' in meta:
|
| 131 |
-
pollution.append(f"Cell {idx} metadata has 'execution'")
|
| 132 |
-
if 'scrolled' in meta or 'collapsed' in meta:
|
| 133 |
-
pollution.append(f"Cell {idx} metadata has scroll/collapsed flag")
|
| 134 |
-
|
| 135 |
-
is_clean = (len(pollution) == 0)
|
| 136 |
-
log_test("Execution Outputs Cleared and Execution Counts Null", is_clean,
|
| 137 |
-
pollution if pollution else "All 22 code cells clean")
|
| 138 |
-
|
| 139 |
-
def test_ast_validity(staged_nb):
|
| 140 |
-
cells = staged_nb['cells']
|
| 141 |
-
syntax_errors = []
|
| 142 |
-
for idx, cell in enumerate(cells):
|
| 143 |
-
if cell.get('cell_type') == 'code':
|
| 144 |
-
src = "".join(cell.get('source', []))
|
| 145 |
-
try:
|
| 146 |
-
ast.parse(src)
|
| 147 |
-
except SyntaxError as e:
|
| 148 |
-
syntax_errors.append(f"Cell {idx} SyntaxError: {e}")
|
| 149 |
-
|
| 150 |
-
no_errors = (len(syntax_errors) == 0)
|
| 151 |
-
log_test("All 22 Code Cells Pass AST Syntax Parsing", no_errors,
|
| 152 |
-
syntax_errors if syntax_errors else "All 22 code cells valid Python AST")
|
| 153 |
-
|
| 154 |
-
def test_modified_cells_semantics(staged_nb):
|
| 155 |
-
cells = staged_nb['cells']
|
| 156 |
-
c1_src = "".join(cells[1].get('source', []))
|
| 157 |
-
c4_src = "".join(cells[4].get('source', []))
|
| 158 |
-
c6_src = "".join(cells[6].get('source', []))
|
| 159 |
-
|
| 160 |
-
# Cell 1 checks
|
| 161 |
-
c1_colab_removed = "google.colab" not in c1_src and "drive.mount" not in c1_src
|
| 162 |
-
c1_packages = "xlsxwriter" in c1_src and "openpyxl" in c1_src
|
| 163 |
-
log_test("Cell 1: Drive Mount Removed & Excel Packages Handled", c1_colab_removed and c1_packages,
|
| 164 |
-
f"Drive mount absent: {c1_colab_removed}, xlsxwriter/openpyxl present: {c1_packages}")
|
| 165 |
-
|
| 166 |
-
# Cell 4 checks
|
| 167 |
-
c4_kaggle_detection = "kaggle" in c4_src or "KAGGLE" in c4_src or "/kaggle/input" in c4_src
|
| 168 |
-
c4_data_resolution = "k001.mat" in c4_src and "k002.mat" in c4_src
|
| 169 |
-
c4_model_resolution = "k005.keras" in c4_src and "k006.keras" in c4_src and "k007.keras" in c4_src
|
| 170 |
-
c4_keys_intact = "'AlexNet_CNN':" in c4_src and "'GFNet':" in c4_src and "'ViT_UNet':" in c4_src
|
| 171 |
-
c4_trusted_roots = "/kaggle/input" in c4_src and "/kaggle/working" in c4_src
|
| 172 |
-
c4_results_dir = "/kaggle/working/results" in c4_src
|
| 173 |
-
|
| 174 |
-
# Read-only guard check: verify MODEL_DIR.mkdir() is NOT called or is safely guarded against /kaggle/input
|
| 175 |
-
c4_readonly_safe = ("MODEL_DIR.mkdir" not in c4_src) or ("if not str(MODEL_DIR).startswith('/kaggle/input')" in c4_src)
|
| 176 |
-
log_test("Cell 4: Dynamic Paths, Model Keys, Trusted Roots, Readonly Safety",
|
| 177 |
-
c4_kaggle_detection and c4_data_resolution and c4_model_resolution and c4_keys_intact and c4_trusted_roots and c4_results_dir and c4_readonly_safe,
|
| 178 |
-
f"Keys intact: {c4_keys_intact}, Trusted roots: {c4_trusted_roots}, Readonly safe: {c4_readonly_safe}")
|
| 179 |
-
|
| 180 |
-
# Cell 6 checks
|
| 181 |
-
c6_kaggle_guard = "Path('/kaggle').exists()" in c6_src or "/kaggle" in c6_src
|
| 182 |
-
log_test("Cell 6: Environment Guard Accepts /kaggle", c6_kaggle_guard,
|
| 183 |
-
f"Guard check for /kaggle present: {c6_kaggle_guard}")
|
| 184 |
-
|
| 185 |
-
def test_kernel_metadata():
|
| 186 |
-
with open(KERNEL_META_PATH, 'r', encoding='utf-8') as f:
|
| 187 |
-
meta = json.load(f)
|
| 188 |
-
|
| 189 |
-
id_ok = meta.get('id') == "daneshselwal/dias-uncertainty-k04"
|
| 190 |
-
code_file_ok = meta.get('code_file') == "k004.ipynb"
|
| 191 |
-
gpu_ok = str(meta.get('enable_gpu')).lower() == "true"
|
| 192 |
-
sources = meta.get('dataset_sources', [])
|
| 193 |
-
expected_sources = ["daneshselwal/dias-dataset-k01", "daneshselwal/dias-baseline-models"]
|
| 194 |
-
sources_ok = sources == expected_sources
|
| 195 |
-
kernel_type_ok = meta.get('kernel_type') == "notebook"
|
| 196 |
-
|
| 197 |
-
meta_ok = id_ok and code_file_ok and gpu_ok and sources_ok and kernel_type_ok
|
| 198 |
-
log_test("kernel-metadata.json Contract Compliance", meta_ok,
|
| 199 |
-
f"id={meta.get('id')}, code_file={meta.get('code_file')}, gpu={meta.get('enable_gpu')}, datasets={sources}")
|
| 200 |
-
|
| 201 |
-
def test_mapping_file(staged_hash, staged_size):
|
| 202 |
-
with open(MAPPING_PATH, 'r', encoding='utf-8') as f:
|
| 203 |
-
mapping = json.load(f)
|
| 204 |
-
|
| 205 |
-
# Check k004 in obfuscated_to_original
|
| 206 |
-
obf_to_orig = mapping.get('obfuscated_to_original', {})
|
| 207 |
-
orig_to_obf = mapping.get('original_to_obfuscated', {})
|
| 208 |
-
|
| 209 |
-
has_staged_obf = ("baseline/staging_uncertainty/k004.ipynb" in obf_to_orig and
|
| 210 |
-
obf_to_orig["baseline/staging_uncertainty/k004.ipynb"] == "baseline/Model_uncertainty_comparison.ipynb")
|
| 211 |
-
has_orig_to_obf = ("baseline/Model_uncertainty_comparison.ipynb" in orig_to_obf and
|
| 212 |
-
orig_to_obf["baseline/Model_uncertainty_comparison.ipynb"] in ["baseline/k004.ipynb", "baseline/staging_uncertainty/k004.ipynb"])
|
| 213 |
-
|
| 214 |
-
files = mapping.get('files', [])
|
| 215 |
-
k004_entry = next((f for f in files if f.get('code') == 'k004'), None)
|
| 216 |
-
|
| 217 |
-
entry_valid = False
|
| 218 |
-
if k004_entry:
|
| 219 |
-
entry_hash = k004_entry.get('sha256')
|
| 220 |
-
entry_size = k004_entry.get('size_bytes')
|
| 221 |
-
entry_staging = k004_entry.get('staging_relative_path')
|
| 222 |
-
entry_valid = (entry_hash == staged_hash and entry_size == staged_size and entry_staging == "baseline/staging_uncertainty/k004.ipynb")
|
| 223 |
-
|
| 224 |
-
mapping_ok = has_staged_obf and has_orig_to_obf and entry_valid
|
| 225 |
-
log_test("kaggle_name_mapping.json Bidirectional & Hash Validation", mapping_ok,
|
| 226 |
-
f"k004 entry found: {k004_entry is not None}, Hash match: {entry_valid}, File hash: {staged_hash}")
|
| 227 |
-
|
| 228 |
-
def test_staging_directory_cleanliness():
|
| 229 |
-
staging_dir = REPO_ROOT / 'baseline' / 'staging_uncertainty'
|
| 230 |
-
files = list(staging_dir.iterdir())
|
| 231 |
-
filenames = sorted([f.name for f in files])
|
| 232 |
-
expected_filenames = ["k004.ipynb", "kernel-metadata.json"]
|
| 233 |
-
clean = filenames == expected_filenames
|
| 234 |
-
log_test("staging_uncertainty Directory Cleanliness (Exact File Match)", clean,
|
| 235 |
-
f"Found files: {filenames}, Expected: {expected_filenames}")
|
| 236 |
-
|
| 237 |
-
def main():
|
| 238 |
-
print("=== STARTING CHALLENGER M2 INDEPENDENT TEST HARNESS ===")
|
| 239 |
-
staged_hash, staged_size = test_files_exist_and_hashes()
|
| 240 |
-
orig_nb, staged_nb = test_notebook_json_and_cell_count()
|
| 241 |
-
test_cell_diff_strictness(orig_nb, staged_nb)
|
| 242 |
-
test_execution_hygiene(staged_nb)
|
| 243 |
-
test_ast_validity(staged_nb)
|
| 244 |
-
test_modified_cells_semantics(staged_nb)
|
| 245 |
-
test_kernel_metadata()
|
| 246 |
-
test_mapping_file(staged_hash, staged_size)
|
| 247 |
-
test_staging_directory_cleanliness()
|
| 248 |
-
|
| 249 |
-
all_passed = all(t['passed'] for t in results.values())
|
| 250 |
-
print("\n=== SUMMARY OF TESTS ===")
|
| 251 |
-
print(f"Total tests: {len(results)}, Passed: {sum(1 for t in results.values() if t['passed'])}, Failed: {sum(1 for t in results.values() if not t['passed'])}")
|
| 252 |
-
if not all_passed:
|
| 253 |
-
print("FAILED TESTS:")
|
| 254 |
-
for name, res in results.items():
|
| 255 |
-
if not res['passed']:
|
| 256 |
-
print(f" - {name}: {res['details']}")
|
| 257 |
-
else:
|
| 258 |
-
print("ALL TESTS PASSED EMPIRICALLY.")
|
| 259 |
-
|
| 260 |
-
if __name__ == '__main__':
|
| 261 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
baseline/verify_m1_dataset.py
DELETED
|
@@ -1,375 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Empirical verification and stress-testing script for Milestone 1:
|
| 4 |
-
Baseline Models Upload & Verification.
|
| 5 |
-
|
| 6 |
-
Performs:
|
| 7 |
-
1. Local Source vs Staging Byte Parity & SHA-256 Checksum Matching
|
| 8 |
-
2. Keras 3 Model Archive (ZIP) Structure Integrity Verification
|
| 9 |
-
3. Kaggle Name Mapping JSON Cross-Validation
|
| 10 |
-
4. Staging Dataset Metadata & Permission Audit
|
| 11 |
-
5. Remote Kaggle API / CLI Query & Size Verification
|
| 12 |
-
6. Kaggle Input Mounting & Edge Case Simulation
|
| 13 |
-
"""
|
| 14 |
-
|
| 15 |
-
import sys
|
| 16 |
-
import os
|
| 17 |
-
import json
|
| 18 |
-
import hashlib
|
| 19 |
-
import zipfile
|
| 20 |
-
import subprocess
|
| 21 |
-
import stat
|
| 22 |
-
|
| 23 |
-
BASE_DIR = "/Users/danesh/Documents/AGY/agy/classification"
|
| 24 |
-
STAGING_DIR = os.path.join(BASE_DIR, "baseline/staging_models")
|
| 25 |
-
SOURCE_DIR = os.path.join(BASE_DIR, "baseline/results/models")
|
| 26 |
-
MAPPING_FILE = os.path.join(BASE_DIR, "kaggle_name_mapping.json")
|
| 27 |
-
METADATA_FILE = os.path.join(STAGING_DIR, "dataset-metadata.json")
|
| 28 |
-
DATASET_SLUG = "daneshselwal/dias-baseline-models"
|
| 29 |
-
|
| 30 |
-
EXPECTED_FILES = {
|
| 31 |
-
"k005.keras": {
|
| 32 |
-
"source": "AlexNet_CNN_best.keras",
|
| 33 |
-
"expected_size": 272550549,
|
| 34 |
-
"mapping_code": "k005"
|
| 35 |
-
},
|
| 36 |
-
"k006.keras": {
|
| 37 |
-
"source": "GFNet_best.keras",
|
| 38 |
-
"expected_size": 130589582,
|
| 39 |
-
"mapping_code": "k006"
|
| 40 |
-
},
|
| 41 |
-
"k007.keras": {
|
| 42 |
-
"source": "ViT_UNet_best.keras",
|
| 43 |
-
"expected_size": 195722847,
|
| 44 |
-
"mapping_code": "k007"
|
| 45 |
-
}
|
| 46 |
-
}
|
| 47 |
-
|
| 48 |
-
def sha256sum(filepath):
|
| 49 |
-
h = hashlib.sha256()
|
| 50 |
-
with open(filepath, "rb") as f:
|
| 51 |
-
while chunk := f.read(1024 * 1024):
|
| 52 |
-
h.update(chunk)
|
| 53 |
-
return h.hexdigest()
|
| 54 |
-
|
| 55 |
-
def test_local_parity_and_integrity():
|
| 56 |
-
print("=== TEST 1: Local Source vs Staging Byte Parity & SHA-256 ===")
|
| 57 |
-
results = {}
|
| 58 |
-
all_passed = True
|
| 59 |
-
|
| 60 |
-
for staged_name, meta in EXPECTED_FILES.items():
|
| 61 |
-
staged_path = os.path.join(STAGING_DIR, staged_name)
|
| 62 |
-
source_path = os.path.join(SOURCE_DIR, meta["source"])
|
| 63 |
-
|
| 64 |
-
staged_exists = os.path.exists(staged_path)
|
| 65 |
-
source_exists = os.path.exists(source_path)
|
| 66 |
-
print(f"Checking {staged_name} (source: {meta['source']}):")
|
| 67 |
-
print(f" Staged file exists: {staged_exists} ({staged_path})")
|
| 68 |
-
print(f" Source file exists: {source_exists} ({source_path})")
|
| 69 |
-
|
| 70 |
-
if not staged_exists or not source_exists:
|
| 71 |
-
print(f" FAIL: File missing!")
|
| 72 |
-
all_passed = False
|
| 73 |
-
continue
|
| 74 |
-
|
| 75 |
-
staged_size = os.path.getsize(staged_path)
|
| 76 |
-
source_size = os.path.getsize(source_path)
|
| 77 |
-
expected_size = meta["expected_size"]
|
| 78 |
-
|
| 79 |
-
print(f" Staged size: {staged_size} bytes")
|
| 80 |
-
print(f" Source size: {source_size} bytes")
|
| 81 |
-
print(f" Expected size: {expected_size} bytes")
|
| 82 |
-
|
| 83 |
-
if staged_size != expected_size or source_size != expected_size:
|
| 84 |
-
print(f" FAIL: Size mismatch!")
|
| 85 |
-
all_passed = False
|
| 86 |
-
else:
|
| 87 |
-
print(f" PASS: Size match exact ({staged_size} bytes)")
|
| 88 |
-
|
| 89 |
-
staged_hash = sha256sum(staged_path)
|
| 90 |
-
source_hash = sha256sum(source_path)
|
| 91 |
-
print(f" Staged SHA-256: {staged_hash}")
|
| 92 |
-
print(f" Source SHA-256: {source_hash}")
|
| 93 |
-
|
| 94 |
-
if staged_hash != source_hash:
|
| 95 |
-
print(f" FAIL: SHA-256 mismatch between source and staged!")
|
| 96 |
-
all_passed = False
|
| 97 |
-
else:
|
| 98 |
-
print(f" PASS: Byte-for-byte SHA-256 match")
|
| 99 |
-
|
| 100 |
-
final_name = meta["source"].replace("_best.keras", "_final.keras")
|
| 101 |
-
final_path = os.path.join(SOURCE_DIR, final_name)
|
| 102 |
-
if os.path.exists(final_path):
|
| 103 |
-
final_size = os.path.getsize(final_path)
|
| 104 |
-
final_hash = sha256sum(final_path)
|
| 105 |
-
if final_hash == staged_hash and meta["source"] != final_name:
|
| 106 |
-
print(f" NOTE: Final model has identical hash to best model: {final_name}")
|
| 107 |
-
else:
|
| 108 |
-
print(f" VERIFIED: Staged file matches '{meta['source']}' specifically (distinct from {final_name})")
|
| 109 |
-
|
| 110 |
-
results[staged_name] = {
|
| 111 |
-
"size": staged_size,
|
| 112 |
-
"sha256": staged_hash,
|
| 113 |
-
"match": (staged_hash == source_hash) and (staged_size == expected_size)
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
return all_passed, results
|
| 117 |
-
|
| 118 |
-
def test_keras_archive_integrity():
|
| 119 |
-
print("\n=== TEST 2: Keras 3 ZIP Archive Structure Integrity ===")
|
| 120 |
-
all_passed = True
|
| 121 |
-
for staged_name in EXPECTED_FILES:
|
| 122 |
-
staged_path = os.path.join(STAGING_DIR, staged_name)
|
| 123 |
-
print(f"Testing ZIP integrity of {staged_name}...")
|
| 124 |
-
try:
|
| 125 |
-
with zipfile.ZipFile(staged_path, 'r') as zf:
|
| 126 |
-
bad_file = zf.testzip()
|
| 127 |
-
if bad_file is not None:
|
| 128 |
-
print(f" FAIL: Corrupt archive entry: {bad_file}")
|
| 129 |
-
all_passed = False
|
| 130 |
-
else:
|
| 131 |
-
namelist = zf.namelist()
|
| 132 |
-
print(f" PASS: Valid ZIP archive. Entries ({len(namelist)}): {namelist[:5]}...")
|
| 133 |
-
has_config = any("config" in name or "metadata" in name for name in namelist)
|
| 134 |
-
print(f" Keras metadata detected in archive: {has_config}")
|
| 135 |
-
except Exception as e:
|
| 136 |
-
print(f" FAIL: Exception reading ZIP {staged_name}: {e}")
|
| 137 |
-
all_passed = False
|
| 138 |
-
return all_passed
|
| 139 |
-
|
| 140 |
-
def test_mapping_json_consistency(local_hashes):
|
| 141 |
-
print("\n=== TEST 3: Cross-Validation with kaggle_name_mapping.json ===")
|
| 142 |
-
all_passed = True
|
| 143 |
-
if not os.path.exists(MAPPING_FILE):
|
| 144 |
-
print(f"FAIL: Mapping file does not exist: {MAPPING_FILE}")
|
| 145 |
-
return False
|
| 146 |
-
|
| 147 |
-
with open(MAPPING_FILE, 'r') as f:
|
| 148 |
-
mapping = json.load(f)
|
| 149 |
-
|
| 150 |
-
obf_to_orig = mapping.get("obfuscated_to_original", {})
|
| 151 |
-
orig_to_obf = mapping.get("original_to_obfuscated", {})
|
| 152 |
-
files_list = {item["code"]: item for item in mapping.get("files", []) if "code" in item}
|
| 153 |
-
|
| 154 |
-
for staged_name, meta in EXPECTED_FILES.items():
|
| 155 |
-
code = meta["mapping_code"]
|
| 156 |
-
print(f"Validating mapping for code '{code}' ({staged_name}):")
|
| 157 |
-
|
| 158 |
-
if code not in files_list:
|
| 159 |
-
print(f" FAIL: Code '{code}' missing from 'files' array in mapping!")
|
| 160 |
-
all_passed = False
|
| 161 |
-
continue
|
| 162 |
-
|
| 163 |
-
file_entry = files_list[code]
|
| 164 |
-
mapped_size = file_entry.get("size_bytes")
|
| 165 |
-
mapped_sha256 = file_entry.get("sha256")
|
| 166 |
-
local_hash = local_hashes.get(staged_name, {}).get("sha256")
|
| 167 |
-
|
| 168 |
-
print(f" Mapping recorded size: {mapped_size}")
|
| 169 |
-
print(f" Actual staged size: {local_hashes.get(staged_name, {}).get('size')}")
|
| 170 |
-
print(f" Mapping recorded sha256: {mapped_sha256}")
|
| 171 |
-
print(f" Actual staged sha256: {local_hash}")
|
| 172 |
-
|
| 173 |
-
if mapped_size != meta["expected_size"]:
|
| 174 |
-
print(f" FAIL: Mapping size mismatch for {code}")
|
| 175 |
-
all_passed = False
|
| 176 |
-
if mapped_sha256 != local_hash:
|
| 177 |
-
print(f" FAIL: Mapping SHA-256 mismatch for {code}")
|
| 178 |
-
all_passed = False
|
| 179 |
-
else:
|
| 180 |
-
print(f" PASS: Code {code} mapping perfectly matches local staged file")
|
| 181 |
-
|
| 182 |
-
staged_rel = f"baseline/staging_models/{staged_name}"
|
| 183 |
-
source_rel = f"baseline/results/models/{meta['source']}"
|
| 184 |
-
|
| 185 |
-
if obf_to_orig.get(staged_rel) != source_rel and obf_to_orig.get(staged_name) != source_rel:
|
| 186 |
-
print(f" FAIL: obf_to_orig missing mapping for {staged_name} -> {source_rel}")
|
| 187 |
-
all_passed = False
|
| 188 |
-
else:
|
| 189 |
-
print(f" PASS: obf_to_orig contains {staged_name} -> {source_rel}")
|
| 190 |
-
|
| 191 |
-
if orig_to_obf.get(source_rel) != staged_rel:
|
| 192 |
-
print(f" FAIL: orig_to_obf missing mapping for {source_rel} -> {staged_rel}")
|
| 193 |
-
all_passed = False
|
| 194 |
-
else:
|
| 195 |
-
print(f" PASS: orig_to_obf contains {source_rel} -> {staged_rel}")
|
| 196 |
-
|
| 197 |
-
return all_passed
|
| 198 |
-
|
| 199 |
-
def test_metadata_and_permissions():
|
| 200 |
-
print("\n=== TEST 4: Staging Metadata and Permissions Audit ===")
|
| 201 |
-
all_passed = True
|
| 202 |
-
|
| 203 |
-
if not os.path.exists(METADATA_FILE):
|
| 204 |
-
print(f"FAIL: dataset-metadata.json missing at {METADATA_FILE}")
|
| 205 |
-
return False
|
| 206 |
-
|
| 207 |
-
with open(METADATA_FILE, 'r') as f:
|
| 208 |
-
try:
|
| 209 |
-
meta = json.load(f)
|
| 210 |
-
print(f"Loaded dataset-metadata.json content:\n{json.dumps(meta, indent=2)}")
|
| 211 |
-
except Exception as e:
|
| 212 |
-
print(f"FAIL: Invalid JSON in dataset-metadata.json: {e}")
|
| 213 |
-
return False
|
| 214 |
-
|
| 215 |
-
if meta.get("id") != DATASET_SLUG:
|
| 216 |
-
print(f"FAIL: Expected id '{DATASET_SLUG}', got '{meta.get('id')}'")
|
| 217 |
-
all_passed = False
|
| 218 |
-
else:
|
| 219 |
-
print(f"PASS: Dataset id matches '{DATASET_SLUG}'")
|
| 220 |
-
|
| 221 |
-
if not meta.get("title"):
|
| 222 |
-
print("FAIL: Title is empty")
|
| 223 |
-
all_passed = False
|
| 224 |
-
else:
|
| 225 |
-
print(f"PASS: Title is present: '{meta.get('title')}'")
|
| 226 |
-
|
| 227 |
-
licenses = meta.get("licenses", [])
|
| 228 |
-
if not licenses or not any(l.get("name") for l in licenses):
|
| 229 |
-
print("FAIL: Missing license specification")
|
| 230 |
-
all_passed = False
|
| 231 |
-
else:
|
| 232 |
-
print(f"PASS: License specified: {licenses[0].get('name')}")
|
| 233 |
-
|
| 234 |
-
for fname in os.listdir(STAGING_DIR):
|
| 235 |
-
fpath = os.path.join(STAGING_DIR, fname)
|
| 236 |
-
mode = os.stat(fpath).st_mode
|
| 237 |
-
is_readable = bool(mode & stat.S_IRUSR)
|
| 238 |
-
print(f"File permissions for {fname}: octal {oct(mode)}, owner readable: {is_readable}")
|
| 239 |
-
if not is_readable:
|
| 240 |
-
print(f"FAIL: File {fname} not readable!")
|
| 241 |
-
all_passed = False
|
| 242 |
-
|
| 243 |
-
return all_passed
|
| 244 |
-
|
| 245 |
-
def get_kaggle_cmd():
|
| 246 |
-
import shutil
|
| 247 |
-
candidates = [
|
| 248 |
-
shutil.which("kaggle"),
|
| 249 |
-
"/opt/anaconda3/bin/kaggle",
|
| 250 |
-
"/Users/danesh/.local/bin/kaggle",
|
| 251 |
-
"/opt/homebrew/bin/kaggle",
|
| 252 |
-
"/usr/local/bin/kaggle"
|
| 253 |
-
]
|
| 254 |
-
for c in candidates:
|
| 255 |
-
if c and os.path.exists(c) and os.access(c, os.X_OK):
|
| 256 |
-
return c
|
| 257 |
-
return "kaggle"
|
| 258 |
-
|
| 259 |
-
def test_kaggle_api_live():
|
| 260 |
-
print(f"\n=== TEST 5: Live Kaggle Dataset Query for '{DATASET_SLUG}' ===")
|
| 261 |
-
status_passed = False
|
| 262 |
-
files_passed = False
|
| 263 |
-
remote_version = None
|
| 264 |
-
remote_files = {}
|
| 265 |
-
kaggle_bin = get_kaggle_cmd()
|
| 266 |
-
print(f"Using kaggle binary: {kaggle_bin}")
|
| 267 |
-
|
| 268 |
-
# 1. Check status
|
| 269 |
-
try:
|
| 270 |
-
cmd = [kaggle_bin, "datasets", "status", DATASET_SLUG, "--format", "json"]
|
| 271 |
-
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
| 272 |
-
print(f"Ran command: {' '.join(cmd)}")
|
| 273 |
-
print(f"Return code: {proc.returncode}")
|
| 274 |
-
print(f"Stdout: {proc.stdout.strip()}")
|
| 275 |
-
print(f"Stderr: {proc.stderr.strip()}")
|
| 276 |
-
|
| 277 |
-
if proc.returncode == 0:
|
| 278 |
-
status_data = json.loads(proc.stdout)
|
| 279 |
-
status = status_data.get("status")
|
| 280 |
-
remote_version = status_data.get("current_version_number")
|
| 281 |
-
print(f"Kaggle API reported status: {status}, current_version_number: {remote_version}")
|
| 282 |
-
if status == "ready" and remote_version == 2:
|
| 283 |
-
print("PASS: Remote dataset status is 'ready' and version is 2")
|
| 284 |
-
status_passed = True
|
| 285 |
-
else:
|
| 286 |
-
print(f"FAIL: Expected status 'ready' and version 2, got status={status}, version={remote_version}")
|
| 287 |
-
else:
|
| 288 |
-
print(f"Kaggle CLI returned error: {proc.stderr}")
|
| 289 |
-
except Exception as e:
|
| 290 |
-
print(f"Exception invoking kaggle CLI status: {e}")
|
| 291 |
-
|
| 292 |
-
# 2. Check files
|
| 293 |
-
try:
|
| 294 |
-
cmd = [kaggle_bin, "datasets", "files", DATASET_SLUG, "--csv"]
|
| 295 |
-
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
| 296 |
-
print(f"\nRan command: {' '.join(cmd)}")
|
| 297 |
-
print(f"Return code: {proc.returncode}")
|
| 298 |
-
print(f"Stdout:\n{proc.stdout.strip()}")
|
| 299 |
-
print(f"Stderr: {proc.stderr.strip()}")
|
| 300 |
-
|
| 301 |
-
if proc.returncode == 0:
|
| 302 |
-
lines = proc.stdout.strip().splitlines()
|
| 303 |
-
import csv
|
| 304 |
-
reader = csv.DictReader(lines)
|
| 305 |
-
for row in reader:
|
| 306 |
-
remote_files[row["name"]] = int(row["size"])
|
| 307 |
-
|
| 308 |
-
print(f"Parsed remote files ({len(remote_files)}): {remote_files}")
|
| 309 |
-
|
| 310 |
-
missing = set(EXPECTED_FILES.keys()) - set(remote_files.keys())
|
| 311 |
-
unexpected = set(remote_files.keys()) - set(EXPECTED_FILES.keys())
|
| 312 |
-
|
| 313 |
-
if missing:
|
| 314 |
-
print(f"FAIL: Missing files on Kaggle: {missing}")
|
| 315 |
-
elif unexpected:
|
| 316 |
-
print(f"WARNING: Unexpected files on Kaggle: {unexpected}")
|
| 317 |
-
else:
|
| 318 |
-
print("PASS: Exactly the 3 expected files exist on Kaggle")
|
| 319 |
-
|
| 320 |
-
size_mismatch = False
|
| 321 |
-
for fname, exp_meta in EXPECTED_FILES.items():
|
| 322 |
-
if fname in remote_files:
|
| 323 |
-
rem_size = remote_files[fname]
|
| 324 |
-
exp_size = exp_meta["expected_size"]
|
| 325 |
-
if rem_size == exp_size:
|
| 326 |
-
print(f"PASS: {fname} remote size ({rem_size}) matches expected ({exp_size})")
|
| 327 |
-
else:
|
| 328 |
-
print(f"FAIL: {fname} remote size ({rem_size}) != expected ({exp_size})")
|
| 329 |
-
size_mismatch = True
|
| 330 |
-
if not missing and not size_mismatch:
|
| 331 |
-
files_passed = True
|
| 332 |
-
except Exception as e:
|
| 333 |
-
print(f"Exception invoking kaggle CLI files: {e}")
|
| 334 |
-
|
| 335 |
-
return (status_passed and files_passed), {"version": remote_version, "status_passed": status_passed, "files_passed": files_passed, "remote_files": remote_files}
|
| 336 |
-
|
| 337 |
-
def test_kaggle_input_mounting_simulation():
|
| 338 |
-
print("\n=== TEST 6: Kaggle /kaggle/input/ Mounting & Path Simulation ===")
|
| 339 |
-
simulated_mount = "/kaggle/input/dias-baseline-models"
|
| 340 |
-
mount_paths = {fname: f"{simulated_mount}/{fname}" for fname in EXPECTED_FILES}
|
| 341 |
-
print(f"Simulated Kaggle mount paths:\n{json.dumps(mount_paths, indent=2)}")
|
| 342 |
-
|
| 343 |
-
dataset_name = DATASET_SLUG.split("/")[1]
|
| 344 |
-
print(f"Kaggle mount directory name: '{dataset_name}'")
|
| 345 |
-
if dataset_name != "dias-baseline-models":
|
| 346 |
-
print(f"FAIL: Unexpected mount dir name: {dataset_name}")
|
| 347 |
-
return False
|
| 348 |
-
print("PASS: Mount folder name matches standard Kaggle input directory convention")
|
| 349 |
-
return True
|
| 350 |
-
|
| 351 |
-
if __name__ == "__main__":
|
| 352 |
-
t1_pass, local_hashes = test_local_parity_and_integrity()
|
| 353 |
-
t2_pass = test_keras_archive_integrity()
|
| 354 |
-
t3_pass = test_mapping_json_consistency(local_hashes)
|
| 355 |
-
t4_pass = test_metadata_and_permissions()
|
| 356 |
-
t5_pass, remote_info = test_kaggle_api_live()
|
| 357 |
-
t6_pass = test_kaggle_input_mounting_simulation()
|
| 358 |
-
|
| 359 |
-
print("\n==================================================")
|
| 360 |
-
print(" SUMMARY VERDICT ")
|
| 361 |
-
print("==================================================")
|
| 362 |
-
print(f"Test 1 (Local Parity & SHA-256): {'PASS' if t1_pass else 'FAIL'}")
|
| 363 |
-
print(f"Test 2 (Keras ZIP Integrity): {'PASS' if t2_pass else 'FAIL'}")
|
| 364 |
-
print(f"Test 3 (Mapping JSON Consistency): {'PASS' if t3_pass else 'FAIL'}")
|
| 365 |
-
print(f"Test 4 (Metadata & Permissions): {'PASS' if t4_pass else 'FAIL'}")
|
| 366 |
-
print(f"Test 5 (Kaggle API Live Query): {'PASS' if t5_pass else 'FAIL'}")
|
| 367 |
-
print(f"Test 6 (Mounting Simulation): {'PASS' if t6_pass else 'FAIL'}")
|
| 368 |
-
|
| 369 |
-
all_tests = [t1_pass, t2_pass, t3_pass, t4_pass, t5_pass, t6_pass]
|
| 370 |
-
if all(all_tests):
|
| 371 |
-
print("\nOVERALL VERDICT: APPROVE")
|
| 372 |
-
sys.exit(0)
|
| 373 |
-
else:
|
| 374 |
-
print("\nOVERALL VERDICT: REQUEST_CHANGES")
|
| 375 |
-
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
credit/Model_training_credit.ipynb
DELETED
|
@@ -1,2065 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"cells": [
|
| 3 |
-
{
|
| 4 |
-
"cell_type": "markdown",
|
| 5 |
-
"id": "5302be31",
|
| 6 |
-
"metadata": {
|
| 7 |
-
"id": "5302be31"
|
| 8 |
-
},
|
| 9 |
-
"source": [
|
| 10 |
-
"# 1.0 \u2014 Setup & Imports\n",
|
| 11 |
-
"\n",
|
| 12 |
-
"Mount Google Drive and import all required standard, scientific, and deep-learning libraries."
|
| 13 |
-
]
|
| 14 |
-
},
|
| 15 |
-
{
|
| 16 |
-
"cell_type": "code",
|
| 17 |
-
"execution_count": null,
|
| 18 |
-
"id": "e55b14ba",
|
| 19 |
-
"metadata": {
|
| 20 |
-
"colab": {
|
| 21 |
-
"base_uri": "https://localhost:8080/"
|
| 22 |
-
},
|
| 23 |
-
"id": "e55b14ba",
|
| 24 |
-
"executionInfo": {
|
| 25 |
-
"status": "ok",
|
| 26 |
-
"timestamp": 1780323019276,
|
| 27 |
-
"user_tz": -330,
|
| 28 |
-
"elapsed": 32248,
|
| 29 |
-
"user": {
|
| 30 |
-
"displayName": "Randy Orton",
|
| 31 |
-
"userId": "11725427482974841050"
|
| 32 |
-
}
|
| 33 |
-
},
|
| 34 |
-
"outputId": "19572730-3129-4f86-b52c-c5723fcaec5b"
|
| 35 |
-
},
|
| 36 |
-
"outputs": [],
|
| 37 |
-
"source": [
|
| 38 |
-
"from google.colab import drive\n",
|
| 39 |
-
"drive.mount('/content/drive')"
|
| 40 |
-
]
|
| 41 |
-
},
|
| 42 |
-
{
|
| 43 |
-
"cell_type": "code",
|
| 44 |
-
"execution_count": null,
|
| 45 |
-
"id": "23495159",
|
| 46 |
-
"metadata": {
|
| 47 |
-
"colab": {
|
| 48 |
-
"base_uri": "https://localhost:8080/"
|
| 49 |
-
},
|
| 50 |
-
"id": "23495159",
|
| 51 |
-
"executionInfo": {
|
| 52 |
-
"status": "ok",
|
| 53 |
-
"timestamp": 1780323025101,
|
| 54 |
-
"user_tz": -330,
|
| 55 |
-
"elapsed": 5821,
|
| 56 |
-
"user": {
|
| 57 |
-
"displayName": "Randy Orton",
|
| 58 |
-
"userId": "11725427482974841050"
|
| 59 |
-
}
|
| 60 |
-
},
|
| 61 |
-
"outputId": "9aea70f9-9842-46db-ab9e-4fad29f942fe"
|
| 62 |
-
},
|
| 63 |
-
"outputs": [],
|
| 64 |
-
"source": [
|
| 65 |
-
"# \u2500\u2500 Standard library \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 66 |
-
"import os\n",
|
| 67 |
-
"import glob\n",
|
| 68 |
-
"import json\n",
|
| 69 |
-
"import time\n",
|
| 70 |
-
"import random\n",
|
| 71 |
-
"from pathlib import Path\n",
|
| 72 |
-
"\n",
|
| 73 |
-
"from scipy.io import loadmat\n",
|
| 74 |
-
"\n",
|
| 75 |
-
"# \u2500\u2500 Scientific / data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 76 |
-
"import numpy as np\n",
|
| 77 |
-
"import pandas as pd\n",
|
| 78 |
-
"\n",
|
| 79 |
-
"# \u2500\u2500 Visualisation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 80 |
-
"import seaborn as sns\n",
|
| 81 |
-
"import matplotlib.pyplot as plt\n",
|
| 82 |
-
"from matplotlib.colors import ListedColormap\n",
|
| 83 |
-
"from matplotlib.patches import Patch\n",
|
| 84 |
-
"\n",
|
| 85 |
-
"# \u2500\u2500 Deep learning \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 86 |
-
"import tensorflow as tf\n",
|
| 87 |
-
"from tensorflow import keras\n",
|
| 88 |
-
"from tensorflow.keras import layers\n",
|
| 89 |
-
"\n",
|
| 90 |
-
"# \u2500\u2500 ML utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 91 |
-
"from sklearn.model_selection import train_test_split\n",
|
| 92 |
-
"from sklearn.metrics import (\n",
|
| 93 |
-
" accuracy_score, cohen_kappa_score, confusion_matrix,\n",
|
| 94 |
-
" classification_report, f1_score, log_loss\n",
|
| 95 |
-
")\n",
|
| 96 |
-
"\n",
|
| 97 |
-
"# \u2500\u2500 Excel export \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 98 |
-
"from openpyxl import Workbook\n",
|
| 99 |
-
"from openpyxl.styles import Font, PatternFill, Alignment, Border, Side\n",
|
| 100 |
-
"from openpyxl.utils import get_column_letter\n",
|
| 101 |
-
"from openpyxl.drawing.image import Image as XLImage\n",
|
| 102 |
-
"\n",
|
| 103 |
-
"sns.set_style('whitegrid')\n",
|
| 104 |
-
"print('TensorFlow:', tf.__version__)"
|
| 105 |
-
]
|
| 106 |
-
},
|
| 107 |
-
{
|
| 108 |
-
"cell_type": "markdown",
|
| 109 |
-
"id": "90f75990",
|
| 110 |
-
"metadata": {
|
| 111 |
-
"id": "90f75990"
|
| 112 |
-
},
|
| 113 |
-
"source": [
|
| 114 |
-
"# 2.0 \u2014 Configuration\n",
|
| 115 |
-
"\n",
|
| 116 |
-
"All paths, seeds, dataset geometry, training hyperparameters, and per-architecture configs in one place. **Edit only this section between experiments.**"
|
| 117 |
-
]
|
| 118 |
-
},
|
| 119 |
-
{
|
| 120 |
-
"cell_type": "code",
|
| 121 |
-
"execution_count": null,
|
| 122 |
-
"id": "899f34ab",
|
| 123 |
-
"metadata": {
|
| 124 |
-
"colab": {
|
| 125 |
-
"base_uri": "https://localhost:8080/"
|
| 126 |
-
},
|
| 127 |
-
"id": "899f34ab",
|
| 128 |
-
"executionInfo": {
|
| 129 |
-
"status": "ok",
|
| 130 |
-
"timestamp": 1780323232678,
|
| 131 |
-
"user_tz": -330,
|
| 132 |
-
"elapsed": 8,
|
| 133 |
-
"user": {
|
| 134 |
-
"displayName": "Randy Orton",
|
| 135 |
-
"userId": "11725427482974841050"
|
| 136 |
-
}
|
| 137 |
-
},
|
| 138 |
-
"outputId": "8afd880f-b7c5-4c09-9afc-91fc3a190656"
|
| 139 |
-
},
|
| 140 |
-
"outputs": [],
|
| 141 |
-
"source": [
|
| 142 |
-
"# \u2500\u2500 Reproducibility \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 143 |
-
"SEED = 42\n",
|
| 144 |
-
"random.seed(SEED)\n",
|
| 145 |
-
"np.random.seed(SEED)\n",
|
| 146 |
-
"tf.random.set_seed(SEED)\n",
|
| 147 |
-
"\n",
|
| 148 |
-
"# \u2500\u2500 Directory layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 149 |
-
"PROJECT_ROOT = Path('/content/drive/My Drive/Classification')\n",
|
| 150 |
-
"DATA_DIR = PROJECT_ROOT / 'data'\n",
|
| 151 |
-
"MODEL_DIR = PROJECT_ROOT / 'credit' / 'models'\n",
|
| 152 |
-
"RESULTS_DIR = PROJECT_ROOT / 'credit' / 'results'\n",
|
| 153 |
-
"ENSEMBLE_DIR = PROJECT_ROOT / 'ensemble' / 'models' / 'ensembles'\n",
|
| 154 |
-
"\n",
|
| 155 |
-
"MODEL_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 156 |
-
"RESULTS_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 157 |
-
"PLOT_DIR = RESULTS_DIR / 'training_plots'\n",
|
| 158 |
-
"PLOT_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 159 |
-
"\n",
|
| 160 |
-
"DATA_FILE = DATA_DIR / 'DIAS.mat' # hyperspectral cube (H, W, Bands)\n",
|
| 161 |
-
"LABEL_FILE = DATA_DIR / 'DIAS_ref.mat' # reference / ground-truth label map\n",
|
| 162 |
-
"\n",
|
| 163 |
-
"# \u2500\u2500 Dataset geometry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 164 |
-
"# H, W, B are inferred automatically from the .mat cube shape once loaded in\n",
|
| 165 |
-
"# Section 3.2, so this notebook works unchanged for any scene size / band count.\n",
|
| 166 |
-
"H, W, B = None, None, None\n",
|
| 167 |
-
"PERCENTILE_CLIP = (2.0, 98.0) # robust per-band stretch range (2nd-98th percentile)\n",
|
| 168 |
-
"PATCH_SIZE = 9\n",
|
| 169 |
-
"INNER_PATCH = 3 # tokenisation patch size for GFNet / ViT\n",
|
| 170 |
-
"\n",
|
| 171 |
-
"# \u2500\u2500 Split ratios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 172 |
-
"TRAIN_PERCENT = 0.75\n",
|
| 173 |
-
"VAL_SPLIT_FROM_TRAIN = 0.20\n",
|
| 174 |
-
"\n",
|
| 175 |
-
"# \u2500\u2500 Training hyperparameters \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 176 |
-
"BATCH_SIZE = 128\n",
|
| 177 |
-
"EPOCHS = 100\n",
|
| 178 |
-
"LEARNING_RATE = 3e-4\n",
|
| 179 |
-
"DROPOUT_RATE = 0.25\n",
|
| 180 |
-
"\n",
|
| 181 |
-
"TRAIN_CFG = {\n",
|
| 182 |
-
" 'label_smoothing': 0.05,\n",
|
| 183 |
-
" 'weight_decay': 1e-4,\n",
|
| 184 |
-
" 'clipnorm': 1.0,\n",
|
| 185 |
-
" 'cosine_alpha': 0.05,\n",
|
| 186 |
-
"}\n",
|
| 187 |
-
"\n",
|
| 188 |
-
"# \u2500\u2500 Architecture configs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 189 |
-
"ALEXNET_CFG = {\n",
|
| 190 |
-
" 'conv_filters': [96, 256, 384, 384, 256],\n",
|
| 191 |
-
" 'dense_units': [4096, 1024, 256, 32],\n",
|
| 192 |
-
"}\n",
|
| 193 |
-
"GFNET_CFG = {\n",
|
| 194 |
-
" 'hidden_dim': 512,\n",
|
| 195 |
-
" 'num_blocks': 5,\n",
|
| 196 |
-
" 'mlp_ratio': 4,\n",
|
| 197 |
-
"}\n",
|
| 198 |
-
"VIT_CFG = {\n",
|
| 199 |
-
" 'projection_dim': 256,\n",
|
| 200 |
-
" 'num_heads': 4,\n",
|
| 201 |
-
" 'transformer_layers': 12,\n",
|
| 202 |
-
" 'mlp_multiplier': 2,\n",
|
| 203 |
-
" 'head_units': [512, 256, 128, 64],\n",
|
| 204 |
-
"}\n",
|
| 205 |
-
"\n",
|
| 206 |
-
"# \u2500\u2500 AlexNet legacy recipe (keeps uncertainty recovery reproducible) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 207 |
-
"ALEXNET_LEGACY_SPLIT_SEED = 10\n",
|
| 208 |
-
"ALEXNET_LEGACY_TRAIN_PERCENT = 0.75\n",
|
| 209 |
-
"ALEXNET_LR_START = 0.01\n",
|
| 210 |
-
"ALEXNET_LR_MAX = 0.02\n",
|
| 211 |
-
"ALEXNET_LR_MIN = 0.005\n",
|
| 212 |
-
"\n",
|
| 213 |
-
"# \u2500\u2500 Sanity print \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 214 |
-
"print('Data :', DATA_FILE)\n",
|
| 215 |
-
"print('Labels :', LABEL_FILE)\n",
|
| 216 |
-
"print('Models :', MODEL_DIR)\n",
|
| 217 |
-
"print('Results:', RESULTS_DIR)"
|
| 218 |
-
]
|
| 219 |
-
},
|
| 220 |
-
{
|
| 221 |
-
"cell_type": "markdown",
|
| 222 |
-
"id": "5897552f",
|
| 223 |
-
"metadata": {
|
| 224 |
-
"id": "5897552f"
|
| 225 |
-
},
|
| 226 |
-
"source": [
|
| 227 |
-
"# 3.0 \u2014 Data Loading & Patch Extraction\n",
|
| 228 |
-
"\n",
|
| 229 |
-
"Reads the hyperspectral/multispectral scene from .mat files, applies a robust per-band percentile-clip normalisation, extracts fixed-size spatial patches centred on every labelled pixel, and builds both the standard and AlexNet-legacy train/test splits."
|
| 230 |
-
]
|
| 231 |
-
},
|
| 232 |
-
{
|
| 233 |
-
"cell_type": "markdown",
|
| 234 |
-
"id": "033bf713",
|
| 235 |
-
"metadata": {
|
| 236 |
-
"id": "033bf713"
|
| 237 |
-
},
|
| 238 |
-
"source": [
|
| 239 |
-
"## 3.1 \u2014 Helper Functions"
|
| 240 |
-
]
|
| 241 |
-
},
|
| 242 |
-
{
|
| 243 |
-
"cell_type": "code",
|
| 244 |
-
"execution_count": null,
|
| 245 |
-
"id": "0662fe6e",
|
| 246 |
-
"metadata": {
|
| 247 |
-
"id": "0662fe6e",
|
| 248 |
-
"executionInfo": {
|
| 249 |
-
"status": "ok",
|
| 250 |
-
"timestamp": 1780323233562,
|
| 251 |
-
"user_tz": -330,
|
| 252 |
-
"elapsed": 10,
|
| 253 |
-
"user": {
|
| 254 |
-
"displayName": "Randy Orton",
|
| 255 |
-
"userId": "11725427482974841050"
|
| 256 |
-
}
|
| 257 |
-
}
|
| 258 |
-
},
|
| 259 |
-
"outputs": [],
|
| 260 |
-
"source": [
|
| 261 |
-
"def load_scene_from_mat(data_path, label_path, data_key=None, label_key=None):\n",
|
| 262 |
-
" \"\"\"Load a hyperspectral/multispectral scene + reference labels from .mat files.\n",
|
| 263 |
-
"\n",
|
| 264 |
-
" Auto-detects the variable name inside each .mat file if not given (matches\n",
|
| 265 |
-
" files such as DIAS.mat / DIAS_ref.mat storing the array under a single named\n",
|
| 266 |
-
" variable). Each spectral band is robustly stretched to [0, 1] using a\n",
|
| 267 |
-
" percentile clip (PERCENTILE_CLIP) rather than literal min/max \u2014 a handful of\n",
|
| 268 |
-
" saturated/no-data pixels (e.g. sensor clipping at int8 range limits -128/127)\n",
|
| 269 |
-
" would otherwise dominate the stretch.\n",
|
| 270 |
-
" \"\"\"\n",
|
| 271 |
-
" def _first_array(mat_dict, key):\n",
|
| 272 |
-
" if key is not None:\n",
|
| 273 |
-
" return mat_dict[key]\n",
|
| 274 |
-
" candidates = [v for k, v in mat_dict.items() if not k.startswith('__')]\n",
|
| 275 |
-
" if not candidates:\n",
|
| 276 |
-
" raise ValueError(f'No data arrays found in {mat_dict.keys()}')\n",
|
| 277 |
-
" return candidates[0]\n",
|
| 278 |
-
"\n",
|
| 279 |
-
" data_mat = loadmat(data_path)\n",
|
| 280 |
-
" label_mat = loadmat(label_path)\n",
|
| 281 |
-
"\n",
|
| 282 |
-
" x = _first_array(data_mat, data_key).astype(np.float32)\n",
|
| 283 |
-
" y = _first_array(label_mat, label_key).astype(np.int32)\n",
|
| 284 |
-
"\n",
|
| 285 |
-
" if x.ndim != 3:\n",
|
| 286 |
-
" raise ValueError(f'Expected a (H, W, Bands) cube, got shape {x.shape}')\n",
|
| 287 |
-
" if y.shape != x.shape[:2]:\n",
|
| 288 |
-
" raise ValueError(f'Label shape {y.shape} does not match image shape {x.shape[:2]}')\n",
|
| 289 |
-
"\n",
|
| 290 |
-
" height, width, bands = x.shape\n",
|
| 291 |
-
"\n",
|
| 292 |
-
" x_norm = np.empty_like(x, dtype=np.float32)\n",
|
| 293 |
-
" for b in range(bands):\n",
|
| 294 |
-
" band = x[:, :, b]\n",
|
| 295 |
-
" lo = np.percentile(band, PERCENTILE_CLIP[0])\n",
|
| 296 |
-
" hi = np.percentile(band, PERCENTILE_CLIP[1])\n",
|
| 297 |
-
" denom = max(hi - lo, 1e-8)\n",
|
| 298 |
-
" x_norm[:, :, b] = np.clip((band - lo) / denom, 0.0, 1.0)\n",
|
| 299 |
-
"\n",
|
| 300 |
-
" return x_norm, y, height, width, bands\n",
|
| 301 |
-
"\n",
|
| 302 |
-
"\n",
|
| 303 |
-
"def extract_labeled_patches(x, y, patch_size=9):\n",
|
| 304 |
-
" \"\"\"Extract spatial patches centred on every labelled (y > 0) pixel.\"\"\"\n",
|
| 305 |
-
" pad = patch_size // 2\n",
|
| 306 |
-
" x_pad = np.pad(x, ((pad, pad), (pad, pad), (0, 0)), mode='edge')\n",
|
| 307 |
-
"\n",
|
| 308 |
-
" coords = np.argwhere(y > 0)\n",
|
| 309 |
-
" patches = np.empty((len(coords), patch_size, patch_size, x.shape[-1]), dtype=np.float32)\n",
|
| 310 |
-
" labels = np.empty((len(coords),), dtype=np.int32)\n",
|
| 311 |
-
"\n",
|
| 312 |
-
" for i, (r, c) in enumerate(coords):\n",
|
| 313 |
-
" patches[i] = x_pad[r:r + patch_size, c:c + patch_size, :]\n",
|
| 314 |
-
" labels[i] = int(y[r, c]) - 1 # 1-indexed \u2192 0-indexed\n",
|
| 315 |
-
"\n",
|
| 316 |
-
" return patches, labels, coords"
|
| 317 |
-
]
|
| 318 |
-
},
|
| 319 |
-
{
|
| 320 |
-
"cell_type": "markdown",
|
| 321 |
-
"id": "7ea13597",
|
| 322 |
-
"metadata": {
|
| 323 |
-
"id": "7ea13597"
|
| 324 |
-
},
|
| 325 |
-
"source": [
|
| 326 |
-
"## 3.2 \u2014 Load Image & Extract Patches"
|
| 327 |
-
]
|
| 328 |
-
},
|
| 329 |
-
{
|
| 330 |
-
"cell_type": "code",
|
| 331 |
-
"execution_count": null,
|
| 332 |
-
"id": "073305cd",
|
| 333 |
-
"metadata": {
|
| 334 |
-
"colab": {
|
| 335 |
-
"base_uri": "https://localhost:8080/"
|
| 336 |
-
},
|
| 337 |
-
"id": "073305cd",
|
| 338 |
-
"executionInfo": {
|
| 339 |
-
"status": "ok",
|
| 340 |
-
"timestamp": 1780323233752,
|
| 341 |
-
"user_tz": -330,
|
| 342 |
-
"elapsed": 178,
|
| 343 |
-
"user": {
|
| 344 |
-
"displayName": "Randy Orton",
|
| 345 |
-
"userId": "11725427482974841050"
|
| 346 |
-
}
|
| 347 |
-
},
|
| 348 |
-
"outputId": "e6e64f91-d536-4561-b6a6-2cb54056b034"
|
| 349 |
-
},
|
| 350 |
-
"outputs": [],
|
| 351 |
-
"source": [
|
| 352 |
-
"print('Loading multispectral image...')\n",
|
| 353 |
-
"x_img, y_img, H, W, B = load_scene_from_mat(DATA_FILE, LABEL_FILE)\n",
|
| 354 |
-
"\n",
|
| 355 |
-
"print('Extracting patches...')\n",
|
| 356 |
-
"X, y, coords = extract_labeled_patches(x_img, y_img, PATCH_SIZE)\n",
|
| 357 |
-
"\n",
|
| 358 |
-
"num_classes = int(np.unique(y).size)\n",
|
| 359 |
-
"input_shape = (PATCH_SIZE, PATCH_SIZE, B)\n",
|
| 360 |
-
"\n",
|
| 361 |
-
"print(f'Scene geometry: H={H} W={W} Bands={B}')\n",
|
| 362 |
-
"print(f'Image : {x_img.shape} | Labels : {y_img.shape}')\n",
|
| 363 |
-
"print(f'Patches: {X.shape} | Classes: {num_classes}')"
|
| 364 |
-
]
|
| 365 |
-
},
|
| 366 |
-
{
|
| 367 |
-
"cell_type": "markdown",
|
| 368 |
-
"id": "df01ed90",
|
| 369 |
-
"metadata": {
|
| 370 |
-
"id": "df01ed90"
|
| 371 |
-
},
|
| 372 |
-
"source": [
|
| 373 |
-
"## 3.3 \u2014 Train / Validation / Test Split\n",
|
| 374 |
-
"\n",
|
| 375 |
-
"Two independent splits are created: a **standard split** (shared by GFNet and ViT) and an **AlexNet legacy split** (separate seed to preserve reproducibility of uncertainty recovery)."
|
| 376 |
-
]
|
| 377 |
-
},
|
| 378 |
-
{
|
| 379 |
-
"cell_type": "code",
|
| 380 |
-
"execution_count": null,
|
| 381 |
-
"id": "fe1596a8",
|
| 382 |
-
"metadata": {
|
| 383 |
-
"colab": {
|
| 384 |
-
"base_uri": "https://localhost:8080/"
|
| 385 |
-
},
|
| 386 |
-
"id": "fe1596a8",
|
| 387 |
-
"executionInfo": {
|
| 388 |
-
"status": "ok",
|
| 389 |
-
"timestamp": 1780323233852,
|
| 390 |
-
"user_tz": -330,
|
| 391 |
-
"elapsed": 97,
|
| 392 |
-
"user": {
|
| 393 |
-
"displayName": "Randy Orton",
|
| 394 |
-
"userId": "11725427482974841050"
|
| 395 |
-
}
|
| 396 |
-
},
|
| 397 |
-
"outputId": "0091a33f-b2e7-41bc-c442-930ccb45c2f1"
|
| 398 |
-
},
|
| 399 |
-
"outputs": [],
|
| 400 |
-
"source": [
|
| 401 |
-
"# \u2500\u2500 Standard split (GFNet & ViT) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 402 |
-
"x_train_full, x_test, y_train_full, y_test = train_test_split(\n",
|
| 403 |
-
" X, y, train_size=TRAIN_PERCENT, random_state=SEED, stratify=y\n",
|
| 404 |
-
")\n",
|
| 405 |
-
"x_train, x_val, y_train, y_val = train_test_split(\n",
|
| 406 |
-
" x_train_full, y_train_full,\n",
|
| 407 |
-
" test_size=VAL_SPLIT_FROM_TRAIN, random_state=SEED, stratify=y_train_full\n",
|
| 408 |
-
")\n",
|
| 409 |
-
"\n",
|
| 410 |
-
"y_train_cat = keras.utils.to_categorical(y_train, num_classes)\n",
|
| 411 |
-
"y_val_cat = keras.utils.to_categorical(y_val, num_classes)\n",
|
| 412 |
-
"y_test_cat = keras.utils.to_categorical(y_test, num_classes)\n",
|
| 413 |
-
"\n",
|
| 414 |
-
"print(f'Train : {x_train.shape} Val : {x_val.shape} Test : {x_test.shape}')\n",
|
| 415 |
-
"\n",
|
| 416 |
-
"# \u2500\u2500 AlexNet legacy split (separate seed for uncertainty recovery) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 417 |
-
"x_train_alex, x_test_alex, y_train_alex, y_test_alex = train_test_split(\n",
|
| 418 |
-
" X, y,\n",
|
| 419 |
-
" train_size=ALEXNET_LEGACY_TRAIN_PERCENT,\n",
|
| 420 |
-
" random_state=ALEXNET_LEGACY_SPLIT_SEED,\n",
|
| 421 |
-
" stratify=y\n",
|
| 422 |
-
")\n",
|
| 423 |
-
"y_train_alex_cat = keras.utils.to_categorical(y_train_alex, num_classes)\n",
|
| 424 |
-
"y_test_alex_cat = keras.utils.to_categorical(y_test_alex, num_classes)\n",
|
| 425 |
-
"\n",
|
| 426 |
-
"print(f'AlexNet Train : {x_train_alex.shape} Test : {x_test_alex.shape}')"
|
| 427 |
-
]
|
| 428 |
-
},
|
| 429 |
-
{
|
| 430 |
-
"cell_type": "markdown",
|
| 431 |
-
"id": "7d481920",
|
| 432 |
-
"metadata": {
|
| 433 |
-
"id": "7d481920"
|
| 434 |
-
},
|
| 435 |
-
"source": [
|
| 436 |
-
"# 4.0 \u2014 Model Definitions\n",
|
| 437 |
-
"\n",
|
| 438 |
-
"Defines all three single-head base architectures (AlexNet CNN, GFNet, ViT with U-Net skip connections) plus the shared custom Keras layers used by GFNet and ViT."
|
| 439 |
-
]
|
| 440 |
-
},
|
| 441 |
-
{
|
| 442 |
-
"cell_type": "markdown",
|
| 443 |
-
"id": "2347fb89",
|
| 444 |
-
"metadata": {
|
| 445 |
-
"id": "2347fb89"
|
| 446 |
-
},
|
| 447 |
-
"source": [
|
| 448 |
-
"## 4.1 \u2014 Shared Custom Keras Layers\n",
|
| 449 |
-
"\n",
|
| 450 |
-
"Four serialisable custom layers: patch extraction, positional encoding (with and without CLS token), and the frequency-domain Global Filter layer used in GFNet."
|
| 451 |
-
]
|
| 452 |
-
},
|
| 453 |
-
{
|
| 454 |
-
"cell_type": "code",
|
| 455 |
-
"execution_count": null,
|
| 456 |
-
"id": "523b622b",
|
| 457 |
-
"metadata": {
|
| 458 |
-
"id": "523b622b",
|
| 459 |
-
"executionInfo": {
|
| 460 |
-
"status": "ok",
|
| 461 |
-
"timestamp": 1780323236816,
|
| 462 |
-
"user_tz": -330,
|
| 463 |
-
"elapsed": 55,
|
| 464 |
-
"user": {
|
| 465 |
-
"displayName": "Randy Orton",
|
| 466 |
-
"userId": "11725427482974841050"
|
| 467 |
-
}
|
| 468 |
-
}
|
| 469 |
-
},
|
| 470 |
-
"outputs": [],
|
| 471 |
-
"source": [
|
| 472 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 473 |
-
"class PatchExtractor(layers.Layer):\n",
|
| 474 |
-
" \"\"\"Extract non-overlapping patches from a spatial image tensor.\"\"\"\n",
|
| 475 |
-
"\n",
|
| 476 |
-
" def __init__(self, patch_size=3, **kwargs):\n",
|
| 477 |
-
" super().__init__(**kwargs)\n",
|
| 478 |
-
" self.patch_size = patch_size\n",
|
| 479 |
-
"\n",
|
| 480 |
-
" def call(self, images):\n",
|
| 481 |
-
" patches = tf.image.extract_patches(\n",
|
| 482 |
-
" images=images,\n",
|
| 483 |
-
" sizes=[1, self.patch_size, self.patch_size, 1],\n",
|
| 484 |
-
" strides=[1, self.patch_size, self.patch_size, 1],\n",
|
| 485 |
-
" rates=[1, 1, 1, 1],\n",
|
| 486 |
-
" padding='VALID',\n",
|
| 487 |
-
" )\n",
|
| 488 |
-
" batch = tf.shape(images)[0]\n",
|
| 489 |
-
" num_patches = tf.shape(patches)[1] * tf.shape(patches)[2]\n",
|
| 490 |
-
" patch_dim = tf.shape(patches)[-1]\n",
|
| 491 |
-
" return tf.reshape(patches, [batch, num_patches, patch_dim])\n",
|
| 492 |
-
"\n",
|
| 493 |
-
" def get_config(self):\n",
|
| 494 |
-
" cfg = super().get_config()\n",
|
| 495 |
-
" cfg.update({'patch_size': self.patch_size})\n",
|
| 496 |
-
" return cfg\n",
|
| 497 |
-
"\n",
|
| 498 |
-
"\n",
|
| 499 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 500 |
-
"class PatchPositionEncoder(layers.Layer):\n",
|
| 501 |
-
" \"\"\"Linear projection + learnable position embedding (no CLS token).\"\"\"\n",
|
| 502 |
-
"\n",
|
| 503 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 504 |
-
" super().__init__(**kwargs)\n",
|
| 505 |
-
" self.num_patches = num_patches\n",
|
| 506 |
-
" self.projection_dim = projection_dim\n",
|
| 507 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 508 |
-
" self.position_embedding = layers.Embedding(\n",
|
| 509 |
-
" input_dim=num_patches, output_dim=projection_dim\n",
|
| 510 |
-
" )\n",
|
| 511 |
-
"\n",
|
| 512 |
-
" def call(self, patches):\n",
|
| 513 |
-
" positions = tf.range(start=0, limit=self.num_patches, delta=1)\n",
|
| 514 |
-
" return self.projection(patches) + self.position_embedding(positions)\n",
|
| 515 |
-
"\n",
|
| 516 |
-
" def get_config(self):\n",
|
| 517 |
-
" cfg = super().get_config()\n",
|
| 518 |
-
" cfg.update({'num_patches': self.num_patches, 'projection_dim': self.projection_dim})\n",
|
| 519 |
-
" return cfg\n",
|
| 520 |
-
"\n",
|
| 521 |
-
"\n",
|
| 522 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 523 |
-
"class PatchEncoderWithCLS(layers.Layer):\n",
|
| 524 |
-
" \"\"\"Linear projection + learnable position embedding WITH a prepended CLS token.\"\"\"\n",
|
| 525 |
-
"\n",
|
| 526 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 527 |
-
" super().__init__(**kwargs)\n",
|
| 528 |
-
" self.num_patches = num_patches\n",
|
| 529 |
-
" self.projection_dim = projection_dim\n",
|
| 530 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 531 |
-
" self.position_embedding = layers.Embedding(\n",
|
| 532 |
-
" input_dim=num_patches + 1, output_dim=projection_dim\n",
|
| 533 |
-
" )\n",
|
| 534 |
-
"\n",
|
| 535 |
-
" def build(self, input_shape):\n",
|
| 536 |
-
" self.cls_token = self.add_weight(\n",
|
| 537 |
-
" name='cls_token', shape=(1, 1, self.projection_dim),\n",
|
| 538 |
-
" initializer='zeros', trainable=True\n",
|
| 539 |
-
" )\n",
|
| 540 |
-
" super().build(input_shape)\n",
|
| 541 |
-
"\n",
|
| 542 |
-
" def call(self, patches):\n",
|
| 543 |
-
" batch = tf.shape(patches)[0]\n",
|
| 544 |
-
" patch_proj = self.projection(patches)\n",
|
| 545 |
-
" cls_tokens = tf.repeat(self.cls_token, repeats=batch, axis=0)\n",
|
| 546 |
-
" x = tf.concat([cls_tokens, patch_proj], axis=1)\n",
|
| 547 |
-
" positions = tf.range(start=0, limit=self.num_patches + 1, delta=1)\n",
|
| 548 |
-
" return x + self.position_embedding(positions)\n",
|
| 549 |
-
"\n",
|
| 550 |
-
" def get_config(self):\n",
|
| 551 |
-
" cfg = super().get_config()\n",
|
| 552 |
-
" cfg.update({'num_patches': self.num_patches, 'projection_dim': self.projection_dim})\n",
|
| 553 |
-
" return cfg\n",
|
| 554 |
-
"\n",
|
| 555 |
-
"\n",
|
| 556 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 557 |
-
"class GlobalFilterLayer(layers.Layer):\n",
|
| 558 |
-
" \"\"\"Learnable complex-valued filter applied in the 2-D frequency domain.\"\"\"\n",
|
| 559 |
-
"\n",
|
| 560 |
-
" def __init__(self, token_side, **kwargs):\n",
|
| 561 |
-
" super().__init__(**kwargs)\n",
|
| 562 |
-
" self.token_side = token_side\n",
|
| 563 |
-
"\n",
|
| 564 |
-
" def build(self, input_shape):\n",
|
| 565 |
-
" channels = int(input_shape[-1])\n",
|
| 566 |
-
" self.w_real = self.add_weight(\n",
|
| 567 |
-
" name='w_real', shape=(self.token_side, self.token_side, channels),\n",
|
| 568 |
-
" initializer='glorot_uniform', trainable=True\n",
|
| 569 |
-
" )\n",
|
| 570 |
-
" self.w_imag = self.add_weight(\n",
|
| 571 |
-
" name='w_imag', shape=(self.token_side, self.token_side, channels),\n",
|
| 572 |
-
" initializer='zeros', trainable=True\n",
|
| 573 |
-
" )\n",
|
| 574 |
-
" super().build(input_shape)\n",
|
| 575 |
-
"\n",
|
| 576 |
-
" def call(self, x):\n",
|
| 577 |
-
" batch = tf.shape(x)[0]\n",
|
| 578 |
-
" channels = tf.shape(x)[-1]\n",
|
| 579 |
-
" x_2d = tf.reshape(x, [batch, self.token_side, self.token_side, channels])\n",
|
| 580 |
-
" x_fft = tf.signal.fft2d(tf.cast(x_2d, tf.complex64))\n",
|
| 581 |
-
" w_complex = tf.complex(self.w_real, self.w_imag)\n",
|
| 582 |
-
" x_filtered = x_fft * w_complex\n",
|
| 583 |
-
" x_spatial = tf.math.real(tf.signal.ifft2d(x_filtered))\n",
|
| 584 |
-
" return tf.reshape(x_spatial, [batch, self.token_side * self.token_side, channels])\n",
|
| 585 |
-
"\n",
|
| 586 |
-
" def get_config(self):\n",
|
| 587 |
-
" cfg = super().get_config()\n",
|
| 588 |
-
" cfg.update({'token_side': self.token_side})\n",
|
| 589 |
-
" return cfg"
|
| 590 |
-
]
|
| 591 |
-
},
|
| 592 |
-
{
|
| 593 |
-
"cell_type": "markdown",
|
| 594 |
-
"id": "aec99a4d",
|
| 595 |
-
"metadata": {
|
| 596 |
-
"id": "aec99a4d"
|
| 597 |
-
},
|
| 598 |
-
"source": [
|
| 599 |
-
"## 4.2 \u2014 AlexNet CNN"
|
| 600 |
-
]
|
| 601 |
-
},
|
| 602 |
-
{
|
| 603 |
-
"cell_type": "code",
|
| 604 |
-
"execution_count": null,
|
| 605 |
-
"id": "bd04d5a2",
|
| 606 |
-
"metadata": {
|
| 607 |
-
"id": "bd04d5a2",
|
| 608 |
-
"executionInfo": {
|
| 609 |
-
"status": "ok",
|
| 610 |
-
"timestamp": 1780323236835,
|
| 611 |
-
"user_tz": -330,
|
| 612 |
-
"elapsed": 16,
|
| 613 |
-
"user": {
|
| 614 |
-
"displayName": "Randy Orton",
|
| 615 |
-
"userId": "11725427482974841050"
|
| 616 |
-
}
|
| 617 |
-
}
|
| 618 |
-
},
|
| 619 |
-
"outputs": [],
|
| 620 |
-
"source": [
|
| 621 |
-
"def build_alexnet(input_shape, num_classes, dropout_rate=0.25, cfg=None):\n",
|
| 622 |
-
" \"\"\"Build a single-head AlexNet-style CNN with configurable conv and dense units.\"\"\"\n",
|
| 623 |
-
" cfg = cfg or ALEXNET_CFG\n",
|
| 624 |
-
"\n",
|
| 625 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 626 |
-
" x = inputs\n",
|
| 627 |
-
"\n",
|
| 628 |
-
" for i, filters in enumerate(cfg['conv_filters'], start=1):\n",
|
| 629 |
-
" x = layers.Conv2D(filters, (3, 3), activation='relu',\n",
|
| 630 |
-
" padding='same', name=f'alex_conv_{i}')(x)\n",
|
| 631 |
-
"\n",
|
| 632 |
-
" x = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2),\n",
|
| 633 |
-
" padding='same', name='alex_pool')(x)\n",
|
| 634 |
-
" x = layers.Flatten(name='alex_flatten')(x)\n",
|
| 635 |
-
"\n",
|
| 636 |
-
" fc_names = ['alex_fc1', 'alex_fc2', 'alex_fc3', 'alex_fc4']\n",
|
| 637 |
-
" drop_names = ['TRAIN_DROPOUT_1', 'TRAIN_DROPOUT_2', 'TRAIN_DROPOUT_3', None]\n",
|
| 638 |
-
"\n",
|
| 639 |
-
" for units, fc_name, drop_name in zip(cfg['dense_units'], fc_names, drop_names):\n",
|
| 640 |
-
" x = layers.Dense(units, activation='relu', name=fc_name)(x)\n",
|
| 641 |
-
" if drop_name:\n",
|
| 642 |
-
" x = layers.Dropout(dropout_rate, name=drop_name)(x)\n",
|
| 643 |
-
"\n",
|
| 644 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='alex_logits')(x)\n",
|
| 645 |
-
" return keras.Model(inputs, outputs, name='AlexNet_SingleHead')"
|
| 646 |
-
]
|
| 647 |
-
},
|
| 648 |
-
{
|
| 649 |
-
"cell_type": "markdown",
|
| 650 |
-
"id": "c2b2a28e",
|
| 651 |
-
"metadata": {
|
| 652 |
-
"id": "c2b2a28e"
|
| 653 |
-
},
|
| 654 |
-
"source": [
|
| 655 |
-
"## 4.3 \u2014 Global Filter Network (GFNet)"
|
| 656 |
-
]
|
| 657 |
-
},
|
| 658 |
-
{
|
| 659 |
-
"cell_type": "code",
|
| 660 |
-
"execution_count": null,
|
| 661 |
-
"id": "41818eea",
|
| 662 |
-
"metadata": {
|
| 663 |
-
"id": "41818eea",
|
| 664 |
-
"executionInfo": {
|
| 665 |
-
"status": "ok",
|
| 666 |
-
"timestamp": 1780323236838,
|
| 667 |
-
"user_tz": -330,
|
| 668 |
-
"elapsed": 2,
|
| 669 |
-
"user": {
|
| 670 |
-
"displayName": "Randy Orton",
|
| 671 |
-
"userId": "11725427482974841050"
|
| 672 |
-
}
|
| 673 |
-
}
|
| 674 |
-
},
|
| 675 |
-
"outputs": [],
|
| 676 |
-
"source": [
|
| 677 |
-
"def gf_block(x, token_side, dim, mlp_ratio=4, dropout_rate=0.25, name_prefix='gf'):\n",
|
| 678 |
-
" \"\"\"One GFNet block: Global Filter \u2192 LayerNorm \u2192 MLP \u2192 residual add.\"\"\"\n",
|
| 679 |
-
" y = layers.LayerNormalization(name=f'{name_prefix}_ln1')(x)\n",
|
| 680 |
-
" y = GlobalFilterLayer(token_side, name=f'{name_prefix}_gfilter')(y)\n",
|
| 681 |
-
" y = layers.LayerNormalization(name=f'{name_prefix}_ln2')(y)\n",
|
| 682 |
-
" y = layers.Dense(dim * mlp_ratio, activation=tf.keras.activations.gelu,\n",
|
| 683 |
-
" name=f'{name_prefix}_mlp1')(y)\n",
|
| 684 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop1')(y)\n",
|
| 685 |
-
" y = layers.Dense(dim, activation=tf.keras.activations.gelu,\n",
|
| 686 |
-
" name=f'{name_prefix}_mlp2')(y)\n",
|
| 687 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop2')(y)\n",
|
| 688 |
-
" return layers.Add(name=f'{name_prefix}_add')([x, y])\n",
|
| 689 |
-
"\n",
|
| 690 |
-
"\n",
|
| 691 |
-
"def build_gfnet(input_shape, num_classes, inner_patch=3,\n",
|
| 692 |
-
" hidden_dim=512, num_blocks=5, mlp_ratio=4, dropout_rate=0.25):\n",
|
| 693 |
-
" \"\"\"Build a single-head Global Filter Network over tokenised image patches.\"\"\"\n",
|
| 694 |
-
" num_patches = (input_shape[0] // inner_patch) * (input_shape[1] // inner_patch)\n",
|
| 695 |
-
" token_side = int(np.sqrt(num_patches))\n",
|
| 696 |
-
"\n",
|
| 697 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 698 |
-
" x = PatchExtractor(inner_patch, name='gf_patch_extractor')(inputs)\n",
|
| 699 |
-
" x = PatchPositionEncoder(num_patches, hidden_dim, name='gf_patch_encoder')(x)\n",
|
| 700 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_1')(x)\n",
|
| 701 |
-
"\n",
|
| 702 |
-
" for i in range(num_blocks):\n",
|
| 703 |
-
" x = gf_block(x, token_side, hidden_dim, mlp_ratio,\n",
|
| 704 |
-
" dropout_rate, name_prefix=f'gf_block_{i+1}')\n",
|
| 705 |
-
"\n",
|
| 706 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_2')(x)\n",
|
| 707 |
-
" x = layers.LayerNormalization(name='gf_final_ln')(x)\n",
|
| 708 |
-
" x = layers.GlobalAveragePooling1D(name='gf_gap')(x)\n",
|
| 709 |
-
" x = layers.Flatten(name='gf_flatten')(x)\n",
|
| 710 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_3')(x)\n",
|
| 711 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='gf_logits')(x)\n",
|
| 712 |
-
"\n",
|
| 713 |
-
" return keras.Model(inputs, outputs, name='GFNet_SingleHead')"
|
| 714 |
-
]
|
| 715 |
-
},
|
| 716 |
-
{
|
| 717 |
-
"cell_type": "markdown",
|
| 718 |
-
"id": "bdb53de0",
|
| 719 |
-
"metadata": {
|
| 720 |
-
"id": "bdb53de0"
|
| 721 |
-
},
|
| 722 |
-
"source": [
|
| 723 |
-
"## 4.4 \u2014 Vision Transformer with U-Net Skip Connections"
|
| 724 |
-
]
|
| 725 |
-
},
|
| 726 |
-
{
|
| 727 |
-
"cell_type": "code",
|
| 728 |
-
"execution_count": null,
|
| 729 |
-
"id": "9fe9b56b",
|
| 730 |
-
"metadata": {
|
| 731 |
-
"id": "9fe9b56b",
|
| 732 |
-
"executionInfo": {
|
| 733 |
-
"status": "ok",
|
| 734 |
-
"timestamp": 1780323236843,
|
| 735 |
-
"user_tz": -330,
|
| 736 |
-
"elapsed": 3,
|
| 737 |
-
"user": {
|
| 738 |
-
"displayName": "Randy Orton",
|
| 739 |
-
"userId": "11725427482974841050"
|
| 740 |
-
}
|
| 741 |
-
}
|
| 742 |
-
},
|
| 743 |
-
"outputs": [],
|
| 744 |
-
"source": [
|
| 745 |
-
"def transformer_block(x, num_heads, projection_dim, mlp_dim, dropout_rate, name_prefix):\n",
|
| 746 |
-
" \"\"\"Standard pre-LN transformer block: Multi-Head Attention + MLP with residual adds.\"\"\"\n",
|
| 747 |
-
" y = layers.LayerNormalization(epsilon=1e-6, name=f'{name_prefix}_ln1')(x)\n",
|
| 748 |
-
" y = layers.MultiHeadAttention(\n",
|
| 749 |
-
" num_heads=num_heads, key_dim=projection_dim,\n",
|
| 750 |
-
" dropout=dropout_rate, name=f'{name_prefix}_mha'\n",
|
| 751 |
-
" )(y, y)\n",
|
| 752 |
-
" x = layers.Add(name=f'{name_prefix}_add1')([y, x])\n",
|
| 753 |
-
"\n",
|
| 754 |
-
" y = layers.LayerNormalization(epsilon=1e-6, name=f'{name_prefix}_ln2')(x)\n",
|
| 755 |
-
" y = layers.Dense(mlp_dim, activation=tf.keras.activations.gelu,\n",
|
| 756 |
-
" name=f'{name_prefix}_mlp1')(y)\n",
|
| 757 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop1')(y)\n",
|
| 758 |
-
" y = layers.Dense(projection_dim, activation=tf.keras.activations.gelu,\n",
|
| 759 |
-
" name=f'{name_prefix}_mlp2')(y)\n",
|
| 760 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop2')(y)\n",
|
| 761 |
-
" return layers.Add(name=f'{name_prefix}_add2')([y, x])\n",
|
| 762 |
-
"\n",
|
| 763 |
-
"\n",
|
| 764 |
-
"def build_vit_unet_singlehead(\n",
|
| 765 |
-
" input_shape, num_classes, inner_patch=3,\n",
|
| 766 |
-
" projection_dim=256, num_heads=4, transformer_layers=12,\n",
|
| 767 |
-
" mlp_multiplier=2, dropout_rate=0.25, head_units=(512, 256, 128, 64)\n",
|
| 768 |
-
"):\n",
|
| 769 |
-
" \"\"\"Build a single-head ViT with encoder-decoder skip connections (U-Net style).\"\"\"\n",
|
| 770 |
-
" num_patches = (input_shape[0] // inner_patch) * (input_shape[1] // inner_patch)\n",
|
| 771 |
-
"\n",
|
| 772 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 773 |
-
" x = PatchExtractor(inner_patch, name='vit_patch_extractor')(inputs)\n",
|
| 774 |
-
" x = PatchEncoderWithCLS(num_patches, projection_dim, name='vit_patch_encoder')(x)\n",
|
| 775 |
-
"\n",
|
| 776 |
-
" block_list = []\n",
|
| 777 |
-
" for i in range(transformer_layers):\n",
|
| 778 |
-
" x = transformer_block(\n",
|
| 779 |
-
" x, num_heads=num_heads, projection_dim=projection_dim,\n",
|
| 780 |
-
" mlp_dim=projection_dim * mlp_multiplier,\n",
|
| 781 |
-
" dropout_rate=0.1, name_prefix=f'vit_block_{i+1}'\n",
|
| 782 |
-
" )\n",
|
| 783 |
-
" if i <= transformer_layers // 2:\n",
|
| 784 |
-
" block_list.append(x)\n",
|
| 785 |
-
" else:\n",
|
| 786 |
-
" x = layers.Add(name=f'vit_skip_add_{i+1}')(\n",
|
| 787 |
-
" [x, block_list[transformer_layers - i - 1]]\n",
|
| 788 |
-
" )\n",
|
| 789 |
-
"\n",
|
| 790 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_1')(x)\n",
|
| 791 |
-
" x = layers.LayerNormalization(epsilon=1e-6, name='vit_cls_norm')(x)\n",
|
| 792 |
-
" cls_token = layers.Lambda(lambda t: t[:, 0, :], name='vit_cls_token')(x)\n",
|
| 793 |
-
"\n",
|
| 794 |
-
" drop_slots = ['TRAIN_DROPOUT_3', None, 'TRAIN_DROPOUT_5', 'TRAIN_DROPOUT_6']\n",
|
| 795 |
-
" head_names = ['vit_head_1', 'vit_head_2', 'vit_head_3', 'vit_head_4']\n",
|
| 796 |
-
" y = cls_token\n",
|
| 797 |
-
" for units, hname, dname in zip(head_units, head_names, drop_slots):\n",
|
| 798 |
-
" y = layers.Dense(units, activation=tf.keras.activations.gelu, name=hname)(y)\n",
|
| 799 |
-
" if dname:\n",
|
| 800 |
-
" y = layers.Dropout(dropout_rate, name=dname)(y)\n",
|
| 801 |
-
"\n",
|
| 802 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='vit_logits')(y)\n",
|
| 803 |
-
" return keras.Model(inputs, outputs, name='ViT_UNet_SingleHead')"
|
| 804 |
-
]
|
| 805 |
-
},
|
| 806 |
-
{
|
| 807 |
-
"cell_type": "markdown",
|
| 808 |
-
"id": "1cdf93bb",
|
| 809 |
-
"metadata": {
|
| 810 |
-
"id": "1cdf93bb"
|
| 811 |
-
},
|
| 812 |
-
"source": [
|
| 813 |
-
"# 5.0 \u2014 Training Helpers\n",
|
| 814 |
-
"\n",
|
| 815 |
-
"Utility functions for calibration metrics, optimiser construction, and the formatted Excel report writer."
|
| 816 |
-
]
|
| 817 |
-
},
|
| 818 |
-
{
|
| 819 |
-
"cell_type": "markdown",
|
| 820 |
-
"id": "fb0c5167",
|
| 821 |
-
"metadata": {
|
| 822 |
-
"id": "fb0c5167"
|
| 823 |
-
},
|
| 824 |
-
"source": [
|
| 825 |
-
"## 5.1 \u2014 Calibration Metrics"
|
| 826 |
-
]
|
| 827 |
-
},
|
| 828 |
-
{
|
| 829 |
-
"cell_type": "code",
|
| 830 |
-
"execution_count": null,
|
| 831 |
-
"id": "59b6cc9e",
|
| 832 |
-
"metadata": {
|
| 833 |
-
"id": "59b6cc9e",
|
| 834 |
-
"executionInfo": {
|
| 835 |
-
"status": "ok",
|
| 836 |
-
"timestamp": 1780323238963,
|
| 837 |
-
"user_tz": -330,
|
| 838 |
-
"elapsed": 16,
|
| 839 |
-
"user": {
|
| 840 |
-
"displayName": "Randy Orton",
|
| 841 |
-
"userId": "11725427482974841050"
|
| 842 |
-
}
|
| 843 |
-
}
|
| 844 |
-
},
|
| 845 |
-
"outputs": [],
|
| 846 |
-
"source": [
|
| 847 |
-
"def multiclass_brier_score(y_onehot, y_prob):\n",
|
| 848 |
-
" \"\"\"Compute the multiclass Brier score (mean squared probability error).\"\"\"\n",
|
| 849 |
-
" return float(np.mean(np.sum((y_prob - y_onehot) ** 2, axis=1)))\n",
|
| 850 |
-
"\n",
|
| 851 |
-
"\n",
|
| 852 |
-
"def expected_calibration_error(y_true, y_prob, n_bins=15):\n",
|
| 853 |
-
" \"\"\"Compute Expected Calibration Error (ECE) using equal-width confidence bins.\"\"\"\n",
|
| 854 |
-
" confidences = np.max(y_prob, axis=1)\n",
|
| 855 |
-
" predictions = np.argmax(y_prob, axis=1)\n",
|
| 856 |
-
" correct = (predictions == y_true).astype(np.float32)\n",
|
| 857 |
-
" bin_edges = np.linspace(0.0, 1.0, n_bins + 1)\n",
|
| 858 |
-
" ece = 0.0\n",
|
| 859 |
-
" for i in range(n_bins):\n",
|
| 860 |
-
" lo, hi = bin_edges[i], bin_edges[i + 1]\n",
|
| 861 |
-
" in_bin = (confidences >= lo) & (\n",
|
| 862 |
-
" confidences <= hi if i == n_bins - 1 else confidences < hi\n",
|
| 863 |
-
" )\n",
|
| 864 |
-
" prop = np.mean(in_bin)\n",
|
| 865 |
-
" if prop > 0:\n",
|
| 866 |
-
" ece += np.abs(np.mean(correct[in_bin]) - np.mean(confidences[in_bin])) * prop\n",
|
| 867 |
-
" return float(ece)"
|
| 868 |
-
]
|
| 869 |
-
},
|
| 870 |
-
{
|
| 871 |
-
"cell_type": "markdown",
|
| 872 |
-
"id": "cb7cf04e",
|
| 873 |
-
"metadata": {
|
| 874 |
-
"id": "cb7cf04e"
|
| 875 |
-
},
|
| 876 |
-
"source": [
|
| 877 |
-
"## 5.2 \u2014 Optimiser & LR Schedule"
|
| 878 |
-
]
|
| 879 |
-
},
|
| 880 |
-
{
|
| 881 |
-
"cell_type": "code",
|
| 882 |
-
"execution_count": null,
|
| 883 |
-
"id": "77799a41",
|
| 884 |
-
"metadata": {
|
| 885 |
-
"id": "77799a41",
|
| 886 |
-
"executionInfo": {
|
| 887 |
-
"status": "ok",
|
| 888 |
-
"timestamp": 1780323239013,
|
| 889 |
-
"user_tz": -330,
|
| 890 |
-
"elapsed": 40,
|
| 891 |
-
"user": {
|
| 892 |
-
"displayName": "Randy Orton",
|
| 893 |
-
"userId": "11725427482974841050"
|
| 894 |
-
}
|
| 895 |
-
}
|
| 896 |
-
},
|
| 897 |
-
"outputs": [],
|
| 898 |
-
"source": [
|
| 899 |
-
"def make_adamw_optimizer(num_train_samples):\n",
|
| 900 |
-
" \"\"\"Build a cosine-decay AdamW optimiser (used for GFNet and ViT).\"\"\"\n",
|
| 901 |
-
" steps_per_epoch = int(np.ceil(num_train_samples / BATCH_SIZE))\n",
|
| 902 |
-
" decay_steps = max(1, steps_per_epoch * EPOCHS)\n",
|
| 903 |
-
" lr_schedule = keras.optimizers.schedules.CosineDecay(\n",
|
| 904 |
-
" initial_learning_rate=LEARNING_RATE,\n",
|
| 905 |
-
" decay_steps=decay_steps,\n",
|
| 906 |
-
" alpha=TRAIN_CFG['cosine_alpha'],\n",
|
| 907 |
-
" )\n",
|
| 908 |
-
" return keras.optimizers.AdamW(\n",
|
| 909 |
-
" learning_rate=lr_schedule,\n",
|
| 910 |
-
" weight_decay=TRAIN_CFG['weight_decay'],\n",
|
| 911 |
-
" clipnorm=TRAIN_CFG['clipnorm'],\n",
|
| 912 |
-
" )\n",
|
| 913 |
-
"\n",
|
| 914 |
-
"\n",
|
| 915 |
-
"def _alexnet_legacy_lr(epoch):\n",
|
| 916 |
-
" \"\"\"Cosine LR schedule oscillating between ALEXNET_LR_MIN and ALEXNET_LR_MAX.\"\"\"\n",
|
| 917 |
-
" if EPOCHS <= 1:\n",
|
| 918 |
-
" return ALEXNET_LR_START\n",
|
| 919 |
-
" phase = np.pi * epoch / (EPOCHS - 1)\n",
|
| 920 |
-
" cosine_decay = 0.5 * (1.0 + np.cos(phase))\n",
|
| 921 |
-
" return float((ALEXNET_LR_MAX - ALEXNET_LR_MIN) * cosine_decay + ALEXNET_LR_MIN)"
|
| 922 |
-
]
|
| 923 |
-
},
|
| 924 |
-
{
|
| 925 |
-
"cell_type": "markdown",
|
| 926 |
-
"id": "fa69c58f",
|
| 927 |
-
"metadata": {
|
| 928 |
-
"id": "fa69c58f"
|
| 929 |
-
},
|
| 930 |
-
"source": [
|
| 931 |
-
"## 5.3 \u2014 Excel Report Helpers\n",
|
| 932 |
-
"\n",
|
| 933 |
-
"Style constants and helper functions for writing DataFrames and plots into a formatted `.xlsx` workbook."
|
| 934 |
-
]
|
| 935 |
-
},
|
| 936 |
-
{
|
| 937 |
-
"cell_type": "code",
|
| 938 |
-
"execution_count": null,
|
| 939 |
-
"id": "04c2c0ec",
|
| 940 |
-
"metadata": {
|
| 941 |
-
"id": "04c2c0ec",
|
| 942 |
-
"executionInfo": {
|
| 943 |
-
"status": "ok",
|
| 944 |
-
"timestamp": 1780323239018,
|
| 945 |
-
"user_tz": -330,
|
| 946 |
-
"elapsed": 3,
|
| 947 |
-
"user": {
|
| 948 |
-
"displayName": "Randy Orton",
|
| 949 |
-
"userId": "11725427482974841050"
|
| 950 |
-
}
|
| 951 |
-
}
|
| 952 |
-
},
|
| 953 |
-
"outputs": [],
|
| 954 |
-
"source": [
|
| 955 |
-
"# \u2500\u2500 Style constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 956 |
-
"_HDR_FILL = PatternFill('solid', start_color='1F4E79')\n",
|
| 957 |
-
"_ALT_FILL = PatternFill('solid', start_color='D6E4F0')\n",
|
| 958 |
-
"_HDR_FONT = Font(name='Arial', bold=True, color='FFFFFF', size=11)\n",
|
| 959 |
-
"_BODY_FONT = Font(name='Arial', size=10)\n",
|
| 960 |
-
"_TITLE_FONT = Font(name='Arial', bold=True, size=13)\n",
|
| 961 |
-
"_CENTER = Alignment(horizontal='center', vertical='center', wrap_text=True)\n",
|
| 962 |
-
"_LEFT = Alignment(horizontal='left', vertical='center')\n",
|
| 963 |
-
"_THIN_SIDE = Side(style='thin', color='AAAAAA')\n",
|
| 964 |
-
"_THIN_BORDER = Border(\n",
|
| 965 |
-
" left=_THIN_SIDE, right=_THIN_SIDE, top=_THIN_SIDE, bottom=_THIN_SIDE\n",
|
| 966 |
-
")\n",
|
| 967 |
-
"\n",
|
| 968 |
-
"\n",
|
| 969 |
-
"def _style_header_row(ws, row, col_start, col_end):\n",
|
| 970 |
-
" \"\"\"Apply dark-blue header styling to a worksheet row range.\"\"\"\n",
|
| 971 |
-
" for c in range(col_start, col_end + 1):\n",
|
| 972 |
-
" cell = ws.cell(row=row, column=c)\n",
|
| 973 |
-
" cell.fill = _HDR_FILL\n",
|
| 974 |
-
" cell.font = _HDR_FONT\n",
|
| 975 |
-
" cell.alignment = _CENTER\n",
|
| 976 |
-
" cell.border = _THIN_BORDER\n",
|
| 977 |
-
"\n",
|
| 978 |
-
"\n",
|
| 979 |
-
"def _style_data_rows(ws, row_start, row_end, col_start, col_end):\n",
|
| 980 |
-
" \"\"\"Apply alternating-row fill and border to a worksheet data range.\"\"\"\n",
|
| 981 |
-
" for r in range(row_start, row_end + 1):\n",
|
| 982 |
-
" fill = _ALT_FILL if r % 2 == 0 else PatternFill()\n",
|
| 983 |
-
" for c in range(col_start, col_end + 1):\n",
|
| 984 |
-
" cell = ws.cell(row=r, column=c)\n",
|
| 985 |
-
" cell.font = _BODY_FONT\n",
|
| 986 |
-
" cell.fill = fill\n",
|
| 987 |
-
" cell.alignment = _LEFT\n",
|
| 988 |
-
" cell.border = _THIN_BORDER\n",
|
| 989 |
-
"\n",
|
| 990 |
-
"\n",
|
| 991 |
-
"def _write_df_to_sheet(ws, df, start_row=1, start_col=1, title=None):\n",
|
| 992 |
-
" \"\"\"Write a DataFrame into a worksheet with headers, alternating rows, and auto-width columns.\"\"\"\n",
|
| 993 |
-
" r = start_row\n",
|
| 994 |
-
" if title:\n",
|
| 995 |
-
" cell = ws.cell(row=r, column=start_col, value=title)\n",
|
| 996 |
-
" cell.font = _TITLE_FONT\n",
|
| 997 |
-
" cell.alignment = _LEFT\n",
|
| 998 |
-
" ws.merge_cells(\n",
|
| 999 |
-
" start_row=r, start_column=start_col,\n",
|
| 1000 |
-
" end_row=r, end_column=start_col + len(df.columns) - 1\n",
|
| 1001 |
-
" )\n",
|
| 1002 |
-
" r += 1\n",
|
| 1003 |
-
"\n",
|
| 1004 |
-
" # Header row\n",
|
| 1005 |
-
" for j, col_name in enumerate(df.columns, start=start_col):\n",
|
| 1006 |
-
" ws.cell(row=r, column=j, value=col_name)\n",
|
| 1007 |
-
" _style_header_row(ws, r, start_col, start_col + len(df.columns) - 1)\n",
|
| 1008 |
-
" r += 1\n",
|
| 1009 |
-
"\n",
|
| 1010 |
-
" # Data rows\n",
|
| 1011 |
-
" data_start = r\n",
|
| 1012 |
-
" for _, row_data in df.iterrows():\n",
|
| 1013 |
-
" for j, val in enumerate(row_data, start=start_col):\n",
|
| 1014 |
-
" ws.cell(row=r, column=j,\n",
|
| 1015 |
-
" value=round(float(val), 6)\n",
|
| 1016 |
-
" if isinstance(val, (float, np.floating)) else val)\n",
|
| 1017 |
-
" r += 1\n",
|
| 1018 |
-
" _style_data_rows(ws, data_start, r - 1, start_col, start_col + len(df.columns) - 1)\n",
|
| 1019 |
-
"\n",
|
| 1020 |
-
" # Auto column width\n",
|
| 1021 |
-
" for j, col_name in enumerate(df.columns, start=start_col):\n",
|
| 1022 |
-
" max_len = max(\n",
|
| 1023 |
-
" len(str(col_name)),\n",
|
| 1024 |
-
" max((len(str(ws.cell(row=row, column=j).value or ''))\n",
|
| 1025 |
-
" for row in range(data_start, r)), default=0)\n",
|
| 1026 |
-
" )\n",
|
| 1027 |
-
" ws.column_dimensions[get_column_letter(j)].width = min(max_len + 4, 40)\n",
|
| 1028 |
-
"\n",
|
| 1029 |
-
" return r # next free row"
|
| 1030 |
-
]
|
| 1031 |
-
},
|
| 1032 |
-
{
|
| 1033 |
-
"cell_type": "markdown",
|
| 1034 |
-
"id": "487d2188",
|
| 1035 |
-
"metadata": {
|
| 1036 |
-
"id": "487d2188"
|
| 1037 |
-
},
|
| 1038 |
-
"source": [
|
| 1039 |
-
"## 5.4 \u2014 Excel Report Builder\n",
|
| 1040 |
-
"\n",
|
| 1041 |
-
"Assembles the full `CREDIT_Results.xlsx` workbook with four sheets: Training Summary, Evaluation Summary, Confusion Matrices, and Plots."
|
| 1042 |
-
]
|
| 1043 |
-
},
|
| 1044 |
-
{
|
| 1045 |
-
"cell_type": "code",
|
| 1046 |
-
"execution_count": null,
|
| 1047 |
-
"id": "80e777c5",
|
| 1048 |
-
"metadata": {
|
| 1049 |
-
"id": "80e777c5",
|
| 1050 |
-
"executionInfo": {
|
| 1051 |
-
"status": "ok",
|
| 1052 |
-
"timestamp": 1780323239031,
|
| 1053 |
-
"user_tz": -330,
|
| 1054 |
-
"elapsed": 3,
|
| 1055 |
-
"user": {
|
| 1056 |
-
"displayName": "Randy Orton",
|
| 1057 |
-
"userId": "11725427482974841050"
|
| 1058 |
-
}
|
| 1059 |
-
}
|
| 1060 |
-
},
|
| 1061 |
-
"outputs": [],
|
| 1062 |
-
"source": [
|
| 1063 |
-
"def create_credit_excel_report(\n",
|
| 1064 |
-
" results_dir,\n",
|
| 1065 |
-
" train_summary_df,\n",
|
| 1066 |
-
" eval_summary_df,\n",
|
| 1067 |
-
" credit_artifacts_dict,\n",
|
| 1068 |
-
" plot_paths # list of (label_str, image_path_str)\n",
|
| 1069 |
-
"):\n",
|
| 1070 |
-
" \"\"\"\n",
|
| 1071 |
-
" Create CREDIT_Results.xlsx with four sheets:\n",
|
| 1072 |
-
" Sheet 1 \u2014 Training Summary\n",
|
| 1073 |
-
" Sheet 2 \u2014 Evaluation Summary\n",
|
| 1074 |
-
" Sheet 3 \u2014 Confusion Matrices (per model, side by side)\n",
|
| 1075 |
-
" Sheet 4 \u2014 Plots (one image per row)\n",
|
| 1076 |
-
" \"\"\"\n",
|
| 1077 |
-
" xlsx_path = Path(results_dir) / 'CREDIT_Results.xlsx'\n",
|
| 1078 |
-
" wb = Workbook()\n",
|
| 1079 |
-
"\n",
|
| 1080 |
-
" # \u2500\u2500 Sheet 1: Training Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1081 |
-
" ws1 = wb.active\n",
|
| 1082 |
-
" ws1.title = 'Training Summary'\n",
|
| 1083 |
-
" ws1.row_dimensions[1].height = 30\n",
|
| 1084 |
-
" _write_df_to_sheet(ws1, train_summary_df,\n",
|
| 1085 |
-
" start_row=1, title='CREDIT \u2014 Training Uncertainty Summary')\n",
|
| 1086 |
-
"\n",
|
| 1087 |
-
" # \u2500\u2500 Sheet 2: Evaluation Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1088 |
-
" ws2 = wb.create_sheet('Evaluation Summary')\n",
|
| 1089 |
-
" _write_df_to_sheet(ws2, eval_summary_df,\n",
|
| 1090 |
-
" start_row=1, title='CREDIT \u2014 Full Evaluation Metrics')\n",
|
| 1091 |
-
"\n",
|
| 1092 |
-
" # \u2500\u2500 Sheet 3: Confusion Matrices \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1093 |
-
" ws3 = wb.create_sheet('Confusion Matrices')\n",
|
| 1094 |
-
" col_cursor = 1\n",
|
| 1095 |
-
" for mname, art in credit_artifacts_dict.items():\n",
|
| 1096 |
-
" cm = art['confusion_matrix']\n",
|
| 1097 |
-
" rep = art['report']\n",
|
| 1098 |
-
" n_cls = cm.shape[0]\n",
|
| 1099 |
-
"\n",
|
| 1100 |
-
" # Title\n",
|
| 1101 |
-
" cell = ws3.cell(row=1, column=col_cursor,\n",
|
| 1102 |
-
" value=f'{mname} \u2014 Confusion Matrix')\n",
|
| 1103 |
-
" cell.font = _TITLE_FONT\n",
|
| 1104 |
-
" ws3.merge_cells(start_row=1, start_column=col_cursor,\n",
|
| 1105 |
-
" end_row=1, end_column=col_cursor + n_cls)\n",
|
| 1106 |
-
"\n",
|
| 1107 |
-
" # Column headers (Predicted)\n",
|
| 1108 |
-
" ws3.cell(row=2, column=col_cursor,\n",
|
| 1109 |
-
" value='True \\\\ Pred').font = Font(bold=True, name='Arial')\n",
|
| 1110 |
-
" for j in range(n_cls):\n",
|
| 1111 |
-
" c = ws3.cell(row=2, column=col_cursor + 1 + j, value=f'C{j+1}')\n",
|
| 1112 |
-
" c.font = Font(bold=True, name='Arial', color='FFFFFF')\n",
|
| 1113 |
-
" c.fill = _HDR_FILL\n",
|
| 1114 |
-
" c.alignment = _CENTER\n",
|
| 1115 |
-
"\n",
|
| 1116 |
-
" # Data rows\n",
|
| 1117 |
-
" for i in range(n_cls):\n",
|
| 1118 |
-
" ws3.cell(row=3 + i, column=col_cursor,\n",
|
| 1119 |
-
" value=f'C{i+1}').font = Font(bold=True, name='Arial')\n",
|
| 1120 |
-
" for j in range(n_cls):\n",
|
| 1121 |
-
" val = int(cm[i, j])\n",
|
| 1122 |
-
" cell = ws3.cell(row=3 + i, column=col_cursor + 1 + j, value=val)\n",
|
| 1123 |
-
" cell.alignment = _CENTER\n",
|
| 1124 |
-
" cell.border = _THIN_BORDER\n",
|
| 1125 |
-
" if i == j: # diagonal \u2192 green\n",
|
| 1126 |
-
" cell.fill = PatternFill('solid', start_color='C6EFCE')\n",
|
| 1127 |
-
" cell.font = Font(name='Arial', bold=True, color='276221')\n",
|
| 1128 |
-
" else:\n",
|
| 1129 |
-
" cell.font = _BODY_FONT\n",
|
| 1130 |
-
"\n",
|
| 1131 |
-
" # Per-class F1 from classification report\n",
|
| 1132 |
-
" f1_row = 3 + n_cls + 1\n",
|
| 1133 |
-
" ws3.cell(row=f1_row, column=col_cursor,\n",
|
| 1134 |
-
" value='Per-Class F1').font = Font(bold=True, name='Arial')\n",
|
| 1135 |
-
" for j in range(n_cls):\n",
|
| 1136 |
-
" key = str(j)\n",
|
| 1137 |
-
" f1 = rep.get(key, {}).get('f1-score', '')\n",
|
| 1138 |
-
" ws3.cell(row=f1_row, column=col_cursor + 1 + j,\n",
|
| 1139 |
-
" value=round(f1, 4) if f1 != '' else '').font = _BODY_FONT\n",
|
| 1140 |
-
"\n",
|
| 1141 |
-
" col_cursor += n_cls + 3 # gap between model tables\n",
|
| 1142 |
-
"\n",
|
| 1143 |
-
" # \u2500\u2500 Sheet 4: Plots \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1144 |
-
" ws4 = wb.create_sheet('Plots')\n",
|
| 1145 |
-
" title_cell = ws4.cell(row=1, column=1, value='CREDIT \u2014 All Output Plots')\n",
|
| 1146 |
-
" title_cell.font = Font(name='Arial', bold=True, size=14)\n",
|
| 1147 |
-
"\n",
|
| 1148 |
-
" img_row = 3\n",
|
| 1149 |
-
" for label, img_path in plot_paths:\n",
|
| 1150 |
-
" if not Path(img_path).exists():\n",
|
| 1151 |
-
" continue\n",
|
| 1152 |
-
" ws4.cell(row=img_row, column=1,\n",
|
| 1153 |
-
" value=label).font = Font(name='Arial', bold=True, size=11)\n",
|
| 1154 |
-
" img_row += 1\n",
|
| 1155 |
-
"\n",
|
| 1156 |
-
" xl_img = XLImage(img_path)\n",
|
| 1157 |
-
" orig_w, orig_h = xl_img.width, xl_img.height\n",
|
| 1158 |
-
" target_w = 900\n",
|
| 1159 |
-
" scale = target_w / orig_w if orig_w > 0 else 1\n",
|
| 1160 |
-
" xl_img.width = int(orig_w * scale)\n",
|
| 1161 |
-
" xl_img.height = int(orig_h * scale)\n",
|
| 1162 |
-
"\n",
|
| 1163 |
-
" cell_addr = f'A{img_row}'\n",
|
| 1164 |
-
" ws4.add_image(xl_img, cell_addr)\n",
|
| 1165 |
-
" # openpyxl row height is in points; 1pt \u2248 0.75px\n",
|
| 1166 |
-
" ws4.row_dimensions[img_row].height = xl_img.height * 0.75\n",
|
| 1167 |
-
" img_row += int(xl_img.height / 15) + 3\n",
|
| 1168 |
-
"\n",
|
| 1169 |
-
" wb.save(xlsx_path)\n",
|
| 1170 |
-
" print(f'\\n\u2705 Excel report saved \u2192 {xlsx_path}')\n",
|
| 1171 |
-
" return str(xlsx_path)"
|
| 1172 |
-
]
|
| 1173 |
-
},
|
| 1174 |
-
{
|
| 1175 |
-
"cell_type": "markdown",
|
| 1176 |
-
"id": "04e1cb62",
|
| 1177 |
-
"metadata": {
|
| 1178 |
-
"id": "04e1cb62"
|
| 1179 |
-
},
|
| 1180 |
-
"source": [
|
| 1181 |
-
"# 6.0 \u2014 CREDIT Distillation\n",
|
| 1182 |
-
"\n",
|
| 1183 |
-
"Generates soft targets from the pre-trained ensemble teachers, builds a dual-head student for each architecture, and trains it via KL divergence (aleatoric head) + MSE (epistemic head)."
|
| 1184 |
-
]
|
| 1185 |
-
},
|
| 1186 |
-
{
|
| 1187 |
-
"cell_type": "markdown",
|
| 1188 |
-
"id": "13bc3c0f",
|
| 1189 |
-
"metadata": {
|
| 1190 |
-
"id": "13bc3c0f"
|
| 1191 |
-
},
|
| 1192 |
-
"source": [
|
| 1193 |
-
"## 6.1 \u2014 Soft Target Generation\n",
|
| 1194 |
-
"\n",
|
| 1195 |
-
"Loads each ensemble teacher, runs inference over the training data, and derives CREDIT targets: `p_star` (normalised per-class minimum \u2014 aleatoric proxy) and `delta_p` (per-class prediction spread \u2014 epistemic proxy)."
|
| 1196 |
-
]
|
| 1197 |
-
},
|
| 1198 |
-
{
|
| 1199 |
-
"cell_type": "code",
|
| 1200 |
-
"execution_count": null,
|
| 1201 |
-
"id": "5b2a9527",
|
| 1202 |
-
"metadata": {
|
| 1203 |
-
"id": "5b2a9527",
|
| 1204 |
-
"executionInfo": {
|
| 1205 |
-
"status": "ok",
|
| 1206 |
-
"timestamp": 1780323240528,
|
| 1207 |
-
"user_tz": -330,
|
| 1208 |
-
"elapsed": 31,
|
| 1209 |
-
"user": {
|
| 1210 |
-
"displayName": "Randy Orton",
|
| 1211 |
-
"userId": "11725427482974841050"
|
| 1212 |
-
}
|
| 1213 |
-
}
|
| 1214 |
-
},
|
| 1215 |
-
"outputs": [],
|
| 1216 |
-
"source": [
|
| 1217 |
-
"def get_ensemble_paths(model_name):\n",
|
| 1218 |
-
" \"\"\"Locate ensemble teacher weights using three fallback search patterns.\"\"\"\n",
|
| 1219 |
-
" # Primary: standardised location\n",
|
| 1220 |
-
" pattern = str(ENSEMBLE_DIR / f'{model_name}_ens_*_final.keras')\n",
|
| 1221 |
-
" paths = glob.glob(pattern)\n",
|
| 1222 |
-
"\n",
|
| 1223 |
-
" # Fallback 1: sub-folder style\n",
|
| 1224 |
-
" if not paths:\n",
|
| 1225 |
-
" pattern = str(\n",
|
| 1226 |
-
" ENSEMBLE_DIR / f'ensembles_{model_name}' / f'{model_name}_ens_*_final.keras'\n",
|
| 1227 |
-
" )\n",
|
| 1228 |
-
" paths = glob.glob(pattern)\n",
|
| 1229 |
-
"\n",
|
| 1230 |
-
" # Fallback 2: old Credit/models location\n",
|
| 1231 |
-
" if not paths:\n",
|
| 1232 |
-
" pattern = str(MODEL_DIR / f'{model_name}_ens_*_final.keras')\n",
|
| 1233 |
-
" paths = glob.glob(pattern)\n",
|
| 1234 |
-
"\n",
|
| 1235 |
-
" return sorted(paths)\n",
|
| 1236 |
-
"\n",
|
| 1237 |
-
"\n",
|
| 1238 |
-
"def generate_credit_targets(ensemble_paths, x_data, batch_size=128):\n",
|
| 1239 |
-
" \"\"\"\n",
|
| 1240 |
-
" Run each teacher over x_data and derive CREDIT interval targets.\n",
|
| 1241 |
-
"\n",
|
| 1242 |
-
" Returns:\n",
|
| 1243 |
-
" p_star_true : (N, C) normalised per-class minimum (aleatoric proxy)\n",
|
| 1244 |
-
" delta_p_true : (N, C) per-class prediction spread (epistemic proxy)\n",
|
| 1245 |
-
" \"\"\"\n",
|
| 1246 |
-
" all_preds = []\n",
|
| 1247 |
-
" for path in ensemble_paths:\n",
|
| 1248 |
-
" print(f' Loading teacher: {path}')\n",
|
| 1249 |
-
" model = tf.keras.models.load_model(path, compile=False, safe_mode=False)\n",
|
| 1250 |
-
" all_preds.append(model.predict(x_data, batch_size=batch_size, verbose=1))\n",
|
| 1251 |
-
" del model\n",
|
| 1252 |
-
" tf.keras.backend.clear_session()\n",
|
| 1253 |
-
"\n",
|
| 1254 |
-
" stacked = tf.stack(all_preds, axis=0) # (M, N, C)\n",
|
| 1255 |
-
" p_min = tf.reduce_min(stacked, axis=0) # (N, C)\n",
|
| 1256 |
-
" p_max = tf.reduce_max(stacked, axis=0) # (N, C)\n",
|
| 1257 |
-
"\n",
|
| 1258 |
-
" delta_p_true = p_max - p_min\n",
|
| 1259 |
-
" p_star_true = p_min / (tf.reduce_sum(p_min, axis=-1, keepdims=True) + 1e-12)\n",
|
| 1260 |
-
" return p_star_true, delta_p_true"
|
| 1261 |
-
]
|
| 1262 |
-
},
|
| 1263 |
-
{
|
| 1264 |
-
"cell_type": "markdown",
|
| 1265 |
-
"id": "4cfa106a",
|
| 1266 |
-
"metadata": {
|
| 1267 |
-
"id": "4cfa106a"
|
| 1268 |
-
},
|
| 1269 |
-
"source": [
|
| 1270 |
-
"## 6.2 \u2014 CREDIT Student (Dual-Head)\n",
|
| 1271 |
-
"\n",
|
| 1272 |
-
"Wraps any single-head base architecture with two output heads: a softmax head for the aleatoric belief (`p_star`) and a sigmoid head for the epistemic spread (`delta_p`)."
|
| 1273 |
-
]
|
| 1274 |
-
},
|
| 1275 |
-
{
|
| 1276 |
-
"cell_type": "code",
|
| 1277 |
-
"execution_count": null,
|
| 1278 |
-
"id": "0cd5e8ff",
|
| 1279 |
-
"metadata": {
|
| 1280 |
-
"id": "0cd5e8ff",
|
| 1281 |
-
"executionInfo": {
|
| 1282 |
-
"status": "ok",
|
| 1283 |
-
"timestamp": 1780323240533,
|
| 1284 |
-
"user_tz": -330,
|
| 1285 |
-
"elapsed": 2,
|
| 1286 |
-
"user": {
|
| 1287 |
-
"displayName": "Randy Orton",
|
| 1288 |
-
"userId": "11725427482974841050"
|
| 1289 |
-
}
|
| 1290 |
-
}
|
| 1291 |
-
},
|
| 1292 |
-
"outputs": [],
|
| 1293 |
-
"source": [
|
| 1294 |
-
"def build_credit_student(base_builder_func, num_classes):\n",
|
| 1295 |
-
" \"\"\"\n",
|
| 1296 |
-
" Attach CREDIT dual-output heads to any single-head base architecture.\n",
|
| 1297 |
-
"\n",
|
| 1298 |
-
" Args:\n",
|
| 1299 |
-
" base_builder_func: Zero-argument callable that returns a Keras model.\n",
|
| 1300 |
-
" num_classes: Number of target classes.\n",
|
| 1301 |
-
"\n",
|
| 1302 |
-
" Returns:\n",
|
| 1303 |
-
" A Keras model with outputs [p_star (softmax), delta_p (sigmoid)].\n",
|
| 1304 |
-
" \"\"\"\n",
|
| 1305 |
-
" base_model = base_builder_func()\n",
|
| 1306 |
-
" features = base_model.layers[-2].output # penultimate feature tensor\n",
|
| 1307 |
-
"\n",
|
| 1308 |
-
" p_star = layers.Dense(num_classes, activation='softmax', name='p_star' )(features)\n",
|
| 1309 |
-
" delta_p = layers.Dense(num_classes, activation='sigmoid', name='delta_p')(features)\n",
|
| 1310 |
-
"\n",
|
| 1311 |
-
" return tf.keras.Model(\n",
|
| 1312 |
-
" inputs=base_model.input,\n",
|
| 1313 |
-
" outputs=[p_star, delta_p],\n",
|
| 1314 |
-
" name='CREDIT_Student'\n",
|
| 1315 |
-
" )"
|
| 1316 |
-
]
|
| 1317 |
-
},
|
| 1318 |
-
{
|
| 1319 |
-
"cell_type": "markdown",
|
| 1320 |
-
"id": "d86f2e38",
|
| 1321 |
-
"metadata": {
|
| 1322 |
-
"id": "d86f2e38"
|
| 1323 |
-
},
|
| 1324 |
-
"source": [
|
| 1325 |
-
"## 6.3 \u2014 Model Builder Registry\n",
|
| 1326 |
-
"\n",
|
| 1327 |
-
"Centralised registry mapping model names to zero-argument builder lambdas."
|
| 1328 |
-
]
|
| 1329 |
-
},
|
| 1330 |
-
{
|
| 1331 |
-
"cell_type": "code",
|
| 1332 |
-
"execution_count": null,
|
| 1333 |
-
"id": "f40ae339",
|
| 1334 |
-
"metadata": {
|
| 1335 |
-
"id": "f40ae339",
|
| 1336 |
-
"executionInfo": {
|
| 1337 |
-
"status": "ok",
|
| 1338 |
-
"timestamp": 1780323240537,
|
| 1339 |
-
"user_tz": -330,
|
| 1340 |
-
"elapsed": 2,
|
| 1341 |
-
"user": {
|
| 1342 |
-
"displayName": "Randy Orton",
|
| 1343 |
-
"userId": "11725427482974841050"
|
| 1344 |
-
}
|
| 1345 |
-
}
|
| 1346 |
-
},
|
| 1347 |
-
"outputs": [],
|
| 1348 |
-
"source": [
|
| 1349 |
-
"model_builders = {\n",
|
| 1350 |
-
" 'AlexNet_CNN': lambda: build_alexnet(\n",
|
| 1351 |
-
" input_shape, num_classes,\n",
|
| 1352 |
-
" dropout_rate=DROPOUT_RATE, cfg=ALEXNET_CFG\n",
|
| 1353 |
-
" ),\n",
|
| 1354 |
-
" 'GFNet': lambda: build_gfnet(\n",
|
| 1355 |
-
" input_shape, num_classes,\n",
|
| 1356 |
-
" inner_patch=INNER_PATCH,\n",
|
| 1357 |
-
" hidden_dim=GFNET_CFG['hidden_dim'],\n",
|
| 1358 |
-
" num_blocks=GFNET_CFG['num_blocks'],\n",
|
| 1359 |
-
" mlp_ratio=GFNET_CFG['mlp_ratio'],\n",
|
| 1360 |
-
" dropout_rate=DROPOUT_RATE\n",
|
| 1361 |
-
" ),\n",
|
| 1362 |
-
" 'ViT_UNet': lambda: build_vit_unet_singlehead(\n",
|
| 1363 |
-
" input_shape, num_classes,\n",
|
| 1364 |
-
" inner_patch=INNER_PATCH,\n",
|
| 1365 |
-
" projection_dim=VIT_CFG['projection_dim'],\n",
|
| 1366 |
-
" num_heads=VIT_CFG['num_heads'],\n",
|
| 1367 |
-
" transformer_layers=VIT_CFG['transformer_layers'],\n",
|
| 1368 |
-
" mlp_multiplier=VIT_CFG['mlp_multiplier'],\n",
|
| 1369 |
-
" dropout_rate=DROPOUT_RATE,\n",
|
| 1370 |
-
" head_units=tuple(VIT_CFG['head_units'])\n",
|
| 1371 |
-
" ),\n",
|
| 1372 |
-
"}"
|
| 1373 |
-
]
|
| 1374 |
-
},
|
| 1375 |
-
{
|
| 1376 |
-
"cell_type": "markdown",
|
| 1377 |
-
"id": "9f24ca6b",
|
| 1378 |
-
"metadata": {
|
| 1379 |
-
"id": "9f24ca6b"
|
| 1380 |
-
},
|
| 1381 |
-
"source": [
|
| 1382 |
-
"## 6.4 \u2014 Training Loop\n",
|
| 1383 |
-
"\n",
|
| 1384 |
-
"For each architecture: locate ensemble teachers \u2192 generate soft targets \u2192 build `tf.data` pipelines \u2192 build and compile the CREDIT student \u2192 train with `ModelCheckpoint` \u2192 compute a quick post-training uncertainty summary."
|
| 1385 |
-
]
|
| 1386 |
-
},
|
| 1387 |
-
{
|
| 1388 |
-
"cell_type": "code",
|
| 1389 |
-
"execution_count": null,
|
| 1390 |
-
"id": "8b145879",
|
| 1391 |
-
"metadata": {
|
| 1392 |
-
"colab": {
|
| 1393 |
-
"base_uri": "https://localhost:8080/"
|
| 1394 |
-
},
|
| 1395 |
-
"id": "8b145879",
|
| 1396 |
-
"executionInfo": {
|
| 1397 |
-
"status": "ok",
|
| 1398 |
-
"timestamp": 1780326555495,
|
| 1399 |
-
"user_tz": -330,
|
| 1400 |
-
"elapsed": 3314957,
|
| 1401 |
-
"user": {
|
| 1402 |
-
"displayName": "Randy Orton",
|
| 1403 |
-
"userId": "11725427482974841050"
|
| 1404 |
-
}
|
| 1405 |
-
},
|
| 1406 |
-
"outputId": "5153aa53-dd92-4dbd-80fa-8ab153d3b51a"
|
| 1407 |
-
},
|
| 1408 |
-
"outputs": [],
|
| 1409 |
-
"source": [
|
| 1410 |
-
"train_results = []\n",
|
| 1411 |
-
"\n",
|
| 1412 |
-
"for model_name, builder in model_builders.items():\n",
|
| 1413 |
-
" print(f\"\\n{'='*25} CREDIT Distillation: {model_name} {'='*25}\")\n",
|
| 1414 |
-
"\n",
|
| 1415 |
-
" # \u2500\u2500 1. Locate ensemble teachers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1416 |
-
" ensemble_paths = get_ensemble_paths(model_name)\n",
|
| 1417 |
-
" if len(ensemble_paths) == 0:\n",
|
| 1418 |
-
" print(' No ensemble models found \u2014 skipping.')\n",
|
| 1419 |
-
" continue\n",
|
| 1420 |
-
" if len(ensemble_paths) < 5:\n",
|
| 1421 |
-
" print(f' Warning: only {len(ensemble_paths)} ensemble models found (expected 5).')\n",
|
| 1422 |
-
"\n",
|
| 1423 |
-
" # \u2500\u2500 2. Select correct data split \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1424 |
-
" x_tr, x_te = (\n",
|
| 1425 |
-
" (x_train_alex, x_test_alex) if model_name == 'AlexNet_CNN'\n",
|
| 1426 |
-
" else (x_train, x_test)\n",
|
| 1427 |
-
" )\n",
|
| 1428 |
-
"\n",
|
| 1429 |
-
" # \u2500\u2500 3. Generate soft targets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1430 |
-
" print(' Generating training targets...')\n",
|
| 1431 |
-
" p_star_tr, delta_p_tr = generate_credit_targets(ensemble_paths, x_tr)\n",
|
| 1432 |
-
"\n",
|
| 1433 |
-
" print(' Generating test targets...')\n",
|
| 1434 |
-
" p_star_te, delta_p_te = generate_credit_targets(ensemble_paths, x_te)\n",
|
| 1435 |
-
"\n",
|
| 1436 |
-
" # \u2500\u2500 4. Build tf.data pipelines \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1437 |
-
" train_ds = (\n",
|
| 1438 |
-
" tf.data.Dataset\n",
|
| 1439 |
-
" .from_tensor_slices((x_tr, (p_star_tr, delta_p_tr)))\n",
|
| 1440 |
-
" .shuffle(1024).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n",
|
| 1441 |
-
" )\n",
|
| 1442 |
-
" test_ds = (\n",
|
| 1443 |
-
" tf.data.Dataset\n",
|
| 1444 |
-
" .from_tensor_slices((x_te, (p_star_te, delta_p_te)))\n",
|
| 1445 |
-
" .batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n",
|
| 1446 |
-
" )\n",
|
| 1447 |
-
"\n",
|
| 1448 |
-
" # \u2500\u2500 5. Build & compile student \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1449 |
-
" tf.keras.backend.clear_session()\n",
|
| 1450 |
-
" student = build_credit_student(builder, num_classes)\n",
|
| 1451 |
-
"\n",
|
| 1452 |
-
" optimizer = (\n",
|
| 1453 |
-
" keras.optimizers.Adagrad(learning_rate=ALEXNET_LR_START)\n",
|
| 1454 |
-
" if model_name == 'AlexNet_CNN'\n",
|
| 1455 |
-
" else keras.optimizers.Adam(learning_rate=LEARNING_RATE)\n",
|
| 1456 |
-
" )\n",
|
| 1457 |
-
"\n",
|
| 1458 |
-
" student.compile(\n",
|
| 1459 |
-
" optimizer=optimizer,\n",
|
| 1460 |
-
" loss={\n",
|
| 1461 |
-
" 'p_star': tf.keras.losses.KLDivergence(),\n",
|
| 1462 |
-
" 'delta_p': tf.keras.losses.MeanSquaredError(),\n",
|
| 1463 |
-
" },\n",
|
| 1464 |
-
" loss_weights={'p_star': 1.0, 'delta_p': 0.5}, # lambda = 0.5\n",
|
| 1465 |
-
" )\n",
|
| 1466 |
-
"\n",
|
| 1467 |
-
" # \u2500\u2500 6. Train \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1468 |
-
" best_path = MODEL_DIR / f'{model_name}_CREDIT_best.keras'\n",
|
| 1469 |
-
" callbacks = [\n",
|
| 1470 |
-
" keras.callbacks.ModelCheckpoint(\n",
|
| 1471 |
-
" filepath=str(best_path), monitor='val_loss',\n",
|
| 1472 |
-
" mode='min', save_best_only=True, verbose=1\n",
|
| 1473 |
-
" )\n",
|
| 1474 |
-
" ]\n",
|
| 1475 |
-
"\n",
|
| 1476 |
-
" t0 = time.perf_counter()\n",
|
| 1477 |
-
" student.fit(\n",
|
| 1478 |
-
" train_ds, validation_data=test_ds,\n",
|
| 1479 |
-
" epochs=EPOCHS, callbacks=callbacks, verbose=1\n",
|
| 1480 |
-
" )\n",
|
| 1481 |
-
" train_time = time.perf_counter() - t0\n",
|
| 1482 |
-
"\n",
|
| 1483 |
-
" # \u2500\u2500 7. Post-training uncertainty summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1484 |
-
" student.load_weights(best_path)\n",
|
| 1485 |
-
" p_star_pred, delta_p_pred = student.predict(x_te, batch_size=BATCH_SIZE)\n",
|
| 1486 |
-
"\n",
|
| 1487 |
-
" au = -np.sum(p_star_pred * np.log(p_star_pred + 1e-12), axis=-1)\n",
|
| 1488 |
-
" eu = np.mean(delta_p_pred, axis=-1)\n",
|
| 1489 |
-
" tu = au + eu\n",
|
| 1490 |
-
"\n",
|
| 1491 |
-
" train_results.append({\n",
|
| 1492 |
-
" 'Model': model_name,\n",
|
| 1493 |
-
" 'Mean_AU': np.mean(au),\n",
|
| 1494 |
-
" 'Mean_EU': np.mean(eu),\n",
|
| 1495 |
-
" 'Mean_TU': np.mean(tu),\n",
|
| 1496 |
-
" 'Train_Time_sec': train_time,\n",
|
| 1497 |
-
" })\n",
|
| 1498 |
-
"\n",
|
| 1499 |
-
"# \u2500\u2500 Save training uncertainty summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1500 |
-
"summary_df = pd.DataFrame(train_results)\n",
|
| 1501 |
-
"summary_path = RESULTS_DIR / 'credit_train_uncertainty_summary.csv'\n",
|
| 1502 |
-
"summary_df.to_csv(summary_path, index=False)\n",
|
| 1503 |
-
"print(f'\\nSaved training summary \u2192 {summary_path}')\n",
|
| 1504 |
-
"print(summary_df)\n",
|
| 1505 |
-
"\n",
|
| 1506 |
-
"# Stash for the final Excel report\n",
|
| 1507 |
-
"_credit_train_summary_df = summary_df.copy()"
|
| 1508 |
-
]
|
| 1509 |
-
},
|
| 1510 |
-
{
|
| 1511 |
-
"cell_type": "markdown",
|
| 1512 |
-
"id": "8cf0a904",
|
| 1513 |
-
"metadata": {
|
| 1514 |
-
"id": "8cf0a904"
|
| 1515 |
-
},
|
| 1516 |
-
"source": [
|
| 1517 |
-
"# 7.0 \u2014 Evaluation\n",
|
| 1518 |
-
"\n",
|
| 1519 |
-
"Loads the best saved CREDIT student weights for each architecture and computes a full suite of classification and calibration metrics \u2014 no re-training."
|
| 1520 |
-
]
|
| 1521 |
-
},
|
| 1522 |
-
{
|
| 1523 |
-
"cell_type": "markdown",
|
| 1524 |
-
"id": "70b726a0",
|
| 1525 |
-
"metadata": {
|
| 1526 |
-
"id": "70b726a0"
|
| 1527 |
-
},
|
| 1528 |
-
"source": [
|
| 1529 |
-
"## 7.1 \u2014 Load, Predict & Compute Metrics"
|
| 1530 |
-
]
|
| 1531 |
-
},
|
| 1532 |
-
{
|
| 1533 |
-
"cell_type": "code",
|
| 1534 |
-
"execution_count": null,
|
| 1535 |
-
"id": "82f55461",
|
| 1536 |
-
"metadata": {
|
| 1537 |
-
"colab": {
|
| 1538 |
-
"base_uri": "https://localhost:8080/"
|
| 1539 |
-
},
|
| 1540 |
-
"id": "82f55461",
|
| 1541 |
-
"executionInfo": {
|
| 1542 |
-
"status": "ok",
|
| 1543 |
-
"timestamp": 1780326576716,
|
| 1544 |
-
"user_tz": -330,
|
| 1545 |
-
"elapsed": 21210,
|
| 1546 |
-
"user": {
|
| 1547 |
-
"displayName": "Randy Orton",
|
| 1548 |
-
"userId": "11725427482974841050"
|
| 1549 |
-
}
|
| 1550 |
-
},
|
| 1551 |
-
"outputId": "f025f072-7db8-4da9-ee44-951a1bd1f685"
|
| 1552 |
-
},
|
| 1553 |
-
"outputs": [],
|
| 1554 |
-
"source": [
|
| 1555 |
-
"eval_results = []\n",
|
| 1556 |
-
"credit_artifacts = {}\n",
|
| 1557 |
-
"\n",
|
| 1558 |
-
"for model_name, builder in model_builders.items():\n",
|
| 1559 |
-
" best_path = MODEL_DIR / f'{model_name}_CREDIT_best.keras'\n",
|
| 1560 |
-
"\n",
|
| 1561 |
-
" if not best_path.exists():\n",
|
| 1562 |
-
" print(f' [{model_name}] No saved weights at {best_path} \u2014 skipping.')\n",
|
| 1563 |
-
" continue\n",
|
| 1564 |
-
"\n",
|
| 1565 |
-
" print(f\"\\n{'='*25} Evaluating: {model_name} {'='*25}\")\n",
|
| 1566 |
-
" tf.keras.backend.clear_session()\n",
|
| 1567 |
-
"\n",
|
| 1568 |
-
" student = build_credit_student(builder, num_classes)\n",
|
| 1569 |
-
" student.load_weights(best_path)\n",
|
| 1570 |
-
"\n",
|
| 1571 |
-
" # Select the correct test split for this architecture\n",
|
| 1572 |
-
" if model_name == 'AlexNet_CNN':\n",
|
| 1573 |
-
" x_te, y_te_true, y_te_cat = x_test_alex, y_test_alex, y_test_alex_cat\n",
|
| 1574 |
-
" else:\n",
|
| 1575 |
-
" x_te, y_te_true, y_te_cat = x_test, y_test, y_test_cat\n",
|
| 1576 |
-
"\n",
|
| 1577 |
-
" p_star_pred, delta_p_pred = student.predict(x_te, batch_size=BATCH_SIZE)\n",
|
| 1578 |
-
"\n",
|
| 1579 |
-
" au = -np.sum(p_star_pred * np.log(p_star_pred + 1e-12), axis=-1)\n",
|
| 1580 |
-
" eu = np.mean(delta_p_pred, axis=-1)\n",
|
| 1581 |
-
" tu = au + eu\n",
|
| 1582 |
-
"\n",
|
| 1583 |
-
" y_pred = np.argmax(p_star_pred, axis=-1)\n",
|
| 1584 |
-
"\n",
|
| 1585 |
-
" credit_artifacts[model_name] = {\n",
|
| 1586 |
-
" 'confusion_matrix': confusion_matrix(y_te_true, y_pred),\n",
|
| 1587 |
-
" 'report': classification_report(\n",
|
| 1588 |
-
" y_te_true, y_pred, output_dict=True, zero_division=0\n",
|
| 1589 |
-
" ),\n",
|
| 1590 |
-
" }\n",
|
| 1591 |
-
"\n",
|
| 1592 |
-
" eval_results.append({\n",
|
| 1593 |
-
" 'Model': model_name,\n",
|
| 1594 |
-
" 'Test_Accuracy': float(accuracy_score(y_te_true, y_pred)),\n",
|
| 1595 |
-
" 'Macro_F1': float(f1_score(y_te_true, y_pred, average='macro')),\n",
|
| 1596 |
-
" 'Cohen_Kappa': float(cohen_kappa_score(y_te_true, y_pred)),\n",
|
| 1597 |
-
" 'Test_NLL': float(log_loss(y_te_true, p_star_pred,\n",
|
| 1598 |
-
" labels=np.arange(num_classes))),\n",
|
| 1599 |
-
" 'Test_Brier': float(multiclass_brier_score(y_te_cat, p_star_pred)),\n",
|
| 1600 |
-
" 'Test_ECE': float(expected_calibration_error(\n",
|
| 1601 |
-
" y_te_true, p_star_pred, n_bins=15)),\n",
|
| 1602 |
-
" 'Mean_AU': float(np.mean(au)),\n",
|
| 1603 |
-
" 'Mean_EU': float(np.mean(eu)),\n",
|
| 1604 |
-
" 'Mean_TU': float(np.mean(tu)),\n",
|
| 1605 |
-
" })\n",
|
| 1606 |
-
"\n",
|
| 1607 |
-
"eval_df = pd.DataFrame(eval_results)\n",
|
| 1608 |
-
"eval_df.to_csv(RESULTS_DIR / 'credit_evaluation_summary.csv', index=False)\n",
|
| 1609 |
-
"print('\\n--- CREDIT Evaluation Summary ---')\n",
|
| 1610 |
-
"print(eval_df.to_string(index=False))\n",
|
| 1611 |
-
"\n",
|
| 1612 |
-
"# Stash for the final Excel report\n",
|
| 1613 |
-
"_credit_eval_df = eval_df.copy()\n",
|
| 1614 |
-
"_credit_artifacts_dict = credit_artifacts"
|
| 1615 |
-
]
|
| 1616 |
-
},
|
| 1617 |
-
{
|
| 1618 |
-
"cell_type": "markdown",
|
| 1619 |
-
"id": "8f9182c1",
|
| 1620 |
-
"metadata": {
|
| 1621 |
-
"id": "8f9182c1"
|
| 1622 |
-
},
|
| 1623 |
-
"source": [
|
| 1624 |
-
"## 7.2 \u2014 Confusion Matrices\n",
|
| 1625 |
-
"\n",
|
| 1626 |
-
"Renders and saves a side-by-side confusion matrix heatmap for every evaluated model."
|
| 1627 |
-
]
|
| 1628 |
-
},
|
| 1629 |
-
{
|
| 1630 |
-
"cell_type": "code",
|
| 1631 |
-
"execution_count": null,
|
| 1632 |
-
"id": "7632b240",
|
| 1633 |
-
"metadata": {
|
| 1634 |
-
"colab": {
|
| 1635 |
-
"base_uri": "https://localhost:8080/",
|
| 1636 |
-
"height": 0
|
| 1637 |
-
},
|
| 1638 |
-
"id": "7632b240",
|
| 1639 |
-
"executionInfo": {
|
| 1640 |
-
"status": "ok",
|
| 1641 |
-
"timestamp": 1780326578351,
|
| 1642 |
-
"user_tz": -330,
|
| 1643 |
-
"elapsed": 1545,
|
| 1644 |
-
"user": {
|
| 1645 |
-
"displayName": "Randy Orton",
|
| 1646 |
-
"userId": "11725427482974841050"
|
| 1647 |
-
}
|
| 1648 |
-
},
|
| 1649 |
-
"outputId": "d082ff93-05eb-4067-a5f2-d10127152519"
|
| 1650 |
-
},
|
| 1651 |
-
"outputs": [],
|
| 1652 |
-
"source": [
|
| 1653 |
-
"if credit_artifacts:\n",
|
| 1654 |
-
" class_ticks = [str(i + 1) for i in range(num_classes)]\n",
|
| 1655 |
-
" n_models = len(credit_artifacts)\n",
|
| 1656 |
-
" fig, axes = plt.subplots(1, n_models, figsize=(7 * n_models, 5.5))\n",
|
| 1657 |
-
" if n_models == 1:\n",
|
| 1658 |
-
" axes = [axes]\n",
|
| 1659 |
-
"\n",
|
| 1660 |
-
" for ax, (mname, art) in zip(axes, credit_artifacts.items()):\n",
|
| 1661 |
-
" sns.heatmap(\n",
|
| 1662 |
-
" art['confusion_matrix'], annot=True, fmt='d', cmap='Blues',\n",
|
| 1663 |
-
" xticklabels=class_ticks, yticklabels=class_ticks, cbar=False, ax=ax\n",
|
| 1664 |
-
" )\n",
|
| 1665 |
-
" ax.set_title(f'{mname} \u2014 CREDIT\\nConfusion Matrix', fontsize=13)\n",
|
| 1666 |
-
" ax.set_xlabel('Predicted Class')\n",
|
| 1667 |
-
" ax.set_ylabel('True Class')\n",
|
| 1668 |
-
"\n",
|
| 1669 |
-
" plt.tight_layout()\n",
|
| 1670 |
-
" save_path = RESULTS_DIR / 'credit_confusion_matrices.png'\n",
|
| 1671 |
-
" plt.savefig(save_path, dpi=220, bbox_inches='tight')\n",
|
| 1672 |
-
" print(f'Saved \u2192 {save_path}')\n",
|
| 1673 |
-
" plt.show()"
|
| 1674 |
-
]
|
| 1675 |
-
},
|
| 1676 |
-
{
|
| 1677 |
-
"cell_type": "markdown",
|
| 1678 |
-
"id": "cdb67872",
|
| 1679 |
-
"metadata": {
|
| 1680 |
-
"id": "cdb67872"
|
| 1681 |
-
},
|
| 1682 |
-
"source": [
|
| 1683 |
-
"# 8.0 \u2014 Spatial Uncertainty Mapping\n",
|
| 1684 |
-
"\n",
|
| 1685 |
-
"Runs full-scene inference for each CREDIT student and produces a 3\u00d74 figure showing: the base prediction map, binary certain/uncertain masks, grey-overlay maps, and per-class pixel-count bar charts \u2014 all for the aleatoric, epistemic, and total uncertainty channels."
|
| 1686 |
-
]
|
| 1687 |
-
},
|
| 1688 |
-
{
|
| 1689 |
-
"cell_type": "markdown",
|
| 1690 |
-
"id": "783b6a92",
|
| 1691 |
-
"metadata": {
|
| 1692 |
-
"id": "783b6a92"
|
| 1693 |
-
},
|
| 1694 |
-
"source": [
|
| 1695 |
-
"## 8.1 \u2014 Extract Full-Scene Patch Array"
|
| 1696 |
-
]
|
| 1697 |
-
},
|
| 1698 |
-
{
|
| 1699 |
-
"cell_type": "code",
|
| 1700 |
-
"execution_count": null,
|
| 1701 |
-
"id": "105e5c3b",
|
| 1702 |
-
"metadata": {
|
| 1703 |
-
"colab": {
|
| 1704 |
-
"base_uri": "https://localhost:8080/"
|
| 1705 |
-
},
|
| 1706 |
-
"id": "105e5c3b",
|
| 1707 |
-
"executionInfo": {
|
| 1708 |
-
"status": "ok",
|
| 1709 |
-
"timestamp": 1780326578373,
|
| 1710 |
-
"user_tz": -330,
|
| 1711 |
-
"elapsed": 18,
|
| 1712 |
-
"user": {
|
| 1713 |
-
"displayName": "Randy Orton",
|
| 1714 |
-
"userId": "11725427482974841050"
|
| 1715 |
-
}
|
| 1716 |
-
},
|
| 1717 |
-
"outputId": "8171ce94-4b55-4df4-942c-b3799d6f89a4"
|
| 1718 |
-
},
|
| 1719 |
-
"outputs": [],
|
| 1720 |
-
"source": [
|
| 1721 |
-
"print('Extracting full-scene patch array (all H\u00d7W pixels)...')\n",
|
| 1722 |
-
"pad = PATCH_SIZE // 2\n",
|
| 1723 |
-
"x_pad = np.pad(x_img, ((pad, pad), (pad, pad), (0, 0)), mode='edge')\n",
|
| 1724 |
-
"\n",
|
| 1725 |
-
"scene_patches = np.empty((H * W, PATCH_SIZE, PATCH_SIZE, B), dtype=np.float32)\n",
|
| 1726 |
-
"for idx, (r, c) in enumerate(\n",
|
| 1727 |
-
" [(r, c) for r in range(H) for c in range(W)]\n",
|
| 1728 |
-
"):\n",
|
| 1729 |
-
" scene_patches[idx] = x_pad[r:r + PATCH_SIZE, c:c + PATCH_SIZE, :]\n",
|
| 1730 |
-
"\n",
|
| 1731 |
-
"print(f'Scene patch tensor: {scene_patches.shape}')"
|
| 1732 |
-
]
|
| 1733 |
-
},
|
| 1734 |
-
{
|
| 1735 |
-
"cell_type": "markdown",
|
| 1736 |
-
"id": "dabe63f1",
|
| 1737 |
-
"metadata": {
|
| 1738 |
-
"id": "dabe63f1"
|
| 1739 |
-
},
|
| 1740 |
-
"source": [
|
| 1741 |
-
"## 8.2 \u2014 Spatial Map Generator\n",
|
| 1742 |
-
"\n",
|
| 1743 |
-
"Defines the function that produces and saves the 3\u00d74 uncertainty figure for a given student model."
|
| 1744 |
-
]
|
| 1745 |
-
},
|
| 1746 |
-
{
|
| 1747 |
-
"cell_type": "code",
|
| 1748 |
-
"execution_count": null,
|
| 1749 |
-
"id": "39c0b971",
|
| 1750 |
-
"metadata": {
|
| 1751 |
-
"id": "39c0b971",
|
| 1752 |
-
"executionInfo": {
|
| 1753 |
-
"status": "ok",
|
| 1754 |
-
"timestamp": 1780326578552,
|
| 1755 |
-
"user_tz": -330,
|
| 1756 |
-
"elapsed": 95,
|
| 1757 |
-
"user": {
|
| 1758 |
-
"displayName": "Randy Orton",
|
| 1759 |
-
"userId": "11725427482974841050"
|
| 1760 |
-
}
|
| 1761 |
-
}
|
| 1762 |
-
},
|
| 1763 |
-
"outputs": [],
|
| 1764 |
-
"source": [
|
| 1765 |
-
"# Same bold, saturated base palette used across the other notebooks,\n",
|
| 1766 |
-
"# auto-extended with additional bold shades (same style) if a future\n",
|
| 1767 |
-
"# dataset has more than 10 classes.\n",
|
| 1768 |
-
"import colorsys\n",
|
| 1769 |
-
"\n",
|
| 1770 |
-
"CLASS_COLOR_BASE = [\n",
|
| 1771 |
-
" '#0000FF', '#00FF00', '#FF0000', '#00FFFF',\n",
|
| 1772 |
-
" '#FF00FF', '#FFFF00', '#A52A2A', '#FFA500',\n",
|
| 1773 |
-
" '#7FFF00', '#8A2BE2',\n",
|
| 1774 |
-
"]\n",
|
| 1775 |
-
"UNCERTAIN_COLOR = '#808080'\n",
|
| 1776 |
-
"\n",
|
| 1777 |
-
"def _generate_extra_shades(n_extra, hue_offset=0.05, sat=0.85, val=0.95):\n",
|
| 1778 |
-
" \"\"\"Bold, highly-saturated extra hues in the same style as CLASS_COLOR_BASE.\"\"\"\n",
|
| 1779 |
-
" shades = []\n",
|
| 1780 |
-
" for i in range(n_extra):\n",
|
| 1781 |
-
" hue = (hue_offset + i / max(n_extra, 1)) % 1.0\n",
|
| 1782 |
-
" r, g, b = colorsys.hsv_to_rgb(hue, sat, val)\n",
|
| 1783 |
-
" shades.append('#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255)))\n",
|
| 1784 |
-
" return shades\n",
|
| 1785 |
-
"\n",
|
| 1786 |
-
"def get_class_colors(n):\n",
|
| 1787 |
-
" \"\"\"Return n distinct hex colours, extending the base 10 with additional bold\n",
|
| 1788 |
-
" shades in the same saturated style if needed.\"\"\"\n",
|
| 1789 |
-
" if n <= len(CLASS_COLOR_BASE):\n",
|
| 1790 |
-
" return CLASS_COLOR_BASE[:n]\n",
|
| 1791 |
-
" return CLASS_COLOR_BASE + _generate_extra_shades(n - len(CLASS_COLOR_BASE))\n",
|
| 1792 |
-
"\n",
|
| 1793 |
-
"def resolve_uncertain_color(class_colors, preferred=UNCERTAIN_COLOR, min_dist=60):\n",
|
| 1794 |
-
" \"\"\"Return `preferred` unless it visually clashes with any class colour, in\n",
|
| 1795 |
-
" which case fall back to a distinct dark slate so 'Uncertain' never collides\n",
|
| 1796 |
-
" with a real class colour.\"\"\"\n",
|
| 1797 |
-
" def _hex_to_rgb(h):\n",
|
| 1798 |
-
" h = h.lstrip('#')\n",
|
| 1799 |
-
" return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))\n",
|
| 1800 |
-
" pr, pg, pb = _hex_to_rgb(preferred)\n",
|
| 1801 |
-
" for c in class_colors:\n",
|
| 1802 |
-
" cr, cg, cb = _hex_to_rgb(c)\n",
|
| 1803 |
-
" dist = ((pr - cr) ** 2 + (pg - cg) ** 2 + (pb - cb) ** 2) ** 0.5\n",
|
| 1804 |
-
" if dist < min_dist:\n",
|
| 1805 |
-
" return '#2F2F2F'\n",
|
| 1806 |
-
" return preferred\n",
|
| 1807 |
-
"\n",
|
| 1808 |
-
"\n",
|
| 1809 |
-
"def generate_spatial_credit_maps(\n",
|
| 1810 |
-
" model_name, student_model, scene_pixels,\n",
|
| 1811 |
-
" H, W, au_thresh=0.5, eu_thresh=0.2, tu_thresh=0.7\n",
|
| 1812 |
-
"):\n",
|
| 1813 |
-
" \"\"\"\n",
|
| 1814 |
-
" Run full-scene inference and save a 3\u00d74 spatial uncertainty figure.\n",
|
| 1815 |
-
"\n",
|
| 1816 |
-
" Panel layout (rows \u00d7 cols):\n",
|
| 1817 |
-
" Row 0: Base prediction | AU binary | EU binary | TU binary\n",
|
| 1818 |
-
" Row 1: AU grey overlay | EU grey overlay | TU grey overlay | (blank)\n",
|
| 1819 |
-
" Row 2: AU bar chart | EU bar chart | TU bar chart | (blank)\n",
|
| 1820 |
-
" \"\"\"\n",
|
| 1821 |
-
" print(f' Inferring full scene for {model_name}...')\n",
|
| 1822 |
-
" p_star_scene, delta_p_scene = student_model.predict(\n",
|
| 1823 |
-
" scene_pixels, batch_size=2048, verbose=1\n",
|
| 1824 |
-
" )\n",
|
| 1825 |
-
" p_star_scene = np.clip(p_star_scene, 1e-12, 1.0)\n",
|
| 1826 |
-
" n_cls = p_star_scene.shape[-1]\n",
|
| 1827 |
-
"\n",
|
| 1828 |
-
" au_scene = -np.sum(p_star_scene * np.log(p_star_scene), axis=-1)\n",
|
| 1829 |
-
" eu_scene = np.mean(delta_p_scene, axis=-1)\n",
|
| 1830 |
-
" tu_scene = au_scene + eu_scene\n",
|
| 1831 |
-
"\n",
|
| 1832 |
-
" pred_map = np.argmax(p_star_scene, axis=-1).reshape(H, W)\n",
|
| 1833 |
-
" au_mask = (au_scene.reshape(H, W) > au_thresh).astype(int)\n",
|
| 1834 |
-
" eu_mask = (eu_scene.reshape(H, W) > eu_thresh).astype(int)\n",
|
| 1835 |
-
" tu_mask = (tu_scene.reshape(H, W) > tu_thresh).astype(int)\n",
|
| 1836 |
-
"\n",
|
| 1837 |
-
" combined_au = np.where(au_mask == 1, n_cls, pred_map)\n",
|
| 1838 |
-
" combined_eu = np.where(eu_mask == 1, n_cls, pred_map)\n",
|
| 1839 |
-
" combined_tu = np.where(tu_mask == 1, n_cls, pred_map)\n",
|
| 1840 |
-
"\n",
|
| 1841 |
-
" class_colors = get_class_colors(n_cls)\n",
|
| 1842 |
-
" uncertain_color = resolve_uncertain_color(class_colors)\n",
|
| 1843 |
-
" cmap_base = ListedColormap(class_colors)\n",
|
| 1844 |
-
" cmap_unc = ListedColormap(class_colors + [uncertain_color])\n",
|
| 1845 |
-
" cmap_binary = ListedColormap(['#FFFF00', '#001F3F'])\n",
|
| 1846 |
-
"\n",
|
| 1847 |
-
" bar_lbls = [f'Class {i}' for i in range(n_cls)] + ['Uncertain']\n",
|
| 1848 |
-
" bar_cols = class_colors + [uncertain_color]\n",
|
| 1849 |
-
"\n",
|
| 1850 |
-
" fig, axes = plt.subplots(3, 4, figsize=(38, 26))\n",
|
| 1851 |
-
" fig.suptitle(\n",
|
| 1852 |
-
" f'{model_name} \u2014 CREDIT Uncertainty Maps (Absolute Thresholds)',\n",
|
| 1853 |
-
" fontsize=24, fontweight='bold', y=0.99\n",
|
| 1854 |
-
" )\n",
|
| 1855 |
-
"\n",
|
| 1856 |
-
" # \u2500\u2500 Row 0: Base prediction + 3 binary maps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1857 |
-
" axes[0, 0].imshow(pred_map, cmap=cmap_base, vmin=0, vmax=n_cls - 1)\n",
|
| 1858 |
-
" axes[0, 0].set_title('Base Prediction Map', fontsize=15)\n",
|
| 1859 |
-
" axes[0, 0].axis('off')\n",
|
| 1860 |
-
"\n",
|
| 1861 |
-
" binary_specs = [\n",
|
| 1862 |
-
" (axes[0, 1], au_mask, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1863 |
-
" (axes[0, 2], eu_mask, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1864 |
-
" (axes[0, 3], tu_mask, f'Total (TU > {tu_thresh})'),\n",
|
| 1865 |
-
" ]\n",
|
| 1866 |
-
" for ax, mask, label in binary_specs:\n",
|
| 1867 |
-
" ax.imshow(mask, cmap=cmap_binary, vmin=0, vmax=1)\n",
|
| 1868 |
-
" ax.set_title(f'Certain vs Uncertain\\n{label}', fontsize=15, pad=10)\n",
|
| 1869 |
-
" ax.axis('off')\n",
|
| 1870 |
-
" ax.legend(\n",
|
| 1871 |
-
" handles=[\n",
|
| 1872 |
-
" Patch(facecolor='#FFFF00', label='Certain'),\n",
|
| 1873 |
-
" Patch(facecolor='#001F3F', label='Uncertain'),\n",
|
| 1874 |
-
" ],\n",
|
| 1875 |
-
" loc='upper left',\n",
|
| 1876 |
-
" bbox_to_anchor=(0.0, -0.02),\n",
|
| 1877 |
-
" borderaxespad=0,\n",
|
| 1878 |
-
" fontsize=11,\n",
|
| 1879 |
-
" framealpha=0.9,\n",
|
| 1880 |
-
" ncol=2\n",
|
| 1881 |
-
" )\n",
|
| 1882 |
-
"\n",
|
| 1883 |
-
" # \u2500\u2500 Row 1: Grey overlay maps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1884 |
-
" overlay_specs = [\n",
|
| 1885 |
-
" (axes[1, 0], combined_au, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1886 |
-
" (axes[1, 1], combined_eu, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1887 |
-
" (axes[1, 2], combined_tu, f'Total (TU > {tu_thresh})'),\n",
|
| 1888 |
-
" ]\n",
|
| 1889 |
-
" for ax, combined, label in overlay_specs:\n",
|
| 1890 |
-
" ax.imshow(combined, cmap=cmap_unc, vmin=0, vmax=n_cls)\n",
|
| 1891 |
-
" ax.set_title(f'Grey Overlay \u2014 {label}', fontsize=15, pad=10)\n",
|
| 1892 |
-
" ax.axis('off')\n",
|
| 1893 |
-
" axes[1, 3].axis('off')\n",
|
| 1894 |
-
"\n",
|
| 1895 |
-
" # \u2500\u2500 Row 2: Pixel-count bar charts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1896 |
-
" bar_specs = [\n",
|
| 1897 |
-
" (axes[2, 0], combined_au, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1898 |
-
" (axes[2, 1], combined_eu, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1899 |
-
" (axes[2, 2], combined_tu, f'Total (TU > {tu_thresh})'),\n",
|
| 1900 |
-
" ]\n",
|
| 1901 |
-
" for ax, combined, label in bar_specs:\n",
|
| 1902 |
-
" uniq, cnt = np.unique(combined, return_counts=True)\n",
|
| 1903 |
-
" c_dict = {int(k): int(v) for k, v in zip(uniq, cnt)}\n",
|
| 1904 |
-
" bar_vals = [c_dict.get(i, 0) for i in range(n_cls + 1)]\n",
|
| 1905 |
-
" ax.bar(bar_lbls, bar_vals, color=bar_cols, edgecolor='black')\n",
|
| 1906 |
-
" ax.set_title(f'Pixel Counts \u2014 {label}', fontsize=15, pad=10)\n",
|
| 1907 |
-
" ax.tick_params(axis='x', rotation=45, labelsize=11)\n",
|
| 1908 |
-
" ax.set_ylabel('Pixel Count', fontsize=12)\n",
|
| 1909 |
-
" max_val = max(bar_vals, default=1)\n",
|
| 1910 |
-
" for i, v in enumerate(bar_vals):\n",
|
| 1911 |
-
" ax.text(i, v + max_val * 0.01,\n",
|
| 1912 |
-
" f'{v:,}', ha='center', va='bottom',\n",
|
| 1913 |
-
" fontweight='bold', fontsize=9)\n",
|
| 1914 |
-
" ax.set_ylim(0, max_val * 1.12)\n",
|
| 1915 |
-
" axes[2, 3].axis('off')\n",
|
| 1916 |
-
"\n",
|
| 1917 |
-
" plt.tight_layout(rect=[0, 0.03, 1, 0.98])\n",
|
| 1918 |
-
" save_path = RESULTS_DIR / f'{model_name}_CREDIT_spatial_maps.png'\n",
|
| 1919 |
-
" plt.savefig(save_path, dpi=300, bbox_inches='tight')\n",
|
| 1920 |
-
" print(f' Saved \u2192 {save_path}')\n",
|
| 1921 |
-
" plt.show()"
|
| 1922 |
-
]
|
| 1923 |
-
},
|
| 1924 |
-
{
|
| 1925 |
-
"cell_type": "markdown",
|
| 1926 |
-
"id": "95c54f00",
|
| 1927 |
-
"metadata": {
|
| 1928 |
-
"id": "95c54f00"
|
| 1929 |
-
},
|
| 1930 |
-
"source": [
|
| 1931 |
-
"## 8.3 \u2014 Run Spatial Mapping for All Models"
|
| 1932 |
-
]
|
| 1933 |
-
},
|
| 1934 |
-
{
|
| 1935 |
-
"cell_type": "code",
|
| 1936 |
-
"execution_count": null,
|
| 1937 |
-
"id": "6758d694",
|
| 1938 |
-
"metadata": {
|
| 1939 |
-
"colab": {
|
| 1940 |
-
"base_uri": "https://localhost:8080/",
|
| 1941 |
-
"height": 1000,
|
| 1942 |
-
"output_embedded_package_id": "1gDB51H9wc43U3d8erY1lxaGDsVsR279b"
|
| 1943 |
-
},
|
| 1944 |
-
"id": "6758d694",
|
| 1945 |
-
"executionInfo": {
|
| 1946 |
-
"status": "ok",
|
| 1947 |
-
"timestamp": 1780326716495,
|
| 1948 |
-
"user_tz": -330,
|
| 1949 |
-
"elapsed": 137941,
|
| 1950 |
-
"user": {
|
| 1951 |
-
"displayName": "Randy Orton",
|
| 1952 |
-
"userId": "11725427482974841050"
|
| 1953 |
-
}
|
| 1954 |
-
},
|
| 1955 |
-
"outputId": "b232112c-8bb8-4e90-f288-7baa55c7ffbe"
|
| 1956 |
-
},
|
| 1957 |
-
"outputs": [],
|
| 1958 |
-
"source": [
|
| 1959 |
-
"for model_name, builder in model_builders.items():\n",
|
| 1960 |
-
" best_path = MODEL_DIR / f'{model_name}_CREDIT_best.keras'\n",
|
| 1961 |
-
" if not best_path.exists():\n",
|
| 1962 |
-
" print(f'[{model_name}] No weights found \u2014 skipping spatial maps.')\n",
|
| 1963 |
-
" continue\n",
|
| 1964 |
-
"\n",
|
| 1965 |
-
" tf.keras.backend.clear_session()\n",
|
| 1966 |
-
" student = build_credit_student(builder, num_classes)\n",
|
| 1967 |
-
" student.load_weights(best_path)\n",
|
| 1968 |
-
"\n",
|
| 1969 |
-
" generate_spatial_credit_maps(\n",
|
| 1970 |
-
" model_name, student, scene_patches, H, W,\n",
|
| 1971 |
-
" au_thresh=0.5, eu_thresh=0.2, tu_thresh=0.7\n",
|
| 1972 |
-
" )"
|
| 1973 |
-
]
|
| 1974 |
-
},
|
| 1975 |
-
{
|
| 1976 |
-
"cell_type": "markdown",
|
| 1977 |
-
"id": "6fddf771",
|
| 1978 |
-
"metadata": {
|
| 1979 |
-
"id": "6fddf771"
|
| 1980 |
-
},
|
| 1981 |
-
"source": [
|
| 1982 |
-
"# 9.0 \u2014 Excel Report Export\n",
|
| 1983 |
-
"\n",
|
| 1984 |
-
"Collects all saved plot paths (confusion matrices, spatial uncertainty maps, and any training-curve PNGs) and writes the final `CREDIT_Results.xlsx` workbook."
|
| 1985 |
-
]
|
| 1986 |
-
},
|
| 1987 |
-
{
|
| 1988 |
-
"cell_type": "code",
|
| 1989 |
-
"execution_count": null,
|
| 1990 |
-
"id": "3ceb105a",
|
| 1991 |
-
"metadata": {
|
| 1992 |
-
"colab": {
|
| 1993 |
-
"base_uri": "https://localhost:8080/",
|
| 1994 |
-
"height": 70
|
| 1995 |
-
},
|
| 1996 |
-
"id": "3ceb105a",
|
| 1997 |
-
"executionInfo": {
|
| 1998 |
-
"status": "ok",
|
| 1999 |
-
"timestamp": 1780326716583,
|
| 2000 |
-
"user_tz": -330,
|
| 2001 |
-
"elapsed": 27,
|
| 2002 |
-
"user": {
|
| 2003 |
-
"displayName": "Randy Orton",
|
| 2004 |
-
"userId": "11725427482974841050"
|
| 2005 |
-
}
|
| 2006 |
-
},
|
| 2007 |
-
"outputId": "3e78adfd-20af-4518-a867-0b2d5fe24826"
|
| 2008 |
-
},
|
| 2009 |
-
"outputs": [],
|
| 2010 |
-
"source": [
|
| 2011 |
-
"# \u2500\u2500 Collect all plot paths \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 2012 |
-
"_plot_entries = []\n",
|
| 2013 |
-
"\n",
|
| 2014 |
-
"# Confusion matrix image\n",
|
| 2015 |
-
"_cm_path = str(RESULTS_DIR / 'credit_confusion_matrices.png')\n",
|
| 2016 |
-
"_plot_entries.append(('Confusion Matrices (all models)', _cm_path))\n",
|
| 2017 |
-
"\n",
|
| 2018 |
-
"# Per-model spatial uncertainty maps\n",
|
| 2019 |
-
"for _mname in model_builders:\n",
|
| 2020 |
-
" _sp_path = str(RESULTS_DIR / f'{_mname}_CREDIT_spatial_maps.png')\n",
|
| 2021 |
-
" _plot_entries.append((f'{_mname} \u2014 Spatial Uncertainty Maps', _sp_path))\n",
|
| 2022 |
-
"\n",
|
| 2023 |
-
"# Any additional training-curve PNGs saved to PLOT_DIR\n",
|
| 2024 |
-
"for _p in sorted(PLOT_DIR.glob('*.png')):\n",
|
| 2025 |
-
" _plot_entries.append((_p.stem.replace('_', ' ').title(), str(_p)))\n",
|
| 2026 |
-
"\n",
|
| 2027 |
-
"# \u2500\u2500 Write the workbook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 2028 |
-
"create_credit_excel_report(\n",
|
| 2029 |
-
" results_dir = RESULTS_DIR,\n",
|
| 2030 |
-
" train_summary_df = _credit_train_summary_df,\n",
|
| 2031 |
-
" eval_summary_df = _credit_eval_df,\n",
|
| 2032 |
-
" credit_artifacts_dict = _credit_artifacts_dict,\n",
|
| 2033 |
-
" plot_paths = _plot_entries,\n",
|
| 2034 |
-
")"
|
| 2035 |
-
]
|
| 2036 |
-
}
|
| 2037 |
-
],
|
| 2038 |
-
"metadata": {
|
| 2039 |
-
"colab": {
|
| 2040 |
-
"provenance": [],
|
| 2041 |
-
"collapsed_sections": [
|
| 2042 |
-
"5302be31",
|
| 2043 |
-
"90f75990",
|
| 2044 |
-
"5897552f",
|
| 2045 |
-
"7d481920",
|
| 2046 |
-
"1cdf93bb",
|
| 2047 |
-
"04e1cb62",
|
| 2048 |
-
"8cf0a904",
|
| 2049 |
-
"6fddf771"
|
| 2050 |
-
],
|
| 2051 |
-
"gpuType": "T4"
|
| 2052 |
-
},
|
| 2053 |
-
"kernelspec": {
|
| 2054 |
-
"display_name": "Python 3",
|
| 2055 |
-
"name": "python3"
|
| 2056 |
-
},
|
| 2057 |
-
"language_info": {
|
| 2058 |
-
"name": "python",
|
| 2059 |
-
"version": "3.10.0"
|
| 2060 |
-
},
|
| 2061 |
-
"accelerator": "GPU"
|
| 2062 |
-
},
|
| 2063 |
-
"nbformat": 4,
|
| 2064 |
-
"nbformat_minor": 5
|
| 2065 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
credit/results/AlexNet_CNN_CREDIT_spatial_maps.png
DELETED
Git LFS Details
|
credit/results/CREDIT_Results.xlsx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:137bd093325fd32c4d70f7fd049216d6a2f6a00a7811c84bcf06c8f04feba04b
|
| 3 |
-
size 2556098
|
|
|
|
|
|
|
|
|
|
|
|
credit/results/GFNet_CREDIT_spatial_maps.png
DELETED
Git LFS Details
|
credit/results/ViT_UNet_CREDIT_spatial_maps.png
DELETED
Git LFS Details
|
credit/results/credit_confusion_matrices.png
DELETED
Git LFS Details
|
credit/results/credit_evaluation_summary.csv
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
Model,Test_Accuracy,Macro_F1,Cohen_Kappa,Test_NLL,Test_Brier,Test_ECE,Mean_AU,Mean_EU,Mean_TU
|
| 2 |
-
AlexNet_CNN,0.9994804809262283,0.9989244889559838,0.9993741482276686,0.0015514798324969712,0.0007639509124735289,0.0005383794759277847,0.0031835082918405533,0.00027990108355879784,0.0034634096082299948
|
| 3 |
-
GFNet,0.9999257829894612,0.9998136737701507,0.9999105961705329,0.0012463549818028674,0.0002783389623474388,0.0009749657112960736,0.007062746211886406,1.1088519613622339e-06,0.007063854485750198
|
| 4 |
-
ViT_UNet,0.9978477066943744,0.9965033072622065,0.9974071374608717,0.04059596322322891,0.004743752003709026,0.03291026674818175,0.21300919353961945,0.00024190374824684113,0.21325111389160156
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
credit/results/credit_train_uncertainty_summary.csv
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
Model,Mean_AU,Mean_EU,Mean_TU,Train_Time_sec
|
| 2 |
-
AlexNet_CNN,0.0031835083,0.00027990108,0.0034634096,2038.4876195370002
|
| 3 |
-
GFNet,0.007062746,1.108852e-06,0.0070638545,2269.2560890150003
|
| 4 |
-
ViT_UNet,0.2130092,0.00024190375,0.21325111,3810.0610708649992
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/Model_training_ensembles.ipynb
DELETED
|
@@ -1,2006 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"nbformat": 4,
|
| 3 |
-
"nbformat_minor": 5,
|
| 4 |
-
"metadata": {
|
| 5 |
-
"kernelspec": {
|
| 6 |
-
"display_name": "Python 3",
|
| 7 |
-
"name": "python3"
|
| 8 |
-
},
|
| 9 |
-
"language_info": {
|
| 10 |
-
"name": "python",
|
| 11 |
-
"version": "3.10.0"
|
| 12 |
-
},
|
| 13 |
-
"colab": {
|
| 14 |
-
"provenance": [],
|
| 15 |
-
"collapsed_sections": [
|
| 16 |
-
"Wq4Ce3i-jmew",
|
| 17 |
-
"_HTyxJcIjme6",
|
| 18 |
-
"WlGs-SYMjme_",
|
| 19 |
-
"djg40XJDjmfB",
|
| 20 |
-
"NKYV1hDsjmfD",
|
| 21 |
-
"hPJBPlZTjmfE",
|
| 22 |
-
"KG2bOKWtjmfI",
|
| 23 |
-
"g1HAWWVBjmfK",
|
| 24 |
-
"t_0f2DsWjmfM"
|
| 25 |
-
],
|
| 26 |
-
"gpuType": "T4"
|
| 27 |
-
},
|
| 28 |
-
"accelerator": "GPU"
|
| 29 |
-
},
|
| 30 |
-
"cells": [
|
| 31 |
-
{
|
| 32 |
-
"cell_type": "markdown",
|
| 33 |
-
"metadata": {
|
| 34 |
-
"id": "Wq4Ce3i-jmew"
|
| 35 |
-
},
|
| 36 |
-
"source": [
|
| 37 |
-
"# 1.0 \u2014 Setup & Imports\n",
|
| 38 |
-
"\n",
|
| 39 |
-
"Mounts Google Drive, imports all required libraries, and seeds all random number generators for reproducibility."
|
| 40 |
-
],
|
| 41 |
-
"id": "Wq4Ce3i-jmew"
|
| 42 |
-
},
|
| 43 |
-
{
|
| 44 |
-
"cell_type": "markdown",
|
| 45 |
-
"metadata": {
|
| 46 |
-
"id": "VIEqxcMqjmez"
|
| 47 |
-
},
|
| 48 |
-
"source": [
|
| 49 |
-
"## 1.1 \u2014 Google Drive & Library Imports"
|
| 50 |
-
],
|
| 51 |
-
"id": "VIEqxcMqjmez"
|
| 52 |
-
},
|
| 53 |
-
{
|
| 54 |
-
"cell_type": "code",
|
| 55 |
-
"execution_count": null,
|
| 56 |
-
"metadata": {
|
| 57 |
-
"colab": {
|
| 58 |
-
"base_uri": "https://localhost:8080/"
|
| 59 |
-
},
|
| 60 |
-
"id": "ZtXnIJLNjme1",
|
| 61 |
-
"outputId": "c5e39849-8f6d-4945-f54d-a8178aa215f3",
|
| 62 |
-
"executionInfo": {
|
| 63 |
-
"status": "ok",
|
| 64 |
-
"timestamp": 1782312434471,
|
| 65 |
-
"user_tz": -330,
|
| 66 |
-
"elapsed": 2071,
|
| 67 |
-
"user": {
|
| 68 |
-
"displayName": "Danesh Selwal",
|
| 69 |
-
"userId": "10565017814556927251"
|
| 70 |
-
}
|
| 71 |
-
}
|
| 72 |
-
},
|
| 73 |
-
"outputs": [],
|
| 74 |
-
"source": [
|
| 75 |
-
"from google.colab import drive\n",
|
| 76 |
-
"drive.mount('/content/drive')\n",
|
| 77 |
-
"\n",
|
| 78 |
-
"# Standard library\n",
|
| 79 |
-
"import os\n",
|
| 80 |
-
"import json\n",
|
| 81 |
-
"import time\n",
|
| 82 |
-
"import random\n",
|
| 83 |
-
"import io\n",
|
| 84 |
-
"from pathlib import Path\n",
|
| 85 |
-
"\n",
|
| 86 |
-
"from scipy.io import loadmat\n",
|
| 87 |
-
"\n",
|
| 88 |
-
"# Numerical / data\n",
|
| 89 |
-
"import numpy as np\n",
|
| 90 |
-
"import pandas as pd\n",
|
| 91 |
-
"\n",
|
| 92 |
-
"# Visualisation\n",
|
| 93 |
-
"import seaborn as sns\n",
|
| 94 |
-
"import matplotlib.pyplot as plt\n",
|
| 95 |
-
"from matplotlib.colors import ListedColormap\n",
|
| 96 |
-
"\n",
|
| 97 |
-
"# Deep learning\n",
|
| 98 |
-
"import tensorflow as tf\n",
|
| 99 |
-
"from tensorflow import keras\n",
|
| 100 |
-
"from tensorflow.keras import layers, regularizers\n",
|
| 101 |
-
"\n",
|
| 102 |
-
"# Scikit-learn\n",
|
| 103 |
-
"from sklearn.model_selection import train_test_split\n",
|
| 104 |
-
"from sklearn.metrics import (\n",
|
| 105 |
-
" accuracy_score,\n",
|
| 106 |
-
" cohen_kappa_score,\n",
|
| 107 |
-
" confusion_matrix,\n",
|
| 108 |
-
" classification_report,\n",
|
| 109 |
-
" f1_score,\n",
|
| 110 |
-
" log_loss,\n",
|
| 111 |
-
")\n",
|
| 112 |
-
"\n",
|
| 113 |
-
"# Excel export\n",
|
| 114 |
-
"from openpyxl import Workbook, load_workbook\n",
|
| 115 |
-
"from openpyxl.drawing.image import Image as XLImage\n",
|
| 116 |
-
"\n",
|
| 117 |
-
"sns.set_style('whitegrid')\n",
|
| 118 |
-
"print('TensorFlow:', tf.__version__)"
|
| 119 |
-
],
|
| 120 |
-
"id": "ZtXnIJLNjme1"
|
| 121 |
-
},
|
| 122 |
-
{
|
| 123 |
-
"cell_type": "markdown",
|
| 124 |
-
"metadata": {
|
| 125 |
-
"id": "9RW99Ciojme2"
|
| 126 |
-
},
|
| 127 |
-
"source": [
|
| 128 |
-
"## 1.2 \u2014 Seed Initialisation\n",
|
| 129 |
-
"\n",
|
| 130 |
-
"Sets the global random seed across Python, NumPy, and TensorFlow to ensure reproducible runs."
|
| 131 |
-
],
|
| 132 |
-
"id": "9RW99Ciojme2"
|
| 133 |
-
},
|
| 134 |
-
{
|
| 135 |
-
"cell_type": "code",
|
| 136 |
-
"execution_count": null,
|
| 137 |
-
"metadata": {
|
| 138 |
-
"id": "d1NVUhVRjme4",
|
| 139 |
-
"executionInfo": {
|
| 140 |
-
"status": "ok",
|
| 141 |
-
"timestamp": 1782312434473,
|
| 142 |
-
"user_tz": -330,
|
| 143 |
-
"elapsed": 5,
|
| 144 |
-
"user": {
|
| 145 |
-
"displayName": "Danesh Selwal",
|
| 146 |
-
"userId": "10565017814556927251"
|
| 147 |
-
}
|
| 148 |
-
}
|
| 149 |
-
},
|
| 150 |
-
"outputs": [],
|
| 151 |
-
"source": [
|
| 152 |
-
"SEED = 42\n",
|
| 153 |
-
"random.seed(SEED)\n",
|
| 154 |
-
"np.random.seed(SEED)\n",
|
| 155 |
-
"tf.random.set_seed(SEED)"
|
| 156 |
-
],
|
| 157 |
-
"id": "d1NVUhVRjme4"
|
| 158 |
-
},
|
| 159 |
-
{
|
| 160 |
-
"cell_type": "markdown",
|
| 161 |
-
"metadata": {
|
| 162 |
-
"id": "_HTyxJcIjme6"
|
| 163 |
-
},
|
| 164 |
-
"source": [
|
| 165 |
-
"# 2.0 \u2014 Configuration\n",
|
| 166 |
-
"\n",
|
| 167 |
-
"All project paths, dataset geometry, training hyperparameters, and per-architecture configs are defined here. Change values in this section only \u2014 nothing below should need editing for typical experiments."
|
| 168 |
-
],
|
| 169 |
-
"id": "_HTyxJcIjme6"
|
| 170 |
-
},
|
| 171 |
-
{
|
| 172 |
-
"cell_type": "markdown",
|
| 173 |
-
"metadata": {
|
| 174 |
-
"id": "qlYXkH0gjme7"
|
| 175 |
-
},
|
| 176 |
-
"source": [
|
| 177 |
-
"## 2.1 \u2014 Paths & Directories"
|
| 178 |
-
],
|
| 179 |
-
"id": "qlYXkH0gjme7"
|
| 180 |
-
},
|
| 181 |
-
{
|
| 182 |
-
"cell_type": "code",
|
| 183 |
-
"execution_count": null,
|
| 184 |
-
"metadata": {
|
| 185 |
-
"colab": {
|
| 186 |
-
"base_uri": "https://localhost:8080/"
|
| 187 |
-
},
|
| 188 |
-
"id": "YGJY_eCAjme7",
|
| 189 |
-
"outputId": "75f5d3b8-aae1-4203-ee2c-a4f966a6fa71",
|
| 190 |
-
"executionInfo": {
|
| 191 |
-
"status": "ok",
|
| 192 |
-
"timestamp": 1782312434483,
|
| 193 |
-
"user_tz": -330,
|
| 194 |
-
"elapsed": 8,
|
| 195 |
-
"user": {
|
| 196 |
-
"displayName": "Danesh Selwal",
|
| 197 |
-
"userId": "10565017814556927251"
|
| 198 |
-
}
|
| 199 |
-
}
|
| 200 |
-
},
|
| 201 |
-
"outputs": [],
|
| 202 |
-
"source": [
|
| 203 |
-
"PROJECT_ROOT = Path('/content/drive/My Drive/Classification/')\n",
|
| 204 |
-
"DATA_DIR = PROJECT_ROOT / 'data'\n",
|
| 205 |
-
"MODEL_DIR = PROJECT_ROOT / 'ensemble' / 'models'\n",
|
| 206 |
-
"MODEL_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 207 |
-
"PLOT_DIR = MODEL_DIR / 'training_plots'\n",
|
| 208 |
-
"PLOT_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 209 |
-
"VIS_DIR = MODEL_DIR / 'scene_visualizations'\n",
|
| 210 |
-
"VIS_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 211 |
-
"\n",
|
| 212 |
-
"DATA_FILE = DATA_DIR / 'DIAS.mat' # hyperspectral cube (H, W, Bands)\n",
|
| 213 |
-
"LABEL_FILE = DATA_DIR / 'DIAS_ref.mat' # reference / ground-truth label map\n",
|
| 214 |
-
"VIS_EXCEL_PATH = VIS_DIR / 'initial_classification_maps.xlsx'\n",
|
| 215 |
-
"\n",
|
| 216 |
-
"print('Data file :', DATA_FILE)\n",
|
| 217 |
-
"print('Label file :', LABEL_FILE)\n",
|
| 218 |
-
"print('Model dir :', MODEL_DIR)\n",
|
| 219 |
-
"print('Plot dir :', PLOT_DIR)"
|
| 220 |
-
],
|
| 221 |
-
"id": "YGJY_eCAjme7"
|
| 222 |
-
},
|
| 223 |
-
{
|
| 224 |
-
"cell_type": "markdown",
|
| 225 |
-
"metadata": {
|
| 226 |
-
"id": "jJujDppQjme8"
|
| 227 |
-
},
|
| 228 |
-
"source": [
|
| 229 |
-
"## 2.2 \u2014 Dataset & Training Hyperparameters"
|
| 230 |
-
],
|
| 231 |
-
"id": "jJujDppQjme8"
|
| 232 |
-
},
|
| 233 |
-
{
|
| 234 |
-
"cell_type": "code",
|
| 235 |
-
"execution_count": null,
|
| 236 |
-
"metadata": {
|
| 237 |
-
"id": "vbuDs_kJjme9",
|
| 238 |
-
"executionInfo": {
|
| 239 |
-
"status": "ok",
|
| 240 |
-
"timestamp": 1782312434496,
|
| 241 |
-
"user_tz": -330,
|
| 242 |
-
"elapsed": 14,
|
| 243 |
-
"user": {
|
| 244 |
-
"displayName": "Danesh Selwal",
|
| 245 |
-
"userId": "10565017814556927251"
|
| 246 |
-
}
|
| 247 |
-
}
|
| 248 |
-
},
|
| 249 |
-
"outputs": [],
|
| 250 |
-
"source": [
|
| 251 |
-
"# H, W, B are inferred automatically from the .mat cube shape once loaded in\n",
|
| 252 |
-
"# Section 3.2, so this notebook works unchanged for any scene size / band count.\n",
|
| 253 |
-
"H, W, B = None, None, None\n",
|
| 254 |
-
"PERCENTILE_CLIP = (2.0, 98.0) # robust per-band stretch range (2nd-98th percentile)\n",
|
| 255 |
-
"PATCH_SIZE = 9\n",
|
| 256 |
-
"INNER_PATCH = 3 # used by GFNet/ViT tokenisation\n",
|
| 257 |
-
"\n",
|
| 258 |
-
"TRAIN_PERCENT = 0.75\n",
|
| 259 |
-
"VAL_SPLIT_FROM_TRAIN = 0.20\n",
|
| 260 |
-
"\n",
|
| 261 |
-
"BATCH_SIZE = 128\n",
|
| 262 |
-
"EPOCHS = 100\n",
|
| 263 |
-
"LEARNING_RATE = 3e-4\n",
|
| 264 |
-
"DROPOUT_RATE = 0.25\n",
|
| 265 |
-
"\n",
|
| 266 |
-
"TRAIN_CFG = {\n",
|
| 267 |
-
" 'label_smoothing': 0.05,\n",
|
| 268 |
-
" 'weight_decay': 1e-4,\n",
|
| 269 |
-
" 'clipnorm': 1.0,\n",
|
| 270 |
-
" 'cosine_alpha': 0.05,\n",
|
| 271 |
-
"}"
|
| 272 |
-
],
|
| 273 |
-
"id": "vbuDs_kJjme9"
|
| 274 |
-
},
|
| 275 |
-
{
|
| 276 |
-
"cell_type": "markdown",
|
| 277 |
-
"metadata": {
|
| 278 |
-
"id": "FQVtnNAZjme9"
|
| 279 |
-
},
|
| 280 |
-
"source": [
|
| 281 |
-
"## 2.3 \u2014 Architecture Configs (Primary & Fallback)"
|
| 282 |
-
],
|
| 283 |
-
"id": "FQVtnNAZjme9"
|
| 284 |
-
},
|
| 285 |
-
{
|
| 286 |
-
"cell_type": "code",
|
| 287 |
-
"execution_count": null,
|
| 288 |
-
"metadata": {
|
| 289 |
-
"colab": {
|
| 290 |
-
"base_uri": "https://localhost:8080/"
|
| 291 |
-
},
|
| 292 |
-
"id": "GgqwWdkyjme9",
|
| 293 |
-
"outputId": "622f35a4-5e34-4c02-b9b3-baecb25ab54c",
|
| 294 |
-
"executionInfo": {
|
| 295 |
-
"status": "ok",
|
| 296 |
-
"timestamp": 1782312434522,
|
| 297 |
-
"user_tz": -330,
|
| 298 |
-
"elapsed": 21,
|
| 299 |
-
"user": {
|
| 300 |
-
"displayName": "Danesh Selwal",
|
| 301 |
-
"userId": "10565017814556927251"
|
| 302 |
-
}
|
| 303 |
-
}
|
| 304 |
-
},
|
| 305 |
-
"outputs": [],
|
| 306 |
-
"source": [
|
| 307 |
-
"CAPACITY_PRESET = 'legacy_arch'\n",
|
| 308 |
-
"\n",
|
| 309 |
-
"ALEXNET_CFG = {\n",
|
| 310 |
-
" 'conv_filters': [96, 256, 384, 384, 256],\n",
|
| 311 |
-
" 'dense_units': [4096, 1024, 256, 32],\n",
|
| 312 |
-
"}\n",
|
| 313 |
-
"\n",
|
| 314 |
-
"GFNET_CFG = {\n",
|
| 315 |
-
" 'hidden_dim': 512,\n",
|
| 316 |
-
" 'num_blocks': 5,\n",
|
| 317 |
-
" 'mlp_ratio': 4,\n",
|
| 318 |
-
"}\n",
|
| 319 |
-
"\n",
|
| 320 |
-
"VIT_CFG = {\n",
|
| 321 |
-
" 'projection_dim': 256,\n",
|
| 322 |
-
" 'num_heads': 4,\n",
|
| 323 |
-
" 'transformer_layers': 12,\n",
|
| 324 |
-
" 'mlp_multiplier': 2,\n",
|
| 325 |
-
" 'head_units': [512, 256, 128, 64],\n",
|
| 326 |
-
"}\n",
|
| 327 |
-
"\n",
|
| 328 |
-
"# Deterministic fallback configs if Colab runs out of memory\n",
|
| 329 |
-
"GFNET_FALLBACK_CFG = {\n",
|
| 330 |
-
" 'hidden_dim': 384,\n",
|
| 331 |
-
" 'num_blocks': 4,\n",
|
| 332 |
-
" 'mlp_ratio': 4,\n",
|
| 333 |
-
"}\n",
|
| 334 |
-
"\n",
|
| 335 |
-
"VIT_FALLBACK_CFG = {\n",
|
| 336 |
-
" 'projection_dim': 192,\n",
|
| 337 |
-
" 'num_heads': 4,\n",
|
| 338 |
-
" 'transformer_layers': 8,\n",
|
| 339 |
-
" 'mlp_multiplier': 2,\n",
|
| 340 |
-
" 'head_units': [384, 192, 96, 64],\n",
|
| 341 |
-
"}\n",
|
| 342 |
-
"\n",
|
| 343 |
-
"print('Architecture preset:', CAPACITY_PRESET)"
|
| 344 |
-
],
|
| 345 |
-
"id": "GgqwWdkyjme9"
|
| 346 |
-
},
|
| 347 |
-
{
|
| 348 |
-
"cell_type": "markdown",
|
| 349 |
-
"metadata": {
|
| 350 |
-
"id": "RDmmrIBtjme-"
|
| 351 |
-
},
|
| 352 |
-
"source": [
|
| 353 |
-
"## 2.4 \u2014 AlexNet Legacy Training Config\n",
|
| 354 |
-
"\n",
|
| 355 |
-
"Separate learning-rate settings for AlexNet, matching the original single-head script for uncertainty recovery."
|
| 356 |
-
],
|
| 357 |
-
"id": "RDmmrIBtjme-"
|
| 358 |
-
},
|
| 359 |
-
{
|
| 360 |
-
"cell_type": "code",
|
| 361 |
-
"execution_count": null,
|
| 362 |
-
"metadata": {
|
| 363 |
-
"id": "iaFlb4N0jme-",
|
| 364 |
-
"executionInfo": {
|
| 365 |
-
"status": "ok",
|
| 366 |
-
"timestamp": 1782312434543,
|
| 367 |
-
"user_tz": -330,
|
| 368 |
-
"elapsed": 19,
|
| 369 |
-
"user": {
|
| 370 |
-
"displayName": "Danesh Selwal",
|
| 371 |
-
"userId": "10565017814556927251"
|
| 372 |
-
}
|
| 373 |
-
}
|
| 374 |
-
},
|
| 375 |
-
"outputs": [],
|
| 376 |
-
"source": [
|
| 377 |
-
"ALEXNET_LEGACY_SPLIT_SEED = 10\n",
|
| 378 |
-
"ALEXNET_LEGACY_TRAIN_PERCENT = 0.75\n",
|
| 379 |
-
"ALEXNET_LR_START = 0.01\n",
|
| 380 |
-
"ALEXNET_LR_MAX = 0.02\n",
|
| 381 |
-
"ALEXNET_LR_MIN = 0.005"
|
| 382 |
-
],
|
| 383 |
-
"id": "iaFlb4N0jme-"
|
| 384 |
-
},
|
| 385 |
-
{
|
| 386 |
-
"cell_type": "markdown",
|
| 387 |
-
"metadata": {
|
| 388 |
-
"id": "xW4Xhcfkjme_"
|
| 389 |
-
},
|
| 390 |
-
"source": [
|
| 391 |
-
"## 2.5 \u2014 Visualisation Palette"
|
| 392 |
-
],
|
| 393 |
-
"id": "xW4Xhcfkjme_"
|
| 394 |
-
},
|
| 395 |
-
{
|
| 396 |
-
"cell_type": "code",
|
| 397 |
-
"execution_count": null,
|
| 398 |
-
"metadata": {
|
| 399 |
-
"id": "2mwkmnUnjme_",
|
| 400 |
-
"executionInfo": {
|
| 401 |
-
"status": "ok",
|
| 402 |
-
"timestamp": 1782312434545,
|
| 403 |
-
"user_tz": -330,
|
| 404 |
-
"elapsed": 4,
|
| 405 |
-
"user": {
|
| 406 |
-
"displayName": "Danesh Selwal",
|
| 407 |
-
"userId": "10565017814556927251"
|
| 408 |
-
}
|
| 409 |
-
}
|
| 410 |
-
},
|
| 411 |
-
"outputs": [],
|
| 412 |
-
"source": [
|
| 413 |
-
"CLASS_COLOR_BASE = [\n",
|
| 414 |
-
" '#0000FF', '#00FF00', '#FF0000', '#00FFFF',\n",
|
| 415 |
-
" '#FF00FF', '#FFFF00', '#A52A2A', '#FFA500',\n",
|
| 416 |
-
" '#7FFF00', '#8A2BE2',\n",
|
| 417 |
-
"]\n",
|
| 418 |
-
"BACKGROUND_COLOR = '#000000'\n",
|
| 419 |
-
"\n",
|
| 420 |
-
"import colorsys\n",
|
| 421 |
-
"\n",
|
| 422 |
-
"def _generate_extra_shades(n_extra, hue_offset=0.05, sat=0.85, val=0.95):\n",
|
| 423 |
-
" \"\"\"Bold, highly-saturated extra hues in the same style as CLASS_COLOR_BASE,\n",
|
| 424 |
-
" for datasets with more than 10 classes.\"\"\"\n",
|
| 425 |
-
" shades = []\n",
|
| 426 |
-
" for i in range(n_extra):\n",
|
| 427 |
-
" hue = (hue_offset + i / max(n_extra, 1)) % 1.0\n",
|
| 428 |
-
" r, g, b = colorsys.hsv_to_rgb(hue, sat, val)\n",
|
| 429 |
-
" shades.append('#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255)))\n",
|
| 430 |
-
" return shades\n",
|
| 431 |
-
"\n",
|
| 432 |
-
"def get_class_colors(n):\n",
|
| 433 |
-
" \"\"\"Return n distinct hex colours, extending the base 10 with additional bold\n",
|
| 434 |
-
" shades in the same saturated style if needed.\"\"\"\n",
|
| 435 |
-
" if n <= len(CLASS_COLOR_BASE):\n",
|
| 436 |
-
" return CLASS_COLOR_BASE[:n]\n",
|
| 437 |
-
" return CLASS_COLOR_BASE + _generate_extra_shades(n - len(CLASS_COLOR_BASE))"
|
| 438 |
-
],
|
| 439 |
-
"id": "2mwkmnUnjme_"
|
| 440 |
-
},
|
| 441 |
-
{
|
| 442 |
-
"cell_type": "markdown",
|
| 443 |
-
"metadata": {
|
| 444 |
-
"id": "WlGs-SYMjme_"
|
| 445 |
-
},
|
| 446 |
-
"source": [
|
| 447 |
-
"# 3.0 \u2014 Data Loading & Preprocessing\n",
|
| 448 |
-
"\n",
|
| 449 |
-
"Loads the hyperspectral/multispectral scene and labels from .mat files, robustly normalises per-band via percentile clip, extracts labeled patches, and creates train/val/test splits."
|
| 450 |
-
],
|
| 451 |
-
"id": "WlGs-SYMjme_"
|
| 452 |
-
},
|
| 453 |
-
{
|
| 454 |
-
"cell_type": "markdown",
|
| 455 |
-
"metadata": {
|
| 456 |
-
"id": "lqxHO9jMjme_"
|
| 457 |
-
},
|
| 458 |
-
"source": [
|
| 459 |
-
"## 3.1 \u2014 Data Loading & Patch Extraction Functions"
|
| 460 |
-
],
|
| 461 |
-
"id": "lqxHO9jMjme_"
|
| 462 |
-
},
|
| 463 |
-
{
|
| 464 |
-
"cell_type": "code",
|
| 465 |
-
"execution_count": null,
|
| 466 |
-
"metadata": {
|
| 467 |
-
"id": "m-DJahHZjme_",
|
| 468 |
-
"executionInfo": {
|
| 469 |
-
"status": "ok",
|
| 470 |
-
"timestamp": 1782312434563,
|
| 471 |
-
"user_tz": -330,
|
| 472 |
-
"elapsed": 16,
|
| 473 |
-
"user": {
|
| 474 |
-
"displayName": "Danesh Selwal",
|
| 475 |
-
"userId": "10565017814556927251"
|
| 476 |
-
}
|
| 477 |
-
}
|
| 478 |
-
},
|
| 479 |
-
"outputs": [],
|
| 480 |
-
"source": [
|
| 481 |
-
"def load_scene_from_mat(data_path, label_path, data_key=None, label_key=None):\n",
|
| 482 |
-
" \"\"\"Load a hyperspectral/multispectral scene + reference labels from .mat files.\n",
|
| 483 |
-
"\n",
|
| 484 |
-
" Auto-detects the variable name inside each .mat file if not given (matches\n",
|
| 485 |
-
" files such as DIAS.mat / DIAS_ref.mat storing the array under a single\n",
|
| 486 |
-
" named variable). Each spectral band is robustly stretched to [0, 1] using\n",
|
| 487 |
-
" a percentile clip (PERCENTILE_CLIP) rather than literal min/max \u2014 a handful\n",
|
| 488 |
-
" of saturated/no-data pixels (e.g. sensor clipping at int8 range limits\n",
|
| 489 |
-
" -128/127) would otherwise dominate the stretch.\n",
|
| 490 |
-
" \"\"\"\n",
|
| 491 |
-
" def _first_array(mat_dict, key):\n",
|
| 492 |
-
" if key is not None:\n",
|
| 493 |
-
" return mat_dict[key]\n",
|
| 494 |
-
" candidates = [v for k, v in mat_dict.items() if not k.startswith('__')]\n",
|
| 495 |
-
" if not candidates:\n",
|
| 496 |
-
" raise ValueError(f'No data arrays found in {mat_dict.keys()}')\n",
|
| 497 |
-
" return candidates[0]\n",
|
| 498 |
-
"\n",
|
| 499 |
-
" data_mat = loadmat(data_path)\n",
|
| 500 |
-
" label_mat = loadmat(label_path)\n",
|
| 501 |
-
"\n",
|
| 502 |
-
" x = _first_array(data_mat, data_key).astype(np.float32)\n",
|
| 503 |
-
" y = _first_array(label_mat, label_key).astype(np.int32)\n",
|
| 504 |
-
"\n",
|
| 505 |
-
" if x.ndim != 3:\n",
|
| 506 |
-
" raise ValueError(f'Expected a (H, W, Bands) cube, got shape {x.shape}')\n",
|
| 507 |
-
" if y.shape != x.shape[:2]:\n",
|
| 508 |
-
" raise ValueError(f'Label shape {y.shape} does not match image shape {x.shape[:2]}')\n",
|
| 509 |
-
"\n",
|
| 510 |
-
" height, width, bands = x.shape\n",
|
| 511 |
-
"\n",
|
| 512 |
-
" x_norm = np.empty_like(x, dtype=np.float32)\n",
|
| 513 |
-
" for b in range(bands):\n",
|
| 514 |
-
" band = x[:, :, b]\n",
|
| 515 |
-
" lo = np.percentile(band, PERCENTILE_CLIP[0])\n",
|
| 516 |
-
" hi = np.percentile(band, PERCENTILE_CLIP[1])\n",
|
| 517 |
-
" denom = max(hi - lo, 1e-8)\n",
|
| 518 |
-
" band_clipped = np.clip(band, lo, hi)\n",
|
| 519 |
-
" x_norm[:, :, b] = (band_clipped - lo) / denom\n",
|
| 520 |
-
"\n",
|
| 521 |
-
" return x_norm, y, height, width, bands\n",
|
| 522 |
-
"\n",
|
| 523 |
-
"\n",
|
| 524 |
-
"def extract_labeled_patches(x, y, patch_size=9):\n",
|
| 525 |
-
" \"\"\"Extract fixed-size patches centred on every labeled (non-zero) pixel.\"\"\"\n",
|
| 526 |
-
" pad = patch_size // 2\n",
|
| 527 |
-
" x_pad = np.pad(x, ((pad, pad), (pad, pad), (0, 0)), mode='edge')\n",
|
| 528 |
-
"\n",
|
| 529 |
-
" coords = np.argwhere(y > 0)\n",
|
| 530 |
-
" patches = np.empty((coords.shape[0], patch_size, patch_size, x.shape[-1]), dtype=np.float32)\n",
|
| 531 |
-
" labels = np.empty((coords.shape[0],), dtype=np.int32)\n",
|
| 532 |
-
"\n",
|
| 533 |
-
" for i, (r, c) in enumerate(coords):\n",
|
| 534 |
-
" patches[i] = x_pad[r:r + patch_size, c:c + patch_size, :]\n",
|
| 535 |
-
" labels[i] = int(y[r, c]) - 1 # convert class IDs to 0..C-1\n",
|
| 536 |
-
"\n",
|
| 537 |
-
" return patches, labels, coords"
|
| 538 |
-
],
|
| 539 |
-
"id": "m-DJahHZjme_"
|
| 540 |
-
},
|
| 541 |
-
{
|
| 542 |
-
"cell_type": "markdown",
|
| 543 |
-
"metadata": {
|
| 544 |
-
"id": "suyQHfy0jmfA"
|
| 545 |
-
},
|
| 546 |
-
"source": [
|
| 547 |
-
"## 3.2 \u2014 Load Dataset"
|
| 548 |
-
],
|
| 549 |
-
"id": "suyQHfy0jmfA"
|
| 550 |
-
},
|
| 551 |
-
{
|
| 552 |
-
"cell_type": "code",
|
| 553 |
-
"execution_count": null,
|
| 554 |
-
"metadata": {
|
| 555 |
-
"colab": {
|
| 556 |
-
"base_uri": "https://localhost:8080/"
|
| 557 |
-
},
|
| 558 |
-
"id": "Cm8sPt9NjmfA",
|
| 559 |
-
"outputId": "0cab272a-842d-43a7-a1e0-30293d03580c",
|
| 560 |
-
"executionInfo": {
|
| 561 |
-
"status": "ok",
|
| 562 |
-
"timestamp": 1782312434709,
|
| 563 |
-
"user_tz": -330,
|
| 564 |
-
"elapsed": 148,
|
| 565 |
-
"user": {
|
| 566 |
-
"displayName": "Danesh Selwal",
|
| 567 |
-
"userId": "10565017814556927251"
|
| 568 |
-
}
|
| 569 |
-
}
|
| 570 |
-
},
|
| 571 |
-
"outputs": [],
|
| 572 |
-
"source": [
|
| 573 |
-
"x_img, y_img, H, W, B = load_scene_from_mat(DATA_FILE, LABEL_FILE)\n",
|
| 574 |
-
"X, y, coords = extract_labeled_patches(x_img, y_img, PATCH_SIZE)\n",
|
| 575 |
-
"\n",
|
| 576 |
-
"num_classes = int(np.unique(y).size)\n",
|
| 577 |
-
"input_shape = (PATCH_SIZE, PATCH_SIZE, B)\n",
|
| 578 |
-
"\n",
|
| 579 |
-
"print('Scene geometry: H=%d W=%d Bands=%d' % (H, W, B))\n",
|
| 580 |
-
"print('x_img:', x_img.shape, ' y_img:', y_img.shape)\n",
|
| 581 |
-
"print('Labeled samples:', X.shape[0])\n",
|
| 582 |
-
"print('Patch tensor:', X.shape)\n",
|
| 583 |
-
"print('Num classes:', num_classes)"
|
| 584 |
-
],
|
| 585 |
-
"id": "Cm8sPt9NjmfA"
|
| 586 |
-
},
|
| 587 |
-
{
|
| 588 |
-
"cell_type": "markdown",
|
| 589 |
-
"metadata": {
|
| 590 |
-
"id": "ROMwfXI8jmfA"
|
| 591 |
-
},
|
| 592 |
-
"source": [
|
| 593 |
-
"## 3.3 \u2014 Train / Val / Test Splits\n",
|
| 594 |
-
"\n",
|
| 595 |
-
"Creates the primary stratified splits used by GFNet and ViT, plus the legacy split used by AlexNet to match original script behaviour."
|
| 596 |
-
],
|
| 597 |
-
"id": "ROMwfXI8jmfA"
|
| 598 |
-
},
|
| 599 |
-
{
|
| 600 |
-
"cell_type": "code",
|
| 601 |
-
"execution_count": null,
|
| 602 |
-
"metadata": {
|
| 603 |
-
"colab": {
|
| 604 |
-
"base_uri": "https://localhost:8080/"
|
| 605 |
-
},
|
| 606 |
-
"id": "fe2Tg2PJjmfA",
|
| 607 |
-
"outputId": "f3a59e38-7c06-45e8-8a70-ef038f5b4adf",
|
| 608 |
-
"executionInfo": {
|
| 609 |
-
"status": "ok",
|
| 610 |
-
"timestamp": 1782312434829,
|
| 611 |
-
"user_tz": -330,
|
| 612 |
-
"elapsed": 105,
|
| 613 |
-
"user": {
|
| 614 |
-
"displayName": "Danesh Selwal",
|
| 615 |
-
"userId": "10565017814556927251"
|
| 616 |
-
}
|
| 617 |
-
}
|
| 618 |
-
},
|
| 619 |
-
"outputs": [],
|
| 620 |
-
"source": [
|
| 621 |
-
"# Primary split (GFNet / ViT)\n",
|
| 622 |
-
"x_train_full, x_test, y_train_full, y_test = train_test_split(\n",
|
| 623 |
-
" X, y, train_size=TRAIN_PERCENT, random_state=SEED, stratify=y,\n",
|
| 624 |
-
")\n",
|
| 625 |
-
"x_train, x_val, y_train, y_val = train_test_split(\n",
|
| 626 |
-
" x_train_full, y_train_full,\n",
|
| 627 |
-
" test_size=VAL_SPLIT_FROM_TRAIN, random_state=SEED, stratify=y_train_full,\n",
|
| 628 |
-
")\n",
|
| 629 |
-
"\n",
|
| 630 |
-
"y_train_cat = keras.utils.to_categorical(y_train, num_classes)\n",
|
| 631 |
-
"y_val_cat = keras.utils.to_categorical(y_val, num_classes)\n",
|
| 632 |
-
"y_test_cat = keras.utils.to_categorical(y_test, num_classes)\n",
|
| 633 |
-
"\n",
|
| 634 |
-
"print('Train:', x_train.shape, y_train.shape)\n",
|
| 635 |
-
"print('Val :', x_val.shape, y_val.shape)\n",
|
| 636 |
-
"print('Test :', x_test.shape, y_test.shape)\n",
|
| 637 |
-
"\n",
|
| 638 |
-
"# AlexNet legacy split (matches old single-head script behaviour)\n",
|
| 639 |
-
"x_train_alex, x_test_alex, y_train_alex, y_test_alex = train_test_split(\n",
|
| 640 |
-
" X, y,\n",
|
| 641 |
-
" train_size=ALEXNET_LEGACY_TRAIN_PERCENT,\n",
|
| 642 |
-
" random_state=ALEXNET_LEGACY_SPLIT_SEED,\n",
|
| 643 |
-
" stratify=y,\n",
|
| 644 |
-
")\n",
|
| 645 |
-
"y_train_alex_cat = keras.utils.to_categorical(y_train_alex, num_classes)\n",
|
| 646 |
-
"y_test_alex_cat = keras.utils.to_categorical(y_test_alex, num_classes)\n",
|
| 647 |
-
"\n",
|
| 648 |
-
"print('AlexNet Train/Test:', x_train_alex.shape, y_train_alex.shape,\n",
|
| 649 |
-
" x_test_alex.shape, y_test_alex.shape)"
|
| 650 |
-
],
|
| 651 |
-
"id": "fe2Tg2PJjmfA"
|
| 652 |
-
},
|
| 653 |
-
{
|
| 654 |
-
"cell_type": "markdown",
|
| 655 |
-
"metadata": {
|
| 656 |
-
"id": "djg40XJDjmfB"
|
| 657 |
-
},
|
| 658 |
-
"source": [
|
| 659 |
-
"# 4.0 \u2014 Model Definitions\n",
|
| 660 |
-
"\n",
|
| 661 |
-
"Defines three single-head architectures: AlexNet-based CNN, Global Filter Network (GFNet), and Vision Transformer (ViT with U-Net style skip connections). Custom Keras layers are registered for serialisation compatibility."
|
| 662 |
-
],
|
| 663 |
-
"id": "djg40XJDjmfB"
|
| 664 |
-
},
|
| 665 |
-
{
|
| 666 |
-
"cell_type": "markdown",
|
| 667 |
-
"metadata": {
|
| 668 |
-
"id": "StRqys60jmfB"
|
| 669 |
-
},
|
| 670 |
-
"source": [
|
| 671 |
-
"## 4.1 \u2014 Shared Custom Keras Layers\n",
|
| 672 |
-
"\n",
|
| 673 |
-
"Reusable layers shared across GFNet and ViT: patch extraction, positional encoding, and the frequency-domain global filter."
|
| 674 |
-
],
|
| 675 |
-
"id": "StRqys60jmfB"
|
| 676 |
-
},
|
| 677 |
-
{
|
| 678 |
-
"cell_type": "code",
|
| 679 |
-
"execution_count": null,
|
| 680 |
-
"metadata": {
|
| 681 |
-
"id": "-S-TKPEjjmfB",
|
| 682 |
-
"executionInfo": {
|
| 683 |
-
"status": "ok",
|
| 684 |
-
"timestamp": 1782312434841,
|
| 685 |
-
"user_tz": -330,
|
| 686 |
-
"elapsed": 15,
|
| 687 |
-
"user": {
|
| 688 |
-
"displayName": "Danesh Selwal",
|
| 689 |
-
"userId": "10565017814556927251"
|
| 690 |
-
}
|
| 691 |
-
}
|
| 692 |
-
},
|
| 693 |
-
"outputs": [],
|
| 694 |
-
"source": [
|
| 695 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 696 |
-
"class PatchExtractor(layers.Layer):\n",
|
| 697 |
-
" \"\"\"Extracts non-overlapping patches from an image tensor.\"\"\"\n",
|
| 698 |
-
"\n",
|
| 699 |
-
" def __init__(self, patch_size=3, **kwargs):\n",
|
| 700 |
-
" super().__init__(**kwargs)\n",
|
| 701 |
-
" self.patch_size = patch_size\n",
|
| 702 |
-
"\n",
|
| 703 |
-
" def call(self, images):\n",
|
| 704 |
-
" patches = tf.image.extract_patches(\n",
|
| 705 |
-
" images=images,\n",
|
| 706 |
-
" sizes=[1, self.patch_size, self.patch_size, 1],\n",
|
| 707 |
-
" strides=[1, self.patch_size, self.patch_size, 1],\n",
|
| 708 |
-
" rates=[1, 1, 1, 1],\n",
|
| 709 |
-
" padding='VALID',\n",
|
| 710 |
-
" )\n",
|
| 711 |
-
" batch = tf.shape(images)[0]\n",
|
| 712 |
-
" num_patches = tf.shape(patches)[1] * tf.shape(patches)[2]\n",
|
| 713 |
-
" patch_dim = tf.shape(patches)[-1]\n",
|
| 714 |
-
" return tf.reshape(patches, [batch, num_patches, patch_dim])\n",
|
| 715 |
-
"\n",
|
| 716 |
-
" def get_config(self):\n",
|
| 717 |
-
" cfg = super().get_config()\n",
|
| 718 |
-
" cfg.update({'patch_size': self.patch_size})\n",
|
| 719 |
-
" return cfg\n",
|
| 720 |
-
"\n",
|
| 721 |
-
"\n",
|
| 722 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 723 |
-
"class PatchPositionEncoder(layers.Layer):\n",
|
| 724 |
-
" \"\"\"Projects patches to a hidden dim and adds learned positional embeddings (no CLS token).\"\"\"\n",
|
| 725 |
-
"\n",
|
| 726 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 727 |
-
" super().__init__(**kwargs)\n",
|
| 728 |
-
" self.num_patches = num_patches\n",
|
| 729 |
-
" self.projection_dim = projection_dim\n",
|
| 730 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 731 |
-
" self.position_embedding = layers.Embedding(input_dim=num_patches, output_dim=projection_dim)\n",
|
| 732 |
-
"\n",
|
| 733 |
-
" def call(self, patches):\n",
|
| 734 |
-
" positions = tf.range(start=0, limit=self.num_patches, delta=1)\n",
|
| 735 |
-
" return self.projection(patches) + self.position_embedding(positions)\n",
|
| 736 |
-
"\n",
|
| 737 |
-
" def get_config(self):\n",
|
| 738 |
-
" cfg = super().get_config()\n",
|
| 739 |
-
" cfg.update({'num_patches': self.num_patches, 'projection_dim': self.projection_dim})\n",
|
| 740 |
-
" return cfg\n",
|
| 741 |
-
"\n",
|
| 742 |
-
"\n",
|
| 743 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 744 |
-
"class GlobalFilterLayer(layers.Layer):\n",
|
| 745 |
-
" \"\"\"Applies a learnable complex-valued frequency filter via 2-D FFT (GFNet core block).\"\"\"\n",
|
| 746 |
-
"\n",
|
| 747 |
-
" def __init__(self, token_side, **kwargs):\n",
|
| 748 |
-
" super().__init__(**kwargs)\n",
|
| 749 |
-
" self.token_side = token_side\n",
|
| 750 |
-
"\n",
|
| 751 |
-
" def build(self, input_shape):\n",
|
| 752 |
-
" channels = int(input_shape[-1])\n",
|
| 753 |
-
" self.w_real = self.add_weight(\n",
|
| 754 |
-
" name='w_real', shape=(self.token_side, self.token_side, channels),\n",
|
| 755 |
-
" initializer='glorot_uniform', trainable=True,\n",
|
| 756 |
-
" )\n",
|
| 757 |
-
" self.w_imag = self.add_weight(\n",
|
| 758 |
-
" name='w_imag', shape=(self.token_side, self.token_side, channels),\n",
|
| 759 |
-
" initializer='zeros', trainable=True,\n",
|
| 760 |
-
" )\n",
|
| 761 |
-
" super().build(input_shape)\n",
|
| 762 |
-
"\n",
|
| 763 |
-
" def call(self, x):\n",
|
| 764 |
-
" batch = tf.shape(x)[0]\n",
|
| 765 |
-
" channels = tf.shape(x)[-1]\n",
|
| 766 |
-
" x_2d = tf.reshape(x, [batch, self.token_side, self.token_side, channels])\n",
|
| 767 |
-
" x_fft = tf.signal.fft2d(tf.cast(x_2d, tf.complex64))\n",
|
| 768 |
-
" w_complex = tf.complex(self.w_real, self.w_imag)\n",
|
| 769 |
-
" x_filtered = x_fft * w_complex\n",
|
| 770 |
-
" x_spatial = tf.math.real(tf.signal.ifft2d(x_filtered))\n",
|
| 771 |
-
" return tf.reshape(x_spatial, [batch, self.token_side * self.token_side, channels])\n",
|
| 772 |
-
"\n",
|
| 773 |
-
" def get_config(self):\n",
|
| 774 |
-
" cfg = super().get_config()\n",
|
| 775 |
-
" cfg.update({'token_side': self.token_side})\n",
|
| 776 |
-
" return cfg\n",
|
| 777 |
-
"\n",
|
| 778 |
-
"\n",
|
| 779 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 780 |
-
"class PatchEncoderWithCLS(layers.Layer):\n",
|
| 781 |
-
" \"\"\"Projects patches to a hidden dim and prepends a trainable CLS token with positional embeddings.\"\"\"\n",
|
| 782 |
-
"\n",
|
| 783 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 784 |
-
" super().__init__(**kwargs)\n",
|
| 785 |
-
" self.num_patches = num_patches\n",
|
| 786 |
-
" self.projection_dim = projection_dim\n",
|
| 787 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 788 |
-
" self.position_embedding = layers.Embedding(input_dim=num_patches + 1, output_dim=projection_dim)\n",
|
| 789 |
-
"\n",
|
| 790 |
-
" def build(self, input_shape):\n",
|
| 791 |
-
" self.cls_token = self.add_weight(\n",
|
| 792 |
-
" name='cls_token', shape=(1, 1, self.projection_dim),\n",
|
| 793 |
-
" initializer='zeros', trainable=True,\n",
|
| 794 |
-
" )\n",
|
| 795 |
-
" super().build(input_shape)\n",
|
| 796 |
-
"\n",
|
| 797 |
-
" def call(self, patches):\n",
|
| 798 |
-
" batch = tf.shape(patches)[0]\n",
|
| 799 |
-
" patch_proj = self.projection(patches)\n",
|
| 800 |
-
" cls_tokens = tf.repeat(self.cls_token, repeats=batch, axis=0)\n",
|
| 801 |
-
" x = tf.concat([cls_tokens, patch_proj], axis=1)\n",
|
| 802 |
-
" positions = tf.range(start=0, limit=self.num_patches + 1, delta=1)\n",
|
| 803 |
-
" return x + self.position_embedding(positions)\n",
|
| 804 |
-
"\n",
|
| 805 |
-
" def get_config(self):\n",
|
| 806 |
-
" cfg = super().get_config()\n",
|
| 807 |
-
" cfg.update({'num_patches': self.num_patches, 'projection_dim': self.projection_dim})\n",
|
| 808 |
-
" return cfg"
|
| 809 |
-
],
|
| 810 |
-
"id": "-S-TKPEjjmfB"
|
| 811 |
-
},
|
| 812 |
-
{
|
| 813 |
-
"cell_type": "markdown",
|
| 814 |
-
"metadata": {
|
| 815 |
-
"id": "7bVMnqfkjmfB"
|
| 816 |
-
},
|
| 817 |
-
"source": [
|
| 818 |
-
"## 4.2 \u2014 AlexNet-Based CNN (Single-Head, Legacy Architecture)"
|
| 819 |
-
],
|
| 820 |
-
"id": "7bVMnqfkjmfB"
|
| 821 |
-
},
|
| 822 |
-
{
|
| 823 |
-
"cell_type": "code",
|
| 824 |
-
"execution_count": null,
|
| 825 |
-
"metadata": {
|
| 826 |
-
"id": "bh2CxuNEjmfC",
|
| 827 |
-
"executionInfo": {
|
| 828 |
-
"status": "ok",
|
| 829 |
-
"timestamp": 1782312434892,
|
| 830 |
-
"user_tz": -330,
|
| 831 |
-
"elapsed": 7,
|
| 832 |
-
"user": {
|
| 833 |
-
"displayName": "Danesh Selwal",
|
| 834 |
-
"userId": "10565017814556927251"
|
| 835 |
-
}
|
| 836 |
-
}
|
| 837 |
-
},
|
| 838 |
-
"outputs": [],
|
| 839 |
-
"source": [
|
| 840 |
-
"def build_alexnet(input_shape, num_classes, dropout_rate=0.25, cfg=None):\n",
|
| 841 |
-
" \"\"\"Build a legacy AlexNet-style CNN with configurable conv and dense layer sizes.\"\"\"\n",
|
| 842 |
-
" cfg = cfg or ALEXNET_CFG\n",
|
| 843 |
-
"\n",
|
| 844 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 845 |
-
" x = inputs\n",
|
| 846 |
-
"\n",
|
| 847 |
-
" for i, filters in enumerate(cfg['conv_filters'], start=1):\n",
|
| 848 |
-
" x = layers.Conv2D(filters, (3, 3), activation='relu', padding='same', name=f'alex_conv_{i}')(x)\n",
|
| 849 |
-
"\n",
|
| 850 |
-
" x = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same', name='alex_pool')(x)\n",
|
| 851 |
-
"\n",
|
| 852 |
-
" dense_units = cfg['dense_units']\n",
|
| 853 |
-
" x = layers.Flatten(name='alex_flatten')(x)\n",
|
| 854 |
-
" x = layers.Dense(dense_units[0], activation='relu', name='alex_fc1')(x)\n",
|
| 855 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_1')(x)\n",
|
| 856 |
-
" x = layers.Dense(dense_units[1], activation='relu', name='alex_fc2')(x)\n",
|
| 857 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_2')(x)\n",
|
| 858 |
-
" x = layers.Dense(dense_units[2], activation='relu', name='alex_fc3')(x)\n",
|
| 859 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_3')(x)\n",
|
| 860 |
-
" x = layers.Dense(dense_units[3], activation='relu', name='alex_fc4')(x)\n",
|
| 861 |
-
"\n",
|
| 862 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='alex_logits')(x)\n",
|
| 863 |
-
" return keras.Model(inputs, outputs, name='AlexNet_SingleHead')"
|
| 864 |
-
],
|
| 865 |
-
"id": "bh2CxuNEjmfC"
|
| 866 |
-
},
|
| 867 |
-
{
|
| 868 |
-
"cell_type": "markdown",
|
| 869 |
-
"metadata": {
|
| 870 |
-
"id": "wHEWTSBgjmfC"
|
| 871 |
-
},
|
| 872 |
-
"source": [
|
| 873 |
-
"## 4.3 \u2014 Global Filter Network (Single-Head, Legacy Architecture)"
|
| 874 |
-
],
|
| 875 |
-
"id": "wHEWTSBgjmfC"
|
| 876 |
-
},
|
| 877 |
-
{
|
| 878 |
-
"cell_type": "code",
|
| 879 |
-
"execution_count": null,
|
| 880 |
-
"metadata": {
|
| 881 |
-
"id": "BTSyxIIKjmfC",
|
| 882 |
-
"executionInfo": {
|
| 883 |
-
"status": "ok",
|
| 884 |
-
"timestamp": 1782312434904,
|
| 885 |
-
"user_tz": -330,
|
| 886 |
-
"elapsed": 16,
|
| 887 |
-
"user": {
|
| 888 |
-
"displayName": "Danesh Selwal",
|
| 889 |
-
"userId": "10565017814556927251"
|
| 890 |
-
}
|
| 891 |
-
}
|
| 892 |
-
},
|
| 893 |
-
"outputs": [],
|
| 894 |
-
"source": [
|
| 895 |
-
"def gf_block(x, token_side, dim, mlp_ratio=4, dropout_rate=0.25, name_prefix='gf'):\n",
|
| 896 |
-
" \"\"\"Single GFNet block: global filter in frequency domain followed by a gated MLP, with residual.\"\"\"\n",
|
| 897 |
-
" y = layers.LayerNormalization(name=f'{name_prefix}_ln1')(x)\n",
|
| 898 |
-
" y = GlobalFilterLayer(token_side, name=f'{name_prefix}_gfilter')(y)\n",
|
| 899 |
-
" y = layers.LayerNormalization(name=f'{name_prefix}_ln2')(y)\n",
|
| 900 |
-
" y = layers.Dense(dim * mlp_ratio, activation=tf.keras.activations.gelu, name=f'{name_prefix}_mlp1')(y)\n",
|
| 901 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop1')(y)\n",
|
| 902 |
-
" y = layers.Dense(dim, activation=tf.keras.activations.gelu, name=f'{name_prefix}_mlp2')(y)\n",
|
| 903 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop2')(y)\n",
|
| 904 |
-
" return layers.Add(name=f'{name_prefix}_add')([x, y])\n",
|
| 905 |
-
"\n",
|
| 906 |
-
"\n",
|
| 907 |
-
"def build_gfnet(input_shape, num_classes, inner_patch=3,\n",
|
| 908 |
-
" hidden_dim=512, num_blocks=5, mlp_ratio=4, dropout_rate=0.25):\n",
|
| 909 |
-
" \"\"\"Build a Global Filter Network with configurable depth and width.\"\"\"\n",
|
| 910 |
-
" num_patches = (input_shape[0] // inner_patch) * (input_shape[1] // inner_patch)\n",
|
| 911 |
-
" token_side = int(np.sqrt(num_patches))\n",
|
| 912 |
-
"\n",
|
| 913 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 914 |
-
" x = PatchExtractor(inner_patch, name='gf_patch_extractor')(inputs)\n",
|
| 915 |
-
" x = PatchPositionEncoder(num_patches, hidden_dim, name='gf_patch_encoder')(x)\n",
|
| 916 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_1')(x)\n",
|
| 917 |
-
"\n",
|
| 918 |
-
" for i in range(num_blocks):\n",
|
| 919 |
-
" x = gf_block(x, token_side, hidden_dim, mlp_ratio, dropout_rate,\n",
|
| 920 |
-
" name_prefix=f'gf_block_{i+1}')\n",
|
| 921 |
-
"\n",
|
| 922 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_2')(x)\n",
|
| 923 |
-
" x = layers.LayerNormalization(name='gf_final_ln')(x)\n",
|
| 924 |
-
" x = layers.GlobalAveragePooling1D(name='gf_gap')(x)\n",
|
| 925 |
-
" x = layers.Flatten(name='gf_flatten')(x)\n",
|
| 926 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_3')(x)\n",
|
| 927 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='gf_logits')(x)\n",
|
| 928 |
-
"\n",
|
| 929 |
-
" return keras.Model(inputs, outputs, name='GFNet_SingleHead')"
|
| 930 |
-
],
|
| 931 |
-
"id": "BTSyxIIKjmfC"
|
| 932 |
-
},
|
| 933 |
-
{
|
| 934 |
-
"cell_type": "markdown",
|
| 935 |
-
"metadata": {
|
| 936 |
-
"id": "vakSvbuAjmfC"
|
| 937 |
-
},
|
| 938 |
-
"source": [
|
| 939 |
-
"## 4.4 \u2014 Vision Transformer with U-Net Skip Connections (Single-Head)"
|
| 940 |
-
],
|
| 941 |
-
"id": "vakSvbuAjmfC"
|
| 942 |
-
},
|
| 943 |
-
{
|
| 944 |
-
"cell_type": "code",
|
| 945 |
-
"execution_count": null,
|
| 946 |
-
"metadata": {
|
| 947 |
-
"id": "MHgOzlppjmfD",
|
| 948 |
-
"executionInfo": {
|
| 949 |
-
"status": "ok",
|
| 950 |
-
"timestamp": 1782312434906,
|
| 951 |
-
"user_tz": -330,
|
| 952 |
-
"elapsed": 16,
|
| 953 |
-
"user": {
|
| 954 |
-
"displayName": "Danesh Selwal",
|
| 955 |
-
"userId": "10565017814556927251"
|
| 956 |
-
}
|
| 957 |
-
}
|
| 958 |
-
},
|
| 959 |
-
"outputs": [],
|
| 960 |
-
"source": [
|
| 961 |
-
"def transformer_block(x, num_heads, projection_dim, mlp_dim, dropout_rate, name_prefix):\n",
|
| 962 |
-
" \"\"\"Standard transformer block: MHSA + FFN, each with pre-LN and residual connection.\"\"\"\n",
|
| 963 |
-
" y = layers.LayerNormalization(epsilon=1e-6, name=f'{name_prefix}_ln1')(x)\n",
|
| 964 |
-
" y = layers.MultiHeadAttention(\n",
|
| 965 |
-
" num_heads=num_heads, key_dim=projection_dim,\n",
|
| 966 |
-
" dropout=dropout_rate, name=f'{name_prefix}_mha',\n",
|
| 967 |
-
" )(y, y)\n",
|
| 968 |
-
" x = layers.Add(name=f'{name_prefix}_add1')([y, x])\n",
|
| 969 |
-
"\n",
|
| 970 |
-
" y = layers.LayerNormalization(epsilon=1e-6, name=f'{name_prefix}_ln2')(x)\n",
|
| 971 |
-
" y = layers.Dense(mlp_dim, activation=tf.keras.activations.gelu, name=f'{name_prefix}_mlp1')(y)\n",
|
| 972 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop1')(y)\n",
|
| 973 |
-
" y = layers.Dense(projection_dim, activation=tf.keras.activations.gelu, name=f'{name_prefix}_mlp2')(y)\n",
|
| 974 |
-
" y = layers.Dropout(dropout_rate, name=f'{name_prefix}_drop2')(y)\n",
|
| 975 |
-
" return layers.Add(name=f'{name_prefix}_add2')([y, x])\n",
|
| 976 |
-
"\n",
|
| 977 |
-
"\n",
|
| 978 |
-
"def build_vit_unet_singlehead(\n",
|
| 979 |
-
" input_shape, num_classes, inner_patch=3,\n",
|
| 980 |
-
" projection_dim=256, num_heads=4, transformer_layers=12,\n",
|
| 981 |
-
" mlp_multiplier=2, dropout_rate=0.25, head_units=(512, 256, 128, 64),\n",
|
| 982 |
-
"):\n",
|
| 983 |
-
" \"\"\"Build a ViT with U-Net-style symmetric skip connections between encoder and decoder blocks.\"\"\"\n",
|
| 984 |
-
" num_patches = (input_shape[0] // inner_patch) * (input_shape[1] // inner_patch)\n",
|
| 985 |
-
"\n",
|
| 986 |
-
" inputs = keras.Input(shape=input_shape)\n",
|
| 987 |
-
" x = PatchExtractor(inner_patch, name='vit_patch_extractor')(inputs)\n",
|
| 988 |
-
" x = PatchEncoderWithCLS(num_patches, projection_dim, name='vit_patch_encoder')(x)\n",
|
| 989 |
-
"\n",
|
| 990 |
-
" block_list = []\n",
|
| 991 |
-
" for i in range(transformer_layers):\n",
|
| 992 |
-
" x = transformer_block(\n",
|
| 993 |
-
" x, num_heads=num_heads, projection_dim=projection_dim,\n",
|
| 994 |
-
" mlp_dim=projection_dim * mlp_multiplier, dropout_rate=0.1,\n",
|
| 995 |
-
" name_prefix=f'vit_block_{i+1}',\n",
|
| 996 |
-
" )\n",
|
| 997 |
-
" # Save encoder-side outputs for skip connections\n",
|
| 998 |
-
" if i <= transformer_layers // 2:\n",
|
| 999 |
-
" block_list.append(x)\n",
|
| 1000 |
-
" else:\n",
|
| 1001 |
-
" x = layers.Add(name=f'vit_skip_add_{i+1}')([x, block_list[transformer_layers - i - 1]])\n",
|
| 1002 |
-
"\n",
|
| 1003 |
-
" x = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_1')(x)\n",
|
| 1004 |
-
" x = layers.LayerNormalization(epsilon=1e-6, name='vit_cls_norm')(x)\n",
|
| 1005 |
-
" cls_token = layers.Lambda(lambda t: t[:, 0, :], name='vit_cls_token')(x)\n",
|
| 1006 |
-
"\n",
|
| 1007 |
-
" y = layers.Dense(head_units[0], activation=tf.keras.activations.gelu, name='vit_head_1')(cls_token)\n",
|
| 1008 |
-
" y = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_3')(y)\n",
|
| 1009 |
-
" y = layers.Dense(head_units[1], activation=tf.keras.activations.gelu, name='vit_head_2')(y)\n",
|
| 1010 |
-
" y = layers.Dense(head_units[2], activation=tf.keras.activations.gelu, name='vit_head_3')(y)\n",
|
| 1011 |
-
" y = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_5')(y)\n",
|
| 1012 |
-
" y = layers.Dense(head_units[3], activation=tf.keras.activations.gelu, name='vit_head_4')(y)\n",
|
| 1013 |
-
" y = layers.Dropout(dropout_rate, name='TRAIN_DROPOUT_6')(y)\n",
|
| 1014 |
-
"\n",
|
| 1015 |
-
" outputs = layers.Dense(num_classes, activation='softmax', name='vit_logits')(y)\n",
|
| 1016 |
-
" return keras.Model(inputs, outputs, name='ViT_UNet_SingleHead')"
|
| 1017 |
-
],
|
| 1018 |
-
"id": "MHgOzlppjmfD"
|
| 1019 |
-
},
|
| 1020 |
-
{
|
| 1021 |
-
"cell_type": "markdown",
|
| 1022 |
-
"metadata": {
|
| 1023 |
-
"id": "NKYV1hDsjmfD"
|
| 1024 |
-
},
|
| 1025 |
-
"source": [
|
| 1026 |
-
"# 5.0 \u2014 Training & Evaluation Helpers\n",
|
| 1027 |
-
"\n",
|
| 1028 |
-
"Utility functions for calibration metrics (Brier score, ECE), the shared AdamW/cosine-decay optimiser factory, and the AlexNet legacy cosine LR scheduler."
|
| 1029 |
-
],
|
| 1030 |
-
"id": "NKYV1hDsjmfD"
|
| 1031 |
-
},
|
| 1032 |
-
{
|
| 1033 |
-
"cell_type": "code",
|
| 1034 |
-
"execution_count": null,
|
| 1035 |
-
"metadata": {
|
| 1036 |
-
"id": "nITKN2ZUjmfD",
|
| 1037 |
-
"executionInfo": {
|
| 1038 |
-
"status": "ok",
|
| 1039 |
-
"timestamp": 1782312434913,
|
| 1040 |
-
"user_tz": -330,
|
| 1041 |
-
"elapsed": 5,
|
| 1042 |
-
"user": {
|
| 1043 |
-
"displayName": "Danesh Selwal",
|
| 1044 |
-
"userId": "10565017814556927251"
|
| 1045 |
-
}
|
| 1046 |
-
}
|
| 1047 |
-
},
|
| 1048 |
-
"outputs": [],
|
| 1049 |
-
"source": [
|
| 1050 |
-
"def multiclass_brier_score(y_onehot, y_prob):\n",
|
| 1051 |
-
" \"\"\"Compute the multiclass Brier score (mean squared error over probability vectors).\"\"\"\n",
|
| 1052 |
-
" return float(np.mean(np.sum((y_prob - y_onehot) ** 2, axis=1)))\n",
|
| 1053 |
-
"\n",
|
| 1054 |
-
"\n",
|
| 1055 |
-
"def expected_calibration_error(y_true, y_prob, n_bins=15):\n",
|
| 1056 |
-
" \"\"\"Compute Expected Calibration Error (ECE) using equal-width confidence bins.\"\"\"\n",
|
| 1057 |
-
" confidences = np.max(y_prob, axis=1)\n",
|
| 1058 |
-
" predictions = np.argmax(y_prob, axis=1)\n",
|
| 1059 |
-
" correct = (predictions == y_true).astype(np.float32)\n",
|
| 1060 |
-
"\n",
|
| 1061 |
-
" bin_edges = np.linspace(0.0, 1.0, n_bins + 1)\n",
|
| 1062 |
-
" ece = 0.0\n",
|
| 1063 |
-
" for i in range(n_bins):\n",
|
| 1064 |
-
" lo, hi = bin_edges[i], bin_edges[i + 1]\n",
|
| 1065 |
-
" in_bin = (confidences >= lo) & (confidences <= hi if i == n_bins - 1 else confidences < hi)\n",
|
| 1066 |
-
" prop = np.mean(in_bin)\n",
|
| 1067 |
-
" if prop > 0:\n",
|
| 1068 |
-
" acc_bin = np.mean(correct[in_bin])\n",
|
| 1069 |
-
" conf_bin = np.mean(confidences[in_bin])\n",
|
| 1070 |
-
" ece += np.abs(acc_bin - conf_bin) * prop\n",
|
| 1071 |
-
" return float(ece)\n",
|
| 1072 |
-
"\n",
|
| 1073 |
-
"\n",
|
| 1074 |
-
"def make_optimizer(num_train_samples):\n",
|
| 1075 |
-
" \"\"\"Create an AdamW optimiser with cosine-decay learning rate schedule.\"\"\"\n",
|
| 1076 |
-
" steps_per_epoch = int(np.ceil(num_train_samples / BATCH_SIZE))\n",
|
| 1077 |
-
" decay_steps = max(1, steps_per_epoch * EPOCHS)\n",
|
| 1078 |
-
" lr_schedule = keras.optimizers.schedules.CosineDecay(\n",
|
| 1079 |
-
" initial_learning_rate=LEARNING_RATE,\n",
|
| 1080 |
-
" decay_steps=decay_steps,\n",
|
| 1081 |
-
" alpha=TRAIN_CFG['cosine_alpha'],\n",
|
| 1082 |
-
" )\n",
|
| 1083 |
-
" return keras.optimizers.AdamW(\n",
|
| 1084 |
-
" learning_rate=lr_schedule,\n",
|
| 1085 |
-
" weight_decay=TRAIN_CFG['weight_decay'],\n",
|
| 1086 |
-
" clipnorm=TRAIN_CFG['clipnorm'],\n",
|
| 1087 |
-
" )\n",
|
| 1088 |
-
"\n",
|
| 1089 |
-
"\n",
|
| 1090 |
-
"def _alexnet_legacy_lr(epoch):\n",
|
| 1091 |
-
" \"\"\"Cosine LR schedule between ALEXNET_LR_MIN and ALEXNET_LR_MAX (AlexNet legacy recipe).\"\"\"\n",
|
| 1092 |
-
" if EPOCHS <= 1:\n",
|
| 1093 |
-
" return ALEXNET_LR_START\n",
|
| 1094 |
-
" phase = np.pi * epoch / (EPOCHS - 1)\n",
|
| 1095 |
-
" cosine_decay = 0.5 * (1.0 + np.cos(phase))\n",
|
| 1096 |
-
" return float((ALEXNET_LR_MAX - ALEXNET_LR_MIN) * cosine_decay + ALEXNET_LR_MIN)"
|
| 1097 |
-
],
|
| 1098 |
-
"id": "nITKN2ZUjmfD"
|
| 1099 |
-
},
|
| 1100 |
-
{
|
| 1101 |
-
"cell_type": "markdown",
|
| 1102 |
-
"metadata": {
|
| 1103 |
-
"id": "hPJBPlZTjmfE"
|
| 1104 |
-
},
|
| 1105 |
-
"source": [
|
| 1106 |
-
"# 6.0 \u2014 Ensemble Training (M=5)\n",
|
| 1107 |
-
"\n",
|
| 1108 |
-
"Trains M=5 independently seeded copies of each selected architecture. Models are first saved locally to avoid Drive I/O crashes mid-run, then copied to Google Drive. Evaluation metrics are collected per ensemble member."
|
| 1109 |
-
],
|
| 1110 |
-
"id": "hPJBPlZTjmfE"
|
| 1111 |
-
},
|
| 1112 |
-
{
|
| 1113 |
-
"cell_type": "markdown",
|
| 1114 |
-
"metadata": {
|
| 1115 |
-
"id": "ntscv9v2jmfE"
|
| 1116 |
-
},
|
| 1117 |
-
"source": [
|
| 1118 |
-
"## 6.1 \u2014 Builder Wrappers & Directory Setup"
|
| 1119 |
-
],
|
| 1120 |
-
"id": "ntscv9v2jmfE"
|
| 1121 |
-
},
|
| 1122 |
-
{
|
| 1123 |
-
"cell_type": "code",
|
| 1124 |
-
"execution_count": null,
|
| 1125 |
-
"metadata": {
|
| 1126 |
-
"id": "vt8pou-hjmfE",
|
| 1127 |
-
"executionInfo": {
|
| 1128 |
-
"status": "ok",
|
| 1129 |
-
"timestamp": 1782312434956,
|
| 1130 |
-
"user_tz": -330,
|
| 1131 |
-
"elapsed": 40,
|
| 1132 |
-
"user": {
|
| 1133 |
-
"displayName": "Danesh Selwal",
|
| 1134 |
-
"userId": "10565017814556927251"
|
| 1135 |
-
}
|
| 1136 |
-
}
|
| 1137 |
-
},
|
| 1138 |
-
"outputs": [],
|
| 1139 |
-
"source": [
|
| 1140 |
-
"import shutil\n",
|
| 1141 |
-
"\n",
|
| 1142 |
-
"def build_gfnet_with_cfg(cfg):\n",
|
| 1143 |
-
" \"\"\"Instantiate GFNet from a config dict.\"\"\"\n",
|
| 1144 |
-
" return build_gfnet(\n",
|
| 1145 |
-
" input_shape, num_classes, inner_patch=INNER_PATCH,\n",
|
| 1146 |
-
" hidden_dim=cfg['hidden_dim'], num_blocks=cfg['num_blocks'],\n",
|
| 1147 |
-
" mlp_ratio=cfg['mlp_ratio'], dropout_rate=DROPOUT_RATE,\n",
|
| 1148 |
-
" )\n",
|
| 1149 |
-
"\n",
|
| 1150 |
-
"\n",
|
| 1151 |
-
"def build_vit_with_cfg(cfg):\n",
|
| 1152 |
-
" \"\"\"Instantiate ViT-UNet from a config dict.\"\"\"\n",
|
| 1153 |
-
" return build_vit_unet_singlehead(\n",
|
| 1154 |
-
" input_shape, num_classes, inner_patch=INNER_PATCH,\n",
|
| 1155 |
-
" projection_dim=cfg['projection_dim'], num_heads=cfg['num_heads'],\n",
|
| 1156 |
-
" transformer_layers=cfg['transformer_layers'], mlp_multiplier=cfg['mlp_multiplier'],\n",
|
| 1157 |
-
" dropout_rate=DROPOUT_RATE, head_units=tuple(cfg['head_units']),\n",
|
| 1158 |
-
" )\n",
|
| 1159 |
-
"\n",
|
| 1160 |
-
"\n",
|
| 1161 |
-
"model_builders = {\n",
|
| 1162 |
-
" 'AlexNet_CNN': lambda: build_alexnet(input_shape, num_classes,\n",
|
| 1163 |
-
" dropout_rate=DROPOUT_RATE, cfg=ALEXNET_CFG),\n",
|
| 1164 |
-
" 'GFNet': lambda: build_gfnet_with_cfg(GFNET_CFG),\n",
|
| 1165 |
-
" 'ViT_UNet': lambda: build_vit_with_cfg(VIT_CFG),\n",
|
| 1166 |
-
"}\n",
|
| 1167 |
-
"\n",
|
| 1168 |
-
"# Local save directory (prevents Drive crash during training)\n",
|
| 1169 |
-
"local_dir = Path('/content/local_models')\n",
|
| 1170 |
-
"local_dir.mkdir(parents=True, exist_ok=True)\n",
|
| 1171 |
-
"\n",
|
| 1172 |
-
"# Google Drive destination for final copies\n",
|
| 1173 |
-
"ensemble_dir = MODEL_DIR / 'ensembles'\n",
|
| 1174 |
-
"ensemble_dir.mkdir(parents=True, exist_ok=True)"
|
| 1175 |
-
],
|
| 1176 |
-
"id": "vt8pou-hjmfE"
|
| 1177 |
-
},
|
| 1178 |
-
{
|
| 1179 |
-
"cell_type": "markdown",
|
| 1180 |
-
"metadata": {
|
| 1181 |
-
"id": "YTiTctJVjmfF"
|
| 1182 |
-
},
|
| 1183 |
-
"source": [
|
| 1184 |
-
"## 6.2 \u2014 Ensemble Training Loop\n",
|
| 1185 |
-
"\n",
|
| 1186 |
-
"Iterates over selected architectures, training M=5 members each with a distinct seed. Uses fallback configs when a ResourceExhaustedError is raised. All weights are saved locally first, then synced to Drive."
|
| 1187 |
-
],
|
| 1188 |
-
"id": "YTiTctJVjmfF"
|
| 1189 |
-
},
|
| 1190 |
-
{
|
| 1191 |
-
"cell_type": "code",
|
| 1192 |
-
"execution_count": null,
|
| 1193 |
-
"metadata": {
|
| 1194 |
-
"colab": {
|
| 1195 |
-
"base_uri": "https://localhost:8080/",
|
| 1196 |
-
"height": 0
|
| 1197 |
-
},
|
| 1198 |
-
"id": "gdbH48OBjmfH",
|
| 1199 |
-
"outputId": "1c8e662c-9560-47c4-db23-62cca0edd2f9",
|
| 1200 |
-
"executionInfo": {
|
| 1201 |
-
"status": "ok",
|
| 1202 |
-
"timestamp": 1782320195390,
|
| 1203 |
-
"user_tz": -330,
|
| 1204 |
-
"elapsed": 7760436,
|
| 1205 |
-
"user": {
|
| 1206 |
-
"displayName": "Danesh Selwal",
|
| 1207 |
-
"userId": "10565017814556927251"
|
| 1208 |
-
}
|
| 1209 |
-
}
|
| 1210 |
-
},
|
| 1211 |
-
"outputs": [],
|
| 1212 |
-
"source": [
|
| 1213 |
-
"M = 5\n",
|
| 1214 |
-
"results_rows = []\n",
|
| 1215 |
-
"model_artifacts = {}\n",
|
| 1216 |
-
"\n",
|
| 1217 |
-
"for model_name, builder in model_builders.items():\n",
|
| 1218 |
-
" if model_name != \"ViT_UNet\": # \ud83d\udd25 change this to the one you want\n",
|
| 1219 |
-
" continue\n",
|
| 1220 |
-
" print(f\"\\n{'=' * 25} Training {model_name} Ensembles (M={M}) {'=' * 25}\")\n",
|
| 1221 |
-
"\n",
|
| 1222 |
-
" for i in range(1, M + 1):\n",
|
| 1223 |
-
" print(f\"\\n--- {model_name} Ensemble Model {i}/{M} ---\")\n",
|
| 1224 |
-
"\n",
|
| 1225 |
-
" # Per-member seed\n",
|
| 1226 |
-
" seed_val = 42 + i\n",
|
| 1227 |
-
" tf.random.set_seed(seed_val)\n",
|
| 1228 |
-
" np.random.seed(seed_val)\n",
|
| 1229 |
-
" random.seed(seed_val)\n",
|
| 1230 |
-
"\n",
|
| 1231 |
-
" tf.keras.backend.clear_session()\n",
|
| 1232 |
-
"\n",
|
| 1233 |
-
" # Build model \u2014 fall back to smaller config on OOM\n",
|
| 1234 |
-
" try:\n",
|
| 1235 |
-
" model = builder()\n",
|
| 1236 |
-
" capacity_tag = CAPACITY_PRESET\n",
|
| 1237 |
-
" except tf.errors.ResourceExhaustedError:\n",
|
| 1238 |
-
" if model_name == 'GFNet':\n",
|
| 1239 |
-
" model = build_gfnet_with_cfg(GFNET_FALLBACK_CFG)\n",
|
| 1240 |
-
" capacity_tag = 'fallback'\n",
|
| 1241 |
-
" elif model_name == 'ViT_UNet':\n",
|
| 1242 |
-
" model = build_vit_with_cfg(VIT_FALLBACK_CFG)\n",
|
| 1243 |
-
" capacity_tag = 'fallback'\n",
|
| 1244 |
-
" else:\n",
|
| 1245 |
-
" raise\n",
|
| 1246 |
-
"\n",
|
| 1247 |
-
" # Local checkpoint paths\n",
|
| 1248 |
-
" best_path = local_dir / f'{model_name}_ens_{i}_best.weights.h5'\n",
|
| 1249 |
-
" final_path = local_dir / f'{model_name}_ens_{i}_final.keras'\n",
|
| 1250 |
-
"\n",
|
| 1251 |
-
" # Compile and set up training data / callbacks per architecture\n",
|
| 1252 |
-
" if model_name == 'AlexNet_CNN':\n",
|
| 1253 |
-
" model.compile(\n",
|
| 1254 |
-
" optimizer=keras.optimizers.Adagrad(learning_rate=ALEXNET_LR_START),\n",
|
| 1255 |
-
" loss='sparse_categorical_crossentropy',\n",
|
| 1256 |
-
" metrics=['accuracy'],\n",
|
| 1257 |
-
" )\n",
|
| 1258 |
-
" callbacks = [\n",
|
| 1259 |
-
" keras.callbacks.ModelCheckpoint(\n",
|
| 1260 |
-
" filepath=str(best_path), monitor='val_accuracy', mode='max',\n",
|
| 1261 |
-
" save_best_only=True, save_weights_only=True, verbose=1,\n",
|
| 1262 |
-
" ),\n",
|
| 1263 |
-
" keras.callbacks.LearningRateScheduler(_alexnet_legacy_lr, verbose=0),\n",
|
| 1264 |
-
" ]\n",
|
| 1265 |
-
" x_tr, y_tr = x_train_alex, y_train_alex\n",
|
| 1266 |
-
" x_va, y_va = x_test_alex, y_test_alex\n",
|
| 1267 |
-
" x_te, y_te, y_te_cat = x_test_alex, y_test_alex, y_test_alex_cat\n",
|
| 1268 |
-
" x_eval_for_metrics, y_eval_for_metrics, y_eval_for_metrics_cat = (\n",
|
| 1269 |
-
" x_test_alex, y_test_alex, y_test_alex_cat\n",
|
| 1270 |
-
" )\n",
|
| 1271 |
-
" fit_shuffle = False\n",
|
| 1272 |
-
"\n",
|
| 1273 |
-
" else:\n",
|
| 1274 |
-
" model.compile(\n",
|
| 1275 |
-
" optimizer=make_optimizer(len(x_train)),\n",
|
| 1276 |
-
" loss=keras.losses.CategoricalCrossentropy(label_smoothing=TRAIN_CFG['label_smoothing']),\n",
|
| 1277 |
-
" metrics=['accuracy'],\n",
|
| 1278 |
-
" )\n",
|
| 1279 |
-
" callbacks = [\n",
|
| 1280 |
-
" keras.callbacks.ModelCheckpoint(\n",
|
| 1281 |
-
" filepath=str(best_path), monitor='val_loss', mode='min',\n",
|
| 1282 |
-
" save_best_only=True, save_weights_only=True, verbose=1,\n",
|
| 1283 |
-
" ),\n",
|
| 1284 |
-
" ]\n",
|
| 1285 |
-
" x_tr, y_tr = x_train, y_train_cat\n",
|
| 1286 |
-
" x_va, y_va = x_val, y_val_cat\n",
|
| 1287 |
-
" x_te, y_te, y_te_cat = x_test, y_test, y_test_cat\n",
|
| 1288 |
-
" x_eval_for_metrics, y_eval_for_metrics, y_eval_for_metrics_cat = (\n",
|
| 1289 |
-
" x_val, y_val, y_val_cat\n",
|
| 1290 |
-
" )\n",
|
| 1291 |
-
" fit_shuffle = True\n",
|
| 1292 |
-
"\n",
|
| 1293 |
-
" # Train\n",
|
| 1294 |
-
" train_start = time.perf_counter()\n",
|
| 1295 |
-
" history_obj = model.fit(\n",
|
| 1296 |
-
" x_tr, y_tr,\n",
|
| 1297 |
-
" validation_data=(x_va, y_va),\n",
|
| 1298 |
-
" epochs=EPOCHS, batch_size=BATCH_SIZE,\n",
|
| 1299 |
-
" callbacks=callbacks, verbose=1, shuffle=fit_shuffle,\n",
|
| 1300 |
-
" )\n",
|
| 1301 |
-
" train_time_sec = float(time.perf_counter() - train_start)\n",
|
| 1302 |
-
"\n",
|
| 1303 |
-
" # Save full model locally, then copy to Drive\n",
|
| 1304 |
-
" model.save(final_path)\n",
|
| 1305 |
-
" shutil.copy(best_path, ensemble_dir / best_path.name)\n",
|
| 1306 |
-
" shutil.copy(final_path, ensemble_dir / final_path.name)\n",
|
| 1307 |
-
"\n",
|
| 1308 |
-
" # Evaluate\n",
|
| 1309 |
-
" y_eval_prob = model.predict(x_eval_for_metrics, batch_size=BATCH_SIZE, verbose=0)\n",
|
| 1310 |
-
" y_test_prob = model.predict(x_te, batch_size=BATCH_SIZE, verbose=0)\n",
|
| 1311 |
-
" y_eval_pred = np.argmax(y_eval_prob, axis=1)\n",
|
| 1312 |
-
" y_test_pred = np.argmax(y_test_prob, axis=1)\n",
|
| 1313 |
-
"\n",
|
| 1314 |
-
" report = classification_report(y_te, y_test_pred, output_dict=True, zero_division=0)\n",
|
| 1315 |
-
" cm = confusion_matrix(y_te, y_test_pred)\n",
|
| 1316 |
-
"\n",
|
| 1317 |
-
" row = {\n",
|
| 1318 |
-
" 'model': f'{model_name}_ens_{i}',\n",
|
| 1319 |
-
" 'capacity_tag': capacity_tag,\n",
|
| 1320 |
-
" 'test_accuracy': float(accuracy_score(y_te, y_test_pred)),\n",
|
| 1321 |
-
" 'kappa': float(cohen_kappa_score(y_te, y_test_pred)),\n",
|
| 1322 |
-
" 'macro_f1': float(f1_score(y_te, y_test_pred, average='macro')),\n",
|
| 1323 |
-
" 'weighted_f1': float(f1_score(y_te, y_test_pred, average='weighted')),\n",
|
| 1324 |
-
" 'val_nll': float(log_loss(y_eval_for_metrics, y_eval_prob, labels=np.arange(num_classes))),\n",
|
| 1325 |
-
" 'test_nll': float(log_loss(y_te, y_test_prob, labels=np.arange(num_classes))),\n",
|
| 1326 |
-
" 'val_brier': multiclass_brier_score(y_eval_for_metrics_cat, y_eval_prob),\n",
|
| 1327 |
-
" 'test_brier': multiclass_brier_score(y_te_cat, y_test_prob),\n",
|
| 1328 |
-
" 'test_ece_15bin': expected_calibration_error(y_te, y_test_prob, n_bins=15),\n",
|
| 1329 |
-
" 'epochs_configured': int(EPOCHS),\n",
|
| 1330 |
-
" 'epochs_ran': int(len(history_obj.history.get('loss', []))),\n",
|
| 1331 |
-
" 'train_time_sec': train_time_sec,\n",
|
| 1332 |
-
" 'best_model_path': str(ensemble_dir / best_path.name),\n",
|
| 1333 |
-
" 'final_model_path': str(ensemble_dir / final_path.name),\n",
|
| 1334 |
-
" }\n",
|
| 1335 |
-
" results_rows.append(row)\n",
|
| 1336 |
-
"\n",
|
| 1337 |
-
" # Store last member's artifacts for plotting\n",
|
| 1338 |
-
" if i == M:\n",
|
| 1339 |
-
" model_artifacts[model_name] = {\n",
|
| 1340 |
-
" 'report': report,\n",
|
| 1341 |
-
" 'confusion_matrix': cm,\n",
|
| 1342 |
-
" 'history': history_obj.history,\n",
|
| 1343 |
-
" }\n",
|
| 1344 |
-
"\n",
|
| 1345 |
-
"summary_df = (pd.DataFrame(results_rows)\n",
|
| 1346 |
-
" .sort_values('test_accuracy', ascending=False)\n",
|
| 1347 |
-
" .reset_index(drop=True))\n",
|
| 1348 |
-
"summary_df"
|
| 1349 |
-
],
|
| 1350 |
-
"id": "gdbH48OBjmfH"
|
| 1351 |
-
},
|
| 1352 |
-
{
|
| 1353 |
-
"cell_type": "markdown",
|
| 1354 |
-
"metadata": {
|
| 1355 |
-
"id": "KG2bOKWtjmfI"
|
| 1356 |
-
},
|
| 1357 |
-
"source": [
|
| 1358 |
-
"# 7.0 \u2014 Results & Analysis\n",
|
| 1359 |
-
"\n",
|
| 1360 |
-
"Saves the classification summary CSV, plots training curves, cross-model metric comparisons, calibration proxy charts, and per-model confusion matrices."
|
| 1361 |
-
],
|
| 1362 |
-
"id": "KG2bOKWtjmfI"
|
| 1363 |
-
},
|
| 1364 |
-
{
|
| 1365 |
-
"cell_type": "markdown",
|
| 1366 |
-
"metadata": {
|
| 1367 |
-
"id": "-0Pc8L9tjmfI"
|
| 1368 |
-
},
|
| 1369 |
-
"source": [
|
| 1370 |
-
"## 7.1 \u2014 Save Summary CSV & Per-Model Reports"
|
| 1371 |
-
],
|
| 1372 |
-
"id": "-0Pc8L9tjmfI"
|
| 1373 |
-
},
|
| 1374 |
-
{
|
| 1375 |
-
"cell_type": "code",
|
| 1376 |
-
"execution_count": null,
|
| 1377 |
-
"metadata": {
|
| 1378 |
-
"id": "r35TTpYcjmfI",
|
| 1379 |
-
"colab": {
|
| 1380 |
-
"base_uri": "https://localhost:8080/",
|
| 1381 |
-
"height": 0
|
| 1382 |
-
},
|
| 1383 |
-
"executionInfo": {
|
| 1384 |
-
"status": "ok",
|
| 1385 |
-
"timestamp": 1782320195423,
|
| 1386 |
-
"user_tz": -330,
|
| 1387 |
-
"elapsed": 39,
|
| 1388 |
-
"user": {
|
| 1389 |
-
"displayName": "Danesh Selwal",
|
| 1390 |
-
"userId": "10565017814556927251"
|
| 1391 |
-
}
|
| 1392 |
-
},
|
| 1393 |
-
"outputId": "ed478e45-b977-41f3-c062-d21333c93139"
|
| 1394 |
-
},
|
| 1395 |
-
"outputs": [],
|
| 1396 |
-
"source": [
|
| 1397 |
-
"summary_path = MODEL_DIR / 'classification_summary.csv'\n",
|
| 1398 |
-
"summary_df.to_csv(summary_path, index=False)\n",
|
| 1399 |
-
"print('Saved summary:', summary_path)\n",
|
| 1400 |
-
"\n",
|
| 1401 |
-
"for model_name, artifact in model_artifacts.items():\n",
|
| 1402 |
-
" report_path = MODEL_DIR / f'{model_name}_classification_report.json'\n",
|
| 1403 |
-
" with open(report_path, 'w', encoding='utf-8') as f:\n",
|
| 1404 |
-
" json.dump(artifact['report'], f, indent=2)\n",
|
| 1405 |
-
"\n",
|
| 1406 |
-
"print('Saved per-model classification reports in:', MODEL_DIR)\n",
|
| 1407 |
-
"summary_df"
|
| 1408 |
-
],
|
| 1409 |
-
"id": "r35TTpYcjmfI"
|
| 1410 |
-
},
|
| 1411 |
-
{
|
| 1412 |
-
"cell_type": "markdown",
|
| 1413 |
-
"metadata": {
|
| 1414 |
-
"id": "-GFcXWgLjmfI"
|
| 1415 |
-
},
|
| 1416 |
-
"source": [
|
| 1417 |
-
"## 7.2 \u2014 Training Curves (Accuracy & Loss)\n",
|
| 1418 |
-
"\n",
|
| 1419 |
-
"Plots dual-axis accuracy/loss curves for each trained model and saves them to `PLOT_DIR`."
|
| 1420 |
-
],
|
| 1421 |
-
"id": "-GFcXWgLjmfI"
|
| 1422 |
-
},
|
| 1423 |
-
{
|
| 1424 |
-
"cell_type": "code",
|
| 1425 |
-
"execution_count": null,
|
| 1426 |
-
"metadata": {
|
| 1427 |
-
"id": "IfLhfCT5jmfI",
|
| 1428 |
-
"colab": {
|
| 1429 |
-
"base_uri": "https://localhost:8080/",
|
| 1430 |
-
"height": 0
|
| 1431 |
-
},
|
| 1432 |
-
"executionInfo": {
|
| 1433 |
-
"status": "ok",
|
| 1434 |
-
"timestamp": 1782320196178,
|
| 1435 |
-
"user_tz": -330,
|
| 1436 |
-
"elapsed": 753,
|
| 1437 |
-
"user": {
|
| 1438 |
-
"displayName": "Danesh Selwal",
|
| 1439 |
-
"userId": "10565017814556927251"
|
| 1440 |
-
}
|
| 1441 |
-
},
|
| 1442 |
-
"outputId": "48859585-392c-4b10-d968-90688b0659b7"
|
| 1443 |
-
},
|
| 1444 |
-
"outputs": [],
|
| 1445 |
-
"source": [
|
| 1446 |
-
"for model_name, artifact in model_artifacts.items():\n",
|
| 1447 |
-
" hist = artifact['history']\n",
|
| 1448 |
-
" epochs = np.arange(1, len(hist['loss']) + 1)\n",
|
| 1449 |
-
"\n",
|
| 1450 |
-
" fig, ax1 = plt.subplots(figsize=(12, 5))\n",
|
| 1451 |
-
" ax1.plot(epochs, hist['accuracy'], color='#1f77b4', linewidth=2, label='Train Accuracy')\n",
|
| 1452 |
-
" ax1.plot(epochs, hist['val_accuracy'], color='#2ca02c', linewidth=2, linestyle='--', label='Val Accuracy')\n",
|
| 1453 |
-
" ax1.set_xlabel('Epoch')\n",
|
| 1454 |
-
" ax1.set_ylabel('Accuracy')\n",
|
| 1455 |
-
" ax1.set_ylim(0, 1.0)\n",
|
| 1456 |
-
" ax1.grid(True, alpha=0.25)\n",
|
| 1457 |
-
"\n",
|
| 1458 |
-
" ax2 = ax1.twinx()\n",
|
| 1459 |
-
" ax2.plot(epochs, hist['loss'], color='#111111', linewidth=1.8, label='Train Loss')\n",
|
| 1460 |
-
" ax2.plot(epochs, hist['val_loss'], color='#d62728', linewidth=1.8, linestyle='--', label='Val Loss')\n",
|
| 1461 |
-
" ax2.set_ylabel('Loss')\n",
|
| 1462 |
-
"\n",
|
| 1463 |
-
" lines = ax1.get_lines() + ax2.get_lines()\n",
|
| 1464 |
-
" labels = [ln.get_label() for ln in lines]\n",
|
| 1465 |
-
" ax1.legend(lines, labels, loc='upper right', frameon=True)\n",
|
| 1466 |
-
" plt.title(f'{model_name}: Accuracy/Loss vs Epoch')\n",
|
| 1467 |
-
" plt.tight_layout()\n",
|
| 1468 |
-
"\n",
|
| 1469 |
-
" curve_path = PLOT_DIR / f'{model_name}_training_curves.png'\n",
|
| 1470 |
-
" plt.savefig(curve_path, dpi=200, bbox_inches='tight')\n",
|
| 1471 |
-
" plt.show()"
|
| 1472 |
-
],
|
| 1473 |
-
"id": "IfLhfCT5jmfI"
|
| 1474 |
-
},
|
| 1475 |
-
{
|
| 1476 |
-
"cell_type": "markdown",
|
| 1477 |
-
"metadata": {
|
| 1478 |
-
"id": "yKCm6__LjmfJ"
|
| 1479 |
-
},
|
| 1480 |
-
"source": [
|
| 1481 |
-
"## 7.3 \u2014 Cross-Model Metric Comparison Charts"
|
| 1482 |
-
],
|
| 1483 |
-
"id": "yKCm6__LjmfJ"
|
| 1484 |
-
},
|
| 1485 |
-
{
|
| 1486 |
-
"cell_type": "code",
|
| 1487 |
-
"execution_count": null,
|
| 1488 |
-
"metadata": {
|
| 1489 |
-
"id": "v-trp7mFjmfJ",
|
| 1490 |
-
"colab": {
|
| 1491 |
-
"base_uri": "https://localhost:8080/",
|
| 1492 |
-
"height": 0
|
| 1493 |
-
},
|
| 1494 |
-
"executionInfo": {
|
| 1495 |
-
"status": "ok",
|
| 1496 |
-
"timestamp": 1782320197433,
|
| 1497 |
-
"user_tz": -330,
|
| 1498 |
-
"elapsed": 1246,
|
| 1499 |
-
"user": {
|
| 1500 |
-
"displayName": "Danesh Selwal",
|
| 1501 |
-
"userId": "10565017814556927251"
|
| 1502 |
-
}
|
| 1503 |
-
},
|
| 1504 |
-
"outputId": "89265a0d-3b7c-405a-a94e-2c698a78b988"
|
| 1505 |
-
},
|
| 1506 |
-
"outputs": [],
|
| 1507 |
-
"source": [
|
| 1508 |
-
"plot_df = summary_df[[\n",
|
| 1509 |
-
" 'model', 'test_accuracy', 'macro_f1', 'kappa',\n",
|
| 1510 |
-
" 'train_time_sec', 'test_nll', 'test_brier', 'test_ece_15bin',\n",
|
| 1511 |
-
"]].copy()\n",
|
| 1512 |
-
"\n",
|
| 1513 |
-
"fig, axes = plt.subplots(2, 2, figsize=(15, 9))\n",
|
| 1514 |
-
"axes = axes.flatten()\n",
|
| 1515 |
-
"palette = ['#4c72b0', '#55a868', '#c44e52']\n",
|
| 1516 |
-
"\n",
|
| 1517 |
-
"metric_specs = [\n",
|
| 1518 |
-
" ('test_accuracy', 'Test Accuracy', (0.0, 1.0), '{:.3f}'),\n",
|
| 1519 |
-
" ('macro_f1', 'Macro F1', (0.0, 1.0), '{:.3f}'),\n",
|
| 1520 |
-
" ('kappa', 'Cohen Kappa', (0.0, 1.0), '{:.3f}'),\n",
|
| 1521 |
-
" ('train_time_sec', 'Train Time (sec)', None, '{:.1f}'),\n",
|
| 1522 |
-
"]\n",
|
| 1523 |
-
"\n",
|
| 1524 |
-
"for ax, (col, title, ylim, fmt) in zip(axes, metric_specs):\n",
|
| 1525 |
-
" local = plot_df[['model', col]].copy()\n",
|
| 1526 |
-
" bars = ax.bar(local['model'], local[col], color=palette[:len(local)])\n",
|
| 1527 |
-
" ax.set_title(title)\n",
|
| 1528 |
-
" ax.set_xlabel('Model')\n",
|
| 1529 |
-
" ax.set_ylabel(title)\n",
|
| 1530 |
-
" if ylim is not None:\n",
|
| 1531 |
-
" ax.set_ylim(*ylim)\n",
|
| 1532 |
-
" ax.grid(axis='y', alpha=0.25)\n",
|
| 1533 |
-
" for patch in bars:\n",
|
| 1534 |
-
" height = patch.get_height()\n",
|
| 1535 |
-
" x = patch.get_x() + patch.get_width() / 2\n",
|
| 1536 |
-
" offset = (ylim[1] - ylim[0]) * 0.02 if ylim else max(local[col].max() * 0.02, 0.05)\n",
|
| 1537 |
-
" ax.text(x, height + offset, fmt.format(height), ha='center', va='bottom', fontsize=9)\n",
|
| 1538 |
-
"\n",
|
| 1539 |
-
"plt.suptitle('Single-Head Model Comparison', y=1.02, fontsize=14)\n",
|
| 1540 |
-
"plt.tight_layout()\n",
|
| 1541 |
-
"comparison_path = PLOT_DIR / 'model_comparison_metrics.png'\n",
|
| 1542 |
-
"plt.savefig(comparison_path, dpi=200, bbox_inches='tight')\n",
|
| 1543 |
-
"plt.show()"
|
| 1544 |
-
],
|
| 1545 |
-
"id": "v-trp7mFjmfJ"
|
| 1546 |
-
},
|
| 1547 |
-
{
|
| 1548 |
-
"cell_type": "markdown",
|
| 1549 |
-
"metadata": {
|
| 1550 |
-
"id": "NUGaljEajmfJ"
|
| 1551 |
-
},
|
| 1552 |
-
"source": [
|
| 1553 |
-
"## 7.4 \u2014 Calibration Proxy Metrics Chart"
|
| 1554 |
-
],
|
| 1555 |
-
"id": "NUGaljEajmfJ"
|
| 1556 |
-
},
|
| 1557 |
-
{
|
| 1558 |
-
"cell_type": "code",
|
| 1559 |
-
"execution_count": null,
|
| 1560 |
-
"metadata": {
|
| 1561 |
-
"id": "c5vVc1AdjmfJ",
|
| 1562 |
-
"colab": {
|
| 1563 |
-
"base_uri": "https://localhost:8080/",
|
| 1564 |
-
"height": 0
|
| 1565 |
-
},
|
| 1566 |
-
"executionInfo": {
|
| 1567 |
-
"status": "ok",
|
| 1568 |
-
"timestamp": 1782320197892,
|
| 1569 |
-
"user_tz": -330,
|
| 1570 |
-
"elapsed": 458,
|
| 1571 |
-
"user": {
|
| 1572 |
-
"displayName": "Danesh Selwal",
|
| 1573 |
-
"userId": "10565017814556927251"
|
| 1574 |
-
}
|
| 1575 |
-
},
|
| 1576 |
-
"outputId": "2cf65229-f99c-45b3-c949-b6fa1424f477"
|
| 1577 |
-
},
|
| 1578 |
-
"outputs": [],
|
| 1579 |
-
"source": [
|
| 1580 |
-
"fig, ax = plt.subplots(figsize=(12, 5))\n",
|
| 1581 |
-
"calib_cols = ['test_nll', 'test_brier', 'test_ece_15bin']\n",
|
| 1582 |
-
"calib_df = plot_df[['model'] + calib_cols].set_index('model')\n",
|
| 1583 |
-
"calib_df.plot(kind='bar', ax=ax, rot=0)\n",
|
| 1584 |
-
"ax.set_title('Uncertainty Proxy Metrics (Lower is Better)')\n",
|
| 1585 |
-
"ax.set_ylabel('Metric Value')\n",
|
| 1586 |
-
"ax.grid(axis='y', alpha=0.25)\n",
|
| 1587 |
-
"plt.tight_layout()\n",
|
| 1588 |
-
"calib_path = PLOT_DIR / 'uncertainty_proxy_metrics.png'\n",
|
| 1589 |
-
"plt.savefig(calib_path, dpi=200, bbox_inches='tight')\n",
|
| 1590 |
-
"plt.show()\n",
|
| 1591 |
-
"print('Saved training comparison plots to:', PLOT_DIR)"
|
| 1592 |
-
],
|
| 1593 |
-
"id": "c5vVc1AdjmfJ"
|
| 1594 |
-
},
|
| 1595 |
-
{
|
| 1596 |
-
"cell_type": "markdown",
|
| 1597 |
-
"metadata": {
|
| 1598 |
-
"id": "Do2l3cYbjmfJ"
|
| 1599 |
-
},
|
| 1600 |
-
"source": [
|
| 1601 |
-
"## 7.5 \u2014 Confusion Matrices"
|
| 1602 |
-
],
|
| 1603 |
-
"id": "Do2l3cYbjmfJ"
|
| 1604 |
-
},
|
| 1605 |
-
{
|
| 1606 |
-
"cell_type": "code",
|
| 1607 |
-
"execution_count": null,
|
| 1608 |
-
"metadata": {
|
| 1609 |
-
"id": "pIJ1f7ZMjmfK",
|
| 1610 |
-
"colab": {
|
| 1611 |
-
"base_uri": "https://localhost:8080/",
|
| 1612 |
-
"height": 0
|
| 1613 |
-
},
|
| 1614 |
-
"executionInfo": {
|
| 1615 |
-
"status": "ok",
|
| 1616 |
-
"timestamp": 1782320198402,
|
| 1617 |
-
"user_tz": -330,
|
| 1618 |
-
"elapsed": 505,
|
| 1619 |
-
"user": {
|
| 1620 |
-
"displayName": "Danesh Selwal",
|
| 1621 |
-
"userId": "10565017814556927251"
|
| 1622 |
-
}
|
| 1623 |
-
},
|
| 1624 |
-
"outputId": "68c7e96e-d1b4-4dcd-c23c-ea8b4294b8a0"
|
| 1625 |
-
},
|
| 1626 |
-
"outputs": [],
|
| 1627 |
-
"source": [
|
| 1628 |
-
"class_ticks = [str(i + 1) for i in range(num_classes)]\n",
|
| 1629 |
-
"fig, axes = plt.subplots(1, len(model_artifacts), figsize=(7 * len(model_artifacts), 5.5))\n",
|
| 1630 |
-
"\n",
|
| 1631 |
-
"if len(model_artifacts) == 1:\n",
|
| 1632 |
-
" axes = [axes]\n",
|
| 1633 |
-
"\n",
|
| 1634 |
-
"for ax, (model_name, artifact) in zip(axes, model_artifacts.items()):\n",
|
| 1635 |
-
" sns.heatmap(\n",
|
| 1636 |
-
" artifact['confusion_matrix'], annot=True, fmt='d', cmap='Blues',\n",
|
| 1637 |
-
" xticklabels=class_ticks, yticklabels=class_ticks, cbar=False, ax=ax,\n",
|
| 1638 |
-
" )\n",
|
| 1639 |
-
" ax.set_title(f'{model_name} Confusion Matrix')\n",
|
| 1640 |
-
" ax.set_xlabel('Predicted')\n",
|
| 1641 |
-
" ax.set_ylabel('True')\n",
|
| 1642 |
-
"\n",
|
| 1643 |
-
"plt.tight_layout()\n",
|
| 1644 |
-
"cm_path = PLOT_DIR / 'confusion_matrices_side_by_side.png'\n",
|
| 1645 |
-
"plt.savefig(cm_path, dpi=220, bbox_inches='tight')\n",
|
| 1646 |
-
"plt.show()\n",
|
| 1647 |
-
"print('Saved confusion matrix figure:', cm_path)"
|
| 1648 |
-
],
|
| 1649 |
-
"id": "pIJ1f7ZMjmfK"
|
| 1650 |
-
},
|
| 1651 |
-
{
|
| 1652 |
-
"cell_type": "markdown",
|
| 1653 |
-
"metadata": {
|
| 1654 |
-
"id": "g1HAWWVBjmfK"
|
| 1655 |
-
},
|
| 1656 |
-
"source": [
|
| 1657 |
-
"# 8.0 \u2014 Scene Visualisation & Classification Maps\n",
|
| 1658 |
-
"\n",
|
| 1659 |
-
"Loads saved models, runs full-scene patch-by-patch inference, and exports classification maps as individual PNGs, a combined overview PNG, and an Excel workbook."
|
| 1660 |
-
],
|
| 1661 |
-
"id": "g1HAWWVBjmfK"
|
| 1662 |
-
},
|
| 1663 |
-
{
|
| 1664 |
-
"cell_type": "markdown",
|
| 1665 |
-
"metadata": {
|
| 1666 |
-
"id": "kbjnbZWRjmfK"
|
| 1667 |
-
},
|
| 1668 |
-
"source": [
|
| 1669 |
-
"## 8.1 \u2014 Visualisation Helper Functions"
|
| 1670 |
-
],
|
| 1671 |
-
"id": "kbjnbZWRjmfK"
|
| 1672 |
-
},
|
| 1673 |
-
{
|
| 1674 |
-
"cell_type": "code",
|
| 1675 |
-
"execution_count": null,
|
| 1676 |
-
"metadata": {
|
| 1677 |
-
"id": "3xH3h143jmfK",
|
| 1678 |
-
"executionInfo": {
|
| 1679 |
-
"status": "ok",
|
| 1680 |
-
"timestamp": 1782320198407,
|
| 1681 |
-
"user_tz": -330,
|
| 1682 |
-
"elapsed": 9,
|
| 1683 |
-
"user": {
|
| 1684 |
-
"displayName": "Danesh Selwal",
|
| 1685 |
-
"userId": "10565017814556927251"
|
| 1686 |
-
}
|
| 1687 |
-
}
|
| 1688 |
-
},
|
| 1689 |
-
"outputs": [],
|
| 1690 |
-
"source": [
|
| 1691 |
-
"# Custom objects dict required for loading serialised models\n",
|
| 1692 |
-
"VIS_CUSTOM_OBJECTS = {\n",
|
| 1693 |
-
" 'PatchExtractor': PatchExtractor,\n",
|
| 1694 |
-
" 'PatchPositionEncoder': PatchPositionEncoder,\n",
|
| 1695 |
-
" 'GlobalFilterLayer': GlobalFilterLayer,\n",
|
| 1696 |
-
" 'PatchEncoderWithCLS': PatchEncoderWithCLS,\n",
|
| 1697 |
-
"}\n",
|
| 1698 |
-
"\n",
|
| 1699 |
-
"\n",
|
| 1700 |
-
"def get_scene_rgb(x_img, bands):\n",
|
| 1701 |
-
" \"\"\"Return an approximate RGB image by selecting three middle bands.\"\"\"\n",
|
| 1702 |
-
" if bands >= 6:\n",
|
| 1703 |
-
" rgb_idx = [bands // 2 - 1, bands // 2, bands // 2 + 1]\n",
|
| 1704 |
-
" elif bands >= 3:\n",
|
| 1705 |
-
" rgb_idx = [0, 1, 2]\n",
|
| 1706 |
-
" else:\n",
|
| 1707 |
-
" raise ValueError(f'Need at least 3 bands to form RGB, got {bands}.')\n",
|
| 1708 |
-
" return np.clip(x_img[:, :, rgb_idx], 0.0, 1.0)\n",
|
| 1709 |
-
"\n",
|
| 1710 |
-
"\n",
|
| 1711 |
-
"def get_display_cmap(num_classes):\n",
|
| 1712 |
-
" \"\"\"Return a ListedColormap with black background plus one colour per class.\"\"\"\n",
|
| 1713 |
-
" return ListedColormap([BACKGROUND_COLOR] + get_class_colors(num_classes))\n",
|
| 1714 |
-
"\n",
|
| 1715 |
-
"\n",
|
| 1716 |
-
"def predict_full_scene_labels(model, x_img, patch_size=9, batch_size=256):\n",
|
| 1717 |
-
" \"\"\"Run patch-by-patch inference over the full scene and return a class-label map (1..K).\"\"\"\n",
|
| 1718 |
-
" pad = patch_size // 2\n",
|
| 1719 |
-
" x_pad = np.pad(x_img, ((pad, pad), (pad, pad), (0, 0)), mode='edge')\n",
|
| 1720 |
-
" preds = np.zeros((x_img.shape[0], x_img.shape[1]), dtype=np.int32)\n",
|
| 1721 |
-
"\n",
|
| 1722 |
-
" for r in range(x_img.shape[0]):\n",
|
| 1723 |
-
" row_patches = np.empty(\n",
|
| 1724 |
-
" (x_img.shape[1], patch_size, patch_size, x_img.shape[-1]), dtype=np.float32,\n",
|
| 1725 |
-
" )\n",
|
| 1726 |
-
" for c in range(x_img.shape[1]):\n",
|
| 1727 |
-
" row_patches[c] = x_pad[r:r + patch_size, c:c + patch_size, :]\n",
|
| 1728 |
-
" row_prob = model.predict(row_patches, batch_size=batch_size, verbose=0)\n",
|
| 1729 |
-
" preds[r] = np.argmax(row_prob, axis=1) + 1 # back to 1..K\n",
|
| 1730 |
-
"\n",
|
| 1731 |
-
" return preds\n",
|
| 1732 |
-
"\n",
|
| 1733 |
-
"\n",
|
| 1734 |
-
"def load_saved_models_for_visualization(model_dir, custom_objects):\n",
|
| 1735 |
-
" \"\"\"Attempt to load best-checkpoint .keras files for all three architectures.\"\"\"\n",
|
| 1736 |
-
" model_files = {\n",
|
| 1737 |
-
" 'AlexNet_CNN': model_dir / 'AlexNet_CNN_best.keras',\n",
|
| 1738 |
-
" 'GFNet': model_dir / 'GFNet_best.keras',\n",
|
| 1739 |
-
" 'ViT_UNet': model_dir / 'ViT_UNet_best.keras',\n",
|
| 1740 |
-
" }\n",
|
| 1741 |
-
" loaded, missing = {}, {}\n",
|
| 1742 |
-
" for model_key, model_path in model_files.items():\n",
|
| 1743 |
-
" if not model_path.exists():\n",
|
| 1744 |
-
" missing[model_key] = f'Missing file: {model_path}'\n",
|
| 1745 |
-
" continue\n",
|
| 1746 |
-
" try:\n",
|
| 1747 |
-
" loaded[model_key] = keras.models.load_model(\n",
|
| 1748 |
-
" model_path, custom_objects=custom_objects, compile=False, safe_mode=False,\n",
|
| 1749 |
-
" )\n",
|
| 1750 |
-
" except Exception as exc:\n",
|
| 1751 |
-
" missing[model_key] = f'Load failed: {exc}'\n",
|
| 1752 |
-
" return loaded, missing\n",
|
| 1753 |
-
"\n",
|
| 1754 |
-
"\n",
|
| 1755 |
-
"def save_single_panel(image, title, save_path, cmap=None, vmin=None, vmax=None):\n",
|
| 1756 |
-
" \"\"\"Save a single image panel to PNG.\"\"\"\n",
|
| 1757 |
-
" fig, ax = plt.subplots(figsize=(8, 6))\n",
|
| 1758 |
-
" if cmap is None:\n",
|
| 1759 |
-
" ax.imshow(image)\n",
|
| 1760 |
-
" else:\n",
|
| 1761 |
-
" ax.imshow(image, cmap=cmap, vmin=vmin, vmax=vmax)\n",
|
| 1762 |
-
" ax.set_title(title, fontsize=14)\n",
|
| 1763 |
-
" ax.axis('off')\n",
|
| 1764 |
-
" fig.tight_layout()\n",
|
| 1765 |
-
" fig.savefig(save_path, dpi=220, bbox_inches='tight')\n",
|
| 1766 |
-
" plt.show()\n",
|
| 1767 |
-
" plt.close(fig)\n",
|
| 1768 |
-
" print('Saved:', save_path)\n",
|
| 1769 |
-
"\n",
|
| 1770 |
-
"\n",
|
| 1771 |
-
"def save_combined_overview(rgb_image, y_true, pred_maps, missing_info, num_classes, save_path):\n",
|
| 1772 |
-
" \"\"\"Save a single figure with RGB, ground truth, and all three classification maps side-by-side.\"\"\"\n",
|
| 1773 |
-
" cmap = get_display_cmap(num_classes)\n",
|
| 1774 |
-
"\n",
|
| 1775 |
-
" panels = [\n",
|
| 1776 |
-
" ('Approximate RGB', rgb_image, 'rgb'),\n",
|
| 1777 |
-
" ('True Label Map', y_true, 'label'),\n",
|
| 1778 |
-
" ('AlexNet Initial Classification', pred_maps.get('AlexNet_CNN'), 'label'),\n",
|
| 1779 |
-
" ('GFNet Initial Classification', pred_maps.get('GFNet'), 'label'),\n",
|
| 1780 |
-
" ('ViT Initial Classification', pred_maps.get('ViT_UNet'), 'label'),\n",
|
| 1781 |
-
" ]\n",
|
| 1782 |
-
"\n",
|
| 1783 |
-
" fig, axes = plt.subplots(1, len(panels), figsize=(30, 7), sharex=True, sharey=True)\n",
|
| 1784 |
-
" for ax, (title, img, kind) in zip(axes, panels):\n",
|
| 1785 |
-
" ax.set_title(title, fontsize=13)\n",
|
| 1786 |
-
" if kind == 'rgb':\n",
|
| 1787 |
-
" ax.imshow(img)\n",
|
| 1788 |
-
" else:\n",
|
| 1789 |
-
" if img is None:\n",
|
| 1790 |
-
" ax.imshow(np.zeros_like(y_true), cmap=cmap, vmin=0, vmax=num_classes)\n",
|
| 1791 |
-
" # Determine which key is missing to show the correct error message\n",
|
| 1792 |
-
" key = next((k for k in ['AlexNet_CNN', 'GFNet', 'ViT_UNet'] if k in title), None)\n",
|
| 1793 |
-
" ax.text(0.5, 0.5, missing_info.get(key, 'Unavailable'),\n",
|
| 1794 |
-
" ha='center', va='center', fontsize=10, wrap=True, transform=ax.transAxes)\n",
|
| 1795 |
-
" else:\n",
|
| 1796 |
-
" ax.imshow(img, cmap=cmap, vmin=0, vmax=num_classes)\n",
|
| 1797 |
-
" ax.axis('off')\n",
|
| 1798 |
-
"\n",
|
| 1799 |
-
" handles = [plt.Rectangle((0, 0), 1, 1, color=BACKGROUND_COLOR, label='Background')]\n",
|
| 1800 |
-
" class_colors = get_class_colors(num_classes)\n",
|
| 1801 |
-
" for i in range(num_classes):\n",
|
| 1802 |
-
" handles.append(plt.Rectangle((0, 0), 1, 1, color=class_colors[i], label=f'Class {i+1}'))\n",
|
| 1803 |
-
" fig.legend(handles=handles, loc='lower center', ncol=min(num_classes + 1, 8),\n",
|
| 1804 |
-
" bbox_to_anchor=(0.5, -0.02), frameon=True)\n",
|
| 1805 |
-
" fig.tight_layout(rect=[0, 0.05, 1, 1])\n",
|
| 1806 |
-
" fig.savefig(save_path, dpi=220, bbox_inches='tight')\n",
|
| 1807 |
-
" plt.show()\n",
|
| 1808 |
-
" plt.close(fig)\n",
|
| 1809 |
-
" print('Saved:', save_path)\n",
|
| 1810 |
-
"\n",
|
| 1811 |
-
"\n",
|
| 1812 |
-
"def save_images_to_excel(excel_path, image_paths, sheet_name='Initial_Classification_Maps'):\n",
|
| 1813 |
-
" \"\"\"Embed PNG images into an Excel workbook sheet at predefined anchor cells.\"\"\"\n",
|
| 1814 |
-
" if excel_path.exists():\n",
|
| 1815 |
-
" wb = load_workbook(excel_path)\n",
|
| 1816 |
-
" if sheet_name in wb.sheetnames:\n",
|
| 1817 |
-
" del wb[sheet_name]\n",
|
| 1818 |
-
" ws = wb.create_sheet(title=sheet_name)\n",
|
| 1819 |
-
" else:\n",
|
| 1820 |
-
" wb = Workbook()\n",
|
| 1821 |
-
" ws = wb.active\n",
|
| 1822 |
-
" ws.title = sheet_name\n",
|
| 1823 |
-
"\n",
|
| 1824 |
-
" anchors = ['A1', 'J1', 'A25', 'J25', 'A49', 'J49']\n",
|
| 1825 |
-
" for path, anchor in zip(image_paths, anchors):\n",
|
| 1826 |
-
" if path.exists():\n",
|
| 1827 |
-
" img = XLImage(str(path))\n",
|
| 1828 |
-
" img.anchor = anchor\n",
|
| 1829 |
-
" ws.add_image(img)\n",
|
| 1830 |
-
"\n",
|
| 1831 |
-
" wb.save(excel_path)\n",
|
| 1832 |
-
" print('Saved Excel workbook:', excel_path)"
|
| 1833 |
-
],
|
| 1834 |
-
"id": "3xH3h143jmfK"
|
| 1835 |
-
},
|
| 1836 |
-
{
|
| 1837 |
-
"cell_type": "markdown",
|
| 1838 |
-
"metadata": {
|
| 1839 |
-
"id": "KBnpo1UMjmfL"
|
| 1840 |
-
},
|
| 1841 |
-
"source": [
|
| 1842 |
-
"## 8.2 \u2014 Load Models & Generate Classification Maps"
|
| 1843 |
-
],
|
| 1844 |
-
"id": "KBnpo1UMjmfL"
|
| 1845 |
-
},
|
| 1846 |
-
{
|
| 1847 |
-
"cell_type": "code",
|
| 1848 |
-
"execution_count": null,
|
| 1849 |
-
"metadata": {
|
| 1850 |
-
"id": "9tRAAuPDjmfL",
|
| 1851 |
-
"colab": {
|
| 1852 |
-
"base_uri": "https://localhost:8080/"
|
| 1853 |
-
},
|
| 1854 |
-
"executionInfo": {
|
| 1855 |
-
"status": "ok",
|
| 1856 |
-
"timestamp": 1782320198451,
|
| 1857 |
-
"user_tz": -330,
|
| 1858 |
-
"elapsed": 38,
|
| 1859 |
-
"user": {
|
| 1860 |
-
"displayName": "Danesh Selwal",
|
| 1861 |
-
"userId": "10565017814556927251"
|
| 1862 |
-
}
|
| 1863 |
-
},
|
| 1864 |
-
"outputId": "f215e392-495c-4bf5-b8df-98462dc7b1e1"
|
| 1865 |
-
},
|
| 1866 |
-
"outputs": [],
|
| 1867 |
-
"source": [
|
| 1868 |
-
"scene_rgb = get_scene_rgb(x_img, B)\n",
|
| 1869 |
-
"display_cmap = get_display_cmap(num_classes)\n",
|
| 1870 |
-
"\n",
|
| 1871 |
-
"loaded_models, missing_models = load_saved_models_for_visualization(MODEL_DIR, VIS_CUSTOM_OBJECTS)\n",
|
| 1872 |
-
"prediction_maps = {}\n",
|
| 1873 |
-
"\n",
|
| 1874 |
-
"for model_key, loaded_model in loaded_models.items():\n",
|
| 1875 |
-
" print(f'Generating full-scene classified image for {model_key}...')\n",
|
| 1876 |
-
" prediction_maps[model_key] = predict_full_scene_labels(\n",
|
| 1877 |
-
" loaded_model, x_img, patch_size=PATCH_SIZE, batch_size=BATCH_SIZE,\n",
|
| 1878 |
-
" )\n",
|
| 1879 |
-
" print(f'{model_key} map shape:', prediction_maps[model_key].shape)\n",
|
| 1880 |
-
"\n",
|
| 1881 |
-
"if missing_models:\n",
|
| 1882 |
-
" print('Some models could not be loaded:')\n",
|
| 1883 |
-
" for model_key, reason in missing_models.items():\n",
|
| 1884 |
-
" print(f' - {model_key}: {reason}')"
|
| 1885 |
-
],
|
| 1886 |
-
"id": "9tRAAuPDjmfL"
|
| 1887 |
-
},
|
| 1888 |
-
{
|
| 1889 |
-
"cell_type": "markdown",
|
| 1890 |
-
"metadata": {
|
| 1891 |
-
"id": "_QerZQMWjmfL"
|
| 1892 |
-
},
|
| 1893 |
-
"source": [
|
| 1894 |
-
"## 8.3 \u2014 Save PNGs & Excel Workbook"
|
| 1895 |
-
],
|
| 1896 |
-
"id": "_QerZQMWjmfL"
|
| 1897 |
-
},
|
| 1898 |
-
{
|
| 1899 |
-
"cell_type": "code",
|
| 1900 |
-
"execution_count": null,
|
| 1901 |
-
"metadata": {
|
| 1902 |
-
"id": "bCBTA_H4jmfM",
|
| 1903 |
-
"colab": {
|
| 1904 |
-
"base_uri": "https://localhost:8080/",
|
| 1905 |
-
"height": 0
|
| 1906 |
-
},
|
| 1907 |
-
"executionInfo": {
|
| 1908 |
-
"status": "ok",
|
| 1909 |
-
"timestamp": 1782320201632,
|
| 1910 |
-
"user_tz": -330,
|
| 1911 |
-
"elapsed": 3179,
|
| 1912 |
-
"user": {
|
| 1913 |
-
"displayName": "Danesh Selwal",
|
| 1914 |
-
"userId": "10565017814556927251"
|
| 1915 |
-
}
|
| 1916 |
-
},
|
| 1917 |
-
"outputId": "f232098e-7b22-452a-a1a4-f1d55d52eef1"
|
| 1918 |
-
},
|
| 1919 |
-
"outputs": [],
|
| 1920 |
-
"source": [
|
| 1921 |
-
"rgb_png = VIS_DIR / 'scene_rgb.png'\n",
|
| 1922 |
-
"gt_png = VIS_DIR / 'ground_truth_label_map.png'\n",
|
| 1923 |
-
"alex_png = VIS_DIR / 'AlexNet_CNN_initial_classification.png'\n",
|
| 1924 |
-
"gfnet_png = VIS_DIR / 'GFNet_initial_classification.png'\n",
|
| 1925 |
-
"vit_png = VIS_DIR / 'ViT_UNet_initial_classification.png'\n",
|
| 1926 |
-
"overview_png = VIS_DIR / 'combined_initial_classification_overview.png'\n",
|
| 1927 |
-
"\n",
|
| 1928 |
-
"save_single_panel(scene_rgb, 'Approximate RGB', rgb_png)\n",
|
| 1929 |
-
"save_single_panel(y_img, 'Ground Truth Label Map', gt_png,\n",
|
| 1930 |
-
" cmap=display_cmap, vmin=0, vmax=num_classes)\n",
|
| 1931 |
-
"\n",
|
| 1932 |
-
"if prediction_maps.get('AlexNet_CNN') is not None:\n",
|
| 1933 |
-
" save_single_panel(prediction_maps['AlexNet_CNN'], 'AlexNet Initial Classification',\n",
|
| 1934 |
-
" alex_png, cmap=display_cmap, vmin=0, vmax=num_classes)\n",
|
| 1935 |
-
"\n",
|
| 1936 |
-
"if prediction_maps.get('GFNet') is not None:\n",
|
| 1937 |
-
" save_single_panel(prediction_maps['GFNet'], 'GFNet Initial Classification',\n",
|
| 1938 |
-
" gfnet_png, cmap=display_cmap, vmin=0, vmax=num_classes)\n",
|
| 1939 |
-
"\n",
|
| 1940 |
-
"if prediction_maps.get('ViT_UNet') is not None:\n",
|
| 1941 |
-
" save_single_panel(prediction_maps['ViT_UNet'], 'ViT Initial Classification',\n",
|
| 1942 |
-
" vit_png, cmap=display_cmap, vmin=0, vmax=num_classes)\n",
|
| 1943 |
-
"\n",
|
| 1944 |
-
"save_combined_overview(\n",
|
| 1945 |
-
" rgb_image=scene_rgb, y_true=y_img,\n",
|
| 1946 |
-
" pred_maps=prediction_maps, missing_info=missing_models,\n",
|
| 1947 |
-
" num_classes=num_classes, save_path=overview_png,\n",
|
| 1948 |
-
")\n",
|
| 1949 |
-
"\n",
|
| 1950 |
-
"image_paths_for_excel = [rgb_png, gt_png, alex_png, gfnet_png, vit_png, overview_png]\n",
|
| 1951 |
-
"save_images_to_excel(VIS_EXCEL_PATH, image_paths_for_excel,\n",
|
| 1952 |
-
" sheet_name='Initial_Classification_Maps')\n",
|
| 1953 |
-
"\n",
|
| 1954 |
-
"print('All visualisation outputs saved in:', VIS_DIR)"
|
| 1955 |
-
],
|
| 1956 |
-
"id": "bCBTA_H4jmfM"
|
| 1957 |
-
},
|
| 1958 |
-
{
|
| 1959 |
-
"cell_type": "markdown",
|
| 1960 |
-
"metadata": {
|
| 1961 |
-
"id": "t_0f2DsWjmfM"
|
| 1962 |
-
},
|
| 1963 |
-
"source": [
|
| 1964 |
-
"# 9.0 \u2014 Model Reload Utility\n",
|
| 1965 |
-
"\n",
|
| 1966 |
-
"Convenience cell for loading any saved `.keras` model from Drive for downstream uncertainty analysis without retraining."
|
| 1967 |
-
],
|
| 1968 |
-
"id": "t_0f2DsWjmfM"
|
| 1969 |
-
},
|
| 1970 |
-
{
|
| 1971 |
-
"cell_type": "code",
|
| 1972 |
-
"execution_count": null,
|
| 1973 |
-
"metadata": {
|
| 1974 |
-
"id": "cPjwBCk5jmfM",
|
| 1975 |
-
"colab": {
|
| 1976 |
-
"base_uri": "https://localhost:8080/"
|
| 1977 |
-
},
|
| 1978 |
-
"executionInfo": {
|
| 1979 |
-
"status": "ok",
|
| 1980 |
-
"timestamp": 1782320201648,
|
| 1981 |
-
"user_tz": -330,
|
| 1982 |
-
"elapsed": 25,
|
| 1983 |
-
"user": {
|
| 1984 |
-
"displayName": "Danesh Selwal",
|
| 1985 |
-
"userId": "10565017814556927251"
|
| 1986 |
-
}
|
| 1987 |
-
},
|
| 1988 |
-
"outputId": "3c5ce609-1833-4c1e-bc97-892abc2b2c7b"
|
| 1989 |
-
},
|
| 1990 |
-
"outputs": [],
|
| 1991 |
-
"source": [
|
| 1992 |
-
"example_path = MODEL_DIR / 'ViT_UNet_final.keras'\n",
|
| 1993 |
-
"if example_path.exists():\n",
|
| 1994 |
-
" loaded_model = keras.models.load_model(\n",
|
| 1995 |
-
" example_path, custom_objects=VIS_CUSTOM_OBJECTS,\n",
|
| 1996 |
-
" compile=False, safe_mode=False,\n",
|
| 1997 |
-
" )\n",
|
| 1998 |
-
" print('Loaded:', example_path)\n",
|
| 1999 |
-
" print('Model name:', loaded_model.name)\n",
|
| 2000 |
-
"else:\n",
|
| 2001 |
-
" print('Example model not found. Train first, then reload.')"
|
| 2002 |
-
],
|
| 2003 |
-
"id": "cPjwBCk5jmfM"
|
| 2004 |
-
}
|
| 2005 |
-
]
|
| 2006 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/Model_uncertainty_CreDE.ipynb
DELETED
|
@@ -1,1365 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"nbformat": 4,
|
| 3 |
-
"nbformat_minor": 5,
|
| 4 |
-
"metadata": {
|
| 5 |
-
"kernelspec": {
|
| 6 |
-
"display_name": "Python 3",
|
| 7 |
-
"name": "python3"
|
| 8 |
-
},
|
| 9 |
-
"language_info": {
|
| 10 |
-
"name": "python",
|
| 11 |
-
"version": "3.10.0"
|
| 12 |
-
},
|
| 13 |
-
"colab": {
|
| 14 |
-
"provenance": [],
|
| 15 |
-
"gpuType": "T4"
|
| 16 |
-
},
|
| 17 |
-
"accelerator": "GPU"
|
| 18 |
-
},
|
| 19 |
-
"cells": [
|
| 20 |
-
{
|
| 21 |
-
"cell_type": "markdown",
|
| 22 |
-
"metadata": {
|
| 23 |
-
"id": "63UryAyvkXs3"
|
| 24 |
-
},
|
| 25 |
-
"source": [
|
| 26 |
-
"# 1.0 \u2014 Setup & Imports\n",
|
| 27 |
-
"\n",
|
| 28 |
-
"Mount Google Drive and import all required standard-library, third-party, and framework packages."
|
| 29 |
-
],
|
| 30 |
-
"id": "63UryAyvkXs3"
|
| 31 |
-
},
|
| 32 |
-
{
|
| 33 |
-
"cell_type": "markdown",
|
| 34 |
-
"metadata": {
|
| 35 |
-
"id": "vfTGGbvBkXs3"
|
| 36 |
-
},
|
| 37 |
-
"source": [
|
| 38 |
-
"## 1.1 \u2014 Drive Mount"
|
| 39 |
-
],
|
| 40 |
-
"id": "vfTGGbvBkXs3"
|
| 41 |
-
},
|
| 42 |
-
{
|
| 43 |
-
"cell_type": "code",
|
| 44 |
-
"execution_count": null,
|
| 45 |
-
"metadata": {
|
| 46 |
-
"colab": {
|
| 47 |
-
"base_uri": "https://localhost:8080/"
|
| 48 |
-
},
|
| 49 |
-
"id": "M7Y8Rl0zkXs3",
|
| 50 |
-
"executionInfo": {
|
| 51 |
-
"status": "ok",
|
| 52 |
-
"timestamp": 1780309403777,
|
| 53 |
-
"user_tz": -330,
|
| 54 |
-
"elapsed": 38883,
|
| 55 |
-
"user": {
|
| 56 |
-
"displayName": "Randy Orton",
|
| 57 |
-
"userId": "11725427482974841050"
|
| 58 |
-
}
|
| 59 |
-
},
|
| 60 |
-
"outputId": "6fcb4ec0-0712-4ac1-883a-59c52b24aa35"
|
| 61 |
-
},
|
| 62 |
-
"outputs": [],
|
| 63 |
-
"source": [
|
| 64 |
-
"from google.colab import drive\n",
|
| 65 |
-
"drive.mount('/content/drive')"
|
| 66 |
-
],
|
| 67 |
-
"id": "M7Y8Rl0zkXs3"
|
| 68 |
-
},
|
| 69 |
-
{
|
| 70 |
-
"cell_type": "markdown",
|
| 71 |
-
"metadata": {
|
| 72 |
-
"id": "JT-hTYlVkXs3"
|
| 73 |
-
},
|
| 74 |
-
"source": [
|
| 75 |
-
"## 1.2 \u2014 Library Imports\n",
|
| 76 |
-
"\n",
|
| 77 |
-
"Groups: standard library \u2192 numerical/data \u2192 visualisation \u2192 deep learning."
|
| 78 |
-
],
|
| 79 |
-
"id": "JT-hTYlVkXs3"
|
| 80 |
-
},
|
| 81 |
-
{
|
| 82 |
-
"cell_type": "code",
|
| 83 |
-
"execution_count": null,
|
| 84 |
-
"metadata": {
|
| 85 |
-
"colab": {
|
| 86 |
-
"base_uri": "https://localhost:8080/"
|
| 87 |
-
},
|
| 88 |
-
"id": "4znih1uIkXs3",
|
| 89 |
-
"executionInfo": {
|
| 90 |
-
"status": "ok",
|
| 91 |
-
"timestamp": 1780309408442,
|
| 92 |
-
"user_tz": -330,
|
| 93 |
-
"elapsed": 4668,
|
| 94 |
-
"user": {
|
| 95 |
-
"displayName": "Randy Orton",
|
| 96 |
-
"userId": "11725427482974841050"
|
| 97 |
-
}
|
| 98 |
-
},
|
| 99 |
-
"outputId": "1ba3cd1a-99e8-4744-feb6-dcd959834bad"
|
| 100 |
-
},
|
| 101 |
-
"outputs": [],
|
| 102 |
-
"source": [
|
| 103 |
-
"# Standard library\n",
|
| 104 |
-
"import os\n",
|
| 105 |
-
"import gc\n",
|
| 106 |
-
"import glob\n",
|
| 107 |
-
"from pathlib import Path\n",
|
| 108 |
-
"\n",
|
| 109 |
-
"from scipy.io import loadmat\n",
|
| 110 |
-
"\n",
|
| 111 |
-
"# Numerical & data\n",
|
| 112 |
-
"import numpy as np\n",
|
| 113 |
-
"import pandas as pd\n",
|
| 114 |
-
"import seaborn as sns\n",
|
| 115 |
-
"\n",
|
| 116 |
-
"# Visualisation\n",
|
| 117 |
-
"import matplotlib.pyplot as plt\n",
|
| 118 |
-
"from matplotlib.colors import ListedColormap\n",
|
| 119 |
-
"from matplotlib.patches import Patch\n",
|
| 120 |
-
"\n",
|
| 121 |
-
"# Deep learning\n",
|
| 122 |
-
"import tensorflow as tf\n",
|
| 123 |
-
"from tensorflow.keras import layers\n",
|
| 124 |
-
"\n",
|
| 125 |
-
"# Metrics & Splitting\n",
|
| 126 |
-
"from sklearn.model_selection import train_test_split\n",
|
| 127 |
-
"from sklearn.metrics import (\n",
|
| 128 |
-
" accuracy_score, cohen_kappa_score, confusion_matrix,\n",
|
| 129 |
-
" classification_report, f1_score, log_loss\n",
|
| 130 |
-
")\n",
|
| 131 |
-
"\n",
|
| 132 |
-
"# \u2500\u2500 Excel export \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 133 |
-
"from openpyxl import Workbook\n",
|
| 134 |
-
"from openpyxl.styles import Font, PatternFill, Alignment, Border, Side\n",
|
| 135 |
-
"from openpyxl.utils import get_column_letter\n",
|
| 136 |
-
"from openpyxl.drawing.image import Image as XLImage\n",
|
| 137 |
-
"\n",
|
| 138 |
-
"sns.set_style(\"whitegrid\")\n",
|
| 139 |
-
"print(\"TensorFlow:\", tf.__version__)"
|
| 140 |
-
],
|
| 141 |
-
"id": "4znih1uIkXs3"
|
| 142 |
-
},
|
| 143 |
-
{
|
| 144 |
-
"cell_type": "markdown",
|
| 145 |
-
"metadata": {
|
| 146 |
-
"id": "7KYP-49QkXs4"
|
| 147 |
-
},
|
| 148 |
-
"source": [
|
| 149 |
-
"# 2.0 \u2014 Configuration\n",
|
| 150 |
-
"\n",
|
| 151 |
-
"All tunable constants and filesystem paths live here. Edit this section before running\n",
|
| 152 |
-
"the notebook on a new dataset or experiment."
|
| 153 |
-
],
|
| 154 |
-
"id": "7KYP-49QkXs4"
|
| 155 |
-
},
|
| 156 |
-
{
|
| 157 |
-
"cell_type": "markdown",
|
| 158 |
-
"metadata": {
|
| 159 |
-
"id": "0_MMSyFCkXs4"
|
| 160 |
-
},
|
| 161 |
-
"source": [
|
| 162 |
-
"## 2.1 \u2014 Constants & Paths"
|
| 163 |
-
],
|
| 164 |
-
"id": "0_MMSyFCkXs4"
|
| 165 |
-
},
|
| 166 |
-
{
|
| 167 |
-
"cell_type": "code",
|
| 168 |
-
"execution_count": null,
|
| 169 |
-
"metadata": {
|
| 170 |
-
"id": "BMVg7YpUkXs4",
|
| 171 |
-
"executionInfo": {
|
| 172 |
-
"status": "ok",
|
| 173 |
-
"timestamp": 1780309408454,
|
| 174 |
-
"user_tz": -330,
|
| 175 |
-
"elapsed": 3,
|
| 176 |
-
"user": {
|
| 177 |
-
"displayName": "Randy Orton",
|
| 178 |
-
"userId": "11725427482974841050"
|
| 179 |
-
}
|
| 180 |
-
}
|
| 181 |
-
},
|
| 182 |
-
"outputs": [],
|
| 183 |
-
"source": [
|
| 184 |
-
"# Reproducibility\n",
|
| 185 |
-
"SEED = 42\n",
|
| 186 |
-
"np.random.seed(SEED)\n",
|
| 187 |
-
"tf.random.set_seed(SEED)\n",
|
| 188 |
-
"\n",
|
| 189 |
-
"# Filesystem\n",
|
| 190 |
-
"ROOT_DIR = Path(\"/content/drive/My Drive/Classification\")\n",
|
| 191 |
-
"MODEL_DIR = Path(\"/content/drive/My Drive/Classification/ensemble/models/ensembles\")\n",
|
| 192 |
-
"DATA_DIR = Path(\"/content/drive/My Drive/Classification/data\")\n",
|
| 193 |
-
"DATA_FILE = DATA_DIR / \"DIAS.mat\" # hyperspectral cube (H, W, Bands)\n",
|
| 194 |
-
"LABEL_FILE = DATA_DIR / \"DIAS_ref.mat\" # reference / ground-truth label map\n",
|
| 195 |
-
"\n",
|
| 196 |
-
"# Scene geometry \u2014 H, W, B are inferred automatically from the .mat cube shape\n",
|
| 197 |
-
"# once loaded in Section 3.1, so this notebook works unchanged for any scene\n",
|
| 198 |
-
"# size / band count.\n",
|
| 199 |
-
"H, W, B = None, None, None\n",
|
| 200 |
-
"PERCENTILE_CLIP = (2.0, 98.0) # robust per-band stretch range (2nd-98th percentile)\n",
|
| 201 |
-
"PATCH_SIZE = 9\n",
|
| 202 |
-
"\n",
|
| 203 |
-
"# Uncertainty thresholds used for masking\n",
|
| 204 |
-
"AU_THRESH = 0.5\n",
|
| 205 |
-
"EU_THRESH = 0.2\n",
|
| 206 |
-
"TU_THRESH = 0.7"
|
| 207 |
-
],
|
| 208 |
-
"id": "BMVg7YpUkXs4"
|
| 209 |
-
},
|
| 210 |
-
{
|
| 211 |
-
"cell_type": "markdown",
|
| 212 |
-
"metadata": {
|
| 213 |
-
"id": "sYYDGaimkXs4"
|
| 214 |
-
},
|
| 215 |
-
"source": [
|
| 216 |
-
"## 2.2 \u2014 Output Directory Setup\n",
|
| 217 |
-
"\n",
|
| 218 |
-
"Creates a dedicated subfolder inside `MODEL_DIR` to hold all CreDE outputs (PNGs and CSV)."
|
| 219 |
-
],
|
| 220 |
-
"id": "sYYDGaimkXs4"
|
| 221 |
-
},
|
| 222 |
-
{
|
| 223 |
-
"cell_type": "code",
|
| 224 |
-
"execution_count": null,
|
| 225 |
-
"metadata": {
|
| 226 |
-
"colab": {
|
| 227 |
-
"base_uri": "https://localhost:8080/"
|
| 228 |
-
},
|
| 229 |
-
"id": "m2H6dkT8kXs4",
|
| 230 |
-
"executionInfo": {
|
| 231 |
-
"status": "ok",
|
| 232 |
-
"timestamp": 1780309409217,
|
| 233 |
-
"user_tz": -330,
|
| 234 |
-
"elapsed": 761,
|
| 235 |
-
"user": {
|
| 236 |
-
"displayName": "Randy Orton",
|
| 237 |
-
"userId": "11725427482974841050"
|
| 238 |
-
}
|
| 239 |
-
},
|
| 240 |
-
"outputId": "8f027b2c-6390-4a3c-b1d0-d2555712d8e9"
|
| 241 |
-
},
|
| 242 |
-
"outputs": [],
|
| 243 |
-
"source": [
|
| 244 |
-
"CREDE_OUT_DIR = ROOT_DIR/ \"ensemble\" / \"results\"\n",
|
| 245 |
-
"CREDE_OUT_DIR.mkdir(parents=True, exist_ok=True)\n",
|
| 246 |
-
"print(f\"CreDE output directory: {CREDE_OUT_DIR}\")"
|
| 247 |
-
],
|
| 248 |
-
"id": "m2H6dkT8kXs4"
|
| 249 |
-
},
|
| 250 |
-
{
|
| 251 |
-
"cell_type": "markdown",
|
| 252 |
-
"metadata": {
|
| 253 |
-
"id": "WO9u-5JckXs4"
|
| 254 |
-
},
|
| 255 |
-
"source": [
|
| 256 |
-
"# 3.0 \u2014 Data Loading & Preprocessing\n",
|
| 257 |
-
"\n",
|
| 258 |
-
"Reads the hyperspectral/multispectral scene and label raster from .mat files, robustly\n",
|
| 259 |
-
"normalises each band via a percentile clip to [0, 1], then extracts every spatial patch needed for full-scene inference."
|
| 260 |
-
],
|
| 261 |
-
"id": "WO9u-5JckXs4"
|
| 262 |
-
},
|
| 263 |
-
{
|
| 264 |
-
"cell_type": "markdown",
|
| 265 |
-
"metadata": {
|
| 266 |
-
"id": "4PqA6hOzkXs4"
|
| 267 |
-
},
|
| 268 |
-
"source": [
|
| 269 |
-
"## 3.1 \u2014 Hyperspectral/Multispectral Data Loader"
|
| 270 |
-
],
|
| 271 |
-
"id": "4PqA6hOzkXs4"
|
| 272 |
-
},
|
| 273 |
-
{
|
| 274 |
-
"cell_type": "code",
|
| 275 |
-
"execution_count": null,
|
| 276 |
-
"metadata": {
|
| 277 |
-
"colab": {
|
| 278 |
-
"base_uri": "https://localhost:8080/"
|
| 279 |
-
},
|
| 280 |
-
"id": "0DLcWZkLkXs4",
|
| 281 |
-
"executionInfo": {
|
| 282 |
-
"status": "ok",
|
| 283 |
-
"timestamp": 1780309410338,
|
| 284 |
-
"user_tz": -330,
|
| 285 |
-
"elapsed": 1119,
|
| 286 |
-
"user": {
|
| 287 |
-
"displayName": "Randy Orton",
|
| 288 |
-
"userId": "11725427482974841050"
|
| 289 |
-
}
|
| 290 |
-
},
|
| 291 |
-
"outputId": "7d510f99-c3c5-43ca-b0f7-c347ab0b28c8"
|
| 292 |
-
},
|
| 293 |
-
"outputs": [],
|
| 294 |
-
"source": [
|
| 295 |
-
"def load_scene_from_mat(data_path, label_path, data_key=None, label_key=None):\n",
|
| 296 |
-
" \"\"\"\n",
|
| 297 |
-
" Load a hyperspectral/multispectral scene + reference labels from .mat files.\n",
|
| 298 |
-
"\n",
|
| 299 |
-
" Auto-detects the variable name inside each .mat file if not given (matches\n",
|
| 300 |
-
" files such as DIAS.mat / DIAS_ref.mat storing the array under a single named\n",
|
| 301 |
-
" variable). Each spectral band is robustly stretched to [0, 1] using a\n",
|
| 302 |
-
" percentile clip (PERCENTILE_CLIP) rather than literal min/max \u2014 a handful of\n",
|
| 303 |
-
" saturated/no-data pixels (e.g. sensor clipping at int8 range limits -128/127)\n",
|
| 304 |
-
" would otherwise dominate the stretch.\n",
|
| 305 |
-
"\n",
|
| 306 |
-
" Returns\n",
|
| 307 |
-
" -------\n",
|
| 308 |
-
" x_norm : np.ndarray Shape (H, W, B), dtype float32, values in [0, 1].\n",
|
| 309 |
-
" y : np.ndarray Shape (H, W), dtype int32.\n",
|
| 310 |
-
" height, width, bands : int\n",
|
| 311 |
-
" \"\"\"\n",
|
| 312 |
-
" def _first_array(mat_dict, key):\n",
|
| 313 |
-
" if key is not None:\n",
|
| 314 |
-
" return mat_dict[key]\n",
|
| 315 |
-
" candidates = [v for k, v in mat_dict.items() if not k.startswith(\"__\")]\n",
|
| 316 |
-
" if not candidates:\n",
|
| 317 |
-
" raise ValueError(f\"No data arrays found in {mat_dict.keys()}\")\n",
|
| 318 |
-
" return candidates[0]\n",
|
| 319 |
-
"\n",
|
| 320 |
-
" data_mat = loadmat(data_path)\n",
|
| 321 |
-
" label_mat = loadmat(label_path)\n",
|
| 322 |
-
"\n",
|
| 323 |
-
" x = _first_array(data_mat, data_key).astype(np.float32)\n",
|
| 324 |
-
" y = _first_array(label_mat, label_key).astype(np.int32)\n",
|
| 325 |
-
"\n",
|
| 326 |
-
" if x.ndim != 3:\n",
|
| 327 |
-
" raise ValueError(f\"Expected a (H, W, Bands) cube, got shape {x.shape}\")\n",
|
| 328 |
-
" if y.shape != x.shape[:2]:\n",
|
| 329 |
-
" raise ValueError(f\"Label shape {y.shape} does not match image shape {x.shape[:2]}\")\n",
|
| 330 |
-
"\n",
|
| 331 |
-
" height, width, bands = x.shape\n",
|
| 332 |
-
"\n",
|
| 333 |
-
" x_norm = np.empty_like(x, dtype=np.float32)\n",
|
| 334 |
-
" for b in range(bands):\n",
|
| 335 |
-
" band = x[:, :, b]\n",
|
| 336 |
-
" lo = np.percentile(band, PERCENTILE_CLIP[0])\n",
|
| 337 |
-
" hi = np.percentile(band, PERCENTILE_CLIP[1])\n",
|
| 338 |
-
" denom = max(hi - lo, 1e-8)\n",
|
| 339 |
-
" x_norm[:, :, b] = np.clip((band - lo) / denom, 0.0, 1.0)\n",
|
| 340 |
-
"\n",
|
| 341 |
-
" return x_norm, y, height, width, bands\n",
|
| 342 |
-
"\n",
|
| 343 |
-
"\n",
|
| 344 |
-
"print(\"Loading hyperspectral/multispectral data...\")\n",
|
| 345 |
-
"x_img, y_img, H, W, B = load_scene_from_mat(DATA_FILE, LABEL_FILE)\n",
|
| 346 |
-
"num_classes = int(np.unique(y_img[y_img > 0]).size)\n",
|
| 347 |
-
"print(f\"Scene shape: {x_img.shape} | H={H} W={W} B={B} | Classes: {num_classes}\")"
|
| 348 |
-
],
|
| 349 |
-
"id": "0DLcWZkLkXs4"
|
| 350 |
-
},
|
| 351 |
-
{
|
| 352 |
-
"cell_type": "markdown",
|
| 353 |
-
"metadata": {
|
| 354 |
-
"id": "8nnfqMLMkXs5"
|
| 355 |
-
},
|
| 356 |
-
"source": [
|
| 357 |
-
"## 3.2 \u2014 Spatial Patch Extraction\n",
|
| 358 |
-
"\n",
|
| 359 |
-
"Pads the scene and slides a `PATCH_SIZE \u00d7 PATCH_SIZE` window over every pixel,\n",
|
| 360 |
-
"producing a flat array of patches ready for batch inference."
|
| 361 |
-
],
|
| 362 |
-
"id": "8nnfqMLMkXs5"
|
| 363 |
-
},
|
| 364 |
-
{
|
| 365 |
-
"cell_type": "code",
|
| 366 |
-
"execution_count": null,
|
| 367 |
-
"metadata": {
|
| 368 |
-
"colab": {
|
| 369 |
-
"base_uri": "https://localhost:8080/"
|
| 370 |
-
},
|
| 371 |
-
"id": "fMjSovXKkXs5",
|
| 372 |
-
"executionInfo": {
|
| 373 |
-
"status": "ok",
|
| 374 |
-
"timestamp": 1780309410514,
|
| 375 |
-
"user_tz": -330,
|
| 376 |
-
"elapsed": 162,
|
| 377 |
-
"user": {
|
| 378 |
-
"displayName": "Randy Orton",
|
| 379 |
-
"userId": "11725427482974841050"
|
| 380 |
-
}
|
| 381 |
-
},
|
| 382 |
-
"outputId": "12fab1e2-e775-4d79-9d56-34966e956c03"
|
| 383 |
-
},
|
| 384 |
-
"outputs": [],
|
| 385 |
-
"source": [
|
| 386 |
-
"print(\"Extracting all spatial patches for full-scene inference...\")\n",
|
| 387 |
-
"\n",
|
| 388 |
-
"pad = PATCH_SIZE // 2\n",
|
| 389 |
-
"x_pad = np.pad(x_img, ((pad, pad), (pad, pad), (0, 0)), mode=\"edge\")\n",
|
| 390 |
-
"\n",
|
| 391 |
-
"scene_pixels_scaled = np.empty((H * W, PATCH_SIZE, PATCH_SIZE, B), dtype=np.float32)\n",
|
| 392 |
-
"\n",
|
| 393 |
-
"idx = 0\n",
|
| 394 |
-
"for r in range(H):\n",
|
| 395 |
-
" for c in range(W):\n",
|
| 396 |
-
" scene_pixels_scaled[idx] = x_pad[r:r + PATCH_SIZE, c:c + PATCH_SIZE, :]\n",
|
| 397 |
-
" idx += 1\n",
|
| 398 |
-
"\n",
|
| 399 |
-
"print(f\"scene_pixels_scaled shape: {scene_pixels_scaled.shape}\")"
|
| 400 |
-
],
|
| 401 |
-
"id": "fMjSovXKkXs5"
|
| 402 |
-
},
|
| 403 |
-
{
|
| 404 |
-
"cell_type": "code",
|
| 405 |
-
"source": [
|
| 406 |
-
"# -----------------------------\n",
|
| 407 |
-
"# 3.3 \u2014 Labeled Patch Extraction & Splitting\n",
|
| 408 |
-
"# -----------------------------\n",
|
| 409 |
-
"print(\"Extracting labeled patches for metric evaluation...\")\n",
|
| 410 |
-
"\n",
|
| 411 |
-
"def extract_labeled_patches(x, y, patch_size=9):\n",
|
| 412 |
-
" pad = patch_size // 2\n",
|
| 413 |
-
" x_pad = np.pad(x, ((pad, pad), (pad, pad), (0, 0)), mode=\"edge\")\n",
|
| 414 |
-
" coords = np.argwhere(y > 0)\n",
|
| 415 |
-
" patches = np.empty((coords.shape[0], patch_size, patch_size, x.shape[-1]), dtype=np.float32)\n",
|
| 416 |
-
" labels = np.empty((coords.shape[0],), dtype=np.int32)\n",
|
| 417 |
-
" for i, (r, c) in enumerate(coords):\n",
|
| 418 |
-
" patches[i] = x_pad[r:r + patch_size, c:c + patch_size, :]\n",
|
| 419 |
-
" labels[i] = int(y[r, c]) - 1\n",
|
| 420 |
-
" return patches, labels, coords\n",
|
| 421 |
-
"\n",
|
| 422 |
-
"X, y_labels, coords = extract_labeled_patches(x_img, y_img, PATCH_SIZE)\n",
|
| 423 |
-
"\n",
|
| 424 |
-
"# Standard Split\n",
|
| 425 |
-
"TRAIN_PERCENT = 0.75\n",
|
| 426 |
-
"x_train_full, x_test, y_train_full, y_test = train_test_split(\n",
|
| 427 |
-
" X, y_labels, train_size=TRAIN_PERCENT, random_state=SEED, stratify=y_labels\n",
|
| 428 |
-
")\n",
|
| 429 |
-
"y_test_cat = tf.keras.utils.to_categorical(y_test, num_classes)\n",
|
| 430 |
-
"\n",
|
| 431 |
-
"print(f\"Standard Test Set Extracted: {x_test.shape}\")"
|
| 432 |
-
],
|
| 433 |
-
"metadata": {
|
| 434 |
-
"colab": {
|
| 435 |
-
"base_uri": "https://localhost:8080/"
|
| 436 |
-
},
|
| 437 |
-
"id": "LTpD-oZSex0w",
|
| 438 |
-
"executionInfo": {
|
| 439 |
-
"status": "ok",
|
| 440 |
-
"timestamp": 1780309410612,
|
| 441 |
-
"user_tz": -330,
|
| 442 |
-
"elapsed": 92,
|
| 443 |
-
"user": {
|
| 444 |
-
"displayName": "Randy Orton",
|
| 445 |
-
"userId": "11725427482974841050"
|
| 446 |
-
}
|
| 447 |
-
},
|
| 448 |
-
"outputId": "18422bc5-e302-4e9b-84f8-3a3424f90dde"
|
| 449 |
-
},
|
| 450 |
-
"id": "LTpD-oZSex0w",
|
| 451 |
-
"execution_count": null,
|
| 452 |
-
"outputs": []
|
| 453 |
-
},
|
| 454 |
-
{
|
| 455 |
-
"cell_type": "markdown",
|
| 456 |
-
"metadata": {
|
| 457 |
-
"id": "l9X69z2skXs5"
|
| 458 |
-
},
|
| 459 |
-
"source": [
|
| 460 |
-
"# 4.0 \u2014 Custom Keras Layers\n",
|
| 461 |
-
"\n",
|
| 462 |
-
"Registers the four custom `tf.keras` layers required to deserialise the saved `.keras` models.\n",
|
| 463 |
-
"All four are decorated with `@register_keras_serializable()` so they survive `load_model`.\n",
|
| 464 |
-
"**Do not alter any layer internals** \u2014 these must match the definitions used during training."
|
| 465 |
-
],
|
| 466 |
-
"id": "l9X69z2skXs5"
|
| 467 |
-
},
|
| 468 |
-
{
|
| 469 |
-
"cell_type": "markdown",
|
| 470 |
-
"metadata": {
|
| 471 |
-
"id": "91HKDq_TkXs5"
|
| 472 |
-
},
|
| 473 |
-
"source": [
|
| 474 |
-
"## 4.1 \u2014 PatchExtractor"
|
| 475 |
-
],
|
| 476 |
-
"id": "91HKDq_TkXs5"
|
| 477 |
-
},
|
| 478 |
-
{
|
| 479 |
-
"cell_type": "code",
|
| 480 |
-
"execution_count": null,
|
| 481 |
-
"metadata": {
|
| 482 |
-
"id": "Yde0Sg5kkXs5",
|
| 483 |
-
"executionInfo": {
|
| 484 |
-
"status": "ok",
|
| 485 |
-
"timestamp": 1780309410635,
|
| 486 |
-
"user_tz": -330,
|
| 487 |
-
"elapsed": 16,
|
| 488 |
-
"user": {
|
| 489 |
-
"displayName": "Randy Orton",
|
| 490 |
-
"userId": "11725427482974841050"
|
| 491 |
-
}
|
| 492 |
-
}
|
| 493 |
-
},
|
| 494 |
-
"outputs": [],
|
| 495 |
-
"source": [
|
| 496 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 497 |
-
"class PatchExtractor(layers.Layer):\n",
|
| 498 |
-
" \"\"\"Extracts non-overlapping image patches and flattens them into a sequence.\"\"\"\n",
|
| 499 |
-
"\n",
|
| 500 |
-
" def __init__(self, patch_size=3, **kwargs):\n",
|
| 501 |
-
" super().__init__(**kwargs)\n",
|
| 502 |
-
" self.patch_size = patch_size\n",
|
| 503 |
-
"\n",
|
| 504 |
-
" def call(self, images):\n",
|
| 505 |
-
" patches = tf.image.extract_patches(\n",
|
| 506 |
-
" images=images,\n",
|
| 507 |
-
" sizes=[1, self.patch_size, self.patch_size, 1],\n",
|
| 508 |
-
" strides=[1, self.patch_size, self.patch_size, 1],\n",
|
| 509 |
-
" rates=[1, 1, 1, 1],\n",
|
| 510 |
-
" padding=\"VALID\"\n",
|
| 511 |
-
" )\n",
|
| 512 |
-
" batch, patch_dim = tf.shape(images)[0], tf.shape(patches)[-1]\n",
|
| 513 |
-
" num_patches = tf.shape(patches)[1] * tf.shape(patches)[2]\n",
|
| 514 |
-
" return tf.reshape(patches, [batch, num_patches, patch_dim])\n",
|
| 515 |
-
"\n",
|
| 516 |
-
" def get_config(self):\n",
|
| 517 |
-
" cfg = super().get_config()\n",
|
| 518 |
-
" cfg.update({\"patch_size\": self.patch_size})\n",
|
| 519 |
-
" return cfg"
|
| 520 |
-
],
|
| 521 |
-
"id": "Yde0Sg5kkXs5"
|
| 522 |
-
},
|
| 523 |
-
{
|
| 524 |
-
"cell_type": "markdown",
|
| 525 |
-
"metadata": {
|
| 526 |
-
"id": "A63Sz62AkXs5"
|
| 527 |
-
},
|
| 528 |
-
"source": [
|
| 529 |
-
"## 4.2 \u2014 PatchPositionEncoder"
|
| 530 |
-
],
|
| 531 |
-
"id": "A63Sz62AkXs5"
|
| 532 |
-
},
|
| 533 |
-
{
|
| 534 |
-
"cell_type": "code",
|
| 535 |
-
"execution_count": null,
|
| 536 |
-
"metadata": {
|
| 537 |
-
"id": "4HbRIJRykXs5",
|
| 538 |
-
"executionInfo": {
|
| 539 |
-
"status": "ok",
|
| 540 |
-
"timestamp": 1780309410703,
|
| 541 |
-
"user_tz": -330,
|
| 542 |
-
"elapsed": 66,
|
| 543 |
-
"user": {
|
| 544 |
-
"displayName": "Randy Orton",
|
| 545 |
-
"userId": "11725427482974841050"
|
| 546 |
-
}
|
| 547 |
-
}
|
| 548 |
-
},
|
| 549 |
-
"outputs": [],
|
| 550 |
-
"source": [
|
| 551 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 552 |
-
"class PatchPositionEncoder(layers.Layer):\n",
|
| 553 |
-
" \"\"\"Projects patches to `projection_dim` and adds learned positional embeddings.\"\"\"\n",
|
| 554 |
-
"\n",
|
| 555 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 556 |
-
" super().__init__(**kwargs)\n",
|
| 557 |
-
" self.num_patches = num_patches\n",
|
| 558 |
-
" self.projection_dim = projection_dim\n",
|
| 559 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 560 |
-
" self.position_embedding = layers.Embedding(input_dim=num_patches, output_dim=projection_dim)\n",
|
| 561 |
-
"\n",
|
| 562 |
-
" def call(self, patches):\n",
|
| 563 |
-
" positions = tf.range(start=0, limit=self.num_patches, delta=1)\n",
|
| 564 |
-
" return self.projection(patches) + self.position_embedding(positions)\n",
|
| 565 |
-
"\n",
|
| 566 |
-
" def get_config(self):\n",
|
| 567 |
-
" cfg = super().get_config()\n",
|
| 568 |
-
" cfg.update({\"num_patches\": self.num_patches, \"projection_dim\": self.projection_dim})\n",
|
| 569 |
-
" return cfg"
|
| 570 |
-
],
|
| 571 |
-
"id": "4HbRIJRykXs5"
|
| 572 |
-
},
|
| 573 |
-
{
|
| 574 |
-
"cell_type": "markdown",
|
| 575 |
-
"metadata": {
|
| 576 |
-
"id": "7YukC294kXs5"
|
| 577 |
-
},
|
| 578 |
-
"source": [
|
| 579 |
-
"## 4.3 \u2014 GlobalFilterLayer"
|
| 580 |
-
],
|
| 581 |
-
"id": "7YukC294kXs5"
|
| 582 |
-
},
|
| 583 |
-
{
|
| 584 |
-
"cell_type": "code",
|
| 585 |
-
"execution_count": null,
|
| 586 |
-
"metadata": {
|
| 587 |
-
"id": "DzxtiRRMkXs5",
|
| 588 |
-
"executionInfo": {
|
| 589 |
-
"status": "ok",
|
| 590 |
-
"timestamp": 1780309410708,
|
| 591 |
-
"user_tz": -330,
|
| 592 |
-
"elapsed": 2,
|
| 593 |
-
"user": {
|
| 594 |
-
"displayName": "Randy Orton",
|
| 595 |
-
"userId": "11725427482974841050"
|
| 596 |
-
}
|
| 597 |
-
}
|
| 598 |
-
},
|
| 599 |
-
"outputs": [],
|
| 600 |
-
"source": [
|
| 601 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 602 |
-
"class GlobalFilterLayer(layers.Layer):\n",
|
| 603 |
-
" \"\"\"\n",
|
| 604 |
-
" Applies a learnable complex-valued filter in the 2-D frequency domain.\n",
|
| 605 |
-
"\n",
|
| 606 |
-
" The token sequence is reshaped to a square grid, transformed with FFT2D,\n",
|
| 607 |
-
" element-wise multiplied by trainable complex weights, then inverse-transformed\n",
|
| 608 |
-
" back to the spatial sequence.\n",
|
| 609 |
-
" \"\"\"\n",
|
| 610 |
-
"\n",
|
| 611 |
-
" def __init__(self, token_side, **kwargs):\n",
|
| 612 |
-
" super().__init__(**kwargs)\n",
|
| 613 |
-
" self.token_side = token_side\n",
|
| 614 |
-
"\n",
|
| 615 |
-
" def build(self, input_shape):\n",
|
| 616 |
-
" channels = int(input_shape[-1])\n",
|
| 617 |
-
" self.w_real = self.add_weight(\n",
|
| 618 |
-
" name=\"w_real\", shape=(self.token_side, self.token_side, channels),\n",
|
| 619 |
-
" initializer=\"glorot_uniform\", trainable=True\n",
|
| 620 |
-
" )\n",
|
| 621 |
-
" self.w_imag = self.add_weight(\n",
|
| 622 |
-
" name=\"w_imag\", shape=(self.token_side, self.token_side, channels),\n",
|
| 623 |
-
" initializer=\"zeros\", trainable=True\n",
|
| 624 |
-
" )\n",
|
| 625 |
-
" super().build(input_shape)\n",
|
| 626 |
-
"\n",
|
| 627 |
-
" def call(self, x):\n",
|
| 628 |
-
" batch = tf.shape(x)[0]\n",
|
| 629 |
-
" channels = tf.shape(x)[-1]\n",
|
| 630 |
-
"\n",
|
| 631 |
-
" x_2d = tf.reshape(x, [batch, self.token_side, self.token_side, channels])\n",
|
| 632 |
-
" x_fft = tf.signal.fft2d(tf.cast(x_2d, tf.complex64))\n",
|
| 633 |
-
" w_complex = tf.complex(self.w_real, self.w_imag)\n",
|
| 634 |
-
" x_filtered = x_fft * w_complex\n",
|
| 635 |
-
" x_spatial = tf.math.real(tf.signal.ifft2d(x_filtered))\n",
|
| 636 |
-
"\n",
|
| 637 |
-
" return tf.reshape(x_spatial, [batch, self.token_side * self.token_side, channels])\n",
|
| 638 |
-
"\n",
|
| 639 |
-
" def get_config(self):\n",
|
| 640 |
-
" cfg = super().get_config()\n",
|
| 641 |
-
" cfg.update({\"token_side\": self.token_side})\n",
|
| 642 |
-
" return cfg"
|
| 643 |
-
],
|
| 644 |
-
"id": "DzxtiRRMkXs5"
|
| 645 |
-
},
|
| 646 |
-
{
|
| 647 |
-
"cell_type": "markdown",
|
| 648 |
-
"metadata": {
|
| 649 |
-
"id": "rKGZtJkNkXs5"
|
| 650 |
-
},
|
| 651 |
-
"source": [
|
| 652 |
-
"## 4.4 \u2014 PatchEncoderWithCLS"
|
| 653 |
-
],
|
| 654 |
-
"id": "rKGZtJkNkXs5"
|
| 655 |
-
},
|
| 656 |
-
{
|
| 657 |
-
"cell_type": "code",
|
| 658 |
-
"execution_count": null,
|
| 659 |
-
"metadata": {
|
| 660 |
-
"id": "y7JZKKM_kXs5",
|
| 661 |
-
"executionInfo": {
|
| 662 |
-
"status": "ok",
|
| 663 |
-
"timestamp": 1780309410713,
|
| 664 |
-
"user_tz": -330,
|
| 665 |
-
"elapsed": 2,
|
| 666 |
-
"user": {
|
| 667 |
-
"displayName": "Randy Orton",
|
| 668 |
-
"userId": "11725427482974841050"
|
| 669 |
-
}
|
| 670 |
-
}
|
| 671 |
-
},
|
| 672 |
-
"outputs": [],
|
| 673 |
-
"source": [
|
| 674 |
-
"@tf.keras.utils.register_keras_serializable()\n",
|
| 675 |
-
"class PatchEncoderWithCLS(layers.Layer):\n",
|
| 676 |
-
" \"\"\"\n",
|
| 677 |
-
" Projects patches, prepends a learnable [CLS] token, and adds positional embeddings.\n",
|
| 678 |
-
"\n",
|
| 679 |
-
" Used by the ViT-UNet architecture; the CLS token aggregates global context\n",
|
| 680 |
-
" for the final classification head.\n",
|
| 681 |
-
" \"\"\"\n",
|
| 682 |
-
"\n",
|
| 683 |
-
" def __init__(self, num_patches, projection_dim, **kwargs):\n",
|
| 684 |
-
" super().__init__(**kwargs)\n",
|
| 685 |
-
" self.num_patches = num_patches\n",
|
| 686 |
-
" self.projection_dim = projection_dim\n",
|
| 687 |
-
" self.projection = layers.Dense(projection_dim)\n",
|
| 688 |
-
" self.position_embedding = layers.Embedding(\n",
|
| 689 |
-
" input_dim=num_patches + 1, output_dim=projection_dim\n",
|
| 690 |
-
" )\n",
|
| 691 |
-
"\n",
|
| 692 |
-
" def build(self, input_shape):\n",
|
| 693 |
-
" self.cls_token = self.add_weight(\n",
|
| 694 |
-
" name=\"cls_token\", shape=(1, 1, self.projection_dim),\n",
|
| 695 |
-
" initializer=\"zeros\", trainable=True\n",
|
| 696 |
-
" )\n",
|
| 697 |
-
" super().build(input_shape)\n",
|
| 698 |
-
"\n",
|
| 699 |
-
" def call(self, patches):\n",
|
| 700 |
-
" batch = tf.shape(patches)[0]\n",
|
| 701 |
-
" patch_proj = self.projection(patches)\n",
|
| 702 |
-
" cls_tokens = tf.repeat(self.cls_token, repeats=batch, axis=0) # broadcast CLS to batch\n",
|
| 703 |
-
" x = tf.concat([cls_tokens, patch_proj], axis=1)\n",
|
| 704 |
-
" positions = tf.range(start=0, limit=self.num_patches + 1, delta=1)\n",
|
| 705 |
-
" return x + self.position_embedding(positions)\n",
|
| 706 |
-
"\n",
|
| 707 |
-
" def get_config(self):\n",
|
| 708 |
-
" cfg = super().get_config()\n",
|
| 709 |
-
" cfg.update({\"num_patches\": self.num_patches, \"projection_dim\": self.projection_dim})\n",
|
| 710 |
-
" return cfg"
|
| 711 |
-
],
|
| 712 |
-
"id": "y7JZKKM_kXs5"
|
| 713 |
-
},
|
| 714 |
-
{
|
| 715 |
-
"cell_type": "markdown",
|
| 716 |
-
"metadata": {
|
| 717 |
-
"id": "-69wdDpDkXs5"
|
| 718 |
-
},
|
| 719 |
-
"source": [
|
| 720 |
-
"## 4.5 \u2014 Custom Objects Registry\n",
|
| 721 |
-
"\n",
|
| 722 |
-
"Collects all custom layer classes into a single dict passed to `tf.keras.models.load_model`."
|
| 723 |
-
],
|
| 724 |
-
"id": "-69wdDpDkXs5"
|
| 725 |
-
},
|
| 726 |
-
{
|
| 727 |
-
"cell_type": "code",
|
| 728 |
-
"execution_count": null,
|
| 729 |
-
"metadata": {
|
| 730 |
-
"id": "TqfqdpwrkXs5",
|
| 731 |
-
"executionInfo": {
|
| 732 |
-
"status": "ok",
|
| 733 |
-
"timestamp": 1780309410718,
|
| 734 |
-
"user_tz": -330,
|
| 735 |
-
"elapsed": 2,
|
| 736 |
-
"user": {
|
| 737 |
-
"displayName": "Randy Orton",
|
| 738 |
-
"userId": "11725427482974841050"
|
| 739 |
-
}
|
| 740 |
-
}
|
| 741 |
-
},
|
| 742 |
-
"outputs": [],
|
| 743 |
-
"source": [
|
| 744 |
-
"CUSTOM_OBJECTS = {\n",
|
| 745 |
-
" \"PatchExtractor\": PatchExtractor,\n",
|
| 746 |
-
" \"PatchPositionEncoder\": PatchPositionEncoder,\n",
|
| 747 |
-
" \"GlobalFilterLayer\": GlobalFilterLayer,\n",
|
| 748 |
-
" \"PatchEncoderWithCLS\": PatchEncoderWithCLS,\n",
|
| 749 |
-
"}"
|
| 750 |
-
],
|
| 751 |
-
"id": "TqfqdpwrkXs5"
|
| 752 |
-
},
|
| 753 |
-
{
|
| 754 |
-
"cell_type": "markdown",
|
| 755 |
-
"metadata": {
|
| 756 |
-
"id": "6hLXTpdokXs5"
|
| 757 |
-
},
|
| 758 |
-
"source": [
|
| 759 |
-
"# 5.0 \u2014 Core Ensemble Functions\n",
|
| 760 |
-
"\n",
|
| 761 |
-
"Functions for discovering saved ensemble checkpoints and running the CreDE\n",
|
| 762 |
-
"credal-bound computation across all ensemble members."
|
| 763 |
-
],
|
| 764 |
-
"id": "6hLXTpdokXs5"
|
| 765 |
-
},
|
| 766 |
-
{
|
| 767 |
-
"cell_type": "markdown",
|
| 768 |
-
"metadata": {
|
| 769 |
-
"id": "mtRX8AUxkXs5"
|
| 770 |
-
},
|
| 771 |
-
"source": [
|
| 772 |
-
"## 5.1 \u2014 Ensemble Path Discovery"
|
| 773 |
-
],
|
| 774 |
-
"id": "mtRX8AUxkXs5"
|
| 775 |
-
},
|
| 776 |
-
{
|
| 777 |
-
"cell_type": "code",
|
| 778 |
-
"execution_count": null,
|
| 779 |
-
"metadata": {
|
| 780 |
-
"id": "UJLC_qOFkXs5",
|
| 781 |
-
"executionInfo": {
|
| 782 |
-
"status": "ok",
|
| 783 |
-
"timestamp": 1780309410818,
|
| 784 |
-
"user_tz": -330,
|
| 785 |
-
"elapsed": 15,
|
| 786 |
-
"user": {
|
| 787 |
-
"displayName": "Randy Orton",
|
| 788 |
-
"userId": "11725427482974841050"
|
| 789 |
-
}
|
| 790 |
-
}
|
| 791 |
-
},
|
| 792 |
-
"outputs": [],
|
| 793 |
-
"source": [
|
| 794 |
-
"def get_ensemble_paths(model_name):\n",
|
| 795 |
-
" \"\"\"\n",
|
| 796 |
-
" Locate all saved `.keras` checkpoint files for a given architecture.\n",
|
| 797 |
-
"\n",
|
| 798 |
-
" Searches `MODEL_DIR/ensembles_<model_name>/` first, then falls back to\n",
|
| 799 |
-
" `MODEL_DIR/ensembles_old/` if the primary directory is empty.\n",
|
| 800 |
-
"\n",
|
| 801 |
-
" Parameters\n",
|
| 802 |
-
" ----------\n",
|
| 803 |
-
" model_name : str Architecture identifier (e.g. 'AlexNet_CNN').\n",
|
| 804 |
-
"\n",
|
| 805 |
-
" Returns\n",
|
| 806 |
-
" -------\n",
|
| 807 |
-
" list[str] Absolute paths to matching checkpoint files.\n",
|
| 808 |
-
" \"\"\"\n",
|
| 809 |
-
" primary_pattern = str(MODEL_DIR / f\"{model_name}_ens_*_final.keras\")\n",
|
| 810 |
-
" paths = glob.glob(primary_pattern)\n",
|
| 811 |
-
"\n",
|
| 812 |
-
" if not paths: # Fallback to legacy directory\n",
|
| 813 |
-
" fallback_pattern = str(MODEL_DIR / \"ensembles_old\" / f\"{model_name}_ens_*_final.keras\")\n",
|
| 814 |
-
" paths = glob.glob(fallback_pattern)\n",
|
| 815 |
-
"\n",
|
| 816 |
-
" return paths"
|
| 817 |
-
],
|
| 818 |
-
"id": "UJLC_qOFkXs5"
|
| 819 |
-
},
|
| 820 |
-
{
|
| 821 |
-
"cell_type": "markdown",
|
| 822 |
-
"metadata": {
|
| 823 |
-
"id": "CzNXiivkkXs5"
|
| 824 |
-
},
|
| 825 |
-
"source": [
|
| 826 |
-
"## 5.2 \u2014 Homogeneous Ensemble Evaluation (CreDE)\n",
|
| 827 |
-
"\n",
|
| 828 |
-
"Loads each ensemble member sequentially, predicts on the full scene,\n",
|
| 829 |
-
"then computes credal bounds (p_min / p_max) to derive aleatoric uncertainty (AU),\n",
|
| 830 |
-
"epistemic uncertainty (EU), and total uncertainty (TU).\n",
|
| 831 |
-
"Each model is deleted immediately after prediction to minimise peak RAM usage."
|
| 832 |
-
],
|
| 833 |
-
"id": "CzNXiivkkXs5"
|
| 834 |
-
},
|
| 835 |
-
{
|
| 836 |
-
"cell_type": "code",
|
| 837 |
-
"execution_count": null,
|
| 838 |
-
"metadata": {
|
| 839 |
-
"id": "DYfcyGmdkXs5",
|
| 840 |
-
"executionInfo": {
|
| 841 |
-
"status": "ok",
|
| 842 |
-
"timestamp": 1780309410822,
|
| 843 |
-
"user_tz": -330,
|
| 844 |
-
"elapsed": 2,
|
| 845 |
-
"user": {
|
| 846 |
-
"displayName": "Randy Orton",
|
| 847 |
-
"userId": "11725427482974841050"
|
| 848 |
-
}
|
| 849 |
-
}
|
| 850 |
-
},
|
| 851 |
-
"outputs": [],
|
| 852 |
-
"source": [
|
| 853 |
-
"def evaluate_homogeneous_ensemble(model_paths, input_data, batch_size=2048):\n",
|
| 854 |
-
" \"\"\"\n",
|
| 855 |
-
" Run CreDE inference over a list of ensemble checkpoints.\n",
|
| 856 |
-
"\n",
|
| 857 |
-
" For each model: predict \u2192 stack \u2192 compute credal bounds \u2192 derive uncertainty measures.\n",
|
| 858 |
-
"\n",
|
| 859 |
-
" Parameters\n",
|
| 860 |
-
" ----------\n",
|
| 861 |
-
" model_paths : list[str] Paths to `.keras` checkpoint files.\n",
|
| 862 |
-
" input_data : np.ndarray Full-scene patch array, shape (N, P, P, B).\n",
|
| 863 |
-
" batch_size : int Inference batch size (default 2048).\n",
|
| 864 |
-
"\n",
|
| 865 |
-
" Returns\n",
|
| 866 |
-
" -------\n",
|
| 867 |
-
" pred_class : np.ndarray Shape (N,) \u2014 argmax of credal mean probabilities.\n",
|
| 868 |
-
" p_star : np.ndarray Shape (N, C) \u2014 normalised lower credal probabilities.\n",
|
| 869 |
-
" au : np.ndarray Shape (N,) \u2014 aleatoric uncertainty (entropy of p_star).\n",
|
| 870 |
-
" eu : np.ndarray Shape (N,) \u2014 epistemic uncertainty (mean credal spread).\n",
|
| 871 |
-
" tu : np.ndarray Shape (N,) \u2014 total uncertainty (au + eu).\n",
|
| 872 |
-
" \"\"\"\n",
|
| 873 |
-
" all_preds = []\n",
|
| 874 |
-
"\n",
|
| 875 |
-
" for path in model_paths:\n",
|
| 876 |
-
" print(f\" -> Loading & predicting: {Path(path).name}\")\n",
|
| 877 |
-
" model = tf.keras.models.load_model(\n",
|
| 878 |
-
" path, compile=False, custom_objects=CUSTOM_OBJECTS, safe_mode=False\n",
|
| 879 |
-
" )\n",
|
| 880 |
-
" preds = model.predict(input_data, batch_size=batch_size, verbose=1)\n",
|
| 881 |
-
" all_preds.append(preds)\n",
|
| 882 |
-
"\n",
|
| 883 |
-
" # Delete model immediately to free RAM before loading the next member\n",
|
| 884 |
-
" del model\n",
|
| 885 |
-
" tf.keras.backend.clear_session()\n",
|
| 886 |
-
" gc.collect()\n",
|
| 887 |
-
"\n",
|
| 888 |
-
" print(\" -> Computing credal bounds...\")\n",
|
| 889 |
-
" stacked_preds = tf.stack(all_preds, axis=0) # (M, N, C)\n",
|
| 890 |
-
" p_min = tf.reduce_min(stacked_preds, axis=0)\n",
|
| 891 |
-
" p_max = tf.reduce_max(stacked_preds, axis=0)\n",
|
| 892 |
-
"\n",
|
| 893 |
-
" delta_p = p_max - p_min\n",
|
| 894 |
-
"\n",
|
| 895 |
-
" # Normalise lower credal probabilities to form p_star\n",
|
| 896 |
-
" p_star = p_min / (tf.reduce_sum(p_min, axis=-1, keepdims=True) + 1e-12)\n",
|
| 897 |
-
" p_star = np.clip(p_star, 1e-12, 1.0)\n",
|
| 898 |
-
"\n",
|
| 899 |
-
" # Uncertainty decomposition\n",
|
| 900 |
-
" au = -np.sum(p_star * np.log(p_star), axis=-1) # aleatoric: entropy\n",
|
| 901 |
-
" eu = np.mean(delta_p, axis=-1) # epistemic: mean credal spread\n",
|
| 902 |
-
" tu = au + eu # total uncertainty\n",
|
| 903 |
-
"\n",
|
| 904 |
-
" pred_class = np.argmax(p_star, axis=-1)\n",
|
| 905 |
-
"\n",
|
| 906 |
-
" # Coerce any TF tensors to numpy\n",
|
| 907 |
-
" to_np = lambda t: t.numpy() if hasattr(t, 'numpy') else t\n",
|
| 908 |
-
" return to_np(pred_class), to_np(p_star), to_np(au), to_np(eu), to_np(tu)"
|
| 909 |
-
],
|
| 910 |
-
"id": "DYfcyGmdkXs5"
|
| 911 |
-
},
|
| 912 |
-
{
|
| 913 |
-
"cell_type": "markdown",
|
| 914 |
-
"metadata": {
|
| 915 |
-
"id": "0_BcMa2UkXs5"
|
| 916 |
-
},
|
| 917 |
-
"source": [
|
| 918 |
-
"# 6.0 \u2014 Visualisation Engine\n",
|
| 919 |
-
"\n",
|
| 920 |
-
"Generates and saves a standardised 6-panel figure for each architecture:\n",
|
| 921 |
-
"base prediction map, binary certain/uncertain mask, pixel-count bar chart,\n",
|
| 922 |
-
"and three grey-overlay maps (AU, EU, TU)."
|
| 923 |
-
],
|
| 924 |
-
"id": "0_BcMa2UkXs5"
|
| 925 |
-
},
|
| 926 |
-
{
|
| 927 |
-
"cell_type": "markdown",
|
| 928 |
-
"metadata": {
|
| 929 |
-
"id": "lpwmFGLBkXs5"
|
| 930 |
-
},
|
| 931 |
-
"source": [
|
| 932 |
-
"## 6.1 \u2014 6-Panel Spatial Mapping Function"
|
| 933 |
-
],
|
| 934 |
-
"id": "lpwmFGLBkXs5"
|
| 935 |
-
},
|
| 936 |
-
{
|
| 937 |
-
"cell_type": "code",
|
| 938 |
-
"execution_count": null,
|
| 939 |
-
"metadata": {
|
| 940 |
-
"id": "CjzaWpWEkXs6",
|
| 941 |
-
"executionInfo": {
|
| 942 |
-
"status": "ok",
|
| 943 |
-
"timestamp": 1780309410825,
|
| 944 |
-
"user_tz": -330,
|
| 945 |
-
"elapsed": 1,
|
| 946 |
-
"user": {
|
| 947 |
-
"displayName": "Randy Orton",
|
| 948 |
-
"userId": "11725427482974841050"
|
| 949 |
-
}
|
| 950 |
-
}
|
| 951 |
-
},
|
| 952 |
-
"outputs": [],
|
| 953 |
-
"source": [
|
| 954 |
-
"# Shared colour palette \u2014 same bold, saturated base used across the other\n",
|
| 955 |
-
"# notebooks, auto-extended with additional bold shades (same style) if a\n",
|
| 956 |
-
"# future dataset has more than 10 classes.\n",
|
| 957 |
-
"import colorsys\n",
|
| 958 |
-
"\n",
|
| 959 |
-
"CLASS_COLOR_BASE = [\n",
|
| 960 |
-
" '#0000FF', '#00FF00', '#FF0000', '#00FFFF', '#FF00FF',\n",
|
| 961 |
-
" '#FFFF00', '#A52A2A', '#FFA500', '#7FFF00', '#8A2BE2'\n",
|
| 962 |
-
"]\n",
|
| 963 |
-
"UNCERTAIN_COLOR = '#808080'\n",
|
| 964 |
-
"\n",
|
| 965 |
-
"def _generate_extra_shades(n_extra, hue_offset=0.05, sat=0.85, val=0.95):\n",
|
| 966 |
-
" \"\"\"Bold, highly-saturated extra hues in the same style as CLASS_COLOR_BASE.\"\"\"\n",
|
| 967 |
-
" shades = []\n",
|
| 968 |
-
" for i in range(n_extra):\n",
|
| 969 |
-
" hue = (hue_offset + i / max(n_extra, 1)) % 1.0\n",
|
| 970 |
-
" r, g, b = colorsys.hsv_to_rgb(hue, sat, val)\n",
|
| 971 |
-
" shades.append('#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255)))\n",
|
| 972 |
-
" return shades\n",
|
| 973 |
-
"\n",
|
| 974 |
-
"def get_class_colors(n):\n",
|
| 975 |
-
" \"\"\"Return n distinct hex colours, extending the base 10 with additional bold\n",
|
| 976 |
-
" shades in the same saturated style if needed.\"\"\"\n",
|
| 977 |
-
" if n <= len(CLASS_COLOR_BASE):\n",
|
| 978 |
-
" return CLASS_COLOR_BASE[:n]\n",
|
| 979 |
-
" return CLASS_COLOR_BASE + _generate_extra_shades(n - len(CLASS_COLOR_BASE))\n",
|
| 980 |
-
"\n",
|
| 981 |
-
"def resolve_uncertain_color(class_colors, preferred=UNCERTAIN_COLOR, min_dist=60):\n",
|
| 982 |
-
" \"\"\"Return `preferred` unless it visually clashes with any class colour, in\n",
|
| 983 |
-
" which case fall back to a distinct dark slate so 'Uncertain' never collides\n",
|
| 984 |
-
" with a real class colour.\"\"\"\n",
|
| 985 |
-
" def _hex_to_rgb(h):\n",
|
| 986 |
-
" h = h.lstrip('#')\n",
|
| 987 |
-
" return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))\n",
|
| 988 |
-
" pr, pg, pb = _hex_to_rgb(preferred)\n",
|
| 989 |
-
" for c in class_colors:\n",
|
| 990 |
-
" cr, cg, cb = _hex_to_rgb(c)\n",
|
| 991 |
-
" dist = ((pr - cr) ** 2 + (pg - cg) ** 2 + (pb - cb) ** 2) ** 0.5\n",
|
| 992 |
-
" if dist < min_dist:\n",
|
| 993 |
-
" return '#2F2F2F'\n",
|
| 994 |
-
" return preferred\n",
|
| 995 |
-
"\n",
|
| 996 |
-
"def generate_spatial_crede_maps(\n",
|
| 997 |
-
" model_name, pred_class_scene, p_star_scene, au_scene, eu_scene, tu_scene,\n",
|
| 998 |
-
" H, W, au_thresh=0.5, eu_thresh=0.2, tu_thresh=0.7\n",
|
| 999 |
-
"):\n",
|
| 1000 |
-
" # H, W are always supplied explicitly by the caller (scene geometry is now\n",
|
| 1001 |
-
" # inferred from the .mat cube, so no dataset-specific defaults are baked in here)\n",
|
| 1002 |
-
" \"\"\"Produce and save a 3x4 spatial uncertainty figure for CreDE.\"\"\"\n",
|
| 1003 |
-
" print(f' -> Generating 3x4 spatial maps for {model_name}...')\n",
|
| 1004 |
-
" n_cls = p_star_scene.shape[-1]\n",
|
| 1005 |
-
"\n",
|
| 1006 |
-
" au_map = au_scene.reshape((H, W))\n",
|
| 1007 |
-
" eu_map = eu_scene.reshape((H, W))\n",
|
| 1008 |
-
" tu_map = tu_scene.reshape((H, W))\n",
|
| 1009 |
-
" pred_map = pred_class_scene.reshape((H, W))\n",
|
| 1010 |
-
"\n",
|
| 1011 |
-
" au_mask = (au_map > au_thresh).astype(int)\n",
|
| 1012 |
-
" eu_mask = (eu_map > eu_thresh).astype(int)\n",
|
| 1013 |
-
" tu_mask = (tu_map > tu_thresh).astype(int)\n",
|
| 1014 |
-
"\n",
|
| 1015 |
-
" combined_au = np.where(au_mask == 1, n_cls, pred_map)\n",
|
| 1016 |
-
" combined_eu = np.where(eu_mask == 1, n_cls, pred_map)\n",
|
| 1017 |
-
" combined_tu = np.where(tu_mask == 1, n_cls, pred_map)\n",
|
| 1018 |
-
"\n",
|
| 1019 |
-
" class_colors = get_class_colors(n_cls)\n",
|
| 1020 |
-
" uncertain_color = resolve_uncertain_color(class_colors)\n",
|
| 1021 |
-
" cmap_base = ListedColormap(class_colors)\n",
|
| 1022 |
-
" cmap_unc = ListedColormap(class_colors + [uncertain_color])\n",
|
| 1023 |
-
" cmap_binary = ListedColormap(['#FFFF00', '#001F3F'])\n",
|
| 1024 |
-
"\n",
|
| 1025 |
-
" bar_lbls = [f'Class {i}' for i in range(n_cls)] + ['Uncertain']\n",
|
| 1026 |
-
" bar_cols = class_colors + [uncertain_color]\n",
|
| 1027 |
-
"\n",
|
| 1028 |
-
" fig, axes = plt.subplots(3, 4, figsize=(38, 26))\n",
|
| 1029 |
-
" fig.suptitle(f'{model_name} \u2014 CreDE Uncertainty Maps (Absolute Thresholds)',\n",
|
| 1030 |
-
" fontsize=24, fontweight='bold', y=0.99)\n",
|
| 1031 |
-
"\n",
|
| 1032 |
-
" # \u2500\u2500 Row 0: Base prediction + 3 binary maps\n",
|
| 1033 |
-
" axes[0, 0].imshow(pred_map, cmap=cmap_base, vmin=0, vmax=n_cls - 1)\n",
|
| 1034 |
-
" axes[0, 0].set_title('Base Prediction Map', fontsize=15)\n",
|
| 1035 |
-
" axes[0, 0].axis('off')\n",
|
| 1036 |
-
"\n",
|
| 1037 |
-
" binary_specs = [\n",
|
| 1038 |
-
" (axes[0, 1], au_mask, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1039 |
-
" (axes[0, 2], eu_mask, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1040 |
-
" (axes[0, 3], tu_mask, f'Total (TU > {tu_thresh})'),\n",
|
| 1041 |
-
" ]\n",
|
| 1042 |
-
" for ax, mask, label in binary_specs:\n",
|
| 1043 |
-
" ax.imshow(mask, cmap=cmap_binary, vmin=0, vmax=1)\n",
|
| 1044 |
-
" ax.set_title(f'Certain vs Uncertain\\n{label}', fontsize=15, pad=10)\n",
|
| 1045 |
-
" ax.axis('off')\n",
|
| 1046 |
-
" ax.legend(\n",
|
| 1047 |
-
" handles=[Patch(facecolor='#FFFF00', label='Certain'),\n",
|
| 1048 |
-
" Patch(facecolor='#001F3F', label='Uncertain')],\n",
|
| 1049 |
-
" loc='upper left', bbox_to_anchor=(0.0, -0.02), borderaxespad=0,\n",
|
| 1050 |
-
" fontsize=11, framealpha=0.9, ncol=2\n",
|
| 1051 |
-
" )\n",
|
| 1052 |
-
"\n",
|
| 1053 |
-
" # \u2500\u2500 Row 1: Grey overlay maps (AU, EU, TU) + blank\n",
|
| 1054 |
-
" overlay_specs = [\n",
|
| 1055 |
-
" (axes[1, 0], combined_au, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1056 |
-
" (axes[1, 1], combined_eu, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1057 |
-
" (axes[1, 2], combined_tu, f'Total (TU > {tu_thresh})'),\n",
|
| 1058 |
-
" ]\n",
|
| 1059 |
-
" for ax, combined, label in overlay_specs:\n",
|
| 1060 |
-
" ax.imshow(combined, cmap=cmap_unc, vmin=0, vmax=n_cls)\n",
|
| 1061 |
-
" ax.set_title(f'Grey Overlay \u2014 {label}', fontsize=15, pad=10)\n",
|
| 1062 |
-
" ax.axis('off')\n",
|
| 1063 |
-
"\n",
|
| 1064 |
-
" axes[1, 3].axis('off')\n",
|
| 1065 |
-
"\n",
|
| 1066 |
-
" # \u2500\u2500 Row 2: Bar charts (AU, EU, TU) + blank\n",
|
| 1067 |
-
" bar_specs = [\n",
|
| 1068 |
-
" (axes[2, 0], combined_au, f'Aleatoric (AU > {au_thresh})'),\n",
|
| 1069 |
-
" (axes[2, 1], combined_eu, f'Epistemic (EU > {eu_thresh})'),\n",
|
| 1070 |
-
" (axes[2, 2], combined_tu, f'Total (TU > {tu_thresh})'),\n",
|
| 1071 |
-
" ]\n",
|
| 1072 |
-
" for ax, combined, label in bar_specs:\n",
|
| 1073 |
-
" uniq, cnt = np.unique(combined, return_counts=True)\n",
|
| 1074 |
-
" c_dict = {int(k): int(v) for k, v in zip(uniq, cnt)}\n",
|
| 1075 |
-
" bar_vals = [c_dict.get(i, 0) for i in range(n_cls + 1)]\n",
|
| 1076 |
-
" ax.bar(bar_lbls, bar_vals, color=bar_cols, edgecolor='black')\n",
|
| 1077 |
-
" ax.set_title(f'Pixel Counts \u2014 {label}', fontsize=15, pad=10)\n",
|
| 1078 |
-
" ax.tick_params(axis='x', rotation=45, labelsize=11)\n",
|
| 1079 |
-
" ax.set_ylabel('Pixel Count', fontsize=12)\n",
|
| 1080 |
-
" max_val = max(bar_vals, default=1)\n",
|
| 1081 |
-
" for i, v in enumerate(bar_vals):\n",
|
| 1082 |
-
" ax.text(i, v + max_val * 0.01,\n",
|
| 1083 |
-
" f'{v:,}', ha='center', va='bottom', fontweight='bold', fontsize=9)\n",
|
| 1084 |
-
" ax.set_ylim(0, max_val * 1.12)\n",
|
| 1085 |
-
"\n",
|
| 1086 |
-
" axes[2, 3].axis('off')\n",
|
| 1087 |
-
"\n",
|
| 1088 |
-
" plt.tight_layout(rect=[0, 0.03, 1, 0.98])\n",
|
| 1089 |
-
" save_path = CREDE_OUT_DIR / f'{model_name}_CreDE_spatial_maps.png'\n",
|
| 1090 |
-
" plt.savefig(save_path, dpi=300, bbox_inches='tight')\n",
|
| 1091 |
-
" plt.show()\n",
|
| 1092 |
-
" print(f' -> Saved: {save_path}')\n",
|
| 1093 |
-
" return str(save_path)"
|
| 1094 |
-
],
|
| 1095 |
-
"id": "CjzaWpWEkXs6"
|
| 1096 |
-
},
|
| 1097 |
-
{
|
| 1098 |
-
"cell_type": "markdown",
|
| 1099 |
-
"metadata": {
|
| 1100 |
-
"id": "zOIG2k38kXs6"
|
| 1101 |
-
},
|
| 1102 |
-
"source": [
|
| 1103 |
-
"# 7.0 \u2014 Main Execution\n",
|
| 1104 |
-
"\n",
|
| 1105 |
-
"Iterates over each target architecture, runs CreDE inference, generates spatial maps,\n",
|
| 1106 |
-
"and accumulates per-architecture metrics into a master summary CSV.\n",
|
| 1107 |
-
"RAM is wiped aggressively after every architecture to prevent OOM errors."
|
| 1108 |
-
],
|
| 1109 |
-
"id": "zOIG2k38kXs6"
|
| 1110 |
-
},
|
| 1111 |
-
{
|
| 1112 |
-
"cell_type": "code",
|
| 1113 |
-
"source": [
|
| 1114 |
-
"# \u2500\u2500 Excel Export Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1115 |
-
"_HDR_FILL = PatternFill('solid', start_color='1F4E79')\n",
|
| 1116 |
-
"_ALT_FILL = PatternFill('solid', start_color='D6E4F0')\n",
|
| 1117 |
-
"_HDR_FONT = Font(name='Arial', bold=True, color='FFFFFF', size=11)\n",
|
| 1118 |
-
"_BODY_FONT = Font(name='Arial', size=10)\n",
|
| 1119 |
-
"_TITLE_FONT = Font(name='Arial', bold=True, size=13)\n",
|
| 1120 |
-
"_CENTER = Alignment(horizontal='center', vertical='center', wrap_text=True)\n",
|
| 1121 |
-
"_LEFT = Alignment(horizontal='left', vertical='center')\n",
|
| 1122 |
-
"_THIN_SIDE = Side(style='thin', color='AAAAAA')\n",
|
| 1123 |
-
"_THIN_BORDER= Border(left=_THIN_SIDE, right=_THIN_SIDE,\n",
|
| 1124 |
-
" top=_THIN_SIDE, bottom=_THIN_SIDE)\n",
|
| 1125 |
-
"\n",
|
| 1126 |
-
"def _style_header_row(ws, row, col_start, col_end):\n",
|
| 1127 |
-
" for c in range(col_start, col_end + 1):\n",
|
| 1128 |
-
" cell = ws.cell(row=row, column=c)\n",
|
| 1129 |
-
" cell.fill = _HDR_FILL\n",
|
| 1130 |
-
" cell.font = _HDR_FONT\n",
|
| 1131 |
-
" cell.alignment = _CENTER\n",
|
| 1132 |
-
" cell.border = _THIN_BORDER\n",
|
| 1133 |
-
"\n",
|
| 1134 |
-
"def _style_data_rows(ws, row_start, row_end, col_start, col_end):\n",
|
| 1135 |
-
" for r in range(row_start, row_end + 1):\n",
|
| 1136 |
-
" fill = _ALT_FILL if r % 2 == 0 else PatternFill()\n",
|
| 1137 |
-
" for c in range(col_start, col_end + 1):\n",
|
| 1138 |
-
" cell = ws.cell(row=r, column=c)\n",
|
| 1139 |
-
" cell.font = _BODY_FONT\n",
|
| 1140 |
-
" cell.fill = fill\n",
|
| 1141 |
-
" cell.alignment = _LEFT\n",
|
| 1142 |
-
" cell.border = _THIN_BORDER\n",
|
| 1143 |
-
"\n",
|
| 1144 |
-
"def _write_df_to_sheet(ws, df, start_row=1, start_col=1, title=None):\n",
|
| 1145 |
-
" r = start_row\n",
|
| 1146 |
-
" if title:\n",
|
| 1147 |
-
" cell = ws.cell(row=r, column=start_col, value=title)\n",
|
| 1148 |
-
" cell.font = _TITLE_FONT\n",
|
| 1149 |
-
" cell.alignment = _LEFT\n",
|
| 1150 |
-
" ws.merge_cells(start_row=r, start_column=start_col,\n",
|
| 1151 |
-
" end_row=r, end_column=start_col + len(df.columns) - 1)\n",
|
| 1152 |
-
" r += 1\n",
|
| 1153 |
-
"\n",
|
| 1154 |
-
" for j, col_name in enumerate(df.columns, start=start_col):\n",
|
| 1155 |
-
" ws.cell(row=r, column=j, value=col_name)\n",
|
| 1156 |
-
" _style_header_row(ws, r, start_col, start_col + len(df.columns) - 1)\n",
|
| 1157 |
-
" r += 1\n",
|
| 1158 |
-
"\n",
|
| 1159 |
-
" data_start = r\n",
|
| 1160 |
-
" for _, row_data in df.iterrows():\n",
|
| 1161 |
-
" for j, val in enumerate(row_data, start=start_col):\n",
|
| 1162 |
-
" ws.cell(row=r, column=j, value=round(float(val), 6)\n",
|
| 1163 |
-
" if isinstance(val, (float, np.floating)) else val)\n",
|
| 1164 |
-
" r += 1\n",
|
| 1165 |
-
" _style_data_rows(ws, data_start, r - 1, start_col, start_col + len(df.columns) - 1)\n",
|
| 1166 |
-
"\n",
|
| 1167 |
-
" for j, col_name in enumerate(df.columns, start=start_col):\n",
|
| 1168 |
-
" max_len = max(len(str(col_name)),\n",
|
| 1169 |
-
" max((len(str(ws.cell(row=row, column=j).value or ''))\n",
|
| 1170 |
-
" for row in range(data_start, r)), default=0))\n",
|
| 1171 |
-
" ws.column_dimensions[get_column_letter(j)].width = min(max_len + 4, 40)\n",
|
| 1172 |
-
" return r\n",
|
| 1173 |
-
"\n",
|
| 1174 |
-
"def create_crede_excel_report(out_dir, summary_df, plot_paths):\n",
|
| 1175 |
-
" \"\"\"Auto-creates CreDE_Results.xlsx with Summary and Plots.\"\"\"\n",
|
| 1176 |
-
" xlsx_path = Path(out_dir) / 'CreDE_Results.xlsx'\n",
|
| 1177 |
-
" wb = Workbook()\n",
|
| 1178 |
-
"\n",
|
| 1179 |
-
" # \u2500\u2500 Sheet 1: Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1180 |
-
" ws1 = wb.active\n",
|
| 1181 |
-
" ws1.title = 'CreDE Summary'\n",
|
| 1182 |
-
" ws1.row_dimensions[1].height = 30\n",
|
| 1183 |
-
" _write_df_to_sheet(ws1, summary_df, start_row=1, title='CreDE \u2014 Inference Summary')\n",
|
| 1184 |
-
"\n",
|
| 1185 |
-
" # \u2500\u2500 Sheet 2: Plots \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n",
|
| 1186 |
-
" if plot_paths:\n",
|
| 1187 |
-
" ws2 = wb.create_sheet('Plots')\n",
|
| 1188 |
-
" title_cell = ws2.cell(row=1, column=1, value='CreDE \u2014 Spatial Uncertainty Maps')\n",
|
| 1189 |
-
" title_cell.font = Font(name='Arial', bold=True, size=14)\n",
|
| 1190 |
-
"\n",
|
| 1191 |
-
" img_row = 3\n",
|
| 1192 |
-
" for label, img_path in plot_paths:\n",
|
| 1193 |
-
" if not Path(img_path).exists():\n",
|
| 1194 |
-
" continue\n",
|
| 1195 |
-
" ws2.cell(row=img_row, column=1, value=label).font = Font(name='Arial', bold=True, size=11)\n",
|
| 1196 |
-
" img_row += 1\n",
|
| 1197 |
-
"\n",
|
| 1198 |
-
" xl_img = XLImage(img_path)\n",
|
| 1199 |
-
" orig_w, orig_h = xl_img.width, xl_img.height\n",
|
| 1200 |
-
" target_w = 900\n",
|
| 1201 |
-
" scale = target_w / orig_w if orig_w > 0 else 1\n",
|
| 1202 |
-
" xl_img.width = int(orig_w * scale)\n",
|
| 1203 |
-
" xl_img.height = int(orig_h * scale)\n",
|
| 1204 |
-
"\n",
|
| 1205 |
-
" ws2.add_image(xl_img, f'A{img_row}')\n",
|
| 1206 |
-
" ws2.row_dimensions[img_row].height = xl_img.height * 0.75\n",
|
| 1207 |
-
" img_row += int(xl_img.height / 15) + 3\n",
|
| 1208 |
-
"\n",
|
| 1209 |
-
" wb.save(xlsx_path)\n",
|
| 1210 |
-
" print(f'\\n\u2705 Excel report saved \u2192 {xlsx_path}')\n",
|
| 1211 |
-
" return str(xlsx_path)"
|
| 1212 |
-
],
|
| 1213 |
-
"metadata": {
|
| 1214 |
-
"id": "VCAvzVzai2Cx",
|
| 1215 |
-
"executionInfo": {
|
| 1216 |
-
"status": "ok",
|
| 1217 |
-
"timestamp": 1780309410855,
|
| 1218 |
-
"user_tz": -330,
|
| 1219 |
-
"elapsed": 28,
|
| 1220 |
-
"user": {
|
| 1221 |
-
"displayName": "Randy Orton",
|
| 1222 |
-
"userId": "11725427482974841050"
|
| 1223 |
-
}
|
| 1224 |
-
}
|
| 1225 |
-
},
|
| 1226 |
-
"id": "VCAvzVzai2Cx",
|
| 1227 |
-
"execution_count": null,
|
| 1228 |
-
"outputs": []
|
| 1229 |
-
},
|
| 1230 |
-
{
|
| 1231 |
-
"cell_type": "code",
|
| 1232 |
-
"source": [
|
| 1233 |
-
"# -----------------------------\n",
|
| 1234 |
-
"# Metric Helpers\n",
|
| 1235 |
-
"# -----------------------------\n",
|
| 1236 |
-
"def multiclass_brier_score(y_onehot, y_prob):\n",
|
| 1237 |
-
" return float(np.mean(np.sum((y_prob - y_onehot) ** 2, axis=1)))\n",
|
| 1238 |
-
"\n",
|
| 1239 |
-
"def expected_calibration_error(y_true, y_prob, n_bins=15):\n",
|
| 1240 |
-
" confidences, predictions = np.max(y_prob, axis=1), np.argmax(y_prob, axis=1)\n",
|
| 1241 |
-
" correct = (predictions == y_true).astype(np.float32)\n",
|
| 1242 |
-
" bin_edges = np.linspace(0.0, 1.0, n_bins + 1)\n",
|
| 1243 |
-
" ece = 0.0\n",
|
| 1244 |
-
" for i in range(n_bins):\n",
|
| 1245 |
-
" in_bin = (confidences >= bin_edges[i]) & (confidences <= bin_edges[i+1] if i == n_bins - 1 else confidences < bin_edges[i+1])\n",
|
| 1246 |
-
" prop = np.mean(in_bin)\n",
|
| 1247 |
-
" if prop > 0:\n",
|
| 1248 |
-
" ece += np.abs(np.mean(correct[in_bin]) - np.mean(confidences[in_bin])) * prop\n",
|
| 1249 |
-
" return float(ece)"
|
| 1250 |
-
],
|
| 1251 |
-
"metadata": {
|
| 1252 |
-
"id": "WJp12J3ee27I",
|
| 1253 |
-
"executionInfo": {
|
| 1254 |
-
"status": "ok",
|
| 1255 |
-
"timestamp": 1780309410869,
|
| 1256 |
-
"user_tz": -330,
|
| 1257 |
-
"elapsed": 30,
|
| 1258 |
-
"user": {
|
| 1259 |
-
"displayName": "Randy Orton",
|
| 1260 |
-
"userId": "11725427482974841050"
|
| 1261 |
-
}
|
| 1262 |
-
}
|
| 1263 |
-
},
|
| 1264 |
-
"id": "WJp12J3ee27I",
|
| 1265 |
-
"execution_count": null,
|
| 1266 |
-
"outputs": []
|
| 1267 |
-
},
|
| 1268 |
-
{
|
| 1269 |
-
"cell_type": "markdown",
|
| 1270 |
-
"metadata": {
|
| 1271 |
-
"id": "Sfygsyj4kXs6"
|
| 1272 |
-
},
|
| 1273 |
-
"source": [
|
| 1274 |
-
"## 7.1 \u2014 Master Evaluation Loop & Summary Export"
|
| 1275 |
-
],
|
| 1276 |
-
"id": "Sfygsyj4kXs6"
|
| 1277 |
-
},
|
| 1278 |
-
{
|
| 1279 |
-
"cell_type": "code",
|
| 1280 |
-
"execution_count": null,
|
| 1281 |
-
"metadata": {
|
| 1282 |
-
"colab": {
|
| 1283 |
-
"base_uri": "https://localhost:8080/",
|
| 1284 |
-
"height": 0,
|
| 1285 |
-
"output_embedded_package_id": "1mLGEUJR_9XlYIqk_TDYaleXtM8GDiIxB"
|
| 1286 |
-
},
|
| 1287 |
-
"id": "GLzhR-fskXs6",
|
| 1288 |
-
"executionInfo": {
|
| 1289 |
-
"status": "ok",
|
| 1290 |
-
"timestamp": 1780309930687,
|
| 1291 |
-
"user_tz": -330,
|
| 1292 |
-
"elapsed": 519817,
|
| 1293 |
-
"user": {
|
| 1294 |
-
"displayName": "Randy Orton",
|
| 1295 |
-
"userId": "11725427482974841050"
|
| 1296 |
-
}
|
| 1297 |
-
},
|
| 1298 |
-
"outputId": "4b8fbfd4-23d0-4e9d-a6d6-9b6f380d6393"
|
| 1299 |
-
},
|
| 1300 |
-
"outputs": [],
|
| 1301 |
-
"source": [
|
| 1302 |
-
"master_results = []\n",
|
| 1303 |
-
"plot_entries = []\n",
|
| 1304 |
-
"\n",
|
| 1305 |
-
"# Assuming you want to loop over these architectures (edit if you have more)\n",
|
| 1306 |
-
"architectures = ['AlexNet_CNN', 'GFNet', 'ViT_UNet']\n",
|
| 1307 |
-
"\n",
|
| 1308 |
-
"for model_name in architectures:\n",
|
| 1309 |
-
" print(f\"\\n{'='*60}\\n Evaluating CreDE: {model_name}\\n{'='*60}\")\n",
|
| 1310 |
-
"\n",
|
| 1311 |
-
" ensemble_paths = get_ensemble_paths(model_name)\n",
|
| 1312 |
-
" if not ensemble_paths:\n",
|
| 1313 |
-
" print(f\" -> No ensemble models found for {model_name}. Skipping.\")\n",
|
| 1314 |
-
" continue\n",
|
| 1315 |
-
"\n",
|
| 1316 |
-
" # Step 1: Run homogenous ensemble evaluation\n",
|
| 1317 |
-
" pred_class, p_star, au, eu, tu = evaluate_homogeneous_ensemble(\n",
|
| 1318 |
-
" ensemble_paths, scene_pixels_scaled, batch_size=2048\n",
|
| 1319 |
-
" )\n",
|
| 1320 |
-
"\n",
|
| 1321 |
-
" # Step 2: Generate 3x4 spatial maps\n",
|
| 1322 |
-
" saved_plot_path = generate_spatial_crede_maps(\n",
|
| 1323 |
-
" model_name, pred_class, p_star, au, eu, tu,\n",
|
| 1324 |
-
" H=H, W=W, au_thresh=AU_THRESH, eu_thresh=EU_THRESH, tu_thresh=TU_THRESH\n",
|
| 1325 |
-
" )\n",
|
| 1326 |
-
" plot_entries.append((f'{model_name} \u2014 Spatial Uncertainty Maps', saved_plot_path))\n",
|
| 1327 |
-
"\n",
|
| 1328 |
-
" # Step 3: Accumulate summary metrics\n",
|
| 1329 |
-
" unique, counts = np.unique(pred_class, return_counts=True)\n",
|
| 1330 |
-
" pixel_counts = dict(zip(unique, counts))\n",
|
| 1331 |
-
"\n",
|
| 1332 |
-
" master_results.append({\n",
|
| 1333 |
-
" \"Model\": f\"{model_name}_CreDE\",\n",
|
| 1334 |
-
" \"Mean_AU\": float(np.mean(au)),\n",
|
| 1335 |
-
" \"Mean_EU\": float(np.mean(eu)),\n",
|
| 1336 |
-
" \"Mean_TU\": float(np.mean(tu)),\n",
|
| 1337 |
-
" **{f\"Class_{int(k)}_Pixels\": int(v) for k, v in pixel_counts.items()}\n",
|
| 1338 |
-
" })\n",
|
| 1339 |
-
"\n",
|
| 1340 |
-
" # Step 4: Aggressively free RAM before next architecture\n",
|
| 1341 |
-
" del pred_class, p_star, au, eu, tu\n",
|
| 1342 |
-
" tf.keras.backend.clear_session()\n",
|
| 1343 |
-
" gc.collect()\n",
|
| 1344 |
-
"\n",
|
| 1345 |
-
"# --- Export master summary & Excel Report ---\n",
|
| 1346 |
-
"if master_results:\n",
|
| 1347 |
-
" df_summary = pd.DataFrame(master_results)\n",
|
| 1348 |
-
"\n",
|
| 1349 |
-
" # Save CSV\n",
|
| 1350 |
-
" csv_path = CREDE_OUT_DIR / \"CreDE_Master_Summary.csv\"\n",
|
| 1351 |
-
" df_summary.to_csv(csv_path, index=False)\n",
|
| 1352 |
-
" print(f\"\\nSaved CSV summary \u2192 {csv_path}\")\n",
|
| 1353 |
-
" print(df_summary.to_string(index=False))\n",
|
| 1354 |
-
"\n",
|
| 1355 |
-
" # Save Excel Report\n",
|
| 1356 |
-
" create_crede_excel_report(\n",
|
| 1357 |
-
" out_dir=CREDE_OUT_DIR,\n",
|
| 1358 |
-
" summary_df=df_summary,\n",
|
| 1359 |
-
" plot_paths=plot_entries\n",
|
| 1360 |
-
" )"
|
| 1361 |
-
],
|
| 1362 |
-
"id": "GLzhR-fskXs6"
|
| 1363 |
-
}
|
| 1364 |
-
]
|
| 1365 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/AlexNet_CNN_CreDE_spatial_maps.png
DELETED
Git LFS Details
|
ensemble/results/AlexNet_CNN_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 0.9982363315696648,
|
| 16 |
-
"recall": 1.0,
|
| 17 |
-
"f1-score": 0.999117387466902,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 0.9992671308171491,
|
| 23 |
-
"f1-score": 0.999633431085044,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 0.9981167608286252,
|
| 28 |
-
"recall": 1.0,
|
| 29 |
-
"f1-score": 0.9990574929311969,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 0.9995276334435522,
|
| 34 |
-
"recall": 0.9985842378480415,
|
| 35 |
-
"f1-score": 0.9990557129367328,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 1.0,
|
| 40 |
-
"recall": 0.9924812030075187,
|
| 41 |
-
"f1-score": 0.9962264150943396,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 0.9984399375975039,
|
| 46 |
-
"recall": 0.9984399375975039,
|
| 47 |
-
"f1-score": 0.9984399375975039,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 0.9993320469051507,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 0.9992900829299183,
|
| 53 |
-
"recall": 0.9985965636587766,
|
| 54 |
-
"f1-score": 0.9989412971389648,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 0.9993329462943703,
|
| 59 |
-
"recall": 0.9993320469051507,
|
| 60 |
-
"f1-score": 0.9993317721236556,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/AlexNet_CNN_training_curves.png
DELETED
Git LFS Details
|
ensemble/results/CreDE_Master_Summary.csv
DELETED
|
@@ -1,4 +0,0 @@
|
|
| 1 |
-
Model,Mean_AU,Mean_EU,Mean_TU,Class_0_Pixels,Class_1_Pixels,Class_2_Pixels,Class_3_Pixels,Class_4_Pixels,Class_5_Pixels,Class_6_Pixels,Class_7_Pixels
|
| 2 |
-
AlexNet_CNN_CreDE,0.06636760383844376,0.10358242690563202,0.16995003819465637,11053,7894,8013,52201,93056,49752,10015,30160
|
| 3 |
-
GFNet_CreDE,0.25558435916900635,0.05023321136832237,0.30581754446029663,11466,7519,9322,50468,94312,48723,9697,30637
|
| 4 |
-
ViT_UNet_CreDE,0.6706158518791199,0.08109056949615479,0.7517063617706299,11598,7868,10433,47332,94079,48711,13606,28517
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/CreDE_Results.xlsx
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:79b093f471f87dca87033b0b31ee89becfebdc0b39d74efd274c88aa5502aa94
|
| 3 |
-
size 2463242
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/GFNet_CreDE_spatial_maps.png
DELETED
Git LFS Details
|
ensemble/results/GFNet_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 1.0,
|
| 16 |
-
"recall": 1.0,
|
| 17 |
-
"f1-score": 1.0,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 1.0,
|
| 23 |
-
"f1-score": 1.0,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 1.0,
|
| 28 |
-
"recall": 1.0,
|
| 29 |
-
"f1-score": 1.0,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 1.0,
|
| 34 |
-
"recall": 1.0,
|
| 35 |
-
"f1-score": 1.0,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 1.0,
|
| 40 |
-
"recall": 1.0,
|
| 41 |
-
"f1-score": 1.0,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 1.0,
|
| 46 |
-
"recall": 1.0,
|
| 47 |
-
"f1-score": 1.0,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 1.0,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 1.0,
|
| 53 |
-
"recall": 1.0,
|
| 54 |
-
"f1-score": 1.0,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 1.0,
|
| 59 |
-
"recall": 1.0,
|
| 60 |
-
"f1-score": 1.0,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/GFNet_training_curves.png
DELETED
Git LFS Details
|
ensemble/results/ViT_UNet_CreDE_spatial_maps.png
DELETED
Git LFS Details
|
ensemble/results/ViT_UNet_classification_report.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"0": {
|
| 3 |
-
"precision": 1.0,
|
| 4 |
-
"recall": 1.0,
|
| 5 |
-
"f1-score": 1.0,
|
| 6 |
-
"support": 2432.0
|
| 7 |
-
},
|
| 8 |
-
"1": {
|
| 9 |
-
"precision": 1.0,
|
| 10 |
-
"recall": 1.0,
|
| 11 |
-
"f1-score": 1.0,
|
| 12 |
-
"support": 1408.0
|
| 13 |
-
},
|
| 14 |
-
"2": {
|
| 15 |
-
"precision": 0.9982300884955753,
|
| 16 |
-
"recall": 0.9964664310954063,
|
| 17 |
-
"f1-score": 0.9973474801061007,
|
| 18 |
-
"support": 566.0
|
| 19 |
-
},
|
| 20 |
-
"3": {
|
| 21 |
-
"precision": 1.0,
|
| 22 |
-
"recall": 0.9996335654085746,
|
| 23 |
-
"f1-score": 0.9998167491295583,
|
| 24 |
-
"support": 2729.0
|
| 25 |
-
},
|
| 26 |
-
"4": {
|
| 27 |
-
"precision": 0.9996856334486011,
|
| 28 |
-
"recall": 1.0,
|
| 29 |
-
"f1-score": 0.9998427920138343,
|
| 30 |
-
"support": 3180.0
|
| 31 |
-
},
|
| 32 |
-
"5": {
|
| 33 |
-
"precision": 1.0,
|
| 34 |
-
"recall": 0.9995280792826805,
|
| 35 |
-
"f1-score": 0.9997639839509087,
|
| 36 |
-
"support": 2119.0
|
| 37 |
-
},
|
| 38 |
-
"6": {
|
| 39 |
-
"precision": 0.9975,
|
| 40 |
-
"recall": 1.0,
|
| 41 |
-
"f1-score": 0.9987484355444305,
|
| 42 |
-
"support": 399.0
|
| 43 |
-
},
|
| 44 |
-
"7": {
|
| 45 |
-
"precision": 0.9968847352024922,
|
| 46 |
-
"recall": 0.9984399375975039,
|
| 47 |
-
"f1-score": 0.9976617303195635,
|
| 48 |
-
"support": 641.0
|
| 49 |
-
},
|
| 50 |
-
"accuracy": 0.9996289149473059,
|
| 51 |
-
"macro avg": {
|
| 52 |
-
"precision": 0.9990375571433336,
|
| 53 |
-
"recall": 0.9992585016730207,
|
| 54 |
-
"f1-score": 0.9991476463830495,
|
| 55 |
-
"support": 13474.0
|
| 56 |
-
},
|
| 57 |
-
"weighted avg": {
|
| 58 |
-
"precision": 0.9996292236692774,
|
| 59 |
-
"recall": 0.9996289149473059,
|
| 60 |
-
"f1-score": 0.9996289400050211,
|
| 61 |
-
"support": 13474.0
|
| 62 |
-
}
|
| 63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/ViT_UNet_training_curves.png
DELETED
Git LFS Details
|
ensemble/results/classification_summary.csv
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
model,capacity_tag,test_accuracy,kappa,macro_f1,weighted_f1,val_nll,test_nll,val_brier,test_brier,test_ece_15bin,epochs_configured,epochs_ran,train_time_sec,best_model_path,final_model_path
|
| 2 |
-
GFNet_ens_2,legacy_arch,1.0,1.0,1.0,1.0,0.008650941411156436,0.008394689763386073,0.0003470874198973904,0.00016413560970697484,0.008325216172798567,100,100,1091.3922517279998,/kaggle/working/results/models/ensembles/GFNet_ens_2_final.keras,/kaggle/working/results/models/ensembles/GFNet_ens_2_final.keras
|
| 3 |
-
GFNet_ens_1,legacy_arch,1.0,1.0,1.0,1.0,0.007782886691788137,0.007580193458155463,0.00022485345195341052,8.675982552089211e-05,0.007542962872771458,100,100,1093.302967228,/kaggle/working/results/models/GFNet_best.keras,/kaggle/working/results/models/ensembles/GFNet_ens_1_final.keras
|
| 4 |
-
GFNet_ens_3,legacy_arch,1.0,1.0,1.0,1.0,0.007835681029395622,0.00770890638266266,0.00015427664263396678,9.220673557648059e-05,0.007669471749499234,100,100,1095.5028445569988,/kaggle/working/results/models/ensembles/GFNet_ens_3_final.keras,/kaggle/working/results/models/ensembles/GFNet_ens_3_final.keras
|
| 5 |
-
GFNet_ens_4,legacy_arch,1.0,1.0,1.0,1.0,0.008375646065942278,0.008268151222375824,0.00011287869605898921,8.793159663540437e-05,0.008229851138791716,100,100,1098.8232494029999,/kaggle/working/results/models/ensembles/GFNet_ens_4_final.keras,/kaggle/working/results/models/ensembles/GFNet_ens_4_final.keras
|
| 6 |
-
GFNet_ens_5,legacy_arch,1.0,1.0,1.0,1.0,0.008144904275139767,0.007577851225909456,0.00037955447040471246,8.569458569951265e-05,0.007541808385567299,100,100,1096.1566321030004,/kaggle/working/results/models/ensembles/GFNet_ens_5_final.keras,/kaggle/working/results/models/ensembles/GFNet_ens_5_final.keras
|
| 7 |
-
ViT_UNet_ens_1,legacy_arch,0.9997031319578448,0.9996423935968177,0.9993130507511258,0.9997032255305212,0.04125208399317739,0.04038559127744248,0.0026588554415351317,0.0022089245494705512,0.038699414931083975,100,100,2127.257505900001,/kaggle/working/results/models/ViT_UNet_best.keras,/kaggle/working/results/models/ensembles/ViT_UNet_ens_1_final.keras
|
| 8 |
-
ViT_UNet_ens_4,legacy_arch,0.9997031319578448,0.9996424018799352,0.999287299739852,0.9997033045823546,0.040882048437208815,0.03983519771741158,0.002633379651110601,0.002130986443866403,0.0381665819871845,100,100,2151.865034081002,/kaggle/working/results/models/ensembles/ViT_UNet_ens_4_final.keras,/kaggle/working/results/models/ensembles/ViT_UNet_ens_4_final.keras
|
| 9 |
-
ViT_UNet_ens_5,legacy_arch,0.9996289149473059,0.9995529894957109,0.9991476463830495,0.9996289400050211,0.042936784080404175,0.0409576964078693,0.0034046686526971017,0.002444263189115926,0.03892784227386362,100,100,2149.094057154998,/kaggle/working/results/models/ensembles/ViT_UNet_ens_5_final.keras,/kaggle/working/results/models/ensembles/ViT_UNet_ens_5_final.keras
|
| 10 |
-
ViT_UNet_ens_3,legacy_arch,0.9996289149473059,0.9995529860759044,0.9989782885709497,0.9996290651977676,0.04151233752284096,0.04109393863130706,0.002767361698916364,0.0024408674943176495,0.03868930220727806,100,100,2108.2808553079976,/kaggle/working/results/models/ensembles/ViT_UNet_ens_3_final.keras,/kaggle/working/results/models/ensembles/ViT_UNet_ens_3_final.keras
|
| 11 |
-
AlexNet_CNN_ens_3,legacy_arch,0.9994804809262283,0.9993741415954641,0.9988388569382961,0.9994798515712402,0.0025376154609043646,0.0025376154609043646,0.0007780503567997554,0.0007780503567997554,0.00025354179789939654,100,100,1067.531324263,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_3_final.keras,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_3_final.keras
|
| 12 |
-
ViT_UNet_ens_2,legacy_arch,0.9994062639156894,0.9992847664645924,0.9985633253467591,0.9994064198749861,0.04356254463611605,0.04152917083272156,0.003853330990829368,0.002736550655265285,0.038360946907812395,100,100,2130.1647957269997,/kaggle/working/results/models/ensembles/ViT_UNet_ens_2_final.keras,/kaggle/working/results/models/ensembles/ViT_UNet_ens_2_final.keras
|
| 13 |
-
AlexNet_CNN_ens_5,legacy_arch,0.9993320469051507,0.9991953279840045,0.9989412971389648,0.9993317721236556,0.0022415306676375002,0.0022415306676375002,0.0010475338113041146,0.0010475338113041146,0.00046385921558888605,100,100,1071.6476408859999,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_5_final.keras,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_5_final.keras
|
| 14 |
-
AlexNet_CNN_ens_1,legacy_arch,0.9989609618524565,0.9987483380657164,0.9980484258102253,0.9989604947263638,0.004822081824650907,0.004822081824650907,0.0018765840682375234,0.0018765840682375234,0.00038955023456118803,100,100,1085.3566581199998,/kaggle/working/results/models/AlexNet_CNN_best.keras,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_1_final.keras
|
| 15 |
-
AlexNet_CNN_ens_4,legacy_arch,0.9968086685468309,0.9961553150730917,0.9947741655752209,0.9968055596174699,0.01159683983002684,0.01159683983002684,0.005574999536239272,0.005574999536239272,0.0012410734126948985,100,100,1071.6318536030003,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_4_final.keras,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_4_final.keras
|
| 16 |
-
AlexNet_CNN_ens_2,legacy_arch,0.9960664984414428,0.9952610255039888,0.9907099898333486,0.9960603671533084,0.013130652896216117,0.013130652896216117,0.006438098751476688,0.006438098751476688,0.001688033413140307,100,100,1069.520384081,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_2_final.keras,/kaggle/working/results/models/ensembles/AlexNet_CNN_ens_2_final.keras
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ensemble/results/combined_initial_classification_overview.png
DELETED
Git LFS Details
|