{"cells":[{"metadata":{"id":"xEt9p-2mbKRM"},"cell_type":"markdown","source":"# Dog vs Cat Classification"},{"metadata":{"id":"ieXTbN0peinz"},"cell_type":"markdown","source":"### Extract file from zip"},{"metadata":{"id":"Wl5eqbM9bOUQ","outputId":"a9956b5b-2658-4c64-fea8-02a6e3569593","trusted":true},"cell_type":"code","source":"# Extract all the file from zip to folder\nimport zipfile\n\nlocal_zip = '../input/dogs-vs-cats-redux-kernels-edition/train.zip'\nzip_ref = zipfile.ZipFile(local_zip,'r')\nzip_ref.extractall('./')\n\nlocal_zip = '../input/dogs-vs-cats-redux-kernels-edition/test.zip'\nzip_ref = zipfile.ZipFile(local_zip,'r')\nzip_ref.extractall('./')","execution_count":null,"outputs":[]},{"metadata":{"id":"GZw6Nm5db1Ih","trusted":true},"cell_type":"code","source":"import os\n\ntest_dir = './test'\ntrain_dir = './train'\n\ntrain_list = os.listdir(train_dir)\ntest_list = os.listdir(test_dir)\nprint('Num of images in training set:',len(train_list))\nprint('Num of images in test set:',len(test_list))","execution_count":null,"outputs":[]},{"metadata":{"id":"uMiWltzNenkp"},"cell_type":"markdown","source":"### DataFrame preparation"},{"metadata":{"id":"uPsBKLT5eFYr","trusted":true},"cell_type":"code","source":"#import all the tools we need\n\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mi\nimport PIL.Image as Image\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"id":"d3-ypBYIeGpO","outputId":"06f43640-21b0-47e3-f6b6-91b5b430e446","trusted":true},"cell_type":"code","source":"train_df = pd.DataFrame(train_list,columns=['path'])\ntrain_df['label'] = train_df.path.str[:3]\ntrain_df.label =train_df.label.map({'cat':0,'dog':1})\ntrain_df.path = train_df.path.apply(lambda x : os.path.join(train_dir,x))\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"ekYJ2YcmfQJs","outputId":"9e64166e-481f-454c-f34d-bb5fba6a9aad","trusted":true},"cell_type":"code","source":"test_df = pd.DataFrame(test_list, columns=['path'])\ntest_df.path = test_df.path.apply(lambda x: os.path.join(test_dir,x))\ntest_df.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"h8UmAhD2hdVE"},"cell_type":"markdown","source":"# Have a look on the files"},{"metadata":{"id":"5kXLvYTofwUT","outputId":"65ad39c0-2203-4bcc-9162-ebd7efcd0ca2","trusted":true},"cell_type":"code","source":"def show_6_photos(dataframe):\n  sample_df = dataframe.sample(6)\n  paths = sample_df.path.tolist()\n  for path in paths:\n    img = mi.imread(path)\n    plt.subplots(figsize=(3,3))\n    plt.imshow(img)\n    plt.show()\n  \n\nshow_6_photos(train_df)","execution_count":null,"outputs":[]},{"metadata":{"id":"2x_20NSxj_HY"},"cell_type":"markdown","source":"# Build Dataset with custom class"},{"metadata":{},"cell_type":"markdown","source":"Split the training dataset into two sets: one for training, one for validation."},{"metadata":{"id":"Ei2oFKDbtpvk","trusted":true},"cell_type":"code","source":"x_train,x_val,y_train,y_val = train_test_split(train_df.drop('label',axis=1),train_df[['label']], test_size=0.3)\nxy_train = pd.concat([x_train,y_train],axis=1)\nxy_val = pd.concat([x_val,y_val],axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":" Create the custom dataset class"},{"metadata":{"id":"EJEPiNWSg7AZ","trusted":true},"cell_type":"code","source":"class img_dataset(Dataset):\n  def __init__(self,dataframe,test=False,transform=None):\n    self.data = dataframe\n    self.transform = transform\n    self.test = test\n  def __getitem__(self,idx):\n    x= Image.open(self.data.iloc[idx,0])\n    if self.transform:\n        x = self.transform(x)\n    if self.test == False:\n      y = self.data.iloc[idx,1]\n      return (x,y)\n    else:\n      return x\n  def __len__(self):\n    return self.data.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"id":"Q0sQWjRVhrgo","trusted":true},"cell_type":"code","source":"# Build the transformers, one for training set, one for validation and test set\ntrain_transformer = transforms.Compose([\n            transforms.CenterCrop((300,300)),\n            transforms.Resize((200,200)),\n            transforms.RandomHorizontalFlip(p=0.5),\n            transforms.RandomAffine(20),\n            transforms.RandomRotation(25),\n            transforms.ToTensor()\n])\ntest_transformer = transforms.Compose([\n            transforms.Resize((200,200)),\n            transforms.ToTensor()\n])","execution_count":null,"outputs":[]},{"metadata":{"id":"J0_lXn_xn0lV","trusted":true},"cell_type":"code","source":"# Get dataset, set batch size to 128\ntrain_set = img_dataset(xy_train,test=False,transform=train_transformer)\nval_set = img_dataset(xy_val, test=False, transform=test_transformer)\ntest_set = img_dataset(test_df,test=True,transform=test_transformer)\n\ntrain_loader = DataLoader(train_set,batch_size=128,shuffle=True)\nval_loader = DataLoader(val_set ,batch_size=128)\ntest_loader = DataLoader(test_set ,batch_size=128)","execution_count":null,"outputs":[]},{"metadata":{"id":"t2IALaUftGAu"},"cell_type":"markdown","source":"# Build the model"},{"metadata":{"id":"bSjr0ZWRoDgH","outputId":"fca90cb9-9769-4e16-de7c-a4032feffe66","trusted":true},"cell_type":"code","source":"'''\nThis function is used to count the image size after pooling\n\n'''\n\ndef size_cal(w,k,s,p):\n  answer = ((w-k)+2*p)/s\n  return answer+1\n\ncnn1 = size_cal(200,5,1,0)\nmax1 = size_cal(cnn1,2,2,0)\ncnn2 = size_cal(max1,3,1,0)\nmax2 = size_cal(cnn2,2,2,0)\ncnn3 = size_cal(max2,3,1,0)\nmax3 = size_cal(cnn3,2,2,0)\n\noutput_size = max3\nprint(output_size)","execution_count":null,"outputs":[]},{"metadata":{"id":"WWipF74toE9c","trusted":true},"cell_type":"code","source":"# Build a model with three convolutional layers\nclass binary_clf_model(nn.Module):\n  def __init__(self, h1,h2,h3,p=0):\n    super(binary_clf_model,self).__init__()\n    # first conv layer\n    self.cnn1 = nn.Conv2d(in_channels=3,out_channels=h1,kernel_size=5,padding=0)\n    self.max1 = nn.MaxPool2d(kernel_size=2, stride=2)\n    # second layer\n    self.cnn2 = nn.Conv2d(in_channels=h1,out_channels=h2,kernel_size=3,padding=0)\n    self.max2 = nn.MaxPool2d(kernel_size=2,stride=2)\n    # thrid layer\n    self.cnn3 = nn.Conv2d(in_channels=h2,out_channels=h3,kernel_size=3,padding=0)\n    self.max3 = nn.MaxPool2d(kernel_size=2,stride=2)\n\n    # Fully connect\n    self.fc1 = nn.Linear(h3*23*23,512)\n\n    # Output Dense layer\n    self.fc2 = nn.Linear(512,1)\n\n    # Dropout layer\n    #self.drop=nn.Dropout(p=p)\n\n  def forward(self,x):\n    x = self.cnn1(x)\n    x = torch.relu(x)\n    #x = self.drop(x)\n    x = self.max1(x)\n\n    x = self.cnn2(x)\n    x = torch.relu(x)\n    #x = self.drop(x)\n    x = self.max2(x)\n\n    x = self.cnn3(x)\n    x = torch.relu(x)\n    #x = self.drop(x)\n    x = self.max3(x)\n\n    x = x.view(x.size(0),-1)\n    x = self.fc1(x)\n    x = torch.relu(x)\n    #x = self.drop(x)\n    x = self.fc2(x)\n    x = torch.sigmoid(x)\n    return x","execution_count":null,"outputs":[]},{"metadata":{"id":"Fw_ysAfTyWTb"},"cell_type":"markdown","source":"# Optimizer ,Cost function, Training function and GPU setting"},{"metadata":{"id":"PBrCIFn40S9L","trusted":true},"cell_type":"code","source":"# GPU setting\ntorch.cuda.is_available()\ndevice = torch.device('cuda:0')","execution_count":null,"outputs":[]},{"metadata":{"id":"hOlzrEsx64rN","trusted":true},"cell_type":"code","source":"def accuracy(y_hat,y):\n  y_hat = y_hat.cpu().detach().numpy().ravel().tolist()\n  y_hat = np.array([1 if item >0.5 else 0 for item in y_hat])\n  y = y.cpu().detach().numpy().ravel()\n  return np.mean(y_hat==y)","execution_count":null,"outputs":[]},{"metadata":{"id":"1riHUfN4zDdO","trusted":true},"cell_type":"code","source":"def training(model,train,valid,epochs,cost_function,optimizer):\n  train_cost_list = []\n  val_cost_list = []\n  accuracy_list = []\n  for epoch in range(epochs):\n    train_cost_sublist= []\n    val_cost_sublist = []\n    acc_sublist = []\n    print('Start training {}/{} epoch'.format(epoch+1,epochs))\n\n    # Training\n    for x,y in train:\n      optimizer.zero_grad()\n      y = y.type(torch.FloatTensor)\n      x,y = x.to(device),y.to(device)\n      model.train()\n      y_hat = model(x)\n      loss = cost_function(y_hat[:,0],y)\n      train_cost_sublist.append(loss.item())\n      loss.backward()\n      optimizer.step()\n    train_cost_list.append(np.mean(train_cost_sublist))\n\n    # Evaluation\n    i=0\n    for x_val,y_val in val_loader:\n      model.eval()\n      y_val = y_val.type(torch.FloatTensor)\n      x_val,y_val = x_val.to(device),y_val.to(device)\n      z = model(x_val)\n      val_loss = cost_function(z[:,0],y_val)\n      val_cost_sublist.append(val_loss.item())\n      acc_sublist.append(accuracy(z,y_val))\n\n    val_cost_list.append(np.mean(val_cost_sublist))\n    accuracy_list.append(np.mean(acc_sublist))\n\n    print('Training loss: {:.3f}, Validation loss: {:.3f}, Accuracy: {:.3f}'.format(np.mean(train_cost_sublist),\n                                          np.mean(val_cost_sublist),\n                                          np.mean(acc_sublist)))\n\n  print('Finish training.')\n\n  return (train_cost_list, val_cost_list,accuracy_list)\n\n","execution_count":null,"outputs":[]},{"metadata":{"id":"QtJ37-w886UK","trusted":true},"cell_type":"code","source":"# model\nmodel = binary_clf_model(64,128,128)\nmodel = model.to(device)\n\n# cost function\ncost_function = nn.BCELoss()\n\n# optimizer \noptimizer = torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.9)\n\n# epochs\n\"\"\"\n30 or above are prefered, but due to the processing power and time, I just use 25 here.\n\"\"\"\nepochs = 25","execution_count":null,"outputs":[]},{"metadata":{"id":"NoyIeIITzcFA","outputId":"62af3db2-7049-469c-d8f8-a2177f895dab","trusted":true},"cell_type":"code","source":"# Starting training\ntrain_cost, val_cost, acc = training(model,train_loader,val_loader,epochs,cost_function,optimizer)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the model\ntorch.save(model.state_dict(), './model.pth')","execution_count":null,"outputs":[]},{"metadata":{"id":"CZ4dBz-Xtlkk"},"cell_type":"markdown","source":"# Prediction"},{"metadata":{"id":"idvTwI5K5NSg","outputId":"448cbb6e-7c92-4f2f-ca11-d0c5b8a477c1","trusted":true},"cell_type":"code","source":"def prediction(test_loader,model):\n    prediction = []\n    model.eval()\n    for x_test in test_loader:\n        x_test = x_test.type(torch.FloatTensor).to(device)\n        y_hat = model(x_test)\n        y_hat = y_hat.cpu().detach().numpy().ravel().tolist()\n        prediction+=y_hat\n    return prediction\n\nprediction = prediction(test_loader,model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Look at the result\ni = 0\nprediction_label = [1 if i > 0.5 else 0 for i in prediction]\nfor x in test_set:\n    plt.imshow(np.transpose(x,(1,2,0)))\n    plt.xlabel('Prediction: '+str(prediction_label[i]))\n    plt.show()\n    i+=1\n    if i == 15:\n        break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the prediction to submission file\nsample_submission = pd.read_csv('../input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv')\nsample_submission.label = prediction\nsample_submission.set_index('id',drop=True,inplace=True)\nsample_submission.to_csv('answer01.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Thank you very much"}],"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":4}