{"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":"code","source":"!pip install ../input/monai1-6wheel/monai-0.6.0-202107081903-py3-none-any.whl","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-09-21T11:15:44.479377Z","iopub.execute_input":"2021-09-21T11:15:44.479675Z","iopub.status.idle":"2021-09-21T11:16:12.138410Z","shell.execute_reply.started":"2021-09-21T11:15:44.479606Z","shell.execute_reply":"2021-09-21T11:16:12.134321Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"Module with datasets\"\"\"\n\nimport abc\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Union\n\nfrom torch.utils.data import Dataset\n\n\nclass BaseDataset(Dataset, abc.ABC):\n    def __init__(self, list_of_paths: Union[List[Path], List[str]]):\n        self.list_of_paths = list_of_paths\n\n        self.img_key = 'image'\n        self.lbl_key = 'label'\n\n    def __getitem__(self, idx: int) -> Dict[str, Any]:\n        raise NotImplementedError('It is base dataset, use implementation!')\n\n    def __len__(self):\n        raise NotImplementedError('It is base dataset, use implementation!')\n","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:16:12.143594Z","iopub.execute_input":"2021-09-21T11:16:12.145989Z","iopub.status.idle":"2021-09-21T11:16:13.397506Z","shell.execute_reply.started":"2021-09-21T11:16:12.145956Z","shell.execute_reply":"2021-09-21T11:16:13.396628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch.nn import functional as F\n\n\nclass MBConvBlock3D(nn.Module):\n    \"\"\"\n    Mobile Inverted Residual Bottleneck Block\n    Args:\n        block_args (namedtuple): BlockArgs, see above\n        global_params (namedtuple): GlobalParam, see above\n    Attributes:\n        has_se (bool): Whether the block contains a Squeeze and Excitation layer.\n    \"\"\"\n\n    def __init__(self, block_args, global_params):\n        super().__init__()\n        self._block_args = block_args\n        self._bn_mom = 1 - global_params.batch_norm_momentum\n        self._bn_eps = global_params.batch_norm_epsilon\n        self.has_se = (self._block_args.se_ratio is not None) and (\n            0 < self._block_args.se_ratio <= 1\n        )\n        self.id_skip = block_args.id_skip  # skip connection and drop connect\n\n        # Get static or dynamic convolution depending on image size\n        Conv3d = get_same_padding_conv3d(image_size=global_params.image_size)\n\n        # Expansion phase\n        inp = self._block_args.input_filters  # number of input channels\n        oup = (\n            self._block_args.input_filters * self._block_args.expand_ratio\n        )  # number of output channels\n        if self._block_args.expand_ratio != 1:\n            self._expand_conv = Conv3d(\n                in_channels=inp, out_channels=oup, kernel_size=1, bias=False\n            )\n            self._bn0 = nn.BatchNorm3d(\n                num_features=oup, momentum=self._bn_mom, eps=self._bn_eps\n            )\n\n        # Depthwise convolution phase\n        k = self._block_args.kernel_size\n        s = self._block_args.stride\n        self._depthwise_conv = Conv3d(\n            in_channels=oup,\n            out_channels=oup,\n            groups=oup,  # groups makes it depthwise\n            kernel_size=k,\n            stride=s,\n            bias=False,\n        )\n        self._bn1 = nn.BatchNorm3d(\n            num_features=oup, momentum=self._bn_mom, eps=self._bn_eps\n        )\n\n        # Squeeze and Excitation layer, if desired\n        if self.has_se:\n            num_squeezed_channels = max(\n                1, int(self._block_args.input_filters * self._block_args.se_ratio)\n            )\n            self._se_reduce = Conv3d(\n                in_channels=oup, out_channels=num_squeezed_channels, kernel_size=1\n            )\n            self._se_expand = Conv3d(\n                in_channels=num_squeezed_channels, out_channels=oup, kernel_size=1\n            )\n\n        # Output phase\n        final_oup = self._block_args.output_filters\n        self._project_conv = Conv3d(\n            in_channels=oup, out_channels=final_oup, kernel_size=1, bias=False\n        )\n        self._bn2 = nn.BatchNorm3d(\n            num_features=final_oup, momentum=self._bn_mom, eps=self._bn_eps\n        )\n        self._swish = MemoryEfficientSwish()\n\n    def forward(self, inputs, drop_connect_rate=None):\n        \"\"\"\n        :param inputs: input tensor\n        :param drop_connect_rate: drop connect rate (float, between 0 and 1)\n        :return: output of block\n        \"\"\"\n\n        # Expansion and Depthwise Convolution\n        x = inputs\n        if self._block_args.expand_ratio != 1:\n            x = self._swish(self._bn0(self._expand_conv(inputs)))\n        x = self._swish(self._bn1(self._depthwise_conv(x)))\n\n        # Squeeze and Excitation\n        if self.has_se:\n            x_squeezed = F.adaptive_avg_pool3d(x, 1)\n            x_squeezed = self._se_expand(self._swish(self._se_reduce(x_squeezed)))\n            x = torch.sigmoid(x_squeezed) * x\n\n        x = self._bn2(self._project_conv(x))\n\n        # Skip connection and drop connect\n        input_filters, output_filters = (\n            self._block_args.input_filters,\n            self._block_args.output_filters,\n        )\n        if (\n            self.id_skip\n            and self._block_args.stride == 1\n            and input_filters == output_filters\n        ):\n            if drop_connect_rate:\n                x = drop_connect(x, p=drop_connect_rate, training=self.training)\n            x = x + inputs  # skip connection\n        return x\n\n    def set_swish(self, memory_efficient=True):\n        \"\"\"Sets swish function as memory efficient (for training) or standard (for export)\"\"\"\n        self._swish = MemoryEfficientSwish() if memory_efficient else Swish()\n\n\nclass EfficientNet3D(nn.Module):\n    \"\"\"\n    An EfficientNet model. Most easily loaded with the .from_name or .from_pretrained methods\n    Args:\n        blocks_args (list): A list of BlockArgs to construct blocks\n        global_params (namedtuple): A set of GlobalParams shared between blocks\n    Example:\n        model = EfficientNet3D.from_pretrained('efficientnet-b0')\n    \"\"\"\n\n    def __init__(self, blocks_args=None, global_params=None, in_channels=3):\n        super().__init__()\n        assert isinstance(blocks_args, list), 'blocks_args should be a list'\n        assert len(blocks_args) > 0, 'block args must be greater than 0'\n        self._global_params = global_params\n        self._blocks_args = blocks_args\n\n        # Get static or dynamic convolution depending on image size\n        Conv3d = get_same_padding_conv3d(image_size=global_params.image_size)\n\n        # Batch norm parameters\n        bn_mom = 1 - self._global_params.batch_norm_momentum\n        bn_eps = self._global_params.batch_norm_epsilon\n\n        # Stem\n        out_channels = round_filters(\n            32, self._global_params\n        )  # number of output channels\n        self._conv_stem = Conv3d(\n            in_channels, out_channels, kernel_size=3, stride=2, bias=False\n        )\n        self._bn0 = nn.BatchNorm3d(\n            num_features=out_channels, momentum=bn_mom, eps=bn_eps\n        )\n\n        # Build blocks\n        self._blocks = nn.ModuleList([])\n        for block_args in self._blocks_args:\n\n            # Update block input and output filters based on depth multiplier.\n            block_args = block_args._replace(\n                input_filters=round_filters(\n                    block_args.input_filters, self._global_params\n                ),\n                output_filters=round_filters(\n                    block_args.output_filters, self._global_params\n                ),\n                num_repeat=round_repeats(block_args.num_repeat, self._global_params),\n            )\n\n            # The first block needs to take care of stride and filter size increase.\n            self._blocks.append(MBConvBlock3D(block_args, self._global_params))\n            if block_args.num_repeat > 1:\n                block_args = block_args._replace(\n                    input_filters=block_args.output_filters, stride=1\n                )\n            for _ in range(block_args.num_repeat - 1):\n                self._blocks.append(MBConvBlock3D(block_args, self._global_params))\n\n        # Head\n        in_channels = block_args.output_filters  # output of final block\n        out_channels = round_filters(1280, self._global_params)\n        self._conv_head = Conv3d(in_channels, out_channels, kernel_size=1, bias=False)\n        self._bn1 = nn.BatchNorm3d(\n            num_features=out_channels, momentum=bn_mom, eps=bn_eps\n        )\n\n        # Final linear layer\n        self._avg_pooling = nn.AdaptiveAvgPool3d(1)\n        self._dropout = nn.Dropout(self._global_params.dropout_rate)\n        self._fc = nn.Linear(out_channels, self._global_params.num_classes)\n        self._swish = MemoryEfficientSwish()\n\n    def set_swish(self, memory_efficient=True):\n        \"\"\"Sets swish function as memory efficient (for training) or standard (for export)\"\"\"\n        self._swish = MemoryEfficientSwish() if memory_efficient else Swish()\n        for block in self._blocks:\n            block.set_swish(memory_efficient)\n\n    def extract_features(self, inputs):\n        \"\"\"Returns output of the final convolution layer\"\"\"\n\n        # Stem\n        x = self._swish(self._bn0(self._conv_stem(inputs)))\n\n        # Blocks\n        for idx, block in enumerate(self._blocks):\n            drop_connect_rate = self._global_params.drop_connect_rate\n            if drop_connect_rate:\n                drop_connect_rate *= float(idx) / len(self._blocks)\n            x = block(x, drop_connect_rate=drop_connect_rate)\n\n        # Head\n        x = self._swish(self._bn1(self._conv_head(x)))\n\n        return x\n\n    def forward(self, inputs):\n        \"\"\"Calls extract_features to extract features, applies final linear layer, and returns logits.\"\"\"\n        bs = inputs.size(0)\n        # Convolution layers\n        x = self.extract_features(inputs)\n\n        if self._global_params.include_top:\n            # Pooling and final linear layer\n            x = self._avg_pooling(x)\n            x = x.view(bs, -1)\n            x = self._dropout(x)\n            x = self._fc(x)\n        return x\n\n    @classmethod\n    def from_name(cls, model_name, override_params=None, in_channels=3):\n        cls._check_model_name_is_valid(model_name)\n        blocks_args, global_params = get_model_params(model_name, override_params)\n        return cls(blocks_args, global_params, in_channels)\n\n    @classmethod\n    def get_image_size(cls, model_name):\n        cls._check_model_name_is_valid(model_name)\n        _, _, res, _ = efficientnet_params(model_name)\n        return res\n\n    @classmethod\n    def _check_model_name_is_valid(cls, model_name):\n        \"\"\"Validates model name.\"\"\"\n        valid_models = ['efficientnet-b' + str(i) for i in range(9)]\n        if model_name not in valid_models:\n            raise ValueError('model_name should be one of: ' + ', '.join(valid_models))\n\n\"\"\"\nThis file contains helper functions for building the model and for loading model parameters.\nThese helper functions are built to mirror those in the official TensorFlow implementation.\n\"\"\"\n\nimport collections\nimport math\nimport re\nfrom functools import partial\n\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\nfrom torch.utils import model_zoo\n\n########################################################################\n############### HELPERS FUNCTIONS FOR MODEL ARCHITECTURE ###############\n########################################################################\n\n\n# Parameters for the entire model (stem, all blocks, and head)\nGlobalParams = collections.namedtuple(\n    'GlobalParams',\n    [\n        'batch_norm_momentum',\n        'batch_norm_epsilon',\n        'dropout_rate',\n        'num_classes',\n        'width_coefficient',\n        'depth_coefficient',\n        'depth_divisor',\n        'min_depth',\n        'drop_connect_rate',\n        'image_size',\n        'include_top',\n    ],\n)\n\n# Parameters for an individual model block\nBlockArgs = collections.namedtuple(\n    'BlockArgs',\n    [\n        'kernel_size',\n        'num_repeat',\n        'input_filters',\n        'output_filters',\n        'expand_ratio',\n        'id_skip',\n        'stride',\n        'se_ratio',\n    ],\n)\n\n# Change namedtuple defaults\nGlobalParams.__new__.__defaults__ = (None,) * len(GlobalParams._fields)  # type: ignore\nBlockArgs.__new__.__defaults__ = (None,) * len(BlockArgs._fields)  # type: ignore\n\n\nclass SwishImplementation(torch.autograd.Function):\n    @staticmethod\n    def forward(ctx, i):\n        result = i * torch.sigmoid(i)\n        ctx.save_for_backward(i)\n        return result\n\n    @staticmethod\n    def backward(ctx, grad_output):\n        i = ctx.saved_variables[0]\n        sigmoid_i = torch.sigmoid(i)\n        return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i)))\n\n\nclass MemoryEfficientSwish(nn.Module):\n    def forward(self, x):\n        return SwishImplementation.apply(x)\n\n\nclass Swish(nn.Module):\n    def forward(self, x):\n        return x * torch.sigmoid(x)\n\n\ndef round_filters(filters, global_params):\n    \"\"\"Calculate and round number of filters based on depth multiplier.\"\"\"\n    multiplier = global_params.width_coefficient\n    if not multiplier:\n        return filters\n    divisor = global_params.depth_divisor\n    min_depth = global_params.min_depth\n    filters *= multiplier\n    min_depth = min_depth or divisor\n    new_filters = max(min_depth, int(filters + divisor / 2) // divisor * divisor)\n    if new_filters < 0.9 * filters:  # prevent rounding by more than 10%\n        new_filters += divisor\n    return int(new_filters)\n\n\ndef round_repeats(repeats, global_params):\n    \"\"\"Round number of filters based on depth multiplier.\"\"\"\n    multiplier = global_params.depth_coefficient\n    if not multiplier:\n        return repeats\n    return int(math.ceil(multiplier * repeats))\n\n\ndef drop_connect(inputs, p, training):\n    \"\"\"Drop connect.\"\"\"\n    if not training:\n        return inputs\n    batch_size = inputs.shape[0]\n    keep_prob = 1 - p\n    random_tensor = keep_prob\n    random_tensor += torch.rand(\n        [batch_size, 1, 1, 1, 1], dtype=inputs.dtype, device=inputs.device\n    )\n    binary_tensor = torch.floor(random_tensor)\n    output = inputs / keep_prob * binary_tensor\n    return output\n\n\ndef get_same_padding_conv3d(image_size=None):\n    \"\"\"Chooses static padding if you have specified an image size, and dynamic padding otherwise.\n    Static padding is necessary for ONNX exporting of models.\"\"\"\n    if image_size is None:\n        return Conv3dDynamicSamePadding\n    else:\n        return partial(Conv3dStaticSamePadding, image_size=image_size)\n\n\nclass Conv3dDynamicSamePadding(nn.Conv3d):\n    \"\"\"3D Convolutions like TensorFlow, for a dynamic image size\"\"\"\n\n    def __init__(\n        self,\n        in_channels,\n        out_channels,\n        kernel_size,\n        stride=1,\n        dilation=1,\n        groups=1,\n        bias=True,\n    ):\n        super().__init__(\n            in_channels, out_channels, kernel_size, stride, 0, dilation, groups, bias\n        )\n        self.stride = self.stride if len(self.stride) == 3 else [self.stride[0]] * 3\n\n    def forward(self, x):\n        ih, iw, iz = x.size()[-3:]\n        kh, kw, kz = self.weight.size()[-3:]\n        sh, sw, sz = self.stride\n        oh, ow, oz = math.ceil(ih / sh), math.ceil(iw / sw), math.ceil(iz / oz)\n        pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)\n        pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)\n        pad_z = max((oz - 1) * self.stride[2] + (kz - 1) * self.dilation[2] + 1 - iz, 0)\n        if pad_h > 0 or pad_w > 0 or pad_z > 0:\n            x = F.pad(\n                x,\n                [\n                    pad_w // 2,\n                    pad_w - pad_w // 2,\n                    pad_h // 2,\n                    pad_h - pad_h // 2,\n                    pad_z // 2,\n                    pad_z - pad_z // 2,\n                ],\n            )\n        return F.conv3d(\n            x,\n            self.weight,\n            self.bias,\n            self.stride,\n            self.padding,\n            self.dilation,\n            self.groups,\n        )\n\n\nclass Conv3dStaticSamePadding(nn.Conv3d):\n    \"\"\"3D Convolutions like TensorFlow, for a fixed image size\"\"\"\n\n    def __init__(\n        self, in_channels, out_channels, kernel_size, image_size=None, **kwargs\n    ):\n        super().__init__(in_channels, out_channels, kernel_size, **kwargs)\n        self.stride = self.stride if len(self.stride) == 3 else [self.stride[0]] * 3\n\n        # Calculate padding based on image size and save it\n        assert image_size is not None\n        ih, iw, iz = (\n            image_size\n            if type(image_size) == list\n            else [image_size, image_size, image_size]\n        )\n        kh, kw, kz = self.weight.size()[-3:]\n        sh, sw, sz = self.stride\n        oh, ow, oz = math.ceil(ih / sh), math.ceil(iw / sw), math.ceil(iz / sz)\n        pad_h = max((oh - 1) * self.stride[0] + (kh - 1) * self.dilation[0] + 1 - ih, 0)\n        pad_w = max((ow - 1) * self.stride[1] + (kw - 1) * self.dilation[1] + 1 - iw, 0)\n        pad_z = max((oz - 1) * self.stride[2] + (kz - 1) * self.dilation[2] + 1 - iz, 0)\n        if pad_h > 0 or pad_w > 0 or pad_z > 0:\n            self.static_padding = nn.ZeroPad2d(\n                (\n                    pad_w // 2,\n                    pad_w - pad_w // 2,\n                    pad_h // 2,\n                    pad_h - pad_h // 2,\n                    pad_z // 2,\n                    pad_z - pad_z // 2,\n                )\n            )\n        else:\n            self.static_padding = Identity()\n\n    def forward(self, x):\n        x = self.static_padding(x)\n        x = F.conv3d(\n            x,\n            self.weight,\n            self.bias,\n            self.stride,\n            self.padding,\n            self.dilation,\n            self.groups,\n        )\n        return x\n\n\nclass Identity(nn.Module):\n    def __init__(\n        self,\n    ):\n        super(Identity, self).__init__()\n\n    def forward(self, input):\n        return input\n\n\n########################################################################\n############## HELPERS FUNCTIONS FOR LOADING MODEL PARAMS ##############\n########################################################################\n\n\ndef efficientnet_params(model_name):\n    \"\"\"Map EfficientNet model name to parameter coefficients.\"\"\"\n    params_dict = {\n        # Coefficients:   width,depth,res,dropout\n        'efficientnet-b0': (1.0, 1.0, 224, 0.2),\n        'efficientnet-b1': (1.0, 1.1, 240, 0.2),\n        'efficientnet-b2': (1.1, 1.2, 260, 0.3),\n        'efficientnet-b3': (1.2, 1.4, 300, 0.3),\n        'efficientnet-b4': (1.4, 1.8, 380, 0.4),\n        'efficientnet-b5': (1.6, 2.2, 456, 0.4),\n        'efficientnet-b6': (1.8, 2.6, 528, 0.5),\n        'efficientnet-b7': (2.0, 3.1, 600, 0.5),\n        'efficientnet-b8': (2.2, 3.6, 672, 0.5),\n        'efficientnet-l2': (4.3, 5.3, 800, 0.5),\n    }\n    return params_dict[model_name]\n\n\nclass BlockDecoder(object):\n    \"\"\"Block Decoder for readability, straight from the official TensorFlow repository\"\"\"\n\n    @staticmethod\n    def _decode_block_string(block_string):\n        \"\"\"Gets a block through a string notation of arguments.\"\"\"\n        assert isinstance(block_string, str)\n\n        ops = block_string.split('_')\n        options = {}\n        for op in ops:\n            splits = re.split(r'(\\d.*)', op)\n            if len(splits) >= 2:\n                key, value = splits[:2]\n                options[key] = value\n\n        # Check stride\n        assert ('s' in options and len(options['s']) == 1) or (\n            len(options['s']) == 3\n            and options['s'][0] == options['s'][1] == options['s'][2]\n        )\n\n        return BlockArgs(\n            kernel_size=int(options['k']),\n            num_repeat=int(options['r']),\n            input_filters=int(options['i']),\n            output_filters=int(options['o']),\n            expand_ratio=int(options['e']),\n            id_skip=('noskip' not in block_string),\n            se_ratio=float(options['se']) if 'se' in options else None,\n            stride=[int(options['s'][0])],\n        )\n\n    @staticmethod\n    def _encode_block_string(block):\n        \"\"\"Encodes a block to a string.\"\"\"\n        args = [\n            'r%d' % block.num_repeat,\n            'k%d' % block.kernel_size,\n            's%d%d%d' % (block.strides[0], block.strides[1], block.strides[2]),\n            'e%s' % block.expand_ratio,\n            'i%d' % block.input_filters,\n            'o%d' % block.output_filters,\n        ]\n        if 0 < block.se_ratio <= 1:\n            args.append('se%s' % block.se_ratio)\n        if block.id_skip is False:\n            args.append('noskip')\n        return '_'.join(args)\n\n    @staticmethod\n    def decode(string_list):\n        \"\"\"\n        Decodes a list of string notations to specify blocks inside the network.\n        :param string_list: a list of strings, each string is a notation of block\n        :return: a list of BlockArgs namedtuples of block args\n        \"\"\"\n        assert isinstance(string_list, list)\n        blocks_args = []\n        for block_string in string_list:\n            blocks_args.append(BlockDecoder._decode_block_string(block_string))\n        return blocks_args\n\n    @staticmethod\n    def encode(blocks_args):\n        \"\"\"\n        Encodes a list of BlockArgs to a list of strings.\n        :param blocks_args: a list of BlockArgs namedtuples of block args\n        :return: a list of strings, each string is a notation of block\n        \"\"\"\n        block_strings = []\n        for block in blocks_args:\n            block_strings.append(BlockDecoder._encode_block_string(block))\n        return block_strings\n\n\ndef efficientnet3d(\n    width_coefficient=None,\n    depth_coefficient=None,\n    dropout_rate=0.2,\n    drop_connect_rate=0.2,\n    image_size=None,\n    num_classes=1000,\n    include_top=True,\n):\n    \"\"\"Creates a efficientnet model.\"\"\"\n\n    blocks_args = [\n        'r1_k3_s222_e1_i32_o16_se0.25',\n        'r2_k3_s222_e6_i16_o24_se0.25',\n        'r2_k5_s222_e6_i24_o40_se0.25',\n        'r3_k3_s222_e6_i40_o80_se0.25',\n        'r3_k5_s111_e6_i80_o112_se0.25',\n        'r4_k5_s222_e6_i112_o192_se0.25',\n        'r1_k3_s111_e6_i192_o320_se0.25',\n    ]\n    blocks_args = BlockDecoder.decode(blocks_args)\n\n    global_params = GlobalParams(\n        batch_norm_momentum=0.99,\n        batch_norm_epsilon=1e-3,\n        dropout_rate=dropout_rate,\n        drop_connect_rate=drop_connect_rate,\n        # data_format='channels_last',  # removed, this is always true in PyTorch\n        num_classes=num_classes,\n        width_coefficient=width_coefficient,\n        depth_coefficient=depth_coefficient,\n        depth_divisor=8,\n        min_depth=None,\n        image_size=image_size,\n        include_top=include_top,\n    )\n\n    return blocks_args, global_params\n\n\ndef get_model_params(model_name, override_params):\n    \"\"\"Get the block args and global params for a given model\"\"\"\n    if model_name.startswith('efficientnet'):\n        w, d, s, p = efficientnet_params(model_name)\n        # note: all models have drop connect rate = 0.2\n        blocks_args, global_params = efficientnet3d(\n            width_coefficient=w, depth_coefficient=d, dropout_rate=p, image_size=s\n        )\n    else:\n        raise NotImplementedError('model name is not pre-defined: %s' % model_name)\n    if override_params:\n        # ValueError will be raised here if override_params has fields not included in global_params.\n        global_params = global_params._replace(**override_params)\n    return blocks_args, global_params\n","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:16:13.399623Z","iopub.execute_input":"2021-09-21T11:16:13.399895Z","iopub.status.idle":"2021-09-21T11:16:13.483087Z","shell.execute_reply.started":"2021-09-21T11:16:13.399870Z","shell.execute_reply":"2021-09-21T11:16:13.482094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pathlib import Path\nfrom typing import List, Optional, Sequence, Tuple, Union\n\nimport numpy as np\nfrom monai.transforms import (\n    AddChanneld,\n    Compose,\n    LoadImaged,\n    Resized,\n    ScaleIntensityRanged,\n)\nfrom torch.utils.data import DataLoader, Dataset\n\n\n\ndef get_preprocessing_transforms(\n    img_key: str,\n    original_min: float = 0.0,\n    original_max: float = 200.0,\n    res_min: float = 0.0,\n    res_max: float = 1.0,\n    spatial_size: Tuple[int, int, int] = (196, 196, 128),\n) -> Compose:\n    preprocessing_transforms = Compose(\n        [\n            AddChanneld(keys=[img_key]),\n            ScaleIntensityRanged(\n                keys=[img_key],\n                a_min=original_min,\n                a_max=original_max,\n                b_min=res_min,\n                b_max=res_max,\n                clip=True,\n            ),\n            Resized(keys=[img_key], spatial_size=spatial_size),\n        ]\n    )\n\n    return preprocessing_transforms","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:16:13.484903Z","iopub.execute_input":"2021-09-21T11:16:13.485504Z","iopub.status.idle":"2021-09-21T11:16:16.255119Z","shell.execute_reply.started":"2021-09-21T11:16:13.485462Z","shell.execute_reply":"2021-09-21T11:16:16.254337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"Module with evaluation dataset\"\"\"\n\nfrom pathlib import Path\nfrom typing import Dict, List, Tuple, Union\n\nimport numpy as np\nimport nibabel as nib\nimport SimpleITK as sitk\n\n\nclass BrainDicomEvalDataset(BaseDataset):\n    def __init__(\n        self,\n        list_of_paths: Union[List[Path], List[str]],\n        spatial_size: Tuple[int, int, int] = (196, 196, 128),\n    ):\n        super().__init__(list_of_paths=list_of_paths)\n\n        self.list_of_dicom_folder_paths = list_of_paths\n\n        self.preprocessing_transforms = get_preprocessing_transforms(\n            img_key=self.img_key,\n            original_min=-200,\n            original_max=2500,\n            res_min=0,\n            res_max=1,\n            spatial_size=spatial_size,\n        )\n\n    def __getitem__(self, idx: int) -> Dict:\n        dicom_folder_path = self.list_of_dicom_folder_paths[idx]\n        self.__save_dicom(dicom_folder_path=dicom_folder_path)\n        image = self._load_ct(ct_path='temp.nii')\n\n        item = {self.img_key: image}\n        item = self.preprocessing_transforms(item)\n\n        return item\n\n    def __len__(self) -> int:\n        return len(self.list_of_dicom_folder_paths)\n\n    @staticmethod\n    def __save_dicom(dicom_folder_path: Union[str, Path]) -> None:\n        sitk.ProcessObject_SetGlobalWarningDisplay(False)\n\n        series_ids = sitk.ImageSeriesReader.GetGDCMSeriesIDs(\n            directory=str(dicom_folder_path)\n        )\n        series_file_names = sitk.ImageSeriesReader.GetGDCMSeriesFileNames(\n            str(dicom_folder_path), series_ids[0]\n        )\n        series_reader = sitk.ImageSeriesReader()\n        series_reader.SetFileNames(series_file_names)\n        series_reader.LoadPrivateTagsOn()\n        image = series_reader.Execute()\n\n        sitk.WriteImage(\n            image=image, fileName='temp.nii', useCompression=False\n        )\n    \n    @staticmethod\n    def _load_ct(ct_path: Union[str, Path]) -> np.ndarray:\n        ct_path = str(ct_path)\n\n        ct_all_info: nib.Nifti1Image = nib.load(filename=ct_path)\n        orig_ornt = nib.io_orientation(ct_all_info.affine)\n        targ_ornt = nib.orientations.axcodes2ornt(axcodes='LPS')\n        transform = nib.orientations.ornt_transform(\n            start_ornt=orig_ornt, end_ornt=targ_ornt\n        )\n\n        img_ornt = ct_all_info.as_reoriented(ornt=transform)\n\n        return img_ornt.get_fdata(dtype=np.float64)\n    \n    @staticmethod\n    def __align_ct(ct_all_info: nib.Nifti1Image) -> np.ndarray:\n        orig_ornt = nib.io_orientation(ct_all_info.affine)\n        targ_ornt = nib.orientations.axcodes2ornt(axcodes='LPS')\n        transform = nib.orientations.ornt_transform(\n            start_ornt=orig_ornt, end_ornt=targ_ornt\n        )\n    \n        img_ornt = ct_all_info.as_reoriented(ornt=transform)\n        \n        return img_ornt.get_fdata()\n\n    @staticmethod\n    def _make_affine(image_and_meta: sitk.Image):\n        # get affine transform in LPS\n        c = [image_and_meta.TransformContinuousIndexToPhysicalPoint(p)\n             for p in ((1, 0, 0),\n                       (0, 1, 0),\n                       (0, 0, 1),\n                       (0, 0, 0))]\n        c = np.array(c)\n        affine = np.concatenate([\n            np.concatenate([c[0:3] - c[3:], c[3:]], axis=0),\n            [[0.], [0.], [0.], [1.]]\n        ], axis=1)\n        affine = np.transpose(affine)\n        # convert to RAS to match nibabel\n        affine = np.matmul(np.diag([-1., -1., 1., 1.]), affine)\n        return affine\n\n","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:16:16.256384Z","iopub.execute_input":"2021-09-21T11:16:16.256706Z","iopub.status.idle":"2021-09-21T11:16:16.598246Z","shell.execute_reply.started":"2021-09-21T11:16:16.256673Z","shell.execute_reply":"2021-09-21T11:16:16.597357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"Module with class for model evaluation\"\"\"\n\nimport os\nfrom pathlib import Path\nfrom typing import List, Tuple, Union\n\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom tqdm import tqdm\n\n\nclass ModelEvaluator:\n    _CSV_COLUMN_NAMES = ['BraTS21ID', 'MGMT_value']\n\n    def __init__(\n        self,\n        model: torch.nn.Module,\n        dataset: BaseDataset,\n        device: torch.device = torch.device('cpu'),\n    ):\n        self.model = model\n        self.dataset = dataset\n        self.device = device\n\n    def eval(\n        self, save_to_csv: bool = False, csv_filepath: str = 'submission.csv'\n    ) -> List[Tuple[int, int]]:\n        evaluation_result = []\n\n        for idx, dataset_item in enumerate(tqdm(self.dataset, postfix='Evaluation...')):\n            image = dataset_item[self.dataset.img_key]\n            image_path = self.dataset.list_of_paths[idx]\n            image_idx = self._get_image_idx(image_path=image_path)\n\n            label = self._predict_one_item(image=image)\n\n            evaluation_result.append((image_idx, label))\n\n        if save_to_csv:\n            self._save_evaluation_result_to_csv(\n                evaluation_result=evaluation_result, filename=csv_filepath\n            )\n\n        return evaluation_result\n\n    def _predict_one_item(\n        self,\n        image: np.ndarray,\n    ) -> int:\n        image_tensor = self._to_tensor(image=image)\n        image_tensor = image_tensor.unsqueeze(0).to(self.device)\n\n        result = self.model(image_tensor)\n        result = torch.softmax(result, dim=1).cpu()\n        label = result[0, 1].detach().numpy()\n\n        return label\n\n    def _save_evaluation_result_to_csv(\n        self,\n        evaluation_result: List[Tuple[int, int]],\n        filename: str = 'submission.csv',\n    ) -> None:\n        result_df = pd.DataFrame(\n            data=evaluation_result,\n            columns=self._CSV_COLUMN_NAMES,\n        )\n\n        result_df.to_csv(filename, index=False)\n\n    @staticmethod\n    def _to_tensor(image: np.ndarray) -> torch.Tensor:\n        tensor = torch.from_numpy(image)\n\n        return tensor\n\n    @staticmethod\n    def _get_image_idx(image_path: Union[str, Path]) -> int:\n        image_path = str(image_path)\n        image_case_name = image_path.split(os.sep)[-2]\n\n        image_idx = int(image_case_name)\n\n        return image_idx\n","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:16:16.599768Z","iopub.execute_input":"2021-09-21T11:16:16.600118Z","iopub.status.idle":"2021-09-21T11:16:16.614831Z","shell.execute_reply.started":"2021-09-21T11:16:16.600081Z","shell.execute_reply":"2021-09-21T11:16:16.613850Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"Module with evaluation\"\"\"\n\nimport glob\nfrom typing import Dict\n\nimport torch\n\ndef rename_keys(state_dict: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:\n    new_state_dict = {}\n\n    for layer_name, layer_weights in state_dict.items():\n        layer_name = layer_name.replace('model.', '')\n\n        new_state_dict[layer_name] = layer_weights\n\n    return new_state_dict\n\n\nif __name__ == '__main__':\n    pattern = '../input/rsna-miccai-brain-tumor-radiogenomic-classification/test/**/FLAIR'\n    model_path = '../input/brainclassificationeffnet/epoch25-step2677.ckpt'\n\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n    list_of_dicom_folder_paths = list(glob.glob(pathname=pattern, recursive=True))\n\n    dataset = BrainDicomEvalDataset(\n        list_of_paths=list_of_dicom_folder_paths,\n    )\n    \n    model_meta = torch.load(\n        model_path,\n        map_location='cpu'\n    )\n\n    model_state_dict = model_meta['state_dict']\n    model_state_dict = rename_keys(state_dict=model_state_dict)\n\n    model_depth = 10\n    num_input_channels = 1\n    num_classes = 2\n\n    model = EfficientNet3D.from_name(\n        \"efficientnet-b5\", override_params={'num_classes': 2}, in_channels=1\n    )\n    model.load_state_dict(model_state_dict)\n    model.to(device=device)\n    model.eval()\n\n    model_evaluator = ModelEvaluator(\n        model=model,\n        dataset=dataset,\n        device=device\n    )\n\n    model_evaluator.eval(save_to_csv=True)\n","metadata":{"execution":{"iopub.status.busy":"2021-09-21T11:17:44.020103Z","iopub.execute_input":"2021-09-21T11:17:44.020487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}