{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport os\nfrom sklearn.model_selection import train_test_split\nimport seaborn as sns\nimport cv2\nimport torch.hub\nimport torchvision\nimport torch.nn as nn\nfrom torch.utils.data import (\n    Dataset,\n    DataLoader,\n)  \nfrom skimage import io\nfrom torchvision.transforms import transforms\nfrom torch import optim\nfrom tqdm import tqdm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-02-16T15:14:40.479233Z","iopub.execute_input":"2022-02-16T15:14:40.479509Z","iopub.status.idle":"2022-02-16T15:14:43.727413Z","shell.execute_reply.started":"2022-02-16T15:14:40.479420Z","shell.execute_reply":"2022-02-16T15:14:43.726639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"TODO\n1. ~Number of images in all folders~\n2. ~analyse the train.csv~\n3. ~Split the data into training and test set ( Try with different methods of splitting )~\n4. Analyse the data ( somewhat dones)\n5. ~make a model for analyzing the species~\n6. Add better performance metric in the model","metadata":{}},{"cell_type":"markdown","source":"I am using the resized happywhale dataset, courtesy of @RDizzl3, \nhttps://www.kaggle.com/rdizzl3/jpeg-happywhale-128x128","metadata":{}},{"cell_type":"code","source":"train_images_path = '../input/jpeg-happywhale-128x128/train_images-128-128/train_images-128-128'\ntest_images_path = '../input/jpeg-happywhale-128x128/test_images-128-128/test_images-128-128'\ntrain_df_path = '../input/happy-whale-and-dolphin/train.csv'","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:43.729177Z","iopub.execute_input":"2022-02-16T15:14:43.729644Z","iopub.status.idle":"2022-02-16T15:14:43.739860Z","shell.execute_reply.started":"2022-02-16T15:14:43.729608Z","shell.execute_reply":"2022-02-16T15:14:43.732436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Number of images in folders","metadata":{}},{"cell_type":"code","source":"print(f'Number of training images - {len(os.listdir(train_images_path))}')\nprint(f'Number of test images - {len(os.listdir(test_images_path))}')","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:43.743230Z","iopub.execute_input":"2022-02-16T15:14:43.743764Z","iopub.status.idle":"2022-02-16T15:14:45.548066Z","shell.execute_reply.started":"2022-02-16T15:14:43.743725Z","shell.execute_reply":"2022-02-16T15:14:45.546613Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Analysing the train df","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(train_df_path)","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.550064Z","iopub.execute_input":"2022-02-16T15:14:45.550829Z","iopub.status.idle":"2022-02-16T15:14:45.637313Z","shell.execute_reply.started":"2022-02-16T15:14:45.550791Z","shell.execute_reply":"2022-02-16T15:14:45.636587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.638652Z","iopub.execute_input":"2022-02-16T15:14:45.638926Z","iopub.status.idle":"2022-02-16T15:14:45.655417Z","shell.execute_reply.started":"2022-02-16T15:14:45.638891Z","shell.execute_reply":"2022-02-16T15:14:45.654620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.info()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.656588Z","iopub.execute_input":"2022-02-16T15:14:45.657037Z","iopub.status.idle":"2022-02-16T15:14:45.689461Z","shell.execute_reply.started":"2022-02-16T15:14:45.656997Z","shell.execute_reply":"2022-02-16T15:14:45.688410Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are no null values, thats a relief","metadata":{}},{"cell_type":"markdown","source":"I just read that there are duplicates in the names, lets check that ourselves!","metadata":{}},{"cell_type":"code","source":"sorted(df['species'].unique())","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.690653Z","iopub.execute_input":"2022-02-16T15:14:45.690979Z","iopub.status.idle":"2022-02-16T15:14:45.703006Z","shell.execute_reply.started":"2022-02-16T15:14:45.690938Z","shell.execute_reply":"2022-02-16T15:14:45.702146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\nThe following are similar\n1. bottlenose_dolphin and bottlenose_dolpin\n2. kiler_whale killer_whale\n3. globis and pilot_whale are same as short_finned_pilot_whale\n                          \n\n*(commersons and common dolphin are different)*","metadata":{}},{"cell_type":"code","source":"# Courtesy of Aleksey Alekseev\ndf.species.replace({\"globis\": \"short_finned_pilot_whale\",\n                          \"pilot_whale\": \"short_finned_pilot_whale\",\n                          \"kiler_whale\": \"killer_whale\",\n                          \"bottlenose_dolpin\": \"bottlenose_dolphin\"}, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.703899Z","iopub.execute_input":"2022-02-16T15:14:45.704086Z","iopub.status.idle":"2022-02-16T15:14:45.724121Z","shell.execute_reply.started":"2022-02-16T15:14:45.704064Z","shell.execute_reply":"2022-02-16T15:14:45.723332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'Ratio of unique images - {len(df.image.unique())/df.shape[0]}')\nprint(f'Number of unique species - {len(df.species.unique())}')\nprint(f'Number of unique individual_id - {len(df.individual_id.unique())/df.shape[0]}')","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.725407Z","iopub.execute_input":"2022-02-16T15:14:45.726131Z","iopub.status.idle":"2022-02-16T15:14:45.748463Z","shell.execute_reply.started":"2022-02-16T15:14:45.726094Z","shell.execute_reply":"2022-02-16T15:14:45.747832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"So it looks like\n1. we have unique images \n2. there are some images with same individual id\n3. There are 26 unique species","metadata":{}},{"cell_type":"code","source":"df['individual_id'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.751018Z","iopub.execute_input":"2022-02-16T15:14:45.751240Z","iopub.status.idle":"2022-02-16T15:14:45.771531Z","shell.execute_reply.started":"2022-02-16T15:14:45.751215Z","shell.execute_reply":"2022-02-16T15:14:45.770725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# converting species to unique integers\nmapping = {item:i for i, item in enumerate(df[\"species\"].unique())}\ndf[\"species_idx\"] = df[\"species\"].apply(lambda x: mapping[x])","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.772876Z","iopub.execute_input":"2022-02-16T15:14:45.773187Z","iopub.status.idle":"2022-02-16T15:14:45.809525Z","shell.execute_reply.started":"2022-02-16T15:14:45.773153Z","shell.execute_reply":"2022-02-16T15:14:45.808872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.810605Z","iopub.execute_input":"2022-02-16T15:14:45.810922Z","iopub.status.idle":"2022-02-16T15:14:45.821616Z","shell.execute_reply.started":"2022-02-16T15:14:45.810887Z","shell.execute_reply":"2022-02-16T15:14:45.820698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Before going further, let us try to split our data into training and test set","metadata":{}},{"cell_type":"code","source":"def split_data(df,method = 'random_split',split_ratio = .5,verbose = False):\n    \n    \n    \n    if(method == 'random_split'):\n        df_train,df_test,_,_ = train_test_split(df,df,test_size = split_ratio,random_state = 5)\n    \n    if(verbose):\n        print(f'df_train : {df_train.shape}')\n        print(f'df_test : {df_test.shape}')\n    df_train.reset_index(inplace = True,drop = True)\n    df_test.reset_index(inplace = True,drop = True)\n    return  df_train,df_test\n        \ndf_train,df_test = split_data(df,'random_split',.3,True)","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.823255Z","iopub.execute_input":"2022-02-16T15:14:45.823948Z","iopub.status.idle":"2022-02-16T15:14:45.848509Z","shell.execute_reply.started":"2022-02-16T15:14:45.823911Z","shell.execute_reply":"2022-02-16T15:14:45.847640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Analysis","metadata":{}},{"cell_type":"code","source":"print(\"Percent Distribution of species\")\n100*(df_train.species.value_counts()/df_train.shape[0])","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.849981Z","iopub.execute_input":"2022-02-16T15:14:45.850234Z","iopub.status.idle":"2022-02-16T15:14:45.862514Z","shell.execute_reply.started":"2022-02-16T15:14:45.850201Z","shell.execute_reply":"2022-02-16T15:14:45.861629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sns.set(rc={'figure.figsize':(11.7,8.27)})\nsns.histplot(data = df_train,y = 'species')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:45.864103Z","iopub.execute_input":"2022-02-16T15:14:45.864582Z","iopub.status.idle":"2022-02-16T15:14:46.295816Z","shell.execute_reply.started":"2022-02-16T15:14:45.864546Z","shell.execute_reply":"2022-02-16T15:14:46.295129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"from what i infer, we already have a bunch of dolphins/whales by their ids, there are multiple images of these having the same id, basically each animal has some photos taken at different instances of time.\n\nThe test set also containes these animals, some are new however and we cannot assign an id to them.\n","metadata":{}},{"cell_type":"code","source":"def draw_image(path):\n    img = cv2.imread(path)\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    plt.axis('off')\n    plt.imshow(img)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:46.297023Z","iopub.execute_input":"2022-02-16T15:14:46.297383Z","iopub.status.idle":"2022-02-16T15:14:46.304314Z","shell.execute_reply.started":"2022-02-16T15:14:46.297344Z","shell.execute_reply":"2022-02-16T15:14:46.303456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for file_names in df_train.groupby(by='individual_id')['image'].apply(list):\n    if(len(file_names)>=3):\n        for file_name in file_names:\n            draw_image(os.path.join(train_images_path,file_name))\n        break","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:46.305699Z","iopub.execute_input":"2022-02-16T15:14:46.306296Z","iopub.status.idle":"2022-02-16T15:14:47.701623Z","shell.execute_reply.started":"2022-02-16T15:14:46.306257Z","shell.execute_reply":"2022-02-16T15:14:47.700976Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model To Classify the species","metadata":{}},{"cell_type":"code","source":"# Making Dataset class for loader\nclass WhaleDataset(Dataset):\n    def __init__(self,df,root_dir,transforms = None):\n        self.df = df\n        self.root_dir = root_dir\n        self.transforms = transforms\n    \n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, index):\n        img_path = os.path.join(self.root_dir,self.df.image[index])\n        img = io.imread(img_path)\n        \n        y_label = torch.tensor(int(self.df.species_idx[index]))\n        \n        if self.transforms:\n            for transform in self.transforms:\n                img = transform(img)\n        \n        return (img,y_label)\n        ","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:47.702855Z","iopub.execute_input":"2022-02-16T15:14:47.703310Z","iopub.status.idle":"2022-02-16T15:14:47.710992Z","shell.execute_reply.started":"2022-02-16T15:14:47.703269Z","shell.execute_reply":"2022-02-16T15:14:47.710182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = WhaleDataset(df = df_train,\n                             root_dir = '../input/jpeg-happywhale-128x128/train_images-128-128/train_images-128-128',\n                            transforms = [transforms.ToTensor()])\ntest_dataset = WhaleDataset(df = df_test,\n                             root_dir = '../input/jpeg-happywhale-128x128/train_images-128-128/train_images-128-128',\n                            transforms = [transforms.ToTensor()])","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:14:47.712528Z","iopub.execute_input":"2022-02-16T15:14:47.712797Z","iopub.status.idle":"2022-02-16T15:14:47.721186Z","shell.execute_reply.started":"2022-02-16T15:14:47.712763Z","shell.execute_reply":"2022-02-16T15:14:47.720492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check accuracy on training & test to see how good our model\nfrom sklearn.metrics import f1_score\n\ndef check_accuracy(loader, model):\n    num_correct = 0\n    num_samples = 0\n    model.eval()\n\n    with torch.no_grad():\n        for x, y in loader:\n            x = x.to(device=device)\n            y = y.to(device=device)\n            scores = model(x)\n            _, predictions = scores.max(1)\n            num_correct += (predictions == y).sum()\n            num_samples += predictions.size(0)\n\n\n    model.train()\n    return num_correct/num_samples\n\n\ndef check_f1_score(loader, model):\n    num_correct = 0\n    num_samples = 0\n    model.eval()\n    y_true = np.array([])\n    y_pred = np.array([])\n    with torch.no_grad():\n        for x, y in loader:\n            x = x.to(device=device)\n            y = y.to(device=device)\n            scores = model(x)\n            _, predictions = scores.max(1)\n            predictions = predictions.cpu().detach().numpy()\n            y = y.cpu().detach().numpy()\n            y_true = np.concatenate((y_true,y))\n            y_pred = np.concatenate((y_pred,predictions))\n        \n    model.train()\n    return f1_score(y_true,y_pred,average = \"weighted\")\n\n","metadata":{"execution":{"iopub.status.busy":"2022-02-16T14:11:45.219925Z","iopub.execute_input":"2022-02-16T14:11:45.220567Z","iopub.status.idle":"2022-02-16T14:11:45.232458Z","shell.execute_reply.started":"2022-02-16T14:11:45.22053Z","shell.execute_reply":"2022-02-16T14:11:45.231624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Hyperparameters\nin_channels = 3\nnum_classes = 26\nlearning_rate = 0.001\nbatch_size = 64\nnum_epochs = 50\n\ntrain_loader = DataLoader(dataset = train_dataset,batch_size = batch_size,shuffle = True)\ntest_loader = DataLoader(dataset = test_dataset,batch_size = batch_size,shuffle = True)\n\n# A model for predicting the speccies\nmodel = torchvision.models.vgg16(pretrained = True)\n\n\n\nfor (idx,param) in enumerate(model.parameters()):\n    if(idx<25):\n        param.requires_grad = False\n\nclass Identity(nn.Module):\n    def __init__(self):\n        super(Identity,self).__init__()\n    def forward(self,x):\n        return x;\n    \nmodel.avgpool = Identity()\nmodel.classifier = nn.Sequential(nn.Linear(8192,512),\n                                 nn.ReLU(),\n                                 nn.Linear(512,256),\n                                 nn.ReLU(),\n                                 nn.Linear(256,26))\n\nmodel.to(device)\n\ntrain_f1_score = []\ntest_f1_score = []\n\n\n\n# Loss and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=learning_rate)\n\n# Train Network\nfor epoch in range(num_epochs):\n    for batch_idx, (data, targets) in enumerate(tqdm(train_loader)):\n        # Get data to cuda if possible\n        data = data.to(device=device)\n        targets = targets.to(device=device)\n\n        # forward\n        scores = model(data)\n        loss = criterion(scores, targets)\n\n        # backward\n        optimizer.zero_grad()\n        loss.backward()\n\n        # gradient descent or adam step\n        optimizer.step()\n        \n    if(epoch%2 == 0):\n        train_f1_score.append(check_f1_score(train_loader, model))\n        test_f1_score.append(check_f1_score(test_loader, model))\n        if(len(test_f1_score) == 1):\n            torch.save(model, 'best-model.pt')\n            torch.save(model.state_dict(), 'best-model-parameters.pt')\n        elif test_f1_score[-1]>max(test_f1_score[0:-1]):\n            torch.save(model, 'best-model.pt')\n            torch.save(model.state_dict(), 'best-model-parameters.pt')","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:19:52.176767Z","iopub.execute_input":"2022-02-16T15:19:52.177218Z","iopub.status.idle":"2022-02-16T15:20:10.455148Z","shell.execute_reply.started":"2022-02-16T15:19:52.177181Z","shell.execute_reply":"2022-02-16T15:20:10.453710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(train_f1_score,c = 'b')\nplt.plot(test_f1_score,c = 'r')\nplt.legend([\"Train f1 score\",\"Test f1 score\"])\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2022-02-16T15:20:10.456281Z","iopub.status.idle":"2022-02-16T15:20:10.456858Z","shell.execute_reply.started":"2022-02-16T15:20:10.456615Z","shell.execute_reply":"2022-02-16T15:20:10.456640Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_f1_score","metadata":{"execution":{"iopub.status.busy":"2022-02-16T14:23:20.096071Z","iopub.execute_input":"2022-02-16T14:23:20.096603Z","iopub.status.idle":"2022-02-16T14:23:20.102314Z","shell.execute_reply.started":"2022-02-16T14:23:20.096564Z","shell.execute_reply":"2022-02-16T14:23:20.101297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}