{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport cv2\nimport tensorflow as tf\nfrom tensorflow.keras import Input, Model\nfrom keras.layers import Dense, Dropout, Flatten, add, BatchNormalization\nimport keras\nfrom keras.applications.xception import Xception\nfrom tensorflow.keras.applications.vgg16 import VGG16\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom tensorflow.keras.applications import InceptionV3\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.utils import to_categorical\nfrom keras.layers.experimental.preprocessing import RandomFlip, RandomRotation, RandomCrop, Rescaling, RandomTranslation\nfrom keras import Sequential\nfrom tqdm import tqdm\nfrom tensorflow.data import Dataset\nfrom kaggle_datasets import KaggleDatasets\nimport tensorflow_addons as tfa\nimport random\nfrom functools import partial\nfrom sklearn.model_selection import train_test_split\nimport re\nseed = 456\nbatch_size = 16\nrandom.seed(seed)\nimage_size = 512","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Counting the Number of samples in the dataset"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"root_dir = '../input/cassava-leaf-disease-classification'\n\ntrain_df = pd.read_csv(os.path.join(root_dir, 'train.csv'))\nprint(\"there are \" + str(train_df.shape[0]) + \" train samples\" )\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Short block to print samples of the train set, each element in a row belongs to the same category"},{"metadata":{"trusted":true},"cell_type":"code","source":"# now looking at examples of each category\n\n#{\"0\": \"Cassava Bacterial Blight (CBB)\", \n#\"1\": \"Cassava Brown Streak Disease (CBSD)\",\n#\"2\": \"Cassava Green Mottle (CGM)\", \n#\"3\": \"Cassava Mosaic Disease (CMD)\",\n#\"4\": \"Healthy\"}\n\n\n\ntrain_img_dir = os.path.join(root_dir, 'train_images')  \n\nfigure = plt.figure(figsize = (20,20))\n\ncont = 0\n    \nfor i in range(5):\n    \n    speci = train_df[train_df['label'] == i]\n    \n    for j in range(5):\n        \n        img = Image.open(os.path.join(train_img_dir, speci.iloc[j,0]))\n        \n        plt.subplot(5,5, cont+1)\n        \n        plt.imshow(img)\n        \n        cont = cont + 1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"help functions for the tensorflow documentation"},{"metadata":{"trusted":true},"cell_type":"code","source":"def _parse_image_function(example_proto):\n  # Parse the input tf.train.Example proto using the dictionary above.\n  return tf.io.parse_single_example(example_proto, image_feature_description)\n\ndef _bytes_feature(value):\n  \"\"\"Returns a bytes_list from a string / byte.\"\"\"\n  if isinstance(value, type(tf.constant(0))):\n    value = value.numpy() # BytesList won't unpack a string from an EagerTensor.\n  return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))\n\ndef _float_feature(value):\n  \"\"\"Returns a float_list from a float / double.\"\"\"\n  return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))\n\ndef _int64_feature(value):\n  \"\"\"Returns an int64_list from a bool / enum / int / uint.\"\"\"\n  return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Functions to read, augment and return the datasets"},{"metadata":{"trusted":true},"cell_type":"code","source":"autotune = tf.data.experimental.AUTOTUNE \n\nrecords_dir = \"../input/cassava-leaf-disease-classification/train_tfrecords\"\n\ntrain_tfnames, valid_tfnames = train_test_split(\n    tf.io.gfile.glob(records_dir + \"/ld_train*.tfrec\"),\n    test_size=0.2, random_state=5\n)\n\ntest_records_dir =  \"../input/cassava-leaf-disease-classification/test_tfrecords\"\ntfnames_test = tf.io.gfile.glob(test_records_dir + \"/ld_test*.tfrec\")\n\n\n\ndef read_tf(example, labeled):\n    \n    \"\"\"\n    this function reads a record and returns its feautres\n    \n    \"\"\"\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 = tf.io.decode_jpeg(example['image'])\n    image = tf.cast(image, tf.float32) / 255.0\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    \n    \ndef load_ds(filenames, labeled = True):\n    \n    tfrecords = tf.data.TFRecordDataset(filenames)\n    dataset = tfrecords.map(partial(read_tf,labeled = labeled), num_parallel_calls = autotune)\n    return dataset\n\ndef augment(image, label):\n    angle = random.random() * 0.79 #random angle between 0 and 0.79 radians roughly 45 degrees \n    image = tf.image.random_flip_up_down(image, seed = seed) \n    image = tf.image.random_flip_left_right(image, seed = seed)\n   # image = tfa.image.gaussian_filter2d(image, 2) I removed the computing burden would increase too much\n    image = tfa.image.rotate(image, angle) #rotating image with a random angle\n    image = tf.image.random_crop(image, [image_size,image_size,3], seed = seed) # randomly cropping a 300x300 section of the image\n    \n    return image, label\n    \n    \ndef augment_val(image, label):\n    image = tf.image.random_crop(image, [image_size,image_size,3], seed = seed) # randomly cropping a 300x300 section of the image\n    \n\n    return image, label\n\ndef get_train_ds():\n    datset = load_ds(train_tfnames)\n    datset = datset.map(augment, num_parallel_calls = autotune)\n    datset = datset.repeat()\n    datset = datset.shuffle(seed)\n    datset = datset.batch(batch_size)\n    datset = datset.prefetch(autotune)\n    \n    return datset\n\ndef get_test_df():\n    \n    datset = load_ds(test_tfnames, labeled = False)\n    datset = datset.map(augment, num_parallel_calls = autotune)\n    datset = datset.batch(batch_size)\n    datset = datset.prefetch(autotune)\n    \n    return datset\n    \n\ndef get_val_ds():\n    dataset = load_ds(valid_tfnames, labeled=True) \n    dataset = dataset.map(augment_val, num_parallel_calls = autotune)\n    dataset = dataset.repeat()\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(autotune)\n    return dataset\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Creating the Model, Several pretrained models were used and their output was normalized to a 512 layer to sum them in the main "},{"metadata":{"trusted":true},"cell_type":"code","source":"#pretrained models\n\nxception_weights_dir = '../input/xception-form-keras/xception_weights_tf_dim_ordering_tf_kernels_notop.h5'\nbase_xcep = Xception(weights = xception_weights_dir, include_top = False, input_shape = (image_size, image_size, 3))\nbase_xcep.trainable  = False \n\n#vgg16_weights_dir = \"../input/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\"\n#base_vgg16 = VGG16(weights = vgg16_weights_dir, include_top = False, input_shape = (image_size,image_size,3))\n#base_vgg16.trainable = False\n\n#resnet50_weights_dir =  \"../input/resnet50-keras/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\"\n#base_resnet50 = ResNet50(weights = resnet50_weights_dir, include_top = False, input_shape = (image_size,image_size,3))\n#base_resnet50.trainable = False\n\n#inceptionv3_dir = \"../input/aaaaa/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5\"\n#base_incepv3 = InceptionV3(weights = inceptionv3_dir, include_top = False, input_shape = (image_size, image_size, 3))\n#base_incepv3.trainable = False\n\nefficientnet_dir = \"../input/efficientnet-b0-for-keras-no-top/efficientnetb0_notop.h5\"\nbase_efficientnet = tf.keras.applications.EfficientNetB0(weights = efficientnet_dir, include_top = False, input_shape = (image_size, image_size, 3))\nbase_efficientnet.trainable = False\n\n    # function to create a base model with standard output of 1024\n\ndef create_base_model(base_model):\n\n        inputs = Input(shape = (image_size,image_size,3))\n        \n        \n\n        base_out = base_model(inputs)\n\n        base_out = Flatten()(base_out)\n\n        base_out = Dropout(0.3)(base_out)\n\n        base1_out = Dense(1024, activation = 'relu')(base_out)\n\n        model = Model(inputs = inputs, outputs = base1_out)\n\n        return model\n\nxcep_base = create_base_model(base_xcep)\n#vgg16_base = create_base_model(base_vgg16)\n#resnet50_base = create_base_model(base_resnet50)\n#incep_base = create_base_model(base_incepv3)\neff_base = create_base_model(base_efficientnet)\n\ndef create_model(base1, base2):\n\n        inputs = Input(shape = (image_size,image_size,3)) \n\n    \n    \n        base1_out = base1(inputs)\n        base2_out = base2(inputs)\n        \n        base_out = add([base1_out, base2_out])\n\n\n        outputs = Dense(5, activation = 'softmax')(base_out)\n\n        model = Model(inputs = inputs, outputs = outputs)\n\n        model.summary()\n\n        model.compile(optimizer = 'adam', loss = 'sparse_categorical_crossentropy')\n\n        return model\n\n\n\nmodel = create_model(eff_base,xcep_base)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"EfficientNet use"},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 30\nsteps_per_epoch = count_data_items(train_tfnames) // batch_size\nsteps_per_epoch_val = count_data_items(valid_tfnames) // batch_size\n\ncall_list = [tf.keras.callbacks.EarlyStopping(\n    monitor='val_loss', patience=2, restore_best_weights=False\n)]\n\nval_ds = get_val_ds()\ntrain_ds = get_train_ds()\n\nhistory = model.fit(train_ds, epochs = epochs, steps_per_epoch = steps_per_epoch, validation_data = val_ds,\n                   callbacks = call_list, validation_steps = steps_per_epoch_val)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The testing stage is not done with the TFRecords because it was giving errors. Instead the images were used directly from the files. TTA applied to improve the accuracy of the output."},{"metadata":{"trusted":true},"cell_type":"code","source":"image_preprocessor = Sequential([\n    RandomFlip(\"horizontal_and_vertical\"),\n    RandomCrop(image_size,image_size),\n    RandomRotation(0.25),\n    Rescaling(1./255)])\n\n\n\ntest_dir = \"../input/cassava-leaf-disease-classification/test_images\"\n\ntest_names = pd.Series(os.listdir(test_dir))\n\n\nfor j in range(3):\n\n    for i in tqdm(range(len(test_names))):\n\n        image = cv2.imread(os.path.join(test_dir, test_names[i]))\n        image = np.expand_dims(image, axis = 0)\n        image = image_preprocessor(image)\n        if i ==0:\n\n            pred = model.predict(image)\n        else:\n            pred = np.concatenate([pred, model.predict(image)])\n            \n    if j ==0:\n        final = pred\n    else:\n        final = final +pred\n     \npred = pd.Series(np.argmax(final, axis = 1))\n\n\ntest_df = pd.concat([test_names, pred], axis = 1)\ntest_df = test_df.rename(columns = {0: 'image_id', 1: 'label'})\n\ntest_df.to_csv('submission.csv', index = False)\n\n","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}