{"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":"## Introduction\n\nThis notebook is a fork of Christof Henkel's [notebook](https://www.kaggle.com/code/christofhenkel/se-resnext50-full-gpu-decoding), full credit goes to him for showing an E2E GPU decoding pipeline.  I've added some additional GPU optimizations to his work.  The notebook now runs in ~3.5 hrs vs 5 hours.  Optimizations include:\n\n1. DALI pipeline extension to include external source variables, windowing, scaling, all on GPU\n2. Full GPU preprocessing and optimization.\n3. No saving intermediate images to disk (all data and processing remains on GPU)\n4. TensorRT models (fp32)\n","metadata":{"execution":{"iopub.execute_input":"2023-02-05T06:44:19.900699Z","iopub.status.busy":"2023-02-05T06:44:19.900319Z","iopub.status.idle":"2023-02-05T06:44:19.920765Z","shell.execute_reply":"2023-02-05T06:44:19.919665Z","shell.execute_reply.started":"2023-02-05T06:44:19.900617Z"},"papermill":{"duration":0.016446,"end_time":"2023-02-06T12:29:18.4474","exception":false,"start_time":"2023-02-06T12:29:18.430954","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"## Requirements","metadata":{"papermill":{"duration":0.016063,"end_time":"2023-02-06T12:29:18.479831","exception":false,"start_time":"2023-02-06T12:29:18.463768","status":"completed"},"tags":[]}},{"cell_type":"code","source":"!pip install /kaggle/input/pytorch112-cu113/{torch-1.12.1+cu113-cp37-cp37m-linux_x86_64.whl,torchvision-0.13.1+cu113-cp37-cp37m-linux_x86_64.whl}\n#install nvidia-pyindex\n!pip install /kaggle/input/torch-tensorrt-pkg/nvidia_pyindex-1.0.9-py3-none-any.whl\n#install nvidia_tensorrt\n!mkdir -p /tmp/pip/cache/\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia-cublas-cu11-2022.4.8.xyz /tmp/pip/cache/nvidia-cublas-cu11-2022.4.8.tar.gz\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia-cuda-runtime-cu11-2022.4.25.xyz /tmp/pip/cache/nvidia-cuda-runtime-cu11-2022.4.25.tar.gz\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia-cudnn-cu11-2022.5.19.xyz /tmp/pip/cache/nvidia-cudnn-cu11-2022.5.19.tar.gz\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia_cublas_cu117-11.10.1.25-py3-none-manylinux1_x86_64.whl /tmp/pip/cache/\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia_cuda_runtime_cu117-11.7.60-py3-none-manylinux1_x86_64.whl /tmp/pip/cache/\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia_cudnn_cu116-8.4.0.27-py3-none-manylinux1_x86_64.whl /tmp/pip/cache/\n!cp /kaggle/input/torch-tensorrt-pkg/nvidia_tensorrt-8.4.3.1-cp37-none-linux_x86_64.whl /tmp/pip/cache/\n!pip install --no-index --find-links /tmp/pip/cache/ nvidia_tensorrt\n#install torch_tensorrt\n!pip install /kaggle/input/torch-tensorrt-pkg/torch_tensorrt-1.2.0-cp37-cp37m-linux_x86_64.whl","metadata":{"papermill":{"duration":0.02929,"end_time":"2023-02-06T12:35:47.806999","exception":false,"start_time":"2023-02-06T12:35:47.777709","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2023-02-14T16:41:54.238091Z","iopub.execute_input":"2023-02-14T16:41:54.238573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q timm==0.6.5 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements\n!pip install -q albumentations==1.2.1 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements\n!pip install -q pylibjpeg-libjpeg==1.3.1 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements\n!pip install -q pydicom==2.0.0 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements\n!pip install -q python-gdcm==3.0.20 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements\n!pip install -q dicomsdl==0.109.1 --no-index --find-links=/kaggle/input/rsna-bc-pip-requirements","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q /kaggle/input/nvidia-dali-nightly-cuda110-1230dev/nvidia_dali_nightly_cuda110-1.23.0.dev20230203-7187866-py3-none-manylinux2014_x86_64.whl","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pydicom\nfrom pydicom.filebase import DicomBytesIO\nfrom nvidia.dali import fn, math, pipeline_def, types\nfrom nvidia.dali.plugin.pytorch import feed_ndarray as feed_ndarray\nimport glob, gc\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom tqdm.notebook import tqdm\nimport timm\nimport torch.nn.functional as F\nfrom torch import nn\nfrom torch.nn.parameter import Parameter\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.cuda.amp import GradScaler, autocast\nfrom types import SimpleNamespace\nfrom typing import Any, Dict\nimport torch_tensorrt\nimport random","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_model_tensorrt(modelpath):\n    model = torch.jit.load(modelpath)\n    return model\n\ndef get_nets_trt():\n    model_array_tensorrt = [get_model_tensorrt('/kaggle/input/dietertensorrt2/checkpoint_last_seed298515.ts'),\n                            get_model_tensorrt('/kaggle/input/dietertensorrt2/checkpoint_last_seed334760.ts'),\n                            get_model_tensorrt('/kaggle/input/dietertensorrt2/checkpoint_last_seed607282.ts'),\n                            get_model_tensorrt('/kaggle/input/dietertensorrt2/checkpoint_last_seed758935.ts'),\n                            get_model_tensorrt('/kaggle/input/dietertensorrt2/checkpoint_last_seed779477.ts'),]\n    return model_array_tensorrt\n\ndef pad_dataframe(df, divisor):\n    remainder = len(df) % divisor\n    if remainder != 0:\n        to_pad = divisor - remainder\n        last_row = df.tail(1)\n        for i in range(to_pad):\n            df = df.append(last_row, ignore_index=True)\n    return df\n\ndef read_encoded_stream(filename):\n    dcmfile = pydicom.dcmread(filename)   \n    if dcmfile.file_meta.TransferSyntaxUID == '1.2.840.10008.1.2.4.90':\n        offset = dcmfile.PixelData.find(b\"\\x00\\x00\\x00\\x0C\")   #<---- the jpeg2000 header\n    else:\n        offset = dcmfile.PixelData.find(b\"\\xff\\xd8\") #<---- the jpeg lossless header    \n\n    buff = np.array(bytearray(dcmfile.PixelData[offset:]), dtype=np.uint8)\n    return buff\n\n\ndef parse_window_element(elem):\n    if type(elem)==list:\n        return float(elem[0])\n    if type(elem)==str:\n        return float(elem)\n    if type(elem)==float:\n        return elem\n    if type(elem)==pydicom.dataelem.DataElement:\n        try:\n            return float(elem[0])\n        except:\n            return float(elem.value)\n    return None\n\ndef read_params(filename):\n    dicom = pydicom.dcmread(filename, stop_before_pixels=True)\n    try:\n        invert = getattr(dicom, \"PhotometricInterpretation\", None) == \"MONOCHROME1\"\n    except:\n        invert = False\n    center = parse_window_element(dicom[\"WindowCenter\"])\n    width = parse_window_element(dicom[\"WindowWidth\"])\n    lower = center - width // 2\n    upper = center + width // 2\n    return invert, lower, upper\n\n@pipeline_def()\ndef pipe():\n    jpegs = fn.external_source(device=\"cpu\", name=\"jpeg\")\n    invert = fn.external_source(device=\"cpu\", name=\"invert\")\n    lower = fn.external_source(device=\"cpu\", name=\"lower\")\n    upper = fn.external_source(device=\"cpu\", name=\"upper\")\n    images = fn.experimental.decoders.image(jpegs, device='mixed', output_type=types.ANY_DATA, dtype=types.UINT16)\n    images = fn.cast(images, dtype=types.FLOAT)\n    images = math.clamp(images, lower, upper)\n    min_val = fn.reductions.min(images)\n    max_val = fn.reductions.max(images)\n    images = (images - min_val) / (max_val - min_val)\n    images = images * (1 - invert) + invert * (1 - images)\n    return images\n\ndef cpu_fallback(f):\n    img_cpu = pydicom.dcmread(f).pixel_array\n    invert, lower, upper = read_params(f)\n    img_cpu = img_cpu.astype(np.float32)\n    img_cpu = np.clip(img_cpu, lower, upper)\n    min_val = img_cpu.min()\n    max_val = img_cpu.max()\n    img_cpu = (img_cpu - min_val) / (max_val - min_val)\n    img_cpu = torch.from_numpy(img_cpu * (1 - invert) + invert * (1 - img_cpu))\n    img_cpu = F.interpolate(img_cpu.view(1, 1, img_cpu.size(0), img_cpu.size(1)), (img_resize, img_resize), mode=\"bilinear\")[0, 0]\n    img_cpu = img_cpu[crop_y:crop_y+cfg.img_size, crop_x:crop_x+cfg.img_size].clone()\n    img_cpu = img_cpu.unsqueeze(-1)\n    return img_cpu.to(\"cuda\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if __name__ == '__main__':\n    batch_size = 1\n    DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n    \n    cfg = SimpleNamespace(**{})\n    cfg.img_size = 1024\n    cfg.backbone = 'seresnext50_32x4d'\n    cfg.pretrained = False\n    cfg.in_channels = 1\n    cfg.classes = ['cancer']\n    cfg.batch_size = 8\n    cfg.data_folder = \"/tmp/output/\"\n    cfg.device = DEVICE\n    \n    \n   \n\n    COMP_FOLDER = '/kaggle/input/rsna-breast-cancer-detection/'\n    DATA_FOLDER = COMP_FOLDER + 'test_images/'\n\n    sample_submission = pd.read_csv(COMP_FOLDER + 'sample_submission.csv')\n\n    PUBLIC_RUN = len(sample_submission) == 2\n\n    DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n\n    RAM_CHECK = True\n    DEBUG = True\n\n    test_df = pd.read_csv(f'{COMP_FOLDER}/test.csv')\n    test_df = test_df.sample(frac=1, random_state=42).reset_index(drop=True)\n    test_df['cancer'] = 0 #dummy value\n\n\n    if PUBLIC_RUN is False:\n        RAM_CHECK = False\n        DEBUG = False\n\n    if RAM_CHECK is True:\n        test_df = pd.read_csv(f'{COMP_FOLDER}/train.csv')\n        patient_filter = list(sorted((set(test_df.patient_id.unique()))))[:8000]\n        test_df = test_df[test_df.patient_id.isin(patient_filter)]\n        DATA_FOLDER = DATA_FOLDER.replace('test','train')\n\n    if DEBUG is True:\n        test_df = test_df.head(500)\n\n    #pad dataframe because of fixed tensor_rt batch_size\n    orig_df_len = len(test_df)\n    test_df = pad_dataframe(test_df, cfg.batch_size).reset_index(drop=True)\n    test_df[\"fns\"] = test_df['patient_id'].astype(str) + '/' + test_df['image_id'].astype(str) + '.dcm'\n    files = [f'{DATA_FOLDER}/{thing}' for thing in test_df[\"fns\"]]\n    print(f'length of files {len(files)}')\n    \n    img_resize = int(cfg.img_size*1.125)\n    crop_x = int(img_resize - cfg.img_size)//2\n    crop_y = int(img_resize - cfg.img_size)//2\n    p = pipe(batch_size=batch_size, num_threads=2, device_id=0, prefetch_queue_depth=1)\n    p.build()\n    \n\n    nets = get_nets_trt()\n\n    inf_tensor_list = []\n    preds = [[] for i in range(len(nets))]\n    for i, f in tqdm(enumerate(files)):\n        try:\n            buff = read_encoded_stream(f)\n            invert, lower, upper = read_params(f)\n            p.feed_input(\"jpeg\", [buff])\n            p.feed_input(\"invert\", [np.array(invert).astype(np.int32)])\n            p.feed_input(\"lower\", [np.array(lower).astype(np.float32)])\n            p.feed_input(\"upper\", [np.array(upper).astype(np.float32)])\n            out = p.run()\n            img = out[0][0]\n            img_torch = torch.empty(img.shape(), dtype=torch.float, device=\"cuda\")\n            feed_ndarray(img, img_torch, cuda_stream=torch.cuda.current_stream(device=0))\n            img_torch = F.interpolate(img_torch.view(1, 1, img_torch.size(0), img_torch.size(1)), (img_resize, img_resize), mode=\"bilinear\")[0, 0]\n            img_torch = img_torch[crop_y:crop_y+cfg.img_size, crop_x:crop_x+cfg.img_size].clone()\n            img_torch = img_torch.unsqueeze(-1)\n            inf_tensor_list.append(img_torch)\n            if len(inf_tensor_list)==cfg.batch_size:\n                inf_tensor = torch.cat(inf_tensor_list, dim=-1)\n                inf_tensor = inf_tensor.permute(2, 0, 1).unsqueeze(1)\n                with torch.inference_mode():\n                    for i, net in enumerate(nets):\n                        logits = net(inf_tensor).sigmoid().float().detach().cpu().numpy()\n                        preds[i] += [logits]\n                inf_tensor_list = []\n\n        except Exception as e:\n            print('Exception flow')\n            try:\n                p = pipe(batch_size=batch_size, num_threads=2, device_id=0, prefetch_queue_depth=1)\n                p.build()\n                img_torch = cpu_fallback(f)\n                inf_tensor_list.append(img_torch)\n                if len(inf_tensor_list)==cfg.batch_size:\n                    inf_tensor = torch.cat(inf_tensor_list, dim=-1)\n                    inf_tensor = inf_tensor.permute(2, 0, 1).unsqueeze(1)\n                    with torch.inference_mode():\n                        for i, net in enumerate(nets):\n                            logits = net(inf_tensor).sigmoid().float().detach().cpu().numpy()\n                            preds[i] += [logits]\n                    inf_tensor_list = []\n            except Exception as e:\n                p = pipe(batch_size=batch_size, num_threads=2, device_id=0, prefetch_queue_depth=1)\n                img_torch = torch.zeros([1024, 1024, 1], dtype=torch.float32, device='cuda')\n                inf_tensor_list.append(img_torch)\n                if len(inf_tensor_list)==cfg.batch_size:\n                    inf_tensor = torch.cat(inf_tensor_list, dim=-1)\n                    inf_tensor = inf_tensor.permute(2, 0, 1).unsqueeze(1)\n                    with torch.inference_mode():\n                        for i, net in enumerate(nets):\n                            logits = net(inf_tensor).sigmoid().float().detach().cpu().numpy()\n                            preds[i] += [logits]\n                    inf_tensor_list = []\n                    \n                    \n\n                \n    \n    preds = np.array([np.concatenate(p, axis=0) for p in preds])\n    preds = preds.mean(0) #average fold predictions\n    preds = preds[:,0]\n    preds.shape\n\n    #remove padded values from df\n    test_df = test_df[:orig_df_len]\n    preds = preds[:orig_df_len]\n\n    patient_id = test_df['patient_id'].values\n    laterality = test_df['laterality'].values\n\n    prediction_id = [f'{i}_{j}' for i,j in  zip(patient_id, laterality)]\n\n    pred_df = pd.DataFrame({'prediction_id': prediction_id, 'cancer_raw': preds})\n\n\n\n    #aggregate by prediction_id , i.e. by patient_laterality\n    sub = pred_df.groupby('prediction_id')[['cancer_raw']].agg('mean')\n\n    # binarize predictions\n    th = np.quantile(sub['cancer_raw'].values,0.97935)\n    sub['cancer'] = (sub['cancer_raw'].values > th).astype(int)\n\n    sub[['cancer']].to_csv('submission.csv')\n\n    if RAM_CHECK:\n\n        def pfbeta(labels, predictions, beta):\n            #official implementation\n            y_true_count = 0\n            ctp = 0\n            cfp = 0\n\n            for idx in range(len(labels)):\n                prediction = min(max(predictions[idx], 0), 1)\n                if (labels[idx]):\n                    y_true_count += 1\n                    ctp += prediction\n        #             cfp += 1 - prediction #bugfix\n                else:\n                    cfp += prediction\n\n            beta_squared = beta * beta\n            c_precision = ctp / (ctp + cfp)\n            c_recall = ctp / y_true_count\n            if (c_precision > 0 and c_recall > 0):\n                result = (1 + beta_squared) * (c_precision * c_recall) / (beta_squared * c_precision + c_recall)\n                return result\n            else:\n                return 0\n\n        #aggregate by prediction_id , i.e. by patient_laterality\n        test_df['prediction_id'] = test_df.apply(lambda x: f'{x.patient_id}_{x.laterality}', 1)\n        test_df_gr = test_df.groupby('prediction_id')[['cancer']].agg('mean')\n\n        # Sort both the same\n        test_df_gr = test_df_gr.loc[sub.index]\n\n        y = test_df_gr['cancer'].values#.astype(np.float32)\n        y_pred = sub['cancer'].values\n\n    #     print(y.shape, y_pred.shape)\n        score = pfbeta(y, y_pred, 1)\n        print(th, score)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}