{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Extract image from zip\nimport zipfile\n\ntrain_zip = zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/train.zip\")\ntrain_zip.extractall(\"/kaggle/working/dataset\")\ntrain_zip.close()\n\ntest_zip = zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/test1.zip\")\ntest_zip.extractall(\"/kaggle/working/dataset\")\ntest_zip.close()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-11-18T16:50:55.612997Z","iopub.execute_input":"2022-11-18T16:50:55.613394Z","iopub.status.idle":"2022-11-18T16:51:14.197365Z","shell.execute_reply.started":"2022-11-18T16:50:55.613360Z","shell.execute_reply":"2022-11-18T16:51:14.194282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from typing import Callable, List, Optional, Type, Union\nfrom torch import Tensor\n\nimport torch\nimport torch.nn as nn\n\n\ndef conv3x3(in_channels: int,\n            out_channels: int,\n            stride: int = 1,\n            groups: int = 1,\n            dilation: int = 1) -> nn.Conv2d:\n    \"\"\"\n    3x3 convolution with padding\n    :param in_channels:\n    :param out_channels:\n    :param stride:\n    :param groups:\n    :param dilation:\n    :return:\n    \"\"\"\n    return nn.Conv2d(\n        in_channels,\n        out_channels,\n        kernel_size=3,\n        stride=stride,\n        padding=dilation,\n        groups=groups,\n        bias=False,\n        dilation=dilation)\n\n\ndef conv1x1(in_channels: int,\n            out_channels: int,\n            stride: int = 1) -> nn.Conv2d:\n    \"\"\"\n    1x1 convolution\n    :param in_channels:\n    :param out_channels:\n    :param stride:\n    :return:\n    \"\"\"\n    return nn.Conv2d(\n        in_channels=in_channels,\n        out_channels=out_channels,\n        kernel_size=1,\n        stride=stride,\n        bias=False)\n\n\nclass ResidualBlock(nn.Module):\n    expansion: int = 1\n\n    def __init__(\n            self,\n            in_channels: int,\n            out_channels: int,\n            stride: int = 1,\n            down_sample: Optional[nn.Module] = None,\n            groups: int = 1,\n            base_width: int = 64,\n            dilation: int = 1,\n            norm_layer: Optional[Callable[..., nn.Module]] = None):\n        \"\"\"\n        :param in_channels:\n        :param out_channels:\n        :param stride:\n        :param down_sample:\n        :param groups:\n        :param base_width:\n        :param dilation:\n        :param norm_layer:\n        \"\"\"\n        super().__init__()\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        if groups != 1 or base_width != 64:\n            raise ValueError(\"BasicBlock only supports groups=1 and base_width=64\")\n        if dilation > 1:\n            raise NotImplementedError(\"Dilation > 1 not supported in BasicBlock\")\n        # Both self.conv1 and self.down_sample layers down_sample the input when stride != 1\n        self.conv1 = conv3x3(in_channels, out_channels, stride)\n        self.bn1 = norm_layer(out_channels)\n        self.relu = nn.ReLU(inplace=True)\n        self.conv2 = conv3x3(out_channels, out_channels)\n        self.bn2 = norm_layer(out_channels)\n        self.down_sample = down_sample\n        self.stride = stride\n\n    def forward(self, x: Tensor) -> Tensor:\n        \"\"\"\n        :param x:\n        :return:\n        \"\"\"\n        identity = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n\n        if self.down_sample is not None:\n            identity = self.down_sample(x)\n\n        out += identity\n        out = self.relu(out)\n\n        return out\n\n\nclass Bottleneck(nn.Module):\n    # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)\n    # while original implementation places the stride at the first 1x1 convolution(self.conv1)\n    # according to \"Deep residual learning for image recognition\"https://arxiv.org/abs/1512.03385.\n    # This variant is also known as ResNet V1.5 and improves accuracy according to\n    # https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.\n\n    expansion: int = 4\n\n    def __init__(\n            self,\n            in_planes: int,\n            planes: int,\n            stride: int = 1,\n            down_sample: Optional[nn.Module] = None,\n            groups: int = 1,\n            base_width: int = 64,\n            dilation: int = 1,\n            norm_layer: Optional[Callable[..., nn.Module]] = None,\n    ) -> None:\n        super().__init__()\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        width = int(planes * (base_width / 64.0)) * groups\n        # Both self.conv2 and self.downsample layers downsample the input when stride != 1\n        self.conv1 = conv1x1(in_planes, width)\n        self.bn1 = norm_layer(width)\n        self.conv2 = conv3x3(width, width, stride, groups, dilation)\n        self.bn2 = norm_layer(width)\n        self.conv3 = conv1x1(width, planes * self.expansion)\n        self.bn3 = norm_layer(planes * self.expansion)\n        self.relu = nn.ReLU(inplace=True)\n        self.down_sample = down_sample\n        self.stride = stride\n\n    def forward(self, x: Tensor) -> Tensor:\n        identity = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n\n        if self.down_sample is not None:\n            identity = self.down_sample(x)\n\n        out += identity\n        out = self.relu(out)\n\n        return out\n\n\nclass ResNet(nn.Module):\n    def __init__(\n            self,\n            block: Type[Union[ResidualBlock, Bottleneck]],\n            layers: List[int],\n            num_classes: int = 1000,\n            zero_init_residual: bool = False,\n            groups: int = 1,\n            width_per_group: int = 64,\n            replace_stride_with_dilation: Optional[List[bool]] = None,\n            norm_layer: Optional[Callable[..., nn.Module]] = None,\n    ) -> None:\n        super().__init__()\n        # _log_api_usage_once(self)\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        self._norm_layer = norm_layer\n\n        self.in_planes = 64\n        self.dilation = 1\n        if replace_stride_with_dilation is None:\n            # each element in the tuple indicates if we should replace\n            # the 2x2 stride with a dilated convolution instead\n            replace_stride_with_dilation = [False, False, False]\n        if len(replace_stride_with_dilation) != 3:\n            raise ValueError(\n                \"replace_stride_with_dilation should be None \"\n                f\"or a 3-element tuple, got {replace_stride_with_dilation}\"\n            )\n        self.groups = groups\n        self.base_width = width_per_group\n        self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=7, stride=2, padding=3, bias=False)\n        self.bn1 = norm_layer(self.in_planes)\n        self.relu = nn.ReLU(inplace=True)\n        self.max_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        self.layer1 = self._make_layer(block, 64, layers[0])\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0])\n        self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1])\n        self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2])\n        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(512 * block.expansion, num_classes)\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\")\n            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):\n                nn.init.constant_(m.weight, 1)\n                nn.init.constant_(m.bias, 0)\n\n        # Zero-initialize the last BN in each residual branch,\n        # so that the residual branch starts with zeros, and each residual block behaves like an identity.\n        # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677\n        if zero_init_residual:\n            for m in self.modules():\n                if isinstance(m, Bottleneck) and m.bn3.weight is not None:\n                    nn.init.constant_(m.bn3.weight, 0)  # type: ignore[arg-type]\n                elif isinstance(m, ResidualBlock) and m.bn2.weight is not None:\n                    nn.init.constant_(m.bn2.weight, 0)  # type: ignore[arg-type]\n\n    def _make_layer(\n            self,\n            block: Type[Union[ResidualBlock, Bottleneck]],\n            planes: int,\n            blocks: int,\n            stride: int = 1,\n            dilate: bool = False,\n    ) -> nn.Sequential:\n        norm_layer = self._norm_layer\n        down_sample = None\n        previous_dilation = self.dilation\n        if dilate:\n            self.dilation *= stride\n            stride = 1\n        if stride != 1 or self.in_planes != planes * block.expansion:\n            down_sample = nn.Sequential(\n                conv1x1(self.in_planes, planes * block.expansion, stride),\n                norm_layer(planes * block.expansion),\n            )\n\n        layers = [block(\n            self.in_planes, planes, stride, down_sample, self.groups, self.base_width, previous_dilation, norm_layer\n        )]\n        self.in_planes = planes * block.expansion\n        for _ in range(1, blocks):\n            layers.append(\n                block(\n                    self.in_planes,\n                    planes,\n                    groups=self.groups,\n                    base_width=self.base_width,\n                    dilation=self.dilation,\n                    norm_layer=norm_layer,\n                )\n            )\n\n        return nn.Sequential(*layers)\n\n    def forward(self, x: Tensor) -> Tensor:\n        # See note [TorchScript super()]\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.max_pool(x)\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n\n        x = self.avg_pool(x)\n        x = torch.flatten(x, 1)\n        x = self.fc(x)\n\n        return x\n\n\nclass ResNet18(ResNet):\n    def __init__(self,\n                 num_classes: int = 1000,\n                 zero_init_residual: bool = False,\n                 groups: int = 1,\n                 width_per_group: int = 64,\n                 replace_stride_with_dilation: Optional[List[bool]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None):\n        super(ResNet18, self).__init__(block=ResidualBlock,\n                                       layers=[2, 2, 2, 2],\n                                       num_classes=num_classes,\n                                       zero_init_residual=zero_init_residual,\n                                       groups=groups,\n                                       width_per_group=width_per_group,\n                                       replace_stride_with_dilation=replace_stride_with_dilation,\n                                       norm_layer=norm_layer)\n\n\nclass ResNet34(ResNet):\n    def __init__(self,\n                 num_classes: int = 1000,\n                 zero_init_residual: bool = False,\n                 groups: int = 1,\n                 width_per_group: int = 64,\n                 replace_stride_with_dilation: Optional[List[bool]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(ResNet34, self).__init__(block=ResidualBlock,\n                                       layers=[3, 4, 6, 3],\n                                       num_classes=num_classes,\n                                       zero_init_residual=zero_init_residual,\n                                       groups=groups,\n                                       width_per_group=width_per_group,\n                                       replace_stride_with_dilation=replace_stride_with_dilation,\n                                       norm_layer=norm_layer)\n\n\nclass ResNet50(ResNet):\n    def __init__(self,\n                 num_classes: int = 1000,\n                 zero_init_residual: bool = False,\n                 groups: int = 1,\n                 width_per_group: int = 64,\n                 replace_stride_with_dilation: Optional[List[bool]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(ResNet50, self).__init__(block=Bottleneck,\n                                       layers=[3, 4, 6, 3],\n                                       num_classes=num_classes,\n                                       zero_init_residual=zero_init_residual,\n                                       groups=groups,\n                                       width_per_group=width_per_group,\n                                       replace_stride_with_dilation=replace_stride_with_dilation,\n                                       norm_layer=norm_layer)\n\n\nclass ResNet101(ResNet):\n    def __init__(self,\n                 num_classes: int = 1000,\n                 zero_init_residual: bool = False,\n                 groups: int = 1,\n                 width_per_group: int = 64,\n                 replace_stride_with_dilation: Optional[List[bool]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(ResNet101, self).__init__(block=Bottleneck,\n                                        layers=[3, 4, 23, 3],\n                                        num_classes=num_classes,\n                                        zero_init_residual=zero_init_residual,\n                                        groups=groups,\n                                        width_per_group=width_per_group,\n                                        replace_stride_with_dilation=replace_stride_with_dilation,\n                                        norm_layer=norm_layer)\n\n\nclass ResNet152(ResNet):\n    def __init__(self,\n                 num_classes: int = 1000,\n                 zero_init_residual: bool = False,\n                 groups: int = 1,\n                 width_per_group: int = 64,\n                 replace_stride_with_dilation: Optional[List[bool]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(ResNet152, self).__init__(block=Bottleneck,\n                                        layers=[3, 8, 36, 3],\n                                        num_classes=num_classes,\n                                        zero_init_residual=zero_init_residual,\n                                        groups=groups,\n                                        width_per_group=width_per_group,\n                                        replace_stride_with_dilation=replace_stride_with_dilation,\n                                        norm_layer=norm_layer)\n","metadata":{"execution":{"iopub.status.busy":"2022-11-18T16:51:14.199875Z","iopub.execute_input":"2022-11-18T16:51:14.200735Z","iopub.status.idle":"2022-11-18T16:51:14.251578Z","shell.execute_reply.started":"2022-11-18T16:51:14.200694Z","shell.execute_reply":"2022-11-18T16:51:14.250592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport os\nimport time\n\nimport numpy as np\nimport torch\nfrom PIL import Image\nfrom matplotlib import pyplot as plt\nfrom sklearn.model_selection import train_test_split\nfrom torch import nn, optim\nfrom torch.utils import data\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\nclass DogCatNet(ResNet50):\n    def __init__(self):\n        super(DogCatNet, self).__init__()\n        self.fc = nn.Sequential(\n            nn.Linear(2048, 1, bias=True),\n            nn.Sigmoid()\n        )\n\n\nclass DogCatDataset(Dataset):\n\n    def __init__(self, imgs, class_to_int, mode=\"train\", transforms=None, root_path='/kaggle/working/dataset/train'):\n\n        super().__init__()\n        self.imgs = imgs\n        self.class_to_int = class_to_int\n        self.mode = mode\n        self.transforms = transforms\n        self.dir_train = os.path.abspath(root_path)\n\n    def __getitem__(self, idx):\n\n        image_name = self.imgs[idx]\n        img = Image.open(os.path.join(self.dir_train, image_name))\n        img = img.resize((224, 224))\n\n        if self.mode == \"train\" or self.mode == \"val\":\n            # Preparing class label\n            label = self.class_to_int(image_name.split(\".\")[0])\n            label = torch.tensor(label, dtype=torch.float32)\n\n            # Apply Transforms on image\n            img = self.transforms(img)\n\n            return img, label\n\n        elif self.mode == \"test\":\n            # Apply Transforms on image\n            img = self.transforms(img)\n\n            return img\n\n    def __len__(self):\n        return len(self.imgs)\n\n\nclass DogCatTrain:\n    def __init__(self):\n        self.train_iterator = None\n        self.valid_iterator = None\n        self.test_iterator = None\n\n        # set hyper parameters\n        self.img_size = 224\n        self.means = (0, 0, 0)\n        self.stds = (1, 1, 1)\n\n        self.batch_size = 16\n\n        # Number of training epochs\n        self.num_epochs = 100\n\n        # Learning rate\n        self.lr = 0.0001\n\n        # Initiate net\n        self.net = DogCatNet()\n\n        # set device\n        self.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\n        self.net.to(self.device)\n\n        # set optimizer\n        self.optimizer = optim.Adam(self.net.parameters(), lr=self.lr)\n\n        # set criterion to calculate loss\n        self.criterion = nn.BCELoss()\n        self.criterion.to(self.device)\n\n        # Learning Rate Scheduler\n        self.lr_scheduler = torch.optim.lr_scheduler.StepLR(self.optimizer, step_size=5, gamma=0.5)\n\n    def load_dataset(self):\n        train_transforms = transforms.Compose([\n            transforms.RandomHorizontalFlip(p=0.5),\n            transforms.RandomRotation(15),\n            transforms.RandomCrop(204),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=self.means,\n                                 std=self.stds)\n        ])\n\n        test_transforms = transforms.Compose([\n            transforms.ToTensor(),\n            transforms.Normalize(mean=self.means,\n                                 std=self.stds)\n        ])\n\n        train_images = os.listdir(os.path.abspath('/kaggle/working/dataset/train'))\n        test_images = os.listdir(os.path.abspath('/kaggle/working/dataset/test1'))\n\n        train_data, valid_data = train_test_split(train_images, test_size=0.20)\n\n        train_dataset = DogCatDataset(train_data, class_to_int=self.class_to_int, mode='train',\n                                      transforms=train_transforms)\n        validation_dataset = DogCatDataset(valid_data, class_to_int=self.class_to_int, mode='val',\n                                           transforms=test_transforms)\n        test_dataset = DogCatDataset(test_images, class_to_int=self.class_to_int, mode='test',\n                                     transforms=test_transforms, root_path=os.path.abspath('/kaggle/working/dataset/test1'))\n\n        self.train_iterator = data.DataLoader(dataset=train_dataset,\n                                              shuffle=True,\n                                              num_workers=4,\n                                              batch_size=self.batch_size)\n\n        self.valid_iterator = data.DataLoader(dataset=validation_dataset,\n                                              shuffle=True,\n                                              num_workers=4,\n                                              batch_size=self.batch_size)\n\n        self.test_iterator = data.DataLoader(dataset=test_dataset,\n                                             shuffle=False,\n                                             num_workers=4,\n                                             batch_size=self.batch_size)\n\n    @staticmethod\n    def class_to_int(x: str):\n        return 0 if 'dog' in x.lower() else 1\n\n    @staticmethod\n    def accuracy(predictions, trues):\n        \"\"\"\n        :param predictions:\n        :param trues:\n        :return:\n        \"\"\"\n        # Converting predictions to 0 or 1\n        predictions = [1 if predictions[i] >= 0.5 else 0 for i in range(len(predictions))]\n\n        # Calculating accuracy by comparing predictions with true labels\n        acc = [1 if predictions[i] == trues[i] else 0 for i in range(len(predictions))]\n\n        # Summing over all correct predictions\n        acc = np.sum(acc) / len(predictions)\n\n        return acc * 100\n\n    def plot_images(self, images, labels, classes, normalize=True):\n        \"\"\"\n        :param images:\n        :param labels:\n        :param classes:\n        :param normalize:\n        :return:\n        \"\"\"\n\n        n_images = len(images)\n        rows = int(np.sqrt(n_images))\n        cols = int(np.sqrt(n_images))\n\n        fig = plt.figure(figsize=(15, 15))\n\n        for i in range(rows * cols):\n\n            ax = fig.add_subplot(rows, cols, i + 1)\n\n            image = images[i]\n\n            if normalize:\n                image = self.normalize_image(image)\n\n            ax.imshow(image.permute(1, 2, 0).cpu().numpy())\n            label = classes[labels[i]]\n            ax.set_title(label)\n            ax.axis('off')\n\n    @staticmethod\n    def format_label(label):\n        label = label.split('.')[-1]\n        label = label.replace('_', ' ')\n        label = label.title()\n        return label.replace(' ', '')\n\n    def calculate_topk_accuracy(self, y_pred, y, k=5):\n        with torch.no_grad():\n            batch_size = y.shape[0]\n            _, top_pred = y_pred.topk(k, 1)\n            top_pred = top_pred.t()\n            correct = top_pred.eq(y.view(1, -1).expand_as(top_pred))\n            correct_1 = correct[:1].reshape(-1).float().sum(0, keepdim=True)\n            correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True)\n            acc_1 = correct_1 / batch_size\n            acc_k = correct_k / batch_size\n        return acc_1, acc_k\n\n    def train(self, iterator: DataLoader):\n        gc.collect()\n        torch.cuda.empty_cache()\n\n        # Local Parameters\n        epoch_loss = []\n        epoch_acc = []\n        start_time = time.time()\n\n        # Iterating over data loader\n        for images, labels in iterator:\n            # Loading images and labels to device\n            images = images.to(self.device)\n            labels = labels.to(self.device)\n            labels = labels.reshape((labels.shape[0], 1))  # [N, 1] - to match with preds shape\n\n            # Reseting Gradients\n            self.optimizer.zero_grad()\n\n            # Forward\n            preds = self.net(images)\n\n            # Calculating Loss\n            _loss = self.criterion(preds, labels)\n            loss = _loss.item()\n            epoch_loss.append(loss)\n\n            # Calculating Accuracy\n            acc = self.accuracy(preds, labels)\n            epoch_acc.append(acc)\n\n            # Backward\n            _loss.backward()\n            self.optimizer.step()\n\n            del images\n            del labels\n\n        # Overall Epoch Results\n        end_time = time.time()\n        total_time = end_time - start_time\n\n        # Acc and Loss\n        epoch_loss = np.mean(epoch_loss)\n        epoch_acc = np.mean(epoch_acc)\n\n        return epoch_loss, epoch_acc, total_time\n\n    def evaluate(self, iterator, best_val_acc, mode='test'):\n        gc.collect()\n        torch.cuda.empty_cache()\n\n        # Local Parameters\n        epoch_loss = []\n        epoch_acc = []\n        start_time = time.time()\n\n        # Iterating over data loader\n        for images, labels in iterator:\n            # Loading images and labels to device\n            images = images.to(self.device)\n            labels = labels.to(self.device)\n            labels = labels.reshape((labels.shape[0], 1))  # [N, 1] - to match with preds shape\n\n            # Forward\n            preds = self.net(images)\n\n            # Calculating Loss\n            _loss = self.criterion(preds, labels)\n            loss = _loss.item()\n            epoch_loss.append(loss)\n\n            # Calculating Accuracy\n            acc = self.accuracy(preds, labels)\n            epoch_acc.append(acc)\n\n            del images\n            del labels\n\n        # Overall Epoch Results\n        end_time = time.time()\n        total_time = end_time - start_time\n\n        # Acc and Loss\n        epoch_loss = np.mean(epoch_loss)\n        epoch_acc = np.mean(epoch_acc)\n\n        # Saving best model\n        if epoch_acc > best_val_acc and mode == 'val':\n            best_val_acc = epoch_acc\n            torch.save(self.net.state_dict(), os.path.abspath('/kaggle/working/dataset/dog_cat_resnet50_best.pth'))\n\n        return epoch_loss, epoch_acc, total_time, best_val_acc\n\n    def train_data(self):\n        best_val_acc = 0\n        torch.cuda.empty_cache()\n\n        for epoch in range(self.num_epochs):  # loop over the dataset multiple times\n            # Training\n            print(f'Training epoch: {epoch + 1}')\n            loss, acc, _time = self.train(self.train_iterator)\n            # Print Epoch Details\n            print(f'Epoch: {epoch + 1} Loss : {loss} Acc : {acc} Time: {_time}')\n\n            # Validation\n            print(f'Validating epoch: {epoch + 1}')\n            loss, acc, _time, best_val_acc = self.evaluate(self.valid_iterator, best_val_acc=best_val_acc, mode='val')\n            # Print Epoch Details\n            print(f'Epoch: {epoch + 1} Loss : {loss} Acc : {acc} Time: {_time}')\n\n            # Test\n            # print(f'Testing epoch: {epoch + 1}')\n            # loss, acc, _time, best_val_acc = self.evaluate(self.test_iterator, best_val_acc=best_val_acc)\n            # # Print Epoch Details\n            # print(f'Epoch: {epoch + 1} Loss : {loss} Acc : {acc} Time: {_time}')\n\n        print('Finished Training')\n\n        # save model\n        model_path = os.path.abspath('/kaggle/working/dataset/dog_cat_net.pth')\n        torch.save(self.net.state_dict(), model_path)\n\n\nif __name__ == '__main__':\n#     # Extract image from zip\n#     import zipfile\n\n#     train_zip = zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/train.zip\")\n#     train_zip.extractall(\"/kaggle/working/dataset\")\n#     train_zip.close()\n\n#     test_zip = zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/test1.zip\")\n#     test_zip.extractall(\"/kaggle/working/dataset\")\n#     test_zip.close()\n\n    # prepare_train_data()\n    # init train\n    trainer = DogCatTrain()\n    trainer.load_dataset()\n    trainer.train_data()\n","metadata":{"execution":{"iopub.status.busy":"2022-11-18T16:53:35.650575Z","iopub.execute_input":"2022-11-18T16:53:35.651081Z"},"trusted":true},"execution_count":null,"outputs":[]}]}