{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"},{"sourceId":8727685,"sourceType":"datasetVersion","datasetId":5236783},{"sourceId":8728463,"sourceType":"datasetVersion","datasetId":5238658},{"sourceId":8761420,"sourceType":"datasetVersion","datasetId":5264030},{"sourceId":9231709,"sourceType":"datasetVersion","datasetId":5583775},{"sourceId":9235934,"sourceType":"datasetVersion","datasetId":5586514},{"sourceId":9238129,"sourceType":"datasetVersion","datasetId":5587999},{"sourceId":9238150,"sourceType":"datasetVersion","datasetId":5588015},{"sourceId":9273089,"sourceType":"datasetVersion","datasetId":5611997},{"sourceId":9306154,"sourceType":"datasetVersion","datasetId":5635376},{"sourceId":9365806,"sourceType":"datasetVersion","datasetId":5679443},{"sourceId":9409759,"sourceType":"datasetVersion","datasetId":5577143},{"sourceId":184402550,"sourceType":"kernelVersion"},{"sourceId":192715298,"sourceType":"kernelVersion"},{"sourceId":193161758,"sourceType":"kernelVersion"},{"sourceId":193298353,"sourceType":"kernelVersion"},{"sourceId":193335312,"sourceType":"kernelVersion"},{"sourceId":193338638,"sourceType":"kernelVersion"},{"sourceId":193417638,"sourceType":"kernelVersion"},{"sourceId":100132,"sourceType":"modelInstanceVersion","modelInstanceId":64905,"modelId":89293},{"sourceId":111113,"sourceType":"modelInstanceVersion","modelInstanceId":85952,"modelId":84065}],"dockerImageVersionId":30762,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"LAUNCH_VARIANT = 'option 28'","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Germany [LSDC Yolo Approach](https://www.kaggle.com/code/namgalielei/lsdc-yolo-approach)\n### [Liam Nguyen](https://www.kaggle.com/namgalielei)","metadata":{}},{"cell_type":"code","source":"!pip install -q --no-index --find-links /kaggle/input/ultralytics ultralytics","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport pydicom\nfrom PIL import Image\nimport numpy as np\nfrom multiprocessing import Pool, cpu_count\n\nimport sklearn.metrics\nimport torch\nimport cv2\nimport numpy as np \nimport pandas as pd \nfrom tqdm.auto import tqdm","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EVAL = False # Change to True to compute the validation score\nIMG_DIR = '/images'\nFOLD = 0\nSAMPLE = False # True for quick debugging\nSEVERITIES = ['Normal/Mild', 'Moderate', 'Severe']\nLEVELS = ['l1_l2', 'l2_l3', 'l3_l4', 'l4_l5', 'l5_s1']\n\nSCS_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-scs/lsdc_yolov8/train/weights/best.pt']\n\nSS_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-ss/lsdc_yolov8/train/weights/best.pt',\n             '/kaggle/input/lsdc-yolo-ssv3/best.pt']\n\nNFN_WEIGHTS = ['/kaggle/input/lsdc-train-yolo-nfn/lsdc_yolov8/train/weights/best.pt',\n              '/kaggle/input/lsdc-yolo-nfnv3/best.pt']","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    import sys\n    sys.path.append('/kaggle/input/lsdc-utils')\n    from metrics import score as lsdc_scoring","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_val_df = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train.csv')","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    train_xy = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_label_coordinates.csv')\n    des = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/train_series_descriptions.csv')\nelse:    \n    des = pd.read_csv('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_series_descriptions.csv')","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_dcm(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    return image\n\ndef convert_dcm_to_jpg(file_path):\n    try:\n        # Read the DICOM file\n        image_array = read_dcm(file_path)\n        \n        # Define the output path\n        relative_path = os.path.relpath(file_path, start=input_directory)\n        output_path = os.path.join(output_directory, relative_path)\n        output_path = output_path.replace('.dcm', '.jpg')\n                \n        # Create the output directory if it doesn't exist\n        os.makedirs(os.path.dirname(output_path), exist_ok=True)\n        \n        # Save the image as a JPEG file\n        cv2.imwrite(output_path, image_array)\n        \n        return output_path\n    except Exception as e:\n        print(f\"Error processing file {file_path}: {e}\")\n        return None\n\ndef process_files(dcm_files):\n    with Pool(cpu_count()) as pool:\n        # Wrap pool.map with tqdm to show the progress bar\n        list(tqdm(pool.imap(convert_dcm_to_jpg, dcm_files), total=len(dcm_files)))\n\ndef get_dcm_files(directory):\n    dcm_files = []\n    for root, dirs, files in os.walk(directory):\n        for file in files:\n            if file.endswith('.dcm'):\n                dcm_files.append(os.path.join(root, file))\n    return dcm_files    ","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Replace these with your input and output directories\nif not EVAL:\n    input_directory = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images'\n\n    output_directory = IMG_DIR\n\n    # Get all .dcm files in the input directory\n    dcm_files = get_dcm_files(input_directory)\n\n    # Process the files using multiprocessing\n    process_files(dcm_files)\n\n    print(f\"Conversion completed. Images saved to {output_directory}\")\nelse:\n    if not os.path.exists(IMG_DIR):\n        print('Unziping data..')\n        !unzip -q -d / /kaggle/input/lsdc-get-all-images/images.zip\n        print('Done unziping data')","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    fold_df = pd.read_csv('/kaggle/input/lsdc-fold-split/5folds.csv')\n    test_df = fold_df[fold_df.fold == FOLD]\n    \nelse:\n    test_df = os.listdir('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images')\n    test_df = pd.DataFrame(test_df, columns=['study_id'])\n    test_df['study_id'] = test_df['study_id'].astype(int)\n    \ntest_df = test_df.merge(des, on=['study_id'])","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def gen_label_map(CONDITIONS):\n    label2id = {}\n    id2label = {}\n    i = 0\n    for cond in CONDITIONS:\n        for level in LEVELS:\n            for severity in SEVERITIES:\n                cls_ = f\"{cond.lower().replace(' ', '_')}_{level}_{severity.lower()}\"\n                label2id[cls_] = i\n                id2label[i] = cls_\n                i+=1\n    return label2id, id2label\n                \nscs_label2id, scs_id2label = gen_label_map(['Spinal Canal Stenosis'])\nss_label2id, ss_id2label = gen_label_map(['Left Subarticular Stenosis', 'Right Subarticular Stenosis'])\nnfn_label2id, nfn_id2label = gen_label_map(['Left Neural Foraminal Narrowing', 'Right Neural Foraminal Narrowing'])","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from ultralytics import YOLO\n\n# Load YOLO Model\nscs_models = []\nfor weight in SCS_WEIGHTS:\n    scs_models.append(YOLO(weight))\n    \nss_models = []\nfor weight in SS_WEIGHTS:\n    ss_models.append(YOLO(weight))\n    \nnfn_models = []\nfor weight in NFN_WEIGHTS:\n    nfn_models.append(YOLO(weight))","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_label_set = train_val_df.iloc[0, 1:].index.tolist()\nscs_label_set = all_label_set[:5]\nnfn_label_set = all_label_set[5:15]\nss_label_set = all_label_set[15:]","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"settings = [\n    ( 'Sagittal T2/STIR', scs_models, scs_id2label, scs_label_set, 0),\n    ( 'Axial T2', ss_models, ss_id2label, ss_label_set, 0),\n    ( 'Sagittal T1', nfn_models, nfn_id2label, nfn_label_set, 0)\n]","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import defaultdict","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_rows = []\n\nfor modality, models, id2label, label_set, thresh in settings:\n    mod_df = test_df[test_df.series_description == modality]\n    \n    if SAMPLE:\n        mod_df = mod_df.sample(20, random_state=610)\n    \n    # for each study, at each level and condition, get the maximum probability score\n    for study_id, group in tqdm(mod_df.groupby('study_id')):\n        predictions = defaultdict(list)\n        for i, row in group.iterrows():\n            # predict on all images from all the series\n            series_dir = os.path.join(IMG_DIR, str(row['study_id']), str(row['series_id']))\n            for model in models:\n                results = model(series_dir, conf=thresh, verbose=False)\n                for res in results:\n                    for pred_class, conf in zip(res.boxes.cls, res.boxes.conf):\n                        pred_class = pred_class.item()\n                        conf = conf.item()\n                        _class = id2label[pred_class]\n                        predictions[_class].append(conf)\n        \n        # aggregate the result on images to obtain study-level prediction\n        for condition in label_set:\n            res_dict = {'row_id': f'{study_id}_{condition}' }\n\n            score_vec = []\n            for severity in SEVERITIES:\n                severity = severity.lower()\n                key = f'{condition}_{severity}'\n                if len(predictions[key]) > 0:\n                    score = np.max(predictions[key])\n                else:\n                    score = thresh\n                score_vec.append(score)\n                \n            # normalize score to sum to 1\n            score_vec = torch.tensor(score_vec)\n            score_vec = score_vec / score_vec.sum()\n\n            for idx, severity in enumerate(SEVERITIES):\n                res_dict[severity.replace('/', '_').lower()] = score_vec[idx].item()\n\n            pred_rows.append(res_dict)\n","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_df = pd.DataFrame(pred_rows)\npred_df","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_df.to_csv('submission_7.csv', index=False)","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def sample_weight(row):\n    if row['normal_mild'] == 1:\n        return 1\n    if row['moderate'] == 1:\n        return 2\n    if row['severe'] == 1:\n        return 4\n    raise ValueError('No such value')\n    \ndef get_class(row):\n    return np.argmax([row['normal_mild'], row['moderate'], row['severe']])","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if EVAL:\n    gt_df = train_val_df.dropna().melt(id_vars=['study_id'], value_vars=all_label_set)\n    gt_df['row_id'] = gt_df['study_id'].astype(str) + '_' + gt_df['variable']\n    gt_df= gt_df[['row_id', 'value']]\n    gt_df = pd.get_dummies(gt_df, columns=['value'], dtype=int)\n    gt_df.columns = ['row_id', 'moderate', 'normal_mild', 'severe']\n    gt_df = gt_df[['row_id', 'normal_mild', 'moderate', 'severe']]\n    gt_df['sample_weight'] = gt_df.apply(sample_weight, axis=1)\n\n    gt_df1 = gt_df.merge(pred_df['row_id'], how='inner', on='row_id').sort_values('row_id').reset_index(drop=True)\n    pred_df1 = pred_df.merge(gt_df1['row_id'], how='inner', on='row_id').sort_values('row_id').reset_index(drop=True)\n    gt_df1['pred_cls'] = gt_df1.apply(get_class, axis=1)\n    pred_df1['pred_cls'] = pred_df1.apply(get_class, axis=1)\n\n    gt_df1[(gt_df1['pred_cls'] != pred_df1['pred_cls'])]\n    pred_df1[(gt_df1['pred_cls'] != pred_df1['pred_cls'])]\n    print('Label count:\\n', gt_df1['pred_cls'].value_counts(normalize=True))\n    print('Prediction accuracy:', (gt_df1['pred_cls'] == pred_df1['pred_cls']).mean())\n    print()\n\n    target_levels = ['normal_mild', 'moderate', 'severe']\n    loss = lsdc_scoring(gt_df1.drop(['pred_cls'], axis=1), pred_df1.drop(['pred_cls'], axis=1), row_id_column_name='row_id', any_severe_scalar=1)\n    print('Total weighted log loss:', loss)","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ls","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## USA [3D Vision Transformer Single-Stage (Inference)](https://www.kaggle.com/code/vsahin/3d-vision-transformer-single-stage-inference)\n### [Victor S](https://www.kaggle.com/vsahin)","metadata":{}},{"cell_type":"markdown","source":"### Dependencies\n#### timm_3d\nTimm models adapted for 3D data\n#### torchio\nMedical imaging focused library. Used for 3D augmentations here\n#### spacecutter\nOrdinal regression layer and related loss and callback functions i.e. `LogisticCumulativeLink`\n#### open3d\nUsed for point cloud initialization and transformation. At first I used this for voxelization too, but dropped it in favor of a simpler sampling approach due to slow runtime.","metadata":{}},{"cell_type":"code","source":"%pip install --quiet /kaggle/input/timm_3d_deps/other/initial/9/pydicom/pydicom/pydicom-2.4.4-py3-none-any.whl\n%pip install timm_3d --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/timm_3d/\n%pip install torchio --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/torchio/\n%pip install itk --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/itk/itk\n%pip install skorch --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/skorch/skorch\n%pip install spacecutter --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/spacecutter/\n%pip install open3d --no-index --quiet --find-links=/kaggle/input/timm_3d_deps/other/initial/9/open3d","metadata":{"_kg_hide-input":true,"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_path = \"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/\"","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\ndef retrieve_test_data(data_path):\n    test_df = pd.read_csv(data_path + 'test_series_descriptions.csv')\n\n    return test_df\n\nretrieve_test_data(data_path)","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\ndef retrieve_image_paths(base_path, study_id, series_id):\n    series_dir = os.path.join(base_path, str(study_id), str(series_id))\n    images = os.listdir(series_dir)\n    image_paths = [os.path.join(series_dir, img) for img in images]\n    return image_paths","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data Loading\n\nQuite a few things are happening here. Refer here for more details on where the affine transforms come from: https://nipy.org/nibabel/dicom/dicom_orientation.html\n\nThe rough idea is -- converting the slices into point cloud format, transforming them into the same patient coordinate space, then converting into a voxel grid.\n\n1. Resizing each slice -- doing this first reduces the runtime from having to transform each point just to resize/downsample later anyway.\n2. Duplicating the slices and applying the affine transforms -- I found duplicating each slice by slice thickness makes it easier for the model to learn. The spatial information between slices might get somewhat warped, but the relative ordering is still the same but with much less empty space.\n3. Sampling into a voxel grid -- I am simply scaling the coordinates into the desired grid size, then sampling each channel by max.","metadata":{}},{"cell_type":"code","source":"import open3d as o3d\nfrom pydicom import dcmread\nimport math\nimport numpy as np\nimport cv2\nimport copy\n\ndef read_study_as_pcd(dir_path, series_types_dict=None, downsampling_factor=1, img_size=(256, 256)):\n    pcd_overall = o3d.geometry.PointCloud()\n\n    for path in glob.glob(os.path.join(dir_path, \"**/*.dcm\"), recursive=True):\n        dicom_slice = dcmread(path)\n\n        series_id = os.path.basename(os.path.dirname(path))\n        study_id = os.path.basename(os.path.dirname(os.path.dirname(path)))\n        if series_types_dict is None or int(series_id) not in series_types_dict:\n            series_desc = dicom_slice.SeriesDescription\n        else:\n            series_desc = series_types_dict[int(series_id)]\n            series_desc = series_desc.split(\" \")[-1]\n\n        x_orig, y_orig = dicom_slice.pixel_array.shape\n        img = np.expand_dims(cv2.resize(dicom_slice.pixel_array, img_size, interpolation=cv2.INTER_AREA), -1)\n        x, y, z = np.where(img)\n\n        downsampling_factor_iter = max(downsampling_factor, int(math.ceil(len(x) / 6e6)))\n\n        index_voxel = np.vstack((x, y, z))[:, ::downsampling_factor_iter]\n        grid_index_array = index_voxel.T\n        pcd = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(grid_index_array.astype(np.float64)))\n\n        vals = np.expand_dims(img[x, y, z][::downsampling_factor_iter], -1)\n        if series_desc == \"T1\":\n            vals = np.pad(vals, ((0, 0), (0, 2)))\n        elif series_desc == \"T2\":\n            vals = np.pad(vals, ((0, 0), (1, 1)))\n        elif series_desc == \"T2/STIR\":\n            vals = np.pad(vals, ((0, 0), (2, 0)))\n        else:\n            raise ValueError(f\"Unknown series desc: {series_desc}\")\n\n        pcd.colors = o3d.utility.Vector3dVector(vals.astype(np.float64))\n\n        dX, dY = dicom_slice.PixelSpacing\n        dZ = dicom_slice.SliceThickness\n\n        X = np.array(list(dicom_slice.ImageOrientationPatient[:3]) + [0]) * dX\n        Y = np.array(list(dicom_slice.ImageOrientationPatient[3:]) + [0]) * dY\n\n        for z in range(int(dZ)):\n            pos = list(dicom_slice.ImagePositionPatient)\n            if series_desc == \"T2\":\n                pos[-1] += z\n            else:\n                pos[0] += z\n            S = np.array(pos + [1])\n\n            transform_matrix = np.array([X, Y, np.zeros(len(X)), S]).T\n            transform_matrix = transform_matrix @ np.matrix(\n                [[0, y_orig / img_size[1], 0, 0],\n                 [x_orig / img_size[0], 0, 0, 0],\n                 [0, 0, 1, 0],\n                 [0, 0, 0, 1]]\n            )\n\n            pcd_overall += copy.deepcopy(pcd).transform(transform_matrix)\n\n    return pcd_overall\n\n\n\ndef read_study_as_voxel_grid(dir_path, series_type_dict=None, downsampling_factor=1, img_size=(256, 256)):\n    pcd_overall = read_study_as_pcd(dir_path,\n                                    series_types_dict=series_type_dict,\n                                    downsampling_factor=downsampling_factor,\n                                    img_size=img_size)\n    box = pcd_overall.get_axis_aligned_bounding_box()\n\n    max_b = np.array(box.get_max_bound())\n    min_b = np.array(box.get_min_bound())\n\n    pts = (np.array(pcd_overall.points) - (min_b)) * (\n                (img_size[0] - 1, img_size[0] - 1, img_size[0] - 1) / (max_b - min_b))\n    coords = np.round(pts).astype(np.int32)\n    vals = np.array(pcd_overall.colors, dtype=np.float16)\n\n    grid = np.zeros((3, img_size[0], img_size[0], img_size[0]), dtype=np.float16)\n    indices = coords[:, 0], coords[:, 1], coords[:, 2]\n\n    np.maximum.at(grid[0], indices, vals[:, 0])\n    np.maximum.at(grid[1], indices, vals[:, 1])\n    np.maximum.at(grid[2], indices, vals[:, 2])\n\n\n    return grid","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom torch.utils.data import Dataset, DataLoader\nimport torchio as tio\nimport torch.nn as nn\nimport pydicom\n\nCONDITIONS = {\n    \"Sagittal T2/STIR\": [\"Spinal Canal Stenosis\"],\n    \"Axial T2\": [\"Left Subarticular Stenosis\", \"Right Subarticular Stenosis\"],\n    \"Sagittal T1\": [\"Left Neural Foraminal Narrowing\", \"Right Neural Foraminal Narrowing\"],\n}\n\n\nclass PatientLevelTestset(Dataset):\n    def __init__(self,\n                 base_path: str,\n                 dataframe: pd.DataFrame,\n                 transform_3d=None):\n        self.base_path = base_path\n\n        self.dataframe = (dataframe[['study_id', \"series_id\", \"series_description\"]]\n                          .drop_duplicates())\n\n        self.subjects = self.dataframe[['study_id']].drop_duplicates().reset_index(drop=True)\n        self.series_descs = {e[0]: e[1] for e in self.dataframe[[\"series_id\", \"series_description\"]].drop_duplicates().values}\n\n        self.transform_3d = transform_3d\n\n    def __len__(self):\n        return len(self.subjects)\n\n    def __getitem__(self, index):\n        curr = self.subjects.iloc[index]\n        study_path = os.path.join(self.base_path, str(curr[\"study_id\"]))\n\n        study_images = read_study_as_voxel_grid(study_path, self.series_descs)\n\n        if self.transform_3d is not None:\n            study_images = self.transform_3d(torch.FloatTensor(study_images))  # .data\n            return study_images.to(torch.half), str(curr[\"study_id\"])\n\n        return torch.HalfTensor(study_images), str(curr[\"study_id\"])\n","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform_3d = tio.Compose([\n    tio.RescaleIntensity([0, 1]),\n])","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_subject_level_testset_and_loader(df: pd.DataFrame,\n                                             transform_3d,\n                                             base_path: str,\n                                             batch_size=1,\n                                             num_workers=0):\n    testset = PatientLevelTestset(base_path, df, transform_3d=transform_3d)\n    test_loader = DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=num_workers)\n\n    return testset, test_loader","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\ndata = retrieve_test_data(data_path)\ndataset, dataloader = create_subject_level_testset_and_loader(data, transform_3d, os.path.join(data_path, \"test_images\"))","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport glob \nimport torch\n\ngrid = dataset[0][0]\n\nfig, axs = plt.subplots(3, 3)\n\naxs[0, 0].imshow(grid[0, 128])\naxs[1, 0].imshow(grid[1, 128])\naxs[2, 0].imshow(grid[2, 128])\n\naxs[0, 1].imshow(grid[0, :, 128])\naxs[1, 1].imshow(grid[1, :, 128])\naxs[2, 1].imshow(grid[2, :, 128])\n\naxs[0, 2].imshow(grid[0, :, :, 128])\naxs[1, 2].imshow(grid[1, :, :, 128])\naxs[2, 2].imshow(grid[2, :, :, 128])\n\nplt.show()","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\ndevice = torch.device(\"cuda\") if torch.cuda.is_available() else \"cpu\"","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Model Architecture\n\nI have somewhat better performance from a ViT. Might be due to better handling the gaps between slices. \n\nAlso note the LogisticCumulativeLink as the final head layer. This allows learning a continuous severity feature from ordinal labels and vice versa for inference.\n\n<!-- ![](https://www.ethanrosenthal.com/2018/12/06/spacecutter-ordinal-regression/index_11_0.png) -->","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\nimport timm_3d\nfrom spacecutter import *\nfrom spacecutter.losses import *\nfrom spacecutter.models import *\nfrom spacecutter.callbacks import *\n\n\nclass CNN_Model_3D_Multihead(nn.Module):\n    def __init__(self,\n                 backbone=\"efficientnet_lite0\",\n                 in_chans=1,\n                 out_classes=5,\n                 cutpoint_margin=0.15,\n                 pretrained=False):\n        super(CNN_Model_3D_Multihead, self).__init__()\n        self.out_classes = out_classes\n\n        self.encoder = timm_3d.create_model(\n            backbone,\n            features_only=False,\n            drop_rate=0,\n            drop_path_rate=0,\n            pretrained=pretrained,\n            in_chans=in_chans,\n            global_pool=\"max\"\n        )\n        if \"efficientnet\" in backbone:\n            head_in_dim = self.encoder.classifier.in_features\n            self.encoder.classifier = nn.Sequential(\n                nn.LayerNorm(head_in_dim),\n                nn.Dropout(0),\n            )\n\n        elif \"vit\" in backbone:\n            self.encoder.head.drop = nn.Dropout(0)\n            head_in_dim = self.encoder.head.fc.in_features\n            self.encoder.head.fc = nn.Identity()\n\n        self.heads = nn.ModuleList(\n            [nn.Sequential(\n                nn.Linear(head_in_dim, 1),\n                LogisticCumulativeLink(3)\n            ) for i in range(out_classes)]\n        )\n\n        self.ascension_callback = AscensionCallback(margin=cutpoint_margin)\n\n    def forward(self, x):\n        feat = self.encoder(x)\n        return torch.swapaxes(torch.stack([head(feat) for head in self.heads]), 0, 1)\n\n    def _ascension_callback(self):\n        for head in self.heads:\n            self.ascension_callback.clip(head[-1])","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CNN_Model_3D_Multihead(backbone=\"maxvit_rmlp_tiny_rw_256\", in_chans=3, out_classes=25).to(device)\nmodel.load_state_dict(torch.load(\"/kaggle/input/rsna-2024/pytorch/vit_voxel_v2/6/maxvit_rmlp_tiny_rw_256_256_v2_fold_3_32.pt\"))","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CONDITIONS = {\n    \"Sagittal T2/STIR\": [\"spinal_canal_stenosis\"],\n    \"Axial T2\": [\"left_subarticular_stenosis\", \"right_subarticular_stenosis\"],\n    \"Sagittal T1\": [\"left_neural_foraminal_narrowing\", \"right_neural_foraminal_narrowing\"],\n}\n\nALL_CONDITIONS = sorted([\"spinal_canal_stenosis\", \"left_subarticular_stenosis\", \"right_subarticular_stenosis\", \"left_neural_foraminal_narrowing\", \"right_neural_foraminal_narrowing\"])\nLEVELS = [\"l1_l2\", \"l2_l3\", \"l3_l4\", \"l4_l5\", \"l5_s1\"]\n\nresults_df = pd.DataFrame({\"row_id\":[], \"normal_mild\": [], \"moderate\": [], \"severe\": []})\n\nALL_CONDITIONS","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Pre-populate results df\nimport glob\nimport os\n\nstudy_ids = glob.glob(\"/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/*\")\nstudy_ids = [os.path.basename(e) for e in study_ids]\n\nresults_df = pd.DataFrame({\"row_id\":[], \"normal_mild\": [], \"moderate\": [], \"severe\": []})\nfor study_id in study_ids:\n    for condition in ALL_CONDITIONS:\n        for level in LEVELS:\n            row_id = f\"{study_id}_{condition}_{level}\"\n            results_df = results_df._append({\"row_id\": row_id, \"normal_mild\": 1/3, \"moderate\": 1/3, \"severe\": 1/3}, ignore_index=True)","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset.dataframe","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch.cuda.amp import autocast\nimport time\n\nstart_time = time.time()\n\nwith torch.no_grad():\n    with autocast(dtype=torch.float16):\n        model.eval()\n\n        for images, study_id in dataloader:\n            output = model(images.to(device))\n            for i, batch_out in enumerate(output):\n                batch_out = output.cpu().numpy()[i]\n                for index, level in enumerate(batch_out):\n                    row_id = f\"{study_id[i]}_{ALL_CONDITIONS[index // 5]}_{LEVELS[index % 5]}\"\n                    results_df.loc[results_df.row_id == row_id,'normal_mild'] = level[0]\n                    results_df.loc[results_df.row_id == row_id,'moderate'] = level[1]\n                    results_df.loc[results_df.row_id == row_id,'severe'] = level[2]\n                \nprint(\"--- %s seconds ---\" % (time.time() - start_time))","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_df","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_df.to_csv('submission_9.csv', index=False)","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Ensemble submissions","metadata":{}},{"cell_type":"code","source":"# option 19-> LB=0.48 work ( 7,9 ) + weights ( 0.450+0.550 ) - previus.1 best option\n# option 20-> LB=0.48 work ( 7,9 ) + weights ( 0.50+0.50 )\n# option 23-> LB=0.48 work ( 7,9 ) + weights ( 0.473+0.527 )\n# option 24-> LB=0.48 work ( 7,9 ) + weights ( 0.427+0.573 ) - previus.2 best option\n# option 25-> LB=0.48 work ( 7,9 ) + weights ( 0.402+0.598 ) - best option\n# option 26-> LB=0.49 work ( 7,9 ) + weights ( 0.377+0.623 )\n\n# some rezult:\n\n# option.18 < option.20 < option.19 < option.24 == (version.48 < version.51 < version.49 < version.55-56)\n# option.24 < option.25 == (version.55-56 < version.58)\n\n# current option:\n\n# option 27-> LB=0.4? work ( 7,9 ) + weights ( 0.407+0.593 )\n# option 28-> LB=0.4? work ( 7,9 ) + weights ( 0.395+0.605 )\n\n# next option:\n\n# option 16-> LB=0.?? work ( 7,8,9 ) + weights ( 0.25+0.20+0.55 )","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if LAUNCH_VARIANT == 'option 28':\n    sm7 = pd.read_csv('submission_7.csv')\n    sm9 = pd.read_csv('submission_9.csv')\n    display(sm7,sm9)\n    sms = pd.merge(sm7,sm9, on=['row_id'])\n    sms['normal_mild'] = sms['normal_mild_x'] *0.58 + 0.42* sms['normal_mild_y']\n    sms['moderate']    = sms['moderate_x']    *0.58 + 0.42* sms['moderate_y']\n    sms['severe']      = sms['severe_x']      *0.58 + 0.42* sms['severe_y']\n\nelif LAUNCH_VARIANT == 'option 27':\n    sm7 = pd.read_csv('submission_7.csv')\n    sm9 = pd.read_csv('submission_9.csv')\n    display(sm7,sm9)\n    sms = pd.merge(sm7,sm9, on=['row_id'])\n    sms['normal_mild'] = sms['normal_mild_x'] *0.407 + 0.593* sms['normal_mild_y']\n    sms['moderate']    = sms['moderate_x']    *0.407 + 0.593* sms['moderate_y']\n    sms['severe']      = sms['severe_x']      *0.407 + 0.593* sms['severe_y']","metadata":{"_kg_hide-input":false,"_kg_hide-output":false},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = sms[['row_id','normal_mild','moderate','severe']]\nsub.to_csv('submission.csv', index=False, float_format='%.7f')\ndisplay(sub)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# if LAUNCH_VARIANT == 'option 26':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.377 + 0.523* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.377 + 0.523* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.377 + 0.523* sms['severe_y']\n\n# elif LAUNCH_VARIANT == 'option 25':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.402 + 0.598* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.402 + 0.598* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.402 + 0.598* sms['severe_y']\n\n# elif LAUNCH_VARIANT == 'option 24':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.427 + 0.573* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.427 + 0.573* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.427 + 0.573* sms['severe_y']\n\n# elif LAUNCH_VARIANT == 'option 23':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.473 + 0.527* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.473 + 0.527* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.473 + 0.527* sms['severe_y']\n\n# elif LAUNCH_VARIANT == 'option 22':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.58 + 0.42* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.58 + 0.42* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.58 + 0.42* sms['severe_y']\n    \n# elif LAUNCH_VARIANT == 'option 21':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.55 + 0.45* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.55 + 0.45* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.55 + 0.45* sms['severe_y']\n\n# if LAUNCH_VARIANT == 'option 20':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.50 + 0.50* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.50 + 0.50* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.50 + 0.50* sms['severe_y']\n\n# if LAUNCH_VARIANT == 'option 18':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.33 + 0.67* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.33 + 0.67* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.33 + 0.67* sms['severe_y']    \n\n# if LAUNCH_VARIANT == 'option 12':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.20 + 0.80* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.20 + 0.80* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.20 + 0.80* sms['severe_y']\n\n# elif LAUNCH_VARIANT == 'option 17':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm9)\n#     sms = pd.merge(sm7,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.257 + 0.743* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.257 + 0.743* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.257 + 0.743* sms['severe_y']    \n    \n# elif LAUNCH_VARIANT == 'option 15':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm8 = pd.read_csv('submission_8.csv')\n#     display(sm7,sm8)\n#     sms = pd.merge(sm7,sm8, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.70 + 0.30* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.70 + 0.30* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.70 + 0.30* sms['severe_y']\n    \n# elif LAUNCH_VARIANT == 'option 16':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm8 = pd.read_csv('submission_8.csv')\n#     sm9 = pd.read_csv('submission_9.csv')\n#     display(sm7,sm8,sm9)\n#     sms = pd.merge(sm7,sm8, on=['row_id'])\n#     sms = pd.merge(sms,sm9, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.35 + 0.20* sms['normal_mild_y'] + 0.45* sms['normal_mild']\n#     sms['moderate']    = sms['moderate_x']    *0.35 + 0.20* sms['moderate_y']    + 0.45* sms['moderate']\n#     sms['severe']      = sms['severe_x']      *0.35 + 0.20* sms['severe_y']      + 0.45* sms['severe']\n\n# elif LAUNCH_VARIANT == 'option 13':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm8 = pd.read_csv('submission_8.csv')\n#     display(sm7,sm8)\n#     sms = pd.merge(sm7,sm8, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.95 + 0.05* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.95 + 0.05* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.95 + 0.05* sms['severe_y']    \n    \n# elif LAUNCH_VARIANT == 'option 14':\n#     sm7 = pd.read_csv('submission_7.csv')\n#     sm8 = pd.read_csv('submission_8.csv')\n#     display(sm7,sm8)\n#     sms = pd.merge(sm7,sm8, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.85 + 0.15* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.85 + 0.15* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.85 + 0.15* sms['severe_y']\n    \n# elif LAUNCH_VARIANT == 'option 10':\n#     sm6 = pd.read_csv('submission_6.csv')\n#     sm7 = pd.read_csv('submission_7.csv')\n#     display(sm6,sm7)\n#     sms = pd.merge(sm6,sm7, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.005 + 0.995* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.005 + 0.995* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.005 + 0.995* sms['severe_y']\n    \n# elif LAUNCH_VARIANT == 'option 11':\n#     sm5 = pd.read_csv('submission_5.csv')\n#     sm6 = pd.read_csv('submission_6.csv')\n#     sm7 = pd.read_csv('submission_7.csv')\n#     display(sm5,sm6,sm7)\n#     sms = pd.merge(sm5,sm6, on=['row_id'])\n#     sms = pd.merge(sm6,sm7, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.0005 + 0.0025* sms['normal_mild_y'] + 0.997* sms['normal_mild']\n#     sms['moderate']    = sms['moderate_x']    *0.0005 + 0.0025* sms['moderate_y']    + 0.997* sms['moderate']\n#     sms['severe']      = sms['severe_x']      *0.0005 + 0.0025* sms['severe_y']      + 0.997* sms['severe']\n    \n# elif LAUNCH_VARIANT == 'option 8':\n#     sm5 = pd.read_csv('submission_5.csv')\n#     sm6 = pd.read_csv('submission_6.csv')\n#     display(sm5,sm6)\n#     sms = pd.merge(sm5,sm6, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.90 + 0.10* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.90 + 0.10* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.90 + 0.10* sms['severe_y']\n    \n# elif LAUNCH_VARIANT == 'option 7':\n#     sm5 = pd.read_csv('submission_5.csv')\n#     sm6 = pd.read_csv('submission_6.csv')\n#     display(sm5,sm6)\n#     sms = pd.merge(sm5,sm6, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.50 + 0.50* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.50 + 0.50* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.50 + 0.50* sms['severe_y']\n    \n# if LAUNCH_VARIANT == 'option 1':\n#     sm1 = pd.read_csv('submission_1.csv')\n#     sm2 = pd.read_csv('submission_2.csv')\n#     display(sm1,sm2)\n#     sms = pd.merge(sm1,sm2, on=['row_id'])\n#     sms['normal_mild'] = (sms['normal_mild_x'] + sms['normal_mild_y']) / 2\n#     sms['moderate']    = (sms['moderate_x']    + sms['moderate_y'])    / 2\n#     sms['severe']      = (sms['severe_x']      + sms['severe_y'])      / 2\n\n# elif LAUNCH_VARIANT == 'option 2':\n#     sm2 = pd.read_csv('submission_2.csv')\n#     sm3 = pd.read_csv('submission_3.csv')\n#     display(sm2,sm3)\n#     sms = pd.merge(sm2,sm3, on=['row_id'])\n#     sms['normal_mild'] = (sms['normal_mild_x'] + sms['normal_mild_y']) / 2\n#     sms['moderate']    = (sms['moderate_x']    + sms['moderate_y'])    / 2\n#     sms['severe']      = (sms['severe_x']      + sms['severe_y'])      / 2\n#     # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n\n# elif LAUNCH_VARIANT == 'option 3':\n#     sm3 = pd.read_csv('submission_3.csv')\n#     sm4 = pd.read_csv('submission_4.csv')\n#     display(sm3,sm4)\n#     sms = pd.merge(sm3,sm4, on=['row_id'])\n#     sms['normal_mild'] = (sms['normal_mild_x'] + sms['normal_mild_y']) / 2\n#     sms['moderate']    = (sms['moderate_x']    + sms['moderate_y'])    / 2\n#     sms['severe']      = (sms['severe_x']      + sms['severe_y'])      / 2\n#     # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n\n# elif LAUNCH_VARIANT == 'option 4':\n#     sm1 = pd.read_csv('submission_1.csv')\n#     sm2 = pd.read_csv('submission_2.csv')\n#     sm4 = pd.read_csv('submission_4.csv')\n#     display(sm1,sm2,sm4)\n#     sms = pd.merge(sm1,sm2, on=['row_id'])\n#     sms = pd.merge(sms,sm4, on=['row_id'])\n#     sms['normal_mild'] = (sms['normal_mild_x'] + sms['normal_mild_y'] + sms['normal_mild']) / 3\n#     sms['moderate']    = (sms['moderate_x']    + sms['moderate_y'])   + sms['moderate']     / 3\n#     sms['severe']      = (sms['severe_x']      + sms['severe_y'])     + sms['severe']       / 3\n#     # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n    \n# elif LAUNCH_VARIANT == 'option 5':\n#     sm2 = pd.read_csv('submission_2.csv')\n#     sm3 = pd.read_csv('submission_3.csv')\n#     display(sm2,sm3)\n#     sms = pd.merge(sm2,sm3, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.45 + 0.55* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.45 + 0.55* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.45 + 0.55* sms['severe_y']\n#     # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n    \n# elif LAUNCH_VARIANT == 'option 6':\n#     sm2 = pd.read_csv('submission_2.csv')\n#     sm3 = pd.read_csv('submission_3.csv')\n#     display(sm2,sm3)\n#     sms = pd.merge(sm2,sm3, on=['row_id'])\n#     sms['normal_mild'] = sms['normal_mild_x'] *0.55 + 0.45* sms['normal_mild_y']\n#     sms['moderate']    = sms['moderate_x']    *0.55 + 0.45* sms['moderate_y']\n#     sms['severe']      = sms['severe_x']      *0.55 + 0.45* sms['severe_y']\n#     # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~","metadata":{"_kg_hide-input":true},"execution_count":null,"outputs":[]}]}