{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport re\nimport math\nimport random\nimport numpy as np\nimport tensorflow as tf\nfrom matplotlib import pyplot as plt\nfrom kaggle_datasets import KaggleDatasets","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# use 224x224 image data, since input for EfficientNet B0 is 224x224x3\n\nIMAGE_SIZE = [224, 224]\nBATCH_SIZE = 64\nAUTO = tf.data.experimental.AUTOTUNE\n\nGCS_DS_PATH = KaggleDatasets().get_gcs_path('tpu-getting-started')\nGCS_PATH = GCS_DS_PATH + '/tfrecords-jpeg-224x224'\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')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# code to load data, note that EfficientNet uses 0-255 input range, while other model might use 0-1\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"class\": tf.io.FixedLenFeature([], tf.int64),  # shape [] means single element\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    label = tf.cast(example['class'], tf.int32)\n    return image, label # returns a dataset of (image, label) pairs\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"id\": tf.io.FixedLenFeature([], tf.string),  # shape [] means single element\n        # class is missing, this competitions's challenge is to predict flower classes for the test dataset\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image, idnum # returns a dataset of image(s)\n\ndef load_dataset(filenames, labeled=True):\n    dataset = tf.data.TFRecordDataset(filenames)\n    dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord)\n    # returns a dataset of (image, label) pairs if labeled=True or (image, id) pairs if labeled=False\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# one-hot encoding for label\n# image augmentation (reduces overfitting)\n# shuffle\n# batch\n# prefetch or cache\n\nNUM_CLASSES = 104\nCROP_SIZE = [200, 200]\n\ndef data_augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    if bool(random.getrandbits(1)):\n        image = tf.image.random_crop(image, [*CROP_SIZE, 3])\n        image = tf.image.resize(image, IMAGE_SIZE)\n    return image, label\n\ndef input_preprocess(image, label):\n    label = tf.one_hot(label, NUM_CLASSES)\n    return image, label\n\ndef get_training_dataset():\n    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n    dataset = dataset.map(input_preprocess, num_parallel_calls=AUTO)\n    dataset = dataset.map(data_augment, num_parallel_calls=AUTO)\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO)\n    return dataset\n\ndef get_validation_dataset():\n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True)\n    dataset = dataset.map(input_preprocess)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    return dataset\n\ndef get_test_dataset():\n    dataset = load_dataset(TEST_FILENAMES, labeled=False)\n    dataset = dataset.batch(BATCH_SIZE)\n    return dataset\n\ndef count_data_items(filenames):\n    # the number of data items is written in the name of the .tfrec\n    # 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_FILENAMES)\nNUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)\nNUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\nprint('Dataset: {} training images, {} validation images, {} unlabeled test images'.format(NUM_TRAINING_IMAGES, NUM_VALIDATION_IMAGES, NUM_TEST_IMAGES))\n\nds_train = get_training_dataset()\nds_valid = get_validation_dataset()\nds_test = get_test_dataset()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"# use noisy-student version of EfficientNet B0 weights (better performance than standard \"imagenet\" weights)\n\n!wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet/noisystudent/noisy_student_efficientnet-b0.tar.gz\n!tar -xf noisy_student_efficientnet-b0.tar.gz\n\n!wget https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/python/keras/applications/efficientnet_weight_update_util.py\n!python efficientnet_weight_update_util.py --model b0 --notop --ckpt noisy_student_efficientnet-b0/model.ckpt --o efficientnetb0_notop.h5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# load EfficientNet B0 without top\n# freeze weights on EfficientNet B0\n# add a softmax top (dropout reduces overfitting)\n\npretrained_model = tf.keras.applications.efficientnet.EfficientNetB0(\n    weights='efficientnetb0_notop.h5',\n    include_top=False,\n    input_shape=[*IMAGE_SIZE, 3]\n)\npretrained_model.trainable = False\n\nmodel = tf.keras.Sequential([\n    # To a base pretrained on ImageNet to extract features from images...\n    pretrained_model,\n    # ... attach a new head to act as a classifier.\n    # tf.keras.layers.BatchNormalization(name='batch_norm1'),\n    # tf.keras.layers.Conv2D(20, 1, activation='relu', name='conv1'),\n    # tf.keras.layers.Flatten(name='flatten'),\n    tf.keras.layers.GlobalAveragePooling2D(name='avg_pool'),\n    tf.keras.layers.BatchNormalization(name='batch_norm2'),\n    tf.keras.layers.Dropout(0.2, name=\"dropout2\"),\n    tf.keras.layers.Dense(NUM_CLASSES, activation='softmax', name='output')\n], name=\"EfficientNetB0\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ideally this should be ADAM with learning rate schedules\n\nLEARNING_RATE = 0.01\n\nmodel.compile(\n    optimizer=tf.optimizers.Adam(learning_rate=LEARNING_RATE),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 20\n\nhistory = model.fit(\n    ds_train,\n    validation_data=ds_valid,\n    epochs=EPOCHS\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_history(hist):\n    plt.plot(hist.history[\"accuracy\"])\n    plt.plot(hist.history[\"val_accuracy\"])\n    plt.title(\"model accuracy\")\n    plt.ylabel(\"accuracy\")\n    plt.xlabel(\"epoch\")\n    plt.legend([\"train\", \"validation\"], loc=\"upper left\")\n    plt.show()\n\nplot_history(history)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NEXT_EPOCHES = 40\n\n# reduce learning rate\nmodel.optimizer.learning_rate = 0.0001\n\nhistory_continued = model.fit(\n    ds_train,\n    validation_data=ds_valid,\n    initial_epoch=EPOCHS,\n    epochs=NEXT_EPOCHES\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_history(history_continued)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# code snippet to load a saved model, to continue from last version\n\n# !wget https://kaggledatastore.blob.core.windows.net/data/flowers/enet0_epoch30.h5\n\n# model = tf.keras.models.load_model('enet0_epoch30.h5')\n\n# !rm enet0_epoch30.h5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# save model\n\nmodel.save('enet0_epoch40.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# predict on test data\n\ntest_ds = get_test_dataset()\n\ntest_images_ds = test_ds.map(lambda image, idnum: image)\nprobabilities = model.predict(test_images_ds)\npredictions = np.argmax(probabilities, axis=-1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# make submission.csv\n\ntest_ids_ds = test_ds.map(lambda image, idnum: idnum).unbatch()\ntest_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES))).numpy().astype('U')\n\nnp.savetxt(\n    'submission.csv',\n    np.rec.fromarrays([test_ids, predictions]),\n    fmt=['%s', '%d'],\n    delimiter=',',\n    header='id,label',\n    comments=''\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}