{"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":"## Summary\n\n\n* Preprocessing: deleted 77 duplicates detected with `image_hash` library, stratified labels\n* Training strategy: 5 folds CV, unweighted ensemble\n* Backbone: EfficientNetB4, `noisy-student` weights, single dense layer on top\n* Optimizer: Adam, learning rate of 1e-3, ReduceLROnPlateau\n* Image size: 600x600\n* Augmentations: heavy augmentations with `albumentations` library, pre-recorded\n","metadata":{"papermill":{"duration":0.009824,"end_time":"2021-03-21T08:24:58.918229","exception":false,"start_time":"2021-03-21T08:24:58.908405","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import sys\nsys.path.append('/kaggle/input/efficientnet-keras-dataset/efficientnet_kaggle')","metadata":{"papermill":{"duration":0.015936,"end_time":"2021-03-21T08:24:58.959372","exception":false,"start_time":"2021-03-21T08:24:58.943436","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.preprocessing import MultiLabelBinarizer\nfrom tqdm.notebook import tqdm\nimport efficientnet.tfkeras as efn\nimport matplotlib.pyplot as plt\nimport tensorflow_addons as tfa\nimport tensorflow as tf\nimport pandas as pd\nimport numpy as np\nimport random\nimport os","metadata":{"papermill":{"duration":5.910497,"end_time":"2021-03-21T08:25:04.879091","exception":false,"start_time":"2021-03-21T08:24:58.968594","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.keras.mixed_precision.set_global_policy('mixed_float16')\n\n\nclass CFG:\n    \n    '''\n    keep these\n    '''\n    strategy = tf.distribute.get_strategy()\n    batch_size = 16 * strategy.num_replicas_in_sync\n    \n    img_size = 600\n    \n    classes = np.array([\n        'complex', \n        'frog_eye_leaf_spot', \n        'powdery_mildew', \n        'rust', \n        'scab'])\n    root = '../input/plant-pathology-2021-fgvc8/test_images'\n    \n    '''\n    tweak these\n    '''\n    seed = 42 # random seed we use for each operation\n    tta_steps = 0 # number of TTA folds, run without TTA if 0","metadata":{"papermill":{"duration":0.881316,"end_time":"2021-03-21T08:25:05.811987","exception":false,"start_time":"2021-03-21T08:25:04.930671","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Helper functions","metadata":{"papermill":{"duration":0.008651,"end_time":"2021-03-21T08:25:05.831717","exception":false,"start_time":"2021-03-21T08:25:05.823066","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def 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 data_augment(image, label):\n    image = tf.image.random_flip_left_right(image, seed=CFG.seed)\n    image = tf.image.random_flip_up_down(image, seed=CFG.seed)\n    \n    k = tf.tf.random.uniform([], minval=0, maxval=4, dtype=tf.int64, seed=CFG.seed)\n    image = tf.image.rot90(image, k=k)\n    \n    image = tf.image.random_hue(image, .1, seed=CFG.seed)\n    image = tf.image.random_saturation(image, .8, 1.2, seed=CFG.seed)\n    image = tf.image.random_contrast(image, .8, 1.2, seed=CFG.seed)\n    image = tf.image.random_brightness(image, .1, seed=CFG.seed)\n    \n    return image, label\n\n\nfeature_map = {\n    'image': tf.io.FixedLenFeature([], tf.string),\n    'image_name': tf.io.FixedLenFeature([], tf.string)}\n\n\ndef read_tfrecord(example):\n    example = tf.io.parse_single_example(example, feature_map)\n    image = decode_image(example['image'])\n    label = example['image_name']\n    return image, label\n\n\ndef get_dataset(filenames, ordered=True, shuffled=False, repeated=False, \n                augmented=False, cached=False, distributed=False):\n    auto = tf.data.experimental.AUTOTUNE\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=auto)\n    if not ordered:\n        ignore_order = tf.data.Options()\n        ignore_order.experimental_deterministic = False\n        dataset = dataset.with_options(ignore_order)\n    dataset = dataset.map(read_tfrecord, num_parallel_calls=auto)\n    if shuffled:\n        dataset = dataset.shuffle(2048, seed=CFG.seed)\n    if repeated:\n        dataset = dataset.repeat()\n    dataset = dataset.batch(CFG.batch_size)\n    if augmented:\n        dataset = dataset.map(data_augment, num_parallel_calls=auto)\n    if cached:\n        dataset = dataset.cache()\n    dataset = dataset.prefetch(auto)\n    if distributed:\n        dataset = CFG.strategy.experimental_distribute_dataset(dataset)\n    return dataset\n\n\ndef get_model():\n    model = tf.keras.models.Sequential(name='EfficientNetB4')\n    \n    model.add(efn.EfficientNetB4(\n        include_top=False,\n        input_shape=(CFG.img_size, CFG.img_size, 3),\n        weights=None,\n        pooling='avg'))\n    \n    model.add(tf.keras.layers.Dense(len(CFG.classes), \n        kernel_initializer=tf.keras.initializers.RandomUniform(seed=CFG.seed),\n        bias_initializer=tf.keras.initializers.Zeros(), name='dense_top'))\n    model.add(tf.keras.layers.Activation('sigmoid', dtype='float32'))\n    \n    return model","metadata":{"_kg_hide-input":true,"papermill":{"duration":0.029166,"end_time":"2021-03-21T08:25:05.871596","exception":false,"start_time":"2021-03-21T08:25:05.84243","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"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):\n    feature = {\n        'image': tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),\n        'image_name': tf.train.Feature(bytes_list=tf.train.BytesList(value=[name]))}\n    sample = tf.train.Example(features=tf.train.Features(feature=feature))\n    return sample.SerializeToString()\n\n\ndef serialize_test():\n    samples = []\n    \n    for path in os.listdir(CFG.root):\n        image = _serialize_image(os.path.join(CFG.root, path))\n        name = path.encode()\n        samples.append(_serialize_sample(image, name))\n    \n    with tf.io.TFRecordWriter('test.tfrec') as writer:\n        [writer.write(x) for x in tqdm(samples, total=len(samples))]","metadata":{"_kg_hide-input":true,"papermill":{"duration":0.022234,"end_time":"2021-03-21T08:25:05.903831","exception":false,"start_time":"2021-03-21T08:25:05.881597","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Test images serialization\nI assume this would take over 10 minutes during inference, but save much more time when submitting large models.","metadata":{"papermill":{"duration":0.00971,"end_time":"2021-03-21T08:25:05.92245","exception":false,"start_time":"2021-03-21T08:25:05.91274","status":"completed"},"tags":[]}},{"cell_type":"code","source":"serialize_test()","metadata":{"papermill":{"duration":2.408723,"end_time":"2021-03-21T08:25:08.340312","exception":false,"start_time":"2021-03-21T08:25:05.931589","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Inspect test images ","metadata":{"papermill":{"duration":0.009759,"end_time":"2021-03-21T08:25:08.360381","exception":false,"start_time":"2021-03-21T08:25:08.350622","status":"completed"},"tags":[]}},{"cell_type":"code","source":"paths = os.listdir(CFG.root)[:3]\n\nfigure, axes = plt.subplots(1, 3, figsize=[20, 10])\n\nfor i, path in enumerate(paths):\n    image = plt.imread(os.path.join(CFG.root, path))\n\n    axes[i].imshow(image)\n    axes[i].axis('off')\n    \nplt.show()","metadata":{"_kg_hide-input":true,"papermill":{"duration":4.163951,"end_time":"2021-03-21T08:25:12.535026","exception":false,"start_time":"2021-03-21T08:25:08.371075","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Run prediction","metadata":{"papermill":{"duration":0.020657,"end_time":"2021-03-21T08:25:12.576291","exception":false,"start_time":"2021-03-21T08:25:12.555634","status":"completed"},"tags":[]}},{"cell_type":"code","source":"size = len(os.listdir(CFG.root))\nfilenames = tf.io.gfile.glob('*.tfrec')\n\nif CFG.tta_steps > 0:\n    dataset = get_dataset(filenames, repeated=True, augmented=False)\nelse:\n    dataset = get_dataset(filenames)   \n    \npredicts = np.zeros((size, len(CFG.classes)))\npaths = tf.io.gfile.glob('../input/modelweights/*.h5')\nprint(tf.io.gfile.glob('../input/efficientnet-keras-dataset/weights/*.h5'))\nfor path in tqdm(paths, total=len(paths)):\n    print(path)\n    with CFG.strategy.scope():\n        model = get_model()\n        model.load_weights(path)\n    \n    if CFG.tta_steps > 0:\n        steps = CFG.tta_steps * (size / CFG.batch_size + 1)\n\n        predict = model.predict(dataset, steps=steps)[:size * CFG.tta_steps] / len(paths)\n        predicts += np.mean(\n        predict.reshape(size, CFG.tta_steps, len(CFG.classes), order='F'), axis=1)\n        \n\n    else:\n        predicts += model.predict(dataset) / len(paths)\n       \nprint(predicts)","metadata":{"papermill":{"duration":17.207831,"end_time":"2021-03-21T08:25:29.804285","exception":false,"start_time":"2021-03-21T08:25:12.596454","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Threshold configuration\n","metadata":{}},{"cell_type":"code","source":"df_true = pd.read_csv(\n    '../input/pp2021-kfold-tfrecords-0/train.csv', index_col='image').drop('healthy', axis=1)\ndf_pred = pd.read_csv(\n    '../input/predict-weight/oof_predicts.csv', index_col='image')\n\ndf_true = df_true.reindex(df_pred.index)\n\ny_true = df_true.values\ny_pred = df_pred.values\n\n'''\nrun evaluation for each threshold in [0, 1)\n'''\nthresholds = np.arange(.01, 1., .01)\nscores = []\n\nfor threshold in thresholds:\n    metric = tfa.metrics.F1Score(\n        num_classes=len(CFG.classes), \n        average=None, \n        threshold=threshold)\n    metric.update_state(y_true, y_pred)\n    scores.append(metric.result().numpy())\n    \ndf = pd.DataFrame(columns=CFG.classes, data=scores, index=pd.Index(thresholds, name='threshold'))\n\n'''\nfind maximum value for each class and the corresponding threshold\n'''\nthresholds = []\nscores = []\n\nfor x in CFG.classes:\n    thresholds.append(df[x].idxmax())\n    scores.append(df[x].max())\n    print(f'{x}: {df.loc[.5, x]:.4f} >>> {df.loc[thresholds[-1], x]:.4f} ({thresholds[-1]:.2f})')\n\nprint(f'\\nmean score: {df.loc[.5].mean():.4f} >>> {np.mean(scores):.4f}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create submission.csv\n","metadata":{"papermill":{"duration":0.034282,"end_time":"2021-03-21T08:25:29.874649","exception":false,"start_time":"2021-03-21T08:25:29.840367","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# for i in range(len(predicts)):\n#     predicts[i] = predicts[i] > thresholds\nprint(predicts)\npredict = predicts.astype('bool')\n\nlabels = []\n\nfor i in range(len(predict)):\n    labels.append(' '.join(CFG.classes[predict[i]]))\n    \nlabels = ['healthy' if ('healthy' in x or x == '') else x for x in labels]\n    \ndf = pd.DataFrame({\n    'image': os.listdir(CFG.root),\n    'labels': labels})\n\ndf.to_csv('submission.csv', index=False)\ndisplay(df.head())","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}