{"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":"This code is for ilustion of using tensorRT.  \nDo not ask me for the models, they are private dataset.\n\nAlthough NextVIT[1] itself provide code for tensorRT deployment, they are using ONNX + trtexec pipeline, which is more difficult to use in kaggle notebook. Here we are using torch-tensorRT pipeline from @tivfrvqhs5[2]. We use padding to trt compiled batch size for \"dynamic batching\".\n\n\n[1] https://github.com/bytedance/Next-ViT  \n[2] https://www.kaggle.com/code/tivfrvqhs5/torch-tensorrt-infer-fp16-and-fp32-benchmarks/notebook  \n\nsome modification needs to be made to the nextvit.py code from [1]:\n```\n[remove]    #if not torch.onnx.is_in_onnx_export() and not self.is_bn_merged:  \n[add]       if not self.is_bn_merged:  \n\n```\n\n```\n[remove]\n            #if self.use_checkpoint:\n            #    x = checkpoint.checkpoint(layer, x)\n            #else:\n```\n\nin development use the function to generate and save the trt engine file 'kaggle-nextvit-b-1536-gpu-aug0-01-swa.trt_fp16.ts'. It may take some time.\n\nNote that tenorRT generates engine file according to the GPU present. So you cannot do this locally in the PC (unless your GPU is the same as kaggle notebook). You must use kaggle notebook to generate  it.\n\n```\n\n    model.encoder.merge_bn() \n    trt_model_fp16 = torch_tensorrt.compile() ...\n    \n```\nIn deployment and submission, just use the saved engine file.\n\n\n----\n\nversion9: public share of this notebook (4hr submission)  \n\nversion10: improved speed dicom reader (1.5x faster). maybe a little loss of accuracy as it now comes different from the dicom reader used for training.  \n\n","metadata":{}},{"cell_type":"code","source":"#https://www.kaggle.com/code/tivfrvqhs5/torch-tensorrt-infer-fp16-and-fp32-benchmarks/notebook\n\nimport os\nos.environ['CUDA_MODULE_LOADING']='LAZY'\n\ntry: \n    import torch_tensorrt\n    \nexcept:\n    #upgrade pytorch to 1.12\n    !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    !pip install /kaggle/input/torch-tensorrt-pkg/nvidia_pyindex-1.0.9-py3-none-any.whl\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\n\ntry: \n    import dicomsdl\n    \nexcept:\n    !pip install /kaggle/input/rsna-2022-whl/pylibjpeg-1.4.0-py3-none-any.whl\n    !pip install /kaggle/input/rsna-2022-whl/python_gdcm-3.0.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\n    !pip install /kaggle/input/rsna-breast-mammography-00/dicomsdl-0.109.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl\n    !cp /kaggle/input/easy-load-the-image-with-nvjpeg2000/nvjpeg2k.so ./\n    \n\n\nimport torch_tensorrt\nimport tensorrt\nimport torch\nprint(torch.__version__)\n\nimport sys\nsys.path.append('/kaggle/input/rsna-breast-mammography-00')\n\nprint('install ok')","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:08:30.195656Z","iopub.execute_input":"2023-01-14T04:08:30.196071Z","iopub.status.idle":"2023-01-14T04:08:30.474393Z","shell.execute_reply.started":"2023-01-14T04:08:30.196042Z","shell.execute_reply":"2023-01-14T04:08:30.473163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here i opensource my dicom converter which is one of the fastest available after combining the hard works of various kagglers. ","metadata":{}},{"cell_type":"code","source":"#this is dicom_reader_v3a.py\n\nimport pandas as pd\nimport numpy as np\nimport cv2\n\nfrom timeit import default_timer as timer\nfrom joblib import Parallel, delayed\nfrom glob import glob\n##from tqdm import tqdm\nfrom tqdm.notebook import tqdm\n\nimport os\nimport sys\n\nimport pydicom\n\nimport dicomsdl\nimport nvjpeg2k\n\n###########################################################################################\n#from pydicom.pixel_data_handlers.util import apply_voi_lut\n\nfrom typing import (\n    Dict, Optional, Union, List, Tuple, TYPE_CHECKING, cast, Iterable,\n    ByteString\n)\nfrom pydicom.valuerep import VR\n\ndef apply_voi_lut(\n    arr: \"np.ndarray\",\n    ds: \"Dataset\",\n    index: int = 0,\n    prefer_lut: bool = True\n) -> \"np.ndarray\":\n    \"\"\"Apply a VOI lookup table or windowing operation to `arr`.\n\n    .. versionadded:: 1.4\n\n    .. versionchanged:: 2.1\n\n        Added the `prefer_lut` keyword parameter\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The :class:`~numpy.ndarray` to apply the VOI LUT or windowing operation\n        to.\n    ds : dataset.Dataset\n        A dataset containing a :dcm:`VOI LUT Module<part03/sect_C.11.2.html>`.\n        If (0028,3010) *VOI LUT Sequence* is present then returns an array\n        of ``np.uint8`` or ``np.uint16``, depending on the 3rd value of\n        (0028,3002) *LUT Descriptor*. If (0028,1050) *Window Center* and\n        (0028,1051) *Window Width* are present then returns an array of\n        ``np.float64``. If neither are present then `arr` will be returned\n        unchanged.\n    index : int, optional\n        When the VOI LUT Module contains multiple alternative views, this is\n        the index of the view to return (default ``0``).\n    prefer_lut : bool\n        When the VOI LUT Module contains both *Window Width*/*Window Center*\n        and *VOI LUT Sequence*, if ``True`` (default) then apply the VOI LUT,\n        otherwise apply the windowing operation.\n\n    Returns\n    -------\n    numpy.ndarray\n        An array with applied VOI LUT or windowing operation.\n\n    Notes\n    -----\n    When the dataset requires a modality LUT or rescale operation as part of\n    the Modality LUT module then that must be applied before any windowing\n    operation.\n\n    See Also\n    --------\n    :func:`~pydicom.pixel_data_handlers.util.apply_modality_lut`\n    :func:`~pydicom.pixel_data_handlers.util.apply_voi`\n    :func:`~pydicom.pixel_data_handlers.util.apply_windowing`\n\n    References\n    ----------\n    * DICOM Standard, Part 3, :dcm:`Annex C.11.2\n      <part03/sect_C.11.html#sect_C.11.2>`\n    * DICOM Standard, Part 3, :dcm:`Annex C.8.11.3.1.5\n      <part03/sect_C.8.11.3.html#sect_C.8.11.3.1.5>`\n    * DICOM Standard, Part 4, :dcm:`Annex N.2.1.1\n      <part04/sect_N.2.html#sect_N.2.1.1>`\n    \"\"\"\n    valid_voi = False\n    if ds.get('VOILUTSequence'):\n        ds.VOILUTSequence = cast(List[\"Dataset\"], ds.VOILUTSequence)\n        valid_voi = None not in [\n            ds.VOILUTSequence[0].get('LUTDescriptor', None),\n            ds.VOILUTSequence[0].get('LUTData', None)\n        ]\n    valid_windowing = None not in [\n        ds.get('WindowCenter', None),\n        ds.get('WindowWidth', None)\n    ]\n\n    if valid_voi and valid_windowing:\n        if prefer_lut:\n            return apply_voi(arr, ds, index)\n\n        return apply_windowing(arr, ds, index)\n\n    if valid_voi:\n        return apply_voi(arr, ds, index)\n\n    if valid_windowing:\n        return apply_windowing(arr, ds, index)\n\n    return arr\n\n\ndef apply_voi(\n    arr: \"np.ndarray\", ds: \"Dataset\", index: int = 0\n) -> \"np.ndarray\":\n    \"\"\"Apply a VOI lookup table to `arr`.\n\n    .. versionadded:: 2.1\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The :class:`~numpy.ndarray` to apply the VOI LUT to.\n    ds : dataset.Dataset\n        A dataset containing a :dcm:`VOI LUT Module<part03/sect_C.11.2.html>`.\n        If (0028,3010) *VOI LUT Sequence* is present then returns an array\n        of ``np.uint8`` or ``np.uint16``, depending on the 3rd value of\n        (0028,3002) *LUT Descriptor*, otherwise `arr` will be returned\n        unchanged.\n    index : int, optional\n        When the VOI LUT Module contains multiple alternative views, this is\n        the index of the view to return (default ``0``).\n\n    Returns\n    -------\n    numpy.ndarray\n        An array with applied VOI LUT.\n\n    See Also\n    --------\n    :func:`~pydicom.pixel_data_handlers.util.apply_modality_lut`\n    :func:`~pydicom.pixel_data_handlers.util.apply_windowing`\n\n    References\n    ----------\n    * DICOM Standard, Part 3, :dcm:`Annex C.11.2\n      <part03/sect_C.11.html#sect_C.11.2>`\n    * DICOM Standard, Part 3, :dcm:`Annex C.8.11.3.1.5\n      <part03/sect_C.8.11.3.html#sect_C.8.11.3.1.5>`\n    * DICOM Standard, Part 4, :dcm:`Annex N.2.1.1\n      <part04/sect_N.2.html#sect_N.2.1.1>`\n    \"\"\"\n    if not ds.get('VOILUTSequence'):\n        return arr\n\n    if not np.issubdtype(arr.dtype, np.integer):\n        print(#warnings.warn\n            \"Applying a VOI LUT on a float input array may give \"\n            \"incorrect results\"\n        )\n\n    # VOI LUT Sequence contains one or more items\n    item = cast(List[\"Dataset\"], ds.VOILUTSequence)[index]\n    lut_descriptor = cast(List[int], item.LUTDescriptor)\n    nr_entries = lut_descriptor[0] or 2**16\n    first_map = lut_descriptor[1]\n\n    # PS3.3 C.8.11.3.1.5: may be 8, 10-16\n    nominal_depth = lut_descriptor[2]\n    if nominal_depth in list(range(10, 17)):\n        dtype = 'uint16'\n    elif nominal_depth == 8:\n        dtype = 'uint8'\n    else:\n        raise NotImplementedError(\n            f\"'{nominal_depth}' bits per LUT entry is not supported\"\n        )\n\n    # Ambiguous VR, US or OW\n    unc_data: Iterable[int]\n    if item['LUTData'].VR == VR.OW:\n        endianness = '<' if ds.is_little_endian else '>'\n        unpack_fmt = f'{endianness}{nr_entries}H'\n        unc_data = unpack(unpack_fmt, cast(bytes, item.LUTData))\n    else:\n        unc_data = cast(List[int], item.LUTData)\n\n    lut_data: \"np.ndarray\" = np.asarray(unc_data, dtype=dtype)\n\n    # IVs < `first_map` get set to first LUT entry (i.e. index 0)\n    clipped_iv = np.zeros(arr.shape, dtype=dtype)\n    # IVs >= `first_map` are mapped by the VOI LUT\n    # `first_map` may be negative, positive or 0\n    mapped_pixels = arr >= first_map\n    clipped_iv[mapped_pixels] = arr[mapped_pixels] - first_map\n    # IVs > number of entries get set to last entry\n    np.clip(clipped_iv, 0, nr_entries - 1, out=clipped_iv)\n\n    return cast(\"np.ndarray\", lut_data[clipped_iv])\n\n\ndef apply_windowing(\n    arr: \"np.ndarray\", ds: \"Dataset\", index: int = 0\n) -> \"np.ndarray\":\n    \"\"\"Apply a windowing operation to `arr`.\n\n    .. versionadded:: 2.1\n\n    Parameters\n    ----------\n    arr : numpy.ndarray\n        The :class:`~numpy.ndarray` to apply the windowing operation to.\n    ds : dataset.Dataset\n        A dataset containing a :dcm:`VOI LUT Module<part03/sect_C.11.2.html>`.\n        If (0028,1050) *Window Center* and (0028,1051) *Window Width* are\n        present then returns an array of ``np.float64``, otherwise `arr` will\n        be returned unchanged.\n    index : int, optional\n        When the VOI LUT Module contains multiple alternative views, this is\n        the index of the view to return (default ``0``).\n\n    Returns\n    -------\n    numpy.ndarray\n        An array with applied windowing operation.\n\n    Notes\n    -----\n    When the dataset requires a modality LUT or rescale operation as part of\n    the Modality LUT module then that must be applied before any windowing\n    operation.\n\n    See Also\n    --------\n    :func:`~pydicom.pixel_data_handlers.util.apply_modality_lut`\n    :func:`~pydicom.pixel_data_handlers.util.apply_voi`\n\n    References\n    ----------\n    * DICOM Standard, Part 3, :dcm:`Annex C.11.2\n      <part03/sect_C.11.html#sect_C.11.2>`\n    * DICOM Standard, Part 3, :dcm:`Annex C.8.11.3.1.5\n      <part03/sect_C.8.11.3.html#sect_C.8.11.3.1.5>`\n    * DICOM Standard, Part 4, :dcm:`Annex N.2.1.1\n      <part04/sect_N.2.html#sect_N.2.1.1>`\n    \"\"\"\n    if \"WindowWidth\" not in ds and \"WindowCenter\" not in ds:\n        return arr\n\n    if ds.PhotometricInterpretation not in ['MONOCHROME1', 'MONOCHROME2']:\n        raise ValueError(\n            \"When performing a windowing operation only 'MONOCHROME1' and \"\n            \"'MONOCHROME2' are allowed for (0028,0004) Photometric \"\n            \"Interpretation\"\n        )\n\n    # May be LINEAR (default), LINEAR_EXACT, SIGMOID or not present, VM 1\n    voi_func = cast(str, getattr(ds, 'VOILUTFunction', 'LINEAR')).upper()\n    # VR DS, VM 1-n\n    elem = ds['WindowCenter']\n    center = (\n        cast(List[float], elem.value)[index] if elem.VM > 1 else elem.value\n    )\n    center = cast(float, center)\n    elem = ds['WindowWidth']\n    width = cast(List[float], elem.value)[index] if elem.VM > 1 else elem.value\n    width = cast(float, width)\n\n    # The output range depends on whether or not a modality LUT or rescale\n    #   operation has been applied\n    ds.BitsStored = cast(int, ds.BitsStored)\n    y_min: float\n    y_max: float\n    if ds.get('ModalityLUTSequence'):\n        # Unsigned - see PS3.3 C.11.1.1.1\n        y_min = 0\n        item = cast(List[\"Dataset\"], ds.ModalityLUTSequence)[0]\n        bit_depth = cast(List[int], item.LUTDescriptor)[2]\n        y_max = 2**bit_depth - 1\n    elif ds.PixelRepresentation == 0:\n        # Unsigned\n        y_min = 0\n        y_max = 2**ds.BitsStored - 1\n    else:\n        # Signed\n        y_min = -2**(ds.BitsStored - 1)\n        y_max = 2**(ds.BitsStored - 1) - 1\n\n    slope = ds.get('RescaleSlope', None)\n    intercept = ds.get('RescaleIntercept', None)\n    if slope is not None and intercept is not None:\n        ds.RescaleSlope = cast(float, ds.RescaleSlope)\n        ds.RescaleIntercept = cast(float, ds.RescaleIntercept)\n        # Otherwise its the actual data range\n        y_min = y_min * ds.RescaleSlope + ds.RescaleIntercept\n        y_max = y_max * ds.RescaleSlope + ds.RescaleIntercept\n\n    y_range = y_max - y_min\n    arr = arr.astype('float32')\n    #arr = arr.astype('float64')\n\n    if voi_func in ['LINEAR', 'LINEAR_EXACT']:\n        # PS3.3 C.11.2.1.2.1 and C.11.2.1.3.2\n        if voi_func == 'LINEAR':\n            if width < 1:\n                raise ValueError(\n                    \"The (0028,1051) Window Width must be greater than or \"\n                    \"equal to 1 for a 'LINEAR' windowing operation\"\n                )\n            center -= 0.5\n            width -= 1\n        elif width <= 0:\n            raise ValueError(\n                \"The (0028,1051) Window Width must be greater than 0 \"\n                \"for a 'LINEAR_EXACT' windowing operation\"\n            )\n\n        below = arr <= (center - width / 2)\n        above = arr > (center + width / 2)\n        between = np.logical_and(~below, ~above)\n\n        arr[below] = y_min\n        arr[above] = y_max\n        if between.any():\n            arr[between] = (\n                ((arr[between] - center) / width + 0.5) * y_range + y_min\n            )\n    elif voi_func == 'SIGMOID':\n        # PS3.3 C.11.2.1.3.1\n        if width <= 0:\n            raise ValueError(\n                \"The (0028,1051) Window Width must be greater than 0 \"\n                \"for a 'SIGMOID' windowing operation\"\n            )\n\n        arr = y_range / (1 + np.exp(-4 * (arr - center) / width)) + y_min\n    else:\n        raise ValueError(\n            f\"Unsupported (0028,1056) VOI LUT Function value '{voi_func}'\"\n        )\n\n    return arr\n###########################################################################################\n\ndef time_to_str(t, mode='min'):\n    if mode=='min':\n        t  = int(t)/60\n        hr = t//60\n        min = t%60\n        return '%2d hr %02d min'%(hr,min)\n\n    elif mode=='sec':\n        t   = int(t)\n        min = t//60\n        sec = t%60\n        return '%2d min %02d sec'%(min,sec)\n\n    else:\n        raise NotImplementedError\n\n\n###########################################################################################\ndef read_image(df, image_dir):\n    image = []\n    for t,d in df.iterrows():\n        image_file = f'{image_dir}/{d.machine_id}/{d.patient_id}/{d.image_id}.png'\n        m = cv2.imread(image_file,cv2.IMREAD_ANYDEPTH)\n        image.append(m)\n    return image\n\ndef make_transfer_syntax_uid(df, dcm_dir):\n    machine_id_to_transfer = {}\n    machine_id = df.machine_id.unique()\n    for i in machine_id:\n        d = df[df.machine_id == i].iloc[0]\n        f = f'{dcm_dir}/{d.patient_id}/{d.image_id}.dcm'\n        dicom = pydicom.dcmread(f)\n        machine_id_to_transfer[i] = dicom.file_meta.TransferSyntaxUID\n    return machine_id_to_transfer\n\ndef normalised_to_8bit(image, photometric_interpretation):\n    xmin = image.min()\n    xmax = image.max()\n\n    norm = np.empty_like(image, dtype=np.uint8)\n    dicomsdl.util.convert_to_uint8(image, norm, xmin, xmax)\n    if photometric_interpretation == 'MONOCHROME1':\n        norm = 255 - norm\n    return norm\n\ndef resize_image_to_height(image, image_height):\n    h, w = image.shape[:2]\n    s = image_height/h\n    if image_height!=h:\n        image = cv2.resize(image, dsize=None, fx=s, fy=s, interpolation=cv2.INTER_LINEAR)\n    return image\n\n#----------------------------------------------------------------\n# dicomsdl reader\ndef dicomsdl_to_numpy_image(ds, index=0):\n    # https://stackoverflow.com/questions/44659924/returning-numpy-arrays-via-pybind11\n    info = ds.getPixelDataInfo()\n    if info['SamplesPerPixel'] != 1:\n        raise RuntimeError('SamplesPerPixel != 1')\n\n    shape = [info['Rows'], info['Cols']]\n    dtype = info['dtype']\n    outarr = np.empty(shape, dtype=dtype)\n    ds.copyFrameData(index, outarr)\n    return outarr\n\ndef dicomsdl_parallel_process(d, dcm_dir, image_dir, image_height, is_voi_lut):\n    dcm_file = f'{dcm_dir}/{d.patient_id}/{d.image_id}.dcm'\n    ds = dicomsdl.open(dcm_file)\n    image = dicomsdl_to_numpy_image(ds)\n    image = resize_image_to_height(image, image_height)\n\n    if is_voi_lut:\n        dc = pydicom.dcmread(dcm_file)\n        image = apply_voi_lut(image, dc)\n        image = image.astype(np.float32)\n    image = normalised_to_8bit(image, ds.PhotometricInterpretation)  # +1\n\n    # save as png\n    os.makedirs(f'{image_dir}/{d.machine_id}/{d.patient_id}', exist_ok=True)\n    cv2.imwrite(f'{image_dir}/{d.machine_id}/{d.patient_id}/{d.image_id}.png', image)\n\ndef process_non_j2k(df, dcm_dir, image_dir, image_height, n_jobs, is_voi_lut=True):\n    #https://stackoverflow.com/questions/56659294/does-joblib-parallel-keep-the-original-order-of-data-passed\n    #Parallel(n_jobs=2, backend='multiprocessing')(\n    Parallel(n_jobs=n_jobs)(\n        delayed(dicomsdl_parallel_process)(d, dcm_dir, image_dir, image_height, is_voi_lut)\n        for t,d in tqdm(df.iterrows())\n    )\n\n#----------------------------------------------------------------\n# nvjpeg2k reader\n\n'''\nTransferSyntaxUID\n1.2.840.10008.1.2.4.70 = JPEG Lossless, Nonhierarchical, First- Order Prediction (Processes 14)\n1.2.840.10008.1.2.4.90 = JPEG 2000 Image Compression (Lossless Only)\n'''\nj2k_decoder = nvjpeg2k.Decoder()\n\ndef process_j2k(df, dcm_dir, image_dir, image_height, is_voi_lut=True):\n    for t, d in tqdm(df.iterrows()):\n        dcm_file = f'{dcm_dir}/{d.patient_id}/{d.image_id}.dcm'\n        dc = pydicom.dcmread(dcm_file)\n        offset = dc.PixelData.find(b'\\x00\\x00\\x00\\x0C')\n        jpeg_stream = bytearray(dc.PixelData[offset:])\n        image = j2k_decoder.decode(jpeg_stream)\n        image = resize_image_to_height(image, image_height)\n        #     jpeg_stream = bytearray(ds.PixelData[offset:])\n        #     jpeg_stream = np.array(bytearray(ds.PixelData[offset:]),np.uint8)\n\n        if is_voi_lut:\n            image = apply_voi_lut(image, dc)\n            image = image.astype(np.float32)\n        image = normalised_to_8bit(image, dc.PhotometricInterpretation)\n\n        # save as png\n        os.makedirs(f'{image_dir}/{d.machine_id}/{d.patient_id}', exist_ok=True)\n        cv2.imwrite(f'{image_dir}/{d.machine_id}/{d.patient_id}/{d.image_id}.png', image)\n\n","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:08:38.449898Z","iopub.execute_input":"2023-01-14T04:08:38.450254Z","iopub.status.idle":"2023-01-14T04:08:38.497965Z","shell.execute_reply.started":"2023-01-14T04:08:38.450223Z","shell.execute_reply":"2023-01-14T04:08:38.496963Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#from dicom_reader_v3a import *\nfrom preprocess_v3 import *\n\n\nimport pandas as pd\nimport numpy as np\nimport cv2\nfrom timeit import default_timer as timer\nfrom tqdm.notebook import tqdm\nfrom joblib import Parallel, delayed\nfrom glob import glob\nfrom sklearn import metrics\nimport gc\n \n\nimport matplotlib\n#matplotlib.use('TkAgg')  \nimport matplotlib.pyplot as plt\n\nimport torch\nfrom torch.utils.data.dataset import Dataset\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data.sampler import *\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.cuda.amp as amp\nprint( 'torch.cuda.device_count() = %d'%torch.cuda.device_count())\nprint( 'torch.cuda.get_device_properties() = %s' % str(torch.cuda.get_device_properties(0))[21:])\n\nimport timm\nprint('timm',timm.__version__)\n#print(timm.__file__)\n\nimport nvjpeg2k\nprint('import ok!')","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:09:07.316303Z","iopub.execute_input":"2023-01-14T04:09:07.316724Z","iopub.status.idle":"2023-01-14T04:09:07.326406Z","shell.execute_reply.started":"2023-01-14T04:09:07.316655Z","shell.execute_reply":"2023-01-14T04:09:07.324808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mode = [\n\n   'submit',  # submit  #local\n\n   #'do-subset',\n   'do-dicom-to-png',\n   'do-breast-box'\n]\n\n\nconvert_height = 1536\nimage_height = 1536\nimage_width  = 960\n\n\nif 'local' in mode:\n    csv_file = '/kaggle/input/rsna-breast-mammography-00/valid_df.fold0.ver02.csv'\n    dcm_dir  = '/kaggle/input/rsna-breast-cancer-detection/train_images'\n\nif 'submit' in mode:\n    csv_file = '/kaggle/input/rsna-breast-cancer-detection/test.csv'\n    dcm_dir  = '/kaggle/input/rsna-breast-cancer-detection/test_images'\n\n\ntest_df = pd.read_csv(csv_file)\nmachine_id_to_transfer = make_transfer_syntax_uid(test_df, dcm_dir)\ntest_df.loc[:, 'i'] = np.arange(len(test_df))\ntest_df.loc[:, 'TransferSyntaxUID'] = test_df.machine_id.map(machine_id_to_transfer)\nif 'local' in mode:\n    test_df.loc[:, 'prediction_id'] = test_df.patient_id.astype(str) + '_' + test_df.laterality\n    if 'do-subset' in mode: \n        test_id = [\n            #  1.2.840.10008.1.2.4.70  count(14)\n            65, 127, 152, 272, 282, 308, 477, 505, 2989, 3542, 7780, 9014, 11094, 11937,\n            #  1.2.840.10008.1.2.4.90  count(26)\n            30, 36, 90, 111, 122, 158, 204, 289, 299, 399, 425, 454, 826, 1703, 1759, 2346, 3021, 4340, 4824, 5059,\n            5769, 6654, 6658, 7053, 7493, 14292\n        ]\n        test_df = test_df[test_df.patient_id.isin(test_id)].reset_index(drop=True)\n\nprint('test_df', test_df.shape)\nprint(test_df)\nprint('')","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:09:18.099347Z","iopub.execute_input":"2023-01-14T04:09:18.099743Z","iopub.status.idle":"2023-01-14T04:09:18.216608Z","shell.execute_reply.started":"2023-01-14T04:09:18.09971Z","shell.execute_reply":"2023-01-14T04:09:18.215717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_debug_submission():\n    submit_df = pd.DataFrame({\n        'prediction_id': test_df.prediction_id,\n        'cancer': 0,\n    })\n\n    submit_df = submit_df.groupby('prediction_id').mean()  \n    submit_df.to_csv('submission.csv', index=True)\n    print('submit_df', submit_df)\n    print('')\n    ","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:01:40.976549Z","iopub.execute_input":"2023-01-14T04:01:40.976898Z","iopub.status.idle":"2023-01-14T04:01:40.983901Z","shell.execute_reply.started":"2023-01-14T04:01:40.976871Z","shell.execute_reply":"2023-01-14T04:01:40.982725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# cache all png images\npng_dir = '/kaggle/tmp/~png' \n\nif 'do-dicom-to-png' in mode: \n\n    j2k_df = test_df[test_df.TransferSyntaxUID == '1.2.840.10008.1.2.4.90'].reset_index(drop=True)\n    non_j2k_df = test_df[test_df.TransferSyntaxUID != '1.2.840.10008.1.2.4.90'].reset_index(drop=True)\n\n    print(f'process_j2k(): {len(j2k_df)}')\n    start_timer = timer()\n    process_j2k(j2k_df, dcm_dir, png_dir, convert_height)\n    #process_non_j2k(j2k_df, dcm_dir, png_dir, convert_height, n_jobs=2)\n    print(time_to_str(timer() - start_timer, 'sec'))\n\n    print(f'process_non_j2k(): {len(non_j2k_df)}')\n    start_timer = timer()\n    process_non_j2k(non_j2k_df, dcm_dir, png_dir, convert_height, n_jobs=2)  \n    print(time_to_str(timer() - start_timer, 'sec'))\n\nelse:\n    pass\n    #png_dir = f'/home/titanx/hengck/share1/kaggle/2022/rsna-breast-mammography/data/my-norm/my-lut-8bit/{convert_height}-aspect'\n\n\nif 'local' in mode: #check \n    m = cv2.imread(f'{png_dir}/21/3021/1557228175.png',cv2.IMREAD_GRAYSCALE)\n    print(m.shape)\n    print(m)\n    plt.imshow(m,cmap='bone')\n    plt.show() \n\nprint('glob', len(glob(f'{png_dir}/**/*.png', recursive=True)))\nprint('gc.collect', gc.collect())\nprint('')","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:09:24.246376Z","iopub.execute_input":"2023-01-14T04:09:24.246782Z","iopub.status.idle":"2023-01-14T04:40:23.763319Z","shell.execute_reply.started":"2023-01-14T04:09:24.246751Z","shell.execute_reply":"2023-01-14T04:40:23.761723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if 'do-breast-box' in mode:\n    test_df = test_df.drop([col for col in ['pad_breast_box','max_pad_breast_shape'] if col in test_df.columns], axis=1)\n\n    class PreprocessDataset(Dataset):\n        def __init__(self, df):\n            self.df = df\n            self.length = len(df)\n            self.image_size = 224\n\n        def __len__(self):\n            return self.length\n\n        def __getitem__(self, index):\n            d = self.df.iloc[index]\n\n            m = cv2.imread(f'{png_dir}/{d.machine_id}/{d.patient_id}/{d.image_id}.png', cv2.IMREAD_GRAYSCALE)\n            h, w = m.shape\n\n            s = self.image_size / h\n            h, w = int(s * h), int(s * w)\n            m = cv2.resize(m, dsize=(w, h), interpolation=cv2.INTER_LINEAR)\n            y = (self.image_size - h) // 2\n            x = (self.image_size - w) // 2\n            rect = (x, y, x + w, y + h)\n\n            image = np.zeros((self.image_size, self.image_size), np.uint8)\n            image[y:y + h, x:x + w] = m\n\n            r = {}\n            r['index'] = index\n            r['d'] = d\n            r['rect'] = rect\n            r['image'] = torch.from_numpy(image)\n            return r\n\n    def proprocess_collate(batch):\n        d = {}\n        key = batch[0].keys()\n        for k in key:\n            d[k] = [b[k] for b in batch] \n        d['image'] = torch.stack(d['image'], 0).unsqueeze(1)\n        return d\n\n    #--------------------------------------------------------------\n    dataset = PreprocessDataset(test_df)\n    loader = DataLoader(\n        dataset,\n        sampler = SequentialSampler(dataset),\n        batch_size  = 32,\n        drop_last   = False,\n        num_workers = 2,\n        pin_memory  = False,\n        collate_fn = proprocess_collate,\n    )\n\n    checkpoint = \\\n        f'/kaggle/input/rsna-breast-mammography-00/resnet34d-mask-kaggle-005-00000387.model.pth'  # 00000774\n    net = PreprocessNet()\n    f = torch.load(checkpoint, map_location=lambda storage, loc: storage)\n    net.load_state_dict(f['state_dict'],strict=True)\n    net.cuda()\n    net.eval()\n\n    box = []\n    laterality = []\n\n    start_timer = timer()\n    for t, batch in enumerate(loader):\n        batch_size = len(batch['index'])\n        for k in ['image']: batch[k] = batch[k].cuda()\n        batch['image'] = batch['image'].half() / 255\n\n        with torch.no_grad():\n            with amp.autocast(enabled = True):\n                output = net(batch)\n\n        output = post_process(batch, output)\n        box.extend(output['box'])\n        laterality.extend(output['laterality'])\n\n        if t==0:\n            overlay =[]\n            for b in range(4):\n                \n                o = draw_preprocess_overlay(\n                    output['image'][b],\n                    output['mask'][b],\n                    output['box'][b],\n                    output['laterality'][b],\n                )\n                overlay.append(o[...,::-1])\n\n            plt.imshow(np.hstack(overlay))\n            plt.show()\n \n\n        print(f'\\r add_breast_box(): {t}/{len(loader)} { time_to_str(timer() - start_timer, \"sec\")}', end='',\n              flush=True)\n    test_df.loc[:,'pad_breast_box']=box\n    test_df.loc[:,'old_laterality']=test_df.laterality.values\n    test_df.loc[:,'laterality']=laterality\n\n    def aggr_max_pad_breast_shape(df):\n        # print(df)\n        box = []\n        for t, d in df.iterrows():\n            # b = eval(d.pad_breast_box)\n            b = d.pad_breast_box\n            box.append(b)\n        box = np.array(box)\n\n        x0, y0, x1, y1 = box.T\n        w = x1 - x0\n        h = y1 - y0\n        max_pad_breast_box_h = h.max()\n        max_pad_breast_box_w = w.max()\n        return [max_pad_breast_box_h, max_pad_breast_box_w]\n\n    gb = test_df.groupby('patient_id').apply(aggr_max_pad_breast_shape).to_frame('max_pad_breast_shape')\n    gb = gb.reset_index(drop=False)\n    test_df = test_df.merge(gb, on=('patient_id'))\n  \nprint(test_df.iloc[0], '\\n')\nprint('gc.collect', gc.collect())\nprint('')\n\n#print('make_debug_submission')\n#make_debug_submission()","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:41:37.769561Z","iopub.execute_input":"2023-01-14T04:41:37.771029Z","iopub.status.idle":"2023-01-14T04:43:49.231833Z","shell.execute_reply.started":"2023-01-14T04:41:37.770978Z","shell.execute_reply":"2023-01-14T04:43:49.2307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def to_list(x):\n    if isinstance(x, list): return x\n    if isinstance(x, str): return eval(x)\n\nclass RsnaDataset(Dataset):\n    def __init__(self, df):\n        self.length = len(df)\n        self.df = df\n\n    def __len__(self):\n        return self.length\n\n    def __getitem__(self, index):\n        d = self.df.iloc[index]\n        m = cv2.imread(f'{png_dir}/{d.machine_id}/{d.patient_id}/{d.image_id}.png',cv2.IMREAD_GRAYSCALE)\n        h, w = m.shape\n        \n        \n        image = np.zeros((image_height, image_width), np.uint8) \n        try: #for degenerate case\n            xmin, ymin, xmax, ymax = (np.array(to_list(d.pad_breast_box)) * h).astype(int)\n            crop = m[ymin:ymax, xmin:xmax]\n\n            mh, mw = (np.array(to_list(d.max_pad_breast_shape)) * h).astype(int)\n            scale = min(image_height/mh,  image_width/mw)\n            dsize = (min(image_width, int(scale * crop.shape[1])), min(image_height, int(scale * crop.shape[0])))\n            if dsize != (crop.shape[1], crop.shape[0]):\n                crop = cv2.resize(crop, dsize=dsize, interpolation=cv2.INTER_LINEAR)\n            ch,cw = crop.shape  \n            x = (image_width  - cw) // 2\n            y = (image_height - ch) // 2\n            image[y:y + ch, x:x + cw] = crop\n\n        except:\n            crop  = m\n            scale = min(image_height / h, image_width / w)\n            dsize = (min(image_width, int(scale * crop.shape[1])), min(image_height, int(scale * crop.shape[0])))\n            if dsize != (crop.shape[1], crop.shape[0]):\n                crop = cv2.resize(crop, dsize=dsize, interpolation=cv2.INTER_LINEAR)\n            ch, cw = crop.shape\n            x = (image_width  - cw) // 2\n            y = (image_height - ch) // 2\n            image[y:y + ch, x:x + cw] = crop\n            \n\n        r = {}\n        r['index'] = index\n        r['d'] = d\n        r['image'] = torch.from_numpy(image)\n        return r\n\ndef null_collate(batch):\n    d = {}\n    key = batch[0].keys()\n    for k in key:\n        d[k] = [b[k] for b in batch]\n\n    d['image'] = torch.stack(d['image']).unsqueeze(1)\n    return d\n\n#####################################################################################################\n\nfrom timm.models.efficientnet import *\nfrom nextvit_bn_merged import *\n\nclass NextVitBNet(nn.Module):\n    def __init__(self,):\n        super(NextVitBNet, self).__init__()\n        self.register_buffer('mean', torch.FloatTensor([0.5, 0.5, 0.5]).reshape(1, 3, 1, 1))\n        self.register_buffer('std', torch.FloatTensor([0.5, 0.5, 0.5]).reshape(1, 3, 1, 1))\n        self.encoder = nextvit_base(pretrained=False)\n        self.cancer = nn.Linear(1024,1)\n\n    def forward(self, image):\n        x = image\n        batch_size,C,H,W = x.shape\n        x = (x - self.mean) / self.std\n\n        e = self.encoder.forward_features(x)\n        x = F.adaptive_avg_pool2d(e,1)\n        x = torch.flatten(x,1,3)\n        cancer = self.cancer(x).reshape(-1)\n        cancer = torch.sigmoid(cancer)\n        return cancer\n    \nclass EffB4Net(nn.Module):\n    def __init__(self,):\n        super(EffB4Net, self).__init__()\n        self.register_buffer('mean', torch.FloatTensor([0.5, 0.5, 0.5]).reshape(1, 3, 1, 1))\n        self.register_buffer('std', torch.FloatTensor([0.5, 0.5, 0.5]).reshape(1, 3, 1, 1))\n        self.encoder = efficientnet_b4(pretrained=False, drop_rate=0, drop_path_rate=0)\n        self.cancer = nn.Linear(1792,1)\n\n    def forward(self, image):\n        x = image\n        batch_size,C,H,W = x.shape\n        x = (x - self.mean) / self.std\n\n        e = self.encoder.forward_features(x)\n        x = F.adaptive_avg_pool2d(e,1)\n        x = torch.flatten(x,1,3)\n        cancer = self.cancer(x).reshape(-1)\n        cancer = torch.sigmoid(cancer)\n        return cancer\n\nprint('define model ok')","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:45:25.283892Z","iopub.execute_input":"2023-01-14T04:45:25.284284Z","iopub.status.idle":"2023-01-14T04:45:25.330043Z","shell.execute_reply.started":"2023-01-14T04:45:25.284252Z","shell.execute_reply":"2023-01-14T04:45:25.329016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 4\nif 0: #generate trt engine file for development only.\n    \n    model = NextVitBNet()\n\n    checkpoint = '/kaggle/input/rsna-breast-mammography-weight-10/nextvit-b-1536-gpu-aug0-01-swa.model.pth'\n    f = torch.load(checkpoint, map_location=lambda storage, loc: storage)\n    model.load_state_dict(f['state_dict'], strict=False)\n    model.eval()\n    model.encoder.merge_bn()\n \n  \n    # The compiled module will have precision as specified by \"op_precision\".\n    # import torch_tensorrt\n    trt_model_fp16 = torch_tensorrt.compile(\n        model,\n        inputs=[\n            torch_tensorrt.Input(\n            [batch_size, 1, image_height, image_width],\n            dtype=torch.half\n        )],\n        enabled_precisions={torch.half},  # Run with FP16\n        workspace_size=1 << 32,\n        require_full_compilation=True,\n    ) \n    torch.jit.save(trt_model_fp16, 'kaggle-nextvit-b-1536-gpu-aug0-01-swa.trt_fp16.ts')\n    #compy this file to your own dataset to use again in submission\n    print('trt_fp16 ok')\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:02:54.190116Z","iopub.status.idle":"2023-01-14T04:02:54.190914Z","shell.execute_reply.started":"2023-01-14T04:02:54.190644Z","shell.execute_reply":"2023-01-14T04:02:54.190685Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 4 \ndef run_submit():\n    threshold = 0.346938 #0.30612\n    \n    if 0:  # pytorch fp16\n        model = [\n            #[EffB4Net,   '/kaggle/input/rsna-breast-mammography-weight-10/effb4-1536-baseline-gpu-aug0-01-swa.model.pth'],\n            [NextVitBNet,   '/kaggle/input/rsna-breast-mammography-weight-10/nextvit-b-1536-gpu-aug0-01-swa.model.pth'],\n\n        ]\n        num_net = len(model)\n\n        net = []\n        for i in range(num_net):\n            Net, checkpoint = model[i]\n            n = Net()\n            f = torch.load(checkpoint, map_location=lambda storage, loc: storage)\n            n.load_state_dict(f['state_dict'], strict=False)  # True\n            n.encoder.merge_bn()\n            n.cuda()\n            n.eval()\n            net.append(n)\n            \n    if 1:  # tensort fp16\n        model = [\n            '/kaggle/input/rsna-breast-mammography-weight-10/kaggle-nextvit-b-1536-gpu-aug0-01-swa.trt_fp16.ts',\n            #'/kaggle/working/kaggle-nextvit-b-1536-gpu-aug0-01-swa.trt_fp16.ts'\n        ]\n        num_net = len(model)\n\n        net = []\n        for i in range(num_net):\n            n = torch.jit.load(model[i])\n            net.append(n)\n            \n            \n            \n    # ----\n    def pad_to_batch_size(image, batch_size):\n        B = len(image)\n        if B == batch_size:\n            return image, False\n        pad = F.pad(input=image, pad=(0, 0, 0, 0, 0, 0, 0, batch_size - B), mode='constant', value=0)\n        return pad, True\n    \n    test_dataset = RsnaDataset(test_df)\n    test_loader = DataLoader(\n        test_dataset,\n        sampler=SequentialSampler(test_dataset),\n        batch_size=batch_size,\n        drop_last=False,\n        num_workers=2,\n        pin_memory=True,\n        collate_fn=null_collate,\n    )\n\n    # ----\n    if 1:\n        result = {\n            'probability': [[] for i in range(num_net)],\n        }\n        test_num = 0\n\n        start_timer = timer()\n        for t, batch in enumerate(test_loader):\n            B = len(batch['index'])\n            image, is_pad = pad_to_batch_size(batch['image'].cuda().half() / 255, batch_size)\n            image0 = image\n            image1 = torch.flip(image, dims=[3, ])  # TTA\n            #print(image0.shape)\n \n            p = 0\n            count = 0 \n            with torch.no_grad():\n                with amp.autocast(enabled=True):\n                    for i in range(num_net):\n                        p += net[i](image0)\n                        count += 1\n\n                        p += net[i](image1)\n                        count += 1\n\n            p = p / count\n            if is_pad:\n                p = p[:B]\n\n            result['probability'].append(p.float().data.cpu().numpy())\n            test_num += B\n            print('\\r %8d / %d  %s' % (test_num, len(test_dataset), time_to_str(timer() - start_timer, 'sec')), end='',\n                  flush=True)\n            torch.cuda.empty_cache()\n        print('')\n        # ---\n        probability = np.concatenate(result['probability'])\n        probability = np.nan_to_num(probability, nan=0, posinf=1, neginf=0)\n        np.save('probability.npy', probability)\n\n    ####################################################\n    probability = np.load('probability.npy')\n    print('probability', probability.shape)\n    print('')\n\n    submit_df = pd.DataFrame({\n        'prediction_id': test_df.prediction_id,\n        'cancer': probability,\n    })\n\n    submit_df = submit_df.groupby('prediction_id').mean()\n    submit_df = submit_df.sort_index()\n    predict = submit_df.cancer.values\n\n    submit_df.loc[:, 'cancer'] = (submit_df.cancer.values > threshold).astype(np.float32)\n    predict_threshold = submit_df.cancer.values\n\n    submit_df.to_csv('submission.csv', index=True)\n    print('submit_df', submit_df)\n\n    if 'local' in mode:\n\n        def get_f1score(probability, truth, threshold):\n\n            if threshold is None:\n                predict = [probability]\n            else:\n                predict = [\n                    (probability > t).astype(np.float32) for t in threshold\n                ]\n\n            f1score = []\n            for p in predict:\n                tp = ((p >= 0.5) & (truth >= 0.5)).sum()\n                fp = ((p >= 0.5) & (truth < 0.5)).sum()\n                fn = ((p < 0.5) & (truth >= 0.5)).sum()\n\n                recall = tp / (tp + fn + 1e-3)\n                precision = tp / (tp + fp + 1e-3)\n                f1 = 2 * recall * precision / (recall + precision + 1e-3)\n                f1score.append(f1)\n            f1score = np.array(f1score)\n            return f1score\n\n        truth_df = test_df[['prediction_id', 'cancer']].groupby('prediction_id').mean()\n        truth_df = truth_df.sort_index()\n        truth = truth_df.cancer.values.astype(int)\n        print('truth_df', truth_df)\n        print('')\n\n        print(mode)\n        auc = metrics.roc_auc_score(truth, predict)\n        print('auc', auc)\n\n        thresh  = np.linspace(0, 1, 50)\n        f1score = get_f1score(predict, truth, thresh)\n        print('f1score.max()', f1score.max())\n        print('@threshold', thresh[f1score.argmax()])\n        print('')\n\n        f1score = get_f1score(predict_threshold, truth, threshold = None)\n        print('f1score', f1score.max())\n        print('@threshold', threshold)\n        print('')\n\n\n\n\n\n\nrun_submit() \nprint(f'***************ok!')\n","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:45:31.724112Z","iopub.execute_input":"2023-01-14T04:45:31.724492Z","iopub.status.idle":"2023-01-14T05:08:59.573411Z","shell.execute_reply.started":"2023-01-14T04:45:31.724459Z","shell.execute_reply":"2023-01-14T05:08:59.572035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\n \n\n['local', 'do-subset']\nauc 0.8894984326018808\nf1score.max() 0.7613699425726291\n@threshold 0.061224489795918366\n\nf1score 0.5801959128894709\n@threshold 0.30612\n\n***************ok!\n\n['local', 'do-dicom-to-png', 'do-breast-box']\n10935 / 10935  23 min 23 sec\n\nauc 0.8949804186222482\nf1score.max() 0.48569579588239525\n@threshold 0.3469387755102041\n\nf1score 0.48569579588239525\n@threshold 0.346938\n\n'''","metadata":{"execution":{"iopub.status.busy":"2023-01-14T04:02:54.194591Z","iopub.status.idle":"2023-01-14T04:02:54.195398Z","shell.execute_reply.started":"2023-01-14T04:02:54.195109Z","shell.execute_reply":"2023-01-14T04:02:54.195133Z"},"trusted":true},"execution_count":null,"outputs":[]}]}