{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":34426,"databundleVersionId":3218632,"sourceType":"competition"},{"sourceId":39911,"sourceType":"datasetVersion","datasetId":31296},{"sourceId":269359,"sourceType":"datasetVersion","datasetId":111880}],"dockerImageVersionId":30734,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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\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\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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-08-01T04:21:27.268033Z","iopub.execute_input":"2024-08-01T04:21:27.268544Z","iopub.status.idle":"2024-08-01T04:21:29.068816Z","shell.execute_reply.started":"2024-08-01T04:21:27.268515Z","shell.execute_reply":"2024-08-01T04:21:29.067896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Cat and Dog Classification using ResNet-KAN Method\n\n### Description of Kolmogorov-Arnold Network (KAN)\n\nThe paper: https://arxiv.org/abs/2404.19756\n\nThe Kolmogorov-Arnold Network (KAN) is a type of artificial neural network based on the Kolmogorov-Arnold Representation Theorem. This network has distinct characteristics in its structure and operation, differing from Multi-Layer Perceptrons (MLPs). KAN features the following:\n\n1. **Kolmogorov-Arnold Representation Theorem**:\n   \n ![image.png](attachment:4d97a307-2dcc-4058-ac94-e4717c8f2965.png)\n   \n2. **Learnable Activations on Edges**:\n   - Unlike MLPs, which have fixed activations on nodes, KAN has learnable activations on edges.\n\n3. **Sum Operation on Nodes**:\n   - KAN combines the results of several sum operations performed on nodes.\n\n### Comparison Between KAN and MLP\n\n#### Similarities\n- **Both models** can be used for universal approximation of continuous functions.\n- **Both models** consist of layers that can be combined to form deep networks.\n\n#### Differences\n1. **Activation Structure**:\n   - **MLP**: Uses fixed activation functions on nodes.\n   - **KAN**: Uses learnable activation functions on edges.\n\n2. **Learning and Operation Locations**:\n   - **MLP**: Learnable weights are on the edges between layers, with fixed activation functions.\n   - **KAN**: The activation functions themselves are on the edges and learnable, with sum operations on nodes.\n\n3. **Mathematical Formulas**:\n\n   ![image.png](attachment:3cd32c62-f6ae-4f5a-b2de-ae644c100045.png)\n\n4. **Model Visualization**:\n   - **MLP Shallow**: Depicted as a network with a single layer of fixed activations on nodes and weights on edges.\n   - **KAN Shallow**: Depicted as a network with learnable functions on edges and sum operations on nodes.\n   - **MLP Deep**: A network with multiple layers, each layer having weights and fixed activation functions.\n   - **KAN Deep**: A network with multiple layers, but the learnable functions are on the edges.\n\n### Conclusion\nKAN and MLP are two different approaches to building neural networks with universal approximation capabilities. KAN offers higher flexibility in terms of having learnable activation functions on edges compared to MLPs, which use fixed activation functions on nodes. This makes KAN more adaptive in some cases, although with additional complexity in learning.","metadata":{},"attachments":{"4d97a307-2dcc-4058-ac94-e4717c8f2965.png":{"image/png":"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"},"3cd32c62-f6ae-4f5a-b2de-ae644c100045.png":{"image/png":"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"}}},{"cell_type":"markdown","source":"\n\n<p align=\"center\">\n  <a href=\"url\"><img src=\"https://www.americanhumane.org/app/uploads/2016/08/shutterstock_162633491.jpg\" align=\"middle\" height=\"383\" width=\"640\" ></a>\n</p>","metadata":{}},{"cell_type":"code","source":"!nvidia-smi","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:21:29.070506Z","iopub.execute_input":"2024-08-01T04:21:29.070870Z","iopub.status.idle":"2024-08-01T04:21:29.200294Z","shell.execute_reply.started":"2024-08-01T04:21:29.070837Z","shell.execute_reply":"2024-08-01T04:21:29.199138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Checking the Directory Cat and Dog","metadata":{}},{"cell_type":"code","source":"!ls '/kaggle/input/intel-image-classification'","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:21:29.201723Z","iopub.execute_input":"2024-08-01T04:21:29.202008Z","iopub.status.idle":"2024-08-01T04:21:29.326570Z","shell.execute_reply.started":"2024-08-01T04:21:29.201971Z","shell.execute_reply":"2024-08-01T04:21:29.325394Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Spliting data training and validation","metadata":{}},{"cell_type":"code","source":"import os\nimport shutil\nfrom sklearn.model_selection import train_test_split\n\n# Base input directory\nbase_dir = '/kaggle/input/intel-image-classification/seg_train/seg_train'\n\n# Classes to process\nclasses = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']\n\n# Output directories for training and validation data\noutput_base_dir = './split_data'\ntrain_output_dir = os.path.join(output_base_dir, 'train')\nvalidation_output_dir = os.path.join(output_base_dir, 'validation')\n\n# Ensure base directories exist\nos.makedirs(train_output_dir, exist_ok=True)\nos.makedirs(validation_output_dir, exist_ok=True)\n\n# Create subdirectories for each class\nfor cls in classes:\n    os.makedirs(os.path.join(train_output_dir, cls), exist_ok=True)\n    os.makedirs(os.path.join(validation_output_dir, cls), exist_ok=True)\n\n# Splitting data into training and validation sets\nfor cls in classes:\n    # Directory with training pictures for current class\n    class_dir = os.path.join(base_dir, cls)\n    images = os.listdir(class_dir)\n\n    # Split images into training and validation\n    train_imgs, val_imgs = train_test_split(images, test_size=0.2, random_state=42)  # 20% for validation\n\n    # Move images to the appropriate training folders\n    for img in train_imgs:\n        src = os.path.join(class_dir, img)\n        dst = os.path.join(train_output_dir, cls, img)\n        shutil.copy(src, dst)\n\n    # Move images to the appropriate validation folders\n    for img in val_imgs:\n        src = os.path.join(class_dir, img)\n        dst = os.path.join(validation_output_dir, cls, img)\n        shutil.copy(src, dst)\n\n# Verify the split\nfor cls in classes:\n    train_cls_dir = os.path.join(train_output_dir, cls)\n    validation_cls_dir = os.path.join(validation_output_dir, cls)\n    print(f\"Total training {cls} images: {len(os.listdir(train_cls_dir))}\")\n    print(f\"Total validation {cls} images: {len(os.listdir(validation_cls_dir))}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:21:29.328777Z","iopub.execute_input":"2024-08-01T04:21:29.329134Z","iopub.status.idle":"2024-08-01T04:23:00.121988Z","shell.execute_reply.started":"2024-08-01T04:21:29.329103Z","shell.execute_reply":"2024-08-01T04:23:00.120876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls './split_data'","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:00.123281Z","iopub.execute_input":"2024-08-01T04:23:00.123758Z","iopub.status.idle":"2024-08-01T04:23:00.249060Z","shell.execute_reply.started":"2024-08-01T04:23:00.123725Z","shell.execute_reply":"2024-08-01T04:23:00.247992Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\n# Output directories for training and validation data\noutput_base_dir = './split_data'\ntrain_output_dir = os.path.join(output_base_dir, 'train')\nvalidation_output_dir = os.path.join(output_base_dir, 'validation')\n\n# Classes\nclasses = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']\n\n# Print the first 10 filenames in each class directory for both training and validation\nfor cls in classes:\n    train_class_dir = os.path.join(train_output_dir, cls)\n    validation_class_dir = os.path.join(validation_output_dir, cls)\n\n    # List all files in the directories\n    train_file_names = os.listdir(train_class_dir)\n    validation_file_names = os.listdir(validation_class_dir)\n\n    # Print the first 10 file names from each directory\n    print(f\"{cls} - Train Files: {train_file_names[:10]}\")\n    print(f\"{cls} - Validation Files: {validation_file_names[:10]}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:00.250605Z","iopub.execute_input":"2024-08-01T04:23:00.250927Z","iopub.status.idle":"2024-08-01T04:23:00.265945Z","shell.execute_reply.started":"2024-08-01T04:23:00.250894Z","shell.execute_reply":"2024-08-01T04:23:00.265160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset exploration","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\n\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\n# Parameters for our graph; we'll output images in a 4x4 configuration\nnrows = 4\nncols = 4\n\n# Index for iterating over images\npic_index = 0","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:00.267004Z","iopub.execute_input":"2024-08-01T04:23:00.267282Z","iopub.status.idle":"2024-08-01T04:23:01.284714Z","shell.execute_reply.started":"2024-08-01T04:23:00.267254Z","shell.execute_reply":"2024-08-01T04:23:01.284016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport os\n\n# Base directory for training data\noutput_base_dir = './split_data'\ntrain_output_dir = os.path.join(output_base_dir, 'train')\n\n# Classes\nclasses = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']\n\n# Define how many images per class to display and the configuration of the grid\nimages_per_class = 2\nnrows = len(classes)\nncols = images_per_class\n\n# Set up matplotlib figure and size it to fit 4x4 pics\nfig = plt.gcf()\nfig.set_size_inches(ncols * 4, nrows * 4)\n\n# Initialize a counter to manage subplot indices\nsubplot_index = 1\n\nfor cls in classes:\n    # Directory for current class\n    class_dir = os.path.join(train_output_dir, cls)\n    \n    # List all files in the directory and pick the first few for display\n    file_names = os.listdir(class_dir)[:images_per_class]\n    \n    for fname in file_names:\n        img_path = os.path.join(class_dir, fname)\n        img = mpimg.imread(img_path)\n        \n        # Set up subplot; subplot indices start at 1\n        sp = plt.subplot(nrows, ncols, subplot_index)\n        sp.axis('Off')  # Don't show axes (or gridlines)\n        plt.imshow(img)\n        \n        subplot_index += 1  # Increment subplot index to move to the next slot\n\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:01.285665Z","iopub.execute_input":"2024-08-01T04:23:01.286021Z","iopub.status.idle":"2024-08-01T04:23:02.145344Z","shell.execute_reply.started":"2024-08-01T04:23:01.285992Z","shell.execute_reply":"2024-08-01T04:23:02.144658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Install library","metadata":{}},{"cell_type":"code","source":"!pip install -q torchsummary","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:02.146297Z","iopub.execute_input":"2024-08-01T04:23:02.146542Z","iopub.status.idle":"2024-08-01T04:23:06.369213Z","shell.execute_reply.started":"2024-08-01T04:23:02.146518Z","shell.execute_reply":"2024-08-01T04:23:06.368216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import Library","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torchvision import transforms, datasets, models\nfrom torch.utils.data import DataLoader, Dataset\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport math\nfrom torchsummary import summary\nfrom sklearn.metrics import classification_report\nfrom torch.optim.lr_scheduler import OneCycleLR\nfrom tqdm.notebook import tqdm","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:06.372447Z","iopub.execute_input":"2024-08-01T04:23:06.372772Z","iopub.status.idle":"2024-08-01T04:23:28.706282Z","shell.execute_reply.started":"2024-08-01T04:23:06.372737Z","shell.execute_reply":"2024-08-01T04:23:28.705218Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 1. Definition of KANLinear\n  - This definition follows the provided source and is used in the CNN-KAN model.\n\n  <p align=\"center\">\n  <a href=\"url\"><img src=\"https://www.marktechpost.com/wp-content/uploads/2024/05/Screenshot-2024-05-04-at-12.59.57-PM.png\" align=\"middle\" height=\"383\" width=\"640\" ></a>\n</p>\n\nKAN vs MLP Complexity (please correct if there are any errors):\n\nBig O notation measures the time or space complexity of an algorithm in terms of its input. To compare the memory and performance requirements between the Kolmogorov-Arnold Network (KAN) and Multi-Layer Perceptron (MLP), we can look at the components that affect memory usage and computational time for both algorithms.\n\n### Multi-Layer Perceptron (MLP)\n\nMLP usually consists of several layers of neurons, where each neuron in one layer is connected to every neuron in the next layer.\n\n- **Time Complexity (Training):** O(N * M * L * E)\n  - N: Number of samples in the dataset\n  - M: Number of neurons per layer\n  - L: Number of layers\n  - E: Number of epochs\n\n- **Space Complexity:** O(M * L + N * F)\n  - M: Number of neurons per layer\n  - L: Number of layers\n  - N: Number of samples in the dataset\n  - F: Dimension of input features\n\n### Kolmogorov-Arnold Network (KAN)\n\nKAN uses learnable activation functions at each edge and summation operations at nodes, adding complexity to the model.\n\n- **Time Complexity (Training):** O(N * (M^2) * L * E)\n  - N: Number of samples in the dataset\n  - M: Number of neurons per layer (since the activation functions are learnable at each edge, there is additional complexity here)\n  - L: Number of layers\n  - E: Number of epochs\n\n- **Space Complexity:** O(M^2 * L + N * F)\n  - M: Number of neurons per layer (with learnable activation functions at each edge, memory requirements increase)\n  - L: Number of layers\n  - N: Number of samples in the dataset\n  - F: Dimension of input features\n\n### Conclusion\n\nFrom the analysis above, it can be concluded that KAN has higher complexity compared to MLP in terms of both time and space.\n\n1. **Time Complexity:**\n   - MLP: O(N * M * L * E)\n   - KAN: O(N * M^2 * L * E)\n\n2. **Space Complexity:**\n   - MLP: O(M * L + N * F)\n   - KAN: O(M^2 * L + N * F)\n\nIt can be seen that KAN requires more memory (RAM) during training because it has learnable activation functions at each edge, which significantly increases the number of parameters that need to be stored and processed.","metadata":{}},{"cell_type":"markdown","source":"## 2. Model CNNKAN\n\nReference: CNN-KAN: https://github.com/jakariaemon/CNN-KAN \n\n  # Basics CNN model for Cat and Dog Classifications with KANLinear\n  \n    class CNNKAN(nn.Module):\n        def __init__(self):\n            super(CNNKAN, self).__init__()\n            self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)  \n            self.pool1 = nn.MaxPool2d(2)\n            self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n            self.pool2 = nn.MaxPool2d(2)\n            self.kan1 = KANLinear(64 * 8 * 8, 256)  \n            self.kan2 = KANLinear(256, 100)\n\n        def forward(self, x):\n            x = F.selu(self.conv1(x))\n            x = self.pool1(x)\n            x = F.selu(self.conv2(x))\n            x = self.pool2(x)\n            x = x.view(x.size(0), -1)\n            x = self.kan1(x)\n            x = self.kan2(x)\n            return x","metadata":{}},{"cell_type":"markdown","source":"### Model VGG16-KAN \n\nReference KANVGG16 architectures from: https://www.linkedin.com/posts/damienbenveniste_this-is-a-pure-kolmogorovarnold-kan-convolutional-activity-7193403017008680960-FOHW?utm_source=share&utm_medium=member_android","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\nclass VGG16(nn.Module):\n    def __init__(self, num_classes=6):\n        super(VGG16, self).__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(64, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(64),\n\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(128, 128, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(128),\n\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(256),\n\n            nn.Conv2d(256, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(512),\n\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(512),\n        )\n        self.classifier = nn.Sequential(\n            nn.Linear(512 * 7 * 7, 4096),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(4096, 4096),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(4096, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = torch.flatten(x, 1)\n        x = self.classifier(x)\n        return x\n\n# Example of how to create the model and print it\nmodel = VGG16(num_classes=6)\nprint(model)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:28.707488Z","iopub.execute_input":"2024-08-01T04:23:28.707955Z","iopub.status.idle":"2024-08-01T04:23:30.054896Z","shell.execute_reply.started":"2024-08-01T04:23:28.707925Z","shell.execute_reply":"2024-08-01T04:23:30.053936Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Creating PyTorch Dataset and DataLoader\n  - Use transformations and PyTorch DataLoader:","metadata":{}},{"cell_type":"code","source":"import os\nimport shutil\n\n# Base directory for training data\nbase_dir = './split_data'\n\n# Subdirectories for training and validation\ntrain_dir = os.path.join(base_dir, 'train')\nval_dir = os.path.join(base_dir, 'validation')\n\n# Correct directory names\ncorrect_names = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']\n\n# Function to rename directories if misnamed\ndef correct_directory_names(dir_path):\n    for item in os.listdir(dir_path):\n        if item in ['building']:  # Add any other incorrect names as needed\n            correct_path = os.path.join(dir_path, 'buildings')\n            incorrect_path = os.path.join(dir_path, item)\n            if os.path.exists(correct_path):\n                # Merge contents if 'buildings' already exists\n                for file in os.listdir(incorrect_path):\n                    shutil.move(os.path.join(incorrect_path, file), correct_path)\n                os.rmdir(incorrect_path)  # Remove the old directory\n            else:\n                # Rename if 'buildings' does not exist\n                os.rename(incorrect_path, correct_path)\n            print(f\"Renamed {incorrect_path} to {correct_path}\")\n\n# Apply corrections\ncorrect_directory_names(train_dir)\ncorrect_directory_names(val_dir)\n\n# Recheck directories\nprint(f\"Contents of {train_dir}: {os.listdir(train_dir)}\")\nprint(f\"Contents of {val_dir}: {os.listdir(val_dir)}\")\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:30.056126Z","iopub.execute_input":"2024-08-01T04:23:30.056434Z","iopub.status.idle":"2024-08-01T04:23:30.064483Z","shell.execute_reply.started":"2024-08-01T04:23:30.056406Z","shell.execute_reply":"2024-08-01T04:23:30.063598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Directory paths\ntrain_dir = './split_data/train'\nval_dir = './split_data/validation'\n\n# Define transformations for the training data\ntransform_train = transforms.Compose([\n    transforms.RandomResizedCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\n# For validation data, we resize to 224x224 and then just convert to tensor and normalize\ntransform_val = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nbatch_size = 128\n\n# Create the datasets\ntrain_dataset = datasets.ImageFolder(root=train_dir, transform=transform_train)\nval_dataset = datasets.ImageFolder(root=val_dir, transform=transform_val)\n\n# Create the data loaders\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=8)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=8)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:30.065568Z","iopub.execute_input":"2024-08-01T04:23:30.065832Z","iopub.status.idle":"2024-08-01T04:23:30.112313Z","shell.execute_reply.started":"2024-08-01T04:23:30.065807Z","shell.execute_reply":"2024-08-01T04:23:30.111534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4. Training dan Evaluasi Model\n  - Bagian ini mendefinisikan loop training dan evaluasi model. Clear Gradients dan Kosongkan Cache","metadata":{}},{"cell_type":"code","source":"# Clear CUDA Cache\ntorch.cuda.empty_cache()","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:30.113464Z","iopub.execute_input":"2024-08-01T04:23:30.113762Z","iopub.status.idle":"2024-08-01T04:23:30.117105Z","shell.execute_reply.started":"2024-08-01T04:23:30.113733Z","shell.execute_reply":"2024-08-01T04:23:30.116390Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport matplotlib.pyplot as plt\nfrom torch.optim.lr_scheduler import OneCycleLR\nfrom torch.cuda.amp import GradScaler, autocast\nfrom sklearn.metrics import classification_report\n\n# Set device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nepoch_number= 3\n\n# Define model\nmodel = VGG16(num_classes=6).to(device)\nprint(model)\nsummary(model, input_size=(3, 224, 224))\n\n# Use DataParallel if using more than one GPU\nif torch.cuda.device_count() > 1:\n    model = nn.DataParallel(model)\n\n# Print model parameter details\ndef print_parameter_details(model):\n    for name, param in model.named_parameters():\n        print(f\"{name}: {param.size()} {'requires_grad' if param.requires_grad else 'frozen'}\")\n\nprint_parameter_details(model)\n\n# Visualize KAN parameters\ndef visualize_kan_parameters(kan_layer, layer_name):\n    base_weights = kan_layer.base_weight.data.cpu().numpy()\n    plt.hist(base_weights.ravel(), bins=50)\n    plt.title(f\"Distribution of Base Weights - {layer_name}\")\n    plt.xlabel(\"Weight Value\")\n    plt.ylabel(\"Frequency\")\n    plt.show()\n    if hasattr(kan_layer, 'spline_weight'):\n        spline_weights = kan_layer.spline_weight.data.cpu().numpy()\n        plt.hist(spline_weights.ravel(), bins=50)\n        plt.title(f\"Distribution of Spline Weights - {layer_name}\")\n        plt.xlabel(\"Weight Value\")\n        plt.ylabel(\"Frequency\")\n        plt.show()\n\n# Use CrossEntropyLoss for multi-class classification\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0001, weight_decay=1e-5)\nscheduler = OneCycleLR(optimizer, max_lr=0.001, steps_per_epoch=len(train_loader), epochs=epoch_number)\n\n# Training function\nscaler = GradScaler()\n\ndef train(model, train_loader, criterion, optimizer, device, epoch):\n    model.train()\n    running_loss = 0.0\n    for batch_idx, (inputs, labels) in enumerate(train_loader):\n        inputs, labels = inputs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        with autocast():\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n        scaler.scale(loss).backward()\n        scaler.step(optimizer)\n        scaler.update()\n        optimizer.zero_grad()\n        running_loss += loss.item()\n\n        if batch_idx % 10 == 0:\n            sys.stdout.write(f'\\rTrain Epoch: {epoch} [{batch_idx * len(inputs)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\\tLoss: {loss.item():.6f}')\n            sys.stdout.flush()\n\n    return running_loss / len(train_loader)\n\n# Validation function\ndef validate(model, val_loader, criterion, device, epoch):\n    model.eval()\n    val_loss = 0.0\n    correct = 0\n    total = 0\n    all_labels = []\n    all_predictions = []\n    with torch.no_grad():\n        for batch_idx, (inputs, labels) in enumerate(val_loader):\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            val_loss += loss.item()\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n            all_labels.extend(labels.cpu().numpy())\n            all_predictions.extend(predicted.cpu().numpy())\n\n            if batch_idx % 10 == 0:\n                print(f'Validation Epoch: {epoch} [{batch_idx * len(inputs)}/{len(val_loader.dataset)} ({100. * batch_idx / len(val_loader):.0f}%)]\\tLoss: {loss.item():.6f}')\n\n    accuracy = 100 * correct / total\n    print(classification_report(all_labels, all_predictions, target_names=['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']))\n    return val_loss / len(val_loader), accuracy\n\n# Early stopping\nbest_val_loss = float('inf')\npatience = 5\npatience_counter = 0\n\n# Lists to store loss and accuracy values\ntrain_losses = []\nval_losses = []\nval_accuracies = []\n\n# Training loop\njumlah_epoch = epoch_number\nfor epoch in range(jumlah_epoch):\n    train_loss = train(model, train_loader, criterion, optimizer, device, epoch)\n    val_loss, val_accuracy = validate(model, val_loader, criterion, device, epoch)\n\n    # Save losses and accuracy\n    train_losses.append(train_loss)\n    val_losses.append(val_loss)\n    val_accuracies.append(val_accuracy)\n\n    print(f\"Epoch {epoch+1}/{jumlah_epoch}, Train Loss: {train_loss:.6f}, Validation Loss: {val_loss:.6f}, Validation Accuracy: {val_accuracy:.2f}%\")\n    scheduler.step(val_loss)\n\n    # Early stopping\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        patience_counter = 0\n        torch.save(model.state_dict(), 'best_model_weights_KAN.pth')\n    else:\n        patience_counter += 1\n        if patience_counter >= patience:\n            print(\"Early stopping triggered\")\n            break\n            \n# Load the best model weights\nmodel.load_state_dict(torch.load('best_model_weights_KAN.pth'))\n\n# Evaluate model on validation set\ndef test(model, val_loader, device):\n    model.eval()\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for inputs, labels in val_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n            # Clear CUDA cache\n            torch.cuda.empty_cache()\n\n    accuracy = 100 * correct / total\n    return accuracy\n\naccuracy = test(model, val_loader, device)\nprint(f'Validation Accuracy: {accuracy:.2f}%')\n\n# Plot training and validation loss\ndef plot_loss(train_losses, val_losses):\n    plt.figure()\n    plt.plot(train_losses, label='Training Loss')\n    plt.plot(val_losses, label='Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.title('Training and Validation Loss')\n    plt.legend()\n    plt.show()\n\n# Plot validation accuracy\ndef plot_accuracy(val_accuracies):\n    plt.figure()\n    plt.plot(val_accuracies, label='Validation Accuracy')\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy')\n    plt.title('Validation Accuracy')\n    plt.legend()\n    plt.show()\n\n# Plot the losses and accuracy\nplot_loss(train_losses, val_losses)\nplot_accuracy(val_accuracies)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T04:23:30.118298Z","iopub.execute_input":"2024-08-01T04:23:30.118554Z","iopub.status.idle":"2024-08-01T05:41:35.901865Z","shell.execute_reply.started":"2024-08-01T04:23:30.118529Z","shell.execute_reply":"2024-08-01T05:41:35.900887Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 5. Evaluasi Model pada Data Test","metadata":{}},{"cell_type":"code","source":"\"\"\"\n# Create the test dataset and loader\ntest_dir = '/tmp/test'\ntest_dataset = datasets.ImageFolder(root=test_dir, transform=transform)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)\n\ndef test(model, test_loader, device):\n    model.eval()\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for inputs, labels in test_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            predicted = (outputs > 0.5).float()\n            total += labels.size(0)\n            correct += (predicted.squeeze() == labels).sum().item()\n\n            # Kosongkan cache CUDA\n            torch.cuda.empty_cache()\n\n    accuracy = 100 * correct / total\n    return accuracy\n\naccuracy = test(model, test_loader, device)\nprint(f'Test Accuracy: {accuracy}%')\n\"\"\"","metadata":{"execution":{"iopub.status.busy":"2024-08-01T05:41:35.903415Z","iopub.execute_input":"2024-08-01T05:41:35.903704Z","iopub.status.idle":"2024-08-01T05:41:35.909678Z","shell.execute_reply.started":"2024-08-01T05:41:35.903672Z","shell.execute_reply":"2024-08-01T05:41:35.909006Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 6. Plot ROC Curve","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import roc_curve, auc, classification_report, accuracy_score, confusion_matrix\nimport torch\n\n# Function to calculate predictions and true labels\ndef get_predictions_and_labels(model, val_loader, device):\n    model.eval()\n    preds = []\n    true_labels = []\n    with torch.no_grad():\n        for inputs, labels in val_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            preds.extend(outputs.cpu().numpy())\n            true_labels.extend(labels.cpu().numpy())\n\n    return np.array(preds), np.array(true_labels)\n\n# Function to plot ROC Curve for each class\ndef plot_roc_curve(true_labels, preds, num_classes):\n    fpr = {}\n    tpr = {}\n    roc_auc = {}\n\n    # One hot encode the true labels\n    true_labels_one_hot = np.eye(num_classes)[true_labels]\n\n    # Calculate ROC curve and ROC area for each class\n    for i in range(num_classes):\n        fpr[i], tpr[i], _ = roc_curve(true_labels_one_hot[:, i], preds[:, i])\n        roc_auc[i] = auc(fpr[i], tpr[i])\n\n    plt.figure(figsize=(10, 8))\n    lw = 2\n\n    for i in range(num_classes):\n        plt.plot(fpr[i], tpr[i], lw=lw, label='Class {0} (area = {1:0.2f})'.format(i, roc_auc[i]))\n\n    plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')\n    plt.xlim([0.0, 1.0])\n    plt.ylim([0.0, 1.05])\n    plt.xlabel('False Positive Rate')\n    plt.ylabel('True Positive Rate')\n    plt.title('Receiver Operating Characteristic for Multi-Class')\n    plt.legend(loc=\"lower right\")\n    plt.show()\n\n# Function to calculate and print classification metrics\ndef print_classification_metrics(true_labels, preds):\n    preds_labels = np.argmax(preds, axis=1)\n    accuracy = accuracy_score(true_labels, preds_labels)\n    report = classification_report(true_labels, preds_labels, target_names=train_dataset.classes)\n    conf_matrix = confusion_matrix(true_labels, preds_labels)\n\n    print(f'Accuracy: {accuracy:.4f}')\n    print('Classification Report:')\n    print(report)\n    print('Confusion Matrix:')\n    print(conf_matrix)\n\n# Assuming you have your model, val_loader, and device already defined\n# Example usage\npreds, true_labels = get_predictions_and_labels(model, val_loader, device)\nnum_classes = 6  # Update this to the correct number of classes\nplot_roc_curve(true_labels, preds, num_classes)\nprint_classification_metrics(true_labels, preds)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T05:41:35.910680Z","iopub.execute_input":"2024-08-01T05:41:35.910941Z","iopub.status.idle":"2024-08-01T05:43:42.098806Z","shell.execute_reply.started":"2024-08-01T05:41:35.910914Z","shell.execute_reply":"2024-08-01T05:43:42.098016Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Making Prediction\n\nCode Colab: https://colab.research.google.com/drive/1h-a2_EvDZYKawcH847a3kj7gyujWsQPR?usp=sharing","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torchvision import transforms\nfrom PIL import Image\nimport os\n\n# Tentukan device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Definisikan transformasi gambar\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\n# Fungsi untuk memuat dan memproses gambar\ndef load_and_preprocess_image(img_path):\n    img = Image.open(img_path).convert('RGB')\n    img_tensor = transform(img).unsqueeze(0).to(device)\n    return img, img_tensor\n\n# Define the model\nclass VGG16(nn.Module):\n    def __init__(self, num_classes=6):\n        super(VGG16, self).__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(64, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(64),\n\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(128, 128, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(128),\n\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(256, 256, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(256),\n\n            nn.Conv2d(256, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(512),\n\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(512, 512, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.BatchNorm2d(512),\n        )\n        self.classifier = nn.Sequential(\n            nn.Linear(512 * 7 * 7, 4096),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(4096, 4096),\n            nn.ReLU(inplace=True),\n            nn.Dropout(0.5),\n            nn.Linear(4096, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = torch.flatten(x, 1)\n        x = self.classifier(x)\n        return x\n\n# Example of how to create the model and print it\nmodel = VGG16(num_classes=6)\n\n# Memuat state_dict dari file dengan penyesuaian nama parameter\ndef load_state_dict_adjusted(model, state_dict_path):\n    state_dict = torch.load(state_dict_path, map_location=device)\n    new_state_dict = {}\n    for k, v in state_dict.items():\n        if k.startswith('module.'):\n            new_state_dict[k[len('module.'):]] = v\n        else:\n            new_state_dict[k] = v\n    model.load_state_dict(new_state_dict)\n\nmodel_path = '/kaggle/working/best_model_weights_KAN.pth'  # Ganti dengan path model Anda\nload_state_dict_adjusted(model, model_path)\n\n# Fungsi untuk melakukan prediksi dan menampilkan gambar\ndef predict_and_display(img, img_tensor):\n    classes = ['buildings', 'forest', 'glacier', 'mountain', 'sea', 'street']\n    model.eval()\n    with torch.no_grad():\n        output = model(img_tensor)\n        prob = torch.softmax(output, dim=1).cpu().numpy().flatten()\n    plt.imshow(np.array(img))\n    plt.axis('off')\n    prediction_text = \"Class Probabilities:\\n\"\n    for i, p in enumerate(prob):\n        prediction_text += f\"{classes[i]}: {p:.4f}\\n\"\n    plt.title(prediction_text)\n    plt.show()\n\n# Fungsi untuk memproses gambar dari direktori seg_pred\ndef process_images_from_directory(directory):\n    for filename in os.listdir(directory):\n        if filename.endswith(\".jpg\") or filename.endswith(\".png\"):\n            img_path = os.path.join(directory, filename)\n            img, img_tensor = load_and_preprocess_image(img_path)\n            predict_and_display(img, img_tensor)\n\n# Set the path to the seg_pred directory\nseg_pred_directory = '/kaggle/input/intel-image-classification/seg_pred/seg_pred/'\n\n# Memanggil fungsi untuk memproses gambar dari direktori seg_pred\nprocess_images_from_directory(seg_pred_directory)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-01T05:43:42.100268Z","iopub.execute_input":"2024-08-01T05:43:42.100607Z"},"trusted":true},"execution_count":null,"outputs":[]}]}