{"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":"markdown","source":"# Training file Hotel Id assignment #\n*more info here*","metadata":{}},{"cell_type":"markdown","source":"# Imports, Setup, etc. #","metadata":{}},{"cell_type":"code","source":"# Imports:\n\n! pip install timm --no-index --find-links=file:///kaggle/input/timm-zipped/timm\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport torch\nimport torch.nn as nn\nimport timm #includes many pre-build models & tools.\n\n\n# Don't know what this does, so I'll just keep it there just in case...\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# File & Folder names (not all used atm):        \nPROJECT_FOLDER = \"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/\"\nDATA_FOLDER = \"../input/hotelid-2022-train-images-256x256/\"\nIMAGE_FOLDER = DATA_FOLDER + \"images/\"\nOUTPUT_FOLDER = \"\"\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image Processing","metadata":{}},{"cell_type":"code","source":"\n# Expecting Data sets and data loaders here!\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Auxiliariy functions","metadata":{}},{"cell_type":"code","source":"def save_checkpoint(model, scheduler, optimizer, epoch, name, loss=None, score=None):\n    checkpoint = {\"epoch\": epoch,\n                  \"model\": model.state_dict(),\n                  \"scheduler\": scheduler.state_dict(),\n                  \"optimizer\": optimizer.state_dict(),\n                  \"loss\": loss,\n                  \"score\": score,\n                  }\n\n    torch.save(checkpoint, f\"{OUTPUT_FOLDER}checkpoint-{name}.pt\")\n\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"DataSet","metadata":{}},{"cell_type":"code","source":"class HotelDataset(Dataset):\n    #Have to define a path parameter\n    \n    def __init__(self, root_dir=\"../input/hotel-id-to-combat-human-trafficking-2022-fgvc9/\", data_path=\"train_images/\", min_images=1, max_images=None, max_entries=None, transform=None):\n        MAX_WINDOWS_FILENAME_CHAR_LENGTH = 260\n        self.root_dir = root_dir\n        self.data_path = data_path\n        self.transform = transform\n        self.full_data_path = os.path.join(root_dir, data_path)\n        \n        #We expect the following structure:\n        #full_data_path contains only folders, each folder representing a hotel, and having an unique name\n        #inside the hotel folders there are only image files\n        \n        dirs = os.listdir(self.full_data_path)\n        self.num_labels = len(dirs)\n        self.total_files = 0\n        for directory in dirs:\n            files=os.listdir(os.path.join(self.full_data_path,directory))\n            files_number = len(files)\n            if max_images != None and files_number > max_images:\n                dirs.remove(directory)\n                continue\n            if files_number < min_images:\n                dirs.remove(directory)\n                continue\n            self.total_files += files_number\n            if max_entries != None and max_entries >= self.total_files:\n                dirs = dirs[:max_entries]\n                break\n        \n        self.hotel_data = np.chararray([self.total_files,2], itemsize=MAX_WINDOWS_FILENAME_CHAR_LENGTH)\n        iterator = 0\n        for directory in dirs:\n            files=os.listdir(os.path.join(self.full_data_path,directory))\n            for f in files:\n                self.hotel_data[iterator] = [f, directory]\n            \n    def __getitem__(self, index):\n        label = self.hotel_data[index,1].decode()\n        hotel_image_id = self.hotel_data[index,0].decode()\n        image_path = os.path.join(self.full_data_path, label, hotel_image_id)\n        \n        image = Image.open(image_path)\n        item = image\n        if self.transform != None:\n            item = self.transform(image)\n        return item, label\n        \n    def __len__(self):\n        return self.total_files","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"# Define model:\n\nclass Hotel_Model (nn.Module):\n    \n    def __init__(self, n_classes = 100, embeddings_size = 64):\n        super(EmbeddingModel, self).__init__()\n        \n        # use existing timm-model for bulk of work:\n        self.main_model = timm.create_model(backbone_name, num_classes=n_classes, pretrained=True)\n        \n        # Use own torch-layers for last embedding step and classification:\n        in_features = self.main_model.get_classifier().infeatures        \n        self.main_model.classifier = nn.Identity # turn off classifier in pre-trained model\n        self.embedding = nn.Linear(in_features, embedding_size)\n        \n        #Add own final layer and classifier !!! CHANGE THIS TO RETRIEVAL !!!\n        self.classifier = nn.Linear(embedding_size, n_classes) \n        \n\n    def embed_and_classify(self,x):\n        x = self.forward(x)\n        return x, self.classifier(x)\n    \n    def forward(self,x):\n        x = self.main_model(x)\n        x = x.view(x.size(0), -1) #re-scale\n        x = self.embedding(x)\n        return x","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training Cycle","metadata":{}},{"cell_type":"code","source":"def train_epoch(train_loader, optimizer, loss_fn, model):\n    \"\"\"Training and adjusting model for one epoch.\"\"\"\n    \n    for i, data in enumerate(train_loader): #loop over all data in current batch (in loader!)        \n        # Initialisation:\n        inputs, labels =  # Unpack data\n        optimizer = zero_grad()\n        \n        # Make prediction, compute loss\n        prediction = model(inputs)\n        loss = loss_fn(outputs, labels)\n        loss.backwards()\n        \n        # Adjust model \n        optimizer.step()\n        \n        # Gather data and stuff: TBW\n\ndef val_epoch(val_loader, loss_fn, model):\n    \"Compute validation data for one epoch\"\n    \n    # Just like train_epoch, but without adjusting the model...\n    total_vloss = 0.0\n    \n    for i, vdata in enumerate(val_loader):\n        inputs, labels = vdata\n        prediction = model(inputs)\n        vloss = loss_fn(prediction, labels)\n        total_vloss += vloss\n        \n    return total_vloss / (i+1)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_complete(train_loader, val_loader, model, optimizer, loss_fn, nmbr_epochs = 5):\n    \"\"\"Trains and Validates model given loaders and initial model.\"\"\"\n\n    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')\n    best_vloss = 10**9\n    for epoch in range(nmbr_epochs):\n        \n        # Train one epoch:\n        model.train(True)\n        train_epoch(train_loader, optimizer, loss_fn, model)\n        \n        # Validate epoch:\n        model.train(False)\n        vloss = val_epoch(val_loader,loss_fn, model)\n        \n        # Save current model:\n        save_checkpoint(model)        ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(test_loader, model):\n    \"\"\"Function to predict the 5 most likely hotels\"\"\"\n    \n    # For now, this is just the most likely hotel, 5 times\n    nmbr_predictions = 1 # Part of loader I assume?\n    predictions = []\n    for i,input in enumerate(test_loader):\n        prediction = model(input)\n        #print(prediction) # I'm guessing this is just one prediction, not probs for all. If so, how do we get these?\n        predictions.append(prediction)\n    \n    return predictions","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Running the code & Making submission:","metadata":{}},{"cell_type":"code","source":"# Define model & Datasets\nmodel = Hotel_Model()\ntrain_loader, val_loader, test_loader = ... #TBW\n\n# Optimizer & Loss function: Placeholders for now:\n\noptimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum = 0.9)\n\nloss_fn = torch.nn.CrossEntropyLoss","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train model:\n\nnmbr_epochs = 1\ntrain_complete(train_loader, val_loader, model, optimizer, loss_fn, nmbr_epochs)\n\n# Make prediction:\n\nfinal_preds = predict(test_loader, model)\nfinal_preds = final_preds # Transform to correct naming: these differ slightly I though, Nicolay?\n\nfinal_preds.to_csv(OUTPUT_FOLDER + 'submission.csv', index=False)","metadata":{},"execution_count":null,"outputs":[]}]}