{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":29762,"databundleVersionId":2541532,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd \nimport os\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\nimport random\nimport cv2\nimport tensorflow as tf","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T23:00:45.863017Z","iopub.execute_input":"2025-12-07T23:00:45.863284Z","iopub.status.idle":"2025-12-07T23:01:03.915918Z","shell.execute_reply.started":"2025-12-07T23:00:45.863239Z","shell.execute_reply":"2025-12-07T23:01:03.915306Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path = '/kaggle/input/landmark-recognition-2021'\nos.listdir(path)\n\ntrain_images = f'{path}/train'\ndf_train = pd.read_csv(f'{path}/train.csv')\ndf_train['path'] = df_train['id'].apply(lambda f: os.path.join('/kaggle/input/landmark-recognition-2021/train', f[0], f[1], f[2], f + '.jpg'))\ndf_train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T23:07:58.113369Z","iopub.execute_input":"2025-12-07T23:07:58.114151Z","iopub.status.idle":"2025-12-07T23:08:02.143991Z","shell.execute_reply.started":"2025-12-07T23:07:58.114124Z","shell.execute_reply":"2025-12-07T23:08:02.143202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_images = f'{path}/test'\ndf_test = pd.read_csv(f'{path}/sample_submission.csv')\ndf_test['path'] = df_test['id'].apply(lambda f: os.path.join('/kaggle/input/landmark-recognition-2021/test', f[0], f[1], f[2], f + '.jpg'))\ndf_test.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T23:08:02.145179Z","iopub.execute_input":"2025-12-07T23:08:02.145429Z","iopub.status.idle":"2025-12-07T23:08:02.189081Z","shell.execute_reply.started":"2025-12-07T23:08:02.145412Z","shell.execute_reply":"2025-12-07T23:08:02.188504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Defining the amount of classes and images in the training dataset.\nnr_classes = len(df_train[\"landmark_id\"].unique())\nnr_images = len(df_train)\n\nprint(\"Number of classes in training dataset: \", nr_classes)\nprint(\"Number of images in training dataset: \", nr_images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T23:08:02.733509Z","iopub.execute_input":"2025-12-07T23:08:02.734212Z","iopub.status.idle":"2025-12-07T23:08:02.758164Z","shell.execute_reply.started":"2025-12-07T23:08:02.734191Z","shell.execute_reply":"2025-12-07T23:08:02.757433Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Histogram of data distribution, to show the amount of images in each class.\n\nhist = plt.figure(figsize = (10, 10))\nax = plt.hist(df_train[\"landmark_id\"], bins = df_train[\"landmark_id\"].unique())\nplt.ylim([0, 400])\nplt.show()\n\nclasses = ax[0]\nfrom0To5 = len(classes[classes <= 5])\nfrom5To10 = len(classes[classes <= 10] - from0To5)\nprint(\"Number of classes with 0 to 5 images: \", from0To5)\nprint(\"Number of classes with 5 to 10 images: \", from5To10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T20:11:28.053710Z","iopub.execute_input":"2025-12-07T20:11:28.053961Z","iopub.status.idle":"2025-12-07T20:12:54.801727Z","shell.execute_reply.started":"2025-12-07T20:11:28.053944Z","shell.execute_reply":"2025-12-07T20:12:54.801099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Overall representation of the data distribution\n\nprint('Distribution of images in classes:')\nvalue_counts = df_train['landmark_id'].value_counts()\nprint(value_counts.describe()) ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T21:00:46.727933Z","iopub.execute_input":"2025-12-07T21:00:46.728656Z","iopub.status.idle":"2025-12-07T21:00:46.762978Z","shell.execute_reply.started":"2025-12-07T21:00:46.728626Z","shell.execute_reply":"2025-12-07T21:00:46.761974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Visualization of some samples.\n\ndisplayImages = []\nfor i in range(0,4):\n    randomClass = df_train[df_train['landmark_id'] == value_counts.iloc[[np.random.randint(0, nr_classes)]].index[0]]\n    for j in range(0,4):\n        randomImages = randomClass.iloc[np.random.randint(0, len(randomClass))]\n        displayImages.append(randomImages)\n        \nplt.subplots(4, 4, figsize = (15, 10))\nfor i in range(len(displayImages)):\n    plt.subplot(4, 4, i + 1)\n    plt.axis('Off')\n    img = cv2.imread(displayImages[i][2])\n    plt.imshow(img)\n    plt.title(f'landmark id: {displayImages[i][1]} ', fontsize=8)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T21:00:50.196730Z","iopub.execute_input":"2025-12-07T21:00:50.197026Z","iopub.status.idle":"2025-12-07T21:00:52.790246Z","shell.execute_reply.started":"2025-12-07T21:00:50.197005Z","shell.execute_reply":"2025-12-07T21:00:52.789316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Setting up hyperparameters and splitting training data into train and val\ndef imagePath(imgPath):\n    images = []\n    for imgFile in imgPath:\n        imgPic = cv2.imread(imgFile, 1)\n        images.append(cv2.resize(imgPic, (img_size, img_size)))\n    \n    return images\n\n# Hyperparameters\nepochs = 20\nbatch_size = 32\nimg_size = 128\ntrain_split = 0.7\nval_split = 0.2\nnrClasses = 100\n\n# Setting up dataset for training\nimgList = []\nlabels = []\ntemp_labels = []\n\n# i = 0\n# for lbl in df_train['landmark_id'].unique():\n#     if i == nrClasses:\n#         break\n#     if(len(df_train['path'][df_train['landmark_id'] == lbl].value_counts()) > 200 and\n#        len(df_train['path'][df_train['landmark_id'] == lbl].value_counts()) < 400): \n#         for path in df_train['path'][df_train['landmark_id'] == lbl]: \n#             imgList.append(path) \n#             labels.append(lbl)\n#             temp_labels.append(i)\n#         i = i + 1\n\n\ni = 0\ncounts_df_train = df_train['landmark_id'].value_counts()\nsorted_label_counts = list(counts_df_train.index)\nfor lbl in sorted_label_counts[:nrClasses]:\n    for path in df_train['path'][df_train['landmark_id'] == lbl]: \n        imgList.append(path) \n        labels.append(lbl)\n        temp_labels.append(i)\n    i = i + 1\n\n\n# Random shuffle dataset, so it is no longer set up in classes\nshuff = list(zip(imgList, temp_labels))\nrandom.shuffle(shuff)\n\nimgList, lbls = zip(*shuff)\n\n# Preparing data to be split into train and val\nimgNr = round(len(imgList) * train_split)\n\ntrainImages = imgList[:imgNr]\ntrainData = imagePath(trainImages)\ntrainLabels = lbls[:imgNr]\n\nprint(\"Number of total images for training: \", len(trainData))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T23:08:10.786774Z","iopub.execute_input":"2025-12-07T23:08:10.787048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Setting images and labels to be split into train and val data\nxData = np.array(trainData)\nyData = tf.keras.utils.to_categorical(trainLabels, num_classes = nrClasses)\n\nx_train, x_val, y_train, y_val = train_test_split(xData, yData, test_size = val_split, random_state = 101)\n\nprint(\"Number of train images: \", len(x_train))\nprint(\"Number of val images: \", len(x_val))\n\n# Random Crop for Data Augmentation\ndef random_crop(image):  \n    height, width = image.shape[:2]\n    random_array = np.random.random(size=4);\n    w = int((width*0.5) * (1+random_array[0]*0.5))\n    h = int((height*0.5) * (1+random_array[1]*0.5))\n    x = int(random_array[2] * (width-w))\n    y = int(random_array[3] * (height-h))\n\n    image_crop = image[y:h+y, x:w+x, 0:3]\n    image_crop = cv2.resize(image_crop, image.shape)\n    return image_crop\n\n# Setting up data generator for data augmentation\ndataGenerator = tf.keras.preprocessing.image.ImageDataGenerator(horizontal_flip = False, # False for no flip, True for flip\n                                                                vertical_flip = False, # False for no flip, True for flip\n                                                                rotation_range = 0, # 0 for no rotation, 90 for rotation\n                                                                zoom_range = 0, # 0 for normal, [0.5, 1.5] for augmentation\n                                                                width_shift_range = 0, # 0 for normal, 0.3 for augmentation\n                                                                height_shift_range = 0, # 0 for normal, 0.3 for augmentation\n                                                                shear_range = 0, # 0 for normal, 45 for augmentation\n                                                                #brightness_range=(0.1, 0.9), # comment for no brightness change\n                                                                #preprocessing_function=random_crop, \n                                                                fill_mode = \"nearest\") # wrap, reflect and constant are some other for experimentation\n                                                 \n\nopt = tf.optimizers.SGD(learning_rate = 0.001) # 0.01\nopt2 = tf.optimizers.Adam(learning_rate = 0.001) #0.01\nopt3 = tf.optimizers.Adagrad(learning_rate = 0.001) #0.01","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CNN model \n# Resnet101 also be used and some other models as well\nResNet152 = tf.keras.applications.resnet.ResNet152(input_shape = (img_size, img_size, 3),\n                                                      include_top = False, #True\n                                                      weights = 'imagenet', \n                                                      pooling = 'avg') # max\n\nfor layer in ResNet152.layers:\n    layer.trainable = False\n\n\ninputs = ResNet152.input\nflatten = tf.keras.layers.Flatten()(ResNet152.output)\ndropout1 = tf.keras.layers.Dropout(0.2)(flatten)\ndense1 = tf.keras.layers.Dense(units = 4096, activation = \"relu\")(dropout1)\ndropout2 = tf.keras.layers.Dropout(0.2)(dense1)\ndense2 = tf.keras.layers.Dense(units = 4096, activation = \"relu\")(dropout2)\ndropout3 = tf.keras.layers.Dropout(0.2)(dense2)\noutput = tf.keras.layers.Dense(units = nrClasses, activation = \"softmax\")(dropout3) # activation = relu, linear, softmax\nmodel = tf.keras.Model(inputs = inputs, outputs = output)\n\nprint(model.summary())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compilation\n#model = ResNet50(input_shape = (128, 128, 3), classes = nrClasses) # should be used when using batch norm editable resnet\nmodel.compile(optimizer = opt2 , loss = \"categorical_crossentropy\", metrics = ['accuracy'])\n#print(model.summary()) # should be used when using batch norm editable resnet\nepochs = 20\nhistory = model.fit(dataGenerator.flow(x_train, y_train, batch_size = batch_size), validation_data = (x_val, y_val), epochs = epochs)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = tf.keras.models.load_model('/kaggle/working/GLR_model_ResNet152_ep20.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-07T21:01:24.591173Z","iopub.execute_input":"2025-12-07T21:01:24.591492Z","iopub.status.idle":"2025-12-07T21:01:27.441468Z","shell.execute_reply.started":"2025-12-07T21:01:24.591471Z","shell.execute_reply":"2025-12-07T21:01:27.440688Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting the performance of the model\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('ResNet152 Accuracy')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Val'], loc = 'upper left')\nplt.show()\n\n# Plot of loss\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('ResNet152 Loss')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Val'], loc = 'upper left')\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predictions of the model -> running model on test data\nfrom sklearn.metrics import precision_recall_fscore_support as score\nfrom sklearn import metrics\n\ntestImages = imgList[len(trainImages):]\ntestImages = imagePath(testImages)\ntestLabels = lbls [len(trainLabels):]\n\ntestData = np.array(testImages)\ntestPrediction = model.predict(dataGenerator.flow(testData, batch_size = batch_size))\n\ngoodAcc = []\nbadAcc = []\nconfidence = []\nfor i in testPrediction:\n    confidence.append(max(i))\n    goodAcc.append(np.argmax(i))\n    badAcc.append(np.argmax(i))\n\nprecision, recall, fscore, support = score(testLabels, goodAcc, labels = np.unique(goodAcc))\n\nprint(\"Confusion Matrix\")\nprint(metrics.confusion_matrix(testLabels, goodAcc))\nprint(\"Classification Report\")\nprint(metrics.classification_report(testLabels, goodAcc, digits = 3))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visulaise the best output of the model on the test data\nplm_count = 0\nfor i in range(len(goodAcc)):\n    plt.axis('Off')\n    if (testLabels[i] == goodAcc[i]):\n        print(\"Perfect Label Match\")\n        title = ('True label: ' + str(testLabels[i]) + '_' + 'Predicted label: ' + str(goodAcc[i]) + '_' + 'confidence: ' + str(confidence[i]))\n        plt.title(title, fontsize = 10)\n        plt.imshow(testImages[i])\n        plt.show()\n\n        plm_count += 1\n        \n    elif(confidence[i] < 0.1):\n        print(\"Poor Confidence\")\n        title = ('True label: ' + str(testLabels[i]) + '_' + 'Predicted label: ' + str(goodAcc[i]) + '_' + 'confidence: ' + str(confidence[i]))\n        plt.title(title, fontsize = 10)\n        plt.imshow(testImages[i])\n        plt.show()\n    if plm_count == 100:\n        break","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"GLR_model_ResNet152_ep20.h5\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}