{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.12"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"},{"sourceId":14152389,"sourceType":"datasetVersion","datasetId":9020093},{"sourceId":14427919,"sourceType":"datasetVersion","datasetId":9215486},{"sourceId":14446305,"sourceType":"datasetVersion","datasetId":9227794},{"sourceId":14467121,"sourceType":"datasetVersion","datasetId":9228259},{"sourceId":14468055,"sourceType":"datasetVersion","datasetId":9241211},{"sourceId":14545827,"sourceType":"datasetVersion","datasetId":9290337},{"sourceId":14560527,"sourceType":"datasetVersion","datasetId":9300255},{"sourceId":14562790,"sourceType":"datasetVersion","datasetId":9301941},{"sourceId":14571768,"sourceType":"datasetVersion","datasetId":9308082},{"sourceId":14596765,"sourceType":"datasetVersion","datasetId":9323890},{"sourceId":14596787,"sourceType":"datasetVersion","datasetId":9323902},{"sourceId":14600771,"sourceType":"datasetVersion","datasetId":9326244},{"sourceId":14600814,"sourceType":"datasetVersion","datasetId":9326272},{"sourceId":14600985,"sourceType":"datasetVersion","datasetId":9326381},{"sourceId":14617117,"sourceType":"datasetVersion","datasetId":9336753},{"sourceId":14617298,"sourceType":"datasetVersion","datasetId":9336868},{"sourceId":14621136,"sourceType":"datasetVersion","datasetId":9339167},{"sourceId":14621191,"sourceType":"datasetVersion","datasetId":9339199},{"sourceId":14621303,"sourceType":"datasetVersion","datasetId":9339277},{"sourceId":14652736,"sourceType":"datasetVersion","datasetId":9360504},{"sourceId":14652818,"sourceType":"datasetVersion","datasetId":9360558},{"sourceId":4535,"sourceType":"modelInstanceVersion","modelInstanceId":3327,"modelId":986}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":535.964435,"end_time":"2026-01-21T10:59:24.922514","environment_variables":{},"exception":true,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-01-21T10:50:28.958079","version":"2.6.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"21b471cc75714a6a8790f23df8837a43":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_fb458a3b750f4c63a50d252531f83a7a","placeholder":"​","style":"IPY_MODEL_903ac6cdf0be4f5ab078b0a8ecf5f45d","tabbable":null,"tooltip":null,"value":" 1/1 [00:12&lt;00:00, 12.40s/it]"}},"2cde64abdea447fc9e6695f141519965":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e7442efc2de847afa8f51d9276cf9b2a","IPY_MODEL_a47e911818004af5a8e6feec4a660be0","IPY_MODEL_21b471cc75714a6a8790f23df8837a43"],"layout":"IPY_MODEL_57d8565afd5d4976b9a99e3b96b70723","tabbable":null,"tooltip":null}},"3f291754e8e6491096fdfac5de274f43":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4823ef192a5148819be44a96154ed080":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"57d8565afd5d4976b9a99e3b96b70723":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"903ac6cdf0be4f5ab078b0a8ecf5f45d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"a47e911818004af5a8e6feec4a660be0":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_fccb5f84c6ab4b128cd2424ef2997a8f","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_4823ef192a5148819be44a96154ed080","tabbable":null,"tooltip":null,"value":1}},"df6f536f14d741bf9886dad68f03ff28":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"e7442efc2de847afa8f51d9276cf9b2a":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_3f291754e8e6491096fdfac5de274f43","placeholder":"​","style":"IPY_MODEL_df6f536f14d741bf9886dad68f03ff28","tabbable":null,"tooltip":null,"value":"100%"}},"fb458a3b750f4c63a50d252531f83a7a":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"fccb5f84c6ab4b128cd2424ef2997a8f":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!uv pip -q install --no-index --find-links /kaggle/input/kagglerequiemts timm==1.0.22 torch==2.6.0\nimport timm\nprint(timm.__version__)","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:35.553182Z","iopub.execute_input":"2026-01-24T02:26:35.553836Z","iopub.status.idle":"2026-01-24T02:26:49.156378Z","shell.execute_reply.started":"2026-01-24T02:26:35.553807Z","shell.execute_reply":"2026-01-24T02:26:49.155594Z"},"papermill":{"duration":14.491562,"end_time":"2026-01-21T10:50:46.217026","exception":false,"start_time":"2026-01-21T10:50:31.725464","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -rf /kaggle/working","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:49.157814Z","iopub.execute_input":"2026-01-24T02:26:49.158114Z","iopub.status.idle":"2026-01-24T02:26:49.286108Z","shell.execute_reply.started":"2026-01-24T02:26:49.158089Z","shell.execute_reply":"2026-01-24T02:26:49.285280Z"},"papermill":{"duration":0.143296,"end_time":"2026-01-21T10:50:46.365715","exception":false,"start_time":"2026-01-21T10:50:46.222419","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:49.287407Z","iopub.execute_input":"2026-01-24T02:26:49.287667Z","iopub.status.idle":"2026-01-24T02:26:49.413193Z","shell.execute_reply.started":"2026-01-24T02:26:49.287639Z","shell.execute_reply":"2026-01-24T02:26:49.412472Z"},"papermill":{"duration":0.141231,"end_time":"2026-01-21T10:50:46.512147","exception":false,"start_time":"2026-01-21T10:50:46.370916","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create embeddings","metadata":{}},{"cell_type":"code","source":"import os\n# IMPORTANT: set CUDA_VISIBLE_DEVICES before importing torch\n# Expose both GPUs; we will explicitly pick cuda:0 below\nos.environ['CUDA_VISIBLE_DEVICES'] = '0'\nimport torch\nimport timm\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom pathlib import Path\nfrom transformers import AutoImageProcessor, AutoModel\nfrom sklearn.metrics import r2_score\nfrom tqdm import tqdm\nimport warnings\nimport random\nfrom safetensors.torch import load_file\nimport time\nimport gc\nfrom contextlib import contextmanager\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom sklearn.model_selection import KFold, StratifiedGroupKFold, GroupKFold\nwarnings.filterwarnings('ignore')\n\nRANDOM_SEED = 42\n\ndef set_seed():\n    random.seed(RANDOM_SEED)\n    np.random.seed(RANDOM_SEED)\n    torch.manual_seed(RANDOM_SEED)\n    torch.cuda.manual_seed_all(RANDOM_SEED)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n\nset_seed()\nos.makedirs('/kaggle/embs', exist_ok=True)\n\nDATA_DIR = '/kaggle/input/csiro-biomass'\nTEST_CSV = f'{DATA_DIR}/test.csv'\n\ndevice = 'cuda'\nmodel = timm.create_model(\n    \"vit_7b_patch16_dinov3.lvd1689m\",\n    pretrained=False\n)\nstate_dict = load_file('/kaggle/input/vit-7b-patch16-dinov3/model.safetensors')\nmodel.load_state_dict(state_dict, strict=True)\ndel state_dict\ngc.collect()\nmodel = model.to(torch.float16).eval().cuda()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data_config = timm.data.resolve_model_data_config(model)\ntransform = timm.data.create_transform(**data_config, is_training=False)\n\nprint(f\"Memory allocated: {torch.cuda.memory_allocated() / 1024**3:.2f} GB\")","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def _split_into_quadrants(img: Image.Image):\n    w, h = img.size\n    w2, h2 = w // 2, h // 2\n    return [\n        img.crop((0, 0, w2, h2)),\n        img.crop((w2, 0, w, h2)),\n        img.crop((0, h2, w2, h)),\n        img.crop((w2, h2, w, h)),\n    ]\n\ndef _split_into_9_parts(img: Image.Image):\n    w, h = img.size\n    w3, h3 = w // 3, h // 3\n    tiles = []\n    for row in range(3):\n        for col in range(3):\n            left = col * w3\n            upper = row * h3\n            right = (col + 1) * w3 if col < 2 else w\n            lower = (row + 1) * h3 if row < 2 else h\n            tiles.append(img.crop((left, upper, right, lower)))\n    return tiles\n\ndef get_embeddings_dino_timm(model, transform, device, images_path, n_path, batch_size=2):\n    model.eval()\n    embeddings = []\n    \n    if n_path == 5:\n        method_splits = _split_into_quadrants\n    if n_path == 10:\n        method_splits = _split_into_9_parts \n\n    with torch.no_grad():\n        for i in tqdm(range(0, len(images_path), batch_size)):\n            batch_paths = images_path[i:i + batch_size]\n            batch_tensors = []\n\n            for img_path in batch_paths:\n                img_full_path = Path(DATA_DIR) / img_path\n                with Image.open(img_full_path) as im:\n                    image = im.convert('RGB')\n                \n                tiles = [image] + method_splits(image)\n                tensors = [transform(tile) for tile in tiles]\n                batch_tensors.extend(tensors)\n\n            inputs = torch.stack(batch_tensors).to(torch.float16).to(device)\n            \n            features = model.forward_features(inputs) \n            patch_embeddings = features[:, 1:, :].detach().cpu().numpy()\n\n            num_patches = patch_embeddings.shape[1]\n            hidden_dim = patch_embeddings.shape[2]\n            \n            emb = patch_embeddings.reshape(len(batch_paths), n_path, num_patches, hidden_dim)\n            emb_combined = emb.mean(axis=1) \n            \n            embeddings.append(emb_combined)\n\n    return np.vstack(embeddings)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.read_csv(TEST_CSV)\ntest_df['target'] = -1 \ntest_df = test_df.pivot_table(\n    index=['image_path',],\n    columns='target_name',\n    values='target',\n    aggfunc='first'\n).reset_index()\n\n# 4 патча + оригинал\ngc.collect()\nembeddings_test = get_embeddings_dino_timm(model, transform, device, test_df.image_path.values, n_path=5)\nnp.save('/kaggle/working/embeddings_test.npy', embeddings_test)\n\n# 9 патчей + оригинал \ngc.collect()\nembeddings_test = get_embeddings_dino_timm(model, transform, device, test_df.image_path.values, n_path=10)\nnp.save('/kaggle/working/embeddings_test_9path.npy', embeddings_test)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"del model, embeddings_test, data_config, transform, test_df","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model 1","metadata":{"papermill":{"duration":0.004776,"end_time":"2026-01-21T10:50:46.521875","exception":false,"start_time":"2026-01-21T10:50:46.517099","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile model1.py\nimport os\nos.environ['CUDA_VISIBLE_DEVICES'] = '0'\nimport torch\nimport timm\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom pathlib import Path\nfrom transformers import AutoImageProcessor, AutoModel\nfrom sklearn.metrics import r2_score\nfrom tqdm import tqdm\nimport warnings\nimport random\nfrom safetensors.torch import load_file\nimport time\nimport gc\nfrom contextlib import contextmanager\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom sklearn.model_selection import KFold, StratifiedGroupKFold, GroupKFold\nwarnings.filterwarnings('ignore')\n\ntarget_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g']\npred_mega_avg = 0\nMODELS_NUM = len([1, 5, 10, 15, 20, 25, 30, 35, 42, 47, 52, 60, 1094, 999]) \nDATA_DIR = '/kaggle/input/csiro-biomass'\nTRAIN_CSV = f'{DATA_DIR}/train.csv'\nTEST_CSV = f'{DATA_DIR}/test.csv' \nBATCH_SIZE = 8\nNUM_WORKERS = 0\nNUM_EPOCHS = 30\nLEARNING_RATE = 0.001\n\ndef predict_loader(model, loader, device):\n        model.eval()\n        preds = []\n        with torch.no_grad():\n            for images, _ in tqdm(loader):\n                images = images.to(device)\n                outputs = model(images)\n                preds.append(outputs.detach().cpu().numpy())\n        return np.concatenate(preds, axis=0)\n        \ndef predict_test_images(model, test_df, embeddings_test, device, batch_size=16, num_workers=NUM_WORKERS):\n        ds = PastureDataset(test_df, embeddings_test, target_bool=False)\n        dl = DataLoader(ds, batch_size=batch_size, shuffle=False, num_workers=num_workers)\n    \n        model.eval()\n        preds = []\n        with torch.no_grad():\n            for images in tqdm(dl):\n                images = images.to(device)\n                outputs = model(images)\n                preds.append(outputs.detach().cpu().numpy())\n        return np.concatenate(preds, axis=0)\n\n    \ndef set_seed():\n        random.seed(RANDOM_SEED)\n        np.random.seed(RANDOM_SEED)\n        torch.manual_seed(RANDOM_SEED)\n        torch.cuda.manual_seed_all(RANDOM_SEED)\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False\n    \nclass PastureDataset(Dataset):\n        def __init__(self, df, emb_data, target_bool=True):\n            self.df = df\n            self.target_bool = target_bool\n            self.emb_data = emb_data \n            \n        def __len__(self):\n            return len(self.df)\n        \n        def __getitem__(self, idx):\n            row = self.df.iloc[idx]\n    \n            if self.target_bool:\n                target = torch.tensor(row[target_names].values.tolist(), dtype=torch.float32)\n                return torch.tensor(self.emb_data[idx], dtype=torch.float32), target\n            else:\n                return torch.tensor(self.emb_data[idx], dtype=torch.float32)\n    \nclass MLP(nn.Module):\n        def __init__(self, in_features=1024):\n            super(MLP, self).__init__()\n            self.classifier = nn.Sequential(\n                nn.Linear(in_features, in_features // 8),\n                nn.LeakyReLU(),\n                nn.Linear(in_features // 8, 3)\n            )\n        \n        def forward(self, x, return_tokens=False):\n            batch_size, num_tokens, token_dim = x.shape\n            x = x.view(-1, token_dim)\n            tok = self.classifier(x)\n            tok = tok.view(batch_size, num_tokens, 3)\n            \n            # Можно попробовать mean вместо sum, если таргеты не зависят от размера фото\n            out = tok.sum(dim=1) \n            \n            if return_tokens:\n                return out, tok\n            return out\n\nfor iseeds in [1, 5, 10, 15, 20, 25, 30, 35, 42, 47, 52, 60, 1094, 999]:\n    print('===============MODEL 1===============')\n    RANDOM_SEED = iseeds\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f'Используется устройство: {device}')\n    \n    SAVE_PATH = 'best_model.pth'\n    \n    print('===============MODEL 1===============')\n    \n    train_df = pd.read_csv(TRAIN_CSV)\n    test_df = pd.read_csv(TEST_CSV)\n    test_df['target'] = -1 \n    \n    train_df = train_df.pivot_table(\n        index=['image_path', 'Sampling_Date', 'State', 'Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm'],\n        columns='target_name',\n        values='target',\n        aggfunc='first'\n    ).reset_index()\n    \n    test_df = test_df.pivot_table(\n        index=['image_path',],\n        columns='target_name',\n        values='target',\n        aggfunc='first'\n    ).reset_index()\n    \n    # %%\n    target_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g']\n    train_df = train_df[~train_df.image_path.isin(['train/ID230058600.jpg', 'train/ID1403107574.jpg'])].reset_index(drop=True)\n    \n    embeddings_train = np.load('/kaggle/input/vit-7b-patch16-dinov3-embeddings/embeddings_train.npy')\n    embeddings_test = np.load('/kaggle/working/embeddings_test.npy')\n    print(embeddings_train.shape)\n    \n    set_seed()\n    N_SPLITS = 5\n    pred_sum = None\n    \n    for fold in range(N_SPLITS):\n        model = MLP(in_features=embeddings_train.shape[2]).to(device)\n        model.load_state_dict(torch.load(f'/kaggle/input/modelseedsmodel1path4/folder{iseeds}/best_model_fold{fold}.pth', map_location=device))\n    \n        fold_pred = predict_test_images(model, test_df, embeddings_test, device, batch_size=16)\n        if pred_sum is None:\n            pred_sum = fold_pred\n        else:\n            pred_sum += fold_pred\n    \n    pred_avg = pred_sum / N_SPLITS\n    pred_mega_avg += pred_avg\n\npred_mega_avg = pred_mega_avg / MODELS_NUM\n\ntest_df[target_names] = pred_mega_avg\ntest_df[target_names] = test_df[target_names].abs()\ntest_df['GDM_g'] = test_df['Dry_Green_g'] + test_df['Dry_Clover_g']\ntest_df['Dry_Total_g'] = test_df['GDM_g'] + test_df['Dry_Dead_g']\nfinal_sub = pd.DataFrame()\ntest_df['ID'] = test_df['image_path'].apply(lambda x: x.split('/')[-1].replace('.jpg',''))\n    \nfor target_name in ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'GDM_g', 'Dry_Total_g']:\n    new_sub = pd.DataFrame({\n        'sample_id': [x+'__'+target_name for x in test_df['ID'].values],\n        'target': test_df[target_name]\n    })\n    final_sub = pd.concat([final_sub, new_sub])\n    \nfinal_sub = final_sub.reset_index(drop=True)\nfinal_sub.to_csv('submission1.csv', index=False)","metadata":{"execution":{"iopub.execute_input":"2026-01-21T10:50:46.53337Z","iopub.status.busy":"2026-01-21T10:50:46.533129Z","iopub.status.idle":"2026-01-21T10:50:46.546075Z","shell.execute_reply":"2026-01-21T10:50:46.54531Z"},"papermill":{"duration":0.020858,"end_time":"2026-01-21T10:50:46.54751","exception":false,"start_time":"2026-01-21T10:50:46.526652","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model 2","metadata":{"papermill":{"duration":0.004771,"end_time":"2026-01-21T10:50:46.557341","exception":false,"start_time":"2026-01-21T10:50:46.55257","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile model2.py\n\nprint('===============MODEL 2===============')\n\nimport os\n# IMPORTANT: set CUDA_VISIBLE_DEVICES before importing torch\n# Expose both GPUs; we will explicitly pick cuda:1 below\nos.environ['CUDA_VISIBLE_DEVICES'] = '1'\nimport torch\nimport timm\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom pathlib import Path\nfrom transformers import AutoImageProcessor, AutoModel\nfrom sklearn.metrics import r2_score\nfrom tqdm import tqdm\nimport warnings\nimport random\nfrom safetensors.torch import load_file\nimport time\nimport gc\nfrom contextlib import contextmanager\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom sklearn.model_selection import KFold, StratifiedGroupKFold, GroupKFold\nwarnings.filterwarnings('ignore')\n\n    \nDATA_DIR = '/kaggle/input/csiro-biomass'\nTRAIN_CSV = f'{DATA_DIR}/train.csv'\nTEST_CSV = f'{DATA_DIR}/test.csv'\n    \nBATCH_SIZE = 8\nNUM_WORKERS = 0\nNUM_EPOCHS = 30\nLEARNING_RATE = 0.001\n    \nSAVE_PATH = 'best_model.pth'\ntarget_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g']\n    \nclass PastureDataset(Dataset):\n        def __init__(self, df, emb_data, target_bool=True):\n            self.df = df\n            self.target_bool = target_bool\n            self.emb_data = emb_data \n            \n        def __len__(self):\n            return len(self.df)\n        \n        def __getitem__(self, idx):\n            row = self.df.iloc[idx]\n    \n            if self.target_bool:\n                target = torch.tensor(row[target_names].values.tolist(), dtype=torch.float32)\n                return torch.tensor(self.emb_data[idx], dtype=torch.float32), target\n            else:\n                return torch.tensor(self.emb_data[idx], dtype=torch.float32)\n    \nclass MLP(nn.Module):\n        def __init__(self, in_features=1024):\n            super(MLP, self).__init__()\n            self.classifier = nn.Sequential(\n                nn.Linear(in_features, in_features // 8),\n                nn.LeakyReLU(),\n                nn.Linear(in_features // 8, 3)\n            )\n        \n        def forward(self, x, return_tokens=False):\n            batch_size, num_tokens, token_dim = x.shape\n            x = x.view(-1, token_dim)\n            tok = self.classifier(x)\n            tok = tok.view(batch_size, num_tokens, 3)\n            \n            # Можно попробовать mean вместо sum, если таргеты не зависят от размера фото\n            out = tok.sum(dim=1) \n            \n            if return_tokens:\n                return out, tok\n            return out\n            \ndef predict_loader(model, loader, device):\n        model.eval()\n        preds = []\n        with torch.no_grad():\n            for images, _ in tqdm(loader):\n                images = images.to(device)\n                outputs = model(images)\n                preds.append(outputs.detach().cpu().numpy())\n        return np.concatenate(preds, axis=0)\n        \ndef predict_test_images(model, test_df, embeddings_test, device, batch_size=16, num_workers=NUM_WORKERS):\n        ds = PastureDataset(test_df, embeddings_test, target_bool=False)\n        dl = DataLoader(ds, batch_size=batch_size, shuffle=False, num_workers=num_workers)\n    \n        model.eval()\n        preds = []\n        with torch.no_grad():\n            for images in tqdm(dl):\n                images = images.to(device)\n                outputs = model(images)\n                preds.append(outputs.detach().cpu().numpy())\n        return np.concatenate(preds, axis=0)\n\n@contextmanager\ndef cpu_model_load_lock(lock_path='/tmp/vit7b_dinov3_cpu_load.lock', poll_seconds=1.0):\n        # Межпроцессный lock: только один процесс одновременно грузит ViT-7B в RAM\n        import fcntl\n        os.makedirs(os.path.dirname(lock_path), exist_ok=True)\n        with open(lock_path, 'w') as lock_file:\n            while True:\n                try:\n                    fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)\n                    break\n                except BlockingIOError:\n                    time.sleep(poll_seconds)\n            try:\n                yield\n            finally:\n                fcntl.flock(lock_file, fcntl.LOCK_UN)\n                \n\nfor iseeds in [1, 5, 10, 15, 20, 25, 30, 35, 42, 47, 52, 60, 1094, 999]:\n    RANDOM_SEED = iseeds\n    \n    def set_seed():\n        random.seed(RANDOM_SEED)\n        np.random.seed(RANDOM_SEED)\n        torch.manual_seed(RANDOM_SEED)\n        torch.cuda.manual_seed_all(RANDOM_SEED)\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False\n    \n    set_seed()\n    os.makedirs('/kaggle/embs', exist_ok=True)\n    os.makedirs('/kaggle/working/model2/', exist_ok=True)\n    \n    # Explicitly choose GPU1\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f'Используется устройство: {device}')\n    \n    set_seed()\n    \n    gc.collect()\n    \n    print('===============MODEL 2===============')\n    \n    train_df = pd.read_csv(TRAIN_CSV)\n    test_df = pd.read_csv(TEST_CSV)\n    test_df['target'] = -1 \n    \n    train_df = train_df.pivot_table(\n        index=['image_path', 'Sampling_Date', 'State', 'Species', 'Pre_GSHH_NDVI', 'Height_Ave_cm'],\n        columns='target_name',\n        values='target',\n        aggfunc='first'\n    ).reset_index()\n    \n    test_df = test_df.pivot_table(\n        index=['image_path',],\n        columns='target_name',\n        values='target',\n        aggfunc='first'\n    ).reset_index()\n    \n    # %%\n    train_df = train_df[~train_df.image_path.isin(['train/ID230058600.jpg', 'train/ID1403107574.jpg'])].reset_index(drop=True)\n    \n    embeddings_train = np.load('/kaggle/input/vit-7b-patch16-dinov3-embeddings/embeddings_train_9path.npy')\n    embeddings_test = np.load('/kaggle/working/embeddings_test_9path.npy')\n    print(embeddings_train.shape)\n    \n    set_seed()\n    predictions = {}\n    \n    N_SPLITS = 5\n    pred_sum = None\n    \n    for fold in range(N_SPLITS):\n        model = MLP(in_features=embeddings_train.shape[2]).to(device)\n        model.load_state_dict(torch.load(f'/kaggle/input/modelsmoreseed9path/folder{iseeds}/best_model_fold{fold}.pth', map_location=device))\n    \n        fold_pred = predict_test_images(model, test_df, embeddings_test, device, batch_size=16)\n        if pred_sum is None:\n            pred_sum = fold_pred\n        else:\n            pred_sum += fold_pred\n    \n    pred_avg = pred_sum / N_SPLITS\n    \ntest_df[target_names] = pred_avg\ntest_df[target_names] = test_df[target_names].abs()\ntest_df['GDM_g'] = test_df['Dry_Green_g'] + test_df['Dry_Clover_g']\ntest_df['Dry_Total_g'] = test_df['GDM_g'] + test_df['Dry_Dead_g']\nfinal_sub = pd.DataFrame()\ntest_df['ID'] = test_df['image_path'].apply(lambda x: x.split('/')[-1].replace('.jpg',''))\n    \nfor target_name in ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'GDM_g', 'Dry_Total_g']:\n    new_sub = pd.DataFrame({\n        'sample_id': [x+'__'+target_name for x in test_df['ID'].values],\n        'target': test_df[target_name]\n    })\n    final_sub = pd.concat([final_sub, new_sub])\n    \nfinal_sub = final_sub.reset_index(drop=True)\nfinal_sub.to_csv('submission2.csv', index=False)","metadata":{"execution":{"iopub.execute_input":"2026-01-21T10:50:46.56938Z","iopub.status.busy":"2026-01-21T10:50:46.568982Z","iopub.status.idle":"2026-01-21T10:50:46.580127Z","shell.execute_reply":"2026-01-21T10:50:46.579406Z"},"papermill":{"duration":0.018403,"end_time":"2026-01-21T10:50:46.581406","exception":false,"start_time":"2026-01-21T10:50:46.563003","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model 3","metadata":{"papermill":{"duration":0.004803,"end_time":"2026-01-21T10:50:46.591165","exception":false,"start_time":"2026-01-21T10:50:46.586362","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile model3.py\nprint('===============MODEL 3===============')\n\nimport sys\nfrom pathlib import Path\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom tqdm.auto import tqdm\nfrom albumentations.pytorch import ToTensorV2\nimport albumentations as A\n\nimport os\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"\n\n\nDATASET_PATH = Path('/kaggle/input/csiro-dinov2-mil-flower-count')\nCUDA = torch.device('cuda')\n\nclass DinoFeatureExtractor(nn.Module):\n    def __init__(self, repo_path, model_name):\n        super().__init__()\n        self.backbone = torch.hub.load(str(repo_path), model_name, source='local', pretrained=False)\n        self.backbone = self.backbone.half()\n        self.backbone.eval()\n        \n    def forward(self, x):\n        with torch.no_grad():\n            out = self.backbone.forward_features(x)\n            return out['x_norm_patchtokens'].mean(dim=1)\n\nREPO_PATH = DATASET_PATH / 'dinov2_repo'\n\nprint(\"Loading DinoV2 Backbone...\")\nfeature_extractor = DinoFeatureExtractor(repo_path=REPO_PATH, model_name='dinov2_vitg14_reg')\nstate_dict = torch.load(DATASET_PATH / 'extractor.pth', map_location='cpu')\nfeature_extractor.load_state_dict(state_dict)\nfeature_extractor.to(CUDA)\n_ = feature_extractor.eval()\n\nclass MilTransformer(nn.Module):\n    def __init__(self, input_dim=1536, hidden_dim=768, n_heads=8, n_layers=2, dropout=0.2, max_seq_len=32):\n        super().__init__()\n        self.fc_in = nn.Sequential(\n            nn.Linear(input_dim, hidden_dim),\n            nn.LayerNorm(hidden_dim),\n            nn.GELU(),\n            nn.Dropout(dropout)\n        )\n        \n        self.d_model = hidden_dim\n        self.pos_embedding = nn.Parameter(torch.randn(1, max_seq_len, hidden_dim) * 0.02)\n\n        encoder_layer = nn.TransformerEncoderLayer(\n            d_model=self.d_model, \n            nhead=n_heads, \n            dim_feedforward=self.d_model * 2,\n            dropout=dropout, \n            activation='gelu',\n            batch_first=True,\n            norm_first=True\n        )\n        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=n_layers)\n        self.attn_v = nn.Linear(self.d_model, hidden_dim)\n        self.attn_u = nn.Linear(self.d_model, hidden_dim)\n        self.attn_w = nn.Linear(hidden_dim, 1)\n        \n        self.head = nn.Sequential(\n            nn.Linear(self.d_model, hidden_dim),\n            nn.ReLU(),\n            nn.Dropout(dropout),\n            nn.Linear(hidden_dim, 3) \n        )\n        \n        self.aux_head = nn.Linear(self.d_model, 2)\n\n        self.flower_head = nn.Sequential(\n            nn.Linear(self.d_model, hidden_dim // 2),\n            nn.ReLU(),\n            nn.Linear(hidden_dim // 2, 1)\n        )\n\n    def forward(self, x):\n        x = self.fc_in(x)\n        b, n, _ = x.shape\n        x = x + self.pos_embedding[:, :n, :]\n        feat = self.transformer(x)\n        v = torch.tanh(self.attn_v(feat))\n        u = torch.sigmoid(self.attn_u(feat))\n        scores = self.attn_w(v * u) \n        \n        weights = torch.softmax(scores, dim=1)\n        global_feat = torch.sum(feat * weights, dim=1)\n\n        raw_out = self.head(global_feat)\n        \n        comps = F.relu(raw_out[:, :3])\n        clover, dead, green = comps[:, 0], comps[:, 1], comps[:, 2]\n        \n        gdm = clover + green\n        total = gdm + dead\n        \n        main_targets = torch.stack([clover, dead, green, total, gdm], dim=1)\n        \n        return main_targets\n\nDEVICE = torch.device('cuda')\nCONFIG = torch.load(DATASET_PATH / 'config.pth', map_location=DEVICE)\n\nprint(\"Loading MIL Heads...\")\nmodels = []\nfor p in sorted(list(DATASET_PATH.glob('fold_*.pth'))):\n    model = MilTransformer(dropout=0.0)\n    model.load_state_dict(torch.load(p, map_location='cpu'))\n    model.to(DEVICE)\n    model.eval()\n    models.append(model)\nprint(f\"Loaded {len(models)} heads.\")\n\ntest_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\ntest_df['image_path'] = test_df['image_path'].apply(lambda x: f'/kaggle/input/csiro-biomass/{x}')\nunique_paths = test_df['image_path'].unique()\n\nextractor_data_transforms = A.Compose([\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2()\n])\n\ndef get_patches_tensor(image_path, patch_size, stride, aug_mode='original'):\n    image = cv2.imread(str(image_path))\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = cv2.resize(image, (2016, 1008))\n\n    if aug_mode == 'h_flip':\n        image = cv2.flip(image, 1)\n    elif aug_mode == 'v_flip':\n        image = cv2.flip(image, 0)\n    elif aug_mode == 'hv_flip':\n        image = cv2.flip(image, -1)\n    \n    h, w, _ = image.shape\n    y_steps = sorted(list(set(list(range(0, h - patch_size + 1, stride)) + [h - patch_size])))\n    x_steps = sorted(list(set(list(range(0, w - patch_size + 1, stride)) + [w - patch_size])))\n    \n    patches = []\n    for y in y_steps:\n        for x in x_steps:\n            patch = image[y:y+patch_size, x:x+patch_size]\n            patch = extractor_data_transforms(image=patch)['image']\n            patches.append(patch)\n            \n    return torch.stack(patches)\n\nresults = []\nPATCH_SIZE = 448\nSTRIDE = 224\ntta_modes = ['original']\nprint(f\"Starting inference on {len(unique_paths)} images...\")\nwith torch.no_grad():\n    for img_path in tqdm(unique_paths):\n        img_tta_preds = []\n        for mode in tta_modes:\n            patches = get_patches_tensor(img_path, PATCH_SIZE, STRIDE, aug_mode=mode)\n            patches = patches.half()\n            patches = patches.to(DEVICE)\n            features = feature_extractor(patches)\n            features = features.unsqueeze(0).float()\n            fold_preds = []\n            for model in models:\n                pred = model(features) # [1, 5]\n                fold_preds.append(pred.cpu().numpy())\n    \n            avg_pred = np.mean(fold_preds, axis=0) # [1, 5]\n            img_tta_preds.append(avg_pred)\n\n        final_img_pred = np.mean(img_tta_preds, axis=0) # [1, 5]\n        final_img_pred = final_img_pred.clip(0, None)\n\n        results.append({\n            'image_path': img_path,\n            'preds': final_img_pred[0]\n        })\n\n\nprint(\"Formatting submission...\")\npath_to_preds = {r['image_path']: r['preds'] for r in results}\ntarget_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']\n\npred_data = []\nfor r in results:\n    path = r['image_path']\n    preds = r['preds']\n    for i, t_name in enumerate(target_names):\n        pred_data.append({\n            'image_path': path,\n            'target_name': t_name,\n            'prediction': preds[i]\n        })\n\npreds_df = pd.DataFrame(pred_data)\nsubmission = test_df.merge(preds_df, on=['image_path', 'target_name'], how='left')\nsubmission['target'] = submission['prediction'].clip(0, None)\n\ndinov2_mil_sub = submission[['sample_id', 'target']]\n\nprint(\"Submission saved!\")\ndinov2_mil_sub.to_csv('submission3.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:49.414988Z","iopub.execute_input":"2026-01-24T02:26:49.415246Z","iopub.status.idle":"2026-01-24T02:26:49.424449Z","shell.execute_reply.started":"2026-01-24T02:26:49.415217Z","shell.execute_reply":"2026-01-24T02:26:49.423614Z"},"papermill":{"duration":0.013823,"end_time":"2026-01-21T10:50:46.609933","exception":false,"start_time":"2026-01-21T10:50:46.59611","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model 4","metadata":{"papermill":{"duration":0.004962,"end_time":"2026-01-21T10:50:46.619901","exception":false,"start_time":"2026-01-21T10:50:46.614939","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile model4.py\n\nprint('===============MODEL 4===============')\nimport os\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"1\"\n\nimport sys\nfrom pathlib import Path\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom tqdm.auto import tqdm\nfrom albumentations.pytorch import ToTensorV2\nimport albumentations as A\n\nCUDA = torch.device('cuda')\n\nos.environ[\"TRANSFORMERS_OFFLINE\"] = \"1\"\nos.environ[\"HF_HUB_OFFLINE\"] = \"1\"\nos.environ[\"TRANSFORMERS_NO_TF\"] = \"1\"\nos.environ[\"TRANSFORMERS_NO_FLAX\"] = \"1\"\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\ntorch.backends.cudnn.benchmark = True\n\nfrom transformers import AutoModel\n\ndinov3_dir = '/kaggle/input/dinov3-7b-lvd-fp16'\n\nclass DinoV3FeatureExtractor(nn.Module):\n    def __init__(self, pretrained_model_name: str):\n        super().__init__()\n        self.backbone = AutoModel.from_pretrained(\n            pretrained_model_name, local_files_only=True, trust_remote_code=True,\n            dtype=torch.float16,\n            low_cpu_mem_usage=True,\n            device_map=\"auto\",\n        )\n        self.backbone.eval()\n        self.backbone = torch.compile(self.backbone)\n\n        self.embed_dim = getattr(self.backbone.config, \"hidden_size\", None)\n        if self.embed_dim is None:\n            self.embed_dim = getattr(self.backbone.config, \"hidden_sizes\", [None])[-1]\n\n    @torch.inference_mode()\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        with torch.autocast(device_type=\"cuda\", dtype=torch.float16):\n            out = self.backbone(pixel_values=x)\n            if hasattr(out, \"pooler_output\") and out.pooler_output is not None:\n                return out.pooler_output\n            hs = out.last_hidden_state\n            if hs.dim() == 3 and hs.size(1) > 1:\n                return hs[:, 1:, :].mean(dim=1)\n            return hs.mean(dim=1) if hs.dim() == 3 else hs\n\nfeature_extractor = DinoV3FeatureExtractor(dinov3_dir)\nfeature_extractor = feature_extractor.to(CUDA)\n\nclass PatchSumRegressor(nn.Module):\n    def __init__(self, input_dim=4096, hidden_dim=768, dropout=0.2, num_heads=8):\n        super().__init__()\n\n        self.projector = nn.Sequential(\n            nn.Linear(input_dim, hidden_dim),\n            nn.LayerNorm(hidden_dim),\n            nn.GELU(),\n            nn.Dropout(dropout)\n        )\n\n        encoder_layer = nn.TransformerEncoderLayer(\n            d_model=hidden_dim,\n            nhead=num_heads,\n            dim_feedforward=hidden_dim * 4,\n            dropout=dropout,\n            activation='gelu',\n            batch_first=True,\n            norm_first=True\n        )\n\n        self.context_encoder = nn.TransformerEncoder(encoder_layer, num_layers=1)\n\n        self.regressor = nn.Sequential(\n            nn.Linear(hidden_dim, hidden_dim // 2),\n            nn.Mish(),\n            nn.Linear(hidden_dim // 2, 3) \n        )\n\n        self.attention = nn.Sequential(\n            nn.Linear(hidden_dim, hidden_dim // 2),\n            nn.Tanh(),\n            nn.Linear(hidden_dim // 2, 3),\n            nn.Sigmoid() \n        )\n        \n    def forward(self, x, area_w=None):\n        h = self.projector(x)\n        h_mixed = self.context_encoder(h)\n        h = h + h_mixed \n\n        raw_preds = F.softplus(self.regressor(h))\n\n        if area_w is not None:\n            area_w = area_w.view(1, -1, 1).to(raw_preds.device)\n            raw_preds = raw_preds * area_w\n        \n        weights = self.attention(h)\n\n        weighted_preds = raw_preds * weights\n        image_preds = weighted_preds.sum(dim=1)\n        \n        clover, dead, green = image_preds[:, 0], image_preds[:, 1], image_preds[:, 2]\n        gdm = clover + green\n        total = gdm + dead\n        \n        return torch.stack([clover, dead, green, total, gdm], dim=1)\n\ndef load_models(sub_name):\n    print(f'loading {sub_name}')\n    dataset_path = Path('/kaggle/input/') / sub_name\n\n    print(\"Loading Heads...\")\n    models = []\n    for p in sorted(list(dataset_path.glob('fold_*.pth'))):\n        model = PatchSumRegressor()\n        model.load_state_dict(torch.load(p, map_location='cpu'))\n        model.to(CUDA)\n        model.eval()\n        model = torch.compile(model)\n        models.append(model)\n    print(f\"Loaded {len(models)} heads.\")\n    return models\n\nmodels_raw = load_models('csiro-dinov3-7b-transformer-area2')\nmodels_4_8 = load_models('csiro-dinov3-7b-transformer-area2-4-8')\n\ndef compute_area_weights(h, w, patch_size=224, stride=112):\n    y_steps = sorted(list(set(list(range(0, h - patch_size + 1, stride)) + [h - patch_size])))\n    x_steps = sorted(list(set(list(range(0, w - patch_size + 1, stride)) + [w - patch_size])))\n\n    coverage = np.zeros((h, w), dtype=np.float32)\n    for y in y_steps:\n        for x in x_steps:\n            coverage[y:y+patch_size, x:x+patch_size] += 1.0\n\n    inv_cov = 1.0 / np.maximum(coverage, 1.0)\n\n    weights = []\n    for y in y_steps:\n        for x in x_steps:\n            weights.append(inv_cov[y:y+patch_size, x:x+patch_size].mean())\n\n    weights = np.array(weights, dtype=np.float32)\n    return weights, (len(y_steps), len(x_steps))\n\narea_w_np, (h_src, w_src) = compute_area_weights(h=1008, w=2016, patch_size=224, stride=112)\nprint(area_w_np.shape, h_src, w_src)\n\ndef downsample_area_w(area_w, h_src=8, w_src=17, target_grid=(4, 8)):\n    w = area_w.view(1, 1, h_src, w_src)  # [1,1,8,17]\n    w_pooled = F.adaptive_avg_pool2d(w, output_size=target_grid)  # [1,1,h2,w2]\n    return w_pooled.view(-1).contiguous()\n\nAREA_W = torch.tensor(area_w_np, dtype=torch.float32)\n\ntest_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\ntest_df['image_path'] = test_df['image_path'].apply(lambda x: f'/kaggle/input/csiro-biomass/{x}')\nunique_paths = test_df['image_path'].unique()\n\nextractor_data_transforms = A.Compose([\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2()\n])\n\ndef downsample_features_adaptive(features, target_grid=(4, 8)):\n    is_batch = features.dim() == 3\n    if not is_batch:\n        features = features.unsqueeze(0)\n        \n    b, n, c = features.shape\n    h_src, w_src = 8, 17\n    x = features.transpose(1, 2).reshape(b, c, h_src, w_src)\n    x_pooled = F.adaptive_avg_pool2d(x, output_size=target_grid)\n    x_flat = x_pooled.flatten(2).transpose(1, 2)\n    \n    if not is_batch:\n        x_flat = x_flat.squeeze(0)\n        \n    return x_flat\n\ndef get_patches_tensor(image_path, patch_size, stride, aug_mode='original'):\n    image = cv2.imread(str(image_path))\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = cv2.resize(image, (2016, 1008))\n\n    if aug_mode == 'h_flip':\n        image = image[::-1, :, :]\n    if aug_mode == 'w_flip':\n        image = image[:, ::-1, :]\n    \n    h, w, _ = image.shape\n    y_steps = sorted(list(set(list(range(0, h - patch_size + 1, stride)) + [h - patch_size])))\n    x_steps = sorted(list(set(list(range(0, w - patch_size + 1, stride)) + [w - patch_size])))\n    \n    patches = []\n    for y in y_steps:\n        for x in x_steps:\n            patch = image[y:y+patch_size, x:x+patch_size]\n            patch = extractor_data_transforms(image=patch)['image']\n            patches.append(patch)\n            \n    return torch.stack(patches)\n\nresults = []\nPATCH_SIZE = 224\nSTRIDE = 112\nEXTRACTOR_BATCH_SIZE = 68\ntta_modes = ['original']\nprint(f\"Starting inference on {len(unique_paths)} images...\")\nwith torch.no_grad():\n    for img_path in tqdm(unique_paths):\n        img_preds = []\n        weight_sum = 0.\n        for mode in tta_modes:\n            patches = get_patches_tensor(img_path, PATCH_SIZE, STRIDE, aug_mode=mode)\n            patches = patches.half()\n            patches = patches.to(CUDA)\n            features_list = []\n            for i in range(0, len(patches), EXTRACTOR_BATCH_SIZE):\n                batch = patches[i : i + EXTRACTOR_BATCH_SIZE]\n                batch_features = feature_extractor(batch)\n                features_list.append(batch_features.clone())\n            features = torch.cat(features_list, dim=0)\n            features = features.unsqueeze(0)\n\n            for models_list, grid, weight in (\n                (models_raw, None, 1),\n                (models_4_8, (4, 8), 1),\n            ):\n                cur_model_features = features.clone()\n                cur_area = AREA_W.clone()\n                if grid is not None:\n                    cur_model_features = downsample_features_adaptive(cur_model_features, grid)\n                    cur_area = downsample_area_w(cur_area, h_src=8, w_src=17, target_grid=grid)\n                cur_area = cur_area.to(CUDA)\n\n                for model in models_list:\n                    pred = model(cur_model_features, area_w=cur_area)\n                    img_preds.append(weight * pred.cpu().numpy())\n                    weight_sum += weight\n\n        final_img_pred = np.sum(img_preds, axis=0) / weight_sum\n        final_img_pred = final_img_pred.clip(0, None)\n\n        results.append({\n            'image_path': img_path,\n            'preds': final_img_pred[0]\n        })\n\nprint(\"Formatting submission...\")\npath_to_preds = {r['image_path']: r['preds'] for r in results}\ntarget_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']\n\npred_data = []\nfor r in results:\n    path = r['image_path']\n    preds = r['preds']\n    for i, t_name in enumerate(target_names):\n        pred_data.append({\n            'image_path': path,\n            'target_name': t_name,\n            'prediction': preds[i]\n        })\n\npreds_df = pd.DataFrame(pred_data)\nsubmission = test_df.merge(preds_df, on=['image_path', 'target_name'], how='left')\nsubmission['target'] = submission['prediction'].clip(0, None)\n\ndinov3_mil_sub = submission[['sample_id', 'target']]\n\nprint(\"Submission saved!\")\ndinov3_mil_sub.to_csv('submission4.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:51.425577Z","iopub.execute_input":"2026-01-24T02:26:51.426417Z","iopub.status.idle":"2026-01-24T02:26:51.435014Z","shell.execute_reply.started":"2026-01-24T02:26:51.426384Z","shell.execute_reply":"2026-01-24T02:26:51.434206Z"},"papermill":{"duration":0.0146,"end_time":"2026-01-21T10:50:46.63946","exception":false,"start_time":"2026-01-21T10:50:46.62486","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model 5","metadata":{}},{"cell_type":"code","source":"%%writefile model5.py\n\nprint('===============MODEL 5===============')\nimport os\nos.environ[\"CUDA_VISIBLE_DEVICES\"] = \"0\"\n\nimport sys\nfrom pathlib import Path\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom tqdm.auto import tqdm\nfrom albumentations.pytorch import ToTensorV2\nimport albumentations as A\n\nCUDA = torch.device('cuda')\n\nos.environ[\"TRANSFORMERS_OFFLINE\"] = \"1\"\nos.environ[\"HF_HUB_OFFLINE\"] = \"1\"\nos.environ[\"TRANSFORMERS_NO_TF\"] = \"1\"\nos.environ[\"TRANSFORMERS_NO_FLAX\"] = \"1\"\nos.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"3\"\ntorch.backends.cudnn.benchmark = True\n\nfrom transformers import AutoModel\n\ndinov3_dir = '/kaggle/input/dinov3-7b-lvd-fp16'\n\nclass DinoV3FeatureExtractor(nn.Module):\n    def __init__(self, pretrained_model_name: str):\n        super().__init__()\n        self.backbone = AutoModel.from_pretrained(\n            pretrained_model_name, local_files_only=True, trust_remote_code=True,\n            dtype=torch.float16,\n            low_cpu_mem_usage=True,\n            device_map=\"auto\",\n        )\n        self.backbone.eval()\n        self.backbone = torch.compile(self.backbone)\n\n        self.embed_dim = getattr(self.backbone.config, \"hidden_size\", None)\n        if self.embed_dim is None:\n            self.embed_dim = getattr(self.backbone.config, \"hidden_sizes\", [None])[-1]\n\n    @torch.inference_mode()\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        with torch.autocast(device_type=\"cuda\", dtype=torch.float16):\n            out = self.backbone(pixel_values=x)\n            if hasattr(out, \"pooler_output\") and out.pooler_output is not None:\n                return out.pooler_output\n            hs = out.last_hidden_state\n            if hs.dim() == 3 and hs.size(1) > 1:\n                return hs[:, 1:, :].mean(dim=1)\n            return hs.mean(dim=1) if hs.dim() == 3 else hs\n\nfeature_extractor = DinoV3FeatureExtractor(dinov3_dir)\nfeature_extractor = feature_extractor.to(CUDA)\n\nclass PatchSumRegressor(nn.Module):\n    def __init__(self, input_dim=4096, hidden_dim=768, dropout=0.2, num_heads=8):\n        super().__init__()\n\n        self.projector = nn.Sequential(\n            nn.Linear(input_dim, hidden_dim),\n            nn.LayerNorm(hidden_dim),\n            nn.GELU(),\n            nn.Dropout(dropout)\n        )\n\n        encoder_layer = nn.TransformerEncoderLayer(\n            d_model=hidden_dim,\n            nhead=num_heads,\n            dim_feedforward=hidden_dim * 4,\n            dropout=dropout,\n            activation='gelu',\n            batch_first=True,\n            norm_first=True\n        )\n\n        self.context_encoder = nn.TransformerEncoder(encoder_layer, num_layers=1)\n\n        self.regressor = nn.Sequential(\n            nn.Linear(hidden_dim, hidden_dim // 2),\n            nn.Mish(),\n            nn.Linear(hidden_dim // 2, 3) \n        )\n\n        self.attention = nn.Sequential(\n            nn.Linear(hidden_dim, hidden_dim // 2),\n            nn.Tanh(),\n            nn.Linear(hidden_dim // 2, 3),\n            nn.Sigmoid() \n        )\n        \n    def forward(self, x, area_w=None):\n        h = self.projector(x)\n        h_mixed = self.context_encoder(h)\n        h = h + h_mixed \n\n        raw_preds = F.softplus(self.regressor(h))\n\n        if area_w is not None:\n            area_w = area_w.view(1, -1, 1).to(raw_preds.device)\n            raw_preds = raw_preds * area_w\n        \n        weights = self.attention(h)\n\n        weighted_preds = raw_preds * weights\n        image_preds = weighted_preds.sum(dim=1)\n        \n        clover, dead, green = image_preds[:, 0], image_preds[:, 1], image_preds[:, 2]\n        gdm = clover + green\n        total = gdm + dead\n        \n        return torch.stack([clover, dead, green, total, gdm], dim=1)\n\n\ndef load_models(sub_name, model_class):\n    print(f'loading {sub_name}')\n    dataset_path = Path('/kaggle/input/') / sub_name\n\n    print(\"Loading Heads...\")\n    models = []\n    for p in sorted(list(dataset_path.glob('fold_*.pth'))):\n        model = model_class()\n        model.load_state_dict(torch.load(p, map_location='cpu'))\n        model.to(CUDA)\n        model.eval()\n        model = torch.compile(model)\n        models.append(model)\n    print(f\"Loaded {len(models)} heads.\")\n    return models\n\nmodels_raw_area2 = load_models('csiro-dinov3-7b-area-336-168', PatchSumRegressor)\n# models_4_8_area2 = load_models('csiro-dinov3-7b-area-336-168-3-6', PatchSumRegressor)\nmodels_3_6_area2 = load_models('csiro-dinov3-7b-area-336-168-3-6', PatchSumRegressor)\n\ndef compute_area_weights(h, w, patch_size=224, stride=112):\n    y_steps = sorted(list(set(list(range(0, h - patch_size + 1, stride)) + [h - patch_size])))\n    x_steps = sorted(list(set(list(range(0, w - patch_size + 1, stride)) + [w - patch_size])))\n\n    coverage = np.zeros((h, w), dtype=np.float32)\n    for y in y_steps:\n        for x in x_steps:\n            coverage[y:y+patch_size, x:x+patch_size] += 1.0\n\n    inv_cov = 1.0 / np.maximum(coverage, 1.0)\n\n    weights = []\n    for y in y_steps:\n        for x in x_steps:\n            weights.append(inv_cov[y:y+patch_size, x:x+patch_size].mean())\n\n    weights = np.array(weights, dtype=np.float32)\n    return weights, (len(y_steps), len(x_steps))\n\narea_w_np, (h_src, w_src) = compute_area_weights(h=1008, w=2016, patch_size=336, stride=168)\nprint(area_w_np.shape, h_src, w_src)\n\ndef downsample_area_w(area_w, h_src=5, w_src=11, target_grid=(4, 8)):\n    w = area_w.view(1, 1, h_src, w_src)  # [1,1,8,17]\n    w_pooled = F.adaptive_avg_pool2d(w, output_size=target_grid)  # [1,1,h2,w2]\n    return w_pooled.view(-1).contiguous()\n\nAREA_W = torch.tensor(area_w_np, dtype=torch.float32)\n\ntest_df = pd.read_csv('/kaggle/input/csiro-biomass/test.csv')\ntest_df['image_path'] = test_df['image_path'].apply(lambda x: f'/kaggle/input/csiro-biomass/{x}')\nunique_paths = test_df['image_path'].unique()\n\nextractor_data_transforms = A.Compose([\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2()\n])\n\ndef downsample_features_adaptive(features, target_grid=(4, 8)):\n    is_batch = features.dim() == 3\n    if not is_batch:\n        features = features.unsqueeze(0)\n        \n    b, n, c = features.shape\n    # h_src, w_src = 8, 17\n    h_src, w_src = 5, 11    \n    x = features.transpose(1, 2).reshape(b, c, h_src, w_src)\n    x_pooled = F.adaptive_avg_pool2d(x, output_size=target_grid)\n    x_flat = x_pooled.flatten(2).transpose(1, 2)\n    \n    if not is_batch:\n        x_flat = x_flat.squeeze(0)\n        \n    return x_flat\n\ndef get_patches_tensor(image_path, patch_size, stride, aug_mode='original'):\n    image = cv2.imread(str(image_path))\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = cv2.resize(image, (2016, 1008))\n\n    if aug_mode == 'h_flip':\n        image = image[::-1, :, :]\n    if aug_mode == 'w_flip':\n        image = image[:, ::-1, :]\n    \n    h, w, _ = image.shape\n    y_steps = sorted(list(set(list(range(0, h - patch_size + 1, stride)) + [h - patch_size])))\n    x_steps = sorted(list(set(list(range(0, w - patch_size + 1, stride)) + [w - patch_size])))\n    \n    patches = []\n    for y in y_steps:\n        for x in x_steps:\n            patch = image[y:y+patch_size, x:x+patch_size]\n            patch = extractor_data_transforms(image=patch)['image']\n            patches.append(patch)\n            \n    return torch.stack(patches)\n\nresults = []\nPATCH_SIZE = 336\nSTRIDE = 168\nEXTRACTOR_BATCH_SIZE = 11\ntta_modes = ['original']\nprint(f\"Starting inference on {len(unique_paths)} images...\")\nwith torch.no_grad():\n    for img_path in tqdm(unique_paths):\n        img_preds = []\n        weight_sum = 0.\n        for mode in tta_modes:\n            patches = get_patches_tensor(img_path, PATCH_SIZE, STRIDE, aug_mode=mode)\n            patches = patches.half()\n            patches = patches.to(CUDA)\n            features_list = []\n            for i in range(0, len(patches), EXTRACTOR_BATCH_SIZE):\n                batch = patches[i : i + EXTRACTOR_BATCH_SIZE]\n                batch_features = feature_extractor(batch)\n                features_list.append(batch_features.clone())\n            features = torch.cat(features_list, dim=0)\n            features = features.unsqueeze(0)\n\n            for models_list, grid, weight in (\n                (models_raw_area2, None, 1),\n                # (models_4_8_area2, (4, 8), 0.4),\n                (models_3_6_area2, (3, 6), 1),\n            ):\n                cur_model_features = features.clone()\n                cur_area = AREA_W.clone()\n                if grid is not None:\n                    cur_model_features = downsample_features_adaptive(cur_model_features, grid)\n                    cur_area = downsample_area_w(cur_area, h_src=5, w_src=11, target_grid=grid)\n                cur_area = cur_area.to(CUDA)\n\n                for model in models_list:\n                    pred = model(cur_model_features, area_w=cur_area)\n                    img_preds.append(weight * pred.cpu().numpy())\n                    weight_sum += weight\n\n        final_img_pred = np.sum(img_preds, axis=0) / weight_sum\n        final_img_pred = final_img_pred.clip(0, None)\n\n        results.append({\n            'image_path': img_path,\n            'preds': final_img_pred[0]\n        })\n\nprint(\"Formatting submission...\")\npath_to_preds = {r['image_path']: r['preds'] for r in results}\ntarget_names = ['Dry_Clover_g', 'Dry_Dead_g', 'Dry_Green_g', 'Dry_Total_g', 'GDM_g']\n\npred_data = []\nfor r in results:\n    path = r['image_path']\n    preds = r['preds']\n    for i, t_name in enumerate(target_names):\n        pred_data.append({\n            'image_path': path,\n            'target_name': t_name,\n            'prediction': preds[i]\n        })\n\npreds_df = pd.DataFrame(pred_data)\nsubmission = test_df.merge(preds_df, on=['image_path', 'target_name'], how='left')\nsubmission['target'] = submission['prediction'].clip(0, None)\n\ndinov3_mil_sub = submission[['sample_id', 'target']]\n\nprint(\"Submission saved!\")\ndinov3_mil_sub.to_csv('submission5.csv', index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Blend","metadata":{"papermill":{"duration":0.005063,"end_time":"2026-01-21T10:50:46.649662","exception":false,"start_time":"2026-01-21T10:50:46.644599","status":"completed"},"tags":[]}},{"cell_type":"code","source":"%%writefile blend.py\nprint('===============BLEND===============')\n\nimport pandas as pd\nsub = pd.read_csv('submission1.csv')\nsub1 = pd.read_csv('submission2.csv')\nsub2 = pd.read_csv('submission3.csv')\nsub3 = pd.read_csv('submission4.csv')\nsub4 = pd.read_csv('submission5.csv')\n\nsub = sub.sort_values('sample_id')\nsub1 = sub1.sort_values('sample_id')\nsub2 = sub2.sort_values('sample_id')\nsub3 = sub3.sort_values('sample_id')\nsub4 = sub4.sort_values('sample_id')\n\nsub1['target'] = sub['target'].values * 0.1 +\\\n                 sub1['target'].values * 0.2 +\\\n                 sub2['target'].values * 0.2 +\\\n                 sub3['target'].values * 0.4 +\\\n                 sub4['target'].values * 0.1\n\nsub1.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:56.221077Z","iopub.execute_input":"2026-01-24T02:26:56.221859Z","iopub.status.idle":"2026-01-24T02:26:56.226953Z","shell.execute_reply.started":"2026-01-24T02:26:56.221828Z","shell.execute_reply":"2026-01-24T02:26:56.226112Z"},"papermill":{"duration":0.011571,"end_time":"2026-01-21T10:50:46.666153","exception":false,"start_time":"2026-01-21T10:50:46.654582","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Run all scripts","metadata":{"papermill":{"duration":0.005276,"end_time":"2026-01-21T10:50:46.676927","exception":false,"start_time":"2026-01-21T10:50:46.671651","status":"completed"},"tags":[]}},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2026-01-24T02:26:58.289036Z","iopub.execute_input":"2026-01-24T02:26:58.289453Z","iopub.status.idle":"2026-01-24T02:26:58.417583Z","shell.execute_reply.started":"2026-01-24T02:26:58.289427Z","shell.execute_reply":"2026-01-24T02:26:58.416712Z"},"papermill":{"duration":0.135021,"end_time":"2026-01-21T10:50:46.817003","exception":false,"start_time":"2026-01-21T10:50:46.681982","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport sys\nimport subprocess\nimport selectors\n\n# In notebooks stdout is often buffered; force unbuffered Python in child processes\nenv = os.environ.copy()\nenv[\"PYTHONUNBUFFERED\"] = \"1\"\n\ndef _stream_process_output(label: str, proc: subprocess.Popen, selector: selectors.BaseSelector):\n    # Register stdout for multiplexed streaming\n    if proc.stdout is not None:\n        selector.register(proc.stdout, selectors.EVENT_READ, data=label)\n\nprocs = [\n    (\"m1\", subprocess.Popen([sys.executable, \"-u\", \"model1.py\"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, env=env)),\n    (\"m2\", subprocess.Popen([sys.executable, \"-u\", \"model2.py\"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, env=env)),\n]\n\nsel = selectors.DefaultSelector()\nfor label, p in procs:\n    _stream_process_output(label, p, sel)\n\n# Stream outputs until both processes exit and pipes are drained\nalive = {label: p for label, p in procs}\nwhile alive or sel.get_map():\n    for key, _ in sel.select(timeout=0.25):\n        label = key.data\n        line = key.fileobj.readline()\n        if line:\n            print(f\"[{label}] {line}\", end=\"\", flush=True)\n        else:\n            try:\n                sel.unregister(key.fileobj)\n            except Exception:\n                pass\n    # Remove finished processes\n    for label, p in list(alive.items()):\n        if p.poll() is not None:\n            alive.pop(label, None)\n\nrc = {label: p.wait() for label, p in procs}\nprint(f\"2 models are ready. Return codes: {rc}\")","metadata":{"execution":{"iopub.execute_input":"2026-01-21T10:50:46.829252Z","iopub.status.busy":"2026-01-21T10:50:46.82882Z","iopub.status.idle":"2026-01-21T10:59:23.257099Z","shell.execute_reply":"2026-01-21T10:59:23.256245Z"},"papermill":{"duration":516.436054,"end_time":"2026-01-21T10:59:23.258641","exception":false,"start_time":"2026-01-21T10:50:46.822587","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nassert os.path.exists('submission1.csv')\nassert os.path.exists('submission2.csv')","metadata":{"execution":{"iopub.execute_input":"2026-01-21T10:59:23.480153Z","iopub.status.busy":"2026-01-21T10:59:23.479603Z","iopub.status.idle":"2026-01-21T10:59:23.492014Z","shell.execute_reply":"2026-01-21T10:59:23.49111Z"},"papermill":{"duration":0.123099,"end_time":"2026-01-21T10:59:23.493191","exception":true,"start_time":"2026-01-21T10:59:23.370092","status":"failed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport gc\ntorch.cuda.empty_cache()\ngc.collect()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip uninstall -y tensorflow protobuf\n!pip install \\\n  --no-index \\\n  --find-links /kaggle/input/transformers-hf \\\n  --force-reinstall \\\n  --no-deps \\\n  transformers huggingface_hub safetensors tokenizers accelerate","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2026-01-24T02:27:01.925274Z","iopub.execute_input":"2026-01-24T02:27:01.926048Z","iopub.status.idle":"2026-01-24T02:27:39.305065Z","shell.execute_reply.started":"2026-01-24T02:27:01.926013Z","shell.execute_reply":"2026-01-24T02:27:39.304283Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport sys\nimport subprocess\nimport selectors\n\n# In notebooks stdout is often buffered; force unbuffered Python in child processes\nenv = os.environ.copy()\nenv[\"PYTHONUNBUFFERED\"] = \"1\"\n\ndef _stream_process_output(label: str, proc: subprocess.Popen, selector: selectors.BaseSelector):\n    # Register stdout for multiplexed streaming\n    if proc.stdout is not None:\n        selector.register(proc.stdout, selectors.EVENT_READ, data=label)\n\nprocs = [\n    (\"m5\", subprocess.Popen([sys.executable, \"-u\", \"model5.py\"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, env=env)),\n    (\"m4\", subprocess.Popen([sys.executable, \"-u\", \"model4.py\"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, env=env)),\n]\n\nsel = selectors.DefaultSelector()\nfor label, p in procs:\n    _stream_process_output(label, p, sel)\n\n# Stream outputs until both processes exit and pipes are drained\nalive = {label: p for label, p in procs}\nwhile alive or sel.get_map():\n    for key, _ in sel.select(timeout=0.25):\n        label = key.data\n        line = key.fileobj.readline()\n        if line:\n            print(f\"[{label}] {line}\", end=\"\", flush=True)\n        else:\n            try:\n                sel.unregister(key.fileobj)\n            except Exception:\n                pass\n    # Remove finished processes\n    for label, p in list(alive.items()):\n        if p.poll() is not None:\n            alive.pop(label, None)\n\nrc = {label: p.wait() for label, p in procs}\nprint(f\"2 models are ready. Return codes: {rc}\")","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[],"trusted":true,"execution":{"iopub.status.busy":"2026-01-24T02:27:39.306630Z","iopub.execute_input":"2026-01-24T02:27:39.306893Z","iopub.status.idle":"2026-01-24T02:32:02.463953Z","shell.execute_reply.started":"2026-01-24T02:27:39.306865Z","shell.execute_reply":"2026-01-24T02:32:02.463306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert os.path.exists('submission5.csv')\nassert os.path.exists('submission4.csv')","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport gc\ntorch.cuda.empty_cache()\ngc.collect()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model3 = subprocess.Popen(['python', 'model3.py'])\nmodel3.wait()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert os.path.exists('submission3.csv')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport gc\ntorch.cuda.empty_cache()\ngc.collect()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"blend = subprocess.Popen(['python', 'blend.py'])\nblend.wait()","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"assert os.path.exists('submission.csv')","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!ls /kaggle/working","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\npd.read_csv('submission.csv')","metadata":{"papermill":{"duration":null,"end_time":null,"exception":null,"start_time":null,"status":"pending"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{},"outputs":[],"execution_count":null}]}