{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport re\nfrom datetime import datetime\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport tensorflow as tf\n\nfrom kaggle_datasets import KaggleDatasets","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import EfficientNetB0\nfrom tensorflow.keras.applications.efficientnet import preprocess_input as effnet_preprocess_input","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import tensorflow_datasets as tfds\nimport tensorflow_hub as hub","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Tensorflow version \" + tf.__version__)\n\ntry:\n  tpu = tf.distribute.cluster_resolver.TPUClusterResolver()  # TPU detection\n  print('Running on TPU ', tpu.cluster_spec().as_dict()['worker'])\nexcept ValueError:\n  raise BaseException('ERROR: Not connected to a TPU runtime; please see the previous cell in this notebook for instructions!')\n\ntf.config.experimental_connect_to_cluster(tpu)\ntf.tpu.experimental.initialize_tpu_system(tpu)\ntpu_strategy = tf.distribute.TPUStrategy(tpu)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path('cassava-leaf-disease-classification')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tfrec_fnames = tf.io.gfile.glob(GCS_PATH + '/train_tfrecords/ld_train*.tfrec')\nlen(tfrec_fnames)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Data Prep"},{"metadata":{"trusted":true},"cell_type":"code","source":"label_to_disease = pd.read_json(os.path.join(GCS_PATH, 'label_num_to_disease_map.json'), typ='series')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_csv = pd.read_csv(os.path.join(GCS_PATH, 'train.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_csv['disease'] = train_csv['label'].map(label_to_disease)\ntrain_csv.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# 75:25 train:valid\ntrain_fnames = tfrec_fnames[:12]\nvalid_fnames = tfrec_fnames[12:]\nprint(len(train_fnames), len(valid_fnames))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Callbacks"},{"metadata":{"trusted":true},"cell_type":"code","source":"early_stop = tf.keras.callbacks.EarlyStopping(monitor = 'val_loss', min_delta = 0.001, \n                                              patience = 5, mode = 'min', verbose = 1,\n                                              restore_best_weights = True)\n\nreduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor = 'val_loss', factor = 0.3, \n                                                 patience = 2, min_delta = 0.001, \n                                                 mode = 'min', verbose = 1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Datasets"},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTO = tf.data.experimental.AUTOTUNE\nBATCH_SIZE = 16 * tpu_strategy.num_replicas_in_sync\nIMAGE_SIZE = [512, 512]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def _parse_function(proto):\n    # feature_description needs to be defined since datasets use graph-execution\n    # - its used to build their shape and type signature\n    feature_description = {\n        'image': tf.io.FixedLenFeature([], tf.string, default_value=''),\n        'image_name': tf.io.FixedLenFeature([], tf.string, default_value=''),\n        'target': tf.io.FixedLenFeature([], tf.int64, default_value=-1)\n    }\n\n    parsed_features = tf.io.parse_single_example(proto, feature_description)\n    image = tf.image.decode_jpeg(parsed_features['image'], channels=3)\n    image = tf.cast(image, tf.float32) # :: [0.0, 255.0]\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    target = tf.one_hot(parsed_features['target'], depth=5)\n    return image, target","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_dataset(tfrecords_fnames):\n    raw_ds = tf.data.TFRecordDataset(tfrecords_fnames, num_parallel_reads=AUTO)\n    parsed_ds = raw_ds.map(_parse_function, num_parallel_calls=AUTO)\n    return parsed_ds","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_train_ds(train_fnames, with_aug=False):\n    ds = load_dataset(train_fnames)\n\n    def data_augment(image, target):\n        modified = tf.image.random_flip_left_right(image)\n        modified = tf.image.random_flip_up_down(image)\n        modified = tf.image.random_brightness(modified, 0.2)\n        #modified = tf.image.random_contrast(modified, 0.2, 0.5)\n        #modified = tf.image.random_hue(modified, 0.2)\n        modified = tf.image.random_saturation(modified, 5, 10)\n        modified = tf.clip_by_value(modified, 0.0, 255.0)\n        return modified, target\n\n    if with_aug:\n        ds = ds.map(data_augment, num_parallel_calls=AUTO)\n\n    return ds.repeat().shuffle(2048).batch(BATCH_SIZE, drop_remainder=True).prefetch(AUTO)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_valid_ds(valid_fnames):\n    ds = load_dataset(valid_fnames)\n    ds = ds.batch(BATCH_SIZE, drop_remainder=True).prefetch(AUTO)\n    return ds","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(fname).group(1)) for fname in filenames]\n    return np.sum(n)\n\nn_train = count_data_items(train_fnames)\nn_valid = count_data_items(valid_fnames)\ntrain_steps = count_data_items(train_fnames) // BATCH_SIZE\nprint(\"TRAINING IMAGES:\", n_train, \", STEPS PER EPOCH:\", train_steps)\nprint(\"VALIDATION IMAGES:\", n_valid)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Plain Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_fn(image, label):\n    image = image / 255.0\n    image = tf.image.resize(image, (224, 224))\n    label = tf.concat([label, [0]], axis=0)\n    return image, label","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_fnames = tfrec_fnames[:12]\nvalid_fnames = tfrec_fnames[12:]\n\ntrain_ds = load_dataset(train_fnames)\ntrain_ds = train_ds.map(preprocess_fn, num_parallel_calls=AUTO)\ntrain_ds = train_ds.repeat().shuffle(2048).batch(BATCH_SIZE, drop_remainder=True).prefetch(AUTO)\n\nvalid_ds = load_dataset(valid_fnames)\nvalid_ds = valid_ds.map(preprocess_fn, num_parallel_calls=AUTO)\nvalid_ds = valid_ds.batch(BATCH_SIZE, drop_remainder=True).prefetch(AUTO)\n\ntrain_steps = count_data_items(train_fnames) // BATCH_SIZE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img, label = next(iter(train_ds))\nprint(img.numpy().max(), img.shape, img.dtype)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"os.environ[\"TFHUB_CACHE_DIR\"] = \"/kaggle/working\"\nwith tpu_strategy.scope():\n    load_locally = tf.saved_model.LoadOptions(experimental_io_device='/job:localhost')\n    cassava = hub.KerasLayer('https://tfhub.dev/google/cropnet/classifier/cassava_disease_V1/2', trainable=True, load_options=load_locally)\n    model = tf.keras.Sequential([tf.keras.Input(shape=(224,224,3)),\n                                 cassava])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),\n              loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False),\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit(train_ds, validation_data=valid_ds,\n          epochs=500, steps_per_epoch=train_steps,\n          callbacks=[reduce_lr, early_stop])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.save('cassava_model.h5')","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}