{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Attention!!!\n\nThis is a very simple but bad quality notebook. \n - I do not use any sort of ranking loss, which would be better.\n - My strategy instead is to min-max scale the relative times (time/normalized) and apply L1-loss\n - My model is also not optimized. It is a relatively simple GNN that embeds the graph and only processes 1 datapoint at a time and is only trained on 1 epoch.\n - The public score would be much better if you paired this submission with a trained model for layout. Since this only contributes to half of the score.\n - Have fun playing around with it!","metadata":{}},{"cell_type":"code","source":"!pip install torch-geometric torch-scatter","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-09-04T09:43:34.909426Z","iopub.execute_input":"2023-09-04T09:43:34.911239Z","iopub.status.idle":"2023-09-04T09:48:23.548053Z","shell.execute_reply.started":"2023-09-04T09:43:34.911185Z","shell.execute_reply":"2023-09-04T09:48:23.546061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nfrom tqdm import tqdm \n\nimport torch\nfrom torch import nn\nfrom torch import Tensor\nfrom torch_geometric.nn import GCNConv\nfrom torch_geometric.datasets import Planetoid\nfrom torch.utils.data import DataLoader, Dataset\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:48:23.550693Z","iopub.execute_input":"2023-09-04T09:48:23.551152Z","iopub.status.idle":"2023-09-04T09:48:26.138141Z","shell.execute_reply.started":"2023-09-04T09:48:23.551115Z","shell.execute_reply":"2023-09-04T09:48:26.136741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can now load all the data in dataframes to make working with it easier","metadata":{}},{"cell_type":"code","source":"def load_df(directory):\n    splits = [\"train\", \"valid\", \"test\"]\n    dfs = dict()\n    \n    for split in splits:\n        path = os.path.join(directory, split)\n        files = os.listdir(path)\n        list_df = []\n        \n        for file in files:\n            d = dict(np.load(os.path.join(path,file)))\n            d['file'] = file\n            list_df.append(d)\n        dfs[split] = pd.DataFrame.from_dict(list_df)\n    return dfs","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:48:26.140060Z","iopub.execute_input":"2023-09-04T09:48:26.140761Z","iopub.status.idle":"2023-09-04T09:48:26.149486Z","shell.execute_reply.started":"2023-09-04T09:48:26.140707Z","shell.execute_reply":"2023-09-04T09:48:26.147690Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"If you try to run the following cell completely uncommented the Kaggle kernel will run out of memory and crash, so we will have to study the datasets individually","metadata":{}},{"cell_type":"code","source":"tile_xla = load_df(\"/kaggle/input/predict-ai-model-runtime/npz_all/npz/tile/xla/\")\n#layout_nlp_random = load_df(\"/kaggle/input/predict-ai-model-runtime/npz_all/npz/layout/nlp/random/\")\n#layout_nlp_default = load_df(\"/kaggle/input/predict-ai-model-runtime/npz_all/npz/layout/nlp/default/\")\n#layout_xla_random = load_df(\"/kaggle/input/predict-ai-model-runtime/npz_all/npz/layout/xla/random/\")\n#layout_xla_random = load_df(\"/kaggle/input/predict-ai-model-runtime/npz_all/npz/layout/xla/default/\")","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:48:26.153129Z","iopub.execute_input":"2023-09-04T09:48:26.153499Z","iopub.status.idle":"2023-09-04T09:49:23.191167Z","shell.execute_reply.started":"2023-09-04T09:48:26.153469Z","shell.execute_reply":"2023-09-04T09:49:23.190121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Dataset and Model","metadata":{}},{"cell_type":"code","source":"class TileDataset(Dataset):\n    def __init__(self, df):\n        self.df = df\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        config_feat = torch.tensor(row['config_feat'].astype(np.float32))\n        node_feat = torch.tensor(row['node_feat'].astype(np.float32))\n        node_opcode = torch.tensor(row['node_opcode'].astype(np.int32))\n        edge_index = torch.tensor(np.swapaxes(row['edge_index'],0,1).astype(np.int32))\n        target = (row['config_runtime']/row['config_runtime_normalizers']).astype(np.float32)\n        # minmax scale the target, we only care about order\n        target = (target-min(target))/(max(target) -min(target))\n        target = torch.tensor(target)\n        return config_feat,node_feat,node_opcode,edge_index,target","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:49:23.192729Z","iopub.execute_input":"2023-09-04T09:49:23.193935Z","iopub.status.idle":"2023-09-04T09:49:23.204040Z","shell.execute_reply.started":"2023-09-04T09:49:23.193893Z","shell.execute_reply":"2023-09-04T09:49:23.202824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SimpleModel(torch.nn.Module):\n    def __init__(self, hidden_channels, graph_feats, hidden_dim):\n        super().__init__()\n        op_embedding_dim = 4 # I choose 4-dimensional embedding\n        self.embedding = torch.nn.Embedding(120, #120 different op-codes\n                                            op_embedding_dim,\n                                           )\n        assert len(hidden_channels)>0\n        in_channels = op_embedding_dim+140\n        self.convs = torch.nn.ModuleList()\n        last_dim = hidden_channels[0]\n        self.convs.append(GCNConv(in_channels, hidden_channels[0]))\n        for i in range(len(hidden_channels)-1):\n            self.convs.append(GCNConv(hidden_channels[i], hidden_channels[i+1]))\n            last_dim = hidden_channels[i+1]\n        self.convs.append(GCNConv(last_dim, graph_feats))\n        \n        self.dense = torch.nn.Sequential(nn.Linear(graph_feats+24, 124),\n                                         nn.ReLU(),\n                                         nn.Linear(124, 124),\n                                         nn.ReLU(),\n                                         nn.Linear(124, 1),\n                                        )\n\n    def forward(self, x_cfg: Tensor,x_feat: Tensor, x_op: Tensor, edge_index: Tensor) -> Tensor:\n        \n        #get graph features\n        x = torch.concat([x_feat,self.embedding(x_op)],dim = 1)\n        #pass though conv layers\n        for conv in self.convs:\n            x = conv(x, edge_index).relu()\n        # get 1d graph embedding using average pooling\n        x_graph = torch.mean(x,0)\n        \n        \n        #put graph data into config data\n        x = torch.concat([x_cfg,x_graph.repeat((len(x_cfg),1))],axis=1)\n        #put into dense nn\n        x = torch.flatten(self.dense(x))\n        return x\n\nmodel = SimpleModel(hidden_channels = [24,32,24,48],graph_feats = 64,hidden_dim=64).to(device)#16,32,16,48","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:49:23.205645Z","iopub.execute_input":"2023-09-04T09:49:23.206129Z","iopub.status.idle":"2023-09-04T09:49:23.275026Z","shell.execute_reply.started":"2023-09-04T09:49:23.206085Z","shell.execute_reply":"2023-09-04T09:49:23.273830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train One Epoch","metadata":{}},{"cell_type":"code","source":"dataset = TileDataset(tile_xla[\"train\"])\ncriterion = torch.nn.L1Loss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3,weight_decay = 0.01)\n\nmodel.train()\npbar = tqdm(range(len(dataset)))\nloss_sum = 0\nn = 0\nfor i in pbar:\n    cfg_ft,nd_ft,nd_op,ind,target = dataset[i]\n    cfg_ft,nd_ft,nd_op,ind,target = cfg_ft.to(device),nd_ft.to(device),nd_op.to(device),ind.to(device),target.to(device)\n    \n    out = model(cfg_ft,nd_ft,nd_op,ind)\n    loss = criterion(out, target)\n    loss.backward()\n    torch.nn.utils.clip_grad_norm_(model.parameters(), 0.01)\n    optimizer.step()\n    \n    loss_sum+=loss.item()\n    n+=1\n    pbar.set_description(f'running loss: {(loss_sum/n):.1f},current loss: {(loss.item()):.1f}')","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:49:23.276799Z","iopub.execute_input":"2023-09-04T09:49:23.277609Z","iopub.status.idle":"2023-09-04T09:53:09.727892Z","shell.execute_reply.started":"2023-09-04T09:49:23.277562Z","shell.execute_reply":"2023-09-04T09:53:09.726681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Evaluate on Validation Dataset","metadata":{}},{"cell_type":"code","source":"dataset = TileDataset(tile_xla[\"valid\"])\ntile_xla_predictions = []\nmodel.eval()\n\npbar = tqdm(range(len(dataset)))\nfor i in pbar:\n    cfg_ft,nd_ft,nd_op,ind,target = dataset[i]\n    cfg_ft,nd_ft,nd_op,ind,target = cfg_ft.to(device),nd_ft.to(device),nd_op.to(device),ind.to(device),target.to(device)\n    \n    out = model(cfg_ft,nd_ft,nd_op,ind)\n    tile_xla_predictions.append(np.argsort(out.detach().numpy())[:5])\n\ndef score_tile(predictions, df):\n    score = 0\n    for i in range(len(df)):\n        predbest = min(df.iloc[i]['config_runtime'][predictions[i]])\n        best = min(df.iloc[i]['config_runtime'])\n        score +=2 - predbest/best\n    score /= len(df)\n    return score\nscore_tile(tile_xla_predictions, tile_xla[\"valid\"])","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:53:09.729527Z","iopub.execute_input":"2023-09-04T09:53:09.730632Z","iopub.status.idle":"2023-09-04T09:53:17.256074Z","shell.execute_reply.started":"2023-09-04T09:53:09.730594Z","shell.execute_reply":"2023-09-04T09:53:17.254899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**0.31 is not bad considering that this model only trained on 1 epoch and is not on a ranking loss!**","metadata":{}},{"cell_type":"markdown","source":"# Predict and Submit (only tile:xla predictions)","metadata":{}},{"cell_type":"code","source":"dataset = TileDataset(tile_xla[\"test\"])\ntile_xla_predictions = []\nmodel.eval()\npbar = tqdm(range(len(dataset)))\nfor i in pbar:\n    cfg_ft,nd_ft,nd_op,ind,target = dataset[i]\n    cfg_ft,nd_ft,nd_op,ind,target = cfg_ft.to(device),nd_ft.to(device),nd_op.to(device),ind.to(device),target.to(device)\n    \n    out = model(cfg_ft,nd_ft,nd_op,ind)\n    tile_xla_predictions.append(np.argsort(out.detach().numpy())[:5])","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:53:17.257357Z","iopub.execute_input":"2023-09-04T09:53:17.257683Z","iopub.status.idle":"2023-09-04T09:53:26.782181Z","shell.execute_reply.started":"2023-09-04T09:53:17.257654Z","shell.execute_reply":"2023-09-04T09:53:26.781038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv('/kaggle/input/predict-ai-model-runtime/sample_submission.csv')\nfor i,filename in enumerate(tile_xla[\"test\"]['file'].values):\n    id = 'tile:xla:' +filename[:-4]\n    sub.loc[sub.ID == id,'TopConfigs'] = ';'.join(tile_xla_predictions[i].astype(str))\nsub.to_csv('submission.csv',index=False)\nsub","metadata":{"execution":{"iopub.status.busy":"2023-09-04T09:53:26.785118Z","iopub.execute_input":"2023-09-04T09:53:26.785504Z","iopub.status.idle":"2023-09-04T09:53:27.322279Z","shell.execute_reply.started":"2023-09-04T09:53:26.785469Z","shell.execute_reply":"2023-09-04T09:53:27.321145Z"},"trusted":true},"execution_count":null,"outputs":[]}],"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}}