{"cells":[{"metadata":{},"cell_type":"markdown","source":"Dog or Cat classifier with Pytorch finetuning.\n\nI use vgg16 provided from Pytorch library.\n\nFor this time analysis, I prepare valuate data picked up from train.zip. It's for validation to avoid over learning. Valuate data is not used to train.\n","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# import packages\nimport glob\nimport os.path as osp\nimport os\nimport random\nimport numpy as np\nimport json\nfrom PIL import Image\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.utils.data as data\nimport torchvision\nfrom torchvision import models, transforms\n\nimport re\nimport csv\n\ntorch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Setting random number seed.\ntorch.manual_seed(1234)\nnp.random.seed(1234)\nrandom.seed(1234)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Preprocess for images.\n# When \"training\", it has data augumentaion.\n\nclass ImageTransform():\n    \"\"\"\n    画像の前処理クラス。訓練時、検証時で異なる動作をする。\n    画像のサイズをリサイズし、色を標準化する。\n\n    Attributes\n    ----------\n    resize : int\n        resize of image (image files have different size.)\n    mean : (R, G, B)\n        standardization colors\n    std : (R, G, B)\n        standardization colors\n    \"\"\"\n\n    def __init__(self, resize, mean, std):\n        self.data_transform = {\n            'train': transforms.Compose([\n                transforms.RandomResizedCrop(\n                   resize, scale=(0.5, 1.0)),  # data augumentation\n                transforms.RandomHorizontalFlip(),  # data augumentation\n                transforms.ToTensor(),  # to Tensor\n                transforms.Normalize(mean, std)  # standerdization\n            ]),\n            'val': transforms.Compose([\n                # transforms.Resize(resize),  # resize\n                transforms.CenterCrop(resize),  # picking up the center of the image resize×resize\n                transforms.ToTensor(),  # to Tensor\n                transforms.Normalize(mean, std)  # standardization\n            ])\n        }\n\n    def __call__(self, img, phase='train'):\n        \"\"\"\n        Parameters\n        ----------\n        phase : 'train' or 'val'\n            setting mode\n        \"\"\"\n        return self.data_transform[phase](img)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# check the preprocess\n\n# 1. open image\nimage_file_path = '../input/training/train/cat.10005.jpg'\n\nimg_originalsize = Image.open(image_file_path)   # [height][width][RGB]\nimg = img_originalsize.resize((256, 256))\n\n# 2. show original image\nplt.imshow(img)\nplt.show()\n\n# 3. show image after preprocess\nsize = 256\nmean = (0.485, 0.456, 0.406)\nstd = (0.229, 0.224, 0.225)\n\ntransform = ImageTransform(size, mean, std)\nimg_transformed = transform(img, phase=\"train\")  # torch.Size([3, 224, 224])\n\n\nimg_transformed = img_transformed.numpy().transpose((1,2,0))\nplt.imshow(img_transformed)\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# making path list\n\ndef make_datapath_list(phase=\"train\"):\n    \"\"\"\n    \n    Parameters\n    ----------\n    phase : 'train' or 'val'\n        setting mode\n\n    Returns\n    -------\n    path_list : list\n        \n    \"\"\"\n\n    rootpath = \"../input/\"\n    \n    if phase == 'train':\n        target_path = osp.join(rootpath+'training/train/*.jpg')\n    else :\n        target_path = osp.join(rootpath+'valuate/val/*.jpg')\n        \n    path_list = []\n\n    # getting file path\n    for path in glob.glob(target_path):\n        path_list.append(path)\n\n    return path_list\n\n\n# run\ntrain_list = make_datapath_list(phase=\"train\")\nval_list = make_datapath_list(phase=\"val\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# making data set successing Pytorch Dataset class\n\nclass dogsAndCatsDataset(data.Dataset):\n    \n    def __init__(self, file_list, transform=None, phase='train'):\n        self.file_list = file_list\n        self.transform = transform\n        self.phase = phase  # setting mode \"train\" or \"test1\"\n\n    def __len__(self):\n        \n        return len(self.file_list)\n\n    def __getitem__(self, index):\n        \n        # loading image for each index\n        img_path = self.file_list[index]\n        \n        # open image file\n        img_originalsize = Image.open(img_path)   # [height][width][RGB]\n        img = img_originalsize.resize((256, 256))\n        \n        # preprocess of image\n        img_transformed = self.transform(\n            img, self.phase)  # torch.Size([3, 256, 256])\n        \n        # pick up label string from file name\n        if self.phase == \"train\":\n            label = img_path[24:27]\n            \n        elif self.phase == \"val\":\n            label = img_path[21:24]\n            \n        # Convert label string -> number\n        if label == \"cat\":\n            label = 0\n        \n        elif label == \"dog\":\n            label = 1\n\n        return img_transformed, label\n\n\n# making dataset\ntrain_dataset = dogsAndCatsDataset(\n    file_list=train_list, transform=ImageTransform(size, mean, std), phase='train')\n\nval_dataset = dogsAndCatsDataset(\n    file_list=val_list, transform=ImageTransform(size, mean, std), phase='val')\n\n# check the motion\n# index = 0\n# print(train_dataset.__getitem__(index)[0].size())\n# print(train_dataset.__getitem__(index)[1])\n\n# print(val_dataset.__getitem__(index)[0].size())\n# print(val_dataset.__getitem__(index)[1])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# setting butch size\nbatch_size = 32\n\n# making data loader\ntrain_dataloader = torch.utils.data.DataLoader(\n    train_dataset, batch_size=batch_size, shuffle=True)\n\nval_dataloader = torch.utils.data.DataLoader(\n    val_dataset, batch_size=batch_size, shuffle=False)\n\n# into dictionary type\ndataloaders_dict = {\"train\": train_dataloader, \"val\": val_dataloader}\n\n# check the motion\n# batch_iterator = iter(dataloaders_dict[\"train\"])  # convert it to iterator\n\n# inputs, labels = next(\n#     batch_iterator)  # 1番目の要素を取り出す\n# print(inputs.size())\n# print(labels)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# loading vgg16 pretrained model\nuse_pretrained = True\nnet = models.vgg16(pretrained=use_pretrained)\n\n# convert output layer for 2 class classifier\nnet.classifier[6] = nn.Linear(in_features=4096, out_features=2)\n\n# setting train mode\nnet.train()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# setting loss function\ncriterion = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# setting fine tuning parameters\nparams_to_update_1 = []\nparams_to_update_2 = []\nparams_to_update_3 = []\n\nupdate_param_names_1 = [\"features\"]\nupdate_param_names_2 = [\"classifier.0.weight\",\n                        \"classifier.0.bias\", \"classifier.3.weight\", \"classifier.3.bias\"]\nupdate_param_names_3 = [\"classifier.6.weight\", \"classifier.6.bias\"]\n\nfor name, param in net.named_parameters():\n    if update_param_names_1[0] in name:\n        param.requires_grad = True\n        params_to_update_1.append(param)\n        #print(\"params_to_update_1に格納：\", name)\n\n    elif name in update_param_names_2:\n        param.requires_grad = True\n        params_to_update_2.append(param)\n        #print(\"params_to_update_2に格納：\", name)\n\n    elif name in update_param_names_3:\n        param.requires_grad = True\n        params_to_update_3.append(param)\n        #print(\"params_to_update_3に格納：\", name)\n\n    else:\n        param.requires_grad = False\n        #print(\"勾配計算なし。学習しない：\", name)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# I use SDG as optimizer.\noptimizer = optim.SGD([\n    {'params': params_to_update_1, 'lr': 1e-4},\n    {'params': params_to_update_2, 'lr': 5e-4},\n    {'params': params_to_update_3, 'lr': 1e-3}\n], momentum=0.9)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# training function\ndef train_model(net, dataloaders_dict, criterion, optimizer, num_epochs):\n    \n    train_accuracy_list = []\n    train_loss_list = []\n    \n    valuate_accuracy_list = []\n    valuate_loss_list = []\n    \n    # setting GPU\n    device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n    #print(\"使用デバイス：\", device)\n\n    # network into GPU\n    net.to(device)\n    torch.backends.cudnn.benchmark = True\n\n    # epoch loop\n    for epoch in range(num_epochs):\n        print('Epoch {}/{}'.format(epoch+1, num_epochs))\n        print('-------------')\n\n        # training and validation\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                net.train()\n            else:\n                net.eval()\n\n            epoch_loss = 0.0\n            epoch_corrects = 0\n\n            # check accuracy before training\n            if (epoch == 0) and (phase == 'train'):\n                continue\n            \n                      \n            # butch loop\n            for inputs, labels in tqdm(dataloaders_dict[phase]):\n                   \n                # send data GPU\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n                \n                # initialize the optimizer\n                optimizer.zero_grad()\n\n                # forward propagation\n                with torch.set_grad_enabled(phase == 'train'):\n                    \n                    outputs = net(inputs)\n                                        \n                    loss = criterion(outputs, labels) # output loss\n                    _, preds = torch.max(outputs, 1)  # predict class\n                    \n                    # back propagation\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                    # sum of loss\n                    epoch_loss += loss.item() * inputs.size(0)  \n                    # sum of correct prediction\n                    epoch_corrects += torch.sum(preds == labels.data)\n            \n            # loss and accuracy for each epoch loop\n            epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset)\n            epoch_acc = epoch_corrects.double(\n                ) / len(dataloaders_dict[phase].dataset)\n            \n            print('{} Loss: {:.4f} Acc: {:.4f}'.format(\n                phase, epoch_loss, epoch_acc))\n            \n            if phase == 'val':\n                valuate_accuracy_list.append(epoch_acc.item())\n                valuate_loss_list.append(epoch_loss)\n            else:\n                train_accuracy_list.append(epoch_acc.item())\n                train_loss_list.append(epoch_loss)\n        \n    return train_accuracy_list, train_loss_list, valuate_accuracy_list, valuate_loss_list","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# training 10 epochs (maybe too many...)\nnum_epochs=10\ntrain_accuracy_list, train_loss_list, valuate_accuracy_list, valuate_loss_list = train_model(net, dataloaders_dict, criterion, optimizer, num_epochs=num_epochs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot training results\n\nepoch_num = list(range(num_epochs-1))\nfig, ax = plt.subplots(facecolor=\"w\")\nax.plot(epoch_num, train_accuracy_list, label=\"train\")\nax.plot(epoch_num, valuate_accuracy_list[1:], label=\"valuate\")\n\nplt.xticks(epoch_num) \n\nax.legend()\nfig = plt.title(\"accuracy\")\n\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nfig, ax = plt.subplots(facecolor=\"w\")\n\nax.plot(epoch_num, train_loss_list, label=\"train\")\nax.plot(epoch_num, valuate_loss_list[1:], label=\"valuate\")\n\nplt.xticks(epoch_num) \n\nax.legend()\nfig = plt.title(\"loss\")\n\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# if save the model\n# save_path = 'weights.pth'\n# torch.save(net.state_dict(), save_path)\n\n\n# start inferrence\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\nnet.to(device)\nnet.eval()\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# input test1 data\ndef make_test1_datapath_list():\n    rootpath = \"../input/testing1/\"\n    \n    target_path = osp.join(rootpath+'test1/*.jpg')\n    print(target_path)\n\n    path_list = []\n\n    # getting path\n    for path in glob.glob(target_path):\n        path_list.append(path)\n\n    return path_list\n\n# run\ntest1_list = make_test1_datapath_list()\n\nids = []\npredictions = []\nsize = 256\nmean = (0.485, 0.456, 0.406)\nstd = (0.229, 0.224, 0.225)\n\nfor path in test1_list:\n    img_originalsize = Image.open(path)   # [height][width][RGB]\n    img = img_originalsize.resize((256, 256))\n    \n    transform = ImageTransform(size, mean, std)\n    img_transformed = transform(img, phase=\"val\")  # torch.Size([3, 256, 256])\n    \n    img_for_net = img_transformed.unsqueeze(0)\n    # into GPU\n    img_for_net = img_for_net.to(device)\n    outputs = net(img_for_net)\n    \n    # predict class\n    _, preds = torch.max(outputs, 1)\n    \n    # print(re.split('[./]',path))\n    splitted = re.split('[./]',path)\n    test1_id = splitted[-2]\n    \n    ids.append(test1_id)\n    predictions.append(preds.item())\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# make submit csv\nsubmitFormat = [ids, predictions]\nsubmitFormat_t = np.array(submitFormat).T\n\nprint(submitFormat_t)\n\nwith open('submit.csv', 'w') as f:\n    writer = csv.writer(f)\n    \n    fieldnames = ['id', 'label']\n    #writer = csv.DictWriter(f, fieldnames=fieldnames)\n    writer.writerow(fieldnames)\n    writer.writerows(submitFormat_t)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"","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":4}