{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Cassava Leaf Disease Classification using tfrecords"},{"metadata":{},"cell_type":"markdown","source":"This notebook utilizes train (original + augmented) and test tfrecords to read cassava leaf images for training using efficientNet architecture. Based on the training, the output image is classified as either a disease or a healthy leaf. Pre-trained weights are used from imagenet to train the model."},{"metadata":{},"cell_type":"markdown","source":"# Importing Packages"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import io\nimport os\nimport csv\nimport cv2\n\nimport pandas as pd\nimport numpy as np\nfrom csv import writer\n\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom functools import partial\nimport albumentations as A\n\nfrom tensorflow import keras\nfrom keras import layers\nfrom keras.layers.core import Dense\nfrom keras.models import Sequential\n\nfrom tensorflow.keras.applications import EfficientNetB6\n\nfrom numpy.random import seed\nseed(3124)\nfrom tensorflow.random import set_seed\nset_seed(3124)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 128\nIMAGE_SIZE = [512, 512]\nEpochs = 10\nnum_classes = 5\nopt = keras.optimizers.Adam(learning_rate=0.001)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Visualization"},{"metadata":{"trusted":true},"cell_type":"code","source":"csv_path ='../input/cassava-leaf-disease-classification/train.csv'\ncld_data = pd.read_csv(csv_path)\ncld_data.info()\nsns.catplot(x='label', data=cld_data, kind='count')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So, there are about 21k leaf images in the dataset, each having label in range (0-4). The catplot shows that there are about 60% samples that belong to label-3 i.e.,leaves having \"Cassava Mosaic Disease (CMD)\" while the rest 4 classes collectively constitute 40% of the dataset."},{"metadata":{},"cell_type":"markdown","source":"# Data Augmentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"def img_augumentation(image, no_transform, label, image_name):\n  transforms = [\n    A.VerticalFlip(p=0.7),\n    A.RandomRotate90(p=0.7),\n    A.RandomBrightness(p=0.8),\n    A.RandomScale(p=0.8),\n    A.RandomRotate90(p=0.5),\n    A.RandomCrop(512,512),\n    A.VerticalFlip(p=0.6),\n    A.RandomRotate90(p=0.9),\n    ]\n    \n  for i in range(no_transform):\n    transform_aug = A.Compose([transforms[i]])\n    transformed = transform_aug(image=image)\n    transformed_image = transformed[\"image\"]\n    transformed_images.append(transformed_image)\n    transformed_images_names.append(image_name+str(i)+\".jpg\")\n    transformed_labels.append(label)\n    \n  ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Writing tfrecords for Augmented Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndef _bytes_feature(value):\n  if isinstance(value, type(tf.constant(0))):\n    value = value.numpy() \n  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n\ndef _int64_feature(value):\n  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))\n\ndef image_example(image_string, image_name, label):\n  feature = {\n        'target': _int64_feature(label),\n        'image': _bytes_feature(image_string),\n        'image_name': _bytes_feature(image_name),\n  }\n  tf_example = tf.train.Example(features=tf.train.Features(feature=feature))\n  return tf_example\n\ndef write_record(image, image_name, label): \n  with tf.io.TFRecordWriter(saving_path) as writer:\n    for img_count in range(len(image)):\n      is_success, im_buf_arr = cv2.imencode(\".jpg\", image[img_count])\n      image_string = im_buf_arr.tobytes()\n      image_name_bytes = str.encode(image_name[img_count])\n      tf_example = image_example(image_string, image_name_bytes, label[img_count])\n      writer.write(tf_example.SerializeToString())\n\ndef write_csv(transformed_names, label):\n  with open('./train.csv', 'a', newline='') as file:\n    writer = csv.writer(file)\n    for img_count in range(len(transformed_names)):\n      new_row = [transformed_names[img_count],label[img_count]]\n      writer.writerow(new_row)\n\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"csv_path =\"../input/cassava-leaf-disease-classification/train.csv\"\ndata = pd.read_csv(csv_path)\nsubmission_data = data.to_csv('./train.csv', index = False)\naug_data = pd.read_csv('./train.csv')\n\nif not os.path.exists('./train_tfrecords'):\n  ! mkdir './train_tfrecords'\n\nPATH = \"./train_tfrecords/ld_train\"\nimg_folder = \"../input/cassava-leaf-disease-classification/train_images\"\nrec_counter = 15\nlabels = [0,1,2,3,4]\nnum_rec = 25\nno_of_transformations = [8,4,4,0,4]\nnum_arr = [42,84,84,0,84]\nnum_images = 1344\nstart = np.zeros(5,int)\nend =  np.zeros(5,int)\n\nfor record in range (num_rec):\n  transformed_images = []\n  transformed_images_names = []\n  transformed_labels = [] \n\n  for label in range(len(labels)):\n    end[label] = end[label] + num_arr[label]\n    if labels[label]!=3:\n      filter = data[\"label\"]==labels[label] \n      _label = data.where(filter)\n      _label = _label.dropna()\n  \n      for image_name in _label[\"image_id\"][start[label]:end[label]]:  \n        img_path = os.path.join(img_folder,image_name)\n        img = plt.imread(img_path)\n        img_augumentation(img, no_of_transformations[label], labels[label], image_name[0:-4])\n\n  rec_counter = rec_counter+1\n  for i in range(5):\n        start[i] = end[i]\n  saving_path = PATH + str(rec_counter) + \"-\" + str(num_images) + \".tfrec\"\n  write_record(transformed_images, transformed_images_names,transformed_labels) \n  write_csv(transformed_images_names,transformed_labels)\n  \n  \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Visualizing Augmented Data "},{"metadata":{"trusted":true},"cell_type":"code","source":"csv_path ='./train.csv'\ncld_data = pd.read_csv(csv_path)\ncld_data.info()\nsns.catplot(x='label', data=cld_data, kind='count')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"By using augmentation,55k leaf images in total are obtained, each having label in range (0-4)."},{"metadata":{},"cell_type":"markdown","source":"# Reading tfrecords\nIn the next step, to speed up the training process and effective utilization of avaialble memory, tfrecords are used to retrieve images, their names and labels with BATCHSIZE of 128. Code for reading tfrecords is taken from [here](https://keras.io/examples/keras_recipes/tfrecord/). \n\n**Data Splitting**\n\nFor 70:30 split of data, out of 41 tfrecords, 29 tfrecords are used for training purpose while the 12 are used as validation dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"main_path='../input/cassava-leaf-disease-classification/train_tfrecords/ld_train'\naug_path=\"./train_tfrecords/ld_train\"\ntrain_Filenames = []\nval_Filenames = []\ntest_Filenames = []\n\nfor record in range(0,12):\n  if record < 10:\n    train_path = main_path + \"0\" + str(record) + \"-1338.tfrec\"\n  else:\n    train_path = main_path + str(record) + \"-1338.tfrec\"\n  train_Filenames.append(train_path) \n\nfor record in range(24,41):\n  train_path = aug_path + str(record) + \"-1344.tfrec\"\n  train_Filenames.append(train_path) \n\nfor record in range(16,24):\n  val_path = aug_path + str(record) + \"-1344.tfrec\"\n  val_Filenames.append(val_path)\n\nfor record in range(12,16):\n  if record==15:\n    val_path = main_path + str(record) + \"-1327.tfrec\"\n  else:\n    val_path = main_path + str(record) + \"-1338.tfrec\"\n  val_Filenames.append(val_path)\n\n\ntest_path =\"../input/cassava-leaf-disease-classification/test_tfrecords/ld_test00-1.tfrec\"\ntest_Filenames.append(test_path)\n\nprint(\"Train TFRecord Files:\", len(train_Filenames))\nprint(\"Validation TFRecord Files:\", len(val_Filenames))\nprint(\"Test TFRecord Files:\", len(test_Filenames))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Training, Validation and Testing Dataset Creation**"},{"metadata":{"trusted":true},"cell_type":"code","source":"                \ndef read_tfrecord(example, labeled):\n    tfrecord_format = (\n        {\n            \"image_name\": tf.io.FixedLenFeature([], tf.string),\n            \"image\": tf.io.FixedLenFeature([], tf.string),\n            \"target\": tf.io.FixedLenFeature([], tf.int64),\n        }\n        if labeled\n        else {\n            \"image_name\": tf.io.FixedLenFeature([], tf.string),\n            \"image\": tf.io.FixedLenFeature([], tf.string),\n        }\n    )\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example[\"image\"])\n    if labeled:\n        label_plot = tf.cast(example[\"target\"], tf.int32)        \n        label = tf.one_hot(label_plot, depth=num_classes)\n        return image, label_plot\n    return image\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.cast(image, tf.float32)\n    image = tf.image.resize(image, IMAGE_SIZE)\n    return image\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\ndef load_dataset(filenames, labeled=True):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(partial(read_tfrecord, labeled=labeled))\n    # returns a dataset of (image, label) pairs if labeled=True or just images if labeled=False\n    return dataset\n\ndef get_dataset(filenames, labeled=True):\n    dataset = load_dataset(filenames, labeled=labeled)\n    dataset = dataset.shuffle(2048, reshuffle_each_iteration=False, seed=3124)\n    dataset = dataset.prefetch(buffer_size=AUTOTUNE)\n    dataset = dataset.batch(BATCH_SIZE)\n    return dataset\n\ntrain_dataset = get_dataset(train_Filenames)\nval_dataset = get_dataset(val_Filenames)\ntest_dataset = get_dataset(test_Filenames, labeled=False)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"After creating the datasets, first batch is loaded and images with their respective labels are displayed using below code."},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_batch(image_batch, label_batch):\n    plt.figure(figsize=(12, 8))\n    for n in range(12):\n        ax = plt.subplot(3, 4, n + 1)\n        plt.imshow(image_batch[n] / 255.0)\n        if label_batch[n] == 4:\n            plt.title(\"Healthy\")\n        elif label_batch[n] == 3:\n            plt.title(\"Cassava Mosaic\")\n        elif label_batch[n] == 2:\n            plt.title(\"Cassava Green Mottle\")\n        elif label_batch[n] == 1:\n            plt.title(\"Cassava Brown Streak\")\n        else:\n            plt.title(\"Cassava Bacterial Blight\")\n        plt.axis(\"off\")\n\nfor image_batch, label_batch in train_dataset.take(1):\n    show_batch(image_batch.numpy(), label_batch.numpy())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Transfer learning (EfficientNet)\nAs the images retrieved using tfrecords have resolution 512x512, so EfficientNet version B6 is used that allows image resolution upto 528x528.\n\nTo determine the loss between labels and predictions, categorical cross entropy and adam oprtimizer is used. The model is trained for 10 epochs and validated on validation dataset. Pre-trained model is loaded and prediction is made."},{"metadata":{"trusted":true},"cell_type":"code","source":"\n#model.compile(optimizer = 'adam' , loss = 'categorical_crossentropy' , metrics=['accuracy'])\n#csv_logger = CSVLogger('training.log', separator=',', append=False)\n#history = model.fit(train_dataset, epochs=100, validation_data=val_dataset, callbacks=[csv_logger])\n\n#model loading\nloaded_model = keras.models.load_model('../input/efficientnet-trained-model/trained_model.h5')\nloaded_model.summary()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#model history for 100 epochs\nhistory = '../input/efficientnet-trained-model/training.log'\nlog_data = pd.read_csv(history, sep=',', engine='python')\nacc = log_data[\"accuracy\"]\nloss = log_data[\"loss\"]\n\nval_acc = log_data[\"val_accuracy\"]\nval_loss = log_data[\"val_loss\"]\n\nprint(\"Training Accuracy : {:.2f}\".format(acc[9]*100),\"%\")\nprint(\"Validation Accuracy : {:.2f}\".format(val_acc[9]*100),\"%\")\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot of Epochs vs Accuracy"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(acc, label='Training Accuracy')\nplt.plot(val_acc, label = 'Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.ylim([0.5, 1])\nplt.legend(loc='lower right')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot of Epochs vs Loss"},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(loss, label='Training Loss')\nplt.plot(val_loss, label = 'Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.ylim([0.5, 1])\nplt.legend(loc='lower right')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test Data Prediction"},{"metadata":{"trusted":true},"cell_type":"code","source":"\npredicted_label = np.argmax(loaded_model.predict(test_dataset))\nprint(\"Predicted Class :\", predicted_label)\ntest_img = '2216849948.jpg'\n\ndata_dict = {'image_id':test_img,'label':predicted_label}\n\nresult = pd.DataFrame(data_dict, index = [1]) \nsubmission_data = result.to_csv('./submission.csv', index = False)\nsub_csv = pd.read_csv('./submission.csv')\nsub_csv","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}