{"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":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\nimport tensorflow_addons as tfa\nimport numpy as np\nimport pandas as pd\nfrom kaggle_datasets import KaggleDatasets\nimport matplotlib.pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:24:10.645481Z","iopub.execute_input":"2021-05-25T07:24:10.645937Z","iopub.status.idle":"2021-05-25T07:24:16.864497Z","shell.execute_reply.started":"2021-05-25T07:24:10.64584Z","shell.execute_reply":"2021-05-25T07:24:16.863224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Detect hardware, return appropriate distribution strategy\ntry:\n    # TPU detection. No parameters necessary if TPU_NAME environment variable is set. On Kaggle this is always the case.\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(tpu.master())\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    # default distribution strategy in Tensorflow. Works on CPU and single GPU.\n    strategy = tf.distribute.get_strategy()\n    \nprint(\"replocas: \", strategy.num_replicas_in_sync)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:47.298437Z","iopub.execute_input":"2021-05-25T07:28:47.298839Z","iopub.status.idle":"2021-05-25T07:28:52.961439Z","shell.execute_reply.started":"2021-05-25T07:28:47.298804Z","shell.execute_reply":"2021-05-25T07:28:52.960553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Get the GCS path\nGCS_PATH = KaggleDatasets().get_gcs_path('plant-pathology-2021-fgvc8')\nprint(GCS_PATH)\n\ntrain_path = GCS_PATH + '/train_images/'\nprint(train_path)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:54.408911Z","iopub.execute_input":"2021-05-25T07:28:54.409308Z","iopub.status.idle":"2021-05-25T07:28:54.743862Z","shell.execute_reply.started":"2021-05-25T07:28:54.409276Z","shell.execute_reply":"2021-05-25T07:28:54.743012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Labels code as multi-label classification","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:56.460591Z","iopub.execute_input":"2021-05-25T07:28:56.460984Z","iopub.status.idle":"2021-05-25T07:28:56.501626Z","shell.execute_reply.started":"2021-05-25T07:28:56.460952Z","shell.execute_reply":"2021-05-25T07:28:56.50061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:57.303483Z","iopub.execute_input":"2021-05-25T07:28:57.303876Z","iopub.status.idle":"2021-05-25T07:28:57.329746Z","shell.execute_reply.started":"2021-05-25T07:28:57.303844Z","shell.execute_reply":"2021-05-25T07:28:57.32886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df['labels'].value_counts().to_frame()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:57.495297Z","iopub.execute_input":"2021-05-25T07:28:57.495691Z","iopub.status.idle":"2021-05-25T07:28:57.513448Z","shell.execute_reply.started":"2021-05-25T07:28:57.495657Z","shell.execute_reply":"2021-05-25T07:28:57.512444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels2id = {\n    'scab': 0,\n    'healthy': 1,\n    'frog_eye_leaf_spot': 2,\n    'rust': 3,\n    'complex': 4,\n    'powdery_mildew': 5\n}\n\nid2labels = {v:k for k,v in labels2id.items()}\n\nlabel_classes = labels2id.keys()\n\ndef label_encoder(x : str):\n    return [1 if label in x.split(' ') else 0 for label in label_classes]\n\ntrain_df['labels'] = train_df['labels'].map(label_encoder)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:57.691163Z","iopub.execute_input":"2021-05-25T07:28:57.691534Z","iopub.status.idle":"2021-05-25T07:28:57.741457Z","shell.execute_reply.started":"2021-05-25T07:28:57.6915Z","shell.execute_reply":"2021-05-25T07:28:57.740351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:58.561444Z","iopub.execute_input":"2021-05-25T07:28:58.561831Z","iopub.status.idle":"2021-05-25T07:28:58.573807Z","shell.execute_reply.started":"2021-05-25T07:28:58.561796Z","shell.execute_reply":"2021-05-25T07:28:58.572765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_size = [600,600]\nbatch_size = 16 * strategy.num_replicas_in_sync\nchannels = 3\nseed = 2021\nnum_classes = len(label_classes)\nAUTOTUNE = tf.data.experimental.AUTOTUNE","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:58.80509Z","iopub.execute_input":"2021-05-25T07:28:58.807254Z","iopub.status.idle":"2021-05-25T07:28:58.813895Z","shell.execute_reply.started":"2021-05-25T07:28:58.807208Z","shell.execute_reply":"2021-05-25T07:28:58.812833Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* The training dataset, validation dataset and test dataset are divided according to 8:1:1","metadata":{}},{"cell_type":"code","source":"image_file_path = np.array([train_path + i for i in train_df['image'].to_list()])\nlabels = train_df['labels'].to_numpy()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:28:59.928362Z","iopub.execute_input":"2021-05-25T07:28:59.928732Z","iopub.status.idle":"2021-05-25T07:28:59.949445Z","shell.execute_reply.started":"2021-05-25T07:28:59.928687Z","shell.execute_reply":"2021-05-25T07:28:59.948319Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedShuffleSplit\n\ntrain_val_sss = StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=seed)\n\nfor train_index, val_index in train_val_sss.split(image_file_path, labels):\n    train_path, val_path = image_file_path[train_index],image_file_path[val_index]\n    train_labels, val_labels = labels[train_index],labels[val_index]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:14.52545Z","iopub.execute_input":"2021-05-25T07:29:14.525825Z","iopub.status.idle":"2021-05-25T07:29:15.409715Z","shell.execute_reply.started":"2021-05-25T07:29:14.525792Z","shell.execute_reply":"2021-05-25T07:29:15.408636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('train : ',len(train_path),'---',len(train_labels))\nprint('val : ',len(val_path),'---',len(val_labels))","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:18.857718Z","iopub.execute_input":"2021-05-25T07:29:18.858116Z","iopub.status.idle":"2021-05-25T07:29:18.866305Z","shell.execute_reply.started":"2021-05-25T07:29:18.858075Z","shell.execute_reply":"2021-05-25T07:29:18.864924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* convert label's dtype from list to tensor","metadata":{}},{"cell_type":"code","source":"train_labels[:3]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:25.861674Z","iopub.execute_input":"2021-05-25T07:29:25.862294Z","iopub.status.idle":"2021-05-25T07:29:25.867588Z","shell.execute_reply.started":"2021-05-25T07:29:25.862245Z","shell.execute_reply":"2021-05-25T07:29:25.866892Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels = [tf.constant(x) for x in train_labels]\nval_labels = [tf.constant(x) for x in val_labels]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:33.956132Z","iopub.execute_input":"2021-05-25T07:29:33.956498Z","iopub.status.idle":"2021-05-25T07:29:33.998344Z","shell.execute_reply.started":"2021-05-25T07:29:33.956467Z","shell.execute_reply":"2021-05-25T07:29:33.997454Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels[:3]","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:35.374411Z","iopub.execute_input":"2021-05-25T07:29:35.374911Z","iopub.status.idle":"2021-05-25T07:29:35.38456Z","shell.execute_reply.started":"2021-05-25T07:29:35.374878Z","shell.execute_reply":"2021-05-25T07:29:35.383547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Process image","metadata":{}},{"cell_type":"code","source":"def random_erasing(img, sl=0.1, sh=0.2, rl=0.4, p=0.3):\n    h = tf.shape(img)[0]\n    w = tf.shape(img)[1]\n    c = tf.shape(img)[2]\n    origin_area = tf.cast(h*w, tf.float32)\n\n    e_size_l = tf.cast(tf.round(tf.sqrt(origin_area * sl * rl)), tf.int32)\n    e_size_h = tf.cast(tf.round(tf.sqrt(origin_area * sh / rl)), tf.int32)\n\n    e_height_h = tf.minimum(e_size_h, h)\n    e_width_h = tf.minimum(e_size_h, w)\n\n    erase_height = tf.random.uniform(shape=[], minval=e_size_l, maxval=e_height_h, dtype=tf.int32)\n    erase_width = tf.random.uniform(shape=[], minval=e_size_l, maxval=e_width_h, dtype=tf.int32)\n\n    erase_area = tf.zeros(shape=[erase_height, erase_width, c])\n    erase_area = tf.cast(erase_area, tf.uint8)\n\n    pad_h = h - erase_height\n    pad_top = tf.random.uniform(shape=[], minval=0, maxval=pad_h, dtype=tf.int32)\n    pad_bottom = pad_h - pad_top\n\n    pad_w = w - erase_width\n    pad_left = tf.random.uniform(shape=[], minval=0, maxval=pad_w, dtype=tf.int32)\n    pad_right = pad_w - pad_left\n\n    erase_mask = tf.pad([erase_area], [[0,0],[pad_top, pad_bottom], [pad_left, pad_right], [0,0]], constant_values=1)\n    erase_mask = tf.squeeze(erase_mask, axis=0)\n    erased_img = tf.multiply(tf.cast(img,tf.float32), tf.cast(erase_mask, tf.float32))\n\n    return tf.cond(tf.random.uniform([], 0, 1) > p, lambda: tf.cast(img, img.dtype), lambda:  tf.cast(erased_img, img.dtype))","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:29:38.097737Z","iopub.execute_input":"2021-05-25T07:29:38.09813Z","iopub.status.idle":"2021-05-25T07:29:38.113197Z","shell.execute_reply.started":"2021-05-25T07:29:38.098092Z","shell.execute_reply":"2021-05-25T07:29:38.111874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_image(image_path, label):\n    image = tf.io.read_file(image_path)\n    image = tf.io.decode_jpeg(image, channels=channels)\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    image = tf.image.resize(image, image_size)\n    return image, label\n\n\naugmentation = keras.Sequential([\n    layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n    layers.experimental.preprocessing.RandomRotation(factor=0.02),\n    layers.experimental.preprocessing.RandomZoom(height_factor=0.2, width_factor=0.2),\n])\n\ndef augment(image, label):\n    image = tf.expand_dims(image, axis=0)\n    image = augmentation(image)[0]\n    #image = tf.image.random_brightness(image, 0.2)\n    #image = tf.image.random_contrast(image, 0.5, 2.0)\n    #image = tf.image.random_saturation(image, 0.75, 1.25)\n    #image = tf.image.random_hue(image, 0.1)\n    image = random_erasing(image)\n    return image,label","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:30:48.265204Z","iopub.execute_input":"2021-05-25T07:30:48.265591Z","iopub.status.idle":"2021-05-25T07:30:48.301799Z","shell.execute_reply.started":"2021-05-25T07:30:48.265558Z","shell.execute_reply":"2021-05-25T07:30:48.300374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds = tf.data.Dataset.from_tensor_slices((train_path,train_labels))\ntrain_ds = train_ds.map(load_image, num_parallel_calls=AUTOTUNE)\ntrain_ds = train_ds.map(augment, num_parallel_calls=AUTOTUNE)\ntrain_ds = train_ds.cache().shuffle(2048).batch(batch_size).prefetch(AUTOTUNE)\n\n\nval_ds = tf.data.Dataset.from_tensor_slices((val_path,val_labels))\nval_ds = val_ds.map(load_image, num_parallel_calls=AUTOTUNE)\nval_ds = val_ds.cache().batch(batch_size).prefetch(AUTOTUNE)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:32:30.867332Z","iopub.execute_input":"2021-05-25T07:32:30.86786Z","iopub.status.idle":"2021-05-25T07:32:31.739178Z","shell.execute_reply.started":"2021-05-25T07:32:30.867805Z","shell.execute_reply":"2021-05-25T07:32:31.738355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* view some pictures","metadata":{}},{"cell_type":"code","source":"image,_ = next(iter(train_ds))\n\nplt.figure(figsize=(20,20))\n\nfor i in range(16):\n    plt.subplot(4,4,i+1)\n    plt.imshow((image[i].numpy() * 255).astype('uint8'))\n    plt.axis('off')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:30:51.884888Z","iopub.execute_input":"2021-05-25T07:30:51.885271Z","iopub.status.idle":"2021-05-25T07:32:30.864597Z","shell.execute_reply.started":"2021-05-25T07:30:51.885235Z","shell.execute_reply":"2021-05-25T07:32:30.863348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_learning_curve(history):\n    history = history.history\n    metrics_names = ['loss','accuracy','precision','recall','f1_score']\n    plt.figure(figsize=(8, 35))\n    for i,name in enumerate(metrics_names):\n        plt.subplot(len(metrics_names),1,i+1)\n        plt.plot(history[name], label='training '+name)\n        plt.plot(history['val_'+name], label='validation '+name)\n        plt.legend(loc='lower right')\n        plt.ylabel(name)\n        plt.ylim([0,1])\n        plt.title('training and validation '+name)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:36:03.953751Z","iopub.execute_input":"2021-05-25T07:36:03.954137Z","iopub.status.idle":"2021-05-25T07:36:03.961838Z","shell.execute_reply.started":"2021-05-25T07:36:03.954101Z","shell.execute_reply":"2021-05-25T07:36:03.960747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# EfficientNetB4","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    DenseNet = keras.applications.EfficientNetB4(include_top=False)\n    model = keras.Sequential()\n    model.add(layers.Input(shape=[*image_size,channels]))\n    model.add(DenseNet)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dense(num_classes,activation='sigmoid'))\n\n    model.compile(\n        optimizer=keras.optimizers.Adam(lr=0.05),\n        loss=keras.losses.BinaryCrossentropy(),\n        metrics=[\n            'accuracy',\n            tf.keras.metrics.Precision(name='precision'),\n            tf.keras.metrics.Recall(name='recall'),\n            tfa.metrics.F1Score(num_classes=num_classes,average='macro',name='f1_score')\n        ]\n     )\n    \n    callbacks = [\n        # keras.callbacks.EarlyStopping(patience=10,verbose=1,restore_best_weights=True),\n        keras.callbacks.ReduceLROnPlateau(factor=0.5,patience=4,verbose=1,min_delta=0.00001,\n                                         monitor='val_f1_score',mode='max'),\n        keras.callbacks.ModelCheckpoint('EfficientNetB4-600.h5',monitor='val_f1_score',mode='max',\n                                        save_best_only=True,verbose=1)\n    ]\n\n    history = model.fit(train_ds,batch_size=batch_size,epochs=100,\n            validation_data=val_ds,callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T07:37:00.440286Z","iopub.execute_input":"2021-05-25T07:37:00.440684Z","iopub.status.idle":"2021-05-25T08:56:13.624308Z","shell.execute_reply.started":"2021-05-25T07:37:00.440652Z","shell.execute_reply":"2021-05-25T08:56:13.621747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_learning_curve(history)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# ResNet152V2","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    ResNet = keras.applications.ResNet152V2(include_top=False)\n    model = keras.Sequential()\n    model.add(layers.Input(shape=[*image_size,channels]))\n    model.add(ResNet)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Dense(num_classes,activation='sigmoid'))\n\n    model.compile(\n        optimizer=keras.optimizers.Adam(lr=0.05),\n        loss=keras.losses.BinaryCrossentropy(),\n        metrics=[\n            'accuracy',\n            tf.keras.metrics.Precision(name='precision'),\n            tf.keras.metrics.Recall(name='recall'),\n            tfa.metrics.F1Score(num_classes=num_classes,average='macro',name='f1_score')\n        ]\n     )\n    \n    callbacks = [\n        # keras.callbacks.EarlyStopping(patience=10,verbose=1,restore_best_weights=True),\n        keras.callbacks.ReduceLROnPlateau(factor=0.5,patience=4,verbose=1, min_delta=0.00001,\n                                         monitor='val_f1_score',mode='max'),\n        keras.callbacks.ModelCheckpoint('ResNet152V2-600.h5',monitor='val_f1_score',mode='max',\n                                        save_best_only=True,verbose=1)\n    ]\n\n    history = model.fit(train_ds,batch_size=batch_size,epochs=100,\n            validation_data=val_ds,callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-05-24T01:44:02.757997Z","iopub.execute_input":"2021-05-24T01:44:02.758382Z","iopub.status.idle":"2021-05-24T03:29:10.681039Z","shell.execute_reply.started":"2021-05-24T01:44:02.758349Z","shell.execute_reply":"2021-05-24T03:29:10.679748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_learning_curve(history)","metadata":{"execution":{"iopub.status.busy":"2021-05-24T03:29:31.391886Z","iopub.execute_input":"2021-05-24T03:29:31.392399Z","iopub.status.idle":"2021-05-24T03:29:32.106565Z","shell.execute_reply.started":"2021-05-24T03:29:31.392366Z","shell.execute_reply":"2021-05-24T03:29:32.105477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submit","metadata":{}},{"cell_type":"code","source":"def load_predict_image(image_path):\n    image = tf.io.read_file(image_path)\n    image = tf.io.decode_jpeg(image, channels=channels)\n    image = tf.image.convert_image_dtype(image, tf.float32)\n    image = tf.image.resize(image, image_size)\n    image = tf.expand_dims(image,axis=0)\n    return image","metadata":{"execution":{"iopub.status.busy":"2021-05-25T09:13:39.726203Z","iopub.execute_input":"2021-05-25T09:13:39.726571Z","iopub.status.idle":"2021-05-25T09:13:39.732631Z","shell.execute_reply.started":"2021-05-25T09:13:39.726539Z","shell.execute_reply":"2021-05-25T09:13:39.73158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_path = '../input/plant-pathology-2021-fgvc8/test_images/'\n\nsubmission = pd.read_csv('../input/plant-pathology-2021-fgvc8/sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2021-05-25T09:13:49.929947Z","iopub.execute_input":"2021-05-25T09:13:49.930307Z","iopub.status.idle":"2021-05-25T09:13:49.947554Z","shell.execute_reply.started":"2021-05-25T09:13:49.930276Z","shell.execute_reply":"2021-05-25T09:13:49.946807Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for model_name in ['EfficientNetB4-600.h5','ResNet152V2-600.h5']:\n    model = keras.models.load_model(model_name)\n    \n    for row in submission.index:\n\n        image = load_predict_image(test_path+submission.loc[row,'image'])\n        predict = model.predict(image)[0]\n        predict = [1 if i>0.5 else 0 for i in predict]\n        result = []\n        for i,j in enumerate(predict):\n            if j:\n                result.append(id2labels.get(i))\n        result = ' '.join(result)\n        submission.loc[row,'labels'] = result\n    \n    submission.to_csv(model_name[:-6]+'submission.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2021-05-25T09:17:12.320318Z","iopub.execute_input":"2021-05-25T09:17:12.320881Z","iopub.status.idle":"2021-05-25T09:17:13.008092Z","shell.execute_reply.started":"2021-05-25T09:17:12.320829Z","shell.execute_reply":"2021-05-25T09:17:13.005033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}