{"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":"Reference: https://www.kaggle.com/code/jessemostipak/getting-started-tpus-cassava-leaf-disease/notebook","metadata":{"id":"p7dlQihPhzPC"}},{"cell_type":"code","source":"# Setup Environment\nimport math, re, os\nimport tensorflow as tf\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tensorflow import keras\nfrom functools import partial\nfrom sklearn.model_selection import train_test_split \nimport json\nimport seaborn as sn\nimport random","metadata":{"id":"dzR2g4A2gYzj","outputId":"8356ac7f-b72d-450a-d136-1f39efa9605e","execution":{"iopub.status.busy":"2023-04-09T09:32:57.687955Z","iopub.execute_input":"2023-04-09T09:32:57.689055Z","iopub.status.idle":"2023-04-09T09:33:10.621899Z","shell.execute_reply.started":"2023-04-09T09:32:57.688998Z","shell.execute_reply":"2023-04-09T09:33:10.620536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setup Parameters\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nIMAGE_SIZE = [512, 512]\nCLASSES = ['0', '1', '2', '3', '4']","metadata":{"id":"R_t42FqMhoc3","execution":{"iopub.status.busy":"2023-04-09T09:33:10.624257Z","iopub.execute_input":"2023-04-09T09:33:10.626138Z","iopub.status.idle":"2023-04-09T09:33:10.633721Z","shell.execute_reply.started":"2023-04-09T09:33:10.626042Z","shell.execute_reply":"2023-04-09T09:33:10.632076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setup training dataset path for generate the five classes' name and their images' count\nBASE_DIR = \"../input/cassava-leaf-disease-classification/\"\nwith open(os.path.join(BASE_DIR, \"label_num_to_disease_map.json\")) as file:\n    map_classes = json.loads(file.read())\n    map_classes = {int(k) : v for k, v in map_classes.items()}\n    \nprint(json.dumps(map_classes, indent=4))\n\ndf_train = pd.read_csv(os.path.join(BASE_DIR, \"train.csv\"))\ndf_train[\"class_name\"] = df_train[\"label\"].map(map_classes)\nplt.figure(figsize=(8, 4))\nsn.countplot(y=\"class_name\", data=df_train);","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:10.635646Z","iopub.execute_input":"2023-04-09T09:33:10.636062Z","iopub.status.idle":"2023-04-09T09:33:11.022660Z","shell.execute_reply.started":"2023-04-09T09:33:10.636020Z","shell.execute_reply":"2023-04-09T09:33:11.021570Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\n# Load the dataset from Kaggle Input and read them as tensor records format\nGCS_PATH = KaggleDatasets().get_gcs_path()\nTRAINING_FILENAMES, VALID_FILENAMES = train_test_split(\n    tf.io.gfile.glob(GCS_PATH + '/train_tfrecords/ld_train*.tfrec'),\n    test_size=0.35, random_state=5\n)\n\nTEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test_tfrecords/ld_test*.tfrec')","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:11.025524Z","iopub.execute_input":"2023-04-09T09:33:11.025942Z","iopub.status.idle":"2023-04-09T09:33:13.461010Z","shell.execute_reply.started":"2023-04-09T09:33:11.025904Z","shell.execute_reply":"2023-04-09T09:33:13.459460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_tfrecord(example, labeled):\n    tfrecord_format = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.int64)\n    } if labeled else {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"image_name\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    if labeled:\n        label = tf.cast(example['target'], tf.int32)\n        return image, label\n    idnum = example['image_name']\n    return image, idnum\n\n\ndef load_dataset(filenames, labeled=True, ordered=True):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)\n    dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(partial(read_tfrecord, labeled=labeled), num_parallel_calls=AUTOTUNE)\n    return dataset\n","metadata":{"id":"B7WOhYplgW90","execution":{"iopub.status.busy":"2023-04-09T09:33:13.463184Z","iopub.execute_input":"2023-04-09T09:33:13.463595Z","iopub.status.idle":"2023-04-09T09:33:13.690936Z","shell.execute_reply.started":"2023-04-09T09:33:13.463555Z","shell.execute_reply":"2023-04-09T09:33:13.689396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setup data augment operation，the most important part of this code\ndef data_augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    \n    # Choosing 0.35 as the maximum delta so the image is not too dark or too bright\n    image = tf.image.random_brightness(image, 0.35) \n    \n    # Choosing 0.9 as the minimum value can avoid feature loss due to excessive changes in the image \n    # Choosing 1.3 as the maximum value allows for more variety in the images to avoid overfitting, while not changing too much\n    image = tf.image.random_contrast(image, 0.9, 1.3) \n    image = tf.image.random_saturation(image, 0.9, 1.3)\n    # 0.1 is a more conservative number, because after experiments found that too much color adjustment will cause bad results\n    image = tf.image.random_hue(image, 0.1)\n    \n    # Perform a random crop, choosing 448 also so as not to lose the details in the center of the image\n    height = 448 \n    width = 448 \n    original_height = tf.shape(image)[0]\n    original_width = tf.shape(image)[1]\n\n    image = tf.image.pad_to_bounding_box(image, 0, 0, original_height + 4, original_width + 4)\n    image = tf.image.random_crop(image, size=[height, width, 3])\n\n    return image, label","metadata":{"id":"ehlghKIekF07","execution":{"iopub.status.busy":"2023-04-09T09:33:13.692573Z","iopub.execute_input":"2023-04-09T09:33:13.692952Z","iopub.status.idle":"2023-04-09T09:33:13.710199Z","shell.execute_reply.started":"2023-04-09T09:33:13.692915Z","shell.execute_reply":"2023-04-09T09:33:13.708881Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# numpy and matplotlib defaults\n\nnp.set_printoptions(threshold=15, linewidth=80)\n\ndef batch_to_numpy_images_and_labels(data):\n    images, labels = data\n    numpy_images = images.numpy()\n    numpy_labels = labels.numpy()\n    if numpy_labels.dtype == object: # binary string in this case, these are image ID strings\n        numpy_labels = [None for _ in enumerate(numpy_images)]\n    # If no labels, only image IDs, return None for labels (this is the case for test data)\n    return numpy_images, numpy_labels\n\ndef title_from_label_and_target(label, correct_label):\n    if correct_label is None:\n        return CLASSES[label], True\n    correct = (label == correct_label)\n    return \"{} [{}{}{}]\".format(CLASSES[label], 'OK' if correct else 'NO', u\"\\u2192\" if not correct else '',\n                                CLASSES[correct_label] if not correct else ''), correct\n\ndef display_one_plant(image, title, subplot, red=False, titlesize=16):\n    plt.subplot(*subplot)\n    plt.axis('off')\n    plt.imshow(image)\n    if len(title) > 0:\n        plt.title(title, fontsize=int(titlesize) if not red else int(titlesize/1.2), color='red' if red else 'black', fontdict={'verticalalignment':'center'}, pad=int(titlesize/1.5))\n    return (subplot[0], subplot[1], subplot[2]+1)\n\ndef display_batch_of_images(databatch, predictions=None):\n    \"\"\"This will work with:\n    display_batch_of_images(images)\n    display_batch_of_images(images, predictions)\n    display_batch_of_images((images, labels))\n    display_batch_of_images((images, labels), predictions)\n    \"\"\"\n    # data\n    images, labels = batch_to_numpy_images_and_labels(databatch)\n    if labels is None:\n        labels = [None for _ in enumerate(images)]\n        \n    # auto-squaring: this will drop data that does not fit into square or square-ish rectangle\n    rows = int(math.sqrt(len(images)))\n    cols = len(images)//rows\n        \n    # size and spacing\n    FIGSIZE = 13.0\n    SPACING = 0.1\n    subplot=(rows,cols,1)\n    if rows < cols:\n        plt.figure(figsize=(FIGSIZE,FIGSIZE/cols*rows))\n    else:\n        plt.figure(figsize=(FIGSIZE/rows*cols,FIGSIZE))\n    \n    # display\n    for i, (image, label) in enumerate(zip(images[:rows*cols], labels[:rows*cols])):\n        title = '' if label is None else CLASSES[label]\n        correct = True\n        if predictions is not None:\n            title, correct = title_from_label_and_target(predictions[i], label)\n        dynamic_titlesize = FIGSIZE*SPACING/max(rows,cols)*40+3 # magic formula tested to work from 1x1 to 10x10 images\n        subplot = display_one_plant(image, title, subplot, not correct, titlesize=dynamic_titlesize)\n    \n    #layout\n    plt.tight_layout()\n    if label is None and predictions is None:\n        plt.subplots_adjust(wspace=0, hspace=0)\n    else:\n        plt.subplots_adjust(wspace=SPACING, hspace=SPACING)\n    plt.show()","metadata":{"id":"Z-N-VqHxx0iz","execution":{"iopub.status.busy":"2023-04-09T09:33:13.712122Z","iopub.execute_input":"2023-04-09T09:33:13.712557Z","iopub.status.idle":"2023-04-09T09:33:13.734332Z","shell.execute_reply.started":"2023-04-09T09:33:13.712517Z","shell.execute_reply":"2023-04-09T09:33:13.732600Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This function will be called 5 times to display 9 randomly selected images from each class\ndef display_random_images_for_label(dataset, target_label, num_images=9):\n    images, labels = batch_to_numpy_images_and_labels(dataset)\n    indices = np.where(labels == target_label)[0]\n    random_indices = random.sample(list(indices), num_images)\n\n    plt.figure(figsize=(9, 9))\n\n    for i, index in enumerate(random_indices):\n        plt.subplot(3, 3, i + 1)\n        plt.xticks([])\n        plt.yticks([])\n        plt.grid(False)\n        plt.imshow(images[index])\n        plt.xlabel(\"Class: {}\".format(CLASSES[target_label]))\n\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:13.736596Z","iopub.execute_input":"2023-04-09T09:33:13.737067Z","iopub.status.idle":"2023-04-09T09:33:13.752630Z","shell.execute_reply.started":"2023-04-09T09:33:13.737023Z","shell.execute_reply":"2023-04-09T09:33:13.750874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Choose batch 200 to make sure we have enough images from each class to display\nvisual_dataset = load_dataset(TRAINING_FILENAMES, labeled=True, ordered=True)\nvisual_dataset = visual_dataset.batch(200)\nvisual_batch = next(iter(visual_dataset))","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:13.754333Z","iopub.execute_input":"2023-04-09T09:33:13.755245Z","iopub.status.idle":"2023-04-09T09:33:20.865023Z","shell.execute_reply.started":"2023-04-09T09:33:13.755184Z","shell.execute_reply":"2023-04-09T09:33:20.863670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" # **\"0\": \"Cassava Bacterial Blight (CBB)\"**","metadata":{}},{"cell_type":"code","source":"# Display 9 images from Class 0 (CBB)\ntarget_label = 0\ndisplay_random_images_for_label(visual_batch, target_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:20.869458Z","iopub.execute_input":"2023-04-09T09:33:20.869904Z","iopub.status.idle":"2023-04-09T09:33:22.348438Z","shell.execute_reply.started":"2023-04-09T09:33:20.869846Z","shell.execute_reply":"2023-04-09T09:33:22.346492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# \"1\": \"Cassava Brown Streak Disease (CBSD)\"","metadata":{}},{"cell_type":"code","source":"# Display 9 images from Class 1 (CBSD)\ntarget_label = 1\ndisplay_random_images_for_label(visual_batch, target_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:22.350325Z","iopub.execute_input":"2023-04-09T09:33:22.351666Z","iopub.status.idle":"2023-04-09T09:33:23.728786Z","shell.execute_reply.started":"2023-04-09T09:33:22.351611Z","shell.execute_reply":"2023-04-09T09:33:23.727414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# \"2\": \"Cassava Green Mottle (CGM)\"","metadata":{}},{"cell_type":"code","source":"# Display 9 images from Class 2 (CGM)\ntarget_label = 2\ndisplay_random_images_for_label(visual_batch, target_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:23.730777Z","iopub.execute_input":"2023-04-09T09:33:23.731633Z","iopub.status.idle":"2023-04-09T09:33:25.087978Z","shell.execute_reply.started":"2023-04-09T09:33:23.731580Z","shell.execute_reply":"2023-04-09T09:33:25.086393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# \"3\": \"Cassava Mosaic Disease (CMD)\"","metadata":{}},{"cell_type":"code","source":"# Display 9 images from Class 3 (CMD)\ntarget_label = 3\ndisplay_random_images_for_label(visual_batch, target_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:25.089828Z","iopub.execute_input":"2023-04-09T09:33:25.091423Z","iopub.status.idle":"2023-04-09T09:33:26.512152Z","shell.execute_reply.started":"2023-04-09T09:33:25.091322Z","shell.execute_reply":"2023-04-09T09:33:26.511000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# \"4\": \"Healthy\"","metadata":{}},{"cell_type":"code","source":"# Display 9 images from Class 4 (Healthy)\ntarget_label = 4\ndisplay_random_images_for_label(visual_batch, target_label)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:26.513863Z","iopub.execute_input":"2023-04-09T09:33:26.514583Z","iopub.status.idle":"2023-04-09T09:33:27.866554Z","shell.execute_reply.started":"2023-04-09T09:33:26.514537Z","shell.execute_reply":"2023-04-09T09:33:27.864498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_one_original_and_one_preprocessed_image_per_class(original_dataset, preprocessed_dataset):\n    original_images, original_labels = batch_to_numpy_images_and_labels(original_dataset)\n    preprocessed_images, preprocessed_labels = batch_to_numpy_images_and_labels(preprocessed_dataset)\n\n    plt.figure(figsize=(20, 8))\n\n    for label in range(len(CLASSES)):\n        # Find the first occurrence of each label\n        index = np.where(original_labels == label)[0][0]\n\n        # Display original image\n        plt.subplot(2, len(CLASSES), label + 1)\n        plt.xticks([])\n        plt.yticks([])\n        plt.grid(False)\n        plt.imshow(original_images[index])\n        plt.xlabel(\"Original\\nClass: {}\".format(CLASSES[label]))\n\n        # Display preprocessed image\n        plt.subplot(2, len(CLASSES), label + 1 + len(CLASSES))\n        plt.xticks([])\n        plt.yticks([])\n        plt.grid(False)\n        plt.imshow(preprocessed_images[index])\n        plt.xlabel(\"Preprocessed\\nClass: {}\".format(CLASSES[label]))\n\n    plt.show()\n\n","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:27.868104Z","iopub.execute_input":"2023-04-09T09:33:27.868548Z","iopub.status.idle":"2023-04-09T09:33:27.880671Z","shell.execute_reply.started":"2023-04-09T09:33:27.868506Z","shell.execute_reply":"2023-04-09T09:33:27.878946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_two_original_and_two_preprocessed_image_per_class(original_dataset, preprocessed_dataset):\n    original_images, original_labels = batch_to_numpy_images_and_labels(original_dataset)\n    preprocessed_images, preprocessed_labels = batch_to_numpy_images_and_labels(preprocessed_dataset)\n\n    plt.figure(figsize=(20, 16))\n\n    for label in range(len(CLASSES)):\n        # Find the first two occurrences of each label\n        indices = np.where(original_labels == label)[0][:2]\n\n        for i, index in enumerate(indices):\n            # Display original image\n            plt.subplot(4, len(CLASSES), 2 * i * len(CLASSES) + label + 1)\n            plt.xticks([])\n            plt.yticks([])\n            plt.grid(False)\n            plt.imshow(original_images[index])\n            plt.xlabel(\"Original\\nClass: {}\".format(CLASSES[label]))\n\n            # Display preprocessed image\n            plt.subplot(4, len(CLASSES), 2 * i * len(CLASSES) + label + 1 + len(CLASSES))\n            plt.xticks([])\n            plt.yticks([])\n            plt.grid(False)\n            plt.imshow(preprocessed_images[index])\n            plt.xlabel(\"Preprocessed\\nClass: {}\".format(CLASSES[label]))\n\n    plt.show()\n\n\n","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:27.882819Z","iopub.execute_input":"2023-04-09T09:33:27.883519Z","iopub.status.idle":"2023-04-09T09:33:27.897383Z","shell.execute_reply.started":"2023-04-09T09:33:27.883238Z","shell.execute_reply":"2023-04-09T09:33:27.896128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Obtain the original dataset, the ordered = True to make sure that the order of the images we show corresponds\noriginal_dataset = load_dataset(TRAINING_FILENAMES, labeled=True, ordered=True)\noriginal_dataset = original_dataset.batch(80)\noriginal_batch = next(iter(original_dataset))\n\n# Obtain the dataset after preprocess\npreprocessed_dataset = load_dataset(TRAINING_FILENAMES, labeled=True, ordered=True)\npreprocessed_dataset = preprocessed_dataset.map(data_augment, num_parallel_calls=AUTOTUNE)\npreprocessed_dataset = preprocessed_dataset.batch(80)\npreprocessed_batch = next(iter(preprocessed_dataset))","metadata":{"id":"3J8syey80vYo","outputId":"48635bcf-02ff-4e2e-f3f0-2901864b5803","_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-04-09T09:33:27.898666Z","iopub.execute_input":"2023-04-09T09:33:27.899079Z","iopub.status.idle":"2023-04-09T09:33:44.237076Z","shell.execute_reply.started":"2023-04-09T09:33:27.899039Z","shell.execute_reply":"2023-04-09T09:33:44.235170Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Original and Preprocessed Images","metadata":{}},{"cell_type":"code","source":"# Display \ndisplay_one_original_and_one_preprocessed_image_per_class(original_batch, preprocessed_batch)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:44.239020Z","iopub.execute_input":"2023-04-09T09:33:44.239458Z","iopub.status.idle":"2023-04-09T09:33:46.150740Z","shell.execute_reply.started":"2023-04-09T09:33:44.239418Z","shell.execute_reply":"2023-04-09T09:33:46.147534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_two_original_and_two_preprocessed_image_per_class(original_batch, preprocessed_batch)","metadata":{"execution":{"iopub.status.busy":"2023-04-09T09:33:46.152953Z","iopub.execute_input":"2023-04-09T09:33:46.153546Z","iopub.status.idle":"2023-04-09T09:33:49.090105Z","shell.execute_reply.started":"2023-04-09T09:33:46.153499Z","shell.execute_reply":"2023-04-09T09:33:49.088403Z"},"trusted":true},"execution_count":null,"outputs":[]}]}