{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"WORKSPACE_PATH = '/kaggle'\n# Define whether to use pretrained weights (ImageNet).\nPRETRAINED_ON_IMAGENET = True \n# Base path to store the model checkpoint files.\nCHECKPOINT_PATH = 'working/'\nDATASETS_PATH = 'input/painter-by-numbers/'\n# Model type name.\nMODEL = 'VGG19' # ['VGG19', 'ResNet50', 'EfficientNet'] \nCHALLENGE = 'creator'\n# How many epochs to train for.\nEPOCHS_PER_SESSION = 5\n\ntraining_type = 'FineTuning' if PRETRAINED_ON_IMAGENET else 'OffTheShelf'\nEXPERIMENT_NAME = f'{MODEL}_{training_type}_PBN_{CHALLENGE}'\nprint(\n    f'Running experiment with the following name: {EXPERIMENT_NAME}\\n'\n    f'Saving checkpoint files under {CHECKPOINT_PATH}\\n'\n    f'Training for {EPOCHS_PER_SESSION} epochs'\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cd $WORKSPACE_PATH","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pip install efficientnet-pytorch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import Image\nfrom zipfile import ZipFile\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport io\nfrom pathlib import Path\nimport random\nimport time\n\nimport torch\nfrom torchvision import models, transforms\nimport torch.nn as nn\nfrom torch.optim import SGD\nfrom efficientnet_pytorch import EfficientNet\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"metadata = pd.read_csv('input/painter-by-numbers/all_data_info.csv')\n\nencoder = LabelEncoder()\n\nmetadata['artist_cat'] = encoder.fit_transform(metadata['artist'].astype(str))\nmetadata['style_cat'] = encoder.fit_transform(metadata['style'].astype(str))\n\nmetadata","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_path = 'input/painter-by-numbers/test.zip'\ntrain_path = 'input/painter-by-numbers/train.zip'\ni = 123\nwith ZipFile(train_path) as myzip:\n    files_in_zip = myzip.namelist()\n\nlen(files_in_zip)\n\nwith ZipFile(train_path) as myzip:\n    with myzip.open(files_in_zip[i]) as myfile:\n        name = (myfile.name).split('/')[-1]\n        img = Image.open(myfile)\nmetadata.query('new_filename == @name')['artist_cat'].values.item()\nimg","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_path = 'input/painter-by-numbers/test.zip'\ntrain_path = 'input/painter-by-numbers/train.zip'\n\ninput_size = 224\nbatch_size = 32 if MODEL!='EfficientNet' else 16\n\nlabels = {\n    'creator': 'artist',\n    'type': 'style'    \n}\n\nclasses = {\n    'creator': max(metadata['artist_cat'].values),\n    'type': max(metadata['style_cat'].values)\n}\n\nnum_classes = classes[CHALLENGE]\n\nnorm_mean = (0.485, 0.456, 0.406)\nnorm_std = (0.229, 0.224, 0.225)\n\ntransform = transforms.Compose([\n        transforms.Resize(input_size),\n        transforms.CenterCrop(input_size),\n        transforms.ToTensor(),\n        transforms.Normalize(norm_mean, norm_std)\n])\n\nclass UnNormalize(object):\n    def __init__(self, mean, std):\n        self.mean = mean\n        self.std = std\n\n    def __call__(self, tensor):\n        for t, m, s in zip(tensor, self.mean, self.std):\n            t.mul_(s).add_(m)\n            # The normalize code -> t.sub_(m).div_(s)\n        return tensor\n\nunnormalize = UnNormalize(norm_mean, norm_std)\n\nTensorToImage = transforms.ToPILImage(mode='RGB')\n\nclass HDF5Dataset(torch.utils.data.Dataset):\n    def __init__(self, path, challenge):\n        \n        self.metadata = pd.read_csv('input/painter-by-numbers/all_data_info.csv')\n\n        encoder = LabelEncoder()\n\n        self.metadata[challenge+'_cat'] = encoder.fit_transform(metadata[challenge].astype(str))\n        \n        with ZipFile(path) as myzip:\n            self.files_in_zip = myzip.namelist()\n\n        self.n_images = len(self.files_in_zip)\n        self.challenge = challenge\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n            \n        with ZipFile(train_path) as myzip:\n            with myzip.open(files_in_zip[idx]) as myfile:\n                name = (myfile.name).split('/')[-1]\n                img = Image.open(myfile)\n\n        return (\n            transform(img),\n            int(self.metadata.query('new_filename == @name')[self.challenge+'_cat'].values.item())\n        )\n    \n    def random(self):\n        return self.__getitem__(\n            random.randint(0, self.n_images)\n        )\n\n    def __len__(self):\n        return self.n_images\n\ntrain_dataset = HDF5Dataset(train_path, labels[CHALLENGE])\ntest_dataset = HDF5Dataset(test_path, labels[CHALLENGE])\n\ntrain_loader = torch.utils.data.DataLoader(train_dataset,\n                                        batch_size=batch_size, \n                                        shuffle=True)\ntest_loader = torch.utils.data.DataLoader(test_dataset,\n                                        batch_size=1, \n                                        shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_model(name, pretrained, nc):\n    if name == 'VGG19':\n        model = models.vgg19(pretrained=pretrained)\n        model.classifier[6] = nn.Linear(4096, nc)\n        return model\n\n    elif name == 'ResNet50':\n        model = models.resnet50(pretrained=pretrained)\n        model.fc = nn.Linear(2048, nc)\n        return model\n\n    elif name == 'EfficientNet':\n        if pretrained:\n            model = EfficientNet.from_pretrained('efficientnet-b7')\n        else:\n            model = EfficientNet.from_name('efficientnet-b7')\n        model._fc = nn.Linear(2560, nc)\n        return model\n    else:\n        print(f'Model called {name} not found...')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint_path_full = CHECKPOINT_PATH+EXPERIMENT_NAME+'.pth'\ncheckpoint_load_path = 'input/artclassification/'+EXPERIMENT_NAME+'.pth'\n\nloss_function = nn.CrossEntropyLoss()\nlearning_rate = 1e-3\nmomentum = 0.9\n\nif Path(checkpoint_load_path).is_file():\n    print('Using checkpoint file...')\n    if ~torch.cuda.is_available():\n        checkpoint = torch.load(checkpoint_load_path, map_location=torch.device('cpu'))\n    else:    \n        checkpoint = torch.load(checkpoint_load_path)\n\n    model = get_model(MODEL, False, num_classes)\n    model.load_state_dict(checkpoint['model_state_dict'])\n\n    # Activate the GPU in the Colab runtime settings to utilize cuda acceleration\n    if torch.cuda.is_available():\n        model.cuda()\n        loss_function.cuda()\n        print(f'Using GPU: {torch.cuda.get_device_name(0)}')\n    else:\n        print('Using CPU')\n\n    optimizer = SGD(model.parameters(), lr=learning_rate, momentum=momentum)\n    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n    epoch = checkpoint['epoch']\n\n    avg_train_losses = checkpoint['avg_train_losses']\n    avg_test_losses = checkpoint['avg_test_losses']\n    accuracy = checkpoint['accuracy']\n\nelse:\n    print(f'No checkpoint file named {EXPERIMENT_NAME}.pth found; Creating new model...')\n    model = get_model(MODEL, PRETRAINED_ON_IMAGENET, num_classes)\n\n    if torch.cuda.is_available():\n        model.cuda()\n        loss_function.cuda()\n        print(f'Using GPU: {torch.cuda.get_device_name(0)}')\n    else:\n        print('Using CPU')\n\n    optimizer = SGD(model.parameters(), lr=learning_rate, momentum=momentum)\n\n    epoch = 0\n    avg_train_losses, avg_test_losses = [], []\n    accuracy = []","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epoch_list = range(epoch+1, epoch + (EPOCHS_PER_SESSION) + 1)\n\nfor e in epoch_list:\n    print(f'\\n-----Epoch {e} started.-----\\n')\n\n    since = time.time()\n    \n    train_losses, test_losses = [], []\n\n    model.train()\n    for batch, (images, labels) in enumerate(train_loader, 1):\n        if torch.cuda.is_available():\n            images, labels = images.cuda(), labels.cuda()\n\n        optimizer.zero_grad()\n        logits = model(images)\n\n        loss = loss_function(logits, labels)\n        loss.backward()\n        train_losses.append(loss.item())\n\n        optimizer.step()\n\n        if batch % 200 == 0 or batch == 1 or batch==len(train_loader):\n            time_elapsed = time.time() - since\n            print(\n                f'Training loss = {np.average(train_losses):8.3f} | ',\n                f'Batch # {batch:6.0f} | [{time_elapsed//60:3.0f}m {time_elapsed%60:2.0f}s]')\n        \n    model.eval()\n    score = 0\n    with torch.no_grad():\n        for images, labels in test_loader:\n            if torch.cuda.is_available():\n                images, labels = images.cuda(), labels.cuda()\n\n            logits = model(images)\n            test_losses.append(loss_function(logits, labels).item())\n\n            top_p, top_class = logits.topk(1, dim=1)\n            correct = top_class.squeeze() == labels\n            score += torch.sum(correct.float())\n\n    # Save epoch stats\n    avg_train_losses.append(np.average(train_losses))\n    avg_test_losses.append(np.average(test_losses))\n    accuracy.append(score/len(test_dataset))\n\n    time_elapsed = time.time() - since\n    print(\n        f\"\\nSummary:\\n\",\n        f\"\\tEpoch: {e}/{epoch_list[-1]}\\n\",\n        f\"\\tLearning Rate: {learning_rate}\\n\",\n        f\"\\tTraining Loss: {avg_train_losses[-1]:.5f}\\n\",\n        f\"\\tTesting Loss: {avg_test_losses[-1]:.5f}\\n\",\n        f\"\\tAccuracy: {accuracy[-1]*100:.2f}%\\n\",\n        f\"\\tDuration: {time_elapsed//60:.0f}m {time_elapsed%60:.0f}s\"\n    )\n    \n    print(f'-----Epoch {e} finished.-----\\n')\n\n    print(f'Saving model as {checkpoint_path_full} at epoch #{e}')\n    torch.save(\n        {\n            'epoch': e,\n            'model_state_dict': model.state_dict(),\n            'optimizer_state_dict': optimizer.state_dict(),\n            'avg_train_losses': avg_train_losses,\n            'avg_test_losses': avg_test_losses,\n            'accuracy': accuracy\n        }, \n        checkpoint_path_full\n    )\n\n# Ensuring multiple runs of this cell stack up\nepoch = e","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"score = 0\nmodel.eval()\nwith torch.no_grad():\n    images, labels = test_dataset.random()\n    images.unsqueeze_(0)\n    if torch.cuda.is_available():\n        images, labels = images.cuda(), labels.cuda()\n    logits = model(images)\n\n    top_p, top_class = logits.topk(1, dim=1)\n    correct = top_class.squeeze() == labels\n    score += torch.sum(correct.float())\n\ntest_accuracy = score/len(test_dataset)\n\nprint(score)","execution_count":null,"outputs":[]},{"metadata":{"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}