{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":81000,"databundleVersionId":8812083,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","ExecuteTime":{"end_time":"2024-07-22T19:52:18.784834Z","start_time":"2024-07-22T19:52:18.434607Z"},"execution":{"iopub.status.busy":"2024-07-26T13:41:41.156957Z","iopub.execute_input":"2024-07-26T13:41:41.157348Z","iopub.status.idle":"2024-07-26T13:41:42.237904Z","shell.execute_reply.started":"2024-07-26T13:41:41.157319Z","shell.execute_reply":"2024-07-26T13:41:42.236724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Load Data & Config","metadata":{}},{"cell_type":"code","source":"import os\nimport tqdm\nimport datetime\n\n\n# If set to True -> only run with a small subset of the data\nDEBUG_MODE = False\n\nclass SubmissionException(Exception):\n    pass  # For raising errors related to the implementation of this submission\n\n\nCROPS = ('maize', 'wheat')\nMODES = ('train', 'test')\n\nFEATURES_TEMPORAL = {\n    # Time series data -- 240 columns reflecting daily values for 30 days before sowing and 210 days after.\n    'tas',       # Mean daily temperature\n    'tasmax',    # Max daily temperature\n    'tasmin',    # Min daily temperature\n    'pr',        # precipitation\n    'rsds',      # shortwave radiation\n}\n\nFEATURES_STATIC = {\n    # Static data\n    'soil_co2',  # crop, year, lon, lat, texture_class, real_year, co2, nitrogen\n    # dominant USDA soil texture class (constant over time), the ambient CO2 concentration (spatially constant), the planting date and the nitrogen application rate (constant over time)\n}\n\nFEATURES = set.union(FEATURES_TEMPORAL, FEATURES_STATIC)\n\n# Sowing date\nINDEX_SOW = 30  # days\n# Time series data length\nSEASON_LENGTH = 240  # days\n# Nr. of soil texture classes\nNUM_TEXTURE_CLASSES = 13  \n\nYEAR_TRAIN_MIN = 1982\nYEAR_TRAIN_MAX = 2020  # Inclusive\nYEAR_TEST_MIN = 2021\nYEAR_TEST_MAX = 2098\n\nPATH_INPUT = os.path.abspath(os.path.join(os.sep, 'kaggle', 'input', 'the-future-crop-challenge'))\n# PATH_INPUT = os.path.abspath(os.path.join(os.getcwd(), 'data'))  # For running the notebook locally\n\n\ndef load_data(crop: str, # Which crop\n              mode: str, # Which dataset (i.e. train/test)\n              take_subset: bool = False,  # If set to true, take a small subset of the data (for debugging purposes)\n             ) -> dict:\n    assert crop in CROPS\n    assert mode in MODES\n    \n    output = dict()\n    \n    for f in FEATURES:\n        path = os.path.join(PATH_INPUT, f'{f}_{crop}_{mode}.parquet')\n        df = pd.read_parquet(path)\n        \n        if f in FEATURES_TEMPORAL:  # Select only the time series data -- drop other columns\n            df = df[[str(i) for i in range(SEASON_LENGTH)]]\n        \n        output[f] = df\n        \n    if mode == 'train':\n        output['target'] = pd.read_parquet(os.path.join(PATH_INPUT, f'{mode}_solutions_{crop}.parquet'))\n    \n    # If required, only take a subset of the data for debugging purposes -- we don't really care which samples\n    if take_subset:\n        num_select = 100  # Take only 100 samples from the dataset\n        # Select which samples based on the index of some feature\n        ixs_selected = output[tuple(FEATURES)[0]].index[:num_select]\n        # Filter all dataframes\n        output = {\n            key: df.loc[ixs_selected] for key, df in output.items()\n        }\n        \n    for df in output.values():\n        df.sort_index(inplace=True)\n        \n    return output\n\n\"\"\"\n    Load all data\n\"\"\"\n\n# Load all available data for all crops\ncrop_data_train = {\n    crop: load_data(crop, 'train', take_subset=DEBUG_MODE) for crop in CROPS\n}\ncrop_data_test = {\n    crop: load_data(crop, 'test', take_subset=DEBUG_MODE) for crop in CROPS\n}\n\n# Separate data in features and targets (if available)\ncrop_features_train = {\n    crop: {\n        k: v for k, v in data.items() if k in FEATURES\n    } for crop, data in crop_data_train.items()\n}\ncrop_features_test = {\n    crop: {\n        k: v for k, v in data.items() if k in FEATURES\n    } for crop, data in crop_data_test.items()\n}\n\ncrop_targets_train = {\n    crop: data['target'] for crop, data in crop_data_train.items()\n}\n\n","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:12:55.447991Z","start_time":"2024-07-22T20:12:37.763428Z"},"execution":{"iopub.status.busy":"2024-07-26T13:41:42.239953Z","iopub.execute_input":"2024-07-26T13:41:42.240463Z","iopub.status.idle":"2024-07-26T13:44:35.259863Z","shell.execute_reply.started":"2024-07-26T13:41:42.240429Z","shell.execute_reply":"2024-07-26T13:44:35.258529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# crop_targets_train['wheat']\ncrop_features_train['wheat']['tas']\n# crop_features_train['wheat'].keys()","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:12:55.456963Z","start_time":"2024-07-22T20:12:55.451882Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:35.261595Z","iopub.execute_input":"2024-07-26T13:44:35.261959Z","iopub.status.idle":"2024-07-26T13:44:35.357192Z","shell.execute_reply.started":"2024-07-26T13:44:35.261926Z","shell.execute_reply":"2024-07-26T13:44:35.355368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"crop_features_train['wheat']['soil_co2']","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:12:55.467843Z","start_time":"2024-07-22T20:12:55.458043Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:35.361033Z","iopub.execute_input":"2024-07-26T13:44:35.361463Z","iopub.status.idle":"2024-07-26T13:44:35.387564Z","shell.execute_reply.started":"2024-07-26T13:44:35.361427Z","shell.execute_reply":"2024-07-26T13:44:35.386103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Compute Supplementary Features\n\nCode for computing growing degree days with optional vernalization factor for winter wheat","metadata":{}},{"cell_type":"code","source":"CROP_GDD_PARAMETERS = {\n    'maize': {\n        't_base': 10,  # Deg. C\n        't_upper': 30  # Deg. C\n    },  # Source: https://ndawn.ndsu.nodak.edu/help-corn-growing-degree-days.html\n    'wheat': {\n        't_base': 5.5,  # Deg. C\n        't_upper': 21,  # Deg. C\n    },  # Source: https://ndawn.ndsu.nodak.edu/help-wheat-growing-degree-days.html\n}\n\n# Winter wheat vernalization parameters are based on \"Climate change effects on wheat phenology depends on cultivar change\":\n# Vernalization Module was calibrated for winter wheat in germany\n# v unit is 0 for days with mean temperature below -4 deg. C or above 17 deg. C\n# v unit is 1 for days with mean temperature between 4 and 10 deg. C\n# v unit is linearly interpolated in missing segments\n# 30 units need to be accumulated for the vernalization factor to be 1\n\n\ndef func_vernalization_unit(x: np.ndarray) -> np.ndarray:\n    return np.interp(x, [-4, 4, 10, 17], [0, 1, 1, 0], left=0, right=0)\n\n\ndef func_vernalization_tres(x: np.ndarray) -> np.ndarray:\n    return np.interp(x, [0, 30], [0, 1], left=0, right=1)\n\n\ndef compute_gdd(crop: str,\n                df_tas: pd.DataFrame,\n                vernalization_factor: bool = True,\n                cumulative: bool = True,\n               ) -> pd.DataFrame:\n    t_base = CROP_GDD_PARAMETERS[crop]['t_base']\n    t_upper = CROP_GDD_PARAMETERS[crop]['t_upper']\n    \n    df_tas = df_tas.clip(upper=t_upper, inplace=False)\n    df_tas = df_tas - t_base\n    df_gdd = df_tas.clip(lower=0)\n    \n    if vernalization_factor and crop == 'wheat':  # Account for vernalization requirement\n        df_ver = func_vernalization_unit(df_tas).cumsum(axis=1)\n        df_ver = func_vernalization_tres(df_ver)\n        df_gdd = df_gdd * df_ver\n    \n    if cumulative:\n        df_gdd = df_gdd.cumsum(axis=1)\n\n    return df_gdd\n\n\n# Compute the cumulative growing degree days \nfor crop in CROPS:\n    crop_features_train[crop]['gdd'] = compute_gdd(crop, crop_features_train[crop]['tas'], cumulative=False, vernalization_factor=False)  # Set cumulative to True?\n    crop_features_test[crop]['gdd'] = compute_gdd(crop, crop_features_test[crop]['tas'], cumulative=False, vernalization_factor=False)\n\n","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:14:14.800987Z","start_time":"2024-07-22T20:14:11.627578Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:35.389427Z","iopub.execute_input":"2024-07-26T13:44:35.389931Z","iopub.status.idle":"2024-07-26T13:44:47.945620Z","shell.execute_reply.started":"2024-07-26T13:44:35.389884Z","shell.execute_reply":"2024-07-26T13:44:47.944441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define Dataset class","metadata":{}},{"cell_type":"code","source":"\n\nclass Dataset:\n    \n    fns_series = ('tasmax', 'tasmin', 'pr', 'rsds', 'gdd')\n    fns_static = ('co2', 'nitrogen', 'texture_class')\n    \n    def __init__(self, crop: str, mode: str):\n        assert crop in CROPS\n        assert mode in MODES\n        \n        self._crop = crop\n        self._mode = mode\n        \n        match self._mode:\n            case 'train':\n                self._data_series = {k: v for k, v in crop_features_train[crop].items() if k in self.fns_series}\n                self._data_static = {k: crop_features_train[crop]['soil_co2'][k] for k in self.fns_static}\n            case 'test':\n                self._data_series = {k: v for k, v in crop_features_test[crop].items() if k in self.fns_series}\n                self._data_static = {k: crop_features_test[crop]['soil_co2'][k] for k in self.fns_static}\n            case _:\n                raise SubmissionException(f'Unrecognized dataset mode: {mode}')\n\n        if mode == 'train':\n            self._targets = crop_targets_train[crop]\n        else:\n            self._targets = None\n            \n    def compute_norm_params(self, use_val_data: bool = False,) -> dict:\n        \n        if use_val_data:  # If set -> use both training and available validation data to compute mean/std\n            data_series = {\n                k: pd.concat([\n                    crop_features_train[self._crop][k],\n                    crop_features_test[self._crop][k],\n                ]) for k in self.fns_series\n                }\n            data_static = {\n                k: pd.concat([\n                    crop_features_train[self._crop]['soil_co2'][k],\n                    crop_features_test[self._crop]['soil_co2'][k],\n                ]) for k in self.fns_static\n            }\n        else:\n            data_series = {k: v for k, v in crop_features_train[crop].items() if k in self.fns_series}\n            data_static = {k: crop_features_train[crop]['soil_co2'][k] for k in self.fns_static}            \n            \n        clip_std = 0.1\n            \n        norm_params = {\n            **{\n                key: (data.values.mean(), max(clip_std, data.values.std())) for key, data in data_series.items()\n            },\n            **{\n                key: (data.values.mean(), max(clip_std, data.values.std())) for key, data in data_static.items()\n            },\n        }\n        \n        return norm_params\n    \n    def has_targets(self) -> bool:\n        return self._targets is not None\n    \n    def get_index(self) -> np.ndarray:\n        return self._data_static[self.fns_static[0]].index.values\n    \n    def _verify_data(self) -> None:\n        index = self._data_static[self.fns_static[0]].index\n        assert all([index.equals(df.index for df in {**self._data_static, **self._data_series}.values())])\n    \n    def __len__(self):\n        return len(self._data_static[self.fns_static[0]])\n    \n    def __getitem__(self, index) -> dict:\n        \n        sample = {\n            'series': {\n                key: self._data_series[key].iloc[index].values for key in self.fns_series\n            },\n            'static': {\n                key: self._data_static[key].iloc[index] for key in self.fns_static\n            },\n        }\n        \n        if self._targets is not None:\n            sample['target'] = self._targets.iloc[index].iloc[0]\n        \n        return sample\n        \n\n    ","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:14:14.808360Z","start_time":"2024-07-22T20:14:14.801996Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:47.947377Z","iopub.execute_input":"2024-07-26T13:44:47.948296Z","iopub.status.idle":"2024-07-26T13:44:47.967330Z","shell.execute_reply.started":"2024-07-26T13:44:47.948258Z","shell.execute_reply":"2024-07-26T13:44:47.966052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define PyTorch Dataset Wrapper\nWraps around the dataset class such that data are cast to PyTorch tensors. Compatible with PyTorch dataloaders","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.data\n\ntorch.set_default_dtype(torch.float32)\n\nDISABLE_CUDA = False\nTORCH_DEVICE = 'cuda' if torch.cuda.is_available() and not DISABLE_CUDA else 'cpu'\n\nTORCH_DEVICE","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:14:14.816217Z","start_time":"2024-07-22T20:14:14.809070Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:47.968663Z","iopub.execute_input":"2024-07-26T13:44:47.969011Z","iopub.status.idle":"2024-07-26T13:44:50.579214Z","shell.execute_reply.started":"2024-07-26T13:44:47.968980Z","shell.execute_reply":"2024-07-26T13:44:50.577910Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n\ndef batch_tensors(*ts):\n    return torch.cat([t.unsqueeze(0) for t in ts], dim=0)\n\n\nclass TorchDatasetWrapper(torch.utils.data.Dataset):\n    \n    def __init__(self, dataset: Dataset):\n        super().__init__()\n        \n        self._dataset = dataset\n        \n    def __len__(self):\n        return len(self._dataset)\n    \n    def __getitem__(self, index) -> dict:\n        \n        sample = self._dataset[index]\n        \n        sample = self._cast_to_tensor(sample)\n        \n        return sample\n    \n    def _cast_to_tensor(self, sample) -> dict:\n        \n        tensor_sample = {\n            'series': {\n                k: torch.tensor(v, device=TORCH_DEVICE, dtype=torch.float32) for k, v in sample['series'].items()\n            },\n            'static': {\n                k: torch.tensor(v, device=TORCH_DEVICE, dtype=torch.float32) for k, v in sample['static'].items()\n            },\n        }\n        \n        if 'texture_class' in tensor_sample['static'].keys():\n            tensor_sample['static']['texture_class'] = F.one_hot(tensor_sample['static']['texture_class'].to(dtype=torch.int64) - 1, num_classes=NUM_TEXTURE_CLASSES)\n            \n        if 'target' in sample:\n            tensor_sample['target'] = torch.tensor(sample['target'], device=TORCH_DEVICE, dtype=torch.float32)\n        \n        return tensor_sample\n    \n    def compute_norm_params(self) -> dict:\n        return self._dataset.compute_norm_params()\n    \n    @classmethod\n    def collate_fn(cls, samples: list) -> dict:\n        # Define how multiple data samples should be combined to form batches\n        \n        data_series = {\n            key: batch_tensors(*[sample['series'][key] for sample in samples]) for key in Dataset.fns_series\n        }\n        data_static = {\n            key: batch_tensors(*[sample['static'][key] for sample in samples]) for key in Dataset.fns_static\n        }\n        \n        batch = {\n            'series': data_series,\n            'static': data_static,\n        }\n        \n        if 'target' in samples[0]:\n            data_target = batch_tensors(*[sample['target'] for sample in samples])\n            batch['target'] = data_target\n        \n        return batch\n    ","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:15:29.379913Z","start_time":"2024-07-22T20:15:29.375229Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:50.580879Z","iopub.execute_input":"2024-07-26T13:44:50.581461Z","iopub.status.idle":"2024-07-26T13:44:50.595711Z","shell.execute_reply.started":"2024-07-26T13:44:50.581420Z","shell.execute_reply":"2024-07-26T13:44:50.594260Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define CNN Model","metadata":{}},{"cell_type":"code","source":"class CNNModel(nn.Module):\n    \n    def __init__(self):\n        super().__init__()\n        \n        self._norm_params = None\n        \n        in_channels = 5\n        num_static = 2 + NUM_TEXTURE_CLASSES\n\n        self._cnn_model = nn.Sequential(\n            nn.Conv1d(in_channels=in_channels,\n                      out_channels=16,\n                      kernel_size=7,\n                     ),\n            \n            nn.AvgPool1d(kernel_size=3,),\n            nn.ReLU(),\n            \n            nn.Conv1d(in_channels=16,\n                      out_channels=32,\n                      kernel_size=7,\n                     ),\n\n            nn.AvgPool1d(kernel_size=3,),\n            nn.ReLU(),\n            \n            nn.Conv1d(in_channels=32,\n                      out_channels=64,\n                      kernel_size=5,\n                     ),\n            \n            nn.AvgPool1d(kernel_size=3,),\n            nn.ReLU(),\n\n            nn.Conv1d(in_channels=64,\n                      out_channels=64,\n                      kernel_size=3,\n                     ),\n            nn.ReLU(),\n        )\n        \n        self._lin_1 = nn.Linear(256 + num_static, 64)\n        self._lin_2 = nn.Linear(64, 1)\n        \n        \n    def forward(self, xs) -> tuple:\n        \n        xs = self._norm_sample(xs)\n        \n        fns_series = ('tasmax', 'tasmin', 'pr', 'rsds', 'gdd')\n        fns_static = ('co2', 'nitrogen', 'texture_class')\n        \n        ts = torch.cat([xs['series'][key].unsqueeze(1) for key in fns_series], dim=1)\n        \n        batch_size = ts.size(0)\n\n        ts = self._cnn_model(ts)\n        \n        ts = torch.cat([\n            ts.view(batch_size, -1),\n            *{xs['static'][key].view(batch_size, -1) for key in fns_static},\n        ], dim=1)\n        \n        ts = self._lin_1(ts)\n        ts = F.relu(ts)\n        ts = self._lin_2(ts)\n        ts = F.relu(ts)\n        \n        return ts.view(-1), {}\n    \n    \n    def _norm_sample(self, xs: dict) -> dict:\n        assert self._norm_params is not None\n        \n        # Explicitly list features that should be normalized\n        fns_series = ('tasmax', 'tasmin', 'pr', 'rsds', 'gdd')\n        fns_static = ('co2', 'nitrogen')\n        \n        data_series = xs['series']\n        data_static = xs['static']\n        \n        data_series_norm = dict()\n        data_static_norm = dict()\n        \n        for key, data in data_series.items():\n            if key not in fns_series:\n                data_series_norm[key] = data\n                continue\n            mean, std = self._norm_params[key]\n            data_series_norm[key] = (data - mean) / std\n\n        for key, data in data_static.items():\n            if key not in fns_static:\n                data_static_norm[key] = data\n                continue\n            mean, std = self._norm_params[key]\n            data_static_norm[key] = (data - mean) / std\n        \n        xs['series'] = data_series_norm\n        xs['static'] = data_static_norm\n                \n        return xs\n    \n    @classmethod\n    def fit(cls,\n            model_name: str,\n            dataset: Dataset,\n            num_epochs: int = 1,\n            batch_size: int = 1,\n            scheduler_step_size: int = None,\n            scheduler_decay: float = 0.5,\n            lr: float = 1e-3,\n            weight_decay: float = 1e-4,\n            model_kwargs: dict = None,\n            model: 'CNNModel' = None,\n            print_period: int = 1,\n            save_period: int = 1,\n           ) -> tuple:\n        \n        scheduler_step_size = scheduler_step_size or num_epochs\n\n        model_kwargs = model_kwargs or dict()\n        model = (model or CNNModel(**model_kwargs)).to(TORCH_DEVICE)\n        \n        model._norm_params = dataset.compute_norm_params(use_val_data=False)\n        \n        dataset_wrapped = TorchDatasetWrapper(dataset)\n        \n        dataloader = torch.utils.data.DataLoader(dataset_wrapped,\n                                                 batch_size=batch_size,\n                                                 collate_fn=dataset_wrapped.collate_fn,\n                                                 shuffle=True,\n                                                )\n                \n        optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay,)\n        \n        scheduler = torch.optim.lr_scheduler.StepLR(optimizer,\n                                                    step_size=scheduler_step_size,\n                                                    gamma=scheduler_decay,\n                                                    )\n                \n        time_start = datetime.datetime.now()\n\n        for epoch_nr in range(1, num_epochs + 1):\n            \n            model.train()\n            \n            verbose = (epoch_nr % print_period) == 0\n            save_checkpoint = (epoch_nr % save_period) == 0\n            \n            epoch_iter = tqdm.tqdm(dataloader, total=len(dataloader)) if verbose else dataloader\n            \n            losses = []\n            \n            for xs in epoch_iter:\n                \n                optimizer.zero_grad()\n                                   \n                loss, _, _ = model.loss(xs)\n                loss.backward()\n                \n                optimizer.step()\n                                \n                losses.append(loss.item())\n                loss_mean = sum(losses) / len(losses)\n                \n                lr = scheduler.get_last_lr()[0]\n                \n                if verbose:\n                    epoch_iter.set_description(\n                        f'{cls.__name__} training epoch [{epoch_nr:6d}/{num_epochs}] | lr: {lr:.7f} | Batch Loss: {loss.item():8.5f} | Loss Mean: {loss_mean:8.5f}',\n                    )\n                \n                if save_checkpoint:\n                    torch.save(model, f'{model_name}.pt')\n                    torch.save(model.state_dict(), f'{model_name}_state.pth')\n            \n            scheduler.step()\n\n        time_end = datetime.datetime.now()\n        \n        return model, {} \n                                   \n    def loss(self, xs: dict) -> tuple:\n        \n        ys_true = xs['target']\n        ys_pred, _ = self(xs)\n        \n        loss = F.mse_loss(ys_pred, ys_true)\n        \n        return loss, ys_pred, {}\n        \n    \n    def predict(self,\n                dataset: Dataset,\n                batch_size: int = 1,\n               ) -> tuple:\n        \n        dataset_wrapped = TorchDatasetWrapper(dataset)\n        \n        dataloader = torch.utils.data.DataLoader(dataset_wrapped,\n                                                 batch_size=batch_size,\n                                                 collate_fn=dataset_wrapped.collate_fn,\n                                                 shuffle=False,\n                                                )\n        \n        self.eval()\n        \n        ys_pred = []\n        \n        with torch.no_grad():\n            for xs in tqdm.tqdm(dataloader, total=len(dataloader)):\n                ys_pred_batch, _ = self(xs)\n                ys_pred.append(ys_pred_batch)\n        \n        ys_pred = torch.cat(ys_pred, dim=0)\n        \n        ys_pred = ys_pred.detach().cpu().numpy()\n        \n        return ys_pred, pd.DataFrame({'ID': dataset.get_index(), 'yield': ys_pred}), {}\n                ","metadata":{"ExecuteTime":{"end_time":"2024-07-22T20:16:41.460021Z","start_time":"2024-07-22T20:16:41.449499Z"},"execution":{"iopub.status.busy":"2024-07-26T13:44:50.597719Z","iopub.execute_input":"2024-07-26T13:44:50.598790Z","iopub.status.idle":"2024-07-26T13:44:50.629934Z","shell.execute_reply.started":"2024-07-26T13:44:50.598728Z","shell.execute_reply":"2024-07-26T13:44:50.628587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Fit Models","metadata":{}},{"cell_type":"code","source":"\"\"\"main\"\"\"\n\ndataset_wheat_train = Dataset('wheat', 'train')\ndataset_wheat_test = Dataset('wheat', 'test')\n\nFIT_MODEL = True\nif FIT_MODEL:\n\n    model_wheat, _ = CNNModel.fit('model_wheat',\n                                  dataset_wheat_train,\n                                  num_epochs=500 if not DEBUG_MODE else 1,\n                                  batch_size=128,\n                                  scheduler_step_size=100,\n                                  scheduler_decay=0.9,\n                                  print_period=1,\n                                  save_period=10,\n                                  )\n\n    # torch.save(model_wheat, 'model_wheat.pt')\n    # torch.save(model_wheat.state_dict(), 'model_state_wheat.pth')\n","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2024-07-26T13:44:50.634017Z","iopub.execute_input":"2024-07-26T13:44:50.634985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"main\"\"\"\n\ndataset_maize_train = Dataset('maize', 'train')\ndataset_maize_test = Dataset('maize', 'test')\n\nFIT_MODEL = True\nif FIT_MODEL:\n\n    model_maize, _ = CNNModel.fit('model_maize',\n                                  dataset_maize_train,\n                                  num_epochs=500 if not DEBUG_MODE else 1,\n                                  batch_size=128,\n                                  scheduler_step_size=100,\n                                  scheduler_decay=0.9,\n                                  print_period=1,\n                                  save_period=10,\n                                  )\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create Submission","metadata":{}},{"cell_type":"code","source":"_, df_wheat, _ = model_wheat.predict(dataset_wheat_test,\n                                     batch_size=1028,\n                                    )\ndf_wheat.to_csv('submission_wheat.csv', index=False, sep=',')\n\n# %cd /kaggle/working\n# from IPython.display import FileLink\n# FileLink('model_state_wheat.pth')","metadata":{"ExecuteTime":{"end_time":"2024-07-24T02:59:44.205500Z","start_time":"2024-07-24T02:57:02.494521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_, df_maize, _ = model_maize.predict(dataset_maize_test,\n                                     batch_size=1028,\n                                    )\ndf_maize.to_csv('submission_maize.csv', index=False, sep=',')\n\n# %cd /kaggle/working\n# from IPython.display import FileLink\n# FileLink('model_state_maize.pth')","metadata":{"ExecuteTime":{"end_time":"2024-07-24T03:02:57.809298Z","start_time":"2024-07-24T02:59:44.206416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndf = pd.concat([df_wheat, df_maize])\n\ndf.to_csv('submission.csv', index=False, sep=',')\n\nprint(df)","metadata":{"ExecuteTime":{"end_time":"2024-07-24T03:02:58.594958Z","start_time":"2024-07-24T03:02:57.809986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}