{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm.notebook import tqdm as tqdm\nfrom collections import Counter\n\nimport cv2\nimport copy\nimport csv\nimport os\nimport pathlib\nimport random\n\n# Assign paths\ninput_path = os.path.join('..', 'input')\ndataset_path = os.path.join(input_path, 'landmark-recognition-2020')\ntrain_path = os.path.join(dataset_path, 'train')\ntest_path = os.path.join(dataset_path, 'test')\ntrain_csv_path = os.path.join(dataset_path, 'train.csv')\nsubmission_csv_path = os.path.join(dataset_path, 'sample_submission.csv')\n\n# Load data to dataframe\ntrain = pd.read_csv(train_csv_path)\nsubmission = pd.read_csv(submission_csv_path)\n\nprint(\"Training dataset has {} images\".format(train.shape[0]))\nprint(\"Submission dataset has {} rows and {} columns \\n\".format(submission.shape[0],submission.shape[1]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create dictionary and load image data\ndata_label_dict = {'image_name': [], 'landmark_id': [], 'image_dir': []}\nfor i in tqdm(range(train.shape[0])):\n    data_label_dict['image_name'].append(train['id'][i])\n    data_label_dict['landmark_id'].append(train['landmark_id'][i])\n    train_image_dir = \"{}/{}/{}/{}/{}.jpg\".format(train_path,train['id'][i][0],train['id'][i][1],train['id'][i][2],train['id'][i])\n    data_label_dict['image_dir'].append(train_image_dir)\n\n# Convert to dataframe\ntrain_pathlabel = pd.DataFrame(data_label_dict)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_pathlabel = pd.DataFrame(data_label_dict)\n# Reduce set to input \ntop_classes = 1000\nid_sorted = train_pathlabel.landmark_id.values\ncount = Counter(id_sorted).most_common(top_classes)\nkeep_classes = [i[0] for i in count]\ntemp_train = train_pathlabel[train_pathlabel.landmark_id.isin(keep_classes)]\ntemp_train = temp_train.sample(frac=1).reset_index(drop=True)\n\nvalue_count = pd.DataFrame(temp_train.landmark_id.value_counts())\nvalue_count.reset_index(inplace=True) \nvalue_count.columns=['landmark_id','count']\n\n\ntop = value_count.landmark_id.iloc[0]\ntop_only = temp_train.loc[temp_train.landmark_id.isin([top])]\n\nreduced_top = top_only[:1000]\n\nwithout = temp_train[temp_train.landmark_id != top]\nfinal_train = pd.concat([without, reduced_top], ignore_index=True)\n\nfinal_train = final_train.sample(frac=1).reset_index(drop=True)\ntrain_pathlabel = final_train\n# Save to csv\ntrain_pathlabel.to_csv(\"train_data.csv\",index=False)\ntrain_pathlabel","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Variables from data\nnr_classes = train_pathlabel.landmark_id.nunique()\nnr_image_dir = train_pathlabel.image_dir.nunique()\nclass_count = train_pathlabel.landmark_id.value_counts()\nworst_classes = train_pathlabel.landmark_id.min()\n\nprint(\"There are {} images from {} classes in the training dataset\".format(nr_image_dir, nr_classes))\nprint(\"Minimum images in each class {}\".format(worst_classes))\n\nprint(\"\\nClasses with 5 or less images in full set:\", (train.landmark_id.value_counts().between(0,5)).sum()) \nprint(\"Classes with between 5 and 10 images in full set:\", (train.landmark_id.value_counts().between(5,10)).sum())\n\nprint(\"\\nClasses with 5 or less images in reduced set:\", (class_count.between(0,5)).sum()) \nprint(\"Classes with between 5 and 10 images in reduced set:\", (class_count.between(5,10)).sum())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Histogram of full set in custom bins\nplt.figure(figsize = (12, 8))\nplot = pd.DataFrame(train.landmark_id.value_counts())\n\norder = ['1-5','5-10','10-50','50-100','100-200','200-500','>=500']\nplot['Number of images'] = np.where(plot['landmark_id']>=500,'>=500',plot['landmark_id'])\nplot['Number of images'] = np.where((plot['landmark_id']>=200) & (plot['landmark_id']<500),'200-500',plot['Number of images'])\nplot['Number of images'] = np.where((plot['landmark_id']>=100) & (plot['landmark_id']<200),'100-200',plot['Number of images'])\nplot['Number of images'] = np.where((plot['landmark_id']>=50) & (plot['landmark_id']<100),'50-100',plot['Number of images'])\nplot['Number of images'] = np.where((plot['landmark_id']>=10) & (plot['landmark_id']<50),'10-50',plot['Number of images'])\nplot['Number of images'] = np.where((plot['landmark_id']>=5) & (plot['landmark_id']<10),'5-10',plot['Number of images'])\nplot['Number of images'] = np.where((plot['landmark_id']>=0) & (plot['landmark_id']<5),'1-5',plot['Number of images'])\n\nplot['Number of images'].value_counts().loc[order].plot(kind = 'bar', width = 0.8)\nplt.xlabel('Number of images')\nplt.ylabel('Classes')\nplt.title('Distribution of classes')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Sort full set and plot the top input classes\nfig = plt.figure(figsize = (12,8))\n\nsns.countplot(x=train_pathlabel.landmark_id, order = train_pathlabel.landmark_id.value_counts().sort_values(ascending=False).iloc[:100].index)\n\nplt.xlabel(\"LandMark Id\")\nplt.ylabel(\"Frequency\")\nplt.xticks(rotation=90)\nplt.title(\"Top 100 Classes in the Dataset\")\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Show four random images from reduced set\nimages = []\n\nfor i in range(4):\n    img = cv2.imread(random.choice(train_pathlabel.image_dir))   \n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    images.append(img)\n\nf, ax = plt.subplots(2,2, figsize=(20,15))\nfor i, img in enumerate(images):        \n        ax[i//2, i%2].imshow(img)\n        ax[i//2, i%2].axis('off')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---"},{"metadata":{},"cell_type":"markdown","source":"# Model training"},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport keras\nfrom tensorflow.keras import activations\nfrom keras.layers import Input, Dense, Conv2D, MaxPool2D, Flatten, BatchNormalization, Dropout\nfrom keras.models import Model, Sequential\nfrom keras.applications.vgg16 import preprocess_input, decode_predictions\nfrom keras.applications import VGG16\nfrom keras.optimizers import SGD, Adam\nfrom keras.preprocessing import image\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import ModelCheckpoint\nfrom keras.utils.np_utils import to_categorical\n\ndata_csv_path = \"./train_data.csv\"\ndata = pd.read_csv(data_csv_path, dtype=str)\n\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Set up parameters\nbatch_size = 128\nvalidation_split = 0.2\nimg_height = 64\nimg_width = 64\ntarget_shape=(img_height,img_width)\n\n# Create generators\ntrain_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)\nvalidation_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)\n\n# Load images to generator\ntrain_generator = train_datagen.flow_from_dataframe(\n        dataframe = data,\n        x_col = \"image_dir\",\n        y_col = \"landmark_id\",\n        target_size = target_shape,\n        color_mode = \"rgb\",\n        class_mode = \"categorical\",\n        batch_size = batch_size,\n        subset = 'training'\n)\n\nvalidation_generator = validation_datagen.flow_from_dataframe(\n        dataframe = data,\n        x_col = \"image_dir\",\n        y_col = \"landmark_id\",\n        target_size = target_shape,\n        shuffle = False,\n        color_mode = \"rgb\",\n        class_mode = \"categorical\",\n        batch_size = 1,\n        subset = 'validation'\n) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Model and training Parameters\nepochs          = 15\nepoch_shuffle   = False\nsteps_per_epoch = train_generator.samples // batch_size\nvalid_per_epoch = validation_generator.samples // batch_size\nloss_func   = \"categorical_crossentropy\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = keras.models.load_model(\"../input/rvgismodel/model.h5\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Predict on every sample in validation set\npredict = model.predict(validation_generator, steps = validation_generator.samples)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create variables from prediction data and labels from generator\npredicted_class_indices = np.argmax(predict,axis=1)\nclass_prob = np.max(predict, axis=1)\n\nlabels = validation_generator.class_indices\nval_classes = validation_generator.classes\nimage_paths = validation_generator.filenames\n\nlabels_dict = dict((v,k) for k,v in labels.items())\npredictions = [labels_dict[k] for k in predicted_class_indices]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create dictionaries and load data\ngood_dict = {'image_path': [], 'landmark_id': [], 'prediction':[], 'probability':[]}\nbad_dict = {'image_path': [], 'landmark_id': [], 'prediction':[], 'probability':[]}\nall_dict = {'image_path': [], 'landmark_id': [], 'prediction':[], 'probability':[]}\n\nfor i in range(predict.shape[0]):\n    all_dict['image_path'].append(image_paths[i])\n    all_dict['landmark_id'].append(labels_dict[val_classes[i]])\n    all_dict['prediction'].append(predictions[i])\n    all_dict['probability'].append(class_prob[i])\n        \n    if val_classes[i] == predicted_class_indices[i]:\n        good_dict['image_path'].append(image_paths[i])\n        good_dict['landmark_id'].append(labels_dict[val_classes[i]])\n        good_dict['prediction'].append(predictions[i])\n        good_dict['probability'].append(class_prob[i])\n    else:\n        bad_dict['image_path'].append(image_paths[i])\n        bad_dict['landmark_id'].append(labels_dict[val_classes[i]])\n        bad_dict['prediction'].append(predictions[i])\n        bad_dict['probability'].append(class_prob[i])\n\n# Save to dataframe        \ngood_preds = pd.DataFrame(good_dict)\nbad_preds = pd.DataFrame(bad_dict)\nall_preds = pd.DataFrame(all_dict)\nperc = len(good_preds.landmark_id)/len(all_preds.landmark_id)*100\n\nprint(\"Predictions: {}\".format(len(all_preds.landmark_id)))\nprint(\"Correct predictions: {}\".format(len(good_preds.landmark_id)))\nprint(\"Icorrect predictions: {}\".format(len(bad_preds.landmark_id)))\nprint(\"Correct percentage: {}%\".format(perc))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Count instances of predictions\nbest_preds = pd.DataFrame(good_preds.landmark_id.value_counts())\nbest_preds.reset_index(inplace=True) \nbest_preds.columns=['landmark_id','correct']\n\nworst_preds = pd.DataFrame(bad_preds.landmark_id.value_counts())\nworst_preds.reset_index(inplace=True) \nworst_preds.columns=['landmark_id','incorrect']\n\nall_preds_count = pd.DataFrame(all_preds.landmark_id.value_counts())\nall_preds_count.reset_index(inplace=True) \nall_preds_count.columns=['landmark_id','predictions']\n\n# Merge datasets\nmerged = all_preds_count.merge(best_preds, how='left', left_on='landmark_id', right_on='landmark_id')\nmerged_all = merged.merge(worst_preds, how='left', left_on='landmark_id', right_on='landmark_id')\n\nmerged_all.fillna(0, inplace=True)\n\n# Compute ratios\nmerged_all = merged_all.assign(correct_ratio = (merged_all.correct/merged_all.predictions)*100)\nmerged_all = merged_all.assign(incorrect_ratio = (merged_all.incorrect/merged_all.predictions)*100)\nmerged_all.correct_ratio = merged_all.correct_ratio.round(decimals=2)\nmerged_all.incorrect_ratio = merged_all.incorrect_ratio.round(decimals=2)\n\nmerged_all.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Dataframe sorted based on highest ratio of correct predictions\nbest_ratio = merged_all.sort_values('correct_ratio', ascending=False)\nbest_ratio.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Dataframe sorted based on highest ratio of incorrect predictions\nworst_ratio = merged_all.sort_values('incorrect_ratio', ascending=False)\nworst_ratio.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Show 6 images with correct classification\ngood_images = []\ngood_names = []\ngood_probs = []\ngood_img_df = good_preds.sample(n=6)\n\nfor i in range(6):\n    img = cv2.imread(good_img_df.image_path.iloc[i])   \n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    good_images.append(img)\n    good_names.append(good_img_df.landmark_id.iloc[i])\n    good_probs.append(good_img_df.probability.iloc[i])\n\nf, ax = plt.subplots(2,3, figsize=(20,15))\nfor i, img in enumerate(good_images):        \n        ax[i//3, i%3].imshow(img)\n        ax[i//3, i%3].axis('off')\n        ax[i//3, i%3].set_title(\"Landmark: {} | Prob: {}\".format(good_names[i], np.round(good_probs[i], 3)))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Show 6 images with incorrect classification\nbad_images = []\nbad_names = []\nbad_probs = []\nimg_df = bad_preds.sample(n=6)\n\nfor i in range(6):\n    img = cv2.imread(img_df.image_path.iloc[i])   \n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    bad_images.append(img)\n    bad_names.append(img_df.landmark_id.iloc[i])\n    bad_probs.append(img_df.probability.iloc[i])\n\nf, ax = plt.subplots(2,3, figsize=(20,15))\nfor i, img in enumerate(bad_images):        \n        ax[i//3, i%3].imshow(img)\n        ax[i//3, i%3].axis('off')\n        ax[i//3, i%3].set_title(\"Landmark: {} | Prob: {}\".format(bad_names[i], np.round(bad_probs[i], 3)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"---"},{"metadata":{},"cell_type":"markdown","source":"# Test and submit"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create test dictionary and load to dataframe \ntest_dict = {'filename': []}\nfor i in tqdm(range(submission.shape[0])):\n    test_dict['filename'].append(\n        test_path + '/' +\n        submission['id'][i][0] + '/' + submission['id'][i][1]+ '/' +\n        submission['id'][i][2]+ '/' + submission['id'][i] + \".jpg\")\n\ntest_pathlabel = pd.DataFrame(test_dict)\ntest_pathlabel.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create test generator and load images\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\ntest_generator = test_datagen.flow_from_dataframe(\n        dataframe = test_pathlabel,\n        x_col=\"filename\",\n        y_col=None,\n        target_size = target_shape,\n        shuffle = False,\n        color_mode = \"rgb\",\n        class_mode = None,\n        batch_size = 1,\n        validate_filenames=False\n) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Predict on every sample\ntest_predict = model.predict(test_generator, steps = test_generator.samples)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load prediction data and write to submission file\nfor i in tqdm(range(len(test_pathlabel))):\n    class_predicted = np.argmax(test_predict[i], axis=0)\n    class_prob = np.max(test_predict[i], axis=0)\n    test_pathlabel.loc[i, \"landmarks\"] = str(class_predicted)+\" \"+str(class_prob)\ntest_pathlabel = test_pathlabel.drop(columns=\"filename\")\ntest_pathlabel.to_csv(\"submission.csv\", index=False)\ntest_pathlabel","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}