{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":64733,"databundleVersionId":8171035,"sourceType":"competition"},{"sourceId":8594688,"sourceType":"datasetVersion","datasetId":5141512}],"dockerImageVersionId":30699,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Introduction\n\nThis solution strongly based on the baselines notebook (Sentinel+Landsat+Bioclim). The main differences are replacing the time series cubes by a tabular representation, removing the most rare species and optimizing a threshold rather than returning 25 species.\n\n\n\nAll credit and thanks to the following notebooks and their owners:\n\nTo picekl for a strong baseline - https://www.kaggle.com/code/picekl/sentinel-landsat-bioclim-baseline-0-31626\n\nTo GO BYEONGGEON for example of dealing with tabular data - https://www.kaggle.com/code/gobyeonggeon/preprocess-visualize-spatial-data-eda-xgb","metadata":{"execution":{"iopub.status.busy":"2024-05-01T13:30:07.053659Z","iopub.execute_input":"2024-05-01T13:30:07.054038Z","iopub.status.idle":"2024-05-01T13:30:07.058148Z","shell.execute_reply.started":"2024-05-01T13:30:07.054008Z","shell.execute_reply":"2024-05-01T13:30:07.057269Z"}}},{"cell_type":"code","source":"import os\nimport torch\nimport tqdm\nimport numpy as np\nimport pandas as pd\nimport torchvision.models as models\nimport torchvision.transforms as transforms\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nfrom sklearn.metrics import precision_recall_fscore_support","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:07.29831Z","start_time":"2024-04-30T21:25:05.354584Z"},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:06:57.259571Z","iopub.execute_input":"2024-05-06T09:06:57.259928Z","iopub.status.idle":"2024-05-06T09:07:04.755868Z","shell.execute_reply.started":"2024-05-06T09:06:57.259898Z","shell.execute_reply":"2024-05-06T09:07:04.754947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport os\n\nimport matplotlib.pyplot as plt\nimport imageio.v3 as  imageio\nimport matplotlib.image as mpimg\nfrom PIL import Image\n\nimport imageio.v3 as  imageio\nimport albumentations as A\n\nfrom albumentations.pytorch import ToTensorV2\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch import nn\nfrom tqdm.notebook import tqdm\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.model_selection import train_test_split\n\nfrom sklearn.model_selection import KFold\nimport torch.nn.functional as F\nfrom torchmetrics import F1Score\nfrom sklearn.preprocessing import MinMaxScaler\n\nimport torch\nimport timm\nimport glob\nimport torchmetrics\nimport time\nimport psutil\nimport os\nimport math\nimport gc\nimport tqdm","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare custom dataset loader\n","metadata":{}},{"cell_type":"code","source":"train_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-train-bioclimatic_monthly/\"\ntrain_metadata_path = \"/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_train.csv\"\ntrain_metadata = pd.read_csv(train_metadata_path)\n\ntest_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-test-bioclimatic_monthly/\"\ntest_metadata_path = \"/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_test.csv\"\ntest_metadata = pd.read_csv(test_metadata_path)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_features = pd.read_csv('/kaggle/input/feature-vectors/train_merged.csv', dtype={'speciesId':int})\ntest_features = pd.read_csv('/kaggle/input/feature-vectors/test_merged.csv', dtype={'speciesId':int})","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FEATURE_COLS = train_features.columns[1:]\nN_FEATURES = len(FEATURE_COLS)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scaler = StandardScaler()\nscaler.fit(train_features.loc[:, FEATURE_COLS].values)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ntrain_metadata['speciesId'] = train_metadata['speciesId'].astype(int)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique, counts = np.unique(train_metadata.speciesId.values, return_counts=True)\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(unique))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"new_unique, new_counts = [], []\nfor u, c in zip(unique, counts):\n    if c > 10:\n        new_unique.append(u)\n        new_counts.append(c)\nunique = np.array(new_unique)\ncounts = np.array(new_counts)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(unique))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_classes = len(unique)\nnum_surveys = len(np.unique(train_metadata.surveyId.values))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"species_dict = {}\nfor i in range(num_classes):\n    species_dict[unique[i]] = i","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\n\ndef construct_patch_path(data_path, survey_id):\n    \"\"\"Construct the patch file path based on plot_id as './CD/AB/XXXXABCD.jpeg'\"\"\"\n    path = data_path\n    for d in (str(survey_id)[-2:], str(survey_id)[-4:-2]):\n        path = os.path.join(path, d)\n\n    path = os.path.join(path, f\"{survey_id}.jpeg\")\n\n    return path\n\nclass TrainDataset(Dataset):\n    def __init__(self, bioclim_data_dir, landsat_data_dir, sentinel_data_dir, metadata, features, transform=None):\n        self.transform = transform\n        self.sentinel_transform = transform = A.Compose([\n            A.Rotate(limit=(-90, 90)),\n            A.RandomBrightnessContrast(brightness_limit=(-0.1, 0.1), contrast_limit=(-0.1, 0.1), p=0.3),\n            A.Normalize(mean=(0.5, 0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5, 0.5), max_pixel_value=1),\n            ToTensorV2(),\n        ])\n      \n        self.bioclim_data_dir = bioclim_data_dir\n        self.landsat_data_dir = landsat_data_dir\n        self.sentinel_data_dir = sentinel_data_dir\n        self.metadata = metadata\n        self.metadata = self.metadata.dropna(subset=\"speciesId\").reset_index(drop=True)\n        self.metadata['speciesId'] = self.metadata['speciesId'].astype(int)\n        self.features = np.array(features, dtype=np.float32)\n        self.label_dict = self.metadata.groupby('surveyId')['speciesId'].apply(list).to_dict()\n        \n        self.metadata = self.metadata.drop_duplicates(subset=\"surveyId\").reset_index(drop=True)\n\n    def __len__(self):\n        return len(self.metadata)\n\n    def __getitem__(self, idx):\n        \n        survey_id = self.metadata.surveyId[idx]\n        \n        landsat_sample = torch.nan_to_num(torch.load(os.path.join(self.landsat_data_dir, f\"GLC24-PA-train-landsat-time-series_{survey_id}_cube.pt\")))\n        bioclim_sample = torch.nan_to_num(torch.load(os.path.join(self.bioclim_data_dir, f\"GLC24-PA-train-bioclimatic_monthly_{survey_id}_cube.pt\")))\n\n        rgb_sample = np.array(Image.open(construct_patch_path(self.sentinel_data_dir, survey_id)))\n        nir_sample = np.array(Image.open(construct_patch_path(self.sentinel_data_dir.replace(\"rgb\", \"nir\").replace(\"RGB\", \"NIR\"), survey_id)))\n        sentinel_sample = np.concatenate((rgb_sample, nir_sample[...,None]), axis=2)\n\n        species_ids = self.label_dict.get(survey_id, [])  # Get list of species IDs for the survey ID\n        label = torch.zeros(num_classes)  # Initialize label tensor\n        for species_id in species_ids:\n            label_id = species_id\n            if label_id in species_dict.keys():\n                label[species_dict[label_id]] = 1 # Set the corresponding class index to 1 for each species\n        \n        if isinstance(landsat_sample, torch.Tensor):\n            landsat_sample = landsat_sample.permute(1, 2, 0)  # Change tensor shape from (C, H, W) to (H, W, C)\n            landsat_sample = landsat_sample.numpy()  # Convert tensor to numpy array\n            \n        if isinstance(bioclim_sample, torch.Tensor):\n            bioclim_sample = bioclim_sample.permute(1, 2, 0)  # Change tensor shape from (C, H, W) to (H, W, C)\n            bioclim_sample = bioclim_sample.numpy()  # Convert tensor to numpy array   \n        \n        if self.transform:\n            landsat_sample = self.transform(landsat_sample)\n            bioclim_sample = self.transform(bioclim_sample)\n            sentinel_sample = self.sentinel_transform(image=sentinel_sample)['image']\n        feature = self.features[idx]\n\n        return landsat_sample, bioclim_sample, sentinel_sample, feature, label, survey_id\n\nclass TestDataset(TrainDataset):\n    def __init__(self, bioclim_data_dir, landsat_data_dir, sentinel_data_dir, metadata, features, transform=None):\n        self.transform = transform\n        self.sentinel_transform = transform = A.Compose([\n            A.Normalize(mean=(0.5, 0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5, 0.5), max_pixel_value=1),\n            ToTensorV2(),\n        ])\n      \n        self.bioclim_data_dir = bioclim_data_dir\n        self.landsat_data_dir = landsat_data_dir\n        self.sentinel_data_dir = sentinel_data_dir\n        self.metadata = metadata\n        self.features = np.array(features, dtype=np.float32)\n        \n    def __getitem__(self, idx):\n        \n        survey_id = self.metadata.surveyId[idx]\n        landsat_sample = torch.nan_to_num(torch.load(os.path.join(self.landsat_data_dir, f\"GLC24-PA-test-landsat_time_series_{survey_id}_cube.pt\")))\n        bioclim_sample = torch.nan_to_num(torch.load(os.path.join(self.bioclim_data_dir, f\"GLC24-PA-test-bioclimatic_monthly_{survey_id}_cube.pt\")))\n        \n        rgb_sample = np.array(Image.open(construct_patch_path(self.sentinel_data_dir, survey_id)))\n        nir_sample = np.array(Image.open(construct_patch_path(self.sentinel_data_dir.replace(\"rgb\", \"nir\").replace(\"RGB\", \"NIR\"), survey_id)))\n        sentinel_sample = np.concatenate((rgb_sample, nir_sample[...,None]), axis=2)\n\n        if isinstance(landsat_sample, torch.Tensor):\n            landsat_sample = landsat_sample.permute(1, 2, 0)  # Change tensor shape from (C, H, W) to (H, W, C)\n            landsat_sample = landsat_sample.numpy()  # Convert tensor to numpy array\n            \n        if isinstance(bioclim_sample, torch.Tensor):\n            bioclim_sample = bioclim_sample.permute(1, 2, 0)  # Change tensor shape from (C, H, W) to (H, W, C)\n            bioclim_sample = bioclim_sample.numpy()  # Convert tensor to numpy array   \n        \n        if self.transform:\n            landsat_sample = self.transform(landsat_sample)\n            bioclim_sample = self.transform(bioclim_sample)\n            sentinel_sample = self.sentinel_transform(image=sentinel_sample)['image']\n        feature = self.features[idx]\n        \n        return landsat_sample, bioclim_sample, sentinel_sample, feature, survey_id","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:32.627928Z","start_time":"2024-04-30T21:25:32.612131Z"},"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:07:04.757772Z","iopub.execute_input":"2024-05-06T09:07:04.758324Z","iopub.status.idle":"2024-05-06T09:07:04.78305Z","shell.execute_reply.started":"2024-05-06T09:07:04.758293Z","shell.execute_reply":"2024-05-06T09:07:04.782005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Load metadata and prepare data loaders","metadata":{}},{"cell_type":"code","source":"# Dataset and DataLoader\nbatch_size = 128\ntransform = transforms.Compose([\n    transforms.ToTensor()\n])\n\n# Load Training metadata\ntrain_landsat_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-train-landsat_time_series/\"\ntrain_bioclim_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-train-bioclimatic_monthly/\"\ntrain_sentinel_data_path=\"/kaggle/input/geolifeclef-2024/PA_Train_SatellitePatches_RGB/pa_train_patches_rgb/\"\ntrain_metadata_path = \"/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_train.csv\"\n\n# train_metadata = pd.read_csv(train_metadata_path)\ndataset_alpine = TrainDataset(train_bioclim_data_path, train_landsat_data_path, train_sentinel_data_path, train_metadata, scaler.transform(train_features.loc[:, FEATURE_COLS].values), transform=transform)\ntrain_loader = DataLoader(dataset_alpine, batch_size=batch_size, shuffle=True, num_workers=4)\n\n# Load Test metadata\ntest_landsat_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-test-landsat_time_series/\"\ntest_bioclim_data_path = \"/kaggle/input/geolifeclef-2024/TimeSeries-Cubes/TimeSeries-Cubes/GLC24-PA-test-bioclimatic_monthly/\"\ntest_sentinel_data_path = \"/kaggle/input/geolifeclef-2024/PA_Test_SatellitePatches_RGB/pa_test_patches_rgb/\"\ntest_metadata_path = \"/kaggle/input/geolifeclef-2024/GLC24_PA_metadata_test.csv\"\n\n# test_metadata = pd.read_csv(test_metadata_path)\ntest_dataset = TestDataset(test_bioclim_data_path, test_landsat_data_path, test_sentinel_data_path, test_metadata, scaler.transform(test_features.loc[:, FEATURE_COLS].values), transform=transform)\ntest_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4)","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:34.532017Z","start_time":"2024-04-30T21:25:32.615562Z"},"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:07:04.784374Z","iopub.execute_input":"2024-05-06T09:07:04.784747Z","iopub.status.idle":"2024-05-06T09:07:10.575053Z","shell.execute_reply.started":"2024-05-06T09:07:04.784703Z","shell.execute_reply":"2024-05-06T09:07:10.574175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define and initialize a Multimodal Model\n","metadata":{}},{"cell_type":"code","source":"import torch.nn.functional as F\n\nclass MultimodalEnsemble(nn.Module):\n    def __init__(self, num_classes):\n        super(MultimodalEnsemble, self).__init__()\n        \n        self.landsat_norm = nn.LayerNorm([6,4,21])\n        self.landsat_model = models.resnet18(weights=None)\n        # Modify the first convolutional layer to accept 6 channels instead of 3\n        self.landsat_model.conv1 = nn.Conv2d(6, 64, kernel_size=3, stride=1, padding=1, bias=False)\n        self.landsat_model.maxpool = nn.Identity()\n        self.landsat_dropout = nn.Dropout(0.1)\n        \n        self.bioclim_norm = nn.LayerNorm([4,19,12])\n        self.bioclim_model = models.resnet18(weights=None)  \n        # Modify the first convolutional layer to accept 4 channels instead of 3\n        self.bioclim_model.conv1 = nn.Conv2d(4, 64, kernel_size=3, stride=1, padding=1, bias=False)\n        self.bioclim_model.maxpool = nn.Identity()\n        self.bioclim_dropout = nn.Dropout(0.1)\n        \n        self.sentinel_model = models.swin_t(weights=\"IMAGENET1K_V1\")\n        # Modify the first layer to accept 4 channels instead of 3\n        self.sentinel_model.features[0][0] = nn.Conv2d(4, 96, kernel_size=(4, 4), stride=(4, 4))\n        self.sentinel_model.head = nn.Identity()\n        self.sentinel_dropout = nn.Dropout(0.1)\n        \n        self.features = nn.Sequential(\n            nn.Linear(N_FEATURES,N_FEATURES),\n            nn.GELU(),\n            nn.Dropout(0.1),\n            nn.Linear(N_FEATURES,1000),\n            nn.GELU(),\n            nn.Dropout(0.1),\n            nn.Linear(1000, 1000),\n        )\n        self.feat_dropout = nn.Dropout(0.1)\n        \n        self.ln1 = nn.LayerNorm(1000)\n        self.ln2 = nn.LayerNorm(1000)\n        self.ln3 = nn.LayerNorm(1000)\n        self.label = nn.Sequential(\n            nn.Linear(2768, 4096),\n            nn.GELU(),\n            nn.Dropout(0.1),\n            nn.Linear(4096, num_classes),\n            nn.GELU(),\n            nn.Dropout(0.1),\n            nn.Linear(num_classes, num_classes),\n        )\n        \n    def forward(self, x, y, z, feat):\n        \n#         x = self.landsat_norm(x)\n#         x = self.landsat_model(x)\n#         x = self.landsat_dropout(self.ln1(x))\n        \n        y = self.bioclim_norm(y)\n        y = self.bioclim_model(y)\n        y = self.bioclim_dropout(self.ln2(y))\n        \n        z = self.sentinel_dropout(self.sentinel_model(z))\n        \n        feat = self.feat_dropout(self.ln3(self.features(feat)))\n        \n        xyz = torch.cat((y, z, feat), dim=1)\n        out = self.label(xyz)\n        return out","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:31.014067Z","start_time":"2024-04-30T21:25:31.01006Z"},"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:08:57.920217Z","iopub.execute_input":"2024-05-06T09:08:57.920574Z","iopub.status.idle":"2024-05-06T09:08:57.933714Z","shell.execute_reply.started":"2024-05-06T09:08:57.920548Z","shell.execute_reply":"2024-05-06T09:08:57.932748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def set_seed(seed):\n    # Set seed for Python's built-in random number generator\n    torch.manual_seed(seed)\n    # Set seed for numpy\n    np.random.seed(seed)\n    # Set seed for CUDA if available\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed_all(seed)\n        # Set cuDNN's random number generator seed for deterministic behavior\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False\n\nset_seed(69)","metadata":{"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:08:58.387461Z","iopub.execute_input":"2024-05-06T09:08:58.387818Z","iopub.status.idle":"2024-05-06T09:08:58.395133Z","shell.execute_reply.started":"2024-05-06T09:08:58.387791Z","shell.execute_reply":"2024-05-06T09:08:58.394142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check if cuda is available\ndevice = torch.device(\"cpu\")\n\nif torch.cuda.is_available():\n    device = torch.device(\"cuda\")\n    print(\"DEVICE = CUDA\")\n\n# num_classes = 11255 # Number of all unique classes within the PO and PA data.\nmodel = MultimodalEnsemble(num_classes).to(device)","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:31.611823Z","start_time":"2024-04-30T21:25:31.607373Z"},"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:08:58.91641Z","iopub.execute_input":"2024-05-06T09:08:58.917225Z","iopub.status.idle":"2024-05-06T09:09:01.804079Z","shell.execute_reply.started":"2024-05-06T09:08:58.917179Z","shell.execute_reply":"2024-05-06T09:09:01.803256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training Loop\n","metadata":{}},{"cell_type":"code","source":"# Hyperparameters\nlearning_rate = 8e-5\nnum_epochs = 12\npositive_weigh_factor = 1.0\n\noptimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)\nscheduler = CosineAnnealingLR(optimizer, T_max=25, verbose=True)","metadata":{"ExecuteTime":{"end_time":"2024-04-30T21:25:32.181927Z","start_time":"2024-04-30T21:25:32.177073Z"},"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:09:04.721477Z","iopub.execute_input":"2024-05-06T09:09:04.721855Z","iopub.status.idle":"2024-05-06T09:09:04.730311Z","shell.execute_reply.started":"2024-05-06T09:09:04.721824Z","shell.execute_reply":"2024-05-06T09:09:04.729183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Training for {num_epochs} epochs started.\")\n\nfor epoch in range(num_epochs):\n    model.train()\n    \n    for batch_idx, (data1, data2, data3, feat, targets, _) in enumerate(train_loader):\n\n        data1 = data1.to(device)\n        data2 = data2.to(device)\n        data3 = data3.to(device)\n        feat = feat.to(device)\n        targets = targets.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(data1, data2, data3, feat)\n\n        pos_weight = targets*positive_weigh_factor  \n        criterion = torch.nn.BCEWithLogitsLoss(pos_weight=pos_weight)\n        loss = criterion(outputs, targets)\n\n        loss.backward()\n        optimizer.step()\n\n        if batch_idx % 278 == 0:\n            print(f\"Epoch {epoch+1}/{num_epochs}, Batch {batch_idx}/{len(train_loader)}, Loss: {loss.item()}\")\n\n    scheduler.step()\n    print(\"Scheduler:\",scheduler.state_dict())\n\n# Save the trained model\nmodel.eval()\ntorch.save(model.state_dict(), \"multimodal-model.pth\")","metadata":{"ExecuteTime":{"start_time":"2024-04-30T21:25:34.536634Z"},"collapsed":false,"is_executing":true,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:09:08.277588Z","iopub.execute_input":"2024-05-06T09:09:08.277946Z","iopub.status.idle":"2024-05-06T10:34:36.512218Z","shell.execute_reply.started":"2024-05-06T09:09:08.277917Z","shell.execute_reply":"2024-05-06T10:34:36.51107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\nprint(\"Done\")","metadata":{"execution":{"iopub.status.busy":"2024-05-06T11:43:07.217927Z","iopub.execute_input":"2024-05-06T11:43:07.218737Z","iopub.status.idle":"2024-05-06T11:43:07.548284Z","shell.execute_reply.started":"2024-05-06T11:43:07.218705Z","shell.execute_reply":"2024-05-06T11:43:07.546977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Loop\n\nAgain, nothing special, just a standard inference.","metadata":{}},{"cell_type":"code","source":"with torch.no_grad():\n    surveys = []\n    top_k_indices = None\n    for batch_idx, (data1, data2, data3, feat, surveyID) in enumerate(test_loader):\n\n        data1 = data1.to(device)\n        data2 = data2.to(device)\n        data3 = data3.to(device)\n        feat = feat.to(device)\n\n        outputs = model(data1, data2, data3, feat)\n        predictions = torch.sigmoid(outputs).cpu().numpy()\n        \n        top_25 = []\n        for el in predictions:\n            answ = np.array(list(np.nonzero(el>0.18)[0]))\n            if len(answ) < 4:\n                answ = np.array(np.argsort(-el)[:4])\n            top_25.append(answ)\n\n        \n        top_25 = np.array([unique[el] for el in top_25], dtype=object)\n        if top_k_indices is None:\n            top_k_indices = top_25\n        else:\n            top_k_indices = np.concatenate((top_k_indices, top_25), axis=0)\n\n        surveys.extend(surveyID.cpu().numpy())","metadata":{"collapsed":false,"jupyter":{"outputs_hidden":false},"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:07:11.048053Z","iopub.status.idle":"2024-05-06T09:07:11.04851Z","shell.execute_reply.started":"2024-05-06T09:07:11.048283Z","shell.execute_reply":"2024-05-06T09:07:11.048302Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Save prediction file! ","metadata":{}},{"cell_type":"code","source":"data_concatenated = [' '.join(map(str, row)) for row in top_k_indices]\n\npd.DataFrame(\n    {'surveyId': surveys,\n     'predictions': data_concatenated,\n    }).to_csv(\"submission.csv\", index = False)","metadata":{"tags":[],"execution":{"iopub.status.busy":"2024-05-06T09:07:11.050549Z","iopub.status.idle":"2024-05-06T09:07:11.051316Z","shell.execute_reply.started":"2024-05-06T09:07:11.051034Z","shell.execute_reply":"2024-05-06T09:07:11.051053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}