{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Hello!\n\nThis is a pretty basic implementation of a **TensorFlow** and **Keras** pipeline for this competition yet without any fancy parts. \nThe main aim here is achieving training and inference speed as high as possible. \n\nThus, we divide the pipeline into 3 steps:\n1. Making own `.tfrec` files as they are not available in the competition data (see the corresponding **[dataset](https://www.kaggle.com/nickuzmenkov/plant-pathology-2021-train-tfrecords)**) and testing whether everything works just fine. Here we use 512x512 image size, but feel free tweak parameters to set your desirable size and number of files\n2. Training 5 folds EfficientNetB4 on TPU in **[this notebook](https://www.kaggle.com/nickuzmenkov/pp2021-tpu-tf-training)**\n3. Separate **[inference notebook](https://www.kaggle.com/nickuzmenkov/pp2021-tpu-tf-inference)** where we also do image pre-serialization to a single `.tfrec` file for maximum speed\n\nAverage time consuming per steps:\n\n| Step # | Accelerator | Approximate time | Comment |\n| --- | --- | --- | --- |\n| 1 | CPU | 36 min | for 512x512 |\n| 2 | TPU | 17 min | per fold |\n| 3 | GPU | 20 min | for 5 models |\n\n### So, let's go ahead"},{"metadata":{},"cell_type":"markdown","source":"### Imports"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.preprocessing import MultiLabelBinarizer\nfrom sklearn.utils import shuffle\nfrom tqdm.notebook import tqdm\nimport tensorflow as tf\nimport pandas as pd\nimport numpy as np\nimport os","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Configuration\nDefine here the desirable number of output files (`subfolds`) and image size (`img_size`)"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CFG:\n    \n    '''\n    keep these\n    '''\n    strategy = tf.distribute.get_strategy()\n    batch_size = 16 * strategy.num_replicas_in_sync\n    \n    root = '../input/plant-pathology-2021-fgvc8/train_images'\n    \n    '''\n    tweak these\n    '''\n    seed = 42 # random seed for shuffling the initial dataframe\n    img_size = 512 # desirable image size\n    subfolds = 64 # number of output files","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Helper functions (serialization)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def _serialize_image(path):\n    image = tf.io.read_file(path)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [CFG.img_size, CFG.img_size])\n    image = tf.cast(image, tf.uint8)\n    return tf.image.encode_jpeg(image).numpy()\n\n\ndef _serialize_sample(image, name, labels):\n    feature = {\n        'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),\n        'name': tf.train.Feature(bytes_list=tf.train.BytesList(value=[name])),\n        'cider_apple_rust': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[0]])),\n        'complex': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[1]])),\n        'frog_eye_leaf_spot': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[2]])),\n        'healthy': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[3]])),\n        'powdery_mildew': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[4]])),\n        'rust': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[5]])),\n        'scab': tf.train.Feature(int64_list=tf.train.Int64List(value=[labels[6]]))}\n    sample = tf.train.Example(features=tf.train.Features(feature=feature))\n    return sample.SerializeToString()\n\n\ndef serialize_fold(fold, filename, transform=None):\n    samples = []\n    \n    for path, labels in fold.iterrows():\n        image = _serialize_image(os.path.join(CFG.root, path))\n        name = path.encode()\n        samples.append(_serialize_sample(image, name, labels))\n    \n    with tf.io.TFRecordWriter(filename + '.tfrec') as writer:\n        [writer.write(x) for x in samples]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Initial DataFrame preprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv', index_col='image')\n\ndf['labels'] = [x.split(' ') for x in df['labels']]\n\nbinarizer = MultiLabelBinarizer()\nlabels = binarizer.fit_transform(df['labels'].values)\n\ndf = pd.DataFrame(\n    index=df.index,\n    columns=binarizer.classes_,\n    data=labels)\n\ndf.to_csv('train.csv')\ndisplay(df.head())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1. Serialization"},{"metadata":{"trusted":true},"cell_type":"code","source":"df = shuffle(df, random_state=CFG.seed)\n\nwith tqdm(total=CFG.subfolds) as bar:\n\n    folder = f'train_tfrecords'\n    os.mkdir(folder)\n        \n    for i, subfold in enumerate(np.array_split(df, CFG.subfolds)):\n        filename=os.path.join(folder, '%.2i-%.3i' % (i, len(subfold)))\n        serialize_fold(subfold, filename=filename)\n                \n        bar.update(1)\n                \nbar.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Helper functions (parsing & training)"},{"metadata":{"trusted":true},"cell_type":"code","source":"feature_map = {\n    'image': tf.io.FixedLenFeature([], tf.string),\n    'name': tf.io.FixedLenFeature([], tf.string),\n    'cider_apple_rust': tf.io.FixedLenFeature([], tf.int64),\n    'complex': tf.io.FixedLenFeature([], tf.int64),\n    'frog_eye_leaf_spot': tf.io.FixedLenFeature([], tf.int64),\n    'healthy': tf.io.FixedLenFeature([], tf.int64),\n    'powdery_mildew': tf.io.FixedLenFeature([], tf.int64),\n    'rust': tf.io.FixedLenFeature([], tf.int64),\n    'scab': tf.io.FixedLenFeature([], tf.int64)}\n\n\ndef count_data_items(filenames):\n    return np.sum([int(x[:-6].split('-')[-1]) for x in filenames])\n\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.reshape(image, [CFG.img_size, CFG.img_size, 3])\n    image = tf.cast(image, tf.float32) / 255.\n    return image\n\n\ndef read_tfrecord(example):\n    example = tf.io.parse_single_example(example, feature_map)\n    image = decode_image(example['image'])\n    target = [\n        tf.cast(example['cider_apple_rust'], tf.float32),\n        tf.cast(example['complex'], tf.float32),\n        tf.cast(example['frog_eye_leaf_spot'], tf.float32),\n        tf.cast(example['healthy'], tf.float32),\n        tf.cast(example['powdery_mildew'], tf.float32),\n        tf.cast(example['rust'], tf.float32),\n        tf.cast(example['scab'], tf.float32)]\n    return image, target\n\n\ndef get_dataset(filenames):\n    auto = tf.data.experimental.AUTOTUNE\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=auto)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=auto)\n    dataset = dataset.batch(CFG.batch_size)\n    dataset = dataset.prefetch(auto)\n    return CFG.strategy.experimental_distribute_dataset(dataset)\n\n\ndef get_model():\n    model = tf.keras.models.Sequential([\n        tf.keras.applications.EfficientNetB0(\n            include_top=False,\n            input_shape=(CFG.img_size, CFG.img_size, 3),\n            weights=None,\n            pooling='avg'),\n        tf.keras.layers.Dense(len(feature_map) - 2),\n        tf.keras.layers.Activation('sigmoid', dtype='float32')\n    ])\n    \n    model.compile(\n        optimizer='adam',\n        loss='categorical_crossentropy',\n        metrics=['accuracy'])\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Testing\nReplace this part with your training pipeline if you want to train in this notebook"},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames = tf.io.gfile.glob('./train_tfrecords/*.tfrec')[:1]\ndataset = get_dataset(filenames)\n\nsteps_per_epoch = count_data_items(filenames) // CFG.batch_size\n\nwith CFG.strategy.scope():\n    model = get_model()\n\nmodel.summary()\n\nhistory = model.fit(\n    dataset, \n    steps_per_epoch=steps_per_epoch,\n    epochs=1,\n    verbose=2)","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}