+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import os
+import time
+import json
+import numpy as np
+import cv2
+import rasterio
+from rasterio.windows import from_bounds
+import torch
+import torch.nn as nn
+import torch.optim as optim
+from torch.utils.data import TensorDataset, DataLoader, random_split
+from scipy.ndimage import distance_transform_edt as distance
+from scipy.spatial.distance import directed_hausdorff
+from huggingface_hub import hf_hub_download
+import ee
+import geemap
+from google.colab import drive
+from sklearn.metrics import accuracy_score, f1_score, jaccard_score, precision_score, recall_score
+from datetime import datetime
+import matplotlib.pyplot as plt
+
+# Mount Drive
+drive.mount('/content/drive', force_remount=True)
+
+# Initialize Earth Engine
+try:
+ ee.Initialize(project='[REDACTED_FOR_SECURITY]')
+except:
+ ee.Authenticate()
+ ee.Initialize(project='[REDACTED_FOR_SECURITY]')
+
+# Configuration
+device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+print(f"Running on: {device}")
+
+SAVE_DIR = '/content/drive/MyDrive/SatMAE_Fair_Comparison/'
+if not os.path.exists(SAVE_DIR): os.makedirs(SAVE_DIR)
+
+BATCH_SIZE = 8
+EPOCHS = 50
+LR_MAX = 1e-4
+LR_MIN = 1e-6
+PATCH_SIZE = 224
+ASSET_ID = 'projects/[REDACTED_FOR_SECURITY]/assets/Punjab_Mask_2024_NEW'
+TIME_WINDOWS = [('2024-11-01', '2024-11-30'), ('2025-02-15', '2025-03-15'), ('2025-04-01', '2025-04-15')]
+
+
+
+
+
+
+
+
+
+
+
+Mounted at /content/drive +Running on: cuda ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+!pip install geedim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Collecting geedim + Downloading geedim-2.0.0-py3-none-any.whl.metadata (6.0 kB) +Requirement already satisfied: numpy>=1.19 in /usr/local/lib/python3.12/dist-packages (from geedim) (2.0.2) +Requirement already satisfied: rasterio>=1.3.8 in /usr/local/lib/python3.12/dist-packages (from geedim) (1.5.0) +Requirement already satisfied: click>=8 in /usr/local/lib/python3.12/dist-packages (from geedim) (8.3.1) +Requirement already satisfied: tqdm>=4.6 in /usr/local/lib/python3.12/dist-packages (from geedim) (4.67.1) +Requirement already satisfied: earthengine-api>=0.1.379 in /usr/local/lib/python3.12/dist-packages (from geedim) (1.5.24) +Requirement already satisfied: tabulate>=0.9 in /usr/local/lib/python3.12/dist-packages (from geedim) (0.9.0) +Requirement already satisfied: fsspec>=2025.2 in /usr/local/lib/python3.12/dist-packages (from geedim) (2025.3.0) +Requirement already satisfied: aiohttp>=3.11 in /usr/local/lib/python3.12/dist-packages (from geedim) (3.13.3) +Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (2.6.1) +Requirement already satisfied: aiosignal>=1.4.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (1.4.0) +Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (25.4.0) +Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (1.8.0) +Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (6.7.0) +Requirement already satisfied: propcache>=0.2.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (0.4.1) +Requirement already satisfied: yarl<2.0,>=1.17.0 in /usr/local/lib/python3.12/dist-packages (from aiohttp>=3.11->geedim) (1.22.0) +Requirement already satisfied: google-cloud-storage in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (3.7.0) +Requirement already satisfied: google-api-python-client>=1.12.1 in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (2.187.0) +Requirement already satisfied: google-auth>=1.4.1 in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (2.43.0) +Requirement already satisfied: google-auth-httplib2>=0.0.3 in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (0.3.0) +Requirement already satisfied: httplib2<1dev,>=0.9.2 in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (0.31.0) +Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from earthengine-api>=0.1.379->geedim) (2.32.4) +Requirement already satisfied: affine in /usr/local/lib/python3.12/dist-packages (from rasterio>=1.3.8->geedim) (2.4.0) +Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from rasterio>=1.3.8->geedim) (2026.1.4) +Requirement already satisfied: cligj>=0.5 in /usr/local/lib/python3.12/dist-packages (from rasterio>=1.3.8->geedim) (0.7.2) +Requirement already satisfied: pyparsing in /usr/local/lib/python3.12/dist-packages (from rasterio>=1.3.8->geedim) (3.3.1) +Requirement already satisfied: typing-extensions>=4.2 in /usr/local/lib/python3.12/dist-packages (from aiosignal>=1.4.0->aiohttp>=3.11->geedim) (4.15.0) +Requirement already satisfied: google-api-core!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0,<3.0.0,>=1.31.5 in /usr/local/lib/python3.12/dist-packages (from google-api-python-client>=1.12.1->earthengine-api>=0.1.379->geedim) (2.29.0) +Requirement already satisfied: uritemplate<5,>=3.0.1 in /usr/local/lib/python3.12/dist-packages (from google-api-python-client>=1.12.1->earthengine-api>=0.1.379->geedim) (4.2.0) +Requirement already satisfied: cachetools<7.0,>=2.0.0 in /usr/local/lib/python3.12/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.379->geedim) (6.2.4) +Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.12/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.379->geedim) (0.4.2) +Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.12/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.379->geedim) (4.9.1) +Requirement already satisfied: idna>=2.0 in /usr/local/lib/python3.12/dist-packages (from yarl<2.0,>=1.17.0->aiohttp>=3.11->geedim) (3.11) +Requirement already satisfied: google-cloud-core<3.0.0,>=2.4.2 in /usr/local/lib/python3.12/dist-packages (from google-cloud-storage->earthengine-api>=0.1.379->geedim) (2.5.0) +Requirement already satisfied: google-resumable-media<3.0.0,>=2.7.2 in /usr/local/lib/python3.12/dist-packages (from google-cloud-storage->earthengine-api>=0.1.379->geedim) (2.8.0) +Requirement already satisfied: google-crc32c<2.0.0,>=1.1.3 in /usr/local/lib/python3.12/dist-packages (from google-cloud-storage->earthengine-api>=0.1.379->geedim) (1.8.0) +Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->earthengine-api>=0.1.379->geedim) (3.4.4) +Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests->earthengine-api>=0.1.379->geedim) (2.5.0) +Requirement already satisfied: googleapis-common-protos<2.0.0,>=1.56.2 in /usr/local/lib/python3.12/dist-packages (from google-api-core!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0,<3.0.0,>=1.31.5->google-api-python-client>=1.12.1->earthengine-api>=0.1.379->geedim) (1.72.0) +Requirement already satisfied: protobuf!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<7.0.0,>=3.19.5 in /usr/local/lib/python3.12/dist-packages (from google-api-core!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0,<3.0.0,>=1.31.5->google-api-python-client>=1.12.1->earthengine-api>=0.1.379->geedim) (5.29.5) +Requirement already satisfied: proto-plus<2.0.0,>=1.22.3 in /usr/local/lib/python3.12/dist-packages (from google-api-core!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.0,<3.0.0,>=1.31.5->google-api-python-client>=1.12.1->earthengine-api>=0.1.379->geedim) (1.27.0) +Requirement already satisfied: pyasn1<0.7.0,>=0.6.1 in /usr/local/lib/python3.12/dist-packages (from pyasn1-modules>=0.2.1->google-auth>=1.4.1->earthengine-api>=0.1.379->geedim) (0.6.1) +Downloading geedim-2.0.0-py3-none-any.whl (73 kB) + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 73.1/73.1 kB 4.0 MB/s eta 0:00:00 +Installing collected packages: geedim +Successfully installed geedim-2.0.0 ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+def get_satmae_data():
+ print("Ingesting Asset...")
+ mask_img = ee.Image(ASSET_ID)
+ roi_geom = mask_img.geometry()
+ mask_file = 'local_mask.tif'
+ if not os.path.exists(mask_file):
+ geemap.download_ee_image(mask_img, mask_file, region=roi_geom, scale=10, crs='EPSG:4326', overwrite=True)
+
+ with rasterio.open(mask_file) as src:
+ b = src.bounds
+ cx, cy = (b.left + b.right)/2, (b.bottom + b.top)/2
+ offset = 0.06
+ window = from_bounds(cx-offset, cy-offset, cx+offset, cy+offset, src.transform)
+ mask = src.read(1, window=window)
+ mask = np.where(mask > 0, 1.0, 0.0).astype(np.float32)
+ target_h, target_w = mask.shape
+ small_roi = ee.Geometry.Rectangle([cx-offset, cy-offset, cx+offset, cy+offset], proj=str(src.crs), geodesic=False)
+
+ stack = []
+ print("Stacking Time Steps...")
+ for i, (start, end) in enumerate(TIME_WINDOWS):
+ fname = f'time_{i}.tif'
+ if not os.path.exists(fname):
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED').filterBounds(small_roi).filterDate(start, end).median().select(['B2','B3','B4','B8','B11','B12'])
+ s1 = ee.ImageCollection('COPERNICUS/S1_GRD').filterBounds(small_roi).filterDate(start, end).mean().select(['VV','VH'])
+ fused = ee.Image.cat([s2, s1]).clip(small_roi)
+ geemap.download_ee_image(fused, fname, region=small_roi, scale=10, crs='EPSG:4326', overwrite=True)
+
+ with rasterio.open(fname) as src:
+ arr = src.read()
+ arr = np.transpose(arr, (1, 2, 0))
+ if arr.shape[:2] != (target_h, target_w):
+ arr = cv2.resize(arr, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
+ s2_n = np.clip(arr[:,:,:6] / 5000.0, 0, 1)
+ s1_n = np.clip((arr[:,:,6:] - (-25.0)) / (0.0 - (-25.0)), 0, 1)
+ stack.append(np.concatenate([s2_n, s1_n], axis=2))
+
+ full_cube = np.stack(stack, axis=2)
+ x_out, y_out = [], []
+ stride = PATCH_SIZE
+
+ print("Creating Patches...")
+ for y in range(0, target_h, stride):
+ for x in range(0, target_w, stride):
+ img_p = full_cube[y:y+stride, x:x+stride]
+ mask_p = mask[y:y+stride, x:x+stride]
+ if img_p.shape[0] != PATCH_SIZE or img_p.shape[1] != PATCH_SIZE: continue
+ if np.min(img_p) < 0: continue
+ x_out.append(img_p)
+ y_out.append(mask_p)
+
+ X = np.array(x_out, dtype=np.float32).transpose(0, 4, 3, 1, 2)
+ y = np.array(y_out, dtype=np.float32)[:, None, :, :]
+ print(f"Data Ready. Shape: {X.shape}")
+ return torch.tensor(X), torch.tensor(y)
+
+X_data, y_data = get_satmae_data()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ingesting Asset... ++
+
+
+
+
+/usr/local/lib/python3.12/dist-packages/geemap/common.py:12471: FutureWarning: 'BaseImage' is deprecated and will be removed in a future release. Please use the 'ee.Image.gd' accessor instead. + img = gd.download.BaseImage(image) ++
+
+
+
+
+...tmae-2026/assets/Punjab_Mask_2024_NEW: 0%| |0/585 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 1.65 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.52 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.15 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 1.61 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 1.25 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +/usr/local/lib/python3.12/dist-packages/geedim/image.py:254: RuntimeWarning: Couldn't find STAC entry for: 'projects/satmae-2026/assets/Punjab_Mask_2024_NEW'. + return STACClient().get(self.id) ++
+
+
+
+
+Stacking Time Steps... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +/usr/local/lib/python3.12/dist-packages/geedim/image.py:254: RuntimeWarning: Couldn't find STAC entry for: 'None'. + return STACClient().get(self.id) ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 1.60 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 ++
+
+
+
+
+Creating Patches... +Data Ready. Shape: (25, 8, 3, 224, 224) ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+class DiceLoss(nn.Module):
+ def __init__(self, smooth=1e-6):
+ super().__init__()
+ self.smooth = smooth
+ def forward(self, inputs, targets):
+ inputs = torch.sigmoid(inputs).view(-1)
+ targets = targets.view(-1)
+ inter = (inputs * targets).sum()
+ dice = (2. * inter + self.smooth) / (inputs.sum() + targets.sum() + self.smooth)
+ return 1 - dice
+
+class FocalTverskyLoss(nn.Module):
+ def __init__(self, alpha=0.7, beta=0.3, gamma=2.0, smooth=1e-6):
+ super().__init__()
+ self.alpha = alpha
+ self.beta = beta
+ self.gamma = gamma
+ self.smooth = smooth
+ def forward(self, inputs, targets):
+ inputs = torch.sigmoid(inputs).view(-1)
+ targets = targets.view(-1)
+ TP = (inputs * targets).sum()
+ FP = ((1-targets) * inputs).sum()
+ FN = (targets * (1-inputs)).sum()
+ Tversky = (TP + self.smooth) / (TP + self.alpha*FP + self.beta*FN + self.smooth)
+ return (1 - Tversky)**self.gamma
+
+class HausdorffDTLoss(nn.Module):
+ def __init__(self, alpha=2.0):
+ super().__init__()
+ self.alpha = alpha
+ def forward(self, pred, gt):
+ with torch.no_grad():
+ gt_np = gt.cpu().numpy()
+ dist_map = np.zeros_like(gt_np)
+ for i in range(len(gt_np)):
+ # Ensure binary mask for distance transform
+ mask = (gt_np[i, 0] > 0.5).astype(np.uint8)
+ if mask.sum() == 0: continue
+ d_in = distance(mask)
+ d_out = distance(1 - mask)
+ dist_map[i, 0] = (d_out - d_in)
+ dist_map = torch.tensor(dist_map, device=pred.device, dtype=torch.float32)
+
+ probs = torch.sigmoid(pred)
+ return torch.mean((probs - gt) ** 2 * (1 + self.alpha * torch.abs(dist_map)))
+
+class CompoundLoss(nn.Module):
+ def __init__(self):
+ super().__init__()
+ self.dice = DiceLoss()
+ self.boundary = HausdorffDTLoss(alpha=2.0)
+ self.focal = FocalTverskyLoss()
+ def forward(self, preds, targets):
+ # Weighted combination: 50% Dice, 30% Boundary, 20% Focal
+ return 0.5*self.dice(preds, targets) + 0.3*self.boundary(preds, targets) + 0.2*self.focal(preds, targets)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+class SatMAEPatchEmbed(nn.Module):
+ def __init__(self, in_chans=8, embed_dim=768, patch_size=16):
+ super().__init__()
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
+ def forward(self, x):
+ B, C, T, H, W = x.shape
+ x = x.permute(0, 2, 1, 3, 4).reshape(B * T, C, H, W)
+ x = self.proj(x).flatten(2).transpose(1, 2)
+ x = x.reshape(B, T, -1, x.shape[-1])
+ return x
+
+class SatMAEBackbone(nn.Module):
+ def __init__(self, num_frames=3, in_chans=8, embed_dim=768, depth=12, num_heads=12):
+ super().__init__()
+ self.patch_embed = SatMAEPatchEmbed(in_chans=in_chans, embed_dim=embed_dim)
+ num_patches = (224 // 16) ** 2
+ self.pos_embed = nn.Parameter(torch.zeros(1, 1, num_patches + 1, embed_dim))
+ self.time_embed = nn.Parameter(torch.zeros(1, num_frames, 1, embed_dim))
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, 1, embed_dim))
+ encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=embed_dim*4, activation="gelu", batch_first=True, norm_first=True)
+ self.blocks = nn.TransformerEncoder(encoder_layer, num_layers=depth)
+ self.norm = nn.LayerNorm(embed_dim)
+
+ def forward(self, x):
+ x = self.patch_embed(x)
+ B, T, N, D = x.shape
+ x = x + self.time_embed
+ x = x.reshape(B, T*N, D)
+ spatial_pos = self.pos_embed[:, :, 1:, :].expand(B, T, -1, -1).reshape(B, T*N, D)
+ x = x + spatial_pos
+ cls_token = self.cls_token.expand(B, -1, -1, -1).reshape(B, 1, D) + self.pos_embed[:, :, 0, :].expand(B, 1, D)
+ x = torch.cat((cls_token, x), dim=1)
+ x = self.blocks(x)
+ x = self.norm(x)
+ return x
+
+class SatMAESegmentation(nn.Module):
+ def __init__(self, num_frames=3, embed_dim=768):
+ super().__init__()
+ print("Constructing Manual SatMAE (Code 1 Base)...")
+ self.backbone = SatMAEBackbone(num_frames=num_frames, embed_dim=embed_dim)
+
+ # Load Weights
+ try:
+ print("Loading Google ViT Weights...")
+ p = hf_hub_download("google/vit-base-patch16-224", "pytorch_model.bin")
+ sd = torch.load(p, map_location='cpu')
+ w = sd['vit.embeddings.patch_embeddings.projection.weight']
+ new_w = torch.zeros(768, 8, 16, 16)
+ new_w[:, :3] = w
+ new_w[:, 3:] = w.mean(1, keepdim=True).repeat(1, 5, 1, 1)
+ self.backbone.patch_embed.proj.weight.data = new_w
+ self.backbone.patch_embed.proj.bias.data = sd['vit.embeddings.patch_embeddings.projection.bias']
+ print("Weights Adapted.")
+ except:
+ print("Weights missing, using random init.")
+
+ # Freezing Strategy (Replicating Code 1 Logic)
+ print("Freezing Transformer Backbone...")
+ # 1. Freeze EVERYTHING initially
+ for param in self.backbone.parameters():
+ param.requires_grad = False
+
+ # 2. Unfreeze only Input and Time embeddings
+ self.backbone.time_embed.requires_grad = True
+ self.backbone.patch_embed.proj.weight.requires_grad = True
+ print("Unfrozen: Patch Embeddings + Time Embeddings")
+
+ # 3. Decoder
+ self.temporal_agg = nn.Conv2d(embed_dim * num_frames, embed_dim, kernel_size=1)
+ self.decoder = nn.Sequential(
+ nn.Upsample(scale_factor=2), nn.Conv2d(embed_dim, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(256, 128, 3, 1, 1), nn.BatchNorm2d(128), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(128, 64, 3, 1, 1), nn.BatchNorm2d(64), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(64, 32, 3, 1, 1), nn.BatchNorm2d(32), nn.GELU(),
+ nn.Conv2d(32, 1, 1)
+ )
+
+ def forward(self, x):
+ features = self.backbone(x)[:, 1:, :]
+ B, L, D = features.shape
+ features = features.view(B, 3, 14, 14, D).permute(0, 4, 1, 2, 3).flatten(1, 2)
+ features = self.temporal_agg(features)
+ return self.decoder(features)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+model = SatMAESegmentation().to(device)
+criterion = CompoundLoss()
+optimizer = optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR_MAX)
+scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2, eta_min=LR_MIN)
+
+ds = TensorDataset(X_data, y_data)
+tr_sz = int(0.85 * len(ds))
+t_ds, v_ds = random_split(ds, [tr_sz, len(ds)-tr_sz])
+train_loader = DataLoader(t_ds, BATCH_SIZE, shuffle=True)
+val_loader = DataLoader(v_ds, BATCH_SIZE, shuffle=False)
+
+print(f"Starting Fair Training ({EPOCHS} Epochs)...")
+history = {'train_loss': [], 'val_loss': []}
+
+for ep in range(EPOCHS):
+ model.train()
+ train_loss = 0
+
+ for x, y in train_loader:
+ x, y = x.to(device), y.to(device)
+ optimizer.zero_grad()
+ preds = model(x)
+ loss = criterion(preds, y)
+ loss.backward()
+ optimizer.step()
+ train_loss += loss.item()
+
+ scheduler.step()
+
+ model.eval()
+ val_loss = 0
+ with torch.no_grad():
+ for x, y in val_loader:
+ x, y = x.to(device), y.to(device)
+ preds = model(x)
+ val_loss += criterion(preds, y).item()
+
+ avg_t = train_loss / len(train_loader)
+ avg_v = val_loss / len(val_loader)
+ history['train_loss'].append(avg_t)
+ history['val_loss'].append(avg_v)
+
+ if (ep+1)%5==0:
+ print(f"Ep {ep+1} | Train: {avg_t:.4f} | Val: {avg_v:.4f}")
+
+torch.save(model.state_dict(), SAVE_DIR + "SatMAE_Code1_Fair.pth")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructing Manual SatMAE (Code 1 Base)... +Loading Google ViT Weights... ++
+
+
+
+
+/usr/local/lib/python3.12/dist-packages/torch/nn/modules/transformer.py:392: UserWarning: enable_nested_tensor is True, but self.use_nested_tensor is False because encoder_layer.norm_first was True + warnings.warn( +/usr/local/lib/python3.12/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: +The secret `HF_TOKEN` does not exist in your Colab secrets. +To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session. +You will be able to reuse this secret in all of your notebooks. +Please note that authentication is recommended but still optional to access public models or datasets. + warnings.warn( ++
+
+
+
+
+pytorch_model.bin: 0%| | 0.00/346M [00:00<?, ?B/s]+
+
+
+
+
+Weights Adapted. +Freezing Transformer Backbone... +Unfrozen: Patch Embeddings + Time Embeddings +Starting Fair Training (50 Epochs)... +Ep 5 | Train: 0.4532 | Val: 0.7533 +Ep 10 | Train: 0.4165 | Val: 0.4827 +Ep 15 | Train: 0.3638 | Val: 0.5578 +Ep 20 | Train: 0.3387 | Val: 0.5015 +Ep 25 | Train: 0.3304 | Val: 0.2919 +Ep 30 | Train: 0.3348 | Val: 0.3181 +Ep 35 | Train: 0.3126 | Val: 0.3009 +Ep 40 | Train: 0.3016 | Val: 0.2724 +Ep 45 | Train: 0.2886 | Val: 0.2965 +Ep 50 | Train: 0.2855 | Val: 0.2557 ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+def calculate_boundary_iou(gt_mask, pred_mask, dilation=5):
+ gt_mask = gt_mask.astype(np.uint8)
+ pred_mask = pred_mask.astype(np.uint8)
+ kernel = np.ones((dilation, dilation), np.uint8)
+ gt_b = cv2.dilate(gt_mask, kernel) - cv2.erode(gt_mask, kernel)
+ pred_b = cv2.dilate(pred_mask, kernel) - cv2.erode(pred_mask, kernel)
+ inter = np.logical_and(gt_b, pred_b).sum()
+ union = np.logical_or(gt_b, pred_b).sum()
+ return inter / (union + 1e-6)
+
+def symmetric_hausdorff(mask_pred, mask_gt):
+ coords_pred = np.argwhere(mask_pred)
+ coords_gt = np.argwhere(mask_gt)
+ if len(coords_pred) == 0 or len(coords_gt) == 0: return 316.0
+ d_pg = directed_hausdorff(coords_pred, coords_gt)[0]
+ d_gp = directed_hausdorff(coords_gt, coords_pred)[0]
+ return max(d_pg, d_gp)
+
+print("Running Final Fair Metrics...")
+model.eval()
+all_preds, all_targets = [], []
+boundary_ious, hausdorff_dists = [], []
+start_time = time.time()
+
+with torch.no_grad():
+ for x, y in val_loader:
+ x = x.to(device)
+ logits = model(x)
+ p_batch = (torch.sigmoid(logits) > 0.5).float().cpu().numpy()
+ y_batch = y.numpy()
+
+ for j in range(len(y_batch)):
+ p, t = p_batch[j, 0].astype(np.uint8), y_batch[j, 0].astype(np.uint8)
+ boundary_ious.append(calculate_boundary_iou(t, p))
+ if np.sum(t) > 0 and np.sum(p) > 0:
+ hausdorff_dists.append(symmetric_hausdorff(p, t))
+
+ all_preds.extend(p_batch.flatten())
+ all_targets.extend(y_batch.flatten())
+
+total_time = time.time() - start_time
+fps = len(val_loader.dataset) / (total_time + 1e-6)
+y_p, y_t = np.array(all_preds).astype(int), np.array(all_targets).astype(int)
+
+metrics = {
+ "Model": "SatMAE_Code1_FrozenBrain_Fair",
+ "Standard_IoU": round(jaccard_score(y_t, y_p, average='binary'), 4),
+ "Boundary_IoU": round(np.mean(boundary_ious), 4),
+ "Hausdorff_Dist": round(np.mean(hausdorff_dists), 2),
+ "FPS": round(fps, 2)
+}
+
+print("FAIR COMPARISON RESULT (Manual Code 1 Base):")
+print(json.dumps(metrics, indent=4))
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Running Final Fair Metrics...
+FAIR COMPARISON RESULT (Manual Code 1 Base):
+{
+ "Model": "SatMAE_Code1_FrozenBrain_Fair",
+ "Standard_IoU": 0.8464,
+ "Boundary_IoU": 0.3481,
+ "Hausdorff_Dist": 18.72,
+ "FPS": 6.46
+}
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import matplotlib.pyplot as plt
+import seaborn as sns
+from sklearn.metrics import accuracy_score, f1_score, jaccard_score, precision_score, recall_score, confusion_matrix
+import pandas as pd
+
+def visualize_and_report(model, loader, history, save_dir, model_name="SatMAE_Code1_Fair"):
+ print(f" Generating Visual Report for {model_name}...")
+ model.eval()
+
+
+ # 1. LOSS CURVE (Train vs Validation)
+
+ if 'train_loss' in history and 'val_loss' in history:
+ plt.figure(figsize=(10, 6))
+ epochs = range(1, len(history['train_loss']) + 1)
+ plt.plot(epochs, history['train_loss'], 'b-', label='Training Loss', linewidth=2)
+ plt.plot(epochs, history['val_loss'], 'r--', label='Validation Loss', linewidth=2)
+ plt.title(f'{model_name} Learning Curve')
+ plt.xlabel('Epochs')
+ plt.ylabel('Compound Loss')
+ plt.legend()
+ plt.grid(True, alpha=0.3)
+ plt.savefig(f"{save_dir}{model_name}_LossCurve.png")
+ plt.show()
+ print(" Loss Graph Saved.")
+
+
+ # 2. SAMPLE VISUALIZATION (Input vs Truth vs Pred)
+
+ try:
+ x_batch, y_batch = next(iter(loader))
+ x_batch, y_batch = x_batch.to(device), y_batch.to(device)
+
+ with torch.no_grad():
+ logits = model(x_batch)
+ preds = (torch.sigmoid(logits) > 0.5).float()
+
+ # Plot 3 samples
+ fig, axes = plt.subplots(3, 3, figsize=(12, 12))
+ cols = ["Input (RGB - Peak Season)", "Ground Truth", "Prediction"]
+ for ax, col in zip(axes[0], cols): ax.set_title(col, fontsize=14, fontweight='bold')
+
+ for i in range(3):
+ if i >= len(x_batch): break
+
+ # Construct RGB from bands B4(2), B3(1), B2(0) of Time Step 1
+ rgb = x_batch[i, [2, 1, 0], 1, :, :].permute(1, 2, 0).cpu().numpy()
+ rgb = np.clip(rgb * 3.5, 0, 1) # Brighten 3.5x
+
+ gt_img = y_batch[i, 0].cpu().numpy()
+ pred_img = preds[i, 0].cpu().numpy()
+
+ axes[i, 0].imshow(rgb)
+ axes[i, 1].imshow(gt_img, cmap='gray')
+ axes[i, 2].imshow(pred_img, cmap='gray')
+ for ax in axes[i]: ax.axis('off')
+
+ plt.tight_layout()
+ plt.savefig(f"{save_dir}{model_name}_Samples.png")
+ plt.show()
+ print(" Sample Predictions Saved.")
+ except Exception as e:
+ print(f" Visualization Error: {e}")
+
+ # -------------------------------------------------------
+ # 3. STANDARD METRICS (Accuracy, F1, Recall, etc.)
+ # -------------------------------------------------------
+ print(" Calculating Pixel-wise Metrics...")
+ all_preds, all_targets = [], []
+
+ with torch.no_grad():
+ for x, y in loader:
+ x = x.to(device)
+ logits = model(x)
+ p_batch = (torch.sigmoid(logits) > 0.5).float().cpu().numpy().flatten()
+ y_batch = y.numpy().flatten()
+ all_preds.extend(p_batch)
+ all_targets.extend(y_batch)
+
+ y_p = np.array(all_preds).astype(int)
+ y_t = np.array(all_targets).astype(int)
+
+ metrics = {
+ "Model": model_name,
+ "Pixel_Accuracy": round(accuracy_score(y_t, y_p), 4),
+ "IoU_Score": round(jaccard_score(y_t, y_p, average='binary'), 4),
+ "F1_Score": round(f1_score(y_t, y_p, average='binary'), 4),
+ "Precision": round(precision_score(y_t, y_p, average='binary'), 4),
+ "Recall": round(recall_score(y_t, y_p, average='binary'), 4)
+ }
+
+ # Save to JSON
+ with open(f"{save_dir}{model_name}_FullMetrics.json", 'w') as f:
+ json.dump(metrics, f, indent=4)
+
+ print("\n FINAL STANDARD METRICS:")
+ print(json.dumps(metrics, indent=4))
+
+# EXECUTE
+visualize_and_report(model, val_loader, history, SAVE_DIR)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Generating Visual Report for SatMAE_Code1_Fair... ++
+
+
+
+
+
+
+
+
+
+
+Loss Graph Saved. ++
+
+
+
+
+
+
+
+
+
+
+ Sample Predictions Saved.
+⏳ Calculating Pixel-wise Metrics...
+
+ FINAL STANDARD METRICS:
+{
+ "Model": "SatMAE_Code1_Fair",
+ "Pixel_Accuracy": 0.8757,
+ "IoU_Score": 0.8464,
+ "F1_Score": 0.9168,
+ "Precision": 0.8918,
+ "Recall": 0.9433
+}
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import os
+import time
+import json
+import numpy as np
+import cv2
+import rasterio
+from rasterio.windows import from_bounds
+import torch
+import torch.nn as nn
+import torch.optim as optim
+from torch.utils.data import TensorDataset, DataLoader, random_split
+from scipy.ndimage import distance_transform_edt as distance
+from scipy.spatial.distance import directed_hausdorff
+from huggingface_hub import hf_hub_download
+import ee
+import geemap
+from google.colab import drive
+from sklearn.metrics import accuracy_score, f1_score, jaccard_score, precision_score, recall_score
+from datetime import datetime
+import matplotlib.pyplot as plt
+
+# 1. SETUP
+drive.mount('/content/drive', force_remount=True)
+
+try:
+ ee.Initialize(project='[REDACTED_FOR_SECURITY]')
+except:
+ ee.Authenticate()
+ ee.Initialize(project='[REDACTED_FOR_SECURITY]')
+
+device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
+print(f" Benchmarking on: {device}")
+
+# SAVE DIRECTORY
+SAVE_DIR = '/content/drive/MyDrive/SatMAE_LongTrain_500/'
+if not os.path.exists(SAVE_DIR): os.makedirs(SAVE_DIR)
+
+# CONFIG
+BATCH_SIZE = 8
+EPOCHS = 500 # <--- LONG TRAINING
+LR_MAX = 1e-4
+LR_MIN = 1e-6
+PATCH_SIZE = 224
+ASSET_ID = 'projects/[REDACTED_FOR_SECURITY]/assets/Punjab_Mask_2024_NEW'
+TIME_WINDOWS = [('2024-11-01', '2024-11-30'), ('2025-02-15', '2025-03-15'), ('2025-04-01', '2025-04-15')]
+
+# 2. DATA LOADING
+def get_satmae_data():
+ print("1. Ingesting Asset...")
+ mask_img = ee.Image(ASSET_ID)
+ roi_geom = mask_img.geometry()
+ mask_file = 'local_mask.tif'
+ if not os.path.exists(mask_file):
+ geemap.download_ee_image(mask_img, mask_file, region=roi_geom, scale=10, crs='EPSG:4326', overwrite=True)
+
+ with rasterio.open(mask_file) as src:
+ b = src.bounds
+ cx, cy = (b.left + b.right)/2, (b.bottom + b.top)/2
+ offset = 0.06
+ window = from_bounds(cx-offset, cy-offset, cx+offset, cy+offset, src.transform)
+ mask = src.read(1, window=window)
+ mask = np.where(mask > 0, 1.0, 0.0).astype(np.float32)
+ target_h, target_w = mask.shape
+ small_roi = ee.Geometry.Rectangle([cx-offset, cy-offset, cx+offset, cy+offset], proj=str(src.crs), geodesic=False)
+
+ stack = []
+ print("2. Stacking Time Steps...")
+ for i, (start, end) in enumerate(TIME_WINDOWS):
+ fname = f'time_{i}.tif'
+ if not os.path.exists(fname):
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED').filterBounds(small_roi).filterDate(start, end).median().select(['B2','B3','B4','B8','B11','B12'])
+ s1 = ee.ImageCollection('COPERNICUS/S1_GRD').filterBounds(small_roi).filterDate(start, end).mean().select(['VV','VH'])
+ fused = ee.Image.cat([s2, s1]).clip(small_roi)
+ geemap.download_ee_image(fused, fname, region=small_roi, scale=10, crs='EPSG:4326', overwrite=True)
+
+ with rasterio.open(fname) as src:
+ arr = src.read()
+ arr = np.transpose(arr, (1, 2, 0))
+ if arr.shape[:2] != (target_h, target_w):
+ arr = cv2.resize(arr, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
+ s2_n = np.clip(arr[:,:,:6] / 5000.0, 0, 1)
+ s1_n = np.clip((arr[:,:,6:] - (-25.0)) / (0.0 - (-25.0)), 0, 1)
+ stack.append(np.concatenate([s2_n, s1_n], axis=2))
+
+ full_cube = np.stack(stack, axis=2)
+ x_out, y_out = [], []
+ stride = PATCH_SIZE
+
+ print("3. Creating Patches...")
+ for y in range(0, target_h, stride):
+ for x in range(0, target_w, stride):
+ img_p = full_cube[y:y+stride, x:x+stride]
+ mask_p = mask[y:y+stride, x:x+stride]
+ if img_p.shape[0] != PATCH_SIZE or img_p.shape[1] != PATCH_SIZE: continue
+ if np.min(img_p) < 0: continue
+ x_out.append(img_p)
+ y_out.append(mask_p)
+
+ X = np.array(x_out, dtype=np.float32).transpose(0, 4, 3, 1, 2)
+ X = np.nan_to_num(X, nan=0.0)
+ y = np.array(y_out, dtype=np.float32)[:, None, :, :]
+ print(f" Data Ready. Shape: {X.shape}")
+ return torch.tensor(X), torch.tensor(y)
+
+X_data, y_data = get_satmae_data()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mounted at /content/drive +🚀 Benchmarking on: cuda +1. Ingesting Asset... +2. Stacking Time Steps... +3. Creating Patches... + Data Ready. Shape: (25, 8, 3, 224, 224) ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# 1. ADVANCED LOSS
+class DiceLoss(nn.Module):
+ def __init__(self, smooth=1e-6):
+ super().__init__()
+ self.smooth = smooth
+ def forward(self, inputs, targets):
+ inputs = torch.sigmoid(inputs).view(-1)
+ targets = targets.view(-1)
+ inter = (inputs * targets).sum()
+ dice = (2. * inter + self.smooth) / (inputs.sum() + targets.sum() + self.smooth)
+ return 1 - dice
+
+class FocalTverskyLoss(nn.Module):
+ def __init__(self, alpha=0.7, beta=0.3, gamma=2.0, smooth=1e-6):
+ super().__init__()
+ self.alpha = alpha
+ self.beta = beta
+ self.gamma = gamma
+ self.smooth = smooth
+ def forward(self, inputs, targets):
+ inputs = torch.sigmoid(inputs).view(-1)
+ targets = targets.view(-1)
+ TP = (inputs * targets).sum()
+ FP = ((1-targets) * inputs).sum()
+ FN = (targets * (1-inputs)).sum()
+ Tversky = (TP + self.smooth) / (TP + self.alpha*FP + self.beta*FN + self.smooth)
+ return (1 - Tversky)**self.gamma
+
+class HausdorffDTLoss(nn.Module):
+ def __init__(self, alpha=2.0):
+ super().__init__()
+ self.alpha = alpha
+ def forward(self, pred, gt):
+ with torch.no_grad():
+ gt_np = gt.cpu().numpy()
+ dist_map = np.zeros_like(gt_np)
+ for i in range(len(gt_np)):
+ mask = (gt_np[i, 0] > 0.5).astype(np.uint8)
+ if mask.sum() == 0: continue
+ d_in = distance(mask)
+ d_out = distance(1 - mask)
+ dist_map[i, 0] = (d_out - d_in)
+ dist_map = torch.tensor(dist_map, device=pred.device, dtype=torch.float32)
+ probs = torch.sigmoid(pred)
+ return torch.mean((probs - gt) ** 2 * (1 + self.alpha * torch.abs(dist_map)))
+
+class CompoundLoss(nn.Module):
+ def __init__(self):
+ super().__init__()
+ self.dice = DiceLoss()
+ self.boundary = HausdorffDTLoss(alpha=2.0)
+ self.focal = FocalTverskyLoss()
+ def forward(self, preds, targets):
+ return 0.5*self.dice(preds, targets) + 0.3*self.boundary(preds, targets) + 0.2*self.focal(preds, targets)
+
+# 2. MODEL DEFINITION
+class SatMAEPatchEmbed(nn.Module):
+ def __init__(self, in_chans=8, embed_dim=768, patch_size=16):
+ super().__init__()
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
+ def forward(self, x):
+ B, C, T, H, W = x.shape
+ x = x.permute(0, 2, 1, 3, 4).reshape(B * T, C, H, W)
+ x = self.proj(x).flatten(2).transpose(1, 2)
+ x = x.reshape(B, T, -1, x.shape[-1])
+ return x
+
+class SatMAEBackbone(nn.Module):
+ def __init__(self, num_frames=3, in_chans=8, embed_dim=768, depth=12, num_heads=12):
+ super().__init__()
+ self.patch_embed = SatMAEPatchEmbed(in_chans=in_chans, embed_dim=embed_dim)
+ num_patches = (224 // 16) ** 2
+ self.pos_embed = nn.Parameter(torch.zeros(1, 1, num_patches + 1, embed_dim))
+ self.time_embed = nn.Parameter(torch.zeros(1, num_frames, 1, embed_dim))
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, 1, embed_dim))
+ encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=embed_dim*4, activation="gelu", batch_first=True, norm_first=True)
+ self.blocks = nn.TransformerEncoder(encoder_layer, num_layers=depth)
+ self.norm = nn.LayerNorm(embed_dim)
+
+ def forward(self, x):
+ x = self.patch_embed(x)
+ B, T, N, D = x.shape
+ x = x + self.time_embed
+ x = x.reshape(B, T*N, D)
+ spatial_pos = self.pos_embed[:, :, 1:, :].expand(B, T, -1, -1).reshape(B, T*N, D)
+ x = x + spatial_pos
+ cls_token = self.cls_token.expand(B, -1, -1, -1).reshape(B, 1, D) + self.pos_embed[:, :, 0, :].expand(B, 1, D)
+ x = torch.cat((cls_token, x), dim=1)
+ x = self.blocks(x)
+ x = self.norm(x)
+ return x
+
+class SatMAESegmentation(nn.Module):
+ def __init__(self, num_frames=3, embed_dim=768):
+ super().__init__()
+ print(" Constructing Manual SatMAE (Fair Comparison)...")
+ self.backbone = SatMAEBackbone(num_frames=num_frames, embed_dim=embed_dim)
+
+ try:
+ print(" Loading Google ViT Weights...")
+ p = hf_hub_download("google/vit-base-patch16-224", "pytorch_model.bin")
+ sd = torch.load(p, map_location='cpu')
+ w = sd['vit.embeddings.patch_embeddings.projection.weight']
+ new_w = torch.zeros(768, 8, 16, 16)
+ new_w[:, :3] = w
+ new_w[:, 3:] = w.mean(1, keepdim=True).repeat(1, 5, 1, 1)
+ self.backbone.patch_embed.proj.weight.data = new_w
+ self.backbone.patch_embed.proj.bias.data = sd['vit.embeddings.patch_embeddings.projection.bias']
+ print(" Weights Adapted.")
+ except:
+ print(" Weights missing, using random init.")
+
+ # FREEZING (Code 1 Logic)
+ print(" Freezing Transformer Backbone...")
+ for param in self.backbone.parameters():
+ param.requires_grad = False
+
+ self.backbone.time_embed.requires_grad = True
+ self.backbone.patch_embed.proj.weight.requires_grad = True
+ print(" Unfrozen: Patch Embeddings + Time Embeddings")
+
+ # Decoder
+ self.temporal_agg = nn.Conv2d(embed_dim * num_frames, embed_dim, kernel_size=1)
+ self.decoder = nn.Sequential(
+ nn.Upsample(scale_factor=2), nn.Conv2d(embed_dim, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(256, 128, 3, 1, 1), nn.BatchNorm2d(128), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(128, 64, 3, 1, 1), nn.BatchNorm2d(64), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(64, 32, 3, 1, 1), nn.BatchNorm2d(32), nn.GELU(),
+ nn.Conv2d(32, 1, 1)
+ )
+
+ def forward(self, x):
+ features = self.backbone(x)[:, 1:, :]
+ B, L, D = features.shape
+ features = features.view(B, 3, 14, 14, D).permute(0, 4, 1, 2, 3).flatten(1, 2)
+ features = self.temporal_agg(features)
+ return self.decoder(features)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# TRAINING LOOP (500 EPOCHS)
+
+
+model = SatMAESegmentation().to(device)
+criterion = CompoundLoss()
+optimizer = optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=LR_MAX)
+scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=50, T_mult=2, eta_min=LR_MIN)
+
+# Dataset Split
+ds = TensorDataset(X_data, y_data)
+tr_sz = int(0.85 * len(ds))
+t_ds, v_ds = random_split(ds, [tr_sz, len(ds)-tr_sz])
+train_loader = DataLoader(t_ds, BATCH_SIZE, shuffle=True)
+val_loader = DataLoader(v_ds, BATCH_SIZE, shuffle=False)
+
+# CHECKPOINT MANAGER
+CHECKPOINT_PATH = SAVE_DIR + "checkpoint.pth"
+start_epoch = 0
+history = {'train_loss': [], 'val_loss': []}
+best_loss = float('inf')
+
+# 1. Try to Resume
+if os.path.exists(CHECKPOINT_PATH):
+ print(f" Checkpoint found! Resuming from Drive...")
+ checkpoint = torch.load(CHECKPOINT_PATH)
+ model.load_state_dict(checkpoint['model_state_dict'])
+ optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
+ scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
+ start_epoch = checkpoint['epoch'] + 1
+ history = checkpoint['history']
+ best_loss = checkpoint['best_loss']
+ print(f" Resuming from Epoch {start_epoch}")
+else:
+ print(" No checkpoint found. Starting from scratch.")
+
+# 2. Training Loop
+print(f" Starting/Resuming Training ({EPOCHS} Epochs total)...")
+
+for ep in range(start_epoch, EPOCHS):
+ model.train()
+ train_loss = 0
+
+ for x, y in train_loader:
+ x, y = x.to(device), y.to(device)
+ optimizer.zero_grad()
+ preds = model(x)
+ loss = criterion(preds, y)
+ loss.backward()
+ optimizer.step()
+ train_loss += loss.item()
+
+ scheduler.step()
+
+ # Validation
+ model.eval()
+ val_loss = 0
+ with torch.no_grad():
+ for x, y in val_loader:
+ x, y = x.to(device), y.to(device)
+ preds = model(x)
+ val_loss += criterion(preds, y).item()
+
+ avg_t = train_loss / len(train_loader)
+ avg_v = val_loss / len(val_loader)
+ history['train_loss'].append(avg_t)
+ history['val_loss'].append(avg_v)
+
+ # Printing
+ if (ep+1) % 1 == 0:
+ print(f"Ep {ep+1}/{EPOCHS} | Train: {avg_t:.4f} | Val: {avg_v:.4f}")
+
+ # 3. SAVE BEST MODEL
+ if avg_v < best_loss:
+ best_loss = avg_v
+ torch.save(model.state_dict(), SAVE_DIR + "SatMAE_500_BEST.pth")
+ print(f" Best Model Saved (Loss: {avg_v:.4f})")
+
+ # 4. SAVE CHECKPOINT (Every 5 Epochs)
+
+ if (ep+1) % 5 == 0:
+ torch.save({
+ 'epoch': ep,
+ 'model_state_dict': model.state_dict(),
+ 'optimizer_state_dict': optimizer.state_dict(),
+ 'scheduler_state_dict': scheduler.state_dict(),
+ 'history': history,
+ 'best_loss': best_loss
+ }, CHECKPOINT_PATH)
+ print(" Checkpoint saved.")
+
+print(" Training Complete!")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructing Manual SatMAE (Fair Comparison)... + Loading Google ViT Weights... + Weights Adapted. + Freezing Transformer Backbone... + Unfrozen: Patch Embeddings + Time Embeddings + No checkpoint found. Starting from scratch. + Starting/Resuming Training (500 Epochs total)... +Ep 1/500 | Train: 1.1114 | Val: 1.1168 + Best Model Saved (Loss: 1.1168) +Ep 2/500 | Train: 0.8696 | Val: 1.1109 + Best Model Saved (Loss: 1.1109) +Ep 3/500 | Train: 0.6986 | Val: 1.0248 + Best Model Saved (Loss: 1.0248) +Ep 4/500 | Train: 0.6150 | Val: 0.9220 + Best Model Saved (Loss: 0.9220) +Ep 5/500 | Train: 0.5689 | Val: 0.8183 + Best Model Saved (Loss: 0.8183) + Checkpoint saved. +Ep 6/500 | Train: 0.5597 | Val: 0.7745 + Best Model Saved (Loss: 0.7745) +Ep 7/500 | Train: 0.5206 | Val: 0.7182 + Best Model Saved (Loss: 0.7182) +Ep 8/500 | Train: 0.5093 | Val: 0.6697 + Best Model Saved (Loss: 0.6697) +Ep 9/500 | Train: 0.4956 | Val: 0.6222 + Best Model Saved (Loss: 0.6222) +Ep 10/500 | Train: 0.4891 | Val: 0.5733 + Best Model Saved (Loss: 0.5733) + Checkpoint saved. +Ep 11/500 | Train: 0.4771 | Val: 0.5191 + Best Model Saved (Loss: 0.5191) +Ep 12/500 | Train: 0.4692 | Val: 0.5000 + Best Model Saved (Loss: 0.5000) +Ep 13/500 | Train: 0.4654 | Val: 0.4646 + Best Model Saved (Loss: 0.4646) +Ep 14/500 | Train: 0.4631 | Val: 0.4598 + Best Model Saved (Loss: 0.4598) +Ep 15/500 | Train: 0.4525 | Val: 0.4977 + Checkpoint saved. +Ep 16/500 | Train: 0.4547 | Val: 0.4641 +Ep 17/500 | Train: 0.4533 | Val: 0.4419 + Best Model Saved (Loss: 0.4419) +Ep 18/500 | Train: 0.4444 | Val: 0.4938 +Ep 19/500 | Train: 0.4441 | Val: 0.4279 + Best Model Saved (Loss: 0.4279) +Ep 20/500 | Train: 0.4376 | Val: 0.4903 + Checkpoint saved. +Ep 21/500 | Train: 0.4354 | Val: 0.4694 +Ep 22/500 | Train: 0.4496 | Val: 0.4661 +Ep 23/500 | Train: 0.4295 | Val: 0.5341 +Ep 24/500 | Train: 0.4311 | Val: 0.4292 +Ep 25/500 | Train: 0.4285 | Val: 0.4372 + Checkpoint saved. +Ep 26/500 | Train: 0.4285 | Val: 0.4743 +Ep 27/500 | Train: 0.4293 | Val: 0.4789 +Ep 28/500 | Train: 0.4258 | Val: 0.4527 +Ep 29/500 | Train: 0.4195 | Val: 0.4505 +Ep 30/500 | Train: 0.4312 | Val: 0.4238 + Best Model Saved (Loss: 0.4238) + Checkpoint saved. +Ep 31/500 | Train: 0.4147 | Val: 0.4320 +Ep 32/500 | Train: 0.4208 | Val: 0.4524 +Ep 33/500 | Train: 0.4175 | Val: 0.4600 +Ep 34/500 | Train: 0.4128 | Val: 0.4510 +Ep 35/500 | Train: 0.4153 | Val: 0.4250 + Checkpoint saved. +Ep 36/500 | Train: 0.4117 | Val: 0.4201 + Best Model Saved (Loss: 0.4201) +Ep 37/500 | Train: 0.4137 | Val: 0.4342 +Ep 38/500 | Train: 0.4120 | Val: 0.4207 +Ep 39/500 | Train: 0.4093 | Val: 0.4157 + Best Model Saved (Loss: 0.4157) +Ep 40/500 | Train: 0.4136 | Val: 0.4196 + Checkpoint saved. +Ep 41/500 | Train: 0.4082 | Val: 0.4242 +Ep 42/500 | Train: 0.4090 | Val: 0.4214 +Ep 43/500 | Train: 0.4183 | Val: 0.4144 + Best Model Saved (Loss: 0.4144) +Ep 44/500 | Train: 0.4092 | Val: 0.4107 + Best Model Saved (Loss: 0.4107) +Ep 45/500 | Train: 0.4095 | Val: 0.4126 + Checkpoint saved. +Ep 46/500 | Train: 0.4079 | Val: 0.4152 +Ep 47/500 | Train: 0.4053 | Val: 0.4157 +Ep 48/500 | Train: 0.4059 | Val: 0.4175 +Ep 49/500 | Train: 0.4074 | Val: 0.4183 +Ep 50/500 | Train: 0.4061 | Val: 0.4177 + Checkpoint saved. +Ep 51/500 | Train: 0.4084 | Val: 0.4795 +Ep 52/500 | Train: 0.4041 | Val: 0.3877 + Best Model Saved (Loss: 0.3877) +Ep 53/500 | Train: 0.4012 | Val: 0.3787 + Best Model Saved (Loss: 0.3787) +Ep 54/500 | Train: 0.4077 | Val: 0.4620 +Ep 55/500 | Train: 0.3927 | Val: 0.3972 + Checkpoint saved. +Ep 56/500 | Train: 0.4039 | Val: 0.5527 +Ep 57/500 | Train: 0.3897 | Val: 0.3761 + Best Model Saved (Loss: 0.3761) +Ep 58/500 | Train: 0.3915 | Val: 0.5613 +Ep 59/500 | Train: 0.3899 | Val: 0.4326 +Ep 60/500 | Train: 0.3834 | Val: 0.5095 + Checkpoint saved. +Ep 61/500 | Train: 0.3803 | Val: 0.3627 + Best Model Saved (Loss: 0.3627) +Ep 62/500 | Train: 0.3775 | Val: 0.4258 +Ep 63/500 | Train: 0.3723 | Val: 0.3942 +Ep 64/500 | Train: 0.3729 | Val: 0.4865 +Ep 65/500 | Train: 0.3745 | Val: 0.4007 + Checkpoint saved. +Ep 66/500 | Train: 0.3656 | Val: 0.4485 +Ep 67/500 | Train: 0.3655 | Val: 0.3595 + Best Model Saved (Loss: 0.3595) +Ep 68/500 | Train: 0.3636 | Val: 0.4382 +Ep 69/500 | Train: 0.3637 | Val: 0.4754 +Ep 70/500 | Train: 0.3640 | Val: 0.4012 + Checkpoint saved. +Ep 71/500 | Train: 0.3646 | Val: 0.4743 +Ep 72/500 | Train: 0.3541 | Val: 0.3739 +Ep 73/500 | Train: 0.3535 | Val: 0.4019 +Ep 74/500 | Train: 0.3487 | Val: 0.3665 +Ep 75/500 | Train: 0.3577 | Val: 0.3559 + Best Model Saved (Loss: 0.3559) + Checkpoint saved. +Ep 76/500 | Train: 0.3438 | Val: 0.4268 +Ep 77/500 | Train: 0.3441 | Val: 0.4200 +Ep 78/500 | Train: 0.3387 | Val: 0.3558 + Best Model Saved (Loss: 0.3558) +Ep 79/500 | Train: 0.3376 | Val: 0.4020 +Ep 80/500 | Train: 0.3389 | Val: 0.3576 + Checkpoint saved. +Ep 81/500 | Train: 0.3413 | Val: 0.3501 + Best Model Saved (Loss: 0.3501) +Ep 82/500 | Train: 0.3420 | Val: 0.3802 +Ep 83/500 | Train: 0.3352 | Val: 0.4098 +Ep 84/500 | Train: 0.3353 | Val: 0.4366 +Ep 85/500 | Train: 0.3295 | Val: 0.3690 + Checkpoint saved. +Ep 86/500 | Train: 0.3377 | Val: 0.3950 +Ep 87/500 | Train: 0.3259 | Val: 0.4308 +Ep 88/500 | Train: 0.3293 | Val: 0.3573 +Ep 89/500 | Train: 0.3259 | Val: 0.3768 +Ep 90/500 | Train: 0.3260 | Val: 0.3433 + Best Model Saved (Loss: 0.3433) + Checkpoint saved. +Ep 91/500 | Train: 0.3191 | Val: 0.3340 + Best Model Saved (Loss: 0.3340) +Ep 92/500 | Train: 0.3202 | Val: 0.3521 +Ep 93/500 | Train: 0.3288 | Val: 0.4079 +Ep 94/500 | Train: 0.3173 | Val: 0.3358 +Ep 95/500 | Train: 0.3168 | Val: 0.3593 + Checkpoint saved. +Ep 96/500 | Train: 0.3191 | Val: 0.4058 +Ep 97/500 | Train: 0.3220 | Val: 0.3680 +Ep 98/500 | Train: 0.3217 | Val: 0.3408 +Ep 99/500 | Train: 0.3131 | Val: 0.3346 +Ep 100/500 | Train: 0.3103 | Val: 0.3887 + Checkpoint saved. +Ep 101/500 | Train: 0.3078 | Val: 0.3531 +Ep 102/500 | Train: 0.3079 | Val: 0.3657 +Ep 103/500 | Train: 0.3101 | Val: 0.3541 +Ep 104/500 | Train: 0.3063 | Val: 0.3655 +Ep 105/500 | Train: 0.3048 | Val: 0.3544 + Checkpoint saved. +Ep 106/500 | Train: 0.3052 | Val: 0.3589 +Ep 107/500 | Train: 0.3015 | Val: 0.3447 +Ep 108/500 | Train: 0.3148 | Val: 0.3484 +Ep 109/500 | Train: 0.3075 | Val: 0.3404 +Ep 110/500 | Train: 0.3183 | Val: 0.3641 + Checkpoint saved. +Ep 111/500 | Train: 0.3001 | Val: 0.3567 +Ep 112/500 | Train: 0.2987 | Val: 0.3647 +Ep 113/500 | Train: 0.3124 | Val: 0.3665 +Ep 114/500 | Train: 0.3009 | Val: 0.3705 +Ep 115/500 | Train: 0.2965 | Val: 0.3556 + Checkpoint saved. +Ep 116/500 | Train: 0.2965 | Val: 0.3529 +Ep 117/500 | Train: 0.2961 | Val: 0.3610 +Ep 118/500 | Train: 0.3023 | Val: 0.3618 +Ep 119/500 | Train: 0.2954 | Val: 0.3724 +Ep 120/500 | Train: 0.3081 | Val: 0.3687 + Checkpoint saved. +Ep 121/500 | Train: 0.3000 | Val: 0.3595 +Ep 122/500 | Train: 0.2936 | Val: 0.3521 +Ep 123/500 | Train: 0.2953 | Val: 0.3529 +Ep 124/500 | Train: 0.2979 | Val: 0.3469 +Ep 125/500 | Train: 0.2946 | Val: 0.3406 + Checkpoint saved. +Ep 126/500 | Train: 0.3004 | Val: 0.3479 +Ep 127/500 | Train: 0.2920 | Val: 0.3631 +Ep 128/500 | Train: 0.2985 | Val: 0.3530 +Ep 129/500 | Train: 0.2932 | Val: 0.3477 +Ep 130/500 | Train: 0.2913 | Val: 0.3561 + Checkpoint saved. +Ep 131/500 | Train: 0.2952 | Val: 0.3579 +Ep 132/500 | Train: 0.2944 | Val: 0.3448 +Ep 133/500 | Train: 0.2901 | Val: 0.3402 +Ep 134/500 | Train: 0.3030 | Val: 0.3467 +Ep 135/500 | Train: 0.2907 | Val: 0.3468 + Checkpoint saved. +Ep 136/500 | Train: 0.2896 | Val: 0.3475 +Ep 137/500 | Train: 0.2921 | Val: 0.3487 +Ep 138/500 | Train: 0.2892 | Val: 0.3456 +Ep 139/500 | Train: 0.2903 | Val: 0.3441 +Ep 140/500 | Train: 0.2936 | Val: 0.3443 + Checkpoint saved. +Ep 141/500 | Train: 0.2895 | Val: 0.3418 +Ep 142/500 | Train: 0.2906 | Val: 0.3434 +Ep 143/500 | Train: 0.2904 | Val: 0.3429 +Ep 144/500 | Train: 0.2941 | Val: 0.3406 +Ep 145/500 | Train: 0.2891 | Val: 0.3417 + Checkpoint saved. +Ep 146/500 | Train: 0.2955 | Val: 0.3395 +Ep 147/500 | Train: 0.2908 | Val: 0.3390 +Ep 148/500 | Train: 0.2905 | Val: 0.3394 +Ep 149/500 | Train: 0.2935 | Val: 0.3417 +Ep 150/500 | Train: 0.2938 | Val: 0.3431 + Checkpoint saved. +Ep 151/500 | Train: 0.2925 | Val: 0.4225 +Ep 152/500 | Train: 0.2983 | Val: 0.5710 +Ep 153/500 | Train: 0.2977 | Val: 0.4190 +Ep 154/500 | Train: 0.3000 | Val: 0.5093 +Ep 155/500 | Train: 0.2994 | Val: 0.4821 + Checkpoint saved. +Ep 156/500 | Train: 0.2939 | Val: 0.4223 +Ep 157/500 | Train: 0.3051 | Val: 0.7092 +Ep 158/500 | Train: 0.3012 | Val: 0.4625 +Ep 159/500 | Train: 0.2998 | Val: 0.3637 +Ep 160/500 | Train: 0.2903 | Val: 0.3580 + Checkpoint saved. +Ep 161/500 | Train: 0.2904 | Val: 0.3386 +Ep 162/500 | Train: 0.2882 | Val: 0.3722 +Ep 163/500 | Train: 0.2793 | Val: 0.3267 + Best Model Saved (Loss: 0.3267) +Ep 164/500 | Train: 0.2801 | Val: 0.3249 + Best Model Saved (Loss: 0.3249) +Ep 165/500 | Train: 0.2784 | Val: 0.3775 + Checkpoint saved. +Ep 166/500 | Train: 0.2778 | Val: 0.3718 +Ep 167/500 | Train: 0.2846 | Val: 0.3043 + Best Model Saved (Loss: 0.3043) +Ep 168/500 | Train: 0.2694 | Val: 0.4308 +Ep 169/500 | Train: 0.2696 | Val: 0.3629 +Ep 170/500 | Train: 0.2669 | Val: 0.3225 + Checkpoint saved. +Ep 171/500 | Train: 0.2667 | Val: 0.3415 +Ep 172/500 | Train: 0.2622 | Val: 0.3694 +Ep 173/500 | Train: 0.2651 | Val: 0.3314 +Ep 174/500 | Train: 0.2636 | Val: 0.3272 +Ep 175/500 | Train: 0.2633 | Val: 0.3695 + Checkpoint saved. +Ep 176/500 | Train: 0.2639 | Val: 0.3303 +Ep 177/500 | Train: 0.2551 | Val: 0.3609 +Ep 178/500 | Train: 0.2586 | Val: 0.3584 +Ep 179/500 | Train: 0.2529 | Val: 0.3316 +Ep 180/500 | Train: 0.2522 | Val: 0.3269 + Checkpoint saved. +Ep 181/500 | Train: 0.2498 | Val: 0.3092 +Ep 182/500 | Train: 0.2488 | Val: 0.3417 +Ep 183/500 | Train: 0.2501 | Val: 0.2986 + Best Model Saved (Loss: 0.2986) +Ep 184/500 | Train: 0.2498 | Val: 0.3138 +Ep 185/500 | Train: 0.2461 | Val: 0.3670 + Checkpoint saved. +Ep 186/500 | Train: 0.2467 | Val: 0.3077 +Ep 187/500 | Train: 0.2443 | Val: 0.3068 +Ep 188/500 | Train: 0.2490 | Val: 0.3402 +Ep 189/500 | Train: 0.2414 | Val: 0.3612 +Ep 190/500 | Train: 0.2422 | Val: 0.2898 + Best Model Saved (Loss: 0.2898) + Checkpoint saved. +Ep 191/500 | Train: 0.2474 | Val: 0.3292 +Ep 192/500 | Train: 0.2404 | Val: 0.3307 +Ep 193/500 | Train: 0.2393 | Val: 0.3076 +Ep 194/500 | Train: 0.2378 | Val: 0.3306 +Ep 195/500 | Train: 0.2381 | Val: 0.3348 + Checkpoint saved. +Ep 196/500 | Train: 0.2336 | Val: 0.2969 +Ep 197/500 | Train: 0.2328 | Val: 0.3303 +Ep 198/500 | Train: 0.2319 | Val: 0.3225 +Ep 199/500 | Train: 0.2328 | Val: 0.3285 +Ep 200/500 | Train: 0.2323 | Val: 0.3153 + Checkpoint saved. +Ep 201/500 | Train: 0.2305 | Val: 0.3323 +Ep 202/500 | Train: 0.2265 | Val: 0.3069 +Ep 203/500 | Train: 0.2298 | Val: 0.3119 +Ep 204/500 | Train: 0.2299 | Val: 0.3350 +Ep 205/500 | Train: 0.2236 | Val: 0.2993 + Checkpoint saved. +Ep 206/500 | Train: 0.2225 | Val: 0.3426 +Ep 207/500 | Train: 0.2210 | Val: 0.3180 +Ep 208/500 | Train: 0.2266 | Val: 0.3280 +Ep 209/500 | Train: 0.2328 | Val: 0.3045 +Ep 210/500 | Train: 0.2230 | Val: 0.3468 + Checkpoint saved. +Ep 211/500 | Train: 0.2185 | Val: 0.3443 +Ep 212/500 | Train: 0.2236 | Val: 0.3191 +Ep 213/500 | Train: 0.2223 | Val: 0.3124 +Ep 214/500 | Train: 0.2163 | Val: 0.3072 +Ep 215/500 | Train: 0.2234 | Val: 0.3120 + Checkpoint saved. +Ep 216/500 | Train: 0.2139 | Val: 0.3480 +Ep 217/500 | Train: 0.2185 | Val: 0.3261 +Ep 218/500 | Train: 0.2129 | Val: 0.2990 +Ep 219/500 | Train: 0.2155 | Val: 0.3008 +Ep 220/500 | Train: 0.2129 | Val: 0.3193 + Checkpoint saved. +Ep 221/500 | Train: 0.2107 | Val: 0.3052 +Ep 222/500 | Train: 0.2114 | Val: 0.3280 +Ep 223/500 | Train: 0.2090 | Val: 0.3011 +Ep 224/500 | Train: 0.2135 | Val: 0.3166 +Ep 225/500 | Train: 0.2125 | Val: 0.2965 + Checkpoint saved. +Ep 226/500 | Train: 0.2086 | Val: 0.2948 +Ep 227/500 | Train: 0.2178 | Val: 0.3173 +Ep 228/500 | Train: 0.2103 | Val: 0.3073 +Ep 229/500 | Train: 0.2037 | Val: 0.3181 +Ep 230/500 | Train: 0.2101 | Val: 0.2933 + Checkpoint saved. +Ep 231/500 | Train: 0.2050 | Val: 0.3185 +Ep 232/500 | Train: 0.2017 | Val: 0.3234 +Ep 233/500 | Train: 0.2026 | Val: 0.2995 +Ep 234/500 | Train: 0.2012 | Val: 0.2922 +Ep 235/500 | Train: 0.2099 | Val: 0.3007 + Checkpoint saved. +Ep 236/500 | Train: 0.2077 | Val: 0.3018 +Ep 237/500 | Train: 0.1993 | Val: 0.3036 +Ep 238/500 | Train: 0.2015 | Val: 0.3091 +Ep 239/500 | Train: 0.2056 | Val: 0.2945 +Ep 240/500 | Train: 0.1968 | Val: 0.2991 + Checkpoint saved. +Ep 241/500 | Train: 0.1958 | Val: 0.2946 +Ep 242/500 | Train: 0.2000 | Val: 0.2966 +Ep 243/500 | Train: 0.2028 | Val: 0.3035 +Ep 244/500 | Train: 0.1944 | Val: 0.2952 +Ep 245/500 | Train: 0.1934 | Val: 0.2998 + Checkpoint saved. +Ep 246/500 | Train: 0.2005 | Val: 0.3116 +Ep 247/500 | Train: 0.1943 | Val: 0.3028 +Ep 248/500 | Train: 0.1917 | Val: 0.2926 +Ep 249/500 | Train: 0.1919 | Val: 0.2993 +Ep 250/500 | Train: 0.1964 | Val: 0.3010 + Checkpoint saved. +Ep 251/500 | Train: 0.1932 | Val: 0.2933 +Ep 252/500 | Train: 0.1933 | Val: 0.2936 +Ep 253/500 | Train: 0.1972 | Val: 0.3020 +Ep 254/500 | Train: 0.1891 | Val: 0.3129 +Ep 255/500 | Train: 0.1890 | Val: 0.2939 + Checkpoint saved. +Ep 256/500 | Train: 0.2050 | Val: 0.2915 +Ep 257/500 | Train: 0.1914 | Val: 0.2981 +Ep 258/500 | Train: 0.1958 | Val: 0.3151 +Ep 259/500 | Train: 0.1913 | Val: 0.2889 + Best Model Saved (Loss: 0.2889) +Ep 260/500 | Train: 0.1901 | Val: 0.2993 + Checkpoint saved. +Ep 261/500 | Train: 0.1989 | Val: 0.2971 +Ep 262/500 | Train: 0.1867 | Val: 0.2913 +Ep 263/500 | Train: 0.1869 | Val: 0.2866 + Best Model Saved (Loss: 0.2866) +Ep 264/500 | Train: 0.1913 | Val: 0.3032 +Ep 265/500 | Train: 0.1889 | Val: 0.2914 + Checkpoint saved. +Ep 266/500 | Train: 0.1849 | Val: 0.2875 +Ep 267/500 | Train: 0.1898 | Val: 0.2952 +Ep 268/500 | Train: 0.1840 | Val: 0.2966 +Ep 269/500 | Train: 0.1905 | Val: 0.3023 +Ep 270/500 | Train: 0.1845 | Val: 0.2958 + Checkpoint saved. +Ep 271/500 | Train: 0.1868 | Val: 0.2919 +Ep 272/500 | Train: 0.1905 | Val: 0.2874 +Ep 273/500 | Train: 0.1832 | Val: 0.2984 +Ep 274/500 | Train: 0.1836 | Val: 0.2972 +Ep 275/500 | Train: 0.1868 | Val: 0.2848 + Best Model Saved (Loss: 0.2848) + Checkpoint saved. +Ep 276/500 | Train: 0.1865 | Val: 0.2960 +Ep 277/500 | Train: 0.1818 | Val: 0.2859 +Ep 278/500 | Train: 0.1877 | Val: 0.2827 + Best Model Saved (Loss: 0.2827) +Ep 279/500 | Train: 0.1799 | Val: 0.2836 +Ep 280/500 | Train: 0.1796 | Val: 0.2894 + Checkpoint saved. +Ep 281/500 | Train: 0.1807 | Val: 0.2935 +Ep 282/500 | Train: 0.1815 | Val: 0.2890 +Ep 283/500 | Train: 0.1929 | Val: 0.2826 + Best Model Saved (Loss: 0.2826) +Ep 284/500 | Train: 0.1837 | Val: 0.2833 +Ep 285/500 | Train: 0.1804 | Val: 0.2901 + Checkpoint saved. +Ep 286/500 | Train: 0.1794 | Val: 0.2932 +Ep 287/500 | Train: 0.1809 | Val: 0.2931 +Ep 288/500 | Train: 0.1803 | Val: 0.2926 +Ep 289/500 | Train: 0.1812 | Val: 0.2841 +Ep 290/500 | Train: 0.1797 | Val: 0.2809 + Best Model Saved (Loss: 0.2809) + Checkpoint saved. +Ep 291/500 | Train: 0.1815 | Val: 0.2899 +Ep 292/500 | Train: 0.1794 | Val: 0.2872 +Ep 293/500 | Train: 0.1823 | Val: 0.2838 +Ep 294/500 | Train: 0.1801 | Val: 0.2924 +Ep 295/500 | Train: 0.1794 | Val: 0.2952 + Checkpoint saved. +Ep 296/500 | Train: 0.1771 | Val: 0.2820 +Ep 297/500 | Train: 0.1802 | Val: 0.2826 +Ep 298/500 | Train: 0.1792 | Val: 0.2877 +Ep 299/500 | Train: 0.1800 | Val: 0.2875 +Ep 300/500 | Train: 0.1779 | Val: 0.2866 + Checkpoint saved. +Ep 301/500 | Train: 0.1756 | Val: 0.2871 +Ep 302/500 | Train: 0.1833 | Val: 0.2837 +Ep 303/500 | Train: 0.1753 | Val: 0.2852 +Ep 304/500 | Train: 0.1801 | Val: 0.2896 +Ep 305/500 | Train: 0.1791 | Val: 0.2833 + Checkpoint saved. +Ep 306/500 | Train: 0.1752 | Val: 0.2805 + Best Model Saved (Loss: 0.2805) +Ep 307/500 | Train: 0.1752 | Val: 0.2866 +Ep 308/500 | Train: 0.1768 | Val: 0.2899 +Ep 309/500 | Train: 0.1767 | Val: 0.2872 +Ep 310/500 | Train: 0.1814 | Val: 0.2877 + Checkpoint saved. +Ep 311/500 | Train: 0.1743 | Val: 0.2877 +Ep 312/500 | Train: 0.1745 | Val: 0.2885 +Ep 313/500 | Train: 0.1753 | Val: 0.2866 +Ep 314/500 | Train: 0.1866 | Val: 0.2875 +Ep 315/500 | Train: 0.1743 | Val: 0.2871 + Checkpoint saved. +Ep 316/500 | Train: 0.1811 | Val: 0.2912 +Ep 317/500 | Train: 0.1751 | Val: 0.2889 +Ep 318/500 | Train: 0.1763 | Val: 0.2836 +Ep 319/500 | Train: 0.1821 | Val: 0.2858 +Ep 320/500 | Train: 0.1780 | Val: 0.2875 + Checkpoint saved. +Ep 321/500 | Train: 0.1744 | Val: 0.2852 +Ep 322/500 | Train: 0.1737 | Val: 0.2828 +Ep 323/500 | Train: 0.1823 | Val: 0.2821 +Ep 324/500 | Train: 0.1732 | Val: 0.2831 +Ep 325/500 | Train: 0.1739 | Val: 0.2850 + Checkpoint saved. +Ep 326/500 | Train: 0.1738 | Val: 0.2851 +Ep 327/500 | Train: 0.1770 | Val: 0.2850 +Ep 328/500 | Train: 0.1729 | Val: 0.2839 +Ep 329/500 | Train: 0.1734 | Val: 0.2840 +Ep 330/500 | Train: 0.1762 | Val: 0.2842 + Checkpoint saved. +Ep 331/500 | Train: 0.1770 | Val: 0.2842 +Ep 332/500 | Train: 0.1799 | Val: 0.2848 +Ep 333/500 | Train: 0.1757 | Val: 0.2856 +Ep 334/500 | Train: 0.1757 | Val: 0.2849 +Ep 335/500 | Train: 0.1735 | Val: 0.2842 + Checkpoint saved. +Ep 336/500 | Train: 0.1764 | Val: 0.2854 +Ep 337/500 | Train: 0.1746 | Val: 0.2846 +Ep 338/500 | Train: 0.1752 | Val: 0.2847 +Ep 339/500 | Train: 0.1783 | Val: 0.2840 +Ep 340/500 | Train: 0.1760 | Val: 0.2855 + Checkpoint saved. +Ep 341/500 | Train: 0.1726 | Val: 0.2846 +Ep 342/500 | Train: 0.1744 | Val: 0.2843 +Ep 343/500 | Train: 0.1728 | Val: 0.2839 +Ep 344/500 | Train: 0.1729 | Val: 0.2843 +Ep 345/500 | Train: 0.1731 | Val: 0.2833 + Checkpoint saved. +Ep 346/500 | Train: 0.1736 | Val: 0.2826 +Ep 347/500 | Train: 0.1725 | Val: 0.2819 +Ep 348/500 | Train: 0.1738 | Val: 0.2817 +Ep 349/500 | Train: 0.1726 | Val: 0.2815 +Ep 350/500 | Train: 0.1752 | Val: 0.2802 + Best Model Saved (Loss: 0.2802) + Checkpoint saved. +Ep 351/500 | Train: 0.1750 | Val: 0.2952 +Ep 352/500 | Train: 0.1748 | Val: 0.3146 +Ep 353/500 | Train: 0.1779 | Val: 0.2868 +Ep 354/500 | Train: 0.1748 | Val: 0.2773 + Best Model Saved (Loss: 0.2773) +Ep 355/500 | Train: 0.1739 | Val: 0.3361 + Checkpoint saved. +Ep 356/500 | Train: 0.1811 | Val: 0.3211 +Ep 357/500 | Train: 0.1744 | Val: 0.3458 +Ep 358/500 | Train: 0.1732 | Val: 0.3534 +Ep 359/500 | Train: 0.1737 | Val: 0.3498 +Ep 360/500 | Train: 0.1759 | Val: 0.3540 + Checkpoint saved. +Ep 361/500 | Train: 0.1746 | Val: 0.3281 +Ep 362/500 | Train: 0.1723 | Val: 0.2979 +Ep 363/500 | Train: 0.1717 | Val: 0.3168 +Ep 364/500 | Train: 0.1812 | Val: 0.2975 +Ep 365/500 | Train: 0.1732 | Val: 0.3157 + Checkpoint saved. +Ep 366/500 | Train: 0.1757 | Val: 0.3168 +Ep 367/500 | Train: 0.1744 | Val: 0.2959 +Ep 368/500 | Train: 0.1727 | Val: 0.2839 +Ep 369/500 | Train: 0.1844 | Val: 0.5944 +Ep 370/500 | Train: 0.1954 | Val: 0.3088 + Checkpoint saved. +Ep 371/500 | Train: 0.1828 | Val: 0.2854 +Ep 372/500 | Train: 0.1807 | Val: 0.3444 +Ep 373/500 | Train: 0.1735 | Val: 0.3101 +Ep 374/500 | Train: 0.1686 | Val: 0.3083 +Ep 375/500 | Train: 0.1698 | Val: 0.2804 + Checkpoint saved. +Ep 376/500 | Train: 0.1676 | Val: 0.3046 +Ep 377/500 | Train: 0.1633 | Val: 0.2738 + Best Model Saved (Loss: 0.2738) +Ep 378/500 | Train: 0.1614 | Val: 0.2691 + Best Model Saved (Loss: 0.2691) +Ep 379/500 | Train: 0.1636 | Val: 0.2825 +Ep 380/500 | Train: 0.1589 | Val: 0.2725 + Checkpoint saved. +Ep 381/500 | Train: 0.1577 | Val: 0.3138 +Ep 382/500 | Train: 0.1590 | Val: 0.2730 +Ep 383/500 | Train: 0.1553 | Val: 0.2949 +Ep 384/500 | Train: 0.1558 | Val: 0.2775 +Ep 385/500 | Train: 0.1612 | Val: 0.2874 + Checkpoint saved. +Ep 386/500 | Train: 0.1538 | Val: 0.2974 +Ep 387/500 | Train: 0.1559 | Val: 0.2758 +Ep 388/500 | Train: 0.1535 | Val: 0.2827 +Ep 389/500 | Train: 0.1530 | Val: 0.2875 +Ep 390/500 | Train: 0.1508 | Val: 0.2762 + Checkpoint saved. +Ep 391/500 | Train: 0.1489 | Val: 0.2879 +Ep 392/500 | Train: 0.1478 | Val: 0.2774 +Ep 393/500 | Train: 0.1468 | Val: 0.2664 + Best Model Saved (Loss: 0.2664) +Ep 394/500 | Train: 0.1466 | Val: 0.2755 +Ep 395/500 | Train: 0.1452 | Val: 0.2723 + Checkpoint saved. +Ep 396/500 | Train: 0.1444 | Val: 0.2684 +Ep 397/500 | Train: 0.1446 | Val: 0.2791 +Ep 398/500 | Train: 0.1470 | Val: 0.2735 +Ep 399/500 | Train: 0.1482 | Val: 0.2794 +Ep 400/500 | Train: 0.1433 | Val: 0.2800 + Checkpoint saved. +Ep 401/500 | Train: 0.1423 | Val: 0.2628 + Best Model Saved (Loss: 0.2628) +Ep 402/500 | Train: 0.1423 | Val: 0.2746 +Ep 403/500 | Train: 0.1426 | Val: 0.2735 +Ep 404/500 | Train: 0.1406 | Val: 0.2689 +Ep 405/500 | Train: 0.1433 | Val: 0.2813 + Checkpoint saved. +Ep 406/500 | Train: 0.1430 | Val: 0.2679 +Ep 407/500 | Train: 0.1457 | Val: 0.2743 +Ep 408/500 | Train: 0.1458 | Val: 0.2878 +Ep 409/500 | Train: 0.1414 | Val: 0.2888 +Ep 410/500 | Train: 0.1390 | Val: 0.2718 + Checkpoint saved. +Ep 411/500 | Train: 0.1391 | Val: 0.2781 +Ep 412/500 | Train: 0.1376 | Val: 0.2500 + Best Model Saved (Loss: 0.2500) +Ep 413/500 | Train: 0.1375 | Val: 0.2754 +Ep 414/500 | Train: 0.1358 | Val: 0.2696 +Ep 415/500 | Train: 0.1358 | Val: 0.2721 + Checkpoint saved. +Ep 416/500 | Train: 0.1351 | Val: 0.2665 +Ep 417/500 | Train: 0.1361 | Val: 0.2781 +Ep 418/500 | Train: 0.1352 | Val: 0.2601 +Ep 419/500 | Train: 0.1380 | Val: 0.2809 +Ep 420/500 | Train: 0.1336 | Val: 0.2686 + Checkpoint saved. +Ep 421/500 | Train: 0.1316 | Val: 0.2631 +Ep 422/500 | Train: 0.1392 | Val: 0.2627 +Ep 423/500 | Train: 0.1328 | Val: 0.2758 +Ep 424/500 | Train: 0.1311 | Val: 0.2583 +Ep 425/500 | Train: 0.1319 | Val: 0.2640 + Checkpoint saved. +Ep 426/500 | Train: 0.1340 | Val: 0.2719 +Ep 427/500 | Train: 0.1294 | Val: 0.2565 +Ep 428/500 | Train: 0.1329 | Val: 0.2663 +Ep 429/500 | Train: 0.1276 | Val: 0.2685 +Ep 430/500 | Train: 0.1292 | Val: 0.2580 + Checkpoint saved. +Ep 431/500 | Train: 0.1270 | Val: 0.2573 +Ep 432/500 | Train: 0.1338 | Val: 0.2545 +Ep 433/500 | Train: 0.1297 | Val: 0.2639 +Ep 434/500 | Train: 0.1257 | Val: 0.2661 +Ep 435/500 | Train: 0.1262 | Val: 0.2672 + Checkpoint saved. +Ep 436/500 | Train: 0.1305 | Val: 0.2637 +Ep 437/500 | Train: 0.1256 | Val: 0.2670 +Ep 438/500 | Train: 0.1262 | Val: 0.2592 +Ep 439/500 | Train: 0.1252 | Val: 0.2607 +Ep 440/500 | Train: 0.1237 | Val: 0.2540 + Checkpoint saved. +Ep 441/500 | Train: 0.1247 | Val: 0.2567 +Ep 442/500 | Train: 0.1227 | Val: 0.2669 +Ep 443/500 | Train: 0.1259 | Val: 0.2570 +Ep 444/500 | Train: 0.1235 | Val: 0.2774 +Ep 445/500 | Train: 0.1212 | Val: 0.2537 + Checkpoint saved. +Ep 446/500 | Train: 0.1220 | Val: 0.2644 +Ep 447/500 | Train: 0.1204 | Val: 0.2532 +Ep 448/500 | Train: 0.1235 | Val: 0.2547 +Ep 449/500 | Train: 0.1244 | Val: 0.2568 +Ep 450/500 | Train: 0.1205 | Val: 0.2582 + Checkpoint saved. +Ep 451/500 | Train: 0.1204 | Val: 0.2677 +Ep 452/500 | Train: 0.1187 | Val: 0.2539 +Ep 453/500 | Train: 0.1190 | Val: 0.2548 +Ep 454/500 | Train: 0.1203 | Val: 0.2486 + Best Model Saved (Loss: 0.2486) +Ep 455/500 | Train: 0.1170 | Val: 0.2537 + Checkpoint saved. +Ep 456/500 | Train: 0.1172 | Val: 0.2482 + Best Model Saved (Loss: 0.2482) +Ep 457/500 | Train: 0.1167 | Val: 0.2515 +Ep 458/500 | Train: 0.1236 | Val: 0.2559 +Ep 459/500 | Train: 0.1169 | Val: 0.2487 +Ep 460/500 | Train: 0.1165 | Val: 0.2533 + Checkpoint saved. +Ep 461/500 | Train: 0.1217 | Val: 0.2519 +Ep 462/500 | Train: 0.1145 | Val: 0.2555 +Ep 463/500 | Train: 0.1181 | Val: 0.2608 +Ep 464/500 | Train: 0.1170 | Val: 0.2469 + Best Model Saved (Loss: 0.2469) +Ep 465/500 | Train: 0.1160 | Val: 0.2468 + Best Model Saved (Loss: 0.2468) + Checkpoint saved. +Ep 466/500 | Train: 0.1177 | Val: 0.2453 + Best Model Saved (Loss: 0.2453) +Ep 467/500 | Train: 0.1145 | Val: 0.2569 +Ep 468/500 | Train: 0.1131 | Val: 0.2624 +Ep 469/500 | Train: 0.1131 | Val: 0.2487 +Ep 470/500 | Train: 0.1125 | Val: 0.2559 + Checkpoint saved. +Ep 471/500 | Train: 0.1148 | Val: 0.2504 +Ep 472/500 | Train: 0.1152 | Val: 0.2471 +Ep 473/500 | Train: 0.1180 | Val: 0.2578 +Ep 474/500 | Train: 0.1139 | Val: 0.2430 + Best Model Saved (Loss: 0.2430) +Ep 475/500 | Train: 0.1151 | Val: 0.2589 + Checkpoint saved. +Ep 476/500 | Train: 0.1109 | Val: 0.2537 +Ep 477/500 | Train: 0.1147 | Val: 0.2612 +Ep 478/500 | Train: 0.1105 | Val: 0.2589 +Ep 479/500 | Train: 0.1104 | Val: 0.2488 +Ep 480/500 | Train: 0.1102 | Val: 0.2574 + Checkpoint saved. +Ep 481/500 | Train: 0.1095 | Val: 0.2503 +Ep 482/500 | Train: 0.1137 | Val: 0.2484 +Ep 483/500 | Train: 0.1089 | Val: 0.2472 +Ep 484/500 | Train: 0.1112 | Val: 0.2576 +Ep 485/500 | Train: 0.1083 | Val: 0.2480 + Checkpoint saved. +Ep 486/500 | Train: 0.1076 | Val: 0.2527 +Ep 487/500 | Train: 0.1083 | Val: 0.2501 +Ep 488/500 | Train: 0.1080 | Val: 0.2446 +Ep 489/500 | Train: 0.1085 | Val: 0.2453 +Ep 490/500 | Train: 0.1062 | Val: 0.2509 + Checkpoint saved. +Ep 491/500 | Train: 0.1082 | Val: 0.2479 +Ep 492/500 | Train: 0.1049 | Val: 0.2479 +Ep 493/500 | Train: 0.1050 | Val: 0.2505 +Ep 494/500 | Train: 0.1048 | Val: 0.2472 +Ep 495/500 | Train: 0.1050 | Val: 0.2457 + Checkpoint saved. +Ep 496/500 | Train: 0.1049 | Val: 0.2452 +Ep 497/500 | Train: 0.1048 | Val: 0.2456 +Ep 498/500 | Train: 0.1035 | Val: 0.2451 +Ep 499/500 | Train: 0.1087 | Val: 0.2448 +Ep 500/500 | Train: 0.1043 | Val: 0.2460 + Checkpoint saved. + Training Complete! ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 4: FINAL EVALUATION & REPORTING
+
+import matplotlib.pyplot as plt
+import json
+import seaborn as sns
+from sklearn.metrics import accuracy_score, f1_score, jaccard_score, precision_score, recall_score, confusion_matrix
+
+def generate_final_report(model, loader, history, save_dir, model_name="SatMAE_Code1_Fair_500"):
+ print(f"Generating Final Report for {model_name}...")
+
+ # 1. LOAD BEST MODEL
+ # ---------------------------------
+ best_path = save_dir + "SatMAE_500_BEST.pth"
+ if os.path.exists(best_path):
+ print(f" Loading Best Saved Model from: {best_path}")
+ model.load_state_dict(torch.load(best_path, map_location=device))
+ else:
+ print(" Best model file not found. Using current model weights.")
+
+ model.eval()
+
+ # 2. PLOT LOSS CURVE
+
+ if 'train_loss' in history and len(history['train_loss']) > 0:
+ plt.figure(figsize=(12, 6))
+ epochs = range(1, len(history['train_loss']) + 1)
+
+ # Plot
+ plt.plot(epochs, history['train_loss'], 'b-', label='Training Loss', linewidth=2)
+ plt.plot(epochs, history['val_loss'], 'r--', label='Validation Loss', linewidth=2)
+
+ plt.title(f'Training Progress ({len(epochs)} Epochs)', fontsize=16)
+ plt.xlabel('Epochs', fontsize=12)
+ plt.ylabel('Compound Loss', fontsize=12)
+ plt.legend(fontsize=12)
+ plt.grid(True, alpha=0.3)
+
+ # Save
+ graph_path = f"{save_dir}{model_name}_LossCurve.png"
+ plt.savefig(graph_path)
+ plt.show()
+ print(f" Loss Graph saved to: {graph_path}")
+ else:
+ print(" No history data found to plot.")
+
+ # 3. VISUALIZATION (Samples)
+
+ try:
+ x_batch, y_batch = next(iter(loader))
+ x_batch, y_batch = x_batch.to(device), y_batch.to(device)
+
+ with torch.no_grad():
+ logits = model(x_batch)
+ preds = (torch.sigmoid(logits) > 0.5).float()
+
+ # Plot 3 samples
+ fig, axes = plt.subplots(3, 3, figsize=(12, 12))
+ cols = ["Input (RGB - Peak Season)", "Ground Truth", "Prediction"]
+ for ax, col in zip(axes[0], cols): ax.set_title(col, fontsize=14, fontweight='bold')
+
+ for i in range(3):
+ if i >= len(x_batch): break
+
+ # Construct RGB from bands B4(2), B3(1), B2(0)
+ rgb = x_batch[i, [2, 1, 0], 1, :, :].permute(1, 2, 0).cpu().numpy()
+ rgb = np.clip(rgb * 3.5, 0, 1) # Brighten 3.5x for visibility
+
+ gt_img = y_batch[i, 0].cpu().numpy()
+ pred_img = preds[i, 0].cpu().numpy()
+
+ # Overlay metrics on image
+ inter = np.logical_and(gt_img, pred_img).sum()
+ union = np.logical_or(gt_img, pred_img).sum()
+ iou = inter / (union + 1e-6)
+
+ axes[i, 0].imshow(rgb)
+ axes[i, 1].imshow(gt_img, cmap='gray')
+ axes[i, 2].imshow(pred_img, cmap='gray')
+ axes[i, 2].text(5, 20, f"IoU: {iou:.2f}", color='lime', fontweight='bold', bbox=dict(facecolor='black', alpha=0.5))
+
+ for ax in axes[i]: ax.axis('off')
+
+ plt.tight_layout()
+ viz_path = f"{save_dir}{model_name}_Visuals.png"
+ plt.savefig(viz_path)
+ plt.show()
+ print(f" Visualizations saved to: {viz_path}")
+ except Exception as e:
+ print(f" Visualization Error: {e}")
+
+ # 4. DETAILED METRICS REPORT
+
+ print(" Calculating Detailed Metrics on Full Validation Set...")
+ all_preds, all_targets = [], []
+
+ with torch.no_grad():
+ for x, y in loader:
+ x = x.to(device)
+ logits = model(x)
+ # Binary Threshold
+ p_batch = (torch.sigmoid(logits) > 0.5).float().cpu().numpy().flatten()
+ y_batch = y.numpy().flatten()
+ all_preds.extend(p_batch)
+ all_targets.extend(y_batch)
+
+ y_p = np.array(all_preds).astype(int)
+ y_t = np.array(all_targets).astype(int)
+
+ metrics = {
+ "Model": model_name,
+ "Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
+ "Pixel_Accuracy": round(accuracy_score(y_t, y_p), 4),
+ "IoU_Score": round(jaccard_score(y_t, y_p, average='binary'), 4),
+ "F1_Score": round(f1_score(y_t, y_p, average='binary'), 4),
+ "Precision": round(precision_score(y_t, y_p, average='binary'), 4),
+ "Recall": round(recall_score(y_t, y_p, average='binary'), 4),
+ "Confusion_Matrix": {
+ "TN": int(confusion_matrix(y_t, y_p).ravel()[0]),
+ "FP": int(confusion_matrix(y_t, y_p).ravel()[1]),
+ "FN": int(confusion_matrix(y_t, y_p).ravel()[2]),
+ "TP": int(confusion_matrix(y_t, y_p).ravel()[3])
+ }
+ }
+
+ # Save JSON to Drive
+ json_path = f"{save_dir}{model_name}_DetailedMetrics.json"
+ with open(json_path, 'w') as f:
+ json.dump(metrics, f, indent=4)
+
+ print("\n FINAL METRICS REPORT:")
+ print(json.dumps(metrics, indent=4))
+ print(f"\n All files (PNGs + JSON) saved to: {save_dir}")
+
+# EXECUTE
+
+if 'history' not in globals():
+ try:
+ print(" Attempting to load history from checkpoint...")
+ ckpt = torch.load(SAVE_DIR + "checkpoint.pth")
+ history = ckpt['history']
+ print(" History loaded.")
+ except:
+ print(" Could not load history. Graphs will be empty.")
+ history = {'train_loss': [], 'val_loss': []}
+
+generate_final_report(model, val_loader, history, SAVE_DIR)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Generating Final Report for SatMAE_Code1_Fair_500... + Loading Best Saved Model from: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_500_BEST.pth ++
+
+
+
+
+
+
+
+
+
+
+Loss Graph saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Code1_Fair_500_LossCurve.png ++
+
+
+
+
+
+
+
+
+
+
+ Visualizations saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Code1_Fair_500_Visuals.png
+ Calculating Detailed Metrics on Full Validation Set...
+
+ FINAL METRICS REPORT:
+{
+ "Model": "SatMAE_Code1_Fair_500",
+ "Timestamp": "2026-01-13 06:12",
+ "Pixel_Accuracy": 0.8671,
+ "IoU_Score": 0.8008,
+ "F1_Score": 0.8894,
+ "Precision": 0.8894,
+ "Recall": 0.8893,
+ "Confusion_Matrix": {
+ "TN": 66806,
+ "FP": 13336,
+ "FN": 13341,
+ "TP": 107221
+ }
+}
+
+ All files (PNGs + JSON) saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 1: HYPER-STACK DATA LOADING
+
+import os
+import numpy as np
+import rasterio
+from rasterio.windows import from_bounds
+import ee
+import geemap
+import torch
+import cv2
+import shutil
+import time
+
+# CONFIG
+TIME_WINDOWS = [
+ ('2024-10-15', '2024-11-15'), # 1. Pre-Sowing
+ ('2024-11-16', '2024-12-15'), # 2. Early Growth
+ ('2024-12-16', '2025-01-15'), # 3. Late Growth
+ ('2025-01-16', '2025-02-15'), # 4. Peak Greenness
+ ('2025-02-16', '2025-03-15'), # 5. Flowering
+ ('2025-03-16', '2025-04-15') # 6. Harvest
+]
+ASSET_ID = 'projects/[REDACTED_FOR_SECURITY]/assets/Punjab_Mask_2024_NEW'
+PATCH_SIZE = 224
+
+def get_hyper_satmae_data():
+ print(" Starting Hyper-Stack Ingestion...")
+ mask_img = ee.Image(ASSET_ID)
+ roi_geom = mask_img.geometry()
+
+ # Download Mask
+ mask_file = 'local_mask_hyper.tif'
+ if not os.path.exists(mask_file):
+ geemap.download_ee_image(mask_img, mask_file, region=roi_geom, scale=10, crs='EPSG:4326', overwrite=True)
+
+ with rasterio.open(mask_file) as src:
+ b = src.bounds
+ cx, cy = (b.left + b.right)/2, (b.bottom + b.top)/2
+ offset = 0.06
+ window = from_bounds(cx-offset, cy-offset, cx+offset, cy+offset, src.transform)
+ mask = src.read(1, window=window)
+ mask = np.where(mask > 0, 1.0, 0.0).astype(np.float32)
+ target_h, target_w = mask.shape
+ small_roi = ee.Geometry.Rectangle([cx-offset, cy-offset, cx+offset, cy+offset], proj=str(src.crs), geodesic=False)
+
+ stack = []
+
+ for i, (start, end) in enumerate(TIME_WINDOWS):
+ fname = f'hyper_time_{i}.tif'
+
+ # RETRY LOGIC for robustness
+ attempts = 0
+ while not os.path.exists(fname) and attempts < 3:
+ try:
+ print(f" Downloading Step {i+1}/{len(TIME_WINDOWS)} (Attempt {attempts+1})...")
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED').filterBounds(small_roi).filterDate(start, end).median().select(['B2','B3','B4','B8','B11','B12'])
+ s1 = ee.ImageCollection('COPERNICUS/S1_GRD').filterBounds(small_roi).filterDate(start, end).mean().select(['VV','VH'])
+ fused = ee.Image.cat([s2, s1]).clip(small_roi)
+ geemap.download_ee_image(fused, fname, region=small_roi, scale=10, crs='EPSG:4326', overwrite=True)
+ except Exception as e:
+ print(f" Error: {e}")
+ attempts += 1
+ time.sleep(2) # Wait 2 seconds before retry
+
+ # Fallback if download failed 3 times
+ if not os.path.exists(fname):
+ print(f" Failed to download {start}. Checking fallback...")
+ if i > 0:
+ print(" Copying previous month's data.")
+ shutil.copy(f'hyper_time_{i-1}.tif', fname)
+ else:
+ raise RuntimeError(" CRITICAL: First time step failed. Cannot proceed.")
+
+ # LOAD & PROCESS
+ with rasterio.open(fname) as src:
+ arr = src.read() # (8, H, W)
+ arr = np.transpose(arr, (1, 2, 0)) # (H, W, 8)
+ if arr.shape[:2] != (target_h, target_w):
+ arr = cv2.resize(arr, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
+
+ # Normalization
+ s2_bands = np.clip(arr[:,:,:6] / 5000.0, 0, 1)
+ s1_bands = np.clip((arr[:,:,6:] - (-25.0)) / 25.0, 0, 1)
+
+ # Indices
+ nir = s2_bands[:, :, 3] + 1e-6
+ red = s2_bands[:, :, 2] + 1e-6
+ blue = s2_bands[:, :, 0] + 1e-6
+
+ ndvi = ((nir - red) / (nir + red) + 1) / 2.0
+ evi = np.clip(2.5 * ((nir - red) / (nir + 6 * red - 7.5 * blue + 1)), 0, 1)
+
+ combined = np.concatenate([s2_bands, s1_bands, ndvi[:, :, None], evi[:, :, None]], axis=2)
+ stack.append(combined)
+
+ full_cube = np.stack(stack, axis=2)
+ x_out, y_out = [], []
+ stride = PATCH_SIZE
+
+ print(" Creating Patches...")
+ for y in range(0, target_h, stride):
+ for x in range(0, target_w, stride):
+ img_p = full_cube[y:y+stride, x:x+stride]
+ mask_p = mask[y:y+stride, x:x+stride]
+
+ if img_p.shape[0] != PATCH_SIZE or img_p.shape[1] != PATCH_SIZE: continue
+ if np.isnan(img_p).any(): continue
+
+ x_out.append(img_p)
+ y_out.append(mask_p)
+
+ X = np.array(x_out, dtype=np.float32).transpose(0, 4, 3, 1, 2) # (B, C, T, H, W)
+ y = np.array(y_out, dtype=np.float32)[:, None, :, :]
+ X = np.nan_to_num(X, nan=0.0)
+
+ print(f" Hyper-Dataset Ready. Shape: {X.shape}")
+ return torch.tensor(X), torch.tensor(y)
+
+X_data, y_data = get_hyper_satmae_data()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/usr/local/lib/python3.12/dist-packages/geemap/common.py:12471: FutureWarning: 'BaseImage' is deprecated and will be removed in a future release. Please use the 'ee.Image.gd' accessor instead. + img = gd.download.BaseImage(image) +WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance. +WARNING:google_auth_httplib2:httplib2 transport does not support per-request timeout. Set the timeout when constructing the httplib2.Http instance. ++
+
+
+
+
+Starting Hyper-Stack Ingestion... ++
+
+
+
+
+...tmae-2026/assets/Punjab_Mask_2024_NEW: 0%| |0/585 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.50 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.60 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 1.78 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 1.92 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.10 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.14 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 1.85 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Downloading Step 1/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.32 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.15 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.86 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 ++
+
+
+
+
+Downloading Step 2/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:googleapiclient.http:Sleeping 1.94 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.29 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 0.64 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Downloading Step 3/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Downloading Step 4/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 1.37 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:googleapiclient.http:Sleeping 0.83 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Downloading Step 5/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:googleapiclient.http:Sleeping 1.80 seconds before retry 1 of 5 for request: POST https://earthengine.googleapis.com/v1/projects/satmae-2026/thumbnails?fields=name&alt=json, after 429 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Downloading Step 6/6 (Attempt 1)... ++
+
+
+
+
+0%| |0/48 tiles [00:00<?]+
+
+
+
+
+WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 +WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: earthengine.googleapis.com. Connection pool size: 10 ++
+
+
+
+
+Creating Patches... + Hyper-Dataset Ready. Shape: (25, 10, 6, 224, 224) ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 2: MODEL DEFINITION
+import torch.nn as nn
+from huggingface_hub import hf_hub_download
+
+class SatMAEPatchEmbed(nn.Module):
+ def __init__(self, in_chans=10, embed_dim=768, patch_size=16):
+ super().__init__()
+ self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
+ def forward(self, x):
+ B, C, T, H, W = x.shape
+ x = x.permute(0, 2, 1, 3, 4).reshape(B * T, C, H, W)
+ x = self.proj(x).flatten(2).transpose(1, 2)
+ x = x.reshape(B, T, -1, x.shape[-1])
+ return x
+
+class SatMAEBackbone(nn.Module):
+ def __init__(self, num_frames=6, in_chans=10, embed_dim=768, depth=12, num_heads=12):
+ super().__init__()
+ self.patch_embed = SatMAEPatchEmbed(in_chans=in_chans, embed_dim=embed_dim)
+ num_patches = (224 // 16) ** 2
+ self.pos_embed = nn.Parameter(torch.zeros(1, 1, num_patches + 1, embed_dim))
+ self.time_embed = nn.Parameter(torch.zeros(1, num_frames, 1, embed_dim))
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, 1, embed_dim))
+ encoder_layer = nn.TransformerEncoderLayer(d_model=embed_dim, nhead=num_heads, dim_feedforward=embed_dim*4, activation="gelu", batch_first=True, norm_first=True)
+ self.blocks = nn.TransformerEncoder(encoder_layer, num_layers=depth)
+ self.norm = nn.LayerNorm(embed_dim)
+
+ def forward(self, x):
+ x = self.patch_embed(x)
+ B, T, N, D = x.shape
+ x = x + self.time_embed
+ x = x.reshape(B, T*N, D)
+ spatial_pos = self.pos_embed[:, :, 1:, :].expand(B, T, -1, -1).reshape(B, T*N, D)
+ x = x + spatial_pos
+ cls_token = self.cls_token.expand(B, -1, -1, -1).reshape(B, 1, D) + self.pos_embed[:, :, 0, :].expand(B, 1, D)
+ x = torch.cat((cls_token, x), dim=1)
+ x = self.blocks(x)
+ x = self.norm(x)
+ return x
+
+class SatMAESegmentation(nn.Module):
+ def __init__(self, num_frames=6, embed_dim=768):
+ super().__init__()
+ print(f" Constructing SatMAE Hyper-Stack ({num_frames} Time Steps)...")
+ self.num_frames = num_frames # <--- SAVE THIS VARIABLE
+ self.backbone = SatMAEBackbone(num_frames=num_frames, in_chans=10, embed_dim=embed_dim)
+
+ try:
+ print(" Adapting Google ViT Weights to 10 Channels...")
+ p = hf_hub_download("google/vit-base-patch16-224", "pytorch_model.bin")
+ sd = torch.load(p, map_location='cpu')
+ w = sd['vit.embeddings.patch_embeddings.projection.weight']
+ new_w = torch.zeros(768, 10, 16, 16)
+ new_w[:, :3] = w
+ new_w[:, 3:] = w.mean(dim=1, keepdim=True).repeat(1, 7, 1, 1)
+ self.backbone.patch_embed.proj.weight.data = new_w
+ self.backbone.patch_embed.proj.bias.data = sd['vit.embeddings.patch_embeddings.projection.bias']
+ print(" Weights Adapted.")
+ except:
+ print(" Weights missing, using random init.")
+
+ # FREEZING
+ for param in self.backbone.parameters(): param.requires_grad = False
+ self.backbone.time_embed.requires_grad = True
+ self.backbone.patch_embed.proj.weight.requires_grad = True
+
+ # DECODER
+ self.temporal_agg = nn.Conv2d(embed_dim * num_frames, embed_dim, kernel_size=1)
+ self.decoder = nn.Sequential(
+ nn.Upsample(scale_factor=2), nn.Conv2d(embed_dim, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(256, 128, 3, 1, 1), nn.BatchNorm2d(128), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(128, 64, 3, 1, 1), nn.BatchNorm2d(64), nn.GELU(),
+ nn.Upsample(scale_factor=2), nn.Conv2d(64, 32, 3, 1, 1), nn.BatchNorm2d(32), nn.GELU(),
+ nn.Conv2d(32, 1, 1)
+ )
+
+ def forward(self, x):
+ features = self.backbone(x)[:, 1:, :]
+ B, L, D = features.shape
+
+
+ features = features.view(B, self.num_frames, 14, 14, D).permute(0, 4, 1, 2, 3).flatten(1, 2)
+
+ features = self.temporal_agg(features)
+ return self.decoder(features)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import os
+# Force delete the old checkpoint
+if os.path.exists('/content/drive/MyDrive/SatMAE_Hyper_500/checkpoint_hyper.pth'):
+ os.remove('/content/drive/MyDrive/SatMAE_Hyper_500/checkpoint_hyper.pth')
+ print(" Bad checkpoint deleted. Ready to start fresh from Epoch 0.")
+else:
+ print(" No checkpoint found. Good to go!")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+No checkpoint found. Good to go! ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 3: TRAINING
+import torch
+import torch.optim as optim
+import torch.optim.swa_utils as swa_utils
+from torch.utils.data import TensorDataset, DataLoader, random_split
+from scipy.ndimage import distance_transform_edt as distance
+import os
+
+# --- CONFIGURATION ---
+RESUME = False
+BATCH_SIZE = 4
+EPOCHS = 500
+CHECKPOINT_PATH = SAVE_DIR + "checkpoint_hyper.pth"
+
+# 1. TIME-SAFE AUGMENTATION
+def apply_augmentation(x, y):
+ """
+ IT will Flip only Spatial Dimensions (H=3, W=4 for x) | (H=2, W=3 for y).
+ Strictly preserves Time (Dim 2).
+ """
+ if np.random.rand() > 0.5: # H-Flip (Width)
+ x = torch.flip(x, [4])
+ y = torch.flip(y, [3])
+
+ if np.random.rand() > 0.5: # V-Flip (Height)
+ x = torch.flip(x, [3])
+ y = torch.flip(y, [2])
+
+ k = np.random.randint(0, 4) # Rotation
+ x = torch.rot90(x, k, [3, 4])
+ y = torch.rot90(y, k, [2, 3])
+ return x, y
+
+# 2. FIXED LOSS (Reshape instead of View)
+class DiceLoss(nn.Module):
+ def __init__(self, smooth=1e-6):
+ super().__init__(); self.smooth = smooth
+ def forward(self, inputs, targets):
+ inputs = torch.sigmoid(inputs).reshape(-1) # Fixed
+ targets = targets.reshape(-1) # Fixed
+ inter = (inputs * targets).sum()
+ return 1 - (2. * inter + self.smooth) / (inputs.sum() + targets.sum() + self.smooth)
+
+class HausdorffDTLoss(nn.Module):
+ def __init__(self, alpha=2.0):
+ super().__init__(); self.alpha = alpha
+ def forward(self, pred, gt):
+ with torch.no_grad():
+ gt_np = gt.cpu().numpy()
+ dist_map = np.zeros_like(gt_np)
+ for i in range(len(gt_np)):
+ mask = (gt_np[i, 0] > 0.5).astype(np.uint8)
+ if mask.sum() == 0: continue
+ d_in = distance(mask); d_out = distance(1 - mask)
+ dist_map[i, 0] = (d_out - d_in)
+ dist_map = torch.tensor(dist_map, device=pred.device, dtype=torch.float32)
+ probs = torch.sigmoid(pred)
+ return torch.mean((probs - gt) ** 2 * (1 + self.alpha * torch.abs(dist_map)))
+
+class CompoundLoss(nn.Module):
+ def __init__(self):
+ super().__init__(); self.dice = DiceLoss(); self.boundary = HausdorffDTLoss(alpha=2.0)
+ def forward(self, p, t): return 0.7*self.dice(p, t) + 0.3*self.boundary(p, t)
+
+# 3. SETUP MODEL & OPTIMIZER
+model = SatMAESegmentation(num_frames=6, embed_dim=768).to(device)
+criterion = CompoundLoss()
+optimizer = optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)
+scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=50, T_mult=2, eta_min=1e-6)
+
+# SWA Config
+swa_model = swa_utils.AveragedModel(model)
+swa_start = 350
+swa_scheduler = swa_utils.SWALR(optimizer, swa_lr=5e-5)
+
+# Loaders
+ds = TensorDataset(X_data, y_data)
+tr_sz = int(0.85 * len(ds))
+t_ds, v_ds = random_split(ds, [tr_sz, len(ds)-tr_sz])
+train_loader = DataLoader(t_ds, BATCH_SIZE, shuffle=True)
+val_loader = DataLoader(v_ds, BATCH_SIZE, shuffle=False)
+
+# 4. RESUME LOGIC (WITH FORCE DELETE)
+start_epoch = 0
+history = {'train_loss': [], 'val_loss': []}
+best_loss = float('inf')
+
+if RESUME and os.path.exists(CHECKPOINT_PATH):
+ print(" Found Checkpoint. Resuming...")
+ ckpt = torch.load(CHECKPOINT_PATH)
+ model.load_state_dict(ckpt['model_state_dict'])
+ optimizer.load_state_dict(ckpt['optimizer_state_dict'])
+ start_epoch = ckpt['epoch'] + 1
+ history = ckpt['history']
+ best_loss = ckpt['best_loss']
+elif not RESUME and os.path.exists(CHECKPOINT_PATH):
+ print(" Force Restart: Deleting old checkpoint...")
+ os.remove(CHECKPOINT_PATH)
+ print(" Starting Fresh from Epoch 0.")
+else:
+ print(" No checkpoint found. Starting Fresh.")
+
+# 5. TRAINING LOOP
+print(f" Starting Hyper-Stack Training ({EPOCHS} Epochs)...")
+
+for ep in range(start_epoch, EPOCHS):
+ model.train()
+ train_loss = 0
+
+ for x, y in train_loader:
+ x, y = x.to(device), y.to(device)
+ x, y = apply_augmentation(x, y) # Safe Augmentation
+
+ optimizer.zero_grad()
+ preds = model(x)
+ loss = criterion(preds, y)
+ loss.backward()
+ optimizer.step()
+ train_loss += loss.item()
+
+ # Validation
+ model.eval()
+ val_loss = 0
+ with torch.no_grad():
+ for x, y in val_loader:
+ x, y = x.to(device), y.to(device)
+ preds = model(x)
+ val_loss += criterion(preds, y).item()
+
+ avg_t = train_loss / len(train_loader)
+ avg_v = val_loss / len(val_loader)
+ history['train_loss'].append(avg_t)
+ history['val_loss'].append(avg_v)
+
+ # --- SWA & SAVING LOGIC ---
+ if ep >= swa_start:
+ # Phase 2: SWA Mode
+ swa_model.update_parameters(model)
+ swa_scheduler.step()
+ lr_stat = f"SWA-LR: {swa_scheduler.get_last_lr()[0]:.1e}"
+ else:
+ # Phase 1: Standard Mode
+ scheduler.step()
+ lr_stat = f"LR: {scheduler.get_last_lr()[0]:.1e}"
+
+ # SAVE BEST MODEL
+ if avg_v < best_loss:
+ best_loss = avg_v
+ torch.save(model.state_dict(), SAVE_DIR + "SatMAE_Hyper_Best.pth")
+ print(f" Best Model Updated (Loss: {best_loss:.4f})")
+
+ # Print Stats (Every 5 Epochs)
+ if (ep+1) % 5 == 0:
+ print(f"Ep {ep+1} | T: {avg_t:.4f} | V: {avg_v:.4f} | {lr_stat}")
+
+ # SAVE CHECKPOINT (Every 5 Epochs)
+ if (ep+1) % 5 == 0:
+ torch.save({
+ 'epoch': ep,
+ 'model_state_dict': model.state_dict(),
+ 'optimizer_state_dict': optimizer.state_dict(),
+ 'history': history,
+ 'best_loss': best_loss
+ }, CHECKPOINT_PATH)
+ print(" Checkpoint Saved.")
+
+print(" Finalizing SWA Model...")
+swa_utils.update_bn(train_loader, swa_model, device=device)
+torch.save(swa_model.state_dict(), SAVE_DIR + "SatMAE_Hyper_SWA.pth")
+print("Done.")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructing SatMAE Hyper-Stack (6 Time Steps)... + Adapting Google ViT Weights to 10 Channels... + Weights Adapted. + Force Restart: Deleting old checkpoint... + Starting Fresh from Epoch 0. + Starting Hyper-Stack Training (500 Epochs)... + Best Model Updated (Loss: 1.0251) + Best Model Updated (Loss: 0.8995) + Best Model Updated (Loss: 0.7510) + Best Model Updated (Loss: 0.6757) + Best Model Updated (Loss: 0.6691) +Ep 5 | T: 0.4751 | V: 0.6691 | LR: 9.8e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.5908) + Best Model Updated (Loss: 0.5308) + Best Model Updated (Loss: 0.4995) + Best Model Updated (Loss: 0.3803) +Ep 10 | T: 0.4097 | V: 0.3803 | LR: 9.1e-05 + Checkpoint Saved. +Ep 15 | T: 0.3892 | V: 0.4322 | LR: 8.0e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.3486) +Ep 20 | T: 0.3657 | V: 0.4360 | LR: 6.6e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.3454) +Ep 25 | T: 0.3834 | V: 0.3533 | LR: 5.1e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.3442) +Ep 30 | T: 0.3406 | V: 0.3442 | LR: 3.5e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.3440) + Best Model Updated (Loss: 0.3201) +Ep 35 | T: 0.3369 | V: 0.3541 | LR: 2.1e-05 + Checkpoint Saved. +Ep 40 | T: 0.3310 | V: 0.3429 | LR: 1.0e-05 + Checkpoint Saved. +Ep 45 | T: 0.3350 | V: 0.3480 | LR: 3.4e-06 + Checkpoint Saved. +Ep 50 | T: 0.3303 | V: 0.3368 | LR: 1.0e-04 + Checkpoint Saved. +Ep 55 | T: 0.3369 | V: 0.3291 | LR: 9.9e-05 + Checkpoint Saved. +Ep 60 | T: 0.3130 | V: 0.3682 | LR: 9.8e-05 + Checkpoint Saved. +Ep 65 | T: 0.3040 | V: 0.3327 | LR: 9.5e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.3022) + Best Model Updated (Loss: 0.2930) +Ep 70 | T: 0.2928 | V: 0.3522 | LR: 9.1e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2837) +Ep 75 | T: 0.2944 | V: 0.7233 | LR: 8.6e-05 + Checkpoint Saved. +Ep 80 | T: 0.2964 | V: 0.3968 | LR: 8.0e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2519) +Ep 85 | T: 0.2780 | V: 0.3613 | LR: 7.3e-05 + Checkpoint Saved. +Ep 90 | T: 0.2656 | V: 0.3216 | LR: 6.6e-05 + Checkpoint Saved. +Ep 95 | T: 0.2633 | V: 0.2880 | LR: 5.8e-05 + Checkpoint Saved. +Ep 100 | T: 0.2594 | V: 0.2766 | LR: 5.1e-05 + Checkpoint Saved. +Ep 105 | T: 0.2603 | V: 0.2719 | LR: 4.3e-05 + Checkpoint Saved. +Ep 110 | T: 0.2521 | V: 0.2780 | LR: 3.5e-05 + Checkpoint Saved. +Ep 115 | T: 0.2447 | V: 0.2759 | LR: 2.8e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2486) +Ep 120 | T: 0.2388 | V: 0.2549 | LR: 2.1e-05 + Checkpoint Saved. +Ep 125 | T: 0.2337 | V: 0.2559 | LR: 1.5e-05 + Checkpoint Saved. +Ep 130 | T: 0.2440 | V: 0.2505 | LR: 1.0e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2432) +Ep 135 | T: 0.2329 | V: 0.2471 | LR: 6.4e-06 + Checkpoint Saved. +Ep 140 | T: 0.2341 | V: 0.2452 | LR: 3.4e-06 + Checkpoint Saved. +Ep 145 | T: 0.2409 | V: 0.2552 | LR: 1.6e-06 + Checkpoint Saved. +Ep 150 | T: 0.2385 | V: 0.2561 | LR: 1.0e-04 + Checkpoint Saved. +Ep 155 | T: 0.2456 | V: 0.3684 | LR: 1.0e-04 + Checkpoint Saved. +Ep 160 | T: 0.2368 | V: 0.3029 | LR: 9.9e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2324) +Ep 165 | T: 0.2236 | V: 0.2324 | LR: 9.9e-05 + Checkpoint Saved. +Ep 170 | T: 0.2155 | V: 0.2603 | LR: 9.8e-05 + Checkpoint Saved. +Ep 175 | T: 0.2164 | V: 0.2942 | LR: 9.6e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2283) +Ep 180 | T: 0.2077 | V: 0.2453 | LR: 9.5e-05 + Checkpoint Saved. +Ep 185 | T: 0.2053 | V: 0.3523 | LR: 9.3e-05 + Checkpoint Saved. +Ep 190 | T: 0.1970 | V: 0.2370 | LR: 9.1e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2277) +Ep 195 | T: 0.1918 | V: 0.2713 | LR: 8.8e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2232) + Best Model Updated (Loss: 0.2218) +Ep 200 | T: 0.2063 | V: 0.2218 | LR: 8.6e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2206) +Ep 205 | T: 0.1925 | V: 0.2233 | LR: 8.3e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2204) +Ep 210 | T: 0.1961 | V: 0.2380 | LR: 8.0e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2092) +Ep 215 | T: 0.1772 | V: 0.2190 | LR: 7.6e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2034) +Ep 220 | T: 0.1720 | V: 0.2034 | LR: 7.3e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.2031) +Ep 225 | T: 0.1701 | V: 0.2031 | LR: 6.9e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.1997) + Best Model Updated (Loss: 0.1951) +Ep 230 | T: 0.1670 | V: 0.2034 | LR: 6.6e-05 + Checkpoint Saved. +Ep 235 | T: 0.1743 | V: 0.2078 | LR: 6.2e-05 + Checkpoint Saved. +Ep 240 | T: 0.1632 | V: 0.2033 | LR: 5.8e-05 + Checkpoint Saved. +Ep 245 | T: 0.1581 | V: 0.1979 | LR: 5.4e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.1937) +Ep 250 | T: 0.1539 | V: 0.1997 | LR: 5.1e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.1904) + Best Model Updated (Loss: 0.1903) + Best Model Updated (Loss: 0.1823) +Ep 255 | T: 0.1661 | V: 0.1823 | LR: 4.7e-05 + Checkpoint Saved. +Ep 260 | T: 0.1517 | V: 0.1960 | LR: 4.3e-05 + Checkpoint Saved. +Ep 265 | T: 0.1536 | V: 0.2066 | LR: 3.9e-05 + Checkpoint Saved. +Ep 270 | T: 0.1480 | V: 0.1840 | LR: 3.5e-05 + Checkpoint Saved. +Ep 275 | T: 0.1467 | V: 0.1850 | LR: 3.2e-05 + Checkpoint Saved. +Ep 280 | T: 0.1433 | V: 0.1832 | LR: 2.8e-05 + Checkpoint Saved. +Ep 285 | T: 0.1436 | V: 0.1888 | LR: 2.5e-05 + Checkpoint Saved. +Ep 290 | T: 0.1401 | V: 0.1862 | LR: 2.1e-05 + Checkpoint Saved. + Best Model Updated (Loss: 0.1815) +Ep 295 | T: 0.1405 | V: 0.1815 | LR: 1.8e-05 + Checkpoint Saved. +Ep 300 | T: 0.1471 | V: 0.1877 | LR: 1.5e-05 + Checkpoint Saved. +Ep 305 | T: 0.1406 | V: 0.1862 | LR: 1.3e-05 + Checkpoint Saved. +Ep 310 | T: 0.1497 | V: 0.1862 | LR: 1.0e-05 + Checkpoint Saved. +Ep 315 | T: 0.1358 | V: 0.1848 | LR: 8.3e-06 + Checkpoint Saved. +Ep 320 | T: 0.1371 | V: 0.1828 | LR: 6.4e-06 + Checkpoint Saved. + Best Model Updated (Loss: 0.1814) + Best Model Updated (Loss: 0.1807) +Ep 325 | T: 0.1454 | V: 0.1852 | LR: 4.8e-06 + Checkpoint Saved. +Ep 330 | T: 0.1380 | V: 0.1826 | LR: 3.4e-06 + Checkpoint Saved. +Ep 335 | T: 0.1454 | V: 0.1812 | LR: 2.4e-06 + Checkpoint Saved. + Best Model Updated (Loss: 0.1800) +Ep 340 | T: 0.1412 | V: 0.1863 | LR: 1.6e-06 + Checkpoint Saved. +Ep 345 | T: 0.1371 | V: 0.1807 | LR: 1.2e-06 + Checkpoint Saved. +Ep 350 | T: 0.1379 | V: 0.1815 | LR: 1.0e-04 + Checkpoint Saved. +Ep 355 | T: 0.1462 | V: 0.2154 | SWA-LR: 7.5e-05 + Checkpoint Saved. +Ep 360 | T: 0.1528 | V: 0.1930 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 365 | T: 0.1351 | V: 0.2021 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 370 | T: 0.1304 | V: 0.1913 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 375 | T: 0.1324 | V: 0.1931 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 380 | T: 0.1316 | V: 0.1892 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 385 | T: 0.1284 | V: 0.1924 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 390 | T: 0.1281 | V: 0.1718 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 395 | T: 0.1379 | V: 0.1814 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 400 | T: 0.1237 | V: 0.1738 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 405 | T: 0.1270 | V: 0.1808 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 410 | T: 0.1214 | V: 0.1739 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 415 | T: 0.1199 | V: 0.1797 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 420 | T: 0.1201 | V: 0.1776 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 425 | T: 0.1196 | V: 0.1850 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 430 | T: 0.1143 | V: 0.1784 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 435 | T: 0.1173 | V: 0.1882 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 440 | T: 0.1141 | V: 0.1758 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 445 | T: 0.1137 | V: 0.1784 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 450 | T: 0.1125 | V: 0.1810 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 455 | T: 0.1155 | V: 0.1704 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 460 | T: 0.1173 | V: 0.1851 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 465 | T: 0.1064 | V: 0.1739 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 470 | T: 0.1103 | V: 0.1829 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 475 | T: 0.1066 | V: 0.1824 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 480 | T: 0.1109 | V: 0.1798 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 485 | T: 0.1041 | V: 0.1758 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 490 | T: 0.1038 | V: 0.1725 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 495 | T: 0.1009 | V: 0.1698 | SWA-LR: 5.0e-05 + Checkpoint Saved. +Ep 500 | T: 0.1037 | V: 0.1798 | SWA-LR: 5.0e-05 + Checkpoint Saved. +💾 Finalizing SWA Model... +Done. ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 4: VISUALIZATION & SAMPLING
+
+import matplotlib.pyplot as plt
+import numpy as np
+import torch
+import os
+
+# CONFIG
+MODEL_NAME = "SatMAE_Hyper_500_Epochs"
+GRAPH_PATH = os.path.join(SAVE_DIR, f"{MODEL_NAME}_LossCurve.png")
+VISUALS_PATH = os.path.join(SAVE_DIR, f"{MODEL_NAME}_VisualSamples.png")
+
+# Check if necessary data exists
+if 'history' not in globals() or not history['train_loss']:
+ print(" No training history found. Cannot plot loss curve.")
+ # Try loading from checkpoint if available
+ ckpt_path = os.path.join(SAVE_DIR, "checkpoint_hyper.pth")
+ if os.path.exists(ckpt_path):
+ print(" Attempting to load history from checkpoint...")
+ ckpt = torch.load(ckpt_path, map_location=device)
+ history = ckpt['history']
+ print(" History loaded.")
+ else:
+ print("Checkpoint not found. Skipping loss plot.")
+ history = None
+
+# 1. PLOT LOSS CURVE
+if history:
+ print(f" Generating Loss Curve Graph for {len(history['train_loss'])} epochs...")
+ epochs = range(1, len(history['train_loss']) + 1)
+
+ plt.figure(figsize=(10, 6))
+ # Plot lines with markers for better visibility
+ plt.plot(epochs, history['train_loss'], 'b-o', label='Training Loss', linewidth=2, markersize=4)
+ plt.plot(epochs, history['val_loss'], 'r--s', label='Validation Loss', linewidth=2, markersize=4)
+
+ # Styling
+ plt.title(f'Training Progress: {MODEL_NAME}', fontsize=16, fontweight='bold')
+ plt.xlabel('Epochs', fontsize=12)
+ plt.ylabel('Compound Loss (Dice + Hausdorff)', fontsize=12)
+ plt.legend(fontsize=12, loc='upper right', frameon=True, shadow=True)
+ plt.grid(True, linestyle='--', alpha=0.6)
+ plt.xticks(fontsize=10)
+ plt.yticks(fontsize=10)
+ plt.tight_layout()
+
+ # Save and Show
+ plt.savefig(GRAPH_PATH, dpi=150)
+ print(f" Loss graph saved to: {GRAPH_PATH}")
+ plt.show()
+
+# 2. GENERATE VISUAL SAMPLES
+
+print("\n Generating Visual Sample Comparisons...")
+model.eval()
+try:
+ # Get a single batch from the validation loader
+ x_batch, y_batch = next(iter(val_loader))
+ x_batch = x_batch.to(device)
+
+ with torch.no_grad():
+ # Get model predictions
+ logits = model(x_batch)
+ # Apply sigmoid and threshold at 0.5 for binary mask
+ preds = (torch.sigmoid(logits) > 0.5).float()
+
+ # Determine how many samples to plot (max 3)
+ num_samples = min(3, x_batch.shape[0])
+
+ # Setup figure: rows = samples, cols = Input, GT, Pred
+ fig, axes = plt.subplots(num_samples, 3, figsize=(15, 5 * num_samples))
+ # Handle single-sample case where axes is 1D
+ if num_samples == 1: axes = np.expand_dims(axes, axis=0)
+
+ cols = ["Input (RGB - Peak Season)", "Ground Truth Mask", "Predicted Mask"]
+ for ax, col in zip(axes[0], cols):
+ ax.set_title(col, fontsize=14, fontweight='bold', pad=10)
+
+ for i in range(num_samples):
+ # --- A. Extract RGB Image for Display ---
+ # Data shape is (B, C, T, H, W). We need (H, W, C) for matplotlib.
+ # Channels [2, 1, 0] correspond to B4(Red), B3(Green), B2(Blue).
+ # Time Step 3 corresponds to Peak Season (Jan/Feb).
+ rgb_img = x_batch[i, [2, 1, 0], 3, :, :].permute(1, 2, 0).cpu().numpy()
+ # Brighten and clip to 0-1 range for better visibility
+ rgb_img = np.clip(rgb_img * 3.5, 0, 1)
+
+ # --- B. Extract Masks ---
+ gt_mask = y_batch[i, 0].numpy()
+ pred_mask = preds[i, 0].cpu().numpy()
+
+ # --- C. Calculate Sample IoU ---
+ intersection = np.logical_and(gt_mask, pred_mask).sum()
+ union = np.logical_or(gt_mask, pred_mask).sum()
+ iou = intersection / (union + 1e-6) # Avoid division by zero
+
+ # --- D. Plotting ---
+ # 1. RGB Input
+ axes[i, 0].imshow(rgb_img)
+ axes[i, 0].axis('off')
+
+ # 2. Ground Truth Mask
+ axes[i, 1].imshow(gt_mask, cmap='gray', vmin=0, vmax=1)
+ axes[i, 1].axis('off')
+
+ # 3. Predicted Mask with IoU Label
+ axes[i, 2].imshow(pred_mask, cmap='gray', vmin=0, vmax=1)
+ axes[i, 2].axis('off')
+ # Add a colored text box for IoU score
+ color = 'lime' if iou > 0.8 else ('yellow' if iou > 0.6 else 'red')
+ axes[i, 2].text(10, 30, f"IoU: {iou:.4f}", color=color, fontsize=12, fontweight='bold',
+ bbox=dict(facecolor='black', alpha=0.7, edgecolor=color))
+
+ plt.tight_layout()
+ plt.subplots_adjust(wspace=0.1, hspace=0.1)
+ plt.savefig(VISUALS_PATH, dpi=150, bbox_inches='tight')
+ print(f" Visual samples saved to: {VISUALS_PATH}")
+ plt.show()
+
+except Exception as e:
+ print(f" Could not generate visual samples. Error: {e}")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Generating Loss Curve Graph for 500 epochs... + Loss graph saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Hyper_500_Epochs_LossCurve.png ++
+
+
+
+
+
+
+
+
+
+
++ Generating Visual Sample Comparisons... + Visual samples saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Hyper_500_Epochs_VisualSamples.png ++
+
+
+
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# CELL 5: ADVANCED METRICS REPORT
+import json
+import time
+import numpy as np
+import torch
+import cv2
+from scipy.spatial.distance import directed_hausdorff
+from sklearn.metrics import accuracy_score, f1_score, jaccard_score, precision_score, recall_score
+from datetime import datetime
+
+# CONFIG
+MODEL_NAME = "SatMAE_Full_FineTune"
+SAVE_PATH = SAVE_DIR + "SatMAE_Advanced_Metrics.json"
+
+def get_boundary(mask, thickness=1):
+
+ mask = mask.astype(np.uint8)
+ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
+ boundary = cv2.morphologyEx(mask, cv2.MORPH_GRADIENT, kernel)
+ return boundary > 0
+
+def calculate_boundary_iou(pred_mask, gt_mask):
+
+ pred_boundary = get_boundary(pred_mask)
+ gt_boundary = get_boundary(gt_mask)
+
+ intersection = np.logical_and(pred_boundary, gt_boundary).sum()
+ union = np.logical_or(pred_boundary, gt_boundary).sum()
+
+ if union == 0: return 1.0 # Perfect match (both empty)
+ return intersection / union
+
+def calculate_hausdorff(pred_mask, gt_mask):
+
+ # Get coordinates of all non-zero pixels
+ pred_pts = np.argwhere(pred_mask > 0)
+ gt_pts = np.argwhere(gt_mask > 0)
+
+ # Handle empty cases
+ if len(pred_pts) == 0 or len(gt_pts) == 0:
+ return 0.0 if (len(pred_pts) == 0 and len(gt_pts) == 0) else 100.0 # Penalty
+
+ # Directed Hausdorff (A->B and B->A), take the max
+ d1 = directed_hausdorff(pred_pts, gt_pts)[0]
+ d2 = directed_hausdorff(gt_pts, pred_pts)[0]
+ return max(d1, d2)
+
+def generate_full_report(model, loader, device):
+ print(f" Starting Advanced Evaluation for {MODEL_NAME}...")
+ model.eval()
+
+ all_preds = []
+ all_targets = []
+
+ boundary_ious = []
+ hausdorff_dists = []
+
+ # 1. INFERENCE SPEED TEST
+
+ print(" Measuring Inference Speed...")
+ start_time = time.time()
+ total_frames = 0
+
+ with torch.no_grad():
+ for x, y in loader:
+ x = x.to(device)
+
+ # Predict
+ logits = model(x)
+ preds = (torch.sigmoid(logits) > 0.5).float().cpu().numpy()
+ targets = y.numpy()
+
+ total_frames += x.shape[0]
+
+ # Loop for Pixel-Wise Metrics
+ for i in range(len(preds)):
+ p_img = preds[i, 0]
+ t_img = targets[i, 0]
+
+ # Flatten for Standard Metrics
+ all_preds.extend(p_img.flatten())
+ all_targets.extend(t_img.flatten())
+
+ # Calculate Advanced Metrics (Per Image)
+ # Only calculate if ground truth has wheat (otherwise boundary is undefined)
+ if t_img.sum() > 0:
+ b_iou = calculate_boundary_iou(p_img, t_img)
+ h_dist = calculate_hausdorff(p_img, t_img)
+
+ boundary_ious.append(b_iou)
+ hausdorff_dists.append(h_dist)
+
+ end_time = time.time()
+ total_time = end_time - start_time
+
+ # 2. CALCULATE METRICS
+
+ print(" Calculating Aggregate Metrics...")
+ y_p = np.array(all_preds).astype(int)
+ y_t = np.array(all_targets).astype(int)
+
+ fps = total_frames / total_time
+ inf_time_per_batch = total_time / len(loader) # Average batch time
+ # Approximate inference per image (Batch Size 4)
+ inf_time_per_img = total_time / total_frames
+
+ metrics = {
+ "Model": MODEL_NAME,
+ "Date": datetime.now().strftime("%Y-%m-%d %H:%M"),
+ "Standard_IoU": round(jaccard_score(y_t, y_p, average='binary'), 4),
+ "Boundary_IoU": round(np.mean(boundary_ious), 4),
+ "Hausdorff_Dist_px": round(np.mean(hausdorff_dists), 2),
+ "F1_Score": round(f1_score(y_t, y_p, average='binary'), 4),
+ "Precision": round(precision_score(y_t, y_p, average='binary'), 4),
+ "Recall": round(recall_score(y_t, y_p, average='binary'), 4),
+ "Accuracy": round(accuracy_score(y_t, y_p), 4),
+ "Inference_Time_Sec": round(inf_time_per_img, 4),
+ "FPS": round(fps, 2)
+ }
+
+ # 3. SAVE & PRINT
+
+ with open(SAVE_PATH, 'w') as f:
+ json.dump(metrics, f, indent=4)
+
+ print("\n" + "="*40)
+ print(" FINAL METRICS REPORT")
+ print("="*40)
+ print(json.dumps(metrics, indent=4))
+ print("="*40)
+ print(f" Report saved to: {SAVE_PATH}")
+
+# EXECUTE
+generate_full_report(model, val_loader, device)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Starting Advanced Evaluation for SatMAE_Full_FineTune...
+⏱️ Measuring Inference Speed...
+ Calculating Aggregate Metrics...
+
+========================================
+ FINAL METRICS REPORT
+========================================
+{
+ "Model": "SatMAE_Full_FineTune",
+ "Date": "2026-01-13 08:17",
+ "Standard_IoU": 0.8551,
+ "Boundary_IoU": 0.29,
+ "Hausdorff_Dist_px": 15.97,
+ "F1_Score": 0.9219,
+ "Precision": 0.9291,
+ "Recall": 0.9148,
+ "Accuracy": 0.9034,
+ "Inference_Time_Sec": 0.2862,
+ "FPS": 3.49
+}
+========================================
+ Report saved to: /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Advanced_Metrics.json
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import json
+import os
+
+# Define the file path
+metrics_path = SAVE_DIR + "SatMAE_Advanced_Metrics.json"
+
+# Define the hyperparameters dictionary
+hyperparameters = {
+ "Architecture": "SatMAE (ViT-Base) - Frozen Backbone",
+ "Input_Specs": "10 Channels x 6 Time Steps",
+ "Epochs": 500,
+ "Batch_Size": 4,
+ "Optimizer": "AdamW (lr=1e-4)",
+ "Scheduler": "CosineAnnealingWarmRestarts (T0=50)",
+ "Loss_Function": "0.7*Dice + 0.3*Hausdorff",
+ "Technique_SWA": "Enabled (Start Ep 350, lr=5e-5)",
+ "Technique_Augmentation": "Spatial Flip/Rotate (Time Preserved)"
+}
+
+# Load, Update, and Save
+if os.path.exists(metrics_path):
+ with open(metrics_path, 'r') as f:
+ data = json.load(f)
+
+ # Add hyperparameters to the existing data
+ data["Hyperparameters"] = hyperparameters
+
+ with open(metrics_path, 'w') as f:
+ json.dump(data, f, indent=4)
+
+ print(f" Hyperparameters added to {metrics_path}")
+ print(json.dumps(data, indent=4))
+else:
+ print(" Metrics file not found. Run Cell 5 first.")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hyperparameters added to /content/drive/MyDrive/SatMAE_LongTrain_500/SatMAE_Advanced_Metrics.json
+{
+ "Model": "SatMAE_Full_FineTune",
+ "Date": "2026-01-13 08:17",
+ "Standard_IoU": 0.8551,
+ "Boundary_IoU": 0.29,
+ "Hausdorff_Dist_px": 15.97,
+ "F1_Score": 0.9219,
+ "Precision": 0.9291,
+ "Recall": 0.9148,
+ "Accuracy": 0.9034,
+ "Inference_Time_Sec": 0.2862,
+ "FPS": 3.49,
+ "Hyperparameters": {
+ "Architecture": "SatMAE (ViT-Base) - Frozen Backbone",
+ "Input_Specs": "10 Channels x 6 Time Steps",
+ "Epochs": 500,
+ "Batch_Size": 4,
+ "Optimizer": "AdamW (lr=1e-4)",
+ "Scheduler": "CosineAnnealingWarmRestarts (T0=50)",
+ "Loss_Function": "0.7*Dice + 0.3*Hausdorff",
+ "Technique_SWA": "Enabled (Start Ep 350, lr=5e-5)",
+ "Technique_Augmentation": "Spatial Flip/Rotate (Time Preserved)"
+ }
+}
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+# Save the model weights to a file
+torch.save(model.state_dict(), "my_best_punjab_model_weights.pth")
+print("Weights saved successfully!")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Weights saved successfully! ++
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import os
+import shutil
+from google.colab import drive
+
+# 1. Mount Google Drive
+e
+drive.mount('/content/drive')
+
+# --- CONFIGURATION
+local_filename = "my_best_punjab_model_weights.pth"
+drive_folder = "/content/drive/MyDrive/Satame_best_500_swa_Project"
+# -------------------------------------------------------------
+
+# 2. Check if the file exists locally
+if os.path.exists(local_filename):
+ print(f" Found '{local_filename}' in the current session.")
+
+ # Create the destination folder in Drive if it doesn't exist
+ if not os.path.exists(drive_folder):
+ os.makedirs(drive_folder)
+ print(f"Created new folder in Drive: {drive_folder}")
+
+ # 3. Copy the file to Drive
+ destination_path = os.path.join(drive_folder, local_filename)
+ try:
+ shutil.copy(local_filename, destination_path)
+ print(f" Success! Weights saved to: {destination_path}")
+ except Exception as e:
+ print(f" Error copying file: {e}")
+
+else:
+ print(f" Could not find '{local_filename}' in the current folder.")
+ print("List of files actually present here:")
+ print(os.listdir('.')) # Lists all files so you can see the correct name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
+ Found 'my_best_punjab_model_weights.pth' in the current session.
+Created new folder in Drive: /content/drive/MyDrive/Satame_best_500_swa_Project
+ Success! Weights saved to: /content/drive/MyDrive/Satame_best_500_swa_Project/my_best_punjab_model_weights.pth
+
+
+
+
+
+
+
+
+
+In [ ]:
+
+
+
+
+
+import torch
+import numpy as np
+import os
+import shutil
+from google.colab import drive
+
+drive.mount('/content/drive')
+drive_folder = "/content/drive/MyDrive/SatMAE_Project/Final_SWA_Save"
+if not os.path.exists(drive_folder):
+ os.makedirs(drive_folder)
+
+model_filename = "SatMAE_SWA_500Epochs.pth"
+embeddings_filename = "SatMAE_SWA_Embeddings.npy"
+device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+
+# 1. UNWRAP MODEL
+if hasattr(swa_model, 'module'):
+ segmentation_model = swa_model.module
+else:
+ segmentation_model = swa_model
+
+segmentation_model.eval()
+
+# 2. FIND BACKBONE
+if hasattr(segmentation_model, 'backbone'):
+ encoder = segmentation_model.backbone
+elif hasattr(segmentation_model, 'encoder'):
+ encoder = segmentation_model.encoder
+elif hasattr(segmentation_model, 'body'):
+ encoder = segmentation_model.body
+else:
+ # Fallback: try to use the model itself if no backbone attribute found
+ encoder = segmentation_model
+
+# 3. SAVE CONFIG & WEIGHTS
+try:
+ # Try to access attributes safely
+ img_size = getattr(encoder.patch_embed, 'img_size', 224) if hasattr(encoder, 'patch_embed') else 224
+ patch_size = getattr(encoder.patch_embed, 'patch_size', 16) if hasattr(encoder, 'patch_embed') else 16
+ in_chans = getattr(encoder.patch_embed.proj, 'in_channels', 10) if hasattr(encoder, 'patch_embed') else 10
+ embed_dim = getattr(encoder, 'embed_dim', 768)
+
+ config_to_save = {
+ "model_class": "SatMAEBackbone",
+ "img_size": img_size,
+ "patch_size": patch_size,
+ "in_chans": in_chans,
+ "embed_dim": embed_dim,
+ "notes": "Extracted from SatMAESegmentation"
+ }
+except Exception as e:
+ config_to_save = {"error": str(e)}
+
+full_package = {
+ "config": config_to_save,
+ "model_state_dict": segmentation_model.state_dict()
+}
+
+torch.save(full_package, model_filename)
+shutil.copy(model_filename, os.path.join(drive_folder, model_filename))
+
+# 4. EXTRACT EMBEDDINGS
+extraction_loader = val_loader
+all_embeddings = []
+
+with torch.no_grad():
+ for i, batch in enumerate(extraction_loader):
+ if isinstance(batch, (list, tuple)):
+ images = batch[0]
+ elif isinstance(batch, dict):
+ images = batch['image']
+ else:
+ images = batch
+
+ images = images.to(device)
+
+ # 5. ROBUST FORWARD PASS
+ # Try standard methods since forward_encoder is missing
+ if hasattr(encoder, 'forward_features'):
+ features = encoder.forward_features(images)
+ else:
+ features = encoder(images)
+
+ # Handle List output (common in Segmentation backbones)
+ if isinstance(features, (list, tuple)):
+ features = features[-1] # Take the deepest layer
+
+ # Handle Dimensions
+ # Case A: (Batch, Channels, H, W) -> Global Average Pool
+ if features.dim() == 4:
+ features = features.mean(dim=[2, 3])
+ # Case B: (Batch, Tokens, Channels) -> Take CLS Token
+ elif features.dim() == 3:
+ features = features[:, 0, :]
+
+ all_embeddings.append(features.cpu().numpy())
+
+if all_embeddings:
+ final_embeddings = np.concatenate(all_embeddings, axis=0)
+ np.save(embeddings_filename, final_embeddings)
+ shutil.copy(embeddings_filename, os.path.join(drive_folder, embeddings_filename))
+ print(f"Saved {final_embeddings.shape} embeddings to {drive_folder}")
+else:
+ print("No embeddings extracted")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
+Saved (4, 768) embeddings to /content/drive/MyDrive/SatMAE_Project/Final_SWA_Save
+
+