{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"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 # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\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\nimport os\n\n# directories\n\n#/kaggle/input/herbarium-2022-fgvc9/train_metadata.json\n#/kaggle/input/herbarium-2022-fgvc9/sample_submission.csv\n#/kaggle/input/herbarium-2022-fgvc9/test_metadata.json\n#/kaggle/input/herbarium-2022-fgvc9/train_images/135/47/13547__018.jpg","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 1. Load metadata ","metadata":{}},{"cell_type":"code","source":"main_dir = '/kaggle/input/herbarium-2022-fgvc9'\nmetadata_file = os.path.join(main_dir, \"{}_metadata.json\")\nimages_dir = os.path.join(main_dir, \"{}_images\")\nmetadata_file, images_dir","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_prefix = 'train'\ntest_prefix = 'test'","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#load metadata\nimport json\n\ndef get_dataframe(prefix):\n    with open(metadata_file.format(prefix)) as f:\n        metadata = json.load(f)\n    \n    if (prefix == train_prefix):\n        df_ann = pd.DataFrame(metadata['annotations'])\n        df_ann = df_ann[['image_id', 'category_id']]\n        df_ann.set_index('image_id', inplace = True)\n\n        df_images = pd.DataFrame(metadata['images'])\n        df_images = df_images[['image_id', 'file_name']]\n        df_images.set_index('image_id', inplace = True)\n\n        return df_ann.join(df_images, how = 'left')\n    else:\n        df = pd.DataFrame(metadata)\n        return df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = get_dataframe(train_prefix)\nprint(len(train_df))\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = get_dataframe(test_prefix)\nprint(len(test_df))\ntest_df.tail()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.isna().sum()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df.isna().sum()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.category_id.hist(figsize = (25, 5))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# how many examples of each class are presented in train dataset?\nclass_count = {target: len(train_df[train_df['category_id'] == target]) for target in sorted(train_df.category_id.unique())}\nmin(class_count.values()), max(class_count.values())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sns\n\nplt.figure(figsize = (15,5))\nsns.countplot(x = list(class_count.values()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# a good approach to solve this task is to increase number of less presented classes by using image random transformations\n# but we would get a huuuuge dataset and it would take a lot of time to process all these images that we cannot afford in case of limited resources\n# so instead of this we take only frequently encounted class samples\n\n# let's say that the minimum number of sample of one class should be 80 or more\nthreshold = 70\nclass_count = {k:v for k, v in class_count.items() if v >= threshold}\n\nprint(len(class_count))\nplt.figure(figsize = (15,5))\nsns.countplot(x = list(class_count.values()))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.concat([train_df[train_df['category_id'] == target] for target in class_count.keys()])\nprint(len(train_df))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.image as mpimg\n\nimage = mpimg.imread(os.path.join(images_dir.format(train_prefix), train_df.iloc[0]['file_name']))\n\nplt.figure(figsize = (5,10))\nplt.imshow(image)\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2. Download images","metadata":{}},{"cell_type":"code","source":"# preprocess label value\nfrom sklearn import preprocessing\n\nle = preprocessing.LabelEncoder()\ntrain_df['category_id_encoded'] = le.fit_transform(train_df['category_id'])\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# how many classes we have in the end?\nnum_of_classes = max(train_df['category_id_encoded']) + 1\nnum_of_classes","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ImageDataGenerator().flow_from_dataframe(class_mode = 'sparse') requires string labels\ntrain_df['category_id_encoded'] = train_df['category_id_encoded'].astype('str')\ntrain_f = train_df.sample(frac = 0.1, replace=False, random_state=0)\ntrain_df\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nimg_size = 120\ntrain_datagen = ImageDataGenerator(rescale = 1/255.,\n                                   validation_split = 0.2)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dir = images_dir.format(train_prefix)\nprint(train_dir)\n\ntrain_gen = train_datagen.flow_from_dataframe(dataframe = train_df,\n                                              directory = train_dir,\n                                              x_col = \"file_name\",\n                                              y_col = \"category_id_encoded\",\n                                              target_size=(img_size, img_size),\n                                              batch_size = 16,\n                                              class_mode = 'sparse',\n                                              subset = 'training')\n\nval_gen = train_datagen.flow_from_dataframe(dataframe = train_df,\n                                            directory = train_dir,\n                                            x_col = \"file_name\",\n                                            y_col = \"category_id_encoded\",\n                                            target_size = (img_size, img_size),\n                                            batch_size = 16,\n                                            class_mode = 'sparse',\n                                            subset = 'validation')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Build a model","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import Input, Dense, Conv2D, MaxPooling2D, Flatten, Dropout, BatchNormalization, Add\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras import datasets, layers, models, losses, Model\n\ninput_size = (img_size, img_size, 3) # all images are 120x120 size + RGB\n\ninput = Input(input_size)\n\ndef inception(x,\n              filters_1x1,\n              filters_3x3_reduce,\n              filters_3x3,\n              filters_5x5_reduce,\n              filters_5x5,\n              filters_pool):\n  path1 = layers.Conv2D(filters_1x1, (1, 1), padding='same',    activation='relu')(x)\n  path2 = layers.Conv2D(filters_3x3_reduce, (1, 1), padding='same', activation='relu')(x)\n  path2 = layers.Conv2D(filters_3x3, (1, 1), padding='same', activation='relu')(path2)\n  path3 = layers.Conv2D(filters_5x5_reduce, (1, 1), padding='same', activation='relu')(x)\n  path3 = layers.Conv2D(filters_5x5, (1, 1), padding='same', activation='relu')(path3)\n  path4 = layers.MaxPool2D((3, 3), strides=(1, 1), padding='same')(x)\n  path4 = layers.Conv2D(filters_pool, (1, 1), padding='same', activation='relu')(path4)\n  return tf.concat([path1, path2, path3, path4], axis=3)\n\ninput_tensor = layers.experimental.preprocessing.Resizing(224, 224, interpolation=\"bilinear\")(input)\nx = layers.Conv2D(64, 7, strides=2, padding='same', activation='relu')(input_tensor)\nx = layers.MaxPooling2D(3, strides=2)(x)\nx = layers.Conv2D(64, 1, strides=1, padding='same', activation='relu')(x)\nx = layers.Conv2D(192, 3, strides=1, padding='same', activation='relu')(x)\nx = layers.MaxPooling2D(3, strides=2)(x)\nx = inception(x, filters_1x1=64, filters_3x3_reduce=96, filters_3x3=128, filters_5x5_reduce=16, filters_5x5=32, filters_pool=32)\nx = inception(x, filters_1x1=128, filters_3x3_reduce=128, filters_3x3=192, filters_5x5_reduce=32, filters_5x5=96, filters_pool=64)\nx = layers.MaxPooling2D(3, strides=2)(x)\nx = inception(x, filters_1x1=192, filters_3x3_reduce=96, filters_3x3=208, filters_5x5_reduce=16, filters_5x5=48, filters_pool=64)\naux1 = layers.AveragePooling2D((5, 5), strides=3)(x)\naux1 =layers.Conv2D(128, 1, padding='same', activation='relu')(aux1)\naux1 = layers.Flatten()(aux1)\naux1 = layers.Dense(1024, activation='relu')(aux1)\naux1 = layers.Dropout(0.7)(aux1)\naux1 = layers.Dense(10, activation='softmax')(aux1)\nx = inception(x, filters_1x1=160, filters_3x3_reduce=112, filters_3x3=224, filters_5x5_reduce=24, filters_5x5=64, filters_pool=64)\nx = inception(x, filters_1x1=128, filters_3x3_reduce=128, filters_3x3=256, filters_5x5_reduce=24, filters_5x5=64, filters_pool=64)\nx = inception(x, filters_1x1=112, filters_3x3_reduce=144, filters_3x3=288, filters_5x5_reduce=32, filters_5x5=64, filters_pool=64)\naux2 = layers.AveragePooling2D((5, 5), strides=3)(x)\naux2 =layers.Conv2D(128, 1, padding='same', activation='relu')(aux2)\naux2 = layers.Flatten()(aux2)\naux2 = layers.Dense(1024, activation='relu')(aux2)\naux2 = layers.Dropout(0.7)(aux2) \naux2 = layers.Dense(10, activation='softmax')(aux2)\nx = inception(x, filters_1x1=256, filters_3x3_reduce=160, filters_3x3=320, filters_5x5_reduce=32, filters_5x5=128, filters_pool=128)\nx = layers.MaxPooling2D(3, strides=2)(x)\nx = inception(x, filters_1x1=256, filters_3x3_reduce=160, filters_3x3=320, filters_5x5_reduce=32, filters_5x5=128, filters_pool=128)\nx = inception(x, filters_1x1=384, filters_3x3_reduce=192, filters_3x3=384, filters_5x5_reduce=48, filters_5x5=128, filters_pool=128)\nx = layers.Flatten()(x)\nx = layers.Dropout(0.4)(x)\nout = layers.Dense(num_of_classes, activation='softmax')(x)\nmodel = Model(inputs = input, outputs = out)\nopt = Adam(learning_rate = 1e-3)\n#model.compile(optimizer='adam', \n#              loss=[losses.sparse_categorical_crossentropy,\n #                   losses.sparse_categorical_crossentropy,\n #                   losses.sparse_categorical_crossentropy]\n #                   loss_weights=[1, 0.3, 0.3],\n #                   metrics=['accuracy'])\n\nmodel.compile(optimizer = opt, loss = 'sparse_categorical_crossentropy',metrics=['accuracy'])\nmodel.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(train_gen, validation_data = val_gen, epochs = 3, verbose = 1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss = history.history['loss']\nval_loss = history.history['val_loss']\nepochs = range(1, len(loss) + 1)\n\nplt.plot(epochs, loss, 'bo', label = 'Trainig loss')\nplt.plot(epochs, val_loss, 'r', label = 'Validation loss')\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = []\ny_true = []\nfor i in range(len(val_gen)):\n    X_batch, y_batch = val_gen[i]\n    pred.extend([np.argmax(x) for x in model.predict(X_batch, verbose = 0)])\n    y_true.extend(y_batch)\n    \nprint(sum(np.array(pred) == np.array(y_true))/len(y_true))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 4. Predict","metadata":{}},{"cell_type":"code","source":"test_dir = images_dir.format(test_prefix)\ntest_datagen = ImageDataGenerator(rescale = 1./255)\ntest_gen = test_datagen.flow_from_dataframe(dataframe = test_df, \n                                            directory = test_dir,\n                                            x_col = \"file_name\",\n                                            target_size = (img_size, img_size),\n                                            batch_size = 32,\n                                            class_mode = None,\n                                            shuffle = False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = []\nfor i in range(len(test_gen)):\n    pred.extend([np.argmax(x) for x in model.predict(test_gen[i], verbose = 0)])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred = le.inverse_transform(pred)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv(os.path.join(main_dir, 'sample_submission.csv'))\nsub['Predicted'] = pred[:len(test_df)]\nsub.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.to_csv('submission.csv', index = False)  ","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}