{"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":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:30px\">\n🦴 Pytorch EfficientNetV2 vertebrae detection (acc: 0.95) 🦴\n</div>\n\n<div class=\"alert alert-block alert-danger\" style=\"text-align:center; font-size:20px;\">\n    ❤️ Dont forget to ▲upvote▲ if you find this notebook usefull!  ❤️\n</div>\n\nThis notebook generates a dataset with C1-C7 vertebrae detections using bare-bones Pytorch and EfficientNet-V2 model.\nHere we solve the most simple possible problem - we directly predict existence of vertebrae in the image instead of running a full-fledged pixel segmentation of scans.\n\n\n![vertebrae_prediction.drawio.png](https://iili.io/4TBrN4.png)\n\n\nCorrect vertebrae labels are essential when training the fracture prediction model. Models trained in this notebook could also be used directly in the submission.\n\n\n1. First we extract vertebrae targets from segmentation files. This is already done in ../input/rsna-2022-spine-fracture-detection-metadata dataset.\n2. Next we build an EfficientNet-V2 based multi-label classification models.\n3. Finally, we average N=5 models to produce vertebrae predictions.\n\n## Results\nResults were tested with groupped k-fold cross validation with grouping by StudyInstanceUID. It's very important to evaluate on StudyInstanceUID instances outside of the training set, because slices within the same StudyInstanceUID are highly correlated and results otherwise will be unrealistic.\n\nHere we compare performance of this notebook with the simple detection using RandomForestClassifier features only that is done in [\"Extracting Vertebrae C1, ..., C7\"](https://www.kaggle.com/code/samuelcortinhas/extracting-vertebrae-c1-c7) notebook:\n\n|                                                                                                              | CV accuracy |\n|--------------------------------------------------------------------------------------------------------------|-------------|\n| Effnetv2                                                                                                     | **0.95**        |\n| [\"Extracting Vertebrae C1, ..., C7\"](https://www.kaggle.com/code/samuelcortinhas/extracting-vertebrae-c1-c7) | 0.88        |\n\n\n## About EfficientNet-V2\n\nEfficientNetV2 is a new family of convolutional networks that have faster training speed and better parameter efficiency than previous models. To develop these models, a combination of training-aware neural architecture search and scaling was used to jointly optimize\ntraining speed and parameter efficiency. The models were searched from the search space enriched\nwith new ops such as Fused-MBConv. Experiments show that EfficientNetV2 models train much faster than state-of-the-art models while being up to 6.8x smaller.\n\nWith progressive learning, our EfficientNetV2 significantly outperforms previous models on ImageNet and CIFAR/Cars/Flowers datasets. By\npretraining on the same ImageNet21k, EfficientNetV2 achieves 87.3% top-1 accuracy on ImageNet ILSVRC2012, outperforming the recent ViT by 2.0% accuracy while training 5x-11x faster using the same computing resources\n\n![Screenshot from 2022-08-26 21-20-36.png](https://miro.medium.com/max/922/1*rZtosLGF5qx1MmRILfKgdA.png)","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:25px\">\n🦴 1. Imports, constants, dependencies 🦴\n</div>","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"code","source":"from typing import List\n\ntry:\n    import pylibjpeg\nexcept:\n    # Offline dependencies for jpeg data extraction and torchvisioin 0.13 (required for EfficientNetV2)\n    !mkdir -p /root/.cache/torch/hub/checkpoints/\n    !cp ../input/rsna-2022-whl/efficientnet_v2_s-dd5fe13b.pth  /root/.cache/torch/hub/checkpoints/\n    !pip install /kaggle/input/rsna-2022-whl/{pydicom-2.3.0-py3-none-any.whl,pylibjpeg-1.4.0-py3-none-any.whl,python_gdcm-3.0.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl}\n    !pip install /kaggle/input/rsna-2022-whl/{torch-1.12.1-cp37-cp37m-manylinux1_x86_64.whl,torchvision-0.13.1-cp37-cp37m-manylinux1_x86_64.whl}","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:10.525296Z","iopub.execute_input":"2022-08-27T16:12:10.526381Z","iopub.status.idle":"2022-08-27T16:12:10.538974Z","shell.execute_reply.started":"2022-08-27T16:12:10.526333Z","shell.execute_reply":"2022-08-27T16:12:10.537996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport os\n\nimport cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport pydicom as dicom\nimport torch\nimport torchvision as tv\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.model_selection import GroupKFold, KFold\nfrom torch.cuda.amp import GradScaler\nfrom torch.cuda.amp import autocast\nfrom torchvision.models.feature_extraction import create_feature_extractor\nfrom tqdm.notebook import tqdm\n\nimport wandb\n\npd.set_option('display.max_rows', 1000)\npd.set_option('display.max_columns', 1000)\n\n\n# Effnet\nWEIGHTS = tv.models.efficientnet.EfficientNet_V2_S_Weights.DEFAULT\n\nTRAIN_IMAGES_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/train_images'\nTEST_IMAGES_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/test_images'\nMETADATA_PATH = '../input/rsna-2022-spine-fracture-detection-metadata'\nEFFNET_CHECKPOINTS_PATH = '../input/vertebrae-detection-checkpoints'\n\nEFFNET_MAX_TRAIN_BATCHES = 10000\nEFFNET_MAX_EVAL_BATCHES = 1000\nONE_CYCLE_MAX_LR = 0.0004\nONE_CYCLE_PCT_START = 0.3\nSAVE_CHECKPOINT_EVERY_STEP = 500\nN_MODELS_FOR_INFERENCE = 2\n\n\n# Common\n# os.environ[\"WANDB_MODE\"] = \"offline\"\nos.environ[\"WANDB_MODE\"] = \"online\"\n\nif os.environ[\"WANDB_MODE\"] == \"online\":\n    try:\n        from kaggle_secrets import UserSecretsClient\n        os.environ['WANDB_API_KEY'] = UserSecretsClient().get_secret(\"WANDB_API_KEY\")\n    except:\n        #  We are running on a laptop\n        print('running locally')\n        TRAIN_IMAGES_PATH = '/mnt/rsna2022/train_images'\n        TEST_IMAGES_PATH = '/mnt/rsna2022/test_images'\n        EFFNET_CHECKPOINTS_PATH = 'seg_checkpoints'\n        METADATA_PATH = '~/Downloads/'\n        os.environ['WANDB_API_KEY'] = 'yourkeyhere'\n\nDEVICE='cuda' if torch.cuda.is_available() else 'cpu'\nif DEVICE == 'cuda':\n    BATCH_SIZE = 32\nelse:\n    BATCH_SIZE = 2\nN_FOLDS = 5","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:10.728027Z","iopub.execute_input":"2022-08-27T16:12:10.728379Z","iopub.status.idle":"2022-08-27T16:12:10.983844Z","shell.execute_reply.started":"2022-08-27T16:12:10.728348Z","shell.execute_reply":"2022-08-27T16:12:10.982865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:25px\">\n    🦴 2. Vertebrae detection dataset\n</div>","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"code","source":"def load_dicom(path):\n    \"\"\"\n    This supports loading both regular and compressed JPEG images. \n    See the first sell with `pip install` commands for the necessary dependencies\n    \"\"\"\n    img=dicom.dcmread(path)\n    img.PhotometricInterpretation = 'YBR_FULL'\n    data = img.pixel_array\n    data = data - np.min(data)\n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data=(data * 255).astype(np.uint8)\n    return cv2.cvtColor(data, cv2.COLOR_GRAY2RGB), img\n\n\nim, meta = load_dicom(f'{TRAIN_IMAGES_PATH}/1.2.826.0.1.3680043.10001/1.dcm')\nplt.figure()\nplt.imshow(im)\nplt.title('regular image')\n\nim, meta = load_dicom(f'{TRAIN_IMAGES_PATH}/1.2.826.0.1.3680043.10014/1.dcm')\nplt.figure()\nplt.imshow(im)\nplt.title('jpeg')","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:11.135146Z","iopub.execute_input":"2022-08-27T16:12:11.135694Z","iopub.status.idle":"2022-08-27T16:12:11.636245Z","shell.execute_reply.started":"2022-08-27T16:12:11.135645Z","shell.execute_reply":"2022-08-27T16:12:11.635148Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\ndf_seg = pd.read_csv(f'{METADATA_PATH}/meta_segmentation.csv')\n\nsplit = GroupKFold(N_FOLDS)\nfor k, (train_idx, test_idx) in enumerate(split.split(df_seg, groups=df_seg.StudyInstanceUID)):\n    df_seg.loc[test_idx, 'split'] = k\n\nsplit = KFold(N_FOLDS)\nfor k, (train_idx, test_idx) in enumerate(split.split(df_seg)):\n    df_seg.loc[test_idx, 'random_split'] = k\n\nslice_max_seg = df_seg.groupby('StudyInstanceUID')['Slice'].max().to_dict()\ndf_seg['SliceRatio'] = 0\ndf_seg['SliceRatio'] = df_seg['Slice'] / df_seg['StudyInstanceUID'].map(slice_max_seg)\n\ndf_seg.sample(10)","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:11.638603Z","iopub.execute_input":"2022-08-27T16:12:11.638975Z","iopub.status.idle":"2022-08-27T16:12:11.72552Z","shell.execute_reply.started":"2022-08-27T16:12:11.638938Z","shell.execute_reply":"2022-08-27T16:12:11.724448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class VertebraeSegmentDataSet(torch.utils.data.Dataset):\n    def __init__(self, df, path, transforms=None):\n        super().__init__()\n        self.df = df\n        self.path = path\n        self.transforms = transforms\n\n    def __getitem__(self, i):\n        path = os.path.join(self.path, self.df.iloc[i].StudyInstanceUID, f'{self.df.iloc[i].Slice}.dcm')\n        try:\n            img = load_dicom(path)[0]\n            img = np.transpose(img, (2, 0, 1))  # Pytorch uses (batch, channel, height, width) order. Converting (height, width, channel) -> (channel, height, width)\n            if self.transforms is not None:\n                img = self.transforms(torch.as_tensor(img))\n        except Exception as ex:\n            print(ex)\n            return None\n\n        if 'C1' in self.df.columns:\n            vert_targets = torch.as_tensor(self.df.iloc[i][['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']].astype('float32').values)\n            return img, vert_targets\n        return img\n\n    def __len__(self):\n        return len(self.df)\n\n\nds_seg = VertebraeSegmentDataSet(df_seg, TRAIN_IMAGES_PATH, WEIGHTS.transforms())\nX, y = ds_seg[300]\nX.shape, y.shape","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:11.727239Z","iopub.execute_input":"2022-08-27T16:12:11.72762Z","iopub.status.idle":"2022-08-27T16:12:11.768788Z","shell.execute_reply.started":"2022-08-27T16:12:11.727582Z","shell.execute_reply":"2022-08-27T16:12:11.767886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:25px\">\n    🦴 3. Vertebrae detection model\n</div>\n\nNote how we extract 'flatten' layer using introspection capabilities of Pytorch (create_feature_extractor)","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"code","source":"class SegEffnetModel(torch.nn.Module):\n    def __init__(self):\n        super().__init__()\n        effnet = tv.models.efficientnet_v2_s(weights=WEIGHTS)\n        self.model = create_feature_extractor(effnet, ['flatten'])\n        self.nn_vertebrae = torch.nn.Sequential(\n            torch.nn.Linear(1280, 7),\n        )\n\n    def forward(self, x):\n        # returns logits\n        x = self.model(x)['flatten']\n        return self.nn_vertebrae(x)\n\n    def predict(self, x):\n        pred = self.forward(x)\n        return torch.sigmoid(pred)\n\n# quick test\nmodel = SegEffnetModel()\nmodel.predict(torch.randn(1, 3, 512, 512))\ndel model","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:12.104185Z","iopub.execute_input":"2022-08-27T16:12:12.104854Z","iopub.status.idle":"2022-08-27T16:12:14.260402Z","shell.execute_reply.started":"2022-08-27T16:12:12.104817Z","shell.execute_reply":"2022-08-27T16:12:14.259394Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:25px\">\n    🦴 4. Model training/evaluation\n</div>\n\n* We save/load pytorch models manually in `save_model`, `load_model`\n* We estimate accuracy metric in `evaluate_segeffnet`\n* 5 models are trained in total with GroupKFold.\n* OneCycleLR scheduler is used as it is one of the most compute efficient schedulers for 1 epoch training with small amount of data.\n* Mixed precision is used to speed up training on Ampere GPUs with fp16.\n* wandb inline and remote metrics are generated","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"code","source":"def gc_collect():\n    gc.collect()\n    torch.cuda.empty_cache()","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:17.120701Z","iopub.execute_input":"2022-08-27T16:12:17.121817Z","iopub.status.idle":"2022-08-27T16:12:17.128316Z","shell.execute_reply.started":"2022-08-27T16:12:17.121766Z","shell.execute_reply":"2022-08-27T16:12:17.127348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def filter_nones(b):\n    return torch.utils.data.default_collate([v for v in b if v is not None])","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:17.301786Z","iopub.execute_input":"2022-08-27T16:12:17.302744Z","iopub.status.idle":"2022-08-27T16:12:17.31002Z","shell.execute_reply.started":"2022-08-27T16:12:17.302692Z","shell.execute_reply":"2022-08-27T16:12:17.307504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def save_model(name, model, optim, scheduler):\n    torch.save({\n        'model': model.state_dict(),\n        'optim': optim.state_dict(),\n        'scheduler': scheduler\n    }, f'{name}.tph')\n\ndef load_model(model, name, path='.'):\n    data = torch.load(os.path.join(path, f'{name}.tph'), map_location=DEVICE)\n    model.load_state_dict(data['model'])\n    optim = torch.optim.Adam(model.parameters())\n    optim.load_state_dict(data['optim'])\n    return model, optim, data['scheduler']\n\n# quick test\nmodel = torch.nn.Linear(2, 1)\noptim = torch.optim.Adam(model.parameters())\nsave_model('testmodel', model, optim, None)\n\nmodel1, optim1, scheduler1 = load_model(torch.nn.Linear(2, 1), 'testmodel')\nassert torch.all(next(iter(model1.parameters())) == next(iter(model.parameters()))).item(), \"Loading/saving is inconsistent!\"","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:17.681975Z","iopub.execute_input":"2022-08-27T16:12:17.682357Z","iopub.status.idle":"2022-08-27T16:12:17.694108Z","shell.execute_reply.started":"2022-08-27T16:12:17.682316Z","shell.execute_reply":"2022-08-27T16:12:17.693192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def evaluate_segeffnet(model: SegEffnetModel, ds, max_batches=1e9, shuffle=False):\n    torch.manual_seed(42)\n    model = model.to(DEVICE)\n    dl_test = torch.utils.data.DataLoader(ds, batch_size=BATCH_SIZE, shuffle=shuffle, num_workers=os.cpu_count(), collate_fn=filter_nones)\n    with torch.no_grad():\n        model.eval()\n        pred = []\n        y = []\n        progress = tqdm(dl_test, desc='Eval', miniters=100)\n        for i, (X, y_vert) in enumerate(progress):\n            with autocast():\n                y_vert_pred = model.predict(X.to(DEVICE))\n            pred.append(y_vert_pred.cpu().numpy())\n            y.append(y_vert.numpy())\n            acc = np.mean(np.mean((pred[-1] > 0.5) == y[-1], axis=0))\n            progress.set_description(f'Eval acc: {acc:.02f}')\n            if i >= max_batches:\n                break\n        pred = np.concatenate(pred)\n        y = np.concatenate(y)\n        acc = np.mean(np.mean((pred > 0.5) == y, axis=0))\n        return acc, pred","metadata":{"pycharm":{"name":"#%%\n"},"execution":{"iopub.status.busy":"2022-08-27T16:12:18.327201Z","iopub.execute_input":"2022-08-27T16:12:18.327905Z","iopub.status.idle":"2022-08-27T16:12:18.337397Z","shell.execute_reply.started":"2022-08-27T16:12:18.327869Z","shell.execute_reply":"2022-08-27T16:12:18.336264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%wandb\n\n\ndef train_segeffnet(ds_train, ds_eval, logger, name):\n    torch.manual_seed(42)\n    dl_train = torch.utils.data.DataLoader(ds_train, batch_size=BATCH_SIZE, shuffle=True, num_workers=os.cpu_count(), collate_fn=filter_nones)\n\n\n    model = SegEffnetModel().to(DEVICE)\n    optim = torch.optim.Adam(model.parameters())\n    scheduler = torch.optim.lr_scheduler.OneCycleLR(optim, max_lr=ONE_CYCLE_MAX_LR, epochs=1, steps_per_epoch=min(EFFNET_MAX_TRAIN_BATCHES, len(dl_train)), pct_start=ONE_CYCLE_PCT_START)\n    model.train()\n    scaler = GradScaler()\n\n    progress = tqdm(dl_train, desc='Train', miniters=10)\n    for batch_idx, (X,  y_vert) in enumerate(progress):\n\n        if batch_idx % SAVE_CHECKPOINT_EVERY_STEP == 0 and EFFNET_MAX_EVAL_BATCHES > 0:\n            eval_loss = evaluate_segeffnet(model, ds_eval, max_batches=EFFNET_MAX_EVAL_BATCHES, shuffle=True)[0]\n            model.train()\n            if logger is not None:\n                logger.log({'eval_acc': eval_loss})\n            if batch_idx > 0:  # don't save untrained model\n                save_model(name, model, optim, scheduler)\n\n        if batch_idx >= EFFNET_MAX_TRAIN_BATCHES:\n            break\n\n        optim.zero_grad()\n        with autocast():\n            y_vert_pred = model.forward(X.to(DEVICE))\n            loss = torch.nn.functional.binary_cross_entropy_with_logits(y_vert_pred, y_vert.to(DEVICE))\n\n            if np.isinf(loss.item()) or np.isnan(loss.item()):\n                print(f'Bad loss, skipping the batch {batch_idx}')\n                del y_vert_pred, loss\n                gc_collect()\n                continue\n\n        scaler.scale(loss).backward()\n        scaler.step(optim)\n        scaler.update()\n        scheduler.step()\n\n        progress.set_description(f'Train loss: {loss.item():.02f}')\n        if logger is not None:\n            logger.log({'loss': loss.item(), 'lr': scheduler.get_last_lr()[0]})\n\n\n    eval_loss = evaluate_segeffnet(model, ds_eval, max_batches=EFFNET_MAX_EVAL_BATCHES, shuffle=True)[0]\n    if logger is not None:\n        logger.log({'eval_acc': eval_loss})\n\n    save_model(name, model, optim, scheduler)\n    return model\n\n\nseg_models = []\nfor fold in range(N_FOLDS):\n    fname = os.path.join(f'{EFFNET_CHECKPOINTS_PATH}/segeffnetv2-f{fold}.tph')\n    if os.path.exists(fname):\n        print(f'Found cached model {fname}')\n        seg_models.append(load_model(SegEffnetModel(), f'segeffnetv2-f{fold}', EFFNET_CHECKPOINTS_PATH)[0].to(DEVICE))\n    else:\n        with wandb.init(project='RSNA-2022', name=f'SegEffNet-v2-fold{fold}') as run:\n            gc_collect()\n            ds_train = VertebraeSegmentDataSet(df_seg.query('split != @fold'), TRAIN_IMAGES_PATH, WEIGHTS.transforms())\n            ds_eval = VertebraeSegmentDataSet(df_seg.query('split == @fold'), TRAIN_IMAGES_PATH, WEIGHTS.transforms())\n            train_segeffnet(ds_train, ds_eval, run, f'segeffnetv2-f{fold}')","metadata":{"collapsed":false,"pycharm":{"name":"#%%wandb\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T16:12:24.816851Z","iopub.execute_input":"2022-08-27T16:12:24.817222Z","iopub.status.idle":"2022-08-27T16:12:42.844104Z","shell.execute_reply.started":"2022-08-27T16:12:24.817168Z","shell.execute_reply":"2022-08-27T16:12:42.843083Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with tqdm(seg_models, desc='Fold') as progress:\n    for fold, model in enumerate(progress):\n        ds = VertebraeSegmentDataSet(df_seg.query('split == @fold'), TRAIN_IMAGES_PATH, WEIGHTS.transforms())\n        acc, pred = evaluate_segeffnet(model, ds, max_batches=1e9, shuffle=False)\n        df_seg.loc[df_seg[df_seg.split == fold].index, ['C1_pred', 'C2_pred', 'C3_pred', 'C4_pred', 'C5_pred', 'C6_pred', 'C7_pred']] = pred\n        progress.set_description(f'Acc: {acc}')","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T16:12:42.846113Z","iopub.execute_input":"2022-08-27T16:12:42.846548Z","iopub.status.idle":"2022-08-27T16:22:28.741754Z","shell.execute_reply.started":"2022-08-27T16:12:42.846507Z","shell.execute_reply":"2022-08-27T16:22:28.740361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = (df_seg[[f'C{i}_pred' for i in range(1, 8)]] > 0.5).values == (df_seg[[f'C{i}' for i in range(1, 8)]] > 0.5).values\nprint('Effnetv2 accuracy per vertebrae', np.mean(acc, axis=0))\nprint('Effnetv2 average accuracy', np.mean(np.mean(acc, axis=0)))","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T16:22:28.74364Z","iopub.execute_input":"2022-08-27T16:22:28.744155Z","iopub.status.idle":"2022-08-27T16:22:28.764125Z","shell.execute_reply.started":"2022-08-27T16:22:28.74411Z","shell.execute_reply":"2022-08-27T16:22:28.762877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-success\" style=\"font-size:25px\">\n    🦴 6. Inference for 100% of training slices\n</div>\n\nHere we'll average outputs of 5 models and store predictions for all train samples","metadata":{"pycharm":{"name":"#%% md\n"}}},{"cell_type":"code","source":"def predict_vertebrae(df, seg_models: List[SegEffnetModel]):\n    df = df.copy()\n    ds = VertebraeSegmentDataSet(df, TRAIN_IMAGES_PATH, WEIGHTS.transforms())\n    dl_test = torch.utils.data.DataLoader(ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=os.cpu_count(), collate_fn=filter_nones)\n    predictions = []\n    with torch.no_grad():\n        with tqdm(dl_test, desc='Eval', miniters=10) as progress:\n            for i, X in enumerate(progress):\n                with autocast():\n                    pred = torch.zeros(len(X), 7).to(DEVICE)\n                    for model in seg_models:\n                        pred += model.predict(X.to(DEVICE)) / len(seg_models)\n                    predictions.append(pred)\n    predictions = torch.concat(predictions).cpu().numpy()\n    return predictions","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T08:58:36.40244Z","iopub.status.idle":"2022-08-27T08:58:36.403448Z","shell.execute_reply.started":"2022-08-27T08:58:36.403167Z","shell.execute_reply":"2022-08-27T08:58:36.403196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train = pd.read_csv(os.path.join(METADATA_PATH, 'meta_train_clean.csv'))","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T08:58:36.405098Z","iopub.status.idle":"2022-08-27T08:58:36.406141Z","shell.execute_reply.started":"2022-08-27T08:58:36.405857Z","shell.execute_reply":"2022-08-27T08:58:36.405884Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for uid in np.random.choice(df_train.StudyInstanceUID, 3):\n    pred = predict_vertebrae(df_train.query('StudyInstanceUID == @uid'), seg_models[:2])\n    plt.figure(figsize=(20, 5))\n    plt.plot(pred)\n    plt.title(f'Vertebrae prediction by slice for UID: {uid}')","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T08:58:36.412429Z","iopub.status.idle":"2022-08-27T08:58:36.412923Z","shell.execute_reply.started":"2022-08-27T08:58:36.412667Z","shell.execute_reply":"2022-08-27T08:58:36.412691Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = predict_vertebrae(df_train, seg_models[:N_MODELS_FOR_INFERENCE])","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T08:58:36.414692Z","iopub.status.idle":"2022-08-27T08:58:36.415197Z","shell.execute_reply.started":"2022-08-27T08:58:36.414925Z","shell.execute_reply":"2022-08-27T08:58:36.414949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train[[f'C{i}' for i in range(1, 8)]] = pred\ndf_train.to_csv('train_segmented.csv', index=False)\n\n# you can find checkpoints along with the output dataset in https://www.kaggle.com/datasets/vslaykovsky/vertebrae-detection-checkpoints","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-08-27T08:58:36.416855Z","iopub.status.idle":"2022-08-27T08:58:36.417796Z","shell.execute_reply.started":"2022-08-27T08:58:36.417531Z","shell.execute_reply":"2022-08-27T08:58:36.417557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train","metadata":{"collapsed":false,"pycharm":{"name":"#%%\n"},"jupyter":{"outputs_hidden":false}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-block alert-danger\" style=\"text-align:center; font-size:20px;\">\n    ❤️ Dont forget to ▲upvote▲ if you find this notebook usefull!  ❤️\n</div>","metadata":{"execution":{"iopub.status.busy":"2022-08-27T08:42:09.429849Z","iopub.execute_input":"2022-08-27T08:42:09.430318Z","iopub.status.idle":"2022-08-27T08:42:09.460306Z","shell.execute_reply.started":"2022-08-27T08:42:09.430229Z","shell.execute_reply":"2022-08-27T08:42:09.458778Z"},"pycharm":{"name":"#%% md\n"}}}]}