{"cells": [{"cell_type": "markdown", "id": "4f7448e4", "metadata": {}, "source": "# exp_20260202_106_nnunet3d_train\n105 \u306e\u30c7\u30d0\u30c3\u30b0\u30ce\u30fc\u30c8\u3092\u30d9\u30fc\u30b9\u306b\u3001SkeletonRecall \u30c8\u30ec\u30fc\u30ca\u3067\u5b66\u7fd2\u3092\u5b9f\u884c\u3059\u308b\u30ce\u30fc\u30c8\u3002  \n- \u4f9d\u5b58\u306f\u30aa\u30d5\u30e9\u30a4\u30f3 wheels DS\uff08vesuvius-nnunet3d-offline-wheels-v1\uff09\u524d\u63d0  \n- \u524d\u51e6\u7406\u6e08\u307f DS: vesuvius-surface-nnunet-preprocessed\uff08b2nd\u5f62\u5f0f\uff09  \n- \u30c8\u30ec\u30fc\u30ca: nnUNetTrainerSkeletonRecall\uff08fork\u7248\uff09  \n- fold=0 / 3d_fullres / ResEncM / GPU\u81ea\u52d5\uff08\u6700\u59272\u679a\uff09  "}, {"cell_type": "markdown", "id": "64bba558", "metadata": {}, "source": "## \u30d1\u30b9\u30fb\u74b0\u5883\u8a2d\u5b9a"}, {"cell_type": "code", "execution_count": null, "id": "43ce264a", "metadata": {}, "outputs": [], "source": "import os, sys, shutil, json\nfrom pathlib import Path\n\nPRE_BASE = Path(\"/kaggle/working/nnUNet_preprocessed\")  # writeable view\nRES_BASE = Path(\"/kaggle/working/nnUNet_results\")\nCUSTOM_NNUNET = Path(\"/kaggle/input/vesuvius-surface-skeleton-recall\")\n# fingerprint/dataset.json \u306f\u30d7\u30ea\u30d7\u30ed DS \u3068\u540c\u3058\u5834\u6240\u306b\u3042\u308b\u524d\u63d0\nSRC_PREPROCESSED = Path(\"/kaggle/input/vesuvius-surface-nnunet-preprocessed\")\nDS_ID = 100\nDS_NAME = f\"Dataset{DS_ID:03d}_VesuviusSurface\"\n\nos.environ[\"nnUNet_raw\"] = str(PRE_BASE)          # \u751f\u30c7\u30fc\u30bf\u3082 write view \u3092\u6307\u3059\nos.environ[\"nnUNet_preprocessed\"] = str(PRE_BASE) # \u524d\u51e6\u7406\u30c7\u30fc\u30bf\u306e\u30eb\u30fc\u30c8\nos.environ[\"nnUNet_results\"] = str(RES_BASE)\nos.environ[\"nnUNet_compile\"] = \"false\"\nos.environ[\"nnUNet_USE_BLOSC2\"] = \"1\"\n\n# wheels install\uff08\u30c8\u30ec\u30fc\u30ca import \u306b\u5fc5\u8981\u306a\u4e00\u5f0f\uff09\nWHEEL_DIR = \"/kaggle/input/vesuvius-nnunet3d-offline-wheels-v1/deps/whl\"\nos.system(\n    \" \".join([\n        \"pip install --no-index -f\", WHEEL_DIR,\n        \"acvl_utils\", \"batchgenerators\", \"batchgeneratorsv2\",\n        \"dynamic-network-architectures\", \"nnunetv2\",\n        \" >/dev/null\"\n    ])\n)\n\n# \u30ab\u30b9\u30bf\u30e0 nnUNet \u3092 PYTHONPATH \u5148\u982d\u306b\ndef _find_custom_root(base: Path):\n    if (base / \"nnunetv2\").exists():\n        return base\n    for sub in base.glob(\"*\"):\n        if sub.is_dir() and (sub / \"nnunetv2\").exists():\n            return sub\n    return None\n\ncustom_root = _find_custom_root(CUSTOM_NNUNET)\nif custom_root:\n    sys.path.insert(0, str(custom_root))\n    os.environ[\"PYTHONPATH\"] = str(custom_root) + \":\" + os.environ.get(\"PYTHONPATH\", \"\")\nprint(\"custom_root:\", custom_root)\nprint(\"PYTHONPATH:\", os.environ.get(\"PYTHONPATH\", \"\")[:200])\n\n# writeable view \u306b dataset.json \u3092\u914d\u7f6e\ndst_ds = PRE_BASE / DS_NAME\ndst_ds.mkdir(parents=True, exist_ok=True)\nfor c in [\n    SRC_PREPROCESSED / \"dataset.json\",\n    SRC_PREPROCESSED / DS_NAME / \"dataset.json\",\n]:\n    if c.exists():\n        shutil.copy2(c, dst_ds / \"dataset.json\")\n        print(\"copied dataset.json from\", c)\n        break\nelse:\n    print(\"WARNING: dataset.json not found in input\")\n# dataset_fingerprint \u3082\u540c\u3058\u5834\u6240\u306b\u3042\u308b\u60f3\u5b9a\u3067\u30b3\u30d4\u30fc\nfp_candidates = [\n    SRC_PREPROCESSED / \"dataset_fingerprint.json\",\n    SRC_PREPROCESSED / DS_NAME / \"dataset_fingerprint.json\",\n]\nfor fp in fp_candidates:\n    if fp.exists():\n        shutil.copy2(fp, dst_ds / \"dataset_fingerprint.json\")\n        print(\"copied dataset_fingerprint.json from\", fp)\n        break\nelse:\n    print(\"WARNING: dataset_fingerprint.json not found in\", fp_candidates)\n\n# plans json \u3092\u30b3\u30d4\u30fc\uff08ResEncM\uff09\nfor c in [\n    SRC_PREPROCESSED / \"nnUNetResEncUNetMPlans.json\",\n    SRC_PREPROCESSED / DS_NAME / \"nnUNetResEncUNetMPlans.json\",\n]:\n    if c.exists():\n        shutil.copy2(c, dst_ds / \"nnUNetResEncUNetMPlans.json\")\n        print(\"copied plans json from\", c)\n        break\nelse:\n    print(\"WARNING: nnUNetResEncUNetMPlans.json not found in input\")\n\n# plan \u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3092\u30b7\u30f3\u30dc\u30ea\u30c3\u30af\u30ea\u30f3\u30af\nplan = dst_ds / \"nnUNetPlans_3d_fullres\"\nif not plan.exists():\n    for cand in [\n        SRC_PREPROCESSED / DS_NAME / \"nnUNetPlans_3d_fullres\",\n        SRC_PREPROCESSED / \"nnUNetPlans_3d_fullres\",\n    ]:\n        if cand.exists():\n            plan.symlink_to(cand, target_is_directory=True)\n            print(\"symlinked plan ->\", cand)\n            break\nprint(\"plan exists:\", plan.exists())\nprint(\"b2nd count:\", len(list(plan.glob('*.b2nd'))))"}, {"cell_type": "markdown", "id": "dd27f4df", "metadata": {}, "source": "## b2nd \u5bfe\u5fdc\u30d1\u30c3\u30c1\uff08get_case_identifiers, load_case\uff09"}, {"cell_type": "code", "execution_count": null, "id": "bfe0db9b", "metadata": {}, "outputs": [], "source": "import importlib, numpy as np, blosc2, pickle, os as _os\n\ntry:\n    u = importlib.import_module(\"nnunetv2.training.dataloading.utils\")\n    def get_case_identifiers(folder: str):\n        ids = []\n        for i in _os.listdir(folder):\n            if i.endswith(\"npz\") and \"segFromPrevStage\" not in i:\n                ids.append(i[:-4])\n            elif i.endswith(\"b2nd\") and (not i.endswith(\"_seg.b2nd\")):\n                ids.append(i[:-5])\n        return ids\n    u.get_case_identifiers = get_case_identifiers\n    print(\"patched get_case_identifiers\")\nexcept Exception as e:\n    print(\"patch get_case_identifiers failed:\", e)\n\ntry:\n    nd = importlib.import_module(\"nnunetv2.training.dataloading.nnunet_dataset\")\n    ds_cls = getattr(nd, \"NNUNetDataset\", None) or getattr(nd, \"nnUNetDataset\", None)\n    def load_case_bn2d(self, key):\n        entry = self.dataset[key]\n        df = entry[\"data_file\"]\n        try:\n            data = np.load(df)[\"data\"]\n        except FileNotFoundError:\n            alt = df.replace(\".npz\", \".b2nd\")\n            entry[\"data_file\"] = alt\n            data = blosc2.open(alt)[:]\n        seg = None\n        seg_file = entry.get(\"seg_file\")\n        if seg_file:\n            try:\n                seg = np.load(seg_file)[\"seg\"]\n            except FileNotFoundError:\n                alt_seg = seg_file.replace(\".npz\", \"_seg.b2nd\")\n                if _os.path.isfile(alt_seg):\n                    entry[\"seg_file\"] = alt_seg\n                    seg = blosc2.open(alt_seg)[:]\n        if seg is None:\n            alt_seg = entry[\"data_file\"].replace(\".b2nd\", \"_seg.b2nd\")\n            if entry[\"data_file\"].endswith(\".b2nd\") and _os.path.isfile(alt_seg):\n                seg = blosc2.open(alt_seg)[:]\n            else:\n                seg = np.load(entry[\"data_file\"])[\"seg\"]\n        props = entry.get(\"properties\")\n        if props is None:\n            pkl = entry.get(\"properties_file\")\n            if pkl and _os.path.isfile(pkl):\n                if pkl.endswith(\".pkl\"):\n                    with open(pkl, \"rb\") as f:\n                        props = pickle.load(f)\n                elif pkl.endswith(\".npy\"):\n                    props = np.load(pkl, allow_pickle=True).item()\n                entry[\"properties\"] = props\n        if props is None:\n            props = {}\n        # class_locations \u30ad\u30fc\u4e0d\u8db3\u3092\u57cb\u3081\u308b\n        cl = props.get(\"class_locations\", {})\n        if isinstance(cl, dict):\n            for key in [(0, 1), (1,), 1]:\n                if key not in cl:\n                    cl[key] = []\n            props[\"class_locations\"] = cl\n        return data, seg, props\n    if ds_cls:\n        ds_cls.load_case = load_case_bn2d\n        print(\"patched nnUNetDataset.load_case\")\nexcept Exception as e:\n    print(\"patch load_case failed:\", e)"}, {"cell_type": "markdown", "id": "9b64d7f9", "metadata": {}, "source": "## \u5b50\u30d7\u30ed\u30bb\u30b9\u3067\u3082\u30d1\u30c3\u30c1\u304c\u52b9\u304f\u3088\u3046 sitecustomize.py \u3092\u66f8\u304d\u8fbc\u3080"}, {"cell_type": "code", "execution_count": null, "id": "f3631bea", "metadata": {}, "outputs": [], "source": "site_py = Path(\"/kaggle/working/sitecustomize.py\")\nsite_py.write_text(r\"\"\"\nimport sys, os, numpy as np, blosc2, pickle\nprint(\"[sitecustomize] loaded\")\n# --- b2nd / npz case discovery ---\ndef get_case_identifiers(folder: str):\n    ids = []\n    for i in os.listdir(folder):\n        if i.endswith(\"npz\") and \"segFromPrevStage\" not in i:\n            ids.append(i[:-4])\n        elif i.endswith(\"b2nd\") and (not i.endswith(\"_seg.b2nd\")):\n            ids.append(i[:-5])\n    return ids\ntry:\n    import nnunetv2.training.dataloading.utils as u\n    u.get_case_identifiers = get_case_identifiers\nexcept Exception as e:\n    print(\"get_case_identifiers patch failed\", e)\n\n# --- load_case fallback for .b2nd / _seg.b2nd and missing class_locations ---\ntry:\n    import nnunetv2.training.dataloading.nnunet_dataset as nd\n    ds_cls = getattr(nd, \"NNUNetDataset\", None) or getattr(nd, \"nnUNetDataset\", None)\n    def load_case_bn2d(self, key):\n        entry = self.dataset[key]\n        df = entry[\"data_file\"]\n        try:\n            data = np.load(df)[\"data\"]\n        except FileNotFoundError:\n            alt = df.replace(\".npz\", \".b2nd\")\n            entry[\"data_file\"] = alt\n            data = blosc2.open(alt)[:]\n        seg = None\n        seg_file = entry.get(\"seg_file\")\n        if seg_file:\n            try:\n                seg = np.load(seg_file)[\"seg\"]\n            except FileNotFoundError:\n                alt_seg = seg_file.replace(\".npz\", \"_seg.b2nd\")\n                if os.path.isfile(alt_seg):\n                    entry[\"seg_file\"] = alt_seg\n                    seg = blosc2.open(alt_seg)[:]\n        if seg is None:\n            alt_seg = entry[\"data_file\"].replace(\".b2nd\", \"_seg.b2nd\")\n            if entry[\"data_file\"].endswith(\".b2nd\") and os.path.isfile(alt_seg):\n                seg = blosc2.open(alt_seg)[:]\n            else:\n                seg = np.load(entry[\"data_file\"])[\"seg\"]\n        props = entry.get(\"properties\")\n        if props is None:\n            pkl = entry.get(\"properties_file\")\n            if pkl and os.path.isfile(pkl):\n                if pkl.endswith(\".pkl\"):\n                    with open(pkl, \"rb\") as f:\n                        props = pickle.load(f)\n                elif pkl.endswith(\".npy\"):\n                    props = np.load(pkl, allow_pickle=True).item()\n                entry[\"properties\"] = props\n        if props is None:\n            props = {}\n        cl = props.get(\"class_locations\", {})\n        if isinstance(cl, dict):\n            for key in [(0, 1), (1,), 1]:\n                if key not in cl:\n                    cl[key] = []\n            props[\"class_locations\"] = cl\n            entry[\"class_locations\"] = cl  # ensure entry also has class_locations\n        return data, seg, props\n    if ds_cls:\n        ds_cls.load_case = load_case_bn2d\nexcept Exception as e:\n    print(\"load_case patch failed\", e)\n\n# --- custom MedialSurfaceRecall trainer (loss = CE + axial skeleton recall) ---\ntry:\n    import torch, torch.nn as nn, torch.nn.functional as F\n    try:\n        from skimage.morphology import skeletonize\n    except Exception as e:\n        print(\"skimage import failed\", e)\n        def skeletonize(x):\n            return x  # fallback: no skeleton\n    from nnunetv2.training.nnUNetTrainer.nnUNetTrainer import nnUNetTrainer\n\n    class MedialSurfaceRecallLoss(nn.Module):\n        def __init__(self, ce_weight=1.0, recall_weight=1.0, thresh=0.5):\n            super().__init__()\n            self.ce = nn.CrossEntropyLoss()\n            self.ce_weight = ce_weight\n            self.recall_weight = recall_weight\n            self.thresh = thresh\n\n        def _axis_recall(self, probs, target, axis):\n            # probs: (B, C, Z, Y, X), target: (B, Z, Y, X)\n            p = probs[:, 1].detach().cpu().numpy()\n            t = target.detach().cpu().numpy()\n            p = np.moveaxis(p, axis, 1)  # axis -> dim1\n            t = np.moveaxis(t, axis, 1)\n            tp = fn = 0.0\n            for b in range(p.shape[0]):\n                for s in range(p.shape[1]):\n                    gt = t[b, s]\n                    if gt.max() == 0:\n                        continue\n                    skel = skeletonize(gt > 0)\n                    pred_mask = p[b, s] > self.thresh\n                    tp += float((pred_mask & skel).sum())\n                    fn += float(((~pred_mask) & skel).sum())\n            denom = tp + fn + 1e-6\n            return 0.0 if denom == 0 else 1.0 - tp / denom\n\n        def forward(self, net_out, target):\n            loss_ce = self.ce(net_out, target.long())\n            probs = torch.softmax(net_out, 1)\n            recalls = [\n                self._axis_recall(probs, target, axis)\n                for axis in (1, 2, 3)  # z, y, x dims of target\n            ]\n            loss_recall = torch.tensor(recalls, device=net_out.device).mean()\n            return self.ce_weight * loss_ce + self.recall_weight * loss_recall\n\n    class nnUNetTrainerMedialSurfaceRecall(nnUNetTrainer):\n        def get_loss(self):\n            return MedialSurfaceRecallLoss()\n\n    import nnunetv2.training.nnUNetTrainer as nnt_mod\n    nnt_mod.nnUNetTrainerMedialSurfaceRecall = nnUNetTrainerMedialSurfaceRecall\n    # run_training \u306e get_trainer_from_args \u3078\u30d5\u30c3\u30af\uff08\u540d\u524d\u3067\u76f4\u63a5\u8fd4\u3059\uff09\n    try:\n        import nnunetv2.run.run_training as rt\n        _orig_get = rt.get_trainer_from_args\n        def get_trainer_from_args(*args, **kwargs):\n            trainer_name = kwargs.get(\"trainer_name\", None)\n            if len(args) >= 4:\n                trainer_name = args[3]\n            obj = _orig_get(*args, **kwargs)\n            if trainer_name == \"nnUNetTrainerMedialSurfaceRecall\":\n                # \u671f\u5f85\u306f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3002loss \u3092\u5dee\u3057\u66ff\u3048\u308b\u3002\n                try:\n                    obj.loss = MedialSurfaceRecallLoss()\n                except Exception as e:\n                    print(\"loss patch failed\", e)\n                return obj\n            return obj\n        rt.get_trainer_from_args = get_trainer_from_args\n    except Exception as e:\n        print(\"get_trainer_from_args hook failed\", e)\n    print(\"[sitecustomize] registered nnUNetTrainerMedialSurfaceRecall\")\nexcept Exception as e:\n    print(\"custom trainer patch failed\", e)\n\"\"\")\nsys.path.insert(0, \"/kaggle/working\")\nprint(\"sitecustomize.py written; PYTHONPATH prefixed with /kaggle/working\")\n# \u30ab\u30fc\u30cd\u30eb\u8d77\u52d5\u5f8c\u306b\u66f8\u3044\u305f\u305f\u3081\u3001\u5373\u53cd\u6620\nimport importlib, sitecustomize as _sc\nimportlib.reload(_sc)"}, {"cell_type": "markdown", "id": "9bf814c2", "metadata": {}, "source": "## MedialSurfaceRecall \u30c8\u30ec\u30fc\u30ca\uff08Villa \u5b9f\u88c5\u3092\u7c21\u7565\u79fb\u690d\uff09"}, {"cell_type": "code", "execution_count": null, "id": "2a6bc137", "metadata": {}, "outputs": [], "source": "# sitecustomize \u3067 nnUNetTrainerMedialSurfaceRecall \u3092\u767b\u9332\u6e08\u307f\u3002\n# \u3053\u3053\u3067\u306f\u5b58\u5728\u30c1\u30a7\u30c3\u30af\u3060\u3051\u884c\u3046\u3002\nimport nnunetv2.training.nnUNetTrainer as nnt_mod\nimport importlib\nimportlib.reload(nnt_mod)\nprint(\"custom trainer available:\", hasattr(nnt_mod, \"nnUNetTrainerMedialSurfaceRecall\"))"}, {"cell_type": "markdown", "id": "ce33ab70", "metadata": {}, "source": "## splits_final.json \u306e\u751f\u6210\uff08\u7121\u3051\u308c\u3070\u4f5c\u308b\uff09"}, {"cell_type": "code", "execution_count": null, "id": "df519e93", "metadata": {}, "outputs": [], "source": "splits_path = dst_ds / \"splits_final.json\"\ndef make_splits():\n    import pandas as pd\n    cvs = Path(\"/kaggle/input/vesuvius-challenge-surface-detection-src/src/assets/cv_splits/exp_20260118_003_cv-split/cv_split.csv\")\n    ids = []\n    folds = []\n    if cvs.exists():\n        df = pd.read_csv(cvs)\n        id_col = \"id\" if \"id\" in df.columns else df.columns[0]\n        ids = df[id_col].astype(str).tolist()\n        folds = df[\"fold\"].astype(int).tolist()\n        print(\"cv_split.csv used:\", cvs, \"rows\", len(ids))\n    else:\n        # \u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af: plan \u306e\u30d5\u30a1\u30a4\u30eb\u540d\u304b\u3089 id \u3092\u62bd\u51fa\u3057\u3066 5-fold round-robin\n        ids = [p.stem.replace(\"_seg\",\"\") for p in plan.glob(\"*.b2nd\")]\n        folds = [i % 5 for i in range(len(ids))]\n        print(\"cv_split.csv absent; fallback ids from plan (*.b2nd) rows\", len(ids))\n    splits = []\n    for f in range(5):\n        tr = [i for i,fold in zip(ids, folds) if fold != f]\n        va = [i for i,fold in zip(ids, folds) if fold == f]\n        splits.append({\"train\": tr, \"val\": va})\n    splits_path.write_text(json.dumps(splits, indent=2))\n    print(\"splits_final.json written:\", splits_path)\n    for f,s in enumerate(splits):\n        print(f\" fold{f}: train={len(s['train'])} val={len(s['val'])}\")\n\n# \u5e38\u306b\u518d\u751f\u6210\u3057\u3066\u78ba\u5b9f\u306b\u4f7f\u3046\nmake_splits()"}, {"cell_type": "markdown", "id": "105000b1", "metadata": {}, "source": "## \u52d5\u4f5c\u30c1\u30a7\u30c3\u30af\uff08\u30c8\u30ec\u30fc\u30ca import \u3068 load_case\uff09"}, {"cell_type": "code", "execution_count": null, "id": "1cf03341", "metadata": {}, "outputs": [], "source": "import importlib, time\nt0 = time.time()\nimportlib.import_module(\"nnunetv2.training.nnUNetTrainer.nnUNetTrainer\")\nimport nnunetv2.training.nnUNetTrainer as nnt_mod\nprint(\"trainer import ok, sec:\", time.time() - t0, \"custom exists:\", hasattr(nnt_mod, \"nnUNetTrainerMedialSurfaceRecall\"))\n\nfrom nnunetv2.training.dataloading.nnunet_dataset import nnUNetDataset\nds = nnUNetDataset(str(plan))\nkeys = list(ds.keys())\nprint(\"num cases:\", len(keys))\nkc = keys[0]\ndata, seg, props = ds.load_case(kc)\nprint(\"sample:\", kc, data.shape, seg.shape, props.get(\"spacing\", \"n/a\"))"}, {"cell_type": "markdown", "id": "d6247072", "metadata": {}, "source": "## \u5b66\u7fd2\u30b3\u30de\u30f3\u30c9\uff08fold=0, 3d_fullres, ResEncM, SkeletonRecall\uff09"}, {"cell_type": "code", "execution_count": null, "id": "d7aa50fe", "metadata": {}, "outputs": [], "source": "import torch, subprocess, shlex, copy\nnum_gpus = torch.cuda.device_count()\nif num_gpus > 2:\n    num_gpus = 2\nprint(\"GPUs:\", num_gpus)\n\n# trainer \u304c\u898b\u3048\u308b\u304b\u4e8b\u524d\u30c1\u30a7\u30c3\u30af\nimport nnunetv2.training.nnUNetTrainer as nnt_mod\nimport importlib\nimportlib.reload(nnt_mod)\nprint(\"custom trainer available:\", hasattr(nnt_mod, \"nnUNetTrainerMedialSurfaceRecall\"))\n\n# run_training \u5074\u304c\u6a19\u6e96\u30c8\u30ec\u30fc\u30ca\u30fc\u540d\u3057\u304b\u89e3\u6c7a\u3067\u304d\u306a\u3044\u305f\u3081\u3001CLI \u306b\u306f nnUNetTrainer \u3092\u6e21\u3057\u3001\n# \u751f\u6210\u5f8c\u306b loss \u3092 MedialSurfaceRecallLoss \u3078\u5dee\u3057\u66ff\u3048\u308b\nimport nnunetv2.run.run_training as rt\nfrom sitecustomize import MedialSurfaceRecallLoss\n_orig_get = rt.get_trainer_from_args\n\nWANDB_RUN = {\n    \"enabled\": True,\n    \"entity\": None,   # \u4f8b: \"your_entity\"\n    \"project\": \"vesuvius-nnunet3d\",\n    \"name\": \"exp_20260202_106_fold1_medial\",\n}\nFOLD = 1  # \u30d0\u30e9\u30f3\u30b9\u304c\u826f\u3044 fold1\n\ndef _get_trainer(*args, **kwargs):\n    obj = _orig_get(*args, **kwargs)\n    try:\n        obj.loss = MedialSurfaceRecallLoss()\n        if WANDB_RUN.get(\"enabled\"):\n            obj.log_wandb = True\n            obj.wandb_entity = WANDB_RUN.get(\"entity\")\n            obj.wandb_project = WANDB_RUN.get(\"project\")\n            obj.wandb_run_name = WANDB_RUN.get(\"name\")\n    except Exception as e:\n        print(\"loss/W&B patch failed\", e)\n    return obj\nrt.get_trainer_from_args = _get_trainer\n\ncmd = (\n    f\"nnUNetv2_train {DS_NAME} 3d_fullres {FOLD} \"\n    f\"-p nnUNetResEncUNetMPlans \"\n    f\"-tr nnUNetTrainer \"\n    f\"-num_gpus {num_gpus} --c\"\n)\nenv = copy.deepcopy(os.environ)\nenv[\"PYTHONPATH\"] = \"/kaggle/working:/kaggle/input/vesuvius-surface-skeleton-recall:\" + env.get(\"PYTHONPATH\", \"\")\nenv[\"PYTHONSAFEPATH\"] = \"1\"\nprint(\"Running with PYTHONPATH head:\", env[\"PYTHONPATH\"].split(\":\")[:2])\nret = subprocess.run(shlex.split(cmd), env=env)\nprint(\"retcode:\", ret.returncode)"}], "metadata": {"jupytext": {"formats": "py:percent,ipynb"}, "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}}, "nbformat": 4, "nbformat_minor": 5}