{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Plant Pathology 2021 - FGVC8\n<img src=\"https://www.researchgate.net/profile/Seung-Yeol-Lee/publication/282210822/figure/fig1/AS:502574476660736@1496834500292/Comparison-of-leaves-with-apple-blotch-disease-and-apple-blotch-like-symptom-A-H-apple.png\" width=\"400\" height=\"400\" />\n\n## Description\nApples are one of the most important temperate fruit crops in the world. Foliar (leaf) diseases pose a major threat to the overall productivity and quality of apple orchards. The current process for disease diagnosis in apple orchards is based on manual scouting by humans, which is time-consuming and expensive.\n\nAlthough computer vision-based models have shown promise for plant disease identification, there are some limitations that need to be addressed. Large variations in visual symptoms of a single disease across different apple cultivars, or new varieties that originated under cultivation, are major challenges for computer vision-based disease identification. These variations arise from differences in natural and image capturing environments, for example, leaf color and leaf morphology, the age of infected tissues, non-uniform image background, and different light illumination during imaging etc.\n\n## About CVPR\nThis competition is part of the Fine-Grained Visual Categorization FGVC8 workshop at the Computer Vision and Pattern Recognition Conference CVPR 2021. A panel will review the top submissions for the competition based on the description of the methods provided. From this, a subset may be invited to present their results at the workshop. Attending the workshop is not required to participate in the competition, however only teams that are attending the workshop will be considered to present their work.","metadata":{}},{"cell_type":"code","source":"!/opt/conda/bin/python3.7 -m pip install --upgrade pip\n! pip install -q efficientnet","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#-------------------\n# importing libraries\n#-------------------\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nimport efficientnet.tfkeras as efn\nimport pandas as pd\nimport numpy as np\nimport os\nimport matplotlib.pyplot as plt\nimport PIL\nimport shutil\nimport csv","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_PATH = \"../input/plant-pathology-2021-fgvc8/train_images/\"\ntrain_df = pd.read_csv(\"../input/plant-pathology-2021-fgvc8/train.csv\")\n\ncount_dict = train_df.labels.value_counts()\nclasses = list(count_dict.index)\nclasses_count = list(count_dict.values)\nprint(\"Number of unique labels: \",len(classes))\n\n\nlabel2id = dict(zip(range(train_df.labels.nunique()),train_df.labels.unique()))\nid2label = dict(zip(train_df.labels.unique(),range(train_df.labels.nunique())))\n#train_df[\"labels\"] = train_df[\"labels\"].map(id2label)\n#train_df.index = train_df[\"image\"]\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Which type of competition is this ? (Multi class or Multi label)\n### Many have doubt whether the follwing competition is multi-class or multi-label . Let me clarify this by giving more details.\n\nIn the details of data it is mentioned as follows:\n> ## **Unhealthy leaves with too many diseases to classify visually will have the complex class, and may also have a subset of the diseases identified.**\n\nLet me show you some labels from `train.csv` file\n\n> **`8002cb321f8bfcdf.jpg     scab frog_eye_leaf_spot complex`**\n\n> **`801f78399a44e7af.jpg     complex`**\n\n> **`80769797ce42f658.jpg      scab frog_eye_leaf_spot`**\n\nYou can see that the image with complex label may have some other diseases mentioned. But from data I got **number of unique labels as 12**. So I considered it as **`Multi-Class classification`** and proceded. ","metadata":{}},{"cell_type":"markdown","source":"# Data Visualization","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(35,15))\nplt.bar(classes,classes_count)\nplt.title(\"Number of instances per class\",fontweight=\"bold\",fontsize=40)\nplt.xlabel(\"Classes\",fontsize = 30)\nplt.xticks(rotation=20,fontsize = 20,fontweight = \"bold\")\nplt.xticks(fontsize = 20,fontweight = \"bold\")\nplt.ylabel(\"Count\",fontsize=30)\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### It's a biased data\n\nAlso the image size are varying as follows:\n## height = [1728,4032], width = [2592,5184]  \n\n### I don't have enough GPU time, so I am using Image size as = [128,128]","metadata":{}},{"cell_type":"code","source":"'''h_ls,w_ls = [],[]\nfor img in os.listdir(TRAIN_PATH):\n    im = PIL.Image.open(os.path.join(TRAIN_PATH,img))\n    h_ls.append(im.height)\n    w_ls.append(im.width)\n\n\nplt.plot(h_ls,w_ls)\nplt.xlabel(\"Height of Image\")\nplt.ylabel(\"Width of Image\")\nplt.title(\"Distribution of Image size of training data\",fontweight = \"bold\")'''\nprint(\"Time Taking\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Pipeline\n\n### As the labels are given in csv file, data pipeline can be prepared by `tf.keras.preprocessing.image.ImageDataGenerator` and there by adding `.flow_from_dataframe` as attribute","metadata":{}},{"cell_type":"code","source":"'''NUM_CLASSES =  len(classes)\nHEIGHT,WIDTH = 64,64\nCHANNELS = 3\nBATCH_SIZE = 32\nSEED = 143\nSPLIT = int(0.8*len(train_df))\nAUTO = tf.data.experimental.AUTOTUNE\n\n\ndef process_img(image):\n    image = tf.image.decode_jpeg(image, channels=CHANNELS)\n    image = tf.image.convert_image_dtype(image, tf.float32) \n    image = tf.image.resize(image, [HEIGHT,WIDTH])\n    return image\n\ndef process_label(filepath):\n    parts = tf.strings.split(filepath, '/')\n    filename = parts[-1]\n    label = train_df[train_df[\"image\"] == filename][\"labels\"][0]\n    label = tf.one_hot(label,depth = NUM_CLASSES,dtype = tf.int32)\n    return label\n\ndef get_image_label(filepath : tf.Tensor):\n    image = tf.io.read_file(filepath)\n    image = process_img(image)\n    label = process_label(filepath)\n    return image,label\n\n\ndef get_dataset(files_ds):\n    dataset = files_ds.map(lambda x: tf.py_function(func=get_image_label,\n                    inp=[x], Tout=(tf.float32,tf.int32)),\n                    num_parallel_calls=tf.data.AUTOTUNE,\n                    deterministic=False)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache().prefetch(AUTO)\n    return dataset\n\n\n\nfiles_ls = tf.io.gfile.glob(TRAIN_PATH + '*.jpg')\nfiles_ds = tf.data.Dataset.list_files(files_ls)\n\ntrain_ds = files_ds.take(SPLIT)\nval_ds = files_ds.skip(SPLIT)\n\ntrain_ds =get_dataset(train_ds)\nval_ds =  get_dataset(val_ds)'''\nprint(\"Different Data Pipeline\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NUM_CLASSES =  len(classes)\nHEIGHT,WIDTH = 128,128\nBATCH_SIZE = 32\nSEED = 143\nSPLIT = 0.2\n\ndatagen = tf.keras.preprocessing.image.ImageDataGenerator(\n    rescale = 1/255.,\n    rotation_range=20,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    horizontal_flip=True,\n    validation_split = SPLIT,\n    zoom_range = 0.2,\n    shear_range = 0.2,\n    vertical_flip = False\n)\n\ntrain_ds = datagen.flow_from_dataframe(\n    train_df,\n    directory = TRAIN_PATH,\n    x_col = \"image\",\n    y_col = \"labels\",\n    target_size = (HEIGHT,WIDTH),\n    class_mode='categorical',\n    batch_size = BATCH_SIZE,\n    subset = \"training\",\n    shuffle = True,\n    seed = SEED,\n    validate_filenames = False\n)\n\nval_ds = datagen.flow_from_dataframe(\n    train_df,\n    directory = TRAIN_PATH,\n    x_col = \"image\",\n    y_col = \"labels\",\n    target_size = (HEIGHT,WIDTH),\n    class_mode='categorical',\n    batch_size = BATCH_SIZE,\n    subset = \"validation\",\n    shuffle = True,\n    seed = SEED,\n    validate_filenames = False\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"def create_model():\n    \n    pretrained = efn.EfficientNetB0(include_top=False, weights='noisy-student',input_shape=[HEIGHT,WIDTH, 3])\n            \n    x = pretrained.output\n    x = tf.keras.layers.GlobalAveragePooling2D() (x)\n    outputs = tf.keras.layers.Dense(NUM_CLASSES,activation=\"softmax\", dtype='float32')(x)\n        \n    model = tf.keras.Model(pretrained.input, outputs)\n    return model\n\nmodel = create_model()\n#model.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Compiling the Model","metadata":{}},{"cell_type":"code","source":"import tensorflow_addons as tfa\n\ndef compile_model(model, lr=0.0001):\n    \n    optimizer = tf.keras.optimizers.Adam(lr=lr)\n    \n    loss = tf.keras.losses.CategoricalCrossentropy()\n    \n    #tf.keras.metrics.CategoricalAccuracy(name='categorical_accuracy')\n    metrics = [\n    tfa.metrics.F1Score(num_classes = NUM_CLASSES,average = \"macro\",name = \"f1_score\")\n    ]\n\n    model.compile(optimizer=optimizer, loss=loss, metrics=metrics)\n\n    return model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Callbacks Function","metadata":{}},{"cell_type":"code","source":"METRIC = \"val_f1_score\"\n\ndef create_callbacks(metric = METRIC):\n    \n    cpk_path = './best_model.h5'\n    \n    checkpoint = tf.keras.callbacks.ModelCheckpoint(\n        filepath=cpk_path,\n        monitor= metric,\n        mode='max',\n        save_best_only=True,\n        verbose=1,\n    )\n\n    reducelr = tf.keras.callbacks.ReduceLROnPlateau(\n        monitor= metric,\n        mode='max',\n        factor=0.1,\n        patience=3,\n        verbose=0\n    )\n\n    earlystop = tf.keras.callbacks.EarlyStopping(\n        monitor= metric,\n        mode='max',\n        patience=10, \n        verbose=1\n    )\n    \n    callbacks = [checkpoint, reducelr, earlystop]         \n    \n    return callbacks","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"EPOCHS= 3\nVERBOSE =1\n\ntf.keras.backend.clear_session()\n\nwith tf.device('/device:GPU:0'):\n    \n    model = create_model()\n    model = compile_model(model, lr=0.0001)\n   \n    callbacks = create_callbacks()\n    \n    history = model.fit(\n                        train_ds,\n                        epochs=EPOCHS,\n                        callbacks=callbacks,\n                        validation_data = val_ds,\n                        verbose=VERBOSE\n                       )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# History plotting","metadata":{}},{"cell_type":"code","source":"acc = history.history['categorical_accuracy']\nval_acc = history.history['val_categorical_accuracy']\nloss = history.history['loss']\nval_loss = history.history['val_loss']\nepochs_range = range(len(history.history['val_loss']))\nplt.figure(figsize=(8, 8))\nplt.subplot(1, 2, 1)\nplt.plot(epochs_range, acc, label='Training Categorical Accuracy')\nplt.plot(epochs_range, val_acc, label='Validation Categorical Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Categorical Accuracy')\nplt.subplot(1, 2, 2)\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### If you have any doubts or suggestions feel free to contact me.\n## Happy coding❤ ","metadata":{}}]}