{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":65471,"databundleVersionId":7211464,"sourceType":"competition"}],"dockerImageVersionId":30627,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nfrom torch.utils.data import DataLoader, random_split , Dataset\nfrom torchvision import models, transforms, datasets\nfrom PIL import Image\nfrom sklearn.metrics import confusion_matrix\nimport csv","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-12-22T08:14:57.966234Z","iopub.execute_input":"2023-12-22T08:14:57.966712Z","iopub.status.idle":"2023-12-22T08:14:59.998848Z","shell.execute_reply.started":"2023-12-22T08:14:57.966674Z","shell.execute_reply":"2023-12-22T08:14:59.997959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***CONSTANTS***","metadata":{}},{"cell_type":"code","source":"train_dir = '/kaggle/input/arabic-letters-classification/Final_Arabic_Alpha_dataset/Final_Arabic_Alpha_dataset/train'\nnum_classes = 65\nimage_shape = (224, 224)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:00.004808Z","iopub.execute_input":"2023-12-22T08:15:00.005133Z","iopub.status.idle":"2023-12-22T08:15:00.010643Z","shell.execute_reply.started":"2023-12-22T08:15:00.005104Z","shell.execute_reply":"2023-12-22T08:15:00.009249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***PREPROCESSING***","metadata":{}},{"cell_type":"code","source":"class MakeDataset(Dataset):\n    def __init__(self, root_folder, transform=None):\n        self.root_folder = root_folder\n        self.transform = transform\n        self.classes = sorted(os.listdir(root_folder), key=lambda x: int(x))\n        self.class_to_idx = {cls: i for i, cls in enumerate(self.classes)}\n        self.images = self.make_dataset()\n\n    def make_dataset(self):\n        images = []\n        for label in self.classes:\n            label_folder = os.path.join(self.root_folder, label)\n            for image_name in os.listdir(label_folder):\n                image_path = os.path.join(label_folder, image_name)\n                item = (image_path, self.class_to_idx[label])\n                images.append(item)\n        return images\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, index):\n        image_path, label = self.images[index]\n        image = Image.open(image_path).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:00.012460Z","iopub.execute_input":"2023-12-22T08:15:00.012779Z","iopub.status.idle":"2023-12-22T08:15:00.024413Z","shell.execute_reply.started":"2023-12-22T08:15:00.012750Z","shell.execute_reply":"2023-12-22T08:15:00.023330Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.Grayscale(num_output_channels=3),\n    transforms.Resize(image_shape),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:00.027062Z","iopub.execute_input":"2023-12-22T08:15:00.027509Z","iopub.status.idle":"2023-12-22T08:15:00.039846Z","shell.execute_reply.started":"2023-12-22T08:15:00.027475Z","shell.execute_reply":"2023-12-22T08:15:00.038407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = MakeDataset(train_dir, transform=transform)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:00.041543Z","iopub.execute_input":"2023-12-22T08:15:00.041956Z","iopub.status.idle":"2023-12-22T08:15:00.224219Z","shell.execute_reply.started":"2023-12-22T08:15:00.041922Z","shell.execute_reply":"2023-12-22T08:15:00.222998Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_size = int(0.8 * len(dataset))\nval_size = int(0.1 * len(dataset))\ntest_size = len(dataset) - train_size - val_size\ntrain_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:00.970099Z","iopub.execute_input":"2023-12-22T08:15:00.970595Z","iopub.status.idle":"2023-12-22T08:15:00.981279Z","shell.execute_reply.started":"2023-12-22T08:15:00.970557Z","shell.execute_reply":"2023-12-22T08:15:00.980221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 8","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:01.195296Z","iopub.execute_input":"2023-12-22T08:15:01.195727Z","iopub.status.idle":"2023-12-22T08:15:01.201252Z","shell.execute_reply.started":"2023-12-22T08:15:01.195696Z","shell.execute_reply":"2023-12-22T08:15:01.199950Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_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)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:01.708520Z","iopub.execute_input":"2023-12-22T08:15:01.708924Z","iopub.status.idle":"2023-12-22T08:15:01.715712Z","shell.execute_reply.started":"2023-12-22T08:15:01.708894Z","shell.execute_reply":"2023-12-22T08:15:01.714482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***VISUALIZATION***","metadata":{}},{"cell_type":"code","source":"def show_images(images, labels, ncols=8):\n    figure, axes = plt.subplots(nrows=1, ncols=ncols, figsize=(15, 2))\n    for i, ax in enumerate(axes):\n        ax.imshow(np.transpose(images[i], (1, 2, 0)))\n        ax.set_title(f'Class: {labels[i]}')\n        ax.axis('off')","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:03.118808Z","iopub.execute_input":"2023-12-22T08:15:03.119241Z","iopub.status.idle":"2023-12-22T08:15:03.126832Z","shell.execute_reply.started":"2023-12-22T08:15:03.119198Z","shell.execute_reply":"2023-12-22T08:15:03.125544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_iter = iter(train_loader)\nimages, labels = data_iter.__next__()","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:20.072777Z","iopub.execute_input":"2023-12-22T08:15:20.073219Z","iopub.status.idle":"2023-12-22T08:15:20.373480Z","shell.execute_reply.started":"2023-12-22T08:15:20.073174Z","shell.execute_reply":"2023-12-22T08:15:20.372126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_images(images, labels)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:15:22.554986Z","iopub.execute_input":"2023-12-22T08:15:22.556078Z","iopub.status.idle":"2023-12-22T08:15:23.194927Z","shell.execute_reply.started":"2023-12-22T08:15:22.556036Z","shell.execute_reply":"2023-12-22T08:15:23.193969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***MODEL***","metadata":{}},{"cell_type":"code","source":"model = models.efficientnet_b7(pretrained=True)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:19:44.766625Z","iopub.execute_input":"2023-12-22T08:19:44.767093Z","iopub.status.idle":"2023-12-22T08:19:54.299313Z","shell.execute_reply.started":"2023-12-22T08:19:44.767062Z","shell.execute_reply":"2023-12-22T08:19:54.297876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n# Assuming the classifier attribute is 'classifier', get the last layer in the Sequential\nlast_layer = list(model.classifier.children())[-1]\n\n\n# Replace it with the correct attribute name\nif isinstance(last_layer, nn.Linear):\n    in_features = last_layer.in_features\n    model.classifier[-1] = nn.Linear(in_features, num_classes)\nelse:\n    raise ValueError(\"The last layer of the classifier is not Linear, please adjust the code accordingly.\")","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:20:05.417868Z","iopub.execute_input":"2023-12-22T08:20:05.418322Z","iopub.status.idle":"2023-12-22T08:20:05.430460Z","shell.execute_reply.started":"2023-12-22T08:20:05.418278Z","shell.execute_reply":"2023-12-22T08:20:05.429071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0001)\nscheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:20:21.304575Z","iopub.execute_input":"2023-12-22T08:20:21.304960Z","iopub.status.idle":"2023-12-22T08:20:21.312054Z","shell.execute_reply.started":"2023-12-22T08:20:21.304931Z","shell.execute_reply":"2023-12-22T08:20:21.310924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:20:28.910339Z","iopub.execute_input":"2023-12-22T08:20:28.910747Z","iopub.status.idle":"2023-12-22T08:20:28.917486Z","shell.execute_reply.started":"2023-12-22T08:20:28.910716Z","shell.execute_reply":"2023-12-22T08:20:28.916355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.cuda.empty_cache()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:20:42.896714Z","iopub.execute_input":"2023-12-22T08:20:42.897232Z","iopub.status.idle":"2023-12-22T08:20:42.905971Z","shell.execute_reply.started":"2023-12-22T08:20:42.897190Z","shell.execute_reply":"2023-12-22T08:20:42.904687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 10\ndesired_training_accuracy = 99.999\nearly_stopping_threshold = 5  \nearly_stopping_counter = 0\nbest_val_loss = float('inf')","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:20:49.541961Z","iopub.execute_input":"2023-12-22T08:20:49.542368Z","iopub.status.idle":"2023-12-22T08:20:49.548507Z","shell.execute_reply.started":"2023-12-22T08:20:49.542337Z","shell.execute_reply":"2023-12-22T08:20:49.547213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lists to store training and validation metrics for plotting curves\ntrain_losses = []\ntrain_accuracies = []\nval_losses = []\nval_accuracies = []\npredictions = []\ntargets = []","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:24:52.726079Z","iopub.execute_input":"2023-12-22T08:24:52.726392Z","iopub.status.idle":"2023-12-22T08:24:52.736842Z","shell.execute_reply.started":"2023-12-22T08:24:52.726365Z","shell.execute_reply":"2023-12-22T08:24:52.735961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    correct_train = 0\n    total_train = 0\n\n    for i, (inputs, labels) in enumerate(train_loader):\n        inputs, labels = inputs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n\n        _, predicted_train = torch.max(outputs.data, 1)\n        total_train += labels.size(0)\n        correct_train += (predicted_train == labels).sum().item()\n\n        # Print training loss and accuracy every 100 batches\n        if i % 100 == 99:\n            print(f'Epoch {epoch + 1}/{num_epochs}, Batch {i + 1}/{len(train_loader)}, '\n                  f'Training Loss: {running_loss / 100}, Training Accuracy: {100 * correct_train / total_train}')\n            running_loss = 0.0\n\n    # Calculate training accuracy after the epoch\n    training_accuracy = correct_train / total_train\n\n    # Validation\n    model.eval()\n    correct_val = 0\n    total_val = 0\n    val_running_loss = 0.0\n\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            loss = criterion(outputs, labels)\n            val_running_loss += loss.item()\n\n            _, predicted_val = torch.max(outputs.data, 1)\n            total_val += labels.size(0)\n            correct_val += (predicted_val == labels).sum().item()\n\n            # Append predictions and targets for confusion matrix\n            predictions.append(predicted_val.cpu().numpy())\n            targets.append(labels.cpu().numpy())\n\n    # Calculate validation accuracy after the epoch\n    validation_accuracy = correct_val / total_val\n    average_val_loss = val_running_loss / len(val_loader)\n\n    print(f'Epoch {epoch + 1}/{num_epochs}, '\n          f'Training Loss: {running_loss / len(train_loader)}, '\n          f'Training Accuracy: {100 * training_accuracy}%, '\n          f'Validation Loss: {average_val_loss}, '\n          f'Validation Accuracy: {100 * validation_accuracy}%')\n\n    # Append training and validation metrics for plotting\n    train_losses.append(running_loss / len(train_loader))\n    train_accuracies.append(training_accuracy)\n    val_losses.append(average_val_loss)\n    val_accuracies.append(validation_accuracy)\n\n    scheduler.step()","metadata":{"execution":{"iopub.status.busy":"2023-12-22T08:22:08.927314Z","iopub.execute_input":"2023-12-22T08:22:08.927796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\ncorrect = 0\ntotal = 0\n\nwith 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 = torch.max(outputs.data, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\ntest_accuracy = correct / total\nprint(f'Test Accuracy: {100 * test_accuracy}%')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***VISUALIZE THE MODEL***","metadata":{}},{"cell_type":"code","source":"# Plotting loss and accuracy curves\nplt.figure(figsize=(12, 4))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plotting training and validation loss\nplt.subplot(1, 2, 1)\nplt.plot(train_losses, label='Training Loss')\nplt.plot(val_losses, label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training and Validation Loss')\nplt.legend()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plotting training and validation accuracy\nplt.subplot(1, 2, 2)\nplt.plot(train_accuracies, label='Training Accuracy')\nplt.plot(val_accuracies, label='Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy (%)')\nplt.title('Training and Validation Accuracy')\nplt.legend()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.tight_layout()\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Confusion Matrix\nconf_matrix = confusion_matrix(np.concatenate(predictions), np.concatenate(targets))\nprint('Confusion Matrix:')\n\n# Print all numbers in the confusion matrix\nprint('Confusion Matrix:')\nfor row in conf_matrix:\n    for value in row:\n        print(value, end=' ')\n    print()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import confusion_matrix\n\n\n# Plot the heatmap\nplt.figure(figsize=(10, 8))\nplt.imshow(conf_matrix, cmap='viridis', interpolation='nearest')\nplt.colorbar()\nplt.title('Confusion Matrix')\nplt.xlabel('Predicted Label')\nplt.ylabel('True Label')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"***PREDICTIONS***","metadata":{}},{"cell_type":"code","source":"model = model\nmodel = model.cuda()\n\ntest_directory =  '/kaggle/input/arabic-letters-classification/Final_Arabic_Alpha_dataset/Final_Arabic_Alpha_dataset/test/'\ncsv_file_path = 'predictions.csv'\n\ntransform = transforms.Compose([\n    transforms.Resize(image_shape),\n    transforms.Grayscale(num_output_channels=3),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\nwith open(csv_file_path, 'w', newline='') as csvfile:\n    csv_writer = csv.writer(csvfile)\n\n    csv_writer.writerow(['ID', 'Label'])\n\n    for filename in sorted(os.listdir(test_directory), key=lambda x: int(x.split('.')[0])):\n        if filename.endswith(\".jpg\") or filename.endswith(\".png\"):\n            image_path = os.path.join(test_directory, filename)\n\n            input_image = Image.open(image_path).convert('RGB')  \n            input_tensor = transform(input_image).unsqueeze(0)\n            input_tensor = input_tensor.cuda()\n\n            with torch.no_grad():\n                output = model(input_tensor)\n\n            _, predicted_class = torch.max(output, 1)\n\n            csv_writer.writerow([int(filename.split('.')[0]), predicted_class.item()])\n\nprint(f\"Predictions saved to: {csv_file_path}\")","metadata":{},"execution_count":null,"outputs":[]}]}