{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Lorentz-Equivariant GNN\n",
"\n",
"Now we will look at graph neural networks using the PyTorch Geometric library: . See {cite:p}`PyTorchGeometric` for more details."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# For Colab\n",
"!pip install torch_geometric\n",
"!pip install torch_sparse\n",
"!pip install torch_scatter"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"import torch_geometric\n",
"\n",
"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
"from tqdm.notebook import tqdm\n",
"import numpy as np\n",
"\n",
"local = False"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# For Colab\n",
"\n",
"!pip install wget\n",
"import wget\n",
"\n",
"!pip install -U PyYAML\n",
"!pip install uproot\n",
"!pip install awkward\n",
"!pip install mplhep"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import yaml\n",
"import os.path\n",
"\n",
"# WGET for colab\n",
"if not os.path.exists(\"definitions_lorentz.yml\"):\n",
" url = \"https://raw.githubusercontent.com/jmduarte/iaifi-summer-school/main/book/definitions_lorentz.yml\"\n",
" definitionsFile = wget.download(url)\n",
"\n",
"with open(\"definitions_lorentz.yml\") as file:\n",
" # The FullLoader parameter handles the conversion from YAML\n",
" # scalar values to Python the dictionary format\n",
" definitions = yaml.load(file, Loader=yaml.FullLoader)\n",
"\n",
"features = definitions[\"features\"]\n",
"spectators = definitions[\"spectators\"]\n",
"labels = definitions[\"labels\"]\n",
"\n",
"nfeatures = definitions[\"nfeatures\"]\n",
"nspectators = definitions[\"nspectators\"]\n",
"nlabels = definitions[\"nlabels\"]\n",
"ntracks = definitions[\"ntracks\"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Graph datasets\n",
"Here we have to define the graph dataset. We do this in a separate class following this example: https://pytorch-geometric.readthedocs.io/en/latest/notes/create_dataset.html#creating-larger-datasets\n",
"\n",
"Formally, a graph is represented by a triplet $\\mathcal G = (\\mathbf{u}, V, E)$, consisting of a graph-level, or *global*, feature vector $\\mathbf{u}$, a set of $N^v$ nodes $V$, and a set of $N^e$ edges $E$.\n",
"The nodes are given by $V = \\{\\mathbf{v}_i\\}_{i=1:N^v}$, where $\\mathbf{v}_i$ represents the $i$th node's attributes.\n",
"The edges connect pairs of nodes and are given by $E = \\{\\left(\\mathbf{e}_k, r_k, s_k\\right)\\}_{k=1:N^e}$, where $\\mathbf{e}_k$ represents the $k$th edge's attributes, and $r_k$ and $s_k$ are the indices of the \"receiver\" and \n",
"\"sender\" nodes, respectively, connected by the $k$th edge (from the sender node to the receiver node).\n",
"The receiver and sender index vectors are an alternative way of encoding the directed adjacency matrix.\n",
"\n",
""
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Processing...\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "51eb09799508446ab490fb88420cc6d3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/8000 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/wmccorma/IAIFI_Summer_School_2022/iaifi-summer-school-PATRICK/book/LorentzGraphDataset.py:153: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at /Users/distiller/project/conda/conda-bld/pytorch_1646756029501/work/torch/csrc/utils/tensor_new.cpp:210.)\n",
" x = torch.tensor(fourvec, dtype=torch.float).T\n",
"Done!\n",
"Processing...\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "85319282abd04bd881fe887ee3782bb1",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/2000 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"['track_pt', 'track_mass', 'track_ptrel', 'track_erel', 'track_etarel', 'track_phirel']\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Done!\n"
]
}
],
"source": [
"# If in colab\n",
"if not os.path.exists(\"LorentzGraphDataset.py\"):\n",
" urlDSD = \"https://raw.githubusercontent.com/jmduarte/iaifi-summer-school/main/book/LorentzGraphDataset.py\"\n",
" DSD = wget.download(urlDSD)\n",
"if not os.path.exists(\"utils.py\"):\n",
" urlUtils = \"https://raw.githubusercontent.com/jmduarte/iaifi-summer-school/main/book/utils.py\"\n",
" utils = wget.download(urlUtils)\n",
"\n",
"\n",
"from LorentzGraphDataset import LorentzGraphDataset\n",
"\n",
"# For Colab\n",
"if not os.path.exists(\"ntuple_merged_90.root\"):\n",
" urlFILE = \"http://opendata.cern.ch/eos/opendata/cms/datascience/HiggsToBBNtupleProducerTool/HiggsToBBNTuple_HiggsToBB_QCD_RunII_13TeV_MC/train/ntuple_merged_90.root\"\n",
" dataFILE = wget.download(urlFILE)\n",
"file_names = [\"ntuple_merged_90.root\"]\n",
"\n",
"##If you pulled github locally\n",
"# if local:\n",
"# file_names = [\n",
"# \"/teams/DSC180A_FA20_A00/b06particlephysics/train/ntuple_merged_10.root\"\n",
"# ]\n",
"# file_names_test = [\n",
"# \"/teams/DSC180A_FA20_A00/b06particlephysics/test/ntuple_merged_0.root\"\n",
"# ]\n",
"# else:\n",
"# file_names = [\n",
"# \"root://eospublic.cern.ch//eos/opendata/cms/datascience/HiggsToBBNtupleProducerTool/HiggsToBBNTuple_HiggsToBB_QCD_RunII_13TeV_MC/train/ntuple_merged_10.root\"\n",
"# ]\n",
"# file_names_test = [\n",
"# \"root://eospublic.cern.ch//eos/opendata/cms/datascience/HiggsToBBNtupleProducerTool/HiggsToBBNTuple_HiggsToBB_QCD_RunII_13TeV_MC/test/ntuple_merged_0.root\"\n",
"# ]\n",
"\n",
"graph_dataset = LorentzGraphDataset(\n",
" \"ldata_train\",\n",
" features,\n",
" labels,\n",
" spectators,\n",
" start_event=0,\n",
" stop_event=8000,\n",
" n_events_merge=1,\n",
" file_names=file_names,\n",
")\n",
"\n",
"test_dataset = LorentzGraphDataset(\n",
" \"ldata_test\",\n",
" features,\n",
" labels,\n",
" spectators,\n",
" start_event=8001,\n",
" stop_event=10001,\n",
" n_events_merge=1,\n",
" file_names=file_names,\n",
")\n",
"print(test_dataset.features)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Graph neural network\n",
"\n",
"Here, we recapitulate the \"graph network\" (GN) formalism {cite:p}`battaglia2018relational`, which generalizes various GNNs and other similar methods.\n",
"GNs are graph-to-graph mappings, whose output graphs have the same node and edge structure as the input. \n",
"Formally, a GN block contains three \"update\" functions, $\\phi$, and three \"aggregation\" functions, $\\rho$.\n",
"The stages of processing in a single GN block are:\n",
"\n",
"$\n",
"\\begin{align}\n",
" \\mathbf{e}'_k &= \\phi^e\\left(\\mathbf{e}_k, \\mathbf{v}_{r_k}, \\mathbf{v}_{s_k}, \\mathbf{u} \\right) & \\mathbf{\\bar{e}}'_i &= \\rho^{e \\rightarrow v}\\left(E'_i\\right) & \\text{(Edge block),}\\\\\n",
" \\mathbf{v}'_i &= \\phi^v\\left(\\mathbf{\\bar{e}}'_i, \\mathbf{v}_i, \\mathbf{u}\\right) & \n",
" \\mathbf{\\bar{e}}' &= \\rho^{e \\rightarrow u}\\left(E'\\right) & \\text{(Node block),}\\\\\n",
" \\mathbf{u}' &= \\phi^u\\left(\\mathbf{\\bar{e}}', \\mathbf{\\bar{v}}', \\mathbf{u}\\right) & \n",
" \\mathbf{\\bar{v}}' &= \\rho^{v \\rightarrow u}\\left(V'\\right) &\\text{(Global block).}\n",
" \\label{eq:gn-functions}\n",
"\\end{align}\n",
"$\n",
"\n",
"where $E'_i = \\left\\{\\left(\\mathbf{e}'_k, r_k, s_k \\right)\\right\\}_{r_k=i,\\; k=1:N^e}$ contains the updated edge features for edges whose receiver node is the $i$th node, $E' = \\bigcup_i E_i' = \\left\\{\\left(\\mathbf{e}'_k, r_k, s_k \\right)\\right\\}_{k=1:N^e}$ is the set of updated edges, and $V'=\\left\\{\\mathbf{v}'_i\\right\\}_{i=1:N^v}$ is the set of updated nodes.\n",
"\n",
"\n",
"\n",
"We will define an interaction network model similar to Ref. {cite:p}`Moreno:2019neq`, but just modeling the particle-particle interactions. It will take as input all of the tracks (with 48 features) without truncating or zero-padding. Another modification is the use of batch normalization {cite:p}`bn` layers to improve the stability of the training."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"import torch.nn as nn\n",
"import torch.nn.functional as F\n",
"import torch_geometric.transforms as T\n",
"from torch_geometric.nn import EdgeConv, global_mean_pool\n",
"from torch.nn import Sequential as Seq, Linear as Lin, ReLU, BatchNorm1d\n",
"from torch_scatter import scatter_mean\n",
"from torch_geometric.nn import MetaLayer\n",
"\n",
"hidden = 16\n",
"outputs = 2\n",
"\n",
"\n",
"class LorentzEdgeBlock(torch.nn.Module):\n",
" def __init__(self):\n",
" super(LorentzEdgeBlock, self).__init__()\n",
" self.edge_mlp = Seq(Lin(4, hidden), ReLU(), Lin(hidden, hidden))\n",
" self.minkowski = torch.from_numpy(\n",
" np.array(\n",
" [\n",
" [-1.0, 0.0, 0.0, 0.0],\n",
" [0.0, 1.0, 0.0, 0.0],\n",
" [0.0, 0.0, 1.0, 0.0],\n",
" [0.0, 0.0, 0.0, 1.0],\n",
" ],\n",
" dtype=np.float32,\n",
" )\n",
" )\n",
"\n",
" def psi(self, x):\n",
" return torch.sign(x) * torch.log(torch.abs(x) + 1)\n",
"\n",
" def innerprod(self, x1, x2):\n",
" return torch.sum(\n",
" torch.mul(torch.matmul(x1, self.minkowski), x2), 1, keepdim=True\n",
" )\n",
"\n",
" def forward(self, src, dest, edge_attr, u, batch):\n",
" out = torch.cat(\n",
" [\n",
" self.innerprod(src, src),\n",
" self.innerprod(src, dest),\n",
" self.psi(self.innerprod(dest, dest)),\n",
" self.psi(self.innerprod(src - dest, src - dest)),\n",
" ],\n",
" dim=1,\n",
" )\n",
" return self.edge_mlp(out)\n",
"\n",
"\n",
"class LorentzNodeBlock(torch.nn.Module):\n",
" def __init__(self):\n",
" super(LorentzNodeBlock, self).__init__()\n",
" self.node_mlp_1 = Seq(Lin(1 + hidden, hidden), ReLU(), Lin(hidden, hidden))\n",
" self.node_mlp_2 = Seq(Lin(1 + hidden, hidden), ReLU(), Lin(hidden, hidden))\n",
" self.minkowski = torch.from_numpy(\n",
" np.array(\n",
" [\n",
" [-1.0, 0.0, 0.0, 0.0],\n",
" [0.0, 1.0, 0.0, 0.0],\n",
" [0.0, 0.0, 1.0, 0.0],\n",
" [0.0, 0.0, 0.0, 1.0],\n",
" ],\n",
" dtype=np.float32,\n",
" )\n",
" )\n",
"\n",
" def innerprod(self, x1, x2):\n",
" return torch.sum(\n",
" torch.mul(torch.matmul(x1, self.minkowski), x2), 1, keepdim=True\n",
" )\n",
"\n",
" def forward(self, x, edge_index, edge_attr, u, batch):\n",
" row, col = edge_index\n",
" out = torch.cat([self.innerprod(x[row], x[row]), edge_attr], dim=1)\n",
" out = self.node_mlp_1(out)\n",
" out = scatter_mean(out, col, dim=0, dim_size=x.size(0))\n",
" out = torch.cat([self.innerprod(x, x), out], dim=1)\n",
" return self.node_mlp_2(out)\n",
"\n",
"\n",
"class GlobalBlock(torch.nn.Module):\n",
" def __init__(self):\n",
" super(GlobalBlock, self).__init__()\n",
" self.global_mlp = Seq(Lin(hidden, hidden), ReLU(), Lin(hidden, outputs))\n",
"\n",
" def forward(self, x, edge_index, edge_attr, u, batch):\n",
" out = scatter_mean(x, batch, dim=0)\n",
" return self.global_mlp(out)\n",
"\n",
"\n",
"class LorentzInteractionNetwork(torch.nn.Module):\n",
" def __init__(self):\n",
" super(LorentzInteractionNetwork, self).__init__()\n",
" self.lorentzinteractionnetwork = MetaLayer(\n",
" LorentzEdgeBlock(), LorentzNodeBlock(), GlobalBlock()\n",
" )\n",
"\n",
" def forward(self, x, edge_index, batch):\n",
"\n",
" x, edge_attr, u = self.lorentzinteractionnetwork(\n",
" x, edge_index, None, None, batch\n",
" )\n",
" return u\n",
"\n",
"\n",
"model = LorentzInteractionNetwork().to(device)\n",
"optimizer = torch.optim.Adam(model.parameters(), lr=1e-6)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define training loop"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"@torch.no_grad()\n",
"def test(model, loader, total, batch_size, leave=False):\n",
" model.eval()\n",
"\n",
" xentropy = nn.CrossEntropyLoss(reduction=\"mean\")\n",
"\n",
" sum_loss = 0.0\n",
" t = tqdm(enumerate(loader), total=total / batch_size, leave=leave)\n",
" for i, data in t:\n",
" data = data.to(device)\n",
" y = torch.argmax(data.y, dim=1)\n",
" batch_output = model(data.x, data.edge_index, data.batch)\n",
" batch_loss_item = xentropy(batch_output, y).item()\n",
" sum_loss += batch_loss_item\n",
" t.set_description(\"loss = %.5f\" % (batch_loss_item))\n",
" t.refresh() # to show immediately the update\n",
"\n",
" return sum_loss / (i + 1)\n",
"\n",
"\n",
"def train(model, optimizer, loader, total, batch_size, leave=False):\n",
" model.train()\n",
"\n",
" xentropy = nn.CrossEntropyLoss(reduction=\"mean\")\n",
"\n",
" sum_loss = 0.0\n",
" t = tqdm(enumerate(loader), total=total / batch_size, leave=leave)\n",
" for i, data in t:\n",
" data = data.to(device)\n",
" y = torch.argmax(data.y, dim=1)\n",
" optimizer.zero_grad()\n",
" batch_output = model(data.x, data.edge_index, data.batch)\n",
" batch_loss = xentropy(batch_output, y)\n",
" batch_loss.backward()\n",
" batch_loss_item = batch_loss.item()\n",
" t.set_description(\"loss = %.5f\" % batch_loss_item)\n",
" t.refresh() # to show immediately the update\n",
" sum_loss += batch_loss_item\n",
" optimizer.step()\n",
"\n",
" return sum_loss / (i + 1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Define training, validation, testing data generators"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7484\n",
"5988\n",
"1496\n",
"1887\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/wmccorma/miniconda3/envs/ml-iaifi/lib/python3.9/site-packages/torch_geometric/deprecation.py:12: UserWarning: 'data.DataListLoader' is deprecated, use 'loader.DataListLoader' instead\n",
" warnings.warn(out)\n"
]
}
],
"source": [
"from torch_geometric.data import Data, DataListLoader, Batch\n",
"from torch.utils.data import random_split\n",
"\n",
"\n",
"def collate(items):\n",
" l = sum(items, [])\n",
" return Batch.from_data_list(l)\n",
"\n",
"\n",
"torch.manual_seed(0)\n",
"valid_frac = 0.20\n",
"full_length = len(graph_dataset)\n",
"valid_num = int(valid_frac * full_length)\n",
"batch_size = 32\n",
"\n",
"train_dataset, valid_dataset = random_split(\n",
" graph_dataset, [full_length - valid_num, valid_num]\n",
")\n",
"\n",
"train_loader = DataListLoader(\n",
" train_dataset, batch_size=batch_size, pin_memory=True, shuffle=True\n",
")\n",
"train_loader.collate_fn = collate\n",
"valid_loader = DataListLoader(\n",
" valid_dataset, batch_size=batch_size, pin_memory=True, shuffle=False\n",
")\n",
"valid_loader.collate_fn = collate\n",
"test_loader = DataListLoader(\n",
" test_dataset, batch_size=batch_size, pin_memory=True, shuffle=False\n",
")\n",
"test_loader.collate_fn = collate\n",
"\n",
"\n",
"train_samples = len(train_dataset)\n",
"valid_samples = len(valid_dataset)\n",
"test_samples = len(test_dataset)\n",
"print(full_length)\n",
"print(train_samples)\n",
"print(valid_samples)\n",
"print(test_samples)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Train"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4218389d3f8f473689e0b7f5acdf982e",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/2 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/187.125 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/46.75 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch: 00, Training Loss: 0.7810\n",
" Validation Loss: 0.7829\n",
"New best model saved to: lorentznetwork_best.pth\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "1114825255ea478bbf1252cd406b9927",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/187.125 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ff4de51a4b5947e88522ef67466ae6e3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/46.75 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch: 01, Training Loss: 0.7805\n",
" Validation Loss: 0.7821\n",
"New best model saved to: lorentznetwork_best.pth\n"
]
}
],
"source": [
"import os.path as osp\n",
"\n",
"n_epochs = 2\n",
"stale_epochs = 0\n",
"best_valid_loss = 99999\n",
"patience = 5\n",
"t = tqdm(range(0, n_epochs))\n",
"\n",
"for epoch in t:\n",
" loss = train(\n",
" model,\n",
" optimizer,\n",
" train_loader,\n",
" train_samples,\n",
" batch_size,\n",
" leave=bool(epoch == n_epochs - 1),\n",
" )\n",
" valid_loss = test(\n",
" model,\n",
" valid_loader,\n",
" valid_samples,\n",
" batch_size,\n",
" leave=bool(epoch == n_epochs - 1),\n",
" )\n",
" print(\"Epoch: {:02d}, Training Loss: {:.4f}\".format(epoch, loss))\n",
" print(\" Validation Loss: {:.4f}\".format(valid_loss))\n",
"\n",
" if valid_loss < best_valid_loss:\n",
" best_valid_loss = valid_loss\n",
" modpath = osp.join(\"lorentznetwork_best.pth\")\n",
" print(\"New best model saved to:\", modpath)\n",
" torch.save(model.state_dict(), modpath)\n",
" stale_epochs = 0\n",
" else:\n",
" print(\"Stale epoch\")\n",
" stale_epochs += 1\n",
" if stale_epochs >= patience:\n",
" print(\"Early stopping after %i stale epochs\" % patience)\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Evaluate on testing data"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "6d13d8453de64f0e866ef3be894b6b90",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
" 0%| | 0/58.96875 [00:00, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# In case you need to load the model from a pth file\n",
"# Trained on 4 vectors (as above in notebook)\n",
"# urlPTH = \"https://raw.githubusercontent.com/jmduarte/iaifi-summer-school/main/book/lorentznetwork_best.pth\"\n",
"# pthFile = wget.download(urlPTH)\n",
"# model.load_state_dict(torch.load(\"lorentznetwork_best.pth\"))\n",
"\n",
"model.eval()\n",
"t = tqdm(enumerate(test_loader), total=test_samples / batch_size)\n",
"y_test = []\n",
"y_predict = []\n",
"track_pt = []\n",
"for i, data in t:\n",
" data = data.to(device)\n",
" batchmask = torch.cat([-1.0 * torch.ones(1), data.batch[:-1]], dim=0)\n",
" track_pt.append(data.x[batchmask != data.batch, 0])\n",
" batch_output = model(data.x, data.edge_index, data.batch)\n",
" y_predict.append(batch_output.detach().cpu().numpy())\n",
" y_test.append(data.y.cpu().numpy())\n",
"track_pt = np.concatenate(track_pt)\n",
"y_test = np.concatenate(y_test)\n",
"y_predict = np.concatenate(y_predict)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"