{"cells":[{"metadata":{"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.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\n\nimport os\nimport time\nimport copy\nimport pandas as pd\nimport numpy as np\n\nfrom random import seed\nfrom random import randint\nimport random\n\nimport torch\nfrom torch import nn\nfrom torch import optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms, models\n\nfrom PIL import Image\nfrom matplotlib import pyplot as plt\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nfrom tqdm import tqdm_notebook as tqdm\n\n\n\ntrain_dir = os.path.join(\"../input/imet-2019-fgvc6/train\")\ntest_dir  = os.path.join(\"../input/imet-2019-fgvc6/test\")\nlabels_csv= os.path.join(\"../input/imet-2019-fgvc6/labels.csv\")\ntrain_csv = os.path.join(\"../input/imet-2019-fgvc6/train.csv\")\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(labels_csv)\nattribute_dict = dict(zip(df.attribute_id,df.attribute_name))\ndel df,labels_csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tag_count = 0 \nculture_count = 0\nfor idx,data in attribute_dict.items():\n    if data.split(\"::\")[0] == 'tag':\n        tag_count+=1\n    if data.split(\"::\")[0] == 'culture':\n        culture_count+=1\nprint('total_categories: {0}\\ntag_categories: {1} \\nculture_categories: {2} ' \\\n      .format(len(attribute_dict),tag_count,culture_count))\n#cross check your results\nassert tag_count+culture_count == len(attribute_dict)\noutput_dim = len(attribute_dict) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(train_csv)\nlabels_dict = dict(zip(df.id,df.attribute_ids))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"idx = len(os.listdir(train_dir))\nnumber = randint(0,idx)\nimage_name = os.listdir(train_dir)[number]\ndef imshow(image):\n    plt.figure(figsize=(6, 6))\n    plt.imshow(image)\n    plt.show()\n# Example image\nx = Image.open(os.path.join(train_dir,image_name))\nfor i in labels_dict[os.listdir(train_dir)[number].split('.')[0]].split():\n    print(attribute_dict[int(i)])\nnp.array(x).shape\nimshow(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 1000\nNUM_EPOCHS = 40\nPERCENTILE = 99.7\nLEARNING_RATE = 0.0001\nDISABLE_TQDM = True","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# need to add more transforms here\ndata_transforms = transforms.Compose([\n        transforms.Resize((32,32)),\n        transforms.ToTensor(),\n    ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils import data\nclass ImageData(data.Dataset):\n    def __init__(self,df,dirpath,transform,test = False):\n        self.df = df\n        self.test = test\n        self.dirpath = dirpath\n        self.conv_to_tensor = transform\n        #image data \n        if not self.test:\n            self.image_arr = np.asarray(str(self.dirpath)+'/'+self.df.iloc[:, 0]+'.png')\n        else:\n            self.image_arr = np.asarray(str(self.dirpath)+'/'+self.df.iloc[:, 0])\n        \n        #labels data\n        if not self.test:\n             self.label_df = self.df.iloc[:,1]\n        \n        # Calculate length of df\n        self.data_len = len(self.df.index)\n\n    def __len__(self):\n        return self.data_len\n    \n    def __getitem__(self, idx):\n        image_name = self.image_arr[idx]\n        img = Image.open(image_name)\n        img_tensor = self.conv_to_tensor(img)\n        if not self.test:\n            image_labels = self.label_df[idx]\n            label_tensor = torch.zeros((1, output_dim))\n            for label in image_labels.split():\n                label_tensor[0, int(label)] = 1\n            image_label = torch.tensor(label_tensor,dtype= torch.float32)\n            return (img_tensor,image_label.squeeze())\n        return (img_tensor)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#df = pd.read_csv(train_csv)\n# if you want to run on less data to quickly check\ndf = pd.read_csv(train_csv)\nfrom sklearn.model_selection import train_test_split\ntrain_df,val_df = train_test_split(df, test_size=0.20)\ntrain_df = train_df.reset_index(drop=True)\nval_df = val_df.reset_index(drop=True)\nprint(f\"Validation_Data Length: {len(val_df)}\\n Train_Data Length: {len(train_df)}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Train dataset\ntrain_dataset = ImageData(train_df,train_dir,data_transforms)\ntrain_loader = data.DataLoader(dataset=train_dataset,batch_size=BATCH_SIZE,shuffle=False)\n\n# validation dataset\nval_dataset = ImageData(val_df,train_dir,data_transforms)\nval_loader = data.DataLoader(dataset=val_dataset,batch_size=BATCH_SIZE,shuffle=False)\n\n# test dataset\ntest_df = pd.DataFrame(os.listdir(test_dir))\ntest_dataset = ImageData(test_df,test_dir,data_transforms,test = True)\ntest_loader = data.DataLoader(dataset=test_dataset,batch_size=BATCH_SIZE,shuffle=False)\n\ndataloaders_dict = {'train':train_loader, 'val':val_loader}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"features, labels = next(iter(train_loader))\nprint(f'Train Features: {features.shape}\\nTrain Labels: {labels.shape}')\nprint()\nfeatures, labels = next(iter(val_loader))\nprint(f'Validation Features: {features.shape}\\nValidation Labels: {labels.shape}')\nprint()\nfeatures = next(iter(test_loader))\nprint(f'Test Features: {features.shape}\\n')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class baseBlock(torch.nn.Module):\n    expansion = 1\n    def __init__(self,input_planes,planes,stride=1,dim_change=None):\n        super(baseBlock,self).__init__()\n        #declare convolutional layers with batch norms\n        self.conv1 = torch.nn.Conv2d(input_planes,planes,stride=stride,kernel_size=3,padding=1)\n        self.bn1   = torch.nn.BatchNorm2d(planes)\n        self.conv2 = torch.nn.Conv2d(planes,planes,stride=1,kernel_size=3,padding=1)\n        self.bn2   = torch.nn.BatchNorm2d(planes)\n        self.dim_change = dim_change\n    def forward(self,x):\n        #Save the residue\n        res = x\n        output = F.relu(self.bn1(self.conv1(x)))\n        output = self.bn2(self.conv2(output))\n\n        if self.dim_change is not None:\n            res = self.dim_change(res)\n        \n        output += res\n        output = F.relu(output)\n\n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class bottleNeck(torch.nn.Module):\n    expansion = 4\n    def __init__(self,input_planes,planes,stride=1,dim_change=None):\n        super(bottleNeck,self).__init__()\n\n        self.conv1 = torch.nn.Conv2d(input_planes,planes,kernel_size=1,stride=1)\n        self.bn1 = torch.nn.BatchNorm2d(planes)\n        self.conv2 = torch.nn.Conv2d(planes,planes,kernel_size=3,stride=stride,padding=1)\n        self.bn2 = torch.nn.BatchNorm2d(planes)\n        self.conv3 = torch.nn.Conv2d(planes,planes*self.expansion,kernel_size=1)\n        self.bn3 = torch.nn.BatchNorm2d(planes*self.expansion)\n        self.dim_change = dim_change\n    \n    def forward(self,x):\n        res = x\n        \n        output = F.relu(self.bn1(self.conv1(x)))\n        output = F.relu(self.bn2(self.conv2(output)))\n        output = self.bn3(self.conv3(output))\n\n        if self.dim_change is not None:\n            res = self.dim_change(res)\n        \n        output += res\n        output = F.relu(output)\n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ResNet(torch.nn.Module):\n    def __init__(self,block,num_layers,classes=1103):\n        super(ResNet,self).__init__()\n        #according to research paper:\n        self.input_planes = 64\n        self.conv1 = torch.nn.Conv2d(3,64,kernel_size=3,stride=1,padding=1)\n        self.bn1   = torch.nn.BatchNorm2d(64)\n        self.layer1 = self._layer(block,64,num_layers[0],stride=1)\n        self.layer2 = self._layer(block,128,num_layers[1],stride=2)\n        self.layer3 = self._layer(block,256,num_layers[2],stride=2)\n        self.layer4 = self._layer(block,512,num_layers[3],stride=2)\n        self.averagePool = torch.nn.AvgPool2d(kernel_size=4,stride=1)\n        self.fc    =  torch.nn.Linear(512*block.expansion,classes)\n    \n    def _layer(self,block,planes,num_layers,stride=1):\n        dim_change = None\n        if stride!=1 or planes != self.input_planes*block.expansion:\n            dim_change = torch.nn.Sequential(torch.nn.Conv2d(self.input_planes,planes*block.expansion,kernel_size=1,stride=stride),\n                                             torch.nn.BatchNorm2d(planes*block.expansion))\n        netLayers =[]\n        netLayers.append(block(self.input_planes,planes,stride=stride,dim_change=dim_change))\n        self.input_planes = planes * block.expansion\n        for i in range(1,num_layers):\n            netLayers.append(block(self.input_planes,planes))\n            self.input_planes = planes * block.expansion\n        \n        return torch.nn.Sequential(*netLayers)\n\n    def forward(self,x):\n        x = F.relu(self.bn1(self.conv1(x)))\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n\n        x = F.avg_pool2d(x,4)\n        x = x.view(x.size(0),-1)\n        x = self.fc(x)\n\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NeuralNet  =  ResNet(bottleNeck,[3,4,6,3])\nNeuralNet.to(device)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NeuralNet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"total_params = sum(p.numel() for p in NeuralNet.parameters())\nprint(f'{total_params:,} total parameters.')\ntotal_trainable_params = sum(p.numel() for p in NeuralNet.parameters() if p.requires_grad)\nprint(f'{total_trainable_params:,} training parameters.')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"TRAINING\")\nprint(\"training examples: \",len(train_dataset))\nprint(\"batch size: \",BATCH_SIZE)\nprint(\"batches available: \",len(train_loader))\nprint()\nprint(\"TESTING\")\nprint(\"validation examples: \",len(val_dataset))\nprint(\"batch size: \",BATCH_SIZE)\nprint(\"batches available: \",len(val_loader))\nprint()\nprint(\"VALIDATION\")\nprint(\"testing examples: \",len(test_dataset))\nprint(\"batch size: \",BATCH_SIZE)\nprint(\"batches available: \",len(test_loader))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NeuralNet = NeuralNet.to(device)\noptimizer = optim.Adam(NeuralNet.parameters(),lr = LEARNING_RATE)\nloss_func = torch.nn.BCEWithLogitsLoss()\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,patience = 2)\nbest_loss = np.inf\nfor epoch in range(NUM_EPOCHS):\n    for phase in ['train', 'val']:\n        start_time = time.time()\n        if phase == 'train':\n            NeuralNet.train()\n        else:\n            NeuralNet.eval()\n            \n        running_loss = 0.0\n        for images_batch, labels_batch in tqdm(dataloaders_dict[phase],disable = DISABLE_TQDM):\n            images_batch = images_batch.to(device)\n            labels_batch = labels_batch.to(device)\n            \n            optimizer.zero_grad()\n            \n            with torch.set_grad_enabled(phase == 'train'):\n                pred_batch = NeuralNet(images_batch)\n                loss = loss_func(pred_batch,labels_batch)\n                \n            if phase == 'train':\n                loss.backward()\n                optimizer.step()\n                \n            running_loss += loss.item() * images_batch.size(0)    \n        epoch_loss = running_loss / len(dataloaders_dict[phase].dataset)            \n\n        if phase == 'val' and epoch_loss < best_loss:            \n            print(\"model val_loss Improved from {:.8f} to {:.8f}\".format(best_loss,epoch_loss))\n            best_loss = epoch_loss\n            best_model_wts = copy.deepcopy(NeuralNet.state_dict())\n        \n        if phase == 'val':\n            scheduler.step(epoch_loss)\n        \n        elapsed_time = time.time()-start_time\n        print(\"Phase: {} | Epoch: {}/{} | {}_loss:{:.8f} | Time: {:.4f}s\".format(phase,\n                                                                              epoch+1,\n                                                                              NUM_EPOCHS,\n                                                                              phase,\n                                                                              epoch_loss,\n                                                                              elapsed_time))\nNeuralNet.load_state_dict(best_model_wts)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NeuralNet.eval()\npredictions = np.zeros((len(test_dataset), output_dim))\ni = 0\nfor test_batch in tqdm(test_loader,disable = DISABLE_TQDM):\n    test_batch = test_batch.to(device)\n    batch_prediction = NeuralNet(test_batch).detach().cpu().numpy()\n    predictions[i * BATCH_SIZE:(i+1) * BATCH_SIZE, :] = batch_prediction\n    i+=1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_class_idx = []\nfor i in range(len(predictions)):         \n    idx_list = np.where(predictions[i] > np.percentile(predictions[i],PERCENTILE))    \n    predicted_class_idx.append(idx_list[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df['attribute_ids'] = predicted_class_idx\ntest_df['attribute_ids'] = test_df['attribute_ids'].apply(lambda x : ' '.join(map(str,list(x))))\ntest_df = test_df.rename(columns={0: 'id'})\ntest_df['id'] = test_df['id'].apply(lambda x : x.split('.')[0])\ntest_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.to_csv('\"../input/imet-2019-fgvc6/submission.csv',index = False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.3"}},"nbformat":4,"nbformat_minor":1}