{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np \nimport matplotlib.pyplot as plt\n\nimport json\nimport os\nimport shutil\nfrom distutils.dir_util import copy_tree\nimport random\nimport re\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\n\nimport torchvision\nimport torchvision.transforms as transforms\nimport torchvision.models as models\n\n\n%matplotlib inline","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-06-06T13:03:32.167852Z","iopub.execute_input":"2022-06-06T13:03:32.168255Z","iopub.status.idle":"2022-06-06T13:03:33.789690Z","shell.execute_reply.started":"2022-06-06T13:03:32.168153Z","shell.execute_reply":"2022-06-06T13:03:33.788563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n\nseed_everything(3001)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.792585Z","iopub.execute_input":"2022-06-06T13:03:33.792909Z","iopub.status.idle":"2022-06-06T13:03:33.801228Z","shell.execute_reply.started":"2022-06-06T13:03:33.792861Z","shell.execute_reply":"2022-06-06T13:03:33.800401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Copy files to the work directory so we can split them to train and validation sets:","metadata":{}},{"cell_type":"code","source":"def move_files(source_folder, destination_folder, files_to_move):\n    # iterate files\n    for file in files_to_move:\n        # construct full file path\n        source = source_folder + file\n        destination = destination_folder + file\n        # move file\n        shutil.move(source, destination)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.802542Z","iopub.execute_input":"2022-06-06T13:03:33.802800Z","iopub.status.idle":"2022-06-06T13:03:33.811521Z","shell.execute_reply.started":"2022-06-06T13:03:33.802769Z","shell.execute_reply":"2022-06-06T13:03:33.810569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import PIL\nimport random\n\ndata_dir = '../input/create-rotated-hotel-images-dataset/data'\n    \ntrain_files = [f for f in os.listdir(os.path.join(data_dir, 'images/train/class/'))]#'./data/images/train/class/')]\nvalid_files = [f for f in os.listdir(os.path.join(data_dir, 'images/valid/class/'))]#./data/images/valid/class/')]","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.813468Z","iopub.execute_input":"2022-06-06T13:03:33.814870Z","iopub.status.idle":"2022-06-06T13:03:33.933247Z","shell.execute_reply.started":"2022-06-06T13:03:33.814830Z","shell.execute_reply":"2022-06-06T13:03:33.931434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load labels data as dictionary. The keys are the file names and the values are the labels:","metadata":{}},{"cell_type":"code","source":"with open(os.path.join(data_dir, 'label.json'), 'r') as f:\n    labels_list = json.load(f)\n    \nlabels_dict = {''.join([str(key), '.png']): value for key, value in enumerate(labels_list)}","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.934224Z","iopub.status.idle":"2022-06-06T13:03:33.934944Z","shell.execute_reply.started":"2022-06-06T13:03:33.934702Z","shell.execute_reply":"2022-06-06T13:03:33.934732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Define the necessary transformations for the images:","metadata":{}},{"cell_type":"code","source":"# required parameters for all images fed into the pretrained models:\nmean_for_norm = np.array([0.485, 0.456, 0.406])\nstd_for_norm = np.array([0.229, 0.224, 0.225])\nimage_size = 224\n\ntransform = transform=transforms.Compose([transforms.Resize(image_size),\n                                          transforms.RandomResizedCrop(image_size),\n                                          transforms.ToTensor(),\n                                          transforms.Normalize(mean_for_norm, std_for_norm),\n                                          transforms.RandomErasing()])","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.936292Z","iopub.status.idle":"2022-06-06T13:03:33.936625Z","shell.execute_reply.started":"2022-06-06T13:03:33.936454Z","shell.execute_reply":"2022-06-06T13:03:33.936476Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Modify ImageFolder method to apply to regression tasks:","metadata":{}},{"cell_type":"code","source":"class RegressionImageFolder(torchvision.datasets.ImageFolder):\n    def __init__(self, root, image_scores, **kwargs) -> None:\n        super().__init__(root, **kwargs)\n        paths, _ = zip(*self.imgs)\n        file_names = [re.search(r'[\\d]+.png',path).group() for path in paths]\n        self.targets = [image_scores[file_name] for file_name in file_names]\n        self.samples = self.imgs = list(zip(paths, self.targets))","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.937867Z","iopub.status.idle":"2022-06-06T13:03:33.938218Z","shell.execute_reply.started":"2022-06-06T13:03:33.938045Z","shell.execute_reply":"2022-06-06T13:03:33.938068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Create the datasets:","metadata":{}},{"cell_type":"code","source":"trainset = RegressionImageFolder(os.path.join(data_dir, 'images/train/'), labels_dict, transform=transform)\nvalidset = RegressionImageFolder(os.path.join(data_dir, 'images/valid/'), labels_dict, transform=transform)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.939232Z","iopub.status.idle":"2022-06-06T13:03:33.939570Z","shell.execute_reply.started":"2022-06-06T13:03:33.939387Z","shell.execute_reply":"2022-06-06T13:03:33.939414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load the datases:","metadata":{}},{"cell_type":"code","source":"batch_size = 32\n\ntrainloader = DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=1)\nvalidloader = DataLoader(validset, batch_size=batch_size, shuffle=True, num_workers=1)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.940772Z","iopub.status.idle":"2022-06-06T13:03:33.941115Z","shell.execute_reply.started":"2022-06-06T13:03:33.940938Z","shell.execute_reply":"2022-06-06T13:03:33.940960Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Inspect the first few images and labels:","metadata":{}},{"cell_type":"code","source":"samples, targets = next(iter(trainloader))\n\ngrid = torchvision.utils.make_grid(samples, nrow=8) \ngrid = grid.permute(1,2,0) * std_for_norm + mean_for_norm\n\nplt.figure(figsize=(15,15))\nplt.imshow(grid)\nplt.axis('off')\n\nprint('targets:\\n', targets.reshape(-1,8).numpy())","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.942164Z","iopub.status.idle":"2022-06-06T13:03:33.942488Z","shell.execute_reply.started":"2022-06-06T13:03:33.942311Z","shell.execute_reply":"2022-06-06T13:03:33.942334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.944412Z","iopub.status.idle":"2022-06-06T13:03:33.945023Z","shell.execute_reply.started":"2022-06-06T13:03:33.944778Z","shell.execute_reply":"2022-06-06T13:03:33.944804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load pretrained VGG16 model for transfer learning:","metadata":{}},{"cell_type":"code","source":"model = models.vgg16(pretrained=True)\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.946362Z","iopub.status.idle":"2022-06-06T13:03:33.946944Z","shell.execute_reply.started":"2022-06-06T13:03:33.946734Z","shell.execute_reply":"2022-06-06T13:03:33.946754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Freeze all feature extraction layers:","metadata":{}},{"cell_type":"code","source":"for param in model.features.parameters():\n    param.requires_grad = False","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.947989Z","iopub.status.idle":"2022-06-06T13:03:33.948548Z","shell.execute_reply.started":"2022-06-06T13:03:33.948361Z","shell.execute_reply":"2022-06-06T13:03:33.948382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Replace the output layer with a single node:","metadata":{}},{"cell_type":"code","source":"num_ftrs = model.classifier[6].in_features\nmodel.classifier[6] = nn.Linear(num_ftrs, 1)","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.949515Z","iopub.status.idle":"2022-06-06T13:03:33.950030Z","shell.execute_reply.started":"2022-06-06T13:03:33.949830Z","shell.execute_reply":"2022-06-06T13:03:33.949849Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Train the model:","metadata":{}},{"cell_type":"code","source":"def save_checkpoint(model, optimizer, epoch, avg_loss, train_rmse, val_rmse):\n    torch.save({\"model\": model.state_dict(),\n                \"optimizer\": optimizer.state_dict(),\n                \"epoch\": epoch,\n                \"avg_loss\": avg_loss,\n                \"train_rmse\": train_rmse,\n                \"val_rmse\": val_rmse\n               }, \"model.pt\")\nprint(\"Saved the checkpoint\")","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.950846Z","iopub.status.idle":"2022-06-06T13:03:33.951310Z","shell.execute_reply.started":"2022-06-06T13:03:33.951140Z","shell.execute_reply":"2022-06-06T13:03:33.951159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# move model to gpu if avaliable:\nmodel.to(device=device, dtype = torch.float32)\n\n# set hyperparameters:\nepochs = 1\nlr = 0.00001#0.0001#0.001\n\n# define loss and optimizer:\nloss_criterion = nn.MSELoss()\noptimizer = optim.Adam(model.parameters(), lr=lr) \n\ntrain_rmse_log = []\nval_rmse_log = []\npatience = 0\n\nif os.path.isdir('../input/imagerotationmodelcheckpoint'):\n    checkpoint = torch.load('../input/imagerotationmodelcheckpoint/model.pt', map_location=device)\n    model.load_state_dict(checkpoint['model'])\n    optimizer.load_state_dict(checkpoint['optimizer'])\n    model.eval()\nelse:\n    for epoch in range(epochs):\n\n        # training:\n        model.train()\n        total_loss = 0\n        for images, labels in tqdm(trainloader, desc=f'Epoch {epoch} Training'):\n\n            images = images.to(device=device, dtype = torch.float32)\n            labels = labels.to(device=device, dtype = torch.float32)\n\n            preds = model(images).squeeze()\n            loss = loss_criterion(preds, labels)\n\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n            total_loss += loss.item() * batch_size\n\n        avg_loss = total_loss/len(trainset)\n\n        train_rmse = np.sqrt(avg_loss)\n        train_rmse_log.append(train_rmse)\n\n\n        # evaluation:\n        model.eval()\n        with torch.no_grad():\n\n            valid_loss = 0\n            for images_valid, labels_valid in tqdm(validloader, desc=f'Epoch {epoch} Evaluation'):\n\n                images_valid = images_valid.to(device=device, dtype = torch.float32)\n                labels_valid = labels_valid.to(device=device, dtype = torch.float32)\n\n                pred_valid = model(images_valid).squeeze()\n                valid_loss += loss_criterion(pred_valid, labels_valid).item() * batch_size\n\n            avg_valid_loss = valid_loss/len(validset)\n\n            val_rmse = np.sqrt(avg_valid_loss)      \n            val_rmse_log.append(val_rmse)\n\n        print(f\"Epoch: {epoch} \\ntrain RMSE: {train_rmse:2.4}, validation RMSE: {val_rmse:2.4}\")\n\n        save_checkpoint(model, optimizer, epoch, avg_loss, train_rmse, val_rmse)\n        \n        # Break if overfitting occures\n        if (val_rmse - train_rmse) >= 1.0:\n            patience += 1\n            if patience > 2:\n                print(\"Overfitting has been detected - trainig will now stop\")\n                break\n        else:\n            patience = 0\n\n        # break if the rmse plateaus:\n        plateau_length = 15\n        if len(train_rmse_log) >= plateau_length:\n            range_last = max(train_rmse_log[-plateau_length:]) - min(train_rmse_log[-plateau_length:])\n            if range_last <= 0.5:\n                print(\"The model has reached a plateau - trainig will now stop\")\n                break","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.952172Z","iopub.status.idle":"2022-06-06T13:03:33.952588Z","shell.execute_reply.started":"2022-06-06T13:03:33.952425Z","shell.execute_reply":"2022-06-06T13:03:33.952443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.display import FileLink\nFileLink('./model.pt')","metadata":{"execution":{"iopub.status.busy":"2022-06-06T13:03:33.953374Z","iopub.status.idle":"2022-06-06T13:03:33.953778Z","shell.execute_reply.started":"2022-06-06T13:03:33.953616Z","shell.execute_reply":"2022-06-06T13:03:33.953633Z"},"trusted":true},"execution_count":null,"outputs":[]}]}