{"cells":[{"metadata":{},"cell_type":"markdown","source":"Install Keras Applications and EfficientNet pip packages to load EfficientNet models.\nThe Keras Applications is a dependency for EfficientNet"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -q '/kaggle/input/birdcall-identification-submission-custom/Keras_Applications-1.0.8-py3-none-any.whl'\n!pip install -q '/kaggle/input/birdcall-identification-submission-custom/efficientnet-1.1.0-py3-none-any.whl'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport tensorflow as tf\nimport efficientnet.tfkeras as efn\nimport matplotlib.pyplot as plt\n\nfrom tqdm.notebook import tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_HEIGHT = 600\nIMG_WIDTH = 800\n\nIMG_SIZE = 600\nIMG_TARGET_SIZE = 512\nN_CHANNELS = 3\nN_LABELS = 5\nN_FOLDS = 5\n\nBATCH_SIZE = 16\n\nAUTO = tf.data.experimental.AUTOTUNE\n\n# ImageNet mean and standard deviation, used for normalizing images\nIMAGENET_MEAN = tf.constant([0.485, 0.456, 0.406], dtype=tf.float32)\nIMAGENET_STD = tf.constant([0.229, 0.224, 0.225], dtype=tf.float32)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_model(fold):\n    # reset to free memory and training variables\n    tf.keras.backend.clear_session()\n    \n    net = efn.EfficientNetB4(\n        include_top=False,\n        weights=None,\n        input_shape=(IMG_TARGET_SIZE, IMG_TARGET_SIZE, N_CHANNELS),\n    )\n    \n    for layer in reversed(net.layers):\n        if isinstance(layer, tf.keras.layers.BatchNormalization):\n            layer.trainable = False\n        else:\n            layer.trainable = True\n\n    model = tf.keras.Sequential([\n        net,\n        tf.keras.layers.Dropout(0.45),\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dropout(0.45),\n        tf.keras.layers.Dense(N_LABELS, activation='softmax', dtype=tf.float32),\n    ])\n    \n    model.load_weights(f'/kaggle/input/cassava-leaf-disease-prediction/model_fold_{fold}_weights.h5')\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Test Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef decode_tfrecord_test(file_path):        \n    image = tf.io.read_file(file_path)\n    image = tf.io.decode_jpeg(image)\n    \n    image = tf.reshape(image, [IMG_HEIGHT, IMG_WIDTH, N_CHANNELS])\n    image = tf.cast(image, tf.float32)\n    \n    # get image id\n    image_id = tf.strings.split(file_path, '/')[-1]\n    \n    return image, image_id","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_test_dataset():\n    ignore_order = tf.data.Options()\n    ignore_order.experimental_deterministic = False\n    \n    test_dataset = tf.data.Dataset.list_files('/kaggle/input/cassava-leaf-disease-classification/test_images/*.jpg')\n    test_dataset = test_dataset.with_options(ignore_order)\n    \n    test_dataset = test_dataset.map(decode_tfrecord_test, num_parallel_calls=AUTO)\n    test_dataset = test_dataset.batch(BATCH_SIZE)\n    test_dataset = test_dataset.prefetch(AUTO)\n    \n    return test_dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def show_first_test_batch():\n    # log info of batch and first few train images\n    imgs, imgs_ids = next(iter(get_test_dataset()))\n    img = imgs[0].numpy().astype(np.float32)\n    \n    print(f'imgs.shape: {imgs.shape}, imgs.dtype: {imgs.dtype}, imgs_ids.shape: {imgs_ids.shape}, imgs_ids.dtype: {imgs_ids.dtype}')\n    print('img mean: {:.3f}, img std {:.3f}, img min: {:.3f}, img max: {:.3f}'.format(img.mean(), img.std(), img.min(), img.max()))\n    print(f'imgs_id: {imgs_ids[0]}')\n\n    img += abs(img.min())\n    img /= img.max()\n    \n    plt.imshow(img)\n    plt.show()\n            \nshow_first_test_batch()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Predictions"},{"metadata":{},"cell_type":"markdown","source":"8x Test Time Augmentation (TTA) is applied for each fold, resulting in 40 predictions for each image. The augmentations used are horizontal/vertical flip, transpose and random zoom."},{"metadata":{"trusted":true},"cell_type":"code","source":"@tf.function\ndef chance(x, y):\n    return tf.random.uniform(shape=[], minval=0, maxval=y, dtype=tf.int32) < x\n\n@tf.function\ndef tta_images(image):\n    offset = tf.random.uniform(shape=(), minval=0, maxval=IMG_WIDTH-IMG_HEIGHT, dtype=tf.int64)\n    image = tf.slice(image, [0, offset, 0], [IMG_HEIGHT, IMG_HEIGHT, N_CHANNELS])\n    \n    # random flip image horizontally\n    image = tf.image.random_flip_left_right(image)\n    # random flip image vertically\n    image = tf.image.random_flip_up_down(image)\n    \n    # random transpose\n    if chance(1,2):\n        image = tf.image.transpose(image)\n    \n    # random crop between 75%-100%\n    crop_size = tf.random.uniform(shape=(), minval=IMG_SIZE*0.75, maxval=IMG_SIZE)\n    image = tf.image.random_crop(image, [crop_size, crop_size, N_CHANNELS])\n    \n    # cast to target dtype and resize\n    image = tf.image.resize(image, [IMG_TARGET_SIZE, IMG_TARGET_SIZE])\n    \n    image /= 255.0\n    image = (image - IMAGENET_MEAN) / IMAGENET_STD\n    \n    return image\n\n@tf.function\ndef predict_tta(model, image, tta=8):\n    images = tf.expand_dims(image, axis=0)\n    images = tf.repeat(images, tta, axis=0)\n    images = tf.map_fn(tta_images, images)\n    \n    preds = model(images, training=False)\n    preds = tf.math.reduce_sum(preds, axis=0)\n    \n    return preds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Predictions are performed per fold and saved in a dictionary, makking an image_id to class probabilities (softmax output). This approach is chosen as it is incensitive for the order of images. The class probabilities for each fold are summed and the index of the maximum probability is chosen as the predicted class."},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.DataFrame(columns=['image_id', 'label'])\npreds_dict = dict()\n\nfor fold in range(N_FOLDS):\n    model = get_model(fold)\n    for idx, (imgs, image_ids) in tqdm(enumerate(get_test_dataset())):\n        for img, image_id in zip(imgs, image_ids.numpy().astype(str)):\n            pred = predict_tta(model, img)\n            if image_id in preds_dict:\n                preds_dict[image_id] += pred\n            else:\n                preds_dict[image_id] = pred\n\n# Out of fold prediction\nfor idx, (image_id, preds) in enumerate(preds_dict.items()):\n    if idx is 0:\n        print(f'image {image_id} predictions:{preds}')\n        \n    label = np.argmax(preds)\n    submission = submission.append({ 'image_id': image_id, 'label': label }, ignore_index=True)\n        \nsubmission.to_csv('./submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"display(submission)","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}