{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","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":4532039,"sourceType":"datasetVersion","datasetId":2579480},{"sourceId":252473,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":215862,"modelId":237573}],"dockerImageVersionId":30840,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np \nimport torch.nn as nn\nimport torch.optim as optim\nfrom PIL import Image\nimport matplotlib.pyplot as plt \nimport torch.nn.functional as F\nfrom torchvision import datasets,transforms\nfrom torch.utils.data import DataLoader,Dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:07.778412Z","iopub.execute_input":"2025-02-07T15:57:07.778836Z","iopub.status.idle":"2025-02-07T15:57:07.783658Z","shell.execute_reply.started":"2025-02-07T15:57:07.778779Z","shell.execute_reply":"2025-02-07T15:57:07.782795Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if torch.cuda.is_available():\n    print('GPU Available.')\n    device = torch.device('cuda')\nelse:\n    print('GPU not Available. Running on CPU')\n    device = torch.device('cpu')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:07.981156Z","iopub.execute_input":"2025-02-07T15:57:07.981468Z","iopub.status.idle":"2025-02-07T15:57:07.986239Z","shell.execute_reply.started":"2025-02-07T15:57:07.981439Z","shell.execute_reply":"2025-02-07T15:57:07.985239Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loading Data","metadata":{}},{"cell_type":"code","source":"FILE_PATH = {\n    'train' : '/kaggle/input/cards-image-datasetclassification/train',\n    'test' : '/kaggle/input/cards-image-datasetclassification/test',\n    'valid' : '/kaggle/input/cards-image-datasetclassification/valid'\n}\nBATCH_SIZE = 64\nIDX2CLASS = {i:c for i,c in enumerate(sorted(os.listdir(FILE_PATH['train'])))}\nLR = 0.0015\nEPOCHS = 50","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:10.085637Z","iopub.execute_input":"2025-02-07T15:57:10.085951Z","iopub.status.idle":"2025-02-07T15:57:10.108070Z","shell.execute_reply.started":"2025-02-07T15:57:10.085927Z","shell.execute_reply":"2025-02-07T15:57:10.107259Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Dset(Dataset):\n    def __init__(self,path,transform=None):\n        super(Dataset, self).__init__()\n        self.data = datasets.ImageFolder(path)\n        self.transform = transform\n\n    def __getitem__(self, index):\n        image,target = self.data[index]\n        if self.transform:\n            image = self.transform(image)\n        return image,target\n\n    def __len__(self):\n        return len(self.data)\n    \n    def get_classes(self):\n        return self.data.classes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:11.461421Z","iopub.execute_input":"2025-02-07T15:57:11.461729Z","iopub.status.idle":"2025-02-07T15:57:11.466618Z","shell.execute_reply.started":"2025-02-07T15:57:11.461701Z","shell.execute_reply":"2025-02-07T15:57:11.465794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_trans = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n    transforms.RandomHorizontalFlip(.3),\n    transforms.RandomRotation(30),\n    transforms.ColorJitter(brightness = 0.3, contrast = 0.3, hue=0.3),\n    transforms.Normalize((.5,),(.5,))\n])\n\nval_trans =  transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n    transforms.Normalize((.5,),(.5,))\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:13.308238Z","iopub.execute_input":"2025-02-07T15:57:13.308559Z","iopub.status.idle":"2025-02-07T15:57:13.314282Z","shell.execute_reply.started":"2025-02-07T15:57:13.308531Z","shell.execute_reply":"2025-02-07T15:57:13.312965Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainset = Dset(FILE_PATH['train'],transform=train_trans)\nvalset = Dset(FILE_PATH['valid'],transform=val_trans)\ntestset = Dset(FILE_PATH['test'],transform=val_trans)\n\nprint(f'Train : {len(trainset)}\\nValid : {len(valset)}\\nTest : {len(testset)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:13.515565Z","iopub.execute_input":"2025-02-07T15:57:13.515828Z","iopub.status.idle":"2025-02-07T15:57:39.442346Z","shell.execute_reply.started":"2025-02-07T15:57:13.515806Z","shell.execute_reply":"2025-02-07T15:57:39.441432Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(trainset,BATCH_SIZE,shuffle=True,pin_memory=True)\nval_loader = DataLoader(valset,BATCH_SIZE,pin_memory=True)\ntest_loader = DataLoader(testset,BATCH_SIZE,pin_memory=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:39.443662Z","iopub.execute_input":"2025-02-07T15:57:39.444012Z","iopub.status.idle":"2025-02-07T15:57:39.448250Z","shell.execute_reply.started":"2025-02-07T15:57:39.443976Z","shell.execute_reply":"2025-02-07T15:57:39.447422Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Designing Model","metadata":{}},{"cell_type":"code","source":"# I just wrote it low level for practice.\n\n# A depth-wise separable convolution:\n#  step 1.Independent depth-wise filtering\n#  step 2.Point-wise 1x1 standard convolution for mapping to output channels\n\nclass SpConv(nn.Module):\n    def __init__(self,in_channels,out_channels,strides=1):\n        super(SpConv, self).__init__()\n        self.strides = strides\n        \n        self.dw_w = nn.Parameter(torch.randn((in_channels,1,3,3)) * 0.01)\n        self.dw_b = nn.Parameter(torch.zeros((in_channels,)))\n        self.dw_norm = nn.BatchNorm2d(in_channels)\n        self.dw_relu = nn.ReLU()\n        \n        self.pw_w = nn.Parameter(torch.randn(out_channels,in_channels,1,1) * 0.01)\n        self.pw_b = nn.Parameter(torch.zeros((out_channels,)))\n        self.pw_norm = nn.BatchNorm2d(out_channels)\n        self.pw_relu = nn.ReLU()\n\n    def forward(self, x):\n\n        # Depth-wise \n        dw = F.conv2d(x,self.dw_w,self.dw_b,self.strides,groups=x.shape[1])\n        dw = self.dw_norm(dw)\n        dw = self.dw_relu(dw)\n\n        # Point-wise\n        pw = F.conv2d(dw,self.pw_w,self.pw_b,1,1)\n        pw = self.pw_norm(pw)\n        pw = self.pw_relu(pw)\n\n        return pw","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:39.449626Z","iopub.execute_input":"2025-02-07T15:57:39.449895Z","iopub.status.idle":"2025-02-07T15:57:39.465568Z","shell.execute_reply.started":"2025-02-07T15:57:39.449873Z","shell.execute_reply":"2025-02-07T15:57:39.464811Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Block(nn.Module):\n    def __init__(self,in_ch,out_ch,_pool=True,_dropout=None):\n        super(Block,self).__init__()\n        \n        self.sequence = nn.Sequential(\n            SpConv(in_ch,out_ch),\n            SpConv(out_ch,out_ch)\n        )\n        \n        if _pool:\n            self.sequence.append(nn.MaxPool2d(2,2))\n        if _dropout:\n            self.sequence.append(nn.Dropout2d(_dropout))\n\n    def forward(self,x):\n        return self.sequence(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:39.466947Z","iopub.execute_input":"2025-02-07T15:57:39.467153Z","iopub.status.idle":"2025-02-07T15:57:39.481343Z","shell.execute_reply.started":"2025-02-07T15:57:39.467135Z","shell.execute_reply":"2025-02-07T15:57:39.480558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self):\n        super(Model, self).__init__()\n\n        self.conv = nn.Sequential(  # output shapes:\n            Block(3,64),                 # (Batch-Size,64,112,112)\n            Block(64,128),               # (Batch-Size,128,56,56)\n            Block(128,256),              # (Batch-Size,256,28,28)\n            Block(256,512,_dropout=.15), # (Batch-Size,512,14,14)\n            )\n        \n        self.fc = nn.Sequential(\n            nn.Linear(512,256),\n            nn.BatchNorm1d(256),\n            nn.ReLU(),\n            nn.Dropout(0.3), # Fully connected with dropout\n            \n            nn.Linear(256,len(IDX2CLASS)) # Projection\n            )\n        \n    def forward(self, x):\n        x = self.conv(x)\n        x = torch.mean(x.flatten(-2,-1),-1)\n        x = self.fc(x)\n        return x","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:39.482229Z","iopub.execute_input":"2025-02-07T15:57:39.482520Z","iopub.status.idle":"2025-02-07T15:57:39.493473Z","shell.execute_reply.started":"2025-02-07T15:57:39.482490Z","shell.execute_reply":"2025-02-07T15:57:39.492823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = Model().to(device)\nprint(f'Parameters: {sum([p.numel() for p in model.parameters()])}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:39.494059Z","iopub.execute_input":"2025-02-07T15:57:39.494268Z","iopub.status.idle":"2025-02-07T15:57:39.739830Z","shell.execute_reply.started":"2025-02-07T15:57:39.494249Z","shell.execute_reply":"2025-02-07T15:57:39.739052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.AdamW(model.parameters(),LR)\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,'min',.7,patience=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:54.156397Z","iopub.execute_input":"2025-02-07T15:57:54.156705Z","iopub.status.idle":"2025-02-07T15:57:54.161265Z","shell.execute_reply.started":"2025-02-07T15:57:54.156680Z","shell.execute_reply":"2025-02-07T15:57:54.160261Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metric = {'loss':[],'val_loss':[],'acc':[],'val_acc':[],'lr':[]}\nfor epoch in range(EPOCHS):\n    model.train()\n    train_loss = 0.\n    train_acc = 0. \n    for idx,(img,target) in enumerate(train_loader):\n        img, target = img.to(device), target.to(device)\n        optimizer.zero_grad(True)\n        logit = model(img)\n        loss = criterion(logit,target)\n        \n        acc = torch.sum(torch.argmax(logit,-1)==target) / len(img)\n        train_loss += loss\n        train_acc += acc\n        loss.backward()\n        optimizer.step()\n        \n        if idx== 0 and epoch==0:\n            print(f'[First Step] Loss: {loss} Accuracy: {acc}')\n    \n    model.eval()\n    val_loss = 0. \n    val_acc = 0.\n    with torch.no_grad():\n        for idx,(img,target) in enumerate(val_loader):\n            img, target = img.to(device), target.to(device)\n            logit = model(img)\n            loss = criterion(logit,target)\n            \n            acc = torch.sum(torch.argmax(logit,-1)==target) / len(img)\n            val_loss += loss \n            val_acc += acc\n    \n    train_acc /= len(train_loader)\n    train_loss /= len(train_loader)\n    val_loss /= len(val_loader)\n    val_acc /= len(val_loader)\n\n    scheduler.step(val_loss)\n    metric['lr'].append(scheduler.get_last_lr()[-1])\n    metric['val_acc'].append(val_acc.item())\n    metric['val_loss'].append(val_loss.item())\n    metric['loss'].append(train_loss.item())\n    metric['acc'].append(train_acc.item())\n    print(f'[{epoch+1}] Loss: {train_loss:.3f} Accuracy: {train_acc:.2f} -- Val_Loss: {val_loss:.3f} Val_Accuracy: {val_acc:.2f} -- LR: {scheduler.get_last_lr()[-1]}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T15:57:56.038770Z","iopub.execute_input":"2025-02-07T15:57:56.039058Z","iopub.status.idle":"2025-02-07T16:57:20.759317Z","shell.execute_reply.started":"2025-02-07T15:57:56.039037Z","shell.execute_reply":"2025-02-07T16:57:20.758343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluattion on test set\nmodel.eval() \nval_loss = 0. \nval_acc = 0. \nwith torch.no_grad(): \n    for idx,(img,target) in enumerate(test_loader):\n        img, target = img.to(device), target.to(device)\n        logit = model(img)\n        loss = criterion(logit,target)\n        \n        acc = torch.sum(torch.argmax(logit,-1)==target) / len(img)\n        val_loss += loss \n        val_acc += acc\nprint(f'Loss: {val_loss/len(test_loader)} - Accuracy: {val_acc/len(test_loader)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T16:57:20.760784Z","iopub.execute_input":"2025-02-07T16:57:20.761120Z","iopub.status.idle":"2025-02-07T16:57:23.159046Z","execution_failed":"2025-02-07T17:10:19.571Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model,'./DSC.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T16:57:23.165869Z","iopub.execute_input":"2025-02-07T16:57:23.166160Z","iopub.status.idle":"2025-02-07T16:57:23.187007Z","execution_failed":"2025-02-07T17:10:19.572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def visualize_results():\n    sub_folders = np.random.choice(os.listdir(FILE_PATH['test']),9)\n    sub_folders = [os.path.join(FILE_PATH['test'],folder) for folder in sub_folders]\n    imgs = []\n    for f in sub_folders:\n        img = os.path.join(f,np.random.choice(os.listdir(f),1).item())\n        imgs.append(img)\n    \n    fig,ax = plt.subplots(3,3,figsize=(15,12))\n    \n    for i,ax in enumerate(ax.flat):\n        origin = Image.open(imgs[i])\n        trans = val_trans(origin).unsqueeze(0).to(device)\n        probs = F.softmax(model(trans),-1)\n        tag = torch.argmax(probs,-1)\n        ax.imshow(origin)\n        ax.title.set_text(IDX2CLASS[tag.item()])\n        ax.set_axis_off()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T16:58:34.876279Z","iopub.execute_input":"2025-02-07T16:58:34.876614Z","iopub.status.idle":"2025-02-07T16:58:34.882842Z","shell.execute_reply.started":"2025-02-07T16:58:34.876584Z","shell.execute_reply":"2025-02-07T16:58:34.881787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_results()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-07T16:58:35.634983Z","iopub.execute_input":"2025-02-07T16:58:35.635257Z","iopub.status.idle":"2025-02-07T16:58:36.759125Z","shell.execute_reply.started":"2025-02-07T16:58:35.635235Z","shell.execute_reply":"2025-02-07T16:58:36.757999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}