{"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":"# [RSNA 2023 Abdominal Trauma Detection](https://www.kaggle.com/competitions/rsna-2023-abdominal-trauma-detection)\n\n> Detect and classify traumatic abdominal injuries\n\n![](https://www.kaggle.com/competitions/52254/images/header)","metadata":{}},{"cell_type":"markdown","source":"# Idea:\n* Same as [RSNA-ATD: CNN [TPU][Train]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-train/) but with **2.5D** data.\n* 2.5D method is simpling stacking different scan of same series to created a `RGB` like image but here each channel is associated with different scans.","metadata":{}},{"cell_type":"markdown","source":"# Notebooks\n* 2.5D:\n    * Train: [RSNA-ATD: 2.5D Series Image [Train]](https://www.kaggle.com/awsaf49/rsna-atd-2-5d-series-image-train)\n    * Infer: [RSNA-ATD: 2.5D Series Image [Infer]](https://www.kaggle.com/awsaf49/rsna-atd-2-5d-series-image-infer)\n* 2D:\n    * Train: [RSNA-ATD: CNN [TPU][Train]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-train/)\n    * Infer: [RSNA-ATD: CNN [TPU][Infer]](https://www.kaggle.com/awsaf49/rsna-atd-cnn-tpu-infer/)","metadata":{}},{"cell_type":"markdown","source":"# Logging:","metadata":{}},{"cell_type":"markdown","source":"# Install Libraries","metadata":{}},{"cell_type":"code","source":"!pip install -q /kaggle/input/rsna-atd-whl-ds/python_gdcm-3.0.22-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n!pip install -q /kaggle/input/rsna-atd-whl-ds/pylibjpeg-1.4.0-py3-none-any.whl\n!cp -r /kaggle/input/efficientnet-keras-dataset/efficientnet_kaggle /tmp/ && pip install -q /tmp/efficientnet_kaggle","metadata":{"_kg_hide-output":true,"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-09-18T09:17:24.583164Z","iopub.execute_input":"2023-09-18T09:17:24.583495Z","iopub.status.idle":"2023-09-18T09:19:05.541061Z","shell.execute_reply.started":"2023-09-18T09:17:24.583466Z","shell.execute_reply":"2023-09-18T09:19:05.53972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install --no-index --no-deps /kaggle/input/vit-keras-012-whl/vit-keras_0.1.2/wheelhouse/vit_keras-0.1.2-py3-none-any.whl\n!pip install --no-index --no-deps /kaggle/input/vit-keras-012-whl/vit-keras_0.1.2/wheelhouse/validators-0.22.0-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:05.543868Z","iopub.execute_input":"2023-09-18T09:19:05.54426Z","iopub.status.idle":"2023-09-18T09:19:09.637927Z","shell.execute_reply.started":"2023-09-18T09:19:05.544223Z","shell.execute_reply":"2023-09-18T09:19:09.63682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import Libraries","metadata":{}},{"cell_type":"code","source":"import os\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # to avoid too many logging messages\nimport pandas as pd, numpy as np, random, shutil\nimport tensorflow as tf, re, math\nimport tensorflow.keras.backend as K\nimport efficientnet.tfkeras as efn\nimport sklearn\nimport matplotlib.pyplot as plt\nimport tensorflow_addons as tfa\nimport yaml\nimport gc\n\n\n\nfrom IPython import display as ipd\nfrom glob import glob\nfrom tqdm.notebook import tqdm","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:09.640071Z","iopub.execute_input":"2023-09-18T09:19:09.640803Z","iopub.status.idle":"2023-09-18T09:19:18.236894Z","shell.execute_reply.started":"2023-09-18T09:19:09.640766Z","shell.execute_reply":"2023-09-18T09:19:18.235949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Version Check","metadata":{}},{"cell_type":"code","source":"print('np:', np.__version__)\nprint('pd:', pd.__version__)\nprint('sklearn:', sklearn.__version__)\nprint('tf:',tf.__version__)\nprint('tfa:', tfa.__version__)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.239488Z","iopub.execute_input":"2023-09-18T09:19:18.240252Z","iopub.status.idle":"2023-09-18T09:19:18.247601Z","shell.execute_reply.started":"2023-09-18T09:19:18.240214Z","shell.execute_reply":"2023-09-18T09:19:18.246611Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration","metadata":{}},{"cell_type":"code","source":"class CFG:\n    debug         = False\n    # device\n    device = \"GPU\" # or \"GPU\"\n    # seed for data-split, layer init, augs\n    seed = 42\n    # which folds of ckpt to use\n    num_folds = 1\n    # dicom to png size\n    resize_dim = 512\n    # size of training image\n    img_size = [512, 512]\n    # clip\n    clip = False\n    # lr-scheduler\n    scheduler   = 'exp' # cosine\n    # threshold\n    thr = 0.50\n    # target column\n    target_col  = [\"bowel_healthy\", \"bowel_injury\", \"extravasation_healthy\",\n                   \"extravasation_injury\", \"kidney_healthy\", \"kidney_low\",\n                   \"kidney_high\", \"liver_healthy\", \"liver_low\", \"liver_high\",\n                   \"spleen_healthy\", \"spleen_low\", \"spleen_high\"]","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.248916Z","iopub.execute_input":"2023-09-18T09:19:18.249993Z","iopub.status.idle":"2023-09-18T09:19:18.258177Z","shell.execute_reply.started":"2023-09-18T09:19:18.249961Z","shell.execute_reply":"2023-09-18T09:19:18.257352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Reproducibility","metadata":{}},{"cell_type":"code","source":"def seeding(SEED):\n    np.random.seed(SEED)\n    random.seed(SEED)\n    os.environ['PYTHONHASHSEED'] = str(SEED)\n#     os.environ['TF_CUDNN_DETERMINISTIC'] = str(SEED)\n    tf.random.set_seed(SEED)\n    print('seeding done!!!')\nseeding(CFG.seed)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.259424Z","iopub.execute_input":"2023-09-18T09:19:18.259866Z","iopub.status.idle":"2023-09-18T09:19:18.269104Z","shell.execute_reply.started":"2023-09-18T09:19:18.259833Z","shell.execute_reply":"2023-09-18T09:19:18.268158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Device Configs\nThis notebook is compatible for **remote-tpu**, **local-tpu**, **multi-gpu** and **single-gpu**. Simple change to `device=\"TPU\"` for **remote-tpu** and `device=\"TPU-1VM\"` for **local-tpu** and finally, `device=\"GPU\"` for single or multi-gpu.","metadata":{}},{"cell_type":"code","source":"if \"TPU\" in CFG.device:\n    tpu = 'local' if CFG.device=='TPU-1VM' else None\n    print(\"connecting to TPU...\")\n    try:\n        tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect(tpu=tpu)\n        strategy = tf.distribute.TPUStrategy(tpu)\n    except:\n        CFG.device = \"GPU\"\n        \nif CFG.device == \"GPU\"  or CFG.device==\"CPU\":\n    ngpu = len(tf.config.experimental.list_physical_devices('GPU'))\n    if ngpu>1:\n        print(\"Using multi GPU\")\n        strategy = tf.distribute.MirroredStrategy()\n    elif ngpu==1:\n        print(\"Using single GPU\")\n        strategy = tf.distribute.get_strategy()\n    else:\n        print(\"Using CPU\")\n        strategy = tf.distribute.get_strategy()\n        CFG.device = \"CPU\"\n\nif CFG.device == \"GPU\":\n    print(\"Num GPUs Available: \", ngpu)\n    \n\nAUTO     = tf.data.experimental.AUTOTUNE\nREPLICAS = strategy.num_replicas_in_sync\nprint(f'REPLICAS: {REPLICAS}')","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:18.270385Z","iopub.execute_input":"2023-09-18T09:19:18.272149Z","iopub.status.idle":"2023-09-18T09:19:18.652365Z","shell.execute_reply.started":"2023-09-18T09:19:18.272118Z","shell.execute_reply":"2023-09-18T09:19:18.651365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Meta Data","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '/kaggle/input/rsna-2023-abdominal-trauma-detection'\nIMG_DIR = '/tmp/dataset/rsna-atd'\nNUM_SLICES = 4 # how many dicom files to take per series (scans)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.653803Z","iopub.execute_input":"2023-09-18T09:19:18.654166Z","iopub.status.idle":"2023-09-18T09:19:18.658617Z","shell.execute_reply.started":"2023-09-18T09:19:18.654132Z","shell.execute_reply":"2023-09-18T09:19:18.657642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Paths","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv(f'{BASE_PATH}/test_series_meta.csv')\ntest_df['dicom_folder'] = BASE_PATH + '/' + 'test_images'\\\n                                    + '/' + test_df.patient_id.astype(str)\\\n                                    + '/' + test_df.series_id.astype(str)\ntest_folders = test_df.dicom_folder.tolist()\n\ntest_paths = []\nfor folder in tqdm(test_folders):\n    paths = sorted(glob(os.path.join(folder, '*dcm')),\n                   key=lambda x: int(x.split('/')[-1].split('.')[0]))\n    NUM_DICOM = len(paths)\n    if len(test_folders)>6: # private test; contains all dicom files/folders\n        STRIDE = -(-NUM_DICOM // (NUM_SLICES + 4))\n        test_paths += [paths[STRIDE:NUM_DICOM-3*STRIDE:STRIDE]]\n    else: # we can't access all the test dicom files in public test\n        test_paths += [paths]\n\ntest_df['dicom_paths'] = test_paths\ntest_df = test_df[test_df.dicom_paths.map(len)>0] # in public test not all folder contains dicom file\n\ntest_df['image_path'] = f'{IMG_DIR}/test_images'\\\n                    + '/' + test_df.patient_id.astype(str)\\\n                    + '/' + test_df.series_id.astype(str) +'.png'\n# test_df = test_df.drop_duplicates()\n\ntest_df.head(2)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.660099Z","iopub.execute_input":"2023-09-18T09:19:18.660665Z","iopub.status.idle":"2023-09-18T09:19:18.757646Z","shell.execute_reply.started":"2023-09-18T09:19:18.660633Z","shell.execute_reply":"2023-09-18T09:19:18.756686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Check If Data Exist?","metadata":{}},{"cell_type":"code","source":"tf.io.gfile.exists(test_df.dicom_paths.iloc[0][0])","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.762125Z","iopub.execute_input":"2023-09-18T09:19:18.76315Z","iopub.status.idle":"2023-09-18T09:19:18.770284Z","shell.execute_reply.started":"2023-09-18T09:19:18.763113Z","shell.execute_reply":"2023-09-18T09:19:18.769119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Test Ditribution","metadata":{}},{"cell_type":"code","source":"print('test_files:',test_df.shape[0])","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:18.771839Z","iopub.execute_input":"2023-09-18T09:19:18.773102Z","iopub.status.idle":"2023-09-18T09:19:18.780428Z","shell.execute_reply.started":"2023-09-18T09:19:18.773067Z","shell.execute_reply":"2023-09-18T09:19:18.779212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# DICOM to PNG","metadata":{}},{"cell_type":"markdown","source":"## Create Directory","metadata":{}},{"cell_type":"code","source":"!rm -r /tmp/Dataset/rsna-atd\n# os.makedirs('/tmp/dataset/rsna-atd/train_images', exist_ok = True)\nos.makedirs(f'{IMG_DIR}/test_images', exist_ok = True)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:18.782362Z","iopub.execute_input":"2023-09-18T09:19:18.783209Z","iopub.status.idle":"2023-09-18T09:19:19.766602Z","shell.execute_reply.started":"2023-09-18T09:19:18.783149Z","shell.execute_reply":"2023-09-18T09:19:19.765233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dicom Utils","metadata":{}},{"cell_type":"code","source":"import cv2\nimport pydicom\n\ndef standardize_pixel_array(dcm: pydicom.dataset.FileDataset) -> np.ndarray:\n    # Correct DICOM pixel_array if PixelRepresentation == 1.\n    pixel_array = dcm.pixel_array\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = pixel_array.dtype \n        new_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n        pixel_array = pydicom.pixel_data_handlers.util.apply_modality_lut(new_array, dcm)\n    return pixel_array\n\n\ndef read_xray(path, fix_monochrome = True):\n    dicom = pydicom.dcmread(path)\n    data = standardize_pixel_array(dicom)\n    data = data - np.min(data)\n    data = data / (np.max(data) + 1e-5)\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1.0 - data\n    IMG_SIZE = [CFG.resize_dim, CFG.resize_dim]\n    data = cv2.resize(data, IMG_SIZE, cv2.INTER_LINEAR)\n    data = (data * 255).astype(np.uint8)\n    return data\n\ndef load_scan(paths):\n    IMG_SIZE = [CFG.resize_dim, CFG.resize_dim]\n    img = np.empty(shape=(*IMG_SIZE, NUM_SLICES), dtype=np.uint8)\n    for i, path in enumerate(paths):\n        img[...,i] = read_xray(path)\n    return img\n\ndef load_img(path):\n    img = cv2.imread(path, -1)[...,::-1]\n    return img\n    \ndef resize_and_save(paths):\n    img = load_scan(paths)\n    file_path = paths[0]\n    sub_path = file_path.split(\"/\",4)[-1].split('.dcm')[0] + '.png'\n    infos = sub_path.split('/')\n    split = infos[-4]\n    pid = infos[-3]\n    sid = infos[-2]\n    iid = infos[-1]; iid = iid.replace('.png','')\n    new_path = os.path.join(IMG_DIR, split, pid, sid + '.png')\n    os.makedirs(new_path.rsplit('/',1)[0], exist_ok=True)\n    cv2.imwrite(new_path, img[...,::-1])\n    del img; gc.collect()\n    return \n\ndef show_img(img):\n    num_channels = img.shape[-1]\n    fig, axes = plt.subplots(1, num_channels+1, figsize=(num_channels*5, 5))\n    axes[0].imshow(img)\n    axes[0].set_title('Original Image')\n    axes[0].axis('off')\n\n    for i in range(num_channels):\n        axes[i+1].imshow(img[:, :, i], cmap='gray')\n        axes[i+1].set_title(f'Channel: {i:02d}')\n        axes[i+1].axis('off')\n\n    plt.tight_layout()\n    plt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:19.769615Z","iopub.execute_input":"2023-09-18T09:19:19.770327Z","iopub.status.idle":"2023-09-18T09:19:20.173998Z","shell.execute_reply.started":"2023-09-18T09:19:19.770285Z","shell.execute_reply":"2023-09-18T09:19:20.172999Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Check","metadata":{}},{"cell_type":"code","source":"test_df.dicom_paths.iloc[0]","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:20.175824Z","iopub.execute_input":"2023-09-18T09:19:20.176206Z","iopub.status.idle":"2023-09-18T09:19:20.183441Z","shell.execute_reply.started":"2023-09-18T09:19:20.176171Z","shell.execute_reply":"2023-09-18T09:19:20.182476Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = load_scan(test_df.dicom_paths.iloc[0])\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:20.185608Z","iopub.execute_input":"2023-09-18T09:19:20.186581Z","iopub.status.idle":"2023-09-18T09:19:21.228314Z","shell.execute_reply.started":"2023-09-18T09:19:20.186543Z","shell.execute_reply":"2023-09-18T09:19:21.227466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Conversion","metadata":{}},{"cell_type":"code","source":"%%time\nfrom joblib import Parallel, delayed\nfile_paths = test_df.dicom_paths.tolist()\n_ = Parallel(n_jobs=-1,backend='loky')(delayed(resize_and_save)(file_path)\\\n                                                  for file_path in tqdm(file_paths))\ndel _; gc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:21.229386Z","iopub.execute_input":"2023-09-18T09:19:21.229924Z","iopub.status.idle":"2023-09-18T09:19:24.207631Z","shell.execute_reply.started":"2023-09-18T09:19:21.22989Z","shell.execute_reply":"2023-09-18T09:19:24.206629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Check Image","metadata":{}},{"cell_type":"code","source":"img = load_img(f'{IMG_DIR}/test_images/50046/24574.png')\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:24.209139Z","iopub.execute_input":"2023-09-18T09:19:24.210062Z","iopub.status.idle":"2023-09-18T09:19:24.958865Z","shell.execute_reply.started":"2023-09-18T09:19:24.210029Z","shell.execute_reply":"2023-09-18T09:19:24.957858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = load_img(f'{IMG_DIR}/test_images/48843/62825.png')\nshow_img(img)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:24.960412Z","iopub.execute_input":"2023-09-18T09:19:24.961029Z","iopub.status.idle":"2023-09-18T09:19:25.656773Z","shell.execute_reply.started":"2023-09-18T09:19:24.960994Z","shell.execute_reply":"2023-09-18T09:19:25.655863Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Pipeline","metadata":{}},{"cell_type":"code","source":"def build_decoder(with_labels=True, target_size=CFG.img_size, ext='png'):\n    def decode(path):\n        file_bytes = tf.io.read_file(path)\n        if ext == 'png':\n            img = tf.image.decode_png(file_bytes, channels=4, dtype=tf.uint8)\n        elif ext in ['jpg', 'jpeg']:\n            img = tf.image.decode_jpeg(file_bytes, channels=3)\n        else:\n            raise ValueError(\"Image extension not supported\")\n\n        img = tf.image.resize(img, target_size, method='bilinear')\n        img = tf.cast(img, tf.float32) / 255.0\n        img = tf.reshape(img, [*target_size, 4])\n\n        return img\n    \n    def decode_with_labels(path, label):\n        return decode(path), tf.cast(label, tf.float32)\n    \n    return decode_with_labels if with_labels else decode\n\n\ndef build_dataset(paths, labels=None, batch_size=32, cache=True,\n                  decode_fn=None, repeat=True, shuffle=1024, \n                  cache_dir=\"\", drop_remainder=False):\n    if cache_dir != \"\" and cache is True:\n        os.makedirs(cache_dir, exist_ok=True)\n    \n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None)\n    \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = paths if labels is None else (paths, labels)\n    \n    ds = tf.data.Dataset.from_tensor_slices(slices)\n    ds = ds.map(decode_fn, num_parallel_calls=AUTO)\n    ds = ds.cache(cache_dir) if cache else ds\n    ds = ds.repeat() if repeat else ds\n    if shuffle: \n        ds = ds.shuffle(shuffle, seed=CFG.seed)\n        opt = tf.data.Options()\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n    ds = ds.batch(batch_size, drop_remainder=drop_remainder)\n    ds = ds.prefetch(AUTO)\n    return ds","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:25.658137Z","iopub.execute_input":"2023-09-18T09:19:25.659271Z","iopub.status.idle":"2023-09-18T09:19:25.673425Z","shell.execute_reply.started":"2023-09-18T09:19:25.659236Z","shell.execute_reply":"2023-09-18T09:19:25.672234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualization","metadata":{}},{"cell_type":"code","source":"def display_batch(batch, size=2):\n    if isinstance(batch, tuple):\n        imgs, tars = batch\n        tars = tf.concat(tars,axis=-1).numpy()\n    else:\n        imgs = batch\n        tars = None\n    \n    plt.figure(figsize=(size*5, 10))\n    for img_idx in range(size):\n        plt.subplot(1, size, img_idx+1)\n        if tars is not None:\n            plt.title(f'{tars[img_idx].round(2)}', fontsize=12)\n        img = imgs[img_idx,]\n        plt.imshow(img)\n        plt.xticks([]); plt.yticks([])\n    plt.tight_layout()\n    plt.show() ","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:25.675004Z","iopub.execute_input":"2023-09-18T09:19:25.67535Z","iopub.status.idle":"2023-09-18T09:19:25.687306Z","shell.execute_reply.started":"2023-09-18T09:19:25.675318Z","shell.execute_reply":"2023-09-18T09:19:25.6864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold_df = test_df.copy()\npaths  = fold_df.image_path.tolist()\nlabels = None\nds = build_dataset(paths, labels, cache=False, batch_size=32,\n                   repeat=True, shuffle=True)\nds = ds.unbatch().batch(20)\nbatch = next(iter(ds))\ndisplay_batch(batch, 5);","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-09-18T09:19:25.688963Z","iopub.execute_input":"2023-09-18T09:19:25.689368Z","iopub.status.idle":"2023-09-18T09:19:29.96787Z","shell.execute_reply.started":"2023-09-18T09:19:25.689337Z","shell.execute_reply":"2023-09-18T09:19:29.96698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Configs","metadata":{}},{"cell_type":"code","source":"\nCKPT_DIRS = [\n    ([512, 512], '/kaggle/input/rsna-atd-2-5d-vit-ds'),\n]\n\nMODEL_CONFIGS = []\nfor img_size, ckpt_dir in  CKPT_DIRS:\n    paths = sorted(glob(os.path.join(ckpt_dir, '*h5')))[-2:-1]\n    if len(paths)==0:\n        print('no model found for :',base_dir)\n    MODEL_CONFIGS.append([img_size, paths])\ndisplay(MODEL_CONFIGS)","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:29.969376Z","iopub.execute_input":"2023-09-18T09:19:29.970396Z","iopub.status.idle":"2023-09-18T09:19:29.986985Z","shell.execute_reply.started":"2023-09-18T09:19:29.970361Z","shell.execute_reply":"2023-09-18T09:19:29.98601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utility","metadata":{}},{"cell_type":"code","source":"def mc_proc(pred):\n    argmax = np.argmax(pred, axis=1).astype('uint8')\n    one_hot = tf.keras.utils.to_categorical(argmax, num_classes=3)\n    return one_hot.astype('uint8')\n\ndef sc_proc(pred, thr=0.5):\n    proc_pred = (pred > thr).astype('uint8')\n    return proc_pred\n\ndef post_proc(pred):\n    proc_pred = np.empty((pred.shape[0], 2 + 2 + 3*3), dtype=np.uint8)\n\n    # bowel, extravasation\n    proc_pred[:, 0] = sc_proc(pred[:, 0])\n    proc_pred[:, 1] = 1 - proc_pred[:, 0]\n    proc_pred[:, 2] = sc_proc(pred[:, 1])\n    proc_pred[:, 3] = 1 - proc_pred[:, 2]\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = mc_proc(pred[:, 2:5])\n    proc_pred[:, 7:10] = mc_proc(pred[:, 5:8])\n    proc_pred[:, 10:13] = mc_proc(pred[:, 8:11])\n\n    return proc_pred\n\ndef post_proc_v2(pred):\n    proc_pred = np.empty((pred.shape[0], 2*2 + 3*3), dtype='float32')\n\n    # bowel, extravasation\n    proc_pred[:, 0] = 1 - pred[:, 0] # bowel-healthy\n    proc_pred[:, 1] = pred[:, 0] # bowel-injured\n    proc_pred[:, 2] = 1 - pred[:, 1] # extra-healthy\n    proc_pred[:, 3] = pred[:, 1] # extra-injured\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = pred[:, 2:5]\n    proc_pred[:, 7:10] = pred[:, 5:8]\n    proc_pred[:, 10:13] = pred[:, 8:11]\n\n    return proc_pred","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:29.988591Z","iopub.execute_input":"2023-09-18T09:19:29.988961Z","iopub.status.idle":"2023-09-18T09:19:30.001024Z","shell.execute_reply.started":"2023-09-18T09:19:29.988907Z","shell.execute_reply":"2023-09-18T09:19:30.000097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference","metadata":{}},{"cell_type":"code","source":"# DATA\nDATASET_NAME = \"organmnist3d\"\nBATCH_SIZE = 8\nAUTO = tf.data.AUTOTUNE\nINPUT_SHAPE = (224, 224, 4, 1)\nNUM_CLASSES = 0\n\n# OPTIMIZER\nLEARNING_RATE = 1e-4\nWEIGHT_DECAY = 1e-5\n\n# TRAINING\nEPOCHS = 30\n\n# TUBELET EMBEDDING\nPATCH_SIZE = (8, 8, 2)\nNUM_PATCHES = (INPUT_SHAPE[0] // PATCH_SIZE[0]) ** 2\n\n# ViViT ARCHITECTURE\nLAYER_NORM_EPS = 1e-6\nPROJECTION_DIM = 16\nNUM_HEADS = 8\nNUM_LAYERS = 4","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.002591Z","iopub.execute_input":"2023-09-18T09:19:30.003255Z","iopub.status.idle":"2023-09-18T09:19:30.016778Z","shell.execute_reply.started":"2023-09-18T09:19:30.003222Z","shell.execute_reply":"2023-09-18T09:19:30.015659Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras import layers\nfrom tensorflow import keras","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.018397Z","iopub.execute_input":"2023-09-18T09:19:30.018757Z","iopub.status.idle":"2023-09-18T09:19:30.02861Z","shell.execute_reply.started":"2023-09-18T09:19:30.018725Z","shell.execute_reply":"2023-09-18T09:19:30.027701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TubeletEmbedding(layers.Layer):\n    def __init__(self, embed_dim, patch_size, **kwargs):\n        super().__init__(**kwargs)\n        self.projection = layers.Conv3D(\n            filters=embed_dim,\n            kernel_size=patch_size,\n            strides=(1,1,1),\n            padding=\"VALID\",\n        )\n        self.flatten = layers.Reshape(target_shape=(-1, embed_dim))\n\n    def call(self, videos):\n        projected_patches = self.projection(videos)\n        flattened_patches = self.flatten(projected_patches)\n        return flattened_patches\n    \nclass PositionalEncoder(layers.Layer):\n    def __init__(self, embed_dim, **kwargs):\n        super().__init__(**kwargs)\n        self.embed_dim = embed_dim\n\n    def build(self, input_shape):\n        _, num_tokens, _ = input_shape\n        self.position_embedding = layers.Embedding(\n            input_dim=num_tokens, output_dim=self.embed_dim\n        )\n        self.positions = tf.range(start=0, limit=num_tokens, delta=1)\n\n    def call(self, encoded_tokens):\n        # Encode the positions and add it to the encoded tokens\n        encoded_positions = self.position_embedding(self.positions)\n        encoded_tokens = encoded_tokens + encoded_positions\n        return encoded_tokens\n    \n","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.031916Z","iopub.execute_input":"2023-09-18T09:19:30.03276Z","iopub.status.idle":"2023-09-18T09:19:30.04273Z","shell.execute_reply.started":"2023-09-18T09:19:30.032726Z","shell.execute_reply":"2023-09-18T09:19:30.04178Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_vivit_classifier(\n    tubelet_embedder,\n    positional_encoder,\n    input_shape=INPUT_SHAPE,\n    transformer_layers=NUM_LAYERS,\n    num_heads=NUM_HEADS,\n    embed_dim=PROJECTION_DIM,\n    layer_norm_eps=LAYER_NORM_EPS,\n    num_classes=NUM_CLASSES,\n    compile_model=True,\n):\n    # Get the input layer\n    inputs = layers.Input(shape=input_shape)\n    # Create patches.\n    patches = tubelet_embedder(inputs)\n    # Encode patches.\n    encoded_patches = positional_encoder(patches)\n\n    # Create multiple layers of the Transformer block.\n    for _ in range(transformer_layers):\n        # Layer normalization and MHSA\n        x1 = layers.LayerNormalization(epsilon=1e-6)(encoded_patches)\n        attention_output = layers.MultiHeadAttention(\n            num_heads=num_heads, key_dim=embed_dim // num_heads, dropout=0.1\n        )(x1, x1)\n\n        # Skip connection\n        x2 = layers.Add()([attention_output, encoded_patches])\n\n        # Layer Normalization and MLP\n        x3 = layers.LayerNormalization(epsilon=1e-6)(x2)\n        x3 = keras.Sequential(\n            [\n                layers.Dense(units=embed_dim * 4, activation=tf.nn.gelu),\n                layers.Dense(units=embed_dim, activation=tf.nn.gelu),\n            ]\n        )(x3)\n\n        # Skip connection\n        encoded_patches = layers.Add()([x3, x2])\n\n    # Layer normalization and Global average pooling.\n    representation = layers.LayerNormalization(epsilon=layer_norm_eps)(encoded_patches)\n    x = layers.GlobalAvgPool1D()(representation)\n\n    # Define 'necks' for each head\n    x_bowel = tf.keras.layers.Dense(32, activation='silu')(x)\n    x_extra = tf.keras.layers.Dense(32, activation='silu')(x)\n    x_liver = tf.keras.layers.Dense(32, activation='silu')(x)\n    x_kidney = tf.keras.layers.Dense(32, activation='silu')(x)\n    x_spleen = tf.keras.layers.Dense(32, activation='silu')(x)\n\n    # Define heads\n    out_bowel = tf.keras.layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel) # use sigmoid to convert predictions to [0-1]\n    out_extra = tf.keras.layers.Dense(1, name='extra', activation='sigmoid')(x_extra) # use sigmoid to convert predictions to [0-1]\n    out_liver = tf.keras.layers.Dense(3, name='liver', activation='softmax')(x_liver) # use softmax for the liver head\n    out_kidney = tf.keras.layers.Dense(3, name='kidney', activation='softmax')(x_kidney) # use softmax for the kidney head\n    out_spleen = tf.keras.layers.Dense(3, name='spleen', activation='softmax')(x_spleen) # use softmax for the spleen head\n\n    # Combine outputs\n#     out = tf.keras.layers.Concatenate()([out_bowel, out_extra, \n#                                          out_liver, out_kidney, out_spleen])\n    out = [out_bowel, out_extra, out_liver, out_kidney, out_spleen]\n\n    # Create model\n    model = tf.keras.Model(inputs=inputs, outputs=out)\n\n    \n    if compile_model:\n        # optimizer\n        opt = tf.keras.optimizers.Adam(learning_rate=0.0001)\n        # loss\n        loss = {\n            'bowel':tf.keras.losses.BinaryCrossentropy(label_smoothing=0.05),\n            'extra':tf.keras.losses.BinaryCrossentropy(label_smoothing=0.05),\n            'liver':tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.05),\n            'kidney':tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.05),\n            'spleen':tf.keras.losses.CategoricalCrossentropy(label_smoothing=0.05),\n        }\n        # metric\n        metrics = {\n            'bowel':['accuracy'],\n            'extra':['accuracy'],\n            'liver':['accuracy'],\n            'kidney':['accuracy'],\n            'spleen':['accuracy'],\n        }\n        # compile\n        model.compile(optimizer=opt,\n                      loss=loss,\n                      metrics=metrics)\n    return model","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.044408Z","iopub.execute_input":"2023-09-18T09:19:30.044752Z","iopub.status.idle":"2023-09-18T09:19:30.064131Z","shell.execute_reply.started":"2023-09-18T09:19:30.044721Z","shell.execute_reply":"2023-09-18T09:19:30.06299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model = create_vivit_classifier(\n#         tubelet_embedder=TubeletEmbedding(\n#             embed_dim=PROJECTION_DIM, patch_size=PATCH_SIZE\n#         ),\n#         positional_encoder=PositionalEncoder(embed_dim=PROJECTION_DIM),\n#     )\n# model.summary()","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.069899Z","iopub.execute_input":"2023-09-18T09:19:30.070209Z","iopub.status.idle":"2023-09-18T09:19:30.077183Z","shell.execute_reply.started":"2023-09-18T09:19:30.070185Z","shell.execute_reply":"2023-09-18T09:19:30.07637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from vit_keras import vit\ndef channel_squeezer(x):\n    # Expand 4 channels to 32 features\n    x = tf.keras.layers.Conv2D(32, (3, 3), padding='same', activation='gelu')(x)\n    # Squeeze 32 features into 3 channels\n    x = tf.keras.layers.Conv2D(3, (3, 3), padding='same', activation='gelu')(x)\n    return x\ndef build_custom_vit_model(dim=CFG.img_size, compile_model=True):\n    # Define backbone using ViT model\n    base = vit.vit_b16(\n        image_size=dim[0],\n        pretrained=False,\n        include_top=False,\n        pretrained_top=False,\n        classes=0  # The original model has no classification head\n    )\n\n    inp = tf.keras.layers.Input((*dim, 4))\n    # Squeeze 4 channels to 3 channels\n    x = channel_squeezer(inp)\n    mlp = tf.keras.Sequential([\n        base,\n        tf.keras.layers.Flatten(),\n#         tf.keras.layers.BatchNormalization(),\n#         tf.keras.layers.Dense(128, activation = tfa.activations.gelu),\n        tf.keras.layers.BatchNormalization(),\n        tf.keras.layers.Dense(64, activation = tfa.activations.gelu)\n    ],\n    name = 'vit_b16_mlp')\n    x = mlp(x)\n#     x = tf.keras.layers.GlobalAveragePooling2D()(x)\n\n    # Define 'necks' for each head\n    x_bowel = tf.keras.layers.Dense(32, activation='gelu')(x)\n    x_extra = tf.keras.layers.Dense(32, activation='gelu')(x)\n    x_liver = tf.keras.layers.Dense(32, activation='gelu')(x)\n    x_kidney = tf.keras.layers.Dense(32, activation='gelu')(x)\n    x_spleen = tf.keras.layers.Dense(32, activation='gelu')(x)\n\n    # Define heads\n    out_bowel = tf.keras.layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel)\n    out_extra = tf.keras.layers.Dense(1, name='extra', activation='sigmoid')(x_extra)\n    out_liver = tf.keras.layers.Dense(3, name='liver', activation='softmax')(x_liver)\n    out_kidney = tf.keras.layers.Dense(3, name='kidney', activation='softmax')(x_kidney)\n    out_spleen = tf.keras.layers.Dense(3, name='spleen', activation='softmax')(x_spleen)\n\n    # Combine outputs\n    outputs = [out_bowel, out_extra, out_liver, out_kidney, out_spleen]\n\n    # Create model\n    model = tf.keras.Model(inputs=inp, outputs=outputs)\n\n    if compile_model:\n        # Optimizer\n        opt = tf.keras.optimizers.Adam(learning_rate=0.0001)\n\n        # Loss functions and metrics for each head\n        loss = {\n            'bowel': 'binary_crossentropy',\n            'extra': 'binary_crossentropy',\n            'liver': 'categorical_crossentropy',\n            'kidney': 'categorical_crossentropy',\n            'spleen': 'categorical_crossentropy',\n        }\n\n        metrics = {\n            'bowel': ['accuracy'],\n            'extra': ['accuracy'],\n            'liver': ['accuracy'],\n            'kidney': ['accuracy'],\n            'spleen': ['accuracy'],\n        }\n\n        # Compile the model\n        model.compile(optimizer=opt, loss=loss, metrics=metrics)\n\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.078701Z","iopub.execute_input":"2023-09-18T09:19:30.079427Z","iopub.status.idle":"2023-09-18T09:19:30.10569Z","shell.execute_reply.started":"2023-09-18T09:19:30.079396Z","shell.execute_reply":"2023-09-18T09:19:30.104612Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = build_custom_vit_model(compile_model = False)\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:30.107318Z","iopub.execute_input":"2023-09-18T09:19:30.10797Z","iopub.status.idle":"2023-09-18T09:19:36.765824Z","shell.execute_reply.started":"2023-09-18T09:19:30.107909Z","shell.execute_reply":"2023-09-18T09:19:36.764878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Getting unique patient IDs from test dataset\npatient_ids = test_df['patient_id'].unique()\n\n# Initializing array to store predictions\npatient_preds = np.zeros(shape=(len(patient_ids), 2*2 + 3*3), dtype='float32')\n\n# Iterating over each patient\nfor pidx, patient_id in tqdm(enumerate(patient_ids), total=len(patient_ids), desc=\"Patients \"):\n    # Query the dataframe for a particular patient\n    patient_df = test_df.query(\"patient_id == @patient_id\", engine=\"python\")\n    \n    # Initializing model predictions array\n    model_preds = np.zeros(shape=(1, 11), dtype=np.float32)\n    \n    print(\"=\"*25)\n    print(f\"   Patient ID: {patient_id}\")\n    print(\"=\"*25)\n    \n    # Iterating over each model\n    for midx, (img_size, fold_paths) in enumerate(MODEL_CONFIGS):\n        \n        # Getting image paths for a patient\n        patient_paths = patient_df.image_path.tolist()\n\n        # Setting batch size based on number of patient paths and dimension of image\n        dim = np.prod(img_size)**0.5\n        CFG.batch_size = REPLICAS * int(32 * 2)\n            \n        # Clip batch_sizs to min\n        min_bs = 2**np.floor(np.log2(len(patient_paths)))\n        CFG.batch_size = min(min_bs, CFG.batch_size)\n\n        # Building dataset for prediction\n        dtest = build_dataset(\n            patient_paths, \n            batch_size=CFG.batch_size, repeat=True, \n            shuffle=False, cache=False,\n            decode_fn=build_decoder(with_labels=False, target_size=img_size),\n        )\n        \n        # Iterating over each fold\n        for fold_path in fold_paths:\n            with strategy.scope():\n                # Loading a model from a fold path\n                model = tf.keras.models.load_model(fold_path, compile=False)\n            \n            # Predicting with the model\n            pred = model.predict(dtest, steps = len(patient_paths) / CFG.batch_size, verbose=1)\n            pred = np.concatenate(pred, axis=-1).astype('float32') # reducing memory footprint\n            pred = pred[:len(patient_paths), :]\n            pred = pred.reshape(len(patient_paths), 11)\n            pred = np.max(pred, axis=0) # taking max prediction of all ct scans for a patient\n            \n            # Store model's prediction\n            model_preds += pred / (len(fold_paths)*len(MODEL_CONFIGS))\n            \n            # Deleting variables to free up memory\n            del model, pred; gc.collect()\n            \n            print('\\n')\n        \n        del dtest, patient_paths; gc.collect()\n            \n    # Adding processed predictions to patient_preds\n    patient_preds[pidx, :] += post_proc_v2(model_preds)[0]\n    \n    del model_preds; gc.collect()\n\nprint(\"Prediction Done!\")","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:19:36.767546Z","iopub.execute_input":"2023-09-18T09:19:36.767889Z","iopub.status.idle":"2023-09-18T09:20:17.435312Z","shell.execute_reply.started":"2023-09-18T09:19:36.767856Z","shell.execute_reply":"2023-09-18T09:20:17.434158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"# Create Submission\npred_df = pd.DataFrame({'patient_id':patient_ids,})\npred_df[CFG.target_col] = patient_preds.astype('float32')\n\n# Align with sample submission\nsub_df = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')\nsub_df = sub_df[['patient_id']]\nsub_df = sub_df.merge(pred_df, on='patient_id', how='left')\n\n# Group by different sample weights\nscale_by_2 = ['kidney_low','liver_low','spleen_low', 'bowel_injury']\nscale_by_4 = ['kidney_high','liver_high','spleen_high']\nscale_by_6 = ['extravasation_injury']\nscale_healthy = ['bowel_healthy', 'extravasation_healthy', 'kidney_healthy', 'liver_healthy', 'spleen_healthy']\n\n# Scale factors based on described metric \nsf_2 = 0.950\nsf_4 = 1.40\nsf_6 = 4.60\nscale_h = 1\n# (1,2,4,6)\n# (1,4,6,28)\nalter1 = 0.9\nalter2 = 1.2\nalter3 = 1.3\nalter4 = 0.3\n\nsub_df[scale_by_2] *=(sf_2 * alter1)\nsub_df[scale_by_4] *=(sf_4 * alter2)\nsub_df[scale_by_6] *=(sf_6 * alter3)\nsub_df[scale_healthy] *=(scale_h * alter4)\n# Store submission\nsub_df.to_csv('submission.csv',index=False)\nsub_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-09-18T09:20:17.436828Z","iopub.execute_input":"2023-09-18T09:20:17.437297Z","iopub.status.idle":"2023-09-18T09:20:17.492572Z","shell.execute_reply.started":"2023-09-18T09:20:17.43726Z","shell.execute_reply":"2023-09-18T09:20:17.491614Z"},"trusted":true},"execution_count":null,"outputs":[]}]}