{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":30664,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Overview\n\nWe will create a customise neural network with PyTorch to do the image classification tasks. First, we download the data and move them to the specific folders.\n\n\n# Loading Data and Visualization Images\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"%%capture\n!unzip /kaggle/input/dogs-vs-cats/train.zip -d /kaggle/working\n!unzip /kaggle/input/dogs-vs-cats/test1.zip -d /kaggle/working","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:30:43.946899Z","iopub.execute_input":"2024-03-10T11:30:43.947624Z","iopub.status.idle":"2024-03-10T11:31:01.213277Z","shell.execute_reply.started":"2024-03-10T11:30:43.947590Z","shell.execute_reply":"2024-03-10T11:31:01.211824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\ndef data_status():\n    for d_path, d_name, filenames in os.walk('/kaggle/working'):\n        print(f\"There are {len(d_name)} directories and {len(filenames)} images in '{d_path}'.\")\ndata_status()\n\n\ntrain_files=os.listdir('/kaggle/working/train')\ntest_files=os.listdir('/kaggle/working/test1')","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:01.215883Z","iopub.execute_input":"2024-03-10T11:31:01.216876Z","iopub.status.idle":"2024-03-10T11:31:01.296126Z","shell.execute_reply.started":"2024-03-10T11:31:01.216825Z","shell.execute_reply":"2024-03-10T11:31:01.295056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision\nfrom torch.utils.data import Dataset, DataLoader, ConcatDataset\nfrom torchvision import transforms\nfrom PIL import Image\n\nclass CatDogDataset(Dataset):\n    def __init__(self, file_list, dir, mode='train', transform = None):\n        self.file_list = file_list\n        self.dir = dir\n        self.mode= mode\n        self.transform = transform\n        if self.mode == 'train':\n            if 'dog' in self.file_list[0]:\n                self.label = 1\n            else:\n                self.label = 0\n            \n    def __len__(self):\n        return len(self.file_list)\n    \n    def __getitem__(self, idx):\n        img = Image.open(os.path.join(self.dir, self.file_list[idx]))\n        if self.transform:\n            img = self.transform(img)\n        if self.mode == 'train':\n            img = img.numpy()\n            return img.astype('float32'), self.label\n        else:\n            img = img.numpy()\n            return img.astype('float32'), self.file_list[idx]\n\n        \n        \nbatch_size=32\n\ntrain_transforms=transforms.Compose([\n    transforms.RandomRotation(30),\n    transforms.Resize((224,224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5],[0.5])\n])\n\n\ncat_files=[tf for tf in train_files if 'cat' in tf]\ndog_files=[tf for tf in train_files if 'dog' in tf]\n\ncats=CatDogDataset(cat_files, '/kaggle/working/train', transform=train_transforms)\n\ndogs=CatDogDataset(dog_files, '/kaggle/working/train', transform=train_transforms)\n\ncatdogs=ConcatDataset([cats, dogs])\n\ndataloader=DataLoader(catdogs, batch_size, shuffle=True, num_workers=4)","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:01.297718Z","iopub.execute_input":"2024-03-10T11:31:01.298423Z","iopub.status.idle":"2024-03-10T11:31:08.055865Z","shell.execute_reply.started":"2024-03-10T11:31:01.298371Z","shell.execute_reply":"2024-03-10T11:31:08.054665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\nimages, labels = next(iter(dataloader))\nplt.figure(figsize=(16,24))\ngrid_imgs=torchvision.utils.make_grid(images[:24])\nnp_grid_imgs=grid_imgs.numpy()\nplt.imshow(np.transpose(np_grid_imgs, (1,2,0)))","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:08.059111Z","iopub.execute_input":"2024-03-10T11:31:08.060085Z","iopub.status.idle":"2024-03-10T11:31:09.763445Z","shell.execute_reply.started":"2024-03-10T11:31:08.060022Z","shell.execute_reply":"2024-03-10T11:31:09.762379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define the Model","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\ntorch.backends.cudnn.deterministic=True\n\nclass ImageClassifier(nn.Module):\n    def __init__(self):\n        super(ImageClassifier, self).__init__()\n        \n        # define convolutional layers\n        \n        self.conv1=nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)\n        self.relu1=nn.ReLU()\n        self.pool1=nn.MaxPool2d(kernel_size=2, stride=2)\n        \n        self.conv2=nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)\n        self.relu2=nn.ReLU()\n        # Since the pooling layer halves the size, the input image width and height must be divisible by 4 to avoid information loss during pooling. So our image should 224x224\n        self.pool2=nn.MaxPool2d(kernel_size=2, stride=2)\n        \n        # Define fully connected layers\n        self.fc1=nn.Linear(128*56*56,512) #Adjust the input size based on the image dimensions\n        self.relu3=nn.ReLU()\n        self.fc2=nn.Linear(512,2) # 2 output\n    \n    def forward(self, x):\n        out=self.conv1(x)\n        out=self.relu1(out)\n        out=self.pool1(out)\n        \n        out=self.conv2(out)\n        out=self.relu2(out)\n        out=self.pool2(out)\n        out=out.view(out.size(0), -1) # flatten the output\n        \n        out=self.fc1(out)\n        out=self.relu3(out)\n        out=self.fc2(out)\n        return out\n\nclassifier=ImageClassifier()\n\ndevice='cuda'\nclassifier.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:09.765202Z","iopub.execute_input":"2024-03-10T11:31:09.765526Z","iopub.status.idle":"2024-03-10T11:31:12.584063Z","shell.execute_reply.started":"2024-03-10T11:31:09.765492Z","shell.execute_reply":"2024-03-10T11:31:12.583055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Optimizing the Model Parameters\n\nWe need loss function and optimizer to train the model.","metadata":{}},{"cell_type":"code","source":"loss_fn=nn.CrossEntropyLoss()\n\nlearning_rate=1e-3\noptimizer=torch.optim.SGD(classifier.parameters(), lr=learning_rate)\nscheduler=torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[500, 1000, 1500], gamma=0.5)","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:12.585354Z","iopub.execute_input":"2024-03-10T11:31:12.585747Z","iopub.status.idle":"2024-03-10T11:31:12.592295Z","shell.execute_reply.started":"2024-03-10T11:31:12.585709Z","shell.execute_reply":"2024-03-10T11:31:12.591247Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"def train_func(dataloader, model, loss_fn, optimizer):\n    total_loss=0\n    size=len(dataloader.dataset)\n    model.train()\n    for batch, (X,y) in enumerate(dataloader):\n        X,y=X.to(device), y.to(device)\n        \n        # compute prediction error\n        pred=model(X)\n        loss=loss_fn(pred,y)\n        \n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        total_loss+=loss.item()\n        scheduler.step()\n        \n        if batch %100==0:\n            p=torch.argmax(pred, dim=1)\n            correct=p.eq(y)\n            acc=torch.mean(correct.float()).cpu()\n            loss,current=loss.item(), batch*len(X)\n            \n            print(f'Batch {batch} -> loss: {loss:>7f} [{current:>5d}/{size:>5d}], Accuracy: {acc:>3f}')\n            loss_list.append(total_loss/size)\n            acc_list.append(acc)\n            \n            \ndef test_func(dataloader, model):\n    size=len(dataloader.dataset)\n    model.eval()\n    test_loss, correct=0,0\n    with torch.no_grad():\n        for X, y in dataloader:\n            X,y =X.to(device), y.to(device)\n            pred=model(X)\n            test_loss+=loss_fn(pred,y).item()\n            correct+=(pred.argmax(1)==y).type(torch.float).sum().item()\n    test_loss/=size\n    correct /=size\n    print(f'Testing Score:\\n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f}\\n')\n            \nepochs=5\nloss_list=[]\nacc_list=[]\n\nfor t in range(epochs):\n    print(f'Epoch {t+1}\\n-------------------')\n    train_func(dataloader, classifier, loss_fn, optimizer)\n    test_func(dataloader, classifier)\nprint(\"Done\")\n\nplt.plot(loss_list, label='loss')\nplt.plot(acc_list, label='accuracy')\nplt.legend()\nplt.title('Training loss and accuracy')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-03-10T12:01:02.385577Z","iopub.execute_input":"2024-03-10T12:01:02.386618Z","iopub.status.idle":"2024-03-10T12:05:04.067263Z","shell.execute_reply.started":"2024-03-10T12:01:02.386572Z","shell.execute_reply":"2024-03-10T12:05:04.066038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save Models","metadata":{}},{"cell_type":"code","source":"torch.save(classifier.state_dict(), \"image_classifier_v01.pth\")","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:14.769213Z","iopub.status.idle":"2024-03-10T11:31:14.769609Z","shell.execute_reply.started":"2024-03-10T11:31:14.769403Z","shell.execute_reply":"2024-03-10T11:31:14.769419Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference and Submission","metadata":{}},{"cell_type":"code","source":"test_transforms=transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor()\n#     transforms.Normalize([0.5],[0.5])\n])\n\ntestset=CatDogDataset(test_files, '/kaggle/working/test1', mode='test', transform=test_transforms)\ntestloader=DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=4)","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:14.770822Z","iopub.status.idle":"2024-03-10T11:31:14.771220Z","shell.execute_reply.started":"2024-03-10T11:31:14.771003Z","shell.execute_reply":"2024-03-10T11:31:14.771017Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\nclassifier.eval()\nfn_list=[]\npred_list=[]\nfor x, fn in testloader:\n    with torch.no_grad():\n        x = x.to(device)\n        output = classifier(x)\n        pred = torch.argmax(output, dim=1)\n        fn_list += [n[:-4] for n in fn]\n        pred_list += [p.item() for p in pred]\n\nsubmission = pd.DataFrame({\"id\":fn_list, \"label\":pred_list})\nsubmission.to_csv('preds_with_customize_nn.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:14.774199Z","iopub.status.idle":"2024-03-10T11:31:14.774521Z","shell.execute_reply.started":"2024-03-10T11:31:14.774352Z","shell.execute_reply":"2024-03-10T11:31:14.774365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Checking the Accuracy by Random Images\n\nNote: We can create training epochs in increase the accuracy of the classifier(model).","metadata":{}},{"cell_type":"code","source":"samples, _ =next(iter(testloader))\n\nsamples=samples.to(device)\nfig=plt.figure(figsize=(24,16))\nfig.tight_layout()\noutput=classifier(samples[:24])\npred=torch.argmax(output, dim=1)\npred=[p.item() for p in pred]\nad={0:'cat', 1:'dog'}\n\nfor num, sample in enumerate(samples[:24]):\n    plt.subplot(4,6,num+1)\n    plt.title(ad[pred[num]])\n    plt.axis('off')\n    sample=sample.cpu().numpy()\n    plt.imshow(np.transpose(sample, (1,2,0)))","metadata":{"execution":{"iopub.status.busy":"2024-03-10T11:31:14.776118Z","iopub.status.idle":"2024-03-10T11:31:14.776431Z","shell.execute_reply.started":"2024-03-10T11:31:14.776281Z","shell.execute_reply":"2024-03-10T11:31:14.776294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Acknowledge\n\n* https://www.kaggle.com/code/kurniazulda/cat-vs-dog\n* https://www.kaggle.com/code/aisuko/fine-tuning-phi-2-with-qlora\n* https://www.kaggle.com/code/aisuko/neural-network-for-image-classification\n* https://www.kaggle.com/code/jaeboklee/pytorch-cat-vs-dog/notebook","metadata":{}}]}