{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10-6 ResNet for cifar10\n",
    "\n",
    "## original code is =>\n",
    "https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html#sphx-glr-beginner-blitz-cifar10-tutorial-py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "\n",
    "import torch.optim as optim\n",
    "\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "import visdom\n",
    "\n",
    "vis = visdom.Visdom()\n",
    "vis.close(env=\"main\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## define value tracker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def value_tracker(value_plot, value, num):\n",
    "    '''num, loss_value, are Tensor'''\n",
    "    vis.line(X=num,\n",
    "             Y=value,\n",
    "             win = value_plot,\n",
    "             update='append'\n",
    "             )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
    "\n",
    "torch.manual_seed(777)\n",
    "if device =='cuda':\n",
    "    torch.cuda.manual_seed_all(777)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))\n",
    "\n",
    "### How to Calculate mean and std in Normalize "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor()\n",
    "])\n",
    "\n",
    "trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform)\n",
    "\n",
    "print(trainset.train_data.shape)\n",
    "\n",
    "train_data_mean = trainset.train_data.mean( axis=(0,1,2) )\n",
    "train_data_std = trainset.train_data.std( axis=(0,1,2) )\n",
    "\n",
    "\n",
    "print(train_data_mean)\n",
    "print(train_data_std)\n",
    "\n",
    "train_data_mean = train_data_mean / 255\n",
    "train_data_std = train_data_std / 255\n",
    "\n",
    "print(train_data_mean)\n",
    "print(train_data_std)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "transform_train = transforms.Compose([\n",
    "    transforms.RandomCrop(32, padding=4),\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize(train_data_mean, train_data_std)\n",
    "])\n",
    "\n",
    "transform_test = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize(train_data_mean, train_data_std)\n",
    "])\n",
    "\n",
    "trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True,\n",
    "                                        download=True, transform=transform_train)\n",
    "trainloader = torch.utils.data.DataLoader(trainset, batch_size=256,\n",
    "                                          shuffle=True, num_workers=0)\n",
    "\n",
    "testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False,\n",
    "                                       download=True, transform=transform_test)\n",
    "\n",
    "testloader = torch.utils.data.DataLoader(testset, batch_size=256,\n",
    "                                         shuffle=False, num_workers=0)\n",
    "\n",
    "classes = ('plane', 'car', 'bird', 'cat',\n",
    "           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## make ResNet50 using resnet.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import resnet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "conv1x1=resnet.conv1x1\n",
    "Bottleneck = resnet.Bottleneck\n",
    "BasicBlock= resnet.BasicBlock"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ResNet(nn.Module):\n",
    "\n",
    "    def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):\n",
    "        super(ResNet, self).__init__()\n",
    "        self.inplanes = 16\n",
    "        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1,\n",
    "                               bias=False)\n",
    "        self.bn1 = nn.BatchNorm2d(16)\n",
    "        self.relu = nn.ReLU(inplace=True)\n",
    "        #self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n",
    "        \n",
    "        self.layer1 = self._make_layer(block, 16, layers[0], stride=1)\n",
    "        self.layer2 = self._make_layer(block, 32, layers[1], stride=1)\n",
    "        self.layer3 = self._make_layer(block, 64, layers[2], stride=2)\n",
    "        self.layer4 = self._make_layer(block, 128, layers[3], stride=2)\n",
    "        \n",
    "        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n",
    "        self.fc = nn.Linear(128 * block.expansion, num_classes)\n",
    "\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Conv2d):\n",
    "                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')\n",
    "            elif isinstance(m, nn.BatchNorm2d):\n",
    "                nn.init.constant_(m.weight, 1)\n",
    "                nn.init.constant_(m.bias, 0)\n",
    "\n",
    "        # Zero-initialize the last BN in each residual branch,\n",
    "        # so that the residual branch starts with zeros, and each residual block behaves like an identity.\n",
    "        # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677\n",
    "        if zero_init_residual:\n",
    "            for m in self.modules():\n",
    "                if isinstance(m, Bottleneck):\n",
    "                    nn.init.constant_(m.bn3.weight, 0)\n",
    "                elif isinstance(m, BasicBlock):\n",
    "                    nn.init.constant_(m.bn2.weight, 0)\n",
    "\n",
    "    def _make_layer(self, block, planes, blocks, stride=1):\n",
    "        downsample = None\n",
    "        if stride != 1 or self.inplanes != planes * block.expansion:\n",
    "            downsample = nn.Sequential(\n",
    "                conv1x1(self.inplanes, planes * block.expansion, stride),\n",
    "                nn.BatchNorm2d(planes * block.expansion),\n",
    "            )\n",
    "\n",
    "        layers = []\n",
    "        layers.append(block(self.inplanes, planes, stride, downsample))\n",
    "        self.inplanes = planes * block.expansion\n",
    "        for _ in range(1, blocks):\n",
    "            layers.append(block(self.inplanes, planes))\n",
    "\n",
    "        return nn.Sequential(*layers)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.conv1(x)\n",
    "        #x.shape =[1, 16, 32,32]\n",
    "        x = self.bn1(x)\n",
    "        x = self.relu(x)\n",
    "        #x = self.maxpool(x)\n",
    "\n",
    "        x = self.layer1(x)\n",
    "        #x.shape =[1, 128, 32,32]\n",
    "        x = self.layer2(x)\n",
    "        #x.shape =[1, 256, 32,32]\n",
    "        x = self.layer3(x)\n",
    "        #x.shape =[1, 512, 16,16]\n",
    "        x = self.layer4(x)\n",
    "        #x.shape =[1, 1024, 8,8]\n",
    "        \n",
    "        x = self.avgpool(x)\n",
    "        x = x.view(x.size(0), -1)\n",
    "        x = self.fc(x)\n",
    "\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "resnet50 = ResNet(resnet.Bottleneck, [3, 4, 6, 3], 10, True).to(device) \n",
    "#1(conv1) + 9(layer1) + 12(layer2) + 18(layer3) + 9(layer4) +1(fc)= ResNet50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "resnet50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "a=torch.Tensor(1,3,32,32).to(device)\n",
    "out = resnet50(a)\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "criterion = nn.CrossEntropyLoss().to(device)\n",
    "optimizer = torch.optim.SGD(resnet50.parameters(), lr = 0.1, momentum = 0.9, weight_decay=5e-4)\n",
    "lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## make plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_plt = vis.line(Y=torch.Tensor(1).zero_(),opts=dict(title='loss_tracker', legend=['loss'], showlegend=True))\n",
    "acc_plt = vis.line(Y=torch.Tensor(1).zero_(),opts=dict(title='Accuracy', legend=['Acc'], showlegend=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## define acc_check function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def acc_check(net, test_set, epoch, save=1):\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    with torch.no_grad():\n",
    "        for data in test_set:\n",
    "            images, labels = data\n",
    "            images = images.to(device)\n",
    "            labels = labels.to(device)\n",
    "            outputs = net(images)\n",
    "\n",
    "            _, predicted = torch.max(outputs.data, 1)\n",
    "\n",
    "            total += labels.size(0)\n",
    "            correct += (predicted == labels).sum().item()\n",
    "    \n",
    "    acc = (100 * correct / total)\n",
    "    print('Accuracy of the network on the 10000 test images: %d %%' % acc)\n",
    "    if save:\n",
    "        torch.save(net.state_dict(), \"./model/model_epoch_{}_acc_{}.pth\".format(epoch, int(acc)))\n",
    "    return acc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training with (acc check + model save)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(len(trainloader))\n",
    "epochs = 150\n",
    "\n",
    "for epoch in range(epochs):  # loop over the dataset multiple times\n",
    "\n",
    "    running_loss = 0.0\n",
    "    lr_sche.step()\n",
    "    for i, data in enumerate(trainloader, 0):\n",
    "        # get the inputs\n",
    "        inputs, labels = data\n",
    "        inputs = inputs.to(device)\n",
    "        labels = labels.to(device)\n",
    "\n",
    "        # zero the parameter gradients\n",
    "        optimizer.zero_grad()\n",
    "\n",
    "        # forward + backward + optimize\n",
    "        outputs = resnet50(inputs)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        # print statistics\n",
    "        running_loss += loss.item()\n",
    "        if i % 30 == 29:    # print every 30 mini-batches\n",
    "            value_tracker(loss_plt, torch.Tensor([running_loss/30]), torch.Tensor([i + epoch*len(trainloader) ]))\n",
    "            print('[%d, %5d] loss: %.3f' %\n",
    "                  (epoch + 1, i + 1, running_loss / 30))\n",
    "            running_loss = 0.0\n",
    "    \n",
    "    #Check Accuracy\n",
    "    acc = acc_check(resnet50, testloader, epoch, save=1)\n",
    "    value_tracker(acc_plt, torch.Tensor([acc]), torch.Tensor([epoch]))\n",
    "    \n",
    "\n",
    "print('Finished Training')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Accuracy Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "correct = 0\n",
    "total = 0\n",
    "\n",
    "with torch.no_grad():\n",
    "    for data in testloader:\n",
    "        images, labels = data\n",
    "        images = images.to(device)\n",
    "        labels = labels.to(device)\n",
    "        outputs = resnet50(images)\n",
    "        \n",
    "        _, predicted = torch.max(outputs.data, 1)\n",
    "        \n",
    "        total += labels.size(0)\n",
    "        \n",
    "        correct += (predicted == labels).sum().item()\n",
    "\n",
    "print('Accuracy of the network on the 10000 test images: %d %%' % (\n",
    "    100 * correct / total))"
   ]
  }
 ],
 "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.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}