{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np\nimport pandas as pd\nimport keras\nimport cv2\nfrom matplotlib import pyplot as plt\nimport os\nimport random\nfrom PIL import Image\n\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n#import os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\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\nsamples = 20000\ndf = pd.read_csv(\"../input/landmark-recognition-2020/train.csv\")# Read the CSV file containing the training labels etc.\ndf_test = pd.read_csv(sample_path)\ndf = df.loc[:samples,:]\nnum_classes = len(df[\"landmark_id\"].unique())\nnum_data = len(df)\ndf.head()#Prints the first 5 entries in the data file to get an idea of how the data is formatted","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#Check the size of the training data\n\nprint(\"Size of training data:\", df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Count how many unique landmarks there are, that is to say the amount of classes\nprint(\"Number of unique classes:\", num_classes)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There are 20001 training samples, belonging to 1020 classes, giving us an average of 19.6 images per class, however, this distribution might not be the case, so let us look into the distribution of samples per class. (Well it can also be seen above, that it is not exactly 20 images for the most frequent ones)"},{"metadata":{"trusted":true},"cell_type":"code","source":"data = pd.DataFrame(df['landmark_id'].value_counts()) #make data frame that is easier to use\n#index the data frame\ndata.reset_index(inplace=True) \ndata.columns=['landmark_id','count']\n\nprint(data.head(10))\nprint(data.tail(10))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As is seen, the top 10 occuring landmarks range from 139 data points to 944 data points, while the bottom 10 all have 2 data points."},{"metadata":{"trusted":true},"cell_type":"code","source":"\nprint(data['count'].describe())#statistical data for the distribution\n\n\nplt.hist(data['count'],100,range = (0,944),label = 'test')#Histogram of the distribution\nplt.xlabel(\"Amount of images\")\nplt.ylabel(\"Occurences\")\n\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As is seen from the describe function as well as the histogram (Doesn't show all the classes), the vast majority of the classes do not have that many images associated with them."},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Amount of classes with five and less datapoints:\", (data['count'].between(0,5)).sum()) \n\nprint(\"Amount of classes with with between five and 10 datapoints:\", (data['count'].between(5,10)).sum())\n\nn = plt.hist(df[\"landmark_id\"],bins=df[\"landmark_id\"].unique())\nfreq_info = n[0]\n\nplt.xlim(0,data['landmark_id'].max())\nplt.ylim(0,data['count'].max())\nplt.xlabel('Landmark ID')\nplt.ylabel('Number of images')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As the above code shows, more than 50% of the 1020 classes have less than 10 images, which might prove difficult when training a classifier. There are a few \"outliers\" in terms of the amount of images they have, which means we might be biased towards these, since it might have a higher chance of getting a \"guess\" right with the higher amount in these classes."},{"metadata":{},"cell_type":"markdown","source":"Training model:"},{"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":"def 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\nprint(\"4 sample images from random classes:\")\nfig=plt.figure(figsize=(16, 16))\nfor i in range(1,5):\n    a = random.choices(os.listdir(base_path), k=3)\n    folder = base_path+'/'+a[0]+'/'+a[1]+'/'+a[2]\n    random_img = random.choice(os.listdir(folder))\n    img = np.array(Image.open(folder+'/'+random_img))\n    fig.add_subplot(1, 4, i)\n    plt.imshow(img)\n    plt.axis('off')\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications import VGG19\nfrom keras.layers import *\nfrom keras import Sequential\n\n\n\n### Parameters\n# learning_rate   = 0.0001\n# decay_speed     = 1e-6\n# momentum        = 0.09\n\n# loss_function   = \"sparse_categorical_crossentropy\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"source_model = VGG19(weights=None)\n#new_layer = Dense(num_classes, activation=activations.softmax, name='prediction')\ndrop_layer = Dropout(0.5)\ndrop_layer2 = Dropout(0.5)\n\n\nmodel = Sequential()\nfor layer in source_model.layers[:-1]: # go through until last layer\n    if layer == source_model.layers[-25]:\n        model.add(BatchNormalization())\n    model.add(layer)\n#     if layer == source_model.layers[-3]:\n#         model.add(drop_layer)\n# model.add(drop_layer2)\nmodel.add(Dense(num_classes, activation=\"softmax\"))\nmodel.summary()\n\n\nopt1 = keras.optimizers.RMSprop(learning_rate = 0.0001, momentum = 0.09)\nopt2 = keras.optimizers.Adam(learning_rate=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-07)\nmodel.compile(optimizer=opt1,\n             loss=\"sparse_categorical_crossentropy\",\n             metrics=[\"accuracy\"])\n\n#sgd = SGD(lr=learning_rate, decay=decay_speed, momentum=momentum, nesterov=True)\n# rms = keras.optimizers.RMSprop(lr=learning_rate, momentum=momentum)\n# model.compile(optimizer=rms,\n#               loss=loss_function,\n#               metrics=[\"accuracy\"])\n# print(\"Model compiled! \\n\")\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)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 16\nepoch_shuffle = True\nweight_classes = True\nepochs = 15\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 = 16\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","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"temp = []\nfor cla, amt in enumerate(freq_info):\n    temp.append([cla, amt])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Good predictions\ngood_preds = np.array(good_preds)\ngood_preds = np.array(sorted(good_preds, key = lambda x: x[2], reverse=True))\n\nprint(\"5 images where classification went well:\")\nfig=plt.figure(figsize=(16, 16))\nfor i in range(1,6):\n    n = int(good_preds[i,0])\n    img, lbl = get_image_from_number(n, validate)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    fig.add_subplot(1, 5, i)\n    plt.imshow(img)\n    lbl2 = np.array(int(good_preds[i,1])).reshape(1,1)\n    sample_cnt = list(df.landmark_id).count(lbl)\n    plt.title(\"Label: \" + str(lbl) + \"\\nClassified as: \" + str(decode_label(lbl2)) + \"\\nSamples in class \" + str(lbl) + \": \" + str(sample_cnt))\n    plt.axis('off')\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Bad predictions\nbad_preds = np.array(bad_preds)\nbad_preds = np.array(sorted(bad_preds, key = lambda x: x[2], reverse=True))\n\nprint(\"5 images where classification failed:\")\nfig=plt.figure(figsize=(16, 16))\nfor i in range(1,6):\n    n = int(bad_preds[i,0])\n    img, lbl = get_image_from_number(n, validate)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    fig.add_subplot(1, 5, i)\n    plt.imshow(img)\n    lbl2 = np.array(int(bad_preds[i,1])).reshape(1,1)\n    sample_cnt = list(df.landmark_id).count(lbl)\n    plt.title(\"Label: \" + str(lbl) + \"\\nClassified as: \" + str(decode_label(lbl2)) + \"\\nSamples in class \" + str(lbl) + \": \" + str(sample_cnt))\n    plt.axis('off')\n    \nplt.show()\n","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}