{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":8728463,"sourceType":"datasetVersion","datasetId":5238658},{"sourceId":184402550,"sourceType":"kernelVersion"},{"sourceId":184791767,"sourceType":"kernelVersion"}],"dockerImageVersionId":30733,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# RSNA2024 LSDC Submission Baseline\nIn the [previous notebook](https://www.kaggle.com/code/itsuki9180/rsna2024-lsdc-training-baseline), We trained the models.\n\nThis notebook will Let the model infer and make a submission.\n\n### My other Notebooks\n- [RSNA2024 LSDC Making Dataset](https://www.kaggle.com/code/itsuki9180/rsna2024-lsdc-making-dataset) \n- [RSNA2024 LSDC Training Baseline](https://www.kaggle.com/code/itsuki9180/rsna2024-lsdc-training-baseline) \n- [RSNA2024 LSDC Submission Baseline](https://www.kaggle.com/code/coderrkj/rsna2024-lsdc-submission-baseline) <- you're reading now","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# Import Libralies","metadata":{}},{"cell_type":"code","source":"import os\nimport gc\nimport sys\nfrom PIL import Image\nfrom tqdm.auto import tqdm\ntqdm.pandas()\nimport cv2\nimport math, random\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom glob import glob\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import KFold\n\nfrom collections import OrderedDict\n\nimport torch\nimport torch.nn.functional as F\nfrom torch import nn\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.optim import AdamW\n\nimport timm\nfrom timm.utils import ModelEmaV2\nfrom transformers import get_cosine_schedule_with_warmup\n\nimport albumentations as A\n\nfrom sklearn.model_selection import KFold\n\nimport re\nimport pydicom","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:08.089173Z","iopub.execute_input":"2024-06-22T17:11:08.089554Z","iopub.status.idle":"2024-06-22T17:11:17.070680Z","shell.execute_reply.started":"2024-06-22T17:11:08.089523Z","shell.execute_reply":"2024-06-22T17:11:17.069677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nN_WORKERS = os.cpu_count()\nUSE_AMP = True\nSEED = 8620\n\nIMG_SIZE = [512, 512]\nIN_CHANS = 30\nN_LABELS = 25\nN_CLASSES = 3 * N_LABELS\n\nN_FOLDS = 5\n\nMODEL_NAME = \"tf_efficientnet_b3.ns_jft_in1k\"\n\nBATCH_SIZE = 1","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:02.061726Z","iopub.execute_input":"2024-06-22T17:13:02.062479Z","iopub.status.idle":"2024-06-22T17:13:02.068137Z","shell.execute_reply.started":"2024-06-22T17:13:02.062447Z","shell.execute_reply":"2024-06-22T17:13:02.067150Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_PATH = Path('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification')\n\ndf = pd.read_csv(DATA_PATH / 'test_series_descriptions.csv')\nsample_sub = pd.read_csv(DATA_PATH / 'sample_submission.csv')\n\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.132375Z","iopub.execute_input":"2024-06-22T17:11:17.132640Z","iopub.status.idle":"2024-06-22T17:11:17.169277Z","shell.execute_reply.started":"2024-06-22T17:11:17.132617Z","shell.execute_reply":"2024-06-22T17:11:17.168336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LABELS = list(sample_sub.columns[1:])\nCONDITIONS = [\n    'spinal_canal_stenosis', \n    'left_neural_foraminal_narrowing', \n    'right_neural_foraminal_narrowing',\n    'left_subarticular_stenosis',\n    'right_subarticular_stenosis'\n]\n\nLEVELS = [\n    'l1_l2',\n    'l2_l3',\n    'l3_l4',\n    'l4_l5',\n    'l5_s1',\n]\nLABELS","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.171901Z","iopub.execute_input":"2024-06-22T17:11:17.172808Z","iopub.status.idle":"2024-06-22T17:11:17.179135Z","shell.execute_reply.started":"2024-06-22T17:11:17.172781Z","shell.execute_reply":"2024-06-22T17:11:17.178208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FAKE_TEST = False # len(sample_sub) <= 25","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.180396Z","iopub.execute_input":"2024-06-22T17:11:17.180728Z","iopub.status.idle":"2024-06-22T17:11:17.187550Z","shell.execute_reply.started":"2024-06-22T17:11:17.180686Z","shell.execute_reply":"2024-06-22T17:11:17.186674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def atoi(text):\n    return int(text) if text.isdigit() else text\n\ndef natural_keys(text):\n    return [ atoi(c) for c in re.split(r'(\\d+)', str(text)) ]","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.189033Z","iopub.execute_input":"2024-06-22T17:11:17.189410Z","iopub.status.idle":"2024-06-22T17:11:17.197125Z","shell.execute_reply.started":"2024-06-22T17:11:17.189378Z","shell.execute_reply":"2024-06-22T17:11:17.196327Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Dataset","metadata":{}},{"cell_type":"code","source":"class RSNA24TestDataset(Dataset):\n    def __init__(self, df, study_ids, transform=None, base_path=DATA_PATH / \"test_images\"):\n        self.df = df\n        self.study_ids = study_ids\n        self.transform = transform\n        self.base_path = Path(base_path)\n    \n    def __len__(self):\n        return len(self.study_ids)\n    \n    def get_img_paths(self, study_id, series_desc):\n        pdf = self.df[self.df['study_id']==study_id]\n        pdf_ = pdf[pdf['series_description']==series_desc]\n        allimgs = []\n        for i, row in pdf_.iterrows():\n            series_path = self.base_path / str(study_id) / str(row[\"series_id\"])\n            pimgs = sorted(series_path.glob(\"*.dcm\"), key=natural_keys) # TODO: check sort key validity\n            allimgs.extend(pimgs)\n            \n        return allimgs\n    \n    def read_dcm_ret_arr(self, src_path):\n        dicom_data = pydicom.dcmread(src_path)\n        image = dicom_data.pixel_array\n        image = (image - image.min()) / (image.max() - image.min() + 1e-6) * 255\n        img = cv2.resize(image, (IMG_SIZE[0], IMG_SIZE[1]),interpolation=cv2.INTER_CUBIC)\n        assert img.shape==(IMG_SIZE[0], IMG_SIZE[1])\n        return img\n\n    def __getitem__(self, idx):\n        x = np.zeros((IMG_SIZE[0], IMG_SIZE[1], IN_CHANS), dtype=np.uint8)\n        st_id = self.study_ids[idx]        \n        \n        # Sagittal T1\n        allimgs_st1 = self.get_img_paths(st_id, 'Sagittal T1')\n        if len(allimgs_st1)==0:\n            print(st_id, ': Sagittal T1, has no images')\n        \n        else:\n            step = len(allimgs_st1) / 10.0\n            st = len(allimgs_st1)/2.0 - 4.0*step\n            end = len(allimgs_st1)+0.0001\n            for j, i in enumerate(np.arange(st, end, step)):\n                try:\n                    ind2 = max(0, int((i-0.5001).round()))\n                    img = self.read_dcm_ret_arr(allimgs_st1[ind2])\n                    x[..., j] = img.astype(np.uint8)\n                except:\n                    print(f'failed to load on {st_id}, Sagittal T1')\n            \n        # Sagittal T2/STIR\n        allimgs_st2 = self.get_img_paths(st_id, 'Sagittal T2/STIR')\n        if len(allimgs_st2)==0:\n            print(st_id, ': Sagittal T2/STIR, has no images')\n            \n        else:\n            step = len(allimgs_st2) / 10.0\n            st = len(allimgs_st2)/2.0 - 4.0*step\n            end = len(allimgs_st2)+0.0001\n            for j, i in enumerate(np.arange(st, end, step)):\n                try:\n                    ind2 = max(0, int((i-0.5001).round()))\n                    img = self.read_dcm_ret_arr(allimgs_st2[ind2])\n                    x[..., j+10] = img.astype(np.uint8)\n                except:\n                    print(f'failed to load on {st_id}, Sagittal T2/STIR')\n            \n        # Axial T2\n        allimgs_at2 = self.get_img_paths(st_id, 'Axial T2')\n        if len(allimgs_at2)==0:\n            print(st_id, ': Axial T2, has no images')\n            \n        else:\n            step = len(allimgs_at2) / 10.0\n            st = len(allimgs_at2)/2.0 - 4.0*step\n            end = len(allimgs_at2)+0.0001\n\n            for j, i in enumerate(np.arange(st, end, step)):\n                try:\n                    ind2 = max(0, int((i-0.5001).round()))\n                    img = self.read_dcm_ret_arr(allimgs_at2[ind2])\n                    x[..., j+20] = img.astype(np.uint8)\n                except:\n                    print(f'failed to load on {st_id}, Axial T2')  \n            \n            \n        if self.transform is not None:\n            x = self.transform(image=x)['image']\n\n        x = x.transpose(2, 0, 1)\n                \n        return x, str(st_id)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.198398Z","iopub.execute_input":"2024-06-22T17:11:17.198664Z","iopub.status.idle":"2024-06-22T17:11:17.219235Z","shell.execute_reply.started":"2024-06-22T17:11:17.198641Z","shell.execute_reply":"2024-06-22T17:11:17.218442Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transforms_test = A.Compose([\n    A.Resize(IMG_SIZE[0], IMG_SIZE[1]),\n    A.Normalize(mean=0.5, std=0.5)\n])\nstudy_ids, base_path = list(df['study_id'].unique()), DATA_PATH / \"test_images\"\nprint(f\"Num of study_ids:\", len(study_ids))","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.220255Z","iopub.execute_input":"2024-06-22T17:11:17.220538Z","iopub.status.idle":"2024-06-22T17:11:17.235601Z","shell.execute_reply.started":"2024-06-22T17:11:17.220516Z","shell.execute_reply":"2024-06-22T17:11:17.234653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if FAKE_TEST:\n    df = pd.read_csv(DATA_PATH / 'train_series_descriptions.csv')\n    study_ids, base_path = list(df['study_id'].unique()), DATA_PATH / \"train_images\"\n    print(f\"Num of study_ids:\", len(study_ids))","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.236736Z","iopub.execute_input":"2024-06-22T17:11:17.237097Z","iopub.status.idle":"2024-06-22T17:11:17.244737Z","shell.execute_reply.started":"2024-06-22T17:11:17.237063Z","shell.execute_reply":"2024-06-22T17:11:17.243912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ds = RSNA24TestDataset(df, study_ids, transforms_test, base_path)\ntest_dl = DataLoader(\n    test_ds, \n    batch_size=1, \n    shuffle=False,\n    num_workers=N_WORKERS,\n    pin_memory=True,\n    drop_last=False\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.249067Z","iopub.execute_input":"2024-06-22T17:11:17.249341Z","iopub.status.idle":"2024-06-22T17:11:17.258719Z","shell.execute_reply.started":"2024-06-22T17:11:17.249319Z","shell.execute_reply":"2024-06-22T17:11:17.257958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Model","metadata":{}},{"cell_type":"code","source":"class RSNA24Model(nn.Module):\n    def __init__(self, model_name, in_c=30, n_classes=75, pretrained=True, features_only=False):\n        super().__init__()\n        self.model = timm.create_model(\n                                    model_name,\n                                    pretrained=pretrained, \n                                    features_only=features_only,\n                                    in_chans=in_c,\n                                    num_classes=n_classes,\n                                    global_pool='avg'\n                                    )\n    \n    def forward(self, x):\n        y = self.model(x)\n        return y","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:10.093523Z","iopub.execute_input":"2024-06-22T17:13:10.094159Z","iopub.status.idle":"2024-06-22T17:13:10.100558Z","shell.execute_reply.started":"2024-06-22T17:13:10.094129Z","shell.execute_reply":"2024-06-22T17:13:10.099524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load Models","metadata":{}},{"cell_type":"code","source":"CKPT_DIR = Path('/kaggle/input/rsna2024-lsdc-training-baseline/rsna24-results')\nCKPT_PATHS = sorted(CKPT_DIR.glob(\"best_wll_model_fold-*.pt\"))\n# CKPT_PATHS = [CKPT_DIR / \"best_wll_model_fold-0.pt\"]","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:11:17.270941Z","iopub.execute_input":"2024-06-22T17:11:17.271295Z","iopub.status.idle":"2024-06-22T17:11:17.286888Z","shell.execute_reply.started":"2024-06-22T17:11:17.271264Z","shell.execute_reply":"2024-06-22T17:11:17.286068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"models = []\nfor i, cp in enumerate(CKPT_PATHS):\n    print(f'loading {cp}...')\n    model = RSNA24Model(MODEL_NAME, IN_CHANS, N_CLASSES, pretrained=False)\n    model.load_state_dict(torch.load(cp))\n    model.eval()\n    model.half()\n    model.to(device)\n    models.append(model)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:18.590734Z","iopub.execute_input":"2024-06-22T17:13:18.591741Z","iopub.status.idle":"2024-06-22T17:13:22.746167Z","shell.execute_reply.started":"2024-06-22T17:13:18.591707Z","shell.execute_reply":"2024-06-22T17:13:22.745277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference loop","metadata":{}},{"cell_type":"code","source":"autocast = torch.cuda.amp.autocast(enabled=USE_AMP, dtype=torch.half)\ny_preds_list = []\ny_preds_per_model = [list() for _ in range(len(models))]\nrow_names = []\n\nwith tqdm(test_dl, leave=True) as pbar:\n    with torch.no_grad():\n        for idx, (x, si) in enumerate(pbar):\n            x = x.to(device)\n            pred_per_study = np.zeros((25, 3))\n            \n            for cond in CONDITIONS:\n                for level in LEVELS:\n                    row_names.append(si[0] + '_' + cond + '_' + level)\n            \n            with autocast:\n                for m_i, m in enumerate(models):\n                    y = m(x)[0]\n                    for col in range(N_LABELS):\n                        pred = y[col*3:col*3+3]\n                        y_pred = pred.float().softmax(0).cpu().numpy()\n                        y_preds_per_model[m_i].append(y_pred)\n                        pred_per_study[col] += y_pred / len(models)\n                y_preds_list.append(pred_per_study)\n\ny_preds = np.concatenate(y_preds_list, axis=0)\n\nfor m_i in range(len(models)):\n    y_preds_per_model[m_i] = np.stack(y_preds_per_model[m_i], axis=0)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:22.747681Z","iopub.execute_input":"2024-06-22T17:13:22.747948Z","iopub.status.idle":"2024-06-22T17:13:25.636074Z","shell.execute_reply.started":"2024-06-22T17:13:22.747925Z","shell.execute_reply":"2024-06-22T17:13:25.634979Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Make Submission","metadata":{}},{"cell_type":"code","source":"sub = pd.DataFrame()\nsub['row_id'] = row_names\nsub[LABELS] = y_preds\nsub.head(25)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:25.637941Z","iopub.execute_input":"2024-06-22T17:13:25.638316Z","iopub.status.idle":"2024-06-22T17:13:25.668451Z","shell.execute_reply.started":"2024-06-22T17:13:25.638250Z","shell.execute_reply":"2024-06-22T17:13:25.667351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.to_csv('submission.csv', index=False)\npd.read_csv('submission.csv').head()","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:25.670803Z","iopub.execute_input":"2024-06-22T17:13:25.671119Z","iopub.status.idle":"2024-06-22T17:13:25.691437Z","shell.execute_reply.started":"2024-06-22T17:13:25.671094Z","shell.execute_reply":"2024-06-22T17:13:25.690480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Axial only portion","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nN_WORKERS = os.cpu_count()\nUSE_AMP = True\nSEED = 8620\n\nIMG_SIZE = [512, 512]\nIN_CHANS = 30\nN_LABELS = 10\nN_CLASSES = 3 * N_LABELS\n\nN_FOLDS = 5\n\nMODEL_NAME = \"densenet169\"\n\nBATCH_SIZE = 1","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:25.692744Z","iopub.execute_input":"2024-06-22T17:13:25.693167Z","iopub.status.idle":"2024-06-22T17:13:25.699555Z","shell.execute_reply.started":"2024-06-22T17:13:25.693125Z","shell.execute_reply":"2024-06-22T17:13:25.698605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LABELS = list(sample_sub.columns[1:])\nCONDITIONS = [\n    'Left Subarticular Stenosis',\n    'Right Subarticular Stenosis'\n]\n\nLEVELS = [\n    'l1_l2',\n    'l2_l3',\n    'l3_l4',\n    'l4_l5',\n    'l5_s1',\n]\nLABELS","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:13:25.702684Z","iopub.execute_input":"2024-06-22T17:13:25.702978Z","iopub.status.idle":"2024-06-22T17:13:25.710544Z","shell.execute_reply.started":"2024-06-22T17:13:25.702950Z","shell.execute_reply":"2024-06-22T17:13:25.709712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNA24TestDataset(Dataset):\n    def __init__(self, df, study_ids, transform=None, base_path=DATA_PATH / \"test_images\"):\n        self.df = df\n        self.study_ids = study_ids\n        self.transform = transform\n        self.base_path = Path(base_path)\n    \n    def __len__(self):\n        return len(self.study_ids)\n    \n    def get_img_paths(self, study_id, series_desc):\n        pdf = self.df[self.df['study_id']==study_id]\n        pdf_ = pdf[pdf['series_description']==series_desc]\n        allimgs = []\n        for i, row in pdf_.iterrows():\n            series_path = self.base_path / str(study_id) / str(row[\"series_id\"])\n            pimgs = sorted(series_path.glob(\"*.dcm\"), key=natural_keys) # TODO: check sort key validity\n            allimgs.extend(pimgs)\n            \n        return allimgs\n    \n    def read_dcm_ret_arr(self, src_path):\n        dicom_data = pydicom.dcmread(src_path)\n        image = dicom_data.pixel_array\n        image = (image - image.min()) / (image.max() - image.min() + 1e-6) * 255\n        img = cv2.resize(image, (IMG_SIZE[0], IMG_SIZE[1]),interpolation=cv2.INTER_CUBIC)\n        assert img.shape==(IMG_SIZE[0], IMG_SIZE[1])\n        return img\n\n    def __getitem__(self, idx):\n        x = np.zeros((IMG_SIZE[0], IMG_SIZE[1], IN_CHANS), dtype=np.uint8)\n        st_id = self.study_ids[idx]        \n        \n        # Axial T2\n        allimgs_at2 = self.get_img_paths(st_id, 'Axial T2')\n        if len(allimgs_at2)==0:\n            print(st_id, ': Axial T2, has no images')\n            \n        else:\n            axt2_indexes = np.linspace(0, len(allimgs_at2), IN_CHANS, endpoint=False)\n            axt2_indexes = axt2_indexes.round().astype(int).clip(0, len(allimgs_at2) - 1)\n\n            for j, i in enumerate(axt2_indexes):\n                try:\n                    img = self.read_dcm_ret_arr(allimgs_at2[int(i)])\n                    x[..., j] = img.astype(np.uint8)\n                except:\n                    print(f'failed to load on {st_id}, Axial T2')  \n            \n            \n        if self.transform is not None:\n            x = self.transform(image=x)['image']\n\n        x = x.transpose(2, 0, 1)\n                \n        return x, str(st_id)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:45.589825Z","iopub.execute_input":"2024-06-22T17:14:45.590225Z","iopub.status.idle":"2024-06-22T17:14:45.607577Z","shell.execute_reply.started":"2024-06-22T17:14:45.590188Z","shell.execute_reply":"2024-06-22T17:14:45.606338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transforms_test = A.Compose([\n    A.Resize(IMG_SIZE[0], IMG_SIZE[1]),\n    A.Normalize(mean=0.5, std=0.5)\n])\nstudy_ids, base_path = list(df['study_id'].unique()), DATA_PATH / \"test_images\"\nprint(f\"Num of study_ids:\", len(study_ids))","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:45.609446Z","iopub.execute_input":"2024-06-22T17:14:45.609785Z","iopub.status.idle":"2024-06-22T17:14:45.626227Z","shell.execute_reply.started":"2024-06-22T17:14:45.609755Z","shell.execute_reply":"2024-06-22T17:14:45.625336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ds = RSNA24TestDataset(df, study_ids, transforms_test, base_path)\ntest_dl = DataLoader(\n    test_ds, \n    batch_size=1, \n    shuffle=False,\n    num_workers=N_WORKERS,\n    pin_memory=True,\n    drop_last=False\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:45.627573Z","iopub.execute_input":"2024-06-22T17:14:45.627906Z","iopub.status.idle":"2024-06-22T17:14:45.638216Z","shell.execute_reply.started":"2024-06-22T17:14:45.627877Z","shell.execute_reply":"2024-06-22T17:14:45.637317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNA24Model(nn.Module):\n    def __init__(self, model_name, in_c=30, n_classes=75, pretrained=True, features_only=False):\n        super().__init__()\n        self.model = timm.create_model(\n                                    model_name,\n                                    pretrained=pretrained, \n                                    features_only=features_only,\n                                    in_chans=in_c,\n                                    num_classes=n_classes,\n                                    global_pool='avg'\n                                    )\n    \n    def forward(self, x):\n        y = self.model(x)\n        return y","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:45.640387Z","iopub.execute_input":"2024-06-22T17:14:45.640975Z","iopub.status.idle":"2024-06-22T17:14:45.650039Z","shell.execute_reply.started":"2024-06-22T17:14:45.640942Z","shell.execute_reply":"2024-06-22T17:14:45.649243Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CKPT_DIR = Path('/kaggle/input/k/coderrkj/rsna2024-lsdc-training-baseline/rsna24-results')\nCKPT_PATHS = sorted(CKPT_DIR.glob(\"best_wll_model_fold-*.pt\"))\n\nmodels = []\nfor i, cp in enumerate(CKPT_PATHS):\n    print(f'loading {cp}...')\n    model = RSNA24Model(MODEL_NAME, IN_CHANS, N_CLASSES, pretrained=False)\n    model.load_state_dict(torch.load(cp))\n    model.eval()\n    model.half()\n    model.to(device)\n    models.append(model)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:45.651348Z","iopub.execute_input":"2024-06-22T17:14:45.651821Z","iopub.status.idle":"2024-06-22T17:14:54.095213Z","shell.execute_reply.started":"2024-06-22T17:14:45.651776Z","shell.execute_reply":"2024-06-22T17:14:54.094427Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"autocast = torch.cuda.amp.autocast(enabled=USE_AMP, dtype=torch.half)\ny_preds_list = []\ny_preds_per_model = [list() for _ in range(len(models))]\nrow_names = []\n\nwith tqdm(test_dl, leave=True) as pbar:\n    with torch.no_grad():\n        for idx, (x, si) in enumerate(pbar):\n            x = x.to(device)\n            pred_per_study = np.zeros((25, 3))\n            \n            for cond in CONDITIONS:\n                for level in LEVELS:\n                    row_names.append(si[0] + '_' + cond + '_' + level)\n            \n            with autocast:\n                for m_i, m in enumerate(models):\n                    y = m(x)[0]\n                    for col in range(N_LABELS):\n                        pred = y[col*3:col*3+3]\n                        y_pred = pred.float().softmax(0).cpu().numpy()\n                        y_preds_per_model[m_i].append(y_pred)\n                        pred_per_study[col] += y_pred / len(models)\n                y_preds_list.append(pred_per_study)\n\ny_preds = np.concatenate(y_preds_list, axis=0)\n\nfor m_i in range(len(models)):\n    y_preds_per_model[m_i] = np.stack(y_preds_per_model[m_i], axis=0)","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:54.096346Z","iopub.execute_input":"2024-06-22T17:14:54.096650Z","iopub.status.idle":"2024-06-22T17:14:55.697247Z","shell.execute_reply.started":"2024-06-22T17:14:54.096624Z","shell.execute_reply":"2024-06-22T17:14:55.695912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for idx, row_name in enumerate(row_names):\n    sub.loc[sub.row_id == row_name, LABELS] = y_preds[idx]","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:55.699500Z","iopub.execute_input":"2024-06-22T17:14:55.700483Z","iopub.status.idle":"2024-06-22T17:14:55.726545Z","shell.execute_reply.started":"2024-06-22T17:14:55.700428Z","shell.execute_reply":"2024-06-22T17:14:55.725572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.to_csv('submission.csv', index=False)\npd.read_csv('submission.csv').head()","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:15:37.749457Z","iopub.execute_input":"2024-06-22T17:15:37.749831Z","iopub.status.idle":"2024-06-22T17:15:37.769501Z","shell.execute_reply.started":"2024-06-22T17:15:37.749802Z","shell.execute_reply":"2024-06-22T17:15:37.768271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Conclusion\nWe created the dataset, performed training, and inference in this notebook. \n\nThis competition is a bit complicated to handle the dataset, so there may be a better way.\n\nI think there are many other areas to improve in my notebook. I hope you can learn from my notebook and get a better score.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport pandas.api.types\nimport sklearn.metrics\n\n\nclass ParticipantVisibleError(Exception):\n    pass\n\n\ndef get_condition(full_location: str) -> str:\n    # Given an input like spinal_canal_stenosis_l1_l2 extracts 'spinal'\n    for injury_condition in ['spinal', 'foraminal', 'subarticular']:\n        if injury_condition in full_location:\n            return injury_condition\n    raise ValueError(f'condition not found in {full_location}')\n\n\ndef score(\n        solution: pd.DataFrame,\n        submission: pd.DataFrame,\n        row_id_column_name: str,\n        any_severe_scalar: float\n    ) -> float:\n    '''\n    Pseudocode:\n    1. Calculate the sample weighted log loss for each medical condition:\n    2. Derive a new any_severe label.\n    3. Calculate the sample weighted log loss for the new any_severe label.\n    4. Return the average of all of the label group log losses as the final score, normalized for the number of columns in each group.\n       This mitigates the impact of spinal stenosis having only half as many columns as the other two conditions.\n    '''\n\n    target_levels = ['normal_mild', 'moderate', 'severe']\n\n    # Run basic QC checks on the inputs\n    if not pandas.api.types.is_numeric_dtype(submission[target_levels].values):\n        raise ParticipantVisibleError('All submission values must be numeric')\n\n    if not np.isfinite(submission[target_levels].values).all():\n        raise ParticipantVisibleError('All submission values must be finite')\n\n    if solution[target_levels].min().min() < 0:\n        raise ParticipantVisibleError('All labels must be at least zero')\n    if submission[target_levels].min().min() < 0:\n        raise ParticipantVisibleError('All predictions must be at least zero')\n\n    solution['study_id'] = solution['row_id'].apply(lambda x: x.split('_')[0])\n    solution['location'] = solution['row_id'].apply(lambda x: '_'.join(x.split('_')[1:]))\n    solution['condition'] = solution['row_id'].apply(get_condition)\n\n    del solution[row_id_column_name]\n    del submission[row_id_column_name]\n    assert sorted(submission.columns) == sorted(target_levels)\n\n    submission['study_id'] = solution['study_id']\n    submission['location'] = solution['location']\n    submission['condition'] = solution['condition']\n\n    condition_losses = []\n    condition_weights = []\n    for condition in ['spinal', 'foraminal', 'subarticular']:\n        condition_indices = solution.loc[solution['condition'] == condition].index.values\n        condition_loss = sklearn.metrics.log_loss(\n            y_true=solution.loc[condition_indices, target_levels].values,\n            y_pred=submission.loc[condition_indices, target_levels].values,\n            sample_weight=solution.loc[condition_indices, 'sample_weight'].values\n        )\n        condition_losses.append(condition_loss)\n        condition_weights.append(1)\n\n    any_severe_spinal_labels = pd.Series(solution.loc[solution['condition'] == 'spinal'].groupby('study_id')['severe'].max())\n    any_severe_spinal_weights = pd.Series(solution.loc[solution['condition'] == 'spinal'].groupby('study_id')['sample_weight'].max())\n    any_severe_spinal_predictions = pd.Series(submission.loc[submission['condition'] == 'spinal'].groupby('study_id')['severe'].max())\n    any_severe_spinal_loss = sklearn.metrics.log_loss(\n        y_true=any_severe_spinal_labels,\n        y_pred=any_severe_spinal_predictions,\n        sample_weight=any_severe_spinal_weights\n    )\n    condition_losses.append(any_severe_spinal_loss)\n    condition_weights.append(any_severe_scalar)\n    return np.average(condition_losses, weights=condition_weights)\n\n\ndef score_from_train(\n    submission: pd.DataFrame,  # Pass submission.csv as a DataFrame\n    train_df: pd.DataFrame,  # Pass train.csv as a DataFrame\n    row_id_column_name=\"row_id\",\n    any_severe_scalar=1.0,\n    sample_weights: dict[str, int]={\"normal_mild\": 1, \"moderate\": 2, \"severe\": 4},\n):\n    target_cols = list(sample_weights.keys())\n    pred = submission.copy() # Copy to prevent changes in original\n    # Normalize values to have a sum of 1.0\n    pred[target_cols] = pred[target_cols].div(pred[target_cols].sum(axis=1), axis=0)\n\n    # Index the study_id in train_df\n    indexed_train_df = train_df.set_index(\"study_id\", verify_integrity=True)\n\n    row_ids = pred[row_id_column_name]\n    study_ids = row_ids.apply(lambda x: x.split('_')[0])\n    locations = row_ids.apply(lambda x: '_'.join(x.split('_')[1:]))\n\n    solution_data = np.zeros_like(pred[target_cols].values)\n    sample_weight_list = []\n    nan_row_ids = set()\n    for idx, (row, study_id, location) in enumerate(zip(row_ids, study_ids, locations)):\n        severity = str(indexed_train_df.at[int(study_id), location]).replace(\"/\", \"_\").lower()\n        if severity in sample_weights:\n            solution_data[idx, target_cols.index(severity)] = 1.0\n            sample_weight_list.append(sample_weights[severity])\n        else:\n            solution_data[idx] = np.nan\n            nan_row_ids.add(row)\n            sample_weight_list.append(np.nan)\n\n    solution = pd.DataFrame({\n        row_id_column_name: pred[row_id_column_name],\n        \"sample_weight\": sample_weight_list\n    })\n    solution[target_cols] = solution_data\n\n    # Change row_ids in nan_row_ids to np.nan\n    pred.loc[pred[row_id_column_name].isin(nan_row_ids), target_cols] = np.nan\n    # Remove nan rows and pass copy to score function\n    # score from https://www.kaggle.com/code/metric/rsna-lumbar-metric-71549?scriptVersionId=181722791 (Version 10)\n    return score(solution.dropna().copy(), pred.dropna().copy(), row_id_column_name, any_severe_scalar)\n\n\nif FAKE_TEST:\n    print(score_from_train(pd.read_csv(\"submission.csv\"), pd.read_csv(DATA_PATH / \"train.csv\")))","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:55.729210Z","iopub.execute_input":"2024-06-22T17:14:55.729534Z","iopub.status.idle":"2024-06-22T17:14:55.758556Z","shell.execute_reply.started":"2024-06-22T17:14:55.729509Z","shell.execute_reply":"2024-06-22T17:14:55.757657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if FAKE_TEST:\n    from itertools import product\n\n    combinations = [[i for i, val in enumerate(p) if val] for p in product(*[(0,1)]*len(models))][1:-1]\n\n    for indices in sorted(combinations):\n        avg_preds = np.zeros_like(y_preds)\n        for idx in indices:\n            avg_preds += y_preds_per_model[idx] / len(indices)\n        submission = pd.DataFrame()\n        submission['row_id'] = row_names\n        submission[LABELS] = avg_preds\n        print(indices, \":\", score_from_train(submission, pd.read_csv(DATA_PATH / \"train.csv\")))","metadata":{"execution":{"iopub.status.busy":"2024-06-22T17:14:55.759664Z","iopub.execute_input":"2024-06-22T17:14:55.760022Z","iopub.status.idle":"2024-06-22T17:14:55.771465Z","shell.execute_reply.started":"2024-06-22T17:14:55.759997Z","shell.execute_reply":"2024-06-22T17:14:55.770555Z"},"trusted":true},"execution_count":null,"outputs":[]}]}