diff --git a/networks/ParticleTransformer_Dynamic_Quantized.ipynb b/networks/ParticleTransformer_Dynamic_Quantized.ipynb new file mode 100644 index 0000000..2fa3219 --- /dev/null +++ b/networks/ParticleTransformer_Dynamic_Quantized.ipynb @@ -0,0 +1,1350 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# !pip install awkward\n", + "# !pip install uproot\n", + "# !pip install vector\n", + "# !pip install requests\n", + "# !pip install torch\n", + "# !pip install tqdm\n", + "# !pip install fairseq\n", + "# !pip install tensorboardX" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# to include the files that has modified ParticleTransformer class for quantization\n", + "\n", + "import sys\n", + "if '/part-vol-2/weaver-core/particle_transformer/notebooks/Efficient-Transformer-Tests' not in sys.path:\n", + " sys.path.append('/part-vol-2/weaver-core/particle_transformer/notebooks/Efficient-Transformer-Tests')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# importing libraries and modified ParticleTransformer class for quantization\n", + "\n", + "import numpy as np\n", + "import awkward as ak\n", + "import uproot\n", + "import vector\n", + "vector.register_awkward()\n", + "import os\n", + "import shutil\n", + "import zipfile\n", + "import tarfile\n", + "import urllib\n", + "import requests\n", + "from tqdm import tqdm\n", + "import torch\n", + "#from weaver.nn.model.ParticleTransformer import ParticleTransformer\n", + "from ParticleTransformer_updated import ParticleTransformer\n", + "from ParticleTransformer_updated_quant_weights import ParticleTransformer as ParticleTransformer_quant\n", + "from weaver.utils.logger import _logger\n", + "import torch.optim as optim\n", + "import time\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Importing data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# importing data\n", + "\n", + "def build_features_and_labels(tree, transform_features=True):\n", + " # load arrays from the tree\n", + " a = tree.arrays(filter_name=['part_*', 'jet_pt', 'jet_energy', 'label_*'])\n", + "\n", + " # compute new features\n", + " a['part_mask'] = ak.ones_like(a['part_energy'])\n", + " a['part_pt'] = np.hypot(a['part_px'], a['part_py'])\n", + " a['part_pt_log'] = np.log(a['part_pt'])\n", + " a['part_e_log'] = np.log(a['part_energy'])\n", + " a['part_logptrel'] = np.log(a['part_pt']/a['jet_pt'])\n", + " a['part_logerel'] = np.log(a['part_energy']/a['jet_energy'])\n", + " a['part_deltaR'] = np.hypot(a['part_deta'], a['part_dphi'])\n", + " a['part_d0'] = np.tanh(a['part_d0val'])\n", + " a['part_dz'] = np.tanh(a['part_dzval'])\n", + "\n", + " # apply standardization\n", + " if transform_features:\n", + " a['part_pt_log'] = (a['part_pt_log'] - 1.7) * 0.7\n", + " a['part_e_log'] = (a['part_e_log'] - 2.0) * 0.7\n", + " a['part_logptrel'] = (a['part_logptrel'] - (-4.7)) * 0.7\n", + " a['part_logerel'] = (a['part_logerel'] - (-4.7)) * 0.7\n", + " a['part_deltaR'] = (a['part_deltaR'] - 0.2) * 4.0\n", + " a['part_d0err'] = _clip(a['part_d0err'], 0, 1)\n", + " a['part_dzerr'] = _clip(a['part_dzerr'], 0, 1)\n", + "\n", + " feature_list = {\n", + " 'pf_points': ['part_deta', 'part_dphi'], # not used in ParT\n", + " 'pf_features': [\n", + " 'part_pt_log', \n", + " 'part_e_log',\n", + " 'part_logptrel',\n", + " 'part_logerel',\n", + " 'part_deltaR',\n", + " 'part_charge',\n", + " 'part_isChargedHadron',\n", + " 'part_isNeutralHadron',\n", + " 'part_isPhoton',\n", + " 'part_isElectron',\n", + " 'part_isMuon',\n", + " 'part_d0',\n", + " 'part_d0err',\n", + " 'part_dz',\n", + " 'part_dzerr',\n", + " 'part_deta',\n", + " 'part_dphi',\n", + " ],\n", + " 'pf_vectors': [\n", + " 'part_px',\n", + " 'part_py',\n", + " 'part_pz',\n", + " 'part_energy',\n", + " ],\n", + " 'pf_mask': ['part_mask']\n", + " }\n", + "\n", + " out = {}\n", + " for k, names in feature_list.items():\n", + " out[k] = np.stack([_pad(a[n], maxlen=128).to_numpy() for n in names], axis=1)\n", + "\n", + " label_list = ['label_QCD', 'label_Hbb', 'label_Hcc', 'label_Hgg', 'label_H4q', 'label_Hqql', 'label_Zqq', 'label_Wqq', 'label_Tbqq', 'label_Tbl']\n", + " out['label'] = np.stack([a[n].to_numpy().astype('int') for n in label_list], axis=1)\n", + " \n", + " return out\n", + "\n", + "def _clip(a, a_min, a_max):\n", + " try:\n", + " return np.clip(a, a_min, a_max)\n", + " except ValueError:\n", + " return ak.unflatten(np.clip(ak.flatten(a), a_min, a_max), ak.num(a))\n", + " \n", + "def _pad(a, maxlen, value=0, dtype='float32'):\n", + " if isinstance(a, np.ndarray) and a.ndim >= 2 and a.shape[1] == maxlen:\n", + " return a\n", + " elif isinstance(a, ak.Array):\n", + " if a.ndim == 1:\n", + " a = ak.unflatten(a, 1)\n", + " a = ak.fill_none(ak.pad_none(a, maxlen, clip=True), value)\n", + " return ak.values_astype(a, dtype)\n", + " else:\n", + " x = (np.ones((len(a), maxlen)) * value).astype(dtype)\n", + " for idx, s in enumerate(a):\n", + " if not len(s):\n", + " continue\n", + " trunc = s[:maxlen].astype(dtype)\n", + " x[idx, :len(trunc)] = trunc\n", + " return x\n", + "\n", + "\n", + "tree = uproot.open('/part-vol-2/weaver-core/particle_transformer/notebooks/JetClass_example_100k.root')['tree']\n", + "\n", + "table = build_features_and_labels(tree)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# creating data\n", + "\n", + "x_particles = table['pf_features']\n", + "x_jets = table['pf_vectors']\n", + "y = table['label']\n", + "x_points = table['pf_points']\n", + "x_mask = table['pf_mask']\n", + "\n", + "r_indexes = np.arange(len(x_particles))\n", + "np.random.shuffle(r_indexes)\n", + "\n", + "# train\n", + "a = 100000\n", + "x_particles_train=x_particles[r_indexes][0:a]\n", + "x_jets_train=x_jets[r_indexes][0:a]\n", + "y_train=y[r_indexes][0:a]\n", + "x_points_train=x_points[r_indexes][0:a]\n", + "x_mask_train=x_mask[r_indexes][0:a]\n", + "\n", + "# test\n", + "a = 500\n", + "x_part_test=x_particles[r_indexes][20000:20000 + a]\n", + "x_jet_test=x_jets[r_indexes][20000:20000 + a]\n", + "y_test=y[r_indexes][20000:20000 + a]\n", + "x_points_test=x_points[r_indexes][20000:20000 + a]\n", + "x_mask_test=x_mask[r_indexes][20000:20000 + a]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Importing models" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-08-24 20:15:53 | INFO | weaver | Model config: {'input_dim': 17, 'num_classes': 10, 'pair_input_dim': 4, 'use_pre_activation_pair': False, 'embed_dims': [128, 512, 128], 'pair_embed_dims': [64, 64, 64], 'num_heads': 8, 'num_layers': 8, 'num_cls_layers': 2, 'block_params': None, 'cls_block_params': {'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, 'fc_params': [], 'activation': 'gelu', 'trim': True, 'for_inference': False}\n", + "2024-08-24 20:15:53 | INFO | weaver | cfg_block: {'embed_dim': 128, 'num_heads': 8, 'ffn_ratio': 4, 'dropout': 0.1, 'attn_dropout': 0.1, 'activation_dropout': 0.1, 'add_bias_kv': False, 'activation': 'gelu', 'scale_fc': True, 'scale_attn': True, 'scale_heads': True, 'scale_resids': True}\n", + "2024-08-24 20:15:53 | INFO | weaver | cfg_cls_block: {'embed_dim': 128, 'num_heads': 8, 'ffn_ratio': 4, 'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0, 'add_bias_kv': False, 'activation': 'gelu', 'scale_fc': True, 'scale_attn': True, 'scale_heads': True, 'scale_resids': True}\n" + ] + }, + { + "data": { + "text/plain": [ + "ParticleTransformerWrapper(\n", + " (mod): ParticleTransformer(\n", + " (trimmer): SequenceTrimmer()\n", + " (embed): Embed(\n", + " (input_bn): BatchNorm1d(17, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (embed): Sequential(\n", + " (0): LayerNorm((17,), eps=1e-05, elementwise_affine=True)\n", + " (1): Linear(in_features=17, out_features=128, bias=True)\n", + " (2): GELU(approximate='none')\n", + " (3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (4): Linear(in_features=128, out_features=512, bias=True)\n", + " (5): GELU(approximate='none')\n", + " (6): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (7): Linear(in_features=512, out_features=128, bias=True)\n", + " (8): GELU(approximate='none')\n", + " )\n", + " )\n", + " (pair_embed): PairEmbed(\n", + " (embed): Sequential(\n", + " (0): BatchNorm1d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (1): Conv1d(4, 64, kernel_size=(1,), stride=(1,))\n", + " (2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (3): GELU(approximate='none')\n", + " (4): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (6): GELU(approximate='none')\n", + " (7): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (8): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (9): GELU(approximate='none')\n", + " (10): Conv1d(64, 8, kernel_size=(1,), stride=(1,))\n", + " (11): BatchNorm1d(8, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (12): GELU(approximate='none')\n", + " )\n", + " )\n", + " (blocks): ModuleList(\n", + " (0-7): 8 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): MultiheadAttention(\n", + " (out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0.1, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0.1, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (cls_blocks): ModuleList(\n", + " (0-1): 2 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): MultiheadAttention(\n", + " (out_proj): NonDynamicallyQuantizableLinear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc): Sequential(\n", + " (0): Linear(in_features=128, out_features=10, bias=True)\n", + " )\n", + " )\n", + ")" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Actual ParticleTransformer model\n", + "\n", + "class ParticleTransformerWrapper(torch.nn.Module):\n", + " def __init__(self, **kwargs) -> None:\n", + " super().__init__()\n", + " self.mod = ParticleTransformer(**kwargs)\n", + " self.attention_matrix = None \n", + " self.interactionMatrix = None\n", + " @torch.jit.ignore\n", + " def no_weight_decay(self):\n", + " return {'mod.cls_token', }\n", + "\n", + " def forward(self, points, features, lorentz_vectors, mask):\n", + " output = self.mod(features, v=lorentz_vectors, mask=mask)\n", + " self.attention_matrix = self.mod.getAttention()\n", + " self.interactionMatrix = self.mod.getInteraction()\n", + " return output\n", + " \n", + " def get_attention_matrix(self):\n", + " return self.attention_matrix\n", + " def get_interactionMatrix(self):\n", + " return self.interactionMatrix\n", + "\n", + "# me \n", + "\n", + "def get_model(**kwargs):\n", + "\n", + " cfg = dict(\n", + " input_dim=17,\n", + " num_classes=10,\n", + " # network configurations\n", + " pair_input_dim=4,\n", + " use_pre_activation_pair=False,\n", + " embed_dims=[128, 512, 128],\n", + " pair_embed_dims= [64,64,64],\n", + " num_heads=8,\n", + " num_layers=8, # make it 8 \n", + " num_cls_layers=2,\n", + " block_params=None,\n", + " cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0},\n", + " fc_params=[],\n", + " activation='gelu',\n", + " # misc\n", + " trim=True,\n", + " for_inference=False,\n", + " )\n", + " cfg.update(**kwargs)\n", + " _logger.info('Model config: %s' % str(cfg))\n", + "\n", + " model = ParticleTransformerWrapper(**cfg)\n", + "\n", + " model_info = {\n", + " }\n", + "\n", + " return model, model_info\n", + "\n", + "base_model, _ = get_model()\n", + "\n", + "base_model" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-08-24 20:15:54 | INFO | weaver | Model config: {'input_dim': 17, 'num_classes': 10, 'pair_input_dim': 4, 'use_pre_activation_pair': False, 'embed_dims': [128, 512, 128], 'pair_embed_dims': [64, 64, 64], 'num_heads': 8, 'num_layers': 8, 'num_cls_layers': 2, 'block_params': None, 'cls_block_params': {'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, 'fc_params': [], 'activation': 'gelu', 'trim': True, 'for_inference': False}\n", + "2024-08-24 20:15:54 | INFO | weaver | cfg_block: {'embed_dim': 128, 'num_heads': 8, 'ffn_ratio': 4, 'dropout': 0.1, 'attn_dropout': 0.1, 'activation_dropout': 0.1, 'add_bias_kv': False, 'activation': 'gelu', 'scale_fc': True, 'scale_attn': True, 'scale_heads': True, 'scale_resids': True}\n", + "2024-08-24 20:15:54 | INFO | weaver | cfg_cls_block: {'embed_dim': 128, 'num_heads': 8, 'ffn_ratio': 4, 'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0, 'add_bias_kv': False, 'activation': 'gelu', 'scale_fc': True, 'scale_attn': True, 'scale_heads': True, 'scale_resids': True}\n" + ] + }, + { + "data": { + "text/plain": [ + "ParticleTransformerWrapper(\n", + " (mod): ParticleTransformer(\n", + " (trimmer): SequenceTrimmer()\n", + " (embed): Embed(\n", + " (input_bn): BatchNorm1d(17, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (embed): Sequential(\n", + " (0): LayerNorm((17,), eps=1e-05, elementwise_affine=True)\n", + " (1): Linear(in_features=17, out_features=128, bias=True)\n", + " (2): GELU(approximate='none')\n", + " (3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (4): Linear(in_features=128, out_features=512, bias=True)\n", + " (5): GELU(approximate='none')\n", + " (6): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (7): Linear(in_features=512, out_features=128, bias=True)\n", + " (8): GELU(approximate='none')\n", + " )\n", + " )\n", + " (pair_embed): PairEmbed(\n", + " (embed): Sequential(\n", + " (0): BatchNorm1d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (1): Conv1d(4, 64, kernel_size=(1,), stride=(1,))\n", + " (2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (3): GELU(approximate='none')\n", + " (4): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (6): GELU(approximate='none')\n", + " (7): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (8): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (9): GELU(approximate='none')\n", + " (10): Conv1d(64, 8, kernel_size=(1,), stride=(1,))\n", + " (11): BatchNorm1d(8, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (12): GELU(approximate='none')\n", + " )\n", + " )\n", + " (blocks): ModuleList(\n", + " (0-7): 8 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): QuantizableMultiheadAttention(\n", + " (out_proj): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_Q): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_K): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_V): Linear(in_features=128, out_features=128, bias=True)\n", + " (q_scaling_product): FloatFunctional(\n", + " (activation_post_process): Identity()\n", + " )\n", + " (quant_attn_output): QuantStub()\n", + " (quant_attn_output_weights): QuantStub()\n", + " (dequant_q): DeQuantStub()\n", + " (dequant_k): DeQuantStub()\n", + " (dequant_v): DeQuantStub()\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0.1, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0.1, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (cls_blocks): ModuleList(\n", + " (0-1): 2 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): QuantizableMultiheadAttention(\n", + " (out_proj): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_Q): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_K): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_V): Linear(in_features=128, out_features=128, bias=True)\n", + " (q_scaling_product): FloatFunctional(\n", + " (activation_post_process): Identity()\n", + " )\n", + " (quant_attn_output): QuantStub()\n", + " (quant_attn_output_weights): QuantStub()\n", + " (dequant_q): DeQuantStub()\n", + " (dequant_k): DeQuantStub()\n", + " (dequant_v): DeQuantStub()\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc): Sequential(\n", + " (0): Linear(in_features=128, out_features=10, bias=True)\n", + " )\n", + " )\n", + ")" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Quantizable ParticleTransformer model\n", + "\n", + "\n", + "class ParticleTransformerWrapper(torch.nn.Module):\n", + " def __init__(self, **kwargs) -> None:\n", + " super().__init__()\n", + " self.mod = ParticleTransformer_quant(**kwargs)\n", + " self.attention_matrix = None \n", + " self.interactionMatrix = None\n", + " @torch.jit.ignore\n", + " def no_weight_decay(self):\n", + " return {'mod.cls_token', }\n", + "\n", + " def forward(self, points, features, lorentz_vectors, mask):\n", + " output = self.mod(features, v=lorentz_vectors, mask=mask)\n", + " self.attention_matrix = self.mod.getAttention()\n", + " self.interactionMatrix = self.mod.getInteraction()\n", + " return output\n", + " \n", + " def get_attention_matrix(self):\n", + " return self.attention_matrix\n", + " def get_interactionMatrix(self):\n", + " return self.interactionMatrix\n", + " \n", + "def get_model(**kwargs):\n", + "\n", + " cfg = dict(\n", + " input_dim=17,\n", + " num_classes=10,\n", + " # network configurations\n", + " pair_input_dim=4,\n", + " use_pre_activation_pair=False,\n", + " embed_dims=[128, 512, 128],\n", + " pair_embed_dims= [64,64,64],\n", + " num_heads=8,\n", + " num_layers=8, # make it 8 \n", + " num_cls_layers=2,\n", + " block_params=None,\n", + " cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0},\n", + " fc_params=[],\n", + " activation='gelu',\n", + " # misc\n", + " trim=True,\n", + " for_inference=False,\n", + " )\n", + " cfg.update(**kwargs)\n", + " _logger.info('Model config: %s' % str(cfg))\n", + "\n", + " model = ParticleTransformerWrapper(**cfg)\n", + "\n", + " model_info = {\n", + " }\n", + "\n", + " return model, model_info\n", + "\n", + "pre_trained_model_quant, _ = get_model()\n", + "\n", + "pre_trained_model_quant" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# setting the loss function and test data\n", + "\n", + "loss_fn = torch.nn.CrossEntropyLoss()\n", + "inp = torch.from_numpy(x_points_test),torch.from_numpy(x_part_test),torch.from_numpy(x_jet_test),torch.from_numpy(x_mask_test)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Loading pretrained weights" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pretrained_dict odict_keys(['mod.cls_token', 'mod.embed.input_bn.weight', 'mod.embed.input_bn.bias', 'mod.embed.input_bn.running_mean', 'mod.embed.input_bn.running_var', 'mod.embed.input_bn.num_batches_tracked', 'mod.embed.embed.0.weight', 'mod.embed.embed.0.bias', 'mod.embed.embed.1.weight', 'mod.embed.embed.1.bias', 'mod.embed.embed.3.weight', 'mod.embed.embed.3.bias', 'mod.embed.embed.4.weight', 'mod.embed.embed.4.bias', 'mod.embed.embed.6.weight', 'mod.embed.embed.6.bias', 'mod.embed.embed.7.weight', 'mod.embed.embed.7.bias', 'mod.pair_embed.embed.0.weight', 'mod.pair_embed.embed.0.bias', 'mod.pair_embed.embed.0.running_mean', 'mod.pair_embed.embed.0.running_var', 'mod.pair_embed.embed.0.num_batches_tracked', 'mod.pair_embed.embed.1.weight', 'mod.pair_embed.embed.1.bias', 'mod.pair_embed.embed.2.weight', 'mod.pair_embed.embed.2.bias', 'mod.pair_embed.embed.2.running_mean', 'mod.pair_embed.embed.2.running_var', 'mod.pair_embed.embed.2.num_batches_tracked', 'mod.pair_embed.embed.4.weight', 'mod.pair_embed.embed.4.bias', 'mod.pair_embed.embed.5.weight', 'mod.pair_embed.embed.5.bias', 'mod.pair_embed.embed.5.running_mean', 'mod.pair_embed.embed.5.running_var', 'mod.pair_embed.embed.5.num_batches_tracked', 'mod.pair_embed.embed.7.weight', 'mod.pair_embed.embed.7.bias', 'mod.pair_embed.embed.8.weight', 'mod.pair_embed.embed.8.bias', 'mod.pair_embed.embed.8.running_mean', 'mod.pair_embed.embed.8.running_var', 'mod.pair_embed.embed.8.num_batches_tracked', 'mod.pair_embed.embed.10.weight', 'mod.pair_embed.embed.10.bias', 'mod.pair_embed.embed.11.weight', 'mod.pair_embed.embed.11.bias', 'mod.pair_embed.embed.11.running_mean', 'mod.pair_embed.embed.11.running_var', 'mod.pair_embed.embed.11.num_batches_tracked', 'mod.blocks.0.c_attn', 'mod.blocks.0.w_resid', 'mod.blocks.0.pre_attn_norm.weight', 'mod.blocks.0.pre_attn_norm.bias', 'mod.blocks.0.attn.in_proj_weight', 'mod.blocks.0.attn.in_proj_bias', 'mod.blocks.0.attn.out_proj.weight', 'mod.blocks.0.attn.out_proj.bias', 'mod.blocks.0.post_attn_norm.weight', 'mod.blocks.0.post_attn_norm.bias', 'mod.blocks.0.pre_fc_norm.weight', 'mod.blocks.0.pre_fc_norm.bias', 'mod.blocks.0.fc1.weight', 'mod.blocks.0.fc1.bias', 'mod.blocks.0.post_fc_norm.weight', 'mod.blocks.0.post_fc_norm.bias', 'mod.blocks.0.fc2.weight', 'mod.blocks.0.fc2.bias', 'mod.blocks.1.c_attn', 'mod.blocks.1.w_resid', 'mod.blocks.1.pre_attn_norm.weight', 'mod.blocks.1.pre_attn_norm.bias', 'mod.blocks.1.attn.in_proj_weight', 'mod.blocks.1.attn.in_proj_bias', 'mod.blocks.1.attn.out_proj.weight', 'mod.blocks.1.attn.out_proj.bias', 'mod.blocks.1.post_attn_norm.weight', 'mod.blocks.1.post_attn_norm.bias', 'mod.blocks.1.pre_fc_norm.weight', 'mod.blocks.1.pre_fc_norm.bias', 'mod.blocks.1.fc1.weight', 'mod.blocks.1.fc1.bias', 'mod.blocks.1.post_fc_norm.weight', 'mod.blocks.1.post_fc_norm.bias', 'mod.blocks.1.fc2.weight', 'mod.blocks.1.fc2.bias', 'mod.blocks.2.c_attn', 'mod.blocks.2.w_resid', 'mod.blocks.2.pre_attn_norm.weight', 'mod.blocks.2.pre_attn_norm.bias', 'mod.blocks.2.attn.in_proj_weight', 'mod.blocks.2.attn.in_proj_bias', 'mod.blocks.2.attn.out_proj.weight', 'mod.blocks.2.attn.out_proj.bias', 'mod.blocks.2.post_attn_norm.weight', 'mod.blocks.2.post_attn_norm.bias', 'mod.blocks.2.pre_fc_norm.weight', 'mod.blocks.2.pre_fc_norm.bias', 'mod.blocks.2.fc1.weight', 'mod.blocks.2.fc1.bias', 'mod.blocks.2.post_fc_norm.weight', 'mod.blocks.2.post_fc_norm.bias', 'mod.blocks.2.fc2.weight', 'mod.blocks.2.fc2.bias', 'mod.blocks.3.c_attn', 'mod.blocks.3.w_resid', 'mod.blocks.3.pre_attn_norm.weight', 'mod.blocks.3.pre_attn_norm.bias', 'mod.blocks.3.attn.in_proj_weight', 'mod.blocks.3.attn.in_proj_bias', 'mod.blocks.3.attn.out_proj.weight', 'mod.blocks.3.attn.out_proj.bias', 'mod.blocks.3.post_attn_norm.weight', 'mod.blocks.3.post_attn_norm.bias', 'mod.blocks.3.pre_fc_norm.weight', 'mod.blocks.3.pre_fc_norm.bias', 'mod.blocks.3.fc1.weight', 'mod.blocks.3.fc1.bias', 'mod.blocks.3.post_fc_norm.weight', 'mod.blocks.3.post_fc_norm.bias', 'mod.blocks.3.fc2.weight', 'mod.blocks.3.fc2.bias', 'mod.blocks.4.c_attn', 'mod.blocks.4.w_resid', 'mod.blocks.4.pre_attn_norm.weight', 'mod.blocks.4.pre_attn_norm.bias', 'mod.blocks.4.attn.in_proj_weight', 'mod.blocks.4.attn.in_proj_bias', 'mod.blocks.4.attn.out_proj.weight', 'mod.blocks.4.attn.out_proj.bias', 'mod.blocks.4.post_attn_norm.weight', 'mod.blocks.4.post_attn_norm.bias', 'mod.blocks.4.pre_fc_norm.weight', 'mod.blocks.4.pre_fc_norm.bias', 'mod.blocks.4.fc1.weight', 'mod.blocks.4.fc1.bias', 'mod.blocks.4.post_fc_norm.weight', 'mod.blocks.4.post_fc_norm.bias', 'mod.blocks.4.fc2.weight', 'mod.blocks.4.fc2.bias', 'mod.blocks.5.c_attn', 'mod.blocks.5.w_resid', 'mod.blocks.5.pre_attn_norm.weight', 'mod.blocks.5.pre_attn_norm.bias', 'mod.blocks.5.attn.in_proj_weight', 'mod.blocks.5.attn.in_proj_bias', 'mod.blocks.5.attn.out_proj.weight', 'mod.blocks.5.attn.out_proj.bias', 'mod.blocks.5.post_attn_norm.weight', 'mod.blocks.5.post_attn_norm.bias', 'mod.blocks.5.pre_fc_norm.weight', 'mod.blocks.5.pre_fc_norm.bias', 'mod.blocks.5.fc1.weight', 'mod.blocks.5.fc1.bias', 'mod.blocks.5.post_fc_norm.weight', 'mod.blocks.5.post_fc_norm.bias', 'mod.blocks.5.fc2.weight', 'mod.blocks.5.fc2.bias', 'mod.blocks.6.c_attn', 'mod.blocks.6.w_resid', 'mod.blocks.6.pre_attn_norm.weight', 'mod.blocks.6.pre_attn_norm.bias', 'mod.blocks.6.attn.in_proj_weight', 'mod.blocks.6.attn.in_proj_bias', 'mod.blocks.6.attn.out_proj.weight', 'mod.blocks.6.attn.out_proj.bias', 'mod.blocks.6.post_attn_norm.weight', 'mod.blocks.6.post_attn_norm.bias', 'mod.blocks.6.pre_fc_norm.weight', 'mod.blocks.6.pre_fc_norm.bias', 'mod.blocks.6.fc1.weight', 'mod.blocks.6.fc1.bias', 'mod.blocks.6.post_fc_norm.weight', 'mod.blocks.6.post_fc_norm.bias', 'mod.blocks.6.fc2.weight', 'mod.blocks.6.fc2.bias', 'mod.blocks.7.c_attn', 'mod.blocks.7.w_resid', 'mod.blocks.7.pre_attn_norm.weight', 'mod.blocks.7.pre_attn_norm.bias', 'mod.blocks.7.attn.in_proj_weight', 'mod.blocks.7.attn.in_proj_bias', 'mod.blocks.7.attn.out_proj.weight', 'mod.blocks.7.attn.out_proj.bias', 'mod.blocks.7.post_attn_norm.weight', 'mod.blocks.7.post_attn_norm.bias', 'mod.blocks.7.pre_fc_norm.weight', 'mod.blocks.7.pre_fc_norm.bias', 'mod.blocks.7.fc1.weight', 'mod.blocks.7.fc1.bias', 'mod.blocks.7.post_fc_norm.weight', 'mod.blocks.7.post_fc_norm.bias', 'mod.blocks.7.fc2.weight', 'mod.blocks.7.fc2.bias', 'mod.cls_blocks.0.c_attn', 'mod.cls_blocks.0.w_resid', 'mod.cls_blocks.0.pre_attn_norm.weight', 'mod.cls_blocks.0.pre_attn_norm.bias', 'mod.cls_blocks.0.attn.in_proj_weight', 'mod.cls_blocks.0.attn.in_proj_bias', 'mod.cls_blocks.0.attn.out_proj.weight', 'mod.cls_blocks.0.attn.out_proj.bias', 'mod.cls_blocks.0.post_attn_norm.weight', 'mod.cls_blocks.0.post_attn_norm.bias', 'mod.cls_blocks.0.pre_fc_norm.weight', 'mod.cls_blocks.0.pre_fc_norm.bias', 'mod.cls_blocks.0.fc1.weight', 'mod.cls_blocks.0.fc1.bias', 'mod.cls_blocks.0.post_fc_norm.weight', 'mod.cls_blocks.0.post_fc_norm.bias', 'mod.cls_blocks.0.fc2.weight', 'mod.cls_blocks.0.fc2.bias', 'mod.cls_blocks.1.c_attn', 'mod.cls_blocks.1.w_resid', 'mod.cls_blocks.1.pre_attn_norm.weight', 'mod.cls_blocks.1.pre_attn_norm.bias', 'mod.cls_blocks.1.attn.in_proj_weight', 'mod.cls_blocks.1.attn.in_proj_bias', 'mod.cls_blocks.1.attn.out_proj.weight', 'mod.cls_blocks.1.attn.out_proj.bias', 'mod.cls_blocks.1.post_attn_norm.weight', 'mod.cls_blocks.1.post_attn_norm.bias', 'mod.cls_blocks.1.pre_fc_norm.weight', 'mod.cls_blocks.1.pre_fc_norm.bias', 'mod.cls_blocks.1.fc1.weight', 'mod.cls_blocks.1.fc1.bias', 'mod.cls_blocks.1.post_fc_norm.weight', 'mod.cls_blocks.1.post_fc_norm.bias', 'mod.cls_blocks.1.fc2.weight', 'mod.cls_blocks.1.fc2.bias', 'mod.norm.weight', 'mod.norm.bias', 'mod.fc.0.weight', 'mod.fc.0.bias'])\n", + "model_dict odict_keys(['mod.cls_token', 'mod.embed.input_bn.weight', 'mod.embed.input_bn.bias', 'mod.embed.input_bn.running_mean', 'mod.embed.input_bn.running_var', 'mod.embed.input_bn.num_batches_tracked', 'mod.embed.embed.0.weight', 'mod.embed.embed.0.bias', 'mod.embed.embed.1.weight', 'mod.embed.embed.1.bias', 'mod.embed.embed.3.weight', 'mod.embed.embed.3.bias', 'mod.embed.embed.4.weight', 'mod.embed.embed.4.bias', 'mod.embed.embed.6.weight', 'mod.embed.embed.6.bias', 'mod.embed.embed.7.weight', 'mod.embed.embed.7.bias', 'mod.pair_embed.embed.0.weight', 'mod.pair_embed.embed.0.bias', 'mod.pair_embed.embed.0.running_mean', 'mod.pair_embed.embed.0.running_var', 'mod.pair_embed.embed.0.num_batches_tracked', 'mod.pair_embed.embed.1.weight', 'mod.pair_embed.embed.1.bias', 'mod.pair_embed.embed.2.weight', 'mod.pair_embed.embed.2.bias', 'mod.pair_embed.embed.2.running_mean', 'mod.pair_embed.embed.2.running_var', 'mod.pair_embed.embed.2.num_batches_tracked', 'mod.pair_embed.embed.4.weight', 'mod.pair_embed.embed.4.bias', 'mod.pair_embed.embed.5.weight', 'mod.pair_embed.embed.5.bias', 'mod.pair_embed.embed.5.running_mean', 'mod.pair_embed.embed.5.running_var', 'mod.pair_embed.embed.5.num_batches_tracked', 'mod.pair_embed.embed.7.weight', 'mod.pair_embed.embed.7.bias', 'mod.pair_embed.embed.8.weight', 'mod.pair_embed.embed.8.bias', 'mod.pair_embed.embed.8.running_mean', 'mod.pair_embed.embed.8.running_var', 'mod.pair_embed.embed.8.num_batches_tracked', 'mod.pair_embed.embed.10.weight', 'mod.pair_embed.embed.10.bias', 'mod.pair_embed.embed.11.weight', 'mod.pair_embed.embed.11.bias', 'mod.pair_embed.embed.11.running_mean', 'mod.pair_embed.embed.11.running_var', 'mod.pair_embed.embed.11.num_batches_tracked', 'mod.blocks.0.c_attn', 'mod.blocks.0.w_resid', 'mod.blocks.0.pre_attn_norm.weight', 'mod.blocks.0.pre_attn_norm.bias', 'mod.blocks.0.attn.in_proj_weight', 'mod.blocks.0.attn.in_proj_bias', 'mod.blocks.0.attn.out_proj.weight', 'mod.blocks.0.attn.out_proj.bias', 'mod.blocks.0.post_attn_norm.weight', 'mod.blocks.0.post_attn_norm.bias', 'mod.blocks.0.pre_fc_norm.weight', 'mod.blocks.0.pre_fc_norm.bias', 'mod.blocks.0.fc1.weight', 'mod.blocks.0.fc1.bias', 'mod.blocks.0.post_fc_norm.weight', 'mod.blocks.0.post_fc_norm.bias', 'mod.blocks.0.fc2.weight', 'mod.blocks.0.fc2.bias', 'mod.blocks.1.c_attn', 'mod.blocks.1.w_resid', 'mod.blocks.1.pre_attn_norm.weight', 'mod.blocks.1.pre_attn_norm.bias', 'mod.blocks.1.attn.in_proj_weight', 'mod.blocks.1.attn.in_proj_bias', 'mod.blocks.1.attn.out_proj.weight', 'mod.blocks.1.attn.out_proj.bias', 'mod.blocks.1.post_attn_norm.weight', 'mod.blocks.1.post_attn_norm.bias', 'mod.blocks.1.pre_fc_norm.weight', 'mod.blocks.1.pre_fc_norm.bias', 'mod.blocks.1.fc1.weight', 'mod.blocks.1.fc1.bias', 'mod.blocks.1.post_fc_norm.weight', 'mod.blocks.1.post_fc_norm.bias', 'mod.blocks.1.fc2.weight', 'mod.blocks.1.fc2.bias', 'mod.blocks.2.c_attn', 'mod.blocks.2.w_resid', 'mod.blocks.2.pre_attn_norm.weight', 'mod.blocks.2.pre_attn_norm.bias', 'mod.blocks.2.attn.in_proj_weight', 'mod.blocks.2.attn.in_proj_bias', 'mod.blocks.2.attn.out_proj.weight', 'mod.blocks.2.attn.out_proj.bias', 'mod.blocks.2.post_attn_norm.weight', 'mod.blocks.2.post_attn_norm.bias', 'mod.blocks.2.pre_fc_norm.weight', 'mod.blocks.2.pre_fc_norm.bias', 'mod.blocks.2.fc1.weight', 'mod.blocks.2.fc1.bias', 'mod.blocks.2.post_fc_norm.weight', 'mod.blocks.2.post_fc_norm.bias', 'mod.blocks.2.fc2.weight', 'mod.blocks.2.fc2.bias', 'mod.blocks.3.c_attn', 'mod.blocks.3.w_resid', 'mod.blocks.3.pre_attn_norm.weight', 'mod.blocks.3.pre_attn_norm.bias', 'mod.blocks.3.attn.in_proj_weight', 'mod.blocks.3.attn.in_proj_bias', 'mod.blocks.3.attn.out_proj.weight', 'mod.blocks.3.attn.out_proj.bias', 'mod.blocks.3.post_attn_norm.weight', 'mod.blocks.3.post_attn_norm.bias', 'mod.blocks.3.pre_fc_norm.weight', 'mod.blocks.3.pre_fc_norm.bias', 'mod.blocks.3.fc1.weight', 'mod.blocks.3.fc1.bias', 'mod.blocks.3.post_fc_norm.weight', 'mod.blocks.3.post_fc_norm.bias', 'mod.blocks.3.fc2.weight', 'mod.blocks.3.fc2.bias', 'mod.blocks.4.c_attn', 'mod.blocks.4.w_resid', 'mod.blocks.4.pre_attn_norm.weight', 'mod.blocks.4.pre_attn_norm.bias', 'mod.blocks.4.attn.in_proj_weight', 'mod.blocks.4.attn.in_proj_bias', 'mod.blocks.4.attn.out_proj.weight', 'mod.blocks.4.attn.out_proj.bias', 'mod.blocks.4.post_attn_norm.weight', 'mod.blocks.4.post_attn_norm.bias', 'mod.blocks.4.pre_fc_norm.weight', 'mod.blocks.4.pre_fc_norm.bias', 'mod.blocks.4.fc1.weight', 'mod.blocks.4.fc1.bias', 'mod.blocks.4.post_fc_norm.weight', 'mod.blocks.4.post_fc_norm.bias', 'mod.blocks.4.fc2.weight', 'mod.blocks.4.fc2.bias', 'mod.blocks.5.c_attn', 'mod.blocks.5.w_resid', 'mod.blocks.5.pre_attn_norm.weight', 'mod.blocks.5.pre_attn_norm.bias', 'mod.blocks.5.attn.in_proj_weight', 'mod.blocks.5.attn.in_proj_bias', 'mod.blocks.5.attn.out_proj.weight', 'mod.blocks.5.attn.out_proj.bias', 'mod.blocks.5.post_attn_norm.weight', 'mod.blocks.5.post_attn_norm.bias', 'mod.blocks.5.pre_fc_norm.weight', 'mod.blocks.5.pre_fc_norm.bias', 'mod.blocks.5.fc1.weight', 'mod.blocks.5.fc1.bias', 'mod.blocks.5.post_fc_norm.weight', 'mod.blocks.5.post_fc_norm.bias', 'mod.blocks.5.fc2.weight', 'mod.blocks.5.fc2.bias', 'mod.blocks.6.c_attn', 'mod.blocks.6.w_resid', 'mod.blocks.6.pre_attn_norm.weight', 'mod.blocks.6.pre_attn_norm.bias', 'mod.blocks.6.attn.in_proj_weight', 'mod.blocks.6.attn.in_proj_bias', 'mod.blocks.6.attn.out_proj.weight', 'mod.blocks.6.attn.out_proj.bias', 'mod.blocks.6.post_attn_norm.weight', 'mod.blocks.6.post_attn_norm.bias', 'mod.blocks.6.pre_fc_norm.weight', 'mod.blocks.6.pre_fc_norm.bias', 'mod.blocks.6.fc1.weight', 'mod.blocks.6.fc1.bias', 'mod.blocks.6.post_fc_norm.weight', 'mod.blocks.6.post_fc_norm.bias', 'mod.blocks.6.fc2.weight', 'mod.blocks.6.fc2.bias', 'mod.blocks.7.c_attn', 'mod.blocks.7.w_resid', 'mod.blocks.7.pre_attn_norm.weight', 'mod.blocks.7.pre_attn_norm.bias', 'mod.blocks.7.attn.in_proj_weight', 'mod.blocks.7.attn.in_proj_bias', 'mod.blocks.7.attn.out_proj.weight', 'mod.blocks.7.attn.out_proj.bias', 'mod.blocks.7.post_attn_norm.weight', 'mod.blocks.7.post_attn_norm.bias', 'mod.blocks.7.pre_fc_norm.weight', 'mod.blocks.7.pre_fc_norm.bias', 'mod.blocks.7.fc1.weight', 'mod.blocks.7.fc1.bias', 'mod.blocks.7.post_fc_norm.weight', 'mod.blocks.7.post_fc_norm.bias', 'mod.blocks.7.fc2.weight', 'mod.blocks.7.fc2.bias', 'mod.cls_blocks.0.c_attn', 'mod.cls_blocks.0.w_resid', 'mod.cls_blocks.0.pre_attn_norm.weight', 'mod.cls_blocks.0.pre_attn_norm.bias', 'mod.cls_blocks.0.attn.in_proj_weight', 'mod.cls_blocks.0.attn.in_proj_bias', 'mod.cls_blocks.0.attn.out_proj.weight', 'mod.cls_blocks.0.attn.out_proj.bias', 'mod.cls_blocks.0.post_attn_norm.weight', 'mod.cls_blocks.0.post_attn_norm.bias', 'mod.cls_blocks.0.pre_fc_norm.weight', 'mod.cls_blocks.0.pre_fc_norm.bias', 'mod.cls_blocks.0.fc1.weight', 'mod.cls_blocks.0.fc1.bias', 'mod.cls_blocks.0.post_fc_norm.weight', 'mod.cls_blocks.0.post_fc_norm.bias', 'mod.cls_blocks.0.fc2.weight', 'mod.cls_blocks.0.fc2.bias', 'mod.cls_blocks.1.c_attn', 'mod.cls_blocks.1.w_resid', 'mod.cls_blocks.1.pre_attn_norm.weight', 'mod.cls_blocks.1.pre_attn_norm.bias', 'mod.cls_blocks.1.attn.in_proj_weight', 'mod.cls_blocks.1.attn.in_proj_bias', 'mod.cls_blocks.1.attn.out_proj.weight', 'mod.cls_blocks.1.attn.out_proj.bias', 'mod.cls_blocks.1.post_attn_norm.weight', 'mod.cls_blocks.1.post_attn_norm.bias', 'mod.cls_blocks.1.pre_fc_norm.weight', 'mod.cls_blocks.1.pre_fc_norm.bias', 'mod.cls_blocks.1.fc1.weight', 'mod.cls_blocks.1.fc1.bias', 'mod.cls_blocks.1.post_fc_norm.weight', 'mod.cls_blocks.1.post_fc_norm.bias', 'mod.cls_blocks.1.fc2.weight', 'mod.cls_blocks.1.fc2.bias', 'mod.norm.weight', 'mod.norm.bias', 'mod.fc.0.weight', 'mod.fc.0.bias'])\n", + "model_dict odict_keys(['mod.cls_token', 'mod.embed.input_bn.weight', 'mod.embed.input_bn.bias', 'mod.embed.input_bn.running_mean', 'mod.embed.input_bn.running_var', 'mod.embed.input_bn.num_batches_tracked', 'mod.embed.embed.0.weight', 'mod.embed.embed.0.bias', 'mod.embed.embed.1.weight', 'mod.embed.embed.1.bias', 'mod.embed.embed.3.weight', 'mod.embed.embed.3.bias', 'mod.embed.embed.4.weight', 'mod.embed.embed.4.bias', 'mod.embed.embed.6.weight', 'mod.embed.embed.6.bias', 'mod.embed.embed.7.weight', 'mod.embed.embed.7.bias', 'mod.pair_embed.embed.0.weight', 'mod.pair_embed.embed.0.bias', 'mod.pair_embed.embed.0.running_mean', 'mod.pair_embed.embed.0.running_var', 'mod.pair_embed.embed.0.num_batches_tracked', 'mod.pair_embed.embed.1.weight', 'mod.pair_embed.embed.1.bias', 'mod.pair_embed.embed.2.weight', 'mod.pair_embed.embed.2.bias', 'mod.pair_embed.embed.2.running_mean', 'mod.pair_embed.embed.2.running_var', 'mod.pair_embed.embed.2.num_batches_tracked', 'mod.pair_embed.embed.4.weight', 'mod.pair_embed.embed.4.bias', 'mod.pair_embed.embed.5.weight', 'mod.pair_embed.embed.5.bias', 'mod.pair_embed.embed.5.running_mean', 'mod.pair_embed.embed.5.running_var', 'mod.pair_embed.embed.5.num_batches_tracked', 'mod.pair_embed.embed.7.weight', 'mod.pair_embed.embed.7.bias', 'mod.pair_embed.embed.8.weight', 'mod.pair_embed.embed.8.bias', 'mod.pair_embed.embed.8.running_mean', 'mod.pair_embed.embed.8.running_var', 'mod.pair_embed.embed.8.num_batches_tracked', 'mod.pair_embed.embed.10.weight', 'mod.pair_embed.embed.10.bias', 'mod.pair_embed.embed.11.weight', 'mod.pair_embed.embed.11.bias', 'mod.pair_embed.embed.11.running_mean', 'mod.pair_embed.embed.11.running_var', 'mod.pair_embed.embed.11.num_batches_tracked', 'mod.blocks.0.c_attn', 'mod.blocks.0.w_resid', 'mod.blocks.0.pre_attn_norm.weight', 'mod.blocks.0.pre_attn_norm.bias', 'mod.blocks.0.attn.in_proj_weight', 'mod.blocks.0.attn.in_proj_bias', 'mod.blocks.0.attn.out_proj.weight', 'mod.blocks.0.attn.out_proj.bias', 'mod.blocks.0.post_attn_norm.weight', 'mod.blocks.0.post_attn_norm.bias', 'mod.blocks.0.pre_fc_norm.weight', 'mod.blocks.0.pre_fc_norm.bias', 'mod.blocks.0.fc1.weight', 'mod.blocks.0.fc1.bias', 'mod.blocks.0.post_fc_norm.weight', 'mod.blocks.0.post_fc_norm.bias', 'mod.blocks.0.fc2.weight', 'mod.blocks.0.fc2.bias', 'mod.blocks.1.c_attn', 'mod.blocks.1.w_resid', 'mod.blocks.1.pre_attn_norm.weight', 'mod.blocks.1.pre_attn_norm.bias', 'mod.blocks.1.attn.in_proj_weight', 'mod.blocks.1.attn.in_proj_bias', 'mod.blocks.1.attn.out_proj.weight', 'mod.blocks.1.attn.out_proj.bias', 'mod.blocks.1.post_attn_norm.weight', 'mod.blocks.1.post_attn_norm.bias', 'mod.blocks.1.pre_fc_norm.weight', 'mod.blocks.1.pre_fc_norm.bias', 'mod.blocks.1.fc1.weight', 'mod.blocks.1.fc1.bias', 'mod.blocks.1.post_fc_norm.weight', 'mod.blocks.1.post_fc_norm.bias', 'mod.blocks.1.fc2.weight', 'mod.blocks.1.fc2.bias', 'mod.blocks.2.c_attn', 'mod.blocks.2.w_resid', 'mod.blocks.2.pre_attn_norm.weight', 'mod.blocks.2.pre_attn_norm.bias', 'mod.blocks.2.attn.in_proj_weight', 'mod.blocks.2.attn.in_proj_bias', 'mod.blocks.2.attn.out_proj.weight', 'mod.blocks.2.attn.out_proj.bias', 'mod.blocks.2.post_attn_norm.weight', 'mod.blocks.2.post_attn_norm.bias', 'mod.blocks.2.pre_fc_norm.weight', 'mod.blocks.2.pre_fc_norm.bias', 'mod.blocks.2.fc1.weight', 'mod.blocks.2.fc1.bias', 'mod.blocks.2.post_fc_norm.weight', 'mod.blocks.2.post_fc_norm.bias', 'mod.blocks.2.fc2.weight', 'mod.blocks.2.fc2.bias', 'mod.blocks.3.c_attn', 'mod.blocks.3.w_resid', 'mod.blocks.3.pre_attn_norm.weight', 'mod.blocks.3.pre_attn_norm.bias', 'mod.blocks.3.attn.in_proj_weight', 'mod.blocks.3.attn.in_proj_bias', 'mod.blocks.3.attn.out_proj.weight', 'mod.blocks.3.attn.out_proj.bias', 'mod.blocks.3.post_attn_norm.weight', 'mod.blocks.3.post_attn_norm.bias', 'mod.blocks.3.pre_fc_norm.weight', 'mod.blocks.3.pre_fc_norm.bias', 'mod.blocks.3.fc1.weight', 'mod.blocks.3.fc1.bias', 'mod.blocks.3.post_fc_norm.weight', 'mod.blocks.3.post_fc_norm.bias', 'mod.blocks.3.fc2.weight', 'mod.blocks.3.fc2.bias', 'mod.blocks.4.c_attn', 'mod.blocks.4.w_resid', 'mod.blocks.4.pre_attn_norm.weight', 'mod.blocks.4.pre_attn_norm.bias', 'mod.blocks.4.attn.in_proj_weight', 'mod.blocks.4.attn.in_proj_bias', 'mod.blocks.4.attn.out_proj.weight', 'mod.blocks.4.attn.out_proj.bias', 'mod.blocks.4.post_attn_norm.weight', 'mod.blocks.4.post_attn_norm.bias', 'mod.blocks.4.pre_fc_norm.weight', 'mod.blocks.4.pre_fc_norm.bias', 'mod.blocks.4.fc1.weight', 'mod.blocks.4.fc1.bias', 'mod.blocks.4.post_fc_norm.weight', 'mod.blocks.4.post_fc_norm.bias', 'mod.blocks.4.fc2.weight', 'mod.blocks.4.fc2.bias', 'mod.blocks.5.c_attn', 'mod.blocks.5.w_resid', 'mod.blocks.5.pre_attn_norm.weight', 'mod.blocks.5.pre_attn_norm.bias', 'mod.blocks.5.attn.in_proj_weight', 'mod.blocks.5.attn.in_proj_bias', 'mod.blocks.5.attn.out_proj.weight', 'mod.blocks.5.attn.out_proj.bias', 'mod.blocks.5.post_attn_norm.weight', 'mod.blocks.5.post_attn_norm.bias', 'mod.blocks.5.pre_fc_norm.weight', 'mod.blocks.5.pre_fc_norm.bias', 'mod.blocks.5.fc1.weight', 'mod.blocks.5.fc1.bias', 'mod.blocks.5.post_fc_norm.weight', 'mod.blocks.5.post_fc_norm.bias', 'mod.blocks.5.fc2.weight', 'mod.blocks.5.fc2.bias', 'mod.blocks.6.c_attn', 'mod.blocks.6.w_resid', 'mod.blocks.6.pre_attn_norm.weight', 'mod.blocks.6.pre_attn_norm.bias', 'mod.blocks.6.attn.in_proj_weight', 'mod.blocks.6.attn.in_proj_bias', 'mod.blocks.6.attn.out_proj.weight', 'mod.blocks.6.attn.out_proj.bias', 'mod.blocks.6.post_attn_norm.weight', 'mod.blocks.6.post_attn_norm.bias', 'mod.blocks.6.pre_fc_norm.weight', 'mod.blocks.6.pre_fc_norm.bias', 'mod.blocks.6.fc1.weight', 'mod.blocks.6.fc1.bias', 'mod.blocks.6.post_fc_norm.weight', 'mod.blocks.6.post_fc_norm.bias', 'mod.blocks.6.fc2.weight', 'mod.blocks.6.fc2.bias', 'mod.blocks.7.c_attn', 'mod.blocks.7.w_resid', 'mod.blocks.7.pre_attn_norm.weight', 'mod.blocks.7.pre_attn_norm.bias', 'mod.blocks.7.attn.in_proj_weight', 'mod.blocks.7.attn.in_proj_bias', 'mod.blocks.7.attn.out_proj.weight', 'mod.blocks.7.attn.out_proj.bias', 'mod.blocks.7.post_attn_norm.weight', 'mod.blocks.7.post_attn_norm.bias', 'mod.blocks.7.pre_fc_norm.weight', 'mod.blocks.7.pre_fc_norm.bias', 'mod.blocks.7.fc1.weight', 'mod.blocks.7.fc1.bias', 'mod.blocks.7.post_fc_norm.weight', 'mod.blocks.7.post_fc_norm.bias', 'mod.blocks.7.fc2.weight', 'mod.blocks.7.fc2.bias', 'mod.cls_blocks.0.c_attn', 'mod.cls_blocks.0.w_resid', 'mod.cls_blocks.0.pre_attn_norm.weight', 'mod.cls_blocks.0.pre_attn_norm.bias', 'mod.cls_blocks.0.attn.in_proj_weight', 'mod.cls_blocks.0.attn.in_proj_bias', 'mod.cls_blocks.0.attn.out_proj.weight', 'mod.cls_blocks.0.attn.out_proj.bias', 'mod.cls_blocks.0.post_attn_norm.weight', 'mod.cls_blocks.0.post_attn_norm.bias', 'mod.cls_blocks.0.pre_fc_norm.weight', 'mod.cls_blocks.0.pre_fc_norm.bias', 'mod.cls_blocks.0.fc1.weight', 'mod.cls_blocks.0.fc1.bias', 'mod.cls_blocks.0.post_fc_norm.weight', 'mod.cls_blocks.0.post_fc_norm.bias', 'mod.cls_blocks.0.fc2.weight', 'mod.cls_blocks.0.fc2.bias', 'mod.cls_blocks.1.c_attn', 'mod.cls_blocks.1.w_resid', 'mod.cls_blocks.1.pre_attn_norm.weight', 'mod.cls_blocks.1.pre_attn_norm.bias', 'mod.cls_blocks.1.attn.in_proj_weight', 'mod.cls_blocks.1.attn.in_proj_bias', 'mod.cls_blocks.1.attn.out_proj.weight', 'mod.cls_blocks.1.attn.out_proj.bias', 'mod.cls_blocks.1.post_attn_norm.weight', 'mod.cls_blocks.1.post_attn_norm.bias', 'mod.cls_blocks.1.pre_fc_norm.weight', 'mod.cls_blocks.1.pre_fc_norm.bias', 'mod.cls_blocks.1.fc1.weight', 'mod.cls_blocks.1.fc1.bias', 'mod.cls_blocks.1.post_fc_norm.weight', 'mod.cls_blocks.1.post_fc_norm.bias', 'mod.cls_blocks.1.fc2.weight', 'mod.cls_blocks.1.fc2.bias', 'mod.norm.weight', 'mod.norm.bias', 'mod.fc.0.weight', 'mod.fc.0.bias'])\n", + "pre_trained_model final odict_keys(['mod.cls_token', 'mod.embed.input_bn.weight', 'mod.embed.input_bn.bias', 'mod.embed.input_bn.running_mean', 'mod.embed.input_bn.running_var', 'mod.embed.input_bn.num_batches_tracked', 'mod.embed.embed.0.weight', 'mod.embed.embed.0.bias', 'mod.embed.embed.1.weight', 'mod.embed.embed.1.bias', 'mod.embed.embed.3.weight', 'mod.embed.embed.3.bias', 'mod.embed.embed.4.weight', 'mod.embed.embed.4.bias', 'mod.embed.embed.6.weight', 'mod.embed.embed.6.bias', 'mod.embed.embed.7.weight', 'mod.embed.embed.7.bias', 'mod.pair_embed.embed.0.weight', 'mod.pair_embed.embed.0.bias', 'mod.pair_embed.embed.0.running_mean', 'mod.pair_embed.embed.0.running_var', 'mod.pair_embed.embed.0.num_batches_tracked', 'mod.pair_embed.embed.1.weight', 'mod.pair_embed.embed.1.bias', 'mod.pair_embed.embed.2.weight', 'mod.pair_embed.embed.2.bias', 'mod.pair_embed.embed.2.running_mean', 'mod.pair_embed.embed.2.running_var', 'mod.pair_embed.embed.2.num_batches_tracked', 'mod.pair_embed.embed.4.weight', 'mod.pair_embed.embed.4.bias', 'mod.pair_embed.embed.5.weight', 'mod.pair_embed.embed.5.bias', 'mod.pair_embed.embed.5.running_mean', 'mod.pair_embed.embed.5.running_var', 'mod.pair_embed.embed.5.num_batches_tracked', 'mod.pair_embed.embed.7.weight', 'mod.pair_embed.embed.7.bias', 'mod.pair_embed.embed.8.weight', 'mod.pair_embed.embed.8.bias', 'mod.pair_embed.embed.8.running_mean', 'mod.pair_embed.embed.8.running_var', 'mod.pair_embed.embed.8.num_batches_tracked', 'mod.pair_embed.embed.10.weight', 'mod.pair_embed.embed.10.bias', 'mod.pair_embed.embed.11.weight', 'mod.pair_embed.embed.11.bias', 'mod.pair_embed.embed.11.running_mean', 'mod.pair_embed.embed.11.running_var', 'mod.pair_embed.embed.11.num_batches_tracked', 'mod.blocks.0.c_attn', 'mod.blocks.0.w_resid', 'mod.blocks.0.pre_attn_norm.weight', 'mod.blocks.0.pre_attn_norm.bias', 'mod.blocks.0.attn.in_proj_weight', 'mod.blocks.0.attn.in_proj_bias', 'mod.blocks.0.attn.out_proj.weight', 'mod.blocks.0.attn.out_proj.bias', 'mod.blocks.0.post_attn_norm.weight', 'mod.blocks.0.post_attn_norm.bias', 'mod.blocks.0.pre_fc_norm.weight', 'mod.blocks.0.pre_fc_norm.bias', 'mod.blocks.0.fc1.weight', 'mod.blocks.0.fc1.bias', 'mod.blocks.0.post_fc_norm.weight', 'mod.blocks.0.post_fc_norm.bias', 'mod.blocks.0.fc2.weight', 'mod.blocks.0.fc2.bias', 'mod.blocks.1.c_attn', 'mod.blocks.1.w_resid', 'mod.blocks.1.pre_attn_norm.weight', 'mod.blocks.1.pre_attn_norm.bias', 'mod.blocks.1.attn.in_proj_weight', 'mod.blocks.1.attn.in_proj_bias', 'mod.blocks.1.attn.out_proj.weight', 'mod.blocks.1.attn.out_proj.bias', 'mod.blocks.1.post_attn_norm.weight', 'mod.blocks.1.post_attn_norm.bias', 'mod.blocks.1.pre_fc_norm.weight', 'mod.blocks.1.pre_fc_norm.bias', 'mod.blocks.1.fc1.weight', 'mod.blocks.1.fc1.bias', 'mod.blocks.1.post_fc_norm.weight', 'mod.blocks.1.post_fc_norm.bias', 'mod.blocks.1.fc2.weight', 'mod.blocks.1.fc2.bias', 'mod.blocks.2.c_attn', 'mod.blocks.2.w_resid', 'mod.blocks.2.pre_attn_norm.weight', 'mod.blocks.2.pre_attn_norm.bias', 'mod.blocks.2.attn.in_proj_weight', 'mod.blocks.2.attn.in_proj_bias', 'mod.blocks.2.attn.out_proj.weight', 'mod.blocks.2.attn.out_proj.bias', 'mod.blocks.2.post_attn_norm.weight', 'mod.blocks.2.post_attn_norm.bias', 'mod.blocks.2.pre_fc_norm.weight', 'mod.blocks.2.pre_fc_norm.bias', 'mod.blocks.2.fc1.weight', 'mod.blocks.2.fc1.bias', 'mod.blocks.2.post_fc_norm.weight', 'mod.blocks.2.post_fc_norm.bias', 'mod.blocks.2.fc2.weight', 'mod.blocks.2.fc2.bias', 'mod.blocks.3.c_attn', 'mod.blocks.3.w_resid', 'mod.blocks.3.pre_attn_norm.weight', 'mod.blocks.3.pre_attn_norm.bias', 'mod.blocks.3.attn.in_proj_weight', 'mod.blocks.3.attn.in_proj_bias', 'mod.blocks.3.attn.out_proj.weight', 'mod.blocks.3.attn.out_proj.bias', 'mod.blocks.3.post_attn_norm.weight', 'mod.blocks.3.post_attn_norm.bias', 'mod.blocks.3.pre_fc_norm.weight', 'mod.blocks.3.pre_fc_norm.bias', 'mod.blocks.3.fc1.weight', 'mod.blocks.3.fc1.bias', 'mod.blocks.3.post_fc_norm.weight', 'mod.blocks.3.post_fc_norm.bias', 'mod.blocks.3.fc2.weight', 'mod.blocks.3.fc2.bias', 'mod.blocks.4.c_attn', 'mod.blocks.4.w_resid', 'mod.blocks.4.pre_attn_norm.weight', 'mod.blocks.4.pre_attn_norm.bias', 'mod.blocks.4.attn.in_proj_weight', 'mod.blocks.4.attn.in_proj_bias', 'mod.blocks.4.attn.out_proj.weight', 'mod.blocks.4.attn.out_proj.bias', 'mod.blocks.4.post_attn_norm.weight', 'mod.blocks.4.post_attn_norm.bias', 'mod.blocks.4.pre_fc_norm.weight', 'mod.blocks.4.pre_fc_norm.bias', 'mod.blocks.4.fc1.weight', 'mod.blocks.4.fc1.bias', 'mod.blocks.4.post_fc_norm.weight', 'mod.blocks.4.post_fc_norm.bias', 'mod.blocks.4.fc2.weight', 'mod.blocks.4.fc2.bias', 'mod.blocks.5.c_attn', 'mod.blocks.5.w_resid', 'mod.blocks.5.pre_attn_norm.weight', 'mod.blocks.5.pre_attn_norm.bias', 'mod.blocks.5.attn.in_proj_weight', 'mod.blocks.5.attn.in_proj_bias', 'mod.blocks.5.attn.out_proj.weight', 'mod.blocks.5.attn.out_proj.bias', 'mod.blocks.5.post_attn_norm.weight', 'mod.blocks.5.post_attn_norm.bias', 'mod.blocks.5.pre_fc_norm.weight', 'mod.blocks.5.pre_fc_norm.bias', 'mod.blocks.5.fc1.weight', 'mod.blocks.5.fc1.bias', 'mod.blocks.5.post_fc_norm.weight', 'mod.blocks.5.post_fc_norm.bias', 'mod.blocks.5.fc2.weight', 'mod.blocks.5.fc2.bias', 'mod.blocks.6.c_attn', 'mod.blocks.6.w_resid', 'mod.blocks.6.pre_attn_norm.weight', 'mod.blocks.6.pre_attn_norm.bias', 'mod.blocks.6.attn.in_proj_weight', 'mod.blocks.6.attn.in_proj_bias', 'mod.blocks.6.attn.out_proj.weight', 'mod.blocks.6.attn.out_proj.bias', 'mod.blocks.6.post_attn_norm.weight', 'mod.blocks.6.post_attn_norm.bias', 'mod.blocks.6.pre_fc_norm.weight', 'mod.blocks.6.pre_fc_norm.bias', 'mod.blocks.6.fc1.weight', 'mod.blocks.6.fc1.bias', 'mod.blocks.6.post_fc_norm.weight', 'mod.blocks.6.post_fc_norm.bias', 'mod.blocks.6.fc2.weight', 'mod.blocks.6.fc2.bias', 'mod.blocks.7.c_attn', 'mod.blocks.7.w_resid', 'mod.blocks.7.pre_attn_norm.weight', 'mod.blocks.7.pre_attn_norm.bias', 'mod.blocks.7.attn.in_proj_weight', 'mod.blocks.7.attn.in_proj_bias', 'mod.blocks.7.attn.out_proj.weight', 'mod.blocks.7.attn.out_proj.bias', 'mod.blocks.7.post_attn_norm.weight', 'mod.blocks.7.post_attn_norm.bias', 'mod.blocks.7.pre_fc_norm.weight', 'mod.blocks.7.pre_fc_norm.bias', 'mod.blocks.7.fc1.weight', 'mod.blocks.7.fc1.bias', 'mod.blocks.7.post_fc_norm.weight', 'mod.blocks.7.post_fc_norm.bias', 'mod.blocks.7.fc2.weight', 'mod.blocks.7.fc2.bias', 'mod.cls_blocks.0.c_attn', 'mod.cls_blocks.0.w_resid', 'mod.cls_blocks.0.pre_attn_norm.weight', 'mod.cls_blocks.0.pre_attn_norm.bias', 'mod.cls_blocks.0.attn.in_proj_weight', 'mod.cls_blocks.0.attn.in_proj_bias', 'mod.cls_blocks.0.attn.out_proj.weight', 'mod.cls_blocks.0.attn.out_proj.bias', 'mod.cls_blocks.0.post_attn_norm.weight', 'mod.cls_blocks.0.post_attn_norm.bias', 'mod.cls_blocks.0.pre_fc_norm.weight', 'mod.cls_blocks.0.pre_fc_norm.bias', 'mod.cls_blocks.0.fc1.weight', 'mod.cls_blocks.0.fc1.bias', 'mod.cls_blocks.0.post_fc_norm.weight', 'mod.cls_blocks.0.post_fc_norm.bias', 'mod.cls_blocks.0.fc2.weight', 'mod.cls_blocks.0.fc2.bias', 'mod.cls_blocks.1.c_attn', 'mod.cls_blocks.1.w_resid', 'mod.cls_blocks.1.pre_attn_norm.weight', 'mod.cls_blocks.1.pre_attn_norm.bias', 'mod.cls_blocks.1.attn.in_proj_weight', 'mod.cls_blocks.1.attn.in_proj_bias', 'mod.cls_blocks.1.attn.out_proj.weight', 'mod.cls_blocks.1.attn.out_proj.bias', 'mod.cls_blocks.1.post_attn_norm.weight', 'mod.cls_blocks.1.post_attn_norm.bias', 'mod.cls_blocks.1.pre_fc_norm.weight', 'mod.cls_blocks.1.pre_fc_norm.bias', 'mod.cls_blocks.1.fc1.weight', 'mod.cls_blocks.1.fc1.bias', 'mod.cls_blocks.1.post_fc_norm.weight', 'mod.cls_blocks.1.post_fc_norm.bias', 'mod.cls_blocks.1.fc2.weight', 'mod.cls_blocks.1.fc2.bias', 'mod.norm.weight', 'mod.norm.bias', 'mod.fc.0.weight', 'mod.fc.0.bias'])\n" + ] + } + ], + "source": [ + "# loading weights in the Actual ParticleTransformer model\n", + "\n", + "# Load the pretrained weights from the .pt file\n", + "pretrained_dict = torch.load(\"/part-vol-2/weaver-core/particle_transformer/models/ParT_full.pt\")\n", + "print('pretrained_dict', pretrained_dict.keys())\n", + "# Load only the parameters that exist in the model\n", + "model_dict = base_model.state_dict()\n", + "print('model_dict', model_dict.keys())\n", + "\n", + "#pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict} # maybe change \n", + "model_dict.update(pretrained_dict)\n", + "print('model_dict', model_dict.keys())\n", + "\n", + "base_model.load_state_dict(model_dict)\n", + "print('pre_trained_model final', base_model.state_dict().keys())\n", + "\n", + "# # Set the model to evaluation mode\n", + "# pre_trained_model.eval()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ParticleTransformerWrapper(\n", + " (mod): ParticleTransformer(\n", + " (trimmer): SequenceTrimmer()\n", + " (embed): Embed(\n", + " (input_bn): BatchNorm1d(17, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (embed): Sequential(\n", + " (0): LayerNorm((17,), eps=1e-05, elementwise_affine=True)\n", + " (1): Linear(in_features=17, out_features=128, bias=True)\n", + " (2): GELU(approximate='none')\n", + " (3): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (4): Linear(in_features=128, out_features=512, bias=True)\n", + " (5): GELU(approximate='none')\n", + " (6): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (7): Linear(in_features=512, out_features=128, bias=True)\n", + " (8): GELU(approximate='none')\n", + " )\n", + " )\n", + " (pair_embed): PairEmbed(\n", + " (embed): Sequential(\n", + " (0): BatchNorm1d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (1): Conv1d(4, 64, kernel_size=(1,), stride=(1,))\n", + " (2): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (3): GELU(approximate='none')\n", + " (4): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (5): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (6): GELU(approximate='none')\n", + " (7): Conv1d(64, 64, kernel_size=(1,), stride=(1,))\n", + " (8): BatchNorm1d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (9): GELU(approximate='none')\n", + " (10): Conv1d(64, 8, kernel_size=(1,), stride=(1,))\n", + " (11): BatchNorm1d(8, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (12): GELU(approximate='none')\n", + " )\n", + " )\n", + " (blocks): ModuleList(\n", + " (0-7): 8 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): QuantizableMultiheadAttention(\n", + " (out_proj): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_Q): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_K): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_V): Linear(in_features=128, out_features=128, bias=True)\n", + " (q_scaling_product): FloatFunctional(\n", + " (activation_post_process): Identity()\n", + " )\n", + " (quant_attn_output): QuantStub()\n", + " (quant_attn_output_weights): QuantStub()\n", + " (dequant_q): DeQuantStub()\n", + " (dequant_k): DeQuantStub()\n", + " (dequant_v): DeQuantStub()\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0.1, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0.1, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (cls_blocks): ModuleList(\n", + " (0-1): 2 x Block(\n", + " (pre_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (attn): QuantizableMultiheadAttention(\n", + " (out_proj): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_Q): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_K): Linear(in_features=128, out_features=128, bias=True)\n", + " (linear_V): Linear(in_features=128, out_features=128, bias=True)\n", + " (q_scaling_product): FloatFunctional(\n", + " (activation_post_process): Identity()\n", + " )\n", + " (quant_attn_output): QuantStub()\n", + " (quant_attn_output_weights): QuantStub()\n", + " (dequant_q): DeQuantStub()\n", + " (dequant_k): DeQuantStub()\n", + " (dequant_v): DeQuantStub()\n", + " )\n", + " (post_attn_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (dropout): Dropout(p=0, inplace=False)\n", + " (pre_fc_norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc1): Linear(in_features=128, out_features=512, bias=True)\n", + " (act): GELU(approximate='none')\n", + " (act_dropout): Dropout(p=0, inplace=False)\n", + " (post_fc_norm): LayerNorm((512,), eps=1e-05, elementwise_affine=True)\n", + " (fc2): Linear(in_features=512, out_features=128, bias=True)\n", + " )\n", + " )\n", + " (norm): LayerNorm((128,), eps=1e-05, elementwise_affine=True)\n", + " (fc): Sequential(\n", + " (0): Linear(in_features=128, out_features=10, bias=True)\n", + " )\n", + " )\n", + ")" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# loading weights in the Quantizable ParticleTransformer model\n", + "\n", + "pretrained_dict = torch.load(\"/part-vol-2/weaver-core/particle_transformer/models/ParT_full.pt\")\n", + "\n", + "def adapt_weights(pretrained_dict):\n", + " new_dict = {}\n", + " for key, value in pretrained_dict.items():\n", + " if 'attn.in_proj_weight' in key:\n", + " # Split the original in_proj_weight into Q, K, V\n", + " q, k, v = torch.chunk(value, 3, dim=0)\n", + " base_key = key.replace('in_proj_weight', 'linear_Q.weight')\n", + " new_dict[base_key] = q\n", + " new_dict[base_key.replace('linear_Q', 'linear_K')] = k\n", + " new_dict[base_key.replace('linear_Q', 'linear_V')] = v\n", + " elif 'attn.in_proj_bias' in key:\n", + " # Split the original in_proj_bias into Q, K, V\n", + " q, k, v = torch.chunk(value, 3, dim=0)\n", + " base_key = key.replace('in_proj_bias', 'linear_Q.bias')\n", + " new_dict[base_key] = q\n", + " new_dict[base_key.replace('linear_Q', 'linear_K')] = k\n", + " new_dict[base_key.replace('linear_Q', 'linear_V')] = v\n", + " else:\n", + " new_dict[key] = value\n", + " return new_dict\n", + "\n", + "adapted_weights = adapt_weights(pretrained_dict)\n", + "\n", + "\n", + "model_dict = pre_trained_model_quant.state_dict()\n", + "model_dict.update(adapted_weights) # Update the model's state dict with the adapted weights\n", + "pre_trained_model_quant.load_state_dict(model_dict)\n", + "\n", + "pre_trained_model_quant" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Comparing the raw vs quantizable model performance" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "for data size: torch.Size([500, 2, 128])\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/part-vol-2/.venv/lib/python3.10/site-packages/torch/nn/functional.py:5137: UserWarning: Support for mismatched key_padding_mask and attn_mask is deprecated. Use same type for both instead.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "model loss tensor(0.3651)\n", + "Quantized model loss tensor(0.3651)\n" + ] + } + ], + "source": [ + "# compare the raw vs quantizable model performance\n", + "\n", + "print('for data size: ', inp[0].shape)\n", + "\n", + "base_model.eval()\n", + "with torch.no_grad():\n", + " y_pred= base_model(*inp)\n", + "\n", + "yloss = loss_fn(y_pred.float(), torch.from_numpy(y_test).float())\n", + "print('model loss', yloss)\n", + "\n", + "pre_trained_model_quant.eval()\n", + "with torch.no_grad():\n", + " y_pred_quant= pre_trained_model_quant(*inp)\n", + "\n", + "yloss_quant = loss_fn(y_pred_quant.float(), torch.from_numpy(y_test).float())\n", + "print('Quantized model loss', yloss_quant)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Training from scratch" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# loading training data\n", + "\n", + "from torch.utils.data import Dataset, DataLoader\n", + "\n", + "learning_rate = 1e-4\n", + "dataloader = DataLoader(x_particles_train, batch_size=16, shuffle=False, sampler=None,\n", + " batch_sampler=None, num_workers=0, collate_fn=None,\n", + " pin_memory=False, drop_last=False, timeout=0,\n", + " worker_init_fn=None)\n", + "ydataloader = DataLoader(y_train, batch_size=16, shuffle=False, sampler=None,\n", + " batch_sampler=None, num_workers=0, collate_fn=None,\n", + " pin_memory=False, drop_last=False, timeout=0,\n", + " worker_init_fn=None)\n", + "xjdataloader = DataLoader(x_jets_train, batch_size=16, shuffle=False, sampler=None,\n", + " batch_sampler=None, num_workers=0, collate_fn=None,\n", + " pin_memory=False, drop_last=False, timeout=0,\n", + " worker_init_fn=None)\n", + "xpointloader = DataLoader(x_points_train, batch_size=16, shuffle=False, sampler=None,\n", + " batch_sampler=None, num_workers=0, collate_fn=None,\n", + " pin_memory=False, drop_last=False, timeout=0,\n", + " worker_init_fn=None)\n", + "xmaskloader = DataLoader(x_mask_train, batch_size=16, shuffle=False, sampler=None,\n", + " batch_sampler=None, num_workers=0, collate_fn=None,\n", + " pin_memory=False, drop_last=False, timeout=0,\n", + " worker_init_fn=None)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# training\n", + "\n", + "model = pre_trained_model_quant, None\n", + "\n", + "\n", + "# ??\n", + "# total_params = sum(p.numel() for p in model[0].parameters())\n", + "# print(total_params)\n", + "# model\n", + "\n", + "# model[0].aux_logits=False\n", + "\n", + "epochs = 1 \n", + "trainloss = np.zeros(epochs)\n", + "valloss = np.zeros(epochs)\n", + "optimizer = optim.Adam(model[0].parameters(), lr=0.0001)\n", + "\n", + "for t in range(epochs):\n", + " for x,y,z,a,b in zip(dataloader, ydataloader, xjdataloader,xpointloader,xmaskloader):\n", + " # Forward pass: compute predicted y by passing x to the model. Module objects\n", + " # override the __c|all__ operator so you can call them like functions. When\n", + " # doing so you pass a Tensor of input data to the Module and it produces\n", + " # a Tensor of output data.\n", + " model[0].train()\n", + " y_pred = model[0](a.float(), x.float(), z.float(), b.float())\n", + " #print(y_pred.shape)\n", + " \n", + " loss = loss_fn(y_pred, y.float())\n", + " \n", + "\n", + " # Zero the gradients before running the backward pass.\n", + " model[0].zero_grad()\n", + "\n", + " # Backward pass: compute gradient of the loss with respect to all the learnable\n", + " # parameters of the model. Internally, the parameters of each Module are stored\n", + " # in Tensors with requires_grad=True, so this call will compute gradients for\n", + " # all learnable parameters in the model.\n", + " loss.backward()\n", + " trainloss[t] = loss\n", + " # Update the weights using gradient descent. Each parameter is a Tensor, so\n", + " # we can access its gradients like we did before.\n", + " optimizer.step()\n", + " with torch.no_grad():\n", + " y_pred= model[0](torch.from_numpy(x_points_test),torch.from_numpy(x_part_test),torch.from_numpy(x_jet_test),torch.from_numpy(x_mask_test))\n", + " yloss = loss_fn(y_pred.float(), torch.from_numpy(y_test).float())\n", + " valloss[t] = yloss\n", + " print('Epoch' +' ' +str(t+1) + ' Train Loss:' +str(trainloss[t]))\n", + " print(' ' + 'Val Loss:' + str(valloss[t]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Dynamic Quantization using quantiizable ParticleTransformer" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Setting the function\n", + "\n", + "pre_trained_model = pre_trained_model_quant\n", + "\n", + "pretrained_quantized_model = torch.quantization.quantize_dynamic(\n", + " pre_trained_model, {torch.nn.Linear}, dtype=torch.qint8\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Size (MB): 8.639375\n", + "Size (MB): 10.640995\n", + "Size (MB): 4.385699\n" + ] + } + ], + "source": [ + "# Comparing the model sizes\n", + "\n", + "def print_size_of_model(model):\n", + " torch.save(model.state_dict(), \"temp.p\")\n", + " print('Size (MB):', os.path.getsize(\"temp.p\")/1e6)\n", + " os.remove('temp.p')\n", + "\n", + "print_size_of_model(base_model)\n", + "print_size_of_model(pre_trained_model)\n", + "print_size_of_model(pretrained_quantized_model)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Size (MB): 8.639375\n", + "Size (MB): 10.640995\n", + "Size (MB): 4.385699\n" + ] + }, + { + "data": { + "image/png": "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devW4cSJE9i7dy+uXLmCM2fOICsrC7a2tqhfvz5atWqF7t27a/0ciUQiwU8//YSWLVti06ZNuHnzJtLT0+Ho6IhevXrhk08+0eyheVOMjY2xfPlyLFmyBAcOHEBMTAxsbGzQq1cvjB8/XudE3ooVK2L79u349ddfcenSJRw9ehROTk7o0qULxo0bh5CQkBIf44QJE3Ds2DFcu3YN165dQ0pKChwcHFCnTh307t0bXbp00fmw/OKLL9CgQQOsX79es5ydnR06duyI4cOH6/05lNfhbXzOC8POzg4bN27EunXrsG/fPpw6dUrzUyN9+/bN96dGunTpAhcXFyxbtgxXr15FREQEvLy88O2336Jfv356AxLwMlxt3rwZoaGh2LdvH+7evYsbN27AxsYGzs7O6N+/P/z9/XV+WLawAgMD4eHhgbNnz+LatWv477//EBcXByMjIzg7OyMgIADdu3fP9672YuqTruPj4/M9tAsAY8eO1QQYS0tL/PXXX9i3bx927dqFmzdv4s6dO7CwsICzszO6du2KNm3aaA7Tmpub49tvv8WFCxdw69YtnD59GllZWXB2dkb79u0xYMCAYt8RnN4eEuF1H2gmIr3y/hbbunXrSns4RESUB89BIiIiIhJhQCIiIiISYUAiIiIiEuE5SEREREQi3INEREREJMKARERERCTC+yCJXLlyBYIgwNjYuLSHQkRERCUoKysLEokE9evXf2Vb7kESEQThtf8G0btMEASoVCpuY3orcD7S24Tz8fUrzGc89yCJqPcc+fj4lPJIyqe0tDTcvn0b1apV469dU6njfKS3Cefj63fjxg2D23IPEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRvRGpqamYOXMmOnbsCHt7e0gkEqxevTrf9rdv30bHjh1haWkJe3t7DB48GLGxsQY/XkpKCiZNmgQvLy+YmJjA3d0dgYGBSEtLy3eZTz75BBKJBAEBAQY9xgcffACJRILq1avrrT906BAkEgkkEgm2bt2qKV+9erWmXP3P2dkZrVu3xv79+w1eRyJ6fXgVGxG9EUqlErNnz4anpyfq1q2LY8eO5ds2MjISLVu2hI2NDX744QekpqZi3rx5uHHjBs6fPw+5XF7gYyUlJaFVq1aIjIzEyJEjUa1aNcTGxuLkyZPIzMzUe4XQxYsXsXr1apiamhZq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mZkZ3N3dAejfhpUrV4ZMJkNcXBxSU1O16hwcHGBnZ4eMjAxER0dr1cnlcnh6egIAoqKiNOEqOzsbxsbGOo+jT5kMSHkTsJj6RWNqalrk/qVSqSZc5C0DXiZWcV1ezs7OetMz8HKCicelTrISiURvv+rHdXR0hL29vVadOmCYmZnpLJs3IatfwPrGZGdnp3khivvNG7L0cXNz0ylTTzwbGxtNGMq7LupvNC4uLjAzM9Pbr4uLS4HbULycehvpe96A/9sWjo6O+fZrbm5e4DbU1696O9nb28PW1lZvnampaYH95j2HTk29DW1tbWFlZaVVp15XY2PjAp8bV1fXfNfVysoK5ubmOh8iRC4uLpp5VVrvEerHLqjf8voekZ6ejtTUVDg6OmraleZ7hL5+X/UZ6OTklG+/FhYWRd6G6jCkr+5V77PqAAYAd+/ezXfsYmUyIDk7OwOAzt4YdZmtrW2xDq8B/xfCxKRSab51gP69WmpGRkY6h5XUJBJJgf0WlHgL6hfIf11e1e+r1rWgOplMpnlR5KUOSHK5PN/lC9qG+fULvHobFvW5Ad6ubWhIv4Zsw+K+Rqj80fe6LKvzW5+3/T1Cvacob/t3ZRu+qc9AQw+vAWX0JG0XFxfY29vj33//1am7fv06atSoUQqjIiIiovKiTAYkAGjfvj2OHTumddzxzJkzCA8PR8eOHUtxZERERFTWvZWH2NavX4/k5GTNyVxHjx7VnIQ8ePBgWFlZYfTo0QgLC8OQIUMwZMgQpKWlYeXKlVAoFOjdu3dpDp+IiIjKuLcyIP31119aV0YcPHgQBw8eBAB069YNVlZWcHNzw/r16zF37lzMnz8fxsbGaNWqFYKCgnhuBRERERXLWxmQjhw5YlC76tWrY+XKla95NERERPSuKbPnIBERERG9LgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxI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aEKJUqnUnEP79OlTnbDypt4j8rK0tISrq2u+n4Hq5yY2Nhbp6eladc7OzrC2tsaLFy/w/PlzrTozMzO4u7sD0L8NK1euDJlMhri4OM2pNWoODg6ws7NDRkYGoqOjterkcjk8PT0BAFFRUZpwlZ2dbfApOGU2IO3duxczZszAgQMH4OrqCgBo3749BEHAvHnz0KVLF9jZ2RWpb6lUiooVK+qUAS8Tq7guL2dnZ73pGQAP+5EOJycnnfmkfvHa2trCyspKq049D42NjQuch66urvnOQysrK5ibm+t8iBC5uLho5pV6vtjZ2elc8GJkZATg5d6Iguahm5ubTpl6ftvY2MDS0lKrTj2/ZTJZgf26uLjkO78tLS11bvOi7lffezsAzR4FR0fHAt+/xcvm3ROhr1/1drK3t4etra3eOlNTU82y6enpSE1NhaOjo6ZdhQoVdPp9U+8R+vp91Wegk5NTvv1aWFgUeRuqw5C+urzbUF+/6gAGAHfv3s137GJlNiAFBwejZs2amnCk5u/vj9DQUNy+fRvNmjUrcv/q3ZBiUqk03zoAml16+qifTCI1uVye73ySyWSaNxax4sxDdb8FtaF3k775WNC37VfNw4LqXvf81kcikRTr/bug9/CC+jV0G6r3FOVt/65sw1f1W9A2LMxzY+jhNaAMn6StVCp1jkcC/3fsVrxbl4iIiMhQZTYgeXl54datW3j06JFW+d69eyGVSuHt7V1KIyMiIqKyrsweYhs+fDhOnDiBDz/8EB9++KHmJO0TJ06gT58+cHFxKe0hEhERURlVZgNS48aNsXHjRixevBghISFITEyEu7s7JkyYgBEjRpT28IiIiKgMK7MBCQDq1KmDFStWlPYwiIiIqJwps+cgEREREb0uDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiDEhEREREIgxIRERERCIMSEREREQiJRKQUlJSkJWVVRJdEREREZU6WWEXePHiBcLCwnDmzBlcvnwZz58/R05ODgDAwsICCoUCvr6+aNOmDXx8fEp8wERERESvm8EBKTo6GsuWLcOePXuQlpYGALCxsYGnpydsbGyQmZmJxMREXLt2DZcvX8by5ctRs2ZNfPTRR+jWrdtrWwEiIiKikmZQQJo3bx7WrVuHnJwctGzZEp06dUK9evVQsWJFnbbp6em4efMm/vnnH+zZsweTJk3C6tWr8d1336FWrVolvgJEREREJc2ggLRx40YMGzYMQ4cOha2tbYFtzczM0KhRIzRq1AhffPEFTp8+jaVLl+LIkSMMSERERFQmGBSQDh8+DBsbmyI9QLNmzdCsWTMkJSUVaXkiIiKiN82gq9iKGo5Kug8iIiKiN4H3QSIiIiISKfRl/iqVCqmpqbC3t9eUpaamYsOGDbhx4wZyc3Ph6+uLAQMGwMTEpEQHS0RERPQmFCog/fzzz1i/fj1UKhXc3Nzwv//9D9WrV0f//v0REREBQRAAAEePHsWuXbsQEhLCkERERERljsEBKTQ0FCtXroSZmRlq1qyJR48e4csvv0THjh3x7NkzjBo1CnXr1kVSUhKCg4Nx48YNrFmzBiNHjnyd4yciIiIqcQYHpG3btsHa2ho7duxAhQoVEBUVhV69emHjxo2YOnUqBg4cqGnbqVMndOjQAWFhYQxIREREVOYYfJL2vXv30KZNG1SoUAEA4O7ujtatWyMnJwdt27bVamtiYoKWLVsiPDy8RAdLRERE9CYYHJBSU1Ph6uqqVab+29nZWae9o6Mj0tPTizk8IiIiojfP4IAkCAKMjIy0ymSy/I/QSaW8gwARERGVTUwxRERERCKFusz/8uXLWLFihebvS5cuAQD+/PNPzSX+4joiIiKisqZQAen06dM4ffq0Tvm8efP0tpdIJEUbFREREVEpMjgg/e9//3ud4yAiIiJ6axgckHr27Pk6x0FERET01ijzJ2nfvHkTo0ePhq+vL+rWrYuAgACsXbu2tIdF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W7dGeno69u/fj8TEREyfPh0uLi6atv3798e1a9ewfft2XL58Gc2aNYOzszPi4uLw8OFDXLt2DfPnz4eHh8drX0ei8ooBiYjeKBMTE6xZswYrVqzAvn37sHr1apiZmaFx48YYNWqU5qotNXNzc6xbtw4LFizAoUOHcOvWLVSrVg2//PILUlJSdAISAHzwwQfYvn07Vq5ciTNnzuDUqVMwNzeHi4sLevXqpfcqstelR48euHjxItq1awc7O7t828nlcqxatQrz58/Hrl27kJycjCpVquCbb75BQECAVluJRIK5c+eiZcuW2LJlC44dO4a0tDTY29ujUqVKmDx5st5bIBCR4SRC3n3cRERUombPno0NGzZg9erV+YYWf39/AMCRI0fe5NCIqAA8B4mI6DWJj4/H9u3b4eXlhaZNm5b2cIioEHiIjYiohB07dgw3b97EgQMHkJaWhnHjxmndNZyI3n4MSEREJSwsLAzbt2+Hs7MzJk6cqHUFGhGVDTwHiYiIiEiE5yARERERiTAgEREREYkwIBERERGJMCARERERiTAgEREREYkwIBERERGJMCARERERiTAgEREREYkwIBERERGJ/D/VLwABuywCYQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plotting model size reduction\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "import os\n", + "\n", + "# Calculate sizes for models\n", + "base_model_size = print_size_of_model(base_model)\n", + "pre_trained_model_size = print_size_of_model(pre_trained_model)\n", + "pretrained_quantized_model_size = print_size_of_model(pretrained_quantized_model)\n", + "\n", + "# Data for plotting\n", + "sizes = [base_model_size, pre_trained_model_size, pretrained_quantized_model_size]\n", + "labels = ['Base Model', 'Quantizable-MHA Model', 'Quantized Model']\n", + "\n", + "# Set the style\n", + "sns.set(style=\"whitegrid\")\n", + "\n", + "# Plottingimport torch\n", + "import os\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "def get_size_of_model(model):\n", + " temp_path = \"temp.p\"\n", + " torch.save(model.state_dict(), temp_path)\n", + " size_mb = os.path.getsize(temp_path) / 1e6\n", + " os.remove(temp_path)\n", + " return size_mb\n", + "\n", + "# Calculate sizes for models\n", + "base_model_size = get_size_of_model(base_model)\n", + "pre_trained_model_size = get_size_of_model(pre_trained_model)\n", + "pretrained_quantized_model_size = get_size_of_model(pretrained_quantized_model)\n", + "\n", + "# Data for plotting\n", + "sizes = [base_model_size, pre_trained_model_size, pretrained_quantized_model_size]\n", + "labels = ['Base Model', 'Quantizable-MHA Model', 'Quantized Model']\n", + "\n", + "# Set the style\n", + "sns.set(style=\"whitegrid\")\n", + "\n", + "# Plotting\n", + "plt.figure(figsize=(6, 6))\n", + "bars = plt.bar(labels, sizes, color=['#1f77b4', '#d55e00', '#2ca02c'], edgecolor='black')\n", + "\n", + "# Adding value annotations on top of bars\n", + "for bar in bars:\n", + " yval = bar.get_height()\n", + " plt.text(bar.get_x() + bar.get_width() / 2, yval, f'{yval:.2f} MB', va='bottom', ha='center', fontsize=12, color='black')\n", + "\n", + "plt.xlabel('Model Type', fontsize=14)\n", + "plt.ylabel('Size (MB)', fontsize=14)\n", + "plt.title('Comparison of Model Sizes', fontsize=16)\n", + "plt.xticks(fontsize=12)\n", + "plt.yticks(fontsize=12)\n", + "plt.grid(axis='y', linestyle='--', alpha=0.7)\n", + "\n", + "# Show plot\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "for data size: torch.Size([500, 2, 128])\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/part-vol-2/.venv/lib/python3.10/site-packages/torch/nn/functional.py:5137: UserWarning: Support for mismatched key_padding_mask and attn_mask is deprecated. Use same type for both instead.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "model loss tensor(0.3651)\n", + "model inference time 5.362913370132446\n", + "for data size: torch.Size([500, 2, 128])\n", + "model loss tensor(0.3651)\n", + "model inference time 5.22483229637146\n", + "quantized_model loss tensor(0.3648)\n", + "quantized model inference time 4.85134220123291\n" + ] + } + ], + "source": [ + "# loss and inference time comparison\n", + "\n", + "base_model.eval()\n", + "with torch.no_grad():\n", + " print('for data size: ', inp[0].shape)\n", + " eval_start_time = time.time()\n", + " y_pred= base_model(*inp)\n", + " eval_end_time = time.time()\n", + "\n", + "yloss = loss_fn(y_pred.float(), torch.from_numpy(y_test).float())\n", + "print('model loss', yloss)\n", + "print('model inference time', eval_end_time - eval_start_time)\n", + "\n", + "pre_trained_model.eval()\n", + "with torch.no_grad():\n", + " print('for data size: ', inp[0].shape)\n", + " eval_start_time_p = time.time()\n", + " y_pred= pre_trained_model(*inp)\n", + " eval_end_time_p = time.time()\n", + "\n", + "yloss = loss_fn(y_pred.float(), torch.from_numpy(y_test).float())\n", + "print('model loss', yloss)\n", + "print('model inference time', eval_end_time_p - eval_start_time_p)\n", + "\n", + "pretrained_quantized_model.eval()\n", + "with torch.no_grad():\n", + " eval_start_time_q = time.time()\n", + " y_pred_quant = pretrained_quantized_model(*inp)\n", + " eval_end_time_q = time.time()\n", + "\n", + "yloss_quant = loss_fn(y_pred_quant.float(), torch.from_numpy(y_test).float())\n", + "print('quantized_model loss', yloss_quant)\n", + "print ('quantized model inference time', eval_end_time_q - eval_start_time_q)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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BQcHm7U3Go0aPHiwDh06pM6dO6tixYo6cOCApk2bpsuXL5tdQv7WW29p06ZNatu2rWrWrKmffvpJAwcOTFHDtWvX1LVrVxkMBvXq1Uvu7u7av3+/3nrrLd26dcsicyFcu3ZN3bt3V2xsrPr06aNChQpp/fr1Gjx4sGbPnm263Hv16tWaNGmSWrRoob59++rOnTs6fvy4jh49ajppfuedd7Rz50717t1b5cqV082bN3X48GGdPHlSlStXTrOGffv2qUyZMhm+HHvcuHFav369WrRooRdeeEHHjh3Tp59+qpMnT2revHlmbc+cOaNRo0ape/fueu6557Ro0SINGjRI7777rmbMmKEePXpIkj777DMNHz5cO3bskJ3dv98xJCYmasCAAapevbpee+01HThwQHPmzFFiYqJeffVVU7ulS5eqWbNmateunRISErR161a9+uqr+vTTT9WkSROzmn766Sdt375dvXr1UqFChVSqVKlUjzMj7+e6des0ZswYVa1aVSNHjtT169e1dOlS/fbbb9qwYYPZN8qJiYnq37+/qlWrptdff10//vijFi1apDJlyqhnz54Zeu8BAFmT284zhg8frtKlS2vkyJH6+++/9fXXX8vd3V2vvfaa3N3dNW3aNH3yySeKiYnRyJEjJUnlypWTJI0fP17r169Xp06d1KdPH50/f17Lli3T33//rRUrVsjBwcG0n9OnT2vUqFHq1q2bunbtqieffFKxsbHq3bu3Ll++rO7du6tkyZI6cuSIPv74Y129elVvvfWWWa1btmzR7du31a1bNxkMBn3xxRcaOnSodu/ebdrXP//8o169eilPnjzq1q2bSpUqpbNnz2rv3r0aMWLEQ39//ysxMVEvvfSSateurdGjR2vz5s1677335OzsrBkzZqhdu3Z69tlntXLlSr3xxhvy9fVVmTJlJCnT79f9Dh8+rJs3b6pv374ZuvIno+edyT777DM5OTlp4MCBOnPmjL766ivlyZNHBoNBUVFReuWVV3T06FGtW7dOpUqV0iuvvGK2/S+//KJt27apT58+cnR01IoVKzRgwAB9/fXX8vb2lnQvJDpy5IjatGmjEiVK6MKFC1qxYoX69u2rrVu3ytnZ2azPd999V+7u7hoyZIhiYmJSPc7r16+rf//+KlSokAYOHChXV1edP39eu3btMrXJzGc8+b3+5ptv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kdqyZYu+//57ffzxx6btpkyZIoPBIF9fX7m6uurYsWP69NNPVaVKFT3zzDOmdv3799fmzZs1atQo9ejRQ//73/+0cOFCjRgxwmLzOwAAAJw7d069evVKc72XlxfhEwAAeOTYVPjk5uamJUuWaOLEiRoyZIjy5cunzp07a8SIEWbtkpKSlJiYaHpdpkwZxcfH66OPPtKNGzdUqFAh+fj4KDg4WH5+fqZ25cqV04oVK7R69WrFxcWpePHi6ty5s4YNG6Y8ef59K8qWLauFCxdqypQpGjhwoNzd3TVs2DC9+OKLln8TAABAruXg4KCrV6+muf7KlSuys7OpC9cBAADSZVPhk3QvIFq8ePED2wQHB6fYZv78+en23aVLF3Xp0iVDddSsWZNvFgEAwENVvXp1rV+/Xv369VP+/PnN1kVHR2vdunWqXr26laoDAADIGpsLnwAAAHKrV155Rb1791aHDh0UGBhoelLuiRMntGTJEl29elUfffSRlasEAADIHMInAAAAG1G9enV98sknGj9+vCZPnmx6qp7RaFTp0qW1YMECHn4CAAAeOYRPAAAANqRBgwbatWuX/v77b509e1aS5OHhocqVK5vCKAAAgEcJ4RMAAICNsbOzU5UqVVSlShVrlwIAAJBthE8AAAA2JjQ0VOfOnVNkZGSq6zt06PBwCwIAAMgGwicAAAAbcfbsWb322ms6duyYjEZjqm0MBgPhEwAAeKQQPgEAANiI8ePH63//+5/Gjh2r2rVry9XV1dolAQAAZBvhEwAAgI347bffFBQUpD59+li7FAAAgBxjZ+0CAAAAcE+hQoVUoEABa5cBAACQowifAAAAbET37t21adMmJSYmWrsUAACAHMNtdwAAADbC09NTSUlJat++vZ5//nmVKFFC9vb2Kdo9++yzVqgOAAAgawifAAAAbMSIESNM/z116tRU2xgMBoWEhDyskgAAALKN8AkAAMBGLF261NolAAAA5DjCJwAAABvh5+dn7RIAAAByHOETAACAjYmPj9dff/2l69evq2bNmnJ3d7d2SQAAAFnG0+4AAABsyNKlS9WwYUP17NlTQ4cO1fHjxyVJERERqlu3rtasWWPlCgEAADKH8AkAAMBGrF27Vu+//74aNWqkyZMny2g0mta5u7vr6aef1rZt26xYIQAAQOYRPgEAANiIL7/8UgEBAfroo4/U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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "# Initialize lists to store results\n", + "models = ['Base Model', 'Quantizable Model', 'Quantized Model']\n", + "losses = []\n", + "inference_times = []\n", + "\n", + "losses.append(yloss.item())\n", + "inference_times.append(eval_end_time - eval_start_time)\n", + "\n", + "losses.append(yloss.item())\n", + "inference_times.append(eval_end_time_p - eval_start_time_p)\n", + "\n", + "losses.append(yloss_quant.item())\n", + "inference_times.append(eval_end_time_q - eval_start_time_q)\n", + "\n", + "# Define colors for the bars\n", + "colors = ['#1f77b4', '#d55e00', '#2ca02c'] # Blue for base, dark orange for quantizable, green for quantized\n", + "\n", + "# Plot losses\n", + "plt.figure(figsize=(12, 6))\n", + "\n", + "# Losses\n", + "plt.subplot(1, 2, 1)\n", + "bars = plt.bar(models, losses, color=colors, edgecolor='black')\n", + "plt.xlabel('Model')\n", + "plt.ylabel('Loss')\n", + "plt.title('Model Loss Comparison')\n", + "plt.ylim(min(losses) - 0.1, max(losses) + 0.1) # Adjust y-axis limits for better visibility\n", + "\n", + "# Add text annotations\n", + "for bar in bars:\n", + " yval = bar.get_height()\n", + " plt.text(bar.get_x() + bar.get_width() / 2, yval + 0.02, f'{yval:.2f}', ha='center', va='bottom')\n", + "\n", + "# Inference times\n", + "plt.subplot(1, 2, 2)\n", + "bars = plt.bar(models, inference_times, color=colors, edgecolor='black')\n", + "plt.xlabel('Model')\n", + "plt.ylabel('Inference Time (seconds)')\n", + "plt.title('Model Inference Time Comparison')\n", + "plt.ylim(min(inference_times) - 0.1, max(inference_times) + 0.1) # Adjust y-axis limits for better visibility\n", + "\n", + "# Add text annotations\n", + "for bar in bars:\n", + " yval = bar.get_height()\n", + " plt.text(bar.get_x() + bar.get_width() / 2, yval + 0.02, f'{yval:.2f}', ha='center', va='bottom')\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "#label_list = ['label_QCD', 'label_Hbb', 'label_Hcc', 'label_Hgg', 'label_H4q', 'label_Hqql', 'label_Zqq', 'label_Wqq', 'label_Tbqq', 'label_Tbl']\n", + "label_list = ['label_Hbb', 'label_Hcc', 'label_Hgg', 'label_H4q', 'label_Hqql', 'label_Zqq', 'label_Wqq', 'label_Tbqq', 'label_Tbl']\n", + "\n", + "def makeRoc(y_pred, labels_val, labels, model, model_type, outputDir='', outputSuffix=''):\n", + " from sklearn.metrics import roc_curve, auc\n", + " if model_type == 'original':\n", + " labels_pred = y_pred\n", + " elif model_type == 'quantized':\n", + " labels_pred = y_pred.dequantize()\n", + " df = pd.DataFrame()\n", + " fpr = {}\n", + " tpr = {}\n", + " auc1 = {}\n", + " plt.figure(figsize=(10,8)) \n", + " g = labels_pred.detach().numpy()\n", + " for i, label in enumerate(labels):\n", + " df[label] = labels_val[:,i]\n", + " df[label + '_pred'] = g[:,i]\n", + " fpr[label], tpr[label], threshold = roc_curve(df[label],df[label+'_pred'])\n", + " auc1[label] = auc(fpr[label], tpr[label])\n", + " plt.plot(fpr[label],tpr[label],label='%s tagger, AUC = %.1f%%'%(label.replace('j_',''),auc1[label]*100.))\n", + " plt.plot([0, 1], [0, 1], lw=1, color='black', linestyle='--')\n", + " #plt.semilogy()\n", + " plt.xlabel(\"Background Efficiency\")\n", + " plt.ylabel(\"Signal Efficiency\")\n", + " plt.xlim([0.0001,1.05])\n", + " plt.ylim(0.0001,1.05)\n", + " # plt.yscale('log')\n", + " # plt.xscale('log')\n", + " plt.grid(True)\n", + " plt.legend(loc='lower right')\n", + " plt.figtext(0.25, 0.90,'Particle Transformer ROC Curve ' + model_type,fontweight='bold', wrap=True, horizontalalignment='right', fontsize=14)\n", + " #plt.figtext(0.35, 0.90,'preliminary', style='italic', wrap=True, horizontalalignment='center', fontsize=14) \n", + " #plt.savefig('%sROC_%s.pdf'%(outputDir, outputSuffix))\n", + " #return labels_pred\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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+P7777F3715cvXoVer0e/v7+qFixosMtMoUKFcKPP/6ItWvXYuvWrYiOjkZCQgK0Wi2KFy+OatWqoWnTpg4HSWfL7dchteDgYEREROCPP/7AhQsXcP/+fSQlJSFfvnwoX7482rZta7feoiOKFCmCn376CatXr8a2bdtw6dIlJCcnw8/PD1WrVkVYWJht7T9X8M477+Cll17CqlWr8Mcff+Du3bsQQqBgwYKoWLEi6tWrh7Zt20pdJhERERF5CIXIyhSERERERERE5HakHQxFREREREREuY7Bj4iIiIiISOYY/IiIiIiIiGSOwY+IiIiIiEjmGPyIiIiIiIhkjsGPiIiIiIhI5hj8yK316tULISEhCAkJQURERI6Pd/jwYdvxQkJCcPPmTSdUmXURERG2Gnr16iVJDUREREQkH7m+gPuzHD58GL17906zXalUwtfXF6VKlUKjRo3Qt29fFClSJN3jnD9/HqtXr8bx48dx69Yt6PV6+Pn5oUKFCmjWrBm6deuG/PnzZ1jL8ePHsX79epw4cQK3b99GcnKybaHxBg0aoHPnzihdunSWHt/58+fx008/4dixY4iNjcXjx4/h4+ODMmXKoG7duujUqRMqV66cpWMSERERERFllyQLuKcX/J5WpEgRrFmzBiVKlLDbbjabMX36dERGRmZ4+wIFCuDTTz9F48aN01wXHx+P8ePHY+fOnRkeo169epnezxMGgwEff/wxfvjhhwz3CwoKwu7dux06JmVs8+bNuHXrFgCgYsWKaNq0aY6Od+vWLWzevNl2uXv37siXL1+OjpkdERER+PnnnwFk7RwkIiIiInoWSVr8ntauXTtUq1YNjx49ws6dO3HhwgUAwL1797Bs2TKMGzfObv8pU6Zg9erVtstFixZF27ZtUaBAAVy4cAHbtm2DxWLBw4cPMWTIECxfvhy1a9e27Z+UlIR+/frhzJkztm1FihRBixYtEBgYiMePH+Ps2bP4/fffHX4MFosFI0eOxJ49e2zb8ufPj5YtW6JMmTIwGAyIjo7GgQMHsvz8ZMejR48kCSx55cnja9eunVOPW6JECfTv39+pxyQiIiIikppLBL8mTZogLCwMANCvXz80bNgQJpMJAHDp0iW7ff/44w+70Fe1alWsWLHCLuQcOnQI/fr1g9VqhclkwqRJk7Bx40YolSlDGr/66iu70NeiRQvM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+fJo3rw52rVr53Dd3t7e+Pjjj/Hmm2/ip59+wrFjxxAbG4vHjx/Dx8cHZcuWRbNmzdIc8+OPP0bhwoXx66+/4sGDBwgKCsJrr72Gvn37omrVqtl8Fu116tQJc+bMsWsJbd++/TP3rVWrFn799Vd89913iIqKwrVr15CcnAxfX1+UKlUKNWvWRIsWLVC3bl2H7rt8+fLYsGEDvvvuO+zevRtXr16F0WhEgQIF8MILL6BHjx5o3LixUx5namPGjEGpUqWwYsUKxMTEICAgAK1bt8bIkSPRpEkT237pjSt0BrVajeXLl2PWrFnYvXs3kpKSULZsWfTq1QuNGjVi8CMiIiKiPKEQT0+bSSQTer0eOp0uzfY9e/ZgyJAhtsurVq1CrVq18rI0IiIiIqI85ZItfkTO8Nlnn+HcuXNo3rw5SpYsCbPZjDNnzth16a1WrVqaCWiIiIiIiOSGwY9kSwiBI0eO2Bawf1qZMmUwd+7cXB3jR0RERETkChj8SLZeeeUV3L9/H3/++ScePHgAo9GI/Pnzo2LFimjZsiVee+01py0fQURERETkyjjGj4iIiIiISOZcbgF3IiIiIiIici4GPyIiIiIiIplj8CMiIiIiIpI5Bj8iIiIiIiKZY/AjIiIiIiKSOQY/IiIiIiIimeM6fm7oxIkTEEJArVZLXQoREREREUnIZDJBoVCgZs2aGe7HFj83JIQAl18kZxFCwGg08pyiHOO5RM7E84mchecSOZMrnk+OZgO2+LmhJy191atXl7gSkoOkpCScO3cOFSpUgI+Pj9TlkBvjuUTOxPOJnIXnEjmTK55Pp0+fdmg/tvgRERERERHJHIMfERERERGRzDH4ERERERERyRyDHxERERERkcwx+BEREREREckcgx8REREREZHMMfgRERERERHJHIMfERERERGRzDH4ERERERERyRyDHxERERERkcwx+BEREREREckcgx8REREREZHMMfgRERERERHJHIMfERERERGRzDH4ERERERERyRyDHxERERERkcwx+BEREREREckcgx8REREREZHMMfgRERERERHJHIMfERERERGRzDH4ERERERERyRyDHxERERERkcwx+Dno2rVrmDhxIjp16oTnnnsO7du3d+h2QggsXrwYL730EmrUqIHu3bvj5MmTuVssERERERFRKgx+Drp48SKioqJQpkwZ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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "makeRoc(y_pred, y_test, label_list, pre_trained_model, 'original')" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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EREREdk5BkMiIiIiIiI7x2BIRERERERk5+RiF0Dmd/78eQiCAIVCIXYpREREREQkIq1WC4lEgvr16+e5HVsMbZAgCOD0lGQOgiBAo9HweiKz4PVE5sTricyF1xKZkzVeT/nNBmwxtEFZLYV16tQRuRIq6dLS0hAeHo4qVarAyclJ7HKohOP1RObE64nMhdcSmZM1Xk+XLl3K13ZsMSQiIiIiIrJzDIZERERERER2jsGQiIiIiIjIzjEYEhERERER2TkGQyIiIiIiIjvHYEhERERERGTnGAyJiIiIiIjsHIMhERERERGRnWMwJCIiIiIisnMMhkRERERERHaOwZCIiIiIiMjOMRgSERERERHZOQZDIiIiIiIiO8dgSEREREREZOcYDImIiIiIiOwcgyEREREREZGdYzAkIiIiIiKycwyGREREREREdo7BkIiIiIiIyM4xGBIREREREdk5BkMiIiIiIiI7x2BIRERERERk5xgMzeTu3buYPXs2goODUbNmTbz66qv52k8QBHz//fdo164dXnrpJfTv3x8XLlywbLFERERERETPYDA0k5s3b+Lw4cOoVKkSAgMD873fypUrsWTJEgwbNgzfffcdvL29MWLECNy/f9+C1RIREREREf0fg6GZtG/fHocPH8aSJUtQq1atfO2jVqvx3XffYcSIERg2bBiaN2+OL774Au7u7vjxxx8tXDEREREREVEmBkMzkUoL/lSeO3cOKSkp6Nq1q3GZUqlEp06dcOTIEXOWR0RERERElCu52AXYs1u3bgEAAgICTJYHBgZi9erVyMjIgIODgxilWQVBEKDXayBo1WKXYhGCIECv1RRpf7W+8PvnR0aGGumpT5AY/wjqdJVFz2VLBEGATisUat9LkbF4mpBh5ooKThAECILBrMfU6nSIj4/H1buJUMj55ye/5NEPoIiLNcuxXCTukEtkZjmW2ASDAK1Gj3/2X4VEKhG7HCrBeC2RORkEAbpybkCNGmKXUmD8yyyipKQkKJVKqFSmb7hdXV0hCAISExMLHQwFQUBaWpo5yhSFIAi4e/FHpCfzXkuxOQKIurJf7DLshjsAd6XYVViQi9gFlECV/vtnFg/MdSAiInqORqPD3K/+wuSxPZGY2FjscowEQYBE8uIPPRgMbZRWq0V4eLjYZRSeoAMYComIiIjIymX2cjNAqZSje8daSMtQ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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "makeRoc(y_pred_quant, y_test, label_list, pretrained_quantized_model, 'quantized')" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Class 0 | ParT: TPR = 50% -> FPR = 0.004366812227074236 | Quantized: TPR = 50% -> FPR = 0.004366812227074236 |\n", + "Class 1 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.0 |\n", + "Class 2 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.002178649237472767 |\n", + "Class 3 | ParT: TPR = 50% -> FPR = 0.009070294784580499 | Quantized: TPR = 50% -> FPR = 0.009070294784580499 |\n", + "Class 4 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.0 |\n", + "Class 5 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.0 |\n", + "Class 6 | ParT: TPR = 50% -> FPR = 0.035555555555555556 | Quantized: TPR = 50% -> FPR = 0.04 |\n", + "Class 7 | ParT: TPR = 50% -> FPR = 0.004524886877828055 | Quantized: TPR = 50% -> FPR = 0.004524886877828055 |\n", + "Class 8 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.0 |\n", + "Class 9 | ParT: TPR = 50% -> FPR = 0.0 | Quantized: TPR = 50% -> FPR = 0.0 |\n", + "Average Background Rejection at TPR = 50% across all classes | ParT: 186.85459449651813 | Quantized: 166.27690493582227 \n" + ] + } + ], + "source": [ + "import numpy as np\n", + "from sklearn.metrics import roc_curve\n", + "\n", + "n_classes = 10\n", + "y_prob = y_pred\n", + "y_prob_quant = y_pred_quant\n", + "\n", + "fpr_at_50_tpr = []\n", + "fpr_at_50_tpr_quant = []\n", + "\n", + "for i in range(n_classes):\n", + " fpr, tpr, thresholds = roc_curve(y_test[:, i], y_prob[:, i])\n", + " fpr_quant, tpr_quant, thresholds_quant = roc_curve(y_test[:, i], y_prob_quant[:, i])\n", + " \n", + " idx = np.abs(tpr - 0.5).argmin()\n", + " idx_quant = np.abs(tpr_quant - 0.5).argmin()\n", + " fpr_at_50_tpr.append(fpr[idx])\n", + " fpr_at_50_tpr_quant.append(fpr_quant[idx_quant])\n", + " print(f\"Class {i} | ParT: TPR = 50% -> FPR = {fpr[idx]} | Quantized: TPR = 50% -> FPR = {fpr_quant[idx_quant]} |\")\n", + "\n", + "average_fpr = np.mean(fpr_at_50_tpr)\n", + "average_fpr_quant = np.mean(fpr_at_50_tpr_quant)\n", + "print(f\"Average Background Rejection at TPR = 50% across all classes | ParT: {1/average_fpr} | Quantized: {1/average_fpr_quant} \")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/networks/ParticleTransformer_updated.py b/networks/ParticleTransformer_updated.py new file mode 100644 index 0000000..c534ae9 --- /dev/null +++ b/networks/ParticleTransformer_updated.py @@ -0,0 +1,1578 @@ +''' Particle Transformer (ParT) + +Paper: "Particle Transformer for Jet Tagging" - https://arxiv.org/abs/2202.03772 +''' +import math +import random +import warnings +import copy +import torch +import torch.nn as nn +from functools import partial + +from typing import Dict, Optional, Tuple +from fairseq import utils +from fairseq.incremental_decoding_utils import with_incremental_state +from fairseq.modules.quant_noise import quant_noise +from torch import Tensor, nn +from torch.nn import Parameter +from weaver.utils.logger import _logger +import torch.nn.functional as F + +# me for quantization +#from torch.ao.nn.quantizable.modules.activation import MultiheadAttention +import sys +if '/part-vol-2/weaver-core' not in sys.path: + sys.path.append('/part-vol-2/weaver-core') + +from quantizable_mha import MultiheadAttention + + +@torch.jit.script +def delta_phi(a, b): + return (a - b + math.pi) % (2 * math.pi) - math.pi + + +@torch.jit.script +def delta_r2(eta1, phi1, eta2, phi2): + return (eta1 - eta2)**2 + delta_phi(phi1, phi2)**2 + + +def to_pt2(x, eps=1e-8): + pt2 = x[:, :2].square().sum(dim=1, keepdim=True) + if eps is not None: + pt2 = pt2.clamp(min=eps) + return pt2 + + +def to_m2(x, eps=1e-8): + m2 = x[:, 3:4].square() - x[:, :3].square().sum(dim=1, keepdim=True) + if eps is not None: + m2 = m2.clamp(min=eps) + return m2 + + +def atan2(y, x): + sx = torch.sign(x) + sy = torch.sign(y) + pi_part = (sy + sx * (sy ** 2 - 1)) * (sx - 1) * (-math.pi / 2) + atan_part = torch.arctan(y / (x + (1 - sx ** 2))) * sx ** 2 + return atan_part + pi_part + + +def to_ptrapphim(x, return_mass=True, eps=1e-8, for_onnx=False): + # x: (N, 4, ...), dim1 : (px, py, pz, E) + px, py, pz, energy = x.split((1, 1, 1, 1), dim=1) + pt = torch.sqrt(to_pt2(x, eps=eps)) + # rapidity = 0.5 * torch.log((energy + pz) / (energy - pz)) + rapidity = 0.5 * torch.log(1 + (2 * pz) / (energy - pz).clamp(min=1e-20)) + phi = (atan2 if for_onnx else torch.atan2)(py, px) + if not return_mass: + return torch.cat((pt, rapidity, phi), dim=1) + else: + m = torch.sqrt(to_m2(x, eps=eps)) + return torch.cat((pt, rapidity, phi, m), dim=1) + + +def boost(x, boostp4, eps=1e-8): + # boost x to the rest frame of boostp4 + # x: (N, 4, ...), dim1 : (px, py, pz, E) + p3 = -boostp4[:, :3] / boostp4[:, 3:].clamp(min=eps) + b2 = p3.square().sum(dim=1, keepdim=True) + gamma = (1 - b2).clamp(min=eps)**(-0.5) + gamma2 = (gamma - 1) / b2 + gamma2.masked_fill_(b2 == 0, 0) + bp = (x[:, :3] * p3).sum(dim=1, keepdim=True) + v = x[:, :3] + gamma2 * bp * p3 + x[:, 3:] * gamma * p3 + return v + + +def p3_norm(p, eps=1e-8): + return p[:, :3] / p[:, :3].norm(dim=1, keepdim=True).clamp(min=eps) + + +def pairwise_lv_fts(xi, xj, num_outputs=4, eps=1e-8, for_onnx=False): + pti, rapi, phii = to_ptrapphim(xi, False, eps=None, for_onnx=for_onnx).split((1, 1, 1), dim=1) + ptj, rapj, phij = to_ptrapphim(xj, False, eps=None, for_onnx=for_onnx).split((1, 1, 1), dim=1) + + delta = delta_r2(rapi, phii, rapj, phij).sqrt() + lndelta = torch.log(delta.clamp(min=eps)) + if num_outputs == 1: + return lndelta + + if num_outputs > 1: + ptmin = ((pti <= ptj) * pti + (pti > ptj) * ptj) if for_onnx else torch.minimum(pti, ptj) + lnkt = torch.log((ptmin * delta).clamp(min=eps)) + lnz = torch.log((ptmin / (pti + ptj).clamp(min=eps)).clamp(min=eps)) + outputs = [lnkt, lnz, lndelta] + + if num_outputs > 3: + xij = xi + xj + lnm2 = torch.log(to_m2(xij, eps=eps)) + outputs.append(lnm2) + + if num_outputs > 4: + lnds2 = torch.log(torch.clamp(-to_m2(xi - xj, eps=None), min=eps)) + outputs.append(lnds2) + + # the following features are not symmetric for (i, j) + if num_outputs > 5: + xj_boost = boost(xj, xij) + costheta = (p3_norm(xj_boost, eps=eps) * p3_norm(xij, eps=eps)).sum(dim=1, keepdim=True) + outputs.append(costheta) + + if num_outputs > 6: + deltarap = rapi - rapj + deltaphi = delta_phi(phii, phij) + outputs += [deltarap, deltaphi] + + assert (len(outputs) == num_outputs) + return torch.cat(outputs, dim=1) + + +def build_sparse_tensor(uu, idx, seq_len): + # inputs: uu (N, C, num_pairs), idx (N, 2, num_pairs) + # return: (N, C, seq_len, seq_len) + batch_size, num_fts, num_pairs = uu.size() + idx = torch.min(idx, torch.ones_like(idx) * seq_len) + i = torch.cat(( + torch.arange(0, batch_size, device=uu.device).repeat_interleave(num_fts * num_pairs).unsqueeze(0), + torch.arange(0, num_fts, device=uu.device).repeat_interleave(num_pairs).repeat(batch_size).unsqueeze(0), + idx[:, :1, :].expand_as(uu).flatten().unsqueeze(0), + idx[:, 1:, :].expand_as(uu).flatten().unsqueeze(0), + ), dim=0) + return torch.sparse_coo_tensor( + i, uu.flatten(), + size=(batch_size, num_fts, seq_len + 1, seq_len + 1), + device=uu.device).to_dense()[:, :, :seq_len, :seq_len] + + +def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): + # From https://github.com/rwightman/pytorch-image-models/blob/18ec173f95aa220af753358bf860b16b6691edb2/timm/layers/weight_init.py#L8 + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + Args: + tensor: an n-dimensional `torch.Tensor` + mean: the mean of the normal distribution + std: the standard deviation of the normal distribution + a: the minimum cutoff value + b: the maximum cutoff value + Examples: + >>> w = torch.empty(3, 5) + >>> nn.init.trunc_normal_(w) + """ + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn("mean is more than 2 std from [a, b] in nn.init.trunc_normal_. " + "The distribution of values may be incorrect.", + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + l = norm_cdf((a - mean) / std) + u = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [l, u], then translate to + # [2l-1, 2u-1]. + tensor.uniform_(2 * l - 1, 2 * u - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +class SequenceTrimmer(nn.Module): + + def __init__(self, enabled=False, target=(0.9, 1.02), **kwargs) -> None: + super().__init__(**kwargs) + self.enabled = enabled + self.target = target + self._counter = 0 + + def forward(self, x, v=None, mask=None, uu=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # uu: (N, C', P, P) + if mask is None: + mask = torch.ones_like(x[:, :1]) + mask = mask.bool() + + if self.enabled: + if self._counter < 5: + self._counter += 1 + else: + if self.training: + q = min(1, random.uniform(*self.target)) + maxlen = torch.quantile(mask.type_as(x).sum(dim=-1), q).long() + rand = torch.rand_like(mask.type_as(x)) + rand.masked_fill_(~mask, -1) + perm = rand.argsort(dim=-1, descending=True) # (N, 1, P) + mask = torch.gather(mask, -1, perm) + x = torch.gather(x, -1, perm.expand_as(x)) + if v is not None: + v = torch.gather(v, -1, perm.expand_as(v)) + if uu is not None: + uu = torch.gather(uu, -2, perm.unsqueeze(-1).expand_as(uu)) + uu = torch.gather(uu, -1, perm.unsqueeze(-2).expand_as(uu)) + else: + maxlen = mask.sum(dim=-1).max() + maxlen = max(maxlen, 1) + if maxlen < mask.size(-1): + mask = mask[:, :, :maxlen] + x = x[:, :, :maxlen] + if v is not None: + v = v[:, :, :maxlen] + if uu is not None: + uu = uu[:, :, :maxlen, :maxlen] + + return x, v, mask, uu + + +class Embed(nn.Module): + def __init__(self, input_dim, dims, normalize_input=True, activation='gelu'): + super().__init__() + + self.input_bn = nn.BatchNorm1d(input_dim) if normalize_input else None + module_list = [] + for dim in dims: + module_list.extend([ + nn.LayerNorm(input_dim), + nn.Linear(input_dim, dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + self.embed = nn.Sequential(*module_list) + + def forward(self, x): + if self.input_bn is not None: + # x: (batch, embed_dim, seq_len) + x = self.input_bn(x) + x = x.permute(2, 0, 1).contiguous() + # x: (seq_len, batch, embed_dim) + return self.embed(x) + + +class PairEmbed(nn.Module): + def __init__( + self, pairwise_lv_dim, pairwise_input_dim, dims, + remove_self_pair=False, use_pre_activation_pair=True, mode='sum', + normalize_input=True, activation='gelu', eps=1e-8, + for_onnx=False): + super().__init__() + + self.pairwise_lv_dim = pairwise_lv_dim + self.pairwise_input_dim = pairwise_input_dim + self.is_symmetric = (pairwise_lv_dim <= 5) and (pairwise_input_dim == 0) + self.remove_self_pair = remove_self_pair + self.mode = mode + self.for_onnx = for_onnx + self.pairwise_lv_fts = partial(pairwise_lv_fts, num_outputs=pairwise_lv_dim, eps=eps, for_onnx=for_onnx) + self.out_dim = dims[-1] + + if self.mode == 'concat': + input_dim = pairwise_lv_dim + pairwise_input_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.embed = nn.Sequential(*module_list) + elif self.mode == 'sum': + if pairwise_lv_dim > 0: + input_dim = pairwise_lv_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.embed = nn.Sequential(*module_list) + + if pairwise_input_dim > 0: + input_dim = pairwise_input_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.fts_embed = nn.Sequential(*module_list) + else: + raise RuntimeError('`mode` can only be `sum` or `concat`') + + def forward(self, x, uu=None): + # x: (batch, v_dim, seq_len) + # uu: (batch, v_dim, seq_len, seq_len) + assert (x is not None or uu is not None) + with torch.no_grad(): + if x is not None: + batch_size, _, seq_len = x.size() + else: + batch_size, _, seq_len, _ = uu.size() + if self.is_symmetric and not self.for_onnx: + i, j = torch.tril_indices(seq_len, seq_len, offset=-1 if self.remove_self_pair else 0, + device=(x if x is not None else uu).device) + if x is not None: + x = x.unsqueeze(-1).repeat(1, 1, 1, seq_len) + xi = x[:, :, i, j] # (batch, dim, seq_len*(seq_len+1)/2) + xj = x[:, :, j, i] + x = self.pairwise_lv_fts(xi, xj) + if uu is not None: + # (batch, dim, seq_len*(seq_len+1)/2) + uu = uu[:, :, i, j] + else: + if x is not None: + x = self.pairwise_lv_fts(x.unsqueeze(-1), x.unsqueeze(-2)) + if self.remove_self_pair: + i = torch.arange(0, seq_len, device=x.device) + x[:, :, i, i] = 0 + x = x.view(-1, self.pairwise_lv_dim, seq_len * seq_len) + if uu is not None: + uu = uu.view(-1, self.pairwise_input_dim, seq_len * seq_len) + if self.mode == 'concat': + if x is None: + pair_fts = uu + elif uu is None: + pair_fts = x + else: + pair_fts = torch.cat((x, uu), dim=1) + + if self.mode == 'concat': + elements = self.embed(pair_fts) # (batch, embed_dim, num_elements) + elif self.mode == 'sum': + if x is None: + elements = self.fts_embed(uu) + elif uu is None: + elements = self.embed(x) + else: + elements = self.embed(x) + self.fts_embed(uu) + + if self.is_symmetric and not self.for_onnx: + y = torch.zeros(batch_size, self.out_dim, seq_len, seq_len, dtype=elements.dtype, device=elements.device) + y[:, :, i, j] = elements + y[:, :, j, i] = elements + else: + y = elements.view(-1, self.out_dim, seq_len, seq_len) + return y + + +class Block(nn.Module): + def __init__(self, embed_dim=128, num_heads=8, ffn_ratio=4, + dropout=0.1, attn_dropout=0.1, activation_dropout=0.1, + add_bias_kv=False, activation='gelu', + scale_fc=True, scale_attn=True, scale_heads=True, scale_resids=True): + super().__init__() + + self.embed_dim = embed_dim + self.num_heads = num_heads + self.head_dim = embed_dim // num_heads + self.ffn_dim = embed_dim * ffn_ratio + self.interaction = None + + self.pre_attn_norm = nn.LayerNorm(embed_dim) + self.attn = nn.MultiheadAttention( + #self.attn = MultiheadAttention( + embed_dim, + num_heads, + dropout=attn_dropout, + add_bias_kv=add_bias_kv, + ) + self.post_attn_norm = nn.LayerNorm(embed_dim) if scale_attn else None + self.dropout = nn.Dropout(dropout) + + self.pre_fc_norm = nn.LayerNorm(embed_dim) + self.fc1 = nn.Linear(embed_dim, self.ffn_dim) + self.act = nn.GELU() if activation == 'gelu' else nn.ReLU() + self.act_dropout = nn.Dropout(activation_dropout) + self.post_fc_norm = nn.LayerNorm(self.ffn_dim) if scale_fc else None + self.fc2 = nn.Linear(self.ffn_dim, embed_dim) + + self.c_attn = nn.Parameter(torch.ones(num_heads), requires_grad=True) if scale_heads else None + self.w_resid = nn.Parameter(torch.ones(embed_dim), requires_grad=True) if scale_resids else None + def getAttention(self): + return self.interaction + + def forward(self, x, x_cls=None, padding_mask=None, attn_mask=None): + """ + Args: + x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)` + x_cls (Tensor, optional): class token input to the layer of shape `(1, batch, embed_dim)` + padding_mask (ByteTensor, optional): binary + ByteTensor of shape `(batch, seq_len)` where padding + elements are indicated by ``1``. + + Returns: + encoded output of shape `(seq_len, batch, embed_dim)` + """ + + if x_cls is not None: + with torch.no_grad(): + # prepend one element for x_cls: -> (batch, 1+seq_len) + padding_mask = torch.cat((torch.zeros_like(padding_mask[:, :1]), padding_mask), dim=1) + # class attention: https://arxiv.org/pdf/2103.17239.pdf + residual = x_cls + u = torch.cat((x_cls, x), dim=0) # (seq_len+1, batch, embed_dim) + u = self.pre_attn_norm(u) + x = self.attn(x_cls, u, u, key_padding_mask=padding_mask)[0] # (1, batch, embed_dim) + else: + residual = x + x = self.pre_attn_norm(x) + x= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[0] # (seq_len, batch, embed_dim) + y= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[1] + self.interaction = y + + + if self.c_attn is not None: + tgt_len = x.size(0) + x = x.view(tgt_len, -1, self.num_heads, self.head_dim) + x = torch.einsum('tbhd,h->tbdh', x, self.c_attn) + x = x.reshape(tgt_len, -1, self.embed_dim) + if self.post_attn_norm is not None: + x = self.post_attn_norm(x) + x = self.dropout(x) + x += residual + + residual = x + x = self.pre_fc_norm(x) + x = self.act(self.fc1(x)) + x = self.act_dropout(x) + if self.post_fc_norm is not None: + x = self.post_fc_norm(x) + x = self.fc2(x) + x = self.dropout(x) + if self.w_resid is not None: + residual = torch.mul(self.w_resid, residual) + x += residual + + return x + + +class ParticleTransformer(nn.Module): + + def __init__(self, + input_dim, + num_classes=10, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[64, 64, 64], + pair_embed_dims=[32, 32, 32], + num_heads=1, + num_layers=1, + num_cls_layers=1, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.attention_matrix = None + self.for_inference = for_inference + self.use_amp = use_amp + embed_dim = embed_dims[-1] if len(embed_dims) > 0 else input_dim + default_cfg = dict(embed_dim=embed_dim, num_heads=num_heads, ffn_ratio=4, + dropout=0.1, attn_dropout=0.1, activation_dropout=0.1, + add_bias_kv=False, activation=activation, + scale_fc=True, scale_attn=True, scale_heads=True, scale_resids=True) + + cfg_block = copy.deepcopy(default_cfg) + if block_params is not None: + cfg_block.update(block_params) + _logger.info('cfg_block: %s' % str(cfg_block)) + + cfg_cls_block = copy.deepcopy(default_cfg) + if cls_block_params is not None: + cfg_cls_block.update(cls_block_params) + _logger.info('cfg_cls_block: %s' % str(cfg_cls_block)) + + self.pair_extra_dim = pair_extra_dim + self.embed = Embed(input_dim, embed_dims, activation=activation) if len(embed_dims) > 0 else nn.Identity() + self.pair_embed = PairEmbed( + pair_input_dim, pair_extra_dim, pair_embed_dims + [cfg_block['num_heads']], + remove_self_pair=remove_self_pair, use_pre_activation_pair=use_pre_activation_pair, + for_onnx=for_inference) if pair_embed_dims is not None and pair_input_dim + pair_extra_dim > 0 else None + self.blocks = nn.ModuleList([Block(**cfg_block) for _ in range(num_layers)]) + self.cls_blocks = nn.ModuleList([Block(**cfg_cls_block) for _ in range(num_cls_layers)]) + self.norm = nn.LayerNorm(embed_dim) + self.interactionMatrix = None + + if fc_params is not None: + fcs = [] + in_dim = embed_dim + for out_dim, drop_rate in fc_params: + fcs.append(nn.Sequential(nn.Linear(in_dim, out_dim), nn.ReLU(), nn.Dropout(drop_rate))) + in_dim = out_dim + fcs.append(nn.Linear(in_dim, num_classes)) + self.fc = nn.Sequential(*fcs) + else: + self.fc = None + + # init + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim), requires_grad=True) + trunc_normal_(self.cls_token, std=.02) + + @torch.jit.ignore + def no_weight_decay(self): + return {'cls_token', } + + def getAttention(self): + return self.attention_matrix + + def getInteraction(self): + return self.interactionMatrix + + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # for pytorch: uu (N, C', num_pairs), uu_idx (N, 2, num_pairs) + # for onnx: uu (N, C', P, P), uu_idx=None + + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for block in self.blocks: + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + self.interactionMatrix = attn_mask + self.attention_matrix = block.interaction + + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + + + return output + + +class ParticleTransformerTagger(nn.Module): + + def __init__(self, + pf_input_dim, + sv_input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[128, 512, 128], + pair_embed_dims=[64, 64, 64], + num_heads=8, + num_layers=8, + num_cls_layers=2, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.use_amp = use_amp + + self.pf_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.sv_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + + self.pf_embed = Embed(pf_input_dim, embed_dims, activation=activation) + self.sv_embed = Embed(sv_input_dim, embed_dims, activation=activation) + + self.part = ParticleTransformer(input_dim=embed_dims[-1], + num_classes=num_classes, + # network configurations + pair_input_dim=pair_input_dim, + pair_extra_dim=pair_extra_dim, + remove_self_pair=remove_self_pair, + use_pre_activation_pair=use_pre_activation_pair, + embed_dims=[], + pair_embed_dims=pair_embed_dims, + num_heads=num_heads, + num_layers=num_layers, + num_cls_layers=num_cls_layers, + block_params=block_params, + cls_block_params=cls_block_params, + fc_params=fc_params, + activation=activation, + # misc + trim=False, + for_inference=for_inference, + use_amp=use_amp) + + @torch.jit.ignore + def no_weight_decay(self): + return {'part.cls_token', } + + def forward(self, pf_x, pf_v=None, pf_mask=None, sv_x=None, sv_v=None, sv_mask=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + + with torch.no_grad(): + pf_x, pf_v, pf_mask, _ = self.pf_trimmer(pf_x, pf_v, pf_mask) + sv_x, sv_v, sv_mask, _ = self.sv_trimmer(sv_x, sv_v, sv_mask) + v = torch.cat([pf_v, sv_v], dim=2) + mask = torch.cat([pf_mask, sv_mask], dim=2) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + pf_x = self.pf_embed(pf_x) # after embed: (seq_len, batch, embed_dim) + sv_x = self.sv_embed(sv_x) + x = torch.cat([pf_x, sv_x], dim=0) + + return self.part(x, v, mask) + + +class ParticleTransformerTaggerWithExtraPairFeatures(nn.Module): + + def __init__(self, + pf_input_dim, + sv_input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[128, 512, 128], + pair_embed_dims=[64, 64, 64], + num_heads=8, + num_layers=8, + num_cls_layers=2, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.use_amp = use_amp + self.for_inference = for_inference + + self.pf_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.sv_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + + self.pf_embed = Embed(pf_input_dim, embed_dims, activation=activation) + self.sv_embed = Embed(sv_input_dim, embed_dims, activation=activation) + + self.part = ParticleTransformer(input_dim=embed_dims[-1], + num_classes=num_classes, + # network configurations + pair_input_dim=pair_input_dim, + pair_extra_dim=pair_extra_dim, + remove_self_pair=remove_self_pair, + use_pre_activation_pair=use_pre_activation_pair, + embed_dims=[], + pair_embed_dims=pair_embed_dims, + num_heads=num_heads, + num_layers=num_layers, + num_cls_layers=num_cls_layers, + block_params=block_params, + cls_block_params=cls_block_params, + fc_params=fc_params, + activation=activation, + # misc + trim=False, + for_inference=for_inference, + use_amp=use_amp) + + @torch.jit.ignore + def no_weight_decay(self): + return {'part.cls_token', } + + def forward(self, pf_x, pf_v=None, pf_mask=None, sv_x=None, sv_v=None, sv_mask=None, pf_uu=None, pf_uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + + with torch.no_grad(): + if not self.for_inference: + if pf_uu_idx is not None: + pf_uu = build_sparse_tensor(pf_uu, pf_uu_idx, pf_x.size(-1)) + + pf_x, pf_v, pf_mask, pf_uu = self.pf_trimmer(pf_x, pf_v, pf_mask, pf_uu) + sv_x, sv_v, sv_mask, _ = self.sv_trimmer(sv_x, sv_v, sv_mask) + v = torch.cat([pf_v, sv_v], dim=2) + mask = torch.cat([pf_mask, sv_mask], dim=2) + uu = torch.zeros(v.size(0), pf_uu.size(1), v.size(2), v.size(2), dtype=v.dtype, device=v.device) + uu[:, :, :pf_x.size(2), :pf_x.size(2)] = pf_uu + + with torch.cuda.amp.autocast(enabled=self.use_amp): + pf_x = self.pf_embed(pf_x) # after embed: (seq_len, batch, embed_dim) + sv_x = self.sv_embed(sv_x) + x = torch.cat([pf_x, sv_x], dim=0) + + return self.part(x, v, mask, uu) + + + +class ParticleTransformerAdd(ParticleTransformer): + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for i, block in enumerate(self.blocks): + x_residual = x.clone() # Make a copy of x for residual connection + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + if i < len(self.blocks) - 1: # Exclude the last block + x = x + x_residual # Add residual connection + self.attention_matrix = x + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + return output + def getAttention(self): + return self.attention_matrix + + +class LinBlock(nn.Module): + def __init__( + self, + embed_dim=128, + num_heads=8, + max_seq_len=128, + attn_type="linformer", + compressed=4, + bucket_size=32, + n_hashes=4, + d_state=16, + d_conv=4, + expand=2, + ffn_ratio=4, + dropout=0.1, + attn_dropout=0.1, + activation_dropout=0.1, + add_bias_kv=False, + activation="gelu", + scale_fc=True, + scale_attn=True, + scale_heads=True, + scale_resids=True, + ): + super().__init__() + + self.embed_dim = embed_dim + self.num_heads = num_heads + self.max_seq_len = max_seq_len + self.compressed = compressed + self.head_dim = embed_dim // num_heads + self.ffn_dim = embed_dim * ffn_ratio + self.attn_type = attn_type + + self.pre_attn_norm = nn.LayerNorm(embed_dim) + + self.attn = MultiheadLinearAttention( + embed_dim, + num_heads, + dropout=attn_dropout, + add_bias_kv=add_bias_kv, + max_seq_len=max_seq_len, + compressed=compressed, + ) + self.post_attn_norm = nn.LayerNorm(embed_dim) if scale_attn else None + self.dropout = nn.Dropout(dropout) + + self.pre_fc_norm = nn.LayerNorm(embed_dim) + self.fc1 = nn.Linear(embed_dim, self.ffn_dim) + self.act = nn.GELU() if activation == "gelu" else nn.ReLU() + self.act_dropout = nn.Dropout(activation_dropout) + self.post_fc_norm = nn.LayerNorm(self.ffn_dim) if scale_fc else None + self.fc2 = nn.Linear(self.ffn_dim, embed_dim) + + self.c_attn = ( + nn.Parameter(torch.ones(num_heads), requires_grad=True) + if scale_heads + else None + ) + self.w_resid = ( + nn.Parameter(torch.ones(embed_dim), requires_grad=True) + if scale_resids + else None + ) + self.interaction = None + + + def getAttention(self): + return self.interaction + + def forward(self, x, x_cls=None, padding_mask=None, attn_mask=None): + """ + Args: + x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)` + x_cls (Tensor, optional): class token input to the layer of shape `(1, batch, embed_dim)` + padding_mask (ByteTensor, optional): binary + ByteTensor of shape `(batch, seq_len)` where padding + elements are indicated by ``1``. + + Returns: + encoded output of shape `(seq_len, batch, embed_dim)` + """ + + if x_cls is not None: + with torch.no_grad(): + # prepend one element for x_cls: -> (batch, 1+seq_len) + padding_mask = torch.cat( + (torch.zeros_like(padding_mask[:, :1]), padding_mask), dim=1 + ) + # class attention: https://arxiv.org/pdf/2103.17239.pdf + residual = x_cls + u = torch.cat((x_cls, x), dim=0) # (seq_len+1, batch, embed_dim) + u = self.pre_attn_norm(u) + x = self.full_attn(x_cls, u, u, key_padding_mask=padding_mask)[ + 0 + ] # (1, batch, embed_dim) + else: + residual = x + x = self.pre_attn_norm(x) + if self.attn_type == "linformer": + x = self.attn(x, x, x, key_padding_mask=padding_mask, attn_mask=attn_mask)[ + 0 + ] # (seq_len, batch, embed_dim) + y= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[1] + self.interaction = y + + elif self.attn_type == "performer": + x = self.attn(x, x, input_mask=padding_mask, attn_mask=attn_mask)[ + 0 + ] # (seq_len, batch, embed_dim) + elif self.attn_type == "reformer": + x = self.attn(x) + elif self.attn_type == "mamba": + x = self.attn(x) + elif self.attn_type == "pairs": + x = self.attn(x, attn_mask)[0] + + if self.c_attn is not None: + tgt_len = x.size(0) + x = x.view(tgt_len, -1, self.num_heads, self.head_dim) + x = torch.einsum("tbhd,h->tbdh", x, self.c_attn) + x = x.reshape(tgt_len, -1, self.embed_dim) + if self.post_attn_norm is not None: + x = self.post_attn_norm(x) + x = self.dropout(x) + x += residual + + residual = x + x = self.pre_fc_norm(x) + x = self.act(self.fc1(x)) + x = self.act_dropout(x) + if self.post_fc_norm is not None: + x = self.post_fc_norm(x) + x = self.fc2(x) + x = self.dropout(x) + if self.w_resid is not None: + residual = torch.mul(self.w_resid, residual) + x += residual + + return x + + +class EfficientParticleTransformer(nn.Module): + def __init__( + self, + input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[64, 64, 64], + pair_embed_dims=[32,32,32], # [64, 64, 64], + num_heads=1, + num_layers=1, + num_cls_layers=1, + block_params=None, + cls_block_params={"dropout": 0, "attn_dropout": 0, "activation_dropout": 0}, + fc_params=[], + activation="gelu", + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs + ) -> None: + super().__init__(**kwargs) + + self.trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.for_inference = for_inference + self.use_amp = use_amp + + embed_dim = embed_dims[-1] if len(embed_dims) > 0 else input_dim + default_cfg = dict( + embed_dim=embed_dim, + num_heads=num_heads, + ffn_ratio=4, + dropout=0.1, + attn_dropout=0.1, + activation_dropout=0.1, + add_bias_kv=False, + activation=activation, + scale_fc=True, + scale_attn=True, + scale_heads=True, + scale_resids=True, + ) + + cfg_block = copy.deepcopy(default_cfg) + if block_params is not None: + cfg_block.update(block_params) + _logger.info("cfg_block: %s" % str(cfg_block)) + + cfg_cls_block = copy.deepcopy(default_cfg) + if cls_block_params is not None: + cfg_cls_block.update(cls_block_params) + _logger.info("cfg_cls_block: %s" % str(cfg_cls_block)) + + self.pair_extra_dim = pair_extra_dim + self.embed = ( + Embed(input_dim, embed_dims, activation=activation) + if len(embed_dims) > 0 + else nn.Identity() + ) + self.pair_embed = PairEmbed( + pair_input_dim, pair_extra_dim, pair_embed_dims + [cfg_block['num_heads']], + remove_self_pair=remove_self_pair, use_pre_activation_pair=use_pre_activation_pair, + for_onnx=for_inference) if pair_embed_dims is not None and pair_input_dim + pair_extra_dim > 0 else None + self.blocks = nn.ModuleList([LinBlock(**cfg_block) for _ in range(num_layers)]) + self.cls_blocks = nn.ModuleList( + [Block(**cfg_cls_block) for _ in range(num_cls_layers)] + ) + self.norm = nn.LayerNorm(embed_dim) + + if fc_params is not None: + fcs = [] + in_dim = embed_dim + for out_dim, drop_rate in fc_params: + fcs.append( + nn.Sequential( + nn.Linear(in_dim, out_dim), nn.ReLU(), nn.Dropout(drop_rate) + ) + ) + in_dim = out_dim + fcs.append(nn.Linear(in_dim, num_classes)) + self.fc = nn.Sequential(*fcs) + else: + self.fc = None + + # init + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim), requires_grad=True) + trunc_normal_(self.cls_token, std=0.02) + self.interactionMatrix = None + self.attention_matrix = None + + + + def getAttention(self): + return self.attention_matrix + + def getInteraction(self): + return self.interactionMatrix + + @torch.jit.ignore + def no_weight_decay(self): + return { + "cls_token", + } + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # for pytorch: uu (N, C', num_pairs), uu_idx (N, 2, num_pairs) + # for onnx: uu (N, C', P, P), uu_idx=None + + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for block in self.blocks: + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + self.interactionMatrix = attn_mask + self.attention_matrix = block.interaction + + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + # print('output:\n', output) + return output + +@with_incremental_state +class MultiheadLinearAttention(nn.Module): + """Multi-headed linformer attention. + + Projects the key and values down to the compressed dimension, before computing self-attention. + + See "Linformer: Self-Attention with Linear Complexity" for more details. + """ + + def __init__( + self, + embed_dim, + num_heads, + kdim=None, + vdim=None, + dropout=0.0, + bias=True, + add_bias_kv=False, + add_zero_attn=False, + self_attention=False, + encoder_decoder_attention=False, + q_noise=0.0, + qn_block_size=8, + compressed=1, + max_seq_len=256, + shared_kv_compressed=0, + shared_compress_layer=None, + freeze_compress=0, + ): + super().__init__() + self.embed_dim = embed_dim + self.kdim = kdim if kdim is not None else embed_dim + self.vdim = vdim if vdim is not None else embed_dim + self.qkv_same_dim = self.kdim == embed_dim and self.vdim == embed_dim + + self.num_heads = num_heads + self.dropout = dropout + self.head_dim = embed_dim // num_heads + assert ( + self.head_dim * num_heads == self.embed_dim + ), "embed_dim must be divisible by num_heads" + self.scaling = self.head_dim**-0.5 + + self.self_attention = self_attention + self.encoder_decoder_attention = encoder_decoder_attention + + assert not self.self_attention or self.qkv_same_dim, ( + "Self-attention requires query, key and " "value to be of the same size" + ) + + self.k_proj = quant_noise( + nn.Linear(self.kdim, embed_dim, bias=bias), q_noise, qn_block_size + ) + self.v_proj = quant_noise( + nn.Linear(self.vdim, embed_dim, bias=bias), q_noise, qn_block_size + ) + self.q_proj = quant_noise( + nn.Linear(embed_dim, embed_dim, bias=bias), q_noise, qn_block_size + ) + + # used for compress sequence to subsequence + if shared_compress_layer is None: + self.compress_seq_len = max_seq_len // compressed + self.compress_k = nn.Linear(max_seq_len, self.compress_seq_len, bias=False) + if shared_kv_compressed == 0: + self.compress_v = nn.Linear( + max_seq_len, self.compress_seq_len, bias=False + ) + self.layerwise_sharing = False + else: + self.compress_k = shared_compress_layer + if shared_kv_compressed == 0: + self.compress_v = shared_compress_layer + self.layerwise_sharing = True + self.shared_kv_compressed = shared_kv_compressed + + self.out_proj = quant_noise( + nn.Linear(embed_dim, embed_dim, bias=bias), q_noise, qn_block_size + ) + + if add_bias_kv: + self.bias_k = Parameter(torch.Tensor(1, 1, embed_dim)) + self.bias_v = Parameter(torch.Tensor(1, 1, embed_dim)) + else: + self.bias_k = self.bias_v = None + + self.add_zero_attn = add_zero_attn + + self.reset_parameters() + + if freeze_compress == 1: + self.compress_k.weight.requires_grad = False + if shared_kv_compressed == 0: + self.compress_v.weight.requires_grad = False + + self.onnx_trace = False + + def prepare_for_onnx_export_(self): + self.onnx_trace = True + + def reset_parameters(self): + if self.qkv_same_dim: + # Empirically observed the convergence to be much better with + # the scaled initialization + nn.init.xavier_uniform_(self.k_proj.weight, gain=1 / math.sqrt(2)) + nn.init.xavier_uniform_(self.v_proj.weight, gain=1 / math.sqrt(2)) + nn.init.xavier_uniform_(self.q_proj.weight, gain=1 / math.sqrt(2)) + if ( + not self.layerwise_sharing + ): # otherwise, we already initialize the parameters + nn.init.xavier_uniform_(self.compress_k.weight, gain=1 / math.sqrt(2)) + if self.shared_kv_compressed == 0: + nn.init.xavier_uniform_( + self.compress_v.weight, gain=1 / math.sqrt(2) + ) + else: + nn.init.xavier_uniform_(self.k_proj.weight) + nn.init.xavier_uniform_(self.v_proj.weight) + nn.init.xavier_uniform_(self.q_proj.weight) + if ( + not self.layerwise_sharing + ): # otherwise, we already initialize the parameters + nn.init.xavier_uniform_(self.compress_k.weight) + if self.shared_kv_compressed == 0: + nn.init.xavier_uniform_(self.compress_v.weight) + + nn.init.xavier_uniform_(self.out_proj.weight) + if self.out_proj.bias is not None: + nn.init.constant_(self.out_proj.bias, 0.0) + if self.bias_k is not None: + nn.init.xavier_normal_(self.bias_k) + if self.bias_v is not None: + nn.init.xavier_normal_(self.bias_v) + + def forward( + self, + query, + key: Optional[Tensor], + value: Optional[Tensor], + key_padding_mask: Optional[Tensor] = None, + incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None, + need_weights: bool = True, + static_kv: bool = False, + attn_mask: Optional[Tensor] = None, + before_softmax: bool = False, + need_head_weights: bool = False, + ) -> Tuple[Tensor, Optional[Tensor]]: + """Input shape: Time x Batch x Channel + + Args: + key_padding_mask (ByteTensor, optional): mask to exclude + keys that are pads, of shape `(batch, src_len)`, where + padding elements are indicated by 1s. + need_weights (bool, optional): return the attention weights, + averaged over heads (default: False). + attn_mask (ByteTensor, optional): typically used to + implement causal attention, where the mask prevents the + attention from looking forward in time (default: None). + before_softmax (bool, optional): return the raw attention + weights and values before the attention softmax. + need_head_weights (bool, optional): return the attention + weights for each head. Implies *need_weights*. Default: + return the average attention weights over all heads. + """ + if need_head_weights: + need_weights = True + + tgt_len, bsz, embed_dim = query.size() + assert embed_dim == self.embed_dim + assert list(query.size()) == [tgt_len, bsz, embed_dim] + + if incremental_state is not None: + saved_state = self._get_input_buffer(incremental_state) + if saved_state is not None and "prev_key" in saved_state: + # previous time steps are cached - no need to recompute + # key and value if they are static + if static_kv: + assert self.encoder_decoder_attention and not self.self_attention + key = value = None + else: + saved_state = None + + if self.self_attention: + q = self.q_proj(query) + + k_input = query.permute(1, 2, 0).contiguous() # B * C * T + k_input = ( + F.linear(k_input, self.compress_k.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + k = self.k_proj(k_input) + + v_input = query.permute(1, 2, 0).contiguous() # B * C * T + if self.shared_kv_compressed == 0: + v_input = ( + F.linear(v_input, self.compress_v.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + if self.shared_kv_compressed == 1: # use shared kv compressed linear layer + v_input = ( + F.linear(v_input, self.compress_k.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + v = self.v_proj(v_input) + elif self.encoder_decoder_attention: + # encoder-decoder attention + q = self.q_proj(query) + if key is None: + assert value is None + k = v = None + else: + k = self.k_proj(key) + v = self.v_proj(key) + + else: + assert key is not None and value is not None + q = self.q_proj(query) + k = self.k_proj(key) + v = self.v_proj(value) + q *= self.scaling + + if self.bias_k is not None: + assert self.bias_v is not None + k = torch.cat([k, self.bias_k.repeat(1, bsz, 1)]) + v = torch.cat([v, self.bias_v.repeat(1, bsz, 1)]) + if attn_mask is not None: + attn_mask = torch.cat( + [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1 + ) + if key_padding_mask is not None: + key_padding_mask = torch.cat( + [ + key_padding_mask, + key_padding_mask.new_zeros(key_padding_mask.size(0), 1), + ], + dim=1, + ) + + q = ( + q.contiguous() + .view(tgt_len, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + if k is not None: + k = ( + k.contiguous() + .view(-1, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + if v is not None: + v = ( + v.contiguous() + .view(-1, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + + if saved_state is not None: + # saved states are stored with shape (bsz, num_heads, seq_len, head_dim) + if "prev_key" in saved_state: + _prev_key = saved_state["prev_key"] + assert _prev_key is not None + prev_key = _prev_key.view(bsz * self.num_heads, -1, self.head_dim) + if static_kv: + k = prev_key + else: + assert k is not None + k = torch.cat([prev_key, k], dim=1) + if "prev_value" in saved_state: + _prev_value = saved_state["prev_value"] + assert _prev_value is not None + prev_value = _prev_value.view(bsz * self.num_heads, -1, self.head_dim) + if static_kv: + v = prev_value + else: + assert v is not None + v = torch.cat([prev_value, v], dim=1) + prev_key_padding_mask: Optional[Tensor] = None + if "prev_key_padding_mask" in saved_state: + prev_key_padding_mask = saved_state["prev_key_padding_mask"] + assert k is not None and v is not None + key_padding_mask = MultiheadLinearAttention._append_prev_key_padding_mask( + key_padding_mask=key_padding_mask, + prev_key_padding_mask=prev_key_padding_mask, + batch_size=bsz, + src_len=k.size(1), + static_kv=static_kv, + ) + + saved_state["prev_key"] = k.view(bsz, self.num_heads, -1, self.head_dim) + saved_state["prev_value"] = v.view(bsz, self.num_heads, -1, self.head_dim) + saved_state["prev_key_padding_mask"] = key_padding_mask + # In this branch incremental_state is never None + assert incremental_state is not None + incremental_state = self._set_input_buffer(incremental_state, saved_state) + assert k is not None + src_len = k.size(1) + + if self.add_zero_attn: + assert v is not None + src_len += 1 + k = torch.cat([k, k.new_zeros((k.size(0), 1) + k.size()[2:])], dim=1) + v = torch.cat([v, v.new_zeros((v.size(0), 1) + v.size()[2:])], dim=1) + if attn_mask is not None: + attn_mask = torch.cat( + [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1 + ) + + attn_weights = torch.bmm(q, k.transpose(1, 2)) + attn_weights = MultiheadLinearAttention.apply_sparse_mask( + attn_weights, tgt_len, src_len, bsz + ) + + assert list(attn_weights.size()) == [bsz * self.num_heads, tgt_len, src_len] + + if attn_mask is not None: + attn_mask = attn_mask.unsqueeze(0) + if self.onnx_trace: + attn_mask = attn_mask.repeat(attn_weights.size(0), 1, 1) + attn_weights += attn_mask + + if before_softmax: + return attn_weights, v + + attn_weights_float = utils.softmax( + attn_weights, dim=-1, onnx_trace=self.onnx_trace + ) + attn_weights = attn_weights_float.type_as(attn_weights) + attn_probs = F.dropout( + attn_weights, + p=self.dropout, + training=self.training, + ) + assert v is not None + attn = torch.bmm(attn_probs, v) + assert list(attn.size()) == [bsz * self.num_heads, tgt_len, self.head_dim] + if self.onnx_trace and attn.size(1) == 1: + # when ONNX tracing a single decoder step (sequence length == 1) + # the transpose is a no-op copy before view, thus unnecessary + attn = attn.contiguous().view(tgt_len, bsz, embed_dim) + else: + attn = attn.transpose(0, 1).contiguous().view(tgt_len, bsz, embed_dim) + attn = self.out_proj(attn) + attn_weights: Optional[Tensor] = None + if need_weights: + attn_weights = attn_weights_float.view( + bsz, self.num_heads, tgt_len, src_len + ).transpose(1, 0) + if not need_head_weights: + # average attention weights over heads + attn_weights = attn_weights.mean(dim=0) + + return attn, attn_weights + + @staticmethod + def _append_prev_key_padding_mask( + key_padding_mask: Optional[Tensor], + prev_key_padding_mask: Optional[Tensor], + batch_size: int, + src_len: int, + static_kv: bool, + ) -> Optional[Tensor]: + # saved key padding masks have shape (bsz, seq_len) + if prev_key_padding_mask is not None and static_kv: + new_key_padding_mask = prev_key_padding_mask + elif prev_key_padding_mask is not None and key_padding_mask is not None: + new_key_padding_mask = torch.cat( + [prev_key_padding_mask.float(), key_padding_mask.float()], dim=1 + ) + # During incremental decoding, as the padding token enters and + # leaves the frame, there will be a time when prev or current + # is None + elif prev_key_padding_mask is not None: + filler = torch.zeros( + (batch_size, src_len - prev_key_padding_mask.size(1)), + device=prev_key_padding_mask.device, + ) + new_key_padding_mask = torch.cat( + [prev_key_padding_mask.float(), filler.float()], dim=1 + ) + elif key_padding_mask is not None: + filler = torch.zeros( + (batch_size, src_len - key_padding_mask.size(1)), + device=key_padding_mask.device, + ) + new_key_padding_mask = torch.cat( + [filler.float(), key_padding_mask.float()], dim=1 + ) + else: + new_key_padding_mask = prev_key_padding_mask + return new_key_padding_mask + + @torch.jit.export + def reorder_incremental_state( + self, + incremental_state: Dict[str, Dict[str, Optional[Tensor]]], + new_order: Tensor, + ): + """Reorder buffered internal state (for incremental generation).""" + input_buffer = self._get_input_buffer(incremental_state) + if input_buffer is not None: + for k in input_buffer.keys(): + input_buffer_k = input_buffer[k] + if input_buffer_k is not None: + if self.encoder_decoder_attention and input_buffer_k.size( + 0 + ) == new_order.size(0): + break + input_buffer[k] = input_buffer_k.index_select(0, new_order) + incremental_state = self._set_input_buffer(incremental_state, input_buffer) + return incremental_state + + def _get_input_buffer( + self, incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] + ) -> Dict[str, Optional[Tensor]]: + result = self.get_incremental_state(incremental_state, "attn_state") + if result is not None: + return result + else: + empty_result: Dict[str, Optional[Tensor]] = {} + return empty_result + + def _set_input_buffer( + self, + incremental_state: Dict[str, Dict[str, Optional[Tensor]]], + buffer: Dict[str, Optional[Tensor]], + ): + return self.set_incremental_state(incremental_state, "attn_state", buffer) + + def apply_sparse_mask(attn_weights, tgt_len: int, src_len: int, bsz: int): + return attn_weights + + def upgrade_state_dict_named(self, state_dict, name): + prefix = name + "." if name != "" else "" + items_to_add = {} + keys_to_remove = [] + for k in state_dict.keys(): + if k.endswith(prefix + "in_proj_weight"): + # in_proj_weight used to be q + k + v with same dimensions + dim = int(state_dict[k].shape[0] / 3) + items_to_add[prefix + "q_proj.weight"] = state_dict[k][:dim] + items_to_add[prefix + "k_proj.weight"] = state_dict[k][dim : 2 * dim] + items_to_add[prefix + "v_proj.weight"] = state_dict[k][2 * dim :] + + keys_to_remove.append(k) + + k_bias = prefix + "in_proj_bias" + if k_bias in state_dict.keys(): + dim = int(state_dict[k].shape[0] / 3) + items_to_add[prefix + "q_proj.bias"] = state_dict[k_bias][:dim] + items_to_add[prefix + "k_proj.bias"] = state_dict[k_bias][ + dim : 2 * dim + ] + items_to_add[prefix + "v_proj.bias"] = state_dict[k_bias][2 * dim :] + + keys_to_remove.append(prefix + "in_proj_bias") + + for k in keys_to_remove: + del state_dict[k] + + for key, value in items_to_add.items(): + state_dict[key] = value diff --git a/networks/ParticleTransformer_updated_quant_weights.py b/networks/ParticleTransformer_updated_quant_weights.py new file mode 100644 index 0000000..a9adaaa --- /dev/null +++ b/networks/ParticleTransformer_updated_quant_weights.py @@ -0,0 +1,1578 @@ +''' Particle Transformer (ParT) + +Paper: "Particle Transformer for Jet Tagging" - https://arxiv.org/abs/2202.03772 +''' +import math +import random +import warnings +import copy +import torch +import torch.nn as nn +from functools import partial + +from typing import Dict, Optional, Tuple +from fairseq import utils +from fairseq.incremental_decoding_utils import with_incremental_state +from fairseq.modules.quant_noise import quant_noise +from torch import Tensor, nn +from torch.nn import Parameter +from weaver.utils.logger import _logger +import torch.nn.functional as F + +# me for quantization +#from torch.ao.nn.quantizable.modules.activation import MultiheadAttention +import sys +if '/part-vol-2/weaver-core' not in sys.path: + sys.path.append('/part-vol-2/weaver-core') + +from quantizable_mha import MultiheadAttention + + +@torch.jit.script +def delta_phi(a, b): + return (a - b + math.pi) % (2 * math.pi) - math.pi + + +@torch.jit.script +def delta_r2(eta1, phi1, eta2, phi2): + return (eta1 - eta2)**2 + delta_phi(phi1, phi2)**2 + + +def to_pt2(x, eps=1e-8): + pt2 = x[:, :2].square().sum(dim=1, keepdim=True) + if eps is not None: + pt2 = pt2.clamp(min=eps) + return pt2 + + +def to_m2(x, eps=1e-8): + m2 = x[:, 3:4].square() - x[:, :3].square().sum(dim=1, keepdim=True) + if eps is not None: + m2 = m2.clamp(min=eps) + return m2 + + +def atan2(y, x): + sx = torch.sign(x) + sy = torch.sign(y) + pi_part = (sy + sx * (sy ** 2 - 1)) * (sx - 1) * (-math.pi / 2) + atan_part = torch.arctan(y / (x + (1 - sx ** 2))) * sx ** 2 + return atan_part + pi_part + + +def to_ptrapphim(x, return_mass=True, eps=1e-8, for_onnx=False): + # x: (N, 4, ...), dim1 : (px, py, pz, E) + px, py, pz, energy = x.split((1, 1, 1, 1), dim=1) + pt = torch.sqrt(to_pt2(x, eps=eps)) + # rapidity = 0.5 * torch.log((energy + pz) / (energy - pz)) + rapidity = 0.5 * torch.log(1 + (2 * pz) / (energy - pz).clamp(min=1e-20)) + phi = (atan2 if for_onnx else torch.atan2)(py, px) + if not return_mass: + return torch.cat((pt, rapidity, phi), dim=1) + else: + m = torch.sqrt(to_m2(x, eps=eps)) + return torch.cat((pt, rapidity, phi, m), dim=1) + + +def boost(x, boostp4, eps=1e-8): + # boost x to the rest frame of boostp4 + # x: (N, 4, ...), dim1 : (px, py, pz, E) + p3 = -boostp4[:, :3] / boostp4[:, 3:].clamp(min=eps) + b2 = p3.square().sum(dim=1, keepdim=True) + gamma = (1 - b2).clamp(min=eps)**(-0.5) + gamma2 = (gamma - 1) / b2 + gamma2.masked_fill_(b2 == 0, 0) + bp = (x[:, :3] * p3).sum(dim=1, keepdim=True) + v = x[:, :3] + gamma2 * bp * p3 + x[:, 3:] * gamma * p3 + return v + + +def p3_norm(p, eps=1e-8): + return p[:, :3] / p[:, :3].norm(dim=1, keepdim=True).clamp(min=eps) + + +def pairwise_lv_fts(xi, xj, num_outputs=4, eps=1e-8, for_onnx=False): + pti, rapi, phii = to_ptrapphim(xi, False, eps=None, for_onnx=for_onnx).split((1, 1, 1), dim=1) + ptj, rapj, phij = to_ptrapphim(xj, False, eps=None, for_onnx=for_onnx).split((1, 1, 1), dim=1) + + delta = delta_r2(rapi, phii, rapj, phij).sqrt() + lndelta = torch.log(delta.clamp(min=eps)) + if num_outputs == 1: + return lndelta + + if num_outputs > 1: + ptmin = ((pti <= ptj) * pti + (pti > ptj) * ptj) if for_onnx else torch.minimum(pti, ptj) + lnkt = torch.log((ptmin * delta).clamp(min=eps)) + lnz = torch.log((ptmin / (pti + ptj).clamp(min=eps)).clamp(min=eps)) + outputs = [lnkt, lnz, lndelta] + + if num_outputs > 3: + xij = xi + xj + lnm2 = torch.log(to_m2(xij, eps=eps)) + outputs.append(lnm2) + + if num_outputs > 4: + lnds2 = torch.log(torch.clamp(-to_m2(xi - xj, eps=None), min=eps)) + outputs.append(lnds2) + + # the following features are not symmetric for (i, j) + if num_outputs > 5: + xj_boost = boost(xj, xij) + costheta = (p3_norm(xj_boost, eps=eps) * p3_norm(xij, eps=eps)).sum(dim=1, keepdim=True) + outputs.append(costheta) + + if num_outputs > 6: + deltarap = rapi - rapj + deltaphi = delta_phi(phii, phij) + outputs += [deltarap, deltaphi] + + assert (len(outputs) == num_outputs) + return torch.cat(outputs, dim=1) + + +def build_sparse_tensor(uu, idx, seq_len): + # inputs: uu (N, C, num_pairs), idx (N, 2, num_pairs) + # return: (N, C, seq_len, seq_len) + batch_size, num_fts, num_pairs = uu.size() + idx = torch.min(idx, torch.ones_like(idx) * seq_len) + i = torch.cat(( + torch.arange(0, batch_size, device=uu.device).repeat_interleave(num_fts * num_pairs).unsqueeze(0), + torch.arange(0, num_fts, device=uu.device).repeat_interleave(num_pairs).repeat(batch_size).unsqueeze(0), + idx[:, :1, :].expand_as(uu).flatten().unsqueeze(0), + idx[:, 1:, :].expand_as(uu).flatten().unsqueeze(0), + ), dim=0) + return torch.sparse_coo_tensor( + i, uu.flatten(), + size=(batch_size, num_fts, seq_len + 1, seq_len + 1), + device=uu.device).to_dense()[:, :, :seq_len, :seq_len] + + +def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.): + # From https://github.com/rwightman/pytorch-image-models/blob/18ec173f95aa220af753358bf860b16b6691edb2/timm/layers/weight_init.py#L8 + r"""Fills the input Tensor with values drawn from a truncated + normal distribution. The values are effectively drawn from the + normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)` + with values outside :math:`[a, b]` redrawn until they are within + the bounds. The method used for generating the random values works + best when :math:`a \leq \text{mean} \leq b`. + Args: + tensor: an n-dimensional `torch.Tensor` + mean: the mean of the normal distribution + std: the standard deviation of the normal distribution + a: the minimum cutoff value + b: the maximum cutoff value + Examples: + >>> w = torch.empty(3, 5) + >>> nn.init.trunc_normal_(w) + """ + def norm_cdf(x): + # Computes standard normal cumulative distribution function + return (1. + math.erf(x / math.sqrt(2.))) / 2. + + if (mean < a - 2 * std) or (mean > b + 2 * std): + warnings.warn("mean is more than 2 std from [a, b] in nn.init.trunc_normal_. " + "The distribution of values may be incorrect.", + stacklevel=2) + + with torch.no_grad(): + # Values are generated by using a truncated uniform distribution and + # then using the inverse CDF for the normal distribution. + # Get upper and lower cdf values + l = norm_cdf((a - mean) / std) + u = norm_cdf((b - mean) / std) + + # Uniformly fill tensor with values from [l, u], then translate to + # [2l-1, 2u-1]. + tensor.uniform_(2 * l - 1, 2 * u - 1) + + # Use inverse cdf transform for normal distribution to get truncated + # standard normal + tensor.erfinv_() + + # Transform to proper mean, std + tensor.mul_(std * math.sqrt(2.)) + tensor.add_(mean) + + # Clamp to ensure it's in the proper range + tensor.clamp_(min=a, max=b) + return tensor + + +class SequenceTrimmer(nn.Module): + + def __init__(self, enabled=False, target=(0.9, 1.02), **kwargs) -> None: + super().__init__(**kwargs) + self.enabled = enabled + self.target = target + self._counter = 0 + + def forward(self, x, v=None, mask=None, uu=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # uu: (N, C', P, P) + if mask is None: + mask = torch.ones_like(x[:, :1]) + mask = mask.bool() + + if self.enabled: + if self._counter < 5: + self._counter += 1 + else: + if self.training: + q = min(1, random.uniform(*self.target)) + maxlen = torch.quantile(mask.type_as(x).sum(dim=-1), q).long() + rand = torch.rand_like(mask.type_as(x)) + rand.masked_fill_(~mask, -1) + perm = rand.argsort(dim=-1, descending=True) # (N, 1, P) + mask = torch.gather(mask, -1, perm) + x = torch.gather(x, -1, perm.expand_as(x)) + if v is not None: + v = torch.gather(v, -1, perm.expand_as(v)) + if uu is not None: + uu = torch.gather(uu, -2, perm.unsqueeze(-1).expand_as(uu)) + uu = torch.gather(uu, -1, perm.unsqueeze(-2).expand_as(uu)) + else: + maxlen = mask.sum(dim=-1).max() + maxlen = max(maxlen, 1) + if maxlen < mask.size(-1): + mask = mask[:, :, :maxlen] + x = x[:, :, :maxlen] + if v is not None: + v = v[:, :, :maxlen] + if uu is not None: + uu = uu[:, :, :maxlen, :maxlen] + + return x, v, mask, uu + + +class Embed(nn.Module): + def __init__(self, input_dim, dims, normalize_input=True, activation='gelu'): + super().__init__() + + self.input_bn = nn.BatchNorm1d(input_dim) if normalize_input else None + module_list = [] + for dim in dims: + module_list.extend([ + nn.LayerNorm(input_dim), + nn.Linear(input_dim, dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + self.embed = nn.Sequential(*module_list) + + def forward(self, x): + if self.input_bn is not None: + # x: (batch, embed_dim, seq_len) + x = self.input_bn(x) + x = x.permute(2, 0, 1).contiguous() + # x: (seq_len, batch, embed_dim) + return self.embed(x) + + +class PairEmbed(nn.Module): + def __init__( + self, pairwise_lv_dim, pairwise_input_dim, dims, + remove_self_pair=False, use_pre_activation_pair=True, mode='sum', + normalize_input=True, activation='gelu', eps=1e-8, + for_onnx=False): + super().__init__() + + self.pairwise_lv_dim = pairwise_lv_dim + self.pairwise_input_dim = pairwise_input_dim + self.is_symmetric = (pairwise_lv_dim <= 5) and (pairwise_input_dim == 0) + self.remove_self_pair = remove_self_pair + self.mode = mode + self.for_onnx = for_onnx + self.pairwise_lv_fts = partial(pairwise_lv_fts, num_outputs=pairwise_lv_dim, eps=eps, for_onnx=for_onnx) + self.out_dim = dims[-1] + + if self.mode == 'concat': + input_dim = pairwise_lv_dim + pairwise_input_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.embed = nn.Sequential(*module_list) + elif self.mode == 'sum': + if pairwise_lv_dim > 0: + input_dim = pairwise_lv_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.embed = nn.Sequential(*module_list) + + if pairwise_input_dim > 0: + input_dim = pairwise_input_dim + module_list = [nn.BatchNorm1d(input_dim)] if normalize_input else [] + for dim in dims: + module_list.extend([ + nn.Conv1d(input_dim, dim, 1), + nn.BatchNorm1d(dim), + nn.GELU() if activation == 'gelu' else nn.ReLU(), + ]) + input_dim = dim + if use_pre_activation_pair: + module_list = module_list[:-1] + self.fts_embed = nn.Sequential(*module_list) + else: + raise RuntimeError('`mode` can only be `sum` or `concat`') + + def forward(self, x, uu=None): + # x: (batch, v_dim, seq_len) + # uu: (batch, v_dim, seq_len, seq_len) + assert (x is not None or uu is not None) + with torch.no_grad(): + if x is not None: + batch_size, _, seq_len = x.size() + else: + batch_size, _, seq_len, _ = uu.size() + if self.is_symmetric and not self.for_onnx: + i, j = torch.tril_indices(seq_len, seq_len, offset=-1 if self.remove_self_pair else 0, + device=(x if x is not None else uu).device) + if x is not None: + x = x.unsqueeze(-1).repeat(1, 1, 1, seq_len) + xi = x[:, :, i, j] # (batch, dim, seq_len*(seq_len+1)/2) + xj = x[:, :, j, i] + x = self.pairwise_lv_fts(xi, xj) + if uu is not None: + # (batch, dim, seq_len*(seq_len+1)/2) + uu = uu[:, :, i, j] + else: + if x is not None: + x = self.pairwise_lv_fts(x.unsqueeze(-1), x.unsqueeze(-2)) + if self.remove_self_pair: + i = torch.arange(0, seq_len, device=x.device) + x[:, :, i, i] = 0 + x = x.view(-1, self.pairwise_lv_dim, seq_len * seq_len) + if uu is not None: + uu = uu.view(-1, self.pairwise_input_dim, seq_len * seq_len) + if self.mode == 'concat': + if x is None: + pair_fts = uu + elif uu is None: + pair_fts = x + else: + pair_fts = torch.cat((x, uu), dim=1) + + if self.mode == 'concat': + elements = self.embed(pair_fts) # (batch, embed_dim, num_elements) + elif self.mode == 'sum': + if x is None: + elements = self.fts_embed(uu) + elif uu is None: + elements = self.embed(x) + else: + elements = self.embed(x) + self.fts_embed(uu) + + if self.is_symmetric and not self.for_onnx: + y = torch.zeros(batch_size, self.out_dim, seq_len, seq_len, dtype=elements.dtype, device=elements.device) + y[:, :, i, j] = elements + y[:, :, j, i] = elements + else: + y = elements.view(-1, self.out_dim, seq_len, seq_len) + return y + + +class Block(nn.Module): + def __init__(self, embed_dim=128, num_heads=8, ffn_ratio=4, + dropout=0.1, attn_dropout=0.1, activation_dropout=0.1, + add_bias_kv=False, activation='gelu', + scale_fc=True, scale_attn=True, scale_heads=True, scale_resids=True): + super().__init__() + + self.embed_dim = embed_dim + self.num_heads = num_heads + self.head_dim = embed_dim // num_heads + self.ffn_dim = embed_dim * ffn_ratio + self.interaction = None + + self.pre_attn_norm = nn.LayerNorm(embed_dim) + #self.attn = nn.MultiheadAttention( + self.attn = MultiheadAttention( + embed_dim, + num_heads, + dropout=attn_dropout, + add_bias_kv=add_bias_kv, + ) + self.post_attn_norm = nn.LayerNorm(embed_dim) if scale_attn else None + self.dropout = nn.Dropout(dropout) + + self.pre_fc_norm = nn.LayerNorm(embed_dim) + self.fc1 = nn.Linear(embed_dim, self.ffn_dim) + self.act = nn.GELU() if activation == 'gelu' else nn.ReLU() + self.act_dropout = nn.Dropout(activation_dropout) + self.post_fc_norm = nn.LayerNorm(self.ffn_dim) if scale_fc else None + self.fc2 = nn.Linear(self.ffn_dim, embed_dim) + + self.c_attn = nn.Parameter(torch.ones(num_heads), requires_grad=True) if scale_heads else None + self.w_resid = nn.Parameter(torch.ones(embed_dim), requires_grad=True) if scale_resids else None + def getAttention(self): + return self.interaction + + def forward(self, x, x_cls=None, padding_mask=None, attn_mask=None): + """ + Args: + x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)` + x_cls (Tensor, optional): class token input to the layer of shape `(1, batch, embed_dim)` + padding_mask (ByteTensor, optional): binary + ByteTensor of shape `(batch, seq_len)` where padding + elements are indicated by ``1``. + + Returns: + encoded output of shape `(seq_len, batch, embed_dim)` + """ + + if x_cls is not None: + with torch.no_grad(): + # prepend one element for x_cls: -> (batch, 1+seq_len) + padding_mask = torch.cat((torch.zeros_like(padding_mask[:, :1]), padding_mask), dim=1) + # class attention: https://arxiv.org/pdf/2103.17239.pdf + residual = x_cls + u = torch.cat((x_cls, x), dim=0) # (seq_len+1, batch, embed_dim) + u = self.pre_attn_norm(u) + x = self.attn(x_cls, u, u, key_padding_mask=padding_mask)[0] # (1, batch, embed_dim) + else: + residual = x + x = self.pre_attn_norm(x) + x= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[0] # (seq_len, batch, embed_dim) + y= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[1] + self.interaction = y + + + if self.c_attn is not None: + tgt_len = x.size(0) + x = x.view(tgt_len, -1, self.num_heads, self.head_dim) + x = torch.einsum('tbhd,h->tbdh', x, self.c_attn) + x = x.reshape(tgt_len, -1, self.embed_dim) + if self.post_attn_norm is not None: + x = self.post_attn_norm(x) + x = self.dropout(x) + x += residual + + residual = x + x = self.pre_fc_norm(x) + x = self.act(self.fc1(x)) + x = self.act_dropout(x) + if self.post_fc_norm is not None: + x = self.post_fc_norm(x) + x = self.fc2(x) + x = self.dropout(x) + if self.w_resid is not None: + residual = torch.mul(self.w_resid, residual) + x += residual + + return x + + +class ParticleTransformer(nn.Module): + + def __init__(self, + input_dim, + num_classes=10, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[64, 64, 64], + pair_embed_dims=[32, 32, 32], + num_heads=1, + num_layers=1, + num_cls_layers=1, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.attention_matrix = None + self.for_inference = for_inference + self.use_amp = use_amp + embed_dim = embed_dims[-1] if len(embed_dims) > 0 else input_dim + default_cfg = dict(embed_dim=embed_dim, num_heads=num_heads, ffn_ratio=4, + dropout=0.1, attn_dropout=0.1, activation_dropout=0.1, + add_bias_kv=False, activation=activation, + scale_fc=True, scale_attn=True, scale_heads=True, scale_resids=True) + + cfg_block = copy.deepcopy(default_cfg) + if block_params is not None: + cfg_block.update(block_params) + _logger.info('cfg_block: %s' % str(cfg_block)) + + cfg_cls_block = copy.deepcopy(default_cfg) + if cls_block_params is not None: + cfg_cls_block.update(cls_block_params) + _logger.info('cfg_cls_block: %s' % str(cfg_cls_block)) + + self.pair_extra_dim = pair_extra_dim + self.embed = Embed(input_dim, embed_dims, activation=activation) if len(embed_dims) > 0 else nn.Identity() + self.pair_embed = PairEmbed( + pair_input_dim, pair_extra_dim, pair_embed_dims + [cfg_block['num_heads']], + remove_self_pair=remove_self_pair, use_pre_activation_pair=use_pre_activation_pair, + for_onnx=for_inference) if pair_embed_dims is not None and pair_input_dim + pair_extra_dim > 0 else None + self.blocks = nn.ModuleList([Block(**cfg_block) for _ in range(num_layers)]) + self.cls_blocks = nn.ModuleList([Block(**cfg_cls_block) for _ in range(num_cls_layers)]) + self.norm = nn.LayerNorm(embed_dim) + self.interactionMatrix = None + + if fc_params is not None: + fcs = [] + in_dim = embed_dim + for out_dim, drop_rate in fc_params: + fcs.append(nn.Sequential(nn.Linear(in_dim, out_dim), nn.ReLU(), nn.Dropout(drop_rate))) + in_dim = out_dim + fcs.append(nn.Linear(in_dim, num_classes)) + self.fc = nn.Sequential(*fcs) + else: + self.fc = None + + # init + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim), requires_grad=True) + trunc_normal_(self.cls_token, std=.02) + + @torch.jit.ignore + def no_weight_decay(self): + return {'cls_token', } + + def getAttention(self): + return self.attention_matrix + + def getInteraction(self): + return self.interactionMatrix + + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # for pytorch: uu (N, C', num_pairs), uu_idx (N, 2, num_pairs) + # for onnx: uu (N, C', P, P), uu_idx=None + + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for block in self.blocks: + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + self.interactionMatrix = attn_mask + self.attention_matrix = block.interaction + + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + + + return output + + +class ParticleTransformerTagger(nn.Module): + + def __init__(self, + pf_input_dim, + sv_input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[128, 512, 128], + pair_embed_dims=[64, 64, 64], + num_heads=8, + num_layers=8, + num_cls_layers=2, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.use_amp = use_amp + + self.pf_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.sv_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + + self.pf_embed = Embed(pf_input_dim, embed_dims, activation=activation) + self.sv_embed = Embed(sv_input_dim, embed_dims, activation=activation) + + self.part = ParticleTransformer(input_dim=embed_dims[-1], + num_classes=num_classes, + # network configurations + pair_input_dim=pair_input_dim, + pair_extra_dim=pair_extra_dim, + remove_self_pair=remove_self_pair, + use_pre_activation_pair=use_pre_activation_pair, + embed_dims=[], + pair_embed_dims=pair_embed_dims, + num_heads=num_heads, + num_layers=num_layers, + num_cls_layers=num_cls_layers, + block_params=block_params, + cls_block_params=cls_block_params, + fc_params=fc_params, + activation=activation, + # misc + trim=False, + for_inference=for_inference, + use_amp=use_amp) + + @torch.jit.ignore + def no_weight_decay(self): + return {'part.cls_token', } + + def forward(self, pf_x, pf_v=None, pf_mask=None, sv_x=None, sv_v=None, sv_mask=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + + with torch.no_grad(): + pf_x, pf_v, pf_mask, _ = self.pf_trimmer(pf_x, pf_v, pf_mask) + sv_x, sv_v, sv_mask, _ = self.sv_trimmer(sv_x, sv_v, sv_mask) + v = torch.cat([pf_v, sv_v], dim=2) + mask = torch.cat([pf_mask, sv_mask], dim=2) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + pf_x = self.pf_embed(pf_x) # after embed: (seq_len, batch, embed_dim) + sv_x = self.sv_embed(sv_x) + x = torch.cat([pf_x, sv_x], dim=0) + + return self.part(x, v, mask) + + +class ParticleTransformerTaggerWithExtraPairFeatures(nn.Module): + + def __init__(self, + pf_input_dim, + sv_input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[128, 512, 128], + pair_embed_dims=[64, 64, 64], + num_heads=8, + num_layers=8, + num_cls_layers=2, + block_params=None, + cls_block_params={'dropout': 0, 'attn_dropout': 0, 'activation_dropout': 0}, + fc_params=[], + activation='gelu', + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs) -> None: + super().__init__(**kwargs) + + self.use_amp = use_amp + self.for_inference = for_inference + + self.pf_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.sv_trimmer = SequenceTrimmer(enabled=trim and not for_inference) + + self.pf_embed = Embed(pf_input_dim, embed_dims, activation=activation) + self.sv_embed = Embed(sv_input_dim, embed_dims, activation=activation) + + self.part = ParticleTransformer(input_dim=embed_dims[-1], + num_classes=num_classes, + # network configurations + pair_input_dim=pair_input_dim, + pair_extra_dim=pair_extra_dim, + remove_self_pair=remove_self_pair, + use_pre_activation_pair=use_pre_activation_pair, + embed_dims=[], + pair_embed_dims=pair_embed_dims, + num_heads=num_heads, + num_layers=num_layers, + num_cls_layers=num_cls_layers, + block_params=block_params, + cls_block_params=cls_block_params, + fc_params=fc_params, + activation=activation, + # misc + trim=False, + for_inference=for_inference, + use_amp=use_amp) + + @torch.jit.ignore + def no_weight_decay(self): + return {'part.cls_token', } + + def forward(self, pf_x, pf_v=None, pf_mask=None, sv_x=None, sv_v=None, sv_mask=None, pf_uu=None, pf_uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + + with torch.no_grad(): + if not self.for_inference: + if pf_uu_idx is not None: + pf_uu = build_sparse_tensor(pf_uu, pf_uu_idx, pf_x.size(-1)) + + pf_x, pf_v, pf_mask, pf_uu = self.pf_trimmer(pf_x, pf_v, pf_mask, pf_uu) + sv_x, sv_v, sv_mask, _ = self.sv_trimmer(sv_x, sv_v, sv_mask) + v = torch.cat([pf_v, sv_v], dim=2) + mask = torch.cat([pf_mask, sv_mask], dim=2) + uu = torch.zeros(v.size(0), pf_uu.size(1), v.size(2), v.size(2), dtype=v.dtype, device=v.device) + uu[:, :, :pf_x.size(2), :pf_x.size(2)] = pf_uu + + with torch.cuda.amp.autocast(enabled=self.use_amp): + pf_x = self.pf_embed(pf_x) # after embed: (seq_len, batch, embed_dim) + sv_x = self.sv_embed(sv_x) + x = torch.cat([pf_x, sv_x], dim=0) + + return self.part(x, v, mask, uu) + + + +class ParticleTransformerAdd(ParticleTransformer): + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for i, block in enumerate(self.blocks): + x_residual = x.clone() # Make a copy of x for residual connection + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + if i < len(self.blocks) - 1: # Exclude the last block + x = x + x_residual # Add residual connection + self.attention_matrix = x + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + return output + def getAttention(self): + return self.attention_matrix + + +class LinBlock(nn.Module): + def __init__( + self, + embed_dim=128, + num_heads=8, + max_seq_len=128, + attn_type="linformer", + compressed=4, + bucket_size=32, + n_hashes=4, + d_state=16, + d_conv=4, + expand=2, + ffn_ratio=4, + dropout=0.1, + attn_dropout=0.1, + activation_dropout=0.1, + add_bias_kv=False, + activation="gelu", + scale_fc=True, + scale_attn=True, + scale_heads=True, + scale_resids=True, + ): + super().__init__() + + self.embed_dim = embed_dim + self.num_heads = num_heads + self.max_seq_len = max_seq_len + self.compressed = compressed + self.head_dim = embed_dim // num_heads + self.ffn_dim = embed_dim * ffn_ratio + self.attn_type = attn_type + + self.pre_attn_norm = nn.LayerNorm(embed_dim) + + self.attn = MultiheadLinearAttention( + embed_dim, + num_heads, + dropout=attn_dropout, + add_bias_kv=add_bias_kv, + max_seq_len=max_seq_len, + compressed=compressed, + ) + self.post_attn_norm = nn.LayerNorm(embed_dim) if scale_attn else None + self.dropout = nn.Dropout(dropout) + + self.pre_fc_norm = nn.LayerNorm(embed_dim) + self.fc1 = nn.Linear(embed_dim, self.ffn_dim) + self.act = nn.GELU() if activation == "gelu" else nn.ReLU() + self.act_dropout = nn.Dropout(activation_dropout) + self.post_fc_norm = nn.LayerNorm(self.ffn_dim) if scale_fc else None + self.fc2 = nn.Linear(self.ffn_dim, embed_dim) + + self.c_attn = ( + nn.Parameter(torch.ones(num_heads), requires_grad=True) + if scale_heads + else None + ) + self.w_resid = ( + nn.Parameter(torch.ones(embed_dim), requires_grad=True) + if scale_resids + else None + ) + self.interaction = None + + + def getAttention(self): + return self.interaction + + def forward(self, x, x_cls=None, padding_mask=None, attn_mask=None): + """ + Args: + x (Tensor): input to the layer of shape `(seq_len, batch, embed_dim)` + x_cls (Tensor, optional): class token input to the layer of shape `(1, batch, embed_dim)` + padding_mask (ByteTensor, optional): binary + ByteTensor of shape `(batch, seq_len)` where padding + elements are indicated by ``1``. + + Returns: + encoded output of shape `(seq_len, batch, embed_dim)` + """ + + if x_cls is not None: + with torch.no_grad(): + # prepend one element for x_cls: -> (batch, 1+seq_len) + padding_mask = torch.cat( + (torch.zeros_like(padding_mask[:, :1]), padding_mask), dim=1 + ) + # class attention: https://arxiv.org/pdf/2103.17239.pdf + residual = x_cls + u = torch.cat((x_cls, x), dim=0) # (seq_len+1, batch, embed_dim) + u = self.pre_attn_norm(u) + x = self.full_attn(x_cls, u, u, key_padding_mask=padding_mask)[ + 0 + ] # (1, batch, embed_dim) + else: + residual = x + x = self.pre_attn_norm(x) + if self.attn_type == "linformer": + x = self.attn(x, x, x, key_padding_mask=padding_mask, attn_mask=attn_mask)[ + 0 + ] # (seq_len, batch, embed_dim) + y= self.attn(x, x, x, key_padding_mask=padding_mask, + attn_mask=attn_mask)[1] + self.interaction = y + + elif self.attn_type == "performer": + x = self.attn(x, x, input_mask=padding_mask, attn_mask=attn_mask)[ + 0 + ] # (seq_len, batch, embed_dim) + elif self.attn_type == "reformer": + x = self.attn(x) + elif self.attn_type == "mamba": + x = self.attn(x) + elif self.attn_type == "pairs": + x = self.attn(x, attn_mask)[0] + + if self.c_attn is not None: + tgt_len = x.size(0) + x = x.view(tgt_len, -1, self.num_heads, self.head_dim) + x = torch.einsum("tbhd,h->tbdh", x, self.c_attn) + x = x.reshape(tgt_len, -1, self.embed_dim) + if self.post_attn_norm is not None: + x = self.post_attn_norm(x) + x = self.dropout(x) + x += residual + + residual = x + x = self.pre_fc_norm(x) + x = self.act(self.fc1(x)) + x = self.act_dropout(x) + if self.post_fc_norm is not None: + x = self.post_fc_norm(x) + x = self.fc2(x) + x = self.dropout(x) + if self.w_resid is not None: + residual = torch.mul(self.w_resid, residual) + x += residual + + return x + + +class EfficientParticleTransformer(nn.Module): + def __init__( + self, + input_dim, + num_classes=None, + # network configurations + pair_input_dim=4, + pair_extra_dim=0, + remove_self_pair=False, + use_pre_activation_pair=True, + embed_dims=[64, 64, 64], + pair_embed_dims=[32,32,32], # [64, 64, 64], + num_heads=1, + num_layers=1, + num_cls_layers=1, + block_params=None, + cls_block_params={"dropout": 0, "attn_dropout": 0, "activation_dropout": 0}, + fc_params=[], + activation="gelu", + # misc + trim=True, + for_inference=False, + use_amp=False, + **kwargs + ) -> None: + super().__init__(**kwargs) + + self.trimmer = SequenceTrimmer(enabled=trim and not for_inference) + self.for_inference = for_inference + self.use_amp = use_amp + + embed_dim = embed_dims[-1] if len(embed_dims) > 0 else input_dim + default_cfg = dict( + embed_dim=embed_dim, + num_heads=num_heads, + ffn_ratio=4, + dropout=0.1, + attn_dropout=0.1, + activation_dropout=0.1, + add_bias_kv=False, + activation=activation, + scale_fc=True, + scale_attn=True, + scale_heads=True, + scale_resids=True, + ) + + cfg_block = copy.deepcopy(default_cfg) + if block_params is not None: + cfg_block.update(block_params) + _logger.info("cfg_block: %s" % str(cfg_block)) + + cfg_cls_block = copy.deepcopy(default_cfg) + if cls_block_params is not None: + cfg_cls_block.update(cls_block_params) + _logger.info("cfg_cls_block: %s" % str(cfg_cls_block)) + + self.pair_extra_dim = pair_extra_dim + self.embed = ( + Embed(input_dim, embed_dims, activation=activation) + if len(embed_dims) > 0 + else nn.Identity() + ) + self.pair_embed = PairEmbed( + pair_input_dim, pair_extra_dim, pair_embed_dims + [cfg_block['num_heads']], + remove_self_pair=remove_self_pair, use_pre_activation_pair=use_pre_activation_pair, + for_onnx=for_inference) if pair_embed_dims is not None and pair_input_dim + pair_extra_dim > 0 else None + self.blocks = nn.ModuleList([LinBlock(**cfg_block) for _ in range(num_layers)]) + self.cls_blocks = nn.ModuleList( + [Block(**cfg_cls_block) for _ in range(num_cls_layers)] + ) + self.norm = nn.LayerNorm(embed_dim) + + if fc_params is not None: + fcs = [] + in_dim = embed_dim + for out_dim, drop_rate in fc_params: + fcs.append( + nn.Sequential( + nn.Linear(in_dim, out_dim), nn.ReLU(), nn.Dropout(drop_rate) + ) + ) + in_dim = out_dim + fcs.append(nn.Linear(in_dim, num_classes)) + self.fc = nn.Sequential(*fcs) + else: + self.fc = None + + # init + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim), requires_grad=True) + trunc_normal_(self.cls_token, std=0.02) + self.interactionMatrix = None + self.attention_matrix = None + + + + def getAttention(self): + return self.attention_matrix + + def getInteraction(self): + return self.interactionMatrix + + @torch.jit.ignore + def no_weight_decay(self): + return { + "cls_token", + } + + def forward(self, x, v=None, mask=None, uu=None, uu_idx=None): + # x: (N, C, P) + # v: (N, 4, P) [px,py,pz,energy] + # mask: (N, 1, P) -- real particle = 1, padded = 0 + # for pytorch: uu (N, C', num_pairs), uu_idx (N, 2, num_pairs) + # for onnx: uu (N, C', P, P), uu_idx=None + + with torch.no_grad(): + if not self.for_inference: + if uu_idx is not None: + uu = build_sparse_tensor(uu, uu_idx, x.size(-1)) + x, v, mask, uu = self.trimmer(x, v, mask, uu) + padding_mask = ~mask.squeeze(1) # (N, P) + + with torch.cuda.amp.autocast(enabled=self.use_amp): + # input embedding + x = self.embed(x).masked_fill(~mask.permute(2, 0, 1), 0) # (P, N, C) + attn_mask = None + if (v is not None or uu is not None) and self.pair_embed is not None: + attn_mask = self.pair_embed(v, uu).view(-1, v.size(-1), v.size(-1)) # (N*num_heads, P, P) + + # transform + for block in self.blocks: + x = block(x, x_cls=None, padding_mask=padding_mask, attn_mask=attn_mask) + self.interactionMatrix = attn_mask + self.attention_matrix = block.interaction + + # extract class token + cls_tokens = self.cls_token.expand(1, x.size(1), -1) # (1, N, C) + for block in self.cls_blocks: + cls_tokens = block(x, x_cls=cls_tokens, padding_mask=padding_mask) + + x_cls = self.norm(cls_tokens).squeeze(0) + + # fc + if self.fc is None: + return x_cls + output = self.fc(x_cls) + if self.for_inference: + output = torch.softmax(output, dim=1) + # print('output:\n', output) + return output + +@with_incremental_state +class MultiheadLinearAttention(nn.Module): + """Multi-headed linformer attention. + + Projects the key and values down to the compressed dimension, before computing self-attention. + + See "Linformer: Self-Attention with Linear Complexity" for more details. + """ + + def __init__( + self, + embed_dim, + num_heads, + kdim=None, + vdim=None, + dropout=0.0, + bias=True, + add_bias_kv=False, + add_zero_attn=False, + self_attention=False, + encoder_decoder_attention=False, + q_noise=0.0, + qn_block_size=8, + compressed=1, + max_seq_len=256, + shared_kv_compressed=0, + shared_compress_layer=None, + freeze_compress=0, + ): + super().__init__() + self.embed_dim = embed_dim + self.kdim = kdim if kdim is not None else embed_dim + self.vdim = vdim if vdim is not None else embed_dim + self.qkv_same_dim = self.kdim == embed_dim and self.vdim == embed_dim + + self.num_heads = num_heads + self.dropout = dropout + self.head_dim = embed_dim // num_heads + assert ( + self.head_dim * num_heads == self.embed_dim + ), "embed_dim must be divisible by num_heads" + self.scaling = self.head_dim**-0.5 + + self.self_attention = self_attention + self.encoder_decoder_attention = encoder_decoder_attention + + assert not self.self_attention or self.qkv_same_dim, ( + "Self-attention requires query, key and " "value to be of the same size" + ) + + self.k_proj = quant_noise( + nn.Linear(self.kdim, embed_dim, bias=bias), q_noise, qn_block_size + ) + self.v_proj = quant_noise( + nn.Linear(self.vdim, embed_dim, bias=bias), q_noise, qn_block_size + ) + self.q_proj = quant_noise( + nn.Linear(embed_dim, embed_dim, bias=bias), q_noise, qn_block_size + ) + + # used for compress sequence to subsequence + if shared_compress_layer is None: + self.compress_seq_len = max_seq_len // compressed + self.compress_k = nn.Linear(max_seq_len, self.compress_seq_len, bias=False) + if shared_kv_compressed == 0: + self.compress_v = nn.Linear( + max_seq_len, self.compress_seq_len, bias=False + ) + self.layerwise_sharing = False + else: + self.compress_k = shared_compress_layer + if shared_kv_compressed == 0: + self.compress_v = shared_compress_layer + self.layerwise_sharing = True + self.shared_kv_compressed = shared_kv_compressed + + self.out_proj = quant_noise( + nn.Linear(embed_dim, embed_dim, bias=bias), q_noise, qn_block_size + ) + + if add_bias_kv: + self.bias_k = Parameter(torch.Tensor(1, 1, embed_dim)) + self.bias_v = Parameter(torch.Tensor(1, 1, embed_dim)) + else: + self.bias_k = self.bias_v = None + + self.add_zero_attn = add_zero_attn + + self.reset_parameters() + + if freeze_compress == 1: + self.compress_k.weight.requires_grad = False + if shared_kv_compressed == 0: + self.compress_v.weight.requires_grad = False + + self.onnx_trace = False + + def prepare_for_onnx_export_(self): + self.onnx_trace = True + + def reset_parameters(self): + if self.qkv_same_dim: + # Empirically observed the convergence to be much better with + # the scaled initialization + nn.init.xavier_uniform_(self.k_proj.weight, gain=1 / math.sqrt(2)) + nn.init.xavier_uniform_(self.v_proj.weight, gain=1 / math.sqrt(2)) + nn.init.xavier_uniform_(self.q_proj.weight, gain=1 / math.sqrt(2)) + if ( + not self.layerwise_sharing + ): # otherwise, we already initialize the parameters + nn.init.xavier_uniform_(self.compress_k.weight, gain=1 / math.sqrt(2)) + if self.shared_kv_compressed == 0: + nn.init.xavier_uniform_( + self.compress_v.weight, gain=1 / math.sqrt(2) + ) + else: + nn.init.xavier_uniform_(self.k_proj.weight) + nn.init.xavier_uniform_(self.v_proj.weight) + nn.init.xavier_uniform_(self.q_proj.weight) + if ( + not self.layerwise_sharing + ): # otherwise, we already initialize the parameters + nn.init.xavier_uniform_(self.compress_k.weight) + if self.shared_kv_compressed == 0: + nn.init.xavier_uniform_(self.compress_v.weight) + + nn.init.xavier_uniform_(self.out_proj.weight) + if self.out_proj.bias is not None: + nn.init.constant_(self.out_proj.bias, 0.0) + if self.bias_k is not None: + nn.init.xavier_normal_(self.bias_k) + if self.bias_v is not None: + nn.init.xavier_normal_(self.bias_v) + + def forward( + self, + query, + key: Optional[Tensor], + value: Optional[Tensor], + key_padding_mask: Optional[Tensor] = None, + incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] = None, + need_weights: bool = True, + static_kv: bool = False, + attn_mask: Optional[Tensor] = None, + before_softmax: bool = False, + need_head_weights: bool = False, + ) -> Tuple[Tensor, Optional[Tensor]]: + """Input shape: Time x Batch x Channel + + Args: + key_padding_mask (ByteTensor, optional): mask to exclude + keys that are pads, of shape `(batch, src_len)`, where + padding elements are indicated by 1s. + need_weights (bool, optional): return the attention weights, + averaged over heads (default: False). + attn_mask (ByteTensor, optional): typically used to + implement causal attention, where the mask prevents the + attention from looking forward in time (default: None). + before_softmax (bool, optional): return the raw attention + weights and values before the attention softmax. + need_head_weights (bool, optional): return the attention + weights for each head. Implies *need_weights*. Default: + return the average attention weights over all heads. + """ + if need_head_weights: + need_weights = True + + tgt_len, bsz, embed_dim = query.size() + assert embed_dim == self.embed_dim + assert list(query.size()) == [tgt_len, bsz, embed_dim] + + if incremental_state is not None: + saved_state = self._get_input_buffer(incremental_state) + if saved_state is not None and "prev_key" in saved_state: + # previous time steps are cached - no need to recompute + # key and value if they are static + if static_kv: + assert self.encoder_decoder_attention and not self.self_attention + key = value = None + else: + saved_state = None + + if self.self_attention: + q = self.q_proj(query) + + k_input = query.permute(1, 2, 0).contiguous() # B * C * T + k_input = ( + F.linear(k_input, self.compress_k.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + k = self.k_proj(k_input) + + v_input = query.permute(1, 2, 0).contiguous() # B * C * T + if self.shared_kv_compressed == 0: + v_input = ( + F.linear(v_input, self.compress_v.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + if self.shared_kv_compressed == 1: # use shared kv compressed linear layer + v_input = ( + F.linear(v_input, self.compress_k.weight[:, 0:tgt_len]) + .permute(2, 0, 1) + .contiguous() + ) + v = self.v_proj(v_input) + elif self.encoder_decoder_attention: + # encoder-decoder attention + q = self.q_proj(query) + if key is None: + assert value is None + k = v = None + else: + k = self.k_proj(key) + v = self.v_proj(key) + + else: + assert key is not None and value is not None + q = self.q_proj(query) + k = self.k_proj(key) + v = self.v_proj(value) + q *= self.scaling + + if self.bias_k is not None: + assert self.bias_v is not None + k = torch.cat([k, self.bias_k.repeat(1, bsz, 1)]) + v = torch.cat([v, self.bias_v.repeat(1, bsz, 1)]) + if attn_mask is not None: + attn_mask = torch.cat( + [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1 + ) + if key_padding_mask is not None: + key_padding_mask = torch.cat( + [ + key_padding_mask, + key_padding_mask.new_zeros(key_padding_mask.size(0), 1), + ], + dim=1, + ) + + q = ( + q.contiguous() + .view(tgt_len, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + if k is not None: + k = ( + k.contiguous() + .view(-1, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + if v is not None: + v = ( + v.contiguous() + .view(-1, bsz * self.num_heads, self.head_dim) + .transpose(0, 1) + ) + + if saved_state is not None: + # saved states are stored with shape (bsz, num_heads, seq_len, head_dim) + if "prev_key" in saved_state: + _prev_key = saved_state["prev_key"] + assert _prev_key is not None + prev_key = _prev_key.view(bsz * self.num_heads, -1, self.head_dim) + if static_kv: + k = prev_key + else: + assert k is not None + k = torch.cat([prev_key, k], dim=1) + if "prev_value" in saved_state: + _prev_value = saved_state["prev_value"] + assert _prev_value is not None + prev_value = _prev_value.view(bsz * self.num_heads, -1, self.head_dim) + if static_kv: + v = prev_value + else: + assert v is not None + v = torch.cat([prev_value, v], dim=1) + prev_key_padding_mask: Optional[Tensor] = None + if "prev_key_padding_mask" in saved_state: + prev_key_padding_mask = saved_state["prev_key_padding_mask"] + assert k is not None and v is not None + key_padding_mask = MultiheadLinearAttention._append_prev_key_padding_mask( + key_padding_mask=key_padding_mask, + prev_key_padding_mask=prev_key_padding_mask, + batch_size=bsz, + src_len=k.size(1), + static_kv=static_kv, + ) + + saved_state["prev_key"] = k.view(bsz, self.num_heads, -1, self.head_dim) + saved_state["prev_value"] = v.view(bsz, self.num_heads, -1, self.head_dim) + saved_state["prev_key_padding_mask"] = key_padding_mask + # In this branch incremental_state is never None + assert incremental_state is not None + incremental_state = self._set_input_buffer(incremental_state, saved_state) + assert k is not None + src_len = k.size(1) + + if self.add_zero_attn: + assert v is not None + src_len += 1 + k = torch.cat([k, k.new_zeros((k.size(0), 1) + k.size()[2:])], dim=1) + v = torch.cat([v, v.new_zeros((v.size(0), 1) + v.size()[2:])], dim=1) + if attn_mask is not None: + attn_mask = torch.cat( + [attn_mask, attn_mask.new_zeros(attn_mask.size(0), 1)], dim=1 + ) + + attn_weights = torch.bmm(q, k.transpose(1, 2)) + attn_weights = MultiheadLinearAttention.apply_sparse_mask( + attn_weights, tgt_len, src_len, bsz + ) + + assert list(attn_weights.size()) == [bsz * self.num_heads, tgt_len, src_len] + + if attn_mask is not None: + attn_mask = attn_mask.unsqueeze(0) + if self.onnx_trace: + attn_mask = attn_mask.repeat(attn_weights.size(0), 1, 1) + attn_weights += attn_mask + + if before_softmax: + return attn_weights, v + + attn_weights_float = utils.softmax( + attn_weights, dim=-1, onnx_trace=self.onnx_trace + ) + attn_weights = attn_weights_float.type_as(attn_weights) + attn_probs = F.dropout( + attn_weights, + p=self.dropout, + training=self.training, + ) + assert v is not None + attn = torch.bmm(attn_probs, v) + assert list(attn.size()) == [bsz * self.num_heads, tgt_len, self.head_dim] + if self.onnx_trace and attn.size(1) == 1: + # when ONNX tracing a single decoder step (sequence length == 1) + # the transpose is a no-op copy before view, thus unnecessary + attn = attn.contiguous().view(tgt_len, bsz, embed_dim) + else: + attn = attn.transpose(0, 1).contiguous().view(tgt_len, bsz, embed_dim) + attn = self.out_proj(attn) + attn_weights: Optional[Tensor] = None + if need_weights: + attn_weights = attn_weights_float.view( + bsz, self.num_heads, tgt_len, src_len + ).transpose(1, 0) + if not need_head_weights: + # average attention weights over heads + attn_weights = attn_weights.mean(dim=0) + + return attn, attn_weights + + @staticmethod + def _append_prev_key_padding_mask( + key_padding_mask: Optional[Tensor], + prev_key_padding_mask: Optional[Tensor], + batch_size: int, + src_len: int, + static_kv: bool, + ) -> Optional[Tensor]: + # saved key padding masks have shape (bsz, seq_len) + if prev_key_padding_mask is not None and static_kv: + new_key_padding_mask = prev_key_padding_mask + elif prev_key_padding_mask is not None and key_padding_mask is not None: + new_key_padding_mask = torch.cat( + [prev_key_padding_mask.float(), key_padding_mask.float()], dim=1 + ) + # During incremental decoding, as the padding token enters and + # leaves the frame, there will be a time when prev or current + # is None + elif prev_key_padding_mask is not None: + filler = torch.zeros( + (batch_size, src_len - prev_key_padding_mask.size(1)), + device=prev_key_padding_mask.device, + ) + new_key_padding_mask = torch.cat( + [prev_key_padding_mask.float(), filler.float()], dim=1 + ) + elif key_padding_mask is not None: + filler = torch.zeros( + (batch_size, src_len - key_padding_mask.size(1)), + device=key_padding_mask.device, + ) + new_key_padding_mask = torch.cat( + [filler.float(), key_padding_mask.float()], dim=1 + ) + else: + new_key_padding_mask = prev_key_padding_mask + return new_key_padding_mask + + @torch.jit.export + def reorder_incremental_state( + self, + incremental_state: Dict[str, Dict[str, Optional[Tensor]]], + new_order: Tensor, + ): + """Reorder buffered internal state (for incremental generation).""" + input_buffer = self._get_input_buffer(incremental_state) + if input_buffer is not None: + for k in input_buffer.keys(): + input_buffer_k = input_buffer[k] + if input_buffer_k is not None: + if self.encoder_decoder_attention and input_buffer_k.size( + 0 + ) == new_order.size(0): + break + input_buffer[k] = input_buffer_k.index_select(0, new_order) + incremental_state = self._set_input_buffer(incremental_state, input_buffer) + return incremental_state + + def _get_input_buffer( + self, incremental_state: Optional[Dict[str, Dict[str, Optional[Tensor]]]] + ) -> Dict[str, Optional[Tensor]]: + result = self.get_incremental_state(incremental_state, "attn_state") + if result is not None: + return result + else: + empty_result: Dict[str, Optional[Tensor]] = {} + return empty_result + + def _set_input_buffer( + self, + incremental_state: Dict[str, Dict[str, Optional[Tensor]]], + buffer: Dict[str, Optional[Tensor]], + ): + return self.set_incremental_state(incremental_state, "attn_state", buffer) + + def apply_sparse_mask(attn_weights, tgt_len: int, src_len: int, bsz: int): + return attn_weights + + def upgrade_state_dict_named(self, state_dict, name): + prefix = name + "." if name != "" else "" + items_to_add = {} + keys_to_remove = [] + for k in state_dict.keys(): + if k.endswith(prefix + "in_proj_weight"): + # in_proj_weight used to be q + k + v with same dimensions + dim = int(state_dict[k].shape[0] / 3) + items_to_add[prefix + "q_proj.weight"] = state_dict[k][:dim] + items_to_add[prefix + "k_proj.weight"] = state_dict[k][dim : 2 * dim] + items_to_add[prefix + "v_proj.weight"] = state_dict[k][2 * dim :] + + keys_to_remove.append(k) + + k_bias = prefix + "in_proj_bias" + if k_bias in state_dict.keys(): + dim = int(state_dict[k].shape[0] / 3) + items_to_add[prefix + "q_proj.bias"] = state_dict[k_bias][:dim] + items_to_add[prefix + "k_proj.bias"] = state_dict[k_bias][ + dim : 2 * dim + ] + items_to_add[prefix + "v_proj.bias"] = state_dict[k_bias][2 * dim :] + + keys_to_remove.append(prefix + "in_proj_bias") + + for k in keys_to_remove: + del state_dict[k] + + for key, value in items_to_add.items(): + state_dict[key] = value diff --git a/networks/quantizable_mha.py b/networks/quantizable_mha.py new file mode 100644 index 0000000..1e81466 --- /dev/null +++ b/networks/quantizable_mha.py @@ -0,0 +1,473 @@ +# mypy: allow-untyped-defs +import torch +import torch.jit # this is needed to avoid a circular import +from torch import nn +import torch.nn.functional as nnF + +from torch import Tensor +from typing import Optional, Tuple + +import warnings + +__all__ = [ + "MultiheadAttention" +] + +class MultiheadAttention(nn.MultiheadAttention): + _FLOAT_MODULE = nn.MultiheadAttention + + r"""Quantizable implementation of the MultiheadAttention. + + Note:: + Please, refer to :class:`~torch.nn.MultiheadAttention` for more + information + + Allows the model to jointly attend to information from different + representation subspaces. + See reference: Attention Is All You Need + + The original MHA module is not quantizable. + This reimplements it by explicitly instantiating the linear layers. + + .. math:: + \text{MultiHead}(Q, K, V) = \text{Concat}(head_1,\dots,head_h)W^O + \text{where} head_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V) + + Args: + embed_dim: total dimension of the model. + num_heads: parallel attention heads. + dropout: a Dropout layer on attn_output_weights. Default: 0.0. + bias: add bias as module parameter. Default: True. + add_bias_kv: add bias to the key and value sequences at dim=0. + add_zero_attn: add a new batch of zeros to the key and + value sequences at dim=1. + kdim: total number of features in key. Default: None. + vdim: total number of features in value. Default: None. + batch_first: If ``True``, then the input and output tensors are provided + as (batch, seq, feature). Default: ``False`` (seq, batch, feature). + + Note that if :attr:`kdim` and :attr:`vdim` are None, they will be set + to :attr:`embed_dim` such that query, key, and value have the same + number of features. + + Examples:: + + >>> import torch.ao.nn.quantizable as nnqa + >>> multihead_attn = nnqa.MultiheadAttention(embed_dim, num_heads) + >>> attn_output, attn_output_weights = multihead_attn(query, key, value) + + Note:: + Please, follow the quantization flow to convert the quantizable MHA. + """ + __constants__ = ['batch_first'] + + def __init__(self, embed_dim: int, num_heads: int, + dropout: float = 0., bias: bool = True, + add_bias_kv: bool = False, add_zero_attn: bool = False, + kdim: Optional[int] = None, vdim: Optional[int] = None, batch_first: bool = False, + device=None, dtype=None) -> None: + factory_kwargs = {'device': device, 'dtype': dtype} + super().__init__(embed_dim, num_heads, dropout, + bias, add_bias_kv, + add_zero_attn, kdim, vdim, batch_first, + **factory_kwargs) + self.linear_Q = nn.Linear(self.embed_dim, self.embed_dim, bias=bias, **factory_kwargs) + self.linear_K = nn.Linear(self.kdim, self.embed_dim, bias=bias, **factory_kwargs) + self.linear_V = nn.Linear(self.vdim, self.embed_dim, bias=bias, **factory_kwargs) + # for the type: ignore, see https://github.com/pytorch/pytorch/issues/58969 + self.out_proj = nn.Linear(self.embed_dim, self.embed_dim, bias=bias, **factory_kwargs) # type: ignore[assignment] + + # Functionals + self.q_scaling_product = torch.ao.nn.quantized.FloatFunctional() + # note: importing torch.ao.nn.quantized at top creates a circular import + + # Quant/Dequant + self.quant_attn_output = torch.ao.quantization.QuantStub() + self.quant_attn_output_weights = torch.ao.quantization.QuantStub() + self.dequant_q = torch.ao.quantization.DeQuantStub() + self.dequant_k = torch.ao.quantization.DeQuantStub() + self.dequant_v = torch.ao.quantization.DeQuantStub() + + def _get_name(self): + return 'QuantizableMultiheadAttention' + + @classmethod + def from_float(cls, other): + assert type(other) == cls._FLOAT_MODULE + assert hasattr(other, 'qconfig'), "The float module must have 'qconfig'" + # Setting the dropout to 0.0! + observed = cls(other.embed_dim, other.num_heads, other.dropout, + (other.in_proj_bias is not None), + (other.bias_k is not None), + other.add_zero_attn, other.kdim, other.vdim, + other.batch_first) + observed.bias_k = other.bias_k + observed.bias_v = other.bias_v + observed.qconfig = other.qconfig + + # Set the linear weights + # for the type: ignores, see https://github.com/pytorch/pytorch/issues/58969 + observed.out_proj.weight = other.out_proj.weight # type: ignore[has-type] + observed.out_proj.bias = other.out_proj.bias # type: ignore[has-type] + if other._qkv_same_embed_dim: + # Use separate params + bias = other.in_proj_bias + _start = 0 + _end = _start + other.embed_dim + weight = other.in_proj_weight[_start:_end, :] + if bias is not None: + bias = torch.nn.Parameter(bias[_start:_end], bias.requires_grad) + observed.linear_Q.weight = torch.nn.Parameter(weight, + weight.requires_grad) + observed.linear_Q.bias = bias + + bias = other.in_proj_bias + _start = _end + _end = _start + other.embed_dim + weight = other.in_proj_weight[_start:_end, :] + if bias is not None: + bias = torch.nn.Parameter(bias[_start:_end], bias.requires_grad) + observed.linear_K.weight = torch.nn.Parameter(weight, + weight.requires_grad) + observed.linear_K.bias = bias + + bias = other.in_proj_bias + _start = _end + weight = other.in_proj_weight[_start:, :] + if bias is not None: + bias = torch.nn.Parameter(bias[_start:], bias.requires_grad) + observed.linear_V.weight = torch.nn.Parameter(weight, + weight.requires_grad) + observed.linear_V.bias = bias + else: + observed.linear_Q.weight = nn.Parameter(other.q_proj_weight) + observed.linear_K.weight = nn.Parameter(other.k_proj_weight) + observed.linear_V.weight = nn.Parameter(other.v_proj_weight) + if other.in_proj_bias is None: + observed.linear_Q.bias = None # type: ignore[assignment] + observed.linear_K.bias = None # type: ignore[assignment] + observed.linear_V.bias = None # type: ignore[assignment] + else: + observed.linear_Q.bias = nn.Parameter(other.in_proj_bias[0:other.embed_dim]) + observed.linear_K.bias = nn.Parameter(other.in_proj_bias[other.embed_dim:(other.embed_dim * 2)]) + observed.linear_V.bias = nn.Parameter(other.in_proj_bias[(other.embed_dim * 2):]) + observed.eval() + # Explicit prepare + observed = torch.ao.quantization.prepare(observed, inplace=True) + return observed + + @torch.jit.unused + def dequantize(self): + r"""Utility to convert the quantized MHA back to float. + + The motivation for this is that it is not trivial to conver the weights + from the format that is used in the quantized version back to the + float. + """ + fp = self._FLOAT_MODULE(self.embed_dim, self.num_heads, self.dropout, + (self.linear_Q._weight_bias()[1] is not None), + (self.bias_k is not None), + self.add_zero_attn, self.kdim, self.vdim, self.batch_first) + assert fp._qkv_same_embed_dim == self._qkv_same_embed_dim + if self.bias_k is not None: + fp.bias_k = nn.Parameter(self.bias_k.dequantize()) + if self.bias_v is not None: + fp.bias_v = nn.Parameter(self.bias_v.dequantize()) + + # Set the linear weights + # Note: Because the linear layers are quantized, mypy does not nkow how + # to deal with them -- might need to ignore the typing checks. + # for the type: ignore[has-type], see https://github.com/pytorch/pytorch/issues/58969 + w, b = self.out_proj._weight_bias() # type: ignore[operator, has-type] + fp.out_proj.weight = nn.Parameter(w.dequantize()) + if b is not None: + fp.out_proj.bias = nn.Parameter(b) + + wQ, bQ = self.linear_Q._weight_bias() # type: ignore[operator] + wQ = wQ.dequantize() + wK, bK = self.linear_K._weight_bias() # type: ignore[operator] + wK = wK.dequantize() + wV, bV = self.linear_V._weight_bias() # type: ignore[operator] + wV = wV.dequantize() + if fp._qkv_same_embed_dim: + # Use separate params + _start = 0 + _end = _start + fp.embed_dim + fp.in_proj_weight[_start:_end, :] = wQ + if fp.in_proj_bias is not None: + assert all(bQ == 0) + fp.in_proj_bias[_start:_end] = bQ + + _start = _end + _end = _start + fp.embed_dim + fp.in_proj_weight[_start:_end, :] = wK + if fp.in_proj_bias is not None: + assert all(bK == 0) + fp.in_proj_bias[_start:_end] = bK + + _start = _end + fp.in_proj_weight[_start:, :] = wV + if fp.in_proj_bias is not None: + assert all(bV == 0) + fp.in_proj_bias[_start:] = bV + else: + fp.q_proj_weight = nn.Parameter(wQ) + fp.k_proj_weight = nn.Parameter(wK) + fp.v_proj_weight = nn.Parameter(wV) + if fp.in_proj_bias is None: + self.linear_Q.bias = None + self.linear_K.bias = None + self.linear_V.bias = None + else: + fp.in_proj_bias[0:fp.embed_dim] = bQ + fp.in_proj_bias[fp.embed_dim:(fp.embed_dim * 2)] = bK + fp.in_proj_bias[(fp.embed_dim * 2):] = bV + + return fp + + @classmethod + def from_observed(cls, other): + # The whole flow is float -> observed -> quantized + # This class does float -> observed only + # See nn.quantized.MultiheadAttention + raise NotImplementedError("It looks like you are trying to prepare an " + "MHA module. Please, see " + "the examples on quantizable MHAs.") + + def forward(self, + query: Tensor, + key: Tensor, + value: Tensor, + key_padding_mask: Optional[Tensor] = None, + need_weights: bool = True, + attn_mask: Optional[Tensor] = None, + average_attn_weights: bool = True, + is_causal: bool = False) -> Tuple[Tensor, Optional[Tensor]]: + r""" + Note:: + Please, refer to :func:`~torch.nn.MultiheadAttention.forward` for more + information + + Args: + query, key, value: map a query and a set of key-value pairs to an output. + See "Attention Is All You Need" for more details. + key_padding_mask: if provided, specified padding elements in the key will + be ignored by the attention. When given a binary mask and a value is True, + the corresponding value on the attention layer will be ignored. + need_weights: output attn_output_weights. + attn_mask: 2D or 3D mask that prevents attention to certain positions. A 2D mask will be broadcasted for all + the batches while a 3D mask allows to specify a different mask for the entries of each batch. + + Shape: + - Inputs: + - query: :math:`(L, N, E)` where L is the target sequence length, N is the batch size, E is + the embedding dimension. :math:`(N, L, E)` if ``batch_first`` is ``True``. + - key: :math:`(S, N, E)`, where S is the source sequence length, N is the batch size, E is + the embedding dimension. :math:`(N, S, E)` if ``batch_first`` is ``True``. + - value: :math:`(S, N, E)` where S is the source sequence length, N is the batch size, E is + the embedding dimension. :math:`(N, S, E)` if ``batch_first`` is ``True``. + - key_padding_mask: :math:`(N, S)` where N is the batch size, S is the source sequence length. + If a BoolTensor is provided, the positions with the + value of ``True`` will be ignored while the position with the value of ``False`` will be unchanged. + - attn_mask: 2D mask :math:`(L, S)` where L is the target sequence length, S is the source sequence length. + 3D mask :math:`(N*num_heads, L, S)` where N is the batch size, L is the target sequence length, + S is the source sequence length. attn_mask ensure that position i is allowed to attend the unmasked + positions. If a BoolTensor is provided, positions with ``True`` + is not allowed to attend while ``False`` values will be unchanged. If a FloatTensor + is provided, it will be added to the attention weight. + - is_causal: If specified, applies a causal mask as attention mask. Mutually exclusive with providing attn_mask. + Default: ``False``. + - average_attn_weights: If true, indicates that the returned ``attn_weights`` should be averaged across + heads. Otherwise, ``attn_weights`` are provided separately per head. Note that this flag only has an + effect when ``need_weights=True.``. Default: True (i.e. average weights across heads) + + - Outputs: + - attn_output: :math:`(L, N, E)` where L is the target sequence length, N is the batch size, + E is the embedding dimension. :math:`(N, L, E)` if ``batch_first`` is ``True``. + - attn_output_weights: If ``average_attn_weights=True``, returns attention weights averaged + across heads of shape :math:`(N, L, S)`, where N is the batch size, L is the target sequence length, + S is the source sequence length. If ``average_attn_weights=False``, returns attention weights per + head of shape :math:`(N, num_heads, L, S)`. + """ + return self._forward_impl(query, key, value, key_padding_mask, + need_weights, attn_mask, average_attn_weights, + is_causal) + + def _forward_impl(self, + query: Tensor, + key: Tensor, + value: Tensor, + key_padding_mask: Optional[Tensor] = None, + need_weights: bool = True, + attn_mask: Optional[Tensor] = None, + average_attn_weights: bool = True, + is_causal: bool = False) -> Tuple[Tensor, Optional[Tensor]]: + # This version will not deal with the static key/value pairs. + # Keeping it here for future changes. + # + # TODO: This method has some duplicate lines with the + # `torch.nn.functional.multi_head_attention`. Will need to refactor. + static_k = None + static_v = None + + if attn_mask is not None and is_causal: + raise AssertionError("Only allow causal mask or attn_mask") + + if is_causal: + raise AssertionError("causal mask not supported by AO MHA module") + + if self.batch_first: + query, key, value = (x.transpose(0, 1) for x in (query, key, value)) + + tgt_len, bsz, embed_dim_to_check = query.size() + assert self.embed_dim == embed_dim_to_check + # allow MHA to have different sizes for the feature dimension + assert key.size(0) == value.size(0) and key.size(1) == value.size(1) + + head_dim = self.embed_dim // self.num_heads + assert head_dim * self.num_heads == self.embed_dim, "embed_dim must be divisible by num_heads" + scaling = float(head_dim) ** -0.5 + + q = self.linear_Q(query) + k = self.linear_K(key) + v = self.linear_V(value) + + q = self.q_scaling_product.mul_scalar(q, scaling) + + if attn_mask is not None: + if attn_mask.dtype == torch.uint8: + warnings.warn( + "Byte tensor for `attn_mask` in `nn.MultiheadAttention` is deprecated. " + "Use bool tensor instead.", + stacklevel=3, + ) + attn_mask = attn_mask.to(torch.bool) + assert attn_mask.is_floating_point() or attn_mask.dtype == torch.bool, \ + f'Only float and bool types are supported for attn_mask, not {attn_mask.dtype}' + + if attn_mask.dim() == 2: + attn_mask = attn_mask.unsqueeze(0) + if list(attn_mask.size()) != [1, query.size(0), key.size(0)]: + raise RuntimeError('The size of the 2D attn_mask is not correct.') + elif attn_mask.dim() == 3: + if list(attn_mask.size()) != [bsz * self.num_heads, query.size(0), key.size(0)]: + raise RuntimeError('The size of the 3D attn_mask is not correct.') + else: + raise RuntimeError(f"attn_mask's dimension {attn_mask.dim()} is not supported") + # attn_mask's dim is 3 now. + + # convert ByteTensor key_padding_mask to bool + if key_padding_mask is not None and key_padding_mask.dtype == torch.uint8: + warnings.warn( + "Byte tensor for `key_padding_mask` in `nn.MultiheadAttention` is deprecated. " + "Use bool tensor instead.", + stacklevel=3, + ) + key_padding_mask = key_padding_mask.to(torch.bool) + if self.bias_k is not None and self.bias_v is not None: + if static_k is None and static_v is None: + + # Explicitly assert that bias_k and bias_v are not None + # in a way that TorchScript can understand. + bias_k = self.bias_k + assert bias_k is not None + bias_v = self.bias_v + assert bias_v is not None + + k = torch.cat([k, bias_k.repeat(1, bsz, 1)]) + v = torch.cat([v, bias_v.repeat(1, bsz, 1)]) + if attn_mask is not None: + attn_mask = nnF.pad(attn_mask, (0, 1)) + if key_padding_mask is not None: + key_padding_mask = nnF.pad(key_padding_mask, (0, 1)) + else: + assert static_k is None, "bias cannot be added to static key." + assert static_v is None, "bias cannot be added to static value." + else: + assert self.bias_k is None + assert self.bias_v is None + + q = q.contiguous().view(tgt_len, bsz * self.num_heads, head_dim).transpose(0, 1) + if k is not None: + k = k.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1) + if v is not None: + v = v.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1) + + if static_k is not None: + assert static_k.size(0) == bsz * self.num_heads + assert static_k.size(2) == head_dim + k = static_k + + if static_v is not None: + assert static_v.size(0) == bsz * self.num_heads + assert static_v.size(2) == head_dim + v = static_v + + src_len = k.size(1) + + if key_padding_mask is not None: + assert key_padding_mask.size(0) == bsz + assert key_padding_mask.size(1) == src_len + + if self.add_zero_attn: + src_len += 1 + k_zeros = torch.zeros((k.size(0), 1) + k.size()[2:]) + if k.is_quantized: + k_zeros = torch.quantize_per_tensor(k_zeros, k.q_scale(), k.q_zero_point(), k.dtype) + k = torch.cat([k, k_zeros], dim=1) + v_zeros = torch.zeros((v.size(0), 1) + k.size()[2:]) + if v.is_quantized: + v_zeros = torch.quantize_per_tensor(v_zeros, v.q_scale(), v.q_zero_point(), v.dtype) + v = torch.cat([v, v_zeros], dim=1) + + if attn_mask is not None: + attn_mask = nnF.pad(attn_mask, (0, 1)) + if key_padding_mask is not None: + key_padding_mask = nnF.pad(key_padding_mask, (0, 1)) + + # Leaving the quantized zone here + q = self.dequant_q(q) + k = self.dequant_k(k) + v = self.dequant_v(v) + attn_output_weights = torch.bmm(q, k.transpose(1, 2)) + assert list(attn_output_weights.size()) == [bsz * self.num_heads, tgt_len, src_len] + + if attn_mask is not None: + if attn_mask.dtype == torch.bool: + attn_output_weights.masked_fill_(attn_mask, float('-inf')) + else: + attn_output_weights += attn_mask + + if key_padding_mask is not None: + attn_output_weights = attn_output_weights.view(bsz, self.num_heads, tgt_len, src_len) + attn_output_weights = attn_output_weights.masked_fill( + key_padding_mask.unsqueeze(1).unsqueeze(2), + float('-inf'), + ) + attn_output_weights = attn_output_weights.view(bsz * self.num_heads, tgt_len, src_len) + + attn_output_weights = nnF.softmax( + attn_output_weights, dim=-1) + attn_output_weights = nnF.dropout(attn_output_weights, p=self.dropout, training=self.training) + + attn_output = torch.bmm(attn_output_weights, v) + assert list(attn_output.size()) == [bsz * self.num_heads, tgt_len, head_dim] + if self.batch_first: + attn_output = attn_output.view(bsz, tgt_len, self.embed_dim) + else: + attn_output = attn_output.transpose(0, 1).contiguous().view(tgt_len, bsz, self.embed_dim) + + # Reentering the quantized zone + attn_output = self.quant_attn_output(attn_output) + # for the type: ignore[has-type], see https://github.com/pytorch/pytorch/issues/58969 + attn_output = self.out_proj(attn_output) # type: ignore[has-type] + attn_output_weights = self.quant_attn_output_weights(attn_output_weights) + + if need_weights: + # average attention weights over heads + attn_output_weights = attn_output_weights.view(bsz, self.num_heads, tgt_len, src_len) + if average_attn_weights: + attn_output_weights = attn_output_weights.mean(dim=1) + return attn_output, attn_output_weights + else: + return attn_output, None \ No newline at end of file