{"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":"code","source":"!pip install -q efficientnet","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:39:54.871975Z","iopub.execute_input":"2021-06-18T17:39:54.872548Z","iopub.status.idle":"2021-06-18T17:40:03.722934Z","shell.execute_reply.started":"2021-06-18T17:39:54.872455Z","shell.execute_reply":"2021-06-18T17:40:03.721789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Importing required libraries\nimport os,time,sys,re\nimport tensorflow as tf\nimport numpy as np\nfrom matplotlib import pyplot as plt\nfrom kaggle_datasets import KaggleDatasets\nfrom tensorflow.keras.models import load_model\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom keras.callbacks import ModelCheckpoint\nimport efficientnet.tfkeras as efn\nprint(\"Running Tensorflow version: \" + str(tf.__version__))\nAUTO = tf.data.experimental.AUTOTUNE","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-18T17:40:03.725028Z","iopub.execute_input":"2021-06-18T17:40:03.725455Z","iopub.status.idle":"2021-06-18T17:40:09.969105Z","shell.execute_reply.started":"2021-06-18T17:40:03.725411Z","shell.execute_reply":"2021-06-18T17:40:09.967989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Detecting accelerator hardware\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\nexcept ValueError:\n    tpu = None\n    gpus = tf.config.experimental.list_logical_devices('GPU')\n\n#Select appropriate distribution strategy for hardware\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\n    print(\"Running on TPU: \" + str(tpu.master()))\nelif len(gpus) > 0:\n    strategy = tf.distribute.MirroredStrategy(gpus)\n    print(\"Running on \",len(gpus),\" GPU(s)\")\nelse:\n    strategy = tf.distribute.get_strategy()\n    print(\"Running on CPU\")","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","execution":{"iopub.status.busy":"2021-06-18T17:40:09.971454Z","iopub.execute_input":"2021-06-18T17:40:09.971872Z","iopub.status.idle":"2021-06-18T17:40:15.824249Z","shell.execute_reply.started":"2021-06-18T17:40:09.971827Z","shell.execute_reply":"2021-06-18T17:40:15.823123Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Declaring useful variables\ndef count_data_items(filenames):\n    # the number of data items is written in the name of the .tfrec files, i.e. flowers00-230.tfrec = 230 data items\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nIMG_SIZE = 512\nGCS_BASE_PATH = KaggleDatasets().get_gcs_path()\nprint(GCS_BASE_PATH)\nprint(\"GCS BASE PATH: \" + str(GCS_BASE_PATH))\n\nGCS_PATHS = {\n    192 : GCS_BASE_PATH + '/tfrecords-jpeg-192x192',\n    224 : GCS_BASE_PATH + '/tfrecords-jpeg-224x224',\n    391 : GCS_BASE_PATH + '/tfrecords-jpeg-331x331',\n    512 : GCS_BASE_PATH + '/tfrecords-jpeg-512x512'\n}\n\nGCS_PATH = GCS_PATHS[IMG_SIZE]\n\nIMAGE_SIZE = [IMG_SIZE,IMG_SIZE]\nEPOCHS = 20\n\n#Classes for flowers to be classified into\nCLASSES = ['Pink Primrose', 'Hard-leaved Pocket Orchid', 'Canterbury Bells', 'Sweet Pea', 'Wild Geranium',\n           'Tiger Lily', 'Moon Orchid', 'Bird Of Paradise', 'Monkshood', 'Globe Thistle',\n           'Snapdragon', \"Colt's Foot\", 'King Protea', 'Spear Thistle', 'Yellow Iris',\n           'Globe-flower', 'Purple Coneflower', 'Peruvian Lily', 'Balloon Flower', 'Giant White Arum Lily',\n           'Fire Lily', 'Pincushion Flower', 'Fritillary', 'Red Ginger', 'Grape Hyacinth',\n           'Corn Poppy', 'Prince Of Wales Feathers', 'Stemless Gentian', 'Artichoke', 'Sweet William',\n           'Carnation', 'Garden Phlox', 'Love In The Mist', 'Cosmos', 'Alpine Sea Holly',\n           'Ruby-lipped Cattleya', 'Cape Flower', 'Great Masterwort', 'Siam Tulip', 'Lenten Rose',\n           'Barberton Daisy', 'Daffodil', 'Sword Lily', 'Poinsettia', 'Bolero Deep Blue',\n           'Wallflower', 'Marigold', 'Buttercup', 'Daisy', 'Common Dandelion', 'Petunia', 'Wild Pansy',\n           'Primula', 'Sunflower', 'Lilac Hibiscus', 'Bishop Of Llandaff', 'Gaura', 'Geranium', 'Orange Dahlia',\n           'Pink-yellow Dahlia', 'Cautleya Spicata', 'Japanese Anemone', 'Black-eyed Susan', 'Silverbush',\n           'Californian Poppy', 'Osteospermum', 'Spring Crocus', 'Iris', 'Windflower', 'Tree Poppy', 'Gazania',\n           'Azalea', 'Water Lily', 'Rose', 'Thorn Apple', 'Morning Glory', 'Passion Flower', 'Lotus', 'Toad Lily',\n           'Anthurium', 'Frangipani', 'Clematis', 'Hibiscus', 'Columbine', 'Desert-rose', 'Tree Mallow',\n           'Magnolia', 'Cyclamen ', 'Watercress', 'Canna Lily', 'Hippeastrum ', 'Bee Balm', 'Pink Quill',\n           'Foxglove', 'Bougainvillea', 'Camellia', 'Mallow', 'Mexican Petunia', 'Bromelia', 'Blanket Flower',\n           'Trumpet Creeper', 'Blackberry Lily', 'Common Tulip', 'Wild Rose']\n\n#Learning rate scheduling variables\nnum_units = strategy.num_replicas_in_sync\nif num_units == 8:\n    BATCH_SIZE = 16 * num_units\n    VALIDATION_BATCH_SIZE = 16 * num_units\n    start_lr = 0.00001\n    min_lr = 0.00001\n    max_lr = 0.00005 * num_units\n    rampup_epochs = 8\n    sustain_epochs = 0\n    exp_decay = 0.8\nelif num_units == 1:\n    BATCH_SIZE = 16\n    VALIDATION_BATCH_SIZE = 16\n    start_lr = 0.00001\n    min_lr = 0.00001\n    max_lr = 0.0002\n    rampup_epochs = 8\n    sustain_epochs = 0\n    exp_decay = 0.8\nelse:\n    BATCH_SIZE = 8 * num_units\n    VALIDATION_BATCH_SIZE = 8 * num_units\n    start_lr = 0.00001\n    min_lr = 0.00001\n    max_lr = 0.00002 * num_units\n    rampup_epochs = 11\n    sustain_epochs = 0\n    exp_decay = 0.8\n    \nTRAINING_FILENAMES   = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILENAMES       = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')\nTRAIN_STEPS = count_data_items(TRAINING_FILENAMES) // BATCH_SIZE\n\nprint(\"TRAINING IMAGES: \", count_data_items(TRAINING_FILENAMES), \", STEPS PER EPOCH: \", TRAIN_STEPS)\nprint(\"VALIDATION IMAGES: \", count_data_items(VALIDATION_FILENAMES))\nprint(\"Total training files: \" + str(len(TRAINING_FILENAMES)))\n\nprint(\"Total validation files: \" + str(len(VALIDATION_FILENAMES)))\nprint(\"Total test files: \" + str(len(TEST_FILENAMES)))","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:15.826211Z","iopub.execute_input":"2021-06-18T17:40:15.826579Z","iopub.status.idle":"2021-06-18T17:40:16.499532Z","shell.execute_reply.started":"2021-06-18T17:40:15.826545Z","shell.execute_reply":"2021-06-18T17:40:16.498527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Display Utilities\ndef dataset_to_numpy_util(dataset,batch_size):\n    dataset = dataset.batch(batch_size)\n    \n    for images,labels in dataset:\n        numpy_images = images.numpy()\n        numpy_labels = labels.numpy()\n        break\n    \n    return numpy_images,numpy_labels\n\ndef get_title(label,correct_label):\n    if label.lower() == correct_label.lower():\n        return ((label + ' [CORRECT]'),False)\n    else:\n        return ((label + ' wrong [Should be ' + correct_label + ']'),True)\n\ndef display_flower(image,title,subplot,red=False):\n    plt.subplot(*subplot)\n    plt.axis('off')\n    plt.imshow(image)\n    plt.title(title,fontsize=16,color = 'red' if red else 'black')\n    return (subplot[0],subplot[1],subplot[2] + 1)\n\ndef display_16_flowers_from_dataset(dataset,test=False):\n    subplot = (4,4,1)\n    plt.figure(figsize=(13,13))\n    images,labels = dataset_to_numpy_util(dataset,16)\n    for i,image in enumerate(images):\n        title = CLASSES[labels[i]] if not test else ''\n        subplot = display_flower(image,title,subplot)\n        if i >= 15:\n            break\n    plt.tight_layout()\n    plt.subplots_adjust(wspace = 0.1,hspace = 0.1)\n    plt.show()\n    \ndef display_16_images_with_predictions(images,predictions,labels):\n    subplot = (4,4,1)\n    plt.figure(figsize=(13,13))\n    classes = np.argmax(predictions, axis=-1)\n    for i,image in enumerate(images):\n        title,fault = get_title(classes[i],labels[i])\n        subplot = display_flower(image,title,subplot,fault)\n        if i>=15:\n            break\n    plt.tight_layout()\n    plt.subplots_adjust(wspace=0.1, hspace=0.1)\n    plt.show()\n    \ndef display_training_curves(training,validation,title,subplot):\n    if subplot%10 == 1:\n        plt.subplots(figsize = (10,10),facecolor='#F0F0F0')\n        plt.tight_layout()\n    ax = plt.subplot(subplot)\n    ax.set_facecolor('#F8F8F8')\n    ax.plot(training)\n    ax.plot(validation)\n    ax.set_title('model '+ title)\n    ax.set_ylabel(title)\n    ax.set_xlabel('epoch')\n    ax.legend(['train', 'valid.'])","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:16.502845Z","iopub.execute_input":"2021-06-18T17:40:16.503149Z","iopub.status.idle":"2021-06-18T17:40:16.518725Z","shell.execute_reply.started":"2021-06-18T17:40:16.50312Z","shell.execute_reply":"2021-06-18T17:40:16.51794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Functions for reading images and labels from dataset\n\ndef decode_image(image):\n    image = tf.image.decode_jpeg(image,channels = 3)\n    image = tf.cast(image,tf.float32)\n    image = image/255.0\n    image = tf.reshape(image,[*IMAGE_SIZE,3])\n    return image\n\ndef read_labeled_tfrecord(example):\n    labeled_features = {\n        'image' : tf.io.FixedLenFeature([],tf.string),\n        'class' : tf.io.FixedLenFeature([],tf.int64)\n    }\n    example = tf.io.parse_single_example(example,labeled_features)\n    image = decode_image(example['image'])\n    label = tf.cast(example['class'],tf.int32)\n    return image,label\n\ndef read_unlabeled_tfrecord(example):\n    unlabeled_features = {\n        'image' : tf.io.FixedLenFeature([],tf.string),\n        'id'    : tf.io.FixedLenFeature([],tf.string)\n    }\n    example = tf.io.parse_single_example(example,unlabeled_features)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image,idnum\n\ndef load_dataset(filenames,labeled = True):\n    ignore_order = tf.data.Options()\n    ignore_order.experimental_deterministic = False\n    \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads = AUTO)\n    dataset = dataset.with_options(ignore_order)\n    if labeled:\n        dataset = dataset.map(read_labeled_tfrecord,num_parallel_calls = AUTO)\n    else:\n        dataset = dataset.map(read_unlabeled_tfrecord,num_parallel_calls = AUTO)\n    return dataset\n\ndef data_augment(image,label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_saturation(image,0,2)\n    return image,label\n\ndef get_batched_dataset(filenames,labeled = True, train = False,validation=False):\n    dataset = load_dataset(filenames,labeled)\n    dataset = dataset.cache()\n    if train:\n        dataset = dataset.map(data_augment, num_parallel_calls = AUTO)\n        dataset = dataset.repeat()\n        dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef learningrate_function(epoch):\n    if epoch < rampup_epochs:\n        lr = (max_lr - start_lr)/rampup_epochs * epoch + start_lr\n    elif epoch < rampup_epochs + sustain_epochs:\n        lr = max_lr\n    else:\n        lr = (max_lr - min_lr) * exp_decay**(epoch - rampup_epochs - sustain_epochs) + min_lr\n    return lr\n\ndef learning_rate_callback():\n    lr_callback = tf.keras.callbacks.LearningRateScheduler(lambda epoch : learningrate_function(epoch),verbose = True)\n    rng = [i for i in range(EPOCHS)]\n    y = [learningrate_function(x) for x in range(EPOCHS)]\n    plt.plot(rng,y)\n    return lr_callback","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:16.519588Z","iopub.execute_input":"2021-06-18T17:40:16.519831Z","iopub.status.idle":"2021-06-18T17:40:16.554688Z","shell.execute_reply.started":"2021-06-18T17:40:16.519807Z","shell.execute_reply":"2021-06-18T17:40:16.553518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"training_dataset = get_batched_dataset(TRAINING_FILENAMES,labeled = True, train = True)\nvalidation_dataset = get_batched_dataset(VALIDATION_FILENAMES,labeled = True, train = False)\ntest_dataset = get_batched_dataset(TEST_FILENAMES,labeled = False, train = False)\n","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:16.556367Z","iopub.execute_input":"2021-06-18T17:40:16.556773Z","iopub.status.idle":"2021-06-18T17:40:16.897653Z","shell.execute_reply.started":"2021-06-18T17:40:16.55673Z","shell.execute_reply":"2021-06-18T17:40:16.896777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display_16_flowers_from_dataset(load_dataset(VALIDATION_FILENAMES,labeled=True),test=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:16.899749Z","iopub.execute_input":"2021-06-18T17:40:16.90023Z","iopub.status.idle":"2021-06-18T17:40:20.806795Z","shell.execute_reply.started":"2021-06-18T17:40:16.9002Z","shell.execute_reply":"2021-06-18T17:40:20.80356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lr_callback = learning_rate_callback()","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:20.808906Z","iopub.execute_input":"2021-06-18T17:40:20.809331Z","iopub.status.idle":"2021-06-18T17:40:20.989116Z","shell.execute_reply.started":"2021-06-18T17:40:20.809274Z","shell.execute_reply":"2021-06-18T17:40:20.98795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Creating the model\nwith strategy.scope():\n    enet = efn.EfficientNetB0(\n    input_shape = (512,512,3),\n    weights = 'imagenet',\n    include_top = False)\n    enet.trainable = True\n    \n    model = tf.keras.Sequential([\n        enet,\n        #pretrained_model,\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(len(CLASSES),activation = 'softmax',dtype=tf.float32)\n    ])\n\n    model.compile(\n        loss = 'sparse_categorical_crossentropy',\n        optimizer = 'adam',\n        metrics = ['accuracy']\n    )\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:20.990878Z","iopub.execute_input":"2021-06-18T17:40:20.991316Z","iopub.status.idle":"2021-06-18T17:40:34.277242Z","shell.execute_reply.started":"2021-06-18T17:40:20.991258Z","shell.execute_reply":"2021-06-18T17:40:34.276579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Fine tuning the model by training\nfilepath = \"model.hdf5\"\ncheckpoint = ModelCheckpoint(filepath, monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')\ncallbacks = [checkpoint, lr_callback]\n\nstart_time = time.time()\nhist = model.fit(training_dataset, validation_data = validation_dataset,steps_per_epoch = TRAIN_STEPS, epochs = EPOCHS, callbacks = callbacks )\nfinal_accuracy = hist.history[\"val_accuracy\"][-5:]\n\nprint(\"FINAL ACCURACY MEAN-5: \", np.mean(final_accuracy))\nprint(\"TRAINING TIME: \", time.time() - start_time, \" sec\")","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:40:34.278238Z","iopub.execute_input":"2021-06-18T17:40:34.278634Z","iopub.status.idle":"2021-06-18T17:52:14.224556Z","shell.execute_reply.started":"2021-06-18T17:40:34.278604Z","shell.execute_reply":"2021-06-18T17:52:14.223469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Visualizing training curves\ndisplay_training_curves(hist.history['loss'], hist.history['val_loss'], 'loss', 211)\ndisplay_training_curves(hist.history['accuracy'], hist.history['val_accuracy'], 'accuracy', 212)","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:52:14.226524Z","iopub.execute_input":"2021-06-18T17:52:14.226951Z","iopub.status.idle":"2021-06-18T17:52:14.664141Z","shell.execute_reply.started":"2021-06-18T17:52:14.226907Z","shell.execute_reply":"2021-06-18T17:52:14.663439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.save('plant_model.hdf5')","metadata":{"execution":{"iopub.status.busy":"2021-06-18T17:52:14.665177Z","iopub.execute_input":"2021-06-18T17:52:14.665613Z","iopub.status.idle":"2021-06-18T17:52:17.108936Z","shell.execute_reply.started":"2021-06-18T17:52:14.665571Z","shell.execute_reply":"2021-06-18T17:52:17.107885Z"},"trusted":true},"execution_count":null,"outputs":[]}]}