From 6a3efe063f425b827206c978a250800b041b4c55 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 16 Jan 2023 11:44:09 +0100 Subject: [PATCH 01/23] initial commit anomaly detection with gradient guidance --- ...r_guidance_anomalydetection_tutorial.ipynb | 778 ++++++++++++++++++ ...fier_guidance_anomalydetection_tutorial.py | 337 ++++++++ 2 files changed, 1115 insertions(+) create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb new file mode 100644 index 00000000..f8e67fbd --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb @@ -0,0 +1,778 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63d95da6", + "metadata": {}, + "source": [ + "# Classifier-free Guidance\n", + "\n", + "This tutorial illustrates how to use MONAI for training a denoising diffusion probabilistic model (DDPM)[1] to create synthetic 2D images using the classifier-free guidance technique [2] to perform conditioning.\n", + "\n", + "\n", + "[1] - Ho et al. \"Denoising Diffusion Probabilistic Models\" https://arxiv.org/abs/2006.11239\n", + "[2] - Ho and Salimans \"Classifier-Free Diffusion Guidance\" https://arxiv.org/abs/2207.12598\n", + "\n", + "\n", + "TODO: Add Open in Colab\n", + "\n", + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "75f2d5f3", + "metadata": {}, + "outputs": [], + "source": [ + "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", + "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "id": "6b766027", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "972ed3f3", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI version: 1.1.dev2239\n", + "Numpy version: 1.23.3\n", + "Pytorch version: 1.8.0+cu111\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 13b24fa92b9d98bd0dc6d5cdcb52504fd09e297b\n", + "MONAI __file__: /media/walter/Storage/Projects/GenerativeModels/venv/lib/python3.8/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: 0.4.10\n", + "Nibabel version: 4.0.2\n", + "scikit-image version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: 2.11.0\n", + "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", + "TorchVision version: 0.9.0+cu111\n", + "tqdm version: 4.64.1\n", + "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", + "psutil version: 5.9.3\n", + "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", + "einops version: 0.6.0\n", + "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", + "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", + "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "import os\n", + "import shutil\n", + "import tempfile\n", + "import time\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from monai import transforms\n", + "from monai.apps import MedNISTDataset\n", + "from monai.config import print_config\n", + "from monai.data import CacheDataset, DataLoader\n", + "from monai.utils import first, set_determinism\n", + "from torch.cuda.amp import GradScaler, autocast\n", + "from tqdm import tqdm\n", + "\n", + "from generative.inferers import DiffusionInferer\n", + "\n", + "# TODO: Add right import reference after deployed\n", + "from generative.networks.nets import DiffusionModelUNet\n", + "from generative.schedulers import DDPMScheduler\n", + "\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "id": "7d4ff515", + "metadata": {}, + "source": [ + "## Setup data directory" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8b4323e7", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmp142o2qtd\n" + ] + } + ], + "source": [ + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "print(root_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "99175d50", + "metadata": {}, + "source": [ + "## Set deterministic training for reproducibility" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "34ea510f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "set_determinism(42)" + ] + }, + { + "cell_type": "markdown", + "id": "fac55e9d", + "metadata": {}, + "source": [ + "## Setup MedNIST Dataset and training and validation dataloaders\n", + "In this tutorial, we will train our models on the MedNIST dataset available on MONAI\n", + "(https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset).\n", + "Here, we will use the \"Hand\" and \"HeadCT\", where our conditioning variable `class` will specify the modality." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "da1927b0", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-12-10 11:50:40,187 - INFO - Downloaded: /tmp/tmp142o2qtd/MedNIST.tar.gz\n", + "2022-12-10 11:50:40,255 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2022-12-10 11:50:40,256 - INFO - Writing into directory: /tmp/tmp142o2qtd.\n" + ] + } + ], + "source": [ + "train_data = MedNISTDataset(root_dir=root_dir, section=\"training\", download=True, progress=False, seed=0)\n", + "train_datalist = []\n", + "for item in train_data.data:\n", + " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", + " train_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n", + "### Classifier-free guidance during training\n", + "\n", + "In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an \"unconditional\" class.\n", + "Here we specify the \"unconditional\" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad:\n", + "\n", + "`transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))`\n", + "\n", + "Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e3184009", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 15990/15990 [00:08<00:00, 1784.85it/s]\n" + ] + } + ], + "source": [ + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", + " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", + " transforms.RandAffined(\n", + " keys=[\"image\"],\n", + " rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)],\n", + " translate_range=[(-1, 1), (-1, 1)],\n", + " scale_range=[(-0.05, 0.05), (-0.05, 0.05)],\n", + " spatial_size=[64, 64],\n", + " padding_mode=\"zeros\",\n", + " prob=0.5,\n", + " ),\n", + " transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)),\n", + " transforms.Lambdad(\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + " ),\n", + " ]\n", + ")\n", + "train_ds = CacheDataset(data=train_datalist, transform=train_transforms)\n", + "train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4c11b93f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-12-10 11:51:08,067 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2022-12-10 11:51:08,067 - INFO - File exists: /tmp/tmp142o2qtd/MedNIST.tar.gz, skipped downloading.\n", + "2022-12-10 11:51:08,068 - INFO - Non-empty folder exists in /tmp/tmp142o2qtd/MedNIST, skipped extracting.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1977/1977 [00:01<00:00, 1545.02it/s]\n" + ] + } + ], + "source": [ + "val_data = MedNISTDataset(root_dir=root_dir, section=\"validation\", download=True, progress=False, seed=0)\n", + "val_datalist = []\n", + "for item in val_data.data:\n", + " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", + " val_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})\n", + "\n", + "\n", + "val_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", + " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", + " transforms.Lambdad(\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + " ),\n", + " ]\n", + ")\n", + "val_ds = CacheDataset(data=val_datalist, transform=val_transforms)\n", + "val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True)" + ] + }, + { + "cell_type": "markdown", + "id": "7f108ebb", + "metadata": {}, + "source": [ + "### Visualisation of the training images" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4105a01f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "batch shape: (128, 1, 64, 64)\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAE4CAYAAACKfUBxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABtbUlEQVR4nO3dWcxe1132/1XaJLYTu57nebZjO3HiTE3akgpBqYDSqoflBM6R4ICzAgdUHCIVkFAlhBAHtEWIilKavm2aqRnt2Ek8z3Y8D/EQx3Zip/Q9+L9/ve+6fld8/7qy7/08tr+fs7W17j2utfZ6tp597U/88pe//GUBAAAAAAAAevRrI70DAAAAAAAAuP3wUAoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN59Kltx6dKlYdkvf/nLTnfm//eJT3yi03qD/NqvDX429z//8z8D63z44Ydh2Z133jlwPfo7tz+ZYx3m+XDr1mWZ3/3iF7/obPstdZxPfvKTA+tcvnw5LBs3blxVvnbtWqijx+KOTduEO4677rorLNPtjRkzJtS5evVqVb7jjjtCnYxPfSoOFZlj03ObOTa3rY0bN1bl6dOnhzqub2l7c2OW/s6tR3+XbWu6Lrf9zDiqdTLH0bruTF93dbL7NEjm3GbPY+a8tdRx+hx/W69/5rpl1u3quP3WMcptX8ckN45NmDChKuvYW0op8+bNC8suXbpUlT/96U+HOjqWXL9+PdTR/T537lyo88Mf/jAsA/qSGTdcncx9vKXOMLff1Vh7s3j//fer8vnz50doT4CRl51/ZujY4uZRLfPf7P5k5naZfczUGWmZc8J/SgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lM6UyWUB9a3mH0r2v3pK74rbntu+yiJTL0FHu/GfyQjJ5WYPWm1135neZOq3vwmaO1R3HBx98UJVdxlQm08llimX2Seu47V+5cmXgelydrt6XHqZMplamjWQydTK/G2amVJ+5P9nfqWFmSmXHlkF1WjOlnK5yvzLnLaM192qYbUvHkUx+YimlLFiwoCq7sW3WrFlVWfNTSill/PjxVdndM10W1Fe/+tWq/Mgjj4Q6f/VXf1WVNYeqlDjWu2wqjKzMHGWYmUp9bv92y1S63bXM44FbVWY+5vJz3Ryh5XeZ7bu5TuuzBuXG/9GYIdWCkQ4AAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPRuVGZKtea1tP4ms73Wd1gzdPuZ/CinyyycDPfOrOpqe5l36jPnyNHj0IypUnyGiWafuEyVTKaQrjublzRmzJiqnMm0crlTun2Xn+XWnbn+g/anlFzuUyZTyu3j7ZQp1ZrppLrMlGq5tpl9yvb11iymQetx+sx5ac2Ual239i3Xj9euXRuWTZs2rSqfP38+1Jk3b94Ny6WUMmfOnKq8fPnyUGfixIlhmY7RDz30UKizYsWKqvzyyy+HOjpGurFmpA0z00jXnVlP39sHblW0d+D/cn9rae6jy49yf8fpvbyrbKbWbFJH98n97TUac79bMNIBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAvRsVQed9BsR2ua1MiK/WcYGFGsjmzrULaMsEZGeCflX2HLW0iZZw7I/aVlfXUtfjAvKcTPikhui566jXzdXJhK9nwnfvvvvusEyPNxv0PqwQZbctlQ11zgSNt4SRO649arsdZoiio9tvPY7WDw20BJ239uvWYMm+r8kgrR/e6Cro3K1HQ0THjRsX6hw9ejQsO3PmTFW+fPlyqPOXf/mXVfmJJ54YuP2dO3eGOqtWrQrLvvWtb1Xls2fPhjp//Md/XJV3794d6rz77rtV2d0jJkyYEJYNK0Sc4GPg9kF/B/4vnQ+UEsPPXZ3WuZ7+TeLmVV3N9VvD2HWM6Gpe2zdGOgAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0Lt0plTr+4mt+RjD2lar1iwWPW9uPZlMFXf+M++wdvUuutunluvWmk3mjkO333r99d3jO+64I7V9fc/X5aVkrv/7778/cPsui0tzTdzx6z5dvXo11BkzZkxYlqHHksn9ylzHVpmcnUzuUDavR2X6bSZTyWnJhvs4v1OZ4289t131464ypfrMOHQybSSTjVZKd+dWx4grV66EOm7dmo/gxrZ//ud/rsrf/va3Qx3NedKMp1JK+fd///ewTPfz+eefD3XGjx9flZ988slQ5zvf+U5VdplaLq8PAD4uMqWAG9P5j7tHu7+jLl26NHDd2v9cXpWu2/095Lav9Vqfmei6yZQCAAAAAAAAkngoBQAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAetdp0PlIB8Q6uk+Z4NuugpdLiSFmLvzszjvvrMoaDltKDNUuJYatuRC1TIhvyzkapmzQe0YmIFLX7c6/hpE799xzT1g2c+bMgevRNuGutbsmGtDrfjdt2rSqrG2tlHi8LiBw48aNYdmwuOuv1zHbHlqCtlsDs1uD1vsMOm+t07qtPsMWu7q2rdeoK8Nso610jHLjyMWLFweu5+DBg2GZrsuFiE6fPr0q33XXXaGOu//p+OfC0Dds2FCVP/vZz4Y6Ov6dOnUq1AGAPnT14SHgZpT5W/eDDz4IdaZMmRKWfeYzn6nKx44dC3V02fLly0OdM2fO3LBciv8bUedN7u+4jNaPiI02/KcUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoXTpTajS+r6z7lMlLallvdj0ui0jzea5evRrqXLlypSq7d0NdNpLuk6uj68qco+z76l2d28x6W6+tvmfszpHuk8s0uX79elimuSYTJ04MdRYtWlSVL1y4EOo89thjVXnevHmhjvPuu+9W5WXLloU6+g71n/3Zn4U6d9xxR1XOvpucySvTc5vJGOoy02lYmVLZ8bDleFvPY0s2VWsdx+3TsDLtsm2k5dpmzmN2jGxZd2sO1zDv0Tr+ZXLf3O90PCwl3iPHjh0b6mg+hKvzN3/zN2GZnpNVq1aFOs8880xVdrkPuo/z588Pdfbu3RuWAUDXMnN94Fbl2vqYMWOqssvvdff/v//7v6/K7m/0kydPVuVJkyaFOt/4xjeq8lNPPRXqtM5tM3Pk0fiMpgX/KQUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0rveg867CeH+Ver+qbIiYHsuKFStCnalTp1bls2fPhjrbt2+vyu+9916oo6HajgvjzgSkZQLTnZbz3xKO3uW6WwOi77zzzoF1Tp06FZbpddNw8lJK+aM/+qOq7EJ0z58/H5Z96UtfqsqbNm0KdTTo/P777w91fvazn1XlmTNnhjqOXn8XPqjn1n0MQH/n1tMakN5Vndag8z7HqK7WlQ3xztTpat0t23LLWgMiuwqa7DuMsiVo3e2jBn3rxzlKiWHkpZQya9asquzuLRos7j4YoSGmn/zkJ0MdF2x68eLFquyO7fjx41XZ3aPfeeedquyOVfcRAIaBoHPcztw8xt3/1c9//vOwbOnSpVV52rRpoc63vvWtqvzSSy+FOhs2bKjKP/rRj0Kdy5cvD9zHjC6fo4w2/KcUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0Lt00HkmaNiFb2UCubROdj2ZQO6uwrgzwbYaqlpKDHpdvXp1qKPB1idPngx19u7dG5adPn26Krswbg2EdWFwd9xxR1X+8MMPB67HceseO3bswN9pQKM71277uixzrV2b0bbtAiP1HLnfaWBvKTEQ2J2jv/iLv/A7+/+4du1aWPbtb3+7Ku/ZsyfU0RD93//93w91NLTPBeY7Ws+dW12WGUdcncy1dr/TZe53mfaXCbrOhOhntt8yZpbi+20m6Fu378LoB633o37n9kllrm1riGsm6Lu1TXalJbSy9WMcrR96cOOPcvefSZMmVeXx48cPXPeFCxdCHf34h6vjxtbM+KvnxJ0PbaPZj4EAv6rWDya4Npm5/+jv3Firv3Njves3OkfI7ndm3bczxp/hcX9rtMw/uvqoi1tXZj2tx+H+1sn+TdCXzPzTjVFu/qPnxB2//k3+n//5n6HOH/7hH1ZlN9e5dOlSWKbzJjfX0mvp5tV6vJm592jEyAYAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHqXzpTK5FVkfzeoTjZTquX93NZ3ejMOHjw4cHuLFy8OdWbOnFmVH3jggVBn0aJFYdnmzZur8o4dO0IdfRfYvWeceafVLRszZkxVdu/w6vbdO736fnw2r0DfBc7kpbh38XV7mW2VEnNOXH7W1KlTq/KUKVNCnUymw1133RWWPf/881V5165doY5ub+nSpaHOo48+WpUPHz4c6pw4cSIsa71uKpOX06eR3n5Gdh/1vfY5c+aEOpqFd+rUqVAnk2mQyaJz/e+DDz644bbcerLHn8lUatFlXoRy+9i67sy9VWXOv7vW77zzTli2adOmqpy9t6jWTBvdb3f/07GVvJaR1TrXbO2TmUzT1v6XyTTM3P+1veuYWUqcj7l1u99phkqmH7n1uHFL7z8ur0VzVtzY4vLqbmeMUd3J9D+VyV3K5qe1jFGOriczjrh1j7b8KMf9PaRjkuZZlhL/ZiullOnTp1dll1f5zW9+84bbKqWUt956qyq7v/U047KU3Pwn099v1gwpxcgGAAAAAACA3vFQCgAAAAAAAL3joRQAAAAAAAB6x0MpAAAAAAAA9O4Tv0wmOLrQsJbwyS5DLLsKrR1msLEGy7nwMw0xX7VqVajjAtLff//9qrxnz55Q59VXX63KLsRYg9Zaz7U7jxoI6IJmM9c/E/7r1p3ZfiYM3h2bhtbNmjUr1Fm5cmVVdgF9GRoGWkrc70z44tWrV0MdPf8uVPTYsWNhmQbruRBFPZfuWuvvXGDr3r17q7I7jy6gUcMe3XXUtpVp667OMEO0lQuxdEGHc+fOrcoPPvhgqKMhikePHg11NPzxzJkzoc758+fDMj0nrUGfKhtGnAkazgQdd/VRj4zMBxuyMkHnek3cNcqEvzotfcuNYzqOZD6Y4Za5Oq3zD3X33XcPrHO7afnQjZO5j2T6f6ZvZ9bjZNpf5iMqbvt6v3NBu5kwXPc7d99Wek9258ONEXpP1jlrKbHfuPu/2+/b2bvvvhuWXb58eQT25Nbj+lGm/+uHjtxvumrHmQ82uflgV3PUkZa5RtnrmPnQl9IPOJQS/0Zz8wE3RmY+2KV1Wj9GM9Iy93r+UwoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPRu8MvkN5DJq9A67p3ClvV0KZMpkOGyiDTDx2Xz6PvhLq/FvUOueUXr168PdebPn1+VNWOqlFK2bt1alS9evBjquHOix5vJYnLvuWYyXVqzyPT94My1de/9utwFzT5xmTobN26syi5TQY/DnSO3fT22TF6VW3cmC8XlTGXe4VaZNtJ6rTNjS6uWcayUtiygrnJYSolt9OTJk6GO5gXOmzcv1NG8NJeN57KodLzLZIpkshhcf3DnRPuya3/DNKz7Vus9srWPZPLjMut2v9NjyeS1ubGuqzEikw3Udzu6WWm7cedN20Qmd8ndozPXVnNfXB237swcMTP+dDVHzmbz6fZacx91/M1kQ7p6mT6amSPd7jJzLbRxY1Qmi0nnyG5e7f6Oe++996pyZoxw+6N/D7Teo9zvWjMlh8UdfyZTy80bMjlf48aNq8ou41fPm8umcrm3Ldw1Go0ZUi0Y2QAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOMyGKwwoVdtv6qGUt625dbybEWwPJXEChBjsePHgw1Llw4UJYpoHoq1evDnVWrFhRlSdOnDiwjoZzlxLD0Esp5cqVK1V5zJgxoY4G0rlzq+ckG+KYuU6ZEM9M0Klbpvvp2lYmfFBD9LJheJm2pUHXmeN37TgT9OhkQlwzIbaD1vtRy1rqZGTXM8wPNGScOnWqKrvAem037kMHGhC8fPnyUEc/qlBKHEvc2Kbjn2truo+ujbjfdRWQ21XbytTJjH9dfgwkE6LqQoxVpt9mA5qVjnUuDDkTou3qtASkt46Ht5uu5mit/Uavt7u3Zq5lJjS4dW6py9w+6jzCHWvmODIh8pmPkbg6LjBYj0U/qlFK3G8XBp35iMvthOD37ugcOTP/dvdDDb92QedujqR/x7kPXWX+RtJ5e+aDDaXEvnwzBGa748jstwsfV+7c6t+67vrrOObaUeYelZnbZj4YNtJ/e7TiP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzh0N0moJDHWyIa7DCvLqcr16LJkwXrf9EydOhGUaiOaCJjVE24Whr1q1qiovW7Ys1PnJT34Slr322mtV2QVU6vFqqGAp8fizofZ6nlyIXSYgMNOOs+H7g+q46+/Om3LnLRMir+fEhehpiKgLw3YyIfJ9Bt1m1zVIlyHSfYYPuv0+f/58VXbXVtuR+xiC0uDHUkoZP358WPbII49UZReQu3PnzqrszpH+LhNYWUouxLQ1WDyjq+vdVTtq/WBJpk7mPGY+IuHOtQaLuqBR17Yy+5T5GANB5210vHHnLRNQq23CXdfWENmW9TiZgNrM2Obav67HjePuPOo46cKXdd1uHNf7iDtHkydPDst0bnPu3LlQRz+Q4+4/V69eDctuZ9mPAWGwrj5+pf3IfTDGtW39e8v10SNHjlRl19d1THBzpJs1/LqFG7Mz9+3MRyS6DIPPftjqdsHIBgAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAepfOlMpkIWV0lWnxUfVa6mR0lSnjcgcymUru/J86daoqX758OdTR94rfeeedUOf++++vyi53av78+WHZ8uXLq/Irr7wS6hw+fLgqnzlzJtTR93Pd8WvuUSkxLyZz3tz513ePM7lTbt2ZLAqXBaHXKNuvMm1Ss1fce9aZTJXWvDhd1pLx0lqnb6Mtd6iU2N5cfplmH7hMg2nTplVll1eg+XWllDJz5syqvG7dulBH36l/++23Q51MFoxzM9wjMutpyT1yWu+jmTHa9e1Mn9BxPDNGZDNVWjIk3Loz4ygiPZfuerTcR12bdTkfOrZkMvXcWKPryW5f67lsSK2TuUdn5yja/+bMmRPqTJgwoSrrWF9KKXv37q3KOq8rxc/RFi9eXJVPnz4d6uickL41GJlS3dF+6/IKtY7r63pN3N9jLhv4nnvuqcrr168fuO4DBw6EOpm/IzJ/I3WZqTosw8yYdXXGjh1blTMZd9m/4zP0vpFpf61z5pHGyAYAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0Lh10PsygM113NmhttAXUuhCzTBh3JpBszJgxA3936dKlUOfll1+uyi5o/ODBg1X5wQcfDHXWrl0blm3YsKEqa6hxKaUcOnSoKu/atSvUOXLkSFU+evRoqONCAzUQ0wUUah0XoqnnMRsQp9c2E5Dq9jETYurajbaJ999//6N39v9wYdSZ0D4XrJeh+93VBxNuZZnA2uwYqe09E77owtCvXLkycFsamOvWvXDhwlBH25YLUddxJBu03dJuWwPDu9IadO50tZ861rl+nPkYhKuj4dNunz/44IMb7s9H/a6l37h91LE9c69H7H+ZD724e6T27UyoeClx/MvcozMfjHDzCLffur2ugoZd+3PHpvf7lStXhjq67Pjx46HOsmXLqvJbb70V6jz//PNhmZ4TN0fUvv3ee++FOpm+1dXHWLIB0SOJoPPhGWZAtJtb7dmzpyq7v/XWrFlTlSdOnBjqbNu2rSq7eVRm3pD9iMJIyvwdl6VjlLu36N9ImTD87DnTsU3vR26Zu0Y3a7C5YmQDAAAAAABA73goBQAAAAAAgN7xUAoAAAAAAAC9+1iZUpn3/DPvPuu63fui7h36ljqZ9/WzmT76Dmcmi6L1vXf3nqmu66677gp19H19zWYpJb4v6zKdXBaV5ky57X/lK1+pyjt27Ah1Lly4UJX37dsX6rgsqhMnTlTlixcvhjqas+Tes86cR0fbWyYvyl1HzavIvi+s23f7rdvX9uC257aVec/c/U7fj3fHr8eRyYvJZJO4ZW6M0PGmNRvH7bdrb4O278ZRXY8bI7UdlZIbozK5U7qe/fv3D6xTSilPPvlkVXaZZpppkskr0hy6j9p+Ji9g0LaydbrK9Mrkrrnr35q7mMld0nObzZ3Qdbk+osfrrlEm0yqTReRoncwcYbRlzIxWXeX1ZDK93P1v0qRJNyyXEu9Rbj2Z+5ibI2k+UyYvKdOP3X3c/W7p0qVV+aGHHgp1tm7dWpV/9rOfhTp6/Pfcc0+oM3Xq1IH75MYtPf7Mfex2z1S6WY8/87dd5h7pZDJenczcTs93po9m79H698/hw4dDHR235s2bF+ro8b/55puhTiZ31rWtlty7LN1e5lpnrm32b4SWLKYu85vcvWSQzDnK0nMy0vlhN+fIBgAAAAAAgJsaD6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDv0kHnTkuIaiYM3MmE37qARA1NzYSxu3Bit48aLOeC7XRdmRBpF9jqZM6/7qMLejt9+vTAOj//+c/DskuXLlXlr371q6GOXrdFixaFOpMnT67K9913X6izffv2sOzkyZNV+dSpU6HO22+/XZXPnz8/cD0uDDATPu+u29ixY6tyJmjPtXUXPqhhp6796TLX/jJhzE5m3VeuXKnK7jh0Pa7/ab/N9hE9lkyIvAse1OvvApv1WF09d251e66OBoS7NnL58uWB22/9GISeN3eO9MMDpZSycePGqnz33XeHOtr/ly1bNnAf3fV34evabtx1awmazMp86CLzoY3MRwUyMmHkbh8zYfyZ+5Hr23q8bhzTOu5en/lAgZOZI7R+DON21xIQnJnruT4yf/78sOyBBx6oyrNmzQp1NMTbGT9+fFWeMGFCqLNnz56w7Lvf/e7AdWto8bFjx0IdHdtd/58zZ05YtmLFiqrsPhjz/PPPV2UXxq73H3fO3PY1tNldfx3LW8PwWwN6W9Y90h86uFmDzrsMiFat9/FM0HZLsHT2WLX9698spcQPCzz88MOhjvZ1N9fZvHlzWKYfusr8jdzlx1gy50mPpfVadzVGZD9G1cW2Wuu0hqGP+Ng2olsHAAAAAADAbYmHUgAAAAAAAOgdD6UAAAAAAADQu3SmVCYLoDUvQN+pnTJlSqij7/SXEvMp3HuemXevdZ9c7oV7hzXzzqbWce+dZupofpD7nTtWPRa3Hs09cu8Uu3OiGTKvvfZaqDNz5syq/Pjjj4c6em5dXse6devCstWrV99wf0qJ72e73J133333hr8pJeZOue25LIZMH9H3pd274Jm8ssz70plMMyfznr3bR92ey6LIjBGaIZMZa0rp7t1rzRCZNm3awN+UEtvWuXPnQp3MdcxkWmRybjJ5PZlMLddmXBbb8ePHq/KmTZtCnQcffLAqT506NdTRvBh3zXQcK6WUI0eOVOWLFy+GOhl63jK5N447b3otW9fdmrvSch93ddyx6b299Tj0nuTGscy6XRaVzi2mT58e6ui9Zvny5aHOP/7jPw7cft8ymZ7a3jLX1l2jTM6IayP6u8wc0Y2/mTmCGyP0nuTy6jQv0N3r3e9chqaaNGlSVXZjlM5bMrmDpcTxz81tdN2//du/HeosXry4Krvjd+P/jBkzBm5f83LcelrukcPUOtfoykjnvrTKzBFb72OZsc71Ub23ZP6OdH8jDfqN28fsunQetXv37lBH72NPPvlkqOPGv23btlVl17fdMqXnO3Ncpfg2oTLXSLVmKjmZTM/MPrXOrTLrUdlMry7PUxf4TykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd+mgcxfslQmk09+5oC0NaJw3b16oo0GLpcRgNw2jdHVcGKSGqLmgaReQquFvLjBM1+WOX4MdXdDlqVOnwjIN2na/0+25oO+77767KrswwMmTJ4dlGpq3a9euUEdDk13QsV7vWbNmhTouNE+XLVy4MNR54IEHqvKBAwdCHQ0RXLBgQajjAuI1/NyFeB49erQqHzt2LNTRfuS25YLmtU1mgq5dP9bz6IKuXZ/Q/XR9JNP/M0H32m41HNVtq5Xb/ty5c6vyqlWrQp0LFy6EZTomuD6q7ejMmTOhjo5t2RBJ7ctujNLz5q6/hjhmAkNLiedE+1opcWxxoZoaRuzuBxqqW0ocI904qsfmgna1rWc+WFFK7Lfu+uuyTPCkC7F09+NMn9DfuX6sx58J9SzFj1stdPzRe1YppUycODEs0367ZMmSUEdD9PXjHKXEe5IL2h6NQefallwb0bbk6mSCzjMhqq7d6lji+p+O9/fdd1+o8+ijj4Zl+oEKN7ZpW3Ljj47RV69eDXVcW9fz9s4774Q62rcyQe8uQF0/GFFK3O9MQLKbI+s4vmPHjlDHWblyZVXW4PVSYrtxfTvzMaDWjx+0BhSPNnocoy3AOKt1v/X4M3PdUuJY1nofHbTeUtrbkc4RtmzZEurouPXFL34x1Fm6dGlYpn/HufO/b9++qnzp0qVQR8cIN0ZmPuKSuf6jsT+Otv7W+vdQ60cUMv0og/+UAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHqXDjrvigso00AuF0bnQiQzwWIafqeBuaXE8E0X6j19+vSB23Ihthoa68K/tI4LmnQBoRos50I0NVjYBRRr0KYG35WSC5Z0QbMaiOeCxvfv31+VP/3pT4c67vzrMndtJ02aVJXXrVsX6qxZs6YquzBOF+x3/vz5qqyhnqXEYFX9jVu3C+x3Idp6vC6wU6+t63+6PbeeTIizCyjVNuJClDXEWK9ZKaW8+uqrVdmFgWe448iE8WqfdPs4ZcqUsEzDJzVUtpQYrOwCIrUfu8Bu9zu9/q5OJmhSz4kLg8+0EXfdNETTtVHtoy6M2vURbVvuIwoarOzGaD1e10bc8Wv/d+df+5/7YMLp06ersgYYu205mfBV96EFlbmPumVubNf7xtSpU0MdvW4aYF6K75N6L3cfSNBlrh9riL6rMxrp9c4E5Lt7fSbo37Ut7X9u/qH9z13/tWvXVuXHHnss1HHzj3fffbcq61zDbd+NrRrs7cbxr33ta2GZfnzlv/7rv0IdHbfcR0X04ytf+cpXQp177703LNP7pgtI1jnJnj17Qh0d29wcyV1/vZZuHrd3796q7Ob/ek5aQ4Vbw3dvBjdD0HnLhzdKifcod//RPpn9O1LX7ebfGTq2ZoOm3Zio9FjcHOngwYNV+aWXXgp13N8o69evr8ruQxM6/zl37lyok/kYj/sbWeeE7vzr8WY+auD+jnFtq6WfuPM/zLElEyKuy7IfQ9Lz5OYImfOf+ahKan+afgUAAAAAAAB8DDyUAgAAAAAAQO94KAUAAAAAAIDefaxMKfdepdL3Fd1vLl++XJXde/8uQ0O5vAjNGXBZAPo7976yy8vRdblMC63j1qNcplQmZ8m9G6vv8GrGQikxi2rVqlUD97GU+F7x4sWLQx3NOXGZMrpP7lq7vCzdvsur0uyRFStWhDqa++PeRXY5F5l2o+3PZZFopoR7p9qdN30X3bX/TBaDXiN3/O7Y9J1h17f03WO3Hm3vro/83d/9XVX+t3/7t1DH0fHGjT96TlzugOZuuEwN128zY4SeI3ce9b1/926+y0LRPnL27NlQR9fl2prWcefItRt9P137mttHd/w6Jrh25LJYZs+eXZXde+56/3Hv4mfeu3fL9Hdu3ZrXM2fOnFBHxwjX/tz4p+fN5U7puOHO//Lly6uyy+aaNm1aWKZ92fVtvW6ZcdRl07lMIe1v7j6aqeO2dzPQNpnJ9HT3CK3j1uPav3J5Jbq9RYsWhTq/9Vu/VZXdfVT7cSmxvel4UEophw8frspPPvlkqPOnf/qnVXn+/Pmhjus3r7/+elV+6qmnQh2d27h+9NBDD1VlzTgrpZRjx46FZTr+LFu2LNTZuXPnDcuOyxRx2Tiac+PmlpncUT2OLvNi3LpU5m+dkZY5jpGWyYvK5OW4uVYmY9atW+/JbhzTscXlV+p93N1r3fin229tj9qPdu/eHeq4cUPHW5cX+tnPfrYqZ/5GcfNId050ey53V8cR94xA8zq7zFTL5CNl2nHmd611lGtHmfW4/da+5O51mXlUxugfxQAAAAAAAHDL4aEUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoXTro3AVk6TIXfpgJ+9KgNw2+LSUX9OyCbjPBXhpi6oLuXLCeBnu5oGkNrXSB2RoINnbs2FDHLdPwMRdQN2HChKrsAsp0+y6M2AW9arCgW7eGmC9YsCDU0fOYCfErJRc+rQHNu3btCnWWLl1alV1gbibYeObMmaGOtomFCxeGOtr+XYigC3rX43UBqXqNNIyxlHhtXWBpJqDY0bBBF76XqaPtODMefdQypdtzYYwnTpyoyi6wct68eWGZtj8Xvpnp/9qOXHt0fVTbmwvo1HPkQtQ1WPL48eOhjguo1PBJd2za3lxg8aFDh6qyO373EQP90IFrD268G8QFlrvwd23bmaBpd4/Q+6gLDHb3Tb2Wrm/r+OvGP723uKBpF9Cu/dbRdut+o9ufPHnywPU4mQ8dOJkQb7eekQ4f1naamaO586jjVvY8tgTEuvFHz7+7j7m2rf1/zZo1oY62LRcGrOvOfLChlHgs7t6u9+1777031FmyZElVdvNh1/9+/dd//YbrKSXe77Zt2xbqvPzyy1U5O0fX8+TaiB7/0aNHQx29J4x0vxqNboZzkpmPZcZj14+0r7mxx/2Npn/buT6iv3MfjNG/UXTuU4r/GI2uy41/Ot65+UdmjHbb1w89uLlF5u8Inf+68dgdm44bbmzVa+Lm2vr3kBuP3H7rvC1zH3P3Ub0m7u9x9xxBl7k5stZx7Vh/5+ZR7t6uf7e73+m63T7qetw+Zoz+UQwAAAAAAAC3HB5KAQAAAAAAoHc8lAIAAAAAAEDvPlamlL576d7F1Pcs3Xu++u6pexc1k8Xh3hfV96zduvXdX/e+qMtiOXbsWFV2uROZvAbN8Mhkarh1u2PT9zpdXsL8+fOr8rp160Id9360nif3Trsuc9dI3w9376u6DBPNgnrooYdCHc200rLj8kpczo2eW5expO3GHb9eW3f9XRaRtj/3Dq+eN5fN5d5zVq7fan93OUtax7VRPbfuWms7cvvTmiml++Te19f33F2mgXuHXfOBXF6Pvovt3rvX3CmX3+bajbblzHvurv+tXr26KmvbKyXmTpUSs6fc2Krn37V1HVvdOTp58mRYpufJZaPpPrlxXPfJ9ZlMpqFrN9on3Tiq++2ukaPbd/0vk/ujv3NjzfLly8MybX/uPqLnzV1/PY7WTCN3rHq93XoybWQ0Zkq15GVl5l/uOrpxU6+ty1TUe7LLZnvqqacGrsdtX/f7K1/5SqijbSKTe+LmEa7/nz59uiq7LJTHHnusKrtsPj0Od101P6uUUpYtW1aVXV6ijj9PPPFEqPPII49U5RdeeCHUee6558IyvZe7+5/m1bi59pYtW6qy61eZsc0df6b/6/l3/T8z1ximkR5rMjKZoplz6+aamkWmfa+UmJ9YSsw0c3Mknce5e5Teo91cJ3OPfPPNN0MdzcJ1Y5T+befaoxsjdS7n5t96/C73Upe58+jofrp59MGDB6uyG0d3795dlTWHtJTc3yiu/WXyc3VOmM2UymRD6+/cPFbnhO44MnnBbm6p+5TJxnV/o2SM/lEMAAAAAAAAtxweSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85daJvKBL269WgYoQtjy/zO0RAztx4N32sNDHTr1rAxF3StYcCnTp0Kddw+ZQKyNURPA5tLiaGd586dC3XmzJkTlunxuqBhDVFz4XsabOqC1jRErZQYyLdkyZJQR/fbBbRpO3Ihrm6fMmHDek1cG9HwTdeP3O9mzZo1cH/0fLt16/G6UHcXPquheZkxwoXvadt2QaP6u0w/dsvc7zL7rcfq2rELUXz77ber8uHDhweu27VRDYh1bX3x4sVhmYZGaqhnKbHduDDETIihCx/WQE7Xt3QfXRi49hEX9Oj2OxOirOfEfehAx1p3HK7dakCl9tlSYtt2/U+PP/NxglJif3cB5XpOXBiwjmPugx1ujNa+5X6n59ZtPxM06mTmCKr1/p8JFe+bG2+VHq+bf+nYlmkjpcQQa7fuRYsWVeUFCxaEOgcOHKjKrj24fvOjH/2oKruxXj+Q4sZx7SPuWrtzomG7Lnz3M5/5TFV2x6Zjmxv/Nm/eHJbpfC9zb88EFLuxfu/evWGZzgnd3PKBBx6oyu48atCxW4+7J2v7d327JaB8pEPNnZsh6DzzMYrMOJr50I2717u/rXT+4drxzJkzq7J+ZKmUONa5eZybW+j4px+VKSWGeLv5h34gwY0jJ06cCMv0XGpgfClx3HYfVdCxxo0R7kNbuv3M37HuYwh6/l2ovDtv7u9WpfMmF5ivY427/o7Ov93fcdq3XZ3MB4vc2K776fY7E3Suy1rnbKN/FAMAAAAAAMAth4dSAAAAAAAA6B0PpQAAAAAAANC7dKaUy4LQ90zdO7xdvdPtshH0d24fM3kJut/Z98UzWTiZ96N1v91xuH3Sdzbd7zR3Sd9fLiVmOMyePTvUce85K81vKSXmA7h91JwHlynjshBOnz5dlS9duhTqaPaAy92ZOnVqVc5kGrl6mfwStx5d5q61ez9Y3+t2fUvfodb350uJ59b148z7yS7TJ9O29bxlsqHctlpzHnT7mbwCty23T9pGXNvSMUlzmEqJeQHa9kspZceOHWGZZh+4vq3tSMeMUuL1z2SjlRLfxXfvouv2XRvVDIFMNlEpub6leSVnz54NdXSMcNlc7rxpm3Bjm15vza8oJfZJ19bcNdHsGZcNqH3L5Q5qFqK7j7p+q/co10ZUa//L/K4lYyprNGZKZc5by7l1+WHLli0Ly+bNm1eV3X1k1apVN/xNKbFNutyT1157beD2XaaM9ncdax13HK7faM7T/v37Q51t27ZVZTe26Di6c+fOUMf1SR1v3fxHj9/dfzTnZf78+aGO2289Nrffuk+uHT366KNV+Zlnngl13PnP5JzomJTJZsqOP326GTKlMmN0RmaOmBnrSsnN0TSLKTNHcOOYu/+/8847Vdndo3Xe5vqo/v3l/mZbt25dWKbzf3eP1nPrMp00i8/lV7lxY82aNQO3r8fr7j86Juv1KMXPrXTe6DLF9L5x7733hjo6trjcVTdv0ywol6ml8103jo30+NOV0T+KAQAAAAAA4JbDQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dNC5C4jLBMu1hG+5oLlMaJ0Lem0JH3VhZJmAdg2VLSWGmLkwXg0tc2HALjRNQ+OWLFkS6syaNasqr127NtTREDUXxucCMjWQzYX/aWioCwidNGlSVdZzVkouDDFTx11HPX7Xjlq5YPPMPmVou3F9TetoeyglBoS6oPNMsLE7b25dKnOO9DgygZVun9w+trQttx53HfWaZILuM+fRBf9r0GQpMWxy4sSJoc7ChQursgva1GDJ6dOnD6xTSjwW1x704wtu/NV9dGOtGyMz9x9tW24fNejbBc278FE9J67/aRh95j7mxkgXEJoJFtfz7e5Ruk+u/bkPXejYqh9ecHXc9ddr5II++5QN0R1pOra4cVzPt5t/aBivW4/7+ICGX3/xi18MdXRuoaHepcSx1YWRP/HEE2GZtlv3MRRt7zofKSXeI916tm7dGpZp0LeeR1fny1/+cqjzxhtvVOXjx4+HOn/+538elulY7s6tHtv3vve9UEfHPzfWu/DfQ4cOVWUXIqzH//jjj4c6+jEMN466dev1d21Ux1Z3j9b7iGv/mXnMMN0MQcduH3VZ5m82Vydz/JkxOnPPdHMEvbe5v4cOHz4clukHUtw+ami6zodKiX3CzZHcHEXHBPc7/btN+2Mp8fjdfMCdk40bN1bl+++/P9TROaK7RjqOu7mG+9CXHpubR+sYmQlRd2Nt5m/L1rlNZt2Z/peR6Uet2+I/pQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOXbCYBmS6oF/9XSbEzq0nEyLsZAJSNTTWBV26YEUNO9MwulJi+K4LqNWgNReQ5gIaNdhNg8dLiYGgbvt6Ht05ciFqGnbrrq0GYro6Z86cqcouaNcFa2q9TNCkC7/TYFfXrrJtUuk+ZQLi3LnO7FMm/NG1o8mTJw/cJ0e35/ZbA0Hd8WtopIaqumWtgeWtofLKhTG7gMTMNVKZwPrsRyW0nmv/O3bsqMpHjhwJdTQw1401btmMGTOqsuvHGr7prr9u3wW2ZwKa3TnS/c58sMKNNe4eqe3f7aPukxv/dD1uH13b1nOZ+YiIG/913HAfrHDb1+25Onou3Ril/W00hvq2fFRl2DSg1fVR3W8X4qv91gU9uzb5pS99qSo/+OCDoY7Od9zYpuO96/8LFiwIy86dO1eVX3/99VBHx0Sdj5QSxxEX4vvcc8+FZTr/Wr9+faijfdT1v3Xr1lXlr33ta6GOCz/W8ca1UZ3vumPTD2asWbMm1HHXX7mgYw1xd210xYoVVdkFFh89ejQs0zE584GCTEDvaBx/uprbDFPr/KflYzTZvxlb9sn1UR0T3cdA3LILFy5UZXf/P3nyZFV2H1pZvnx5VXbzKDduzp49uyq786H3X3f8+sEK14/dHEmPzc2j9f7j6ujf2pmPM5US73fugzl6jtzfHzr+ZbZVSrxOmbbu/v5Qbh7VOrbpsQxzrBn9oxgAAAAAAABuOTyUAgAAAAAAQO94KAUAAAAAAIDepTOl3HuG+s6mq6PZEy5TQzMlXF6Fe89TM50yeSHTpk0buG6Xu+SyUPRdfPcu7vTp02/4m1LiO8TufV33frDmRbj3VfW9Uvcu7pUrV6qye1/Zveeqx6L5CaXEfBrNeCglvp+beae2lHje5s+fH+roe87uOur2s++iZ/dzkJZMg6xMpkumH2fyuly7dcuUjgluH9278CpzPVwf1WWtuRPuHGk9t2737nuLzPbdu/BK81NKibkHLj9v5syZYZmOJXPnzh24PZdNov3WnUe9H5QSj9/lHOj9JpON6N7pd2N7JgtM153JInC5L+7YMrmPeo5ce9T1uPuoyzDI5Hy0yGaqDUtrNknfNK8p07Zdpon2kd/93d8Ndf7gD/4gLNN7spv/qUymojuOS5cuhWW7d++uyi+//HKos3bt2qrsxj/NnXLzKLffOk6uXr061Mkcm2YqPfDAA6GOa396T3D3CM3ZcpmCOm9zcz23TPfJZYrpPPbVV18NdTTTxeWuujG55d7aev8faTdDplRmHp05jswYkbmOTiYb0f2tpVx7dNvXccuNLdr+Xbs+cOBAVXb5Vfr3aClxbufo38R79+4duO6VK1eGOkuWLAnLdN7ijk3HZPf3QCab0J1bXeYydnVdbv517NixquyOQ//WLiW2E3eP1DqZjOXseJDJlGoZ71rnaKN/FAMAAAAAAMAth4dSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd+mgcxeQqGFvLqBLg81cQG4m6GvGjBlhmYbIuvAv/Z0LSNOgs4sXL4Y6LqBL99MFxmlApgs/0+25wEi3bheapjREzgXGa2jgmTNnQp09e/aEZRpsmwmadOFrmfBB1yY0/P2tt94KdTR8zoXvaZtw4dyu/et5c0Gj2m7cevScZEPVMyHmGXrdsgHumWBfvf7u+LWOC0zWc5IJLHcyQc+Z43fXKBO+mQmMd9vPBJS73+kY7UKMNejYbUt/54IeHQ0fd2OLjr/uHJ06daoqu/Ews0/ugxHHjx+vyi5o3bVJ5cYoDT93Yex633Ljup7/bKivtgl3bnWZO0c6Jrp25LR8DCITUDsaQ31HY9B5ZvzVgNjPfvazoc7Xv/71qrxhw4ZQJxM+fujQoVBHQ2wzwbtuWxqYXUoM+818MMOFaOtxuPX83u/9Xlh28uTJqjxlypRQJ9O39Hh1XlmK/0DQa6+9VpU1DLmUUv7jP/6jKu/cuTPU0THR3f90rlVKvLYuIF8/kPHUU0+FOrrMfVSjNehdx9LM3CIbYt2n0TgmqtZzlPkYTuZau2ur/S9zj3TXX7n7caaNug+W6Hjj2r/+zo1R7m+bzLxJ+7Gb6+zbt68q79q1K9Rx458+E1i3bl2oo3MSN0fVc6vz2lJ8QL22JTf/0+27OZKeI7d9d/01WN1tX6+ba3+67i4/mNWidVujfxQDAAAAAADALYeHUgAAAAAAAOgdD6UAAAAAAADQu3SmVCYvwb2vqu+5u/dl9f3ITO5OKTHnw71n7zIMlL776d4FzWzf2b17d1V2792fOHGiKrvjcO+w6jXJ7M+sWbPCMs05cddx2rRpA9ftzpFe20xek3sX2x2bHot7X1mXuePXLAr3Tm/mHXL3vnzLe7XZvIjM+/n6DrV7p1ozNLL7rNe7tY/o9tz2dT1uW26M0nfPM+9ZZzKdMtty68pcs0zumqvj+pZe29ZMDd2+y+9zGQa6bpeXov0tk1fkrqPLRtB1nTt3LtTRvuUynbTfuP7ochbGjRtXld04kmlbeh3dtXZ03ZncKXduM9vLtO1h5r71mZeQyfgbDbS9aXssJd4T77333lBn4cKFVdm1I5ch8uabb1Zlzd0oJY5Jbh91vHHXWrPpSonH7/KiNm3aVJXdPVKzUd31f+ONN8IyHUsyuT9u+w888EBVdufItT/NnnHXSOcbLi9MM/3cWKu5W6XEvFTN7yullEWLFlVlbWulxCwsd63dHFnHZDf+6jJ3/pW71490ptNIbz8jM0d0f2voPN7NEbRPuL8HXKau9lHXt3Uf3VxH5wSZe20p8Vjc3ELbu/s7Rn/n7tku01n30/UtvW6ur+nY6uZR7pwsWLCgKmfmEZlsVsfN0fQ8ufOm9x/XtrSNuHPk8qIzfyPoeJPJmM387VVKbt7U1dwmM0aN/lEMAAAAAAAAtxweSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85dsJYGZLmAtMOHD1fly5cvhzqZYFsXUKYBhS5YTEOs3T5q+Fom1LqUGEjmjk1D0134m4aIuRA3F76ov3Phl0uXLr3h/pQSw+c1VLOUUtavXx+WZUJ0dd0uRE6XuWvtAko1INmt+8KFC1X55MmToY6eb3f9XfhgJmxP+0gm6M0F3bm2rW3ZhThq2F0mxDrTjl09N0a4dSndJ3etNSDVtTV3bJmgWz1vrv1lrrWONaW0BRK6fqztz9Vxx6brzpy3TB91MuHrbvzVMcJ9aGDu3LlVef78+aGO6zeTJk0aWEf30d2P9Py7Oi5YUs+Ja0e6Lrce3Ud3HTNt1J1/7duujvYJNx65NpIZ7zJ9JBN+2lX4eWZbzs0QdD5jxoxQ5/HHH6/Kn//850MdDaPevHlzqPO9730vLNN5y+rVq0OdyZMnV2XXt7Rtuzbj+o1u331oZvv27VXZjaM6/3HbP3LkSFim4edbtmwJdebMmVOVv/rVr4Y6Gqzr5iiurWuIsJsj6BzRrUf7xNmzZ0Mdd0/S6/bKK6+EOnv37q3KOmcrJTfX0LG+lBjingnaduOf3pPcvCbzEZVhuhmCzt0YqW3E3f/dB7KUXkfXHmbPnh2W6d9k7t6q7cjd63SM1H5Vir9GGn7tPgag7c0dm46b7j7mlukHCtw8Qo/NjSO6fTeOu76tH03IfDDGrbv1g2l6j3AfbNA24cY6PbduH6dPnx6WqcwHk9z4r/M419cyY9SIj2O9bQkAAAAAAAD4P3goBQAAAAAAgN7xUAoAAAAAAAC9S2dKOfqeoXuH8cyZMzcsu/U47l1Y3V5mPV3mPuh7nZncrcw+uvW495z1HWr3vrS+i/3lL3851Pn0pz9dlV3ug3unW48/c201P6KU+A65O1Z3To4ePVqV9d1kt26XFzFx4sSwTOk75aXEc+Lalv7OveesOVenTp0Kddy70HpOXO6YLnN5Ce+8884Ny6X4vCTNlXGZHnq8mf7nztGBAweqsnun2h2/Xtt169aFOvoOu8vP0j7iMiVcH9H2596X12viMhWUO9Zly5aFZXpN3HnTTAV3jXQfs31Uz79rWzomuCwGzVRx2Xyuj2o9148y47hy63HtRs+Ty6LQ8+1yFzTDwGUauPFP99Pdf/R43XnM5CVkuLaVyULJ5C46emxu+zqOubwsXZbNtBtp2t5mzpwZ6ixcuPCG5VLi8bt7lDtvmk/psuC037h+pO3W9SM3Jmo70Wy6UuJ1c2OL3v9cm3U5K7rf7rzpsbhsKs1wmjZtWqjjxq1MXuC5c+eqsuvbuh53H7t48WJYptfStRE9R5m8vEw2jNsnN9fTOaEb//Q43FynNYvudqdzC9f+NXcpkzHr2uPu3bvDMu23mdwf10c0i2jr1q2hjrtHaNty93at4+ZR2kdc7pSb/+vc1s0RdIxwmXLaJ9y9JpOX6fqxzhFd/9f9duNh5m/0TKZhJr8yOx5k7m26LneNWudkow3/KQUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD07mMFnffJhYZp2JkLkdNAsEyoqgtRddt3gYxKw8dc0JxyQccutG7OnDlVedGiRaHOk08+WZXvu+++UEfD5zTUrhR/bjV82AX06fnfvn17qKOhfS4M1IWoa7Cgu7YabOnC92bMmFGV582bF+q48FcNH3Qh0hrIuHHjxlBny5YtVXnXrl2hjmsTuj0Xvq2BhC4gUYOVsyHSLrS0hfY3FxirofYuDNHR8+8+BqBtxPXRsWPHVmV3PrSO254Lejx27FhVzoTouu27ZTr+uLFN25Eb13SZa2uu/Wv4uwto1HHEhZjq+OuukdtvHTenTp0a6mRCszMhlpnz5tqtXjcXIqzn24VaunOr63ZtS5e5oGkd/zOBxaX49q50ey7oU9d9+vTpUMe1fw27dWHw+jt3HfV6u/vhaAw617bk5gh6vO6a6T3BtRG9j5YS+5/7YIb2CXf/yYTGujraT9wYpeHnbv5z/PjxquzGKBeQrHMJN0fSD8u4vq33P3cd3dii93vXR7X/uzmahiEvWbIk1HEfaNBgYfcRi8cff7wqu/nf008/XZVXrFgxcB9LKWXz5s1V2fVt7SOu/ev9JvPBilIIP1eZD224uYUuc+O/rtv1dbdMxyjXjvR6u7meziPcWJf5YJe7/2t/dx9s0Dnizp07Qx03/9HtuT6SmW/rmOg+KuA+BqQfv3Af7NHz7fZH7+3ub0a3T/q3lWujeo4yHyxzdTK/czLzj5ZnHa3rzshuP/yu6VcAAAAAAADAx8BDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0LuhB51riFYmMCsbDpgJ0tLtZQJK3T5mwnBd+KEGJLoQRQ3IdGHcLnx73bp1NyyXEoNeXfibrtsFjU6ePDks06BxF6y3Z8+equzC/3SZCyN0NHzOhYHr+XZBoxp06wJzXZvQsF8NbCwlBjS6EE8NbXSBoRq06n7nrq2uyx2bBhu6/pe5Jq4/ZsL3tP+5EF091y4M1v1Ol7n2p1yItm7fhUG68EsNmnUhlnq+XTvSvuaO1Y0tui73u8zHF3Tdrj+4c6LtzYWYZvZRx0gXhu/OW2b8031yx5a5j7k2qeOmC4PWOq6v6f3HhbFqGyklHr/bvvZJF/Ss599dI3dOdLx1AaU6jrkwcr0n6XGV4tuf9ls3/umxuHak++2OYzSGGut4d+DAgVBHw6cPHToU6miwtAu6dh/o0DmBC7qdNm1aVXbjqF6jzJhVSrz+bh6n8zZ3bfWcuA9muPBh7duzZs0KdXS+59qx9i1XJxPsrKHCpZTy4IMPVmXXt/RjFBrOXor/iIp+xOOhhx4KdZYvX16VT548Gepo0LO71504cSIs07HMfaBB1+Xm8ZmPumTuG7e7zPnIzCMzf8e5fuz6jc6t3VxbuXu97rc71swczX3USo9FP2BUSry3u48xuIB4nUu485/pIzpuu7HWHb/O5dw4ptfN3SN03e5e74LOtd24eZSu280RdK6T+aiEW5b5YErmeYRbT6ZvZdY9TPynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6N3HypTK5ENl6rTSnAG3LV2WyYty72K6d0j1/Uy3bs0LeOyxx0KdxYsXV2WXTaDv3Tsui0PfK3Z5AfoO744dO0KdjRs3hmW6LpcXo9fIZeoM2p+PkskiyryLrtffvXfs8qqUy4J68cUXq7LLS9i/f39VfuONN1LrPn/+fFV2x6/vfrt3uvWauHfh3XnTdbl3kXWZu7aa1+Dee9Z3+N3+uOPXTIndu3eHOnptXT/OvHfv8or0/fTMu/hu3Xpsro5ms5Ti36tXmjvjjl/3WzNGSvHXVs+Jq6PH78YRHWsy2VxumWv/eryur+uY4HIf3Lihx+bOreZFuHuNnkd3/O7c6vG7vAxtIy73TtuIa39u+3q+3TimORcuv0WPw23LZYjo9lwWh9Zx91Gt43InMu1vmPMhR8+lu7dopqQ7t5qN5DKl1q5dG5Zt3ry5Kh89ejTU0Zwrl5em45/rxzqPKiXmfLj2p9fb9T89/mz71/vUvn37Qh3NVHH5obo9dxxu3qjH77Kw9Hy79Sg3/r3++uthWSYvUcc2l02m1//RRx8NdVzOoLaTrVu3hjqaV+Ouo55/N9Zk8lowWCZ3qsv8rpbfZTLtMtlIpeTmdgsWLKjKCxcuDHV0TrBy5cpQx2UxZbJ59Ry5bF5d5vIDXV6yzi0zeZFurqt/a7hjdeOPjv+ZLDJ3/d19S7m/UXS+6zKddY7m2pFeo8zfUaXE8+TurZlMWd1e9u/4sJ6mXwEAAAAAAAAfAw+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA79JJVKMxsM+FBisXLKY0xMuFoTkaIu7CF1evXl2V16xZE+rMmDHjhvtTig/j04A0F+KqoZEu6FbXraG2H7VMw9ZciJqG37njmDp1alV2bc0F67kgN6Uhfu7aaoicO//vvPNOWKZhn25/9HgPHToU6rz88stV2V0jd040SM6FH+s+ufOf+RiAO7bMBwLcskHbzwQmuzDATB/RUN0sbduuHbllLpBQ6Tly578laNDVy4QPuvVoYLKOWaX48HNtkxoYXEouIFL7fyawsZQY2unCN3Ucz4SBurbmAjJdaKfSa+TWrW3LXUd3HjXE040/GjTuwvF1/HPH6gLKtS25/qD3Fhciqv1YP2BQig8oP3bsWFU+ceLEwN+5fqx9NNOv3e/cxyD65O4RGn7uzr/2t89//vOhzpNPPhmWaSC6C4jXsVznA6XEMGy9rqX4cUvPvwsI13W5oHEN6HXt37WtF154oSq7jwjs3bu3KruA4jlz5lRl9zEINx/WMdndNzT8XMfDUmJAvbuObvzX7f2v//W/Qh29Jxw5ciTUmT9/flV2oeYu/Fh/d/DgwVBHx1J3b9HznQ06R83dt3S8zXwwolXLfNTJzKPdWO/+jtG/I9wYvXTp0qq8bt26getxgeWuj+qYqKHqpZQyffr0quzmGq5PKnf+dUx093E9FjdG6TVx9zE3/9G5jftgjT5HcH8P65zEzf3c3+j6Ozf/0vPm+pGO/24e69qfzondtdU6maB7t3133RSjKAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F06U8ppffe2Zb1uPfqep3sXXN+9dFkQuh73vuTixYvDsvXr11fl5cuXhzqaveKyMPQdTvdu+iuvvBKWaRbBvn37Qh3NC3GZCvoOr3un2b2fq+c2856rO4+6PXf93bXV8+QyFfS9bpd7ou9Lu+Nw7/nqfu/YsSPU2bZtW1X+/ve/H+ocPny4Krt3cV2byORFZfLR9By5d+Ez7/ln+rqro8fhtp95X9+1EX0X27VjPUfuXOv1d1l1mYw79059Jp9G9zszHpYSz5PLwtB1uePQdbttuX6jbTmTTeayCWbNmnXD8kf9TrfvxjbNC8nkdbk+6tqkti03/ug1cbkzmqmQza/S8d/ltWheTybTwG3f5czoutw+6vbdPUpzH/Q3HyWTIaLt1rV/bROZbEK3/ZHOlDp//nxYpm3SZfM8++yzVXnz5s2hzpe+9KWw7OGHH67KLq9Rz60bI3TekJ0j6Dj1wx/+MNTRedOyZctCHZ0juWwWl8Wh2SM/+clPQh3N9HL9SOek2dxTHdtd39Jr4sZ23Sc3RmVyL1370z7ickc008tlA2bm/66Pan93WTzab929zvXtzPhzO2nNdMrkdbXmTum6Xd/SOm4c02w0zXgqxecVaYaT5vC5fXLr1nbsco8y5yjzd5zLvdV9zOau6TJXR7efyfR02VTubzTNmXPXSMcNN4/TbET3t4Ybf3S8zeSVZfJjHTdG6pzYjX86JrtnJDr/dvPx3/md3xm4j/ynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dNB5JkQrUyfzu0zQVykxkM2FjykXULlo0aKq7IIuXUC3rsuFuGr4oQs6e+mll6ryyZMnQ50tW7aEZRrk5kIkNfwtExjoZELEXUCmht9pqFwpMeh53rx5oY4LFtSwYRc0qet2x7Fw4cKq7IIO3e9OnDhRld01+vnPf16V3fFr+FwmsNstc/0mE2Ku3HXMtJtMiGEmxDITGJoN+suMSZnt67l116g1DN6tS+nY5o7fBRRq2KKro+Po1KlTQx0NOnRhyBr0WUr8iIALsZ09e3ZVXrFiRaijwZ4aKlmKDwjWQFwXYqnr1uDzUmKwr9u+u/9ou3XjiC5z11ZDS929xu2TBoIfO3Ys1NH7iAso1XuSCzp2Y5t+2KP1QwN6Hl0Ya2tAqPb31jEqE3Q+0tzYriHy7mMsem3dtd65c2dY9oUvfKEqf+5znwt11qxZU5UzYayOO9fatzZs2BDqaLvRcPZS4sdI3BjhwofnzJlTld34+y//8i9V2R2rHseBAwdCHXf8jz76aFXWD6+UUsqqVauqshsjdGx1Y4SbN+k45fqWjgn33XdfqPPII49UZXf/cWOizv/cfis3tugyd65H+iMGN4PWoHPto63jauYauT6qcxT3oZWlS5fesFyKn3/o9tz4q/dfF+KtbdvNtTIfyHnmmWdCncwHo/Q43N9sbllm/qPn2wVt6/HrR15KyZ1b/buulDj/ch8n0vHH3Wvd+KPrcuOotvdM+898HKyUeE3c/ScTdK4f+nAfoyDoHAAAAAAAAKMSD6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDv0kHnTmuweVc0tE2Dr0spZcGCBVV55cqVoc78+fOrsgvaduFfGiyugaGlxPBPV+fo0aNV2YXIuoBYXdYa9KxBZy7ELRMs68LXdN0uoE+P1wWGumubCWjTQFIX/qfhgy6gzoUfvvDCC1X5+eefH1jHBf1lQgTd+dfQOhdiqKF5rj3o9jPbcjJ1XNvS7WdC9d05cuvOBB2rzHl058iFGGqbdOvW32UC011gpzs2Hcvmzp0b6mjfcmGcuszVcfuk4ecahuh+566tjnXu/LsxwvV3tWnTpoHb1/Pvxhq3fT02N0bq9lavXh3q6Lhx6NChUMeFH2tosQsxfvvtt6vym2++Gerofcv1NXdsLXOEzAcTslr6vwsazYyjzmgLOndh+Dre6nyklFxgvwvR1vB3Dd4vJd6jly9fHuroBxPcdXTh4xpsq4HlpcSxxQXk6nG4EFc3t5s5c2ZV/uxnPxvq6MdQzp49G+osWbKkKm/evDnUcQG9n/nMZ6py5kMLbq6p44/rI+68adhuZm7hxmy9R+7duzfUcfP2TP/PfERCx3t3j3B9y833bmet86/MOKpta/z48aGOa1v694aONaXEj2G5dqx/I7i/Y1xAtLYb1450/NmzZ0+oo3MNN45kPmLkQtz1nLj5j/Z1N9a48G09NldHP0bl5h8XLlyoyu4cHTlyJCw7fvx4VXYfGtNluq1S4piQ+ThVKbm/o7VO5oMt7lo7Let2c31tE66tf+Mb3xi4P/ynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F06U8q956vvFWZyJtw71rpul83i3mHUfBJ9776UUhYvXlyVZ8yYEepoPorLBnDvomoWkr6bWkp8P11zqEqJ7/C7c+0yLPS95q5yN9y5dvuk3LXNZPHoOdJ3/EvxGTa63+5dbM15efzxxwduX98fL8VnOPz3f/93VX7xxRcH7qN7X1rf/c3kd7l1u+vfUse1Nbf9THvTY3Hr0XfP3bvQmWyezLFlcqcymVKurbtlmUyNzPWfMmVKVXbZUO4dbs0n0vWUEvNRXBaAjsluHNNsgFJiX9aMlVJizpHmwJRSyrRp06qyGw8czVlybUvfhXftSPMpsmOtjkmu3bqcw0Hrcdl8LgtFs6c0v6aUUnbs2FGV3fifyVTqKmPS9VGVPf/alzPzmEymQuv2R5pmvJUSszBcFolrE4PWU0qcI+3bty/U+du//duqfN9994U6mjPlcjddzpDmY7ksEO1/c+bMCXV0HHeZKi53UsdEt+7Zs2dX5e3bt4c6OrYfPHgw1HFZXOqRRx4Jy15//fWqfOnSpVBHx5GNGzeGOprNV0rsy+68aZ3nnnsu1NFzollZpfi5vY7/mf7v7m06j3T9n/yowTLztsyY6e7/U6dOrcpurHM5U/fee29VdvcxHdsyuWsuv85l6u3fv78qu/6nY7L7O0LnbZn7eClx3HLnVq+b276eN3et3d92mUxNzXRy11bHepcppRm/pcT5j5tbZXKfbnfu2rbgP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzjMhpi5YLRMirEGHLmhXQ81LKWXDhg1V2YX4akChC4M9ceJEVXaB5Rr0WEopp0+frsouRM4tUxr054KOW2VCrIdJt+eurYaBuqDjpUuXDly3C9H8jd/4jarswkg1aM+FiD711FNh2csvv1yVXUCmBuK5a6vX312jYYaYt9Rxy1z/12WZwPRM0LA7jy5od9B63O9cG501a1ZVdqHiGsbt1uXOo65Lg8dLiQGdLozS7beey0mTJoU699xzT1V2QdOZMToz1rnwSQ0NdWO0fnzCnSMXvqnB6i6MVs+ROzYNGnfX3wVk6jlxH1FQGnxaShy3NBy+FD9uvfbaa1V5586doY72pUzQ+GgL8P44Wo4l+5vRFojqAnozYfB6HG4cc8eqffmtt94KdbS/6XyslPgRETfWz58/Pyxz80b1uc99rirrx3FKiWOLu4+4MUE/EKHz0VLiHEX7bCml/Mmf/ElVfuCBB0IdF2K+devWquzmyK+88kpV1uDzUmKwvPvwj7v+OidzH5rQ+Z4Lo9bw+2XLloU6u3btCsu2bdtWlV271XuCG/+0jutH7v6X+UDA7aT1fGj/c/MfbduuP7r7v95L3Ye2dJm71tpu3IeX3NxGA6Ld73Ru4fpa5p7kzr/OpVwf0Tpu/qXH7+bo7iNiDz/8cFV2fUtpOHkpcRx75plnQh33EQkXLI+Rw39KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3nWaKeVk3iHWd2Fdpox7F1h/595X1fdFz5w5E+ro+7r6jm8puSwGd6yZvKBM7k7mHeJsFpHqKmcq806zq5PJtHB5LStXrqzKv/mbvxnqaKbE0aNHQx3N1NGsqFJK+clPfhKWaa6Cy7TSc+ves870kcy1bc2UyrRRR/c7m0U1aPvuOHRZJr+olNhv3Lq1vbm8gvXr11dlbTMftW7NS3HjmG7PZSNl3tfP5GW5vATNMHCZBpcvX67Kbox0eQmZdSv3jr/+bsaMGaGOy4/RvDp33rQtufFH8yncvcZZs2ZNVXZZDJoh5fqM3rfefvvtUMdlwWiGT2aMducokzN1K8vca53RlinlZLIBB/3mo36nfcv1bR1L9+7dG+pozpC717vcSbVq1aqwbO3atVXZtX+d/7nco8mTJ4dlek7c2P71r3+9KmsOVCmlfPe7363Kmmdais9Z0fuPHmsppRw+fLgqP/vss6GOHr8bf7/whS+EZXq8mvFUSikLFy6syu48aj9y87if/vSnYZne/1ymqI7lmh9ZSswrc/lV7t6KWnbepvTcuvmH9rVsfpXOW9y9LvM3qu6jzplK8cev442rk/k7btB6S/H3Iz2XbvzVDDc3/9U50oULF0KdJ598MizTeZubR+q5ffrpp0MdzeJ74403Qp1MXpVrN5l5081wr78Z8J9SAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO8+VtB5S7CXCxHTZS7ozQVkbtmypSq7MDINOtTg21JisJ1bj9tvDS3OhJE7+rvMb9w+ZQLaugo1z65b6+j1KCWeRxfq50KMlYZ6lhLD/lzQ8LFjx6ryj3/841DHhf/p9tz2MyHuev3ddWwNEe9KS9DiMLlQURcifv369aqcCWx2IY7Tp0+vyi4w1Y0tmTEy00YyIZqub+nvzp07N/B3LmhSj81ty/1OQzRd0Ln7iIGaOXNmVV6xYkWoM3v27LBMgy1diK0GdLq2pfcf91ED/fCCW/e+ffsG7qNrM9u3b6/K7mMMmzZtCsv0fLuxRftEn+NKlu5T9h6Zkel/g37zUQg/HUzHDddGNQx7yZIloY6O0aXE8+9CtPWjASdPngx19D7i7jXunjB37tyq7ALC9Xf6cYRSSvnZz35WlV0Y+pQpU8Kyhx9+uCq7sVbHRHeO9EMLbvw/depUWKZjstvHQ4cOVWU3/uq8TX/zUTIfmlmwYEFVnjZtWqij46g7j2PHjg3L6P+DZcZ2XebmMRqs7dbj7m0j/RGPlu23Bm9nlrmPuOgYuXz58lBHP2rgPkbh+oiO/+5vvb/+67+uym+++Waoo3/HZULNS4ljQuZ6jLa/h24lnFkAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dKaUexfVvZ896HfunV59P9O90+kyVHSZe89Tt+e2r8fhMq3cssw7q7o9t4+aoZA5Dif7DvVIclkMmk/grr/L+Vm2bFlVdrlj+u6/vvdcSikvvPBCVX7ttdcG7mMpMVcmk3Pk2pFmUWXzSrrKVWl9P125dqu/c+vR9p/JZnPbcuORXrdMHzl//nyoc+DAgaqsWUGl+HarbcTlpeg7/G5c0UyLTH5bKTHTKTOOue3rMpef5fqfy5BSet3c9T979mxVdpkSmp9SShw33HnTvJhJkyaFOpr7omNPKf7aHj16tCq7dqP79OKLL4Y6mhelGTOl+POv/cRdf60z0jkoo+2eVUp7NuNIn8ubQSZTUfNi9u/fH+q4vCLNJ3Fju45tLhtJ+42bj7r7j46Tbr+1L+t8pJTY3jQHqZRS3njjjbBMs1fmz58f6qxdu7Yqu7w8vSZuPuTmTXpt3di6aNGiquzO7ebNm2+43lJ8ppeO/y53UMdkvde4ZW4e6zJF3XlCLfM3WmYe2dVYm8kLymwr8/dxKbkMo8z9pvW+qfs5fvz4UEczpNzfOqtWrRq4P65v6XjzzW9+M9R56623qvLBgwdDHbdPKpOpiZHFf0oBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAvUsHnbsQXQ2EywTUZYJWXRhZZvuZ8Du3j7rMBdR1GT4+aD3ZwDpdd+YcteoqVNsFjWsY6NSpU0MdF5iswXbTp08PdTRE75/+6Z9CnZ/+9KdVORPGV0ruumn4pWujmTBipzV8t2U9mYDw1vDJloBK10ddYKGu2wWUKheGq0GLEyZMCHVcu9Vz5ELENYzchYjrsuyHFzTEO/sRhxat7T/TjvR6nzhxItT58Y9/PHAfp02bFpbpmDR27NhQR6+tXrNS/PnX0FAX0P6DH/ygKrvA4i1btlRlF2rugnZbQjzd/VevSVf3g1LaQly73L7qct0EnQ+mfcSNrdrfXND/yZMnw7LPfe5zVVlDtUuJ/d/NIzJB3+6+oaHpR44cCXVeeumlqjxnzpxQ54knnqjK+gGXUkr50Y9+FJbphxa0XEocE9evXx/q3HvvvVV59+7doc7x48fDMg02nzFjRqhz5syZquyuo963XRtxH8PReZu7t7799ttV2QWt633UIdS8TVdjZFd/j3W1P9l7b+bepnM093ed/i7zt0YpcbxbvHhxqKNj0po1a0Id12+UCyP/h3/4h6r8+uuvhzo632v5u/qjftfyNzL39eHhP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzl0YmIZ9uTouNHWQTBh5KTH8zdXJhLFntuVktq9aA9rcudVlmWvUpZbw2QsXLoRlEydOHPg7FyKt4ZMvvvhiqKPH/9xzz4U627dvr8rZoFutlwkIdtdWj8OtJ9snWnTVJzJtNBO+2PpRgcxHFDKB7W5bFy9erMousPHw4cNhmQa0uuPX/e4qsN1xbcsFZCvdJ3euMx+6yIxt7vprGLEL1XQhtvrRAhfGqyGeLkRXj0PDcUsp5e677w7LNFj4qaeeCnU0xHzv3r2hjgYbZ4P+M2OUXkvXHjLXsauA8Naxbpjh563bIhB1MA3t17G2lBhi7T584u7tGqJ93333hTpz586tym4+otufMmVKqOPGZA0237NnT6ijYejuoyo6jrig8UOHDoVlOm66cXvnzp1VedWqVaGOhp/rOSsl96GTbdu2hTp633T7qOO9u/6ZD224e0Tm3pqZ62bmFre71o9odDVHdevWe2lmbpOZ67Tej7L3dpX5YNHMmTPDsmXLllXlDRs2hDp63dwYqeO2fhyolFJee+21sOz555+vyu4jDi0f48n2UdX339Go8Z9SAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgdx8rU0rf83Tva+o73Zn1ZN6fdevKZFFk3nvPZOO4dbfmpajsu8iZ99yH+U57Sz6WZhyUEt/h1/yGUvy70Lou9776d77znaq8devWUEevicsUcBlCmv2QyYsaN25cqJPJJsssG2ad1pwnrZPpW5ntZ9t6JlNC1+XaqF4jt55MFo9rR9pu3Xoy79Rnx03VkoXg9tH9Ttu/o/vtMhU0U8T19RUrVoRlCxYsqMqu/+nxu2v7wQcfVGV3/Dt27AjLNC9B81vc79w9Srfn6rRmWGi/cdcs0/9GWp8Ze1lkUQyWycfT/uf6sdYpJfY3l7uk+VAuL0Xv9ZMnTw513BxFxxLNuCol3hPcGLFr164b7k8ppYwdOzYs07HV9dvz589XZZcFo/vtxt/3338/LHvvvfeqsssL0/EmkzuZpet295YWZEW1ackGKiV3v2m9J7XkNWVk24iuO7M/rv/pelzu3fz588OyWbNmVeXp06cP3P6mTZvCMs29c/lRbmzTfLhsXqnKZGplfufGn5ZMMbThP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qVT/1yIl4YGujDClhBdF+KWDX9Wur3WoGVHj98dmy7LBP21hihmzpHbfibo2YWI6nXKBNS5fdTQ0AkTJoQ6K1euDMu03gsvvBDqvPzyy1XZXSM9DtfWXRhrJlhP24gLMezqereGmGdk+r/Tsv3WDwa4fdQ26fY500cz44hrW5mPKGTWM2i92XVnPvTQOo6432m/cdvXPpEZI5ctWxaWLVmyJCybNGlSVV66dGmoo+OIhgqXEkN8L126FOq8+eabYdnmzZur8vHjx0OdTEBmJjA+o6ug2a5C1d2yLkOEW/pSVx8syW6/T+7YRts+uo8IKDcfcddE5zKuH2mfPHHiRKiT+RiFmyPo9tx+Z+4tyn0wIyMz/3H7+Pbbb1flbNCvjjeZsc7N/1vv/6OtbeP20eU9MjNHXbRoUVV2H35xf1s9/vjjVXnGjBmhzv79+6vy008/Heps2bKlKutHDkrxY4ty/b9l3tJl3+9q/oPB+E8pAAAAAAAA9I6HUgAAAAAAAOgdD6UAAAAAAADQu3SmlNOSD9NlXsTNoOV4s7/JnNtMXoZy7++6ZZlMKTV27NiwbNasWVX5/vvvD3WmTJkSluk7y88880yoo1kwmbyybH5Zaz7SsLbfVR0nm6HSomXdXeautbwf7n4zGse2TO5Z5vy7LLQMHRMyY4TLZpk+fXpVXrBgQajjMqXmzJlTlU+fPh3qaM6Yy0I4cuRIVXa5U9u2bQvLzp49W5Vdu9F8mq7yo7rUZT5Gy7a67O9drLv1fLRmwd2sMudJ62RyR1rH8UymaCYb0O2jWzas9tclPd5MNpM7R+4ekbkmLdmw5EfhVpWZI0ybNi3UWbVqVVWeOnVqqKO5U6WUMmbMmKqsc51SSvnXf/3Xqnzw4MFQR3M2W8e11txL3Bpu3dkQAAAAAAAARi0eSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85bAxu7Ch8cZtBpV7raVmvQeYYL+syESGa27661BosvX7481FmzZk1VdmF8LkR0+/btVXnr1q2hzl133VWV3bHdDEHjw9x+Rldh6K6OrjsTRut0dW6HGZjaEuo6bHpuMyGy7nq4jwho+G0maF1DPUspZfXq1VV54sSJoc7kyZMHbt8d2/Hjx6tyJsR87969oY4GfTruvI3GYPNhaR1/+uwTw5wzjMag85EO2h5tujwffZ7b1j6SCTHPaP2IxqD9KSX2m+zHEEa6bwG/KtdmNYx8xYoVoc6ECROqsvvwy9133x2W7d+/vyp///vfD3U02DzzMQo3H3T9NhNs3uccHSOLERsAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPQunSnltOTVZHKnstkww3qvdDTmV7WuW99Pbr1GmSwq9zvNfpkxY0aoM27cuKrs3jF+7733wrLXXnutKl+5ciXU0XyaO++8M9QZbZlSXWY6DFNLhlRXfavLa9TVONJVpkfrbzLH7zJtMnkdrWN0hv7uiSeeCHUmTZpUlVetWhXqTJ06NSy7fPlyVXbHtmnTpqq8ZcuWUEczFa5duxbquP6g44373a2iyzbRldb73bBk8xoxuox0O8rcI7q6t7Vuy2XIZOY2mXsLGTK4Xbh7hM5/9G+mUmKmpptr7Ny5Myx79tlnq/KpU6cG7qPr6/p3m+Z5ltL+9w+ZUrcP/lMKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F066Lw1RLh13S11bhXDPFYXIp4Jo3bBdhrI5343ffr0G5ZLKWXu3LlVec6cOaHOvn37wrJXX321KrvwPw3EcwF5emytIdou6Lg1oLtFNvy6xWgM/89sq8+g+ew+qUwbzaw307YzQeeOtm03jrhlum4N4yyllAcffLAquzFi2rRpVXnWrFkD97GUUsaPH1+Vn3766VDnlVdeqcpHjx4NdVxop3Ln8f333x/4O/0Yw/Xr1wf+BtHNMEcg6PzmNNJtK/PBiszvhvlRj+yyFpm5FuHHuBW4jzFNmDChKi9atCjU0Y+6uLnOkSNHwjL9QJT7W0/nP12Oh5kxivvm7YP/lAIAAAAAAEDveCgFAAAAAACA3vFQCgAAAAAAAL3joRQAAAAAAAB6lw46d7oKOu8q2HekwygzRnofXWBc63XMBJ1PnDixKrvA4PPnz1flT30qNsuNGzeGZe++++7A7Wv48pgxY0Kd1jDyloD+1lD/PtuNCxFtNayg9ex69Vj6Dkxsud6tga2Zbblrq+ckE9h/7dq11D5p0PiGDRtCnRkzZtzwN6WUMnv27Kr83nvvhTouIP3ZZ5+tyi+//HKoc/z48arsxqiWwPosFxA/2g0z6PRWRmArutD6UZOu5ujZD220rNvJ3CNvp3EEty43t9I50oULF0KdH/zgB1VZ/z4qJdfXMh91cWHouu7svIYPFOD/xX9KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qUzpYb5vnrr9kd6n1oyrfrcHyeTF5PJnSrFv1es7rrrrqp8zz33hDq67K233gp1nnvuubDs+vXrVdll0XT1vnKm/bVe25FuN9omXBvJtJvWTKPMerrKuRrpvK6+cy+0/bs+q/29tc+4/rdixYqq7HKfxo4dW5UnTZoU6uh+u/bw/PPPh2UvvfRSVd6/f3+o0zJuto6RLq/hZsgZuln7yLDyKoaZn4nRp6v80q6uf3Y9Xc1RMtyYrPvZVe5UNtOKnCncbFyb3bVrV1XetGlTqHPx4sWB67777rvDssuXL/8Ke/f/yeROOa1/o+qy1u1j9OM/pQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOndEWIjjMwOhMiGLfgdUt4Zuf+lS85Boid+3atYF13Lp+8YtfDPydW8+YMWOq8t69e0Odc+fOhWUakOy2r8ei23L6DuzP1LlZ90mv9zC3Ncww+kxAY2b7rUGvmf12624JiM2GeKvJkyeHZStXrqzK2mdLKWXZsmVV2Y0/J0+erMqnTp0KdZ5++umw7MiRI35nPyZ3Ptx5c2OSyoSoj7SRvte3tv+MPsPHR+O1xa9upAPrs/fIYX7opWVbI/2hEeBmdOzYsars/o5T+pGpUnKh5pkPDWXmNe6jOpnfuXsk983bB/8pBQAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAepfOlHLvi2eyMPR3LlNIf5ep0yqTu+K05sW01OmSbi9zrO69X/d+8AcffFCV3TvM+u7z7NmzQx3Ni3n99ddDnXHjxoVl2ibuuOOOgXVcO9J9bL0erXkJLbk/2XVntOZFuDYxaN2Z99WdzLYy28/UyeyPOx+ubWXWlVl3S36cq5fp227d2tddNptmQ5US+6QbI1xenNq2bVtV3rhxY6hz9uzZsCzT/jI5B5k6Tma8bbm3DTM/JiO7rZHOlGvJS3N9tqUfO2RjoAtdtUcAo0vm3vrhhx8OrKNzto+z/Zb5T+ucCbc37mwAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA7z5W0HnGMEO8MyG2w9qWW9Z6joYZUKuBmJnAehcY7oKmMyH2ly9frsrvvvtuqLNjx46qfPXq1VDHBZ1fu3atKmtguVvm6nQVdD9MXQUEtwak9hlQ7PaxNQw+sz+txzasdXcV6lxK7LeZj1G0hjG766Zjyd133x3qXLhwoSpv3bo11NmyZUtVdiGebvvXr1+vyiPdjzNaP2rQVfj5MM/RSJ/rzLG11skg6BwAACDiP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzp1M+GcmoLjPwHLndgpx/cUvfhHqaEBwa2CuhgqXUsqpU6eqsgsxfvPNNwdu3wWta4iyq6P71GWIcJ8BuU4mtDxzbVvW26WW89jVtrLbb92fzMcQumoj7ncarJz50IGrox8IGDNmTKjjQsynTJlyw/WUUsrrr79eld96661Q5/3336/Kro26/q/L3LEN80MTI22YIf5dbT8j81GNVn3etwk6BwAAiPhPKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0LtOM6VczkNLplRmPX1rzeIZ5n53lQ+U2UeXhaHb//DDD0Ods2fPVuWLFy+GOpcuXarK48aNC3XcujXXJnP+M5k62UyjliyW1vW05Edlt9+SO/Wr1LvRtrpab9Ywc7+GmXvVOo5kMmw058nlzmWysa5duzZw2c6dO0OdN954Y+B6JkyYUJUvX74c6rgx4s4776zKH3zwQajTd4ZaF7rsI31mSnXZtrvSZ6YdmVIAAADRzTcbBwAAAAAAwE2Ph1IAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB36aDz1oBgDfbMBC0PMzC87xBl1XocrWG0d9xxR9P2MuvWZZ/85CdDHQ0fdkHDGkbsrr8LP77rrrtuuC1XpzVoezQGnXcVYt5ViLHbR20Tbltap3U9mb7V1fFnw7Fb9qnLMOhMiLlyYcy6zAWN7927NyzTjxhs3bp14PZ1n9163DnScaSUOCZk+pHTOra3fOgjE4Y9zKDzzD66Y+3qIwLDvEdmfjfM4HeCzgEAACL+UwoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPTuY2VKZXNVBq0nk03jciZatp+RzYvoKueqVVdZOJnck0wWi8uU0gwNl/uidVzuxtixYwfuk8vL0e253KlMpllXeSVOV7lPXeVOufVklrk6mg+U2b5rR5n9cbRtZ7JwWjNl3D5lMmRaxrHs2KPrdu0/s496HV3G29GjR8OyEydOVOX33nsv1BkzZkxVdtdf+7ZmxZXiz+P169erssvYy+QeZnSVKeUMMwsxs63MPdrJjG2ZPjpovR+lJQsrO/61IFMKGN3cGKH9NlPHLWuto9vLrAcAbjb8pxQAAAAAAAB6x0MpAAAAAAAA9I6HUgAAAAAAAOgdD6UAAAAAAADQu3TQudNV0LmG+LUGnbcGj490iOwwaUCwO7cu/DijJcQ3c21dHRd0rsfmQoxbQtxbQ3wzQZMuxLk1RLgloLt1+26ZrmuYIc6t5ygTYt0y/rQeq2sjmd9lxja3nszxa4i525bWcdyHBt5///2qrKHmpZRy9erVquz68bhx4wZuK/MRA3f+u/rQQFdB510Gn7fcE7vcfp9B55l1O30GnTuZNgKMpJbA7tbfZULEh7l9AMDI4D+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA79KZUl3lxQwzL6M1Uyqz/dZMl5Y6rdw+6vZcNkxrhofLdRn0O5f7cueddw7cH7ctXabrKaWU69evV2WXTZXJ1HJZTCqTO9LaRlrbTSbTpTVTS7X2v8zvWjOdMsfSkumSPUdd5VVlspEy58S147vuuqsqu/7Q0tfdut0+ap90Y9S1a9cG7o/r23pudT2ldJcplbkmXfXt7D1K62XqZLaXXc+wMqWy5zGz3y3Xv8v7uLabzL0G/eoq06irvKRhbr+reTQAAL8K/lMKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6N0nfkmqIQAAAAAAAHrGf0oBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd/8bQYaLGb7jjI0AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "check_data = first(train_loader)\n", + "print(f\"batch shape: {check_data['image'].shape}\")\n", + "image_visualisation = torch.cat(\n", + " [check_data[\"image\"][0, 0], check_data[\"image\"][1, 0], check_data[\"image\"][2, 0], check_data[\"image\"][3, 0]], dim=1\n", + ")\n", + "plt.figure(\"training images\", (12, 6))\n", + "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "08428bc6", + "metadata": {}, + "source": [ + "### Define network, scheduler, optimizer, and inferer\n", + "At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", + "the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms\n", + "in the 3rd level, each with 1 attention head (`num_head_channels=64`).\n", + "\n", + "In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use:\n", + "\n", + "`\n", + "with_conditioning=True,\n", + "cross_attention_dim=1,\n", + "`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bee5913e", + "metadata": { + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "device = torch.device(\"cuda\")\n", + "\n", + "model = DiffusionModelUNet(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=1,\n", + " num_channels=(64, 64, 64),\n", + " attention_levels=(False, False, True),\n", + " num_res_blocks=1,\n", + " num_head_channels=64,\n", + " with_conditioning=True,\n", + " cross_attention_dim=1,\n", + ")\n", + "model.to(device)\n", + "\n", + "scheduler = DDPMScheduler(\n", + " num_train_timesteps=1000,\n", + ")\n", + "\n", + "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", + "\n", + "inferer = DiffusionInferer(scheduler)" + ] + }, + { + "cell_type": "markdown", + "id": "2a4d3ab2", + "metadata": {}, + "source": [ + "### Model training\n", + "Here, we are training our model for 75 epochs (training time: ~50 minutes)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6c0ed909", + "metadata": { + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 0 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 125/125 [00:27<00:00, 4.61it/s, loss=0.723]\n", + "Epoch 1: 100%|██████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.276]\n", + "Epoch 2: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0965]\n", + "Epoch 3: 100%|█████████| 125/125 [00:27<00:00, 4.62it/s, loss=0.0376]\n", + "Epoch 4: 100%|█████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0224]\n", + "Epoch 5: 100%|█████████| 125/125 [00:27<00:00, 4.47it/s, loss=0.0187]\n", + "Epoch 6: 100%|█████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0179]\n", + "Epoch 7: 100%|█████████| 125/125 [00:28<00:00, 4.44it/s, loss=0.0169]\n", + "Epoch 8: 100%|█████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0161]\n", + "Epoch 9: 100%|██████████| 125/125 [00:27<00:00, 4.50it/s, loss=0.016]\n", + "Epoch 10: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0156]\n", + "Epoch 11: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0152]\n", + "Epoch 12: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0152]\n", + "Epoch 13: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0151]\n", + "Epoch 14: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0147]\n", + "Epoch 15: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0151]\n", + "Epoch 16: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0151]\n", + "Epoch 17: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0146]\n", + "Epoch 18: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0144]\n", + "Epoch 19: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0143]\n", + "Epoch 20: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0145]\n", + "Epoch 21: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0143]\n", + "Epoch 22: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0138]\n", + "Epoch 23: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 24: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0134]\n", + "Epoch 25: 100%|████████| 125/125 [00:27<00:00, 4.49it/s, loss=0.0135]\n", + "Epoch 26: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 27: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0136]\n", + "Epoch 28: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0135]\n", + "Epoch 29: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0131]\n", + "Epoch 30: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0128]\n", + "Epoch 31: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0129]\n", + "Epoch 32: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0128]\n", + "Epoch 33: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 34: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0138]\n", + "Epoch 35: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0131]\n", + "Epoch 36: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0132]\n", + "Epoch 37: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", + "Epoch 38: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0124]\n", + "Epoch 39: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0124]\n", + "Epoch 40: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0132]\n", + "Epoch 41: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0128]\n", + "Epoch 42: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0122]\n", + "Epoch 43: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", + "Epoch 44: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0129]\n", + "Epoch 45: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0132]\n", + "Epoch 46: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0125]\n", + "Epoch 47: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0123]\n", + "Epoch 48: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0123]\n", + "Epoch 49: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0125]\n", + "Epoch 50: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", + "Epoch 51: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", + "Epoch 52: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0124]\n", + "Epoch 53: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", + "Epoch 54: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0123]\n", + "Epoch 55: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", + "Epoch 56: 100%|█████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.012]\n", + "Epoch 57: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0126]\n", + "Epoch 58: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", + "Epoch 59: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0126]\n", + "Epoch 60: 100%|████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.0119]\n", + "Epoch 61: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0122]\n", + "Epoch 62: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0119]\n", + "Epoch 63: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0125]\n", + "Epoch 64: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", + "Epoch 65: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0121]\n", + "Epoch 66: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0117]\n", + "Epoch 67: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", + "Epoch 68: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0123]\n", + "Epoch 69: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", + "Epoch 70: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.012]\n", + "Epoch 71: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0118]\n", + "Epoch 72: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0117]\n", + "Epoch 73: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0119]\n", + "Epoch 74: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0125]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train completed, total time: 2074.1517136096954.\n" + ] + } + ], + "source": [ + "n_epochs = 75\n", + "val_interval = 5\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, batch in progress_bar:\n", + " images = batch[\"image\"].to(device)\n", + " classes = batch[\"class\"].to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + "\n", + " epoch_loss += loss.item()\n", + "\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"loss\": epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " for step, batch in enumerate(val_loader):\n", + " images = batch[\"image\"].to(device)\n", + " classes = batch[\"class\"].to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train completed, total time: {total_time}.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "### Learning curves" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f8385176", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAArsAAAILCAYAAADoqVT3AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABsNklEQVR4nO3deXwTdf7H8ffk6EFLkVuEolYERESrKKgccixWLu9rVwFBRfFAcFVcF11cZAEX3Z8WVlyVXV1PVNBVAQURUVEsKogKHhSkgCA3PWiTzPz+SJM2toVOm5Jp+3o+Hn00+WZm8s2nQd/55jvfMSzLsgQAAADUQa5YdwAAAACoKYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AOMJef/11dejQQR06dFBOTk6suwMAdZon1h0AUHdde+21WrlypTp27Kg33ngj1t1xjEaNGqljx46SJK/XG+PeVE1RUZHeeecdLV++XF9//bV2796tgoICNWjQQG3atFGXLl10wQUXqHv37rHuKoB6zrAsy4p1JwDUTYTdumnhwoX629/+pl9++UVSMLA3b95cDRo00K5du7Rnz57wtmeccYamTp2qtm3bxqq7AOo5RnYBAJX2xBNP6NFHH5UkpaWl6bbbblPv3r2VlJQU3ub777/X008/rTfffFOrVq3SVVddpf/+979KS0uLVbcB1GPM2QUAVMrixYvDQXfAgAF64403NHDgwIigK0nt27fXtGnTNHPmTHk8Hu3atUvjx4+XaZqx6DaAeo6wC8DRsrOz9eCDD+qCCy7QaaedptNOO039+vXTvffeq7Vr1x5y302bNumhhx7SkCFDlJ6erk6dOumss87SH/7wBz3//PPy+/3l7hc6eeztt9/WDz/8oOuuu05du3bVlVdeGd7m2muvVYcOHXT//fdLkpYtW6ZRo0apR48e6ty5s3r06KE777xTGzZsKHP8ik5Qy8nJCbd/9dVXKigo0BNPPKEhQ4bo9NNP16mnnqrBgwcrMzNTRUVF5fbd5/PpmWee0cUXX6z09HR17dpVV111ld58801J0osvvhh+Djt8Pp8mT54sSWrXrp1mzJihuLi4Q+7Tt29fDRs2THFxcTr66KP166+/hh+bMGGCOnTooL59+1a4f2XrlJWVpSuvvFLp6ekaP368/vnPf4Yf37p16yH7OGLECHXo0EEZGRllHluxYoXuuOMO9enTR507d1bXrl01ZMgQPfzwwxGv5bd++eUXTZs2TRdeeKFOP/10de7cWT179tQVV1yhf/3rX9q9e/ch+wQgupjGAMCx5s+fr4kTJ6qoqEiGYahly5ayLEs5OTnKycnR/PnzNX78eN1www1l9l2yZInuuOOOcCg8+uijlZiYqG3btikrK0tZWVl655139PTTTyshIaHc58/NzdX111+vPXv2qE2bNmrYsGG52z399NOaPn26kpOTdfTRR8s0Tf3666966623tGzZMs2bN0+pqam2XntBQYGGDx+u1atXq1WrVjr66KOVk5OjH374QT/88IPWrl2rJ554ImKfwsJCjRo1Sp9//rkkqUGDBmrevLl+/PFH3XXXXfr88891wgkn2OpHyMKFC7Vt2zZJ0h133HHYoBty++2365ZbblFycnKVnvdwtm7dqokTJ8o0TbVu3VqJiYkaPHiw/vGPf0iSFi1apOuuu67cfXft2qWVK1dKki688MJwu2VZ+utf/6rnn39eUnBOcqtWrbR//359//33+v777/XSSy8pMzNTZ599dsQxV69erZEjRyo3N1eS1KJFCx199NHavXu3Vq9erdWrV2vOnDl67rnnqvy3AGAPI7sAHGnVqlW67777VFRUpIyMDC1btkzLli3Thx9+qBUrVujCCy+UaZr6+9//riVLlkTse+DAAd1zzz0qKipS+/bt9d5772nZsmVauHChVq1apXHjxkmSsrKy9NRTT1XYh7lz56pVq1b64IMP9M4775S77Zo1a/R///d/mjRpkj799FO9/fbb+uSTT/Twww+H+/Kvf/3L9ut/+OGHlZeXp3nz5oWff8WKFfrd734nSVq6dKm+/PLLiH2eeOKJcNC97rrr9Omnn2rBggVasWKF7r33Xr366qtasGCB7b5I0vLlyyVJDRs2VJ8+fSq9X2JiYo0FXUl65pln1L17d3300Ud666239NBDDyk1NVXp6emSgmG3IgsXLlQgEJBhGBo6dGi4/V//+peef/55GYahO+64QytXrtR7772nzz77TG+88YZOOeUU5ebm6pZbbtH27dsjjnn//fcrNzdXJ598st577z0tX75c77zzjj799FPNnTtXxx9/vHbt2qUHHnigZgoCoAzCLgBHmjFjhvx+v8444ww9+uijatmyZfixJk2aaPr06Tr33HMlKTyKF/Lxxx+HR9buueeeiJUAPB6PbrrpJp155pmSpHfeeafCPnzzzTeaPn26mjRpUuE23333nW6//XZdddVVEcuIDR06VGeddZYk6bPPPqvkqy7x008/6cknn1SnTp3CbUlJSZowYUL4funjBgIBvfDCC5KCKyBMmDBB8fHxkoIjkyNGjNAtt9yir776ynZfpGCol6RTTjlFHo9zvhTcsGGDpk6dWmbe8JAhQyRJX331VXjViN8KBf+uXbuqdevWkqT9+/eHR8xvvPFG3XzzzWrQoEF4n44dO+qZZ55RkyZNlJeXF/FBZu/evVq3bp0kafTo0WVWoOjSpYv+9re/6cwzz1SbNm0qnIoCILoIuwAcZ+vWrVq1apWk4JxKl6v8/1T94Q9/kBQ8+3/Tpk3h9oyMDK1du1Yffvhhheu8nnLKKZKkzZs3V9iPzp07H3bJLK/Xq6uvvrrcx0Jr6VYUtg6lf//+4QBWWunpFKWPu3btWu3du1eSdPHFF5d7zOuuuy4iuNkRmmd69NFHV2n/mtKrV69yp5dccMEF8ng8siyr3NHd7du3h99jpacwvP/++8rLy5PL5dKIESPKfc6UlJTwSPC7774bbi+9kueOHTvK3Tc9PV3//e9/NXXq1EpPBQFQPYRdAI5T+uv5E088scLtTj311PDtb775JuIxj8ejli1bVjgKGQp9hxpdq8ycyrS0tDKjiiGhr+8PHjx42OP8ViiMH+q4hYWF4bbs7Ozw7ZNPPrnc/ZKSknTGGWfY7osk5eXlSQpOS3CSiv5GTZo0UY8ePSSVP5Vh4cKFMk1T8fHxESenffHFF5Kkpk2bHnJEP/Te2759u3bt2iVJaty4sU466SRJ0tSpUzVjxoxyT1AEcGQ557soAChW+kz38s6SL89v5076/X699dZbWrRokTZs2KDdu3dr//79tvrRuHHjw26TkpJS4WMVjUhXRkUnw5U+bumRxFDgkqRmzZpVuO/xxx8fnn9rR3Jysvbu3asDBw7Y3rcmHSqQDhkyRB988IG++OIL7dixQy1atAg/FprC0Ldv34ha79y5U1LwPVjZFSt++eUXNW3aVFJwrvXIkSO1Y8cOPfnkk3ryySd1zDHHqFu3burRo4f69OlT4YcjADWDsAvAcQoKCsK327dvX6nQWPrr+QMHDuj666+PmJ/avHlztWvXLjzSu3PnznCwqcwxK2IYxmG3qQq7xy09enyor8erOo2hRYsW2rt3b8R0ESc41Ehzv3791KBBA+Xn5+vdd9/VNddcIyk4TSb03rjooosi9gm99+Lj43X88cfb7s+JJ56ohQsX6qWXXtKrr76qDRs2aOvWrZo3b57mzZunpKQkjRw5UmPGjKnWhyEAlUfYBeA4pQPZrFmzbC/bNXXq1HCYGTZsmIYPH642bdpEbPP4448rMzOz2n11itInx1W0frBUtSkVUvBr+++//17ffvutcnNza3SFhZDqXs0+MTFR/fv315tvvqmFCxeGw+6CBQtkWVbEVIeQ0HuvSZMmVb7EdVJSkkaNGqVRo0Zp8+bN+vjjj/XJJ59o+fLlysvL0+OPP66tW7dqypQp1Xp9ACqHj5UAHKf0SVB2T+4KBAJ66623JEk9e/bUfffdVyboStK+ffuq10mHadSoUfj2oS5aUHpurx29e/eWFLy4xGuvvVbp/UzT1OTJk8usAhEauT5UoI3GlInQqgyrVq0Kj+SHVuAYNGhQmTndoffer7/+GpUrvqWmpuqqq67SY489pg8//FDnn3++JOm1115jPi9whBB2AThOly5dwrcPtVSWZVllRjF3794dHr0MLS/2W6ZpVmneqpOVXjXihx9+KHeb/Px8ZWVlVen4ffv2DX9omDVr1iGvIFbas88+q+eee05XXnmlPv7443B7aKpFaIm48nz99ddV6mtp55xzjpo2bSrTNPXBBx8oJycnfOW9305hkEpODPT7/Ye8Qp/P56vwsYpG1hs2bKi//OUv4fvff/99JV4BgOoi7AJwnFatWoVXDXjhhRfCKwH81htvvKGzzz5bd911lwKBgCRFXA2tohHOp59+OmLuaelVDWqrU089NRwg33777XK3+c9//lNhLQ/H7XZr0qRJMgxDe/fu1ejRow972dv58+dr+vTpkqRzzz1X55xzTvix0Ajq/v37tWXLljL7btmyRYsXL65SX0vzeDwaOHCgJOnDDz8MH/OEE05Q586dy2zfv3//8FSGJ598ssLjPvDAA+rTp0/EOrv//ve/1atXL914440V7lc6CHOiGnBkEHYBONKdd94pl8ulrVu36oYbbogIp0VFRXrllVf0wAMPaP/+/UpOTpbb7ZYUHD1r3769pOBXxaWXJNu5c6ceeughZWZm6pZbbgm3V3W000kaNGigQYMGSQpeKvnpp58OfwDw+Xz697//rccff7zC0e7K6NGjhyZMmCDDMPTNN99o8ODBev7557Vnz56I7b777jvdcccdmjBhggKBgDp16qRHHnkk4qS70AU3JGnatGkRJyWuW7dON954Y/jvWF2hqQyffPJJ+Gp7pdfWLS05OVk333yzJOm9997TX/7yl4gpL7t379bf/vY3vfbaa9q6dWvEFJn27dtr+/bt+vjjjzVp0qQya+1u2bIlfFGQJk2aVOtvAaDyOEENQI3Lzs6uMFyUdtVVV4Uv0HDGGWdoypQpmjhxolatWqUBAwaodevW8nq9+uWXX8JTFc455xzdfffdEce58847dfPNN+vAgQO69NJLdcwxx8gwDG3dulVut1vTp09Xenq6Zs+eLZ/Pp5tuukmpqanKzMxUWlpa9AtwhNx5551auXKltmzZounTp2vWrFk6+uij9csvvyg3N1e33XabLMsKX1K4KkaMGKHU1FQ99NBD2rJlix588EFNnjxZzZs3V0pKinbu3BkOv263W5dddpnuueeeMqOYZ5xxhnr37q1ly5Zp0aJF+vDDD9W6dWsVFBRoy5YtOuWUU/THP/5Rw4YNq1ZNpOCod9u2bfXzzz9r5cqVZS4P/FvXX3+9tmzZopdeekkvvviiXn31VbVu3VpFRUXasWNHeHR29OjRuuCCC8L7nXPOObrpppv0xBNP6IUXXtBLL70Ursv+/fvDy+M1aNBAM2bMiPgWAkDNIewCqHGFhYXhy6geym+XArv44ot1xhln6D//+Y9WrFihbdu2yefz6aijjlL37t114YUX6oILLiizTNd5552nZ555Rk8++aS+/vpr/fLLL2rSpImGDBmikSNHhq9s9uCDDyozM1M7duyQZVmOu2CCXc2bN9drr72mWbNmaenSpdq+fbv27dun0047Tdddd5169Oihxx57TFL1lkzr16+fevbsqXfeeUcffvihvvnmG+3atUu7du1ScnKy0tPT1b17d1100UU67rjjKjxOZmamZs+erYULF2rz5s3hkdKxY8fquuuu048//ljlPv7WkCFDNHPmTEnBUeVWrVpVuK3L5dKkSZM0cOBAvfzyy/ryyy+1detWGYYRnmJz9dVX67TTTiuz77hx43Teeedp/vz5WrFihbZv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9BctWPCWJOn222+SJM2d+6a2bduq22+/Sdddd4NGjRodPubq1V/pueee0TffrFVBQb4aN26iM8/sppEjb9TRR7eK6HtyckNNnTpDmZmP6quvvlBRkU/HH5+m0aNv0emnd61EtaOPsOsAXo9bEieoAQDgVAMHDtG//jVLb775erlh9403XldCQoIGDhyqSZP+rI8+WqZhw0bqrLO66+DBg3r55Rc0bdpkFRUV2ZrmsGPHdt1++81KSEjQ2LF/1LHHttXmzdmaMOGPKiryKTGxZNtfftmmMWNuUHJysm67bZxatTpGmzf/rCeeyNTtt9+k//znRbVo0VIjR94oj8er//1vnv74x3vVseNJatasubZt21rm+T/99BPdc884nXhiB40ff7eaN2+hjRs36KmnntBnn63Qv//9gho3bhLePj8/T3/841hlZAzUpZdeqS1bNisz8x/605/+qJdfnq9GjY6yVfdoIOw6QGjOLheVAADUVl/sWKO3NryrwkCh7X1dLkOmadVAr0rEu+M1OG2ATm/RpUr7Jycna8CAC/TGG6/r669X65RTTg0/9t133+j779dpyJCLFB8fJ4/Hoyuu+L1uuOHm8DYnn3yKBg3qpwUL3rIVdufNe1X5+Xm6774H1Lt3X3k8LvXvf548nnhNnjxJjRo1Cm+7efMmdelymi699Ap163a2JOmUU05VQUGBHn10upYvX6ZLL71CrVodo2bNmkmS2rY9Vh07dqrw+TMz/6GEhAQ98sjjSkkJPtdpp52uo45qrD//+R69/PILuummW8Pbb926Rffd9xddcMFgSVJ6+hnatGmTXnzxOWVlrVS/fgMq/dqjhbDrAKFpDAHTUsA05XYxlRoAULss3rRM2/N3xLobh7T452VVDrtS8MSzN954XfPnvxYRdkMnpl1yyRWKj0/QX/86tcy+ycnJatq0mX75ZZut5/z669UyDEPdup0T0d6nTz899NCDEW1nntldZ57Zvcwxjj32OEnS9u32nnvHju3auHGDevXqEw66Ieee20tut1urVn0e0W4Yhvr27R/R1qZNqiRp3759tp4/Wgi7DlD6whJ+vyV3XAw7AwBAFfQ/trfjR3b7t+1drWOkpbXTaaedrqVLl2js2DuVktJIeXm5WrLkXZ1yyqnhObHff79Or776slat+lx79uxWUVFR+BilR2IrY9eunUpK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82JTU1N133336ZJLLlFiIuvBVRZXUAMAAIiOal9BTZISEhKUkJBQ7mPXXnttNJ6iXmHOLgAAQHRU+1pz3377raZMmRLRtm7dOl1zzTVKT0/XoEGDtHDhwuo+Tb3idhkKXcCNsAsAAFB11Qq769ev1zXXXKMXXnhBphkMZfv379fIkSOVlZWluLg4bdiwQePHj9eqVaui0uH6wDCM8Ogu0xgAAACqrlph95lnnpHf79esWbPClwueO3eudu/erd///vf67LPPtGjRIqWkpOjZZ5+NSofri9C8XUZ2AQAAqq5aYffzzz/XgAED1KtXr3Dbe++9J4/HE740b9u2bTVgwAB9+eWX1etpPRMe2SXsAgAAVFm1wu7OnTvVrl278P28vDytXbtWp556qpo0aRJub926tXbv3l2dp6p3CLsAAADVV62w63a7VVhYGL6/cuVK+f3+MpcLLigoYOkxm7wetyTm7AIAAFRHtcLuscceqxUrVoTvv/jiizIMQ+edd17Edl9//bVatmxZnaeqd0Jzdv2M7AIAAFRZtdbZHTBggB577DFdddVVcrlc+vLLL3XaaaepU6dOkqRAIKAXX3xRK1as0MiRI6PS4foiNI0hYFoKmKbcrmqvEgcAAFDvVCvsjho1SqtWrdLHH38sSWrVqpWmT58efnzjxo2aPHmyjjnmGMKuTaUvLOH3W3LHxbAzAAAAtVS1wm58fLyefvppbdy4Ufv371fHjh0VF1eSytLS0jRixAhdd911ESes4fAirqIWMBUvdwx7AwAAUDtF5XLBxx13XLnthmFowoQJ0XiKeic0Z1diRQYAAICqikrY/eWXX7Rw4UJ9++232rNnjwzDUNOmTdW5c2cNHDhQjRs3jsbT1CsRI7v+QAx7AgAAUHtVO+z++9//1owZM+T3+2VZVsRj8+fP14wZM/Tggw9q8ODB1X2qesXjYWQXAACguqoVdpctW6apU6cqMTFRF154obp06aImTZrINE3t3r1bq1at0qJFizRhwgS1bdtWXbp0iVa/67zSI7tFhF0AAIAqqVbYfe6559SoUSO98sorOvbYY8s8ftVVV+mGG27Q1VdfraeeekqPPfZYdZ6uXmHOLgAAQPVVa/HWtWvX6vzzzy836Ia0b99e559/vr744ovqPFW989vVGAAAAGBftcJubm6ujj766MNu16ZNG+3du7c6T1XvxDFnFwAAoNqqFXZTUlK0efPmw263detWpaSkVOep6h2vp2RdXS4ZDAAAUDXVCrunnnqq3n33Xa1fv77CbdatW6cFCxbotNNOq85T1TteRnYBAACqrVonqF133XX64IMPdPnll2vQoEFKT08PXylt165dysrK0qJFixQIBDRq1KiodLi+YM4uAABA9VUr7J511ll68MEH9dBDD2nevHmaP39+xOOWZSkxMVGTJ0/WGWecUZ2nqndYjQEAAKD6qn1Ricsvv1x9+vTRO++8o7Vr12rXrl3hK6idcsopGjRoEFdQqwKmMQAAAFRfVC4X3KxZMw0bNqzCx5csWaJ58+YpMzMzGk9XL3i4XDAAAEC1VesEtcratGmTlixZciSeqs5gzi4AAED1HZGwC/uYswsAAFB9hF2HYs4uAABA9RF2HYqwCwAAUH2EXYdizi4AAED1EXYdijm7AAAA1UfYdSimMQAAAFSf7XV2zz77bNtPcvDgQdv71HeEXQAAgOqzHXb37NlTpScyDKNK+9VXzNkFAACoPtthl4tDHBlul0suw5BpWfL5CLsAAABVYTvstm7duib6gXJ4PS4V+gKM7AIAAFQRJ6g5WGgqg88fiHFPAAAAaifCroOVhF1GdgEAAKqCsOtghF0AAIDqIew6WDjsMmcXAACgSgi7Dha6iprPb8qyrBj3BgAAoPZxdNidO3euBg4cqM6dO6tnz56aNm2afD5fhdvv2bNHkydPVt++fdW5c2edd955mjZtWq29qEVoZNeypIBJ2AUAALDL9tJjR8r8+fM1ceJETZgwQf369dP69es1ceJE5efna9KkSWW2N01T119/vfLz8/XQQw+pTZs2ysrK0v33369ff/1Vf//732PwKqrnt1dR87gd/dkEAADAcRwbdjMzMzVo0CCNGDFCkpSamqqdO3dq0qRJGjNmjFq2bBmx/XfffadNmzZp1qxZOuuss8L7ZGVlacGCBbIsq9Zdxc3rjryKWmIM+wIAAFAbOXKocOPGjdq8ebN69+4d0d6rVy+Zpqnly5eX2efkk09WVlZWOOiGuFwuud3uWhd0pciRXT8rMgAAANjmyJHd7OxsSVLbtm0j2lu1aiWv16sNGzYc9hh+v1/vv/++3nrrLd122222nt/lMuRyRS8cu4tHaN02pyHEed3h26Ykj8eRn02irqr1qq+olz3U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9hj00jleffVVXXjhhUpPT1efPn305z//Wbt27Qo//sMPP+j6669Xenq60tPTdcMNN+inn36KYY9jxzRNPfPMMxo8eLC6dOmibt26aezYsdqyZUt4G95jFRs5cqQ6dOignJyccBv1KtG3b1916NChzM/gwYPD21CvsnJycnTrrbfq9NNP15lnnqkxY8Zo69at4cepWVBOTk6576/Qz+uvvy6JelWahRozb948q0OHDtacOXOsn3/+2Xrvvfes7t27W/fff3+suxZze/bssUaPHm316NHDOv3006077rgj4vGffvrJ6tKli3XPPfdYP/30k5WVlWVdfvnl1uDBg62ioqIY9Tq2nnnmGatjx47W008/bW3cuNFatmyZ1atXL+vqq6+2TNO0du/ebXXv3t0aNWqUtW7dOuvrr7+2Ro8ebZ177rnWvn37Yt39I27KlCnWaaedZs2fP9/6+eefrY8++sjq16+f1bdvX6uwsJD32CHMnTvX6tSpk9W+fXtr8+bNlmXxb/K3+vTpY02dOtXasWNHxM/u3bsty6Je5dm3b5/Vp08f66abbrK+//57a/Xq1dbFF19sZWRkWIFAgJqV4vf7y7y3duzYYb3xxhtW586drU2bNlEvGwi7Nahfv37W+PHjI9pefPFFq2PHjtYvv/wSo145w3PPPWdde+211s6dO60+ffqUCbsTJkywevfubfl8vnDbTz/9ZLVv39763//+d6S7G3OmaVrnnnuuNWHChIj2l19+2Wrfvr313XffWY8//rh16qmnWnv37g0/vnfvXqtLly7WE088caS7HFM+n88677zzrMzMzIj2+fPnW+3bt7fWrFnDe6wC27dvt7p27WpNmjQpIuxSr0h9+vSxHnvssQofp15lZWZmWueee65VUFAQbsvOzrYWLFhgHTx4kJodRlFRkZWRkWE9/PDDlmXxHrODaQw1ZOPGjdq8ebN69+4d0d6rVy+Zpqnly5fHqGfO0Lt3b82ZM0dNmzYt9/GPPvpIPXr0kMfjCbelpaWpTZs2+vDDD49UNx3DMAy99dZb+tOf/hTR3rJlS0lSXl6ePvroI6Wnp6tRo0bhxxs1aqRTTz213tXM4/Fo6dKluuWWWyLaXa7gf/K8Xi/vsQo8+OCDSk9P1/nnnx/RTr3soV5lvfvuu+rfv78SEhLCbccdd5wyMjIUHx9PzQ7jP//5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8tSoUSOlp6dr1KhR5Y4a//LLL5o1a5Y+/PBD7dy5U40aNVKfPn1066236uijjy63bytWrNBjjz2mdevWSZJOPvlkjR8/XqeffnrEdl9++aWeeuoprV69Wnv27FFycrJSU1M1ZMgQXXPNNXK73dUvFABUAmEXABzqs88+04IFC/S73/1OPXr0UE5Ojt58803ddNNNmjVrlvr27Rve9rHHHtPMmTPVuHFjDRgwQC1bttTPP/+sRYsW6f3339eMGTN0wQUXhLffsGGDrrrqKhUUFGjo0KFq06aNfvzxR7322mt67733NHfuXLVt2zaiP5988omeeeYZDR06VL1799aKFSv06aefatSoUXrnnXfUqlUrSVJWVpaGDx+uhIQEXXDBBWrdurUOHDigZcuWacqUKVq9erUeeeSRI1NEALAAADVi8+bNVvv27a2zzjrL1n733HOP1b59e6tDhw7W8uXLIx579dVXrfbt21sZGRnhtm+++cbq0KGDddZZZ1nbtm2L2P7zzz+3OnbsaJ155plWfn5+uP2SSy6x2rdvX+b4//3vf6327dtbo0ePDrddc801Vvv27a3u3btb2dnZ4XbTNK0RI0ZY7du3t+bMmRNuHz9+vNW+fXvrgw8+iDh2UVGRdfXVV1tnnHGGtXXrVls1AYCqYmQXAGqYZVnKyck55DZer1ctW7aMaEtPT1ePHj0i2i666CJNmzZNGzZs0ObNm5Wamqr58+fLsiz9/ve/LzP9oGvXrurWrZtWrFih5cuXa8CAAfruu++0du1adezYsczxL730Um3ZskUtWrQo08crrrhCxx13XPi+YRjq2bOnPvnkE23ZsiXcvm/fPkkqM1XB6/Xq2WeflcfD/3oAHDn8FwcAati+ffsOuwRZx44d9cYbb0S0/XYerBQMkMcff7y++uorbdiwQampqVq7dm2F20tSly5dtGLFCn3zzTcaMGBAeL3ck046qcy2CQkJuvvuu8s9TufOncu0paSkSJJyc3PDbX369NHy5cs1fvx4jRo1Sv3799cJJ5wgSQRdAEcc/9UBgBqWlJSk6dOnH3Kb5OTkMm1NmzYtd9ujjjpKkrR//35J0q5duw65fZMmTSRJe/bsidg+FFQrq7ztXa7gtYksywq3/eEPf1BeXp6eeOIJPfLII3rkkUfUvHlz9ejRQxdffLG6detm63kBoDoIuwBQw7xer/r37297v1CQ/C3TNCUFlzSTgtMJpMjAWd72oe1Cxy0qKrLdp8q68cYbdfXVV+uDDz7QRx99pI8//ljz5s3TvHnzdPnll2vy5Mk19twAUBqXCwYAhwqNxP7W3r17JZWM5IZ+h0Zsf2v37t3lbh9qrykNGzbUkCFDNG3aNC1fvlxPP/20WrZsqblz52rFihU1+twAEELYBQCHWr16dZk2v9+v7OxsSVKbNm0kSaeccookadWqVeUe54svvojYLvQ7KytLgUAgYlvTNHXHHXfo9ttvl9/vr1K/9+3bF3HCmhQcVe7Ro4euv/56SdI333xTpWMDgF2EXQBwqM8++0yff/55RNvrr7+uAwcOqFOnTuHVGy699FK5XC699NJL2rZtW8T2H3/8sVatWqWWLVuGV17o0KGDTj75ZO3atUuvv/56xPbvvPOOFixYoLy8vCqdTLZnzx6dc845uu6668KrMpQWCrmhNXkBoKYxZxcAalhhYaGefvrpw24XHx+va665Jnz/wgsv1I033qh+/frp+OOPD19Uwu1266677gpvd+KJJ+qOO+7QI488oksuuUQZGRlq2rSpNmzYoPfee08JCQmaNm2avF5veJ+HHnpI1157re6//3599tlnOuGEE/TTTz9pwYIFSk5OrnBFhsNp3Lixbr75Zj3++OMaNGiQ+vfvr6OPPloFBQX64osvtHLlSp188sn63e9+V6XjA4BdhF0AqGEFBQWHXY1BCs5xLR12O3furEsvvVSZmZlaunSpTNNUly5ddNttt+mcc86J2Hf06NFq166dnnvuOb311lsqKChQkyZNlJGREX6stJNOOknz5s1TZmamPvnkEy1cuFCNGjXSoEGDdOutt5a5epodt956qzp06KBXXnlFixcv1t69e+X1enXcccfp9ttv1/DhwxUXF1fl4wOAHYZV0em7AICYmDBhgubNm6eJEydGhF8AgH3M2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdxQlqAAAAqLMY2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ31/+1MPX1gW6EJAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.style.use(\"seaborn-v0_8\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + ")\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0cd48c2d", + "metadata": {}, + "source": [ + "### Sampling process with classifier-free guidance\n", + "In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`).\n", + "Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f71e4924", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:14<00:00, 71.08it/s]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model.eval()\n", + "guidance_scale = 7.0\n", + "conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device)\n", + "\n", + "noise = torch.randn((1, 1, 64, 64))\n", + "noise = noise.to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "progress_bar = tqdm(scheduler.timesteps)\n", + "for t in progress_bar:\n", + " with autocast(enabled=True):\n", + " with torch.no_grad():\n", + " noise_input = torch.cat([noise] * 2)\n", + " model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning)\n", + " noise_pred_uncond, noise_pred_text = model_output.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " noise, _ = scheduler.step(noise_pred, t, noise)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3483b097", + "metadata": {}, + "source": [ + "### Cleanup data directory\n", + "\n", + "Remove directory if a temporary was used." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b00d4f9a", + "metadata": {}, + "outputs": [], + "source": [ + "if directory is None:\n", + " shutil.rmtree(root_dir)" + ] + } + ], + "metadata": { + "jupytext": { + "formats": "py:percent,ipynb" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py new file mode 100644 index 00000000..ad765369 --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py @@ -0,0 +1,337 @@ +# --- +# jupyter: +# jupytext: +# formats: py:percent,ipynb +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.14.1 +# kernelspec: +# display_name: Python 3 +# language: python +# name: python3 +# --- + +# %% [markdown] +# # Anomaly Detection with classifier guidance +# +# This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. +# +# +# [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 + +# +# TODO: Add Open in Colab +# +# ## Setup environment + +# %% +# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" +# !python -c "import matplotlib" || pip install -q matplotlib +# %matplotlib inline + +# %% [markdown] +# ## Setup imports + +# %% jupyter={"outputs_hidden": false} +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import shutil +import tempfile +import time + +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from monai import transforms +from monai.apps import MedNISTDataset +from monai.config import print_config +from monai.data import CacheDataset, DataLoader +from monai.utils import first, set_determinism +from torch.cuda.amp import GradScaler, autocast +from tqdm import tqdm + +from generative.inferers import DiffusionInferer + +# TODO: Add right import reference after deployed +from generative.networks.nets import DiffusionModelUNet +from generative.schedulers import DDPMScheduler + +print_config() + +# %% [markdown] +# ## Setup data directory + +# %% jupyter={"outputs_hidden": false} +directory = os.environ.get("MONAI_DATA_DIRECTORY") +root_dir = tempfile.mkdtemp() if directory is None else directory +print(root_dir) + +# %% [markdown] +# ## Set deterministic training for reproducibility + +# %% jupyter={"outputs_hidden": false} +set_determinism(42) + +# %% [markdown] +# ## Setup MedNIST Dataset and training and validation dataloaders +# In this tutorial, we will train our models on the MedNIST dataset available on MONAI +# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset). +# Here, we will use the "Hand" and "HeadCT", where our conditioning variable `class` will specify the modality. + +# %% jupyter={"outputs_hidden": false} +train_data = MedNISTDataset(root_dir=root_dir, section="training", download=True, progress=False, seed=0) +train_datalist = [] +for item in train_data.data: + if item["class_name"] in ["Hand", "HeadCT"]: + train_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) + +# %% [markdown] +# Here we use transforms to augment the training dataset, as usual: +# +# 1. `LoadImaged` loads the hands images from files. +# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. +# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. +# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# +# ### Classifier-free guidance during training +# +# In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an "unconditional" class. +# Here we specify the "unconditional" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad: +# +# `transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))` +# +# Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format. + +# %% jupyter={"outputs_hidden": false} +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + transforms.RandAffined( + keys=["image"], + rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)], + translate_range=[(-1, 1), (-1, 1)], + scale_range=[(-0.05, 0.05), (-0.05, 0.05)], + spatial_size=[64, 64], + padding_mode="zeros", + prob=0.5, + ), + transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)), + transforms.Lambdad( + keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) + ), + ] +) +train_ds = CacheDataset(data=train_datalist, transform=train_transforms) +train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True) + +# %% jupyter={"outputs_hidden": false} +val_data = MedNISTDataset(root_dir=root_dir, section="validation", download=True, progress=False, seed=0) +val_datalist = [] +for item in val_data.data: + if item["class_name"] in ["Hand", "HeadCT"]: + val_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) + + +val_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + transforms.Lambdad( + keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) + ), + ] +) +val_ds = CacheDataset(data=val_datalist, transform=val_transforms) +val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True) + +# %% [markdown] +# ### Visualisation of the training images + +# %% jupyter={"outputs_hidden": false} +check_data = first(train_loader) +print(f"batch shape: {check_data['image'].shape}") +image_visualisation = torch.cat( + [check_data["image"][0, 0], check_data["image"][1, 0], check_data["image"][2, 0], check_data["image"][3, 0]], dim=1 +) +plt.figure("training images", (12, 6)) +plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + +# %% [markdown] +# ### Define network, scheduler, optimizer, and inferer +# At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using +# the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms +# in the 3rd level, each with 1 attention head (`num_head_channels=64`). +# +# In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use: +# +# ` +# with_conditioning=True, +# cross_attention_dim=1, +# ` + +# %% jupyter={"outputs_hidden": false} +device = torch.device("cuda") + +model = DiffusionModelUNet( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(64, 64, 64), + attention_levels=(False, False, True), + num_res_blocks=1, + num_head_channels=64, + with_conditioning=True, + cross_attention_dim=1, +) +model.to(device) + +scheduler = DDPMScheduler( + num_train_timesteps=1000, +) + +optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) + +inferer = DiffusionInferer(scheduler) +# %% [markdown] +# ### Model training +# Here, we are training our model for 75 epochs (training time: ~50 minutes). + +# %% jupyter={"outputs_hidden": false} +n_epochs = 75 +val_interval = 5 +epoch_loss_list = [] +val_epoch_loss_list = [] + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, batch in progress_bar: + images = batch["image"].to(device) + classes = batch["class"].to(device) + optimizer.zero_grad(set_to_none=True) + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + epoch_loss += loss.item() + + progress_bar.set_postfix( + { + "loss": epoch_loss / (step + 1), + } + ) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + for step, batch in enumerate(val_loader): + images = batch["image"].to(device) + classes = batch["class"].to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train completed, total time: {total_time}.") +# %% [markdown] +# ### Learning curves + +# %% jupyter={"outputs_hidden": false} +plt.style.use("seaborn-v0_8") +plt.title("Learning Curves", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() + +# %% [markdown] +# ### Sampling process with classifier-free guidance +# In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`). +# Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector. + +# %% jupyter={"outputs_hidden": false} +model.eval() +guidance_scale = 7.0 +conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device) + +noise = torch.randn((1, 1, 64, 64)) +noise = noise.to(device) +scheduler.set_timesteps(num_inference_steps=1000) +progress_bar = tqdm(scheduler.timesteps) +for t in progress_bar: + with autocast(enabled=True): + with torch.no_grad(): + noise_input = torch.cat([noise] * 2) + model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning) + noise_pred_uncond, noise_pred_text = model_output.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + noise, _ = scheduler.step(noise_pred, t, noise) + +plt.style.use("default") +plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + +# %% [markdown] +# ### Cleanup data directory +# +# Remove directory if a temporary was used. + +# %% +if directory is None: + shutil.rmtree(root_dir) From a750e53872d10c25b4fdccd0d4d1350ac8296357 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 9 Feb 2023 17:29:10 +0100 Subject: [PATCH 02/23] first reversed loop for DDIM is implemented. Classifier guidance is on the way --- .../networks/nets/diffusion_model_encoder.py | 1661 ++++++++++++++ .../networks/nets/diffusion_model_unet.py | 243 ++ generative/networks/schedulers/ddim.py | 87 + tutorials/Untitled.ipynb | 33 + ...pm_classifier_free_guidance_tutorial.ipynb | 471 +++- ..._ddpm_classifier_free_guidance_tutorial.py | 10 +- ...r_guidance_anomalydetection_tutorial.ipynb | 2022 +++++++++++++---- ...fier_guidance_anomalydetection_tutorial.py | 569 +++-- .../Untitled.ipynb | 33 + .../Untitled1.ipynb | 6 + .../load_2d_brats.ipynb | 437 ++++ .../load_2d_brats.py | 201 ++ 12 files changed, 5092 insertions(+), 681 deletions(-) create mode 100644 generative/networks/nets/diffusion_model_encoder.py create mode 100644 tutorials/Untitled.ipynb create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb create mode 100644 tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py diff --git a/generative/networks/nets/diffusion_model_encoder.py b/generative/networks/nets/diffusion_model_encoder.py new file mode 100644 index 00000000..7d87181c --- /dev/null +++ b/generative/networks/nets/diffusion_model_encoder.py @@ -0,0 +1,1661 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ========================================================================= +# Adapted from https://github.com/huggingface/diffusers +# which has the following license: +# https://github.com/huggingface/diffusers/blob/main/LICENSE + +# Copyright 2022 UC Berkeley Team and The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ========================================================================= + +import math +from typing import List, Optional, Sequence, Tuple, Union + +import torch +import torch.nn.functional as F +from monai.networks.blocks import Convolution +from monai.networks.layers.factories import Pool +from torch import nn + +__all__ = ["DiffusionModelEncoder"] + + +def zero_module(module: nn.Module) -> nn.Module: + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +class GEGLU(nn.Module): + """ + A variant of the gated linear unit activation function from https://arxiv.org/abs/2002.05202. + + Args: + dim_in: number of channels in the input. + dim_out: number of channels in the output. + """ + + def __init__(self, dim_in: int, dim_out: int) -> None: + super().__init__() + self.proj = nn.Linear(dim_in, dim_out * 2) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x, gate = self.proj(x).chunk(2, dim=-1) + return x * F.gelu(gate.to(dtype=torch.float32)).to(dtype=gate.dtype) + + +class FeedForward(nn.Module): + """ + A feed-forward layer. + + Args: + num_channels: number of channels in the input. + dim_out: number of channels in the output. If not given, defaults to `dim`. + mult: multiplier to use for the hidden dimension. + dropout: dropout probability to use. + """ + + def __init__(self, num_channels: int, dim_out: Optional[int] = None, mult: int = 4, dropout: float = 0.0) -> None: + super().__init__() + inner_dim = int(num_channels * mult) + dim_out = dim_out if dim_out is not None else num_channels + + self.net = nn.Sequential(GEGLU(num_channels, inner_dim), nn.Dropout(dropout), nn.Linear(inner_dim, dim_out)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return self.net(x) + + +class CrossAttention(nn.Module): + """ + A cross attention layer. + + Args: + query_dim: number of channels in the query. + cross_attention_dim: number of channels in the context. + num_attention_heads: number of heads to use for multi-head attention. + num_head_channels: number of channels in each head. + dropout: dropout probability to use. + """ + + def __init__( + self, + query_dim: int, + cross_attention_dim: Optional[int] = None, + num_attention_heads: int = 8, + num_head_channels: int = 64, + dropout: float = 0.0, + ) -> None: + super().__init__() + inner_dim = num_head_channels * num_attention_heads + cross_attention_dim = cross_attention_dim if cross_attention_dim is not None else query_dim + + self.scale = num_head_channels**-0.5 + self.heads = num_attention_heads + + self.to_q = nn.Linear(query_dim, inner_dim, bias=False) + self.to_k = nn.Linear(cross_attention_dim, inner_dim, bias=False) + self.to_v = nn.Linear(cross_attention_dim, inner_dim, bias=False) + + self.to_out = nn.Sequential(nn.Linear(inner_dim, query_dim), nn.Dropout(dropout)) + + def reshape_heads_to_batch_dim(self, x: torch.Tensor) -> torch.Tensor: + batch_size, seq_len, dim = x.shape + head_size = self.heads + x = x.reshape(batch_size, seq_len, head_size, dim // head_size) + x = x.permute(0, 2, 1, 3).reshape(batch_size * head_size, seq_len, dim // head_size) + return x + + def reshape_batch_dim_to_heads(self, x: torch.Tensor) -> torch.Tensor: + batch_size, seq_len, dim = x.shape + head_size = self.heads + x = x.reshape(batch_size // head_size, head_size, seq_len, dim) + x = x.permute(0, 2, 1, 3).reshape(batch_size // head_size, seq_len, dim * head_size) + return x + + def _attention(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor) -> torch.Tensor: + attention_scores = torch.matmul(query, key.transpose(-1, -2)) * self.scale + attention_probs = attention_scores.softmax(dim=-1) + # compute attention output + hidden_states = torch.matmul(attention_probs, value) + + # reshape hidden_states + hidden_states = self.reshape_batch_dim_to_heads(hidden_states) + return hidden_states + + def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: + query = self.to_q(x) + context = context if context is not None else x + key = self.to_k(context) + value = self.to_v(context) + + query = self.reshape_heads_to_batch_dim(query) + key = self.reshape_heads_to_batch_dim(key) + value = self.reshape_heads_to_batch_dim(value) + + x = self._attention(query, key, value) + + return self.to_out(x) + + +class BasicTransformerBlock(nn.Module): + """ + A basic Transformer block. + + Args: + num_channels: number of channels in the input and output. + num_attention_heads: number of heads to use for multi-head attention. + num_head_channels: number of channels in each attention head. + dropout: dropout probability to use. + cross_attention_dim: size of the context vector for cross attention. + """ + + def __init__( + self, + num_channels: int, + num_attention_heads: int, + num_head_channels: int, + dropout: float = 0.0, + cross_attention_dim: Optional[int] = None, + ) -> None: + super().__init__() + self.attn1 = CrossAttention( + query_dim=num_channels, + num_attention_heads=num_attention_heads, + num_head_channels=num_head_channels, + dropout=dropout, + ) # is a self-attention + self.ff = FeedForward(num_channels, dropout=dropout) + self.attn2 = CrossAttention( + query_dim=num_channels, + cross_attention_dim=cross_attention_dim, + num_attention_heads=num_attention_heads, + num_head_channels=num_head_channels, + dropout=dropout, + ) # is a self-attention if context is None + self.norm1 = nn.LayerNorm(num_channels) + self.norm2 = nn.LayerNorm(num_channels) + self.norm3 = nn.LayerNorm(num_channels) + + def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: + # 1. Self-Attention + x = self.attn1(self.norm1(x)) + x + + # 2. Cross-Attention + x = self.attn2(self.norm2(x), context=context) + x + + # 3. Feed-forward + x = self.ff(self.norm3(x)) + x + return x + + +class SpatialTransformer(nn.Module): + """ + Transformer block for image-like data. First, project the input (aka embedding) and reshape to b, t, d. Then apply + standard transformer action. Finally, reshape to image. + + Args: + spatial_dims: number of spatial dimensions. + in_channels: number of channels in the input and output. + num_attention_heads: number of heads to use for multi-head attention. + num_head_channels: number of channels in each attention head. + num_layers: number of layers of Transformer blocks to use. + dropout: dropout probability to use. + norm_num_groups: number of groups for the normalization. + norm_eps: epsilon for the normalization. + cross_attention_dim: number of context dimensions to use. + """ + + def __init__( + self, + spatial_dims: int, + in_channels: int, + num_attention_heads: int, + num_head_channels: int, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + cross_attention_dim: Optional[int] = None, + ) -> None: + super().__init__() + self.spatial_dims = spatial_dims + self.in_channels = in_channels + inner_dim = num_attention_heads * num_head_channels + + self.norm = nn.GroupNorm(num_groups=norm_num_groups, num_channels=in_channels, eps=norm_eps, affine=True) + + self.proj_in = Convolution( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=inner_dim, + strides=1, + kernel_size=1, + padding=0, + conv_only=True, + ) + + self.transformer_blocks = nn.ModuleList( + [ + BasicTransformerBlock( + num_channels=inner_dim, + num_attention_heads=num_attention_heads, + num_head_channels=num_head_channels, + dropout=dropout, + cross_attention_dim=cross_attention_dim, + ) + for _ in range(num_layers) + ] + ) + + self.proj_out = zero_module( + Convolution( + spatial_dims=spatial_dims, + in_channels=inner_dim, + out_channels=in_channels, + strides=1, + kernel_size=1, + padding=0, + conv_only=True, + ) + ) + + def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: + # note: if no context is given, cross-attention defaults to self-attention + batch = channel = height = width = depth = -1 + if self.spatial_dims == 2: + batch, channel, height, width = x.shape + if self.spatial_dims == 3: + batch, channel, height, width, depth = x.shape + + residual = x + x = self.norm(x) + x = self.proj_in(x) + + inner_dim = x.shape[1] + + if self.spatial_dims == 2: + x = x.permute(0, 2, 3, 1).reshape(batch, height * width, inner_dim) + if self.spatial_dims == 3: + x = x.permute(0, 2, 3, 4, 1).reshape(batch, height * width * depth, inner_dim) + + for block in self.transformer_blocks: + x = block(x, context=context) + + if self.spatial_dims == 2: + x = x.reshape(batch, height, width, inner_dim).permute(0, 3, 1, 2) + if self.spatial_dims == 3: + x = x.reshape(batch, height, width, depth, inner_dim).permute(0, 4, 1, 2, 3) + + x = self.proj_out(x) + return x + residual + + +class AttentionBlock(nn.Module): + """ + An attention block that allows spatial positions to attend to each other. Uses three q, k, v linear layers to + compute attention. + + Args: + spatial_dims: number of spatial dimensions. + num_channels: number of channels in the input and output. + num_head_channels: number of channels in each attention head. + norm_num_groups: number of groups to use for group norm. + norm_eps: epsilon value to use for group norm. + """ + + def __init__( + self, + spatial_dims: int, + num_channels: int, + num_head_channels: Optional[int] = None, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + ) -> None: + super().__init__() + self.spatial_dims = spatial_dims + self.num_channels = num_channels + + self.num_heads = num_channels // num_head_channels if num_head_channels is not None else 1 + self.num_head_size = num_head_channels + self.norm = nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels, eps=norm_eps, affine=True) + + # define q,k,v as linear layers + self.query = nn.Linear(num_channels, num_channels) + self.key = nn.Linear(num_channels, num_channels) + self.value = nn.Linear(num_channels, num_channels) + + self.proj_attn = nn.Linear(num_channels, num_channels) + + def transpose_for_scores(self, projection: torch.Tensor) -> torch.Tensor: + new_projection_shape = projection.size()[:-1] + (self.num_heads, -1) + # move heads to 2nd position (B, T, H * D) -> (B, T, H, D) -> (B, H, T, D) + new_projection = projection.view(new_projection_shape).permute(0, 2, 1, 3) + return new_projection + + def forward(self, x: torch.Tensor) -> torch.Tensor: + residual = x + + batch = channel = height = width = depth = -1 + if self.spatial_dims == 2: + batch, channel, height, width = x.shape + if self.spatial_dims == 3: + batch, channel, height, width, depth = x.shape + + # norm + x = self.norm(x) + + if self.spatial_dims == 2: + x = x.view(batch, channel, height * width).transpose(1, 2) + if self.spatial_dims == 3: + x = x.view(batch, channel, height * width * depth).transpose(1, 2) + + # proj to q, k, v + query_proj = self.query(x) + key_proj = self.key(x) + value_proj = self.value(x) + + # transpose + query_states = self.transpose_for_scores(query_proj) + key_states = self.transpose_for_scores(key_proj) + value_states = self.transpose_for_scores(value_proj) + + # get scores + scale = 1 / math.sqrt(math.sqrt(self.num_channels / self.num_heads)) + attention_scores = torch.matmul(query_states * scale, key_states.transpose(-1, -2) * scale) + attention_probs = torch.softmax(attention_scores.float(), dim=-1) + + # compute attention output + x = torch.matmul(attention_probs, value_states) + + x = x.permute(0, 2, 1, 3).contiguous() + new_x_shape = x.size()[:-2] + (self.num_channels,) + x = x.view(new_x_shape) + + # compute next hidden states + x = self.proj_attn(x) + + if self.spatial_dims == 2: + x = x.transpose(-1, -2).reshape(batch, channel, height, width) + if self.spatial_dims == 3: + x = x.transpose(-1, -2).reshape(batch, channel, height, width, depth) + + return x + residual + + +def get_timestep_embedding(timesteps: torch.Tensor, embedding_dim: int, max_period: int = 10000) -> torch.Tensor: + """ + Create sinusoidal timestep embeddingsfollowing the implementation in Ho et al. "Denoising Diffusion Probabilistic + Models" https://arxiv.org/abs/2006.11239. + + Args: + timesteps: a 1-D Tensor of N indices, one per batch element. + embedding_dim: the dimension of the output. + max_period: controls the minimum frequency of the embeddings. + """ + assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array" + + half_dim = embedding_dim // 2 + exponent = -math.log(max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32, device=timesteps.device) + freqs = torch.exp(exponent / half_dim) + + args = timesteps[:, None].float() * freqs[None, :] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + + # zero pad + if embedding_dim % 2 == 1: + embedding = torch.nn.functional.pad(embedding, (0, 1, 0, 0)) + + return embedding + + +class Downsample(nn.Module): + """ + Downsampling layer. + + Args: + spatial_dims: number of spatial dimensions. + num_channels: number of input channels. + use_conv: if True uses Convolution instead of Pool average to perform downsampling. + out_channels: number of output channels. + padding: controls the amount of implicit zero-paddings on both sides for padding number of points + for each dimension. + """ + + def __init__( + self, + spatial_dims: int, + num_channels: int, + use_conv: bool, + out_channels: Optional[int] = None, + padding: int = 1, + ) -> None: + super().__init__() + self.num_channels = num_channels + self.out_channels = out_channels or num_channels + self.use_conv = use_conv + if use_conv: + self.op = Convolution( + spatial_dims=spatial_dims, + in_channels=self.num_channels, + out_channels=self.out_channels, + strides=2, + kernel_size=3, + padding=padding, + conv_only=True, + ) + else: + assert self.num_channels == self.out_channels + self.op = Pool[Pool.AVG, spatial_dims](kernel_size=2, stride=2) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + assert x.shape[1] == self.num_channels + return self.op(x) + + +class Upsample(nn.Module): + """ + Upsampling layer with an optional convolution. + + Args: + spatial_dims: number of spatial dimensions. + num_channels: number of input channels. + use_conv: if True uses Convolution instead of Pool average to perform downsampling. + out_channels: number of output channels. + padding: controls the amount of implicit zero-paddings on both sides for padding number of points for each + dimension. + """ + + def __init__( + self, + spatial_dims: int, + num_channels: int, + use_conv: bool, + out_channels: Optional[int] = None, + padding: int = 1, + ) -> None: + super().__init__() + self.num_channels = num_channels + self.out_channels = out_channels or num_channels + self.use_conv = use_conv + if use_conv: + self.conv = Convolution( + spatial_dims=spatial_dims, + in_channels=self.num_channels, + out_channels=self.out_channels, + strides=1, + kernel_size=3, + padding=padding, + conv_only=True, + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + assert x.shape[1] == self.num_channels + x = F.interpolate(x, scale_factor=2.0, mode="nearest") + if self.use_conv: + x = self.conv(x) + return x + + +class ResnetBlock(nn.Module): + """ + Residual block with timestep conditioning. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + temb_channels: number of timestep embedding channels. + out_channels: number of output channels. + up: if True, performs upsampling. + down: if True, performs downsampling. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + """ + + def __init__( + self, + spatial_dims: int, + in_channels: int, + temb_channels: int, + out_channels: Optional[int] = None, + up: bool = False, + down: bool = False, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + ) -> None: + super().__init__() + self.spatial_dims = spatial_dims + self.channels = in_channels + self.emb_channels = temb_channels + self.out_channels = out_channels or in_channels + self.up = up + self.down = down + + self.norm1 = nn.GroupNorm(num_groups=norm_num_groups, num_channels=in_channels, eps=norm_eps, affine=True) + self.nonlinearity = nn.SiLU() + self.conv1 = Convolution( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=self.out_channels, + strides=1, + kernel_size=3, + padding=1, + conv_only=True, + ) + + self.upsample = self.downsample = None + if self.up: + self.upsample = Upsample(spatial_dims, in_channels, use_conv=False) + elif down: + self.downsample = Downsample(spatial_dims, in_channels, use_conv=False) + + self.time_emb_proj = nn.Linear( + temb_channels, + self.out_channels, + ) + + self.norm2 = nn.GroupNorm(num_groups=norm_num_groups, num_channels=self.out_channels, eps=norm_eps, affine=True) + self.conv2 = zero_module( + Convolution( + spatial_dims=spatial_dims, + in_channels=self.out_channels, + out_channels=self.out_channels, + strides=1, + kernel_size=3, + padding=1, + conv_only=True, + ) + ) + + if self.out_channels == in_channels: + self.skip_connection = nn.Identity() + else: + self.skip_connection = Convolution( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=self.out_channels, + strides=1, + kernel_size=1, + padding=0, + conv_only=True, + ) + + def forward(self, x: torch.Tensor, emb: torch.Tensor) -> torch.Tensor: + h = x + h = self.norm1(h) + h = self.nonlinearity(h) + + if self.upsample is not None: + if h.shape[0] >= 64: + x = x.contiguous() + h = h.contiguous() + x = self.upsample(x) + h = self.upsample(h) + elif self.downsample is not None: + x = self.downsample(x) + h = self.downsample(h) + + h = self.conv1(h) + + if self.spatial_dims == 2: + temb = self.time_emb_proj(self.nonlinearity(emb))[:, :, None, None] + else: + temb = self.time_emb_proj(self.nonlinearity(emb))[:, :, None, None, None] + h = h + temb + + h = self.norm2(h) + h = self.nonlinearity(h) + h = self.conv2(h) + + return self.skip_connection(x) + h + + +class DownBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_downsample: bool = True, + downsample_padding: int = 1, + ) -> None: + """ + Unet's down block containing resnet and downsamplers blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_downsample: if True add downsample block. + downsample_padding: padding used in the downsampling block. + """ + super().__init__() + resnets = [] + + for i in range(num_res_blocks): + in_channels = in_channels if i == 0 else out_channels + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + + self.resnets = nn.ModuleList(resnets) + + if add_downsample: + self.downsampler = Downsample( + spatial_dims=spatial_dims, + num_channels=out_channels, + use_conv=True, + out_channels=out_channels, + padding=downsample_padding, + ) + else: + self.downsampler = None + + def forward( + self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None + ) -> Tuple[torch.Tensor, List[torch.Tensor]]: + del context + output_states = [] + + for resnet in self.resnets: + hidden_states = resnet(hidden_states, temb) + output_states.append(hidden_states) + + if self.downsampler is not None: + hidden_states = self.downsampler(hidden_states) + output_states.append(hidden_states) + + return hidden_states, output_states + + +class AttnDownBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_downsample: bool = True, + downsample_padding: int = 1, + num_head_channels: int = 1, + ) -> None: + """ + Unet's down block containing resnet, downsamplers and self-attention blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_downsample: if True add downsample block. + downsample_padding: padding used in the downsampling block. + num_head_channels: number of channels in each attention head. + """ + super().__init__() + resnets = [] + attentions = [] + + for i in range(num_res_blocks): + in_channels = in_channels if i == 0 else out_channels + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + attentions.append( + AttentionBlock( + spatial_dims=spatial_dims, + num_channels=out_channels, + num_head_channels=num_head_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + + self.attentions = nn.ModuleList(attentions) + self.resnets = nn.ModuleList(resnets) + + if add_downsample: + self.downsampler = Downsample( + spatial_dims=spatial_dims, + num_channels=out_channels, + use_conv=True, + out_channels=out_channels, + padding=downsample_padding, + ) + else: + self.downsampler = None + + def forward( + self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None + ) -> Tuple[torch.Tensor, List[torch.Tensor]]: + del context + output_states = [] + + for resnet, attn in zip(self.resnets, self.attentions): + hidden_states = resnet(hidden_states, temb) + hidden_states = attn(hidden_states) + output_states.append(hidden_states) + + if self.downsampler is not None: + hidden_states = self.downsampler(hidden_states) + output_states.append(hidden_states) + + return hidden_states, output_states + + +class CrossAttnDownBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_downsample: bool = True, + downsample_padding: int = 1, + num_head_channels: int = 1, + transformer_num_layers: int = 1, + cross_attention_dim: Optional[int] = None, + ) -> None: + """ + Unet's down block containing resnet, downsamplers and cross-attention blocks. + + Args: + spatial_dims: number of spatial dimensions. + in_channels: number of input channels. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_downsample: if True add downsample block. + downsample_padding: padding used in the downsampling block. + num_head_channels: number of channels in each attention head. + transformer_num_layers: number of layers of Transformer blocks to use. + cross_attention_dim: number of context dimensions to use. + """ + super().__init__() + resnets = [] + attentions = [] + + for i in range(num_res_blocks): + in_channels = in_channels if i == 0 else out_channels + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + + attentions.append( + SpatialTransformer( + spatial_dims=spatial_dims, + in_channels=out_channels, + num_attention_heads=out_channels // num_head_channels, + num_head_channels=num_head_channels, + num_layers=transformer_num_layers, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + cross_attention_dim=cross_attention_dim, + ) + ) + + self.attentions = nn.ModuleList(attentions) + self.resnets = nn.ModuleList(resnets) + + if add_downsample: + self.downsampler = Downsample( + spatial_dims=spatial_dims, + num_channels=out_channels, + use_conv=True, + out_channels=out_channels, + padding=downsample_padding, + ) + else: + self.downsampler = None + + def forward( + self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None + ) -> Tuple[torch.Tensor, List[torch.Tensor]]: + output_states = [] + + for resnet, attn in zip(self.resnets, self.attentions): + hidden_states = resnet(hidden_states, temb) + hidden_states = attn(hidden_states, context=context) + output_states.append(hidden_states) + + if self.downsampler is not None: + hidden_states = self.downsampler(hidden_states) + output_states.append(hidden_states) + + return hidden_states, output_states + + +class AttnMidBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + temb_channels: int, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + num_head_channels: int = 1, + ) -> None: + """ + Unet's mid block containing resnet and self-attention blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + temb_channels: number of timestep embedding channels. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + num_head_channels: number of channels in each attention head. + """ + super().__init__() + self.attention = None + + self.resnet_1 = ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + self.attention = AttentionBlock( + spatial_dims=spatial_dims, + num_channels=in_channels, + num_head_channels=num_head_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + + self.resnet_2 = ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + + def forward( + self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None + ) -> torch.Tensor: + del context + hidden_states = self.resnet_1(hidden_states, temb) + hidden_states = self.attention(hidden_states) + hidden_states = self.resnet_2(hidden_states, temb) + + return hidden_states + + +class CrossAttnMidBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + temb_channels: int, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + num_head_channels: int = 1, + transformer_num_layers: int = 1, + cross_attention_dim: Optional[int] = None, + ) -> None: + """ + Unet's mid block containing resnet and cross-attention blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + temb_channels: number of timestep embedding channels + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + num_head_channels: number of channels in each attention head. + transformer_num_layers: number of layers of Transformer blocks to use. + cross_attention_dim: number of context dimensions to use. + """ + super().__init__() + self.attention = None + + self.resnet_1 = ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + self.attention = SpatialTransformer( + spatial_dims=spatial_dims, + in_channels=in_channels, + num_attention_heads=in_channels // num_head_channels, + num_head_channels=num_head_channels, + num_layers=transformer_num_layers, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + cross_attention_dim=cross_attention_dim, + ) + self.resnet_2 = ResnetBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + + def forward( + self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None + ) -> torch.Tensor: + hidden_states = self.resnet_1(hidden_states, temb) + hidden_states = self.attention(hidden_states, context=context) + hidden_states = self.resnet_2(hidden_states, temb) + + return hidden_states + + +class UpBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + prev_output_channel: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_upsample: bool = True, + ) -> None: + """ + Unet's up block containing resnet and upsamplers blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + prev_output_channel: number of channels from residual connection. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_upsample: if True add downsample block. + """ + super().__init__() + resnets = [] + + for i in range(num_res_blocks): + res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels + resnet_in_channels = prev_output_channel if i == 0 else out_channels + + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=resnet_in_channels + res_skip_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + + self.resnets = nn.ModuleList(resnets) + + if add_upsample: + self.upsampler = Upsample( + spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels + ) + else: + self.upsampler = None + + def forward( + self, + hidden_states: torch.Tensor, + res_hidden_states_list: List[torch.Tensor], + temb: torch.Tensor, + context: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + del context + for i, resnet in enumerate(self.resnets): + # pop res hidden states + res_hidden_states = res_hidden_states_list[-1] + res_hidden_states_list = res_hidden_states_list[:-1] + hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) + + hidden_states = resnet(hidden_states, temb) + + if self.upsampler is not None: + hidden_states = self.upsampler(hidden_states) + + return hidden_states + + +class AttnUpBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + prev_output_channel: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_upsample: bool = True, + num_head_channels: int = 1, + ) -> None: + """ + Unet's up block containing resnet, upsamplers, and self-attention blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + prev_output_channel: number of channels from residual connection. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_upsample: if True add downsample block. + num_head_channels: number of channels in each attention head. + """ + super().__init__() + resnets = [] + attentions = [] + + for i in range(num_res_blocks): + res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels + resnet_in_channels = prev_output_channel if i == 0 else out_channels + + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=resnet_in_channels + res_skip_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + attentions.append( + AttentionBlock( + spatial_dims=spatial_dims, + num_channels=out_channels, + num_head_channels=num_head_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + + self.resnets = nn.ModuleList(resnets) + self.attentions = nn.ModuleList(attentions) + + if add_upsample: + self.upsampler = Upsample( + spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels + ) + else: + self.upsampler = None + + def forward( + self, + hidden_states: torch.Tensor, + res_hidden_states_list: List[torch.Tensor], + temb: torch.Tensor, + context: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + del context + for resnet, attn in zip(self.resnets, self.attentions): + # pop res hidden states + res_hidden_states = res_hidden_states_list[-1] + res_hidden_states_list = res_hidden_states_list[:-1] + hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) + + hidden_states = resnet(hidden_states, temb) + hidden_states = attn(hidden_states) + + if self.upsampler is not None: + hidden_states = self.upsampler(hidden_states) + + return hidden_states + + +class CrossAttnUpBlock(nn.Module): + def __init__( + self, + spatial_dims: int, + in_channels: int, + prev_output_channel: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int = 1, + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + add_upsample: bool = True, + num_head_channels: int = 1, + transformer_num_layers: int = 1, + cross_attention_dim: Optional[int] = None, + ) -> None: + """ + Unet's up block containing resnet, upsamplers, and self-attention blocks. + + Args: + spatial_dims: The number of spatial dimensions. + in_channels: number of input channels. + prev_output_channel: number of channels from residual connection. + out_channels: number of output channels. + temb_channels: number of timestep embedding channels. + num_res_blocks: number of residual blocks. + norm_num_groups: number of groups for the group normalization. + norm_eps: epsilon for the group normalization. + add_upsample: if True add downsample block. + num_head_channels: number of channels in each attention head. + transformer_num_layers: number of layers of Transformer blocks to use. + cross_attention_dim: number of context dimensions to use. + """ + super().__init__() + resnets = [] + attentions = [] + + for i in range(num_res_blocks): + res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels + resnet_in_channels = prev_output_channel if i == 0 else out_channels + + resnets.append( + ResnetBlock( + spatial_dims=spatial_dims, + in_channels=resnet_in_channels + res_skip_channels, + out_channels=out_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + ) + ) + attentions.append( + SpatialTransformer( + spatial_dims=spatial_dims, + in_channels=out_channels, + num_attention_heads=out_channels // num_head_channels, + num_head_channels=num_head_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + ) + + self.attentions = nn.ModuleList(attentions) + self.resnets = nn.ModuleList(resnets) + + if add_upsample: + self.upsampler = Upsample( + spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels + ) + else: + self.upsampler = None + + def forward( + self, + hidden_states: torch.Tensor, + res_hidden_states_list: List[torch.Tensor], + temb: torch.Tensor, + context: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + for resnet, attn in zip(self.resnets, self.attentions): + # pop res hidden states + res_hidden_states = res_hidden_states_list[-1] + res_hidden_states_list = res_hidden_states_list[:-1] + hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) + + hidden_states = resnet(hidden_states, temb) + hidden_states = attn(hidden_states, context=context) + + if self.upsampler is not None: + hidden_states = self.upsampler(hidden_states) + + return hidden_states + + +def get_down_block( + spatial_dims: int, + in_channels: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int, + norm_num_groups: int, + norm_eps: float, + add_downsample: bool, + with_attn: bool, + with_cross_attn: bool, + num_head_channels: int, + transformer_num_layers: int, + cross_attention_dim: Optional[int], +) -> nn.Module: + if with_attn: + return AttnDownBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_downsample=add_downsample, + num_head_channels=num_head_channels, + ) + elif with_cross_attn: + return CrossAttnDownBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_downsample=add_downsample, + num_head_channels=num_head_channels, + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + else: + return DownBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_downsample=add_downsample, + ) + + +def get_mid_block( + spatial_dims: int, + in_channels: int, + temb_channels: int, + norm_num_groups: int, + norm_eps: float, + with_conditioning: bool, + num_head_channels: int, + transformer_num_layers: int, + cross_attention_dim: Optional[int], +) -> nn.Module: + if with_conditioning: + return CrossAttnMidBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + num_head_channels=num_head_channels, + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + else: + return AttnMidBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + temb_channels=temb_channels, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + num_head_channels=num_head_channels, + ) + + +def get_up_block( + spatial_dims: int, + in_channels: int, + prev_output_channel: int, + out_channels: int, + temb_channels: int, + num_res_blocks: int, + norm_num_groups: int, + norm_eps: float, + add_upsample: bool, + with_attn: bool, + with_cross_attn: bool, + num_head_channels: int, + transformer_num_layers: int, + cross_attention_dim: Optional[int], +) -> nn.Module: + if with_attn: + return AttnUpBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + prev_output_channel=prev_output_channel, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_upsample=add_upsample, + num_head_channels=num_head_channels, + ) + elif with_cross_attn: + return CrossAttnUpBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + prev_output_channel=prev_output_channel, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_upsample=add_upsample, + num_head_channels=num_head_channels, + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + else: + return UpBlock( + spatial_dims=spatial_dims, + in_channels=in_channels, + prev_output_channel=prev_output_channel, + out_channels=out_channels, + temb_channels=temb_channels, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_upsample=add_upsample, + ) + + +class DiffusionModelEncoder(nn.Module): + """ + Unet network with timestep embedding and attention mechanisms for conditioning based on + Rombach et al. "High-Resolution Image Synthesis with Latent Diffusion Models" https://arxiv.org/abs/2112.10752 + and Pinaya et al. "Brain Imaging Generation with Latent Diffusion Models" https://arxiv.org/abs/2209.07162 + + Args: + spatial_dims: number of spatial dimensions. + in_channels: number of input channels. + out_channels: number of output channels. + num_res_blocks: number of residual blocks (see ResnetBlock) per level. + num_channels: tuple of block output channels. + attention_levels: list of levels to add attention. + norm_num_groups: number of groups for the normalization. + norm_eps: epsilon for the normalization. + num_head_channels: number of channels in each attention head. + with_conditioning: if True add spatial transformers to perform conditioning. + transformer_num_layers: number of layers of Transformer blocks to use. + cross_attention_dim: number of context dimensions to use. + num_class_embeds: if specified (as an int), then this model will be class-conditional with `num_class_embeds` + classes. + """ + + def __init__( + self, + spatial_dims: int, + in_channels: int, + out_channels: int, + num_res_blocks: int, + num_channels: Sequence[int] = (32, 64, 64, 64), + attention_levels: Sequence[bool] = (False, False, True, True), + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + num_head_channels: Union[int, Sequence[int]] = 8, + with_conditioning: bool = False, + transformer_num_layers: int = 1, + cross_attention_dim: Optional[int] = None, + num_class_embeds: Optional[int] = None, + ) -> None: + super().__init__() + if with_conditioning is True and cross_attention_dim is None: + raise ValueError( + ( + "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " + "when using with_conditioning." + ) + ) + if cross_attention_dim is not None and with_conditioning is False: + raise ValueError( + "DiffusionModelUNet expects with_conditioning=True when specifying the cross_attention_dim." + ) + + # All number of channels should be multiple of num_groups + if any((out_channel % norm_num_groups) != 0 for out_channel in num_channels): + raise ValueError("DiffusionModelUNet expects all num_channels being multiple of norm_num_groups") + + if isinstance(num_head_channels, int): + num_head_channels = (num_head_channels,) * len(attention_levels) + + if len(num_head_channels) != len(attention_levels): + raise ValueError( + "num_head_channels should have the same length as attention_levels. For the i levels without attention," + " i.e. `attention_level[i]=False`, the num_head_channels[i] will be ignored." + ) + + self.in_channels = in_channels + self.block_out_channels = num_channels + self.out_channels = out_channels + self.num_res_blocks = num_res_blocks + self.attention_levels = attention_levels + self.num_head_channels = num_head_channels + self.with_conditioning = with_conditioning + + # input + self.conv_in = Convolution( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=num_channels[0], + strides=1, + kernel_size=3, + padding=1, + conv_only=True, + ) + + # time + time_embed_dim = num_channels[0] * 4 + self.time_embed = nn.Sequential( + nn.Linear(num_channels[0], time_embed_dim), + nn.SiLU(), + nn.Linear(time_embed_dim, time_embed_dim), + ) + + # class embedding + self.num_class_embeds = num_class_embeds + if num_class_embeds is not None: + self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim) + + # down + self.down_blocks = nn.ModuleList([]) + output_channel = num_channels[0] + for i in range(len(num_channels)): + input_channel = output_channel + output_channel = num_channels[i] + is_final_block = i == len(num_channels) - 1 + + down_block = get_down_block( + spatial_dims=spatial_dims, + in_channels=input_channel, + out_channels=output_channel, + temb_channels=time_embed_dim, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_downsample=not is_final_block, + with_attn=(attention_levels[i] and not with_conditioning), + with_cross_attn=(attention_levels[i] and with_conditioning), + num_head_channels=num_head_channels[i], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + self.down_blocks.append(down_block) + + # mid + self.middle_block = get_mid_block( + spatial_dims=spatial_dims, + in_channels=num_channels[-1], + temb_channels=time_embed_dim, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + with_conditioning=with_conditioning, + num_head_channels=num_head_channels[-1], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + # up + self.up_blocks = nn.ModuleList([]) + reversed_block_out_channels = list(reversed(num_channels)) + reversed_attention_levels = list(reversed(attention_levels)) + reversed_num_head_channels = list(reversed(num_head_channels)) + output_channel = reversed_block_out_channels[0] + for i in range(len(reversed_block_out_channels)): + prev_output_channel = output_channel + output_channel = reversed_block_out_channels[i] + input_channel = reversed_block_out_channels[min(i + 1, len(num_channels) - 1)] + + is_final_block = i == len(num_channels) - 1 + + up_block = get_up_block( + spatial_dims=spatial_dims, + in_channels=input_channel, + prev_output_channel=prev_output_channel, + out_channels=output_channel, + temb_channels=time_embed_dim, + num_res_blocks=num_res_blocks + 1, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_upsample=not is_final_block, + with_attn=(reversed_attention_levels[i] and not with_conditioning), + with_cross_attn=(reversed_attention_levels[i] and with_conditioning), + num_head_channels=reversed_num_head_channels[i], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + self.up_blocks.append(up_block) + + # out + self.out = nn.Sequential( + nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels[0], eps=norm_eps, affine=True), + nn.SiLU(), + zero_module( + Convolution( + spatial_dims=spatial_dims, + in_channels=num_channels[0], + out_channels=out_channels, + strides=1, + kernel_size=3, + padding=1, + conv_only=True, + ) + ), + ) + + def forward( + self, + x: torch.Tensor, + timesteps: torch.Tensor, + context: Optional[torch.Tensor] = None, + class_labels: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + """ + Args: + x: input tensor (N, C, SpatialDims). + timesteps: timestep tensor (N,). + context: context tensor (N, 1, ContextDim). + class_labels: context tensor (N, ). + """ + # 1. time + t_emb = get_timestep_embedding(timesteps, self.block_out_channels[0]) + emb = self.time_embed(t_emb) + + # 2. class + if self.num_class_embeds is not None: + if class_labels is None: + raise ValueError("class_labels should be provided when num_class_embeds > 0") + class_emb = self.class_embedding(class_labels) + emb = emb + class_emb + + # 3. initial convolution + h = self.conv_in(x) + + # 4. down + if context is not None and self.with_conditioning is False: + raise ValueError("model should have with_conditioning = True if context is provided") + down_block_res_samples: List[torch.Tensor] = [h] + for downsample_block in self.down_blocks: + h, res_samples = downsample_block(hidden_states=h, temb=emb, context=context) + for residual in res_samples: + down_block_res_samples.append(residual) + h=h.flatten() + print('h', h.shape) + + # # 5. mid + # h = self.middle_block(hidden_states=h, temb=emb, context=context) + # + # # 6. up + # for upsample_block in self.up_blocks: + # res_samples = down_block_res_samples[-len(upsample_block.resnets) :] + # down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)] + # h = upsample_block(hidden_states=h, res_hidden_states_list=res_samples, temb=emb, context=context) + + # 7. output block + + self.out = nn.Linear(len(h), self.out_channels) + output=self.out(h) + + return output diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index d29aa2d6..506b3010 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1655,3 +1655,246 @@ def forward( h = self.out(h) return h + + + +class DiffusionModelEncoder(nn.Module): + """ + Unet network with timestep embedding and attention mechanisms for conditioning based on + Rombach et al. "High-Resolution Image Synthesis with Latent Diffusion Models" https://arxiv.org/abs/2112.10752 + and Pinaya et al. "Brain Imaging Generation with Latent Diffusion Models" https://arxiv.org/abs/2209.07162 + + Args: + spatial_dims: number of spatial dimensions. + in_channels: number of input channels. + out_channels: number of output channels. + num_res_blocks: number of residual blocks (see ResnetBlock) per level. + num_channels: tuple of block output channels. + attention_levels: list of levels to add attention. + norm_num_groups: number of groups for the normalization. + norm_eps: epsilon for the normalization. + num_head_channels: number of channels in each attention head. + with_conditioning: if True add spatial transformers to perform conditioning. + transformer_num_layers: number of layers of Transformer blocks to use. + cross_attention_dim: number of context dimensions to use. + num_class_embeds: if specified (as an int), then this model will be class-conditional with `num_class_embeds` + classes. + """ + + def __init__( + self, + spatial_dims: int, + in_channels: int, + out_channels: int, + num_res_blocks: int, + num_channels: Sequence[int] = (32, 64, 64, 64), + attention_levels: Sequence[bool] = (False, False, True, True), + norm_num_groups: int = 32, + norm_eps: float = 1e-6, + num_head_channels: Union[int, Sequence[int]] = 8, + with_conditioning: bool = False, + transformer_num_layers: int = 1, + cross_attention_dim: Optional[int] = None, + num_class_embeds: Optional[int] = None, + ) -> None: + super().__init__() + if with_conditioning is True and cross_attention_dim is None: + raise ValueError( + ( + "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " + "when using with_conditioning." + ) + ) + if cross_attention_dim is not None and with_conditioning is False: + raise ValueError( + "DiffusionModelUNet expects with_conditioning=True when specifying the cross_attention_dim." + ) + + # All number of channels should be multiple of num_groups + if any((out_channel % norm_num_groups) != 0 for out_channel in num_channels): + raise ValueError("DiffusionModelUNet expects all num_channels being multiple of norm_num_groups") + + if isinstance(num_head_channels, int): + num_head_channels = (num_head_channels,) * len(attention_levels) + + if len(num_head_channels) != len(attention_levels): + raise ValueError( + "num_head_channels should have the same length as attention_levels. For the i levels without attention," + " i.e. `attention_level[i]=False`, the num_head_channels[i] will be ignored." + ) + + self.in_channels = in_channels + self.block_out_channels = num_channels + self.out_channels = out_channels + self.num_res_blocks = num_res_blocks + self.attention_levels = attention_levels + self.num_head_channels = num_head_channels + self.with_conditioning = with_conditioning + + # input + self.conv_in = Convolution( + spatial_dims=spatial_dims, + in_channels=in_channels, + out_channels=num_channels[0], + strides=1, + kernel_size=3, + padding=1, + conv_only=True, + ) + + # time + time_embed_dim = num_channels[0] * 4 + self.time_embed = nn.Sequential( + nn.Linear(num_channels[0], time_embed_dim), + nn.SiLU(), + nn.Linear(time_embed_dim, time_embed_dim), + ) + + # class embedding + self.num_class_embeds = num_class_embeds + if num_class_embeds is not None: + self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim) + + # down + self.down_blocks = nn.ModuleList([]) + output_channel = num_channels[0] + for i in range(len(num_channels)): + input_channel = output_channel + output_channel = num_channels[i] + is_final_block = i == len(num_channels) - 1 + + down_block = get_down_block( + spatial_dims=spatial_dims, + in_channels=input_channel, + out_channels=output_channel, + temb_channels=time_embed_dim, + num_res_blocks=num_res_blocks, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_downsample=not is_final_block, + with_attn=(attention_levels[i] and not with_conditioning), + with_cross_attn=(attention_levels[i] and with_conditioning), + num_head_channels=num_head_channels[i], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + self.down_blocks.append(down_block) + + # mid + self.middle_block = get_mid_block( + spatial_dims=spatial_dims, + in_channels=num_channels[-1], + temb_channels=time_embed_dim, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + with_conditioning=with_conditioning, + num_head_channels=num_head_channels[-1], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + # up + self.up_blocks = nn.ModuleList([]) + reversed_block_out_channels = list(reversed(num_channels)) + reversed_attention_levels = list(reversed(attention_levels)) + reversed_num_head_channels = list(reversed(num_head_channels)) + output_channel = reversed_block_out_channels[0] + for i in range(len(reversed_block_out_channels)): + prev_output_channel = output_channel + output_channel = reversed_block_out_channels[i] + input_channel = reversed_block_out_channels[min(i + 1, len(num_channels) - 1)] + + is_final_block = i == len(num_channels) - 1 + + up_block = get_up_block( + spatial_dims=spatial_dims, + in_channels=input_channel, + prev_output_channel=prev_output_channel, + out_channels=output_channel, + temb_channels=time_embed_dim, + num_res_blocks=num_res_blocks + 1, + norm_num_groups=norm_num_groups, + norm_eps=norm_eps, + add_upsample=not is_final_block, + with_attn=(reversed_attention_levels[i] and not with_conditioning), + with_cross_attn=(reversed_attention_levels[i] and with_conditioning), + num_head_channels=reversed_num_head_channels[i], + transformer_num_layers=transformer_num_layers, + cross_attention_dim=cross_attention_dim, + ) + + self.up_blocks.append(up_block) + self.out = nn.Linear(16384, self.out_channels) + # out + # self.out = nn.Sequential( + # nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels[0], eps=norm_eps, affine=True), + # nn.SiLU(), + # zero_module( + # Convolution( + # spatial_dims=spatial_dims, + # in_channels=num_channels[0], + # out_channels=out_channels, + # strides=1, + # kernel_size=3, + # padding=1, + # conv_only=True, + # ) + # ), + # ) + + + def forward( + self, + x: torch.Tensor, + timesteps: torch.Tensor, + context: Optional[torch.Tensor] = None, + class_labels: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + """ + Args: + x: input tensor (N, C, SpatialDims). + timesteps: timestep tensor (N,). + context: context tensor (N, 1, ContextDim). + class_labels: context tensor (N, ). + """ + # 1. time + t_emb = get_timestep_embedding(timesteps, self.block_out_channels[0]) + emb = self.time_embed(t_emb) + + # 2. class + if self.num_class_embeds is not None: + if class_labels is None: + raise ValueError("class_labels should be provided when num_class_embeds > 0") + class_emb = self.class_embedding(class_labels) + emb = emb + class_emb + + # 3. initial convolution + h = self.conv_in(x) + + # 4. down + if context is not None and self.with_conditioning is False: + raise ValueError("model should have with_conditioning = True if context is provided") + down_block_res_samples: List[torch.Tensor] = [h] + for downsample_block in self.down_blocks: + h, res_samples = downsample_block(hidden_states=h, temb=emb, context=context) + for residual in res_samples: + down_block_res_samples.append(residual) + h=h.reshape(h.shape[0] ,-1) + + + # # 5. mid + # h = self.middle_block(hidden_states=h, temb=emb, context=context) + # + # # 6. up + # for upsample_block in self.up_blocks: + # res_samples = down_block_res_samples[-len(upsample_block.resnets) :] + # down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)] + # h = upsample_block(hidden_states=h, res_hidden_states_list=res_samples, temb=emb, context=context) + + # 7. output block + + + output=self.out(h) + + return output diff --git a/generative/networks/schedulers/ddim.py b/generative/networks/schedulers/ddim.py index 916d2f30..fba6d406 100644 --- a/generative/networks/schedulers/ddim.py +++ b/generative/networks/schedulers/ddim.py @@ -223,6 +223,93 @@ def step( return pred_prev_sample, pred_original_sample + + + def reversed_step( + self, + model_output: torch.Tensor, + timestep: int, + sample: torch.Tensor, + eta: float = 0.0, + generator: Optional[torch.Generator] = None, + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Predict the sample at the previous timestep by reversing the SDE. Core function to propagate the diffusion + process from the learned model outputs (most often the predicted noise). + + Args: + model_output: direct output from learned diffusion model. + timestep: current discrete timestep in the diffusion chain. + sample: current instance of sample being created by diffusion process. + eta: weight of noise for added noise in diffusion step. + predict_epsilon: flag to use when model predicts the samples directly instead of the noise, epsilon. + generator: random number generator. + + Returns: + pred_prev_sample: Predicted previous sample + pred_original_sample: Predicted original sample + """ + # See formulas (12) and (16) of DDIM paper https://arxiv.org/pdf/2010.02502.pdf + # Ideally, read DDIM paper in-detail understanding + + # Notation ( -> + # - model_output -> e_theta(x_t, t) + # - pred_original_sample -> f_theta(x_t, t) or x_0 + # - std_dev_t -> sigma_t + # - eta -> η + # - pred_sample_direction -> "direction pointing to x_t" + # - pred_prev_sample -> "x_t-1" + + # 1. get previous step value (=t-1) + prev_timestep = timestep - self.num_train_timesteps // self.num_inference_steps #t-1 + post_timestep = timestep + self.num_train_timesteps // self.num_inference_steps #t+1 + + # 2. compute alphas, betas + alpha_prod_t = self.alphas_cumprod[timestep] + alpha_prod_t_prev = self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod #alpha at timestep t-1 + alpha_prod_t_post = self.alphas_cumprod[post_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod #alpha at timestep t+1 + + + beta_prod_t = 1 - alpha_prod_t + + # 3. compute predicted original sample from predicted noise also called + # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + if self.prediction_type == "epsilon": + pred_original_sample = (sample - beta_prod_t ** (0.5) * model_output) / alpha_prod_t ** (0.5) + elif self.prediction_type == "sample": + pred_original_sample = model_output + elif self.prediction_type == "v_prediction": + pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output + # predict V + model_output = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample + + # 4. Clip "predicted x_0" + if self.clip_sample: + pred_original_sample = torch.clamp(pred_original_sample, -1, 1) + + # 5. compute variance: "sigma_t(η)" -> see formula (16) #I thought we set sigma to 0 here??? + # σ_t = sqrt((1 − α_t−1)/(1 − α_t)) * sqrt(1 − α_t/α_t−1) + variance = self._get_variance(timestep, prev_timestep) + std_dev_t = eta * variance ** (0.5) + + # 6. compute "direction pointing to x_t" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + pred_sample_direction = (1 - alpha_prod_t_post - std_dev_t**2) ** (0.5) * model_output + + # 7. compute x_t+1 without "random noise" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + pred_post_sample = alpha_prod_t_post ** (0.5) * pred_original_sample + pred_sample_direction + + if eta > 0: + # randn_like does not support generator https://github.com/pytorch/pytorch/issues/27072 + device = model_output.device if torch.is_tensor(model_output) else "cpu" + noise = torch.randn(model_output.shape, dtype=model_output.dtype, generator=generator).to(device) + variance = self._get_variance(timestep, prev_timestep) ** (0.5) * eta * noise + + pred_prev_sample = pred_prev_sample + variance + + return pred_post_sample, pred_original_sample + + + def add_noise( self, original_samples: torch.Tensor, diff --git a/tutorials/Untitled.ipynb b/tutorials/Untitled.ipynb new file mode 100644 index 00000000..c0c04ff7 --- /dev/null +++ b/tutorials/Untitled.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "761199be-b371-4cb7-a66f-ae739eccb554", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb index f8e67fbd..f8a2cdff 100644 --- a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb +++ b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb @@ -41,9 +41,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "id": "972ed3f3", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -53,25 +54,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "MONAI version: 1.1.dev2239\n", - "Numpy version: 1.23.3\n", - "Pytorch version: 1.8.0+cu111\n", + "MONAI version: 1.1.dev2248\n", + "Numpy version: 1.23.2\n", + "Pytorch version: 1.12.1\n", "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", - "MONAI rev id: 13b24fa92b9d98bd0dc6d5cdcb52504fd09e297b\n", - "MONAI __file__: /media/walter/Storage/Projects/GenerativeModels/venv/lib/python3.8/site-packages/monai/__init__.py\n", + "MONAI rev id: 3400bd91422ccba9ccc3aa2ffe7fecd4eb5596bf\n", + "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", "\n", "Optional dependencies:\n", - "Pytorch Ignite version: 0.4.10\n", - "Nibabel version: 4.0.2\n", - "scikit-image version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Nibabel version: 4.0.1\n", + "scikit-image version: 0.19.3\n", "Pillow version: 9.2.0\n", - "Tensorboard version: 2.11.0\n", + "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n", "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", - "TorchVision version: 0.9.0+cu111\n", + "TorchVision version: 0.13.1\n", "tqdm version: 4.64.1\n", "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", - "psutil version: 5.9.3\n", - "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", + "psutil version: 5.9.4\n", + "pandas version: 1.5.3\n", "einops version: 0.6.0\n", "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", @@ -114,8 +115,9 @@ "from generative.inferers import DiffusionInferer\n", "\n", "# TODO: Add right import reference after deployed\n", - "from generative.networks.nets import DiffusionModelUNet\n", - "from generative.schedulers import DDPMScheduler\n", + "\n", + "from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet\n", + "from generative.networks.schedulers.ddpm import DDPMScheduler\n", "\n", "print_config()" ] @@ -130,9 +132,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "id": "8b4323e7", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -142,7 +145,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/tmp142o2qtd\n" + "/tmp/tmp_kncxscb\n" ] } ], @@ -162,9 +165,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "id": "34ea510f", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -187,9 +191,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "id": "da1927b0", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -199,9 +204,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "2022-12-10 11:50:40,187 - INFO - Downloaded: /tmp/tmp142o2qtd/MedNIST.tar.gz\n", - "2022-12-10 11:50:40,255 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", - "2022-12-10 11:50:40,256 - INFO - Writing into directory: /tmp/tmp142o2qtd.\n" + "2023-01-20 12:00:04,842 - INFO - Downloaded: /tmp/tmp_kncxscb/MedNIST.tar.gz\n", + "2023-01-20 12:00:04,994 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2023-01-20 12:00:04,995 - INFO - Writing into directory: /tmp/tmp_kncxscb.\n" ] } ], @@ -237,9 +242,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 11, "id": "e3184009", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -249,7 +255,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 15990/15990 [00:08<00:00, 1784.85it/s]\n" + "Loading dataset: 100%|███████████████████| 15990/15990 [00:18<00:00, 879.46it/s]\n" ] } ], @@ -280,9 +286,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "id": "4c11b93f", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -292,16 +299,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "2022-12-10 11:51:08,067 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", - "2022-12-10 11:51:08,067 - INFO - File exists: /tmp/tmp142o2qtd/MedNIST.tar.gz, skipped downloading.\n", - "2022-12-10 11:51:08,068 - INFO - Non-empty folder exists in /tmp/tmp142o2qtd/MedNIST, skipped extracting.\n" + "2023-01-20 12:08:21,572 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2023-01-20 12:08:21,573 - INFO - File exists: /tmp/tmp_kncxscb/MedNIST.tar.gz, skipped downloading.\n", + "2023-01-20 12:08:21,574 - INFO - Non-empty folder exists in /tmp/tmp_kncxscb/MedNIST, skipped extracting.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Loading dataset: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1977/1977 [00:01<00:00, 1545.02it/s]\n" + "Loading dataset: 100%|█████████████████████| 1977/1977 [00:02<00:00, 735.36it/s]\n" ] } ], @@ -337,9 +344,10 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, "id": "4105a01f", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -349,13 +357,13 @@ "name": "stderr", "output_type": "stream", "text": [ - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + "/tmp/ipykernel_14682/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + "/tmp/ipykernel_14682/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + "/tmp/ipykernel_14682/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + "/tmp/ipykernel_14682/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n" ] }, @@ -363,12 +371,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "batch shape: (128, 1, 64, 64)\n" + "batch shape: torch.Size([128, 1, 64, 64])\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -410,9 +418,10 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 13, "id": "bee5913e", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -455,9 +464,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "id": "6c0ed909", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -468,88 +478,286 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 100%|██████████| 125/125 [00:27<00:00, 4.61it/s, loss=0.723]\n", - "Epoch 1: 100%|██████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.276]\n", - "Epoch 2: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0965]\n", - "Epoch 3: 100%|█████████| 125/125 [00:27<00:00, 4.62it/s, loss=0.0376]\n", - "Epoch 4: 100%|█████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0224]\n", - "Epoch 5: 100%|█████████| 125/125 [00:27<00:00, 4.47it/s, loss=0.0187]\n", - "Epoch 6: 100%|█████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0179]\n", - "Epoch 7: 100%|█████████| 125/125 [00:28<00:00, 4.44it/s, loss=0.0169]\n", - "Epoch 8: 100%|█████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0161]\n", - "Epoch 9: 100%|██████████| 125/125 [00:27<00:00, 4.50it/s, loss=0.016]\n", - "Epoch 10: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0156]\n", - "Epoch 11: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0152]\n", - "Epoch 12: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0152]\n", - "Epoch 13: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0151]\n", - "Epoch 14: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0147]\n", - "Epoch 15: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0151]\n", - "Epoch 16: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0151]\n", - "Epoch 17: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0146]\n", - "Epoch 18: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0144]\n", - "Epoch 19: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0143]\n", - "Epoch 20: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0145]\n", - "Epoch 21: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0143]\n", - "Epoch 22: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0138]\n", - "Epoch 23: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 24: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0134]\n", - "Epoch 25: 100%|████████| 125/125 [00:27<00:00, 4.49it/s, loss=0.0135]\n", - "Epoch 26: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 27: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0136]\n", - "Epoch 28: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0135]\n", - "Epoch 29: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0131]\n", - "Epoch 30: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0128]\n", - "Epoch 31: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0129]\n", - "Epoch 32: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0128]\n", - "Epoch 33: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 34: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0138]\n", - "Epoch 35: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0131]\n", - "Epoch 36: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0132]\n", - "Epoch 37: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", - "Epoch 38: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0124]\n", - "Epoch 39: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0124]\n", - "Epoch 40: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0132]\n", - "Epoch 41: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0128]\n", - "Epoch 42: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0122]\n", - "Epoch 43: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", - "Epoch 44: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0129]\n", - "Epoch 45: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0132]\n", - "Epoch 46: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0125]\n", - "Epoch 47: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0123]\n", - "Epoch 48: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0123]\n", - "Epoch 49: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0125]\n", - "Epoch 50: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", - "Epoch 51: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", - "Epoch 52: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0124]\n", - "Epoch 53: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", - "Epoch 54: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0123]\n", - "Epoch 55: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", - "Epoch 56: 100%|█████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.012]\n", - "Epoch 57: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0126]\n", - "Epoch 58: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", - "Epoch 59: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0126]\n", - "Epoch 60: 100%|████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.0119]\n", - "Epoch 61: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0122]\n", - "Epoch 62: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0119]\n", - "Epoch 63: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0125]\n", - "Epoch 64: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", - "Epoch 65: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0121]\n", - "Epoch 66: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0117]\n", - "Epoch 67: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", - "Epoch 68: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0123]\n", - "Epoch 69: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", - "Epoch 70: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.012]\n", - "Epoch 71: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0118]\n", - "Epoch 72: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0117]\n", - "Epoch 73: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0119]\n", - "Epoch 74: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0125]\n" + "Epoch 0: 0%| | 0/125 [00:00 24\u001b[0m noise_pred \u001b[38;5;241m=\u001b[39m \u001b[43minferer\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mimages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdiffusion_model\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnoise\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnoise\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcondition\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mclasses\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 26\u001b[0m loss \u001b[38;5;241m=\u001b[39m F\u001b[38;5;241m.\u001b[39mmse_loss(noise_pred\u001b[38;5;241m.\u001b[39mfloat(), noise\u001b[38;5;241m.\u001b[39mfloat())\n\u001b[1;32m 28\u001b[0m scaler\u001b[38;5;241m.\u001b[39mscale(loss)\u001b[38;5;241m.\u001b[39mbackward()\n", + "\u001b[0;31mTypeError\u001b[0m: DiffusionInferer.__call__() missing 1 required positional argument: 'timesteps'" ] } ], @@ -569,6 +777,7 @@ " for step, batch in progress_bar:\n", " images = batch[\"image\"].to(device)\n", " classes = batch[\"class\"].to(device)\n", + " print('images', images.shape, 'classes', classes.shape, classes)\n", " optimizer.zero_grad(set_to_none=True)\n", "\n", " with autocast(enabled=True):\n", @@ -627,23 +836,34 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "id": "f8385176", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAArsAAAILCAYAAADoqVT3AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABsNklEQVR4nO3deXwTdf7H8ffk6EFLkVuEolYERESrKKgccixWLu9rVwFBRfFAcFVcF11cZAEX3Z8WVlyVXV1PVNBVAQURUVEsKogKHhSkgCA3PWiTzPz+SJM2toVOm5Jp+3o+Hn00+WZm8s2nQd/55jvfMSzLsgQAAADUQa5YdwAAAACoKYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AOMJef/11dejQQR06dFBOTk6suwMAdZon1h0AUHdde+21WrlypTp27Kg33ngj1t1xjEaNGqljx46SJK/XG+PeVE1RUZHeeecdLV++XF9//bV2796tgoICNWjQQG3atFGXLl10wQUXqHv37rHuKoB6zrAsy4p1JwDUTYTdumnhwoX629/+pl9++UVSMLA3b95cDRo00K5du7Rnz57wtmeccYamTp2qtm3bxqq7AOo5RnYBAJX2xBNP6NFHH5UkpaWl6bbbblPv3r2VlJQU3ub777/X008/rTfffFOrVq3SVVddpf/+979KS0uLVbcB1GPM2QUAVMrixYvDQXfAgAF64403NHDgwIigK0nt27fXtGnTNHPmTHk8Hu3atUvjx4+XaZqx6DaAeo6wC8DRsrOz9eCDD+qCCy7QaaedptNOO039+vXTvffeq7Vr1x5y302bNumhhx7SkCFDlJ6erk6dOumss87SH/7wBz3//PPy+/3l7hc6eeztt9/WDz/8oOuuu05du3bVlVdeGd7m2muvVYcOHXT//fdLkpYtW6ZRo0apR48e6ty5s3r06KE777xTGzZsKHP8ik5Qy8nJCbd/9dVXKigo0BNPPKEhQ4bo9NNP16mnnqrBgwcrMzNTRUVF5fbd5/PpmWee0cUXX6z09HR17dpVV111ld58801J0osvvhh+Djt8Pp8mT54sSWrXrp1mzJihuLi4Q+7Tt29fDRs2THFxcTr66KP166+/hh+bMGGCOnTooL59+1a4f2XrlJWVpSuvvFLp6ekaP368/vnPf4Yf37p16yH7OGLECHXo0EEZGRllHluxYoXuuOMO9enTR507d1bXrl01ZMgQPfzwwxGv5bd++eUXTZs2TRdeeKFOP/10de7cWT179tQVV1yhf/3rX9q9e/ch+wQgupjGAMCx5s+fr4kTJ6qoqEiGYahly5ayLEs5OTnKycnR/PnzNX78eN1www1l9l2yZInuuOOOcCg8+uijlZiYqG3btikrK0tZWVl655139PTTTyshIaHc58/NzdX111+vPXv2qE2bNmrYsGG52z399NOaPn26kpOTdfTRR8s0Tf3666966623tGzZMs2bN0+pqam2XntBQYGGDx+u1atXq1WrVjr66KOVk5OjH374QT/88IPWrl2rJ554ImKfwsJCjRo1Sp9//rkkqUGDBmrevLl+/PFH3XXXXfr88891wgkn2OpHyMKFC7Vt2zZJ0h133HHYoBty++2365ZbblFycnKVnvdwtm7dqokTJ8o0TbVu3VqJiYkaPHiw/vGPf0iSFi1apOuuu67cfXft2qWVK1dKki688MJwu2VZ+utf/6rnn39eUnBOcqtWrbR//359//33+v777/XSSy8pMzNTZ599dsQxV69erZEjRyo3N1eS1KJFCx199NHavXu3Vq9erdWrV2vOnDl67rnnqvy3AGAPI7sAHGnVqlW67777VFRUpIyMDC1btkzLli3Thx9+qBUrVujCCy+UaZr6+9//riVLlkTse+DAAd1zzz0qKipS+/bt9d5772nZsmVauHChVq1apXHjxkmSsrKy9NRTT1XYh7lz56pVq1b64IMP9M4775S77Zo1a/R///d/mjRpkj799FO9/fbb+uSTT/Twww+H+/Kvf/3L9ut/+OGHlZeXp3nz5oWff8WKFfrd734nSVq6dKm+/PLLiH2eeOKJcNC97rrr9Omnn2rBggVasWKF7r33Xr366qtasGCB7b5I0vLlyyVJDRs2VJ8+fSq9X2JiYo0FXUl65pln1L17d3300Ud666239NBDDyk1NVXp6emSgmG3IgsXLlQgEJBhGBo6dGi4/V//+peef/55GYahO+64QytXrtR7772nzz77TG+88YZOOeUU5ebm6pZbbtH27dsjjnn//fcrNzdXJ598st577z0tX75c77zzjj799FPNnTtXxx9/vHbt2qUHHnigZgoCoAzCLgBHmjFjhvx+v8444ww9+uijatmyZfixJk2aaPr06Tr33HMlKTyKF/Lxxx+HR9buueeeiJUAPB6PbrrpJp155pmSpHfeeafCPnzzzTeaPn26mjRpUuE23333nW6//XZdddVVEcuIDR06VGeddZYk6bPPPqvkqy7x008/6cknn1SnTp3CbUlJSZowYUL4funjBgIBvfDCC5KCKyBMmDBB8fHxkoIjkyNGjNAtt9yir776ynZfpGCol6RTTjlFHo9zvhTcsGGDpk6dWmbe8JAhQyRJX331VXjViN8KBf+uXbuqdevWkqT9+/eHR8xvvPFG3XzzzWrQoEF4n44dO+qZZ55RkyZNlJeXF/FBZu/evVq3bp0kafTo0WVWoOjSpYv+9re/6cwzz1SbNm0qnIoCILoIuwAcZ+vWrVq1apWk4JxKl6v8/1T94Q9/kBQ8+3/Tpk3h9oyMDK1du1Yffvhhheu8nnLKKZKkzZs3V9iPzp07H3bJLK/Xq6uvvrrcx0Jr6VYUtg6lf//+4QBWWunpFKWPu3btWu3du1eSdPHFF5d7zOuuuy4iuNkRmmd69NFHV2n/mtKrV69yp5dccMEF8ng8siyr3NHd7du3h99jpacwvP/++8rLy5PL5dKIESPKfc6UlJTwSPC7774bbi+9kueOHTvK3Tc9PV3//e9/NXXq1EpPBQFQPYRdAI5T+uv5E088scLtTj311PDtb775JuIxj8ejli1bVjgKGQp9hxpdq8ycyrS0tDKjiiGhr+8PHjx42OP8ViiMH+q4hYWF4bbs7Ozw7ZNPPrnc/ZKSknTGGWfY7osk5eXlSQpOS3CSiv5GTZo0UY8ePSSVP5Vh4cKFMk1T8fHxESenffHFF5Kkpk2bHnJEP/Te2759u3bt2iVJaty4sU466SRJ0tSpUzVjxoxyT1AEcGQ557soAChW+kz38s6SL89v5076/X699dZbWrRokTZs2KDdu3dr//79tvrRuHHjw26TkpJS4WMVjUhXRkUnw5U+bumRxFDgkqRmzZpVuO/xxx8fnn9rR3Jysvbu3asDBw7Y3rcmHSqQDhkyRB988IG++OIL7dixQy1atAg/FprC0Ldv34ha79y5U1LwPVjZFSt++eUXNW3aVFJwrvXIkSO1Y8cOPfnkk3ryySd1zDHHqFu3burRo4f69OlT4YcjADWDsAvAcQoKCsK327dvX6nQWPrr+QMHDuj666+PmJ/avHlztWvXLjzSu3PnznCwqcwxK2IYxmG3qQq7xy09enyor8erOo2hRYsW2rt3b8R0ESc41Ehzv3791KBBA+Xn5+vdd9/VNddcIyk4TSb03rjooosi9gm99+Lj43X88cfb7s+JJ56ohQsX6qWXXtKrr76qDRs2aOvWrZo3b57mzZunpKQkjRw5UmPGjKnWhyEAlUfYBeA4pQPZrFmzbC/bNXXq1HCYGTZsmIYPH642bdpEbPP4448rMzOz2n11itInx1W0frBUtSkVUvBr+++//17ffvutcnNza3SFhZDqXs0+MTFR/fv315tvvqmFCxeGw+6CBQtkWVbEVIeQ0HuvSZMmVb7EdVJSkkaNGqVRo0Zp8+bN+vjjj/XJJ59o+fLlysvL0+OPP66tW7dqypQp1Xp9ACqHj5UAHKf0SVB2T+4KBAJ66623JEk9e/bUfffdVyboStK+ffuq10mHadSoUfj2oS5aUHpurx29e/eWFLy4xGuvvVbp/UzT1OTJk8usAhEauT5UoI3GlInQqgyrVq0Kj+SHVuAYNGhQmTndoffer7/+GpUrvqWmpuqqq67SY489pg8//FDnn3++JOm1115jPi9whBB2AThOly5dwrcPtVSWZVllRjF3794dHr0MLS/2W6ZpVmneqpOVXjXihx9+KHeb/Px8ZWVlVen4ffv2DX9omDVr1iGvIFbas88+q+eee05XXnmlPv7443B7aKpFaIm48nz99ddV6mtp55xzjpo2bSrTNPXBBx8oJycnfOW9305hkEpODPT7/Ye8Qp/P56vwsYpG1hs2bKi//OUv4fvff/99JV4BgOoi7AJwnFatWoVXDXjhhRfCKwH81htvvKGzzz5bd911lwKBgCRFXA2tohHOp59+OmLuaelVDWqrU089NRwg33777XK3+c9//lNhLQ/H7XZr0qRJMgxDe/fu1ejRow972dv58+dr+vTpkqRzzz1X55xzTvix0Ajq/v37tWXLljL7btmyRYsXL65SX0vzeDwaOHCgJOnDDz8MH/OEE05Q586dy2zfv3//8FSGJ598ssLjPvDAA+rTp0/EOrv//ve/1atXL914440V7lc6CHOiGnBkEHYBONKdd94pl8ulrVu36oYbbogIp0VFRXrllVf0wAMPaP/+/UpOTpbb7ZYUHD1r3769pOBXxaWXJNu5c6ceeughZWZm6pZbbgm3V3W000kaNGigQYMGSQpeKvnpp58OfwDw+Xz697//rccff7zC0e7K6NGjhyZMmCDDMPTNN99o8ODBev7557Vnz56I7b777jvdcccdmjBhggKBgDp16qRHHnkk4qS70AU3JGnatGkRJyWuW7dON954Y/jvWF2hqQyffPJJ+Gp7pdfWLS05OVk333yzJOm9997TX/7yl4gpL7t379bf/vY3vfbaa9q6dWvEFJn27dtr+/bt+vjjjzVp0qQya+1u2bIlfFGQJk2aVOtvAaDyOEENQI3Lzs6uMFyUdtVVV4Uv0HDGGWdoypQpmjhxolatWqUBAwaodevW8nq9+uWXX8JTFc455xzdfffdEce58847dfPNN+vAgQO69NJLdcwxx8gwDG3dulVut1vTp09Xenq6Zs+eLZ/Pp5tuukmpqanKzMxUWlpa9AtwhNx5551auXKltmzZounTp2vWrFk6+uij9csvvyg3N1e33XabLMsKX1K4KkaMGKHU1FQ99NBD2rJlix588EFNnjxZzZs3V0pKinbu3BkOv263W5dddpnuueeeMqOYZ5xxhnr37q1ly5Zp0aJF+vDDD9W6dWsVFBRoy5YtOuWUU/THP/5Rw4YNq1ZNpOCod9u2bfXzzz9r5cqVZS4P/FvXX3+9tmzZopdeekkvvviiXn31VbVu3VpFRUXasWNHeHR29OjRuuCCC8L7nXPOObrpppv0xBNP6IUXXtBLL70Ursv+/fvDy+M1aNBAM2bMiPgWAkDNIewCqHGFhYXhy6geym+XArv44ot1xhln6D//+Y9WrFihbdu2yefz6aijjlL37t114YUX6oILLiizTNd5552nZ555Rk8++aS+/vpr/fLLL2rSpImGDBmikSNHhq9s9uCDDyozM1M7duyQZVmOu2CCXc2bN9drr72mWbNmaenSpdq+fbv27dun0047Tdddd5169Oihxx57TFL1lkzr16+fevbsqXfeeUcffvihvvnmG+3atUu7du1ScnKy0tPT1b17d1100UU67rjjKjxOZmamZs+erYULF2rz5s3hkdKxY8fquuuu048//ljlPv7WkCFDNHPmTEnBUeVWrVpVuK3L5dKkSZM0cOBAvfzyy/ryyy+1detWGYYRnmJz9dVX67TTTiuz77hx43Teeedp/vz5WrFihbZv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9BctWPCWJOn222+SJM2d+6a2bduq22+/Sdddd4NGjRodPubq1V/pueee0TffrFVBQb4aN26iM8/sppEjb9TRR7eK6HtyckNNnTpDmZmP6quvvlBRkU/HH5+m0aNv0emnd61EtaOPsOsAXo9bEieoAQDgVAMHDtG//jVLb775erlh9403XldCQoIGDhyqSZP+rI8+WqZhw0bqrLO66+DBg3r55Rc0bdpkFRUV2ZrmsGPHdt1++81KSEjQ2LF/1LHHttXmzdmaMOGPKiryKTGxZNtfftmmMWNuUHJysm67bZxatTpGmzf/rCeeyNTtt9+k//znRbVo0VIjR94oj8er//1vnv74x3vVseNJatasubZt21rm+T/99BPdc884nXhiB40ff7eaN2+hjRs36KmnntBnn63Qv//9gho3bhLePj8/T3/841hlZAzUpZdeqS1bNisz8x/605/+qJdfnq9GjY6yVfdoIOw6QGjOLheVAADUVl/sWKO3NryrwkCh7X1dLkOmadVAr0rEu+M1OG2ATm/RpUr7Jycna8CAC/TGG6/r669X65RTTg0/9t133+j779dpyJCLFB8fJ4/Hoyuu+L1uuOHm8DYnn3yKBg3qpwUL3rIVdufNe1X5+Xm6774H1Lt3X3k8LvXvf548nnhNnjxJjRo1Cm+7efMmdelymi699Ap163a2JOmUU05VQUGBHn10upYvX6ZLL71CrVodo2bNmkmS2rY9Vh07dqrw+TMz/6GEhAQ98sjjSkkJPtdpp52uo45qrD//+R69/PILuummW8Pbb926Rffd9xddcMFgSVJ6+hnatGmTXnzxOWVlrVS/fgMq/dqjhbDrAKFpDAHTUsA05XYxlRoAULss3rRM2/N3xLobh7T452VVDrtS8MSzN954XfPnvxYRdkMnpl1yyRWKj0/QX/86tcy+ycnJatq0mX75ZZut5/z669UyDEPdup0T0d6nTz899NCDEW1nntldZ57Zvcwxjj32OEnS9u32nnvHju3auHGDevXqEw66Ieee20tut1urVn0e0W4Yhvr27R/R1qZNqiRp3759tp4/Wgi7DlD6whJ+vyV3XAw7AwBAFfQ/trfjR3b7t+1drWOkpbXTaaedrqVLl2js2DuVktJIeXm5WrLkXZ1yyqnhObHff79Or776slat+lx79uxWUVFR+BilR2IrY9eunUpK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82JTU1N133336ZJLLlFiIuvBVRZXUAMAAIiOal9BTZISEhKUkJBQ7mPXXnttNJ6iXmHOLgAAQHRU+1pz3377raZMmRLRtm7dOl1zzTVKT0/XoEGDtHDhwuo+Tb3idhkKXcCNsAsAAFB11Qq769ev1zXXXKMXXnhBphkMZfv379fIkSOVlZWluLg4bdiwQePHj9eqVaui0uH6wDCM8Ogu0xgAAACqrlph95lnnpHf79esWbPClwueO3eudu/erd///vf67LPPtGjRIqWkpOjZZ5+NSofri9C8XUZ2AQAAqq5aYffzzz/XgAED1KtXr3Dbe++9J4/HE740b9u2bTVgwAB9+eWX1etpPRMe2SXsAgAAVFm1wu7OnTvVrl278P28vDytXbtWp556qpo0aRJub926tXbv3l2dp6p3CLsAAADVV62w63a7VVhYGL6/cuVK+f3+MpcLLigoYOkxm7wetyTm7AIAAFRHtcLuscceqxUrVoTvv/jiizIMQ+edd17Edl9//bVatmxZnaeqd0Jzdv2M7AIAAFRZtdbZHTBggB577DFdddVVcrlc+vLLL3XaaaepU6dOkqRAIKAXX3xRK1as0MiRI6PS4foiNI0hYFoKmKbcrmqvEgcAAFDvVCvsjho1SqtWrdLHH38sSWrVqpWmT58efnzjxo2aPHmyjjnmGMKuTaUvLOH3W3LHxbAzAAAAtVS1wm58fLyefvppbdy4Ufv371fHjh0VF1eSytLS0jRixAhdd911ESes4fAirqIWMBUvdwx7AwAAUDtF5XLBxx13XLnthmFowoQJ0XiKeic0Z1diRQYAAICqikrY/eWXX7Rw4UJ9++232rNnjwzDUNOmTdW5c2cNHDhQjRs3jsbT1CsRI7v+QAx7AgAAUHtVO+z++9//1owZM+T3+2VZVsRj8+fP14wZM/Tggw9q8ODB1X2qesXjYWQXAACguqoVdpctW6apU6cqMTFRF154obp06aImTZrINE3t3r1bq1at0qJFizRhwgS1bdtWXbp0iVa/67zSI7tFhF0AAIAqqVbYfe6559SoUSO98sorOvbYY8s8ftVVV+mGG27Q1VdfraeeekqPPfZYdZ6uXmHOLgAAQPVVa/HWtWvX6vzzzy836Ia0b99e559/vr744ovqPFW989vVGAAAAGBftcJubm6ujj766MNu16ZNG+3du7c6T1XvxDFnFwAAoNqqFXZTUlK0efPmw263detWpaSkVOep6h2vp2RdXS4ZDAAAUDXVCrunnnqq3n33Xa1fv77CbdatW6cFCxbotNNOq85T1TteRnYBAACqrVonqF133XX64IMPdPnll2vQoEFKT08PXylt165dysrK0qJFixQIBDRq1KiodLi+YM4uAABA9VUr7J511ll68MEH9dBDD2nevHmaP39+xOOWZSkxMVGTJ0/WGWecUZ2nqndYjQEAAKD6qn1Ricsvv1x9+vTRO++8o7Vr12rXrl3hK6idcsopGjRoEFdQqwKmMQAAAFRfVC4X3KxZMw0bNqzCx5csWaJ58+YpMzMzGk9XL3i4XDAAAEC1VesEtcratGmTlixZciSeqs5gzi4AAED1HZGwC/uYswsAAFB9hF2HYs4uAABA9RF2HYqwCwAAUH2EXYdizi4AAED1EXYdijm7AAAA1UfYdSimMQAAAFSf7XV2zz77bNtPcvDgQdv71HeEXQAAgOqzHXb37NlTpScyDKNK+9VXzNkFAACoPtthl4tDHBlul0suw5BpWfL5CLsAAABVYTvstm7duib6gXJ4PS4V+gKM7AIAAFQRJ6g5WGgqg88fiHFPAAAAaifCroOVhF1GdgEAAKqCsOtghF0AAIDqIew6WDjsMmcXAACgSgi7Dha6iprPb8qyrBj3BgAAoPZxdNidO3euBg4cqM6dO6tnz56aNm2afD5fhdvv2bNHkydPVt++fdW5c2edd955mjZtWq29qEVoZNeypIBJ2AUAALDL9tJjR8r8+fM1ceJETZgwQf369dP69es1ceJE5efna9KkSWW2N01T119/vfLz8/XQQw+pTZs2ysrK0v33369ff/1Vf//732PwKqrnt1dR87gd/dkEAADAcRwbdjMzMzVo0CCNGDFCkpSamqqdO3dq0qRJGjNmjFq2bBmx/XfffadNmzZp1qxZOuuss8L7ZGVlacGCBbIsq9Zdxc3rjryKWmIM+wIAAFAbOXKocOPGjdq8ebN69+4d0d6rVy+Zpqnly5eX2efkk09WVlZWOOiGuFwuud3uWhd0pciRXT8rMgAAANjmyJHd7OxsSVLbtm0j2lu1aiWv16sNGzYc9hh+v1/vv/++3nrrLd122222nt/lMuRyRS8cu4tHaN02pyHEed3h26Ykj8eRn02irqr1qq+olz3U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9hj00jleffVVXXjhhUpPT1efPn305z//Wbt27Qo//sMPP+j6669Xenq60tPTdcMNN+inn36KYY9jxzRNPfPMMxo8eLC6dOmibt26aezYsdqyZUt4G95jFRs5cqQ6dOignJyccBv1KtG3b1916NChzM/gwYPD21CvsnJycnTrrbfq9NNP15lnnqkxY8Zo69at4cepWVBOTk6576/Qz+uvvy6JelWahRozb948q0OHDtacOXOsn3/+2Xrvvfes7t27W/fff3+suxZze/bssUaPHm316NHDOv3006077rgj4vGffvrJ6tKli3XPPfdYP/30k5WVlWVdfvnl1uDBg62ioqIY9Tq2nnnmGatjx47W008/bW3cuNFatmyZ1atXL+vqq6+2TNO0du/ebXXv3t0aNWqUtW7dOuvrr7+2Ro8ebZ177rnWvn37Yt39I27KlCnWaaedZs2fP9/6+eefrY8++sjq16+f1bdvX6uwsJD32CHMnTvX6tSpk9W+fXtr8+bNlmXxb/K3+vTpY02dOtXasWNHxM/u3bsty6Je5dm3b5/Vp08f66abbrK+//57a/Xq1dbFF19sZWRkWIFAgJqV4vf7y7y3duzYYb3xxhtW586drU2bNlEvGwi7Nahfv37W+PHjI9pefPFFq2PHjtYvv/wSo145w3PPPWdde+211s6dO60+ffqUCbsTJkywevfubfl8vnDbTz/9ZLVv39763//+d6S7G3OmaVrnnnuuNWHChIj2l19+2Wrfvr313XffWY8//rh16qmnWnv37g0/vnfvXqtLly7WE088caS7HFM+n88677zzrMzMzIj2+fPnW+3bt7fWrFnDe6wC27dvt7p27WpNmjQpIuxSr0h9+vSxHnvssQofp15lZWZmWueee65VUFAQbsvOzrYWLFhgHTx4kJodRlFRkZWRkWE9/PDDlmXxHrODaQw1ZOPGjdq8ebN69+4d0d6rVy+Zpqnly5fHqGfO0Lt3b82ZM0dNmzYt9/GPPvpIPXr0kMfjCbelpaWpTZs2+vDDD49UNx3DMAy99dZb+tOf/hTR3rJlS0lSXl6ePvroI6Wnp6tRo0bhxxs1aqRTTz213tXM4/Fo6dKluuWWWyLaXa7gf/K8Xi/vsQo8+OCDSk9P1/nnnx/RTr3soV5lvfvuu+rfv78SEhLCbccdd5wyMjIUHx9PzQ7jP//5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8tSoUSOlp6dr1KhR5Y4a//LLL5o1a5Y+/PBD7dy5U40aNVKfPn1066236uijjy63bytWrNBjjz2mdevWSZJOPvlkjR8/XqeffnrEdl9++aWeeuoprV69Wnv27FFycrJSU1M1ZMgQXXPNNXK73dUvFABUAmEXABzqs88+04IFC/S73/1OPXr0UE5Ojt58803ddNNNmjVrlvr27Rve9rHHHtPMmTPVuHFjDRgwQC1bttTPP/+sRYsW6f3339eMGTN0wQUXhLffsGGDrrrqKhUUFGjo0KFq06aNfvzxR7322mt67733NHfuXLVt2zaiP5988omeeeYZDR06VL1799aKFSv06aefatSoUXrnnXfUqlUrSVJWVpaGDx+uhIQEXXDBBWrdurUOHDigZcuWacqUKVq9erUeeeSRI1NEALAAADVi8+bNVvv27a2zzjrL1n733HOP1b59e6tDhw7W8uXLIx579dVXrfbt21sZGRnhtm+++cbq0KGDddZZZ1nbtm2L2P7zzz+3OnbsaJ155plWfn5+uP2SSy6x2rdvX+b4//3vf6327dtbo0ePDrddc801Vvv27a3u3btb2dnZ4XbTNK0RI0ZY7du3t+bMmRNuHz9+vNW+fXvrgw8+iDh2UVGRdfXVV1tnnHGGtXXrVls1AYCqYmQXAGqYZVnKyck55DZer1ctW7aMaEtPT1ePHj0i2i666CJNmzZNGzZs0ObNm5Wamqr58+fLsiz9/ve/LzP9oGvXrurWrZtWrFih5cuXa8CAAfruu++0du1adezYsczxL730Um3ZskUtWrQo08crrrhCxx13XPi+YRjq2bOnPvnkE23ZsiXcvm/fPkkqM1XB6/Xq2WeflcfD/3oAHDn8FwcAati+ffsOuwRZx44d9cYbb0S0/XYerBQMkMcff7y++uorbdiwQampqVq7dm2F20tSly5dtGLFCn3zzTcaMGBAeL3ck046qcy2CQkJuvvuu8s9TufOncu0paSkSJJyc3PDbX369NHy5cs1fvx4jRo1Sv3799cJJ5wgSQRdAEcc/9UBgBqWlJSk6dOnH3Kb5OTkMm1NmzYtd9ujjjpKkrR//35J0q5duw65fZMmTSRJe/bsidg+FFQrq7ztXa7gtYksywq3/eEPf1BeXp6eeOIJPfLII3rkkUfUvHlz9ejRQxdffLG6detm63kBoDoIuwBQw7xer/r37297v1CQ/C3TNCUFlzSTgtMJpMjAWd72oe1Cxy0qKrLdp8q68cYbdfXVV+uDDz7QRx99pI8//ljz5s3TvHnzdPnll2vy5Mk19twAUBqXCwYAhwqNxP7W3r17JZWM5IZ+h0Zsf2v37t3lbh9qrykNGzbUkCFDNG3aNC1fvlxPP/20WrZsqblz52rFihU1+twAEELYBQCHWr16dZk2v9+v7OxsSVKbNm0kSaeccookadWqVeUe54svvojYLvQ7KytLgUAgYlvTNHXHHXfo9ttvl9/vr1K/9+3bF3HCmhQcVe7Ro4euv/56SdI333xTpWMDgF2EXQBwqM8++0yff/55RNvrr7+uAwcOqFOnTuHVGy699FK5XC699NJL2rZtW8T2H3/8sVatWqWWLVuGV17o0KGDTj75ZO3atUuvv/56xPbvvPOOFixYoLy8vCqdTLZnzx6dc845uu6668KrMpQWCrmhNXkBoKYxZxcAalhhYaGefvrpw24XHx+va665Jnz/wgsv1I033qh+/frp+OOPD19Uwu1266677gpvd+KJJ+qOO+7QI488oksuuUQZGRlq2rSpNmzYoPfee08JCQmaNm2avF5veJ+HHnpI1157re6//3599tlnOuGEE/TTTz9pwYIFSk5OrnBFhsNp3Lixbr75Zj3++OMaNGiQ+vfvr6OPPloFBQX64osvtHLlSp188sn63e9+V6XjA4BdhF0AqGEFBQWHXY1BCs5xLR12O3furEsvvVSZmZlaunSpTNNUly5ddNttt+mcc86J2Hf06NFq166dnnvuOb311lsqKChQkyZNlJGREX6stJNOOknz5s1TZmamPvnkEy1cuFCNGjXSoEGDdOutt5a5epodt956qzp06KBXXnlFixcv1t69e+X1enXcccfp9ttv1/DhwxUXF1fl4wOAHYZV0em7AICYmDBhgubNm6eJEydGhF8AgH3M2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdxQlqAAAAqLMY2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ31/+1MPX1gW6EJAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" + "ename": "OSError", + "evalue": "'seaborn-v0_8' not found in the style library and input is not a valid URL or path; see `style.available` for list of available styles", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/style/core.py:127\u001b[0m, in \u001b[0;36muse\u001b[0;34m(style)\u001b[0m\n\u001b[1;32m 126\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 127\u001b[0m rc \u001b[38;5;241m=\u001b[39m \u001b[43mrc_params_from_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstyle\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muse_default_template\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 128\u001b[0m _apply_style(rc)\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/__init__.py:854\u001b[0m, in \u001b[0;36mrc_params_from_file\u001b[0;34m(fname, fail_on_error, use_default_template)\u001b[0m\n\u001b[1;32m 840\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 841\u001b[0m \u001b[38;5;124;03mConstruct a `RcParams` from file *fname*.\u001b[39;00m\n\u001b[1;32m 842\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 852\u001b[0m \u001b[38;5;124;03m parameters specified in the file. (Useful for updating dicts.)\u001b[39;00m\n\u001b[1;32m 853\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m--> 854\u001b[0m config_from_file \u001b[38;5;241m=\u001b[39m \u001b[43m_rc_params_in_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfail_on_error\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfail_on_error\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 856\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m use_default_template:\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/__init__.py:780\u001b[0m, in \u001b[0;36m_rc_params_in_file\u001b[0;34m(fname, transform, fail_on_error)\u001b[0m\n\u001b[1;32m 779\u001b[0m rc_temp \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m--> 780\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m _open_file_or_url(fname) \u001b[38;5;28;01mas\u001b[39;00m fd:\n\u001b[1;32m 781\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/contextlib.py:135\u001b[0m, in \u001b[0;36m_GeneratorContextManager.__enter__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 135\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 136\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m:\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/__init__.py:757\u001b[0m, in \u001b[0;36m_open_file_or_url\u001b[0;34m(fname)\u001b[0m\n\u001b[1;32m 756\u001b[0m encoding \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 757\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mencoding\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mencoding\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[1;32m 758\u001b[0m \u001b[38;5;28;01myield\u001b[39;00m f\n", + "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'seaborn-v0_8'", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[8], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstyle\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43muse\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseaborn-v0_8\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2\u001b[0m plt\u001b[38;5;241m.\u001b[39mtitle(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLearning Curves\u001b[39m\u001b[38;5;124m\"\u001b[39m, fontsize\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m20\u001b[39m)\n\u001b[1;32m 3\u001b[0m plt\u001b[38;5;241m.\u001b[39mplot(np\u001b[38;5;241m.\u001b[39mlinspace(\u001b[38;5;241m1\u001b[39m, n_epochs, n_epochs), epoch_loss_list, color\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mC0\u001b[39m\u001b[38;5;124m\"\u001b[39m, linewidth\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m2.0\u001b[39m, label\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTrain\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/style/core.py:130\u001b[0m, in \u001b[0;36muse\u001b[0;34m(style)\u001b[0m\n\u001b[1;32m 128\u001b[0m _apply_style(rc)\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mIOError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[0;32m--> 130\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mIOError\u001b[39;00m(\n\u001b[1;32m 131\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{!r}\u001b[39;00m\u001b[38;5;124m not found in the style library and input is not a \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 132\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalid URL or path; see `style.available` for list of \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 133\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mavailable styles\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(style)) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01merr\u001b[39;00m\n", + "\u001b[0;31mOSError\u001b[0m: 'seaborn-v0_8' not found in the style library and input is not a valid URL or path; see `style.available` for list of available styles" + ] } ], "source": [ @@ -680,6 +900,7 @@ "execution_count": 15, "id": "f71e4924", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -756,7 +977,7 @@ "formats": "py:percent,ipynb" }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -770,7 +991,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py index d59d634c..ca0c9f23 100644 --- a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py +++ b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py @@ -6,9 +6,9 @@ # extension: .py # format_name: percent # format_version: '1.3' -# jupytext_version: 1.14.1 +# jupytext_version: 1.14.4 # kernelspec: -# display_name: Python 3 +# display_name: Python 3 (ipykernel) # language: python # name: python3 # --- @@ -66,8 +66,9 @@ from generative.inferers import DiffusionInferer # TODO: Add right import reference after deployed -from generative.networks.nets import DiffusionModelUNet -from generative.schedulers import DDPMScheduler + +from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet +from generative.networks.schedulers.ddpm import DDPMScheduler print_config() @@ -231,6 +232,7 @@ for step, batch in progress_bar: images = batch["image"].to(device) classes = batch["class"].to(device) + print('images', images.shape, 'classes', classes.shape, classes) optimizer.zero_grad(set_to_none=True) with autocast(enabled=True): diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb index f8e67fbd..248180d7 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb @@ -5,13 +5,12 @@ "id": "63d95da6", "metadata": {}, "source": [ - "# Classifier-free Guidance\n", + "# Anomaly Detection with classifier guidance\n", "\n", - "This tutorial illustrates how to use MONAI for training a denoising diffusion probabilistic model (DDPM)[1] to create synthetic 2D images using the classifier-free guidance technique [2] to perform conditioning.\n", + "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", "\n", "\n", - "[1] - Ho et al. \"Denoising Diffusion Probabilistic Models\" https://arxiv.org/abs/2006.11239\n", - "[2] - Ho and Salimans \"Classifier-Free Diffusion Guidance\" https://arxiv.org/abs/2207.12598\n", + "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", "\n", "\n", "TODO: Add Open in Colab\n", @@ -21,13 +20,132 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "id": "75f2d5f3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "running install\n", + "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n", + " warnings.warn(\n", + "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/easy_install.py:144: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n", + " warnings.warn(\n", + "running bdist_egg\n", + "running egg_info\n", + "writing generative.egg-info/PKG-INFO\n", + "writing dependency_links to generative.egg-info/dependency_links.txt\n", + "writing requirements to generative.egg-info/requires.txt\n", + "writing top-level names to generative.egg-info/top_level.txt\n", + "reading manifest file 'generative.egg-info/SOURCES.txt'\n", + "writing manifest file 'generative.egg-info/SOURCES.txt'\n", + "installing library code to build/bdist.linux-x86_64/egg\n", + "running install_lib\n", + "warning: install_lib: 'build/lib' does not exist -- no Python modules to install\n", + "\n", + "creating build/bdist.linux-x86_64/egg\n", + "creating build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/requires.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "zip_safe flag not set; analyzing archive contents...\n", + "creating 'dist/generative-0.1.0-py3.10.egg' and adding 'build/bdist.linux-x86_64/egg' to it\n", + "removing 'build/bdist.linux-x86_64/egg' (and everything under it)\n", + "Processing generative-0.1.0-py3.10.egg\n", + "Removing /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", + "Copying generative-0.1.0-py3.10.egg to /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "generative 0.1.0 is already the active version in easy-install.pth\n", + "\n", + "Installed /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", + "Processing dependencies for generative==0.1.0\n", + "Searching for lpips==0.1.4\n", + "Best match: lpips 0.1.4\n", + "Processing lpips-0.1.4-py3.10.egg\n", + "lpips 0.1.4 is already the active version in easy-install.pth\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg\n", + "Searching for tqdm==4.64.1\n", + "Best match: tqdm 4.64.1\n", + "Processing tqdm-4.64.1-py3.10.egg\n", + "tqdm 4.64.1 is already the active version in easy-install.pth\n", + "Installing tqdm script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg\n", + "Searching for scipy==1.9.0\n", + "Best match: scipy 1.9.0\n", + "Adding scipy 1.9.0 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for numpy==1.23.2\n", + "Best match: numpy 1.23.2\n", + "Adding numpy 1.23.2 to easy-install.pth file\n", + "Installing f2py script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing f2py3 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing f2py3.10 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for torchvision==0.13.1\n", + "Best match: torchvision 0.13.1\n", + "Adding torchvision 0.13.1 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for torch==1.12.1\n", + "Best match: torch 1.12.1\n", + "Adding torch 1.12.1 to easy-install.pth file\n", + "Installing convert-caffe2-to-onnx script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing convert-onnx-to-caffe2 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing torchrun script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for Pillow==9.2.0\n", + "Best match: Pillow 9.2.0\n", + "Adding Pillow 9.2.0 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for requests==2.28.1\n", + "Best match: requests 2.28.1\n", + "Adding requests 2.28.1 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for typing-extensions==4.3.0\n", + "Best match: typing-extensions 4.3.0\n", + "Adding typing-extensions 4.3.0 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for certifi==2022.6.15\n", + "Best match: certifi 2022.6.15\n", + "Adding certifi 2022.6.15 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for urllib3==1.26.11\n", + "Best match: urllib3 1.26.11\n", + "Adding urllib3 1.26.11 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for idna==3.3\n", + "Best match: idna 3.3\n", + "Adding idna 3.3 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for charset-normalizer==2.1.1\n", + "Best match: charset-normalizer 2.1.1\n", + "Adding charset-normalizer 2.1.1 to easy-install.pth file\n", + "Installing normalizer script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Finished processing dependencies for generative==0.1.0\n" + ] + } + ], "source": [ + "!python /home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/setup.py install\n", "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "!python -c \"import seaborn\" || pip install -q seaborn\n", "%matplotlib inline" ] }, @@ -41,45 +159,27 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "id": "972ed3f3", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "MONAI version: 1.1.dev2239\n", - "Numpy version: 1.23.3\n", - "Pytorch version: 1.8.0+cu111\n", - "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", - "MONAI rev id: 13b24fa92b9d98bd0dc6d5cdcb52504fd09e297b\n", - "MONAI __file__: /media/walter/Storage/Projects/GenerativeModels/venv/lib/python3.8/site-packages/monai/__init__.py\n", - "\n", - "Optional dependencies:\n", - "Pytorch Ignite version: 0.4.10\n", - "Nibabel version: 4.0.2\n", - "scikit-image version: NOT INSTALLED or UNKNOWN VERSION.\n", - "Pillow version: 9.2.0\n", - "Tensorboard version: 2.11.0\n", - "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", - "TorchVision version: 0.9.0+cu111\n", - "tqdm version: 4.64.1\n", - "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", - "psutil version: 5.9.3\n", - "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", - "einops version: 0.6.0\n", - "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", - "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", - "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", - "\n", - "For details about installing the optional dependencies, please visit:\n", - " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", - "\n" + "ename": "ZipImportError", + "evalue": "bad local file header: '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZipImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[4], line 29\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcuda\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mamp\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m GradScaler, autocast\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtqdm\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m tqdm\n\u001b[0;32m---> 29\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mgenerative\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01minferers\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DiffusionInferer\n\u001b[1;32m 31\u001b[0m \u001b[38;5;66;03m# TODO: Add right import reference after deployed\u001b[39;00m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mgenerative\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnetworks\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnets\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdiffusion_model_unet\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DiffusionModelUNet, DiffusionModelEncoder\n", + "File \u001b[0;32m:196\u001b[0m, in \u001b[0;36mget_code\u001b[0;34m(self, fullname)\u001b[0m\n", + "File \u001b[0;32m:752\u001b[0m, in \u001b[0;36m_get_module_code\u001b[0;34m(self, fullname)\u001b[0m\n", + "File \u001b[0;32m:598\u001b[0m, in \u001b[0;36m_get_data\u001b[0;34m(archive, toc_entry)\u001b[0m\n", + "\u001b[0;31mZipImportError\u001b[0m: bad local file header: '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg'" ] } ], @@ -98,13 +198,14 @@ "import shutil\n", "import tempfile\n", "import time\n", - "\n", + "import os\n", "import matplotlib.pyplot as plt\n", + "import seaborn\n", "import numpy as np\n", "import torch\n", "import torch.nn.functional as F\n", "from monai import transforms\n", - "from monai.apps import MedNISTDataset\n", + "from monai.apps import MedNISTDataset, DecathlonDataset\n", "from monai.config import print_config\n", "from monai.data import CacheDataset, DataLoader\n", "from monai.utils import first, set_determinism\n", @@ -114,10 +215,13 @@ "from generative.inferers import DiffusionInferer\n", "\n", "# TODO: Add right import reference after deployed\n", - "from generative.networks.nets import DiffusionModelUNet\n", - "from generative.schedulers import DDPMScheduler\n", + "from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder\n", "\n", - "print_config()" + "from generative.networks.schedulers.ddpm import DDPMScheduler\n", + "from generative.networks.schedulers.ddim import DDIMScheduler\n", + "print_config()\n", + "\n", + "\n" ] }, { @@ -130,9 +234,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "id": "8b4323e7", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -142,13 +247,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/tmp142o2qtd\n" + "/home/juliawolleb/PycharmProjects/MONAI/data_brats\n" ] } ], "source": [ "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", - "root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "#root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "root_dir='/home/juliawolleb/PycharmProjects/MONAI/data_brats'\n", + "\n", "print(root_dir)" ] }, @@ -162,9 +269,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 15, "id": "34ea510f", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -179,152 +287,122 @@ "id": "fac55e9d", "metadata": {}, "source": [ - "## Setup MedNIST Dataset and training and validation dataloaders\n", - "In this tutorial, we will train our models on the MedNIST dataset available on MONAI\n", - "(https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset).\n", - "Here, we will use the \"Hand\" and \"HeadCT\", where our conditioning variable `class` will specify the modality." + "## Setup BRATS Dataset for 2D slices and training and validation dataloaders\n", + "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "da1927b0", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "2022-12-10 11:50:40,187 - INFO - Downloaded: /tmp/tmp142o2qtd/MedNIST.tar.gz\n", - "2022-12-10 11:50:40,255 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", - "2022-12-10 11:50:40,256 - INFO - Writing into directory: /tmp/tmp142o2qtd.\n" + "Task01_BrainTumour.tar: 71%|█████████▉ | 5.03G/7.09G [04:43<01:46, 20.8MB/s]" ] } ], "source": [ - "train_data = MedNISTDataset(root_dir=root_dir, section=\"training\", download=True, progress=False, seed=0)\n", - "train_datalist = []\n", - "for item in train_data.data:\n", - " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", - " train_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})" - ] - }, - { - "cell_type": "markdown", - "id": "6986f55c", - "metadata": {}, - "source": [ - "Here we use transforms to augment the training dataset, as usual:\n", - "\n", - "1. `LoadImaged` loads the hands images from files.\n", - "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", - "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", - "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", "\n", - "### Classifier-free guidance during training\n", "\n", - "In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an \"unconditional\" class.\n", - "Here we specify the \"unconditional\" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad:\n", + "batch_size = 2\n", + "channel = 0 # 0 = Flair\n", + "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", "\n", - "`transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))`\n", - "\n", - "Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "e3184009", - "metadata": { - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 15990/15990 [00:08<00:00, 1784.85it/s]\n" - ] - } - ], - "source": [ "train_transforms = transforms.Compose(\n", " [\n", - " transforms.LoadImaged(keys=[\"image\"]),\n", - " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", - " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", - " transforms.RandAffined(\n", - " keys=[\"image\"],\n", - " rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)],\n", - " translate_range=[(-1, 1), (-1, 1)],\n", - " scale_range=[(-0.05, 0.05), (-0.05, 0.05)],\n", - " spatial_size=[64, 64],\n", - " padding_mode=\"zeros\",\n", - " prob=0.5,\n", - " ),\n", - " transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)),\n", - " transforms.Lambdad(\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + " transforms.LoadImaged(keys=[\"image\",\"label\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\",\"label\"]),\n", + " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", + " transforms.AddChanneld(keys=[\"image\"]),\n", + " transforms.EnsureTyped(keys=[\"image\",\"label\"]),\n", + " transforms.Orientationd(keys=[\"image\",\"label\"], axcodes=\"RAS\"),\n", + " transforms.Spacingd(\n", + " keys=[\"image\",\"label\"],\n", + " pixdim=(3.0, 3.0, 2.0),\n", + " mode=(\"bilinear\", \"nearest\"),\n", " ),\n", + " transforms.CenterSpatialCropd(keys=[\"image\",\"label\"], roi_size=(64, 64, 64)),\n", + " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", + " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", + " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", " ]\n", ")\n", - "train_ds = CacheDataset(data=train_datalist, transform=train_transforms)\n", - "train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True)" + "train_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"training\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "nb_3D_images_to_mix = 2\n", + "train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", + "print(f'Image shape {train_ds[0][\"image\"].shape}')\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 7, - "id": "4c11b93f", - "metadata": { - "jupyter": { - "outputs_hidden": false - } - }, + "execution_count": 13, + "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2022-12-10 11:51:08,067 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", - "2022-12-10 11:51:08,067 - INFO - File exists: /tmp/tmp142o2qtd/MedNIST.tar.gz, skipped downloading.\n", - "2022-12-10 11:51:08,068 - INFO - Non-empty folder exists in /tmp/tmp142o2qtd/MedNIST, skipped extracting.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Loading dataset: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1977/1977 [00:01<00:00, 1545.02it/s]\n" + "2023-02-02 10:39:45,467 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-02-02 10:39:45,469 - INFO - File exists: /tmp/tmpyurp7egh/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-02-02 10:39:45,471 - INFO - Non-empty folder exists in /tmp/tmpyurp7egh/Task01_BrainTumour, skipped extracting.\n", + "Image shape torch.Size([1, 64, 64, 64])\n" ] } ], "source": [ - "val_data = MedNISTDataset(root_dir=root_dir, section=\"validation\", download=True, progress=False, seed=0)\n", - "val_datalist = []\n", - "for item in val_data.data:\n", - " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", - " val_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})\n", - "\n", "\n", - "val_transforms = transforms.Compose(\n", - " [\n", - " transforms.LoadImaged(keys=[\"image\"]),\n", - " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", - " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", - " transforms.Lambdad(\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - " ),\n", - " ]\n", + "val_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"validation\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", ")\n", - "val_ds = CacheDataset(data=val_datalist, transform=val_transforms)\n", - "val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True)" + "val_loader_3D = DataLoader(val_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", + "print(f'Image shape {val_ds[0][\"image\"].shape}')\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n" ] }, { @@ -337,38 +415,26 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 47, "id": "4105a01f", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", - "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", - " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "batch shape: (128, 1, 64, 64)\n" + "Batch shape: torch.Size([128, 1, 64, 64])\n", + "Slices class: tensor([0., 0., 0., 1.])\n" ] }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAE4CAYAAACKfUBxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABtbUlEQVR4nO3dWcxe1132/1XaJLYTu57nebZjO3HiTE3akgpBqYDSqoflBM6R4ICzAgdUHCIVkFAlhBAHtEWIilKavm2aqRnt2Ek8z3Y8D/EQx3Zip/Q9+L9/ve+6fld8/7qy7/08tr+fs7W17j2utfZ6tp597U/88pe//GUBAAAAAAAAevRrI70DAAAAAAAAuP3wUAoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN59Kltx6dKlYdkvf/nLTnfm//eJT3yi03qD/NqvDX429z//8z8D63z44Ydh2Z133jlwPfo7tz+ZYx3m+XDr1mWZ3/3iF7/obPstdZxPfvKTA+tcvnw5LBs3blxVvnbtWqijx+KOTduEO4677rorLNPtjRkzJtS5evVqVb7jjjtCnYxPfSoOFZlj03ObOTa3rY0bN1bl6dOnhzqub2l7c2OW/s6tR3+XbWu6Lrf9zDiqdTLH0bruTF93dbL7NEjm3GbPY+a8tdRx+hx/W69/5rpl1u3quP3WMcptX8ckN45NmDChKuvYW0op8+bNC8suXbpUlT/96U+HOjqWXL9+PdTR/T537lyo88Mf/jAsA/qSGTdcncx9vKXOMLff1Vh7s3j//fer8vnz50doT4CRl51/ZujY4uZRLfPf7P5k5naZfczUGWmZc8J/SgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lM6UyWUB9a3mH0r2v3pK74rbntu+yiJTL0FHu/GfyQjJ5WYPWm1135neZOq3vwmaO1R3HBx98UJVdxlQm08llimX2Seu47V+5cmXgelydrt6XHqZMplamjWQydTK/G2amVJ+5P9nfqWFmSmXHlkF1WjOlnK5yvzLnLaM192qYbUvHkUx+YimlLFiwoCq7sW3WrFlVWfNTSill/PjxVdndM10W1Fe/+tWq/Mgjj4Q6f/VXf1WVNYeqlDjWu2wqjKzMHGWYmUp9bv92y1S63bXM44FbVWY+5vJz3Ryh5XeZ7bu5TuuzBuXG/9GYIdWCkQ4AAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPRuVGZKtea1tP4ms73Wd1gzdPuZ/CinyyycDPfOrOpqe5l36jPnyNHj0IypUnyGiWafuEyVTKaQrjublzRmzJiqnMm0crlTun2Xn+XWnbn+g/anlFzuUyZTyu3j7ZQp1ZrppLrMlGq5tpl9yvb11iymQetx+sx5ac2Ual239i3Xj9euXRuWTZs2rSqfP38+1Jk3b94Ny6WUMmfOnKq8fPnyUGfixIlhmY7RDz30UKizYsWKqvzyyy+HOjpGurFmpA0z00jXnVlP39sHblW0d+D/cn9rae6jy49yf8fpvbyrbKbWbFJH98n97TUac79bMNIBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAvRsVQed9BsR2ua1MiK/WcYGFGsjmzrULaMsEZGeCflX2HLW0iZZw7I/aVlfXUtfjAvKcTPikhui566jXzdXJhK9nwnfvvvvusEyPNxv0PqwQZbctlQ11zgSNt4SRO649arsdZoiio9tvPY7WDw20BJ239uvWYMm+r8kgrR/e6Cro3K1HQ0THjRsX6hw9ejQsO3PmTFW+fPlyqPOXf/mXVfmJJ54YuP2dO3eGOqtWrQrLvvWtb1Xls2fPhjp//Md/XJV3794d6rz77rtV2d0jJkyYEJYNK0Sc4GPg9kF/B/4vnQ+UEsPPXZ3WuZ7+TeLmVV3N9VvD2HWM6Gpe2zdGOgAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0Lt0plTr+4mt+RjD2lar1iwWPW9uPZlMFXf+M++wdvUuutunluvWmk3mjkO333r99d3jO+64I7V9fc/X5aVkrv/7778/cPsui0tzTdzx6z5dvXo11BkzZkxYlqHHksn9ylzHVpmcnUzuUDavR2X6bSZTyWnJhvs4v1OZ4289t131464ypfrMOHQybSSTjVZKd+dWx4grV66EOm7dmo/gxrZ//ud/rsrf/va3Qx3NedKMp1JK+fd///ewTPfz+eefD3XGjx9flZ988slQ5zvf+U5VdplaLq8PAD4uMqWAG9P5j7tHu7+jLl26NHDd2v9cXpWu2/095Lav9Vqfmei6yZQCAAAAAAAAkngoBQAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAetdp0PlIB8Q6uk+Z4NuugpdLiSFmLvzszjvvrMoaDltKDNUuJYatuRC1TIhvyzkapmzQe0YmIFLX7c6/hpE799xzT1g2c+bMgevRNuGutbsmGtDrfjdt2rSqrG2tlHi8LiBw48aNYdmwuOuv1zHbHlqCtlsDs1uD1vsMOm+t07qtPsMWu7q2rdeoK8Nso610jHLjyMWLFweu5+DBg2GZrsuFiE6fPr0q33XXXaGOu//p+OfC0Dds2FCVP/vZz4Y6Ov6dOnUq1AGAPnT14SHgZpT5W/eDDz4IdaZMmRKWfeYzn6nKx44dC3V02fLly0OdM2fO3LBciv8bUedN7u+4jNaPiI02/KcUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoXTpTajS+r6z7lMlLallvdj0ui0jzea5evRrqXLlypSq7d0NdNpLuk6uj68qco+z76l2d28x6W6+tvmfszpHuk8s0uX79elimuSYTJ04MdRYtWlSVL1y4EOo89thjVXnevHmhjvPuu+9W5WXLloU6+g71n/3Zn4U6d9xxR1XOvpucySvTc5vJGOoy02lYmVLZ8bDleFvPY0s2VWsdx+3TsDLtsm2k5dpmzmN2jGxZd2sO1zDv0Tr+ZXLf3O90PCwl3iPHjh0b6mg+hKvzN3/zN2GZnpNVq1aFOs8880xVdrkPuo/z588Pdfbu3RuWAUDXMnN94Fbl2vqYMWOqssvvdff/v//7v6/K7m/0kydPVuVJkyaFOt/4xjeq8lNPPRXqtM5tM3Pk0fiMpgX/KQUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0rveg867CeH+Ver+qbIiYHsuKFStCnalTp1bls2fPhjrbt2+vyu+9916oo6HajgvjzgSkZQLTnZbz3xKO3uW6WwOi77zzzoF1Tp06FZbpddNw8lJK+aM/+qOq7EJ0z58/H5Z96UtfqsqbNm0KdTTo/P777w91fvazn1XlmTNnhjqOXn8XPqjn1n0MQH/n1tMakN5Vndag8z7HqK7WlQ3xztTpat0t23LLWgMiuwqa7DuMsiVo3e2jBn3rxzlKiWHkpZQya9asquzuLRos7j4YoSGmn/zkJ0MdF2x68eLFquyO7fjx41XZ3aPfeeedquyOVfcRAIaBoHPcztw8xt3/1c9//vOwbOnSpVV52rRpoc63vvWtqvzSSy+FOhs2bKjKP/rRj0Kdy5cvD9zHjC6fo4w2/KcUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0Lt00HkmaNiFb2UCubROdj2ZQO6uwrgzwbYaqlpKDHpdvXp1qKPB1idPngx19u7dG5adPn26Krswbg2EdWFwd9xxR1X+8MMPB67HceseO3bswN9pQKM71277uixzrV2b0bbtAiP1HLnfaWBvKTEQ2J2jv/iLv/A7+/+4du1aWPbtb3+7Ku/ZsyfU0RD93//93w91NLTPBeY7Ws+dW12WGUdcncy1dr/TZe53mfaXCbrOhOhntt8yZpbi+20m6Fu378LoB633o37n9kllrm1riGsm6Lu1TXalJbSy9WMcrR96cOOPcvefSZMmVeXx48cPXPeFCxdCHf34h6vjxtbM+KvnxJ0PbaPZj4EAv6rWDya4Npm5/+jv3Firv3Njves3OkfI7ndm3bczxp/hcX9rtMw/uvqoi1tXZj2tx+H+1sn+TdCXzPzTjVFu/qPnxB2//k3+n//5n6HOH/7hH1ZlN9e5dOlSWKbzJjfX0mvp5tV6vJm592jEyAYAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHqXzpTK5FVkfzeoTjZTquX93NZ3ejMOHjw4cHuLFy8OdWbOnFmVH3jggVBn0aJFYdnmzZur8o4dO0IdfRfYvWeceafVLRszZkxVdu/w6vbdO736fnw2r0DfBc7kpbh38XV7mW2VEnNOXH7W1KlTq/KUKVNCnUymw1133RWWPf/881V5165doY5ub+nSpaHOo48+WpUPHz4c6pw4cSIsa71uKpOX06eR3n5Gdh/1vfY5c+aEOpqFd+rUqVAnk2mQyaJz/e+DDz644bbcerLHn8lUatFlXoRy+9i67sy9VWXOv7vW77zzTli2adOmqpy9t6jWTBvdb3f/07GVvJaR1TrXbO2TmUzT1v6XyTTM3P+1veuYWUqcj7l1u99phkqmH7n1uHFL7z8ur0VzVtzY4vLqbmeMUd3J9D+VyV3K5qe1jFGOriczjrh1j7b8KMf9PaRjkuZZlhL/ZiullOnTp1dll1f5zW9+84bbKqWUt956qyq7v/U047KU3Pwn099v1gwpxcgGAAAAAACA3vFQCgAAAAAAAL3joRQAAAAAAAB6x0MpAAAAAAAA9O4Tv0wmOLrQsJbwyS5DLLsKrR1msLEGy7nwMw0xX7VqVajjAtLff//9qrxnz55Q59VXX63KLsRYg9Zaz7U7jxoI6IJmM9c/E/7r1p3ZfiYM3h2bhtbNmjUr1Fm5cmVVdgF9GRoGWkrc70z44tWrV0MdPf8uVPTYsWNhmQbruRBFPZfuWuvvXGDr3r17q7I7jy6gUcMe3XXUtpVp667OMEO0lQuxdEGHc+fOrcoPPvhgqKMhikePHg11NPzxzJkzoc758+fDMj0nrUGfKhtGnAkazgQdd/VRj4zMBxuyMkHnek3cNcqEvzotfcuNYzqOZD6Y4Za5Oq3zD3X33XcPrHO7afnQjZO5j2T6f6ZvZ9bjZNpf5iMqbvt6v3NBu5kwXPc7d99Wek9258ONEXpP1jlrKbHfuPu/2+/b2bvvvhuWXb58eQT25Nbj+lGm/+uHjtxvumrHmQ82uflgV3PUkZa5RtnrmPnQl9IPOJQS/0Zz8wE3RmY+2KV1Wj9GM9Iy93r+UwoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPRu8MvkN5DJq9A67p3ClvV0KZMpkOGyiDTDx2Xz6PvhLq/FvUOueUXr168PdebPn1+VNWOqlFK2bt1alS9evBjquHOix5vJYnLvuWYyXVqzyPT94My1de/9utwFzT5xmTobN26syi5TQY/DnSO3fT22TF6VW3cmC8XlTGXe4VaZNtJ6rTNjS6uWcayUtiygrnJYSolt9OTJk6GO5gXOmzcv1NG8NJeN57KodLzLZIpkshhcf3DnRPuya3/DNKz7Vus9srWPZPLjMut2v9NjyeS1ubGuqzEikw3Udzu6WWm7cedN20Qmd8ndozPXVnNfXB237swcMTP+dDVHzmbz6fZacx91/M1kQ7p6mT6amSPd7jJzLbRxY1Qmi0nnyG5e7f6Oe++996pyZoxw+6N/D7Teo9zvWjMlh8UdfyZTy80bMjlf48aNq8ou41fPm8umcrm3Ldw1Go0ZUi0Y2QAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOMyGKwwoVdtv6qGUt625dbybEWwPJXEChBjsePHgw1Llw4UJYpoHoq1evDnVWrFhRlSdOnDiwjoZzlxLD0Esp5cqVK1V5zJgxoY4G0rlzq+ckG+KYuU6ZEM9M0Klbpvvp2lYmfFBD9LJheJm2pUHXmeN37TgT9OhkQlwzIbaD1vtRy1rqZGTXM8wPNGScOnWqKrvAem037kMHGhC8fPnyUEc/qlBKHEvc2Kbjn2truo+ujbjfdRWQ21XbytTJjH9dfgwkE6LqQoxVpt9mA5qVjnUuDDkTou3qtASkt46Ht5uu5mit/Uavt7u3Zq5lJjS4dW6py9w+6jzCHWvmODIh8pmPkbg6LjBYj0U/qlFK3G8XBp35iMvthOD37ugcOTP/dvdDDb92QedujqR/x7kPXWX+RtJ5e+aDDaXEvnwzBGa748jstwsfV+7c6t+67vrrOObaUeYelZnbZj4YNtJ/e7TiP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzh0N0moJDHWyIa7DCvLqcr16LJkwXrf9EydOhGUaiOaCJjVE24Whr1q1qiovW7Ys1PnJT34Slr322mtV2QVU6vFqqGAp8fizofZ6nlyIXSYgMNOOs+H7g+q46+/Om3LnLRMir+fEhehpiKgLw3YyIfJ9Bt1m1zVIlyHSfYYPuv0+f/58VXbXVtuR+xiC0uDHUkoZP358WPbII49UZReQu3PnzqrszpH+LhNYWUouxLQ1WDyjq+vdVTtq/WBJpk7mPGY+IuHOtQaLuqBR17Yy+5T5GANB5210vHHnLRNQq23CXdfWENmW9TiZgNrM2Obav67HjePuPOo46cKXdd1uHNf7iDtHkydPDst0bnPu3LlQRz+Q4+4/V69eDctuZ9mPAWGwrj5+pf3IfTDGtW39e8v10SNHjlRl19d1THBzpJs1/LqFG7Mz9+3MRyS6DIPPftjqdsHIBgAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAepfOlMpkIWV0lWnxUfVa6mR0lSnjcgcymUru/J86daoqX758OdTR94rfeeedUOf++++vyi53av78+WHZ8uXLq/Irr7wS6hw+fLgqnzlzJtTR93Pd8WvuUSkxLyZz3tz513ePM7lTbt2ZLAqXBaHXKNuvMm1Ss1fce9aZTJXWvDhd1pLx0lqnb6Mtd6iU2N5cfplmH7hMg2nTplVll1eg+XWllDJz5syqvG7dulBH36l/++23Q51MFoxzM9wjMutpyT1yWu+jmTHa9e1Mn9BxPDNGZDNVWjIk3Loz4ygiPZfuerTcR12bdTkfOrZkMvXcWKPryW5f67lsSK2TuUdn5yja/+bMmRPqTJgwoSrrWF9KKXv37q3KOq8rxc/RFi9eXJVPnz4d6uickL41GJlS3dF+6/IKtY7r63pN3N9jLhv4nnvuqcrr168fuO4DBw6EOpm/IzJ/I3WZqTosw8yYdXXGjh1blTMZd9m/4zP0vpFpf61z5pHGyAYAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0Lh10PsygM113NmhttAXUuhCzTBh3JpBszJgxA3936dKlUOfll1+uyi5o/ODBg1X5wQcfDHXWrl0blm3YsKEqa6hxKaUcOnSoKu/atSvUOXLkSFU+evRoqONCAzUQ0wUUah0XoqnnMRsQp9c2E5Dq9jETYurajbaJ999//6N39v9wYdSZ0D4XrJeh+93VBxNuZZnA2uwYqe09E77owtCvXLkycFsamOvWvXDhwlBH25YLUddxJBu03dJuWwPDu9IadO50tZ861rl+nPkYhKuj4dNunz/44IMb7s9H/a6l37h91LE9c69H7H+ZD724e6T27UyoeClx/MvcozMfjHDzCLffur2ugoZd+3PHpvf7lStXhjq67Pjx46HOsmXLqvJbb70V6jz//PNhmZ4TN0fUvv3ee++FOpm+1dXHWLIB0SOJoPPhGWZAtJtb7dmzpyq7v/XWrFlTlSdOnBjqbNu2rSq7eVRm3pD9iMJIyvwdl6VjlLu36N9ImTD87DnTsU3vR26Zu0Y3a7C5YmQDAAAAAABA73goBQAAAAAAgN7xUAoAAAAAAAC9+1iZUpn3/DPvPuu63fui7h36ljqZ9/WzmT76Dmcmi6L1vXf3nqmu66677gp19H19zWYpJb4v6zKdXBaV5ky57X/lK1+pyjt27Ah1Lly4UJX37dsX6rgsqhMnTlTlixcvhjqas+Tes86cR0fbWyYvyl1HzavIvi+s23f7rdvX9uC257aVec/c/U7fj3fHr8eRyYvJZJO4ZW6M0PGmNRvH7bdrb4O278ZRXY8bI7UdlZIbozK5U7qe/fv3D6xTSilPPvlkVXaZZpppkskr0hy6j9p+Ji9g0LaydbrK9Mrkrrnr35q7mMld0nObzZ3Qdbk+osfrrlEm0yqTReRoncwcYbRlzIxWXeX1ZDK93P1v0qRJNyyXEu9Rbj2Z+5ibI2k+UyYvKdOP3X3c/W7p0qVV+aGHHgp1tm7dWpV/9rOfhTp6/Pfcc0+oM3Xq1IH75MYtPf7Mfex2z1S6WY8/87dd5h7pZDJenczcTs93po9m79H698/hw4dDHR235s2bF+ro8b/55puhTiZ31rWtlty7LN1e5lpnrm32b4SWLKYu85vcvWSQzDnK0nMy0vlhN+fIBgAAAAAAgJsaD6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDv0kHnTkuIaiYM3MmE37qARA1NzYSxu3Bit48aLOeC7XRdmRBpF9jqZM6/7qMLejt9+vTAOj//+c/DskuXLlXlr371q6GOXrdFixaFOpMnT67K9913X6izffv2sOzkyZNV+dSpU6HO22+/XZXPnz8/cD0uDDATPu+u29ixY6tyJmjPtXUXPqhhp6796TLX/jJhzE5m3VeuXKnK7jh0Pa7/ab/N9hE9lkyIvAse1OvvApv1WF09d251e66OBoS7NnL58uWB22/9GISeN3eO9MMDpZSycePGqnz33XeHOtr/ly1bNnAf3fV34evabtx1awmazMp86CLzoY3MRwUyMmHkbh8zYfyZ+5Hr23q8bhzTOu5en/lAgZOZI7R+DON21xIQnJnruT4yf/78sOyBBx6oyrNmzQp1NMTbGT9+fFWeMGFCqLNnz56w7Lvf/e7AdWto8bFjx0IdHdtd/58zZ05YtmLFiqrsPhjz/PPPV2UXxq73H3fO3PY1tNldfx3LW8PwWwN6W9Y90h86uFmDzrsMiFat9/FM0HZLsHT2WLX9698spcQPCzz88MOhjvZ1N9fZvHlzWKYfusr8jdzlx1gy50mPpfVadzVGZD9G1cW2Wuu0hqGP+Ng2olsHAAAAAADAbYmHUgAAAAAAAOgdD6UAAAAAAADQu3SmVCYLoDUvQN+pnTJlSqij7/SXEvMp3HuemXevdZ9c7oV7hzXzzqbWce+dZupofpD7nTtWPRa3Hs09cu8Uu3OiGTKvvfZaqDNz5syq/Pjjj4c6em5dXse6devCstWrV99wf0qJ72e73J133333hr8pJeZOue25LIZMH9H3pd274Jm8ssz70plMMyfznr3bR92ey6LIjBGaIZMZa0rp7t1rzRCZNm3awN+UEtvWuXPnQp3MdcxkWmRybjJ5PZlMLddmXBbb8ePHq/KmTZtCnQcffLAqT506NdTRvBh3zXQcK6WUI0eOVOWLFy+GOhl63jK5N447b3otW9fdmrvSch93ddyx6b299Tj0nuTGscy6XRaVzi2mT58e6ui9Zvny5aHOP/7jPw7cft8ymZ7a3jLX1l2jTM6IayP6u8wc0Y2/mTmCGyP0nuTy6jQv0N3r3e9chqaaNGlSVXZjlM5bMrmDpcTxz81tdN2//du/HeosXry4Krvjd+P/jBkzBm5f83LcelrukcPUOtfoykjnvrTKzBFb72OZsc71Ub23ZP6OdH8jDfqN28fsunQetXv37lBH72NPPvlkqOPGv23btlVl17fdMqXnO3Ncpfg2oTLXSLVmKjmZTM/MPrXOrTLrUdlMry7PUxf4TykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd+mgcxfslQmk09+5oC0NaJw3b16oo0GLpcRgNw2jdHVcGKSGqLmgaReQquFvLjBM1+WOX4MdXdDlqVOnwjIN2na/0+25oO+77767KrswwMmTJ4dlGpq3a9euUEdDk13QsV7vWbNmhTouNE+XLVy4MNR54IEHqvKBAwdCHQ0RXLBgQajjAuI1/NyFeB49erQqHzt2LNTRfuS25YLmtU1mgq5dP9bz6IKuXZ/Q/XR9JNP/M0H32m41HNVtq5Xb/ty5c6vyqlWrQp0LFy6EZTomuD6q7ejMmTOhjo5t2RBJ7ctujNLz5q6/hjhmAkNLiedE+1opcWxxoZoaRuzuBxqqW0ocI904qsfmgna1rWc+WFFK7Lfu+uuyTPCkC7F09+NMn9DfuX6sx58J9SzFj1stdPzRe1YppUycODEs0367ZMmSUEdD9PXjHKXEe5IL2h6NQefallwb0bbk6mSCzjMhqq7d6lji+p+O9/fdd1+o8+ijj4Zl+oEKN7ZpW3Ljj47RV69eDXVcW9fz9s4774Q62rcyQe8uQF0/GFFK3O9MQLKbI+s4vmPHjlDHWblyZVXW4PVSYrtxfTvzMaDWjx+0BhSPNnocoy3AOKt1v/X4M3PdUuJY1nofHbTeUtrbkc4RtmzZEurouPXFL34x1Fm6dGlYpn/HufO/b9++qnzp0qVQR8cIN0ZmPuKSuf6jsT+Otv7W+vdQ60cUMv0og/+UAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHqXDjrvigso00AuF0bnQiQzwWIafqeBuaXE8E0X6j19+vSB23Ihthoa68K/tI4LmnQBoRos50I0NVjYBRRr0KYG35WSC5Z0QbMaiOeCxvfv31+VP/3pT4c67vzrMndtJ02aVJXXrVsX6qxZs6YquzBOF+x3/vz5qqyhnqXEYFX9jVu3C+x3Idp6vC6wU6+t63+6PbeeTIizCyjVNuJClDXEWK9ZKaW8+uqrVdmFgWe448iE8WqfdPs4ZcqUsEzDJzVUtpQYrOwCIrUfu8Bu9zu9/q5OJmhSz4kLg8+0EXfdNETTtVHtoy6M2vURbVvuIwoarOzGaD1e10bc8Wv/d+df+5/7YMLp06ersgYYu205mfBV96EFlbmPumVubNf7xtSpU0MdvW4aYF6K75N6L3cfSNBlrh9riL6rMxrp9c4E5Lt7fSbo37Ut7X9u/qH9z13/tWvXVuXHHnss1HHzj3fffbcq61zDbd+NrRrs7cbxr33ta2GZfnzlv/7rv0IdHbfcR0X04ytf+cpXQp177703LNP7pgtI1jnJnj17Qh0d29wcyV1/vZZuHrd3796q7Ob/ek5aQ4Vbw3dvBjdD0HnLhzdKifcod//RPpn9O1LX7ebfGTq2ZoOm3Zio9FjcHOngwYNV+aWXXgp13N8o69evr8ruQxM6/zl37lyok/kYj/sbWeeE7vzr8WY+auD+jnFtq6WfuPM/zLElEyKuy7IfQ9Lz5OYImfOf+ahKan+afgUAAAAAAAB8DDyUAgAAAAAAQO94KAUAAAAAAIDefaxMKfdepdL3Fd1vLl++XJXde/8uQ0O5vAjNGXBZAPo7976yy8vRdblMC63j1qNcplQmZ8m9G6vv8GrGQikxi2rVqlUD97GU+F7x4sWLQx3NOXGZMrpP7lq7vCzdvsur0uyRFStWhDqa++PeRXY5F5l2o+3PZZFopoR7p9qdN30X3bX/TBaDXiN3/O7Y9J1h17f03WO3Hm3vro/83d/9XVX+t3/7t1DH0fHGjT96TlzugOZuuEwN128zY4SeI3ce9b1/926+y0LRPnL27NlQR9fl2prWcefItRt9P137mttHd/w6Jrh25LJYZs+eXZXde+56/3Hv4mfeu3fL9Hdu3ZrXM2fOnFBHxwjX/tz4p+fN5U7puOHO//Lly6uyy+aaNm1aWKZ92fVtvW6ZcdRl07lMIe1v7j6aqeO2dzPQNpnJ9HT3CK3j1uPav3J5Jbq9RYsWhTq/9Vu/VZXdfVT7cSmxvel4UEophw8frspPPvlkqPOnf/qnVXn+/Pmhjus3r7/+elV+6qmnQh2d27h+9NBDD1VlzTgrpZRjx46FZTr+LFu2LNTZuXPnDcuOyxRx2Tiac+PmlpncUT2OLvNi3LpU5m+dkZY5jpGWyYvK5OW4uVYmY9atW+/JbhzTscXlV+p93N1r3fin229tj9qPdu/eHeq4cUPHW5cX+tnPfrYqZ/5GcfNId050ey53V8cR94xA8zq7zFTL5CNl2nHmd611lGtHmfW4/da+5O51mXlUxugfxQAAAAAAAHDL4aEUAAAAAAAAesdDKQAAAAAAAPSOh1IAAAAAAADoXTro3AVk6TIXfpgJ+9KgNw2+LSUX9OyCbjPBXhpi6oLuXLCeBnu5oGkNrXSB2RoINnbs2FDHLdPwMRdQN2HChKrsAsp0+y6M2AW9arCgW7eGmC9YsCDU0fOYCfErJRc+rQHNu3btCnWWLl1alV1gbibYeObMmaGOtomFCxeGOtr+XYigC3rX43UBqXqNNIyxlHhtXWBpJqDY0bBBF76XqaPtODMefdQypdtzYYwnTpyoyi6wct68eWGZtj8Xvpnp/9qOXHt0fVTbmwvo1HPkQtQ1WPL48eOhjguo1PBJd2za3lxg8aFDh6qyO373EQP90IFrD268G8QFlrvwd23bmaBpd4/Q+6gLDHb3Tb2Wrm/r+OvGP723uKBpF9Cu/dbRdut+o9ufPHnywPU4mQ8dOJkQb7eekQ4f1naamaO586jjVvY8tgTEuvFHz7+7j7m2rf1/zZo1oY62LRcGrOvOfLChlHgs7t6u9+1777031FmyZElVdvNh1/9+/dd//YbrKSXe77Zt2xbqvPzyy1U5O0fX8+TaiB7/0aNHQx29J4x0vxqNboZzkpmPZcZj14+0r7mxx/2Npn/buT6iv3MfjNG/UXTuU4r/GI2uy41/Ot65+UdmjHbb1w89uLlF5u8Inf+68dgdm44bbmzVa+Lm2vr3kBuP3H7rvC1zH3P3Ub0m7u9x9xxBl7k5stZx7Vh/5+ZR7t6uf7e73+m63T7qetw+Zoz+UQwAAAAAAAC3HB5KAQAAAAAAoHc8lAIAAAAAAEDvPlamlL576d7F1Pcs3Xu++u6pexc1k8Xh3hfV96zduvXdX/e+qMtiOXbsWFV2uROZvAbN8Mhkarh1u2PT9zpdXsL8+fOr8rp160Id9360nif3Trsuc9dI3w9376u6DBPNgnrooYdCHc200rLj8kpczo2eW5expO3GHb9eW3f9XRaRtj/3Dq+eN5fN5d5zVq7fan93OUtax7VRPbfuWms7cvvTmiml++Te19f33F2mgXuHXfOBXF6Pvovt3rvX3CmX3+bajbblzHvurv+tXr26KmvbKyXmTpUSs6fc2Krn37V1HVvdOTp58mRYpufJZaPpPrlxXPfJ9ZlMpqFrN9on3Tiq++2ukaPbd/0vk/ujv3NjzfLly8MybX/uPqLnzV1/PY7WTCN3rHq93XoybWQ0Zkq15GVl5l/uOrpxU6+ty1TUe7LLZnvqqacGrsdtX/f7K1/5SqijbSKTe+LmEa7/nz59uiq7LJTHHnusKrtsPj0Od101P6uUUpYtW1aVXV6ijj9PPPFEqPPII49U5RdeeCHUee6558IyvZe7+5/m1bi59pYtW6qy61eZsc0df6b/6/l3/T8z1ximkR5rMjKZoplz6+aamkWmfa+UmJ9YSsw0c3Mknce5e5Teo91cJ3OPfPPNN0MdzcJ1Y5T+befaoxsjdS7n5t96/C73Upe58+jofrp59MGDB6uyG0d3795dlTWHtJTc3yiu/WXyc3VOmM2UymRD6+/cPFbnhO44MnnBbm6p+5TJxnV/o2SM/lEMAAAAAAAAtxweSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85daJvKBL269WgYoQtjy/zO0RAztx4N32sNDHTr1rAxF3StYcCnTp0Kddw+ZQKyNURPA5tLiaGd586dC3XmzJkTlunxuqBhDVFz4XsabOqC1jRErZQYyLdkyZJQR/fbBbRpO3Ihrm6fMmHDek1cG9HwTdeP3O9mzZo1cH/0fLt16/G6UHcXPquheZkxwoXvadt2QaP6u0w/dsvc7zL7rcfq2rELUXz77ber8uHDhweu27VRDYh1bX3x4sVhmYZGaqhnKbHduDDETIihCx/WQE7Xt3QfXRi49hEX9Oj2OxOirOfEfehAx1p3HK7dakCl9tlSYtt2/U+PP/NxglJif3cB5XpOXBiwjmPugx1ujNa+5X6n59ZtPxM06mTmCKr1/p8JFe+bG2+VHq+bf+nYlmkjpcQQa7fuRYsWVeUFCxaEOgcOHKjKrj24fvOjH/2oKruxXj+Q4sZx7SPuWrtzomG7Lnz3M5/5TFV2x6Zjmxv/Nm/eHJbpfC9zb88EFLuxfu/evWGZzgnd3PKBBx6oyu48atCxW4+7J2v7d327JaB8pEPNnZsh6DzzMYrMOJr50I2717u/rXT+4drxzJkzq7J+ZKmUONa5eZybW+j4px+VKSWGeLv5h34gwY0jJ06cCMv0XGpgfClx3HYfVdCxxo0R7kNbuv3M37HuYwh6/l2ovDtv7u9WpfMmF5ivY427/o7Ov93fcdq3XZ3MB4vc2K776fY7E3Suy1rnbKN/FAMAAAAAAMAth4dSAAAAAAAA6B0PpQAAAAAAANC7dKaUy4LQ90zdO7xdvdPtshH0d24fM3kJut/Z98UzWTiZ96N1v91xuH3Sdzbd7zR3Sd9fLiVmOMyePTvUce85K81vKSXmA7h91JwHlynjshBOnz5dlS9duhTqaPaAy92ZOnVqVc5kGrl6mfwStx5d5q61ez9Y3+t2fUvfodb350uJ59b148z7yS7TJ9O29bxlsqHctlpzHnT7mbwCty23T9pGXNvSMUlzmEqJeQHa9kspZceOHWGZZh+4vq3tSMeMUuL1z2SjlRLfxXfvouv2XRvVDIFMNlEpub6leSVnz54NdXSMcNlc7rxpm3Bjm15vza8oJfZJ19bcNdHsGZcNqH3L5Q5qFqK7j7p+q/co10ZUa//L/K4lYyprNGZKZc5by7l1+WHLli0Ly+bNm1eV3X1k1apVN/xNKbFNutyT1157beD2XaaM9ncdax13HK7faM7T/v37Q51t27ZVZTe26Di6c+fOUMf1SR1v3fxHj9/dfzTnZf78+aGO2289Nrffuk+uHT366KNV+Zlnngl13PnP5JzomJTJZsqOP326GTKlMmN0RmaOmBnrSsnN0TSLKTNHcOOYu/+/8847Vdndo3Xe5vqo/v3l/mZbt25dWKbzf3eP1nPrMp00i8/lV7lxY82aNQO3r8fr7j86Juv1KMXPrXTe6DLF9L5x7733hjo6trjcVTdv0ywol6ml8103jo30+NOV0T+KAQAAAAAA4JbDQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dNC5C4jLBMu1hG+5oLlMaJ0Lem0JH3VhZJmAdg2VLSWGmLkwXg0tc2HALjRNQ+OWLFkS6syaNasqr127NtTREDUXxucCMjWQzYX/aWioCwidNGlSVdZzVkouDDFTx11HPX7Xjlq5YPPMPmVou3F9TetoeyglBoS6oPNMsLE7b25dKnOO9DgygZVun9w+trQttx53HfWaZILuM+fRBf9r0GQpMWxy4sSJoc7ChQursgva1GDJ6dOnD6xTSjwW1x704wtu/NV9dGOtGyMz9x9tW24fNejbBc278FE9J67/aRh95j7mxkgXEJoJFtfz7e5Ruk+u/bkPXejYqh9ecHXc9ddr5II++5QN0R1pOra4cVzPt5t/aBivW4/7+ICGX3/xi18MdXRuoaHepcSx1YWRP/HEE2GZtlv3MRRt7zofKSXeI916tm7dGpZp0LeeR1fny1/+cqjzxhtvVOXjx4+HOn/+538elulY7s6tHtv3vve9UEfHPzfWu/DfQ4cOVWUXIqzH//jjj4c6+jEMN466dev1d21Ux1Z3j9b7iGv/mXnMMN0MQcduH3VZ5m82Vydz/JkxOnPPdHMEvbe5v4cOHz4clukHUtw+ami6zodKiX3CzZHcHEXHBPc7/btN+2Mp8fjdfMCdk40bN1bl+++/P9TROaK7RjqOu7mG+9CXHpubR+sYmQlRd2Nt5m/L1rlNZt2Z/peR6Uet2+I/pQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOXbCYBmS6oF/9XSbEzq0nEyLsZAJSNTTWBV26YEUNO9MwulJi+K4LqNWgNReQ5gIaNdhNg8dLiYGgbvt6Ht05ciFqGnbrrq0GYro6Z86cqcouaNcFa2q9TNCkC7/TYFfXrrJtUuk+ZQLi3LnO7FMm/NG1o8mTJw/cJ0e35/ZbA0Hd8WtopIaqumWtgeWtofLKhTG7gMTMNVKZwPrsRyW0nmv/O3bsqMpHjhwJdTQw1401btmMGTOqsuvHGr7prr9u3wW2ZwKa3TnS/c58sMKNNe4eqe3f7aPukxv/dD1uH13b1nOZ+YiIG/913HAfrHDb1+25Onou3Ril/W00hvq2fFRl2DSg1fVR3W8X4qv91gU9uzb5pS99qSo/+OCDoY7Od9zYpuO96/8LFiwIy86dO1eVX3/99VBHx0Sdj5QSxxEX4vvcc8+FZTr/Wr9+faijfdT1v3Xr1lXlr33ta6GOCz/W8ca1UZ3vumPTD2asWbMm1HHXX7mgYw1xd210xYoVVdkFFh89ejQs0zE584GCTEDvaBx/uprbDFPr/KflYzTZvxlb9sn1UR0T3cdA3LILFy5UZXf/P3nyZFV2H1pZvnx5VXbzKDduzp49uyq786H3X3f8+sEK14/dHEmPzc2j9f7j6ujf2pmPM5US73fugzl6jtzfHzr+ZbZVSrxOmbbu/v5Qbh7VOrbpsQxzrBn9oxgAAAAAAABuOTyUAgAAAAAAQO94KAUAAAAAAIDepTOl3HuG+s6mq6PZEy5TQzMlXF6Fe89TM50yeSHTpk0buG6Xu+SyUPRdfPcu7vTp02/4m1LiO8TufV33frDmRbj3VfW9Uvcu7pUrV6qye1/Zveeqx6L5CaXEfBrNeCglvp+beae2lHje5s+fH+roe87uOur2s++iZ/dzkJZMg6xMpkumH2fyuly7dcuUjgluH9278CpzPVwf1WWtuRPuHGk9t2737nuLzPbdu/BK81NKibkHLj9v5syZYZmOJXPnzh24PZdNov3WnUe9H5QSj9/lHOj9JpON6N7pd2N7JgtM153JInC5L+7YMrmPeo5ce9T1uPuoyzDI5Hy0yGaqDUtrNknfNK8p07Zdpon2kd/93d8Ndf7gD/4gLNN7spv/qUymojuOS5cuhWW7d++uyi+//HKos3bt2qrsxj/NnXLzKLffOk6uXr061Mkcm2YqPfDAA6GOa396T3D3CM3ZcpmCOm9zcz23TPfJZYrpPPbVV18NdTTTxeWuujG55d7aev8faTdDplRmHp05jswYkbmOTiYb0f2tpVx7dNvXccuNLdr+Xbs+cOBAVXb5Vfr3aClxbufo38R79+4duO6VK1eGOkuWLAnLdN7ijk3HZPf3QCab0J1bXeYydnVdbv517NixquyOQ//WLiW2E3eP1DqZjOXseJDJlGoZ71rnaKN/FAMAAAAAAMAth4dSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd+mgcxeQqGFvLqBLg81cQG4m6GvGjBlhmYbIuvAv/Z0LSNOgs4sXL4Y6LqBL99MFxmlApgs/0+25wEi3bheapjREzgXGa2jgmTNnQp09e/aEZRpsmwmadOFrmfBB1yY0/P2tt94KdTR8zoXvaZtw4dyu/et5c0Gj2m7cevScZEPVMyHmGXrdsgHumWBfvf7u+LWOC0zWc5IJLHcyQc+Z43fXKBO+mQmMd9vPBJS73+kY7UKMNejYbUt/54IeHQ0fd2OLjr/uHJ06daoqu/Ews0/ugxHHjx+vyi5o3bVJ5cYoDT93Yex633Ljup7/bKivtgl3bnWZO0c6Jrp25LR8DCITUDsaQ31HY9B5ZvzVgNjPfvazoc7Xv/71qrxhw4ZQJxM+fujQoVBHQ2wzwbtuWxqYXUoM+818MMOFaOtxuPX83u/9Xlh28uTJqjxlypRQJ9O39Hh1XlmK/0DQa6+9VpU1DLmUUv7jP/6jKu/cuTPU0THR3f90rlVKvLYuIF8/kPHUU0+FOrrMfVSjNehdx9LM3CIbYt2n0TgmqtZzlPkYTuZau2ur/S9zj3TXX7n7caaNug+W6Hjj2r/+zo1R7m+bzLxJ+7Gb6+zbt68q79q1K9Rx458+E1i3bl2oo3MSN0fVc6vz2lJ8QL22JTf/0+27OZKeI7d9d/01WN1tX6+ba3+67i4/mNWidVujfxQDAAAAAADALYeHUgAAAAAAAOgdD6UAAAAAAADQu3SmVCYvwb2vqu+5u/dl9f3ITO5OKTHnw71n7zIMlL776d4FzWzf2b17d1V2792fOHGiKrvjcO+w6jXJ7M+sWbPCMs05cddx2rRpA9ftzpFe20xek3sX2x2bHot7X1mXuePXLAr3Tm/mHXL3vnzLe7XZvIjM+/n6DrV7p1ozNLL7rNe7tY/o9tz2dT1uW26M0nfPM+9ZZzKdMtty68pcs0zumqvj+pZe29ZMDd2+y+9zGQa6bpeXov0tk1fkrqPLRtB1nTt3LtTRvuUynbTfuP7ochbGjRtXld04kmlbeh3dtXZ03ZncKXduM9vLtO1h5r71mZeQyfgbDbS9aXssJd4T77333lBn4cKFVdm1I5ch8uabb1Zlzd0oJY5Jbh91vHHXWrPpSonH7/KiNm3aVJXdPVKzUd31f+ONN8IyHUsyuT9u+w888EBVdufItT/NnnHXSOcbLi9MM/3cWKu5W6XEvFTN7yullEWLFlVlbWulxCwsd63dHFnHZDf+6jJ3/pW71490ptNIbz8jM0d0f2voPN7NEbRPuL8HXKau9lHXt3Uf3VxH5wSZe20p8Vjc3ELbu/s7Rn/n7tku01n30/UtvW6ur+nY6uZR7pwsWLCgKmfmEZlsVsfN0fQ8ufOm9x/XtrSNuHPk8qIzfyPoeJPJmM387VVKbt7U1dwmM0aN/lEMAAAAAAAAtxweSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85dsJYGZLmAtMOHD1fly5cvhzqZYFsXUKYBhS5YTEOs3T5q+Fom1LqUGEjmjk1D0134m4aIuRA3F76ov3Phl0uXLr3h/pQSw+c1VLOUUtavXx+WZUJ0dd0uRE6XuWvtAko1INmt+8KFC1X55MmToY6eb3f9XfhgJmxP+0gm6M0F3bm2rW3ZhThq2F0mxDrTjl09N0a4dSndJ3etNSDVtTV3bJmgWz1vrv1lrrWONaW0BRK6fqztz9Vxx6brzpy3TB91MuHrbvzVMcJ9aGDu3LlVef78+aGO6zeTJk0aWEf30d2P9Py7Oi5YUs+Ja0e6Lrce3Ud3HTNt1J1/7duujvYJNx65NpIZ7zJ9JBN+2lX4eWZbzs0QdD5jxoxQ5/HHH6/Kn//850MdDaPevHlzqPO9730vLNN5y+rVq0OdyZMnV2XXt7Rtuzbj+o1u331oZvv27VXZjaM6/3HbP3LkSFim4edbtmwJdebMmVOVv/rVr4Y6Gqzr5iiurWuIsJsj6BzRrUf7xNmzZ0Mdd0/S6/bKK6+EOnv37q3KOmcrJTfX0LG+lBjingnaduOf3pPcvCbzEZVhuhmCzt0YqW3E3f/dB7KUXkfXHmbPnh2W6d9k7t6q7cjd63SM1H5Vir9GGn7tPgag7c0dm46b7j7mlukHCtw8Qo/NjSO6fTeOu76tH03IfDDGrbv1g2l6j3AfbNA24cY6PbduH6dPnx6WqcwHk9z4r/M419cyY9SIj2O9bQkAAAAAAAD4P3goBQAAAAAAgN7xUAoAAAAAAAC9S2dKOfqeoXuH8cyZMzcsu/U47l1Y3V5mPV3mPuh7nZncrcw+uvW495z1HWr3vrS+i/3lL3851Pn0pz9dlV3ug3unW48/c201P6KU+A65O1Z3To4ePVqV9d1kt26XFzFx4sSwTOk75aXEc+Lalv7OveesOVenTp0Kddy70HpOXO6YLnN5Ce+8884Ny6X4vCTNlXGZHnq8mf7nztGBAweqsnun2h2/Xtt169aFOvoOu8vP0j7iMiVcH9H2596X12viMhWUO9Zly5aFZXpN3HnTTAV3jXQfs31Uz79rWzomuCwGzVRx2Xyuj2o9148y47hy63HtRs+Ty6LQ8+1yFzTDwGUauPFP99Pdf/R43XnM5CVkuLaVyULJ5C46emxu+zqOubwsXZbNtBtp2t5mzpwZ6ixcuPCG5VLi8bt7lDtvmk/psuC037h+pO3W9SM3Jmo70Wy6UuJ1c2OL3v9cm3U5K7rf7rzpsbhsKs1wmjZtWqjjxq1MXuC5c+eqsuvbuh53H7t48WJYptfStRE9R5m8vEw2jNsnN9fTOaEb//Q43FynNYvudqdzC9f+NXcpkzHr2uPu3bvDMu23mdwf10c0i2jr1q2hjrtHaNty93at4+ZR2kdc7pSb/+vc1s0RdIxwmXLaJ9y9JpOX6fqxzhFd/9f9duNh5m/0TKZhJr8yOx5k7m26LneNWudkow3/KQUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD07mMFnffJhYZp2JkLkdNAsEyoqgtRddt3gYxKw8dc0JxyQccutG7OnDlVedGiRaHOk08+WZXvu+++UEfD5zTUrhR/bjV82AX06fnfvn17qKOhfS4M1IWoa7Cgu7YabOnC92bMmFGV582bF+q48FcNH3Qh0hrIuHHjxlBny5YtVXnXrl2hjmsTuj0Xvq2BhC4gUYOVsyHSLrS0hfY3FxirofYuDNHR8+8+BqBtxPXRsWPHVmV3PrSO254Lejx27FhVzoTouu27ZTr+uLFN25Eb13SZa2uu/Wv4uwto1HHEhZjq+OuukdtvHTenTp0a6mRCszMhlpnz5tqtXjcXIqzn24VaunOr63ZtS5e5oGkd/zOBxaX49q50ey7oU9d9+vTpUMe1fw27dWHw+jt3HfV6u/vhaAw617bk5gh6vO6a6T3BtRG9j5YS+5/7YIb2CXf/yYTGujraT9wYpeHnbv5z/PjxquzGKBeQrHMJN0fSD8u4vq33P3cd3dii93vXR7X/uzmahiEvWbIk1HEfaNBgYfcRi8cff7wqu/nf008/XZVXrFgxcB9LKWXz5s1V2fVt7SOu/ev9JvPBilIIP1eZD224uYUuc+O/rtv1dbdMxyjXjvR6u7meziPcWJf5YJe7/2t/dx9s0Dnizp07Qx03/9HtuT6SmW/rmOg+KuA+BqQfv3Af7NHz7fZH7+3ub0a3T/q3lWujeo4yHyxzdTK/czLzj5ZnHa3rzshuP/yu6VcAAAAAAADAx8BDKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0LuhB51riFYmMCsbDpgJ0tLtZQJK3T5mwnBd+KEGJLoQRQ3IdGHcLnx73bp1NyyXEoNeXfibrtsFjU6ePDks06BxF6y3Z8+equzC/3SZCyN0NHzOhYHr+XZBoxp06wJzXZvQsF8NbCwlBjS6EE8NbXSBoRq06n7nrq2uyx2bBhu6/pe5Jq4/ZsL3tP+5EF091y4M1v1Ol7n2p1yItm7fhUG68EsNmnUhlnq+XTvSvuaO1Y0tui73u8zHF3Tdrj+4c6LtzYWYZvZRx0gXhu/OW2b8031yx5a5j7k2qeOmC4PWOq6v6f3HhbFqGyklHr/bvvZJF/Ss599dI3dOdLx1AaU6jrkwcr0n6XGV4tuf9ls3/umxuHak++2OYzSGGut4d+DAgVBHw6cPHToU6miwtAu6dh/o0DmBC7qdNm1aVXbjqF6jzJhVSrz+bh6n8zZ3bfWcuA9muPBh7duzZs0KdXS+59qx9i1XJxPsrKHCpZTy4IMPVmXXt/RjFBrOXor/iIp+xOOhhx4KdZYvX16VT548Gepo0LO71504cSIs07HMfaBB1+Xm8ZmPumTuG7e7zPnIzCMzf8e5fuz6jc6t3VxbuXu97rc71swczX3USo9FP2BUSry3u48xuIB4nUu485/pIzpuu7HWHb/O5dw4ptfN3SN03e5e74LOtd24eZSu280RdK6T+aiEW5b5YErmeYRbT6ZvZdY9TPynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6N3HypTK5ENl6rTSnAG3LV2WyYty72K6d0j1/Uy3bs0LeOyxx0KdxYsXV2WXTaDv3Tsui0PfK3Z5AfoO744dO0KdjRs3hmW6LpcXo9fIZeoM2p+PkskiyryLrtffvXfs8qqUy4J68cUXq7LLS9i/f39VfuONN1LrPn/+fFV2x6/vfrt3uvWauHfh3XnTdbl3kXWZu7aa1+Dee9Z3+N3+uOPXTIndu3eHOnptXT/OvHfv8or0/fTMu/hu3Xpsro5ms5Ti36tXmjvjjl/3WzNGSvHXVs+Jq6PH78YRHWsy2VxumWv/eryur+uY4HIf3Lihx+bOreZFuHuNnkd3/O7c6vG7vAxtIy73TtuIa39u+3q+3TimORcuv0WPw23LZYjo9lwWh9Zx91Gt43InMu1vmPMhR8+lu7dopqQ7t5qN5DKl1q5dG5Zt3ry5Kh89ejTU0Zwrl5em45/rxzqPKiXmfLj2p9fb9T89/mz71/vUvn37Qh3NVHH5obo9dxxu3qjH77Kw9Hy79Sg3/r3++uthWSYvUcc2l02m1//RRx8NdVzOoLaTrVu3hjqaV+Ouo55/N9Zk8lowWCZ3qsv8rpbfZTLtMtlIpeTmdgsWLKjKCxcuDHV0TrBy5cpQx2UxZbJ59Ry5bF5d5vIDXV6yzi0zeZFurqt/a7hjdeOPjv+ZLDJ3/d19S7m/UXS+6zKddY7m2pFeo8zfUaXE8+TurZlMWd1e9u/4sJ6mXwEAAAAAAAAfAw+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA79JJVKMxsM+FBisXLKY0xMuFoTkaIu7CF1evXl2V16xZE+rMmDHjhvtTig/j04A0F+KqoZEu6FbXraG2H7VMw9ZciJqG37njmDp1alV2bc0F67kgN6Uhfu7aaoicO//vvPNOWKZhn25/9HgPHToU6rz88stV2V0jd040SM6FH+s+ufOf+RiAO7bMBwLcskHbzwQmuzDATB/RUN0sbduuHbllLpBQ6Tly578laNDVy4QPuvVoYLKOWaX48HNtkxoYXEouIFL7fyawsZQY2unCN3Ucz4SBurbmAjJdaKfSa+TWrW3LXUd3HjXE040/GjTuwvF1/HPH6gLKtS25/qD3Fhciqv1YP2BQig8oP3bsWFU+ceLEwN+5fqx9NNOv3e/cxyD65O4RGn7uzr/2t89//vOhzpNPPhmWaSC6C4jXsVznA6XEMGy9rqX4cUvPvwsI13W5oHEN6HXt37WtF154oSq7jwjs3bu3KruA4jlz5lRl9zEINx/WMdndNzT8XMfDUmJAvbuObvzX7f2v//W/Qh29Jxw5ciTUmT9/flV2oeYu/Fh/d/DgwVBHx1J3b9HznQ06R83dt3S8zXwwolXLfNTJzKPdWO/+jtG/I9wYvXTp0qq8bt26getxgeWuj+qYqKHqpZQyffr0quzmGq5PKnf+dUx093E9FjdG6TVx9zE3/9G5jftgjT5HcH8P65zEzf3c3+j6Ozf/0vPm+pGO/24e69qfzondtdU6maB7t3133RSjKAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F06U8ppffe2Zb1uPfqep3sXXN+9dFkQuh73vuTixYvDsvXr11fl5cuXhzqaveKyMPQdTvdu+iuvvBKWaRbBvn37Qh3NC3GZCvoOr3un2b2fq+c2856rO4+6PXf93bXV8+QyFfS9bpd7ou9Lu+Nw7/nqfu/YsSPU2bZtW1X+/ve/H+ocPny4Krt3cV2byORFZfLR9By5d+Ez7/ln+rqro8fhtp95X9+1EX0X27VjPUfuXOv1d1l1mYw79059Jp9G9zszHpYSz5PLwtB1uePQdbttuX6jbTmTTeayCWbNmnXD8kf9TrfvxjbNC8nkdbk+6tqkti03/ug1cbkzmqmQza/S8d/ltWheTybTwG3f5czoutw+6vbdPUpzH/Q3HyWTIaLt1rV/bROZbEK3/ZHOlDp//nxYpm3SZfM8++yzVXnz5s2hzpe+9KWw7OGHH67KLq9Rz60bI3TekJ0j6Dj1wx/+MNTRedOyZctCHZ0juWwWl8Wh2SM/+clPQh3N9HL9SOek2dxTHdtd39Jr4sZ23Sc3RmVyL1370z7ickc008tlA2bm/66Pan93WTzab929zvXtzPhzO2nNdMrkdbXmTum6Xd/SOm4c02w0zXgqxecVaYaT5vC5fXLr1nbsco8y5yjzd5zLvdV9zOau6TJXR7efyfR02VTubzTNmXPXSMcNN4/TbET3t4Ybf3S8zeSVZfJjHTdG6pzYjX86JrtnJDr/dvPx3/md3xm4j/ynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dNB5JkQrUyfzu0zQVykxkM2FjykXULlo0aKq7IIuXUC3rsuFuGr4oQs6e+mll6ryyZMnQ50tW7aEZRrk5kIkNfwtExjoZELEXUCmht9pqFwpMeh53rx5oY4LFtSwYRc0qet2x7Fw4cKq7IIO3e9OnDhRld01+vnPf16V3fFr+FwmsNstc/0mE2Ku3HXMtJtMiGEmxDITGJoN+suMSZnt67l116g1DN6tS+nY5o7fBRRq2KKro+Po1KlTQx0NOnRhyBr0WUr8iIALsZ09e3ZVXrFiRaijwZ4aKlmKDwjWQFwXYqnr1uDzUmKwr9u+u/9ou3XjiC5z11ZDS929xu2TBoIfO3Ys1NH7iAso1XuSCzp2Y5t+2KP1QwN6Hl0Ya2tAqPb31jEqE3Q+0tzYriHy7mMsem3dtd65c2dY9oUvfKEqf+5znwt11qxZU5UzYayOO9fatzZs2BDqaLvRcPZS4sdI3BjhwofnzJlTld34+y//8i9V2R2rHseBAwdCHXf8jz76aFXWD6+UUsqqVauqshsjdGx1Y4SbN+k45fqWjgn33XdfqPPII49UZXf/cWOizv/cfis3tugyd65H+iMGN4PWoHPto63jauYauT6qcxT3oZWlS5fesFyKn3/o9tz4q/dfF+KtbdvNtTIfyHnmmWdCncwHo/Q43N9sbllm/qPn2wVt6/HrR15KyZ1b/buulDj/ch8n0vHH3Wvd+KPrcuOotvdM+898HKyUeE3c/ScTdK4f+nAfoyDoHAAAAAAAAKMSD6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDv0kHnTmuweVc0tE2Dr0spZcGCBVV55cqVoc78+fOrsgvaduFfGiyugaGlxPBPV+fo0aNV2YXIuoBYXdYa9KxBZy7ELRMs68LXdN0uoE+P1wWGumubCWjTQFIX/qfhgy6gzoUfvvDCC1X5+eefH1jHBf1lQgTd+dfQOhdiqKF5rj3o9jPbcjJ1XNvS7WdC9d05cuvOBB2rzHl058iFGGqbdOvW32UC011gpzs2Hcvmzp0b6mjfcmGcuszVcfuk4ecahuh+566tjnXu/LsxwvV3tWnTpoHb1/Pvxhq3fT02N0bq9lavXh3q6Lhx6NChUMeFH2tosQsxfvvtt6vym2++Gerofcv1NXdsLXOEzAcTslr6vwsazYyjzmgLOndh+Dre6nyklFxgvwvR1vB3Dd4vJd6jly9fHuroBxPcdXTh4xpsq4HlpcSxxQXk6nG4EFc3t5s5c2ZV/uxnPxvq6MdQzp49G+osWbKkKm/evDnUcQG9n/nMZ6py5kMLbq6p44/rI+68adhuZm7hxmy9R+7duzfUcfP2TP/PfERCx3t3j3B9y833bmet86/MOKpta/z48aGOa1v694aONaXEj2G5dqx/I7i/Y1xAtLYb1450/NmzZ0+oo3MNN45kPmLkQtz1nLj5j/Z1N9a48G09NldHP0bl5h8XLlyoyu4cHTlyJCw7fvx4VXYfGtNluq1S4piQ+ThVKbm/o7VO5oMt7lo7Let2c31tE66tf+Mb3xi4P/ynFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F06U8q956vvFWZyJtw71rpul83i3mHUfBJ9776UUhYvXlyVZ8yYEepoPorLBnDvomoWkr6bWkp8P11zqEqJ7/C7c+0yLPS95q5yN9y5dvuk3LXNZPHoOdJ3/EvxGTa63+5dbM15efzxxwduX98fL8VnOPz3f/93VX7xxRcH7qN7X1rf/c3kd7l1u+vfUse1Nbf9THvTY3Hr0XfP3bvQmWyezLFlcqcymVKurbtlmUyNzPWfMmVKVXbZUO4dbs0n0vWUEvNRXBaAjsluHNNsgFJiX9aMlVJizpHmwJRSyrRp06qyGw8czVlybUvfhXftSPMpsmOtjkmu3bqcw0Hrcdl8LgtFs6c0v6aUUnbs2FGV3fifyVTqKmPS9VGVPf/alzPzmEymQuv2R5pmvJUSszBcFolrE4PWU0qcI+3bty/U+du//duqfN9994U6mjPlcjddzpDmY7ksEO1/c+bMCXV0HHeZKi53UsdEt+7Zs2dX5e3bt4c6OrYfPHgw1HFZXOqRRx4Jy15//fWqfOnSpVBHx5GNGzeGOprNV0rsy+68aZ3nnnsu1NFzollZpfi5vY7/mf7v7m06j3T9n/yowTLztsyY6e7/U6dOrcpurHM5U/fee29VdvcxHdsyuWsuv85l6u3fv78qu/6nY7L7O0LnbZn7eClx3HLnVq+b276eN3et3d92mUxNzXRy11bHepcppRm/pcT5j5tbZXKfbnfu2rbgP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzjMhpi5YLRMirEGHLmhXQ81LKWXDhg1V2YX4akChC4M9ceJEVXaB5Rr0WEopp0+frsouRM4tUxr054KOW2VCrIdJt+eurYaBuqDjpUuXDly3C9H8jd/4jarswkg1aM+FiD711FNh2csvv1yVXUCmBuK5a6vX312jYYaYt9Rxy1z/12WZwPRM0LA7jy5od9B63O9cG501a1ZVdqHiGsbt1uXOo65Lg8dLiQGdLozS7beey0mTJoU699xzT1V2QdOZMToz1rnwSQ0NdWO0fnzCnSMXvqnB6i6MVs+ROzYNGnfX3wVk6jlxH1FQGnxaShy3NBy+FD9uvfbaa1V5586doY72pUzQ+GgL8P44Wo4l+5vRFojqAnozYfB6HG4cc8eqffmtt94KdbS/6XyslPgRETfWz58/Pyxz80b1uc99rirrx3FKiWOLu4+4MUE/EKHz0VLiHEX7bCml/Mmf/ElVfuCBB0IdF2K+devWquzmyK+88kpV1uDzUmKwvPvwj7v+OidzH5rQ+Z4Lo9bw+2XLloU6u3btCsu2bdtWlV271XuCG/+0jutH7v6X+UDA7aT1fGj/c/MfbduuP7r7v95L3Ye2dJm71tpu3IeX3NxGA6Ld73Ru4fpa5p7kzr/OpVwf0Tpu/qXH7+bo7iNiDz/8cFV2fUtpOHkpcRx75plnQh33EQkXLI+Rw39KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3nWaKeVk3iHWd2Fdpox7F1h/595X1fdFz5w5E+ro+7r6jm8puSwGd6yZvKBM7k7mHeJsFpHqKmcq806zq5PJtHB5LStXrqzKv/mbvxnqaKbE0aNHQx3N1NGsqFJK+clPfhKWaa6Cy7TSc+ves870kcy1bc2UyrRRR/c7m0U1aPvuOHRZJr+olNhv3Lq1vbm8gvXr11dlbTMftW7NS3HjmG7PZSNl3tfP5GW5vATNMHCZBpcvX67Kbox0eQmZdSv3jr/+bsaMGaGOy4/RvDp33rQtufFH8yncvcZZs2ZNVXZZDJoh5fqM3rfefvvtUMdlwWiGT2aMducokzN1K8vca53RlinlZLIBB/3mo36nfcv1bR1L9+7dG+pozpC717vcSbVq1aqwbO3atVXZtX+d/7nco8mTJ4dlek7c2P71r3+9KmsOVCmlfPe7363Kmmdais9Z0fuPHmsppRw+fLgqP/vss6GOHr8bf7/whS+EZXq8mvFUSikLFy6syu48aj9y87if/vSnYZne/1ymqI7lmh9ZSswrc/lV7t6KWnbepvTcuvmH9rVsfpXOW9y9LvM3qu6jzplK8cev442rk/k7btB6S/H3Iz2XbvzVDDc3/9U50oULF0KdJ598MizTeZubR+q5ffrpp0MdzeJ74403Qp1MXpVrN5l5081wr78Z8J9SAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO8+VtB5S7CXCxHTZS7ozQVkbtmypSq7MDINOtTg21JisJ1bj9tvDS3OhJE7+rvMb9w+ZQLaugo1z65b6+j1KCWeRxfq50KMlYZ6lhLD/lzQ8LFjx6ryj3/841DHhf/p9tz2MyHuev3ddWwNEe9KS9DiMLlQURcifv369aqcCWx2IY7Tp0+vyi4w1Y0tmTEy00YyIZqub+nvzp07N/B3LmhSj81ty/1OQzRd0Ln7iIGaOXNmVV6xYkWoM3v27LBMgy1diK0GdLq2pfcf91ED/fCCW/e+ffsG7qNrM9u3b6/K7mMMmzZtCsv0fLuxRftEn+NKlu5T9h6Zkel/g37zUQg/HUzHDddGNQx7yZIloY6O0aXE8+9CtPWjASdPngx19D7i7jXunjB37tyq7ALC9Xf6cYRSSvnZz35WlV0Y+pQpU8Kyhx9+uCq7sVbHRHeO9EMLbvw/depUWKZjstvHQ4cOVWU3/uq8TX/zUTIfmlmwYEFVnjZtWqij46g7j2PHjg3L6P+DZcZ2XebmMRqs7dbj7m0j/RGPlu23Bm9nlrmPuOgYuXz58lBHP2rgPkbh+oiO/+5vvb/+67+uym+++Waoo3/HZULNS4ljQuZ6jLa/h24lnFkAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANC7dKaUexfVvZ896HfunV59P9O90+kyVHSZe89Tt+e2r8fhMq3cssw7q7o9t4+aoZA5Dif7DvVIclkMmk/grr/L+Vm2bFlVdrlj+u6/vvdcSikvvPBCVX7ttdcG7mMpMVcmk3Pk2pFmUWXzSrrKVWl9P125dqu/c+vR9p/JZnPbcuORXrdMHzl//nyoc+DAgaqsWUGl+HarbcTlpeg7/G5c0UyLTH5bKTHTKTOOue3rMpef5fqfy5BSet3c9T979mxVdpkSmp9SShw33HnTvJhJkyaFOpr7omNPKf7aHj16tCq7dqP79OKLL4Y6mhelGTOl+POv/cRdf60z0jkoo+2eVUp7NuNIn8ubQSZTUfNi9u/fH+q4vCLNJ3Fju45tLhtJ+42bj7r7j46Tbr+1L+t8pJTY3jQHqZRS3njjjbBMs1fmz58f6qxdu7Yqu7w8vSZuPuTmTXpt3di6aNGiquzO7ebNm2+43lJ8ppeO/y53UMdkvde4ZW4e6zJF3XlCLfM3WmYe2dVYm8kLymwr8/dxKbkMo8z9pvW+qfs5fvz4UEczpNzfOqtWrRq4P65v6XjzzW9+M9R56623qvLBgwdDHbdPKpOpiZHFf0oBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAvUsHnbsQXQ2EywTUZYJWXRhZZvuZ8Du3j7rMBdR1GT4+aD3ZwDpdd+YcteoqVNsFjWsY6NSpU0MdF5iswXbTp08PdTRE75/+6Z9CnZ/+9KdVORPGV0ruumn4pWujmTBipzV8t2U9mYDw1vDJloBK10ddYKGu2wWUKheGq0GLEyZMCHVcu9Vz5ELENYzchYjrsuyHFzTEO/sRhxat7T/TjvR6nzhxItT58Y9/PHAfp02bFpbpmDR27NhQR6+tXrNS/PnX0FAX0P6DH/ygKrvA4i1btlRlF2rugnZbQjzd/VevSVf3g1LaQly73L7qct0EnQ+mfcSNrdrfXND/yZMnw7LPfe5zVVlDtUuJ/d/NIzJB3+6+oaHpR44cCXVeeumlqjxnzpxQ54knnqjK+gGXUkr50Y9+FJbphxa0XEocE9evXx/q3HvvvVV59+7doc7x48fDMg02nzFjRqhz5syZquyuo963XRtxH8PReZu7t7799ttV2QWt633UIdS8TVdjZFd/j3W1P9l7b+bepnM093ed/i7zt0YpcbxbvHhxqKNj0po1a0Id12+UCyP/h3/4h6r8+uuvhzo632v5u/qjftfyNzL39eHhP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzl0YmIZ9uTouNHWQTBh5KTH8zdXJhLFntuVktq9aA9rcudVlmWvUpZbw2QsXLoRlEydOHPg7FyKt4ZMvvvhiqKPH/9xzz4U627dvr8rZoFutlwkIdtdWj8OtJ9snWnTVJzJtNBO+2PpRgcxHFDKB7W5bFy9erMousPHw4cNhmQa0uuPX/e4qsN1xbcsFZCvdJ3euMx+6yIxt7vprGLEL1XQhtvrRAhfGqyGeLkRXj0PDcUsp5e677w7LNFj4qaeeCnU0xHzv3r2hjgYbZ4P+M2OUXkvXHjLXsauA8Naxbpjh563bIhB1MA3t17G2lBhi7T584u7tGqJ93333hTpz586tym4+otufMmVKqOPGZA0237NnT6ijYejuoyo6jrig8UOHDoVlOm66cXvnzp1VedWqVaGOhp/rOSsl96GTbdu2hTp633T7qOO9u/6ZD224e0Tm3pqZ62bmFre71o9odDVHdevWe2lmbpOZ67Tej7L3dpX5YNHMmTPDsmXLllXlDRs2hDp63dwYqeO2fhyolFJee+21sOz555+vyu4jDi0f48n2UdX339Go8Z9SAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgdx8rU0rf83Tva+o73Zn1ZN6fdevKZFFk3nvPZOO4dbfmpajsu8iZ99yH+U57Sz6WZhyUEt/h1/yGUvy70Lou9776d77znaq8devWUEevicsUcBlCmv2QyYsaN25cqJPJJsssG2ad1pwnrZPpW5ntZ9t6JlNC1+XaqF4jt55MFo9rR9pu3Xoy79Rnx03VkoXg9tH9Ttu/o/vtMhU0U8T19RUrVoRlCxYsqMqu/+nxu2v7wQcfVGV3/Dt27AjLNC9B81vc79w9Srfn6rRmWGi/cdcs0/9GWp8Ze1lkUQyWycfT/uf6sdYpJfY3l7uk+VAuL0Xv9ZMnTw513BxFxxLNuCol3hPcGLFr164b7k8ppYwdOzYs07HV9dvz589XZZcFo/vtxt/3338/LHvvvfeqsssL0/EmkzuZpet295YWZEW1ackGKiV3v2m9J7XkNWVk24iuO7M/rv/pelzu3fz588OyWbNmVeXp06cP3P6mTZvCMs29c/lRbmzTfLhsXqnKZGplfufGn5ZMMbThP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qVT/1yIl4YGujDClhBdF+KWDX9Wur3WoGVHj98dmy7LBP21hihmzpHbfibo2YWI6nXKBNS5fdTQ0AkTJoQ6K1euDMu03gsvvBDqvPzyy1XZXSM9DtfWXRhrJlhP24gLMezqereGmGdk+r/Tsv3WDwa4fdQ26fY500cz44hrW5mPKGTWM2i92XVnPvTQOo6432m/cdvXPpEZI5ctWxaWLVmyJCybNGlSVV66dGmoo+OIhgqXEkN8L126FOq8+eabYdnmzZur8vHjx0OdTEBmJjA+o6ug2a5C1d2yLkOEW/pSVx8syW6/T+7YRts+uo8IKDcfcddE5zKuH2mfPHHiRKiT+RiFmyPo9tx+Z+4tyn0wIyMz/3H7+Pbbb1flbNCvjjeZsc7N/1vv/6OtbeP20eU9MjNHXbRoUVV2H35xf1s9/vjjVXnGjBmhzv79+6vy008/Heps2bKlKutHDkrxY4ty/b9l3tJl3+9q/oPB+E8pAAAAAAAA9I6HUgAAAAAAAOgdD6UAAAAAAADQu3SmlNOSD9NlXsTNoOV4s7/JnNtMXoZy7++6ZZlMKTV27NiwbNasWVX5/vvvD3WmTJkSluk7y88880yoo1kwmbyybH5Zaz7SsLbfVR0nm6HSomXdXeautbwf7n4zGse2TO5Z5vy7LLQMHRMyY4TLZpk+fXpVXrBgQajjMqXmzJlTlU+fPh3qaM6Yy0I4cuRIVXa5U9u2bQvLzp49W5Vdu9F8mq7yo7rUZT5Gy7a67O9drLv1fLRmwd2sMudJ62RyR1rH8UymaCYb0O2jWzas9tclPd5MNpM7R+4ekbkmLdmw5EfhVpWZI0ybNi3UWbVqVVWeOnVqqKO5U6WUMmbMmKqsc51SSvnXf/3Xqnzw4MFQR3M2W8e11txL3Bpu3dkQAAAAAAAARi0eSgEAAAAAAKB3PJQCAAAAAABA73goBQAAAAAAgN6lg85bAxu7Ch8cZtBpV7raVmvQeYYL+syESGa27661BosvX7481FmzZk1VdmF8LkR0+/btVXnr1q2hzl133VWV3bHdDEHjw9x+Rldh6K6OrjsTRut0dW6HGZjaEuo6bHpuMyGy7nq4jwho+G0maF1DPUspZfXq1VV54sSJoc7kyZMHbt8d2/Hjx6tyJsR87969oY4GfTruvI3GYPNhaR1/+uwTw5wzjMag85EO2h5tujwffZ7b1j6SCTHPaP2IxqD9KSX2m+zHEEa6bwG/KtdmNYx8xYoVoc6ECROqsvvwy9133x2W7d+/vyp///vfD3U02DzzMQo3H3T9NhNs3uccHSOLERsAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPQunSnltOTVZHKnstkww3qvdDTmV7WuW99Pbr1GmSwq9zvNfpkxY0aoM27cuKrs3jF+7733wrLXXnutKl+5ciXU0XyaO++8M9QZbZlSXWY6DFNLhlRXfavLa9TVONJVpkfrbzLH7zJtMnkdrWN0hv7uiSeeCHUmTZpUlVetWhXqTJ06NSy7fPlyVXbHtmnTpqq8ZcuWUEczFa5duxbquP6g44373a2iyzbRldb73bBk8xoxuox0O8rcI7q6t7Vuy2XIZOY2mXsLGTK4Xbh7hM5/9G+mUmKmpptr7Ny5Myx79tlnq/KpU6cG7qPr6/p3m+Z5ltL+9w+ZUrcP/lMKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6F066Lw1RLh13S11bhXDPFYXIp4Jo3bBdhrI5343ffr0G5ZLKWXu3LlVec6cOaHOvn37wrJXX321KrvwPw3EcwF5emytIdou6Lg1oLtFNvy6xWgM/89sq8+g+ew+qUwbzaw307YzQeeOtm03jrhlum4N4yyllAcffLAquzFi2rRpVXnWrFkD97GUUsaPH1+Vn3766VDnlVdeqcpHjx4NdVxop3Ln8f333x/4O/0Yw/Xr1wf+BtHNMEcg6PzmNNJtK/PBiszvhvlRj+yyFpm5FuHHuBW4jzFNmDChKi9atCjU0Y+6uLnOkSNHwjL9QJT7W0/nP12Oh5kxivvm7YP/lAIAAAAAAEDveCgFAAAAAACA3vFQCgAAAAAAAL3joRQAAAAAAAB6lw46d7oKOu8q2HekwygzRnofXWBc63XMBJ1PnDixKrvA4PPnz1flT30qNsuNGzeGZe++++7A7Wv48pgxY0Kd1jDyloD+1lD/PtuNCxFtNayg9ex69Vj6Dkxsud6tga2Zbblrq+ckE9h/7dq11D5p0PiGDRtCnRkzZtzwN6WUMnv27Kr83nvvhTouIP3ZZ5+tyi+//HKoc/z48arsxqiWwPosFxA/2g0z6PRWRmArutD6UZOu5ujZD220rNvJ3CNvp3EEty43t9I50oULF0KdH/zgB1VZ/z4qJdfXMh91cWHouu7svIYPFOD/xX9KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qUzpYb5vnrr9kd6n1oyrfrcHyeTF5PJnSrFv1es7rrrrqp8zz33hDq67K233gp1nnvuubDs+vXrVdll0XT1vnKm/bVe25FuN9omXBvJtJvWTKPMerrKuRrpvK6+cy+0/bs+q/29tc+4/rdixYqq7HKfxo4dW5UnTZoU6uh+u/bw/PPPh2UvvfRSVd6/f3+o0zJuto6RLq/hZsgZuln7yLDyKoaZn4nRp6v80q6uf3Y9Xc1RMtyYrPvZVe5UNtOKnCncbFyb3bVrV1XetGlTqHPx4sWB67777rvDssuXL/8Ke/f/yeROOa1/o+qy1u1j9OM/pQAAAAAAANA7HkoBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDepYPOndEWIjjMwOhMiGLfgdUt4Zuf+lS85Boid+3atYF13Lp+8YtfDPydW8+YMWOq8t69e0Odc+fOhWUakOy2r8ei23L6DuzP1LlZ90mv9zC3Ncww+kxAY2b7rUGvmf12624JiM2GeKvJkyeHZStXrqzK2mdLKWXZsmVV2Y0/J0+erMqnTp0KdZ5++umw7MiRI35nPyZ3Ptx5c2OSyoSoj7SRvte3tv+MPsPHR+O1xa9upAPrs/fIYX7opWVbI/2hEeBmdOzYsars/o5T+pGpUnKh5pkPDWXmNe6jOpnfuXsk983bB/8pBQAAAAAAgN7xUAoAAAAAAAC946EUAAAAAAAAepfOlHLvi2eyMPR3LlNIf5ep0yqTu+K05sW01OmSbi9zrO69X/d+8AcffFCV3TvM+u7z7NmzQx3Ni3n99ddDnXHjxoVl2ibuuOOOgXVcO9J9bL0erXkJLbk/2XVntOZFuDYxaN2Z99WdzLYy28/UyeyPOx+ubWXWlVl3S36cq5fp227d2tddNptmQ5US+6QbI1xenNq2bVtV3rhxY6hz9uzZsCzT/jI5B5k6Tma8bbm3DTM/JiO7rZHOlGvJS3N9tqUfO2RjoAtdtUcAo0vm3vrhhx8OrKNzto+z/Zb5T+ucCbc37mwAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA7z5W0HnGMEO8MyG2w9qWW9Z6joYZUKuBmJnAehcY7oKmMyH2ly9frsrvvvtuqLNjx46qfPXq1VDHBZ1fu3atKmtguVvm6nQVdD9MXQUEtwak9hlQ7PaxNQw+sz+txzasdXcV6lxK7LeZj1G0hjG766Zjyd133x3qXLhwoSpv3bo11NmyZUtVdiGebvvXr1+vyiPdjzNaP2rQVfj5MM/RSJ/rzLG11skg6BwAACDiP6UAAAAAAADQOx5KAQAAAAAAoHc8lAIAAAAAAEDveCgFAAAAAACA3qWDzp1M+GcmoLjPwHLndgpx/cUvfhHqaEBwa2CuhgqXUsqpU6eqsgsxfvPNNwdu3wWta4iyq6P71GWIcJ8BuU4mtDxzbVvW26WW89jVtrLbb92fzMcQumoj7ncarJz50IGrox8IGDNmTKjjQsynTJlyw/WUUsrrr79eld96661Q5/3336/Kro26/q/L3LEN80MTI22YIf5dbT8j81GNVn3etwk6BwAAiPhPKQAAAAAAAPSOh1IAAAAAAADoHQ+lAAAAAAAA0LtOM6VczkNLplRmPX1rzeIZ5n53lQ+U2UeXhaHb//DDD0Ods2fPVuWLFy+GOpcuXarK48aNC3XcujXXJnP+M5k62UyjliyW1vW05Edlt9+SO/Wr1LvRtrpab9Ywc7+GmXvVOo5kMmw058nlzmWysa5duzZw2c6dO0OdN954Y+B6JkyYUJUvX74c6rgx4s4776zKH3zwQajTd4ZaF7rsI31mSnXZtrvSZ6YdmVIAAADRzTcbBwAAAAAAwE2Ph1IAAAAAAADoHQ+lAAAAAAAA0DseSgEAAAAAAKB36aDz1oBgDfbMBC0PMzC87xBl1XocrWG0d9xxR9P2MuvWZZ/85CdDHQ0fdkHDGkbsrr8LP77rrrtuuC1XpzVoezQGnXcVYt5ViLHbR20Tbltap3U9mb7V1fFnw7Fb9qnLMOhMiLlyYcy6zAWN7927NyzTjxhs3bp14PZ1n9163DnScaSUOCZk+pHTOra3fOgjE4Y9zKDzzD66Y+3qIwLDvEdmfjfM4HeCzgEAACL+UwoAAAAAAAC946EUAAAAAAAAesdDKQAAAAAAAPTuY2VKZXNVBq0nk03jciZatp+RzYvoKueqVVdZOJnck0wWi8uU0gwNl/uidVzuxtixYwfuk8vL0e253KlMpllXeSVOV7lPXeVOufVklrk6mg+U2b5rR5n9cbRtZ7JwWjNl3D5lMmRaxrHs2KPrdu0/s496HV3G29GjR8OyEydOVOX33nsv1BkzZkxVdtdf+7ZmxZXiz+P169erssvYy+QeZnSVKeUMMwsxs63MPdrJjG2ZPjpovR+lJQsrO/61IFMKGN3cGKH9NlPHLWuto9vLrAcAbjb8pxQAAAAAAAB6x0MpAAAAAAAA9I6HUgAAAAAAAOgdD6UAAAAAAADQu3TQudNV0LmG+LUGnbcGj490iOwwaUCwO7cu/DijJcQ3c21dHRd0rsfmQoxbQtxbQ3wzQZMuxLk1RLgloLt1+26ZrmuYIc6t5ygTYt0y/rQeq2sjmd9lxja3nszxa4i525bWcdyHBt5///2qrKHmpZRy9erVquz68bhx4wZuK/MRA3f+u/rQQFdB510Gn7fcE7vcfp9B55l1O30GnTuZNgKMpJbA7tbfZULEh7l9AMDI4D+lAAAAAAAA0DseSgEAAAAAAKB3PJQCAAAAAABA79KZUl3lxQwzL6M1Uyqz/dZMl5Y6rdw+6vZcNkxrhofLdRn0O5f7cueddw7cH7ctXabrKaWU69evV2WXTZXJ1HJZTCqTO9LaRlrbTSbTpTVTS7X2v8zvWjOdMsfSkumSPUdd5VVlspEy58S147vuuqsqu/7Q0tfdut0+ap90Y9S1a9cG7o/r23pudT2ldJcplbkmXfXt7D1K62XqZLaXXc+wMqWy5zGz3y3Xv8v7uLabzL0G/eoq06irvKRhbr+reTQAAL8K/lMKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6N0nfkmqIQAAAAAAAHrGf0oBAAAAAACgdzyUAgAAAAAAQO94KAUAAAAAAIDe8VAKAAAAAAAAveOhFAAAAAAAAHrHQykAAAAAAAD0jodSAAAAAAAA6B0PpQAAAAAAANA7HkoBAAAAAACgd/8bQYaLGb7jjI0AAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -378,11 +444,24 @@ } ], "source": [ - "check_data = first(train_loader)\n", - "print(f\"batch shape: {check_data['image'].shape}\")\n", - "image_visualisation = torch.cat(\n", - " [check_data[\"image\"][0, 0], check_data[\"image\"][1, 0], check_data[\"image\"][2, 0], check_data[\"image\"][3, 0]], dim=1\n", - ")\n", + "\n", + "\n", + "from typing import Dict\n", + "def get_batched_2d_axial_slices(data : Dict):\n", + " images_3D = data['image']\n", + " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1])\n", + " slice_label = data['slice_label']\n", + " #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float()\n", + " slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze()\n", + " return batched_2d_slices, slice_label\n", + "\n", + "check_data = first(train_loader_3D)\n", + "batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data)\n", + "idx = list(torch.randperm(batched_2d_slices.shape[0]))\n", + "slices = [0,30,45,63]\n", + "print(f\"Batch shape: {batched_2d_slices.shape}\")\n", + "print(f\"Slices class: {slice_label[idx][slices].view(-1)}\")\n", + "image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze()\n", "plt.figure(\"training images\", (12, 6))\n", "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", "plt.axis(\"off\")\n", @@ -390,6 +469,30 @@ "plt.show()" ] }, + { + "cell_type": "code", + "execution_count": 48, + "id": "4249e4be-f7e7-48e9-9aa9-436da8c1d1e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(torch.Size([2, 1, 64, 64]), torch.Size([2]))" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))#\n", + "a,b = next(subset_2D) #what is a, what is b? Are these the next images? \n", + "a.shape, b.shape" + ] + }, { "cell_type": "markdown", "id": "08428bc6", @@ -410,9 +513,10 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 49, "id": "bee5913e", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -430,8 +534,8 @@ " attention_levels=(False, False, True),\n", " num_res_blocks=1,\n", " num_head_channels=64,\n", - " with_conditioning=True,\n", - " cross_attention_dim=1,\n", + " with_conditioning=False,\n", + " # cross_attention_dim=1,\n", ")\n", "model.to(device)\n", "\n", @@ -447,17 +551,20 @@ { "cell_type": "markdown", "id": "2a4d3ab2", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ - "### Model training\n", - "Here, we are training our model for 75 epochs (training time: ~50 minutes)." + "### Model training of the Diffusion Model\n", + "Here, we are training our diffusion model for 75 epochs (training time: ~50 minutes)." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 50, "id": "6c0ed909", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -468,218 +575,1347 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 100%|██████████| 125/125 [00:27<00:00, 4.61it/s, loss=0.723]\n", - "Epoch 1: 100%|██████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.276]\n", - "Epoch 2: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0965]\n", - "Epoch 3: 100%|█████████| 125/125 [00:27<00:00, 4.62it/s, loss=0.0376]\n", - "Epoch 4: 100%|█████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0224]\n", - "Epoch 5: 100%|█████████| 125/125 [00:27<00:00, 4.47it/s, loss=0.0187]\n", - "Epoch 6: 100%|█████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0179]\n", - "Epoch 7: 100%|█████████| 125/125 [00:28<00:00, 4.44it/s, loss=0.0169]\n", - "Epoch 8: 100%|█████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0161]\n", - "Epoch 9: 100%|██████████| 125/125 [00:27<00:00, 4.50it/s, loss=0.016]\n", - "Epoch 10: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0156]\n", - "Epoch 11: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0152]\n", - "Epoch 12: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0152]\n", - "Epoch 13: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0151]\n", - "Epoch 14: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0147]\n", - "Epoch 15: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0151]\n", - "Epoch 16: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0151]\n", - "Epoch 17: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0146]\n", - "Epoch 18: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0144]\n", - "Epoch 19: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0143]\n", - "Epoch 20: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0145]\n", - "Epoch 21: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0143]\n", - "Epoch 22: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0138]\n", - "Epoch 23: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 24: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0134]\n", - "Epoch 25: 100%|████████| 125/125 [00:27<00:00, 4.49it/s, loss=0.0135]\n", - "Epoch 26: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 27: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0136]\n", - "Epoch 28: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0135]\n", - "Epoch 29: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0131]\n", - "Epoch 30: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0128]\n", - "Epoch 31: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0129]\n", - "Epoch 32: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0128]\n", - "Epoch 33: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", - "Epoch 34: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0138]\n", - "Epoch 35: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0131]\n", - "Epoch 36: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0132]\n", - "Epoch 37: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", - "Epoch 38: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0124]\n", - "Epoch 39: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0124]\n", - "Epoch 40: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0132]\n", - "Epoch 41: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0128]\n", - "Epoch 42: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0122]\n", - "Epoch 43: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", - "Epoch 44: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0129]\n", - "Epoch 45: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0132]\n", - "Epoch 46: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0125]\n", - "Epoch 47: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0123]\n", - "Epoch 48: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0123]\n", - "Epoch 49: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0125]\n", - "Epoch 50: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", - "Epoch 51: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", - "Epoch 52: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0124]\n", - "Epoch 53: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", - "Epoch 54: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0123]\n", - "Epoch 55: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", - "Epoch 56: 100%|█████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.012]\n", - "Epoch 57: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0126]\n", - "Epoch 58: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", - "Epoch 59: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0126]\n", - "Epoch 60: 100%|████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.0119]\n", - "Epoch 61: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0122]\n", - "Epoch 62: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0119]\n", - "Epoch 63: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0125]\n", - "Epoch 64: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", - "Epoch 65: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0121]\n", - "Epoch 66: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0117]\n", - "Epoch 67: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", - "Epoch 68: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0123]\n", - "Epoch 69: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", - "Epoch 70: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.012]\n", - "Epoch 71: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0118]\n", - "Epoch 72: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0117]\n", - "Epoch 73: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0119]\n", - "Epoch 74: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0125]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train completed, total time: 2074.1517136096954.\n" + "Epoch 0: 1%| | 1/128 [00:00<00:16, 7.89it/s, loss=0.982]" ] - } - ], - "source": [ - "n_epochs = 75\n", - "val_interval = 5\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, batch in progress_bar:\n", - " images = batch[\"image\"].to(device)\n", - " classes = batch[\"class\"].to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - "\n", - " epoch_loss += loss.item()\n", - "\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " for step, batch in enumerate(val_loader):\n", - " images = batch[\"image\"].to(device)\n", - " classes = batch[\"class\"].to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train completed, total time: {total_time}.\")" - ] - }, - { - "cell_type": "markdown", - "id": "a676b3fe", - "metadata": {}, - "source": [ - "### Learning curves" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "f8385176", - "metadata": { - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ + }, { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAArsAAAILCAYAAADoqVT3AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABsNklEQVR4nO3deXwTdf7H8ffk6EFLkVuEolYERESrKKgccixWLu9rVwFBRfFAcFVcF11cZAEX3Z8WVlyVXV1PVNBVAQURUVEsKogKHhSkgCA3PWiTzPz+SJM2toVOm5Jp+3o+Hn00+WZm8s2nQd/55jvfMSzLsgQAAADUQa5YdwAAAACoKYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AOMJef/11dejQQR06dFBOTk6suwMAdZon1h0AUHdde+21WrlypTp27Kg33ngj1t1xjEaNGqljx46SJK/XG+PeVE1RUZHeeecdLV++XF9//bV2796tgoICNWjQQG3atFGXLl10wQUXqHv37rHuKoB6zrAsy4p1JwDUTYTdumnhwoX629/+pl9++UVSMLA3b95cDRo00K5du7Rnz57wtmeccYamTp2qtm3bxqq7AOo5RnYBAJX2xBNP6NFHH5UkpaWl6bbbblPv3r2VlJQU3ub777/X008/rTfffFOrVq3SVVddpf/+979KS0uLVbcB1GPM2QUAVMrixYvDQXfAgAF64403NHDgwIigK0nt27fXtGnTNHPmTHk8Hu3atUvjx4+XaZqx6DaAeo6wC8DRsrOz9eCDD+qCCy7QaaedptNOO039+vXTvffeq7Vr1x5y302bNumhhx7SkCFDlJ6erk6dOumss87SH/7wBz3//PPy+/3l7hc6eeztt9/WDz/8oOuuu05du3bVlVdeGd7m2muvVYcOHXT//fdLkpYtW6ZRo0apR48e6ty5s3r06KE777xTGzZsKHP8ik5Qy8nJCbd/9dVXKigo0BNPPKEhQ4bo9NNP16mnnqrBgwcrMzNTRUVF5fbd5/PpmWee0cUXX6z09HR17dpVV111ld58801J0osvvhh+Djt8Pp8mT54sSWrXrp1mzJihuLi4Q+7Tt29fDRs2THFxcTr66KP166+/hh+bMGGCOnTooL59+1a4f2XrlJWVpSuvvFLp6ekaP368/vnPf4Yf37p16yH7OGLECHXo0EEZGRllHluxYoXuuOMO9enTR507d1bXrl01ZMgQPfzwwxGv5bd++eUXTZs2TRdeeKFOP/10de7cWT179tQVV1yhf/3rX9q9e/ch+wQgupjGAMCx5s+fr4kTJ6qoqEiGYahly5ayLEs5OTnKycnR/PnzNX78eN1www1l9l2yZInuuOOOcCg8+uijlZiYqG3btikrK0tZWVl655139PTTTyshIaHc58/NzdX111+vPXv2qE2bNmrYsGG52z399NOaPn26kpOTdfTRR8s0Tf3666966623tGzZMs2bN0+pqam2XntBQYGGDx+u1atXq1WrVjr66KOVk5OjH374QT/88IPWrl2rJ554ImKfwsJCjRo1Sp9//rkkqUGDBmrevLl+/PFH3XXXXfr88891wgkn2OpHyMKFC7Vt2zZJ0h133HHYoBty++2365ZbblFycnKVnvdwtm7dqokTJ8o0TbVu3VqJiYkaPHiw/vGPf0iSFi1apOuuu67cfXft2qWVK1dKki688MJwu2VZ+utf/6rnn39eUnBOcqtWrbR//359//33+v777/XSSy8pMzNTZ599dsQxV69erZEjRyo3N1eS1KJFCx199NHavXu3Vq9erdWrV2vOnDl67rnnqvy3AGAPI7sAHGnVqlW67777VFRUpIyMDC1btkzLli3Thx9+qBUrVujCCy+UaZr6+9//riVLlkTse+DAAd1zzz0qKipS+/bt9d5772nZsmVauHChVq1apXHjxkmSsrKy9NRTT1XYh7lz56pVq1b64IMP9M4775S77Zo1a/R///d/mjRpkj799FO9/fbb+uSTT/Twww+H+/Kvf/3L9ut/+OGHlZeXp3nz5oWff8WKFfrd734nSVq6dKm+/PLLiH2eeOKJcNC97rrr9Omnn2rBggVasWKF7r33Xr366qtasGCB7b5I0vLlyyVJDRs2VJ8+fSq9X2JiYo0FXUl65pln1L17d3300Ud666239NBDDyk1NVXp6emSgmG3IgsXLlQgEJBhGBo6dGi4/V//+peef/55GYahO+64QytXrtR7772nzz77TG+88YZOOeUU5ebm6pZbbtH27dsjjnn//fcrNzdXJ598st577z0tX75c77zzjj799FPNnTtXxx9/vHbt2qUHHnigZgoCoAzCLgBHmjFjhvx+v8444ww9+uijatmyZfixJk2aaPr06Tr33HMlKTyKF/Lxxx+HR9buueeeiJUAPB6PbrrpJp155pmSpHfeeafCPnzzzTeaPn26mjRpUuE23333nW6//XZdddVVEcuIDR06VGeddZYk6bPPPqvkqy7x008/6cknn1SnTp3CbUlJSZowYUL4funjBgIBvfDCC5KCKyBMmDBB8fHxkoIjkyNGjNAtt9yir776ynZfpGCol6RTTjlFHo9zvhTcsGGDpk6dWmbe8JAhQyRJX331VXjViN8KBf+uXbuqdevWkqT9+/eHR8xvvPFG3XzzzWrQoEF4n44dO+qZZ55RkyZNlJeXF/FBZu/evVq3bp0kafTo0WVWoOjSpYv+9re/6cwzz1SbNm0qnIoCILoIuwAcZ+vWrVq1apWk4JxKl6v8/1T94Q9/kBQ8+3/Tpk3h9oyMDK1du1Yffvhhheu8nnLKKZKkzZs3V9iPzp07H3bJLK/Xq6uvvrrcx0Jr6VYUtg6lf//+4QBWWunpFKWPu3btWu3du1eSdPHFF5d7zOuuuy4iuNkRmmd69NFHV2n/mtKrV69yp5dccMEF8ng8siyr3NHd7du3h99jpacwvP/++8rLy5PL5dKIESPKfc6UlJTwSPC7774bbi+9kueOHTvK3Tc9PV3//e9/NXXq1EpPBQFQPYRdAI5T+uv5E088scLtTj311PDtb775JuIxj8ejli1bVjgKGQp9hxpdq8ycyrS0tDKjiiGhr+8PHjx42OP8ViiMH+q4hYWF4bbs7Ozw7ZNPPrnc/ZKSknTGGWfY7osk5eXlSQpOS3CSiv5GTZo0UY8ePSSVP5Vh4cKFMk1T8fHxESenffHFF5Kkpk2bHnJEP/Te2759u3bt2iVJaty4sU466SRJ0tSpUzVjxoxyT1AEcGQ557soAChW+kz38s6SL89v5076/X699dZbWrRokTZs2KDdu3dr//79tvrRuHHjw26TkpJS4WMVjUhXRkUnw5U+bumRxFDgkqRmzZpVuO/xxx8fnn9rR3Jysvbu3asDBw7Y3rcmHSqQDhkyRB988IG++OIL7dixQy1atAg/FprC0Ldv34ha79y5U1LwPVjZFSt++eUXNW3aVFJwrvXIkSO1Y8cOPfnkk3ryySd1zDHHqFu3burRo4f69OlT4YcjADWDsAvAcQoKCsK327dvX6nQWPrr+QMHDuj666+PmJ/avHlztWvXLjzSu3PnznCwqcwxK2IYxmG3qQq7xy09enyor8erOo2hRYsW2rt3b8R0ESc41Ehzv3791KBBA+Xn5+vdd9/VNddcIyk4TSb03rjooosi9gm99+Lj43X88cfb7s+JJ56ohQsX6qWXXtKrr76qDRs2aOvWrZo3b57mzZunpKQkjRw5UmPGjKnWhyEAlUfYBeA4pQPZrFmzbC/bNXXq1HCYGTZsmIYPH642bdpEbPP4448rMzOz2n11itInx1W0frBUtSkVUvBr+++//17ffvutcnNza3SFhZDqXs0+MTFR/fv315tvvqmFCxeGw+6CBQtkWVbEVIeQ0HuvSZMmVb7EdVJSkkaNGqVRo0Zp8+bN+vjjj/XJJ59o+fLlysvL0+OPP66tW7dqypQp1Xp9ACqHj5UAHKf0SVB2T+4KBAJ66623JEk9e/bUfffdVyboStK+ffuq10mHadSoUfj2oS5aUHpurx29e/eWFLy4xGuvvVbp/UzT1OTJk8usAhEauT5UoI3GlInQqgyrVq0Kj+SHVuAYNGhQmTndoffer7/+GpUrvqWmpuqqq67SY489pg8//FDnn3++JOm1115jPi9whBB2AThOly5dwrcPtVSWZVllRjF3794dHr0MLS/2W6ZpVmneqpOVXjXihx9+KHeb/Px8ZWVlVen4ffv2DX9omDVr1iGvIFbas88+q+eee05XXnmlPv7443B7aKpFaIm48nz99ddV6mtp55xzjpo2bSrTNPXBBx8oJycnfOW9305hkEpODPT7/Ye8Qp/P56vwsYpG1hs2bKi//OUv4fvff/99JV4BgOoi7AJwnFatWoVXDXjhhRfCKwH81htvvKGzzz5bd911lwKBgCRFXA2tohHOp59+OmLuaelVDWqrU089NRwg33777XK3+c9//lNhLQ/H7XZr0qRJMgxDe/fu1ejRow972dv58+dr+vTpkqRzzz1X55xzTvix0Ajq/v37tWXLljL7btmyRYsXL65SX0vzeDwaOHCgJOnDDz8MH/OEE05Q586dy2zfv3//8FSGJ598ssLjPvDAA+rTp0/EOrv//ve/1atXL914440V7lc6CHOiGnBkEHYBONKdd94pl8ulrVu36oYbbogIp0VFRXrllVf0wAMPaP/+/UpOTpbb7ZYUHD1r3769pOBXxaWXJNu5c6ceeughZWZm6pZbbgm3V3W000kaNGigQYMGSQpeKvnpp58OfwDw+Xz697//rccff7zC0e7K6NGjhyZMmCDDMPTNN99o8ODBev7557Vnz56I7b777jvdcccdmjBhggKBgDp16qRHHnkk4qS70AU3JGnatGkRJyWuW7dON954Y/jvWF2hqQyffPJJ+Gp7pdfWLS05OVk333yzJOm9997TX/7yl4gpL7t379bf/vY3vfbaa9q6dWvEFJn27dtr+/bt+vjjjzVp0qQya+1u2bIlfFGQJk2aVOtvAaDyOEENQI3Lzs6uMFyUdtVVV4Uv0HDGGWdoypQpmjhxolatWqUBAwaodevW8nq9+uWXX8JTFc455xzdfffdEce58847dfPNN+vAgQO69NJLdcwxx8gwDG3dulVut1vTp09Xenq6Zs+eLZ/Pp5tuukmpqanKzMxUWlpa9AtwhNx5551auXKltmzZounTp2vWrFk6+uij9csvvyg3N1e33XabLMsKX1K4KkaMGKHU1FQ99NBD2rJlix588EFNnjxZzZs3V0pKinbu3BkOv263W5dddpnuueeeMqOYZ5xxhnr37q1ly5Zp0aJF+vDDD9W6dWsVFBRoy5YtOuWUU/THP/5Rw4YNq1ZNpOCod9u2bfXzzz9r5cqVZS4P/FvXX3+9tmzZopdeekkvvviiXn31VbVu3VpFRUXasWNHeHR29OjRuuCCC8L7nXPOObrpppv0xBNP6IUXXtBLL70Ursv+/fvDy+M1aNBAM2bMiPgWAkDNIewCqHGFhYXhy6geym+XArv44ot1xhln6D//+Y9WrFihbdu2yefz6aijjlL37t114YUX6oILLiizTNd5552nZ555Rk8++aS+/vpr/fLLL2rSpImGDBmikSNHhq9s9uCDDyozM1M7duyQZVmOu2CCXc2bN9drr72mWbNmaenSpdq+fbv27dun0047Tdddd5169Oihxx57TFL1lkzr16+fevbsqXfeeUcffvihvvnmG+3atUu7du1ScnKy0tPT1b17d1100UU67rjjKjxOZmamZs+erYULF2rz5s3hkdKxY8fquuuu048//ljlPv7WkCFDNHPmTEnBUeVWrVpVuK3L5dKkSZM0cOBAvfzyy/ryyy+1detWGYYRnmJz9dVX67TTTiuz77hx43Teeedp/vz5WrFihbZv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9BctWPCWJOn222+SJM2d+6a2bduq22+/Sdddd4NGjRodPubq1V/pueee0TffrFVBQb4aN26iM8/sppEjb9TRR7eK6HtyckNNnTpDmZmP6quvvlBRkU/HH5+m0aNv0emnd61EtaOPsOsAXo9bEieoAQDgVAMHDtG//jVLb775erlh9403XldCQoIGDhyqSZP+rI8+WqZhw0bqrLO66+DBg3r55Rc0bdpkFRUV2ZrmsGPHdt1++81KSEjQ2LF/1LHHttXmzdmaMOGPKiryKTGxZNtfftmmMWNuUHJysm67bZxatTpGmzf/rCeeyNTtt9+k//znRbVo0VIjR94oj8er//1vnv74x3vVseNJatasubZt21rm+T/99BPdc884nXhiB40ff7eaN2+hjRs36KmnntBnn63Qv//9gho3bhLePj8/T3/841hlZAzUpZdeqS1bNisz8x/605/+qJdfnq9GjY6yVfdoIOw6QGjOLheVAADUVl/sWKO3NryrwkCh7X1dLkOmadVAr0rEu+M1OG2ATm/RpUr7Jycna8CAC/TGG6/r669X65RTTg0/9t133+j779dpyJCLFB8fJ4/Hoyuu+L1uuOHm8DYnn3yKBg3qpwUL3rIVdufNe1X5+Xm6774H1Lt3X3k8LvXvf548nnhNnjxJjRo1Cm+7efMmdelymi699Ap163a2JOmUU05VQUGBHn10upYvX6ZLL71CrVodo2bNmkmS2rY9Vh07dqrw+TMz/6GEhAQ98sjjSkkJPtdpp52uo45qrD//+R69/PILuummW8Pbb926Rffd9xddcMFgSVJ6+hnatGmTXnzxOWVlrVS/fgMq/dqjhbDrAKFpDAHTUsA05XYxlRoAULss3rRM2/N3xLobh7T452VVDrtS8MSzN954XfPnvxYRdkMnpl1yyRWKj0/QX/86tcy+ycnJatq0mX75ZZut5/z669UyDEPdup0T0d6nTz899NCDEW1nntldZ57Zvcwxjj32OEnS9u32nnvHju3auHGDevXqEw66Ieee20tut1urVn0e0W4Yhvr27R/R1qZNqiRp3759tp4/Wgi7DlD6whJ+vyV3XAw7AwBAFfQ/trfjR3b7t+1drWOkpbXTaaedrqVLl2js2DuVktJIeXm5WrLkXZ1yyqnhObHff79Or776slat+lx79uxWUVFR+BilR2IrY9eunUpK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82JTU1N133336ZJLLlFiIuvBVRZXUAMAAIiOal9BTZISEhKUkJBQ7mPXXnttNJ6iXmHOLgAAQHRU+1pz3377raZMmRLRtm7dOl1zzTVKT0/XoEGDtHDhwuo+Tb3idhkKXcCNsAsAAFB11Qq769ev1zXXXKMXXnhBphkMZfv379fIkSOVlZWluLg4bdiwQePHj9eqVaui0uH6wDCM8Ogu0xgAAACqrlph95lnnpHf79esWbPClwueO3eudu/erd///vf67LPPtGjRIqWkpOjZZ5+NSofri9C8XUZ2AQAAqq5aYffzzz/XgAED1KtXr3Dbe++9J4/HE740b9u2bTVgwAB9+eWX1etpPRMe2SXsAgAAVFm1wu7OnTvVrl278P28vDytXbtWp556qpo0aRJub926tXbv3l2dp6p3CLsAAADVV62w63a7VVhYGL6/cuVK+f3+MpcLLigoYOkxm7wetyTm7AIAAFRHtcLuscceqxUrVoTvv/jiizIMQ+edd17Edl9//bVatmxZnaeqd0Jzdv2M7AIAAFRZtdbZHTBggB577DFdddVVcrlc+vLLL3XaaaepU6dOkqRAIKAXX3xRK1as0MiRI6PS4foiNI0hYFoKmKbcrmqvEgcAAFDvVCvsjho1SqtWrdLHH38sSWrVqpWmT58efnzjxo2aPHmyjjnmGMKuTaUvLOH3W3LHxbAzAAAAtVS1wm58fLyefvppbdy4Ufv371fHjh0VF1eSytLS0jRixAhdd911ESes4fAirqIWMBUvdwx7AwAAUDtF5XLBxx13XLnthmFowoQJ0XiKeic0Z1diRQYAAICqikrY/eWXX7Rw4UJ9++232rNnjwzDUNOmTdW5c2cNHDhQjRs3jsbT1CsRI7v+QAx7AgAAUHtVO+z++9//1owZM+T3+2VZVsRj8+fP14wZM/Tggw9q8ODB1X2qesXjYWQXAACguqoVdpctW6apU6cqMTFRF154obp06aImTZrINE3t3r1bq1at0qJFizRhwgS1bdtWXbp0iVa/67zSI7tFhF0AAIAqqVbYfe6559SoUSO98sorOvbYY8s8ftVVV+mGG27Q1VdfraeeekqPPfZYdZ6uXmHOLgAAQPVVa/HWtWvX6vzzzy836Ia0b99e559/vr744ovqPFW989vVGAAAAGBftcJubm6ujj766MNu16ZNG+3du7c6T1XvxDFnFwAAoNqqFXZTUlK0efPmw263detWpaSkVOep6h2vp2RdXS4ZDAAAUDXVCrunnnqq3n33Xa1fv77CbdatW6cFCxbotNNOq85T1TteRnYBAACqrVonqF133XX64IMPdPnll2vQoEFKT08PXylt165dysrK0qJFixQIBDRq1KiodLi+YM4uAABA9VUr7J511ll68MEH9dBDD2nevHmaP39+xOOWZSkxMVGTJ0/WGWecUZ2nqndYjQEAAKD6qn1Ricsvv1x9+vTRO++8o7Vr12rXrl3hK6idcsopGjRoEFdQqwKmMQAAAFRfVC4X3KxZMw0bNqzCx5csWaJ58+YpMzMzGk9XL3i4XDAAAEC1VesEtcratGmTlixZciSeqs5gzi4AAED1HZGwC/uYswsAAFB9hF2HYs4uAABA9RF2HYqwCwAAUH2EXYdizi4AAED1EXYdijm7AAAA1UfYdSimMQAAAFSf7XV2zz77bNtPcvDgQdv71HeEXQAAgOqzHXb37NlTpScyDKNK+9VXzNkFAACoPtthl4tDHBlul0suw5BpWfL5CLsAAABVYTvstm7duib6gXJ4PS4V+gKM7AIAAFQRJ6g5WGgqg88fiHFPAAAAaifCroOVhF1GdgEAAKqCsOtghF0AAIDqIew6WDjsMmcXAACgSgi7Dha6iprPb8qyrBj3BgAAoPZxdNidO3euBg4cqM6dO6tnz56aNm2afD5fhdvv2bNHkydPVt++fdW5c2edd955mjZtWq29qEVoZNeypIBJ2AUAALDL9tJjR8r8+fM1ceJETZgwQf369dP69es1ceJE5efna9KkSWW2N01T119/vfLz8/XQQw+pTZs2ysrK0v33369ff/1Vf//732PwKqrnt1dR87gd/dkEAADAcRwbdjMzMzVo0CCNGDFCkpSamqqdO3dq0qRJGjNmjFq2bBmx/XfffadNmzZp1qxZOuuss8L7ZGVlacGCBbIsq9Zdxc3rjryKWmIM+wIAAFAbOXKocOPGjdq8ebN69+4d0d6rVy+Zpqnly5eX2efkk09WVlZWOOiGuFwuud3uWhd0pciRXT8rMgAAANjmyJHd7OxsSVLbtm0j2lu1aiWv16sNGzYc9hh+v1/vv/++3nrrLd122222nt/lMuRyRS8cu4tHaN02pyHEed3h26Ykj8eRn02irqr1qq+olz3U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9hj00jleffVVXXjhhUpPT1efPn305z//Wbt27Qo//sMPP+j6669Xenq60tPTdcMNN+inn36KYY9jxzRNPfPMMxo8eLC6dOmibt26aezYsdqyZUt4G95jFRs5cqQ6dOignJyccBv1KtG3b1916NChzM/gwYPD21CvsnJycnTrrbfq9NNP15lnnqkxY8Zo69at4cepWVBOTk6576/Qz+uvvy6JelWahRozb948q0OHDtacOXOsn3/+2Xrvvfes7t27W/fff3+suxZze/bssUaPHm316NHDOv3006077rgj4vGffvrJ6tKli3XPPfdYP/30k5WVlWVdfvnl1uDBg62ioqIY9Tq2nnnmGatjx47W008/bW3cuNFatmyZ1atXL+vqq6+2TNO0du/ebXXv3t0aNWqUtW7dOuvrr7+2Ro8ebZ177rnWvn37Yt39I27KlCnWaaedZs2fP9/6+eefrY8++sjq16+f1bdvX6uwsJD32CHMnTvX6tSpk9W+fXtr8+bNlmXxb/K3+vTpY02dOtXasWNHxM/u3bsty6Je5dm3b5/Vp08f66abbrK+//57a/Xq1dbFF19sZWRkWIFAgJqV4vf7y7y3duzYYb3xxhtW586drU2bNlEvGwi7Nahfv37W+PHjI9pefPFFq2PHjtYvv/wSo145w3PPPWdde+211s6dO60+ffqUCbsTJkywevfubfl8vnDbTz/9ZLVv39763//+d6S7G3OmaVrnnnuuNWHChIj2l19+2Wrfvr313XffWY8//rh16qmnWnv37g0/vnfvXqtLly7WE088caS7HFM+n88677zzrMzMzIj2+fPnW+3bt7fWrFnDe6wC27dvt7p27WpNmjQpIuxSr0h9+vSxHnvssQofp15lZWZmWueee65VUFAQbsvOzrYWLFhgHTx4kJodRlFRkZWRkWE9/PDDlmXxHrODaQw1ZOPGjdq8ebN69+4d0d6rVy+Zpqnly5fHqGfO0Lt3b82ZM0dNmzYt9/GPPvpIPXr0kMfjCbelpaWpTZs2+vDDD49UNx3DMAy99dZb+tOf/hTR3rJlS0lSXl6ePvroI6Wnp6tRo0bhxxs1aqRTTz213tXM4/Fo6dKluuWWWyLaXa7gf/K8Xi/vsQo8+OCDSk9P1/nnnx/RTr3soV5lvfvuu+rfv78SEhLCbccdd5wyMjIUHx9PzQ7jP//5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8tSoUSOlp6dr1KhR5Y4a//LLL5o1a5Y+/PBD7dy5U40aNVKfPn1066236uijjy63bytWrNBjjz2mdevWSZJOPvlkjR8/XqeffnrEdl9++aWeeuoprV69Wnv27FFycrJSU1M1ZMgQXXPNNXK73dUvFABUAmEXABzqs88+04IFC/S73/1OPXr0UE5Ojt58803ddNNNmjVrlvr27Rve9rHHHtPMmTPVuHFjDRgwQC1bttTPP/+sRYsW6f3339eMGTN0wQUXhLffsGGDrrrqKhUUFGjo0KFq06aNfvzxR7322mt67733NHfuXLVt2zaiP5988omeeeYZDR06VL1799aKFSv06aefatSoUXrnnXfUqlUrSVJWVpaGDx+uhIQEXXDBBWrdurUOHDigZcuWacqUKVq9erUeeeSRI1NEALAAADVi8+bNVvv27a2zzjrL1n733HOP1b59e6tDhw7W8uXLIx579dVXrfbt21sZGRnhtm+++cbq0KGDddZZZ1nbtm2L2P7zzz+3OnbsaJ155plWfn5+uP2SSy6x2rdvX+b4//3vf6327dtbo0ePDrddc801Vvv27a3u3btb2dnZ4XbTNK0RI0ZY7du3t+bMmRNuHz9+vNW+fXvrgw8+iDh2UVGRdfXVV1tnnHGGtXXrVls1AYCqYmQXAGqYZVnKyck55DZer1ctW7aMaEtPT1ePHj0i2i666CJNmzZNGzZs0ObNm5Wamqr58+fLsiz9/ve/LzP9oGvXrurWrZtWrFih5cuXa8CAAfruu++0du1adezYsczxL730Um3ZskUtWrQo08crrrhCxx13XPi+YRjq2bOnPvnkE23ZsiXcvm/fPkkqM1XB6/Xq2WeflcfD/3oAHDn8FwcAati+ffsOuwRZx44d9cYbb0S0/XYerBQMkMcff7y++uorbdiwQampqVq7dm2F20tSly5dtGLFCn3zzTcaMGBAeL3ck046qcy2CQkJuvvuu8s9TufOncu0paSkSJJyc3PDbX369NHy5cs1fvx4jRo1Sv3799cJJ5wgSQRdAEcc/9UBgBqWlJSk6dOnH3Kb5OTkMm1NmzYtd9ujjjpKkrR//35J0q5duw65fZMmTSRJe/bsidg+FFQrq7ztXa7gtYksywq3/eEPf1BeXp6eeOIJPfLII3rkkUfUvHlz9ejRQxdffLG6detm63kBoDoIuwBQw7xer/r37297v1CQ/C3TNCUFlzSTgtMJpMjAWd72oe1Cxy0qKrLdp8q68cYbdfXVV+uDDz7QRx99pI8//ljz5s3TvHnzdPnll2vy5Mk19twAUBqXCwYAhwqNxP7W3r17JZWM5IZ+h0Zsf2v37t3lbh9qrykNGzbUkCFDNG3aNC1fvlxPP/20WrZsqblz52rFihU1+twAEELYBQCHWr16dZk2v9+v7OxsSVKbNm0kSaeccookadWqVeUe54svvojYLvQ7KytLgUAgYlvTNHXHHXfo9ttvl9/vr1K/9+3bF3HCmhQcVe7Ro4euv/56SdI333xTpWMDgF2EXQBwqM8++0yff/55RNvrr7+uAwcOqFOnTuHVGy699FK5XC699NJL2rZtW8T2H3/8sVatWqWWLVuGV17o0KGDTj75ZO3atUuvv/56xPbvvPOOFixYoLy8vCqdTLZnzx6dc845uu6668KrMpQWCrmhNXkBoKYxZxcAalhhYaGefvrpw24XHx+va665Jnz/wgsv1I033qh+/frp+OOPD19Uwu1266677gpvd+KJJ+qOO+7QI488oksuuUQZGRlq2rSpNmzYoPfee08JCQmaNm2avF5veJ+HHnpI1157re6//3599tlnOuGEE/TTTz9pwYIFSk5OrnBFhsNp3Lixbr75Zj3++OMaNGiQ+vfvr6OPPloFBQX64osvtHLlSp188sn63e9+V6XjA4BdhF0AqGEFBQWHXY1BCs5xLR12O3furEsvvVSZmZlaunSpTNNUly5ddNttt+mcc86J2Hf06NFq166dnnvuOb311lsqKChQkyZNlJGREX6stJNOOknz5s1TZmamPvnkEy1cuFCNGjXSoEGDdOutt5a5epodt956qzp06KBXXnlFixcv1t69e+X1enXcccfp9ttv1/DhwxUXF1fl4wOAHYZV0em7AICYmDBhgubNm6eJEydGhF8AgH3M2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdxQlqAAAAqLMY2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ31/+1MPX1gW6EJAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.style.use(\"seaborn-v0_8\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - ")\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "0cd48c2d", - "metadata": {}, - "source": [ - "### Sampling process with classifier-free guidance\n", - "In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`).\n", - "Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector." - ] - }, - { - "cell_type": "code", - "execution_count": 15, + "name": "stdout", + "output_type": "stream", + "text": [ + "step 0 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 1 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 2%|▍ | 3/128 [00:00<00:13, 9.55it/s, loss=1]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 2 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 3 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 4 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 5%|▋ | 7/128 [00:00<00:11, 10.26it/s, loss=0.992]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 5 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 6 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 7 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 7%|▊ | 9/128 [00:00<00:11, 10.38it/s, loss=0.988]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 8 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 9 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 10 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 10%|█ | 13/128 [00:01<00:10, 10.52it/s, loss=0.981]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 11 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 12 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 13 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 12%|█▎ | 15/128 [00:01<00:10, 10.51it/s, loss=0.978]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 14 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 15 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 16 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 15%|█▋ | 19/128 [00:01<00:10, 10.65it/s, loss=0.971]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 17 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 18 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 19 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 16%|█▊ | 21/128 [00:02<00:10, 10.22it/s, loss=0.968]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 20 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 21 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 18%|█▉ | 23/128 [00:02<00:10, 9.82it/s, loss=0.964]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 22 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 23 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 20%|██▏ | 25/128 [00:02<00:10, 9.50it/s, loss=0.961]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 24 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 25 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 21%|██▎ | 27/128 [00:02<00:10, 9.34it/s, loss=0.956]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 26 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([1., 1.], device='cuda:0')\n", + "step 27 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 1.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 1.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 23%|██▍ | 29/128 [00:02<00:10, 9.18it/s, loss=0.953]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 28 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 29 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 24%|██▋ | 31/128 [00:03<00:10, 9.21it/s, loss=0.949]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 30 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 31 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 26%|██▊ | 33/128 [00:03<00:10, 9.20it/s, loss=0.944]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 32 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 33 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 27%|███▎ | 35/128 [00:03<00:10, 9.12it/s, loss=0.94]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 34 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 35 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 29%|███▏ | 37/128 [00:03<00:10, 9.07it/s, loss=0.934]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 36 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 37 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 30%|███▎ | 39/128 [00:04<00:09, 9.09it/s, loss=0.929]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 38 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 39 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 32%|███▌ | 41/128 [00:04<00:09, 9.15it/s, loss=0.925]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 40 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 41 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 34%|███▋ | 43/128 [00:04<00:09, 9.17it/s, loss=0.921]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 42 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 43 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 35%|███▊ | 45/128 [00:04<00:09, 9.15it/s, loss=0.916]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 44 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 45 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 37%|████ | 47/128 [00:04<00:09, 8.95it/s, loss=0.912]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 46 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 47 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 38%|████▏ | 49/128 [00:05<00:08, 9.00it/s, loss=0.906]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 48 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 49 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 40%|████▍ | 51/128 [00:05<00:08, 9.08it/s, loss=0.904]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 50 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 51 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 41%|████▌ | 53/128 [00:05<00:08, 9.06it/s, loss=0.899]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 52 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 53 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 43%|████▋ | 55/128 [00:05<00:07, 9.18it/s, loss=0.894]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 54 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 55 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 45%|████▉ | 57/128 [00:05<00:07, 9.18it/s, loss=0.889]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 56 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 57 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 46%|█████ | 59/128 [00:06<00:07, 9.22it/s, loss=0.884]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 58 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 59 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 48%|█████▎ | 62/128 [00:06<00:06, 10.20it/s, loss=0.877]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "step 60 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 61 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n", + "step 62 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", + "image torch.Size([2, 1, 64, 64])\n", + "classes tensor([0., 0.], device='cuda:0')\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 49%|█████▍ | 63/128 [00:06<00:06, 9.58it/s, loss=0.874]\n", + "Epoch 1: 0%| | 0/128 [00:00)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7611, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7668, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7561, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7131, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.6271, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.6277, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.6392, device='cuda:0', grad_fn=)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 9%|██▏ | 12/128 [00:00<00:03, 35.77it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.6372, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7021, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7493, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7826, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7407, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7278, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7590, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7495, device='cuda:0', grad_fn=)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 17%|███▉ | 22/128 [00:00<00:03, 35.29it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7754, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7786, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7738, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7787, device='cuda:0', grad_fn=)\n", + "h torch.Size([6, 64, 16, 16]) 6\n", + "h torch.Size([6, 16384])\n", + "loss tensor(0.7888, device='cuda:0', grad_fn=)\n", + "h torch.Size([2, 64, 16, 16]) 2\n", + "h torch.Size([2, 16384])\n", + "loss tensor(0.7906, device='cuda:0', grad_fn=)\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 1: 0%| | 0/128 [00:00 3\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlinspace\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_epochs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_epochs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepoch_loss_list\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mC0\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlinewidth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m2.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlabel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mTrain\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m plt\u001b[38;5;241m.\u001b[39mplot(\n\u001b[1;32m 5\u001b[0m np\u001b[38;5;241m.\u001b[39mlinspace(val_interval, n_epochs, \u001b[38;5;28mint\u001b[39m(n_epochs \u001b[38;5;241m/\u001b[39m val_interval)),\n\u001b[1;32m 6\u001b[0m val_epoch_loss_list,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 9\u001b[0m label\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mValidation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 10\u001b[0m )\n\u001b[1;32m 11\u001b[0m plt\u001b[38;5;241m.\u001b[39myticks(fontsize\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m12\u001b[39m)\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/pyplot.py:2767\u001b[0m, in \u001b[0;36mplot\u001b[0;34m(scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2765\u001b[0m \u001b[38;5;129m@_copy_docstring_and_deprecators\u001b[39m(Axes\u001b[38;5;241m.\u001b[39mplot)\n\u001b[1;32m 2766\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mplot\u001b[39m(\u001b[38;5;241m*\u001b[39margs, scalex\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, scaley\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m-> 2767\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgca\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2768\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscalex\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscalex\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscaley\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscaley\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2769\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mdata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m}\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_axes.py:1635\u001b[0m, in \u001b[0;36mAxes.plot\u001b[0;34m(self, scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1393\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1394\u001b[0m \u001b[38;5;124;03mPlot y versus x as lines and/or markers.\u001b[39;00m\n\u001b[1;32m 1395\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1632\u001b[0m \u001b[38;5;124;03m(``'green'``) or hex strings (``'#008000'``).\u001b[39;00m\n\u001b[1;32m 1633\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1634\u001b[0m kwargs \u001b[38;5;241m=\u001b[39m cbook\u001b[38;5;241m.\u001b[39mnormalize_kwargs(kwargs, mlines\u001b[38;5;241m.\u001b[39mLine2D)\n\u001b[0;32m-> 1635\u001b[0m lines \u001b[38;5;241m=\u001b[39m [\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_lines(\u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39mdata, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)]\n\u001b[1;32m 1636\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m line \u001b[38;5;129;01min\u001b[39;00m lines:\n\u001b[1;32m 1637\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39madd_line(line)\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_base.py:312\u001b[0m, in \u001b[0;36m_process_plot_var_args.__call__\u001b[0;34m(self, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 310\u001b[0m this \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m args[\u001b[38;5;241m0\u001b[39m],\n\u001b[1;32m 311\u001b[0m args \u001b[38;5;241m=\u001b[39m args[\u001b[38;5;241m1\u001b[39m:]\n\u001b[0;32m--> 312\u001b[0m \u001b[38;5;28;01myield from\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_plot_args\u001b[49m\u001b[43m(\u001b[49m\u001b[43mthis\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_base.py:498\u001b[0m, in \u001b[0;36m_process_plot_var_args._plot_args\u001b[0;34m(self, tup, kwargs, return_kwargs)\u001b[0m\n\u001b[1;32m 495\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes\u001b[38;5;241m.\u001b[39myaxis\u001b[38;5;241m.\u001b[39mupdate_units(y)\n\u001b[1;32m 497\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m!=\u001b[39m y\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m]:\n\u001b[0;32m--> 498\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mx and y must have same first dimension, but \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 499\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhave shapes \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m and \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 500\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m y\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[1;32m 501\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mx and y can be no greater than 2D, but have \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 502\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mshapes \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m and \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[0;31mValueError\u001b[0m: x and y must have same first dimension, but have shapes (20,) and (5,)" + ] + }, + { + "data": { + "image/png": "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c/jwYcfl1aXee++9CvcyZWVl6f/+7/8klT03BqhpnOOCeiktLU2rV6++ar877rhDt956q0aMGKEtW7bozTffVEpKijp37qxJkyapf//+atGihc6cOaMff/xRycnJ2rhxo06ePKkxY8Y4ltOsWTMNGTJEH330kRITE3X33Xdr0qRJatOmjU6cOKF3331Xq1evVrt27XTq1KlrPpfDNJMnT9aqVau0d+9evfzyyzp48KAmTZqk4OBgxy3/P/roI/Xs2dNx35DSIOiuRo0a6e2339a9996rr776Sh988IFuueUWjRo1SgMGDFBwcLC8vLyUk5OjXbt26d1333VshK/UvXt39e7dW7t27dLrr7+u8+fPa+zYsbLb7Tpw4IBeeeUVff755+rTp89V7+tTFQ8//LBeffVVnTt3zvFFii1atNCgQYMqHLdq1SpFRkYqOztb8+fP15YtW/TII4/o9ttvl6+vr/Ly8vTdd9/pk08+0aeffqphw4ZpxIgRjvGLFy/WqFGjdM8992jAgAHq1KmT7Ha7fvnlF+3evVtLlixxXBX32GOPVfvnBipUp/ftBarR5bf8r+xj1apVjvEXL160/vSnP1keHh5XHefn52edPXvWaf2ZmZlWmzZtyh3Tpk0ba9++fU5fuHelq906vip9Dx065PLzlnLnSxYr0r9/f0uS1b9/f5fv//TTT9Ytt9xS7s8nOjra+vjjjx2vd+3aVeH6rubcuXPW1KlTLW9v76vOp81msx5++GHr6NGjZZaTkZFhtWzZstyxcXFxbn3JojtKSkqcvi5AFXxlwpUOHz5s/fa3v63U38H48eOdxpbOZUUPDw8P629/+5tbnweoDhwqAv4fDw8PPffcc0pPT9eTTz6p7t276+abb5aHh4duvPFG3XbbbRo1apTefPNNZWdnq3Hjxk7jQ0JCtGfPHs2cOVMdOnSQj4+P7Ha7unbtqrlz5yotLc1xLk1D1KZNG3377beaN2+ewsPD1bhxY910003q3bu3li1bpo8//tjp8NvlX/pXFb6+vlq8eLEOHDigv//97xo4cKDatGmjxo0by9fXV0FBQYqOjtZf//pXHTp0SP/85z9dfuVAx44dtWfPHj322GMKDQ2Vt7e3WrRoobvvvlsfffSRy0NI1cVms5W5pf+Vr8sTGhqqr776Sps2bdJDDz2ksLAwNWnSRF5eXmrR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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + ")\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0cd48c2d", + "metadata": {}, + "source": [ + "### Sampling process with classifier-free guidance\n", + "In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`).\n", + "Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector." + ] + }, + { + "cell_type": "code", + "execution_count": 27, "id": "f71e4924", "metadata": { + "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -689,12 +1925,12 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:14<00:00, 71.08it/s]\n" + "100%|███████████████████████████████████████| 1000/1000 [00:12<00:00, 77.06it/s]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -716,7 +1952,7 @@ " with autocast(enabled=True):\n", " with torch.no_grad():\n", " noise_input = torch.cat([noise] * 2)\n", - " model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning)\n", + " model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device))\n", " noise_pred_uncond, noise_pred_text = model_output.chunk(2)\n", " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", "\n", @@ -756,7 +1992,7 @@ "formats": "py:percent,ipynb" }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -770,7 +2006,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py index ad765369..da6de829 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py @@ -6,9 +6,9 @@ # extension: .py # format_name: percent # format_version: '1.3' -# jupytext_version: 1.14.1 +# jupytext_version: 1.14.4 # kernelspec: -# display_name: Python 3 +# display_name: Python 3 (ipykernel) # language: python # name: python3 # --- @@ -20,15 +20,17 @@ # # # [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 - +# # # TODO: Add Open in Colab # # ## Setup environment # %% +# !python /home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/setup.py install # !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" # !python -c "import matplotlib" || pip install -q matplotlib +# !python -c "import seaborn" || pip install -q seaborn # %matplotlib inline # %% [markdown] @@ -49,13 +51,14 @@ import shutil import tempfile import time - +import os import matplotlib.pyplot as plt +import seaborn import numpy as np import torch import torch.nn.functional as F from monai import transforms -from monai.apps import MedNISTDataset +from monai.apps import MedNISTDataset, DecathlonDataset from monai.config import print_config from monai.data import CacheDataset, DataLoader from monai.utils import first, set_determinism @@ -65,18 +68,25 @@ from generative.inferers import DiffusionInferer # TODO: Add right import reference after deployed -from generative.networks.nets import DiffusionModelUNet -from generative.schedulers import DDPMScheduler +from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder +from generative.networks.schedulers.ddpm import DDPMScheduler +from generative.networks.schedulers.ddim import DDIMScheduler print_config() +train=False + + # %% [markdown] # ## Setup data directory # %% jupyter={"outputs_hidden": false} directory = os.environ.get("MONAI_DATA_DIRECTORY") -root_dir = tempfile.mkdtemp() if directory is None else directory -print(root_dir) +#root_dir = tempfile.mkdtemp() if directory is None else directory +root_dir='/home/juliawolleb/PycharmProjects/MONAI/val_brats' +root_dir_val='/home/juliawolleb/PycharmProjects/MONAI/val_brats' + +print(root_dir, root_dir_val) # %% [markdown] # ## Set deterministic training for reproducibility @@ -85,17 +95,71 @@ set_determinism(42) # %% [markdown] -# ## Setup MedNIST Dataset and training and validation dataloaders -# In this tutorial, we will train our models on the MedNIST dataset available on MONAI -# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset). -# Here, we will use the "Hand" and "HeadCT", where our conditioning variable `class` will specify the modality. +# ## Setup BRATS Dataset for 2D slices and training and validation dataloaders +# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150 # %% jupyter={"outputs_hidden": false} -train_data = MedNISTDataset(root_dir=root_dir, section="training", download=True, progress=False, seed=0) -train_datalist = [] -for item in train_data.data: - if item["class_name"] in ["Hand", "HeadCT"]: - train_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) + + +batch_size = 2 +channel = 0 # 0 = Flair +assert channel in [0, 1, 2, 3], "Choose a valid channel" + +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image","label"]), + transforms.EnsureChannelFirstd(keys=["image","label"]), + transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), + transforms.AddChanneld(keys=["image"]), + transforms.EnsureTyped(keys=["image","label"]), + transforms.Orientationd(keys=["image","label"], axcodes="RAS"), + transforms.Spacingd( + keys=["image","label"], + pixdim=(3.0, 3.0, 2.0), + mode=("bilinear", "nearest"), + ), + transforms.CenterSpatialCropd(keys=["image","label"], roi_size=(64, 64, 64)), + transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), + transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), + transforms.Lambdad(keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()), + ] +) +print('download training set') +train_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="training", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=False, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) +nb_3D_images_to_mix =20 +train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4) + +print(f'Image shape {train_ds[0]["image"].shape}') + + + +print('download val set') + +# %% + +val_ds = DecathlonDataset( + root_dir=root_dir_val, + task="Task01_BrainTumour", + section="validation", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=False, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) +val_loader_3D = DataLoader(val_ds, batch_size=2, shuffle=True, num_workers=4) +print(f'Image shape {val_ds[0]["image"].shape}') + + # %% [markdown] # Here we use transforms to augment the training dataset, as usual: @@ -105,75 +169,54 @@ # 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. # 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. # -# ### Classifier-free guidance during training -# -# In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an "unconditional" class. -# Here we specify the "unconditional" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad: -# -# `transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))` # -# Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format. -# %% jupyter={"outputs_hidden": false} -train_transforms = transforms.Compose( - [ - transforms.LoadImaged(keys=["image"]), - transforms.EnsureChannelFirstd(keys=["image"]), - transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), - transforms.RandAffined( - keys=["image"], - rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)], - translate_range=[(-1, 1), (-1, 1)], - scale_range=[(-0.05, 0.05), (-0.05, 0.05)], - spatial_size=[64, 64], - padding_mode="zeros", - prob=0.5, - ), - transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)), - transforms.Lambdad( - keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) - ), - ] -) -train_ds = CacheDataset(data=train_datalist, transform=train_transforms) -train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True) +# %% [markdown] +# ### Visualisation of the training images # %% jupyter={"outputs_hidden": false} -val_data = MedNISTDataset(root_dir=root_dir, section="validation", download=True, progress=False, seed=0) -val_datalist = [] -for item in val_data.data: - if item["class_name"] in ["Hand", "HeadCT"]: - val_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) -val_transforms = transforms.Compose( - [ - transforms.LoadImaged(keys=["image"]), - transforms.EnsureChannelFirstd(keys=["image"]), - transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), - transforms.Lambdad( - keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) - ), - ] -) -val_ds = CacheDataset(data=val_datalist, transform=val_transforms) -val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True) +from typing import Dict +def get_batched_2d_axial_slices(data : Dict): + images_3D = data['image'] + batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1]) + slice_label = data['slice_label'] + #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float() + slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze() + return batched_2d_slices, slice_label +print('check data') -# %% [markdown] -# ### Visualisation of the training images +if train==True: + check_data = first(train_loader_3D) + batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data) + idx = list(torch.randperm(batched_2d_slices.shape[0])) + print('idx', len(idx)) + print(f"Batch shape: {batched_2d_slices.shape}") + print(f"Slices class: {slice_label[idx][slices].view(-1)}") + subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) # -# %% jupyter={"outputs_hidden": false} -check_data = first(train_loader) -print(f"batch shape: {check_data['image'].shape}") -image_visualisation = torch.cat( - [check_data["image"][0, 0], check_data["image"][1, 0], check_data["image"][2, 0], check_data["image"][3, 0]], dim=1 -) +check_data_val = first(val_loader_3D) +batched_2d_slices_val, slice_label_val = get_batched_2d_axial_slices(check_data_val) + + + +idx_val=list(torch.randperm(batched_2d_slices_val.shape[0])) +slices = [0,30,45,63] + +image_visualisation = torch.cat(batched_2d_slices_val[idx_val][slices].squeeze().split(1), dim=2).squeeze() plt.figure("training images", (12, 6)) plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") plt.axis("off") plt.tight_layout() plt.show() +# %% + +subset_2D_val = zip(batched_2d_slices_val.split(1),slice_label_val.split(1))# + + + # %% [markdown] # ### Define network, scheduler, optimizer, and inferer # At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using @@ -198,104 +241,245 @@ attention_levels=(False, False, True), num_res_blocks=1, num_head_channels=64, - with_conditioning=True, - cross_attention_dim=1, + with_conditioning=False, + # cross_attention_dim=1, ) model.to(device) -scheduler = DDPMScheduler( +scheduler = DDIMScheduler( num_train_timesteps=1000, ) optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) inferer = DiffusionInferer(scheduler) -# %% [markdown] -# ### Model training -# Here, we are training our model for 75 epochs (training time: ~50 minutes). +# %% [markdown] tags=[] +# ### Model training of the Diffusion Model +# Here, we are training our diffusion model for 75 epochs (training time: ~50 minutes). # %% jupyter={"outputs_hidden": false} -n_epochs = 75 -val_interval = 5 +n_epochs =100 +val_interval = 1 epoch_loss_list = [] val_epoch_loss_list = [] -scaler = GradScaler() -total_start = time.time() -for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) - progress_bar.set_description(f"Epoch {epoch}") - for step, batch in progress_bar: - images = batch["image"].to(device) - classes = batch["class"].to(device) - optimizer.zero_grad(set_to_none=True) - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - - epoch_loss += loss.item() - - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch + 1) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - for step, batch in enumerate(val_loader): - images = batch["image"].to(device) - classes = batch["class"].to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() +if train==False: + model.load_state_dict(torch.load("./model.pt", map_location={'cuda:0': 'cpu'})) +else: + scaler = GradScaler() + total_start = time.time() + for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) + subset_2D_val = zip(batched_2d_slices_val.split(1), slice_label.split(1)) # + + progress_bar = tqdm(enumerate(subset_2D), total=len(idx), ncols=10) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a,b) in progress_bar: + print('step', step, a.shape, b.shape, b) + images = a.to(device) + classes = b.to(device) + optimizer.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + epoch_loss += loss.item() + progress_bar.set_postfix( { - "val_loss": val_epoch_loss / (step + 1), + "loss": epoch_loss / (step + 1), } ) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + progress_bar_val = tqdm(enumerate(subset_2D_val), total=len(idx_val), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: + images = a.to(device) + classes = b.to(device) + timesteps = torch.randint(0, 1000, (len(images),)).to(device)#torch.from_numpy(np.arange(0, 1000)[::-1].copy()) + + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + total_time = time.time() - total_start + print(f"train diffusion completed, total time: {total_time}.") + + plt.style.use("seaborn-bright") + plt.title("Learning Curves Diffusion Model", fontsize=20) + plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") + plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) + plt.yticks(fontsize=12) + plt.xticks(fontsize=12) + plt.xlabel("Epochs", fontsize=16) + plt.ylabel("Loss", fontsize=16) + plt.legend(prop={"size": 14}) + #plt.show() + #torch.save(model.state_dict(), "./model.pt") -total_time = time.time() - total_start -print(f"train completed, total time: {total_time}.") + +# %% +### Model training of the Classification Model +#Here, we are training our binary classification model for 5 epochs. + +# %% +## First, we define the classification model + + +# %% +classifier = DiffusionModelEncoder( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(64, 64, 64), + # attention_levels=(False, False, True), + num_res_blocks=1, + num_head_channels=64, + with_conditioning=False, + # cross_attention_dim=1, +) +classifier.to(device) +batch_size=6 + + +# %% +n_epochs = 100 +val_interval = 1 +epoch_loss_list = [] +val_epoch_loss_list = [] +optimizer = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) + +classifier.to(device) + + +if train==False: + classifier.load_state_dict(torch.load("./classifier.pt", map_location={'cuda:0': 'cpu'})) +else: + scaler = GradScaler() + total_start = time.time() + for epoch in range(n_epochs): + classifier.train() + epoch_loss = 0 + subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) + subset_2D_val = zip(batched_2d_slices_val.split(1), slice_label.split(1)) # + progress_bar = tqdm(enumerate(subset_2D), total=len(idx), ncols=20) + progress_bar.set_description(f"Epoch {epoch}") + + + for step, (a,b) in progress_bar: + images = a.to(device) + classes = b.to(device) + optimizer.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + + with autocast(enabled=True): + # Generate random noise + noise = 0*torch.randn_like(images).to(device) + + # Get model prediction + # pred=classifier(images) + + pred = inferer(inputs=images, diffusion_model=classifier, noise=noise, timesteps=timesteps) #remove the class conditioning + print('pred', pred) + # noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning + loss = F.binary_cross_entropy_with_logits(pred[:,0].float(), classes.float()) + print('loss', loss) + #scaler.scale(loss).backward() + # scaler.step(optimizer) + loss.backward() + optimizer.step() + #scaler.update() + + epoch_loss += loss.item() + + progress_bar.set_postfix( + { + "loss": epoch_loss / (step + 1), + } + ) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + classifier.eval() + val_epoch_loss = 0 + progress_bar = tqdm(enumerate(subset_2D_val), total=len(idx), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a,b) in progress_bar: + images = a.to(device) + classes = b.to(device) + + timesteps = torch.randint(0, 1000, (len(images),)).to(device)#torch.from_numpy(np.arange(0, 1000)[::-1].copy()) + + with torch.no_grad(): + with autocast(enabled=True): + noise = 0*torch.randn_like(images).to(device) + pred = inferer(inputs=images, diffusion_model=classifier, noise=noise, timesteps=timesteps) + val_loss = F.binary_cross_entropy_with_logits(pred[:,0].float(), classes.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix( + { + "val_loss": val_epoch_loss / (step + 1), + } + ) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + total_time = time.time() - total_start + print(f"train completed, total time: {total_time}.") + # torch.save(classifier.state_dict(), "./classifier.pt") # %% [markdown] # ### Learning curves # %% jupyter={"outputs_hidden": false} -plt.style.use("seaborn-v0_8") -plt.title("Learning Curves", fontsize=20) -plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") -plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", -) -plt.yticks(fontsize=12) -plt.xticks(fontsize=12) -plt.xlabel("Epochs", fontsize=16) -plt.ylabel("Loss", fontsize=16) -plt.legend(prop={"size": 14}) -plt.show() + plt.style.use("seaborn-bright") + plt.title("Learning Curves", fontsize=20) + plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") + plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) + plt.yticks(fontsize=12) + plt.xticks(fontsize=12) + plt.xlabel("Epochs", fontsize=16) + plt.ylabel("Loss", fontsize=16) + plt.legend(prop={"size": 14}) + #plt.show() # %% [markdown] # ### Sampling process with classifier-free guidance @@ -304,22 +488,83 @@ # %% jupyter={"outputs_hidden": false} model.eval() -guidance_scale = 7.0 +guidance_scale = 0 conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device) -noise = torch.randn((1, 1, 64, 64)) +# %% [markdown] +# ### Pick an input slice to be transformed + +inputimg = batched_2d_slices_val[50][0,...] +plt.figure("input") +plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + + +noise = inputimg[None,None,...]#torch.randn((1, 1, 64, 64)) noise = noise.to(device) scheduler.set_timesteps(num_inference_steps=1000) -progress_bar = tqdm(scheduler.timesteps) -for t in progress_bar: +L=20 +progress_bar = tqdm(range(L)) #go back and forth L timesteps + + + +for t in progress_bar: #go through the noising process + print('t noising', t) + with autocast(enabled=True): with torch.no_grad(): - noise_input = torch.cat([noise] * 2) - model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning) - noise_pred_uncond, noise_pred_text = model_output.chunk(2) - noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) - noise, _ = scheduler.step(noise_pred, t, noise) + noise_input = noise + print('inputshape', noise_input.shape) + model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device)) + # noise_pred_uncond, noise_pred_text = model_output.chunk(2) #this is supposed to be epsilon + noise_pred = model_output #this is supposed to be epsilon + + noise, _ = scheduler.reversed_step(noise_pred, t, noise) + +plt.style.use("default") +plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +def cond_fn(x, t, y=None): #compute the gradient + assert y is not None + with torch.enable_grad(): + x_in = x.detach().requires_grad_(True) + logits = classifier(x_in, t) + log_probs = F.log_softmax(logits, dim=-1) + selected = log_probs[range(len(logits)), y.view(-1)] + a = th.autograd.grad(selected.sum(), x_in)[0] + return a, a * args.classifier_scale +#desired class +y=torch.tensor(0) +scale=100 + +for i in progress_bar: #go through the denoising process + t=L-i + print('t denoising', t) + with autocast(enabled=True): + with torch.enable_grad(): + noise_input = noise + print('inputshape', noise_input.shape) + model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device)) + + x_in = noise_input.detach().requires_grad_(True) + + logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(noise.device)) + print('logits', logits) + log_probs = F.log_softmax(logits, dim=-1) + selected = log_probs[range(len(logits)), y.view(-1)] + a = torch.autograd.grad(selected.sum(), x_in)[0] + # noise_pred_uncond, noise_pred_text = model_output.chunk(2) #this is supposed to be epsilon + noise_pred = model_output # this is supposed to be epsilon + updated_noise=noise_pred - scale*a + + noise, _ = scheduler.step(updated_noise, t, noise) plt.style.use("default") plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") @@ -327,11 +572,17 @@ plt.axis("off") plt.show() + +diff=inputimg.cpu()-noise[0, 0].cpu() +plt.style.use("default") +plt.imshow(diff, cmap="jet") +plt.tight_layout() +plt.axis("off") +plt.show() # %% [markdown] # ### Cleanup data directory # # Remove directory if a temporary was used. # %% -if directory is None: - shutil.rmtree(root_dir) + diff --git a/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb new file mode 100644 index 00000000..3f9e39a8 --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb @@ -0,0 +1,33 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "f37e04b7-9695-4a24-85bb-fffdd87ee1b9", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb new file mode 100644 index 00000000..363fcab7 --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb new file mode 100644 index 00000000..06ad686a --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "cf6673e1", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# # Diff-SCM\n", + "# \n", + "# This tutorial illustrates how to load the 2D BRATS dataset.\n", + "# \n", + "# \n", + "# ## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "2dc388db", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done\n" + ] + } + ], + "source": [ + "\n", + "\n", + "get_ipython().system('python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"')\n", + "get_ipython().system('python -c \"import matplotlib\" || pip install -q matplotlib')\n", + "get_ipython().run_line_magic('matplotlib', 'inline')\n", + "print('done')\n", + "\n", + "\n", + "# ## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4167c04e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI version: 1.1.dev2248\n", + "Numpy version: 1.23.2\n", + "Pytorch version: 1.12.1\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 3400bd91422ccba9ccc3aa2ffe7fecd4eb5596bf\n", + "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Nibabel version: 4.0.1\n", + "scikit-image version: 0.19.3\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n", + "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", + "TorchVision version: 0.13.1\n", + "tqdm version: 4.64.1\n", + "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", + "psutil version: 5.9.4\n", + "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", + "einops version: 0.6.0\n", + "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", + "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", + "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "\n", + "\n", + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "import os\n", + "import shutil\n", + "import tempfile\n", + "import time\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from monai import transforms\n", + "from monai.apps import DecathlonDataset\n", + "from monai.config import print_config\n", + "from monai.data import CacheDataset, DataLoader\n", + "from monai.utils import first, set_determinism\n", + "from torch.cuda.amp import GradScaler, autocast\n", + "from tqdm import tqdm\n", + "\n", + "from generative.inferers import DiffusionInferer\n", + "\n", + "# TODO: Add right import reference after deployed\n", + "from generative.networks.nets import DiffusionModelUNet\n", + "from generative.networks.schedulers import DDPMScheduler\n", + "\n", + "print_config()\n", + "\n", + "\n", + "# ## Setup data directory" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "86b390cd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmpf7ygl4zq\n" + ] + } + ], + "source": [ + "\n", + "\n", + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "print(root_dir)\n", + "root_dir= '/tmp/tmp6o69ziv1'\n", + "\n", + "\n", + "# ## Set deterministic training for reproducibility" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "6d644892", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "set_determinism(42)\n", + "\n", + "\n", + "# ## Setup MedNIST Dataset and training and validation dataloaders\n", + "# In this tutorial, we will train our models on the MedNIST dataset available on MONAI\n", + "# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset).\n", + "# Here, we will use the \"Hand\" and \"HeadCT\", where our conditioning variable `class` will specify the modality." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "5c29c6a2", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2023-01-20 09:47:29,125 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-01-20 09:47:29,126 - INFO - File exists: /tmp/tmp6o69ziv1/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-01-20 09:47:29,127 - INFO - Non-empty folder exists in /tmp/tmp6o69ziv1/Task01_BrainTumour, skipped extracting.\n", + "Image shape torch.Size([1, 64, 64, 64])\n" + ] + } + ], + "source": [ + "\n", + "\n", + "batch_size = 2\n", + "channel = 0 # 0 = Flair\n", + "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", + "\n", + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\",\"label\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\",\"label\"]),\n", + " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", + " transforms.AddChanneld(keys=[\"image\"]),\n", + " transforms.EnsureTyped(keys=[\"image\",\"label\"]),\n", + " transforms.Orientationd(keys=[\"image\",\"label\"], axcodes=\"RAS\"),\n", + " transforms.Spacingd(\n", + " keys=[\"image\",\"label\"],\n", + " pixdim=(3.0, 3.0, 2.0),\n", + " mode=(\"bilinear\", \"nearest\"),\n", + " ),\n", + " transforms.CenterSpatialCropd(keys=[\"image\",\"label\"], roi_size=(64, 64, 64)),\n", + " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", + " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", + " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", + " ]\n", + ")\n", + "train_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"training\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "nb_3D_images_to_mix = 2\n", + "train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", + "print(f'Image shape {train_ds[0][\"image\"].shape}')" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "16e750a6", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "from typing import Dict\n", + "def get_batched_2d_axial_slices(data : Dict):\n", + " images_3D = data['image']\n", + " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1])\n", + " slice_label = data['slice_label']\n", + " #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float()\n", + " slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze()\n", + " return batched_2d_slices, slice_label\n", + "\n", + "\n", + "# ### Visualisation of the training images" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "310b925c", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "check_data torch.Size([2, 1, 64, 64, 64]) torch.Size([2, 64])\n" + ] + } + ], + "source": [ + "\n", + "\n", + "check_data = first(train_loader_3D)\n", + "print('check_data', check_data[\"image\"].shape, check_data[\"slice_label\"].shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "4105a01f", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "idx [tensor(125), tensor(70), tensor(71), tensor(10), tensor(112), tensor(72), tensor(100), tensor(108), tensor(48), tensor(90), tensor(5), tensor(83), tensor(53), tensor(38), tensor(121), tensor(115), tensor(116), tensor(75), tensor(34), tensor(2), tensor(118), tensor(46), tensor(57), tensor(64), tensor(107), tensor(126), tensor(109), tensor(98), tensor(15), tensor(13), tensor(113), tensor(93), tensor(106), tensor(73), tensor(4), tensor(102), tensor(21), tensor(96), tensor(30), tensor(18), tensor(91), tensor(16), tensor(77), tensor(49), tensor(50), tensor(123), tensor(28), tensor(42), tensor(23), tensor(6), tensor(12), tensor(65), tensor(31), tensor(41), tensor(3), tensor(101), tensor(67), tensor(54), tensor(62), tensor(120), tensor(94), tensor(80), tensor(35), tensor(9), tensor(82), tensor(84), tensor(14), tensor(32), tensor(127), tensor(59), tensor(25), tensor(63), tensor(87), tensor(92), tensor(40), tensor(97), tensor(51), tensor(7), tensor(105), tensor(19), tensor(88), tensor(36), tensor(20), tensor(110), tensor(29), tensor(111), tensor(60), tensor(44), tensor(45), tensor(52), tensor(68), tensor(124), tensor(37), tensor(117), tensor(85), tensor(17), tensor(95), tensor(55), tensor(0), tensor(56), tensor(86), tensor(58), tensor(47), tensor(89), tensor(122), tensor(22), tensor(78), tensor(79), tensor(11), tensor(61), tensor(119), tensor(27), tensor(114), tensor(103), tensor(43), tensor(99), tensor(24), tensor(8), tensor(81), tensor(33), tensor(104), tensor(26), tensor(66), tensor(39), tensor(69), tensor(76), tensor(74), tensor(1)] 128\n", + "Batch shape: torch.Size([128, 1, 64, 64])\n", + "Slices class: tensor([0., 1., 0., 0.])\n" + ] + }, + { + "data": { + "image/png": "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0DkAAAAA05KlFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNzZvq4OTk5Nk8DwAAAADOI74pBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBz/w/KFbC7XwVz4QAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data)\n", + "idx = list(torch.randperm(batched_2d_slices.shape[0]))\n", + "print('idx', idx, len(idx))\n", + "slices = [0,30,45,63]\n", + "print(f\"Batch shape: {batched_2d_slices.shape}\")\n", + "print(f\"Slices class: {slice_label[idx][slices].view(-1)}\")\n", + "image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze()\n", + "plt.figure(\"training images\", (12, 6))\n", + "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "21e0c944", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "torch.Size([128])" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "\n", + "slice_label.shape\n", + "\n", + "\n", + "# ## Check Distribution of Healthy / Unhealthy" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "1114650d", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(torch.Size([2, 1, 64, 64]), torch.Size([2]))" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))#\n", + "a,b = next(subset_2D) #what is a, what is b?\n", + "a.shape, b.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "5633a8c8", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAHDCAYAAACESXgYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9mElEQVR4nO3de5yN5f7/8fcyhzUHMxODOWSMoSHFRAgjezBm2kjbRiclkk50kG0TkqVvjd1UUom9dcCuSAdKXyWDTGSqITrQg9rOmyGSmRxGzPX7w2/W1zIHs8bMpaXX8/G4Hw/rWte678+61j1rvd1HhzHGCAAAwJIa57sAAADwx0L4AAAAVhE+AACAVYQPAABgFeEDAABYRfgAAABWET4AAIBVhA8AAGAV4QMAAFhF+DgPZs2aJYfD4Z6CgoIUHR2tLl26aNKkSdq3b1+J17hcLjkcDq+Wc+TIEblcLq1YscKr15W2rIYNG+raa6/1aj5nM2fOHE2ZMqXU5xwOh1wuV5Uur6otW7ZMbdq0UWhoqBwOh957771Kz6t4ndi2bZu7bdCgQWrYsOE51/l70bBhQw0aNMj9ePfu3XK5XFq/fn2JvoMGDVLNmjXtFVdJ27Ztk8Ph0KxZs6pl/qWtAxkZGaWua8Xr0Jo1a6qllvKU91mWZuPGjXK5XB7rO/5YCB/n0cyZM5WTk6OsrCy9+OKLatmypZ588kk1a9ZMS5cu9eg7ZMgQ5eTkeDX/I0eOaOLEiV6Hj8osqzLKCx85OTkaMmRItddQWcYY3XDDDQoICNDChQuVk5OjlJSUKl3G+PHjtWDBgiqd5/m0YMECjR8/3v149+7dmjhxYoV/sP6ISlsHygof55O3n+XGjRs1ceJEwscfmP/5LuCPrHnz5mrTpo37cd++ffXQQw/p6quvVp8+ffTDDz8oKipKklS/fn3Vr1+/Wus5cuSIQkJCrCzrbNq3b39el382u3fv1s8//6y//vWvSk1NrZZlNG7cuFrme760atXqfJfgcy60deCP6OjRowoKCvJ6y/WFji0fvzMNGjTQM888o4KCAv3rX/9yt5e2K2T58uXq3LmzIiMjFRwcrAYNGqhv3746cuSItm3bprp160qSJk6c6N7FU7zZu3h+X331lfr166datWq5v+jK28WzYMECJSUlKSgoSI0aNdLzzz/v8Xxpuw8kacWKFXI4HO6tMJ07d9aiRYu0fft2j11QxUrb7fLdd9/pL3/5i2rVqqWgoCC1bNlSs2fPLnU5c+fO1bhx4xQbG6vw8HB169ZNmzZtKnvgT7Nq1SqlpqYqLCxMISEhSk5O1qJFi9zPu1wudzgbPXq0HA5HubtHioqK9Pjjj6tp06YKDg7WRRddpKSkJD333HPl1lHaJveioiK98MILatmypXte7du318KFCz36zZs3Tx06dFBoaKhq1qypa665RuvWrfPos2XLFt10002KjY2V0+lUVFSUUlNTy/3f66JFi+RwOJSbm+tue/fdd+VwONSzZ0+PvklJSerbt6/78em7XVasWKG2bdtKkm6//Xb353/mZ/7jjz+qR48eqlmzpuLi4vS3v/1NhYWF5Q2b+/2np6crJiZGwcHBatasmR5++GEdPnzYo1/x7p2KLGf37t264YYbFBYWpoiICN14443Ky8s7ay35+fny9/fXU0895W7bv3+/atSooYiICJ04ccLd/sADD6hu3boqvt/nmeuAw+HQ4cOHNXv2bPeYde7c2WN5BQUFuvfee1WnTh1FRkaqT58+2r17t0efoqIiZWZm6tJLL5XT6VS9evV02223adeuXR79ztxVVqxz587u5Vb0syw2a9YsXX/99ZKkLl26uPsX77qqyDKLl+twODRnzhyNHj1aMTExqlmzpnr16qW9e/eqoKBAd911l+rUqaM6dero9ttv16+//uoxz2PHjmnMmDFKSEhQYGCgLr74Yg0bNky//PKLR7+y3s+ZtRZ//y1ZskSDBw9W3bp1FRISUqF19o+G8PE71KNHD/n5+enTTz8ts8+2bdvUs2dPBQYG6tVXX9XixYv1j3/8Q6GhoTp+/LhiYmK0ePFiSdIdd9yhnJwc5eTkeGz2lqQ+ffrokksu0dtvv61//vOf5da1fv16DR8+XA899JAWLFig5ORkPfjgg3r66ae9fo/Tpk1Tx44dFR0d7a6tvF09mzZtUnJysjZs2KDnn39e8+fP12WXXaZBgwYpMzOzRP+xY8dq+/btevnllzVjxgz98MMP6tWrl06ePFluXdnZ2eratasOHTqkV155RXPnzlVYWJh69eqlefPmSTq1W2r+/PmSpPvvv185OTnl7h7JzMyUy+XSzTffrEWLFmnevHm64447SnzBVcSgQYP04IMPqm3btpo3b57efPNNXXfddR5hLyMjQzfffLMuu+wyvfXWW3rttddUUFCgTp06aePGje5+PXr00Nq1a5WZmamsrCxNnz5drVq1KreulJQUBQQEeOwWXLp0qYKDg5Wdna3ffvtNkrRv3z5999136tatW6nzufLKKzVz5kxJ0iOPPOL+/E/f1fbbb7/puuuuU2pqqt5//30NHjxYzz77rJ588smzjtMPP/ygHj166JVXXtHixYs1fPhwvfXWW+rVq1eJvhVZztGjR9WtWzctWbJEkyZN0ttvv63o6GjdeOONZ60lPDxcbdu29RizZcuWyel0qqCgQF9++aW7fenSperatWuZ4T8nJ0fBwcHq0aOHe8ymTZvm0WfIkCEKCAjQnDlzlJmZqRUrVujWW2/16HPvvfdq9OjRSktL08KFC/U///M/Wrx4sZKTk7V///6zvqfTVeSzPF3Pnj2VkZEhSXrxxRfd/c8MrxU1duxY7du3T7NmzdIzzzyjFStW6Oabb1bfvn0VERGhuXPnatSoUXrttdc0duxY9+uMMerdu7eefvppDRgwQIsWLdKIESM0e/Zsde3a9ZwCw+DBgxUQEKDXXntN77zzjgICAio9rwuWgXUzZ840kkxubm6ZfaKiokyzZs3cjydMmGBO/7jeeecdI8msX7++zHn89NNPRpKZMGFCieeK5/foo4+W+dzp4uPjjcPhKLG8tLQ0Ex4ebg4fPuzx3rZu3erR75NPPjGSzCeffOJu69mzp4mPjy+19jPrvummm4zT6TQ7duzw6Ne9e3cTEhJifvnlF4/l9OjRw6PfW2+9ZSSZnJycUpdXrH379qZevXqmoKDA3XbixAnTvHlzU79+fVNUVGSMMWbr1q1GknnqqafKnZ8xxlx77bWmZcuW5fYpbdwGDhzoMT6ffvqpkWTGjRtX5nx27Nhh/P39zf333+/RXlBQYKKjo80NN9xgjDFm//79RpKZMmXKWes/09VXX226du3qfnzJJZeYv//976ZGjRomOzvbGGPMG2+8YSSZzZs3u/vFx8ebgQMHuh/n5uYaSWbmzJklljFw4EAjybz11lse7T169DBNmzb1qt6ioiLz22+/mezsbCPJfP31114vZ/r06UaSef/99z363XnnnWW+h9M98sgjJjg42Bw7dswYY8yQIUPMn//8Z5OUlGQmTpxojDHmv//9r5FkZsyY4VHfmX8joaGhHuNY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KsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAqmoLH9OmTVNCQoKCgoLUunVrrVy5sroWBQAAfEi1hI958+Zp+PDhGjdunNatW6dOnTqpe/fu2rFjR3UsDgAA+JBqCR+TJ0/WHXfcoSFDhqhZs2aaMmWK4uLiNH369OpYHAAA8CH+VT3D48ePa+3atXr44Yc92tPT07V69eoS/QsLC1VYWOh+fOjQIUlSfn5+VZcmSSoqPFIt84Wn6vr8AFzY+I62ozq+o4vnaYw5a98qDx/79+/XyZMnFRUV5dEeFRWlvLy8Ev0nTZqkiRMnlmiPi4ur6tJgUcSU810BAKAs1fkdXVBQoIiIiHL7VHn4KOZwODweG2NKtEnSmDFjNGLECPfjoqIi/fzzz4qMjCy1/7nIz89XXFycdu7cqfDw8CqdN/4P42wH42wPY20H42xHdY2zMUYFBQWKjY09a98qDx916tSRn59fia0c+/btK7E1RJKcTqecTqdH20UXXVTVZXkIDw9nxbaAcbaDcbaHsbaDcbajOsb5bFs8ilX5AaeBgYFq3bq1srKyPNqzsrKUnJxc1YsDAAA+plp2u4wYMUIDBgxQmzZt1KFDB82YMUM7duzQPffcUx2LAwAAPqRawseNN96oAwcO6LHHHtOePXvUvHlzffjhh4qPj6+OxVWY0+nUhAkTSuzmQdVinO1gnO1hrO1gnO34PYyzw1TknBgAAIAqwr1dAACAVYQPAABgFeEDAABYRfgAAABWXXDhY9q0aUpISFBQUJBat26tlStXlts/OztbrVu3VlBQkBo1aqR//vOflir1bd6M8/z585WWlqa6desqPDxcHTp00Mcff2yxWt/l7fpc7LPPPpO/v79atmxZvQVeILwd58LCQo0bN07x8fFyOp1q3LixXn31VUvV+jZvx/qNN97QFVdcoZCQEMXExOj222/XgQMHLFXrez799FP16tVLsbGxcjgceu+99876mvPyO2guIG+++aYJCAgwL730ktm4caN58MEHTWhoqNm+fXup/bds2WJCQkLMgw8+aDZu3GheeuklExAQYN555x3LlfsWb8f5wQcfNE8++aT58ssvzebNm82YMWNMQECA+eqrryxX7lu8Hediv/zyi2nUqJFJT083V1xxhZ1ifVhlxvm6664z7dq1M1lZWWbr1q3miy++MJ999pnFqn2Tt2O9cuVKU6NGDfPcc8+ZLVu2mJUrV5rLL7/c9O7d23LlvuPDDz8048aNM++++66RZBYsWFBu//P1O3hBhY+rrrrK3HPPPR5tl156qXn44YdL7T9q1Chz6aWXerTdfffdpn379tVW44XA23EuzWWXXWYmTpxY1aVdUCo7zjfeeKN55JFHzIQJEwgfFeDtOH/00UcmIiLCHDhwwEZ5FxRvx/qpp54yjRo18mh7/vnnTf369autxgtJRcLH+fodvGB2uxw/flxr165Venq6R3t6erpWr15d6mtycnJK9L/mmmu0Zs0a/fbbb9VWqy+rzDifqaioSAUFBapdu3Z1lHhBqOw4z5w5U//5z380YcKE6i7xglCZcV64cKHatGmjzMxMXXzxxWrSpIlGjhypo0eP2ijZZ1VmrJOTk7Vr1y59+OGHMsZo7969euedd9SzZ08bJf8hnK/fwWq7q61t+/fv18mTJ0vcvC4qKqrETe6K5eXlldr/xIkT2r9/v2JiYqqtXl9VmXE+0zPPPKPDhw/rhhtuqI4SLwiVGecffvhBDz/8sFauXCl//wvmT7taVWact2zZolWrVikoKEgLFizQ/v37NXToUP38888c91GOyox1cnKy3njjDd144406duyYTpw4oeuuu04vvPCCjZL/EM7X7+AFs+WjmMPh8HhsjCnRdrb+pbXDk7fjXGzu3LlyuVyaN2+e6tWrV13lXTAqOs4nT55U//79NXHiRDVp0sRWeRcMb9bnoqIiORwOvfHGG7rqqqvUo0cPTZ48WbNmzWLrRwV4M9YbN27UAw88oEcffVRr167V4sWLtXXrVu4TVsXOx+/gBfPfozp16sjPz69Egt63b1+JVFcsOjq61P7+/v6KjIystlp9WWXGudi8efN0xx136O2331a3bt2qs0yf5+04FxQUaM2aNVq3bp3uu+8+Sad+JI0x8vf315IlS9S1a1crtfuSyqzPMTExuvjiiz1uHd6sWTMZY7Rr1y4lJiZWa82+qjJjPWnSJHXs2FF///vfJUlJSUkKDQ1Vp06d9Pjjj7N1ugqcr9/BC2bLR2BgoFq3bq2srCyP9qysLCUnJ5f6mg4dOpTov2TJErVp00YBAQHVVqsvq8w4S6e2eAwaNEhz5sxhf20FeDvO4eHh+vbbb7V+/Xr3dM8996hp06Zav3692rVrZ6t0n1KZ9bljx47avXu3fv31V3fb5s2bVaNGDdWvX79a6/VllRnrI0eOqEYNz58pPz8/Sf/3v3Ocm/P2O1ith7NaVnwa1yuvvGI2btxohg8fbkJDQ822bduMMcY8/PDDZsCAAe7+xacYPfTQQ2bjxo3mlVde4VTbCvB2nOfMmWP8/f3Niy++aPbs2eOefvnll/P1FnyCt+N8Js52qRhvx7mgoMDUr1/f9OvXz2zYsMFkZ2ebxMREM2TIkPP1FnyGt2M9c+ZM4+/vb6ZNm2b+85//mFWrVpk2bdqYq6666ny9hd+9goICs27dOrNu3TojyUyePNmsW7fOfTrz7+V38IIKH8YY8+KLL5r4+HgTGBhorrzySpOdne1+buDAgSYlJcWj/4oVK0yrVq1MYGCgadiwoZk+fbrlin2TN+OckpJiJJWYBg4caL9wH+Pt+nw6wkfFeTvO33//venWrZsJDg429evXNyNGjDBHjhyxXLVv8nasn3/+eXPZZZeZ4OBgExMTY2655Raza9cuy1X7jk8++aTc79vfy++gwxi2XQEAAHsumGM+AACAbyB8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsOr/AejmQumx7IhfAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "plt.hist(slice_label.view(-1).numpy(),bins = 5);\n", + "plt.title(\"Distribution of slices with and without tumour \\n 0 = no tumour, 1 = tumour\");" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97cbfc78-54b5-4a98-b0b3-60e3a71fd25e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "jupytext": { + "formats": "py:percent,ipynb" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + }, + "vscode": { + "interpreter": { + "hash": "a7e6f8385898884a13cbe220eefefb32cba5012927a94186742ddc14746e4dba" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py new file mode 100644 index 00000000..db954dba --- /dev/null +++ b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py @@ -0,0 +1,201 @@ +# --- +# jupyter: +# jupytext: +# formats: py:percent,ipynb +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.14.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# %% + +# # Diff-SCM +# +# This tutorial illustrates how to load the 2D BRATS dataset. +# +# +# ## Setup environment + +# %% + + +get_ipython().system('python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]"') +get_ipython().system('python -c "import matplotlib" || pip install -q matplotlib') +get_ipython().run_line_magic('matplotlib', 'inline') +print('done') + + +# ## Setup imports + +# %% + + +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import shutil +import tempfile +import time + +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from monai import transforms +from monai.apps import DecathlonDataset +from monai.config import print_config +from monai.data import CacheDataset, DataLoader +from monai.utils import first, set_determinism +from torch.cuda.amp import GradScaler, autocast +from tqdm import tqdm + +from generative.inferers import DiffusionInferer + +# TODO: Add right import reference after deployed +from generative.networks.nets import DiffusionModelUNet +from generative.networks.schedulers import DDPMScheduler + +print_config() + + +# ## Setup data directory + +# %% + + +directory = os.environ.get("MONAI_DATA_DIRECTORY") +root_dir = tempfile.mkdtemp() if directory is None else directory +print(root_dir) +root_dir= '/tmp/tmp6o69ziv1' + + +# ## Set deterministic training for reproducibility + +# %% + + +set_determinism(42) + + +# ## Setup MedNIST Dataset and training and validation dataloaders +# In this tutorial, we will train our models on the MedNIST dataset available on MONAI +# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset). +# Here, we will use the "Hand" and "HeadCT", where our conditioning variable `class` will specify the modality. + +# %% + + +batch_size = 2 +channel = 0 # 0 = Flair +assert channel in [0, 1, 2, 3], "Choose a valid channel" + +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image","label"]), + transforms.EnsureChannelFirstd(keys=["image","label"]), + transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), + transforms.AddChanneld(keys=["image"]), + transforms.EnsureTyped(keys=["image","label"]), + transforms.Orientationd(keys=["image","label"], axcodes="RAS"), + transforms.Spacingd( + keys=["image","label"], + pixdim=(3.0, 3.0, 2.0), + mode=("bilinear", "nearest"), + ), + transforms.CenterSpatialCropd(keys=["image","label"], roi_size=(64, 64, 64)), + transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), + transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), + transforms.Lambdad(keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()), + ] +) +train_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="training", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=True, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) +nb_3D_images_to_mix = 2 +train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4) +print(f'Image shape {train_ds[0]["image"].shape}') + + +# %% + + +from typing import Dict +def get_batched_2d_axial_slices(data : Dict): + images_3D = data['image'] + batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1]) + slice_label = data['slice_label'] + #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float() + slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze() + return batched_2d_slices, slice_label + + +# ### Visualisation of the training images + +# %% + + +check_data = first(train_loader_3D) +print('check_data', check_data["image"].shape, check_data["slice_label"].shape) + + +# %% + + +batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data) +idx = list(torch.randperm(batched_2d_slices.shape[0])) +print('idx', idx, len(idx)) +slices = [0,30,45,63] +print(f"Batch shape: {batched_2d_slices.shape}") +print(f"Slices class: {slice_label[idx][slices].view(-1)}") +image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze() +plt.figure("training images", (12, 6)) +plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + + +# %% + + +slice_label.shape + + +# ## Check Distribution of Healthy / Unhealthy + +# %% + +subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))# +a,b = next(subset_2D) #what is a, what is b? +a.shape, b.shape + + +# %% + + +plt.hist(slice_label.view(-1).numpy(),bins = 5); +plt.title("Distribution of slices with and without tumour \n 0 = no tumour, 1 = tumour"); + + +# %% From cc7b704b2165504b5a05cd2293859ebace48c1b6 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 22 Feb 2023 17:31:22 +0100 Subject: [PATCH 03/23] anomaly detection tutorial is complete, the training needs to be checked --- .../networks/nets/diffusion_model_unet.py | 23 +- ...r_guidance_anomalydetection_tutorial.ipynb | 1836 ++++++----------- ...fier_guidance_anomalydetection_tutorial.py | 395 ++-- 3 files changed, 882 insertions(+), 1372 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 506b3010..729c0bd0 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1761,7 +1761,7 @@ def __init__( for i in range(len(num_channels)): input_channel = output_channel output_channel = num_channels[i] - is_final_block = i == len(num_channels) - 1 + is_final_block = i == len(num_channels) #- 1 down_block = get_down_block( spatial_dims=spatial_dims, @@ -1825,7 +1825,15 @@ def __init__( ) self.up_blocks.append(up_block) - self.out = nn.Linear(16384, self.out_channels) + # self.out = nn.Linear(4096, self.out_channels) + self.out = nn.Sequential( + nn.Linear(8192, 512), + nn.ReLU(), + nn.Dropout(0.2), + nn.Linear(512, self.out_channels), + #nn.Sigmoid(), + ) + # out # self.out = nn.Sequential( # nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels[0], eps=norm_eps, affine=True), @@ -1877,14 +1885,15 @@ def forward( raise ValueError("model should have with_conditioning = True if context is provided") down_block_res_samples: List[torch.Tensor] = [h] for downsample_block in self.down_blocks: - h, res_samples = downsample_block(hidden_states=h, temb=emb, context=context) - for residual in res_samples: - down_block_res_samples.append(residual) - h=h.reshape(h.shape[0] ,-1) + h, _ = downsample_block(hidden_states=h, temb=emb, context=context) + # for residual in res_samples: + # down_block_res_samples.append(residual) + # # 5. mid - # h = self.middle_block(hidden_states=h, temb=emb, context=context) + + h = h.reshape(h.shape[0], -1) # # # 6. up # for upsample_block in self.up_blocks: diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb index 248180d7..fbeaf69c 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "id": "75f2d5f3", "metadata": {}, "outputs": [ @@ -145,8 +145,7 @@ "!python /home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/setup.py install\n", "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", "!python -c \"import matplotlib\" || pip install -q matplotlib\n", - "!python -c \"import seaborn\" || pip install -q seaborn\n", - "%matplotlib inline" + "!python -c \"import seaborn\" || pip install -q seaborn" ] }, { @@ -159,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "972ed3f3", "metadata": { "collapsed": false, @@ -169,17 +168,44 @@ }, "outputs": [ { - "ename": "ZipImportError", - "evalue": "bad local file header: '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mZipImportError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[4], line 29\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtorch\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcuda\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mamp\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m GradScaler, autocast\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mtqdm\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m tqdm\n\u001b[0;32m---> 29\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mgenerative\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01minferers\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DiffusionInferer\n\u001b[1;32m 31\u001b[0m \u001b[38;5;66;03m# TODO: Add right import reference after deployed\u001b[39;00m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mgenerative\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnetworks\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mnets\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdiffusion_model_unet\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DiffusionModelUNet, DiffusionModelEncoder\n", - "File \u001b[0;32m:196\u001b[0m, in \u001b[0;36mget_code\u001b[0;34m(self, fullname)\u001b[0m\n", - "File \u001b[0;32m:752\u001b[0m, in \u001b[0;36m_get_module_code\u001b[0;34m(self, fullname)\u001b[0m\n", - "File \u001b[0;32m:598\u001b[0m, in \u001b[0;36m_get_data\u001b[0;34m(archive, toc_entry)\u001b[0m\n", - "\u001b[0;31mZipImportError\u001b[0m: bad local file header: '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg'" + "name": "stderr", + "output_type": "stream", + "text": [ + "Setting up a new session...\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/generative/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", + "MONAI version: 1.1.dev2248\n", + "Numpy version: 1.23.2\n", + "Pytorch version: 1.12.1\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 3400bd91422ccba9ccc3aa2ffe7fecd4eb5596bf\n", + "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Nibabel version: 4.0.1\n", + "scikit-image version: 0.19.3\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n", + "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", + "TorchVision version: 0.13.1\n", + "tqdm version: 4.64.1\n", + "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", + "psutil version: 5.9.4\n", + "pandas version: 1.5.3\n", + "einops version: 0.6.0\n", + "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", + "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", + "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" ] } ], @@ -198,9 +224,10 @@ "import shutil\n", "import tempfile\n", "import time\n", + "from typing import Dict\n", "import os\n", + "import torch.nn as nn\n", "import matplotlib.pyplot as plt\n", - "import seaborn\n", "import numpy as np\n", "import torch\n", "import torch.nn.functional as F\n", @@ -211,9 +238,14 @@ "from monai.utils import first, set_determinism\n", "from torch.cuda.amp import GradScaler, autocast\n", "from tqdm import tqdm\n", + "torch.multiprocessing.set_sharing_strategy('file_system')\n", + "import sys\n", + "sys.path.append('/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/')\n", + "print('path', sys.path)\n", "\n", "from generative.inferers import DiffusionInferer\n", "\n", + "\n", "# TODO: Add right import reference after deployed\n", "from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder\n", "\n", @@ -221,7 +253,18 @@ "from generative.networks.schedulers.ddim import DDIMScheduler\n", "print_config()\n", "\n", - "\n" + "losstrain_window = viz.line(Y=torch.zeros((1)).cpu(), X=torch.zeros((1)).cpu(),\n", + " opts=dict(xlabel='epoch', ylabel='Loss', title='training loss'))\n", + "lossval_window = viz.line(Y=torch.zeros((1)).cpu(), X=torch.zeros((1)).cpu(),\n", + " opts=dict(xlabel='epoch', ylabel='Loss', title='val loss '))\n", + "\n", + "train_classifier=False\n", + "train_diffusionmodel=False\n", + "def visualize(img):\n", + " _min = img.min()\n", + " _max = img.max()\n", + " normalized_img = (img - _min)/ (_max - _min)\n", + " return normalized_img" ] }, { @@ -234,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 4, "id": "8b4323e7", "metadata": { "collapsed": false, @@ -242,21 +285,11 @@ "outputs_hidden": false } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/juliawolleb/PycharmProjects/MONAI/data_brats\n" - ] - } - ], + "outputs": [], "source": [ "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", "#root_dir = tempfile.mkdtemp() if directory is None else directory\n", - "root_dir='/home/juliawolleb/PycharmProjects/MONAI/data_brats'\n", - "\n", - "print(root_dir)" + "root_dir='/home/juliawolleb/PycharmProjects/MONAI/brats' #path to where the data is stored" ] }, { @@ -269,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 5, "id": "34ea510f", "metadata": { "collapsed": false, @@ -279,13 +312,15 @@ }, "outputs": [], "source": [ - "set_determinism(42)" + "set_determinism(36)" ] }, { "cell_type": "markdown", - "id": "fac55e9d", - "metadata": {}, + "id": "c3f70dd1-236a-47ff-a244-575729ad92ba", + "metadata": { + "tags": [] + }, "source": [ "## Setup BRATS Dataset for 2D slices and training and validation dataloaders\n", "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150" @@ -293,7 +328,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "id": "da1927b0", "metadata": { "collapsed": false, @@ -306,14 +341,25 @@ "name": "stderr", "output_type": "stream", "text": [ - "Task01_BrainTumour.tar: 71%|█████████▉ | 5.03G/7.09G [04:43<01:46, 20.8MB/s]" + "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/utils/deprecate_utils.py:107: FutureWarning: : Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n", + " warn_deprecated(obj, msg, warning_category)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "download training set\n", + "len train data 388\n", + "total slices torch.Size([17072, 1, 64, 64])\n", + "total lbaels torch.Size([17072])\n", + "download val set\n" ] } ], "source": [ "\n", "\n", - "batch_size = 2\n", "channel = 0 # 0 = Flair\n", "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", "\n", @@ -336,59 +382,114 @@ " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", " ]\n", ")\n", + "print('download training set')\n", "train_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", " task=\"Task01_BrainTumour\",\n", " section=\"training\", # validation\n", " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", " seed=0,\n", " transform=train_transforms,\n", ")\n", - "nb_3D_images_to_mix = 2\n", - "train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", - "print(f'Image shape {train_ds[0][\"image\"].shape}')\n", + "print('len train data', len(train_ds))\n", "\n", + "def get_batched_2d_axial_slices(data : Dict):\n", + " images_3D = data['image']\n", + " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", + " slice_label = data['slice_label']\n", + " slice_label = torch.cat(slice_label.split(1, dim = -1)[10:-10],0).squeeze()\n", + " return batched_2d_slices, slice_label\n", "\n", + "preprocessing_train=False\n", + "if preprocessing_train == True:\n", + " train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", + " print(f'Image shape {train_ds[0][\"image\"].shape}')\n", "\n", + " data_2d_slices=[]\n", + " data_slice_label = []\n", + " check_data = first(train_loader_3D)\n", + " for i, data in enumerate(train_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices.append(b2d)\n", + " data_slice_label.append(slice_label2d)\n", + " total_train_slices=torch.cat(data_2d_slices,0)\n", + " total_train_labels=torch.cat(data_slice_label,0)\n", "\n", + " torch.save(total_train_slices, 'total_train_slices.pt')\n", + " torch.save(total_train_labels, 'total_train_labels.pt')\n", "\n", + "else:\n", + " total_train_slices=torch.load('total_train_slices.pt')\n", + " total_train_labels=torch.load('total_train_labels.pt')\n", + " print('total slices', total_train_slices.shape)\n", + " print('total lbaels', total_train_labels.shape)\n", "\n" ] }, + { + "cell_type": "markdown", + "id": "fac55e9d", + "metadata": { + "tags": [] + }, + "source": [ + "## Setup BRATS Dataset for 2D slices validation dataloader\n", + "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150" + ] + }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 7, "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", - "metadata": {}, + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2023-02-02 10:39:45,467 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-02-02 10:39:45,469 - INFO - File exists: /tmp/tmpyurp7egh/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-02-02 10:39:45,471 - INFO - Non-empty folder exists in /tmp/tmpyurp7egh/Task01_BrainTumour, skipped extracting.\n", - "Image shape torch.Size([1, 64, 64, 64])\n" + "total slices torch.Size([4224, 1, 64, 64])\n", + "total lbaels torch.Size([4224])\n" ] } ], "source": [ - "\n", "val_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", " task=\"Task01_BrainTumour\",\n", " section=\"validation\", # validation\n", " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", " seed=0,\n", " transform=train_transforms,\n", ")\n", - "val_loader_3D = DataLoader(val_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", - "print(f'Image shape {val_ds[0][\"image\"].shape}')\n", - "\n" + "\n", + "\n", + "preprocessing_val=False\n", + "if preprocessing_val == True:\n", + " val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", + " print(f'Image shape {val_ds[0][\"image\"].shape}')\n", + " print('len val data', len(val_ds))\n", + " data_2d_slices_val=[]\n", + " data_slice_label_val = []\n", + " for i, data in enumerate(val_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices_val.append(b2d)\n", + " data_slice_label_val.append(slice_label2d)\n", + " total_val_slices=torch.cat(data_2d_slices_val,0)\n", + " total_val_labels=torch.cat(data_slice_label_val,0)\n", + " torch.save(total_val_slices, 'total_val_slices.pt')\n", + " torch.save(total_val_labels, 'total_val_labels.pt')\n", + "\n", + "else:\n", + " total_val_slices=torch.load('total_val_slices.pt')\n", + " total_val_labels=torch.load('total_val_labels.pt')\n", + " print('total slices', total_val_slices.shape)\n", + " print('total lbaels', total_val_labels.shape)" ] }, { @@ -405,94 +506,6 @@ "\n" ] }, - { - "cell_type": "markdown", - "id": "7f108ebb", - "metadata": {}, - "source": [ - "### Visualisation of the training images" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "4105a01f", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Batch shape: torch.Size([128, 1, 64, 64])\n", - "Slices class: tensor([0., 0., 0., 1.])\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "from typing import Dict\n", - "def get_batched_2d_axial_slices(data : Dict):\n", - " images_3D = data['image']\n", - " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1])\n", - " slice_label = data['slice_label']\n", - " #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float()\n", - " slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze()\n", - " return batched_2d_slices, slice_label\n", - "\n", - "check_data = first(train_loader_3D)\n", - "batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data)\n", - "idx = list(torch.randperm(batched_2d_slices.shape[0]))\n", - "slices = [0,30,45,63]\n", - "print(f\"Batch shape: {batched_2d_slices.shape}\")\n", - "print(f\"Slices class: {slice_label[idx][slices].view(-1)}\")\n", - "image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze()\n", - "plt.figure(\"training images\", (12, 6))\n", - "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.axis(\"off\")\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "4249e4be-f7e7-48e9-9aa9-436da8c1d1e5", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(torch.Size([2, 1, 64, 64]), torch.Size([2]))" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))#\n", - "a,b = next(subset_2D) #what is a, what is b? Are these the next images? \n", - "a.shape, b.shape" - ] - }, { "cell_type": "markdown", "id": "08428bc6", @@ -501,19 +514,12 @@ "### Define network, scheduler, optimizer, and inferer\n", "At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", "the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms\n", - "in the 3rd level, each with 1 attention head (`num_head_channels=64`).\n", - "\n", - "In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use:\n", - "\n", - "`\n", - "with_conditioning=True,\n", - "cross_attention_dim=1,\n", - "`" + "in the 3rd level, each with 1 attention head (`num_head_channels=64`).\n" ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 8, "id": "bee5913e", "metadata": { "collapsed": false, @@ -539,7 +545,7 @@ ")\n", "model.to(device)\n", "\n", - "scheduler = DDPMScheduler(\n", + "scheduler = DDIMScheduler(\n", " num_train_timesteps=1000,\n", ")\n", "\n", @@ -561,13 +567,158 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 9, "id": "6c0ed909", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false - }, + } + }, + "outputs": [], + "source": [ + "n_epochs =75\n", + "batch_size=32\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "train_diffusionmodel=False\n", + "if train_diffusionmodel==False:\n", + " model.load_state_dict(torch.load(\"./diffusion_model.pt\", map_location={'cuda:0': 'cpu'}))\n", + "else:\n", + " scaler = GradScaler()\n", + " total_start = time.time()\n", + " for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0])) #shuffle training data new\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + "\n", + " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", + "\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes), ncols=10)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a,b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning\n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + " if step%20==0:\n", + " print('step', step, loss)\n", + "\n", + " epoch_loss += loss.item()\n", + "\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"loss\": epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + "\n", + " if (epoch) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + "\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + " total_time = time.time() - total_start\n", + " torch.save(model.state_dict(), \"./diffusion_model.pt\") #save the trained model\n", + "\n", + " print(f\"train diffusion completed, total time: {total_time}.\")\n", + "\n", + " plt.style.use(\"seaborn-bright\")\n", + " plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", + " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + " plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + " plt.yticks(fontsize=12)\n", + " plt.xticks(fontsize=12)\n", + " plt.xlabel(\"Epochs\", fontsize=16)\n", + " plt.ylabel(\"Loss\", fontsize=16)\n", + " plt.legend(prop={\"size\": 14})\n", + " plt.show()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", + "metadata": {}, + "source": [ + "### Model training of the Classification Model\n", + "#First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n", + "#Here, we are training our binary classification model for 20 epochs." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "44cc6928-2525-4e61-8805-15b409097bbb", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "\n", + "\n", + "classifier = DiffusionModelEncoder(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=2,\n", + " num_channels=(32,64,128),\n", + " attention_levels=(False, True, True),\n", + " num_res_blocks=1,\n", + " num_head_channels=64,\n", + " with_conditioning=False,\n", + ")\n", + "classifier.to(device)\n", + "batch_size=32" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", + "metadata": { "lines_to_next_cell": 0 }, "outputs": [ @@ -575,728 +726,347 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 1%| | 1/128 [00:00<00:16, 7.89it/s, loss=0.982]" + "Epoch 0: : 534it [00:29, 18.32it/s, loss=0.576] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 0 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 1 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 2%|▍ | 3/128 [00:00<00:13, 9.55it/s, loss=1]" + "Epoch 0: : 132it [00:02, 56.95it/s, val_loss=0.305]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 2 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 3 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 4 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" + "final step val 131\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 5%|▋ | 7/128 [00:00<00:11, 10.26it/s, loss=0.992]" + "Epoch 1: : 534it [00:29, 18.34it/s, loss=0.576] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 5 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 6 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 7 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 7%|▊ | 9/128 [00:00<00:11, 10.38it/s, loss=0.988]" + "Epoch 1: : 132it [00:02, 56.37it/s, val_loss=0.315]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 8 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 9 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 10 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" + "final step val 131\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 10%|█ | 13/128 [00:01<00:10, 10.52it/s, loss=0.981]" + "Epoch 2: : 534it [00:31, 16.99it/s, loss=0.573] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 11 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 12 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 13 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 12%|█▎ | 15/128 [00:01<00:10, 10.51it/s, loss=0.978]" + "Epoch 2: : 132it [00:02, 52.98it/s, val_loss=0.312]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 14 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 15 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 16 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" + "final step val 131\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 15%|█▋ | 19/128 [00:01<00:10, 10.65it/s, loss=0.971]" + "Epoch 3: : 534it [00:31, 17.21it/s, loss=0.572] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 17 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 18 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 19 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 16%|█▊ | 21/128 [00:02<00:10, 10.22it/s, loss=0.968]" + "Epoch 3: : 132it [00:02, 53.04it/s, val_loss=0.334]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 20 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 21 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" + "final step val 131\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 18%|█▉ | 23/128 [00:02<00:10, 9.82it/s, loss=0.964]" + "Epoch 4: : 534it [00:31, 17.16it/s, loss=0.569] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 22 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 23 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: 20%|██▏ | 25/128 [00:02<00:10, 9.50it/s, loss=0.961]" + "Epoch 4: : 132it [00:02, 52.93it/s, val_loss=0.36] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "step 24 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 25 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 21%|██▎ | 27/128 [00:02<00:10, 9.34it/s, loss=0.956]" + "final step val 131\n", + "train completed, total time: 167.07577848434448.\n", + "epl 5\n" ] }, { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 26 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([1., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([1., 1.], device='cuda:0')\n", - "step 27 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 1.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 1.], device='cuda:0')\n" - ] - }, + "data": { + "image/png": "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aDk4AUCXLl0QHByMzZs3W9wHERER1Q82H55Onz6Ntm3bmoQaAAgPD5e3V2bw4MFQq9Xw9vZGbGxsmX1Kvi9p88F+qtIHERERPRxs/rJdRkYGgoKCypR7e3vL28vTpEkTTJ8+HVFRUfDw8MCpU6ewYMECREVF4cCBA4iIiDBpo6TNB/upqI/CwkIUFhbK3+fk5FRtYkRERGSXbD48AYAkSdXaNmDAAAwYMED+vnv37hg0aBDCwsIwc+ZMbNmypUptVdTH/PnzMWfOnHK3ExERUf1i85ftfHx8zJ75yczMBGD+bFFFWrRogW7duiEpKcmkD8D8WazMzMwK+5g2bRqys7PlT3JysqLxEBERkX2x+fAUFhaGc+fOQa/Xm5SfOnUKQPGddEoJIaBS/TH1kjZK2nywn4r6cHR0hIeHh8mHiIiI6i+bD09Dhw5FXl4eNm7caFIeHx8PPz8/dO7cWVF7V65cwYEDBxAVFSWXNWvWDJ06dcLq1athMBjk8qSkJJw/fx6xsbGWTYKIiIjqDZtf8zRw4ED07dsXkydPRk5ODlq3bo2EhARs27YNq1evlp/xNGHCBMTHx+PSpUsIDAwEAPTp0wfdu3dHeHi4vGB84cKFkCQJ7777rkk/7733Hvr27Ythw4bhxRdfRFpaGt58802EhoZi3LhxtT5vIiIisk02H54AYNOmTZg+fTpmzpyJzMxMhISEICEhASNGjJDrGAwGGAwGCCHksrCwMKxfvx4ffPAB8vPz4evri169euHtt99GcHCwSR89e/ZEYmIiZs6ciSFDhsDFxQWDBw/G+++/z6eLExERkUwSpdMGWSwnJweenp7Izs7m+iciIiI7oeTvt82veSIiIiKyJQxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQIMT0REREQKMDwRERERKcDwRERERKQAwxMRERGRAgxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQIMT0REREQKMDwRERERKcDwRERERKQAwxMRERGRAgxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQKauh4AERHVTzqdDgaDoa6H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szRs2LDMXXel9erVy1pdEREREdUZq7/bLj8/H5cuXUJubi7c3d3RqlUrODs7W7sbIiIiojphtfeObN++HT179oSnpyciIiLQrVs3REREwNPTE7169cKOHTus1RUREdFDZ/bs2ZAkSX4cUAlJksy+y1VpO9Y0duxYSJKEq1ev1lgfdckq4Wn27NmIiYnB3r17odfrodVq4efnB61WC71ej927d2PgwIGYPXu2NbojIiKyOXFxcZAkCevWrauwXkZGBhwdHdGwYUMUFRXV0uisa9WqVZAkCatWrarrodQJi8PTtm3b8M4770ClUuHFF1/E+fPnUVBQgOTkZBQUFOD8+fN48cUX5Rfvbt++3RrjJiIisikTJkwA8Mfd5eVZvXo1ioqKMHr0aJNXm1XXuXPn8J///Mfidqxp/vz5OHfuHJo1a1bXQ6kRFoenf/3rX5AkCStWrMBHH32ENm3amGxv06YNPvroI6xYsQJCCCxZssTSLomIiGxO79690aJFC+zcuRPJycnl1isJVyVhy1IhISFo3ry5VdqylqZNmyIkJARarbauh1IjLA5PR44cgb+/P0aPHl1hvVGjRiEgIACHDx+2tEsiIiKbI0kSxo0bB6PRiPj4eLN1fv75Z5w4cQKdOnWCt7c3Zs2ahaioKPj6+sLR0REtWrTAiy++iLS0NEX9mlvzlJycjLi4OHh7e8PNzQ09evTA3r17zbZRVFSEpUuXon///ggICICjoyN8fX0RGxuLX375xaTu2LFjMW7cOADAuHHjIEmS/Cldp7w1T/Hx8YiKioKbmxvc3NwQFRVl9ue1e/duSJKE2bNn49ixY+jfvz/c3d3h6emJoUOH1ul6KovDU25ubpVfudK4cWPcvXvX0i6JiIhs0rhx46BSqbBq1SoIIcpsL33Wae/evVi0aBEaN26MuLg4vPLKK2jVqhU++eQTREdHIzs7u9rj+P333xEdHY1169ahU6dO+Nvf/gZvb2/07dtXfp1aaZmZmZg6dSoKCwsRExOD//f//h969uyJxMREdOnSBUeOHJHrPv3003jqqacAAE899RRmzZolfyrz//7f/8PYsWNx48YNTJgwARMnTkRKSgrGjh2Lv//972b3OXr0KJ544gloNBq88MIL6NixI/773/+iT58+KCgoqOZPyELCQi1bthTu7u4iLy+vwnp5eXnCzc1NtGzZ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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_epochs = 5\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", + "\n", + "classifier.to(device)\n", + "\n", + "train_classifier=False\n", + "if train_classifier==False:\n", + " classifier.load_state_dict(torch.load(\"./classifier5.pt\", map_location={'cuda:0': 'cpu'}))\n", + "else:\n", + "\n", + " scaler = GradScaler()\n", + " total_start = time.time()\n", + " for epoch in range(n_epochs):\n", + " classifier.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + "\n", + " for step, (a,b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset\n", + " optimizer_cls.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=False):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image\n", + " pred=classifier(noisy_img, timesteps)\n", + " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", + "\n", + " loss.backward()\n", + " optimizer_cls.step()\n", + "\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"loss\": epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + " print('final step train', step)\n", + "\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " classifier.eval()\n", + " val_epoch_loss = 0\n", + " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", + " for step, (a,b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise\n", + "\n", + " with torch.no_grad():\n", + " with autocast(enabled=False):\n", + " noise = torch.randn_like(images).to(device)\n", + " pred = classifier(images, timesteps)\n", + " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " _, predicted = torch.max(pred, 1);\n", + " progress_bar_val.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + " print('final step val', step)\n", + "\n", + "\n", + " total_time = time.time() - total_start\n", + " print(f\"train completed, total time: {total_time}.\")\n", + " torch.save(classifier.state_dict(), \"./classifier5.pt\")\n", + " \n", + " ## Learning curves for the Classifier\n", + " \n", + " plt.style.use(\"seaborn-bright\")\n", + " plt.title(\"Learning Curves\", fontsize=20)\n", + " print('epl', len(epoch_loss_list))\n", + " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + " plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + " plt.yticks(fontsize=12)\n", + " plt.xticks(fontsize=12)\n", + " plt.xlabel(\"Epochs\", fontsize=16)\n", + " plt.ylabel(\"Loss\", fontsize=16)\n", + " plt.legend(prop={\"size\": 14})\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "### For Image-to-Image Translation to a Healthy Subject, we pick a disesed subject of the validation set" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", + "metadata": {}, + "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 23%|██▍ | 29/128 [00:02<00:10, 9.18it/s, loss=0.953]" - ] + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 28 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 29 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 24%|██▋ | 31/128 [00:03<00:10, 9.21it/s, loss=0.949]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 30 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 31 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 26%|██▊ | 33/128 [00:03<00:10, 9.20it/s, loss=0.944]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 32 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 33 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 27%|███▎ | 35/128 [00:03<00:10, 9.12it/s, loss=0.94]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 34 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 35 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 29%|███▏ | 37/128 [00:03<00:10, 9.07it/s, loss=0.934]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 36 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 37 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 30%|███▎ | 39/128 [00:04<00:09, 9.09it/s, loss=0.929]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 38 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 39 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 32%|███▌ | 41/128 [00:04<00:09, 9.15it/s, loss=0.925]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 40 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 41 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 34%|███▋ | 43/128 [00:04<00:09, 9.17it/s, loss=0.921]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 42 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 43 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 35%|███▊ | 45/128 [00:04<00:09, 9.15it/s, loss=0.916]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 44 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 45 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 37%|████ | 47/128 [00:04<00:09, 8.95it/s, loss=0.912]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 46 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 47 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 38%|████▏ | 49/128 [00:05<00:08, 9.00it/s, loss=0.906]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 48 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 49 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 40%|████▍ | 51/128 [00:05<00:08, 9.08it/s, loss=0.904]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 50 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 51 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 41%|████▌ | 53/128 [00:05<00:08, 9.06it/s, loss=0.899]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 52 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 53 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 43%|████▋ | 55/128 [00:05<00:07, 9.18it/s, loss=0.894]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 54 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 55 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 45%|████▉ | 57/128 [00:05<00:07, 9.18it/s, loss=0.889]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 56 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 57 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 46%|█████ | 59/128 [00:06<00:07, 9.22it/s, loss=0.884]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 58 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 59 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 48%|█████▎ | 62/128 [00:06<00:06, 10.20it/s, loss=0.877]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "step 60 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 61 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n", - "step 62 torch.Size([2, 1, 64, 64]) torch.Size([2]) tensor([0., 0.])\n", - "image torch.Size([2, 1, 64, 64])\n", - "classes tensor([0., 0.], device='cuda:0')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 49%|█████▍ | 63/128 [00:06<00:06, 9.58it/s, loss=0.874]\n", - "Epoch 1: 0%| | 0/128 [00:00)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7611, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7668, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7561, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7131, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.6271, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.6277, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.6392, device='cuda:0', grad_fn=)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 9%|██▏ | 12/128 [00:00<00:03, 35.77it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.6372, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7021, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7493, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7826, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7407, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7278, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7590, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7495, device='cuda:0', grad_fn=)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: 17%|███▉ | 22/128 [00:00<00:03, 35.29it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7754, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7786, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7738, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7787, device='cuda:0', grad_fn=)\n", - "h torch.Size([6, 64, 16, 16]) 6\n", - "h torch.Size([6, 16384])\n", - "loss tensor(0.7888, device='cuda:0', grad_fn=)\n", - "h torch.Size([2, 64, 16, 16]) 2\n", - "h torch.Size([2, 16384])\n", - "loss tensor(0.7906, device='cuda:0', grad_fn=)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 1: 0%| | 0/128 [00:00 3\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlinspace\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_epochs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_epochs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepoch_loss_list\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mC0\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlinewidth\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m2.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlabel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mTrain\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m plt\u001b[38;5;241m.\u001b[39mplot(\n\u001b[1;32m 5\u001b[0m np\u001b[38;5;241m.\u001b[39mlinspace(val_interval, n_epochs, \u001b[38;5;28mint\u001b[39m(n_epochs \u001b[38;5;241m/\u001b[39m val_interval)),\n\u001b[1;32m 6\u001b[0m val_epoch_loss_list,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 9\u001b[0m label\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mValidation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 10\u001b[0m )\n\u001b[1;32m 11\u001b[0m plt\u001b[38;5;241m.\u001b[39myticks(fontsize\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m12\u001b[39m)\n", - "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/pyplot.py:2767\u001b[0m, in \u001b[0;36mplot\u001b[0;34m(scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2765\u001b[0m \u001b[38;5;129m@_copy_docstring_and_deprecators\u001b[39m(Axes\u001b[38;5;241m.\u001b[39mplot)\n\u001b[1;32m 2766\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mplot\u001b[39m(\u001b[38;5;241m*\u001b[39margs, scalex\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, scaley\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m-> 2767\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgca\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2768\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscalex\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscalex\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscaley\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscaley\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2769\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mdata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m}\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_axes.py:1635\u001b[0m, in \u001b[0;36mAxes.plot\u001b[0;34m(self, scalex, scaley, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1393\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1394\u001b[0m \u001b[38;5;124;03mPlot y versus x as lines and/or markers.\u001b[39;00m\n\u001b[1;32m 1395\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1632\u001b[0m \u001b[38;5;124;03m(``'green'``) or hex strings (``'#008000'``).\u001b[39;00m\n\u001b[1;32m 1633\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1634\u001b[0m kwargs \u001b[38;5;241m=\u001b[39m cbook\u001b[38;5;241m.\u001b[39mnormalize_kwargs(kwargs, mlines\u001b[38;5;241m.\u001b[39mLine2D)\n\u001b[0;32m-> 1635\u001b[0m lines \u001b[38;5;241m=\u001b[39m [\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_lines(\u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39mdata, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)]\n\u001b[1;32m 1636\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m line \u001b[38;5;129;01min\u001b[39;00m lines:\n\u001b[1;32m 1637\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39madd_line(line)\n", - "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_base.py:312\u001b[0m, in \u001b[0;36m_process_plot_var_args.__call__\u001b[0;34m(self, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 310\u001b[0m this \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m args[\u001b[38;5;241m0\u001b[39m],\n\u001b[1;32m 311\u001b[0m args \u001b[38;5;241m=\u001b[39m args[\u001b[38;5;241m1\u001b[39m:]\n\u001b[0;32m--> 312\u001b[0m \u001b[38;5;28;01myield from\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_plot_args\u001b[49m\u001b[43m(\u001b[49m\u001b[43mthis\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/anaconda3/envs/experiment/lib/python3.10/site-packages/matplotlib/axes/_base.py:498\u001b[0m, in \u001b[0;36m_process_plot_var_args._plot_args\u001b[0;34m(self, tup, kwargs, return_kwargs)\u001b[0m\n\u001b[1;32m 495\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes\u001b[38;5;241m.\u001b[39myaxis\u001b[38;5;241m.\u001b[39mupdate_units(y)\n\u001b[1;32m 497\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m!=\u001b[39m y\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m]:\n\u001b[0;32m--> 498\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mx and y must have same first dimension, but \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 499\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhave shapes \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m and \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 500\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m x\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m y\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[1;32m 501\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mx and y can be no greater than 2D, but have \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 502\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mshapes \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m and \u001b[39m\u001b[38;5;132;01m{\u001b[39;00my\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", - "\u001b[0;31mValueError\u001b[0m: x and y must have same first dimension, but have shapes (20,) and (5,)" + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████████████████████████████████████| 100/100 [00:01<00:00, 51.06it/s]\n" ] }, { "data": { - "image/png": "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c/jwYcfl1aXee++9CvcyZWVl6f/+7/8klT03BqhpnOOCeiktLU2rV6++ar877rhDt956q0aMGKEtW7bozTffVEpKijp37qxJkyapf//+atGihc6cOaMff/xRycnJ2rhxo06ePKkxY8Y4ltOsWTMNGTJEH330kRITE3X33Xdr0qRJatOmjU6cOKF3331Xq1evVrt27XTq1KlrPpfDNJMnT9aqVau0d+9evfzyyzp48KAmTZqk4OBgxy3/P/roI/Xs2dNx35DSIOiuRo0a6e2339a9996rr776Sh988IFuueUWjRo1SgMGDFBwcLC8vLyUk5OjXbt26d1333VshK/UvXt39e7dW7t27dLrr7+u8+fPa+zYsbLb7Tpw4IBeeeUVff755+rTp89V7+tTFQ8//LBeffVVnTt3zvFFii1atNCgQYMqHLdq1SpFRkYqOztb8+fP15YtW/TII4/o9ttvl6+vr/Ly8vTdd9/pk08+0aeffqphw4ZpxIgRjvGLFy/WqFGjdM8992jAgAHq1KmT7Ha7fvnlF+3evVtLlixxXBX32GOPVfvnBipUp/ftBarR5bf8r+xj1apVjvEXL160/vSnP1keHh5XHefn52edPXvWaf2ZmZlWmzZtyh3Tpk0ba9++fU5fuHelq906vip9Dx065PLzlnLnSxYr0r9/f0uS1b9/f5fv//TTT9Ytt9xS7s8nOjra+vjjjx2vd+3aVeH6rubcuXPW1KlTLW9v76vOp81msx5++GHr6NGjZZaTkZFhtWzZstyxcXFxbn3JojtKSkqcvi5AFXxlwpUOHz5s/fa3v63U38H48eOdxpbOZUUPDw8P629/+5tbnweoDhwqAv4fDw8PPffcc0pPT9eTTz6p7t276+abb5aHh4duvPFG3XbbbRo1apTefPNNZWdnq3Hjxk7jQ0JCtGfPHs2cOVMdOnSQj4+P7Ha7unbtqrlz5yotLc1xLk1D1KZNG3377beaN2+ewsPD1bhxY910003q3bu3li1bpo8//tjp8NvlX/pXFb6+vlq8eLEOHDigv//97xo4cKDatGmjxo0by9fXV0FBQYqOjtZf//pXHTp0SP/85z9dfuVAx44dtWfPHj322GMKDQ2Vt7e3WrRoobvvvlsfffSRy0NI1cVms5W5pf+Vr8sTGhqqr776Sps2bdJDDz2ksLAwNWnSRF5eXmrR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\n", + "image/png": "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1qhl2d4i48mTJ8vc0qVLg3Hv+E92drbMVa5cORi//PLLZU1OU8/fI488Imv27t0rc9498XvzjiB5R3n+hW92AIDo0ewAANGj2QEAokezAwBEj2YHAIgezQ4AEL08qf6Hjz/+uMz97W9/C8a9EfkaNWoE42+88Yas8cb0vSMGyvHHHy9zbdq0Cca3bt0qa/r16xeMe9vb33nnHZmrW7duMO798kLbtm1lTh2n8H71oFy5csG4d2Jl/vz5MlenTp1g/NVXX5U18+bNkzl1TyTZpv/FF1/Imuuuu07mtm/fHoxfc801subss8+WOXWMoHTp0rJm165dwfhpp50ma5588kmZa9euXTB+3nnnyZq5c+fKnHo/3XzzzbJGHVfwfv3hrbfekjl1RGTTpk2yZsWKFTKX5BdL/v73vwfj3jGoGTNmyJyqU7/AYma2f/9+mcvJowfecbUUT7z9D7lz5z6cy+GbHQAgfjQ7AED0aHYAgOjR7AAA0aPZAQCil/I0pre4VvEWIKupvDfffFPWrF+/XubGjBkTjF955ZWyxlsa++CDD8qcsnbt2mDcmwz0qMm3QoUKyZotW7bI3D//+c9gfOrUqbLmk08+Cca9xc3eBKCiljObmf3Hf/yHzKmpwXPPPVfWTJkyJRhXi33N9JJvM7MOHToE45deeqms8ZZbq6lZz6OPPhqMq0XZZmZXX321zKnn1Zu4rFChgsyp58hb7qsmChctWiRrvMndHj16BOPe4mZvUlnxlm/36tUrGPfuB28aUz0Xa9askTWHO9WYqpyexvQmTFPBNzsAQPRodgCA6NHsAADRo9kBAKJHswMARI9mBwCIXspHD37++WeZUyPWO3bskDUZGRnhC8qjL6lo0aIyt23btmDcO8rgHS8YNGhQMH7hhRfKmgceeCAY90Zwn3rqKZkbMmRIMN60aVNZM2LECJlTxzPy5csna84666xgXL1+ZmYXXHCBzH366afBeIECBWTN888/n/ZjeUcFatasGYyrxcNmZmeccYbMKd9++23aNWZ62bg6MmGmj73UqlVL1nhj/wMHDgzGR40aJWtWr14tc++//34wrhZYm5nddNNNwXixYsVkTfPmzWVu2bJlwXiVKlVkTRJ33323zC1cuDAYHzp0aKLHWr58edo13iLonOQdcTh48GDafy9Jza/xzQ4AED2aHQAgejQ7AED0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\n", "text/plain": [ "
" ] @@ -1882,55 +1361,54 @@ } ], "source": [ - "plt.style.use(\"seaborn-bright\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - ")\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" + "L=100\n", + "current_img = inputimg[None,None,...].to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "\n", + "\n", + "progress_bar = tqdm(range(L)) #go back and forth L timesteps\n", + "for t in progress_bar: #go through the noising process\n", + "\n", + " with autocast(enabled=False):\n", + " with torch.no_grad():\n", + " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " current_img, _ = scheduler.reversed_step(model_output, t, current_img)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()\n" ] }, { "cell_type": "markdown", - "id": "0cd48c2d", + "id": "a7c8346a-6296-4800-b978-c10fcdf09779", "metadata": {}, "source": [ - "### Sampling process with classifier-free guidance\n", - "In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`).\n", - "Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector." + "### Denoising Process using gradient guidance\n", + "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\n", + "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). The scale s is used to amplify the gradient." ] }, { "cell_type": "code", - "execution_count": 27, - "id": "f71e4924", + "execution_count": 38, + "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } + "lines_to_next_cell": 0 }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|███████████████████████████████████████| 1000/1000 [00:12<00:00, 77.06it/s]\n" + "100%|█████████████████████████████████████████| 100/100 [00:06<00:00, 14.41it/s]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1940,50 +1418,72 @@ } ], "source": [ - "model.eval()\n", - "guidance_scale = 7.0\n", - "conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device)\n", "\n", - "noise = torch.randn((1, 1, 64, 64))\n", - "noise = noise.to(device)\n", - "scheduler.set_timesteps(num_inference_steps=1000)\n", - "progress_bar = tqdm(scheduler.timesteps)\n", - "for t in progress_bar:\n", + "\n", + "y=torch.tensor(0) #define the desired class label\n", + "scale=10 #define the desired gradient scale s\n", + "progress_bar = tqdm(range(L)) #go back and forth L timesteps\n", + "\n", + "for i in progress_bar: #go through the denoising process\n", + "\n", + " t=L-i\n", " with autocast(enabled=True):\n", - " with torch.no_grad():\n", - " noise_input = torch.cat([noise] * 2)\n", - " model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device))\n", - " noise_pred_uncond, noise_pred_text = model_output.chunk(2)\n", - " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) # this is supposed to be epsilon\n", + "\n", + " with torch.enable_grad():\n", + " x_in = current_img.detach().requires_grad_(True)\n", + " logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " log_probs = F.log_softmax(logits, dim=-1)\n", + " selected = log_probs[range(len(logits)), y.view(-1)]\n", + " a = torch.autograd.grad(selected.sum(), x_in)[0]\n", + " alpha_prod_t = scheduler.alphas_cumprod[t]\n", + " updated_noise = model_output- (1 - alpha_prod_t).sqrt() * scale*a #update the predicted noise epsilon with the gradient of the classifier\n", "\n", - " noise, _ = scheduler.step(noise_pred, t, noise)\n", + " current_img, _ = scheduler.step(updated_noise, t, current_img)\n", + " torch.cuda.empty_cache()\n", "\n", "plt.style.use(\"default\")\n", - "plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", - "plt.show()" + "plt.show()\n", + "\n" ] }, { "cell_type": "markdown", - "id": "3483b097", + "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", "metadata": {}, "source": [ - "### Cleanup data directory\n", - "\n", - "Remove directory if a temporary was used." + "### Anomaly Detection\n", + "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction." ] }, { "cell_type": "code", - "execution_count": 13, - "id": "b00d4f9a", + "execution_count": 39, + "id": "ecffaaf3-a7df-453e-81a9-757113d85084", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "if directory is None:\n", - " shutil.rmtree(root_dir)" + "\n", + "diff=inputimg.cpu()-current_img[0, 0].cpu().detach().numpy()\n", + "plt.style.use(\"default\")\n", + "plt.imshow(diff, cmap=\"jet\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" ] } ], diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py index da6de829..c02f1003 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py @@ -31,7 +31,6 @@ # !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" # !python -c "import matplotlib" || pip install -q matplotlib # !python -c "import seaborn" || pip install -q seaborn -# %matplotlib inline # %% [markdown] # ## Setup imports @@ -51,9 +50,10 @@ import shutil import tempfile import time +from typing import Dict import os +import torch.nn as nn import matplotlib.pyplot as plt -import seaborn import numpy as np import torch import torch.nn.functional as F @@ -64,9 +64,14 @@ from monai.utils import first, set_determinism from torch.cuda.amp import GradScaler, autocast from tqdm import tqdm +torch.multiprocessing.set_sharing_strategy('file_system') +import sys +sys.path.append('/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/') +print('path', sys.path) from generative.inferers import DiffusionInferer + # TODO: Add right import reference after deployed from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder @@ -74,7 +79,13 @@ from generative.networks.schedulers.ddim import DDIMScheduler print_config() -train=False +train_classifier=False +train_diffusionmodel=False +def visualize(img): + _min = img.min() + _max = img.max() + normalized_img = (img - _min)/ (_max - _min) + return normalized_img # %% [markdown] @@ -83,25 +94,21 @@ # %% jupyter={"outputs_hidden": false} directory = os.environ.get("MONAI_DATA_DIRECTORY") #root_dir = tempfile.mkdtemp() if directory is None else directory -root_dir='/home/juliawolleb/PycharmProjects/MONAI/val_brats' -root_dir_val='/home/juliawolleb/PycharmProjects/MONAI/val_brats' - -print(root_dir, root_dir_val) +root_dir='/home/juliawolleb/PycharmProjects/MONAI/brats' #path to where the data is stored # %% [markdown] # ## Set deterministic training for reproducibility # %% jupyter={"outputs_hidden": false} -set_determinism(42) +set_determinism(36) -# %% [markdown] +# %% [markdown] tags=[] # ## Setup BRATS Dataset for 2D slices and training and validation dataloaders # As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150 # %% jupyter={"outputs_hidden": false} -batch_size = 2 channel = 0 # 0 = Flair assert channel in [0, 1, 2, 3], "Choose a valid channel" @@ -135,19 +142,48 @@ seed=0, transform=train_transforms, ) -nb_3D_images_to_mix =20 -train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4) +print('len train data', len(train_ds)) + +def get_batched_2d_axial_slices(data : Dict): + images_3D = data['image'] + batched_2d_slices = torch.cat(images_3D.split(1, dim = -1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. + slice_label = data['slice_label'] + slice_label = torch.cat(slice_label.split(1, dim = -1)[10:-10],0).squeeze() + return batched_2d_slices, slice_label -print(f'Image shape {train_ds[0]["image"].shape}') +preprocessing_train=False +if preprocessing_train == True: + train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) + print(f'Image shape {train_ds[0]["image"].shape}') + + data_2d_slices=[] + data_slice_label = [] + check_data = first(train_loader_3D) + for i, data in enumerate(train_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices.append(b2d) + data_slice_label.append(slice_label2d) + total_train_slices=torch.cat(data_2d_slices,0) + total_train_labels=torch.cat(data_slice_label,0) + torch.save(total_train_slices, 'total_train_slices.pt') + torch.save(total_train_labels, 'total_train_labels.pt') +else: + total_train_slices=torch.load('total_train_slices.pt') + total_train_labels=torch.load('total_train_labels.pt') + print('total slices', total_train_slices.shape) + print('total lbaels', total_train_labels.shape) -print('download val set') -# %% +# %% [markdown] tags=[] +# ## Setup BRATS Dataset for 2D slices validation dataloader +# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150 + +# %% val_ds = DecathlonDataset( - root_dir=root_dir_val, + root_dir=root_dir, task="Task01_BrainTumour", section="validation", # validation cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise @@ -156,10 +192,30 @@ seed=0, transform=train_transforms, ) -val_loader_3D = DataLoader(val_ds, batch_size=2, shuffle=True, num_workers=4) -print(f'Image shape {val_ds[0]["image"].shape}') +preprocessing_val=False +if preprocessing_val == True: + val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) + print(f'Image shape {val_ds[0]["image"].shape}') + print('len val data', len(val_ds)) + data_2d_slices_val=[] + data_slice_label_val = [] + for i, data in enumerate(val_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices_val.append(b2d) + data_slice_label_val.append(slice_label2d) + total_val_slices=torch.cat(data_2d_slices_val,0) + total_val_labels=torch.cat(data_slice_label_val,0) + torch.save(total_val_slices, 'total_val_slices.pt') + torch.save(total_val_labels, 'total_val_labels.pt') + +else: + total_val_slices=torch.load('total_val_slices.pt') + total_val_labels=torch.load('total_val_labels.pt') + print('total slices', total_val_slices.shape) + print('total lbaels', total_val_labels.shape) + # %% [markdown] # Here we use transforms to augment the training dataset, as usual: @@ -171,64 +227,12 @@ # # -# %% [markdown] -# ### Visualisation of the training images - -# %% jupyter={"outputs_hidden": false} - - -from typing import Dict -def get_batched_2d_axial_slices(data : Dict): - images_3D = data['image'] - batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1]) - slice_label = data['slice_label'] - #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float() - slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze() - return batched_2d_slices, slice_label -print('check data') - -if train==True: - check_data = first(train_loader_3D) - batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data) - idx = list(torch.randperm(batched_2d_slices.shape[0])) - print('idx', len(idx)) - print(f"Batch shape: {batched_2d_slices.shape}") - print(f"Slices class: {slice_label[idx][slices].view(-1)}") - subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) # - -check_data_val = first(val_loader_3D) -batched_2d_slices_val, slice_label_val = get_batched_2d_axial_slices(check_data_val) - - - -idx_val=list(torch.randperm(batched_2d_slices_val.shape[0])) -slices = [0,30,45,63] - -image_visualisation = torch.cat(batched_2d_slices_val[idx_val][slices].squeeze().split(1), dim=2).squeeze() -plt.figure("training images", (12, 6)) -plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") -plt.axis("off") -plt.tight_layout() -plt.show() - -# %% - -subset_2D_val = zip(batched_2d_slices_val.split(1),slice_label_val.split(1))# - - - # %% [markdown] # ### Define network, scheduler, optimizer, and inferer # At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using # the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms # in the 3rd level, each with 1 attention head (`num_head_channels=64`). # -# In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use: -# -# ` -# with_conditioning=True, -# cross_attention_dim=1, -# ` # %% jupyter={"outputs_hidden": false} device = torch.device("cuda") @@ -258,26 +262,30 @@ def get_batched_2d_axial_slices(data : Dict): # Here, we are training our diffusion model for 75 epochs (training time: ~50 minutes). # %% jupyter={"outputs_hidden": false} -n_epochs =100 +n_epochs =75 +batch_size=32 val_interval = 1 epoch_loss_list = [] val_epoch_loss_list = [] - -if train==False: - model.load_state_dict(torch.load("./model.pt", map_location={'cuda:0': 'cpu'})) +train_diffusionmodel=False +if train_diffusionmodel==False: + model.load_state_dict(torch.load("model.pt", map_location={'cuda:0': 'cpu'})) else: scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): model.train() epoch_loss = 0 - subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) - subset_2D_val = zip(batched_2d_slices_val.split(1), slice_label.split(1)) # + indexes = list(torch.randperm(total_train_slices.shape[0])) #shuffle training data new + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(idx), ncols=10) + subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # + + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes), ncols=10) progress_bar.set_description(f"Epoch {epoch}") for step, (a,b) in progress_bar: - print('step', step, a.shape, b.shape, b) images = a.to(device) classes = b.to(device) optimizer.zero_grad(set_to_none=True) @@ -295,6 +303,8 @@ def get_batched_2d_axial_slices(data : Dict): scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() + if step%20==0: + print('step', step, loss) epoch_loss += loss.item() @@ -305,16 +315,17 @@ def get_batched_2d_axial_slices(data : Dict): ) epoch_loss_list.append(epoch_loss / (step + 1)) + if (epoch) % val_interval == 0: model.eval() val_epoch_loss = 0 - progress_bar_val = tqdm(enumerate(subset_2D_val), total=len(idx_val), ncols=70) + progress_bar_val = tqdm(enumerate(subset_2D_val)) progress_bar.set_description(f"Epoch {epoch}") for step, (a, b) in progress_bar_val: images = a.to(device) classes = b.to(device) - timesteps = torch.randint(0, 1000, (len(images),)).to(device)#torch.from_numpy(np.arange(0, 1000)[::-1].copy()) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) with torch.no_grad(): with autocast(enabled=True): noise = torch.randn_like(images).to(device) @@ -330,6 +341,8 @@ def get_batched_2d_axial_slices(data : Dict): val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start + torch.save(model.state_dict(), "./diffusion_model.pt") #save the trained model + print(f"train diffusion completed, total time: {total_time}.") plt.style.use("seaborn-bright") @@ -347,125 +360,124 @@ def get_batched_2d_axial_slices(data : Dict): plt.xlabel("Epochs", fontsize=16) plt.ylabel("Loss", fontsize=16) plt.legend(prop={"size": 14}) - #plt.show() - #torch.save(model.state_dict(), "./model.pt") + plt.show() -# %% -### Model training of the Classification Model -#Here, we are training our binary classification model for 5 epochs. + +# %% [markdown] +# ### Model training of the Classification Model +# #First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. +# #Here, we are training our binary classification model for 20 epochs. # %% -## First, we define the classification model -# %% classifier = DiffusionModelEncoder( spatial_dims=2, in_channels=1, - out_channels=1, - num_channels=(64, 64, 64), - # attention_levels=(False, False, True), + out_channels=2, + num_channels=(32,64,128), + attention_levels=(False, True, True), num_res_blocks=1, num_head_channels=64, with_conditioning=False, - # cross_attention_dim=1, ) classifier.to(device) -batch_size=6 +batch_size=32 # %% -n_epochs = 100 +n_epochs = 20 val_interval = 1 epoch_loss_list = [] val_epoch_loss_list = [] -optimizer = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) +optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) classifier.to(device) - -if train==False: +train_classifier=False +if train_classifier==False: classifier.load_state_dict(torch.load("./classifier.pt", map_location={'cuda:0': 'cpu'})) else: + scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): classifier.train() epoch_loss = 0 - subset_2D = zip(batched_2d_slices.split(batch_size), slice_label.split(batch_size)) - subset_2D_val = zip(batched_2d_slices_val.split(1), slice_label.split(1)) # - progress_bar = tqdm(enumerate(subset_2D), total=len(idx), ncols=20) + indexes = list(torch.randperm(total_train_slices.shape[0])) + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size) progress_bar.set_description(f"Epoch {epoch}") - for step, (a,b) in progress_bar: images = a.to(device) classes = b.to(device) - optimizer.zero_grad(set_to_none=True) + weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset + optimizer_cls.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with autocast(enabled=True): + with autocast(enabled=False): # Generate random noise - noise = 0*torch.randn_like(images).to(device) + noise = torch.randn_like(images).to(device) # Get model prediction - # pred=classifier(images) - - pred = inferer(inputs=images, diffusion_model=classifier, noise=noise, timesteps=timesteps) #remove the class conditioning - print('pred', pred) - # noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning - loss = F.binary_cross_entropy_with_logits(pred[:,0].float(), classes.float()) - print('loss', loss) - #scaler.scale(loss).backward() - # scaler.step(optimizer) + noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image + pred=classifier(noisy_img, timesteps) + loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") + loss.backward() - optimizer.step() - #scaler.update() + optimizer_cls.step() epoch_loss += loss.item() - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) + { + "loss": epoch_loss / (step + 1), + } + ) epoch_loss_list.append(epoch_loss / (step + 1)) + print('final step train', step) + if (epoch + 1) % val_interval == 0: classifier.eval() val_epoch_loss = 0 - progress_bar = tqdm(enumerate(subset_2D_val), total=len(idx), ncols=70) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a,b) in progress_bar: + subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar_val.set_description(f"Epoch {epoch}") + for step, (a,b) in progress_bar_val: images = a.to(device) classes = b.to(device) - - timesteps = torch.randint(0, 1000, (len(images),)).to(device)#torch.from_numpy(np.arange(0, 1000)[::-1].copy()) + timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise with torch.no_grad(): - with autocast(enabled=True): - noise = 0*torch.randn_like(images).to(device) - pred = inferer(inputs=images, diffusion_model=classifier, noise=noise, timesteps=timesteps) - val_loss = F.binary_cross_entropy_with_logits(pred[:,0].float(), classes.float()) + with autocast(enabled=False): + noise = torch.randn_like(images).to(device) + pred = classifier(images, timesteps) + val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") val_epoch_loss += val_loss.item() - progress_bar.set_postfix( + _, predicted = torch.max(pred, 1); + progress_bar_val.set_postfix( { "val_loss": val_epoch_loss / (step + 1), } ) val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + print('final step val', step) + total_time = time.time() - total_start print(f"train completed, total time: {total_time}.") - # torch.save(classifier.state_dict(), "./classifier.pt") -# %% [markdown] -# ### Learning curves - -# %% jupyter={"outputs_hidden": false} + torch.save(classifier.state_dict(), "./classifier.pt") + + ## Learning curves for the Classifier + plt.style.use("seaborn-bright") plt.title("Learning Curves", fontsize=20) + print('epl', len(epoch_loss_list)) plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") plt.plot( np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), @@ -479,110 +491,99 @@ def get_batched_2d_axial_slices(data : Dict): plt.xlabel("Epochs", fontsize=16) plt.ylabel("Loss", fontsize=16) plt.legend(prop={"size": 14}) - #plt.show() - + plt.show() # %% [markdown] -# ### Sampling process with classifier-free guidance -# In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`). -# Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector. +# ### For Image-to-Image Translation to a Healthy Subject, we pick a disesed subject of the validation set -# %% jupyter={"outputs_hidden": false} -model.eval() -guidance_scale = 0 -conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device) +# %% -# %% [markdown] -# ### Pick an input slice to be transformed -inputimg = batched_2d_slices_val[50][0,...] -plt.figure("input") +inputimg = total_val_slices[27][0,...] # Pick an input slice to be transformed (100,20 +inputlabel= total_val_labels[27] # Check whether it is healthy or diseased + +plt.figure("input"+str(inputlabel)) plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") plt.axis("off") plt.tight_layout() plt.show() +model.eval() +classifier.eval() -noise = inputimg[None,None,...]#torch.randn((1, 1, 64, 64)) -noise = noise.to(device) -scheduler.set_timesteps(num_inference_steps=1000) -L=20 -progress_bar = tqdm(range(L)) #go back and forth L timesteps +# %% [markdown] +# ### Encoding the input image in noise with the reversed DDIM sampling scheme +# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme. +# We define the number of steps in the noising and denoising process by L. +# +# %% jupyter={"outputs_hidden": false} +L=180 +current_img = inputimg[None,None,...].to(device) +scheduler.set_timesteps(num_inference_steps=1000) +progress_bar = tqdm(range(L)) #go back and forth L timesteps for t in progress_bar: #go through the noising process - print('t noising', t) - with autocast(enabled=True): + with autocast(enabled=False): with torch.no_grad(): - - noise_input = noise - print('inputshape', noise_input.shape) - model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device)) - # noise_pred_uncond, noise_pred_text = model_output.chunk(2) #this is supposed to be epsilon - noise_pred = model_output #this is supposed to be epsilon - - noise, _ = scheduler.reversed_step(noise_pred, t, noise) + model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) + current_img, _ = scheduler.reversed_step(model_output, t, current_img) plt.style.use("default") -plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") plt.tight_layout() plt.axis("off") plt.show() -def cond_fn(x, t, y=None): #compute the gradient - assert y is not None - with torch.enable_grad(): - x_in = x.detach().requires_grad_(True) - logits = classifier(x_in, t) - log_probs = F.log_softmax(logits, dim=-1) - selected = log_probs[range(len(logits)), y.view(-1)] - a = th.autograd.grad(selected.sum(), x_in)[0] - return a, a * args.classifier_scale -#desired class -y=torch.tensor(0) -scale=100 + +# %% [markdown] +# ### Denoising Process using gradient guidance +# From the noisy image, we apply DDIM sampling scheme for denoising for L steps. +# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). The scale s is used to amplify the gradient. + +# %% + + +y=torch.tensor(0) #define the desired class label +scale=1 #define the desired gradient scale s +progress_bar = tqdm(range(L)) #go back and forth L timesteps for i in progress_bar: #go through the denoising process + t=L-i - print('t denoising', t) with autocast(enabled=True): - with torch.enable_grad(): - noise_input = noise - print('inputshape', noise_input.shape) - model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device)) + model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) # this is supposed to be epsilon - x_in = noise_input.detach().requires_grad_(True) - - logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(noise.device)) - print('logits', logits) + with torch.enable_grad(): + x_in = current_img.detach().requires_grad_(True) + logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device)) log_probs = F.log_softmax(logits, dim=-1) selected = log_probs[range(len(logits)), y.view(-1)] a = torch.autograd.grad(selected.sum(), x_in)[0] - # noise_pred_uncond, noise_pred_text = model_output.chunk(2) #this is supposed to be epsilon - noise_pred = model_output # this is supposed to be epsilon - updated_noise=noise_pred - scale*a + alpha_prod_t = scheduler.alphas_cumprod[t] + updated_noise = model_output- (1 - alpha_prod_t).sqrt() * scale*a #update the predicted noise epsilon with the gradient of the classifier - noise, _ = scheduler.step(updated_noise, t, noise) + current_img, _ = scheduler.step(updated_noise, t, current_img) + torch.cuda.empty_cache() plt.style.use("default") -plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap="gray") plt.tight_layout() plt.axis("off") plt.show() -diff=inputimg.cpu()-noise[0, 0].cpu() +# %% [markdown] +# ### Anomaly Detection +# To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction. + +# %% + +diff=abs(inputimg.cpu()-current_img[0, 0].cpu()).detach().numpy() plt.style.use("default") plt.imshow(diff, cmap="jet") plt.tight_layout() plt.axis("off") plt.show() -# %% [markdown] -# ### Cleanup data directory -# -# Remove directory if a temporary was used. - -# %% - From 2a0bed97d8399a4c4a8bc72400a6a17fae8ebbbd Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 22 Feb 2023 17:42:30 +0100 Subject: [PATCH 04/23] cleaned up the classification network for gradient guidance --- .../networks/nets/diffusion_model_unet.py | 83 +------------------ 1 file changed, 2 insertions(+), 81 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 729c0bd0..59492ab4 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1660,9 +1660,7 @@ def forward( class DiffusionModelEncoder(nn.Module): """ - Unet network with timestep embedding and attention mechanisms for conditioning based on - Rombach et al. "High-Resolution Image Synthesis with Latent Diffusion Models" https://arxiv.org/abs/2112.10752 - and Pinaya et al. "Brain Imaging Generation with Latent Diffusion Models" https://arxiv.org/abs/2209.07162 + Classification Network based on the Encoder of the Diffusion Model, followed by fully connected layers for classification Args: spatial_dims: number of spatial dimensions. @@ -1781,76 +1779,16 @@ def __init__( self.down_blocks.append(down_block) - # mid - self.middle_block = get_mid_block( - spatial_dims=spatial_dims, - in_channels=num_channels[-1], - temb_channels=time_embed_dim, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - with_conditioning=with_conditioning, - num_head_channels=num_head_channels[-1], - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - # up - self.up_blocks = nn.ModuleList([]) - reversed_block_out_channels = list(reversed(num_channels)) - reversed_attention_levels = list(reversed(attention_levels)) - reversed_num_head_channels = list(reversed(num_head_channels)) - output_channel = reversed_block_out_channels[0] - for i in range(len(reversed_block_out_channels)): - prev_output_channel = output_channel - output_channel = reversed_block_out_channels[i] - input_channel = reversed_block_out_channels[min(i + 1, len(num_channels) - 1)] - - is_final_block = i == len(num_channels) - 1 - up_block = get_up_block( - spatial_dims=spatial_dims, - in_channels=input_channel, - prev_output_channel=prev_output_channel, - out_channels=output_channel, - temb_channels=time_embed_dim, - num_res_blocks=num_res_blocks + 1, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_upsample=not is_final_block, - with_attn=(reversed_attention_levels[i] and not with_conditioning), - with_cross_attn=(reversed_attention_levels[i] and with_conditioning), - num_head_channels=reversed_num_head_channels[i], - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - - self.up_blocks.append(up_block) - # self.out = nn.Linear(4096, self.out_channels) self.out = nn.Sequential( nn.Linear(8192, 512), nn.ReLU(), nn.Dropout(0.2), nn.Linear(512, self.out_channels), - #nn.Sigmoid(), ) - # out - # self.out = nn.Sequential( - # nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels[0], eps=norm_eps, affine=True), - # nn.SiLU(), - # zero_module( - # Convolution( - # spatial_dims=spatial_dims, - # in_channels=num_channels[0], - # out_channels=out_channels, - # strides=1, - # kernel_size=3, - # padding=1, - # conv_only=True, - # ) - # ), - # ) - + def forward( self, @@ -1883,27 +1821,10 @@ def forward( # 4. down if context is not None and self.with_conditioning is False: raise ValueError("model should have with_conditioning = True if context is provided") - down_block_res_samples: List[torch.Tensor] = [h] for downsample_block in self.down_blocks: h, _ = downsample_block(hidden_states=h, temb=emb, context=context) - # for residual in res_samples: - # down_block_res_samples.append(residual) - - - - # # 5. mid h = h.reshape(h.shape[0], -1) - # - # # 6. up - # for upsample_block in self.up_blocks: - # res_samples = down_block_res_samples[-len(upsample_block.resnets) :] - # down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)] - # h = upsample_block(hidden_states=h, res_hidden_states_list=res_samples, temb=emb, context=context) - - # 7. output block - - output=self.out(h) return output From 20535f0491913056dd46fb5bc194e88c4241b50e Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 23 Feb 2023 14:04:46 +0100 Subject: [PATCH 05/23] cleaning up --- .../networks/nets/diffusion_model_encoder.py | 1661 ------------- .../networks/nets/diffusion_model_unet.py | 19 +- generative/networks/schedulers/ddim.py | 25 +- tutorials/Untitled.ipynb | 33 - .../generative/2d_vqvae/2d_vqvae_tutorial.py | 1 - ...r_guidance_anomalydetection_tutorial.ipynb | 2158 +++++++++++++---- ...fier_guidance_anomalydetection_tutorial.py | 334 +-- .../Untitled.ipynb | 33 - .../Untitled1.ipynb | 6 - .../load_2d_brats.ipynb | 437 ---- .../load_2d_brats.py | 201 -- 11 files changed, 1882 insertions(+), 3026 deletions(-) delete mode 100644 generative/networks/nets/diffusion_model_encoder.py delete mode 100644 tutorials/Untitled.ipynb delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py diff --git a/generative/networks/nets/diffusion_model_encoder.py b/generative/networks/nets/diffusion_model_encoder.py deleted file mode 100644 index 7d87181c..00000000 --- a/generative/networks/nets/diffusion_model_encoder.py +++ /dev/null @@ -1,1661 +0,0 @@ -# Copyright (c) MONAI Consortium -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ========================================================================= -# Adapted from https://github.com/huggingface/diffusers -# which has the following license: -# https://github.com/huggingface/diffusers/blob/main/LICENSE - -# Copyright 2022 UC Berkeley Team and The HuggingFace Team. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ========================================================================= - -import math -from typing import List, Optional, Sequence, Tuple, Union - -import torch -import torch.nn.functional as F -from monai.networks.blocks import Convolution -from monai.networks.layers.factories import Pool -from torch import nn - -__all__ = ["DiffusionModelEncoder"] - - -def zero_module(module: nn.Module) -> nn.Module: - """ - Zero out the parameters of a module and return it. - """ - for p in module.parameters(): - p.detach().zero_() - return module - - -class GEGLU(nn.Module): - """ - A variant of the gated linear unit activation function from https://arxiv.org/abs/2002.05202. - - Args: - dim_in: number of channels in the input. - dim_out: number of channels in the output. - """ - - def __init__(self, dim_in: int, dim_out: int) -> None: - super().__init__() - self.proj = nn.Linear(dim_in, dim_out * 2) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - x, gate = self.proj(x).chunk(2, dim=-1) - return x * F.gelu(gate.to(dtype=torch.float32)).to(dtype=gate.dtype) - - -class FeedForward(nn.Module): - """ - A feed-forward layer. - - Args: - num_channels: number of channels in the input. - dim_out: number of channels in the output. If not given, defaults to `dim`. - mult: multiplier to use for the hidden dimension. - dropout: dropout probability to use. - """ - - def __init__(self, num_channels: int, dim_out: Optional[int] = None, mult: int = 4, dropout: float = 0.0) -> None: - super().__init__() - inner_dim = int(num_channels * mult) - dim_out = dim_out if dim_out is not None else num_channels - - self.net = nn.Sequential(GEGLU(num_channels, inner_dim), nn.Dropout(dropout), nn.Linear(inner_dim, dim_out)) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - return self.net(x) - - -class CrossAttention(nn.Module): - """ - A cross attention layer. - - Args: - query_dim: number of channels in the query. - cross_attention_dim: number of channels in the context. - num_attention_heads: number of heads to use for multi-head attention. - num_head_channels: number of channels in each head. - dropout: dropout probability to use. - """ - - def __init__( - self, - query_dim: int, - cross_attention_dim: Optional[int] = None, - num_attention_heads: int = 8, - num_head_channels: int = 64, - dropout: float = 0.0, - ) -> None: - super().__init__() - inner_dim = num_head_channels * num_attention_heads - cross_attention_dim = cross_attention_dim if cross_attention_dim is not None else query_dim - - self.scale = num_head_channels**-0.5 - self.heads = num_attention_heads - - self.to_q = nn.Linear(query_dim, inner_dim, bias=False) - self.to_k = nn.Linear(cross_attention_dim, inner_dim, bias=False) - self.to_v = nn.Linear(cross_attention_dim, inner_dim, bias=False) - - self.to_out = nn.Sequential(nn.Linear(inner_dim, query_dim), nn.Dropout(dropout)) - - def reshape_heads_to_batch_dim(self, x: torch.Tensor) -> torch.Tensor: - batch_size, seq_len, dim = x.shape - head_size = self.heads - x = x.reshape(batch_size, seq_len, head_size, dim // head_size) - x = x.permute(0, 2, 1, 3).reshape(batch_size * head_size, seq_len, dim // head_size) - return x - - def reshape_batch_dim_to_heads(self, x: torch.Tensor) -> torch.Tensor: - batch_size, seq_len, dim = x.shape - head_size = self.heads - x = x.reshape(batch_size // head_size, head_size, seq_len, dim) - x = x.permute(0, 2, 1, 3).reshape(batch_size // head_size, seq_len, dim * head_size) - return x - - def _attention(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor) -> torch.Tensor: - attention_scores = torch.matmul(query, key.transpose(-1, -2)) * self.scale - attention_probs = attention_scores.softmax(dim=-1) - # compute attention output - hidden_states = torch.matmul(attention_probs, value) - - # reshape hidden_states - hidden_states = self.reshape_batch_dim_to_heads(hidden_states) - return hidden_states - - def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: - query = self.to_q(x) - context = context if context is not None else x - key = self.to_k(context) - value = self.to_v(context) - - query = self.reshape_heads_to_batch_dim(query) - key = self.reshape_heads_to_batch_dim(key) - value = self.reshape_heads_to_batch_dim(value) - - x = self._attention(query, key, value) - - return self.to_out(x) - - -class BasicTransformerBlock(nn.Module): - """ - A basic Transformer block. - - Args: - num_channels: number of channels in the input and output. - num_attention_heads: number of heads to use for multi-head attention. - num_head_channels: number of channels in each attention head. - dropout: dropout probability to use. - cross_attention_dim: size of the context vector for cross attention. - """ - - def __init__( - self, - num_channels: int, - num_attention_heads: int, - num_head_channels: int, - dropout: float = 0.0, - cross_attention_dim: Optional[int] = None, - ) -> None: - super().__init__() - self.attn1 = CrossAttention( - query_dim=num_channels, - num_attention_heads=num_attention_heads, - num_head_channels=num_head_channels, - dropout=dropout, - ) # is a self-attention - self.ff = FeedForward(num_channels, dropout=dropout) - self.attn2 = CrossAttention( - query_dim=num_channels, - cross_attention_dim=cross_attention_dim, - num_attention_heads=num_attention_heads, - num_head_channels=num_head_channels, - dropout=dropout, - ) # is a self-attention if context is None - self.norm1 = nn.LayerNorm(num_channels) - self.norm2 = nn.LayerNorm(num_channels) - self.norm3 = nn.LayerNorm(num_channels) - - def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: - # 1. Self-Attention - x = self.attn1(self.norm1(x)) + x - - # 2. Cross-Attention - x = self.attn2(self.norm2(x), context=context) + x - - # 3. Feed-forward - x = self.ff(self.norm3(x)) + x - return x - - -class SpatialTransformer(nn.Module): - """ - Transformer block for image-like data. First, project the input (aka embedding) and reshape to b, t, d. Then apply - standard transformer action. Finally, reshape to image. - - Args: - spatial_dims: number of spatial dimensions. - in_channels: number of channels in the input and output. - num_attention_heads: number of heads to use for multi-head attention. - num_head_channels: number of channels in each attention head. - num_layers: number of layers of Transformer blocks to use. - dropout: dropout probability to use. - norm_num_groups: number of groups for the normalization. - norm_eps: epsilon for the normalization. - cross_attention_dim: number of context dimensions to use. - """ - - def __init__( - self, - spatial_dims: int, - in_channels: int, - num_attention_heads: int, - num_head_channels: int, - num_layers: int = 1, - dropout: float = 0.0, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - cross_attention_dim: Optional[int] = None, - ) -> None: - super().__init__() - self.spatial_dims = spatial_dims - self.in_channels = in_channels - inner_dim = num_attention_heads * num_head_channels - - self.norm = nn.GroupNorm(num_groups=norm_num_groups, num_channels=in_channels, eps=norm_eps, affine=True) - - self.proj_in = Convolution( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=inner_dim, - strides=1, - kernel_size=1, - padding=0, - conv_only=True, - ) - - self.transformer_blocks = nn.ModuleList( - [ - BasicTransformerBlock( - num_channels=inner_dim, - num_attention_heads=num_attention_heads, - num_head_channels=num_head_channels, - dropout=dropout, - cross_attention_dim=cross_attention_dim, - ) - for _ in range(num_layers) - ] - ) - - self.proj_out = zero_module( - Convolution( - spatial_dims=spatial_dims, - in_channels=inner_dim, - out_channels=in_channels, - strides=1, - kernel_size=1, - padding=0, - conv_only=True, - ) - ) - - def forward(self, x: torch.Tensor, context: Optional[torch.Tensor] = None) -> torch.Tensor: - # note: if no context is given, cross-attention defaults to self-attention - batch = channel = height = width = depth = -1 - if self.spatial_dims == 2: - batch, channel, height, width = x.shape - if self.spatial_dims == 3: - batch, channel, height, width, depth = x.shape - - residual = x - x = self.norm(x) - x = self.proj_in(x) - - inner_dim = x.shape[1] - - if self.spatial_dims == 2: - x = x.permute(0, 2, 3, 1).reshape(batch, height * width, inner_dim) - if self.spatial_dims == 3: - x = x.permute(0, 2, 3, 4, 1).reshape(batch, height * width * depth, inner_dim) - - for block in self.transformer_blocks: - x = block(x, context=context) - - if self.spatial_dims == 2: - x = x.reshape(batch, height, width, inner_dim).permute(0, 3, 1, 2) - if self.spatial_dims == 3: - x = x.reshape(batch, height, width, depth, inner_dim).permute(0, 4, 1, 2, 3) - - x = self.proj_out(x) - return x + residual - - -class AttentionBlock(nn.Module): - """ - An attention block that allows spatial positions to attend to each other. Uses three q, k, v linear layers to - compute attention. - - Args: - spatial_dims: number of spatial dimensions. - num_channels: number of channels in the input and output. - num_head_channels: number of channels in each attention head. - norm_num_groups: number of groups to use for group norm. - norm_eps: epsilon value to use for group norm. - """ - - def __init__( - self, - spatial_dims: int, - num_channels: int, - num_head_channels: Optional[int] = None, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - ) -> None: - super().__init__() - self.spatial_dims = spatial_dims - self.num_channels = num_channels - - self.num_heads = num_channels // num_head_channels if num_head_channels is not None else 1 - self.num_head_size = num_head_channels - self.norm = nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels, eps=norm_eps, affine=True) - - # define q,k,v as linear layers - self.query = nn.Linear(num_channels, num_channels) - self.key = nn.Linear(num_channels, num_channels) - self.value = nn.Linear(num_channels, num_channels) - - self.proj_attn = nn.Linear(num_channels, num_channels) - - def transpose_for_scores(self, projection: torch.Tensor) -> torch.Tensor: - new_projection_shape = projection.size()[:-1] + (self.num_heads, -1) - # move heads to 2nd position (B, T, H * D) -> (B, T, H, D) -> (B, H, T, D) - new_projection = projection.view(new_projection_shape).permute(0, 2, 1, 3) - return new_projection - - def forward(self, x: torch.Tensor) -> torch.Tensor: - residual = x - - batch = channel = height = width = depth = -1 - if self.spatial_dims == 2: - batch, channel, height, width = x.shape - if self.spatial_dims == 3: - batch, channel, height, width, depth = x.shape - - # norm - x = self.norm(x) - - if self.spatial_dims == 2: - x = x.view(batch, channel, height * width).transpose(1, 2) - if self.spatial_dims == 3: - x = x.view(batch, channel, height * width * depth).transpose(1, 2) - - # proj to q, k, v - query_proj = self.query(x) - key_proj = self.key(x) - value_proj = self.value(x) - - # transpose - query_states = self.transpose_for_scores(query_proj) - key_states = self.transpose_for_scores(key_proj) - value_states = self.transpose_for_scores(value_proj) - - # get scores - scale = 1 / math.sqrt(math.sqrt(self.num_channels / self.num_heads)) - attention_scores = torch.matmul(query_states * scale, key_states.transpose(-1, -2) * scale) - attention_probs = torch.softmax(attention_scores.float(), dim=-1) - - # compute attention output - x = torch.matmul(attention_probs, value_states) - - x = x.permute(0, 2, 1, 3).contiguous() - new_x_shape = x.size()[:-2] + (self.num_channels,) - x = x.view(new_x_shape) - - # compute next hidden states - x = self.proj_attn(x) - - if self.spatial_dims == 2: - x = x.transpose(-1, -2).reshape(batch, channel, height, width) - if self.spatial_dims == 3: - x = x.transpose(-1, -2).reshape(batch, channel, height, width, depth) - - return x + residual - - -def get_timestep_embedding(timesteps: torch.Tensor, embedding_dim: int, max_period: int = 10000) -> torch.Tensor: - """ - Create sinusoidal timestep embeddingsfollowing the implementation in Ho et al. "Denoising Diffusion Probabilistic - Models" https://arxiv.org/abs/2006.11239. - - Args: - timesteps: a 1-D Tensor of N indices, one per batch element. - embedding_dim: the dimension of the output. - max_period: controls the minimum frequency of the embeddings. - """ - assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array" - - half_dim = embedding_dim // 2 - exponent = -math.log(max_period) * torch.arange(start=0, end=half_dim, dtype=torch.float32, device=timesteps.device) - freqs = torch.exp(exponent / half_dim) - - args = timesteps[:, None].float() * freqs[None, :] - embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) - - # zero pad - if embedding_dim % 2 == 1: - embedding = torch.nn.functional.pad(embedding, (0, 1, 0, 0)) - - return embedding - - -class Downsample(nn.Module): - """ - Downsampling layer. - - Args: - spatial_dims: number of spatial dimensions. - num_channels: number of input channels. - use_conv: if True uses Convolution instead of Pool average to perform downsampling. - out_channels: number of output channels. - padding: controls the amount of implicit zero-paddings on both sides for padding number of points - for each dimension. - """ - - def __init__( - self, - spatial_dims: int, - num_channels: int, - use_conv: bool, - out_channels: Optional[int] = None, - padding: int = 1, - ) -> None: - super().__init__() - self.num_channels = num_channels - self.out_channels = out_channels or num_channels - self.use_conv = use_conv - if use_conv: - self.op = Convolution( - spatial_dims=spatial_dims, - in_channels=self.num_channels, - out_channels=self.out_channels, - strides=2, - kernel_size=3, - padding=padding, - conv_only=True, - ) - else: - assert self.num_channels == self.out_channels - self.op = Pool[Pool.AVG, spatial_dims](kernel_size=2, stride=2) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - assert x.shape[1] == self.num_channels - return self.op(x) - - -class Upsample(nn.Module): - """ - Upsampling layer with an optional convolution. - - Args: - spatial_dims: number of spatial dimensions. - num_channels: number of input channels. - use_conv: if True uses Convolution instead of Pool average to perform downsampling. - out_channels: number of output channels. - padding: controls the amount of implicit zero-paddings on both sides for padding number of points for each - dimension. - """ - - def __init__( - self, - spatial_dims: int, - num_channels: int, - use_conv: bool, - out_channels: Optional[int] = None, - padding: int = 1, - ) -> None: - super().__init__() - self.num_channels = num_channels - self.out_channels = out_channels or num_channels - self.use_conv = use_conv - if use_conv: - self.conv = Convolution( - spatial_dims=spatial_dims, - in_channels=self.num_channels, - out_channels=self.out_channels, - strides=1, - kernel_size=3, - padding=padding, - conv_only=True, - ) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - assert x.shape[1] == self.num_channels - x = F.interpolate(x, scale_factor=2.0, mode="nearest") - if self.use_conv: - x = self.conv(x) - return x - - -class ResnetBlock(nn.Module): - """ - Residual block with timestep conditioning. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - temb_channels: number of timestep embedding channels. - out_channels: number of output channels. - up: if True, performs upsampling. - down: if True, performs downsampling. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - """ - - def __init__( - self, - spatial_dims: int, - in_channels: int, - temb_channels: int, - out_channels: Optional[int] = None, - up: bool = False, - down: bool = False, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - ) -> None: - super().__init__() - self.spatial_dims = spatial_dims - self.channels = in_channels - self.emb_channels = temb_channels - self.out_channels = out_channels or in_channels - self.up = up - self.down = down - - self.norm1 = nn.GroupNorm(num_groups=norm_num_groups, num_channels=in_channels, eps=norm_eps, affine=True) - self.nonlinearity = nn.SiLU() - self.conv1 = Convolution( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=self.out_channels, - strides=1, - kernel_size=3, - padding=1, - conv_only=True, - ) - - self.upsample = self.downsample = None - if self.up: - self.upsample = Upsample(spatial_dims, in_channels, use_conv=False) - elif down: - self.downsample = Downsample(spatial_dims, in_channels, use_conv=False) - - self.time_emb_proj = nn.Linear( - temb_channels, - self.out_channels, - ) - - self.norm2 = nn.GroupNorm(num_groups=norm_num_groups, num_channels=self.out_channels, eps=norm_eps, affine=True) - self.conv2 = zero_module( - Convolution( - spatial_dims=spatial_dims, - in_channels=self.out_channels, - out_channels=self.out_channels, - strides=1, - kernel_size=3, - padding=1, - conv_only=True, - ) - ) - - if self.out_channels == in_channels: - self.skip_connection = nn.Identity() - else: - self.skip_connection = Convolution( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=self.out_channels, - strides=1, - kernel_size=1, - padding=0, - conv_only=True, - ) - - def forward(self, x: torch.Tensor, emb: torch.Tensor) -> torch.Tensor: - h = x - h = self.norm1(h) - h = self.nonlinearity(h) - - if self.upsample is not None: - if h.shape[0] >= 64: - x = x.contiguous() - h = h.contiguous() - x = self.upsample(x) - h = self.upsample(h) - elif self.downsample is not None: - x = self.downsample(x) - h = self.downsample(h) - - h = self.conv1(h) - - if self.spatial_dims == 2: - temb = self.time_emb_proj(self.nonlinearity(emb))[:, :, None, None] - else: - temb = self.time_emb_proj(self.nonlinearity(emb))[:, :, None, None, None] - h = h + temb - - h = self.norm2(h) - h = self.nonlinearity(h) - h = self.conv2(h) - - return self.skip_connection(x) + h - - -class DownBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_downsample: bool = True, - downsample_padding: int = 1, - ) -> None: - """ - Unet's down block containing resnet and downsamplers blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_downsample: if True add downsample block. - downsample_padding: padding used in the downsampling block. - """ - super().__init__() - resnets = [] - - for i in range(num_res_blocks): - in_channels = in_channels if i == 0 else out_channels - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - - self.resnets = nn.ModuleList(resnets) - - if add_downsample: - self.downsampler = Downsample( - spatial_dims=spatial_dims, - num_channels=out_channels, - use_conv=True, - out_channels=out_channels, - padding=downsample_padding, - ) - else: - self.downsampler = None - - def forward( - self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None - ) -> Tuple[torch.Tensor, List[torch.Tensor]]: - del context - output_states = [] - - for resnet in self.resnets: - hidden_states = resnet(hidden_states, temb) - output_states.append(hidden_states) - - if self.downsampler is not None: - hidden_states = self.downsampler(hidden_states) - output_states.append(hidden_states) - - return hidden_states, output_states - - -class AttnDownBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_downsample: bool = True, - downsample_padding: int = 1, - num_head_channels: int = 1, - ) -> None: - """ - Unet's down block containing resnet, downsamplers and self-attention blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_downsample: if True add downsample block. - downsample_padding: padding used in the downsampling block. - num_head_channels: number of channels in each attention head. - """ - super().__init__() - resnets = [] - attentions = [] - - for i in range(num_res_blocks): - in_channels = in_channels if i == 0 else out_channels - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - attentions.append( - AttentionBlock( - spatial_dims=spatial_dims, - num_channels=out_channels, - num_head_channels=num_head_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - - self.attentions = nn.ModuleList(attentions) - self.resnets = nn.ModuleList(resnets) - - if add_downsample: - self.downsampler = Downsample( - spatial_dims=spatial_dims, - num_channels=out_channels, - use_conv=True, - out_channels=out_channels, - padding=downsample_padding, - ) - else: - self.downsampler = None - - def forward( - self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None - ) -> Tuple[torch.Tensor, List[torch.Tensor]]: - del context - output_states = [] - - for resnet, attn in zip(self.resnets, self.attentions): - hidden_states = resnet(hidden_states, temb) - hidden_states = attn(hidden_states) - output_states.append(hidden_states) - - if self.downsampler is not None: - hidden_states = self.downsampler(hidden_states) - output_states.append(hidden_states) - - return hidden_states, output_states - - -class CrossAttnDownBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_downsample: bool = True, - downsample_padding: int = 1, - num_head_channels: int = 1, - transformer_num_layers: int = 1, - cross_attention_dim: Optional[int] = None, - ) -> None: - """ - Unet's down block containing resnet, downsamplers and cross-attention blocks. - - Args: - spatial_dims: number of spatial dimensions. - in_channels: number of input channels. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_downsample: if True add downsample block. - downsample_padding: padding used in the downsampling block. - num_head_channels: number of channels in each attention head. - transformer_num_layers: number of layers of Transformer blocks to use. - cross_attention_dim: number of context dimensions to use. - """ - super().__init__() - resnets = [] - attentions = [] - - for i in range(num_res_blocks): - in_channels = in_channels if i == 0 else out_channels - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - - attentions.append( - SpatialTransformer( - spatial_dims=spatial_dims, - in_channels=out_channels, - num_attention_heads=out_channels // num_head_channels, - num_head_channels=num_head_channels, - num_layers=transformer_num_layers, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - cross_attention_dim=cross_attention_dim, - ) - ) - - self.attentions = nn.ModuleList(attentions) - self.resnets = nn.ModuleList(resnets) - - if add_downsample: - self.downsampler = Downsample( - spatial_dims=spatial_dims, - num_channels=out_channels, - use_conv=True, - out_channels=out_channels, - padding=downsample_padding, - ) - else: - self.downsampler = None - - def forward( - self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None - ) -> Tuple[torch.Tensor, List[torch.Tensor]]: - output_states = [] - - for resnet, attn in zip(self.resnets, self.attentions): - hidden_states = resnet(hidden_states, temb) - hidden_states = attn(hidden_states, context=context) - output_states.append(hidden_states) - - if self.downsampler is not None: - hidden_states = self.downsampler(hidden_states) - output_states.append(hidden_states) - - return hidden_states, output_states - - -class AttnMidBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - temb_channels: int, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - num_head_channels: int = 1, - ) -> None: - """ - Unet's mid block containing resnet and self-attention blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - temb_channels: number of timestep embedding channels. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - num_head_channels: number of channels in each attention head. - """ - super().__init__() - self.attention = None - - self.resnet_1 = ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - self.attention = AttentionBlock( - spatial_dims=spatial_dims, - num_channels=in_channels, - num_head_channels=num_head_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - - self.resnet_2 = ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - - def forward( - self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None - ) -> torch.Tensor: - del context - hidden_states = self.resnet_1(hidden_states, temb) - hidden_states = self.attention(hidden_states) - hidden_states = self.resnet_2(hidden_states, temb) - - return hidden_states - - -class CrossAttnMidBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - temb_channels: int, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - num_head_channels: int = 1, - transformer_num_layers: int = 1, - cross_attention_dim: Optional[int] = None, - ) -> None: - """ - Unet's mid block containing resnet and cross-attention blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - temb_channels: number of timestep embedding channels - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - num_head_channels: number of channels in each attention head. - transformer_num_layers: number of layers of Transformer blocks to use. - cross_attention_dim: number of context dimensions to use. - """ - super().__init__() - self.attention = None - - self.resnet_1 = ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - self.attention = SpatialTransformer( - spatial_dims=spatial_dims, - in_channels=in_channels, - num_attention_heads=in_channels // num_head_channels, - num_head_channels=num_head_channels, - num_layers=transformer_num_layers, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - cross_attention_dim=cross_attention_dim, - ) - self.resnet_2 = ResnetBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - - def forward( - self, hidden_states: torch.Tensor, temb: torch.Tensor, context: Optional[torch.Tensor] = None - ) -> torch.Tensor: - hidden_states = self.resnet_1(hidden_states, temb) - hidden_states = self.attention(hidden_states, context=context) - hidden_states = self.resnet_2(hidden_states, temb) - - return hidden_states - - -class UpBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - prev_output_channel: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_upsample: bool = True, - ) -> None: - """ - Unet's up block containing resnet and upsamplers blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - prev_output_channel: number of channels from residual connection. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_upsample: if True add downsample block. - """ - super().__init__() - resnets = [] - - for i in range(num_res_blocks): - res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels - resnet_in_channels = prev_output_channel if i == 0 else out_channels - - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=resnet_in_channels + res_skip_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - - self.resnets = nn.ModuleList(resnets) - - if add_upsample: - self.upsampler = Upsample( - spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels - ) - else: - self.upsampler = None - - def forward( - self, - hidden_states: torch.Tensor, - res_hidden_states_list: List[torch.Tensor], - temb: torch.Tensor, - context: Optional[torch.Tensor] = None, - ) -> torch.Tensor: - del context - for i, resnet in enumerate(self.resnets): - # pop res hidden states - res_hidden_states = res_hidden_states_list[-1] - res_hidden_states_list = res_hidden_states_list[:-1] - hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) - - hidden_states = resnet(hidden_states, temb) - - if self.upsampler is not None: - hidden_states = self.upsampler(hidden_states) - - return hidden_states - - -class AttnUpBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - prev_output_channel: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_upsample: bool = True, - num_head_channels: int = 1, - ) -> None: - """ - Unet's up block containing resnet, upsamplers, and self-attention blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - prev_output_channel: number of channels from residual connection. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_upsample: if True add downsample block. - num_head_channels: number of channels in each attention head. - """ - super().__init__() - resnets = [] - attentions = [] - - for i in range(num_res_blocks): - res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels - resnet_in_channels = prev_output_channel if i == 0 else out_channels - - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=resnet_in_channels + res_skip_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - attentions.append( - AttentionBlock( - spatial_dims=spatial_dims, - num_channels=out_channels, - num_head_channels=num_head_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - - self.resnets = nn.ModuleList(resnets) - self.attentions = nn.ModuleList(attentions) - - if add_upsample: - self.upsampler = Upsample( - spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels - ) - else: - self.upsampler = None - - def forward( - self, - hidden_states: torch.Tensor, - res_hidden_states_list: List[torch.Tensor], - temb: torch.Tensor, - context: Optional[torch.Tensor] = None, - ) -> torch.Tensor: - del context - for resnet, attn in zip(self.resnets, self.attentions): - # pop res hidden states - res_hidden_states = res_hidden_states_list[-1] - res_hidden_states_list = res_hidden_states_list[:-1] - hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) - - hidden_states = resnet(hidden_states, temb) - hidden_states = attn(hidden_states) - - if self.upsampler is not None: - hidden_states = self.upsampler(hidden_states) - - return hidden_states - - -class CrossAttnUpBlock(nn.Module): - def __init__( - self, - spatial_dims: int, - in_channels: int, - prev_output_channel: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int = 1, - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - add_upsample: bool = True, - num_head_channels: int = 1, - transformer_num_layers: int = 1, - cross_attention_dim: Optional[int] = None, - ) -> None: - """ - Unet's up block containing resnet, upsamplers, and self-attention blocks. - - Args: - spatial_dims: The number of spatial dimensions. - in_channels: number of input channels. - prev_output_channel: number of channels from residual connection. - out_channels: number of output channels. - temb_channels: number of timestep embedding channels. - num_res_blocks: number of residual blocks. - norm_num_groups: number of groups for the group normalization. - norm_eps: epsilon for the group normalization. - add_upsample: if True add downsample block. - num_head_channels: number of channels in each attention head. - transformer_num_layers: number of layers of Transformer blocks to use. - cross_attention_dim: number of context dimensions to use. - """ - super().__init__() - resnets = [] - attentions = [] - - for i in range(num_res_blocks): - res_skip_channels = in_channels if (i == num_res_blocks - 1) else out_channels - resnet_in_channels = prev_output_channel if i == 0 else out_channels - - resnets.append( - ResnetBlock( - spatial_dims=spatial_dims, - in_channels=resnet_in_channels + res_skip_channels, - out_channels=out_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - ) - ) - attentions.append( - SpatialTransformer( - spatial_dims=spatial_dims, - in_channels=out_channels, - num_attention_heads=out_channels // num_head_channels, - num_head_channels=num_head_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - ) - - self.attentions = nn.ModuleList(attentions) - self.resnets = nn.ModuleList(resnets) - - if add_upsample: - self.upsampler = Upsample( - spatial_dims=spatial_dims, num_channels=out_channels, use_conv=True, out_channels=out_channels - ) - else: - self.upsampler = None - - def forward( - self, - hidden_states: torch.Tensor, - res_hidden_states_list: List[torch.Tensor], - temb: torch.Tensor, - context: Optional[torch.Tensor] = None, - ) -> torch.Tensor: - for resnet, attn in zip(self.resnets, self.attentions): - # pop res hidden states - res_hidden_states = res_hidden_states_list[-1] - res_hidden_states_list = res_hidden_states_list[:-1] - hidden_states = torch.cat([hidden_states, res_hidden_states], dim=1) - - hidden_states = resnet(hidden_states, temb) - hidden_states = attn(hidden_states, context=context) - - if self.upsampler is not None: - hidden_states = self.upsampler(hidden_states) - - return hidden_states - - -def get_down_block( - spatial_dims: int, - in_channels: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int, - norm_num_groups: int, - norm_eps: float, - add_downsample: bool, - with_attn: bool, - with_cross_attn: bool, - num_head_channels: int, - transformer_num_layers: int, - cross_attention_dim: Optional[int], -) -> nn.Module: - if with_attn: - return AttnDownBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_downsample=add_downsample, - num_head_channels=num_head_channels, - ) - elif with_cross_attn: - return CrossAttnDownBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_downsample=add_downsample, - num_head_channels=num_head_channels, - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - else: - return DownBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_downsample=add_downsample, - ) - - -def get_mid_block( - spatial_dims: int, - in_channels: int, - temb_channels: int, - norm_num_groups: int, - norm_eps: float, - with_conditioning: bool, - num_head_channels: int, - transformer_num_layers: int, - cross_attention_dim: Optional[int], -) -> nn.Module: - if with_conditioning: - return CrossAttnMidBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - num_head_channels=num_head_channels, - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - else: - return AttnMidBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - temb_channels=temb_channels, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - num_head_channels=num_head_channels, - ) - - -def get_up_block( - spatial_dims: int, - in_channels: int, - prev_output_channel: int, - out_channels: int, - temb_channels: int, - num_res_blocks: int, - norm_num_groups: int, - norm_eps: float, - add_upsample: bool, - with_attn: bool, - with_cross_attn: bool, - num_head_channels: int, - transformer_num_layers: int, - cross_attention_dim: Optional[int], -) -> nn.Module: - if with_attn: - return AttnUpBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - prev_output_channel=prev_output_channel, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_upsample=add_upsample, - num_head_channels=num_head_channels, - ) - elif with_cross_attn: - return CrossAttnUpBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - prev_output_channel=prev_output_channel, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_upsample=add_upsample, - num_head_channels=num_head_channels, - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - else: - return UpBlock( - spatial_dims=spatial_dims, - in_channels=in_channels, - prev_output_channel=prev_output_channel, - out_channels=out_channels, - temb_channels=temb_channels, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_upsample=add_upsample, - ) - - -class DiffusionModelEncoder(nn.Module): - """ - Unet network with timestep embedding and attention mechanisms for conditioning based on - Rombach et al. "High-Resolution Image Synthesis with Latent Diffusion Models" https://arxiv.org/abs/2112.10752 - and Pinaya et al. "Brain Imaging Generation with Latent Diffusion Models" https://arxiv.org/abs/2209.07162 - - Args: - spatial_dims: number of spatial dimensions. - in_channels: number of input channels. - out_channels: number of output channels. - num_res_blocks: number of residual blocks (see ResnetBlock) per level. - num_channels: tuple of block output channels. - attention_levels: list of levels to add attention. - norm_num_groups: number of groups for the normalization. - norm_eps: epsilon for the normalization. - num_head_channels: number of channels in each attention head. - with_conditioning: if True add spatial transformers to perform conditioning. - transformer_num_layers: number of layers of Transformer blocks to use. - cross_attention_dim: number of context dimensions to use. - num_class_embeds: if specified (as an int), then this model will be class-conditional with `num_class_embeds` - classes. - """ - - def __init__( - self, - spatial_dims: int, - in_channels: int, - out_channels: int, - num_res_blocks: int, - num_channels: Sequence[int] = (32, 64, 64, 64), - attention_levels: Sequence[bool] = (False, False, True, True), - norm_num_groups: int = 32, - norm_eps: float = 1e-6, - num_head_channels: Union[int, Sequence[int]] = 8, - with_conditioning: bool = False, - transformer_num_layers: int = 1, - cross_attention_dim: Optional[int] = None, - num_class_embeds: Optional[int] = None, - ) -> None: - super().__init__() - if with_conditioning is True and cross_attention_dim is None: - raise ValueError( - ( - "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " - "when using with_conditioning." - ) - ) - if cross_attention_dim is not None and with_conditioning is False: - raise ValueError( - "DiffusionModelUNet expects with_conditioning=True when specifying the cross_attention_dim." - ) - - # All number of channels should be multiple of num_groups - if any((out_channel % norm_num_groups) != 0 for out_channel in num_channels): - raise ValueError("DiffusionModelUNet expects all num_channels being multiple of norm_num_groups") - - if isinstance(num_head_channels, int): - num_head_channels = (num_head_channels,) * len(attention_levels) - - if len(num_head_channels) != len(attention_levels): - raise ValueError( - "num_head_channels should have the same length as attention_levels. For the i levels without attention," - " i.e. `attention_level[i]=False`, the num_head_channels[i] will be ignored." - ) - - self.in_channels = in_channels - self.block_out_channels = num_channels - self.out_channels = out_channels - self.num_res_blocks = num_res_blocks - self.attention_levels = attention_levels - self.num_head_channels = num_head_channels - self.with_conditioning = with_conditioning - - # input - self.conv_in = Convolution( - spatial_dims=spatial_dims, - in_channels=in_channels, - out_channels=num_channels[0], - strides=1, - kernel_size=3, - padding=1, - conv_only=True, - ) - - # time - time_embed_dim = num_channels[0] * 4 - self.time_embed = nn.Sequential( - nn.Linear(num_channels[0], time_embed_dim), - nn.SiLU(), - nn.Linear(time_embed_dim, time_embed_dim), - ) - - # class embedding - self.num_class_embeds = num_class_embeds - if num_class_embeds is not None: - self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim) - - # down - self.down_blocks = nn.ModuleList([]) - output_channel = num_channels[0] - for i in range(len(num_channels)): - input_channel = output_channel - output_channel = num_channels[i] - is_final_block = i == len(num_channels) - 1 - - down_block = get_down_block( - spatial_dims=spatial_dims, - in_channels=input_channel, - out_channels=output_channel, - temb_channels=time_embed_dim, - num_res_blocks=num_res_blocks, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_downsample=not is_final_block, - with_attn=(attention_levels[i] and not with_conditioning), - with_cross_attn=(attention_levels[i] and with_conditioning), - num_head_channels=num_head_channels[i], - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - - self.down_blocks.append(down_block) - - # mid - self.middle_block = get_mid_block( - spatial_dims=spatial_dims, - in_channels=num_channels[-1], - temb_channels=time_embed_dim, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - with_conditioning=with_conditioning, - num_head_channels=num_head_channels[-1], - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - - # up - self.up_blocks = nn.ModuleList([]) - reversed_block_out_channels = list(reversed(num_channels)) - reversed_attention_levels = list(reversed(attention_levels)) - reversed_num_head_channels = list(reversed(num_head_channels)) - output_channel = reversed_block_out_channels[0] - for i in range(len(reversed_block_out_channels)): - prev_output_channel = output_channel - output_channel = reversed_block_out_channels[i] - input_channel = reversed_block_out_channels[min(i + 1, len(num_channels) - 1)] - - is_final_block = i == len(num_channels) - 1 - - up_block = get_up_block( - spatial_dims=spatial_dims, - in_channels=input_channel, - prev_output_channel=prev_output_channel, - out_channels=output_channel, - temb_channels=time_embed_dim, - num_res_blocks=num_res_blocks + 1, - norm_num_groups=norm_num_groups, - norm_eps=norm_eps, - add_upsample=not is_final_block, - with_attn=(reversed_attention_levels[i] and not with_conditioning), - with_cross_attn=(reversed_attention_levels[i] and with_conditioning), - num_head_channels=reversed_num_head_channels[i], - transformer_num_layers=transformer_num_layers, - cross_attention_dim=cross_attention_dim, - ) - - self.up_blocks.append(up_block) - - # out - self.out = nn.Sequential( - nn.GroupNorm(num_groups=norm_num_groups, num_channels=num_channels[0], eps=norm_eps, affine=True), - nn.SiLU(), - zero_module( - Convolution( - spatial_dims=spatial_dims, - in_channels=num_channels[0], - out_channels=out_channels, - strides=1, - kernel_size=3, - padding=1, - conv_only=True, - ) - ), - ) - - def forward( - self, - x: torch.Tensor, - timesteps: torch.Tensor, - context: Optional[torch.Tensor] = None, - class_labels: Optional[torch.Tensor] = None, - ) -> torch.Tensor: - """ - Args: - x: input tensor (N, C, SpatialDims). - timesteps: timestep tensor (N,). - context: context tensor (N, 1, ContextDim). - class_labels: context tensor (N, ). - """ - # 1. time - t_emb = get_timestep_embedding(timesteps, self.block_out_channels[0]) - emb = self.time_embed(t_emb) - - # 2. class - if self.num_class_embeds is not None: - if class_labels is None: - raise ValueError("class_labels should be provided when num_class_embeds > 0") - class_emb = self.class_embedding(class_labels) - emb = emb + class_emb - - # 3. initial convolution - h = self.conv_in(x) - - # 4. down - if context is not None and self.with_conditioning is False: - raise ValueError("model should have with_conditioning = True if context is provided") - down_block_res_samples: List[torch.Tensor] = [h] - for downsample_block in self.down_blocks: - h, res_samples = downsample_block(hidden_states=h, temb=emb, context=context) - for residual in res_samples: - down_block_res_samples.append(residual) - h=h.flatten() - print('h', h.shape) - - # # 5. mid - # h = self.middle_block(hidden_states=h, temb=emb, context=context) - # - # # 6. up - # for upsample_block in self.up_blocks: - # res_samples = down_block_res_samples[-len(upsample_block.resnets) :] - # down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)] - # h = upsample_block(hidden_states=h, res_hidden_states_list=res_samples, temb=emb, context=context) - - # 7. output block - - self.out = nn.Linear(len(h), self.out_channels) - output=self.out(h) - - return output diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 59492ab4..a264cd89 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1656,8 +1656,7 @@ def forward( return h - - + class DiffusionModelEncoder(nn.Module): """ Classification Network based on the Encoder of the Diffusion Model, followed by fully connected layers for classification @@ -1759,7 +1758,7 @@ def __init__( for i in range(len(num_channels)): input_channel = output_channel output_channel = num_channels[i] - is_final_block = i == len(num_channels) #- 1 + is_final_block = i == len(num_channels) # - 1 down_block = get_down_block( spatial_dims=spatial_dims, @@ -1779,16 +1778,12 @@ def __init__( self.down_blocks.append(down_block) - - self.out = nn.Sequential( - nn.Linear(8192, 512), - nn.ReLU(), - nn.Dropout(0.2), + nn.Linear(4096, 512), + nn.ReLU(), + nn.Dropout(0.1), nn.Linear(512, self.out_channels), - ) - - + ) def forward( self, @@ -1825,6 +1820,6 @@ def forward( h, _ = downsample_block(hidden_states=h, temb=emb, context=context) h = h.reshape(h.shape[0], -1) - output=self.out(h) + output = self.out(h) return output diff --git a/generative/networks/schedulers/ddim.py b/generative/networks/schedulers/ddim.py index fba6d406..6acc253a 100644 --- a/generative/networks/schedulers/ddim.py +++ b/generative/networks/schedulers/ddim.py @@ -223,8 +223,6 @@ def step( return pred_prev_sample, pred_original_sample - - def reversed_step( self, model_output: torch.Tensor, @@ -249,8 +247,7 @@ def reversed_step( pred_prev_sample: Predicted previous sample pred_original_sample: Predicted original sample """ - # See formulas (12) and (16) of DDIM paper https://arxiv.org/pdf/2010.02502.pdf - # Ideally, read DDIM paper in-detail understanding + # See Appendix F at https://arxiv.org/pdf/2105.05233.pdf, or Equation (6) in https://arxiv.org/pdf/2203.04306.pdf # Notation ( -> # - model_output -> e_theta(x_t, t) @@ -258,17 +255,20 @@ def reversed_step( # - std_dev_t -> sigma_t # - eta -> η # - pred_sample_direction -> "direction pointing to x_t" - # - pred_prev_sample -> "x_t-1" + # - pred_post_sample -> "x_t+1" # 1. get previous step value (=t-1) - prev_timestep = timestep - self.num_train_timesteps // self.num_inference_steps #t-1 - post_timestep = timestep + self.num_train_timesteps // self.num_inference_steps #t+1 + prev_timestep = timestep - self.num_train_timesteps // self.num_inference_steps # t-1 + post_timestep = timestep + self.num_train_timesteps // self.num_inference_steps # t+1 # 2. compute alphas, betas alpha_prod_t = self.alphas_cumprod[timestep] - alpha_prod_t_prev = self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod #alpha at timestep t-1 - alpha_prod_t_post = self.alphas_cumprod[post_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod #alpha at timestep t+1 - + alpha_prod_t_prev = ( + self.alphas_cumprod[prev_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod + ) # alpha at timestep t-1 + alpha_prod_t_post = ( + self.alphas_cumprod[post_timestep] if prev_timestep >= 0 else self.final_alpha_cumprod + ) # alpha at timestep t+1 beta_prod_t = 1 - alpha_prod_t @@ -302,14 +302,9 @@ def reversed_step( # randn_like does not support generator https://github.com/pytorch/pytorch/issues/27072 device = model_output.device if torch.is_tensor(model_output) else "cpu" noise = torch.randn(model_output.shape, dtype=model_output.dtype, generator=generator).to(device) - variance = self._get_variance(timestep, prev_timestep) ** (0.5) * eta * noise - - pred_prev_sample = pred_prev_sample + variance return pred_post_sample, pred_original_sample - - def add_noise( self, original_samples: torch.Tensor, diff --git a/tutorials/Untitled.ipynb b/tutorials/Untitled.ipynb deleted file mode 100644 index c0c04ff7..00000000 --- a/tutorials/Untitled.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "761199be-b371-4cb7-a66f-ae739eccb554", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/generative/2d_vqvae/2d_vqvae_tutorial.py b/tutorials/generative/2d_vqvae/2d_vqvae_tutorial.py index b1e313fe..f1ac0a53 100644 --- a/tutorials/generative/2d_vqvae/2d_vqvae_tutorial.py +++ b/tutorials/generative/2d_vqvae/2d_vqvae_tutorial.py @@ -42,7 +42,6 @@ import matplotlib.pyplot as plt import numpy as np import torch -import torch.nn.functional as F from monai import transforms from monai.apps import MedNISTDataset from monai.config import print_config diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb index fbeaf69c..4a5f4384 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb @@ -5,7 +5,7 @@ "id": "63d95da6", "metadata": {}, "source": [ - "# Anomaly Detection with classifier guidance\n", + "# Weakly Supervised Anomaly Detection with Classifier Guidance\n", "\n", "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", "\n", @@ -158,22 +158,16 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "972ed3f3", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false - } + }, + "lines_to_next_cell": 2 }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Setting up a new session...\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -248,23 +242,9 @@ "\n", "# TODO: Add right import reference after deployed\n", "from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder\n", - "\n", "from generative.networks.schedulers.ddpm import DDPMScheduler\n", "from generative.networks.schedulers.ddim import DDIMScheduler\n", - "print_config()\n", - "\n", - "losstrain_window = viz.line(Y=torch.zeros((1)).cpu(), X=torch.zeros((1)).cpu(),\n", - " opts=dict(xlabel='epoch', ylabel='Loss', title='training loss'))\n", - "lossval_window = viz.line(Y=torch.zeros((1)).cpu(), X=torch.zeros((1)).cpu(),\n", - " opts=dict(xlabel='epoch', ylabel='Loss', title='val loss '))\n", - "\n", - "train_classifier=False\n", - "train_diffusionmodel=False\n", - "def visualize(img):\n", - " _min = img.min()\n", - " _max = img.max()\n", - " normalized_img = (img - _min)/ (_max - _min)\n", - " return normalized_img" + "print_config()" ] }, { @@ -277,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "8b4323e7", "metadata": { "collapsed": false, @@ -302,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "34ea510f", "metadata": { "collapsed": false, @@ -312,7 +292,7 @@ }, "outputs": [], "source": [ - "set_determinism(36)" + "set_determinism(42)" ] }, { @@ -322,20 +302,32 @@ "tags": [] }, "source": [ - "## Setup BRATS Dataset for 2D slices and training and validation dataloaders\n", - "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150" + "## Setup BRATS Dataset in 2D slices for training\n", + "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150.\n", + "If we set `preprocessing_train=True`, we stack all slices into a tensor and save it as _total_train_slices.pt_. \n", + "If we set `preprocessing_train=False`, we load the saved tensor.\n", + "The corresponding labels are saved as _total_train_labels.pt._" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 29, - "id": "da1927b0", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, + "execution_count": 5, + "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", + "metadata": {}, "outputs": [ { "name": "stderr", @@ -344,22 +336,9 @@ "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/utils/deprecate_utils.py:107: FutureWarning: : Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n", " warn_deprecated(obj, msg, warning_category)\n" ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "download training set\n", - "len train data 388\n", - "total slices torch.Size([17072, 1, 64, 64])\n", - "total lbaels torch.Size([17072])\n", - "download val set\n" - ] } ], "source": [ - "\n", - "\n", "channel = 0 # 0 = Flair\n", "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", "\n", @@ -381,8 +360,32 @@ " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", " ]\n", - ")\n", - "print('download training set')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "da1927b0", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "len train data 388\n", + "total slices torch.Size([17072, 1, 64, 64])\n", + "total lbaels torch.Size([17072])\n" + ] + } + ], + "source": [ + "\n", "train_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", " task=\"Task01_BrainTumour\",\n", @@ -403,6 +406,7 @@ " return batched_2d_slices, slice_label\n", "\n", "preprocessing_train=False\n", + "\n", "if preprocessing_train == True:\n", " train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", " print(f'Image shape {train_ds[0][\"image\"].shape}')\n", @@ -435,8 +439,11 @@ "tags": [] }, "source": [ - "## Setup BRATS Dataset for 2D slices validation dataloader\n", - "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150" + "## Setup BRATS Dataset in 2D slices for validation \n", + "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150.\n", + "If we set `preprocessing_val=True`, we stack all slices into a tensor and save it as _total_val_slices.pt_.\n", + "If we set `preprocessing_val=False`, we load the saved tensor.\n", + "The corresponding labels are saved as _total_val_labels.pt_." ] }, { @@ -492,20 +499,6 @@ " print('total lbaels', total_val_labels.shape)" ] }, - { - "cell_type": "markdown", - "id": "6986f55c", - "metadata": {}, - "source": [ - "Here we use transforms to augment the training dataset, as usual:\n", - "\n", - "1. `LoadImaged` loads the hands images from files.\n", - "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", - "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", - "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", - "\n" - ] - }, { "cell_type": "markdown", "id": "08428bc6", @@ -513,8 +506,8 @@ "source": [ "### Define network, scheduler, optimizer, and inferer\n", "At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", - "the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms\n", - "in the 3rd level, each with 1 attention head (`num_head_channels=64`).\n" + "the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms\n", + "in the 3rd level (`num_head_channels=64`).\n" ] }, { @@ -526,7 +519,7 @@ "jupyter": { "outputs_hidden": false }, - "lines_to_next_cell": 0 + "lines_to_next_cell": 2 }, "outputs": [], "source": [ @@ -562,7 +555,8 @@ }, "source": [ "### Model training of the Diffusion Model\n", - "Here, we are training our diffusion model for 75 epochs (training time: ~50 minutes)." + "If we set `train_diffusionmodel=True`, we are training our diffusion model for 75 epochs, and save the model as _diffusion_model.pt_.\n", + "If we set `train_diffusionmodel=False`, we load a pretrained model." ] }, { @@ -577,14 +571,16 @@ }, "outputs": [], "source": [ - "n_epochs =75\n", + "n_epochs =1\n", "batch_size=32\n", "val_interval = 1\n", "epoch_loss_list = []\n", "val_epoch_loss_list = []\n", + "\n", "train_diffusionmodel=False\n", + "\n", "if train_diffusionmodel==False:\n", - " model.load_state_dict(torch.load(\"./diffusion_model.pt\", map_location={'cuda:0': 'cpu'}))\n", + " model.load_state_dict(torch.load(\"model.pt\", map_location={'cuda:0': 'cpu'}))\n", "else:\n", " scaler = GradScaler()\n", " total_start = time.time()\n", @@ -598,13 +594,13 @@ "\n", " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", "\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes), ncols=10)\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", " progress_bar.set_description(f\"Epoch {epoch}\")\n", " for step, (a,b) in progress_bar:\n", " images = a.to(device)\n", " classes = b.to(device)\n", " optimizer.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device) #pick a random time step t\n", "\n", " with autocast(enabled=True):\n", " # Generate random noise\n", @@ -618,11 +614,7 @@ " scaler.scale(loss).backward()\n", " scaler.step(optimizer)\n", " scaler.update()\n", - " if step%20==0:\n", - " print('step', step, loss)\n", - "\n", " epoch_loss += loss.item()\n", - "\n", " progress_bar.set_postfix(\n", " {\n", " \"loss\": epoch_loss / (step + 1),\n", @@ -681,30 +673,27 @@ }, { "cell_type": "markdown", - "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", + "id": "546f9983-c2e2-4c24-b03a-ebe34627638a", "metadata": {}, "source": [ - "### Model training of the Classification Model\n", - "#First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n", - "#Here, we are training our binary classification model for 20 epochs." + "## Define the Classification Model\n", + "First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 10, "id": "44cc6928-2525-4e61-8805-15b409097bbb", "metadata": { "lines_to_next_cell": 2 }, "outputs": [], "source": [ - "\n", - "\n", "classifier = DiffusionModelEncoder(\n", " spatial_dims=2,\n", " in_channels=1,\n", " out_channels=2,\n", - " num_channels=(32,64,128),\n", + " num_channels=(32,64,64),\n", " attention_levels=(False, True, True),\n", " num_res_blocks=1,\n", " num_head_channels=64,\n", @@ -714,9 +703,19 @@ "batch_size=32" ] }, + { + "cell_type": "markdown", + "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", + "metadata": {}, + "source": [ + "## Model training of the Classification Model\n", + "If we set `train_classifier=True`, we are training our diffusion model for 100 epochs, and save the model as _classifier.pt_.\n", + "If we set `train_classifier=False`, we load a pretrained model." + ] + }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 110, "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", "metadata": { "lines_to_next_cell": 0 @@ -726,7 +725,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: : 534it [00:29, 18.32it/s, loss=0.576] \n" + "Epoch 0: : 534it [00:24, 21.51it/s, loss=0.534] \n" ] }, { @@ -740,21 +739,23 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 0: : 132it [00:02, 56.95it/s, val_loss=0.305]\n" + "Epoch 0: : 132it [00:01, 66.23it/s, val_loss=0.259]\n", + "Epoch 1: : 534it [00:27, 19.68it/s, loss=0.538] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "final step val 131\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 1: : 534it [00:29, 18.34it/s, loss=0.576] \n" + "Epoch 1: : 132it [00:02, 57.32it/s, val_loss=0.28] \n", + "Epoch 2: : 534it [00:27, 19.43it/s, loss=0.531] \n" ] }, { @@ -768,21 +769,23 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 1: : 132it [00:02, 56.37it/s, val_loss=0.315]\n" + "Epoch 2: : 132it [00:02, 60.17it/s, val_loss=0.32] \n", + "Epoch 3: : 534it [00:27, 19.41it/s, loss=0.539] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "final step val 131\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 2: : 534it [00:31, 16.99it/s, loss=0.573] \n" + "Epoch 3: : 132it [00:02, 58.98it/s, val_loss=0.294]\n", + "Epoch 4: : 534it [00:27, 19.40it/s, loss=0.536] \n" ] }, { @@ -796,21 +799,23 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 2: : 132it [00:02, 52.98it/s, val_loss=0.312]\n" + "Epoch 4: : 132it [00:02, 59.34it/s, val_loss=0.256]\n", + "Epoch 5: : 534it [00:27, 19.47it/s, loss=0.536] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "final step val 131\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 3: : 534it [00:31, 17.21it/s, loss=0.572] \n" + "Epoch 5: : 132it [00:02, 59.58it/s, val_loss=0.278]\n", + "Epoch 6: : 534it [00:27, 19.49it/s, loss=0.53] \n" ] }, { @@ -824,21 +829,23 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 3: : 132it [00:02, 53.04it/s, val_loss=0.334]\n" + "Epoch 6: : 132it [00:02, 59.91it/s, val_loss=0.29] \n", + "Epoch 7: : 534it [00:27, 19.58it/s, loss=0.533] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "final step val 131\n" + "final step train 533\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Epoch 4: : 534it [00:31, 17.16it/s, loss=0.569] \n" + "Epoch 7: : 132it [00:02, 59.94it/s, val_loss=0.271]\n", + "Epoch 8: : 534it [00:27, 19.67it/s, loss=0.54] \n" ] }, { @@ -852,166 +859,1533 @@ "name": "stderr", "output_type": "stream", "text": [ - "Epoch 4: : 132it [00:02, 52.93it/s, val_loss=0.36] \n" + "Epoch 8: : 132it [00:02, 60.28it/s, val_loss=0.261]\n", + "Epoch 9: : 534it [00:26, 19.84it/s, loss=0.535] \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "final step val 131\n", - "train completed, total time: 167.07577848434448.\n", - "epl 5\n" + "final step train 533\n" ] }, { - "data": { - "image/png": "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aDk4AUCXLl0QHByMzZs3W9wHERER1Q82H55Onz6Ntm3bmoQaAAgPD5e3V2bw4MFQq9Xw9vZGbGxsmX1Kvi9p88F+qtIHERERPRxs/rJdRkYGgoKCypR7e3vL28vTpEkTTJ8+HVFRUfDw8MCpU6ewYMECREVF4cCBA4iIiDBpo6TNB/upqI/CwkIUFhbK3+fk5FRtYkRERGSXbD48AYAkSdXaNmDAAAwYMED+vnv37hg0aBDCwsIwc+ZMbNmypUptVdTH/PnzMWfOnHK3ExERUf1i85ftfHx8zJ75yczMBGD+bFFFWrRogW7duiEpKcmkD8D8WazMzMwK+5g2bRqys7PlT3JysqLxEBERkX2x+fAUFhaGc+fOQa/Xm5SfOnUKQPGddEoJIaBS/TH1kjZK2nywn4r6cHR0hIeHh8mHiIiI6i+bD09Dhw5FXl4eNm7caFIeHx8PPz8/dO7cWVF7V65cwYEDBxAVFSWXNWvWDJ06dcLq1athMBjk8qSkJJw/fx6xsbGWTYKIiIjqDZtf8zRw4ED07dsXkydPRk5ODlq3bo2EhARs27YNq1evlp/xNGHCBMTHx+PSpUsIDAwEAPTp0wfdu3dHeHi4vGB84cKFkCQJ7777rkk/7733Hvr27Ythw4bhxRdfRFpaGt58802EhoZi3LhxtT5vIiIisk02H54AYNOmTZg+fTpmzpyJzMxMhISEICEhASNGjJDrGAwGGAwGCCHksrCwMKxfvx4ffPAB8vPz4evri169euHtt99GcHCwSR89e/ZEYmIiZs6ciSFDhsDFxQWDBw/G+++/z6eLExERkUwSpdMGWSwnJweenp7Izs7m+iciIiI7oeTvt82veSIiIiKyJQxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQIMT0REREQKMDwRERERKcDwRERERKQAwxMRERGRAgxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQIMT0REREQKMDwRERERKcDwRERERKQAwxMRERGRAgxPRERERAowPBEREREpwPBEREREpADDExEREZECDE9ERERECjA8ERERESnA8ERERESkAMMTERERkQKauh4AERHVTzqdDgaDoa6H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szRs2LDMXXel9erVy1pdEREREdUZq7/bLj8/H5cuXUJubi7c3d3RqlUrODs7W7sbIiIiojphtfeObN++HT179oSnpyciIiLQrVs3REREwNPTE7169cKOHTus1RUREdFDZ/bs2ZAkSX4cUAlJksy+y1VpO9Y0duxYSJKEq1ev1lgfdckq4Wn27NmIiYnB3r17odfrodVq4efnB61WC71ej927d2PgwIGYPXu2NbojIiKyOXFxcZAkCevWrauwXkZGBhwdHdGwYUMUFRXV0uisa9WqVZAkCatWrarrodQJi8PTtm3b8M4770ClUuHFF1/E+fPnUVBQgOTkZBQUFOD8+fN48cUX5Rfvbt++3RrjJiIisikTJkwA8Mfd5eVZvXo1ioqKMHr0aJNXm1XXuXPn8J///Mfidqxp/vz5OHfuHJo1a1bXQ6kRFoenf/3rX5AkCStWrMBHH32ENm3amGxv06YNPvroI6xYsQJCCCxZssTSLomIiGxO79690aJFC+zcuRPJycnl1isJVyVhy1IhISFo3ry5VdqylqZNmyIkJARarbauh1IjLA5PR44cgb+/P0aPHl1hvVGjRiEgIACHDx+2tEsiIiKbI0kSxo0bB6PRiPj4eLN1fv75Z5w4cQKdOnWCt7c3Zs2ahaioKPj6+sLR0REtWrTAiy++iLS0NEX9mlvzlJycjLi4OHh7e8PNzQ09evTA3r17zbZRVFSEpUuXon///ggICICjoyN8fX0RGxuLX375xaTu2LFjMW7cOADAuHHjIEmS/Cldp7w1T/Hx8YiKioKbmxvc3NwQFRVl9ue1e/duSJKE2bNn49ixY+jfvz/c3d3h6emJoUOH1ul6KovDU25ubpVfudK4cWPcvXvX0i6JiIhs0rhx46BSqbBq1SoIIcpsL33Wae/evVi0aBEaN26MuLg4vPLKK2jVqhU++eQTREdHIzs7u9rj+P333xEdHY1169ahU6dO+Nvf/gZvb2/07dtXfp1aaZmZmZg6dSoKCwsRExOD//f//h969uyJxMREdOnSBUeOHJHrPv3003jqqacAAE899RRmzZolfyrz//7f/8PYsWNx48YNTJgwARMnTkRKSgrGjh2Lv//972b3OXr0KJ544gloNBq88MIL6NixI/773/+iT58+KCgoqOZPyELCQi1bthTu7u4iLy+vwnp5eXnCzc1NtGzZ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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "n_epochs = 5\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", - "\n", - "classifier.to(device)\n", - "\n", - "train_classifier=False\n", - "if train_classifier==False:\n", - " classifier.load_state_dict(torch.load(\"./classifier5.pt\", map_location={'cuda:0': 'cpu'}))\n", - "else:\n", - "\n", - " scaler = GradScaler()\n", - " total_start = time.time()\n", - " for epoch in range(n_epochs):\n", - " classifier.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - "\n", - " for step, (a,b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset\n", - " optimizer_cls.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - "\n", - " with autocast(enabled=False):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image\n", - " pred=classifier(noisy_img, timesteps)\n", - " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", - "\n", - " loss.backward()\n", - " optimizer_cls.step()\n", - "\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - " print('final step train', step)\n", - "\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " classifier.eval()\n", - " val_epoch_loss = 0\n", - " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", - " for step, (a,b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise\n", - "\n", - " with torch.no_grad():\n", - " with autocast(enabled=False):\n", - " noise = torch.randn_like(images).to(device)\n", - " pred = classifier(images, timesteps)\n", - " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " _, predicted = torch.max(pred, 1);\n", - " progress_bar_val.set_postfix(\n", - " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - " print('final step val', step)\n", - "\n", - "\n", - " total_time = time.time() - total_start\n", - " print(f\"train completed, total time: {total_time}.\")\n", - " torch.save(classifier.state_dict(), \"./classifier5.pt\")\n", - " \n", - " ## Learning curves for the Classifier\n", - " \n", - " plt.style.use(\"seaborn-bright\")\n", - " plt.title(\"Learning Curves\", fontsize=20)\n", - " print('epl', len(epoch_loss_list))\n", - " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - " plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - " plt.yticks(fontsize=12)\n", - " plt.xticks(fontsize=12)\n", - " plt.xlabel(\"Epochs\", fontsize=16)\n", - " plt.ylabel(\"Loss\", fontsize=16)\n", - " plt.legend(prop={\"size\": 14})\n", - " plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "a676b3fe", - "metadata": {}, - "source": [ - "### For Image-to-Image Translation to a Healthy Subject, we pick a disesed subject of the validation set" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 9: : 132it [00:02, 59.37it/s, val_loss=0.243]\n", + "Epoch 10: : 534it [00:27, 19.77it/s, loss=0.537] \n" + ] }, { - "data": { - "text/plain": [ - "DiffusionModelEncoder(\n", - " (conv_in): Convolution(\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 10: : 132it [00:02, 60.28it/s, val_loss=0.274]\n", + "Epoch 11: : 534it [00:26, 19.79it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 11: : 132it [00:02, 60.96it/s, val_loss=0.278]\n", + "Epoch 12: : 534it [00:26, 19.95it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 12: : 132it [00:02, 60.47it/s, val_loss=0.292]\n", + "Epoch 13: : 534it [00:26, 19.90it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 13: : 132it [00:02, 59.46it/s, val_loss=0.248]\n", + "Epoch 14: : 534it [00:26, 19.80it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 14: : 132it [00:02, 61.53it/s, val_loss=0.284]\n", + "Epoch 15: : 534it [00:26, 19.92it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 15: : 132it [00:02, 61.58it/s, val_loss=0.273]\n", + "Epoch 16: : 534it [00:26, 19.88it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 16: : 132it [00:02, 61.05it/s, val_loss=0.267]\n", + "Epoch 17: : 534it [00:26, 19.95it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 17: : 132it [00:02, 60.88it/s, val_loss=0.26] \n", + "Epoch 18: : 534it [00:26, 19.83it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 18: : 132it [00:02, 60.91it/s, val_loss=0.318]\n", + "Epoch 19: : 534it [00:26, 20.29it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 19: : 132it [00:02, 63.26it/s, val_loss=0.265]\n", + "Epoch 20: : 534it [00:26, 19.84it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 20: : 132it [00:02, 60.27it/s, val_loss=0.289]\n", + "Epoch 21: : 534it [00:26, 20.11it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 21: : 132it [00:02, 60.49it/s, val_loss=0.27] \n", + "Epoch 22: : 534it [00:26, 19.89it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 22: : 132it [00:02, 60.80it/s, val_loss=0.322]\n", + "Epoch 23: : 534it [00:26, 20.02it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 23: : 132it [00:02, 60.56it/s, val_loss=0.3] \n", + "Epoch 24: : 534it [00:26, 20.01it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 24: : 132it [00:02, 60.69it/s, val_loss=0.287]\n", + "Epoch 25: : 534it [00:26, 20.00it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 25: : 132it [00:02, 61.55it/s, val_loss=0.263]\n", + "Epoch 26: : 534it [00:26, 20.04it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 26: : 132it [00:02, 61.16it/s, val_loss=0.328]\n", + "Epoch 27: : 534it [00:26, 19.95it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 27: : 132it [00:02, 61.15it/s, val_loss=0.263]\n", + "Epoch 28: : 534it [00:26, 20.06it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 28: : 132it [00:02, 60.67it/s, val_loss=0.292]\n", + "Epoch 29: : 534it [00:26, 20.10it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 29: : 132it [00:02, 61.57it/s, val_loss=0.294]\n", + "Epoch 30: : 534it [00:26, 19.98it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 30: : 132it [00:02, 61.60it/s, val_loss=0.284]\n", + "Epoch 31: : 534it [00:26, 20.08it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 31: : 132it [00:02, 61.04it/s, val_loss=0.276]\n", + "Epoch 32: : 534it [00:26, 19.86it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 32: : 132it [00:02, 62.82it/s, val_loss=0.285]\n", + "Epoch 33: : 534it [00:26, 20.13it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 33: : 132it [00:02, 61.12it/s, val_loss=0.277]\n", + "Epoch 34: : 534it [00:26, 20.05it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 34: : 132it [00:02, 61.71it/s, val_loss=0.278]\n", + "Epoch 35: : 534it [00:26, 20.08it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 35: : 132it [00:02, 62.17it/s, val_loss=0.27] \n", + "Epoch 36: : 534it [00:26, 20.21it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 36: : 132it [00:02, 62.01it/s, val_loss=0.267]\n", + "Epoch 37: : 534it [00:26, 20.04it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 37: : 132it [00:02, 61.29it/s, val_loss=0.278]\n", + "Epoch 38: : 534it [00:26, 20.21it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 38: : 132it [00:02, 62.59it/s, val_loss=0.285]\n", + "Epoch 39: : 534it [00:26, 20.13it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 39: : 132it [00:02, 60.36it/s, val_loss=0.279]\n", + "Epoch 40: : 534it [00:26, 20.04it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 40: : 132it [00:02, 61.89it/s, val_loss=0.274]\n", + "Epoch 41: : 534it [00:26, 20.00it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 41: : 132it [00:02, 61.62it/s, val_loss=0.275]\n", + "Epoch 42: : 534it [00:26, 20.11it/s, loss=0.527] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 42: : 132it [00:02, 61.35it/s, val_loss=0.308]\n", + "Epoch 43: : 534it [00:26, 19.83it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 43: : 132it [00:02, 60.97it/s, val_loss=0.31] \n", + "Epoch 44: : 534it [00:26, 20.22it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 44: : 132it [00:02, 61.49it/s, val_loss=0.306]\n", + "Epoch 45: : 534it [00:26, 19.95it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 45: : 132it [00:02, 60.71it/s, val_loss=0.293]\n", + "Epoch 46: : 534it [00:26, 20.02it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 46: : 132it [00:02, 61.20it/s, val_loss=0.254]\n", + "Epoch 47: : 534it [00:26, 19.88it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 47: : 132it [00:02, 61.35it/s, val_loss=0.253]\n", + "Epoch 48: : 534it [00:26, 20.19it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 48: : 132it [00:02, 61.04it/s, val_loss=0.274]\n", + "Epoch 49: : 534it [00:26, 19.95it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 49: : 132it [00:02, 61.74it/s, val_loss=0.28] \n", + "Epoch 50: : 534it [00:26, 20.03it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 50: : 132it [00:02, 59.53it/s, val_loss=0.292]\n", + "Epoch 51: : 534it [00:26, 19.93it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 51: : 132it [00:02, 60.65it/s, val_loss=0.299]\n", + "Epoch 52: : 534it [00:26, 19.89it/s, loss=0.526] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 52: : 132it [00:02, 61.33it/s, val_loss=0.279]\n", + "Epoch 53: : 534it [00:26, 20.04it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 53: : 132it [00:02, 61.07it/s, val_loss=0.282]\n", + "Epoch 54: : 534it [00:26, 19.83it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 54: : 132it [00:02, 60.71it/s, val_loss=0.296]\n", + "Epoch 55: : 534it [00:26, 20.05it/s, loss=0.521] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 55: : 132it [00:02, 60.82it/s, val_loss=0.296]\n", + "Epoch 56: : 534it [00:26, 19.90it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 56: : 132it [00:02, 60.35it/s, val_loss=0.288]\n", + "Epoch 57: : 534it [00:26, 19.92it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 57: : 132it [00:02, 60.57it/s, val_loss=0.279]\n", + "Epoch 58: : 534it [00:27, 19.76it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 58: : 132it [00:02, 61.55it/s, val_loss=0.265]\n", + "Epoch 59: : 534it [00:26, 19.85it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 59: : 132it [00:02, 60.26it/s, val_loss=0.309]\n", + "Epoch 60: : 534it [00:26, 19.94it/s, loss=0.521] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 60: : 132it [00:02, 60.22it/s, val_loss=0.284]\n", + "Epoch 61: : 534it [00:27, 19.57it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 61: : 132it [00:02, 58.50it/s, val_loss=0.267]\n", + "Epoch 62: : 534it [00:27, 19.67it/s, loss=0.527] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 62: : 132it [00:02, 61.49it/s, val_loss=0.278]\n", + "Epoch 63: : 534it [00:27, 19.65it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 63: : 132it [00:02, 59.89it/s, val_loss=0.291]\n", + "Epoch 64: : 534it [00:27, 19.59it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 64: : 132it [00:02, 61.56it/s, val_loss=0.31] \n", + "Epoch 65: : 534it [00:27, 19.39it/s, loss=0.517] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 65: : 132it [00:02, 55.48it/s, val_loss=0.353]\n", + "Epoch 66: : 534it [00:28, 19.05it/s, loss=0.516] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 66: : 132it [00:02, 56.32it/s, val_loss=0.294]\n", + "Epoch 67: : 534it [00:27, 19.12it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 67: : 132it [00:02, 57.70it/s, val_loss=0.303]\n", + "Epoch 68: : 534it [00:27, 19.11it/s, loss=0.521] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 68: : 132it [00:02, 56.41it/s, val_loss=0.278]\n", + "Epoch 69: : 534it [00:27, 19.10it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 69: : 132it [00:02, 58.40it/s, val_loss=0.302]\n", + "Epoch 70: : 534it [00:27, 19.32it/s, loss=0.517] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 70: : 132it [00:02, 59.59it/s, val_loss=0.285]\n", + "Epoch 71: : 534it [00:27, 19.31it/s, loss=0.518] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 71: : 132it [00:02, 58.24it/s, val_loss=0.302]\n", + "Epoch 72: : 534it [00:27, 19.33it/s, loss=0.525] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 72: : 132it [00:02, 59.33it/s, val_loss=0.301]\n", + "Epoch 73: : 534it [00:27, 19.47it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 73: : 132it [00:02, 59.77it/s, val_loss=0.301]\n", + "Epoch 74: : 534it [00:26, 19.83it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 74: : 132it [00:02, 60.28it/s, val_loss=0.321]\n", + "Epoch 75: : 534it [00:26, 19.82it/s, loss=0.523] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 75: : 132it [00:02, 59.62it/s, val_loss=0.3] \n", + "Epoch 76: : 534it [00:26, 19.90it/s, loss=0.518] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 76: : 132it [00:02, 60.27it/s, val_loss=0.292]\n", + "Epoch 77: : 534it [00:26, 19.87it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 77: : 132it [00:02, 60.70it/s, val_loss=0.302]\n", + "Epoch 78: : 534it [00:26, 19.78it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 78: : 132it [00:02, 59.56it/s, val_loss=0.292]\n", + "Epoch 79: : 534it [00:26, 19.85it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 79: : 132it [00:02, 61.08it/s, val_loss=0.29] \n", + "Epoch 80: : 534it [00:27, 19.76it/s, loss=0.518] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 80: : 132it [00:02, 59.27it/s, val_loss=0.305]\n", + "Epoch 81: : 534it [00:26, 19.92it/s, loss=0.517] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 81: : 132it [00:02, 61.01it/s, val_loss=0.314]\n", + "Epoch 82: : 534it [00:27, 19.75it/s, loss=0.515] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 82: : 132it [00:02, 59.88it/s, val_loss=0.309]\n", + "Epoch 83: : 534it [00:26, 19.84it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 83: : 132it [00:02, 59.66it/s, val_loss=0.296]\n", + "Epoch 84: : 534it [00:27, 19.69it/s, loss=0.519] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 84: : 132it [00:02, 59.83it/s, val_loss=0.332]\n", + "Epoch 85: : 534it [00:26, 19.85it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 85: : 132it [00:02, 59.60it/s, val_loss=0.317]\n", + "Epoch 86: : 534it [00:27, 19.77it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 86: : 132it [00:02, 58.63it/s, val_loss=0.302]\n", + "Epoch 87: : 534it [00:26, 19.79it/s, loss=0.519] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 87: : 132it [00:02, 60.47it/s, val_loss=0.296]\n", + "Epoch 88: : 534it [00:27, 19.74it/s, loss=0.515] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 88: : 132it [00:02, 60.28it/s, val_loss=0.312]\n", + "Epoch 89: : 534it [00:26, 19.89it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 89: : 132it [00:02, 60.52it/s, val_loss=0.289]\n", + "Epoch 90: : 534it [00:26, 19.79it/s, loss=0.519] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 90: : 132it [00:02, 59.43it/s, val_loss=0.332]\n", + "Epoch 91: : 534it [00:26, 19.82it/s, loss=0.517] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 91: : 132it [00:02, 60.42it/s, val_loss=0.31] \n", + "Epoch 92: : 534it [00:26, 19.81it/s, loss=0.514] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 92: : 132it [00:02, 59.98it/s, val_loss=0.299]\n", + "Epoch 93: : 534it [00:26, 19.90it/s, loss=0.524] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 93: : 132it [00:02, 61.64it/s, val_loss=0.315]\n", + "Epoch 94: : 534it [00:26, 19.84it/s, loss=0.516] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 94: : 132it [00:02, 60.29it/s, val_loss=0.331]\n", + "Epoch 95: : 534it [00:26, 19.84it/s, loss=0.514] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 95: : 132it [00:02, 61.00it/s, val_loss=0.306]\n", + "Epoch 96: : 534it [00:27, 19.72it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 96: : 132it [00:02, 59.72it/s, val_loss=0.307]\n", + "Epoch 97: : 534it [00:26, 19.99it/s, loss=0.52] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 97: : 132it [00:02, 60.52it/s, val_loss=0.336]\n", + "Epoch 98: : 534it [00:26, 19.83it/s, loss=0.512] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 98: : 132it [00:02, 60.33it/s, val_loss=0.36] \n", + "Epoch 99: : 534it [00:26, 19.87it/s, loss=0.514] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 99: : 132it [00:02, 60.57it/s, val_loss=0.327]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train completed, total time: 2959.368038415909.\n", + "epl 100\n" + ] + }, + { + "data": { + "image/png": "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rVsxbtw41K1bF2FhYRg+fLjs8CwhhUGz7QgpQcRv9itXrnQ442P5RjhhwgRh+O/555/H6NGjUa9ePYSFhcHLy0vo5VO5cmXEx8cX+AleqVQ6820QAPXr18fu3bsBAKdPnxaGEj2Fo7/T/v374/XXX0dOTg5WrVqFdu3aAeBeq2vXrgUAdO3aVTbY5F/P1apVw5YtWxy+tmrVqknuv/DCC+jcuTPWrl2LnTt34tChQ3j06BGSkpKwYsUKrFixAiNGjMDSpUup7om4BQVPhJQgfA0SwBV1161b1+ljpKenC29qQ4YMkRT0WhJnYkoDcZBZUFaksK0K2rVrh88++wwA8Oeff2LgwIGFOh4fFJhMJrv7uXuIMCAgAH369MG6devw22+/YfHixdBoNNi7d68wvGarzo1/PT948ADR0dEONwmVExgYiLFjx2Ls2LEAuBqoLVu2YPHixUhMTERsbCwaNmyICRMmuHwOQngUghNSgjRs2FC4vWvXLpeOce3aNej1egDAoEGDbO535coVZGZmunSOkqpOnTrCbXF9mZyCHi9I165dhRl7v/32GxISEgp1PL6GKjU11e5+fEG8O/HB0ePHj4XO9HwBua+vL/r27Sv7PP71nJ2djSNHjrj1mmrXro333nsPR48eFQry161b59ZzkNKLgidCSpAaNWqgdu3aAIA1a9bgzp07Th/DYDAIt+0thfL99987f4ElXKVKlRAVFQWAC2hsLQmSm5uL3377rVDn0mg0mDx5snC8MWPGOFzndffuXezdu1eyjR/KysjIsBkg5eXlYf369YW4ank9evQQivBXrlyJ3NxcbNiwAQA3LGxrNqE4qFqwYIHbrwvgZg3yv9OCJj4Q4igKnggpYT744AMA3Btu//797Q4f6XQ6fPvtt5IgoEaNGkJNE9+l3NLWrVuxePFiN151yfHaa68B4KbET5kyRXafKVOmIDExsdDnmjBhAjp06ACA64rer18/u79PlmWxcuVKNG7cGOfOnZM8xtcaAcDnn38u+9wJEya45botqdVqoXXDH3/8gVWrViE9PR2A/dYUTZs2RdeuXQEA27Ztw4wZM+ye59atW0LrA96mTZvsZtvi4+Nx+fJlANa1UoS4imqeCClmZ86cwfLlywvcr02bNqhRowYGDx6MnTt3IjY2FqdOnULt2rXx2muvoV27dihbtiyysrJw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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "train_classifier=True\n", + "\n", + "n_epochs = 100\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", + "\n", + "classifier.to(device)\n", + "weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset\n", + "\n", + "\n", + "if train_classifier==False:\n", + " classifier.load_state_dict(torch.load(\"./classifier_small.pt\", map_location={'cuda:0': 'cpu'}))\n", + "else:\n", + "\n", + " scaler = GradScaler()\n", + " total_start = time.time()\n", + " for epoch in range(n_epochs):\n", + " classifier.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + "\n", + " for step, (a,b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " \n", + " optimizer_cls.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=False):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image\n", + " pred=classifier(noisy_img, timesteps)\n", + " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", + "\n", + " loss.backward()\n", + " optimizer_cls.step()\n", + "\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix(\n", + " {\n", + " \"loss\": epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + " print('final step train', step)\n", + "\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " classifier.eval()\n", + " val_epoch_loss = 0\n", + " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", + " for step, (a,b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise\n", + "\n", + " with torch.no_grad():\n", + " with autocast(enabled=False):\n", + " noise = torch.randn_like(images).to(device)\n", + " pred = classifier(images, timesteps)\n", + " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " _, predicted = torch.max(pred, 1);\n", + " progress_bar_val.set_postfix(\n", + " {\n", + " \"val_loss\": val_epoch_loss / (step + 1),\n", + " }\n", + " )\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "\n", + " total_time = time.time() - total_start\n", + " print(f\"train completed, total time: {total_time}.\")\n", + " torch.save(classifier.state_dict(), \"./classifier_100.pt\")\n", + " \n", + " ## Learning curves for the Classifier\n", + " \n", + " plt.style.use(\"seaborn-bright\")\n", + " plt.title(\"Learning Curves\", fontsize=20)\n", + " print('epl', len(epoch_loss_list))\n", + " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + " plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + " plt.yticks(fontsize=12)\n", + " plt.xticks(fontsize=12)\n", + " plt.xlabel(\"Epochs\", fontsize=16)\n", + " plt.ylabel(\"Loss\", fontsize=16)\n", + " plt.legend(prop={\"size\": 14})\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "# Image-to-Image Translation to a Healthy Subject\n", + "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "DiffusionModelEncoder(\n", + " (conv_in): Convolution(\n", " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", " )\n", " (time_embed): Sequential(\n", @@ -1078,11 +2452,11 @@ " (2): AttnDownBlock(\n", " (attentions): ModuleList(\n", " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=128, out_features=128, bias=True)\n", - " (key): Linear(in_features=128, out_features=128, bias=True)\n", - " (value): Linear(in_features=128, out_features=128, bias=True)\n", - " (proj_attn): Linear(in_features=128, out_features=128, bias=True)\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (query): Linear(in_features=64, out_features=64, bias=True)\n", + " (key): Linear(in_features=64, out_features=64, bias=True)\n", + " (value): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", " )\n", " )\n", " (resnets): ModuleList(\n", @@ -1090,216 +2464,33 @@ " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", " (nonlinearity): SiLU()\n", " (conv1): Convolution(\n", - " (conv): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=128, bias=True)\n", - " (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " )\n", - " (middle_block): AttnMidBlock(\n", - " (resnet_1): ResnetBlock(\n", - " (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=128, bias=True)\n", - " (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " (attention): AttentionBlock(\n", - " (norm): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=128, out_features=128, bias=True)\n", - " (key): Linear(in_features=128, out_features=128, bias=True)\n", - " (value): Linear(in_features=128, out_features=128, bias=True)\n", - " (proj_attn): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " (resnet_2): ResnetBlock(\n", - " (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=128, bias=True)\n", - " (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (up_blocks): ModuleList(\n", - " (0): AttnUpBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=128, bias=True)\n", - " (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (1): ResnetBlock(\n", - " (norm1): GroupNorm(32, 192, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(192, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=128, bias=True)\n", - " (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(192, 128, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=128, out_features=128, bias=True)\n", - " (key): Linear(in_features=128, out_features=128, bias=True)\n", - " (value): Linear(in_features=128, out_features=128, bias=True)\n", - " (proj_attn): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " (1): AttentionBlock(\n", - " (norm): GroupNorm(32, 128, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=128, out_features=128, bias=True)\n", - " (key): Linear(in_features=128, out_features=128, bias=True)\n", - " (value): Linear(in_features=128, out_features=128, bias=True)\n", - " (proj_attn): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " )\n", - " (upsampler): Upsample(\n", - " (conv): Convolution(\n", - " (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (1): AttnUpBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 192, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(192, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (1): ResnetBlock(\n", - " (norm1): GroupNorm(32, 96, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(96, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", " (conv2): Convolution(\n", " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(96, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=64, out_features=64, bias=True)\n", - " (key): Linear(in_features=64, out_features=64, bias=True)\n", - " (value): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " (1): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=64, out_features=64, bias=True)\n", - " (key): Linear(in_features=64, out_features=64, bias=True)\n", - " (value): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (upsampler): Upsample(\n", - " (conv): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (skip_connection): Identity()\n", " )\n", " )\n", - " )\n", - " (2): UpBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 96, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(96, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", - " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(96, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " (1): ResnetBlock(\n", - " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(64, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", - " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(64, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", " )\n", " )\n", " )\n", " )\n", " (out): Sequential(\n", - " (0): Linear(in_features=8192, out_features=512, bias=True)\n", + " (0): Linear(in_features=4096, out_features=512, bias=True)\n", " (1): ReLU()\n", - " (2): Dropout(p=0.2, inplace=False)\n", + " (2): Dropout(p=0.1, inplace=False)\n", " (3): Linear(in_features=512, out_features=2, bias=True)\n", " )\n", ")" ] }, - "execution_count": 36, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -1307,8 +2498,8 @@ "source": [ "\n", "\n", - "inputimg = total_val_slices[100][0,...] # Pick an input slice to be transformed\n", - "inputlabel= total_val_labels[100] # Check whether it is healthy or diseased\n", + "inputimg = total_val_slices[150][0,...] # Pick an input slice of the validation set to be transformed \n", + "inputlabel= total_val_labels[150] # Check whether it is healthy or diseased\n", "\n", "plt.figure(\"input\"+str(inputlabel))\n", "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", @@ -1326,32 +2517,32 @@ "metadata": {}, "source": [ "### Encoding the input image in noise with the reversed DDIM sampling scheme\n", - "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\n", - "We define the number of steps in the noising and denoising process by L.\n" + "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\\\n", + "We define the number of steps in the noising and denoising process by L.\\\n", + "The encoding process is presented in Equation of the paper \"Diffusion Models for Medical Anomaly Detection\" (https://arxiv.org/pdf/2203.04306.pdf).\n" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 125, "id": "f71e4924", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false - }, - "lines_to_next_cell": 2 + } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|█████████████████████████████████████████| 100/100 [00:01<00:00, 51.06it/s]\n" + "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 44.00it/s]\n" ] }, { "data": { - "image/png": "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1qhl2d4i48mTJ8vc0qVLg3Hv+E92drbMVa5cORi//PLLZU1OU8/fI488Imv27t0rc9498XvzjiB5R3n+hW92AIDo0ewAANGj2QEAokezAwBEj2YHAIgezQ4AEL08qf6Hjz/+uMz97W9/C8a9EfkaNWoE42+88Yas8cb0vSMGyvHHHy9zbdq0Cca3bt0qa/r16xeMe9vb33nnHZmrW7duMO798kLbtm1lTh2n8H71oFy5csG4d2Jl/vz5MlenTp1g/NVXX5U18+bNkzl1TyTZpv/FF1/Imuuuu07mtm/fHoxfc801subss8+WOXWMoHTp0rJm165dwfhpp50ma5588kmZa9euXTB+3nnnyZq5c+fKnHo/3XzzzbJGHVfwfv3hrbfekjl1RGTTpk2yZsWKFTKX5BdL/v73vwfj3jGoGTNmyJyqU7/AYma2f/9+mcvJowfecbUUT7z9D7lz5z6cy+GbHQAgfjQ7AED0aHYAgOjR7AAA0aPZAQCil/I0pre4VvEWIKupvDfffFPWrF+/XubGjBkTjF955ZWyxlsa++CDD8qcsnbt2mDcmwz0qMm3QoUKyZotW7bI3D//+c9gfOrUqbLmk08+Cca9xc3eBKCiljObmf3Hf/yHzKmpwXPPPVfWTJkyJRhXi33N9JJvM7MOHToE45deeqms8ZZbq6lZz6OPPhqMq0XZZmZXX321zKnn1Zu4rFChgsyp58hb7qsmChctWiRrvMndHj16BOPe4mZvUlnxlm/36tUrGPfuB28aUz0Xa9askTWHO9WYqpyexvQmTFPBNzsAQPRodgCA6NHsAADRo9kBAKJHswMARI9mBwCIXspHD37++WeZUyPWO3bskDUZGRnhC8qjL6lo0aIyt23btmDcO8rgHS8YNGhQMH7hhRfKmgceeCAY90Zwn3rqKZkbMmRIMN60aVNZM2LECJlTxzPy5csna84666xgXL1+ZmYXXHCBzH366afBeIECBWTN888/n/ZjeUcFatasGYyrxcNmZmeccYbMKd9++23aNWZ62bg6MmGmj73UqlVL1nhj/wMHDgzGR40aJWtWr14tc++//34wrhZYm5nddNNNwXixYsVkTfPmzWVu2bJlwXiVKlVkTRJ33323zC1cuDAYHzp0aKLHWr58edo13iLonOQdcTh48GDafy9Jza/xzQ4AED2aHQAgejQ7AED0aHYAgOjR7AAA0aPZAQCil/LRA8+qVauC8YkTJ8oaNVY8fvx4WXPPPffI3Nlnnx2Mv/7667LGU6pUqWDcOxqhRsO9Dd/e0Qj1iw3e5nlvhFkdm1Cj+GZ6i/wzzzwja7xfXlC84w8edZShdevWskZtsv/yyy9lTb169WSuZMmSwfiwYcNkzc033yxzarS+U6dOsmbJkiXB+IABA2SNp3///sH4SSedlOjvvfLKK8F47969ZU2/fv2Cce896L2Gd9xxRzCujlmYmbVv317mlFatWslcw4YNg/Fy5crJGnUUxcxsz549qV5WSvLnzx+MZ2Vl5ejjJJH0M+Jf+GYHAIgezQ4AED2aHQAgejQ7AED0aHYAgOjlyDRmnTp1gvEmTZrIGjU1mJ2dLWvq168vcxMmTAjGW7RoIWs+++wzmXvppZeC8fLly8ua7t27B+OzZs2SNccff7zMeUt3FTXBZmbWtm3bYPyRRx6RNWry7ZtvvpE13uLm2rVrB+MLFiyQNd40Wrdu3YJxb8mxej28xeCvvvqqzD377LPB+PTp02WNN42p7lk1cWmmJw1ffvllWXPjjTfK3CmnnBKMt2nTRtZ41PtpzZo1skZNX3sTiMcee6zM1ahRIxivUKGCrPFcdNFFwfh7770naypVqhSMe0u0vUlIb8m8cswx+juONzmerpz8W2aHv8Cab3YAgOjR7AAA0aPZAQCiR7MDAESPZgcAiB7NDgAQvRw5evDuu+8G4xdffLGsUWPPK1eulDWVK1eWueXLlwfj3tJkb6T3iSeeCMbVv9VMX59agmtm1q5dO5lTvDH9a665RubUyL33b3rooYeCcbVU2sxs5MiRMte5c+e0r2HatGkypxYd9+3bV9YsXrw4GL/zzjtljbdg+/LLLw/G1ai7mb8kvUOHDsH4ww8/LGvUsRJvKbG6bjN/kbayYcMGmVNHdrzx9EGDBgXjd999t6xZtGiRzKkF4N7z4Ln00kuD8XHjxsmahQsXBuNFihSRNd5RAXU0yBvTP9wR/lTl9OOwCBoAgEOg2QEAokezAwBEj2YHAIgezQ4AEL0cmcZUE1VVq1aVNRs3bgzGveXMXk4tvPUmLps3by5z6vqKFSsma1588cVg/KOPPpI1VapUkbl69eoF42qZsplZRkaGzD355JPB+K5du2TNjz/+GIwnnYxSE7redOczzzwjc1deeWUw7v2blKlTp8qcWmRsZlawYMFg3JuMbdiwocypybyaNWvKGjXt2LFjR1lz7rnnypx6nfr06SNrPvjgA5lTC5p3794taxo0aBCMf/HFF7LmvPPOk7lNmzYF4++//76s8XTp0iUYP+6442SNet94z4O6v8ySLVs+cOCAzB08eDDtv/dbyZ8//2HV880OABA9mh0AIHo0OwBA9Gh2AIDo0ewAANGj2QEAopcjRw8Ub0z/1FNPDcZHjRola9TyaDOzL7/8Mhi//vrrZc3mzZtlTo3gXnHFFbKmevXqwbg3Vuwdp9ixY0cwvnfvXlmjFi17tm7dKnNPP/10MD5z5kxZ4x05UX9PLbQ1Mzv99NNlTo15d+vWTdasXr06GJ89e7as+eSTT2ROja5791elSpVkrkmTJsH4hx9+KGsKFSoUjHtHOnLlyiVzaqn5pEmTZM3SpUtlTt3L6rrN9D2RN29eWaOO/5iZlSxZMhhPsmDes2XLFplTR4N27twpa7xF0OoYQe7cuWVNkuMKRwPv35QKvtkBAKJHswMARI9mBwCIHs0OABA9mh0AIHo0OwBA9I7o0QN1HMBMbyc/6aSTZM3bb78tc+XKlQvGFyxYIGu8MeoTTjghGG/Tpo2sUaPcSUd91Wb84cOHy5pLL71U5m6//fZgfPHixbKma9euMqc88cQTMqeei6+//lrWqG31Zma1atUKxr37aO7cucF4y5YtZU337t1lrkSJEjKn7NmzJ+2aFi1ayNxDDz0UjJ944omyxvslh969ewfj6tcLzMzuvfdemVO/SnLaaafJGvXrBv369ZM13uukrr1Ro0aypkOHDjL3l7/8ReYUdQzJG6v3jh6o99PR/OsFh6I+R70jJ6ngmx0AIHo0OwBA9Gh2AIDo0ewAANGj2QEAondEpzGnTJkic1OnTg3GvYmzefPmydzatWuDcTWBaGbWsWNHmbv44ouD8VtuuUXW/Pjjj8G4WjxspqfezMzWrVsXjHvTnVlZWTLXo0ePYNx7ztXzN2TIEFmjJgM9BQsWlLmaNWvKXNu2bYNxb7m1Wgjct29fWZOZmSlzavJTTRybmd1xxx0yp+6X9evXy5oNGzYE496Un7d8eNmyZTKnePe5Wo48ffp0WaNeJ29Csm7dujKn3k/ec/TNN9/InKKWk5uZ7dq1KxjPzs6WNfv27ZO5/fv3p35hfxDq8+1w/618swMARI9mBwCIHs0OABA9mh0AIHo0OwBA9Gh2AIDo5folxS3FajlnUqecckowfs0118iaAwcOyFzPnj3TvoazzjpL5saOHRuMf/TRR7KmXr16wfiIESNkTaFChWSuYsWKwfj27dtljbeMVy3FVmPrZnpJtHekI8mCXG/ZbZ48+oSMei5KlSolaz7++ONg3Ftc7t3/6r70FmJ7xwj69OkTjP/jH/+QNWpM/7vvvpM1tWvXlrk5c+akdW1mesmxmdmqVauC8aJFi8qa5s2bB+OzZs2SNR51JGbFihWyZsyYMTKnlnlnZGSkXeMtOfbuvb179wbjSZfPH83UcTAz/3P5X/hmBwCIHs0OABA9mh0AIHo0OwBA9Gh2AIDo0ewAANE7or964Jk9e3Ywvm3bNlnTuXNnmbvvvvuC8eeee07WeBvXb7755mC8WbNmskZtkZ88ebKs+frrr2VObdpv166drDn11FNlTo3Wd+3aVdZ8/vnnwfiSJUtkzbhx42ROjf0//fTTssYbn/d+WUNRY9kffPCBrNmxY4fMffrpp8G4d+Rk0qRJMqd+AcIbJ1f3Xr58+WSNGu03M2vTpk0wft1118makSNHypw6WlK2bFlZs2bNmmC8dOnSssY7nqTG/r3X3TtGoH6NwPs1CfULCwcPHpQ13rGcGI8YHCl8swMARI9mBwCIHs0OABA9mh0AIHo0OwBA9H63aUxl+fLlMte/f3+Z69SpUzD+5JNPypo33nhD5lavXh2M//zzz7Kmb9++wbi3TFZNJ5rpxadDhgyRNWohsJlZ06ZNg3HveT3zzDOD8WrVqsmaQYMGyZw3xal4E5dZWVnBuFo8bKancC+99FJZs27dOplT98ro0aNljfc6qcm8xo0by5pnnnkmGPcmLsePHy9zahL4uOOOkzUe9TolWTDv/ZuqV68uc2oC3FusrqYnzfTkp/dvKlCgQDDuTVxmZ2fL3L8T7zlKqT6HrgMAgKMWzQ4AED2aHQAgejQ7AED0aHYAgOjR7AAA0Tvqjh54fvrpJ5l76KGHgvG2bdvKGm88vXXr1sF4ksWr77zzjsy9+eabMqdGw73jGZUqVZK5Dh06BOM9evSQNWrMu0yZMrLGuz717/3oo49kzdVXXy1z7733XjDujaCrxb+33367rGnVqpXM7d69Oxj3lhyr6zYzq127djD+1VdfyZpZs2YF41u3bpU1ajG49/e8ozKbN2+WuVGjRgXjr7/+uqxRo/333nuvrPnwww9lTi1Jz5s3r6xRy5493meEOoKR9O/9O0ny3P0a3+wAANGj2QEAokezAwBEj2YHAIgezQ4AEL0/1DSmR03EvfDCC7KmYsWKMqcW9aoFuZ5hw4bJnDe5+N133wXjEydOlDUrV66Uue7duwfjd9xxh6x5/vnng/GhQ4fKmoIFC8qcWrrrTaV6C2D37t0bjLdo0ULWqOXWF1xwgazxphrV5OLUqVNljZq4NDMrX758ML5hwwZZo5Zle/f/6aefLnPe1KWyadOmtGtuvfXWtGuuuuoqmZswYYLMqfeGN3Hp3XtqYbe3PFrl1OSpWbJl2fi/+GYHAIgezQ4AED2aHQAgejQ7AED0aHYAgOjR7AAA0cv1S4pbRpOMv3ojuN6o7dGgQoUKwXjPnj1ljTpGcMopp8gab1G1GtNXy5nNzObMmSNz9evXD8bVCLWZPv6wdu1aWaMWLZuZ5c+fPxi/8MILZc3AgQNlTh0j8Mbq+/fvH4x7/6YvvvhC5tQRg/vvv1/WXHLJJTJXuXLlYLxWrVqyJsn701sa3rJly2Bc3Q9mZvny5ZM5dTTCu5fnzp2b9uMsW7ZM5goXLhyM79y5U9Z48uRJ/+SWunZvybH32u7fvz/ta/ijaty4scyp++vX+GYHAIgezQ4AED2aHQAgejQ7AED0aHYAgOjR7AAA0TuiRw9ipMaXzczOPvvsYHz9+vWy5owzzpC5QoUKBeNqdP5IaN++fTDujdV7v7yg/k1nnXWWrLnttttkrnPnzsH4li1bZI0a4S9btqys+fHHH2VO/fLC9ddfL2smT54sc+pXI7xf6VCj66eeeqqs8cb0a9SoEYwnPfYyduzYYNz7+ClTpkww7r2fvM+pJGP/efPmlTl17d4vJajjCt4vL3hHg472I1w5yTtONGPGjEPW880OABA9mh0AIHo0OwBA9Gh2AIDo0ewAANFjGjMHqefIe4pPOOEEmdu9e3daj2NmtmHDBplTU41fffWVrKlXr14w7k2lTpo0SebUta9evVrWeIoVKxaMb926Vdaoab7SpUvLmpdeeknmpk+fHowPHTpU1qil3GZm8+fPD8ZbtWola7p27RqMq9fPTE87mpk1bNgwGP/mm29kjWfdunWJ6o5marLSm5BMMo2J/3LyySfL3OzZsw9Zzzc7AED0aHYAgOjR7AAA0aPZAQCiR7MDAESPZgcAiF54DhaJpHiK439YsWLFEbiSsAsuuCAY90bkH3rooWC8bdu2OXJNqejXr5/MderUKe2/16tXr2DcGxkfOHCgzFWtWjUYP/HEE2VNixYtZO77778Pxr3xarVQXP1bzcyee+45mVPHKbZt2yZrvIXKOUmN75v5x3LUgm11xOdQkixh/nda3JzTDve545sdACB6NDsAQPRodgCA6NHsAADRo9kBAKJHswMARI+jB/9GHn/88WA8f/78smbkyJE5eg1qbHz//v2yZufOnTLXvn37YHz48OGyZuLEicG4Gt8/lLVr1wbje/bskTWVK1dO+3G8EXn1vA4ePFjWZGZmypy6J36r4wUe717xjh6o5++YY/T/83uPpXhHkPj1mN8P3+wAANGj2QEAokezAwBEj2YHAIgezQ4AEL1cv6S4vTjJFFHu3LlljoWofwwlSpQIxrOzs2WNN92pliaXKlVK1lSoUEHmihcvHozv2LFD1qjpztdee03WePe/+jetX79e1pQsWVLmli9fLnN/VOr5y5cvn6xJMgnpfa6oqUvvteVzKjnveU2yNL969eoyt3jx4kPW880OABA9mh0AIHo0OwBA9Gh2AIDo0ewAANGj2QEAondEF0EXLFhQ5tRS1oMHDx6py0EC3rJgxRv737x5c9p/z7sn1PEWb2RcHafwjsp4I/KrVq0KxvPmzStrtm/fLnO/FW8Bck6/D9VxFG88XT1/SY8DqNfXuwbvnlDPkTdWrxZ2e8+39/fU8YykY/9J3k9Kks9/M/26Fy1aNO1r+DW+2QEAokezAwBEj2YHAIgezQ4AED2aHQAgekd0GtObZEqyCPS3nB47Gqh/r5roMvOf13379h32NR2unH6d1JSYNz2pFkF7C6y96THFm2Dbs2dP2n/Po67du1e8ib3ChQsH496krff+VLzrSzJpmOT+Svo5pa7Du4Ykz5FH3WNJpzFz8v2Z9N+qPqe85emp4JsdACB6NDsAQPRodgCA6NHsAADRo9kBAKJHswMARC9Hjh6oxbply5aVNbt27QrGN2zYIGuys7NlTi0PzcrKkjUeNY6cZEzZq1Hj1WZ6LNtbMOyN3O/duzcY98aN1QizN9qc5IiDN6afZAmtd68o3qi0dyxB3WPec+T9PXVPeCPj6tq956FQoUJpX4N33UleJ+85V/d50veTej2SHj1Q/17vecjJz5VD5ZLUqM8c9dlhpp9X717xqKNBxYsXT/T3/oVvdgCA6NHsAADRo9kBAKJHswMARI9mBwCIHs0OABC9HDl6kJmZGYyXK1dO1px44onBeLVq1WSNNyKfkZERjCcZHTbTI7jeqHROj+mrceQk122mR+S9a1D/Xq/Ge87VaLg3Mu5R/17v76mc99x5RzqS/D1v1FzVJTmu4N17Hu/fq3jvzyTHctS1J31PK0l/RUTlkhw98HifOeo9neRxzPRxD+8a1C9heEek1qxZI3NLly4Nxr3ekAq+2QEAokezAwBEj2YHAIgezQ4AED2aHQAgerl+SXGTaNKpLgAAjqRU2hjf7AAA0aPZAQCiR7MDAESPZgcAiB7NDgAQPZodACB6KS+CTvGEAgAARx2+2QEAokezAwBEj2YHAIgezQ4AED2aHQAgejQ7AED0aHYAgOjR7AAA0aPZAQCi9/8AuDeQHVcWchIAAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -1361,7 +2552,7 @@ } ], "source": [ - "L=100\n", + "L=200\n", "current_img = inputimg[None,None,...].to(device)\n", "scheduler.set_timesteps(num_inference_steps=1000)\n", "\n", @@ -1378,7 +2569,8 @@ "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", - "plt.show()\n" + "plt.show()\n", + "\n" ] }, { @@ -1386,29 +2578,30 @@ "id": "a7c8346a-6296-4800-b978-c10fcdf09779", "metadata": {}, "source": [ - "### Denoising Process using gradient guidance\n", + "### Denoising Process using Gradient Guidance\n", "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\n", - "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). The scale s is used to amplify the gradient." + "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \\\n", + "The scale s is used to amplify the gradient." ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 126, "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", "metadata": { - "lines_to_next_cell": 0 + "lines_to_next_cell": 2 }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|█████████████████████████████████████████| 100/100 [00:06<00:00, 14.41it/s]\n" + "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.41it/s]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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U1Onji0iYsaMGXKtubm5GHdbD9TP2r9/v8w5ceKEXHviiSeKcfe2i6FDh8q1Xr16FePr1q2TOWo7ituK8sEHH8g1dV/Onz9f5hw4cECu/f73v5dryrRp04rxzZs3d/qzkBtvPQAAICh2AIAqQLEDAKRHsQMApEexAwCkRzfmh2jp0qXFuBvc7Abh9ujRoxjv1k3P71bDniP0AF3VRerW3DBe11nZ3t5ejA8YMEDmvPXWW3LtscceK8bdMGp1zrt37y5z3DlqaWkpxseNGydz1JDvCN1JOnjwYJnzgx/8oNM/Z+/evXLNdfUCtxq6MQEACIodAKAKUOwAAOlR7AAA6VHsAADpUewAAOmx9eAmW7FihVxTQ5PdkONdu3bJtffff78Yf/TRR2VOJW3658+fl2s1NTXFuNueobYrROjv9KlPfUrmtLW1FeO9e/fudE6EHnzt2v7dMO9+/foV4xcvXpQ577zzTjH+wgsvyJwOPvrALY+tBwAABMUOAFAFKHYAgPQodgCA9Ch2AID06MasIuoaLlu2TOaozs8rV67IHLemui5dp6EaiB2huyQ3btwocxYtWlSMHzlyROaojssIPcR69erVMscNWlYDml0XLlDN6MYEACAodgCAKkCxAwCkR7EDAKRHsQMApEexAwCkx9YDWN/4xjeK8dGjR8scN+RYDZZubW2VOYcPH5Zra9asKcbdYOkdO3YU408++aTMeeWVV+QagJuLrQcAAATFDgBQBSh2AID0KHYAgPQodgCA9Ch2AID02HoAAPhYY+sBAABBsQMAVAGKHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AIL1uHf2H169fv5HHAQDADcNfdgCA9Ch2AID0KHYAgPQodgCA9Ch2AID0KHYAgPQodgCA9Ch2AID0KHYAgPT+HzbxLsbNP3t+AAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] @@ -1421,14 +2614,15 @@ "\n", "\n", "y=torch.tensor(0) #define the desired class label\n", - "scale=10 #define the desired gradient scale s\n", + "scale=5 #define the desired gradient scale s\n", "progress_bar = tqdm(range(L)) #go back and forth L timesteps\n", "\n", "for i in progress_bar: #go through the denoising process\n", "\n", " t=L-i\n", " with autocast(enabled=True):\n", - " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) # this is supposed to be epsilon\n", + " with torch.no_grad():\n", + " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)).detach() # this is supposed to be epsilon\n", "\n", " with torch.enable_grad():\n", " x_in = current_img.detach().requires_grad_(True)\n", @@ -1446,8 +2640,7 @@ "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", - "plt.show()\n", - "\n" + "plt.show()" ] }, { @@ -1455,19 +2648,19 @@ "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", "metadata": {}, "source": [ - "### Anomaly Detection\n", + "# Anomaly Detection\n", "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction." ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 127, "id": "ecffaaf3-a7df-453e-81a9-757113d85084", "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbsAAAG7CAYAAABaaTseAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAeVElEQVR4nO3df+yvZ1kf8PsrFUtX5EgbqPYsHFybFIbQ1Q7IwKwKGsmEGTXCkEhdxOAkW8LUzegf5w/J/EniWObEJisOhRJFlBnJ1syqxQkpXQWkzVq2b+NhOyLVM0CsUvnsD7PMZZ/3u+c8ntOeXuf1+vO+v9fz3J/n8zzfK5/kuq/nYLfb7RYADPZ5j/YCAOBck+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxLjrdPzw4OH4OlwEA2+x2xx/2b/yyA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGu+jRXgDng88vc599BM/1SEmf6Qkbj/fQGZ5nrUf2mgN+2QEwnmQHwHiSHQDjSXYAjCfZATCeakzWI1txmc51tqsTt1R9HstTV708z701jD//jeVcj1TFpcpOWMsvOwAuAJIdAONJdgCMJ9kBMJ5kB8B4kh0A49l6MM7ZLvv/3jB+aw656CV57qG0hl8ua2ju2xCT1vDJcpo35Lnnf22YeHVZQznX0afvHf78u3LMZy//yTDTHvHUwHor2xw4f/llB8B4kh0A40l2AIwn2QEwnmQHwHiqMcdpFXFfGMZfm0OuCJWax0rF5QvLEn7sT8LEN5agW8pcqiRN51lrravC+B+WmKeUuavDeLrea611mKdO3LN3+LPPKtf8yD/bO/y0P9p/rLXWuv/p1+TjHabq0y3311q1+hQeAX7ZATCeZAfAeJIdAONJdgCMJ9kBMJ5kB8B4th6M8/oy96th/FM55OQl+8efVU7zY2Vu3R3GD0tMK1u/Loy/t8RcFsZbQ+y2ht8P428qMa1JdPhMV5SQS/cP3/+VZXtBO976/v3DJ0vIgz9cJtu1TTSW5uzxyw6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxjvY7Xa70/rDg+PneCn8/54Qxh8qMVeWuVQi37rVp90pLaZ5chh/aon5YJlL62hvPXj5/uG2ESe9KGGttR4M49eXmDvK3OGG44WtB3Fta611cZl7RRj/wRJzom3PuCmMtxjbFTg9u93xh/0bv+wAGE+yA2A8yQ6A8SQ7AMaT7AAYTyPoR0SrKmtfQYprxzssc6lSs1V3porQtu5WCZmqJ99ZYlKz57Xitbj0ZTnk68L4DeU0rarxeBhPFZJrrfWJMndtmUsuD+PHcsjRH703zp34pav3Txwpa/h0qdA99YL9489/Xo757TeUkyXt2UiVmltieKzxyw6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxtMI+jGplUq3RtBpS8CXlZj3hvFSMl63JaRm1M/OIZeHMvi1cpn+D+WQp335PXvHj5VtGw+sy+Lchz8aOjTfd5AXcVV+7L7qb/zK3vH/9IG0Z2KtdfIMx9da68NlLm212H/p/sKJMpe2Wpz6hRxz7Bv3jx++sZyoOPL6sIYfLkFtW84WtjKcCxpBA8CS7AC4AEh2AIwn2QEwnmQHwHiSHQDj2XoQpfL+J5aYLWXKrRQ5dZFvbxVonhbGP1Vi0vraGp5c5tI2gqfkkNufHqde94If2Tv+svXuGPPV996+d/xtV//9GPNT67Vx7nHhe/9b664Y8zPrW+PcH3xs/7X4vIv+PMZ87q6/tn+ibQdo2xIOw3jbrtDe5BC3HrT7/84w/skSc6zMpW00hyUmvK1hrbUu/ur940fK4U6mNznYkvBXYesBACzJDoALgGQHwHiSHQDjSXYAjNc69l4AWkPlJ2yI2aJVYaWvp8Wkda+1YjPjVln5jDB+b4l5VZ66MYz/YOo8vNabrvz2OPfM9ZG949f/+R0x5uTVT9o7/rz1vhjzZ+sL4tz3rB/dO375eiDGXF5KF/8gVKZ+7sHHx5hYAXhfDqnVmKfCeKvu/PSGuUvL8/TpdLJLyon+sMyle/mqEpMqotdaD4b75WR+Nv7B7ov3jr/t4H+WNajUPBv8sgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8cZvPfi8k98T5z53xU+VyFTCf7bLgEtpc9wS0BpOHytzae1tu0Jq6ly2F9yap173ov2Nm79p/UKM+c31FXHurnXt3vHfe9xfjzFHQl39v437ItY6tb4ozn38ffsbbF92fa7Tf+PjXh/nXv0b79g7fvAluWf7O/7uS/eO/+Pn/USMOfl9Xxrn4raEtr3gVJm7OB2v9aFP2wjaforWhDxtPWhbb9pWoyvL3H5vO0gxP1uibD04G/yyA2A8yQ6A8SQ7AMaT7AAYT7IDYLyD3W7XyqH+7x8eHD/HS/krOvHP948f/fES1KoQ01wrYG1Vkkk7XqoSa81ur9uwhuflqSueun88XO611lrflJs6f+DK/ev7rfV3YszvrVxZeet60d7x71r/Osa88o/ftnf8hy6NIetbdkfj3C3r5XvHLyvNnv/R79wc5770Ob+7d/yBP02NvNd6/Bf82d7xt69XxJgX/dJvxbn19aGZ8Y1X55i78lSeO16Cwr23PrkhZq38rOXrutYTy1yqrPx4iUkVut9YYt5Q5lRqrrXWbnf8Yf/GLzsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGG/O1oOolSJvKdttjWG3HO9TG2L2Nx7+C6U0PJZRt60Mz94//N25gfXFP5CPd/2TPrB3/CvWb8aYS9Zn4tyXrP+xd/wf/of92wvWWuulX7O/0fK167/EmJvXt8W5Z66P7B3/jf+VG1hf9aSPxrkP/9Tf3j+RdzKsdX0Y/+0Sc2O5X78u3OcfLv8ujh7kuRMPhIl35phNz0bblnAsjLfm0e2Z/lgYb83d07OxdUtT+7wXDlsPAGBJdgBcACQ7AMaT7AAYT7IDYDzJDoDxWr3reSh1GV8rvyGgaSX3actCO096U8Lvb4hZK5dE37DxeB868+M9K4zflEMevCiXct9+zVfvHf/zVz8uxvzn3/2qOHf0b+7vzv/7X5O3nFy/7tg7/mvrK2PMqT8+Eud+7cH9cW+57FtjzCs/+otxbp3aP/z5r81l5p+9PZS755c1rHVz2UbzdWH8sG0vKOda726TQSr73/Ksr7XW4RmOr5W3K6y11jPCeHsGk7a9ILyBYq119q/RXH7ZATCeZAfAeJIdAONJdgCMJ9kBMN551wj6n+5ygeiPH1xSIlOT46ZVQKV1tOa0qQLqmtNbzmkf73k55EhpBH0qNeO9rBwvjKcqzbV6Vd7FYfz5Jea1ZS59TbfnkD+7cX9F4ePvyI/CN7/oLXHuHb/y6r3jz/h7d8aYh1auPv2vN127d/zgKz8XY9btoUqyfRcPlrlPh/F7Ssx7WtXxLWUuSc9aq3Zs/wdS9emWBu5r5f8fV5WYtPbWYP59Ze5bwvibSsw8GkEDwJLsALgASHYAjCfZATCeZAfAeJIdAOOdd42gf/yLf6DM3lbmUll9217QSqVTOXJuxrvWC8L4lobTa+XP9M4ccio1p10rlzen86y1ToWYO16eY1pJe9oTcM/7c8jNr89zrwrjN+SQx/982GJQtlO84yf2by9Ya60r/sl/2zv+I+t7YswD6/I4F7cYvKs0YU5Pcvsu2twnwnh7nOq/k9TEvT0bW7YetAWmubb1oJ0rfd4Ws+Uztf8R7X8Yf5lfdgCMJ9kBMJ5kB8B4kh0A40l2AIwn2QEw3qP41oNjYbyV0qa3AKyVa81bF/T7ylxaR+tOnkqRj5aYUva/PhbGn1xiWpnyF4bxdl3TdXhxiWll1O8I460EvXlKGH9NDvn6MH6snOauMlfesBB994bjpTdGrJW3TaS3F6zVb//0toSTJebT7V5+dxg/LDHpfk3f+Vr9rSTpuWn3a7tI6e0G7X9E+n+UnvW1+vOZ/ufcX2K2vuXh/OWtBwCwJDsALgCSHQDjSXYAjCfZATDeOW4E3aqSWoVRkqqz1srlY7VzbZHW3hpBp0rIe0tMqx5LVWKt2fOHylzSPlM618+WmHbNUyVdq7Db4qfz1Ltesn/82lI1m/s2l4/7mRzz1kvyXKqg/KayhpvD+JES0yo1UyPoei/fVuZStWGrHk73/5b7a61c8dv+Dbbjpf8R5XuP50qVnWv1itV7wnirMJ1XjXk6/LIDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPHO8daDVuKaGgy3ktnWADmVI7fjtZL7tDWiHS81eW3X4coyl8q1W+l1O9fdYfzZJeYwjLetI23LSdpqcV2JKY7esH/8xG0lKDT3vavdX+0zHYbxsoYT33Dmx7upbVMJj/Kpdq+E86y18rPRrkNr4n4sjLdrnu6x9m+rbU9KyjaQ+qyla9TWkD5Tazjd/kds+b93Z5mbyy87AMaT7AAYT7IDYDzJDoDxJDsAxjvH1ZhbtCqiNpeqnLY2PU0VUK0KMVXLbVl3i0tVlWvlqre1cnVnq0pNt0irnkznab6szJXrd+Ij+8e/9oYc855bwsSbyhq+t8yle+xYiXlnmft4mUtSJfDVJeZYmUuN1Vs1ZnvWnhvGW4Vpui9bBWe7dpeF8fYMbqkWbbY0o27XKGnVnVu/w8c2v+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYLxHcevB8TD+0yUmle22uVY6nMq118plxS0mac1km1YinLRmvKmEv22NSMfb0nB3rXzLlWv0wtKo94XP3D9+R1nCDS8vk8GHy9wnUnl/u65PLXPpnm3XPJWMb23cnO7/tkWkadtbknT92md6yobjtf8r7TvcssUmfbftu9jS+HrL2mbzyw6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxjsPtx5sfetB+iit5LkdL2nbCNK52nlaGfWWr6dttUhap/N0vLIdYNNWhptyyO2vzHNHnr5//NqyhB/bhYn21oP2lod0jVpX/GNlLnXnb/dyKkFvZfXvLnMvDePtXmmd9tM62jVK17xt/2nPZzpX29Kx5a0kzQMb1rBlO1Fb99w3GzR+2QEwnmQHwHiSHQDjSXYAjCfZATDewW63S6Vp/+8fHhw/x0v5P1p14pYqoi0Nd9faVmmV1t7W3Rq2pqa2rcKufd7DMN4qt14SxltlYPtM6Zo/o8SUz3QkXPNXlMM9K4zfXmLe/stl8jCM31hi2vX71TD+qRKTGjS36sQry1yqeGz3SvOLYfy1JSZVKLY1bGlu3Z7PLdWdqeKyHa+tYcs1b/8jtn6H56/d7vjD/o1fdgCMJ9kBMJ5kB8B4kh0A40l2AIwn2QEw3qPYCDp43ffnuRMl7l3/MUxsLb2+Koy3rRGppLc0Oa5NbVOz5XtKTCtPT5+3bRU429K57iwxpVT6VGhY/PNfXmLC+KVlCde/LM+dDOPXlONdURr/3vqacJ62/SFd17a95u4yl0rXW2n/q8tcep5aM+q07eUzJabdy6m8v8VsaTp9uCHmbDe3fm6JeW+Zm8svOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAY7wzeevCGMrvlbQT7PWOXS7zv/orrcuDtbw0TrcN3K8u+IYy3bvVPL3PJzWUuXddyHdb7y1zaelDK4Dd9t1u6qqeu/WvV9V102f7xto0gzbWtLReXuRvO8DxrrfXzHyyT6b5snezTmxLa9pr2vac1tO82bS9Ya61jG44Xtt60DVOtSj/ey+2Zbt4SxtsbPLacqz2DaatR2yIyj7ceAMCS7AC4AEh2AIwn2QEwnmQHwHjnXSPou59eKg0Pj5fI1KC5NVi9v8x9KIy3irN/Gca/ocQcK3OpcuupJaZ9pel47TOlarlWRbel+fbHS0xpvv1QqFS7tMScSJWQt+aYB8t9+Z5wLY6lRsZrraPPznMnUmXlvTkm3hNPKDHN4Ybj3Vfm0vfRKkJDofhDByWmSfd/u5dbk+hWQZyk67f1e0rXtVV9bqmWfuzzyw6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxjuDrQdnr9nzWmutVxzfP/721nC6lJNv0rYlpPL01i34xWH8sMRsaVTdGgK3JtGpaWw7Xlpfi2n3ymfCeG4Avq4q33uqdm9f0+Wh7P8TrYFv+7y37R8+TJ91rfxdrLXWA2H8WIlJa28NgVvZf/rXULZnrNCUe63VtyUk6f7fUvK/Vt4Ss7VMPx2vNZhPMW27zpYdYhfm9oLGLzsAxpPsABhPsgNgPMkOgPEkOwDGO4Myny2VkKW56dt/Oky0Csm2hlR11qqcWjXaa8L4R0pMama8tTIqrf3OEtOaRG+pqD0M4+26tqrGa/YPX9yaPZfDpe/j0mfmkCNh/BOlcvHSo3nuh16+d/ibv+stMeQdB6nicq383GxpAN6emVZhmpqDt2rfJq2jPRupSvLXS0xrqJzu/7aGdrxUbdti0tzW/xHpM7Xv/SxX1j9G+GUHwHiSHQDjSXYAjCfZATCeZAfAeJIdAOMd7Ha73Wn94cHxDYd/XplLZd5Xlpi2VSCV7rYy4MMyl8qy226NVBreSn1bXX2Ka02E2/rS9WtNekNT26//zhzyrnK4dW+bDMpWi4v3l/03T/uTe/aO338QtkWstXKZ+VprpS0G5Rpt2sLStuWkLQGHG87TtCbM7Vxtm0OS7vN2HbY02G7/V5r0v6WV/W9p6ryl6fqHNpznsWu3O/6wf+OXHQDjSXYAjCfZATCeZAfAeJIdAONJdgCMt6UO9vTd+pI89+JbNhywbSNI5bmHJaZtCdiy9WDLmyGadK5Wet3KqF+wf/iFpXz/WWG87Zi4tMytq/cPf7rFFA/+ZJj4hhhy/8Fvh5n/HmPevPu5OPcdH/2Z/RNXfSDGrHV3mUsXt92vXx7Gt3a/T2to626d+9N9ueUZbKX47X9E2hrUYtpnSteoHS99H2GLz1qrv0XkTWWOv8wvOwDGk+wAGE+yA2A8yQ6A8SQ7AMY7g2rMM680vO5Ft8e5O9dLw0xqqrtWb1i8pdFsq5oKlYutKXGtLNuyhlQJlqrK1urf02X7h28v674mHO+mcprYcHetXM33whLTKkyfG8bzdX3dbn8D5FvWV8WY7zj4d2UNoSL0SGkEfap9pveG8ZeVmBNhPDVcX6tf17NdCZniWrVjupfbZ2qN5Lcc75Iy95Qw3kqV0zVqa/jZMsfp8ssOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMY7g60HZ15W/23r5jh35+2h1PyFbUkfO+M1dO0zvS+Mt3LttCWglWvvL4P/C6lMuZV4l0a9rwqNua8t2xW+OzUzDg2d11r987amtkkq8V4rN/q+OUb8q4N0vD8t53lrnrojbDE4WQ53abl+/z7M3VqOd1e6l1tpf3ue0j3WGhYflrlUWt/uh0+F8dYIvZXwp+ezbS9o22jS/6r2fKa5dh3aF8/p8ssOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMY72O12u9P6w4PjGw5fStpv/v794+8qh7urzB3eEiZaGXwry07d09ubF64J4217QbOlXLutL5WNpzc8rLXWsTC+tfN8uuatDD6tYa11JGwFOVIOd5i2Edxfglq5+7eH8fT2grX6503XKL0pZK28jabdD+3tGen73fJ2kbXyvdeuQyrtb2/2aGX/W5z52166dB+1t6mkLRhrbXvTyjy73fGH/Ru/7AAYT7IDYDzJDoDxJDsAxpPsABjvHFdjZs/Zfe3e8c+Uaqp7D369HLFVLCVbKpladdYjVT3WmmUfLXOfCeOtufWzw3ir5Evnado1KnNXhPF2GR4M44cl5qF2vBP7xy8qi2jHi9evVS6mKsl2f7WK2rTAT5aYdq5UYdru5fS9t+e2Xdi0vvaZLitzaR3XlZhQdXnkZTnk1PFyPNZSjQkAay3JDoALgGQHwHiSHQDjSXYAjCfZATBeq/s9p37n4D17x79v97kY8y/WF5Qjbin7b7Ycb8vlbGXUqVR6azPetMWglYynMvgPlpjWCDqVebfrWq7RyVDef2k53sVh/EhZQvtqD8P1u7zEnGzbM9oWg6Q1NU9amX5aX2rovFYv4U+fqR0vfaarSsxhmUv3edtG0453dRhvzbc/tH/4VGsEzdnglx0A40l2AIwn2QEwnmQHwHiSHQDjPWqNoDe58Xieu/mNGw64pRKySRWAT9lwrLVyNdrWCtNUddYq+VKFXWse3Wypcn1imUsVhe2aHwvjrZl4+7zpEfo3Jea5G87VSkLT2tt32+ZSk+gW06pwU9zhhuO1atUtz22rxmzHOwzjHy8xW5rP83A0ggaAJdkBcAGQ7AAYT7IDYDzJDoDxJDsAxntMbT34jt0Xxbk3HzwpzLTmtM2W8v5UXt3KzFu5e2rG2xr4thL5dC3O5jaLtfpWgbPdsHhL8+20hi8rMe3zplL4W0tMW/eWLSypKfFhiWn3ypYS+fbdpvL+ww1r2HK/trh2HdIWjLXWuiGM33Raq+HssfUAAJZkB8AFQLIDYDzJDoDxJDsAxpPsABhvS932o+bNB39UZp8Txu88y6toJeiptP/JJaaVa6eS+/a1pe0K7Vxb17clZssa2taDLVtLUkzbKtA646fP9OINMU37rO8N49dtOM9aeZtDe562bGVonyltFWjXrj2faa69ReGqMveWMsf5xi87AMaT7AAYT7IDYDzJDoDxJDsAxntMVWN2t4Xx7ywxrcLu5jDeGuReGcbvLzGtaXKqbttScblWrnhs1Y6pIq7FbGlU3WLaNUoeKHOXbYjZ0uS4aedKx0v311prfSiMH5aYby5zoQHyxa/JIQ+eKMe7O4z/YYlJ997WRtAv2z98tIScOL7xXJxv/LIDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEGbT1IfjJPHT2e5060LQZJKqtvZeb3lrlUgt6+tlaWnRrhbvmsbQ1tG0Eq4U+Nhx9O+rytKXFaw9UlpjWJTs2WW1l9266Qmjo/u8Sk7yNsIVhr9abOYX0PlpC6NSLdY21LR9pO8akS0xpBH98/3HZMMIZfdgCMJ9kBMJ5kB8B4kh0A40l2AIwn2QEw3gWw9aDY1NG8lYynUvN2mVuJfIrb8laBtXKZ/patB20N7XifDOMfLDE3lrlUWr/l1k4l/2vl7QXtXO06tBL5G8J42/6Q1tBK+99f5pIXlLljZe7nNpzraWH8vhLTtntwIfPLDoDxJDsAxpPsABhPsgNgPMkOgPEOdrvd7rT+8OD4OV7KBKkS8sklZkul5pZmz+1crWIvHS9VVa61bX3tGrX1XRXGWwPkVkmafKzMpYbKLy4xrVIzXb92HQ7DeGua3L6ndDw4/+x2xx/2b/yyA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxLuxG0FUqkd/S5HhL6fxauTS8bS+4u8ylZsatFH9L8+hW0r4lps2l5sjPLTGtHD95YplL17U1JW6fqd0vSfpMbYtI254Bs/hlB8B4kh0A40l2AIwn2QEwnmQHwHiqMaNWbXg2j9WqJ1vVZfLaPHX0sv3jJ27bcJ7UpHqtbdWY7Va8pMw9NYzfU2KuCeOtCrJ93jR3W4lp3+2Wql6g8csOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaz9eC8tmX7w5vy1Ikta0iNqo+WmNZ8+Mkb1tCOd3iG42utdWcYL9s22nUFznt+2QEwnmQHwHiSHQDjSXYAjCfZATCeZAfAeLYeXFBSp/22xeG+Mxzfqr0F4Gy+gaKxvQCm8ssOgPEkOwDGk+wAGE+yA2A8yQ6A8VRjXlAeqarGLR7JtZ3P1wE4F/yyA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGk+wAGE+yA2A8yQ6A8SQ7AMaT7AAYT7IDYDzJDoDxJDsAxpPsABhPsgNgPMkOgPEkOwDGO9jtdrtHexEAcC75ZQfAeJIdAONJdgCMJ9kBMJ5kB8B4kh0A40l2AIwn2QEwnmQHwHj/G4C86Cgz4ibqAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] @@ -1477,14 +2670,35 @@ } ], "source": [ + "def visualize(img):\n", + " _min = img.min()\n", + " _max = img.max()\n", + " normalized_img = (img - _min)/ (_max - _min)\n", + " return normalized_img\n", "\n", - "diff=inputimg.cpu()-current_img[0, 0].cpu().detach().numpy()\n", + "diff=abs(inputimg.cpu()-current_img[0, 0].cpu()).detach().numpy()\n", "plt.style.use(\"default\")\n", "plt.imshow(diff, cmap=\"jet\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", "plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a0aff7cd-91a5-406d-81d6-69921f9dc141", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfec3184-a975-4f23-a054-3a327789b435", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py index c02f1003..466c7295 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py @@ -14,7 +14,7 @@ # --- # %% [markdown] -# # Anomaly Detection with classifier guidance +# # Weakly Supervised Anomaly Detection with Classifier Guidance # # This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. # @@ -47,45 +47,34 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -import shutil -import tempfile +import sys import time from typing import Dict -import os -import torch.nn as nn + import matplotlib.pyplot as plt import numpy as np import torch import torch.nn.functional as F from monai import transforms -from monai.apps import MedNISTDataset, DecathlonDataset +from monai.apps import DecathlonDataset from monai.config import print_config -from monai.data import CacheDataset, DataLoader +from monai.data import DataLoader from monai.utils import first, set_determinism from torch.cuda.amp import GradScaler, autocast from tqdm import tqdm -torch.multiprocessing.set_sharing_strategy('file_system') -import sys -sys.path.append('/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/') -print('path', sys.path) from generative.inferers import DiffusionInferer +from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet +from generative.networks.schedulers.ddim import DDIMScheduler +torch.multiprocessing.set_sharing_strategy("file_system") -# TODO: Add right import reference after deployed -from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder -from generative.networks.schedulers.ddpm import DDPMScheduler -from generative.networks.schedulers.ddim import DDIMScheduler -print_config() +sys.path.append("/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/") +print("path", sys.path) -train_classifier=False -train_diffusionmodel=False -def visualize(img): - _min = img.min() - _max = img.max() - normalized_img = (img - _min)/ (_max - _min) - return normalized_img + +print_config() # %% [markdown] @@ -93,45 +82,60 @@ def visualize(img): # %% jupyter={"outputs_hidden": false} directory = os.environ.get("MONAI_DATA_DIRECTORY") -#root_dir = tempfile.mkdtemp() if directory is None else directory -root_dir='/home/juliawolleb/PycharmProjects/MONAI/brats' #path to where the data is stored +# root_dir = tempfile.mkdtemp() if directory is None else directory +root_dir = "/home/juliawolleb/PycharmProjects/MONAI/brats" # path to where the data is stored # %% [markdown] # ## Set deterministic training for reproducibility # %% jupyter={"outputs_hidden": false} -set_determinism(36) +set_determinism(42) # %% [markdown] tags=[] -# ## Setup BRATS Dataset for 2D slices and training and validation dataloaders -# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150 - -# %% jupyter={"outputs_hidden": false} +# ## Setup BRATS Dataset in 2D slices for training +# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150. +# If we set `preprocessing_train=True`, we stack all slices into a tensor and save it as _total_train_slices.pt_. +# If we set `preprocessing_train=False`, we load the saved tensor. +# The corresponding labels are saved as _total_train_labels.pt._ +# %% [markdown] +# Here we use transforms to augment the training dataset, as usual: +# +# 1. `LoadImaged` loads the hands images from files. +# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. +# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. +# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# +# +# %% channel = 0 # 0 = Flair assert channel in [0, 1, 2, 3], "Choose a valid channel" train_transforms = transforms.Compose( [ - transforms.LoadImaged(keys=["image","label"]), - transforms.EnsureChannelFirstd(keys=["image","label"]), + transforms.LoadImaged(keys=["image", "label"]), + transforms.EnsureChannelFirstd(keys=["image", "label"]), transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), transforms.AddChanneld(keys=["image"]), - transforms.EnsureTyped(keys=["image","label"]), - transforms.Orientationd(keys=["image","label"], axcodes="RAS"), + transforms.EnsureTyped(keys=["image", "label"]), + transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), transforms.Spacingd( - keys=["image","label"], + keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest"), ), - transforms.CenterSpatialCropd(keys=["image","label"], roi_size=(64, 64, 64)), + transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), - transforms.Lambdad(keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()), + transforms.Lambdad( + keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() + ), ] ) -print('download training set') + +# %% jupyter={"outputs_hidden": false} + train_ds = DecathlonDataset( root_dir=root_dir, task="Task01_BrainTumour", @@ -142,44 +146,51 @@ def visualize(img): seed=0, transform=train_transforms, ) -print('len train data', len(train_ds)) +print("len train data", len(train_ds)) -def get_batched_2d_axial_slices(data : Dict): - images_3D = data['image'] - batched_2d_slices = torch.cat(images_3D.split(1, dim = -1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. - slice_label = data['slice_label'] - slice_label = torch.cat(slice_label.split(1, dim = -1)[10:-10],0).squeeze() + +def get_batched_2d_axial_slices(data: Dict): + images_3D = data["image"] + batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( + -1 + ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. + slice_label = data["slice_label"] + slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() return batched_2d_slices, slice_label -preprocessing_train=False -if preprocessing_train == True: + +preprocessing_train = False + +if preprocessing_train is True: train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) print(f'Image shape {train_ds[0]["image"].shape}') - data_2d_slices=[] + data_2d_slices = [] data_slice_label = [] check_data = first(train_loader_3D) for i, data in enumerate(train_loader_3D): b2d, slice_label2d = get_batched_2d_axial_slices(data) data_2d_slices.append(b2d) data_slice_label.append(slice_label2d) - total_train_slices=torch.cat(data_2d_slices,0) - total_train_labels=torch.cat(data_slice_label,0) + total_train_slices = torch.cat(data_2d_slices, 0) + total_train_labels = torch.cat(data_slice_label, 0) - torch.save(total_train_slices, 'total_train_slices.pt') - torch.save(total_train_labels, 'total_train_labels.pt') + torch.save(total_train_slices, "total_train_slices.pt") + torch.save(total_train_labels, "total_train_labels.pt") else: - total_train_slices=torch.load('total_train_slices.pt') - total_train_labels=torch.load('total_train_labels.pt') - print('total slices', total_train_slices.shape) - print('total lbaels', total_train_labels.shape) - + total_train_slices = torch.load("total_train_slices.pt") + total_train_labels = torch.load("total_train_labels.pt") + print("total slices", total_train_slices.shape) + print("total lbaels", total_train_labels.shape) # %% [markdown] tags=[] -# ## Setup BRATS Dataset for 2D slices validation dataloader -# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150 +# ## Setup BRATS Dataset in 2D slices for validation +# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150. +# If we set `preprocessing_val=True`, we stack all slices into a tensor and save it as _total_val_slices.pt_. +# If we set `preprocessing_val=False`, we load the saved tensor. +# The corresponding labels are saved as _total_val_labels.pt_. # %% val_ds = DecathlonDataset( @@ -194,44 +205,34 @@ def get_batched_2d_axial_slices(data : Dict): ) -preprocessing_val=False -if preprocessing_val == True: +preprocessing_val = False +if preprocessing_val is True: val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) print(f'Image shape {val_ds[0]["image"].shape}') - print('len val data', len(val_ds)) - data_2d_slices_val=[] + print("len val data", len(val_ds)) + data_2d_slices_val = [] data_slice_label_val = [] for i, data in enumerate(val_loader_3D): b2d, slice_label2d = get_batched_2d_axial_slices(data) data_2d_slices_val.append(b2d) data_slice_label_val.append(slice_label2d) - total_val_slices=torch.cat(data_2d_slices_val,0) - total_val_labels=torch.cat(data_slice_label_val,0) - torch.save(total_val_slices, 'total_val_slices.pt') - torch.save(total_val_labels, 'total_val_labels.pt') + total_val_slices = torch.cat(data_2d_slices_val, 0) + total_val_labels = torch.cat(data_slice_label_val, 0) + torch.save(total_val_slices, "total_val_slices.pt") + torch.save(total_val_labels, "total_val_labels.pt") else: - total_val_slices=torch.load('total_val_slices.pt') - total_val_labels=torch.load('total_val_labels.pt') - print('total slices', total_val_slices.shape) - print('total lbaels', total_val_labels.shape) - + total_val_slices = torch.load("total_val_slices.pt") + total_val_labels = torch.load("total_val_labels.pt") + print("total slices", total_val_slices.shape) + print("total lbaels", total_val_labels.shape) -# %% [markdown] -# Here we use transforms to augment the training dataset, as usual: -# -# 1. `LoadImaged` loads the hands images from files. -# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. -# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. -# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. -# -# # %% [markdown] # ### Define network, scheduler, optimizer, and inferer # At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using -# the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms -# in the 3rd level, each with 1 attention head (`num_head_channels=64`). +# the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms +# in the 3rd level (`num_head_channels=64`). # # %% jupyter={"outputs_hidden": false} @@ -246,7 +247,7 @@ def get_batched_2d_axial_slices(data : Dict): num_res_blocks=1, num_head_channels=64, with_conditioning=False, - # cross_attention_dim=1, + # cross_attention_dim=1, ) model.to(device) @@ -257,57 +258,60 @@ def get_batched_2d_axial_slices(data : Dict): optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) inferer = DiffusionInferer(scheduler) + + # %% [markdown] tags=[] # ### Model training of the Diffusion Model -# Here, we are training our diffusion model for 75 epochs (training time: ~50 minutes). +# If we set `train_diffusionmodel=True`, we are training our diffusion model for 100 epochs, and save the model as _diffusion_model.pt_. +# If we set `train_diffusionmodel=False`, we load a pretrained model. # %% jupyter={"outputs_hidden": false} -n_epochs =75 -batch_size=32 +n_epochs = 100 +batch_size = 32 val_interval = 1 epoch_loss_list = [] val_epoch_loss_list = [] -train_diffusionmodel=False -if train_diffusionmodel==False: - model.load_state_dict(torch.load("model.pt", map_location={'cuda:0': 'cpu'})) + +train_diffusionmodel = False + +if train_diffusionmodel is False: + model.load_state_dict(torch.load("diffusion_model.pt", map_location={"cuda:0": "cpu"})) else: scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): model.train() epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) #shuffle training data new + indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new data_train = total_train_slices[indexes] # shuffle the training data labels_train = total_train_labels[indexes] subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes), ncols=10) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) progress_bar.set_description(f"Epoch {epoch}") - for step, (a,b) in progress_bar: + for step, (a, b) in progress_bar: images = a.to(device) classes = b.to(device) optimizer.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t with autocast(enabled=True): # Generate random noise noise = torch.randn_like(images).to(device) # Get model prediction - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning + noise_pred = inferer( + inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps + ) # remove the class conditioning loss = F.mse_loss(noise_pred.float(), noise.float()) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() - if step%20==0: - print('step', step, loss) - epoch_loss += loss.item() - progress_bar.set_postfix( { "loss": epoch_loss / (step + 1), @@ -315,13 +319,12 @@ def get_batched_2d_axial_slices(data : Dict): ) epoch_loss_list.append(epoch_loss / (step + 1)) - if (epoch) % val_interval == 0: model.eval() val_epoch_loss = 0 progress_bar_val = tqdm(enumerate(subset_2D_val)) progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: + for step, (a, b) in progress_bar_val: images = a.to(device) classes = b.to(device) @@ -341,7 +344,7 @@ def get_batched_2d_axial_slices(data : Dict): val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start - torch.save(model.state_dict(), "./diffusion_model.pt") #save the trained model + torch.save(model.state_dict(), "./diffusion_model.pt") # save the trained model print(f"train diffusion completed, total time: {total_time}.") @@ -363,41 +366,46 @@ def get_batched_2d_axial_slices(data : Dict): plt.show() - # %% [markdown] -# ### Model training of the Classification Model -# #First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. -# #Here, we are training our binary classification model for 20 epochs. +# ## Define the Classification Model +# First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. +# # %% - - classifier = DiffusionModelEncoder( spatial_dims=2, in_channels=1, out_channels=2, - num_channels=(32,64,128), + num_channels=(32, 64, 64), attention_levels=(False, True, True), num_res_blocks=1, num_head_channels=64, with_conditioning=False, ) classifier.to(device) -batch_size=32 +batch_size = 32 +# %% [markdown] +# ## Model training of the Classification Model +# If we set `train_classifier=True`, we are training our diffusion model for 100 epochs, and save the model as _classifier.pt_. +# If we set `train_classifier=False`, we load a pretrained model. + # %% -n_epochs = 20 +train_classifier = True + +n_epochs = 100 val_interval = 1 epoch_loss_list = [] val_epoch_loss_list = [] -optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) +optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) classifier.to(device) +weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset + -train_classifier=False -if train_classifier==False: - classifier.load_state_dict(torch.load("./classifier.pt", map_location={'cuda:0': 'cpu'})) +if train_classifier is False: + classifier.load_state_dict(torch.load("./classifier.pt", map_location={"cuda:0": "cpu"})) else: scaler = GradScaler() @@ -409,13 +417,13 @@ def get_batched_2d_axial_slices(data : Dict): data_train = total_train_slices[indexes] # shuffle the training data labels_train = total_train_labels[indexes] subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) progress_bar.set_description(f"Epoch {epoch}") - for step, (a,b) in progress_bar: + for step, (a, b) in progress_bar: images = a.to(device) classes = b.to(device) - weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset + optimizer_cls.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) @@ -424,8 +432,8 @@ def get_batched_2d_axial_slices(data : Dict): noise = torch.randn_like(images).to(device) # Get model prediction - noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image - pred=classifier(noisy_img, timesteps) + noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image + pred = classifier(noisy_img, timesteps) loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") loss.backward() @@ -433,13 +441,12 @@ def get_batched_2d_axial_slices(data : Dict): epoch_loss += loss.item() progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) + { + "loss": epoch_loss / (step + 1), + } + ) epoch_loss_list.append(epoch_loss / (step + 1)) - print('final step train', step) - + print("final step train", step) if (epoch + 1) % val_interval == 0: classifier.eval() @@ -447,10 +454,12 @@ def get_batched_2d_axial_slices(data : Dict): subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # progress_bar_val = tqdm(enumerate(subset_2D_val)) progress_bar_val.set_description(f"Epoch {epoch}") - for step, (a,b) in progress_bar_val: + for step, (a, b) in progress_bar_val: images = a.to(device) classes = b.to(device) - timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise + timesteps = torch.randint(0, 1, (len(images),)).to( + device + ) # check validation accuracy on the original images, i.e., do not add noise with torch.no_grad(): with autocast(enabled=False): @@ -459,25 +468,23 @@ def get_batched_2d_axial_slices(data : Dict): val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") val_epoch_loss += val_loss.item() - _, predicted = torch.max(pred, 1); + _, predicted = torch.max(pred, 1) progress_bar_val.set_postfix( { "val_loss": val_epoch_loss / (step + 1), } ) val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - print('final step val', step) - total_time = time.time() - total_start print(f"train completed, total time: {total_time}.") torch.save(classifier.state_dict(), "./classifier.pt") - + ## Learning curves for the Classifier - + plt.style.use("seaborn-bright") plt.title("Learning Curves", fontsize=20) - print('epl', len(epoch_loss_list)) + print("epl", len(epoch_loss_list)) plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") plt.plot( np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), @@ -493,15 +500,16 @@ def get_batched_2d_axial_slices(data : Dict): plt.legend(prop={"size": 14}) plt.show() # %% [markdown] -# ### For Image-to-Image Translation to a Healthy Subject, we pick a disesed subject of the validation set +# # Image-to-Image Translation to a Healthy Subject +# We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. # %% -inputimg = total_val_slices[27][0,...] # Pick an input slice to be transformed (100,20 -inputlabel= total_val_labels[27] # Check whether it is healthy or diseased +inputimg = total_val_slices[150][0, ...] # Pick an input slice of the validation set to be transformed +inputlabel = total_val_labels[150] # Check whether it is healthy or diseased -plt.figure("input"+str(inputlabel)) +plt.figure("input" + str(inputlabel)) plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") plt.axis("off") plt.tight_layout() @@ -512,18 +520,19 @@ def get_batched_2d_axial_slices(data : Dict): # %% [markdown] # ### Encoding the input image in noise with the reversed DDIM sampling scheme -# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme. -# We define the number of steps in the noising and denoising process by L. +# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\ +# We define the number of steps in the noising and denoising process by L.\ +# The encoding process is presented in Equation of the paper "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/pdf/2203.04306.pdf). # # %% jupyter={"outputs_hidden": false} -L=180 -current_img = inputimg[None,None,...].to(device) +L = 200 +current_img = inputimg[None, None, ...].to(device) scheduler.set_timesteps(num_inference_steps=1000) -progress_bar = tqdm(range(L)) #go back and forth L timesteps -for t in progress_bar: #go through the noising process +progress_bar = tqdm(range(L)) # go back and forth L timesteps +for t in progress_bar: # go through the noising process with autocast(enabled=False): with torch.no_grad(): @@ -537,24 +546,27 @@ def get_batched_2d_axial_slices(data : Dict): plt.show() - # %% [markdown] -# ### Denoising Process using gradient guidance +# ### Denoising Process using Gradient Guidance # From the noisy image, we apply DDIM sampling scheme for denoising for L steps. -# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). The scale s is used to amplify the gradient. +# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \ +# The scale s is used to amplify the gradient. # %% -y=torch.tensor(0) #define the desired class label -scale=1 #define the desired gradient scale s -progress_bar = tqdm(range(L)) #go back and forth L timesteps +y = torch.tensor(0) # define the desired class label +scale = 5 # define the desired gradient scale s +progress_bar = tqdm(range(L)) # go back and forth L timesteps -for i in progress_bar: #go through the denoising process +for i in progress_bar: # go through the denoising process - t=L-i + t = L - i with autocast(enabled=True): - model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) # this is supposed to be epsilon + with torch.no_grad(): + model_output = model( + current_img, timesteps=torch.Tensor((t,)).to(current_img.device) + ).detach() # this is supposed to be epsilon with torch.enable_grad(): x_in = current_img.detach().requires_grad_(True) @@ -563,7 +575,9 @@ def get_batched_2d_axial_slices(data : Dict): selected = log_probs[range(len(logits)), y.view(-1)] a = torch.autograd.grad(selected.sum(), x_in)[0] alpha_prod_t = scheduler.alphas_cumprod[t] - updated_noise = model_output- (1 - alpha_prod_t).sqrt() * scale*a #update the predicted noise epsilon with the gradient of the classifier + updated_noise = ( + model_output - (1 - alpha_prod_t).sqrt() * scale * a + ) # update the predicted noise epsilon with the gradient of the classifier current_img, _ = scheduler.step(updated_noise, t, current_img) torch.cuda.empty_cache() @@ -576,14 +590,24 @@ def get_batched_2d_axial_slices(data : Dict): # %% [markdown] -# ### Anomaly Detection +# # Anomaly Detection # To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction. # %% +def visualize(img): + _min = img.min() + _max = img.max() + normalized_img = (img - _min) / (_max - _min) + return normalized_img -diff=abs(inputimg.cpu()-current_img[0, 0].cpu()).detach().numpy() + +diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() plt.style.use("default") plt.imshow(diff, cmap="jet") plt.tight_layout() plt.axis("off") plt.show() + +# %% + +# %% diff --git a/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb deleted file mode 100644 index 3f9e39a8..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/Untitled.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f37e04b7-9695-4a24-85bb-fffdd87ee1b9", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb deleted file mode 100644 index 363fcab7..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/Untitled1.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb deleted file mode 100644 index 06ad686a..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.ipynb +++ /dev/null @@ -1,437 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "cf6673e1", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# # Diff-SCM\n", - "# \n", - "# This tutorial illustrates how to load the 2D BRATS dataset.\n", - "# \n", - "# \n", - "# ## Setup environment" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "2dc388db", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "done\n" - ] - } - ], - "source": [ - "\n", - "\n", - "get_ipython().system('python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"')\n", - "get_ipython().system('python -c \"import matplotlib\" || pip install -q matplotlib')\n", - "get_ipython().run_line_magic('matplotlib', 'inline')\n", - "print('done')\n", - "\n", - "\n", - "# ## Setup imports" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "4167c04e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "MONAI version: 1.1.dev2248\n", - "Numpy version: 1.23.2\n", - "Pytorch version: 1.12.1\n", - "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", - "MONAI rev id: 3400bd91422ccba9ccc3aa2ffe7fecd4eb5596bf\n", - "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", - "\n", - "Optional dependencies:\n", - "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", - "Nibabel version: 4.0.1\n", - "scikit-image version: 0.19.3\n", - "Pillow version: 9.2.0\n", - "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n", - "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", - "TorchVision version: 0.13.1\n", - "tqdm version: 4.64.1\n", - "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", - "psutil version: 5.9.4\n", - "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", - "einops version: 0.6.0\n", - "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", - "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", - "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", - "\n", - "For details about installing the optional dependencies, please visit:\n", - " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", - "\n" - ] - } - ], - "source": [ - "\n", - "\n", - "# Copyright 2020 MONAI Consortium\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "# http://www.apache.org/licenses/LICENSE-2.0\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License.\n", - "import os\n", - "import shutil\n", - "import tempfile\n", - "import time\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import torch\n", - "import torch.nn.functional as F\n", - "from monai import transforms\n", - "from monai.apps import DecathlonDataset\n", - "from monai.config import print_config\n", - "from monai.data import CacheDataset, DataLoader\n", - "from monai.utils import first, set_determinism\n", - "from torch.cuda.amp import GradScaler, autocast\n", - "from tqdm import tqdm\n", - "\n", - "from generative.inferers import DiffusionInferer\n", - "\n", - "# TODO: Add right import reference after deployed\n", - "from generative.networks.nets import DiffusionModelUNet\n", - "from generative.networks.schedulers import DDPMScheduler\n", - "\n", - "print_config()\n", - "\n", - "\n", - "# ## Setup data directory" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "86b390cd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/tmp/tmpf7ygl4zq\n" - ] - } - ], - "source": [ - "\n", - "\n", - "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", - "root_dir = tempfile.mkdtemp() if directory is None else directory\n", - "print(root_dir)\n", - "root_dir= '/tmp/tmp6o69ziv1'\n", - "\n", - "\n", - "# ## Set deterministic training for reproducibility" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "6d644892", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "\n", - "set_determinism(42)\n", - "\n", - "\n", - "# ## Setup MedNIST Dataset and training and validation dataloaders\n", - "# In this tutorial, we will train our models on the MedNIST dataset available on MONAI\n", - "# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset).\n", - "# Here, we will use the \"Hand\" and \"HeadCT\", where our conditioning variable `class` will specify the modality." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "5c29c6a2", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2023-01-20 09:47:29,125 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-01-20 09:47:29,126 - INFO - File exists: /tmp/tmp6o69ziv1/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-01-20 09:47:29,127 - INFO - Non-empty folder exists in /tmp/tmp6o69ziv1/Task01_BrainTumour, skipped extracting.\n", - "Image shape torch.Size([1, 64, 64, 64])\n" - ] - } - ], - "source": [ - "\n", - "\n", - "batch_size = 2\n", - "channel = 0 # 0 = Flair\n", - "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", - "\n", - "train_transforms = transforms.Compose(\n", - " [\n", - " transforms.LoadImaged(keys=[\"image\",\"label\"]),\n", - " transforms.EnsureChannelFirstd(keys=[\"image\",\"label\"]),\n", - " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", - " transforms.AddChanneld(keys=[\"image\"]),\n", - " transforms.EnsureTyped(keys=[\"image\",\"label\"]),\n", - " transforms.Orientationd(keys=[\"image\",\"label\"], axcodes=\"RAS\"),\n", - " transforms.Spacingd(\n", - " keys=[\"image\",\"label\"],\n", - " pixdim=(3.0, 3.0, 2.0),\n", - " mode=(\"bilinear\", \"nearest\"),\n", - " ),\n", - " transforms.CenterSpatialCropd(keys=[\"image\",\"label\"], roi_size=(64, 64, 64)),\n", - " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", - " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", - " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", - " ]\n", - ")\n", - "train_ds = DecathlonDataset(\n", - " root_dir=root_dir,\n", - " task=\"Task01_BrainTumour\",\n", - " section=\"training\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", - " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", - " seed=0,\n", - " transform=train_transforms,\n", - ")\n", - "nb_3D_images_to_mix = 2\n", - "train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4)\n", - "print(f'Image shape {train_ds[0][\"image\"].shape}')" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "16e750a6", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "\n", - "from typing import Dict\n", - "def get_batched_2d_axial_slices(data : Dict):\n", - " images_3D = data['image']\n", - " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1])\n", - " slice_label = data['slice_label']\n", - " #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float()\n", - " slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze()\n", - " return batched_2d_slices, slice_label\n", - "\n", - "\n", - "# ### Visualisation of the training images" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "310b925c", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "check_data torch.Size([2, 1, 64, 64, 64]) torch.Size([2, 64])\n" - ] - } - ], - "source": [ - "\n", - "\n", - "check_data = first(train_loader_3D)\n", - "print('check_data', check_data[\"image\"].shape, check_data[\"slice_label\"].shape)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "4105a01f", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "idx [tensor(125), tensor(70), tensor(71), tensor(10), tensor(112), tensor(72), tensor(100), tensor(108), tensor(48), tensor(90), tensor(5), tensor(83), tensor(53), tensor(38), tensor(121), tensor(115), tensor(116), tensor(75), tensor(34), tensor(2), tensor(118), tensor(46), tensor(57), tensor(64), tensor(107), tensor(126), tensor(109), tensor(98), tensor(15), tensor(13), tensor(113), tensor(93), tensor(106), tensor(73), tensor(4), tensor(102), tensor(21), tensor(96), tensor(30), tensor(18), tensor(91), tensor(16), tensor(77), tensor(49), tensor(50), tensor(123), tensor(28), tensor(42), tensor(23), tensor(6), tensor(12), tensor(65), tensor(31), tensor(41), tensor(3), tensor(101), tensor(67), tensor(54), tensor(62), tensor(120), tensor(94), tensor(80), tensor(35), tensor(9), tensor(82), tensor(84), tensor(14), tensor(32), tensor(127), tensor(59), tensor(25), tensor(63), tensor(87), tensor(92), tensor(40), tensor(97), tensor(51), tensor(7), tensor(105), tensor(19), tensor(88), tensor(36), tensor(20), tensor(110), tensor(29), tensor(111), tensor(60), tensor(44), tensor(45), tensor(52), tensor(68), tensor(124), tensor(37), tensor(117), tensor(85), tensor(17), tensor(95), tensor(55), tensor(0), tensor(56), tensor(86), tensor(58), tensor(47), tensor(89), tensor(122), tensor(22), tensor(78), tensor(79), tensor(11), tensor(61), tensor(119), tensor(27), tensor(114), tensor(103), tensor(43), tensor(99), tensor(24), tensor(8), tensor(81), tensor(33), tensor(104), tensor(26), tensor(66), tensor(39), tensor(69), tensor(76), tensor(74), tensor(1)] 128\n", - "Batch shape: torch.Size([128, 1, 64, 64])\n", - "Slices class: tensor([0., 1., 0., 0.])\n" - ] - }, - { - "data": { - "image/png": "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0DkAAAAA05KlFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNWUoBAAAA0JylFAAAAADNzZvq4OTk5Nk8DwAAAADOI74pBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBzllIAAAAANGcpBQAAAEBz/w/KFbC7XwVz4QAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data)\n", - "idx = list(torch.randperm(batched_2d_slices.shape[0]))\n", - "print('idx', idx, len(idx))\n", - "slices = [0,30,45,63]\n", - "print(f\"Batch shape: {batched_2d_slices.shape}\")\n", - "print(f\"Slices class: {slice_label[idx][slices].view(-1)}\")\n", - "image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze()\n", - "plt.figure(\"training images\", (12, 6))\n", - "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.axis(\"off\")\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "21e0c944", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "torch.Size([128])" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "\n", - "slice_label.shape\n", - "\n", - "\n", - "# ## Check Distribution of Healthy / Unhealthy" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "1114650d", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(torch.Size([2, 1, 64, 64]), torch.Size([2]))" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))#\n", - "a,b = next(subset_2D) #what is a, what is b?\n", - "a.shape, b.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "5633a8c8", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAHDCAYAAACESXgYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9mElEQVR4nO3de5yN5f7/8fcyhzUHMxODOWSMoSHFRAgjezBm2kjbRiclkk50kG0TkqVvjd1UUom9dcCuSAdKXyWDTGSqITrQg9rOmyGSmRxGzPX7w2/W1zIHs8bMpaXX8/G4Hw/rWte678+61j1rvd1HhzHGCAAAwJIa57sAAADwx0L4AAAAVhE+AACAVYQPAABgFeEDAABYRfgAAABWET4AAIBVhA8AAGAV4QMAAFhF+DgPZs2aJYfD4Z6CgoIUHR2tLl26aNKkSdq3b1+J17hcLjkcDq+Wc+TIEblcLq1YscKr15W2rIYNG+raa6/1aj5nM2fOHE2ZMqXU5xwOh1wuV5Uur6otW7ZMbdq0UWhoqBwOh957771Kz6t4ndi2bZu7bdCgQWrYsOE51/l70bBhQw0aNMj9ePfu3XK5XFq/fn2JvoMGDVLNmjXtFVdJ27Ztk8Ph0KxZs6pl/qWtAxkZGaWua8Xr0Jo1a6qllvKU91mWZuPGjXK5XB7rO/5YCB/n0cyZM5WTk6OsrCy9+OKLatmypZ588kk1a9ZMS5cu9eg7ZMgQ5eTkeDX/I0eOaOLEiV6Hj8osqzLKCx85OTkaMmRItddQWcYY3XDDDQoICNDChQuVk5OjlJSUKl3G+PHjtWDBgiqd5/m0YMECjR8/3v149+7dmjhxYoV/sP6ISlsHygof55O3n+XGjRs1ceJEwscfmP/5LuCPrHnz5mrTpo37cd++ffXQQw/p6quvVp8+ffTDDz8oKipKklS/fn3Vr1+/Wus5cuSIQkJCrCzrbNq3b39el382u3fv1s8//6y//vWvSk1NrZZlNG7cuFrme760atXqfJfgcy60deCP6OjRowoKCvJ6y/WFji0fvzMNGjTQM888o4KCAv3rX/9yt5e2K2T58uXq3LmzIiMjFRwcrAYNGqhv3746cuSItm3bprp160qSJk6c6N7FU7zZu3h+X331lfr166datWq5v+jK28WzYMECJSUlKSgoSI0aNdLzzz/v8Xxpuw8kacWKFXI4HO6tMJ07d9aiRYu0fft2j11QxUrb7fLdd9/pL3/5i2rVqqWgoCC1bNlSs2fPLnU5c+fO1bhx4xQbG6vw8HB169ZNmzZtKnvgT7Nq1SqlpqYqLCxMISEhSk5O1qJFi9zPu1wudzgbPXq0HA5HubtHioqK9Pjjj6tp06YKDg7WRRddpKSkJD333HPl1lHaJveioiK98MILatmypXte7du318KFCz36zZs3Tx06dFBoaKhq1qypa665RuvWrfPos2XLFt10002KjY2V0+lUVFSUUlNTy/3f66JFi+RwOJSbm+tue/fdd+VwONSzZ0+PvklJSerbt6/78em7XVasWKG2bdtKkm6//Xb353/mZ/7jjz+qR48eqlmzpuLi4vS3v/1NhYWF5Q2b+/2np6crJiZGwcHBatasmR5++GEdPnzYo1/x7p2KLGf37t264YYbFBYWpoiICN14443Ky8s7ay35+fny9/fXU0895W7bv3+/atSooYiICJ04ccLd/sADD6hu3boqvt/nmeuAw+HQ4cOHNXv2bPeYde7c2WN5BQUFuvfee1WnTh1FRkaqT58+2r17t0efoqIiZWZm6tJLL5XT6VS9evV02223adeuXR79ztxVVqxz587u5Vb0syw2a9YsXX/99ZKkLl26uPsX77qqyDKLl+twODRnzhyNHj1aMTExqlmzpnr16qW9e/eqoKBAd911l+rUqaM6dero9ttv16+//uoxz2PHjmnMmDFKSEhQYGCgLr74Yg0bNky//PKLR7+y3s+ZtRZ//y1ZskSDBw9W3bp1FRISUqF19o+G8PE71KNHD/n5+enTTz8ts8+2bdvUs2dPBQYG6tVXX9XixYv1j3/8Q6GhoTp+/LhiYmK0ePFiSdIdd9yhnJwc5eTkeGz2lqQ+ffrokksu0dtvv61//vOf5da1fv16DR8+XA899JAWLFig5ORkPfjgg3r66ae9fo/Tpk1Tx44dFR0d7a6tvF09mzZtUnJysjZs2KDnn39e8+fP12WXXaZBgwYpMzOzRP+xY8dq+/btevnllzVjxgz98MMP6tWrl06ePFluXdnZ2eratasOHTqkV155RXPnzlVYWJh69eqlefPmSTq1W2r+/PmSpPvvv185OTnl7h7JzMyUy+XSzTffrEWLFmnevHm64447SnzBVcSgQYP04IMPqm3btpo3b57efPNNXXfddR5hLyMjQzfffLMuu+wyvfXWW3rttddUUFCgTp06aePGje5+PXr00Nq1a5WZmamsrCxNnz5drVq1KreulJQUBQQEeOwWXLp0qYKDg5Wdna3ffvtNkrRv3z5999136tatW6nzufLKKzVz5kxJ0iOPPOL+/E/f1fbbb7/puuuuU2pqqt5//30NHjxYzz77rJ588smzjtMPP/ygHj166JVXXtHixYs1fPhwvfXWW+rVq1eJvhVZztGjR9WtWzctWbJEkyZN0ttvv63o6GjdeOONZ60lPDxcbdu29RizZcuWyel0qqCgQF9++aW7fenSperatWuZ4T8nJ0fBwcHq0aOHe8ymTZvm0WfIkCEKCAjQnDlzlJmZqRUrVujWW2/16HPvvfdq9OjRSktL08KFC/U///M/Wrx4sZKTk7V///6zvqfTVeSzPF3Pnj2VkZEhSXrxxRfd/c8MrxU1duxY7du3T7NmzdIzzzyjFStW6Oabb1bfvn0VERGhuXPnatSoUXrttdc0duxY9+uMMerdu7eefvppDRgwQIsWLdKIESM0e/Zsde3a9ZwCw+DBgxUQEKDXXntN77zzjgICAio9rwuWgXUzZ840kkxubm6ZfaKiokyzZs3cjydMmGBO/7jeeecdI8msX7++zHn89NNPRpKZMGFCieeK5/foo4+W+dzp4uPjjcPhKLG8tLQ0Ex4ebg4fPuzx3rZu3erR75NPPjGSzCeffOJu69mzp4mPjy+19jPrvummm4zT6TQ7duzw6Ne9e3cTEhJifvnlF4/l9OjRw6PfW2+9ZSSZnJycUpdXrH379qZevXqmoKDA3XbixAnTvHlzU79+fVNUVGSMMWbr1q1GknnqqafKnZ8xxlx77bWmZcuW5fYpbdwGDhzoMT6ffvqpkWTGjRtX5nx27Nhh/P39zf333+/RXlBQYKKjo80NN9xgjDFm//79RpKZMmXKWes/09VXX226du3qfnzJJZeYv//976ZGjRomOzvbGGPMG2+8YSSZzZs3u/vFx8ebgQMHuh/n5uYaSWbmzJklljFw4EAjybz11lse7T169DBNmzb1qt6ioiLz22+/mezsbCPJfP31114vZ/r06UaSef/99z363XnnnWW+h9M98sgjJjg42Bw7dswYY8yQIUPMn//8Z5OUlGQmTpxojDHmv//9r5FkZsyY4VHfmX8joaGhHuNY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KsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAqmoLH9OmTVNCQoKCgoLUunVrrVy5sroWBQAAfEi1hI958+Zp+PDhGjdunNatW6dOnTqpe/fu2rFjR3UsDgAA+JBqCR+TJ0/WHXfcoSFDhqhZs2aaMmWK4uLiNH369OpYHAAA8CH+VT3D48ePa+3atXr44Yc92tPT07V69eoS/QsLC1VYWOh+fOjQIUlSfn5+VZcmSSoqPFIt84Wn6vr8AFzY+I62ozq+o4vnaYw5a98qDx/79+/XyZMnFRUV5dEeFRWlvLy8Ev0nTZqkiRMnlmiPi4ur6tJgUcSU810BAKAs1fkdXVBQoIiIiHL7VHn4KOZwODweG2NKtEnSmDFjNGLECPfjoqIi/fzzz4qMjCy1/7nIz89XXFycdu7cqfDw8CqdN/4P42wH42wPY20H42xHdY2zMUYFBQWKjY09a98qDx916tSRn59fia0c+/btK7E1RJKcTqecTqdH20UXXVTVZXkIDw9nxbaAcbaDcbaHsbaDcbajOsb5bFs8ilX5AaeBgYFq3bq1srKyPNqzsrKUnJxc1YsDAAA+plp2u4wYMUIDBgxQmzZt1KFDB82YMUM7duzQPffcUx2LAwAAPqRawseNN96oAwcO6LHHHtOePXvUvHlzffjhh4qPj6+OxVWY0+nUhAkTSuzmQdVinO1gnO1hrO1gnO34PYyzw1TknBgAAIAqwr1dAACAVYQPAABgFeEDAABYRfgAAABWXXDhY9q0aUpISFBQUJBat26tlStXlts/OztbrVu3VlBQkBo1aqR//vOflir1bd6M8/z585WWlqa6desqPDxcHTp00Mcff2yxWt/l7fpc7LPPPpO/v79atmxZvQVeILwd58LCQo0bN07x8fFyOp1q3LixXn31VUvV+jZvx/qNN97QFVdcoZCQEMXExOj222/XgQMHLFXrez799FP16tVLsbGxcjgceu+99876mvPyO2guIG+++aYJCAgwL730ktm4caN58MEHTWhoqNm+fXup/bds2WJCQkLMgw8+aDZu3GheeuklExAQYN555x3LlfsWb8f5wQcfNE8++aT58ssvzebNm82YMWNMQECA+eqrryxX7lu8Hediv/zyi2nUqJFJT083V1xxhZ1ifVhlxvm6664z7dq1M1lZWWbr1q3miy++MJ999pnFqn2Tt2O9cuVKU6NGDfPcc8+ZLVu2mJUrV5rLL7/c9O7d23LlvuPDDz8048aNM++++66RZBYsWFBu//P1O3hBhY+rrrrK3HPPPR5tl156qXn44YdL7T9q1Chz6aWXerTdfffdpn379tVW44XA23EuzWWXXWYmTpxY1aVdUCo7zjfeeKN55JFHzIQJEwgfFeDtOH/00UcmIiLCHDhwwEZ5FxRvx/qpp54yjRo18mh7/vnnTf369autxgtJRcLH+fodvGB2uxw/flxr165Venq6R3t6erpWr15d6mtycnJK9L/mmmu0Zs0a/fbbb9VWqy+rzDifqaioSAUFBapdu3Z1lHhBqOw4z5w5U//5z380YcKE6i7xglCZcV64cKHatGmjzMxMXXzxxWrSpIlGjhypo0eP2ijZZ1VmrJOTk7Vr1y59+OGHMsZo7969euedd9SzZ08bJf8hnK/fwWq7q61t+/fv18mTJ0vcvC4qKqrETe6K5eXlldr/xIkT2r9/v2JiYqqtXl9VmXE+0zPPPKPDhw/rhhtuqI4SLwiVGecffvhBDz/8sFauXCl//wvmT7taVWact2zZolWrVikoKEgLFizQ/v37NXToUP38888c91GOyox1cnKy3njjDd144406duyYTpw4oeuuu04vvPCCjZL/EM7X7+AFs+WjmMPh8HhsjCnRdrb+pbXDk7fjXGzu3LlyuVyaN2+e6tWrV13lXTAqOs4nT55U//79NXHiRDVp0sRWeRcMb9bnoqIiORwOvfHGG7rqqqvUo0cPTZ48WbNmzWLrRwV4M9YbN27UAw88oEcffVRr167V4sWLtXXrVu4TVsXOx+/gBfPfozp16sjPz69Egt63b1+JVFcsOjq61P7+/v6KjIystlp9WWXGudi8efN0xx136O2331a3bt2qs0yf5+04FxQUaM2aNVq3bp3uu+8+Sad+JI0x8vf315IlS9S1a1crtfuSyqzPMTExuvjiiz1uHd6sWTMZY7Rr1y4lJiZWa82+qjJjPWnSJHXs2FF///vfJUlJSUkKDQ1Vp06d9Pjjj7N1ugqcr9/BC2bLR2BgoFq3bq2srCyP9qysLCUnJ5f6mg4dOpTov2TJErVp00YBAQHVVqsvq8w4S6e2eAwaNEhz5sxhf20FeDvO4eHh+vbbb7V+/Xr3dM8996hp06Zav3692rVrZ6t0n1KZ9bljx47avXu3fv31V3fb5s2bVaNGDdWvX79a6/VllRnrI0eOqEYNz58pPz8/Sf/3v3Ocm/P2O1ith7NaVnwa1yuvvGI2btxohg8fbkJDQ822bduMMcY8/PDDZsCAAe7+xacYPfTQQ2bjxo3mlVde4VTbCvB2nOfMmWP8/f3Niy++aPbs2eOefvnll/P1FnyCt+N8Js52qRhvx7mgoMDUr1/f9OvXz2zYsMFkZ2ebxMREM2TIkPP1FnyGt2M9c+ZM4+/vb6ZNm2b+85//mFWrVpk2bdqYq6666ny9hd+9goICs27dOrNu3TojyUyePNmsW7fOfTrz7+V38IIKH8YY8+KLL5r4+HgTGBhorrzySpOdne1+buDAgSYlJcWj/4oVK0yrVq1MYGCgadiwoZk+fbrlin2TN+OckpJiJJWYBg4caL9wH+Pt+nw6wkfFeTvO33//venWrZsJDg429evXNyNGjDBHjhyxXLVv8nasn3/+eXPZZZeZ4OBgExMTY2655Raza9cuy1X7jk8++aTc79vfy++gwxi2XQEAAHsumGM+AACAbyB8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsOr/AejmQumx7IhfAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "plt.hist(slice_label.view(-1).numpy(),bins = 5);\n", - "plt.title(\"Distribution of slices with and without tumour \\n 0 = no tumour, 1 = tumour\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "97cbfc78-54b5-4a98-b0b3-60e3a71fd25e", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "jupytext": { - "formats": "py:percent,ipynb" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - }, - "vscode": { - "interpreter": { - "hash": "a7e6f8385898884a13cbe220eefefb32cba5012927a94186742ddc14746e4dba" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py b/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py deleted file mode 100644 index db954dba..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/load_2d_brats.py +++ /dev/null @@ -1,201 +0,0 @@ -# --- -# jupyter: -# jupytext: -# formats: py:percent,ipynb -# text_representation: -# extension: .py -# format_name: percent -# format_version: '1.3' -# jupytext_version: 1.14.4 -# kernelspec: -# display_name: Python 3 (ipykernel) -# language: python -# name: python3 -# --- - -# %% - -# # Diff-SCM -# -# This tutorial illustrates how to load the 2D BRATS dataset. -# -# -# ## Setup environment - -# %% - - -get_ipython().system('python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]"') -get_ipython().system('python -c "import matplotlib" || pip install -q matplotlib') -get_ipython().run_line_magic('matplotlib', 'inline') -print('done') - - -# ## Setup imports - -# %% - - -# Copyright 2020 MONAI Consortium -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import shutil -import tempfile -import time - -import matplotlib.pyplot as plt -import numpy as np -import torch -import torch.nn.functional as F -from monai import transforms -from monai.apps import DecathlonDataset -from monai.config import print_config -from monai.data import CacheDataset, DataLoader -from monai.utils import first, set_determinism -from torch.cuda.amp import GradScaler, autocast -from tqdm import tqdm - -from generative.inferers import DiffusionInferer - -# TODO: Add right import reference after deployed -from generative.networks.nets import DiffusionModelUNet -from generative.networks.schedulers import DDPMScheduler - -print_config() - - -# ## Setup data directory - -# %% - - -directory = os.environ.get("MONAI_DATA_DIRECTORY") -root_dir = tempfile.mkdtemp() if directory is None else directory -print(root_dir) -root_dir= '/tmp/tmp6o69ziv1' - - -# ## Set deterministic training for reproducibility - -# %% - - -set_determinism(42) - - -# ## Setup MedNIST Dataset and training and validation dataloaders -# In this tutorial, we will train our models on the MedNIST dataset available on MONAI -# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset). -# Here, we will use the "Hand" and "HeadCT", where our conditioning variable `class` will specify the modality. - -# %% - - -batch_size = 2 -channel = 0 # 0 = Flair -assert channel in [0, 1, 2, 3], "Choose a valid channel" - -train_transforms = transforms.Compose( - [ - transforms.LoadImaged(keys=["image","label"]), - transforms.EnsureChannelFirstd(keys=["image","label"]), - transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), - transforms.AddChanneld(keys=["image"]), - transforms.EnsureTyped(keys=["image","label"]), - transforms.Orientationd(keys=["image","label"], axcodes="RAS"), - transforms.Spacingd( - keys=["image","label"], - pixdim=(3.0, 3.0, 2.0), - mode=("bilinear", "nearest"), - ), - transforms.CenterSpatialCropd(keys=["image","label"], roi_size=(64, 64, 64)), - transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), - transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), - transforms.Lambdad(keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()), - ] -) -train_ds = DecathlonDataset( - root_dir=root_dir, - task="Task01_BrainTumour", - section="training", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise - num_workers=4, - download=True, # Set download to True if the dataset hasnt been downloaded yet - seed=0, - transform=train_transforms, -) -nb_3D_images_to_mix = 2 -train_loader_3D = DataLoader(train_ds, batch_size=nb_3D_images_to_mix, shuffle=True, num_workers=4) -print(f'Image shape {train_ds[0]["image"].shape}') - - -# %% - - -from typing import Dict -def get_batched_2d_axial_slices(data : Dict): - images_3D = data['image'] - batched_2d_slices = torch.cat(images_3D.split(1, dim = -1), 0).squeeze(-1) # images_3D.view(images_3D.shape[0]*images_3D.shape[-1],*images_3D.shape[1:-1]) - slice_label = data['slice_label'] - #slice_label = (mask_label.reshape(mask_label.shape[0], -1, mask_label.shape[-1]).sum(1) > 0 ).float() - slice_label = torch.cat(slice_label.split(1, dim = -1),0).squeeze() - return batched_2d_slices, slice_label - - -# ### Visualisation of the training images - -# %% - - -check_data = first(train_loader_3D) -print('check_data', check_data["image"].shape, check_data["slice_label"].shape) - - -# %% - - -batched_2d_slices, slice_label = get_batched_2d_axial_slices(check_data) -idx = list(torch.randperm(batched_2d_slices.shape[0])) -print('idx', idx, len(idx)) -slices = [0,30,45,63] -print(f"Batch shape: {batched_2d_slices.shape}") -print(f"Slices class: {slice_label[idx][slices].view(-1)}") -image_visualisation = torch.cat(batched_2d_slices[idx][slices].squeeze().split(1), dim=2).squeeze() -plt.figure("training images", (12, 6)) -plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") -plt.axis("off") -plt.tight_layout() -plt.show() - - -# %% - - -slice_label.shape - - -# ## Check Distribution of Healthy / Unhealthy - -# %% - -subset_2D = zip(batched_2d_slices.split(batch_size),slice_label.split(batch_size))# -a,b = next(subset_2D) #what is a, what is b? -a.shape, b.shape - - -# %% - - -plt.hist(slice_label.view(-1).numpy(),bins = 5); -plt.title("Distribution of slices with and without tumour \n 0 = no tumour, 1 = tumour"); - - -# %% From c0cf8b319fcccf3b93fa609deec6a95449565c81 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 23 Feb 2023 16:10:08 +0100 Subject: [PATCH 06/23] run tests on all files --- .../networks/nets/diffusion_model_unet.py | 15 ++------ .../networks/nets/patchgan_discriminator.py | 1 - generative/networks/schedulers/ddim.py | 8 +--- tests/min_tests.py | 1 - tests/runner.py | 1 - tests/test_diffusion_inferer.py | 1 - tests/test_patch_gan.py | 1 - tests/utils.py | 3 -- .../generative/2d_ldm/2d_ldm_tutorial.py | 1 - .../2d_vqvae_transformer_tutorial.py | 4 +- ..._ddpm_classifier_free_guidance_tutorial.py | 2 +- ...fier_guidance_anomalydetection_tutorial.py | 38 ++++--------------- .../distributed_training/ddpm_training_ddp.py | 1 - 13 files changed, 14 insertions(+), 63 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 0ffdd7bf..c23d98f8 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1882,8 +1882,8 @@ def __init__( super().__init__() if with_conditioning is True and cross_attention_dim is None: raise ValueError( - "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " - "when using with_conditioning." + "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " + "when using with_conditioning." ) if cross_attention_dim is not None and with_conditioning is False: raise ValueError( @@ -1925,9 +1925,7 @@ def __init__( # time time_embed_dim = num_channels[0] * 4 self.time_embed = nn.Sequential( - nn.Linear(num_channels[0], time_embed_dim), - nn.SiLU(), - nn.Linear(time_embed_dim, time_embed_dim), + nn.Linear(num_channels[0], time_embed_dim), nn.SiLU(), nn.Linear(time_embed_dim, time_embed_dim) ) # class embedding @@ -1961,12 +1959,7 @@ def __init__( self.down_blocks.append(down_block) - self.out = nn.Sequential( - nn.Linear(4096, 512), - nn.ReLU(), - nn.Dropout(0.1), - nn.Linear(512, self.out_channels), - ) + self.out = nn.Sequential(nn.Linear(4096, 512), nn.ReLU(), nn.Dropout(0.1), nn.Linear(512, self.out_channels)) def forward( self, diff --git a/generative/networks/nets/patchgan_discriminator.py b/generative/networks/nets/patchgan_discriminator.py index b5a98c88..bf09b743 100644 --- a/generative/networks/nets/patchgan_discriminator.py +++ b/generative/networks/nets/patchgan_discriminator.py @@ -154,7 +154,6 @@ def __init__( dropout: float | tuple = 0.0, last_conv_kernel_size: int | None = None, ) -> None: - super().__init__() self.num_layers_d = num_layers_d self.num_channels = num_channels diff --git a/generative/networks/schedulers/ddim.py b/generative/networks/schedulers/ddim.py index 1c7f85bb..607f2bb1 100644 --- a/generative/networks/schedulers/ddim.py +++ b/generative/networks/schedulers/ddim.py @@ -306,13 +306,7 @@ def reversed_step( return pred_post_sample, pred_original_sample - def add_noise( - self, - original_samples: torch.Tensor, - noise: torch.Tensor, - timesteps: torch.Tensor, - ) -> torch.Tensor: - + def add_noise(self, original_samples: torch.Tensor, noise: torch.Tensor, timesteps: torch.Tensor) -> torch.Tensor: """ Add noise to the original samples. diff --git a/tests/min_tests.py b/tests/min_tests.py index b4373dd8..dcb8b6b4 100644 --- a/tests/min_tests.py +++ b/tests/min_tests.py @@ -53,7 +53,6 @@ def run_testsuit(): if __name__ == "__main__": - # testing import submodules from monai.utils.module import load_submodules diff --git a/tests/runner.py b/tests/runner.py index 96a1d4a5..7a7cc9f2 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -114,7 +114,6 @@ def get_default_pattern(loader): if __name__ == "__main__": - # Parse input arguments args = parse_args() diff --git a/tests/test_diffusion_inferer.py b/tests/test_diffusion_inferer.py index c450ed3d..6faf0e68 100644 --- a/tests/test_diffusion_inferer.py +++ b/tests/test_diffusion_inferer.py @@ -53,7 +53,6 @@ class TestDiffusionSamplingInferer(unittest.TestCase): @parameterized.expand(TEST_CASES) def test_call(self, model_params, input_shape): - model = DiffusionModelUNet(**model_params) device = "cuda:0" if torch.cuda.is_available() else "cpu" model.to(device) diff --git a/tests/test_patch_gan.py b/tests/test_patch_gan.py index 7e8df802..50bab99e 100644 --- a/tests/test_patch_gan.py +++ b/tests/test_patch_gan.py @@ -94,7 +94,6 @@ def test_too_small_shape(self): MultiScalePatchDiscriminator(**TEST_TOO_SMALL_SIZE[0]) def test_script(self): - net = MultiScalePatchDiscriminator( num_d=2, num_layers_d=3, diff --git a/tests/utils.py b/tests/utils.py index 601bd9e9..a16f77f6 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,8 +1,6 @@ # COPIED FROM https://github.com/Project-MONAI/MONAI/blob/fdd07f36ecb91cfcd491533f4792e1a67a9f89fc/tests/utils.py # --------------------------------------------------------------- -from __future__ import annotations - # Copyright (c) MONAI Consortium # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -578,7 +576,6 @@ def run_process(func, args, kwargs, results): results.put(e) def __call__(self, obj): - if self.skip_timing: return obj diff --git a/tutorials/generative/2d_ldm/2d_ldm_tutorial.py b/tutorials/generative/2d_ldm/2d_ldm_tutorial.py index bc464a99..3667d086 100644 --- a/tutorials/generative/2d_ldm/2d_ldm_tutorial.py +++ b/tutorials/generative/2d_ldm/2d_ldm_tutorial.py @@ -406,7 +406,6 @@ scheduler.set_timesteps(num_inference_steps=1000) with torch.no_grad(): - z_mu, z_sigma = autoencoderkl.encode(image) z = autoencoderkl.sampling(z_mu, z_sigma) diff --git a/tutorials/generative/2d_vqvae_transformer/2d_vqvae_transformer_tutorial.py b/tutorials/generative/2d_vqvae_transformer/2d_vqvae_transformer_tutorial.py index bcd38b91..288a04e5 100644 --- a/tutorials/generative/2d_vqvae_transformer/2d_vqvae_transformer_tutorial.py +++ b/tutorials/generative/2d_vqvae_transformer/2d_vqvae_transformer_tutorial.py @@ -342,10 +342,10 @@ # %% [markdown] # First we will define a function to allow us to generate random samples from the transformer. This will allow us to keep track of training progress as well to see how samples look during the training cycle + # %% @torch.no_grad() def generate(net, vqvae_model, starting_tokens, seq_len, **kwargs): - progress_bar = iter(range(seq_len)) latent_seq = starting_tokens.long() @@ -395,7 +395,6 @@ def generate(net, vqvae_model, starting_tokens, seq_len, **kwargs): progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=110) progress_bar.set_description(f"Epoch {epoch}") for step, batch in progress_bar: - images = batch["image"].to(device) # Encode images using vqvae and transformer to 1D sequence quantizations = vqvae_model.index_quantize(images) @@ -429,7 +428,6 @@ def generate(net, vqvae_model, starting_tokens, seq_len, **kwargs): val_loss = 0 with torch.no_grad(): for val_step, batch in enumerate(val_loader, start=1): - images = batch["image"].to(device) # Encode images using vqvae and transformer to 1D sequence quantizations = vqvae_model.index_quantize(images) diff --git a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py index c8c14c29..65543957 100644 --- a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py +++ b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py @@ -230,7 +230,7 @@ for step, batch in progress_bar: images = batch["image"].to(device) classes = batch["class"].to(device) - print('images', images.shape, 'classes', classes.shape, classes) + print("images", images.shape, "classes", classes.shape, classes) optimizer.zero_grad(set_to_none=True) with autocast(enabled=True): diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py index 466c7295..f2433f86 100644 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py +++ b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py @@ -120,11 +120,7 @@ transforms.AddChanneld(keys=["image"]), transforms.EnsureTyped(keys=["image", "label"]), transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), - transforms.Spacingd( - keys=["image", "label"], - pixdim=(3.0, 3.0, 2.0), - mode=("bilinear", "nearest"), - ), + transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), @@ -251,9 +247,7 @@ def get_batched_2d_axial_slices(data: Dict): ) model.to(device) -scheduler = DDIMScheduler( - num_train_timesteps=1000, -) +scheduler = DDIMScheduler(num_train_timesteps=1000) optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) @@ -312,11 +306,7 @@ def get_batched_2d_axial_slices(data: Dict): scaler.step(optimizer) scaler.update() epoch_loss += loss.item() - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) epoch_loss_list.append(epoch_loss / (step + 1)) if (epoch) % val_interval == 0: @@ -336,11 +326,7 @@ def get_batched_2d_axial_slices(data: Dict): val_loss = F.mse_loss(noise_pred.float(), noise.float()) val_epoch_loss += val_loss.item() - progress_bar.set_postfix( - { - "val_loss": val_epoch_loss / (step + 1), - } - ) + progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start @@ -407,7 +393,6 @@ def get_batched_2d_axial_slices(data: Dict): if train_classifier is False: classifier.load_state_dict(torch.load("./classifier.pt", map_location={"cuda:0": "cpu"})) else: - scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): @@ -440,11 +425,7 @@ def get_batched_2d_axial_slices(data: Dict): optimizer_cls.step() epoch_loss += loss.item() - progress_bar.set_postfix( - { - "loss": epoch_loss / (step + 1), - } - ) + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) epoch_loss_list.append(epoch_loss / (step + 1)) print("final step train", step) @@ -469,11 +450,7 @@ def get_batched_2d_axial_slices(data: Dict): val_epoch_loss += val_loss.item() _, predicted = torch.max(pred, 1) - progress_bar_val.set_postfix( - { - "val_loss": val_epoch_loss / (step + 1), - } - ) + progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start @@ -533,7 +510,6 @@ def get_batched_2d_axial_slices(data: Dict): progress_bar = tqdm(range(L)) # go back and forth L timesteps for t in progress_bar: # go through the noising process - with autocast(enabled=False): with torch.no_grad(): model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) @@ -560,7 +536,6 @@ def get_batched_2d_axial_slices(data: Dict): progress_bar = tqdm(range(L)) # go back and forth L timesteps for i in progress_bar: # go through the denoising process - t = L - i with autocast(enabled=True): with torch.no_grad(): @@ -593,6 +568,7 @@ def get_batched_2d_axial_slices(data: Dict): # # Anomaly Detection # To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction. + # %% def visualize(img): _min = img.min() diff --git a/tutorials/generative/distributed_training/ddpm_training_ddp.py b/tutorials/generative/distributed_training/ddpm_training_ddp.py index 07fab1b0..de0f3734 100644 --- a/tutorials/generative/distributed_training/ddpm_training_ddp.py +++ b/tutorials/generative/distributed_training/ddpm_training_ddp.py @@ -83,7 +83,6 @@ def __init__( num_workers: int = 0, shuffle: bool = False, ) -> None: - if not os.path.isdir(root_dir): raise ValueError("root directory root_dir must be a directory.") self.section = section From 7bb6d88524b3eec24310d3aa10ae6ac382874554 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 15 Mar 2023 09:21:51 +0100 Subject: [PATCH 07/23] create folder for the anomaly detection tutorials --- .../networks/nets/diffusion_model_unet.py | 17 +- .../mednist_ddpm/bundle/configs/common.yaml | 9 +- .../mednist_ddpm/bundle/configs/infer.yaml | 2 +- .../mednist_ddpm/bundle/configs/logging.conf | 2 +- .../mednist_ddpm/bundle/configs/metadata.json | 4 +- .../mednist_ddpm/bundle/configs/train.yaml | 30 +- .../bundle/configs/train_multigpu.yaml | 4 +- .../bundle/docs/sub_train_multigpu.sh | 2 +- .../mednist_ddpm/bundle/scripts/__init__.py | 4 +- ...tection_tutorial_classifier_guidance.ipynb | 2913 +++++++++++++++++ ...ydetection_tutorial_classifier_guidance.py | 553 ++++ 11 files changed, 3507 insertions(+), 33 deletions(-) create mode 100644 tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb create mode 100644 tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index c23d98f8..0a4b495b 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1868,16 +1868,19 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - num_res_blocks: int, + num_res_blocks: Sequence[int] | int = (2, 2, 2, 2), num_channels: Sequence[int] = (32, 64, 64, 64), attention_levels: Sequence[bool] = (False, False, True, True), norm_num_groups: int = 32, norm_eps: float = 1e-6, - num_head_channels: Union[int, Sequence[int]] = 8, + resblock_updown: bool = False, + num_head_channels: int | Sequence[int] = 8, with_conditioning: bool = False, transformer_num_layers: int = 1, - cross_attention_dim: Optional[int] = None, - num_class_embeds: Optional[int] = None, + cross_attention_dim: int | None = None, + num_class_embeds: int | None = None, + upcast_attention: bool = False, + ) -> None: super().__init__() if with_conditioning is True and cross_attention_dim is None: @@ -1941,20 +1944,24 @@ def __init__( output_channel = num_channels[i] is_final_block = i == len(num_channels) # - 1 + + down_block = get_down_block( spatial_dims=spatial_dims, in_channels=input_channel, out_channels=output_channel, temb_channels=time_embed_dim, - num_res_blocks=num_res_blocks, + num_res_blocks=num_res_blocks[i], norm_num_groups=norm_num_groups, norm_eps=norm_eps, add_downsample=not is_final_block, + resblock_updown=resblock_updown, with_attn=(attention_levels[i] and not with_conditioning), with_cross_attn=(attention_levels[i] and with_conditioning), num_head_channels=num_head_channels[i], transformer_num_layers=transformer_num_layers, cross_attention_dim=cross_attention_dim, + upcast_attention=upcast_attention, ) self.down_blocks.append(down_block) diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml index e48b917b..c6073eb5 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml @@ -1,6 +1,6 @@ # This file defines common definitions used in training and inference, most importantly the network definition -imports: +imports: - $import os - $import datetime - $import torch @@ -27,8 +27,8 @@ network_def: attention_levels: [false, true, true] num_res_blocks: 1 num_head_channels: 128 - -network: $@network_def.to(@device) + +network: $@network_def.to(@device) bundle_root: . ckpt_path: $@bundle_root + '/models/model.pt' @@ -54,8 +54,7 @@ base_transforms: scheduler: _target_: generative.networks.schedulers.DDPMScheduler num_train_timesteps: '@num_train_timesteps' - + inferer: _target_: generative.inferers.DiffusionInferer scheduler: '@scheduler' - \ No newline at end of file diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml index f140c3b6..46297e18 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml @@ -35,4 +35,4 @@ testing: #alternative version which saves to a jpg file testing_jpg: - '@load_state' -- '$@save_trans(@sample(@noise.to(@device))[0])' \ No newline at end of file +- '$@save_trans(@sample(@noise.to(@device))[0])' diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf b/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf index db85a0b9..91c1a21c 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf +++ b/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf @@ -18,4 +18,4 @@ formatter=fullFormatter args=(sys.stdout,) [formatter_fullFormatter] -format=%(asctime)s - %(name)s - %(levelname)s - %(message)s \ No newline at end of file +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json b/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json index aef66f9f..1e657634 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json +++ b/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json @@ -7,7 +7,9 @@ "monai_version": "1.0.0", "pytorch_version": "1.10.2", "numpy_version": "1.21.2", - "optional_packages_version": {"generative":"0.1.0"}, + "optional_packages_version": { + "generative": "0.1.0" + }, "task": "MedNIST Hand Generation", "description": "", "authors": "Walter Hugo Lopez Pinaya, Mark Graham, and Eric Kerfoot", diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml index 739b3c1f..0297c2b3 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml @@ -4,7 +4,7 @@ output_dir: $datetime.datetime.now().strftime('./results/output_%y%m%d_%H%M%S') dataset_dir: ./data -train_data: +train_data: _target_ : MedNISTDataset root_dir: '@dataset_dir' section: training @@ -12,7 +12,7 @@ train_data: progress: false seed: 0 -val_data: +val_data: _target_ : MedNISTDataset root_dir: '@dataset_dir' section: validation @@ -37,7 +37,7 @@ save_interval: 5 train_transforms: - _target_: RandAffined keys: '@image' - rotate_range: + rotate_range: - ['$-np.pi / 36', '$np.pi / 36'] - ['$-np.pi / 36', '$np.pi / 36'] translate_range: @@ -49,14 +49,14 @@ train_transforms: spatial_size: [64, 64] padding_mode: "zeros" prob: '@rand_prob' - + train_ds: _target_: Dataset data: $@train_datalist transform: _target_: Compose transforms: '$@base_transforms + @train_transforms' - + train_loader: _target_: ThreadDataLoader dataset: '@train_ds' @@ -65,7 +65,7 @@ train_loader: num_workers: '@num_workers' use_thread_workers: '@use_thread_workers' persistent_workers: '$@num_workers > 0' - shuffle: true + shuffle: true val_ds: _target_: Dataset @@ -73,7 +73,7 @@ val_ds: transform: _target_: Compose transforms: '@base_transforms' - + val_loader: _target_: DataLoader dataset: '@val_ds' @@ -81,19 +81,19 @@ val_loader: num_workers: '@num_workers' persistent_workers: '$@num_workers > 0' shuffle: false - + lossfn: _target_: torch.nn.MSELoss - + optimizer: _target_: torch.optim.Adam params: $@network.parameters() lr: '@lr' - + prepare_batch: _target_: scripts.DiffusionPrepareBatch num_train_timesteps: '@num_train_timesteps' - + val_handlers: - _target_: StatsHandler name: train_log @@ -114,7 +114,7 @@ evaluator: output_transform: $monai.handlers.from_engine([@pred, @label]) metric_cmp_fn: '$scripts.inv_metric_cmp_fn' val_handlers: '$list(filter(bool, @val_handlers))' - + handlers: - _target_: CheckpointLoader _disabled_: $not os.path.exists(@ckpt_path) @@ -144,14 +144,14 @@ trainer: optimizer: '@optimizer' inferer: '@inferer' prepare_batch: '@prepare_batch' - key_train_metric: + key_train_metric: train_acc: _target_: MeanSquaredError output_transform: $monai.handlers.from_engine([@pred, @label]) metric_cmp_fn: '$scripts.inv_metric_cmp_fn' train_handlers: '$list(filter(bool, @handlers))' amp: '@use_amp' - -training: + +training: - '$monai.utils.set_determinism(0)' - '$@trainer.run()' diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml index 2811612f..51f5acf4 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml @@ -21,10 +21,10 @@ vsampler: shuffle: false val_loader#sampler: '@vsampler' -training: +training: - $import torch.distributed as dist - $dist.init_process_group(backend='nccl') - $torch.cuda.set_device(@device) - $monai.utils.set_determinism(seed=123), - $@trainer.run() -- $dist.destroy_process_group() \ No newline at end of file +- $dist.destroy_process_group() diff --git a/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh b/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh index 7c424af0..4d5f6af0 100644 --- a/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh +++ b/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh @@ -33,4 +33,4 @@ torchrun --standalone --nnodes=1 --nproc_per_node=2 -m monai.bundle run training --config_file "$CONFIG" \ --logging_file "$BUNDLE/configs/logging.conf" \ --bundle_root "$BUNDLE" \ - --dataset_dir "$DATASET" \ No newline at end of file + --dataset_dir "$DATASET" diff --git a/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py b/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py index 344830d2..9f3fc41c 100644 --- a/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py +++ b/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py @@ -32,8 +32,8 @@ def get_timesteps(self, images: torch.Tensor) -> torch.Tensor: def __call__( self, - batchdata: Dict[str, torch.Tensor], - device: Union[str, torch.device] | None = None, + batchdata: dict[str, torch.Tensor], + device: str | torch.device | None = None, non_blocking: bool = False, **kwargs, ): diff --git a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb new file mode 100644 index 00000000..e335e271 --- /dev/null +++ b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb @@ -0,0 +1,2913 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63d95da6", + "metadata": {}, + "source": [ + "# Diffusion Models for Medical Anomaly Detection with Classifier Guidance\n", + "\n", + "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", + "\n", + "We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\\\n", + "We then tranlsate an input slice to its healthy reconstruction using DDIMs.\\\n", + "Anomaly detection is performed by taking the difference between input and output, as proposed in [1].\n", + "\n", + "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", + "\n", + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "75f2d5f3", + "metadata": {}, + "outputs": [], + "source": [ + "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", + "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "!python -c \"import seaborn\" || pi resblock_updown: bool = False,p install -q seaborn" + ] + }, + { + "cell_type": "markdown", + "id": "6b766027", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "972ed3f3", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/anomaly_detection/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", + "MONAI version: 1.2.dev2304\n", + "Numpy version: 1.23.2\n", + "Pytorch version: 1.12.1\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 9a57be5aab9f2c2a134768c0c146399150e247a0\n", + "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: 0.4.10\n", + "ITK version: 5.3.0\n", + "Nibabel version: 4.0.1\n", + "scikit-image version: 0.19.3\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: 2.12.0\n", + "gdown version: 4.6.4\n", + "TorchVision version: 0.13.1\n", + "tqdm version: 4.64.1\n", + "lmdb version: 1.4.0\n", + "psutil version: 5.9.4\n", + "pandas version: 1.5.3\n", + "einops version: 0.6.0\n", + "transformers version: 4.21.3\n", + "mlflow version: 2.1.1\n", + "pynrrd version: 1.0.0\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "import os\n", + "import sys\n", + "import time\n", + "from typing import Dict\n", + "import tempfile\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from monai import transforms\n", + "from monai.apps import DecathlonDataset\n", + "from monai.config import print_config\n", + "from monai.data import DataLoader\n", + "from monai.utils import first, set_determinism\n", + "from torch.cuda.amp import GradScaler, autocast\n", + "from tqdm import tqdm\n", + "\n", + "from generative.inferers import DiffusionInferer\n", + "from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet\n", + "from generative.networks.schedulers.ddim import DDIMScheduler\n", + "\n", + "\n", + "torch.multiprocessing.set_sharing_strategy(\"file_system\")\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "id": "7d4ff515", + "metadata": {}, + "source": [ + "## Setup data directory" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8b4323e7", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory" + ] + }, + { + "cell_type": "markdown", + "id": "99175d50", + "metadata": {}, + "source": [ + "## Set deterministic training for reproducibility" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "34ea510f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "set_determinism(42)" + ] + }, + { + "cell_type": "markdown", + "id": "c3f70dd1-236a-47ff-a244-575729ad92ba", + "metadata": { + "tags": [] + }, + "source": [ + "## Preprocessing of the BRATS Dataset in 2D slices for training\n", + "We download the BRATS training dataset from the Decathlon dataset. \\\n", + "We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\\\n", + "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_.\n" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n", + "To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ": Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n" + ] + } + ], + "source": [ + "channel = 0 # 0 = Flair\n", + "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", + "\n", + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\", \"label\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n", + " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", + " transforms.AddChanneld(keys=[\"image\"]),\n", + " transforms.EnsureTyped(keys=[\"image\", \"label\"]),\n", + " transforms.Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\"),\n", + " transforms.Spacingd(keys=[\"image\", \"label\"], pixdim=(3.0, 3.0, 2.0), mode=(\"bilinear\", \"nearest\")),\n", + " transforms.CenterSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 64)),\n", + " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", + " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", + " transforms.Lambdad(\n", + " keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze()\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "def get_batched_2d_axial_slices(data: Dict):\n", + " images_3D = data[\"image\"]\n", + " batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", + " slice_label = data[\"slice_label\"]\n", + " slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze()\n", + " return batched_2d_slices, slice_label\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "da1927b0", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2023-03-13 16:09:17,074 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-03-13 16:09:17,075 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-03-13 16:09:17,076 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n", + "len train data 388\n" + ] + } + ], + "source": [ + "\n", + "train_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"training\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "print(\"len train data\", len(train_ds)) #this gives the number of patients in the training set\n", + "\n", + "\n", + "\n", + "train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", + "data_2d_slices = []\n", + "data_slice_label = []\n", + "for i, data in enumerate(train_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices.append(b2d)\n", + " data_slice_label.append(slice_label2d)\n", + " \n", + "total_train_slices = torch.cat(data_2d_slices, 0)\n", + "total_train_labels = torch.cat(data_slice_label, 0)" + ] + }, + { + "cell_type": "markdown", + "id": "fac55e9d", + "metadata": { + "tags": [] + }, + "source": [ + "## Preprocessing of the BRATS Dataset in 2D slices for validation\n", + "We download the BRATS validation dataset from the Decathlon dataset. \n", + "We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_.\n", + "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2023-03-13 16:19:38,821 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-03-13 16:19:38,824 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-03-13 16:19:38,826 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n" + ] + } + ], + "source": [ + "val_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"validation\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "\n", + "\n", + "val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", + "data_2d_slices_val = []\n", + "data_slice_label_val = []\n", + "for i, data in enumerate(val_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices_val.append(b2d)\n", + " data_slice_label_val.append(slice_label2d)\n", + "\n", + "total_val_slices = torch.cat(data_2d_slices_val, 0)\n", + "total_val_labels = torch.cat(data_slice_label_val, 0)\n" + ] + }, + { + "cell_type": "markdown", + "id": "08428bc6", + "metadata": {}, + "source": [ + "## Define network, scheduler, optimizer, and inferer\n", + "At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", + "the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms\n", + "in the 3rd level (`num_head_channels=64`).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "bee5913e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "device = torch.device(\"cuda\")\n", + "\n", + "model = DiffusionModelUNet(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=1,\n", + " num_channels=(64, 64, 64),\n", + " attention_levels=(False, False, True),\n", + " num_res_blocks=1,\n", + " num_head_channels=64,\n", + " with_conditioning=False,\n", + ")\n", + "model.to(device)\n", + "\n", + "scheduler = DDIMScheduler(num_train_timesteps=1000)\n", + "\n", + "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", + "\n", + "inferer = DiffusionInferer(scheduler)" + ] + }, + { + "cell_type": "markdown", + "id": "2a4d3ab2", + "metadata": { + "tags": [] + }, + "source": [ + "## Model training of the diffusion model\n", + "We train our diffusion model for 100 epochs, with a batch size of 32." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6c0ed909", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 534it [01:42, 5.21it/s, loss=0.163] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 1: : 534it [01:46, 4.99it/s, loss=0.0234] \n", + "4224it [01:27, 48.47it/s]\n", + "Epoch 2: : 534it [01:47, 4.96it/s, loss=0.0207] \n", + "4224it [01:26, 48.76it/s]\n", + "Epoch 3: : 534it [01:46, 4.99it/s, loss=0.0199] \n", + "4224it [01:24, 49.73it/s]\n", + "Epoch 4: : 534it [01:46, 5.00it/s, loss=0.0198] \n", + "4224it [01:26, 48.90it/s]\n", + "Epoch 5: : 534it [01:46, 5.00it/s, loss=0.0192] \n", + "4224it [01:26, 48.97it/s]\n", + "Epoch 6: : 534it [01:47, 4.98it/s, loss=0.0199] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 7: : 534it [01:47, 4.99it/s, loss=0.0188] \n", + "4224it [01:26, 48.65it/s]\n", + "Epoch 8: : 534it [01:47, 4.95it/s, loss=0.0184] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 9: : 534it [01:47, 4.98it/s, loss=0.0179] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 10: : 534it [01:47, 4.98it/s, loss=0.0183] \n", + "4224it [01:25, 49.12it/s]\n", + "Epoch 11: : 534it [01:47, 4.98it/s, loss=0.0183] \n", + "4224it [01:26, 48.83it/s]\n", + "Epoch 12: : 534it [01:48, 4.94it/s, loss=0.0182] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 13: : 534it [01:47, 4.95it/s, loss=0.0185] \n", + "4224it [01:27, 48.52it/s]\n", + "Epoch 14: : 534it [01:47, 4.95it/s, loss=0.0176] \n", + "4224it [01:27, 48.54it/s]\n", + "Epoch 15: : 534it [01:47, 4.95it/s, loss=0.018] \n", + "4224it [01:26, 48.60it/s]\n", + "Epoch 16: : 534it [01:47, 4.99it/s, loss=0.0181] \n", + "4224it [01:27, 48.10it/s]\n", + "Epoch 17: : 534it [01:47, 4.95it/s, loss=0.0179] \n", + "4224it [01:28, 47.99it/s]\n", + "Epoch 18: : 534it [01:47, 4.97it/s, loss=0.0177] \n", + "4224it [01:26, 48.73it/s]\n", + "Epoch 19: : 534it [01:47, 4.97it/s, loss=0.0179] \n", + "4224it [01:28, 47.86it/s]\n", + "Epoch 20: : 534it [01:47, 4.95it/s, loss=0.0177] \n", + "4224it [01:28, 47.48it/s]\n", + "Epoch 21: : 534it [01:47, 4.95it/s, loss=0.0175] \n", + "4224it [01:27, 48.23it/s]\n", + "Epoch 22: : 534it [01:47, 4.95it/s, loss=0.0171] \n", + "4224it [01:24, 49.97it/s]\n", + "Epoch 23: : 534it [01:47, 4.96it/s, loss=0.0169] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 24: : 534it [01:47, 4.98it/s, loss=0.0172] \n", + "4224it [01:27, 48.49it/s]\n", + "Epoch 25: : 534it [01:47, 4.99it/s, loss=0.0168] \n", + "4224it [01:25, 49.39it/s]\n", + "Epoch 26: : 534it [01:46, 5.00it/s, loss=0.0169] \n", + "4224it [01:26, 48.62it/s]\n", + "Epoch 27: : 534it [01:47, 4.98it/s, loss=0.0171] \n", + "4224it [01:27, 48.43it/s]\n", + "Epoch 28: : 534it [01:47, 4.97it/s, loss=0.0175] \n", + "4224it [01:25, 49.18it/s]\n", + "Epoch 29: : 534it [01:46, 5.01it/s, loss=0.0171] \n", + "4224it [01:25, 49.59it/s]\n", + "Epoch 30: : 534it [01:47, 4.95it/s, loss=0.017] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 31: : 534it [01:47, 4.99it/s, loss=0.0169] \n", + "4224it [01:25, 49.12it/s]\n", + "Epoch 32: : 534it [01:46, 4.99it/s, loss=0.0168] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 33: : 534it [01:46, 5.00it/s, loss=0.0166] \n", + "4224it [01:26, 48.82it/s]\n", + "Epoch 34: : 534it [01:47, 4.97it/s, loss=0.0173] \n", + "4224it [01:27, 48.49it/s]\n", + "Epoch 35: : 534it [01:46, 4.99it/s, loss=0.0169] \n", + "4224it [01:26, 48.66it/s]\n", + "Epoch 36: : 534it [01:47, 4.99it/s, loss=0.0171] \n", + "4224it [01:26, 48.92it/s]\n", + "Epoch 37: : 534it [01:47, 4.97it/s, loss=0.0166] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 38: : 534it [01:47, 4.99it/s, loss=0.0163] \n", + "4224it [01:27, 48.55it/s]\n", + "Epoch 39: : 534it [01:47, 4.97it/s, loss=0.0166] \n", + "4224it [01:26, 48.55it/s]\n", + "Epoch 40: : 534it [01:46, 5.00it/s, loss=0.0169] \n", + "4224it [01:25, 49.31it/s]\n", + "Epoch 41: : 534it [01:47, 4.99it/s, loss=0.0169] \n", + "4224it [01:26, 48.85it/s]\n", + "Epoch 42: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 43: : 534it [01:46, 4.99it/s, loss=0.0171] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 44: : 534it [01:47, 4.99it/s, loss=0.0167] \n", + "4224it [01:27, 48.53it/s]\n", + "Epoch 45: : 534it [01:46, 5.00it/s, loss=0.0167] \n", + "4224it [01:27, 48.40it/s]\n", + "Epoch 46: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 47: : 534it [01:47, 4.99it/s, loss=0.0162] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 48: : 534it [01:46, 5.00it/s, loss=0.017] \n", + "4224it [01:27, 48.50it/s]\n", + "Epoch 49: : 534it [01:47, 4.98it/s, loss=0.0164] \n", + "4224it [01:27, 48.21it/s]\n", + "Epoch 50: : 534it [01:47, 4.97it/s, loss=0.0168] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 51: : 534it [01:47, 4.98it/s, loss=0.0163] \n", + "4224it [01:27, 48.10it/s]\n", + "Epoch 52: : 534it [01:47, 4.97it/s, loss=0.0158] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 53: : 534it [01:47, 4.96it/s, loss=0.0163] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 54: : 534it [01:47, 4.96it/s, loss=0.0157] \n", + "4224it [01:27, 48.03it/s]\n", + "Epoch 55: : 534it [01:47, 4.99it/s, loss=0.0164] \n", + "4224it [01:27, 48.19it/s]\n", + "Epoch 56: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.46it/s]\n", + "Epoch 57: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:27, 48.47it/s]\n", + "Epoch 58: : 534it [01:47, 4.97it/s, loss=0.017] \n", + "4224it [01:27, 48.46it/s]\n", + "Epoch 59: : 534it [01:47, 4.98it/s, loss=0.0164] \n", + "4224it [01:27, 48.38it/s]\n", + "Epoch 60: : 534it [01:47, 4.94it/s, loss=0.0165] \n", + "4224it [01:27, 48.27it/s]\n", + "Epoch 61: : 534it [01:47, 4.96it/s, loss=0.0164] \n", + "4224it [01:27, 48.50it/s]\n", + "Epoch 62: : 534it [01:47, 4.97it/s, loss=0.0164] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 63: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:27, 48.06it/s]\n", + "Epoch 64: : 534it [01:47, 4.97it/s, loss=0.0163] \n", + "4224it [01:27, 48.35it/s]\n", + "Epoch 65: : 534it [01:47, 4.98it/s, loss=0.0159] \n", + "4224it [01:27, 48.53it/s]\n", + "Epoch 66: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 67: : 534it [01:47, 4.97it/s, loss=0.0164] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 68: : 534it [01:48, 4.94it/s, loss=0.016] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 69: : 534it [01:47, 4.98it/s, loss=0.0156] \n", + "4224it [01:27, 48.34it/s]\n", + "Epoch 70: : 534it [01:47, 4.98it/s, loss=0.0162] \n", + "4224it [01:26, 48.96it/s]\n", + "Epoch 71: : 534it [01:47, 4.97it/s, loss=0.0159] \n", + "4224it [01:25, 49.55it/s]\n", + "Epoch 72: : 534it [01:47, 4.97it/s, loss=0.0159] \n", + "4224it [01:27, 48.08it/s]\n", + "Epoch 73: : 534it [01:47, 4.97it/s, loss=0.0165] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 74: : 534it [01:47, 4.98it/s, loss=0.0161] \n", + "4224it [01:27, 48.33it/s]\n", + "Epoch 75: : 534it [01:46, 5.00it/s, loss=0.0164] \n", + "4224it [01:27, 48.20it/s]\n", + "Epoch 76: : 534it [01:47, 4.95it/s, loss=0.0165] \n", + "4224it [01:26, 48.73it/s]\n", + "Epoch 77: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:27, 48.45it/s]\n", + "Epoch 78: : 534it [01:47, 4.95it/s, loss=0.0158] \n", + "4224it [01:27, 48.42it/s]\n", + "Epoch 79: : 534it [01:47, 4.96it/s, loss=0.0163] \n", + "4224it [01:26, 48.85it/s]\n", + "Epoch 80: : 534it [01:47, 4.96it/s, loss=0.0156] \n", + "4224it [01:27, 48.52it/s]\n", + "Epoch 81: : 534it [01:47, 4.97it/s, loss=0.0158] \n", + "4224it [01:27, 48.44it/s]\n", + "Epoch 82: : 534it [01:47, 4.97it/s, loss=0.0163] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 83: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:27, 48.24it/s]\n", + "Epoch 84: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 85: : 534it [01:47, 4.99it/s, loss=0.0153] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 86: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.54it/s]\n", + "Epoch 87: : 534it [01:47, 4.96it/s, loss=0.0159] \n", + "4224it [01:27, 48.22it/s]\n", + "Epoch 88: : 534it [01:47, 4.96it/s, loss=0.0159] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 89: : 534it [01:47, 4.95it/s, loss=0.0164] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 90: : 534it [01:47, 4.96it/s, loss=0.0161] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 91: : 534it [01:47, 4.95it/s, loss=0.0158] \n", + "4224it [01:26, 48.94it/s]\n", + "Epoch 92: : 534it [01:47, 4.96it/s, loss=0.0158] \n", + "4224it [01:26, 48.65it/s]\n", + "Epoch 93: : 534it [01:47, 4.96it/s, loss=0.0166] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 94: : 534it [01:47, 4.98it/s, loss=0.0161] \n", + "4224it [01:26, 48.78it/s]\n", + "Epoch 95: : 534it [01:48, 4.94it/s, loss=0.0155] \n", + "4224it [01:26, 48.95it/s]\n", + "Epoch 96: : 534it [01:47, 4.98it/s, loss=0.0162] \n", + "4224it [01:26, 48.96it/s]\n", + "Epoch 97: : 534it [01:47, 4.97it/s, loss=0.016] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 98: : 534it [01:47, 4.98it/s, loss=0.016] \n", + "4224it [01:27, 48.48it/s]\n", + "Epoch 99: : 534it [01:47, 4.98it/s, loss=0.0157] \n", + "4224it [01:27, 48.33it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train diffusion completed, total time: 19490.821256637573.\n" + ] + }, + { + "data": { + "image/png": "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7NKmTZsMwACMSZMmFbl+WlqaMXr0aMs2cns5Ozsbn3zySa71T506lW/drNdjjz1mpKWl5bqNL774wlJu586dRvPmzXPUzzq27GXDw8ONzp0757nPVq1aGfHx8bnuc+jQoQZgBAUF5bo++35nz55tODk55boPDw8PY8uWLXme33Pnzhl16tTJs419+vQxfvrpJ8v/b9q0Kc9t5eWHH36wOs/FVdC5yZL9Z3H69Okc64OCggzAGDp0qPHRRx/leg4NwzDeeOMNAzBMJlOu28nuP//5j6Xu7t27c6w/ceKE0bBhw3zfi3Xr1jWOHTuW6/Y/+OADw8HBocD3c1xcXL7tzM3p06ct9YcOHVroeqNGjbLUW7hwYY712c9zbvvK6xUUFGT1M8zrlX27ue0rN/m9h+Li4oz77ruvwP0+/PDDOepOmjTJ6r2Tm9DQUMPV1TXP7To6Ohr//ve/86xfUr/7Bcn+t/uLL74w9uzZY/W37UYHDx60rN+zZ4/Vzy6vvxtnzpwxGjRokO95bteunXH16tU827l582bDx8cnz/pTpkwp1M9lx44dRvXq1fNtS6tWrYyLFy/mWr9Dhw4GYHTo0CHf81raNOfpNjVy5EjLt5levXoxZMgQ6tWrh8lkYu/evcycOZODBw8yatQoqlatSt++fa3qZ2Rk4OLiQo8ePejWrRsNGzbEz8+P6Ohojh07xpw5czh48CALFiygVq1aTJkypcD2HDhwgCeeeIJBgwZRtWpVzp07h6ura46yo0aNYtu2bQwdOpRHHnnEUvbdd9/l999/Z8eOHUybNo233377ps/P2rVr2b59O02bNuWFF16gSZMmJCUlsWLFCmbNmkViYiKPP/44x48fx8XFxapuamoqvXv35sSJE5bzO2rUKAIDAzl//jyffPIJK1eu5PLlyzfdPsDSswDQp0+fYm3rVti5cycLFiwgMDCQl19+mbvuuouMjAx++eUXAIYMGcK0adMwDINFixbx2muv5bmthQsXAhAcHMydd95pte7PP//k3nvv5dKlS3h7ezNq1Ci6du1KlSpViImJ4aeffmLWrFkcP36cnj17smfPHnx9fS319+/fz8svv4zZbOaOO+7g2WefpXnz5vj5+REfH8/x48fZtGkTK1asuAVnKW9du3blk08+AeCXX34pcEIxQPXq1S09LMOHD2fXrl3cfffdfPHFF5YyWd/67777bgCaNGkCwOjRo3nmmWcs5cqXL19ixwKZvUdZP/s+ffowZMgQatSogZubG5cvX2bfvn2sXLnypu4Z9OOPPzJs2DAMw8DLy4uXXnqJrl274uTkxNatW3n77be5cuUKr732GuXKlWP06NF5bqs4v/s3o0WLFjRu3Jjw8HC++uorpk+fbrU+q0eqUaNGtGjRgn379uW7vfj4eDp37sypU6eAzAsBRowYgb+/P6dPn+bDDz9ky5Yt/Prrr/Tp04dffvklR2/WmTNn6Nu3L3FxcTg4ODBq1CgGDBiAr68v+/fvZ/r06UyaNMnyHsrLgQMH6NSpEwkJCVSuXJnRo0dz3333UaFCBaKiovj+++/5+OOP2bFjBw8++CC//PKL/Qwf2zq9yc0pTs/T0qVLLXU//fTTXMskJSVZendq1qyZo/coPj7eiIyMzHMfZrPZGDZsmAEYnp6exvXr13OUufHb7+eff57n9m4s+9VXX+Uok5ycbDRu3NgAjAoVKuTa41XYnifA6N27t5GSkpKjzLRp0yxlli9fnmP9Bx98YFn/7LPP5rqfZ5991mpfN9Pz1K1bN0v9vHpUiqKke54Ao0mTJsa1a9fy3Nadd95pAEajRo3yLHPs2DHL9qZOnZpjfZ8+fQzACAwMNE6ePJnrNvbs2WN4enoagPHGG29YrZswYYLlfZrXt1/DMIzr168bGRkZea7Py832PJ04ccJSr3PnzjnWF9QbVNhv7IX5O1ISPU+BgYEGYAwYMCDfbeTWG5JfD0dqaqqlZ8PLy8v4448/cpQ5c+aMUa1aNUvP0eXLl3OUKYnf/cK4sefJMAzjnXfeMQAjICDA6j1mNpst52369OmGYRgF9jy9/PLLlvU3vteztjlkyBBLmblz5+Yo079/f8v6RYsW5VgfGxtrNGvWzOqc5bafpk2bGoDRrFmzXM+5YRjG6tWrLb2+n332WY71ZbXnSXOebkNZPTL9+vXjySefzLWMm5sbH374IZD5LeTGOTmenp5Uq1Ytz32YTCZmzJiBo6MjCQkJBY6Rd+7cmREjRhSq/f379+exxx7LsdzV1ZVnn30WyLx8P7e5E4WVNYcot2+Wzz//vGV51jfp7D7++GMA/P39LXOSbvTee+/h7+9/0+0DrObBVKlSpVjbulXmzJmT75ViQ4YMAeDgwYN5fqPO6nUCcvS+hIeHs3LlSgA+/PBDatWqles2WrRowZgxYwCYN2+e1bqLFy8CmVeb5XcefX19cXAovT+ZFSpUsPz72rVrpbbfWyXrPN933335lvPz8yvSdlesWGG5YvD111+nefPmOcoEBQVZfhcTExOteuJuVJzf/Zs1ZMgQHBwcOH/+vFWP8ubNm4mIiMDBwcHyu5KflJQUPvvsMwAaNmyY68UGJpOJuXPnWt5fWX/ns/z555989913QGYPYdacrOy8vb0tvaJ5+fHHH9m/fz+QOWcr+73FsuvZsycDBgwAyPfnUtYoPN1mLly4wO7duwF45JFH8i3boEEDyxv+999/z7dsWloa58+f5/Dhw4SHhxMeHk5kZKTlF7SgrubC/GEoTNm77rrL8u+sbuub0a1btzyvWvP29rbce+fGfVy4cIGjR48CmefXzc0t1224ubkxcODAm24fQFxcnOXfxbmZ4q0SGBhY4AdlSEiIJZAsWrQo1zJZVyG1bds2RzjK+iPv4eFhuUItL+3btwcybxgaERFhWZ71JeDQoUPs2LEj322UpqwbVYL1z9peZZ3nr7/+ulB31C6srC9mJpMp3y9gAwcOtAzX5vdl7mZ/94ujevXqdOrUCbCeOJ71744dOxIQEFDgdnbv3s3169eBzMn7eU0u9/Hxsfz9P3ToEH/++adl3aZNmywTuIcPH57nvlq1akWjRo3yXJ/1u1m/fv0Cr+zN+t3cuXOn3UweV3i6zezatcvy75CQkFwfG5H9ldW7kfWtMbu0tDTmzJlDmzZt8PLyIjAwkIYNG9KkSRPLKyoqCiDXq4WyK8pl88HBwXmuy/6ttTgfOPntI/t+btxHeHi45d/Zg1xuCpovUBBvb2/LvxMSEoq1rVuhMD/TatWqWa76CwsLy3E1286dOzl27BiQe2jOej8nJiZarsbL65V9Xlj293NISAjOzs6kpKRw77330rdvX/773/9y8ODBUrm6Li/Z31s+Pj42a0dJGTp0KABbt261zC1bsWJFsef+Zf3O1axZM9/bdLi4uNCiRQurOrm52d/94nriiScAWLp0KUlJSSQlJbFs2TIAHn/88UJtI/txtW7dOt+y2ddnr5f9qsSWLVvmu41WrVrluS7rd/Po0aMFfs5kjRikpqYSHR2d7z7LCoWn20xWmCmqG78pRkdH07ZtW5599lm2b99uuVdMXpKSkvJdX5TJqVmXcOcm+7BKcb7B5LeP7Pu5cR/Zh1fy+0MOUKlSpZtsXabs3eCXLl0q1rZuhcL+TLNCUUREBD///LPVuqwhOycnp1x7Skvi/RwcHExYWBjly5cnPT2dlStXMnr0aBo3bkzlypV5/PHHS3SIprCyf+Eo6lBWWTRhwgRGjBiByWQiKiqKOXPm0L9/f6pUqUKTJk2YNGnSTb2Psz5sCzN0nXUTyvw+oG/2d7+4+vfvj4eHB3FxcXz33Xd8++23xMbG4u7uzsMPP1yobWQ/roLOR/YbcmavV5S/Yfnto6Q+a8oqXW13m8n+C79w4cJC9/jc+EH4wgsvWIb/sq7maNq0KZUrV7Y8lR0yb/oXERFR4Df4wt67RP7SrFkz1q1bB8CePXvyfIyHrRT2Z9q/f3+eeeYZkpKSWLRoER06dAAy36tZd1Lu3r17rmEz6/18xx138P333xe6bXfccYfV/z/88MN07dqVr7/+mrVr1/LLL79w+fJlrly5woIFC1iwYAFDhw5l3rx5pTbv6Y8//rD8u379+qWyz1vJ2dmZzz//nJdeeomwsDA2btzIrl27SE1NtQz1f/DBByxYsIAHH3ywyNsvzFV6tuxJLIiXlxf9+vVj4cKFfPXVV5a2PvTQQ1a9zIVV0PnI61xkX36z24C/fjfvvfde/vvf/+a7neyKOxe0tCg83WayT0I1mUy5PiqiILGxsZYPtUcffdRqQu+N/g4TXYsie8gs6JtXcYcrOnTowPvvvw9kTs4cNGhQsbaXFQoKunleSQ8R+vj40LdvX5YsWcI333zD7NmzcXFxYePGjZbhtbzmuWW9ny9dukRwcHChbxKaG19fX0aNGsWoUaOAzLkg33//PbNnzyYyMpLQ0FBatGjBCy+8cNP7KIqsYAwU6kait1JJvjcaNmzI1KlTmTp1KklJSfz2228sWrSIL7/8kvj4eEJCQjh58mS+F6Rkl9Url9vUghtl9WyV1Z68J554goULF1oexQKFH7ID6+O6ePFivo/cyd7Ll71e9n9funQp34eH5/c3rkKFCly6dInLly/f1OdMWadhu9tM1pg/YPULWhTHjx8nLS0NgMGDB+dZ7ujRo8THx9/UPuxV9gmU2eeX5aag9QXp3r275VvaN998Y7ni6GZlfbvNmnCal6wJ8SUpKxxdu3bNcmf6rAnknp6eefZEZL2fExMT+e2330q0TQ0bNuTVV19l27Ztlgn5S5YsKdF95OXy5ctWx9+9e/dS2W9est4bBX0ZKup7w93dna5duzJv3jzL1XBJSUmWKygLI+uD+cyZM/l+mKelpVl688rqh3mXLl2oVq0a6enplsexFOVnn/24tm/fnm/Z7BdHZK+Xdd8vyJxzmJ/81mf9bh47doyzZ8/mux17pPB0m6lTpw4NGzYEYPHixZw7d67I20hPT7f8O7/x6aJ01f5dBAQEWL7tffPNN3k+EiQ5OZlvvvmmWPtycXHh5Zdftmxv5MiRhZ6Hcf78eTZu3Gi1LGsoKy4uLs8PwdTUVMsk1pLUq1cvyzfehQsXkpyczPLly4HMYYu8ribMHqrefffdEm8XZF41mPUzLejCh5JgNpsZNmyY5Xdr1KhRNu8pyXpv7NmzJ8+hmvDw8AIfgZKfLl26WP5dlPPctWtXIHMI6cbbUGS3dOlSYmJirOqUNY6Ojjz++OO4urri6urKY489VqQpDXfddZfl1iChoaF5/j2Ii4uzfBFo2LChVS9fp06dLPsMDQ3Nc1+7du3Kd+L9Aw88YPn3rfrdtCWFp9vQG2+8AWR+4Pbv3z/f4aOUlBTmzp1rFQLq1KljGQvPukv5jVauXMns2bNLsNX24+mnnwYyL4kfN25crmXGjRtHZGRksff1wgsvWC5xXrt2Lf369cv352kYBgsXLuSuu+6y3IMlS9ZcI4AZM2bkWveFF14okXbfyNnZ2XLrhh9++IFFixYRGxsL5H9ripYtW1q+ma9atYpJkyblu58zZ87keADrt99+m29vW0REBEeOHAFyzpUqaefOnaNnz56sWrUKyJzMXtAxlYas90ZkZGSuD7CNi4vL9zYB0dHRBT4bMHtPeFHOc79+/Sw9sP/+979zvS1KRESE5YuGh4dHvpfg29o777xDcnIyycnJlmH5wnJ1dbXcu+/gwYO5PtnBMAyeffZZS0DNutItS7Vq1SxfSr7//vtce1vj4+Mtw9t5efjhh2nQoAEAH330EZ9//nm+5cPDw/nhhx/yLVOWaM7T38DevXuZP39+geXatWtHnTp1CAkJYe3atYSGhrJ7924aNmzI008/TYcOHahUqRIJCQmcPHmSX375heXLlxMdHW25jBYyx7J79+7Njz/+yKpVq+jZsydPP/00NWrUICoqimXLljF//nxq1arF9evXiz23x948++yzfPHFF4SHh/Phhx9y6tQpnn76aQICAiyPZ/nxxx9p1aqVpev8Zh5JAZlzUZYsWUKfPn3Yvn07P/zwA7Vr12bIkCF07tyZgIAAnJ2duXjxItu2bWPZsmWWIHCjFi1a0KZNG7Zt28ann35KamoqQ4cOxdfXl+PHj/Pf//6XzZs307Zt2wLv+3UzHnvsMT7++GOSkpIsD/+tVKlSgU+R/+KLL7j77rv5888/efPNN1m7di0jRoygSZMmuLm5cfXqVfbv38+aNWvYuHEjDz30kNWN/2bOnMmQIUO4//776dy5Mw0aNMDX15dr166xa9cuZs+ebblaNL/HehTG9evXrb6tJyUlcf36dQ4dOsTmzZtZuXKlpWe3fv36rFy50upRMrby2GOPMXnyZGJjYxk5ciQnTpygR48emEwmdu3axQcffMCFCxdo0aKF1UT3LLGxsTz44IPUrFmT/v3707p1a4KCgnBycuLPP//khx9+sNzcMSAgIMfjoPLj7OzMJ598YnmcSLt27Rg3bhxdunSxPJ5l+vTpliG9999/P88bNv4dTJw4keXLl3Pq1CmmTp1KeHh4jsezZN30uG3btrmGoBkzZrBu3Tri4uJ49NFH2bJlCwMGDMDHx8fyeJZjx45x99135zn9wNHRka+//pp77rmH+Ph4nnzySb755hseffRR6tevj7OzM1FRUfzxxx+sXLmSrVu38tJLLxXpZ29TtrituRRf9lv8F/aV9SgAwzCM9PR045VXXjEcHR0LrOfp6WkkJiZa7f/cuXNGjRo18qxTo0YN4+DBg/k+1qGgx3zcTNnsj8LIfrxZivJg4PwU9MiAs2fPGrVr187z/HTv3t1YvXq15f+3bduW7/4KkpSUZLzwwguGi4tLgT9Pk8lkPPbYY8aFCxdybOfw4cNG5cqV86z74osvFunBwEVhNputHu1CPo+3udGZM2eMli1bFur3YPjw4VZ1s36W+b0KeqhsfgrzsN7sLx8fH+PFF180EhIS8t1uaT6exTAMY8mSJXn+vXBzczOWLFmS5+9XYc9B9erVjT179uTYd2EeQDt//vwSezBwfor7uJDcHs9SFIV5MPDp06eN4ODgfM/1vffem++DgTdt2mR4e3vnWX/SpEmF+rns27fPqFu3bqF+/lOmTMlRX49nkTLF0dGRd955h0OHDvHSSy/RokULypcvj6OjI97e3jRq1IghQ4YQGhrKn3/+ibu7u1X9wMBA9uzZw7hx46hXrx6urq74+vrSrFkzJk2axN69ey1zq25HNWrUYN++fUyZMoXGjRvj7u5OuXLlaNOmDXPnzmX16tVWQ6HF7V1wc3Nj5syZHD9+nOnTp9O1a1dq1KiBu7s7bm5u+Pv70717d9566y1Onz7NV199leslwcHBwezZs4fRo0cTFBSEi4sLlSpVomfPnvz444+5DueVFJPJlOPxK4V5GC5kPn5j+/btrFixgsGDB3PHHXfg4eGBs7MzlSpV4p577uGll15iy5YtOYYPlixZwsKFCxk2bBjNmzenatWqODk54eXlRePGjXnmmWf4448/GD9+fIkdK2Qer4+PDwEBAbRu3ZrRo0fz1VdfERkZyYwZMwq831BpGzhwIFu3bqVfv35UqlQJFxcXAgMDGTp0KLt27cr3jvlBQUHs3buX9957j169elG/fn3KlSuHk5MTFStWtFw5evjwYauLWopi6NChHDlyhBdeeIEGDRrg6emJu7s7tWvX5qmnnrolP8OyqmbNmuzbt48PP/yQDh06UKFCBZydnalSpQo9e/bkq6++4ueff853Ll3Hjh05ePCg1d+CKlWqcP/997NmzZpcH/2Sm6ZNm3Lo0CFCQ0N56KGHCAwMxM3NDRcXF6pVq0bHjh1544032L17NxMnTiyhM3DrmQyjDN/4QuRvbNq0aUyYMAEnJyfi4uLyfJSLiIiULep5ErEBwzAs98pq3ry5gpOIiB1ReBK5Bc6cOWN1S4cbTZw40TJxOOuZXyIiYh80bCdyC0yePJkvvviCRx99lHvvvRd/f3/S0tI4fPgwoaGhlqtdGjZsyJ49e3B1dbVtg0VEpNB0qwKRW+TcuXNMnz49z/XBwcH8+OOPCk4iInZG4UnkFhg5ciS+vr6sXbuWEydOcPnyZZKSkvDz86NZs2b069ePESNG4OLiYuumiohIEWnYTkRERKQI1PNUwsxmM5GRkXh7e9/0XaNFRESkdBmGQVxcHP7+/jg45H89ncJTCYuMjCQwMNDWzRAREZGbEBERQUBAQL5lFJ5KmLe3N5B58n18fGzcGhERESmM2NhYAgMDLZ/j+VF4KmFZQ3U+Pj4KTyIiInamMFNudJNMERERkSJQeBIREREpAoUnERERkSJQeBIREREpAoUnERERkSJQeBIREREpAt2qQEREbom0tDQyMjJs3Qy5jTk6OuLs7Fzi21V4EhGREhUbG8uVK1dISUmxdVNEcHV1pWLFiiV670WFJxERKTGxsbFcuHABLy8vKlasiLOzs57zKTZhGAZpaWnExMRw4cIFgBILUApPIiJSYq5cuYKXlxcBAQEKTWJz7u7ueHt7c/78ea5cuVJi4UkTxkVEpESkpaWRkpKCr6+vgpOUGSaTCV9fX1JSUkhLSyuRbSo8iYhIiciaHH4rJuiKFEfWe7KkLmDQsJ2d2HkENv0B6RnQ/z4IDrJ1i0REcqdeJylrSvo9qfBkJ37eB//6OPPf9QIUnkRERGxFw3Z2wjHbTypdt00RERGxGYUnO+Hk+Ne/FZ5ERCQ7k8lEx44dbd2M24ZdhKf4+HjGjh2Lv78/bm5uNG/enMWLFxdY7/z584wdO5YOHTpQrlw5TCYT8+fPz7N8QkICEydOpF69eri6ulKhQgU6derE8ePHS/Bobk728JRhtl07REQkdyaTqUgvsV92Meepf//+7Ny5k+nTp1OvXj0WLVpESEgIZrOZRx99NM96J06cYOHChTRv3pzevXsTFhaWZ9n4+Hg6depEZGQkr776Kk2bNiUmJoatW7eSmJh4Kw6rSBzV8yQiUqZNmjQpx7IpU6bg6+vL2LFjb+m+Dx8+jIeHxy3dh/ylzIenVatWsW7dOktgAujUqRNnz55l3LhxDBo0CMfsySKb9u3bc/nyZQB27dqVb3h64403OHz4MPv376dWrVqW5Q888EAJHs3N07CdiEjZNnny5BzLpkyZQrly5XJdV5KCg4Nv6fbFWpkftluxYgVeXl4MHDjQavnw4cOJjIxk+/btedZ1cCjc4SUmJvLZZ58xcOBAq+BUlmjYTkTk7+HMmTOYTCaGDRvGkSNH6N+/PxUrVsRkMnHmzBkg87MvJCSEOnXq4OHhga+vL/fddx/Lli3LdZu5zXkaNmyYZZtz586lQYMGuLm5ERQUxJQpUzCb9WFys8p8eAoPD6dBgwY4OVl3kjVt2tSyvrh2795NQkICdevWZfTo0ZQvXx4XFxfuvvtufvzxx2JvvySo50lE5O/lxIkTtGnThkuXLjF06FCGDRuGi4sLAOPHj+fgwYO0a9eOF154gYEDB3L06FEGDBjA7Nmzi7SfcePGMWnSJNq0acPTTz8NZPaSTZgwocSP6XZR5oftrl69mmtvkJ+fn2V9cWU9MPCdd96hSZMmfPnllzg4ODBjxgz69u3L6tWr6dGjR651U1JSrJ4cHhsbW+z25Ea3KhAR+Xv57bffmDBhAm+++WaOdatWrcrx2RcfH88999zDhAkTGDlyZKHnOO3evZv9+/dTrVo1ACZMmEDdunWZPXs2kyZNsgQ2KbwyH54g/zuDlsQVC1ldly4uLqxevRpvb28gc25V3bp1mTp1ap7h6e2332bKlCnFbkNBNGwnIvbu7lFwMdrWrchfVT/Y9Ukp7atqVd54441c1+XWaeDl5cWwYcN46aWX2LlzJx06dCjUfiZMmGAJTgAVK1bkwQcfJDQ0lKNHj9KkSZObO4DbWJkPTxUqVMi1dyk6OvM3MKsHqrj7ALjnnnsswQnAw8ODDh068O233+ZZd/z48bz44ouW/4+NjSUwMLDYbbqRhu1ExN5djIYLV2zdirKjWbNmefb6REVFMX36dFavXs3Zs2dJSkqyWh8ZGVno/dx55505lgUEBABw/fr1wjdYLMp8eGrSpAlhYWGkp6dbzXs6cOAAAI0bNy72PrLmT+XGMIx8J567urri6upa7DYURMN2ImLvqhb/u+4tV5ptrFKlSq7Lo6OjadmyJefOnePee++la9eulCtXDkdHR/bu3ct3331nNV2kIL6+vjmWZX2eltSDcm83ZT489evXj08//ZRly5YxaNAgy/LQ0FD8/f1p3bp1sfdRrVo12rZty2+//UZsbCw+Pj5A5lV4W7ZsoU2bNsXeR3FZDdvpvS4idqi0hsPsRV7TTj7//HPOnTvHtGnTeP31163WTZ8+ne+++640mif5KPPhqVevXnTr1o3Ro0cTGxtLnTp1CAsLY82aNSxYsMByj6eRI0cSGhrKyZMnCQr666m5S5cuBeDUqVNA5v2evLy8ABgwYICl3Pvvv0+nTp3o0aMH//rXvzCZTMyYMYMrV64wderU0jrcPGnYTkTk9nDy5Ekg9/sM/vLLL6XdHMlFmQ9PAMuXL+f1119n4sSJREdHExwcTFhYGIMHD7aUycjIICMjA8MwrOreeH+oOXPmMGfOHACrsvfccw8bNmzgjTfeYMiQIQC0adOGzZs307Zt21t1aIWmYTsRkdtDVgfAr7/+ajWZe9GiRaxatcpWzZJs7CI8eXl5MWvWLGbNmpVnmfnz5+f63Lobw1R+2rVrx+bNm2+ihbeerrYTEbk9PP7447zzzjs899xzbNq0iaCgIPbv38/69evp378/y5cvt3UTb3tl/iaZkknDdiIit4eAgAC2bNlCly5dWL9+PR9//DEpKSn89NNP9O3b19bNE8BkFKVrRgoUGxuLr68vMTExlonnJWHnEWj1j8x/P9sPZr9QYpsWESkRycnJnD59mjvuuAM3NzdbN0fEojDvzaJ8fqvnyU5o2E5ERKRsUHiyExq2ExERKRsUnuyErrYTEREpGxSe7ISG7URERMoGhSc7oWE7ERGRskHhyU5o2E5ERKRsUHiyE3q2nYiISNmg8GQnNGwnIiJSNig82QkN24mIiJQNCk92QlfbiYiIlA0KT3ZCw3YiIiJlg8KTnXBUeBIRESkTFJ7shHqeREREygaFJzuRfcK45jyJiNx+Jk+ejMlkYvPmzVbLTSYTHTt2LPZ2StKwYcMwmUycOXPmlu3DlhSe7ISDA5hMmf9Wz5OISNkTEhKCyWRi8eLF+Za7evUqrq6uVKxYkdTU1FJqXcmaP38+JpOJ+fPn27opNqHwZEeyhu4UnkREyp6RI0cC8MUXX+RbbsGCBaSmpvL444/j4uJS7P0ePnyYL7/8stjbKUlvv/02hw8fpnr16rZuyi3hZOsGSOE5OUJauobtRETKoi5dulCzZk3Wr19PREQEgYGBuZbLCldZYau4goODS2Q7JalatWpUq1bN1s24ZdTzZEey5j2p50lEpOwxmUwMHz4cs9lMaGhormV2797Nvn37aNWqFX5+fkyaNIk2bdpQuXJlXF1dqVmzJs888wxRUVFF2m9uc54iIiIICQnBz88PLy8vOnTowM8//5zrNlJTU5k9ezY9evQgMDAQV1dXKleuTP/+/fnjjz+syg4bNozhw4cDMHz4cEwmk+WVvUxec55CQ0Np06YNXl5eeHl50aZNm1zP1+bNmzGZTEyePJk9e/bQo0cPvL298fX1pV+/fjadT6XwZEc0bCciUrYNHz4cBwcH5s+fj2EYOdZn73X6+eefmTFjBlWqVCEkJITnnnuO2rVr89FHH9G2bVtiYmJuuh1//vknbdu2ZfHixbRq1Yrnn38ePz8/unXrxrZt23KUj46OZuzYsaSkpNC7d2/++c9/0rFjR1atWsU999zDzp07LWUfeughHnzwQQAefPBBJk2aZHkV5J///CfDhg3j/PnzjBw5kieffJILFy4wbNgwXnzxxVzr7Nq1i/vuuw8nJyeefvpp7r77br799lu6du1KcnLyTZ6hYjKkRMXExBiAERMTU+LbrviAYdDBMGqHlPimRUSKLSkpyTh06JCRlJRk66bYVI8ePQzA2Lx5s9Xy5ORko3z58oaHh4cRExNjXLp0yYiLi8tRPzQ01ACMadOmWS2fNGmSARibNm2yWg4YHTp0sFo2dOjQXLfx8ccfG0CO7SQnJxvnz5/P0Zbw8HDDy8vL6Nq1q9XyL774wgCML774ItdzkLX/06dPW5b9/PPPBmA0aNDAuH79umX59evXjeDgYAMwfvnlF8vyTZs2Wdq6ePFiq+0//vjjBmCEhYXluv8bFea9WZTPb815siMathMRe9Y6ZgQXzdG2bka+qjr4sd13XrG2MWLECNauXcu8efPo0KGDZfmKFSu4du0aQ4cOxcfHBx8fn1zrP/744zz33HOsX7+e119/vcj7T01N5euvv6Zy5cq89NJLVuuefPJJZsyYwbFjx6yWu7q65jq5u1GjRnTq1Im1a9eSlpaGs7NzkduTJevKvMmTJ+Pr62tZ7uvry6RJkwgJCWH+/Pm0a9fOql779u0ZNGiQ1bIRI0bw1VdfsXPnTgYPHnzTbbpZCk92RMN2ImLPLpqjuWBctnUz8lcCF+Q89NBDVKhQgaVLl/Lhhx/i7e0NwLx5maFsxIgRlrLLly/n448/Zs+ePVy7do2MjL/+wEdGRt7U/o8ePUpycjKdO3fGzc3Nap2DgwP33HNPjvAEsHfvXt59911+/fVXLl68SFpamtX6K1euFGsSeNbcqdzmZ2Ut27t3b451d955Z45lAQEBAFy/fv2m21McCk92JCs86Wo7EbFHVR38SiSc3EpVHfyKvQ0XFxcee+wxZs2axZIlSxg5ciQRERFs2LCBunXr0r59ewBmzJjByy+/TKVKlejevTsBAQG4u7sDMHPmTFJSUm5q/1lzpSpXrpzr+ipVquRYtnXrVjp37gxA9+7dqVu3Ll5eXphMJr799lv27dt30+3JEhsbi4ODA5UqVcq1TQ4ODrnO88reS5XFySkzvmQPm6VJ4cmOOKrnSUTsWHGHw+zJyJEjmTVrFvPmzWPkyJHMnz8fs9ls6XVKT09n6tSp+Pv7s3fvXqtAYRgG77777k3vOyts5HXF3qVLl3Ise+utt0hJSeHXX3/l3nvvtVq3bds29u3bd9PtyeLj44PZbOby5cs5gl1UVBRmsznPocyyRlfb2REN24mI2IcmTZrQsmVLtm7dypEjR5g/fz6Ojo4MHToUyBwCi4mJoU2bNjl6Ynbt2kVSUtJN77t+/fq4ubmxa9euHFejmc1mtm7dmqPOyZMn8fPzyxGcEhMT2bNnT47yjv/7Nl+Unp8WLVoA5PpYmC1btgDQvHnzQm/PlhSe7IiG7URE7EfWTTCffPJJTp06Re/evS1zhipXroy7uzt79uwhMTHRUufatWs899xzxdqvi4sLjzzyCFFRUcyYMcNq3WeffZbrfKegoCCuXbvGwYMHLcsyMjJ4+eWXuXw55zw1P7/M4c3z588Xul1ZwXHKlCnExsZalsfGxjJlyhSrMmWdhu3siK62ExGxHyEhIbz44ov89ttvgPUdxR0cHHjmmWeYMWMGzZo1o2/fvsTGxrJ69WqCgoLw9/cv1r6nT5/Ohg0beOONN/j1119p0aIFhw8fZtWqVXTv3p2ffvrJqvxzzz3HTz/9RLt27XjkkUdwc3Nj8+bNXLhwgY4dO+boLWrbti3u7u7MnDmT2NhYS+/Zq6++mmeb2rdvz3PPPcfs2bNp3LgxDz/8MIZhsHz5ciIiInj++ect88HKOvU82REN24mI2A8fHx8GDBgAZE6Ivv/++63Wv/3227z11luYTCbmzp3LunXrGDx4MD/99FOxbgkAmY9H2bp1K4MGDWLbtm3MmjWLq1evsm7dOtq2bZujfJ8+fVi6dCm1atViwYIFLFq0iODgYHbs2EFQUFCO8n5+fixdupS6devy0UcfMX78eMaPH19gu/7v//6PefPmUbVqVT755BM+/fRTqlatyrx585g1a1axjrk0mQwjl1ugyk2LjY3F19eXmJiYEp/41uofsPMIODhAxsYS3bSISLElJydz+vRp7rjjjhyXyIvYUmHem0X5/FbPkx3JGrYzmzNfIiIiUvrsIjzFx8czduxY/P39cXNzo3nz5ixevLjAeufPn2fs2LF06NCBcuXKYTKZLHc4zU9SUhL16tXDZDLx/vvvl8ARlIysYTvQpHERERFbsYvw1L9/f0JDQ5k0aRKrV6+mZcuWhISEsGjRonzrnThxgoULF+Li4kLv3r0Lvb8JEyaQkJBQ3GaXOKvwpHlPIiIiNlHmr7ZbtWoV69atY9GiRYSEhADQqVMnzp49y7hx4xg0aJDlfhM3at++veUSy127dhEWFlbg/nbs2MHs2bNZuHAhAwcOLLkDKQGO2aKuJo2LiIjYRpnveVqxYgVeXl45gszw4cOJjIxk+/btedZ1cCja4aWmpjJixAjGjBnD3XfffVPtvZWy9zwpPImIiNhGmQ9P4eHhNGjQwPIcmyxNmza1rC8pb775JgkJCUydOrXEtlmSNOdJRETE9sr8sN3Vq1epVatWjuVZdze9evVqiewn62nSP/zwA56enrneUTU3KSkpVg9LzH7X1JLmqJ4nEbEDugOOlDUl/Z4s8z1PACaT6abWFVZ6ejojRoxg0KBB9OjRo0h13377bXx9fS2vwMDAYrcnLxq2E5GyLGv+aVpamo1bImIt6z2Z1xzpoirz4alChQq59i5FR0cDf/VAFcfMmTM5deoUkyZN4vr161y/ft3Sg5ScnMz169fzfPjh+PHjiYmJsbwiIiKK3Z68aNhORMoyZ2dnXF1diYmJUe+TlBmGYRATE4Orq2ux79yepcwP2zVp0oSwsDDS09Ot5j0dOHAAgMaNGxd7H+Hh4cTExFC3bt0c6yZMmMCECRP4448/cn3as6urK66ursVuQ2HoajsRKesqVqzIhQsXOH/+PL6+vjg7O5fICIFIURmGQVpaGjExMcTHx1O9evUS23aZD0/9+vXj008/ZdmyZQwaNMiyPDQ0FH9/f1q3bl3sfbz66qsMGzbMatnFixcJCQnhH//4B4MGDaJOnTrF3k9xadhORMq6rMdaXLlyhQsXLti4NSKZnRzVq1cv0Uemlfnw1KtXL7p168bo0aOJjY2lTp06hIWFsWbNGhYsWGAZvxw5ciShoaGcPHnS6iGGS5cuBeDUqVNA5v2evLy8ACwPbAwODiY4ONhqv2fOnAGgdu3adOzY8VYeYqFp2E5E7IGPjw8+Pj6kpaXlOeVBpDQ4OjqW2FBddmU+PAEsX76c119/nYkTJxIdHU1wcDBhYWEMHjzYUiYjI4OMjIwc4+w33h9qzpw5zJkzB7C/K0I0bCci9sTZ2fmWfHCJ2JrJsLcEUcYV5anMRTX6A/jv95n/3vMptMg5RUtERERuQlE+v8v81XbyFz3bTkRExPYUnuyIhu1ERERsT+HJjuhqOxEREdtTeLIjutpORETE9hSe7IiebSciImJ7Ck92RMN2IiIitqfwZEc0bCciImJ7Ck92RD1PIiIitqfwZEd0qwIRERHbU3iyIxq2ExERsT2FJzuiYTsRERHbU3iyIxq2ExERsT2FJzuinicRERHbU3iyI3owsIiIiO0pPNkRDduJiIjYnsKTHdGwnYiIiO0pPNkR3apARETE9hSe7IgeDCwiImJ7Ck92RMN2IiIitqfwZEc0bCciImJ7Ck92RFfbiYiI2J7Ckx3RsJ2IiIjtKTzZEQ3biYiI2J7Ckx3RsJ2IiIjtKTzZEQ3biYiI2J7Ckx3Rs+1ERERsT+HJjmjYTkRExPYUnuyIhu1ERERsT+HJjuhqOxEREdtTeLIjeradiIiI7Sk82REN24mIiNiewpMd0bCdiIiI7Sk82RFdbSciImJ7dhGe4uPjGTt2LP7+/ri5udG8eXMWL15cYL3z588zduxYOnToQLly5TCZTMyfPz9HudjYWN566y06duxI1apV8fLyokmTJrzzzjskJyffgiO6ORq2ExERsT27CE/9+/cnNDSUSZMmsXr1alq2bElISAiLFi3Kt96JEydYuHAhLi4u9O7dO89y586dY+bMmdx555188sknfP/99wwYMIDJkyfTp08fDMMo6UO6KRq2ExERsT0nWzegIKtWrWLdunUsWrSIkJAQADp16sTZs2cZN24cgwYNwjH7ZWjZtG/fnsuXLwOwa9cuwsLCci13xx13cObMGTw9PS3LOnfujKenJ+PGjeO3336jXbt2JXxkRadhOxEREdsr8z1PK1aswMvLi4EDB1otHz58OJGRkWzfvj3Pug4OhTs8T09Pq+CUpVWrVgBEREQUocW3jobtREREbK/Mh6fw8HAaNGiAk5N1J1nTpk0t62+VjRs3AtCoUaNbto+i0LPtREREbK/MD9tdvXqVWrVq5Vju5+dnWX8r7N+/n3fffZd+/fpZglpuUlJSSElJsfx/bGzsLWkPaNhORESkLCjzPU8AJpPpptbdrDNnztCnTx8CAwP57LPP8i379ttv4+vra3kFBgaWeHuyaNhORETE9sp8eKpQoUKuvUvR0dHAXz1QJeXs2bN06tQJJycnNmzYUOD2x48fT0xMjOV1K+dHOepqOxEREZsr8+GpSZMmHD58mPT0dKvlBw4cAKBx48Yltq+zZ8/SsWNHDMNg06ZNBAQEFFjH1dUVHx8fq9etYjJB1hx49TyJiIjYRpkPT/369SM+Pp5ly5ZZLQ8NDcXf35/WrVuXyH7OnTtHx44dycjIYOPGjQQFBZXIdkta1tCdwpOIiIhtlPkJ47169aJbt26MHj2a2NhY6tSpQ1hYGGvWrGHBggWWezyNHDmS0NBQTp48aRV8li5dCsCpU6eAzPs9eXl5ATBgwAAAoqKi6NSpE3/++Seff/45UVFRREVFWbYREBBQqF6o0uDkCKlpGrYTERGxlTIfngCWL1/O66+/zsSJE4mOjiY4OJiwsDAGDx5sKZORkUFGRkaOu4HfeH+oOXPmMGfOHABL2UOHDlnC1WOPPZZj/5MmTWLy5MkleUg3zVHDdiIiIjZlMsrKs0f+JmJjY/H19SUmJuaWzH/y6wvX4qBuABxbUOKbFxERuS0V5fO7zM95EmtZc540bCciImIbCk92RsN2IiIitqXwZGd0tZ2IiIhtKTzZGcuwncKTiIiITSg82RkN24mIiNiWwpOd0bCdiIiIbSk82RldbSciImJbCk92xlE9TyIiIjal8GRnNGwnIiJiWwpPdkbhSURExLYUnuyMY7afmFnznkREREqdwpOdyep5AvU+iYiI2ILCk51ReBIREbEthSc7k33YTrcrEBERKX0KT3ZGPU8iIiK2pfBkZxSeREREbEvhyc5YDdspPImIiJQ6hSc7o54nERER21J4sjMKTyIiIral8GRnHLOFJ11tJyIiUvoUnuyMep5ERERsS+HJzig8iYiI2JbCk51x0rCdiIiITSk82ZnstypQz5OIiEjpU3iyMxq2ExERsS2FJzujYTsRERHbUniyMxq2ExERsS2FJzujYTsRERHbUniyM1bDdgpPIiIipU7hyc5o2E5ERMS2FJ7sjIbtREREbEvhyc7oajsRERHbsovwFB8fz9ixY/H398fNzY3mzZuzePHiAuudP3+esWPH0qFDB8qVK4fJZGL+/Pl5ll+/fj1t27bFw8ODihUrMmzYMKKiokrwSIrPUT1PIiIiNmUX4al///6EhoYyadIkVq9eTcuWLQkJCWHRokX51jtx4gQLFy7ExcWF3r1751t2y5Yt9OrViypVqvDdd98xa9Ys1q9fT5cuXUhJSSnJwykWDduJiIjYlpOtG1CQVatWsW7dOhYtWkRISAgAnTp14uzZs4wbN45BgwbhmL07Jpv27dtz+fJlAHbt2kVYWFie+xk3bhz16tVj6dKlODllnpY77riDe++9l3nz5jF69OgSPrKbo2E7ERER2yrzPU8rVqzAy8uLgQMHWi0fPnw4kZGRbN++Pc+6Dg6FO7wLFy6wc+dOHn/8cUtwArjnnnuoV68eK1asuLnG3wK62k5ERMS2ynx4Cg8Pp0GDBlahBqBp06aW9SWxj+zbvHE/JbGPkqJhOxEREdsq88N2V69epVatWjmW+/n5WdaXxD6yb/PG/eS3j5SUFKs5UbGxscVuT340bCciImJbZb7nCcBkMt3UupLaT377ePvtt/H19bW8AgMDS6w9udGwnYiIiG2V+fBUoUKFXHt+oqOjgdx7i25mH5B7L1Z0dHS++xg/fjwxMTGWV0RERLHbkx8N24mIiNhWmQ9PTZo04fDhw6Snp1stP3DgAACNGzcu9j6ytpG1zRv3k98+XF1d8fHxsXrdSnq2nYiIiG2V+fDUr18/4uPjWbZsmdXy0NBQ/P39ad26dbH3Ub16dVq1asWCBQvIyJZItm3bxtGjR+nfv3+x91FSNGwnIiJiW2V+wnivXr3o1q0bo0ePJjY2ljp16hAWFsaaNWtYsGCB5R5PI0eOJDQ0lJMnTxIUFGSpv3TpUgBOnToFZN7vycvLC4ABAwZYyr3zzjt069aNgQMH8swzzxAVFcWrr75K48aNGT58eGkdboE0bCciImJbtzQ8nTt3jrCwMCIjI7nzzjt5/PHHC33vpeyWL1/O66+/zsSJE4mOjiY4OJiwsDAGDx5sKZORkUFGRgaGYVjVvfH+UHPmzGHOnDkAVmU7duzIqlWrmDhxIn379sXDw4M+ffrw3nvv4erqWuQ23yq62k5ERMS2TMaNaaOIPvroI15//XUmT57M888/b1m+bds2evToQXx8PIZhYDKZ6Ny5M2vXrr2pAGUvYmNj8fX1JSYm5pbMf1q9HXr/K/Pfk4fBpGElvgsREZHbTlE+v4udYr7//ntiY2NzzAt68cUXiYuL45577mHs2LFUq1aNjRs3FuqBvpI3DduJiIjYVrHD05EjR6hUqRIBAQGWZadPn2bbtm00aNCAn3/+mQ8++IA1a9ZgGAafffZZcXd5W9OwnYiIiG0VOzxdvnzZKjgBbNq0CYDBgwdbbjDZuHFj6tSpw4kTJ4q7y9uarrYTERGxrWKHp4yMDJKTk62W/fLLL5hMJjp06GC13M/Pj8uXLxd3l7c1DduJiIjYVrHDU82aNTlx4gTXr18HMsPUmjVrcHNzo23btlZlC7pbtxRMw3YiIiK2VezwdP/995OSksKjjz7KypUrGTVqFJcuXeL+++/H2dnZUi4mJoZTp05Z3YNJik7DdiIiIrZV7Ps8vfbaa3z77besWbOGtWvXYhgGvr6+TJ061arcsmXLMJvNdOrUqbi7vK1p2E5ERMS2ih2e/Pz82LNnD5999hnHjx8nMDCQ4cOHU61aNatyp06d4sEHH+Thhx8u7i5va3q2nYiIiG2VyB3GfXx8ePHFF/MtM23atJLY1W1Pw3YiIiK29fe91ffflIbtREREbKvY4SkyMpLvv/+e8PBwq+WGYfDBBx/QoEEDfH196dy5M3v37i3u7m57Ck8iIiK2VezwNGvWLPr168ehQ4esln/wwQeMGzeOo0ePEhcXx+bNm+nSpQtRUVHF3eVtzVG3KhAREbGpYoenDRs24OLiwkMPPWRZlpGRwbvvvouDgwP//e9/2bt3L48++ijXrl1j5syZxd3lbU09TyIiIrZV7PB04cIFqlevjouLi2XZtm3buHz5Mvfffz+jRo2iadOmfPzxx3h4eLB69eri7vK2pvAkIiJiW8UOT9HR0VSsWNFqWdbjWfr06WNZ5unpSd26dTl79mxxd3lby361nYbtRERESl+xw5OHhweXLl2yWrZ582YA2rdvb7Xc2dmZtLS04u7ytqaeJxEREdsqdnhq0qQJ586dY9u2bQBERESwadMmqlevTr169azKnj17lipVqhR3l7c1hScRERHbKnZ4evLJJzEMg969ezNgwADuuece0tPTefLJJ63KHT58mMuXL9O4cePi7vK2pmE7ERER2yp2eHriiSd48cUXiY2NZfny5Vy4cIEBAwbw6quvWpX74osvAOjWrVtxd3lbU8+TiIiIbZkMwzBKYkNXrlzh5MmTBAYG4u/vn2P9xo0biYuL47777sPPz68kdlkmxcbG4uvrS0xMDD4+PiW+fcMAh/89W7lVA9j+UYnvQkRE5LZTlM/vEnm2HUDFihVzXHWXXefOnUtqV7c1kylz6C7DrAcDi4iI2EKJhacsSUlJnDx5kri4OLy9valduzbu7u4lvZvbmpNjZnjSsJ2IiEjpK7EHA69du5aOHTvi6+tLs2bNaNeuHc2aNbM81+6nn34qqV3d9rLmPSk8iYiIlL4SCU+TJ0+md+/e/Pzzz6Snp+Ps7Iy/vz/Ozs6kp6ezefNmevXqxeTJk0tid7e9rOfb6Wo7ERGR0lfs8LRmzRrefPNNHBwceOaZZzh69CjJyclERESQnJzM0aNHeeaZZ3B0dGTq1KmsXbu2JNp9W1PPk4iIiO0UOzz93//9HyaTiXnz5vHhhx9St25dq/V169blww8/ZN68eRiGwaxZs4q7y9uewpOIiIjtFPtWBZUqVcLDw6NQz6wLCgoiISGBK1euFGeXZdqtvlUBgP/D8OdVCKwM55bckl2IiIjcVory+V3snqe4uLhCP3KlSpUqJCQkFHeXtz31PImIiNhOscOTv78/R44cKTAUJSQkcPjwYapVq1bcXd72FJ5ERERsp9jhqUePHsTHx/PUU0+Rmpqaa5nU1FSefPJJEhMT6dmzZ3F3edvLer6drrYTEREpfcWe8xQREUGzZs2IiYmhSpUqPPXUUzRs2JDKlSsTFRXFoUOH+PTTT7l06RK+vr7s27ePwMDAkmp/mVMac54aPAFHzoGPJ8T8eEt2ISIiclsp1cezBAYGsnr1ah555BEiIiKYNm1ajjKGYVCjRg2WLFnytw5OpUXDdiIiIrZTIjfJbN26NUeOHOHTTz9lwIABNG3alFq1atG0aVMGDBjAZ599xuHDh/Hy8mL//v1F3n58fDxjx47F398fNzc3mjdvzuLFiwtVNyoqimHDhlGxYkU8PDxo27YtGzZsyFEuJSWF9957j8aNG+Pp6UmVKlXo1asXW7duLXJ7bzXLsJ3Ck4iISKkrsWfbubu7M3LkSEaOHJlnmQ4dOnDt2jXS09OLtO3+/fuzc+dOpk+fTr169Vi0aBEhISGYzWYeffTRPOulpKTQpUsXrl+/zqxZs6hcuTJz5syhZ8+erF+/ng4dOljKPvXUUyxcuJDx48fTuXNnoqOjmT59Oh06dOC3336jVatWRWrzraSeJxEREdsp9pynoqhUqRLR0dFkFKHLZNWqVdx///2WwJSle/fuHDx4kHPnzuGY9bySG8ydO5cxY8awdetW2rZtC0B6ejrNmjXDy8uL7du3A5khy9PTk5CQEL766itL/T///BN/f3+ef/75Qt/cszTmPLUZDdsPZ/7bvAlMpluyGxERkdtGqd7n6VZbsWIFXl5eDBw40Gr58OHDiYyMtASgvOrWr1/fEpwAnJyceOyxx9ixYwcXLlwAwMHBAQcHB3x9fa3q+/j44ODggJubWwkeUfFlz4pmXXEnIiJSqsp8eAoPD6dBgwY4OVmPMDZt2tSyPr+6WeVyq3vw4EEAnJ2deeaZZwgNDeXbb78lNjaWM2fO8NRTT+Hr68tTTz1VUodTIpyyhScN3YmIiJSuEpvzdKtcvXqVWrVq5Vju5+dnWZ9f3axyBdX9z3/+g6+vLw8//DDm/3Xn1KhRg40bN1KnTp0895GSkkJKSorl/2NjYws4ouK7MTy53vI9ioiISJYy3/MEYMpnUk9+64pS96233uL9999n8uTJbNq0ie+++4769evTrVs3/vjjjzy38fbbb+Pr62t5lcatGLKHJ90oU0REpHSV+fBUoUKFXHuXoqOjAXLtWSpq3cOHDzNx4kSmTJnChAkT6NixIw888AA//vgj5cqV48UXX8xzH+PHjycmJsbyioiIKNLx3QzHbD81DduJiIiUriIP23355Zc3vbPsw1uF1aRJE8LCwkhPT7ea93TgwAEAGjdunG/drHLZ3Vh33759GIZBy5Ytrco5OzvTrFkztmzZkuc+XF1dcXUt3YEzzXkSERGxnSKHp2HDhhU4VJYXwzCKXLdfv358+umnLFu2jEGDBlmWh4aG4u/vT+vWrfOt+8wzz7B9+3ZLufT0dBYsWEDr1q3x9/cHsPx327ZtVvd+SklJYc+ePQQEBBSpzbeahu1ERERsp8jhqUaNGjcdnm5Gr1696NatG6NHjyY2NpY6deoQFhbGmjVrWLBggeUeTyNHjiQ0NJSTJ08SFBQEwIgRI5gzZw4DBw5k+vTpVK5cmblz53L06FHWr19v2Ue7du1o2bIlkydPJjExkfbt2xMTE8Ps2bM5ffq01b2fygIN24mIiNhOkcPTmTNnbkEz8rd8+XJef/11Jk6cSHR0NMHBwYSFhTF48GBLmYyMDDIyMsh+z09XV1c2bNjAK6+8wnPPPUdiYiLNmzdn9erVVj1MDg4OrFu3jvfee49vvvmG999/Hy8vLxo2bMiqVavo1atXqR5vQTRsJyIiYjuleofx20Fp3GH88bdgwbrMfx9fAHXK1qiiiIiI3flb3WFcctKwnYiIiO0oPNkhDduJiIjYjsKTHdLVdiIiIraj8GSHHNXzJCIiYjMKT3ZIw3YiIiK2o/BkhzRsJyIiYjsKT3ZIV9uJiIjYjsKTHdKwnYiIiO0oPNkhhScRERHbUXiyQ9mH7TTnSUREpHQpPNkh9TyJiIjYjsKTHVJ4EhERsR2FJztkNWyn8CQiIlKqFJ7skHqeREREbEfhyQ4pPImIiNiOwpMdctQdxkVERGxG4ckOqedJRETEdhSe7JDCk4iIiO0oPNkh3SRTRETEdhSe7JB6nkRERGxH4ckOKTyJiIjYjsKTHdKwnYiIiO0oPNkh9TyJiIjYjsKTHVJ4EhERsR2FJzukZ9uJiIjYjsKTHVLPk4iIiO0oPNkhhScRERHbUXiyQ3q2nYiIiO0oPNkh9TyJiIjYjsKTHVJ4EhERsR2FJzukm2SKiIjYjsKTHVLPk4iIiO0oPNkhhScRERHbsYvwFB8fz9ixY/H398fNzY3mzZuzePHiQtWNiopi2LBhVKxYEQ8PD9q2bcuGDRtyLZuQkMDEiROpV68erq6uVKhQgU6dOnH8+PGSPJxi07CdiIiI7TjZugGF0b9/f3bu3Mn06dOpV68eixYtIiQkBLPZzKOPPppnvZSUFLp06cL169eZNWsWlStXZs6cOfTs2ZP169fToUMHS9n4+Hg6depEZGQkr776Kk2bNiUmJoatW7eSmJhYGodZaOp5EhERsZ0yH55WrVrFunXrLIEJoFOnTpw9e5Zx48YxaNAgHLPf+Cibzz//nPDwcLZu3Urbtm0tdZs1a8Yrr7zC9u3bLWXfeOMNDh8+zP79+6lVq5Zl+QMPPHALj+7mKDyJiIjYTpkftluxYgVeXl4MHDjQavnw4cOJjIy0CkC51a1fv74lOAE4OTnx2GOPsWPHDi5cuABAYmIin332GQMHDrQKTmWVnm0nIiJiO2U+PIWHh9OgQQOcnKw7yZo2bWpZn1/drHK51T148CAAu3fvJiEhgbp16zJ69GjKly+Pi4sLd999Nz/++GO+7UtJSSE2Ntbqdaup50lERMR2ynx4unr1Kn5+fjmWZy27evVqsetm9UC98847HDhwgC+//JIVK1bg4+ND3759Wbt2bZ77ePvtt/H19bW8AgMDC39wN0nhSURExHbKfHgCMJlMN7WusHXN5sxL1lxcXFi9ejV9+/bl/vvvZ+XKlVSrVo2pU6fmuY3x48cTExNjeUVEROTbnpKgZ9uJiIjYTpmfMF6hQoVce5eio6MBcu1ZKmrdChUqAHDPPffg7e1tKefh4UGHDh349ttv89yHq6srrq6uBR9ICVLPk4iIiO2U+Z6nJk2acPjwYdLT062WHzhwAIDGjRvnWzerXH51c5sXlcUwDBwcytZpUngSERGxnbKVCnLRr18/4uPjWbZsmdXy0NBQ/P39ad26db51jxw5YnVFXnp6OgsWLKB169b4+/sDUK1aNdq2bctvv/1mNeE7MTGRLVu20KZNmxI+quLRTTJFRERsp8yHp169etGtWzdGjx7Np59+yqZNmxg1ahRr1qzh3XfftdzjaeTIkTg5OXH27FlL3REjRtCoUSMGDhzIokWLWL9+PY888ghHjx7lnXfesdrP+++/T1xcHD169ODbb7/lu+++o2fPnly5ciXfOU+2oJ4nERER2ynz4Qlg+fLlPP7440ycOJGePXuyfft2wsLCGDJkiKVMRkYGGRkZGIZhWebq6sqGDRvo1KkTzz33HH379uXPP/9k9erVVncXh8z5Ths2bMDV1ZUhQ4bw6KOP4uzszObNm63uE1UWODhA1jx4hScREZHSZTKypw0pttjYWHx9fYmJicHHx+eW7ce5S2ZwurMe7P7klu1GRETktlCUz2+76HmSnLKG7tTzJCIiUroUnuyUwpOIiIhtKDzZqawr7vRsOxERkdKl8GSn1PMkIiJiGwpPdkrhSURExDYUnuxUVnjSTTJFRERKl8KTnXJUz5OIiIhNKDzZKQ3biYiI2IbCk51SeBIREbENhSc7ZblVgeY8iYiIlCqFJzulnicRERHbUHiyUwpPIiIitqHwZKc0bCciImIbCk92KqvnyWzOfImIiEjpUHiyU1nhCdT7JCIiUpoUnuyUY7afnB4OLCIiUnoUnuxU9p4nTRoXEREpPQpPdkrhSURExDYUnuyUo+Y8iYiI2ITCk51Sz5OIiIhtKDzZKYUnERER21B4slNWV9tp2E5ERKTUKDzZKfU8iYiI2IbCk51SeBIREbENhSc7pWE7ERER21B4slPqeRIREbENhSc7pfAkIiJiGwpPdkrPthMREbENhSc7pZ4nERER21B4slMKTyIiIrah8GSn9Gw7ERER21B4slPqeRIREbENuwhP8fHxjB07Fn9/f9zc3GjevDmLFy8uVN2oqCiGDRtGxYoV8fDwoG3btmzYsCHfOklJSdSrVw+TycT7779fEodQ4hSeREREbMPJ1g0ojP79+7Nz506mT59OvXr1WLRoESEhIZjNZh599NE866WkpNClSxeuX7/OrFmzqFy5MnPmzKFnz56sX7+eDh065FpvwoQJJCQk3KrDKRG6SaaIiIhtlPnwtGrVKtatW2cJTACdOnXi7NmzjBs3jkGDBuGYfQJQNp9//jnh4eFs3bqVtm3bWuo2a9aMV155he3bt+eos2PHDmbPns3ChQsZOHDgrTuwYlLPk4iIiG2U+WG7FStW4OXllSPIDB8+nMjIyFwDUPa69evXtwQnACcnJx577DF27NjBhQsXrMqnpqYyYsQIxowZw913312yB1LCFJ5ERERso8yHp/DwcBo0aICTk3UnWdOmTS3r86ubVS63ugcPHrRa/uabb5KQkMDUqVOL2+xbTsN2IiIitlHmh+2uXr1KrVq1ciz38/OzrM+vbla5guru3buXd999lx9++AFPT08uX75cqPalpKSQkpJi+f/Y2NhC1Ssu9TyJiIjYRpnveQIwmUw3ta6wddPT0xkxYgSDBg2iR48eRWrb22+/ja+vr+UVGBhYpPo3S+FJRETENsp8eKpQoUKuvUvR0dEAufYsFbXuzJkzOXXqFJMmTeL69etcv37d0oOUnJzM9evXycjjAXLjx48nJibG8oqIiCjaAd4kPdtORETENsp8eGrSpAmHDx8mPT3davmBAwcAaNy4cb51s8rlVzc8PJyYmBjq1q1L+fLlKV++PM2aNQMyb1tQvnz5XLcD4Orqio+Pj9WrNKjnSURExDbKfHjq168f8fHxLFu2zGp5aGgo/v7+tG7dOt+6R44csboiLz09nQULFtC6dWv8/f0BePXVV9m0aZPVKywsDIB//OMfbNq0iTp16tyCo7t5Ck8iIiK2UeYnjPfq1Ytu3boxevRoYmNjqVOnDmFhYaxZs4YFCxZY7vE0cuRIQkNDOXnyJEFBQQCMGDGCOXPmMHDgQKZPn07lypWZO3cuR48eZf369ZZ9BAcHExwcbLXfM2fOAFC7dm06duxYKsdaFHq2nYiIiG2U+fAEsHz5cl5//XUmTpxIdHQ0wcHBhIWFMXjwYEuZjIwMMjIyMAzDsszV1ZUNGzbwyiuv8Nxzz5GYmEjz5s1ZvXp1nncXtxfqeRIREbENk5E9bUixxcbG4uvrS0xMzC2d//TNZnhkcua/3x8NLw26ZbsSERH52yvK53eZn/MkudNNMkVERGxD4clOadhORETENhSe7JTCk4iIiG0oPNkpDduJiIjYhsKTnVLPk4iIiG0oPNkphScRERHbUHiyU9mH7RSeRERESo/Ck53K3vOkBwOLiIiUHoUnO6VhOxEREdtQeLJTjgpPIiIiNqHwZKec9GBgERERm1B4slMathMREbENhSc7pavtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU7p2XYiIiK2ofBkpzRsJyIiYhsKT3ZK4UlERMQ2FJ7slKOuthMREbEJhSc7ZTKBw/9+eup5EhERKT0KT3Ysa+hO4UlERKT0KDzZEbNh5nRGpOX/s25XoGE7ERGR0qPwZCd2pR/m3tin6RQ3hngjEVDPk4iIiC0oPNmJN5PmsTPjEOfNUbyd9CWg8CQiImILCk92YobH87jgDMAHyWEcyzinYTsREREbUHiyE3UdA3nRLQSANNIZmzgTR0cj8//TbdkyERGR24vCkx0Z7/4EAQ6VAfgpbTsubX8FICIKfthqy5aJiIjcPhSe7IinyZ33PZ6z/H/ykFngkgLA0Lfh3CVbtUxEROT2YRfhKT4+nrFjx+Lv74+bmxvNmzdn8eLFhaobFRXFsGHDqFixIh4eHrRt25YNGzZYlYmNjeWtt96iY8eOVK1aFS8vL5o0acI777xDcnLyrTikm/awcyc6Od0FQLTbnzR8YSEA1+IgZKqG8ERERG41uwhP/fv3JzQ0lEmTJrF69WpatmxJSEgIixYtyrdeSkoKXbp0YcOGDcyaNYvvvvuOKlWq0LNnT7Zs2WIpd+7cOWbOnMmdd97JJ598wvfff8+AAQOYPHkyffr0wTCMW32IhWYymZjl+U+cyLzU7mzbrwholHnvp63hMHGeLVsnIiLy92cyylIyyMWqVau4//77WbRoESEhIZbl3bt35+DBg5w7dw7H7A96y2bu3LmMGTOGrVu30rZtWwDS09Np1qwZXl5ebN++HYCEhAQAPD09req///77jBs3jl9++YV27doVqr2xsbH4+voSExODj49PkY+3sMYlfsh/ksMAuCvlLnYPe5+MFBcAVr8DPVvfsl2LiIj87RTl87vM9zytWLECLy8vBg4caLV8+PDhREZGWgJQXnXr169vCU4ATk5OPPbYY+zYsYMLFy4AmaHpxuAE0KpVKwAiIiJK4lBK1AT34VQzVQBgt+tuGvzfG+CUBsDj/4bPVkJ8oi1bKCIi8vdU5sNTeHg4DRo0wMnJyWp506ZNLevzq5tVLre6Bw8ezHffGzduBKBRo0ZFanNp8DF5EuY1FQ/cADhc6TeqvTUBHNO5EgNPvQ9VH02k9U+hNLv0FA/GvsJ/khazJ/0oGUbmXTXPZVzky5RVjIifxp0xQ3k5cXaZGqIUEREpi5wKLmJbV69epVatWjmW+/n5WdbnVzerXFHr7t+/n3fffZd+/frlGsCypKSkkJKSYvn/2NjYPMuWtHbOzfje+z36xr1MEilE1f2FylMmEfXWa5i6f0fSQwvZ7XsdgIPp8GP6b5AEjkneuKZ4k1gu0mp7+zNO4HYlAH7qx7KfISYBBneG14ZAxXI33840Ix1nU5l/q4mIiBRKme95gsxJ0jez7mbrnjlzhj59+hAYGMhnn32W7/bffvttfH19La/AwMB8y5e0js538q33u7iROd/pasPNuC/og8PQOZj+F5xulOEelyM4ZXnbeTZvbTjHkXPw51X4zzdQewi8tcDg14QjHM44U+jeqTQjnafi38brWmfuj3uJ8PRTN3WMIiIiZUmZD08VKlTItYcoOjoaINeepeLUPXv2LJ06dcLJyYkNGzbku32A8ePHExMTY3nZYn5UF+e7We49Hdf/BahUUyoAJky0vNKN9vMW0XDWV1T45p847+yIEeuLkeaMcbA55q9HkPHGh5h/ejCzjmsKDi9MxeSYjkvm02CITUpngtN7dEwZSZOYIQRdfoTn4v7DurQdpBipubYpyUjh4fjxfJG6kgwyWJu2jTtjh/JMwrtcMkcX+RhjzPF8m7qFLWl/WIYdRUREbKHMj6U0adKEsLAw0tPTreY9HThwAIDGjRvnWzerXHZ51T179iwdO3bEMAw2b95MQEBAge1zdXXF1dW1UMdyK3V3bs1Sr3/zSPzrJJFCf+eOTHQfSWO/WvByVqlawAAyMgwOnjXYeNGBdWdhyylIOBmM0XgPJv8ITPUO8fKSL3nePII3FibwZeOJmO7cZtlXpFMkH6Ut5aO0pbikedAr8X7+XTmE+p5VAIgzEngo7l9sSf/Dqo1mzHyS8h1fJa6j97WHqZdWnxpGdQLN1fHEk4goOBkJpyLh1J/g7AgDesdzsc0SPkxbwnUjDoDKpvL0c+nAwy6dae/UDCcNCYqISCkq87cqWL16Nb1792bx4sUMGjTIsrxXr17s378/31sVfPTRRzzzzDNs27aN1q0zr91PT0+nefPmeHl5sW3bX4Hg3LlzdOjQgYyMDDZv3pzrPKvCKK1bFeQlynyNVNIsj3EpjNQ0Mofp/A7ygDGaDDJwxJGlXm/xZtI8/sg4BoCR5gxHG0P9A5icb7gbZ7ojlfb3pMf1B1nfbCYXKxzKrJPkjvn9aZhqHsf08JeYPHK/BNC45geRNTDOB8H5mhjngzDVPYyp72JM3nF5tt0pzR3HVHfM6Y4Y6U6Y05xwO34ndTaNJsDDmyrloVoFqFEZalbNfAVWBgcHuHwdLl2DiOg0TqVEcYdzFar4OFHRFyr4QjkvKGBU2GaSjBTWpe3AGSd6OrcpcPi6NGQYGVwz4vAz+eBgKtlO7WhzLEczzlHRwZeqDn54m3JeHWuP9qQf5dmE93EwOdDTuQ19nO+lmWPdMvHzFLndFOXzu8yHJ8i8p9OuXbt45513qFOnDmFhYXz66acsWLCAIUOGADBy5EhCQ0M5efIkQUFBQOZk7rvuuovY2FimT59O5cqVmTt3Lj/88APr16+nQ4cOQOZdyNu2bcuFCxf4/PPPqV27ttX+AwICCtULBbYPT8U1JfFzpibnvNNmOZM3sxKmE7e7OZuOJLDJ2MGVur9iarsZk1vud2E34rwxT/0ATjTMXOAbjWnwZ5i6/oDJ0VykdhkZjjj82g3DLQmj+e+YXHMfLrSUjwzA/N5bcLZOjnUmEximDGi4F1O79ZnH4B2LEeeDsb09xu+dYP/duDg4Ub0iBFT669X4Dri7PtQJyOAX4w/+SD/KVSOWq8Z1rppjiTUSaO3UiBfcHqGSQ/kCjyvVSGNt2jbOmS/RxbklwY5B1sdhwMkLsOMIHItMJ7X+Ho7V/IkNzluIIzOIjnTtyxyPl0u0B+7CZXBzyQyR+TEMg50Zh1mc8hNLUjdy0bhKfYcaPOf2CI+79sTT5F6sdkSaL/N+0kI+SfmOZP76mXviTlUHP8qbfPAyuf/v5YGfyZuOznfSzblVmQ9YK1N/Y0j8JBJIsloe4FCZPs738rzbI9RzrFHo7aUb6aSTgZvJ9j3hIrdSgpFEvJFEFYf8p9UU1d8uPMXHx/P666+zZMkSoqOjCQ4OZvz48QwePNhSZtiwYYSGhnL69Glq1qxpWX7p0iVeeeUVVq5cSWJiIs2bN2fq1Kl07drVUmbz5s106tQpz/1PmjSJyZMnF6qt9h6e0ox07ot9ml0ZRyzLajpU4wfv92ngWNOq7LlLsOrgdT4zvmF//WWYPf7qITKu+RH48Uy6VKxNq2BwdYaUNEhOhYuOFznjdYjL7ue56nGeaI/zXPOOINE951wok9kR8+aemL95Ai79L8C6JWK6ayumezZB0ElwTAendEyOGeAZD86ZH7JGiivGR//C+LlHZj3nFGi0F9Pdv2JquwVT+byvtjTivDF23wNHmmIcbQwRd4DZESpHYuq0CscuqzAq5v0wQed0d2rtexiXVSEkXy1H09rQsn5m8LqznsEJl+N8nriKb9LXEW26nnmshomWV7ty79GhmC7cwYFTsP2omWvVDmC6dyOmezbl2ea6UW158uibVHT1wMU5c8jzuttFVlYK45T7UcxOqWQ4pJLukEaGKY0ajlW5yzGYu52CudOpPnUdAjkd6cDXm2DxRjjwv7n9gfUvE9guHOdG4aRUiIAMJ4wUVzJSXEhNceRP/91Ee57PtU3lTN486foAg1y64mlywxknnE1OOOOEgYEZM/HJBqcumolLMOHu5ISHozOeTk4kusSw0GMRYawkhfyDcm5ccKaT8130cb6Xfi4dqOpQIddyGUYGn6V8T6yRyAMu7ajvGMSl6MywWtsfGgTdmt7Hj5KX80LifzCT95cIN1yY5vE0z7s+kmdPXoaRwZb0vSxO/YlvkjeTZqQzzvUJxns9iovJOdc6hmHckp6tNCOdS8Zfv8MmTDjiQGVT+UL3RKYaafyeHs7+jBOcN0dZXpfM0dzj1IQ5nuNwL+FwmGKk4mpyKdY2kowUNqbtJtixBrUdC/dFu6REma8xM3kxy1I30dqpEZPdn6SWY/VibzfDyOCo+Rx7049xwnyBdCMd8/9+b80Y1HMM5FGX7qUe1sPTTxESPwE/Bx82eM8u0S+Nf7vwZE/sPTwBHM04S8uYESSSzF2O9fnO+708P3yyxBoJvB/9LZ+lfYtnhhdfub9JG7+iXXl4zRzL4YyzHDGf4XDGWVxxZpjr/ThfDuDDFbB0C7i7QqOa/3vdAfUDoVI58PMGVxc4k/Enj8S/zp6Mo5btdojtztW0BI747ibdKWcvmXO6OzVjG3HG5yBpTkk51gMYiR5wyR/THSeKdExGkjvGlh6ACbxjMPlch4pRmKrlHjgADLMps/frWoXMXrEKl3OWSfDE2NsaU6tfMDln3hzVOFEf81vvg4M5c4i02/eWdQUxpblgjvWBBC9I9IYkD6h+FlPli4U+VlO6M+VigrhWoWjnqFDbTnXFY09n4lLTMwNk1ss9AZND/n/CHFLduGPZ61Q91hkXp8z3UNNa0KJRCvPrT2Y1P1vKel+qQ9yGzmRs7QjJHlQLiqPlXbE0ahxHbR9v0g81ZddhB3YegUNnIagKdG8J3e+GTi3A53+dXQlGEpHmK/xpvooTjvg5+FDe5E05kxcTkj7hg/89HQBgoEtnprg/xYa0XaxM+41NabtJ5a+fW7VLzan+9WuUT6xOj5bQoW08UZUPsj59J1+nrCfSyPn+cL9ck0eOjWNojeZ4ucOZpGusdF7DBp9VRLqfxslwxtVwxc1ww93kShWjIrVNNahnCqK+Uw3qu1bD08V6OkSgQ5Vcg4thGHyVupqXE2cTbeS8VYsHbjRwDKKh4x00cqxFLUd/PHDH0+Rm6Zncmr6fdWk72Zy2J0dPXHbtnVrwrfc7+OTRq2gYBn8aV9iXfoLf4k5wKi6WRzzb8VClZjnKRpov88+EWaxI20LLjOY8G/M8Fa/X5Vpc5nvk3sbg978/4THmeNxNrjkC6cmM8/w35Vvmp6zkmhGHKy781/MVHnftlWv70v7XO1gSAfCC+TIzkhbyacr3JPHX7XKcDCeecevP6+7DqOCQf9dxejpEx4GfbwZHzWfZmXGYXemH2ZN+lAMZJ622m5vqpkq84T6cOy/cz/YDTtxRDdo1gQy3ODan7+GM+U9SjXRSSSPVSMOMQRunxvRyboOjyfr9ZRgG2zMOsiRlA8GOQTzh2ssqmBmGwbyUH3gh8T+WXug33IYz2ePJop66PCk82dDfITxBZrrfm3GM/i4d8TC52bo5RZJspPB84gfMS1mZZxlXXOjl3IZBLl3p7XIPniZ3kowU1qZtY2nqJn5I/TXfP+KYHTD2tMHY2hnjSmWI8818OaZjejCs0MHFSHXB2HEfXKiBqeeKPG8vAeBkdqbOpbZ47uzO+TX3EHnRFRrtweHV8Zg84zO3F10BPONyDGsa6Y6Q5gLpzmB2yHc/RWGYTRB+J8Yv3TB+75gZvO44hun+JZjuW1/o8Jbn9pPcMdb0x/g+BGJyGwY1wCUF3JPANQmqn8N092+Zr0rWPYPmr4djLBkBhgN4xeIw/l+YGuwvWntO1se8aBT80RrI6r0xoO4hHLt9j3vTcMzlL5PinFCo7b3s8hj/9nwaB5MD5y7Bsp9hye8J7Gj2Gaa+S6zPw/b2mO44DoGncw2MRpI7uKRm9sBmHfOmnpjckqDlr5icineVqlu6B02PP0z5LYM4e6w8UdehVv3rXAt5j5M1Nhdr2wUyTGDKPOYGaQ1Y7TuDALfMYGAYBuvSdvB/icv4Pe0gMY7Xc1R32duO1n/8g04V78DVxWClxw/suHsOGe7xf+0iwwFjfV+MsKcgtjwmn+tUf3AD5vvWcKniIRwMB2o4VKWOY3XqOAZw2vwna9O25dgXwBjHwczwGW3pFfnTfIVpSV/wecoPOOBAk+RmVDjZlutb2hKxvwb+ta9S7q5wzHUOcrXSYTycHbnLaEobowV30QgfZ1cqls/gkPk029LD+Tl9L8tTN1uF7Bu5p3vxstujPOxxHw0da1p6/yKiYPWODBZHHOZ3l22k1t2LqfaRzN+hm2T8WR3j2yFQ7ioOLXZA3UPgmPf7LcChMgPT+1Juex+izvtyod569tRaxjnvv770VjRXYGT6ozxuehAPdzOvmd5jcdo6y/pmjnVY5PUm9W+Y6lAcCk829HcJT38H81J+4LmEDyzDPlVMfvRybktvl7Z0dW6V57dXyOzK35N+jG3p4WxLD+f39ANEGleo6xDIMNf7edy1J86xlTgVCVdjM19XYuBaXGZPmG+NKNYHfsVS5x9y/IEzpbngcaE+1Q/1pNaZzlRy8sHXE7z9Ejlc/1s2Bi4i1vkaAM440d25NQNdOtPXuR2+Dl6W7Vy8CheuwP60U4yv9DJXnK3Dgku6Oy1PD6D5oRBSon25Hg/X4jPbGGW+xmW/oyQFHMn8w1n1Ai6+8Zi84kj9X++bB27c5diAugmN8TjbGNOZenh7gJdPKh7eqbh5ppDyZxUO7/fj90Ow7wRkZB+FKncVU+cfoeoFcErPDJZOaZn/Njvg4mjCy90BbzcH3FwM0k3ppJFGuimdDDLwPtsUr02PEPNnOa7EQHpG5pyz5nWgWR1ocgdcjIbth2HbIdh1NHNYOJMBNY/j0G8Rpvv++oNrbGuPeeHTOIx7HVONM5nLktwxfhyIqekuTPUOFeq9ZRxqRrWNw7noch6j63eYah0vVD1L/QxHjE9ewmfrg3Rqkflz3HnkhkKN9uDw7L8xVfkz7+2kO8IfbTB+7k7DK+2oc/c5frrrPVJr5n0cxtlamQHSJSXz5ZaEySvvizJy1E92w/jpITjeANOI/7MaSjb23wUJ3n8Vdk4F/3NQ9UKBPYQArgl+NIptRb2Y5hzZ5c/e7ZXJuFwJapzGYcKLmLwze7aMc3fgP/c/JNY4RFzPLzHXuvHk5dLuDAeMzb0wVY7E1OSPvMsleGVeGNN0Z6EDp5HqAieDrcJ41YiWTEx6lR89fuCnqotJy6XXO2t/WV9+cl2f5gznauFQPQLDLecFN0aKK8ZPD2L89CCm9j9hemAxJtcbeowSvXA51RiXs/WJLReBqenOfC/Egcx5o6azdSl/pS4BSbVwwxUHkwMOmMhwSGN3zRVk3PVrvtsoiJHhAMke+R9/rC8kemKq+te9Ce863Z+no5+lezNXAgp/bVSBFJ5sSOGpbDmccYZNabtp7dSIFo71bvoqMMMwSCQZD9yKNF/kkjmaAxknKWfyoqKpHJUcyhW4jUQjmZWpv2LGoKdzG8o5eOdZNssF82X6xr2ceZd4XPiHW39ecXuMygVMWk9Nywx+JqDq/0Zm04x0Yo0EfEyeRbozfGIy7D8Jx87DsYi//ms2oF4A1K+ROcxaLwDqBf41JFJS0tIh8krm/Dp318yXk6PBzJTFvJo0N9f5RU7x5TGmzcD1fH0GdIDuvf7kXJ2N/JyxB1dc8DZ7E3/Zh8gIT477/8y1SsfybYOR4gJXq0B0RYzoinC9AjhkZPYGesWBVxwku2P+LgT2t8xzO8E1MoeN6tVL4LcWc/jR+zsAHMyOuF+sQ/zexhhHG+Mc3prBrXz5xwPQpmHm/KwMI4O3L3/LO3xMktP/HnqeVIE7I3vRPro3gWlBxCdBXBLEJWa+Yonjsuc5rnmfI8b3HDFuUVyPh7T/ZQeTWyLctTXnVbZZxx3ri/m/42BbHnNHXVJwDjpL+YanSPO5wvXUZHBLBtfkzIB1vibG3lZwtjZ/9ejdoMYpHCaNtYQ1I80pR3uMmHJwqh6crUPNlDrUqJHEjkbzSfHJObQJ4P57T5pt/weJrX7icMsvSHPOY9j+bK3MXtuq5zF5/tWraERVxVjbD2N9H5yTypHeeQWmJ/+TZ+gyktwh3idHr+hNSfTEvLo/xspBuCeXZ+az4GCC/9t8mfBWn2HqtKrQF+Y4Xq1C+tGGGCcaYBxvAKfqQ1IhLrioexCHRz/B1GyX1WIjoibGvpZwvCFGihtkOEG6E3gk4NBxNdz5e65tM07Wx9hwP6YmezC13ZxzfYIn5rnj4ffM99nSKfBwh0IdYqEoPNmQwpPYSrKRwqa0PTR3qks1h4q2bk6ZsiZ1G0MSJhFj/PUNt45DAD96z6AmATg4FDwx3GyYWZ62mUmJn3LUfM5qXUvHhjzt9hD3xXVhxz43tuyFLfsybwHi7poZbO5rCu2bZs6T2rIP1u2C9bszwytk9qg93D7zw6DBDSMRe9OPEWMkcJdTfbxMHkTHwqEzmeXyuiIy0nyZsJR11HOsQS/nNkWeWGsYcP4yhJ+Gw2ch3vMSfzRYxE++35Ni+mtY+O6ENnTaNp5jByqSlAL+FaB6pf/9939Xq1avBBV9/zrHcYmZ7T94JvPihJ1HYPex7D2HmWpWhX73wQP3ZNZdf+E8/9doLAk+1r1x7pF1qfHrUFrGtqfnXY50veuvR0olGsl8mLyUtxO/Is6U+fOvllGNOW7jeMC7tWUbF81XeSPxY+an/giAv6ki/UzdqHW8J5F76nDuEkRcNohIiiHSOXPOYlvXBnS7M3N/d9eHPcfg7T/28uO9r2P4XP/rXKY7Yvz0EMY3wwlyL0fzDmdwb/M7EYG/c8bxLHWoQVBcI7wjGpN6uCHRSWlcrPoHF/3/4LL/HyT4RmJcrQhHm2AcaYxxtAmcrgvpLjSqCV9PypwDmmXXEXjvl7P87raVuMD9JNQMx+zz12R+t1Rv7sloySC/1vRwbUWAQ2XiE+HwOTh4OvOLz4kLcPx85ishl04zTzcI6QKj+kJCrT1sSN9FTYeqdHNuhWtMVTbvzXyPr92Z+T6yUvEilR9eSWr7H0lzjaNV7H20Ofcw3hcacSXGRFIKXPQ8xb6mX3G2znpwMONypgHJ707BuPjXZPh9n0PT2pQYhScbUngSKZuOZpylX9y/OGaO4G7HYL73fr/AnrncpBvpLExdS2jKaho4BvGk64O0cKqXa9nYhMxbPrjkfuEbZnNmiPD2gKCqRW6KTVwyR/Of5MX8mr6PJ1x68ZTrgyVy9V5aeuYH9/bDEJ8EXe/K/GC8cdPnzVH0jXuZAxknucepKePdnijUvc6ummP4OOVbHHFgjNvDeJk8ci13JOMsV80xtHFqlGNScxazOfPllEcePZZ0kb5XJnDS4zAtLnfi6finaVshgKAq4JX7bvOVZKRw+bILYetNfPVTZugEePJ+mPUceBQwLdUwDE6bI9mbcYyqpoq0cmpQ6DBtGHA9HjIyMoflzUbmvyv6glsh5r0bRmb4XrsTdh+FugEwsCM0rFmo3XM6I5JjGRF0dr4Lc5oTp/78K9j944GCj70oFJ5sSOFJpOxKNdI4kHGS5o518/xglLIvw8jgkhFNNVPFMntDUcMwSCKlxC+4MYzMwJ2aDi3qluimb3tF+fzWcy1E5LbhYnLmLqdgWzdDisnR5Ii/qZKtm5Evk8mEByV/pbLJZD1EJ7ZR5h8MLCIiIlKWKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRONm6AX83hmEAEBsba+OWiIiISGFlfW5nfY7nR+GphMXFxQEQGBho45aIiIhIUcXFxeHr65tvGZNRmIglhWY2m4mMjMTb2xuTyXTT24mNjSUwMJCIiAh8fHxKsIVyI53r0qXzXXp0rkuPznXpuVXn2jAM4uLi8Pf3x8Eh/1lN6nkqYQ4ODgQEBJTY9nx8fPSLWEp0rkuXznfp0bkuPTrXpedWnOuCepyyaMK4iIiISBEoPImIiIgUgcJTGeXq6sqkSZNwdXW1dVP+9nSuS5fOd+nRuS49Otelpyyca00YFxERESkC9TyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCUxkTHx/P2LFj8ff3x83NjebNm7N48WJbN8uubdy4kREjRhAcHIynpyfVq1fnwQcfZPfu3TnK7tmzh65du+Ll5UW5cuXo378/p06dskGr/z4+++wzTCYTXl5eOdbpfBffr7/+Su/evSlfvjzu7u7UrVuXqVOnWpXReS6+P/74g4ceegh/f388PDwIDg7mzTffJDEx0aqcznXRxMXF8corr9C9e3cqVaqEyWRi8uTJuZYtyrmdPXs2wcHBuLq6cscddzBlyhTS0tJKrN0KT2VM//79CQ0NZdKkSaxevZqWLVsSEhLCokWLbN00u/XRRx9x5swZXnjhBVatWsWsWbOIioqiTZs2bNy40VLuyJEjdOzYkdTUVJYsWcK8efM4duwY9913H5cvX7bhEdivCxcu8PLLL+Pv759jnc538S1atIgOHTrg6+vLl19+yapVq/jXv/5l9WwunefiO3ToEPfccw9nzpxh5syZrFy5ksGDB/Pmm28SEhJiKadzXXRXr17lk08+ISUlhYceeijPckU5t2+99RYvvPAC/fv3Z+3atTzzzDP8+9//ZsyYMSXXcEPKjB9//NEAjEWLFlkt79atm+Hv72+kp6fbqGX27dKlSzmWxcXFGVWqVDG6dOliWTZw4ECjYsWKRkxMjGXZmTNnDGdnZ+OVV14plbb+3fTp08fo27evMXToUMPT09Nqnc538Zw/f97w9PQ0Ro8enW85nefie/311w3AOHHihNXyUaNGGYARHR1tGIbO9c0wm82G2Ww2DMMwLl++bADGpEmTcpQr7Lm9cuWK4ebmZowaNcqq/ltvvWWYTCbj4MGDJdJu9TyVIStWrMDLy4uBAwdaLR8+fDiRkZFs377dRi2zb5UrV86xzMvLi4YNGxIREQFAeno6K1eu5OGHH7a63X9QUBCdOnVixYoVpdbev4sFCxawZcsW5s6dm2OdznfxffbZZyQkJPCvf/0rzzI6zyXD2dkZyPnojnLlyuHg4ICLi4vO9U0ymUwFPge2KOd2zZo1JCcnM3z4cKttDB8+HMMw+Pbbb0uk3QpPZUh4eDgNGjTAycn6kYNNmza1rJeSERMTw549e2jUqBEAJ0+eJCkpyXKus2vatCknTpwgOTm5tJtpt6Kiohg7dizTp0/P9VmPOt/F9/PPP+Pn58eRI0do3rw5Tk5OVK5cmX/84x/ExsYCOs8lZejQoZQrV47Ro0dz6tQp4uLiWLlyJR9//DFjxozB09NT5/oWKsq5zfqcbNKkiVW5atWqUbFixRL7HFV4KkOuXr2Kn59fjuVZy65evVraTfrbGjNmDAkJCbz++uvAX+c2r/NvGAbXrl0r1Tbas2eeeYb69eszevToXNfrfBffhQsXSExMZODAgQwaNIj169czbtw4vvzyS3r37o1hGDrPJaRmzZr8/vvvhIeHU7t2bXx8fOjbty9Dhw5l1qxZgN7Tt1JRzu3Vq1dxdXXF09Mz17Il9TnqVHARKU35dV8W1LUphTNhwgQWLlzI7Nmzueuuu6zW6fwX37Jly/jhhx/4448/CjxnOt83z2w2k5yczKRJk3j11VcB6NixIy4uLowdO5YNGzbg4eEB6DwX15kzZ+jbty9VqlRh6dKlVKpUie3btzNt2jTi4+P5/PPPLWV1rm+dwp7b0vgZKDyVIRUqVMg1FUdHRwO5p24pmilTpjBt2jTeeustnn32WcvyChUqALn37kVHR2MymShXrlxpNdNuxcfHM2bMGJ577jn8/f25fv06AKmpqQBcv34dZ2dnne8SUKFCBY4fP06PHj2slvfq1YuxY8eyZ88eHnzwQUDnubheffVVYmNj2bt3r6VHo3379lSsWJERI0bwxBNPULVqVUDn+lYoyt+LChUqkJycTGJiouXLQ/ayN35hvlkatitDmjRpwuHDh0lPT7dafuDAAQAaN25si2b9bUyZMoXJkyczefJkXnvtNat1tWvXxt3d3XKusztw4AB16tTBzc2ttJpqt65cucKlS5eYMWMG5cuXt7zCwsJISEigfPnyDBkyROe7BOQ2/wOw3KbAwcFB57mE7N27l4YNG+YYCmrZsiWAZThP5/rWKMq5zZrrdGPZixcvcuXKlRL7HFV4KkP69etHfHw8y5Yts1oeGhqKv78/rVu3tlHL7N/UqVOZPHkyb7zxBpMmTcqx3snJib59+7J8+XLi4uIsy8+dO8emTZvo379/aTbXblWtWpVNmzblePXo0QM3Nzc2bdrEtGnTdL5LwMMPPwzA6tWrrZavWrUKgDZt2ug8lxB/f38OHjxIfHy81fLff/8dgICAAJ3rW6go57Znz564ubkxf/58q23Mnz8fk8mU772kiqREbnggJaZbt25G+fLljU8++cTYuHGj8dRTTxmAsWDBAls3zW69//77BmD07NnT+P3333O8shw+fNjw8vIy2rdvb6xatcpYvny50bhxY8Pf39+Iioqy4RHYv9zu86TzXXx9+/Y1XF1djalTpxrr1q0z3n77bcPNzc3o06ePpYzOc/F99913hslkMtq0aWN8/fXXxoYNG4y33nrL8PLyMho2bGikpKQYhqFzfbNWrVplfPPNN8a8efMMwBg4cKDxzTffGN98842RkJBgGEbRzu20adMMk8lkvPbaa8bmzZuN9957z3B1dTWeeuqpEmuzwlMZExcXZzz//PNG1apVDRcXF6Np06ZGWFiYrZtl1zp06GAAeb6y27Vrl9GlSxfDw8PD8PHxMR566KEcN8aTosstPBmGzndxJSYmGv/6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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_epochs = 100\n", + "batch_size = 32\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", + "\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(\n", + " inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) \n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + "\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train diffusion completed, total time: {total_time}.\")\n", + "\n", + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "326101ed-333b-44a9-933f-55760b5d93a4", + "metadata": {}, + "source": [ + "## Check the performance of the diffusion model\n", + "\n", + "We generate a random image from noise to check whether our diffusion model works properly for an image generation task.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8f7a9e99-a8a4-4c8f-a42f-17ef91b18585", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 1000/1000 [00:10<00:00, 95.94it/s]\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAloAAABOCAYAAAD4g7hOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADC7ElEQVR4nOz9d3CUR7cnAB/lnHOeV5orzSvNFbPSlKSVZiXNKl/lq3yRhLQgkC5RS1SRrSIHA1pyRgUmg80asMHECxgDtjELxhgTLzYOYAOvA7bh9/0x200/YQTv1r3fV/UVXXUKNPPM8zzdfbr7nN9JNgBAr9vr9rq9bq/b6/a6vW6v2394s/3/9Qu8bq/b6/a6vW6v2+v2uv3/a3staL1ur9vr9rq9bq/b6/a6/Se114LW6/a6vW6v2+v2ur1ur9t/UnstaL1ur9vr9rq9bq/b6/a6/Se114LW6/a6vW6v2+v2ur1ur9t/UnstaL1ur9vr9rq9bq/b6/a6/Se114LW6/a6vW6v2+v2ur1ur9t/UnstaL1ur9vr9rq9bq/b6/a6/Se114LW6/a6vW6v2+v2ur1ur9t/VsMrNiLilJ6ezj83mUwYM2YMgoODQUQYOHAgiAj79u2DRqNBv379kJiYiPr6esk9AGDWrFkgInh4eKC6ulryHXumm5sbfv75ZyQnJ+PJkyews7MDAAQEBPDrL126JLk3EaGjowMDBw5Ed3c3PvvsM0RHR0u+N5lMit+o0S+//AIAOHnyJKKjo1FZWQkiQmBgIIgI+/fvR0lJCYgIe/fuBRHxa1hfPvroIxAREhMTQUT4448/JPfOyMgAEeHy5csICwvjfQeAX3/9tc/36+npQVhYGI4dO4YpU6Zg5cqVku/l/bZGx48fl4z7ggUL+HeBgYGoq6vDmDFjQEQ4efIkiAhvv/02v2bp0qUAgLS0NISFhfH3JyIMHz4cALBp0yYQEUaPHo2JEyfi8ePHivlm1N3dLflbo9EgKioKFy5cQHFxseL6V6UBAwbw54WGhmLBggVwdnYGEaGxsRFEhLfeegve3t5oaGiAjY0NJkyYwH/v5+cHAJg8eTL/bNGiRZL5/umnn0BEiIqKwu3bt9HS0oJdu3apvvfDhw8V71hbW4uZM2di7NixePDgAWxtbSXfa7Xal/bT1taW93PDhg3IyclBeno6iAjZ2dl8HnNzc/l429raQqPRSPqyb98+EBF0Oh2ICGvXrpXMWWRkJGxtbfHw4UN4enpKeHf37t38XmazWfGOM2bMgNFoxNtvv421a9dixIgRku9TUlJeaU4/++wzCQ+NGzeOf2dvb48JEyagtbVVwruzZ8/m11y4cAEAEBMTg5iYGAnvLl68GADwxhtvgIjw5ptvora2VtJP+ZympaVJ/i4uLkZAQAA+/vhj1NbW4vPPP/9/4t3Ozk7J82bMmCEZXx8fH0yZMgV2dnbo6OgAEWHixIn8mtTUVABAY2MjXFxcQETYvn07iAj+/v4AgAMHDoCIYDAYsGnTJkyYMAG1tbWYMGGCop+MN+R9nzt3LhoaGnD58mXF905OTi/tZ0BAAI4dO4bvv/8epaWlMJvNCA0NlfDRlClTEBcXBw8PD6SnpyM8PFxyDwAYOnQov5+/vz+ioqJARHjy5AnOnTsHOzs7uLi4YPPmzfDx8cGxY8dARLh27RoyMzMl61H+jiUlJUhJScHQoUPR1dXF93ZGsbGxrzSnra2tWL16NVpbW+Hs7MzXGaOsrCx+r6KiIhAR4uPj+fcjRozgv/X19QURYfz48SCynEM7duzg1xcWFiIoKAiTJk3iYzh69Og+30+v18PFxQXV1dXQ6/X8Hf5ecnZ2hp+fH7y8vEBEcHR0VFzj4OAg+Ve8xsHBAb6+vrCzs+Ofubm5gciy14WFhXGetrW1haOjI9/XXV1d+dgwUns+e087Ozt+71ehV2n/T4IWEcHb2xvu7u6cET/++GPOOFevXsWqVatARIrFxjogv6dGo1EcrozmzZsn+buhoQF37txRLK6+BiIoKAhEloPW3t6ef+fv748ZM2YAAM6dO6f4bVBQEJ8wohfChZ+fH06fPo179+7xZxiNRtVnM0FKXLQffPCB4lr57wMCAnDmzBmsXr36pX2srKyEyWSSjKv8QG5pacHjx48BQLER+Pr6wtnZmQuwy5cv54tw3bp1uHv3LogIp06dUp2n5uZmybNTU1NV59ka7d27F7/99ttLrxswYIDivoWFhZJrCgoKcOjQIdU5sbW1hZeXF+/nmDFjuKDe1dWFu3fvYtSoUQgJCcHx48cVPKrWJw8PDxw9elT1fZlwKv7NBLFX5V32fyYsMIqOjsby5csBAKtWrYKnp6fk+8DAQL6xRUZGcp4zm824du0abt261eccyT8vKipCVFQURo4cqbh2+PDhkr/j4+Nx6dIldHZ2vrSPRqMRa9euxYkTJ/h7+/j48O9tbGy4sA5YhDv5OnFzc+PKz6pVq7iws2fPHty8eRNEhB9++IELICLdvXtX0ld2mLwq7+7bt++VhKdhw4Yp7mswGCTXlJWV4dSpUwCg+M7JyQmenp5c6Bg3bhx6e3s57548eRIpKSkoKyuTKEpExPdK8dnssGGKoJzkAuP48ePxySefvLSfTDEQn1VQUKDgDybENjc3K+7h5eUFb29vEFkOeybo5ufnY+XKlZg5cyaIXgj+L+PduLg4pKWlSfiKkVyQ0uv1WLRoERISEl7aVy8vL/T29qK4uBhElj1XFAZcXV1RXV2Ny5cvY8+ePfzMFL93cnLiZ0xubi5sbGxAZNnTm5qaQGQRqOR8T2RRcPPy8vjfbM8fPHjwS9/d3t4epaWlHCjoi3Q6HWxtbSVKpYeHh+Sa2NhYNDY24q233lIdZ5EcHBy4QOXk5ARXV1f+f/a5nCIiIhSfycfTGjk6Ov5dwpMoW7D5EPnb1dUVwcHBEoW2T/npla5iF8textvbG4MGDeKMHRcXp2Dyd999F6NGjVL89saNGxLpGACmTp2K6upqfqhbo9bWVqxYsQJEFuRl586dEtShoaGBa+ri4vn5559BRGhvb8ehQ4f4c8X+7dixA0QkQdgCAwP5pldSUoLly5fz3zJtoaWlRXWMxo4dKzlsAGDChAmYMGECZsyYwRFAayTe89ixYxJN3Gw2qx4c77//PsrLy5Gfn88F4GfPngEA3nvvPRw+fFj1WS4uLvDx8UFXV5fk2fv378fTp09BRAgODlbtJxFh165dfMFcvXoVy5cvx8KFC2E2m/H777+/cj+vXLmCVatW8Y2WiNDW1qbQhqdMmYKTJ0/C19cX69atg5ubG3p6egAAW7du5ff89NNPFc/z9/fnyAcAfkiL73H9+nUuMIp09+5d5Ofn879//vlnzJs3D5WVlVbHhtHOnTv5nL3//vvYtm0bX0NEhJqaGoVwWFpayu/b2dmJZcuW8Xf9+eef+XdffPEFiEiyroKCgvi7bt68mfMiAD6eEyZMwFdffaV41+3bt0tQJgDo7u7G+PHjsWPHjpcia+y9wsPDcfDgQa5JE1kOFCY0i3Tx4kXExcWhsrISn332mWSNXr58WZXfiSyKj0aj4YcmQwoZLxARkpOTVefHzc1NggSzfs6fPx/19fVcAHwV3j158iTmzJkj+b6lpUXxm4ULF2LJkiXQaDQc6T1y5AgAYOPGjfyeIiIlzimbFwD8sBHfQw1NIrIIhcnJySAihIaGYt++fZg8eTLy8/Nx//79Pvt57tw5zpvvvPMO5syZI1Ei8/PzOWrEqKGhgQtn7e3taGhogF6vBwDeX7YuiEiy5n19fbnlYe3atarK28CBAxVIKJEFsRQVsHPnzqGpqQmlpaUSJFCNvL29+T5rMpkwduxYLkgRWYQO+Rr19fXF0qVLYWdnB5PJxJXR999/H2fPnsWGDRvg6urKhVtReXdzc0NAQAACAgLg4OCAqqoqEBFWr14NvV4PIouixJAqkUJDQzlSTUSYOXMmUlJSkJGRAYPBIPlOjUSluaysTNIvR0dHfqaLlJubi4SEBPj7+3MBurm5GfPnz0dRURGGDBkCIqWAwogBHWyu7ezsJIKZXIBjJO7/Dg4OcHJygqOjI2xtba3+Ru2eTLgVv5dbDdh7snliz7G3t4ePjw/c3Nz4O79Ke2VBi23eiYmJGDZsGA4cOICIiAg8ePCAv1hraysaGhokk8ckcpEYXO3s7AxbW1v09PSgu7ubS6zLli3D+++/DyKSaM/BwcEKLWvo0KEAgG+//Rbu7u7w8vLigh1bkImJiRw2F9+1pKQE3377reR+TKBiz2G/Wb9+PV8AhYWFaGxslGzOalqhHGadP38+VqxYIVm0jx49QlhYmGTDYouM6AWKYjAY8OWXX+LZs2cc1v7zzz/5OzKtpn///jh//jy/T0xMDIKCghQHjJ+fH1/kzs7O/Hs2nmxDGTRoEKZMmcJ/x5BKubbd0NDA/9+/f3+sWrVKolUB4AIcg3iJiAvIpaWl/LNNmzYBABcoRHMVQze7urpQV1eH9957j/8uNTUVP//8s8IszOD4uLg4bN68GVOmTOHmb8aHHR0dGDJkCH9nd3d3vsmJxA4Ee3t7BAQEYMWKFZgyZQpHjq5du8ZNKswUyd5BvrgnTZoEALh16xYcHByg0+k4xP3DDz+AyCLYG41GyfzV19dj0qRJ3NTBKCkpCUSEjIwMREZG8t8cOnSIbyQ1NTVobW3F+vXr+e8YKiISE2jYZrl06VL09PRwBcbW1ha3b99W5XM2z4xyc3Px888/49dff+WmOaZMAS+Qm6qqKvzyyy/8d8XFxdBqtQreZW4KoaGhcHJy4t93dXVxVNpgMKC1tVWyFzElSm5OF+d5+PDh6OnpkaCHANC/f38JEk5E3HzMDnRnZ2fs2rULAPgetGvXLn4PJtgzc5OIpBgMBgBQCCohISEgsmjz7777LjIyMlBQUMCFIj8/P/Tv3x8tLS1cgGJmLPnhw97XxsYGcXFxmD59OgYPHszn+O7du1i4cKGEl8S17u/vzz8bPXo0nj59yvdytuaSkpL4GqmpqUFSUpJk/nJycrBw4UKJ6V1co7GxsTAYDFi1ahU8PDwwf/58fk12djZyc3Mlikl5ebmCd9m4MqFm0KBBaGpq4opBSkoK5s6dy/cM8bdyc7XJZMLevXuxbt06LtAy9PbcuXPw8fGBu7s7jEYjP7OILCY/vV6vMK+yfcLX1xeOjo4cmcvIyOBCUUhICOLj4yVnHVtjctOZKCAYjUaYTCaJcDR48GCEh4dzBVi8VpxTNzc3VFZWor29na/RsrIyEFnM52yNJCQkKECMgIAAdHV1KdaHSGzs7O3tJSiUnZ2d5Hdi/0QSP2emQfF3np6equZANQHKyckJXl5e/Bxi93Z2duaClb29Pezs7CTjxpAseT9fpf1diBaDLh8+fMhRHZHkE2CNnj9/DiKyavrT6XQwm82orKzEd999pzCZlJeXY/Xq1YrDXiQbGxvMmzcPFy5c4Ju3yLjl5eVYsmSJxJ+KyOKDwfzJvv76a24aFEnchOQkMvmaNWv4u7DPRETKwcEBZrMZpaWlOHbsmGRTYXTo0CGJEKNGnZ2dOHLkCL7++msFDK7T6bBw4UIJ5LtmzRqUlpZyoWncuHF4+vQpH0+2GYSEhCjMUYxEnzudTodp06ZJvm9ra5P8nZWVhaKiIsyYMUMVUTty5AgmTJigujAYVVVV4cCBA7h27Ro/qEXfiLFjx2Lp0qX87xMnTsBgMODu3bv8vr/88otEcGSk5kPE5kj8myF71nxNkpKSUFhYiObmZty/f5/7QzEaNWoUFi1apGoGYBQSEoL169fj6tWr3J9PFGZqamqwfPly3ndbW1sUFxfj7t27XMN89OiRqu+iXFGxRgcPHgSRFJoXTTy+vr7Iz89HfX09Ll26pNC2fX19sWfPHoXJSE5TpkzBJ598gh9//FExJikpKVi2bBmmTp3KP1uwYAHefPNNTJ8+HUQWIfHHH3+U+JYRWd9biKRmrNzcXIk/DlsP4t/Z2dkoLy/H0qVLVU1Vhw8ffqmpprm5GUePHsWtW7fw7rvvguiFnyeRRUgUzX0HDx5ERkYG95lycnLCxYsXubLHDgl7e3tVhYDxkfj3O++8I/mbCayMkpOTkZOTg6amJuzbt48rY4wmTJiA4cOH8/1BjeLj47Fs2TKcPHmSo2ri+JaXl2Pq1Kn8AMvMzIRer8fWrVu5ILRhwwaFsM7u/Sq8y/ZYkXdFATYwMBDJyckwm83o6elBTk6O5PcajQYdHR0v9W9tampCT08PV1REv+GEhAS0tbVJzsSioiKUlpbydVtdXc39q8T7WttziaSIn06nU6BGIiJlY2ODiIgIaLVa5Ofnq659psz01c+EhASuoLF7sOfa29sjNTVVgpzl5OTA3d1d4jPl7u7e597+MrLmUyX21dbWFg4ODorxZOvHmklSvIeLiws8PDxUTZL29vYKVI3oP1jQEiV9OVTb09MDIstmsXjxYhBJUQvxeo1Gw7UXcYK7uroQGxuL9957DydOnOCLqqSkBO3t7ZLnhYaGYtu2bYqBkGs3KSkpWLhwoaWj9GJjbW5uliAokydPVt08Nm/eLPl71KhRSE1NRVZWFtatW6e4Xq699OvXD0TSBf/uu+/C09MTnZ2duHv3LhdI9Hq96uZy5MgRxWdM42DEhCgAHBb38fFBQ0MDdwhl3xO9EBTZxtDZ2SmR3CsrK3mggiigMVq2bJlkE1bzTcvIyEBYWBjy8vLw559/Yt26dRzBVDOXHjx4kPthWNOMRHOJOKeVlZWSezJzKUO/2PsFBwdLBERnZ2cuDM+cOZMjUKItX0Qf9Xo99/UTF1xzczMMBgMuXbqEw4cPc2S2qqpKsbkVFRVxU5G48OVj6O/vj9OnT0v6WVxcjAEDBkjmPz09nQvI4mEiFwgmTJgAf39/1NbW8j6LBwNbt3IeEw/jtWvXIjAwELNnz8bVq1e5kpKRkaFq1hDRRkYMdWE0ZswY6HQ6AOCHUkxMDPr37y9RsL7//nsQvdhX2IHD+JRReXk5Bg8eDBcXF4UJj0iJ3smdj4ksvlReXl4oLS3Fd999h9mzZ8PR0RF+fn4KpY/IYtIX+USNGG8D4Eg0e19RkXry5AmIiCNiTIhKTk6W8EhUVBRH6zo7OxXOvh4eHpL9Va2fjH+SkpJw/PhxrFixgiMccr9HIovyxIReUYgT9wJmTrly5YqEd5mwyv5mfmZMERF9RkUkmOiFG0d+fj5f5+L6kyOqbCzE96qvr0dQUBCGDh2K2bNn83FNTExUdfCW+1aqjWFaWhpyc3Nx48YNvpa1Wi1MJpNkruR+iuzd5cqdVqtFZGQkPDw8VIUiudKmZi5LT0+Hk5MT4uLi0NTUhKSkJNjb28PLy0uVB8rLy18qgLB36e7ulgAT0dHRkr2I+SDKFSY5SmVjY6NwfJd/39f7iGRra8utMuJn8uucnJz6RNzE53p5eUlMmba2tpJ7sndm/foPFbQmTZoErVaLuro6/Pzzz5yRACi0x3v37ql21ppG4urqquoQ6ebmhrFjxyo+Z/C8jY0NsrOzOQMBUGw4jNiiY8wg+nSdOHGCH6BVVVVITExEQkICfv/9d+6f0djYqEAI9u7da3UDs8a8cq2SUWNjo0LDXL9+vUSoKCkpga2tLS5duiRxYOzfv7/ifjqdDvb29njrrbf4ZwC4dqnValFQUID4+HicOXMG3333neQ6+f3U5oFIfUMmIg7Ny7ULo9Eo8W8isghKzAGYyHLIBAYGYuDAgdi4caPkt+LvmGaYnZ0tGY/169dLDsS2tjbodDqMHTuW++lZ6+fFixdV+yM37TLSarWqflxBQUGqvokHDx7kz21ra0NcXBycnJwkJjM5MadcxrsiivfWW29xXhs4cCA3Ofz5558csezp6VEIUtevX3+pw6qclixZovr54MGDFZtYb28vAPA1k5OTA1tbWzx69Egyj3LBi8iCfhJZHNnZZ99//z0XKBka0a9fP1y6dAnXrl0DkeWQl89pUlISN4HISc3/ROynXIvOyspSRJeNHTuWRy2yOQgICOC+bNbWCUOzdDqdhFfPnDmDiooKEFk2+Lq6OsTFxWHSpEn48MMP++RdpvDKyZqfTlpamgK1IrKggQw5U5tTNufR0dGIiorC/v37rfIMm7OKigoFUiMKMxUVFYiMjITRaERvby93hZg5c6ZC6LDmAN8XiYq1SGpC1siRI/H111+jqKgIISEhSEhIgL29PXbs2CFRTtRQbfauonlz1qxZ/HeRkZEIDw9HcHAwBgwYgJqaGhBZFBo5KpqYmKg6P0TWlVG2duQUGRmpMB+mpaVhwIAB3AVGp9PBzc0N+fn5EsFXbs5mvOni4sLBBCLLGcYiztk72tjYwNnZWaK8qp3R1hAra0KXNXTMxsZG9Teurq68H05OTlx4UkO/XvYsGxsbyfj/hwparMknmGlqdnZ23BlYq9XiypUriolWW7xEFqSCCTpyRigqKuIOoSITeXl58ZBj1h49eoTRo0cjKSkJkydPlmzozHdDDpOKyBuRZZMFAD8/PwlMS/TC94T59xQVFeGnn37i2hmRRegRmU0kk8lkNcKHmdWIiJtFGFVWVuLYsWO8n3/++ScWLFgAvV6P6upqSZoFIlIIMmr99PX1BQBMmTJF0c8bN26AyOIXcvr0ady5cwfr16+XOPUyh0e1jcrNzc2qA3FJSQnfUOS8lJKSgo0bN+Lu3bt4+vQpAODo0aMoKipCXl4eNzkyze5lDvaMmHYtHwPmP+Pi4oK2tjYAgNlsVqCILi4uqsIsEamaIYks5qmSkhKuAcrNGAsXLsSdO3d4Px8/foxhw4bBaDRyJEbUmIhIYR6T07Zt2wBAsXnodDr+Hvn5+QCAxMRE/PHHHxJtMysry6rikJeXp4pusnsyc43c7Nbe3o5z587h119/5fw7a9Ys6HQ6hT9nSkrKS7VO9p4AUF1drdi0GY/ExcXh3r172Lp1Kw4ePCgxX7FDUM28GBoaqvB9Y5Sbm8v5Xj5OOTk52L59O+7fv88DT7Zv346MjAyYzWZFmgxmEnwZnTt3DgcPHlSYMpgQ6uXlxf0qa2pqVCOCrQmaagg6kWWfys3NlexrIi/NmjUL58+fx1dffYUHDx7g9OnTqK6uRr9+/fhezQSLR48egahvcxiRRRj59NNPFbxrMpkQFRUFGxsbpKam4unTpwgNDVUIW6GhoVZNYGlpaaoIEfPDZCi3eF64uLigqqoKPT092LlzJw4fPoy3334btbW1iIiIQHZ2tsTKw9bXy9JXaLVadHV1ITg4WLEfMYSLBTskJyejrq5OYm2JioqyKmT4+PgoXGEYhYWFcRRPfj7FxMSgtLQUQ4YMQWdnJ0aOHImcnBwEBgYiPDycI81sbfblsiOSh4eHqiAjCnzOzs7w9vaGg4OD6tq3JmhZE8zk6JNIdnZ2cHZ2hqenJ7y8vODl5QVXV1fY2dlxvy85f7xKP1+lvbKgtXnzZmg0Gu5H5OrqCgD8oGAbiKi5ZWVlSZgJAHp7e7mpQL4pE0nhUQDcsVKN2AYCAN7e3jAYDJwJampqJILdb7/9pgh5ZoiDCN03NzcjIiJCkuoBsORGAl6Y5kR0Ljc3V+InwcYmIyMDABQ+SXJo1WQy4datWxJnU/nitLW1xblz59Db2wutVsvRjYaGBpjNZt7vmTNnKqLsmFO5v78/h/4dHR1RWVkpSZ0h9lOM/GTIh06nUziaA0BlZSXeeustAJAsIibAMXSLXa+GXrJFxeYCsEQCinMoRlnGxMQAgAQREX3nRC179uzZyMzM5GaHsrIyCW8zdIEJqDY2NsjKypKgkoAldxYLqlCLJBOFMQASNE5ObAwAwNPTUzKupaWlqKurk9xLfigyPyPRLD9lyhSEh4dLgiEA4I8//gAAbqYW11hubq4E/mepThjvyk3k8sNhwYIF+Pjjj61ubmx+AIuDeFJSEncmrq2tRU5ODhf2tm3bpjC/s+hirVbLFS2NRoP8/Hxs2LBB0s9vv/0Wz54947myxOgig8GgQKMAoKysDJcuXVLkq2NCrSikALDqh+rm5oa8vDy+t8XGxnJednJyQlpaGv87JSUFjx49kmzkDMUiehEsxOa3vLyco5oTJkzA559/jps3b+Lu3bt8vTEhwcPDQ7WfDQ0NPI+bWg5BkZ8BqEY7yvkcAEJCQiRCbHp6usSM+vjxY47aMGJKtZgSZNCgQQgNDZWggO+++y62b9+Od955h69tUbhJSEiQICU7duzA8uXLER8fDwAKEySRVOBbuHChxP9PTmwcHzx4gOTkZMTHx3NkOzk5WTLO3d3dCr9U5i8rCoB+fn4ICwuTIPCdnZ0YOHAgOjo6+B4kClfBwcEKQXXLli2Ijo7G8OHDFTkq2X4t7l8tLS1W0TFXV1cEBATAaDSipaUFQUFB/Dz38/NDQEAAF7qjo6MlexPRC/O2nZ2dBD21t7eXCE+urq7w8PDgZG3cRdJqtTzC0Nr5KD7Dy8urT2WNfRcUFMT9usTvxb89PDys7mvys+Fl7ZUFLXYwLFmyhOelEYktEAAKO67ai8r9rogsmxpzoDQYDJy52GIAwKM02GIWha2XTRobNGZOu3nzJmxsbCQO5Mypu6mpCb/++qtCQ2TmvUePHmHq1Kl8E7H2fDXzhKenJ9577z2+aTMhiS1iPjlEPApIvnhfhdhiYchPZmYm35DFYIarV6+qmjRramqQkJCAH3/8kftJiCaol9n2iSwRhCx1gLhAmTDK+tnT08Pnh72jvO99kZ2dHfr378+FXmbq3Lp1KxcsNm/erOogzoRNAIpNRI3UIp06Ojp4XirRTDF48GAeNcg2XDbfTGh8Fd5lmiHzSTp06BB8fHz4ujSZTHyddHV14dGjR4oNmI0DAAwfPpxr0Go53YjUzcIajQanT5/mfzN/NGb+EueUKVJMGJD7PPZF8nxpogM74ws3Nzdcu3aN+5yJZDQaMXr0aJw8eZLPl6iAvMy5lsiCeDLkSO5PI/ZTRFeYYGcNvbdGCxYsgFarhdFo5HyxcuVKfrisXLlSNTqU8TmAV8qHpIb6jB8/ns+pKPBlZWXx/GVElv2EHdbyBLCvQmxdTJ48GeHh4XzvbG5u5v/v378/li1bpkDT2Pjfu3ePJ2dVmxdGakh7cnIyN6/a2dnx84X1GQDefPNNvp4YvxORJMjmZcQE6g0bNsDJyUmClDE+DgkJQXNzswJps7Ozg4eHB0pLS5Gbm8ufL/qgioKEWm4olruL9YH50zGe7+3t5eMgF7oZ4vP38G5JSQlcXFwQGhrKz3tR6XVxcVFFtlg/vLy8rEYbvowCAwMV/WPk7u7OhXq1yMZXSZ6r9s42Njb8Hg4ODq8kP72yoMU0arkmz6RZ8dAQpVp5pmlxYv39/XH9+nUA4BAzWzhswmbPns19E8SN/88//5Tca/HixTxPj9hY0sm8vDzY29tj+/bt8PX15QfEqFGjAAAPHjzAvn37uAYt5vwhUsKlwIv8PERSm3x8fLzC1+fkyZP8ndi9RI0oLy+P+5qIG7eYKZ6RmMeLtbVr1/KDtLi4GCNGjEBaWppi/AFg+fLl2L9/P+zs7BTmqISEBG5SqaiowNixYyXPlx++8nebOHEifw4TppmvEOMVFnUlJ/m9WIoLMSP25s2b+Tj19PQgMjISXV1dEtMza4sXL8Znn33GeVMeXcRQSWaqBaCI1BLvKSoNBoMB33//PQDwfE+sv2zzZHnE5Ita3k8xdYjYzp49C6IXAsvu3bvh6enJD7uLFy8CAK5fv46PPvqI84U8ApSNu+jLKCoQoql58uTJEr52cXHB559/zt9JnpCR8T4zN7P1wz5nub2ILKH0zP9JbCLyZzAYMGvWLERHR0sSFLK2Z88erhTI/VH0ej0/eJKTk/Hxxx9LxloUItTmYe3atfw5TKFhvBESEgI3NzeOlMr9BeX3YvP+wQcf8HvOmjWLj+2YMWOQkpKCpqYmid8Paz09PTh79iznWTkKzniRpb8Rny/3vQMgEVyqqqp4klY29swEzvaQOXPmKKIx1fo5ffp0SSUIAPjggw84KsfSOMyaNQseHh4cEf3zzz8BgJt2GRokF5DYXiQio6JwJe5fy5cvl+zDBoMB7777Lu7fv48vvvhCkv+KjVFDQwMXOuQok4jE5eTkSFBoALhz544ElfPx8UF7e7tkjbL9+8iRI5g2bRpHA9USRrP/u7u7Y8KECRJLi4g6Ozk5SdZvQkICpk6dip07d+L48eMoKCiAn58f5x0fHx+EhYVxZVmOeMoTgrP9q6OjAz/88AO2b98Oo9HI9xGtVsvXp2jBuHXrFt58803k5eW9UgYCe3t72NraSqJv5VRZWYmQkBDY29sjLi6Op7bp7u5GbW0t4uLikJSUBDs7O5453hpaJq4zIilQEBkZybPLMx615rAfGBjITb8iYNBXe2VBS3RMlad2EB2p4+PjrWoZRIRvvvkGU6ZM4f5cbOH+9NNPkozdqampCAgIkJhGli1bxn0b4uLiuHag1+sxYsQIvnjYps+IMWxvb68CVhTRh4yMDBw9epQLcOzdmNa2ZcsWyW/VTGDMObCyspKnZhD7eeXKFY4IskWflpYmWdRigkoiKYo1ePBg/gz5pkdkOWTj4uIUflqiOYLIot0zZ1QWmchSF4wYMUJi7lTTDkWNHQBGjhzJS6FERkZi9uzZ/P3YwdjU1AS9Xi85CMQ+tLS0cMSwvr6eo5VqTr7sd3I0Qx5QcOPGDYkPW1FRET9MfHx88OzZM/5dSkqKQvCUH4IzZszAmDFjJHPK/mVjUl9fj4CAAIkp4fDhw9wcYzKZOI/l5ORIknAyn0f5vMmz1BNJMyV3d3fj3Llz3MTE3okpSOJ82tjYSKJRGbFDbdWqVTh48CDa29sl/Xz48CEvq8N8JwwGg+RwGzx4ML/ezs5Ogv61tbVxh1Q13s3KykJdXZ3CQV6+31y+fJmbhZjgw1BbeYJgudlKXE8uLi747bffMHnyZEk/mXAkPru2tlZicpU73otmqsrKSq5sig7sjJiiIDdbyf1rrl27pkj5wnjFYDBIstGr+SCJQRqAJQfdmjVrAAAffvghSkpKeB/Ywd3R0YHY2FjJmj969Ci/f05ODj9wMzMzJfun3LTDfN22bt3apzln3bp1mD9/Pkc9mL8fU3rElDihoaGK/cjGxoav271792L58uWKckUff/wxhg8fDl9fX9jY2ECv1yMpKUkizDQ0NHBrh4+Pj8QdxGw2c1Ov3CeW8XpDQ4Ni/xHf1d/fHyNGjODrlu0PoklZ9JdVCx7Lzs6Gt7c3SktLsXLlSnR2duLw4cP4/vvvERcXh6VLl/KI/KamJtjb2yM7O1vCC+Hh4RJ3DlGI02g0fH9m/ogimc1m2NjYKIQ1ueJRVFSkEFDY/LJ8VOL8idexlCUJCQkwmUwYN24chg0bhs7OTsycORODBg1Cc3Mzhg8fDqPRiNLSUgQHByMpKUny/kTSyEzRDCg6zav5gbG5fpmzPMtB+bL2yoIWkbSmINsUPv30U47Q+Pr64saNGwCgOBzj4uJQU1MDwIJqvaxkhcigIiQ4cuRIvgCsma68vb0lCMfHH3+M1tZW5Ofno6OjQ3WTd3Jy4puq6N/D+hESEoILFy7wz3fu3AnAUgNRDrUWFBTg+PHjOHDgAPbs2aNYMPL0DKw/os+Gj48PD/VWc26X/5YRACQlJaG1tVW1n0QWxCs1NRWurq5c8xBrXom/YxnvASgQhJEjR3KflKFDhyoyLrPNlR2uauH24vPUMjyLG4D8N1FRUaivr8fDhw8VC5/IsqEwVIj5/jg4OEiS0bJ3jImJ4Vq2vB9MQwQs2rRaFnVxwVrrJ0N75JqVSO7u7oqIlry8PFRWVqK7u1t1Tr28vHilA3FdMZSioqJCYvJlSI9aPrOioiIAlrqU165dUzh9y53P2ToT11t8fDx/H2vO9SJfsLm6dOkSzGazxFwlpxEjRsDBwQFZWVkc5RG1e/F3U6dOBWDJni/nD1adAQBWr16tSKDJDiaGRKr5k4rPU0vMzEiORgGAXq9HU1OT1X6y3FJEL9wytFqtoo4okQXtYb6VcgF65MiRSElJAQAMGTIE586dUw2qYPuIiGaKxMyWfTlCy/33ACAhIQHFxcWYM2eOBN1k5O/vz/dbkUfZvtTa2sqRNScnJ541f/bs2ZLnGY1GrtQypYNFurJrYmNj+R6r1+s5KiiaU9PS0rgg3pdpSdyry8vLMWLECBiNRlRUVKjOqY2NDTeHJyYm8r1CtP6I0acFBQVYs2YNRo8eLSkFFB4ejvb2dsyYMQNbt27F7Nmz0dbWJpmX8vJyeHp68nOmtrYW7u7uErTewcGBKyRqpW0YydGhSZMmITQ0FHq9XhG0Jb4jA1vY+IvCjCgI2dnZITw8HKmpqejXrx8vG8bqIzc0NKCrqwvNzc1obW1FcXExkpKSEB4eDpPJhLS0NGRnZ8PV1RUVFRUKRZ5IWhPRWj/V+JJFF4oZ/eX0H45oiYtHXriYQY9M+hOvZwfxnTt3FPXwkpOTFRo8I5YpWYSvRYaQC1nywRUl+MDAQOj1ekV4K5HUD0hu3rG2CYrXyP/+9ddfMWvWLJw4cUIiLKrl3WIL9u7du9xhnTGGuABF5EzeB7FUEFu4age5PAM+QxoWLFgg6Qc7UMQsx1FRUQpHb8BSgHbAgAE8AS2RBXKXRzGKi1Q+ZuI8WUt9oUYFBQWqB4Y8UkT0awHAgwjYb5njuFoNTlaL7csvv0RdXZ3ku7y8PNX0DezdAEiED/FdX1bQXNz4ExISkJCQoDjEiKRojejDBOCl5VTUeBcAj7plpo3g4GBeLF1OCQkJACAxs7u7u0sUC1HAkWuO8txT6enpqg6v8vp67OA5e/aspB9M+ROFnsmTJ0uUEdbOnDmDlStX4ssvv+TfLVu2TJV3PTw8sHfvXsUaEhUPa6kViJRRXoWFhaq8KxfWmD+el5cXAHC/F7ZmWL44doCIQRC3b9/mvDtixAjJOA0dOpSvAznJryWSIivWAlnEsWL/T0tLQ2xsrKpCLO5RYlqDa9euKQ5wuWlJRKFZZYArV65g3rx5ePLkCUf8KyoqsHbtWlWBqb6+Hr///rsEdWZpF6zxq0ii762TkxN0Op2qb5M8ZQM7pzo6OiRINtu3RLN8Z2enxMT4ww8/YPfu3Zg/fz7mz5/PXQQSEhIwefJkrkCKLg75+flYvHixRHn09/eXKEBqSqq1sddoNKo5J+V7GhP85OVxGK+K51hwcDByc3MRGRmJwMBADBs2DOPGjUN7ezuampowYsQIZGVlwWg0orKyEkVFRTAajUhLS+NCV2ZmJhoaGjB8+HAJcCEvTN0X74pkZ2f3Svm8/sN9tNiNjx8/js8++6xPad9aOLF4H7mkLGcGdj0TCI4cOSJhzL40ZSKLCe+9996DjY0N1/bYd1qtVrJQ1LS4ZcuWAUCf6IM1O/SVK1ckzv5ynxb5Rnbw4EEu8C1dulRSoLgv+zWjTz/9FK2trVy6Fs0YoklB/ly2QABYzYdFpIweYSTm1SFS12jFvwcOHIg7d+6AyLI5yc1hbHFaG/M9e/ZIyvGI9xcPU5bcUk5Xr15VRLXJyZrZW3yW/P3u3bsn4ce4uDgA4D4SJ0+elOThYu9qLedbRUUFf0+GsrHvNBoNF+Dc3d1VUUCWZ66vPFnWqg0AL3xgxMLxRKRaDuf+/fs8QOXgwYOS8X2Vou8ffvgh8vPzOZImfic3fcl/y9BWMZePnNSyZxNZhJTHjx9LxrUv3l22bBlPWZOfny/hXR8fnz4z/bOxYY7Wct4VeUetn0SW/FrW0jEwkie0JCIFOig3MQKQaPpsTNneduDAAYl/F+NdtXQ8RBZhUUygLD5bFGIKCgpUizZPmjQJW7ZsUVUsGLW2tqrO6Y8//oisrCy4uroiLy9PYs4aO3asRBDV6XT46quvuDl/06ZNklQtfaE8RJa9bvLkyQgLC1O1Hoj8JPeBIrIgcGPHju2zrl9+fj50Oh2CgoIk91u9ejUWLlwIrVaL7Oxsvne7uroiLS2N+zuzz2bPno25c+fC19cXzc3NksjOl0XpEVmUcjaWmzZtkij34h4j5qYUyc3Nzaqzu7OzM/R6PbKzs2EwGJCUlMSRuJaWFowaNQpmsxm5ubkoLi6G0WjklWNGjx6NtLQ09OvXDwaDAY2NjRg5ciTy8/ORmJiI2tral8oJIrHoRiKLMGqNB+To1qs0W/o7Wm9vL/3zP/8zJSYm0tOnT4mISKPRSK6prKykd955h8rLy1XvsWLFCiIievLkCUVFRVFlZSUREf3xxx+0f/9+ioiIoNTUVFq6dCnNnz+ffHx8iIjIbDbT8uXLaffu3ZSbm0tOTk6k1+spKytLcn+TyURERPn5+fTw4UMCQF5eXkREFBoaSkRE9+/fJx8fH7KsDaL6+nrJPZYvX07Lli0jGxsb+v777/nnBQUF/P8RERF0+PBh2rdvn6KP165do46ODv63m5sbdXd3ExFRZ2cnbdq0iYiIysrKaPDgwXT79m26e/cuERH967/+K5nNZnr48CEZjUZKT0+niIgI3i95P4mI+vXrR+vXr6cJEybwd2Pt888/5/20sbGR3KOwsJCioqLIxsaGDh48SE5OTkREdPDgQcl1fn5+1L9/f0U/T506xZ9PRPTrr79SQ0MDERElJCTQsWPHiIioqamJ0tPTqbCwkDZu3EhERKmpqTRkyBB66623qKSkhJqbm+nu3buUnp5OHh4e/BnOzs78/xUVFTRw4EAKCgoiIqJ///d/59/dunWLHj16RAMGDKCAgADFuy5ZsoT69etHpaWl/LPGxkbJNUVFRXTixAnOc6yFhIQQEdHo0aOJiOj7778ng8FABoOBiIiOHz9Of/75J5lMJsrIyKBZs2bRihUryM/Pj4iIvL296d///d9p7dq1VFlZSc+fPyej0Ujx8fGS5+h0OiIiGjhwIL3zzjtERGRnZye55v79+5SXl0cA6G9/+xstWbJE8n1vby+NHTuWbGxs6Mcff+Sfe3t78/9nZGTQW2+9Re+//75inL766it64403iIjop59+Ij8/P+rp6SEiC2+uWbOGnJycqLa2lubNm0cnTpyg3377jYgs66O0tJQePnxIZrOZ/st/+S+k0+kUvJuZmUlERJGRkZSamkrvv/8+NTU1ERFx/iEiunr1qlXebWpqort375KNjQ1dvHiR8+6iRYsk1z158oTmzZun6Of58+fJw8ODUlNTiYjozz//pGnTphERUUdHB3355ZdEZFmvra2tFBsbSx988AHvZ0FBAV25coXy8/OpoqKCnj9/TiaTiTw9PfkzRD4qKCigzs5Ovl+dPn2af3fnzh0CQDExMYp+enl5UXt7O/3X//pfadasWfxz9q6slZeX0/PnzxX9ZHPD9p+vv/6aGhsbSa/Xk7e3Nx07doySkpJo6NChVF9fT//jf/wP2rJlC1+D9+7do5ycHJo2bRrV1tbSb7/9Rkajka8J1hh/dXR00KJFizjvP3nyhF/zww8/UGdnJ/X29tJ7771Hly9fltyjo6ODli5dSv/yL/9Cv/76q6IvRBb+OHz4MM2dO1fyeWBgID18+JD+9V//lZ49e0Z37tyhoKAgmjNnDhERabVaOnToEJWUlFBHRweNGTOGTp06RQ4ODkRElJ2dTbNnz6atW7eS2WymkJAQ0mq1fE2yptVqiYgoLi6O/vKXv9C9e/coKSmJPvnkE4qNjeXXPX78mPPQ2LFjJffo168f3bp1i+bNmycZH/Es0+v19PPPP1NFRQV9++239PXXX/PvHjx4QGFhYfQP//AP9PDhQ7KxsaFx48bRs2fPKCMjgzZt2kSZmZk0dOhQamtrI3t7e/rhhx/ojz/+oOjoaNq2bRvNnj2bkpKSKCYmhry8vPjZyJqLiwv/f25uLp0/f5769etH9+7dk7zz3/72N+rq6iIiotmzZyvmy9bWln7++Wd69uwZ/8zf359cXV3Jx8eHYmJiKCoqitzc3Oj58+f0+PFjcnFxobi4OHr69Ck9f/6c/P396c8//6Rnz55RREQERUREkLu7O/30008UGBhI8fHx9I//+I/k4eFBf/vb3+iPP/4gZ2dn+vXXX+mvf/0rpaWlUWhoKDk6OirWl9icnJzo6dOnZGNjQwDo8ePHiu/t7e3p999/t3oPq+2VxLH/K62LZW+YhCrC58CLEHbR/MFyn/zwww9WJcxp06YhPj5egsaIGjHLgfXTTz9h+PDhmDJlikTTEmF9g8GAsWPHcjifvb+odcjRBKPRyB19Fy1aJHFqZ9cwDfvbb7/l5hIRGk5OToa3tzeSkpIkGdlFOn/+PBwdHdHS0qKqtbHcQADw3nvvoV+/fhLzKtOKiSx+RXq9njuxd3V1AbAkUpX7gTFqaGhAUVERRo8ejbCwMO43IGqXLOCA+TwQScOIvb29uWnTmga+YsUKBAcHo6WlRaIBMYSOadGLFi3CtWvXEBgYKMldFhAQIIkCio+Pl2RPZ8/97bffVJ9fVlYGd3d3HqghRsMxYsEAtra2ANQjDpnpGoCqFh8ZGYnOzk4UFhZKeEEcT2Z2/fnnn9Hc3KzIryX6fLGCuozv1HhX7msQGxvLx27z5s0cXRE1eqa1AuCh4mK2/5ycHHh6emLu3LlWQ9kvXLgAvV4vyegtohQs0SsA7N27F7W1tRJkV0S7WHb369evg8iCCvb29vLyRGpUWFiI9vZ2ZGVlwWQycd9FEclha76np4ffW4w2NZvNHCW3xruffPIJ90FRi/xiWcsvXryIL774AjExMdwnjcgSVCM60TL3CTnvsoS5cqqqqkJQUBAPwlEz87F+sqLNao7TLEIceGF2FOeLmWjGjRsnMf+KZujk5GTExcXh2rVrKC4uVqSYEKsFxMXFYd26ddzcpca7Irm5uSEoKIijg8OHD+eIAtufw8LC4ODggKCgIDx//pyfD+KYtLa2IiUlBUePHuXmNIaO29nZITo6Grt378aAAQMwbdo0BAUFwc7ODkVFRfycYv04deoUxo8fzwtis2cw1JbIYpoTy0598cUXiI2N7ZN34+LieDLr2NhYjoqLSFVNTQ28vb3R3d2N6dOnIyYmRhLl379/fzQ0NKCqqgpbtmxBTEwMtFotdDod/3ft2rWorq7G2LFjedkhs9nM9zEWedvd3Y3hw4dDo9FIXFxES4+trS28vb35WEdFRWHNmjVwdHS0Wl9To9HA19eX85CaX1R2djbi4uJQWFiIjo4OFBYWorCwEOnp6YiOjkZBQQGqqqpQVFSEsWPHoqysDGlpaSgsLITZbEZBQQGam5vR0NCApqYm1NfX8/2gqKgICQkJiIyMhEajQUZGBsrLy6HRaBRuCXKUTTyPGV/0VdeT6D/BR2vbtm3cDltfX89fhJnz5PmmPv/8c870LNkmAMyfP5+HvrK8Nvb29pg3bx7ee+89iY/E48ePeV4tRqKJSzRR9vT0KN4hKCgIXV1dPApR3PDUIunYxsWcIaOjo7nfUFBQEEpLSxEfHy8RIvfv3y/ZSFauXIk7d+7g6NGj/LB99913uYAxYsQInD17VuJ8XllZiZ9++knxLsw3pqenh9uXKysrJUIDO0A7OzuxZs0aHsnE/FqslWTw8fGRONmLKReYICAexNXV1ZJ+zpo1C4cPHwYAHnlUXl7OfR4qKirw4YcfKvx7ACggWfauhw8flvizqOVHampq4lF/7B2tldcgsghsmZmZfM7EiDTmtC7PMv3w4UNuPjMajXjvvfcAAKNHj+YHFTsENRoNduzYgVOnTin6KTeNsmcnJSVJhIPe3l5FskOtVotx48bxfjITL5F6JIyLiwvS0tJ45GNKSgqP1jKZTEhPT0dzc7NEmGSpVUQeAIATJ05wU+f169f5BrVw4ULcuHFDIlCuW7eOB20w8vX15Wau7u5uzrvjxo1TFXgnTJiAY8eO8SzYzPRrLct1XFwcX78eHh68pqKdnR2WLl0KHx8fiUm1p6dH0s+3334b3377LQCL83h4eDjmz5+P5uZmODs7Y+LEibh//74ie7uawHD79m2+DzABV6PRSHwuGY0ePRrd3d38N6LSZI13MzIy+AEgmnzY2ti8eTM/zFjmdMbrkydP5qk5Fi1aBLPZDJPJhHfffRdarRb19fX48MMPcebMGQlPAVAI22yttLe3Sxyv586dq3CQz8zMxPDhw/HgwQMQWQS3vvwvXV1dodfr+X2MRiM3XQ4YMABmsxlTp07l0aje3t44d+4cF24NBgPee+89PHnyBHv27EFTUxP69euH8+fP8wjftWvX4sSJExI/uAMHDiiqQPTr148r2SUlJfwwbm5uVk1SXF9fj56eHp7ugIER1gSRgIAAHgTi7e3NldDU1FQMGzYM5eXlaGpqQnR0NGJjYzFnzhzOh2FhYVi4cCG2bduG3bt3Y/jw4SguLuZ1WnNzc9HV1YWNGzdi+vTpiI2Nha+vL8rKyniKHXHMWbRwYWEhN2GGhYWp+pCmpqZK6lX25WpCZDnzRfOum5sbTyrev39/FBQUoKGhAfn5+cjOzkZtbS03+5lMJrS2tqKzsxNdXV1oa2tDfX09Ghsb0dHRgYaGBrS0tGD48OE8yavJZILZbMbYsWMl7+bt7c191lxdXSXvpObgbm9vDwcHB24SZYWq++rrf6igBYCjA9nZ2VyIAoCbN29KNqGGhgZJSG5GRgaXTIks/lg5OTkSZ96ioiJFck92T3FAxM03NjYWtra26OzsxJw5cxS12FgINpNKZ8+ezQ8Aa4IW81FgfzPEhEW7iAKRwWDAggUL+MEzadIkREREcLt/aWkpmpqaFGHccuHxyJEjyM7Olvi9yZ1r09LSkJmZiTlz5qg61q9fvx4//PADiCwbPQvfZUKtGrOcPn2aC5UsEzyLrhPD9IksmzYbO9bfDRs28E191qxZkjB/IpL4CrAN9N69e4rPxL+HDh0KZ2dnzJ07FytXrlQsBrk/xPr161XnUXS2XL58ORfqR4wYwTdbdtCK9xs5cqQk+slgMGDmzJl88W7duhVVVVUSgUWt7InaoSz2laF1c+bMwcqVKxVRb/369ZPco7e3lycgteb7NGzYMP4be3t7jpRdvnz5xYL/v9cOGDAAvb29HNEcP348SkpKMH36dHh6emL69Ono6OiQVFMICQlR5EB7+vQpIiMjJY7A8vFITk5GS0sLVq5cqUiRQmQpM8PQ5JKSEu4rYy3CzcvLC59//jkfT7ZGCwsLAVhK3zCELzQ0FBs2bOBzumLFCmi1Whw4cABmsxlmsxkrVqxQvLM4Vvb29ujs7FT498mLow8cOBBxcXFYvHixqpIwfvx4yX2tCVoierx8+XK+F8ydO5eXSQIsKVXE+/X09HBUxWAwoKioCPv370dHRwc0Gg3ef/99hZ+XGPTi6urKKy7I30lEXvr37w9nZ2dMnToVc+bMUSg62dnZknssWrRItV6eSM3NzRzhi4mJwfz586HRaHD37l0cPXpUcr/u7m709vZyx/yuri5MnToVs2bNgtlsxoYNGzBhwgTJGdTU1CTxwdLr9bh9+zb8/f0lGdNF5d3T0xORkZGorKzEsGHDFP6QLAqS7Q3FxcX8XLFWLsvd3R2tra3w8vKCjY0Nuru7odVqMWTIEFy+fBkrVqzAqFGj4OTkhObmZsyZM4fvDdOmTUNDQwPmzJmD/v37o6OjAxMnTuQCuJubG3Jycng1FVdXV2RlZWHixIlobGyUoDNyX724uDiEh4cjOztbkW+QyKKsMd5xcHBQ7NtqJEbrGQwGpKWlobS0FN3d3RgwYAD3sSorK0NHRweGDh0Ks9mMuro63r+mpibU1tZiyJAhaG1thclkQlxcHIqLizF8+HCYTCakpKSgoqICLS0tGDNmjMI3VRSu2Dno5OSk6mduZ2cn+f2rONC/SntlQUt0fGbQMKs59tVXX3FNmmmPIozq5eUlETbUFjLL2dPV1QWDwQB7e3tF1mHmJC6alJgTLQDJwW5vb4+SkhKkp6cjLS1NkUFXzbTm5+eH2NhY/n4Mpk5LS8OGDRsAWMxyzs7O/MAXI+5YmDLbgNW0Wtb/sWPHoqWlBZ6enopD9vbt25w5xAAAycQJ11dUVMBsNsNoNFo1GYqUlpbG83+cOnWKozf9+vXjyFV+fj7S09Oh0WgUz2S5gcTP5Joh+37GjBmYOXMmQkNDkZiYKBGm9Xo9R07EuWYo4fPnzyVwbmZmJgoKCpCcnKyaN0hObNNj0Y6+vr48jw7L4zZ8+HBu2mNJZdmhy7RS8d3UePfo0aP45ZdfMG/ePBiNRnh4eEjMoEQvUDMRUWO5fp4/f84jyNh6KS4uRnp6Okwmk8JZVR5Zyf5m5YGIXmThr6iowJkzZ/D555+jtbUVfn5+WLRoEQBLaR52j0WLFsHX15eboOQCsdj/uXPnYtCgQQgPD5eUwSF6YcL38fHh+wRTeNR4t7y8HGazGenp6a/kPB8VFcWVgWnTpnFlrby8nAsyHR0dKC0tRWNjo+KZTFFiZuikpCTVCgSsnwsXLkRiYqIiurezs5MfpmI6mGvXrqn2My8vDzk5OTAaja90SLF1wRQ8FjWdmJiIIUOG8L1ywoQJSEpKws8//wwAnL+YUCHWA1XjXQC4cOECli5dipycHMTHxysQXrYHifsUyz12584dSYJcVpLHYDBIyuVYI2aOZ7zr4eGB7OxsuLm5obW1FefPn8eePXswefJkmM1mbN26FQD4+nJzc0NnZyeGDRvGkQumcIrUv39/AMCcOXMwaNAgpKWlKZRZplTIo37//PNPfPDBBwpBOykpCfHx8aoRh2omM3t7e3h6emL37t2Ii4tDdnY2vLy8UF9fj9GjR+Pdd9/FyJEj0dLSggkTJuDXX3/FyZMn+fusXLkSGo2GC5DDhg3jSqOrqyvCw8Oh1+vx5MkTTJw4EePGjUNlZaVCISgoKOBChoheDRs2DPPnz5ckYSV6kcwzKCjIahkfkZhVysfHB/7+/tBoNLxucWVlJcaPH4/+/fujsbERAwYMwNy5c7FmzRo0NDTAYDCgvr4emZmZ6OjogF6vR0FBAdrb25GUlISEhAR+DsyYMQPjxo1DZ2cnBg8ejLa2NkWQD9s7RYXd2dkZQUFB8PT0VC0YLf77KvQq7e9CtNiNmWQrCgHTp0/n11jL+8SYgnV60KBBkvuyCXr+/DkAqellx44dVlEoZoZhiJvoY6A2KOz/8nIUKSkpEoh7xYoVfPGyd9fpdLh+/bokB5UaiYJdZGQkfy7TKtra2pCZmakY2zlz5kj8AdTCxpnAKQ8dFknc/ERfAyKL1lZXV8fnyWw2cy1GfJfdu3dj7dq1fZYrYUgEo1u3bqFfv36cUZmwyZqYK0YURJn5h1G/fv1gNpuh1+tfmjSObSTyCM2KigrExMRIfKKYyVYsB8MEDyJppJtITKBgQs3MmTMlqQHE8QMgKWuzfft2q76JDKFlgpZaySY13pWn+YiNjZUkFV63bh00Gg1CQ0O5L+OAAQP4PdSSEaqRGA3INL26ujosX74cgFRY27ZtmwQxEU0tzOTKkNO+ni8eaPLxMJlMGDZsGOfpzs5Ozsds/Ws0Gnz55ZeYP3++anZzRnLzGGCJMmb7AuMR1th6NxqNkoSPYpQwe2cm8Lws6ontB3ItvLi4GDqdTpLqgO23TMiwtbXFgQMH8Pz5c4SGhqpGHRK9QNBZv9555x28/fbb/JliGR9AWt9y/vz5XNmRC06FhYVwdnbmaSj6KqNiLdrXyckJgYGBkj1v8uTJSE5ORnNzMxcI5s+fjydPniAhIUFRtYMRU/aZ6Y/lSfPw8IDRaERSUhIqKiq4K4CIznZ1dUn2czGKlCmvzD+tL0FZRMPl/j2RkZHIyMjg59zAgQNRUlKChIQEPs9NTU3YsWMHxo4di/b2dv4eckFu4MCBiIyMRFBQEEpKSnDo0CHuOhAXF4fJkyejuroa33zzDd5//31udu3Xr59ESWX+hozc3d25u4laUXFGYuS1/HwIDQ2Fk5MT/zwxMRElJSUwm81obm5GfHw8Kioq0NnZiVmzZnETKEtY7uXlhcDAQPj7+2Pw4MFIT09HXl4esrOzMWzYMAwaNAjV1dWoqKjA8OHDUVtbizVr1mDNmjUSoVJU0uXKKjufXqX0z/9XTYeihr5//36FM3lLSwu++eYbEL1wHExISEBvby8MBgPmzp2Lbdu28U2boQXiBsZC/0W6ceMGVq1ahdbWVrS0tGDkyJFWD0OR+hKCxANLTsOGDeNM9OTJE4U/kdlsxokTJ0D0oliyyWSCu7s7vy8A/P7774rCk0yI69+/P3dgZ7Rlyxbs3r0bHR0dGDJkCCorK/vMzcOor2vEfjKYXRRWmdCzdOlSFBYWSg5vrVaLnp4eDB8+XDWclf0r93/p6urih6k4JmxxV1VV4eOPP8b06dMxZMgQ1NTUKFARNeor99TkyZMVmowobIsCyKZNmyQOwT4+Ppg0aRIP9Wf30Wg0+Prrr0FE+Prrr3lGbSJpKRu2EbLvmIkkMDAQDx8+xOrVq9He3o7W1lZMmTLFakoHkeR5lxi5uLj0ybsbNmzgGufjx48Vh3xHRwf3m2Emo4KCAphMJvz555+8H+wZTKgEwAXqxYsXY8eOHZL7XrhwAbt27cLQoUPR0dGBxsZGiX+fNbJ2YMp5l/EOG9vw8HB+qJ48eRJ5eXmSg7GiogInT55EQ0MDR7w8PDx4ziV2/48//hhnzpzhvztz5gyf87S0NH4tO2i7urpw8uRJdHV18TUqR3/UqK9kpmr1C8WqC8y8mJaWhtmzZ0sO+by8PGzevJmvNzZOzEGe9VO0RqSlpfEchYwX2XfMbJaamooLFy6gu7sbTU1N/FB8WT/7Io1GY7WmJpGlIgLj3X379qGgoIAfjiaTCQsXLuQoK/P7rKqqwtixY7F69WoMGjQI58+f531hVg8APN3E9u3bJVU2goKCsGPHDowfPx7FxcXIz89HTk7OKyFxYroWOV29etXqd/7+/lzgffPNNzmiy+ams7MTW7ZsQVNTE89pmJ+fD6PRyBOGnzx5EuvXr0dHRwd0Oh369euHzZs385JlLS0tHIU3mUzw9fXFqFGjuNCSmJiI6Ojol+bzI3ohtKqRiPIygVg8L52dnWFjY4P09HRUVFSgsLAQRqMRxcXFPOnq7NmzUVNTg7S0NMTFxcFkMmHEiBHQarWYOHEi2traMGbMGBgMBu6KM3bsWBQVFaGqqgrd3d2Ij49HQ0MD90Pt378/NBoNnJycYG9v/0p1TfsiGxubPoWtV2mvLGixA0hcoCzTu8FgUGxiACQSvTWB4PTp0ygtLeWmJ/nhoFYEVUy2KSIkRNYLEbN36+7uRmpqqmoB1oiICEWRamaXB8DzsDDzA3MmFvOzMD8K+b3ZImH3JVI6qrNxZWOQn5/PPxML+RIR3zzklJ+fj6FDhyIxMRHFxcUSnzY2H9XV1RIBYeLEiRyJ4IxBloP5ww8/5CbiH3/8kd9r3LhxitIoRJbNz2w2S/op939R81+QR9mJY2QNrhbnyGw2K6I4WR045kMCgB9GTKv19fVFVFSUgnfF+1iLJtqzZw/efPNNbnJgfWAInBxJJHrhRHrp0iWFL8TLso/PmTMHKSkpCo06MjISDg4OHCVi14trduvWrQgKClL0U0T2NmzYoLqxPnz4EMHBwZJxEddpYGAgV07YRitu4nKfQmvFpefOnQudTofS0lKYTCZVBDszM5MnWAQshZTludW8vLywb98+bNy4kSsCojlp3bp1qry7aNEiSdABkTI3EDOHMo04JCSEz4c8KKAvAYsVIG9oaFA9tFm0ZlJSEnJycgCAI9isRUdHc/MoM6eK715UVKSKJAUGBmLVqlU4e/Ysd49g5iJmvlUTNhhvXL58WWLmdXV1tWptEPeShIQERQ46NnZirc+UlBSUlZUhLy8PADBz5kxu6nd2dsbTp0/xyy+/SCJa16xZA5PJpEg4e/LkSdTW1vKs9AaDAXFxcZxPU1NTFcgN2xu1Wq3C6Vu04ojEIsNZIWh5LUPGK+y5V65cQXZ2Nlc2bty4gffeew/V1dXYsmULRowYgSVLluD999+X+PJNnDgRI0aMUJyT3d3dKC4uxieffIKQkBBERUVhwIAB0Ol0SEhIQGJiIt+PmALl4eHBhVl5EXRrApa9vT3MZjPs7OwQFRWlmvOQ9d3Ozg4mkwnNzc0oLy9HRkYGJk6ciMWLF6OzsxPTp09Hd3c3ampq0NXVhY6ODoSFhcHe3h51dXUYNGgQR8JCQ0Oh1WpRWlrK/bsqKipgMplQV1eHrKwsVFdXIycnB7m5uYr3Yuv17ymYza61hnoxs+OrtL8rvUNXVxdKSkq4RgRYQuLFel7yTOPiglu6dCkAcGc9RkOGDMG4ceMkmrL4XDHaRu2+4uD9+OOPHIZ99OgRSkpKuO+NvIYaY4pvv/0Wc+fOxTfffIOMjAy0t7dzDY6ZMadMmSLZTOTCBJHlgGUb3vr163l4OSOmvYq/Y87YOp1OEhwgz0I9ceJEiXPnxYsXuWl1x44dAAAbGxsAyvJHjD7//HPcvn0bo0aNwqlTp2AwGLiAdeTIEQDgc8nmIioqCnv27FHU8GL9kB9MbLOR91M+p0x4Ek0GjHnF3w0ePJibPsQC1wAkGaJF2rx5MwAgMTGRX//GG28gLy8P9fX1/D3c3d3x66+/gshywNTU1HBEQ/7erHSTvCpCWVkZPvnkE+5ULu+ntQzcauPz448/YsiQIdwvjiGCAFSF08zMTDx8+JA7RXt5eWHKlCk8GIO1sWPHSpza1XiXiHiJn88//1xSbovxLgtlZp+dOnWK/y2aDOURZtu2bZMgjg8fPuSI2rlz53D16lVuorS21j///HOcOnWKO4cXFRWhu7sbtra2vLj3hx9+CK1WyzP25+bmSuqwMn5mY8PSoYjfM9OyGu8eO3ZMovjJEZqYmBjJuC1YsICXgFq4cKGEd9XK0RBZTPE//vgjR9Tc3d25f9Kbb74JAHj48KEkeWxBQQEWLVqkmsEfsKTzAMD5gsiCQDCndbXC80QvFFo1EtdeREQETp48ySP8xH4+ffpU4VNIZEGczp49i6KiIjx//hy2trY8Y73JZMKTJ0/w/PlzjBo1iiPSWq1WtYRacHAwTpw4gY8//hhXrlzhTvVsrqZNm4bKykqJr+Unn3zC7yP6JMmVmLlz50rQ0o0bN3Lle8WKFdzScunSJaso15tvvomRI0di2rRpHMEaNGgQkpKSsGXLFly6dAmLFi3CoEGD+BnT0tKCnTt3Ijg4mO+VZrMZBoMBDx48wKRJk7hLhIODA+zs7NDR0YHo6GjJuZOTk4Pbt2+jra2NC0hE0vq5bA5FK1BBQQFPBZGbm8tdUebNm2c1yrukpASNjY2Ijo5GW1sbNBoNSkpKUFRUhP79+2Py5MmYMmUKhg4dijfeeAPu7u6oqanhTu9JSUlwcXHh5s05c+agqakJnZ2dqKioQGhoKI/YLiwsRGdnJ5KTkxEcHAyDwYD58+dj0qRJSEtL69NXWUwSa2NjwxV0e3t7Dkb4+vpaTSbLyvLY2Njwqg0va3+3jxYAjBkzBnV1dRgyZAj3YWLCE0MHRGcyVpCTScnx8fF9Zug2m83clCM+V+3adevWca1j5cqV3KzHFgL7HTtA09PTrUaqERHfAAHg4MGDILKU+mCbRXR0NGd8AJLNKDAwEJWVlbCxsUFkZCT8/f1Vw9nFUP6bN28iMTERe/fu5RnFxWuZIKDRaLjWNGLECFy8eJFrNcOGDcOvv/7KNTym+Yr+AixKjtVC8/LyQnNzM2bPno0vvvgCbm5uuHPnDpycnHhaCiaw9fb2KvKCsU1b3BjkUWLiYv70009x9OhR+Pn5YevWrdi7d6/EEVHs95EjR6DRaKDVavH+++/z8YqPj8etW7cUPNHR0aHIgSUW42XmMgBoa2vD3LlzUVVVxd+XRSSyqDXxPg0NDbC1teX9TE9P7zNfjuirCFjyylkzJXzwwQfcr2bz5s2SNXHw4EFFP8vLyxXlSUQzIyu7AwD79++HTqfD0aNH+YHDxpFFlonausFgQHFxMWJiYhAZGYn4+HiFQsTWJvv/48eP4e/vj++//15RZJn1gciCsrLDifm2sfeeMWMGAMDNzY2bVQICAiQRxAy2ZweZ2WxGamoqDh06hCtXrsDPzw9//vknNBoNn9OpU6fC1tYW169flwjHgYGBmDVrFuzt7VFZWYng4GBMmTJFsamKprJLly5h1apVqK+vR3d3t8Q3T867zDSdlZWFDz74gB9WFRUVePDggWJO1TL7iwjGO++8A4PBgDt37qCpqQn79+9HTU0NRyCYYDd9+nRFRYQJEyYgODgYQ4cOhb+/P/r3769QbERat24dR9pu3bqF8vJy7n8lp61bt3JkcdGiRRLU+p133lH0s7S0VIEOiS4gzEfz8uXLWLp0KVpbW7Fq1SpUVFTAwcEBixYtQlhYGOrq6vDnn39KyjdVVlairKwMFRUVSElJQWlpqWpkNhMoQ0NDceDAAXh5eeHs2bPw9fVVKMUM0SsoKOB819LSgrlz53J+bG9v58ouC6jRarWqSCZDuY1GI+Li4jB37lzMnTsXOTk56O3tRUVFBQoKCpCdnY2WlhYUFhait7dXgqBlZ2dj8ODByMzMRHV1NXJzczFixAiFEiZaa1ikYmtrK8xms8JcL/JfWloaHBwcEBMTg/Lyco706fV6tLe3c1MhO/PUooJFQbWwsBAFBQXo378/qqqqMHLkSAwaNAgNDQ3QaDRobGxEZWUlOjo6MHjwYCQlJSEuLg45OTmoq6tDUVERamtrUVJSgrq6OpSVlUGr1SIwMBAeHh4wGAwIDQ2FnZ0dqqqq0NHRgeLiYpSXlyMiIkJVwCeSltpzdHSU+G+5uroq/CbVEC352fVS2emVroK6kCOHLwsLCyW1smxtbWE0GvH5558ramQBL8wWycnJiqieDz74QNUMKCIrcrOi2kHPBunPP/9EYWEhjh07ptqfqKgo9PT0qNZD3L59u+Tv+Ph4Re07nU6HCRMmKMwHz54944V+2QIQ/TJycnKwb98+1XIpLF2CWjJCkZKSkiTC29WrVzF//nyr8zZx4kTFeBORIu8Tq0gvmkbDw8NVC6eyeU9ISOALX44m3Lp1S1UbYhplfHx8n86JLi4uEp4AgOLiYtUcZEQWoVJEARmpRSwOGDAAb7zxBi5evAgii9bDkB/xek9PTwDgggDzJRDv9fXXX6vW8BQ3IXmEnfygFzdAwGJO+fzzz1URt5SUFAliJZK8wHVpaSlGjx7N58/BwQEGgwEbNmyQFE1nz2VO+kVFRfDy8pL4vk2aNAk7duxQnTMmQMrXqBzSz8/P5ybX9PR0vPPOO7yEkFp/pk6dquqMzMrjMNLr9ejq6pIgL3q9nqM74rUbNmzAb7/9hsDAQH7APnz4kH9vY2ODK1euqPp9srm3ljeJkVarlSg+ANDe3i5JwiuffzVTstz3LTQ0FJMmTcKuXbv42tDpdKitrQUgTWTKXAC6urrg5uYGjUajcEG4ceOGJMcfI2bmdnZ2fqn5RUSIAMBoNOLYsWOqa1Ht8CeyKILyYJ9Ro0ZJCqsnJCSgoKAA69evV9z7+++/5yZlVt5F/H7atGmYOHGi5NC1sbGBu7u7JGpO/I08YXFaWpokP19TUxO6u7sV0fJsndXX1/MzU4xumzNnjuQ9ampqMHr0aI7k6fV67ki+YcMGSS3DKVOmYNasWTw5Z2hoKHeZYIlFOzs7JfzJhARmSuyrVBdbs+IeOGbMGKSlpVlF61kqB51Oh/j4eCQnJyMlJQVNTU1IT0/nPk/l5eUYPHgwRo0ahcrKSiQlJSEvLw9lZWUYOHAgTCYT3yfz8vIwcOBAmM1mJCQkQKfT8Xfy8/NDdHQ0+vfvj7y8PIX5+FUc3hmJQldAQADs7Ozg7u6uGkVqZ2cHR0fHV5OfXukqWOph9VW5/fz589i/fz+++eYbAOACy4gRI3gEngjnmc1mXLp0CaGhoVYTEwLgG/vUqVNRXFzMzQTidSzaTP752bNnJSjBzZs34ePjo1qzberUqQDAC1SK34mHYkBAAC5evMgL2jLp3tphT2QxjwQEBFhFQo4dO8adjQcOHIjU1FRu1lCLoJS/++HDh3Hp0iWuMc6fPx8DBgyAv7+/4lpWBFO+ybCNQ/z7jz/+wPHjxwG8iNBhKAXLDi5qsm+++SY6OjokmcP7evf6+noeCcfqgbHkmN7e3pg4caIkw/rChQtx9+5djrSlpKTgzJkz8PX1xdmzZ1VrZTIzQl8ZfgFLhOuPP/7I38/T0xOZmZkchRX9QWpra3H69GkYjUZVH0J2TyYQsDBrAIogCBahI45LdHQ0rl27xnnXyckJz549g5+fn2pNwG3btuGzzz4DkXq9O0bDhg3DV199hXv37kl4iwmXcgoKCsLdu3fh7+8vERJEevToEUdJJk6ciMLCQh6RqOanIn/38+fP4/Lly3ycDxw4gPLyctV8TnFxcXj8+DHMZrNCYJOjJQBw48YNAODBFixRKPPrFH+zY8cO1NbWKniIkdFo5Ci7r68vampqeJoFhjqwTdrDw0NSRYPIggDduXOHj1VNTQ127NgBjUaDJ0+eKGpsPn/+XGGmJiJFPioA+PTTT7mLA5EF4dFoNBxNE2vtjRgxAlu2bLFaj5bdk/kSDRw4kPMuqwnLEB2GQIqKaE5ODo4fP84jOoODg/HVV18hLCwMgwcPVhQI37lzJ9/75AK0GLm9fv16HD9+HBcuXAAAjjDPmTNHNcDEbDZj//790Ov1Vgthnzt3jit+DQ0NSEtLw+TJk/HFF19IDmf2f3n+w3nz5mHOnDlcSerp6eGK5s8//yy5Njk5GStWrIDBYOC8bm9vDzs7OwkCZTQa0dvbi3Xr1uHkyZOctxYsWMCVIqIX6JyPjw86OzuRnp5utX5pZmYmjwAODw+HVqtFYmIitmzZouAnOzs7hRLOnNDZs5OTk7nv7Pjx4xWO4m1tbcjPz0dqaiqMRiP0ej2Sk5NRUFAArVaLqKgonpB53LhxPLgkLS0N+fn5SEpK4mgpEyrd3NxgMpmQkZEBvV6P0NBQeHt7w8vLC8HBwQgODuY8z4Qilnw0MDBQ1ZndxsZGMs/MJCieiwwVc3R0VJyXbm5uPEDpZe3vMh0y/x1mcmpubuab6cqVK/H999+ran2ib5RaYVT2woWFhYiJiYHJZFJMtjwMVVyYbLAAi6AUExOD0tJSREREqPplAeDJH+U0bNgwLjRFRETg+vXr0Gg0XMuLiorCF198gfb2dj7wzAwSHBzMpWm157Lrmpqa+OKUby5qyS/lCwEAysrKYG9vz5EVtd+dOnUKkydPtmprvnfvHkcIz58/j5CQEEkqhE8//RR79+5VtXeLzpNq/ltEFnMJ828wGAwS7dpaZB3b+Hx8fDB//nx8+umn8PDw4KYcNSfmoqIinDx5UoEqsIPv7t27XMidOHEihg4digkTJnBN7siRI/j+++9VC0qzUkNExH1/5BQYGMi1Q61Wa1VxEIkJOYGBgfD19QUAREVFISUlhW9KbOzEAweAKhpJZIkkZGbSpKQknD59Gunp6Vyw6N+/P3766Sd0dHRwVIJp6eJ8iH2Wz1dRURHs7e3h5OSkEG7k6JmcmJCfnp4OV1dXzrNqEW2PHj3CmDFjVPPZJCUl8QhlDw8PXL16FeHh4RLevXPnDnbt2sUPB9ZfFxeXPtO/iHzIxiYrK0uSYsRaVBrbC729vfHBBx9g3bp18PDw4Dmt1FwWJkyYgLfeekuBcLJ+nzt3jh+2b775Jurr67Fw4UJu1rlw4QJu3bqlynNimh1RURBJq9VyZcFoNFotqi4SQ7cCAwORmJiIZ8+eISQkBFlZWQgICEBDQwPnWSYQu7u749NPP7WabmPFihV8fMrKyrBkyRI0NzfzfHAzZszAp59+ivb2dq58sUOYHcps3uT3joiIgI2NDQ/aCgsL42ZLtkeonVviurO1tcXDhw+5SSo/Px8uLi6qfllvv/22VR7T6/WYPHky/P39kZaWhvnz5yMnJwednZ3Q6XQwmUxYuXIlxo8fj+zsbLi7u0Ov18PW1hbR0dG872p7MhOO9Xo9wsLC4OjoqEiP8rLEsQ4ODtwNxM7OjvO6WoRiXl4eMjMzVRN/ElmUCbPZjLS0NNTU1HBToMlkgsFgwJAhQzB06FCUlpbyYtHh4eGIiIhAREQE7O3t4e7uriid4+7uDk9PT4SGhnJXnpiYmD7TUcjXFZtTNseicKb2OzVLF6P/UEFLzFEkpkCQk3wiMzIyUFhY2Ge6BTaRgCXhHls4bMO3VhZAjcmsEYNzhw4dahWFkFN+fr5VFEp0kCSywPWs5tLL3kWN1q1bB61W22duLKKX114aN24cysrKkJycrBgz+eJnC0Sv10tSIIgkz2Hl5uaGkpIS6HS6ly5aa0xZVFRkVdB9GbED32g08khMsT6meA0jtlDd3Nwk+cXkJI9aNJlMSE1NfWV+kfezrKwMRUVFimS5crImqDJiSMy4ceMkZgM5iZtBbW2t1fQJcpO/Xq9HYWHh31XpnlFRURGmTJmCgoICRQSwnNRqRYq0atUqGI1GVFZWKtBV+XoTEy5a8/eU8yfL8p2YmPhKyVHl9Mknn6CgoEAStKJG1jZrhkIUFxdj8+bN8Pb2VkR7ya0GzB80KipK4l8lznV4eLjCtFFQUIC0tLSXmjTV6LfffpPk1uuL+spx5+Pjw9MCDRw4sE8+9/Pzg7OzM7y9vbnvDpF0X4+KilLsaWlpadxJ/O8N4x88eDD/rVqEsEgv23e7urrg5+eHwsJCVSRXpPDwcF5jkAV/GAwGxMTEcIFKzv96vZ5H3L5KUmo5FRUVISIi4qW/VTOREb1AMePj45GWloaAgACFb5h8fpOSkpCUlASTyYTKykpuUkxPT0dsbCz0ej1SU1Oh0Wjg7OzM0SmtVouwsDCEhob2KeAQWQQncX+NioqCm5vb32UutEbsvtbWM6P/UEGLOUmqTYBYR0mMviKyHHLh4eFoaWmROKeJCzQsLAzt7e0cghYPNhbhSESq2lZJSQlcXV0ljtfsukGDBiEkJAQDBw7kJgHmE5Geno6zZ8/C399fMpB1dXUSmFFEppYuXcoRKDU/n6SkJMTGxvYpFMrDZsUNn/kUWCs2m5qaKtEImS2+pKQEQ4YMgZOTE1avXo24uDheAsTX1xfXrl3jGzpzpk5MTJSMs6gVvvnmm1ZNKEQvULi+csnISdS4T5w4gfDwcKtQ9+DBg1U35cjISO6LsXfvXgwdOhQ6nY4jKWvWrOH9Y2PJEvqpPScjI0Pi5M9MLeLziJR+RWrFwNXos88+42iG2iHAyg2JvMT63dnZCRcXF8ybN4/73rAqBNXV1TziTUQJ5aiGqAn29vbyjVF+nbOzs1Wt9FWJmX9YLh+RgoKCoNfrJQgL80Grra1FQ0MDjEYjNm3aBK1Wy30C4+LicPfuXb6embkrOTlZor2Ka2LNmjV8zOW+GjY2NnysRZP0y0hEnfft2wcnJyereaXq6+slwjAL0jAajdyxfPfu3dwpmK2BvXv3cuSKOXL7+vpK9hJx/+zs7JQEK8iTRLP1LBe05GMikihM9Pb2oqWlxer1gwYNsirYiAjc1KlT4eLiwoOUBgwYwJ39ReVbvrczAdPb2xsrV67k+6Y8y3l0dDTc3d0VyOfL/I4YMTcNIvWybFqtFj4+PpJxZPzAXAsyMzMxcuRIiUVAp9Ohp6eHC/vMvKnVavmeb2trK1F+J0yYgJaWFoSHh8NkMsHT0xMODg7w9PREVFQUF8LlwtKr5OYjeuEjbK2qRkREhMT/ju17oaGhMBqNcHR0RFFREeLj45GRkYHg4GC4ubmhqqqKC5fs3u7u7jAYDIiNjYVWq+X+bGFhYaiqqkJDQwNSU1Oh1+sRHx8PX19f+Pj4IDAwkKOGwcHBkrPZx8fHqtCj1+v5ue3n5wd7e3ur2d37EpzEEj2Ojo6SqERHR0fO86Ig9x8qaL3//vvYvHkzSktLeciy3D75solmhXf1ej22b9+OmJiYPn2b5FRWVqYwg0VHR/Nki4xERmT+Bd3d3dwRFgDOnj2LixcvcodeVl7CxcUF7777LiIiIiR9Yr5hAKzmjGEkmna6urokxaxfJUlcYWGhqunxzJkzkgUvCpfx8fH46KOPAFhynrF26dIlfPnll9ixYwf0er2kT+vWrUNDQwO+//57vvgvXboEALzortr7sQNbjhyNHDmS+wr1xcxivTu1nGOLFi3C22+/zf8W0VQPDw/u6F9bW8uT4P7yyy/48ssvceLECYlwTmTxpdiwYQMmTpzIN0Lm0M/8lV6VB4ks/jzl5eU4fPhwn9eJ9xWjpBjJAyrkxNI2dHV18QLTgCU1wBdffIExY8bg9u3b3C+rvb2dm1DF+4pNDsP3RePGjZOUpLKGSIhpWRhiJL+GJdVkJEbr5uXl4ffff8f9+/d5WSIA+PLLL/H1119j7969qKmp4Y7tHh4e2L59O0pKSgCAb37iGrXmd8ZQO/lB3NLSwmsm9oVIM/Q0MDBQlXc3bNggQU1F041Wq+XJQ9vb23Hu3Dm+1q5du4Zjx45JxpvI4hIwdepULFmyhEcMs1QRzB9W7T2tOawvWbKEl//qa+7FNBVqfn9tbW08WleNWOH3trY27m96//59HDlyBO+//z5qa2tx7949Pnf19fU8ByHzA/Xy8sIXX3yBq1evAgA/+OUVINRoypQpSEpK4oFI1khMlpyamqqKusqT0opITlZWFo4cOYI5c+agvr4eN2/exMmTJ9HT04PVq1ejs7MTtbW1vASdj48PWltbkZWVhQ0bNnB+XL16NXbu3Il9+/bxgAe5IMuuFZVznU6HzMxMLsT3JXSxtR8YGKhqZhPdFYhIkr8wKCgIFRUVmDJlCoxGI4YMGYLp06ejpaUFzc3NqK6uho+PjyQoISsrC/n5+aitrUVeXh5iY2NRX1+PoUOHYvjw4Rg+fDh0Oh38/PwQGhoKBwcHODk58Xn29fXlY+Dk5ITY2FjExMS81EIgjoEaQufg4NCnsuzo6Ag/Pz84ODjwlE1eXl5wd3eHq6srbG1tJevrVdorC1oMKQAgQTuYpiH6a8jTDIjENF4WzTRp0iSJpiZqNoyx2LVMm2lqauIbTFJSEk6ePClhPnGjX7lyJdra2hATE6OAOm1sbNDW1iYxPTHzByDNdaNmPmJarViOQ06McSdOnGi10CgR4ZtvvuHh2cwU4u3tjUePHvFkrps3b5YIV3LNBLAk9JObgJiUzsZMTOHANnuGaAQGBnKNjfWZzY+NjY1k8cn7w8o/ML8K0alRHPNdu3bB0dGRh0QTEXe6J7IcLn2l4GDXqfnKsFxiRBbo18nJSZJ0VkQimNArhmOze4qoD4OvmbmTmaEnT56M0NBQeHp6qib4O3bsGEd42fesRhyRxQH3s88+kyxacUzv3buHnJwcpKWlKYRad3d3bN26lW8Ytra23AEZABd4iV5o4CKKw9JfWCtsLK7FJUuWcF5TC4hZsGABj3pkaGx8fDwA8Mi5gwcPSta2eHgzX0itVqtAYPz8/JCQkMDHzNPTU5Lcln1OZFn3TPhh48WeU1RUZDXcm81PdXU1R34TEhIUyIFer+f7l2iWZQoOkWUv6AsNBiy5seRosLu7O8/BR2RBwry8vJCbm8tz9bB38/T05Lwo+g0yAUTci5mgx8Zs6dKlsLe3x6hRoxAQEAA/Pz/VqOaVK1eipKREgviLqUuampoUDv/i+P7888/QarWqPlmBgYE4duyY5DMWQANA4odZXV0NrVYrsZSwiN6+gm6YsDJ27FjOUxqNRiFgjxw5kguwTPjU6/U4ePAggoKC4O7ujqlTp0rOF3GN5ubmYsyYMYiNjVUI6IGBgcjOzuZClrOzM1egz549ixUrVvB3Ky0t5TVr09LS4O3tzdFOOUoqp+joaOTn5/N7+fv7K86AmJgYzhdsjdja2qKuro6f0xkZGZI9Qm4Wnjx5MveNks+7t7c330s8PDzg5uaGhIQEnu+qqKgI/fr1Q0FBASorK/m/RqMRWVlZ0Ov1PLkqkQUxYvzMzpy4uDheb9HW1hb29vYICQnhCCr7rbu7uwLRcnJykqRr6AvBZ8iuNTOqHOx5lfbKgpa3tzfu3r3LF1pycjKOHj3KN2zgRSFpAFwLmDZtGhITEyV2a3FzZCR3Cmd5TcS0BWzAxZxJavfy8fHBpUuXsHHjRknGYfE3TIjq6urC1q1bER8fj08++QRmsxk7d+7k17P3YH/PnDmT///UqVNcUxfLo7BNNCcnR8GUJpNJ8hlgSTAqSuHMoVMM5b9586ai9AmRxQwBWLQ9JumzDZAl7iR6cfARWYRdlqMFsNQCYyilfJy0Wi0iIiIkYegshYWYpkLMe8Q2IxEW5wwnXMOED7H0kre3t+qcssLQPj4+PCRd/q4Gg0EiaHV1deHJkyc8yvSHH37gfAaA+9oA4FF1ADgiwZ4jmjhZ5JlIotlGrZ9MK5OjlGr91Ov1ePz4MaZOncrD5OX9ZMjw9u3b8cYbb6C4uBjffvst9Ho9Pv74Y379oUOHJL8HXiR5/eOPP3hS3Li4OD5/bHOTF5Ylsmi87HtWpFduWmJor2j+BqAa5XbmzBk8ffqU80FZWRk/9BiKwZ7L+PTgwYOYO3cu5s2bx79n/Cgfp8DAQIwcOVJS+J1dI2re7JmM0tPTXzqnTIAT760WEUpEPFGzq6urRKAgelHqx8vLC6GhofzzuXPn4saNG0hLS8Pvv/+OlStXciUBABdgAEj2YFYdgCHC7JAnIknhciLLgSL6Yan1kwmscpOdWj+zs7Px1VdfoaysTKLcsO+/++47Pu779u1DUVERGhoacPXqVYSFhWHTpk0ALAhlV1cXhg4dKilgz2pK3rt3j+fdYqbn4OBgfpAyoV8UjsQADzFRLVOibGxsuHAijgnLYygnplwwxaOrq4ubCd966y0+/vn5+Vi1ahUiIyMxf/58lJSUoKGhAadOnUJAQABXKB88eMBLyVy4cAEpKSmYNm0aNmzYgNDQUCQnJ3MXAraWgoKCeEoH9l46nY4LgizhsWgdIHohKIpASGpqqiowkpeXh3HjxsHFxYX/jgm9BoOBu6/4+/tzMCYnJwcNDQ1ISkrCwIEDUVVVhdraWqSmpmLmzJkoLy9Heno6Zs2ahfr6ehQXF2PgwIEoLCyEyWRCbW0tgoKCkJKSAg8PD9jZ2SEvLw9hYWHw9fXl0YFs7hwdHbF8+XJFkmVGcmRQDZm3s7ODh4eHpNyOCCp4eHjwz93c3Ph1YlLpvtr/U8JS8WBcu3YtAEt2drbJLliwAKNGjeIpEESNdMGCBRyuZYc7kboJiT1PLFwsatSi8y27/40bN3hm6h9++EGiZbDwZPE3jx494n9v3LhR0k/5ZgJYMr6zBTlq1ChMmjQJra2t/Np3332X59Zh/j+iiUmsGeng4IDo6Ghe3kQ0gYp5fERiws2sWbMk7yqaK+Vjaa0vNjY2vISHuADF6yMiIhAZGYm5c+fyLPY2NjY80SSRJSS/oqJCkoBQ3Ji9vLzg6uqKzs5OhIWFSQpIy7NZi/mnGD+J/ZRHV4qm2F9//RUAkJ2dzbXw7777jjvNA5Cgj2I/mca/fPlyTJ8+HY8fP8bOnTtRXl7Or9m2bRv3nxHTHYiavK2tLT9Mdu3axd9X7swtOjaz+//000+YNWsW8vLycO3aNYn2LRYu9vDwAGDJccUW/1tvvfVS3p04cSIXfnt6erB06VJuhvXw8MD169eRmZkJrVbLgyPERI6i8OTk5ISGhgaEhoZi9uzZkhxcYh4h8RBgJZxE9BKQCmHyEiesMQ1Up9NxYfHtt9/m92HCGms6nQ6xsbGoqanBpk2bJPmXNmzYwP9/6NAhmEwmfmCIa4z1My4uDi0tLTCbzRxdJiLFxi73k5Lz7ssqXACWlBgMpfzxxx85OgZYCrIzlI21iIgItLW1ITQ0FFu2bOElh6ZOncrNzUSWhKCLFi2Cvb29Vd9IX19fzJgxAx4eHti6dSv36ZFH8omIHRuDzz77DDk5OZg6dSp6e3slh5tYqYLl95o/fz5fNwcOHOCVAn766SccOXKEC/RtbW0ALALksGHDEBQUhNWrV2P58uXo7e3F48ePkZeXh7Nnz0Kj0WDgwIG8WP2BAwf4c0VTqZeXF5qamhAVFYWpU6dKitszNJ7xGvs/Q+AWL17M+ePo0aMSBEjunwwAp0+f5v00m818/5k3bx727NmD2NhY7qt27949bNy4EXV1dbyU2rRp09DZ2YmbN28iKSkJEydORHd3NwIDAzFu3DgYjUaeb5FIKjy5ubkhMTERmZmZMJlMkjGwFrjChJDQ0FAu2M+bN0+C2MsDA3p7e7F06VJeT5TdPz09HSaTCW1tbSgtLUVJSQliY2MxZ84czJw5ExUVFaisrERpaSkaGhq4WbGsrAyVlZVobW1FREQEKisrkZ+fj+TkZG5lkaNKXl5eSE5Ohru7O7y8vDiaJU/rIP7NeJTNj4uLiwLpEl1gXFxcFKki3Nzc8CrtlQWt6upqjhhkZmZyhMpaVEBRUZGqP1JycjKmTJkiye3CiCWRY3/LHSTT0tKwc+dOpKWlqf6e2al55/7v5ytXruTJJ5ubm/l3ct+DpUuXYt68efy57H4M2VCLstPr9ZIs5Gzw/fz8MGzYMNV0BBqNBtevX+fQrNyfydXVFSdPnsSoUaMQHR2tWBTMv0WuQScmJmLFihU4ffo09u3bB8CS/0zcKNmhdv36dZSXl8Pe3p7fnwm+zJlYTvKs5Gw8GhoarJb9OX/+vNV0BEQW52U2l2rJOMPDw3lKC/Hze/fuobi4GACwY8cOLF26FNHR0dwMJ9b9YzlgWOoPZn61lnNq8ODBqmbekpISLFmyRBXtmTdvnuQglacjqa6uxrZt26xGeLKaegAkJWxOnjwJo9EIAJJkm2xtMUXixIkTmDRpEj8c2VyxzVUtCqempoabGxl5enoiKSkJs2bNUigmbA2KyKpcgAwNDcWpU6fQ3t6OoqIixTiyNCmDBg2SzGn//v3R3d2Np0+fSio6iGZ9g8GA4OBgnDx5EjU1NbCzs+MmPOaLo2ZO0mg0isoGer0eQUFBGDdunGrtRWdn5z7TEdjZ2eHtt9/G7NmzYW9vLxHAiF6YHdSqPXzyySfo7OzE77//jtOnT2PQoEEwmUx8PxCRzLq6Ou587OvryxURtTQ5RBZFVhzz4OBgBAQEYOjQoVi7dq1q2oFVq1ZJck3J9+3Bgwdj4cKFiIyMVK0WINYSFRXbo0ePcv/UwYMH49KlS3B3d+fuBAxdvnz5MqqqqnitwtbWVmi1WmRlZSE8PByRkZHcMZnde+zYsYqkwMnJyWhoaMDs2bNVK3IUFBRI+EOukMbExGD69OnIzMxUHScmhMnLANXW1vJk2FVVVTxXmHh/Ly8vmM1mdHR0IC0tDdHR0Whvb4evry8/Z1pbWxEZGQmdTscPfJbbTTwj8vLykJeXh/b2dtXi7REREejo6JCYTOW8XVlZiZSUFLi6uipcUBhKHRkZKUFs3d3dUVpaCrPZjIaGBgwYMAARERHQarWc35ngOWTIEBiNRl6zNDc3F42NjTAajWhsbERmZiays7NhMplgNBqRm5uLhoYGZGZmIjg4GIGBgTCbzcjLy0NlZSVqamoUriLMdMiEHx8fH4UPqoODAxeo+vLLkvvmiSV2HB0dFXIO209ZAuuXtVcWtJhz6a5du16aGZgxIQCJzwULoRW/l9vq2aEvTr5a5MvYsWMVKQKYlKvRaDBlyhS88cYbfBHIhQcAWL16tSLrO4NZv/rqKxC9QKXUnCTFfrDK8iIxm/VPP/2k2GxF6st3KyMjA/PmzVONoHBzc0NnZyfWrl2LpqYmlJeXK4QyABg1apTi+Z2dnSgsLMT48ePh7OzM+6kWjaXVajFlyhTs2bNHcR+mjfX09CAyMlK1n8xEJTrBy8nPzw+7du2yGg3W1NSEjRs3oqOjA3V1dSgtLZX0VavVSnxlGBUUFODdd9+FjY0NZs+ebTXiRhwvpqWIn8uLjbNxFa9h5nIx8k5trcyfP19hSmNm38zMTCxduhSzZs1CS0sLysvLFVnfAUswhygEREZGckGZRQgz/yi1CCyxH2KtUuYLwgQXvlH0MWbWqLq62mo6Da1Wi1mzZmHLli3o6OhAfX29QkgCwJ3j5eOn1+t5tDND+9RQ8W3btqGiogJ//PGH4j5MGWxtbeUJi+W/Zwd2X35XOp0Ovb29qn40Xl5e6OzsxI4dOzB06FA0NjaitrZWIuSPHTuWO8mLv62oqOCFtuvq6ji/W0s1AICjzuwzGxsbfhiLcy73y2Kou2hlkO95dnZ2mDVrlsK/jB06hYWFmDdvHjo7OzkqI6+acPfuXUmlCTZ+TFHr6emBn58fV3bVAisAcL81tjaCgoJQX1+PsLAwXLt2jV/30UcfSX4rmu8ZMqGmQOfl5SmCntj1Wq0Wra2tGD58OOchecqPr776Cg0NDRJrDJFFKA0ODsbw4cMRHh7OTbtqedB27tyJ3Nxc7Nu3T5IlPjs7m5euycnJQXd3t6pbCdvr+srrFxgYaDUXlpeXF4xGI4qLi5GYmAidTgedTidB8MvKypCenq4QbFli1MzMTC4s6fV67ofGnNsjIiKQnZ2N1tZWNDQ0oKOjAykpKTCZTMjPz0d9fT3Ky8sxcOBA5ObmYuzYsRKk0dHRka/78vLyPlOIyIV1kWxtbeHo6AgHBweeSFaunLq6uir2c+aq8rL2yoIWkaW4M9N2GMOyCBh2UDQ0NPDSK8yU1N3djXnz5vEaWOfPn+edEKM+5Exy8uRJnh3ZWgI4ETFi7ymaWUaMGIE//viD/80WNIOq2TViJENxcbHEFMLMeMCLLNB6vZ6bHZkZKCUlBadOncKsWbPw+PFjfPvtt5wJ5s6dazX/zs6dO7lZRjyARSft1NRU7pvG+nnlyhXJ5g6ApyhgqI1oolKzYct9adi/zNxGRNizZw8PySWymFPu3r2LwYMH46uvvgIASSkPsYSHyPhvvvkmTp8+jdTUVImjpRwGZr4I6enpWL16Ndra2iS+egC4H5qawyLbdERTpnhQsgjTu3fvSsy1Y8eOxRdffAF/f3+++W7duhXd3d08aECMZhPTmojU1taGL774gn9vTbgTN2E29iziisiCUn3zzTf8b7YpMx52d3dHbW2tZIxbW1sl6ArboIEXJjoWicvmhPHa8ePHsWXLFgDA48eP+Xvv3r3baiLLjz76iK830ewnrlfRHMOe++TJE0W9MIZwsGAacY2Ka5rIIjyK+bNE3mW/i4iIwFdffYWKigrOnwaDAT/99BNmzJiBX375BcCLw9psNkuCJcSIwV27duHKlSs89w/7XJ4HiwlDCxcuRGVlJVatWiVBdIEXRd/VIr9YGgQx+IbxK9GLQBnAUlyaafDr16/HyZMn0dDQgLi4OAQEBODs2bOYN28evvzySwDAhQsX+FqTI5mMFi1ahJs3b3K+UAvyICKJuQ2wCD6iWf7+/fvYv38//5vxDzv0U1JSoNVqJf625eXlHIlmaXlaW1vx3Xffoby8HO7u7pgwYQK+++47pKWlcURr+vTp2LJlC3bt2oXHjx/jk08+4aj/+vXrVddfRkYGDh06xM8fkUdFxVeMFmY8JipSgYGBOHz4MBd+GbonujiIpj0ii1Ik+gXu2LEDNjY2OHHiBCZOnIjk5GTU1NRwH+OpU6fC398fNTU1WLlyJSZMmICtW7di9+7dHP1saWmR8CLjUQcHB8yZMwdz5sxBVFSUBPgQ59bJyYn7JNXX18PFxQVVVVUSxHrBggX83Gd8J+49jM+ZQOrs7Izs7GxotVrExMRgwIABSElJwZgxY7iQWlRUhObmZvTv3x81NTUoKChAcXExWltb0b9/fwwdOhTjxo1De3s78vPzkZmZidzcXH5ei1niOzo60NnZqWrCF0k0/bEoQvEzb2/vPtEvuQD2SvLTK10FcORn3Lhx+PHHH3lF7QULFkhQqePHj2P+/PlYuHAhhg0bJsnWzGjz5s2IiIhQONexARThP7PZzDedgIAAJCYmKpw65Qth3rx5mD59OpycnLizKTNNsIXMmEt+D2bCYXXBWPHUGTNmYNeuXVxwamtrw7Jly7B48WJkZmbi66+/VkxAUFAQiouLuWYkbk5sU2LMrdFoOBN7eHigX79+qK2tlUSPyfs5depULFq0SBGFlZOTw/tBRKrmD9GcePPmTZw+fRp6vR7Tp0/H+vXrJUjf22+/jcWLF6OmpgZXrlxRzdK/aNEi1Rp0zHFbfAcxf05CQgKSk5N5n+R05swZGAwGLF26lDvYvvHGGwAsvmIi2qN2cOl0Oo48LV++HA8ePMCgQYMwdOhQrFmzRjK+586dw+rVqzFt2jT09PQoHPyJLL4a4gHMtHu2cYoac3V1NUcOYmJikJKSYrWf4ibONmnGg3Fxcejp6VFNESGSKDQBFn/CwsJCrFq1CidPnuTraMOGDdi2bRs2b96MxsZGjjiKNGDAAB4ZRCTNas5qnDE/moKCAs7jGo0GJpMJb7zxhmomfdbPtWvXYvny5fwABiz+ks3NzZJDSC3KkQn8Xl5eePLkCd5//30YjUYsX74c77zzDn/XsLAwnDhxAu+88w46Ojrw7NkzzveicL9w4UJ+MIuIKkNemIDKCgY7OTkhJCQE2dnZyMnJUZ3ToqIijB07Fo2NjVixYgUXHpkgy2oUsuvlJlgiC7rIkK/169fjzJkzvLD1li1bJMXKL168iH379qGnp0dSJ9bV1ZUH2pw8eVKiyLGxZcrqwIED4eXlBS8vLwwbNgwmkwkuLi5IT0+32k9xTufMmcN5g/li2dvbY/LkyVYrQTBiilRNTQ2OHz+O8ePHo6WlBT09Pdi/fz9fV/v27cOWLVuwefNmTJkyBR988AG3eLC5W7RoERoaGriiyszoZrMZ/fv3R2ZmJtLS0hAZGYlBgwZxHtfr9UhJSUFXV5fqQcv6OWLECAwcOJBnX3/06BFGjRqFkpISyT6nhjyyd4yPj0dPTw9Gjx6NoqIiTJgwATNmzMCIESPg7++PpqYmzJ8/H/PmzcOgQYPw1ltvcR5lSbEzMjIwfPhwvh5aWlrg6+uLmJgYzJgxAwaDgfeX+UJ5e3sjLCwM0dHR0Gq1knNC5F1W99NsNnMEiQl6/v7+EqRRDbVnSpS/vz8qKytRW1uLoqIi1NTUoKGhAYMGDUJubi7y8vLQ2trKBavW1lZUVlZCp9PBbDbzhLItLS2orKyEVquFRqOBXq/ntUtzc3MxYMAANDY2orm5GeXl5fydGDJlLR8c2wecnJy4edbBwYGfJa+SBPdV2t/tDE9kibximpu1gsfFxcUKp0txcuQFKffs2cMPgsWLF+ONN95AV1cX7t+/b7WDPj4+Enh33759/DBiEVeMMcXfVVZWIi8vj2804nuyRJBEljIgIgqkZoJJSEjAmTNn+nxH8e+enh5eNzA+Ph47duzAxIkTFTlp5EKDqEUPGjRIEoFk7dkmkwm5ubl8wxGvHT16NIdRe3t7JVGFaskYw8PDVX1ZRMhY1Ao0Gg1u3LjB/z516hRGjx6NN954o8+s8HLzC9O8z507J0FARDNTUFAQioqK+IYp5mYrKiriB69Wq8WzZ8+4sCxPUMoW3rBhwxSavOgLIa+dJioau3btwty5c9HY2Ngn7wYGBkoEp/379/MQbVHAE1NgiLzr4uICg8EgiVYVndABKIqEi+Tm5oaioiIuCKuR3Idt8+bNHI1paGjArl270NnZycv+qM2hk5OTxNTU09PDzYl98W5eXh6ysrL44SImTBbRgtOnT0vqyomKFJFFq87Ly1PU2JPzkEgdHR081UpAQAA+++wzLFiwAPv375cge3LhQTRpODs783mUF5oXE+hGRkbCbDZzXhDHJCcnhwsgpaWluHnzJt8j5Qeks7MzzGYzVq1apUhFI6LWIkpO9CIS0d7eHp988gnWrVuHmTNnKsx7oo9QWFiYJIpv6dKlSEpKkmR0JyJFri6WuoDxj8gjIhr27rvvcleP1NRUiX+Mp6cncnNzMXLkSEV+KzHKUH6+bNq0ie9rM2fOxMaNGzF58mRFni1x7Dw9PSVz2tnZya0SLCiGXSfew2AwIC4ujt9LRONF/unq6sLcuXMREBCA0NBQicXDxcUFeXl5aGpq4oKwmD+M7V0GgwFarZYr+WPGjMHSpUvh6+uL6upqLF26FJMmTUJPT4/Ex0lE7RwcHCT7SEBAAD8z5WeBaPnx9vZGcHAw98sTrxXNi0ajEfX19cjKykJaWhoqKiqQnJyMxMREJCYmcoWupaUF2dnZvByPo6MjSkpKEBUVxROBh4eHw9HRESaTCUOHDuW/7ezs5Lm5mpqa+PMdHByspmkgeiFIyQUqOWhiY2PDz0s5QPMfKmgxjWvBggU4dOiQRMBhUYhubm5ceGEvzpgwNTVVVWtjxO4XHx+PS5cuSWBQkZHF0FtxAWZkZPD7v/XWW1br0jG/FZECAgIUaExbWxuePn0Kg8HAEZzx48fz3zMIli0O5pyanZ1tVfgkIkmo7c6dOyWbhaOjI2d4a+8vPnv+/PkSrVY+nozZxEUkt9drtVrcuHEDlZWV/OCJioriZlHG2GzTYM9mTozWmFjcZBcsWICPPvpI4gvBNo2MjAzJPUSTIlvs+fn5VkN3PTw8VBG2yspKzjcMNdy6dSu2bt0qEZTYwRYQEGDVTJKVldUn77JNq6qqCpcvX5aMsfg7UeAXBfCioiJuQvnggw8UNQQZqQnAGo2GB6aweZ4yZQqePHmCvLw8Pl9btmxBdXU1vLy8+HuwcWAmv4qKClVUkpEoVB89elSSg0usmScGBchz8TBla9u2bVaFPPHAETc1eUmY3Nxc3L9/H4WFhXz95eXl4fPPP4ejoyPS09MRFBTEhTW2dltbW1FeXm41kEfs544dO3Dp0iXOG1FRUXyMGPpARIq8PUwxGj58OK5cuaL6nJCQEFWfxIqKCr7Rs320t7cXU6dO5VFlQUFBACwpIzIyMlBQUKDwZzKbzTw5ptrzRRP5nDlz8PXXX3NeiIiIQE5ODlxdXaHRaCRrTJ42gR24O3bssFpmSU2pYSZOkXdbW1uxevVqSfLM3bt3o6ysDGazGU1NTYiLi+O829LSAi8vL3R0dPTpi8TQw6KiIhw6dAhz5szhNfLEwC0xWlrcNz08PLhgPX78eEVKELU1LpJc+E1ISEBnZyfS0tK42XLAgAF44403YDabUVdXh5ycHA5ojB8/Hu7u7mhra0N9fT2Sk5Ph6OioQM2mTp2K4OBgJCYmYs6cOVi0aBFKSkr4OSaacJmAJM8vx3g6LS3NaoSql5eXavklub+bra0t9ysrKiqC0WjkKFZBQQFKS0tRVlaGsrIypKWloaioCNnZ2SgvL0dRUREyMzOh1+sRGxuL4OBgbuLMz89HQUEB8vLyMGjQIIwaNQotLS0oLi5GTk4OUlNTERMTg/j4eGg0GtUE2my92tjY9FlKyppfF6NXabb0ii06OpqIiEaPHk2enp5kNBpp3rx5REQUGRlJRER/+9vfqKqqiiorK+n333+ngIAA+qd/+ify8PCg6dOn0//+3/+biIimT59ORETV1dX8/gcOHCAiop9++on+8R//kXbt2kVERPX19fT48WMKDw8nnU5HH3/8MQ0cOJC0Wi398ssv/Pf/+q//SitXriQnJyd6++23adGiRfy7iIgI/v+5c+dSZ2cnNTc3082bN4mIyGg00vnz52nw4ME0evRoIiJavXo1/fzzz5SRkUH//M//TAaDgebMmUNz586lEydO0Lx582jw4MG0Y8cOIiJydnYmf39/8vb2pk8++YRSU1PJaDQSEZFer+fPt7e35/+fNGkSDRw4kIiIAgIC6Pfff6cnT56QTqejkydPEhHRe++9p5iLpUuXkr+/P926dYv++te/8s/d3d0l45mXl0elpaX04MEDIiIaOHAg7dmzh4iIVq1aRURE169fp61bt9J/+S//hXQ6HU2cOJFu375NXl5eZDAY6O7duzRq1Cj68ccfiYjIbDZTXFwcff7553Tu3DnauHGjYoyJiBITE/n/P/74Y0pJSaHbt2/zz7777juKj4+nZ8+e0fPnz+nx48dERPTbb7/xa9544w0aOHAg2draUklJCdnY2PDv/P39iYjoyZMndPnyZWptbaWrV68SEZHJZKLPPvuMHj9+TL29vRQSEkJEFl7y9/enfv36cd5l9/zuu+/oX/7lX6i0tJSIiFJSUqi2tpYiIiJo165ddOjQISIimjVrFhER5efnExGRo6Mj3bp1i4iI/v3f/50SEhL4GNfV1dGdO3dIr9dTeno6XblyhZYtW0ZExMeTiOh//s//SatWrSKNRkP/63/9L/4sIqLQ0FD+/zfffJNGjx5NU6ZMod27dxMRUXh4OF27do0GDBhAOTk5fNx+++03SklJoZEjR5KXlxf9y7/8C+3cuZN++uknGjNmDHV2dtKmTZvI1dWV/vKXv1BiYiKZTCY6d+4c6XQ6amlpISKiqqoq/vxvvvmG/7+1tZU6OzuJiCgvL4+uX79Ozs7OZDab6cMPPyS9Xk8rVqyQzGdBQQFt3LiR/P396cKFC/Tf//t/59+5urry/+/YsYMGDRpElZWV/Pd1dXX0wQcf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2Xc7Oztx4MABiVGhlp6ePqxb5sqVK/xvsRxRSkqKVCqGMef8cf78eQnwnjhxgi8ASjbBVTkRGheii+yXX37BP//5T9y6dQsDAwOwWCyqDZqbmxsAZ9WEW7duSX3Jx8dn2KLZohie7hFpeMRyNYw5N38nT57Ehg0beKDEpUuXVNovasqFXNnEsmYXLlzA6dOnATj1YX19fQgLC1OlowCcLDqx9q7GobJR5CdjTMW47Nq1SzVvvvnmm5ILt6WlRWJKR7K+0AK/adMmzhDm5eVh27ZtuHz5MhwOB2fzKW8htZaWFpSXl6OwsFDl4k9KSnKZ34oxWS8o5mWLj49XMUk1NTUoKSlBbm4uz+EkjgulrnE4997cuXMlAFhTU4P6+nqe8oYxdQJwHx8fPPvsswgNDcXChQsl92pgYKBmhLby/tJxxPeUXifSgXl6enJywsvLyyUAeprrUTy+yWTitQw9PDwwevRonlNL/A5tqJTpOBhTM3EjMbf/DADddNNNN91000033f7L5v5/fQK66aabbrrppptu/19NB1q66aabbrrppptuf5LpQEs33XTTTTfddNPtTzIdaOmmm2666aabbrr9SaYDLd1000033XTTTbc/yXSgpZtuuummm2666fYnmQ60dNNNN91000033f4k04GWbrrppptuuumm259kOtDSTTfddNNNN910+5PsfwAmd61phvc9EwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model.eval()\n", + "noise = torch.randn((1, 1, 64, 64))\n", + "noise = noise.to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "with autocast(enabled=True):\n", + " image, intermediates = inferer.sample(\n", + " input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100\n", + " )\n", + "\n", + "chain = torch.cat(intermediates, dim=-1)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "546f9983-c2e2-4c24-b03a-ebe34627638a", + "metadata": {}, + "source": [ + "## Define the classification model\n", + "First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "44cc6928-2525-4e61-8805-15b409097bbb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DiffusionModelEncoder(\n", + " (conv_in): Convolution(\n", + " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_embed): Sequential(\n", + " (0): Linear(in_features=32, out_features=128, bias=True)\n", + " (1): SiLU()\n", + " (2): Linear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (down_blocks): ModuleList(\n", + " (0): DownBlock(\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", + " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (1): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (2): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (out): Sequential(\n", + " (0): Linear(in_features=4096, out_features=512, bias=True)\n", + " (1): ReLU()\n", + " (2): Dropout(p=0.1, inplace=False)\n", + " (3): Linear(in_features=512, out_features=2, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "classifier = DiffusionModelEncoder(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=2,\n", + " num_channels=(32, 64, 64),\n", + " attention_levels=(False, True, True),\n", + " num_res_blocks=(1,1,1),\n", + " num_head_channels=64,\n", + " with_conditioning=False,\n", + ")\n", + "classifier.to(device)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", + "metadata": {}, + "source": [ + "## Model training of the classification model\n", + "We train our classification model for 100 epochs.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 534it [00:24, 22.16it/s, loss=0.671] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 17it [00:00, 65.41it/s, val_loss=0.288]\n", + "Epoch 1: : 534it [00:24, 21.99it/s, loss=0.612] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 1: : 17it [00:00, 66.80it/s, val_loss=0.363]\n", + "Epoch 2: : 534it [00:24, 21.92it/s, loss=0.586] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 2: : 17it [00:00, 68.07it/s, val_loss=0.226]\n", + "Epoch 3: : 534it [00:26, 20.48it/s, loss=0.581] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 3: : 17it [00:00, 63.17it/s, val_loss=0.217]\n", + "Epoch 4: : 534it [00:25, 20.99it/s, loss=0.579] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 4: : 17it [00:00, 63.70it/s, val_loss=0.211]\n", + "Epoch 5: : 534it [00:26, 20.46it/s, loss=0.572] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 5: : 17it [00:00, 63.46it/s, val_loss=0.234]\n", + "Epoch 6: : 534it [00:25, 20.66it/s, loss=0.577] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 6: : 17it [00:00, 63.53it/s, val_loss=0.306]\n", + "Epoch 7: : 534it [00:26, 20.39it/s, loss=0.57] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 7: : 17it [00:00, 62.97it/s, val_loss=0.372]\n", + "Epoch 8: : 534it [00:25, 20.72it/s, loss=0.572] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 8: : 17it [00:00, 63.76it/s, val_loss=0.208]\n", + "Epoch 9: : 534it [00:26, 20.18it/s, loss=0.565] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 9: : 17it [00:00, 61.70it/s, val_loss=0.245]\n", + "Epoch 10: : 534it [00:26, 20.22it/s, loss=0.563] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 10: : 17it [00:00, 63.48it/s, val_loss=0.181]\n", + "Epoch 11: : 534it [00:26, 20.42it/s, loss=0.564] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 11: : 17it [00:00, 64.20it/s, val_loss=0.196]\n", + "Epoch 12: : 534it [00:26, 20.35it/s, loss=0.562] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 12: : 17it [00:00, 64.27it/s, val_loss=0.235]\n", + "Epoch 13: : 534it [00:26, 20.31it/s, loss=0.562] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 13: : 17it [00:00, 62.05it/s, val_loss=0.2] \n", + "Epoch 14: : 534it [00:26, 20.35it/s, loss=0.557] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 14: : 17it [00:00, 63.59it/s, val_loss=0.232]\n", + "Epoch 15: : 534it [00:26, 20.25it/s, loss=0.558] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 15: : 17it [00:00, 62.56it/s, val_loss=0.236]\n", + "Epoch 16: : 534it [00:26, 20.39it/s, loss=0.559] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 16: : 17it [00:00, 62.08it/s, val_loss=0.227]\n", + "Epoch 17: : 534it [00:26, 20.44it/s, loss=0.561] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 17: : 17it [00:00, 61.93it/s, val_loss=0.232]\n", + "Epoch 18: : 534it [00:26, 20.10it/s, loss=0.556] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 18: : 17it [00:00, 61.19it/s, val_loss=0.265]\n", + "Epoch 19: : 534it [00:26, 20.52it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 19: : 17it [00:00, 61.85it/s, val_loss=0.214]\n", + "Epoch 20: : 534it [00:26, 20.13it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 20: : 17it [00:00, 62.12it/s, val_loss=0.304]\n", + "Epoch 21: : 534it [00:26, 20.33it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 21: : 17it [00:00, 60.91it/s, val_loss=0.235]\n", + "Epoch 22: : 534it [00:26, 20.19it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 22: : 17it [00:00, 62.88it/s, val_loss=0.232]\n", + "Epoch 23: : 534it [00:26, 20.24it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 23: : 17it [00:00, 62.73it/s, val_loss=0.146]\n", + "Epoch 24: : 534it [00:26, 20.32it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 24: : 17it [00:00, 62.44it/s, val_loss=0.223]\n", + "Epoch 25: : 534it [00:26, 20.20it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 25: : 17it [00:00, 62.95it/s, val_loss=0.286]\n", + "Epoch 26: : 534it [00:26, 20.24it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 26: : 17it [00:00, 63.56it/s, val_loss=0.316]\n", + "Epoch 27: : 534it [00:26, 20.20it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 27: : 17it [00:00, 61.08it/s, val_loss=0.217]\n", + "Epoch 28: : 534it [00:26, 20.18it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 28: : 17it [00:00, 63.45it/s, val_loss=0.155]\n", + "Epoch 29: : 534it [00:26, 20.30it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 29: : 17it [00:00, 62.70it/s, val_loss=0.227]\n", + "Epoch 30: : 534it [00:25, 20.61it/s, loss=0.55] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 30: : 17it [00:00, 66.44it/s, val_loss=0.2] \n", + "Epoch 31: : 534it [00:26, 20.32it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 31: : 17it [00:00, 61.60it/s, val_loss=0.258]\n", + "Epoch 32: : 534it [00:26, 20.40it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 32: : 17it [00:00, 63.44it/s, val_loss=0.17] \n", + "Epoch 33: : 534it [00:26, 20.37it/s, loss=0.546] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 33: : 17it [00:00, 62.44it/s, val_loss=0.197]\n", + "Epoch 34: : 534it [00:26, 20.23it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 34: : 17it [00:00, 64.16it/s, val_loss=0.227]\n", + "Epoch 35: : 534it [00:26, 20.28it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 35: : 17it [00:00, 61.64it/s, val_loss=0.182]\n", + "Epoch 36: : 534it [00:26, 20.24it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 36: : 17it [00:00, 62.97it/s, val_loss=0.189]\n", + "Epoch 37: : 534it [00:26, 20.37it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 37: : 17it [00:00, 63.50it/s, val_loss=0.232]\n", + "Epoch 38: : 534it [00:26, 20.30it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 38: : 17it [00:00, 62.30it/s, val_loss=0.175]\n", + "Epoch 39: : 534it [00:26, 20.25it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 39: : 17it [00:00, 62.73it/s, val_loss=0.219]\n", + "Epoch 40: : 534it [00:26, 20.17it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 40: : 17it [00:00, 62.13it/s, val_loss=0.169]\n", + "Epoch 41: : 534it [00:26, 20.06it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 41: : 17it [00:00, 61.03it/s, val_loss=0.153]\n", + "Epoch 42: : 534it [00:26, 20.06it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 42: : 17it [00:00, 62.17it/s, val_loss=0.18] \n", + "Epoch 43: : 534it [00:26, 20.04it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 43: : 17it [00:00, 61.85it/s, val_loss=0.168]\n", + "Epoch 44: : 534it [00:26, 19.98it/s, loss=0.542] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 44: : 17it [00:00, 61.28it/s, val_loss=0.181]\n", + "Epoch 45: : 534it [00:26, 20.16it/s, loss=0.542] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 45: : 17it [00:00, 63.26it/s, val_loss=0.154]\n", + "Epoch 46: : 534it [00:26, 20.08it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 46: : 17it [00:00, 61.43it/s, val_loss=0.151]\n", + "Epoch 47: : 534it [00:26, 20.06it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 47: : 17it [00:00, 62.66it/s, val_loss=0.174]\n", + "Epoch 48: : 534it [00:26, 20.27it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 48: : 17it [00:00, 62.88it/s, val_loss=0.148]\n", + "Epoch 49: : 534it [00:26, 20.32it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 49: : 17it [00:00, 62.37it/s, val_loss=0.178]\n", + "Epoch 50: : 534it [00:26, 20.24it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 50: : 17it [00:00, 62.16it/s, val_loss=0.203]\n", + "Epoch 51: : 534it [00:26, 20.33it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 51: : 17it [00:00, 63.13it/s, val_loss=0.178]\n", + "Epoch 52: : 534it [00:26, 20.37it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 52: : 17it [00:00, 63.45it/s, val_loss=0.191]\n", + "Epoch 53: : 534it [00:26, 20.32it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 53: : 17it [00:00, 62.24it/s, val_loss=0.182]\n", + "Epoch 54: : 534it [00:26, 20.10it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 54: : 17it [00:00, 63.44it/s, val_loss=0.184]\n", + "Epoch 55: : 534it [00:26, 19.94it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 55: : 17it [00:00, 62.61it/s, val_loss=0.165]\n", + "Epoch 56: : 534it [00:26, 20.19it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 56: : 17it [00:00, 61.80it/s, val_loss=0.175]\n", + "Epoch 57: : 534it [00:26, 20.07it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 57: : 17it [00:00, 62.74it/s, val_loss=0.164]\n", + "Epoch 58: : 534it [00:26, 20.27it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 58: : 17it [00:00, 62.64it/s, val_loss=0.159]\n", + "Epoch 59: : 534it [00:26, 20.23it/s, loss=0.536] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 59: : 17it [00:00, 63.27it/s, val_loss=0.166]\n", + "Epoch 60: : 534it [00:26, 20.21it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 60: : 17it [00:00, 62.98it/s, val_loss=0.146]\n", + "Epoch 61: : 534it [00:26, 20.03it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 61: : 17it [00:00, 61.23it/s, val_loss=0.153]\n", + "Epoch 62: : 534it [00:26, 20.15it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 62: : 17it [00:00, 62.14it/s, val_loss=0.18] \n", + "Epoch 63: : 534it [00:26, 20.22it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 63: : 17it [00:00, 61.99it/s, val_loss=0.152]\n", + "Epoch 64: : 534it [00:26, 20.04it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 64: : 17it [00:00, 61.32it/s, val_loss=0.14] \n", + "Epoch 65: : 534it [00:26, 20.25it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 65: : 17it [00:00, 63.32it/s, val_loss=0.145]\n", + "Epoch 66: : 534it [00:26, 20.14it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 66: : 17it [00:00, 61.50it/s, val_loss=0.154]\n", + "Epoch 67: : 534it [00:26, 20.09it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 67: : 17it [00:00, 59.68it/s, val_loss=0.148]\n", + "Epoch 68: : 534it [00:26, 20.25it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 68: : 17it [00:00, 63.40it/s, val_loss=0.172]\n", + "Epoch 69: : 534it [00:26, 20.34it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 69: : 17it [00:00, 61.41it/s, val_loss=0.211]\n", + "Epoch 70: : 534it [00:26, 20.22it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 70: : 17it [00:00, 60.88it/s, val_loss=0.158]\n", + "Epoch 71: : 534it [00:26, 20.51it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 71: : 17it [00:00, 62.84it/s, val_loss=0.129]\n", + "Epoch 72: : 534it [00:26, 20.30it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 72: : 17it [00:00, 63.48it/s, val_loss=0.197]\n", + "Epoch 73: : 534it [00:26, 20.27it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 73: : 17it [00:00, 62.99it/s, val_loss=0.158]\n", + "Epoch 74: : 534it [00:26, 20.17it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 74: : 17it [00:00, 62.28it/s, val_loss=0.147]\n", + "Epoch 75: : 534it [00:26, 20.25it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 75: : 17it [00:00, 63.89it/s, val_loss=0.131]\n", + "Epoch 76: : 534it [00:26, 20.34it/s, loss=0.536] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 76: : 17it [00:00, 61.53it/s, val_loss=0.155]\n", + "Epoch 77: : 534it [00:26, 20.15it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 77: : 17it [00:00, 61.50it/s, val_loss=0.158]\n", + "Epoch 78: : 534it [00:26, 20.20it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 78: : 17it [00:00, 62.59it/s, val_loss=0.153]\n", + "Epoch 79: : 534it [00:26, 20.19it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 79: : 17it [00:00, 61.60it/s, val_loss=0.162]\n", + "Epoch 80: : 534it [00:26, 20.31it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 80: : 17it [00:00, 63.66it/s, val_loss=0.181]\n", + "Epoch 81: : 534it [00:26, 20.48it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 81: : 17it [00:00, 63.58it/s, val_loss=0.216]\n", + "Epoch 82: : 534it [00:26, 20.11it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 82: : 17it [00:00, 60.27it/s, val_loss=0.139]\n", + "Epoch 83: : 534it [00:26, 20.29it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 83: : 17it [00:00, 62.75it/s, val_loss=0.202]\n", + "Epoch 84: : 534it [00:26, 20.10it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 84: : 17it [00:00, 60.65it/s, val_loss=0.148]\n", + "Epoch 85: : 534it [00:26, 20.23it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 85: : 17it [00:00, 63.67it/s, val_loss=0.153]\n", + "Epoch 86: : 534it [00:26, 20.20it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 86: : 17it [00:00, 63.29it/s, val_loss=0.153]\n", + "Epoch 87: : 534it [00:26, 20.26it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 87: : 17it [00:00, 63.14it/s, val_loss=0.148]\n", + "Epoch 88: : 534it [00:26, 20.04it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 88: : 17it [00:00, 63.95it/s, val_loss=0.194]\n", + "Epoch 89: : 534it [00:26, 20.19it/s, loss=0.527] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 89: : 17it [00:00, 62.93it/s, val_loss=0.175]\n", + "Epoch 90: : 534it [00:26, 20.35it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 90: : 17it [00:00, 63.62it/s, val_loss=0.173]\n", + "Epoch 91: : 534it [00:26, 20.25it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 91: : 17it [00:00, 63.33it/s, val_loss=0.167]\n", + "Epoch 92: : 534it [00:26, 20.20it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 92: : 17it [00:00, 61.01it/s, val_loss=0.183]\n", + "Epoch 93: : 534it [00:26, 20.18it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 93: : 17it [00:00, 61.76it/s, val_loss=0.179]\n", + "Epoch 94: : 534it [00:26, 20.31it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 94: : 17it [00:00, 63.02it/s, val_loss=0.152]\n", + "Epoch 95: : 534it [00:26, 20.17it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 95: : 17it [00:00, 63.23it/s, val_loss=0.148]\n", + "Epoch 96: : 534it [00:26, 20.11it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 96: : 17it [00:00, 63.35it/s, val_loss=0.154]\n", + "Epoch 97: : 534it [00:26, 20.35it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 97: : 17it [00:00, 62.97it/s, val_loss=0.17] \n", + "Epoch 98: : 534it [00:26, 20.25it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 98: : 17it [00:00, 62.36it/s, val_loss=0.138]\n", + "Epoch 99: : 534it [00:26, 20.22it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 99: : 17it [00:00, 63.30it/s, val_loss=0.193]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train completed, total time: 2708.850436449051.\n" + ] + }, + { + "data": { + "image/png": "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QQ0bNkSLFi3w77//4uLFi1i5ciWUSiVGjBgBgB9+Sk5ORqtWrawyJkePHkVmZmahz/3YY49Bp9Ph6NGjVrOwzGYz/v33X6vXXLt2DYGBgVbBUEZGBo4fP261v5BcKEiGpmnTpgBgc8mQvXv3AgCaNGni8PFcye0DorNnz6Ju3bpiKk3QqFEj8Xl79u3bh7p162LdunV47LHHoFQqER4ejvfee89th8wAoG5kbtfqvaeA9ML/GyKEEOJEQuPFF198EdevX0fv3r3FGpyQkBB4eHjg+PHjyMjIEF+TmJhY5A/mGo0Gzz33HB48eICFCxfKnvvuu+9s1g9FRkYiMTER586dE7eZTCa8/fbbePjQuuYrMJAfWrxz547D1yUEg7Nnz0ZKSoq4PSUlBbNnz5bt4+7cfsgsPj4e1atXt9ou/MXFx8fbfe3du3fx8OFDvPHGG5gzZw7q1auHv//+GwsWLEBMTAx++uknu6/V6/WysVXpX3Rx4zigV0tg6Z98cfWek0Cf1iV2ekIIIXZERUXhrbfewoEDBwDIO1MrFAq8+uqrWLhwIRo3box+/fohJSUFW7ZsQWRkJMLCwop07gULFuDvv//GtGnT8M8//6Bp06a4cOECNm/ejO7du2P79u2y/V9//XVs374dbdu2xXPPPQedToc9e/bg7t276Nixo1VWp3Xr1vDw8MDnn3+OlJQUMcv13nvv2b2m9u3b4/XXX8fixYvRoEEDPPPMM2CMYf369YiJicEbb7wh1le5O7fPEAGQdcosyHNmsxmpqan46quvMH78eHTq1Alz587F66+/jtWrV+Pq1at2Xzt//nz4+fmJX/batReXni1zH9OwGSGEuAdfX188++yzAPii4T59+sienz9/PubNmweO4/DVV19hx44dGDJkCLZv316k6e0Av3TGv//+i8GDB+PgwYNYtGgR4uPjsWPHDrRubf2puW/fvvjtt99QvXp1rFq1CqtXr0adOnVw+PBhREZaF6cGBgbit99+Q61atfD1119jypQpmDJlSr7X9cUXX2D58uUIDQ3FsmXL8O233yI0NBTLly/HokWLivQzlySOMRttN91I69atYTKZcPjwYdn2c+fOoUGDBli6dKnd6fOVKlXC/fv3kZCQgICAAHH79u3b0aNHD/zyyy947rnnbL7WVoYoIiICycnJJVIglpoBBD0FGIxA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CCJMoYJECywLLe8SuvFCLzIwjKjwonNJSQQWYRYRqEEQWFamSSXigVTMXE9lh/e7iB3abzXtb2kyzw8WlnfmnXlnLpaH3ZnZ0NBQWbBggfj7+0thYaGMj4//sZk1Ij89C56IiIhIhXgNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxER0QxpNBr+4znRPMdARESzKjAwUAkQv3r9//8WERH9Tc5zPQARqVNISAiWLVs26XIvL69ZnIaI1I6BiIjmxJEjR7B79+65HoOICAB/MiMiIiJiICKif9/PFy1XVFRg1apVMBgM8PT0RGpqKl6+fDlp78jICIqLixEZGQm9Xg93d3esXr0ap06dwtevXyft+/DhAwoLCxETEwN3d3cYDAaYzWbs378fT58+nbSvuroa8fHxMBqN8PDwwObNmyddv729HTk5OQgKCoKLiwuMRiOCgoKwbds2XLt2bZpnh4j+CCEimkUBAQECQM6fPz/tHgACQEpKSgSAeHt7y8qVK8VoNAoAWbhwodTV1Tn09fb2isViEQCi1WolMjJSzGazsr3ExEQZGxtz6Hv27Jn4+voqfeHh4RIdHS3u7u4CQLKysiac78yZM6LRaMTHx0diY2NFr9cLADEYDNLc3GzX09bWJkuWLBEA4ubmJhaLRaKjo8XT01MASFRU1LTPDxHNHAMREc2qmQQinU4npaWl8u3bNxERGRkZkV27dgkACQgIkNHRUbu+9PR0ASARERHy5s0bpf7kyRPx8vISAHL48GG7nsHBQfH39xcAkpKSIh0dHXbLa2tr5cqVKxPO5+bmZndcVqtVEhISBIDs2LHDricvL08JV0NDQ3bLmpubpby8fNrnh4hmjoGIiGaVLRBN9RoYGFB6bLWtW7c6bO/z58/i7e0tAOTcuXNKvbW1VTQajQCQhoYGh77KykoBIHq9XqxWq1I/duyYABCz2SyfPn2a1jHZ5jt48KDDsufPnwsA8fDwsKsnJycLAGlsbJzWPojo7+JdZkQ0J6a67d7Z2fHjKTc316G2YMECZGdno7i4GDU1NdizZw8A4P79+xARrF27FjExMQ596enpMJlM6OzsxKNHj5CSkgIAqKqqAgDk5+fDxcXlt44pOzvboWaxWODq6orBwUH09/dj8eLFAAA/Pz8AwI0bN2CxWPhgR6I5xkBERHPiv9x2bzabf1lvbW1Varb34eHhE/ZotVqEhYWhs7MTra2tSiBqbm4GAMTFxf3WbAAQHBw8YX3p0qXo6OjA8PCwEohyc3Nx8eJFFBUV4dKlS0hJScG6deuwceNG+Pr6/va+iWhmeJcZEc0bk32jZHuI49DQkFIbHh7+Zc9kfVarFQCwaNGi355Pr9dPWNdqf3zUiohSi46ORm1tLZKSktDV1YXy8nJkZGTAZDIhOTlZCWZENDsYiIho3ujr65uw3tvbCwAwGo1KzWAw2C2bSE9Pj0Of7f3Hjx9nNOt0xMXFoaamBgMDA7h79y4KCgpgMplw7949JCYmzsoMRPQDAxERzRuTfWtiq4eGhio12/tXr15N2PP9+3e0tLQ49EVERAAAHj9+PPOBp8lgMCA5ORlHjx5FS0sLgoOD0dXVherq6lmbgUjtGIiIaN44ffq0Q218fBxnz54FACQlJSn1pKQkaDQa1NfXT/hgxJs3b6KzsxN6vR5r1qxR6qmpqQCAkydPYnx8/A8fwdTc3NxgsVgAAN3d3bO+fyK1YiAionnjzp07OHHihHItztjYGPbu3Yvu7m74+flh586dyrrLly9HWloaACAzMxPv3r1TljU0NODQoUMAgLy8PLufzPbt24eAgAA0NTUhLS0NXV1ddjPU19fj6tWrMz6WAwcO4Pr16xgdHbWr19bW4sGDBwCA2NjYGe+HiKZHIz9f5UdE9JcFBgaivb19ytvut2/froQW2y3pJSUlKCgogLe3N/z8/PD69WtYrVa4urqipqYG8fHxdtvo6+tDQkICXrx4AScnJ6xYsQJfvnxRfkbbtGkTbt++DVdXV7u+xsZGpKSk4P3799BqtTCbzdDpdGhra8Pg4CCysrJw4cIFZX3bfJN9nNqOua2tDYGBgQB+XFTd2NgIZ2dnhISEwGg0oqenB+3t7QCAjIwMXL58eZpnlYhmioGIiGaVLRxMJT8/H2VlZQDsA0dFRQXKysrQ1NQEnU6H9evXo6ioCJGRkRNuZ2RkBMePH0dlZSXevn0LrVaL8PBwZGZmIicnBzqdbsK+/v5+lJaW4tatW2hra4OTkxNMJhM2bNiAnJwcREVFKev+l0D08OFDVFVVoa6uDh0dHRgcHISPjw/CwsKQm5uLLVu28NlERLOIgYiI/nlTBQ4iopniNURERESkegxEREREpHoMRERERKR6DERERESkevxzVyL65/FiaiL62/gNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqd7/AL3d+lWfpCm1AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "batch_size = 32\n", + "n_epochs = 100\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", + "\n", + "classifier.to(device)\n", + "weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset\n", + "\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " classifier.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + "\n", + " for step, (a, b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + "\n", + " optimizer_cls.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=False):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image\n", + " pred = classifier(noisy_img, timesteps)\n", + " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", + "\n", + " loss.backward()\n", + " optimizer_cls.step()\n", + "\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + " print(\"final step train\", step)\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " classifier.eval()\n", + " val_epoch_loss = 0\n", + " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " timesteps = torch.randint(0, 1, (len(images),)).to(\n", + " device\n", + " ) # check validation accuracy on the original images, i.e., do not add noise\n", + "\n", + " with torch.no_grad():\n", + " with autocast(enabled=False):\n", + " noise = torch.randn_like(images).to(device)\n", + " pred = classifier(images, timesteps)\n", + " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " _, predicted = torch.max(pred, 1)\n", + " progress_bar_val.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train completed, total time: {total_time}.\")\n", + "\n", + " ## Learning curves for the Classifier\n", + "\n", + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "# Image-to-image translation to a healthy subject\n", + "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "DiffusionModelEncoder(\n", + " (conv_in): Convolution(\n", + " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_embed): Sequential(\n", + " (0): Linear(in_features=32, out_features=128, bias=True)\n", + " (1): SiLU()\n", + " (2): Linear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (down_blocks): ModuleList(\n", + " (0): DownBlock(\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", + " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (1): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (2): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (out): Sequential(\n", + " (0): Linear(in_features=4096, out_features=512, bias=True)\n", + " (1): ReLU()\n", + " (2): Dropout(p=0.1, inplace=False)\n", + " (3): Linear(in_features=512, out_features=2, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed\n", + "inputlabel = total_val_labels[120] # Check whether it is healthy or diseased\n", + "\n", + "plt.figure(\"input\" + str(inputlabel))\n", + "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "model.eval()\n", + "classifier.eval()" + ] + }, + { + "cell_type": "markdown", + "id": "0cd48c2d", + "metadata": {}, + "source": [ + "### Encoding the input image in noise with the reversed DDIM sampling scheme\n", + "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\\\n", + "We define the number of steps in the noising and denoising process by L.\\\n", + "The encoding process is presented in Equation 6 of the paper \"Diffusion Models for Medical Anomaly Detection\" (https://arxiv.org/pdf/2203.04306.pdf).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "f71e4924", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 49.96it/s]\n" + ] + }, + { + "data": { + "image/png": "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ABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXXz2Isin76NGj3ri12b1GjRox/509e/bI3PXXXy9zJUqU8MYrV64sa4YPH57zC8uBjz76yBu3RuStnHosrK396nlSG9+dizb2v2LFClljadeuXUx/xzk9nm5tis+XL5/MqeMZ1uNg/YJH9+7dvfH7779f1mzevNkbX7lypazZuXOnzCnvvvuuzJUtW1bmOnXq5I3feuutskYdiVHj+87pXx5xTv8CivU8tWzZUubUMSTrV0QaNGjgjT///POyRv1Kh3POLVq0SOaUS+F4QUJCgsylpKR449bRg5zgmx0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXaczExMSYa4oVK+aNW0tUv/32W5lTk1HWIlwrpyxcuFDmnn32WW9cLal2zrlKlSrJnFpQa02PvfzyyzLXt29fb3z16tWypkyZMt74Qw89JGusSci6det649YEW/PmzWVuzpw53nivXr1kzQcffOCNR1nO7Jxzp06d8sat12vJkiVlrmPHjt74kCFDZI2adlS35Zz9PGVnZ3vj1sTlgQMHZK5nz57eeLdu3WSNmkrduHGjrBk3bpzMTZgwwRtXU5rO2YvV27Rp442/+eabsgb/zXrtWcvxzwff7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQPJodACB4cTl6oEavLWp5tLVwVy0ltnL58+eXNWq82vL+++/LnBontxZEv/TSSzJnLYdVWrRoIXNqtL5QoUIx/52srCyZK1eunMypRcK33367rPnyyy9lTh09GDNmjKxRx1Rq1aolaz7++GOZq1ixoswpr7/+usz9/ve/j/n21qxZ441bI97qWIlzzu3fv98b79evn6zJnVv/31ldR9u2bWWNYh1XyJs3r8x16dIl5r81efJkmUtKSor59vDL8uSJS1v6X/hmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMHLdc6aTf75PzQ2wjdq1MgbX7p0qaxR4+7WSHvRokVlLj09XeaUtLQ0mcvIyIj59h599FFv3NqqHm/W3/rNb37jjaempsqa66677ryv6efULy+88sorcf07FvU43H///bJm4MCBMte5c2dvfMeOHbLG+luLFy/2xidOnChr1HOYkpIia5YsWSJz6vhIkyZNZI31fmrXrp03bh2VUc+T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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "L = 200\n", + "current_img = inputimg[None, None, ...].to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "\n", + "\n", + "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", + "for t in progress_bar: # go through the noising process\n", + " with autocast(enabled=False):\n", + " with torch.no_grad():\n", + " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " current_img, _ = scheduler.reversed_step(model_output, t, current_img)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a7c8346a-6296-4800-b978-c10fcdf09779", + "metadata": {}, + "source": [ + "### Denoising process using gradient guidance\n", + "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\\\n", + "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \\\n", + "The scale s is used to amplify the gradient." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.16it/s]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "y = torch.tensor(0) # define the desired class label\n", + "scale = 5 # define the desired gradient scale s\n", + "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", + "\n", + "for i in progress_bar: # go through the denoising process\n", + " t = L - i\n", + " with autocast(enabled=True):\n", + " with torch.no_grad():\n", + " model_output = model(\n", + " current_img, timesteps=torch.Tensor((t,)).to(current_img.device)\n", + " ).detach() # this is supposed to be epsilon\n", + "\n", + " with torch.enable_grad():\n", + " x_in = current_img.detach().requires_grad_(True)\n", + " logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " log_probs = F.log_softmax(logits, dim=-1)\n", + " selected = log_probs[range(len(logits)), y.view(-1)]\n", + " a = torch.autograd.grad(selected.sum(), x_in)[0]\n", + " alpha_prod_t = scheduler.alphas_cumprod[t]\n", + " updated_noise = (\n", + " model_output - (1 - alpha_prod_t).sqrt() * scale * a\n", + " ) # update the predicted noise epsilon with the gradient of the classifier\n", + "\n", + " current_img, _ = scheduler.step(updated_noise, t, current_img)\n", + " torch.cuda.empty_cache()\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "# Anomaly Detection\n", + "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "ecffaaf3-a7df-453e-81a9-757113d85084", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy()\n", + "plt.style.use(\"default\")\n", + "plt.imshow(diff, cmap=\"jet\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "jupytext": { + "formats": "py:percent,ipynb" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py new file mode 100644 index 00000000..6c25ce87 --- /dev/null +++ b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py @@ -0,0 +1,553 @@ +# --- +# jupyter: +# jupytext: +# formats: py:percent,ipynb +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.14.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# %% [markdown] +# # Diffusion Models for Medical Anomaly Detection with Classifier Guidance +# +# This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. +# +# We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\ +# We then tranlsate an input slice to its healthy reconstruction using DDIMs.\ +# Anomaly detection is performed by taking the difference between input and output, as proposed in [1]. +# +# [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 +# +# ## Setup environment + +# %% +# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" +# !python -c "import matplotlib" || pip install -q matplotlib +# !python -c "import seaborn" || pi resblock_updown: bool = False,p install -q seaborn + +# %% [markdown] +# ## Setup imports + +# %% jupyter={"outputs_hidden": false} +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import time +from typing import Dict +import tempfile +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from monai import transforms +from monai.apps import DecathlonDataset +from monai.config import print_config +from monai.data import DataLoader +from monai.utils import set_determinism +from torch.cuda.amp import GradScaler, autocast +from tqdm import tqdm + +from generative.inferers import DiffusionInferer +from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet +from generative.networks.schedulers.ddim import DDIMScheduler + + +torch.multiprocessing.set_sharing_strategy("file_system") +print_config() + + +# %% [markdown] +# ## Setup data directory + +# %% jupyter={"outputs_hidden": false} +directory = os.environ.get("MONAI_DATA_DIRECTORY") +root_dir = tempfile.mkdtemp() if directory is None else directory + +# %% [markdown] +# ## Set deterministic training for reproducibility + +# %% jupyter={"outputs_hidden": false} +set_determinism(42) + +# %% [markdown] tags=[] +# ## Preprocessing of the BRATS Dataset in 2D slices for training +# We download the BRATS training dataset from the Decathlon dataset. \ +# We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\ +# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_. +# + +# %% [markdown] +# Here we use transforms to augment the training dataset, as usual: +# +# 1. `LoadImaged` loads the hands images from files. +# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. +# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. +# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# +# To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain. + +# %% +channel = 0 # 0 = Flair +assert channel in [0, 1, 2, 3], "Choose a valid channel" + +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image", "label"]), + transforms.EnsureChannelFirstd(keys=["image", "label"]), + transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), + transforms.AddChanneld(keys=["image"]), + transforms.EnsureTyped(keys=["image", "label"]), + transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), + transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), + transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), + transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), + transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), + transforms.Lambdad( + keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() + ), + ] +) + +def get_batched_2d_axial_slices(data: Dict): + images_3D = data["image"] + batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. + slice_label = data["slice_label"] + slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() + return batched_2d_slices, slice_label + + + +# %% jupyter={"outputs_hidden": false} + +train_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="training", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=True, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) +print("len train data", len(train_ds)) #this gives the number of patients in the training set + + + +train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) +data_2d_slices = [] +data_slice_label = [] +for i, data in enumerate(train_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices.append(b2d) + data_slice_label.append(slice_label2d) + +total_train_slices = torch.cat(data_2d_slices, 0) +total_train_labels = torch.cat(data_slice_label, 0) + +# %% [markdown] tags=[] +# ## Preprocessing of the BRATS Dataset in 2D slices for validation +# We download the BRATS validation dataset from the Decathlon dataset. +# We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_. +# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_. +# + +# %% +val_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="validation", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=True, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) + + +val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) +data_2d_slices_val = [] +data_slice_label_val = [] +for i, data in enumerate(val_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices_val.append(b2d) + data_slice_label_val.append(slice_label2d) + +total_val_slices = torch.cat(data_2d_slices_val, 0) +total_val_labels = torch.cat(data_slice_label_val, 0) + + +# %% [markdown] +# ## Define network, scheduler, optimizer, and inferer +# At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using +# the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms +# in the 3rd level (`num_head_channels=64`). +# + +# %% jupyter={"outputs_hidden": false} +device = torch.device("cuda") + +model = DiffusionModelUNet( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(64, 64, 64), + attention_levels=(False, False, True), + num_res_blocks=1, + num_head_channels=64, + with_conditioning=False, +) +model.to(device) + +scheduler = DDIMScheduler(num_train_timesteps=1000) + +optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) + +inferer = DiffusionInferer(scheduler) + + +# %% [markdown] tags=[] +# ## Model training of the diffusion model +# We train our diffusion model for 100 epochs, with a batch size of 32. + +# %% jupyter={"outputs_hidden": false} +n_epochs = 100 +batch_size = 32 +val_interval = 1 +epoch_loss_list = [] +val_epoch_loss_list = [] + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # + + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + optimizer.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer( + inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: + images = a.to(device) + classes = b.to(device) + + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train diffusion completed, total time: {total_time}.") + +plt.style.use("seaborn-bright") +plt.title("Learning Curves Diffusion Model", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() + + +# %% [markdown] +# ## Check the performance of the diffusion model +# +# We generate a random image from noise to check whether our diffusion model works properly for an image generation task. +# +# + +# %% +model.eval() +noise = torch.randn((1, 1, 64, 64)) +noise = noise.to(device) +scheduler.set_timesteps(num_inference_steps=1000) +with autocast(enabled=True): + image, intermediates = inferer.sample( + input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100 + ) + +chain = torch.cat(intermediates, dim=-1) + +plt.style.use("default") +plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# ## Define the classification model +# First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. +# + +# %% +classifier = DiffusionModelEncoder( + spatial_dims=2, + in_channels=1, + out_channels=2, + num_channels=(32, 64, 64), + attention_levels=(False, True, True), + num_res_blocks=(1,1,1), + num_head_channels=64, + with_conditioning=False, +) +classifier.to(device) + + + +# %% [markdown] +# ## Model training of the classification model +# We train our classification model for 100 epochs. +# + +# %% +batch_size = 32 +n_epochs = 100 +val_interval = 1 +epoch_loss_list = [] +val_epoch_loss_list = [] +optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) + +classifier.to(device) +weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset + + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + classifier.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + + optimizer_cls.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + + with autocast(enabled=False): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image + pred = classifier(noisy_img, timesteps) + loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") + + loss.backward() + optimizer_cls.step() + + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + print("final step train", step) + + if (epoch + 1) % val_interval == 0: + classifier.eval() + val_epoch_loss = 0 + subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar_val.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: + images = a.to(device) + classes = b.to(device) + timesteps = torch.randint(0, 1, (len(images),)).to( + device + ) # check validation accuracy on the original images, i.e., do not add noise + + with torch.no_grad(): + with autocast(enabled=False): + noise = torch.randn_like(images).to(device) + pred = classifier(images, timesteps) + val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") + + val_epoch_loss += val_loss.item() + _, predicted = torch.max(pred, 1) + progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train completed, total time: {total_time}.") + + ## Learning curves for the Classifier + +plt.style.use("seaborn-bright") +plt.title("Learning Curves", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", + ) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() +# %% [markdown] +# # Image-to-image translation to a healthy subject +# We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. + +# %% + +inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed +inputlabel = total_val_labels[120] # Check whether it is healthy or diseased + +plt.figure("input" + str(inputlabel)) +plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + +model.eval() +classifier.eval() + +# %% [markdown] +# ### Encoding the input image in noise with the reversed DDIM sampling scheme +# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\ +# We define the number of steps in the noising and denoising process by L.\ +# The encoding process is presented in Equation 6 of the paper "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/pdf/2203.04306.pdf). +# + +# %% jupyter={"outputs_hidden": false} +L = 200 +current_img = inputimg[None, None, ...].to(device) +scheduler.set_timesteps(num_inference_steps=1000) + + +progress_bar = tqdm(range(L)) # go back and forth L timesteps +for t in progress_bar: # go through the noising process + with autocast(enabled=False): + with torch.no_grad(): + model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) + current_img, _ = scheduler.reversed_step(model_output, t, current_img) + +plt.style.use("default") +plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# ### Denoising process using gradient guidance +# From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\ +# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \ +# The scale s is used to amplify the gradient. + +# %% + + +y = torch.tensor(0) # define the desired class label +scale = 5 # define the desired gradient scale s +progress_bar = tqdm(range(L)) # go back and forth L timesteps + +for i in progress_bar: # go through the denoising process + t = L - i + with autocast(enabled=True): + with torch.no_grad(): + model_output = model( + current_img, timesteps=torch.Tensor((t,)).to(current_img.device) + ).detach() # this is supposed to be epsilon + + with torch.enable_grad(): + x_in = current_img.detach().requires_grad_(True) + logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device)) + log_probs = F.log_softmax(logits, dim=-1) + selected = log_probs[range(len(logits)), y.view(-1)] + a = torch.autograd.grad(selected.sum(), x_in)[0] + alpha_prod_t = scheduler.alphas_cumprod[t] + updated_noise = ( + model_output - (1 - alpha_prod_t).sqrt() * scale * a + ) # update the predicted noise epsilon with the gradient of the classifier + + current_img, _ = scheduler.step(updated_noise, t, current_img) + torch.cuda.empty_cache() + +plt.style.use("default") +plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# # Anomaly Detection +# To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction. + + +# %% + +diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() +plt.style.use("default") +plt.imshow(diff, cmap="jet") +plt.tight_layout() +plt.axis("off") +plt.show() From 72922f0e7724c389158ace2373fd8582f74849b1 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 15 Mar 2023 09:23:05 +0100 Subject: [PATCH 08/23] remove old folder --- ...r_guidance_anomalydetection_tutorial.ipynb | 2728 ----------------- ...fier_guidance_anomalydetection_tutorial.py | 589 ---- 2 files changed, 3317 deletions(-) delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb delete mode 100644 tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb deleted file mode 100644 index 4a5f4384..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.ipynb +++ /dev/null @@ -1,2728 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "63d95da6", - "metadata": {}, - "source": [ - "# Weakly Supervised Anomaly Detection with Classifier Guidance\n", - "\n", - "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", - "\n", - "\n", - "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", - "\n", - "\n", - "TODO: Add Open in Colab\n", - "\n", - "## Setup environment" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "75f2d5f3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "running install\n", - "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n", - " warnings.warn(\n", - "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/easy_install.py:144: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n", - " warnings.warn(\n", - "running bdist_egg\n", - "running egg_info\n", - "writing generative.egg-info/PKG-INFO\n", - "writing dependency_links to generative.egg-info/dependency_links.txt\n", - "writing requirements to generative.egg-info/requires.txt\n", - "writing top-level names to generative.egg-info/top_level.txt\n", - "reading manifest file 'generative.egg-info/SOURCES.txt'\n", - "writing manifest file 'generative.egg-info/SOURCES.txt'\n", - "installing library code to build/bdist.linux-x86_64/egg\n", - "running install_lib\n", - "warning: install_lib: 'build/lib' does not exist -- no Python modules to install\n", - "\n", - "creating build/bdist.linux-x86_64/egg\n", - "creating build/bdist.linux-x86_64/egg/EGG-INFO\n", - "copying generative.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO\n", - "copying generative.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", - "copying generative.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", - "copying generative.egg-info/requires.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", - "copying generative.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", - "zip_safe flag not set; analyzing archive contents...\n", - "creating 'dist/generative-0.1.0-py3.10.egg' and adding 'build/bdist.linux-x86_64/egg' to it\n", - "removing 'build/bdist.linux-x86_64/egg' (and everything under it)\n", - "Processing generative-0.1.0-py3.10.egg\n", - "Removing /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", - "Copying generative-0.1.0-py3.10.egg to /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "generative 0.1.0 is already the active version in easy-install.pth\n", - "\n", - "Installed /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", - "Processing dependencies for generative==0.1.0\n", - "Searching for lpips==0.1.4\n", - "Best match: lpips 0.1.4\n", - "Processing lpips-0.1.4-py3.10.egg\n", - "lpips 0.1.4 is already the active version in easy-install.pth\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg\n", - "Searching for tqdm==4.64.1\n", - "Best match: tqdm 4.64.1\n", - "Processing tqdm-4.64.1-py3.10.egg\n", - "tqdm 4.64.1 is already the active version in easy-install.pth\n", - "Installing tqdm script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg\n", - "Searching for scipy==1.9.0\n", - "Best match: scipy 1.9.0\n", - "Adding scipy 1.9.0 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for numpy==1.23.2\n", - "Best match: numpy 1.23.2\n", - "Adding numpy 1.23.2 to easy-install.pth file\n", - "Installing f2py script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "Installing f2py3 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "Installing f2py3.10 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for torchvision==0.13.1\n", - "Best match: torchvision 0.13.1\n", - "Adding torchvision 0.13.1 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for torch==1.12.1\n", - "Best match: torch 1.12.1\n", - "Adding torch 1.12.1 to easy-install.pth file\n", - "Installing convert-caffe2-to-onnx script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "Installing convert-onnx-to-caffe2 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "Installing torchrun script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for Pillow==9.2.0\n", - "Best match: Pillow 9.2.0\n", - "Adding Pillow 9.2.0 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for requests==2.28.1\n", - "Best match: requests 2.28.1\n", - "Adding requests 2.28.1 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for typing-extensions==4.3.0\n", - "Best match: typing-extensions 4.3.0\n", - "Adding typing-extensions 4.3.0 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for certifi==2022.6.15\n", - "Best match: certifi 2022.6.15\n", - "Adding certifi 2022.6.15 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for urllib3==1.26.11\n", - "Best match: urllib3 1.26.11\n", - "Adding urllib3 1.26.11 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for idna==3.3\n", - "Best match: idna 3.3\n", - "Adding idna 3.3 to easy-install.pth file\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Searching for charset-normalizer==2.1.1\n", - "Best match: charset-normalizer 2.1.1\n", - "Adding charset-normalizer 2.1.1 to easy-install.pth file\n", - "Installing normalizer script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", - "\n", - "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", - "Finished processing dependencies for generative==0.1.0\n" - ] - } - ], - "source": [ - "!python /home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/setup.py install\n", - "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", - "!python -c \"import matplotlib\" || pip install -q matplotlib\n", - "!python -c \"import seaborn\" || pip install -q seaborn" - ] - }, - { - "cell_type": "markdown", - "id": "6b766027", - "metadata": {}, - "source": [ - "## Setup imports" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "972ed3f3", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/generative/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", - "MONAI version: 1.1.dev2248\n", - "Numpy version: 1.23.2\n", - "Pytorch version: 1.12.1\n", - "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", - "MONAI rev id: 3400bd91422ccba9ccc3aa2ffe7fecd4eb5596bf\n", - "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", - "\n", - "Optional dependencies:\n", - "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", - "Nibabel version: 4.0.1\n", - "scikit-image version: 0.19.3\n", - "Pillow version: 9.2.0\n", - "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n", - "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", - "TorchVision version: 0.13.1\n", - "tqdm version: 4.64.1\n", - "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", - "psutil version: 5.9.4\n", - "pandas version: 1.5.3\n", - "einops version: 0.6.0\n", - "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", - "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", - "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", - "\n", - "For details about installing the optional dependencies, please visit:\n", - " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", - "\n" - ] - } - ], - "source": [ - "# Copyright 2020 MONAI Consortium\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "# http://www.apache.org/licenses/LICENSE-2.0\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License.\n", - "import os\n", - "import shutil\n", - "import tempfile\n", - "import time\n", - "from typing import Dict\n", - "import os\n", - "import torch.nn as nn\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import torch\n", - "import torch.nn.functional as F\n", - "from monai import transforms\n", - "from monai.apps import MedNISTDataset, DecathlonDataset\n", - "from monai.config import print_config\n", - "from monai.data import CacheDataset, DataLoader\n", - "from monai.utils import first, set_determinism\n", - "from torch.cuda.amp import GradScaler, autocast\n", - "from tqdm import tqdm\n", - "torch.multiprocessing.set_sharing_strategy('file_system')\n", - "import sys\n", - "sys.path.append('/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/')\n", - "print('path', sys.path)\n", - "\n", - "from generative.inferers import DiffusionInferer\n", - "\n", - "\n", - "# TODO: Add right import reference after deployed\n", - "from generative.networks.nets.diffusion_model_unet import DiffusionModelUNet, DiffusionModelEncoder\n", - "from generative.networks.schedulers.ddpm import DDPMScheduler\n", - "from generative.networks.schedulers.ddim import DDIMScheduler\n", - "print_config()" - ] - }, - { - "cell_type": "markdown", - "id": "7d4ff515", - "metadata": {}, - "source": [ - "## Setup data directory" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "8b4323e7", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", - "#root_dir = tempfile.mkdtemp() if directory is None else directory\n", - "root_dir='/home/juliawolleb/PycharmProjects/MONAI/brats' #path to where the data is stored" - ] - }, - { - "cell_type": "markdown", - "id": "99175d50", - "metadata": {}, - "source": [ - "## Set deterministic training for reproducibility" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "34ea510f", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "set_determinism(42)" - ] - }, - { - "cell_type": "markdown", - "id": "c3f70dd1-236a-47ff-a244-575729ad92ba", - "metadata": { - "tags": [] - }, - "source": [ - "## Setup BRATS Dataset in 2D slices for training\n", - "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150.\n", - "If we set `preprocessing_train=True`, we stack all slices into a tensor and save it as _total_train_slices.pt_. \n", - "If we set `preprocessing_train=False`, we load the saved tensor.\n", - "The corresponding labels are saved as _total_train_labels.pt._" - ] - }, - { - "cell_type": "markdown", - "id": "6986f55c", - "metadata": {}, - "source": [ - "Here we use transforms to augment the training dataset, as usual:\n", - "\n", - "1. `LoadImaged` loads the hands images from files.\n", - "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", - "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", - "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/utils/deprecate_utils.py:107: FutureWarning: : Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n", - " warn_deprecated(obj, msg, warning_category)\n" - ] - } - ], - "source": [ - "channel = 0 # 0 = Flair\n", - "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", - "\n", - "train_transforms = transforms.Compose(\n", - " [\n", - " transforms.LoadImaged(keys=[\"image\",\"label\"]),\n", - " transforms.EnsureChannelFirstd(keys=[\"image\",\"label\"]),\n", - " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", - " transforms.AddChanneld(keys=[\"image\"]),\n", - " transforms.EnsureTyped(keys=[\"image\",\"label\"]),\n", - " transforms.Orientationd(keys=[\"image\",\"label\"], axcodes=\"RAS\"),\n", - " transforms.Spacingd(\n", - " keys=[\"image\",\"label\"],\n", - " pixdim=(3.0, 3.0, 2.0),\n", - " mode=(\"bilinear\", \"nearest\"),\n", - " ),\n", - " transforms.CenterSpatialCropd(keys=[\"image\",\"label\"], roi_size=(64, 64, 64)),\n", - " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", - " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", - " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0 ).float().squeeze()),\n", - " ]\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "da1927b0", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "len train data 388\n", - "total slices torch.Size([17072, 1, 64, 64])\n", - "total lbaels torch.Size([17072])\n" - ] - } - ], - "source": [ - "\n", - "train_ds = DecathlonDataset(\n", - " root_dir=root_dir,\n", - " task=\"Task01_BrainTumour\",\n", - " section=\"training\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", - " num_workers=4,\n", - " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", - " seed=0,\n", - " transform=train_transforms,\n", - ")\n", - "print('len train data', len(train_ds))\n", - "\n", - "def get_batched_2d_axial_slices(data : Dict):\n", - " images_3D = data['image']\n", - " batched_2d_slices = torch.cat(images_3D.split(1, dim = -1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", - " slice_label = data['slice_label']\n", - " slice_label = torch.cat(slice_label.split(1, dim = -1)[10:-10],0).squeeze()\n", - " return batched_2d_slices, slice_label\n", - "\n", - "preprocessing_train=False\n", - "\n", - "if preprocessing_train == True:\n", - " train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", - " print(f'Image shape {train_ds[0][\"image\"].shape}')\n", - "\n", - " data_2d_slices=[]\n", - " data_slice_label = []\n", - " check_data = first(train_loader_3D)\n", - " for i, data in enumerate(train_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices.append(b2d)\n", - " data_slice_label.append(slice_label2d)\n", - " total_train_slices=torch.cat(data_2d_slices,0)\n", - " total_train_labels=torch.cat(data_slice_label,0)\n", - "\n", - " torch.save(total_train_slices, 'total_train_slices.pt')\n", - " torch.save(total_train_labels, 'total_train_labels.pt')\n", - "\n", - "else:\n", - " total_train_slices=torch.load('total_train_slices.pt')\n", - " total_train_labels=torch.load('total_train_labels.pt')\n", - " print('total slices', total_train_slices.shape)\n", - " print('total lbaels', total_train_labels.shape)\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "fac55e9d", - "metadata": { - "tags": [] - }, - "source": [ - "## Setup BRATS Dataset in 2D slices for validation \n", - "As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150.\n", - "If we set `preprocessing_val=True`, we stack all slices into a tensor and save it as _total_val_slices.pt_.\n", - "If we set `preprocessing_val=False`, we load the saved tensor.\n", - "The corresponding labels are saved as _total_val_labels.pt_." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total slices torch.Size([4224, 1, 64, 64])\n", - "total lbaels torch.Size([4224])\n" - ] - } - ], - "source": [ - "val_ds = DecathlonDataset(\n", - " root_dir=root_dir,\n", - " task=\"Task01_BrainTumour\",\n", - " section=\"validation\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", - " num_workers=4,\n", - " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", - " seed=0,\n", - " transform=train_transforms,\n", - ")\n", - "\n", - "\n", - "preprocessing_val=False\n", - "if preprocessing_val == True:\n", - " val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", - " print(f'Image shape {val_ds[0][\"image\"].shape}')\n", - " print('len val data', len(val_ds))\n", - " data_2d_slices_val=[]\n", - " data_slice_label_val = []\n", - " for i, data in enumerate(val_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices_val.append(b2d)\n", - " data_slice_label_val.append(slice_label2d)\n", - " total_val_slices=torch.cat(data_2d_slices_val,0)\n", - " total_val_labels=torch.cat(data_slice_label_val,0)\n", - " torch.save(total_val_slices, 'total_val_slices.pt')\n", - " torch.save(total_val_labels, 'total_val_labels.pt')\n", - "\n", - "else:\n", - " total_val_slices=torch.load('total_val_slices.pt')\n", - " total_val_labels=torch.load('total_val_labels.pt')\n", - " print('total slices', total_val_slices.shape)\n", - " print('total lbaels', total_val_labels.shape)" - ] - }, - { - "cell_type": "markdown", - "id": "08428bc6", - "metadata": {}, - "source": [ - "### Define network, scheduler, optimizer, and inferer\n", - "At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", - "the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms\n", - "in the 3rd level (`num_head_channels=64`).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "bee5913e", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "device = torch.device(\"cuda\")\n", - "\n", - "model = DiffusionModelUNet(\n", - " spatial_dims=2,\n", - " in_channels=1,\n", - " out_channels=1,\n", - " num_channels=(64, 64, 64),\n", - " attention_levels=(False, False, True),\n", - " num_res_blocks=1,\n", - " num_head_channels=64,\n", - " with_conditioning=False,\n", - " # cross_attention_dim=1,\n", - ")\n", - "model.to(device)\n", - "\n", - "scheduler = DDIMScheduler(\n", - " num_train_timesteps=1000,\n", - ")\n", - "\n", - "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", - "\n", - "inferer = DiffusionInferer(scheduler)" - ] - }, - { - "cell_type": "markdown", - "id": "2a4d3ab2", - "metadata": { - "tags": [] - }, - "source": [ - "### Model training of the Diffusion Model\n", - "If we set `train_diffusionmodel=True`, we are training our diffusion model for 75 epochs, and save the model as _diffusion_model.pt_.\n", - "If we set `train_diffusionmodel=False`, we load a pretrained model." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "6c0ed909", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "n_epochs =1\n", - "batch_size=32\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "\n", - "train_diffusionmodel=False\n", - "\n", - "if train_diffusionmodel==False:\n", - " model.load_state_dict(torch.load(\"model.pt\", map_location={'cuda:0': 'cpu'}))\n", - "else:\n", - " scaler = GradScaler()\n", - " total_start = time.time()\n", - " for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0])) #shuffle training data new\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - "\n", - " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", - "\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a,b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device) #pick a random time step t\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) #remove the class conditioning\n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - "\n", - " if (epoch) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - "\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - " total_time = time.time() - total_start\n", - " torch.save(model.state_dict(), \"./diffusion_model.pt\") #save the trained model\n", - "\n", - " print(f\"train diffusion completed, total time: {total_time}.\")\n", - "\n", - " plt.style.use(\"seaborn-bright\")\n", - " plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", - " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - " plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - " plt.yticks(fontsize=12)\n", - " plt.xticks(fontsize=12)\n", - " plt.xlabel(\"Epochs\", fontsize=16)\n", - " plt.ylabel(\"Loss\", fontsize=16)\n", - " plt.legend(prop={\"size\": 14})\n", - " plt.show()\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "546f9983-c2e2-4c24-b03a-ebe34627638a", - "metadata": {}, - "source": [ - "## Define the Classification Model\n", - "First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "44cc6928-2525-4e61-8805-15b409097bbb", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "classifier = DiffusionModelEncoder(\n", - " spatial_dims=2,\n", - " in_channels=1,\n", - " out_channels=2,\n", - " num_channels=(32,64,64),\n", - " attention_levels=(False, True, True),\n", - " num_res_blocks=1,\n", - " num_head_channels=64,\n", - " with_conditioning=False,\n", - ")\n", - "classifier.to(device)\n", - "batch_size=32" - ] - }, - { - "cell_type": "markdown", - "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", - "metadata": {}, - "source": [ - "## Model training of the Classification Model\n", - "If we set `train_classifier=True`, we are training our diffusion model for 100 epochs, and save the model as _classifier.pt_.\n", - "If we set `train_classifier=False`, we load a pretrained model." - ] - }, - { - "cell_type": "code", - "execution_count": 110, - "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", - "metadata": { - "lines_to_next_cell": 0 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 534it [00:24, 21.51it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 132it [00:01, 66.23it/s, val_loss=0.259]\n", - "Epoch 1: : 534it [00:27, 19.68it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 1: : 132it [00:02, 57.32it/s, val_loss=0.28] \n", - "Epoch 2: : 534it [00:27, 19.43it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 2: : 132it [00:02, 60.17it/s, val_loss=0.32] \n", - "Epoch 3: : 534it [00:27, 19.41it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 3: : 132it [00:02, 58.98it/s, val_loss=0.294]\n", - "Epoch 4: : 534it [00:27, 19.40it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 4: : 132it [00:02, 59.34it/s, val_loss=0.256]\n", - "Epoch 5: : 534it [00:27, 19.47it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 5: : 132it [00:02, 59.58it/s, val_loss=0.278]\n", - "Epoch 6: : 534it [00:27, 19.49it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 6: : 132it [00:02, 59.91it/s, val_loss=0.29] \n", - "Epoch 7: : 534it [00:27, 19.58it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 7: : 132it [00:02, 59.94it/s, val_loss=0.271]\n", - "Epoch 8: : 534it [00:27, 19.67it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 8: : 132it [00:02, 60.28it/s, val_loss=0.261]\n", - "Epoch 9: : 534it [00:26, 19.84it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 9: : 132it [00:02, 59.37it/s, val_loss=0.243]\n", - "Epoch 10: : 534it [00:27, 19.77it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 10: : 132it [00:02, 60.28it/s, val_loss=0.274]\n", - "Epoch 11: : 534it [00:26, 19.79it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 11: : 132it [00:02, 60.96it/s, val_loss=0.278]\n", - "Epoch 12: : 534it [00:26, 19.95it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 12: : 132it [00:02, 60.47it/s, val_loss=0.292]\n", - "Epoch 13: : 534it [00:26, 19.90it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 13: : 132it [00:02, 59.46it/s, val_loss=0.248]\n", - "Epoch 14: : 534it [00:26, 19.80it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 14: : 132it [00:02, 61.53it/s, val_loss=0.284]\n", - "Epoch 15: : 534it [00:26, 19.92it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 15: : 132it [00:02, 61.58it/s, val_loss=0.273]\n", - "Epoch 16: : 534it [00:26, 19.88it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 16: : 132it [00:02, 61.05it/s, val_loss=0.267]\n", - "Epoch 17: : 534it [00:26, 19.95it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 17: : 132it [00:02, 60.88it/s, val_loss=0.26] \n", - "Epoch 18: : 534it [00:26, 19.83it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 18: : 132it [00:02, 60.91it/s, val_loss=0.318]\n", - "Epoch 19: : 534it [00:26, 20.29it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 19: : 132it [00:02, 63.26it/s, val_loss=0.265]\n", - "Epoch 20: : 534it [00:26, 19.84it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 20: : 132it [00:02, 60.27it/s, val_loss=0.289]\n", - "Epoch 21: : 534it [00:26, 20.11it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 21: : 132it [00:02, 60.49it/s, val_loss=0.27] \n", - "Epoch 22: : 534it [00:26, 19.89it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 22: : 132it [00:02, 60.80it/s, val_loss=0.322]\n", - "Epoch 23: : 534it [00:26, 20.02it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 23: : 132it [00:02, 60.56it/s, val_loss=0.3] \n", - "Epoch 24: : 534it [00:26, 20.01it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 24: : 132it [00:02, 60.69it/s, val_loss=0.287]\n", - "Epoch 25: : 534it [00:26, 20.00it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 25: : 132it [00:02, 61.55it/s, val_loss=0.263]\n", - "Epoch 26: : 534it [00:26, 20.04it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 26: : 132it [00:02, 61.16it/s, val_loss=0.328]\n", - "Epoch 27: : 534it [00:26, 19.95it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 27: : 132it [00:02, 61.15it/s, val_loss=0.263]\n", - "Epoch 28: : 534it [00:26, 20.06it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 28: : 132it [00:02, 60.67it/s, val_loss=0.292]\n", - "Epoch 29: : 534it [00:26, 20.10it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 29: : 132it [00:02, 61.57it/s, val_loss=0.294]\n", - "Epoch 30: : 534it [00:26, 19.98it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 30: : 132it [00:02, 61.60it/s, val_loss=0.284]\n", - "Epoch 31: : 534it [00:26, 20.08it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 31: : 132it [00:02, 61.04it/s, val_loss=0.276]\n", - "Epoch 32: : 534it [00:26, 19.86it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 32: : 132it [00:02, 62.82it/s, val_loss=0.285]\n", - "Epoch 33: : 534it [00:26, 20.13it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 33: : 132it [00:02, 61.12it/s, val_loss=0.277]\n", - "Epoch 34: : 534it [00:26, 20.05it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 34: : 132it [00:02, 61.71it/s, val_loss=0.278]\n", - "Epoch 35: : 534it [00:26, 20.08it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 35: : 132it [00:02, 62.17it/s, val_loss=0.27] \n", - "Epoch 36: : 534it [00:26, 20.21it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 36: : 132it [00:02, 62.01it/s, val_loss=0.267]\n", - "Epoch 37: : 534it [00:26, 20.04it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 37: : 132it [00:02, 61.29it/s, val_loss=0.278]\n", - "Epoch 38: : 534it [00:26, 20.21it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 38: : 132it [00:02, 62.59it/s, val_loss=0.285]\n", - "Epoch 39: : 534it [00:26, 20.13it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 39: : 132it [00:02, 60.36it/s, val_loss=0.279]\n", - "Epoch 40: : 534it [00:26, 20.04it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 40: : 132it [00:02, 61.89it/s, val_loss=0.274]\n", - "Epoch 41: : 534it [00:26, 20.00it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 41: : 132it [00:02, 61.62it/s, val_loss=0.275]\n", - "Epoch 42: : 534it [00:26, 20.11it/s, loss=0.527] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 42: : 132it [00:02, 61.35it/s, val_loss=0.308]\n", - "Epoch 43: : 534it [00:26, 19.83it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 43: : 132it [00:02, 60.97it/s, val_loss=0.31] \n", - "Epoch 44: : 534it [00:26, 20.22it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 44: : 132it [00:02, 61.49it/s, val_loss=0.306]\n", - "Epoch 45: : 534it [00:26, 19.95it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 45: : 132it [00:02, 60.71it/s, val_loss=0.293]\n", - "Epoch 46: : 534it [00:26, 20.02it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 46: : 132it [00:02, 61.20it/s, val_loss=0.254]\n", - "Epoch 47: : 534it [00:26, 19.88it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 47: : 132it [00:02, 61.35it/s, val_loss=0.253]\n", - "Epoch 48: : 534it [00:26, 20.19it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 48: : 132it [00:02, 61.04it/s, val_loss=0.274]\n", - "Epoch 49: : 534it [00:26, 19.95it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 49: : 132it [00:02, 61.74it/s, val_loss=0.28] \n", - "Epoch 50: : 534it [00:26, 20.03it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 50: : 132it [00:02, 59.53it/s, val_loss=0.292]\n", - "Epoch 51: : 534it [00:26, 19.93it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 51: : 132it [00:02, 60.65it/s, val_loss=0.299]\n", - "Epoch 52: : 534it [00:26, 19.89it/s, loss=0.526] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 52: : 132it [00:02, 61.33it/s, val_loss=0.279]\n", - "Epoch 53: : 534it [00:26, 20.04it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 53: : 132it [00:02, 61.07it/s, val_loss=0.282]\n", - "Epoch 54: : 534it [00:26, 19.83it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 54: : 132it [00:02, 60.71it/s, val_loss=0.296]\n", - "Epoch 55: : 534it [00:26, 20.05it/s, loss=0.521] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 55: : 132it [00:02, 60.82it/s, val_loss=0.296]\n", - "Epoch 56: : 534it [00:26, 19.90it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 56: : 132it [00:02, 60.35it/s, val_loss=0.288]\n", - "Epoch 57: : 534it [00:26, 19.92it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 57: : 132it [00:02, 60.57it/s, val_loss=0.279]\n", - "Epoch 58: : 534it [00:27, 19.76it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 58: : 132it [00:02, 61.55it/s, val_loss=0.265]\n", - "Epoch 59: : 534it [00:26, 19.85it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 59: : 132it [00:02, 60.26it/s, val_loss=0.309]\n", - "Epoch 60: : 534it [00:26, 19.94it/s, loss=0.521] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 60: : 132it [00:02, 60.22it/s, val_loss=0.284]\n", - "Epoch 61: : 534it [00:27, 19.57it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 61: : 132it [00:02, 58.50it/s, val_loss=0.267]\n", - "Epoch 62: : 534it [00:27, 19.67it/s, loss=0.527] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 62: : 132it [00:02, 61.49it/s, val_loss=0.278]\n", - "Epoch 63: : 534it [00:27, 19.65it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 63: : 132it [00:02, 59.89it/s, val_loss=0.291]\n", - "Epoch 64: : 534it [00:27, 19.59it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 64: : 132it [00:02, 61.56it/s, val_loss=0.31] \n", - "Epoch 65: : 534it [00:27, 19.39it/s, loss=0.517] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 65: : 132it [00:02, 55.48it/s, val_loss=0.353]\n", - "Epoch 66: : 534it [00:28, 19.05it/s, loss=0.516] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 66: : 132it [00:02, 56.32it/s, val_loss=0.294]\n", - "Epoch 67: : 534it [00:27, 19.12it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 67: : 132it [00:02, 57.70it/s, val_loss=0.303]\n", - "Epoch 68: : 534it [00:27, 19.11it/s, loss=0.521] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 68: : 132it [00:02, 56.41it/s, val_loss=0.278]\n", - "Epoch 69: : 534it [00:27, 19.10it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 69: : 132it [00:02, 58.40it/s, val_loss=0.302]\n", - "Epoch 70: : 534it [00:27, 19.32it/s, loss=0.517] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 70: : 132it [00:02, 59.59it/s, val_loss=0.285]\n", - "Epoch 71: : 534it [00:27, 19.31it/s, loss=0.518] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 71: : 132it [00:02, 58.24it/s, val_loss=0.302]\n", - "Epoch 72: : 534it [00:27, 19.33it/s, loss=0.525] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 72: : 132it [00:02, 59.33it/s, val_loss=0.301]\n", - "Epoch 73: : 534it [00:27, 19.47it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 73: : 132it [00:02, 59.77it/s, val_loss=0.301]\n", - "Epoch 74: : 534it [00:26, 19.83it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 74: : 132it [00:02, 60.28it/s, val_loss=0.321]\n", - "Epoch 75: : 534it [00:26, 19.82it/s, loss=0.523] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 75: : 132it [00:02, 59.62it/s, val_loss=0.3] \n", - "Epoch 76: : 534it [00:26, 19.90it/s, loss=0.518] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 76: : 132it [00:02, 60.27it/s, val_loss=0.292]\n", - "Epoch 77: : 534it [00:26, 19.87it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 77: : 132it [00:02, 60.70it/s, val_loss=0.302]\n", - "Epoch 78: : 534it [00:26, 19.78it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 78: : 132it [00:02, 59.56it/s, val_loss=0.292]\n", - "Epoch 79: : 534it [00:26, 19.85it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 79: : 132it [00:02, 61.08it/s, val_loss=0.29] \n", - "Epoch 80: : 534it [00:27, 19.76it/s, loss=0.518] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 80: : 132it [00:02, 59.27it/s, val_loss=0.305]\n", - "Epoch 81: : 534it [00:26, 19.92it/s, loss=0.517] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 81: : 132it [00:02, 61.01it/s, val_loss=0.314]\n", - "Epoch 82: : 534it [00:27, 19.75it/s, loss=0.515] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 82: : 132it [00:02, 59.88it/s, val_loss=0.309]\n", - "Epoch 83: : 534it [00:26, 19.84it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 83: : 132it [00:02, 59.66it/s, val_loss=0.296]\n", - "Epoch 84: : 534it [00:27, 19.69it/s, loss=0.519] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 84: : 132it [00:02, 59.83it/s, val_loss=0.332]\n", - "Epoch 85: : 534it [00:26, 19.85it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 85: : 132it [00:02, 59.60it/s, val_loss=0.317]\n", - "Epoch 86: : 534it [00:27, 19.77it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 86: : 132it [00:02, 58.63it/s, val_loss=0.302]\n", - "Epoch 87: : 534it [00:26, 19.79it/s, loss=0.519] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 87: : 132it [00:02, 60.47it/s, val_loss=0.296]\n", - "Epoch 88: : 534it [00:27, 19.74it/s, loss=0.515] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 88: : 132it [00:02, 60.28it/s, val_loss=0.312]\n", - "Epoch 89: : 534it [00:26, 19.89it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 89: : 132it [00:02, 60.52it/s, val_loss=0.289]\n", - "Epoch 90: : 534it [00:26, 19.79it/s, loss=0.519] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 90: : 132it [00:02, 59.43it/s, val_loss=0.332]\n", - "Epoch 91: : 534it [00:26, 19.82it/s, loss=0.517] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 91: : 132it [00:02, 60.42it/s, val_loss=0.31] \n", - "Epoch 92: : 534it [00:26, 19.81it/s, loss=0.514] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 92: : 132it [00:02, 59.98it/s, val_loss=0.299]\n", - "Epoch 93: : 534it [00:26, 19.90it/s, loss=0.524] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 93: : 132it [00:02, 61.64it/s, val_loss=0.315]\n", - "Epoch 94: : 534it [00:26, 19.84it/s, loss=0.516] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 94: : 132it [00:02, 60.29it/s, val_loss=0.331]\n", - "Epoch 95: : 534it [00:26, 19.84it/s, loss=0.514] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 95: : 132it [00:02, 61.00it/s, val_loss=0.306]\n", - "Epoch 96: : 534it [00:27, 19.72it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 96: : 132it [00:02, 59.72it/s, val_loss=0.307]\n", - "Epoch 97: : 534it [00:26, 19.99it/s, loss=0.52] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 97: : 132it [00:02, 60.52it/s, val_loss=0.336]\n", - "Epoch 98: : 534it [00:26, 19.83it/s, loss=0.512] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 98: : 132it [00:02, 60.33it/s, val_loss=0.36] \n", - "Epoch 99: : 534it [00:26, 19.87it/s, loss=0.514] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 99: : 132it [00:02, 60.57it/s, val_loss=0.327]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train completed, total time: 2959.368038415909.\n", - "epl 100\n" - ] - }, - { - "data": { - "image/png": "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rVsxbtw41K1bF2FhYRg+fLjs8CwhhUGz7QgpQcRv9itXrnQ442P5RjhhwgRh+O/555/H6NGjUa9ePYSFhcHLy0vo5VO5cmXEx8cX+AleqVQ6820QAPXr18fu3bsBAKdPnxaGEj2Fo7/T/v374/XXX0dOTg5WrVqFdu3aAeBeq2vXrgUAdO3aVTbY5F/P1apVw5YtWxy+tmrVqknuv/DCC+jcuTPWrl2LnTt34tChQ3j06BGSkpKwYsUKrFixAiNGjMDSpUup7om4BQVPhJQgfA0SwBV1161b1+ljpKenC29qQ4YMkRT0WhJnYkoDcZBZUFaksK0K2rVrh88++wwA8Oeff2LgwIGFOh4fFJhMJrv7uXuIMCAgAH369MG6devw22+/YfHixdBoNNi7d68wvGarzo1/PT948ADR0dEONwmVExgYiLFjx2Ls2LEAuBqoLVu2YPHixUhMTERsbCwaNmyICRMmuHwOQngUghNSgjRs2FC4vWvXLpeOce3aNej1egDAoEGDbO535coVZGZmunSOkqpOnTrCbXF9mZyCHi9I165dhRl7v/32GxISEgp1PL6GKjU11e5+fEG8O/HB0ePHj4XO9HwBua+vL/r27Sv7PP71nJ2djSNHjrj1mmrXro333nsPR48eFQry161b59ZzkNKLgidCSpAaNWqgdu3aAIA1a9bgzp07Th/DYDAIt+0thfL99987f4ElXKVKlRAVFQWAC2hsLQmSm5uL3377rVDn0mg0mDx5snC8MWPGOFzndffuXezdu1eyjR/KysjIsBkg5eXlYf369YW4ank9evQQivBXrlyJ3NxcbNiwAQA3LGxrNqE4qFqwYIHbrwvgZg3yv9OCJj4Q4igKnggpYT744AMA3Btu//797Q4f6XQ6fPvtt5IgoEaNGkJNE9+l3NLWrVuxePFiN151yfHaa68B4KbET5kyRXafKVOmIDExsdDnmjBhAjp06ACA64rer18/u79PlmWxcuVKNG7cGOfOnZM8xtcaAcDnn38u+9wJEya45botqdVqoXXDH3/8gVWrViE9PR2A/dYUTZs2RdeuXQEA27Ztw4wZM+ye59atW0LrA96mTZvsZtvi4+Nx+fJlANa1UoS4imqeCClmZ86cwfLlywvcr02bNqhRowYGDx6MnTt3IjY2FqdOnULt2rXx2muvoV27dihbtiyysrJw/fp1HDp0CBs2bEBKSgpefvll4TihoaHo2bMn/vzzT2zbtg3du3fHa6+9hsqVK+Phw4dYv349li9fjsjISKSmpha6tqekefPNN7Fs2TJcuHABX3/9NW7cuIHXXnsNlSpVEpZn+fPPP9GsWTOhrxAfjDpLoVBg3bp16N27N44dO4Y//vgD1atXx9ChQ9GxY0dUqlQJarUa9+/fx9GjR7F+/XohELDUsGFDtGjRAkePHsWPP/6IvLw8jBgxAoGBgbh27Rq+//577N+/Hy1btiyw75crhg0bhh9++AE5OTnC4r9ly5ZFly5d7D5v2bJlaNKkCe7du4ePPvoIO3fuxOjRoxETEwMvLy8kJyfj3Llz2LFjB/bu3Yvnn38egwcPFp6/cOFCDB06FL169ULHjh1Rq1YtBAYG4vHjxzh58iQWL14szBYdN26c279vUkoVa39zQkop8fIsjn4tW7ZMeL7BYGDfeecdVqlUFvg8X19fNjs7W3L+O3fusJUrV7b5nMqVK7MXL16ULBJrqaBlPlzZ9+bNm7LfL8+ZhYHtadeuHQuAbdeunezjt2/fZqtXr27z59O1a1d2+/btwv2jR4/aPV9BcnJy2AkTJrAajabA3yfDMOywYcPYhIQEq+PExcWxYWFhNp/79ttvO7UwsDNMJpNkaRfYWd7G0q1bt9imTZs69P9g1KhRkufyv0t7X0qlkv3444+d+n4IsYeG7QgpgZRKJT755BNcunQJkyZNQsOGDREcHAylUgl/f3/UqVMHQ4cORWxsLO7duwdvb2/J8yMiInD69GlMmTIFUVFR0Gq1CAwMRP369TFjxgycOXNGqK0qjSpXroyzZ89i1qxZqFu3Lry9vREUFIQWLVrg22+/xfbt2yVDoeKFal3h5eWFhQsX4tq1a5g/fz46d+6MypUrw9vbG15eXggPD0fXrl0xd+5c3Lx5E7/++qvs8jDR0dE4ffo0xo0bhypVqkCj0aBs2bLo3r07/vzzT9nhPHdhGMZq+RXL+7ZUqVIFx44dw8aNGzFo0CBUq1YNPj4+UKvVKFu2LFq1aoVJkybhwIED+PnnnyXPXbduHVauXImRI0eiQYMGKF++PFQqFfz8/FC3bl28/vrr+PfffzF16lS3fa+EMCxLLVgJIcRZc+bMwYcffgiVSoWMjAybS7kQQp4+lHkihBAnsSwr9Mpq0KABBU6ElDIUPBFCiIVbt25JWjpYmj59urAO3ogRI57UZRFCPAQN2xFCiIWZM2di2bJlGDJkCFq3bo3w8HDo9XrExcUhNjYW+/fvB8A1Yjx9+jS0Wm3xXjAh5ImiVgWEECLjzp07mD9/vs3Ho6Oj8eeff1LgREgpRMETIYRYGDNmDAIDA7Fz5078999/ePToEXJychASEoL69eujX79+GD16NDQaTXFfKiGkGNCwHSGEEEKIEyjz5GYmkwmJiYnw9/d3ueswIYQQQp4slmWRkZGB8PBwKBQFzKcrvv6cjsvIyGAnTJjAVqhQgdVqtWz9+vXZ1atXF/g8cSddy6979+5J9rXVpbZbt25OXWt8fLzTnaPpi77oi77oi77oyzO+4uPjC3yvLxGZp/79++PEiROYP38+oqKisGrVKgwePBgmk8mhDrbLli1DdHS0ZFtoaKjVfpGRkVi5cqVkW1BQkFPX6u/vD4BbjDIgIMCp5xJCCCGkeKSnpyMiIkJ4H7fH44Onbdu2Yffu3ULABAAdOnTA7du3MWXKFAwcOBBKpdLuMerWrYsmTZoUeC5vb2+0aNGiUNfLD9UFBARQ8EQIIYSUMI6U3Hh8k8yNGzfCz88PAwYMkGwfNWoUEhMTcezYsWK6MkIIIYSURh4fPF24cAG1atWCSiVNktWrV094vCC9e/eGUqlESEgI+vfvb/M5169fR0hICFQqFapXr45p06YhJyen8N8EIYQQQp4aHj9sl5ycjMjISKvtISEhwuO2lC9fHtOmTUOLFi0QEBCA8+fPY/78+WjRogWOHDmC+vXrC/u2adMGAwcORHR0NHJycrB9+3YsWLAAhw8fxr59+2xW3ut0Ouh0OuF+enq6q98qIYQQQkoAjw+eAPvjj/Ye6969O7p37y7cb9u2LXr16oWYmBhMnz4dmzdvFh6bM2eO5Lk9e/ZE1apVMXnyZGzevBn9+vWTPce8efMwa9YsR78VQgghhJRwHj9sFxoaKptdSklJAWDOQDmqatWqaNOmDY4ePVrgvsOGDQMAu/tOnToVaWlpwld8fLxT10MIIYSQksXjg6eYmBjExcVZrXB+/vx5ANxMOmexLFtwAywRe/tqtVphZh3NsCOEEEKefh4fPPXr1w+ZmZlYv369ZHtsbCzCw8PRvHlzp4538+ZNHDlyxKGWBLGxsQBQ6PYFhBBCCHl6eHzNU48ePdClSxeMGzcO6enpqFGjBlavXo0dO3ZgxYoVQo+nMWPGIDY2FtevX0eVKlUAAJ07d0bbtm1Rr149oWB8wYIFYBgGs2fPFs5x6NAhzJ07F/369UNkZCRyc3Oxfft2LFmyBB07dkSfPn2K5XsnhBBCiOfx+OAJADZs2IBp06Zh+vTpSElJQXR0NFavXo1BgwYJ+xiNRhiNRrCidY5jYmKwdu1afPbZZ8jJyUFYWBg6duyIDz/8EFFRUcJ+FSpUgFKpxOzZs5GUlASGYVCzZk189NFHmDRpklNDfIQQQgh5ujGsONoghZaeno7AwECkpaVR/RMhhBBSQjjz/k0pFUIIIYQQJ1DwRAghhBDiBAqeiMtuJAJLtwEZ2cV9JYQQQsiTQ8HTU+7MNWDgLGDFLvceNy0TaDMeGLMAaD8RyM517/EJIYQQT0XB01PMYAAGzATW7QOGfwy8+wNgMrnn2J+sBu7lN34/fRUY9QlAUw8IIYSUBhQ8lWC/7Qe6TwG2/i3/+IrdwH8J5vsLVgND5wC6vMKd984D4MvfpNvW7QPm/Fq44xJCCCElAQVPJRDLAh+vAF6aCew8AQz8CLh5T7qPwSANZvj1k9fsBbpNAVIzrPd31LSfgNz8AKxDQ/Oxpy8FNhw0X+Phc0D/D4HwF4DFGxw/PiGEEOLJqM+TmxV1nyeDARj/FfD9Fun2rk2BHQvMgcyy7cDoT7jbnRsDb73A1T7l6LhtFcsAwf5ASgaQks4FQ92bAZvnAhq17fOfugI0eY27HRIAXF/JXcvUH7ltPl7Ax68AK/cAJy6bn6dUAHG/ADUrFf5nQAghhLgb9Xl6SmXnAv2nSwOnQF/u310ngF/zi8L1BmD2L+Z9Zo4E+rQC9i8EygZx2xKSgAs3gcQkcxZpx3FgVqzt87MsMPk78/0ZI4Agf+DdIcDQzuZrnPi1NHACAKOJy0w9aTOXAdHDgSnfAY9Sn/z5CSGEPH0oeCohHqUCHf8H/JFf36RWASs/4L54//sGePgY+GWneRivSxOgdQx3u1kt4J9vgJZ1uPveWiC8DFC3GqDilgjE/FXAkfPy17D1H2D/Ge52jYrA/z3H3WYY4Kcp3PHF6lcHfn7HHLCt2csVlz8pW//mgsEr8cBna4HIwdyQY0q688fK0XHDkP9e437G7iq8J4QQUvLQsJ2bFcWwHcsC7SYAh85x9/19gI2zgU6NuftDZgOr/+JuD2jPZX1u3efu/y0KlsT0Bi4A4328ggssAKBaBeDsz9x5xPvHjOICEQBY/xHQv630mPeTuSFFgAusOjbiAquv1gMTFnPbuzUFdnxa8Pd86goQ/5DLmOWv/eyUrBygzkjg9gPrxwJ8galDgHcGA/aWLdQbgL9OAav+AjYdlvazUquA8FCgekXg2Riu9qt5LcBL6/y1WmJZ7rwBvtYBKSGEkKLhzPs3BU9uVlQ1T6eucAFUoB+wbT5Qv4b5sYePgVojrDMqjgYqAGA0Am0nAH9f4O6P7slljQAuizV+EfDnUe5+67rAocXm+qqC6PKA6JfNAd3eL7lgQw7LcoXu/BDf0M7AL+/bD3LkvPsDN7sQANrVB+pUA37cygVEvMkDgU/HWT83VwdMX8bVjSWlOX5OrZoLVCe9BPRu5dz1is1aDsxczt1eMQ0Y2sX1Y3k6oxGYsYwLcuePBSqWLe4rIoSUVhQ8FaOiLBjf9y9QPRyoXM76sV92AiPmSbf98w3QQibrZMuNRKD+GCAzh7u/+kPgWgKXleLrohgG+Ptr544LcPVYL3/M3W5eC/jnW+vgS5cHvPqZuXaLN2UQsOD/rI+54xjw73/AyO5AhVDz9vM3gEavAgYjV/x+fikQFcG1WJjzK/DzNvOw26LxXDE9LyMbeP4DYO9p6bkCfYHnWnMBUkIScPcR96/cECDDAPu+BNo1cOhHI7FiF9eTi6dWcRMBOjZy/lglwdcbzNnKZyK4oJwf5iWEkCeJgqdiVNSz7WxhWa4Fwe6T3P3uzYDtC5w/ztJtXNdwORVCgW8nAs8/6/xxjUagwStckTrADTuKj5OcBvT70Dw0CXDZJj7I+eIN4H8DuNupGcCbi7gZfQAQ5Mc9PrI793N49i1zBm3mSGDGSOm1LPkDeO1z7jbDAL/NBF5oBySlAj3fMxe7a9VcwDSkE/fzlBuSu/MAOHCWqwXbe9qcXatcDjj3M5cpdNTBs0DnSdLsGMAN3x1ZDNSNLPgYKelc8GswckX6BiNQJtC5gORBCvDBz1ww8/ZLzmf9HHU/GXjmZSA9y7ytYU1g7xfcRARCCHmSKHgqRsUVPAHArXtAp0ncm9H+hdxQlbNYluvNtOmweZtSAUx4kZtdF+Dr+vX98Tfw3Pvc7ZqVgJe7chmt3DzufNcTuce8tdxw1aNU4P++MD9/1YdAWBAwcj6X+bHUtSk3pDhjmfkc536WD3o+/NncB0urBmKncsXlcbe5bcH+3PCoMxk2oxHo+DYXBAHA8K7ckKMjrt0FWrxuzmSN7cPNhNz6D3e/Ulng6Le2h7UOnwM++sUcPIuplFwW8cX2BV+HyQR0+J/5e3j9eeDrCY4P0Tpj2BxzACzWui6w6zOu7YUtWTnA5iNAdGWgUZT7r40QUvpQ8FSMijN4Arg3cIWicG92j1K5N/IbicCz9YBvJgIxDmQ9CsKy3Hp4fFZITvkQYMvHQNNo7v7MZeb2CUoFl03hBfoCbeubZyBa2vO5uahe7lpGzQdid1o/Fl4G2PWpa8Hn7ftAvTHmbMq6mVwRvz3JadzPm+8G360psHUeoNNzgQyfCatXHVj1AfczCvbnfscHznBB075/7Z/Dx4sLvgr6PS78jZu1KTbhBeDLN90bQO37l5s9CnD9wjbP4TKPfI1Zt6bc68Cy55jRCPyyi8uMJSZxr/WvJwDj+rrv2gghpRMFT8WouIMnd8nMBhKTueyNO980/77AFb4bjNaPNajBNekU13SxLJd9WvKHdN+OjYDl7wERYcC2o9wwnDgbNbQzsOID2KU3AL3ek2ZrqocDuz/nZhy6Sly3FOzP1VzZyhhl5XDDrUfyA8q61YAjX5szfA9SgJZvWHeQVyq4IUHLmqtqFYAmz3DZJpWSC4D5Y1cPB07+YHtI7PJtoOGr0vo2/q/D5IFc3Zk7Xgt5eq627vId7v6Pk4FXenNtLDr8zxx4xkRyDV6b1wJa1Aau3uX6jJ27bn3M94YAc18puiFGQsjTj4KnYvS0BE9F6cINIO4O4K0BvPK//H24N0u5Nz9+geNNh7khtk9eA8b3l+6bnsXNsPvhDyCqEnBgEVAupOBrycjmMiAnr3B9qXYsAMqHFvw8e1gWGPQRt94fwPXa2rHA+nvLzgV6TzVnjcqHAMe+s54QcOUO0OpN+/2palYCPhjO1WepRC0ocnRcto/vr9WrBZfRsbwWgwFoPR44Hsfdn/ACF8yOXmAOoKYO5QKUwgZQ81YC7+d3pG9RmwsW+es5dI4LJvlO+PY0ipL2DRvaGVj6rv0O+XJ0edbnU6sAX2/njvO02HQI2H0KeON5oHbV4r4aQp4cCp6KEQVPRcNk4gqyoysDlcJs75eZzfWF8nai35LBwM3aa1hDGngURko6UG80NyMPAAZ3Ar543RyY5eqAvh9wneEBbghy75e263du3eMCw4Qkblg1KY37t3xI/tI7HWz3w7p9H2g8FkjOD75mjABmjpLuI+7zFRUB/PsjN9T301ZuBiSvR3OuFQPfw8tZt+4BtUdywYpCAZz6AWhQU7rPvn+5CQGXbskfo3EU8Pnr3GzGbzZys/X4v2KdGnE9yGwV6v91imtZkZAEPEzlMntpWdb7KRRcIPrjZNd7d7Es9/uNDC85yxKJe7JZ9pPzdPxroCjq80jpQMFTMaLgifD2nAS6TDbfD/AFZo0EXu3NrTPI983y9+Hqs4qyIebuk0D3d6SzFxvUAMoGAo8zgU5vc8OYCgU3s09cKP/9ZmDcl9Lj1asOTHyRCzC0Gseu4dY9rq7pzH/c/QkvAAvH297/USqXCTsWBxy9xA31junJBaLizNmmQ8Dg2ebhxshwrjaseW3zPkYjVzsnXrbIER0ackPJ4oaxABfYrdjNDSvaaiPxv6+Bhb8Dvl7AiR+AWlWcO7c7mEyOD2V+s5ELWsVUSi6bN7yr+6/NnU5dAXq8C5QLBjbMLjnBKvEsFDwVIwqeiNivu7hP8o8zzNsCfM11Pb5ewM5PzUvoFKVPVgHvLbG/z9ShwMevWm9ftYdb/PmORcf28iFcvdHYPvazfbtPckOZ/NBjeBkgLrZwszfF/rkI9Jlqzq6plMDs0VwX+ccZwJA55iwfL8CXe7MNCwL8vKUZi0PngKxc7nbzWsC2T7jC9hwdMPdXYMEac7D55zyge3PpsbccAfpOM99vURs4vFg+O5ir44YabQU5qRnAJ6u58732nGOBwemrwKdrgA2HgI4NuWWcQuz8OfpuM/C6KECOrmyuSQO418R7QwqX1WFZ4NtNXNZvyiCuHtAdjEZusXI+KC8XzP2fEjcSfhLSs7gSBGeHjYvTicvA4fPch5LC/F9My+T+j3dq7L7fa3Gg4KkYUfBELCWlAu//BPz0p3loAeCCje2fuNZM0xWWtViWYiKBE9/bziQZDNyb8Ze/cZkgsQqhXOD1ai/pMBfLckHbtJ/NWa8aFYFNc1ybzWjPrXtcBkp8be0bADfumYM+hYLrZD6+n/3huKMXuUxGaiZ3PyaSqyl7/0dzSw1egC83k5HPLN19CNR/xbpG7fPXub5ZYpsPc81tNWquJ9nY3tKh472nudYc8Q+5+wwDvNAWeHcw0CRaeix+mPDTNcBfFk1en4ngAsDIcOvvVdz3DADeHwZ8NIobDv1us3n7/z3HzWx0ZbkkAPh9P1e7CHA/q23zgaoyEzOOXuQ6zr/YzrFz/fwn8IrFSgqBvsCf860/lNxL5oZqlUpu0oVSwf0/jAhzLTA0GrnZvos3cr+r+tWBg1+570NBUXqQAtQYyvWFa1WXa2+jdrFsodsU7rXXsCb3N8TV10hxo+CpGFHwRGw5Hge8sZArTvfSAH98DHRu8mSvgWW52Ylxt4FHadzSPo9SuT+an/4fUMPB4Y6jF4FP1wIbDkq3lw/hMiNKBZf9eZwBnBIVdfduCfz6ftE1wdQbuOVtPl4pDVQBICwYWDsdaG9jaSBL564DXScDDx5bP6ZWcQEAP/OvejhX7B/kxw2BHsjvk9WsFvfpnmW53/m5pebM0R9/Ay9MlzZFrV0V+HwcF1BPXQIsWm/7+to34H7efO3W/RRz5k1O2SBgy1zzkOzRi9yw4lpRMP3uYGDeWPNMywWrpdnK59tw/dacqSkEuMC53mjg4i3ztnLBXEsOPgi8+5Brk/H7Ae7+a32A7yfZP256FlBzGPc6BrjfCd+rzVsLbPiI+71vOQJs+Ztb2FtOv2e5Zrm23vSTUrk6Q775rN4A7DvDZdL4xri815/n2rsUJYOBO29yuvkrN49r8SG3AoUccRsYgPvdz3/N+Wu5eBOoK6qhXPkBMKSz88fxBBQ8FSMKnog9JhPXOqBKOcf/yHmys/9xf4A3HrK/H8NwmZUPhj+ZdgL7/gWGzeV6QQHcJ+t1M5xfO++/u1zXd/EC08/WA36YBESU5WYyns0PoNo34B7j66oiwoAzP3E/n6/yg6C29bmle3ae4JYBytPLnzckQJq56tCQK4ZfvEE+mLNUsxJX2N+uPtB/ujmg8NJwGcI/j5pnVvJstaNYuRsY9Yk5yGtdl5uxaW8Y0NK6fVydnyUfLy6YvnWfa27LLw3Fs7cOJiBdw/LFdlz7kn4fyjeLLcgHw4HZY6TbWJbrKbZgtXx7FVsOLy66ofgDZ7jXtlyjYB8vrt6vbxv7x8jRAVUGch+cxLZ/Yj0EXZBJ3wJfrDPffyYCuLi8ZGafKHgqRhQ8kdLozDUuSNj6j/WbTMUyXLDRq+WTvaakVOCL34DQAK61hau1KHcfcjVTdx9xb7Aju5sDwDsPgGb/Zx3QKBTAgYVAm3pcL6+Y0eZeXSO6AWv2ck1QAa4A/o3nuTehYxYBjVbNDTO+9QJ3zFwd1yT00zXmpqoAFxSVC+aCpnF9gb6tzW9ejzO4VQP2n5H//sKCuXqmiS/aHrrac5ILSvjgplYVrv2GIx8ATCbu++dnT66ZDnyzSboUk5i31tw6ono4l62T6zZ/PYGbuZmn535Ocb9wfc50ecDQOcD6g9bPaRwFNH6GC4qMJm7f1XvNQ8qWy0aJVyKwpUdz4K3+XPuVt/MbzEZX5masujpTUw7LcpnCKd9JmwVbYhgui/z2S7Z/n+JZtOFlzB8yygRyAb+jHzLy9EClAdZB2K/vA8M8fJKBHAqeihEFT6Q0M5nMa+oZ84c4/H2e7uaV/1wE2k+UZpFmjQKmjzDfF3dUF3upA7ByGlfnxLJcUPXuD1yNU6Mo7k1IrteS0cgVdHtpuODHsuDdUp6eqwsSL7pdP3/G5KCOjr3J/3sN6PGOOVCsWIbLQBW0PI4469SqLpeVydNz2azVf5n3Yxiurmr2aK7Ynm/uamth8H4fmJeRspzoYDBwDVVX7uFWK3iuFTdkLNfm5PO13L4A91o9/h0QXUXajwzgflf+3uYGtKGBXG+xqAjucaOR68fGZ/Q+fBn4aLT9n42lOw+4BdQzcoAezbgMUuMoLph85VPu9cFrUZtriBsawF3L4fPSesaxfbgaNcs6Jpblhtn4YPbYd1y2lF8Kqm194K/PHWvbsvEQF5gD0kkGNSsBl5YXfIx/r3GZtNw87sOETs/97ejR3H7GsahQ8FSMKHgipPQRd5VvVx/46wvrYYtxXwDfbzHff6EtsHq69ZubLo9b67BWFfcOfbAsd/4z/wGDO3J1Vc4WSd9I5FpeXLvL3VcpuRmNHw6XD8CMRm65Iv6NetdnXNNYgAu0Z8VyEwoa1AAWTzAvy3T5Nld0n6fnAu/j33EZI97e01xtGcDVfV1dYd1OwlEsCwyZbQ5MoisDL3eTBk6L3wLe7F/wsc7fABq9ygUAKiVw+kfHl7ZKzwJav2lePJ0XXgbw0UozjXxRv/j1wbLAR7HAzOXmbZ0bA7/PkvY923mc+x0CQJsY4NBibomoBq+YhwLfHwbMGVPw66P3e+aWKzs/5QJOPsMZO5X7Odpy6gq3eoLlQugA97M7t/TJt/eg4KkYUfBESOn0237uk/TkgfL1QBnZ3JT6q/FcgfLaGa7PbipOj1K5thDiIcZnIoAfp3A1X2Jr93IzPAGuVurQYus3ZL1B/ucw91eu3gjgsmQnfuCG6mJ3Aj9vMw8VLXsXGNmjcN9TVg73Rn7+hvVj88cC7w5x/Fjiob7mtbgO+gUFwQYD0Od9YMdx+/v5+3BBSb9nbe+zcje3MgCfCW1YkxtiDQvm7vMz4wCuJxZ/rCPnuaWz+CHBFrW5hrrdmskHUYlJQMRLXBAcEQbcXM1lv9pP5B6vUZFrRyKXfcrRcUGmuB2Gpb6tgU1zbT9eFCh4KkYUPBFCbEnL5Nboa/JMye6Ercvjsgwfr5RmDl7uxg2PtYnh+meJs067P3NudqnewAWb/IzGahWs13hs8gw37OSOYeHrCdz5+PYUADf0OmuU7efIydVxWZwr8dz9iDBuzcraVbgh2NZ1gWcqm/dnWW4WLt8WIiSAmxUZdxvYfIQrftfpuYzYxtnckGJBjpznJiTwC20/E8Gt2ZmWydWfAVzbiqu/SgO7Bau5YWOxZrW4IKpHc+lrVjysKf45dfyfeckpW4HthMXmSRSNorhaQi8NoFEBL88z12AVZeG9HAqeihEFT4SQ0uLiTa4Wx7LvF8D1/rqXzN22lXUqyMnLQPPXzQXdPKWCC9K+mej8DEp7th3lMkAmEzdb8dNxrgW5h84Bbd+y/XizWtzEg0EdgeU7zIXmahW32kDb+uZ9s3K4obwGNRzv5g9wQ59dJpuH4iqX44K4bfnDbIvGcxMRLK0/wM18FLeVALg2Fcvf44YAWRaIGmYeSryx2ryYuvh7rx4OXP5Fmn0Sr7zgpeGGNsXDc+Jidr5G7kl90KDgqRhR8EQIKU2MRm723LSfrFsN8PZ87voaedN+4tZeBLjhu5HduT5C/DCUu525xi1Z1L5B4d60f/6Ta4x76bZ5RQFLGjWXYePfhQuqE3LW7ftcqw1xvRTANRGN/812nZjJxPVw++gX6VBmVASX/UpONwdInRoBe76QPr/z2+ZGrc1qAZ+N44Z0H2dw/b74gG7hm8CEF6XPNRi4jCXfXsNyBmRRouCpGFHwRAgpjdKzuBlyh89zX8cuccNN/Z7lFmt2NRBhWeDgWW44y9Hia0/Cstww1KXbXE3cmr3yzTpdmZ3niPvJQNcp0iBo8kAuq1YQk4lrWvp/X5iXmPL14jJFJ69w9+WaYh69yPVAE7dU6Pcsd7zNR7j7nRpxEwjkhlw3H+aGHQFuuPL8Uvct2m4PBU/FiIInQgjh6qISk7lmok/ija8kOfsfV/i+YjdX+D6qB/DzO0U3PJWSDvR6jxte9fHiCrmdadJ7I5FrScA3hOUF+gL3Nsh3nN97mqttspw9yD/v/DKuHkwOywLPjje3q1gymVtQ3WTiWkFsOsx9L3u/dG8bFAqeihEFT4QQQhyhN3BL68j1n3K37FyuD1S96gX35rL1/LGfcb2zeAUtRWM0cjVdHy41178BwIppwNAu9s/39wWudQPA1c/1bc1lrcTHOfYdNyzoLhQ8FSMKngghhDyNWBb4eiPXDV+jAk4tkc4ctCUrB/h8HRe8vdgOmDHSsSybuBGqJYUC+GYC8H99nfoW7KLgqRhR8EQIIeRplpTKNbIsqgW+eXG3gZhR5toprRro2pSb+denFbfYtTs58/5NI9GEEEIIcViZoCdznlpVgK3zgN2ngJa1ge7NAD8XO8m7GwVPhBBCCPFI3ZtzX57mKV6ukxBCCCHE/Sh4IoQQQghxAgVPhBBCCCFOoOCJEEIIIcQJFDwRQgghhDihRARPmZmZmDhxIsLDw+Hl5YUGDRpgzZo1BT5v+fLlYBhG9uv+/ftW++/ZswctW7aEj48PypQpg5EjR+Lhw4dF8S0RQgghpIQqEa0K+vfvjxMnTmD+/PmIiorCqlWrMHjwYJhMJgwZMqTA5y9btgzR0dGSbaGhoZL7Bw4cQI8ePdCrVy9s3rwZDx8+xLvvvotOnTrh5MmT0GplFu8hhBBCSKnj8cHTtm3bsHv3biFgAoAOHTrg9u3bmDJlCgYOHAilUmn3GHXr1kWTJk3s7jNlyhRERUXh999/hyp/Fctq1aqhdevWWLp0KcaNc2AJakIIIYQ89Tx+2G7jxo3w8/PDgAEDJNtHjRqFxMREHDt2rNDnSEhIwIkTJzB8+HAhcAKAVq1aISoqChs3biz0OQghhBDydPD44OnChQuoVauWJKgBgHr16gmPF6R3795QKpUICQlB//79rZ7D3+ePaXkeR85BCCGEkNLB44ftkpOTERkZabU9JCREeNyW8uXLY9q0aWjRogUCAgJw/vx5zJ8/Hy1atMCRI0dQv359yTH4Y1qex945dDoddDqdcD89Pd2xb4wQQgghJZLHB08AwDCMS491794d3bt3F+63bdsWvXr1QkxMDKZPn47Nmzc7dCx755g3bx5mzZpl83FCCCGEPF08ftguNDRUNvOTkpICQD5bZE/VqlXRpk0bHD16VHIOQD6LlZKSYvccU6dORVpamvAVHx/v1PUQQgghpGTx+OApJiYGcXFxMBgMku3nz58HwM2kcxbLslAozN86fwz+mJbnsXcOrVaLgIAAyRchhBBCnl4eHzz169cPmZmZWL9+vWR7bGwswsPD0bx5c6eOd/PmTRw5cgQtWrQQtlWsWBHNmjXDihUrYDQahe1Hjx7FlStX0L9//8J9E4QQQgh5anh8zVOPHj3QpUsXjBs3Dunp6ahRowZWr16NHTt2YMWKFUKPpzFjxiA2NhbXr19HlSpVAACdO3dG27ZtUa9ePaFgfMGCBWAYBrNnz5ac55NPPkGXLl0wYMAAvP7663j48CHee+891K1bF6NGjXri3zchhBBCPJPHB08AsGHDBkybNg3Tp09HSkoKoqOjsXr1agwaNEjYx2g0wmg0gmVZYVtMTAzWrl2Lzz77DDk5OQgLC0PHjh3x4YcfIioqSnKO9u3bY9u2bZg+fTr69OkDHx8f9O7dG59++il1FyeEEEKIgGHF0QYptPT0dAQGBiItLY3qnwghhJASwpn3b4+veSKEEEII8SQUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDiBAqeCCGEEEKcQMETIYQQQogTKHgihBBCCHECBU+EEEIIIU6g4IkQQgghxAkUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDiBAqeCCGEEEKcQMETIYQQQogTKHgihBBCCHECBU+EEEIIIU6g4IkQQgghxAkUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDiBAqeCCGEEEKcQMETIYQQQogTKHgihBBCCHECBU+EEEIIIU6g4IkQQgghxAkUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDiBAqeCCGEEEKcQMETIYQQQogTKHgihBBCCHECBU+EEEIIIU6g4IkQQgghxAkUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDiBAqeCCGEEEKcQMETIYQQQogTKHgihBBCCHECBU+EEEIIIU6g4IkQQgghxAkUPBFCCCGEOIGCJ0IIIYQQJ1DwRAghhBDihBIRPGVmZmLixIkIDw+Hl5cXGjRogDVr1jh9nA8++AAMw6Bu3bpWj7Vv3x4Mw1h9de/e3R3fAiGEEEKeEqrivgBH9O/fHydOnMD8+fMRFRWFVatWYfDgwTCZTBgyZIhDxzhz5gw+++wzlCtXzuY+kZGRWLlypWRbUFBQYS6dEEIIIU8ZhmVZtrgvwp5t27ahV69eQsDE69q1Ky5evIg7d+5AqVTaPYbBYEDTpk3Rtm1bnD17FklJSbhw4YJkn/bt28tud1Z6ejoCAwORlpaGgICAQh2LEEIIIU+GM+/fHj9st3HjRvj5+WHAgAGS7aNGjUJiYiKOHTtW4DHmz5+PlJQUzJ07t6gukxBCCCGlhMcHTxcuXECtWrWgUklHGOvVqyc8bs+lS5cwZ84cfPfdd/Dz87O77/Xr1xESEgKVSoXq1atj2rRpyMnJKdw3QAghhJCnisfXPCUnJyMyMtJqe0hIiPC4LSaTCaNHj0b//v3Rs2dPu+dp06YNBg4ciOjoaOTk5GD79u1YsGABDh8+jH379kGhkI8zdToddDqdcD89Pd2Rb4sQQgghJZTHB08AwDCMS4998cUXuHbtGrZs2VLgOebMmSO537NnT1StWhWTJ0/G5s2b0a9fP9nnzZs3D7NmzSrw+IQQQgh5Onj8sF1oaKhsdiklJQWAOQNl6c6dO5g+fTpmzJgBjUaD1NRUpKamwmAwwGQyITU1tcAhuWHDhgEAjh49anOfqVOnIi0tTfiKj4939FsjhBBCSAnk8cFTTEwM4uLiYDAYJNvPnz8PALI9mwDgxo0byMnJwYQJExAcHCx8HTlyBHFxcQgODsbUqVMdugZbQ3YAoNVqERAQIPkihBBCyNPL44ft+vXrhx9//BHr16/HwIEDhe2xsbEIDw9H8+bNZZ/XoEED7Nu3z2r7xIkTkZaWhmXLlqFSpUp2zx0bGwsAaNGiRSG+A0IIIYQ8TTw+eOrRowe6dOmCcePGIT09HTVq1MDq1auxY8cOrFixQujxNGbMGMTGxuL69euoUqUKgoKC0L59e6vjBQUFwWAwSB47dOgQ5s6di379+iEyMhK5ubnYvn07lixZgo4dO6JPnz5P6LslhBBCiKfz+OAJADZs2IBp06Zh+vTpSElJQXR0NFavXo1BgwYJ+xiNRhiNRrjS87NChQpQKpWYPXs2kpKSwDAMatasiY8++giTJk2yO2xHCCGEkNLF4zuMlzTUYZwQQggpeZ6qDuOEEEIIIZ6EgidCCCGEECdQ8EQIIYQQ4gQKngghhBBCnEDBEyGEEEKIEyh4IoQQQghxAgVPhBBCCCFOoOCJEEIIIcQJFDwRQgghhDiBgidCCCGEECdQ8EQIIYQQ4gQKngghhBBCnEDBEyGEEEKIEyh4IoQQQghxAgVPhBBCCCFOoOCJEEIIIcQJFDwRQgghhDiBgidCCCGEECdQ8EQIIYQQ4gQKngghhBBCnEDBEyGEEEKIEyh4IoQQQghxAgVPhBBCCCFOoOCJEEIIIcQJFDwRQgghhDiBgidCCCGEECeoivsCCCGEPJ30ej2MRmNxXwYpxZRKJdRqtduPS8ETIYQQt0pPT0dSUhJ0Ol1xXwoh0Gq1KFOmDAICAtx2TAqeCCGEuE16ejoSEhLg5+eHMmXKQK1Wg2GY4r4sUgqxLAu9Xo+0tDQkJCQAgNsCKAqeCCGEuE1SUhL8/PxQqVIlCppIsfP29oa/vz/u3r2LpKQktwVPVDBOCCHELfR6PXQ6HQIDAylwIh6DYRgEBgZCp9NBr9e75ZgUPBFCCHELvji8KAp0CSkM/jXprgkMFDwRQghxK8o6EU/j7tckBU+EEEIIIU6g4IkQQgghxAkUPBFCCCElHMMwaN++fXFfRqlBrQoIIYQQN3C2roZl2SK6ElLUKHgihBBC3GDGjBlW22bNmoXAwEBMnDixSM8dFxcHHx+fIj0HMWNYCn3dKj09HYGBgUhLS3NrK3hCCPF0ubm5uHnzJqpVqwYvL6/ivhyPwDAMqlSpglu3bhX3pZRqjrw2nXn/pponQggh5Am6desWGIbByJEjcfnyZfTv3x9lypQBwzBCkLVx40YMHjwYNWrUgI+PDwIDA/Hss89i/fr1sseUq3kaOXKkcMxvv/0WtWrVgpeXF6pUqYJZs2bBZDIV8Xf69CrSYbs7d+5g9erVSExMRKNGjTB8+HAoFBSvEUIIIf/99x9atGiBOnXqYMSIEUhJSYFGowEATJ06FRqNBm3atEGFChXw6NEjbNmyBS+++CK++uorjB8/3uHzTJkyBfv370fv3r3RtWtXbNq0CTNnzkReXh7mzp1bVN/e040tpG+//ZYNDg5mFy1aJNn+zz//sAEBAaxCoWAZhmEVCgXbuXNn1mg0FvaUHi0tLY0FwKalpRX3pRBCyBOVk5PDXrp0ic3JySnuS/EYANgqVapItt28eZMFwAJgP/zwQ9nnXb9+3WpbRkYGGxMTwwYGBrJZWVlW52nXrp1k24gRI1gAbLVq1djExERh+6NHj9igoCDW39+f1el0rn1jJYwjr01n3r8LnXnasmUL0tPT0b9/f8n2t99+GxkZGWjdujWaNm2KdevWYe/evVizZg2GDBlS2NMSQggpYZqMBe6nFPdV2Fc+BDi55Amdq3x5fPDBB7KPRUZGWm3z8/PDyJEjMWnSJJw4cQLt2rVz6DwffvghKlSoINwvU6YM+vbti9jYWFy5cgUxMTGufQOlWKGDp8uXL6Ns2bKoVKmSsO3mzZs4evQoatWqhYMHD4JhGIwePRr16tXDTz/9RMETIYSUQvdTgISk4r4Kz1G/fn1hmM7Sw4cPMX/+fGzfvh23b99GTk6O5PHExESHz9OoUSOrbfx7dmpqquMXTASFDp4ePXqEWrVqSbbt27cPADBo0CCh70XdunVRo0YN/Pfff06fIzMzEx988AHWrVuHlJQUREdH47333sOgQYOcOs4HH3yAuXPnok6dOrhw4YLV43v27MGHH36Is2fPwsfHB71798aCBQsQFhbm9DUTQgiRKh9S3FdQsCd5jeXKlZPdnpKSgqZNm+LOnTto3bo1OnfujKCgICiVSpw5cwabN2+GTqdz+DyBgYFW21Qq7u3fXQvlljaFDp6MRiNyc3Ml2w4dOgSGYaxSiiEhITh79qzT5+jfvz9OnDiB+fPnIyoqCqtWrcLgwYNhMpkczmKdOXMGn332mc0X64EDB9CjRw/06tULmzdvxsOHD/Huu++iU6dOOHnyJLRardPXTQghxOxJDYeVFLaaav7888+4c+cO5syZg2nTpkkemz9/PjZv3vwkLo/YUejgqWrVqvjvv/+QmpqKoKAgGI1G7NixA15eXmjZsqVk35SUFISEOBfWb9u2Dbt37xYCJgDo0KEDbt++jSlTpmDgwIFQKpV2j2EwGDBq1Ci89tprOHv2LJKSrPPGU6ZMQVRUFH7//XchIq9WrRpat26NpUuXYty4cU5dNyGEEOKK69evAwCee+45q8cOHTr0pC+HyCh034BevXpBp9NhyJAh2Lp1K8aOHYsHDx6gV69eUKvVwn5paWm4ceMGqlSp4tTxN27cCD8/PwwYMECyfdSoUUhMTMSxY8cKPMb8+fORkpJic0pmQkICTpw4geHDhwuBEwC0atUKUVFR2Lhxo1PXTAghhLiKf588fPiwZPuqVauwbdu24rgkYqHQmaf3338fmzZtwo4dO7Bz506wLIvAwEDMnj1bst/69ethMpnQoUMHp45/4cIF1KpVSxLUAEC9evWEx1u1amXz+ZcuXcKcOXOwYcMG+Pn52TyH+JiW5zly5IjN4+t0OsnYc3p6uu1vhhBCCCnA8OHD8cknn2D8+PHYt28fqlSpgnPnzmHPnj3o378/NmzYUNyXWOoVOngKCQnB6dOn8dNPP+HatWuIiIjAqFGjJNMiAeDGjRvo27cvXnjhBaeOn5ycLDtlkx/+S05Otvlck8mE0aNHo3///ujZs6fdc4iPaXkee+eYN28eZs2aZfNxQgghxBmVKlXCgQMH8M4772DPnj0wGAxo1KgRdu3ahfj4eAqePIBbOowHBATg7bfftrvPnDlzXD6+vZWq7T32xRdf4Nq1a9iyZUuhzmPvHFOnTpV87+np6YiIiHDofIQQQp5urMzysVWrVpXdLla/fn3s3LlT9rGRI0c6dJ7ly5dj+fLlsseYOXMmZs6cafcaiG1FujyLO4SGhspmflJSuE5rtgrQ79y5g+nTp2P+/PnQaDRCLwuDwQCTyYTU1FRotVp4e3sjNDQUgHwWq6Aid61WSzPxCCGEkFKk0AXjiYmJ2LJli1XfJJZl8cUXX6BWrVoIDAxEx44dcebMGaePHxMTg7i4OBgMBsn28+fPA+D6R8m5ceMGcnJyMGHCBAQHBwtfR44cQVxcHIKDgzF16lTJMfhjWp7H1jkIIYQQUvoUOnhatGgR+vXrh0uXLkm2f/HFF5gyZQquXLmCjIwM7N+/H506dcLDhw+dOn6/fv2QmZlptZJ0bGwswsPD0bx5c9nnNWjQAPv27bP6ql+/PqpWrYp9+/bhzTffBABUrFgRzZo1w4oVKyQNw44ePYorV65YLT1DCCGEkFKssIvtNW7cmPXy8pIsLmgwGNiwsDBWpVKxP/zwA3v27Fl26NChLMMw7NSpU50+R5cuXdjg4GB2yZIl7N69e9lXX32VBcCuWLFC2Gf06NGsUqlkb926ZfdY7dq1Y+vUqWO1fd++faxKpWL79evH7t69m125ciUbERHB1q1bl83NzXX4WmlhYEJIaUULAxNP5e6FgQudeUpISEDFihUl6/McPXoUjx49Qq9evTB27FjUq1cPP/zwA3x8fLB9+3anz7FhwwYMHz4c06dPR/fu3XHs2DGsXr0aQ4cOFfYxGo0wGo0FFuHZ0r59e2zbtg337t1Dnz59MH78eHTo0AF//fUX1TQRQgghRFDogvGUlBSrxpf88iy9e/cWtvn6+qJmzZq4ffu20+fw8/PDokWLsGjRIpv72JtVILZ//36bj3Xp0gVdunRx+voIIYQQUnoUOvPk4+ODBw8eSLbxAUrbtm0l29VqNfR6fWFPSQghhBBSbAodPMXExODOnTs4evQoACA+Ph779u1DxYoVERUVJdn39u3bNhfmJYQQQggpCQodPL3yyitgWRY9e/bEiy++iFatWsFgMOCVV16R7BcXF4dHjx7RtH9CCCGElGiFDp5efvllvP3220hPT8eGDRuQkJCAF198Ee+9955kv2XLlgEA1RQRQgghpERzS4fxzz77DO+99x6uX7+OiIgIhIeHW+3TvXt3tG7dGs8++6w7TkkIIYQQUizctjxLmTJlUKZMGZuPd+zY0V2nIoQQQggpNm5f2y4nJwfXr19HRkYG/P39Ub16dXh7e7v7NIQQQgghxaLQNU+8nTt3on379ggMDET9+vXRpk0b1K9fX1jXbteuXe46FSGEEFLqzJw5EwzDWPUrZBgG7du3L/Rx3GnkyJFgGAa3bt0qsnMUJ7cETzNnzkTPnj1x8OBBGAwGqNVqhIeHQ61Ww2AwYP/+/ejRowdmzpzpjtMRQgghHmfw4MFgGAZr1qyxu19ycjK0Wi3KlCmDvLy8J3R17rV8+XIwDONQc+qnUaGDpx07duCjjz6CQqHA66+/jitXriA3Nxfx8fHIzc3FlStX8Prrr0OpVGL27NnYuXOnO66bEEII8ShjxowBYJ5dbsuKFSuQl5eH4cOHS5Y2c1VcXBx++eWXQh/HnebNm4e4uDhUrFixuC+lSBQ6ePrqq6/AMAyWLl2Kr7/+GjVr1pQ8XrNmTXz99ddYunQpWJa1u8QKIYQQUlJ16tQJVatWxZ49exAfH29zPz644oOtwoqOjkblypXdcix3qVChAqKjo6FWq4v7UopEoYOnEydOoFKlShg+fLjd/YYNG4aIiAgcP368sKckhBBCPA7DMBg1ahRMJhNiY2Nl9zl16hTOnj2LZs2aISQkBDNmzECLFi0QFhYGrVaLqlWr4vXXX8fDhw+dOq9czVN8fDwGDx6MkJAQ+Pn5oV27djh48KDsMfLy8rB48WJ069YNERER0Gq1CAsLQ//+/fHvv/9K9h05ciRGjRoFABg1ahQYhhG+xPvYqnmKjY1FixYt4OfnBz8/P7Ro0UL257V//34wDIOZM2fi9OnT6NatG/z9/REYGIh+/foVaz1VoYOnjIwMh5dcKVeuHLKysgp7SkIIIcQjjRo1CgqFAsuXLwfLslaPi7NOBw8exOeff45y5cph8ODBGD9+PKpXr47vvvsOLVu2RFpamsvXce/ePbRs2RJr1qxBs2bN8NZbbyEkJARdunQRllMTS0lJwcSJE6HT6dCzZ0/873//Q/v27bFt2za0atUKJ06cEPZ9/vnn0bdvXwBA3759MWPGDOGrIP/73/8wcuRI3L17F2PGjMErr7yChIQEjBw5Em+//bbsc06ePIlnn30WKpUKr732Gpo0aYJNmzahc+fOyM3NdfEnVEhsIVWrVo319/dnMzMz7e6XmZnJ+vn5sdWqVSvsKT1aWloaC4BNS0sr7kshhJAnKicnh7106RKbk5NT3JdSrLp168YCYPfv3y/ZnpubywYHB7M+Pj5sWloa++DBAzYjI8Pq+bGxsSwAds6cOZLtM2bMYAGw+/btk2wHwLZr106ybcSIEbLH+OGHH1gAVsfJzc1l7969a3UtFy5cYP38/NjOnTtLti9btowFwC5btkz2Z8Cf/+bNm8K2gwcPsgDYWrVqsampqcL21NRUNjo6mgXAHjp0SNi+b98+4VrXrFkjOf7w4cNZAOzq1atlz2/JkdemM+/fhe7z1K1bN/zwww949dVXsXz5ctnit7y8PLzyyivIzs5G9+7dC3tKQgghJVDztNG4b0op7suwq7wiBMcClxbqGKNHj8bOnTuxdOlStGvXTti+ceNGPH78GCNGjEBAQAACAgJknz98+HCMHz8ee/bswbRp05w+f15eHtauXYuwsDBMmjRJ8tgrr7yCzz//HFevXpVs12q1ssXdderUQYcOHbBz507o9fpC1TDxM/NmzpyJwMBAYXtgYCBmzJiBwYMHY/ny5WjTpo3keW3btsXAgQMl20aPHo1ff/0VJ06cwKBBg1y+JlcVOnh6//33sXbtWqxduxb79+/Hq6++itq1ayMsLAwPHz7EpUuX8OOPP+LBgwcIDAzE1KlT3XHdhBBCSpj7phQksI+K+zLsMxX+EM8//zxCQ0Px+++/4+uvv4a/vz8AYOlSLigbPXq0sO+GDRvwww8/4PTp03j8+DGMRqPwWGJiokvn52e9d+zYEV5eXpLHFAoFWrVqZRU8AcCZM2ewYMECHD58GPfv34der5c8npSUhAoVKrh0TQCE2im5+ix+25kzZ6wea9SokdW2SpUqAQBSU1Ndvp7CKHTwFBERge3bt+Oll15CfHw85syZY7UPy7KoXLky1q1bh4iIiMKekhBCSAlUXhHiluCkKJVXhBT6GBqNBsOGDcOiRYuwbt06jBkzBvHx8fjrr79Qs2ZNtG3bFgDw+eefY/LkyShbtiy6du2KSpUqCStyLFy4EDqdzqXz87VSYWFhso/L1Sn//fffwjJqXbt2Rc2aNeHn5weGYbBp0yacPXvW5evhpaenQ6FQoGzZsrLXpFAoZOu8xFkqnkrFhS/iYPNJcsvyLM2bN8fly5exatUq7Nq1C1evXkVmZib8/PwQFRWFbt26YfDgwbh58ybOnTuHevXqueO0hBBCSpDCDoeVJGPGjMGiRYuwdOlSjBkzBsuXL4fJZBKyTgaDAbNnz0Z4eDjOnDkjCShYlsWCBQtcPjcfbNiasffgwQOrbXPnzoVOp8Phw4fRunVryWNHjx7F2bNnXb4eXkBAAEwmEx49emQV2D18+BAmk8nmUKancdvadt7e3hgzZozdvhXt2rXD48ePYTAY3HVaQgghxOPExMSgadOm+Pvvv3H58mUsX74cSqUSI0aMAMANgaWlpaFTp05WmZiTJ08iJyfH5XM/88wz8PLywsmTJ5GbmysZujOZTPj777+tnnP9+nWEhIRYBU7Z2dk4ffq01f5KpRKAc5mfhg0b4t9//8X+/fvx0ksvSR47cOAAAKBBgwYOH684uW1tO0exMlM3CSGEkKcNn0x45ZVXcOPGDfTs2VOoGQoLC4O3tzdOnz6N7Oxs4TmPHz/G+PHjC3VejUaDl156CQ8fPsTnn38ueeynn36SrXeqUqUKHj9+jIsXLwrbjEYjJk+ejEePrOvUQkK44c27d+86fF184Dhr1iykp6cL29PT0zFr1izJPp7ObZknQgghhJgNHjwYb7/9No4cOQJA2lGcX9Ls888/R/369dGnTx+kp6dj+/btqFKlCsLDwwt17vnz5+Ovv/7CBx98gMOHD6Nhw4aIi4vDtm3b0LVrV+zatUuy//jx47Fr1y60adMGL730Ery8vLB//34kJCSgffv2VosIt2zZEt7e3li4cCHS09OF7Nl7771n85ratm2L8ePHY/Hixahbty5eeOEFsCyLDRs2ID4+Hm+99ZZQD+bpnnjmiRBCCCkNAgIC8OKLLwLgCqJ79eoleXzevHmYO3cuGIbBt99+i927d2PQoEHYtWtXoZc1qVChAv7++28MHDgQR48exaJFi5CcnIzdu3ejZcuWVvv37t0bv//+OyIjI7FixQqsWrUK0dHROH78OKpUqWK1f0hICH7//XfUrFkT3333HaZOnerQbPqvvvoKS5cuRfny5bFkyRL8+OOPKF++PJYuXVqilm9j2Cc4jla2bFmkpKQUW3X8k5Ceno7AwECkpaWVmMI3Qghxh9zcXNy8eRPVqlWzmiJPSHFy5LXpzPs3ZZ4IIYQQQpxAwRMhhBBCiBOcLhj/5ZdfXD5ZYRtsEUIIIYQUN6eDp5EjR4JhGJdOxrKsy88lhBBCCPEETgdPlStXpgCIEEIIIaWW08HTrVu3iuAyCCGEEEJKBioYJ4QQQghxAgVPhBBC3IqW4SKext2vSQqeCCGEuAW/WKxery/mKyFEin9N8q/RwqLgiRBCiFuo1WpotVqkpaVR9ol4DJZlkZaWBq1WW+hlb3i0MDAhhBC3KVOmDBISEnD37l0EBgZCrVbTDG1SLFiWhV6vR1paGjIzM1GxYkW3HZuCJ0IIIW7DrwmWlJSEhISEYr4aQgCtVouKFSu6db1ZCp4IIYS4VUBAAAICAqDX65/qheCJ51MqlW4bqhOj4IkQQkiRUKvVRfLGRUqPY4aLOKI/h1HaXghWuC9zVFgUPBFCCCHE42Sy2eiVMQmpbAbumO5joe//ivuSBDTbjhBCCCEe57zhBlLZDADAccOlYr4aKQqeCCGEEOJxLhlvCLfvmB4U45VYo+CJEEIIIR7novGmcPs+m4xcVleMVyNFwRMhhBBCPM5FUeYJAOJND4vpSqxR8EQIIYQQj3NJlHkCgFume8V0JdZKRPCUmZmJiRMnIjw8HF5eXmjQoAHWrFlT4PP27NmDLl26IDw8HFqtFmFhYejYsSO2bdtmtW/79u3BMIzVV/fu3YviWyKEEEKIDSmmdNxjkyXb7hjvF9PVWCsRrQr69++PEydOYP78+YiKisKqVaswePBgmEwmDBkyxObzkpOTUadOHbzyyisoX748UlJS8P3336NXr1749ddfMWzYMMn+kZGRWLlypWRbUFBQUXxLhBDyVMlicwAAvox3MV8JeRpYDtkBwG2T5wRPDOvhqzdu27YNvXr1EgImXteuXXHx4kXcuXPHqVWS9Xo9qlWrhsjISBw8eFDY3r59eyQlJeHChQuFut709HQEBgYiLS3Nra3gCSHEU9023kfD9JfBgMG/AbGorCxf3JdESrgfcjfijezPJNuGabpjud+HRXZOZ96/PX7YbuPGjfDz88OAAQMk20eNGoXExEQcO3bMqeOp1WoEBQVBpSoRSTdCCPF4W/WHkc5mIY3NxDb9P8V9OeQpYFnvBHhW5snjg6cLFy6gVq1aVsFOvXr1hMcLYjKZYDAYkJiYiBkzZuDq1auYNGmS1X7Xr19HSEgIVCoVqlevjmnTpiEnJ8c93wghhDyl7ptShNspbHoxXgl5WlwQDdt5QwsAuO1BBeMen35JTk5GZGSk1faQkBDh8YL07NkTO3fuBMAtWLl27Vr06tVLsk+bNm0wcOBAREdHIycnB9u3b8eCBQtw+PBh7Nu3DwqFfJyp0+mg05l7T6Sn0x8OQkjp8kBU2EvBE3EHPvNUjglBRUVZnDZeQYIpCQbWABVT/KFL8V+BAxiGcekx3uLFi5Gamop79+5hxYoVGDhwIGJjYyU1VHPmzJE8p2fPnqhatSomT56MzZs3o1+/frLHnjdvHmbNmuXgd0IIIU8fceYp1ZRRjFdCngYPTY/xiE0FANRRRiKA8cFp4xUYYUSCKQlVPKCmzuOH7UJDQ2WzSykp3H9WPgNlT82aNdG0aVM899xzWLduHTp16oQ33ngDJpPJ7vP42XhHjx61uc/UqVORlpYmfMXHxxd4PYQQ8jR5YKLME3Ef8Uy7OspqqKwwB0ueUvfk8cFTTEwM4uLiYDAYJNvPnz8PAKhbt67Tx2zWrBkeP36MR48eObS/rSE7ANBqtQgICJB8EUJIaSLOPD1mKfNECkdcLF5bWQ1VlRWE+3coeHJMv379kJmZifXr10u2x8bGIjw8HM2bN3fqeCzL4sCBAwgKCkJoaKjdfWNjYwEALVq0cO6iCSGklDCxJjxgxcETZZ5I4VgGT1VEmSdP6TLu8TVPPXr0QJcuXTBu3Dikp6ejRo0aWL16NXbs2IEVK1YIPZ7GjBmD2NhYXL9+HVWqVAEA9O3bF/Xr10eDBg0QGhqKxMRELF++HAcOHMA333wjzOA7dOgQ5s6di379+iEyMhK5ubnYvn07lixZgo4dO6JPnz7F9v0TQognS2HTYYBRuP+Yap5IIV2wGLa7KQqYPCXz5PHBEwBs2LAB06ZNw/Tp05GSkoLo6GisXr0agwYNEvYxGo0wGo0Q9/xs3bo1fv/9d3z99ddIT09HUFAQmjRpgq1bt0pm21WoUAFKpRKzZ89GUlISGIZBzZo18dFHH2HSpEl2h+0IIaQ0Ew/ZAVTzRAqHZVkh81SRKYsghT+qwPy+fttDlmjx+A7jJQ11GCeElCZ79CfQPWOiZFtm8F54MdriuSBSot0zJSEitS8AoIuqGbYHfAmWZRH8uAsykYOaigjEBRW8tq0rnqoO44QQQjzXA4vME0BF48R1F0X1TnVUXI9HhmGEovE7pgcwsfZnyj8JFDwRQghx2X2TTCsZEw3dEddYting8e0KdMiTTFAoLhQ8EUIIcdl9mTcyyjwRV1nOtONV8bBeTxQ8EUIIcdkDmcwTtSsgrrooCZ6qCrfFjTLveEDROAVPhBBCXGY52w6gzBNxjXimXRVFefgzvsJjVSWZpwdP/NosUfBECCHEZeJFgXnUroC44q7pIdLZLADSITsAqCxaz84Tej1R8EQIIcRlspknapRJXHDRRr0TAI/rMk7BEyGEEJfoWQOS2TQAgAZqYTvVPBFXiGfa1VVGSh4LY4KhhQYAZZ4IIYSUYA/Zx8LtZ5SVhdspVPNEXBBnvCXctsw8KRgFKivKAeC6jBd3f28KngghhLhE3OMpWllFuE0F48QV10zxwu0oZYTV4/zQXSZyiv01RsETIYQQl4jrnaIUlcGAAUBNMolrbhgTAHBDdOKZdrwqSs/p9UTBEyGEEJeIZ9pVVJRFIOMHAEilzFOJdcd4X5jx9iRls7m4l/96ilRWlN1H0uuJgidCCCElkTjzVF4RihDGHwC1KnCnZFMaYnV/IsH0qMjPtUd/AtXTXkSN1BeRbEor1LHyWL1TQdgNY6Jwu4aikuw+njTjjoInQgghLhF3Fy+nCEFwfvD0mM3wiMVbnwajsuZgTNbH6J/xbpGfa1veEbBgkcKm4y/9CZeP89D0GNVS+6Pi4z7413DVoedcN90VbjuSebpdzF3GKXgihBDiEnHmqRwTgmAmAABgggkZbHZxXdZT5R/DeQDAKeMV/Ge8W8DehSNep/Cs8T+Xj/N73l48YFOQAx3W5u1x6DnX8+udAKC6Qj54qkrDdoQQQkq6+6xF5knhL9ynobvCy2Kls8p26o8W6fnEwfB543WXj3PQcEa4fVnUfsCeGyZR8GQj8xSuKAMllACAO8W8RAsFT4R4AB2bV9yXQIjTHuS/2QYyfvBmtAjJzzwB1K7AHe6aHkru79QfK9LziYdhXQ2eWJbFQf2/wv3LxtsOPc+RzJOKUaGSoiwAmm1HSKn3auY8BD/uiljdn8V9KYQ4he/zVJ4JAQBh2A6gzJM7xFsET/v1p5HL6orsfOJhu3jTA5daTlwx3ZE0T71hSnTomvmaJ3/4oAwTZHM/vu4pmU1DZjEODVPwREgxymF1WJa3FXnQ42fd1uK+HEIclslmIxM5AIByilAAEArGAWpX4A4JFsFTNnJx2HCuSM6Vw+qQxmZKtrmSfRJnnQCu/u1aAbVaetaA2/nDcNWVFcEwjM19q3pI0TgFT4QUI/EbTCp9UiclyANJmwIu8xSiEGWeqFFmoVlmnoCiG7p7ILPA8zkXisbF9U68guqe7pjuwwgjAKC6jTYFPMmMu2IcuqPgiZBilCr6pJdqyrSzJyGeRdLjSRi2M2eeqOap8OR6O+0qoqJxcfE/77yTwZNlvRPvssl+3dN1UbG4rTYFvKaq2nhJ0wmTvYZKAqknTVVsZyaESAKmVJaCJ1JyPJDMtOOH7ajmyZ3iRTPKqisq4ropAReNNxFvfIAIZTm3nks282RwLni6bkpAIpsEgGtoyWeGCioad6RYnNdb0xq9Na2duq6iQJknQlzEsmyhGwGKh+2ykYs8Vl/YyyLkibgvM2wnzTxR8FRY/Gw7DdQYoukmbN9VBEN3csHTBeMNGFmjw8c4aDBnnUZoewltBQoatnMm8+QpKHgixAUG1oAOGW+gcurzOG9wvR+K5fIFlgWbhHiq++Lu4gyXeRLXPD020bBdYd3NH7arpCiL7poWwvaiqHsSz7Tzhw8AIBd5+M/keGPOg/ozwu1OqiaokZ9FumK8YzcIuyHKPNUoIPPkKSh4IsQFRwzncdhwFvfZZPyg2+jycSxnJNHQHSkpxNPR5TJPnjhsd9l4G1eNd4r7MhySyWYLfx8qKcqhiTJa6KO1x3ACetbg1vOJezx1UDcWbjszdMdnnryhRVNVLTyjrAKAC8LsFXfzDTI1UKNifh8nT0fB01NGx+Yhm80t7st46j0SvXGcK0QnXstgiYInUlKIM0/l82uefOAFDdQAPK9VwUlDHGLShqJu2lCcNVwr7sspkHimXYQiDEpGiS7qZgC4jPUxw0W3nk88DNtV3Vy47ejft1vGe0LX7xaqutAwakTnB08AEGej7ollWaHmqZqiApSM0ulrLw4UPD1FHuUvxljhcW+Hu7oS1ySJVhy/YLwOlmVdOo518ORZbziE2MK/2SqgQNn8poYMwwjZkRQPey1vyjsIFixMMGGL/lBxX06BxN3F+WxMN1FQ4+6hO3Ew3EndRLjtaLsCcb3Ts+oGAIBayqrCNlt1T/fYJOSAa6JZUuqdAAqenio79EfxkH2MLORgtW5XcV/OUy2JTRVup7NZLq+zlGbxBmNZA0WIp3qQXyNTlgmSZAv4obvHHtbn6ZThsnD7X8PVYrwSx9yVZJ64mXV85glwf8sC/vdZhglCDUUlBDC+ABxvVyCud2qragAAiJYET/If6K9L6p3s93jyJBQ8PUUSTUnC7SumkjGu/6Rls7l4O2shPspZ6nK2CACSRZknwLVmcoBM5omKbEkJwLKseWmW/HonXnB+0Xgmctxel+MqlmVxymgOnk4brxTj1ThGPGxXSREGAKigKIMGypoAgFPGK3hoeiz7XGeJf5/lFMFgGAYxyuoAuAV4HQmE+cyTBmo0V9UBAEQrKwuP2+r1dKMEzrQDKHh6qtwTB080bCdrTd5ufKX7DR/l/IwDButmbo5KZqXB0wUX657SLDJNVPNESoLHbAb04AIjvscTzxMbZd4y3ZMUsN81PXRb4FFUEmSCJ0Baj+SulgXpbBZywS1Ozs+crKesITx+3njD7vPvmh7ihikRANBMVRvejBYA4M/4Ctd+2XhL9gPrf6LMU2QJmWkHUPD0VBFnnq4a453qz1Fa3DAmmm+LPvE4K8kieHJ1BXLLTBMFT6QkEHej5ruL80I8cMadOOvE8/ShO8uCcZ44ePpLf8It55L8PvMzidLgyX5mXdxVnB+y40UruKLxx2yGZIYmT/x3uDplnkhx4Du7AoAOebhluleMV+OZxI37kiyG3pyRbEqV3Hc985Rpcd8zPqkTYo94Zpa9zJOnrG930iATPHn40B2fefKCBqFMoLC9paouvMFldvbqTxWq/IAn9/uMUVUXthVUliBez66tuqHkMXHReJxM0Thf88SAQTVFBUcvudhR8PQUEQ/bAcDlEtLP5EkSfxK2HHpzRrLFJ+orxnjksjqnj0OtCkhJ9MBknangBYsbZXrIh4FTcsFTMWSeHpvSkcXmOLQvn3mqpAgDwzDCdi2jEWazJbCP3FLfKvl95mcS6yojwYA777kCGgHzmScVlGipqit5TNyuQK5onM88RSjCoGU0Llx98aDg6SnBsqxk2A6guic54k/ClkXfzkiyyDwZYbTZx8QeapJJSiLposD2ap6KP/NkYk1CgXh5JhQ+8ALw5IvG9+pPITy1D6JTB+KS8abdfdPZLGHmrbjeiddRZW5iuVd/stDX9kDS8JT7ffoxPsI6cxeM122WgdwzJeGqKR4A0ERVC76Mt+TxZyTB0y3JY49N6cIH2pJU7wRQ8PTUSGbThAJO3pUCVrIujcTBiquZp1xWh0xYf3p0duhOx+YJRZrm66PgiXi++5JFgS1rnjwr83TdlCAMjzdV1UJ9FTdb7aYp8Ym1U2BZFu9mfw09DLjHJqNPxmSrkQKxuzaKxXmd1E2F244GTyzL4rLxNjLZbKvHJEvtiH6f/Iy7HOgk68+JiVsUtFM1tHrc3rDd9RJa7wRQ8PTUsMw6AbY7upZm4sZ9lkXfjhIP2fnB/CnL2U7jljPtuG3F/2ZDSEEeSBYFtsw8mYMnT6h5Eg/ZNVZFo7HyGeH+v8Yn02l8l/44/jWahwlvm+6jb8Y7soEMAMQb7QdP9ZU1hDqo/YZ/HZoc9LPuD9RNG4IGaS9blRg8sJFJrKcyF43bqns6YDgt3G6ntg6ewphgIRtpOcQoXtOuegnq8QRQ8PTUuCf65MCjYTtr4pqnFBeH7cTDfXztAQBccHKBYLlu4qkmyjw5ysAaYGJNxX0ZpZJ0UeBgyWPBCvOwnSd0zD8pmmnXWBmNhqoo4f6/hiczdDc/9xfhNv+B67TxCoZkzoBBphfWXVHT3QiZ4EnBKNBe1QgA9zM+bSy4fmuZbisArm2DZQG93Gw7QDrjztYadwdE9U6tVDFWjzMMIzTLvGt6iAzRh0bKPJFid4+1zjwls2lWtTmlmYE1SGa3uZp5Ej+vrrK6MEzhbLsCuSE6y9l3xNo1YzzGZM6F/+NOaJY+Gon5K88Tazo2r+CdXMB3o9ZAjSBRjRMgHbbzhFYFlpmnhqLM05OoezqiP4dD+bPRohVVcCjgBwQyfgCAbfq/MTF7odWMubui13RFmeAJkC6hUlDLgnQ2SxJEXrDo28RnnlRQSn5/kuBJJvN0z5QkZJOaqGrBj/GRPX8tSd2TOft0vYT2eAIoeHpqiIftxGleuamhpZVlsJLCprvUC0vcpqAsEyTUBdxnk/HIicZ7cp/KM5Ej+0mUABcNNzAscybqpA1BbN426GHAGeM1dM+YSB8SZKzR7UbI424YnPmh2499T9RdXDwTDLBoVVDMwZORNQrZpcqKcghTBKOWsiq04GZ1PYnM0ye5vwq33/EehhhVdfzm9zHUUAEAvtdtxLe69ZLn3LXR40msoyh42qs/ZfcaDunPwgjz3zrL+kw+kxjGBEPBmMOCKorywu/zgP5fq0XnC6p34kUrqgq3xUXjlHkixU5cfCieiXGF2hUILItXWbAuFWiLa55CFQFC8AQU3IlXLM3GEJ1cLVRpxrIs3sj6FPXTh2NN3m6YIB2qu2S8hR4Z/6OlbSz8rPsDOuTht7y9kjfjwrpreiis7ShXp+JJHcavmO4IkzsaK6MBAGpGJfyfvWqKL9L1JM8YrmKb/m8AXPA2WNMVANBR3RhLfN8T9vsk51dJ9ileNGwnV/MEANUVFVFFUR4AcMRwDjl2WqVYFpWLs+RG1oiH+b9Py/o1BaPA8+p2AIAMZGNr3mHJ4+LFgOXqnXjidgXiD/R8zVNZJkhYS6+koODpKSHOPHVQm4MnW+sJlUZyn4LFC/w6SvycUFHmCXBuxp2tIMkT6kQ8yX+mu/hBt0m4X4YJwlzv/8OpgOUIZ8oAAP41XkWfzCk2C3BLI3Fdkq1FWV1x3HBRuM2vYSamYlTwBzd8U9yLA1sO2Zlvm4fuzhqKrmh8Qe4K4fZkr6FQMyrh/nBtD3RXtwDANTgWF5Qn5A/beUMrGUYTYxgGHVVc9kmHPPxtOG/zOvYbpJmpC8YbQrCWzKYLWSnLmZMAMETbVbi9Kk+64HxB9U68WhYLBKeaMrBHfwIJLPd9lrQhO4CCp6cGHzwxYNBWlD617KtRmsnN/El24Y+7uGC8jCIQMaIZKeedKBoXB0nlREtcULsCKfGSOgM0HXE96He86z0c9VU1sTNgEcowQQCAfwzn0S/jPZealT6N+LokwL1/B44ZLgm35YInAAjJb5RZ3JkncfDURFVLuN1QMuOuaJplXjPG4/e8fQC44bBR2t5W+/RRtxFub9UfAcBlWvnMU4RFg0xLHdUF93tKMqXirEW9UhqbKZzjvqRBpjTzBHDDcXz2a4f+qDBEft+ULHw4t1fvBHDDf3xX9K36IyiT2h3dMyYKj5e0ITuAgqenBl8wXo4JQWVFOeGTHw3bmck17EsudOYpEHWU1YROvM4UjYuDpMr56XfL7QS4Y7ov3O6gaixpwldLWRU7/L8Uipb3GU5havZ3T/waPY2eNUgyre78O3CsgMwTYB66S2HT3bJ8iKukM+3MAZN4xt3pIqp7+ix3pTDEPMFroLBYrlgvTWvh9p/5Q2LpbJYw1GirWJzXUVI0Lh88iRdAV0Ep3OZLDOz17AK4obuBms4AAAOM+C1vLwDpenb26p34YzyjrAwAVsPuCijwgqaD3ed7IgqengIm1iQUcIYryoBhGKGr6y3TPbtj4aWJ3Kdgy2VWHCF+ThkmEL6Mt9CJ96LxhsNF6OKZdVWV5UXbadhO7LYoeBIHmbwGqihs9f8MXvlFwCvydkBfyovuLRdgddewnZ41CNmcaopwhCmCZffjez0ZYESWTEPZJ8HAGnAmfwmW6oqKkmVj6iojhYLtoljjLs2UiV91OwAAgYwf/k/bT3a/SoowNFByTTtPGa8g0fTI5oLAcsopQlBXGQmAmzkoN0y6T1RM/pKmk3CbLzGQ9uyyDp4AYKimm3CbH7oTB2VtCwieAOBNrwFQQQkvaNBcWQeva/vjZ9/3ERe4Gn01bQt8vqcpEcFTZmYmJk6ciPDwcHh5eaFBgwZYs2ZNgc/bs2cPunTpgvDwcGi1WoSFhaFjx47Ytm2bzf1btmwJHx8flClTBiNHjsTDh+4rtCwqj9hUYcy6goKrAeEL9FiwuErZJwA2ap5cmKXFz7ZTQilMOeb/gNnrxGtJPGxXRbQgJmWepMSZpypK6+AJAFqo6uI5zbMAuCBZ/Ie9NBK/IQLuG7a7YLyBHHAfxmxlnQCLdgXFVPd0yXhL6OAvrncCuPXh+P+zccbbVrPICuug4V/kQQ8AGKrpikCFn819e6nN2adtef84VCwuxtc9mWCSfd3zwZMKSvyftr+wXT54sh62A7hmmfzP6x/DedwwJgj1Tkoo0Vptu96JN1LbC6nBu5EavBtHApfgK99JGKHtherKktUck1cigqf+/fsjNjYWM2bMwPbt29G0aVMMHjwYq1atsvu85ORk1KlTB19++SV27dqFH374AWq1Gr169cKKFSsk+x44cAA9evRAuXLlsHnzZixatAh79uxBp06doNN5duZGXCxeIf/FL57dQM0yOXKfylzJPPF9nkKZAGFab4yoH4qjRePiIKmqOHiiRpkSd0RvJpUV5Wzu1y9/VhAAbMo7UKTX5Oksg6d7bLLN2Z3OEA/ZNVPVtrmfuFEm/6FFx+ZheOZMdEx/UyiILkqnLJpjWuKH7kww2eye7SrxEFoXdTO7+/bWiOueDkt+Ng4FT5KWBdKhuwSTeeHgpqraaKKKFobuLgjDdubXirj20tIQUfZpYe4ac72TMtpuvZOYF6OFSlQ0X5J5fPC0bds27N69G99++y1ee+01dOjQAT/++CO6dOmCKVOmwGi0PUQycOBALFy4EAMHDkS7du3Qr18/bN26FRUrVsSSJUsk+06ZMgVRUVH4/fff0aVLFwwdOhTr1q3DhQsXsHTp0qL+NgtFEjwx0swTAFx2w6rbT4MU2WG7VKePwxeM88sjAECMKlK4XdAK5DzxbLsIUVBAs+2k+MxTaP4QqS3dNS2E/j2b8g6W6u7jD9kUq23umHl7zHBBuG0v8xQss77dz7o/sDpvNw4a/sWPuZtln8eyLIZlzsQzqS9Jir0tTc5ejIjHz6FXxiR8lbsOV413rGqrbM2040mKxg22i8ZX6XbCO6UdhmXOdLh+a6+BC2KUUBY4pNVY+YxQqP2X/iSuiv5eOxI8tVU3gDI/INqjPyG5xv1689IpHVSNoGHUQu1RnPEW8lg9HogLxm1kngBgUH7dEwB8L5r9aq9FwdPM44OnjRs3ws/PDwMGDJBsHzVqFBITE3Hs2DGnjqdWqxEUFASVyhz9JiQk4MSJExg+fLhke6tWrRAVFYWNGzcW7psoYvdFwVO4MGxXVdhGM+44csN2zs62Ey8KHKoQBU8uZJ742iZfeKOsIki0nTJPPANrQEL+67uKTL2TmD/jiy75C6beZ5NxVJQlKW0eyDRrdUcG+nj+TDsN1EKtjhxpryeuGe3CXHOpha3/I6eMl7EmbzeumxKwIGeF7D43jYlYmLsG99hk7NQfxdvZi1A7bTCi0l5Cl/S30CLtFdRNHYJYnbk8o5GoNQHPkaLxPFaPydmLoYcBa/J247jxkux+YommR7iU/ze3iTLa7pAdwBVT99S0BMAN+6/WmdsBRNjJtPICGF8hkL1qisf3OvP7lbjeiW9hUze/tYoBRlwx3sF9B2qeAKCysrwQCIqLvtvlLxNT2nh88HThwgXUqlVLEtQAQL169YTHC2IymWAwGJCYmIgZM2bg6tWrmDRpkuQc4mNanseRcxSnRFY8bMcFT9UVFYX0LA3bceQKxp3t82RZLM6LVIQLU3EdnXHHD88FMX4IYsx/YKlJplmCKUmo57M3ZMfrpzEP3W3Ul96hO8thO6DwReMppnRhCKiBsia0jMbmvuKap8emDGzRH8INk7nlhK1ruShqMnvIcEY203PYcFb2uTdNidhnOIWTxjhcNt0WarNqK6vKNmCsr6wpZGzEQ3xim/IOSIrvl+v+lN1PbJ8o2yMeUrOnt6hlwT3R7LdKirIOPX+q18vC7SnZi3HRwPVx4jNgWmjQUlUXgLk+E+CC2Af55/OGFn6wP/w2VNNVct/ReqenkccHT8nJyQgJsY6G+W3JydYL4lrq2bMn1Go1KlasiIULF2Lt2rXo1auX5BziY1qex945dDod0tPTJV9P2j2ZzJOaUQkzwK4Y75TqIQweX/PkDx9hcU5ni1nFBeZlRNkiJaNEnfw/SjdMiQ51LeZrnoIUfpL1wWjYzuyOZKZdwcFTb3Ub4Q1xU96BJzZNXs8aMCxzJqJTB9odAnpSZIftChk8HRf1d2phZ8gOsKx5ysDnuaslj18z3UUeq7d63kXjTeH2Q/Yxrpnirfbh14kDgO993sV879fRQdVYmD3HgIE/fBDOlEEj5TP41Pst2Wv0ZrSIyf8/e874n6SeSzi+aHgKANbm7SmwuFxcd9TJweCpk7qJMOTM84W31bqBtvTQtMSb2hcBALnIw7CsmYgz3RLqBVupYuCV3ypBnCU/b7wuZJ7KK0Lt9pQCgBc0HaCBWrjvTL3T08bjgycAdn+hBf2yAWDx4sU4fvw4Nm/ejG7dumHgwIFYvXq11X62jmXvHPPmzUNgYKDwFRERUeD1uFuiTPAEmIfucpEnme5dWvHDdiGKAGHIrTCZJ8vOv83zC2hZsNitP273OHrWIEzhDmT8ESj6ZEyz7cwkxeI2ZtqJhSoChZ4zN02JOGssuu7RYt/rNmJN3m78Z7qLeTmxT+Sc9oiH7dyVgZb2d6prd19xzdM2/d84apBm740w4j/TXavnXRIFTwBwSLR2Gu+wnss8qaHCUG03TPYeit0BXwkzuXTBB/E4ZDfuBG/G8cCl6KZpbvM6xbPPZudIa1svGW9Klh8BuB5M9iYjsCyLv/KzPd7QCtmegvgy3uiglg5/VVKUdej9jTff53Uhq3TeeB39M8zLv4hXnRCviHDacEX4uyjX48lSkMIfvdSthPultd4JKAHBU2hoqGzmJyWFi5blskWWatasiaZNm+K5557DunXr0KlTJ7zxxhswmUzCOQD5LFZKSordc0ydOhVpaWnCV3y89SelosZnnpRQomx+t2UAQmEg4N7lGUoilmXNwRMTIAy5pbAZTmXlxIsClxH9rAFpw7steYfsHkdc1xTE+EHLaIRhP6p5MhMH/QXVPPEkQ3dPYNZdsikNH+X8LNy3NQT0JPFDMV7QoLayGgBuEdbC9L8S1/vYm2kHSGuexJmiaIX8Gmc8y+DJcojuvikZV/OzUU1UtSSNJ7WMBn6Mj2Rh24K8rO0hZDR36I9KsmtLcjcJt19Qm5s4LtfJt7oBgGumeGEdwdaqenaHNi2Jh+4Ax4rFxbwYLVb4zhIyWOLgtINovdPKinLCMKb451vezkw7sf/z4gJOBgz6a9o7dY1PE48PnmJiYhAXFweDQfqf/vx5bh2funUdi+zFmjVrhsePH+PRo0eSY/DHtDyPvXNotVoEBARIvp40PvNUngmBkjF3kJW0Kyjla9xlIhuG/NqZYCYAIfnBkxFGp4IVvk0BwC3NItZe1Uj4o7RN/7fdNyrxOfmsE98zijJPZuJhO0eKZwHgeU1boeP7pidQ9zQ7Z6mknu626b5kCR97DKwBX+SsxttZCzEj+0d8lrMSS3I3YVPeAWSxrjeX5DNP5RQhwt8Bg41sjyNMrElY0y6MCZa01pAjtx5bBSYU07xHCvctg6cMNkuSaQSAQxbB0xHDOeH2s6r6jly6XRpGjfdE9UJzcpYBALLYHPyaxzW59IYW3/u+gxr5iyDvM5zCLeM92eP9JRmya+rUtYgzOoDjr3exuqpILPB5Q7LND95oIpptyDCMUGLA98EC7M+0E+ukboK/A37EsYCfJUvelDYeHzz169cPmZmZWL9+vWR7bGwswsPD0by57ZSsHJZlceDAAQQFBQkZp4oVK6JZs2ZYsWKFpPXB0aNHceXKFfTv39/W4YqdgTXgQX5BYwXRkB1gOeOudAdPj03mN7cQRYAk8BEHRAVJFu0bwkiDJw2jFhb6fMxm2CxsBaQBEl/XwBeNp5qo5oknfjN1NPNUQVEGLfKHSy4abxbphIlLxpv4Tmc9G9fR7NP3uo14J+drfKX7DXNzl+O9nG/xevaneDHzfQzJnOHSNRlYg/A6DWNCJH8HXP1ZXDPFCwFic1WdAoeTQhTWtTpveg1AfZV5hp5l8HRJJhN1y3QP8Ubza0A8jNfGDcETwDVv5AOVbfq/cdIQh7V5e4QPOAM1nRGsCMDL2p4AuGH5X/K2yx5LXO8kXnfOERHKcpIZjBUdLBa39Lr2BfQUBWLPqhtIFiQGINR6iTkybMdrpqotO4OxNPH44KlHjx7o0qULxo0bhx9//BH79u3D2LFjsWPHDixYsABKJZdpGTNmDFQqFW7fNv9x6Nu3L6ZPn44NGzbgwIEDWL16Nbp3744DBw5g7ty5khl8n3zyCS5fvowBAwZgz549WLVqFV566SXUrVsXo0aNeuLft6Meso+FaaPhFsHTMwoatuOJ2xQEM/6SHk3JzgRPJtuZJwB4Tv2scPuP/LWq5KRKMk9+kn8zkA1DKV9ehHfHyGWevKG1Gia1R9ow86C7L0swJftrYTZgPVEhrqPrpW22M7z7p/4IjujP2XzclkdsKlhwhfLlFMGIFv0dkBsqc8RxBxYDFvODj1C4D3DFz2O1fVFDUUnYbtlCRTxkJ/5diz+E8LcZMGitsp4d7Qou+zRcuD8nZxl+yDUHxK95cUurDNd0FzKav+i2WQ33G1kj9hu4mXYhTIDdVg62iIMeW930C8IwDH72fR+1lVWhgALjvQZY7SMuGufJLQpMbPP44AkANmzYgOHDh2P69Ono3r07jh07htWrV2Po0KHCPkajEUajUTK7pnXr1tixYwdeeeUVdOrUCePHjwfDMNi6dStef/11yTnat2+Pbdu24d69e+jTpw/Gjx+PDh064K+//oJWa72go6ewVSwOAIEKP4TnN82k4MmczQlm/CU9mpIcHGIBpAXmcm/m3dUthALdP/SHbc72Eq9fx2ecgkSf1tPZbJvXwLIstuQdQsO0l1EvbahQY/G0YVlWqHmqoijvVPHs86K1soqq7ml73j/YqT8KgBti+dF3qvCYvQaPvCw2RxiGqqwoh+3+X2Kd3xxM9Bok7GNZxOwIcZsCbtiuqnDf1QWCpZ3FCw6eGIZBiKjuaYy2N4IVAdAwatTMH/66bLwjWQdSPNNupNY8G5oPmFJNGTib3wm8vrJGgf2TnDFS20uoMdqqP4JT+evdNVI+gyb53ckjlOXQWcUNxd0y3bNaCuVf41UhO9de1UhSQuGo171eQLSiCuooq+F50QcAZ5VVBONkwHI8Dt6Frmrr0Zm6Mpknez2eiLUSETz5+flh0aJFuHfvHnQ6Hc6ePYtBgwZJ9lm+fDlYlkXVqlWFbe+88w6OHz+OlJQUGAwGJCUlYceOHZI2BWJdunTBP//8g5ycHCQnJyM2NhZhYc4V7T1p9yRLs5SxeryGkpv9l8SmItPOG/LT7rHFLDlx5inFicyTONAqw1hnnoIU/kIjuZumRGEJBEv2hu24x+WH7q4a76B35mT0z3wP543Xccl4C5/aaCZY0iWzaUKvHmfrPyKVFYVP/ieNcUIGy130rAFTshcL9+d7v44GyprwzW+BcdqBxWYP6s8I6591V7dAF3Uz9Nd0wDzvcYhUhAMA9hhO4G+9dS2mPeKZdmFMCGoqI4SMiasfovjgiQGDpjLduuXwiwZz2Y+XhO18DZYOebhlMtcOiTNPo7W9hQwVX3D+t+G8kFF7VtXApe/DFi2jwbui7BPvNe3zkqB9lCioi7UoHN8rakjpaH8nS+UVobgQtApnAn5FiKJw9bMaRm2zI79c8FTOwZonwikRwROxzV7mCQAqiraJ9/Uk+/Wn0S59HH7O3VJk57CqeWJcyzzxgZZ4UWBLz4nWqtqilx+WEa9fF5T/CVoaPEmLxrPYHEzN/g7104YL2Q7eL7rtyHgKG2vedmBBYHvEC66eNMa55Zp4S3SbhOVOWqpi8JKmE5SMEg3ya3ocKRoXt7MQr3+mZlSY6j1CuO9s9knc46m8IgTejBbV8gu8rxhvO937KpvNxbn8xq91lZHwl2k4KWeK1zBEKMphjvdrqKYMF7bXyp/9B0jrnPjgKYjxR01FBBoqo4R9kkypkuLxNmr31DuJjdb2RkXGXGcUwPhikLaLZJ/nNM8KH3bW5+2T9HP7S39CuO1ofydbnMmyuiJYEWA1m8/R2XaEQ8FTCSe3rp2YOBv1JBbjdMWE7C9wxHAO/8teJEnju5NlzVOIwrWaJ/GiwLb+wPWR1D3JB0/i2XYBFrPtLB8HgEGZH+LT3BXQg6uFqqQIEwpmM5CNVaIlHZ4W0gaZzgdP1ZUVhdvu/uDwk84c6H/hM0F4LTQWrZdWUNH4HgMXPCmgkEwlB4Bhmu6olp992m04btUnyR7LYTsAeCY/25OBbMmKBI44Zbgs1HUV1KJAbJi2O24GbcA73sMk22uJZgHzNVjpbJYw/FxbWQ0Mw+BZdQNhvyOGc5LaJ3cVi4tpGQ3e9TZnn4ZrultlbrwYLQZruIAqBzoMyHgff+QdlgzBRijKCTPzPJll9onPFBLHUPBUwt0XtfKvIJN2Fc/YuOfkG8iTKFq+Y7wv1DpkI1fShNKdpMGTNPPkVPCUn02QKxbnVVGWFw0ZXZYNWtNkh+38ZR83skbsyf9Uq4EaU71G4GLgKiz0mSjs871u4xPrpv2kuNLjSSxc9Np3Z/B03XhXWIKnubIOmoqmazcSDWnZq3uKNz4Qsi7NVLUl9W4Al30SFzHPzp9C74gHrHTYDjAHT4DzReN/G8zDho4UixdEnHnii8bFQ3a182u02ooCpF36Yzhp4LKHUYoIp2aGOeNVbV9M0A7EIE0XzPJ+VXYfcT3WX4aT6Jf5Lqqk9hOm/XdSNSnyzJE7iIvGgxh/oQM5cQwFTyVcwcN25jeQBAffQIysEX0yJiP0cXe7M8bcYZdFJ+77JutGpe5gWfMkXlpFbnhlU94BrNLtlAQkuaxO6Apu2abAkjj7tFXmZ5hq0SRT/K/l4/Gmh0LGqYe6JWb7jIUv440GqihhSv5543W7rRFKojuiKequ9LwJL6Ih6y2i32dfUWE6ADQWBU/2ZtztMZiHeLqomsnuM1zbQwgad+qPyi4fIuehJPPEZRNqiZpTOtuuYLv+H+G2O2qNnlFWFmqwLskGT1xw1Uo0m+4X3Xbh/0BRDNnx1IwKn/u+hRV+M60CWl5jVTQW+kyUDPGJaxSdbVFQXMTtCmjIznkUPJVwfDZJDZWkCJonHspLdHDY7rDhHLbr/0EWcvBV7jr3XKgNlvU7D2TW5HIH8Rp2IQppwbhln6e/9efxYub7eDnrI2zWm6e521oUWM5zGnPwtEUvFzyJZ9vZLxi/bkoQbouHogBgnGh5ie9l+g2VZHcKm3liiiZ4Ejfe7Cv6PQNcVsSRonFb9U5iGobLMvIcrX2SDNvJZJ6cCZ6STWlC5ukZRWXUVBZ++SkfxktosnnZeAssy+KiwTp4ClUECkNL/MQBwP3F4q5402sArgf9jk1+C/Cc+lmhuN0HXuhs4/fpaeqKlmkpqkze04yCpxKOf1OooAiVXZagogtDFxvy9gm3TxkvF9miwnrWIKwDxXsominkTuIO0CFMALwZLXzgBcB6tp14PSvxm5ytRYHlNFDWFLIl+/SnrAq6U2U7jIsWBxYVlF83mrtCW9ZSvKjpILRM2JC3v8gyd8WBb5CphFIy8cFRgYyfsOSNs0PWtjw0PRaCiVqKqpKgBIBV0bj4NcMTD8MGMn5oZqdLs73lQ2xeY/6wnQZqoY6ulqhdQZwTwdMO/VGhj5x4+aHC4q8nEzm4a3ooyTzVEQ3rydU2FUW9kytUjAq9Na2xwX8+bgatxw8+72JvwNclpnaotrKq0Auws5Pd0AkFTyWanjUIfyjlisUBaR1UAltw5snEmrAhb79wP53NEtaScrejhguS2SpA0Q/baaAW3lCFxYEthu3Ef8hPGMyztMQZKrksnxjDMOiTP9srD3rs1B+TPM7XNHlDK6x/FaSQLxgXL6kRaZF50jIajNb2BgDoYcBS3Va711WS8DVPFRVloLLokOwIhmGEDw/OFknbslV/WJgu/5xF1oknKRqXGbr713hVqMHrqGps93uzXD7EkbYU/P+hcooQofYmVBEoBNnOZJ62irKmlmuvFYZ46ag40y3h/1ww4y9p1miZZaqkCCtwaZjiEK4oizFez5Wo5UpUjAp/B/yIfwJ+krzGiGMoeCrBxIGGXL0TwL258n80HRm2+8dwAfdYaQBz0uDead68HRZDdkARDtvlZ55CRLPk+KG3ZDZNUtskLqg9Z/wPOSw3ZJBicjx4AoA+ojfXHXnS75UPjsQz7AJtDdsZzcN2crN4xmqfF2pIlug2PRXdybPYHKGQ35V6Jx4/2zSNzSzUWnE8cbdyy3onnrho/LTMjDtHhuzEXtb2EAKKTfqDuGqn0aWRNQpBfjlGmgHhFwpPZJOsPrTIyWPNQX8w449WKufXEbWltii7dNRwUfhgx8+041nWN7VR1S8RxdglRaDCD01Vtehn6gIKnkow8adpW8ETYO71dM+UXOAQ3HrRkB3vhANDBa7YZZGNAYpu2I6veRKvucUvXmqAUXgzMbJGSfBkgBH/Gq4CsL8osJw2qvpQ5P8Xu5A/O4vHD9uJ65xsFYxfz888qaFChMxK61WVFYRFRe+aHuJP/d8FXtuToGPzMCnrK4zP+tzpwMWVNe3kuLNoPIPNEhZ+rciUFTpPWyqoaHyXk8GTF6PFW/lNJlmw+DJ3jc19k9g0YZgtzKKOpZaTa10eNJwR/l/0ULd0KftnizjzJC4TEA/ZAVzZAd8wFHDPYsCEuAMFTyVYQd3FefyUbT0MdhfBNbEmbNDvB8C9UfOOF0Hm6b4pGf8auaCkmuiPo61hu2vGeAzNnOFSI808Vi/MkgsWrfYurlvil125ZbonWWkcAE4YL0n2AYBQB9ZZ82a0wh/+S8ZbQuBqZM3BWmABwRPLskLmKVIRbnPJh/8TFY5/m7tedh8A2Ji3HwEpndA/470i66nFm5/zKxbp1uI73QZMzf7WqefeNhauxxOvghuDp13649Dlvzae0zwrW2MI2C8az2Cz8E9+zVQNRSVJ80h7xmr7wh8+ALiZZ7b+n8gVi/OecXLG3Z95R4TbvdXuq3cCuHoxnrgLf22L4AmQdutur27k1usgxFUUPJVgBbUpkHvMXqPM48ZLQqO6zuqmiM7/Y3vWeA06Ns/m81whHrp4SdNJCNYesvKZp7k5y7E2bw9ey/4E14zO1WA9tljXjhcqCqT4mXTieiceX/cknrHnSOYJML8ZZCNXqOERD5mIp0N7MVpowdU/8cN699gkYaZRdaXtxntd1c1QXcHVQ/1lOCmb8chldRif9QWykYst+kOIzdtmtY+7JJge4bPclcL973QbHSp25hV2ph1PPJ28sEXjm0Rr5NmqdwLsF43v1/8LQ37DSUeyTrwghT/Gej0PgFvWZHHub7L7iYe9LQuXo53o9cSyLLbqueBJBSW6qVs4fK2OEK+7KVZbZtmQD71H42VND3ztM9mqQJ+Q4kLBUwmW6GjmiXFsxt16UaH4C5oOQvFjHvTC8gzuIi6g7q5uIUyVFX9yFhMvaLo6b7dT50qx6PHEE2eP+De4SzJvKvywpTTz5FjwJB4q4ZuByvV4srzP1zz9J6l3khaLiykYhWRB2fk5v1jts0z3p6Sp6vTsH4tsvcMPs3+QTC9nweKNrE8drscSD9sVpubJ0Q8OBclj9diW3+8okPFDe5X9DIitovE9og8NXZ2c0v6W10vCh4zvdRtl65bEw96W08/FQ2IFLVx8yXgTN02JALiibXcuwssT/9/gWQ7bAdzQ3VK/D/B/Xv3cfg2EuIqCpxJM/Ela7lMcT9quQP4NhGVZofZABSWeUz8r6ZzszronI2sUMk8BjC9aqOoKxa2P2FTZ4aS7ojfT1bpdTnXTFmeMxMN2oaKFN/kAS/yJnM9SXTclINmUVuCiwHLqSNbx4oKnNAeCp7T8N8brdmbaWRql7SUUFm/Q78dFg3k4RM8aJJkggOtO/2mOdJs7nDJcxi952wFwP0P+Z/Cv8Sq+1W1w6Bh3CrmuHc9dw3YHDP8Kv7de6lZQF1D/01j0f4cvGv/PeFfo+aWCEu2cHIKqqCiLYZruALjX0E8yQ9jiDx+WjQ8jFOWE9cyOGi7aDWT5rBMA9HZjiwIxy+AplAlEGFMypvkTQsFTCebosJ0j69udMl4WhpU6qBojRBGApqJ1rNwZPJ0yXhFmUnVSNYGaUQkreptgsqrL0rMG3BcNR1wzxeOEEwu9SjJPooCpjDjzlJ9V4gMcBRR4SdNJePyEIU64ZnuLAluqLRM8pUrWtZMehz9uOpsFE2vCf6IeT9ULWC/Li9FiktcQ4f4nub8Kt1fl7RR+v42Vz0CV39Tvi9zVwlCtO7Asi8nZi4X7H3qPxvc+7wn3p2cvcSgDdFuyrp3rmSdxf6h7hWhXsEW0RqGtWXZijVTmzNMRwznMyP4R9dKGIT7/Q0ArVT1hTUNnvO09WLi9KHct8li95HHpsJ00eGIYBq3zu3ZnIQdnjf/ZPI8keHJzvRPPMniqpaxKs75IiUHBUwnGD8F4QSNZF82SI28glkN2AFBfWUMYJjjhxqJxcVdxvpZCXNxqOXSXaEoSeuvwVjuxEG6qRYNMXqh4cWBTOkysScg81VBUlPSYOWG8JARP9hYFthStrCLMuHMo85RfA8WCRTqbJekuXsNOzRNvrFdfYUhxTd4eXDfehZE14pMccyD1hc9EvJ5fYJ4DHaZnL3Hoe3HEZv1BHDKcAcAVTo/T9kdLdV28qu0LgGuK+L+shQUehw8yyjBBVouzOsMdmScTa8Lm/BYFWmjQTd28wOeIi8Z36I9ibu5y5IELdCopwvCVz9suXUstZVU8l7/0TwL7CKvzpP8PHsosCizWWrTkCb+QraVHpsfCQsS1FFXt1toVRrRF8CQ3ZEeIp6LgqYQyskZhFlaEopzdN/Nwyfp21p/6xUN2SiiFJSe0jAb18xe4vWK6gzRR1+vCENc78XUf4uLW+xZ9puQyI2vz9jhcPyNdFFhcMC5eHDgVt0z3hDqdWspqFsOWcQ4tCmzJi9EKhdxx+TPupEuzyA/bAVyGiu8uroTSocJpX8YbE7wGAuCyeAtyV2B93j6h0WlbVUO0VtfDB96jhYD717wdQjuGwtCxeXg3+xvh/ic+bwjDWx97/x/K8p3Q9fvxZ57tdgoG1iCsw1iYrBPALQXCf5+OLk9k6be8vUJbkM7qpvBjfAp8jrhonKeCElO8huFC4ErUVVkXRjtqstdQ4fbC3LWSx8SLAlvOtgMsgie9fPC0Xf+P8GHFnV3FLdW2CJ7kZtoR4qkoeCqhrprikY1cAEA90erYcsowgUIGSe7T91njNSHD0U7VEGVFgQy/dAQLFqfsrNXlqBRTujDrqrayKirn17OUF3VCt+z1JA6e+CzOQ/Yx9uilS7vYOydP0qqAkWaexMXitZRVEamoKGSq/jacF9odONKmQIx/U8iBDrdM9ywKxqUZQ3Hw9JjNEH4vVRTloGHUDp3vDe0LwvDfL7rtmJ7zo/DYVG+uk3CIIgDTvEYC4H63U7IXI8H0CIf0ZxCr+xNzcpZhr/6UU9/nt7kbhOttr2ok6UgdrAjAZz5vCff/L2u+ZNkZsQRTEoz5M9IKGzwBQHj+ayvRlORUrRzATel/LWu+cP9lbQ+Hn9tJtORFJ1UTnAn8FfN8xjkUfNnTSh2DpkpuSP288TpuGe8Jj/GZJzVUkg8KvLrKSGG48IjhnOzPY2sRtigQK6sIlgydU/BEShIKnkqoM6JMgeUnXEsKRiHURMkFT9Ihu/aSx9xZ96RnDRibNU9o4tdVNP1Z3A3ZcthOHDy9mD+kCMBqyMKWxzZqnsTDdklsqtX6WgzDCN+/OFvkaLE4z3LGnXjdOsvaKfH6dtdNd4UZVQXVO0mOofDD69oXAHC9vfjlXZooa6GzyvyG/rpXfyErtt9wGlVSn0eHjDcwJutjzMz5CT0y/id5ndmjZw1YkF9jxYDBpz7jrbKhQzRd0UnF9ey5xyaja8YExBsfWB1LWu/kerE4j59tmos8ye+xINlsLgZmfoDM/KB5iKYr+qvbO/z8yV5DsNhnErb4fYod/gslrQIKS1zEza+TB5j/74QxwbLZaCWjREtVDAAuw3tDNCwMcO0s+Oa1oUwgWrqxq7gcccBUR6ZNASGeioKnEuq0URQ8KaMK3J+v/UhiU616NolrHyz717greDKwBgzLnIlNeq52xAvmNdkAaXHrfdZ28PSKtq+QrdmUd9ChztUpkponc3DiAy945fdVSmHTEScKnviAp6nSeq2qUCeG7QDrGXeSmieFZfBkLiI+KZpOXr2AmXaWJni9JCx8zHvfe4TkDVXLaDDPZ5zNYxhhxEc5Sx063y79cTzKL7rvp26Hhirr1yTDMFjl95Hw87htuo9uGROsguV4cXfxQsy040nbFThe9zQ+63OhgWNtZVV86zvFqYJmH8YL47z6o6emldsLocULufIzV02sCQ/zfwdy9U48e3VPW/VHhGCxl7qVzaas7vKe93BUV1TEO17DSsyCuoQAFDyVWOKMgNwblaWKNno9sSyLs8Zrwj6W/aKiFBFCmt/VonEDa8CIrNlYr+fqqrTQYJP/AsmnTumwne3gqbqiopB9ykKOZBaULdImmebME8MwQt1TkilVMtOOb8YnDh55jvZ44lnOuBMHT5aZJ/EwnrgXjzOZJ4Drnv6aqC9OXWWk7BBMP3V7TPEahgbKmuilbo23tAPwpc9E4fWyRX9ItuGmJXEWcLi2u839QhWB2OG/UFij76opHt0zJkqGVt01044nKRp3YHFsAFim2yo0EfWFN9b6zS30cJs7NVFGC6+VvYaTMLJGJLNpwnCnvUDEXvC0UrdTuD1E282dlyyrq7o5rgStw8d2gnhCPBEFTyUQy7I4k595Ks+ESgIPWyramHV0x/RAeDOvp7KunVIwCjTJz74ksI+cbjRoZI0Yk/Ux1ubtAQBooMZ6v3mST86A/WG7+PzgiQGDcEUZDNZ0FR5b5cDQHf/GzICRZHYAc/F3EpuGuPwlKyIV4fBmtAAgKRoXnuNkzdMzysqSGXeONMkEpEt71HAy8wRww0bVFOHQQI1PfcbLLifCMAzm+YzDycDl2Oy/AF/4TsR4rwF413u4sM9HOT/bPU8GmyXMRgthAgrsRl1BUQa7/BcJgdF543U8m/4aeqT/Dy3SxuDL3NXCvm4ZthPPNnUg83TWcA3jsz4X7n/v+45sQ8fipGSUwhDoYzYDp4xX8MBkv1ic11RVS6iBFBeNJ5lSsT2/EWg4UwYdCmgESkhpRsFTCXTH9EDIphRU78SrYKNR5rn8rBNgu/C8iWih05NOZp8+yFmClXncp1k1VPjN72N011i/uQYx/tCAK4h+YDFsl5AfPJVnQqBmVHhWVV/oOr1Lf7zAxYT5mqcgxs9qGCIkP4ukh0EowBdnisIUwaiqqCB5jri5piO8GK3QHTzOeEtoecBdk7SoV5yJEmeoXJkuXk4RgrOBvyIhaItTS4EAwBhtH6Gh4lb9Ebu/9815h4RZigM0HR0qbK+sLI+d/ouEpp5XTHew23AcJ42Xhde2EkrJorCukjaJLTh4eif7a2F9w//T9sNgbdcCnlE8LIfu7PV4EvNhvNAovwP6ZdNtPMr///Nb3l5h6ZhB2i5FPmRHSElGwVMJdMbJeidAmnlKEA1diBvl1bcRPDUTDV05sz4ZAPys47ogq6DEWr856KVpJbsfwzCyS7TksXqhBqpSfsCkYBQYpOkCgKvLWVPAci18qwJxjyeeXNsByyyDZfbJ2dl2gDkgy0UezuX/zDVQCzVXPLl+XQwYl4MIH8YLwU4GewBXD/We18vC/dk5y2zuK87+ibOCBampjMAO/4WSLIkCCoQygYhWVMGnPm+6dO2WnOn1ZGSNOGq4CIAbxv5cNEPQ04iDpz36ExY9nuzXD7VWm4fu/snv6bRCt0PYxncyJ4TIo+CpBPrXyXonQNrrSfwGcs4gCp5sZLHEdT/OZJ4em9KFwKWNqr7dxVQB89BdEpsmLNFyz5Qs9JypJPoehmrM9RhzcpbZ7OFjYk1CJiNYJniSq1+ynDLNTwvnOdPniVdLdEx+Bl0Q42dVSGxZQA5wTRW98ocRn6RR2l5Chu9P/RHZmrcHphRhtlcVRXm0yp/J5ai6qkhcDVqHK4Fr8ShoO3KDD+BB8DZcCFqFt7xeKvw3AenSRQUFT9dNCUJLimaq2tAyGrv7F6dqynChduwfw3lJQ1V7w3YA9/+Rd9hwFv8Z7+KYkQsa6ylryA7hE0LMKHgqgcSZp4YOZp7CbXz65rMg3tAKf4gtVVSUFd6AThovw8SaHDrnTZO5/0w1BzIn4iVa+Jlb8ZIFYsOE23VVkRig6QiAyyyNzZov27Mmnc0Sgi+5vjcOBU8WmSdnWxUA8t2T5ZZ4sayBAiC0E3jSuOyT/dqndXl/Ca0nBmu6ytZVFcSX8UZ1ZSUEKwJcen5ByitCwYALUgsKns6JMrElIYDgh2MNMOK3vL+E7faG7QBIWhAc1p+VZA+HOJE9JKS0ouCpBDqTX6cUyPg5FJQA8qvLZ7BZQg+gusrqdmsc+OxTGpuJi6Ip/fbcyl+VHeA+JRdEPNTAD92JZ9pVsph59bXPZKFmZof+KH7UbbY6pq117XiWbQcYMHhGWVmyrZHqGaHgG3B+th0g3wBQPniyDvCcbVPgTqO0vYXC7u36f3Asf0iLJ52d5ZlvumpGJSw4W9D6dmcM5hrABkrH6gmLk7iWTfz/0nJRYEtlFcGIVnAzSk8br+AXHTezkAHjsTVehHgSCp5KmCRTqhBQ1FfWdLh/jB/jI7Qc4GccnTfcEB6vX8Cn7HbqhsLtX/O2O3TOG0Zz8ORIzU6YeH07Vi54KivZP1QRiB99pwr3J2cvliykC1gET3I1TxaBUKQiHD6MtD+SL+Mt1IP5w8fhRYHFnlFWhhLS4FQuy+QFjVA4z3O2TYE7aRg1pnqNEO6/lDFNCKCuGeNxMn+B5gbKmh7dIZr/8HDPlCwMCcuRZJ5KQPDUTtXQ6nUF2O/zxOPrngww4lZ+lriDqrGkwJ4QIo+CpxLmX0mxuHN/3PnePQmmR2BZ1uKNwn7wNETTVXhT/0W33arRppybTmaeyov+4DuSeQKAHpqWGJu/6Gw2cjEqa47kzdHWunY8y+JvW1PSP/UZj2dVDfCl70SXGh5qGY0w447HLwIsxjCMVVDlSpsCdxqh7YlaiqoAuMkGHdLfwI+5m6VZJ03R9wQqDL5o3AijMCQs52x+5imI8XdLj6miFqjwQ3NVHck2JZSyHxQsifs98YY9gd5OhDwNKHgqYcTDCo4Wi/P4T9850CGVzZC0KahfQCBWRhGEfpp2ALgu5XxfH3ucrnliCgqe5D8RL/B5U8hs/WM4j89yVwmPpZpE3cVlh+2k22xlT9qrG2FfwDcYqe1V0Ldhk+Wx5TJPctuLM/MEcNmn3QFfCUXGedBjXPYCLMhdAYAb6hmo7Vycl1ggWzV/YkmmVGEmaj1lDbd3BS8qlj3Twpggh2rHLIMnb2iF/+OEEPsoeCphXGlTwLN8AzlrEBfHVi/w+a9onxNu/6T7o8D9b+YP2/nCG2UdmN4vLnLlV4fngycFFFbdz3l+jA+W+X4o1CXNzPkJV413AEiXZpHLPFk2vCzKoSfLrJat4b8Ay+CpmDNPAFd0vdv/K7ylNc+Ay4MeALcIsKcP9diabSrmSNsOT9RFJe3h5ciQHQBEKioKNYMA0FfTFv4WTWQJIfIoeCph/s1fKkMLjdMLjYrfQOJND4V1uyIV4Q790WyvaiTMyNtrOGlVXyRmZM11FJHKcIc+xUuH7ZIBAHfzi9v5Bpm2tFbXwySvIQC4hpfvZ38HoOCaJ8vMU1F2krZc+NRW8CTOPJVnQj1mWRA1o8IXvhOwwncmvGFuneCpheJi0nYF8m0tHGnb4YmaqqIlr6WCZtrxGIaRZJ+GevjQKyGehIKnEiSTzca1/NlxMcpIu8GEHHF24JDhjNDPpqB6Jx7DMBgjyj79bCf7lGB6BD0MABwbsgOsh+3yWL1QOF5J1KbAlg+8R6JC/ifpTfqDOKQ/Y1HzZB08+cFHqOViwBRp8OTwsJ2oFirSA7JOlgZpu+BIwBJ0UjXBEE3XEvGm60ijzLMOdNv3RCpGJVlKRbzUUUEmew9FpCIcAzWd0dXJLvSElGYUPJUgZw3/CT2LGjhZ7wRIh+125K9hBQD1nPiUPULbU1gXK1b3J/JYvex+4nony+VNbAlk/IRA5iH7GImmJFGDzIKDJ1/GGzO9XxHuv5P9DVJM5qVQ5GqeGIYR+ijVVla1mmnnTlHKCMnMKEdqniyLzD1FPVUN7AxYhF/8Zji0HEtxk6xvZ6NdAT+BQgUlanvYWnYFEbcscDTzBHA9zK4G/YaVfrNoORZCnEDBUwlSmHonQDpsJ67vcGbWXpgiGH01bQFwAc4f+sOy+4ln2kU6MNMO4AIZfujuvilZ0iDTkeAJAEZqe6Fu/vDYCeMlbNYfEh6Tq3kCgG98p2Cophu+83nXoXO4SstoUFNU/B1o43rEQzCurGlHrImzrnKLW+exesQZbwEAopVViqWje2G8oOmA8kwoNFCjr7ptcV8OIU89Cp5KEEkDPxdqMmwV9To7RCEpHM/dIrvPDaN5qYhqTmRP+F5PSWwabpvuC9sdDZ6UjBLzvF8X7osX17UVPLVVN0Cs33S0Uju3tIgrxMOCtjJP4iL2msU80+5pUYYJhCo/63dPZtjukvGmMMxckobseGUUQfgv6DfcCdr0RF7HhJR2FDyVIHzmSQGFS3/gyzHBkk7ZAJflqKIo79RxOqoaC3VMuw3HhVl1YrdEw3aOZp4A80whFqxkDb8IB4MnAOiuboFOqiaSbT7w8ohsAr++XwDjixgbMxxf0nRCTUUEmivroLemzZO8vKeWgjHP1pSreTonmWlXcorFxbwYLcoogor7MggpFSh4KiHyWL0wO+4ZRWWXanNUjMqqmDRGWd3pfjYKRoEx2j7CfbnC8RuiYTtHa54A6Yy7U8bLwm25Bpm2MAyDT3zeENYzA+TrnYrDME13HA74ARcCVwod3y1VUZbHpcDVOBK4pEhrsEobfjLBIzbVqlZPnNUtCWvaEUKKFwVPJYR4WMHZ5philkN3rvazGantJRQ/yzXM5LNRFZhQeDuR8REv0SLOPNlqkGlLA1UUhmu6C/cd6bj8JDAMgxaqupL6M1v7EfcSv/bv5bfC4D0NmSdCyJNDwVMJccYoXrDU9eDJstGkq/1syitC0TD/OuJMt5AsmtWWxeYILQaqOTnVXrw4MN9KgWuQGWrrKTZ95DNW6EdU3UNnrZEnx1aXcZZlhTYFFZhQhCkcn+pPCCmdKHgqIc6IsjCutCngWWaeClMcK26w97fhvHD7llG8LIvjQ3YAF5RZqsCEQuVkTyuAKzL/0/8LvO01GJ/6jHf6+eTpUkHSrsA84+6u6SEe53eid6ZtByGk9HL+HYkUi/e9R6KrujnOGK+hkZsyTwoorLpeO6O1OgaLdGsBcMFTn/ziZnG9k6MNMnniYTueozPt5LRVN0BbdQOXn0+eHrYyT2ckazxSvRMhpGAUPJUQYYpg9NS0Qk+0KtRxxJmnZxSVnapHstRKlHk6Yjgn3HalxxOvnMyQSWGCJ0J44ZJeT+bgieqdCCHOomG7Ukb86buws4rKK0KFte5OGuKQy+oAALeMrmeexAuV8ih4Iu4g6TIuCp7OimbalaQ17QghxYeCp1KmubKOMPPsRU2HQh+Pr3vKgx4nDVxrAcmwnZOZpwDGF1poJNsoeCLuIF4c+LLxFtLZLADmzJM3tNSUlBDikBIRPGVmZmLixIkIDw+Hl5cXGjRogDVr1hT4vA0bNmDw4MGoUaMGvL29UbVqVQwdOhTXrl2z2rd9+/ZgGMbqq3v37jJHLrkCFX64HLgWFwJXoZ+mfaGP10pl7mbMF43zw3YaqCVvWI5gGMZq6C5CScETKbxAxg8+4PpmnTReRsXHfTAi8yP8l7/Ydh1lJK3vRghxSImoeerfvz9OnDiB+fPnIyoqCqtWrcLgwYNhMpkwZMgQm8/75JNPUL58eUybNg2RkZGIj4/Hxx//f3v3HhTVef4B/HuWhV3Y5SKo4AaEgBpAIWiSyq9JvIaLjkTAoZrGkeI1BKO2SSTRtqhArjXROk1+OopiEBONGqsBrVVaNT912qJGDdaqQBAmIqDscg/y/v5I2bgBlAPLsuD3M7Mzy3Pe9/CcJ5F9OOfwnrcwZswYnD59GiNHjjQZ7+vrix07dpjEXFxceuKQepWrwgmuMM+6R0/bPm58/1Xz1xBCoPC/f233qGIIFJL8/txdcsO3+PG5do9IbJ6o+yRJwnxVFP7YuBsAUI9G7Gg6bNzelUceEdHDyeqbp5ycHBw5csTYMAHAxIkTUVxcjNdffx0zZ86EjU37vy0eOHAAgwebfvBOmjQJPj4++PDDD7F582aTbfb29ggNDe2ZA+mnHlMMhZvkjEpRjf9r/hrfiUrUoQGA/Et2rdwVA4C7P37NM09kLmsdluKXqghkNuZgZ9MR3PnvEgUAMMbmsV7MjIj6Equ/bLdv3z5otVrExcWZxBMSElBWVoYzZ850OPenjRMA6HQ6eHp6oqSkxOy5PowkSTLe93RbGJD7/SnjNrk3i7e6d60nBRTwaGf5AqKukCQJTyoDsEHzKm647McOzWpE2T6DOLtJ+KUqvLfTI6I+wuqbp4sXLyIgIABKpelJsuDgYON2Oa5fv47i4uI2l+wA4Nq1a3B1dYVSqYSfnx9WrlyJ+vr6++6vsbERer3e5PWwufe+p08aDxnfd7V5GnzP8/d0ioFdWiCT6EHUkgozVc9hn+O72KlNhVZy6O2UiKiPsPpPpcrKSvj6tl3I0dXV1bi9s5qbmzFv3jxotVr8+te/Ntn2zDPPYObMmfD390d9fT1yc3Px3nvv4eTJk8jLy4NC0X6f+fbbb2P16tUyjqj/eVr5431PJ5rPGd/LXeOplfs9DwfmX9oREZG1sfrmCbj/Q1I7+wBVIQTmzZuHEydOYM+ePfDy8jLZnpaWZvL11KlT4ePjg9deew379+9HTExMu/t988038Zvf/Mb4tV6vb7Pv/m6McgTUsEMDmkzi5rhsx+aJiIisjdVftnNzc2v37FJV1Q8Pnm09A3U/QgjMnz8fWVlZ2LZtG6ZPn96p7z179mwAwOnTpzsco1Kp4OTkZPJ62KgkOzylDGwT7+oN494KD+N7PtCXiIisjdU3T0FBQSgoKEBzc7NJ/MKFH9YUGjVq1H3ntzZOW7duxebNm40NkRwdXbKjH9173xMAuEnOcJI0XdrXEzb+eEUVh3DbsUhUx5ojPSIiIrOx+q4gJiYGNTU12LNnj0k8MzMTOp0OY8eO7XCuEAILFizA1q1bsXHjRiQkJMj63pmZmQDA5Qs64el7nnMHdP2SHfDDpdgPNcuQ4/gBL9sREZHVsfp7nqZMmYKwsDAkJiZCr9dj2LBh2LlzJw4dOoSsrCzjGk/z5s1DZmYmrl27Bm9vbwDAkiVLsGXLFsydOxdBQUEml99UKhVGjx4NADhx4gTS09MRExMDX19fNDQ0IDc3F5s2bcKkSZMQFRVl+QPvY/5HOQoSJAgIAMCjNkN6OSMiIqKeYfXNE/DDY1ZWrlyJ3//+96iqqoK/vz927tyJWbNmGcfcvXsXd+/ehRDCGDtw4AAAICMjAxkZGSb79Pb2RlFREQBgyJAhsLGxQWpqKioqKiBJEoYPH441a9bg1Vdf5WW7ThigcMIoG19cuHsNQPfOPBEREVkzSdzbbVC36fV6ODs7o7q6+qG7eTyp9n1sbPwCAPC/DsmYr36+dxMiIiLqJDmf3zylQmYzyy4MAGALJSbZPtHL2RAREfWMPnHZjvqGZ21DcMk5G/ZQYaiNx4MnEBER9UFsnsisHrPx7u0UiIiIehQv2xERERHJwOaJiIiISAY2T0REREQysHkiIiIikoHNExEREZEMbJ6IiIiIZGDzRERERCQDmyciIiIiGdg8EREREcnA5omIiIhIBjZPRERERDKweSIiIiKSgc0TERERkQzK3k6gvxFCAAD0en0vZ0JERESd1fq53fo5fj9snszMYDAAALy8vHo5EyIiIpLLYDDA2dn5vmMk0ZkWizqtpaUFZWVlcHR0hCRJXd6PXq+Hl5cXSkpK4OTkZMYM6adYa8tivS2HtbYc1tpyeqrWQggYDAbodDooFPe/q4lnnsxMoVDA09PTbPtzcnLiP0QLYa0ti/W2HNbaclhry+mJWj/ojFMr3jBOREREJAObJyIiIiIZ2DxZKZVKhZSUFKhUqt5Opd9jrS2L9bYc1tpyWGvLsYZa84ZxIiIiIhl45omIiIhIBjZPRERERDKweSIiIiKSgc2TlampqcGyZcug0+mgVqsREhKCTz/9tLfT6tOOHTuGuXPnwt/fHxqNBo888gimT5+Of/3rX23G5ufn47nnnoNWq4WLiwtiY2Nx/fr1Xsi6/9i8eTMkSYJWq22zjfXuvpMnT2Lq1KkYMGAA7O3tMXz4cKSmppqMYZ277+zZs4iOjoZOp4ODgwP8/f2xZs0a1NXVmYxjreUxGAxYvnw5wsPDMWjQIEiShFWrVrU7Vk5tN2zYAH9/f6hUKjz66KNYvXo1vv/+e7PlzebJysTGxiIzMxMpKSnIzc3FU089hRdeeAHZ2dm9nVqf9fHHH6OoqAhLly5FTk4O1q9fj/LycoSGhuLYsWPGcZcvX8aECRPQ1NSEXbt2ISMjA1euXMGzzz6LW7du9eIR9F2lpaV47bXXoNPp2mxjvbsvOzsb48ePh7OzM7Zv346cnBwkJyebPJuLde6+b775Bj//+c9RVFSEdevW4eDBg5g1axbWrFmDF154wTiOtZavsrISmzZtQmNjI6KjozscJ6e26enpWLp0KWJjY3H48GG8/PLLeOutt5CUlGS+xAVZjS+//FIAENnZ2SbxsLAwodPpRHNzcy9l1rfdvHmzTcxgMAh3d3cxefJkYywuLk4MHDhQVFdXG2NFRUXC1tZWLF++3CK59jfTpk0TUVFRIj4+Xmg0GpNtrHf33LhxQ2g0GpGYmHjfcaxz961cuVIAEFevXjWJL1y4UAAQVVVVQgjWuitaWlpES0uLEEKIW7duCQAiJSWlzbjO1raiokKo1WqxcOFCk/np6elCkiRx6dIls+TNM09WZN++fdBqtYiLizOJJyQkoKysDGfOnOmlzPq2wYMHt4lptVoEBgaipKQEANDc3IyDBw9ixowZJsv9e3t7Y+LEidi3b5/F8u0vsrKy8Pe//x0fffRRm22sd/dt3rwZtbW1SE5O7nAM62wetra2ANo+usPFxQUKhQJ2dnasdRdJkvTA58DKqe2hQ4fQ0NCAhIQEk30kJCRACIEvvvjCLHmzebIiFy9eREBAAJRK00cOBgcHG7eTeVRXVyM/Px8jR44EAFy7dg319fXGWt8rODgYV69eRUNDg6XT7LPKy8uxbNkyvPPOO+0+65H17r7jx4/D1dUVly9fRkhICJRKJQYPHoyXXnoJer0eAOtsLvHx8XBxcUFiYiKuX78Og8GAgwcPYuPGjUhKSoJGo2Gte5Cc2rZ+TgYFBZmMGzJkCAYOHGi2z1E2T1aksrISrq6ubeKtscrKSkun1G8lJSWhtrYWK1euBPBjbTuqvxACt2/ftmiOfdnLL7+Mxx57DImJie1uZ727r7S0FHV1dYiLi8PMmTPx17/+Fa+//jq2b9+OqVOnQgjBOpuJj48PTp06hYsXL8LPzw9OTk6IiopCfHw81q9fD4D/T/ckObWtrKyESqWCRqNpd6y5PkeVDx5ClnS/05cPOrVJnfO73/0OO3bswIYNG/DEE0+YbGP9u2/Pnj04cOAAzp49+8Casd5d19LSgoaGBqSkpOCNN94AAEyYMAF2dnZYtmwZjh49CgcHBwCsc3cVFRUhKioK7u7u+PzzzzFo0CCcOXMGaWlpqKmpwZYtW4xjWeue09naWuK/AZsnK+Lm5tZuV1xVVQWg/a6b5Fm9ejXS0tKQnp6OxYsXG+Nubm4A2j+7V1VVBUmS4OLiYqk0+6yamhokJSXhlVdegU6nw507dwAATU1NAIA7d+7A1taW9TYDNzc3/Oc//0FERIRJfMqUKVi2bBny8/Mxffp0AKxzd73xxhvQ6/U4d+6c8YzGuHHjMHDgQMydOxdz5syBh4cHANa6J8j5eeHm5oaGhgbU1dUZf3m4d+xPf2HuKl62syJBQUEoKChAc3OzSfzChQsAgFGjRvVGWv3G6tWrsWrVKqxatQorVqww2ebn5wd7e3tjre914cIFDBs2DGq12lKp9lkVFRW4efMm1q5diwEDBhhfO3fuRG1tLQYMGIAXX3yR9TaD9u7/AGBcpkChULDOZnLu3DkEBga2uRT01FNPAYDxch5r3TPk1Lb1Xqefjv3uu+9QUVFhts9RNk9WJCYmBjU1NdizZ49JPDMzEzqdDmPHju2lzPq+1NRUrFq1Cr/97W+RkpLSZrtSqURUVBT27t0Lg8FgjH/77bfIy8tDbGysJdPtszw8PJCXl9fmFRERAbVajby8PKSlpbHeZjBjxgwAQG5urkk8JycHABAaGso6m4lOp8OlS5dQU1NjEj916hQAwNPTk7XuQXJqGxkZCbVajW3btpnsY9u2bZAk6b5rSclilgUPyGzCwsLEgAEDxKZNm8SxY8fEggULBACRlZXV26n1WX/4wx8EABEZGSlOnTrV5tWqoKBAaLVaMW7cOJGTkyP27t0rRo0aJXQ6nSgvL+/FI+j72lvnifXuvqioKKFSqURqaqo4cuSIePvtt4VarRbTpk0zjmGdu2///v1CkiQRGhoqPvvsM3H06FGRnp4utFqtCAwMFI2NjUII1rqrcnJyxO7du0VGRoYAIOLi4sTu3bvF7t27RW1trRBCXm3T0tKEJElixYoV4m9/+5t4//33hUqlEgsWLDBbzmyerIzBYBBLliwRHh4ews7OTgQHB4udO3f2dlp92vjx4wWADl/3+uc//ykmT54sHBwchJOTk4iOjm6zMB7J117zJATr3V11dXUiOTlZeHl5CaVSKYYOHSrefPNN0dDQYDKOde6+Y8eOifDwcOHh4SHs7e3FiBEjxKuvvioqKipMxrHW8nl7e3f487mwsNA4Tk5t169fL0aMGCHs7OzE0KFDRUpKimhqajJbzpIQ96zjT0RERET3xXueiIiIiGRg80REREQkA5snIiIiIhnYPBERERHJwOaJiIiISAY2T0REREQysHkiIiIikoHNExGRBUmSZLYnuxNR72DzRERWy8fHx9hs3O/10+dYERH1JGVvJ0BE9CDDhw/H4MGDO9zu7u5uwWyI6GHH5omIrN6KFSvwq1/9qrfTICICwMt2RERERLKweSKifuXeG7Kzs7Pxs5/9DFqtFq6uroiOjsbFixc7nFtbW4u0tDQEBwdDo9HAyckJY8eOxZ/+9Cc0Nzd3OK+qqgopKSkYPXo0nJycoNVqERAQgJdeeglnz57tcF5ubi7GjRsHR0dHODs7Y8qUKR2OLy4uxqJFi+Dr6wuVSgVHR0f4+voiJiYGn376aSerQ0RmIYiIrJS3t7cAILZu3drpOQAEAPHuu+8KAMLDw0M8+eSTwtHRUQAQ9vb24sSJE23mlZeXi6CgIAFAKBQKERwcLAICAoz7CwsLE/X19W3mnTt3Tuh0OuO8wMBAERISIpycnAQAER8f325+H3/8sZAkSQwZMkSMGTNGaDQaAUBotVpRUFBgMqewsFAMHDhQABAODg4iKChIhISECFdXVwFAPP74452uDxF1H5snIrJa3WmebG1txdq1a8Xdu3eFEELU1taKF198UQAQ3t7eoq6uzmTejBkzBAAxcuRIcfXqVWP8H//4h3B3dxcAxPLly03mVFdXi6FDhwoAIjIyUpSUlJhsP378uMjKymo3PwcHB5Pj0uv1YvLkyQKAmDlzpsmcxYsXGxsxg8Fgsq2goEBs3Lix0/Uhou5j80REVqu1eXrQ6/bt28Y5rbHnn3++zf4aGxuFh4eHACAyMjKM8StXrghJkgQAkZ+f32berl27BACh0WiEXq83xt977z0BQAQEBIiGhoZOHVNrfq+88kqbbV9//bUAIJydnU3iERERAoA4f/58p74HEfUs/rUdEVm9By1VoFS2/VGWlJTUJmZnZ4f58+cjLS0Nhw8fRkJCAgDgyJEjEELgmWeewejRo9vMmzFjBjw9PXHjxg189dVXiIyMBADs378fALB06VKoVCpZxzR//vw2saCgIKjValRXV6OyshJubm4AAC8vLwDA559/jqCgIC6ySdTL2DwRkdXrylIFAQEB941fuXLFGGt9HxgY2O4chUIBf39/3LhxA1euXDE2TwUFBQCA0NBQWbkBgJ+fX7vxQYMGoaSkBDU1NcbmKSkpCZmZmUhNTcX27dsRGRmJZ599FhMnToROp5P9vYmoe/jXdkTUL3V0pqp1QU2DwWCM1dTU3HdOR/P0ej0AwMXFRXZ+Go2m3bhC8cOPZSGEMRYSEoLjx48jPDwcpaWl2LhxI2bPng1PT09EREQYmzgisgw2T0TUL926davdeHl5OQDA0dHRGNNqtSbb2nPz5s0281rf37lzp1u5dkZoaCgOHz6M27dv49ChQ0hOToanpyf+8pe/ICwszCI5ENEP2DwRUb/U0dmY1viIESOMsdb333zzTbtzWlpacPny5TbzRo4cCQA4ffp09xPuJK1Wi4iICLzzzju4fPky/Pz8UFpaitzcXIvlQPSwY/NERP3SRx991CbW1NSELVu2AADCw8ON8fDwcEiShJMnT7a7SOXevXtx48YNaDQaPP3008Z4dHQ0AGDDhg1oamoy8xE8mIODA4KCggAAZWVlFv/+RA8rNk9E1C99+eWXWL9+vfHeofr6eixYsABlZWXw8vLCrFmzjGOHDRuG2NhYAMCcOXNw/fp147b8/HwsWbIEALB48WKTy3YLFy6Et7c3Ll26hNjYWJSWlprkcPLkSezYsaPbx5KYmIjPPvsMdXV1JvHjx4/j6NGjAIAxY8Z0+/sQUedI4t67EomIrIiPjw+Ki4sfuFTBL37xC2OD0/pn/O+++y6Sk5Ph4eEBLy8v/Pvf/4Zer4darcbhw4cxbtw4k33cunULkydPxoULF2BjY4NRo0bh+++/N17Ke+6553DgwAGo1WqTeefPn0dkZCS+++47KBQKBAQEwNbWFoWFhaiurkZ8fDy2bdtmHN+aX0c/eluPubCwED4+PgB+uGH8/PnzUCqVGD58OBwdHXHz5k0UFxcDAGbPno1PPvmkk1Ulou5i80REVqu1kXiQpUuXYt26dQBMm5Ps7GysW7cOly5dgq2tLcaPH4/U1FQEBwe3u5/a2lp88MEH2LVrF65duwaFQoHAwEDMmTMHixYtgq2tbbvzKisrsXbtWvz5z39GYWEhbGxs4OnpiQkTJmDRokV4/PHHjWO70jzl5eVh//79OHHiBEpKSlBdXY0hQ4bA398fSUlJmDZtGtd+IrIgNk9E1K88qDkhIuou3vNEREREJAObJyIiIiIZ2DwRERERycDmiYiIiEgGPhiYiPoV3ihORD2NZ56IiIiIZGDzRERERCQDmyciIiIiGdg8EREREcnA5omIiIhIBjZPRERERDKweSIiIiKSgc0TERERkQxsnoiIiIhk+H/+Arh02uNaDQAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "train_classifier=True\n", - "\n", - "n_epochs = 100\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", - "\n", - "classifier.to(device)\n", - "weight=torch.tensor((3,1)).float().to(device) #account for the class imbalance in the dataset\n", - "\n", - "\n", - "if train_classifier==False:\n", - " classifier.load_state_dict(torch.load(\"./classifier_small.pt\", map_location={'cuda:0': 'cpu'}))\n", - "else:\n", - "\n", - " scaler = GradScaler()\n", - " total_start = time.time()\n", - " for epoch in range(n_epochs):\n", - " classifier.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes)/batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - "\n", - " for step, (a,b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " \n", - " optimizer_cls.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - "\n", - " with autocast(enabled=False):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noisy_img=scheduler.add_noise(images,noise, timesteps ) #add t steps of noise to the input image\n", - " pred=classifier(noisy_img, timesteps)\n", - " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", - "\n", - " loss.backward()\n", - " optimizer_cls.step()\n", - "\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix(\n", - " {\n", - " \"loss\": epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - " print('final step train', step)\n", - "\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " classifier.eval()\n", - " val_epoch_loss = 0\n", - " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", - " for step, (a,b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " timesteps = torch.randint(0, 1, (len(images),)).to(device) #check validation accuracy on the original images, i.e., do not add noise\n", - "\n", - " with torch.no_grad():\n", - " with autocast(enabled=False):\n", - " noise = torch.randn_like(images).to(device)\n", - " pred = classifier(images, timesteps)\n", - " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " _, predicted = torch.max(pred, 1);\n", - " progress_bar_val.set_postfix(\n", - " {\n", - " \"val_loss\": val_epoch_loss / (step + 1),\n", - " }\n", - " )\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - "\n", - " total_time = time.time() - total_start\n", - " print(f\"train completed, total time: {total_time}.\")\n", - " torch.save(classifier.state_dict(), \"./classifier_100.pt\")\n", - " \n", - " ## Learning curves for the Classifier\n", - " \n", - " plt.style.use(\"seaborn-bright\")\n", - " plt.title(\"Learning Curves\", fontsize=20)\n", - " print('epl', len(epoch_loss_list))\n", - " plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - " plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - " plt.yticks(fontsize=12)\n", - " plt.xticks(fontsize=12)\n", - " plt.xlabel(\"Epochs\", fontsize=16)\n", - " plt.ylabel(\"Loss\", fontsize=16)\n", - " plt.legend(prop={\"size\": 14})\n", - " plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "a676b3fe", - "metadata": {}, - "source": [ - "# Image-to-Image Translation to a Healthy Subject\n", - "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." - ] - }, - { - "cell_type": "code", - "execution_count": 124, - "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "DiffusionModelEncoder(\n", - " (conv_in): Convolution(\n", - " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_embed): Sequential(\n", - " (0): Linear(in_features=32, out_features=128, bias=True)\n", - " (1): SiLU()\n", - " (2): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " (down_blocks): ModuleList(\n", - " (0): DownBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", - " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (1): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=64, out_features=64, bias=True)\n", - " (key): Linear(in_features=64, out_features=64, bias=True)\n", - " (value): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (2): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (query): Linear(in_features=64, out_features=64, bias=True)\n", - " (key): Linear(in_features=64, out_features=64, bias=True)\n", - " (value): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " )\n", - " (out): Sequential(\n", - " (0): Linear(in_features=4096, out_features=512, bias=True)\n", - " (1): ReLU()\n", - " (2): Dropout(p=0.1, inplace=False)\n", - " (3): Linear(in_features=512, out_features=2, bias=True)\n", - " )\n", - ")" - ] - }, - "execution_count": 124, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "\n", - "inputimg = total_val_slices[150][0,...] # Pick an input slice of the validation set to be transformed \n", - "inputlabel= total_val_labels[150] # Check whether it is healthy or diseased\n", - "\n", - "plt.figure(\"input\"+str(inputlabel))\n", - "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.axis(\"off\")\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "model.eval()\n", - "classifier.eval()" - ] - }, - { - "cell_type": "markdown", - "id": "0cd48c2d", - "metadata": {}, - "source": [ - "### Encoding the input image in noise with the reversed DDIM sampling scheme\n", - "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\\\n", - "We define the number of steps in the noising and denoising process by L.\\\n", - "The encoding process is presented in Equation of the paper \"Diffusion Models for Medical Anomaly Detection\" (https://arxiv.org/pdf/2203.04306.pdf).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 125, - "id": "f71e4924", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 44.00it/s]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "L=200\n", - "current_img = inputimg[None,None,...].to(device)\n", - "scheduler.set_timesteps(num_inference_steps=1000)\n", - "\n", - "\n", - "progress_bar = tqdm(range(L)) #go back and forth L timesteps\n", - "for t in progress_bar: #go through the noising process\n", - "\n", - " with autocast(enabled=False):\n", - " with torch.no_grad():\n", - " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device))\n", - " current_img, _ = scheduler.reversed_step(model_output, t, current_img)\n", - "\n", - "plt.style.use(\"default\")\n", - "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "a7c8346a-6296-4800-b978-c10fcdf09779", - "metadata": {}, - "source": [ - "### Denoising Process using Gradient Guidance\n", - "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\n", - "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \\\n", - "The scale s is used to amplify the gradient." - ] - }, - { - "cell_type": "code", - "execution_count": 126, - "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.41it/s]\n" - ] - }, - { - "data": { - "image/png": "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U1Onji0iYsaMGXKtubm5GHdbD9TP2r9/v8w5ceKEXHviiSeKcfe2i6FDh8q1Xr16FePr1q2TOWo7ituK8sEHH8g1dV/Onz9f5hw4cECu/f73v5dryrRp04rxzZs3d/qzkBtvPQAAICh2AIAqQLEDAKRHsQMApEexAwCkRzfmh2jp0qXFuBvc7Abh9ujRoxjv1k3P71bDniP0AF3VRerW3DBe11nZ3t5ejA8YMEDmvPXWW3LtscceK8bdMGp1zrt37y5z3DlqaWkpxseNGydz1JDvCN1JOnjwYJnzgx/8oNM/Z+/evXLNdfUCtxq6MQEACIodAKAKUOwAAOlR7AAA6VHsAADpUewAAOmx9eAmW7FihVxTQ5PdkONdu3bJtffff78Yf/TRR2VOJW3658+fl2s1NTXFuNueobYrROjv9KlPfUrmtLW1FeO9e/fudE6EHnzt2v7dMO9+/foV4xcvXpQ577zzTjH+wgsvyJwOPvrALY+tBwAABMUOAFAFKHYAgPQodgCA9Ch2AID06MasIuoaLlu2TOaozs8rV67IHLemui5dp6EaiB2huyQ3btwocxYtWlSMHzlyROaojssIPcR69erVMscNWlYDml0XLlDN6MYEACAodgCAKkCxAwCkR7EDAKRHsQMApEexAwCkx9YDWN/4xjeK8dGjR8scN+RYDZZubW2VOYcPH5Zra9asKcbdYOkdO3YU408++aTMeeWVV+QagJuLrQcAAATFDgBQBSh2AID0KHYAgPQodgCA9Ch2AID02HoAAPhYY+sBAABBsQMAVAGKHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AID2KHQAgPYodACA9ih0AIL1uHf2H169fv5HHAQDADcNfdgCA9Ch2AID0KHYAgPQodgCA9Ch2AID0KHYAgPQodgCA9Ch2AID0KHYAgPT+HzbxLsbNP3t+AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "y=torch.tensor(0) #define the desired class label\n", - "scale=5 #define the desired gradient scale s\n", - "progress_bar = tqdm(range(L)) #go back and forth L timesteps\n", - "\n", - "for i in progress_bar: #go through the denoising process\n", - "\n", - " t=L-i\n", - " with autocast(enabled=True):\n", - " with torch.no_grad():\n", - " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)).detach() # this is supposed to be epsilon\n", - "\n", - " with torch.enable_grad():\n", - " x_in = current_img.detach().requires_grad_(True)\n", - " logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device))\n", - " log_probs = F.log_softmax(logits, dim=-1)\n", - " selected = log_probs[range(len(logits)), y.view(-1)]\n", - " a = torch.autograd.grad(selected.sum(), x_in)[0]\n", - " alpha_prod_t = scheduler.alphas_cumprod[t]\n", - " updated_noise = model_output- (1 - alpha_prod_t).sqrt() * scale*a #update the predicted noise epsilon with the gradient of the classifier\n", - "\n", - " current_img, _ = scheduler.step(updated_noise, t, current_img)\n", - " torch.cuda.empty_cache()\n", - "\n", - "plt.style.use(\"default\")\n", - "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", - "metadata": {}, - "source": [ - "# Anomaly Detection\n", - "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction." - ] - }, - { - "cell_type": "code", - "execution_count": 127, - "id": "ecffaaf3-a7df-453e-81a9-757113d85084", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def visualize(img):\n", - " _min = img.min()\n", - " _max = img.max()\n", - " normalized_img = (img - _min)/ (_max - _min)\n", - " return normalized_img\n", - "\n", - "diff=abs(inputimg.cpu()-current_img[0, 0].cpu()).detach().numpy()\n", - "plt.style.use(\"default\")\n", - "plt.imshow(diff, cmap=\"jet\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a0aff7cd-91a5-406d-81d6-69921f9dc141", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfec3184-a975-4f23-a054-3a327789b435", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "jupytext": { - "formats": "py:percent,ipynb" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py b/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py deleted file mode 100644 index f2433f86..00000000 --- a/tutorials/generative/classifier_guidance_anomalydetection/2d_classifier_guidance_anomalydetection_tutorial.py +++ /dev/null @@ -1,589 +0,0 @@ -# --- -# jupyter: -# jupytext: -# formats: py:percent,ipynb -# text_representation: -# extension: .py -# format_name: percent -# format_version: '1.3' -# jupytext_version: 1.14.4 -# kernelspec: -# display_name: Python 3 (ipykernel) -# language: python -# name: python3 -# --- - -# %% [markdown] -# # Weakly Supervised Anomaly Detection with Classifier Guidance -# -# This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. -# -# -# [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 -# -# -# TODO: Add Open in Colab -# -# ## Setup environment - -# %% -# !python /home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/setup.py install -# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" -# !python -c "import matplotlib" || pip install -q matplotlib -# !python -c "import seaborn" || pip install -q seaborn - -# %% [markdown] -# ## Setup imports - -# %% jupyter={"outputs_hidden": false} -# Copyright 2020 MONAI Consortium -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import sys -import time -from typing import Dict - -import matplotlib.pyplot as plt -import numpy as np -import torch -import torch.nn.functional as F -from monai import transforms -from monai.apps import DecathlonDataset -from monai.config import print_config -from monai.data import DataLoader -from monai.utils import first, set_determinism -from torch.cuda.amp import GradScaler, autocast -from tqdm import tqdm - -from generative.inferers import DiffusionInferer -from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet -from generative.networks.schedulers.ddim import DDIMScheduler - -torch.multiprocessing.set_sharing_strategy("file_system") - - -sys.path.append("/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/") -print("path", sys.path) - - -print_config() - - -# %% [markdown] -# ## Setup data directory - -# %% jupyter={"outputs_hidden": false} -directory = os.environ.get("MONAI_DATA_DIRECTORY") -# root_dir = tempfile.mkdtemp() if directory is None else directory -root_dir = "/home/juliawolleb/PycharmProjects/MONAI/brats" # path to where the data is stored - -# %% [markdown] -# ## Set deterministic training for reproducibility - -# %% jupyter={"outputs_hidden": false} -set_determinism(42) - -# %% [markdown] tags=[] -# ## Setup BRATS Dataset in 2D slices for training -# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150. -# If we set `preprocessing_train=True`, we stack all slices into a tensor and save it as _total_train_slices.pt_. -# If we set `preprocessing_train=False`, we load the saved tensor. -# The corresponding labels are saved as _total_train_labels.pt._ - -# %% [markdown] -# Here we use transforms to augment the training dataset, as usual: -# -# 1. `LoadImaged` loads the hands images from files. -# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. -# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. -# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. -# -# - -# %% -channel = 0 # 0 = Flair -assert channel in [0, 1, 2, 3], "Choose a valid channel" - -train_transforms = transforms.Compose( - [ - transforms.LoadImaged(keys=["image", "label"]), - transforms.EnsureChannelFirstd(keys=["image", "label"]), - transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), - transforms.AddChanneld(keys=["image"]), - transforms.EnsureTyped(keys=["image", "label"]), - transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), - transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), - transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), - transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), - transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), - transforms.Lambdad( - keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() - ), - ] -) - -# %% jupyter={"outputs_hidden": false} - -train_ds = DecathlonDataset( - root_dir=root_dir, - task="Task01_BrainTumour", - section="training", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise - num_workers=4, - download=False, # Set download to True if the dataset hasnt been downloaded yet - seed=0, - transform=train_transforms, -) -print("len train data", len(train_ds)) - - -def get_batched_2d_axial_slices(data: Dict): - images_3D = data["image"] - batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( - -1 - ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. - slice_label = data["slice_label"] - slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() - return batched_2d_slices, slice_label - - -preprocessing_train = False - -if preprocessing_train is True: - train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) - print(f'Image shape {train_ds[0]["image"].shape}') - - data_2d_slices = [] - data_slice_label = [] - check_data = first(train_loader_3D) - for i, data in enumerate(train_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices.append(b2d) - data_slice_label.append(slice_label2d) - total_train_slices = torch.cat(data_2d_slices, 0) - total_train_labels = torch.cat(data_slice_label, 0) - - torch.save(total_train_slices, "total_train_slices.pt") - torch.save(total_train_labels, "total_train_labels.pt") - -else: - total_train_slices = torch.load("total_train_slices.pt") - total_train_labels = torch.load("total_train_labels.pt") - print("total slices", total_train_slices.shape) - print("total lbaels", total_train_labels.shape) - - -# %% [markdown] tags=[] -# ## Setup BRATS Dataset in 2D slices for validation -# As baseline, we use the load_2d_brats.ipynb written by Pedro in issue 150. -# If we set `preprocessing_val=True`, we stack all slices into a tensor and save it as _total_val_slices.pt_. -# If we set `preprocessing_val=False`, we load the saved tensor. -# The corresponding labels are saved as _total_val_labels.pt_. - -# %% -val_ds = DecathlonDataset( - root_dir=root_dir, - task="Task01_BrainTumour", - section="validation", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise - num_workers=4, - download=False, # Set download to True if the dataset hasnt been downloaded yet - seed=0, - transform=train_transforms, -) - - -preprocessing_val = False -if preprocessing_val is True: - val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) - print(f'Image shape {val_ds[0]["image"].shape}') - print("len val data", len(val_ds)) - data_2d_slices_val = [] - data_slice_label_val = [] - for i, data in enumerate(val_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices_val.append(b2d) - data_slice_label_val.append(slice_label2d) - total_val_slices = torch.cat(data_2d_slices_val, 0) - total_val_labels = torch.cat(data_slice_label_val, 0) - torch.save(total_val_slices, "total_val_slices.pt") - torch.save(total_val_labels, "total_val_labels.pt") - -else: - total_val_slices = torch.load("total_val_slices.pt") - total_val_labels = torch.load("total_val_labels.pt") - print("total slices", total_val_slices.shape) - print("total lbaels", total_val_labels.shape) - - -# %% [markdown] -# ### Define network, scheduler, optimizer, and inferer -# At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using -# the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms -# in the 3rd level (`num_head_channels=64`). -# - -# %% jupyter={"outputs_hidden": false} -device = torch.device("cuda") - -model = DiffusionModelUNet( - spatial_dims=2, - in_channels=1, - out_channels=1, - num_channels=(64, 64, 64), - attention_levels=(False, False, True), - num_res_blocks=1, - num_head_channels=64, - with_conditioning=False, - # cross_attention_dim=1, -) -model.to(device) - -scheduler = DDIMScheduler(num_train_timesteps=1000) - -optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) - -inferer = DiffusionInferer(scheduler) - - -# %% [markdown] tags=[] -# ### Model training of the Diffusion Model -# If we set `train_diffusionmodel=True`, we are training our diffusion model for 100 epochs, and save the model as _diffusion_model.pt_. -# If we set `train_diffusionmodel=False`, we load a pretrained model. - -# %% jupyter={"outputs_hidden": false} -n_epochs = 100 -batch_size = 32 -val_interval = 1 -epoch_loss_list = [] -val_epoch_loss_list = [] - -train_diffusionmodel = False - -if train_diffusionmodel is False: - model.load_state_dict(torch.load("diffusion_model.pt", map_location={"cuda:0": "cpu"})) -else: - scaler = GradScaler() - total_start = time.time() - for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - - subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # - - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) - optimizer.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noise_pred = inferer( - inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps - ) # remove the class conditioning - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() - progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - - total_time = time.time() - total_start - torch.save(model.state_dict(), "./diffusion_model.pt") # save the trained model - - print(f"train diffusion completed, total time: {total_time}.") - - plt.style.use("seaborn-bright") - plt.title("Learning Curves Diffusion Model", fontsize=20) - plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") - plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", - ) - plt.yticks(fontsize=12) - plt.xticks(fontsize=12) - plt.xlabel("Epochs", fontsize=16) - plt.ylabel("Loss", fontsize=16) - plt.legend(prop={"size": 14}) - plt.show() - - -# %% [markdown] -# ## Define the Classification Model -# First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. -# - -# %% -classifier = DiffusionModelEncoder( - spatial_dims=2, - in_channels=1, - out_channels=2, - num_channels=(32, 64, 64), - attention_levels=(False, True, True), - num_res_blocks=1, - num_head_channels=64, - with_conditioning=False, -) -classifier.to(device) -batch_size = 32 - - -# %% [markdown] -# ## Model training of the Classification Model -# If we set `train_classifier=True`, we are training our diffusion model for 100 epochs, and save the model as _classifier.pt_. -# If we set `train_classifier=False`, we load a pretrained model. - -# %% -train_classifier = True - -n_epochs = 100 -val_interval = 1 -epoch_loss_list = [] -val_epoch_loss_list = [] -optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) - -classifier.to(device) -weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset - - -if train_classifier is False: - classifier.load_state_dict(torch.load("./classifier.pt", map_location={"cuda:0": "cpu"})) -else: - scaler = GradScaler() - total_start = time.time() - for epoch in range(n_epochs): - classifier.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) - - optimizer_cls.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - - with autocast(enabled=False): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image - pred = classifier(noisy_img, timesteps) - loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") - - loss.backward() - optimizer_cls.step() - - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - print("final step train", step) - - if (epoch + 1) % val_interval == 0: - classifier.eval() - val_epoch_loss = 0 - subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar_val.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - timesteps = torch.randint(0, 1, (len(images),)).to( - device - ) # check validation accuracy on the original images, i.e., do not add noise - - with torch.no_grad(): - with autocast(enabled=False): - noise = torch.randn_like(images).to(device) - pred = classifier(images, timesteps) - val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") - - val_epoch_loss += val_loss.item() - _, predicted = torch.max(pred, 1) - progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - - total_time = time.time() - total_start - print(f"train completed, total time: {total_time}.") - torch.save(classifier.state_dict(), "./classifier.pt") - - ## Learning curves for the Classifier - - plt.style.use("seaborn-bright") - plt.title("Learning Curves", fontsize=20) - print("epl", len(epoch_loss_list)) - plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") - plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", - ) - plt.yticks(fontsize=12) - plt.xticks(fontsize=12) - plt.xlabel("Epochs", fontsize=16) - plt.ylabel("Loss", fontsize=16) - plt.legend(prop={"size": 14}) - plt.show() -# %% [markdown] -# # Image-to-Image Translation to a Healthy Subject -# We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. - -# %% - - -inputimg = total_val_slices[150][0, ...] # Pick an input slice of the validation set to be transformed -inputlabel = total_val_labels[150] # Check whether it is healthy or diseased - -plt.figure("input" + str(inputlabel)) -plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") -plt.axis("off") -plt.tight_layout() -plt.show() - -model.eval() -classifier.eval() - -# %% [markdown] -# ### Encoding the input image in noise with the reversed DDIM sampling scheme -# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\ -# We define the number of steps in the noising and denoising process by L.\ -# The encoding process is presented in Equation of the paper "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/pdf/2203.04306.pdf). -# - -# %% jupyter={"outputs_hidden": false} -L = 200 -current_img = inputimg[None, None, ...].to(device) -scheduler.set_timesteps(num_inference_steps=1000) - - -progress_bar = tqdm(range(L)) # go back and forth L timesteps -for t in progress_bar: # go through the noising process - with autocast(enabled=False): - with torch.no_grad(): - model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) - current_img, _ = scheduler.reversed_step(model_output, t, current_img) - -plt.style.use("default") -plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") -plt.tight_layout() -plt.axis("off") -plt.show() - - -# %% [markdown] -# ### Denoising Process using Gradient Guidance -# From the noisy image, we apply DDIM sampling scheme for denoising for L steps. -# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \ -# The scale s is used to amplify the gradient. - -# %% - - -y = torch.tensor(0) # define the desired class label -scale = 5 # define the desired gradient scale s -progress_bar = tqdm(range(L)) # go back and forth L timesteps - -for i in progress_bar: # go through the denoising process - t = L - i - with autocast(enabled=True): - with torch.no_grad(): - model_output = model( - current_img, timesteps=torch.Tensor((t,)).to(current_img.device) - ).detach() # this is supposed to be epsilon - - with torch.enable_grad(): - x_in = current_img.detach().requires_grad_(True) - logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device)) - log_probs = F.log_softmax(logits, dim=-1) - selected = log_probs[range(len(logits)), y.view(-1)] - a = torch.autograd.grad(selected.sum(), x_in)[0] - alpha_prod_t = scheduler.alphas_cumprod[t] - updated_noise = ( - model_output - (1 - alpha_prod_t).sqrt() * scale * a - ) # update the predicted noise epsilon with the gradient of the classifier - - current_img, _ = scheduler.step(updated_noise, t, current_img) - torch.cuda.empty_cache() - -plt.style.use("default") -plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap="gray") -plt.tight_layout() -plt.axis("off") -plt.show() - - -# %% [markdown] -# # Anomaly Detection -# To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model, which is the healthy reconstruction. - - -# %% -def visualize(img): - _min = img.min() - _max = img.max() - normalized_img = (img - _min) / (_max - _min) - return normalized_img - - -diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() -plt.style.use("default") -plt.imshow(diff, cmap="jet") -plt.tight_layout() -plt.axis("off") -plt.show() - -# %% - -# %% From 0d62a181d5916d581d6d4474eefcd06ae118f225 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 15 Mar 2023 10:20:28 +0100 Subject: [PATCH 09/23] autofix after testing --- .../networks/nets/diffusion_model_unet.py | 3 - .../mednist_ddpm/bundle/scripts/__init__.py | 1 - ...ydetection_tutorial_classifier_guidance.py | 251 +++++++++--------- .../anomaly_detection_with_transformers.py | 2 - 4 files changed, 125 insertions(+), 132 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index eefafa05..11f77a52 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1930,7 +1930,6 @@ def __init__( cross_attention_dim: int | None = None, num_class_embeds: int | None = None, upcast_attention: bool = False, - ) -> None: super().__init__() if with_conditioning is True and cross_attention_dim is None: @@ -1994,8 +1993,6 @@ def __init__( output_channel = num_channels[i] is_final_block = i == len(num_channels) # - 1 - - down_block = get_down_block( spatial_dims=spatial_dims, in_channels=input_channel, diff --git a/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py b/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py index 7998525d..c44e4a34 100644 --- a/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py +++ b/model-zoo/models/mednist_ddpm/bundle/scripts/__init__.py @@ -1,7 +1,6 @@ from __future__ import annotations - def inv_metric_cmp_fn(current_metric: float, prev_best: float) -> bool: """ This inverts comparison for those metrics which reduce like loss values, such that the lower one is better. diff --git a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py index 6c25ce87..0b63c5d1 100644 --- a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py +++ b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py @@ -122,15 +122,17 @@ ] ) + def get_batched_2d_axial_slices(data: Dict): images_3D = data["image"] - batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. + batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( + -1 + ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. slice_label = data["slice_label"] slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() return batched_2d_slices, slice_label - # %% jupyter={"outputs_hidden": false} train_ds = DecathlonDataset( @@ -143,17 +145,16 @@ def get_batched_2d_axial_slices(data: Dict): seed=0, transform=train_transforms, ) -print("len train data", len(train_ds)) #this gives the number of patients in the training set - +print("len train data", len(train_ds)) # this gives the number of patients in the training set train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) data_2d_slices = [] data_slice_label = [] for i, data in enumerate(train_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices.append(b2d) - data_slice_label.append(slice_label2d) + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices.append(b2d) + data_slice_label.append(slice_label2d) total_train_slices = torch.cat(data_2d_slices, 0) total_train_labels = torch.cat(data_slice_label, 0) @@ -182,9 +183,9 @@ def get_batched_2d_axial_slices(data: Dict): data_2d_slices_val = [] data_slice_label_val = [] for i, data in enumerate(val_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices_val.append(b2d) - data_slice_label_val.append(slice_label2d) + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices_val.append(b2d) + data_slice_label_val.append(slice_label2d) total_val_slices = torch.cat(data_2d_slices_val, 0) total_val_labels = torch.cat(data_slice_label_val, 0) @@ -233,58 +234,57 @@ def get_batched_2d_axial_slices(data: Dict): scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # - - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + model.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # + + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + optimizer.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + progress_bar_val = tqdm(enumerate(subset_2D_val)) progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar: + for step, (a, b) in progress_bar_val: images = a.to(device) classes = b.to(device) - optimizer.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noise_pred = inferer( - inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() - progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train diffusion completed, total time: {total_time}.") @@ -293,12 +293,12 @@ def get_batched_2d_axial_slices(data: Dict): plt.title("Learning Curves Diffusion Model", fontsize=20) plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", - ) + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) plt.yticks(fontsize=12) plt.xticks(fontsize=12) plt.xlabel("Epochs", fontsize=16) @@ -345,14 +345,13 @@ def get_batched_2d_axial_slices(data: Dict): out_channels=2, num_channels=(32, 64, 64), attention_levels=(False, True, True), - num_res_blocks=(1,1,1), + num_res_blocks=(1, 1, 1), num_head_channels=64, with_conditioning=False, ) classifier.to(device) - # %% [markdown] # ## Model training of the classification model # We train our classification model for 100 epochs. @@ -373,78 +372,78 @@ def get_batched_2d_axial_slices(data: Dict): scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): - classifier.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - - for step, (a, b) in progress_bar: + classifier.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + + optimizer_cls.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + + with autocast(enabled=False): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image + pred = classifier(noisy_img, timesteps) + loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") + + loss.backward() + optimizer_cls.step() + + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + print("final step train", step) + + if (epoch + 1) % val_interval == 0: + classifier.eval() + val_epoch_loss = 0 + subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar_val.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: images = a.to(device) classes = b.to(device) + timesteps = torch.randint(0, 1, (len(images),)).to( + device + ) # check validation accuracy on the original images, i.e., do not add noise - optimizer_cls.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): + with autocast(enabled=False): + noise = torch.randn_like(images).to(device) + pred = classifier(images, timesteps) + val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") - with autocast(enabled=False): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image - pred = classifier(noisy_img, timesteps) - loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") - - loss.backward() - optimizer_cls.step() - - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - print("final step train", step) - - if (epoch + 1) % val_interval == 0: - classifier.eval() - val_epoch_loss = 0 - subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar_val.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - timesteps = torch.randint(0, 1, (len(images),)).to( - device - ) # check validation accuracy on the original images, i.e., do not add noise - - with torch.no_grad(): - with autocast(enabled=False): - noise = torch.randn_like(images).to(device) - pred = classifier(images, timesteps) - val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") - - val_epoch_loss += val_loss.item() - _, predicted = torch.max(pred, 1) - progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + val_epoch_loss += val_loss.item() + _, predicted = torch.max(pred, 1) + progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train completed, total time: {total_time}.") - ## Learning curves for the Classifier +## Learning curves for the Classifier plt.style.use("seaborn-bright") plt.title("Learning Curves", fontsize=20) plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", - ) + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) plt.yticks(fontsize=12) plt.xticks(fontsize=12) plt.xlabel("Epochs", fontsize=16) diff --git a/tutorials/generative/anomaly_detection/anomaly_detection_with_transformers.py b/tutorials/generative/anomaly_detection/anomaly_detection_with_transformers.py index a436cab9..fd8f0adf 100644 --- a/tutorials/generative/anomaly_detection/anomaly_detection_with_transformers.py +++ b/tutorials/generative/anomaly_detection/anomaly_detection_with_transformers.py @@ -329,7 +329,6 @@ progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=110) progress_bar.set_description(f"Epoch {epoch}") for step, batch in progress_bar: - images = batch["image"].to(device) optimizer.zero_grad(set_to_none=True) @@ -352,7 +351,6 @@ val_loss = 0 with torch.no_grad(): for val_step, batch in enumerate(val_loader, start=1): - images = batch["image"].to(device) logits, quantizations_target, _ = inferer( From 5b2cf1b3fe65928635673d07f0e1fe76c802229a Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 15 Mar 2023 12:16:53 +0100 Subject: [PATCH 10/23] move anomaly detection tutorials to the folder /tutorials/generative/anomaly_detection --- ...tection_tutorial_classifier_guidance.ipynb | 2913 +++++++++++++++++ ...ydetection_tutorial_classifier_guidance.py | 552 ++++ 2 files changed, 3465 insertions(+) create mode 100644 tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb create mode 100644 tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb new file mode 100644 index 00000000..e335e271 --- /dev/null +++ b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb @@ -0,0 +1,2913 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "63d95da6", + "metadata": {}, + "source": [ + "# Diffusion Models for Medical Anomaly Detection with Classifier Guidance\n", + "\n", + "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", + "\n", + "We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\\\n", + "We then tranlsate an input slice to its healthy reconstruction using DDIMs.\\\n", + "Anomaly detection is performed by taking the difference between input and output, as proposed in [1].\n", + "\n", + "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", + "\n", + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "75f2d5f3", + "metadata": {}, + "outputs": [], + "source": [ + "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", + "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "!python -c \"import seaborn\" || pi resblock_updown: bool = False,p install -q seaborn" + ] + }, + { + "cell_type": "markdown", + "id": "6b766027", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "972ed3f3", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/anomaly_detection/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", + "MONAI version: 1.2.dev2304\n", + "Numpy version: 1.23.2\n", + "Pytorch version: 1.12.1\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 9a57be5aab9f2c2a134768c0c146399150e247a0\n", + "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: 0.4.10\n", + "ITK version: 5.3.0\n", + "Nibabel version: 4.0.1\n", + "scikit-image version: 0.19.3\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: 2.12.0\n", + "gdown version: 4.6.4\n", + "TorchVision version: 0.13.1\n", + "tqdm version: 4.64.1\n", + "lmdb version: 1.4.0\n", + "psutil version: 5.9.4\n", + "pandas version: 1.5.3\n", + "einops version: 0.6.0\n", + "transformers version: 4.21.3\n", + "mlflow version: 2.1.1\n", + "pynrrd version: 1.0.0\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "import os\n", + "import sys\n", + "import time\n", + "from typing import Dict\n", + "import tempfile\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from monai import transforms\n", + "from monai.apps import DecathlonDataset\n", + "from monai.config import print_config\n", + "from monai.data import DataLoader\n", + "from monai.utils import first, set_determinism\n", + "from torch.cuda.amp import GradScaler, autocast\n", + "from tqdm import tqdm\n", + "\n", + "from generative.inferers import DiffusionInferer\n", + "from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet\n", + "from generative.networks.schedulers.ddim import DDIMScheduler\n", + "\n", + "\n", + "torch.multiprocessing.set_sharing_strategy(\"file_system\")\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "id": "7d4ff515", + "metadata": {}, + "source": [ + "## Setup data directory" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8b4323e7", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory" + ] + }, + { + "cell_type": "markdown", + "id": "99175d50", + "metadata": {}, + "source": [ + "## Set deterministic training for reproducibility" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "34ea510f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "set_determinism(42)" + ] + }, + { + "cell_type": "markdown", + "id": "c3f70dd1-236a-47ff-a244-575729ad92ba", + "metadata": { + "tags": [] + }, + "source": [ + "## Preprocessing of the BRATS Dataset in 2D slices for training\n", + "We download the BRATS training dataset from the Decathlon dataset. \\\n", + "We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\\\n", + "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_.\n" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n", + "To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ": Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n" + ] + } + ], + "source": [ + "channel = 0 # 0 = Flair\n", + "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", + "\n", + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\", \"label\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n", + " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", + " transforms.AddChanneld(keys=[\"image\"]),\n", + " transforms.EnsureTyped(keys=[\"image\", \"label\"]),\n", + " transforms.Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\"),\n", + " transforms.Spacingd(keys=[\"image\", \"label\"], pixdim=(3.0, 3.0, 2.0), mode=(\"bilinear\", \"nearest\")),\n", + " transforms.CenterSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 64)),\n", + " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", + " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", + " transforms.Lambdad(\n", + " keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze()\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "def get_batched_2d_axial_slices(data: Dict):\n", + " images_3D = data[\"image\"]\n", + " batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", + " slice_label = data[\"slice_label\"]\n", + " slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze()\n", + " return batched_2d_slices, slice_label\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "da1927b0", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2023-03-13 16:09:17,074 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-03-13 16:09:17,075 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-03-13 16:09:17,076 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n", + "len train data 388\n" + ] + } + ], + "source": [ + "\n", + "train_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"training\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "print(\"len train data\", len(train_ds)) #this gives the number of patients in the training set\n", + "\n", + "\n", + "\n", + "train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", + "data_2d_slices = []\n", + "data_slice_label = []\n", + "for i, data in enumerate(train_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices.append(b2d)\n", + " data_slice_label.append(slice_label2d)\n", + " \n", + "total_train_slices = torch.cat(data_2d_slices, 0)\n", + "total_train_labels = torch.cat(data_slice_label, 0)" + ] + }, + { + "cell_type": "markdown", + "id": "fac55e9d", + "metadata": { + "tags": [] + }, + "source": [ + "## Preprocessing of the BRATS Dataset in 2D slices for validation\n", + "We download the BRATS validation dataset from the Decathlon dataset. \n", + "We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_.\n", + "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2023-03-13 16:19:38,821 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", + "2023-03-13 16:19:38,824 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", + "2023-03-13 16:19:38,826 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n" + ] + } + ], + "source": [ + "val_ds = DecathlonDataset(\n", + " root_dir=root_dir,\n", + " task=\"Task01_BrainTumour\",\n", + " section=\"validation\", # validation\n", + " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " num_workers=4,\n", + " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " seed=0,\n", + " transform=train_transforms,\n", + ")\n", + "\n", + "\n", + "val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", + "data_2d_slices_val = []\n", + "data_slice_label_val = []\n", + "for i, data in enumerate(val_loader_3D):\n", + " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", + " data_2d_slices_val.append(b2d)\n", + " data_slice_label_val.append(slice_label2d)\n", + "\n", + "total_val_slices = torch.cat(data_2d_slices_val, 0)\n", + "total_val_labels = torch.cat(data_slice_label_val, 0)\n" + ] + }, + { + "cell_type": "markdown", + "id": "08428bc6", + "metadata": {}, + "source": [ + "## Define network, scheduler, optimizer, and inferer\n", + "At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", + "the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms\n", + "in the 3rd level (`num_head_channels=64`).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "bee5913e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "device = torch.device(\"cuda\")\n", + "\n", + "model = DiffusionModelUNet(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=1,\n", + " num_channels=(64, 64, 64),\n", + " attention_levels=(False, False, True),\n", + " num_res_blocks=1,\n", + " num_head_channels=64,\n", + " with_conditioning=False,\n", + ")\n", + "model.to(device)\n", + "\n", + "scheduler = DDIMScheduler(num_train_timesteps=1000)\n", + "\n", + "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", + "\n", + "inferer = DiffusionInferer(scheduler)" + ] + }, + { + "cell_type": "markdown", + "id": "2a4d3ab2", + "metadata": { + "tags": [] + }, + "source": [ + "## Model training of the diffusion model\n", + "We train our diffusion model for 100 epochs, with a batch size of 32." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6c0ed909", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 534it [01:42, 5.21it/s, loss=0.163] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 1: : 534it [01:46, 4.99it/s, loss=0.0234] \n", + "4224it [01:27, 48.47it/s]\n", + "Epoch 2: : 534it [01:47, 4.96it/s, loss=0.0207] \n", + "4224it [01:26, 48.76it/s]\n", + "Epoch 3: : 534it [01:46, 4.99it/s, loss=0.0199] \n", + "4224it [01:24, 49.73it/s]\n", + "Epoch 4: : 534it [01:46, 5.00it/s, loss=0.0198] \n", + "4224it [01:26, 48.90it/s]\n", + "Epoch 5: : 534it [01:46, 5.00it/s, loss=0.0192] \n", + "4224it [01:26, 48.97it/s]\n", + "Epoch 6: : 534it [01:47, 4.98it/s, loss=0.0199] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 7: : 534it [01:47, 4.99it/s, loss=0.0188] \n", + "4224it [01:26, 48.65it/s]\n", + "Epoch 8: : 534it [01:47, 4.95it/s, loss=0.0184] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 9: : 534it [01:47, 4.98it/s, loss=0.0179] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 10: : 534it [01:47, 4.98it/s, loss=0.0183] \n", + "4224it [01:25, 49.12it/s]\n", + "Epoch 11: : 534it [01:47, 4.98it/s, loss=0.0183] \n", + "4224it [01:26, 48.83it/s]\n", + "Epoch 12: : 534it [01:48, 4.94it/s, loss=0.0182] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 13: : 534it [01:47, 4.95it/s, loss=0.0185] \n", + "4224it [01:27, 48.52it/s]\n", + "Epoch 14: : 534it [01:47, 4.95it/s, loss=0.0176] \n", + "4224it [01:27, 48.54it/s]\n", + "Epoch 15: : 534it [01:47, 4.95it/s, loss=0.018] \n", + "4224it [01:26, 48.60it/s]\n", + "Epoch 16: : 534it [01:47, 4.99it/s, loss=0.0181] \n", + "4224it [01:27, 48.10it/s]\n", + "Epoch 17: : 534it [01:47, 4.95it/s, loss=0.0179] \n", + "4224it [01:28, 47.99it/s]\n", + "Epoch 18: : 534it [01:47, 4.97it/s, loss=0.0177] \n", + "4224it [01:26, 48.73it/s]\n", + "Epoch 19: : 534it [01:47, 4.97it/s, loss=0.0179] \n", + "4224it [01:28, 47.86it/s]\n", + "Epoch 20: : 534it [01:47, 4.95it/s, loss=0.0177] \n", + "4224it [01:28, 47.48it/s]\n", + "Epoch 21: : 534it [01:47, 4.95it/s, loss=0.0175] \n", + "4224it [01:27, 48.23it/s]\n", + "Epoch 22: : 534it [01:47, 4.95it/s, loss=0.0171] \n", + "4224it [01:24, 49.97it/s]\n", + "Epoch 23: : 534it [01:47, 4.96it/s, loss=0.0169] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 24: : 534it [01:47, 4.98it/s, loss=0.0172] \n", + "4224it [01:27, 48.49it/s]\n", + "Epoch 25: : 534it [01:47, 4.99it/s, loss=0.0168] \n", + "4224it [01:25, 49.39it/s]\n", + "Epoch 26: : 534it [01:46, 5.00it/s, loss=0.0169] \n", + "4224it [01:26, 48.62it/s]\n", + "Epoch 27: : 534it [01:47, 4.98it/s, loss=0.0171] \n", + "4224it [01:27, 48.43it/s]\n", + "Epoch 28: : 534it [01:47, 4.97it/s, loss=0.0175] \n", + "4224it [01:25, 49.18it/s]\n", + "Epoch 29: : 534it [01:46, 5.01it/s, loss=0.0171] \n", + "4224it [01:25, 49.59it/s]\n", + "Epoch 30: : 534it [01:47, 4.95it/s, loss=0.017] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 31: : 534it [01:47, 4.99it/s, loss=0.0169] \n", + "4224it [01:25, 49.12it/s]\n", + "Epoch 32: : 534it [01:46, 4.99it/s, loss=0.0168] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 33: : 534it [01:46, 5.00it/s, loss=0.0166] \n", + "4224it [01:26, 48.82it/s]\n", + "Epoch 34: : 534it [01:47, 4.97it/s, loss=0.0173] \n", + "4224it [01:27, 48.49it/s]\n", + "Epoch 35: : 534it [01:46, 4.99it/s, loss=0.0169] \n", + "4224it [01:26, 48.66it/s]\n", + "Epoch 36: : 534it [01:47, 4.99it/s, loss=0.0171] \n", + "4224it [01:26, 48.92it/s]\n", + "Epoch 37: : 534it [01:47, 4.97it/s, loss=0.0166] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 38: : 534it [01:47, 4.99it/s, loss=0.0163] \n", + "4224it [01:27, 48.55it/s]\n", + "Epoch 39: : 534it [01:47, 4.97it/s, loss=0.0166] \n", + "4224it [01:26, 48.55it/s]\n", + "Epoch 40: : 534it [01:46, 5.00it/s, loss=0.0169] \n", + "4224it [01:25, 49.31it/s]\n", + "Epoch 41: : 534it [01:47, 4.99it/s, loss=0.0169] \n", + "4224it [01:26, 48.85it/s]\n", + "Epoch 42: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 43: : 534it [01:46, 4.99it/s, loss=0.0171] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 44: : 534it [01:47, 4.99it/s, loss=0.0167] \n", + "4224it [01:27, 48.53it/s]\n", + "Epoch 45: : 534it [01:46, 5.00it/s, loss=0.0167] \n", + "4224it [01:27, 48.40it/s]\n", + "Epoch 46: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 47: : 534it [01:47, 4.99it/s, loss=0.0162] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 48: : 534it [01:46, 5.00it/s, loss=0.017] \n", + "4224it [01:27, 48.50it/s]\n", + "Epoch 49: : 534it [01:47, 4.98it/s, loss=0.0164] \n", + "4224it [01:27, 48.21it/s]\n", + "Epoch 50: : 534it [01:47, 4.97it/s, loss=0.0168] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 51: : 534it [01:47, 4.98it/s, loss=0.0163] \n", + "4224it [01:27, 48.10it/s]\n", + "Epoch 52: : 534it [01:47, 4.97it/s, loss=0.0158] \n", + "4224it [01:27, 48.36it/s]\n", + "Epoch 53: : 534it [01:47, 4.96it/s, loss=0.0163] \n", + "4224it [01:27, 48.32it/s]\n", + "Epoch 54: : 534it [01:47, 4.96it/s, loss=0.0157] \n", + "4224it [01:27, 48.03it/s]\n", + "Epoch 55: : 534it [01:47, 4.99it/s, loss=0.0164] \n", + "4224it [01:27, 48.19it/s]\n", + "Epoch 56: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.46it/s]\n", + "Epoch 57: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:27, 48.47it/s]\n", + "Epoch 58: : 534it [01:47, 4.97it/s, loss=0.017] \n", + "4224it [01:27, 48.46it/s]\n", + "Epoch 59: : 534it [01:47, 4.98it/s, loss=0.0164] \n", + "4224it [01:27, 48.38it/s]\n", + "Epoch 60: : 534it [01:47, 4.94it/s, loss=0.0165] \n", + "4224it [01:27, 48.27it/s]\n", + "Epoch 61: : 534it [01:47, 4.96it/s, loss=0.0164] \n", + "4224it [01:27, 48.50it/s]\n", + "Epoch 62: : 534it [01:47, 4.97it/s, loss=0.0164] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 63: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:27, 48.06it/s]\n", + "Epoch 64: : 534it [01:47, 4.97it/s, loss=0.0163] \n", + "4224it [01:27, 48.35it/s]\n", + "Epoch 65: : 534it [01:47, 4.98it/s, loss=0.0159] \n", + "4224it [01:27, 48.53it/s]\n", + "Epoch 66: : 534it [01:47, 4.97it/s, loss=0.0161] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 67: : 534it [01:47, 4.97it/s, loss=0.0164] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 68: : 534it [01:48, 4.94it/s, loss=0.016] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 69: : 534it [01:47, 4.98it/s, loss=0.0156] \n", + "4224it [01:27, 48.34it/s]\n", + "Epoch 70: : 534it [01:47, 4.98it/s, loss=0.0162] \n", + "4224it [01:26, 48.96it/s]\n", + "Epoch 71: : 534it [01:47, 4.97it/s, loss=0.0159] \n", + "4224it [01:25, 49.55it/s]\n", + "Epoch 72: : 534it [01:47, 4.97it/s, loss=0.0159] \n", + "4224it [01:27, 48.08it/s]\n", + "Epoch 73: : 534it [01:47, 4.97it/s, loss=0.0165] \n", + "4224it [01:26, 48.59it/s]\n", + "Epoch 74: : 534it [01:47, 4.98it/s, loss=0.0161] \n", + "4224it [01:27, 48.33it/s]\n", + "Epoch 75: : 534it [01:46, 5.00it/s, loss=0.0164] \n", + "4224it [01:27, 48.20it/s]\n", + "Epoch 76: : 534it [01:47, 4.95it/s, loss=0.0165] \n", + "4224it [01:26, 48.73it/s]\n", + "Epoch 77: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:27, 48.45it/s]\n", + "Epoch 78: : 534it [01:47, 4.95it/s, loss=0.0158] \n", + "4224it [01:27, 48.42it/s]\n", + "Epoch 79: : 534it [01:47, 4.96it/s, loss=0.0163] \n", + "4224it [01:26, 48.85it/s]\n", + "Epoch 80: : 534it [01:47, 4.96it/s, loss=0.0156] \n", + "4224it [01:27, 48.52it/s]\n", + "Epoch 81: : 534it [01:47, 4.97it/s, loss=0.0158] \n", + "4224it [01:27, 48.44it/s]\n", + "Epoch 82: : 534it [01:47, 4.97it/s, loss=0.0163] \n", + "4224it [01:26, 48.57it/s]\n", + "Epoch 83: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:27, 48.24it/s]\n", + "Epoch 84: : 534it [01:47, 4.96it/s, loss=0.016] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 85: : 534it [01:47, 4.99it/s, loss=0.0153] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 86: : 534it [01:47, 4.98it/s, loss=0.0167] \n", + "4224it [01:27, 48.54it/s]\n", + "Epoch 87: : 534it [01:47, 4.96it/s, loss=0.0159] \n", + "4224it [01:27, 48.22it/s]\n", + "Epoch 88: : 534it [01:47, 4.96it/s, loss=0.0159] \n", + "4224it [01:26, 48.77it/s]\n", + "Epoch 89: : 534it [01:47, 4.95it/s, loss=0.0164] \n", + "4224it [01:26, 48.56it/s]\n", + "Epoch 90: : 534it [01:47, 4.96it/s, loss=0.0161] \n", + "4224it [01:26, 48.68it/s]\n", + "Epoch 91: : 534it [01:47, 4.95it/s, loss=0.0158] \n", + "4224it [01:26, 48.94it/s]\n", + "Epoch 92: : 534it [01:47, 4.96it/s, loss=0.0158] \n", + "4224it [01:26, 48.65it/s]\n", + "Epoch 93: : 534it [01:47, 4.96it/s, loss=0.0166] \n", + "4224it [01:26, 48.70it/s]\n", + "Epoch 94: : 534it [01:47, 4.98it/s, loss=0.0161] \n", + "4224it [01:26, 48.78it/s]\n", + "Epoch 95: : 534it [01:48, 4.94it/s, loss=0.0155] \n", + "4224it [01:26, 48.95it/s]\n", + "Epoch 96: : 534it [01:47, 4.98it/s, loss=0.0162] \n", + "4224it [01:26, 48.96it/s]\n", + "Epoch 97: : 534it [01:47, 4.97it/s, loss=0.016] \n", + "4224it [01:26, 48.79it/s]\n", + "Epoch 98: : 534it [01:47, 4.98it/s, loss=0.016] \n", + "4224it [01:27, 48.48it/s]\n", + "Epoch 99: : 534it [01:47, 4.98it/s, loss=0.0157] \n", + "4224it [01:27, 48.33it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train diffusion completed, total time: 19490.821256637573.\n" + ] + }, + { + "data": { + "image/png": "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7NKmTZsMwACMSZMmFbl+WlqaMXr0aMs2cns5Ozsbn3zySa71T506lW/drNdjjz1mpKWl5bqNL774wlJu586dRvPmzXPUzzq27GXDw8ONzp0757nPVq1aGfHx8bnuc+jQoQZgBAUF5bo++35nz55tODk55boPDw8PY8uWLXme33Pnzhl16tTJs419+vQxfvrpJ8v/b9q0Kc9t5eWHH36wOs/FVdC5yZL9Z3H69Okc64OCggzAGDp0qPHRRx/leg4NwzDeeOMNAzBMJlOu28nuP//5j6Xu7t27c6w/ceKE0bBhw3zfi3Xr1jWOHTuW6/Y/+OADw8HBocD3c1xcXL7tzM3p06ct9YcOHVroeqNGjbLUW7hwYY712c9zbvvK6xUUFGT1M8zrlX27ue0rN/m9h+Li4oz77ruvwP0+/PDDOepOmjTJ6r2Tm9DQUMPV1TXP7To6Ohr//ve/86xfUr/7Bcn+t/uLL74w9uzZY/W37UYHDx60rN+zZ4/Vzy6vvxtnzpwxGjRokO95bteunXH16tU827l582bDx8cnz/pTpkwp1M9lx44dRvXq1fNtS6tWrYyLFy/mWr9Dhw4GYHTo0CHf81raNOfpNjVy5EjLt5levXoxZMgQ6tWrh8lkYu/evcycOZODBw8yatQoqlatSt++fa3qZ2Rk4OLiQo8ePejWrRsNGzbEz8+P6Ohojh07xpw5czh48CALFiygVq1aTJkypcD2HDhwgCeeeIJBgwZRtWpVzp07h6ura46yo0aNYtu2bQwdOpRHHnnEUvbdd9/l999/Z8eOHUybNo233377ps/P2rVr2b59O02bNuWFF16gSZMmJCUlsWLFCmbNmkViYiKPP/44x48fx8XFxapuamoqvXv35sSJE5bzO2rUKAIDAzl//jyffPIJK1eu5PLlyzfdPsDSswDQp0+fYm3rVti5cycLFiwgMDCQl19+mbvuuouMjAx++eUXAIYMGcK0adMwDINFixbx2muv5bmthQsXAhAcHMydd95pte7PP//k3nvv5dKlS3h7ezNq1Ci6du1KlSpViImJ4aeffmLWrFkcP36cnj17smfPHnx9fS319+/fz8svv4zZbOaOO+7g2WefpXnz5vj5+REfH8/x48fZtGkTK1asuAVnKW9du3blk08+AeCXX34pcEIxQPXq1S09LMOHD2fXrl3cfffdfPHFF5YyWd/67777bgCaNGkCwOjRo3nmmWcs5cqXL19ixwKZvUdZP/s+ffowZMgQatSogZubG5cvX2bfvn2sXLnypu4Z9OOPPzJs2DAMw8DLy4uXXnqJrl274uTkxNatW3n77be5cuUKr732GuXKlWP06NF5bqs4v/s3o0WLFjRu3Jjw8HC++uorpk+fbrU+q0eqUaNGtGjRgn379uW7vfj4eDp37sypU6eAzAsBRowYgb+/P6dPn+bDDz9ky5Yt/Prrr/Tp04dffvklR2/WmTNn6Nu3L3FxcTg4ODBq1CgGDBiAr68v+/fvZ/r06UyaNMnyHsrLgQMH6NSpEwkJCVSuXJnRo0dz3333UaFCBaKiovj+++/5+OOP2bFjBw8++CC//PKL/Qwf2zq9yc0pTs/T0qVLLXU//fTTXMskJSVZendq1qyZo/coPj7eiIyMzHMfZrPZGDZsmAEYnp6exvXr13OUufHb7+eff57n9m4s+9VXX+Uok5ycbDRu3NgAjAoVKuTa41XYnifA6N27t5GSkpKjzLRp0yxlli9fnmP9Bx98YFn/7LPP5rqfZ5991mpfN9Pz1K1bN0v9vHpUiqKke54Ao0mTJsa1a9fy3Nadd95pAEajRo3yLHPs2DHL9qZOnZpjfZ8+fQzACAwMNE6ePJnrNvbs2WN4enoagPHGG29YrZswYYLlfZrXt1/DMIzr168bGRkZea7Py832PJ04ccJSr3PnzjnWF9QbVNhv7IX5O1ISPU+BgYEGYAwYMCDfbeTWG5JfD0dqaqqlZ8PLy8v4448/cpQ5c+aMUa1aNUvP0eXLl3OUKYnf/cK4sefJMAzjnXfeMQAjICDA6j1mNpst52369OmGYRgF9jy9/PLLlvU3vteztjlkyBBLmblz5+Yo079/f8v6RYsW5VgfGxtrNGvWzOqc5bafpk2bGoDRrFmzXM+5YRjG6tWrLb2+n332WY71ZbXnSXOebkNZPTL9+vXjySefzLWMm5sbH374IZD5LeTGOTmenp5Uq1Ytz32YTCZmzJiBo6MjCQkJBY6Rd+7cmREjRhSq/f379+exxx7LsdzV1ZVnn30WyLx8P7e5E4WVNYcot2+Wzz//vGV51jfp7D7++GMA/P39LXOSbvTee+/h7+9/0+0DrObBVKlSpVjbulXmzJmT75ViQ4YMAeDgwYN5fqPO6nUCcvS+hIeHs3LlSgA+/PBDatWqles2WrRowZgxYwCYN2+e1bqLFy8CmVeb5XcefX19cXAovT+ZFSpUsPz72rVrpbbfWyXrPN933335lvPz8yvSdlesWGG5YvD111+nefPmOcoEBQVZfhcTExOteuJuVJzf/Zs1ZMgQHBwcOH/+vFWP8ubNm4mIiMDBwcHyu5KflJQUPvvsMwAaNmyY68UGJpOJuXPnWt5fWX/ns/z555989913QGYPYdacrOy8vb0tvaJ5+fHHH9m/fz+QOWcr+73FsuvZsycDBgwAyPfnUtYoPN1mLly4wO7duwF45JFH8i3boEEDyxv+999/z7dsWloa58+f5/Dhw4SHhxMeHk5kZKTlF7SgrubC/GEoTNm77rrL8u+sbuub0a1btzyvWvP29rbce+fGfVy4cIGjR48CmefXzc0t1224ubkxcODAm24fQFxcnOXfxbmZ4q0SGBhY4AdlSEiIJZAsWrQo1zJZVyG1bds2RzjK+iPv4eFhuUItL+3btwcybxgaERFhWZ71JeDQoUPs2LEj322UpqwbVYL1z9peZZ3nr7/+ulB31C6srC9mJpMp3y9gAwcOtAzX5vdl7mZ/94ujevXqdOrUCbCeOJ71744dOxIQEFDgdnbv3s3169eBzMn7eU0u9/Hxsfz9P3ToEH/++adl3aZNmywTuIcPH57nvlq1akWjRo3yXJ/1u1m/fv0Cr+zN+t3cuXOn3UweV3i6zezatcvy75CQkFwfG5H9ldW7kfWtMbu0tDTmzJlDmzZt8PLyIjAwkIYNG9KkSRPLKyoqCiDXq4WyK8pl88HBwXmuy/6ttTgfOPntI/t+btxHeHi45d/Zg1xuCpovUBBvb2/LvxMSEoq1rVuhMD/TatWqWa76CwsLy3E1286dOzl27BiQe2jOej8nJiZarsbL65V9Xlj293NISAjOzs6kpKRw77330rdvX/773/9y8ODBUrm6Li/Z31s+Pj42a0dJGTp0KABbt261zC1bsWJFsef+Zf3O1axZM9/bdLi4uNCiRQurOrm52d/94nriiScAWLp0KUlJSSQlJbFs2TIAHn/88UJtI/txtW7dOt+y2ddnr5f9qsSWLVvmu41WrVrluS7rd/Po0aMFfs5kjRikpqYSHR2d7z7LCoWn20xWmCmqG78pRkdH07ZtW5599lm2b99uuVdMXpKSkvJdX5TJqVmXcOcm+7BKcb7B5LeP7Pu5cR/Zh1fy+0MOUKlSpZtsXabs3eCXLl0q1rZuhcL+TLNCUUREBD///LPVuqwhOycnp1x7Skvi/RwcHExYWBjly5cnPT2dlStXMnr0aBo3bkzlypV5/PHHS3SIprCyf+Eo6lBWWTRhwgRGjBiByWQiKiqKOXPm0L9/f6pUqUKTJk2YNGnSTb2Psz5sCzN0nXUTyvw+oG/2d7+4+vfvj4eHB3FxcXz33Xd8++23xMbG4u7uzsMPP1yobWQ/roLOR/YbcmavV5S/Yfnto6Q+a8oqXW13m8n+C79w4cJC9/jc+EH4wgsvWIb/sq7maNq0KZUrV7Y8lR0yb/oXERFR4Df4wt67RP7SrFkz1q1bB8CePXvyfIyHrRT2Z9q/f3+eeeYZkpKSWLRoER06dAAy36tZd1Lu3r17rmEz6/18xx138P333xe6bXfccYfV/z/88MN07dqVr7/+mrVr1/LLL79w+fJlrly5woIFC1iwYAFDhw5l3rx5pTbv6Y8//rD8u379+qWyz1vJ2dmZzz//nJdeeomwsDA2btzIrl27SE1NtQz1f/DBByxYsIAHH3ywyNsvzFV6tuxJLIiXlxf9+vVj4cKFfPXVV5a2PvTQQ1a9zIVV0PnI61xkX36z24C/fjfvvfde/vvf/+a7neyKOxe0tCg83WayT0I1mUy5PiqiILGxsZYPtUcffdRqQu+N/g4TXYsie8gs6JtXcYcrOnTowPvvvw9kTs4cNGhQsbaXFQoKunleSQ8R+vj40LdvX5YsWcI333zD7NmzcXFxYePGjZbhtbzmuWW9ny9dukRwcHChbxKaG19fX0aNGsWoUaOAzLkg33//PbNnzyYyMpLQ0FBatGjBCy+8cNP7KIqsYAwU6kait1JJvjcaNmzI1KlTmTp1KklJSfz2228sWrSIL7/8kvj4eEJCQjh58mS+F6Rkl9Url9vUghtl9WyV1Z68J554goULF1oexQKFH7ID6+O6ePFivo/cyd7Ll71e9n9funQp34eH5/c3rkKFCly6dInLly/f1OdMWadhu9tM1pg/YPULWhTHjx8nLS0NgMGDB+dZ7ujRo8THx9/UPuxV9gmU2eeX5aag9QXp3r275VvaN998Y7ni6GZlfbvNmnCal6wJ8SUpKxxdu3bNcmf6rAnknp6eefZEZL2fExMT+e2330q0TQ0bNuTVV19l27Ztlgn5S5YsKdF95OXy5ctWx9+9e/dS2W9est4bBX0ZKup7w93dna5duzJv3jzL1XBJSUmWKygLI+uD+cyZM/l+mKelpVl688rqh3mXLl2oVq0a6enplsexFOVnn/24tm/fnm/Z7BdHZK+Xdd8vyJxzmJ/81mf9bh47doyzZ8/mux17pPB0m6lTpw4NGzYEYPHixZw7d67I20hPT7f8O7/x6aJ01f5dBAQEWL7tffPNN3k+EiQ5OZlvvvmmWPtycXHh5Zdftmxv5MiRhZ6Hcf78eTZu3Gi1LGsoKy4uLs8PwdTUVMsk1pLUq1cvyzfehQsXkpyczPLly4HMYYu8ribMHqrefffdEm8XZF41mPUzLejCh5JgNpsZNmyY5Xdr1KhRNu8pyXpv7NmzJ8+hmvDw8AIfgZKfLl26WP5dlPPctWtXIHMI6cbbUGS3dOlSYmJirOqUNY6Ojjz++OO4urri6urKY489VqQpDXfddZfl1iChoaF5/j2Ii4uzfBFo2LChVS9fp06dLPsMDQ3Nc1+7du3Kd+L9Aw88YPn3rfrdtCWFp9vQG2+8AWR+4Pbv3z/f4aOUlBTmzp1rFQLq1KljGQvPukv5jVauXMns2bNLsNX24+mnnwYyL4kfN25crmXGjRtHZGRksff1wgsvWC5xXrt2Lf369cv352kYBgsXLuSuu+6y3IMlS9ZcI4AZM2bkWveFF14okXbfyNnZ2XLrhh9++IFFixYRGxsL5H9ripYtW1q+ma9atYpJkyblu58zZ87keADrt99+m29vW0REBEeOHAFyzpUqaefOnaNnz56sWrUKyJzMXtAxlYas90ZkZGSuD7CNi4vL9zYB0dHRBT4bMHtPeFHOc79+/Sw9sP/+979zvS1KRESE5YuGh4dHvpfg29o777xDcnIyycnJlmH5wnJ1dbXcu+/gwYO5PtnBMAyeffZZS0DNutItS7Vq1SxfSr7//vtce1vj4+Mtw9t5efjhh2nQoAEAH330EZ9//nm+5cPDw/nhhx/yLVOWaM7T38DevXuZP39+geXatWtHnTp1CAkJYe3atYSGhrJ7924aNmzI008/TYcOHahUqRIJCQmcPHmSX375heXLlxMdHW25jBYyx7J79+7Njz/+yKpVq+jZsydPP/00NWrUICoqimXLljF//nxq1arF9evXiz23x948++yzfPHFF4SHh/Phhx9y6tQpnn76aQICAiyPZ/nxxx9p1aqVpev8Zh5JAZlzUZYsWUKfPn3Yvn07P/zwA7Vr12bIkCF07tyZgIAAnJ2duXjxItu2bWPZsmWWIHCjFi1a0KZNG7Zt28ann35KamoqQ4cOxdfXl+PHj/Pf//6XzZs307Zt2wLv+3UzHnvsMT7++GOSkpIsD/+tVKlSgU+R/+KLL7j77rv5888/efPNN1m7di0jRoygSZMmuLm5cfXqVfbv38+aNWvYuHEjDz30kNWN/2bOnMmQIUO4//776dy5Mw0aNMDX15dr166xa9cuZs+ebblaNL/HehTG9evXrb6tJyUlcf36dQ4dOsTmzZtZuXKlpWe3fv36rFy50upRMrby2GOPMXnyZGJjYxk5ciQnTpygR48emEwmdu3axQcffMCFCxdo0aKF1UT3LLGxsTz44IPUrFmT/v3707p1a4KCgnBycuLPP//khx9+sNzcMSAgIMfjoPLj7OzMJ598YnmcSLt27Rg3bhxdunSxPJ5l+vTpliG9999/P88bNv4dTJw4keXLl3Pq1CmmTp1KeHh4jsezZN30uG3btrmGoBkzZrBu3Tri4uJ49NFH2bJlCwMGDMDHx8fyeJZjx45x99135zn9wNHRka+//pp77rmH+Ph4nnzySb755hseffRR6tevj7OzM1FRUfzxxx+sXLmSrVu38tJLLxXpZ29TtrituRRf9lv8F/aV9SgAwzCM9PR045VXXjEcHR0LrOfp6WkkJiZa7f/cuXNGjRo18qxTo0YN4+DBg/k+1qGgx3zcTNnsj8LIfrxZivJg4PwU9MiAs2fPGrVr187z/HTv3t1YvXq15f+3bduW7/4KkpSUZLzwwguGi4tLgT9Pk8lkPPbYY8aFCxdybOfw4cNG5cqV86z74osvFunBwEVhNputHu1CPo+3udGZM2eMli1bFur3YPjw4VZ1s36W+b0KeqhsfgrzsN7sLx8fH+PFF180EhIS8t1uaT6exTAMY8mSJXn+vXBzczOWLFmS5+9XYc9B9erVjT179uTYd2EeQDt//vwSezBwfor7uJDcHs9SFIV5MPDp06eN4ODgfM/1vffem++DgTdt2mR4e3vnWX/SpEmF+rns27fPqFu3bqF+/lOmTMlRX49nkTLF0dGRd955h0OHDvHSSy/RokULypcvj6OjI97e3jRq1IghQ4YQGhrKn3/+ibu7u1X9wMBA9uzZw7hx46hXrx6urq74+vrSrFkzJk2axN69ey1zq25HNWrUYN++fUyZMoXGjRvj7u5OuXLlaNOmDXPnzmX16tVWQ6HF7V1wc3Nj5syZHD9+nOnTp9O1a1dq1KiBu7s7bm5u+Pv70717d9566y1Onz7NV199leslwcHBwezZs4fRo0cTFBSEi4sLlSpVomfPnvz444+5DueVFJPJlOPxK4V5GC5kPn5j+/btrFixgsGDB3PHHXfg4eGBs7MzlSpV4p577uGll15iy5YtOYYPlixZwsKFCxk2bBjNmzenatWqODk54eXlRePGjXnmmWf4448/GD9+fIkdK2Qer4+PDwEBAbRu3ZrRo0fz1VdfERkZyYwZMwq831BpGzhwIFu3bqVfv35UqlQJFxcXAgMDGTp0KLt27cr3jvlBQUHs3buX9957j169elG/fn3KlSuHk5MTFStWtFw5evjwYauLWopi6NChHDlyhBdeeIEGDRrg6emJu7s7tWvX5qmnnrolP8OyqmbNmuzbt48PP/yQDh06UKFCBZydnalSpQo9e/bkq6++4ueff853Ll3Hjh05ePCg1d+CKlWqcP/997NmzZpcH/2Sm6ZNm3Lo0CFCQ0N56KGHCAwMxM3NDRcXF6pVq0bHjh1544032L17NxMnTiyhM3DrmQyjDN/4QuRvbNq0aUyYMAEnJyfi4uLyfJSLiIiULep5ErEBwzAs98pq3ry5gpOIiB1ReBK5Bc6cOWN1S4cbTZw40TJxOOuZXyIiYh80bCdyC0yePJkvvviCRx99lHvvvRd/f3/S0tI4fPgwoaGhlqtdGjZsyJ49e3B1dbVtg0VEpNB0qwKRW+TcuXNMnz49z/XBwcH8+OOPCk4iInZG4UnkFhg5ciS+vr6sXbuWEydOcPnyZZKSkvDz86NZs2b069ePESNG4OLiYuumiohIEWnYTkRERKQI1PNUwsxmM5GRkXh7e9/0XaNFRESkdBmGQVxcHP7+/jg45H89ncJTCYuMjCQwMNDWzRAREZGbEBERQUBAQL5lFJ5KmLe3N5B58n18fGzcGhERESmM2NhYAgMDLZ/j+VF4KmFZQ3U+Pj4KTyIiInamMFNudJNMERERkSJQeBIREREpAoUnERERkSJQeBIREREpAoUnERERkSJQeBIREREpAt2qQEREbom0tDQyMjJs3Qy5jTk6OuLs7Fzi21V4EhGREhUbG8uVK1dISUmxdVNEcHV1pWLFiiV670WFJxERKTGxsbFcuHABLy8vKlasiLOzs57zKTZhGAZpaWnExMRw4cIFgBILUApPIiJSYq5cuYKXlxcBAQEKTWJz7u7ueHt7c/78ea5cuVJi4UkTxkVEpESkpaWRkpKCr6+vgpOUGSaTCV9fX1JSUkhLSyuRbSo8iYhIiciaHH4rJuiKFEfWe7KkLmDQsJ2d2HkENv0B6RnQ/z4IDrJ1i0REcqdeJylrSvo9qfBkJ37eB//6OPPf9QIUnkRERGxFw3Z2wjHbTypdt00RERGxGYUnO+Hk+Ne/FZ5ERCQ7k8lEx44dbd2M24ZdhKf4+HjGjh2Lv78/bm5uNG/enMWLFxdY7/z584wdO5YOHTpQrlw5TCYT8+fPz7N8QkICEydOpF69eri6ulKhQgU6derE8ePHS/Bobk728JRhtl07REQkdyaTqUgvsV92Meepf//+7Ny5k+nTp1OvXj0WLVpESEgIZrOZRx99NM96J06cYOHChTRv3pzevXsTFhaWZ9n4+Hg6depEZGQkr776Kk2bNiUmJoatW7eSmJh4Kw6rSBzV8yQiUqZNmjQpx7IpU6bg6+vL2LFjb+m+Dx8+jIeHxy3dh/ylzIenVatWsW7dOktgAujUqRNnz55l3LhxDBo0CMfsySKb9u3bc/nyZQB27dqVb3h64403OHz4MPv376dWrVqW5Q888EAJHs3N07CdiEjZNnny5BzLpkyZQrly5XJdV5KCg4Nv6fbFWpkftluxYgVeXl4MHDjQavnw4cOJjIxk+/btedZ1cCjc4SUmJvLZZ58xcOBAq+BUlmjYTkTk7+HMmTOYTCaGDRvGkSNH6N+/PxUrVsRkMnHmzBkg87MvJCSEOnXq4OHhga+vL/fddx/Lli3LdZu5zXkaNmyYZZtz586lQYMGuLm5ERQUxJQpUzCb9WFys8p8eAoPD6dBgwY4OVl3kjVt2tSyvrh2795NQkICdevWZfTo0ZQvXx4XFxfuvvtufvzxx2JvvySo50lE5O/lxIkTtGnThkuXLjF06FCGDRuGi4sLAOPHj+fgwYO0a9eOF154gYEDB3L06FEGDBjA7Nmzi7SfcePGMWnSJNq0acPTTz8NZPaSTZgwocSP6XZR5oftrl69mmtvkJ+fn2V9cWU9MPCdd96hSZMmfPnllzg4ODBjxgz69u3L6tWr6dGjR651U1JSrJ4cHhsbW+z25Ea3KhAR+Xv57bffmDBhAm+++WaOdatWrcrx2RcfH88999zDhAkTGDlyZKHnOO3evZv9+/dTrVo1ACZMmEDdunWZPXs2kyZNsgQ2KbwyH54g/zuDlsQVC1ldly4uLqxevRpvb28gc25V3bp1mTp1ap7h6e2332bKlCnFbkNBNGwnIvbu7lFwMdrWrchfVT/Y9Ukp7atqVd54441c1+XWaeDl5cWwYcN46aWX2LlzJx06dCjUfiZMmGAJTgAVK1bkwQcfJDQ0lKNHj9KkSZObO4DbWJkPTxUqVMi1dyk6OvM3MKsHqrj7ALjnnnsswQnAw8ODDh068O233+ZZd/z48bz44ouW/4+NjSUwMLDYbbqRhu1ExN5djIYLV2zdirKjWbNmefb6REVFMX36dFavXs3Zs2dJSkqyWh8ZGVno/dx55505lgUEBABw/fr1wjdYLMp8eGrSpAlhYWGkp6dbzXs6cOAAAI0bNy72PrLmT+XGMIx8J567urri6upa7DYURMN2ImLvqhb/u+4tV5ptrFKlSq7Lo6OjadmyJefOnePee++la9eulCtXDkdHR/bu3ct3331nNV2kIL6+vjmWZX2eltSDcm83ZT489evXj08//ZRly5YxaNAgy/LQ0FD8/f1p3bp1sfdRrVo12rZty2+//UZsbCw+Pj5A5lV4W7ZsoU2bNsXeR3FZDdvpvS4idqi0hsPsRV7TTj7//HPOnTvHtGnTeP31163WTZ8+ne+++640mif5KPPhqVevXnTr1o3Ro0cTGxtLnTp1CAsLY82aNSxYsMByj6eRI0cSGhrKyZMnCQr666m5S5cuBeDUqVNA5v2evLy8ABgwYICl3Pvvv0+nTp3o0aMH//rXvzCZTMyYMYMrV64wderU0jrcPGnYTkTk9nDy5Ekg9/sM/vLLL6XdHMlFmQ9PAMuXL+f1119n4sSJREdHExwcTFhYGIMHD7aUycjIICMjA8MwrOreeH+oOXPmMGfOHACrsvfccw8bNmzgjTfeYMiQIQC0adOGzZs307Zt21t1aIWmYTsRkdtDVgfAr7/+ajWZe9GiRaxatcpWzZJs7CI8eXl5MWvWLGbNmpVnmfnz5+f63Lobw1R+2rVrx+bNm2+ihbeerrYTEbk9PP7447zzzjs899xzbNq0iaCgIPbv38/69evp378/y5cvt3UTb3tl/iaZkknDdiIit4eAgAC2bNlCly5dWL9+PR9//DEpKSn89NNP9O3b19bNE8BkFKVrRgoUGxuLr68vMTExlonnJWHnEWj1j8x/P9sPZr9QYpsWESkRycnJnD59mjvuuAM3NzdbN0fEojDvzaJ8fqvnyU5o2E5ERKRsUHiyExq2ExERKRsUnuyErrYTEREpGxSe7ISG7URERMoGhSc7oWE7ERGRskHhyU5o2E5ERKRsUHiyE3q2nYiISNmg8GQnNGwnIiJSNig82QkN24mIiJQNCk92QlfbiYiIlA0KT3ZCw3YiIiJlg8KTnXBUeBIRESkTFJ7shHqeREREygaFJzuRfcK45jyJiNx+Jk+ejMlkYvPmzVbLTSYTHTt2LPZ2StKwYcMwmUycOXPmlu3DlhSe7ISDA5hMmf9Wz5OISNkTEhKCyWRi8eLF+Za7evUqrq6uVKxYkdTU1FJqXcmaP38+JpOJ+fPn27opNqHwZEeyhu4UnkREyp6RI0cC8MUXX+RbbsGCBaSmpvL444/j4uJS7P0ePnyYL7/8stjbKUlvv/02hw8fpnr16rZuyi3hZOsGSOE5OUJauobtRETKoi5dulCzZk3Wr19PREQEgYGBuZbLCldZYau4goODS2Q7JalatWpUq1bN1s24ZdTzZEey5j2p50lEpOwxmUwMHz4cs9lMaGhormV2797Nvn37aNWqFX5+fkyaNIk2bdpQuXJlXF1dqVmzJs888wxRUVFF2m9uc54iIiIICQnBz88PLy8vOnTowM8//5zrNlJTU5k9ezY9evQgMDAQV1dXKleuTP/+/fnjjz+syg4bNozhw4cDMHz4cEwmk+WVvUxec55CQ0Np06YNXl5eeHl50aZNm1zP1+bNmzGZTEyePJk9e/bQo0cPvL298fX1pV+/fjadT6XwZEc0bCciUrYNHz4cBwcH5s+fj2EYOdZn73X6+eefmTFjBlWqVCEkJITnnnuO2rVr89FHH9G2bVtiYmJuuh1//vknbdu2ZfHixbRq1Yrnn38ePz8/unXrxrZt23KUj46OZuzYsaSkpNC7d2/++c9/0rFjR1atWsU999zDzp07LWUfeughHnzwQQAefPBBJk2aZHkV5J///CfDhg3j/PnzjBw5kieffJILFy4wbNgwXnzxxVzr7Nq1i/vuuw8nJyeefvpp7r77br799lu6du1KcnLyTZ6hYjKkRMXExBiAERMTU+LbrviAYdDBMGqHlPimRUSKLSkpyTh06JCRlJRk66bYVI8ePQzA2Lx5s9Xy5ORko3z58oaHh4cRExNjXLp0yYiLi8tRPzQ01ACMadOmWS2fNGmSARibNm2yWg4YHTp0sFo2dOjQXLfx8ccfG0CO7SQnJxvnz5/P0Zbw8HDDy8vL6Nq1q9XyL774wgCML774ItdzkLX/06dPW5b9/PPPBmA0aNDAuH79umX59evXjeDgYAMwfvnlF8vyTZs2Wdq6ePFiq+0//vjjBmCEhYXluv8bFea9WZTPb815siMathMRe9Y6ZgQXzdG2bka+qjr4sd13XrG2MWLECNauXcu8efPo0KGDZfmKFSu4du0aQ4cOxcfHBx8fn1zrP/744zz33HOsX7+e119/vcj7T01N5euvv6Zy5cq89NJLVuuefPJJZsyYwbFjx6yWu7q65jq5u1GjRnTq1Im1a9eSlpaGs7NzkduTJevKvMmTJ+Pr62tZ7uvry6RJkwgJCWH+/Pm0a9fOql779u0ZNGiQ1bIRI0bw1VdfsXPnTgYPHnzTbbpZCk92RMN2ImLPLpqjuWBctnUz8lcCF+Q89NBDVKhQgaVLl/Lhhx/i7e0NwLx5maFsxIgRlrLLly/n448/Zs+ePVy7do2MjL/+wEdGRt7U/o8ePUpycjKdO3fGzc3Nap2DgwP33HNPjvAEsHfvXt59911+/fVXLl68SFpamtX6K1euFGsSeNbcqdzmZ2Ut27t3b451d955Z45lAQEBAFy/fv2m21McCk92JCs86Wo7EbFHVR38SiSc3EpVHfyKvQ0XFxcee+wxZs2axZIlSxg5ciQRERFs2LCBunXr0r59ewBmzJjByy+/TKVKlejevTsBAQG4u7sDMHPmTFJSUm5q/1lzpSpXrpzr+ipVquRYtnXrVjp37gxA9+7dqVu3Ll5eXphMJr799lv27dt30+3JEhsbi4ODA5UqVcq1TQ4ODrnO88reS5XFySkzvmQPm6VJ4cmOOKrnSUTsWHGHw+zJyJEjmTVrFvPmzWPkyJHMnz8fs9ls6XVKT09n6tSp+Pv7s3fvXqtAYRgG77777k3vOyts5HXF3qVLl3Ise+utt0hJSeHXX3/l3nvvtVq3bds29u3bd9PtyeLj44PZbOby5cs5gl1UVBRmsznPocyyRlfb2REN24mI2IcmTZrQsmVLtm7dypEjR5g/fz6Ojo4MHToUyBwCi4mJoU2bNjl6Ynbt2kVSUtJN77t+/fq4ubmxa9euHFejmc1mtm7dmqPOyZMn8fPzyxGcEhMT2bNnT47yjv/7Nl+Unp8WLVoA5PpYmC1btgDQvHnzQm/PlhSe7IiG7URE7EfWTTCffPJJTp06Re/evS1zhipXroy7uzt79uwhMTHRUufatWs899xzxdqvi4sLjzzyCFFRUcyYMcNq3WeffZbrfKegoCCuXbvGwYMHLcsyMjJ4+eWXuXw55zw1P7/M4c3z588Xul1ZwXHKlCnExsZalsfGxjJlyhSrMmWdhu3siK62ExGxHyEhIbz44ov89ttvgPUdxR0cHHjmmWeYMWMGzZo1o2/fvsTGxrJ69WqCgoLw9/cv1r6nT5/Ohg0beOONN/j1119p0aIFhw8fZtWqVXTv3p2ffvrJqvxzzz3HTz/9RLt27XjkkUdwc3Nj8+bNXLhwgY4dO+boLWrbti3u7u7MnDmT2NhYS+/Zq6++mmeb2rdvz3PPPcfs2bNp3LgxDz/8MIZhsHz5ciIiInj++ect88HKOvU82REN24mI2A8fHx8GDBgAZE6Ivv/++63Wv/3227z11luYTCbmzp3LunXrGDx4MD/99FOxbgkAmY9H2bp1K4MGDWLbtm3MmjWLq1evsm7dOtq2bZujfJ8+fVi6dCm1atViwYIFLFq0iODgYHbs2EFQUFCO8n5+fixdupS6devy0UcfMX78eMaPH19gu/7v//6PefPmUbVqVT755BM+/fRTqlatyrx585g1a1axjrk0mQwjl1ugyk2LjY3F19eXmJiYEp/41uofsPMIODhAxsYS3bSISLElJydz+vRp7rjjjhyXyIvYUmHem0X5/FbPkx3JGrYzmzNfIiIiUvrsIjzFx8czduxY/P39cXNzo3nz5ixevLjAeufPn2fs2LF06NCBcuXKYTKZLHc4zU9SUhL16tXDZDLx/vvvl8ARlIysYTvQpHERERFbsYvw1L9/f0JDQ5k0aRKrV6+mZcuWhISEsGjRonzrnThxgoULF+Li4kLv3r0Lvb8JEyaQkJBQ3GaXOKvwpHlPIiIiNlHmr7ZbtWoV69atY9GiRYSEhADQqVMnzp49y7hx4xg0aJDlfhM3at++veUSy127dhEWFlbg/nbs2MHs2bNZuHAhAwcOLLkDKQGO2aKuJo2LiIjYRpnveVqxYgVeXl45gszw4cOJjIxk+/btedZ1cCja4aWmpjJixAjGjBnD3XfffVPtvZWy9zwpPImIiNhGmQ9P4eHhNGjQwPIcmyxNmza1rC8pb775JgkJCUydOrXEtlmSNOdJRETE9sr8sN3Vq1epVatWjuVZdze9evVqiewn62nSP/zwA56enrneUTU3KSkpVg9LzH7X1JLmqJ4nEbEDugOOlDUl/Z4s8z1PACaT6abWFVZ6ejojRoxg0KBB9OjRo0h13377bXx9fS2vwMDAYrcnLxq2E5GyLGv+aVpamo1bImIt6z2Z1xzpoirz4alChQq59i5FR0cDf/VAFcfMmTM5deoUkyZN4vr161y/ft3Sg5ScnMz169fzfPjh+PHjiYmJsbwiIiKK3Z68aNhORMoyZ2dnXF1diYmJUe+TlBmGYRATE4Orq2ux79yepcwP2zVp0oSwsDDS09Ot5j0dOHAAgMaNGxd7H+Hh4cTExFC3bt0c6yZMmMCECRP4448/cn3as6urK66ursVuQ2HoajsRKesqVqzIhQsXOH/+PL6+vjg7O5fICIFIURmGQVpaGjExMcTHx1O9evUS23aZD0/9+vXj008/ZdmyZQwaNMiyPDQ0FH9/f1q3bl3sfbz66qsMGzbMatnFixcJCQnhH//4B4MGDaJOnTrF3k9xadhORMq6rMdaXLlyhQsXLti4NSKZnRzVq1cv0Uemlfnw1KtXL7p168bo0aOJjY2lTp06hIWFsWbNGhYsWGAZvxw5ciShoaGcPHnS6iGGS5cuBeDUqVNA5v2evLy8ACwPbAwODiY4ONhqv2fOnAGgdu3adOzY8VYeYqFp2E5E7IGPjw8+Pj6kpaXlOeVBpDQ4OjqW2FBddmU+PAEsX76c119/nYkTJxIdHU1wcDBhYWEMHjzYUiYjI4OMjIwc4+w33h9qzpw5zJkzB7C/K0I0bCci9sTZ2fmWfHCJ2JrJsLcEUcYV5anMRTX6A/jv95n/3vMptMg5RUtERERuQlE+v8v81XbyFz3bTkRExPYUnuyIhu1ERERsT+HJjuhqOxEREdtTeLIjutpORETE9hSe7IiebSciImJ7Ck92RMN2IiIitqfwZEc0bCciImJ7Ck92RD1PIiIitqfwZEd0qwIRERHbU3iyIxq2ExERsT2FJzuiYTsRERHbU3iyIxq2ExERsT2FJzuinicRERHbU3iyI3owsIiIiO0pPNkRDduJiIjYnsKTHdGwnYiIiO0pPNkR3apARETE9hSe7IgeDCwiImJ7Ck92RMN2IiIitqfwZEc0bCciImJ7Ck92RFfbiYiI2J7Ckx3RsJ2IiIjtKTzZEQ3biYiI2J7Ckx3RsJ2IiIjtKTzZEQ3biYiI2J7Ckx3Rs+1ERERsT+HJjmjYTkRExPYUnuyIhu1ERERsT+HJjuhqOxEREdtTeLIjeradiIiI7Sk82REN24mIiNiewpMd0bCdiIiI7Sk82RFdbSciImJ7dhGe4uPjGTt2LP7+/ri5udG8eXMWL15cYL3z588zduxYOnToQLly5TCZTMyfPz9HudjYWN566y06duxI1apV8fLyokmTJrzzzjskJyffgiO6ORq2ExERsT27CE/9+/cnNDSUSZMmsXr1alq2bElISAiLFi3Kt96JEydYuHAhLi4u9O7dO89y586dY+bMmdx555188sknfP/99wwYMIDJkyfTp08fDMMo6UO6KRq2ExERsT0nWzegIKtWrWLdunUsWrSIkJAQADp16sTZs2cZN24cgwYNwjH7ZWjZtG/fnsuXLwOwa9cuwsLCci13xx13cObMGTw9PS3LOnfujKenJ+PGjeO3336jXbt2JXxkRadhOxEREdsr8z1PK1aswMvLi4EDB1otHz58OJGRkWzfvj3Pug4OhTs8T09Pq+CUpVWrVgBEREQUocW3jobtREREbK/Mh6fw8HAaNGiAk5N1J1nTpk0t62+VjRs3AtCoUaNbto+i0LPtREREbK/MD9tdvXqVWrVq5Vju5+dnWX8r7N+/n3fffZd+/fpZglpuUlJSSElJsfx/bGzsLWkPaNhORESkLCjzPU8AJpPpptbdrDNnztCnTx8CAwP57LPP8i379ttv4+vra3kFBgaWeHuyaNhORETE9sp8eKpQoUKuvUvR0dHAXz1QJeXs2bN06tQJJycnNmzYUOD2x48fT0xMjOV1K+dHOepqOxEREZsr8+GpSZMmHD58mPT0dKvlBw4cAKBx48Yltq+zZ8/SsWNHDMNg06ZNBAQEFFjH1dUVHx8fq9etYjJB1hx49TyJiIjYRpkPT/369SM+Pp5ly5ZZLQ8NDcXf35/WrVuXyH7OnTtHx44dycjIYOPGjQQFBZXIdkta1tCdwpOIiIhtlPkJ47169aJbt26MHj2a2NhY6tSpQ1hYGGvWrGHBggWWezyNHDmS0NBQTp48aRV8li5dCsCpU6eAzPs9eXl5ATBgwAAAoqKi6NSpE3/++Seff/45UVFRREVFWbYREBBQqF6o0uDkCKlpGrYTERGxlTIfngCWL1/O66+/zsSJE4mOjiY4OJiwsDAGDx5sKZORkUFGRkaOu4HfeH+oOXPmMGfOHABL2UOHDlnC1WOPPZZj/5MmTWLy5MkleUg3zVHDdiIiIjZlMsrKs0f+JmJjY/H19SUmJuaWzH/y6wvX4qBuABxbUOKbFxERuS0V5fO7zM95EmtZc540bCciImIbCk92RsN2IiIitqXwZGd0tZ2IiIhtKTzZGcuwncKTiIiITSg82RkN24mIiNiWwpOd0bCdiIiIbSk82RldbSciImJbCk92xlE9TyIiIjal8GRnNGwnIiJiWwpPdkbhSURExLYUnuyMY7afmFnznkREREqdwpOdyep5AvU+iYiI2ILCk51ReBIREbEthSc7k33YTrcrEBERKX0KT3ZGPU8iIiK2pfBkZxSeREREbEvhyc5YDdspPImIiJQ6hSc7o54nERER21J4sjMKTyIiIral8GRnHLOFJ11tJyIiUvoUnuyMep5ERERsS+HJzig8iYiI2JbCk51x0rCdiIiITSk82ZnstypQz5OIiEjpU3iyMxq2ExERsS2FJzujYTsRERHbUniyMxq2ExERsS2FJzujYTsRERHbUniyM1bDdgpPIiIipU7hyc5o2E5ERMS2FJ7sjIbtREREbEvhyc7oajsRERHbsovwFB8fz9ixY/H398fNzY3mzZuzePHiAuudP3+esWPH0qFDB8qVK4fJZGL+/Pl5ll+/fj1t27bFw8ODihUrMmzYMKKiokrwSIrPUT1PIiIiNmUX4al///6EhoYyadIkVq9eTcuWLQkJCWHRokX51jtx4gQLFy7ExcWF3r1751t2y5Yt9OrViypVqvDdd98xa9Ys1q9fT5cuXUhJSSnJwykWDduJiIjYlpOtG1CQVatWsW7dOhYtWkRISAgAnTp14uzZs4wbN45BgwbhmL07Jpv27dtz+fJlAHbt2kVYWFie+xk3bhz16tVj6dKlODllnpY77riDe++9l3nz5jF69OgSPrKbo2E7ERER2yrzPU8rVqzAy8uLgQMHWi0fPnw4kZGRbN++Pc+6Dg6FO7wLFy6wc+dOHn/8cUtwArjnnnuoV68eK1asuLnG3wK62k5ERMS2ynx4Cg8Pp0GDBlahBqBp06aW9SWxj+zbvHE/JbGPkqJhOxEREdsq88N2V69epVatWjmW+/n5WdaXxD6yb/PG/eS3j5SUFKs5UbGxscVuT340bCciImJbZb7nCcBkMt3UupLaT377ePvtt/H19bW8AgMDS6w9udGwnYiIiG2V+fBUoUKFXHt+oqOjgdx7i25mH5B7L1Z0dHS++xg/fjwxMTGWV0RERLHbkx8N24mIiNhWmQ9PTZo04fDhw6Snp1stP3DgAACNGzcu9j6ytpG1zRv3k98+XF1d8fHxsXrdSnq2nYiIiG2V+fDUr18/4uPjWbZsmdXy0NBQ/P39ad26dbH3Ub16dVq1asWCBQvIyJZItm3bxtGjR+nfv3+x91FSNGwnIiJiW2V+wnivXr3o1q0bo0ePJjY2ljp16hAWFsaaNWtYsGCB5R5PI0eOJDQ0lJMnTxIUFGSpv3TpUgBOnToFZN7vycvLC4ABAwZYyr3zzjt069aNgQMH8swzzxAVFcWrr75K48aNGT58eGkdboE0bCciImJbtzQ8nTt3jrCwMCIjI7nzzjt5/PHHC33vpeyWL1/O66+/zsSJE4mOjiY4OJiwsDAGDx5sKZORkUFGRgaGYVjVvfH+UHPmzGHOnDkAVmU7duzIqlWrmDhxIn379sXDw4M+ffrw3nvv4erqWuQ23yq62k5ERMS2TMaNaaOIPvroI15//XUmT57M888/b1m+bds2evToQXx8PIZhYDKZ6Ny5M2vXrr2pAGUvYmNj8fX1JSYm5pbMf1q9HXr/K/Pfk4fBpGElvgsREZHbTlE+v4udYr7//ntiY2NzzAt68cUXiYuL45577mHs2LFUq1aNjRs3FuqBvpI3DduJiIjYVrHD05EjR6hUqRIBAQGWZadPn2bbtm00aNCAn3/+mQ8++IA1a9ZgGAafffZZcXd5W9OwnYiIiG0VOzxdvnzZKjgBbNq0CYDBgwdbbjDZuHFj6tSpw4kTJ4q7y9uarrYTERGxrWKHp4yMDJKTk62W/fLLL5hMJjp06GC13M/Pj8uXLxd3l7c1DduJiIjYVrHDU82aNTlx4gTXr18HMsPUmjVrcHNzo23btlZlC7pbtxRMw3YiIiK2VezwdP/995OSksKjjz7KypUrGTVqFJcuXeL+++/H2dnZUi4mJoZTp05Z3YNJik7DdiIiIrZV7Ps8vfbaa3z77besWbOGtWvXYhgGvr6+TJ061arcsmXLMJvNdOrUqbi7vK1p2E5ERMS2ih2e/Pz82LNnD5999hnHjx8nMDCQ4cOHU61aNatyp06d4sEHH+Thhx8u7i5va3q2nYiIiG2VyB3GfXx8ePHFF/MtM23atJLY1W1Pw3YiIiK29fe91ffflIbtREREbKvY4SkyMpLvv/+e8PBwq+WGYfDBBx/QoEEDfH196dy5M3v37i3u7m57Ck8iIiK2VezwNGvWLPr168ehQ4esln/wwQeMGzeOo0ePEhcXx+bNm+nSpQtRUVHF3eVtzVG3KhAREbGpYoenDRs24OLiwkMPPWRZlpGRwbvvvouDgwP//e9/2bt3L48++ijXrl1j5syZxd3lbU09TyIiIrZV7PB04cIFqlevjouLi2XZtm3buHz5Mvfffz+jRo2iadOmfPzxx3h4eLB69eri7vK2pvAkIiJiW8UOT9HR0VSsWNFqWdbjWfr06WNZ5unpSd26dTl79mxxd3lby361nYbtRERESl+xw5OHhweXLl2yWrZ582YA2rdvb7Xc2dmZtLS04u7ytqaeJxEREdsqdnhq0qQJ586dY9u2bQBERESwadMmqlevTr169azKnj17lipVqhR3l7c1hScRERHbKnZ4evLJJzEMg969ezNgwADuuece0tPTefLJJ63KHT58mMuXL9O4cePi7vK2pmE7ERER2yp2eHriiSd48cUXiY2NZfny5Vy4cIEBAwbw6quvWpX74osvAOjWrVtxd3lbU8+TiIiIbZkMwzBKYkNXrlzh5MmTBAYG4u/vn2P9xo0biYuL47777sPPz68kdlkmxcbG4uvrS0xMDD4+PiW+fcMAh/89W7lVA9j+UYnvQkRE5LZTlM/vEnm2HUDFihVzXHWXXefOnUtqV7c1kylz6C7DrAcDi4iI2EKJhacsSUlJnDx5kri4OLy9valduzbu7u4lvZvbmpNjZnjSsJ2IiEjpK7EHA69du5aOHTvi6+tLs2bNaNeuHc2aNbM81+6nn34qqV3d9rLmPSk8iYiIlL4SCU+TJ0+md+/e/Pzzz6Snp+Ps7Iy/vz/Ozs6kp6ezefNmevXqxeTJk0tid7e9rOfb6Wo7ERGR0lfs8LRmzRrefPNNHBwceOaZZzh69CjJyclERESQnJzM0aNHeeaZZ3B0dGTq1KmsXbu2JNp9W1PPk4iIiO0UOzz93//9HyaTiXnz5vHhhx9St25dq/V169blww8/ZN68eRiGwaxZs4q7y9uewpOIiIjtFPtWBZUqVcLDw6NQz6wLCgoiISGBK1euFGeXZdqtvlUBgP/D8OdVCKwM55bckl2IiIjcVory+V3snqe4uLhCP3KlSpUqJCQkFHeXtz31PImIiNhOscOTv78/R44cKTAUJSQkcPjwYapVq1bcXd72FJ5ERERsp9jhqUePHsTHx/PUU0+Rmpqaa5nU1FSefPJJEhMT6dmzZ3F3edvLer6drrYTEREpfcWe8xQREUGzZs2IiYmhSpUqPPXUUzRs2JDKlSsTFRXFoUOH+PTTT7l06RK+vr7s27ePwMDAkmp/mVMac54aPAFHzoGPJ8T8eEt2ISIiclsp1cezBAYGsnr1ah555BEiIiKYNm1ajjKGYVCjRg2WLFnytw5OpUXDdiIiIrZTIjfJbN26NUeOHOHTTz9lwIABNG3alFq1atG0aVMGDBjAZ599xuHDh/Hy8mL//v1F3n58fDxjx47F398fNzc3mjdvzuLFiwtVNyoqimHDhlGxYkU8PDxo27YtGzZsyFEuJSWF9957j8aNG+Pp6UmVKlXo1asXW7duLXJ7bzXLsJ3Ck4iISKkrsWfbubu7M3LkSEaOHJlnmQ4dOnDt2jXS09OLtO3+/fuzc+dOpk+fTr169Vi0aBEhISGYzWYeffTRPOulpKTQpUsXrl+/zqxZs6hcuTJz5syhZ8+erF+/ng4dOljKPvXUUyxcuJDx48fTuXNnoqOjmT59Oh06dOC3336jVatWRWrzraSeJxEREdsp9pynoqhUqRLR0dFkFKHLZNWqVdx///2WwJSle/fuHDx4kHPnzuGY9bySG8ydO5cxY8awdetW2rZtC0B6ejrNmjXDy8uL7du3A5khy9PTk5CQEL766itL/T///BN/f3+ef/75Qt/cszTmPLUZDdsPZ/7bvAlMpluyGxERkdtGqd7n6VZbsWIFXl5eDBw40Gr58OHDiYyMtASgvOrWr1/fEpwAnJyceOyxx9ixYwcXLlwAwMHBAQcHB3x9fa3q+/j44ODggJubWwkeUfFlz4pmXXEnIiJSqsp8eAoPD6dBgwY4OVmPMDZt2tSyPr+6WeVyq3vw4EEAnJ2deeaZZwgNDeXbb78lNjaWM2fO8NRTT+Hr68tTTz1VUodTIpyyhScN3YmIiJSuEpvzdKtcvXqVWrVq5Vju5+dnWZ9f3axyBdX9z3/+g6+vLw8//DDm/3Xn1KhRg40bN1KnTp0895GSkkJKSorl/2NjYws4ouK7MTy53vI9ioiISJYy3/MEYMpnUk9+64pS96233uL9999n8uTJbNq0ie+++4769evTrVs3/vjjjzy38fbbb+Pr62t5lcatGLKHJ90oU0REpHSV+fBUoUKFXHuXoqOjAXLtWSpq3cOHDzNx4kSmTJnChAkT6NixIw888AA//vgj5cqV48UXX8xzH+PHjycmJsbyioiIKNLx3QzHbD81DduJiIiUriIP23355Zc3vbPsw1uF1aRJE8LCwkhPT7ea93TgwAEAGjdunG/drHLZ3Vh33759GIZBy5Ytrco5OzvTrFkztmzZkuc+XF1dcXUt3YEzzXkSERGxnSKHp2HDhhU4VJYXwzCKXLdfv358+umnLFu2jEGDBlmWh4aG4u/vT+vWrfOt+8wzz7B9+3ZLufT0dBYsWEDr1q3x9/cHsPx327ZtVvd+SklJYc+ePQQEBBSpzbeahu1ERERsp8jhqUaNGjcdnm5Gr1696NatG6NHjyY2NpY6deoQFhbGmjVrWLBggeUeTyNHjiQ0NJSTJ08SFBQEwIgRI5gzZw4DBw5k+vTpVK5cmblz53L06FHWr19v2Ue7du1o2bIlkydPJjExkfbt2xMTE8Ps2bM5ffq01b2fygIN24mIiNhOkcPTmTNnbkEz8rd8+XJef/11Jk6cSHR0NMHBwYSFhTF48GBLmYyMDDIyMsh+z09XV1c2bNjAK6+8wnPPPUdiYiLNmzdn9erVVj1MDg4OrFu3jvfee49vvvmG999/Hy8vLxo2bMiqVavo1atXqR5vQTRsJyIiYjuleofx20Fp3GH88bdgwbrMfx9fAHXK1qiiiIiI3flb3WFcctKwnYiIiO0oPNkhDduJiIjYjsKTHdLVdiIiIraj8GSHHNXzJCIiYjMKT3ZIw3YiIiK2o/BkhzRsJyIiYjsKT3ZIV9uJiIjYjsKTHdKwnYiIiO0oPNkhhScRERHbUXiyQ9mH7TTnSUREpHQpPNkh9TyJiIjYjsKTHVJ4EhERsR2FJztkNWyn8CQiIlKqFJ7skHqeREREbEfhyQ4pPImIiNiOwpMdctQdxkVERGxG4ckOqedJRETEdhSe7JDCk4iIiO0oPNkh3SRTRETEdhSe7JB6nkRERGxH4ckOKTyJiIjYjsKTHdKwnYiIiO0oPNkh9TyJiIjYjsKTHVJ4EhERsR2FJzukZ9uJiIjYjsKTHVLPk4iIiO0oPNkhhScRERHbUXiyQ3q2nYiIiO0oPNkh9TyJiIjYjsKTHVJ4EhERsR2FJzukm2SKiIjYjsKTHVLPk4iIiO0oPNkhhScRERHbsYvwFB8fz9ixY/H398fNzY3mzZuzePHiQtWNiopi2LBhVKxYEQ8PD9q2bcuGDRtyLZuQkMDEiROpV68erq6uVKhQgU6dOnH8+PGSPJxi07CdiIiI7TjZugGF0b9/f3bu3Mn06dOpV68eixYtIiQkBLPZzKOPPppnvZSUFLp06cL169eZNWsWlStXZs6cOfTs2ZP169fToUMHS9n4+Hg6depEZGQkr776Kk2bNiUmJoatW7eSmJhYGodZaOp5EhERsZ0yH55WrVrFunXrLIEJoFOnTpw9e5Zx48YxaNAgHLPf+Cibzz//nPDwcLZu3Urbtm0tdZs1a8Yrr7zC9u3bLWXfeOMNDh8+zP79+6lVq5Zl+QMPPHALj+7mKDyJiIjYTpkftluxYgVeXl4MHDjQavnw4cOJjIy0CkC51a1fv74lOAE4OTnx2GOPsWPHDi5cuABAYmIin332GQMHDrQKTmWVnm0nIiJiO2U+PIWHh9OgQQOcnKw7yZo2bWpZn1/drHK51T148CAAu3fvJiEhgbp16zJ69GjKly+Pi4sLd999Nz/++GO+7UtJSSE2Ntbqdaup50lERMR2ynx4unr1Kn5+fjmWZy27evVqsetm9UC98847HDhwgC+//JIVK1bg4+ND3759Wbt2bZ77ePvtt/H19bW8AgMDC39wN0nhSURExHbKfHgCMJlMN7WusHXN5sxL1lxcXFi9ejV9+/bl/vvvZ+XKlVSrVo2pU6fmuY3x48cTExNjeUVEROTbnpKgZ9uJiIjYTpmfMF6hQoVce5eio6MBcu1ZKmrdChUqAHDPPffg7e1tKefh4UGHDh349ttv89yHq6srrq6uBR9ICVLPk4iIiO2U+Z6nJk2acPjwYdLT062WHzhwAIDGjRvnWzerXH51c5sXlcUwDBwcytZpUngSERGxnbKVCnLRr18/4uPjWbZsmdXy0NBQ/P39ad26db51jxw5YnVFXnp6OgsWLKB169b4+/sDUK1aNdq2bctvv/1mNeE7MTGRLVu20KZNmxI+quLRTTJFRERsp8yHp169etGtWzdGjx7Np59+yqZNmxg1ahRr1qzh3XfftdzjaeTIkTg5OXH27FlL3REjRtCoUSMGDhzIokWLWL9+PY888ghHjx7lnXfesdrP+++/T1xcHD169ODbb7/lu+++o2fPnly5ciXfOU+2oJ4nERER2ynz4Qlg+fLlPP7440ycOJGePXuyfft2wsLCGDJkiKVMRkYGGRkZGIZhWebq6sqGDRvo1KkTzz33HH379uXPP/9k9erVVncXh8z5Ths2bMDV1ZUhQ4bw6KOP4uzszObNm63uE1UWODhA1jx4hScREZHSZTKypw0pttjYWHx9fYmJicHHx+eW7ce5S2ZwurMe7P7klu1GRETktlCUz2+76HmSnLKG7tTzJCIiUroUnuyUwpOIiIhtKDzZqawr7vRsOxERkdKl8GSn1PMkIiJiGwpPdkrhSURExDYUnuxUVnjSTTJFRERKl8KTnXJUz5OIiIhNKDzZKQ3biYiI2IbCk51SeBIREbENhSc7ZblVgeY8iYiIlCqFJzulnicRERHbUHiyUwpPIiIitqHwZKc0bCciImIbCk92KqvnyWzOfImIiEjpUHiyU1nhCdT7JCIiUpoUnuyUY7afnB4OLCIiUnoUnuxU9p4nTRoXEREpPQpPdkrhSURExDYUnuyUo+Y8iYiI2ITCk51Sz5OIiIhtKDzZKYUnERER21B4slNWV9tp2E5ERKTUKDzZKfU8iYiI2IbCk51SeBIREbENhSc7pWE7ERER21B4slPqeRIREbENhSc7pfAkIiJiGwpPdkrPthMREbENhSc7pZ4nERER21B4slMKTyIiIrah8GSn9Gw7ERER21B4slPqeRIREbENuwhP8fHxjB07Fn9/f9zc3GjevDmLFy8uVN2oqCiGDRtGxYoV8fDwoG3btmzYsCHfOklJSdSrVw+TycT7779fEodQ4hSeREREbMPJ1g0ojP79+7Nz506mT59OvXr1WLRoESEhIZjNZh599NE866WkpNClSxeuX7/OrFmzqFy5MnPmzKFnz56sX7+eDh065FpvwoQJJCQk3KrDKRG6SaaIiIhtlPnwtGrVKtatW2cJTACdOnXi7NmzjBs3jkGDBuGYfQJQNp9//jnh4eFs3bqVtm3bWuo2a9aMV155he3bt+eos2PHDmbPns3ChQsZOHDgrTuwYlLPk4iIiG2U+WG7FStW4OXllSPIDB8+nMjIyFwDUPa69evXtwQnACcnJx577DF27NjBhQsXrMqnpqYyYsQIxowZw913312yB1LCFJ5ERERso8yHp/DwcBo0aICTk3UnWdOmTS3r86ubVS63ugcPHrRa/uabb5KQkMDUqVOL2+xbTsN2IiIitlHmh+2uXr1KrVq1ciz38/OzrM+vbla5guru3buXd999lx9++AFPT08uX75cqPalpKSQkpJi+f/Y2NhC1Ssu9TyJiIjYRpnveQIwmUw3ta6wddPT0xkxYgSDBg2iR48eRWrb22+/ja+vr+UVGBhYpPo3S+FJRETENsp8eKpQoUKuvUvR0dEAufYsFbXuzJkzOXXqFJMmTeL69etcv37d0oOUnJzM9evXycjjAXLjx48nJibG8oqIiCjaAd4kPdtORETENsp8eGrSpAmHDx8mPT3davmBAwcAaNy4cb51s8rlVzc8PJyYmBjq1q1L+fLlKV++PM2aNQMyb1tQvnz5XLcD4Orqio+Pj9WrNKjnSURExDbKfHjq168f8fHxLFu2zGp5aGgo/v7+tG7dOt+6R44csboiLz09nQULFtC6dWv8/f0BePXVV9m0aZPVKywsDIB//OMfbNq0iTp16tyCo7t5Ck8iIiK2UeYnjPfq1Ytu3boxevRoYmNjqVOnDmFhYaxZs4YFCxZY7vE0cuRIQkNDOXnyJEFBQQCMGDGCOXPmMHDgQKZPn07lypWZO3cuR48eZf369ZZ9BAcHExwcbLXfM2fOAFC7dm06duxYKsdaFHq2nYiIiG2U+fAEsHz5cl5//XUmTpxIdHQ0wcHBhIWFMXjwYEuZjIwMMjIyMAzDsszV1ZUNGzbwyiuv8Nxzz5GYmEjz5s1ZvXp1nncXtxfqeRIREbENk5E9bUixxcbG4uvrS0xMzC2d//TNZnhkcua/3x8NLw26ZbsSERH52yvK53eZn/MkudNNMkVERGxD4clOadhORETENhSe7JTCk4iIiG0oPNkpDduJiIjYhsKTnVLPk4iIiG0oPNkphScRERHbUHiyU9mH7RSeRERESo/Ck53K3vOkBwOLiIiUHoUnO6VhOxEREdtQeLJTjgpPIiIiNqHwZKec9GBgERERm1B4slMathMREbENhSc7pavtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU5p2E5ERMQ2FJ7slIbtREREbEPhyU7p2XYiIiK2ofBkpzRsJyIiYhsKT3ZK4UlERMQ2FJ7slKOuthMREbEJhSc7ZTKBw/9+eup5EhERKT0KT3Ysa+hO4UlERKT0KDzZEbNh5nRGpOX/s25XoGE7ERGR0qPwZCd2pR/m3tin6RQ3hngjEVDPk4iIiC0oPNmJN5PmsTPjEOfNUbyd9CWg8CQiImILCk92YobH87jgDMAHyWEcyzinYTsREREbUHiyE3UdA3nRLQSANNIZmzgTR0cj8//TbdkyERGR24vCkx0Z7/4EAQ6VAfgpbTsubX8FICIKfthqy5aJiIjcPhSe7IinyZ33PZ6z/H/ykFngkgLA0Lfh3CVbtUxEROT2YRfhKT4+nrFjx+Lv74+bmxvNmzdn8eLFhaobFRXFsGHDqFixIh4eHrRt25YNGzZYlYmNjeWtt96iY8eOVK1aFS8vL5o0acI777xDcnLyrTikm/awcyc6Od0FQLTbnzR8YSEA1+IgZKqG8ERERG41uwhP/fv3JzQ0lEmTJrF69WpatmxJSEgIixYtyrdeSkoKXbp0YcOGDcyaNYvvvvuOKlWq0LNnT7Zs2WIpd+7cOWbOnMmdd97JJ598wvfff8+AAQOYPHkyffr0wTCMW32IhWYymZjl+U+cyLzU7mzbrwholHnvp63hMHGeLVsnIiLy92cyylIyyMWqVau4//77WbRoESEhIZbl3bt35+DBg5w7dw7H7A96y2bu3LmMGTOGrVu30rZtWwDS09Np1qwZXl5ebN++HYCEhAQAPD09req///77jBs3jl9++YV27doVqr2xsbH4+voSExODj49PkY+3sMYlfsh/ksMAuCvlLnYPe5+MFBcAVr8DPVvfsl2LiIj87RTl87vM9zytWLECLy8vBg4caLV8+PDhREZGWgJQXnXr169vCU4ATk5OPPbYY+zYsYMLFy4AmaHpxuAE0KpVKwAiIiJK4lBK1AT34VQzVQBgt+tuGvzfG+CUBsDj/4bPVkJ8oi1bKCIi8vdU5sNTeHg4DRo0wMnJyWp506ZNLevzq5tVLre6Bw8ezHffGzduBKBRo0ZFanNp8DF5EuY1FQ/cADhc6TeqvTUBHNO5EgNPvQ9VH02k9U+hNLv0FA/GvsJ/khazJ/0oGUbmXTXPZVzky5RVjIifxp0xQ3k5cXaZGqIUEREpi5wKLmJbV69epVatWjmW+/n5WdbnVzerXFHr7t+/n3fffZd+/frlGsCypKSkkJKSYvn/2NjYPMuWtHbOzfje+z36xr1MEilE1f2FylMmEfXWa5i6f0fSQwvZ7XsdgIPp8GP6b5AEjkneuKZ4k1gu0mp7+zNO4HYlAH7qx7KfISYBBneG14ZAxXI33840Ix1nU5l/q4mIiBRKme95gsxJ0jez7mbrnjlzhj59+hAYGMhnn32W7/bffvttfH19La/AwMB8y5e0js538q33u7iROd/pasPNuC/og8PQOZj+F5xulOEelyM4ZXnbeTZvbTjHkXPw51X4zzdQewi8tcDg14QjHM44U+jeqTQjnafi38brWmfuj3uJ8PRTN3WMIiIiZUmZD08VKlTItYcoOjoaINeepeLUPXv2LJ06dcLJyYkNGzbku32A8ePHExMTY3nZYn5UF+e7We49Hdf/BahUUyoAJky0vNKN9vMW0XDWV1T45p847+yIEeuLkeaMcbA55q9HkPHGh5h/ejCzjmsKDi9MxeSYjkvm02CITUpngtN7dEwZSZOYIQRdfoTn4v7DurQdpBipubYpyUjh4fjxfJG6kgwyWJu2jTtjh/JMwrtcMkcX+RhjzPF8m7qFLWl/WIYdRUREbKHMj6U0adKEsLAw0tPTreY9HThwAIDGjRvnWzerXHZ51T179iwdO3bEMAw2b95MQEBAge1zdXXF1dW1UMdyK3V3bs1Sr3/zSPzrJJFCf+eOTHQfSWO/WvByVqlawAAyMgwOnjXYeNGBdWdhyylIOBmM0XgPJv8ITPUO8fKSL3nePII3FibwZeOJmO7cZtlXpFMkH6Ut5aO0pbikedAr8X7+XTmE+p5VAIgzEngo7l9sSf/Dqo1mzHyS8h1fJa6j97WHqZdWnxpGdQLN1fHEk4goOBkJpyLh1J/g7AgDesdzsc0SPkxbwnUjDoDKpvL0c+nAwy6dae/UDCcNCYqISCkq87cqWL16Nb1792bx4sUMGjTIsrxXr17s378/31sVfPTRRzzzzDNs27aN1q0zr91PT0+nefPmeHl5sW3bX4Hg3LlzdOjQgYyMDDZv3pzrPKvCKK1bFeQlynyNVNIsj3EpjNQ0Mofp/A7ygDGaDDJwxJGlXm/xZtI8/sg4BoCR5gxHG0P9A5icb7gbZ7ojlfb3pMf1B1nfbCYXKxzKrJPkjvn9aZhqHsf08JeYPHK/BNC45geRNTDOB8H5mhjngzDVPYyp72JM3nF5tt0pzR3HVHfM6Y4Y6U6Y05xwO34ndTaNJsDDmyrloVoFqFEZalbNfAVWBgcHuHwdLl2DiOg0TqVEcYdzFar4OFHRFyr4QjkvKGBU2GaSjBTWpe3AGSd6OrcpcPi6NGQYGVwz4vAz+eBgKtlO7WhzLEczzlHRwZeqDn54m3JeHWuP9qQf5dmE93EwOdDTuQ19nO+lmWPdMvHzFLndFOXzu8yHJ8i8p9OuXbt45513qFOnDmFhYXz66acsWLCAIUOGADBy5EhCQ0M5efIkQUFBQOZk7rvuuovY2FimT59O5cqVmTt3Lj/88APr16+nQ4cOQOZdyNu2bcuFCxf4/PPPqV27ttX+AwICCtULBbYPT8U1JfFzpibnvNNmOZM3sxKmE7e7OZuOJLDJ2MGVur9iarsZk1vud2E34rwxT/0ATjTMXOAbjWnwZ5i6/oDJ0VykdhkZjjj82g3DLQmj+e+YXHMfLrSUjwzA/N5bcLZOjnUmEximDGi4F1O79ZnH4B2LEeeDsb09xu+dYP/duDg4Ub0iBFT669X4Dri7PtQJyOAX4w/+SD/KVSOWq8Z1rppjiTUSaO3UiBfcHqGSQ/kCjyvVSGNt2jbOmS/RxbklwY5B1sdhwMkLsOMIHItMJ7X+Ho7V/IkNzluIIzOIjnTtyxyPl0u0B+7CZXBzyQyR+TEMg50Zh1mc8hNLUjdy0bhKfYcaPOf2CI+79sTT5F6sdkSaL/N+0kI+SfmOZP76mXviTlUHP8qbfPAyuf/v5YGfyZuOznfSzblVmQ9YK1N/Y0j8JBJIsloe4FCZPs738rzbI9RzrFHo7aUb6aSTgZvJ9j3hIrdSgpFEvJFEFYf8p9UU1d8uPMXHx/P666+zZMkSoqOjCQ4OZvz48QwePNhSZtiwYYSGhnL69Glq1qxpWX7p0iVeeeUVVq5cSWJiIs2bN2fq1Kl07drVUmbz5s106tQpz/1PmjSJyZMnF6qt9h6e0ox07ot9ml0ZRyzLajpU4wfv92ngWNOq7LlLsOrgdT4zvmF//WWYPf7qITKu+RH48Uy6VKxNq2BwdYaUNEhOhYuOFznjdYjL7ue56nGeaI/zXPOOINE951wok9kR8+aemL95Ai79L8C6JWK6ayumezZB0ElwTAendEyOGeAZD86ZH7JGiivGR//C+LlHZj3nFGi0F9Pdv2JquwVT+byvtjTivDF23wNHmmIcbQwRd4DZESpHYuq0CscuqzAq5v0wQed0d2rtexiXVSEkXy1H09rQsn5m8LqznsEJl+N8nriKb9LXEW26nnmshomWV7ty79GhmC7cwYFTsP2omWvVDmC6dyOmezbl2ea6UW158uibVHT1wMU5c8jzuttFVlYK45T7UcxOqWQ4pJLukEaGKY0ajlW5yzGYu52CudOpPnUdAjkd6cDXm2DxRjjwv7n9gfUvE9guHOdG4aRUiIAMJ4wUVzJSXEhNceRP/91Ee57PtU3lTN486foAg1y64mlywxknnE1OOOOEgYEZM/HJBqcumolLMOHu5ISHozOeTk4kusSw0GMRYawkhfyDcm5ccKaT8130cb6Xfi4dqOpQIddyGUYGn6V8T6yRyAMu7ajvGMSl6MywWtsfGgTdmt7Hj5KX80LifzCT95cIN1yY5vE0z7s+kmdPXoaRwZb0vSxO/YlvkjeTZqQzzvUJxns9iovJOdc6hmHckp6tNCOdS8Zfv8MmTDjiQGVT+UL3RKYaafyeHs7+jBOcN0dZXpfM0dzj1IQ5nuNwL+FwmGKk4mpyKdY2kowUNqbtJtixBrUdC/dFu6REma8xM3kxy1I30dqpEZPdn6SWY/VibzfDyOCo+Rx7049xwnyBdCMd8/9+b80Y1HMM5FGX7qUe1sPTTxESPwE/Bx82eM8u0S+Nf7vwZE/sPTwBHM04S8uYESSSzF2O9fnO+708P3yyxBoJvB/9LZ+lfYtnhhdfub9JG7+iXXl4zRzL4YyzHDGf4XDGWVxxZpjr/ThfDuDDFbB0C7i7QqOa/3vdAfUDoVI58PMGVxc4k/Enj8S/zp6Mo5btdojtztW0BI747ibdKWcvmXO6OzVjG3HG5yBpTkk51gMYiR5wyR/THSeKdExGkjvGlh6ACbxjMPlch4pRmKrlHjgADLMps/frWoXMXrEKl3OWSfDE2NsaU6tfMDln3hzVOFEf81vvg4M5c4i02/eWdQUxpblgjvWBBC9I9IYkD6h+FlPli4U+VlO6M+VigrhWoWjnqFDbTnXFY09n4lLTMwNk1ss9AZND/n/CHFLduGPZ61Q91hkXp8z3UNNa0KJRCvPrT2Y1P1vKel+qQ9yGzmRs7QjJHlQLiqPlXbE0ahxHbR9v0g81ZddhB3YegUNnIagKdG8J3e+GTi3A53+dXQlGEpHmK/xpvooTjvg5+FDe5E05kxcTkj7hg/89HQBgoEtnprg/xYa0XaxM+41NabtJ5a+fW7VLzan+9WuUT6xOj5bQoW08UZUPsj59J1+nrCfSyPn+cL9ck0eOjWNojeZ4ucOZpGusdF7DBp9VRLqfxslwxtVwxc1ww93kShWjIrVNNahnCqK+Uw3qu1bD08V6OkSgQ5Vcg4thGHyVupqXE2cTbeS8VYsHbjRwDKKh4x00cqxFLUd/PHDH0+Rm6Zncmr6fdWk72Zy2J0dPXHbtnVrwrfc7+OTRq2gYBn8aV9iXfoLf4k5wKi6WRzzb8VClZjnKRpov88+EWaxI20LLjOY8G/M8Fa/X5Vpc5nvk3sbg978/4THmeNxNrjkC6cmM8/w35Vvmp6zkmhGHKy781/MVHnftlWv70v7XO1gSAfCC+TIzkhbyacr3JPHX7XKcDCeecevP6+7DqOCQf9dxejpEx4GfbwZHzWfZmXGYXemH2ZN+lAMZJ622m5vqpkq84T6cOy/cz/YDTtxRDdo1gQy3ODan7+GM+U9SjXRSSSPVSMOMQRunxvRyboOjyfr9ZRgG2zMOsiRlA8GOQTzh2ssqmBmGwbyUH3gh8T+WXug33IYz2ePJop66PCk82dDfITxBZrrfm3GM/i4d8TC52bo5RZJspPB84gfMS1mZZxlXXOjl3IZBLl3p7XIPniZ3kowU1qZtY2nqJn5I/TXfP+KYHTD2tMHY2hnjSmWI8818OaZjejCs0MHFSHXB2HEfXKiBqeeKPG8vAeBkdqbOpbZ47uzO+TX3EHnRFRrtweHV8Zg84zO3F10BPONyDGsa6Y6Q5gLpzmB2yHc/RWGYTRB+J8Yv3TB+75gZvO44hun+JZjuW1/o8Jbn9pPcMdb0x/g+BGJyGwY1wCUF3JPANQmqn8N092+Zr0rWPYPmr4djLBkBhgN4xeIw/l+YGuwvWntO1se8aBT80RrI6r0xoO4hHLt9j3vTcMzlL5PinFCo7b3s8hj/9nwaB5MD5y7Bsp9hye8J7Gj2Gaa+S6zPw/b2mO44DoGncw2MRpI7uKRm9sBmHfOmnpjckqDlr5icineVqlu6B02PP0z5LYM4e6w8UdehVv3rXAt5j5M1Nhdr2wUyTGDKPOYGaQ1Y7TuDALfMYGAYBuvSdvB/icv4Pe0gMY7Xc1R32duO1n/8g04V78DVxWClxw/suHsOGe7xf+0iwwFjfV+MsKcgtjwmn+tUf3AD5vvWcKniIRwMB2o4VKWOY3XqOAZw2vwna9O25dgXwBjHwczwGW3pFfnTfIVpSV/wecoPOOBAk+RmVDjZlutb2hKxvwb+ta9S7q5wzHUOcrXSYTycHbnLaEobowV30QgfZ1cqls/gkPk029LD+Tl9L8tTN1uF7Bu5p3vxstujPOxxHw0da1p6/yKiYPWODBZHHOZ3l22k1t2LqfaRzN+hm2T8WR3j2yFQ7ioOLXZA3UPgmPf7LcChMgPT+1Juex+izvtyod569tRaxjnvv770VjRXYGT6ozxuehAPdzOvmd5jcdo6y/pmjnVY5PUm9W+Y6lAcCk829HcJT38H81J+4LmEDyzDPlVMfvRybktvl7Z0dW6V57dXyOzK35N+jG3p4WxLD+f39ANEGleo6xDIMNf7edy1J86xlTgVCVdjM19XYuBaXGZPmG+NKNYHfsVS5x9y/IEzpbngcaE+1Q/1pNaZzlRy8sHXE7z9Ejlc/1s2Bi4i1vkaAM440d25NQNdOtPXuR2+Dl6W7Vy8CheuwP60U4yv9DJXnK3Dgku6Oy1PD6D5oRBSon25Hg/X4jPbGGW+xmW/oyQFHMn8w1n1Ai6+8Zi84kj9X++bB27c5diAugmN8TjbGNOZenh7gJdPKh7eqbh5ppDyZxUO7/fj90Ow7wRkZB+FKncVU+cfoeoFcErPDJZOaZn/Njvg4mjCy90BbzcH3FwM0k3ppJFGuimdDDLwPtsUr02PEPNnOa7EQHpG5pyz5nWgWR1ocgdcjIbth2HbIdh1NHNYOJMBNY/j0G8Rpvv++oNrbGuPeeHTOIx7HVONM5nLktwxfhyIqekuTPUOFeq9ZRxqRrWNw7noch6j63eYah0vVD1L/QxHjE9ewmfrg3Rqkflz3HnkhkKN9uDw7L8xVfkz7+2kO8IfbTB+7k7DK+2oc/c5frrrPVJr5n0cxtlamQHSJSXz5ZaEySvvizJy1E92w/jpITjeANOI/7MaSjb23wUJ3n8Vdk4F/3NQ9UKBPYQArgl+NIptRb2Y5hzZ5c/e7ZXJuFwJapzGYcKLmLwze7aMc3fgP/c/JNY4RFzPLzHXuvHk5dLuDAeMzb0wVY7E1OSPvMsleGVeGNN0Z6EDp5HqAieDrcJ41YiWTEx6lR89fuCnqotJy6XXO2t/WV9+cl2f5gznauFQPQLDLecFN0aKK8ZPD2L89CCm9j9hemAxJtcbeowSvXA51RiXs/WJLReBqenOfC/Egcx5o6azdSl/pS4BSbVwwxUHkwMOmMhwSGN3zRVk3PVrvtsoiJHhAMke+R9/rC8kemKq+te9Ce863Z+no5+lezNXAgp/bVSBFJ5sSOGpbDmccYZNabtp7dSIFo71bvoqMMMwSCQZD9yKNF/kkjmaAxknKWfyoqKpHJUcyhW4jUQjmZWpv2LGoKdzG8o5eOdZNssF82X6xr2ceZd4XPiHW39ecXuMygVMWk9Nywx+JqDq/0Zm04x0Yo0EfEyeRbozfGIy7D8Jx87DsYi//ms2oF4A1K+ROcxaLwDqBf41JFJS0tIh8krm/Dp318yXk6PBzJTFvJo0N9f5RU7x5TGmzcD1fH0GdIDuvf7kXJ2N/JyxB1dc8DZ7E3/Zh8gIT477/8y1SsfybYOR4gJXq0B0RYzoinC9AjhkZPYGesWBVxwku2P+LgT2t8xzO8E1MoeN6tVL4LcWc/jR+zsAHMyOuF+sQ/zexhhHG+Mc3prBrXz5xwPQpmHm/KwMI4O3L3/LO3xMktP/HnqeVIE7I3vRPro3gWlBxCdBXBLEJWa+Yonjsuc5rnmfI8b3HDFuUVyPh7T/ZQeTWyLctTXnVbZZxx3ri/m/42BbHnNHXVJwDjpL+YanSPO5wvXUZHBLBtfkzIB1vibG3lZwtjZ/9ejdoMYpHCaNtYQ1I80pR3uMmHJwqh6crUPNlDrUqJHEjkbzSfHJObQJ4P57T5pt/weJrX7icMsvSHPOY9j+bK3MXtuq5zF5/tWraERVxVjbD2N9H5yTypHeeQWmJ/+TZ+gyktwh3idHr+hNSfTEvLo/xspBuCeXZ+az4GCC/9t8mfBWn2HqtKrQF+Y4Xq1C+tGGGCcaYBxvAKfqQ1IhLrioexCHRz/B1GyX1WIjoibGvpZwvCFGihtkOEG6E3gk4NBxNdz5e65tM07Wx9hwP6YmezC13ZxzfYIn5rnj4ffM99nSKfBwh0IdYqEoPNmQwpPYSrKRwqa0PTR3qks1h4q2bk6ZsiZ1G0MSJhFj/PUNt45DAD96z6AmATg4FDwx3GyYWZ62mUmJn3LUfM5qXUvHhjzt9hD3xXVhxz43tuyFLfsybwHi7poZbO5rCu2bZs6T2rIP1u2C9bszwytk9qg93D7zw6DBDSMRe9OPEWMkcJdTfbxMHkTHwqEzmeXyuiIy0nyZsJR11HOsQS/nNkWeWGsYcP4yhJ+Gw2ch3vMSfzRYxE++35Ni+mtY+O6ENnTaNp5jByqSlAL+FaB6pf/9939Xq1avBBV9/zrHcYmZ7T94JvPihJ1HYPex7D2HmWpWhX73wQP3ZNZdf+E8/9doLAk+1r1x7pF1qfHrUFrGtqfnXY50veuvR0olGsl8mLyUtxO/Is6U+fOvllGNOW7jeMC7tWUbF81XeSPxY+an/giAv6ki/UzdqHW8J5F76nDuEkRcNohIiiHSOXPOYlvXBnS7M3N/d9eHPcfg7T/28uO9r2P4XP/rXKY7Yvz0EMY3wwlyL0fzDmdwb/M7EYG/c8bxLHWoQVBcI7wjGpN6uCHRSWlcrPoHF/3/4LL/HyT4RmJcrQhHm2AcaYxxtAmcrgvpLjSqCV9PypwDmmXXEXjvl7P87raVuMD9JNQMx+zz12R+t1Rv7sloySC/1vRwbUWAQ2XiE+HwOTh4OvOLz4kLcPx85ishl04zTzcI6QKj+kJCrT1sSN9FTYeqdHNuhWtMVTbvzXyPr92Z+T6yUvEilR9eSWr7H0lzjaNV7H20Ofcw3hcacSXGRFIKXPQ8xb6mX3G2znpwMONypgHJ707BuPjXZPh9n0PT2pQYhScbUngSKZuOZpylX9y/OGaO4G7HYL73fr/AnrncpBvpLExdS2jKaho4BvGk64O0cKqXa9nYhMxbPrjkfuEbZnNmiPD2gKCqRW6KTVwyR/Of5MX8mr6PJ1x68ZTrgyVy9V5aeuYH9/bDEJ8EXe/K/GC8cdPnzVH0jXuZAxknucepKePdnijUvc6ummP4OOVbHHFgjNvDeJk8ci13JOMsV80xtHFqlGNScxazOfPllEcePZZ0kb5XJnDS4zAtLnfi6finaVshgKAq4JX7bvOVZKRw+bILYetNfPVTZugEePJ+mPUceBQwLdUwDE6bI9mbcYyqpoq0cmpQ6DBtGHA9HjIyMoflzUbmvyv6glsh5r0bRmb4XrsTdh+FugEwsCM0rFmo3XM6I5JjGRF0dr4Lc5oTp/78K9j944GCj70oFJ5sSOFJpOxKNdI4kHGS5o518/xglLIvw8jgkhFNNVPFMntDUcMwSCKlxC+4MYzMwJ2aDi3qluimb3tF+fzWcy1E5LbhYnLmLqdgWzdDisnR5Ii/qZKtm5Evk8mEByV/pbLJZD1EJ7ZR5h8MLCIiIlKWKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCk4iIiEgRONm6AX83hmEAEBsba+OWiIiISGFlfW5nfY7nR+GphMXFxQEQGBho45aIiIhIUcXFxeHr65tvGZNRmIglhWY2m4mMjMTb2xuTyXTT24mNjSUwMJCIiAh8fHxKsIVyI53r0qXzXXp0rkuPznXpuVXn2jAM4uLi8Pf3x8Eh/1lN6nkqYQ4ODgQEBJTY9nx8fPSLWEp0rkuXznfp0bkuPTrXpedWnOuCepyyaMK4iIiISBEoPImIiIgUgcJTGeXq6sqkSZNwdXW1dVP+9nSuS5fOd+nRuS49Otelpyyca00YFxERESkC9TyJiIiIFIHCk4iIiEgRKDyJiIiIFIHCUxkTHx/P2LFj8ff3x83NjebNm7N48WJbN8uubdy4kREjRhAcHIynpyfVq1fnwQcfZPfu3TnK7tmzh65du+Ll5UW5cuXo378/p06dskGr/z4+++wzTCYTXl5eOdbpfBffr7/+Su/evSlfvjzu7u7UrVuXqVOnWpXReS6+P/74g4ceegh/f388PDwIDg7mzTffJDEx0aqcznXRxMXF8corr9C9e3cqVaqEyWRi8uTJuZYtyrmdPXs2wcHBuLq6cscddzBlyhTS0tJKrN0KT2VM//79CQ0NZdKkSaxevZqWLVsSEhLCokWLbN00u/XRRx9x5swZXnjhBVatWsWsWbOIioqiTZs2bNy40VLuyJEjdOzYkdTUVJYsWcK8efM4duwY9913H5cvX7bhEdivCxcu8PLLL+Pv759jnc538S1atIgOHTrg6+vLl19+yapVq/jXv/5l9WwunefiO3ToEPfccw9nzpxh5syZrFy5ksGDB/Pmm28SEhJiKadzXXRXr17lk08+ISUlhYceeijPckU5t2+99RYvvPAC/fv3Z+3atTzzzDP8+9//ZsyYMSXXcEPKjB9//NEAjEWLFlkt79atm+Hv72+kp6fbqGX27dKlSzmWxcXFGVWqVDG6dOliWTZw4ECjYsWKRkxMjGXZmTNnDGdnZ+OVV14plbb+3fTp08fo27evMXToUMPT09Nqnc538Zw/f97w9PQ0Ro8enW85nefie/311w3AOHHihNXyUaNGGYARHR1tGIbO9c0wm82G2Ww2DMMwLl++bADGpEmTcpQr7Lm9cuWK4ebmZowaNcqq/ltvvWWYTCbj4MGDJdJu9TyVIStWrMDLy4uBAwdaLR8+fDiRkZFs377dRi2zb5UrV86xzMvLi4YNGxIREQFAeno6K1eu5OGHH7a63X9QUBCdOnVixYoVpdbev4sFCxawZcsW5s6dm2OdznfxffbZZyQkJPCvf/0rzzI6zyXD2dkZyPnojnLlyuHg4ICLi4vO9U0ymUwFPge2KOd2zZo1JCcnM3z4cKttDB8+HMMw+Pbbb0uk3QpPZUh4eDgNGjTAycn6kYNNmza1rJeSERMTw549e2jUqBEAJ0+eJCkpyXKus2vatCknTpwgOTm5tJtpt6Kiohg7dizTp0/P9VmPOt/F9/PPP+Pn58eRI0do3rw5Tk5OVK5cmX/84x/ExsYCOs8lZejQoZQrV47Ro0dz6tQp4uLiWLlyJR9//DFjxozB09NT5/oWKsq5zfqcbNKkiVW5atWqUbFixRL7HFV4KkOuXr2Kn59fjuVZy65evVraTfrbGjNmDAkJCbz++uvAX+c2r/NvGAbXrl0r1Tbas2eeeYb69eszevToXNfrfBffhQsXSExMZODAgQwaNIj169czbtw4vvzyS3r37o1hGDrPJaRmzZr8/vvvhIeHU7t2bXx8fOjbty9Dhw5l1qxZgN7Tt1JRzu3Vq1dxdXXF09Mz17Il9TnqVHARKU35dV8W1LUphTNhwgQWLlzI7Nmzueuuu6zW6fwX37Jly/jhhx/4448/CjxnOt83z2w2k5yczKRJk3j11VcB6NixIy4uLowdO5YNGzbg4eEB6DwX15kzZ+jbty9VqlRh6dKlVKpUie3btzNt2jTi4+P5/PPPLWV1rm+dwp7b0vgZKDyVIRUqVMg1FUdHRwO5p24pmilTpjBt2jTeeustnn32WcvyChUqALn37kVHR2MymShXrlxpNdNuxcfHM2bMGJ577jn8/f25fv06AKmpqQBcv34dZ2dnne8SUKFCBY4fP06PHj2slvfq1YuxY8eyZ88eHnzwQUDnubheffVVYmNj2bt3r6VHo3379lSsWJERI0bwxBNPULVqVUDn+lYoyt+LChUqkJycTGJiouXLQ/ayN35hvlkatitDmjRpwuHDh0lPT7dafuDAAQAaN25si2b9bUyZMoXJkyczefJkXnvtNat1tWvXxt3d3XKusztw4AB16tTBzc2ttJpqt65cucKlS5eYMWMG5cuXt7zCwsJISEigfPnyDBkyROe7BOQ2/wOw3KbAwcFB57mE7N27l4YNG+YYCmrZsiWAZThP5/rWKMq5zZrrdGPZixcvcuXKlRL7HFV4KkP69etHfHw8y5Yts1oeGhqKv78/rVu3tlHL7N/UqVOZPHkyb7zxBpMmTcqx3snJib59+7J8+XLi4uIsy8+dO8emTZvo379/aTbXblWtWpVNmzblePXo0QM3Nzc2bdrEtGnTdL5LwMMPPwzA6tWrrZavWrUKgDZt2ug8lxB/f38OHjxIfHy81fLff/8dgICAAJ3rW6go57Znz564ubkxf/58q23Mnz8fk8mU772kiqREbnggJaZbt25G+fLljU8++cTYuHGj8dRTTxmAsWDBAls3zW69//77BmD07NnT+P3333O8shw+fNjw8vIy2rdvb6xatcpYvny50bhxY8Pf39+Iioqy4RHYv9zu86TzXXx9+/Y1XF1djalTpxrr1q0z3n77bcPNzc3o06ePpYzOc/F99913hslkMtq0aWN8/fXXxoYNG4y33nrL8PLyMho2bGikpKQYhqFzfbNWrVplfPPNN8a8efMMwBg4cKDxzTffGN98842RkJBgGEbRzu20adMMk8lkvPbaa8bmzZuN9957z3B1dTWeeuqpEmuzwlMZExcXZzz//PNG1apVDRcXF6Np06ZGWFiYrZtl1zp06GAAeb6y27Vrl9GlSxfDw8PD8PHxMR566KEcN8aTosstPBmGzndxJSYmGv/6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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_epochs = 100\n", + "batch_size = 32\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", + "\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(\n", + " inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) \n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + "\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train diffusion completed, total time: {total_time}.\")\n", + "\n", + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "326101ed-333b-44a9-933f-55760b5d93a4", + "metadata": {}, + "source": [ + "## Check the performance of the diffusion model\n", + "\n", + "We generate a random image from noise to check whether our diffusion model works properly for an image generation task.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8f7a9e99-a8a4-4c8f-a42f-17ef91b18585", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 1000/1000 [00:10<00:00, 95.94it/s]\n" + ] + }, + { + "data": { + "image/png": "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LS0oLi5GTk4OUlNTERMTg/j4eGg0GtUE2my92tjY9FlKyppfF6NXabb0ii06OpqIiEaPHk2enp5kNBpp3rx5REQUGRlJRER/+9vfqKqqiiorK+n333+ngIAA+qd/+ify8PCg6dOn0//+3/+biIimT59ORETV1dX8/gcOHCAiop9++on+8R//kXbt2kVERPX19fT48WMKDw8nnU5HH3/8MQ0cOJC0Wi398ssv/Pf/+q//SitXriQnJyd6++23adGiRfy7iIgI/v+5c+dSZ2cnNTc3082bN4mIyGg00vnz52nw4ME0evRoIiJavXo1/fzzz5SRkUH//M//TAaDgebMmUNz586lEydO0Lx582jw4MG0Y8cOIiJydnYmf39/8vb2pk8++YRSU1PJaDQSEZFer+fPt7e35/+fNGkSDRw4kIiIAgIC6Pfff6cnT56QTqejkydPEhHRe++9p5iLpUuXkr+/P926dYv++te/8s/d3d0l45mXl0elpaX04MEDIiIaOHAg7dmzh4iIVq1aRURE169fp61bt9J/+S//hXQ6HU2cOJFu375NXl5eZDAY6O7duzRq1Cj68ccfiYjIbDZTXFwcff7553Tu3DnauHGjYoyJiBITE/n/P/74Y0pJSaHbt2/zz7777juKj4+nZ8+e0fPnz+nx48dERPTbb7/xa9544w0aOHAg2draUklJCdnY2PDv/P39iYjoyZMndPnyZWptbaWrV68SEZHJZKLPPvuMHj9+TL29vRQSEkJEFl7y9/enfv36cd5l9/zuu+/oX/7lX6i0tJSIiFJSUqi2tpYiIiJo165ddOjQISIimjVrFhER5efnExGRo6Mj3bp1i4iI/v3f/50SEhL4GNfV1dGdO3dIr9dTeno6XblyhZYtW0ZExMeTiOh//s//SatWrSKNRkP/63/9L/4sIqLQ0FD+/zfffJNGjx5NU6ZMod27dxMRUXh4OF27do0GDBhAOTk5fNx+++03SklJoZEjR5KXlxf9y7/8C+3cuZN++uknGjNmDHV2dtKmTZvI1dWV/vKXv1BiYiKZTCY6d+4c6XQ6amlpISKiqqoq/vxvvvmG/7+1tZU6OzuJiCgvL4+uX79Ozs7OZDab6cMPPyS9Xk8rVqyQzGdBQQFt3LiR/P396cKFC/Tf//t/59+5urry/+/YsYMGDRpElZWV/Pd1dXX0wQcf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2Xc7Oztx4MABiVGhlp6ePqxb5sqVK/xvsRxRSkqKVCqGMef8cf78eQnwnjhxgi8ASjbBVTkRGheii+yXX37BP//5T9y6dQsDAwOwWCyqDZqbmxsAZ9WEW7duSX3Jx8dn2KLZohie7hFpeMRyNYw5N38nT57Ehg0beKDEpUuXVNovasqFXNnEsmYXLlzA6dOnATj1YX19fQgLC1OlowCcLDqx9q7GobJR5CdjTMW47Nq1SzVvvvnmm5ILt6WlRWJKR7K+0AK/adMmzhDm5eVh27ZtuHz5MhwOB2fzKW8htZaWFpSXl6OwsFDl4k9KSnKZ34oxWS8o5mWLj49XMUk1NTUoKSlBbm4uz+EkjgulrnE4997cuXMlAFhTU4P6+nqe8oYxdQJwHx8fPPvsswgNDcXChQsl92pgYKBmhLby/tJxxPeUXifSgXl6enJywsvLyyUAeprrUTy+yWTitQw9PDwwevRonlNL/A5tqJTpOBhTM3EjMbf/DADddNNNN91000033f7L5v5/fQK66aabbrrppptu/19NB1q66aabbrrppptuf5LpQEs33XTTTTfddNPtTzIdaOmmm2666aabbrr9SaYDLd1000033XTTTbc/yXSgpZtuuummm2666fYnmQ60dNNNN91000033f4k04GWbrrppptuuumm259kOtDSTTfddNNNN910+5PsfwAmd61phvc9EwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model.eval()\n", + "noise = torch.randn((1, 1, 64, 64))\n", + "noise = noise.to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "with autocast(enabled=True):\n", + " image, intermediates = inferer.sample(\n", + " input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100\n", + " )\n", + "\n", + "chain = torch.cat(intermediates, dim=-1)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "546f9983-c2e2-4c24-b03a-ebe34627638a", + "metadata": {}, + "source": [ + "## Define the classification model\n", + "First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "44cc6928-2525-4e61-8805-15b409097bbb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "DiffusionModelEncoder(\n", + " (conv_in): Convolution(\n", + " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_embed): Sequential(\n", + " (0): Linear(in_features=32, out_features=128, bias=True)\n", + " (1): SiLU()\n", + " (2): Linear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (down_blocks): ModuleList(\n", + " (0): DownBlock(\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", + " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (1): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (2): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (out): Sequential(\n", + " (0): Linear(in_features=4096, out_features=512, bias=True)\n", + " (1): ReLU()\n", + " (2): Dropout(p=0.1, inplace=False)\n", + " (3): Linear(in_features=512, out_features=2, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "classifier = DiffusionModelEncoder(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=2,\n", + " num_channels=(32, 64, 64),\n", + " attention_levels=(False, True, True),\n", + " num_res_blocks=(1,1,1),\n", + " num_head_channels=64,\n", + " with_conditioning=False,\n", + ")\n", + "classifier.to(device)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", + "metadata": {}, + "source": [ + "## Model training of the classification model\n", + "We train our classification model for 100 epochs.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 534it [00:24, 22.16it/s, loss=0.671] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: : 17it [00:00, 65.41it/s, val_loss=0.288]\n", + "Epoch 1: : 534it [00:24, 21.99it/s, loss=0.612] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 1: : 17it [00:00, 66.80it/s, val_loss=0.363]\n", + "Epoch 2: : 534it [00:24, 21.92it/s, loss=0.586] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 2: : 17it [00:00, 68.07it/s, val_loss=0.226]\n", + "Epoch 3: : 534it [00:26, 20.48it/s, loss=0.581] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 3: : 17it [00:00, 63.17it/s, val_loss=0.217]\n", + "Epoch 4: : 534it [00:25, 20.99it/s, loss=0.579] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 4: : 17it [00:00, 63.70it/s, val_loss=0.211]\n", + "Epoch 5: : 534it [00:26, 20.46it/s, loss=0.572] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 5: : 17it [00:00, 63.46it/s, val_loss=0.234]\n", + "Epoch 6: : 534it [00:25, 20.66it/s, loss=0.577] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 6: : 17it [00:00, 63.53it/s, val_loss=0.306]\n", + "Epoch 7: : 534it [00:26, 20.39it/s, loss=0.57] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 7: : 17it [00:00, 62.97it/s, val_loss=0.372]\n", + "Epoch 8: : 534it [00:25, 20.72it/s, loss=0.572] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 8: : 17it [00:00, 63.76it/s, val_loss=0.208]\n", + "Epoch 9: : 534it [00:26, 20.18it/s, loss=0.565] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 9: : 17it [00:00, 61.70it/s, val_loss=0.245]\n", + "Epoch 10: : 534it [00:26, 20.22it/s, loss=0.563] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 10: : 17it [00:00, 63.48it/s, val_loss=0.181]\n", + "Epoch 11: : 534it [00:26, 20.42it/s, loss=0.564] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 11: : 17it [00:00, 64.20it/s, val_loss=0.196]\n", + "Epoch 12: : 534it [00:26, 20.35it/s, loss=0.562] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 12: : 17it [00:00, 64.27it/s, val_loss=0.235]\n", + "Epoch 13: : 534it [00:26, 20.31it/s, loss=0.562] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 13: : 17it [00:00, 62.05it/s, val_loss=0.2] \n", + "Epoch 14: : 534it [00:26, 20.35it/s, loss=0.557] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 14: : 17it [00:00, 63.59it/s, val_loss=0.232]\n", + "Epoch 15: : 534it [00:26, 20.25it/s, loss=0.558] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 15: : 17it [00:00, 62.56it/s, val_loss=0.236]\n", + "Epoch 16: : 534it [00:26, 20.39it/s, loss=0.559] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 16: : 17it [00:00, 62.08it/s, val_loss=0.227]\n", + "Epoch 17: : 534it [00:26, 20.44it/s, loss=0.561] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 17: : 17it [00:00, 61.93it/s, val_loss=0.232]\n", + "Epoch 18: : 534it [00:26, 20.10it/s, loss=0.556] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 18: : 17it [00:00, 61.19it/s, val_loss=0.265]\n", + "Epoch 19: : 534it [00:26, 20.52it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 19: : 17it [00:00, 61.85it/s, val_loss=0.214]\n", + "Epoch 20: : 534it [00:26, 20.13it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 20: : 17it [00:00, 62.12it/s, val_loss=0.304]\n", + "Epoch 21: : 534it [00:26, 20.33it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 21: : 17it [00:00, 60.91it/s, val_loss=0.235]\n", + "Epoch 22: : 534it [00:26, 20.19it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 22: : 17it [00:00, 62.88it/s, val_loss=0.232]\n", + "Epoch 23: : 534it [00:26, 20.24it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 23: : 17it [00:00, 62.73it/s, val_loss=0.146]\n", + "Epoch 24: : 534it [00:26, 20.32it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 24: : 17it [00:00, 62.44it/s, val_loss=0.223]\n", + "Epoch 25: : 534it [00:26, 20.20it/s, loss=0.553] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 25: : 17it [00:00, 62.95it/s, val_loss=0.286]\n", + "Epoch 26: : 534it [00:26, 20.24it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 26: : 17it [00:00, 63.56it/s, val_loss=0.316]\n", + "Epoch 27: : 534it [00:26, 20.20it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 27: : 17it [00:00, 61.08it/s, val_loss=0.217]\n", + "Epoch 28: : 534it [00:26, 20.18it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 28: : 17it [00:00, 63.45it/s, val_loss=0.155]\n", + "Epoch 29: : 534it [00:26, 20.30it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 29: : 17it [00:00, 62.70it/s, val_loss=0.227]\n", + "Epoch 30: : 534it [00:25, 20.61it/s, loss=0.55] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 30: : 17it [00:00, 66.44it/s, val_loss=0.2] \n", + "Epoch 31: : 534it [00:26, 20.32it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 31: : 17it [00:00, 61.60it/s, val_loss=0.258]\n", + "Epoch 32: : 534it [00:26, 20.40it/s, loss=0.549] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 32: : 17it [00:00, 63.44it/s, val_loss=0.17] \n", + "Epoch 33: : 534it [00:26, 20.37it/s, loss=0.546] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 33: : 17it [00:00, 62.44it/s, val_loss=0.197]\n", + "Epoch 34: : 534it [00:26, 20.23it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 34: : 17it [00:00, 64.16it/s, val_loss=0.227]\n", + "Epoch 35: : 534it [00:26, 20.28it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 35: : 17it [00:00, 61.64it/s, val_loss=0.182]\n", + "Epoch 36: : 534it [00:26, 20.24it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 36: : 17it [00:00, 62.97it/s, val_loss=0.189]\n", + "Epoch 37: : 534it [00:26, 20.37it/s, loss=0.548] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 37: : 17it [00:00, 63.50it/s, val_loss=0.232]\n", + "Epoch 38: : 534it [00:26, 20.30it/s, loss=0.554] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 38: : 17it [00:00, 62.30it/s, val_loss=0.175]\n", + "Epoch 39: : 534it [00:26, 20.25it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 39: : 17it [00:00, 62.73it/s, val_loss=0.219]\n", + "Epoch 40: : 534it [00:26, 20.17it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 40: : 17it [00:00, 62.13it/s, val_loss=0.169]\n", + "Epoch 41: : 534it [00:26, 20.06it/s, loss=0.547] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 41: : 17it [00:00, 61.03it/s, val_loss=0.153]\n", + "Epoch 42: : 534it [00:26, 20.06it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 42: : 17it [00:00, 62.17it/s, val_loss=0.18] \n", + "Epoch 43: : 534it [00:26, 20.04it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 43: : 17it [00:00, 61.85it/s, val_loss=0.168]\n", + "Epoch 44: : 534it [00:26, 19.98it/s, loss=0.542] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 44: : 17it [00:00, 61.28it/s, val_loss=0.181]\n", + "Epoch 45: : 534it [00:26, 20.16it/s, loss=0.542] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 45: : 17it [00:00, 63.26it/s, val_loss=0.154]\n", + "Epoch 46: : 534it [00:26, 20.08it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 46: : 17it [00:00, 61.43it/s, val_loss=0.151]\n", + "Epoch 47: : 534it [00:26, 20.06it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 47: : 17it [00:00, 62.66it/s, val_loss=0.174]\n", + "Epoch 48: : 534it [00:26, 20.27it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 48: : 17it [00:00, 62.88it/s, val_loss=0.148]\n", + "Epoch 49: : 534it [00:26, 20.32it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 49: : 17it [00:00, 62.37it/s, val_loss=0.178]\n", + "Epoch 50: : 534it [00:26, 20.24it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 50: : 17it [00:00, 62.16it/s, val_loss=0.203]\n", + "Epoch 51: : 534it [00:26, 20.33it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 51: : 17it [00:00, 63.13it/s, val_loss=0.178]\n", + "Epoch 52: : 534it [00:26, 20.37it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 52: : 17it [00:00, 63.45it/s, val_loss=0.191]\n", + "Epoch 53: : 534it [00:26, 20.32it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 53: : 17it [00:00, 62.24it/s, val_loss=0.182]\n", + "Epoch 54: : 534it [00:26, 20.10it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 54: : 17it [00:00, 63.44it/s, val_loss=0.184]\n", + "Epoch 55: : 534it [00:26, 19.94it/s, loss=0.544] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 55: : 17it [00:00, 62.61it/s, val_loss=0.165]\n", + "Epoch 56: : 534it [00:26, 20.19it/s, loss=0.545] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 56: : 17it [00:00, 61.80it/s, val_loss=0.175]\n", + "Epoch 57: : 534it [00:26, 20.07it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 57: : 17it [00:00, 62.74it/s, val_loss=0.164]\n", + "Epoch 58: : 534it [00:26, 20.27it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 58: : 17it [00:00, 62.64it/s, val_loss=0.159]\n", + "Epoch 59: : 534it [00:26, 20.23it/s, loss=0.536] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 59: : 17it [00:00, 63.27it/s, val_loss=0.166]\n", + "Epoch 60: : 534it [00:26, 20.21it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 60: : 17it [00:00, 62.98it/s, val_loss=0.146]\n", + "Epoch 61: : 534it [00:26, 20.03it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 61: : 17it [00:00, 61.23it/s, val_loss=0.153]\n", + "Epoch 62: : 534it [00:26, 20.15it/s, loss=0.54] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 62: : 17it [00:00, 62.14it/s, val_loss=0.18] \n", + "Epoch 63: : 534it [00:26, 20.22it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 63: : 17it [00:00, 61.99it/s, val_loss=0.152]\n", + "Epoch 64: : 534it [00:26, 20.04it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 64: : 17it [00:00, 61.32it/s, val_loss=0.14] \n", + "Epoch 65: : 534it [00:26, 20.25it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 65: : 17it [00:00, 63.32it/s, val_loss=0.145]\n", + "Epoch 66: : 534it [00:26, 20.14it/s, loss=0.539] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 66: : 17it [00:00, 61.50it/s, val_loss=0.154]\n", + "Epoch 67: : 534it [00:26, 20.09it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 67: : 17it [00:00, 59.68it/s, val_loss=0.148]\n", + "Epoch 68: : 534it [00:26, 20.25it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 68: : 17it [00:00, 63.40it/s, val_loss=0.172]\n", + "Epoch 69: : 534it [00:26, 20.34it/s, loss=0.543] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 69: : 17it [00:00, 61.41it/s, val_loss=0.211]\n", + "Epoch 70: : 534it [00:26, 20.22it/s, loss=0.538] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 70: : 17it [00:00, 60.88it/s, val_loss=0.158]\n", + "Epoch 71: : 534it [00:26, 20.51it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 71: : 17it [00:00, 62.84it/s, val_loss=0.129]\n", + "Epoch 72: : 534it [00:26, 20.30it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 72: : 17it [00:00, 63.48it/s, val_loss=0.197]\n", + "Epoch 73: : 534it [00:26, 20.27it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 73: : 17it [00:00, 62.99it/s, val_loss=0.158]\n", + "Epoch 74: : 534it [00:26, 20.17it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 74: : 17it [00:00, 62.28it/s, val_loss=0.147]\n", + "Epoch 75: : 534it [00:26, 20.25it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 75: : 17it [00:00, 63.89it/s, val_loss=0.131]\n", + "Epoch 76: : 534it [00:26, 20.34it/s, loss=0.536] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 76: : 17it [00:00, 61.53it/s, val_loss=0.155]\n", + "Epoch 77: : 534it [00:26, 20.15it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 77: : 17it [00:00, 61.50it/s, val_loss=0.158]\n", + "Epoch 78: : 534it [00:26, 20.20it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 78: : 17it [00:00, 62.59it/s, val_loss=0.153]\n", + "Epoch 79: : 534it [00:26, 20.19it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 79: : 17it [00:00, 61.60it/s, val_loss=0.162]\n", + "Epoch 80: : 534it [00:26, 20.31it/s, loss=0.537] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 80: : 17it [00:00, 63.66it/s, val_loss=0.181]\n", + "Epoch 81: : 534it [00:26, 20.48it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 81: : 17it [00:00, 63.58it/s, val_loss=0.216]\n", + "Epoch 82: : 534it [00:26, 20.11it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 82: : 17it [00:00, 60.27it/s, val_loss=0.139]\n", + "Epoch 83: : 534it [00:26, 20.29it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 83: : 17it [00:00, 62.75it/s, val_loss=0.202]\n", + "Epoch 84: : 534it [00:26, 20.10it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 84: : 17it [00:00, 60.65it/s, val_loss=0.148]\n", + "Epoch 85: : 534it [00:26, 20.23it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 85: : 17it [00:00, 63.67it/s, val_loss=0.153]\n", + "Epoch 86: : 534it [00:26, 20.20it/s, loss=0.532] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 86: : 17it [00:00, 63.29it/s, val_loss=0.153]\n", + "Epoch 87: : 534it [00:26, 20.26it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 87: : 17it [00:00, 63.14it/s, val_loss=0.148]\n", + "Epoch 88: : 534it [00:26, 20.04it/s, loss=0.535] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 88: : 17it [00:00, 63.95it/s, val_loss=0.194]\n", + "Epoch 89: : 534it [00:26, 20.19it/s, loss=0.527] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 89: : 17it [00:00, 62.93it/s, val_loss=0.175]\n", + "Epoch 90: : 534it [00:26, 20.35it/s, loss=0.528] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 90: : 17it [00:00, 63.62it/s, val_loss=0.173]\n", + "Epoch 91: : 534it [00:26, 20.25it/s, loss=0.522] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 91: : 17it [00:00, 63.33it/s, val_loss=0.167]\n", + "Epoch 92: : 534it [00:26, 20.20it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 92: : 17it [00:00, 61.01it/s, val_loss=0.183]\n", + "Epoch 93: : 534it [00:26, 20.18it/s, loss=0.531] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 93: : 17it [00:00, 61.76it/s, val_loss=0.179]\n", + "Epoch 94: : 534it [00:26, 20.31it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 94: : 17it [00:00, 63.02it/s, val_loss=0.152]\n", + "Epoch 95: : 534it [00:26, 20.17it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 95: : 17it [00:00, 63.23it/s, val_loss=0.148]\n", + "Epoch 96: : 534it [00:26, 20.11it/s, loss=0.533] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 96: : 17it [00:00, 63.35it/s, val_loss=0.154]\n", + "Epoch 97: : 534it [00:26, 20.35it/s, loss=0.534] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 97: : 17it [00:00, 62.97it/s, val_loss=0.17] \n", + "Epoch 98: : 534it [00:26, 20.25it/s, loss=0.53] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 98: : 17it [00:00, 62.36it/s, val_loss=0.138]\n", + "Epoch 99: : 534it [00:26, 20.22it/s, loss=0.529] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "final step train 533\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 99: : 17it [00:00, 63.30it/s, val_loss=0.193]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train completed, total time: 2708.850436449051.\n" + ] + }, + { + "data": { + "image/png": "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QQ0bNkSLFi3w77//4uLFi1i5ciWUSiVGjBgBgB9+Sk5ORqtWrawyJkePHkVmZmahz/3YY49Bp9Ph6NGjVrOwzGYz/v33X6vXXLt2DYGBgVbBUEZGBo4fP261v5BcKEiGpmnTpgBgc8mQvXv3AgCaNGni8PFcye0DorNnz6Ju3bpiKk3QqFEj8Xl79u3bh7p162LdunV47LHHoFQqER4ejvfee89th8wAoG5kbtfqvaeA9ML/GyKEEOJEQuPFF198EdevX0fv3r3FGpyQkBB4eHjg+PHjyMjIEF+TmJhY5A/mGo0Gzz33HB48eICFCxfKnvvuu+9s1g9FRkYiMTER586dE7eZTCa8/fbbePjQuuYrMJAfWrxz547D1yUEg7Nnz0ZKSoq4PSUlBbNnz5bt4+7cfsgsPj4e1atXt9ou/MXFx8fbfe3du3fx8OFDvPHGG5gzZw7q1auHv//+GwsWLEBMTAx++uknu6/V6/WysVXpX3Rx4zigV0tg6Z98cfWek0Cf1iV2ekIIIXZERUXhrbfewoEDBwDIO1MrFAq8+uqrWLhwIRo3box+/fohJSUFW7ZsQWRkJMLCwop07gULFuDvv//GtGnT8M8//6Bp06a4cOECNm/ejO7du2P79u2y/V9//XVs374dbdu2xXPPPQedToc9e/bg7t276Nixo1VWp3Xr1vDw8MDnn3+OlJQUMcv13nvv2b2m9u3b4/XXX8fixYvRoEEDPPPMM2CMYf369YiJicEbb7wh1le5O7fPEAGQdcosyHNmsxmpqan46quvMH78eHTq1Alz587F66+/jtWrV+Pq1at2Xzt//nz4+fmJX/batReXni1zH9OwGSGEuAdfX188++yzAPii4T59+sienz9/PubNmweO4/DVV19hx44dGDJkCLZv316k6e0Av3TGv//+i8GDB+PgwYNYtGgR4uPjsWPHDrRubf2puW/fvvjtt99QvXp1rFq1CqtXr0adOnVw+PBhREZaF6cGBgbit99+Q61atfD1119jypQpmDJlSr7X9cUXX2D58uUIDQ3FsmXL8O233yI0NBTLly/HokWLivQzlySOMRttN91I69atYTKZcPjwYdn2c+fOoUGDBli6dKnd6fOVKlXC/fv3kZCQgICAAHH79u3b0aNHD/zyyy947rnnbL7WVoYoIiICycnJJVIglpoBBD0FGIxA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CCJMoYJECywLLe8SuvFCLzIwjKjwonNJSQQWYRYRqEEQWFamSSXigVTMXE9lh/e7iB3abzXtb2kyzw8WlnfmnXlnLpaH3ZnZ0NBQWbBggfj7+0thYaGMj4//sZk1Ij89C56IiIhIhXgNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxER0QxpNBr+4znRPMdARESzKjAwUAkQv3r9//8WERH9Tc5zPQARqVNISAiWLVs26XIvL69ZnIaI1I6BiIjmxJEjR7B79+65HoOICAB/MiMiIiJiICKif9/PFy1XVFRg1apVMBgM8PT0RGpqKl6+fDlp78jICIqLixEZGQm9Xg93d3esXr0ap06dwtevXyft+/DhAwoLCxETEwN3d3cYDAaYzWbs378fT58+nbSvuroa8fHxMBqN8PDwwObNmyddv729HTk5OQgKCoKLiwuMRiOCgoKwbds2XLt2bZpnh4j+CCEimkUBAQECQM6fPz/tHgACQEpKSgSAeHt7y8qVK8VoNAoAWbhwodTV1Tn09fb2isViEQCi1WolMjJSzGazsr3ExEQZGxtz6Hv27Jn4+voqfeHh4RIdHS3u7u4CQLKysiac78yZM6LRaMTHx0diY2NFr9cLADEYDNLc3GzX09bWJkuWLBEA4ubmJhaLRaKjo8XT01MASFRU1LTPDxHNHAMREc2qmQQinU4npaWl8u3bNxERGRkZkV27dgkACQgIkNHRUbu+9PR0ASARERHy5s0bpf7kyRPx8vISAHL48GG7nsHBQfH39xcAkpKSIh0dHXbLa2tr5cqVKxPO5+bmZndcVqtVEhISBIDs2LHDricvL08JV0NDQ3bLmpubpby8fNrnh4hmjoGIiGaVLRBN9RoYGFB6bLWtW7c6bO/z58/i7e0tAOTcuXNKvbW1VTQajQCQhoYGh77KykoBIHq9XqxWq1I/duyYABCz2SyfPn2a1jHZ5jt48KDDsufPnwsA8fDwsKsnJycLAGlsbJzWPojo7+JdZkQ0J6a67d7Z2fHjKTc316G2YMECZGdno7i4GDU1NdizZw8A4P79+xARrF27FjExMQ596enpMJlM6OzsxKNHj5CSkgIAqKqqAgDk5+fDxcXlt44pOzvboWaxWODq6orBwUH09/dj8eLFAAA/Pz8AwI0bN2CxWPhgR6I5xkBERHPiv9x2bzabf1lvbW1Varb34eHhE/ZotVqEhYWhs7MTra2tSiBqbm4GAMTFxf3WbAAQHBw8YX3p0qXo6OjA8PCwEohyc3Nx8eJFFBUV4dKlS0hJScG6deuwceNG+Pr6/va+iWhmeJcZEc0bk32jZHuI49DQkFIbHh7+Zc9kfVarFQCwaNGi355Pr9dPWNdqf3zUiohSi46ORm1tLZKSktDV1YXy8nJkZGTAZDIhOTlZCWZENDsYiIho3ujr65uw3tvbCwAwGo1KzWAw2C2bSE9Pj0Of7f3Hjx9nNOt0xMXFoaamBgMDA7h79y4KCgpgMplw7949JCYmzsoMRPQDAxERzRuTfWtiq4eGhio12/tXr15N2PP9+3e0tLQ49EVERAAAHj9+PPOBp8lgMCA5ORlHjx5FS0sLgoOD0dXVherq6lmbgUjtGIiIaN44ffq0Q218fBxnz54FACQlJSn1pKQkaDQa1NfXT/hgxJs3b6KzsxN6vR5r1qxR6qmpqQCAkydPYnx8/A8fwdTc3NxgsVgAAN3d3bO+fyK1YiAionnjzp07OHHihHItztjYGPbu3Yvu7m74+flh586dyrrLly9HWloaACAzMxPv3r1TljU0NODQoUMAgLy8PLufzPbt24eAgAA0NTUhLS0NXV1ddjPU19fj6tWrMz6WAwcO4Pr16xgdHbWr19bW4sGDBwCA2NjYGe+HiKZHIz9f5UdE9JcFBgaivb19ytvut2/froQW2y3pJSUlKCgogLe3N/z8/PD69WtYrVa4urqipqYG8fHxdtvo6+tDQkICXrx4AScnJ6xYsQJfvnxRfkbbtGkTbt++DVdXV7u+xsZGpKSk4P3799BqtTCbzdDpdGhra8Pg4CCysrJw4cIFZX3bfJN9nNqOua2tDYGBgQB+XFTd2NgIZ2dnhISEwGg0oqenB+3t7QCAjIwMXL58eZpnlYhmioGIiGaVLRxMJT8/H2VlZQDsA0dFRQXKysrQ1NQEnU6H9evXo6ioCJGRkRNuZ2RkBMePH0dlZSXevn0LrVaL8PBwZGZmIicnBzqdbsK+/v5+lJaW4tatW2hra4OTkxNMJhM2bNiAnJwcREVFKev+l0D08OFDVFVVoa6uDh0dHRgcHISPjw/CwsKQm5uLLVu28NlERLOIgYiI/nlTBQ4iopniNURERESkegxEREREpHoMRERERKR6DERERESkevxzVyL65/FiaiL62/gNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqd7/AL3d+lWfpCm1AAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "batch_size = 32\n", + "n_epochs = 100\n", + "val_interval = 1\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", + "\n", + "classifier.to(device)\n", + "weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset\n", + "\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " classifier.train()\n", + " epoch_loss = 0\n", + " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", + " data_train = total_train_slices[indexes] # shuffle the training data\n", + " labels_train = total_train_labels[indexes]\n", + " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", + " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + "\n", + " for step, (a, b) in progress_bar:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + "\n", + " optimizer_cls.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=False):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image\n", + " pred = classifier(noisy_img, timesteps)\n", + " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", + "\n", + " loss.backward()\n", + " optimizer_cls.step()\n", + "\n", + " epoch_loss += loss.item()\n", + " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + " print(\"final step train\", step)\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " classifier.eval()\n", + " val_epoch_loss = 0\n", + " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", + " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", + " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", + " for step, (a, b) in progress_bar_val:\n", + " images = a.to(device)\n", + " classes = b.to(device)\n", + " timesteps = torch.randint(0, 1, (len(images),)).to(\n", + " device\n", + " ) # check validation accuracy on the original images, i.e., do not add noise\n", + "\n", + " with torch.no_grad():\n", + " with autocast(enabled=False):\n", + " noise = torch.randn_like(images).to(device)\n", + " pred = classifier(images, timesteps)\n", + " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " _, predicted = torch.max(pred, 1)\n", + " progress_bar_val.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train completed, total time: {total_time}.\")\n", + "\n", + " ## Learning curves for the Classifier\n", + "\n", + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + " )\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "# Image-to-image translation to a healthy subject\n", + "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdUAAAHWCAYAAAAhLRNZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAf80lEQVR4nO3da+yWBf0/8OsbIIqcBBUFBBRRYnhAOy1d5+lcrbVprXJOW7pWLZdt9TQ3bT3xUT1w67BqVk9qtbVfs9PWPDZPGWikCQICchYPiAgC/yf/7d/27/OG390HEny9nr657+9139d13x/v7Xr7GTt48ODBAQD4j73tv30AAHC8MFQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQZf7j/cGxs7EgeBwC8qR3O/4DQL1UAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCajP9vHwBvfjfccEOZ7d+/v8x27dpVZnPnzi2zDRs2lNmsWbPKbOLEiWW2d+/eMtu0aVOZvf7662V29913l1lyxx13lNkTTzxRZpMmTSqzO++8c6RjAXr5pQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCZjBw8ePHhY/3Bs7EgfC0fQZz7zmZgvW7aszE477bQye/rpp8vsxBNPLLM33nijzFKNZdy4cWW2ZcuWMkuVmp/+9KdldrT95Cc/KbPzzz+/zDZv3lxmv/rVr8osnYfp06eX2cqVK8vsnnvuKTM4lh3OuPRLFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATVRqjiO33HJLmc2fPz8+dtq0aWX23HPPlVnaRDN16tQy27dvX5kdOHCgzB555JEyu+aaa8rsxhtvLLM3k4cffrjM0paatWvXltl73vOeMkvn77777iuztJ3o3nvvLbNUwXrwwQfLDN4MVGoA4CgyVAGgiaEKAE0MVQBoYqgCQBNDFQCajP9vH8Bb1Y9+9KMy27ZtW5mlKsqUKVPK7JVXXjm8A/s30raZ1157rczSsaZtM+lYU93mWKnNjCpVTm666aYyS3W4U089tcxWr15dZj/84Q/L7Kyzziqz973vfWWWNiWlela6lm6//fYygyPBL1UAaGKoAkATQxUAmhiqANDEUAWAJoYqADRRqTmCvvWtb5VZqjkc5uKg/8/evXvL7MILL4yPfeihh8psyZIlZbZnz56RspdeeqnMUk3nHe94R5kdK9LWn7SJ5qqrriqzI7FFat26dWX2zne+s8zeeOONMkvX6Ouvv15mqVKzcOHCMrvjjjvKLFXQ0nH+/ve/L7P/+Z//KTPeGvxSBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJroqf6HvvzlL5dZ6sGtX7++zE466aQyO/PMM8ts+/btZbZmzZoyG4ZhOPnkk8ts69atZfbyyy/H562k9WCpM3vxxReP9PeOtk2bNpXZaaedVmZve1v937lz5879j47p31m7dm2ZpZVqL7zwQpml13DCCSeU2dKlS8vsxRdfLLMtW7aU2ahd1NSVvvrqq8ssXZ9pzd4wDMNXv/rVmHNs8EsVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNVGr+Q6nmMH369DJLt/q/8sorZfbPf/5zpMfNnz+/zIYh1wt27txZZgsWLCizXbt2ldm8efPKbNQ1dG8maf1ZWnGWKlNHwjPPPFNmqVKTVvel+kt6XKripM/L+PGjfY2lek+qiqVzmz5nK1eujMdz4403ltkPfvCD+FjePPxSBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE5Wa/yvdzj579uwye+CBB8rssssuK7PTTz+9zNK2mVQtWLhwYZmlesQw5K0cr776apkdPHiwzCZOnFhmqVqRnjNtVXn/+99fZkfCL3/5yzK74oorymzFihVldvnll5dZOkcPP/xwme3evbvM0nai559/vsxmzpxZZqecckqZJamClrbiJBs3biyzCRMmlNm0adPKLJ2HPXv2lNm73/3uMhuG/F3yne98p8xuvvnm+LwcXX6pAkATQxUAmhiqANDEUAWAJoYqADQxVAGgydjB1F/41384Nnakj+WIu/baa8ss3e6ebr1PGyv+/ve/l9nixYvLbM6cOWWWNl3MmDGjzPbv319mw5BrPKkak543bexIG3X++te/ltkZZ5xRZt/+9rfLbFTbtm0b6XFp087HPvaxMksbXjZs2FBm6WOcqjGp/vKHP/yhzNL5W7p0aZmNusFm3759ZXbgwIEyS5WhVDNL13XappO2DB2qFpQ+v2mjTjq/X/va1+Lf5H/ncMalX6oA0MRQBYAmhioANDFUAaCJoQoATQxVAGjyltpSc95555VZui0/3c6/d+/eMlu2bFmZPfjggyMdS7rVf8uWLWWWXvsw5A0haaPOjh07yuzcc88ts7T5JlWKli9fXmZpa8wll1xSZqmmlOpEqTKUajPJmjVrymz9+vVltnPnzjJLFZdU1UjXWqrUPPvss2WWqnnpWJL0eUmbm9J7nWozp556aplt3rx5pOcchmE46aSTyuyJJ54os7QZh6PPL1UAaGKoAkATQxUAmhiqANDEUAWAJoYqADQ57rbUfP3rXy+zefPmldmuXbvKLG3PSM+5e/fuMkv1gVSPSPWetOUibQcZhlyRmDZtWpldeumlZZYqAunvLVmyZKRjSZ588skyS3Wi9PpG9dprr5XZU089VWaj1jVSBSTVX+65554yO+2008ps8uTJZZbqL+ncTpkypczStZ2+tzZu3FhmU6dOLbNUNUqv71BWrVpVZgsWLCiz9N6krU6f/exnD+u4+H9sqQGAo8hQBYAmhioANDFUAaCJoQoATQxVAGhy3G2pSbWSVGVINZYkbQ6ZOXNmmT300ENlNnv27DJLm1/Sxpx0a/2h8lQheP755+PzjvL3UnXkhRdeKLO0NSZt2lm8eHGZJen2+nQ9pWs0VVXS+3LllVeW2XPPPVdmf/zjH8ts0aJFZZYqUelzdvLJJ5fZ1q1by2zt2rVlNmHChJGOZdy4cWX2+uuvl1mqiqW/d6gtPB/60IdiXknvzYoVK0Z6TkbnlyoANDFUAaCJoQoATQxVAGhiqAJAE0MVAJock5Waj3/84yM9Lm2iSRsy0gaQVJ3Yvn17maXNIXPmzCmzVG95+eWXR3rOYRiGU045pczS63/mmWfKbOHChWWW3rdUu9i0aVOZrVu3rszSdp/Vq1eXWar+pNrFu971rjJL5s6dO1KWbNmypcyWLl1aZqk2k2olaatTOrdp20yqGo26GSZVm1JVLJ339DlLn/lhGIZHHnmkzC644IIyS5uU0uYbjgy/VAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0GTsYFq18a//cGzsSB/LYfvkJz9ZZmkTxLJly8rs1VdfLbM9e/aUWdp0MWXKlDJL72fabpPqCqlacP7555fZMOSKS6qVvPHGG2WWqgepGrNgwYIyS/WeVClKry9VHdJ1kd7v66+/vsxGlepU6TpM0laV9PdSDenxxx8vs1RVSbWnJH2WUvUnfVek6zpJm3bS53oYhmHq1KkjHU86hymbN29emX3uc58rs7eywxmXfqkCQBNDFQCaGKoA0MRQBYAmhioANDFUAaDJMVmpufbaa8tswoQJZZY2XezevbvM0iaPVANI79nkyZPLLN2Wn6oTqRqS6gPDkKsO48fXy4zS8aQsvca0HeXiiy8us0mTJpVZqsaMWlVJm31SPeKcc84ps7RJafHixWWWpFpFui7SBqKnnnqqzFLtKW1uShWsVIkatWZ11llnlVmqrqVraf78+WX2t7/9rcyGYfRNUak2lM79obbmVG6++eaRHnc8UKkBgKPIUAWAJoYqADQxVAGgiaEKAE0MVQBoUncl3sQWLlxYZieffHKZpa0iZ555ZpmlW/3TLevpdv5U70kbKdLt/OkW+VRTGYZhuOyyy0b6m2nbTKqjpMpCOr+pWpEqIKlqlc7TjBkzyizVqVasWFFmy5cvL7Pk0UcfLbNUC0qVsCVLlpTZokWLyixda/fff3+ZpfOertETTzyxzPbu3Vtm6XO9efPmMkt1m3QtpY1Wc+fOLbNhyNdh+k5Ir/H0008vs6effrrM0mYqMr9UAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQ5Jis1KQay0UXXVRm69atK7MpU6aU2cSJE8ssVRnSZolU00k1jiTdIv/EE0/Ex65du7bM3vve95ZZqjOsXLmyzK666qoye+yxx8osbXiZM2dOma1atarMRq1PHInzm+pLO3bsGOk507k/++yzyyxJW51SHSN9ztJrT5WSF198scxSBStdS+k503uW3pdDVWpWr15dZqm+lTanpEpNek/T9xqZX6oA0MRQBYAmhioANDFUAaCJoQoATQxVAGhyTFZqpk6dWmapOpHqH2m7zaibWFIVJ9UO0iaPVONYsGBBmR3qFvlUPUhVjlSbSRWJlKWtG+nvpa0qkyZNKrMTTjihzNK5SFtFUhVny5YtZZaOM71n6TjTRqTf/va3ZTZv3rwyO9TWo0r6vDz//PNlls7t9u3byyxtPErVvFSzSt8x6TOfKjPDkK/7dF2k9y195/3mN78ps/SZIPNLFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATY7JSs369evL7CMf+UiZpVvv01aKdOt9umU/3eq+YcOGMkvbLJYvX15mqTqRKgLDkLeqpPpEev1pC0Z6b1KFafr06WWWjjNtD0nVmFNPPbXMUr3pxBNPLLO0VSRVu9KGlwMHDpRZ2nCSKi7btm0rs3Sc559/fpml6tb48fXXUbpe5s+fX2bpNaSaWaoo7dmzp8xmz55dZofaPpW+Z9LrSM+bvtdS3SZ9dsn8UgWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQJOxg+n+/n/9h2NjR/pYWtx2221llioJqY6RqhOpBpD+XtpmkaRqSKpOpArEoaQNGZMnTy6zadOmjfS4VKk56aSTyizVAJ566qkySxuDUs0h1Tzuv//+MksbbNLrSzWdD3/4w2WWzn26ftesWVNmjzzySJmlitIHP/jBMlu3bl2ZpTpcOg/pM7F06dIyS+/Z1q1byyxVWBYvXlxmw5Cv31SZSu9NqhulbUnPPvtsmd1zzz1ldrw7nHHplyoANDFUAaCJoQoATQxVAGhiqAJAE0MVAJocd5Wa73//+2WWqgwbN24ss1SrSNtdnn766TJL9YFUxUn1j7SJ5tVXXy2zYRiGmTNnxrySNuOkbNeuXWWWtvQkqeqQqgWjbn9J5z7VI9L1lKpWqdqVjjM9Z6qApFpJ+iylrSmpbpPes/ScqfL12GOPlVn6fKY6WHo/03k44YQTymwYhuHcc88ts1tvvbXMvvSlL5VZumZSfevJJ58ss2984xtldrxTqQGAo8hQBYAmhioANDFUAaCJoQoATQxVAGhSr6g4Rt10001ldtFFF5XZ2WefXWannnpqmaWqyqc//ekySzWOVHPYu3dvmd13331ldumll5bZMAzD+vXryyzVbdLrSBWJtHElbd1Iz5lqUcnOnTvLLFUkUiUh3Xo/6nOmx6X6S7qeUr0nndsdO3aU2fTp08ssVWpSJSxtVFm9enWZ/frXvy6z6667rszScaZzlLJDbYpK9ZcLL7ywzNK1lj7X6bsrnQsyv1QBoImhCgBNDFUAaGKoAkATQxUAmhiqANDkuKvUJLfcckuZpQ0n48fXb1O69T5JNYe0ASRVai655JIySxtVhiFv5dizZ0+ZpVv202aYJFU5UrVizpw5ZfbSSy+VWdqKs3z58jJLFYi0kSRVTtL7uXv37jKbPHlymaVrLR1L+ntpW9LmzZvLLH2WUiUqvb5UF3vggQfKLBm1KpY+u4f6rti3b1+ZXXnllWX2+OOPl9ns2bPLbM2aNWWWzi+ZX6oA0MRQBYAmhioANDFUAaCJoQoATQxVAGgydjCtOPjXfzg2dqSPpcWf/vSnMvvLX/5SZqkCkW5LX7t2bZmlrRSpwpM2uCxevLjMXnjhhTJLmzyGIW+lmDhxYpmlmsCsWbPKLFUWXnnllTIb9Vb/tJEjVU6StE0nbTZKtahDVZ8qqTKUqj+pTjRu3Lgy279/f5mlzTfp3C5btqzMUpUqSa8hvWepYpaqKOlaSp+jYcj1pk2bNo30uFSLuvrqq8vs2WefLbNPfepTZXa8O5xx6ZcqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaHHdbav7xj3+UWdqakuooK1asKLNUm0m1kVQ7SI9buXJlmaVazI4dO8rsUI9N2zMuuOCCMvvnP/9ZZqtWrSqzVBEYta6RboWfMWNGmaXzm2ozqaJ10kknlVmqeZxyyillll5D+nupapReQ6oFpbpY8uSTT470uG3btpVZ2rCUqmujbhJasmRJmaX3ehhybSidw1Gvw7vuuqvMzj333DIj80sVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNjrstNbfeemuZpQ0gaZtF2raSqjjnnHNOmaXtEZs3by6ztFXk+eefH+lxw5Bv2U/Hk2osixYtKrMjUeVIUu0gvW+pxpJe+6hbeA61yaSStqOkWtCECRPKLH3m02tIWar+7Ny5s8zSpp10vaRNQul9SdWtdO2OH1+3FNNmn2HIryO9b+k7KH2uL7zwwjJL1b30Wfr85z9fZscDW2oA4CgyVAGgiaEKAE0MVQBoYqgCQBNDFQCaHHeVmmuuuabM0paadMt6qh2kW+hHrY2kv5eqBSk7cOBAmQ1Drqqk5502bVqZpYpE2hCS6iiTJ08us3QuUp0qbYZJFZf0nEm6LlJdIdVm0nGmc5SkDS/J2rVryyxV11LFJZ3bN954o8xS3SR9zlIdLF2fyUMPPRTz9PrTZzB9N6fHpe1T6dpOtcXjnUoNABxFhioANDFUAaCJoQoATQxVAGhiqAJAk+OuUpNcf/31ZbZ48eIyS7elJ2lbx9lnn11mO3bsGOnvpY0qaevGMORKzcKFC8ssbQFJ1YO0PSPVIFKlJl3K6ThTdSRVOdI189hjj5VZqnKkmkO6DlOlZtTzsGHDhpEeN2nSpDJL13Y6f+n7J10TaQNRqi+lytuuXbtGOpYLLrigzIZhGFatWlVm6VjT9ZvO/e9///syu/fee8ssXdvHO5UaADiKDFUAaGKoAkATQxUAmhiqANDEUAWAJnVf4DiUNrVMnz69zLZu3TrS35s6dWqZpYpH2rqRNlmk7SepMjMMuQqQqkGpApKOJ9VK0mtMVYe0WSNVMs4666wye/nll8tsy5YtZZY2Io26EWjUus3mzZvLLL3XqTqS3uv0viSp/rF///4y2717d5mlOliqmaXrJW1YSt8jX/nKV8psGIbhtttuK7O0+Wf58uVltn79+jJLtagPf/jDZfZWrtQcDr9UAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQ5C1VqbnvvvvKLG35SBtlUh3jxRdfLLOXXnqpzObOnVtmo25UOdRt8O985zvLLL2OVP9JlYxUg5g5c2aZpXpIqgik7RLbtm0rs7RxJVVHUvUn1Vg2btxYZqeffnqZpbpYylK1KdWsUh0lXYfpHKUaS9pclDa4PPfccyMdS3p96TysXr26zO68884yG4Zh+NWvflVm6Vycd955ZZY+16luc6itVtT8UgWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQJO3VKUmbXqYMWNGmW3YsKHMUhUn1SpSbSZVPNKt9b/97W/LbNGiRWU2DHmjTHpvUqXmtNNOK7NUKRq1zrBz586RsvT60rGkzTBnnHFGmaVaVDqWtNkobUdJ1ZF0/lI1JtXM1q1bV2Zp41GqxqTjTJ/B9LjklFNOKbO0uSht70mfz2EYhiVLlpTZrFmzymzlypVllupw6f0+1LFS80sVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBN3lKVmmThwoVlljZkJHv37i2zcePGldm+ffvK7JlnnimzOXPmlFnaVDIM+VhTrSRtOdm6dWuZTZkypcxS7SJVJFLdaNq0aSP9vZSlSk16XKovpbpNqhPt2LGjzNJGoGXLlpVZ2vCSNhClikt6X9LnLNVN0rWdthqlazCd23QNps/g7Nmzy2wYhmHNmjVllj5L6XpKtZm0teuPf/xjmZH5pQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCZjBw8ePHhY/3Bs7Egfy5vWj3/84zJLt7qnbTOpwjJqfSAdy4knnlhmw5Bv508bUNLzpgpIkq61VDcadRvLod6byurVq8ssbXhJlZO0ASWdh/SezZw5s8xWrVpVZm9/+9vLLG0/SRWP9PrSZpi08Si91+lzlqpGqfK2f//+Mps0aVKZpQ02h3re9DWdtgKlbVCpDnfnnXeW2VvZ4YxLv1QBoImhCgBNDFUAaGKoAkATQxUAmhiqANDElprDcMMNN5TZjTfeWGZLly4ts7QFI916n+oKaetG2tYxDMNw9tlnl1m69X7UbTPpcevXry+ztHUjSbWZtI0lncP0GlL9JZ3f9PrS+5KqVulaS+c21ZdSVePd7353mT3++ONllrYhpUpUqtSkmtmZZ55ZZqn6k97rdP7SdTYMw/Dggw+WWXpPR6U2c2T4pQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCa21BxB8+bNK7NLLrmkzObPn19m5557bplNnjy5zPbs2VNmw5ArC2kjycknn1xmqXqwcePGMrv88stHelyquKRNHgsWLCiz7du3l1mqN51wwglllrbppFpJqumk+lKqE426vSdtYEo1pHQtpXOUqirpGnz66afLLJ33dC2l1/7zn/+8zGbPnl1mw5Brbel4fve735XZo48+Gv8m/zu21ADAUWSoAkATQxUAmhiqANDEUAWAJoYqADRRqfkvufbaa8ts8eLFZZZurX/ggQfK7H3ve188nlQBSVtO7r333jJL1YoZM2aU2c6dO8ss1VHS9pcnn3yyzC6++OIyO+OMM8osvd+p3jRr1qwymzhxYpmlc7R79+4y27VrV5mlOsqGDRvK7LLLLiuzVDVasWJFmaVKyd69e8ssnaPvfe97ZZauwbTd5j3veU+ZLVu2rMw++clPltkw+I49FqjUAMBRZKgCQBNDFQCaGKoA0MRQBYAmhioANBn/3z6At6qf/exnZfbFL36xzNKWlpRdccUV8XjShpBk3LhxZZY2oKTtL6mOMmnSpDLbt29fmaXqyN/+9rcyS9WRs846q8xS9SltXNm/f3+ZvfDCC2W2ZcuWMkuv/aWXXiqzRYsWlVmqxvzpT38qs49+9KNlll5fqlKlCs+mTZvKLFXXlixZUmbf/OY3ywz8UgWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCa6Km+Cd15551H/W/OmTOnzNK6o9SrfOaZZ8pswYIFZTZhwoQye+KJJ8ps5syZZTZ16tQyG7Ubmp4zdVG3bdtWZmlN27Rp08osrZNLj1u/fn2ZpZ5x6rCmdWsrV64ss9SnTR3kv/71r2V20UUXlVlaTfiFL3yhzCDxSxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE3GDqa+xL/+w7GxI30s/BfdddddZZZqHskrr7xSZqnm8dprr5VZqrikSsbevXvLLFVqUq3k0ksvLbN0nKky9Oc//7nM0mtIK+rSerfXX3+9zNJqu7vvvrvMHn744TJbuHBhmaXaTPKBD3ygzG6//faRnhP+ncMZl36pAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiUoNbzp33HFHmb366qtllqo/F154YZmlqsr27dvL7Be/+EWZffCDHyyzVP1Zu3ZtmW3atKnMtmzZUmZpI9Bjjz1WZqn2lLYajR9fL79Kj0vbbeDNQKUGAI4iQxUAmhiqANDEUAWAJoYqADQxVAGgiUoNx41rrrmmzGbNmlVmixYtKrNx48aV2apVq8rskUceKbMDBw6UWarbvO1t9X8DT548uczS9pe0NWbSpEll9olPfKLMNmzYUGZXX311mcGbnUoNABxFhioANDFUAaCJoQoATQxVAGhiqAJAE5Ua3hK++93vltn06dPL7LrrrjsCRwMci1RqAOAoMlQBoImhCgBNDFUAaGKoAkATQxUAmqjUAMBhUKkBgKPIUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNDFUAaGKoAkATQxUAmhiqANDEUAWAJoYqADQxVAGgiaEKAE0MVQBoYqgCQBNDFQCaGKoA0MRQBYAmhioANDFUAaCJoQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBNxh/uPzx48OCRPA4AOOb5pQoATQxVAGhiqAJAE0MVAJoYqgDQxFAFgCaGKgA0MVQBoImhCgBN/g/D0kzFoCVMEgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "DiffusionModelEncoder(\n", + " (conv_in): Convolution(\n", + " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_embed): Sequential(\n", + " (0): Linear(in_features=32, out_features=128, bias=True)\n", + " (1): SiLU()\n", + " (2): Linear(in_features=128, out_features=128, bias=True)\n", + " )\n", + " (down_blocks): ModuleList(\n", + " (0): DownBlock(\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", + " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (1): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Convolution(\n", + " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " (2): AttnDownBlock(\n", + " (attentions): ModuleList(\n", + " (0): AttentionBlock(\n", + " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", + " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", + " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", + " )\n", + " )\n", + " (resnets): ModuleList(\n", + " (0): ResnetBlock(\n", + " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (nonlinearity): SiLU()\n", + " (conv1): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", + " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", + " (conv2): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " )\n", + " (skip_connection): Identity()\n", + " )\n", + " )\n", + " (downsampler): Downsample(\n", + " (op): Convolution(\n", + " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " )\n", + " )\n", + " )\n", + " (out): Sequential(\n", + " (0): Linear(in_features=4096, out_features=512, bias=True)\n", + " (1): ReLU()\n", + " (2): Dropout(p=0.1, inplace=False)\n", + " (3): Linear(in_features=512, out_features=2, bias=True)\n", + " )\n", + ")" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed\n", + "inputlabel = total_val_labels[120] # Check whether it is healthy or diseased\n", + "\n", + "plt.figure(\"input\" + str(inputlabel))\n", + "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "model.eval()\n", + "classifier.eval()" + ] + }, + { + "cell_type": "markdown", + "id": "0cd48c2d", + "metadata": {}, + "source": [ + "### Encoding the input image in noise with the reversed DDIM sampling scheme\n", + "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\\\n", + "We define the number of steps in the noising and denoising process by L.\\\n", + "The encoding process is presented in Equation 6 of the paper \"Diffusion Models for Medical Anomaly Detection\" (https://arxiv.org/pdf/2203.04306.pdf).\n" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "f71e4924", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 49.96it/s]\n" + ] + }, + { + "data": { + "image/png": "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ABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXXz2Isin76NGj3ri12b1GjRox/509e/bI3PXXXy9zJUqU8MYrV64sa4YPH57zC8uBjz76yBu3RuStnHosrK396nlSG9+dizb2v2LFClljadeuXUx/xzk9nm5tis+XL5/MqeMZ1uNg/YJH9+7dvfH7779f1mzevNkbX7lypazZuXOnzCnvvvuuzJUtW1bmOnXq5I3feuutskYdiVHj+87pXx5xTv8CivU8tWzZUubUMSTrV0QaNGjgjT///POyRv1Kh3POLVq0SOaUS+F4QUJCgsylpKR449bRg5zgmx0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXaczExMSYa4oVK+aNW0tUv/32W5lTk1HWIlwrpyxcuFDmnn32WW9cLal2zrlKlSrJnFpQa02PvfzyyzLXt29fb3z16tWypkyZMt74Qw89JGusSci6det649YEW/PmzWVuzpw53nivXr1kzQcffOCNR1nO7Jxzp06d8sat12vJkiVlrmPHjt74kCFDZI2adlS35Zz9PGVnZ3vj1sTlgQMHZK5nz57eeLdu3WSNmkrduHGjrBk3bpzMTZgwwRtXU5rO2YvV27Rp442/+eabsgb/zXrtWcvxzwff7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQPJodACB4cTl6oEavLWp5tLVwVy0ltnL58+eXNWq82vL+++/LnBontxZEv/TSSzJnLYdVWrRoIXNqtL5QoUIx/52srCyZK1eunMypRcK33367rPnyyy9lTh09GDNmjKxRx1Rq1aolaz7++GOZq1ixoswpr7/+usz9/ve/j/n21qxZ441bI97qWIlzzu3fv98b79evn6zJnVv/31ldR9u2bWWNYh1XyJs3r8x16dIl5r81efJkmUtKSor59vDL8uSJS1v6X/hmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMHLdc6aTf75PzQ2wjdq1MgbX7p0qaxR4+7WSHvRokVlLj09XeaUtLQ0mcvIyIj59h599FFv3NqqHm/W3/rNb37jjaempsqa66677ryv6efULy+88sorcf07FvU43H///bJm4MCBMte5c2dvfMeOHbLG+luLFy/2xidOnChr1HOYkpIia5YsWSJz6vhIkyZNZI31fmrXrp03bh2VUc+T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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "L = 200\n", + "current_img = inputimg[None, None, ...].to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "\n", + "\n", + "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", + "for t in progress_bar: # go through the noising process\n", + " with autocast(enabled=False):\n", + " with torch.no_grad():\n", + " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " current_img, _ = scheduler.reversed_step(model_output, t, current_img)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a7c8346a-6296-4800-b978-c10fcdf09779", + "metadata": {}, + "source": [ + "### Denoising process using gradient guidance\n", + "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\\\n", + "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \\\n", + "The scale s is used to amplify the gradient." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.16it/s]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "\n", + "y = torch.tensor(0) # define the desired class label\n", + "scale = 5 # define the desired gradient scale s\n", + "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", + "\n", + "for i in progress_bar: # go through the denoising process\n", + " t = L - i\n", + " with autocast(enabled=True):\n", + " with torch.no_grad():\n", + " model_output = model(\n", + " current_img, timesteps=torch.Tensor((t,)).to(current_img.device)\n", + " ).detach() # this is supposed to be epsilon\n", + "\n", + " with torch.enable_grad():\n", + " x_in = current_img.detach().requires_grad_(True)\n", + " logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device))\n", + " log_probs = F.log_softmax(logits, dim=-1)\n", + " selected = log_probs[range(len(logits)), y.view(-1)]\n", + " a = torch.autograd.grad(selected.sum(), x_in)[0]\n", + " alpha_prod_t = scheduler.alphas_cumprod[t]\n", + " updated_noise = (\n", + " model_output - (1 - alpha_prod_t).sqrt() * scale * a\n", + " ) # update the predicted noise epsilon with the gradient of the classifier\n", + "\n", + " current_img, _ = scheduler.step(updated_noise, t, current_img)\n", + " torch.cuda.empty_cache()\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "# Anomaly Detection\n", + "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "ecffaaf3-a7df-453e-81a9-757113d85084", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy()\n", + "plt.style.use(\"default\")\n", + "plt.imshow(diff, cmap=\"jet\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "jupytext": { + "formats": "py:percent,ipynb" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py new file mode 100644 index 00000000..0b63c5d1 --- /dev/null +++ b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py @@ -0,0 +1,552 @@ +# --- +# jupyter: +# jupytext: +# formats: py:percent,ipynb +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.14.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# %% [markdown] +# # Diffusion Models for Medical Anomaly Detection with Classifier Guidance +# +# This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. +# +# We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\ +# We then tranlsate an input slice to its healthy reconstruction using DDIMs.\ +# Anomaly detection is performed by taking the difference between input and output, as proposed in [1]. +# +# [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 +# +# ## Setup environment + +# %% +# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" +# !python -c "import matplotlib" || pip install -q matplotlib +# !python -c "import seaborn" || pi resblock_updown: bool = False,p install -q seaborn + +# %% [markdown] +# ## Setup imports + +# %% jupyter={"outputs_hidden": false} +# Copyright 2020 MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import time +from typing import Dict +import tempfile +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from monai import transforms +from monai.apps import DecathlonDataset +from monai.config import print_config +from monai.data import DataLoader +from monai.utils import set_determinism +from torch.cuda.amp import GradScaler, autocast +from tqdm import tqdm + +from generative.inferers import DiffusionInferer +from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet +from generative.networks.schedulers.ddim import DDIMScheduler + + +torch.multiprocessing.set_sharing_strategy("file_system") +print_config() + + +# %% [markdown] +# ## Setup data directory + +# %% jupyter={"outputs_hidden": false} +directory = os.environ.get("MONAI_DATA_DIRECTORY") +root_dir = tempfile.mkdtemp() if directory is None else directory + +# %% [markdown] +# ## Set deterministic training for reproducibility + +# %% jupyter={"outputs_hidden": false} +set_determinism(42) + +# %% [markdown] tags=[] +# ## Preprocessing of the BRATS Dataset in 2D slices for training +# We download the BRATS training dataset from the Decathlon dataset. \ +# We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\ +# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_. +# + +# %% [markdown] +# Here we use transforms to augment the training dataset, as usual: +# +# 1. `LoadImaged` loads the hands images from files. +# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. +# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. +# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# +# To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain. + +# %% +channel = 0 # 0 = Flair +assert channel in [0, 1, 2, 3], "Choose a valid channel" + +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image", "label"]), + transforms.EnsureChannelFirstd(keys=["image", "label"]), + transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), + transforms.AddChanneld(keys=["image"]), + transforms.EnsureTyped(keys=["image", "label"]), + transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), + transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), + transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), + transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), + transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), + transforms.Lambdad( + keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() + ), + ] +) + + +def get_batched_2d_axial_slices(data: Dict): + images_3D = data["image"] + batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( + -1 + ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. + slice_label = data["slice_label"] + slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() + return batched_2d_slices, slice_label + + +# %% jupyter={"outputs_hidden": false} + +train_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="training", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=True, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) +print("len train data", len(train_ds)) # this gives the number of patients in the training set + + +train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) +data_2d_slices = [] +data_slice_label = [] +for i, data in enumerate(train_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices.append(b2d) + data_slice_label.append(slice_label2d) + +total_train_slices = torch.cat(data_2d_slices, 0) +total_train_labels = torch.cat(data_slice_label, 0) + +# %% [markdown] tags=[] +# ## Preprocessing of the BRATS Dataset in 2D slices for validation +# We download the BRATS validation dataset from the Decathlon dataset. +# We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_. +# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_. +# + +# %% +val_ds = DecathlonDataset( + root_dir=root_dir, + task="Task01_BrainTumour", + section="validation", # validation + cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + num_workers=4, + download=True, # Set download to True if the dataset hasnt been downloaded yet + seed=0, + transform=train_transforms, +) + + +val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) +data_2d_slices_val = [] +data_slice_label_val = [] +for i, data in enumerate(val_loader_3D): + b2d, slice_label2d = get_batched_2d_axial_slices(data) + data_2d_slices_val.append(b2d) + data_slice_label_val.append(slice_label2d) + +total_val_slices = torch.cat(data_2d_slices_val, 0) +total_val_labels = torch.cat(data_slice_label_val, 0) + + +# %% [markdown] +# ## Define network, scheduler, optimizer, and inferer +# At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using +# the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms +# in the 3rd level (`num_head_channels=64`). +# + +# %% jupyter={"outputs_hidden": false} +device = torch.device("cuda") + +model = DiffusionModelUNet( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(64, 64, 64), + attention_levels=(False, False, True), + num_res_blocks=1, + num_head_channels=64, + with_conditioning=False, +) +model.to(device) + +scheduler = DDIMScheduler(num_train_timesteps=1000) + +optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) + +inferer = DiffusionInferer(scheduler) + + +# %% [markdown] tags=[] +# ## Model training of the diffusion model +# We train our diffusion model for 100 epochs, with a batch size of 32. + +# %% jupyter={"outputs_hidden": false} +n_epochs = 100 +batch_size = 32 +val_interval = 1 +epoch_loss_list = [] +val_epoch_loss_list = [] + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # + + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + optimizer.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: + images = a.to(device) + classes = b.to(device) + + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train diffusion completed, total time: {total_time}.") + +plt.style.use("seaborn-bright") +plt.title("Learning Curves Diffusion Model", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() + + +# %% [markdown] +# ## Check the performance of the diffusion model +# +# We generate a random image from noise to check whether our diffusion model works properly for an image generation task. +# +# + +# %% +model.eval() +noise = torch.randn((1, 1, 64, 64)) +noise = noise.to(device) +scheduler.set_timesteps(num_inference_steps=1000) +with autocast(enabled=True): + image, intermediates = inferer.sample( + input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100 + ) + +chain = torch.cat(intermediates, dim=-1) + +plt.style.use("default") +plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# ## Define the classification model +# First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. +# + +# %% +classifier = DiffusionModelEncoder( + spatial_dims=2, + in_channels=1, + out_channels=2, + num_channels=(32, 64, 64), + attention_levels=(False, True, True), + num_res_blocks=(1, 1, 1), + num_head_channels=64, + with_conditioning=False, +) +classifier.to(device) + + +# %% [markdown] +# ## Model training of the classification model +# We train our classification model for 100 epochs. +# + +# %% +batch_size = 32 +n_epochs = 100 +val_interval = 1 +epoch_loss_list = [] +val_epoch_loss_list = [] +optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) + +classifier.to(device) +weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset + + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + classifier.train() + epoch_loss = 0 + indexes = list(torch.randperm(total_train_slices.shape[0])) + data_train = total_train_slices[indexes] # shuffle the training data + labels_train = total_train_labels[indexes] + subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) + progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) + progress_bar.set_description(f"Epoch {epoch}") + + for step, (a, b) in progress_bar: + images = a.to(device) + classes = b.to(device) + + optimizer_cls.zero_grad(set_to_none=True) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + + with autocast(enabled=False): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image + pred = classifier(noisy_img, timesteps) + loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") + + loss.backward() + optimizer_cls.step() + + epoch_loss += loss.item() + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + print("final step train", step) + + if (epoch + 1) % val_interval == 0: + classifier.eval() + val_epoch_loss = 0 + subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # + progress_bar_val = tqdm(enumerate(subset_2D_val)) + progress_bar_val.set_description(f"Epoch {epoch}") + for step, (a, b) in progress_bar_val: + images = a.to(device) + classes = b.to(device) + timesteps = torch.randint(0, 1, (len(images),)).to( + device + ) # check validation accuracy on the original images, i.e., do not add noise + + with torch.no_grad(): + with autocast(enabled=False): + noise = torch.randn_like(images).to(device) + pred = classifier(images, timesteps) + val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") + + val_epoch_loss += val_loss.item() + _, predicted = torch.max(pred, 1) + progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train completed, total time: {total_time}.") + +## Learning curves for the Classifier + +plt.style.use("seaborn-bright") +plt.title("Learning Curves", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() +# %% [markdown] +# # Image-to-image translation to a healthy subject +# We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. + +# %% + +inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed +inputlabel = total_val_labels[120] # Check whether it is healthy or diseased + +plt.figure("input" + str(inputlabel)) +plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + +model.eval() +classifier.eval() + +# %% [markdown] +# ### Encoding the input image in noise with the reversed DDIM sampling scheme +# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\ +# We define the number of steps in the noising and denoising process by L.\ +# The encoding process is presented in Equation 6 of the paper "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/pdf/2203.04306.pdf). +# + +# %% jupyter={"outputs_hidden": false} +L = 200 +current_img = inputimg[None, None, ...].to(device) +scheduler.set_timesteps(num_inference_steps=1000) + + +progress_bar = tqdm(range(L)) # go back and forth L timesteps +for t in progress_bar: # go through the noising process + with autocast(enabled=False): + with torch.no_grad(): + model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) + current_img, _ = scheduler.reversed_step(model_output, t, current_img) + +plt.style.use("default") +plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# ### Denoising process using gradient guidance +# From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\ +# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \ +# The scale s is used to amplify the gradient. + +# %% + + +y = torch.tensor(0) # define the desired class label +scale = 5 # define the desired gradient scale s +progress_bar = tqdm(range(L)) # go back and forth L timesteps + +for i in progress_bar: # go through the denoising process + t = L - i + with autocast(enabled=True): + with torch.no_grad(): + model_output = model( + current_img, timesteps=torch.Tensor((t,)).to(current_img.device) + ).detach() # this is supposed to be epsilon + + with torch.enable_grad(): + x_in = current_img.detach().requires_grad_(True) + logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device)) + log_probs = F.log_softmax(logits, dim=-1) + selected = log_probs[range(len(logits)), y.view(-1)] + a = torch.autograd.grad(selected.sum(), x_in)[0] + alpha_prod_t = scheduler.alphas_cumprod[t] + updated_noise = ( + model_output - (1 - alpha_prod_t).sqrt() * scale * a + ) # update the predicted noise epsilon with the gradient of the classifier + + current_img, _ = scheduler.step(updated_noise, t, current_img) + torch.cuda.empty_cache() + +plt.style.use("default") +plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + + +# %% [markdown] +# # Anomaly Detection +# To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction. + + +# %% + +diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() +plt.style.use("default") +plt.imshow(diff, cmap="jet") +plt.tight_layout() +plt.axis("off") +plt.show() From df74d7d8aae89228dbb462b79a4c0e5ba6ee1fe0 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 15 Mar 2023 12:22:00 +0100 Subject: [PATCH 11/23] remove old folder --- ...tection_tutorial_classifier_guidance.ipynb | 2913 ----------------- ...ydetection_tutorial_classifier_guidance.py | 552 ---- 2 files changed, 3465 deletions(-) delete mode 100644 tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb delete mode 100644 tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py diff --git a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb deleted file mode 100644 index e335e271..00000000 --- a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb +++ /dev/null @@ -1,2913 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "63d95da6", - "metadata": {}, - "source": [ - "# Diffusion Models for Medical Anomaly Detection with Classifier Guidance\n", - "\n", - "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", - "\n", - "We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\\\n", - "We then tranlsate an input slice to its healthy reconstruction using DDIMs.\\\n", - "Anomaly detection is performed by taking the difference between input and output, as proposed in [1].\n", - "\n", - "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", - "\n", - "## Setup environment" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "id": "75f2d5f3", - "metadata": {}, - "outputs": [], - "source": [ - "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", - "!python -c \"import matplotlib\" || pip install -q matplotlib\n", - "!python -c \"import seaborn\" || pi resblock_updown: bool = False,p install -q seaborn" - ] - }, - { - "cell_type": "markdown", - "id": "6b766027", - "metadata": {}, - "source": [ - "## Setup imports" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "972ed3f3", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/anomaly_detection/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", - "MONAI version: 1.2.dev2304\n", - "Numpy version: 1.23.2\n", - "Pytorch version: 1.12.1\n", - "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", - "MONAI rev id: 9a57be5aab9f2c2a134768c0c146399150e247a0\n", - "MONAI __file__: /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/monai/__init__.py\n", - "\n", - "Optional dependencies:\n", - "Pytorch Ignite version: 0.4.10\n", - "ITK version: 5.3.0\n", - "Nibabel version: 4.0.1\n", - "scikit-image version: 0.19.3\n", - "Pillow version: 9.2.0\n", - "Tensorboard version: 2.12.0\n", - "gdown version: 4.6.4\n", - "TorchVision version: 0.13.1\n", - "tqdm version: 4.64.1\n", - "lmdb version: 1.4.0\n", - "psutil version: 5.9.4\n", - "pandas version: 1.5.3\n", - "einops version: 0.6.0\n", - "transformers version: 4.21.3\n", - "mlflow version: 2.1.1\n", - "pynrrd version: 1.0.0\n", - "\n", - "For details about installing the optional dependencies, please visit:\n", - " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", - "\n" - ] - } - ], - "source": [ - "# Copyright 2020 MONAI Consortium\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "# http://www.apache.org/licenses/LICENSE-2.0\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License.\n", - "import os\n", - "import sys\n", - "import time\n", - "from typing import Dict\n", - "import tempfile\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import torch\n", - "import torch.nn.functional as F\n", - "from monai import transforms\n", - "from monai.apps import DecathlonDataset\n", - "from monai.config import print_config\n", - "from monai.data import DataLoader\n", - "from monai.utils import first, set_determinism\n", - "from torch.cuda.amp import GradScaler, autocast\n", - "from tqdm import tqdm\n", - "\n", - "from generative.inferers import DiffusionInferer\n", - "from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet\n", - "from generative.networks.schedulers.ddim import DDIMScheduler\n", - "\n", - "\n", - "torch.multiprocessing.set_sharing_strategy(\"file_system\")\n", - "print_config()" - ] - }, - { - "cell_type": "markdown", - "id": "7d4ff515", - "metadata": {}, - "source": [ - "## Setup data directory" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "8b4323e7", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", - "root_dir = tempfile.mkdtemp() if directory is None else directory" - ] - }, - { - "cell_type": "markdown", - "id": "99175d50", - "metadata": {}, - "source": [ - "## Set deterministic training for reproducibility" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "34ea510f", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [], - "source": [ - "set_determinism(42)" - ] - }, - { - "cell_type": "markdown", - "id": "c3f70dd1-236a-47ff-a244-575729ad92ba", - "metadata": { - "tags": [] - }, - "source": [ - "## Preprocessing of the BRATS Dataset in 2D slices for training\n", - "We download the BRATS training dataset from the Decathlon dataset. \\\n", - "We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\\\n", - "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_.\n" - ] - }, - { - "cell_type": "markdown", - "id": "6986f55c", - "metadata": {}, - "source": [ - "Here we use transforms to augment the training dataset, as usual:\n", - "\n", - "1. `LoadImaged` loads the hands images from files.\n", - "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", - "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", - "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", - "\n", - "To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - ": Class `AddChannel` has been deprecated since version 0.8. please use MetaTensor data type and monai.transforms.EnsureChannelFirst instead.\n" - ] - } - ], - "source": [ - "channel = 0 # 0 = Flair\n", - "assert channel in [0, 1, 2, 3], \"Choose a valid channel\"\n", - "\n", - "train_transforms = transforms.Compose(\n", - " [\n", - " transforms.LoadImaged(keys=[\"image\", \"label\"]),\n", - " transforms.EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n", - " transforms.Lambdad(keys=[\"image\"], func=lambda x: x[channel, :, :, :]),\n", - " transforms.AddChanneld(keys=[\"image\"]),\n", - " transforms.EnsureTyped(keys=[\"image\", \"label\"]),\n", - " transforms.Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\"),\n", - " transforms.Spacingd(keys=[\"image\", \"label\"], pixdim=(3.0, 3.0, 2.0), mode=(\"bilinear\", \"nearest\")),\n", - " transforms.CenterSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 64)),\n", - " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", - " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", - " transforms.Lambdad(\n", - " keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze()\n", - " ),\n", - " ]\n", - ")\n", - "\n", - "def get_batched_2d_axial_slices(data: Dict):\n", - " images_3D = data[\"image\"]\n", - " batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", - " slice_label = data[\"slice_label\"]\n", - " slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze()\n", - " return batched_2d_slices, slice_label\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "da1927b0", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2023-03-13 16:09:17,074 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-03-13 16:09:17,075 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-03-13 16:09:17,076 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n", - "len train data 388\n" - ] - } - ], - "source": [ - "\n", - "train_ds = DecathlonDataset(\n", - " root_dir=root_dir,\n", - " task=\"Task01_BrainTumour\",\n", - " section=\"training\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", - " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", - " seed=0,\n", - " transform=train_transforms,\n", - ")\n", - "print(\"len train data\", len(train_ds)) #this gives the number of patients in the training set\n", - "\n", - "\n", - "\n", - "train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", - "data_2d_slices = []\n", - "data_slice_label = []\n", - "for i, data in enumerate(train_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices.append(b2d)\n", - " data_slice_label.append(slice_label2d)\n", - " \n", - "total_train_slices = torch.cat(data_2d_slices, 0)\n", - "total_train_labels = torch.cat(data_slice_label, 0)" - ] - }, - { - "cell_type": "markdown", - "id": "fac55e9d", - "metadata": { - "tags": [] - }, - "source": [ - "## Preprocessing of the BRATS Dataset in 2D slices for validation\n", - "We download the BRATS validation dataset from the Decathlon dataset. \n", - "We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_.\n", - "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2023-03-13 16:19:38,821 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-03-13 16:19:38,824 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-03-13 16:19:38,826 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n" - ] - } - ], - "source": [ - "val_ds = DecathlonDataset(\n", - " root_dir=root_dir,\n", - " task=\"Task01_BrainTumour\",\n", - " section=\"validation\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", - " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", - " seed=0,\n", - " transform=train_transforms,\n", - ")\n", - "\n", - "\n", - "val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", - "data_2d_slices_val = []\n", - "data_slice_label_val = []\n", - "for i, data in enumerate(val_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices_val.append(b2d)\n", - " data_slice_label_val.append(slice_label2d)\n", - "\n", - "total_val_slices = torch.cat(data_2d_slices_val, 0)\n", - "total_val_labels = torch.cat(data_slice_label_val, 0)\n" - ] - }, - { - "cell_type": "markdown", - "id": "08428bc6", - "metadata": {}, - "source": [ - "## Define network, scheduler, optimizer, and inferer\n", - "At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", - "the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms\n", - "in the 3rd level (`num_head_channels=64`).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "bee5913e", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "device = torch.device(\"cuda\")\n", - "\n", - "model = DiffusionModelUNet(\n", - " spatial_dims=2,\n", - " in_channels=1,\n", - " out_channels=1,\n", - " num_channels=(64, 64, 64),\n", - " attention_levels=(False, False, True),\n", - " num_res_blocks=1,\n", - " num_head_channels=64,\n", - " with_conditioning=False,\n", - ")\n", - "model.to(device)\n", - "\n", - "scheduler = DDIMScheduler(num_train_timesteps=1000)\n", - "\n", - "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", - "\n", - "inferer = DiffusionInferer(scheduler)" - ] - }, - { - "cell_type": "markdown", - "id": "2a4d3ab2", - "metadata": { - "tags": [] - }, - "source": [ - "## Model training of the diffusion model\n", - "We train our diffusion model for 100 epochs, with a batch size of 32." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "6c0ed909", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 534it [01:42, 5.21it/s, loss=0.163] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 1: : 534it [01:46, 4.99it/s, loss=0.0234] \n", - "4224it [01:27, 48.47it/s]\n", - "Epoch 2: : 534it [01:47, 4.96it/s, loss=0.0207] \n", - "4224it [01:26, 48.76it/s]\n", - "Epoch 3: : 534it [01:46, 4.99it/s, loss=0.0199] \n", - "4224it [01:24, 49.73it/s]\n", - "Epoch 4: : 534it [01:46, 5.00it/s, loss=0.0198] \n", - "4224it [01:26, 48.90it/s]\n", - "Epoch 5: : 534it [01:46, 5.00it/s, loss=0.0192] \n", - "4224it [01:26, 48.97it/s]\n", - "Epoch 6: : 534it [01:47, 4.98it/s, loss=0.0199] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 7: : 534it [01:47, 4.99it/s, loss=0.0188] \n", - "4224it [01:26, 48.65it/s]\n", - "Epoch 8: : 534it [01:47, 4.95it/s, loss=0.0184] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 9: : 534it [01:47, 4.98it/s, loss=0.0179] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 10: : 534it [01:47, 4.98it/s, loss=0.0183] \n", - "4224it [01:25, 49.12it/s]\n", - "Epoch 11: : 534it [01:47, 4.98it/s, loss=0.0183] \n", - "4224it [01:26, 48.83it/s]\n", - "Epoch 12: : 534it [01:48, 4.94it/s, loss=0.0182] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 13: : 534it [01:47, 4.95it/s, loss=0.0185] \n", - "4224it [01:27, 48.52it/s]\n", - "Epoch 14: : 534it [01:47, 4.95it/s, loss=0.0176] \n", - "4224it [01:27, 48.54it/s]\n", - "Epoch 15: : 534it [01:47, 4.95it/s, loss=0.018] \n", - "4224it [01:26, 48.60it/s]\n", - "Epoch 16: : 534it [01:47, 4.99it/s, loss=0.0181] \n", - "4224it [01:27, 48.10it/s]\n", - "Epoch 17: : 534it [01:47, 4.95it/s, loss=0.0179] \n", - "4224it [01:28, 47.99it/s]\n", - "Epoch 18: : 534it [01:47, 4.97it/s, loss=0.0177] \n", - "4224it [01:26, 48.73it/s]\n", - "Epoch 19: : 534it [01:47, 4.97it/s, loss=0.0179] \n", - "4224it [01:28, 47.86it/s]\n", - "Epoch 20: : 534it [01:47, 4.95it/s, loss=0.0177] \n", - "4224it [01:28, 47.48it/s]\n", - "Epoch 21: : 534it [01:47, 4.95it/s, loss=0.0175] \n", - "4224it [01:27, 48.23it/s]\n", - "Epoch 22: : 534it [01:47, 4.95it/s, loss=0.0171] \n", - "4224it [01:24, 49.97it/s]\n", - "Epoch 23: : 534it [01:47, 4.96it/s, loss=0.0169] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 24: : 534it [01:47, 4.98it/s, loss=0.0172] \n", - "4224it [01:27, 48.49it/s]\n", - "Epoch 25: : 534it [01:47, 4.99it/s, loss=0.0168] \n", - "4224it [01:25, 49.39it/s]\n", - "Epoch 26: : 534it [01:46, 5.00it/s, loss=0.0169] \n", - "4224it [01:26, 48.62it/s]\n", - "Epoch 27: : 534it [01:47, 4.98it/s, loss=0.0171] \n", - "4224it [01:27, 48.43it/s]\n", - "Epoch 28: : 534it [01:47, 4.97it/s, loss=0.0175] \n", - "4224it [01:25, 49.18it/s]\n", - "Epoch 29: : 534it [01:46, 5.01it/s, loss=0.0171] \n", - "4224it [01:25, 49.59it/s]\n", - "Epoch 30: : 534it [01:47, 4.95it/s, loss=0.017] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 31: : 534it [01:47, 4.99it/s, loss=0.0169] \n", - "4224it [01:25, 49.12it/s]\n", - "Epoch 32: : 534it [01:46, 4.99it/s, loss=0.0168] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 33: : 534it [01:46, 5.00it/s, loss=0.0166] \n", - "4224it [01:26, 48.82it/s]\n", - "Epoch 34: : 534it [01:47, 4.97it/s, loss=0.0173] \n", - "4224it [01:27, 48.49it/s]\n", - "Epoch 35: : 534it [01:46, 4.99it/s, loss=0.0169] \n", - "4224it [01:26, 48.66it/s]\n", - "Epoch 36: : 534it [01:47, 4.99it/s, loss=0.0171] \n", - "4224it [01:26, 48.92it/s]\n", - "Epoch 37: : 534it [01:47, 4.97it/s, loss=0.0166] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 38: : 534it [01:47, 4.99it/s, loss=0.0163] \n", - "4224it [01:27, 48.55it/s]\n", - "Epoch 39: : 534it [01:47, 4.97it/s, loss=0.0166] \n", - "4224it [01:26, 48.55it/s]\n", - "Epoch 40: : 534it [01:46, 5.00it/s, loss=0.0169] \n", - "4224it [01:25, 49.31it/s]\n", - "Epoch 41: : 534it [01:47, 4.99it/s, loss=0.0169] \n", - "4224it [01:26, 48.85it/s]\n", - "Epoch 42: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 43: : 534it [01:46, 4.99it/s, loss=0.0171] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 44: : 534it [01:47, 4.99it/s, loss=0.0167] \n", - "4224it [01:27, 48.53it/s]\n", - "Epoch 45: : 534it [01:46, 5.00it/s, loss=0.0167] \n", - "4224it [01:27, 48.40it/s]\n", - "Epoch 46: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 47: : 534it [01:47, 4.99it/s, loss=0.0162] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 48: : 534it [01:46, 5.00it/s, loss=0.017] \n", - "4224it [01:27, 48.50it/s]\n", - "Epoch 49: : 534it [01:47, 4.98it/s, loss=0.0164] \n", - "4224it [01:27, 48.21it/s]\n", - "Epoch 50: : 534it [01:47, 4.97it/s, loss=0.0168] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 51: : 534it [01:47, 4.98it/s, loss=0.0163] \n", - "4224it [01:27, 48.10it/s]\n", - "Epoch 52: : 534it [01:47, 4.97it/s, loss=0.0158] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 53: : 534it [01:47, 4.96it/s, loss=0.0163] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 54: : 534it [01:47, 4.96it/s, loss=0.0157] \n", - "4224it [01:27, 48.03it/s]\n", - "Epoch 55: : 534it [01:47, 4.99it/s, loss=0.0164] \n", - "4224it [01:27, 48.19it/s]\n", - "Epoch 56: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.46it/s]\n", - "Epoch 57: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:27, 48.47it/s]\n", - "Epoch 58: : 534it [01:47, 4.97it/s, loss=0.017] \n", - "4224it [01:27, 48.46it/s]\n", - "Epoch 59: : 534it [01:47, 4.98it/s, loss=0.0164] \n", - "4224it [01:27, 48.38it/s]\n", - "Epoch 60: : 534it [01:47, 4.94it/s, loss=0.0165] \n", - "4224it [01:27, 48.27it/s]\n", - "Epoch 61: : 534it [01:47, 4.96it/s, loss=0.0164] \n", - "4224it [01:27, 48.50it/s]\n", - "Epoch 62: : 534it [01:47, 4.97it/s, loss=0.0164] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 63: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:27, 48.06it/s]\n", - "Epoch 64: : 534it [01:47, 4.97it/s, loss=0.0163] \n", - "4224it [01:27, 48.35it/s]\n", - "Epoch 65: : 534it [01:47, 4.98it/s, loss=0.0159] \n", - "4224it [01:27, 48.53it/s]\n", - "Epoch 66: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 67: : 534it [01:47, 4.97it/s, loss=0.0164] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 68: : 534it [01:48, 4.94it/s, loss=0.016] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 69: : 534it [01:47, 4.98it/s, loss=0.0156] \n", - "4224it [01:27, 48.34it/s]\n", - "Epoch 70: : 534it [01:47, 4.98it/s, loss=0.0162] \n", - "4224it [01:26, 48.96it/s]\n", - "Epoch 71: : 534it [01:47, 4.97it/s, loss=0.0159] \n", - "4224it [01:25, 49.55it/s]\n", - "Epoch 72: : 534it [01:47, 4.97it/s, loss=0.0159] \n", - "4224it [01:27, 48.08it/s]\n", - "Epoch 73: : 534it [01:47, 4.97it/s, loss=0.0165] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 74: : 534it [01:47, 4.98it/s, loss=0.0161] \n", - "4224it [01:27, 48.33it/s]\n", - "Epoch 75: : 534it [01:46, 5.00it/s, loss=0.0164] \n", - "4224it [01:27, 48.20it/s]\n", - "Epoch 76: : 534it [01:47, 4.95it/s, loss=0.0165] \n", - "4224it [01:26, 48.73it/s]\n", - "Epoch 77: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:27, 48.45it/s]\n", - "Epoch 78: : 534it [01:47, 4.95it/s, loss=0.0158] \n", - "4224it [01:27, 48.42it/s]\n", - "Epoch 79: : 534it [01:47, 4.96it/s, loss=0.0163] \n", - "4224it [01:26, 48.85it/s]\n", - "Epoch 80: : 534it [01:47, 4.96it/s, loss=0.0156] \n", - "4224it [01:27, 48.52it/s]\n", - "Epoch 81: : 534it [01:47, 4.97it/s, loss=0.0158] \n", - "4224it [01:27, 48.44it/s]\n", - "Epoch 82: : 534it [01:47, 4.97it/s, loss=0.0163] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 83: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:27, 48.24it/s]\n", - "Epoch 84: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 85: : 534it [01:47, 4.99it/s, loss=0.0153] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 86: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.54it/s]\n", - "Epoch 87: : 534it [01:47, 4.96it/s, loss=0.0159] \n", - "4224it [01:27, 48.22it/s]\n", - "Epoch 88: : 534it [01:47, 4.96it/s, loss=0.0159] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 89: : 534it [01:47, 4.95it/s, loss=0.0164] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 90: : 534it [01:47, 4.96it/s, loss=0.0161] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 91: : 534it [01:47, 4.95it/s, loss=0.0158] \n", - "4224it [01:26, 48.94it/s]\n", - "Epoch 92: : 534it [01:47, 4.96it/s, loss=0.0158] \n", - "4224it [01:26, 48.65it/s]\n", - "Epoch 93: : 534it [01:47, 4.96it/s, loss=0.0166] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 94: : 534it [01:47, 4.98it/s, loss=0.0161] \n", - "4224it [01:26, 48.78it/s]\n", - "Epoch 95: : 534it [01:48, 4.94it/s, loss=0.0155] \n", - "4224it [01:26, 48.95it/s]\n", - "Epoch 96: : 534it [01:47, 4.98it/s, loss=0.0162] \n", - "4224it [01:26, 48.96it/s]\n", - "Epoch 97: : 534it [01:47, 4.97it/s, loss=0.016] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 98: : 534it [01:47, 4.98it/s, loss=0.016] \n", - "4224it [01:27, 48.48it/s]\n", - "Epoch 99: : 534it [01:47, 4.98it/s, loss=0.0157] \n", - "4224it [01:27, 48.33it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train diffusion completed, total time: 19490.821256637573.\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk8AAAHZCAYAAACfEN+tAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAACCZ0lEQVR4nO3dd3gU1eLG8e+m90DoISFIDR1UmiK9CyoIQkSlKV7EwlXxikoTvGLBCz8Erw2JAkGkWJAi3YJ0KaF3AhECBNL7zu+P3KxZ0knIZuX9PM8+4sw5M2cmm+y755yZMRmGYSAiIiIiheJg6waIiIiI2BOFJxEREZEiUHgSERERKQKFJxEREZEiUHgSERERKQKFJxEREZEiUHgSERERKQKFJxEREZEiUHgSERERKQKFJ7mtDBs2DJPJRM2aNW3dFJFiqVmzJiaTiWHDhuVZJikpiSlTptCsWTM8PT0xmUyYTCbGjh1rVe7cuXM8/fTT1K5dGzc3N0u5b7/99pYeQ2FNnjzZ0iYpO0rj59KxY0dMJhMdO3a8Zfu4GQpPdmrz5s2WN+3kyZNt3RwpIyIiInjvvffo3r07d9xxB15eXri7u1O9enV69OjBtGnTOH36tK2beVs5c+aM5Xc1+8vR0ZFy5coRFBREmzZtGDNmDF999RXx8fElst+0tDS6du3K5MmT2b9/P4mJibmWO3fuHHfddReffPIJp06dIiUlpUT2L7nL/rfbZDLh7e2d588mu6SkJHx9fa3qbt68+dY3WHLlZOsGiEjxpaSk8NprrzFnzpxcP/wiIyOJjIzkp59+YuLEiQwcOJD333+fwMBAG7RWAMxmMzExMcTExHDu3Dm2b9/O3Llz8fb25sknn2Tq1Kl4enre9Pa/+eYbtm7dCmT2uA4dOpSKFSsCWP4LMG3aNK5cuYKTkxNvvfUW7du3x8vLC4CgoKBiHKEURnx8PN9++y2PPvpovuW+++47YmNjS6lVUhCFJ7mtzJ8/n/nz59u6GSXq6tWrPPDAA5YPSm9vb0JCQujSpQsBAQE4Oztz8eJFfvvtN5YvX87x48dZsmQJbdu2zTF8I7fWgw8+yLRp0yz/n5iYyPXr1zl06BBbtmxh5cqVxMXF8Z///Icff/yRlStXUrdu3Vy3debMmXz3tX79egCqVq3KZ599hqOjY77lHnroIV555ZWbOKpbb/LkyX/LHnY3NzeSk5P56quvCgxPX331lVUdsS2FJxE7ZjabGTx4sCU49e7dmy+++ILKlSvnKNu3b1/+/e9/s2DBAsaNG1faTRWgXLlyNG7cOMfy7t27M3bsWM6dO8eTTz7JunXrOHbsGH369GH79u2UK1euyPu6cOECALVq1cozOGUvV69evSLvQ4rngQceYMmSJaxbt46LFy9StWrVXMtFRUXx008/AZkB/Ouvvy7NZkouNOdJxI7Nnj3b0nPQtWtXvvvuu1yDUxYHBweeeOIJdu/eTdOmTUurmVJINWrUYPXq1dx///0AHDt27KZ7XLKGb52dnfMtl5qaWqhyUvK6d+9O1apVycjIICwsLM9yYWFhpKenU6VKFbp161aKLZS8KDzd5nbs2MFTTz1FvXr18PLywtPTk+DgYMaMGcPx48fzrXvq1ClmzJhB3759qVmzJu7u7ri7uxMUFMSgQYNYs2ZNvvXnz59vmfh45swZUlJSmDlzJm3atKFixYpWk+FvLGs2m/nkk0+45557KF++PJ6enjRt2pS33nor38mXBV1td+Mk/J07dxISEkJAQACurq5Ur16dxx9/nMOHD+d7bAAJCQm8+eabNGnSBE9PTypUqEC7du2YN28ehmFYTRy9mYmfaWlpvPfee0BmV/4XX3yBk1PhOpMDAgLo3Lmz1bLCXol448/iRjdeBbZ7926GDRvGHXfcgaurq+XKnNq1a2MymWjXrl2B7b148SJOTk6YTCZeeumlXMukp6fz+eef07t3b/z9/XF1daVixYq0b9+emTNnFjjUsXv3bkaOHEm9evXw9PTEzc2NwMBA7rrrLsaMGcP333+PYRgFtrW4HB0dmT9/Ph4eHgB8+umnXLlyJUe53K62yz45fcuWLQBs2bLFapJxzZo1rX6GWaZMmWJVLvt2C3NlHxT8HsrIyGD+/Pn06NGDqlWr4uLiQrly5ahbty5dunTh3//+N4cOHcpRr7BXdZ05c4Z//vOfNGrUCG9vbzw8PKhbty5PP/00Bw4cyLduSf7uF5ajoyMhISHAX8Nyufnyyy8BePTRR/PtRcwuNTWVuXPn0qlTJypVqoSLiwtVq1ald+/eLFiwALPZXOA2zp8/z5gxY6hVqxZubm74+/vzwAMPWL6wFVZiYiIzZ86kU6dOVKlSBRcXFypXrkz37t354osvyMjIKNL2ygRD7NKmTZsMwACMSZMmFbl+WlqaMXr0aMs2cns5Ozsbn3zySa71T506lW/drNdjjz1mpKWl5bqNL774wlJu586dRvPmzXPUzzq27GXDw8ONzp0757nPVq1aGfHx8bnuc+jQoQZgBAUF5bo++35nz55tODk55boPDw8PY8uWLXme33Pnzhl16tTJs419+vQxfvrpJ8v/b9q0Kc9t5eWHH36wOs/FVdC5yZL9Z3H69Okc64OCggzAGDp0qPHRRx/leg4NwzDeeOMNAzBMJlOu28nuP//5j6Xu7t27c6w/ceKE0bBhw3zfi3Xr1jWOHTuW6/Y/+OADw8HBocD3c1xcXL7tzM3p06ct9YcOHVroeqNGjbLUW7hwYY712c9zbvvK6xUUFGT1M8zrlX27ue0rN/m9h+Li4oz77ruvwP0+/PDDOepOmjTJ6r2Tm9DQUMPV1TXP7To6Ohr//ve/86xfUr/7Bcn+t/uLL74w9uzZY/W37UYHDx60rN+zZ4/Vzy6vvxtnzpwxGjRokO95bteunXH16tU827l582bDx8cnz/pTpkwp1M9lx44dRvXq1fNtS6tWrYyLFy/mWr9Dhw4GYHTo0CHf81raNOfpNjVy5EjLt5levXoxZMgQ6tWrh8lkYu/evcycOZODBw8yatQoqlatSt++fa3qZ2Rk4OLiQo8ePejWrRsNGzbEz8+P6Ohojh07xpw5czh48CALFiygVq1aTJkypcD2HDhwgCeeeIJBgwZRtWpVzp07h6ura46yo0aNYtu2bQwdOpRHHnnEUvbdd9/l999/Z8eOHUybNo233377ps/P2rVr2b59O02bNuWFF16gSZMmJCUlsWLFCmbNmkViYiKPP/44x48fx8XFxapuamoqvXv35sSJE5bzO2rUKAIDAzl//jyffPIJK1eu5PLlyzfdPsDSswDQp0+fYm3rVti5cycLFiwgMDCQl19+mbvuuouMjAx++eUXAIYMGcK0adMwDINFixbx2muv5bmthQsXAhAcHMydd95pte7PP//k3nvv5dKlS3h7ezNq1Ci6du1KlSpViImJ4aeffmLWrFkcP36cnj17smfPHnx9fS319+/fz8svv4zZbOaOO+7g2WefpXnz5vj5+REfH8/x48fZtGkTK1asuAVnKW9du3blk08+AeCXX34pcEIxQPXq1S09LMOHD2fXrl3cfffdfPHF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6NixIy4uLowdO5YNGzbg4eEB6DwX15kzZ+jbty9VqlRh6dKlVKpUie3btzNt2jTi4+P5/PPPLWV1rm+dwp7b0vgZKDyVIRUqVMg1FUdHRwO5p24pmilTpjBt2jTeeustnn32WcvyChUqALn37kVHR2MymShXrlxpNdNuxcfHM2bMGJ577jn8/f25fv06AKmpqQBcv34dZ2dnne8SUKFCBY4fP06PHj2slvfq1YuxY8eyZ88eHnzwQUDnubheffVVYmNj2bt3r6VHo3379lSsWJERI0bwxBNPULVqVUDn+lYoyt+LChUqkJycTGJiouXLQ/ayN35hvlkatitDmjRpwuHDh0lPT7dafuDAAQAaN25si2b9bUyZMoXJkyczefJkXnvtNat1tWvXxt3d3XKusztw4AB16tTBzc2ttJpqt65cucKlS5eYMWMG5cuXt7zCwsJISEigfPnyDBkyROe7BOQ2/wOw3KbAwcFB57mE7N27l4YNG+YYCmrZsiWAZThP5/rWKMq5zZrrdGPZixcvcuXKlRL7HFV4KkP69etHfHw8y5Yts1oeGhqKv78/rVu3tlHL7N/UqVOZPHkyb7zxBpMmTcqx3snJib59+7J8+XLi4uIsy8+dO8emTZvo379/aTbXblWtWpVNmzblePXo0QM3Nzc2bdrEtGnTdL5LwMMPPwzA6tWrrZavWrUKgDZt2ug8lxB/f38OHjxIfHy81fLff/8dgICAAJ3rW6go57Znz564ubkxf/58q23Mnz8fk8mU772kiqREbnggJaZbt25G+fLljU8++cTYuHGj8dRTTxmAsWDBAls3zW69//77BmD07NnT+P3333O8shw+fNjw8vIy2rdvb6xatcpYvny50bhxY8Pf39+Iioqy4RHYv9zu86TzXXx9+/Y1XF1djalTpxrr1q0z3n77bcPNzc3o06ePpYzOc/F99913hslkMtq0aWN8/fXXxoYNG4y33nrL8PLyMho2bGikpKQYhqFzfbNWrVplfPPNN8a8efMMwBg4cKDxzTffGN98842RkJBgGEbRzu20adMMk8lkvPbaa8bmzZuN9957z3B1dTWeeuqpEmuzwlMZExcXZzz//PNG1apVDRcXF6Np06ZGWFiYrZtl1zp06GAAeb6y27Vrl9GlSxfDw8PD8PHxMR566KEcN8aTosstPBmGzndxJSYmGv/6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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "n_epochs = 100\n", - "batch_size = 32\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", - "\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(\n", - " inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) \n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - " if (epoch) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - "\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " progress_bar.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train diffusion completed, total time: {total_time}.\")\n", - "\n", - "plt.style.use(\"seaborn-bright\")\n", - "plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "326101ed-333b-44a9-933f-55760b5d93a4", - "metadata": {}, - "source": [ - "## Check the performance of the diffusion model\n", - "\n", - "We generate a random image from noise to check whether our diffusion model works properly for an image generation task.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "8f7a9e99-a8a4-4c8f-a42f-17ef91b18585", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|███████████████████████████████████████| 1000/1000 [00:10<00:00, 95.94it/s]\n" - ] - }, - { - "data": { - "image/png": "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LS0oLi5GTk4OUlNTERMTg/j4eGg0GtUE2my92tjY9FlKyppfF6NXabb0ii06OpqIiEaPHk2enp5kNBpp3rx5REQUGRlJRER/+9vfqKqqiiorK+n333+ngIAA+qd/+ify8PCg6dOn0//+3/+biIimT59ORETV1dX8/gcOHCAiop9++on+8R//kXbt2kVERPX19fT48WMKDw8nnU5HH3/8MQ0cOJC0Wi398ssv/Pf/+q//SitXriQnJyd6++23adGiRfy7iIgI/v+5c+dSZ2cnNTc3082bN4mIyGg00vnz52nw4ME0evRoIiJavXo1/fzzz5SRkUH//M//TAaDgebMmUNz586lEydO0Lx582jw4MG0Y8cOIiJydnYmf39/8vb2pk8++YRSU1PJaDQSEZFer+fPt7e35/+fNGkSDRw4kIiIAgIC6Pfff6cnT56QTqejkydPEhHRe++9p5iLpUuXkr+/P926dYv++te/8s/d3d0l45mXl0elpaX04MEDIiIaOHAg7dmzh4iIVq1aRURE169fp61bt9J/+S//hXQ6HU2cOJFu375NXl5eZDAY6O7duzRq1Cj68ccfiYjIbDZTXFwcff7553Tu3DnauHGjYoyJiBITE/n/P/74Y0pJSaHbt2/zz7777juKj4+nZ8+e0fPnz+nx48dERPTbb7/xa9544w0aOHAg2draUklJCdnY2PDv/P39iYjoyZMndPnyZWptbaWrV68SEZHJZKLPPvuMHj9+TL29vRQSEkJEFl7y9/enfv36cd5l9/zuu+/oX/7lX6i0tJSIiFJSUqi2tpYiIiJo165ddOjQISIimjVrFhER5efnExGRo6Mj3bp1i4iI/v3f/50SEhL4GNfV1dGdO3dIr9dTeno6XblyhZYtW0ZExMeTiOh//s//SatWrSKNRkP/63/9L/4sIqLQ0FD+/zfffJNGjx5NU6ZMod27dxMRUXh4OF27do0GDBhAOTk5fNx+++03SklJoZEjR5KXlxf9y7/8C+3cuZN++uknGjNmDHV2dtKmTZvI1dWV/vKXv1BiYiKZTCY6d+4c6XQ6amlpISKiqqoq/vxvvvmG/7+1tZU6OzuJiCgvL4+uX79Ozs7OZDab6cMPPyS9Xk8rVqyQzGdBQQFt3LiR/P396cKFC/Tf//t/59+5urry/+/YsYMGDRpElZWV/Pd1dXX0wQcf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2Xc7Oztx4MABiVGhlp6ePqxb5sqVK/xvsRxRSkqKVCqGMef8cf78eQnwnjhxgi8ASjbBVTkRGheii+yXX37BP//5T9y6dQsDAwOwWCyqDZqbmxsAZ9WEW7duSX3Jx8dn2KLZohie7hFpeMRyNYw5N38nT57Ehg0beKDEpUuXVNovasqFXNnEsmYXLlzA6dOnATj1YX19fQgLC1OlowCcLDqx9q7GobJR5CdjTMW47Nq1SzVvvvnmm5ILt6WlRWJKR7K+0AK/adMmzhDm5eVh27ZtuHz5MhwOB2fzKW8htZaWFpSXl6OwsFDl4k9KSnKZ34oxWS8o5mWLj49XMUk1NTUoKSlBbm4uz+EkjgulrnE4997cuXMlAFhTU4P6+nqe8oYxdQJwHx8fPPvsswgNDcXChQsl92pgYKBmhLby/tJxxPeUXifSgXl6enJywsvLyyUAeprrUTy+yWTitQw9PDwwevRonlNL/A5tqJTpOBhTM3EjMbf/DADddNNNN91000033f7L5v5/fQK66aabbrrppptu/19NB1q66aabbrrppptuf5LpQEs33XTTTTfddNPtTzIdaOmmm2666aabbrr9SaYDLd1000033XTTTbc/yXSgpZtuuummm2666fYnmQ60dNNNN91000033f4k04GWbrrppptuuumm259kOtDSTTfddNNNN910+5PsfwAmd61phvc9EwAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "model.eval()\n", - "noise = torch.randn((1, 1, 64, 64))\n", - "noise = noise.to(device)\n", - "scheduler.set_timesteps(num_inference_steps=1000)\n", - "with autocast(enabled=True):\n", - " image, intermediates = inferer.sample(\n", - " input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100\n", - " )\n", - "\n", - "chain = torch.cat(intermediates, dim=-1)\n", - "\n", - "plt.style.use(\"default\")\n", - "plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "546f9983-c2e2-4c24-b03a-ebe34627638a", - "metadata": {}, - "source": [ - "## Define the classification model\n", - "First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "44cc6928-2525-4e61-8805-15b409097bbb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "DiffusionModelEncoder(\n", - " (conv_in): Convolution(\n", - " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_embed): Sequential(\n", - " (0): Linear(in_features=32, out_features=128, bias=True)\n", - " (1): SiLU()\n", - " (2): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " (down_blocks): ModuleList(\n", - " (0): DownBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", - " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (1): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (2): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " )\n", - " (out): Sequential(\n", - " (0): Linear(in_features=4096, out_features=512, bias=True)\n", - " (1): ReLU()\n", - " (2): Dropout(p=0.1, inplace=False)\n", - " (3): Linear(in_features=512, out_features=2, bias=True)\n", - " )\n", - ")" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "classifier = DiffusionModelEncoder(\n", - " spatial_dims=2,\n", - " in_channels=1,\n", - " out_channels=2,\n", - " num_channels=(32, 64, 64),\n", - " attention_levels=(False, True, True),\n", - " num_res_blocks=(1,1,1),\n", - " num_head_channels=64,\n", - " with_conditioning=False,\n", - ")\n", - "classifier.to(device)\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "45fab83a-b4c8-42cb-96c9-4e9f1e191111", - "metadata": {}, - "source": [ - "## Model training of the classification model\n", - "We train our classification model for 100 epochs.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", - "metadata": { - "lines_to_next_cell": 0 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 534it [00:24, 22.16it/s, loss=0.671] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 17it [00:00, 65.41it/s, val_loss=0.288]\n", - "Epoch 1: : 534it [00:24, 21.99it/s, loss=0.612] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 1: : 17it [00:00, 66.80it/s, val_loss=0.363]\n", - "Epoch 2: : 534it [00:24, 21.92it/s, loss=0.586] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 2: : 17it [00:00, 68.07it/s, val_loss=0.226]\n", - "Epoch 3: : 534it [00:26, 20.48it/s, loss=0.581] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 3: : 17it [00:00, 63.17it/s, val_loss=0.217]\n", - "Epoch 4: : 534it [00:25, 20.99it/s, loss=0.579] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 4: : 17it [00:00, 63.70it/s, val_loss=0.211]\n", - "Epoch 5: : 534it [00:26, 20.46it/s, loss=0.572] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 5: : 17it [00:00, 63.46it/s, val_loss=0.234]\n", - "Epoch 6: : 534it [00:25, 20.66it/s, loss=0.577] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 6: : 17it [00:00, 63.53it/s, val_loss=0.306]\n", - "Epoch 7: : 534it [00:26, 20.39it/s, loss=0.57] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 7: : 17it [00:00, 62.97it/s, val_loss=0.372]\n", - "Epoch 8: : 534it [00:25, 20.72it/s, loss=0.572] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 8: : 17it [00:00, 63.76it/s, val_loss=0.208]\n", - "Epoch 9: : 534it [00:26, 20.18it/s, loss=0.565] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 9: : 17it [00:00, 61.70it/s, val_loss=0.245]\n", - "Epoch 10: : 534it [00:26, 20.22it/s, loss=0.563] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 10: : 17it [00:00, 63.48it/s, val_loss=0.181]\n", - "Epoch 11: : 534it [00:26, 20.42it/s, loss=0.564] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 11: : 17it [00:00, 64.20it/s, val_loss=0.196]\n", - "Epoch 12: : 534it [00:26, 20.35it/s, loss=0.562] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 12: : 17it [00:00, 64.27it/s, val_loss=0.235]\n", - "Epoch 13: : 534it [00:26, 20.31it/s, loss=0.562] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 13: : 17it [00:00, 62.05it/s, val_loss=0.2] \n", - "Epoch 14: : 534it [00:26, 20.35it/s, loss=0.557] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 14: : 17it [00:00, 63.59it/s, val_loss=0.232]\n", - "Epoch 15: : 534it [00:26, 20.25it/s, loss=0.558] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 15: : 17it [00:00, 62.56it/s, val_loss=0.236]\n", - "Epoch 16: : 534it [00:26, 20.39it/s, loss=0.559] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 16: : 17it [00:00, 62.08it/s, val_loss=0.227]\n", - "Epoch 17: : 534it [00:26, 20.44it/s, loss=0.561] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 17: : 17it [00:00, 61.93it/s, val_loss=0.232]\n", - "Epoch 18: : 534it [00:26, 20.10it/s, loss=0.556] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 18: : 17it [00:00, 61.19it/s, val_loss=0.265]\n", - "Epoch 19: : 534it [00:26, 20.52it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 19: : 17it [00:00, 61.85it/s, val_loss=0.214]\n", - "Epoch 20: : 534it [00:26, 20.13it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 20: : 17it [00:00, 62.12it/s, val_loss=0.304]\n", - "Epoch 21: : 534it [00:26, 20.33it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 21: : 17it [00:00, 60.91it/s, val_loss=0.235]\n", - "Epoch 22: : 534it [00:26, 20.19it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 22: : 17it [00:00, 62.88it/s, val_loss=0.232]\n", - "Epoch 23: : 534it [00:26, 20.24it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 23: : 17it [00:00, 62.73it/s, val_loss=0.146]\n", - "Epoch 24: : 534it [00:26, 20.32it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 24: : 17it [00:00, 62.44it/s, val_loss=0.223]\n", - "Epoch 25: : 534it [00:26, 20.20it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 25: : 17it [00:00, 62.95it/s, val_loss=0.286]\n", - "Epoch 26: : 534it [00:26, 20.24it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 26: : 17it [00:00, 63.56it/s, val_loss=0.316]\n", - "Epoch 27: : 534it [00:26, 20.20it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 27: : 17it [00:00, 61.08it/s, val_loss=0.217]\n", - "Epoch 28: : 534it [00:26, 20.18it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 28: : 17it [00:00, 63.45it/s, val_loss=0.155]\n", - "Epoch 29: : 534it [00:26, 20.30it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 29: : 17it [00:00, 62.70it/s, val_loss=0.227]\n", - "Epoch 30: : 534it [00:25, 20.61it/s, loss=0.55] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 30: : 17it [00:00, 66.44it/s, val_loss=0.2] \n", - "Epoch 31: : 534it [00:26, 20.32it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 31: : 17it [00:00, 61.60it/s, val_loss=0.258]\n", - "Epoch 32: : 534it [00:26, 20.40it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 32: : 17it [00:00, 63.44it/s, val_loss=0.17] \n", - "Epoch 33: : 534it [00:26, 20.37it/s, loss=0.546] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 33: : 17it [00:00, 62.44it/s, val_loss=0.197]\n", - "Epoch 34: : 534it [00:26, 20.23it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 34: : 17it [00:00, 64.16it/s, val_loss=0.227]\n", - "Epoch 35: : 534it [00:26, 20.28it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 35: : 17it [00:00, 61.64it/s, val_loss=0.182]\n", - "Epoch 36: : 534it [00:26, 20.24it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 36: : 17it [00:00, 62.97it/s, val_loss=0.189]\n", - "Epoch 37: : 534it [00:26, 20.37it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 37: : 17it [00:00, 63.50it/s, val_loss=0.232]\n", - "Epoch 38: : 534it [00:26, 20.30it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 38: : 17it [00:00, 62.30it/s, val_loss=0.175]\n", - "Epoch 39: : 534it [00:26, 20.25it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 39: : 17it [00:00, 62.73it/s, val_loss=0.219]\n", - "Epoch 40: : 534it [00:26, 20.17it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 40: : 17it [00:00, 62.13it/s, val_loss=0.169]\n", - "Epoch 41: : 534it [00:26, 20.06it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 41: : 17it [00:00, 61.03it/s, val_loss=0.153]\n", - "Epoch 42: : 534it [00:26, 20.06it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 42: : 17it [00:00, 62.17it/s, val_loss=0.18] \n", - "Epoch 43: : 534it [00:26, 20.04it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 43: : 17it [00:00, 61.85it/s, val_loss=0.168]\n", - "Epoch 44: : 534it [00:26, 19.98it/s, loss=0.542] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 44: : 17it [00:00, 61.28it/s, val_loss=0.181]\n", - "Epoch 45: : 534it [00:26, 20.16it/s, loss=0.542] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 45: : 17it [00:00, 63.26it/s, val_loss=0.154]\n", - "Epoch 46: : 534it [00:26, 20.08it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 46: : 17it [00:00, 61.43it/s, val_loss=0.151]\n", - "Epoch 47: : 534it [00:26, 20.06it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 47: : 17it [00:00, 62.66it/s, val_loss=0.174]\n", - "Epoch 48: : 534it [00:26, 20.27it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 48: : 17it [00:00, 62.88it/s, val_loss=0.148]\n", - "Epoch 49: : 534it [00:26, 20.32it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 49: : 17it [00:00, 62.37it/s, val_loss=0.178]\n", - "Epoch 50: : 534it [00:26, 20.24it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 50: : 17it [00:00, 62.16it/s, val_loss=0.203]\n", - "Epoch 51: : 534it [00:26, 20.33it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 51: : 17it [00:00, 63.13it/s, val_loss=0.178]\n", - "Epoch 52: : 534it [00:26, 20.37it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 52: : 17it [00:00, 63.45it/s, val_loss=0.191]\n", - "Epoch 53: : 534it [00:26, 20.32it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 53: : 17it [00:00, 62.24it/s, val_loss=0.182]\n", - "Epoch 54: : 534it [00:26, 20.10it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 54: : 17it [00:00, 63.44it/s, val_loss=0.184]\n", - "Epoch 55: : 534it [00:26, 19.94it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 55: : 17it [00:00, 62.61it/s, val_loss=0.165]\n", - "Epoch 56: : 534it [00:26, 20.19it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 56: : 17it [00:00, 61.80it/s, val_loss=0.175]\n", - "Epoch 57: : 534it [00:26, 20.07it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 57: : 17it [00:00, 62.74it/s, val_loss=0.164]\n", - "Epoch 58: : 534it [00:26, 20.27it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 58: : 17it [00:00, 62.64it/s, val_loss=0.159]\n", - "Epoch 59: : 534it [00:26, 20.23it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 59: : 17it [00:00, 63.27it/s, val_loss=0.166]\n", - "Epoch 60: : 534it [00:26, 20.21it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 60: : 17it [00:00, 62.98it/s, val_loss=0.146]\n", - "Epoch 61: : 534it [00:26, 20.03it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 61: : 17it [00:00, 61.23it/s, val_loss=0.153]\n", - "Epoch 62: : 534it [00:26, 20.15it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 62: : 17it [00:00, 62.14it/s, val_loss=0.18] \n", - "Epoch 63: : 534it [00:26, 20.22it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 63: : 17it [00:00, 61.99it/s, val_loss=0.152]\n", - "Epoch 64: : 534it [00:26, 20.04it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 64: : 17it [00:00, 61.32it/s, val_loss=0.14] \n", - "Epoch 65: : 534it [00:26, 20.25it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 65: : 17it [00:00, 63.32it/s, val_loss=0.145]\n", - "Epoch 66: : 534it [00:26, 20.14it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 66: : 17it [00:00, 61.50it/s, val_loss=0.154]\n", - "Epoch 67: : 534it [00:26, 20.09it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 67: : 17it [00:00, 59.68it/s, val_loss=0.148]\n", - "Epoch 68: : 534it [00:26, 20.25it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 68: : 17it [00:00, 63.40it/s, val_loss=0.172]\n", - "Epoch 69: : 534it [00:26, 20.34it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 69: : 17it [00:00, 61.41it/s, val_loss=0.211]\n", - "Epoch 70: : 534it [00:26, 20.22it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 70: : 17it [00:00, 60.88it/s, val_loss=0.158]\n", - "Epoch 71: : 534it [00:26, 20.51it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 71: : 17it [00:00, 62.84it/s, val_loss=0.129]\n", - "Epoch 72: : 534it [00:26, 20.30it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 72: : 17it [00:00, 63.48it/s, val_loss=0.197]\n", - "Epoch 73: : 534it [00:26, 20.27it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 73: : 17it [00:00, 62.99it/s, val_loss=0.158]\n", - "Epoch 74: : 534it [00:26, 20.17it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 74: : 17it [00:00, 62.28it/s, val_loss=0.147]\n", - "Epoch 75: : 534it [00:26, 20.25it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 75: : 17it [00:00, 63.89it/s, val_loss=0.131]\n", - "Epoch 76: : 534it [00:26, 20.34it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 76: : 17it [00:00, 61.53it/s, val_loss=0.155]\n", - "Epoch 77: : 534it [00:26, 20.15it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 77: : 17it [00:00, 61.50it/s, val_loss=0.158]\n", - "Epoch 78: : 534it [00:26, 20.20it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 78: : 17it [00:00, 62.59it/s, val_loss=0.153]\n", - "Epoch 79: : 534it [00:26, 20.19it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 79: : 17it [00:00, 61.60it/s, val_loss=0.162]\n", - "Epoch 80: : 534it [00:26, 20.31it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 80: : 17it [00:00, 63.66it/s, val_loss=0.181]\n", - "Epoch 81: : 534it [00:26, 20.48it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 81: : 17it [00:00, 63.58it/s, val_loss=0.216]\n", - "Epoch 82: : 534it [00:26, 20.11it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 82: : 17it [00:00, 60.27it/s, val_loss=0.139]\n", - "Epoch 83: : 534it [00:26, 20.29it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 83: : 17it [00:00, 62.75it/s, val_loss=0.202]\n", - "Epoch 84: : 534it [00:26, 20.10it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 84: : 17it [00:00, 60.65it/s, val_loss=0.148]\n", - "Epoch 85: : 534it [00:26, 20.23it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 85: : 17it [00:00, 63.67it/s, val_loss=0.153]\n", - "Epoch 86: : 534it [00:26, 20.20it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 86: : 17it [00:00, 63.29it/s, val_loss=0.153]\n", - "Epoch 87: : 534it [00:26, 20.26it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 87: : 17it [00:00, 63.14it/s, val_loss=0.148]\n", - "Epoch 88: : 534it [00:26, 20.04it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 88: : 17it [00:00, 63.95it/s, val_loss=0.194]\n", - "Epoch 89: : 534it [00:26, 20.19it/s, loss=0.527] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 89: : 17it [00:00, 62.93it/s, val_loss=0.175]\n", - "Epoch 90: : 534it [00:26, 20.35it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 90: : 17it [00:00, 63.62it/s, val_loss=0.173]\n", - "Epoch 91: : 534it [00:26, 20.25it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 91: : 17it [00:00, 63.33it/s, val_loss=0.167]\n", - "Epoch 92: : 534it [00:26, 20.20it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 92: : 17it [00:00, 61.01it/s, val_loss=0.183]\n", - "Epoch 93: : 534it [00:26, 20.18it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 93: : 17it [00:00, 61.76it/s, val_loss=0.179]\n", - "Epoch 94: : 534it [00:26, 20.31it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 94: : 17it [00:00, 63.02it/s, val_loss=0.152]\n", - "Epoch 95: : 534it [00:26, 20.17it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 95: : 17it [00:00, 63.23it/s, val_loss=0.148]\n", - "Epoch 96: : 534it [00:26, 20.11it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 96: : 17it [00:00, 63.35it/s, val_loss=0.154]\n", - "Epoch 97: : 534it [00:26, 20.35it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 97: : 17it [00:00, 62.97it/s, val_loss=0.17] \n", - "Epoch 98: : 534it [00:26, 20.25it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 98: : 17it [00:00, 62.36it/s, val_loss=0.138]\n", - "Epoch 99: : 534it [00:26, 20.22it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 99: : 17it [00:00, 63.30it/s, val_loss=0.193]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train completed, total time: 2708.850436449051.\n" - ] - }, - { - "data": { - "image/png": "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QQ0bNkSLFi3w77//4uLFi1i5ciWUSiVGjBgBgB9+Sk5ORqtWrawyJkePHkVmZmahz/3YY49Bp9Ph6NGjVrOwzGYz/v33X6vXXLt2DYGBgVbBUEZGBo4fP261v5BcKEiGpmnTpgBgc8mQvXv3AgCaNGni8PFcye0DorNnz6Ju3bpiKk3QqFEj8Xl79u3bh7p162LdunV47LHHoFQqER4ejvfee89th8wAoG5kbtfqvaeA9ML/GyKEEOJEQuPFF198EdevX0fv3r3FGpyQkBB4eHjg+PHjyMjIEF+TmJhY5A/mGo0Gzz33HB48eICFCxfKnvvuu+9s1g9FRkYiMTER586dE7eZTCa8/fbbePjQuuYrMJAfWrxz547D1yUEg7Nnz0ZKSoq4PSUlBbNnz5bt4+7cfsgsPj4e1atXt9ou/MXFx8fbfe3du3fx8OFDvPHGG5gzZw7q1auHv//+GwsWLEBMTAx++uknu6/V6/WysVXpX3Rx4zigV0tg6Z98cfWek0Cf1iV2ekIIIXZERUXhrbfewoEDBwDIO1MrFAq8+uqrWLhwIRo3box+/fohJSUFW7ZsQWRkJMLCwop07gULFuDvv//GtGnT8M8//6Bp06a4cOECNm/ejO7du2P79u2y/V9//XVs374dbdu2xXPPPQedToc9e/bg7t276Nixo1VWp3Xr1vDw8MDnn3+OlJQUMcv13nvv2b2m9u3b4/XXX8fixYvRoEEDPPPMM2CMYf369YiJicEbb7wh1le5O7fPEAGQdcosyHNmsxmpqan46quvMH78eHTq1Alz587F66+/jtWrV+Pq1at2Xzt//nz4+fmJX/batReXni1zH9OwGSGEuAdfX188++yzAPii4T59+sienz9/PubNmweO4/DVV19hx44dGDJkCLZv316k6e0Av3TGv//+i8GDB+PgwYNYtGgR4uPjsWPHDrRubf2puW/fvvjtt99QvXp1rFq1CqtXr0adOnVw+PBhREZaF6cGBgbit99+Q61atfD1119jypQpmDJlSr7X9cUXX2D58uUIDQ3FsmXL8O233yI0NBTLly/HokWLivQzlySOMRttN91I69atYTKZcPjwYdn2c+fOoUGDBli6dKnd6fOVKlXC/fv3kZCQgICAAHH79u3b0aNHD/zyyy947rnnbL7WVoYoIiICycnJJVIglpoBBD0FGIxA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CCJMoYJECywLLe8SuvFCLzIwjKjwonNJSQQWYRYRqEEQWFamSSXigVTMXE9lh/e7iB3abzXtb2kyzw8WlnfmnXlnLpaH3ZnZ0NBQWbBggfj7+0thYaGMj4//sZk1Ij89C56IiIhIhXgNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxER0QxpNBr+4znRPMdARESzKjAwUAkQv3r9//8WERH9Tc5zPQARqVNISAiWLVs26XIvL69ZnIaI1I6BiIjmxJEjR7B79+65HoOICAB/MiMiIiJiICKif9/PFy1XVFRg1apVMBgM8PT0RGpqKl6+fDlp78jICIqLixEZGQm9Xg93d3esXr0ap06dwtevXyft+/DhAwoLCxETEwN3d3cYDAaYzWbs378fT58+nbSvuroa8fHxMBqN8PDwwObNmyddv729HTk5OQgKCoKLiwuMRiOCgoKwbds2XLt2bZpnh4j+CCEimkUBAQECQM6fPz/tHgACQEpKSgSAeHt7y8qVK8VoNAoAWbhwodTV1Tn09fb2isViEQCi1WolMjJSzGazsr3ExEQZGxtz6Hv27Jn4+voqfeHh4RIdHS3u7u4CQLKysiac78yZM6LRaMTHx0diY2NFr9cLADEYDNLc3GzX09bWJkuWLBEA4ubmJhaLRaKjo8XT01MASFRU1LTPDxHNHAMREc2qmQQinU4npaWl8u3bNxERGRkZkV27dgkACQgIkNHRUbu+9PR0ASARERHy5s0bpf7kyRPx8vISAHL48GG7nsHBQfH39xcAkpKSIh0dHXbLa2tr5cqVKxPO5+bmZndcVqtVEhISBIDs2LHDricvL08JV0NDQ3bLmpubpby8fNrnh4hmjoGIiGaVLRBN9RoYGFB6bLWtW7c6bO/z58/i7e0tAOTcuXNKvbW1VTQajQCQhoYGh77KykoBIHq9XqxWq1I/duyYABCz2SyfPn2a1jHZ5jt48KDDsufPnwsA8fDwsKsnJycLAGlsbJzWPojo7+JdZkQ0J6a67d7Z2fHjKTc316G2YMECZGdno7i4GDU1NdizZw8A4P79+xARrF27FjExMQ596enpMJlM6OzsxKNHj5CSkgIAqKqqAgDk5+fDxcXlt44pOzvboWaxWODq6orBwUH09/dj8eLFAAA/Pz8AwI0bN2CxWPhgR6I5xkBERHPiv9x2bzabf1lvbW1Varb34eHhE/ZotVqEhYWhs7MTra2tSiBqbm4GAMTFxf3WbAAQHBw8YX3p0qXo6OjA8PCwEohyc3Nx8eJFFBUV4dKlS0hJScG6deuwceNG+Pr6/va+iWhmeJcZEc0bk32jZHuI49DQkFIbHh7+Zc9kfVarFQCwaNGi355Pr9dPWNdqf3zUiohSi46ORm1tLZKSktDV1YXy8nJkZGTAZDIhOTlZCWZENDsYiIho3ujr65uw3tvbCwAwGo1KzWAw2C2bSE9Pj0Of7f3Hjx9nNOt0xMXFoaamBgMDA7h79y4KCgpgMplw7949JCYmzsoMRPQDAxERzRuTfWtiq4eGhio12/tXr15N2PP9+3e0tLQ49EVERAAAHj9+PPOBp8lgMCA5ORlHjx5FS0sLgoOD0dXVherq6lmbgUjtGIiIaN44ffq0Q218fBxnz54FACQlJSn1pKQkaDQa1NfXT/hgxJs3b6KzsxN6vR5r1qxR6qmpqQCAkydPYnx8/A8fwdTc3NxgsVgAAN3d3bO+fyK1YiAionnjzp07OHHihHItztjYGPbu3Yvu7m74+flh586dyrrLly9HWloaACAzMxPv3r1TljU0NODQoUMAgLy8PLufzPbt24eAgAA0NTUhLS0NXV1ddjPU19fj6tWrMz6WAwcO4Pr16xgdHbWr19bW4sGDBwCA2NjYGe+HiKZHIz9f5UdE9JcFBgaivb19ytvut2/froQW2y3pJSUlKCgogLe3N/z8/PD69WtYrVa4urqipqYG8fHxdtvo6+tDQkICXrx4AScnJ6xYsQJfvnxRfkbbtGkTbt++DVdXV7u+xsZGpKSk4P3799BqtTCbzdDpdGhra8Pg4CCysrJw4cIFZX3bfJN9nNqOua2tDYGBgQB+XFTd2NgIZ2dnhISEwGg0oqenB+3t7QCAjIwMXL58eZpnlYhmioGIiGaVLRxMJT8/H2VlZQDsA0dFRQXKysrQ1NQEnU6H9evXo6ioCJGRkRNuZ2RkBMePH0dlZSXevn0LrVaL8PBwZGZmIicnBzqdbsK+/v5+lJaW4tatW2hra4OTkxNMJhM2bNiAnJwcREVFKev+l0D08OFDVFVVoa6uDh0dHRgcHISPjw/CwsKQm5uLLVu28NlERLOIgYiI/nlTBQ4iopniNURERESkegxEREREpHoMRERERKR6DERERESkevxzVyL65/FiaiL62/gNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqd7/AL3d+lWfpCm1AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "batch_size = 32\n", - "n_epochs = 100\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", - "\n", - "classifier.to(device)\n", - "weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset\n", - "\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " classifier.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - "\n", - " for step, (a, b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - "\n", - " optimizer_cls.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - "\n", - " with autocast(enabled=False):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image\n", - " pred = classifier(noisy_img, timesteps)\n", - " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", - "\n", - " loss.backward()\n", - " optimizer_cls.step()\n", - "\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - " print(\"final step train\", step)\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " classifier.eval()\n", - " val_epoch_loss = 0\n", - " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " timesteps = torch.randint(0, 1, (len(images),)).to(\n", - " device\n", - " ) # check validation accuracy on the original images, i.e., do not add noise\n", - "\n", - " with torch.no_grad():\n", - " with autocast(enabled=False):\n", - " noise = torch.randn_like(images).to(device)\n", - " pred = classifier(images, timesteps)\n", - " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " _, predicted = torch.max(pred, 1)\n", - " progress_bar_val.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train completed, total time: {total_time}.\")\n", - "\n", - " ## Learning curves for the Classifier\n", - "\n", - "plt.style.use(\"seaborn-bright\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "a676b3fe", - "metadata": {}, - "source": [ - "# Image-to-image translation to a healthy subject\n", - "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "DiffusionModelEncoder(\n", - " (conv_in): Convolution(\n", - " (conv): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_embed): Sequential(\n", - " (0): Linear(in_features=32, out_features=128, bias=True)\n", - " (1): SiLU()\n", - " (2): Linear(in_features=128, out_features=128, bias=True)\n", - " )\n", - " (down_blocks): ModuleList(\n", - " (0): DownBlock(\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=32, bias=True)\n", - " (norm2): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(32, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (1): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 32, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Convolution(\n", - " (conv): Conv2d(32, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " (2): AttnDownBlock(\n", - " (attentions): ModuleList(\n", - " (0): AttentionBlock(\n", - " (norm): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (to_q): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_k): Linear(in_features=64, out_features=64, bias=True)\n", - " (to_v): Linear(in_features=64, out_features=64, bias=True)\n", - " (proj_attn): Linear(in_features=64, out_features=64, bias=True)\n", - " )\n", - " )\n", - " (resnets): ModuleList(\n", - " (0): ResnetBlock(\n", - " (norm1): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (nonlinearity): SiLU()\n", - " (conv1): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (time_emb_proj): Linear(in_features=128, out_features=64, bias=True)\n", - " (norm2): GroupNorm(32, 64, eps=1e-06, affine=True)\n", - " (conv2): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", - " )\n", - " (skip_connection): Identity()\n", - " )\n", - " )\n", - " (downsampler): Downsample(\n", - " (op): Convolution(\n", - " (conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " )\n", - " )\n", - " )\n", - " (out): Sequential(\n", - " (0): Linear(in_features=4096, out_features=512, bias=True)\n", - " (1): ReLU()\n", - " (2): Dropout(p=0.1, inplace=False)\n", - " (3): Linear(in_features=512, out_features=2, bias=True)\n", - " )\n", - ")" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed\n", - "inputlabel = total_val_labels[120] # Check whether it is healthy or diseased\n", - "\n", - "plt.figure(\"input\" + str(inputlabel))\n", - "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.axis(\"off\")\n", - "plt.tight_layout()\n", - "plt.show()\n", - "\n", - "model.eval()\n", - "classifier.eval()" - ] - }, - { - "cell_type": "markdown", - "id": "0cd48c2d", - "metadata": {}, - "source": [ - "### Encoding the input image in noise with the reversed DDIM sampling scheme\n", - "In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\\\n", - "We define the number of steps in the noising and denoising process by L.\\\n", - "The encoding process is presented in Equation 6 of the paper \"Diffusion Models for Medical Anomaly Detection\" (https://arxiv.org/pdf/2203.04306.pdf).\n" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "f71e4924", - "metadata": { - "collapsed": false, - "jupyter": { - "outputs_hidden": false - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 49.96it/s]\n" - ] - }, - { - "data": { - "image/png": "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ABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXXz2Isin76NGj3ri12b1GjRox/509e/bI3PXXXy9zJUqU8MYrV64sa4YPH57zC8uBjz76yBu3RuStnHosrK396nlSG9+dizb2v2LFClljadeuXUx/xzk9nm5tis+XL5/MqeMZ1uNg/YJH9+7dvfH7779f1mzevNkbX7lypazZuXOnzCnvvvuuzJUtW1bmOnXq5I3feuutskYdiVHj+87pXx5xTv8CivU8tWzZUubUMSTrV0QaNGjgjT///POyRv1Kh3POLVq0SOaUS+F4QUJCgsylpKR449bRg5zgmx0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXaczExMSYa4oVK+aNW0tUv/32W5lTk1HWIlwrpyxcuFDmnn32WW9cLal2zrlKlSrJnFpQa02PvfzyyzLXt29fb3z16tWypkyZMt74Qw89JGusSci6det649YEW/PmzWVuzpw53nivXr1kzQcffOCNR1nO7Jxzp06d8sat12vJkiVlrmPHjt74kCFDZI2adlS35Zz9PGVnZ3vj1sTlgQMHZK5nz57eeLdu3WSNmkrduHGjrBk3bpzMTZgwwRtXU5rO2YvV27Rp442/+eabsgb/zXrtWcvxzwff7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQPJodACB4cTl6oEavLWp5tLVwVy0ltnL58+eXNWq82vL+++/LnBontxZEv/TSSzJnLYdVWrRoIXNqtL5QoUIx/52srCyZK1eunMypRcK33367rPnyyy9lTh09GDNmjKxRx1Rq1aolaz7++GOZq1ixoswpr7/+usz9/ve/j/n21qxZ441bI97qWIlzzu3fv98b79evn6zJnVv/31ldR9u2bWWNYh1XyJs3r8x16dIl5r81efJkmUtKSor59vDL8uSJS1v6X/hmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMHLdc6aTf75PzQ2wjdq1MgbX7p0qaxR4+7WSHvRokVlLj09XeaUtLQ0mcvIyIj59h599FFv3NqqHm/W3/rNb37jjaempsqa66677ryv6efULy+88sorcf07FvU43H///bJm4MCBMte5c2dvfMeOHbLG+luLFy/2xidOnChr1HOYkpIia5YsWSJz6vhIkyZNZI31fmrXrp03bh2VUc+T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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "L = 200\n", - "current_img = inputimg[None, None, ...].to(device)\n", - "scheduler.set_timesteps(num_inference_steps=1000)\n", - "\n", - "\n", - "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", - "for t in progress_bar: # go through the noising process\n", - " with autocast(enabled=False):\n", - " with torch.no_grad():\n", - " model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device))\n", - " current_img, _ = scheduler.reversed_step(model_output, t, current_img)\n", - "\n", - "plt.style.use(\"default\")\n", - "plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "a7c8346a-6296-4800-b978-c10fcdf09779", - "metadata": {}, - "source": [ - "### Denoising process using gradient guidance\n", - "From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\\\n", - "Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \\\n", - "The scale s is used to amplify the gradient." - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", - "metadata": { - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.16it/s]\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "y = torch.tensor(0) # define the desired class label\n", - "scale = 5 # define the desired gradient scale s\n", - "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", - "\n", - "for i in progress_bar: # go through the denoising process\n", - " t = L - i\n", - " with autocast(enabled=True):\n", - " with torch.no_grad():\n", - " model_output = model(\n", - " current_img, timesteps=torch.Tensor((t,)).to(current_img.device)\n", - " ).detach() # this is supposed to be epsilon\n", - "\n", - " with torch.enable_grad():\n", - " x_in = current_img.detach().requires_grad_(True)\n", - " logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device))\n", - " log_probs = F.log_softmax(logits, dim=-1)\n", - " selected = log_probs[range(len(logits)), y.view(-1)]\n", - " a = torch.autograd.grad(selected.sum(), x_in)[0]\n", - " alpha_prod_t = scheduler.alphas_cumprod[t]\n", - " updated_noise = (\n", - " model_output - (1 - alpha_prod_t).sqrt() * scale * a\n", - " ) # update the predicted noise epsilon with the gradient of the classifier\n", - "\n", - " current_img, _ = scheduler.step(updated_noise, t, current_img)\n", - " torch.cuda.empty_cache()\n", - "\n", - "plt.style.use(\"default\")\n", - "plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap=\"gray\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "d2e343f8-c6f3-4071-a5e6-771e2343c3bc", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "# Anomaly Detection\n", - "To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction." - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "ecffaaf3-a7df-453e-81a9-757113d85084", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy()\n", - "plt.style.use(\"default\")\n", - "plt.imshow(diff, cmap=\"jet\")\n", - "plt.tight_layout()\n", - "plt.axis(\"off\")\n", - "plt.show()" - ] - } - ], - "metadata": { - "jupytext": { - "formats": "py:percent,ipynb" - }, - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py deleted file mode 100644 index 0b63c5d1..00000000 --- a/tutorials/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py +++ /dev/null @@ -1,552 +0,0 @@ -# --- -# jupyter: -# jupytext: -# formats: py:percent,ipynb -# text_representation: -# extension: .py -# format_name: percent -# format_version: '1.3' -# jupytext_version: 1.14.4 -# kernelspec: -# display_name: Python 3 (ipykernel) -# language: python -# name: python3 -# --- - -# %% [markdown] -# # Diffusion Models for Medical Anomaly Detection with Classifier Guidance -# -# This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. -# -# We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\ -# We then tranlsate an input slice to its healthy reconstruction using DDIMs.\ -# Anomaly detection is performed by taking the difference between input and output, as proposed in [1]. -# -# [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 -# -# ## Setup environment - -# %% -# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" -# !python -c "import matplotlib" || pip install -q matplotlib -# !python -c "import seaborn" || pi resblock_updown: bool = False,p install -q seaborn - -# %% [markdown] -# ## Setup imports - -# %% jupyter={"outputs_hidden": false} -# Copyright 2020 MONAI Consortium -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import os -import time -from typing import Dict -import tempfile -import matplotlib.pyplot as plt -import numpy as np -import torch -import torch.nn.functional as F -from monai import transforms -from monai.apps import DecathlonDataset -from monai.config import print_config -from monai.data import DataLoader -from monai.utils import set_determinism -from torch.cuda.amp import GradScaler, autocast -from tqdm import tqdm - -from generative.inferers import DiffusionInferer -from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet -from generative.networks.schedulers.ddim import DDIMScheduler - - -torch.multiprocessing.set_sharing_strategy("file_system") -print_config() - - -# %% [markdown] -# ## Setup data directory - -# %% jupyter={"outputs_hidden": false} -directory = os.environ.get("MONAI_DATA_DIRECTORY") -root_dir = tempfile.mkdtemp() if directory is None else directory - -# %% [markdown] -# ## Set deterministic training for reproducibility - -# %% jupyter={"outputs_hidden": false} -set_determinism(42) - -# %% [markdown] tags=[] -# ## Preprocessing of the BRATS Dataset in 2D slices for training -# We download the BRATS training dataset from the Decathlon dataset. \ -# We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\ -# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_. -# - -# %% [markdown] -# Here we use transforms to augment the training dataset, as usual: -# -# 1. `LoadImaged` loads the hands images from files. -# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. -# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. -# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. -# -# To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain. - -# %% -channel = 0 # 0 = Flair -assert channel in [0, 1, 2, 3], "Choose a valid channel" - -train_transforms = transforms.Compose( - [ - transforms.LoadImaged(keys=["image", "label"]), - transforms.EnsureChannelFirstd(keys=["image", "label"]), - transforms.Lambdad(keys=["image"], func=lambda x: x[channel, :, :, :]), - transforms.AddChanneld(keys=["image"]), - transforms.EnsureTyped(keys=["image", "label"]), - transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), - transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), - transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), - transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), - transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), - transforms.Lambdad( - keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() - ), - ] -) - - -def get_batched_2d_axial_slices(data: Dict): - images_3D = data["image"] - batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( - -1 - ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. - slice_label = data["slice_label"] - slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() - return batched_2d_slices, slice_label - - -# %% jupyter={"outputs_hidden": false} - -train_ds = DecathlonDataset( - root_dir=root_dir, - task="Task01_BrainTumour", - section="training", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise - num_workers=4, - download=True, # Set download to True if the dataset hasnt been downloaded yet - seed=0, - transform=train_transforms, -) -print("len train data", len(train_ds)) # this gives the number of patients in the training set - - -train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) -data_2d_slices = [] -data_slice_label = [] -for i, data in enumerate(train_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices.append(b2d) - data_slice_label.append(slice_label2d) - -total_train_slices = torch.cat(data_2d_slices, 0) -total_train_labels = torch.cat(data_slice_label, 0) - -# %% [markdown] tags=[] -# ## Preprocessing of the BRATS Dataset in 2D slices for validation -# We download the BRATS validation dataset from the Decathlon dataset. -# We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_. -# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_. -# - -# %% -val_ds = DecathlonDataset( - root_dir=root_dir, - task="Task01_BrainTumour", - section="validation", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise - num_workers=4, - download=True, # Set download to True if the dataset hasnt been downloaded yet - seed=0, - transform=train_transforms, -) - - -val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) -data_2d_slices_val = [] -data_slice_label_val = [] -for i, data in enumerate(val_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices_val.append(b2d) - data_slice_label_val.append(slice_label2d) - -total_val_slices = torch.cat(data_2d_slices_val, 0) -total_val_labels = torch.cat(data_slice_label_val, 0) - - -# %% [markdown] -# ## Define network, scheduler, optimizer, and inferer -# At this step, we instantiate the MONAI components to create a DDIM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using -# the deterministic DDIM scheduler containing 1000 timesteps, and a 2D UNET with attention mechanisms -# in the 3rd level (`num_head_channels=64`). -# - -# %% jupyter={"outputs_hidden": false} -device = torch.device("cuda") - -model = DiffusionModelUNet( - spatial_dims=2, - in_channels=1, - out_channels=1, - num_channels=(64, 64, 64), - attention_levels=(False, False, True), - num_res_blocks=1, - num_head_channels=64, - with_conditioning=False, -) -model.to(device) - -scheduler = DDIMScheduler(num_train_timesteps=1000) - -optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) - -inferer = DiffusionInferer(scheduler) - - -# %% [markdown] tags=[] -# ## Model training of the diffusion model -# We train our diffusion model for 100 epochs, with a batch size of 32. - -# %% jupyter={"outputs_hidden": false} -n_epochs = 100 -batch_size = 32 -val_interval = 1 -epoch_loss_list = [] -val_epoch_loss_list = [] - -scaler = GradScaler() -total_start = time.time() -for epoch in range(n_epochs): - model.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # - - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) - optimizer.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t - - with autocast(enabled=True): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - - loss = F.mse_loss(noise_pred.float(), noise.float()) - - scaler.scale(loss).backward() - scaler.step(optimizer) - scaler.update() - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - - if (epoch) % val_interval == 0: - model.eval() - val_epoch_loss = 0 - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with torch.no_grad(): - with autocast(enabled=True): - noise = torch.randn_like(images).to(device) - noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - val_loss = F.mse_loss(noise_pred.float(), noise.float()) - - val_epoch_loss += val_loss.item() - progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - -total_time = time.time() - total_start -print(f"train diffusion completed, total time: {total_time}.") - -plt.style.use("seaborn-bright") -plt.title("Learning Curves Diffusion Model", fontsize=20) -plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") -plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", -) -plt.yticks(fontsize=12) -plt.xticks(fontsize=12) -plt.xlabel("Epochs", fontsize=16) -plt.ylabel("Loss", fontsize=16) -plt.legend(prop={"size": 14}) -plt.show() - - -# %% [markdown] -# ## Check the performance of the diffusion model -# -# We generate a random image from noise to check whether our diffusion model works properly for an image generation task. -# -# - -# %% -model.eval() -noise = torch.randn((1, 1, 64, 64)) -noise = noise.to(device) -scheduler.set_timesteps(num_inference_steps=1000) -with autocast(enabled=True): - image, intermediates = inferer.sample( - input_noise=noise, diffusion_model=model, scheduler=scheduler, save_intermediates=True, intermediate_steps=100 - ) - -chain = torch.cat(intermediates, dim=-1) - -plt.style.use("default") -plt.imshow(chain[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") -plt.tight_layout() -plt.axis("off") -plt.show() - - -# %% [markdown] -# ## Define the classification model -# First, we define the classification model. It follows the encoder architecture of the diffusion model, combined with linear layers for binary classification between healthy and diseased slices. -# - -# %% -classifier = DiffusionModelEncoder( - spatial_dims=2, - in_channels=1, - out_channels=2, - num_channels=(32, 64, 64), - attention_levels=(False, True, True), - num_res_blocks=(1, 1, 1), - num_head_channels=64, - with_conditioning=False, -) -classifier.to(device) - - -# %% [markdown] -# ## Model training of the classification model -# We train our classification model for 100 epochs. -# - -# %% -batch_size = 32 -n_epochs = 100 -val_interval = 1 -epoch_loss_list = [] -val_epoch_loss_list = [] -optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) - -classifier.to(device) -weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset - - -scaler = GradScaler() -total_start = time.time() -for epoch in range(n_epochs): - classifier.train() - epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) - - optimizer_cls.zero_grad(set_to_none=True) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - - with autocast(enabled=False): - # Generate random noise - noise = torch.randn_like(images).to(device) - - # Get model prediction - noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image - pred = classifier(noisy_img, timesteps) - loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") - - loss.backward() - optimizer_cls.step() - - epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) - epoch_loss_list.append(epoch_loss / (step + 1)) - print("final step train", step) - - if (epoch + 1) % val_interval == 0: - classifier.eval() - val_epoch_loss = 0 - subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar_val.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - timesteps = torch.randint(0, 1, (len(images),)).to( - device - ) # check validation accuracy on the original images, i.e., do not add noise - - with torch.no_grad(): - with autocast(enabled=False): - noise = torch.randn_like(images).to(device) - pred = classifier(images, timesteps) - val_loss = F.cross_entropy(pred, classes.long(), reduction="mean") - - val_epoch_loss += val_loss.item() - _, predicted = torch.max(pred, 1) - progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - -total_time = time.time() - total_start -print(f"train completed, total time: {total_time}.") - -## Learning curves for the Classifier - -plt.style.use("seaborn-bright") -plt.title("Learning Curves", fontsize=20) -plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") -plt.plot( - np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), - val_epoch_loss_list, - color="C1", - linewidth=2.0, - label="Validation", -) -plt.yticks(fontsize=12) -plt.xticks(fontsize=12) -plt.xlabel("Epochs", fontsize=16) -plt.ylabel("Loss", fontsize=16) -plt.legend(prop={"size": 14}) -plt.show() -# %% [markdown] -# # Image-to-image translation to a healthy subject -# We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. - -# %% - -inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed -inputlabel = total_val_labels[120] # Check whether it is healthy or diseased - -plt.figure("input" + str(inputlabel)) -plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") -plt.axis("off") -plt.tight_layout() -plt.show() - -model.eval() -classifier.eval() - -# %% [markdown] -# ### Encoding the input image in noise with the reversed DDIM sampling scheme -# In order to sample using gradient guidance, we first need to encode the input image in noise by using the reversed DDIM sampling scheme.\ -# We define the number of steps in the noising and denoising process by L.\ -# The encoding process is presented in Equation 6 of the paper "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/pdf/2203.04306.pdf). -# - -# %% jupyter={"outputs_hidden": false} -L = 200 -current_img = inputimg[None, None, ...].to(device) -scheduler.set_timesteps(num_inference_steps=1000) - - -progress_bar = tqdm(range(L)) # go back and forth L timesteps -for t in progress_bar: # go through the noising process - with autocast(enabled=False): - with torch.no_grad(): - model_output = model(current_img, timesteps=torch.Tensor((t,)).to(current_img.device)) - current_img, _ = scheduler.reversed_step(model_output, t, current_img) - -plt.style.use("default") -plt.imshow(current_img[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") -plt.tight_layout() -plt.axis("off") -plt.show() - - -# %% [markdown] -# ### Denoising process using gradient guidance -# From the noisy image, we apply DDIM sampling scheme for denoising for L steps.\ -# Additionally, we apply gradient guidance using the classifier network towards the desired class label y=0 (healthy). This is presented in Algorithm 2 of https://arxiv.org/pdf/2105.05233.pdf, and in Algorithm 1 of https://arxiv.org/pdf/2203.04306.pdf. \ -# The scale s is used to amplify the gradient. - -# %% - - -y = torch.tensor(0) # define the desired class label -scale = 5 # define the desired gradient scale s -progress_bar = tqdm(range(L)) # go back and forth L timesteps - -for i in progress_bar: # go through the denoising process - t = L - i - with autocast(enabled=True): - with torch.no_grad(): - model_output = model( - current_img, timesteps=torch.Tensor((t,)).to(current_img.device) - ).detach() # this is supposed to be epsilon - - with torch.enable_grad(): - x_in = current_img.detach().requires_grad_(True) - logits = classifier(x_in, timesteps=torch.Tensor((t,)).to(current_img.device)) - log_probs = F.log_softmax(logits, dim=-1) - selected = log_probs[range(len(logits)), y.view(-1)] - a = torch.autograd.grad(selected.sum(), x_in)[0] - alpha_prod_t = scheduler.alphas_cumprod[t] - updated_noise = ( - model_output - (1 - alpha_prod_t).sqrt() * scale * a - ) # update the predicted noise epsilon with the gradient of the classifier - - current_img, _ = scheduler.step(updated_noise, t, current_img) - torch.cuda.empty_cache() - -plt.style.use("default") -plt.imshow(current_img[0, 0].cpu().detach().numpy(), vmin=0, vmax=1, cmap="gray") -plt.tight_layout() -plt.axis("off") -plt.show() - - -# %% [markdown] -# # Anomaly Detection -# To get the anomaly map, we compute the difference between the input image the output of our image-to-image translation model towards the healthy reconstruction. - - -# %% - -diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() -plt.style.use("default") -plt.imshow(diff, cmap="jet") -plt.tight_layout() -plt.axis("off") -plt.show() From 18ebcb9c329bcf5529aa757e4bede6d1d50bfc34 Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:21:12 +0100 Subject: [PATCH 12/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 11f77a52..aba62d23 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -2019,8 +2019,8 @@ def forward( self, x: torch.Tensor, timesteps: torch.Tensor, - context: Optional[torch.Tensor] = None, - class_labels: Optional[torch.Tensor] = None, + context: torch.Tensor | None = None, + class_labels: torch.Tensor | None = None, ) -> torch.Tensor: """ Args: From e9b3978481ca4f4d6c9ea523d49ea96ec22dc3fd Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:21:45 +0100 Subject: [PATCH 13/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index aba62d23..2287ecf3 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1945,6 +1945,8 @@ def __init__( # All number of channels should be multiple of num_groups if any((out_channel % norm_num_groups) != 0 for out_channel in num_channels): raise ValueError("DiffusionModelUNet expects all num_channels being multiple of norm_num_groups") + if len(num_channels) != len(attention_levels): + raise ValueError("DiffusionModelEncoder expects num_channels being same size of attention_levels") if isinstance(num_head_channels, int): num_head_channels = (num_head_channels,) * len(attention_levels) From 88672f6a0eba6f5cec3efe57e1d6741e2864e2ea Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:21:54 +0100 Subject: [PATCH 14/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 2287ecf3..a9814d1e 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1934,7 +1934,7 @@ def __init__( super().__init__() if with_conditioning is True and cross_attention_dim is None: raise ValueError( - "DiffusionModelUNet expects dimension of the cross-attention conditioning (cross_attention_dim) " + "DiffusionModelEncoder expects dimension of the cross-attention conditioning (cross_attention_dim) " "when using with_conditioning." ) if cross_attention_dim is not None and with_conditioning is False: From bc204f388305476a878d3dbe5f0d6f9274f4616b Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:38:38 +0100 Subject: [PATCH 15/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index a9814d1e..f07370ba 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1939,7 +1939,7 @@ def __init__( ) if cross_attention_dim is not None and with_conditioning is False: raise ValueError( - "DiffusionModelUNet expects with_conditioning=True when specifying the cross_attention_dim." + "DiffusionModelEncoder expects with_conditioning=True when specifying the cross_attention_dim." ) # All number of channels should be multiple of num_groups From b7c5613ad59308be433c4261ca0bbe3c91acd88e Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:39:07 +0100 Subject: [PATCH 16/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index f07370ba..a5a7708d 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1944,7 +1944,7 @@ def __init__( # All number of channels should be multiple of num_groups if any((out_channel % norm_num_groups) != 0 for out_channel in num_channels): - raise ValueError("DiffusionModelUNet expects all num_channels being multiple of norm_num_groups") + raise ValueError("DiffusionModelEncoder expects all num_channels being multiple of norm_num_groups") if len(num_channels) != len(attention_levels): raise ValueError("DiffusionModelEncoder expects num_channels being same size of attention_levels") From 614fb9e0cca07ab15631de13d5997f7941908080 Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:39:23 +0100 Subject: [PATCH 17/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index a5a7708d..c67ffa29 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -2033,6 +2033,11 @@ def forward( """ # 1. time t_emb = get_timestep_embedding(timesteps, self.block_out_channels[0]) + + # timesteps does not contain any weights and will always return f32 tensors + # but time_embedding might actually be running in fp16. so we need to cast here. + # there might be better ways to encapsulate this. + t_emb = t_emb.to(dtype=x.dtype) emb = self.time_embed(t_emb) # 2. class From 1cc8fc2a3ad4155dfd07d11bfef36fe7cf36c486 Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:23:56 +0100 Subject: [PATCH 18/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 1 + 1 file changed, 1 insertion(+) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index c67ffa29..6e23a6be 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -2045,6 +2045,7 @@ def forward( if class_labels is None: raise ValueError("class_labels should be provided when num_class_embeds > 0") class_emb = self.class_embedding(class_labels) + class_emb = class_emb.to(dtype=x.dtype) emb = emb + class_emb # 3. initial convolution From 55513e118e321689bab1ace1a3116a2644081c21 Mon Sep 17 00:00:00 2001 From: JuliaWolleb <67380907+JuliaWolleb@users.noreply.github.com> Date: Mon, 20 Mar 2023 11:28:42 +0100 Subject: [PATCH 19/23] Update generative/networks/nets/diffusion_model_unet.py Co-authored-by: Walter Hugo Lopez Pinaya --- generative/networks/nets/diffusion_model_unet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 6e23a6be..cf893a12 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1949,7 +1949,7 @@ def __init__( raise ValueError("DiffusionModelEncoder expects num_channels being same size of attention_levels") if isinstance(num_head_channels, int): - num_head_channels = (num_head_channels,) * len(attention_levels) + num_head_channels = ensure_tuple_rep(num_head_channels, len(attention_levels)) if len(num_head_channels) != len(attention_levels): raise ValueError( From b4b82c3fcde3209151a035fa6d0acd5db3029b82 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 21 Mar 2023 20:02:48 +0100 Subject: [PATCH 20/23] include Walters changes in the tutorial --- .../networks/nets/diffusion_model_unet.py | 8 +- generative/networks/schedulers/ddim.py | 39 +- ...tection_tutorial_classifier_guidance.ipynb | 2576 ++++------------- ...ydetection_tutorial_classifier_guidance.py | 202 +- 4 files changed, 659 insertions(+), 2166 deletions(-) diff --git a/generative/networks/nets/diffusion_model_unet.py b/generative/networks/nets/diffusion_model_unet.py index 11f77a52..6efaeb79 100644 --- a/generative/networks/nets/diffusion_model_unet.py +++ b/generative/networks/nets/diffusion_model_unet.py @@ -1894,7 +1894,8 @@ def forward( class DiffusionModelEncoder(nn.Module): """ - Classification Network based on the Encoder of the Diffusion Model, followed by fully connected layers for classification + Classification Network based on the Encoder of the Diffusion Model, followed by fully connected layers. This network is based on + Wolleb et al. "Diffusion Models for Medical Anomaly Detection" (https://arxiv.org/abs/2203.04306). Args: spatial_dims: number of spatial dimensions. @@ -1905,12 +1906,13 @@ class DiffusionModelEncoder(nn.Module): attention_levels: list of levels to add attention. norm_num_groups: number of groups for the normalization. norm_eps: epsilon for the normalization. + resblock_updown: if True use residual blocks for downsampling. num_head_channels: number of channels in each attention head. with_conditioning: if True add spatial transformers to perform conditioning. transformer_num_layers: number of layers of Transformer blocks to use. cross_attention_dim: number of context dimensions to use. - num_class_embeds: if specified (as an int), then this model will be class-conditional with `num_class_embeds` - classes. + num_class_embeds: if specified (as an int), then this model will be class-conditional with `num_class_embeds` classes. + upcast_attention: if True, upcast attention operations to full precision. """ def __init__( diff --git a/generative/networks/schedulers/ddim.py b/generative/networks/schedulers/ddim.py index f9010ab7..86ef011a 100644 --- a/generative/networks/schedulers/ddim.py +++ b/generative/networks/schedulers/ddim.py @@ -8,12 +8,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# + # ========================================================================= # Adapted from https://github.com/huggingface/diffusers # which has the following license: # https://github.com/huggingface/diffusers/blob/main/LICENSE -# + # Copyright 2022 UC Berkeley Team and The HuggingFace Team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,7 @@ # limitations under the License. # ========================================================================= -from __future__ import annotations +from typing import Optional, Tuple, Union import numpy as np import torch @@ -41,7 +41,6 @@ class DDIMScheduler(nn.Module): Denoising diffusion implicit models is a scheduler that extends the denoising procedure introduced in denoising diffusion probabilistic models (DDPMs) with non-Markovian guidance. Based on: Song et al. "Denoising Diffusion Implicit Models" https://arxiv.org/abs/2010.02502 - Args: num_train_timesteps: number of diffusion steps used to train the model. beta_start: the starting `beta` value of inference. @@ -103,18 +102,16 @@ def __init__( # standard deviation of the initial noise distribution self.init_noise_sigma = 1.0 + # setable values + self.num_inference_steps = None self.timesteps = torch.from_numpy(np.arange(0, num_train_timesteps)[::-1].astype(np.int64)) self.clip_sample = clip_sample self.steps_offset = steps_offset - # default the number of inference timesteps to the number of train steps - self.set_timesteps(num_train_timesteps) - - def set_timesteps(self, num_inference_steps: int, device: str | torch.device | None = None) -> None: + def set_timesteps(self, num_inference_steps: int, device: Optional[Union[str, torch.device]] = None) -> None: """ Sets the discrete timesteps used for the diffusion chain. Supporting function to be run before inference. - Args: num_inference_steps: number of diffusion steps used when generating samples with a pre-trained model. device: target device to put the data. @@ -150,12 +147,11 @@ def step( timestep: int, sample: torch.Tensor, eta: float = 0.0, - generator: torch.Generator | None = None, - ) -> tuple[torch.Tensor, torch.Tensor]: + generator: Optional[torch.Generator] = None, + ) -> Tuple[torch.Tensor, torch.Tensor]: """ Predict the sample at the previous timestep by reversing the SDE. Core function to propagate the diffusion process from the learned model outputs (most often the predicted noise). - Args: model_output: direct output from learned diffusion model. timestep: current discrete timestep in the diffusion chain. @@ -163,7 +159,6 @@ def step( eta: weight of noise for added noise in diffusion step. predict_epsilon: flag to use when model predicts the samples directly instead of the noise, epsilon. generator: random number generator. - Returns: pred_prev_sample: Predicted previous sample pred_original_sample: Predicted original sample @@ -192,13 +187,12 @@ def step( # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf if self.prediction_type == "epsilon": pred_original_sample = (sample - beta_prod_t ** (0.5) * model_output) / alpha_prod_t ** (0.5) - pred_epsilon = model_output elif self.prediction_type == "sample": pred_original_sample = model_output - pred_epsilon = (sample - alpha_prod_t ** (0.5) * pred_original_sample) / beta_prod_t ** (0.5) elif self.prediction_type == "v_prediction": pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output - pred_epsilon = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample + # predict V + model_output = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample # 4. Clip "predicted x_0" if self.clip_sample: @@ -210,7 +204,7 @@ def step( std_dev_t = eta * variance ** (0.5) # 6. compute "direction pointing to x_t" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf - pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * pred_epsilon + pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * model_output # 7. compute x_t-1 without "random noise" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf pred_prev_sample = alpha_prod_t_prev ** (0.5) * pred_original_sample + pred_sample_direction @@ -236,7 +230,6 @@ def reversed_step( """ Predict the sample at the previous timestep by reversing the SDE. Core function to propagate the diffusion process from the learned model outputs (most often the predicted noise). - Args: model_output: direct output from learned diffusion model. timestep: current discrete timestep in the diffusion chain. @@ -244,7 +237,6 @@ def reversed_step( eta: weight of noise for added noise in diffusion step. predict_epsilon: flag to use when model predicts the samples directly instead of the noise, epsilon. generator: random number generator. - Returns: pred_prev_sample: Predicted previous sample pred_original_sample: Predicted original sample @@ -307,15 +299,18 @@ def reversed_step( return pred_post_sample, pred_original_sample - def add_noise(self, original_samples: torch.Tensor, noise: torch.Tensor, timesteps: torch.Tensor) -> torch.Tensor: + def add_noise( + self, + original_samples: torch.Tensor, + noise: torch.Tensor, + timesteps: torch.Tensor, + ) -> torch.Tensor: """ Add noise to the original samples. - Args: original_samples: original samples noise: noise to add to samples timesteps: timesteps tensor indicating the timestep to be computed for each sample. - Returns: noisy_samples: sample with added noise """ diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb index e335e271..d931452e 100644 --- a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb +++ b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb @@ -10,7 +10,7 @@ "This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1].\n", "\n", "We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\\\n", - "We then tranlsate an input slice to its healthy reconstruction using DDIMs.\\\n", + "We then translate an input slice to its healthy reconstruction using DDIMs.\\\n", "Anomaly detection is performed by taking the difference between input and output, as proposed in [1].\n", "\n", "[1] - Wolleb et al. \"Diffusion Models for Medical Anomaly Detection\" https://arxiv.org/abs/2203.04306\n", @@ -20,14 +20,82 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 90, "id": "75f2d5f3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "running install\n", + "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.\n", + " warnings.warn(\n", + "/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/setuptools/command/easy_install.py:144: EasyInstallDeprecationWarning: easy_install command is deprecated. Use build and pip and other standards-based tools.\n", + " warnings.warn(\n", + "running bdist_egg\n", + "running egg_info\n", + "writing generative.egg-info/PKG-INFO\n", + "writing dependency_links to generative.egg-info/dependency_links.txt\n", + "writing requirements to generative.egg-info/requires.txt\n", + "writing top-level names to generative.egg-info/top_level.txt\n", + "reading manifest file 'generative.egg-info/SOURCES.txt'\n", + "writing manifest file 'generative.egg-info/SOURCES.txt'\n", + "installing library code to build/bdist.linux-x86_64/egg\n", + "running install_lib\n", + "warning: install_lib: 'build/lib' does not exist -- no Python modules to install\n", + "\n", + "creating build/bdist.linux-x86_64/egg\n", + "creating build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/requires.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "copying generative.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO\n", + "zip_safe flag not set; analyzing archive contents...\n", + "creating 'dist/generative-0.1.0-py3.10.egg' and adding 'build/bdist.linux-x86_64/egg' to it\n", + "removing 'build/bdist.linux-x86_64/egg' (and everything under it)\n", + "Processing generative-0.1.0-py3.10.egg\n", + "Removing /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", + "Copying generative-0.1.0-py3.10.egg to /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "generative 0.1.0 is already the active version in easy-install.pth\n", + "\n", + "Installed /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg\n", + "Processing dependencies for generative==0.1.0\n", + "Searching for monai-weekly==1.2.dev2304\n", + "Best match: monai-weekly 1.2.dev2304\n", + "Adding monai-weekly 1.2.dev2304 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for numpy==1.23.2\n", + "Best match: numpy 1.23.2\n", + "Adding numpy 1.23.2 to easy-install.pth file\n", + "Installing f2py script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing f2py3 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing f2py3.10 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for torch==1.12.1\n", + "Best match: torch 1.12.1\n", + "Adding torch 1.12.1 to easy-install.pth file\n", + "Installing convert-caffe2-to-onnx script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing convert-onnx-to-caffe2 script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "Installing torchrun script to /home/juliawolleb/anaconda3/envs/experiment/bin\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Searching for typing-extensions==4.3.0\n", + "Best match: typing-extensions 4.3.0\n", + "Adding typing-extensions 4.3.0 to easy-install.pth file\n", + "\n", + "Using /home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages\n", + "Finished processing dependencies for generative==0.1.0\n" + ] + } + ], "source": [ "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", "!python -c \"import matplotlib\" || pip install -q matplotlib\n", - "!python -c \"import seaborn\" || pi resblock_updown: bool = False,p install -q seaborn" + "!python -c \"import seaborn\" || pip install -q seaborn" ] }, { @@ -40,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 91, "id": "972ed3f3", "metadata": { "collapsed": false, @@ -54,7 +122,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/anomaly_detection/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", + "path ['/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection', '/home/juliawolleb/anaconda3/envs/experiment/lib/python310.zip', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/lib-dynload', '', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/PyYAML-6.0-py3.10-linux-x86_64.egg', '/home/juliawolleb/PycharmProjects/Python_Tutorials/Calgary_Infants/calgary/HD-BET', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/lpips-0.1.4-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/tqdm-4.64.1-py3.10.egg', '/home/juliawolleb/anaconda3/envs/experiment/lib/python3.10/site-packages/generative-0.1.0-py3.10.egg', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/', '/home/juliawolleb/PycharmProjects/MONAI/GenerativeModels/']\n", "MONAI version: 1.2.dev2304\n", "Numpy version: 1.23.2\n", "Pytorch version: 1.12.1\n", @@ -97,8 +165,8 @@ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License.\n", + "\n", "import os\n", - "import sys\n", "import time\n", "from typing import Dict\n", "import tempfile\n", @@ -110,16 +178,15 @@ "from monai.apps import DecathlonDataset\n", "from monai.config import print_config\n", "from monai.data import DataLoader\n", - "from monai.utils import first, set_determinism\n", + "from monai.utils import set_determinism\n", "from torch.cuda.amp import GradScaler, autocast\n", "from tqdm import tqdm\n", "\n", "from generative.inferers import DiffusionInferer\n", "from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet\n", "from generative.networks.schedulers.ddim import DDIMScheduler\n", - "\n", - "\n", "torch.multiprocessing.set_sharing_strategy(\"file_system\")\n", + "\n", "print_config()" ] }, @@ -133,7 +200,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 92, "id": "8b4323e7", "metadata": { "collapsed": false, @@ -157,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 93, "id": "34ea510f", "metadata": { "collapsed": false, @@ -179,28 +246,17 @@ "source": [ "## Preprocessing of the BRATS Dataset in 2D slices for training\n", "We download the BRATS training dataset from the Decathlon dataset. \\\n", - "We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\\\n", - "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_.\n" - ] - }, - { - "cell_type": "markdown", - "id": "6986f55c", - "metadata": {}, - "source": [ - "Here we use transforms to augment the training dataset, as usual:\n", + "We slice the volumes in axial 2D slices, and assign slice-wise labels (0 for healthy, 1 for diseased) to all slices.\n", + "Here we use transforms to augment the training dataset:\n", "\n", - "1. `LoadImaged` loads the hands images from files.\n", + "1. `LoadImaged` loads the brain MR images from files.\n", "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", - "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", - "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", - "\n", - "To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain." + "1. `ScaleIntensityRangePercentilesd` takes the lower and upper intensity percentiles and scales them to [0, 1].\n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 94, "id": "c68d2d91-9a0b-4ac1-ae49-f4a64edbd82a", "metadata": {}, "outputs": [ @@ -225,27 +281,19 @@ " transforms.EnsureTyped(keys=[\"image\", \"label\"]),\n", " transforms.Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\"),\n", " transforms.Spacingd(keys=[\"image\", \"label\"], pixdim=(3.0, 3.0, 2.0), mode=(\"bilinear\", \"nearest\")),\n", - " transforms.CenterSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 64)),\n", + " transforms.CenterSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 44)),\n", " transforms.ScaleIntensityRangePercentilesd(keys=\"image\", lower=0, upper=99.5, b_min=0, b_max=1),\n", + " transforms.RandSpatialCropd(keys=[\"image\", \"label\"], roi_size=(64, 64, 1), random_size=False),\n", + " transforms.Lambdad(keys=[\"image\", \"label\"], func=lambda x: x.squeeze(-1)),\n", " transforms.CopyItemsd(keys=[\"label\"], times=1, names=[\"slice_label\"]),\n", - " transforms.Lambdad(\n", - " keys=[\"slice_label\"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze()\n", - " ),\n", + " transforms.Lambdad(keys=[\"slice_label\"], func=lambda x: 0.0 if x.sum() > 0 else 1.0),\n", " ]\n", - ")\n", - "\n", - "def get_batched_2d_axial_slices(data: Dict):\n", - " images_3D = data[\"image\"]\n", - " batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze(-1) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain.\n", - " slice_label = data[\"slice_label\"]\n", - " slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze()\n", - " return batched_2d_slices, slice_label\n", - "\n" + ")" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 107, "id": "da1927b0", "metadata": { "collapsed": false, @@ -254,43 +302,49 @@ } }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|████████████████████████| 388/388 [03:02<00:00, 2.13it/s]" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "2023-03-13 16:09:17,074 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-03-13 16:09:17,075 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-03-13 16:09:17,076 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n", - "len train data 388\n" + "Length of training data: 388\n", + "Train image shape torch.Size([1, 64, 64])\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" ] } ], "source": [ + "batch_size=64\n", "\n", "train_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", " task=\"Task01_BrainTumour\",\n", " section=\"training\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " cache_rate=1.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", " seed=0,\n", " transform=train_transforms,\n", ")\n", - "print(\"len train data\", len(train_ds)) #this gives the number of patients in the training set\n", "\n", + "print(f\"Length of training data: {len(train_ds)}\") # this gives the number of patients in the training set\n", + "print(f'Train image shape {train_ds[0][\"image\"].shape}')\n", "\n", - "\n", - "train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4)\n", - "data_2d_slices = []\n", - "data_slice_label = []\n", - "for i, data in enumerate(train_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices.append(b2d)\n", - " data_slice_label.append(slice_label2d)\n", - " \n", - "total_train_slices = torch.cat(data_2d_slices, 0)\n", - "total_train_labels = torch.cat(data_slice_label, 0)" + "train_loader = DataLoader(\n", + " train_ds, batch_size=batch_size, shuffle=True, num_workers=4, drop_last=True, persistent_workers=True\n", + ")" ] }, { @@ -301,24 +355,38 @@ }, "source": [ "## Preprocessing of the BRATS Dataset in 2D slices for validation\n", - "We download the BRATS validation dataset from the Decathlon dataset. \n", - "We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_.\n", - "The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_.\n" + "We download the BRATS validation dataset from the Decathlon dataset, and define the dataloader to load 2D slices for validation.\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 77, "id": "73d72110-a8b3-4e03-91cc-1dab4d5a7b87", - "metadata": {}, + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|██████████████████████████| 96/96 [00:48<00:00, 2.00it/s]" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "2023-03-13 16:19:38,821 - INFO - Verified 'Task01_BrainTumour.tar', md5: 240a19d752f0d9e9101544901065d872.\n", - "2023-03-13 16:19:38,824 - INFO - File exists: /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour.tar, skipped downloading.\n", - "2023-03-13 16:19:38,826 - INFO - Non-empty folder exists in /home/juliawolleb/PycharmProjects/MONAI/brats/Task01_BrainTumour, skipped extracting.\n" + "Length of training data: 96\n", + "Validation Image shape torch.Size([1, 64, 64])\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" ] } ], @@ -326,25 +394,19 @@ "val_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", " task=\"Task01_BrainTumour\",\n", - " section=\"validation\", # validation\n", - " cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", + " section=\"validation\",\n", + " cache_rate=1.0, # you may need a few Gb of RAM... Set to 0 otherwise\n", " num_workers=4,\n", - " download=True, # Set download to True if the dataset hasnt been downloaded yet\n", + " download=False, # Set download to True if the dataset hasnt been downloaded yet\n", " seed=0,\n", " transform=train_transforms,\n", ")\n", + "print(f\"Length of training data: {len(val_ds)}\")\n", + "print(f'Validation Image shape {val_ds[0][\"image\"].shape}')\n", "\n", - "\n", - "val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4)\n", - "data_2d_slices_val = []\n", - "data_slice_label_val = []\n", - "for i, data in enumerate(val_loader_3D):\n", - " b2d, slice_label2d = get_batched_2d_axial_slices(data)\n", - " data_2d_slices_val.append(b2d)\n", - " data_slice_label_val.append(slice_label2d)\n", - "\n", - "total_val_slices = torch.cat(data_2d_slices_val, 0)\n", - "total_val_labels = torch.cat(data_slice_label_val, 0)\n" + "val_loader = DataLoader(\n", + " val_ds, batch_size=batch_size, shuffle=False, num_workers=4, drop_last=True, persistent_workers=True\n", + ")" ] }, { @@ -360,14 +422,13 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 108, "id": "bee5913e", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false - }, - "lines_to_next_cell": 2 + } }, "outputs": [], "source": [ @@ -389,7 +450,8 @@ "\n", "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", "\n", - "inferer = DiffusionInferer(scheduler)" + "inferer = DiffusionInferer(scheduler)\n", + "\n" ] }, { @@ -400,12 +462,12 @@ }, "source": [ "## Model training of the diffusion model\n", - "We train our diffusion model for 100 epochs, with a batch size of 32." + "We train our diffusion model for 2000 epochs." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 109, "id": "6c0ed909", "metadata": { "collapsed": false, @@ -415,222 +477,116 @@ "lines_to_next_cell": 2 }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 534it [01:42, 5.21it/s, loss=0.163] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 1: : 534it [01:46, 4.99it/s, loss=0.0234] \n", - "4224it [01:27, 48.47it/s]\n", - "Epoch 2: : 534it [01:47, 4.96it/s, loss=0.0207] \n", - "4224it [01:26, 48.76it/s]\n", - "Epoch 3: : 534it [01:46, 4.99it/s, loss=0.0199] \n", - "4224it [01:24, 49.73it/s]\n", - "Epoch 4: : 534it [01:46, 5.00it/s, loss=0.0198] \n", - "4224it [01:26, 48.90it/s]\n", - "Epoch 5: : 534it [01:46, 5.00it/s, loss=0.0192] \n", - "4224it [01:26, 48.97it/s]\n", - "Epoch 6: : 534it [01:47, 4.98it/s, loss=0.0199] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 7: : 534it [01:47, 4.99it/s, loss=0.0188] \n", - "4224it [01:26, 48.65it/s]\n", - "Epoch 8: : 534it [01:47, 4.95it/s, loss=0.0184] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 9: : 534it [01:47, 4.98it/s, loss=0.0179] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 10: : 534it [01:47, 4.98it/s, loss=0.0183] \n", - "4224it [01:25, 49.12it/s]\n", - "Epoch 11: : 534it [01:47, 4.98it/s, loss=0.0183] \n", - "4224it [01:26, 48.83it/s]\n", - "Epoch 12: : 534it [01:48, 4.94it/s, loss=0.0182] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 13: : 534it [01:47, 4.95it/s, loss=0.0185] \n", - "4224it [01:27, 48.52it/s]\n", - "Epoch 14: : 534it [01:47, 4.95it/s, loss=0.0176] \n", - "4224it [01:27, 48.54it/s]\n", - "Epoch 15: : 534it [01:47, 4.95it/s, loss=0.018] \n", - "4224it [01:26, 48.60it/s]\n", - "Epoch 16: : 534it [01:47, 4.99it/s, loss=0.0181] \n", - "4224it [01:27, 48.10it/s]\n", - "Epoch 17: : 534it [01:47, 4.95it/s, loss=0.0179] \n", - "4224it [01:28, 47.99it/s]\n", - "Epoch 18: : 534it [01:47, 4.97it/s, loss=0.0177] \n", - "4224it [01:26, 48.73it/s]\n", - "Epoch 19: : 534it [01:47, 4.97it/s, loss=0.0179] \n", - "4224it [01:28, 47.86it/s]\n", - "Epoch 20: : 534it [01:47, 4.95it/s, loss=0.0177] \n", - "4224it [01:28, 47.48it/s]\n", - "Epoch 21: : 534it [01:47, 4.95it/s, loss=0.0175] \n", - "4224it [01:27, 48.23it/s]\n", - "Epoch 22: : 534it [01:47, 4.95it/s, loss=0.0171] \n", - "4224it [01:24, 49.97it/s]\n", - "Epoch 23: : 534it [01:47, 4.96it/s, loss=0.0169] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 24: : 534it [01:47, 4.98it/s, loss=0.0172] \n", - "4224it [01:27, 48.49it/s]\n", - "Epoch 25: : 534it [01:47, 4.99it/s, loss=0.0168] \n", - "4224it [01:25, 49.39it/s]\n", - "Epoch 26: : 534it [01:46, 5.00it/s, loss=0.0169] \n", - "4224it [01:26, 48.62it/s]\n", - "Epoch 27: : 534it [01:47, 4.98it/s, loss=0.0171] \n", - "4224it [01:27, 48.43it/s]\n", - "Epoch 28: : 534it [01:47, 4.97it/s, loss=0.0175] \n", - "4224it [01:25, 49.18it/s]\n", - "Epoch 29: : 534it [01:46, 5.01it/s, loss=0.0171] \n", - "4224it [01:25, 49.59it/s]\n", - "Epoch 30: : 534it [01:47, 4.95it/s, loss=0.017] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 31: : 534it [01:47, 4.99it/s, loss=0.0169] \n", - "4224it [01:25, 49.12it/s]\n", - "Epoch 32: : 534it [01:46, 4.99it/s, loss=0.0168] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 33: : 534it [01:46, 5.00it/s, loss=0.0166] \n", - "4224it [01:26, 48.82it/s]\n", - "Epoch 34: : 534it [01:47, 4.97it/s, loss=0.0173] \n", - "4224it [01:27, 48.49it/s]\n", - "Epoch 35: : 534it [01:46, 4.99it/s, loss=0.0169] \n", - "4224it [01:26, 48.66it/s]\n", - "Epoch 36: : 534it [01:47, 4.99it/s, loss=0.0171] \n", - "4224it [01:26, 48.92it/s]\n", - "Epoch 37: : 534it [01:47, 4.97it/s, loss=0.0166] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 38: : 534it [01:47, 4.99it/s, loss=0.0163] \n", - "4224it [01:27, 48.55it/s]\n", - "Epoch 39: : 534it [01:47, 4.97it/s, loss=0.0166] \n", - "4224it [01:26, 48.55it/s]\n", - "Epoch 40: : 534it [01:46, 5.00it/s, loss=0.0169] \n", - "4224it [01:25, 49.31it/s]\n", - "Epoch 41: : 534it [01:47, 4.99it/s, loss=0.0169] \n", - "4224it [01:26, 48.85it/s]\n", - "Epoch 42: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 43: : 534it [01:46, 4.99it/s, loss=0.0171] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 44: : 534it [01:47, 4.99it/s, loss=0.0167] \n", - "4224it [01:27, 48.53it/s]\n", - "Epoch 45: : 534it [01:46, 5.00it/s, loss=0.0167] \n", - "4224it [01:27, 48.40it/s]\n", - "Epoch 46: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 47: : 534it [01:47, 4.99it/s, loss=0.0162] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 48: : 534it [01:46, 5.00it/s, loss=0.017] \n", - "4224it [01:27, 48.50it/s]\n", - "Epoch 49: : 534it [01:47, 4.98it/s, loss=0.0164] \n", - "4224it [01:27, 48.21it/s]\n", - "Epoch 50: : 534it [01:47, 4.97it/s, loss=0.0168] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 51: : 534it [01:47, 4.98it/s, loss=0.0163] \n", - "4224it [01:27, 48.10it/s]\n", - "Epoch 52: : 534it [01:47, 4.97it/s, loss=0.0158] \n", - "4224it [01:27, 48.36it/s]\n", - "Epoch 53: : 534it [01:47, 4.96it/s, loss=0.0163] \n", - "4224it [01:27, 48.32it/s]\n", - "Epoch 54: : 534it [01:47, 4.96it/s, loss=0.0157] \n", - "4224it [01:27, 48.03it/s]\n", - "Epoch 55: : 534it [01:47, 4.99it/s, loss=0.0164] \n", - "4224it [01:27, 48.19it/s]\n", - "Epoch 56: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.46it/s]\n", - "Epoch 57: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:27, 48.47it/s]\n", - "Epoch 58: : 534it [01:47, 4.97it/s, loss=0.017] \n", - "4224it [01:27, 48.46it/s]\n", - "Epoch 59: : 534it [01:47, 4.98it/s, loss=0.0164] \n", - "4224it [01:27, 48.38it/s]\n", - "Epoch 60: : 534it [01:47, 4.94it/s, loss=0.0165] \n", - "4224it [01:27, 48.27it/s]\n", - "Epoch 61: : 534it [01:47, 4.96it/s, loss=0.0164] \n", - "4224it [01:27, 48.50it/s]\n", - "Epoch 62: : 534it [01:47, 4.97it/s, loss=0.0164] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 63: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:27, 48.06it/s]\n", - "Epoch 64: : 534it [01:47, 4.97it/s, loss=0.0163] \n", - "4224it [01:27, 48.35it/s]\n", - "Epoch 65: : 534it [01:47, 4.98it/s, loss=0.0159] \n", - "4224it [01:27, 48.53it/s]\n", - "Epoch 66: : 534it [01:47, 4.97it/s, loss=0.0161] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 67: : 534it [01:47, 4.97it/s, loss=0.0164] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 68: : 534it [01:48, 4.94it/s, loss=0.016] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 69: : 534it [01:47, 4.98it/s, loss=0.0156] \n", - "4224it [01:27, 48.34it/s]\n", - "Epoch 70: : 534it [01:47, 4.98it/s, loss=0.0162] \n", - "4224it [01:26, 48.96it/s]\n", - "Epoch 71: : 534it [01:47, 4.97it/s, loss=0.0159] \n", - "4224it [01:25, 49.55it/s]\n", - "Epoch 72: : 534it [01:47, 4.97it/s, loss=0.0159] \n", - "4224it [01:27, 48.08it/s]\n", - "Epoch 73: : 534it [01:47, 4.97it/s, loss=0.0165] \n", - "4224it [01:26, 48.59it/s]\n", - "Epoch 74: : 534it [01:47, 4.98it/s, loss=0.0161] \n", - "4224it [01:27, 48.33it/s]\n", - "Epoch 75: : 534it [01:46, 5.00it/s, loss=0.0164] \n", - "4224it [01:27, 48.20it/s]\n", - "Epoch 76: : 534it [01:47, 4.95it/s, loss=0.0165] \n", - "4224it [01:26, 48.73it/s]\n", - "Epoch 77: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:27, 48.45it/s]\n", - "Epoch 78: : 534it [01:47, 4.95it/s, loss=0.0158] \n", - "4224it [01:27, 48.42it/s]\n", - "Epoch 79: : 534it [01:47, 4.96it/s, loss=0.0163] \n", - "4224it [01:26, 48.85it/s]\n", - "Epoch 80: : 534it [01:47, 4.96it/s, loss=0.0156] \n", - "4224it [01:27, 48.52it/s]\n", - "Epoch 81: : 534it [01:47, 4.97it/s, loss=0.0158] \n", - "4224it [01:27, 48.44it/s]\n", - "Epoch 82: : 534it [01:47, 4.97it/s, loss=0.0163] \n", - "4224it [01:26, 48.57it/s]\n", - "Epoch 83: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:27, 48.24it/s]\n", - "Epoch 84: : 534it [01:47, 4.96it/s, loss=0.016] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 85: : 534it [01:47, 4.99it/s, loss=0.0153] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 86: : 534it [01:47, 4.98it/s, loss=0.0167] \n", - "4224it [01:27, 48.54it/s]\n", - "Epoch 87: : 534it [01:47, 4.96it/s, loss=0.0159] \n", - "4224it [01:27, 48.22it/s]\n", - "Epoch 88: : 534it [01:47, 4.96it/s, loss=0.0159] \n", - "4224it [01:26, 48.77it/s]\n", - "Epoch 89: : 534it [01:47, 4.95it/s, loss=0.0164] \n", - "4224it [01:26, 48.56it/s]\n", - "Epoch 90: : 534it [01:47, 4.96it/s, loss=0.0161] \n", - "4224it [01:26, 48.68it/s]\n", - "Epoch 91: : 534it [01:47, 4.95it/s, loss=0.0158] \n", - "4224it [01:26, 48.94it/s]\n", - "Epoch 92: : 534it [01:47, 4.96it/s, loss=0.0158] \n", - "4224it [01:26, 48.65it/s]\n", - "Epoch 93: : 534it [01:47, 4.96it/s, loss=0.0166] \n", - "4224it [01:26, 48.70it/s]\n", - "Epoch 94: : 534it [01:47, 4.98it/s, loss=0.0161] \n", - "4224it [01:26, 48.78it/s]\n", - "Epoch 95: : 534it [01:48, 4.94it/s, loss=0.0155] \n", - "4224it [01:26, 48.95it/s]\n", - "Epoch 96: : 534it [01:47, 4.98it/s, loss=0.0162] \n", - "4224it [01:26, 48.96it/s]\n", - "Epoch 97: : 534it [01:47, 4.97it/s, loss=0.016] \n", - "4224it [01:26, 48.79it/s]\n", - "Epoch 98: : 534it [01:47, 4.98it/s, loss=0.016] \n", - "4224it [01:27, 48.48it/s]\n", - "Epoch 99: : 534it [01:47, 4.98it/s, loss=0.0157] \n", - "4224it [01:27, 48.33it/s]\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "train diffusion completed, total time: 19490.821256637573.\n" + "Epoch 0 Validation loss 0.9828271865844727\n", + "Epoch 20 Validation loss 0.45277565717697144\n", + "Epoch 40 Validation loss 0.16044068336486816\n", + "Epoch 60 Validation loss 0.06908729672431946\n", + "Epoch 80 Validation loss 0.037922561168670654\n", + "Epoch 100 Validation loss 0.024700244888663292\n", + "Epoch 120 Validation loss 0.02825773134827614\n", + "Epoch 140 Validation loss 0.01575350947678089\n", + "Epoch 160 Validation loss 0.02807718887925148\n", + "Epoch 180 Validation loss 0.03635002672672272\n", + "Epoch 200 Validation loss 0.018522320315241814\n", + "Epoch 220 Validation loss 0.020984284579753876\n", + "Epoch 240 Validation loss 0.02985953912138939\n", + "Epoch 260 Validation loss 0.018604595214128494\n", + "Epoch 280 Validation loss 0.02505004033446312\n", + "Epoch 300 Validation loss 0.018166495487093925\n", + "Epoch 320 Validation loss 0.012706207111477852\n", + "Epoch 340 Validation loss 0.03222103416919708\n", + "Epoch 360 Validation loss 0.010545151308178902\n", + "Epoch 380 Validation loss 0.017768580466508865\n", + "Epoch 400 Validation loss 0.023036960512399673\n", + "Epoch 420 Validation loss 0.023991823196411133\n", + "Epoch 440 Validation loss 0.014143284410238266\n", + "Epoch 460 Validation loss 0.010133783333003521\n", + "Epoch 480 Validation loss 0.019768211990594864\n", + "Epoch 500 Validation loss 0.016018100082874298\n", + "Epoch 520 Validation loss 0.016411196440458298\n", + "Epoch 540 Validation loss 0.012067019008100033\n", + "Epoch 560 Validation loss 0.017793692648410797\n", + "Epoch 580 Validation loss 0.015390219166874886\n", + "Epoch 600 Validation loss 0.015438873320817947\n", + "Epoch 620 Validation loss 0.019228052347898483\n", + "Epoch 640 Validation loss 0.022589124739170074\n", + "Epoch 660 Validation loss 0.022526469081640244\n", + "Epoch 680 Validation loss 0.0310574471950531\n", + "Epoch 700 Validation loss 0.016018839552998543\n", + "Epoch 720 Validation loss 0.018153013661503792\n", + "Epoch 740 Validation loss 0.01506253331899643\n", + "Epoch 760 Validation loss 0.00914084818214178\n", + "Epoch 780 Validation loss 0.017407484352588654\n", + "Epoch 800 Validation loss 0.013946758583188057\n", + "Epoch 820 Validation loss 0.013289306312799454\n", + "Epoch 840 Validation loss 0.007855996489524841\n", + "Epoch 860 Validation loss 0.01187637448310852\n", + "Epoch 880 Validation loss 0.018494905903935432\n", + "Epoch 900 Validation loss 0.009516816586256027\n", + "Epoch 920 Validation loss 0.030950400978326797\n", + "Epoch 940 Validation loss 0.017931077629327774\n", + "Epoch 960 Validation loss 0.017525378614664078\n", + "Epoch 980 Validation loss 0.016576599329710007\n", + "Epoch 1000 Validation loss 0.007525463588535786\n", + "Epoch 1020 Validation loss 0.008745957165956497\n", + "Epoch 1040 Validation loss 0.023068588227033615\n", + "Epoch 1060 Validation loss 0.023049402981996536\n", + "Epoch 1080 Validation loss 0.020367465913295746\n", + "Epoch 1100 Validation loss 0.026941468939185143\n", + "Epoch 1120 Validation loss 0.019598377868533134\n", + "Epoch 1140 Validation loss 0.023052945733070374\n", + "Epoch 1160 Validation loss 0.020239276811480522\n", + "Epoch 1180 Validation loss 0.009076420217752457\n", + "Epoch 1200 Validation loss 0.011559909209609032\n", + "Epoch 1220 Validation loss 0.023455770686268806\n", + "Epoch 1240 Validation loss 0.015224231407046318\n", + "Epoch 1260 Validation loss 0.020417172461748123\n", + "Epoch 1280 Validation loss 0.025817634537816048\n", + "Epoch 1300 Validation loss 0.012675277888774872\n", + "Epoch 1320 Validation loss 0.014165625907480717\n", + "Epoch 1340 Validation loss 0.021743204444646835\n", + "Epoch 1360 Validation loss 0.00959782674908638\n", + "Epoch 1380 Validation loss 0.014942880719900131\n", + "Epoch 1400 Validation loss 0.033313099294900894\n", + "Epoch 1420 Validation loss 0.025836177170276642\n", + "Epoch 1440 Validation loss 0.015067282132804394\n", + "Epoch 1460 Validation loss 0.01235564611852169\n", + "Epoch 1480 Validation loss 0.012111244723200798\n", + "Epoch 1500 Validation loss 0.00833088904619217\n", + "Epoch 1520 Validation loss 0.01528056338429451\n", + "Epoch 1540 Validation loss 0.017444560304284096\n", + "Epoch 1560 Validation loss 0.014621825888752937\n", + "Epoch 1580 Validation loss 0.019431518390774727\n", + "Epoch 1600 Validation loss 0.016186822205781937\n", + "Epoch 1620 Validation loss 0.02027059532701969\n", + "Epoch 1640 Validation loss 0.01720491796731949\n", + "Epoch 1660 Validation loss 0.011756360530853271\n", + "Epoch 1680 Validation loss 0.02627478912472725\n", + "Epoch 1700 Validation loss 0.023451916873455048\n", + "Epoch 1720 Validation loss 0.011613328941166401\n", + "Epoch 1740 Validation loss 0.026256393641233444\n", + "Epoch 1760 Validation loss 0.008156227879226208\n", + "Epoch 1780 Validation loss 0.01597723178565502\n", + "Epoch 1800 Validation loss 0.013070507906377316\n", + "Epoch 1820 Validation loss 0.01726200059056282\n", + "Epoch 1840 Validation loss 0.009824991226196289\n", + "Epoch 1860 Validation loss 0.014878236688673496\n", + "Epoch 1880 Validation loss 0.017673484981060028\n", + "Epoch 1900 Validation loss 0.016455603763461113\n", + "Epoch 1920 Validation loss 0.02442217618227005\n", + "Epoch 1940 Validation loss 0.026278261095285416\n", + "Epoch 1960 Validation loss 0.02376818098127842\n", + "Epoch 1980 Validation loss 0.016214493662118912\n", + "train diffusion completed, total time: 6097.77689909935.\n" ] }, { "data": { - "image/png": "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7NKmTZsMwACMSZMmFbl+WlqaMXr0aMs2cns5Ozsbn3zySa71T506lW/drNdjjz1mpKWl5bqNL774wlJu586dRvPmzXPUzzq27GXDw8ONzp0757nPVq1aGfHx8bnuc+jQoQZgBAUF5bo++35nz55tODk55boPDw8PY8uWLXme33Pnzhl16tTJs419+vQxfvrpJ8v/b9q0Kc9t5eWHH36wOs/FVdC5yZL9Z3H69Okc64OCggzAGDp0qPHRRx/leg4NwzDeeOMNAzBMJlOu28nuP//5j6Xu7t27c6w/ceKE0bBhw3zfi3Xr1jWOHTuW6/Y/+OADw8HBocD3c1xcXL7tzM3p06ct9YcOHVroeqNGjbLUW7hwYY712c9zbvvK6xUUFGT1M8zrlX27ue0rN/m9h+Li4oz77ruvwP0+/PDDOepOmjTJ6r2Tm9DQUMPV1TXP7To6Ohr//ve/86xfUr/7Bcn+t/uLL74w9uzZY/W37UYHDx60rN+zZ4/Vzy6vvxtnzpwxGjRokO95bteunXH16tU827l582bDx8cnz/pTpkwp1M9lx44dRvXq1fNtS6tWrYyLFy/mWr9Dhw4GYHTo0CHf81raNOfpNjVy5EjLt5levXoxZMgQ6tWrh8lkYu/evcycOZODBw8yatQoqlatSt++fa3qZ2Rk4OLiQo8ePejWrRsNGzbEz8+P6Ohojh07xpw5czh48CALFiygVq1aTJkypcD2HDhwgCeeeIJBgwZRtWpVzp07h6ura46yo0aNYtu2bQwdOpRHHnnEUvbdd9/l999/Z8eOHUybNo233377ps/P2rVr2b59O02bNuWFF16gSZMmJCUlsWLFCmbNmkViYiKPP/44x48fx8XFxapuamoqvXv35sSJE5bzO2rUKAIDAzl//jyffPIJK1eu5PLlyzfdPsDSswDQp0+fYm3rVti5cycLFiwgMDCQl19+mbvuuouMjAx++eUXAIYMGcK0adMwDINFixbx2muv5bmthQsXAhAcHMydd95pte7PP//k3nvv5dKlS3h7ezNq1Ci6du1KlSpViImJ4aeffmLWrFkcP36cnj17smfPHnx9fS319+/fz8svv4zZbOaOO+7g2WefpXnz5vj5+REfH8/x48fZtGkTK1asuAVnKW9du3blk08+AeCXX34pcEIxQPXq1S09LMOHD2fXrl3cfffdfPHF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6NixIy4uLowdO5YNGzbg4eEB6DwX15kzZ+jbty9VqlRh6dKlVKpUie3btzNt2jTi4+P5/PPPLWV1rm+dwp7b0vgZKDyVIRUqVMg1FUdHRwO5p24pmilTpjBt2jTeeustnn32WcvyChUqALn37kVHR2MymShXrlxpNdNuxcfHM2bMGJ577jn8/f25fv06AKmpqQBcv34dZ2dnne8SUKFCBY4fP06PHj2slvfq1YuxY8eyZ88eHnzwQUDnubheffVVYmNj2bt3r6VHo3379lSsWJERI0bwxBNPULVqVUDn+lYoyt+LChUqkJycTGJiouXLQ/ayN35hvlkatitDmjRpwuHDh0lPT7dafuDAAQAaN25si2b9bUyZMoXJkyczefJkXnvtNat1tWvXxt3d3XKusztw4AB16tTBzc2ttJpqt65cucKlS5eYMWMG5cuXt7zCwsJISEigfPnyDBkyROe7BOQ2/wOw3KbAwcFB57mE7N27l4YNG+YYCmrZsiWAZThP5/rWKMq5zZrrdGPZixcvcuXKlRL7HFV4KkP69etHfHw8y5Yts1oeGhqKv78/rVu3tlHL7N/UqVOZPHkyb7zxBpMmTcqx3snJib59+7J8+XLi4uIsy8+dO8emTZvo379/aTbXblWtWpVNmzblePXo0QM3Nzc2bdrEtGnTdL5LwMMPPwzA6tWrrZavWrUKgDZt2ug8lxB/f38OHjxIfHy81fLff/8dgICAAJ3rW6go57Znz564ubkxf/58q23Mnz8fk8mU772kiqREbnggJaZbt25G+fLljU8++cTYuHGj8dRTTxmAsWDBAls3zW69//77BmD07NnT+P3333O8shw+fNjw8vIy2rdvb6xatcpYvny50bhxY8Pf39+Iioqy4RHYv9zu86TzXXx9+/Y1XF1djalTpxrr1q0z3n77bcPNzc3o06ePpYzOc/F99913hslkMtq0aWN8/fXXxoYNG4y33nrL8PLyMho2bGikpKQYhqFzfbNWrVplfPPNN8a8efMMwBg4cKDxzTffGN98842RkJBgGEbRzu20adMMk8lkvPbaa8bmzZuN9957z3B1dTWeeuqpEmuzwlMZExcXZzz//PNG1apVDRcXF6Np06ZGWFiYrZtl1zp06GAAeb6y27Vrl9GlSxfDw8PD8PHxMR566KEcN8aTosstPBmGzndxJSYmGv/6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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHZCAYAAABn8CRaAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAACIC0lEQVR4nO3dd3xT5dsG8OukI92FsktL2VKgDEGW7A2CbNkbQQTFySuCAoKCAwQR/IEyKhsEFNnIFgQZsstehbJL90zyvH8ccpo0SWfSpOX68umH9Mzn5CTNnftZkhBCgIiIiOgFprJ3AYiIiIjsjQERERERvfAYEBEREdELjwERERERvfAYEBEREdELjwERERERvfAYEBEREdELjwERERERvfAYEBEREdELjwERFQhDhgyBJEkoW7asvYtClCtly5aFJEkYMmSIxW0SExMxdepU1KxZE56enpAkCZIk4b333jPa7s6dOxg1ahQqVKgANzc3Zbvff//dpteQVVOmTFHKRI4jL+5L8+bNIUkSmjdvbrNzZBcDIgezf/9+5YU4ZcoUexeHHER4eDi+/fZbtG3bFuXKlYOXlxfc3d1RunRptGvXDtOnT8fNmzftXcwXyq1bt5T3quGPk5MTChUqhKCgIDRo0ABjxozB8uXLERcXZ5XzpqamonXr1pgyZQrOnj2LhIQEs9vduXMHderUwaJFi3Djxg0kJydb5fxknuHfbkmS4O3tbfHeGEpMTISvr6/Rvvv377d9gcmEs70LQESWJScn49NPP8X8+fPNfqBFREQgIiICu3btwueff45evXrhu+++Q2BgoB1KSwCg0+kQHR2N6Oho3LlzB8eOHcOCBQvg7e2NESNGYNq0afD09Mzx8devX48jR44AkDOjgwcPRtGiRQFA+R8Apk+fjidPnsDZ2RlffvklmjZtCi8vLwBAUFBQLq6QsiIuLg6///47+vXrl+F2f/zxB2JiYvKoVJQRBkRUICxbtgzLli2zdzGs6unTp3j99deVDz9vb2/07dsXrVq1QkBAAFxcXPDgwQMcPnwYGzduxNWrV7Fu3To0bNjQpOqEbKtLly6YPn268ntCQgKioqJw8eJFHDhwAFu2bEFsbCy+//57bN26FVu2bEGlSpXMHuvWrVsZnuuvv/4CAJQsWRK//PILnJycMtyua9euGD9+fA6uyvamTJlSIDPhbm5uSEpKwvLlyzMNiJYvX260D9kPAyIiB6TT6dCnTx8lGOrYsSOWLl2K4sWLm2zbuXNnfPXVV1ixYgU+/vjjvC4qAShUqBCqV69usrxt27Z47733cOfOHYwYMQK7d+/GlStX0KlTJxw7dgyFChXK9rnu3bsHAChfvrzFYMhwu8qVK2f7HJQ7r7/+OtatW4fdu3fjwYMHKFmypNntHj16hF27dgGQg+q1a9fmZTEpHbYhInJA8+bNU77ht27dGn/88YfZYEhPpVJh0KBBOHnyJGrUqJFXxaQsKlOmDLZv347XXnsNAHDlypUcZ0b0VacuLi4ZbpeSkpKl7cj62rZti5IlS0Kr1WL16tUWt1u9ejU0Gg1KlCiBNm3a5GEJyRwGRAXUv//+izfffBOVK1eGl5cXPD09UaVKFYwZMwZXr17NcN8bN25g1qxZ6Ny5M8qWLQt3d3e4u7sjKCgIvXv3xo4dOzLcf9myZUrjwFu3biE5ORlz5sxBgwYNULRoUaMG4+m31el0WLRoERo1aoTChQvD09MTNWrUwJdffplhA8XMepmlb6h+/Phx9O3bFwEBAVCr1ShdujQGDhyIsLCwDK8NAOLj4/HFF18gJCQEnp6eKFKkCBo3bowlS5ZACGHUuDInjSNTU1Px7bffApDT6EuXLoWzc9aSuQEBAWjZsqXRsqz2wEt/L9JL3/vp5MmTGDJkCMqVKwe1Wq30SKlQoQIkSULjxo0zLe+DBw/g7OwMSZLw4Ycfmt1Go9Fg8eLF6NixI/z9/aFWq1G0aFE0bdoUc+bMybSa4eTJkxg+fDgqV64MT09PuLm5ITAwEHXq1MGYMWOwefNmCCEyLWtuOTk5YdmyZfDw8AAA/Pzzz3jy5InJduZ6mRk24D5w4AAA4MCBA0YNccuWLWt0D/WmTp1qtJ3hcbPSow3I/DWk1WqxbNkytGvXDiVLloSrqysKFSqESpUqoVWrVvjqq69w8eJFk/2y2pvp1q1beP/991GtWjV4e3vDw8MDlSpVwqhRo3Du3LkM97Xmez+rnJyc0LdvXwBpVWLm/PrrrwCAfv36ZZjtM5SSkoIFCxagRYsWKFasGFxdXVGyZEl07NgRK1asgE6ny/QYd+/exZgxY1C+fHm4ubnB398fr7/+uvIlLKsSEhIwZ84ctGjRAiVKlICrqyuKFy+Otm3bYunSpdBqtdk6nt0Jcij79u0TAAQAMXny5Gzvn5qaKkaPHq0cw9yPi4uLWLRokdn9b9y4keG++p8BAwaI1NRUs8dYunSpst3x48dFrVq1TPbXX5vhtufPnxctW7a0eM569eqJuLg4s+ccPHiwACCCgoLMrjc877x584Szs7PZc3h4eIgDBw5YfH7v3LkjKlasaLGMnTp1Ert27VJ+37dvn8VjWfLnn38aPc+5ldlzo2d4L27evGmyPigoSAAQgwcPFj/99JPZ51AIISZNmiQACEmSzB7H0Pfff6/se/LkSZP1165dE1WrVs3wtVipUiVx5coVs8efPXu2UKlUmb6eY2NjMyynOTdv3lT2Hzx4cJb3GzlypLLfypUrTdYbPs/mzmXpJygoyOgeWvoxPK65c5mT0WsoNjZWNGnSJNPz9ujRw2TfyZMnG712zAkNDRVqtdricZ2cnMRXX31lcX9rvfczY/i3e+nSpeLUqVNGf9vSu3DhgrL+1KlTRvfO0t+NW7duieDg4Ayf58aNG4unT59aLOf+/fuFj4+Pxf2nTp2apfvy77//itKlS2dYlnr16okHDx6Y3b9Zs2YCgGjWrFmGz2teYhuiAmb48OHKt44OHTqgf//+qFy5MiRJwunTpzFnzhxcuHABI0eORMmSJdG5c2ej/bVaLVxdXdGuXTu0adMGVatWhZ+fHyIjI3HlyhXMnz8fFy5cwIoVK1C+fHlMnTo10/KcO3cOgwYNQu/evVGyZEncuXMHarXaZNuRI0fi6NGjGDx4MN544w1l22+++Qb//PMP/v33X0yfPh0zZszI8fOzc+dOHDt2DDVq1MC4ceMQEhKCxMREbNq0CXPnzkVCQgIGDhyIq1evwtXV1WjflJQUdOzYEdeuXVOe35EjRyIwMBB3797FokWLsGXLFjx+/DjH5QOgZAAAoFOnTrk6li0cP34cK1asQGBgID766CPUqVMHWq0Whw4dAgD0798f06dPhxACq1atwqeffmrxWCtXrgQAVKlSBS+//LLRuvv37+PVV1/Fw4cP4e3tjZEjR6J169YoUaIEoqOjsWvXLsydOxdXr15F+/btcerUKfj6+ir7nz17Fh999BF0Oh3KlSuHsWPHolatWvDz80NcXByuXr2Kffv2YdOmTTZ4lixr3bo1Fi1aBAA4dOhQpo1uAaB06dJKJmTo0KE4ceIE6tati6VLlyrb6L+d161bFwAQEhICABg9ejTefvttZbvChQtb7VoAOcujv/edOnVC//79UaZMGbi5ueHx48c4c+YMtmzZkqMxbbZu3YohQ4ZACAEvLy98+OGHaN26NZydnXHkyBHMmDEDT548waeffopChQph9OjRFo+Vm/d+TtSuXRvVq1fH+fPnsXz5csycOdNovT5zVK1aNdSuXRtnzpzJ8HhxcXFo2bIlbty4AUBuLD9s2DD4+/vj5s2b+PHHH3HgwAH8/fff6NSpEw4dOmSSdbp16xY6d+6M2NhYqFQqjBw5Ej179oSvry/Onj2LmTNnYvLkycpryJJz586hRYsWiI+PR/HixTF69Gg0adIERYoUwaNHj7B582YsXLgQ//77L7p06YJDhw7lj6pbe0dkZCw3GaLffvtN2ffnn382u01iYqKShSlbtqxJlicuLk5ERERYPIdOpxNDhgwRAISnp6eIiooy2Sb9t9TFixdbPF76bZcvX26yTVJSkqhevboAIIoUKWI2M5XVDBEA0bFjR5GcnGyyzfTp05VtNm7caLJ+9uzZyvqxY8eaPc/YsWONzpWTDFGbNm2U/S1lPrLD2hkiACIkJEQ8e/bM4rFefvllAUBUq1bN4jZXrlxRjjdt2jST9Z06dRIARGBgoLh+/brZY5w6dUp4enoKAGLSpElG6z777DPldWrpW6oQQkRFRQmtVmtxvSU5zRBdu3ZN2a9ly5Ym6zPL2mT1m3VW/o5YI0MUGBgoAIiePXtmeAxzWYuMMhEpKSlKBsLLy0v8999/JtvcunVLlCpVSsnwPH782GQba7z3syJ9hkgIIb7++msBQAQEBBi9xnQ6nfK8zZw5UwghMs0QffTRR8r69K91/TH79++vbLNgwQKTbbp3766sX7Vqlcn6mJgYUbNmTaPnzNx5atSoIQCImjVrmn3OhRBi+/btSnb2l19+MVnviBkitiEqQPSZk27dumHEiBFmt3Fzc8OPP/4IQP62kL6Ni6enJ0qVKmXxHJIkYdasWXByckJ8fHymdc4tW7bEsGHDslT+7t27Y8CAASbL1Wo1xo4dC0Duim6uLUJW6dvkmPsG+O677yrL9d94DS1cuBAA4O/vr7TxSe/bb7+Fv79/jssHwKhdSYkSJXJ1LFuZP39+hj2k+vfvDwC4cOGCxW+++uwQAJMsyfnz57FlyxYAwI8//ojy5cubPUbt2rUxZswYAMCSJUuM1j148ACA3Msqo+fR19cXKlXe/SksUqSI8vjZs2d5dl5b0T/PTZo0yXA7Pz+/bB1306ZNSk+5iRMnolatWibbBAUFKe/FhIQEo4xZerl57+dU//79oVKpcPfuXaPM7/79+xEeHg6VSqW8VzKSnJyMX375BQBQtWpVsw3yJUnCggULlNeX/u+83v379/HHH38AkDN5+jZOhry9vZXspSVbt27F2bNnAchtoAzHvjLUvn179OzZEwAyvC+OhAFRAXHv3j2cPHkSAPDGG29kuG1wcLDyIv7nn38y3DY1NRV3795FWFgYzp8/j/PnzyMiIkJ502WW5s3Kmz0r29apU0d5rE8Z50SbNm0s9tby9vZWxoZJf4579+7h8uXLAOTn183Nzewx3Nzc0KtXrxyXDwBiY2OVx7kZwM9WAgMDM/3w69u3rxJkrFq1yuw2+t43DRs2NAl49H+4PTw8lJ5ZljRt2hSAPEhleHi4slwf2F+8eBH//vtvhsfIS/rBEQHje51f6Z/ntWvXZmlk5qzSf9mSJCnDL1W9evVSqkoz+oKW0/d+bpQuXRotWrQAYNy4Wv+4efPmCAgIyPQ4J0+eRFRUFAC5gbulBtg+Pj7K3/+LFy/i/v37yrp9+/YpjZyHDh1q8Vz16tVDtWrVLK7XvzdfeumlTHu06t+bx48fzxcNrBkQFRAnTpxQHvft29fslAKGP/oshP7bnaHU1FTMnz8fDRo0gJeXFwIDA1G1alWEhIQoP48ePQIAs71kDGWnC3iVKlUsrjP8dpmbD5GMzmF4nvTnOH/+vPLYMDgzJ7P698x4e3srj+Pj43N1LFvIyj0tVaqU0ttt9erVJr24jh8/jitXrgAwHwjrX88JCQlKLzRLP4btrAxfz3379oWLiwuSk5Px6quvonPnzvjf//6HCxcu5EmvMksMX1s+Pj52K4e1DB48GABw5MgRpa3Wpk2bct2WTv+eK1u2bIZDTri6uqJ27dpG+5iT0/d+bg0aNAgA8NtvvyExMRGJiYnYsGEDAGDgwIFZOobhddWvXz/DbQ3XG+5n2BvvlVdeyfAY9erVs7hO/968fPlypp8z+sx+SkoKIiMjMzynI2BAVEDoA5TsSv+NLjIyEg0bNsTYsWNx7NgxZSwTSxITEzNcn50GnPruyOYYVmnk5ptGRucwPE/6cxhWbWT0xxkAihUrlsPSyQxT0A8fPszVsWwhq/dUH+iEh4fj4MGDRuv01WXOzs5mM5rWeD1XqVIFq1evRuHChaHRaLBlyxaMHj0a1atXR/HixTFw4ECrVo9kleGXiOxWIzmizz77DMOGDYMkSXj06BHmz5+P7t27o0SJEggJCcHkyZNz9DrWf4BmpdpYP/BhRh+6OX3v51b37t3h4eGB2NhY/PHHH/j9998RExMDd3d39OjRI0vHMLyuzJ4Pw0EgDffLzt+wjM5hrc8aR8ReZgWE4Zt45cqVWc7MpP9wGzdunFL1pu/FUKNGDRQvXlyZLRuQB5oLDw/P9Jt2VsfWoDQ1a9bE7t27AQCnTp2yOMWDvWT1nnbv3h1vv/02EhMTsWrVKjRr1gyA/FrVj8jbtm1bswGk/vVcrlw5bN68OctlK1eunNHvPXr0QOvWrbF27Vrs3LkThw4dwuPHj/HkyROsWLECK1aswODBg7FkyZI8a0f033//KY9feumlPDmnLbm4uGDx4sX48MMPsXr1auzduxcnTpxASkqKUs0+e/ZsrFixAl26dMn28bPSO82eGb/MeHl5oVu3bli5ciWWL1+ulLVr165G2eCsyuz5sPRcGC7P6TGAtPfmq6++iv/9738ZHsdQbttW5gUGRAWEYUNNSZLMTiOQmZiYGOWDql+/fkaNXtMrCI1Bs8MwcMzsG1JuqwqaNWuG7777DoDcgLF37965Op7+gz6zAdusXT3n4+ODzp07Y926dVi/fj3mzZsHV1dX7N27V6nastRuTP96fvjwIapUqZLlgSnN8fX1xciRIzFy5EgActuKzZs3Y968eYiIiEBoaChq166NcePG5fgc2aEPdgFkafBKW7Lma6Nq1aqYNm0apk2bhsTERBw+fBirVq3Cr7/+iri4OPTt2xfXr1/PsNOGIX32zFy1fnr6DJSjZtwGDRqElStXKtN0AFmvLgOMr+vBgwcZTsdimI0z3M/w8cOHDzOcADqjv3FFihTBw4cP8fjx4xx9zjgyVpkVEPo6dABGb7rsuHr1KlJTUwEAffr0sbjd5cuXERcXl6Nz5FeGjQwN22uZk9n6zLRt21b5NrV+/Xqlp01O6b+F6htlWqJvNG5N+oDn2bNnygjn+kbWnp6eFjMG+tdzQkICDh8+bNUyVa1aFZ988gmOHj2qNFpft26dVc9hyePHj42uv23btnlyXkv0r43MvuBk97Xh7u6O1q1bY8mSJUovsMTERKXnYFboP2xv3bqV4Qd0amqqknVz1A/oVq1aoVSpUtBoNMpUHdm594bXdezYsQy3NexAYLifflwqQG7Dl5GM1uvfm1euXMHt27czPE5+w4CogKhYsSKqVq0KAFizZg3u3LmT7WNoNBrlcUb1vdlJkxYUAQEByrey9evXW5wuIikpCevXr8/VuVxdXfHRRx8pxxs+fHiW2zXcvXsXe/fuNVqmr0aKjY21+MGWkpKiNPS0pg4dOijfTFeuXImkpCRs3LgRgFxlYKkXnWGg9M0331i9XIDcW05/TzPrHGANOp0OQ4YMUd5bI0eOtHtGQ//aOHXqlMVqkvPnz2c6PUZGWrVqpTzOzvPcunVrAHL1TfohFQz99ttviI6ONtrH0Tg5OWHgwIFQq9VQq9UYMGBAtpoT1KlTRxnmIjQ01OLfg9jYWCW4r1q1qlE2rkWLFso5Q0NDLZ7rxIkTGTZOf/3115XHtnpv2gsDogJk0qRJAOQP0e7du2dYdZOcnIwFCxYYfbBXrFhRqVvWj3ad3pYtWzBv3jwrljr/GDVqFAC5e7elWeU//vhjRERE5Ppc48aNU7rr7ty5E926dcvwfgohsHLlStSpU0cZI0RP33YHAGbNmmV233Hjxlml3Om5uLgowxD8+eefWLVqFWJiYgBkPMzCK6+8onyD3rZtGyZPnpzheW7dumUyiebvv/+eYVYsPDwcly5dAmDa9sja7ty5g/bt22Pbtm0A5AbfmV1TXtC/NiIiIsxOQhobG5thl/fIyMhM54IzzFhn53nu1q2bkin96quvzA7xER4ernx58PDwyLA7ub19/fXXSEpKQlJSklIlnlVqtVoZW+7ChQtmZwgQQmDs2LFK0Knv4aVXqlQp5YvG5s2bzWZF4+LilKplS3r06IHg4GAAwE8//YTFixdnuP358+fx559/ZriNo2AbIgd2+vRpLFu2LNPtGjdujIoVK6Jv377YuXMnQkNDcfLkSVStWhWjRo1Cs2bNUKxYMcTHx+P69es4dOgQNm7ciMjISKVLKCDXDXfs2BFbt27Ftm3b0L59e4waNQplypTBo0ePsGHDBixbtgzly5dHVFRUrtvK5Ddjx47F0qVLcf78efz444+4ceMGRo0ahYCAAGXqjq1bt6JevXpK2jon0xUActuOdevWoVOnTjh27Bj+/PNPVKhQAf3790fLli0REBAAFxcXPHjwAEePHsWGDRuUD/f0ateujQYNGuDo0aP4+eefkZKSgsGDB8PX1xdXr17F//73P+zfvx8NGzbMdFyqnBgwYAAWLlyIxMREZQLXYsWKZTq799KlS1G3bl3cv38fX3zxBXbu3Ilhw4YhJCQEbm5uePr0Kc6ePYsdO3Zg79696Nq1q9Fgc3PmzEH//v3x2muvoWXLlggODoavry+ePXuGEydOYN68eUovyYymfMiKqKgoo2/ViYmJiIqKwsWLF7F//35s2bJFycC+9NJL2LJli9E0I/YyYMAATJkyBTExMRg+fDiuXbuGdu3aQZIknDhxArNnz8a9e/dQu3Zto8bgejExMejSpQvKli2L7t27o379+ggKCoKzszPu37+PP//8UxlQMCAgwGSqoIy4uLhg0aJFylQTjRs3xscff4xWrVopU3fMnDlTqU777rvvLA4SWBB8/vnn2LhxI27cuIFp06bh/PnzJlN36AfabdiwodnAZtasWdi9ezdiY2PRr18/HDhwAD179oSPj48ydceVK1dQt25di1X/Tk5OWLt2LRo1aoS4uDiMGDEC69evR79+/fDSSy/BxcUFjx49wn///YctW7bgyJEj+PDDD7N17+3GHsNjk2WGw79n9Uc/TLwQQmg0GjF+/Hjh5OSU6X6enp4iISHB6Px37twRZcqUsbhPmTJlxIULFzIc8j+zKSBysq3hNAmG16uXncldM5LZcPK3b98WFSpUsPj8tG3bVmzfvl35/ejRoxmeLzOJiYli3LhxwtXVNdP7KUmSGDBggLh3757JccLCwkTx4sUt7vvBBx9ka3LX7NDpdEbTfiCDqU/Su3XrlnjllVey9D4YOnSo0b76e5nRT2YTg2YkKxOuGv74+PiIDz74QMTHx2d43LycukMIIdatW2fx74Wbm5tYt26dxfdXVp+D0qVLi1OnTpmcOyuTiC5btsxqk7tmJLdTSZibuiM7sjK5682bN0WVKlUyfK5fffXVDCd33bdvn/D29ra4/+TJk7N0X86cOSMqVaqUpfs/depUk/05dQfZnJOTE77++mtcvHgRH374IWrXro3ChQvDyckJ3t7eqFatGvr374/Q0FDcv38f7u7uRvsHBgbi1KlT+Pjjj1G5cmWo1Wr4+vqiZs2amDx5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HYWLi4t4/PixsiyvrzG73dJz+3p0xOvTD5WwYMGCbB3bnvcvK58J+eU9yIDITtq0aSMKFy4sFi1aJPbu3SvefPNNAUCsWLHC3kWz6LvvvhMARPv27c0OJCaEPLZEo0aNxA8//CC2bdsmtm/fLj755BPh5uYmqlWrJuLi4oyOqR+E69NPPxX79+8X3377rVCr1XYZuLBv377i//7v/5TB7RYtWiReeukl4ezsLHbv3q1sFxYWJry8vETTpk3Ftm3bxMaNG0X16tUzHGTMEa7P0Jo1awQAsWjRIpN1+ekebtu2Taxfv14sWbJEABC9evUS69evF+vXrxfx8fFCCNvcr6SkJFGtWjURGBgoVq5cKXbv3i26detm9YEZs3KNCQkJ4pVXXhGSJIm5c+eavC+vXbumHOubb74RQ4YMEcuXLxf79u0Ta9euFW3bthUAxJQpU+xyjZldn61ej45yfYbat28v3N3dRVRUlNljOdr9y8pnghD55z3IgMhOYmNjxbvvvitKliwpXF1dRY0aNcTq1avtXawMNWvWTACw+COE/E21W7duomzZssLd3V24urqKSpUqifHjx1t8k8+dO1dUrlxZuLq6ijJlyojJkyeLlJSUvLw0IYT8LaRWrVrC19dXODk5iWLFiolu3bqJf//912TbEydOiFatWgkPDw/h4+MjunbtavTBY8hRrs9QmzZthKenp9E3Nr38dA+DgoIsvh5v3rypbGeL+/XgwQMxaNAg4efnJ9zc3ESDBg2MAue8usabN29m+L4cPHiwcqzNmzeLxo0bi2LFiglnZ2dl9GBLf3vy4hozuz5bvh4d4fr07ty5I1QqlRg0aJDFYzna/cvKZ4JefngPSkI8r3wkIiIiekGxUTURERG98BgQERER0QuPARERERG98BgQERER0QuPARERERG98BgQERER0QuPARERERG98BgQERHlkiRJdp3hnohyjwEREeWpsmXLKgFERj/Lli2zd1GJ6AXibO8CENGLqVKlSihevLjF9SVKlMjD0hDRi44BERHZxaeffoohQ4bYuxhERABYZUZERETEgIiIHJ9ho+VVq1ahXr168PLygp+fH7p27Yrz589b3Dc+Ph7Tp09HjRo14OnpCR8fH9SvXx/z58+HRqOxuF9kZCQmT56M2rVrw8fHB15eXggODsZbb72F//77z+J+27dvR9Om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\n", "text/plain": [ "
" ] @@ -640,67 +596,55 @@ } ], "source": [ - "n_epochs = 100\n", - "batch_size = 32\n", - "val_interval = 1\n", + "n_epochs = 2000\n", + "val_interval = 20\n", "epoch_loss_list = []\n", "val_epoch_loss_list = []\n", "\n", "scaler = GradScaler()\n", "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " model.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) #\n", - "\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " optimizer.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", - "\n", - " with autocast(enabled=True):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noise_pred = inferer(\n", - " inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) \n", - "\n", - " loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " scaler.scale(loss).backward()\n", - " scaler.step(optimizer)\n", - " scaler.update()\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - "\n", - " if (epoch) % val_interval == 0:\n", - " model.eval()\n", - " val_epoch_loss = 0\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", "\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - " with torch.no_grad():\n", - " with autocast(enabled=True):\n", - " noise = torch.randn_like(images).to(device)\n", - " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", - " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " progress_bar.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + "\n", + " for step, data in enumerate(train_loader):\n", + " images = data['image'].to(device)\n", + " classes = data['slice_label'].to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + " epoch_loss += loss.item()\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + "\n", + " for step, data in enumerate(val_loader):\n", + " images = data['image'].to(device)\n", + " classes = data['slice_label'].to(device)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + " print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1))\n", "\n", "total_time = time.time() - total_start\n", "print(f\"train diffusion completed, total time: {total_time}.\")\n", @@ -709,12 +653,12 @@ "plt.title(\"Learning Curves Diffusion Model\", fontsize=20)\n", "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + ")\n", "plt.yticks(fontsize=12)\n", "plt.xticks(fontsize=12)\n", "plt.xlabel(\"Epochs\", fontsize=16)\n", @@ -736,7 +680,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 161, "id": "8f7a9e99-a8a4-4c8f-a42f-17ef91b18585", "metadata": { "lines_to_next_cell": 2 @@ -746,12 +690,12 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|███████████████████████████████████████| 1000/1000 [00:10<00:00, 95.94it/s]\n" + "100%|███████████████████████████████████████| 1000/1000 [00:23<00:00, 42.86it/s]\n" ] }, { "data": { - "image/png": "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LS0oLi5GTk4OUlNTERMTg/j4eGg0GtUE2my92tjY9FlKyppfF6NXabb0ii06OpqIiEaPHk2enp5kNBpp3rx5REQUGRlJRER/+9vfqKqqiiorK+n333+ngIAA+qd/+ify8PCg6dOn0//+3/+biIimT59ORETV1dX8/gcOHCAiop9++on+8R//kXbt2kVERPX19fT48WMKDw8nnU5HH3/8MQ0cOJC0Wi398ssv/Pf/+q//SitXriQnJyd6++23adGiRfy7iIgI/v+5c+dSZ2cnNTc3082bN4mIyGg00vnz52nw4ME0evRoIiJavXo1/fzzz5SRkUH//M//TAaDgebMmUNz586lEydO0Lx582jw4MG0Y8cOIiJydnYmf39/8vb2pk8++YRSU1PJaDQSEZFer+fPt7e35/+fNGkSDRw4kIiIAgIC6Pfff6cnT56QTqejkydPEhHRe++9p5iLpUuXkr+/P926dYv++te/8s/d3d0l45mXl0elpaX04MEDIiIaOHAg7dmzh4iIVq1aRURE169fp61bt9J/+S//hXQ6HU2cOJFu375NXl5eZDAY6O7duzRq1Cj68ccfiYjIbDZTXFwcff7553Tu3DnauHGjYoyJiBITE/n/P/74Y0pJSaHbt2/zz7777juKj4+nZ8+e0fPnz+nx48dERPTbb7/xa9544w0aOHAg2draUklJCdnY2PDv/P39iYjoyZMndPnyZWptbaWrV68SEZHJZKLPPvuMHj9+TL29vRQSEkJEFl7y9/enfv36cd5l9/zuu+/oX/7lX6i0tJSIiFJSUqi2tpYiIiJo165ddOjQISIimjVrFhER5efnExGRo6Mj3bp1i4iI/v3f/50SEhL4GNfV1dGdO3dIr9dTeno6XblyhZYtW0ZExMeTiOh//s//SatWrSKNRkP/63/9L/4sIqLQ0FD+/zfffJNGjx5NU6ZMod27dxMRUXh4OF27do0GDBhAOTk5fNx+++03SklJoZEjR5KXlxf9y7/8C+3cuZN++uknGjNmDHV2dtKmTZvI1dWV/vKXv1BiYiKZTCY6d+4c6XQ6amlpISKiqqoq/vxvvvmG/7+1tZU6OzuJiCgvL4+uX79Ozs7OZDab6cMPPyS9Xk8rVqyQzGdBQQFt3LiR/P396cKFC/Tf//t/59+5urry/+/YsYMGDRpElZWV/Pd1dXX0wQcf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2Xc7Oztx4MABiVGhlp6ePqxb5sqVK/xvsRxRSkqKVCqGMef8cf78eQnwnjhxgi8ASjbBVTkRGheii+yXX37BP//5T9y6dQsDAwOwWCyqDZqbmxsAZ9WEW7duSX3Jx8dn2KLZohie7hFpeMRyNYw5N38nT57Ehg0beKDEpUuXVNovasqFXNnEsmYXLlzA6dOnATj1YX19fQgLC1OlowCcLDqx9q7GobJR5CdjTMW47Nq1SzVvvvnmm5ILt6WlRWJKR7K+0AK/adMmzhDm5eVh27ZtuHz5MhwOB2fzKW8htZaWFpSXl6OwsFDl4k9KSnKZ34oxWS8o5mWLj49XMUk1NTUoKSlBbm4uz+EkjgulrnE4997cuXMlAFhTU4P6+nqe8oYxdQJwHx8fPPvsswgNDcXChQsl92pgYKBmhLby/tJxxPeUXifSgXl6enJywsvLyyUAeprrUTy+yWTitQw9PDwwevRonlNL/A5tqJTpOBhTM3EjMbf/DADddNNNN91000033f7L5v5/fQK66aabbrrppptu/19NB1q66aabbrrppptuf5LpQEs33XTTTTfddNPtTzIdaOmmm2666aabbrr9SaYDLd1000033XTTTbc/yXSgpZtuuummm2666fYnmQ60dNNNN91000033f4k04GWbrrppptuuumm259kOtDSTTfddNNNN910+5PsfwAmd61phvc9EwAAAABJRU5ErkJggg==\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -790,9 +734,11 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 174, "id": "44cc6928-2525-4e61-8805-15b409097bbb", - "metadata": {}, + "metadata": { + "lines_to_next_cell": 2 + }, "outputs": [ { "data": { @@ -903,24 +849,25 @@ ")" ] }, - "execution_count": 48, + "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "device = torch.device(\"cuda\")\n", "classifier = DiffusionModelEncoder(\n", " spatial_dims=2,\n", " in_channels=1,\n", " out_channels=2,\n", " num_channels=(32, 64, 64),\n", " attention_levels=(False, True, True),\n", - " num_res_blocks=(1,1,1),\n", + " num_res_blocks=(1, 1, 1),\n", " num_head_channels=64,\n", " with_conditioning=False,\n", ")\n", - "classifier.to(device)\n", - "\n" + "\n", + "classifier.to(device)" ] }, { @@ -929,1660 +876,250 @@ "metadata": {}, "source": [ "## Model training of the classification model\n", - "We train our classification model for 100 epochs.\n" + "We train our classification model for 1000 epochs.\n" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 24, "id": "de18d5cb-68e7-407c-afe9-8efd7a5a904a", "metadata": { "lines_to_next_cell": 0 }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 534it [00:24, 22.16it/s, loss=0.671] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 0: : 17it [00:00, 65.41it/s, val_loss=0.288]\n", - "Epoch 1: : 534it [00:24, 21.99it/s, loss=0.612] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 1: : 17it [00:00, 66.80it/s, val_loss=0.363]\n", - "Epoch 2: : 534it [00:24, 21.92it/s, loss=0.586] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 2: : 17it [00:00, 68.07it/s, val_loss=0.226]\n", - "Epoch 3: : 534it [00:26, 20.48it/s, loss=0.581] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 3: : 17it [00:00, 63.17it/s, val_loss=0.217]\n", - "Epoch 4: : 534it [00:25, 20.99it/s, loss=0.579] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 4: : 17it [00:00, 63.70it/s, val_loss=0.211]\n", - "Epoch 5: : 534it [00:26, 20.46it/s, loss=0.572] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 5: : 17it [00:00, 63.46it/s, val_loss=0.234]\n", - "Epoch 6: : 534it [00:25, 20.66it/s, loss=0.577] \n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "final step train 533\n" + "Epoch 9 Validation loss 0.2536351333061854\n", + "Epoch 19 Validation loss 0.3019549027085304\n", + "Epoch 29 Validation loss 0.34552596261103946\n", + "Epoch 39 Validation loss 0.2783070926864942\n", + "Epoch 49 Validation loss 0.28460513055324554\n", + "Epoch 59 Validation loss 0.25296298414468765\n", + "Epoch 69 Validation loss 0.3343521902958552\n", + "Epoch 79 Validation loss 0.2634535978237788\n", + "Epoch 89 Validation loss 0.2862999041875203\n", + "Epoch 99 Validation loss 0.22700381030639014\n", + "Epoch 109 Validation loss 0.27035540093978244\n", + "Epoch 119 Validation loss 0.2451721504330635\n", + "Epoch 129 Validation loss 0.2890484283367793\n", + "Epoch 139 Validation loss 0.27566688507795334\n", + "Epoch 149 Validation loss 0.28788923223813373\n", + "Epoch 159 Validation loss 0.2524748469392459\n", + "Epoch 169 Validation loss 0.3107323000828425\n", + "Epoch 179 Validation loss 0.21660694728295007\n", + "Epoch 189 Validation loss 0.2702282816171646\n", + "Epoch 199 Validation loss 0.2677164326111476\n", + "Epoch 209 Validation loss 0.33349836121002835\n", + "Epoch 219 Validation loss 0.2969249188899994\n", + "Epoch 229 Validation loss 0.268981905033191\n", + "Epoch 239 Validation loss 0.29199230174223584\n", + "Epoch 249 Validation loss 0.2806356226404508\n", + "Epoch 259 Validation loss 0.301661084095637\n", + "Epoch 269 Validation loss 0.25811708470185596\n", + "Epoch 279 Validation loss 0.2599738910794258\n", + "Epoch 289 Validation loss 0.23392533014218012\n", + "Epoch 299 Validation loss 0.2580989971756935\n", + "Epoch 309 Validation loss 0.22807281464338303\n", + "Epoch 319 Validation loss 0.2510971352458\n", + "Epoch 329 Validation loss 0.25221700221300125\n", + "Epoch 339 Validation loss 0.25722870975732803\n", + "Epoch 349 Validation loss 0.2516109471519788\n", + "Epoch 359 Validation loss 0.22627043972412744\n", + "Epoch 369 Validation loss 0.28725822021563846\n", + "Epoch 379 Validation loss 0.2712069054444631\n", + "Epoch 389 Validation loss 0.29460274676481885\n", + "Epoch 399 Validation loss 0.2599460730950038\n", + "Epoch 409 Validation loss 0.22882529348134995\n", + "Epoch 419 Validation loss 0.24265126883983612\n", + "Epoch 429 Validation loss 0.23436561226844788\n", + "Epoch 439 Validation loss 0.25520699471235275\n", + "Epoch 449 Validation loss 0.22466829667488733\n", + "Epoch 459 Validation loss 0.26379595696926117\n", + "Epoch 469 Validation loss 0.23318989326556525\n", + "Epoch 479 Validation loss 0.264743114511172\n", + "Epoch 489 Validation loss 0.25179669509331387\n", + "Epoch 499 Validation loss 0.20064709583918253\n", + "Epoch 509 Validation loss 0.2527008851369222\n", + "Epoch 519 Validation loss 0.24675505111614862\n", + "Epoch 529 Validation loss 0.2267578070362409\n", + "Epoch 539 Validation loss 0.2342942381898562\n", + "Epoch 549 Validation loss 0.2587633654475212\n", + "Epoch 559 Validation loss 0.21963710337877274\n", + "Epoch 569 Validation loss 0.2676527574658394\n", + "Epoch 579 Validation loss 0.25124627848466236\n", + "Epoch 589 Validation loss 0.22307553887367249\n", + "Epoch 599 Validation loss 0.28288981815179187\n", + "Epoch 609 Validation loss 0.2745586136976878\n", + "Epoch 619 Validation loss 0.2356488679846128\n", + "Epoch 629 Validation loss 0.191768117249012\n", + "Epoch 639 Validation loss 0.23102722316980362\n", + "Epoch 649 Validation loss 0.2544248104095459\n", + "Epoch 659 Validation loss 0.23119398951530457\n", + "Epoch 669 Validation loss 0.20733060439427695\n", + "Epoch 679 Validation loss 0.22538802524407706\n", + "Epoch 689 Validation loss 0.216872605184714\n", + "Epoch 699 Validation loss 0.22977381944656372\n", + "Epoch 709 Validation loss 0.21891566862662634\n", + "Epoch 719 Validation loss 0.223398727675279\n", + "Epoch 729 Validation loss 0.24623310069243112\n", + "Epoch 739 Validation loss 0.23960118989149728\n", + "Epoch 749 Validation loss 0.21641289939483008\n", + "Epoch 759 Validation loss 0.21971949686606726\n", + "Epoch 769 Validation loss 0.22835112363100052\n", + "Epoch 779 Validation loss 0.2273434673746427\n", + "Epoch 789 Validation loss 0.18299358462293944\n", + "Epoch 799 Validation loss 0.1827801006535689\n", + "Epoch 809 Validation loss 0.21519174302617708\n", + "Epoch 819 Validation loss 0.1936649220685164\n", + "Epoch 829 Validation loss 0.23625890165567398\n", + "Epoch 839 Validation loss 0.2425163264075915\n", + "Epoch 849 Validation loss 0.16746311262249947\n", + "Epoch 859 Validation loss 0.20408761004606882\n", + "Epoch 869 Validation loss 0.2144848903020223\n", + "Epoch 879 Validation loss 0.23374033719301224\n", + "Epoch 889 Validation loss 0.23659739891688028\n", + "Epoch 899 Validation loss 0.24609535684188208\n", + "Epoch 909 Validation loss 0.2324757898847262\n", + "Epoch 919 Validation loss 0.24446949362754822\n", + "Epoch 929 Validation loss 0.19177630295356116\n", + "Epoch 939 Validation loss 0.2438896174232165\n", + "Epoch 949 Validation loss 0.2519366617004077\n", + "Epoch 959 Validation loss 0.20046784232060114\n", + "Epoch 969 Validation loss 0.21268909921248755\n", + "Epoch 979 Validation loss 0.2184151684244474\n", + "Epoch 989 Validation loss 0.21281357357899347\n", + "Epoch 999 Validation loss 0.21612912913163504\n", + "train completed, total time: 1351.5848128795624.\n" ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 6: : 17it [00:00, 63.53it/s, val_loss=0.306]\n", - "Epoch 7: : 534it [00:26, 20.39it/s, loss=0.57] \n" - ] - }, + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAHZCAYAAABn8CRaAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADJeUlEQVR4nOydd3jU9B/H37m7LrqgZZaNgGwQUUFQFBmCKIIKAiJL4YeKe6MMQUUUBXHhYCiylOFiKwiiLBEQ2btQdulurzfy+yNN7ptcksut3h39vJ6nT+8yv5fLJe98JsfzPA+CIAiCIIgyjCnUAyAIgiAIggg1JIgIgiAIgijzkCAiCIIgCKLMQ4KIIAiCIIgyDwkigiAIgiDKPCSICIIgCIIo85AgIgiCIAiizEOCiCAIgiCIMg8JIoIgCIIgyjwkiAiCCDlDhgwBx3GoU6dOqIdCEEQZhQQRQQSQDRs2gOM4cByH8ePHh3o4RJiQnp6Od999F127dkXdunWRkJCAuLg4VK9eHd26dcOkSZNw/PjxUA+TIMo0llAPgCAI4mrFarXi1Vdfxccffwyr1eo2PyMjAxkZGVizZg3Gjh2LBx54AO+99x5q1qwZgtESRNmGBBFBECFnzpw5mDNnTqiHEVAuX76Me+65B3/++ScAIDExEf3798cdd9yBGjVqICoqCufOncPmzZuxdOlSHD58GIsXL0a7du3w9NNPh3bwBFEGIUFEEAQRYJxOJx588EFJDPXo0QOzZ89G5cqV3Za9++678dZbb2HevHl44YUXSnuoBEGUQIKIIAgiwMyYMQPr1q0DAHTu3Bk//PADLBbty63JZMLDDz+MTp064dChQ6U1TIIgGCiomiDCkG3btuHRRx9Fw4YNkZCQgPj4eDRq1AiPP/44Dh8+rLvusWPHMHXqVNx9992oU6cO4uLiEBcXh9q1a6Nfv35YtWqV7vpz5syRAsNPnDgBq9WKadOmoW3btqhYsaIsYFy5rNPpxOeff46bb74ZFSpUQHx8PFq0aIE333wTBQUFmvv0lGWmDFTfvn07+vfvjxo1aiAmJgbVq1fHoEGDsH//ft3PBgD5+fl444030Lx5c8THxyM1NRUdOnTArFmzwPO8LDB+w4YNHrenxGaz4d133wUAxMbGYvbs2bpiiKVGjRro1KmTbJrRDDzld6GkTp064DgOQ4YMAQD8/fffGDJkCOrWrYuYmBhwHAcAuOaaa8BxHDp06OBxvOfOnYPFYgHHcXjuuedUl7Hb7fjqq6/Qo0cPpKWlISYmBhUrVsStt96KadOmoaioSHcff//9N4YPH46GDRsiPj4esbGxqFmzJq6//no8/vjj+PHHH8HzvMexEoRHeIIgAsb69et5ADwAfty4cV6vb7PZ+FGjRknbUPuLioriP//8c9X1jx07pruu+PfQQw/xNptNdRuzZ8+Wltu+fTvfqlUrt/XFz8Yuu3fvXr5Tp06a+7zxxhv5vLw81X0OHjyYB8DXrl1bdT673xkzZvAWi0V1H+XKleN///13zeN76tQpvn79+ppj7NmzJ79mzRrp/fr16zW3pcVPP/0kO87+4unYiLDfxfHjx93m165dmwfADx48mP/0009VjyHP8/xrr73GA+A5jlPdDssHH3wgrfv333+7zT9y5AjfpEkT3XOxQYMG/KFDh1S3//777/Mmk8nj+Zybm6s7ToIwArnMCCKMGD58OL7++msAQPfu3TFw4EA0bNgQHMdh165dmDZtGv777z+MGDECVatWxd133y1b3+FwIDo6Gt26dUOXLl3QpEkTpKSkIDMzE4cOHcLHH3+M//77D/PmzUO9evUwYcIEj+P5999/8fDDD6Nfv36oWrUqTp06hZiYGLdlR4wYgS1btmDw4MHo27evtOyUKVPw119/Ydu2bZg0aRLefvttn4/P6tWrsXXrVrRo0QJPPfUUmjdvjsLCQixbtgzTp09HQUEBBg0ahMOHDyM6Olq2bnFxMXr06IEjR45Ix3fEiBGoWbMmTp8+jc8//xw///wzLl686PP4AOD333+XXvfs2dOvbQWD7du3Y968eahZsyaef/55XH/99XA4HNi0aRMAYODAgZg0aRJ4nsf8+fPx6quvam7r22+/BQA0atQIrVu3ls07e/Ys2rdvj/PnzyMxMREjRoxA586dUaVKFWRnZ2PNmjWYPn06Dh8+jDvvvBM7d+5EcnKytP6ePXvw/PPPw+l0om7dunjiiSfQqlUrpKSkIC8vD4cPH8b69euxbNmyIBwlokwSakVGEFcT/liIvv/+e2ndL774QnWZwsJCyQpTp04dNytPXl4en5GRobkPp9PJDxkyhAfAx8fH81lZWW7LsJYGAPxXX32luT3lst98843bMkVFRXyzZs14AHxqaqqq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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "n_epochs = 1000\n", + "val_interval = 10\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", + "\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " classifier.train()\n", + " epoch_loss = 0\n", + "\n", + " for step, data in enumerate(train_loader):\n", + " images = data['image'].to(device)\n", + " classes = data['slice_label'].to(device)\n", + " #classes[classes==2]=0\n", + "\n", + " optimizer_cls.zero_grad(set_to_none=True)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + "\n", + " with autocast(enabled=False):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image\n", + " pred = classifier(noisy_img, timesteps)\n", + "\n", + " loss = F.cross_entropy(pred, classes.long())\n", + "\n", + " loss.backward()\n", + " optimizer_cls.step()\n", + "\n", + " epoch_loss += loss.item()\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " classifier.eval()\n", + " val_epoch_loss = 0\n", + "\n", + " for step, data_val in enumerate(val_loader):\n", + " images = data_val['image'].to(device)\n", + " classes = data_val['slice_label'].to(device)\n", + " timesteps = torch.randint(0, 1, (len(images),)).to(\n", + " device\n", + " ) # check validation accuracy on the original images, i.e., do not add noise\n", + "\n", + " with torch.no_grad():\n", + " with autocast(enabled=False):\n", + " noise = torch.randn_like(images).to(device)\n", + " pred = classifier(images, timesteps)\n", + " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " _, predicted = torch.max(pred, 1)\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + " print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train completed, total time: {total_time}.\")\n", + "\n", + "## Learning curves for the Classifier\n", + "\n", + "plt.style.use(\"seaborn-bright\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + ")\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "# Image-to-image translation to a healthy subject\n", + "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", + "metadata": {}, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "final step train 533\n" + "minmax tensor(0.) tensor(1.3396)\n" ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 7: : 17it [00:00, 62.97it/s, val_loss=0.372]\n", - "Epoch 8: : 534it [00:25, 20.72it/s, loss=0.572] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 8: : 17it [00:00, 63.76it/s, val_loss=0.208]\n", - "Epoch 9: : 534it [00:26, 20.18it/s, loss=0.565] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 9: : 17it [00:00, 61.70it/s, val_loss=0.245]\n", - "Epoch 10: : 534it [00:26, 20.22it/s, loss=0.563] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 10: : 17it [00:00, 63.48it/s, val_loss=0.181]\n", - "Epoch 11: : 534it [00:26, 20.42it/s, loss=0.564] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 11: : 17it [00:00, 64.20it/s, val_loss=0.196]\n", - "Epoch 12: : 534it [00:26, 20.35it/s, loss=0.562] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 12: : 17it [00:00, 64.27it/s, val_loss=0.235]\n", - "Epoch 13: : 534it [00:26, 20.31it/s, loss=0.562] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 13: : 17it [00:00, 62.05it/s, val_loss=0.2] \n", - "Epoch 14: : 534it [00:26, 20.35it/s, loss=0.557] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 14: : 17it [00:00, 63.59it/s, val_loss=0.232]\n", - "Epoch 15: : 534it [00:26, 20.25it/s, loss=0.558] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 15: : 17it [00:00, 62.56it/s, val_loss=0.236]\n", - "Epoch 16: : 534it [00:26, 20.39it/s, loss=0.559] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 16: : 17it [00:00, 62.08it/s, val_loss=0.227]\n", - "Epoch 17: : 534it [00:26, 20.44it/s, loss=0.561] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 17: : 17it [00:00, 61.93it/s, val_loss=0.232]\n", - "Epoch 18: : 534it [00:26, 20.10it/s, loss=0.556] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 18: : 17it [00:00, 61.19it/s, val_loss=0.265]\n", - "Epoch 19: : 534it [00:26, 20.52it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 19: : 17it [00:00, 61.85it/s, val_loss=0.214]\n", - "Epoch 20: : 534it [00:26, 20.13it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 20: : 17it [00:00, 62.12it/s, val_loss=0.304]\n", - "Epoch 21: : 534it [00:26, 20.33it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 21: : 17it [00:00, 60.91it/s, val_loss=0.235]\n", - "Epoch 22: : 534it [00:26, 20.19it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 22: : 17it [00:00, 62.88it/s, val_loss=0.232]\n", - "Epoch 23: : 534it [00:26, 20.24it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 23: : 17it [00:00, 62.73it/s, val_loss=0.146]\n", - "Epoch 24: : 534it [00:26, 20.32it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 24: : 17it [00:00, 62.44it/s, val_loss=0.223]\n", - "Epoch 25: : 534it [00:26, 20.20it/s, loss=0.553] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 25: : 17it [00:00, 62.95it/s, val_loss=0.286]\n", - "Epoch 26: : 534it [00:26, 20.24it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 26: : 17it [00:00, 63.56it/s, val_loss=0.316]\n", - "Epoch 27: : 534it [00:26, 20.20it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 27: : 17it [00:00, 61.08it/s, val_loss=0.217]\n", - "Epoch 28: : 534it [00:26, 20.18it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 28: : 17it [00:00, 63.45it/s, val_loss=0.155]\n", - "Epoch 29: : 534it [00:26, 20.30it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 29: : 17it [00:00, 62.70it/s, val_loss=0.227]\n", - "Epoch 30: : 534it [00:25, 20.61it/s, loss=0.55] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 30: : 17it [00:00, 66.44it/s, val_loss=0.2] \n", - "Epoch 31: : 534it [00:26, 20.32it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 31: : 17it [00:00, 61.60it/s, val_loss=0.258]\n", - "Epoch 32: : 534it [00:26, 20.40it/s, loss=0.549] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 32: : 17it [00:00, 63.44it/s, val_loss=0.17] \n", - "Epoch 33: : 534it [00:26, 20.37it/s, loss=0.546] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 33: : 17it [00:00, 62.44it/s, val_loss=0.197]\n", - "Epoch 34: : 534it [00:26, 20.23it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 34: : 17it [00:00, 64.16it/s, val_loss=0.227]\n", - "Epoch 35: : 534it [00:26, 20.28it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 35: : 17it [00:00, 61.64it/s, val_loss=0.182]\n", - "Epoch 36: : 534it [00:26, 20.24it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 36: : 17it [00:00, 62.97it/s, val_loss=0.189]\n", - "Epoch 37: : 534it [00:26, 20.37it/s, loss=0.548] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 37: : 17it [00:00, 63.50it/s, val_loss=0.232]\n", - "Epoch 38: : 534it [00:26, 20.30it/s, loss=0.554] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 38: : 17it [00:00, 62.30it/s, val_loss=0.175]\n", - "Epoch 39: : 534it [00:26, 20.25it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 39: : 17it [00:00, 62.73it/s, val_loss=0.219]\n", - "Epoch 40: : 534it [00:26, 20.17it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 40: : 17it [00:00, 62.13it/s, val_loss=0.169]\n", - "Epoch 41: : 534it [00:26, 20.06it/s, loss=0.547] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 41: : 17it [00:00, 61.03it/s, val_loss=0.153]\n", - "Epoch 42: : 534it [00:26, 20.06it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 42: : 17it [00:00, 62.17it/s, val_loss=0.18] \n", - "Epoch 43: : 534it [00:26, 20.04it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 43: : 17it [00:00, 61.85it/s, val_loss=0.168]\n", - "Epoch 44: : 534it [00:26, 19.98it/s, loss=0.542] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 44: : 17it [00:00, 61.28it/s, val_loss=0.181]\n", - "Epoch 45: : 534it [00:26, 20.16it/s, loss=0.542] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 45: : 17it [00:00, 63.26it/s, val_loss=0.154]\n", - "Epoch 46: : 534it [00:26, 20.08it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 46: : 17it [00:00, 61.43it/s, val_loss=0.151]\n", - "Epoch 47: : 534it [00:26, 20.06it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 47: : 17it [00:00, 62.66it/s, val_loss=0.174]\n", - "Epoch 48: : 534it [00:26, 20.27it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 48: : 17it [00:00, 62.88it/s, val_loss=0.148]\n", - "Epoch 49: : 534it [00:26, 20.32it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 49: : 17it [00:00, 62.37it/s, val_loss=0.178]\n", - "Epoch 50: : 534it [00:26, 20.24it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 50: : 17it [00:00, 62.16it/s, val_loss=0.203]\n", - "Epoch 51: : 534it [00:26, 20.33it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 51: : 17it [00:00, 63.13it/s, val_loss=0.178]\n", - "Epoch 52: : 534it [00:26, 20.37it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 52: : 17it [00:00, 63.45it/s, val_loss=0.191]\n", - "Epoch 53: : 534it [00:26, 20.32it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 53: : 17it [00:00, 62.24it/s, val_loss=0.182]\n", - "Epoch 54: : 534it [00:26, 20.10it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 54: : 17it [00:00, 63.44it/s, val_loss=0.184]\n", - "Epoch 55: : 534it [00:26, 19.94it/s, loss=0.544] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 55: : 17it [00:00, 62.61it/s, val_loss=0.165]\n", - "Epoch 56: : 534it [00:26, 20.19it/s, loss=0.545] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 56: : 17it [00:00, 61.80it/s, val_loss=0.175]\n", - "Epoch 57: : 534it [00:26, 20.07it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 57: : 17it [00:00, 62.74it/s, val_loss=0.164]\n", - "Epoch 58: : 534it [00:26, 20.27it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 58: : 17it [00:00, 62.64it/s, val_loss=0.159]\n", - "Epoch 59: : 534it [00:26, 20.23it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 59: : 17it [00:00, 63.27it/s, val_loss=0.166]\n", - "Epoch 60: : 534it [00:26, 20.21it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 60: : 17it [00:00, 62.98it/s, val_loss=0.146]\n", - "Epoch 61: : 534it [00:26, 20.03it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 61: : 17it [00:00, 61.23it/s, val_loss=0.153]\n", - "Epoch 62: : 534it [00:26, 20.15it/s, loss=0.54] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 62: : 17it [00:00, 62.14it/s, val_loss=0.18] \n", - "Epoch 63: : 534it [00:26, 20.22it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 63: : 17it [00:00, 61.99it/s, val_loss=0.152]\n", - "Epoch 64: : 534it [00:26, 20.04it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 64: : 17it [00:00, 61.32it/s, val_loss=0.14] \n", - "Epoch 65: : 534it [00:26, 20.25it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 65: : 17it [00:00, 63.32it/s, val_loss=0.145]\n", - "Epoch 66: : 534it [00:26, 20.14it/s, loss=0.539] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 66: : 17it [00:00, 61.50it/s, val_loss=0.154]\n", - "Epoch 67: : 534it [00:26, 20.09it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 67: : 17it [00:00, 59.68it/s, val_loss=0.148]\n", - "Epoch 68: : 534it [00:26, 20.25it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 68: : 17it [00:00, 63.40it/s, val_loss=0.172]\n", - "Epoch 69: : 534it [00:26, 20.34it/s, loss=0.543] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 69: : 17it [00:00, 61.41it/s, val_loss=0.211]\n", - "Epoch 70: : 534it [00:26, 20.22it/s, loss=0.538] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 70: : 17it [00:00, 60.88it/s, val_loss=0.158]\n", - "Epoch 71: : 534it [00:26, 20.51it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 71: : 17it [00:00, 62.84it/s, val_loss=0.129]\n", - "Epoch 72: : 534it [00:26, 20.30it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 72: : 17it [00:00, 63.48it/s, val_loss=0.197]\n", - "Epoch 73: : 534it [00:26, 20.27it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 73: : 17it [00:00, 62.99it/s, val_loss=0.158]\n", - "Epoch 74: : 534it [00:26, 20.17it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 74: : 17it [00:00, 62.28it/s, val_loss=0.147]\n", - "Epoch 75: : 534it [00:26, 20.25it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 75: : 17it [00:00, 63.89it/s, val_loss=0.131]\n", - "Epoch 76: : 534it [00:26, 20.34it/s, loss=0.536] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 76: : 17it [00:00, 61.53it/s, val_loss=0.155]\n", - "Epoch 77: : 534it [00:26, 20.15it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 77: : 17it [00:00, 61.50it/s, val_loss=0.158]\n", - "Epoch 78: : 534it [00:26, 20.20it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 78: : 17it [00:00, 62.59it/s, val_loss=0.153]\n", - "Epoch 79: : 534it [00:26, 20.19it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 79: : 17it [00:00, 61.60it/s, val_loss=0.162]\n", - "Epoch 80: : 534it [00:26, 20.31it/s, loss=0.537] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 80: : 17it [00:00, 63.66it/s, val_loss=0.181]\n", - "Epoch 81: : 534it [00:26, 20.48it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 81: : 17it [00:00, 63.58it/s, val_loss=0.216]\n", - "Epoch 82: : 534it [00:26, 20.11it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 82: : 17it [00:00, 60.27it/s, val_loss=0.139]\n", - "Epoch 83: : 534it [00:26, 20.29it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 83: : 17it [00:00, 62.75it/s, val_loss=0.202]\n", - "Epoch 84: : 534it [00:26, 20.10it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 84: : 17it [00:00, 60.65it/s, val_loss=0.148]\n", - "Epoch 85: : 534it [00:26, 20.23it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 85: : 17it [00:00, 63.67it/s, val_loss=0.153]\n", - "Epoch 86: : 534it [00:26, 20.20it/s, loss=0.532] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 86: : 17it [00:00, 63.29it/s, val_loss=0.153]\n", - "Epoch 87: : 534it [00:26, 20.26it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 87: : 17it [00:00, 63.14it/s, val_loss=0.148]\n", - "Epoch 88: : 534it [00:26, 20.04it/s, loss=0.535] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 88: : 17it [00:00, 63.95it/s, val_loss=0.194]\n", - "Epoch 89: : 534it [00:26, 20.19it/s, loss=0.527] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 89: : 17it [00:00, 62.93it/s, val_loss=0.175]\n", - "Epoch 90: : 534it [00:26, 20.35it/s, loss=0.528] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 90: : 17it [00:00, 63.62it/s, val_loss=0.173]\n", - "Epoch 91: : 534it [00:26, 20.25it/s, loss=0.522] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 91: : 17it [00:00, 63.33it/s, val_loss=0.167]\n", - "Epoch 92: : 534it [00:26, 20.20it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 92: : 17it [00:00, 61.01it/s, val_loss=0.183]\n", - "Epoch 93: : 534it [00:26, 20.18it/s, loss=0.531] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 93: : 17it [00:00, 61.76it/s, val_loss=0.179]\n", - "Epoch 94: : 534it [00:26, 20.31it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 94: : 17it [00:00, 63.02it/s, val_loss=0.152]\n", - "Epoch 95: : 534it [00:26, 20.17it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 95: : 17it [00:00, 63.23it/s, val_loss=0.148]\n", - "Epoch 96: : 534it [00:26, 20.11it/s, loss=0.533] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 96: : 17it [00:00, 63.35it/s, val_loss=0.154]\n", - "Epoch 97: : 534it [00:26, 20.35it/s, loss=0.534] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 97: : 17it [00:00, 62.97it/s, val_loss=0.17] \n", - "Epoch 98: : 534it [00:26, 20.25it/s, loss=0.53] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 98: : 17it [00:00, 62.36it/s, val_loss=0.138]\n", - "Epoch 99: : 534it [00:26, 20.22it/s, loss=0.529] \n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "final step train 533\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch 99: : 17it [00:00, 63.30it/s, val_loss=0.193]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "train completed, total time: 2708.850436449051.\n" - ] - }, - { - "data": { - "image/png": "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QQ0bNkSLFi3w77//4uLFi1i5ciWUSiVGjBgBgB9+Sk5ORqtWrawyJkePHkVmZmahz/3YY49Bp9Ph6NGjVrOwzGYz/v33X6vXXLt2DYGBgVbBUEZGBo4fP261v5BcKEiGpmnTpgBgc8mQvXv3AgCaNGni8PFcye0DorNnz6Ju3bpiKk3QqFEj8Xl79u3bh7p162LdunV47LHHoFQqER4ejvfee89th8wAoG5kbtfqvaeA9ML/GyKEEOJEQuPFF198EdevX0fv3r3FGpyQkBB4eHjg+PHjyMjIEF+TmJhY5A/mGo0Gzz33HB48eICFCxfKnvvuu+9s1g9FRkYiMTER586dE7eZTCa8/fbbePjQuuYrMJAfWrxz547D1yUEg7Nnz0ZKSoq4PSUlBbNnz5bt4+7cfsgsPj4e1atXt9ou/MXFx8fbfe3du3fx8OFDvPHGG5gzZw7q1auHv//+GwsWLEBMTAx++uknu6/V6/WysVXpX3Rx4zigV0tg6Z98cfWek0Cf1iV2ekIIIXZERUXhrbfewoEDBwDIO1MrFAq8+uqrWLhwIRo3box+/fohJSUFW7ZsQWRkJMLCwop07gULFuDvv//GtGnT8M8//6Bp06a4cOECNm/ejO7du2P79u2y/V9//XVs374dbdu2xXPPPQedToc9e/bg7t276Nixo1VWp3Xr1vDw8MDnn3+OlJQUMcv13nvv2b2m9u3b4/XXX8fixYvRoEEDPPPMM2CMYf369YiJicEbb7wh1le5O7fPEAGQdcosyHNmsxmpqan46quvMH78eHTq1Alz587F66+/jtWrV+Pq1at2Xzt//nz4+fmJX/batReXni1zH9OwGSGEuAdfX188++yzAPii4T59+sienz9/PubNmweO4/DVV19hx44dGDJkCLZv316k6e0Av3TGv//+i8GDB+PgwYNYtGgR4uPjsWPHDrRubf2puW/fvvjtt99QvXp1rFq1CqtXr0adOnVw+PBhREZaF6cGBgbit99+Q61atfD1119jypQpmDJlSr7X9cUXX2D58uUIDQ3FsmXL8O233yI0NBTLly/HokWLivQzlySOMRttN91I69atYTKZcPjwYdn2c+fOoUGDBli6dKnd6fOVKlXC/fv3kZCQgICAAHH79u3b0aNHD/zyyy947rnnbL7WVoYoIiICycnJJVIglpoBBD0FGIxA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CCJMoYJECywLLe8SuvFCLzIwjKjwonNJSQQWYRYRqEEQWFamSSXigVTMXE9lh/e7iB3abzXtb2kyzw8WlnfmnXlnLpaH3ZnZ0NBQWbBggfj7+0thYaGMj4//sZk1Ij89C56IiIhIhXgNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxER0QxpNBr+4znRPMdARESzKjAwUAkQv3r9//8WERH9Tc5zPQARqVNISAiWLVs26XIvL69ZnIaI1I6BiIjmxJEjR7B79+65HoOICAB/MiMiIiJiICKif9/PFy1XVFRg1apVMBgM8PT0RGpqKl6+fDlp78jICIqLixEZGQm9Xg93d3esXr0ap06dwtevXyft+/DhAwoLCxETEwN3d3cYDAaYzWbs378fT58+nbSvuroa8fHxMBqN8PDwwObNmyddv729HTk5OQgKCoKLiwuMRiOCgoKwbds2XLt2bZpnh4j+CCEimkUBAQECQM6fPz/tHgACQEpKSgSAeHt7y8qVK8VoNAoAWbhwodTV1Tn09fb2isViEQCi1WolMjJSzGazsr3ExEQZGxtz6Hv27Jn4+voqfeHh4RIdHS3u7u4CQLKysiac78yZM6LRaMTHx0diY2NFr9cLADEYDNLc3GzX09bWJkuWLBEA4ubmJhaLRaKjo8XT01MASFRU1LTPDxHNHAMREc2qmQQinU4npaWl8u3bNxERGRkZkV27dgkACQgIkNHRUbu+9PR0ASARERHy5s0bpf7kyRPx8vISAHL48GG7nsHBQfH39xcAkpKSIh0dHXbLa2tr5cqVKxPO5+bmZndcVqtVEhISBIDs2LHDricvL08JV0NDQ3bLmpubpby8fNrnh4hmjoGIiGaVLRBN9RoYGFB6bLWtW7c6bO/z58/i7e0tAOTcuXNKvbW1VTQajQCQhoYGh77KykoBIHq9XqxWq1I/duyYABCz2SyfPn2a1jHZ5jt48KDDsufPnwsA8fDwsKsnJycLAGlsbJzWPojo7+JdZkQ0J6a67d7Z2fHjKTc316G2YMECZGdno7i4GDU1NdizZw8A4P79+xARrF27FjExMQ596enpMJlM6OzsxKNHj5CSkgIAqKqqAgDk5+fDxcXlt44pOzvboWaxWODq6orBwUH09/dj8eLFAAA/Pz8AwI0bN2CxWPhgR6I5xkBERHPiv9x2bzabf1lvbW1Varb34eHhE/ZotVqEhYWhs7MTra2tSiBqbm4GAMTFxf3WbAAQHBw8YX3p0qXo6OjA8PCwEohyc3Nx8eJFFBUV4dKlS0hJScG6deuwceNG+Pr6/va+iWhmeJcZEc0bk32jZHuI49DQkFIbHh7+Zc9kfVarFQCwaNGi355Pr9dPWNdqf3zUiohSi46ORm1tLZKSktDV1YXy8nJkZGTAZDIhOTlZCWZENDsYiIho3ujr65uw3tvbCwAwGo1KzWAw2C2bSE9Pj0Of7f3Hjx9nNOt0xMXFoaamBgMDA7h79y4KCgpgMplw7949JCYmzsoMRPQDAxERzRuTfWtiq4eGhio12/tXr15N2PP9+3e0tLQ49EVERAAAHj9+PPOBp8lgMCA5ORlHjx5FS0sLgoOD0dXVherq6lmbgUjtGIiIaN44ffq0Q218fBxnz54FACQlJSn1pKQkaDQa1NfXT/hgxJs3b6KzsxN6vR5r1qxR6qmpqQCAkydPYnx8/A8fwdTc3NxgsVgAAN3d3bO+fyK1YiAionnjzp07OHHihHItztjYGPbu3Yvu7m74+flh586dyrrLly9HWloaACAzMxPv3r1TljU0NODQoUMAgLy8PLufzPbt24eAgAA0NTUhLS0NXV1ddjPU19fj6tWrMz6WAwcO4Pr16xgdHbWr19bW4sGDBwCA2NjYGe+HiKZHIz9f5UdE9JcFBgaivb19ytvut2/froQW2y3pJSUlKCgogLe3N/z8/PD69WtYrVa4urqipqYG8fHxdtvo6+tDQkICXrx4AScnJ6xYsQJfvnxRfkbbtGkTbt++DVdXV7u+xsZGpKSk4P3799BqtTCbzdDpdGhra8Pg4CCysrJw4cIFZX3bfJN9nNqOua2tDYGBgQB+XFTd2NgIZ2dnhISEwGg0oqenB+3t7QCAjIwMXL58eZpnlYhmioGIiGaVLRxMJT8/H2VlZQDsA0dFRQXKysrQ1NQEnU6H9evXo6ioCJGRkRNuZ2RkBMePH0dlZSXevn0LrVaL8PBwZGZmIicnBzqdbsK+/v5+lJaW4tatW2hra4OTkxNMJhM2bNiAnJwcREVFKev+l0D08OFDVFVVoa6uDh0dHRgcHISPjw/CwsKQm5uLLVu28NlERLOIgYiI/nlTBQ4iopniNURERESkegxEREREpHoMRERERKR6DERERESkevxzVyL65/FiaiL62/gNEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqR4DEREREakeAxERERGpHgMRERERqd7/AL3d+lWfpCm1AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "batch_size = 32\n", - "n_epochs = 100\n", - "val_interval = 1\n", - "epoch_loss_list = []\n", - "val_epoch_loss_list = []\n", - "optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5)\n", - "\n", - "classifier.to(device)\n", - "weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset\n", - "\n", - "\n", - "scaler = GradScaler()\n", - "total_start = time.time()\n", - "for epoch in range(n_epochs):\n", - " classifier.train()\n", - " epoch_loss = 0\n", - " indexes = list(torch.randperm(total_train_slices.shape[0]))\n", - " data_train = total_train_slices[indexes] # shuffle the training data\n", - " labels_train = total_train_labels[indexes]\n", - " subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size))\n", - " progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size)\n", - " progress_bar.set_description(f\"Epoch {epoch}\")\n", - "\n", - " for step, (a, b) in progress_bar:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - "\n", - " optimizer_cls.zero_grad(set_to_none=True)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - "\n", - " with autocast(enabled=False):\n", - " # Generate random noise\n", - " noise = torch.randn_like(images).to(device)\n", - "\n", - " # Get model prediction\n", - " noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image\n", - " pred = classifier(noisy_img, timesteps)\n", - " loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction=\"mean\")\n", - "\n", - " loss.backward()\n", - " optimizer_cls.step()\n", - "\n", - " epoch_loss += loss.item()\n", - " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", - " epoch_loss_list.append(epoch_loss / (step + 1))\n", - " print(\"final step train\", step)\n", - "\n", - " if (epoch + 1) % val_interval == 0:\n", - " classifier.eval()\n", - " val_epoch_loss = 0\n", - " subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) #\n", - " progress_bar_val = tqdm(enumerate(subset_2D_val))\n", - " progress_bar_val.set_description(f\"Epoch {epoch}\")\n", - " for step, (a, b) in progress_bar_val:\n", - " images = a.to(device)\n", - " classes = b.to(device)\n", - " timesteps = torch.randint(0, 1, (len(images),)).to(\n", - " device\n", - " ) # check validation accuracy on the original images, i.e., do not add noise\n", - "\n", - " with torch.no_grad():\n", - " with autocast(enabled=False):\n", - " noise = torch.randn_like(images).to(device)\n", - " pred = classifier(images, timesteps)\n", - " val_loss = F.cross_entropy(pred, classes.long(), reduction=\"mean\")\n", - "\n", - " val_epoch_loss += val_loss.item()\n", - " _, predicted = torch.max(pred, 1)\n", - " progress_bar_val.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", - " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - "\n", - "total_time = time.time() - total_start\n", - "print(f\"train completed, total time: {total_time}.\")\n", - "\n", - " ## Learning curves for the Classifier\n", - "\n", - "plt.style.use(\"seaborn-bright\")\n", - "plt.title(\"Learning Curves\", fontsize=20)\n", - "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", - "plt.plot(\n", - " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", - " val_epoch_loss_list,\n", - " color=\"C1\",\n", - " linewidth=2.0,\n", - " label=\"Validation\",\n", - " )\n", - "plt.yticks(fontsize=12)\n", - "plt.xticks(fontsize=12)\n", - "plt.xlabel(\"Epochs\", fontsize=16)\n", - "plt.ylabel(\"Loss\", fontsize=16)\n", - "plt.legend(prop={\"size\": 14})\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "a676b3fe", - "metadata": {}, - "source": [ - "# Image-to-image translation to a healthy subject\n", - "We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "fe0d9eac-1477-4d6d-a885-d3c4acb4a781", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" }, { "data": { @@ -2693,18 +1230,20 @@ ")" ] }, - "execution_count": 43, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "\n", - "inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed\n", - "inputlabel = total_val_labels[120] # Check whether it is healthy or diseased\n", + "idx_unhealthy = np.argwhere(data_val[\"slice_label\"].numpy() == 0).squeeze()\n", + "idx = idx_unhealthy[4] # Pick a random slice of the validation set to be transformed\n", + "inputimg = data_val[\"image\"][idx] # Pick an input slice of the validation set to be transformed\n", + "inputlabel = data_val[\"slice_label\"][idx] # Check whether it is healthy or diseased\n", + "print('minmax', inputimg.min(), inputimg.max())\n", "\n", "plt.figure(\"input\" + str(inputlabel))\n", - "plt.imshow(inputimg, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.imshow(inputimg[0,...], vmin=0, vmax=1, cmap=\"gray\")\n", "plt.axis(\"off\")\n", "plt.tight_layout()\n", "plt.show()\n", @@ -2726,7 +1265,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 176, "id": "f71e4924", "metadata": { "collapsed": false, @@ -2740,12 +1279,12 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:04<00:00, 49.96it/s]\n" + "100%|█████████████████████████████████████████| 200/200 [00:05<00:00, 33.36it/s]\n" ] }, { "data": { - "image/png": "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ABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXXz2Isin76NGj3ri12b1GjRox/509e/bI3PXXXy9zJUqU8MYrV64sa4YPH57zC8uBjz76yBu3RuStnHosrK396nlSG9+dizb2v2LFClljadeuXUx/xzk9nm5tis+XL5/MqeMZ1uNg/YJH9+7dvfH7779f1mzevNkbX7lypazZuXOnzCnvvvuuzJUtW1bmOnXq5I3feuutskYdiVHj+87pXx5xTv8CivU8tWzZUubUMSTrV0QaNGjgjT///POyRv1Kh3POLVq0SOaUS+F4QUJCgsylpKR449bRg5zgmx0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAgheXaczExMSYa4oVK+aNW0tUv/32W5lTk1HWIlwrpyxcuFDmnn32WW9cLal2zrlKlSrJnFpQa02PvfzyyzLXt29fb3z16tWypkyZMt74Qw89JGusSci6det649YEW/PmzWVuzpw53nivXr1kzQcffOCNR1nO7Jxzp06d8sat12vJkiVlrmPHjt74kCFDZI2adlS35Zz9PGVnZ3vj1sTlgQMHZK5nz57eeLdu3WSNmkrduHGjrBk3bpzMTZgwwRtXU5rO2YvV27Rp442/+eabsgb/zXrtWcvxzwff7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQPJodACB4cTl6oEavLWp5tLVwVy0ltnL58+eXNWq82vL+++/LnBontxZEv/TSSzJnLYdVWrRoIXNqtL5QoUIx/52srCyZK1eunMypRcK33367rPnyyy9lTh09GDNmjKxRx1Rq1aolaz7++GOZq1ixoswpr7/+usz9/ve/j/n21qxZ441bI97qWIlzzu3fv98b79evn6zJnVv/31ldR9u2bWWNYh1XyJs3r8x16dIl5r81efJkmUtKSor59vDL8uSJS1v6X/hmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMHLdc6aTf75PzQ2wjdq1MgbX7p0qaxR4+7WSHvRokVlLj09XeaUtLQ0mcvIyIj59h599FFv3NqqHm/W3/rNb37jjaempsqa66677ryv6efULy+88sorcf07FvU43H///bJm4MCBMte5c2dvfMeOHbLG+luLFy/2xidOnChr1HOYkpIia5YsWSJz6vhIkyZNZI31fmrXrp03bh2VUc+T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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbsAAAG7CAYAAABaaTseAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAsNElEQVR4nO3daXSV5fX38UtCEjJAwiiDzKMoiogFqVJpbdElaLXLAZYWLBbEKmBFi2NRcVhW6oAWB2iloii04kAFnFAZlgwikzJJCVFkDiRkJCQ8L562z3/9n+u34dzcCfHq9/Nyb/Y59xlyNmetfe1z0pEjR444AAACVutEXwAAAFWNZgcACB7NDgAQPJodACB4NDsAQPBodgCA4NHsAADBo9kBAIJX+1j/4aZNm2SuU6dO3ni3bt1kzdq1a4/1rv9j4cKFMjd79mxvfNmyZbKmefPmMpedne2Nd+3aVdacdNJJ3vi3334ra/bt2ydzJSUl3ri1B6CgoEDmUlJSvPHCwkJZU6uW//9DSUlJssbKHT582BtPTk6WNZmZmQnfl7pu55yrXdv/treuwXpMinUNlvT0dG9cPXfO6fdEWVmZrKlTp47MVVRUeOPl5eWyprKyUubUe896TOr21G05p19b5/TrYV13RkaGzKnrSE1NlTXqdapXr17CNc45V1RU5I1bz0Nubq7MFRcXe+O//vWvZU3Pnj1lLoqpU6d648OGDTuu2+WbHQAgeDQ7AEDwaHYAgODR7AAAwaPZAQCCR7MDAATvpGP9PTs1Vu+cc/369fPGFyxYIGvU2HNpaemxXA4AoAZQvaE6fyr1WO6Lb3YAgODR7AAAwaPZAQCCR7MDAASPZgcACN4xL4K2dO/e3Rtfs2aNrFELkK0lr2lpaTKXn58vcwCA6KzPZfX5v27dOlmjFk5XJb7ZAQCCR7MDAASPZgcACB7NDgAQPJodACB4NDsAQPBiOXrQtGlTb7xly5ayRo2etm/fXtZ069ZN5tQxh+TkZFmTmZkpc7Vq+f8fYI3gqiXWJSUlssZaYKqeo4MHD8qasrIymVPPRXl5uaxRucrKSlmjnruorOdc3VdSUlLCNZbatfWfinoNreXpltTUVG+8oqIi4duy3l9Rr0+xnleVO3z4cKz3Yz2mKAuLozx/asm9JSMjQ+asx1tYWOiNHzhwQNbk5OQc62X9R+/evWWudevW3vimTZtkDUcPAACoAjQ7AEDwaHYAgODR7AAAwaPZAQCCF8s0ppoWsqYG1ZTfxo0bZU3z5s1lbsuWLd64NU3VsGFDmVPTfGpSzjk9WWZNHlmTkEVFRd64NZUXZWIP/1fUCc4oz7l1e+o6rPdy3NcQZXLRev5UzprqVTlr4tKamlV1hw4dSvganNPPX3p6uqxRz5/1uWItwFfT13l5ebImCmvCVD1e6/P/ROCbHQAgeDQ7AEDwaHYAgODR7AAAwaPZAQCCR7MDAAQvlqMHBQUF3niUUWRrrNhaYKqWMFv2798vc2ppsnU/ahQ5yrWh+lXnkQ5rpD3KcuQo4n5M1jGa/ybWMvYorOMU1fVesY5GqMdrHe3avXu3zGVlZXnj+fn5suZY8M0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAghfL0YNdu3Z549Y2cTUya41Dx71Fu6SkJFIOAKpLdR0vsBQWFspcZmamN259/luq6vHyzQ4AEDyaHQAgeDQ7AEDwaHYAgODR7AAAwYtlGtNaEqocOXIk4RprIWoUderUkTmWNwOoCazPPTW9HuXz1VJcXCxzanL9wIEDke6rqKgoUt3R8M0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAghfLLH9SUpI3bo2rVlZWJnw/5eXlCddYOF4AoKar6Yug416ar45aHO/zwDc7AEDwaHYAgODR7AAAwaPZAQCCR7MDAASPZgcACF4sRw/U6GlZWVkcN/8feXl5sd4eAODorF8iyM/Pj/W+ohxLOxZ8swMABI9mBwAIHs0OABA8mh0AIHg0OwBA8GKZxjxy5Ig3npaWJmuiTPBkZmbKHJOaAFA11LJ/5/Ti5qhq1fJ/BzveKU2+2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwqvToQWlpaRw3/x9qJBUAUHWspf7l5eWx3tfhw4djvb1/o3sAAIJHswMABI9mBwAIHs0OABA8mh0AIHixTGOqBZ1qSjOq5OTkWG8PAHB0qampMqcWQVvLoysqKmRO/YBASUmJrDkWfLMDAASPZgcACB7NDgAQPJodACB4NDsAQPBodgCA4MVy9EAt7szPz4/j5v8j7sXSAICjU8cLnHMuJSXFG7eOKxQXF8ucOsp2vPhmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGL5ehB3L9uoNSqRW/+b6S2oDt3/JvQ45CVleWNn3zyybJm06ZNMtezZ09vfPPmzbJGjX+ra3PO3kpfWFjojW/fvl3WIFzq/eCccwcPHvTGreMFlrKyskh1R0P3AAAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwYpnGrFOnThw3c1R169aVuX379lXLNaD6VdfE5UMPPSRzauLMOedycnK88Z07d8oaa6n5xo0bE74GZc+ePTLXoEEDmcvLy0v4vhAuayI6IyPDG7cWQVfVxKWFb3YAgODR7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQvFiOHlgjpnGqriMO+H8GDRrkjXfo0EHWfPTRRzK3ePHi476m49WlSxdv/J577pE1b7/9tsw98sgj3vgLL7wga4YPHy5zGzZs8MZPPfVUWTN27FhvfPbs2bJmy5YtMhe37Oxsb9xaIp+fn++N16tXT9YUFBQkdF04fhUVFd74iTheYOGbHQAgeDQ7AEDwaHYAgODR7AAAwaPZAQCCR7MDAAQvlqMH1ihwnKrriEOoevfu7Y2rsXDnnJsxY4Y3bo3iFxYWytz555/vjasxc+ec69y5s8xdcskl3vinn34qa4YNGyZzysCBAxOusYwYMSLW23v88ccTrnnuuedk7sYbbzyey/n/qGND1i9DKBwvqH61aiX+vcg6Kmb96kdV4ZsdACB4NDsAQPBodgCA4NHsAADBo9kBAIIXyzRmcXFxHDdzVCdiguf7Ztq0aTK3ZMkSb/z5559P+H4uvfTShGuqwpgxY070JUgPPvigzGVlZcnc3LlzE4o759z8+fO98dGjR8saayI0IyPDG7/uuutkjSXK1KUSdSF8Tfj8SEpK8sbVMuWawnruVK4mPN//E9/sAADBo9kBAIJHswMABI9mBwAIHs0OABA8mh0AIHixHD2orKyM42aOqnbtWC73e2Py5Mne+MiRI2XNkCFDZO7IkSPeeJSjB5aGDRvK3PLly73xdu3axXoNUVjLbqOMhh86dEjmrEXL7777bsL31bZtW288JydH1px00kkyt3btWm/89NNPlzXr1q2TOXVk4ZNPPpE1ubm53nijRo1kjfXeW716tczFSR3bcE4/52VlZbLGWoBfUlLijcd9lMH67E1JSfHGreehqKjouK8pUXyzAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwvlfjjdaklZpyUotXnXPu8OHDx31Nx6Jv374y9+mnn8rcLbfc4o2rqcqjsabvEhX1GiZMmBDr7ally+vXr5c1Y8eO9cat98ru3btlbsaMGd64taTaerznn3++N24tQN66das3br3mvXv3lrlu3brJXBQvv/xywjXq2sePHy9rbrjhBpnLzMz0xq2l3KrGOee2bNnijScnJ8sa9bpbE5fp6ekyp6Yu1ZRmVNZjUrk4P2/iwDc7AEDwaHYAgODR7AAAwaPZAQCCR7MDAASPZgcACF4sRw+sBbpxspaHqpHe6jpeYLGOF1jUtcc90htl7H/lypUyt3//fplTC4anTp0qa6xxcnXtI0aMkDWXXHKJN3733XfLmpdeeknmPv/8c2/89ttvlzXqWIlzzi1cuFDmFPWeuPTSS2XNTTfdJHPbt2/3xocNG5bYhR2H+vXre+OPPvqorPnDH/4gc+r1KCwslDXt27eXOXUU5MCBA7ImiuLiYpmrriX81meOOv5QEz57/ye+2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwYjl6YG3sjlPUzfj/TaznSI0PW2PF6vYWLFgga9SvCli3Z41/W0cPHn74YW+8R48esub555/3xq3t8tb4t3r+HnvsMVmzYcMGmVPPhXqszuljObVr6z/xlJQUmVuxYoU3Xp2/uJGXl5dQ3DnnXnnlFZlr1aqVN56bmytr9uzZI3PqV1isz0N1VMCqsUb4S0tLvXHr7ykK62+jbt263nhaWpqsUdddlfhmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDgxTKNmZSUFMfNHJU1WaZyNWEZaWZmpsxFmZqKOhGn6qxJuSeeeMIbP/nkk2XNBx98IHPqvpo0aSJrrOdPLW+ePXu2rJkwYYI3fuqpp8qa8ePHy5x17crNN98sc+r5O/vss2XNlClTvPEGDRrIGutvY9u2bd54586dZc0XX3whc2o5clZWlqyJMsFpXUOU2zt48KDMNW3a1BtXz53F+gxVi5ar06FDh2SuvLzcGz8RE5cWvtkBAIJHswMABI9mBwAIHs0OABA8mh0AIHg0OwBA8GI5elBdWrduLXNqUW+U8WrnnCspKfHGrfHlm266yRt/9tlnZU0UZ555psytXr1a5oYOHeqNWwuGf/vb33rj3bt3lzXvv/++zG3evNkbnzp1qqz56KOPZO7111/3xi+++GJZo5YwW0c6Lr/8cpnbvXu3N75+/XpZ88gjj8hclBH5KMdKotyeZenSpTLXs2fPhG9PvVc6duyY8G0551y/fv288VWrVskatbjZOecaNWrkjVtj+jk5Od54TTheYKkJR7iOF9/sAADBo9kBAIJHswMABI9mBwAIHs0OABA8mh0AIHixHD2wxnMTZY1Kq+MAzjmXl5fnjRcUFES6PcUayY77iMHOnTu9cesXB6wx6mnTpnnj1mNq06aNNz5z5kxZo0aynXNu3rx53nh+fr6smT59usz17t3bG+/Tp4+ssY4EKOrXH5zTj/e9996TNWPGjJG58847zxtftGiRrFF/N9Zra/2tjR071hu3/p5efPFFmXvmmWe88dzcXFmjjntYr5/1yxArV66UOaVFixYyt2TJEm+8a9euskYdwbCOQe3Zs0fmqkutWvp7UXX96s3x4psdACB4NDsAQPBodgCA4NHsAADBo9kBAIJ3whZBq0kwa7Knfv36Mte8eXNvvF27drLGmm6LQk2Cff7557LGmpYrLS31xhcsWCBrBg0alPB9WTVqEbQ1yTdy5EiZe+6557zxWbNmyRpr8e9bb73ljffq1UvWRJlcvPLKK2VuypQp3rg1wXbNNdfInHpMUZYzL1y4UOaiTGpef/31CV+Dc87ddttt3ria+nTOuQ8//NAbV+8h56JNXFqsx6uWN1vXULu2/yPXmmBevny5zKn3WNwTnGlpaTKXnp7ujdepU0fWRJmEP158swMABI9mBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDgxXL0oLy8POEaNfZ8+PBhWbN161aZy8nJ8cb37t2b0HUdD2s8V7FG+NVzdO6558qaZs2aRbov5bXXXvPG//KXv8iaoUOHypwaG//ggw9kzZlnnilz3333nTduPUfqeZ04caKsWbFihcyp6xs4cKCseeGFF2SuU6dO3rj1+l1wwQXe+McffyxrhgwZInNRjjlY74lRo0YlFHdOP94nn3xS1li5KO//CRMmyJxaVL1s2TJZs3///oSvoSYoKiqSuUOHDnnjNW1BNN/sAADBo9kBAIJHswMABI9mBwAIHs0OABC8WKYx1ULUuFmLdaMsI1VLWZ1zrrCw8Ngv7F/iXiydn5/vjWdnZ8saa4pOTRvOnTtX1lx00UXe+LvvvitrrOW56vqsxciNGzeWOXV9alLOOT2V99VXX8katcjYOec++eQTb7xPnz6yxloOfv/993vjffv2lTUXXnihN37KKafIGmu6WS2+/tvf/iZr3njjDZk79dRTvXFrOlFNNTZt2lTWPPLIIzKn9OjRQ+aspc6zZ8/2xrt06SJr1OO1FkFbn68NGzb0xnft2iVrDh48KHOKNVmp/p6sz+sToWZdDQAAVYBmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDgxXL0oLpGTK0l0WoZdf369WVNr169ZC4vL88bv++++2SNGv+Oqm3btt74ggULZM1NN90kc2p8ePz48bLmnXfe8cZnzpwpa6zR5kcffdQbnzNnjqyxjlqo4xTWUYEpU6Z446+88oqsmT9/vsz9+c9/9sbvvPNOWWM555xzvHFrGXWUxc3Lly+XOfUcjRkzRtZYy4LV83fZZZfJmrvuussb37lzp6y54447ZE4tDa+u5enOOTdu3DhvfN68ebKmQ4cOMqc+E60jV1GOHkR5HmoavtkBAIJHswMABI9mBwAIHs0OABA8mh0AIHg0OwBA8GI5epCSkpJwTVZWljdu/RKB2pzunHPbt2/3xnNzc2XN3r17ZU79goF1vEAdwVCbyZ1zbs+ePTI3ePBgb/yCCy6QNWeccYbMqeuYNGmSrHnrrbe8cWsUWf0KgHN69FrFnbNH+NWW+/79+8uaTZs2eeNnnnmmrHn99ddlbtCgQd74mjVrZE1paanMqWMEcY9/f/311zKnXsPmzZvLmieeeELm1N+7+mUPy5AhQ2Ru8eLFMnfttdd649bRA0uU4x7WEQOluLhY5tSvvVjvryisx6qONFVWVsZ6DceLb3YAgODR7AAAwaPZAQCCR7MDAASPZgcACF4s05hpaWn+GzcmK9WiUmvZszW5pRY3W1NEauIyKjV91KRJE1lTUFAgc88884w3PmHCBFljTRQuXbrUG69bt66sUUuOhw4dKmsaNWokc9ddd503Pn36dFnz05/+VOb+/ve/e+MbNmyQNWrB8IgRI2SNZdmyZd7473//e1lz//33y5x6zw4fPjyxC3P2BKe13Fo9JjX1fLT7WrVqlcwlentRpiCt22vRokWk2zv77LO9cbU8PSrr70l9Xlp/03Evglafe9X1AwHHqmZdDQAAVYBmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDgnXTkGOd4rdFTNUZtjchbC2WVH/zgBzI3a9ashG8vbmqs/uWXX471fqyR9ldeeUXm1OJfa1R6wIAB3vjJJ58sa3bt2iVz69ev98a7du0qa6KMmkdZmmzdj3V7ahm1tcBaPa/OOTdnzhxvPOr1KbfddpvMjRkzxhu3jj/MnTtX5urVq+eN9+nTR9b85Cc/8cat4zoLFiyQObWw21rGPn78eJlTrOM/6homT54sa9q1aydz5eXl3rh19MZagK9Yf58dO3b0xtVRJ+ec27lzZ8LXYDmWzwi+2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwYvnVAzX2qbZhO6dHpSsqKmRNenq6zKWmpnrjZWVlsiZucR8xUJo1ayZzSUlJCd+e+nUF55xr3bq1N757925ZE2UM3nLvvffK3KFDhxK+vSij15Zx48YlFHfOudmzZ8ucOnrwwAMPyJpRo0Z540899ZSsmTRpksy1bNnSG4/7FwfmzZsna2644QZvXP3ShXPObdy4UeZSUlK88RdffFHWRDnuYf3Cg/oVkbZt28oa6/YaNGjgjWdkZMiaKO9/6xcMVC45OTnh+6lKfLMDAASPZgcACB7NDgAQPJodACB4NDsAQPBimca0JiiVw4cPe+N16tSRNdZkVHVOXSpqyeuMGTNivZ/BgwfLXKNGjWTu008/9ca3bt0qa7p163bsF/Yv1uvUoUMHb/z222+XNQ8++KDMbd682RvfsmWLrFG5hg0byhqLmpabOHGirFm3bp3MqUXC1gLwKFOS27dvT7jm4Ycflrm77rpL5rZt2+aNW+/lX/ziF964tWj81VdflTk1PdmrVy9ZY1HPuZqmdc65X/3qV964tWDb+nzdv3+/N56ZmSlroigtLZU59Tyoz/gThW92AIDg0ewAAMGj2QEAgkezAwAEj2YHAAgezQ4AELxYjh6osVRrZLagoMAbLyoqkjXl5eUypxaiFhYWyhq1GPZodUqUIwbWyPjUqVO98Y8//ljWfP755zL39NNPe+PWUmI1rm3VDBgwQOY+++wzb9wabW7RooXMqeXgUUbxTzvtNJmLshDYWoRuLdZV9xVlwbZVYz0mNbpuHS9Yu3atzHXp0sUbX7Rokaz56quvvHFryXGU5+jNN9+M9fas5dsffvihN26999asWZPwNUT5/LJEOUYQdWl4VeGbHQAgeDQ7AEDwaHYAgODR7AAAwaPZAQCCF8s0Zu3a/puxfpa9bt26CdecfvrpMnfw4EFvfOXKlbLmu+++k7maoG3btt74ueeeK2sGDhwoc9YSX+Wf//ynN96uXTtZE2UKa8KECTJ37733JnxfUaYQv/zyS1ljeeedd7zxnJwcWdO3b1+ZGzdunDce5Xm1apYsWSJz99xzT8L3ZS0Nnzdvnjd+0UUXyRq1FP7qq6+WNUOHDpW5O++80xtv1qyZrIli9OjRCdf07t1b5rKysmQuPz/fG7c+R62pdsVaLK2mY61rOBH4ZgcACB7NDgAQPJodACB4NDsAQPBodgCA4NHsAADBi+XogWKNzKpxVWtBrrWMdO/evd54UlKSrKnp1BEDa/R6z549Mqfqbr31VlmzYsUKbzzKYmTn9Ai6NeoeZQw+yvVZxwF2794tc+o5t97L1u0pkyZNkrlbbrnFG//5z38ua6wFyNOnT/fGr732WlljUUcMohwRibrcWrGO5ERZBB2FtZS+R48eMrdgwQJvPMrxAktJSUmst3ci8M0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAMGj2QEAghfL0QM13m9tvVa/lHDo0CFZY91ehw4dvPHzzjtP1rz//vsyt3r16oRvb9GiRTKnWKPNw4cP98bVln3n7NFrlUtPT5c1PXv29MatcW3rGp5++mlvvH///rImyqh548aNZY0a4b/55ptlzR//+EeZu+SSS7zxAQMGyJoo1PECi3W8wBLliEGjRo1kTh0NslxxxRXe+CmnnCJr1K8AOOdcdna2Nz5r1ixZ06dPH5lTvxphHXdSR67ULzxYNc7p4y3WZ2VZWZnMKepXapzTxyZSU1MTvp+qxDc7AEDwaHYAgODR7AAAwaPZAQCCR7MDAAQvlmlMtaDZmqxUU1NpaWmyZt++fTK3fft2bzzuhahRJi6jev75573xG2+8UdZYk4v16tXzxgsKCmSNmnY866yzZM2Pf/xjmRs9erQ33q1bN1lz5513ylxhYaE3bk3/nX766d541KW/TZo08cb79esna9SkrXPOXXDBBd64NYWobu+BBx6QNVdddZXMqefCWhpuPefLly/3xkeOHClrJk+e7I1HWfZs1Vmv+3333SdzahqzZcuWsqZTp04JX8P+/ftlrrKy0huPMnEZlXpe1bWdKHyzAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwaHYAgODFcvSgqKgoobhzzlVUVHjj1lEB6yiDqlMLop2zR5jVIugorPvJy8tL+Passf877rhD5h577DFv3DrS8ctf/tIb/+KLLxKucU4fLbGOK1jUQuoxY8bIGjXab9m4caPMde7c2Ru3Xnfr+evRo4c3ro74OKev7+qrr5Y1ffv2lbkhQ4Z440888YSsGTFihMxt3rzZG1fHC5zTz9+6detkjXWERd2e9TpZS9KVVq1aydzu3bu98VWrVsma5s2bJ3wNcbOeh6ysLG/cWm59IvDNDgAQPJodACB4NDsAQPBodgCA4NHsAADBo9kBAIIXy9EDdSTAGj1NTk72xuvXry9rkpKSEr4GNerrnHObNm2SuerSoEEDmVOb0K1R6ZUrV8rc5Zdf7o03bNhQ1nTp0iWha3POuZkzZ8rc+PHjvfF27drJGuu++vfv743Pnz9f1qhjCRdffLGssUavo2zTt6jb27JlS8I106dPlzVNmzaVuZdeeskbt47kqPeKRR1Bci7a82f9bUR9PZQ2bdp44+qXDZxzbtmyZd649TdYE349wDr2oj57rZoTgW92AIDg0ewAAMGj2QEAgkezAwAEj2YHAAheLNOYirW4WU1hlZWVyRprGa+acrImmU455RSZsxazJmr06NEy99RTTyV8e9ZUmbWoNyUlxRsvKSmRNfPmzfPG33nnHVnTuHFjmfvTn/7kjVvXbU2Ybt261Ru///77ZY1aDm69FtZ7ZfDgwd74448/LmvOPfdcmVOv71//+ldZk5+f741fe+21ssaaWB0wYIA3vmjRIlljLV2/6667vPFatRL//3Zubq7MxT1xaVHL5z/44ANZo/4GCwsLZU379u1lbufOnd547dr6oz3KlKT1vMY9jVxV+GYHAAgezQ4AEDyaHQAgeDQ7AEDwaHYAgODR7AAAwYvl6IEaH87MzJQ1aizVqrGWJqsl0dY4dOfOnWVuzpw5MqekpqZ6408//bSsefvttxO+nwMHDshcdna2zKkR4d/85jeyZv/+/QnfjzVyvHDhQm984sSJsmb79u0y98ADD3jjQ4YMkTVqebS1RPiyyy6TuREjRnjjbdu2lTVdu3aVOXUd/fr1kzUff/yxN7506VJZYz3nP/zhD73x119/XdZYx0fU+yiKVq1ayVx1LoLu2LGjN66OAzinl+N369ZN1lh/70rcS5itz+W6devGel9VhW92AIDg0ewAAMGj2QEAgkezAwAEj2YHAAheLNOYagKquLhY1pSWlnrj1hSRNRGkFksvWbJE1uzZs0fmorCWWCs5OTkyV1RU5I3Xr18/4ftxLto02nvvveeN/+xnP5M11gLwN998M+FrGDhwYMI1aurTOefWrl3rjV9zzTWyxpqanTJlijduLeO1NGrUyBvft2+frJk8ebI3vmPHDlnz/vvvy5xaDn7VVVfJGmsSUonynow6cdm9e3dvfNOmTbImOTlZ5lasWOGNW0ud1US5NWkeZRozburz1Tn9XlGfXycK3+wAAMGj2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeFV69KCyslLWqCMG1thulOWm6oiDc/EfPYhCLdx1zrmMjAxv3FosPWrUKJlTi3pvvfVWWaNGzaMu1VXvFWucfNGiRTJ3/vnne+NvvPFGYhfmnGvdurXMWUdYmjRp4o1bY/+33367zH399dfeeJs2bWTN3r17vfH8/HxZE+WoQJTF5c4599xzz8V2DVdeeWWka1i1apU3fv3118uaxYsXy5x6zq3jCurxWscL4lyiXRXUDwHUNN+PqwQA4DjQ7AAAwaPZAQCCR7MDAASPZgcACB7NDgAQvCo9elBeXp7wbVm/HBBlBNfa1r1x48aEby9u1mizYh0vSE1NlTnriIGijhhYr5N1DQMGDPDG//GPf8iaevXqyZx67y1dulTWvPzyy974tm3bZI111GLu3Lne+Nlnny1rtm/fLnNRriGK3/3udzI3ePBgb3zGjBmR7kv9osTIkSNlzXnnneeNW0dROnToIHM/+tGPvPG8vDxZY/0igjoaZB09UEcMUlJSZE3cr3sU1hEu9esG1q+fnAh8swMABI9mBwAIHs0OABA8mh0AIHg0OwBA8GKZxlTq1q0rc2oCypq8a9q0qcxt2bLFG7cmA9U0lXPO5ebmytyJVr9+fZmzJlZfe+01b3zXrl2yRi3ztqbHrEW9M2fO9MatibOCggKZU3V33323rHn22WcTih+Nmr7r1q2brIkyYWctTb7iiiu8cWshtrVY/dVXX/XG+/XrJ2uGDx8uc+raL774Ylmjplw/+eQTWWMtd7/sssu88c8++0zWHDx4UObWr1/vjUeZtLVkZ2fLnJqKLikpkTVRFupby55ZBA0AQA1BswMABI9mBwAIHs0OABA8mh0AIHg0OwBA8GI5epCUlJRQ3GKNmefn5yd8e9ZYvXXMoSaLshDbOb2M94wzzpA1a9as8catpdLNmjWTuSeffNIbHz9+vKyxcmqkPcpo/7Rp02Tu/vvvl7kLL7zQG580aZKsmT9/vszdcMMN3niUx1QTlgg759xZZ53ljY8bN07WqOMjDz30kKw555xzZE69l63PnB07dsictfBZibJY3cpVF7Xs2TnnCgsLvXEWQQMAUM1odgCA4NHsAADBo9kBAIJHswMABI9mBwAIXixHD9T4d5Rt2GlpaTKXnp4uc2oMuEGDBrKmYcOGCees4w8qV9NGcP+3rVu3xnp7rVu3ljn1SxhjxoyRNeXl5TKnRs1PO+00WfPll1964zk5ObKmf//+Mjd58mRv3HoeLJ07d064Ro3jv/3227Jm6dKlCd+PRf2qhnPOrVy50hvfuHGjrPnmm2+88Z49e8oa6zGpEfl169bJGuvzyBrHV9RnWGlpqazJysqSOfWrDNbfTBTW86B+WaamHHv5N77ZAQCCR7MDAASPZgcACB7NDgAQPJodACB4VboI+vDhwwnfVklJicxZC1vV9JG1RLWysjLhXKtWrWSNms7asGGDrLGWZVdUVHjjavrJOfvxNmnSxBvPzMyUNWray/LZZ58lnFPLj6NSS6+dc2737t0J397mzZtlbtiwYd749ddfL2tatGghc3PmzDn2C/uX3//+99649TdoTcupCWuL9ZyPGjXKGx87dqysUe/X1atXyxprqrFly5beeHZ2tqyxnqMDBw5449bfk5pQV1PKztmvhfqMiJv1PiouLvbGrdfiROCbHQAgeDQ7AEDwaHYAgODR7AAAwaPZAQCCR7MDAAQvlqMHatFx3AuQo4yyRhmhdk4vPo2y/LVOnToyZ40Oq5x1vMCiRu6jjOLXFGoB+KxZs2SNel4nTpwoa6wjMfXq1fPG9+3bJ2sse/fuTbgmyjGfPn36yNzixYsTvj3LtGnTvHHraIsa4e/ataussW5P/R3u2LFD1ljHnVRdSkpKwtdgfU6p97hz+jPW+lyxPkfVUQvraIQ6umF97ll/T1WFb3YAgODR7AAAwaPZAQCCR7MDAASPZgcACF4s05hquWnUSUglyk/N5+fny5y1hFktgrYmjNR91bSFqFUtKytL5qzXI4oo7wnFmryzqKlL671ivSfUJF2bNm1kjZpc/Pbbb2VN3BOX6nPAOf2eUMuZnXPuyy+/9Mbz8vJkjTU1qBY3W+9Ja1JTTWZbk5Bqataa4LQ+R9VkdpTpXOu+rOdoz5493njUv6eqwjc7AEDwaHYAgODR7AAAwaPZAQCCR7MDAASPZgcACF4sRw/UmL412h9F7dqJX641Dq3GtZ2LdvRA3Zf1PBQWFsqcerxRx4qrS9zHC76voh45Ua9vTk6OrFHvy+o89qL+ZpzTo/XW35P6u7GWCFvLt9Xfkxqdd84+RqAek1oM7pz+jFALmJ1zLiMjQ+aKi4u9cWtZfJQF/dbzoFjHKVgEDQBAFaDZAQCCR7MDAASPZgcACB7NDgAQPJodACB4sRw9UGOkcY+gb9u2LeEaaxw6Nzf3eC6nytX0IwaoOaIcMWjatKnMqRF+61cmkpOTZe67777zxq3jFOr9/80338gai3UsIU67d++ulvupTtavP6ijESfieIGFb3YAgODR7AAAwaPZAQCCR7MDAASPZgcACF4s05h169aN42aOKisrS+aqa/mwtdw0yoJV4ETZuXNnrLdnTWpauRPNWsJ85MiRarwSv5rwmRNlAX5N8/24SgAAjgPNDgAQPJodACB4NDsAQPBodgCA4NHsAADBi+XoQXZ2tjce90hvlGW3ceN4ARCWmnC8wFITPnOspc7fl4X1fLMDAASPZgcACB7NDgAQPJodACB4NDsAQPBodgCA4MVy9CAvL88bt7ZhV1RUJHw/1ubtsrKyhG8PwIllfUZUVlZW45XExzpypX7BIOoxLfU5GvdxgNTUVJmrXdvfRpKSkmRNlM//48U3OwBA8Gh2AIDg0ewAAMGj2QEAgkezAwAEL5ZpzMzMTG887omb4uLiWG8PwIn1fZ24tFjTk+rxJicnR7ovNQkZ9zRmmzZtZK5Ro0beeOPGjWXNzp07j/eSEsY3OwBA8Gh2AIDg0ewAAMGj2QEAgkezAwAEj2YHAAheLEcPzjnnHG+8b9++smb79u3e+JYtW2RNixYtZC4nJ0fmlHr16smctZhViTJWbI1eq+s7ePCgrCkoKEj4vqzHWl2j4VEXAqtrtxbXKuXl5Qnfj5WzHpN1feo6Dh06JGvU0t309HRZYx0NUtdujbRbI/cZGRneeN26dWWNuj7ruq3XSV17lPeKc/rxWs+Rui/rPW69j9TjtY5plZSUyFxaWpo33rlzZ1nTvXt3b7y0tFTWzJs3T+bUe7Zjx46y5ljwzQ4AEDyaHQAgeDQ7AEDwaHYAgODR7AAAwTvmacyRI0fKXKdOnbzxbt26yZooi0DVwumorOm2srIyb9yaOItbYWGhNx73ktfqfExK1KlPde3WJFhNoN5fUakJRes9Xp3U62FNDaoJ0yjTudZ9WdPS1uSneu9Z15CSkpJwTZRpTGvi0qImIXNzc2VNnTp1vPG9e/dGugb1Oq1evTrS7f0b3+wAAMGj2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeCcdOca5c2s0tmHDht74vn37ol0VAKDGqF1bn1KL+yhUFMfSxvhmBwAIHs0OABA8mh0AIHg0OwBA8Gh2AIDg0ewAAME75qMH9evXl7kDBw7EdT0AgMCpX0pwzrkdO3Z441YP4ugBAACOZgcA+C9AswMABI9mBwAIHs0OABA8vd3zf9m/f3/CNx5lefSgQYNkTa9evWTu66+/9sabNGkia6wFprVq+f8fkJqaKmvKysq88ZSUFFlj5TIyMrzxysrKhK/BOefS0tK88YqKClmTlJTkjScnJ8sa9dxZrGkqdQ1Wznpe1etuTYhZ72X1eljXbS3WLS4u9sat96t6/qzrtt4r6vmzaqz7sl4PxXr+otQcPHgw4ZqioiKZKy0t9cYPHToka6Kwrk/9HVrXvXXrVplLT0/3xtu3by9r1PPaunVrWbNlyxaZy87OlrnjwTc7AEDwaHYAgODR7AAAwaPZAQCCR7MDAASPZgcACN4xL4IGAOD7im92AIDg0ewAAMGj2QEAgkezAwAEj2YHAAgezQ4AEDyaHQAgeDQ7AEDwaHYAgOD9H+Go+JK6e2HQAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] @@ -2756,10 +1295,9 @@ ], "source": [ "L = 200\n", - "current_img = inputimg[None, None, ...].to(device)\n", + "current_img = inputimg[None, ...].to(device)\n", "scheduler.set_timesteps(num_inference_steps=1000)\n", "\n", - "\n", "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", "for t in progress_bar: # go through the noising process\n", " with autocast(enabled=False):\n", @@ -2787,7 +1325,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 173, "id": "7ab274bd-ea60-4674-b59b-d41de98fee5b", "metadata": { "lines_to_next_cell": 2 @@ -2797,12 +1335,12 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|█████████████████████████████████████████| 200/200 [00:11<00:00, 17.16it/s]\n" + "100%|█████████████████████████████████████████| 200/200 [00:15<00:00, 12.79it/s]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -2812,10 +1350,8 @@ } ], "source": [ - "\n", - "\n", "y = torch.tensor(0) # define the desired class label\n", - "scale = 5 # define the desired gradient scale s\n", + "scale = 6 # define the desired gradient scale s\n", "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", "\n", "for i in progress_bar: # go through the denoising process\n", @@ -2860,13 +1396,13 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 175, "id": "ecffaaf3-a7df-453e-81a9-757113d85084", "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -2879,11 +1415,19 @@ "\n", "diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy()\n", "plt.style.use(\"default\")\n", - "plt.imshow(diff, cmap=\"jet\")\n", + "plt.imshow(diff[0,...], cmap=\"jet\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", "plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c459ab23-459d-4063-824e-39dac93abb43", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py index 0b63c5d1..1df629ac 100644 --- a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py +++ b/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py @@ -19,7 +19,7 @@ # This tutorial illustrates how to use MONAI for training a 2D gradient-guided anomaly detection using DDIMs [1]. # # We train a diffusion model on 2D slices of brain MR images. A classification model is trained to predict whether the given slice shows a tumor or not.\ -# We then tranlsate an input slice to its healthy reconstruction using DDIMs.\ +# We then translate an input slice to its healthy reconstruction using DDIMs.\ # Anomaly detection is performed by taking the difference between input and output, as proposed in [1]. # # [1] - Wolleb et al. "Diffusion Models for Medical Anomaly Detection" https://arxiv.org/abs/2203.04306 @@ -29,7 +29,7 @@ # %% # !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" # !python -c "import matplotlib" || pip install -q matplotlib -# !python -c "import seaborn" || pi resblock_updown: bool = False,p install -q seaborn +# !python -c "import seaborn" || pip install -q seaborn # %% [markdown] # ## Setup imports @@ -45,9 +45,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + import os import time -from typing import Dict import tempfile import matplotlib.pyplot as plt import numpy as np @@ -64,9 +64,8 @@ from generative.inferers import DiffusionInferer from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet from generative.networks.schedulers.ddim import DDIMScheduler - - torch.multiprocessing.set_sharing_strategy("file_system") + print_config() @@ -86,19 +85,13 @@ # %% [markdown] tags=[] # ## Preprocessing of the BRATS Dataset in 2D slices for training # We download the BRATS training dataset from the Decathlon dataset. \ -# We slice the volumes in axial 2D slices and stack them into a tensor called _total_train_slices_ (this takes a while).\ -# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_train_labels_. +# We slice the volumes in axial 2D slices, and assign slice-wise labels (0 for healthy, 1 for diseased) to all slices. +# Here we use transforms to augment the training dataset: # - -# %% [markdown] -# Here we use transforms to augment the training dataset, as usual: -# -# 1. `LoadImaged` loads the hands images from files. +# 1. `LoadImaged` loads the brain MR images from files. # 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. -# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. -# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# 1. `ScaleIntensityRangePercentilesd` takes the lower and upper intensity percentiles and scales them to [0, 1]. # -# To avoid a bias in the classification labels, cut the lowest and highest 10 slices, as most tumors occur in the middle part of the brain. # %% channel = 0 # 0 = Flair @@ -113,82 +106,59 @@ transforms.EnsureTyped(keys=["image", "label"]), transforms.Orientationd(keys=["image", "label"], axcodes="RAS"), transforms.Spacingd(keys=["image", "label"], pixdim=(3.0, 3.0, 2.0), mode=("bilinear", "nearest")), - transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 64)), + transforms.CenterSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 44)), transforms.ScaleIntensityRangePercentilesd(keys="image", lower=0, upper=99.5, b_min=0, b_max=1), + transforms.RandSpatialCropd(keys=["image", "label"], roi_size=(64, 64, 1), random_size=False), + transforms.Lambdad(keys=["image", "label"], func=lambda x: x.squeeze(-1)), transforms.CopyItemsd(keys=["label"], times=1, names=["slice_label"]), - transforms.Lambdad( - keys=["slice_label"], func=lambda x: (x.reshape(x.shape[0], -1, x.shape[-1]).sum(1) > 0).float().squeeze() - ), + transforms.Lambdad(keys=["slice_label"], func=lambda x: 0.0 if x.sum() > 0 else 1.0), ] ) - -def get_batched_2d_axial_slices(data: Dict): - images_3D = data["image"] - batched_2d_slices = torch.cat(images_3D.split(1, dim=-1)[10:-10], 0).squeeze( - -1 - ) # we cut the lowest and highest 10 slices, because we are interested in the middle part of the brain. - slice_label = data["slice_label"] - slice_label = torch.cat(slice_label.split(1, dim=-1)[10:-10], 0).squeeze() - return batched_2d_slices, slice_label - - # %% jupyter={"outputs_hidden": false} +batch_size=64 train_ds = DecathlonDataset( root_dir=root_dir, task="Task01_BrainTumour", section="training", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + cache_rate=1.0, # you may need a few Gb of RAM... Set to 0 otherwise num_workers=4, - download=True, # Set download to True if the dataset hasnt been downloaded yet + download=False, # Set download to True if the dataset hasnt been downloaded yet seed=0, transform=train_transforms, ) -print("len train data", len(train_ds)) # this gives the number of patients in the training set - -train_loader_3D = DataLoader(train_ds, batch_size=1, shuffle=True, num_workers=4) -data_2d_slices = [] -data_slice_label = [] -for i, data in enumerate(train_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices.append(b2d) - data_slice_label.append(slice_label2d) +print(f"Length of training data: {len(train_ds)}") # this gives the number of patients in the training set +print(f'Train image shape {train_ds[0]["image"].shape}') -total_train_slices = torch.cat(data_2d_slices, 0) -total_train_labels = torch.cat(data_slice_label, 0) +train_loader = DataLoader( + train_ds, batch_size=batch_size, shuffle=True, num_workers=4, drop_last=True, persistent_workers=True +) # %% [markdown] tags=[] # ## Preprocessing of the BRATS Dataset in 2D slices for validation -# We download the BRATS validation dataset from the Decathlon dataset. -# We slice the volumes in axial 2D slices and stack them into a tensor called _total_val_slices_. -# The corresponding slice-wise labels (0 for healthy, 1 for diseased) are stored in the tensor _total_val_labels_. +# We download the BRATS validation dataset from the Decathlon dataset, and define the dataloader to load 2D slices for validation. +# # # %% val_ds = DecathlonDataset( root_dir=root_dir, task="Task01_BrainTumour", - section="validation", # validation - cache_rate=0.0, # you may need a few Gb of RAM... Set to 0 otherwise + section="validation", + cache_rate=1.0, # you may need a few Gb of RAM... Set to 0 otherwise num_workers=4, - download=True, # Set download to True if the dataset hasnt been downloaded yet + download=False, # Set download to True if the dataset hasnt been downloaded yet seed=0, transform=train_transforms, ) +print(f"Length of training data: {len(val_ds)}") +print(f'Validation Image shape {val_ds[0]["image"].shape}') - -val_loader_3D = DataLoader(val_ds, batch_size=1, shuffle=True, num_workers=4) -data_2d_slices_val = [] -data_slice_label_val = [] -for i, data in enumerate(val_loader_3D): - b2d, slice_label2d = get_batched_2d_axial_slices(data) - data_2d_slices_val.append(b2d) - data_slice_label_val.append(slice_label2d) - -total_val_slices = torch.cat(data_2d_slices_val, 0) -total_val_labels = torch.cat(data_slice_label_val, 0) +val_loader = DataLoader( + val_ds, batch_size=batch_size, shuffle=False, num_workers=4, drop_last=True, persistent_workers=True +) # %% [markdown] @@ -220,33 +190,27 @@ def get_batched_2d_axial_slices(data: Dict): inferer = DiffusionInferer(scheduler) + # %% [markdown] tags=[] # ## Model training of the diffusion model -# We train our diffusion model for 100 epochs, with a batch size of 32. +# We train our diffusion model for 2000 epochs. # %% jupyter={"outputs_hidden": false} -n_epochs = 100 -batch_size = 32 -val_interval = 1 +n_epochs = 2000 +val_interval = 20 epoch_loss_list = [] val_epoch_loss_list = [] scaler = GradScaler() total_start = time.time() + for epoch in range(n_epochs): model.train() epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) # shuffle training data new - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - subset_2D_val = zip(total_val_slices.split(1), total_val_labels.split(1)) # - - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) + + for step, data in enumerate(train_loader): + images = data['image'].to(device) + classes = data['slice_label'].to(device) optimizer.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t @@ -256,35 +220,31 @@ def get_batched_2d_axial_slices(data: Dict): # Get model prediction noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) - loss = F.mse_loss(noise_pred.float(), noise.float()) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update() epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) epoch_loss_list.append(epoch_loss / (step + 1)) if (epoch) % val_interval == 0: model.eval() val_epoch_loss = 0 - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with torch.no_grad(): + for step, data in enumerate(val_loader): + images = data['image'].to(device) + classes = data['slice_label'].to(device) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): with autocast(enabled=True): noise = torch.randn_like(images).to(device) noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) val_loss = F.mse_loss(noise_pred.float(), noise.float()) - val_epoch_loss += val_loss.item() - progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss += val_loss.item() val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train diffusion completed, total time: {total_time}.") @@ -339,6 +299,7 @@ def get_batched_2d_axial_slices(data: Dict): # # %% +device = torch.device("cuda") classifier = DiffusionModelEncoder( spatial_dims=2, in_channels=1, @@ -349,41 +310,34 @@ def get_batched_2d_axial_slices(data: Dict): num_head_channels=64, with_conditioning=False, ) + classifier.to(device) # %% [markdown] # ## Model training of the classification model -# We train our classification model for 100 epochs. +# We train our classification model for 1000 epochs. # # %% -batch_size = 32 -n_epochs = 100 -val_interval = 1 + +n_epochs = 1000 +val_interval = 10 epoch_loss_list = [] val_epoch_loss_list = [] optimizer_cls = torch.optim.Adam(params=classifier.parameters(), lr=2.5e-5) -classifier.to(device) -weight = torch.tensor((3, 1)).float().to(device) # account for the class imbalance in the dataset - scaler = GradScaler() total_start = time.time() for epoch in range(n_epochs): classifier.train() epoch_loss = 0 - indexes = list(torch.randperm(total_train_slices.shape[0])) - data_train = total_train_slices[indexes] # shuffle the training data - labels_train = total_train_labels[indexes] - subset_2D = zip(data_train.split(batch_size), labels_train.split(batch_size)) - progress_bar = tqdm(enumerate(subset_2D), total=len(indexes) / batch_size) - progress_bar.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar: - images = a.to(device) - classes = b.to(device) + for step, data in enumerate(train_loader): + images = data['image'].to(device) + classes = data['slice_label'].to(device) + #classes[classes==2]=0 optimizer_cls.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) @@ -395,25 +349,22 @@ def get_batched_2d_axial_slices(data: Dict): # Get model prediction noisy_img = scheduler.add_noise(images, noise, timesteps) # add t steps of noise to the input image pred = classifier(noisy_img, timesteps) - loss = F.cross_entropy(pred, classes.long(), weight=weight, reduction="mean") - loss.backward() - optimizer_cls.step() + loss = F.cross_entropy(pred, classes.long()) + + loss.backward() + optimizer_cls.step() epoch_loss += loss.item() - progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) epoch_loss_list.append(epoch_loss / (step + 1)) - print("final step train", step) if (epoch + 1) % val_interval == 0: classifier.eval() val_epoch_loss = 0 - subset_2D_val = zip(total_val_slices.split(batch_size), total_val_labels.split(batch_size)) # - progress_bar_val = tqdm(enumerate(subset_2D_val)) - progress_bar_val.set_description(f"Epoch {epoch}") - for step, (a, b) in progress_bar_val: - images = a.to(device) - classes = b.to(device) + + for step, data_val in enumerate(val_loader): + images = data_val['image'].to(device) + classes = data_val['slice_label'].to(device) timesteps = torch.randint(0, 1, (len(images),)).to( device ) # check validation accuracy on the original images, i.e., do not add noise @@ -426,8 +377,8 @@ def get_batched_2d_axial_slices(data: Dict): val_epoch_loss += val_loss.item() _, predicted = torch.max(pred, 1) - progress_bar_val.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) - val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train completed, total time: {total_time}.") @@ -455,12 +406,14 @@ def get_batched_2d_axial_slices(data: Dict): # We pick a diseased subject of the validation set as input image. We want to translate it to its healthy reconstruction. # %% - -inputimg = total_val_slices[120][0, ...] # Pick an input slice of the validation set to be transformed -inputlabel = total_val_labels[120] # Check whether it is healthy or diseased +idx_unhealthy = np.argwhere(data_val["slice_label"].numpy() == 0).squeeze() +idx = idx_unhealthy[4] # Pick a random slice of the validation set to be transformed +inputimg = data_val["image"][idx] # Pick an input slice of the validation set to be transformed +inputlabel = data_val["slice_label"][idx] # Check whether it is healthy or diseased +print('minmax', inputimg.min(), inputimg.max()) plt.figure("input" + str(inputlabel)) -plt.imshow(inputimg, vmin=0, vmax=1, cmap="gray") +plt.imshow(inputimg[0,...], vmin=0, vmax=1, cmap="gray") plt.axis("off") plt.tight_layout() plt.show() @@ -477,10 +430,9 @@ def get_batched_2d_axial_slices(data: Dict): # %% jupyter={"outputs_hidden": false} L = 200 -current_img = inputimg[None, None, ...].to(device) +current_img = inputimg[None, ...].to(device) scheduler.set_timesteps(num_inference_steps=1000) - progress_bar = tqdm(range(L)) # go back and forth L timesteps for t in progress_bar: # go through the noising process with autocast(enabled=False): @@ -502,10 +454,8 @@ def get_batched_2d_axial_slices(data: Dict): # The scale s is used to amplify the gradient. # %% - - y = torch.tensor(0) # define the desired class label -scale = 5 # define the desired gradient scale s +scale = 6 # define the desired gradient scale s progress_bar = tqdm(range(L)) # go back and forth L timesteps for i in progress_bar: # go through the denoising process @@ -546,7 +496,9 @@ def get_batched_2d_axial_slices(data: Dict): diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() plt.style.use("default") -plt.imshow(diff, cmap="jet") +plt.imshow(diff[0,...], cmap="jet") plt.tight_layout() plt.axis("off") plt.show() + +# %% From 62a8fe379b6601f02a23a6bd6bdcac862fb43fe8 Mon Sep 17 00:00:00 2001 From: Walter Hugo Lopez Pinaya Date: Wed, 22 Mar 2023 12:13:00 +0000 Subject: [PATCH 21/23] Fix changed files Signed-off-by: Walter Hugo Lopez Pinaya --- generative/networks/schedulers/ddim.py | 37 ++++++++++--------- .../mednist_ddpm/bundle/configs/common.yaml | 9 +++-- .../mednist_ddpm/bundle/configs/infer.yaml | 2 +- .../mednist_ddpm/bundle/configs/logging.conf | 2 +- .../mednist_ddpm/bundle/configs/metadata.json | 4 +- .../mednist_ddpm/bundle/configs/train.yaml | 30 +++++++-------- .../bundle/configs/train_multigpu.yaml | 4 +- .../bundle/docs/sub_train_multigpu.sh | 2 +- tests/utils.py | 2 +- 9 files changed, 47 insertions(+), 45 deletions(-) diff --git a/generative/networks/schedulers/ddim.py b/generative/networks/schedulers/ddim.py index 3fd58321..7f155dbb 100644 --- a/generative/networks/schedulers/ddim.py +++ b/generative/networks/schedulers/ddim.py @@ -8,12 +8,12 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +# # ========================================================================= # Adapted from https://github.com/huggingface/diffusers # which has the following license: # https://github.com/huggingface/diffusers/blob/main/LICENSE - +# # Copyright 2022 UC Berkeley Team and The HuggingFace Team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,7 @@ # limitations under the License. # ========================================================================= -from typing import Optional, Tuple, Union +from __future__ import annotations import numpy as np import torch @@ -41,6 +41,7 @@ class DDIMScheduler(nn.Module): Denoising diffusion implicit models is a scheduler that extends the denoising procedure introduced in denoising diffusion probabilistic models (DDPMs) with non-Markovian guidance. Based on: Song et al. "Denoising Diffusion Implicit Models" https://arxiv.org/abs/2010.02502 + Args: num_train_timesteps: number of diffusion steps used to train the model. beta_start: the starting `beta` value of inference. @@ -102,16 +103,18 @@ def __init__( # standard deviation of the initial noise distribution self.init_noise_sigma = 1.0 - # setable values - self.num_inference_steps = None self.timesteps = torch.from_numpy(np.arange(0, num_train_timesteps)[::-1].astype(np.int64)) self.clip_sample = clip_sample self.steps_offset = steps_offset - def set_timesteps(self, num_inference_steps: int, device: Optional[Union[str, torch.device]] = None) -> None: + # default the number of inference timesteps to the number of train steps + self.set_timesteps(num_train_timesteps) + + def set_timesteps(self, num_inference_steps: int, device: str | torch.device | None = None) -> None: """ Sets the discrete timesteps used for the diffusion chain. Supporting function to be run before inference. + Args: num_inference_steps: number of diffusion steps used when generating samples with a pre-trained model. device: target device to put the data. @@ -147,11 +150,12 @@ def step( timestep: int, sample: torch.Tensor, eta: float = 0.0, - generator: Optional[torch.Generator] = None, - ) -> Tuple[torch.Tensor, torch.Tensor]: + generator: torch.Generator | None = None, + ) -> tuple[torch.Tensor, torch.Tensor]: """ Predict the sample at the previous timestep by reversing the SDE. Core function to propagate the diffusion process from the learned model outputs (most often the predicted noise). + Args: model_output: direct output from learned diffusion model. timestep: current discrete timestep in the diffusion chain. @@ -159,6 +163,7 @@ def step( eta: weight of noise for added noise in diffusion step. predict_epsilon: flag to use when model predicts the samples directly instead of the noise, epsilon. generator: random number generator. + Returns: pred_prev_sample: Predicted previous sample pred_original_sample: Predicted original sample @@ -187,12 +192,13 @@ def step( # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf if self.prediction_type == "epsilon": pred_original_sample = (sample - beta_prod_t ** (0.5) * model_output) / alpha_prod_t ** (0.5) + pred_epsilon = model_output elif self.prediction_type == "sample": pred_original_sample = model_output + pred_epsilon = (sample - alpha_prod_t ** (0.5) * pred_original_sample) / beta_prod_t ** (0.5) elif self.prediction_type == "v_prediction": pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output - # predict V - model_output = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample + pred_epsilon = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample # 4. Clip "predicted x_0" if self.clip_sample: @@ -204,7 +210,7 @@ def step( std_dev_t = eta * variance ** (0.5) # 6. compute "direction pointing to x_t" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf - pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * model_output + pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * pred_epsilon # 7. compute x_t-1 without "random noise" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf pred_prev_sample = alpha_prod_t_prev ** (0.5) * pred_original_sample + pred_sample_direction @@ -279,18 +285,15 @@ def reversed_step( return pred_post_sample, pred_original_sample - def add_noise( - self, - original_samples: torch.Tensor, - noise: torch.Tensor, - timesteps: torch.Tensor, - ) -> torch.Tensor: + def add_noise(self, original_samples: torch.Tensor, noise: torch.Tensor, timesteps: torch.Tensor) -> torch.Tensor: """ Add noise to the original samples. + Args: original_samples: original samples noise: noise to add to samples timesteps: timesteps tensor indicating the timestep to be computed for each sample. + Returns: noisy_samples: sample with added noise """ diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml index c6073eb5..e48b917b 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/common.yaml @@ -1,6 +1,6 @@ # This file defines common definitions used in training and inference, most importantly the network definition -imports: +imports: - $import os - $import datetime - $import torch @@ -27,8 +27,8 @@ network_def: attention_levels: [false, true, true] num_res_blocks: 1 num_head_channels: 128 - -network: $@network_def.to(@device) + +network: $@network_def.to(@device) bundle_root: . ckpt_path: $@bundle_root + '/models/model.pt' @@ -54,7 +54,8 @@ base_transforms: scheduler: _target_: generative.networks.schedulers.DDPMScheduler num_train_timesteps: '@num_train_timesteps' - + inferer: _target_: generative.inferers.DiffusionInferer scheduler: '@scheduler' + \ No newline at end of file diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml index 46297e18..f140c3b6 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/infer.yaml @@ -35,4 +35,4 @@ testing: #alternative version which saves to a jpg file testing_jpg: - '@load_state' -- '$@save_trans(@sample(@noise.to(@device))[0])' +- '$@save_trans(@sample(@noise.to(@device))[0])' \ No newline at end of file diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf b/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf index 91c1a21c..db85a0b9 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf +++ b/model-zoo/models/mednist_ddpm/bundle/configs/logging.conf @@ -18,4 +18,4 @@ formatter=fullFormatter args=(sys.stdout,) [formatter_fullFormatter] -format=%(asctime)s - %(name)s - %(levelname)s - %(message)s +format=%(asctime)s - %(name)s - %(levelname)s - %(message)s \ No newline at end of file diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json b/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json index 1e657634..aef66f9f 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json +++ b/model-zoo/models/mednist_ddpm/bundle/configs/metadata.json @@ -7,9 +7,7 @@ "monai_version": "1.0.0", "pytorch_version": "1.10.2", "numpy_version": "1.21.2", - "optional_packages_version": { - "generative": "0.1.0" - }, + "optional_packages_version": {"generative":"0.1.0"}, "task": "MedNIST Hand Generation", "description": "", "authors": "Walter Hugo Lopez Pinaya, Mark Graham, and Eric Kerfoot", diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml index 919e3a21..459e23bd 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/train.yaml @@ -4,7 +4,7 @@ output_dir: $datetime.datetime.now().strftime('./results/output_%y%m%d_%H%M%S') dataset_dir: ./data -train_data: +train_data: _target_ : MedNISTDataset root_dir: '@dataset_dir' section: training @@ -12,7 +12,7 @@ train_data: progress: false seed: 0 -val_data: +val_data: _target_ : MedNISTDataset root_dir: '@dataset_dir' section: validation @@ -37,7 +37,7 @@ save_interval: 5 train_transforms: - _target_: RandAffined keys: '@image' - rotate_range: + rotate_range: - ['$-np.pi / 36', '$np.pi / 36'] - ['$-np.pi / 36', '$np.pi / 36'] translate_range: @@ -49,14 +49,14 @@ train_transforms: spatial_size: [64, 64] padding_mode: "zeros" prob: '@rand_prob' - + train_ds: _target_: Dataset data: $@train_datalist transform: _target_: Compose transforms: '$@base_transforms + @train_transforms' - + train_loader: _target_: ThreadDataLoader dataset: '@train_ds' @@ -65,7 +65,7 @@ train_loader: num_workers: '@num_workers' use_thread_workers: '@use_thread_workers' persistent_workers: '$@num_workers > 0' - shuffle: true + shuffle: true val_ds: _target_: Dataset @@ -73,7 +73,7 @@ val_ds: transform: _target_: Compose transforms: '@base_transforms' - + val_loader: _target_: DataLoader dataset: '@val_ds' @@ -81,19 +81,19 @@ val_loader: num_workers: '@num_workers' persistent_workers: '$@num_workers > 0' shuffle: false - + lossfn: _target_: torch.nn.MSELoss - + optimizer: _target_: torch.optim.Adam params: $@network.parameters() lr: '@lr' - + prepare_batch: _target_: generative.engines.DiffusionPrepareBatch num_train_timesteps: '@num_train_timesteps' - + val_handlers: - _target_: StatsHandler name: train_log @@ -114,7 +114,7 @@ evaluator: output_transform: $monai.handlers.from_engine([@pred, @label]) metric_cmp_fn: '$scripts.inv_metric_cmp_fn' val_handlers: '$list(filter(bool, @val_handlers))' - + handlers: - _target_: CheckpointLoader _disabled_: $not os.path.exists(@ckpt_path) @@ -144,14 +144,14 @@ trainer: optimizer: '@optimizer' inferer: '@inferer' prepare_batch: '@prepare_batch' - key_train_metric: + key_train_metric: train_acc: _target_: MeanSquaredError output_transform: $monai.handlers.from_engine([@pred, @label]) metric_cmp_fn: '$scripts.inv_metric_cmp_fn' train_handlers: '$list(filter(bool, @handlers))' amp: '@use_amp' - -training: + +training: - '$monai.utils.set_determinism(0)' - '$@trainer.run()' diff --git a/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml b/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml index 51f5acf4..2811612f 100644 --- a/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml +++ b/model-zoo/models/mednist_ddpm/bundle/configs/train_multigpu.yaml @@ -21,10 +21,10 @@ vsampler: shuffle: false val_loader#sampler: '@vsampler' -training: +training: - $import torch.distributed as dist - $dist.init_process_group(backend='nccl') - $torch.cuda.set_device(@device) - $monai.utils.set_determinism(seed=123), - $@trainer.run() -- $dist.destroy_process_group() +- $dist.destroy_process_group() \ No newline at end of file diff --git a/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh b/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh index 4d5f6af0..7c424af0 100644 --- a/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh +++ b/model-zoo/models/mednist_ddpm/bundle/docs/sub_train_multigpu.sh @@ -33,4 +33,4 @@ torchrun --standalone --nnodes=1 --nproc_per_node=2 -m monai.bundle run training --config_file "$CONFIG" \ --logging_file "$BUNDLE/configs/logging.conf" \ --bundle_root "$BUNDLE" \ - --dataset_dir "$DATASET" + --dataset_dir "$DATASET" \ No newline at end of file diff --git a/tests/utils.py b/tests/utils.py index d8f86178..c2c81dde 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,6 @@ # COPIED FROM https://github.com/Project-MONAI/MONAI/blob/fdd07f36ecb91cfcd491533f4792e1a67a9f89fc/tests/utils.py # --------------------------------------------------------------- - +# # Copyright (c) MONAI Consortium # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From e09220e3caeb4aed71e2667350da841b59bfb235 Mon Sep 17 00:00:00 2001 From: Walter Hugo Lopez Pinaya Date: Wed, 22 Mar 2023 12:13:55 +0000 Subject: [PATCH 22/23] Fix changed files Signed-off-by: Walter Hugo Lopez Pinaya --- ...pm_classifier_free_guidance_tutorial.ipynb | 774 ++++++++++++++++++ ..._ddpm_classifier_free_guidance_tutorial.py | 327 ++++++++ 2 files changed, 1101 insertions(+) create mode 100644 tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb create mode 100644 tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py diff --git a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb new file mode 100644 index 00000000..d417ff1d --- /dev/null +++ b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb @@ -0,0 +1,774 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "470cb233", + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright (c) MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, + { + "cell_type": "markdown", + "id": "63d95da6", + "metadata": {}, + "source": [ + "# Classifier-free Guidance\n", + "\n", + "This tutorial illustrates how to use MONAI for training a denoising diffusion probabilistic model (DDPM)[1] to create synthetic 2D images using the classifier-free guidance technique [2] to perform conditioning.\n", + "\n", + "\n", + "[1] - Ho et al. \"Denoising Diffusion Probabilistic Models\" https://arxiv.org/abs/2006.11239\n", + "[2] - Ho and Salimans \"Classifier-Free Diffusion Guidance\" https://arxiv.org/abs/2207.12598\n", + "\n", + "\n", + "\n", + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "75f2d5f3", + "metadata": {}, + "outputs": [], + "source": [ + "!python -c \"import monai\" || pip install -q \"monai-weekly[tqdm]\"\n", + "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "id": "6b766027", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "972ed3f3", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI version: 1.1.dev2239\n", + "Numpy version: 1.23.3\n", + "Pytorch version: 1.8.0+cu111\n", + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 13b24fa92b9d98bd0dc6d5cdcb52504fd09e297b\n", + "MONAI __file__: /media/walter/Storage/Projects/GenerativeModels/venv/lib/python3.8/site-packages/monai/__init__.py\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: 0.4.10\n", + "Nibabel version: 4.0.2\n", + "scikit-image version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Pillow version: 9.2.0\n", + "Tensorboard version: 2.11.0\n", + "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n", + "TorchVision version: 0.9.0+cu111\n", + "tqdm version: 4.64.1\n", + "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n", + "psutil version: 5.9.3\n", + "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n", + "einops version: 0.6.0\n", + "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n", + "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n", + "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "import os\n", + "import shutil\n", + "import tempfile\n", + "import time\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from monai import transforms\n", + "from monai.apps import MedNISTDataset\n", + "from monai.config import print_config\n", + "from monai.data import CacheDataset, DataLoader\n", + "from monai.utils import first, set_determinism\n", + "from torch.cuda.amp import GradScaler, autocast\n", + "from tqdm import tqdm\n", + "\n", + "from generative.inferers import DiffusionInferer\n", + "from generative.networks.nets import DiffusionModelUNet\n", + "from generative.networks.schedulers import DDPMScheduler\n", + "\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "id": "7d4ff515", + "metadata": {}, + "source": [ + "## Setup data directory" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8b4323e7", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/tmp142o2qtd\n" + ] + } + ], + "source": [ + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "print(root_dir)" + ] + }, + { + "cell_type": "markdown", + "id": "99175d50", + "metadata": {}, + "source": [ + "## Set deterministic training for reproducibility" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "34ea510f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], + "source": [ + "set_determinism(42)" + ] + }, + { + "cell_type": "markdown", + "id": "fac55e9d", + "metadata": {}, + "source": [ + "## Setup MedNIST Dataset and training and validation dataloaders\n", + "In this tutorial, we will train our models on the MedNIST dataset available on MONAI\n", + "(https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset).\n", + "Here, we will use the \"Hand\" and \"HeadCT\", where our conditioning variable `class` will specify the modality." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "da1927b0", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-12-10 11:50:40,187 - INFO - Downloaded: /tmp/tmp142o2qtd/MedNIST.tar.gz\n", + "2022-12-10 11:50:40,255 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2022-12-10 11:50:40,256 - INFO - Writing into directory: /tmp/tmp142o2qtd.\n" + ] + } + ], + "source": [ + "train_data = MedNISTDataset(root_dir=root_dir, section=\"training\", download=True, progress=False, seed=0)\n", + "train_datalist = []\n", + "for item in train_data.data:\n", + " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", + " train_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})" + ] + }, + { + "cell_type": "markdown", + "id": "6986f55c", + "metadata": {}, + "source": [ + "Here we use transforms to augment the training dataset, as usual:\n", + "\n", + "1. `LoadImaged` loads the hands images from files.\n", + "1. `EnsureChannelFirstd` ensures the original data to construct \"channel first\" shape.\n", + "1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1].\n", + "1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform.\n", + "\n", + "### Classifier-free guidance during training\n", + "\n", + "In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an \"unconditional\" class.\n", + "Here we specify the \"unconditional\" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad:\n", + "\n", + "`transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))`\n", + "\n", + "Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e3184009", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 15990/15990 [00:08<00:00, 1784.85it/s]\n" + ] + } + ], + "source": [ + "train_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", + " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", + " transforms.RandAffined(\n", + " keys=[\"image\"],\n", + " rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)],\n", + " translate_range=[(-1, 1), (-1, 1)],\n", + " scale_range=[(-0.05, 0.05), (-0.05, 0.05)],\n", + " spatial_size=[64, 64],\n", + " padding_mode=\"zeros\",\n", + " prob=0.5,\n", + " ),\n", + " transforms.RandLambdad(keys=[\"class\"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)),\n", + " transforms.Lambdad(\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + " ),\n", + " ]\n", + ")\n", + "train_ds = CacheDataset(data=train_datalist, transform=train_transforms)\n", + "train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4c11b93f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2022-12-10 11:51:08,067 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n", + "2022-12-10 11:51:08,067 - INFO - File exists: /tmp/tmp142o2qtd/MedNIST.tar.gz, skipped downloading.\n", + "2022-12-10 11:51:08,068 - INFO - Non-empty folder exists in /tmp/tmp142o2qtd/MedNIST, skipped extracting.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Loading dataset: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1977/1977 [00:01<00:00, 1545.02it/s]\n" + ] + } + ], + "source": [ + "val_data = MedNISTDataset(root_dir=root_dir, section=\"validation\", download=True, progress=False, seed=0)\n", + "val_datalist = []\n", + "for item in val_data.data:\n", + " if item[\"class_name\"] in [\"Hand\", \"HeadCT\"]:\n", + " val_datalist.append({\"image\": item[\"image\"], \"class\": 1 if item[\"class_name\"] == \"Hand\" else 2})\n", + "\n", + "\n", + "val_transforms = transforms.Compose(\n", + " [\n", + " transforms.LoadImaged(keys=[\"image\"]),\n", + " transforms.EnsureChannelFirstd(keys=[\"image\"]),\n", + " transforms.ScaleIntensityRanged(keys=[\"image\"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True),\n", + " transforms.Lambdad(\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + " ),\n", + " ]\n", + ")\n", + "val_ds = CacheDataset(data=val_datalist, transform=val_transforms)\n", + "val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True)" + ] + }, + { + "cell_type": "markdown", + "id": "7f108ebb", + "metadata": {}, + "source": [ + "### Visualisation of the training images" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4105a01f", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n", + "/tmp/ipykernel_16221/734547315.py:17: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", + " keys=[\"class\"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "batch shape: (128, 1, 64, 64)\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "check_data = first(train_loader)\n", + "print(f\"batch shape: {check_data['image'].shape}\")\n", + "image_visualisation = torch.cat(\n", + " [check_data[\"image\"][0, 0], check_data[\"image\"][1, 0], check_data[\"image\"][2, 0], check_data[\"image\"][3, 0]], dim=1\n", + ")\n", + "plt.figure(\"training images\", (12, 6))\n", + "plt.imshow(image_visualisation, vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "08428bc6", + "metadata": {}, + "source": [ + "### Define network, scheduler, optimizer, and inferer\n", + "At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using\n", + "the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms\n", + "in the 3rd level, each with 1 attention head (`num_head_channels=64`).\n", + "\n", + "In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use:\n", + "\n", + "`\n", + "with_conditioning=True,\n", + "cross_attention_dim=1,\n", + "`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "bee5913e", + "metadata": { + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "device = torch.device(\"cuda\")\n", + "\n", + "model = DiffusionModelUNet(\n", + " spatial_dims=2,\n", + " in_channels=1,\n", + " out_channels=1,\n", + " num_channels=(64, 64, 64),\n", + " attention_levels=(False, False, True),\n", + " num_res_blocks=1,\n", + " num_head_channels=(0, 0, 64),\n", + " with_conditioning=True,\n", + " cross_attention_dim=1,\n", + ")\n", + "model.to(device)\n", + "\n", + "scheduler = DDPMScheduler(num_train_timesteps=1000)\n", + "\n", + "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", + "\n", + "inferer = DiffusionInferer(scheduler)" + ] + }, + { + "cell_type": "markdown", + "id": "2a4d3ab2", + "metadata": {}, + "source": [ + "### Model training\n", + "Here, we are training our model for 75 epochs (training time: ~50 minutes)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "6c0ed909", + "metadata": { + "jupyter": { + "outputs_hidden": false + }, + "lines_to_next_cell": 0 + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 0: 100%|██████████| 125/125 [00:27<00:00, 4.61it/s, loss=0.723]\n", + "Epoch 1: 100%|██████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.276]\n", + "Epoch 2: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0965]\n", + "Epoch 3: 100%|█████████| 125/125 [00:27<00:00, 4.62it/s, loss=0.0376]\n", + "Epoch 4: 100%|█████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0224]\n", + "Epoch 5: 100%|█████████| 125/125 [00:27<00:00, 4.47it/s, loss=0.0187]\n", + "Epoch 6: 100%|█████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0179]\n", + "Epoch 7: 100%|█████████| 125/125 [00:28<00:00, 4.44it/s, loss=0.0169]\n", + "Epoch 8: 100%|█████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0161]\n", + "Epoch 9: 100%|██████████| 125/125 [00:27<00:00, 4.50it/s, loss=0.016]\n", + "Epoch 10: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0156]\n", + "Epoch 11: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0152]\n", + "Epoch 12: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0152]\n", + "Epoch 13: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0151]\n", + "Epoch 14: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0147]\n", + "Epoch 15: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0151]\n", + "Epoch 16: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0151]\n", + "Epoch 17: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0146]\n", + "Epoch 18: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0144]\n", + "Epoch 19: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0143]\n", + "Epoch 20: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0145]\n", + "Epoch 21: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0143]\n", + "Epoch 22: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0138]\n", + "Epoch 23: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 24: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0134]\n", + "Epoch 25: 100%|████████| 125/125 [00:27<00:00, 4.49it/s, loss=0.0135]\n", + "Epoch 26: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 27: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0136]\n", + "Epoch 28: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0135]\n", + "Epoch 29: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0131]\n", + "Epoch 30: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0128]\n", + "Epoch 31: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0129]\n", + "Epoch 32: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0128]\n", + "Epoch 33: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0135]\n", + "Epoch 34: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0138]\n", + "Epoch 35: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0131]\n", + "Epoch 36: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0132]\n", + "Epoch 37: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", + "Epoch 38: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0124]\n", + "Epoch 39: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0124]\n", + "Epoch 40: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0132]\n", + "Epoch 41: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0128]\n", + "Epoch 42: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0122]\n", + "Epoch 43: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", + "Epoch 44: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0129]\n", + "Epoch 45: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0132]\n", + "Epoch 46: 100%|████████| 125/125 [00:27<00:00, 4.53it/s, loss=0.0125]\n", + "Epoch 47: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0123]\n", + "Epoch 48: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0123]\n", + "Epoch 49: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0125]\n", + "Epoch 50: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0127]\n", + "Epoch 51: 100%|████████| 125/125 [00:27<00:00, 4.54it/s, loss=0.0125]\n", + "Epoch 52: 100%|████████| 125/125 [00:27<00:00, 4.52it/s, loss=0.0124]\n", + "Epoch 53: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", + "Epoch 54: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0123]\n", + "Epoch 55: 100%|████████| 125/125 [00:27<00:00, 4.55it/s, loss=0.0127]\n", + "Epoch 56: 100%|█████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.012]\n", + "Epoch 57: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0126]\n", + "Epoch 58: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", + "Epoch 59: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0126]\n", + "Epoch 60: 100%|████████| 125/125 [00:27<00:00, 4.60it/s, loss=0.0119]\n", + "Epoch 61: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0122]\n", + "Epoch 62: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0119]\n", + "Epoch 63: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0125]\n", + "Epoch 64: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", + "Epoch 65: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0121]\n", + "Epoch 66: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0117]\n", + "Epoch 67: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0121]\n", + "Epoch 68: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0123]\n", + "Epoch 69: 100%|████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.0121]\n", + "Epoch 70: 100%|█████████| 125/125 [00:27<00:00, 4.57it/s, loss=0.012]\n", + "Epoch 71: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0118]\n", + "Epoch 72: 100%|████████| 125/125 [00:27<00:00, 4.59it/s, loss=0.0117]\n", + "Epoch 73: 100%|████████| 125/125 [00:27<00:00, 4.58it/s, loss=0.0119]\n", + "Epoch 74: 100%|████████| 125/125 [00:27<00:00, 4.56it/s, loss=0.0125]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train completed, total time: 2074.1517136096954.\n" + ] + } + ], + "source": [ + "n_epochs = 75\n", + "val_interval = 5\n", + "epoch_loss_list = []\n", + "val_epoch_loss_list = []\n", + "\n", + "scaler = GradScaler()\n", + "total_start = time.time()\n", + "for epoch in range(n_epochs):\n", + " model.train()\n", + " epoch_loss = 0\n", + " progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70)\n", + " progress_bar.set_description(f\"Epoch {epoch}\")\n", + " for step, batch in progress_bar:\n", + " images = batch[\"image\"].to(device)\n", + " classes = batch[\"class\"].to(device)\n", + " optimizer.zero_grad(set_to_none=True)\n", + "\n", + " with autocast(enabled=True):\n", + " # Generate random noise\n", + " noise = torch.randn_like(images).to(device)\n", + "\n", + " # Get model prediction\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", + "\n", + " loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " scaler.scale(loss).backward()\n", + " scaler.step(optimizer)\n", + " scaler.update()\n", + "\n", + " epoch_loss += loss.item()\n", + "\n", + " progress_bar.set_postfix({\"loss\": epoch_loss / (step + 1)})\n", + " epoch_loss_list.append(epoch_loss / (step + 1))\n", + "\n", + " if (epoch + 1) % val_interval == 0:\n", + " model.eval()\n", + " val_epoch_loss = 0\n", + " for step, batch in enumerate(val_loader):\n", + " images = batch[\"image\"].to(device)\n", + " classes = batch[\"class\"].to(device)\n", + " with torch.no_grad():\n", + " with autocast(enabled=True):\n", + " noise = torch.randn_like(images).to(device)\n", + " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes)\n", + " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", + "\n", + " val_epoch_loss += val_loss.item()\n", + " progress_bar.set_postfix({\"val_loss\": val_epoch_loss / (step + 1)})\n", + " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", + "\n", + "total_time = time.time() - total_start\n", + "print(f\"train completed, total time: {total_time}.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a676b3fe", + "metadata": {}, + "source": [ + "### Learning curves" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f8385176", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAArsAAAILCAYAAADoqVT3AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy89olMNAAAACXBIWXMAAA9hAAAPYQGoP6dpAABsNklEQVR4nO3deXwTdf7H8ffk6EFLkVuEolYERESrKKgccixWLu9rVwFBRfFAcFVcF11cZAEX3Z8WVlyVXV1PVNBVAQURUVEsKogKHhSkgCA3PWiTzPz+SJM2toVOm5Jp+3o+Hn00+WZm8s2nQd/55jvfMSzLsgQAAADUQa5YdwAAAACoKYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AAADUWYRdAAAA1FmEXQAAANRZhF0AOMJef/11dejQQR06dFBOTk6suwMAdZon1h0AUHdde+21WrlypTp27Kg33ngj1t1xjEaNGqljx46SJK/XG+PeVE1RUZHeeecdLV++XF9//bV2796tgoICNWjQQG3atFGXLl10wQUXqHv37rHuKoB6zrAsy4p1JwDUTYTdumnhwoX629/+pl9++UVSMLA3b95cDRo00K5du7Rnz57wtmeccYamTp2qtm3bxqq7AOo5RnYBAJX2xBNP6NFHH5UkpaWl6bbbblPv3r2VlJQU3ub777/X008/rTfffFOrVq3SVVddpf/+979KS0uLVbcB1GPM2QUAVMrixYvDQXfAgAF64403NHDgwIigK0nt27fXtGnTNHPmTHk8Hu3atUvjx4+XaZqx6DaAeo6wC8DRsrOz9eCDD+qCCy7QaaedptNOO039+vXTvffeq7Vr1x5y302bNumhhx7SkCFDlJ6erk6dOumss87SH/7wBz3//PPy+/3l7hc6eeztt9/WDz/8oOuuu05du3bVlVdeGd7m2muvVYcOHXT//fdLkpYtW6ZRo0apR48e6ty5s3r06KE777xTGzZsKHP8ik5Qy8nJCbd/9dVXKigo0BNPPKEhQ4bo9NNP16mnnqrBgwcrMzNTRUVF5fbd5/PpmWee0cUXX6z09HR17dpVV111ld58801J0osvvhh+Djt8Pp8mT54sSWrXrp1mzJihuLi4Q+7Tt29fDRs2THFxcTr66KP166+/hh+bMGGCOnTooL59+1a4f2XrlJWVpSuvvFLp6ekaP368/vnPf4Yf37p16yH7OGLECHXo0EEZGRllHluxYoXuuOMO9enTR507d1bXrl01ZMgQPfzwwxGv5bd++eUXTZs2TRdeeKFOP/10de7cWT179tQVV1yhf/3rX9q9e/ch+wQgupjGAMCx5s+fr4kTJ6qoqEiGYahly5ayLEs5OTnKycnR/PnzNX78eN1www1l9l2yZInuuOOOcCg8+uijlZiYqG3btikrK0tZWVl655139PTTTyshIaHc58/NzdX111+vPXv2qE2bNmrYsGG52z399NOaPn26kpOTdfTRR8s0Tf3666966623tGzZMs2bN0+pqam2XntBQYGGDx+u1atXq1WrVjr66KOVk5OjH374QT/88IPWrl2rJ554ImKfwsJCjRo1Sp9//rkkqUGDBmrevLl+/PFH3XXXXfr88891wgkn2OpHyMKFC7Vt2zZJ0h133HHYoBty++2365ZbblFycnKVnvdwtm7dqokTJ8o0TbVu3VqJiYkaPHiw/vGPf0iSFi1apOuuu67cfXft2qWVK1dKki688MJwu2VZ+utf/6rnn39eUnBOcqtWrbR//359//33+v777/XSSy8pMzNTZ599dsQxV69erZEjRyo3N1eS1KJFCx199NHavXu3Vq9erdWrV2vOnDl67rnnqvy3AGAPI7sAHGnVqlW67777VFRUpIyMDC1btkzLli3Thx9+qBUrVujCCy+UaZr6+9//riVLlkTse+DAAd1zzz0qKipS+/bt9d5772nZsmVauHChVq1apXHjxkmSsrKy9NRTT1XYh7lz56pVq1b64IMP9M4775S77Zo1a/R///d/mjRpkj799FO9/fbb+uSTT/Twww+H+/Kvf/3L9ut/+OGHlZeXp3nz5oWff8WKFfrd734nSVq6dKm+/PLLiH2eeOKJcNC97rrr9Omnn2rBggVasWKF7r33Xr366qtasGCB7b5I0vLlyyVJDRs2VJ8+fSq9X2JiYo0FXUl65pln1L17d3300Ud666239NBDDyk1NVXp6emSgmG3IgsXLlQgEJBhGBo6dGi4/V//+peef/55GYahO+64QytXrtR7772nzz77TG+88YZOOeUU5ebm6pZbbtH27dsjjnn//fcrNzdXJ598st577z0tX75c77zzjj799FPNnTtXxx9/vHbt2qUHHnigZgoCoAzCLgBHmjFjhvx+v8444ww9+uijatmyZfixJk2aaPr06Tr33HMlKTyKF/Lxxx+HR9buueeeiJUAPB6PbrrpJp155pmSpHfeeafCPnzzzTeaPn26mjRpUuE23333nW6//XZdddVVEcuIDR06VGeddZYk6bPPPqvkqy7x008/6cknn1SnTp3CbUlJSZowYUL4funjBgIBvfDCC5KCKyBMmDBB8fHxkoIjkyNGjNAtt9yir776ynZfpGCol6RTTjlFHo9zvhTcsGGDpk6dWmbe8JAhQyRJX331VXjViN8KBf+uXbuqdevWkqT9+/eHR8xvvPFG3XzzzWrQoEF4n44dO+qZZ55RkyZNlJeXF/FBZu/evVq3bp0kafTo0WVWoOjSpYv+9re/6cwzz1SbNm0qnIoCILoIuwAcZ+vWrVq1apWk4JxKl6v8/1T94Q9/kBQ8+3/Tpk3h9oyMDK1du1Yffvhhheu8nnLKKZKkzZs3V9iPzp07H3bJLK/Xq6uvvrrcx0Jr6VYUtg6lf//+4QBWWunpFKWPu3btWu3du1eSdPHFF5d7zOuuuy4iuNkRmmd69NFHV2n/mtKrV69yp5dccMEF8ng8siyr3NHd7du3h99jpacwvP/++8rLy5PL5dKIESPKfc6UlJTwSPC7774bbi+9kueOHTvK3Tc9PV3//e9/NXXq1EpPBQFQPYRdAI5T+uv5E088scLtTj311PDtb775JuIxj8ejli1bVjgKGQp9hxpdq8ycyrS0tDKjiiGhr+8PHjx42OP8ViiMH+q4hYWF4bbs7Ozw7ZNPPrnc/ZKSknTGGWfY7osk5eXlSQpOS3CSiv5GTZo0UY8ePSSVP5Vh4cKFMk1T8fHxESenffHFF5Kkpk2bHnJEP/Te2759u3bt2iVJaty4sU466SRJ0tSpUzVjxoxyT1AEcGQ557soAChW+kz38s6SL89v5076/X699dZbWrRokTZs2KDdu3dr//79tvrRuHHjw26TkpJS4WMVjUhXRkUnw5U+bumRxFDgkqRmzZpVuO/xxx8fnn9rR3Jysvbu3asDBw7Y3rcmHSqQDhkyRB988IG++OIL7dixQy1atAg/FprC0Ldv34ha79y5U1LwPVjZFSt++eUXNW3aVFJwrvXIkSO1Y8cOPfnkk3ryySd1zDHHqFu3burRo4f69OlT4YcjADWDsAvAcQoKCsK327dvX6nQWPrr+QMHDuj666+PmJ/avHlztWvXLjzSu3PnznCwqcwxK2IYxmG3qQq7xy09enyor8erOo2hRYsW2rt3b8R0ESc41Ehzv3791KBBA+Xn5+vdd9/VNddcIyk4TSb03rjooosi9gm99+Lj43X88cfb7s+JJ56ohQsX6qWXXtKrr76qDRs2aOvWrZo3b57mzZunpKQkjRw5UmPGjKnWhyEAlUfYBeA4pQPZrFmzbC/bNXXq1HCYGTZsmIYPH642bdpEbPP4448rMzOz2n11itInx1W0frBUtSkVUvBr+++//17ffvutcnNza3SFhZDqXs0+MTFR/fv315tvvqmFCxeGw+6CBQtkWVbEVIeQ0HuvSZMmVb7EdVJSkkaNGqVRo0Zp8+bN+vjjj/XJJ59o+fLlysvL0+OPP66tW7dqypQp1Xp9ACqHj5UAHKf0SVB2T+4KBAJ66623JEk9e/bUfffdVyboStK+ffuq10mHadSoUfj2oS5aUHpurx29e/eWFLy4xGuvvVbp/UzT1OTJk8usAhEauT5UoI3GlInQqgyrVq0Kj+SHVuAYNGhQmTndoffer7/+GpUrvqWmpuqqq67SY489pg8//FDnn3++JOm1115jPi9whBB2AThOly5dwrcPtVSWZVllRjF3794dHr0MLS/2W6ZpVmneqpOVXjXihx9+KHeb/Px8ZWVlVen4ffv2DX9omDVr1iGvIFbas88+q+eee05XXnmlPv7443B7aKpFaIm48nz99ddV6mtp55xzjpo2bSrTNPXBBx8oJycnfOW9305hkEpODPT7/Ye8Qp/P56vwsYpG1hs2bKi//OUv4fvff/99JV4BgOoi7AJwnFatWoVXDXjhhRfCKwH81htvvKGzzz5bd911lwKBgCRFXA2tohHOp59+OmLuaelVDWqrU089NRwg33777XK3+c9//lNhLQ/H7XZr0qRJMgxDe/fu1ejRow972dv58+dr+vTpkqRzzz1X55xzTvix0Ajq/v37tWXLljL7btmyRYsXL65SX0vzeDwaOHCgJOnDDz8MH/OEE05Q586dy2zfv3//8FSGJ598ssLjPvDAA+rTp0/EOrv//ve/1atXL914440V7lc6CHOiGnBkEHYBONKdd94pl8ulrVu36oYbbogIp0VFRXrllVf0wAMPaP/+/UpOTpbb7ZYUHD1r3769pOBXxaWXJNu5c6ceeughZWZm6pZbbgm3V3W000kaNGigQYMGSQpeKvnpp58OfwDw+Xz697//rccff7zC0e7K6NGjhyZMmCDDMPTNN99o8ODBev7557Vnz56I7b777jvdcccdmjBhggKBgDp16qRHHnkk4qS70AU3JGnatGkRJyWuW7dON954Y/jvWF2hqQyffPJJ+Gp7pdfWLS05OVk333yzJOm9997TX/7yl4gpL7t379bf/vY3vfbaa9q6dWvEFJn27dtr+/bt+vjjjzVp0qQya+1u2bIlfFGQJk2aVOtvAaDyOEENQI3Lzs6uMFyUdtVVV4Uv0HDGGWdoypQpmjhxolatWqUBAwaodevW8nq9+uWXX8JTFc455xzdfffdEce58847dfPNN+vAgQO69NJLdcwxx8gwDG3dulVut1vTp09Xenq6Zs+eLZ/Pp5tuukmpqanKzMxUWlpa9AtwhNx5551auXKltmzZounTp2vWrFk6+uij9csvvyg3N1e33XabLMsKX1K4KkaMGKHU1FQ99NBD2rJlix588EFNnjxZzZs3V0pKinbu3BkOv263W5dddpnuueeeMqOYZ5xxhnr37q1ly5Zp0aJF+vDDD9W6dWsVFBRoy5YtOuWUU/THP/5Rw4YNq1ZNpOCod9u2bfXzzz9r5cqVZS4P/FvXX3+9tmzZopdeekkvvviiXn31VbVu3VpFRUXasWNHeHR29OjRuuCCC8L7nXPOObrpppv0xBNP6IUXXtBLL70Ursv+/fvDy+M1aNBAM2bMiPgWAkDNIewCqHGFhYXhy6geym+XArv44ot1xhln6D//+Y9WrFihbdu2yefz6aijjlL37t114YUX6oILLiizTNd5552nZ555Rk8++aS+/vpr/fLLL2rSpImGDBmikSNHhq9s9uCDDyozM1M7duyQZVmOu2CCXc2bN9drr72mWbNmaenSpdq+fbv27dun0047Tdddd5169Oihxx57TFL1lkzr16+fevbsqXfeeUcffvihvvnmG+3atUu7du1ScnKy0tPT1b17d1100UU67rjjKjxOZmamZs+erYULF2rz5s3hkdKxY8fquuuu048//ljlPv7WkCFDNHPmTEnBUeVWrVpVuK3L5dKkSZM0cOBAvfzyy/ryyy+1detWGYYRnmJz9dVX67TTTiuz77hx43Teeedp/vz5WrFihbZv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9BctWPCWJOn222+SJM2d+6a2bduq22+/Sdddd4NGjRodPubq1V/pueee0TffrFVBQb4aN26iM8/sppEjb9TRR7eK6HtyckNNnTpDmZmP6quvvlBRkU/HH5+m0aNv0emnd61EtaOPsOsAXo9bEieoAQDgVAMHDtG//jVLb775erlh9403XldCQoIGDhyqSZP+rI8+WqZhw0bqrLO66+DBg3r55Rc0bdpkFRUV2ZrmsGPHdt1++81KSEjQ2LF/1LHHttXmzdmaMOGPKiryKTGxZNtfftmmMWNuUHJysm67bZxatTpGmzf/rCeeyNTtt9+k//znRbVo0VIjR94oj8er//1vnv74x3vVseNJatasubZt21rm+T/99BPdc884nXhiB40ff7eaN2+hjRs36KmnntBnn63Qv//9gho3bhLePj8/T3/841hlZAzUpZdeqS1bNisz8x/605/+qJdfnq9GjY6yVfdoIOw6QGjOLheVAADUVl/sWKO3NryrwkCh7X1dLkOmadVAr0rEu+M1OG2ATm/RpUr7Jycna8CAC/TGG6/r669X65RTTg0/9t133+j779dpyJCLFB8fJ4/Hoyuu+L1uuOHm8DYnn3yKBg3qpwUL3rIVdufNe1X5+Xm6774H1Lt3X3k8LvXvf548nnhNnjxJjRo1Cm+7efMmdelymi699Ap163a2JOmUU05VQUGBHn10upYvX6ZLL71CrVodo2bNmkmS2rY9Vh07dqrw+TMz/6GEhAQ98sjjSkkJPtdpp52uo45qrD//+R69/PILuummW8Pbb926Rffd9xddcMFgSVJ6+hnatGmTXnzxOWVlrVS/fgMq/dqjhbDrAKFpDAHTUsA05XYxlRoAULss3rRM2/N3xLobh7T452VVDrtS8MSzN954XfPnvxYRdkMnpl1yyRWKj0/QX/86tcy+ycnJatq0mX75ZZut5/z669UyDEPdup0T0d6nTz899NCDEW1nntldZ57Zvcwxjj32OEnS9u32nnvHju3auHGDevXqEw66Ieee20tut1urVn0e0W4Yhvr27R/R1qZNqiRp3759tp4/Wgi7DlD6whJ+vyV3XAw7AwBAFfQ/trfjR3b7t+1drWOkpbXTaaedrqVLl2js2DuVktJIeXm5WrLkXZ1yyqnhObHff79Or776slat+lx79uxWUVFR+BilR2IrY9eunUpKSlJCQkJEe1JSsho0aBDRZlmW3nnnf1q48G1lZ/+k/fv3yzRLvjW2W+MdO4IfXlq0aFnmMa/Xq6OOaqydO3+NaG/YMEXx8Qlltg32LzbfYBN2HSDiKmoBU/Fyx7A3AADYd3qLLlUaNfV4XGrcOEl79uTViul8l1xyhb76aoLeeed/uuqqa7Rw4TsqKCgIT0348ccfdNNNoxQfH68RI0apY8dO4VD6xz+Old/vs/V81iHy6W/D65NPztJzz83Rqaema9y4e9SqVSt5vV6tW/edpk2bbO+FKjhKW9yLSmwTum/7aWocYdcBQnN2JVZkAADAyXr1Ok/NmjXX22+/qauuukZvv/2mmjZtqvPO6ydJWrjwbRUVFeqBByard+8+4f38fr8OHNivxNJnlFVC48aNtWXLZhUWFio+Pj7cvnfvHhUU5EeMFL/55us66qjG+sc/ZoVHU6VgAK+Ko48+WpK0ffsvZR4rLCzU3r171KnTyVU69pHE5FAHiBjZ9QcOsSUAAIglj8ejCy+8RNnZG/TBB0v0/ffrNHToJfJ4guOHobVrGzduHLHfyy8/r6KiojJr2x5Op06dZVmWVqz4KKL9/feXlNk2EAgoOTk5Iuj6fD7NnftiRN+kkhHZQ/WnadNmat++o7KyPtfevXsjHvvoow8VCATKzCV2IsKuA3g8jOwCAFBbDB16sTwej6ZPnxIOvyFnntlNkvTPfz6mzz//TJ9//pn+9rcHtWrV5+ra9Szl5ubq3XcX6NdfK3cy30UXXaq4uHj9/e9T9fbbb2rVqizNnj1bL7/8QpmTxrp27aacnM168slZWrPmKy1evEjXXz9M/fufL0nKyvpMq1d/Kb/fr+bNW0iS3nxznpYte7/c0VtJuu22cfL5ivTHP96upUsX66uvvtDcuS/p73//m9q0SdVll11lu35HGmHXAX47ZxcAADhX06bNdN55/bR//z716tUn4opj55zTQ3fc8Uft2bNH99wzTlOn/lWNGjXS3/72d1177XVq2rSZpk9/SFlZKyv1XKmpbfXoo5lq0yZVM2ZM0913j9eqVav097//Q0cddVTEtnfeOUG/+12G3nxznu688za9/PILGjnyBv3+98N02WVXatu2rbr//gk6ePCg+vUboK5dz9JHHy3T3/721wpXiUhPP0OZmU+qUaOjNG3aQxo79ma99NJ/9bvfna9//vMZJScnV7mOR4phWYea+lw//frrgage73CT719a8oPe/XyzJOnea07XiW2Oiurz1za17WSFWKNe9lAv+6iZPdTLHuplD/Uq0bx5w0ptx8iuA3iZxgAAAFAjCLsOQNgFAACoGYRdB4jzlKyrS9gFAACIHsKuA3CCGgAAQM0g7DoA0xgAAABqBmHXAbiCGgAAQM0g7DoAI7sAAAA1g7DrAB4uFwwAAFAjCLsOwAlqAAAANYOw6wDM2QUAAKgZhF0HYM4uAABAzSDsOgBhFwAAoGYQdh2AObsAAAA1g7DrAMzZBQAAqBmEXQdgGgMAAEDNIOw6AGEXAACgZhB2HYA5uwAAADWDsOsAbpdLLsOQxMguAABANBF2HSI0uusn7AIAAEQNYdchQmGXkV0AAIDocXTYnTt3rgYOHKjOnTurZ8+emjZtmnw+3yH3+fTTT3XllVeqS5cu6tGjhyZPnqyioqIj1OOqC4dd5uwCAABEjWPD7vz58zVx4kRdccUVWrBggR544AHNnz9fkydPrnCf1atX6/rrr9c555yjt99+W3/961/1v//9T3/961+PYM+rJrTWLiO7AAAA0eOJdQcqkpmZqUGDBmnEiBGSpNTUVO3cuVOTJk3SmDFj1LJlyzL7PPLII+rVq5fGjh0b3iczM1N+v/9Idr1KvF7CLgAAQLQ5cmR348aN2rx5s3r37h3R3qtXL5mmqeXLl5fZZ+/evVq5cqUGDx4c0X7mmWfq7LPPrtH+RgMjuwAAANHnyLCbnZ0tSWrbtm1Ee6tWreT1erVhw4Yy+6xfv16maaphw4YaP368zj33XPXp00f/+Mc/DjvP1wlCc3ZNy5KfebsAAABR4chpDLm5uZKkpKSkiHbDMJSUlBR+vLRdu3ZJkiZPnqzrrrtON9xwg1auXKmHH35Y+/fv1/3331/p53e5DLlcRjVeQSR38aht6Hd54rzu8G1LksfjyM8hR0Rl6oUS1Mse6mUfNbOHetlDveyhXvY5MuxWRWj0duDAgbrqqqskSSeddJK2bdum5557TrfeequaNGlSqWM1aZIkw4he2A1JSUms8LEGid7w7aTkBDVKjo/689c2h6oXyqJe9lAv+6iZPdTLHuplD/WqPEeG3ZSUFEkqM4JrWZby8vLCj5fWsGFDSVLnzp0j2rt27ao5c+bohx9+ULdu3Sr1/Lt350V9ZDclJVH79xcoUNEUBcsK3/x1Z65Mn/NPqqsplaoXwqiXPdTLPmpmD/Wyh3rZQ71KNG6cdPiN5NCwm5aWJknatGmT0tPTw+05OTny+Xxq165dmX2OO+44SdK+ffsi2q3iEJmcnFzp5zdNS6ZpHX5DmwIBs8IrpHlKheuDhX6upKZD1wtlUS97qJd91Mwe6mUP9bKHelWeIyd8pKamKi0tTUuXLo1oX7JkiTwej3r27Flmn7S0NKWmpuq9996LaM/KylJ8fHw4DDuVt9QcXVZkAAAAiA5Hhl1JGjt2rBYtWqQ5c+Zoy5YtWrx4sWbOnKlhw4apadOmWrNmjTIyMpSVlRXe54477tD777+vxx57TJs3b9bcuXP14osvavjw4WVOdnMar7vkBDWuogYAABAdjpzGIEkZGRmaPn26Zs+erRkzZqhZs2YaPny4xowZI0kqKChQdna28vPzw/sMHjxYlmVp9uzZevLJJ9W0aVPdeuutuv7662P1MiqNkV0AAIDoc2zYlaShQ4dq6NCh5T7WrVs3rV+/vkz7kCFDNGTIkJruWtR5CLsAAABR59hpDPUNI7sAAADRR9h1CG+pxaGZswsAABAdhF2HiBzZDcSwJwAAAHUHYdchmMYAAAAQfYRdhyDsAgAARB9h1yGYswsAABB9hF2HYGQXAAAg+gi7DkHYBQAAiD7CrkMQdgEAAKKPsOsQcR53+DZzdgEAAKKDsOsQEZcL9hF2AQAAooGw6xAR0xgY2QUAAIgKwq5DRCw9xpxdAACAqCDsOgQnqAEAAEQfYdchIsNuIIY9AQAAqDsIuw7BFdQAAACij7DrEC6XIbfLkMQ0BgAAgGgh7DpIaCoDYRcAACA6CLsOQtgFAACILsKug4TDLnN2AQAAooKw6yChk9T8jOwCAABEBWHXQZjGAAAAEF2EXQcpHXYty4pxbwAAAGo/wq6DhKYxWJICJmEXAACgugi7DsIlgwEAAKKLsOsgXo87fJuwCwAAUH2EXQfxMLILAAAQVYRdBwnN2ZVYaxcAACAaCLsOEudlZBcAACCaCLsOUnpkt8gfiGFPAAAA6gbCroOUXo2Bq6gBAABUH2HXQVh6DAAAILoIuw5C2AUAAIguwq6DsBoDAABAdBF2HYSRXQAAgOgi7DoIF5UAAACILsKugzCyCwAAEF2EXQfxut3h28zZBQAAqD7CroMwsgsAABBdhF0HIewCAABEF2HXQQi7AAAA0eWJdQcOZe7cuZozZ45+/vlnNW7cWIMHD9b48ePl9XrLbJuTk6N+/fqVe5w//OEPuv/++2u6u9XGOrsAAADR5diwO3/+fE2cOFETJkxQv379tH79ek2cOFH5+fmaNGlShfs9/vjjSk9Pj2hLTEys6e5GReTIbiCGPQEAAKgbHBt2MzMzNWjQII0YMUKSlJqaqp07d2rSpEkaM2aMWrZsWe5+jRo1UvPmzY9gT6OHaQwAAADR5cg5uxs3btTmzZvVu3fviPZevXrJNE0tX748Rj2rWYRdAACA6HJk2M3OzpYktW3bNqK9VatW8nq92rBhQyy6VeMiwi5zdgEAAKrNkdMYcnNzJUlJSUkR7YZhKCkpKfx4ed5++23NmDFDP//8s4466ihdcsklGjFihOLi4ir9/C6XIZfLqFrny+EuPvHM7T70Z4vE+JI/RyBgRVw+uD6pbL0QRL3soV72UTN7qJc91Mse6mWfI8NuVbjdbjVr1kwHDx7U3XffrQYNGuijjz7SY489po0bN2rKlCmVPlaTJkkyjOiF3ZCUlEOfKGeaVsltSY0bJ1W8cT1wuHohEvWyh3rZR83soV72UC97qFflOTLspqSkSFKZEVzLspSXlxd+vLRWrVrp448/jmjr1KmT8vLy9MQTT+jWW2/VMcccU6nn3707L+ojuykpidq/v0CBw0xP8Lpd8gVMFRz0ac+evKj1oTaxUy9QL7uol33UzB7qZQ/1sod6lajsoKAjw25aWpokadOmTRHLiOXk5Mjn86ldu3aVPtZJJ50kSdq+fXulw65pWhGjrNESCJjyH+bEM48nGHaLfIfftq6rTL1QgnrZQ73so2b2UC97qJc91KvyHDnhIzU1VWlpaVq6dGlE+5IlS+TxeNSzZ88y+yxevFgTJkyQ3++PaP/666/lcrnKnOzmVKGT1FiNAQAAoPocGXYlaezYsVq0aJHmzJmjLVu2aPHixZo5c6aGDRumpk2bas2aNcrIyFBWVpYkqWXLlnrrrbc0btw4rV27Vps2bdJ///tfPfvss7rsssvUtGnTGL+iygldRY3VGAAAAKrPkdMYJCkjI0PTp0/X7NmzNWPGDDVr1kzDhw/XmDFjJEkFBQXKzs5Wfn6+JOmUU07RnDlzNGvWLF1//fXKzc1V69atdeutt2rUqFGxfCm2MLILAAAQPY4Nu5I0dOhQDR06tNzHunXrpvXr10e0nXnmmZozZ86R6FqNIewCAABEj2OnMdRXobDrD5iyrOifJAcAAFCfEHYdxltqkWg/83YBAACqhbDrMBGXDGYqAwAAQLUQdh2GsAsAABA9hF2HIewCAABED2HXYUrP2WWtXQAAgOoh7DoMI7sAAADRQ9h1GA9hFwAAIGoIuw7DyC4AAED0EHYdhjm7AAAA0UPYdRhGdgEAAKKHsOswXo87fJuwCwAAUD2EXYdhZBcAACB6CLsOEzFn1x+IYU8AAABqP8Kuw8R5GdkFAACIFsKuw7AaAwAAQPQQdh2GObsAAADRQ9h1GMIuAABA9BB2HYbLBQMAAEQPYddhmLMLAAAQPYRdh2EaAwAAQPQQdh2GsAsAABA9hF2H4XLBAAAA0UPYdRjm7AIAAEQPYddhmMYAAAAQPYRdh/G4jfBtwi4AAED1EHYdxjCM8OguYRcAAKB6CLsOFJq3y5xdAACA6iHsOlBoZNfvD8S4JwAAALUbYdeBmMYAAAAQHYRdBwqHXaYxAAAAVAth14HCc3YZ2QUAAKgWwq4DhefsBiyZphXj3gAAANRehF0HiriwBFMZAAAAqoyw60Bejzt8m6kMAAAAVUfYdSAuGQwAABAdhF0HYhoDAABAdBB2HSi0GoPEyC4AAEB1EHYdqPTIrp+wCwAAUGWEXQdizi4AAEB0EHYdKDLsBmLYEwAAgNrN0WF37ty5GjhwoDp37qyePXtq2rRp8vl8ldp37969Ovfcc9W3b98a7mX0RczZ5QQ1AACAKnNs2J0/f74mTpyoK664QgsWLNADDzyg+fPna/LkyZXaf8qUKdq7d2/NdrKGMI0BAAAgOhwbdjMzMzVo0CCNGDFCqamp6t+/v8aOHatXXnlF27dvP+S+H374oRYtWqShQ4ceod5Gl4ewCwAAEBWODLsbN27U5s2b1bt374j2Xr16yTRNLV++vMJ9c3Nz9cADD+i2227TMcccU9NdrRGM7AIAAERHjYbdPXv2yO/3294vOztbktS2bduI9latWsnr9WrDhg0V7jtjxgw1btxY1113ne3ndQrm7AIAAESHp7oHWLZsmebOnavMzMxw2yeffKL77rtPv/zyi5KSknTLLbfYCp+5ubmSpKSkpIh2wzCUlJQUfvy3srKyNHfuXL3yyityu91VeDVBLpchl8uo8v6/5S4Or2535T5bJMSX/FkCphUxraE+sFuv+o562UO97KNm9lAve6iXPdTLvmqF3aysLN1yyy0yDEOmacrlcmnHjh265ZZbVFBQoE6dOiknJ0fTp0/Xcccdpz59+kSr32UUFhbqvvvu04gRI9SpU6dqHatJkyQZRvTCbkhKSmKltmvcqGQ7j9ejxo2TDrF13VXZeiGIetlDveyjZvZQL3uolz3Uq/KqFXafffZZJSYm6vnnn5fLFfyE8fLLL6ugoEC33367xowZo7179+qiiy7SSy+9VOmwm5KSIkllRnAty1JeXl748dIef/xxeTwe3XbbbdV5SZKk3bvzoj6ym5KSqP37CxSoxLSEwoMly6vtP3BQe/bkRa0vtYHdetV31Mse6mUfNbOHetlDveyhXiUqOxhYrbC7Zs0aDRgwQO3btw+3LV26VAkJCRo2bJgk6aijjlL//v21YMGCSh83LS1NkrRp0yalp6eH23NycuTz+dSuXbsy+7zzzjvatm1bxPamacqyLHXq1EljxozRrbfeWqnnN01LpmlVur+VFQiYlbr8b+mcXegL1NtLBle2XgiiXvZQL/uomT3Uyx7qZQ/1qrxqhd1du3bp2GOPDd/ft2+fvvvuO51zzjlKTk4Ot7do0UL79u2r9HFTU1OVlpampUuX6qKLLgq3L1myRB6PRz179iyzz9NPP13mghMvvPCClixZoqefflpNmza18cpiy+spmW/MagwAAABVV63ZzXFxcRFTDT7++GNZlqVzzz03Yrvc3NwyJ5sdztixY7Vo0SLNmTNHW7Zs0eLFizVz5kwNGzZMTZs21Zo1a5SRkaGsrCxJ0vHHH6/27dtH/DRt2lRerzd8u7bgcsEAAADRUa2we8IJJ2jp0qXy+/0yTVPPPvusDMMoMzd35cqVat26ta1jZ2RkaPr06Xr11Vd1/vnna/LkyRo+fLjuuusuSVJBQYGys7OVn59fnZfgSKyzCwAAEB3VmsYwePBgTZkyRQMGDJAkbdu2Tb169dLxxx8vScrPz9fjjz+u1atXV+nEsaFDh1Z4FbRu3bpp/fr1h9z/tttui8oJa0daHGEXAAAgKqoVdq+55hr9+OOPev311+X3+3XKKado6tSp4cd37dqlOXPm6KSTTqrVF3k40rhcMAAAQHRUK+y6XC49+OCD+tOf/qS8vLwy82JTU1N133336ZJLLlFiIuvBVRZXUAMAAIiOal9BTZISEhKUkJBQ7mPXXnttNJ6iXmHOLgAAQHRU+1pz3377raZMmRLRtm7dOl1zzTVKT0/XoEGDtHDhwuo+Tb3idhkKXcCNsAsAAFB11Qq769ev1zXXXKMXXnhBphkMZfv379fIkSOVlZWluLg4bdiwQePHj9eqVaui0uH6wDCM8Ogu0xgAAACqrlph95lnnpHf79esWbPClwueO3eudu/erd///vf67LPPtGjRIqWkpOjZZ5+NSofri9C8XUZ2AQAAqq5aYffzzz/XgAED1KtXr3Dbe++9J4/HE740b9u2bTVgwAB9+eWX1etpPRMe2SXsAgAAVFm1wu7OnTvVrl278P28vDytXbtWp556qpo0aRJub926tXbv3l2dp6p3CLsAAADVV62w63a7VVhYGL6/cuVK+f3+MpcLLigoYOkxm7wetyTm7AIAAFRHtcLuscceqxUrVoTvv/jiizIMQ+edd17Edl9//bVatmxZnaeqd0Jzdv2M7AIAAFRZtdbZHTBggB577DFdddVVcrlc+vLLL3XaaaepU6dOkqRAIKAXX3xRK1as0MiRI6PS4foiNI0hYFoKmKbcrmqvEgcAAFDvVCvsjho1SqtWrdLHH38sSWrVqpWmT58efnzjxo2aPHmyjjnmGMKuTaUvLOH3W3LHxbAzAAAAtVS1wm58fLyefvppbdy4Ufv371fHjh0VF1eSytLS0jRixAhdd911ESes4fAirqIWMBUvdwx7AwAAUDtF5XLBxx13XLnthmFowoQJ0XiKeic0Z1diRQYAAICqikrY/eWXX7Rw4UJ9++232rNnjwzDUNOmTdW5c2cNHDhQjRs3jsbT1CsRI7v+QAx7AgAAUHtVO+z++9//1owZM+T3+2VZVsRj8+fP14wZM/Tggw9q8ODB1X2qesXjYWQXAACguqoVdpctW6apU6cqMTFRF154obp06aImTZrINE3t3r1bq1at0qJFizRhwgS1bdtWXbp0iVa/67zSI7tFhF0AAIAqqVbYfe6559SoUSO98sorOvbYY8s8ftVVV+mGG27Q1VdfraeeekqPPfZYdZ6uXmHOLgAAQPVVa/HWtWvX6vzzzy836Ia0b99e559/vr744ovqPFW989vVGAAAAGBftcJubm6ujj766MNu16ZNG+3du7c6T1XvxDFnFwAAoNqqFXZTUlK0efPmw263detWpaSkVOep6h2vp2RdXS4ZDAAAUDXVCrunnnqq3n33Xa1fv77CbdatW6cFCxbotNNOq85T1TteRnYBAACqrVonqF133XX64IMPdPnll2vQoEFKT08PXylt165dysrK0qJFixQIBDRq1KiodLi+YM4uAABA9VUr7J511ll68MEH9dBDD2nevHmaP39+xOOWZSkxMVGTJ0/WGWecUZ2nqndYjQEAAKD6qn1Ricsvv1x9+vTRO++8o7Vr12rXrl3hK6idcsopGjRoEFdQqwKmMQAAAFRfVC4X3KxZMw0bNqzCx5csWaJ58+YpMzMzGk9XL3i4XDAAAEC1VesEtcratGmTlixZciSeqs5gzi4AAED1HZGwC/uYswsAAFB9hF2HYs4uAABA9RF2HYqwCwAAUH2EXYdizi4AAED1EXYdijm7AAAA1UfYdSimMQAAAFSf7XV2zz77bNtPcvDgQdv71HeEXQAAgOqzHXb37NlTpScyDKNK+9VXzNkFAACoPtthl4tDHBlul0suw5BpWfL5CLsAAABVYTvstm7duib6gXJ4PS4V+gKM7AIAAFQRJ6g5WGgqg88fiHFPAAAAaifCroOVhF1GdgEAAKqCsOtghF0AAIDqIew6WDjsMmcXAACgSgi7Dha6iprPb8qyrBj3BgAAoPZxdNidO3euBg4cqM6dO6tnz56aNm2afD5fhdvv2bNHkydPVt++fdW5c2edd955mjZtWq29qEVoZNeypIBJ2AUAALDL9tJjR8r8+fM1ceJETZgwQf369dP69es1ceJE5efna9KkSWW2N01T119/vfLz8/XQQw+pTZs2ysrK0v33369ff/1Vf//732PwKqrnt1dR87gd/dkEAADAcRwbdjMzMzVo0CCNGDFCkpSamqqdO3dq0qRJGjNmjFq2bBmx/XfffadNmzZp1qxZOuuss8L7ZGVlacGCBbIsq9Zdxc3rjryKWmIM+wIAAFAbOXKocOPGjdq8ebN69+4d0d6rVy+Zpqnly5eX2efkk09WVlZWOOiGuFwuud3uWhd0pciRXT8rMgAAANjmyJHd7OxsSVLbtm0j2lu1aiWv16sNGzYc9hh+v1/vv/++3nrrLd122222nt/lMuRyRS8cu4tHaN02pyHEed3h26Ykj8eRn02irqr1qq+olz3U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9hj00jleffVVXXjhhUpPT1efPn305z//Wbt27Qo//sMPP+j6669Xenq60tPTdcMNN+inn36KYY9jxzRNPfPMMxo8eLC6dOmibt26aezYsdqyZUt4G95jFRs5cqQ6dOignJyccBv1KtG3b1916NChzM/gwYPD21CvsnJycnTrrbfq9NNP15lnnqkxY8Zo69at4cepWVBOTk6576/Qz+uvvy6JelWahRozb948q0OHDtacOXOsn3/+2Xrvvfes7t27W/fff3+suxZze/bssUaPHm316NHDOv3006077rgj4vGffvrJ6tKli3XPPfdYP/30k5WVlWVdfvnl1uDBg62ioqIY9Tq2nnnmGatjx47W008/bW3cuNFatmyZ1atXL+vqq6+2TNO0du/ebXXv3t0aNWqUtW7dOuvrr7+2Ro8ebZ177rnWvn37Yt39I27KlCnWaaedZs2fP9/6+eefrY8++sjq16+f1bdvX6uwsJD32CHMnTvX6tSpk9W+fXtr8+bNlmXxb/K3+vTpY02dOtXasWNHxM/u3bsty6Je5dm3b5/Vp08f66abbrK+//57a/Xq1dbFF19sZWRkWIFAgJqV4vf7y7y3duzYYb3xxhtW586drU2bNlEvGwi7Nahfv37W+PHjI9pefPFFq2PHjtYvv/wSo145w3PPPWdde+211s6dO60+ffqUCbsTJkywevfubfl8vnDbTz/9ZLVv39763//+d6S7G3OmaVrnnnuuNWHChIj2l19+2Wrfvr313XffWY8//rh16qmnWnv37g0/vnfvXqtLly7WE088caS7HFM+n88677zzrMzMzIj2+fPnW+3bt7fWrFnDe6wC27dvt7p27WpNmjQpIuxSr0h9+vSxHnvssQofp15lZWZmWueee65VUFAQbsvOzrYWLFhgHTx4kJodRlFRkZWRkWE9/PDDlmXxHrODaQw1ZOPGjdq8ebN69+4d0d6rVy+Zpqnly5fHqGfO0Lt3b82ZM0dNmzYt9/GPPvpIPXr0kMfjCbelpaWpTZs2+vDDD49UNx3DMAy99dZb+tOf/hTR3rJlS0lSXl6ePvroI6Wnp6tRo0bhxxs1aqRTTz213tXM4/Fo6dKluuWWWyLaXa7gf/K8Xi/vsQo8+OCDSk9P1/nnnx/RTr3soV5lvfvuu+rfv78SEhLCbccdd5wyMjIUHx9PzQ7jP//5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8tSoUSOlp6dr1KhR5Y4a//LLL5o1a5Y+/PBD7dy5U40aNVKfPn1066236uijjy63bytWrNBjjz2mdevWSZJOPvlkjR8/XqeffnrEdl9++aWeeuoprV69Wnv27FFycrJSU1M1ZMgQXXPNNXK73dUvFABUAmEXABzqs88+04IFC/S73/1OPXr0UE5Ojt58803ddNNNmjVrlvr27Rve9rHHHtPMmTPVuHFjDRgwQC1bttTPP/+sRYsW6f3339eMGTN0wQUXhLffsGGDrrrqKhUUFGjo0KFq06aNfvzxR7322mt67733NHfuXLVt2zaiP5988omeeeYZDR06VL1799aKFSv06aefatSoUXrnnXfUqlUrSVJWVpaGDx+uhIQEXXDBBWrdurUOHDigZcuWacqUKVq9erUeeeSRI1NEALAAADVi8+bNVvv27a2zzjrL1n733HOP1b59e6tDhw7W8uXLIx579dVXrfbt21sZGRnhtm+++cbq0KGDddZZZ1nbtm2L2P7zzz+3OnbsaJ155plWfn5+uP2SSy6x2rdvX+b4//3vf6327dtbo0ePDrddc801Vvv27a3u3btb2dnZ4XbTNK0RI0ZY7du3t+bMmRNuHz9+vNW+fXvrgw8+iDh2UVGRdfXVV1tnnHGGtXXrVls1AYCqYmQXAGqYZVnKyck55DZer1ctW7aMaEtPT1ePHj0i2i666CJNmzZNGzZs0ObNm5Wamqr58+fLsiz9/ve/LzP9oGvXrurWrZtWrFih5cuXa8CAAfruu++0du1adezYsczxL730Um3ZskUtWrQo08crrrhCxx13XPi+YRjq2bOnPvnkE23ZsiXcvm/fPkkqM1XB6/Xq2WeflcfD/3oAHDn8FwcAati+ffsOuwRZx44d9cYbb0S0/XYerBQMkMcff7y++uorbdiwQampqVq7dm2F20tSly5dtGLFCn3zzTcaMGBAeL3ck046qcy2CQkJuvvuu8s9TufOncu0paSkSJJyc3PDbX369NHy5cs1fvx4jRo1Sv3799cJJ5wgSQRdAEcc/9UBgBqWlJSk6dOnH3Kb5OTkMm1NmzYtd9ujjjpKkrR//35J0q5duw65fZMmTSRJe/bsidg+FFQrq7ztXa7gtYksywq3/eEPf1BeXp6eeOIJPfLII3rkkUfUvHlz9ejRQxdffLG6detm63kBoDoIuwBQw7xer/r37297v1CQ/C3TNCUFlzSTgtMJpMjAWd72oe1Cxy0qKrLdp8q68cYbdfXVV+uDDz7QRx99pI8//ljz5s3TvHnzdPnll2vy5Mk19twAUBqXCwYAhwqNxP7W3r17JZWM5IZ+h0Zsf2v37t3lbh9qrykNGzbUkCFDNG3aNC1fvlxPP/20WrZsqblz52rFihU1+twAEELYBQCHWr16dZk2v9+v7OxsSVKbNm0kSaeccookadWqVeUe54svvojYLvQ7KytLgUAgYlvTNHXHHXfo9ttvl9/vr1K/9+3bF3HCmhQcVe7Ro4euv/56SdI333xTpWMDgF2EXQBwqM8++0yff/55RNvrr7+uAwcOqFOnTuHVGy699FK5XC699NJL2rZtW8T2H3/8sVatWqWWLVuGV17o0KGDTj75ZO3atUuvv/56xPbvvPOOFixYoLy8vCqdTLZnzx6dc845uu6668KrMpQWCrmhNXkBoKYxZxcAalhhYaGefvrpw24XHx+va665Jnz/wgsv1I033qh+/frp+OOPD19Uwu1266677gpvd+KJJ+qOO+7QI488oksuuUQZGRlq2rSpNmzYoPfee08JCQmaNm2avF5veJ+HHnpI1157re6//3599tlnOuGEE/TTTz9pwYIFSk5OrnBFhsNp3Lixbr75Zj3++OMaNGiQ+vfvr6OPPloFBQX64osvtHLlSp188sn63e9+V6XjA4BdhF0AqGEFBQWHXY1BCs5xLR12O3furEsvvVSZmZlaunSpTNNUly5ddNttt+mcc86J2Hf06NFq166dnnvuOb311lsqKChQkyZNlJGREX6stJNOOknz5s1TZmamPvnkEy1cuFCNGjXSoEGDdOutt5a5epodt956qzp06KBXXnlFixcv1t69e+X1enXcccfp9ttv1/DhwxUXF1fl4wOAHYZV0em7AICYmDBhgubNm6eJEydGhF8AgH3M2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdxQlqAAAAqLMY2QUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ1F2AUAAECdRdgFAABAnUXYBQAAQJ31/+1MPX1gW6EJAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.style.use(\"seaborn-v0_8\")\n", + "plt.title(\"Learning Curves\", fontsize=20)\n", + "plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color=\"C0\", linewidth=2.0, label=\"Train\")\n", + "plt.plot(\n", + " np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)),\n", + " val_epoch_loss_list,\n", + " color=\"C1\",\n", + " linewidth=2.0,\n", + " label=\"Validation\",\n", + ")\n", + "plt.yticks(fontsize=12)\n", + "plt.xticks(fontsize=12)\n", + "plt.xlabel(\"Epochs\", fontsize=16)\n", + "plt.ylabel(\"Loss\", fontsize=16)\n", + "plt.legend(prop={\"size\": 14})\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0cd48c2d", + "metadata": {}, + "source": [ + "### Sampling process with classifier-free guidance\n", + "In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`).\n", + "Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f71e4924", + "metadata": { + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1000/1000 [00:14<00:00, 71.08it/s]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model.eval()\n", + "guidance_scale = 7.0\n", + "conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device)\n", + "\n", + "noise = torch.randn((1, 1, 64, 64))\n", + "noise = noise.to(device)\n", + "scheduler.set_timesteps(num_inference_steps=1000)\n", + "progress_bar = tqdm(scheduler.timesteps)\n", + "for t in progress_bar:\n", + " with autocast(enabled=True):\n", + " with torch.no_grad():\n", + " noise_input = torch.cat([noise] * 2)\n", + " model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning)\n", + " noise_pred_uncond, noise_pred_text = model_output.chunk(2)\n", + " noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)\n", + "\n", + " noise, _ = scheduler.step(noise_pred, t, noise)\n", + "\n", + "plt.style.use(\"default\")\n", + "plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.tight_layout()\n", + "plt.axis(\"off\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3483b097", + "metadata": {}, + "source": [ + "### Cleanup data directory\n", + "\n", + "Remove directory if a temporary was used." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b00d4f9a", + "metadata": {}, + "outputs": [], + "source": [ + "if directory is None:\n", + " shutil.rmtree(root_dir)" + ] + } + ], + "metadata": { + "jupytext": { + "formats": "py:percent,ipynb" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py new file mode 100644 index 00000000..a46622a3 --- /dev/null +++ b/tutorials/generative/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.py @@ -0,0 +1,327 @@ +# --- +# jupyter: +# jupytext: +# formats: py:percent,ipynb +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.14.4 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# %% +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# %% [markdown] +# # Classifier-free Guidance +# +# This tutorial illustrates how to use MONAI for training a denoising diffusion probabilistic model (DDPM)[1] to create synthetic 2D images using the classifier-free guidance technique [2] to perform conditioning. +# +# +# [1] - Ho et al. "Denoising Diffusion Probabilistic Models" https://arxiv.org/abs/2006.11239 +# [2] - Ho and Salimans "Classifier-Free Diffusion Guidance" https://arxiv.org/abs/2207.12598 +# +# +# +# ## Setup environment + +# %% +# !python -c "import monai" || pip install -q "monai-weekly[tqdm]" +# !python -c "import matplotlib" || pip install -q matplotlib +# %matplotlib inline + +# %% [markdown] +# ## Setup imports + +# %% jupyter={"outputs_hidden": false} +import os +import shutil +import tempfile +import time + +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn.functional as F +from monai import transforms +from monai.apps import MedNISTDataset +from monai.config import print_config +from monai.data import CacheDataset, DataLoader +from monai.utils import first, set_determinism +from torch.cuda.amp import GradScaler, autocast +from tqdm import tqdm + +from generative.inferers import DiffusionInferer +from generative.networks.nets import DiffusionModelUNet +from generative.networks.schedulers import DDPMScheduler + +print_config() + +# %% [markdown] +# ## Setup data directory + +# %% jupyter={"outputs_hidden": false} +directory = os.environ.get("MONAI_DATA_DIRECTORY") +root_dir = tempfile.mkdtemp() if directory is None else directory +print(root_dir) + +# %% [markdown] +# ## Set deterministic training for reproducibility + +# %% jupyter={"outputs_hidden": false} +set_determinism(42) + +# %% [markdown] +# ## Setup MedNIST Dataset and training and validation dataloaders +# In this tutorial, we will train our models on the MedNIST dataset available on MONAI +# (https://docs.monai.io/en/stable/apps.html#monai.apps.MedNISTDataset). +# Here, we will use the "Hand" and "HeadCT", where our conditioning variable `class` will specify the modality. + +# %% jupyter={"outputs_hidden": false} +train_data = MedNISTDataset(root_dir=root_dir, section="training", download=True, progress=False, seed=0) +train_datalist = [] +for item in train_data.data: + if item["class_name"] in ["Hand", "HeadCT"]: + train_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) + +# %% [markdown] +# Here we use transforms to augment the training dataset, as usual: +# +# 1. `LoadImaged` loads the hands images from files. +# 1. `EnsureChannelFirstd` ensures the original data to construct "channel first" shape. +# 1. `ScaleIntensityRanged` extracts intensity range [0, 255] and scales to [0, 1]. +# 1. `RandAffined` efficiently performs rotate, scale, shear, translate, etc. together based on PyTorch affine transform. +# +# ### Classifier-free guidance during training +# +# In order to use the classifier-free guidance during training time, we need to not just have the `class` variable saying the modality of the image (`1` for Hands and `2` for HeadCTs) but we also need to train the model with an "unconditional" class. +# Here we specify the "unconditional" class with the value `-1` with a probability of training on unconditional being 15%. Specified in the following line using MONAI's RandLambdad: +# +# `transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x))` +# +# Finally, our conditioning variable need to have the format (batch_size, 1, cross_attention_dim) when feeding into the model. For this reason, we use Lambdad to reshape our variables in the right format. + +# %% jupyter={"outputs_hidden": false} +train_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + transforms.RandAffined( + keys=["image"], + rotate_range=[(-np.pi / 36, np.pi / 36), (-np.pi / 36, np.pi / 36)], + translate_range=[(-1, 1), (-1, 1)], + scale_range=[(-0.05, 0.05), (-0.05, 0.05)], + spatial_size=[64, 64], + padding_mode="zeros", + prob=0.5, + ), + transforms.RandLambdad(keys=["class"], prob=0.15, func=lambda x: -1 * torch.ones_like(x)), + transforms.Lambdad( + keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) + ), + ] +) +train_ds = CacheDataset(data=train_datalist, transform=train_transforms) +train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=4, persistent_workers=True) + +# %% jupyter={"outputs_hidden": false} +val_data = MedNISTDataset(root_dir=root_dir, section="validation", download=True, progress=False, seed=0) +val_datalist = [] +for item in val_data.data: + if item["class_name"] in ["Hand", "HeadCT"]: + val_datalist.append({"image": item["image"], "class": 1 if item["class_name"] == "Hand" else 2}) + + +val_transforms = transforms.Compose( + [ + transforms.LoadImaged(keys=["image"]), + transforms.EnsureChannelFirstd(keys=["image"]), + transforms.ScaleIntensityRanged(keys=["image"], a_min=0.0, a_max=255.0, b_min=0.0, b_max=1.0, clip=True), + transforms.Lambdad( + keys=["class"], func=lambda x: torch.tensor(x, dtype=torch.float32).unsqueeze(0).unsqueeze(0) + ), + ] +) +val_ds = CacheDataset(data=val_datalist, transform=val_transforms) +val_loader = DataLoader(val_ds, batch_size=128, shuffle=False, num_workers=4, persistent_workers=True) + +# %% [markdown] +# ### Visualisation of the training images + +# %% jupyter={"outputs_hidden": false} +check_data = first(train_loader) +print(f"batch shape: {check_data['image'].shape}") +image_visualisation = torch.cat( + [check_data["image"][0, 0], check_data["image"][1, 0], check_data["image"][2, 0], check_data["image"][3, 0]], dim=1 +) +plt.figure("training images", (12, 6)) +plt.imshow(image_visualisation, vmin=0, vmax=1, cmap="gray") +plt.axis("off") +plt.tight_layout() +plt.show() + +# %% [markdown] +# ### Define network, scheduler, optimizer, and inferer +# At this step, we instantiate the MONAI components to create a DDPM, the UNET, the noise scheduler, and the inferer used for training and sampling. We are using +# the original DDPM scheduler containing 1000 timesteps in its Markov chain, and a 2D UNET with attention mechanisms +# in the 3rd level, each with 1 attention head (`num_head_channels=64`). +# +# In order to pass conditioning variables with dimension of 1 (just specifying the modality of the image), we use: +# +# ` +# with_conditioning=True, +# cross_attention_dim=1, +# ` + +# %% jupyter={"outputs_hidden": false} +device = torch.device("cuda") + +model = DiffusionModelUNet( + spatial_dims=2, + in_channels=1, + out_channels=1, + num_channels=(64, 64, 64), + attention_levels=(False, False, True), + num_res_blocks=1, + num_head_channels=(0, 0, 64), + with_conditioning=True, + cross_attention_dim=1, +) +model.to(device) + +scheduler = DDPMScheduler(num_train_timesteps=1000) + +optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5) + +inferer = DiffusionInferer(scheduler) +# %% [markdown] +# ### Model training +# Here, we are training our model for 75 epochs (training time: ~50 minutes). + +# %% jupyter={"outputs_hidden": false} +n_epochs = 75 +val_interval = 5 +epoch_loss_list = [] +val_epoch_loss_list = [] + +scaler = GradScaler() +total_start = time.time() +for epoch in range(n_epochs): + model.train() + epoch_loss = 0 + progress_bar = tqdm(enumerate(train_loader), total=len(train_loader), ncols=70) + progress_bar.set_description(f"Epoch {epoch}") + for step, batch in progress_bar: + images = batch["image"].to(device) + classes = batch["class"].to(device) + optimizer.zero_grad(set_to_none=True) + + with autocast(enabled=True): + # Generate random noise + noise = torch.randn_like(images).to(device) + + # Get model prediction + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) + + loss = F.mse_loss(noise_pred.float(), noise.float()) + + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + + epoch_loss += loss.item() + + progress_bar.set_postfix({"loss": epoch_loss / (step + 1)}) + epoch_loss_list.append(epoch_loss / (step + 1)) + + if (epoch + 1) % val_interval == 0: + model.eval() + val_epoch_loss = 0 + for step, batch in enumerate(val_loader): + images = batch["image"].to(device) + classes = batch["class"].to(device) + with torch.no_grad(): + with autocast(enabled=True): + noise = torch.randn_like(images).to(device) + noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, condition=classes) + val_loss = F.mse_loss(noise_pred.float(), noise.float()) + + val_epoch_loss += val_loss.item() + progress_bar.set_postfix({"val_loss": val_epoch_loss / (step + 1)}) + val_epoch_loss_list.append(val_epoch_loss / (step + 1)) + +total_time = time.time() - total_start +print(f"train completed, total time: {total_time}.") +# %% [markdown] +# ### Learning curves + +# %% jupyter={"outputs_hidden": false} +plt.style.use("seaborn-v0_8") +plt.title("Learning Curves", fontsize=20) +plt.plot(np.linspace(1, n_epochs, n_epochs), epoch_loss_list, color="C0", linewidth=2.0, label="Train") +plt.plot( + np.linspace(val_interval, n_epochs, int(n_epochs / val_interval)), + val_epoch_loss_list, + color="C1", + linewidth=2.0, + label="Validation", +) +plt.yticks(fontsize=12) +plt.xticks(fontsize=12) +plt.xlabel("Epochs", fontsize=16) +plt.ylabel("Loss", fontsize=16) +plt.legend(prop={"size": 14}) +plt.show() + +# %% [markdown] +# ### Sampling process with classifier-free guidance +# In order to sample using classifier-free guidance, for each step of the process we need to have 2 elements, one generated conditioned in the desired class (here we want to condition on Hands `=1`) and one using the unconditional class (`=-1`). +# Instead using directly the predicted class in every step, we use the unconditional plus the direction vector pointing to the condition that we want (`noise_pred_text - noise_pred_uncond`). The effect of the condition is defined by the `guidance_scale` defining the influence of our direction vector. + +# %% jupyter={"outputs_hidden": false} +model.eval() +guidance_scale = 7.0 +conditioning = torch.cat([-1 * torch.ones(1, 1, 1).float(), torch.ones(1, 1, 1).float()], dim=0).to(device) + +noise = torch.randn((1, 1, 64, 64)) +noise = noise.to(device) +scheduler.set_timesteps(num_inference_steps=1000) +progress_bar = tqdm(scheduler.timesteps) +for t in progress_bar: + with autocast(enabled=True): + with torch.no_grad(): + noise_input = torch.cat([noise] * 2) + model_output = model(noise_input, timesteps=torch.Tensor((t,)).to(noise.device), context=conditioning) + noise_pred_uncond, noise_pred_text = model_output.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + noise, _ = scheduler.step(noise_pred, t, noise) + +plt.style.use("default") +plt.imshow(noise[0, 0].cpu(), vmin=0, vmax=1, cmap="gray") +plt.tight_layout() +plt.axis("off") +plt.show() + +# %% [markdown] +# ### Cleanup data directory +# +# Remove directory if a temporary was used. + +# %% +if directory is None: + shutil.rmtree(root_dir) From 4b786f8046fa07dbc353fede45b3941af5c73659 Mon Sep 17 00:00:00 2001 From: Walter Hugo Lopez Pinaya Date: Wed, 22 Mar 2023 12:21:46 +0000 Subject: [PATCH 23/23] Move files and update License Signed-off-by: Walter Hugo Lopez Pinaya --- ...tection_tutorial_classifier_guidance.ipynb | 87 ++++++++++--------- ...ydetection_tutorial_classifier_guidance.py | 65 +++++++------- 2 files changed, 77 insertions(+), 75 deletions(-) rename tutorials/generative/anomaly_detection/{classifier_guidance_anomalydetection => }/anomalydetection_tutorial_classifier_guidance.ipynb (99%) rename tutorials/generative/anomaly_detection/{classifier_guidance_anomalydetection => }/anomalydetection_tutorial_classifier_guidance.py (94%) diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb b/tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.ipynb similarity index 99% rename from tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb rename to tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.ipynb index d931452e..71e58a54 100644 --- a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.ipynb +++ b/tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.ipynb @@ -1,5 +1,24 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2470cf02", + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright (c) MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License." + ] + }, { "cell_type": "markdown", "id": "63d95da6", @@ -93,7 +112,7 @@ } ], "source": [ - "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm, einops]\"\n", + "!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm]\"\n", "!python -c \"import matplotlib\" || pip install -q matplotlib\n", "!python -c \"import seaborn\" || pip install -q seaborn" ] @@ -111,7 +130,6 @@ "execution_count": 91, "id": "972ed3f3", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -155,20 +173,8 @@ } ], "source": [ - "# Copyright 2020 MONAI Consortium\n", - "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", - "# you may not use this file except in compliance with the License.\n", - "# You may obtain a copy of the License at\n", - "# http://www.apache.org/licenses/LICENSE-2.0\n", - "# Unless required by applicable law or agreed to in writing, software\n", - "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", - "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", - "# See the License for the specific language governing permissions and\n", - "# limitations under the License.\n", - "\n", "import os\n", "import time\n", - "from typing import Dict\n", "import tempfile\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", @@ -185,6 +191,7 @@ "from generative.inferers import DiffusionInferer\n", "from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet\n", "from generative.networks.schedulers.ddim import DDIMScheduler\n", + "\n", "torch.multiprocessing.set_sharing_strategy(\"file_system\")\n", "\n", "print_config()" @@ -203,7 +210,6 @@ "execution_count": 92, "id": "8b4323e7", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -227,7 +233,6 @@ "execution_count": 93, "id": "34ea510f", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -296,7 +301,6 @@ "execution_count": 107, "id": "da1927b0", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false } @@ -326,7 +330,7 @@ } ], "source": [ - "batch_size=64\n", + "batch_size = 64\n", "\n", "train_ds = DecathlonDataset(\n", " root_dir=root_dir,\n", @@ -425,10 +429,10 @@ "execution_count": 108, "id": "bee5913e", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false - } + }, + "lines_to_next_cell": 2 }, "outputs": [], "source": [ @@ -450,8 +454,7 @@ "\n", "optimizer = torch.optim.Adam(params=model.parameters(), lr=2.5e-5)\n", "\n", - "inferer = DiffusionInferer(scheduler)\n", - "\n" + "inferer = DiffusionInferer(scheduler)" ] }, { @@ -470,7 +473,6 @@ "execution_count": 109, "id": "6c0ed909", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -609,8 +611,8 @@ " epoch_loss = 0\n", "\n", " for step, data in enumerate(train_loader):\n", - " images = data['image'].to(device)\n", - " classes = data['slice_label'].to(device)\n", + " images = data[\"image\"].to(device)\n", + " classes = data[\"slice_label\"].to(device)\n", " optimizer.zero_grad(set_to_none=True)\n", " timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t\n", "\n", @@ -633,18 +635,18 @@ " val_epoch_loss = 0\n", "\n", " for step, data in enumerate(val_loader):\n", - " images = data['image'].to(device)\n", - " classes = data['slice_label'].to(device)\n", - " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", - " with torch.no_grad():\n", + " images = data[\"image\"].to(device)\n", + " classes = data[\"slice_label\"].to(device)\n", + " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", + " with torch.no_grad():\n", " with autocast(enabled=True):\n", " noise = torch.randn_like(images).to(device)\n", " noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps)\n", " val_loss = F.mse_loss(noise_pred.float(), noise.float())\n", "\n", - " val_epoch_loss += val_loss.item()\n", + " val_epoch_loss += val_loss.item()\n", " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - " print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1))\n", + " print(\"Epoch\", epoch, \"Validation loss\", val_epoch_loss / (step + 1))\n", "\n", "total_time = time.time() - total_start\n", "print(f\"train diffusion completed, total time: {total_time}.\")\n", @@ -1021,9 +1023,9 @@ " epoch_loss = 0\n", "\n", " for step, data in enumerate(train_loader):\n", - " images = data['image'].to(device)\n", - " classes = data['slice_label'].to(device)\n", - " #classes[classes==2]=0\n", + " images = data[\"image\"].to(device)\n", + " classes = data[\"slice_label\"].to(device)\n", + " # classes[classes==2]=0\n", "\n", " optimizer_cls.zero_grad(set_to_none=True)\n", " timesteps = torch.randint(0, 1000, (len(images),)).to(device)\n", @@ -1049,8 +1051,8 @@ " val_epoch_loss = 0\n", "\n", " for step, data_val in enumerate(val_loader):\n", - " images = data_val['image'].to(device)\n", - " classes = data_val['slice_label'].to(device)\n", + " images = data_val[\"image\"].to(device)\n", + " classes = data_val[\"slice_label\"].to(device)\n", " timesteps = torch.randint(0, 1, (len(images),)).to(\n", " device\n", " ) # check validation accuracy on the original images, i.e., do not add noise\n", @@ -1064,7 +1066,7 @@ " val_epoch_loss += val_loss.item()\n", " _, predicted = torch.max(pred, 1)\n", " val_epoch_loss_list.append(val_epoch_loss / (step + 1))\n", - " print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1))\n", + " print(\"Epoch\", epoch, \"Validation loss\", val_epoch_loss / (step + 1))\n", "\n", "total_time = time.time() - total_start\n", "print(f\"train completed, total time: {total_time}.\")\n", @@ -1240,10 +1242,10 @@ "idx = idx_unhealthy[4] # Pick a random slice of the validation set to be transformed\n", "inputimg = data_val[\"image\"][idx] # Pick an input slice of the validation set to be transformed\n", "inputlabel = data_val[\"slice_label\"][idx] # Check whether it is healthy or diseased\n", - "print('minmax', inputimg.min(), inputimg.max())\n", + "print(\"minmax\", inputimg.min(), inputimg.max())\n", "\n", "plt.figure(\"input\" + str(inputlabel))\n", - "plt.imshow(inputimg[0,...], vmin=0, vmax=1, cmap=\"gray\")\n", + "plt.imshow(inputimg[0, ...], vmin=0, vmax=1, cmap=\"gray\")\n", "plt.axis(\"off\")\n", "plt.tight_layout()\n", "plt.show()\n", @@ -1268,7 +1270,6 @@ "execution_count": 176, "id": "f71e4924", "metadata": { - "collapsed": false, "jupyter": { "outputs_hidden": false }, @@ -1351,7 +1352,7 @@ ], "source": [ "y = torch.tensor(0) # define the desired class label\n", - "scale = 6 # define the desired gradient scale s\n", + "scale = 6 # define the desired gradient scale s\n", "progress_bar = tqdm(range(L)) # go back and forth L timesteps\n", "\n", "for i in progress_bar: # go through the denoising process\n", @@ -1415,7 +1416,7 @@ "\n", "diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy()\n", "plt.style.use(\"default\")\n", - "plt.imshow(diff[0,...], cmap=\"jet\")\n", + "plt.imshow(diff[0, ...], cmap=\"jet\")\n", "plt.tight_layout()\n", "plt.axis(\"off\")\n", "plt.show()" @@ -1449,7 +1450,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.10.6" } }, "nbformat": 4, diff --git a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py b/tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.py similarity index 94% rename from tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py rename to tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.py index 1df629ac..54fdb6f6 100644 --- a/tutorials/generative/anomaly_detection/classifier_guidance_anomalydetection/anomalydetection_tutorial_classifier_guidance.py +++ b/tutorials/generative/anomaly_detection/anomalydetection_tutorial_classifier_guidance.py @@ -13,6 +13,18 @@ # name: python3 # --- +# %% +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # %% [markdown] # # Diffusion Models for Medical Anomaly Detection with Classifier Guidance # @@ -27,7 +39,7 @@ # ## Setup environment # %% -# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm, einops]" +# !python -c "import monai" || pip install -q "monai-weekly[pillow, tqdm]" # !python -c "import matplotlib" || pip install -q matplotlib # !python -c "import seaborn" || pip install -q seaborn @@ -35,17 +47,6 @@ # ## Setup imports # %% jupyter={"outputs_hidden": false} -# Copyright 2020 MONAI Consortium -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - import os import time import tempfile @@ -64,6 +65,7 @@ from generative.inferers import DiffusionInferer from generative.networks.nets.diffusion_model_unet import DiffusionModelEncoder, DiffusionModelUNet from generative.networks.schedulers.ddim import DDIMScheduler + torch.multiprocessing.set_sharing_strategy("file_system") print_config() @@ -116,7 +118,7 @@ ) # %% jupyter={"outputs_hidden": false} -batch_size=64 +batch_size = 64 train_ds = DecathlonDataset( root_dir=root_dir, @@ -190,7 +192,6 @@ inferer = DiffusionInferer(scheduler) - # %% [markdown] tags=[] # ## Model training of the diffusion model # We train our diffusion model for 2000 epochs. @@ -209,8 +210,8 @@ epoch_loss = 0 for step, data in enumerate(train_loader): - images = data['image'].to(device) - classes = data['slice_label'].to(device) + images = data["image"].to(device) + classes = data["slice_label"].to(device) optimizer.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) # pick a random time step t @@ -233,18 +234,18 @@ val_epoch_loss = 0 for step, data in enumerate(val_loader): - images = data['image'].to(device) - classes = data['slice_label'].to(device) - timesteps = torch.randint(0, 1000, (len(images),)).to(device) - with torch.no_grad(): + images = data["image"].to(device) + classes = data["slice_label"].to(device) + timesteps = torch.randint(0, 1000, (len(images),)).to(device) + with torch.no_grad(): with autocast(enabled=True): noise = torch.randn_like(images).to(device) noise_pred = inferer(inputs=images, diffusion_model=model, noise=noise, timesteps=timesteps) val_loss = F.mse_loss(noise_pred.float(), noise.float()) - val_epoch_loss += val_loss.item() + val_epoch_loss += val_loss.item() val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1)) + print("Epoch", epoch, "Validation loss", val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train diffusion completed, total time: {total_time}.") @@ -335,9 +336,9 @@ epoch_loss = 0 for step, data in enumerate(train_loader): - images = data['image'].to(device) - classes = data['slice_label'].to(device) - #classes[classes==2]=0 + images = data["image"].to(device) + classes = data["slice_label"].to(device) + # classes[classes==2]=0 optimizer_cls.zero_grad(set_to_none=True) timesteps = torch.randint(0, 1000, (len(images),)).to(device) @@ -363,8 +364,8 @@ val_epoch_loss = 0 for step, data_val in enumerate(val_loader): - images = data_val['image'].to(device) - classes = data_val['slice_label'].to(device) + images = data_val["image"].to(device) + classes = data_val["slice_label"].to(device) timesteps = torch.randint(0, 1, (len(images),)).to( device ) # check validation accuracy on the original images, i.e., do not add noise @@ -378,7 +379,7 @@ val_epoch_loss += val_loss.item() _, predicted = torch.max(pred, 1) val_epoch_loss_list.append(val_epoch_loss / (step + 1)) - print('Epoch', epoch, 'Validation loss', val_epoch_loss / (step + 1)) + print("Epoch", epoch, "Validation loss", val_epoch_loss / (step + 1)) total_time = time.time() - total_start print(f"train completed, total time: {total_time}.") @@ -410,10 +411,10 @@ idx = idx_unhealthy[4] # Pick a random slice of the validation set to be transformed inputimg = data_val["image"][idx] # Pick an input slice of the validation set to be transformed inputlabel = data_val["slice_label"][idx] # Check whether it is healthy or diseased -print('minmax', inputimg.min(), inputimg.max()) +print("minmax", inputimg.min(), inputimg.max()) plt.figure("input" + str(inputlabel)) -plt.imshow(inputimg[0,...], vmin=0, vmax=1, cmap="gray") +plt.imshow(inputimg[0, ...], vmin=0, vmax=1, cmap="gray") plt.axis("off") plt.tight_layout() plt.show() @@ -455,7 +456,7 @@ # %% y = torch.tensor(0) # define the desired class label -scale = 6 # define the desired gradient scale s +scale = 6 # define the desired gradient scale s progress_bar = tqdm(range(L)) # go back and forth L timesteps for i in progress_bar: # go through the denoising process @@ -496,7 +497,7 @@ diff = abs(inputimg.cpu() - current_img[0, 0].cpu()).detach().numpy() plt.style.use("default") -plt.imshow(diff[0,...], cmap="jet") +plt.imshow(diff[0, ...], cmap="jet") plt.tight_layout() plt.axis("off") plt.show()