{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"},{"sourceId":7362733,"sourceType":"datasetVersion","datasetId":4277011}],"dockerImageVersionId":30627,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport os \nimport torch.nn as nn\nfrom torch.utils.data import Dataset ,DataLoader, Subset\nimport torch.optim as optim\nimport numpy as np\nimport torch\nimport torchvision\nfrom torchvision import transforms\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom torch.utils.data import random_split\nfrom tqdm import tqdm","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:36.422668Z","iopub.execute_input":"2024-01-08T13:23:36.423027Z","iopub.status.idle":"2024-01-08T13:23:36.428812Z","shell.execute_reply.started":"2024-01-08T13:23:36.422997Z","shell.execute_reply":"2024-01-08T13:23:36.427823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CFG","metadata":{}},{"cell_type":"code","source":"data_dir = '/kaggle/input/cassava-leaf-disease-classification'\nbatch_size = 32\nepochs = 30\nnum_classes = 5\nlr_rate = 0.05\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:36.43092Z","iopub.execute_input":"2024-01-08T13:23:36.431724Z","iopub.status.idle":"2024-01-08T13:23:36.443722Z","shell.execute_reply.started":"2024-01-08T13:23:36.431688Z","shell.execute_reply":"2024-01-08T13:23:36.442902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DATA PREPARING","metadata":{}},{"cell_type":"code","source":"# Design a specific Dataset to read local data\nclass CustomDataset(Dataset):\n    # Initialize the dataset with the CSV file, root directory, and optional transform\n    def __init__(self, csv_file, root_dir, transform=None):\n        self.data_frame = pd.read_csv(csv_file)\n        self.root_dir = root_dir\n        self.transform = transform\n        \n    # Return the number of samples in the dataset\n    def __len__(self):\n        return len(self.data_frame)\n\n    # Get the file name and label for the specified index\n    def __getitem__(self, idx):\n        img_name = self.data_frame.iloc[idx, 0]\n        img_path = os.path.join(self.root_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n\n        label = int(self.data_frame.iloc[idx, 1])\n        # Apply the specified transform to the image if it exists\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n# Define data transform\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n])\n# Read images from local file\ndataset = CustomDataset(csv_file=os.path.join(data_dir, \"train.csv\"), root_dir=os.path.join(data_dir, \"train_images\"), transform=transform)\n\n# Calculate the dataset size\ndataset_size = len(dataset)\n\n# Define the ratio\ntrain_ratio = 0.8\nval_ratio = 0.1\ntest_ratio = 0.1\n\n# Calculate the size of every dataset\ntrain_size = int(train_ratio * dataset_size)\nval_size = int(val_ratio * dataset_size)\ntest_size = dataset_size - train_size - val_size\n\n# Split the dataset\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n\n# Use DataLoader to load data\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\ntest_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n\n# Print the size of dataset\nprint(f'Train dataset size: {len(train_dataset)}')\nprint(f'Validation dataset size: {len(val_dataset)}')\nprint(f'Test dataset size: {len(test_dataset)}')","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:36.445331Z","iopub.execute_input":"2024-01-08T13:23:36.445647Z","iopub.status.idle":"2024-01-08T13:23:36.47901Z","shell.execute_reply.started":"2024-01-08T13:23:36.445617Z","shell.execute_reply":"2024-01-08T13:23:36.478144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## LOAD PRETRAINED MODEL","metadata":{}},{"cell_type":"code","source":"# Design ResNet\nclass BasicBlock(nn.Module):\n    # expansion refers to the multiple of decreasing the scale to increase the dimension in each small residual block\n    expansion = 1\n \n    # init()：Start initialize\n    def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):\n        super(BasicBlock, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,\n                               kernel_size=3, stride=stride, padding=1, bias=False)\n        # Use batch normalization\n        self.bn1 = nn.BatchNorm2d(out_channel)\n        # Use ReLU as the activation function\n        self.relu = nn.ReLU()\n        self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,\n                               kernel_size=3, stride=1, padding=1, bias=False)\n        self.bn2 = nn.BatchNorm2d(out_channel)\n        self.downsample = downsample\n \n    # forward()：The forward propagation process is defined and the connections between the layers are described\n    def forward(self, x):\n        # The residual block retains the original input\n        identity = x\n        # In the case of a dashed residual structure, downsampling is performed\n        if self.downsample is not None:\n            identity = self.downsample(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        # The main branch and the shortcut branch data are added\n        out += identity\n        out = self.relu(out)\n \n        return out\n \n \n# Define the residual structure of ResNet50/101/152\nclass Bottleneck(nn.Module):\n    # expansion refers to the multiple of decreasing the scale to increase the dimension in each small residual block\n    expansion = 4\n \n    # init()：Start initialize\n    def __init__(self, in_channel, out_channel, stride=1, downsample=None,\n                 groups=1, width_per_group=64):\n        super(Bottleneck, self).__init__()\n \n        width = int(out_channel * (width_per_group / 64.)) * groups\n \n        self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,\n                               kernel_size=1, stride=1, bias=False)\n        # Use batch normalization\n        self.bn1 = nn.BatchNorm2d(width)\n        # -----------------------------------------\n        self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,\n                               kernel_size=3, stride=stride, bias=False, padding=1)\n        self.bn2 = nn.BatchNorm2d(width)\n        # -----------------------------------------\n        self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel * self.expansion,\n                               kernel_size=1, stride=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(out_channel * self.expansion)\n        # Use ReLU as the activation function\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n \n    # forward()：The forward propagation process is defined and the connections between the layers are described\n    def forward(self, x):\n        # The residual block retains the original input\n        identity = x\n        # In the case of a dashed residual structure, downsampling is performed\n        if self.downsample is not None:\n            identity = self.downsample(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        # The main branch and the shortcut branch data are added\n        out += identity\n        out = self.relu(out)\n \n        return out\n \n \n# Define ResNet class\nclass ResNet(nn.Module):\n    # initialize the function\n    def __init__(self,\n                 block,\n                 blocks_num,\n                 num_classes=1000,\n                 include_top=True,\n                 groups=1,\n                 width_per_group=64):\n        super(ResNet, self).__init__()\n        self.include_top = include_top\n        # The maxpool has 64 output channels and 64 residual structure input channels\n        self.in_channel = 64\n \n        self.groups = groups\n        self.width_per_group = width_per_group\n \n        self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,\n                               padding=3, bias=False)\n        self.bn1 = nn.BatchNorm2d(self.in_channel)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        # Shallow stride=1, deep stride=2\n        # block：Two types of residual modules are defined\n        # block_num：The number of residual blocks in the module\n        self.layer1 = self._make_layer(block, 64, blocks_num[0])\n        self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)\n        self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)\n        self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)\n        if self.include_top:\n            # Adaptive average pooling, with specified outputs (H, W) and no change in the number of channels\n            self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n            # Fully connected layer\n            self.fc = nn.Linear(512 * block.expansion, num_classes)\n        # Inherit nn. Module class, self.modules(), which returns all modules in the network\n        for m in self.modules():\n            # if convolution layer\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')\n \n    # Define the Residuals module, which consists of several residual blocks\n    def _make_layer(self, block, channel, block_num, stride=1):\n        downsample = None\n        if stride != 1 or self.in_channel != channel * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(channel * block.expansion))\n \n        layers = []\n        layers.append(block(self.in_channel,\n                            channel,\n                            downsample=downsample,\n                            stride=stride,\n                            groups=self.groups,\n                            width_per_group=self.width_per_group))\n        self.in_channel = channel * block.expansion\n \n        for _ in range(1, block_num):\n            layers.append(block(self.in_channel,\n                                channel,\n                                groups=self.groups,\n                                width_per_group=self.width_per_group))\n        # Sequential：Custom sequences are connected into models to generate network structures\n        return nn.Sequential(*layers)\n \n    # forward()：The forward propagation process is defined and the connections between the layers are described\n    def forward(self, x):\n        # Static layer\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n        # Dynamic layers\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n \n        if self.include_top:\n            x = self.avgpool(x)\n            x = torch.flatten(x, 1)\n            x = self.fc(x)\n \n        return x","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:36.54188Z","iopub.execute_input":"2024-01-08T13:23:36.542184Z","iopub.status.idle":"2024-01-08T13:23:36.573247Z","shell.execute_reply.started":"2024-01-08T13:23:36.542158Z","shell.execute_reply":"2024-01-08T13:23:36.572245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model =  ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=True)\n\nmodel.load_state_dict(torch.load('/kaggle/input/model-resnet50-sgd/model'),strict=False)\nmodel = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:36.575213Z","iopub.execute_input":"2024-01-08T13:23:36.575521Z","iopub.status.idle":"2024-01-08T13:23:37.072932Z","shell.execute_reply.started":"2024-01-08T13:23:36.575497Z","shell.execute_reply":"2024-01-08T13:23:37.072123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## PREDICT","metadata":{}},{"cell_type":"code","source":"def evaluate(model, data_loader):\n    # Set the model to evaluation mode\n    model.eval()\n    correct = 0\n    total = 0\n    # Use tqdm for a progress bar during training\n    progress_bar = tqdm(enumerate(data_loader), total=len(data_loader))\n    with torch.no_grad():\n        for batch_idx, (images, labels) in progress_bar:\n            images = images.to(device)\n            labels = labels.to(device)\n            outputs = model(images)\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n            \n    # Calculate accuracy and print the result\n    accuracy = correct / total\n    print(\"\\n Evaluation accuracy: {}\".format(accuracy))\n    return accuracy\n\nevaluate(model, train_loader)\nevaluate(model, val_loader)\nevaluate(model, test_loader)","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:23:37.074034Z","iopub.execute_input":"2024-01-08T13:23:37.074328Z","iopub.status.idle":"2024-01-08T13:28:51.591013Z","shell.execute_reply.started":"2024-01-08T13:23:37.074301Z","shell.execute_reply":"2024-01-08T13:28:51.590087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## RESULT","metadata":{}},{"cell_type":"code","source":"# Get an enumeration of the test loader\nexamples = enumerate(test_loader)\nbatch_idx, (example_data, example_targets) = next(examples)\n# Get predictions through the model\nwith torch.no_grad():\n    example_data = example_data.to(device)\n    output = model(example_data)\nfig = plt.figure()\n\n# Iterate over the first 9 examples in the batch\nfor i in range(9):\n  plt.subplot(3,3,i+1)\n  plt.tight_layout()\n  plt.imshow(example_data[i][0].cpu())\n  plt.title(\"Prediction: {}\".format(output.data.max(1, keepdim=True)[1][i].item()))\n  plt.xticks([])\n  plt.yticks([])\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:28:51.592073Z","iopub.execute_input":"2024-01-08T13:28:51.592373Z","iopub.status.idle":"2024-01-08T13:28:52.826206Z","shell.execute_reply.started":"2024-01-08T13:28:51.592348Z","shell.execute_reply":"2024-01-08T13:28:52.825352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Add your image to predict the class\nimg = Image.open('xx.jpg').convert('RGB')\n# Transform the image to fit the input type\nx = transform(img).to(device)\npred_class = torch.argmax(model(x)).item()\nplt.figure(figsize=(6,8))\nplt.imshow(img)\nplt.title('Predicted Class: %s' % pred_class)\nplt.axis('off')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-08T13:28:52.828054Z","iopub.execute_input":"2024-01-08T13:28:52.828368Z","iopub.status.idle":"2024-01-08T13:28:53.287907Z","shell.execute_reply.started":"2024-01-08T13:28:52.828342Z","shell.execute_reply":"2024-01-08T13:28:53.286687Z"},"trusted":true},"execution_count":null,"outputs":[]}]}