{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","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":22990,"datasetId":1136396,"databundleVersionId":2048213},{"sourceType":"datasetVersion","sourceId":2010866,"datasetId":979056,"databundleVersionId":2050389},{"sourceType":"datasetVersion","sourceId":1663886,"datasetId":982136,"databundleVersionId":1700216}],"dockerImageVersionId":30733,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# 下载data\n\n!kaggle competitions download -c hubmap-kidney-segmentation\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T00:52:54.516221Z","iopub.execute_input":"2025-12-15T00:52:54.516487Z","iopub.status.idle":"2025-12-15T00:52:55.863101Z","shell.execute_reply.started":"2025-12-15T00:52:54.516459Z","shell.execute_reply":"2025-12-15T00:52:55.861996Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#解压data\n!unzip hubmap-kidney-segmentation.zip -d data/\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import numpy as np # linear algebra\n# import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2025-11-30T10:37:10.991326Z","iopub.execute_input":"2025-11-30T10:37:10.991673Z","iopub.status.idle":"2025-11-30T10:37:10.995685Z","shell.execute_reply.started":"2025-11-30T10:37:10.991647Z","shell.execute_reply":"2025-11-30T10:37:10.994820Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# main_path = '/kaggle/input/hubmap-kidney-segmentation/'\n# main_path","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:10.997051Z","iopub.execute_input":"2025-11-30T10:37:10.997278Z","iopub.status.idle":"2025-11-30T10:37:11.005544Z","shell.execute_reply.started":"2025-11-30T10:37:10.997260Z","shell.execute_reply":"2025-11-30T10:37:11.004698Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q albumentations==1.3.0","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:28.347577Z","iopub.execute_input":"2025-12-15T02:10:28.348173Z","iopub.status.idle":"2025-12-15T02:10:38.297654Z","shell.execute_reply.started":"2025-12-15T02:10:28.348143Z","shell.execute_reply":"2025-12-15T02:10:38.296359Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%reset -f","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:40.072841Z","iopub.execute_input":"2025-12-15T02:10:40.073501Z","iopub.status.idle":"2025-12-15T02:10:40.137388Z","shell.execute_reply.started":"2025-12-15T02:10:40.073471Z","shell.execute_reply":"2025-12-15T02:10:40.136384Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:42.288273Z","iopub.execute_input":"2025-12-15T02:10:42.288920Z","iopub.status.idle":"2025-12-15T02:10:42.292730Z","shell.execute_reply.started":"2025-12-15T02:10:42.288893Z","shell.execute_reply":"2025-12-15T02:10:42.291800Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import GroupKFold\n\nimport torch\nimport torchvision\nfrom torch import nn\nfrom torch.optim import Adam\nfrom torchvision import transforms\nfrom torch.nn import functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\nfrom torch.optim.lr_scheduler import CosineAnnealingLR, ReduceLROnPlateau, CosineAnnealingWarmRestarts\n","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:44.352387Z","iopub.execute_input":"2025-12-15T02:10:44.352668Z","iopub.status.idle":"2025-12-15T02:10:49.056393Z","shell.execute_reply.started":"2025-12-15T02:10:44.352647Z","shell.execute_reply":"2025-12-15T02:10:49.055726Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom scipy.ndimage.interpolation import zoom\nfrom albumentations.pytorch import ToTensorV2\nfrom PIL import Image","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:49.057998Z","iopub.execute_input":"2025-12-15T02:10:49.058659Z","iopub.status.idle":"2025-12-15T02:10:50.111003Z","shell.execute_reply.started":"2025-12-15T02:10:49.058623Z","shell.execute_reply":"2025-12-15T02:10:50.110122Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# !pip install --upgrade --force-reinstall albumentations\n# !pip install -q opencv-python==4.5.5.64","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:22.684843Z","iopub.execute_input":"2025-11-30T10:37:22.685366Z","iopub.status.idle":"2025-11-30T10:37:22.688964Z","shell.execute_reply.started":"2025-11-30T10:37:22.685326Z","shell.execute_reply":"2025-11-30T10:37:22.688178Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import albumentations as A","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:52.037669Z","iopub.execute_input":"2025-12-15T02:10:52.038120Z","iopub.status.idle":"2025-12-15T02:10:52.042115Z","shell.execute_reply.started":"2025-12-15T02:10:52.038095Z","shell.execute_reply":"2025-12-15T02:10:52.041233Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#定义一个数据增强的类\n\nclass RandAugment:\n    def __init__(self, n, m):\n        self.n = n  # 每次要随机选择多少个数据增强操作\n        self.m = m  # 表示增强的“强度”（magnitude），比如旋转多少度、颜色变多强等\n\n    def __call__(self, img):\n        ops = self.get_augmentations()  # Get random augmentation operations\n        for op in ops:\n            img = op(img)\n        return img\n\n    def get_augmentations(self):\n        augmentation_list = [\n            A.HorizontalFlip(p=0.5), # 以 0.5 概率做水平翻转\n            A.VerticalFlip(p=0.5),   # 以 50% 概率做垂直翻转。\n            A.RandomRotate90(p=0.5), # 以 50% 概率随机旋转 0°/90°/180°/270°\n            # Add more albumentations augmentations as needed\n        ]\n        return torch.randperm(len(augmentation_list))[:self.n].tolist()\n\n# torch.randperm(n) 会生成一个 0 到 n-1 的随机排列。\n# torch.randperm(3)\n# tensor([2, 0, 1]) or tensor([1, 0, 2])\n# [:self.n] —— 截取前 n 个数字\n\n","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:10:59.091777Z","iopub.execute_input":"2025-12-15T02:10:59.092122Z","iopub.status.idle":"2025-12-15T02:10:59.099225Z","shell.execute_reply.started":"2025-12-15T02:10:59.092094Z","shell.execute_reply":"2025-12-15T02:10:59.098224Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"A.__version__","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:02.191977Z","iopub.execute_input":"2025-12-15T02:11:02.192622Z","iopub.status.idle":"2025-12-15T02:11:02.198422Z","shell.execute_reply.started":"2025-12-15T02:11:02.192593Z","shell.execute_reply":"2025-12-15T02:11:02.197605Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport sys\nimport time\nimport random","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:03.786658Z","iopub.execute_input":"2025-12-15T02:11:03.786994Z","iopub.status.idle":"2025-12-15T02:11:03.791153Z","shell.execute_reply.started":"2025-12-15T02:11:03.786965Z","shell.execute_reply":"2025-12-15T02:11:03.790226Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Dice Loss** is suitable for imbalanced segmentation tasks and directly optimizes the overlap between predicted and ground truth masks.\n（直接优化预测 mask 与真实 mask 之间的重叠（overlap）程度。）\n\n**Hausdorff Distance** is used as a metric to evaluate segmentation performance by measuring boundary discrepancies.\n\n**Lovasz Loss** optimizes the Jaccard index (IoU), making it beneficial for tasks where the IoU score is crucial.","metadata":{"execution":{"iopub.status.busy":"2024-06-14T03:55:50.444841Z","iopub.execute_input":"2024-06-14T03:55:50.445256Z","iopub.status.idle":"2024-06-14T03:55:50.452782Z","shell.execute_reply.started":"2024-06-14T03:55:50.445218Z","shell.execute_reply":"2024-06-14T03:55:50.451244Z"}}},{"cell_type":"markdown","source":"Combining a U-Net architecture with a Vision Transformer (ViT) as the encoder leverages the strengths of both convolutional networks for detailed, localized predictions and transformers for capturing global context. \n\n**U-Net with ViT Encoder**\n\n**U-Net:** Traditionally uses a series of convolutions and pooling layers in its encoder (downsampling path) to extract hierarchical features, and a series of transposed convolutions and convolutions in its decoder (upsampling path) to produce segmentation maps.\n\n**ViT (Vision Transformer):** Uses transformer layers instead of convolutions to process image patches, capturing long-range dependencies and contextual information more effectively.\n\n`Architecture Overview`\n\n**ViT Encoder:**\nSplit the input image into patches.\n\nFlatten and embed these patches.\n\nPass the embedded patches through multiple transformer layers.\n\n**U-Net Decoder:**\nUse transposed convolutions to upsample the features.\n\nCombine features from the encoder through skip connections.","metadata":{}},{"cell_type":"code","source":"#整个训练流程的大脑：所有训练、模型、优化器、scheduler、数据增强的设置都在这里统一管理。\nclass CFG:\n    data = 256 #512 # 数据集的尺寸或 patch 尺寸（你使用 256×256 图像）。\n    debug=False\n    apex=False\n    print_freq=100\n    num_workers=4\n    img_size=256 # appropriate input size for encoder \n    # 使用一种余弦退火 + 重启的学习率调度器：\n    scheduler='CosineAnnealingWarmRestarts' # ['ReduceLROnPlateau', 'CosineAnnealingLR', 'CosineAnnealingWarmRestarts']\n   \n    epoch=5 # Change epochs[5,10]\n    \n    # 训练使用 Lovasz Loss（专门为 IoU 优化的 loss）。\n    criterion= 'Lovasz' #'DiceBCELoss' # ['DiceLoss', 'Hausdorff', 'Lovasz']\n    base_model='Unet' # ['Unet']\n    encoder = 'vit' # ['attention','efficientnet-b5'] or other encoders from smp\n    lr=1e-4\n    min_lr=1e-6\n    batch_size=4\n    weight_decay=1e-6\n    gradient_accumulation_steps=1\n    seed=2021\n    n_fold=5\n    trn_fold= 0 #[0, 1, 2, 3, 4]\n    train=True\n    inference=False\n    optimizer = 'Adam'\n    T_0=10\n    \n    #RandAugment 参数\n    N=5 \n    M=9\n    \n    T_max=10\n    #factor=0.2\n    #patience=4\n    #eps=1e-6\n    smoothing=1\n    in_channels=3\n    \n    #Vision Transformer 的 transformer block 数量\n    vit_blocks=12 #[8, 12]\n    \n    vit_linear=1024 #1024\n    classes=1\n    MODEL_NAME = 'R50-ViT-B_16'","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:08.538831Z","iopub.execute_input":"2025-12-15T02:11:08.539179Z","iopub.status.idle":"2025-12-15T02:11:08.545375Z","shell.execute_reply.started":"2025-12-15T02:11:08.539150Z","shell.execute_reply":"2025-12-15T02:11:08.544383Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"main_dir = '../input/hubmap-256x256/'\ntrain_dir = '../input/hubmap-256x256//train/'\nmasks_dir = '../input/hubmap-256x256//masks/'","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:20.104690Z","iopub.execute_input":"2025-12-15T02:11:20.105370Z","iopub.status.idle":"2025-12-15T02:11:20.108881Z","shell.execute_reply.started":"2025-12-15T02:11:20.105341Z","shell.execute_reply":"2025-12-15T02:11:20.108038Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#函数通过设置 Python、NumPy、PyTorch、CUDA 和 CuDNN 的随机种子，尝试让每次训练结果尽可能一致（可复现）\ndef seed_torch(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    \n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    #the following line gives ~10% speedup\n    #but may lead to some stochasticity in the results \n    torch.backends.cudnn.benchmark = True # from @Iafoss comment\n\n#为整个训练程序设定种子\nseed_torch(seed=CFG.seed)\nprint(f\"Seeding set to: {CFG.seed}\")\n\n# deterministic=True → 追求可复现\n# benchmark=True → 追求速度，可能带来一些随机性\n# 结果大多数时候非常接近，但不保证 100% 完全一样。\n# 深度学习本身有噪声 → 你只需要“趋势稳定”","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:22.300914Z","iopub.execute_input":"2025-12-15T02:11:22.301253Z","iopub.status.idle":"2025-12-15T02:11:22.310014Z","shell.execute_reply.started":"2025-12-15T02:11:22.301225Z","shell.execute_reply":"2025-12-15T02:11:22.309292Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Dataset","metadata":{"execution":{"iopub.status.busy":"2024-06-14T04:11:34.225712Z","iopub.execute_input":"2024-06-14T04:11:34.226928Z","iopub.status.idle":"2024-06-14T04:11:34.232182Z","shell.execute_reply.started":"2024-06-14T04:11:34.22686Z","shell.execute_reply":"2024-06-14T04:11:34.230815Z"}}},{"cell_type":"code","source":"# \nimport os\n\nDATASET_DIR = \"/kaggle/input/hubmap-256x256-test-data\"   # ← 改成你右侧 dataset 名称\n\nfor root, dirs, files in os.walk(DATASET_DIR):\n    print(\"📁 Folder:\", root)\n    for f in files:\n        print(\"   -\", f)\n    print()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-15T02:27:14.176220Z","iopub.execute_input":"2025-12-15T02:27:14.176996Z","iopub.status.idle":"2025-12-15T02:27:22.714015Z","shell.execute_reply.started":"2025-12-15T02:27:14.176967Z","shell.execute_reply":"2025-12-15T02:27:22.713216Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv('../input/hubmap-kidney-segmentation/train.csv')\ntrain_df.head()\n# train_df.count()\n\n# 从像素编号 296084587 开始，连续填充 4 个像素 = 1（前景）其他都是0.","metadata":{"execution":{"iopub.status.busy":"2025-12-15T02:11:30.973271Z","iopub.execute_input":"2025-12-15T02:11:30.974177Z","iopub.status.idle":"2025-12-15T02:11:31.357117Z","shell.execute_reply.started":"2025-12-15T02:11:30.974145Z","shell.execute_reply":"2025-12-15T02:11:31.356115Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.count()\n\n#这表示你当前使用的是一个 预处理过的 256×256 patch 数据集，其中只有 15 个 patch 包含有效 mask。","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T10:37:23.221570Z","iopub.execute_input":"2025-11-30T10:37:23.221829Z","iopub.status.idle":"2025-11-30T10:37:23.229169Z","shell.execute_reply.started":"2025-11-30T10:37:23.221804Z","shell.execute_reply":"2025-11-30T10:37:23.228270Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#定义 5 套不同的数据增强，将会得到5组数据\n# base / weak / strong 都是对原图做不同程度的 transformation\n'''\n| mode   | 用途     | 特点                            |\n| ------ | ------ | ----------------------------- |\n| base   | 常用训练增强 | 翻转、旋转、变形、颜色增强                 |\n| rand   | 强增强    | RandAugment + 颜色 + 几何增强       |\n| strong | 最强增强   | elastic + noise + distortions |\n| weak   | 较弱增强   | 小幅度旋转 + 翻转 + resize           |\n| valid  | 验证     | 无增强，只 resize                  |\n\n\n为什么要这么多模式？\n\n因为：\n\n某些模型早期适合 weak\n\n中期适合 base\n\n提升泛化能力适合 strong / rand\n\n验证必须 valid\n\n'''\ndef get_transform(mode='base'):\n    if mode == 'base':\n        base_transform = A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size, p = 1.0),\n            A.HorizontalFlip(),\n            A.VerticalFlip(),\n            A.RandomRotate90(),\n            A.ShiftScaleRotate(\n                shift_limit = 0.0625,\n                scale_limit = 0.2,\n                rotate_limit = 20,\n                p = 0.4,\n                border_mode = cv2.BORDER_REFLECT\n            ),\n            A.OneOf([\n                A.OpticalDistortion(p=0.4),\n                A.GridDistortion(p = 0.1),\n                A.IAAPiecewiseAffine(p=0.4)\n            ], p=0.3),\n            \n            A.OneOf([\n                A.HueSaturationValue(10,15,10),\n                A.CLAHE(clip_limit = 3),\n                A.RandomBrightnessContrast(),\n            ], p = 0.4),\n            ToTensorV2()\n        ],p = 1.0)\n        \n        return base_transform\n    \n    elif mode == 'rand':\n        rand_transform = A.Compose([\n            RandAugment(CFG.N, CFG.M),\n            A.Transpose(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.HorizontalFlip(p=0.5),\n            A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, border_mode=0, p=0.85),\n            A.Resize(CFG.img_size, CFG.img_size, p=1.0),\n            A.Normalize(),\n            ToTensorV2()\n        ])\n        return rand_transform\n    \n    elif mode == 'strong':\n        strong_transform = A.Compose([\n            A.Transpose(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.HorizontalFlip(p=0.5),\n            A.ElasticTransform(alpha=120, sigma=120 * 0.05, alpha_affine=120 * 0.03, p=0.5),\n            A.OneOf([\n                A.RandomGamma(),\n                A.GaussNoise()           \n            ], p=0.5),\n            A.OneOf([\n                A.OpticalDistortion(p=0.4),\n                A.GridDistortion(p=0.2),\n                A.IAAPiecewiseAffine(p=0.4),\n            ], p=0.5),\n            A.OneOf([\n                A.HueSaturationValue(10,15,10),\n                A.CLAHE(clip_limit=4),\n                A.RandomBrightnessContrast(),            \n            ], p=0.5),\n            A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.1, rotate_limit=15, border_mode=0, p=0.85),\n            A.Resize(CFG.img_size, CFG.img_size, p=1.0),\n            ToTensorV2()\n        ])\n    \n        return strong_transform\n    \n    elif mode == 'weak':\n        weak_transform = A.Compose([\n            A.Resize(CFG.img_size, CFG.img_size, p=0.5),\n            A.HorizontalFlip(),\n            A.VerticalFlip(),\n            A.RandomRotate90(),\n            A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=15, p=0.4, \n                               border_mode=cv2.BORDER_REFLECT),\n            ToTensorV2()\n            ], p=1.0)\n        \n        return weak_transform\n    \n    elif mode == 'valid':\n        val_transform = A.Compose([\n                A.Resize(CFG.img_size, CFG.img_size, p=1.0),\n                ToTensorV2()\n            ], p=1.0)\n        return val_transform\n    \n    else:\n        print(\"Mode Unknown!\")\n        ","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.230287Z","iopub.execute_input":"2025-11-30T10:37:23.230527Z","iopub.status.idle":"2025-11-30T10:37:23.245220Z","shell.execute_reply.started":"2025-11-30T10:37:23.230508Z","shell.execute_reply":"2025-11-30T10:37:23.244575Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# rand = get_transform(mode = 'rand')","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.246253Z","iopub.execute_input":"2025-11-30T10:37:23.246689Z","iopub.status.idle":"2025-11-30T10:37:23.256393Z","shell.execute_reply.started":"2025-11-30T10:37:23.246669Z","shell.execute_reply":"2025-11-30T10:37:23.255714Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#值从哪儿来？\n\nmean = np.array([0.65459856,0.48386562,0.69428385])\nstd = np.array([0.15167958,0.23584107,0.13146145])\nmean, std","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.257480Z","iopub.execute_input":"2025-11-30T10:37:23.258259Z","iopub.status.idle":"2025-11-30T10:37:23.269581Z","shell.execute_reply.started":"2025-11-30T10:37:23.258231Z","shell.execute_reply":"2025-11-30T10:37:23.268929Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 根据 train.csv 的 id，从硬盘上筛选对应的 patch 文件名，构建训练数据集。\n# train.csv 决定了「哪些原图有标注（mask）」以及「哪些原图属于训练集」。\n# 没有在 train.csv 里的图，就没有 mask，不能用来训练。\n\n'''\n① 看 train.csv 中哪些原图有标签\n② 从 train 目录中找到所有这些原图切出来的 patch\n③ 用这些 patch 和它们的 mask 来训练模型\n\n'''\n\nclass HuBMAPDataset(Dataset):\n    def __init__(self, main_dir, df, train = True, transform = None):\n        self.ids = df.id.values\n        self.fnames = [fname for fname in os.listdir(train_dir) if fname.split('_')[0] in self.ids]\n        \n        self.main_dir = main_dir\n        self.df = df\n        self.train = train\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.fnames)\n    \n    def __getitem__(self, idx):\n        fname = self.fnames[idx]\n        \n        img = cv2.cvtColor(cv2.imread(os.path.join(main_dir, 'train', fname)), cv2.COLOR_BGR2RGB)\n        mask = cv2.imread(os.path.join(main_dir, 'masks', fname), cv2.IMREAD_GRAYSCALE)\n        \n        if self.transform is not None:\n            aug = self.transform(image = img, mask = mask)\n            img, mask = aug['image'], aug['mask']\n            \n        img = img.type('torch.FloatTensor')\n        img = img/255\n        mask = mask.type('torch.FloatTensor')\n        \n        return img, mask","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.270639Z","iopub.execute_input":"2025-11-30T10:37:23.270869Z","iopub.status.idle":"2025-11-30T10:37:23.279946Z","shell.execute_reply.started":"2025-11-30T10:37:23.270851Z","shell.execute_reply":"2025-11-30T10:37:23.279260Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"#### Visualize Augmented Data","metadata":{}},{"cell_type":"code","source":"#这个函数用来可视化（展示）数据增强后的图像和对应的 mask，看看 augmentation 是否正常。\n\ndef vis_aug_data(dataset, length = 6):\n    # 创建图像展示区域（画布）\n    plt.figure(figsize = (15,10))\n    \n    N = length // 2\n\n    #循环展示 length 组数据\n    for i in range(length):\n        image, mask = train_dataset[i]\n        plt.subplot(3,4,2*i+1)\n        #显示 image，并转换维度\n        plt.imshow(np.transpose((image),(1,2,0)))\n        plt.axis(False)\n        plt.subplot(3,4,2*i+2)\n        plt.imshow(mask)\n        plt.axis(False)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.280840Z","iopub.execute_input":"2025-11-30T10:37:23.281090Z","iopub.status.idle":"2025-11-30T10:37:23.291414Z","shell.execute_reply.started":"2025-11-30T10:37:23.281064Z","shell.execute_reply":"2025-11-30T10:37:23.290618Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 创建带有 base 数据增强的 HuBMAP 训练数据集，并可视化增强后的图像和 mask，以检查增强是否正常\n# train_dataset = 使用 base augmentation 的训练数据\n# vis_aug_data = 显示 前6 个增强后的 (image, mask) 样本\n# base augmentation\ntrain_dataset = HuBMAPDataset(main_dir, \n                              train_df, \n                              train=True, \n                              transform=get_transform('base'))\n\nvis_aug_data(train_dataset, 6)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:23.292337Z","iopub.execute_input":"2025-11-30T10:37:23.292568Z","iopub.status.idle":"2025-11-30T10:37:24.377877Z","shell.execute_reply.started":"2025-11-30T10:37:23.292551Z","shell.execute_reply":"2025-11-30T10:37:24.377041Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# weak augmentation\n\ntrain_dataset = HuBMAPDataset(main_dir, train_df, train=True, transform=get_transform('weak'))\n\nvis_aug_data(train_dataset, 6)\n# train_dataset[0][1].shape","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:24.378938Z","iopub.execute_input":"2025-11-30T10:37:24.379227Z","iopub.status.idle":"2025-11-30T10:37:25.130852Z","shell.execute_reply.started":"2025-11-30T10:37:24.379203Z","shell.execute_reply":"2025-11-30T10:37:25.130003Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# strong augmentation\ntrain_dataset = HuBMAPDataset(main_dir, train_df, train=True, transform=get_transform('strong'))\n\nvis_aug_data(train_dataset, 6)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:25.135064Z","iopub.execute_input":"2025-11-30T10:37:25.135323Z","iopub.status.idle":"2025-11-30T10:37:26.447178Z","shell.execute_reply.started":"2025-11-30T10:37:25.135303Z","shell.execute_reply":"2025-11-30T10:37:26.446281Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# valid augmentation\ntrain_dataset = HuBMAPDataset(main_dir, train_df, train=True, transform=get_transform('valid'))\n\nvis_aug_data(train_dataset, 6)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:26.448239Z","iopub.execute_input":"2025-11-30T10:37:26.448663Z","iopub.status.idle":"2025-11-30T10:37:27.181043Z","shell.execute_reply.started":"2025-11-30T10:37:26.448640Z","shell.execute_reply":"2025-11-30T10:37:27.180250Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 查看你的 patch 文件夹里有哪些原图 ID（比如：2f6ecfcdf、aaa6a05cc）。\n# 它从文件名中提取出每个 patch 所对应的原图 ID，做成一个 DataFrame。\n'''\n这个 DataFrame (dir_df) 中 一定会有大量重复的 id，因为：\n\n同一张原图（同一个 ID）会被切成很多个 patch，所以这个 ID 会出现在许多行里。\n\n'''\n\nif CFG.data == 512:\n    directory_list = \"no name\"\nelif CFG.data == 256:\n    directory_list = os.listdir('../input/hubmap-256x256/train')\n    \ndirectory_list = [fnames.split('_')[0] for fnames in directory_list]\ndir_df = pd.DataFrame(directory_list, columns=['id'])\ndir_df","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:27.182235Z","iopub.execute_input":"2025-11-30T10:37:27.182629Z","iopub.status.idle":"2025-11-30T10:37:27.206102Z","shell.execute_reply.started":"2025-11-30T10:37:27.182603Z","shell.execute_reply":"2025-11-30T10:37:27.205322Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Model ","metadata":{}},{"cell_type":"code","source":"#正在设置环境，准备使用 TransUNet（UNet + ViT Encoder），而不是普通 UNet\n\n!wget https://storage.googleapis.com/vit_models/imagenet21k/R50%2BViT-B_16.npz\n#! pip install self-attention-cv\n!pip install einops\n!pip install ml_collections\ntu_path = '../input/transunet/TransUNet-main'\nsys.path.append(tu_path)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:27.207297Z","iopub.execute_input":"2025-11-30T10:37:27.207638Z","iopub.status.idle":"2025-11-30T10:37:59.383333Z","shell.execute_reply.started":"2025-11-30T10:37:27.207610Z","shell.execute_reply":"2025-11-30T10:37:59.382434Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nPath('/kaggle/working/networks').mkdir(parents=True, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.384741Z","iopub.execute_input":"2025-11-30T10:37:59.385029Z","iopub.status.idle":"2025-11-30T10:37:59.389709Z","shell.execute_reply.started":"2025-11-30T10:37:59.385003Z","shell.execute_reply":"2025-11-30T10:37:59.388938Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import requests\n\nif Path(\"/kaggle/working/networks/vit_seg_modeling.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `vit_seg_modeling.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/Beckschen/TransUNet/main/networks/vit_seg_modeling.py\")\n    with open(\"/kaggle/working/networks/vit_seg_modeling.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.390981Z","iopub.execute_input":"2025-11-30T10:37:59.391273Z","iopub.status.idle":"2025-11-30T10:37:59.517809Z","shell.execute_reply.started":"2025-11-30T10:37:59.391248Z","shell.execute_reply":"2025-11-30T10:37:59.517106Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nimport requests\n\nif Path(\"/kaggle/working/networks/vit_seg_configs.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `vit_seg_configs.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/Beckschen/TransUNet/main/networks/vit_seg_configs.py\")\n    with open(\"/kaggle/working/networks/vit_seg_configs.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.518855Z","iopub.execute_input":"2025-11-30T10:37:59.519425Z","iopub.status.idle":"2025-11-30T10:37:59.627533Z","shell.execute_reply.started":"2025-11-30T10:37:59.519397Z","shell.execute_reply":"2025-11-30T10:37:59.626792Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import requests\n\nif Path(\"/kaggle/working/networks/vit_seg_modeling_resnet_skip.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `vit_seg_configs.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/Beckschen/TransUNet/main/networks/vit_seg_modeling_resnet_skip.py\")\n    with open(\"/kaggle/working/networks/vit_seg_modeling_resnet_skip.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.628367Z","iopub.execute_input":"2025-11-30T10:37:59.628575Z","iopub.status.idle":"2025-11-30T10:37:59.738148Z","shell.execute_reply.started":"2025-11-30T10:37:59.628559Z","shell.execute_reply":"2025-11-30T10:37:59.737445Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import os\n# os.remove('/kaggle/working/vit_seg_modeling.py')","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.739023Z","iopub.execute_input":"2025-11-30T10:37:59.739249Z","iopub.status.idle":"2025-11-30T10:37:59.742660Z","shell.execute_reply.started":"2025-11-30T10:37:59.739229Z","shell.execute_reply":"2025-11-30T10:37:59.741902Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n#导入 ViT 模型与配置字典\n\nfrom networks.vit_seg_modeling import VisionTransformer as ViT_seg\nfrom networks.vit_seg_modeling import CONFIGS as CONFIGS_ViT_seg\n\n#选择模型配置\nconfig_vit = CONFIGS_ViT_seg[CFG.MODEL_NAME]\n\n#修改配置（非常重要）\n\n#设置输出类别数（0=背景，1=肾小球）\nconfig_vit.n_classes = 1\n# 设置 skip connections 数量\nconfig_vit.n_skip = 3\n#设置 ViT 预训练权重\nconfig_vit.pretrained_path = './R50+ViT-B_16.npz'\n# 设置 transformer dropout\nconfig_vit.transformer.dropout_rate = 0.2\n# 设置 MLP 隐藏层维度（ViT-B/16 的默认 mlp_dim 就是：768*4 = 3072）\nconfig_vit.transformer.mlp_dim = 3072\n#设置 attention heads 数量\nconfig_vit.transformer.num_heads = 4\n# 设置 transformer 层数 （原始 ViT-B/16 = 12 layers， 这里减少到 8，是为了：\n# 降低显存，减少训练时间，仍保持较好性能\nconfig_vit.transformer.num_layers = 8\n\nconfig_vit","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:37:59.743525Z","iopub.execute_input":"2025-11-30T10:37:59.743745Z","iopub.status.idle":"2025-11-30T10:38:00.029688Z","shell.execute_reply.started":"2025-11-30T10:37:59.743727Z","shell.execute_reply":"2025-11-30T10:38:00.028900Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ViTHuBMAP(nn.Module):\n    # 构造函数，设置默认配置\n    def __init__(self, configs = config_vit):\n        super(ViTHuBMAP,self).__init__()\n        \n        # 构建 TransUNet 模型（核心！）\n        self.model = ViT_seg(configs, \n                             img_size = CFG.img_size, \n                             num_classes = CFG.classes)\n        # 加载预训练权重（不需要再次预训练）\n        self.model.load_from(weights = np.load(configs.pretrained_path))\n        \n    # forward：定义前向传播（模型怎么跑）   \n    def forward(self, x):\n        img_segs = self.model(x)\n        return img_segs","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.030751Z","iopub.execute_input":"2025-11-30T10:38:00.031005Z","iopub.status.idle":"2025-11-30T10:38:00.036032Z","shell.execute_reply.started":"2025-11-30T10:38:00.030985Z","shell.execute_reply":"2025-11-30T10:38:00.035291Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#专门创建一个文件夹，用来存放所有自定义的 loss 函数文件\n\nPath('/kaggle/working/losses_pytorch').mkdir(parents=True, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.037009Z","iopub.execute_input":"2025-11-30T10:38:00.037239Z","iopub.status.idle":"2025-11-30T10:38:00.050542Z","shell.execute_reply.started":"2025-11-30T10:38:00.037221Z","shell.execute_reply":"2025-11-30T10:38:00.049750Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 检查是否有 Hausdorff Loss这个文件\nif Path(\"/kaggle/working/losses_pytorch/hausdorff.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `hausdorff.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/JunMa11/SegLossOdyssey/master/losses_pytorch/hausdorff.py\")\n    with open(\"/kaggle/working/losses_pytorch/hausdorff.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.051582Z","iopub.execute_input":"2025-11-30T10:38:00.051857Z","iopub.status.idle":"2025-11-30T10:38:00.185646Z","shell.execute_reply.started":"2025-11-30T10:38:00.051838Z","shell.execute_reply":"2025-11-30T10:38:00.184772Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 检查Lovasz Loss\nif Path(\"/kaggle/working/losses_pytorch/lovasz_loss.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `lovasz_loss.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/JunMa11/SegLossOdyssey/master/losses_pytorch/lovasz_loss.py\")\n    with open(\"/kaggle/working/losses_pytorch/lovasz_loss.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.186683Z","iopub.execute_input":"2025-11-30T10:38:00.186908Z","iopub.status.idle":"2025-11-30T10:38:00.297082Z","shell.execute_reply.started":"2025-11-30T10:38:00.186890Z","shell.execute_reply":"2025-11-30T10:38:00.296298Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 检查Focal_loss\n\nif Path(\"/kaggle/working/losses_pytorch/focal_loss.py\").is_file():\n    print(\"Already Exist!\")\nelse:\n    print(\"Downloading `focal_loss.py` ...\")\n    request = requests.get(\"https://raw.githubusercontent.com/JunMa11/SegLossOdyssey/master/losses_pytorch/focal_loss.py\")\n    with open(\"/kaggle/working/losses_pytorch/focal_loss.py\",\"wb\") as f:\n        f.write(request.content)\n    print(\"Completed\")","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.298159Z","iopub.execute_input":"2025-11-30T10:38:00.298862Z","iopub.status.idle":"2025-11-30T10:38:00.404815Z","shell.execute_reply.started":"2025-11-30T10:38:00.298826Z","shell.execute_reply":"2025-11-30T10:38:00.403972Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from losses_pytorch.hausdorff import HausdorffDTLoss\nfrom losses_pytorch.lovasz_loss import LovaszSoftmax\nfrom losses_pytorch.focal_loss import FocalLoss","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.405730Z","iopub.execute_input":"2025-11-30T10:38:00.405973Z","iopub.status.idle":"2025-11-30T10:38:00.414593Z","shell.execute_reply.started":"2025-11-30T10:38:00.405954Z","shell.execute_reply":"2025-11-30T10:38:00.413882Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#定义计算Diceloss = 1-Dice Score\n\nclass DiceLoss(nn.Module):\n    def __init__(self, weight = None, size_average = True):\n        super(DiceLoss, self).__init__()\n   \n    \n    def forward(self, inputs, targets, smooth = CFG.smoothing):\n        # #首先对模型输出做 sigmoid（Sigmoid 把它变成概率（0～1））\n        inputs = F.sigmoid(inputs)\n\n        #展平为 1D 向量\n        inputs = inputs.view(-1)\n        targets = targets.view(-1)\n        \n        # 计算交集 intersection\n        # 乘积表示：“预测为正类的概率 × Ground truth 正类像素” \n        # 全部相加就是预测与真实的“重叠面积”。\n        #也就是dice公式中的：∣P∩G∣\n        intersection = (inputs * targets).sum()\n\n        #计算 Dice Score = 2*∣P∩G∣/（∣P｜+｜G∣）\n        dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)\n        \n        return dice\n\n# 这个实现实际上返回的是 Dice Score（越大越好），不是 Loss（越小越好）\n# Dice Loss = 1 - Dice Score。\n# Dice越接近1预测越准。\n\n\n'''\n为什么加smooth？\n1：避免分母为 0（最重要）\n2.使 gradient 更稳定（尤其是小目标）\n3.避免 Dice 在前景面积极小时出现极端高/低值\n'''","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.415567Z","iopub.execute_input":"2025-11-30T10:38:00.415854Z","iopub.status.idle":"2025-11-30T10:38:00.424643Z","shell.execute_reply.started":"2025-11-30T10:38:00.415826Z","shell.execute_reply":"2025-11-30T10:38:00.423856Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#DiceBCELoss = Dice Loss + Binary Cross Entropy (BCE)\n'''\nDice 强在哪里？\n→ 区域重叠，解决前景小的问题\n\nBCE 强在哪里？\n→ 每像素优化，收敛快，不会震荡\n\n解决类别不平衡\nDice 解决 foreground 少的问题\n\n\n为什么 segmentation 要用 BCE？\n\n因为 segmentation 本质就是：“每个像素都是一个二分类问题：1 or 0”\n所以 BCE 对 segmentation 非常自然、非常合适\n\n它擅长：\n\n✔ 1. 提供“稳定、连续”的梯度 → 训练容易：BCE 的梯度不会消失、不会爆炸，训练非常稳。\n\n✔ 2. 在边界处效果很好：BCE 会逐像素惩罚，把细节学得更清楚。\n\n✔ 3. 小、简单任务用 BCE 足够好：例如前景不太稀疏的 segmentation。\n\n但是单独用BCE：Class imbalance（分类不平衡）\n'''\n\nclass DiceBCELoss(nn.Module):\n    \n    # Formula Given Above\n    def __init__(self, weight = None, size_average = True):\n        super(DiceBCELoss, self).__init__()\n        \n    def forward(self, inputs, targets, smooth = CFG.smoothing):\n        #sigmoid 把 logits 转成概率\n        inputs = F.sigmoid(inputs)\n\n        #展平成一维\n        inputs = inputs.view(-1)\n        targets = targets.view(-1)\n        \n        #计算 intersection（Dice numerator）\n        intersection = (inputs * targets).mean()\n        dice_loss = 1 - (2.*intersection + smooth)/(inputs.mean() + targets.mean() + smooth)\n        BCE = F.binary_cross_entropy(inputs, targets, reduction = 'mean')\n        \n        Dice_BCE = BCE + dice_loss\n        \n        return Dice_BCE  ","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.425665Z","iopub.execute_input":"2025-11-30T10:38:00.425945Z","iopub.status.idle":"2025-11-30T10:38:00.434260Z","shell.execute_reply.started":"2025-11-30T10:38:00.425925Z","shell.execute_reply":"2025-11-30T10:38:00.433423Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#为三种不同的 Loss 定义 PyTorch 封装类，方便之后在训练中切换\n'''\n1. Hausdorff Distance Loss\n\n用来衡量 预测边界 vs 真实边界 的差距\n越接近 → loss 越小\n对“肾小球边缘”特别重要\n\n2. Focal Loss 是什么？\n\n专门为“不平衡分类”设计。\n降低 easy samples（容易预测背景）权重\n提高 hard samples（前景、边界、难预测像素）权重\n对背景给予很低权重,强调肾小球（前景像素）,大幅缓解类别不平衡\n\n3. Lovasz Loss 是什么？\n\nIoU（Jaccard）本身的 surrogate loss\n这非常适合 segmentation，因为：比 Dice 对面积差异更敏感\n直接优化竞赛中用的 IoU\n对“不规则形状”、“边界模糊”、“类不平衡”特别有效\n\n\n\nHausdorff Loss 强调边界距离 → 改善 segmentation 边缘质量\n\nFocal Loss 解决前景稀少 → 强化肾小球像素\n\nLovasz Loss 直接优化 IoU → 改善整体 segmentation 性能\n\n\n'''\nclass Hausdorff_loss(nn.Module):\n    def __init__(self):\n        super(Hausdorff_loss, self).__init__()\n        \n    def forward(self, inputs, targets):\n        return HausdorffDTLoss()(inputs, targets)\n    \nclass FocalDLoss(nn.Module):\n    def __init__(self):\n        super(FocalDLoss, self).__init__()\n        \n    def forward(self, inputs, targets):\n        return FocalLoss()(inputs, targets)\n    \n    \nclass Lovasz_loss(nn.Module):\n    def __init__(self):\n        super(Lovasz_loss, self).__init__()\n        \n    def forward(self, inputs, targets):\n        return LovaszSoftmax()(inputs, targets)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.435269Z","iopub.execute_input":"2025-11-30T10:38:00.435586Z","iopub.status.idle":"2025-11-30T10:38:00.445758Z","shell.execute_reply.started":"2025-11-30T10:38:00.435566Z","shell.execute_reply":"2025-11-30T10:38:00.445107Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#根据配置（CFG.criterion）自动选择你要用的 Loss\n\nif CFG.criterion == 'DiceBCELoss':\n    criterion = DiceBCELoss()\nelif CFG.criterion == 'DiceLoss':\n    criterion = DiceLoss()\nelif CFG.criterion == 'FocalLoss':\n    criterion = FocalDLoss()\nelif CFG.criterion == 'Hausdorff':\n    criterion = Hausdorff_loss()\nelif CFG.criterion == 'Lovasz':\n    criterion = Lovasz_loss()\n\n# criterion","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.446516Z","iopub.execute_input":"2025-11-30T10:38:00.446760Z","iopub.status.idle":"2025-11-30T10:38:00.456270Z","shell.execute_reply.started":"2025-11-30T10:38:00.446742Z","shell.execute_reply":"2025-11-30T10:38:00.455558Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#在一次训练步骤中：执行 forward → 计算 loss → 返回 loss 和预测结果\n'''\n你可以把这个函数理解成：“一次 forward + loss 计算”的打包函数\n\n它会自动完成：\n\n把 model / images / targets 放到 GPU\n\n执行模型预测\n\n计算 loss\n\n返回 loss 和预测结果\n\n训练 loop 就可以保持非常干净、简洁\n\n'''\ndef HuBMAPLoss(images, targets, model, device, loss_func = criterion):\n    model.to(device)\n    images = images.to(device)\n    targets = targets.to(device)\n\n    outputs = model(images)\n    loss = loss_func(outputs, targets)\n    \n    return loss, outputs\n","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.457115Z","iopub.execute_input":"2025-11-30T10:38:00.457312Z","iopub.status.idle":"2025-11-30T10:38:00.463787Z","shell.execute_reply.started":"2025-11-30T10:38:00.457296Z","shell.execute_reply":"2025-11-30T10:38:00.463137Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#定义流程：并没有开始train\n#train_one_epoch() → 用训练集训练模型（开启梯度，反向传播）\n\ndef train_one_epoch(epoch, model, device, optimizer, scheduler, trainloader):\n    model.train()\n    t = time.time()\n    total_loss = 0 \n    \n    for step, (images, targets) in enumerate(trainloader):\n        loss, outputs = HuBMAPLoss(images, targets, model, device)\n        \n        #把 loss 反传，计算梯度，但是：⚠️ 没有立刻更新参数\n        # 因为使用了 gradient accumulation（梯度累积）。\n        loss.backward()\n        \n        # 梯度累积（每 4 个 batch 更新一次）\n        # 为了确保：即使训练集最后剩下不足 4 个 batch也要执行一次 optimizer.step()\n        # 否则最后几个 batch 的梯度会丢失。\n        if ((step+1)%4==0 or (step+1)==len(trainloader)):\n            optimizer.step()\n            scheduler.step()\n            optimizer.zero_grad()\n        loss = loss.detach().item()\n        total_loss += loss\n\n        # 每 10 个 batch 打印一次训练状态，或者到最后一个 batch 打印一次。\n        if ((step+1)%10==0 or (step+1)==len(trainloader)):\n            print(\n                    f'epoch {epoch} train step {step+1}/{len(trainloader)}, ' + \\\n                    f'loss: {total_loss/len(trainloader):.4f}, ' + \\\n                    f'time: {(time.time() - t):.4f}', end= '\\r' if (step + 1) != len(trainloader) else '\\n'\n                )\n\n            \n#valid_one_epoch()：→ 用验证集评估模型（不更新参数）        \ndef valid_one_epoch(epoch, model, device, optimizer, scheduler, validloader):\n    model.eval()\n    t = time.time()\n    total_loss = 0\n    \n    for step, (images, targets) in enumerate(validloader):\n        loss, outputs = HuBMAPLoss(images, targets, model, device)\n        loss = loss.detach().item()\n        total_loss += loss\n        \n        if ((step+1)%4==0 or (step+1)==len(validloader)):\n            scheduler.step(total_loss/len(validloader))\n        \n        if ((step+1)%10==0 or (step+1)==len(validloader)):\n            print(\n                    f'**epoch {epoch} trainz step {step+1}/{len(validloader)}, ' + \\\n                    f'loss: {total_loss/len(validloader):.4f}, ' + \\\n                    f'time: {(time.time() - t):.4f}', end= '\\r' if (step + 1) != len(validloader) else '\\n'\n                )\n\n        ","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.465134Z","iopub.execute_input":"2025-11-30T10:38:00.465722Z","iopub.status.idle":"2025-11-30T10:38:00.475555Z","shell.execute_reply.started":"2025-11-30T10:38:00.465696Z","shell.execute_reply":"2025-11-30T10:38:00.474704Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# data preparation（数据准备）data splitting（数据划分）cross-validation 设置（n folds）\n### Creating fold columns\n\n# 从你的配置中读取折数（也就是要切成几份） cross validation。\nFOLDS = CFG.n_fold\n\n# 创建一个 GroupKFold 分折器。\n# GroupKFold 保证：同一张大图（同一个 slide id）所有 patch 一定会在同一个 fold 中\ngkf = GroupKFold(FOLDS)\n\n# 给 DataFrame 新建一列 Folds，用来保存每个 id 属于哪一个 fold。\n# 初始全部设成 0。\ndir_df['Folds'] = 0\n\n#同一个 “id” 的所有 patch 会被归为一组\nfor fold, (tr_idx, val_idx) in enumerate(gkf.split(dir_df, groups = dir_df[dir_df.columns[0]].values)):\n    dir_df.loc[val_idx, 'Folds'] = fold\n    \ndir_df\n\n\n#每个 slide（原始大图）被切成多个 patch（256×256）。\n#所有来自同一个 slide 的 patch 必须属于同一个 fold。","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.476455Z","iopub.execute_input":"2025-11-30T10:38:00.476689Z","iopub.status.idle":"2025-11-30T10:38:00.507557Z","shell.execute_reply.started":"2025-11-30T10:38:00.476671Z","shell.execute_reply":"2025-11-30T10:38:00.506799Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### The Real Training","metadata":{}},{"cell_type":"code","source":"#根据选择的 fold，把数据分成 train 和 validation，然后创建对应的数据加载器（DataLoader）。\n\ndef prepare_train_valid_dataloader(df, fold):\n    #按 fold 分割 train / validation\n    train_ids = df[~(df.Folds.isin(fold))]\n    val_ids = df[df.Folds.isin(fold)]\n\n    #创建训练 Dataset：读取 train_ids 下的所有 patch，使用 BASE 数据增强（强度适中的增强）\n    train_ds = HuBMAPDataset(main_dir, train_ids, train = True, transform = get_transform('base'))\n    \n    #创建验证 Dataset：valid 模式（只做 resize，不做增强）\n    val_ds = HuBMAPDataset(main_dir, val_ids, train = True, transform = get_transform('valid'))\n\n    #创建训练 DataLoader\n    train_loader = DataLoader(train_ds, batch_size = CFG.batch_size, pin_memory = True, shuffle = True, num_workers=CFG.num_workers)\n    \n    #创建验证 DataLoader\n    val_loader = DataLoader(val_ds, batch_size = CFG.batch_size, pin_memory = True, shuffle = False, num_workers= CFG.num_workers)\n    \n    \n    return train_loader, val_loader\n\n'''\n为什么用base数据增强？\n\n底层原始数据没变（磁盘上的 patch 一样），但喂给模型的图像，是“原图的随机变形版本”\n\n好处：\n增加“有效样本数”（同一张图每个 epoch 看起来都不一样）\n提高模型泛化能力\n\n'''","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.508473Z","iopub.execute_input":"2025-11-30T10:38:00.508766Z","iopub.status.idle":"2025-11-30T10:38:00.515870Z","shell.execute_reply.started":"2025-11-30T10:38:00.508739Z","shell.execute_reply":"2025-11-30T10:38:00.515136Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n#加载你的模型并放到 device\nmodel = ViTHuBMAP().to(device)\n\n#定义优化器 Optimizer（Adam）\noptimizer = Adam(model.parameters(), \n                 lr = CFG.lr, \n                 weight_decay= CFG.weight_decay, \n                 amsgrad = False)\n\n# scheduler setting：学习率调度器 自动调整 optimizer 的学习率，帮助模型稳定收敛\nif CFG.scheduler == 'CosineAnnealingWarmRestarts':\n    scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=CFG.T_0, T_mult=1, eta_min=CFG.min_lr, last_epoch=-1)\nelif CFG.scheduler == 'ReduceLROnPlateau':\n    scheduler = ReduceLROnPlateauReduceLROnPlateau(optimizer, mode='min', factor=CFG.factor, patience=CFG.patience, verbose=True, eps=CFG.eps)\nelif CFG.scheduler == 'CosineAnnealingLR':\n    scheduler = CosineAnnealingLR(optimizer, T_max=CFG.T_max, eta_min=CFG.min_lr, last_epoch=-1)\n\n\n'''\nScheduler = 自动调节学习率的工具\n单一固定 LR → 无法适应训练过程的不同阶段，Scheduler 会在训练过程中“自动改变”学习率，让模型更快更稳定地收敛。\n\n\n1. CosineAnnealingWarmRestarts\n一种 余弦退火 + 周期性重启 的学习率调整方法。像海浪一样\n学习率曲线像这样:高 → 降低 → 重启为高 → 再降低 → 再重启\n为什么要用？\n\n模型可能在某一局部最优停住\n重启能跳出局部最优，提升性能\n常用于 segmentation/model fine-tuning\n\n2.ReduceLROnPlateau（当 val loss 不下降时降低学习率）:\n\n如果验证损失连续几次不下降（停滞不前），就自动降低学习率。\n因为高学习率会使模型跳来跳去学不好；降低学习率模型才能“慢慢精细学习”。\n\n3.CosineAnnealingLR（无重启）:\n\n没有重启,只是简单的余弦下降,只下降，不回升\n训练任务简单、损失下降稳定。\n'''","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:00.516809Z","iopub.execute_input":"2025-11-30T10:38:00.517034Z","iopub.status.idle":"2025-11-30T10:38:02.254999Z","shell.execute_reply.started":"2025-11-30T10:38:00.517017Z","shell.execute_reply":"2025-11-30T10:38:02.254168Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"#整个过程只训练一个 fold（根据你的 CFG 设置），不是 n-fold CV，而是 “挑一个 fold 进行训练”。\n\n# 选择一个fold\n# 这告诉你：你要训练第 0 个 fold（验证集是 fold 0，训练集是其余 fold）\nprint(f'Training Loop [{CFG.trn_fold}]...')\n\n#遍历所有 fold\nfor fold, (tr_idx, val_idx) in enumerate(gkf.split(dir_df, groups=dir_df[dir_df.columns[0]].values)):\n    if fold != CFG.trn_fold: # 只选择一个作为validation，如果不是当前指定的 fold，就跳过\n        continue \n        \n    #创建 DataLoader（train 和 valid）\n    trainloader, validloader = prepare_train_valid_dataloader(dir_df, [fold])\n\n    #进入 epoch 循环\n    for epoch in range(CFG.epoch):\n        \n        #训练一个 epoch ：每个 batch：\n        # 前向传播， 计算 loss， 反向传播， 优化器更新参数， scheduler.step()， 调整学习率， 打印训练损失\n        train_one_epoch(epoch, model, device, optimizer, scheduler, trainloader)\n        \n        # 验证一个 epoch（无反向传播）\n        with torch.no_grad():\n            valid_one_epoch(epoch, model, device, optimizer, scheduler, validloader)\n        \n        #torch.save(model.state_dict(),f'FOLD-{fold}-EPOCH-{epoch}-model.pth')\n\n    \n    #epoch 循环结束后，保存最终模型：这就是你训练好的最终权重。    \n    torch.save(model.state_dict(),f'FOLD-{fold}-model.pth')\n    \n    #del model, optimizer, scheduler, trainloader, validloader","metadata":{"execution":{"iopub.status.busy":"2025-11-30T10:38:02.256210Z","iopub.execute_input":"2025-11-30T10:38:02.256551Z","iopub.status.idle":"2025-11-30T11:26:17.897012Z","shell.execute_reply.started":"2025-11-30T10:38:02.256522Z","shell.execute_reply":"2025-11-30T11:26:17.896108Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# try:\n#     import torchinfo\n# except:\n#     !pip install torchinfo\n#     import torchinfo\n\n# from torchinfo import summary\n# summary(model, input_size = [4,3,256,256])","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:26:17.898625Z","iopub.execute_input":"2025-11-30T11:26:17.899296Z","iopub.status.idle":"2025-11-30T11:26:17.903931Z","shell.execute_reply.started":"2025-11-30T11:26:17.899254Z","shell.execute_reply":"2025-11-30T11:26:17.903020Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#把 fold = 4 当作 validation set，其它 folds (0,1,2,3) 当作 training set\ntrainloader, validloader = prepare_train_valid_dataloader(dir_df, [4])\n\n#这个 dataloader 在一个 epoch 中有多少个 batch\n# Vali有多少，Train有多少，总共有多少\nlen(validloader), len(trainloader), dir_df.shape[0]/4","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:26:17.904813Z","iopub.execute_input":"2025-11-30T11:26:17.905014Z","iopub.status.idle":"2025-11-30T11:26:19.997993Z","shell.execute_reply.started":"2025-11-30T11:26:17.904996Z","shell.execute_reply":"2025-11-30T11:26:19.997188Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluation：\n# Metrices：Metrics 就是用来衡量：模型预测的 mask 是否与真实 mask 一致？\n\nimport torch\nimport numpy as np\n\n#1. Dice Coefficient（Dice 系数）\ndef dice_coefficient(y_true, y_pred):\n    smooth = 1\n    inputs = y_true.view(-1)\n    targets = y_pred.view(-1)\n        \n    intersection = (inputs * targets).sum()\n    dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)\n        \n    return dice\n    \n#2. IoU（Intersection over Union）\ndef iou(y_true, y_pred):\n    smooth = 1\n    intersection = torch.sum(y_true * y_pred)\n    union = torch.sum(y_true) + torch.sum(y_pred) - intersection\n    iou = (intersection + smooth) / (union + smooth)\n    return iou\n\n#3. Accuracy（精确度）\ndef accuracy(y_true, y_pred):\n    correct = torch.sum((y_pred == y_true).float())\n    total = y_true.numel()\n    acc = correct / total\n    return acc\n\n#4. Precision（查准率）\ndef precision(y_true, y_pred):\n    smooth = 1\n    true_positive = torch.sum(y_true * y_pred)\n    predicted_positive = torch.sum(y_pred)\n    precision = (true_positive + smooth) / (predicted_positive + smooth)\n    return precision\n\n#5.Recall（召回率）\ndef recall(y_true, y_pred):\n    smooth = 1\n    true_positive = torch.sum(y_true * y_pred)\n    actual_positive = torch.sum(y_true)\n    recall = (true_positive + smooth) / (actual_positive + smooth)\n    return recall","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:26:19.998952Z","iopub.execute_input":"2025-11-30T11:26:19.999211Z","iopub.status.idle":"2025-11-30T11:26:20.007093Z","shell.execute_reply.started":"2025-11-30T11:26:19.999190Z","shell.execute_reply":"2025-11-30T11:26:20.006302Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#calculate_metrices() 会在验证集上跑整个模型，\n#把 Dice、IoU、Accuracy、Precision、Recall 都算出来，求平均，然后保存成 CSV 文件\n# ⚠️ 注意：pred_bin = (pred_mask.squeeze() > 0.5).float()！！！\nimport torch\nimport numpy as np\n\ndef calculate_metrices(validloader, model, device):\n    total_dice = 0.0\n    total_iou = 0.0\n    total_acc = 0.0\n    total_prec = 0.0\n    total_rec = 0.0\n    num_batches = 0\n\n    model.eval()\n    with torch.no_grad():\n        for img, mask in validloader:\n            img = img.to(device)\n            mask = mask.to(device)\n            pred_mask = model(img).cpu()\n        \n            pred_new_mask = (pred_mask.squeeze() > 0.5).float()\n            new_tensor = torch.ones_like(pred_new_mask) - pred_new_mask\n\n            batch_dice = 0.0\n            batch_iou_score = 0.0\n            batch_acc = 0.0\n            batch_prec = 0.0\n            batch_rec = 0.0\n            \n            batch_size = img.size(0)\n            \n            for i in range(batch_size):\n                           \n                batch_dice += dice_coefficient(mask[i].cpu(),  new_tensor[i].squeeze())\n                batch_iou_score += iou(mask[i].cpu(), new_tensor[i].squeeze())\n                batch_acc += accuracy(mask[i].cpu(), new_tensor[i].squeeze())\n                batch_prec += precision(mask[i].cpu(),new_tensor[i].squeeze())\n                batch_rec += recall(mask[i].cpu(), new_tensor[i].squeeze())\n\n            \n            batch_dice = batch_dice/batch_size\n            batch_iou_score = batch_iou_score/batch_size\n            batch_acc = batch_acc/batch_size\n            batch_prec = batch_prec/batch_size\n            batch_rec = batch_rec/batch_size\n            \n\n            total_dice += batch_dice\n            total_iou += batch_iou_score\n            total_acc += batch_acc\n            total_prec += batch_prec\n            total_rec += batch_rec\n            \n            \n            num_batches += 1\n    \n        mean_dice = total_dice / num_batches\n        mean_iou = total_iou / num_batches\n        mean_acc = total_acc / num_batches\n        mean_prec = total_prec / num_batches\n        mean_rec = total_rec / num_batches\n        \n        \n        data = {\n            'Mean Dice' : mean_dice,\n            'IoU' : mean_iou,\n            'Accuracy' : mean_acc,\n            'Precision' : mean_prec,\n            'Recall' : mean_rec,\n        }\n        \n        df = pd.DataFrame.from_dict(data, orient='index').T\n        \n        csv_file_path = '/kaggle/working/metrices.csv'\n        df.to_csv(csv_file_path, index = True)\n        \n# Assuming 'model' and 'device' are defined elsewhere\nwith torch.no_grad():\n    calculate_metrices(validloader, model, device)\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T11:26:20.008470Z","iopub.execute_input":"2025-11-30T11:26:20.008828Z","iopub.status.idle":"2025-11-30T11:27:01.313444Z","shell.execute_reply.started":"2025-11-30T11:26:20.008791Z","shell.execute_reply":"2025-11-30T11:27:01.312319Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 从验证集取一个 batch，\n# 用训练好的 best model 做预测，\n# 把原图、预测 mask、真实 mask 放在同一张图里可视化\n\ndef plot_result(validloader, n_sample=4):\n    img, mask = next(iter(validloader))\n    model.load_state_dict(torch.load(f'./FOLD-{CFG.trn_fold}-model.pth'))\n    model.eval()\n    img = img.to(device)\n    pred_mask = model(img).squeeze(1).cpu().detach()\n    \n    img = img.cpu()\n    mask = mask.cpu()\n    \n    plt.figure(figsize=(15, 10))\n    for i in range(n_sample):\n        # Display the original image\n        plt.subplot(n_sample, 3, 3*i+1)\n        plt.imshow(np.transpose(img[i].numpy(), (1, 2, 0)))\n        plt.axis('off')\n        plt.title(\"Image\")\n\n        # Display the predicted mask\n        plt.subplot(n_sample, 3, 3*i+2)\n        plt.imshow(pred_mask[i], cmap='gray')\n        plt.axis('off')\n        plt.title(\"Predicted Mask\")\n        \n        # Display the original mask\n        plt.subplot(n_sample, 3, 3*i+3)\n        plt.imshow(mask[i].numpy(), cmap='gray')\n        plt.axis('off')\n        plt.title(\"Original Mask\")\n    \n    plt.tight_layout()\n    plt.savefig('/kaggle/working/single_batch.png')","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:01.315141Z","iopub.execute_input":"2025-11-30T11:27:01.315985Z","iopub.status.idle":"2025-11-30T11:27:01.323598Z","shell.execute_reply.started":"2025-11-30T11:27:01.315943Z","shell.execute_reply":"2025-11-30T11:27:01.322807Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with torch.no_grad():\n    plot_result(validloader)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:01.324766Z","iopub.execute_input":"2025-11-30T11:27:01.325325Z","iopub.status.idle":"2025-11-30T11:27:03.602549Z","shell.execute_reply.started":"2025-11-30T11:27:01.325304Z","shell.execute_reply":"2025-11-30T11:27:03.601651Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## What you should do to improve the results:\n- Use data normalization\n- Use anthor implementation for TransUnet (Applied in V16)\n- Reduce the probability of the augmentations\n- Play with visionTrans hyperparameters","metadata":{"execution":{"iopub.status.busy":"2024-06-22T03:34:10.954065Z","iopub.execute_input":"2024-06-22T03:34:10.954401Z","iopub.status.idle":"2024-06-22T03:34:10.961518Z","shell.execute_reply.started":"2024-06-22T03:34:10.95437Z","shell.execute_reply":"2024-06-22T03:34:10.960373Z"}}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def dice_coefficient_2(y_true, y_pred):\n#     smooth = 1e-6\n#     y_true_f = y_true.view(-1)\n#     y_pred_f = y_pred.view(-1)\n    \n#     print(torch.sum(y_true_f))\n#     print(torch.sum(y_pred_f))\n    \n#     if torch.sum(y_true_f) == 0 and torch.sum(y_pred_f) == 0:\n#         return 1.0\n    \n#     intersection = torch.sum(y_true_f * y_pred_f)\n#     union = torch.sum(y_true_f) + torch.sum(y_pred_f)\n#     dice = (2. * intersection + smooth) / (union + smooth)\n#     return dice\n","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:03.603827Z","iopub.execute_input":"2025-11-30T11:27:03.604162Z","iopub.status.idle":"2025-11-30T11:27:03.608532Z","shell.execute_reply.started":"2025-11-30T11:27:03.604130Z","shell.execute_reply":"2025-11-30T11:27:03.607729Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dice_metric = []\niou_metric = []\nacc_metric = []\nprec_metric = []\nrecall_metric = []\n\nimport matplotlib.pyplot as plt\n\ndef plot_result(validloader, model, device, batch_idx, n_sample=4):\n    model.eval()\n    with torch.no_grad():\n        for idx, (img, mask) in enumerate(validloader):\n            if idx == batch_idx:\n                break\n                \n        img = img.to(device)\n        mask = mask.to(device)\n        pred_mask = model(img).cpu()\n        \n        pred_new_mask = (pred_mask.squeeze() > 0.5).float()\n        new_tensor = torch.ones_like(pred_new_mask) - pred_new_mask\n\n        N = n_sample // 2\n        plt.figure(figsize=(15, 8))\n        for i in range(n_sample):\n            plt.subplot(N, 4, 2*i+1)\n            plt.imshow(mask[i].cpu().squeeze(), cmap = 'gray')\n            plt.axis('off')\n            plt.title('Mask')\n\n            plt.subplot(N, 4, 2*i+2)\n            plt.imshow(new_tensor[i])\n            plt.axis('off')\n            plt.title('Predicted Mask')\n\n            plt.tight_layout()\n            plt.savefig('/kaggle/working/another_batch.png')\n#             print(mask[i].shape)  256 * 256\n#             print(new_tensor[i].squeeze().shape)  #1 * 256 * 256\n            \n            # Compute evaluation metrics\n            dice = dice_coefficient(mask[i].cpu(),  new_tensor[i].squeeze())\n            iou_score = iou(mask[i].cpu(), new_tensor[i].squeeze())\n            acc = accuracy(mask[i].cpu(), new_tensor[i].squeeze())\n            prec = precision(mask[i].cpu(),new_tensor[i].squeeze())\n            rec = recall(mask[i].cpu(), new_tensor[i].squeeze())\n            \n\n            dice_metric.append(dice)\n            iou_metric.append(iou_score)\n            acc_metric.append(acc)\n            prec_metric.append(prec)\n            recall_metric.append(rec)\n            \n\n\n# Assuming 'model' and 'device' are defined elsewhere\nwith torch.no_grad():\n    plot_result(validloader, model, device, 10)\n","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:03.609713Z","iopub.execute_input":"2025-11-30T11:27:03.610026Z","iopub.status.idle":"2025-11-30T11:27:05.986288Z","shell.execute_reply.started":"2025-11-30T11:27:03.609999Z","shell.execute_reply":"2025-11-30T11:27:05.985292Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dice_values = [tensor.item() for tensor in dice_metric]\niou_values = [tensor.item() for tensor in iou_metric]\nacc_values = [tensor.item() for tensor in acc_metric]\nprec_values = [tensor.item() for tensor in prec_metric]\nrecall_values = [tensor.item() for tensor in recall_metric]","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:05.987894Z","iopub.execute_input":"2025-11-30T11:27:05.988681Z","iopub.status.idle":"2025-11-30T11:27:05.993956Z","shell.execute_reply.started":"2025-11-30T11:27:05.988643Z","shell.execute_reply":"2025-11-30T11:27:05.993161Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dice_values, iou_values, acc_values, prec_values, recall_values","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:05.994822Z","iopub.execute_input":"2025-11-30T11:27:05.995028Z","iopub.status.idle":"2025-11-30T11:27:06.011145Z","shell.execute_reply.started":"2025-11-30T11:27:05.995012Z","shell.execute_reply":"2025-11-30T11:27:06.010443Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = {\n            'Batch Dice' : dice_values,\n            'Batch IoU' : iou_values,\n            'Batch Accuracy' : acc_values,\n            'Batch Precision' : prec_values,\n            'Batch Recall' : recall_values,\n        }\n\ndf = pd.DataFrame(data)\ncsv_file_path = '/kaggle/working/batch_metrices.csv'\ndf.to_csv(csv_file_path, index=False)","metadata":{"execution":{"iopub.status.busy":"2025-11-30T11:27:06.012236Z","iopub.execute_input":"2025-11-30T11:27:06.012890Z","iopub.status.idle":"2025-11-30T11:27:06.020529Z","shell.execute_reply.started":"2025-11-30T11:27:06.012861Z","shell.execute_reply":"2025-11-30T11:27:06.019882Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}