{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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 in \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 \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\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# Any results you write to the current directory are saved as output.\nfrom torch.utils.data import Dataset,DataLoader\nfrom tqdm import tqdm_notebook as tqdm\nfrom imgaug import augmenters as iaa\nimport torchvision.models as models\nfrom torch.autograd import Variable\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport torch.nn as nn\nimport torch\nimport cv2\nimport gc","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Make Train_Images/Test_Images Directory and Unzip"},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"!mkdir train_images\n!mkdir test_images\n!unzip /kaggle/input/2019-3rd-ml-month-with-kakr/train.zip -d train_images\n!unzip /kaggle/input/2019-3rd-ml-month-with-kakr/test.zip -d test_images","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Read Train.csv / Class.csv"},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv('/kaggle/input/2019-3rd-ml-month-with-kakr/train.csv')\nclasses = pd.read_csv('/kaggle/input/2019-3rd-ml-month-with-kakr/class.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_dict = classes.set_index('id').to_dict()['name']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Dict List"},{"metadata":{"trusted":true},"cell_type":"code","source":"class_dict","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## One-Hot Encoding"},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.concat([train,pd.get_dummies(train['class'])],axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Check The Number of Classes and Balance"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['class'].nunique()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.groupby('class')['class'].count().plot(kind='bar',title='Classes Counting')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Crop and Resize Images"},{"metadata":{"trusted":true},"cell_type":"code","source":"class crop_resize(object):\n    def __init__(self,df):\n        self.df = df\n        \n            \n    def __call__(self):\n        if 'class' in self.df.columns:\n            path = os.path.join(os.getcwd(),'train_images')\n            save_path = os.path.join(os.getcwd(),'resized_train_images')\n            if os.path.isdir('resized_train_images'):\n                !rm -r 'resized_train_images'\n            !mkdir resized_train_images\n        else:\n            path = os.path.join(os.getcwd(),'test_images')\n            save_path = os.path.join(os.getcwd(),'resized_test_images')\n            if os.path.isdir('resized_test_images'):\n                !rm -r 'resized_test_images'\n            !mkdir resized_test_images\n        \n        for fname in self.df.img_file:\n            Image = cv2.imread(os.path.join(path,fname))\n            x1,x2,y1,y2 = tuple(self.df.set_index('img_file').loc[fname, ['bbox_x1','bbox_x2','bbox_y1','bbox_y2']])\n            h,w,_ = Image.shape #height x width x channels\n            b1,b2 = x1-x2 , y1-y2\n            padd_x1,padd_x2 = max(int(x1 - b1*0.01),0), min(int(x2 - b1*0.01),w-1)\n            padd_y1,padd_y2 = max(int(y1 - b2*0.01),0), min(int(y2 - b2*0.01),h-1)\n            #get crop it\n            Image = Image[padd_y1:padd_y2,padd_x1:padd_x2]\n            Image = cv2.resize(Image,(244,244))\n            status = cv2.imwrite(os.path.join(save_path,fname),Image)\n            print('save {} at {}'.format(fname,save_path))\n        \n            \n            ","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"preprocess = crop_resize(train)\npreprocess()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Image Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"class transforms(object):\n    def __init__(self):\n        #Scheme(Tentative)\n        self.transforms = iaa.Sequential(\n            [iaa.Fliplr(0.5),\n             iaa.Sometimes(0.5,iaa.AdditiveGaussianNoise(loc=0, scale =(0.0, 0.05*255),per_channel=0.5)),\n             iaa.Sometimes(0.5,iaa.ContrastNormalization((0.75,1.5))),\n             iaa.Sometimes(0.5,iaa.GaussianBlur(sigma=(0,0.5))),\n             iaa.Sometimes(0.5,iaa.Affine(shear=(-5, 5))),\n             iaa.Sometimes(0.5,iaa.Grayscale(alpha=(0.0, 1.0)))\n            ],random_order=True)\n        \n    def __call__(self,Image,fname,df,preprocessed):\n        if preprocessed:\n            Image = self.transforms.augment_image(Image)\n        else:\n            #get bounding_box\n            x1,x2,y1,y2 = tuple(df.set_index('img_file').loc[fname, ['bbox_x1','bbox_x2','bbox_y1','bbox_y2']])\n            h,w,_ = Image.shape #height x width x channels\n            b1,b2 = x1-x2 , y1-y2\n            padd_x1,padd_x2 = max(int(x1 - b1*0.01),0), min(int(x2 - b1*0.01),w-1)\n            padd_y1,padd_y2 = max(int(y1 - b2*0.01),0), min(int(y2 - b2*0.01),h-1)\n            #get crop it\n            Image = Image[padd_y1:padd_y2,padd_x1:padd_x2]\n            #get transforms\n            Image = self.transforms.augment_image(Image)\n            #resize\n            Image = cv2.resize(Image,(244,244))\n        return Image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Custom DataLoader"},{"metadata":{"trusted":true,"_kg_hide-output":false},"cell_type":"code","source":"class CarDataset(Dataset):\n    \n    def __init__(self,df,transforms=None,preprocessed=True,root='/kaggle/input/'):\n        self.transforms = transforms\n        self.df = df\n        self.preprocessed = preprocessed\n        if 'class' in self.df:\n            self.df = df\n            self.classes = df.set_index('img_file')['class'].to_dict\n            if self.preprocessed:\n                self.path = os.path.join(os.getcwd(),'resized_train_images')\n            else:\n                self.path = os.path.join(os.getcwd(),'train_images')\n        else:\n            if self.preprocessed:\n                self.path = os.path.join(os.getcwd(),'resized_test_images')\n            else:\n                self.path = os.path.join(os.getcwd(),'test_images')\n            \n            \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self,idx):\n        if 'class' in self.df:\n            #label = self.df.iloc[:,-196:].values[idx]\n            label = self.df['class'].values[idx]-1\n            fname = self.df.img_file.values[idx]\n            Image = cv2.imread(os.path.join(self.path,fname))\n            if self.transforms is not None:\n                transform = self.transforms()\n                Image = transform(Image,fname,self.df,self.preprocessed)\n            else:\n                Image = cv2.resize(Image,(244,244))\n            return Image.astype(np.float)/255.0,label\n        else:\n            fname = self.df.img_file.values[idx]\n            Image = cv2.imread(os.path.join(self.path,fname))\n            #Image = cv2.resize(Image,(244,244))\n            return np.transpose(Image.astype(np.float)/255.0,(2,1,0))\n            ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Before Crop and Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_images = CarDataset(train, preprocessed=False)\ntrain_loader = torch.utils.data.DataLoader(train_images,batch_size=9,shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\na = next(iter(train_loader))\nfig,ax = plt.subplots(3,3, figsize=(25,25))\nfor i in range(9):\n    j = i//3\n    k = i%3\n    ax[j,k].imshow(a[0][i])\n    ax[j,k].set_title(class_dict[int(torch.max(a[1][i],0)[1].detach().numpy())+1],fontsize= 15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## After Crop and Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_images = CarDataset(train,transforms=transforms)\ntrain_loader = torch.utils.data.DataLoader(train_images,batch_size=9,shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"a = next(iter(train_loader))\nfig,ax = plt.subplots(3,3, figsize=(25,25))\nfor i in range(9):\n    j = i//3\n    k = i%3\n    ax[j,k].imshow(a[0][i])\n    ax[j,k].set_title(class_dict[int(torch.max(a[1][i],0)[1].detach().numpy())+1],fontsize= 15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Build Custom Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"class AdaptiveConcatPool2d(nn.Module):\n    def __init__(self,size=None):\n        super(AdaptiveConcatPool2d, self).__init__()\n        size = size or (1,1)\n        self.avgpool = nn.AdaptiveAvgPool2d(size)\n        self.maxpool = nn.AdaptiveMaxPool2d(size)\n        \n    def forward(self,x):\n        return torch.cat([self.maxpool(x),self.avgpool(x)],1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"class ResNeXt50(nn.Module):\n    def __init__(self,pretrained=True):\n        super(ResNeXt50,self).__init__()\n        encoder = models.resnext50_32x4d(pretrained=pretrained)#,progress=False)\n        encoder = nn.Sequential(*list(encoder.children()))\n        \n        # cut tail\n        self.cnn = nn.Sequential(\n            encoder[0],\n            encoder[1],\n            encoder[2],\n            encoder[3],\n            encoder[4],\n            encoder[5],\n            encoder[6],\n            encoder[7],\n        )\n        '''\n        # freeze weight\n        print('Freeze Pretrained model')\n        for param in self.cnn.parameters():\n            print(f'before {param.requires_grad}')\n            param.requires_grad = False\n            print(f'after {param.requires_grad}')\n        '''\n            \n        \n        # add layers\n        self.clf = nn.Sequential(\n            AdaptiveConcatPool2d(),\n            nn.Flatten(),\n            nn.Dropout(0.4),\n            nn.Linear(4096,512),\n            nn.ReLU(inplace=True),\n            nn.BatchNorm1d(512),\n            nn.Dropout(0.2),\n            nn.Linear(512,196)\n        )\n        \n    def forward(self,x):\n        x = self.cnn(x)\n        x = self.clf(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nmodel = ResNeXt50().to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Check Model Summary"},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"!pip install torchsummary\nfrom torchsummary import summary\nsummary(model, input_size=(3,224,224))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Custom Learner"},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Strafified split(6:4) X\n### Strafified split(9:1) O"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss(reduction = 'mean')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Learner(object):\n    \n    def __init__(self):\n        \n        self.train_losses = []\n        self.valid_losses = []\n        self.train_accs = []\n        self.valid_accs = []\n        \n    def fit(self,epochs=5,batch_size=64,shuffle=True):\n        model.to(device)\n        self.train(model=model,epochs=epochs,batch_size=batch_size,shuffle=shuffle)\n        \n        return self.train_losses,self.valid_losses,self.train_accs,self.valid_accs\n    \n    def train(self,model,epochs,batch_size=32,shuffle=True):\n        optimizer = optimizer = torch.optim.SGD(model.parameters(), lr=0.05, momentum=0.9)\n        scheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr=0.001, max_lr=0.05)\n        for epoch in range(epochs):\n            model.train()\n            running_loss = 0.0\n            running_f1 = 0.0\n            running_acc = 0.0\n            print(f'epoch {epoch+1}/{epochs}')\n            x_train, x_valid, _, _ = train_test_split(train, train['class'],\n                                                    stratify=train['class'], \n                                                    test_size=0.1)\n            train_set = CarDataset(x_train,transforms=transforms)\n            train_loader = torch.utils.data.DataLoader(train_set,batch_size=batch_size,shuffle=shuffle)\n            for idx,(inputs,labels) in tqdm(enumerate(train_loader),total=len(train_loader)):\n                optimizer.zero_grad()\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n                outputs = model(inputs.permute(0,3,2,1).float())\n                loss = criterion(outputs,labels)\n                loss.backward()\n                optimizer.step()\n                scheduler.step()\n                running_loss += loss\n                running_acc += (outputs.argmax(dim=1) == labels).float().mean()\n            self.train_losses.append(running_loss/len(train_loader))\n            self.train_accs.append(running_acc/len(train_loader))\n            if epoch%5 == 3:\n            #if epoch == epoch: #for monitoring\n                gc.collect()\n                val_loss , val_acc = self.valid(model=model,shuffle=shuffle,x_valid=x_valid)\n                print('train_loss : {:.2f} | train_acc : {:.2f} | valid_loss : {:.2f} | valid_acc : {:.2f}'.format(running_loss/len(train_loader),running_acc/len(train_loader)\n                                                                                                                         ,val_loss,val_acc))\n    def valid(self,model,batch_size=200,shuffle=True,x_valid=None):\n        valid_set = CarDataset(x_valid,transforms=transforms)\n        valid_loader = torch.utils.data.DataLoader(valid_set,batch_size=batch_size,shuffle=shuffle)\n        model.eval()\n        running_loss = 0.0\n        running_f1 = 0.0\n        running_acc = 0.0\n        with torch.no_grad():\n            for idx,(inputs,labels) in tqdm(enumerate(valid_loader),total=len(valid_loader)):\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n                outputs = model(inputs.permute(0,3,2,1).float())\n                loss = criterion(outputs,labels)\n                running_loss += loss\n                running_acc += (outputs.argmax(dim=1) == labels).float().mean()\n        self.valid_losses.append(running_loss/len(valid_loader))\n        self.valid_accs.append(running_acc/len(valid_loader))\n        return running_loss/len(valid_loader), running_acc/len(valid_loader)\n            ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"learner = Learner()\ntrain_losses, valid_losses, train_accs, valid_accs = learner.fit(epochs=10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axs = plt.subplots(2, 2, figsize=(15, 5))\naxs[0,0].plot(train_losses)\naxs[0,0].set_title('Train Loss')\naxs[0,1].plot(train_accs)\naxs[0,1].set_title('Train acc')\naxs[1,0].plot(valid_losses)\naxs[1,0].set_title('Valid Loss')\naxs[1,1].plot(valid_accs)\naxs[1,1].set_title('Valid acc')\nfig.tight_layout()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv('/kaggle/input/2019-3rd-ml-month-with-kakr/test.csv')","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"preprocess = crop_resize(test)\npreprocess()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images = CarDataset(test)\ntest_loader = torch.utils.data.DataLoader(test_images,batch_size=9,shuffle=False)\nprediction = []\nmodel.eval()\nwith torch.no_grad():\n    \n    for idx, (inputs) in tqdm(enumerate(test_loader),total=len(test_loader)):\n        inputs.to(device)\n        \n        outputs = model(inputs.float().cuda())\n        preds = outputs.argmax(dim=1).detach().cpu().numpy()\n        prediction.append(preds)\n    prediction = np.hstack(prediction)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = np.hstack(prediction)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv('/kaggle/input/2019-3rd-ml-month-with-kakr/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission['class'] =prediction + 1 # correction\nsubmission.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images = CarDataset(test)\ntest_loader = torch.utils.data.DataLoader(test_images,batch_size=9,shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"a = next(iter(train_loader))\nfig,ax = plt.subplots(3,3, figsize=(25,25))\nfor i in range(9):\n    j = i//3\n    k = i%3\n    ax[j,k].imshow(a[0][i])\n    ax[j,k].set_title(class_dict[int(torch.max(a[1][i],0)[1].detach().numpy())+1],fontsize= 15)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Incomplete ver.\nTo do.\n* Fine-Tuning\n* Evaluation //\n* Submision //\n\n- unfreeze\n- apply different learning rate for each layers.\n\nAny advices will be appreciated."},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -r train_images\n!rm -r resized_train_images\n!rm -r test_images\n!rm -r resized_test_images","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}