{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.10","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":30121,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport cv2\nfrom keras.applications import VGG19\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten, GlobalAveragePooling2D\nfrom keras.optimizers import Adam\nfrom sklearn.preprocessing import LabelEncoder\nfrom keras.utils import Sequence\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau, Callback\nfrom keras.applications.vgg19 import preprocess_input\nimport matplotlib.pyplot as plt\n\n# Load dataset\nfile_path = '/kaggle/input/landmark-recognition-2021/train.csv'\ndf = pd.read_csv(file_path)\n\n# Filter top 50 landmarks\ntop_50_landmarks = df['landmark_id'].value_counts().head(10)  # Changed to 50 landmarks\ntop_50_landmark_ids = top_50_landmarks.index\nfiltered_df = df[df['landmark_id'].isin(top_50_landmark_ids)]\n\n# Re-encode labels to 0-49\nlabel_encoder = LabelEncoder()\nfiltered_df['encoded_landmark_id'] = label_encoder.fit_transform(filtered_df['landmark_id'])\n\n# Split dataset into train and validate\ntrain_df, validate_df = np.split(filtered_df.sample(frac=1), [int(.8 * len(filtered_df))])\nprint(f\"Training on {len(train_df)} samples\")\nprint(f\"Validation on {len(validate_df)} samples\")\n\n# Global variables\nbatch_size = 1  # Increased batch size for better performance\nepochs = 7\nbase_path = \"/kaggle/input/landmark-recognition-2021/train\"  # Update path to dataset\nimage_size = (224, 224)\n\n# Custom data generator with MixUp and CutMix\nclass LandmarkDataGenerator(Sequence):\n    def __init__(self, dataframe, batch_size, base_path, target_size=(224, 224), shuffle=True, mixup_alpha=0.2, cutmix_alpha=0.2):\n        self.dataframe = dataframe\n        self.batch_size = batch_size\n        self.base_path = base_path\n        self.target_size = target_size\n        self.shuffle = shuffle\n        self.mixup_alpha = mixup_alpha\n        self.cutmix_alpha = cutmix_alpha\n        self.indices = np.arange(len(self.dataframe))\n        if self.shuffle:\n            np.random.shuffle(self.indices)\n\n    def __len__(self):\n        return int(np.ceil(len(self.dataframe) / self.batch_size))\n\n    def __getitem__(self, idx):\n        start = idx * self.batch_size\n        end = min((idx + 1) * self.batch_size, len(self.dataframe))\n        indices = self.indices[start:end]\n        image_array = []\n        label_array = []\n        for i in indices:\n            fname, label = self.dataframe.iloc[i][['id', 'encoded_landmark_id']]\n            fname += \".jpg\"\n            f1, f2, f3 = fname[0], fname[1], fname[2]\n            path = os.path.join(self.base_path, f1, f2, f3, fname)\n            img = cv2.imread(path)\n            if img is not None:\n                img = cv2.resize(img, self.target_size)\n                img = preprocess_input(img)  # Use VGG19 preprocessing\n                image_array.append(img)\n                label_array.append(label)\n            else:\n                continue  # Skip corrupted images\n\n        # Convert lists to numpy arrays\n        image_array = np.array(image_array)\n        label_array = np.array(label_array)\n\n        # Apply MixUp or CutMix\n        if self.mixup_alpha > 0:\n            image_array, label_array = self.apply_mixup(image_array, label_array)\n\n        if self.cutmix_alpha > 0:\n            image_array, label_array = self.apply_cutmix(image_array, label_array)\n\n        return image_array, label_array\n\n    def on_epoch_end(self):\n        if self.shuffle:\n            np.random.shuffle(self.indices)\n\n    def apply_mixup(self, images, labels):\n        \"\"\"Applies MixUp augmentation.\"\"\"\n        lam = np.random.beta(self.mixup_alpha, self.mixup_alpha)\n        batch_size = images.shape[0]\n        index = np.random.permutation(batch_size)\n\n        mixed_images = lam * images + (1 - lam) * images[index]\n        mixed_labels = lam * labels + (1 - lam) * labels[index]\n        return mixed_images, mixed_labels\n\n    def apply_cutmix(self, images, labels):\n        \"\"\"Applies CutMix augmentation.\"\"\"\n        batch_size = images.shape[0]\n        index = np.random.permutation(batch_size)\n        lam = np.random.beta(self.cutmix_alpha, self.cutmix_alpha)\n\n        # Randomly select a region to cut and mix\n        h, w, _ = images.shape[1:]\n        cx = np.random.randint(w)\n        cy = np.random.randint(h)\n        bw = int(np.sqrt(1 - lam) * w)\n        bh = int(np.sqrt(1 - lam) * h)\n\n        # Define the cutout box\n        x1 = np.clip(cx - bw // 2, 0, w)\n        y1 = np.clip(cy - bh // 2, 0, h)\n        x2 = np.clip(cx + bw // 2, 0, w)\n        y2 = np.clip(cy + bh // 2, 0, h)\n\n        # Cut and paste\n        images[:, y1:y2, x1:x2, :] = images[index, y1:y2, x1:x2, :]\n        labels = lam * labels + (1 - lam) * labels[index]\n\n        return images, labels\n\n# Create data generators with MixUp and CutMix\ntrain_generator = LandmarkDataGenerator(train_df, batch_size, base_path, target_size=image_size, mixup_alpha=0.2, cutmix_alpha=0.2)\nvalidate_generator = LandmarkDataGenerator(validate_df, batch_size, base_path, target_size=image_size, shuffle=False, mixup_alpha=0, cutmix_alpha=0)\n\n# Build the VGG19 model with GlobalAveragePooling and additional fine-tuning\nsource_model = VGG19(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\nfor layer in source_model.layers[:15]:\n    layer.trainable = False  # Freeze the first 15 layers of VGG19\n\nmodel = Sequential([\n    *source_model.layers,\n    GlobalAveragePooling2D(),  # Change to GlobalAveragePooling\n    Dense(256, activation='relu'),\n    Dropout(0.03),\n    Dense(10, activation='softmax')  # Change to 50 classes for top 50 landmarks\n])\n\n# Compile the model with a higher learning rate and scheduler\nmodel.compile(optimizer=Adam(learning_rate=0.00001),\n              loss='sparse_categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Custom EarlyStopping based on accuracy > 90% and convergence of training and validation loss\nclass CustomEarlyStopping(Callback):\n    def __init__(self, accuracy_threshold=0.90, patience=5):\n        super().__init__()\n        self.accuracy_threshold = accuracy_threshold\n        self.patience = patience\n        self.best_loss = float('inf')\n        self.wait = 0\n\n    def on_epoch_end(self, epoch, logs=None):\n        logs = logs or {}\n        val_loss = logs.get('val_loss')\n        train_loss = logs.get('loss')\n        accuracy = logs.get('accuracy')\n\n        if accuracy >= self.accuracy_threshold and abs(train_loss - val_loss) < 0.01:\n            self.wait += 1\n            if self.wait >= self.patience:\n                print(f\"\\nEarly stopping triggered at epoch {epoch+1}\")\n                self.model.stop_training = True\n        else:\n            self.wait = 0  # Reset wait if condition is not met\n\n# Add this custom early stopping to the list of callbacks\ncustom_early_stopping = CustomEarlyStopping(accuracy_threshold=0.90, patience=3)\n\n# Add callbacks for saving the best model, early stopping, and learning rate scheduler\ncheckpoint = ModelCheckpoint(\"best_model_50_landmarks.h5\", save_best_only=True, monitor='val_accuracy', mode='max')\nearly_stopping = EarlyStopping(monitor='val_accuracy', patience=5, restore_best_weights=True)\nlr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6)\n\n# Train the model with the custom early stopping\nhistory = model.fit(\n    train_generator,\n    validation_data=validate_generator,\n    epochs=epochs,\n    callbacks=[checkpoint, custom_early_stopping, lr_scheduler],\n    verbose=1  # This enables live accuracy and loss display\n)\n\n# Save the final model\nmodel.save(\"Top50LandmarksModel.h5\")\n\n# Plot training and validation accuracy\nplt.figure(figsize=(8, 6))\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.title('Model Accuracy')\nplt.legend()\nplt.show()\n\n# Plot training and validation loss\nplt.figure(figsize=(8, 6))\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.title('Model Loss')\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-03T00:39:25.649566Z","iopub.execute_input":"2024-12-03T00:39:25.649865Z","iopub.status.idle":"2024-12-03T01:07:19.027162Z","shell.execute_reply.started":"2024-12-03T00:39:25.649840Z","shell.execute_reply":"2024-12-03T01:07:19.026302Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport pandas as pd\nimport os\nfrom keras.applications.vgg19 import preprocess_input\nfrom keras.models import load_model\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\n\n# Load the model\nmodel = load_model(\"Top50LandmarksModel.h5\")\n\n# Load train.csv for actual landmark ids (for top 10 landmarks)\ntrain_csv_path = '/kaggle/input/landmark-recognition-2021/train.csv'\ndf = pd.read_csv(train_csv_path)\n\n# Filter top 10 landmarks based on frequency\ntop_10_landmarks = df['landmark_id'].value_counts().head(10)  # Top 10 landmarks\ntop_10_landmark_ids = top_10_landmarks.index\ndf_top_10 = df[df['landmark_id'].isin(top_10_landmark_ids)]\n\n# Initialize label encoder to map back from encoded labels to actual landmark IDs\nlabel_encoder = LabelEncoder()\nlabel_encoder.fit(df_top_10['landmark_id'].values)\n\n# Function to get the actual landmark ID from train.csv for the given image ID\ndef get_actual_landmark(image_id):\n    actual_landmark_id = df_top_10.loc[df_top_10['id'] == image_id, 'landmark_id'].values\n    return actual_landmark_id[0] if len(actual_landmark_id) > 0 else None\n\n# Function to predict landmark from image\ndef predict_landmark(image_name):\n    # Build the full image path assuming the structure is in subfolders\n    f1, f2, f3 = image_name[0], image_name[1], image_name[2]\n    image_path = os.path.join('/kaggle/input/landmark-recognition-2021/train', f1, f2, f3, image_name + '.jpg')\n    \n    # Read and preprocess the image\n    img = cv2.imread(image_path)\n    img = cv2.resize(img, (224, 224))  # Resize image to the required size\n    img_display = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # Convert to RGB for display\n    img = preprocess_input(img)  # Preprocess the image for VGG19\n    \n    # Expand dimensions to match the input shape of the model\n    img = np.expand_dims(img, axis=0)\n    \n    # Get the prediction from the model\n    prediction = model.predict(img)\n    \n    # Get the predicted landmark ID (the index of the max value in the prediction array)\n    predicted_landmark_id = np.argmax(prediction, axis=1)[0]\n    \n    # Map the predicted encoded label back to the actual landmark ID\n    predicted_actual_landmark_id = label_encoder.inverse_transform([predicted_landmark_id])[0]\n    \n    # Get the actual landmark ID from train.csv using the image's ID\n    actual_landmark_id = get_actual_landmark(image_name)\n    \n    return image_name, predicted_actual_landmark_id, actual_landmark_id, img_display\n\n# Input: Provide the image name (without the '.jpg' extension)\nimage_name = '0a7c7d7c0ce4e3a6'  # Replace with the actual image name (e.g., '00001')\n\n# Get the result\nimage_id, predicted_actual_landmark_id, actual_landmark_id, img_display = predict_landmark(image_name)\n\n# Print the result\nprint(f\"Image ID: {image_id}\")\nprint(f\"Predicted Landmark ID: {predicted_actual_landmark_id}\")\nprint(f\"Actual Landmark ID: {actual_landmark_id}\")\n\n# Display the image\nplt.figure(figsize=(6, 6))\nplt.imshow(img_display)\nplt.title(f\"Predicted Landmark ID: {predicted_actual_landmark_id}\\nActual Landmark ID: {actual_landmark_id}\")\nplt.axis('off')  # Hide axes\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-03T02:31:10.379306Z","iopub.execute_input":"2024-12-03T02:31:10.379601Z","iopub.status.idle":"2024-12-03T02:31:12.257776Z","shell.execute_reply.started":"2024-12-03T02:31:10.379578Z","shell.execute_reply":"2024-12-03T02:31:12.256951Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport pandas as pd\nimport os\nfrom keras.applications.vgg19 import preprocess_input\nfrom keras.models import load_model\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\nfrom zipfile import ZipFile\nimport tensorflow as tf\n\n# Check if TensorFlow is using GPU\nprint(\"Num GPUs Available: \", len(tf.config.experimental.list_physical_devices('GPU')))\ntf.debugging.set_log_device_placement(True)  # This will log device placement, helpful for debugging\n\n# Load the model\nmodel = load_model(\"Top50LandmarksModel.h5\")\n\n# Load train.csv for actual landmark ids (for top 10 landmarks)\ntrain_csv_path = '/kaggle/input/landmark-recognition-2021/train.csv'\ndf = pd.read_csv(train_csv_path)\n\n# Filter top 10 landmarks based on frequency\ntop_10_landmarks = df['landmark_id'].value_counts().head(10)  # Top 10 landmarks\ntop_10_landmark_ids = top_10_landmarks.index\ndf_top_10 = df[df['landmark_id'].isin(top_10_landmark_ids)]\n\n# Initialize label encoder to map back from encoded labels to actual landmark IDs\nlabel_encoder = LabelEncoder()\nlabel_encoder.fit(df_top_10['landmark_id'].values)\n\n# Create a zip file to store images\nzip_filename = \"top_10_landmarks_images.zip\"\nwith ZipFile(zip_filename, 'w') as zipf:\n    \n    # Function to get the actual landmark ID from train.csv for the given image ID\n    def get_actual_landmark(image_id):\n        actual_landmark_id = df_top_10.loc[df_top_10['id'] == image_id, 'landmark_id'].values\n        return actual_landmark_id[0] if len(actual_landmark_id) > 0 else None\n\n    # Function to predict landmark from image\n    def predict_landmark(image_name):\n        # Build the full image path assuming the structure is in subfolders\n        f1, f2, f3 = image_name[0], image_name[1], image_name[2]\n        image_path = os.path.join('/kaggle/input/landmark-recognition-2021/train', f1, f2, f3, image_name + '.jpg')\n        \n        # Read and preprocess the image\n        img = cv2.imread(image_path)\n        img = cv2.resize(img, (224, 224))  # Resize image to the required size\n        img_display = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # Convert to RGB for display\n        img = preprocess_input(img)  # Preprocess the image for VGG19\n        \n        # Expand dimensions to match the input shape of the model\n        img = np.expand_dims(img, axis=0)\n        \n        # Get the prediction from the model\n        prediction = model.predict(img)\n        \n        # Get the predicted landmark ID (the index of the max value in the prediction array)\n        predicted_landmark_id = np.argmax(prediction, axis=1)[0]\n        \n        # Map the predicted encoded label back to the actual landmark ID\n        predicted_actual_landmark_id = label_encoder.inverse_transform([predicted_landmark_id])[0]\n        \n        # Get the actual landmark ID from train.csv using the image's ID\n        actual_landmark_id = get_actual_landmark(image_name)\n        \n        return image_name, predicted_actual_landmark_id, actual_landmark_id, img_display\n\n    # Loop over top 10 landmarks and get all images for each\n    for landmark_id in top_10_landmark_ids:\n        # Filter the dataframe for the current landmark\n        landmark_df = df_top_10[df_top_10['landmark_id'] == landmark_id]\n        \n        for image_name in landmark_df['id']:\n            # Get the result for each image\n            image_id, predicted_actual_landmark_id, actual_landmark_id, img_display = predict_landmark(image_name)\n\n            # Save image to a temporary file\n            img_filename = f\"{image_id}_pred_{predicted_actual_landmark_id}_actual_{actual_landmark_id}.jpg\"\n            img_path = f\"/kaggle/working/{img_filename}\"\n            cv2.imwrite(img_path, cv2.cvtColor(img_display, cv2.COLOR_RGB2BGR))\n            \n            # Add the image to the zip file\n            zipf.write(img_path, img_filename)\n\n            # Optionally, delete the temporary image file after adding it to the zip\n            os.remove(img_path)\n\nprint(f\"Zip file created: {zip_filename}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-03T02:34:40.241149Z","iopub.execute_input":"2024-12-03T02:34:40.241491Z","iopub.status.idle":"2024-12-03T02:50:28.031789Z","shell.execute_reply.started":"2024-12-03T02:34:40.241462Z","shell.execute_reply":"2024-12-03T02:50:28.030883Z"}},"outputs":[],"execution_count":null}]}