{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# HIV Inhibitor Prediction Using Graph Neural Networks (GNN) on Amazon SageMaker\n", "\n", "**Note:** This notebook was last tested with the `Python 3 (Pytorch 1.12 Python 3.8 CPU Optimized)` environment image in Amazon SageMaker Studio.\n", "\n", "## Learning Objectives\n", "\n", "- Understand the basics of graph neural networks and how they can be applied to molecular graphs\n", "- Install and use the Deep Graph Library (DGL)\n", "- Build, train, and deploy a DGL model on SageMaker\n", "- Perform hyperparameter tuning of deep learning models\n", "- Use your own scripts to train custom models in SageMaker \n", "- Track model training and other tasks using SageMaker Experiments\n", "\n", "\n", "## Introduction\n", "\n", "Human immunodeficiency virus type 1 (HIV-1) is the most common cause of Acquired Immunodeficiency Syndrome (AIDS). One ongoing area of research is finding compounds that inhibit HIV-1 viral replication. Schematically, this is shown below as:\n", "\n", "![Inhibitor](img/1.jpg)\n", "
\n", " \n", "Image Source : Biological evaluation of molecules of the azaBINOL class as antiviral agents (https://www.sciencedirect.com/science/article/abs/pii/S0968089619306704)\n", " \n", "
\n", "\n", "
\n", "
\n", "
\n", "\n", "### Why is Deep Learning Useful for Analyzing Biological Networks?\n", "\n", "If you are familiar with classical network analysis, you may have encountered concepts such as the [betweenness centrality](https://en.wikipedia.org/wiki/Betweenness_centrality), [degree centrality](https://en.wikipedia.org/wiki/Centrality#Degree_centrality), or [random walk with restart](https://towardsdatascience.com/random-walks-with-restart-explained-77c3fe216bca). These methods are useful for calculating properties of nodes, analyzing networks, or grouping disease [genes](https://pubmed.ncbi.nlm.nih.gov/18371930/). However, these methods are **transductive,** which means that they can only generate features for a particular graph. They cannot predict edges, classify graphs, or perform other tasks where multiple graphs are needed. See [this](https://arxiv.org/abs/1706.02216) paper for further discussion of this issue.\n", "\n", "(A quick note on nomenclature: we use the term “graphs\" to refer to biological networks; we reserve the term \"network\" for a neural network. Although it is common in the computational biology field to refer to biological graphs as networks, in the deep learning field, \"network\" refers almost exclusively to a neural network).\n", "\n", "[Convolution neural networks](https://www.d2l.ai/chapter_convolutional-neural-networks/index.html), commonly used in computer vision, are also useful for analyzing graphs. Convolutions allow for **inductive** learning, whereby features are learned for different graph topologies. These convolutions transform the underlying information in the graph nodes and edges. While a single convolutional layer is generally not sufficient for most tasks, deep graph convolutional neural networks can perform graph prediction (i.e., predict the class of a network), link prediction (predict missing edges in a network), and other tasks. \n", "\n", "Deep learning models can also incorporate different edge types as well as external information about edges and nodes. This makes deep learning an attractive approach for analyzing and making predictions about complex graphs. Biological networks are frequently very heterogeneous and include diverse data types such as metabolic, biophysical, proteomic and functional assays, and information about gene regulatory networks. For example, [this](https://www.amazon.science/blog/amazon-web-services-open-sources-biological-knowledge-graph-to-fight-covid-19) blog post shows how a knowledge graph with diverse node and edge types can predict drug repurposing.\n", "\n", "While scientists can create their own convolutional layers, deep learning researchers have already built many convolutions and architectures that have proven useful in many applications. For example, [GraphSage](https://arxiv.org/pdf/1706.02216.pdf) can predict protein-protein interactions. Another commonly used approach is [Graph Attention Networks](https://arxiv.org/pdf/1710.10903.pdf) (GAT).\n", "\n", "For a more details of deep graph learning and how it can help analyze biological data, see [this](https://academic.oup.com/bib/article/22/2/1515/5964185) review paper. You may also find [this](http://snap.stanford.edu/deepnetbio-ismb/) tutorial useful.\n", "\n", "### What is the Deep Graph Library (DGL) and When Should You Use It?\n", "\n", "Deep Graph Library (DGL) allows researches and developers to easily and quickly apply deep graph learning approaches to their data by abstracting away much of the difficult deep learning work and code. The DGL library comes with a number of prebuilt layers, including [GraphSage convolutions](https://docs.dgl.ai/generated/dgl.nn.pytorch.conv.SAGEConv.html#dgl.nn.pytorch.conv.SAGEConv), [GATs](https://docs.dgl.ai/generated/dgl.nn.pytorch.conv.GATConv.html#dgl.nn.pytorch.conv.GATConv), and [others](https://docs.dgl.ai/api/python/nn-pytorch.html). Users have have the flexibility to create their own layers and architectures as well. \n", "\n", "The [DGL-LifeScience](https://lifesci.dgl.ai/index.html) Python package provides an even further abstraction of DGL, so that computational biologists, biochemists, and bioinformaticians who wish to leverage deep graph methods can easily do so for certain common use cases and performing common operations in the context of analyzing small and large molecules. If you want to learn more about how to use the DGL library, we recommend getting started with [this](https://docs.dgl.ai/en/0.6.x/guide/graph.html) tutorial.\n", "\n", "### Notebook Overview\n", "\n", "This example notebook trains multiple graph neural network models using Deep Graph Library and deploys them using Amazon SageMaker, a comprehensive and fully-managed machine learning service. With SageMaker, data scientists and developers can quickly and easily build and train machine learning models and then directly deploy them into a production-ready hosted environment. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Install Dependencies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's start by installing the latest version of `dgl` and other Python dependencies." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "%pip install --disable-pip-version-check -U -q -r requirements.txt" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/plain": [ "'2022.09.3'" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from functools import partial\n", "import numpy as np\n", "import torch\n", "import torch.nn as nn\n", "import pandas as pd\n", "\n", "import dgl\n", "\n", "from dgllife.model import load_pretrained\n", "from dgllife.utils import smiles_to_bigraph, EarlyStopping, Meter, CanonicalAtomFeaturizer, CanonicalBondFeaturizer\n", "from functools import partial\n", "from torch.optim import Adam\n", "from torch.utils.data import DataLoader\n", "\n", "from dgllife.data import HIV\n", "\n", "import rdkit\n", "from rdkit import Chem\n", "from rdkit.Chem import Draw\n", "\n", "rdkit.__version__" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [] }, "outputs": [], "source": [ "node_featurizer = CanonicalAtomFeaturizer(atom_data_field='feat')\n", "edge_featurizer = None\n", "num_workers = 1\n", "split_ratio = \"0.7:0.2:0.1\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Explore the Dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The [Drug Therapeutics Program (DTP) AIDS Antiviral Screen](https://wiki.nci.nih.gov/display/NCIDTPdata/AIDS+Antiviral+Screen+Data) tested the ability of 43,850 compounds to inhibit viral replication. The DGL library has a pre-processed version of this dataset where each compound is classified as either Confirmed Inactive (CI; labeled as 0) or Confirmed Moderately Active/Confirmed Active (CM,CA; labeled as 1). You can download and inspect the raw dataset from [here](https://moleculenet.org/datasets-1). Alternatively, you can download a subset of the data focused on HIV [here](https://deepchemdata.s3-us-west-1.amazonaws.com/datasets/HIV.csv). This data is in .csv format and provides the struture of the molecule (in [SMILES](https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system) format), the type of activity, and activity against HIV.\n", "\n", "\n", "|SMILES string |activity |HIV_active |\n", "|--- |--- |--- |\n", "|CC(C)(CCC(=O)O)CCC(=O)O |CI |0 |\n", "|O=C(O)Cc1ccc(SSc2ccc(CC(=O)O)cc2)cc1 |CM |1 |\n", "|O=C(O)c1ccccc1SSc1ccccc1C(=O)O |CI |0 |\n", "|CCCCCCCCCCCC(=O)Nc1ccc(SSc2ccc(NC(=O)CCCCCCCCCCC)cc2)cc1 |CI |0 | \n", "\n", "Confirmed inactive (CI) compounds are labeled 0, while confirmed moderately active (CM)/confirmed active (CA) are labeled 1. We can use these labels to define our ML task as a **graph classification problem**. We will construct a graph to represent each molecule, considering the atoms as nodes and the chemical bonds between atoms as edges. Then, we will use GNN techniques to classify each molecule as either active or inactive." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "dataset = HIV(smiles_to_graph=partial(smiles_to_bigraph, add_self_loop=True),\n", " node_featurizer=node_featurizer,\n", " edge_featurizer=edge_featurizer,\n", " n_jobs=num_workers)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "dgllife.data.hiv.HIV" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(dataset)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(41127, 2)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dataset.df.shape" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
smilesHIV_active
0CCC1=[O+][Cu-3]2([O+]=C(CC)C1)[O+]=C(CC)CC(CC)...0
1C(=Cc1ccccc1)C1=[O+][Cu-3]2([O+]=C(C=Cc3ccccc3...0
2CC(=O)N1c2ccccc2Sc2c1ccc1ccccc210
3Nc1ccc(C=Cc2ccc(N)cc2S(=O)(=O)O)c(S(=O)(=O)O)c10
4O=S(=O)(O)CCS(=O)(=O)O0
5CCOP(=O)(Nc1cccc(Cl)c1)OCC0
6O=C(O)c1ccccc1O0
7CC1=C2C(=COC(C)C2C)C(O)=C(C(=O)O)C1=O0
8O=[N+]([O-])c1ccc(SSc2ccc([N+](=O)[O-])cc2[N+]...0
9O=[N+]([O-])c1ccccc1SSc1ccccc1[N+](=O)[O-]0
10CC(C)(CCC(=O)O)CCC(=O)O0
11O=C(O)Cc1ccc(SSc2ccc(CC(=O)O)cc2)cc11
12O=C(O)c1ccccc1SSc1ccccc1C(=O)O0
13CCCCCCCCCCCC(=O)Nc1ccc(SSc2ccc(NC(=O)CCCCCCCCC...0
14Sc1cccc2c(S)cccc120
\n", "
" ], "text/plain": [ " smiles HIV_active\n", "0 CCC1=[O+][Cu-3]2([O+]=C(CC)C1)[O+]=C(CC)CC(CC)... 0\n", "1 C(=Cc1ccccc1)C1=[O+][Cu-3]2([O+]=C(C=Cc3ccccc3... 0\n", "2 CC(=O)N1c2ccccc2Sc2c1ccc1ccccc21 0\n", "3 Nc1ccc(C=Cc2ccc(N)cc2S(=O)(=O)O)c(S(=O)(=O)O)c1 0\n", "4 O=S(=O)(O)CCS(=O)(=O)O 0\n", "5 CCOP(=O)(Nc1cccc(Cl)c1)OCC 0\n", "6 O=C(O)c1ccccc1O 0\n", "7 CC1=C2C(=COC(C)C2C)C(O)=C(C(=O)O)C1=O 0\n", "8 O=[N+]([O-])c1ccc(SSc2ccc([N+](=O)[O-])cc2[N+]... 0\n", "9 O=[N+]([O-])c1ccccc1SSc1ccccc1[N+](=O)[O-] 0\n", "10 CC(C)(CCC(=O)O)CCC(=O)O 0\n", "11 O=C(O)Cc1ccc(SSc2ccc(CC(=O)O)cc2)cc1 1\n", "12 O=C(O)c1ccccc1SSc1ccccc1C(=O)O 0\n", "13 CCCCCCCCCCCC(=O)Nc1ccc(SSc2ccc(NC(=O)CCCCCCCCC... 0\n", "14 Sc1cccc2c(S)cccc12 0" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dataset.df.head(15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our dataset contains around 41,000 molecules in `SMILES` format. The `HIV_active` column (label) indicates if the molecule inhibits HIV. Let's verify if there are any missing values in the dataset." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 41127 entries, 0 to 41126\n", "Data columns (total 2 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 smiles 41127 non-null object\n", " 1 HIV_active 41127 non-null int64 \n", "dtypes: int64(1), object(1)\n", "memory usage: 642.7+ KB\n" ] } ], "source": [ "dataset.df.info()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dataset.df.isnull().values.any()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are no missing values in this dataset. Next, let's explore the class distribution of the dataset." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 39684\n", "1 1443\n", "Name: HIV_active, dtype: int64" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dataset.df['HIV_active'].value_counts()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "dataset.df['HIV_active'].value_counts().plot(kind='bar')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are only ~3% compounds classified as HIV inhibtiors. This means that the dataset is heavily imbalaced." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Explore Molecular Properties With RDKit\n", "\n", "[RDKit](https://www.rdkit.org/docs/cppapi/index.html) is an open-source cheminformatics toolkit. It includes a collection of standard cheminformatics functions for molecule I/O, substructure searching, chemical reactions, 2D and 3D coordinate generation, fingerprinting, etc.\n", "\n", "We are going to use this library to explore the molecules in the dataset. First, let's randomly select and visualize several molecules." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAACWAAAASwCAIAAADwxubWAAAABmJLR0QA/wD/AP+gvaeTAAAgAElEQVR4nOzdebyXc/4//uc5p1WFkeyE0VSypywRLWQpc4upRjqSUiZjyZdOY/jKOmUte4eYFpmyRKNCqWihnMoHbRRKsrRolzrnvH9/vPv08x3D1Fk6o+t+v/nj3el9va7n1e04531dj9fz9cpIpVIBAAAAAAAAJENmWRcAAAAAAAAA7DwCQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECCCAgBAAAAAAAgQQSEAAAAAAAAkCACQgAAAAAAAEgQASEAAAAAAAAkiIAQAAAAAAAAEkRACAAAAAAAAAkiIAQAAAAAAIAEERACAAAAAABAgggIAQAAAAAAIEEEhAAAAAAAAJAgAkIAAAAAAABIEAEhAAAAAAAAJIiAEAAAAAAAABJEQAgAAAAAAAAJIiAEAAAAAACABBEQAgAAAAAAQIIICAEAAAAAACBBBIQAAAAAAACQIAJCAAAAAAAASBABIQAAAAAAACSIgBAAAAAAAAASREAIAAAAAAAACSIgBAAAAAAAgAQREAIAAAAAAECClCvrAgAAAAAohsmTY+HCOPHEOProsi4FAIBfBx2EAAAAQBJt2rSpa9euNWrUOOigg+bMmVPW5RRV797x9ttRu3Y8+GC89FKpnGLevOjWLbp0iUcfjVQqhg+PDz/c+lc331wqZwQAoJRlpFKpsq4BAAAAYOcpLCx87rnn/vrXvy5evDj9lQoVKlx//fU333xzlSpVyra2HXb66TF5ckTE99/H738fb7xRwuNv3hwtWsQLL0T16tG7d9SuHZ99FqeeGmeeGalUnHNOvP56CZ8RAIDSp4MQAAAASJB33nnn9NNP79Chw+LFi4888sj777//yiuvLCgo6NOnT+3atQcPHlzWBe6IzZtjt922vq5cOTZvjojo2zfuuSe2bCmZUyxYEEcfHdWrR0RcdtnWAPLFF6N//3jooZI5BQAAO52AEAAAAEiE+fPnt23b9tRTT502bdqBBx44YMCAJ5988pRTTnn88cdnzJhx8sknf/nllx07dmzWrNm8efPKutjtU6FCbN4cmzZFRCxcGIccEt98E717R05OHHtsTJxY3PFXr46srCgs3PrHgoLIyoqIOOGEaNYsmjUr7vgAAJQRASEAAACwi1uxYsW111579NFHP//881WqVMnJyRk3btyUKVNOO+20dPvgCSecMG3atEGDBu29994TJkw49thjr7322vXr15d14dvhttuibdu49tq44Ya4/fb46KMYOzaOPDLmzYumTaNVq/jii6IMu2JFXHttHHJIVK4cc+fGV19FRDz+eLRqFRFx2GFx1FFRr15JXggA7Fo2bYpZs7a+njkzfvihTKuBnxAQAgAAALusjRs39u3b97e//e1DDz2UkZHRtWvXvLy8H3744bjjjhsyZEjlypVbtWqVn58fERkZGZdeeumCBQuuueaagoKChx56qG7dus8//3xZX8F/0rhxjBoVffrEyy/HrFnRvHn07h3DhkW/flG1arz6atStG717b119dHts2BC33RaHHRYPPRSbNsU778TAgXH33dGlSxx7bPz+91GvXuy7b0RERkY0aVJ6VwYAv2orV8bDD2993a9ffPddmVYDPyEgBAAAAHZBhYWFgwcPPuKII3r16rV27drmzZtPnz79yCOPPOWUU/r165efn9+mTZs5c+bceeedFStW3HbUXnvt1b9//+nTpzds2HDp0qVt27Zt1arVZ599VoYXsl0qV46IKCyMGjXirbeiQYNYtixmzIjWrdOB3/SOHSdPnvwfBiksjMGDo1at6N071q+P5s1j1qxo3z4OOywefjieeiqysyMifv/7qFt36yG9epXmVQHAr1tBQXz/fXz/fRQUlHUp8BMZqVSqrGuA5Jo0adLGjRubNGlSOX0vBwAAQEkYP378//k//+eDDz6IiAYNGtxzzz3Lly/v1avXp59+GhHNmze/9957jzvuuF8YobCwcOjQoddff/3KlSsrV67cs2fPXr16VapUaSddQJGtXh233hqPPhoFBXHAAfG3v0WNGpvuvvvg2bNXbtzYoUOHe++9d990/9//642xY5vfdFPm++9HRJx0Utx3X5x22s4uHgB2ovz8/D59+ixZsuTcc89t3bp1iY//5Zdx1lnRvHlExBtvxKRJsd9+JX4SKDoBIZSlww8//LPPPlu0aNHhhx9e1rUAAADsCiZPnty7d+8JEyZExCGHHHLHHXccccQRN95447Rp0yLiyCOP7Nu3b8uWLbdztK+//rpnz55Dhw5NpVLpdUrPO++8Uqy+pOTlxVVXxYwZERFnn72kX7+Rb7zx17/+dcOGDXvuuWfv3r3//Oc/Z2Vlpd87Z86cnj17jhkzZvoZZzT87LO4447Izo6MjLKsHwBK0xdffDFs2LDHHntsyZIlEZGVlfXkk0926tSpZM/y5Zdx883xzDMREdnZce+9AkL+u1hiFAAAANhF3H777WecccaECROqV6/+4IMPjh49+tVXX23UqNG0adMOPPDAAQMGfPDBB9ufDkbEfvvtN3jw4EmTJh111FGLFi06//zzzzjjjBnp4O2/2YknxrvvxqBBUaPGipUrjzj22E8//fTdd99t2bLl6tWrr7vuuhNPPHHatGmLFy/Ozs4+5phjxowZU7169Tl/+EN88klceql0EIBd0qZNm4YNG9a0adOaNWv26tVryZIltWvXPvDAAwsKCi6//PIePXqktyWGhNBBCGVJByEAAEAJqlq16oYNG84999x+/frdf//9Tz/9dH5+/u67756Tk3PdddfttttuRR45Pz//0UcfveWWW9atW1ezZs3PP/+85KouTStWPPC3v93Yr19hYeGhhx7ar1+/jIyMa6+99vPPP8/IyChfvvzmzZsrVKhw2WWX3XnnnTVq1CjrcgGgVMybN2/QoEEDBw5csWJFRFSqVKlVq1ZXXHHFQQcdVLdu3dzc3Kuvvnrz5s2nn376888//2/X4i6CLVti0KDo3DkyMuLVV+Occ6JcuRIZGEqGgBDKkoAQAACgBKVvshYsWLB69eqTTz45Kyvr8ssvv+222/YroSW98vLyGjRokJmZWVBQUCID7hwzZ87s3r17uvGxWbNmf/nLXy666KI1a9ZkZGS0b9/+rrvuqlmzZlnXCAAl7/vvv3/11Vdzc3PHjx+f/kr9+vWzs7PPPvvsUaNGPfnkk19//fWyZct23333mTNnXnjhhUuWLDnooINefPHFhg0bFv/sGzbEQQdFv37RsWNceGE891xUrFj8UaHEWGIUAGDnmjEjrr46rr025swp61IAYFeTzu0qVarUsGHDBx544KOPPhowYECJpIPdu3fv1q3b/vvvHxHF6UQsE/Xr1582bdojjzyy5557vvnmmy1atFizZk3VqlXfe++9oUOHSgcB2PXMnDmzW7du++yzT9u2bcePH7/HHnt07do1Ly/v1ltvnTBhwjHHHNOrV69FixbVqFFj4cKFEVG/fv28vLwzzzxz6dKljRs3fvrpp0ukjLPPjuefj5UrS2QwKGECQgCAnWjhwrjttrj77rjllrj66li+vKwLIiLi22+/HTFixDnnnHPkkUfefPPNZV0OAEW3ZcuWiChfvnxEXHfddbVr1y6pkV988cXc3NwNGzZsG//XJSsr66qrrlq0aNE111xTWFgYEe3atatfv35Z1wUAJWn16tW5ubnHH3/8iSeemJubu379+vr16w8YMOC99947/PDDL7roogsuuGDUqFGZmZktW7YcMWLEwoULTzjhhPSxNWrUGDduXE5Ozg8//NC5c+du3bqlP1cUwapV8dBDsW5dZGbGrbeGu0z+O1nyFgBgJxozJjp3jmrVolq1aNMmJk6Mtm3LuqaEWrdu3fTp08ePHz9+/PhZs2ZtW3j/7rvvrlOnTocOHcq2PACK5scBYWmMnPZrDAjT9tprr/79+2dmZvbr169y5cplXQ4AlKR27dq9+OKL6eUE9ttvv44dO2ZnZ8+dO3fw4MHdu3dPf7127dqdOnXq1KnTPvvs89MRypUr16dPn3r16nXr1i03N3fu3LnPP//8Di1FMHNm5ObG0KGxcePWrzRoEIMGbV1CaMGCKLnJS1BcAkIAgJ1oy5bY9kixfPko6mxEimbNmjVvv/32xIkTJ06c+MEHH6T7JyKiSpUqjRo1Ou2006ZPnz569OjLLrssKyvr4osvLttqASgCAeH2OOSQQ+LXfxUA8GPjxo0bMWJERDRt2vTKK6+sW7fu0KFDmzRpsnz58oioVKnShRde2LVr12bNmmVkZPzyUNnZ2fXq1WvduvWUKVNOPPHEF1988aSTTvrlQ5Yti0GDYuDAWLQoIiIrK847L2rXjg8+iIi4444YOzYeeSR69Yo774ycnJK4YCg2ASEAwE505pnx8MPRsmUUFsarr0b//mVd0K5vw4YN77zzzpQpU6ZOnfrWW29te7xbrly5448/vnnz5s2bN99vv/2mTp2655573nLLLbfddlvv3r2zs7MzMzPbtWtXtsUDsKMEhNuj9P6VAKCsrFu3LiIaNGjw5ptvtm/fvu3/rtZTv379Ll26XHzxxXvsscf2j3bCCSfk5eW1a9du4sSJZ5xxxqOPPtq5c+efvq2goGDMmDHjxlV7/PEz8/MjImrWjMsvj06d4uCDIyJatIiI+M1vYtGiyMmJ/Pzo1Ss++SQefTQqVizmFUNxCQgBAHaWdeviuOOiadO4/PJIpeLyy6NmzbKuaddUWFj44IMPrlq1atKkSTNmzMhP36hFVKhQ4fTTT2/SpEmTJk1q1qyZl5c3fvz4Ll26LF68OCJOOeWUdu3a3XrrrVu2bLnrrruys7N32223Vq1alemlALBjSjsgTC9J/WuP1jZv3hy//qsAgB9buHBhRDRq1CgijjrqqD322KNdu3bdunXbtsXgjqpRo8Ybb7xx88039+3bt0uXLjNmzHj44YcrVKiQ/tulS5c+++yzjz/++OLFi/fa63eVKy8444y49NK48MLIyvr3A/btG8cfH126xMCBMXt2vPRSCTwS+Oyzz6666qr169e3atXqxhtvLO5wJIyAEABgZ/nb3+KJJ6Jfv+jWLQ45JA44oKwL2mrRokUvv/zyP/7xj3POOeeOO+4o63JKQOvWrUeNGpV+nZWVVb9+/ebNmzdq1KhWrVozZsyYOHFip06dPv/8823v33fffc8888zmzZun/3jnnXcWFBT06dPnD3/4w0svvXT++efv/EsAoGjSk0LKlSvhxx2FhYWFhYWZmZnp7Yt+7dGaDkIAdj0ff/xxRNSuXTsirr766h49ehR/t930loRHHXVU165dc3Nz58yZM2zYsGnTpg0cOHDChAnpTSvq1KnTpUuXSy/dWKPGbv9xwD/+MerWjdatY9asaNAghg+PJk2KUtjmzZtfeeWVwYMHjx07Nv3hZOrUqYWFhT179vyPC6jCNgJCAICdZezY+O67OPDAyM6OhQvj/ffj2GPLqpavvvpqypQp48ePf+ONN7ZFZTNnzly+fPkTTzxRVlWViM2bN48dOzYiLrroos6dO9epUycvL2/KlCm33XbbzJkzt72tWrVqJ510UnqJ0RNOOOFfbqL+9re/FRQU3HvvvW3atHn11VebNm26sy8DgB23ZcuWVCpVrly5En80tq3lbteI1naNq4BdwLhx4x5++OGjjjrqzjvvzMzMLOty4NdtwYIFEfG73/0uIqpVq1aCI3fo0KFWrVoXXXTR1KlT69Wrt379+ojYbbfd2rRp06VLl9NOO22HRjv22HjvvfjjH2P8+DjnnNQTT4zt1Om87T987ty5Tz311JAhQ1asWBERlStXvuCCC1auXDlhwoRevXrNnj174MCBVapU2aGSSCwBIQDATvH11/E//xNVqsRBB8XChbHXXnHUUTu5hGXLlk2YMGHSpEkTJ0789NNPt329Ro0aZ5xxxooVK6ZNmzZgwIB99933tttu28m1laApU6Zs2bLluOOOe+GFF3Jycs477/+/19pjjz0aN27ctGnTJk2aHH300b/8FKZv374bNmx47LHHWrVqNXr06DPPPLPUSwegeEp7fVEBIVAi1q5d+49//CM3Nzc9fe3VV1/Ny8sbMWLEnnvuWdalwa9YOiBMdxCWuJNOOikvL69+/fpbtmzZZ599unbtesUVV+y1115FG6169XjttejVK9577+7LL7/5zTcvefLJJ3+533HTpk3//Oc/c3Nz33zzzfSC50ceeeSll17apUuX6tWrR8To0aM7dOgwfPjwefPmjRw58vDDDy9abSSKgBAAYKd47bVIpaJp0xg3LiKiRYuf3ZegRC1fvnzSpElTpkyZOnXqrFmz0jcSEVG1atWTTz453T93/PHHZ2Zmfvfdd5MmTWrXrt3tt99evnz5m2++eSeUVxpee+21iDjnnHMiom7dulWqVDnllFPSS4yedNJJ2/8wNCMj45FHHikoKBgwYEDLli3HjBnTuHHjUqwbgGJL517bNgcqjZF3jWit9P6hgF82efLkp5566oUXXti4cWNE7L333occcsgnn3wybty4U045ZeTIkXXq1CnrGuFXafXq1cuXL69SpcoBpbCXx4033rh+/fq//OUvlSpVWrZs2dtvv138/1WzsuLee2P48CPy8qo8++yzc+fOHTlyZM1/tyfhzJkzBw8ePHTo0FWrVkXE7rvv/sc//jE7O/tfOhfPP//86dOnt27d+oMPPmjQoMFzzz139tlnF7NIdnkCQgCAneL11yMiWrSI117b+qLULF68+P3333/nnXfGjx//41DwF6KyTz75pGnTpt26dRs2bNjFF198yy23ZGVl/eUvfym9IktPOiBs0aJFRPzxj3/s0KFDkXeiysjIeOyxxzZu3DhkyJBWrVqNGzeuYcOGOzrIunXrJk+ePHHixCeffHL//fe/7777bGoIUEp0EG6nXeMq4Ffku+++e/755x955JEPP/wwIjIzMxs1anTppZdmZ2dXrlx5yZIlF1544cyZMxs2bPj3v//9wgsvLOt64ddn/vz5EVG7du3S2IHvueee+/LLL6+//vrFixeXK1euBJvz2rVrV7du3datW8+ePfvEE08cPnz4tu0t1qxZM3z48CeeeGL27Nnpr9SvX79r166XXHLJz60g+rvf/e7dd9/t2LHjyJEjzzvvvLvuuqv4WxKuXr26f//+Tz/99MEHH/z888/vv//+xRmN/zYCQnZRBQU7py0jQQoLIyIsiA9QNAUFWxsHmzWLXr0iIyNKbSrfSy+91K5du/z8/PQfd9ttt1NPPbVRo0annXZa48aNf65X4MMPP/zqq69uueWWvn37Dhw4sFOnTjfddFNWVlbPnj1Lqc5S8tVXX3300UfVqlU79dRTI6JSpUrFHDAzM/OZZ54pKCgYNmxYixYtxo0bd+KJJ/7HozZu3Dhr1qypU6eOHz/+7bffTu9cFRFr1qy57LLLpk6dmt4YA4CStRMCwm2bEZb4KXYmASHsHIWFhRMmTMjNzX3llVfSPz3233//Sy+9tGvXrj8OGA455JApU6Z069Zt8ODBf/jDH3r27Hn33XfbknAXtGxZPPtsFBRE27bx37H8YyqVGjdu3DXXXHPwwQffddddRZgN+d/j448/jtJZX3TDhg3Lli2rWLHipk2bCgoKatWqVbIt+Mccc8x777138cUXv/HGGy1atLjjjjtOO+20IUOGPPvssxs2bIiI3/zmN23atPnzn/989NFH/8fRqlWr9uKLL95zzz033XRTr169Zs2a9fTTTxdtS8KZM2fm5uZuK2PJkiVnnnnmK6+8otF5VyIgZJczcmQMHRp77x1r1sTjj4fd5k4AACAASURBVMdvflPWBe0SevaM5cujsDAOPDDuvrusqwH4FXrvvVi5MmrViqVLY/36OP74KLVpd/fcc09+fn716tWvu+66Jk2aNGzYcHse/1144YXDhg1r3759Tk5OOiPs3Llzr169qlat2r1791IqtTSMGTMmlUo1a9asBO/ZsrKyBg8eXFBQMHz48LPPPvvNN988/vjjf/q2/Pz8//mf/xk/fvz48eMnT578ww8/bDu8fv36zZs3P+KII/7+979PnTq1WbNmkyZN+u1vf1tSFQKQpoNwO+0aVwH/zZYtWzZkyJABAwZ89tlnEZGVldW8efOuXbu2bt363y5uUalSpUGDBjVq1Oiqq67q27fvhx9++OyzzxZ/S8IvvviiW7duWVlZHTp0aNeuXTFHo1jWrIkOHaJfv6hQIbp0iWHDYr/9yrCcr776avDgwU8++eSiRYsi4uOPP549e/aLL754xhlnlGFVxZHegLA0JmIuWLAglUrVqlVr4cKFUToZ5F577TVmzJiTTz45Ly/vpptuSi8ClJmZ2aJFiy5dulxwwQU7dHubkZGRk5Nz9NFHX3LJJSNGjJg/f/5LL720/bef33777aBBgwYOHJj+J83MzDzrrLOOOeaYcePGffDBBw0bNnzmmWcuuuiiIlwm/4UEhOxaNm2Khx6KCRMiIyPeeCMeeCDuuGMnnHb9+vX33HNPrVq1KlasuEMHpudfjB49et99992hA99///3LLrtsJzUfTJwYWVnxzDMRET16xNSp0ajRzjgvwC7kg3fe2bdGjX3POWfrQqPnnFNKJyosLEw/g5g4ceL2zC78sbZt227cuDGdCz788MO5ublXXHHFn//858zMzCuvvLKYhX3//Zy1a8dUqHDob35zUUQpzoZ+/fXX43/XFy1BWVlZQ4YM+f7770eNGtWiRYuJEyfWq1cv/t9QcOrUqd9///2299evXz/duHn22Wfvscce6a+3b9++ZcuWEydObNKkyVtvvXXYYYeVbJ0ACScg3E67xlXAf6GCgoKJEyfm5uaOHDkyvaTHwQcf3L59++7dux9yyCH/8fCuXbvWrl27bdu2Y8aMadiw4ciRI9OfOYtfxuuvv/79999fdtllRRiNkvHGG3HhhXHMMRERXbrEK69Et247v4qCgoIxY8Y89dRTY8aMSX9vHHrooc2aNZs1a9bs2bObN29+55135uTk7PzCii+dZpVGerdt5NI7RURkZWXts88+EbH77ruvXbs2lUqNHj36nGI8OjjvvPNmzJiR3pLwhBNOGDJkyAUXXPAL7/9px/MBBxyQnZ29reN506ZNV1555aBBg9q0aaPRedeRgl3JggWpK67Y+nrNmlSrVjvhnOvWrdv21K8IitzfkJWVNWrUqJ1wgakHH0y99NLW1//4R+qxx3bGSQF2LSeffHJGRsak1167v127t449tmDSpFI60XvvvRcRhx566FtvvXXiiSc+tuM/tJ966qmMjIyMjIzHH3+8f//+EZGRkZGbm1ucqgoLf5g/v9GGDTPXrh1fnHH+o/z8/L322isiFi1aVBrj//DDDy1btoyIvffeu0ePHmefffaPl2rJzMw87rjjevToMWrUqNWrV//cIBs2bGjcuHFE1KxZ8/PPPy+NOgESa968eRFRp06d9B8//vjjkhr5o48+ioh69eq98MILEXHRRReV1Mhlom3bthExfPjwsi4Edin5+fnbFg6tVKlS+/btJ0yYUFhYuKPjLFmyJL2mfdWqVV944YUdOnbBggU9e/bcNg29UqVKF1xwwbbVL7p27bp58+YdrYeS8fTTqb//fevrUaNS9923k8//xRdf9OnTp2bNmtseSLZs2XLEiBH5+fmpVOqTTz7p06dPerO69u3bb9iwoQRPvWHDrC++uOGbb/oVFHxfgsP+i6OOOioiZs6cWeIj33rrrRFx0003pSP2AQMGlPgp0tJNfh9++GHFihUzMjLWr19f/DHXrl2b3tY03Vb4b38iLV26tE+fPttmr6Y7nkeMGLFly5afvnnAgAHpCUbnnnvud999V/wKKVs6CNm1VK8ey5dvff3NN1Gjxk4452OPPbZmzZry5cufe+65O9pBOGbMmA0bNpx33nk7uhL02LFj169f379//1atWu3QgUWx997x7bdbX3/zTemtiQewq1q1atV7771XoUKF/Q877IYRI6pWrbry1FNLaaLda6+9FhHnnnvu2LFj8/LymjRpsqMjdO7ceePGjddcc0337t0HDBjwwAMPXH/99X/605922223Sy65pAgl5ecvz8r6TUHBusLCDdWqNSvCCNvv3XffXbVqVZ06dUpw0/gfq1ChwgsvvHDeeedNnz79wQcfTH/x8MMPb968efPmzZs2bVq9evX/OMhuu+32z3/+86yzzpoxY8ZZZ501adKkAw44oDSqBUigHzfGjRgx4pJLLunevfvtt99enDmd/zLyTui9KyhYvXHj+5Uq/a58+dL6BaGDEEpDVlZW48aNK1as2LFjx86dO++9995FG+fggw+ePHnyDnXq/PDDD6NGjcrNzX3zzTdTqVRE1K1b98dl5ObmXn311bm5ufPnzx8xYsSOLmRFCWjYMO65Jzp2jIgYNy7++Medc9r098bgwYPHjh1bUFAQEbVr1+7UqVOnTp3S/WoR8cgjj9xwww2PPPLIyy+/nJ2dPWzYsLlz544cOfLQQw8tkRqWLLnq8MOfKyhYm5m5Yw9Ot19hYeGiRYsyMjJq1apV4oNv291w4sSJUWodhJs3b168eHFWVlZWVtYPP/xwyCGHFG3jwH9RrVq1F154Ib0lYd++fefNmzdkyJDdd989/l2rca1atdq3b3/55Zf/QsfztkbnsWPHNmjQYOTIkelotsi2bNkyatSoHj16FBYWtmnTZtuNNjtJWSeUUNIuuyz19NOp995LXXRR6t13U6lUqjTnRn3zzTfpW83XXnutCIenp2YUocth5cqV6UeQpdtEmJ+fKixMrV2bato09eabqXHjUs2apUp0DhFAEgwbNiwizj777CeffDIiWrduXXrnOu200yLi5ZdfPu644yIi/YCgCNIfyrOysoYOHXrvvfemXz/33HPbefiWLctXrx61dGnO3Ln18/IyNm9eumnTwqVLcxYsaFZYuKloJW2PW265JSKuu+660hj8tddeu/nmmz/88MN+/fpFRK1atYYPH/7NN98UbbTVq1enJ4bXrl37q6++KtlSARJrxIgREVG+fPnPP//8rrvuSm/0tf/++w8dOrQITTz/YvPmzWvWrBk0aFBEdOjQoUQK/qmCgvXz55++fHnu11/fX0qnSKVS6Yb4f/7zn6V3CkimjRs3luBoP+7UWbVq1b99z5w5c3JycrZNU6tcuXKbNm3GjRv303dOmTJl//33j4iDDjpoxowZJVgn2+uxx1IdOqQ6dkz17bsTzjZv3rycnJwa/9s+UalSpfT3xk9/Ifbo0SP9nj/96U8ffPBBnTp1IqJ69erjx5fMAjDz55+6cuWQUm0fTG+0ceCBB5bG4CeccEJEvPPOO+n/0Urp9m3u3LkRccQRR4wePToimjdvXrLjjx49Or2taZ06dR599NGcnJxtEwUqVqz4c98bP6c4jc7bzJ8//8Ybb/yX+QrXX3/9v+1cpJQICNnlFBSkXn459fjjqQULUt98k8rOTp1/fumd7fLLL4+ICy64oGiHFzkgTKVS6aeTv/3tbzdtKrUnrfffnzrppNTMmamVK1PPPJMaNCj18wumAfBzOnbsGBH3339/eh/v0luQJN3RXr58+Y8//jgjI6NKlSrF+R1x3333bcsF08Fb+fLlX3nllZ8/+3fffffKkiXXzplzTF5eRl5epP+bNavq2rVbc8rFi6/auPH9wsLS+rjfoEGDiBg7dmxpDJ5uoOzfv//vf//7iBg4cGAxB/zuu+/St5pHH3308uXLi1vfU0+lOndOXXFF6sMPU6lU6pZbtn599OjUO+8Ud3CAX4lVq1ZVrVo1IqpVq3bffffNnDkzPXUmIho3bvxh+idkMbzzzjv16tU78MADq1ev/uqrr5ZIzf9i8+av5s8/vaCgBFYV+wXpzXqLNs8V2JneeuutdJvXEUcc8eMfYmvWrBkwYECjRo22PVWvX7/+gAED0luX/Zwvv/zy5JNPTmdFzzzzTPHLW7t27S233FKnTp3zzz9/3bp1xR9wV9a2beqaa1LffZcaPz71xReld57ly5cPHz68efPm6fVCI+LII4/s06fPypUrf+GooUOH7rbbbhHRqFGjBQsWpG95ypUr16dPn6KVUVCwfvnygfPnN96yZXl+/qpvv31k3ryTt2wp9l3Pz0gvpdO0adMSH7mwsLBatWoRsXDhwojYfffdS/wUaSNHjoyI888//4EHHoiIq666qsRPMX/+/Lp16/44jTvmmGMeeuihn5t/8Mu2bWuaXry0oKBgOw/ctGnTiBEjfvwtWrdu3d69e99www3prbgaN2789ddfF6EkikBAyC7q++9TffumFixI7blnKiI1enRpnGTWrFmZmZkVKlRYsGBB0UYoTkC4ZcuWdAf3PffcU7Sz/wfffLP1X2/MmFSfPqkvvyyVswDs6goLC9MTdT/44IP0fL3S23bu+eefj4gmTZo8/fTTxZm/ss3//b//N50LvvzyyzfddFNEVKhQ4cfdBuvXrx83blxOTk6jRo0qVqwwe3aN/w0FK8+f32jp0pw1a8YVFm7Kz1/16acdPv20/aeftt+48aMPP6y1bt1bxaztp5YvX56ZmVmpUqWS3TAjraCgIP1oZs6cOen1WJYsWVL8Yb/99tt69epFxLHHHvvLd+z/wciRqRtvTKVSqZUrU2eckdq4MXXWWVv/6tFHUyNHFr9UgF+LGTNmtGnTJv28qXbt2q+//vqgQYPSk9PLlSt3zTXX/PLT858zb9689NPSdPqYfvH73/++ZH+tFxSsS6VSK1c+9/HHZ3/55V9TqVQqVdzGx5+aN29e+sNJz549i99YCZS2JUuWpKfBVa1adfDgwdOmTevatWt6MkRE7Lnnnl27dp09e/Z2jrZp06YrrrgifWxxtiTMy8v7cRkRccoppyxbtqxoo+36Vq1KRaSqVUvNnJmKSNWrV0rnycvLS3fPp783unfvPmvWrO08dtasWek1RQ844IBp06b16dMnvbDtxRdfvEN3WOvXz/j8866zZ++evjf85psHU6mCVCr15Ze3rF07/osvbti06ZOiXNsv6t+/f0RceeWVJT7y0qVLI6JGjRpTp06NiAYNGpT4KdL69OkTET169Ljyyisj4qGHHiqNs3z33Xd169atVq3aueeeO3369OIPuD2Nztv8S8dztWrVsrOzf9zxrNF55xMQsovq2DEVkerePXX//amI1BFHpEq6za6wsPD0009P31MVeZDiBISpVGr8+PHpH6al8gnsiitSEanzz0+99FIqIlWzZkp/N8COmzVrVvrT7dtvv52evFl65+rSpUtE9OnTp127dhHx6KOPFn/MbbngqFGjevbsmX591113/eUvfzn55JO33XxGRMWKFadM6bhsWe916976hXVEv/iiR15ezJ69+/r17xa/vB8bOnRoRJxzzjklO2zajBkzIuKwww6bMGFCeqJlSY38zTffHHnkkRFx/PHHF2Xm5g8/pL7+OvWnP6Xmzt36lZ49U9Onp048MfXww6mHH061bSsgBBLozTff3DZHvmXLlnPmzLnmmmuysrLSzz0HDRq0/UOtWLEiJycnPaW9SpUqOTk5q1at6tevX3qzicqVK996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pUvlCxe2s7PLWskjACBTYmNj1QU0pyeSLXPnzhWR4cOHjxgxQkRmzZplrpEfPnyobmmpJU1z5swx18gAYBbx8fHvvfeePLtSJz09PTAw0N3dXa2MF5Fy5cp5e3s/qz2Yr6+vujn21ltvmaUl4YMHDwwGg52d3ZkzZ0SkatWq2R/zqebPny8igwYNmjBhgoh89dVXZj+FamlRp04ds49skpwc9eefw//4o4/aVMZckpKSBg8erF4AXl5eqte7em3079/f1Me3TJky3t7eFy9eNOOpM2TaNG3z5kePFy/Wlix52ROApml/tyRUt1xq167dtWtXtem6iLzyyivjxo0Lz2R2u2XLFtXDMrOFzorRaNyzZ0+fPn1M06hcuXJamhkqax9nk7UUAch1rKyshgwZcuzYMVtb2927d6vW6xmXlpZmNBrV7cV/ufHjxw8dOvThw4em944MSkpK6tSp07lz55o1a6buAgMALM1gMMybNy8hIWHFihVdu3YNDAxUldwi8uDBgyNHjuzatWvXrl0nT540Go3q5/nz569fv36LFi1cXV1btmypFoWMGjWqR48emzdvbteuXVBQkFpIa0bnzp27evVq6dKl33jjDfOO/A+pqTFxcdsdHGoUK9YzO+NUrFgxMDCwVatWBw8e/Oyzz3777beOHTsGBga+9957GzdutLe3z+A4jRo1Wr58uekzgApiLWLvXtuLF2sVLy7Hj0clJNzs3du0sw0AwHJUD0LTXeNcyvQsUlJSxKxPx7QFX974QwHIexwdHdetWzd16tTPP/98/PjxJ0+eXLRoUYECBUQkOjp6xYoV8+fPv3LliohYW1u7urp6eXm99957KgJ8Ki8vLycnp549e27durVRo0YbNmwwFR1mzaFDhzRNS01NdXFxEZHM3pDMONWZvlq1aseOHbPQicLDwy00somdXfmKFeeafVh7e/tFixY1btz4448/XrBgwe+//96wYcM9e/ao14aNjc3bb7/t6enp5uZmbW1t9rO/WL58kpDw6PHDh1KqVA7MAX+vM6hXr97AgQNDQ0NDQ0NVqbGXl9e7776bhU9Bb7311tGjR997770zZ840bNhw1apVat+gF7p+/frSpUsXLlx48eJF+bviWU3D7C9RAkLohdFoXLRokYh4e3s/3mY2Z7m7u8fGxqr9S82oaNGiRYsWzcIv+vr6uri4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AAB0hB6EAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAAAAADpCQAgAAAAAAAAAAADoCAEhAAAAAAAAAAAAoCMEhAAAAAAAAAAAAICOEBACAAAAAAAAAAAAOkJACAAAAAAAAAAAAOgIASEAAAAAAAAAAACgIwSEAAAAAAAAAAAAgI4QEAIAAAAAAAAAAAA6QkAIAAAAAAAAAAAA6AgBIQAAAAAAAAAAAKAjBIQAAAAAAAAAAACAjhAQAgAAAAAAAP+PvXuP87nOG///mgOGGGaiknWqKzrYXFYOra7alrZWMarVVToSUde1lbSJ5NxECdeqdlHJtqVSLEq1m9pqK1JMixyLZCXsjMM0yBx+f8z+fG2HXYfPGHnd7390+/jM5/N6P+evaebxeb3eAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAABARARCAAAAAAAAiIhACAAAAAAAABERCAEAAAAAACAiAiEAAAAAAABERCAEAAAAAACAiAiEAAAAAAAAEBGBEAAAAAAAACIiEAIAAAAAAEBEBEIAAAAAAACIiEAIAAAAAAAAEREIAQAAAAAAICICIQAAAAAAAEREIAQAAAAAAICICIQAAAAAAAAQEYEQAAAAAAAAIiIQAgAAAAAAQEQEQgAAAAAAAIiIQAgAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAAREQgBAAAAAAAgIgIhAAAAAAAABARgRAAAAAAAAAiIhACAAAAAACHQkFBQVFRUXlPAQiEAAAAAABAWSooKJg1a9Y111xTrVq1o446asSIEeU9EcQuqaSkpLxnAAAAAAAAjjSbNm2aMWPG9OnT58yZs2vXrj3Pp6amTpgwoWvXruU4G0ROIAQAAAAAABLms88+e+mll2bNmvXKK6/s3r07hJCcnHzmmWd26NAhKyvr2WefHTRoUFJS0sCBAwcPHlzew0KkBH/gPp4AACAASURBVEIAAAAAAOBgffLJJ7NmzZo6deo777xTmh5SU1NbtWrVuXPnyy67rHbt2iGENWvWvPfee19++eUNN9xQWFj4y1/+cuzYscnJ7oYGh5pACAAAAAAAHKAlS5ZMnTr1hRde+OCDD0qfqVy5ctu2bTt37pyVlVW9evU9rywoKGjWrNnKlSsfeOCBhg0bXnHFFTt37rzyyisnTZpUoUKFchofIiUQAgAAAAAA+2fx4sX/+7//u2bNmk8//bT0maOPPrpDhw4XX3zxz372s7S0tG9914MPPnjLLbcUFxfffPPNnTp16tSp07Zt29q2bTt9+vRq1aodwvEhdgIhAAAAAACwf4499tiNGzeGEGrVqnXBBRd07tz5ggsu2JeNgE899dR11123e/fua6655tZbb73ooovWr1/fokWLF198sVatWmU/OBCCQAgAAAAAAOyXzz77rF69eqmpqX/4wx/at2+flJS0X2+fM2fOxRdfvH379g4dOowcOTIrK2vlypUnn3zyK6+8Uq9evTKaGdibO38CAAAAAAD7Yd68eSGEdu3aNWjQ4MMPPywsLNyvt7dt23bOnDm1atWaNWtW165dZ8yY0axZs2XLlrVu3fqvf/1r2YwM/BOBEAAAAAAA2A+lgbBVq1b3339/s2bNJkyYsL8rtGjR4s0336xXr968efN+8YtfTJky5bzzzvv8889/8pOfvP3222UwMvBPBEIAAAAAAGA/7AmE7777bgihdevWB7DIySefPHfu3KZNm3700Ufnn3/+Aw880Llz57y8vJ/97GcvvvhigicG/pl7EAIAAAAAAPtq9+7dNWrU2LFjx4oVKxo1apSWlrZ169YKFSoc2Gp5eXkdO3b8y1/+kpmZOXPmzCeeeGL8+PEpKSnjx4+//vrrEzs5sIcdhAAAAAAAwL7661//WlBQ0KhRoxUrVpSUlLRo0eKA62AIISMj49VXX73kkktyc3PPP//8rKysAQMGFBUV9ejRo3PnzgkcG9ibQAgAAAAAAOyruXPnhhBat25detDogZ0vurdKlSo988wz11577ZdfftmpU6emTZuOGTOmpKRk2rRpGzZsSMDEwDcIhAAAAAAAwL7acwPC0lLYqlWrg18zNTV10qRJgwYNSk1NrVOnTo8ePZKTk0MIFStWPPjFgW9yD0IAAAAAAGBfNWrUaOXKlR988EG7du3y8vLWrVtXp06dRC3+6aef1q9f/7XXXmvbtm3Lli1LYySQcHYQAgAAAAAA+yQ3N3fVqlVVqlSpWLFiXl5e3bp1E1gHQwj169cPe21STODKwN4EQgAAAAAAYJ/MnTu3pKTkjDPOmD9/fkjEDQi/lUAIZU0gBADg38vOzr7iiiseeOCB8h4EAACA8lSa7lq3bl2mDW/PVcpicSAIhAAA/Gtbt27t2bPnXXfd9fTTT99+++3NmjWbOnXqkXEf62uuueZ3v/tdYWFheQ8CAADwvTF37twQQqtWrUoflEXDW7169YYNG2rWrHnCCSckfHGgVNKR8ccdOPS++OKLTp06vf/++w0bNkxPT8/IyEhPT69evXp6evqeBzVq1KhevfreT1atWrW8BweA/fDKK69079593bp1lStXrlu37pYtWzZu3BhCaNas2d13392pU6ekpKTynvEAzZkzp127dkcfffTq1aurVatW3uMAcFgYOzbcemsIIeTkhK1bwznn/OP5V14JeXnh8svDli1h1qxw9dXlOCMAlKeSkpKjjz46Ly9vxYoVp556alJS0tatWytXrpzYq0yZMqVLly4dOnSYOXNmYlcG9kgt7wHg+6p3796ln5FZuXLlvr8rOTk5PT19165dd999d79+/cpsun9SXLxj+/bXUlOPqVKlWX7+m9Wq/TSEsGvXqqSkShUr1j00MwDwvbNt27Zf/epXEydOLCkpad269aRJkxo1avTxxx+//vrrQ4cOXbhw4SWXXHLaaafdcccdV155ZUpKSnnPu9+GDRsWQujTp486CMAeL774j0C4enX4/PP/FwhzcsITT4Q2bUJycnjrLYEQgHgtW7YsLy+vbt2669atKywsbNGiRcLrYHADQjgkBEI4EO+8884zzzyTmpo6fvz4Nm3abNu2bevWrVu2bNm6deu2bdtK/1n6IC8vb+8nCwoKtmzZEkK46667srKyTj311EMw7erVV1Sv3uGrr9ampf3Hhg0jSgPh9u2vpaRkCoQAfKu33nqrW7duq1atSktLGzx48O23356SkjJ16tTLL7/80ksvffnllxcsWDB06NAlS5Zce+21Q4YM6du3b7du3VJTvzf/b/nOO++88cYbNWrUuPHGG8t7FgAOI4WFYdq0EEKYNy/Ur/9PX/rVr8Idd4RRo8plLgA4XOw5VrTszhfd+yplsThQ6nvzRxw4fBQUFFx33XXFxcUDBw7s1q3bTTfd9OSTT37zcNHMzMzS00e/9qWqVateddVVr7322n333ff4448fgoF3795Qtep/paU1CiGUlBQXFv49hFBU9GVKSuYhuDoA3y8FBQVDhw69//77i4uLW7Zs+fjjj59yyimlX1q2bFlycvLUqVOnTZt2+eWXT58+feHChcOHD1+5cmXPnj1HjBhxyy239OrVq1KlSuX7LeyLQYMGhRB69+5do0aN8p4FgMNLWloIIVSs+PXnTzwxNGoUZs8OIYTPPgs1a4Yy2C8BAIe7PXv7Shtey5YtE36JXbt25eTkJCcnn3HGGQlfHNjDPQhhv/Xp02f06NGnn376/PnzK1as2KVLlylTpuz72/Pz8zdv3tyoUaOioqIlS5Y0bty47EYttWPHX7/4YlRR0fYGDR5fseKn6ennhRAKCj6sWfP6jIxflPXVAfgeefvtt7t27bpy5coKFSrcdtttw4YNq1Chwt4vWLNmzZgxYyZMmLBz587k5OT27dsPGDBg+fLl2dnZy5cvDyHUr1+/d+/ePXv2TCv98+phae7c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fvz9998fQqjttG4oe6nlPQAc7oqKiq699tpdu3bdeOON7dq1S/j6/fv3nzRp0jPPPNOvX78f/vCHB79gcXHBmjXXhVBcu/bAKlWaHfyCAByRSrf35eXlbd68+bnnnisoKCijSxwmOwhXrVo1derUChUq3H777eU9CwBHuDPP/MeDW28NF14Yzj77H//86qsQwj/2ER6MjRs3fvrpp5UrV961a1dRUVHpDYDz8/N3795dWFi4ffv2EML27dsLCwt3796dn58fQti2bdumTZtyc3NHjx7dtGnTg50AAPZy00031a5du0uXLr/+9a9zc3Mfe+yxChX+acdCcXHxwoULZ82aNWXKlBUrVpQ+mZGR0a5du4suuqhFixZ//OMfn3jiieuvv76wsDCEcPTRR2dlZZXDdwKREQjh3xg1atT8+fPr1as3cuTIsli/bt26119//cMPP5ydnT1lypSDX/Dll8ced9z6ypVPP+64fge/GgBHqtLtfbm5uXtKYRld4jDZQThs2LDCwsIbbrihfv365T0LALGoUCH07x+uvDK0ahVCCB06hCpVwrx54YUXDnzNp59++vrrrz/gT/a0bt168+bNRx111IFPAADfcPHFF8+ePbtTp06///3vN2zYMG3atGrVqoUQFi9ePG7cuBkzZnzxxRelrzz++OOzsrIuueSSWrVqzZo16//+7/8WLFhQ+qW0tLQLLrjgxz/+8X//93//4Ac/KLdvBqKRVFJSUt4zwOFrxYoV//mf/7ljx46XXnrpggsuKKOrfP755yeeeOKuXbtycnIOchPh3LlzzzrrrJYtfzB79rQaNX6UqAkBOPIUFRVVrFgxhLB48eJTTz21cePGy5YtS+wl1q9fX6dOndq1a69fvz6xK++vTz75pHHjxklJScuXL2/YsGH5DgNAJHr3DmPGhBDCrbeGK64Izz0Xhg8PlSqFJUvCX/4SevY8wGV/9KMfLVy4MC0t7T/+4z8qVaqUnJxcvXr1EMJRRx1VsWLFlJSU9PT0EELVqlUrVKiQmppa+vfZatWqbd++/YEHHti0aVPfvn1HjBiRqG8TAPb44IMP2rdvv3HjxhYtWsyePbtmzZqvvvrqeeedF0Jo0KBBx44dO3fuXL169eeff/7ZZ59dunRp6buqVKny05/+tHPnzp06dSr9KQYcGgIhfKfCwsKWLVsuXLiwW7dujz76aJle65e//OWDDz542WWXPfPMMwe8yK5du5o3b75kyZJ+/fol8FZSABypMjIytmzZsmLFikaNGh1zzDF7PtGZKDt37qxcuXJaWtqOHTsSsNzMmeG550JycmjdOvTq9a9fu3Xr1tzc3Ly8vLy8vNzc3P79+69atapr166PPfZYAiYBgH3z+9+Ht94KN9wQmjdPzIIvvPBChw4datasuXjx4mOPPXa/3pubm/vxxx+feeaZSUlJ77//voNGASgLn3zyyfnnn79q1apTTjnl5Zdfrl279n333XfhhRfm5+e/8MILzz///KpVq0pfmZmZeeGFF3bo0KF9+/a2tkO5EAjhO02aNKlbt25VqlRZt25dRkbG0qVLTznllDK6Vukmwp07d+bk5Jx++ukHtkj//v3vvffexo0b5+TkpKWlJXZCAI48J5544ieffLJ8+fKTTz45JSXlq6++SkpKSuwlqlSpsmPHjoKCgsqVKx/UQmvXhl/+MkybFlJSwo03hi5dwvr14W9/C3l5ITc35OXtedBox46P168vLi7e+93JycklJSV//vOfz95zGygAKHtduoQpU8KTT4YuXRKwWnFxcfPmzXNycsaOHVtQUNC0adP27dvv43uff/75G2+88e677162bNnDDz981llnvfnmmwn/uQ8AIYQNGzb8/Oc/z8nJqV279l133bVgwYKZM2du3ry59Kt169bt1KnTxRdffPbZZ6ekpJTvqBA59yCE71S63SElJaVChQrnnHPOu+++W3bnktWuXbt79+7jxo0bNmzY1KlTD2CFnJycUaNGJScnP/LII+ogAPui9O6D27dvP+qoo/Lz8/Pz80tPIUvsJXbs2JGbm1unTp2DWmjevHD++aH0t8df/CK88UZ48snwbWeiVjvuuOLi4urVq2dkZGRmZmZkZGRkZBQWFvbq1UsdBOAQq1QphBB27UrMapMnT87JyWnQoMFPfvKTM844IykpaeXKlft4b90qVaps2rRpwIAB77333h/+8Ie//OUvkydPvu666xIzGQDs5bjjjnv99dc7duz41ltv3XrrrYWFhSGEhg0bdujQoXPnzm3atPEJFThMCITwnXr16vXoo48uWLBg0qRJDRo0ePPNN7OzsydOnFhGl7vzzjvHjx8/a9asE044ITMzs3r16tWrV09PT09PTy99UL169Ro1anztydKDuQsLC6+//vrdu3ffdtttZ511VhlNCMARJjMzM4SQm5ubmZmZn5+fl5eX8ECYmZm5fv36vLy8gw2EaWlhzzmlO3aEtLRw9dXh738PGRkhIyNkZu558FpGRrXMzOTk5IMfHgAOUgID4c6dOwcPHhxCuOeeewYNGlRYWPg///M/+1gHQwg///nPO3bsOHPmzCFDhtx3331XXXXV7bffftFFF9WsWTMBwwHAP6tRo8aUKVPq1q2bnJw8ZMiQSy+99LTTTivvoYCvEwjhOyUnJw8cOLBTp0733nvvn/70p6eeemry5Mn9+vU74YQTyuJyW7duDSGkpaWtXr169erV+/7GjIyM1NTUTZs2NWzYcNiwYWUxGwBHpNIdhHl5eRkZGWvXrs3Nza1Xr15ZXCI3N/dgFzr77DB6dOjSJRx1VPjtb8OYMeGkk771hdUP9koAkDAJDIS//vWv165d27Rp0wYNGsycObNq1aoDBgzYrxXGjRv32muvTZky5eqrr27btu2cOXP69+8/YcKEBAwHAN+Qk5NTUlJy5plnDhw4sLxnAb6dQAj/SlZWVosWLebPnz9nzpwrr7xy8uTJ99xzz6OPPprwCxUXF/fo0eOrr766/PLLBwwYsG3btry8vK1bt27btm3btm17HmzZsmXLli17P1m64SOEULVq1ZYtW1apUiXhswFwpNp7B2EIofQHSllcIgErV6oUJk4Mo0aF3bvDwIHfVQcB4LCSqECYl5c3cuTIEML9999/xx13lJSU9OnT57jjjtuvRerVqzdgwIA777zz5ptvnj59eosWLR599NHrrrvuxz/+8cHOBwDfMG/evBBC69aty3sQ4DsJhPBvDBgwICsrq3QT4ZNPPjl58uS+ffs2atQosVcZM2bM22+/ffzxx48dO3bTpk3nnntu+v+v9KzRGjVqpKen/+AHP9j7iNGMjIyqVaumpaUtWrSoY8eOM2bMWL9+/fHHH5/Y2QA4Uu29gzAkZJ/fd1ziYFcuKQk//nE48cTw0EPhmGMSMxkAlL02p3689ZKvmtcrDuGgzlXLzs7Ozc0999xz8/Pz33777Vq1at12220HsE6fPn2mTJny4YcfTps27fbbbx8+fHivXr0++OCDChUqHMx4APBNpYGwVatW5T0I8J0EQvg3OnbsWLqJ8NVXX7366qsnTZo0YsSIxx57LIGXWL169aBBg0IIDz/8cEZGxsqVK7dv3759+/a//e1v+/L2YcOGDRgwoFOnTtOnTx81atTo0aMTOBsAR7A9gfBw30E4c2ZYuDBs2BDS0xMzFgAcElknTMk6/e5wzF0hDD/gRf72t7899NBDSUlJI0eOvO6660IIgwcPTj+gn4mpqakPPvjg2WefnZ2dPW/evClTpixatGjcuHEHlhsB4LuUlJTMnz8/CIRweBMI4d8bOHBghw4dRo4c+frrrz/55JO/+93v7rzzzkRtIiwpKenRo8eXX355zTXXZGVlhRBatmy55xzRPUeJbtmypfTB3ueObt26dcuWLbVr1w4hDB06dMaMGb/5zW/69OlTp06dhMwGwJHta0eMlt0OwoMNhPfcE0II/fqFtLREDAUAh0pqpRBCKDqoM0b79++/Y8eOK6+8cuHChR999NEJJ5zQvXv3A17trLPOuvbaax9//PE77rjjoYceuuCCC+6+++5LL720fv36BzMkAOxt6dKleXl59erVc9QZHM4EQvj3LrroopYtW7733nuzZ8+++uqrH3300ezs7Mcffzwhiz/88MNz5sypVavWqFGj9jxZeqzofq3TpEmTTp06TZs27f777x87dmxCZgPgyLan3p144okhoTsI//znP+fl5V188cUJOGJ09uwwf3449thwEH8MBfj/2LvvuCrr/o/j78MGlZEbK8utiCNSUTG3oqKG+zZXaqJmtkw0d1qusmHm3rkSRXCTmgMVzJlbHFEOxIEgKMg4vz/w9u7X3V0Ch0iv1/OPHhfXua7P93N8aDzOeV/f7xfIG9b2kpSW/YDw+PHj3377rZ2d3fDhw319fSVNmjTJzs4uJ019+umnGzZsCAsL69OnT4cOHYKCgt5///2goKCc1AQA4LciIiLEBoTAP55VXjcAPBkylwCdOHHie++9Z2dn9+233549ezbnZaOjo4cPHy5p5syZhQsXzmG1jz76yMrKavbs2Y+5NikAwOB+N4PQIgFhUlLS4MGDGzVq1Lt376tXr2YugBYdHZ39ip98IkkffCBHx5y3BwDA3yrHMwgDAwMzMjIGDhy4bt26y5cv16hRo0OHDjlsqmDBgp988omkt99+e8KECS4uLmvWrNmwYUMOywIA8Mj58zUaNBjTsGGXvG4EwJ8hIAQeS8uWLWvVqnXz5s3Nmzf36NEjPT3948zlznImICDg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a1THx8vFRYmJDx/lTExUx47askWVK2vLFpUo8XgVZ83SgAGyttbq1dq9W8eOad26nH/q27FjR+PGjWvWrBkZGTl69Ojx48dLcnJyatSoUceOHV999dUsrRd6+/btVq1aRUREFCtWbPPmzdUyl1gFnkwEhMBjuXz5/atXp+/Y0eG99xbZ5fjRlcWLF/fq1atMmTIrVqyoWbOmg4PD+fPns/QL+A9dvXq1cuXKcXFxK1asCA0NXbFiRcOGDbdv326pJ0wBAMgty/0UtVH1PlT6A+37VBVeVefgP7wwPFxjxmjHDkkqUEADB6pLF507p06dJGn1ajVooOPH1aiRJAUFyd9f1tZ/29sAADwljh9XtWqysdGpUypdOptFki8suvvrQvsijWQ23U38waVYj/wlX892S0uXLu3Ro0ehQoXOnDlTsGDBbNfJlPn1qLe39759+/jACABPgStXrtStWzc6OtrPz2/06NFNmjQpV67ctm3bXP5r07579+4dOnQoMjJy//79kZGRV65c+e2rvr6+GzduzPacwt+6dk0tWujYMZUsqa1bVb78X93w3Xf6179kNmvBAl25opEj5eCgH36Qt3cOO/nkk09GjBgxePDgL7/88vjx41988YW/v3+TJk0cHByyVzApKal9+/Zbt251dXUNCQl55ZVXctghkFcs8E8deOolJUXGxn5pY2MePPi9nKeDkrp161a+fPnz58+fOnWqTZs29+/f/+yzz3JeduDAgXFxcX5+fvnz51+xYoWTk9PcuXP5sAcA+KeLOaKoTbLNp2o9dXC2JNX78H9d6+Oj7du1Z4/8/HT3riZPVmio3n5bR45I0sKFionRhg0PL160SGlpf8MbAAA8bYYOVUaG3nwz++mgpESXtF8K7I7LdznBJSHGZmeSw+2ctNS9e/dGjRrdvHlz5MiROakjKTY29osvvpD06aef8oERAJ4C8fHxrVq1io6O9vb2njhxYuvWrRMSEsqVK/doBuFvOTk51atXb8iQIWvWrLl8+fKVK1dCQ0PHjBnTqFEjSVu2bNnw6ANVzhQvrp075eOj6GjVqaN9+/706p9/UMxHKlVYkycrNVUjR8raWkuX5jwdlBQZGSmpVq1akjw9PefPn+/n55ftdFBSvnz5QkNDu3TpcufOnWbNmgUFBeW8SSBPsMo88BfM5pTo6D5mc3qxYh86Ob1s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AAAA/lpAQMCcOXPGjh07ZsyYXBoiLi6uTp06P//8c3Jy8qOTpUuX9vb2rlWrlrd3nWrVvGz/YjagPvxQEyfquee0d6+yuknfqVPy9dWQIdq+Xd98oxIl9MUX+te/dOGCgoMVHKwLFx5e6eqq118/WrfuhRYtWjg5Of2uzoMHD3r27Lly5Up7e/ulS5devXr1vffey8jICAwMnDRpUtZ6kiSZzebChQvfunXr7NmzHh4eku7cuZPvMcNP4B+GgBAAAAAAAAAAgCfDli1bWrRoUbVq1aNHj+ZG/fv37zdr1iw8PLxChQojR448ceJERETEwYMHExMTMy+oWPHl8+d/rFJFdevKy0teXvLweHjv/PmqX19lyujQIaWl6auvNHKkKlbMThsJCXJ21ssvq2JFLV2qgAA995xGjXr4atGiattW7dqpYUPZ2f1ZHbPZ/MEHH3z22WfW1tYzZszIly9f7969U1NTe/XqNXfuXBsbmyx1df369ZdffjkjI2PFihX169d/6aWXMnciBJ5EWfvbDwAAAAAAAAAA8krjxo3d3NyOHTt27ty5cuXKWbZ4enr6a6+9Fh4e/uyzz4aFhT3376l/6enpmUlhREREUlKFM2d06JAeRWPu7qpVSy1bKjhYW7dq1SodPy5HRy1blv1OnJ0lqUQJlS6tjRslqXFjLVggf3/5+6tOHVk93v5pJpPp008/LVy48PDhw/v37x8YGLh27drOnTsvWrQoLi5uxYoVjo6Oj99V0aJFf/311zt37syZM0esL4onHAEhAAAAAAAAAABPBltb25YtWy5btiw0NHTIkCEWrGw2m/v16xccHFyoUKHvv//+ud8sDGptbV21atWqVasGBARIuntXx45p716FhysyUlevKjhYdnaysVG9ejnKBf9bYKDatJG7u559VhcvZrtIYNGiRd94443JkycnJiZu3769devWISEhLVq0CAkJcXFxefxS8fHxmzdvHj16tKRixYplsyHgH+DxQnYAAAAAAAAAAPAP4O/vLyk4ONiyZQMDAxcsWODk5BQaGlqhQoU/ubJAAfn4KDBQ69fr+nWdPavFi9WvnyQNGKClSxUfb7GuHB31zjtasyandXr16rVmzRpHR8cZM2ZMmTIlc37krl27fHx8rly58pe337p1a8mSJa1bty5atGjXrl1TUlLy58/fvn37nLYF5B32IAQAAAAAAAAA4Ilx7969woULJycn//rrr+7u7hap+fXXX7/11lu2trahoaG+vr7ZK9K2rUJCtG+fXntNkyapc+ecdvXNNxo4UJImTNCbb8rNLacFd+3a1bZt2/j4+EaNGn311VcdOnQ4c+bMiy++uHXr1rJly/739b/88ktwcHBwcHB4eHh6eroka2vrevXq1alTp2PHjtWqVctpQ0DeYYlRAAAAAAAAAACeGE5OTs2aNVu3bl1ISMiAAQNSUlKioqIqVapk9Zj78v2X5cuXv/322yaTad68edlOByUVLSpJdeqobVvlz5/tMg+lpurIkYfH1ta6ccMCAWH9+vXDw8N9fX137NjRq1evkJCQ119/PSoq6nczqS5evLh+/frVq1fv27cv8yV7e/uGDRv6+fl17tyZlUXxdCAgBAAAAAAAAADgSeLv779u3brg4OABAwZERkbWr18/f/78VatW9fLy8vHxqV+/fpEiRR6z1LZt215//fWMjIxp06b16NEjJ11VrPjwoEcPJSbmpJIkmc26du3h8c2bSknJacFMlStXDg8Pb968+cGDB1u0aBEcHGxnZ1euXDlJJ0+eXL169erVq0+dOpV5saOjY+PGjTt27Ni2bdssbVUI/POxxCgAAAAAAAAAAE+SO3fuFC1a1Gw2X7lyJSIiYvDgwT///PNvLyhVqpS3t7e3t3etWrWqV69ua2v7h3UOHDjQuHHjxMTEESNGTJgwIYddNW2q77+XpDVrdPOmAgJyVO3BA5Urp1atJGnfPi1ZIk/PHDb4H9evX2/ZsuXhw4eLFi3ao0ePBw8erFu3Ljo6OvPVggULtm7dul27dk2bNnVwcLDYqMA/CQEhAAAAAAAAAABPmIoVK545c6ZEiRL9+/f39vYuX7782bNnw8PDDx06tHfv3ri4uEdX2traVqlSpW7dul5eXl5eXh4eHpnno6KifHx8YmNje/TosWjRIpPJlMOWGjTQW29JUkSEypSxQED46qsKDpakwED16WPJgFBSQkJC27Ztd+7c+ehM4cKFfX19O3bs6Ovr+78iVeCpwRKjAAAAAAAAAAA8YQYNGjR48OArV66MGjUq80ypUqXq1q3bpEmT0aNH29raHjhwICIiIjIy8syZM4cOHTp06FDmZVFRUWXKlLly5UrTpk1jY2Nbt249f/78nKeDkkwm5csnSZaadGdlJXt7SbK2tkzB33J2dg4JCWnbtu2pU6f8/Pz69etXs2ZNi/w5AE8EZhACAAAAAAAAAPDkOXDgwPHjx48fPx4ZGXnkyJGU32zT5+zsXKNGjcwlRitVqnThwoXIyMiIiIgLFy6cPn369u3br7zyyqlTp2rXrr1t2zYnJyeL9GPZJUbT07Vqlbp2laStW1W9uh57X0UAf42AEAAAAAAAAACAJ1taWtrZs2f37t2bucro6dOnf/vlf/Hixb28vHx8fOrWrVu6dOkOHTrs27evcuXKu3fvdnNzs1QP772nadMkadcuxcerTRtLFQZgeQSEAAAAAAAAAAA8Va5fv565vuj+/fsPHjyYmJj46CWTyWQ2m1944YV9+/YVL148D5sEkIcICAEAAAAAAAAAeGqlp6c/2oYwPDz88OHDzs7OwcHBjRo1yuvWAOQZAkIAAAAAAAAAAIwiNja2UKFCVlZWed0IgLxEQAgAAAAAAAAAAAAYCM8IAAAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAAAAAAAAAGQkAIAAAAAAAAAAAAGAgBIQAAAAAAAAAAAGAgBIQAAAAAAAAAAACAgRAQAgAAAAAAAAAAAAZCQAgAAAAAAAAAAAAYCAEhAAAAAAAAAAAAYCAEhAAAAAAAAAAAAICBEBACAAAAAAAAAAAABkJACAAAAAAAAAAAABgIASEAAAAAAAAAAABgIASEAAAAAAAAAAAAgIEQEAIAAAAA8H/t2YEAAAAAgCB/60EujQAAAEYEIQAAAAAAAIwIQgAAAAAAABgRhAAAAAAAADAiCAEAAAAAAGBEEAIAAAAAAMCIIAQAAAAAAIARQQgAAAAAAAAjghAAAAAAAABGBCEAAAAAAACMCEIAAAAAAAAYEYQAAAAAAAAwIggBAAAAAABgRBACAAAAAADAiCAEAAAAAACAEUEIAAAA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\n", "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_molecule_start_index = 10\n", "\n", "sample_smiles = dataset.df['smiles'][random_molecule_start_index:random_molecule_start_index + 8].values\n", "sample_molecules = [Chem.MolFromSmiles(smile) for smile in sample_smiles]\n", "Draw.MolsToGridImage(sample_molecules, molsPerRow=4, subImgSize=(600, 600))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's look at a single molecule and explore its properties." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'CC(C)(CCC(=O)O)CCC(=O)O'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sample_smiles[0]" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol = sample_molecules[0]\n", "mol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use the [RDKit::Atom](https://www.rdkit.org/docs/cppapi/classRDKit_1_1Atom.html) class to further explore the features of the molecules." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total number of atoms in the molecule : 13\n" ] } ], "source": [ "atoms = mol.GetAtoms()\n", "print(\"Total number of atoms in the molecule : {}\".format(len(atoms)))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 4,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 2,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 2,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 3,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'O',\n", " 'atomic_numbers': 8,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'O',\n", " 'atomic_numbers': 8,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 2,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 2,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP3,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'C',\n", " 'atomic_numbers': 6,\n", " 'degree': 3,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'O',\n", " 'atomic_numbers': 8,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False},\n", " {'atomic_symbol': 'O',\n", " 'atomic_numbers': 8,\n", " 'degree': 1,\n", " 'formal_charge': 0,\n", " 'hybridization': rdkit.Chem.rdchem.HybridizationType.SP2,\n", " 'is_aromatic': False}]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "molecule_features = []\n", "for atom in atoms:\n", " atom_features = {}\n", " atom_features['atomic_symbol'] = atom.GetSymbol()\n", " atom_features['atomic_numbers'] = atom.GetAtomicNum()\n", " atom_features['degree'] = atom.GetDegree()\n", " atom_features['formal_charge'] = atom.GetFormalCharge()\n", " atom_features['hybridization'] = atom.GetHybridization()\n", " atom_features['is_aromatic'] = atom.GetIsAromatic()\n", " molecule_features.append(atom_features)\n", "molecule_features" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we featurize the atoms of our molecule as nodes." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'feat': tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", " 1., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 1., 0., 0.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", " 1., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,\n", " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,\n", " 0., 0.],\n", " [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,\n", " 0., 0.],\n", " [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1.,\n", " 0., 0.],\n", " [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,\n", " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,\n", " 0., 0.],\n", " [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0.,\n", " 0., 0.],\n", " [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,\n", " 0., 0.]])}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "atom_featurizer = CanonicalAtomFeaturizer(atom_data_field='feat')\n", "atom_featurizer(mol)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "74" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "atom_featurizer.feat_size()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We already have these embedded in our graph since we used the [node featurizer](https://lifesci.dgl.ai/generated/dgllife.utils.CanonicalAtomFeaturizer.html) earlier in this notebook. \n", "\n", "Let's decode the graph associated to the above index." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "smiles, graphs, labels, masks = map(list, zip(*dataset))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'CC(C)(CCC(=O)O)CCC(=O)O'" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "smiles[random_molecule_start_index]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_graph = graphs[random_molecule_start_index]\n", "\n", "random_graph.num_nodes()" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "37" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_graph.num_edges()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "torch.Size([13, 74])" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_graph.ndata['feat'].shape" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,\n", " 1., 0.])" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_graph.ndata['feat'][0, :]" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor([0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n", " 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 1., 0.,\n", " 0., 0.])" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_graph.ndata['feat'][1, :]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the order of the atoms represented in the graph is different. This will not be a problem for our analysis." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Split the Dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `dgl-lifesci` package provides methods to split data into training, validation and test sets based on [several strategies](https://lifesci.dgl.ai/api/utils.splitters.html).\n", "\n", "We will use the [`ScaffoldSplitter`](https://lifesci.dgl.ai/api/utils.splitters.html#dgllife.utils.ScaffoldSplitter) for this project. This method groups molecules based on their scaffolds and sorts groups based on their sizes. The groups are then split for k-fold cross validation.\n", "\n", "As with other k-fold splitting methods, each molecule will appear only once in the validation set among all folds. In addition, this method ensures that molecules with the same scaffold will be collectively in either the training set or the validation set for each fold. Scaffold splitting, rather than random splitting, is commonly used in chemoinformatics to ensure the training and testing sets include similar molecules." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from dgllife.utils import ScaffoldSplitter, RandomSplitter\n", "\n", "train_ratio, val_ratio, test_ratio = map(float, split_ratio.split(':'))\n", "\n", "train_set, val_set, test_set = ScaffoldSplitter.train_val_test_split(\n", " dataset, frac_train=train_ratio, frac_val=val_ratio, frac_test=test_ratio,\n", " scaffold_func='smiles')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets check the distribution of classes of train, validation, test datasets after the split." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28788" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(train_set)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "train_set.dataset.df.loc[train_set.indices]['HIV_active'].value_counts().plot(kind=\"bar\")" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8226" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(val_set)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "val_set.dataset.df.loc[val_set.indices]['HIV_active'].value_counts().plot(kind=\"bar\")" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4113" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(test_set)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "test_set.dataset.df.loc[test_set.indices]['HIV_active'].value_counts().plot(kind=\"bar\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All three datasets (train, test, validation) have a similar class distribution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Upload Data to S3\n", "In order to accomodate model training on SageMaker we need to upload the data to s3 location. We are going to use the sagemaker.Session.upload_data function to upload our datasets to an S3 location. The return value inputs identifies the location -- we will use later when we start the training job." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "import sagemaker\n", "from sagemaker import get_execution_role\n", "\n", "role = get_execution_role()\n", "session = sagemaker.Session()\n", "bucket = session.default_bucket()\n", "\n", "s3_prefix = \"./hiv_inhibitor_prediction/sagemaker\"\n", "\n", "dataset.df.to_csv(\"full.csv\", index=False)\n", "pd.DataFrame(train_set.indices, columns =[\"indices\"]).to_csv(\"train.csv\", index=False)\n", "pd.DataFrame(val_set.indices, columns =[\"indices\"]).to_csv(\"validation.csv\", index=False)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "input_full = session.upload_data(path=\"full.csv\", bucket=bucket, key_prefix=s3_prefix)\n", "input_train = session.upload_data(path=\"train.csv\", bucket=bucket, key_prefix=s3_prefix)\n", "input_val = session.upload_data(path=\"validation.csv\", bucket=bucket, key_prefix=s3_prefix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see `GCNPredictor` architecture compised of multple layers of `gnn_layers` which itself comprised of DGL `GraphConv`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Train on SageMaker\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Explore the Model Architecture\n", "\n", "We are going to represent each of the modelcule as a graph with each atom as a node. The atom properties will be the node features after doing the data transformations. We will then use these features to classify the whole graph/molecule as whether it inhibits HIV virus replication or not using graph neural networks (GNNs). In GNN terms this is considered as graph classification problem.\n", "\n", "We will use prebuilt GCN and GAT model architectures packaged with DGL-LifeSci to train the model. Please refer to the GCNPredictor [documentation](https://lifesci.dgl.ai/_modules/dgllife/model/model_zoo/gcn_predictor.html) and [code](https://github.com/awslabs/dgl-lifesci/blob/master/python/dgllife/model/model_zoo/gcn_predictor.py) for more information." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "GCNPredictor(\n", " (gnn): GCN(\n", " (gnn_layers): ModuleList(\n", " (0): GCNLayer(\n", " (graph_conv): GraphConv(in=10, out=10, normalization=none, activation=)\n", " (dropout): Dropout(p=0.0, inplace=False)\n", " (bn_layer): BatchNorm1d(10, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", " (1): GCNLayer(\n", " (graph_conv): GraphConv(in=10, out=4, normalization=none, activation=)\n", " (dropout): Dropout(p=0.0, inplace=False)\n", " (bn_layer): BatchNorm1d(4, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", " )\n", " )\n", " (readout): WeightedSumAndMax(\n", " (weight_and_sum): WeightAndSum(\n", " (atom_weighting): Sequential(\n", " (0): Linear(in_features=4, out_features=1, bias=True)\n", " (1): Sigmoid()\n", " )\n", " )\n", " )\n", " (predict): MLPPredictor(\n", " (predict): Sequential(\n", " (0): Dropout(p=0.0, inplace=False)\n", " (1): Linear(in_features=8, out_features=128, bias=True)\n", " (2): ReLU()\n", " (3): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (4): Linear(in_features=128, out_features=1, bias=True)\n", " )\n", " )\n", ")" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dgllife.model import GCNPredictor\n", "import torch.nn.functional as F\n", "\n", "model = GCNPredictor(\n", " in_feats=10,\n", " hidden_feats=[10, 4],\n", " activation=[F.relu, F.relu],\n", " residual=[False] * 2\n", " )\n", "\n", "model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View Training Script\n", "\n", "We are going to use Pytorch as the DGL backend. Our training script will save model training artifacts to a file path called `model_dir`, as defined by the SageMaker PyTorch image. When training is finished, SageMaker will upload the model artifacts saved in `model_dir` to S3 for later deployment.\n", "\n", "We save this script in a file named `code/train.py`. \n", " " ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[0;32mimport\u001b[0m \u001b[0margparse\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mdgl\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mdgllife\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mEarlyStopping\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMeter\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdouble\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptim\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mAdam\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mutils\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mload_model\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minit_featurizers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel_saved_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel_params_saved_path\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0ms3_downloaded_HIV_dataset\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mS3DownloadedHIVDataset\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mrun_a_train_epoch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepoch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata_loader\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss_criterion\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_meter\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMeter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mbatch_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_data\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_loader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0msmiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbatch_data\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msmiles\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Avoid potential issues with batch normalization\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mcontinue\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mlogits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Mask non-existing labels\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mloss_criterion\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlogits\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mmasks\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_meter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlogits\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mbatch_id\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprint_every\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'epoch [{:d}] of [{:d}], batch {:d}/{:d}, loss [{:.4f}]'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mepoch\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_id\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_loader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitem\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_score\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrain_meter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompute_metric\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'epoch [{:d}] of [{:d}], training:{} [{:.4f}]'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mepoch\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtrain_score\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mrun_an_eval_epoch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata_loader\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0meval_meter\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMeter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mno_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mbatch_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_data\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_loader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0msmiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbatch_data\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mlogits\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0meval_meter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlogits\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0meval_meter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompute_metric\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Hello I am training with following args.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcuda\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_available\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mdevice\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'cuda:0'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mdevice\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'cpu'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Device : [{}]\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mnode_featurizer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0medge_featurizer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minit_featurizers\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgnn_featurizer_type\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mdataset\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mS3DownloadedHIVDataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfull_data\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mnode_featurizer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnode_featurizer\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0medge_featurizer\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0medge_featurizer\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mn_jobs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_set\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mval_set\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msplit_dataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdataset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_loader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtrain_set\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mcollate_fn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcollate_molgraphs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mval_loader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mval_set\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mcollate_fn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcollate_molgraphs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mmodel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload_model\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode_featurizer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mloss_criterion\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mBCEWithLogitsLoss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreduction\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'none'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0moptimizer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAdam\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlr\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlearning_rate\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mweight_decay\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mweight_decay\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mstopper\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mEarlyStopping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpatience\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpatience\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mfilename\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmodel_saved_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel_dir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mmetric\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mepoch\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Train\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mrun_a_train_epoch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepoch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtrain_loader\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mloss_criterion\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Validation and early stop\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mval_score\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrun_an_eval_epoch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mval_loader\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mearly_stop\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstopper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mval_score\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'epoch [{:d}] of [{:d}], validation:{} [{:.4f}], best validation:{} [{:.4f}]'\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mepoch\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mval_score\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmetric\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstopper\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbest_score\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mearly_stop\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0msave_model_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0msplit_dataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdataset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain_set\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSubset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_data\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"/train.csv\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindices\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_numpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mval_set\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSubset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mval_data\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"/validation.csv\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindices\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_numpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtrain_set\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mval_set\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mpredict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mbg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mnode_feats\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'h'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m#edge_feats = bg.edata.pop('e').to(args['device'])\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode_feats\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0mcollate_molgraphs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Batching a list of datapoints for dataloader.\u001b[0m\n", "\u001b[0;34m Parameters\u001b[0m\n", "\u001b[0;34m ----------\u001b[0m\n", "\u001b[0;34m data : list of 4-tuples.\u001b[0m\n", "\u001b[0;34m Each tuple is for a single datapoint, consisting of\u001b[0m\n", "\u001b[0;34m a SMILES, a DGLGraph, all-task labels and optionally a binary\u001b[0m\n", "\u001b[0;34m mask indicating the existence of labels.\u001b[0m\n", "\u001b[0;34m Returns\u001b[0m\n", "\u001b[0;34m -------\u001b[0m\n", "\u001b[0;34m smiles : list\u001b[0m\n", "\u001b[0;34m List of smiles\u001b[0m\n", "\u001b[0;34m bg : DGLGraph\u001b[0m\n", "\u001b[0;34m The batched DGLGraph.\u001b[0m\n", "\u001b[0;34m labels : Tensor of dtype float32 and shape (B, T)\u001b[0m\n", "\u001b[0;34m Batched datapoint labels. B is len(data) and\u001b[0m\n", "\u001b[0;34m T is the number of total tasks.\u001b[0m\n", "\u001b[0;34m masks : Tensor of dtype float32 and shape (B, T)\u001b[0m\n", "\u001b[0;34m Batched datapoint binary mask, indicating the\u001b[0m\n", "\u001b[0;34m existence of labels.\u001b[0m\n", "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0msmiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgraphs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mbg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbatch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgraphs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_n_initializer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_initializer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_e_initializer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdgl\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minit\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_initializer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mlabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdim\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmasks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdim\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msmiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmasks\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0msave_model_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mfilename\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel_params_saved_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodel_dir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mgnn_params_keys\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfilter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0marg\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0marg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstartswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"gnn\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvars\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mgnn_params\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0mkey\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__dict__\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgnn_params_keys\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mfile\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"w\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgnn_params\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;32mif\u001b[0m \u001b[0m__name__\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m\"__main__\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Loading Parameters\\n\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margparse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mArgumentParser\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'HIV Inhibitor Binary Classification'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Feature engineering hyper-params\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-f'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-featurizer-type'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchoices\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'canonical'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'attentivefp'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'canonical'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Featurization for atoms (and bonds). This is required for models '\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m'other than gin_supervised_**.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# model evaluation hyper-params\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-me'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--metric'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchoices\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'roc_auc_score'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'pr_auc_score'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'roc_auc_score'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Metric for evaluation (default: roc_auc_score)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# model architecture hyper-params\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-nw'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--num-workers'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Number of processes for data loading (default: 1)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-mn'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-model-name'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchoices\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'GCN-p'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'GAT-p'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'GCN-p'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'DGL Life model implementation to be used. '\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-hl'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-hidden-feats'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m256\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'No of hidden GCNLayers to to be use i.e hoe many nerighers to be considered.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-res'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-residuals'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbool\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Whether to use residual connections in the GCNLayer or not.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-batchnorm'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-batchnorm'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mbool\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Whether to use batch norm in each GCNLayer or not.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-dropout'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-dropout'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdouble\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.001\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Drop out percentage'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-al'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-alphas'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdouble\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.08\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Alphas'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-nh'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-num-heads'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdouble\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Number of heads'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-predictor_hidden_feats'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--gnn-predictor-hidden-feats'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m512\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Training hyper-params\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-bs'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--batch-size'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m512\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Batch size for the data loaders (default : 32)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'--epochs'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Maximum number of epochs for training. '\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m'We set a large number by default as early stopping '\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m'will be performed. (default: 3)'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-lr'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--learning-rate'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdouble\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.001\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Learning rate'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-wd'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--weight-decay'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdouble\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0.001\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Weight decay.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-patience'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--patience'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m30\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Monitoring params\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-pe'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--print-every'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Print the training progress every X mini-batches'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'-md'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'--mode'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"local\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mhelp\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Mode of running this script [sm, local]'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m# Container environment\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"--model-dir\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menviron\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"SM_MODEL_DIR\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"--full-data\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menviron\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"SM_CHANNEL_DATA_FULL\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"--train-data\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menviron\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"SM_CHANNEL_DATA_TRAIN\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_argument\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"--val-data\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menviron\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"SM_CHANNEL_DATA_VAL\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;31m#parser.add_argument(\"--num-gpus\", type=int, default=os.environ[\"SM_NUM_GPUS\"])\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparse_args\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%pycat code/train.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create an Experiment\n", "\n", "SageMaker Experiments enable us to organize related trials for later comparison." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from smexperiments.experiment import Experiment\n", "\n", "experiment_name = \"HIV-Inhibitor-Prediction-Experiment\"\n", "\n", "try :\n", " experiment = Experiment.load(experiment_name)\n", "except Exception as e:\n", " if e.response['Error']['Code'] == \"ResourceNotFound\":\n", " print(\"Experiment name [{}], does not exists. Hence creating.\".format(experiment_name))\n", " experiment = Experiment.create(experiment_name = experiment_name,\n", " description = \"Experiment to track the HIV inhibitor prediction trials.\")\n", " else:\n", " raise e\n", "experiment " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define Hyperparameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's define hyperparameters assocated with the model. As per the training script we created above here are some of the hyperparameters that we can use to tune our model(s). One highlight here is that the model architecture is also given here as the hyperparameter which allows other model architecutures like GAT or MPNN. " ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "from sagemaker.pytorch import PyTorch\n", "import time\n", "\n", "hyperparameters={\n", " # Feature Engineering\n", " \"gnn-featurizer-type\": 'canonical',\n", " \n", " # Model Architecture\n", " \"gnn-model-name\" : 'GCN-p',\n", " \"gnn-residuals\" : False,\n", " \"gnn-batchnorm\" : True,\n", " \"gnn-dropout\" : 0.0013086019242321,\n", " \"gnn-predictor-hidden-feats\" : 512,\n", " \n", " # Training\n", " \"batch-size\" : 512,\n", " \"epochs\" : 20,\n", " \"learning-rate\" : 0.000508635928951698,\n", " \"weight-decay\" : 0.0013253058161908312,\n", " \"patience\" : 30\n", "}\n", "\n", "metric_definitions =[\n", " {'Name': 'train:roc_auc_score', 'Regex': 'training:roc_auc_score\\s\\[([0-9\\\\,.]+)\\]'},\n", " {'Name': 'validation:roc_auc_score', 'Regex': ',\\svalidation:roc_auc_score\\s\\[([0-9\\\\,.]+)\\]'},\n", " {'Name': 'best validation:roc_auc_score', 'Regex': 'best\\svalidation:roc_auc_score\\s\\[([0-9\\\\,.]+)\\]'},\n", " {'Name': 'epoch', 'Regex': 'epoch\\s\\[([0-9]+)\\]'},\n", " {'Name': 'train:loss', 'Regex': 'loss\\s\\[([0-9\\\\,.]+)\\]'}\n", " ]\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create Trial\n", "\n", "From here onwards each model training that we are going to perform will be tracked under the experiement that we created above as a seperate trial." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "trial_name = \"hiv-inhibitor-prediction-training-{}-{}\".format(hyperparameters[\"gnn-model-name\"], time.strftime(\"%m-%d-%Y-%H-%M-%S\"))\n", "trial = experiment.create_trial(trial_name=trial_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Submit Training Job\n", "\n", "DGL supports Tensorflow, PyTorch and MXNet runtimes. For for this project we are going to use `PyTorch` as the backend.\n", "\n", "The Amazon SageMaker Python SDK makes it easier to run a PyTorch script in Amazon SageMaker using its PyTorch estimator. We can also use the SageMaker Python SDK to deploy the trained model and run predictions. For more information on how to use this SDK with PyTorch, see the [SageMaker Python SDK documentation](https://sagemaker.readthedocs.io/en/stable/).\n", "\n", "To start, we use the PyTorch estimator class to train our model. When creating our estimator, we make sure to specify a few things:\n", "\n", "* `entry_point`: The name of our PyTorch script. It contains our training code which loads data from the input channels, configures training with hyperparameters, runs the training loop, and saves the model artifacts. It also contains code to load and run the model during inference.\n", "\n", "* `source_dir`: The location of our training scripts and `requirements.txt` file. The `requirements.txt` file lists packages you want to use with your script.\n", "\n", "* `framework_version`: The PyTorch version we want to use. The PyTorch estimator supports both single-machine & multi-machine, distributed PyTorch training using `SMDataParallel`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "training_job_name = \"tr-{}\".format(trial_name) \n", "print('Training job name: ', training_job_name)\n", "\n", "estimator = PyTorch(\n", " entry_point = \"train.py\",\n", " source_dir = \"code\",\n", " role = role,\n", " framework_version = \"1.9.0\",\n", " py_version=\"py38\",\n", " instance_count=1,\n", " instance_type=\"ml.p3.2xlarge\",\n", " debugger_hook_config=False,\n", " disable_profiler=True,\n", " hyperparameters = hyperparameters,\n", " metric_definitions=metric_definitions\n", ")\n", "\n", "estimator.fit({\"data_full\" : input_full, \"data_train\" : input_train, \"data_val\" : input_val}, \n", " job_name = training_job_name,\n", " experiment_config = {\n", " \"TrialName\" : trial.trial_name,\n", " \"TrialComponentDisplayName\" : \"TrainingJob\",\n", " })" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View Training Results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The initial training should produce a model with a validation AUC value between 75% and 80%. You can see more metrics like the training loss, validation loss, and ROC score over time in the Experiements view in SageMaker Studio." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we deploy the model to an endpoint, let's see the where the trained model artifacts are stored in S3.\n" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stored s3://sagemaker-us-west-2-167428594774/tr-hiv-inhibitor-prediction-training-GCN-p-12-15-2022-15-37-52/output/model.tar.gz as model_data\n" ] } ], "source": [ "model_data = estimator.model_data\n", "print(\"Stored {} as model_data\".format(model_data))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Deploy the Model on Amazon SageMaker\n", "\n", "After training our model, we host it as an Amazon SageMaker Endpoint. We need to implement a few methods in `inference.py` for the endpoint to load the model and serve predictions correctly.\n", "\n", "* `model_fn()`: Loads the saved model and return a model object that can be used for model serving. The SageMaker PyTorch model server loads our model by invoking `model_fn`.\n", "* `input_fn()`: Deserializes and prepares the prediction input. In this example, our request body is first serialized to a JSON object containing SMILES strings for the target molecules. The `input_fn()` function first processes the graph using DGL. It then adds the features to each node using the same featurizer used during training. Finally, the function returns the graph with features in the format required by the model.\n", "* `predict_fn()`: Performs the prediction and returns the result. To deploy our endpoint, we call `deploy()` on our PyTorch estimator object, passing in our desired number of instances and instance type." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create a Real-Time Inference Endpoint" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Endpoint name: HIV-Inhibitor-Prediction-EP-12-15-2022-15-48-22\n" ] } ], "source": [ "from sagemaker.pytorch import PyTorchModel\n", "\n", "endpoint_name = \"HIV-Inhibitor-Prediction-EP-{}\".format(time.strftime(\"%m-%d-%Y-%H-%M-%S\"))\n", "print(\"Endpoint name: \", endpoint_name)\n", "\n", "model = PyTorchModel(model_data=model_data, source_dir='code',\n", " entry_point='inference.py', role=role, framework_version=\"1.9.0\", py_version='py38')" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:sagemaker:Creating model with name: pytorch-inference-2022-12-15-15-48-24-925\n", "INFO:sagemaker:Creating endpoint-config with name HIV-Inhibitor-Prediction-EP-12-15-2022-15-48-22\n", "INFO:sagemaker:Creating endpoint with name HIV-Inhibitor-Prediction-EP-12-15-2022-15-48-22\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "------!" ] } ], "source": [ "predictor = model.deploy(initial_instance_count=1, instance_type=\"ml.c5.xlarge\", endpoint_name=endpoint_name)\n", "\n", "predictor.serializer = sagemaker.serializers.JSONSerializer()\n", "predictor.deserializer = sagemaker.deserializers.JSONDeserializer()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test the Endpoint With a Single Target Molecule\n", "\n", "First, let's define a function to convert molecular data into SMILES format." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "def collate_molgraphs(data):\n", " \"\"\"Batching a list of datapoints for dataloader.\n", " Parameters\n", " ----------\n", " data : list of 4-tuples.\n", " Each tuple is for a single datapoint, consisting of\n", " a SMILES, a DGLGraph, all-task labels and optionally a binary\n", " mask indicating the existence of labels.\n", " Returns\n", " -------\n", " smiles : list\n", " List of SMILES strings\n", " bg : DGLGraph\n", " The batched DGLGraph.\n", " labels : Tensor of dtype float32 and shape (B, T)\n", " Batched datapoint labels. B is len(data) and\n", " T is the number of total tasks.\n", " masks : Tensor of dtype float32 and shape (B, T)\n", " Batched datapoint binary mask, indicating the\n", " existence of labels.\n", " \"\"\"\n", "\n", " smiles, graphs, labels, masks = map(list, zip(*data))\n", " \n", " bg = dgl.batch(graphs)\n", " bg.set_n_initializer(dgl.init.zero_initializer)\n", " bg.set_e_initializer(dgl.init.zero_initializer)\n", " labels = torch.stack(labels, dim=0)\n", " masks = torch.stack(masks, dim=0)\n", "\n", " return smiles, bg, labels, masks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can use the `collate_molgraphs()` function to process the test data." ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "test_smiles, bg, test_labels, masks = collate_molgraphs(test_set)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's use our deployed endpoint to classify a single molecule from the test set. First, we select a random target." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol = Chem.MolFromSmiles(test_smiles[110])\n", "mol" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, let's serialize the molecule data into JSON format and submit it to the endpoint." ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[-7.176329612731934]" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "json = {\n", " \"smiles\" : \n", " [test_smiles[110]]\n", "}\n", "\n", "prediction_logits = predictor.predict(json)\n", "prediction_logits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The endpoint response returns the logit value that our target molecule is an inhibitor." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Test the Endpoint With Multiple Targets\n" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "json = {\n", " \"smiles\" : \n", " test_smiles\n", "}\n", "\n", "prediction_logits = predictor.predict(json)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Examine the Test Results " ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.722756329786207" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.metrics import roc_curve, roc_auc_score\n", "import matplotlib.pyplot as plt \n", "\n", "roc_auc_score(test_labels[:,0].numpy(), np.asarray(prediction_logits))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ROC AUC score for our initial testing should be around 0.75. Let's plot the ROC curve." ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fpr, tpr, _ = roc_curve(test_labels[:,0].numpy(), np.asarray(prediction_logits))\n", "\n", "plt.plot(fpr, tpr)\n", "plt.title(\"ROC Plot\")\n", "plt.xlabel(\"False Positive Rate\")\n", "plt.ylabel(\"True Positive Rate\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hyperparameter Tuning \n", "\n", "So far we have trained a single model with fixed hyperparameters. Next let's try to further optimize the model using [Amazon SageMaker Hyperparameter Tuning](https://docs.aws.amazon.com/sagemaker/latest/dg/automatic-model-tuning-how-it-works.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Submit Hyperparameter Tuning Job" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Training job name: hpo-hiv-gcn-p-12-15-15-51-40\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING:sagemaker.estimator:No finished training job found associated with this estimator. Please make sure this estimator is only used for building workflow config\n", "INFO:sagemaker.image_uris:image_uri is not presented, retrieving image_uri based on instance_type, framework etc.\n", "INFO:sagemaker:Creating hyperparameter tuning job with name: hpo-hiv-gcn-p-12-15-15-51-40\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "..............................................................................................................................................................................................................!\n" ] } ], "source": [ "from sagemaker.tuner import (\n", " IntegerParameter,\n", " CategoricalParameter,\n", " ContinuousParameter,\n", " HyperparameterTuner,\n", ")\n", "gcn_hyperparameter_ranges = {\n", " \n", " \"gnn-dropout\": ContinuousParameter(0.001 , 0.003),\n", " \"gnn-predictor-hidden-feats\" : CategoricalParameter([128, 256, 512]),\n", " \n", " \"batch-size\" : CategoricalParameter([256, 512]),\n", " \"learning-rate\" : ContinuousParameter(0.0001, 0.001),\n", " \"weight-decay\" : ContinuousParameter(0.001, 0.01)\n", " \n", "}\n", "\n", "objective_metric_name = \"best validation:roc_auc_score\"\n", "\n", "gcn_estimator = PyTorch(\n", " entry_point = \"train.py\",\n", " source_dir = \"code\",\n", " role = role,\n", " framework_version = \"1.9.0\",\n", " py_version=\"py38\",\n", " instance_count=1,\n", " instance_type=\"ml.p3.2xlarge\",\n", " debugger_hook_config=False,\n", " disable_profiler=True\n", ")\n", "\n", "gcn_tuner = HyperparameterTuner(\n", " gcn_estimator,\n", " objective_metric_name,\n", " gcn_hyperparameter_ranges,\n", " metric_definitions,\n", " max_jobs=1,\n", " max_parallel_jobs=1\n", ")\n", "\n", "hyper_parameter_job_name = \"hpo-hiv-gcn-p-{}\".format(time.strftime(\"%m-%d-%H-%M-%S\")) \n", "print('Training job name: ', hyper_parameter_job_name)\n", "\n", "gcn_tuner.fit({\"data_full\" : input_full, \"data_train\" : input_train, \"data_val\" : input_val}, job_name = hyper_parameter_job_name)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If your notebook loses its connection, you can reattach it by specifying the job name, like `gcn_tuner = HyperparameterTuner.attach(\"hpo-hiv-gcn-p-03-16-02-38-07\")`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Evaluate Tuned Model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's examine the best model and its hyperparameters." ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'TrainingJobName': 'hpo-hiv-gcn-p-12-15-15-51-40-001-bcb06c6b',\n", " 'TrainingJobArn': 'arn:aws:sagemaker:us-west-2:167428594774:training-job/hpo-hiv-gcn-p-12-15-15-51-40-001-bcb06c6b',\n", " 'CreationTime': datetime.datetime(2022, 12, 15, 15, 51, 45, tzinfo=tzlocal()),\n", " 'TrainingStartTime': datetime.datetime(2022, 12, 15, 15, 53, 59, tzinfo=tzlocal()),\n", " 'TrainingEndTime': datetime.datetime(2022, 12, 15, 16, 6, 24, tzinfo=tzlocal()),\n", " 'TrainingJobStatus': 'Completed',\n", " 'TunedHyperParameters': {'batch-size': '\"256\"',\n", " 'gnn-dropout': '0.001047059471760684',\n", " 'gnn-predictor-hidden-feats': '\"128\"',\n", " 'learning-rate': '0.0008192232031754262',\n", " 'weight-decay': '0.00485718776241423'},\n", " 'FinalHyperParameterTuningJobObjectiveMetric': {'MetricName': 'best validation:roc_auc_score',\n", " 'Value': 0.7642999887466431},\n", " 'ObjectiveStatus': 'Succeeded'}" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import boto3\n", "\n", "smclient = boto3.client(\"sagemaker\")\n", "\n", "best_overall_training_job = smclient.describe_hyper_parameter_tuning_job(\n", " HyperParameterTuningJobName=hyper_parameter_job_name\n", ")\n", "\n", "best_overall_training_job[\"BestTrainingJob\"]" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "2022-12-15 16:06:27 Starting - Preparing the instances for training\n", "2022-12-15 16:06:27 Downloading - Downloading input data\n", "2022-12-15 16:06:27 Training - Training image download completed. Training in progress.\n", "2022-12-15 16:06:27 Uploading - Uploading generated training model\n", "2022-12-15 16:06:27 Completed - Resource retained for reuse\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO:sagemaker:Creating model with name: pytorch-inference-2022-12-15-16-09-04-003\n", "INFO:sagemaker:Creating endpoint-config with name best-gcn-HIV-Inhibitor-Prediction-EP-12-15-2022-15-48-22\n", "INFO:sagemaker:Creating endpoint with name best-gcn-HIV-Inhibitor-Prediction-EP-12-15-2022-15-48-22\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "-----!" ] } ], "source": [ "best_gcn_training_job = sagemaker.estimator.Estimator.attach(best_overall_training_job[\"BestTrainingJob\"][\"TrainingJobName\"])\n", "best_gcn_model = PyTorchModel(model_data=best_gcn_training_job.model_data, source_dir='code',\n", " entry_point='inference.py', role=role, framework_version=\"1.9.0\", py_version='py38')\n", "\n", "best_gcn_predictor = best_gcn_model.deploy(initial_instance_count=1, instance_type=\"ml.c5.xlarge\", endpoint_name=\"best-gcn-\" + endpoint_name)\n", "best_gcn_predictor.serializer = sagemaker.serializers.JSONSerializer()\n", "best_gcn_predictor.deserializer = sagemaker.deserializers.JSONDeserializer()" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.7037138608316114" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "json = {\n", " \"smiles\" : \n", " test_smiles\n", "}\n", "\n", "prediction_logits = best_gcn_predictor.predict(json)\n", "roc_auc_score(test_labels[:,0].numpy(), np.asarray(prediction_logits))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Clean up \n", "\n", "Lastly, please remember to delete the Amazon SageMaker endpoint to avoid charges:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "best_gcn_predictor.delete_endpoint()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "predictor.delete_endpoint()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "import os\n", "for x in ['full.csv', 'train.csv', 'validation.csv', 'hiv_dglgraph.bin']: os.remove(x) " ] } ], "metadata": { "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "Python 3 (PyTorch 1.12 Python 3.8 CPU Optimized)", "language": "python", "name": "python3__SAGEMAKER_INTERNAL__arn:aws:sagemaker:us-east-1:081325390199:image/pytorch-1.12-cpu-py38" }, "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.8.13" }, "vscode": { "interpreter": { "hash": "a8534c14445fc6cdc3039d8140510d6736e5b4960d89f445a45d8db6afd8452b" } } }, "nbformat": 4, "nbformat_minor": 4 }