{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([64, 10])\n",
      "tensor([[9],\n",
      "        [9],\n",
      "        [9],\n",
      "        [9],\n",
      "        [9],\n",
      "        [5],\n",
      "        [9],\n",
      "        [5],\n",
      "        [2],\n",
      "        [9]])\n",
      "Accuracy: 1.5625%\n",
      "Epoch: 1 out of 30\n",
      "Training Loss: 0.510\n",
      "Test Loss: 0.454\n",
      "Test Accuracy: 0.836\n",
      "\n",
      "Epoch: 2 out of 30\n",
      "Training Loss: 0.389\n",
      "Test Loss: 0.412\n",
      "Test Accuracy: 0.852\n",
      "\n",
      "Epoch: 3 out of 30\n",
      "Training Loss: 0.353\n",
      "Test Loss: 0.388\n",
      "Test Accuracy: 0.861\n",
      "\n",
      "Epoch: 4 out of 30\n",
      "Training Loss: 0.330\n",
      "Test Loss: 0.428\n",
      "Test Accuracy: 0.847\n",
      "\n",
      "Epoch: 5 out of 30\n",
      "Training Loss: 0.315\n",
      "Test Loss: 0.381\n",
      "Test Accuracy: 0.865\n",
      "\n",
      "Epoch: 6 out of 30\n",
      "Training Loss: 0.303\n",
      "Test Loss: 0.388\n",
      "Test Accuracy: 0.864\n",
      "\n",
      "Epoch: 7 out of 30\n",
      "Training Loss: 0.292\n",
      "Test Loss: 0.364\n",
      "Test Accuracy: 0.872\n",
      "\n",
      "Epoch: 8 out of 30\n",
      "Training Loss: 0.281\n",
      "Test Loss: 0.370\n",
      "Test Accuracy: 0.869\n",
      "\n",
      "Epoch: 9 out of 30\n",
      "Training Loss: 0.270\n",
      "Test Loss: 0.365\n",
      "Test Accuracy: 0.877\n",
      "\n",
      "Epoch: 10 out of 30\n",
      "Training Loss: 0.267\n",
      "Test Loss: 0.366\n",
      "Test Accuracy: 0.877\n",
      "\n",
      "Epoch: 11 out of 30\n",
      "Training Loss: 0.260\n",
      "Test Loss: 0.369\n",
      "Test Accuracy: 0.873\n",
      "\n",
      "Epoch: 12 out of 30\n",
      "Training Loss: 0.254\n",
      "Test Loss: 0.377\n",
      "Test Accuracy: 0.876\n",
      "\n",
      "Epoch: 13 out of 30\n",
      "Training Loss: 0.244\n",
      "Test Loss: 0.369\n",
      "Test Accuracy: 0.879\n",
      "\n",
      "Epoch: 14 out of 30\n",
      "Training Loss: 0.243\n",
      "Test Loss: 0.371\n",
      "Test Accuracy: 0.879\n",
      "\n",
      "Epoch: 15 out of 30\n",
      "Training Loss: 0.237\n",
      "Test Loss: 0.377\n",
      "Test Accuracy: 0.883\n",
      "\n",
      "Epoch: 16 out of 30\n",
      "Training Loss: 0.230\n",
      "Test Loss: 0.407\n",
      "Test Accuracy: 0.874\n",
      "\n",
      "Epoch: 17 out of 30\n",
      "Training Loss: 0.228\n",
      "Test Loss: 0.370\n",
      "Test Accuracy: 0.879\n",
      "\n",
      "Epoch: 18 out of 30\n",
      "Training Loss: 0.221\n",
      "Test Loss: 0.376\n",
      "Test Accuracy: 0.878\n",
      "\n",
      "Epoch: 19 out of 30\n",
      "Training Loss: 0.222\n",
      "Test Loss: 0.376\n",
      "Test Accuracy: 0.881\n",
      "\n",
      "Epoch: 20 out of 30\n",
      "Training Loss: 0.217\n",
      "Test Loss: 0.387\n",
      "Test Accuracy: 0.880\n",
      "\n",
      "Epoch: 21 out of 30\n",
      "Training Loss: 0.209\n",
      "Test Loss: 0.401\n",
      "Test Accuracy: 0.877\n",
      "\n",
      "Epoch: 22 out of 30\n",
      "Training Loss: 0.210\n",
      "Test Loss: 0.392\n",
      "Test Accuracy: 0.883\n",
      "\n",
      "Epoch: 23 out of 30\n",
      "Training Loss: 0.204\n",
      "Test Loss: 0.411\n",
      "Test Accuracy: 0.878\n",
      "\n",
      "Epoch: 24 out of 30\n",
      "Training Loss: 0.202\n",
      "Test Loss: 0.391\n",
      "Test Accuracy: 0.882\n",
      "\n",
      "Epoch: 25 out of 30\n",
      "Training Loss: 0.195\n",
      "Test Loss: 0.392\n",
      "Test Accuracy: 0.883\n",
      "\n",
      "Epoch: 26 out of 30\n",
      "Training Loss: 0.195\n",
      "Test Loss: 0.471\n",
      "Test Accuracy: 0.878\n",
      "\n",
      "Epoch: 27 out of 30\n",
      "Training Loss: 0.191\n",
      "Test Loss: 0.431\n",
      "Test Accuracy: 0.881\n",
      "\n",
      "Epoch: 28 out of 30\n",
      "Training Loss: 0.195\n",
      "Test Loss: 0.418\n",
      "Test Accuracy: 0.882\n",
      "\n",
      "Epoch: 29 out of 30\n",
      "Training Loss: 0.192\n",
      "Test Loss: 0.390\n",
      "Test Accuracy: 0.887\n",
      "\n",
      "Epoch: 30 out of 30\n",
      "Training Loss: 0.185\n",
      "Test Loss: 0.428\n",
      "Test Accuracy: 0.875\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from torchvision import datasets, transforms\n",
    "from torch import nn, optim\n",
    "import torch.nn.functional as F\n",
    "\n",
    "# Define a transform to normalize the data\n",
    "transform = transforms.Compose([transforms.ToTensor(),\n",
    "                                transforms.Normalize((0.5, 0.5, 0.5),\n",
    "                                                     (0.5, 0.5, 0.5))])\n",
    "\n",
    "# Download and load the training data\n",
    "trainset = datasets.FashionMNIST(\n",
    "    '.pytorch/F_MNIST_data/', download=True, train=True, transform=transform)\n",
    "\n",
    "trainloader = torch.utils.data.DataLoader(\n",
    "    trainset, batch_size=64, shuffle=True)\n",
    "\n",
    "# Download and load the test data\n",
    "testset = datasets.FashionMNIST(\n",
    "    '.pytorch/F_MNIST_data/', download=True, train=False,\n",
    "    transform=transform)\n",
    "\n",
    "testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)\n",
    "\n",
    "\n",
    "class Classifier(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.fc1 = nn.Linear(784, 256)\n",
    "        self.fc2 = nn.Linear(256, 128)\n",
    "        self.fc3 = nn.Linear(128, 64)\n",
    "        self.fc4 = nn.Linear(64, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        # make sure input tensor is flattened\n",
    "        x = x.view(x.shape[0], -1)\n",
    "\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = F.relu(self.fc3(x))\n",
    "        x = F.log_softmax(self.fc4(x), dim=1)\n",
    "\n",
    "        return x\n",
    "\n",
    "\n",
    "model = Classifier()\n",
    "\n",
    "images, labels = next(iter(testloader))\n",
    "\n",
    "# Get the class probabilities\n",
    "ps = torch.exp(model(images))\n",
    "\n",
    "# Make sure the shape is appropriate, we should get 10 class probabilities for\n",
    "# 64 examples\n",
    "print(ps.shape)\n",
    "\n",
    "top_p, top_class = ps.topk(1, dim=1)\n",
    "# Look at the most likely classes for the first 10 examples\n",
    "print(top_class[:10, :])\n",
    "\n",
    "\n",
    "equals = top_class == labels.view(*top_class.shape)\n",
    "\n",
    "\n",
    "accuracy = torch.mean(equals.type(torch.FloatTensor))\n",
    "print(f'Accuracy: {accuracy.item()*100}%')\n",
    "\n",
    "\n",
    "# Model begins\n",
    "\n",
    "model = Classifier()\n",
    "criterion = nn.NLLLoss()\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.003)\n",
    "\n",
    "epochs = 30\n",
    "steps = 0\n",
    "\n",
    "trainLosses, testLosses = [], []\n",
    "for e in range(epochs):\n",
    "    runningLoss = 0\n",
    "    for images, labels in trainloader:\n",
    "\n",
    "        optimizer.zero_grad()\n",
    "\n",
    "        log_ps = model(images)\n",
    "        loss = criterion(log_ps, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        runningLoss += loss.item()\n",
    "\n",
    "    else:\n",
    "        testLoss = 0\n",
    "        accuracy = 0\n",
    "\n",
    "        # Turn off gradients for validation step\n",
    "        with torch.no_grad():\n",
    "            for images, labels in testloader:\n",
    "                # Get the output\n",
    "                log_ps = model(images)\n",
    "                # Get the loss\n",
    "                testLoss += criterion(log_ps, labels)\n",
    "\n",
    "                # Get the probabilities\n",
    "                ps = torch.exp(log_ps)\n",
    "                # Get the most likely class for each prediction\n",
    "                top_p, top_class = ps.topk(1, dim=1)\n",
    "                # Check if the predictions match the actual label\n",
    "                equals = top_class == labels.view(*top_class.shape)\n",
    "                # Update accuracy\n",
    "                accuracy += torch.mean(equals.type(torch.FloatTensor))\n",
    "\n",
    "        # Update train loss\n",
    "        trainLosses.append(runningLoss / len(trainloader))\n",
    "        # Update test loss\n",
    "        testLosses.append(testLoss / len(testloader))\n",
    "\n",
    "        # Print output\n",
    "        print(f'Epoch: {e+1} out of {epochs}')\n",
    "        print(f'Training Loss: {runningLoss/len(trainloader):.3f}')\n",
    "        print(f'Test Loss: {testLoss/len(testloader):.3f}')\n",
    "        print(f'Test Accuracy: {accuracy/len(testloader):.3f}')\n",
    "        print()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
