{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"I'm a newbie in deeplearning and I spend a lot of time to learn pytorch, pytorch_lightning in this compition, and also learned a lot from [graphnet_example](https://www.kaggle.com/code/rasmusrse/graphnet-example), [early sharing](https://www.kaggle.com/code/anjum48/early-sharing-prize-dynedge-1-046), [Paper Overview](https://www.kaggle.com/code/antonsevostianov/paper-overview-graph-neural-networks-in-icecube)\n\n**The purpose of this note book is to show the pipline of use gnn model to train model and predict.** I rewrite the model base on [graphnet_example](https://www.kaggle.com/code/rasmusrse/graphnet-example), [early sharing](https://www.kaggle.com/code/anjum48/early-sharing-prize-dynedge-1-046) and use pytorch_lightning to realize the entire train pipline. And I will also add my own ideas, I will add comments as many as possible to make sure you can get my point, **I really hope this can help kagglers who also want to learn sth in this compitation**\n\nHere is what you can read in the notebook:\n1. coding parts:\n    1. way to use pytorch_lightning to train model\n    2. a command line training code(maybe you can copy the code to pycharm, as I do the opposite)\n2. model parts:\n    1. a \"feature engineering friendlly\" GNN model(I try to reproduce the model in my way, but based on the GraphNet's source code)\n    2. some details I found in the model\n\nI use toy data(sample from train data) to train the model and do prediction. The notebook may not bring you a high score, but the code I wrote is quiet \"flexible\" and you can do your own adjustments on it.\n\nI know the code may contain errors, I will keep updating this\n\nver 1: this notebook\n\nSo if you find it's useful, pleeeeeeeeeeeeeeeeeeeeeeeease upvote, I would be very grateful.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"# Move software to working disk\n!rm  -r software\n!scp -r /kaggle/input/graphnet-and-dependencies/software .\n\n# Install dependencies\n!pip install /kaggle/working/software/dependencies/torch-1.11.0+cu115-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/working/software/dependencies/torch_cluster-1.6.0-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/working/software/dependencies/torch_scatter-2.0.9-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/working/software/dependencies/torch_sparse-0.6.13-cp37-cp37m-linux_x86_64.whl\n!pip install /kaggle/working/software/dependencies/torch_geometric-2.0.4.tar.gz\n\n# Install GraphNeT\n!cd software/graphnet;pip install --no-index --find-links=\"/kaggle/working/software/dependencies\" -e .[torch]\n\n# Append to PATH\nimport sys\nsys.path.append('/kaggle/working/software/graphnet/src')","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:36:54.481551Z","iopub.execute_input":"2023-03-27T23:36:54.482031Z","iopub.status.idle":"2023-03-27T23:39:26.808780Z","shell.execute_reply.started":"2023-03-27T23:36:54.481995Z","shell.execute_reply":"2023-03-27T23:39:26.807294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:42:00.853721Z","iopub.execute_input":"2023-03-27T23:42:00.854081Z","iopub.status.idle":"2023-03-27T23:42:00.858693Z","shell.execute_reply.started":"2023-03-27T23:42:00.854051Z","shell.execute_reply":"2023-03-27T23:42:00.857760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nfrom typing import Any, Callable, List, Optional, Sequence, Tuple, Union\n\nimport torch\nfrom torch import LongTensor, Tensor\n\n# from torch.utils.data import DataLoader, Dataset\nfrom torch_geometric.data import Batch, Data\nfrom torch_geometric.nn import knn_graph\n\nfrom torch import Tensor\nfrom pandarallel import pandarallel\n\nfrom torch_geometric.data import Dataset, DataLoader\n\nimport pandas as pd\nimport numpy as np\nimport os\n\njoin = os.path.join\n","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:42:24.359540Z","iopub.execute_input":"2023-03-27T23:42:24.361917Z","iopub.status.idle":"2023-03-27T23:42:35.063581Z","shell.execute_reply.started":"2023-03-27T23:42:24.361847Z","shell.execute_reply":"2023-03-27T23:42:35.062590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define train data","metadata":{}},{"cell_type":"markdown","source":"### Dataset","metadata":{}},{"cell_type":"code","source":"path_dataori = '/kaggle/input/icecube-neutrinos-in-deep-ice'\n\npath_train_meta = path_trainbatch = '/kaggle/input/icecube-checkdata/' ","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:09.175024Z","iopub.execute_input":"2023-03-27T23:44:09.175698Z","iopub.status.idle":"2023-03-27T23:44:09.180304Z","shell.execute_reply.started":"2023-03-27T23:44:09.175661Z","shell.execute_reply":"2023-03-27T23:44:09.179306Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_meta = pd.read_parquet(join(path_train_meta, 'meta_data_exp.parquet'))","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:16.434587Z","iopub.execute_input":"2023-03-27T23:44:16.434939Z","iopub.status.idle":"2023-03-27T23:44:16.594619Z","shell.execute_reply.started":"2023-03-27T23:44:16.434905Z","shell.execute_reply":"2023-03-27T23:44:16.593719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The `IceCubeDataSet` below do not read data in `__getitem__` func, I myself merge a huge train data to train model. You may try [Chunk based data loading with caching](https://www.kaggle.com/code/iafoss/chunk-based-data-loading-with-caching) also.\n\nYou can do feature engineering in dataset parts, but to be simplify, I write a dict to map the index and feaname. However the first three cols are \"fixed\" and consider to be positional variable and will be used in GNN model to calculate `edgeindex`\n\nAll of the feature engineering way I wrote base on the open kernals I saw, you may try the effect yourself and in dataset you can decide the y_label form \"a/z\" or \"x/y/z\" or a kind of \"embedding\" y_label","metadata":{}},{"cell_type":"code","source":"feas_index_dict = {0:'x'\n                   , 1: 'y'\n                   , 2: 'z'\n                   , 3: 'time'\n                   , 4: 'charge'\n                   , 5: 'qe'\n                   , 6: 'auxiliary'}","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:18.575292Z","iopub.execute_input":"2023-03-27T23:44:18.575625Z","iopub.status.idle":"2023-03-27T23:44:18.580077Z","shell.execute_reply.started":"2023-03-27T23:44:18.575597Z","shell.execute_reply":"2023-03-27T23:44:18.579183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class IceCubeDataSet(Dataset):\n    \n    def __init__(self\n                 , batch_idx: [str, int]\n                 , pulse_limit: int\n                 , train_meta_df: pd.DataFrame\n                 , mode = 'train'\n                 , y_type='a/z'):\n        super().__init__()\n        \n        if mode == 'train' or mode == 'valid':\n            if y_type == 'a/z':\n                self.graph_col_y = [\"azimuth\", \"zenith\"]\n            elif y_type == 'x/y/z':\n                self.graph_col_y = [\"x\", \"y\", \"z\"]\n            else:\n                raise NotImplementedError\n        else:\n            self.graph_col_y = None\n        \n        if mode == 'train' and y_type == 'x/y/z':\n            pandarallel.initialize(nb_workers=5, progress_bar=False) #  parallel_apply\n            train_meta['xyz'] = train_meta[['azimuth', 'zenith']].parallel_apply(lambda x: self._angle_to_xyz(x[0], x[1]), axis=1)\n            train_meta['x'] = train_meta['xyz'].apply(lambda x: x[0])\n            train_meta['y'] = train_meta['xyz'].apply(lambda x: x[1])\n            train_meta['z'] = train_meta['xyz'].apply(lambda x: x[2])\n            \n            del train_meta['xyz']\n            \n        batch_df = pd.read_parquet(join(path_trainbatch, f'batch_{batch_idx}.parquet')).reset_index()\n        \n        self.metabatch_df = batch_df.merge(train_meta, on='event_id')\n        self.eventid_list = batch_df['event_id'].unique().tolist()\n        # self.f_scattering, self.f_absorption = self._ice_transparency()\n        self.sensor_df = self._prepare_sensors()\n        \n        self.pulse_limit = pulse_limit\n        self.graph_col_x = [\"x\", \"y\", \"z\", \"time\", \"charge\", \"qe\", \"auxiliary\"] \n        \n        del batch_df\n    \n    def len(self):\n        return len(self.eventid_list)\n    \n    def get(self, idx):\n        \n        eventid_use = self.eventid_list[idx]\n        metabatch_df_event = self.metabatch_df.loc[self.metabatch_df['event_id'] == eventid_use]\n        metabatch_df_event = metabatch_df_event.merge(self.sensor_df, on='sensor_id', how='left')\n        \n        tmp_list = []\n        for i in self.graph_col_x:\n            tmp_list.append(metabatch_df_event[i].values)\n            \n        graph_x = torch.tensor(np.stack(tmp_list, axis=-1), dtype=torch.float32)\n        \n        graph_data = Data(x=graph_x, n_pulses=torch.tensor(graph_x.shape[0], dtype=torch.int32))\n        \n        # Add ice transparency data\n        # z = graph_data.x[:, 2].numpy()\n        # scattering = torch.tensor(self.f_scattering(z), dtype=torch.float32).view(-1, 1)\n        # # absorption = torch.tensor(self.f_absorption(z), dtype=torch.float32).view(-1, 1)\n        # graph_data.x = torch.cat([graph_data.x, scattering], dim=1)\n        \n        # Downsample the large events\n        if graph_data.n_pulses > self.pulse_limit:\n            graph_data.x = graph_data.x[np.random.choice(graph_data.n_pulses, self.pulse_limit)]\n            graph_data.n_pulses = torch.tensor(self.pulse_limit, dtype=torch.int32)\n        \n        # Builds graph from the k-nearest neighbours.\n        graph_data.edge_index = knn_graph(\n            graph_data.x[:, [0, 1, 2]],  # x, y, z\n            k=8,\n            batch=None,\n            loop=False\n        )\n        \n        if self.graph_col_y is not None:\n            y = metabatch_df_event[self.graph_col_y].values[[0]]\n            y = torch.tensor(y, dtype=torch.float32)\n            graph_data.y = y\n        \n        return graph_data\n\n    def _ice_transparency(self, datum=1950):\n        # Data from page 31 of https://arxiv.org/pdf/1301.5361.pdf\n        # Datum is from footnote 8 of page 29\n        df = pd.read_csv(join(path_dataori, 'sensor_geometry.csv'), delim_whitespace=True)\n        df[\"z\"] = df[\"depth\"] - datum\n        df[\"z_norm\"] = df[\"z\"] / 500\n        df[[\"scattering_len_norm\", \"absorption_len_norm\"]] = RobustScaler().fit_transform(\n            df[[\"scattering_len\", \"absorption_len\"]]\n        )\n        \n        # These are both roughly equivalent after scaling\n        f_scattering = interp1d(df[\"z_norm\"], df[\"scattering_len_norm\"])\n        f_absorption = interp1d(df[\"z_norm\"], df[\"absorption_len_norm\"])\n        return f_scattering, f_absorption\n    \n    def _prepare_sensors(self):\n        \n        sensors = pd.read_csv(join(path_dataori, \"sensor_geometry.csv\")).astype(\n            {\n                \"sensor_id\": np.int16,\n                \"x\": np.float32,\n                \"y\": np.float32,\n                \"z\": np.float32,\n            }\n        )\n        sensors[\"string\"] = 0\n        sensors[\"qe\"] = 1\n\n        for i in range(len(sensors) // 60):\n            start, end = i * 60, (i * 60) + 60\n            sensors.loc[start:end, \"string\"] = i\n\n            # High Quantum Efficiency in the lower 50 DOMs - https://arxiv.org/pdf/2209.03042.pdf (Figure 1)\n            if i in range(78, 86):\n                start_veto, end_veto = i * 60, (i * 60) + 10\n                start_core, end_core = end_veto + 1, (i * 60) + 60\n                sensors.loc[start_core:end_core, \"qe\"] = 1.35\n        \n        # https://github.com/graphnet-team/graphnet/blob/b2bad25528652587ab0cdb7cf2335ee254cfa2db/src/graphnet/models/detector/icecube.py#L33-L41\n        # Assume that \"rde\" (relative dom efficiency) is equivalent to QE\n        sensors[\"x\"] /= 500\n        sensors[\"y\"] /= 500\n        sensors[\"z\"] /= 500\n        sensors[\"qe\"] -= 1.25\n        sensors[\"qe\"] /= 0.25\n        \n        return sensors\n    \n    @staticmethod\n    def _angle_to_xyz(az, zen):\n        x = np.cos(az) * np.sin(zen)\n        y = np.sin(az) * np.sin(zen)\n        z = np.cos(zen)\n        return (x, y, z)\n","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:20.540492Z","iopub.execute_input":"2023-03-27T23:44:20.540828Z","iopub.status.idle":"2023-03-27T23:44:20.565856Z","shell.execute_reply.started":"2023-03-27T23:44:20.540798Z","shell.execute_reply":"2023-03-27T23:44:20.564722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = IceCubeDataSet('exp_train_b512', 999, train_meta)\n\nvalidation_data = IceCubeDataSet('exp_valid_b512', 999, train_meta, mode='valid')\n\ntest_data = IceCubeDataSet('exp_test_b512', 999, train_meta, mode='valid')  # to display local test","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:26.718379Z","iopub.execute_input":"2023-03-27T23:44:26.718740Z","iopub.status.idle":"2023-03-27T23:44:26.991173Z","shell.execute_reply.started":"2023-03-27T23:44:26.718710Z","shell.execute_reply":"2023-03-27T23:44:26.989836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## dataloader of pl form\n\nIn pytorch_lightning, we can make \"dataloader\" in two ways:\n1. make dataset in pytorch at first and make dataloader use pl (used in the notebooks)\n2. [Inherited superclass pl.LightningDataModule to make dataset and dataloader](https://lightning.ai/docs/pytorch/latest/notebooks/lightning_examples/datamodules.html?highlight=datamodule)","metadata":{}},{"cell_type":"code","source":"import pytorch_lightning as pl","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:33.017676Z","iopub.execute_input":"2023-03-27T23:44:33.018021Z","iopub.status.idle":"2023-03-27T23:44:33.023632Z","shell.execute_reply.started":"2023-03-27T23:44:33.017992Z","shell.execute_reply":"2023-03-27T23:44:33.022406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class IceCubeDataLoader(pl.LightningDataModule):\n    def __init__(self, train_dataset, valid_dataset, test_dataset, batch_size=512):\n        super(IceCubeDataLoader, self).__init__()\n        self.train_dataset = train_dataset\n        self.val_dataset = valid_dataset\n        self.test_dataset = test_dataset\n        \n        self.batch_size = batch_size\n    \n    def train_dataloader(self):\n        return DataLoader(self.train_dataset, batch_size=self.batch_size, shuffle=True, num_workers=0)\n\n    def val_dataloader(self):\n        return DataLoader(self.val_dataset, batch_size=self.batch_size, shuffle=True)\n    \n    def test_dataloader(self):\n        return DataLoader(self.test_dataset, batch_size=1, shuffle=True)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:35.064682Z","iopub.execute_input":"2023-03-27T23:44:35.065603Z","iopub.status.idle":"2023-03-27T23:44:35.073109Z","shell.execute_reply.started":"2023-03-27T23:44:35.065546Z","shell.execute_reply":"2023-03-27T23:44:35.071506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"icecube_dataloader = IceCubeDataLoader(train_data, validation_data, test_data, 512)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:38.701559Z","iopub.execute_input":"2023-03-27T23:44:38.701895Z","iopub.status.idle":"2023-03-27T23:44:38.706608Z","shell.execute_reply.started":"2023-03-27T23:44:38.701866Z","shell.execute_reply":"2023-03-27T23:44:38.705519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model parts\n\nmodel parts introduction:\n1. mlp parts\n2. edgeconv part\n3. main part","metadata":{}},{"cell_type":"code","source":"from torch import nn\nfrom torch.nn import Linear\nfrom torch_geometric.nn import EdgeConv","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:41.476420Z","iopub.execute_input":"2023-03-27T23:44:41.476807Z","iopub.status.idle":"2023-03-27T23:44:41.481578Z","shell.execute_reply.started":"2023-03-27T23:44:41.476775Z","shell.execute_reply":"2023-03-27T23:44:41.480522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### mlp","metadata":{}},{"cell_type":"markdown","source":"#### mlp in EdgeConv\n\nNotice we need to do `i_in *= 2` at the first rolling, because if you put mlp in EdgeConv, pytorch_geometrics will do this calculation as first(I comment out the relevant code to make the error show)\n\n![image.png](attachment:15fb8057-2229-4339-9d19-66a40a6a3d58.png)\n\n![image.png](attachment:e5a83651-e4f9-43dd-a630-561fba487cd1.png)","metadata":{},"attachments":{"15fb8057-2229-4339-9d19-66a40a6a3d58.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAABEsAAABSCAYAAACsTlo7AAAgAElEQVR4nOzdd5xV1bnw8d/e+/Q+vTCFGRiG3hERwd67WDBqjBpLoonXGI0ab2Lqm3bjNWpM1BjR2GIBGyAWQEC6dBgYYHov58zpZZf3jzmOMzAIKpZ77/r+w4eZ3c7aZ/Za69nPWksy9u0zEARBEARBEARBEARBEKC8HPnrvgZBEARBEARBEARBEIRvEhEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEARBEIR+RLBEEARBEARBEARBEAShHxEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEARBEIR+RLBEEARBEARBEARBEAShHxEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEARBEIR+RLBEEARBEARBEARBEAShHxEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEARBEIR+RLBEEARBEARBEARBEAShHxEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEARBEIR+RLBEEARBEARBEARBEAShHxEsEQRBEARBEARBEARB6EcESwRBEARBEARBEARBEPoRwRJBEARBEARBEATh/6SkDis7bGzxW77uSxGOgqQOC5sd7A6av/CxlPtvu+3+I924qa2NVxYvpqGlheKCAszm3gsIRSIsXLaMlxctYsPWrVgtFnIyM1HkrycWszXSwuNtaxjlyMepfPO+9Ev8u3m1axuTnEMwy8pn3n+xv4pXu7Z+7v2/TH41yp+blmGRTRRZfV/35XyjtSSDPNuxka5UhGKrD5P09cYuo3qKB5tX4FejjLDnUJ/w82TbOl7s3My7gWp8JjuFFg+SJA3Y7/1ANY+3rUUzDIbZswBI6horgjW85d9JntmNz2Q/7Pm7UhF+1/Q+XsVGgcXzpXzGjzUleni24yN6tBhDLN6vvey/qKiW5IHmDwhrSYbbs494v8X+Kp5oW4MiyZTZMr/EK/zqxPQUi/xVbI20UGzxYZVNX/clfaqIluQvLSuI6SnKbVlf67V8XHYf9Oyn1JqB4xtYf34T1CX8PNiygnWheia5ijBLn68e7lHj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"},"e5a83651-e4f9-43dd-a630-561fba487cd1.png":{"image/png":"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"}}},{"cell_type":"code","source":"def mlp_inedgeconv(input_size: int, feats_list: list, mlp_activation: Callable):\n    \n    mlp_tmp = []\n    \n    full_feats_list = [input_size] + feats_list\n    \n    full_feats_zip = zip(full_feats_list[:-1], full_feats_list[1:])\n    for ix, (i_in, i_out) in enumerate(full_feats_zip):\n        if ix == 0:\n            i_in *= 2\n        mlp_tmp.append(Linear(i_in, i_out))\n        mlp_tmp.append(mlp_activation)\n    \n    \n    mlp_tmp_fn = nn.Sequential(*mlp_tmp)\n    \n    return mlp_tmp_fn\n","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:45.605435Z","iopub.execute_input":"2023-03-27T23:44:45.605783Z","iopub.status.idle":"2023-03-27T23:44:45.613783Z","shell.execute_reply.started":"2023-03-27T23:44:45.605753Z","shell.execute_reply":"2023-03-27T23:44:45.612214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### mlp in other parts\n\nThe different is we don't need to do `i_in *= 2` this part","metadata":{}},{"cell_type":"code","source":"def mlp_skipandreadout(input_size: int, feats_list: list, mlp_activation: Callable):\n    '''after_edgeconv, readout'''\n    mlp_tmp = []\n    \n    full_feats_list = [input_size] + feats_list\n    \n    full_feats_zip = zip(full_feats_list[:-1], full_feats_list[1:])\n    for ix, (i_in, i_out) in enumerate(full_feats_zip):\n        mlp_tmp.append(Linear(i_in, i_out))\n        mlp_tmp.append(mlp_activation)\n    mlp_tmp_fn = nn.Sequential(*mlp_tmp)\n    \n    return mlp_tmp_fn","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:48.519207Z","iopub.execute_input":"2023-03-27T23:44:48.519597Z","iopub.status.idle":"2023-03-27T23:44:48.527763Z","shell.execute_reply.started":"2023-03-27T23:44:48.519563Z","shell.execute_reply":"2023-03-27T23:44:48.526357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### EdgeConv\n\n[Acorrending to docs](https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.nn.conv.EdgeConv.html?highlight=EdgeConv), we need to input params `nn` and `aggr` to the father class: torch_geometric.nn.EdgeConv","metadata":{}},{"cell_type":"code","source":"class EdgeConv_manual(EdgeConv):\n    def __init__(self\n                 , mlp_inner: Callable\n                 , agg_str:str= 'max'\n                 , edgeidx_nbnum=8\n                 , subfeas_list:List[int]= None\n                 , **kwargs):\n        '''\n        edgeidx_nbnum: number of neibour to recal the edge_index\n        subfeas_list: index of col to cal the neibour\n        '''\n        \n        self.subfeas_list = subfeas_list\n        self.edgeidx_nbnum = edgeidx_nbnum\n        \n        super(EdgeConv_manual, self).__init__(nn=mlp_inner, aggr=agg_str, **kwargs)\n        \n    def forward(self, x, edge_index, batch=None):\n        \n        x = super().forward(x, edge_index)\n        \n        # Recompute adjacency\n        edge_index = knn_graph(\n            x=x[:, self.subfeas_list],\n            k=self.edgeidx_nbnum,\n            batch=batch,\n        )\n        return x, edge_index","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:51.144685Z","iopub.execute_input":"2023-03-27T23:44:51.145061Z","iopub.status.idle":"2023-03-27T23:44:51.153534Z","shell.execute_reply.started":"2023-03-27T23:44:51.145029Z","shell.execute_reply":"2023-03-27T23:44:51.151989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### main model of pl form\n\nIf you want to use pytorch_lightning to define a model, you should define the following funcs:\n1. `__init__`: define the layers and params in the models (same to pytorch version)\n2. `forward`: way to forward-propagating (same to pytorch version)\n3. `training_step`: run every batch, the output is \"loss\" and you don't need to call any \"backward\" funcs, with the input of \"data\", \"batch_idx\"\n4. `validation_step`: you can define the execution frequency, normally every epoch one time, but in the compitation we may have many batch in one epoch, you can increase the frequency, with the input of \"data\", \"batch_idx\"\n5. `test_step`: run after you train the model, with the input of \"data\", \"batch_idx\"\n6. `configure_optimizers`: define lr, optmize and so on\n\nThere are other parts in pytorch_lightning(I, for now, haven't use):\n1. `train_step_end`, `validation_step_end`, `test_step_end`\n2. `train_epoch_end`, `validation_epoch_end`, `test_epoch_end`\n\nothers thing you cando (I do this in \"train model\" parts):\n1. log infoes in pl: in the model we defined, we use \"self.log(...)\" to gather infoes. and we should define different kind of logger to save the infoes.\n    1. There are many kind of logger, like MLFlowLogger, CSVLogger, TensorBoardLogger etc...\n2. use [callback func](https://lightning.ai/docs/pytorch/latest/extensions/callbacks.html?highlight=callback) to realize function like early stop, gather trained model every epoch\n","metadata":{}},{"cell_type":"code","source":"from torch_geometric.data import Data, Batch\nfrom torch_geometric.utils import homophily\nfrom torch_geometric.nn import knn\n# from typing import List, Optional, Sequence, Tuple\nfrom torch_scatter import scatter_max, scatter_mean, scatter_min, scatter_sum\n\nfrom torch.nn import ReLU\nfrom torch.nn import LeakyReLU\n\nimport sys\nsys.path.append('/kaggle/working/software/graphnet/src')\nfrom graphnet.models.utils import calculate_xyzt_homophily","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:44:57.437622Z","iopub.execute_input":"2023-03-27T23:44:57.438013Z","iopub.status.idle":"2023-03-27T23:44:58.415865Z","shell.execute_reply.started":"2023-03-27T23:44:57.437980Z","shell.execute_reply":"2023-03-27T23:44:58.414781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dyn_mlp_act_dict = {'ReLU': ReLU(), 'LeakyReLU': LeakyReLU()}","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:02.724918Z","iopub.execute_input":"2023-03-27T23:45:02.725617Z","iopub.status.idle":"2023-03-27T23:45:02.730927Z","shell.execute_reply.started":"2023-03-27T23:45:02.725576Z","shell.execute_reply":"2023-03-27T23:45:02.729878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Notice I don't define params in `__init__`, I find that if I do so, the `self.save_hyperparameters()` will not work, so I define all of them in function `model_hyper_args`. In addition, it is more reasonable in the way of \"command line\" training\n\nThe code base on [GraphNet's source code](https://github.com/graphnet-team/graphnet), and I do my changes, but it looks very familiar to the source code.\n\nHere are params I defind:\n1. `input_feas_num`: depend on the feature engineering you do, but the first three should be \"x\", \"y\", \"z\"(location vars)\n2. `dyn_mlp_act`: actfinc in all kind of mlp\n3. `dyn_subfeas_list`: feas to cal neibor to edgeindex\n4. `dyn_layersize_list`: size of mlp in EdgeConv\n5. `after_dyn_mlpsize_list`: size of mlp before pooling\n6. `readout_mlpsize_list`: size of mlp of readout\n7. `edgeidx_nbnum`: neibourhood num\n8. `global_pooling_stats_list`: global pooling stats\n9. `if_add_feas_afterpool`: if add homo features after pooling and before readout (in source code, the `if_add_feas_afterpool` and `if_add_feas_atstart` is one param, I sperate it into two params)\n10. `if_add_feas_atstart`: if add homo features to EdgeConv\n11. `y_type`: 'a/z' or 'x/y/z', **currently this is quiet different from [the paper](https://iopscience.iop.org/article/10.1088/1748-0221/17/11/P11003/pdf)**, because according to it, we don't predict 'a/z' or 'x/y/z' directly but predict \"embedding\", I will explain it in loss parts.(the difference in code is in the readout layer)\n12. `loss_name`: \"MSE\" or \"vMF\"","metadata":{}},{"cell_type":"code","source":"class DynEdgeModel(pl.LightningModule):\n    def __init__(self, args):\n        \n        super(DynEdgeModel, self).__init__()\n        \n        self.save_hyperparameters()\n        \n        if args.readout_mlpsize_list == []:\n            self.readout_mlpsize_list = [128]  # hidden layers\n        else:\n            self.readout_mlpsize_list = args.readout_mlpsize_list\n        self.y_type = args.y_type\n        self.loss_name = args.loss_name\n        \n        if args.y_type == 'a/z':\n            self.output_size = 2\n        elif args.y_type == 'x/y/z':\n            self.output_size = 3\n        else:\n            raise ValueError\n        self.readout_mlpsize_list.append(self.output_size)\n        \n        if self.loss_name == 'MSE' and self.y_type == 'a/z':\n            self.loss_func = MSELoss2d()\n        elif self.loss_name == 'MSE' and self.y_type == 'x/y/z':\n            self.loss_func = MSELoss3d()\n        elif self.loss_name == 'vMF' and self.y_type == 'a/z':\n            self.loss_func = VonMisesFisher2DLoss()\n        elif self.loss_name == 'vMF' and self.y_type == 'x/y/z':\n            self.loss_func = VonMisesFisher3DLoss()\n        else:\n            raise NotImplementedError\n        \n        self.input_feas = args.input_feas_num\n        \n        self.dyn_mlp_act = dyn_mlp_act_dict[args.dyn_mlp_act]\n        if args.dyn_subfeas_list == []:\n            self.dyn_subfeas_list = slice(0, 3)\n        else: \n            self.dyn_subfeas_list = args.dyn_subfeas_list\n        self.edgeidx_nbnum = args.edgeidx_nbnum\n        \n        \n        self.if_add_feas_atstart = args.if_add_feas_atstart\n        self.if_add_feas_afterpool = args.if_add_feas_afterpool\n        self.added_feas_num = 4\n        \n        if args.dyn_layersize_list is []:\n            self.dyn_layersize_list = [(128, 256), (334, 256), (336, 256), (336, 256)]\n        else:\n            self.dyn_layersize_list = args.dyn_layersize_list\n        \n        if args.after_dyn_mlpsize_list == []:\n            self.after_dyn_mlpsize_list = [(336, 256)]\n        else:\n            self.after_dyn_mlpsize_list = args.after_dyn_mlpsize_list\n        \n        if args.global_pooling_stats_list == []:\n            self.global_pooling_stats_list = ['max', 'min', 'mean', 'sum']\n        else:\n            self.global_pooling_stats_list = args.global_pooling_stats_list\n        self.global_pooling_stats_dict = {'max': scatter_max\n            , 'mean': scatter_mean\n            , 'min': scatter_min\n            , 'sum': scatter_sum}\n        \n        self._edgeconv_layers()\n        self._after_dyn_mlp_layer()\n        self._readout_mlp_layer()\n        \n        # 'a/z' or 'x/y/z'\n        if args.y_type == 'a/z':\n            self.acc_func = angular_dist_score_az\n        elif args.y_type == 'x/y/z':\n            self.acc_func = angular_dist_score_xyz\n        else:\n            raise NotImplementedError\n    \n    def _readout_mlp_layer(self):\n        # readout_mlp_inputs = self.input_feas\n        readout_mlp_inputs = 0\n        if self.if_add_feas_afterpool:\n            readout_mlp_inputs += self.added_feas_num\n        \n        readout_mlp_inputs += len(self.global_pooling_stats_list) * [tup[-1] for tup in self.after_dyn_mlpsize_list][-1]\n        \n        self.readout_mlp_layer = mlp_skipandreadout(readout_mlp_inputs, self.readout_mlpsize_list, self.dyn_mlp_act)\n    \n    def _after_dyn_mlp_layer(self):\n        after_dyn_mlp_inputs = self.input_feas\n        if self.if_add_feas_atstart:\n            after_dyn_mlp_inputs += self.added_feas_num\n        after_dyn_mlp_inputs += sum([tup[-1] for tup in self.dyn_layersize_list])\n        \n        self.after_dyn_mlp_layer_total = torch.nn.ModuleList()\n        for onelayer in self.after_dyn_mlpsize_list:\n            self.after_dyn_mlp_layer_total.append(\n                mlp_skipandreadout(after_dyn_mlp_inputs, list(onelayer), self.dyn_mlp_act))\n            after_dyn_mlp_inputs = onelayer[-1]\n    \n    def _edgeconv_layers(self):\n        conv_layers_inputs = self.input_feas\n        if self.if_add_feas_atstart:\n            conv_layers_inputs += self.added_feas_num\n        \n        self.conv_layers_total = torch.nn.ModuleList()\n        \n        conv_layers_inputs_iter = conv_layers_inputs\n        for onelayer in self.dyn_layersize_list:\n            mlp_tmp = mlp_inedgeconv(conv_layers_inputs_iter, list(onelayer), self.dyn_mlp_act)\n            conv_layer_tmp = EdgeConv_manual(mlp_tmp, 'add', 8, self.dyn_subfeas_list)\n            self.conv_layers_total.append(conv_layer_tmp)\n            \n            conv_layers_inputs_iter = onelayer[-1]\n    \n    def forward(self, data: Batch):\n        \n        # DataBatch(x=[8001, 6], gt=[300], n_pulses=[100], eid=[100], batch=[8001], ptr=[101])\n        \n        data_feas = data.x  # [\"x\", \"y\", \"z\", \"time\", \"charge\", \"qe\", \"auxiliary\"] + [...]\n        data_batch = data.batch\n        data_edge_index = data.edge_index\n        \n        data_edge_index = knn_graph(data_feas[:, :3], self.edgeidx_nbnum, data_batch)\n        \n        if self.if_add_feas_atstart or self.if_add_feas_afterpool:\n            homo_x, homo_y, homo_z, homo_t = calculate_xyzt_homophily(data_feas, data_edge_index, data_batch)\n            \n            homo_total = torch.cat([homo_x, homo_y, homo_z, homo_t], dim=-1)\n        \n        if self.if_add_feas_atstart:\n            _, batch_cnt = torch.unique_consecutive(data_batch, return_counts=True)\n            homo_total_atstart = torch.repeat_interleave(homo_total, batch_cnt, dim=0)\n            \n            x = torch.cat((data_feas, homo_total_atstart), dim=1)\n        else:\n            x = data_feas\n        \n        skip_connections = [x]\n        conv_edge_index = data_edge_index\n        for conv_layer in self.conv_layers_total:\n            x, edge_index = conv_layer(x, conv_edge_index)\n            conv_edge_index = knn_graph(x=x[:, :3], k=self.edgeidx_nbnum, batch=data_batch)\n            skip_connections.append(x)\n        x = torch.cat(skip_connections, dim=1)\n        \n        for after_dyn_mlp_layer in self.after_dyn_mlp_layer_total:\n            x = after_dyn_mlp_layer(x)\n        \n        after_pooling = []\n        for pooling_stats in self.global_pooling_stats_list:\n            \n            pooling_func = self.global_pooling_stats_dict[pooling_stats]\n            if pooling_stats in ['max', 'min']:\n                out = pooling_func(x, data_batch, dim=0)[0]\n            elif pooling_stats in ['sum', 'mean']:\n                out = pooling_func(x, data_batch, dim=0)\n            else:\n                raise NotImplementedError\n            \n            after_pooling.append(out)\n        x = torch.cat(after_pooling, dim=1)\n        \n        if self.if_add_feas_afterpool:\n            x = torch.cat((x, homo_total), dim=-1)\n        else:\n            x = x\n        \n        readout_out = self.readout_mlp_layer(x)\n        \n        return readout_out\n    \n    def training_step(self, data, batch_idx):  \n        '''execute by batch'''\n        \n        predict = self.forward(data)\n        \n        if self.y_type == 'x/y/z':\n            kappa = predict.norm(dim=1, p=2) + 1e-8\n            pred_x = predict[:, 0] / kappa\n            pred_y = predict[:, 1] / kappa\n            pred_z = predict[:, 2] / kappa\n            predict = torch.stack([pred_x, pred_y, pred_z, kappa], dim=1)  # (Batch, 4)\n            predict_acc = torch.stack([pred_x, pred_y, pred_z], dim=1)  # (Batch, 3)\n            \n            true = data.y.view(-1, 3)  # (Batch, 3)\n            \n        elif self.y_type == 'a/z':\n            true = data.y.view(-1, 2)  # (Batch, 2)\n            predict_acc = predict\n        \n        # todo 加上accuracy\n        loss = self.loss_func(predict, true).sum()\n        acc = self.acc_func(predict_acc, true)\n\n        \n        self.log('train_loss', loss, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True)\n        self.log('train_acc', acc, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True)\n        \n        return loss  # {\"loss\": loss}\n    \n    def validation_step(self, data, batch_idx):  # batch_idx\n        \n        # tensorboard = self.logger.experiment\n        \n        predict = self.forward(data)\n        if self.y_type == 'x/y/z':\n            kappa = predict.norm(dim=1, p=2) + 1e-8\n            pred_x = predict[:, 0] / kappa\n            pred_y = predict[:, 1] / kappa\n            pred_z = predict[:, 2] / kappa\n            predict = torch.stack([pred_x, pred_y, pred_z, kappa], dim=1)  # (Batch, 4)\n            predict_acc = torch.stack([pred_x, pred_y, pred_z], dim=1)  # (Batch, 3)\n   \n            true = data.y.view(-1, 3)  # (Batch, 3)\n        elif self.y_type == 'a/z':\n            true = data.y.view(-1, 2)  # (Batch, 2)\n            predict_acc = predict\n        \n        loss = self.loss_func(predict, true).sum()\n        acc = self.acc_func(predict_acc, true)\n        \n        self.log('val_loss', loss, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True)  # monitor this val\n        self.log('val_acc', acc, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True)\n        \n        # tensorboard.add_histogram(...)\n        \n        return loss  # val_loss: loss\n    \n    def test_step(self, data, batch_idx):\n        '''local infer'''\n        \n        predict = self.forward(data)\n        if self.y_type == 'x/y/z':\n            kappa = predict.norm(dim=1, p=2) + 1e-8\n            pred_x = predict[:, 0] / kappa\n            pred_y = predict[:, 1] / kappa\n            pred_z = predict[:, 2] / kappa\n            predict = torch.stack([pred_x, pred_y, pred_z, kappa], dim=1)  # (Batch, 4)\n            predict_acc = torch.stack([pred_x, pred_y, pred_z], dim=1)  # (Batch, 3)\n        \n            true = data.y.view(-1, 3)  # (Batch, 3)\n        elif self.y_type == 'a/z':\n            true = data.y.view(-1, 2)  # (Batch, 2)\n            predict_acc = predict\n    \n        loss = self.loss_func(predict, true).sum()\n        acc = self.acc_func(predict_acc, true)\n    \n        self.log('test_loss', loss, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True) \n        self.log('val_acc', acc, on_step=False, on_epoch=True, prog_bar=True,\n                 logger=True)\n    \n        # tensorboard.add_histogram(...)\n    \n        return loss  # test_loss: loss\n    \n    def configure_optimizers(self):\n        optimizer = torch.optim.Adam(self.parameters(), lr=0.01)\n        # scheduler = torch.optim.lr_scheduler.SequentialLR(\n        #     optimizer,\n        #     schedulers=[\n        #         torch.optim.lr_scheduler.LinearLR(\n        #             optimizer, 1e-12, 1.0, self.hparams.num_warmup_step\n        #         ),\n        #         torch.optim.lr_scheduler.CosineAnnealingLR(\n        #             optimizer, self.hparams.num_total_step, self.hparams.min_lr\n        #         ),\n        #     ],\n        #     milestones=[self.hparams.num_warmup_step],\n        # )\n        # return {\n        #     'optimizer': optimizer,\n        #     'lr_scheduler': {\n        #         'scheduler': scheduler,\n        #         'interval': 'step',\n        #     },\n        # }\n        return {'optimizer': optimizer}\n    \n    @staticmethod\n    def model_hyper_args(parent_parser):\n        \n        parser = argparse.ArgumentParser(parents=[parent_parser], add_help=False)\n        \n        parser.add_argument('--input_feas_num', type=int, default=7)\n        parser.add_argument('--dyn_mlp_act', type=str, default='LeakyReLU', help='LeakyReLU, ReLU')\n        parser.add_argument('--dyn_subfeas_list', type=list, default=[0, 1, 2], help='List[int]')\n        parser.add_argument('--dyn_layersize_list', type=list, default=[(128, 256), (334, 256), (336, 256), (336, 256)], help='List[Tuple[int]]')\n        parser.add_argument('--after_dyn_mlpsize_list', type=list, default=[(336, 256)], help='List[Tuple[int]]')\n        parser.add_argument('--readout_mlpsize_list', type=list, default=[128], help='List[int]')\n        parser.add_argument('--edgeidx_nbnum', type=int, default=8)\n        parser.add_argument('--global_pooling_stats_list', type=list, default=['max', 'min', 'sum', 'mean'])\n        parser.add_argument('--if_add_feas_afterpool', type=bool, default=True)\n        parser.add_argument('--if_add_feas_atstart', type=bool, default=True)\n        parser.add_argument('--y_type', type=str, default='a/z')\n        parser.add_argument('--loss_name', type=str, default='MSE')\n        \n        return parser\n        \n","metadata":{"execution":{"iopub.status.busy":"2023-03-28T00:09:46.079558Z","iopub.execute_input":"2023-03-28T00:09:46.079959Z","iopub.status.idle":"2023-03-28T00:09:46.129123Z","shell.execute_reply.started":"2023-03-28T00:09:46.079926Z","shell.execute_reply":"2023-03-28T00:09:46.128333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## loss\n\nAccording to the [paper(8/25)](https://iopscience.iop.org/article/10.1088/1748-0221/17/11/P11003/pdf) the host provided, **they don't use 'a/z' or 'x/y/z' as label but an embedding one to both Azimuth and Zenith**. func `AzimuthReconstructionWithKappa` and `ZenithReconstructionWithKappa` turn the model's output to Azimuth and zenith with kappa seperately(it's a kind of \"regularization methods\") which is a little bit strange to me because they also provide the func `DirectionReconstructionWithKappa`, which use 3d version of output with kappa caluated by all 3d data.\n\n```python\nfrom graphnet.models.task.reconstruction import AzimuthReconstruction, AzimuthReconstructionWithKappa\nfrom graphnet.models.task.reconstruction import ZenithReconstruction, AzimuthReconstructionWithKappa\nfrom graphnet.models.task.reconstruction import DirectionReconstructionWithKappa\n```\n\nAnyway, you can try use direct 'a/z' or 'x/y/z' version of y_label or use an embedding version. Just make sure you really understand the paper. After all what kind of y_label we use depends on their result.\n\nLoss func we may use(2d version or 3d version):\n1. MSE, MAE, etc (general loss in many regression tasks)\n2. vMF\n\nIn a word, the main process is:\n1. generate the model output\n2. turn it to 'a/z' or 'x/y/z' version if you use \"embedding output\"(use funcs I mentioned before)\n3. select loss func: MSE or vMF or both of them.\n\nrelevent source code:\n1. [loss func parts](https://github.com/graphnet-team/graphnet/blob/main/src/graphnet/training/loss_functions.py)\n2. [task parts](https://github.com/graphnet-team/graphnet/blob/main/src/graphnet/models/task/reconstruction.py)","metadata":{}},{"cell_type":"code","source":"from graphnet.training.loss_functions import MSELoss as MSELoss2d\nfrom graphnet.training.loss_functions import LossFunction\nfrom graphnet.training.loss_functions import VonMisesFisher2DLoss, VonMisesFisher3DLoss","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:14.942060Z","iopub.execute_input":"2023-03-27T23:45:14.942431Z","iopub.status.idle":"2023-03-27T23:45:14.951764Z","shell.execute_reply.started":"2023-03-27T23:45:14.942397Z","shell.execute_reply":"2023-03-27T23:45:14.950659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MSELoss3d(LossFunction):\n    \"\"\"Mean squared error loss of 3d version\"\"\"\n\n    def _forward(self, prediction: Tensor, target: Tensor) -> Tensor:\n        \"\"\"Implement loss calculation.\"\"\"\n        # Check(s)\n        assert prediction.dim() == 3\n        assert prediction.size() == target.size()\n\n        elements = torch.mean((prediction - target) ** 2, dim=-1)\n        return elements","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:17.666853Z","iopub.execute_input":"2023-03-27T23:45:17.667206Z","iopub.status.idle":"2023-03-27T23:45:17.673292Z","shell.execute_reply.started":"2023-03-27T23:45:17.667176Z","shell.execute_reply":"2023-03-27T23:45:17.672315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def angular_dist_score_az(y_pred, y_true):\n    \"\"\"\n    calculate the MAE of the angular distance between two directions.\n    # https://www.kaggle.com/code/sohier/mean-angular-error\n    \"\"\"\n\n    az_true = y_true[:, 0]\n    zen_true = y_true[:, 1]\n\n    az_pred = y_pred[:, 0]\n    zen_pred = y_pred[:, 1]\n\n    # pre-compute all sine and cosine values\n    sa1 = torch.sin(az_true)\n    ca1 = torch.cos(az_true)\n    sz1 = torch.sin(zen_true)\n    cz1 = torch.cos(zen_true)\n\n    sa2 = torch.sin(az_pred)\n    ca2 = torch.cos(az_pred)\n    sz2 = torch.sin(zen_pred)\n    cz2 = torch.cos(zen_pred)\n\n    # scalar product of the two cartesian vectors (x = sz*ca, y = sz*sa, z = cz)\n    scalar_prod = sz1 * sz2 * (ca1 * ca2 + sa1 * sa2) + (cz1 * cz2)\n\n    # scalar product of two unit vectors is always between -1 and 1, this is against nummerical instability\n    # that might otherwise occure from the finite precision of the sine and cosine functions\n    scalar_prod = torch.clamp(scalar_prod, -1, 1)\n\n    # convert back to an angle (in radian)\n    return torch.mean(torch.abs(torch.arccos(scalar_prod)))  # mean abs error\n\n\ndef angular_dist_score_xyz(y_pred, y_true):\n    \n    def _xyz_to_angle(xyz_b):\n        x, y, z = xyz_b.t()\n        az = torch.arccos(x / torch.sqrt(x ** 2 + y ** 2)) * torch.sign(y)\n        zen = torch.arccos(z / torch.sqrt(x ** 2 + y ** 2 + z ** 2))\n        return torch.stack([az, zen], dim=1)\n\n    y_pred = _xyz_to_angle(y_pred)\n    y_true = _xyz_to_angle(y_true)\n    \n\n    az_true = y_true[:, 0]\n    zen_true = y_true[:, 1]\n\n    az_pred = y_pred[:, 0]\n    zen_pred = y_pred[:, 1]\n\n    # pre-compute all sine and cosine values\n    sa1 = torch.sin(az_true)\n    ca1 = torch.cos(az_true)\n    sz1 = torch.sin(zen_true)\n    cz1 = torch.cos(zen_true)\n\n    sa2 = torch.sin(az_pred)\n    ca2 = torch.cos(az_pred)\n    sz2 = torch.sin(zen_pred)\n    cz2 = torch.cos(zen_pred)\n    \n    # scalar product of the two cartesian vectors (x = sz*ca, y = sz*sa, z = cz)\n    scalar_prod = sz1 * sz2 * (ca1 * ca2 + sa1 * sa2) + (cz1 * cz2)\n\n    # scalar product of two unit vectors is always between -1 and 1, this is against nummerical instability\n    # that might otherwise occure from the finite precision of the sine and cosine functions\n    scalar_prod = torch.clamp(scalar_prod, -1, 1)\n\n    # convert back to an angle (in radian)\n    return torch.mean(torch.abs(torch.arccos(scalar_prod)))  # mean abs error","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:20.154712Z","iopub.execute_input":"2023-03-27T23:45:20.155065Z","iopub.status.idle":"2023-03-27T23:45:20.167310Z","shell.execute_reply.started":"2023-03-27T23:45:20.155035Z","shell.execute_reply":"2023-03-27T23:45:20.166248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train model","metadata":{}},{"cell_type":"markdown","source":"### define the model\n\nWe use `parser = argparse.ArgumentParser()` define a \"father\" parser and we can use it to define more params we want","metadata":{}},{"cell_type":"code","source":"import argparse\n\nparser = argparse.ArgumentParser()\n\nparser = DynEdgeModel.model_hyper_args(parser)\n\nargs = parser.parse_args(args=[])  # we need 'args=[]' in jupyter notebook enveriments","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:23.807719Z","iopub.execute_input":"2023-03-27T23:45:23.808116Z","iopub.status.idle":"2023-03-27T23:45:23.815216Z","shell.execute_reply.started":"2023-03-27T23:45:23.808084Z","shell.execute_reply":"2023-03-27T23:45:23.813879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"icecubemodel = DynEdgeModel(args)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:26.447686Z","iopub.execute_input":"2023-03-27T23:45:26.448146Z","iopub.status.idle":"2023-03-27T23:45:26.494436Z","shell.execute_reply.started":"2023-03-27T23:45:26.448103Z","shell.execute_reply":"2023-03-27T23:45:26.493335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### define and use pl.trainer","metadata":{}},{"cell_type":"markdown","source":"[pytorch_lightning trainer's doc](https://lightning.ai/docs/pytorch/latest/api/lightning.pytorch.trainer.trainer.Trainer.html?highlight=trainer%20fit#lightning.pytorch.trainer.trainer.Trainer.fit):\n1. define the obj\n2. call the `.fit` method of this obj","metadata":{}},{"cell_type":"markdown","source":"[ModelCheckpoint](https://lightning.ai/docs/pytorch/latest/api/lightning.pytorch.callbacks.ModelCheckpoint.html?highlight=modelcheckpoint#lightning.pytorch.callbacks.ModelCheckpoint): Save the model periodically by monitoring a quantity. Every metric logged with log() or log_dict() in LightningModule is a candidate for the monitor key","metadata":{}},{"cell_type":"code","source":"from pytorch_lightning.callbacks import ModelCheckpoint\n\ncb_ckpt_path = os.path.join(os.getcwd(), 'ckpt')\n\ncb_ckpt = ModelCheckpoint(dirpath=cb_ckpt_path\n                          , filename='mytry-{epoch}-{val_loss:.2f}-{other_metric:.2f}'\n                          , monitor='val_loss'\n                          , save_top_k=-1  #  save all model(every epoch)\n                          , every_n_epochs=1)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:29.499783Z","iopub.execute_input":"2023-03-27T23:45:29.500127Z","iopub.status.idle":"2023-03-27T23:45:29.506276Z","shell.execute_reply.started":"2023-03-27T23:45:29.500097Z","shell.execute_reply":"2023-03-27T23:45:29.505328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.callbacks.early_stopping import EarlyStopping\n\ncb_earlystop = EarlyStopping(monitor='val_loss'\n                              , min_delta=0.01\n                              , patience=3\n                              , strict=True  # check if we define the monitor val\n                              )","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:43.205848Z","iopub.execute_input":"2023-03-27T23:45:43.206184Z","iopub.status.idle":"2023-03-27T23:45:43.210741Z","shell.execute_reply.started":"2023-03-27T23:45:43.206156Z","shell.execute_reply":"2023-03-27T23:45:43.210049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.callbacks import LearningRateMonitor\n\ncb_lrmonitor = LearningRateMonitor(logging_interval='step')  # epoch, step","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:46.038244Z","iopub.execute_input":"2023-03-27T23:45:46.038655Z","iopub.status.idle":"2023-03-27T23:45:46.044384Z","shell.execute_reply.started":"2023-03-27T23:45:46.038622Z","shell.execute_reply":"2023-03-27T23:45:46.043213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.loggers.tensorboard import TensorBoardLogger \n\nlogger_path = os.path.join(os.getcwd(), 'logsave')\n\ntblogger = TensorBoardLogger(save_dir=logger_path, name='logtb', version=None)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:47.928556Z","iopub.execute_input":"2023-03-27T23:45:47.928953Z","iopub.status.idle":"2023-03-27T23:45:47.934843Z","shell.execute_reply.started":"2023-03-27T23:45:47.928916Z","shell.execute_reply":"2023-03-27T23:45:47.933787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pytorch_lightning.loggers import CSVLogger \n\ncsvlogger = CSVLogger(save_dir=logger_path, name=\"logcsv\", version=None)","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:50.268928Z","iopub.execute_input":"2023-03-27T23:45:50.269372Z","iopub.status.idle":"2023-03-27T23:45:50.274082Z","shell.execute_reply.started":"2023-03-27T23:45:50.269338Z","shell.execute_reply":"2023-03-27T23:45:50.273352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer = pl.Trainer(accelerator='auto'  # “cpu”, “gpu”, “tpu”, “ipu”, “hpu”, “mps”, “auto”\n                     , devices=\"auto\"\n                     # , fast_dev_run = 1  # only use to make sure if the trainer run successfully\n                     , precision=32  # Semi-precision training, try input 16 to speed up the training process\n                     , max_epochs=4\n                     \n                     # train\n                     # # decide this func's executio nfrequency. 1.0 means every epoch, \n                     # in this comp we may reduce that cause one epoch has too many data\n                     , val_check_interval=1.0  \n                     \n                     # logger\n                     , logger=[tblogger, csvlogger]\n                     , log_every_n_steps=1\n                     \n                     , enable_checkpointing=True \n                     , enable_progress_bar=True\n                     , enable_model_summary=True\n                     # callback\n                     , callbacks=[cb_ckpt, cb_earlystop])","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:52.474201Z","iopub.execute_input":"2023-03-27T23:45:52.474850Z","iopub.status.idle":"2023-03-27T23:45:52.518237Z","shell.execute_reply.started":"2023-03-27T23:45:52.474809Z","shell.execute_reply":"2023-03-27T23:45:52.517325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.fit(icecubemodel\n            , train_dataloaders=icecube_dataloader.train_dataloader()\n            , val_dataloaders=icecube_dataloader.val_dataloader())","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:45:55.715290Z","iopub.execute_input":"2023-03-27T23:45:55.715650Z","iopub.status.idle":"2023-03-27T23:55:53.908606Z","shell.execute_reply.started":"2023-03-27T23:45:55.715621Z","shell.execute_reply":"2023-03-27T23:55:53.907639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## make predict ","metadata":{}},{"cell_type":"markdown","source":"### predict locally\n\npytorch_lightning will not use test data until we call the `.test` methods","metadata":{}},{"cell_type":"code","source":"trainer.test(icecubemodel, dataloaders=icecube_dataloader.test_dataloader(), ckpt_path='best')","metadata":{"execution":{"iopub.status.busy":"2023-03-27T23:56:34.870788Z","iopub.execute_input":"2023-03-27T23:56:34.871168Z","iopub.status.idle":"2023-03-27T23:56:57.959003Z","shell.execute_reply.started":"2023-03-27T23:56:34.871135Z","shell.execute_reply":"2023-03-27T23:56:57.958053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"OK, The model really not so good, We need train it with more data and spend time to do finetune work. If I make any progress, I will update them :)","metadata":{}},{"cell_type":"markdown","source":"\n\n\n","metadata":{}}]}