diff --git a/Manasritha_CNN_task/Manasritha.ipynb b/Manasritha_CNN_task/Manasritha.ipynb new file mode 100644 index 0000000..999fbf0 --- /dev/null +++ b/Manasritha_CNN_task/Manasritha.ipynb @@ -0,0 +1,847 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "Manasritha.ipynb", + "provenance": [], + "collapsed_sections": [ + "D_uk0N0CuCJu", + "pVVOZoySss1c", + "SoJh8htRvs0_", + "dM5LUuq-v0D4", + "EChdov-rsINc", + "S6g3Secxw73k", + "6xdFH0S-tOe5", + "0jKwtSnczNmN", + "3w7cUbeK2bLG", + "1dt84j6L4fi5", + "XyqkKRGm48E6", + "S2ERMacD8MsL", + "2yDog-I9TquI" + ] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "code", + "source": [ + "from IPython.display import clear_output\n", + "\n", + "!pip install pyprind\n", + "\n", + "clear_output()" + ], + "metadata": { + "id": "uJ72toZAuJv9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from google.colab.patches import cv2_imshow\n", + "import cv2\n", + "from PIL import Image\n", + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "import sklearn\n", + "from sklearn import preprocessing\n", + "from sklearn.model_selection import train_test_split\n", + "import numpy as np\n", + "import pandas as pd\n", + "import gzip\n", + "\n", + "import os\n", + "import glob\n", + "import time\n", + "import random\n", + "import gc\n", + "import json\n", + "import copy\n", + "import pyprind\n", + "import tqdm\n", + "from dataclasses import dataclass, field\n", + "from typing import Union, List, Dict, Any, Optional, cast\n", + "\n", + "import torch\n", + "import torchvision\n", + "import torchtext\n", + "\n", + "from torchvision import transforms\n", + "from torch.utils.data import DataLoader\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "from torchsummary import summary" + ], + "metadata": { + "id": "kJkjypzhuEsz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!gdown --id 1oYnD7Izl3LVVzjEMyLxLklX30TKWHgGG\n", + "!unzip /content/cifar-10.zip\n", + "!rm -rf /content/cifar-10.zip\n", + "!mv /content/cifar-10/sample_submission.csv /content/cifar-10/test_labels.csv\n", + "\n", + "clear_output()" + ], + "metadata": { + "id": "rk-72wvstOEd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "def process():\n", + " data = pd.read_csv(os.path.join('/content/cifar-10', 'train_labels.csv'))\n", + " encoder = preprocessing.LabelEncoder()\n", + " encoder.fit(data['label'])\n", + " return encoder\n", + "\n", + "encoder = process()" + ], + "metadata": { + "id": "My1DLmX6xUFw" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "data = pd.read_csv(os.path.join('/content/cifar-10', 'train_labels.csv'))\n", + "files = data['id']\n", + "labels = encoder.transform(data['label'])\n", + "\n", + "train_data_files, valid_data_files, train_data_label, valid_data_label = train_test_split(files, labels, test_size=0.1, shuffle=True, stratify=labels)\n", + "\n", + "train_data = pd.DataFrame({'id': train_data_files, 'label': train_data_label})\n", + "valid_data = pd.DataFrame({'id': valid_data_files, 'label': valid_data_label})\n", + "\n", + "unique, counts = np.unique(labels, return_counts=True)\n", + "weights = torch.Tensor(sklearn.utils.class_weight.compute_class_weight(class_weight=\"balanced\", classes=unique, y=np.asarray(labels)))" + ], + "metadata": { + "id": "ak2lfBubwugU" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "data = pd.read_csv(os.path.join('/content/cifar-10', 'test_labels.csv'))\n", + "test_data_files = data['id']\n", + "test_data_label = encoder.transform(data['label'])\n", + "\n", + "test_data = pd.DataFrame({'id': test_data_files, 'label': test_data_label})" + ], + "metadata": { + "id": "fDWZKkfJwud6" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "@dataclass\n", + "class TrainingArgs():\n", + "\n", + " seed: int = 17\n", + " lr: float = 3e-4\n", + " batch_size: int = 64\n", + " num_workers: int = os.cpu_count()\n", + " max_epochs: str = 250\n", + " device: torch.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "\n", + " img_size: int = 32\n", + "\n", + " root_dir: str = '/content/cifar-10'\n", + " train_file: dict = train_data\n", + " valid_file: dict = valid_data\n", + " test_file: dict = test_data\n", + " data: tuple = ()\n", + " checkpoint: str = '/content/'\n", + "\n", + " project_name: str = 'CIFAR10'\n", + "\n", + "args = TrainingArgs()" + ], + "metadata": { + "id": "AMo4bQJezJui" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "class CreateDataset(torch.utils.data.Dataset):\n", + " def __init__(self, args, mode='train'):\n", + " self.args = args\n", + " self.mode = mode\n", + "\n", + " if mode=='train':\n", + " self.entry = self.args.train_file\n", + " elif mode=='valid':\n", + " self.entry = self.args.valid_file\n", + " self.mode = 'train'\n", + " elif mode=='test':\n", + " self.entry = self.args.test_file \n", + "\n", + " self.transform = torchvision.transforms.Compose(\n", + " [\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),\n", + " ]\n", + " )\n", + "\n", + " def __getitem__(self, index):\n", + " data = self.entry.iloc[index]\n", + " file_name = os.path.join(self.args.root_dir, self.mode, str(data['id'])+'.png')\n", + " image = Image.open(file_name)\n", + " image = self.transform(image)\n", + " label = data['label']\n", + " return image, label\n", + "\n", + " def __len__(self):\n", + " return len(self.entry)" + ], + "metadata": { + "id": "UCGrSq1NttOq" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "---" + ], + "metadata": { + "id": "huC57aPyvpA_" + } + }, + { + "cell_type": "code", + "source": [ + "train_dataset = CreateDataset(args, mode='train')\n", + "valid_dataset = CreateDataset(args, mode='valid')\n", + "test_dataset = CreateDataset(args, mode='test')\n", + "\n", + "args.data = (train_dataset, valid_dataset, test_dataset)" + ], + "metadata": { + "id": "gyf8b7t0uRq5" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "trainloader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n", + "validloader = DataLoader(valid_dataset, batch_size=32, shuffle=False)\n", + "testloader = DataLoader(test_dataset, batch_size=32, shuffle=False)" + ], + "metadata": { + "id": "NH15A3gHuYeB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "---" + ], + "metadata": { + "id": "vdoUtdT8vta6" + } + }, + { + "cell_type": "code", + "source": [ + "data = next(iter(trainloader))" + ], + "metadata": { + "id": "H7QNEGjA1-PL" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "data[0].shape, data[1].shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "w8XBPxmq1_66", + "outputId": "e276a1c0-1df0-4881-a42a-83b72b27fd8e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(torch.Size([32, 3, 32, 32]), torch.Size([32]))" + ] + }, + "metadata": {}, + "execution_count": 12 + } + ] + }, + { + "cell_type": "code", + "source": [ + "index = 0\n", + "\n", + "image = cv2.resize(data[0][index].permute(1, 2, 0).numpy()*255, (224, 224))\n", + "label = encoder.inverse_transform(data[1])[index]\n", + "\n", + "cv2_imshow(image)\n", + "print(label)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 259 + }, + "id": "k8JSTlRG1MNz", + "outputId": "3aff15b6-96a3-4594-809b-68eefb7e8350" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "image/png": "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\n" + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "airplane\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#Resnet34\n", + "def conv3x3(in_channels, out_channels, stride=1):\n", + " return nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)\n", + "\n", + "def conv7x7(in_channels, out_channels, stride=1):\n", + " return nn.Conv2d(in_channels, out_channels, kernel_size=7, stride=stride, padding=1, bias=False)\n", + "\n", + "class BasicBlock(nn.Module):\n", + " def __init__(self, in_channels, out_channels, stride=1, downsample=None):\n", + " super(BasicBlock, self).__init__()\n", + " self.conv1 = conv3x3(in_channels, out_channels, stride)\n", + " self.bn1 = nn.BatchNorm2d(out_channels)\n", + " self.relu = nn.ReLU(inplace=True) # Activation function - max(0,x)\n", + " self.conv2 = conv3x3(out_channels, out_channels)\n", + " self.bn2 = nn.BatchNorm2d(out_channels)\n", + " self.downsample = downsample\n", + " \n", + " def forward(self, x):\n", + " residual = x\n", + " out = self.conv1(x)\n", + " out = self.bn1(out)\n", + " out = self.relu(out)\n", + " out = self.conv2(out)\n", + " out = self.bn2(out)\n", + " if self.downsample:\n", + " residual = self.downsample(x)\n", + " out += residual\n", + " out = self.relu(out)\n", + " return out\n", + "\n", + "class ResNet(nn.Module):\n", + " def __init__(self, block, layers, num_classes=10):\n", + " super(ResNet, self).__init__()\n", + " self.in_channels = 16\n", + " self.conv = conv3x3(3, 16)\n", + " self.bn = nn.BatchNorm2d(16)\n", + " self.relu = nn.ReLU(inplace=True)\n", + " self.layer1 = self.make_layer(block, 16, layers[0])\n", + " self.layer2 = self.make_layer(block, 32, layers[1], 2)\n", + " self.layer3 = self.make_layer(block, 64, layers[2], 2)\n", + " self.layer4 = self.make_layer(block, 128, layers[3], 2)\n", + " self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))\n", + " self.fc = nn.Linear(128, num_classes)\n", + " \n", + " def make_layer(self, block, out_channels, blocks, stride=1):\n", + " downsample = None\n", + " if (stride != 1) or (self.in_channels != out_channels):\n", + " downsample = nn.Sequential(\n", + " conv3x3(self.in_channels, out_channels, stride=stride),\n", + " nn.BatchNorm2d(out_channels))\n", + " layers = []\n", + " layers.append(block(self.in_channels, out_channels, stride, downsample))\n", + " self.in_channels = out_channels\n", + " for i in range(1, blocks):\n", + " layers.append(block(out_channels, out_channels))\n", + " return nn.Sequential(*layers)\n", + " \n", + " def forward(self, x):\n", + " out = self.conv(x)\n", + " out = self.bn(out)\n", + " out = self.relu(out)\n", + " out = self.layer1(out)\n", + " out = self.layer2(out)\n", + " out = self.layer3(out)\n", + " out = self.layer4(out)\n", + " out = self.avg_pool(out)\n", + " out = torch.flatten(out, 1) \n", + " out = self.fc(out)\n", + " return out\n", + "\n", + "\n", + "model = ResNet(BasicBlock, [3, 4, 6, 3])" + ], + "metadata": { + "id": "DHcn9rT04WrT" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#Resnet18\n", + "def conv3x3(in_channels, out_channels, stride=1):\n", + " return nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)\n", + "\n", + "class BasicBlock(nn.Module):\n", + " def __init__(self, in_channels, out_channels, stride=1, downsample=None):\n", + " super(BasicBlock, self).__init__()\n", + " self.conv1 = conv3x3(in_channels, out_channels, stride)\n", + " self.bn1 = nn.BatchNorm2d(out_channels)\n", + " self.relu = nn.ReLU(inplace=True) # Activation function - max(0,x)\n", + " self.conv2 = conv3x3(out_channels, out_channels)\n", + " self.bn2 = nn.BatchNorm2d(out_channels)\n", + " self.downsample = downsample\n", + " \n", + " def forward(self, x):\n", + " residual = x\n", + " out = self.conv1(x)\n", + " out = self.bn1(out)\n", + " out = self.relu(out)\n", + " out = self.conv2(out)\n", + " out = self.bn2(out)\n", + " if self.downsample:\n", + " residual = self.downsample(x)\n", + " out += residual\n", + " out = self.relu(out)\n", + " return out\n", + "\n", + "class ResNet(nn.Module):\n", + " def __init__(self, block, layers, num_classes=10):\n", + " super(ResNet, self).__init__()\n", + " self.in_channels = 16\n", + " self.conv = conv3x3(3, 16)\n", + " self.bn = nn.BatchNorm2d(16)\n", + " self.relu = nn.ReLU(inplace=True)\n", + " self.layer1 = self.make_layer(block, 16, layers[0])\n", + " self.layer2 = self.make_layer(block, 32, layers[1], 2)\n", + " self.layer3 = self.make_layer(block, 64, layers[2], 2)\n", + " self.layer4 = self.make_layer(block, 128, layers[3], 2)\n", + " self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))\n", + " self.fc = nn.Linear(128, num_classes)\n", + " \n", + " def make_layer(self, block, out_channels, blocks, stride=1):\n", + " downsample = None\n", + " if (stride != 1) or (self.in_channels != out_channels):\n", + " downsample = nn.Sequential(\n", + " conv3x3(self.in_channels, out_channels, stride=stride),\n", + " nn.BatchNorm2d(out_channels))\n", + " layers = []\n", + " layers.append(block(self.in_channels, out_channels, stride, downsample))\n", + " self.in_channels = out_channels\n", + " for i in range(1, blocks):\n", + " layers.append(block(out_channels, out_channels))\n", + " return nn.Sequential(*layers)\n", + " \n", + " def forward(self, x):\n", + " out = self.conv(x)\n", + " out = self.bn(out)\n", + " out = self.relu(out)\n", + " out = self.layer1(out)\n", + " out = self.layer2(out)\n", + " out = self.layer3(out)\n", + " out = self.layer4(out)\n", + " out = self.avg_pool(out)\n", + " out = torch.flatten(out, 1) \n", + " out = self.fc(out)\n", + " return out\n", + "\n", + "\n", + "model = ResNet(BasicBlock, [2, 2, 2, 2])" + ], + "metadata": { + "id": "uOD8uP6y9qWP" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "class Trainer():\n", + " def __init__(self, args, architecture):\n", + "\n", + " self.args = args\n", + "\n", + " random.seed(self.args.seed)\n", + " np.random.seed(self.args.seed)\n", + " torch.manual_seed(self.args.seed)\n", + " torch.cuda.manual_seed(self.args.seed)\n", + " torch.cuda.manual_seed_all(self.args.seed)\n", + "\n", + " self.trainloader, self.validloader, self.testloader = self.get_iterator(self.args.data)\n", + " \n", + " self.model = architecture.to(self.args.device) # self.get_model()\n", + " self.criterion = self.get_criterion()\n", + " self.optimizer = self.get_optimizer()\n", + " self.scheduler = self.get_scheduler()\n", + "\n", + " self.train_loss = []\n", + " self.train_metrics = {'accuracy': []}\n", + " self.valid_loss = []\n", + " self.valid_metrics = {'accuracy': []}\n", + "\n", + " self.start_epoch = 0\n", + "\n", + " def get_iterator(self, data):\n", + " train, valid, test = data\n", + " trainloader = DataLoader(train, batch_size=self.args.batch_size, shuffle=True, num_workers=self.args.num_workers, pin_memory=True, drop_last=True)\n", + " validloader = DataLoader(valid, batch_size=self.args.batch_size, shuffle=False, num_workers=self.args.num_workers, pin_memory=True, drop_last=True)\n", + " testloader = DataLoader(test, batch_size=self.args.batch_size, shuffle=False, num_workers=self.args.num_workers, pin_memory=True, drop_last=False)\n", + " return trainloader, validloader, testloader\n", + "\n", + " def get_criterion(self):\n", + " return nn.CrossEntropyLoss(weight=weights).to(self.args.device)\n", + " \n", + " def get_optimizer(self):\n", + " return torch.optim.Adam(self.model.parameters(), lr=self.args.lr)\n", + "\n", + " def get_scheduler(self):\n", + " return torch.optim.lr_scheduler.CosineAnnealingLR(self.optimizer, self.args.max_epochs, eta_min=1e-12, last_epoch=-1, verbose=False)\n", + "\n", + " def get_model(self, architecture):\n", + " model = ResNet(args).to(self.args.device)\n", + " return model\n", + "\n", + " def get_model_params(self):\n", + " return sum(p.numel() for p in self.model.parameters())/1e6\n", + "\n", + " def get_accuracy(self, output, label):\n", + " return (torch.argmax(output, dim=1)==label).float().sum().item()\n", + "\n", + " def save(self, epoch):\n", + " torch.save({\n", + " 'model_state_dict': self.model.state_dict(),\n", + " 'optimizer_state_dict': self.optimizer.state_dict(),\n", + " }, os.path.join(self.args.checkpoint, \"model.pth\"))\n", + " torch.save({\n", + " 'epoch': epoch,\n", + " 'args': args,\n", + " 'loss': (self.train_loss, self.valid_loss),\n", + " 'metrics': (self.train_metrics, self.valid_metrics)\n", + " }, os.path.join(self.args.checkpoint, \"metrics.pth\"))\n", + " \n", + " def load(self):\n", + " if os.path.exists(os.path.join(self.args.checkpoint, \"model.pth\")):\n", + " checkpoints = torch.load(os.path.join(self.args.checkpoint, \"model.pth\"), map_location=self.args.device)\n", + " self.model.load_state_dict(checkpoints['model_state_dict'])\n", + " self.optimizer.load_state_dict(checkpoints['optimizer_state_dict'])\n", + "\n", + " if os.path.exists(os.path.join(self.args.checkpoint, \"metrics.pth\")):\n", + " checkpoints = torch.load(os.path.join(self.args.checkpoint, \"metrics.pth\"), map_location=self.args.device)\n", + " self.args = checkpoints['args']\n", + " self.train_loss, self.valid_loss = checkpoints['loss']\n", + " self.train_metrics, self.valid_metrics = checkpoints['metrics']\n", + " return checkpoints['epoch']\n", + " return 0\n", + "\n", + " def train(self):\n", + " epoch_loss = 0\n", + " epoch_metrics = {'accuracy': 0}\n", + "\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + " self.model.train()\n", + "\n", + " with torch.autograd.set_detect_anomaly(True):\n", + " bar = pyprind.ProgBar(len(self.trainloader), bar_char='█')\n", + " for index, (data, label) in enumerate(self.trainloader):\n", + " data = data.to(self.args.device).float()\n", + " label = label.long().to(self.args.device)\n", + "\n", + " self.optimizer.zero_grad()\n", + " \n", + " output = self.model(data)\n", + "\n", + " loss = self.criterion(output, label)\n", + "\n", + " loss.backward()\n", + "\n", + " self.optimizer.step()\n", + "\n", + " epoch_loss += loss.item()/len(self.trainloader)\n", + "\n", + " epoch_metrics['accuracy'] += self.get_accuracy(output, label)/len(self.args.data[0])\n", + "\n", + " bar.update()\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + "\n", + " return epoch_loss, epoch_metrics\n", + "\n", + " def evaluate(self):\n", + " epoch_loss = 0\n", + " epoch_metrics = {'accuracy': 0}\n", + "\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + " self.model.eval()\n", + "\n", + " with torch.autograd.set_detect_anomaly(True):\n", + " with torch.no_grad():\n", + " bar = pyprind.ProgBar(len(self.validloader), bar_char='█')\n", + " for index, (data, label) in enumerate(self.validloader):\n", + " data = data.to(self.args.device).float()\n", + " label = label.long().to(self.args.device)\n", + "\n", + " output = self.model(data)\n", + "\n", + " loss = self.criterion(output, label)\n", + "\n", + " epoch_loss += loss.item()/len(self.validloader)\n", + " epoch_metrics['accuracy'] += self.get_accuracy(output, label)/len(self.args.data[1])\n", + "\n", + " bar.update()\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + "\n", + " return epoch_loss, epoch_metrics\n", + "\n", + " def test(self):\n", + "\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + " self.model.eval()\n", + "\n", + " outputs = torch.empty([0,])\n", + "\n", + " with torch.autograd.set_detect_anomaly(True):\n", + " with torch.no_grad():\n", + " bar = pyprind.ProgBar(len(self.testloader), bar_char='█')\n", + " for index, (data, label) in enumerate(self.testloader):\n", + " data = data.to(self.args.device)\n", + "\n", + " output = torch.argmax(self.model(data)).detach().cpu()\n", + " outputs = torch.cat((outputs, output), dim=0)\n", + "\n", + " bar.update()\n", + " gc.collect()\n", + " torch.cuda.empty_cache()\n", + "\n", + " return outputs\n", + " \n", + " def fit(self, next=True):\n", + " if next:\n", + " self.start_epoch = self.load()\n", + "\n", + " for epoch in range(self.start_epoch+1, self.args.max_epochs+1, 1):\n", + "\n", + " epoch_train_loss, epoch_train_metrics = self.train()\n", + " epoch_train_accuracy = epoch_train_metrics['accuracy']\n", + "\n", + " self.train_loss.append(epoch_train_loss)\n", + " self.train_metrics['accuracy'].append(epoch_train_metrics['accuracy'])\n", + "\n", + " epoch_valid_loss, epoch_valid_metrics = self.evaluate()\n", + " epoch_valid_accuracy = epoch_valid_metrics['accuracy']\n", + "\n", + " \n", + " self.valid_loss.append(epoch_valid_loss)\n", + " self.valid_metrics['accuracy'].append(epoch_valid_metrics['accuracy']) \n", + "\n", + " self.scheduler.step()\n", + " for param_group in self.optimizer.param_groups:\n", + " self.args.lr = param_group['lr']\n", + " if epoch_valid_metrics['accuracy'] >= max(self.valid_metrics['accuracy']):\n", + " self.save(epoch)\n", + "\n", + " time.sleep(1)\n", + " print(f'Epoch {epoch}/{self.args.max_epochs} | Training: Loss = {round(epoch_train_loss, 4)} Accuracy = {round(epoch_train_accuracy, 4)} | Validation: Loss = {round(epoch_valid_loss, 4)} Accuracy = {round(epoch_valid_accuracy, 4)}')" + ], + "metadata": { + "id": "U9sm8odkGgMz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Training" + ], + "metadata": { + "id": "jPrxVsc1IsX1" + } + }, + { + "cell_type": "code", + "source": [ + "trainer = Trainer(args, architecture=model)" + ], + "metadata": { + "id": "ka3HRNorHKhu" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "trainer.fit()" + ], + "metadata": { + "id": "hp6TOW0iHcpt", + "outputId": "a29e6ce3-b15d-41f0-b20c-884e6290083a", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:02:28\n", + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:06\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch 1/250 | Training: Loss = 1.4824 Accuracy = 0.4541 | Validation: Loss = 1.2464 Accuracy = 0.5458\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:02:15\n", + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:06\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch 2/250 | Training: Loss = 1.0399 Accuracy = 0.6282 | Validation: Loss = 0.9934 Accuracy = 0.6514\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:02:13\n", + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:06\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch 3/250 | Training: Loss = 0.8124 Accuracy = 0.7158 | Validation: Loss = 0.8548 Accuracy = 0.6968\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:02:15\n", + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:06\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch 4/250 | Training: Loss = 0.6656 Accuracy = 0.7663 | Validation: Loss = 0.8246 Accuracy = 0.721\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:02:13\n", + "0% [██████████████████████████████] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:06\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch 5/250 | Training: Loss = 0.5512 Accuracy = 0.8092 | Validation: Loss = 0.8392 Accuracy = 0.7216\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "0% [████████████████ ] 100% | ETA: 00:01:03" + ] + } + ] + } + ] +} \ No newline at end of file