{"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":"# Import Necesary Libraries","metadata":{}},{"cell_type":"code","source":"import math, re, os\nimport tensorflow as tf\nimport tensorflow.keras as keras\nimport tensorflow_addons as tfa\nimport numpy as np\nimport pandas as pd\nimport json\nimport random\nimport PIL\nfrom tqdm import tqdm\nfrom matplotlib import pyplot as plt\nfrom kaggle_datasets import KaggleDatasets\nfrom sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\n\nAUTO = tf.data.experimental.AUTOTUNE","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Setup and Detect TPU \n\nPlease read the kaggle documentation for using tpu: \n[Tensor Processing Units (TPUs) Documentation | Kaggle](https://www.kaggle.com/docs/tpu)","metadata":{}},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()  # TPU detection\n    print(\"Running on TPU \", tpu.cluster_spec().as_dict()[\"worker\"])\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:\n    print(\"Not connected to a TPU runtime. Using CPU/GPU strategy\")\n    strategy = tf.distribute.MirroredStrategy()\n\n# NEW on TPU in TensorFlow 24: shorter cross-compatible TPU/GPU/multi-GPU/cluster-GPU detection code\n\ntry: # detect TPUs\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.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)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_size = (512,512)\nepochs= 12\nbatch_size = 16 * strategy.num_replicas_in_sync\nprint('Batch size:', batch_size)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read Data From Google Cloud Storage (GCS)\n\nTPUs read data directly from Google Cloud Storage (GCS). This Kaggle utility will copy the dataset to a GCS bucket co-located with the TPU. If you have multiple datasets attached to the notebook, you can pass the name of a specific dataset to the get_gcs_path function. The name of the dataset is the name of the directory it is mounted in. Use !ls /kaggle/input/ to list attached datasets.\n\n> from kaggle_datasets import KaggleDatasets","metadata":{}},{"cell_type":"code","source":"gcs_path = KaggleDatasets().get_gcs_path()\nprint(gcs_path)\n\ntraining_files = tf.io.gfile.glob(gcs_path + '/train_tfrecords/*.tfrec')\ntest_files = tf.io.gfile.glob(gcs_path + '/test_tfrecords/*.tfrec')\n\nprint('Training tfrecords: '+ str(len(training_files)))\nprint('Test tfrecords: '+ str(len(test_files)))\n\n\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\nNUM_TRAINING_IMAGES = count_data_items(training_files)\nNUM_TEST_IMAGES = count_data_items(test_files)\n\nprint('Dataset: {} training images, {} unlabeled test images'.format(NUM_TRAINING_IMAGES, NUM_TEST_IMAGES))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The train.csv & label_num_to_disease_map.json","metadata":{}},{"cell_type":"code","source":"with open('../input/cassava-leaf-disease-classification/label_num_to_disease_map.json') as js:\n    classes = json.load(js)\nprint(classes)\n\ntrain = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\nprint('Number of entries:', len(train))\nprint('Label Frequencies:')\nprint(train['label'].value_counts().plot.bar())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Functions for reading tfrecord files\n\n\n**decode_image** - For converting bytestring images into arrays.\n\n**read_labeled_tfrecord** - Returns image & label from the tfrecords.\n\n**read_labeled_tfrecord_with_imageid** - Returns image, label & image id from the tfrecords.\n\n**read_unlabeled_tfrecord** - Returns image & image id.\n\nThe **keys of the dictionaries** (*i.e. LABELED_TFREC_FORMAT, UNLABELED_TFREC_FORMAT*) need to match the **keys in the tfrecords**. If the keys dont match then it will throw an **InvalidArgumentError**. \n> InvalidArgumentError: Feature: (data type: string) is required but could not be found\n\n*When training we will use the **read_labeled_tfrecords** because we dont need the image ids during training.*","metadata":{}},{"cell_type":"code","source":"def decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)  # image format uint8 [0,255]\n    image = tf.reshape(image, [*image_size, 3]) # explicit size needed for TPU\n    return image\n\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"target\": tf.io.FixedLenFeature([], tf.int64),  # shape [] means single element\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    \n    image = decode_image(example['image'])\n    label = tf.cast(example['target'], tf.int32)\n\n    return image, label\n\n\ndef read_labeled_tfrecord_with_imageid(example):\n    LABELED_TFREC_FORMAT_WITH_ID = {\n        \"target\": tf.io.FixedLenFeature([], tf.int64),  # shape [] means single element\n        \"image_name\": tf.io.FixedLenFeature([], tf.string),\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT_WITH_ID)\n    \n    image = decode_image(example['image'])\n    label = tf.cast(example['target'], tf.int32)\n    image_name = example['image_name']\n    \n    return image, label, image_name # returns a dataset of (image, label) pairs\\\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        'image_name' : tf.io.FixedLenFeature([], tf.string),\n        'image' : tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    \n    image = decode_image(example['image'])\n    image_name = example['image_name']\n    \n    return image, image_name","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize Some Images\nDisplay 1 image from each tfrecord with corresponding image_id & disease. \n\nDisplay the test image.","metadata":{}},{"cell_type":"code","source":"# Custom function for visualization\ndef show_im(fig, row, col, index, path=None, image=None, title=None, title_color='white'):\n    if image is not None:\n      image = image\n    elif path is not None:\n      image = PIL.Image.open(path)   \n    ax = fig.add_subplot(row, col, index)\n    ax.set_xticks([]), ax.set_yticks([])  # to hide tick values on X and Y axis\n    ax.imshow(image)\n    \n    if title:\n        plt.title(title,\n                  color=title_color)\n        \n    fig.tight_layout(pad=0.02)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display 1 image from each tfrecord with corresponding image_id & disease\nfig1 = plt.figure(figsize=(20,20))\n\nfor i in range(len(training_files)):\n    raw_dataset = tf.data.TFRecordDataset(training_files[i])\n    for raw_record in raw_dataset.take(1):\n        image, label, image_name = read_labeled_tfrecord_with_imageid(raw_record)\n        label = str(int(label))\n        image_name = image_name.numpy().decode('utf-8')\n\n        show_im(fig1,4,4,i+1,image=image, title=f'{image_name}/{classes[label]}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display the test data (only 1 image)\nfig1 = plt.figure(figsize=(8,8))\n\nraw_dataset = tf.data.TFRecordDataset(test_files[0])\nfor raw_record in raw_dataset.take(1):\n\n    image, image_name = read_unlabeled_tfrecord(raw_record)\n    image_name = image_name.numpy().decode('utf-8')\n\n    show_im(fig1,1,1,1,image=image, title=f'{image_name}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Functions for Loading Dataset\n\nRead from TFRecords. For optimal performance, read from multiple files at once.","metadata":{}},{"cell_type":"code","source":"def load_dataset(filenames, labeled=True, ordered=False):\n    # When ordered=False, disregarding data order. Order does not matter since we will be shuffling the data anyway.\n\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False # disable order, increase speed\n\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO) # automatically interleaves reads from multiple files\n    dataset = dataset.with_options(ignore_order) # uses data as soon as it streams in, rather than in its original order\n    dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    # returns a dataset of (image, label) pairs if labeled=True or (image, id) pairs if labeled=False\n    return dataset\n\ndef get_training_dataset():\n    dataset = load_dataset(training_files, labeled=True)\n    dataset = dataset.repeat() # the training dataset must repeat for several epochs\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)\n    return dataset\n\ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(test_files, labeled=False, ordered=ordered)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)\n    return dataset","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Training data shape:\")\nfor image, label in get_training_dataset().take(3):\n    print(image.numpy().shape, label.numpy().shape)\nprint(\"Training data label examples:\", label.numpy())\n\nprint(\"Test data shape:\")\nfor image, image_name in get_test_dataset().take(3):\n    print(image.numpy().shape, image_name.numpy().shape)\nprint(\"Test data IDs:\", image_name.numpy().astype('U')) # U=unicode string","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define & Compile Model within strategy.scope():","metadata":{}},{"cell_type":"code","source":"seed = 1200\n\nwith strategy.scope():\n#     img_adjust = tf.keras.layers.Lambda(lambda data: tf.keras.applications.inception_resnet_v2.preprocess_input(tf.cast(data, tf.float32)), \n#                                               input_shape=[*image_size, 3])\n    \n    pretrained = keras.applications.InceptionResNetV2(include_top=False, weights=\"imagenet\", input_shape=(*image_size, 3))\n    pretrained.trainable = True\n    \n    model = keras.Sequential([\n#         img_adjust,\n        pretrained,\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dense(len(classes), \n            kernel_initializer=keras.initializers.RandomUniform(seed=seed),\n            bias_initializer=keras.initializers.Zeros(), name='dense_top', activation='softmax')\n    ])\n\n    model.compile(loss= keras.losses.SparseCategoricalCrossentropy(), \n                  optimizer= keras.optimizers.Adam(lr=1e-4), \n                  metrics= ['sparse_categorical_accuracy'],\n                  steps_per_execution=16\n                 )\n\nprint(model.summary())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"steps_per_epoch = NUM_TRAINING_IMAGES//batch_size\nprint(steps_per_epoch)\n\nmodel.fit(get_training_dataset(), epochs=epochs, steps_per_epoch=steps_per_epoch)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Let's Validate on Training Data\n\nValidate on 10 batches of 1000 images and the validation accuracy is soemthing like this.","metadata":{}},{"cell_type":"code","source":"val_dataset = get_training_dataset()\nval_dataset = val_dataset.unbatch().batch(1000)\nbatch = iter(val_dataset)\n\nfor i in range(1, 11):\n    val_images, val_labels = next(batch)\n\n    probabilities = model.predict(val_images)\n    predictions = np.argmax(probabilities, axis=-1)\n\n    correct = 0\n    for j in range(len(val_labels.numpy())):\n        if val_labels.numpy()[j]==predictions[j]:\n            correct +=1\n\n    print(f'Validation accuracy of batch {i}: ', correct/1000*100)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# These are great scores! \nHowever there might be some catches ;)  .\n\nI am not going to be exploring that in this notebook. ","metadata":{}},{"cell_type":"markdown","source":"# Visualization\nVisualization of the model's performance on actual image files (not tfrecords). Here visualization on 25 random images is done.","metadata":{}},{"cell_type":"code","source":"random_images = random.sample(range(1, NUM_TRAINING_IMAGES), 25)\n\n\nfig1 = plt.figure(figsize=(35,35))\n\nj=0\nfor i in tqdm(random_images):\n    j+=1\n    \n    img = PIL.Image.open('../input/cassava-leaf-disease-classification/train_images/'+train['image_id'][i])\n    img = img.resize(image_size)  # Resize to (512,512)\n    array = keras.preprocessing.image.img_to_array(img)  # Convert the image to a tensor\n    array = tf.reshape(array, (1, 512, 512, 3))\n\n    \n    output = model.predict(array)\n    index = str(np.argmax(output))\n    \n    prediction = classes[index]\n    truth = classes[str(train['label'][i])]\n    \n    if prediction==truth:\n        title_color='green'\n    else:\n        title_color='red'\n    \n    show_im(fig1, 5, 5, j, image=img, title=f'{prediction}/{truth}', title_color=title_color)\n    ","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}