{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":117682,"databundleVersionId":15062069},{"sourceType":"kernelVersion","sourceId":296033877},{"sourceType":"kernelVersion","sourceId":296038497}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Model Training Results\n\n## Configuration Summary\n| Version | Image Size | Epochs | Model File     |\n|---------|------------|--------|----------------|\n| 1       | 96×96      | 10     | `*96.pth`      |\n| 4       | 128×128    | 10     | `*128.pth`     |\n| 3       | 160×160    | 10     | `*160.pth`     |\n| 5       | 96×96      | 20     | `*96.pth`      |\n| 6       | 128×128    | 20     | `*128.pth`     |\n| 7       | 160×160    | 20     | `*160.pth`     |\n\n## Model Groups\n\n### 10-Epoch Training\n- **V1**: 96×96 image size\n- **V4**: 128×128 image size  \n- **V3**: 160×160 image size\n\n### 20-Epoch Training  \n- **V5**: 96×96 image size\n- **V6**: 128×128 image size\n- **V7**: 160×160 image size","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input/vesuviusmonaioffline'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\nfor dirname, _, filenames in os.walk('/kaggle/input/fork-of-vesuvius-predictions'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-22T07:11:22.622603Z","iopub.execute_input":"2026-02-22T07:11:22.622787Z","iopub.status.idle":"2026-02-22T07:11:23.692773Z","shell.execute_reply.started":"2026-02-22T07:11:22.622768Z","shell.execute_reply":"2026-02-22T07:11:23.692012Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define the path to your offline packages\npackage_path = \"/kaggle/input/vesuviusmonaioffline/linux_packages\"\n\n# Install monai and imagecodecs\n# We use --no-deps to avoid conflicts with the pre-installed PyTorch/Numpy\n!pip install monai imagecodecs --no-index --find-links {package_path} --no-deps\n\n# Verify\nimport monai\nimport imagecodecs\nprint(f\"Success! Monai: {monai.__version__}, Imagecodecs: {imagecodecs.__version__}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T07:11:23.694324Z","iopub.execute_input":"2026-02-22T07:11:23.694690Z","iopub.status.idle":"2026-02-22T07:12:01.927022Z","shell.execute_reply.started":"2026-02-22T07:11:23.694665Z","shell.execute_reply":"2026-02-22T07:12:01.926375Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(\"--- DIAGNOSTICS ---\")\nTRAIN_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_images\"\n\nif os.path.exists(TRAIN_DIR):\n    print(f\"✅ Path exists: {TRAIN_DIR}\")\n    contents = os.listdir(TRAIN_DIR)\n    print(f\"📂 Contents found: {len(contents)}\")\n    \n    if len(contents) > 0:\n        print(f\"   First 5 items: {contents[:5]}\")\n        sample_path = os.path.join(TRAIN_DIR, contents[0])\n        if os.path.isdir(sample_path):\n            print(\"   👉 Structure: Sub-directories (Correct for standard dataset)\")\n        else:\n            print(\"   👉 Structure: FILES (The code was expecting Folders!)\")\n    else:\n        print(\"   ⚠️ The folder is EMPTY.\")\nelse:\n    print(f\"❌ PATH NOT FOUND: {TRAIN_DIR}\")\n    # Check parent directory to see where we are\n    parent = \"/kaggle/input/vesuvius-challenge-surface-detection\"\n    print(f\"   Checking parent: {parent}\")\n    if os.path.exists(parent):\n        print(f\"   Parent contents: {os.listdir(parent)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T07:12:01.927927Z","iopub.execute_input":"2026-02-22T07:12:01.928477Z","iopub.status.idle":"2026-02-22T07:12:01.948539Z","shell.execute_reply.started":"2026-02-22T07:12:01.928430Z","shell.execute_reply":"2026-02-22T07:12:01.947868Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# GPU","metadata":{}},{"cell_type":"markdown","source":"## 96","metadata":{}},{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np\nimport tifffile\nimport gc\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom monai.networks.nets import SegResNet\nfrom monai.losses import DiceLoss\n\n# --- CONFIGURATION ---\nif not torch.cuda.is_available():\n    raise RuntimeError(\"❌ GPU not detected. Enable 'GPU T4 x2' in Accelerator settings.\")\n\nDEVICE = \"cuda\"\nTRAIN_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_images\"\nLABEL_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_labels\"\nPATCH_SIZE = (96, 96, 96) \nBATCH_SIZE = 2\nEPOCHS = 20\nLEARNING_RATE = 1e-4\n\n# --- ROBUST LOADER ---\ndef load_volume_robust(path):\n    try:\n        vol = tifffile.imread(path)\n    except Exception:\n        try:\n            img = Image.open(path)\n            frames = []\n            while True:\n                frames.append(np.array(img))\n                try: img.seek(img.tell() + 1)\n                except EOFError: break\n            vol = np.stack(frames)\n            if vol.ndim == 2: vol = vol[None, ...] \n        except Exception as e:\n            return np.zeros((100,100,100), dtype=np.float32)\n    return vol.astype(np.float32)\n\n# --- THE FIXED DATASET ---\nclass VesuviusLazyDataset(Dataset):\n    def __init__(self, samples_per_epoch=1000):\n        self.samples_per_epoch = samples_per_epoch\n        self.file_pairs = []\n        \n        files = [f for f in os.listdir(TRAIN_DIR) if f.endswith('.tif')]\n        print(f\"Indexing {len(files)} volumes...\")\n        \n        for fname in files:\n            vol_id = fname.replace('.tif', '')\n            vol_path = os.path.join(TRAIN_DIR, fname)\n            \n            possible_labels = [\n                os.path.join(LABEL_DIR, fname),\n                os.path.join(LABEL_DIR, f\"{vol_id}_label.tif\")\n            ]\n            lbl_path = next((p for p in possible_labels if os.path.exists(p)), None)\n            \n            if lbl_path:\n                self.file_pairs.append((vol_path, lbl_path))\n        \n        print(f\"Found {len(self.file_pairs)} valid pairs.\")\n\n    def __len__(self):\n        return self.samples_per_epoch\n\n    def __getitem__(self, idx):\n        pair_idx = np.random.randint(len(self.file_pairs))\n        vol_path, lbl_path = self.file_pairs[pair_idx]\n        \n        try:\n            # 1. Load Data\n            vol = load_volume_robust(vol_path)\n            lbl = load_volume_robust(lbl_path)\n            \n            # 2. Binarize Label Safely (Forces exactly 0 or 1 for DiceLoss)\n            lbl = (lbl > 0).astype(np.float32)\n            \n            # 3. GLOBAL NORMALIZATION (Fixes \"Blindness\")\n            non_zero = vol > 0\n            if non_zero.sum() > 0:\n                v_mean, v_std = vol[non_zero].mean(), vol[non_zero].std() + 1e-8\n            else:\n                v_mean, v_std = vol.mean(), vol.std() + 1e-8\n                \n            # 4. TARGETED CROP (Fixes \"10 Tries\" Bug)\n            z_idx, y_idx, x_idx = np.nonzero(lbl)\n            \n            if len(z_idx) > 0:\n                # 90% chance to specifically crop on top of the surface\n                if np.random.rand() > 0.1:\n                    rand_i = np.random.randint(len(z_idx))\n                    z_center, y_center, x_center = z_idx[rand_i], y_idx[rand_i], x_idx[rand_i]\n                    \n                    z_start = max(0, min(z_center - PATCH_SIZE[0]//2, vol.shape[0] - PATCH_SIZE[0]))\n                    y_start = max(0, min(y_center - PATCH_SIZE[1]//2, vol.shape[1] - PATCH_SIZE[1]))\n                    x_start = max(0, min(x_center - PATCH_SIZE[2]//2, vol.shape[2] - PATCH_SIZE[2]))\n                else:\n                    # 10% chance for random background crop to reduce false positives\n                    z_start = np.random.randint(0, max(1, vol.shape[0] - PATCH_SIZE[0]))\n                    y_start = np.random.randint(0, max(1, vol.shape[1] - PATCH_SIZE[1]))\n                    x_start = np.random.randint(0, max(1, vol.shape[2] - PATCH_SIZE[2]))\n            else:\n                z_start = np.random.randint(0, max(1, vol.shape[0] - PATCH_SIZE[0]))\n                y_start = np.random.randint(0, max(1, vol.shape[1] - PATCH_SIZE[1]))\n                x_start = np.random.randint(0, max(1, vol.shape[2] - PATCH_SIZE[2]))\n                \n            img_p = vol[z_start:z_start+PATCH_SIZE[0], y_start:y_start+PATCH_SIZE[1], x_start:x_start+PATCH_SIZE[2]]\n            lbl_p = lbl[z_start:z_start+PATCH_SIZE[0], y_start:y_start+PATCH_SIZE[1], x_start:x_start+PATCH_SIZE[2]]\n\n            # 5. PADDING (Fixes PyTorch batch crashing if volume depth < 96)\n            pad_z = max(0, PATCH_SIZE[0] - img_p.shape[0])\n            pad_y = max(0, PATCH_SIZE[1] - img_p.shape[1])\n            pad_x = max(0, PATCH_SIZE[2] - img_p.shape[2])\n            \n            if pad_z > 0 or pad_y > 0 or pad_x > 0:\n                img_p = np.pad(img_p, ((0, pad_z), (0, pad_y), (0, pad_x)), mode='constant', constant_values=0)\n                lbl_p = np.pad(lbl_p, ((0, pad_z), (0, pad_y), (0, pad_x)), mode='constant', constant_values=0)\n\n            # Apply Global Normalization\n            img_p = (img_p - v_mean) / v_std\n            \n            return torch.tensor(img_p, dtype=torch.float32).unsqueeze(0), torch.tensor(lbl_p, dtype=torch.float32).unsqueeze(0)\n            \n        except Exception as e:\n            print(f\"Error loading {vol_path}: {e}\")\n            return torch.zeros((1, *PATCH_SIZE), dtype=torch.float32), torch.zeros((1, *PATCH_SIZE), dtype=torch.float32)\n\n# --- TRAIN LOOP ---\ndef train():\n    print(\"Initializing Fixed Targeted Training...\")\n    gc.collect()\n    torch.cuda.empty_cache()\n    \n    dataset = VesuviusLazyDataset(samples_per_epoch=1000)\n    if len(dataset.file_pairs) == 0: return\n\n    loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n\n    model = SegResNet(\n        spatial_dims=3, \n        in_channels=1, \n        out_channels=1, \n        init_filters=32,\n        dropout_prob=0.2\n    ).to(DEVICE)\n    \n    optimizer = torch.optim.AdamW(model.parameters(), lr=LEARNING_RATE)\n    loss_function = DiceLoss(sigmoid=True)\n\n    print(\"Starting Training...\")\n    best_loss = 1.0\n    \n    for epoch in range(EPOCHS):\n        model.train()\n        epoch_loss = 0\n        step = 0\n        \n        for inputs, labels in loader:\n            inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)\n            \n            optimizer.zero_grad()\n            \n            with torch.amp.autocast('cuda'):\n                outputs = model(inputs)\n                loss = loss_function(outputs, labels)\n            \n            loss.backward()\n            optimizer.step()\n            \n            epoch_loss += loss.item()\n            step += 1\n            print(f\"  Epoch {epoch+1}/{EPOCHS} Step {step} Loss {loss.item():.4f}\", end=\"\\r\")\n        \n        avg_loss = epoch_loss / max(step, 1)\n        print(f\"\\nEpoch {epoch+1} Avg Loss: {avg_loss:.4f}\")\n        \n        if avg_loss < best_loss:\n            best_loss = avg_loss\n            torch.save(model.state_dict(), \"best_model_96.pth\")\n            print(\"  >>> Saved best_model_96.pth\")\n            \n    print(\"\\nDONE! Download 'best_model_96.pth' from the Output tab.\")\n\nif __name__ == \"__main__\":\n    train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-22T07:12:01.949449Z","iopub.execute_input":"2026-02-22T07:12:01.949791Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 128","metadata":{}},{"cell_type":"code","source":"# import os\n# import torch\n# import numpy as np\n# import tifffile\n# from torch.utils.data import Dataset, DataLoader\n# from monai.networks.nets import SegResNet\n# from monai.losses import DiceLoss\n# import gc\n\n# # --- CONFIGURATION ---\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# TRAIN_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_images\"\n# LABEL_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_labels\"\n# PATCH_SIZE = (128, 128, 128) \n# BATCH_SIZE = 2\n# EPOCHS = 20\n# LEARNING_RATE = 1e-4\n\n# # --- LAZY DATASET ---\n# class VesuviusLazyDataset(Dataset):\n#     def __init__(self, samples_per_epoch=1000):\n#         self.samples_per_epoch = samples_per_epoch\n#         self.file_pairs = []\n        \n#         files = [f for f in os.listdir(TRAIN_DIR) if f.endswith('.tif')]\n#         print(f\"Indexing {len(files)} volumes...\")\n        \n#         for fname in files:\n#             vol_id = fname.replace('.tif', '')\n#             vol_path = os.path.join(TRAIN_DIR, fname)\n            \n#             possible_labels = [\n#                 os.path.join(LABEL_DIR, fname),\n#                 os.path.join(LABEL_DIR, f\"{vol_id}_label.tif\")\n#             ]\n#             lbl_path = next((p for p in possible_labels if os.path.exists(p)), None)\n            \n#             if lbl_path:\n#                 self.file_pairs.append((vol_path, lbl_path))\n        \n#         print(f\"Found {len(self.file_pairs)} valid pairs.\")\n\n#     def __len__(self):\n#         return self.samples_per_epoch\n\n#     def __getitem__(self, idx):\n#         pair_idx = np.random.randint(len(self.file_pairs))\n#         vol_path, lbl_path = self.file_pairs[pair_idx]\n        \n#         try:\n#             # 1. Load Data\n#             vol = tifffile.imread(vol_path)\n#             lbl = tifffile.imread(lbl_path)\n            \n#             # 2. Random Crop\n#             for _ in range(10):\n#                 x = np.random.randint(0, max(1, vol.shape[0] - PATCH_SIZE[0]))\n#                 y = np.random.randint(0, max(1, vol.shape[1] - PATCH_SIZE[1]))\n#                 z = np.random.randint(0, max(1, vol.shape[2] - PATCH_SIZE[2]))\n                \n#                 img_p = vol[x:x+PATCH_SIZE[0], y:y+PATCH_SIZE[1], z:z+PATCH_SIZE[2]]\n#                 lbl_p = lbl[x:x+PATCH_SIZE[0], y:y+PATCH_SIZE[1], z:z+PATCH_SIZE[2]]\n                \n#                 if lbl_p.sum() > 0: \n#                     break\n\n#             # 3. Normalize & CAST TO FLOAT32 (Crucial Step!)\n#             img_p = img_p.astype(np.float32)\n#             v_mean = img_p.mean()\n#             v_std = img_p.std()\n#             img_p = (img_p - v_mean) / (v_std + 1e-8)\n            \n#             # *** FIX: Force explicit float32 conversion here ***\n#             img_tensor = torch.tensor(img_p, dtype=torch.float32).unsqueeze(0)\n#             lbl_tensor = torch.tensor(lbl_p, dtype=torch.float32).unsqueeze(0)\n            \n#             return img_tensor, lbl_tensor\n            \n#         except Exception as e:\n#             print(f\"Error loading {vol_path}: {e}\")\n#             return torch.zeros((1, *PATCH_SIZE), dtype=torch.float32), torch.zeros((1, *PATCH_SIZE), dtype=torch.float32)\n\n# # --- TRAIN LOOP ---\n# def train():\n#     print(\"Initializing Fixed Training...\")\n#     gc.collect()\n#     torch.cuda.empty_cache()\n    \n#     dataset = VesuviusLazyDataset(samples_per_epoch=1000)\n#     if len(dataset.file_pairs) == 0: return\n\n#     loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n\n#     model = SegResNet(\n#         spatial_dims=3, \n#         in_channels=1, \n#         out_channels=1, \n#         init_filters=32,\n#         dropout_prob=0.2\n#     ).to(DEVICE)\n    \n#     optimizer = torch.optim.AdamW(model.parameters(), lr=LEARNING_RATE)\n#     loss_function = DiceLoss(sigmoid=True)\n\n#     print(\"Starting Training...\")\n#     best_loss = 1.0\n    \n#     for epoch in range(EPOCHS):\n#         model.train()\n#         epoch_loss = 0\n#         step = 0\n        \n#         for inputs, labels in loader:\n#             inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)\n            \n#             optimizer.zero_grad()\n            \n#             # *** FIX: Updated autocast syntax ***\n#             with torch.amp.autocast('cuda'):\n#                 outputs = model(inputs)\n#                 loss = loss_function(outputs, labels)\n            \n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             step += 1\n#             print(f\"  Epoch {epoch+1}/{EPOCHS} Step {step} Loss {loss.item():.4f}\", end=\"\\r\")\n        \n#         avg_loss = epoch_loss / max(step, 1)\n#         print(f\"\\nEpoch {epoch+1} Avg Loss: {avg_loss:.4f}\")\n        \n#         if avg_loss < best_loss:\n#             best_loss = avg_loss\n#             torch.save(model.state_dict(), \"best_model_128.pth\")\n#             print(\"  >>> Saved best_model_128.pth\")\n            \n#     print(\"\\nDONE! Download 'best_model_128.pth' from the Output tab.\")\n\n# if __name__ == \"__main__\":\n#     train()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 160","metadata":{}},{"cell_type":"code","source":"# import os\n# import torch\n# import numpy as np\n# import tifffile\n# from torch.utils.data import Dataset, DataLoader\n# from monai.networks.nets import SegResNet\n# from monai.losses import DiceLoss\n# import gc\n\n# # --- CONFIGURATION ---\n# DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n# TRAIN_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_images\"\n# LABEL_DIR = \"/kaggle/input/vesuvius-challenge-surface-detection/train_labels\"\n# PATCH_SIZE = (160, 160, 160) \n# BATCH_SIZE = 1\n# EPOCHS = 20\n# LEARNING_RATE = 1e-4\n\n# # --- LAZY DATASET ---\n# class VesuviusLazyDataset(Dataset):\n#     def __init__(self, samples_per_epoch=1000):\n#         self.samples_per_epoch = samples_per_epoch\n#         self.file_pairs = []\n        \n#         files = [f for f in os.listdir(TRAIN_DIR) if f.endswith('.tif')]\n#         print(f\"Indexing {len(files)} volumes...\")\n        \n#         for fname in files:\n#             vol_id = fname.replace('.tif', '')\n#             vol_path = os.path.join(TRAIN_DIR, fname)\n            \n#             possible_labels = [\n#                 os.path.join(LABEL_DIR, fname),\n#                 os.path.join(LABEL_DIR, f\"{vol_id}_label.tif\")\n#             ]\n#             lbl_path = next((p for p in possible_labels if os.path.exists(p)), None)\n            \n#             if lbl_path:\n#                 self.file_pairs.append((vol_path, lbl_path))\n        \n#         print(f\"Found {len(self.file_pairs)} valid pairs.\")\n\n#     def __len__(self):\n#         return self.samples_per_epoch\n\n#     def __getitem__(self, idx):\n#         pair_idx = np.random.randint(len(self.file_pairs))\n#         vol_path, lbl_path = self.file_pairs[pair_idx]\n        \n#         try:\n#             # 1. Load Data\n#             vol = tifffile.imread(vol_path)\n#             lbl = tifffile.imread(lbl_path)\n            \n#             # 2. Random Crop\n#             for _ in range(10):\n#                 x = np.random.randint(0, max(1, vol.shape[0] - PATCH_SIZE[0]))\n#                 y = np.random.randint(0, max(1, vol.shape[1] - PATCH_SIZE[1]))\n#                 z = np.random.randint(0, max(1, vol.shape[2] - PATCH_SIZE[2]))\n                \n#                 img_p = vol[x:x+PATCH_SIZE[0], y:y+PATCH_SIZE[1], z:z+PATCH_SIZE[2]]\n#                 lbl_p = lbl[x:x+PATCH_SIZE[0], y:y+PATCH_SIZE[1], z:z+PATCH_SIZE[2]]\n                \n#                 if lbl_p.sum() > 0: \n#                     break\n\n#             # 3. Normalize & CAST TO FLOAT32 (Crucial Step!)\n#             img_p = img_p.astype(np.float32)\n#             v_mean = img_p.mean()\n#             v_std = img_p.std()\n#             img_p = (img_p - v_mean) / (v_std + 1e-8)\n            \n#             # *** FIX: Force explicit float32 conversion here ***\n#             img_tensor = torch.tensor(img_p, dtype=torch.float32).unsqueeze(0)\n#             lbl_tensor = torch.tensor(lbl_p, dtype=torch.float32).unsqueeze(0)\n            \n#             return img_tensor, lbl_tensor\n            \n#         except Exception as e:\n#             print(f\"Error loading {vol_path}: {e}\")\n#             return torch.zeros((1, *PATCH_SIZE), dtype=torch.float32), torch.zeros((1, *PATCH_SIZE), dtype=torch.float32)\n\n# # --- TRAIN LOOP ---\n# def train():\n#     print(\"Initializing Fixed Training...\")\n#     gc.collect()\n#     torch.cuda.empty_cache()\n    \n#     dataset = VesuviusLazyDataset(samples_per_epoch=1000)\n#     if len(dataset.file_pairs) == 0: return\n\n#     loader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\n\n#     model = SegResNet(\n#         spatial_dims=3, \n#         in_channels=1, \n#         out_channels=1, \n#         init_filters=32,\n#         dropout_prob=0.2\n#     ).to(DEVICE)\n    \n#     optimizer = torch.optim.AdamW(model.parameters(), lr=LEARNING_RATE)\n#     loss_function = DiceLoss(sigmoid=True)\n\n#     print(\"Starting Training...\")\n#     best_loss = 1.0\n    \n#     for epoch in range(EPOCHS):\n#         model.train()\n#         epoch_loss = 0\n#         step = 0\n        \n#         for inputs, labels in loader:\n#             inputs, labels = inputs.to(DEVICE), labels.to(DEVICE)\n            \n#             optimizer.zero_grad()\n            \n#             # *** FIX: Updated autocast syntax ***\n#             with torch.amp.autocast('cuda'):\n#                 outputs = model(inputs)\n#                 loss = loss_function(outputs, labels)\n            \n#             loss.backward()\n#             optimizer.step()\n            \n#             epoch_loss += loss.item()\n#             step += 1\n#             print(f\"  Epoch {epoch+1}/{EPOCHS} Step {step} Loss {loss.item():.4f}\", end=\"\\r\")\n        \n#         avg_loss = epoch_loss / max(step, 1)\n#         print(f\"\\nEpoch {epoch+1} Avg Loss: {avg_loss:.4f}\")\n        \n#         if avg_loss < best_loss:\n#             best_loss = avg_loss\n#             torch.save(model.state_dict(), \"best_model_160.pth\")\n#             print(\"  >>> Saved best_model_160.pth\")\n            \n#     print(\"\\nDONE! Download 'best_model_160.pth' from the Output tab.\")\n\n# if __name__ == \"__main__\":\n#     train()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}