{"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 # 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\nfor 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 20GB 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","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\nfrom sklearn.utils import shuffle\nfrom sklearn.utils import class_weight\nfrom sklearn.preprocessing import minmax_scale\nfrom sklearn.model_selection import train_test_split\nimport random\nimport cv2\nfrom imgaug import augmenters as iaa\nimport warnings\nwarnings.filterwarnings('ignore')\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.layers import Dense, Dropout, Activation, Input, BatchNormalization, GlobalAveragePooling2D\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping\nfrom tensorflow.keras.experimental import CosineDecay\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.layers.experimental.preprocessing import RandomCrop,CenterCrop, RandomRotation","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"training_folder = '../input/cassava-leaf-disease-classification/train_images/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"samples_df = pd.read_csv(\"../input/cassava-leaf-disease-classification/train.csv\")\nsamples_df = shuffle(samples_df, random_state=10)\nsamples_df[\"filepath\"] = training_folder+samples_df[\"image_id\"]\nsamples_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"training_percentage = 0.8\ntraining_item_count = int(len(samples_df)*training_percentage)\nvalidation_item_count = len(samples_df)-int(len(samples_df)*training_percentage)\ntraining_df = samples_df[:training_item_count]\nvalidation_df = samples_df[training_item_count:]\n\nprint(training_df.shape)\nprint(validation_df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 8\nimage_size = 512\ninput_shape = (image_size, image_size, 3)\ndropout_rate = 0.4\nclasses_to_predict = sorted(training_df.label.unique())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"training_data = tf.data.Dataset.from_tensor_slices((training_df.filepath.values, training_df.label.values))\nvalidation_data = tf.data.Dataset.from_tensor_slices((validation_df.filepath.values, validation_df.label.values))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_image_and_label_from_path(image_path, label):\n    img = tf.io.read_file(image_path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    return img, label\n\nAUTOTUNE = tf.data.experimental.AUTOTUNE\n\ntraining_data = training_data.map(load_image_and_label_from_path, num_parallel_calls=AUTOTUNE)\nvalidation_data = validation_data.map(load_image_and_label_from_path, num_parallel_calls=AUTOTUNE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"training_data_batches = training_data.shuffle(buffer_size=1000).batch(batch_size).prefetch(buffer_size=AUTOTUNE)\nvalidation_data_batches = validation_data.shuffle(buffer_size=1000).batch(batch_size).prefetch(buffer_size=AUTOTUNE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"adapt_data = tf.data.Dataset.from_tensor_slices(training_df.filepath.values)\ndef adapt_mode(image_path):\n    img = tf.io.read_file(image_path)\n    img = tf.image.decode_jpeg(img, channels=3)\n    img = layers.experimental.preprocessing.Rescaling(1.0 / 255)(img)\n    return img\n\nadapt_data = adapt_data.map(adapt_mode, num_parallel_calls=AUTOTUNE)\nadapt_data_batches = adapt_data.shuffle(buffer_size=1000).batch(batch_size).prefetch(buffer_size=AUTOTUNE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_augmentation_layers = tf.keras.Sequential(\n    [\n        layers.experimental.preprocessing.RandomCrop(height=image_size, width=image_size),\n        layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        layers.experimental.preprocessing.RandomRotation(0.25),\n        layers.experimental.preprocessing.RandomZoom((-0.2, 0)),\n        layers.experimental.preprocessing.RandomContrast((0.2,0.2))\n    ]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = Image.open(\"../input/cassava-leaf-disease-classification/train_images/3412658650.jpg\")\nplt.imshow(image)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = tf.expand_dims(np.array(image), 0)\nprint(image.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nfor i in range(9):\n  augmented_image = data_augmentation_layers(image)\n  # print(augmented_image.shape)\n  ax = plt.subplot(3, 3, i + 1)\n  plt.imshow(augmented_image[0])\n  plt.axis(\"off\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"try: # detect TPUs\n    tpu = None\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver() # TPU detection\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept ValueError: # detect GPUs\n    strategy = tf.distribute.MirroredStrategy() # for GPU or multi-GPU machines\n    #strategy = tf.distribute.get_strategy() # default strategy that works on CPU and single GPU\n    #strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy() # for clusters of multi-GPU machines\n\nprint(\"Number of accelerators: \", strategy.num_replicas_in_sync)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"efficientnet = EfficientNetB3(weights=\"imagenet\", \n                              include_top=False, \n                              input_shape=input_shape, \n                              drop_connect_rate=dropout_rate)\ninputs = Input(shape=input_shape)\naugmented = data_augmentation_layers(inputs)\nefficientnet = efficientnet(augmented)\npooling = layers.GlobalAveragePooling2D()(efficientnet)\ndropout = layers.Dropout(dropout_rate)(pooling)\noutputs = Dense(len(classes_to_predict), activation=\"softmax\")(dropout)\nmodel = Model(inputs=inputs, outputs=outputs)\n    \nmodel.summary()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nmodel.get_layer('efficientnetb3').get_layer('normalization').adapt(adapt_data_batches)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 8\ndecay_steps = int(round(len(training_df)/batch_size))*epochs\ncosine_decay = CosineDecay(initial_learning_rate=1e-4, decay_steps=decay_steps, alpha=0.3)\n\nmodel_save = ModelCheckpoint('best_model.h5', \n                             save_best_only = True, \n                             save_weights_only = True,\n                             monitor = 'val_loss', \n                             mode = 'min', verbose = 1)\n\nearly_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n                           patience = 5, mode = 'min', verbose = 1,\n                           restore_best_weights = True)\n\nreduce_lr = ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n                              patience = 2, min_delta = 0.001, \n                              mode = 'min', verbose = 1)\n\ncallbacks = [model_save, early_stop, reduce_lr]\n\nmodel.compile(loss=\"sparse_categorical_crossentropy\", optimizer=tf.keras.optimizers.Adam(cosine_decay), metrics=[\"accuracy\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nhistory = model.fit(training_data_batches,\n                  epochs = epochs, \n                  validation_data=validation_data_batches,\n                  callbacks=callbacks)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Loss over epochs')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='best')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def scan_over_image(img_path, crop_size=512):\n    '''\n    Will extract 512x512 images covering the whole original image\n    with some overlap between images\n    '''\n    \n    img = Image.open(img_path)\n    img_height, img_width = img.size\n    img = np.array(img)\n    \n    y = random.randint(0,img_height-crop_size)\n    x = random.randint(0,img_width-crop_size)\n\n    x_img_origins = [0,img_width-crop_size]\n    y_img_origins = [0,img_height-crop_size]\n    img_list = []\n    for x in x_img_origins:\n        for y in y_img_origins:\n            img_list.append(img[x:x+crop_size , y:y+crop_size,:])\n  \n    return np.array(img_list)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def display_samples(img_path):\n    '''\n    Display all 512x512 images extracted from original images\n    '''\n    \n    img_list = scan_over_image(img_path)\n    sample_number = len(img_list)\n    fig = plt.figure(figsize = (8,sample_number))\n    for i in range(0,sample_number):\n        ax = fig.add_subplot(2, 4, i+1)\n        ax.imshow(img_list[i])\n        ax.set_title(str(i))\n    plt.tight_layout()\n    plt.show()\n\ndisplay_samples(\"../input/cassava-leaf-disease-classification/train_images/3412658650.jpg\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_time_augmentation_layers = tf.keras.Sequential(\n    [\n        layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        layers.experimental.preprocessing.RandomZoom((-0.2, 0)),\n        layers.experimental.preprocessing.RandomContrast((0.2,0.2))\n    ]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict_and_vote(image_filename, folder, TTA_runs=4):\n    '''\n    Run the model over 4 local areas of the given image,\n    before making a decision depending on the most predicted\n    disease.\n    '''\n    \n    #apply TTA to each of the 4 images and sum all predictions for each local image\n    localised_predictions = []\n    local_image_list = scan_over_image(folder+image_filename)\n    for local_image in local_image_list:\n        duplicated_local_image = tf.convert_to_tensor(np.array([local_image for i in range(TTA_runs)]))\n        augmented_images = test_time_augmentation_layers(duplicated_local_image)\n        \n        predictions = model.predict(augmented_images)\n        localised_predictions.append(np.sum(predictions, axis=0))\n    \n    #sum all predictions from all 4 images and retrieve the index of the highest value\n    global_predictions = np.sum(np.array(localised_predictions),axis=0)\n    final_prediction = np.argmax(global_predictions)\n    \n    return final_prediction","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def run_predictions_over_image_list(image_list, folder):\n    predictions = []\n    with tqdm(total=len(image_list)) as pbar:\n        for image_filename in image_list:\n            pbar.update(1)\n            predictions.append(predict_and_vote(image_filename, folder))\n    return predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_df[\"results\"] = run_predictions_over_image_list(validation_df[\"image_id\"], training_folder)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_df[:20]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"true_positives = 0\nprediction_distribution_per_class = {\"0\":{\"0\": 0, \"1\": 0, \"2\":0, \"3\":0, \"4\":0},\n                                     \"1\":{\"0\": 0, \"1\": 0, \"2\":0, \"3\":0, \"4\":0},\n                                     \"2\":{\"0\": 0, \"1\": 0, \"2\":0, \"3\":0, \"4\":0},\n                                     \"3\":{\"0\": 0, \"1\": 0, \"2\":0, \"3\":0, \"4\":0},\n                                     \"4\":{\"0\": 0, \"1\": 0, \"2\":0, \"3\":0, \"4\":0}}\nnumber_of_images = len(validation_df)\nfor idx, pred in validation_df.iterrows():\n    if int(pred[\"label\"]) == pred.results:\n        true_positives+=1\n    prediction_distribution_per_class[str(pred[\"label\"])][str(pred.results)] += 1\nprint(\"accuracy: {}%\".format(true_positives/number_of_images*100))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction_distribution_per_class","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"heatmap_df = pd.DataFrame(columns={\"groundtruth\",\"prediction\",\"value\"})\nfor key in prediction_distribution_per_class.keys():\n    for pred_key in prediction_distribution_per_class[key].keys():\n        value = prediction_distribution_per_class[key][pred_key]/validation_df.query(\"label==@key\").count()[0]\n        heatmap_df = heatmap_df.append({\"groundtruth\":key,\"prediction\":pred_key,\"value\":value}, ignore_index=True)   \n\nheatmap = heatmap_df.pivot(index='groundtruth', columns='prediction', values='value')\nsns.heatmap(heatmap,cmap=\"Blues\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_folder = '../input/cassava-leaf-disease-classification/test_images/'\nsubmission_df = pd.DataFrame(columns={\"image_id\",\"label\"})\nsubmission_df[\"image_id\"] =  os.listdir(test_folder)\nsubmission_df[\"label\"] = 0\n\nsubmission_df[\"label\"] = run_predictions_over_image_list(submission_df[\"image_id\"], test_folder)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}