{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "id": "RqYoTBJrCcJx" }, "outputs": [], "source": [ "%%capture\n", "!pip install torch_geometric rdkit\n", "!pip install torch-scatter -f https://data.pyg.org/whl/torch-$(python -c \"import torch; print(torch.__version__)\").html" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "M0suAYOT5R-N" }, "outputs": [], "source": [ "# Standard library\n", "from collections import defaultdict\n", "from typing import List, Tuple\n", "import importlib.resources as pkg_resources\n", "from multiprocessing.pool import ThreadPool\n", "\n", "# Third-party scientific stack\n", "import numpy as np\n", "import pandas as pd\n", "from scipy.sparse import coo_matrix\n", "from scipy.sparse.linalg import eigsh\n", "\n", "# RDKit\n", "from rdkit import Chem\n", "\n", "# PyTorch core\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "\n", "# PyTorch Geometric\n", "import torch_geometric\n", "from torch_geometric.data import Data, Batch, DataLoader\n", "from torch_geometric.nn import GCNConv, GINConv, BatchNorm, global_mean_pool\n", "from torch_geometric.loader import DataLoader as PyGDataLoader" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "4Wginy3UChb_", "outputId": "981c9246-0f1c-403b-bac3-fc4941611c14" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount(\"/content/drive\", force_remount=True).\n" ] } ], "source": [ "from google.colab import drive\n", "drive.mount('/content/drive')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "obXQxOveCM9l" }, "outputs": [], "source": [ "def set_seed(seed):\n", " \"\"\"\n", " Fix all random seeds for reproducibility across Python, NumPy, and PyTorch.\n", " \"\"\"\n", " import random\n", " random.seed(seed)\n", " np.random.seed(seed)\n", " torch.manual_seed(seed)\n", " if torch.cuda.is_available():\n", " torch.cuda.manual_seed_all(seed)\n", " torch.backends.cudnn.deterministic = True\n", " torch.backends.cudnn.benchmark = False\n", "\n", "set_seed(42)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "fYyEoAXuCM9m" }, "outputs": [], "source": [ "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "from torch_geometric.nn import (\n", " PNAConv,\n", " global_mean_pool,\n", " global_max_pool,\n", " global_add_pool\n", ")\n", "from torch_geometric.utils import degree\n", "\n", "class PolyatomicNet(nn.Module):\n", " def __init__(self, node_feat_dim, edge_feat_dim, graph_feat_dim,deg,\n", " hidden_dim=128, num_layers=5, dropout=0.1):\n", " super().__init__()\n", "\n", " self.node_emb = nn.Linear(node_feat_dim, hidden_dim)\n", " self.deg = deg\n", " self.virtualnode_emb = nn.Embedding(1, hidden_dim)\n", " self.vn_mlp = nn.Sequential(\n", " nn.Linear(hidden_dim, hidden_dim),\n", " nn.ReLU(),\n", " nn.Linear(hidden_dim, hidden_dim)\n", " )\n", "\n", " # For graph-level feature projection\n", " self.graph_proj = nn.Sequential(\n", " nn.Linear(graph_feat_dim, hidden_dim),\n", " nn.ReLU(),\n", " nn.Linear(hidden_dim, hidden_dim)\n", " )\n", "\n", " # PNAConv requires degree preprocessing\n", " self.deg_emb = nn.Embedding(20, hidden_dim) # cap degree buckets\n", "\n", " aggregators = ['mean', 'min', 'max', 'std']\n", " scalers = ['identity', 'amplification', 'attenuation']\n", "\n", " self.convs = nn.ModuleList()\n", " self.bns = nn.ModuleList()\n", "\n", " for _ in range(num_layers):\n", " conv = PNAConv(\n", " in_channels=hidden_dim,\n", " out_channels=hidden_dim,\n", " aggregators=aggregators,\n", " scalers=scalers,\n", " edge_dim=edge_feat_dim,\n", " towers=4,\n", " pre_layers=1,\n", " post_layers=1,\n", " divide_input=True,\n", " deg=deg\n", " )\n", " self.convs.append(conv)\n", " self.bns.append(nn.BatchNorm1d(hidden_dim))\n", "\n", " self.dropout = nn.Dropout(dropout)\n", "\n", " # Final readout\n", " self.readout = nn.Sequential(\n", " nn.Linear(hidden_dim * 3, hidden_dim),\n", " nn.ReLU(),\n", " nn.Dropout(dropout),\n", " nn.Linear(hidden_dim, hidden_dim // 2),\n", " nn.ReLU(),\n", " nn.Linear(hidden_dim // 2, 1),\n", " )\n", "\n", " def forward(self, data):\n", " x, edge_index, edge_attr, batch = (\n", " data.x,\n", " data.edge_index,\n", " data.edge_attr,\n", " data.batch,\n", " )\n", "\n", " deg = degree(edge_index[0], x.size(0), dtype=torch.long).clamp(max=19)\n", " h = self.node_emb(x) + self.deg_emb(deg)\n", "\n", " vn = self.virtualnode_emb(\n", " torch.zeros(batch.max().item() + 1, dtype=torch.long, device=x.device)\n", " )\n", "\n", " for conv, bn in zip(self.convs, self.bns):\n", " h = h + vn[batch]\n", " h = conv(h, edge_index, edge_attr)\n", " h = bn(h)\n", " h = F.relu(h)\n", " h = self.dropout(h)\n", " vn = vn + self.vn_mlp(global_mean_pool(h, batch))\n", "\n", " mean_pool = global_mean_pool(h, batch)\n", " max_pool = global_max_pool(h, batch)\n", " # add_pool = global_add_pool(h, batch)\n", "\n", " # graph_feats shape: [batch_size, graph_feat_dim]\n", " if hasattr(data, 'graph_feats') and isinstance(data, torch_geometric.data.Batch):\n", " g_feats = torch.stack([g.graph_feats for g in data.to_data_list()], dim=0).to(x.device)\n", " else:\n", " g_feats = data.graph_feats.unsqueeze(0).to(x.device)\n", " g_proj = self.graph_proj(g_feats)\n", "\n", " final_input = torch.cat([mean_pool, max_pool, g_proj], dim=1)\n", " return self.readout(final_input).view(-1)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "id": "daTRiHSFCM9m" }, "outputs": [], "source": [ "from torch.amp import autocast, GradScaler\n", "from sklearn.metrics import mean_absolute_error, mean_squared_error\n", "\n", "scaler_grad = GradScaler()\n", "\n", "def train(model, loader, optimizer, loss_fn, scaler=scaler_grad, accum_steps=8):\n", " model.train()\n", " total_loss = 0.0\n", " optimizer.zero_grad()\n", "\n", " for i, batch in enumerate(loader):\n", " batch = batch.to(device)\n", " with autocast(device_type='cuda', dtype=torch.float16):\n", " output = model(batch)\n", " loss = loss_fn(output, batch.y.view(-1)) / accum_steps\n", "\n", " scaler_grad.scale(loss).backward()\n", "\n", " if (i + 1) % accum_steps == 0 or (i + 1 == len(loader)):\n", " scaler.step(optimizer)\n", " scaler.update()\n", " optimizer.zero_grad()\n", "\n", " total_loss += loss.item() * batch.num_graphs * accum_steps\n", "\n", " return total_loss / len(loader.dataset)\n", "\n", "def compute_metrics_with_ci(trues, preds, n_boot=2000, alpha=0.05, seed=42):\n", " trues = np.array(trues)\n", " preds = np.array(preds)\n", " mae = mean_absolute_error(trues, preds)\n", " rmse = np.sqrt(mean_squared_error(trues, preds))\n", "\n", " rng = np.random.RandomState(seed)\n", " mae_samples, rmse_samples = [], []\n", " n = len(trues)\n", " for _ in range(n_boot):\n", " idx = rng.randint(0, n, n)\n", " t, p = trues[idx], preds[idx]\n", " mae_samples.append(mean_absolute_error(t, p))\n", " rmse_samples.append(np.sqrt(mean_squared_error(t, p)))\n", "\n", " lower, upper = 100 * alpha / 2, 100 * (1 - alpha / 2)\n", " mae_ci = (np.percentile(mae_samples, lower), np.percentile(mae_samples, upper))\n", " rmse_ci = (np.percentile(rmse_samples, lower), np.percentile(rmse_samples, upper))\n", " return {'mae': mae, 'mae_ci': mae_ci, 'rmse': rmse, 'rmse_ci': rmse_ci}\n", "\n", "def evaluate(model, loader):\n", " model.eval()\n", " preds, trues = [], []\n", " with torch.no_grad(), autocast(device_type='cuda', dtype=torch.float16):\n", " for batch in loader:\n", " batch = batch.to(device)\n", " out = model(batch)\n", " preds.append(out.view(-1))\n", " trues.append(batch.y.view(-1))\n", " preds = torch.cat(preds)\n", " trues = torch.cat(trues)\n", " return torch.sqrt(torch.mean((preds - trues) ** 2)).item()\n", "\n", "def evaluate_with_ci(model, loader):\n", " model.eval()\n", " preds, trues = [], []\n", " with torch.no_grad(), autocast(device_type='cuda', dtype=torch.float16):\n", " for batch in loader:\n", " batch = batch.to(device)\n", " out = model(batch)\n", " y_true = batch.y.view(-1).cpu().tolist()\n", " y_pred = out.view(-1).cpu().tolist()\n", " trues.extend(y_true)\n", " preds.extend(y_pred)\n", " return compute_metrics_with_ci(trues, preds)\n", "\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "b7jK5FEjCM9m" }, "outputs": [], "source": [ "loading = True\n", "if loading:\n", " train_set = torch.load('/content/drive/MyDrive/lipophil_polyatomic_data/polyatomic_data_lipophil.pt', weights_only=False)\n", " test_set = torch.load('/content/drive/MyDrive/lipophil_polyatomic_data/polyatomic_test_data_lipophil.pt', weights_only=False)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "aT9Ljr-SgTsE" }, "outputs": [], "source": [ "import warnings\n", "warnings.filterwarnings(\"ignore\", message=\".*An output with one or more elements was resized.*\")\n", "warnings.filterwarnings(\"ignore\", message=\".*FutureWarning:.*\")\n", "warnings.filterwarnings(\"ignore\", category=UserWarning, module=\"torch.optim.lr_scheduler\")" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "id": "_ZBbQn-98XEu" }, "outputs": [], "source": [ "from torch_geometric.loader import DataLoader\n", "\n", "train_loader = DataLoader(train_set, batch_size=128, shuffle=True, num_workers=8, pin_memory=True)\n", "test_loader = DataLoader(test_set, batch_size=128)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "id": "XchdS-ZoCM9m" }, "outputs": [], "source": [ "node_feat_dim = train_set[0].x.shape[1]\n", "edge_feat_dim = train_set[0].edge_attr.shape[1]\n", "graph_feat_dim = train_set[0].graph_feats.shape[0]\n", "\n", "from torch_geometric.utils import degree\n", "\n", "deg = torch.zeros(128, dtype=torch.long)\n", "\n", "for data in train_loader:\n", " d = degree(data.edge_index[1], num_nodes=data.num_nodes, dtype=torch.long)\n", " bc = torch.bincount(d, minlength=deg.size(0))\n", " if bc.size(0) > deg.size(0):\n", " new_deg = torch.zeros(bc.size(0), dtype=torch.long)\n", " new_deg[:deg.size(0)] = deg\n", " deg = new_deg\n", " deg += bc\n", "\n", "model = PolyatomicNet(\n", " node_feat_dim=node_feat_dim,\n", " edge_feat_dim=edge_feat_dim,\n", " graph_feat_dim=graph_feat_dim,\n", " deg=deg,\n", ")\n", "\n", "input_dim = train_set[0].x.size(1)\n", "optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n", "scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", " optimizer, mode='min', patience=10, factor=0.5, verbose=True\n", ")\n", "loss_fn = nn.SmoothL1Loss(beta=0.5)\n", "\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", "model = model.to(device)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Sp8U1CA2JmoL", "outputId": "2239fd38-c080-43d6-ac3c-8a1a1a8e1421" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 01 | Train Loss: 0.5943 | Test MAE: 0.8046 (95% CI [0.7590, 0.8513]) | Test RMSE: 1.0218 (95% CI [0.9617, 1.0806])\n", "Epoch 02 | Train Loss: 0.5771 | Test MAE: 0.8002 (95% CI [0.7539, 0.8467]) | Test RMSE: 1.0185 (95% CI [0.9580, 1.0792])\n", "Epoch 03 | Train Loss: 0.5605 | Test MAE: 0.7535 (95% CI [0.7103, 0.7986]) | Test RMSE: 0.9599 (95% CI [0.9031, 1.0171])\n", "Epoch 04 | Train Loss: 0.5315 | Test MAE: 0.7404 (95% CI [0.7016, 0.7805]) | Test RMSE: 0.9239 (95% CI [0.8766, 0.9719])\n", "Epoch 05 | Train Loss: 0.5032 | Test MAE: 0.8859 (95% CI [0.8412, 0.9308]) | Test RMSE: 1.0662 (95% CI [1.0193, 1.1121])\n", "Epoch 06 | Train Loss: 0.5014 | Test MAE: 0.7826 (95% CI [0.7362, 0.8297]) | Test RMSE: 1.0081 (95% CI [0.9490, 1.0657])\n", "Epoch 07 | Train Loss: 0.4802 | Test MAE: 0.7119 (95% CI [0.6728, 0.7523]) | Test RMSE: 0.8873 (95% CI [0.8403, 0.9350])\n", "Epoch 08 | Train Loss: 0.4727 | Test MAE: 0.7104 (95% CI [0.6703, 0.7509]) | Test RMSE: 0.8859 (95% CI [0.8378, 0.9337])\n", "Epoch 09 | Train Loss: 0.4673 | Test MAE: 0.7104 (95% CI [0.6729, 0.7490]) | Test RMSE: 0.8803 (95% CI [0.8345, 0.9273])\n", "Epoch 10 | Train Loss: 0.4557 | Test MAE: 0.7119 (95% CI [0.6721, 0.7506]) | Test RMSE: 0.8815 (95% CI [0.8354, 0.9291])\n", "Epoch 11 | Train Loss: 0.4598 | Test MAE: 0.7039 (95% CI [0.6664, 0.7417]) | Test RMSE: 0.8657 (95% CI [0.8221, 0.9104])\n", "Epoch 12 | Train Loss: 0.4408 | Test MAE: 0.7127 (95% CI [0.6723, 0.7565]) | Test RMSE: 0.9122 (95% CI [0.8589, 0.9676])\n", "Epoch 13 | Train Loss: 0.4392 | Test MAE: 0.6718 (95% CI [0.6346, 0.7091]) | Test RMSE: 0.8371 (95% CI [0.7915, 0.8842])\n", "Epoch 14 | Train Loss: 0.4239 | Test MAE: 0.9362 (95% CI [0.8893, 0.9794]) | Test RMSE: 1.1119 (95% CI [1.0635, 1.1551])\n", "Epoch 15 | Train Loss: 0.4135 | Test MAE: 0.8397 (95% CI [0.7930, 0.8918]) | Test RMSE: 1.0707 (95% CI [1.0128, 1.1294])\n", "Epoch 16 | Train Loss: 0.3985 | Test MAE: 0.7541 (95% CI [0.7098, 0.7995]) | Test RMSE: 0.9647 (95% CI [0.9087, 1.0179])\n", "Epoch 17 | Train Loss: 0.4162 | Test MAE: 0.7120 (95% CI [0.6718, 0.7565]) | Test RMSE: 0.9148 (95% CI [0.8641, 0.9684])\n", "Epoch 18 | Train Loss: 0.3940 | Test MAE: 0.6237 (95% CI [0.5877, 0.6614]) | Test RMSE: 0.7984 (95% CI [0.7517, 0.8465])\n", "Epoch 19 | Train Loss: 0.3880 | Test MAE: 0.6442 (95% CI [0.6067, 0.6835]) | Test RMSE: 0.8313 (95% CI [0.7835, 0.8805])\n", "Epoch 20 | Train Loss: 0.3973 | Test MAE: 0.6318 (95% CI [0.5954, 0.6711]) | Test RMSE: 0.8211 (95% CI [0.7725, 0.8727])\n", "Epoch 21 | Train Loss: 0.3763 | Test MAE: 0.5911 (95% CI [0.5569, 0.6279]) | Test RMSE: 0.7676 (95% CI [0.7202, 0.8155])\n", "Epoch 22 | Train Loss: 0.3671 | Test MAE: 0.6434 (95% CI [0.6052, 0.6847]) | Test RMSE: 0.8352 (95% CI [0.7863, 0.8878])\n", "Epoch 23 | Train Loss: 0.3604 | Test MAE: 0.6489 (95% CI [0.6116, 0.6905]) | Test RMSE: 0.8388 (95% CI [0.7894, 0.8894])\n", "Epoch 24 | Train Loss: 0.3413 | Test MAE: 0.6602 (95% CI [0.6228, 0.6978]) | Test RMSE: 0.8235 (95% CI [0.7787, 0.8676])\n", "Epoch 25 | Train Loss: 0.3413 | Test MAE: 0.6013 (95% CI [0.5652, 0.6375]) | Test RMSE: 0.7824 (95% CI [0.7354, 0.8300])\n", "Epoch 26 | Train Loss: 0.3379 | Test MAE: 0.6744 (95% CI [0.6387, 0.7120]) | Test RMSE: 0.8354 (95% CI [0.7897, 0.8826])\n", "Epoch 27 | Train Loss: 0.3287 | Test MAE: 0.6842 (95% CI [0.6454, 0.7249]) | Test RMSE: 0.8793 (95% CI [0.8273, 0.9300])\n", "Epoch 28 | Train Loss: 0.3380 | Test MAE: 0.5912 (95% CI [0.5562, 0.6268]) | Test RMSE: 0.7654 (95% CI [0.7200, 0.8120])\n", "Epoch 29 | Train Loss: 0.3172 | Test MAE: 0.6528 (95% CI [0.6163, 0.6938]) | Test RMSE: 0.8352 (95% CI [0.7907, 0.8830])\n", "Epoch 30 | Train Loss: 0.3174 | Test MAE: 0.9994 (95% CI [0.9520, 1.0473]) | Test RMSE: 1.1705 (95% CI [1.1227, 1.2189])\n", "Epoch 31 | Train Loss: 0.3206 | Test MAE: 0.7928 (95% CI [0.7496, 0.8394]) | Test RMSE: 0.9942 (95% CI [0.9428, 1.0472])\n", "Epoch 32 | Train Loss: 0.3183 | Test MAE: 0.5625 (95% CI [0.5288, 0.5980]) | Test RMSE: 0.7300 (95% CI [0.6829, 0.7789])\n", "Epoch 33 | Train Loss: 0.3149 | Test MAE: 0.6162 (95% CI [0.5791, 0.6523]) | Test RMSE: 0.8013 (95% CI [0.7536, 0.8487])\n", "Epoch 34 | Train Loss: 0.3104 | Test MAE: 0.6204 (95% CI [0.5827, 0.6592]) | Test RMSE: 0.7950 (95% CI [0.7504, 0.8436])\n", "Epoch 35 | Train Loss: 0.3050 | Test MAE: 0.5997 (95% CI [0.5628, 0.6359]) | Test RMSE: 0.7868 (95% CI [0.7366, 0.8359])\n", "Epoch 36 | Train Loss: 0.3138 | Test MAE: 0.5355 (95% CI [0.5031, 0.5705]) | Test RMSE: 0.7058 (95% CI [0.6588, 0.7543])\n", "Epoch 37 | Train Loss: 0.2936 | Test MAE: 0.7361 (95% CI [0.6955, 0.7777]) | Test RMSE: 0.9210 (95% CI [0.8731, 0.9689])\n", "Epoch 38 | Train Loss: 0.2766 | Test MAE: 0.5961 (95% CI [0.5623, 0.6318]) | Test RMSE: 0.7625 (95% CI [0.7154, 0.8112])\n", "Epoch 39 | Train Loss: 0.2824 | Test MAE: 0.5254 (95% CI [0.4937, 0.5598]) | Test RMSE: 0.6938 (95% CI [0.6469, 0.7405])\n", "Epoch 40 | Train Loss: 0.2883 | Test MAE: 0.6495 (95% CI [0.6102, 0.6912]) | Test RMSE: 0.8463 (95% CI [0.7953, 0.8971])\n", "Epoch 41 | Train Loss: 0.2879 | Test MAE: 0.7258 (95% CI [0.6879, 0.7659]) | Test RMSE: 0.8936 (95% CI [0.8490, 0.9398])\n", "Epoch 42 | Train Loss: 0.2796 | Test MAE: 0.6922 (95% CI [0.6523, 0.7335]) | Test RMSE: 0.8885 (95% CI [0.8383, 0.9384])\n", "Epoch 43 | Train Loss: 0.2775 | Test MAE: 0.6467 (95% CI [0.6087, 0.6852]) | Test RMSE: 0.8160 (95% CI [0.7681, 0.8654])\n", "Epoch 44 | Train Loss: 0.2754 | Test MAE: 0.6010 (95% CI [0.5643, 0.6381]) | Test RMSE: 0.7886 (95% CI [0.7390, 0.8376])\n", "Epoch 45 | Train Loss: 0.2635 | Test MAE: 0.5278 (95% CI [0.4958, 0.5620]) | Test RMSE: 0.6978 (95% CI [0.6502, 0.7458])\n", "Epoch 46 | Train Loss: 0.2594 | Test MAE: 0.5359 (95% CI [0.5006, 0.5720]) | Test RMSE: 0.7229 (95% CI [0.6721, 0.7738])\n", "Epoch 47 | Train Loss: 0.2599 | Test MAE: 0.5426 (95% CI [0.5093, 0.5773]) | Test RMSE: 0.7057 (95% CI [0.6610, 0.7515])\n", "Epoch 48 | Train Loss: 0.2649 | Test MAE: 0.5860 (95% CI [0.5506, 0.6228]) | Test RMSE: 0.7665 (95% CI [0.7181, 0.8148])\n", "Epoch 49 | Train Loss: 0.2507 | Test MAE: 0.5366 (95% CI [0.5050, 0.5700]) | Test RMSE: 0.7033 (95% CI [0.6608, 0.7471])\n" ] } ], "source": [ "history = {}\n", "N = 50\n", "for epoch in range(1, N):\n", " tr_loss = train(model, train_loader, optimizer, loss_fn)\n", " metrics = evaluate_with_ci(model, test_loader)\n", " print(f\"Epoch {epoch:02d} | Train Loss: {tr_loss:.4f} | \"\n", " f\"Test MAE: {metrics['mae']:.4f} (95% CI [{metrics['mae_ci'][0]:.4f}, {metrics['mae_ci'][1]:.4f}]) | \"\n", " f\"Test RMSE: {metrics['rmse']:.4f} (95% CI [{metrics['rmse_ci'][0]:.4f}, {metrics['rmse_ci'][1]:.4f}])\")\n", " history[epoch] = metrics\n", " scheduler.step(metrics['mae'])" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "bCUfyuUlKjJb", "outputId": "58469f70-ea2a-4a04-951f-7ee9f902dba7" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "********************\n", "Test MAE: 0.5366 (95% CI [0.5050, 0.5700])\n", "Test RMSE: 0.7033 (95% CI [0.6608, 0.7471])\n", "********************\n" ] } ], "source": [ "final = history[N-1]\n", "print(\"*\"*20)\n", "print(f\"Test MAE: {final['mae']:.4f} (95% CI [{final['mae_ci'][0]:.4f}, {final['mae_ci'][1]:.4f}])\")\n", "print(f\"Test RMSE: {final['rmse']:.4f} (95% CI [{final['rmse_ci'][0]:.4f}, {final['rmse_ci'][1]:.4f}])\")\n", "print(\"*\"*20)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "p12w2xn4Lb-U" }, "outputs": [], "source": [] } ], "metadata": { "accelerator": "GPU", "colab": { "gpuType": "T4", "machine_shape": "hm", "provenance": [] }, "kernelspec": { "display_name": "Python 3", "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.12.11" } }, "nbformat": 4, "nbformat_minor": 0 }