{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Import required libraries "},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport random\nimport os\nimport cv2\nimport multiprocessing\nimport tensorflow.keras.layers as L\nfrom PIL import Image\nfrom collections import Counter\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential\nfrom keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom keras.layers import Activation, Dropout, Flatten, Dense, BatchNormalization\nfrom keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D\nfrom keras.utils import to_categorical\nfrom keras import Model\nfrom keras.callbacks import ModelCheckpoint\nfrom keras.models import load_model\nfrom tensorflow.keras.applications.xception import Xception\nfrom keras.optimizers import Adam \nfrom keras.optimizers import Adagrad\n\n\n\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test GPU availability and find available CPU threads"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Finding available CPU threads just in case i have to use multiprocessing in the training. Just in case i run out of GPU time. \nfrom tensorflow.python.client import device_lib\nthreads = multiprocessing.cpu_count()\nprint(\"There are% 2d threads available \" %(threads))  \n\n\nprint(device_lib.list_local_devices())\n\ntf.debugging.set_log_device_placement(False)\n\n# Create some tensors to test that the GPU is turned on and available. \na = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])\nb = tf.constant([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])\nc = tf.matmul(a, b)\n\nprint(c)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 1.Exploratory Data Analysis"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Read data from train and test csv files. \ntrain_data = pd.read_csv('../input/landmark-recognition-2020/train.csv')\ntest_data = pd.read_csv('../input/landmark-recognition-2020/sample_submission.csv')\n\n# See shape of our data to determine size of dataset \nprint(\"The size of the dataset is% 2d images\" %(train_data.shape[0]))  \ntrain_data.head(5)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Total class count in dataset and class distribution"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Get amount of classes in our dataset. \nclass_count = train_data.landmark_id.value_counts()\nprint(class_count.head(20))\nprint(\"There are% 2d classes \" %(class_count.shape[0]))  \n# Histogram showing instances per class\nhist_data = pd.DataFrame(train_data['landmark_id'].value_counts()) \nhist_data.reset_index(inplace=True) \nhist_data.columns=['landmark_id','count']\n\nfigure = plt.figure(figsize = (14, 14))\n\nplt.hist(hist_data['count'],500,range = (0,300))#Histogram of the distribution\nplt.xlabel(\"Training Samples\")\nplt.ylabel(\"Occurences\")\n\n#plt.hist(train_data[\"landmark_id\"],bins=train_data[\"landmark_id\"].unique())\n#freq_info = n[0]\n\n#plt.xlim(0,hist_data['landmark_id'].max())\n#plt.ylim(0,2000)\n#plt.xlabel('Landmark ID')\n#plt.ylabel('Number of images')\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Classes with less than 5 training samples? And between 5 and 10 training samples.\nFrom the histogram it can be seen that there is quite some variety between how many samples each class contain. \nTo further explore this we look at how many samples have less than 5 samples and how many classes have between 5-10 samples."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Classes with less than 5 training samples\nbelowFive = class_count[class_count < 5].index.shape[0]\nprint(\"There are% 2d classes with less than 5 training samples\" %(belowFive))  \n# Classes with between 5 and 10 training samples\nfiltered_classes = class_count[class_count >= 5]\nbetweenFiveTen = filtered_classes[filtered_classes <= 10].index.shape[0]\nprint(\"There are% 2d classes with between 5 and 10 training samples\" %(betweenFiveTen))  \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Showing 4 random images each from a random class"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Select 4 random images from the dataset.\ndef get_image_path(img_id):\n    image_path = f\"../input/landmark-recognition-2020/train/{img_id[0]}/{img_id[1]}/{img_id[2]}/{img_id}.jpg\"\n    img = np.array(Image.open(image_path).resize((224, 224), Image.LANCZOS))\n    return img\n\nclasses = train_data.landmark_id.unique()\nrandom_classes = random.choices(classes, k=4)\n    \nfigure = plt.figure(figsize = (14, 14))\n\nfor i in range(len(random_classes)):\n    random_image = train_data.loc[train_data['landmark_id'] == random_classes[i]]\n    random_path = random.choice(random_image.id.values)\n    # Display the randomly selected images.\n    image = get_image_path(random_path)\n    figure.add_subplot(2, 2, i+1)\n    plt.title(random_path)\n    plt.imshow(image)\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Parameters and data preparation/augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Hyperparameters \nval_rate = 0.2\nbatch_size = 32\nmin_samples = 20\nimg_width = img_height = 192\n\nselected_classes = class_count[class_count >= min_samples].index\ntrain_data = train_data.loc[train_data.landmark_id.isin(selected_classes)]\nprint(train_data.shape)\n\nkeep_classes = 1000 # Since many classes have a low sample count we only keep the 1000 most frequent classes in the dataset. \n#Only keep the 1000 most common classes in the dataset. \nc = train_data.landmark_id.values\ncount = Counter(c).most_common(keep_classes)\nkeep_labels = [i[0] for i in count]\ntrain_data = train_data[train_data.landmark_id.isin(keep_labels)]\n\ntrain_data['landmark_id'] = train_data.landmark_id.astype(str)\ntrain_data[\"id\"] = train_data.id.str[0]+\"/\"+train_data.id.str[1]+\"/\"+train_data.id.str[2]+\"/\"+train_data.id+\".jpg\"\nprint(train_data.shape)\nnum_classes = len(count)\nprint(num_classes)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"\ntrain_datagen_augmented = ImageDataGenerator(\n        validation_split=val_rate,\n        rotation_range=10,\n        rescale=1. / 255,      \n        shear_range=0.2,       \n        zoom_range=0.2,        \n        horizontal_flip=True)  \n\ndatagen = ImageDataGenerator(rescale=1. / 255)\n\n\ntrain_generator = train_datagen_augmented.flow_from_dataframe(\n    train_data,\n    directory=\"/kaggle/input/landmark-recognition-2020/train/\",\n    x_col=\"id\",\n    y_col=\"landmark_id\",\n    weight_col=None,\n    target_size=(img_width, img_height),\n    color_mode=\"rgb\",\n    classes=None,\n    class_mode=\"categorical\",\n    batch_size=batch_size,\n    shuffle=True,\n    subset=\"training\",\n    interpolation=\"nearest\",\n    validate_filenames=False)\n\nvalidation_generator = train_datagen_augmented.flow_from_dataframe(\n    train_data,\n    directory=\"/kaggle/input/landmark-recognition-2020/train/\",\n    x_col=\"id\",\n    y_col=\"landmark_id\",\n    weight_col=None,\n    target_size=(img_width, img_height),\n    color_mode=\"rgb\",\n    classes=None,\n    class_mode=\"categorical\",\n    batch_size=batch_size,\n    shuffle=True,\n    subset=\"validation\",\n    interpolation=\"nearest\",\n    validate_filenames=False)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model creation"},{"metadata":{},"cell_type":"markdown","source":"**Xception model creation**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def my_model(input_shape, num_classes, dropout, learning_rate = 0.0002):\n    '''\n    base_model = Xception(weights=None, include_top=False, input_shape=input_shape)\n    base_model.load_weights(\"../input/keraspretrainedmodel/xception_weights_tf_dim_ordering_tf_kernels_notop.h5\")\n    x = base_model.output\n    x = L.GlobalAveragePooling2D()(x)\n    x = L.Dropout(dropout)(x)\n    predictions = L.Dense(num_classes, activation='softmax')(x)\n    model = Model(inputs=base_model.input, outputs=predictions)\n    '''\n\n    base_model = Xception(input_shape=input_shape, \n                           weights=None, include_top=False)\n    base_model.load_weights(\"../input/keraspretrainedmodel/xception_weights_tf_dim_ordering_tf_kernels_notop.h5\")\n\n\n    x = base_model.output\n    x = L.Dropout(dropout)(x)\n    x = L.SeparableConv2D(256, kernel_size=(3, 3), activation='relu',kernel_initializer = tf.keras.initializers.he_uniform(seed=1))(x)\n    x = L.BatchNormalization()(x)\n    x = L.SeparableConv2D(128, kernel_size=(3, 3), activation='relu',kernel_initializer = tf.keras.initializers.he_uniform(seed=3))(x)\n    x = L.BatchNormalization()(x)\n    x = L.SeparableConv2D(num_classes,kernel_size = (1,1), depth_multiplier=1, activation = 'relu',\n                kernel_initializer = tf.keras.initializers.he_uniform(seed=0),\n                kernel_regularizer=tf.keras.regularizers.l1_l2(l1=0.1, l2=0.01)\n                )(x)\n    x = L.GlobalMaxPooling2D()(x)\n    x = L.BatchNormalization()(x)\n    x = L.Flatten()(x)\n\n    pred = L.Dense(num_classes, activation = 'softmax')(x)\n\n    for layer in base_model.layers:\n        layer.trainable = False\n\n    model = Model(inputs = base_model.input,outputs = pred,name='model')\n\n    model.compile(loss='categorical_crossentropy',experimental_steps_per_execution=8, optimizer = Adagrad(learning_rate=0.01), metrics='categorical_accuracy')\n\n    model.summary()\n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = my_model(input_shape = (img_width, img_height, 3), num_classes = num_classes, dropout = 0.5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 30 # maximum number of epochs\ntrain_samples  = int(len(train_data)*(1-val_rate))//batch_size\nvalidation_samples  = int(len(train_data)*val_rate)//batch_size\n\nprint(train_samples)\nprint(validation_samples)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# Model saving callback\ncheckpointer = ModelCheckpoint('basic_cnn.h5', monitor='val_loss', verbose=1, save_best_only=True)\n\n# Early stopping\nearly_stopping = EarlyStopping(monitor='val_loss', verbose=1, patience=10)\n\nhistory = model.fit_generator(\n        train_generator,\n        steps_per_epoch=train_samples // batch_size,\n        epochs=epochs,\n        callbacks=[checkpointer, early_stopping],\n        use_multiprocessing=True,\n        workers=threads,\n        verbose=1,\n        validation_data=validation_generator,\n        validation_steps=validation_samples // batch_size,)\n\nmodel.save(\"basic_cnn.h5\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Unfreeze xception layers"},{"metadata":{"trusted":true},"cell_type":"code","source":"#for layer in model.layers:\n#    layer.trainable = True\n    \n#model.compile(loss='categorical_crossentropy', experimental_steps_per_execution=8, optimizer = Adam(learning_rate=0.0001), metrics='categorical_accuracy')\n#model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#history = model.fit_generator(\n#        train_generator,\n#        steps_per_epoch=train_samples // batch_size,\n#        epochs=epochs,\n#        callbacks=[checkpointer, early_stopping],\n#        use_multiprocessing=True,\n#        workers=threads,\n#        verbose=1,\n#        validation_data=validation_generator,\n#        validation_steps=validation_samples // batch_size)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Evaluation of model"},{"metadata":{"trusted":true},"cell_type":"code","source":"\n#model.evaluate_generator(validation_generator, validation_samples, use_multiprocessing=True, workers=threads, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generate submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(\"/kaggle/input/landmark-recognition-2020/sample_submission.csv\")\nsubmission[\"id\"] = submission.id.str[0]+\"/\"+submission.id.str[1]+\"/\"+submission.id.str[2]+\"/\"+submission.id+\".jpg\"\nbest_model = load_model(\"basic_cnn.h5\")\n\ntest_gen = ImageDataGenerator().flow_from_dataframe(\n    submission,\n    directory=\"/kaggle/input/landmark-recognition-2020/test/\",\n    x_col=\"id\",\n    y_col=None,\n    weight_col=None,\n    target_size=(img_width, img_height),\n    color_mode=\"rgb\",\n    classes=None,\n    class_mode=None,\n    batch_size=1,\n    shuffle=True,\n    subset=None,\n    interpolation=\"nearest\",\n    validate_filenames=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_pred_one_hot = best_model.predict_generator(test_gen, verbose=1, steps=len(submission))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_pred = np.argmax(y_pred_one_hot, axis=-1)\ny_prob = np.max(y_pred_one_hot, axis=-1)\nprint(y_pred.shape, y_prob.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_test_image_path(img_id):\n    #image_path = f\"../input/landmark-recognition-2020/test/{img_id[0]}{img_id[1]}{img_id[2]}{img_id}\"\n    image_path = f\"../input/landmark-recognition-2020/test/{img_id}\"\n\n    img = np.array(Image.open(image_path).resize((224, 224), Image.LANCZOS))\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"y_uniq = np.unique(train_data.landmark_id.values)\ny_pred = [y_uniq[Y] for Y in y_pred]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Extracting best and worst classficiations from predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"temp_sub = submission\n\nfor i in range(len(temp_sub)):\n    temp_sub.loc[i, \"landmarks\"] = str(y_pred[i])\n\ntemp_sub.insert(2, \"pred\", y_prob)    \n\nworst_preds = temp_sub.sort_values(by=['pred'])\nworst_preds = worst_preds[0:5]\nbest_preds = temp_sub.sort_values(by=['pred'], ascending=False)\nbest_preds = best_preds[0:5]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5 Worst classifications "},{"metadata":{"trusted":true},"cell_type":"code","source":"figure = plt.figure(figsize = (14, 14))\nworst_images = worst_preds.id.values\n\nfor i in range(len(worst_images)):\n    path = worst_images[i]\n    # Display the randomly selected images.\n    image = get_test_image_path(path)\n    figure.add_subplot(3, 3, i+1)\n    plt.title(worst_preds.pred.values[i])\n    plt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5 Best classfications"},{"metadata":{"trusted":true},"cell_type":"code","source":"figure = plt.figure(figsize = (14, 14))\nbest_images = best_preds.id.values\n\nfor i in range(len(best_images)):\n    path = best_images[i]\n    image = get_test_image_path(path)\n    figure.add_subplot(3, 3, i+1)\n    plt.title(best_preds.pred.values[i])\n    plt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in range(len(submission)):\n    submission.loc[i, \"landmarks\"] = str(y_pred[i])+\" \"+str(y_prob[i])\n    splitText1 = submission.loc[i, \"id\"].split(\"/\")\n    splitText2 = splitText1[3].split(\".\")\n    submission.loc[i, \"id\"] = splitText2[0]\n\n\nsubmission = submission.drop(columns=\"pred\")\n#submission = submission.drop(columns=\"id\")\nsubmission.to_csv(\"submission.csv\", index=False)\nsubmission","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#model.evaluate_generator(validation_generator, \n#                         validation_samples, \n#                         verbose=1,\n#                         use_multiprocessing=True,\n#                         workers=threads)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#fig, ax = plt.subplots()\n#plt.plot(history.history['accuracy'])\n#plt.plot(history.history['val_accuracy'])\n#plt.title('Model accuracy')\n#plt.ylabel('Accuracy')\n#plt.xlabel('Epoch')\n#plt.legend(['Train', 'Test'], loc='upper left')\n#plt.show()\n","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}