{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install python-box timm pytorch-lightning==1.5.10 grad-cam ttach","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:54:42.586759Z","iopub.execute_input":"2023-01-05T00:54:42.587149Z","iopub.status.idle":"2023-01-05T00:55:21.535911Z","shell.execute_reply.started":"2023-01-05T00:54:42.587119Z","shell.execute_reply":"2023-01-05T00:55:21.53473Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport warnings\nfrom pprint import pprint\nfrom glob import glob\nfrom tqdm import tqdm\nimport cv2\nimport matplotlib.image as mpimg\n\n\nimport torch\nimport torch.optim as optim\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport torchvision.transforms as T\nfrom box import Box\nfrom timm import create_model\nfrom sklearn.model_selection import StratifiedKFold\nfrom torchvision.io import read_image, ImageReadMode\nfrom torch.utils.data import DataLoader, Dataset\nfrom pytorch_grad_cam import GradCAMPlusPlus\nfrom pytorch_grad_cam.utils.image import show_cam_on_image\n\nimport pytorch_lightning as pl\nfrom pytorch_lightning.utilities.seed import seed_everything\nfrom pytorch_lightning import callbacks\nfrom pytorch_lightning.callbacks.progress import ProgressBarBase\nfrom pytorch_lightning.callbacks.early_stopping import EarlyStopping\nfrom pytorch_lightning.loggers import TensorBoardLogger\nfrom pytorch_lightning import LightningDataModule, LightningModule\n\n\nwarnings.filterwarnings(\"ignore\")","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:55:21.540181Z","iopub.execute_input":"2023-01-05T00:55:21.540494Z","iopub.status.idle":"2023-01-05T00:55:24.149317Z","shell.execute_reply.started":"2023-01-05T00:55:21.540462Z","shell.execute_reply":"2023-01-05T00:55:24.148296Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"config = {'seed': 2021,\n          'root': '/kaggle/input/rsna-mammography-images-as-pngs/', \n          'n_splits': 5,\n          'epoch': 20,\n          'trainer': {\n              'gpus': 1,\n              'accumulate_grad_batches': 1,\n              'progress_bar_refresh_rate': 1,\n              'fast_dev_run': False,\n              'num_sanity_val_steps': 0,\n              'resume_from_checkpoint': None,\n          },\n          'transform':{\n              'name': 'get_default_transforms',\n              'image_size': 512\n          },\n          'train_loader':{\n              'batch_size': 64,\n              'shuffle': True,\n              'num_workers': 4,\n              'pin_memory': False,\n              'drop_last': True,\n          },\n          'val_loader': {\n              'batch_size': 64,\n              'shuffle': False,\n              'num_workers': 4,\n              'pin_memory': False,\n              'drop_last': False\n         },\n          'model':{\n              'name': 'swin_tiny_patch4_window7_224',\n              'output_dim': 1\n          },\n          'optimizer':{\n              'name': 'optim.AdamW',\n              'params':{\n                  'lr': 1e-5\n              },\n          },\n          'scheduler':{\n              'name': 'optim.lr_scheduler.CosineAnnealingWarmRestarts',\n              'params':{\n                  'T_0': 20,\n                  'eta_min': 1e-4,\n              }\n          },\n          'loss': 'nn.BCEWithLogitsLoss',\n}\n\nconfig = Box(config)","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:55:24.150954Z","iopub.execute_input":"2023-01-05T00:55:24.151616Z","iopub.status.idle":"2023-01-05T00:55:24.162423Z","shell.execute_reply.started":"2023-01-05T00:55:24.151577Z","shell.execute_reply":"2023-01-05T00:55:24.161487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_dir = os.path.join('/kaggle/input', 'rsna-breast-cancer-detection')\ntrain_images = os.path.join('/kaggle/input', 'rsna-mammography-images-as-pngs/images_as_pngs_512/train_images_processed_512')\n\nBASE_CSV_PATH = base_dir\nBASE_IMG_PATH = train_images\n# Train Images and test images paths\n# train_images = glob(f\"{CFG.train_images}/*/*.png\")\n# test_images = glob(f\"{CFG.test_images}/*/*.dcm\")\n\ndf = pd.read_csv(f'{BASE_CSV_PATH}/train.csv')\ndf['image_path'] = f'{BASE_IMG_PATH}/'\\\n                    + '/' + df.patient_id.astype(str)\\\n                    + '/' + df.image_id.astype(str)\\\n                    + '.png'\nprint('Train:')\ndisplay(df.head(2))\n\n# test\ntest_df = pd.read_csv(f'{BASE_CSV_PATH}/test.csv')\ntest_df['image_path'] = f'{BASE_CSV_PATH}/test_images'\\\n                    + '/' + test_df.patient_id.astype(str)\\\n                    + '/' + test_df.image_id.astype(str)\\\n                    + '.dcm'\nprint('\\nTest:')\ndisplay(test_df.head(2))","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:55:24.165849Z","iopub.execute_input":"2023-01-05T00:55:24.166701Z","iopub.status.idle":"2023-01-05T00:55:24.388804Z","shell.execute_reply.started":"2023-01-05T00:55:24.166667Z","shell.execute_reply":"2023-01-05T00:55:24.387886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df.iloc[:200]","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:55:24.390282Z","iopub.execute_input":"2023-01-05T00:55:24.390772Z","iopub.status.idle":"2023-01-05T00:55:24.396086Z","shell.execute_reply.started":"2023-01-05T00:55:24.390734Z","shell.execute_reply":"2023-01-05T00:55:24.394935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[df[\"cancer\"] == 1]","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:16:57.222929Z","iopub.execute_input":"2023-01-05T01:16:57.223598Z","iopub.status.idle":"2023-01-05T01:16:57.248189Z","shell.execute_reply.started":"2023-01-05T01:16:57.223566Z","shell.execute_reply":"2023-01-05T01:16:57.24728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import random\nnum = random.randint(0, 200)\nimage = read_image(df[\"image_path\"][num])\nimage = torch.reshape(image, [512, 512, 1])\nimage = image.numpy()\nimage = crop_image_only_outside(image)\nimage = cv2.resize(image, [512, 512, 1])\nimage = torch.tensor(image)\n# image = torch.tensor(image)","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:19:16.229611Z","iopub.execute_input":"2023-01-05T01:19:16.229988Z","iopub.status.idle":"2023-01-05T01:19:16.261557Z","shell.execute_reply.started":"2023-01-05T01:19:16.229956Z","shell.execute_reply":"2023-01-05T01:19:16.260156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(image.numpy())\nplt.title(df[\"cancer\"][num])","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:18:20.345181Z","iopub.execute_input":"2023-01-05T01:18:20.345458Z","iopub.status.idle":"2023-01-05T01:18:20.620726Z","shell.execute_reply.started":"2023-01-05T01:18:20.345433Z","shell.execute_reply":"2023-01-05T01:18:20.619748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Construct the Dataset","metadata":{}},{"cell_type":"code","source":"class CancerfinderDataset(Dataset):\n    def __init__(self, df, image_size=224):\n        self._X = df[\"image_path\"].values\n        self._y = None\n        if \"cancer\" in df.keys():\n            self._y = df[\"cancer\"].values\n        self._transform = T.Resize([image_size, image_size])\n        self._crop = crop_image_only_outside\n\n    def __len__(self):\n        return len(self._X)\n\n    def __getitem__(self, idx):\n        image_path = self._X[idx]\n        image = read_image(image_path)\n        image = torch.reshape(image, [512, 512, 1])\n        image = image.numpy()\n        image = self._crop(image)\n        image = torch.tensor(image)\n        image = image.resize_([512, 512, 1])\n#         image = self._transform(image)\n#         image = torch.eq(image, 1).to(torch.long) \n\n        if self._y is not None:\n            label = self._y[idx]\n            return image, label\n        return image\n\nclass CancerfinderDataModule(LightningDataModule):\n    def __init__(\n        self,\n        train_df,\n        val_df,\n        cfg,\n    ):\n        super().__init__()\n        self._train_df = train_df\n        self._val_df = val_df\n        self._cfg = cfg\n\n    def __create_dataset(self, train=True):\n        return (\n            CancerfinderDataset(self._train_df, self._cfg.transform.image_size)\n            if train\n            else CancerfinderDataset(self._val_df, self._cfg.transform.image_size)\n        )\n\n    def train_dataloader(self):\n        dataset = self.__create_dataset(True)\n        return DataLoader(dataset, **self._cfg.train_loader)\n\n    def val_dataloader(self):\n        dataset = self.__create_dataset(False)\n        return DataLoader(dataset, **self._cfg.val_loader)","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:06:40.153727Z","iopub.execute_input":"2023-01-05T01:06:40.154694Z","iopub.status.idle":"2023-01-05T01:06:40.166072Z","shell.execute_reply.started":"2023-01-05T01:06:40.154658Z","shell.execute_reply":"2023-01-05T01:06:40.165018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Apply Augmentation","metadata":{}},{"cell_type":"code","source":"IMAGENET_MEAN = [0.485, 0.456, 0.406]  # RGB\nIMAGENET_STD = [0.229, 0.224, 0.225]  # RGB\n\ndef crop_image_only_outside(img,tol=0):\n    # img is 2D or 3D image data\n    # tol  is tolerance\n    mask = img>tol\n    if img.ndim==3:\n        mask = mask.all(2)\n    m,n = mask.shape\n    mask0,mask1 = mask.any(0),mask.any(1)\n    col_start,col_end = mask0.argmax(),n-mask0[::-1].argmax()\n    row_start,row_end = mask1.argmax(),m-mask1[::-1].argmax()\n    \n    return img[row_start:row_end,col_start:col_end]\n\n\ndef get_default_transforms():\n    transform = {\n        \"train\": T.Compose(\n            [\n                T.RandomHorizontalFlip(),\n                T.RandomVerticalFlip(),\n                T.RandomAffine(15, translate=(0.1, 0.1), scale=(0.9, 1.1)),\n                T.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1),\n                T.ConvertImageDtype(torch.float),\n                T.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),\n            ]\n        ),\n        \"val\": T.Compose(\n            [\n                T.ConvertImageDtype(torch.float),\n                T.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),\n            ]\n        ),\n    }\n    return transform","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:06:40.708925Z","iopub.execute_input":"2023-01-05T01:06:40.71011Z","iopub.status.idle":"2023-01-05T01:06:40.71991Z","shell.execute_reply.started":"2023-01-05T01:06:40.710066Z","shell.execute_reply":"2023-01-05T01:06:40.718882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build the Model","metadata":{}},{"cell_type":"code","source":"def mixup(x: torch.Tensor, y: torch.Tensor, alpha: float = 1.0):\n    assert alpha > 0, \"alpha should be larger than 0\"\n    assert x.size(0) > 1, \"Mixup cannot be applied to a single instance.\"\n\n    lam = np.random.beta(alpha, alpha)\n    rand_index = torch.randperm(x.size()[0])\n    mixed_x = lam * x + (1 - lam) * x[rand_index, :]\n    target_a, target_b = y, y[rand_index]\n    return mixed_x, target_a, target_b, lam\n\nclass Model(pl.LightningModule):\n    def __init__(self, cfg):\n        super().__init__()\n        self.cfg = cfg\n        self.__build_model()\n        self._criterion = eval(self.cfg.loss)()\n        self.transform = get_default_transforms()\n        \n        self.save_hyperparameters(cfg)\n\n    def __build_model(self):\n        self.backbone = create_model(\n            self.cfg.model.name, pretrained=True, num_classes=0, in_chans=3\n        )\n        num_features = self.backbone.num_features\n        self.fc = nn.Sequential(\n            nn.Dropout(0.5), nn.Linear(num_features, self.cfg.model.output_dim)\n        )\n\n    def forward(self, x):\n        f = self.backbone(x)\n        out = self.fc(f)\n        return out\n\n    def training_step(self, batch, batch_idx):\n        loss, pred, labels = self.__share_step(batch, 'train')\n        return {'loss': loss, 'pred': pred, 'labels': labels}\n        \n    def validation_step(self, batch, batch_idx):\n        loss, pred, labels = self.__share_step(batch, 'val')\n        return {'pred': pred, 'labels': labels}\n    \n    def __share_step(self, batch, mode):\n        images, labels = batch\n        labels = labels.float() / 100.0\n        images = self.transform[mode](images)\n        \n        if torch.rand(1)[0] < 0.5 and mode == 'train':\n            mix_images, target_a, target_b, lam = mixup(images, labels, alpha=0.5)\n            logits = self.forward(mix_images).squeeze(1)\n            loss = self._criterion(logits, target_a) * lam + \\\n                (1 - lam) * self._criterion(logits, target_b)\n        else:\n            logits = self.forward(images).squeeze(1)\n            loss = self._criterion(logits, labels)\n        \n        pred = logits.sigmoid().detach().cpu() * 100.\n        labels = labels.detach().cpu() * 100.\n        return loss, pred, labels\n        \n    def training_epoch_end(self, outputs):\n        self.__share_epoch_end(outputs, 'train')\n\n    def validation_epoch_end(self, outputs):\n        self.__share_epoch_end(outputs, 'val')    \n        \n    def __share_epoch_end(self, outputs, mode):\n        preds = []\n        labels = []\n        for out in outputs:\n            pred, label = out['pred'], out['labels']\n            preds.append(pred)\n            labels.append(label)\n        preds = torch.cat(preds)\n        labels = torch.cat(labels)\n        metrics = torch.sqrt(((labels - preds) ** 2).mean())\n        self.log(f'{mode}_loss', metrics)\n    \n    def check_gradcam(self, dataloader, target_layer, target_category, reshape_transform=None):\n        cam = GradCAMPlusPlus(\n            model=self,\n            target_layers=[target_layer], \n            use_cuda=self.cfg.trainer.gpus, \n            reshape_transform=reshape_transform)\n        \n        org_images, labels = iter(dataloader).next()\n        cam.batch_size = len(org_images)\n        images = self.transform['val'](org_images)\n        images = images.to(self.device)\n        logits = self.forward(images).squeeze(1)\n        pred = logits.sigmoid().detach().cpu().numpy() * 100\n        labels = labels.cpu().numpy()\n        \n#         grayscale_cam = cam(input_tensor=images, target_category=target_category, eigen_smooth=True)\n        grayscale_cam = cam(input_tensor=images, eigen_smooth=True)\n        org_images = org_images.detach().cpu().numpy().transpose(0, 2, 3, 1) / 255.\n        return org_images, grayscale_cam, pred, labels\n\n    def configure_optimizers(self):\n        optimizer = eval(self.cfg.optimizer.name)(\n            self.parameters(), **self.cfg.optimizer.params\n        )\n        scheduler = eval(self.cfg.scheduler.name)(\n            optimizer,\n            **self.cfg.scheduler.params\n        )\n        return [optimizer], [scheduler]","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:06:41.423012Z","iopub.execute_input":"2023-01-05T01:06:41.423395Z","iopub.status.idle":"2023-01-05T01:06:41.446673Z","shell.execute_reply.started":"2023-01-05T01:06:41.423363Z","shell.execute_reply":"2023-01-05T01:06:41.445393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_dataloader = CancerfinderDataModule(df, df, config).val_dataloader()\nimages, labels = iter(sample_dataloader).next()\n\nplt.figure(figsize=(12, 12))\nfor it, (image, label) in enumerate(zip(images[:16], labels[:16])):\n    plt.subplot(4, 4, it+1)\n    plt.imshow(image.permute(0, 1, 2))\n    plt.axis('off')\n    plt.title(f'Cancer: {int(label)}')","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:06:41.988721Z","iopub.execute_input":"2023-01-05T01:06:41.990134Z","iopub.status.idle":"2023-01-05T01:06:44.178682Z","shell.execute_reply.started":"2023-01-05T01:06:41.990082Z","shell.execute_reply":"2023-01-05T01:06:44.177674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images","metadata":{"execution":{"iopub.status.busy":"2023-01-05T01:03:46.271136Z","iopub.execute_input":"2023-01-05T01:03:46.27222Z","iopub.status.idle":"2023-01-05T01:03:46.29497Z","shell.execute_reply.started":"2023-01-05T01:03:46.272181Z","shell.execute_reply":"2023-01-05T01:03:46.293957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train the Model","metadata":{}},{"cell_type":"code","source":"skf = StratifiedKFold(\n    n_splits=config.n_splits, shuffle=True, random_state=config.seed\n)\n\nfor fold, (train_idx, val_idx) in enumerate(skf.split(df[\"image_path\"], df[\"cancer\"])):\n    train_df = df.loc[train_idx].reset_index(drop=True)\n    val_df = df.loc[val_idx].reset_index(drop=True)\n    datamodule = CancerfinderDataModule(train_df, val_df, config)\n    model = Model(config)\n    earlystopping = EarlyStopping(monitor=\"val_loss\")\n    lr_monitor = callbacks.LearningRateMonitor()\n    loss_checkpoint = callbacks.ModelCheckpoint(\n        filename=\"best_loss\",\n        monitor=\"val_loss\",\n        save_top_k=1,\n        mode=\"min\",\n        save_last=False,\n    )\n    logger = TensorBoardLogger(config.model.name)\n    \n    trainer = pl.Trainer(\n        logger=logger,\n        max_epochs=config.epoch,\n        callbacks=[lr_monitor, loss_checkpoint, earlystopping],\n        **config.trainer,\n    )\n    trainer.fit(model, datamodule=datamodule)","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:54:32.271427Z","iopub.status.idle":"2023-01-05T00:54:32.272163Z","shell.execute_reply.started":"2023-01-05T00:54:32.271924Z","shell.execute_reply":"2023-01-05T00:54:32.271945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Construct a Class Activation Map to Visualize Training","metadata":{}},{"cell_type":"code","source":"# gradcam reshape_transform for vit\ndef reshape_transform(tensor, height=7, width=7):\n    result = tensor.reshape(tensor.size(0),\n                            height, width, tensor.size(2))\n\n    # like in CNNs.\n    result = result.permute(0, 3, 1, 2)\n    return result","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:54:32.273428Z","iopub.status.idle":"2023-01-05T00:54:32.274211Z","shell.execute_reply.started":"2023-01-05T00:54:32.273928Z","shell.execute_reply":"2023-01-05T00:54:32.273951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = Model(config) \nmodel.load_state_dict(torch.load(f'{config.model.name}/default/version_0/checkpoints/best_loss.ckpt')['state_dict'])\nmodel = model.cuda().eval()\nconfig.val_loader.batch_size = 16\ndatamodule = CancerfinderDataModule(train_df, val_df, config)","metadata":{"execution":{"iopub.status.busy":"2023-01-05T00:54:32.275531Z","iopub.status.idle":"2023-01-05T00:54:32.276269Z","shell.execute_reply.started":"2023-01-05T00:54:32.276029Z","shell.execute_reply":"2023-01-05T00:54:32.276052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images, grayscale_cams, preds, labels = model.check_gradcam(\n                                            datamodule.val_dataloader(), \n                                            target_layer=model.backbone.layers[-1].blocks[-1].norm1,\n                                            target_category=None,\n                                            reshape_transform=reshape_transform)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T22:20:35.120611Z","iopub.execute_input":"2022-12-25T22:20:35.121061Z","iopub.status.idle":"2022-12-25T22:20:37.209144Z","shell.execute_reply.started":"2022-12-25T22:20:35.12102Z","shell.execute_reply":"2022-12-25T22:20:37.207236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 12))\nfor it, (image, grayscale_cam, pred, label) in enumerate(zip(images, grayscale_cams, preds, labels)):\n    plt.subplot(4, 4, it + 1)\n    visualization = show_cam_on_image(image, grayscale_cam)\n    plt.imshow(visualization)\n    plt.title(f'pred: {pred:.1f} label: {label}')\n    plt.axis('off')","metadata":{"execution":{"iopub.status.busy":"2022-12-25T22:20:37.901601Z","iopub.execute_input":"2022-12-25T22:20:37.902047Z","iopub.status.idle":"2022-12-25T22:20:39.336363Z","shell.execute_reply.started":"2022-12-25T22:20:37.902004Z","shell.execute_reply":"2022-12-25T22:20:39.33522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize Results","metadata":{}},{"cell_type":"code","source":"from tensorboard.backend.event_processing.event_accumulator import EventAccumulator\n\npath = glob(f'./{config.model.name}/default/version_0/events*')[0]\nevent_acc = EventAccumulator(path, size_guidance={'scalars': 0})\nevent_acc.Reload()\n\nscalars = {}\nfor tag in event_acc.Tags()['scalars']:\n    events = event_acc.Scalars(tag)\n    scalars[tag] = [event.value for event in events]","metadata":{"execution":{"iopub.status.busy":"2022-12-25T21:58:54.341143Z","iopub.status.idle":"2022-12-25T21:58:54.341657Z","shell.execute_reply.started":"2022-12-25T21:58:54.341393Z","shell.execute_reply":"2022-12-25T21:58:54.341417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sns\nsns.set()\n\nplt.figure(figsize=(16, 6))\nplt.subplot(1, 2, 1)\nplt.plot(range(len(scalars['lr-AdamW'])), scalars['lr-AdamW'])\nplt.xlabel('epoch')\nplt.ylabel('lr')\nplt.title('adamw lr')\n\nplt.subplot(1, 2, 2)\nplt.plot(range(len(scalars['train_loss'])), scalars['train_loss'], label='train_loss')\nplt.plot(range(len(scalars['val_loss'])), scalars['val_loss'], label='val_loss')\nplt.legend()\nplt.ylabel('rmse')\nplt.xlabel('epoch')\nplt.title('train/val rmse')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-12-25T21:58:54.598549Z","iopub.execute_input":"2022-12-25T21:58:54.598879Z","iopub.status.idle":"2022-12-25T21:59:05.522934Z","shell.execute_reply.started":"2022-12-25T21:58:54.598844Z","shell.execute_reply":"2022-12-25T21:59:05.521089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('best_val_loss', min(scalars['val_loss']))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T21:59:05.523719Z","iopub.status.idle":"2022-12-25T21:59:05.524071Z","shell.execute_reply.started":"2022-12-25T21:59:05.523904Z","shell.execute_reply":"2022-12-25T21:59:05.523921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}