{"nbformat_minor": 1, "cells": [{"metadata": {"_cell_guid": "8d5d1032-7585-4d84-bf93-dfe69988431b", "_uuid": "910a7bc60c761f7c79417a97aa90e1c0b7db0e87"}, "cell_type": "markdown", "source": ["# Multiclass object identifcation of dog breeds, Pytorch Edition\n", "\n", "See [Keras version](https://www.kaggle.com/nothxplz/keras-inception-resnet-inception-resnet50) here.\n", "\n", "Assumes following directory structure.\n", "\n", "```bash\n", ".\n", "\u251c\u2500\u2500 data\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 classify_by_dir.sh\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 labels.csv\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 sample_submission.csv\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 sample_submission.csv.zip\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 test\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 test.zip\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 train\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 train.zip\n", "\u2502\u00a0\u00a0 \u251c\u2500\u2500 unsorted\n", "\u2502\u00a0\u00a0 \u2514\u2500\u2500 val\n", "\u251c\u2500\u2500 keras.best.h5\n", "\u251c\u2500\u2500 keras.ipynb\n", "\u2514\u2500\u2500 submit.csv\n", "```\n", "Save the following script in your data directory as `classify_by_dir.sh` then run\n", "\n", "`unzip train.zip && mv train unsorted && ./classify_by_dir.sh`\n", "\n", "```bash\n", "#!/bin/bash\n", "shuf -o labels.csv <labels.csv\n", "unsorted_dir=unsorted\n", "counter=0\n", "# SHUFFLE THE FILE WOOO\n", "while IFS=, read -r image class; do\n", "\t# very roughly 25% to val\n", "\tif (($counter == 4)); then\n", "\t\tsorted_dir=val\n", "\t\tmkdir -p $sorted_dir/$class\n", "\t\tmv $unsorted_dir/$image.jpg $sorted_dir/$class/$image.jpg\n", "\t\tcounter=0\n", "\telse\n", "\t\tsorted_dir=train\n", "\t\tmkdir -p $sorted_dir/$class\n", "\t\tmv $unsorted_dir/$image.jpg $sorted_dir/$class/$image.jpg\n", "\tfi\n", "\t((counter++))\n", "\n", "done <labels.csv\n", "```"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "2800c720-b2a8-471f-954e-6f94c51ac95f", "_uuid": "ebed93956ece7f8103c31a1a5e28c32614cfbd6b", "collapsed": true}, "cell_type": "code", "source": ["from __future__ import print_function\n", "\n", "import os\n", "import os.path\n", "import shutil\n", "import time\n", "\n", "import numpy as np\n", "print(\"np v.{}\".format(np.__version__))\n", "import torch\n", "print(\"pytorch v.{}\".format(torch.__version__))\n", "import torch.backends.cudnn as cudnn\n", "import torch.nn as nn\n", "import torch.nn.parallel\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision.datasets as datasets\n", "import torchvision.models as models\n", "import torchvision.transforms as transforms\n", "from tqdm import tqdm_notebook\n", "\n", "# available models\n", "# ['alexnet', 'densenet', 'densenet121', 'densenet161', 'densenet169', 'densenet201', ' inception', 'inception_v3', 'resnet', 'resnet101', 'resnet152', 'resnet18', 'resnet34',  'resnet50', 'squeezenet', 'squeezenet1_0', 'squeezenet1_1', 'vgg', 'vgg11', 'vgg11_bn', ' vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19', 'vgg19_bn']\n", "# Globals\n", "MODEL = 'resnet50'\n", "RESUME = False\n", "DATA_WORKERS = 4\n", "BATCH_SIZE = 32\n", "LEARNING_RATE = 1e-3\n", "EPOCHS = 30\n", "WEIGHT_DECAY = 0.0005\n", "CLASSES = 120\n", "BEST_ACC = 0"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "21f558e7-860d-4bde-bd44-6bc22c1f386e", "_uuid": "567d3ef6cc2e3fc6f1d98c2f59f63e290c5d8cc8", "collapsed": true}, "cell_type": "code", "source": ["model = models.__dict__[MODEL](pretrained=True)\n", "\n", "for param in model.parameters():\n", "    param.requires_grad = False\n", "\n", "if MODEL is 'resnet50':\n", "    model.fc = nn.Linear(2048, CLASSES)\n", "    \n", "model = torch.nn.DataParallel(model).cuda()\n", "cudnn.benchmark = True\n", "criterion = nn.CrossEntropyLoss().cuda()\n", "optimizer = optim.Adam(model.module.fc.parameters(), lr=LEARNING_RATE, weight_decay=WEIGHT_DECAY)"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "fa5cbdb5-8396-4108-9c05-0ddd0f2bfabb", "_uuid": "d8cba460b0a0d9d12b17e3efc8e8af5d41e2260a", "collapsed": true}, "cell_type": "code", "source": ["data_dir = os.path.join(os.getcwd(),'data')\n", "traindir = os.path.join(data_dir, 'train')\n", "valdir = os.path.join(data_dir, 'val')\n", "testdir = os.path.join(data_dir, 'test')\n", "\n", "normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n", "\n", "train_loader = data.DataLoader(\n", "    datasets.ImageFolder(traindir,\n", "                         transforms.Compose([\n", "                             transforms.RandomResizedCrop(224),\n", "                             transforms.RandomHorizontalFlip(),\n", "                             transforms.ToTensor(),\n", "                             normalize,\n", "                         ])),\n", "    batch_size=BATCH_SIZE,\n", "    shuffle=True,\n", "    num_workers=DATA_WORKERS,\n", "    pin_memory=True)\n", "\n", "val_loader = data.DataLoader(\n", "    datasets.ImageFolder(valdir,\n", "                         transforms.Compose([\n", "                             transforms.Resize(256),\n", "                             transforms.CenterCrop(224),\n", "                             transforms.ToTensor(),\n", "                             normalize,\n", "                         ])),\n", "    batch_size=BATCH_SIZE,\n", "    shuffle=True,\n", "    num_workers=DATA_WORKERS,\n", "    pin_memory=True)\n", "\n", "assert len(train_loader.dataset.classes) is len(val_loader.dataset.classes)\n", "assert len(train_loader.dataset.classes) is CLASSES"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "97e9cdc2-2eda-4316-8dea-d147ea60ac64", "_uuid": "aca68edbef78d7ae724f4344295023fe01286cff", "collapsed": true}, "cell_type": "code", "source": ["def adjust_learning_rate(optimizer, epoch):\n", "    \"\"\"Sets the learning rate to the initial LR decayed by 10 every 10 epochs\"\"\"\n", "    lr = LEARNING_RATE * (0.1 ** (epoch // 10))\n", "    for param_group in optimizer.param_groups:\n", "        param_group['lr'] = lr"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "9b89ab57-c132-47e5-b569-47e84a14f0a3", "_uuid": "65708a4631e751e08c059a2e03b614328850ba2f", "collapsed": true}, "cell_type": "code", "source": ["class AverageMeter(object):\n", "    \"\"\"Computes and stores the average and current value\"\"\"\n", "\n", "    def __init__(self):\n", "        self.reset()\n", "\n", "    def reset(self):\n", "        self.val = 0\n", "        self.avg = 0\n", "        self.sum = 0\n", "        self.count = 0\n", "\n", "    def update(self, val, n=1):\n", "        self.val = val\n", "        self.sum += val * n\n", "        self.count += n\n", "        self.avg = self.sum / self.count\n"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "67810417-ae20-4763-961a-9339780e9769", "_uuid": "0c2c43c737a1128d2b6fdf37a8033a5e0a891e3f", "collapsed": true}, "cell_type": "code", "source": ["def train(train_loader, model, criterion, optimizer, epoch):\n", "    losses = AverageMeter()\n", "    model.train()\n", "\n", "    pbar = tqdm_notebook(train_loader)\n", "    for i, (images, target) in enumerate(pbar):\n", "        target = target.cuda(async=True)\n", "        image_var = torch.autograd.Variable(images)\n", "        label_var = torch.autograd.Variable(target)\n", "        y_pred = model(image_var)\n", "        loss = criterion(y_pred, label_var)\n", "        losses.update(loss.data[0], images.size(0))\n", "        optimizer.zero_grad()\n", "        loss.backward()\n", "        optimizer.step()\n", "        pbar.set_description(\"EPOCH[{0}][{1}/{2}]\".format(epoch, i, len(train_loader)))\n", "        pbar.set_postfix(loss=\"{loss.val:.4f} ({loss.avg:.4f})\".format(loss=losses))\n"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "e252581a-a12d-4ba6-8736-d4d32597ce3c", "_uuid": "602d0548e2a3d266e56c549f7a9670161611690b", "collapsed": true}, "cell_type": "code", "source": ["def validate(val_loader, model, criterion):\n", "    losses = AverageMeter()\n", "    model.eval()\n", "    pbar = tqdm_notebook(val_loader)\n", "    for i, (images, labels) in enumerate(pbar):\n", "        labels = labels.cuda(async=True)\n", "        image_var = torch.autograd.Variable(images, volatile=True)\n", "        label_var = torch.autograd.Variable(labels, volatile=True)\n", "        y_pred = model(image_var)\n", "        loss = criterion(y_pred, label_var)\n", "        losses.update(loss.data[0], images.size(0))\n", "        pbar.set_description(\"VALIDATION[{}/{}]\".format(i, len(val_loader)))\n", "        pbar.set_postfix(loss=\"{loss.val:.4f} ({loss.avg:.4f})\".format(loss=losses))\n", "\n", "    return losses.avg\n"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "6366cbb7-b470-4c21-b895-0ea73436fd6f", "_uuid": "174981e4753beed30a373fc6b3d56f9eef50c766", "collapsed": true}, "cell_type": "code", "source": ["for epoch in range(EPOCHS):\n", "    \n", "    adjust_learning_rate(optimizer, epoch)\n", "    # train for one epoch\n", "    train(train_loader, model, criterion, optimizer, epoch)\n", "    # validate, and track loss to see how we did \n", "    loss = validate(val_loader, model, criterion)\n", "    \n", "    is_best = loss > BEST_ACC\n", "    best_loss = max(loss, BEST_ACC)\n", "    state = {\n", "        'epoch': epoch + 1,\n", "        'arch': MODEL,\n", "        'state_dict': model.state_dict(),\n", "        'best_loss': BEST_ACC,\n", "    }\n", "    torch.save(state, 'checkpoint.pth.tar')\n", "    if is_best:\n", "        shutil.copyfile('checkpoint.pth.tar', 'model_best_{}.pth.tar'.format(BEST_ACC))\n"]}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "d6a54a31-3333-4253-a62c-999c208efe38", "_uuid": "c0e4077fde8dfffe12e898567aeceeb26c925ed5", "collapsed": true}, "cell_type": "code", "source": []}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "c52b1e36-9f34-47af-9ecf-eab5aeab0c2a", "_uuid": "bd7252a2730b4e892ca2c0e0b0525dd2bbb1d710", "collapsed": true}, "cell_type": "code", "source": []}, {"execution_count": null, "outputs": [], "metadata": {"_cell_guid": "13c8e823-d9ff-4aca-87f8-cf41dd0eb891", "_uuid": "2e4e558a501a9e015a9f3a067840eb537f6d9b54", "collapsed": true}, "cell_type": "code", "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3", "name": "python3", "language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "mimetype": "text/x-python", "nbconvert_exporter": "python", "file_extension": ".py", "name": "python", "pygments_lexer": "ipython3", "version": "3.6.3"}}, "nbformat": 4}