{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport keras\nimport cv2\nfrom matplotlib import pyplot as plt\nimport os\n\nsample_path = r\"/kaggle/input/landmark-recognition-2020/sample_submission.csv\"\ntrain_path = r\"/kaggle/input/landmark-recognition-2020/train.csv\"\nbase_path = r\"/kaggle/input/landmark-recognition-2020/train\"\ntest_path = r\"/kaggle/input/landmark-recognition-2020/test\"\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Exploration of the dataset"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"### Numerical exploration of the dataset.\ndf = pd.read_csv(train_path)\ndf_test = pd.read_csv(sample_path)\n\nsamples = 20000\n\ndf = df.loc[:samples,:]\n\nnum_classes = len(df[\"landmark_id\"].unique())\nnum_data = len(df)\n\nprint(\":::Training set:::\")\nprint(\"Number of classes: \", num_classes)\nprint(\"Number of datapoints: \", num_data)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"### Histogram of the distribution of labels.\nfig=plt.figure(figsize=(12, 8))\nn = plt.hist(df[\"landmark_id\"],bins=df[\"landmark_id\"].unique())\nplt.ylim(top=250)\nplt.title(\"Distribution of labels\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Analysis of training set:\nfreq_info = n[0]   # Get the frequency information for all classes and reject the rest\nsec0_5 = len(freq_info[freq_info<=5])\nsec5_10 = len(freq_info[freq_info<=10]) - sec0_5\nprint(\"Classes with <5 instances:\", sec0_5)\nprint(\"Classes with 5> and <10 instances:\", sec5_10)\n\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\nlencoder = LabelEncoder()\nlencoder.fit(df[\"landmark_id\"])\n\ndef encode_label(lbl):\n    return lencoder.transform(lbl)\n    \ndef decode_label(lbl):\n    return lencoder.inverse_transform(lbl)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Visualize random images from the dataset\n\ndef get_image_from_number(num):\n    fname, label = df.loc[num,:]\n    fname = fname + \".jpg\"\n    f1 = fname[0]\n    f2 = fname[1]\n    f3 = fname[2]\n    path = os.path.join(f1,f2,f3,fname)\n    im = cv2.imread(os.path.join(base_path,path))\n    return im, label\n\n\nfig=plt.figure(figsize=(16, 8))\n\ncolumns = 4\nrows = 2\nfor i in range(1, columns*rows +1):\n    n = np.random.randint(num_data)\n    img, lbl = get_image_from_number(n)\n    fig.add_subplot(rows, columns, i)\n    plt.imshow(img)\n    plt.title(\"Label = \" + str(lbl))\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Network design"},{"metadata":{"trusted":true},"cell_type":"code","source":"### Recreation of the VGG19 architecture for basic. Allowing adjustments like BatchNormalization and so on.\nfrom keras.layers import *\nfrom keras import Sequential\n\nmodel = Sequential()\nmodel.add(Input(shape=(224,224,3)))\nmodel.add(BatchNormalization())\nmodel.add(Conv2D(64, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(64, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Conv2D(128, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(128, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(Conv2D(256, kernel_size = (3,3), padding = \"same\"))\nmodel.add(MaxPooling2D())\nmodel.add(Flatten())\nmodel.add(Dense(4096, activation = \"relu\"))\nmodel.add(Dense(4096, activation = \"relu\"))\nmodel.add(Dense(num_classes, activation=\"softmax\"))\nprint(model.summary())\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Compile the network, with the new optimizer I just learned about ;)\nopt = keras.optimizers.RMSprop(learning_rate = 0.0001, momentum = 0.09)\nmodel.compile(optimizer=opt,\n             loss=\"sparse_categorical_crossentropy\",\n             metrics=[\"accuracy\"])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Function used for processing the data, fitted into a data generator.\ndef get_image_from_number(num, df):\n    fname, label = df.iloc[num,:]\n    fname = fname + \".jpg\"\n    f1 = fname[0]\n    f2 = fname[1]\n    f3 = fname[2]\n    path = os.path.join(f1,f2,f3,fname)\n    im = cv2.imread(os.path.join(base_path,path))\n    return im, label\n\ndef image_reshape(im, target_size):\n    return cv2.resize(im, target_size)\n    \ndef get_batch(dataframe,start, batch_size):\n    image_array = []\n    label_array = []\n    \n    end_img = start+batch_size\n    if end_img > len(dataframe):\n        end_img = len(dataframe)\n\n    for idx in range(start, end_img):\n        n = idx\n        im, label = get_image_from_number(n, dataframe)\n        im = image_reshape(im, (224, 224)) / 255.0\n        image_array.append(im)\n        label_array.append(label)\n        \n    label_array = encode_label(label_array)\n    return np.array(image_array), np.array(label_array)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 50\nepoch_shuffle = True\nweight_classes = True\nepochs = 10\n\n# Split train data up into 80% and 20% validation\ntrain, validate = np.split(df.sample(frac=1), [int(.8*len(df))])\nprint(\"Training on:\", len(train), \"samples\")\nprint(\"Validation on:\", len(validate), \"samples\")\n\n    \nfor e in range(epochs):\n    print(\"Epoch: \", str(e+1) + \"/\" + str(epochs))\n    if epoch_shuffle:\n        train = train.sample(frac = 1)\n    for it in range(int(np.ceil(len(train)/batch_size))):\n\n        X_train, y_train = get_batch(train, it*batch_size, batch_size)\n\n        model.train_on_batch(X_train, y_train)\n        \n\nmodel.save(\"Model.h5\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Test on training set\nbatch_size = 50\n\nerrors = 0\ngood_preds = []\nbad_preds = []\n\nfor it in range(int(np.ceil(len(validate)/batch_size))):\n\n    X_train, y_train = get_batch(validate, it*batch_size, batch_size)\n\n    result = model.predict(X_train)\n    cla = np.argmax(result, axis=1)\n    for idx, res in enumerate(result):\n        print(\"Class:\", cla[idx], \"- Confidence:\", np.round(res[cla[idx]],2), \"- GT:\", y_train[idx])\n        if cla[idx] != y_train[idx]:\n            errors = errors + 1\n            bad_preds.append([batch_size*it + idx, cla[idx], res[cla[idx]]])\n        else:\n            good_preds.append([batch_size*it + idx, cla[idx], res[cla[idx]]])\n\nprint(\"Errors: \", errors, \"Acc:\", np.round(100*(len(validate)-errors)/len(validate),2))\n\n        \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Plot 4 best predictions\n\nfig=plt.figure(figsize=(16, 8))\n\ngood_preds = np.array(good_preds)\ngood_preds = np.array(sorted(good_preds, key = lambda x: x[2], reverse=True))\nprint(good_preds.shape)\n\ncolumns = 4\nrows = 2\nfor i in range(1, columns*rows +1):\n    n = int(good_preds[i,0])\n    print(n)\n    img, lbl = get_image_from_number(n, validate)\n    fig.add_subplot(rows, columns, i)\n    plt.imshow(img)\n    lbl2 = np.array(int(good_preds[i,1])).reshape(1,1)\n    plt.title(\"Label = \" + str(lbl) + \" Classified:\" + str(decode_label(lbl2)) + \" Confidence:\" + str(np.round(good_preds[i,2],2)))\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Plot 4 worst predictions\n\nfig=plt.figure(figsize=(16, 8))\n\nbad_preds = np.array(bad_preds)\nbad_preds = np.array(sorted(bad_preds, key = lambda x: x[2], reverse=True))\n\ncolumns = 4\nrows = 2\nfor i in range(1, columns*rows +1):\n    n = int(bad_preds[i,0])\n    print(n)\n    img, lbl = get_image_from_number(n, validate)\n    fig.add_subplot(rows, columns, i)\n    plt.imshow(img)\n    lbl2 = np.array(int(bad_preds[i,1])).reshape(1,1)\n    plt.title(\"Label = \" + str(lbl) + \" Classified:\" + str(decode_label(lbl2)) + \" Confidence:\" + str(np.round(bad_preds[i,2],2)))\nplt.show()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Explaining the errors"},{"metadata":{"trusted":true},"cell_type":"code","source":"temp = []\nfor cla, amt in enumerate(freq_info):\n    temp.append([cla, amt])\n\ntemp2 = np.array(sorted(temp, key = lambda x: x[1], reverse=True))\n\nprint(\"Top 5 most frequent labels.\")\nfor t in range(5):\n    lbl = np.array(int(temp2[t,0])).reshape(1,)\n    print(\"Class:\", decode_label(lbl)[0], \"has\", int(temp2[t,1]), \"instances.\")\n    \n\nerrors = np.array([1668, 1983, 2037, 1469, 1468, 2586, 226])\nencoded_errors = encode_label(errors)\nwrong_preds = np.array([1189, 2434, 1346, 1924, 1127, 309])\nwrong_preds_encoded = encode_label(wrong_preds)\nprint(\"\\nClasses with wrong predicitions.\")\nfor idx, lbl in enumerate(encoded_errors):\n\n    print(\"Label:\", errors[idx], \"has\", int(temp[lbl-1][1]), \"instances.\")\n    \nprint(\"\\nClasses with high tendency to be predicted\")\nfor idx, lbl in enumerate(wrong_preds_encoded):\n    print(\"Label:\", wrong_preds[idx], \"has\", int(temp[lbl-1][1]), \"instances.\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Submission\ndef get_image_from_number(num):\n    fname = df_test.loc[num,\"id\"]\n    fname = fname + \".jpg\"\n    f1 = fname[0]\n    f2 = fname[1]\n    f3 = fname[2]\n    path = os.path.join(f1,f2,f3,fname)\n    im = cv2.imread(os.path.join(test_path,path))\n    return im\n\ndef get_max_class(preds):\n    p = preds\n    confidence = np.max(p)\n    cla = np.argmax(p)\n    label = decode_label(cla.reshape(1,1))[0]\n    \n    return label, np.round(confidence,2)\n    \ntest_samples = len(df_test)\ntest_df = df_test.copy()\nfor sample in range(test_samples):\n    img = get_image_from_number(sample)\n    img = image_reshape(img, (224, 224)).reshape(1, 224, 224, 3)\n    \n    result = model.predict(img)\n    \n    label, conf = get_max_class(result)\n    test_df.at[sample, 'landmark'] = str(label) + \" \" + str(conf)\n    print(label, conf)\n\ntest_df.to_csv(\"submission.csv\", index = False, header = True)\n\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}