{"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":"%matplotlib inline\nimport IPython.core.display         \n# setup output image format (Chrome works best)\nIPython.core.display.set_matplotlib_formats(\"svg\")\nimport matplotlib.pyplot as plt\nimport matplotlib\nfrom numpy import *\nfrom sklearn import *\nimport pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport tensorflow.keras as keras\nimport PIL\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport os\nfrom tqdm import tqdm\nimport tensorflow_addons as tfa\nimport random\nfrom sklearn.preprocessing import MultiLabelBinarizer\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.models import Sequential, Model\nfrom tensorflow.keras.layers import Dense, Activation, Conv2D, Flatten, Dropout, Input, BatchNormalization, \\\n                                    GlobalAveragePooling2D, Concatenate\nfrom tensorflow.keras.callbacks import TensorBoard\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.preprocessing import image\nimport logging\nlogging.basicConfig()\nimport struct\nprint(keras.__version__, tf.__version__)\n# use keras backend (K) to force channels-last ordering\nK.set_image_data_format('channels_last')\npd.set_option(\"display.max_columns\", None)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv('../input/plant-pathology-2021-fgvc8/train.csv')\nprint(len(train))\nprint(train.columns)\n# print(train['labels'].value_counts())\nprint(train['labels'].value_counts().plot.bar())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train[:1800]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['labels'] = train['labels'].apply(lambda string: string.split(' '))\ntrain","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"s = list(train['labels'])\nmlb = MultiLabelBinarizer()\ntrainx = pd.DataFrame(mlb.fit_transform(s), columns=mlb.classes_, index=train.index)\nprint(trainx.columns)\nprint(trainx.sum())\n\nlabels = list(trainx.sum().keys())\nprint(labels)\nlabel_counts = trainx.sum().values.tolist()\n\nfig, ax = plt.subplots(1,1, figsize=(10,6))\n\nsns.barplot(x= labels, y= label_counts, ax=ax)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Since the data size is too huge (18632 images wiht about 15GB), I use the resize version by [resized-plant2021](https://www.kaggle.com/ankursingh12/resized-plant2021) by Ankur Singh.And I use flow_from_dataframe mehod from keras to prevent out of memory issues since it can read the image by batch without loading all at once.","metadata":{}},{"cell_type":"code","source":"%%time\ndef add_gauss_noise(X, sigma2=0.1):  #0.05\n    # add Gaussian noise with zero mean, and variance sigma2\n    return X + np.random.normal(0, sigma2, X.shape)\n\n# build the data augmenter\ndatagen = ImageDataGenerator(\n    rescale=1/255.0,\n    rotation_range=10,         # image rotation\n    width_shift_range=0.1,     # image shifting\n    height_shift_range=0.1,    # image shifting\n    shear_range=0.1,           # shear transformation\n    zoom_range=0.1,            # zooming\n    horizontal_flip=True, \n    preprocessing_function=add_gauss_noise, \n    validation_split=0.1\n)\nbsize = 16\n\ntrain_data = datagen.flow_from_dataframe(\n    train,\n    directory='../input/resized-plant2021/img_sz_512',\n    x_col=\"image\",\n    y_col= 'labels',\n    subset=\"training\",\n    color_mode=\"rgb\",\n    target_size = (224,224),\n    class_mode=\"categorical\",\n    batch_size=bsize,\n    shuffle=False,\n    seed=40,\n)\nvalid_data = datagen.flow_from_dataframe(\n    train,\n    directory='../input/resized-plant2021/img_sz_512',\n    x_col=\"image\",\n    y_col= 'labels',\n    subset=\"validation\",\n    color_mode=\"rgb\",\n    target_size = (224,224),\n    class_mode=\"categorical\",\n    batch_size=bsize,\n    shuffle=False,\n    seed=40,\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"accname = 'f1_score'\n\ndef plot_history(history): \n    fig, ax1 = plt.subplots()\n    \n    ax1.plot(history.history['loss'], 'r', label=\"training loss ({:.6f})\".format(history.history['loss'][-1]))\n    ax1.plot(history.history['val_loss'], 'r--', label=\"validation loss ({:.6f})\".format(history.history['val_loss'][-1]))\n    ax1.grid(True)\n    ax1.set_xlabel('iteration')\n    ax1.legend(loc=\"best\", fontsize=9)    \n    ax1.set_ylabel('loss', color='r')\n    ax1.tick_params('y', colors='r')\n\n    if accname in history.history:\n        ax2 = ax1.twinx()\n\n        ax2.plot(history.history[accname], 'b', label=\"training f1_score ({:.4f})\".format(history.history[accname][-1]))\n        ax2.plot(history.history['val_'+accname], 'b--', label=\"validation f1_score ({:.4f})\".format(history.history['val_'+accname][-1]))\n\n        ax2.legend(loc=\"lower right\", fontsize=9)\n        ax2.set_ylabel('acc', color='b')        \n        ax2.tick_params('y', colors='b')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import tensorflow.keras.applications.resnet50 as resnet\nfrom tensorflow.keras.preprocessing import image\nK.clear_session()\nrandom.seed(4487); tf.random.set_seed(4487)\n\n# create the base pre-trained model with-out the classifier\n# using global average pooling\nweight_path = '../input/tf-keras-pretrained-model-weights/No Top/densenet121_weights_tf_dim_ordering_tf_kernels_notop.h5'\nbase_model = tf.keras.applications.DenseNet121(weights=weight_path, include_top=False, pooling='avg')\n\n# start with the output of the ResNet50 (1x1x2048) \nx = base_model.output\n\n# # fully-connected layer \n# x = Dense(128, activation='relu')(x)\n# # fully-connected layer \n# x = Dense(64, activation='relu')(x)\n# # fully-connected layer \nx = Dense(16, activation='relu')(x)\n# finally, the softmax for the classifier \npredictions = Dense(6, activation='sigmoid')(x)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# build the model for training\n# - need to specify the input layer and the output layer\nmodel_ft = Model(inputs=base_model.input, outputs=predictions)\n\n# # fix the layers of the ResNet50.\n# for layer in base_model.layers:\n#     layer.trainable = False\n\nf1 = tfa.metrics.F1Score(num_classes=6, average='macro')\n\n# compile the model - only the layers that we added will be trained\nmodel_ft.compile(optimizer=keras.optimizers.SGD(lr=0.03, \n                                decay=1e-4,  # decay LR each iteration (batch) \n                                momentum=0.8, nesterov=True), \n              loss='binary_crossentropy', metrics=[f1])\n# model_ft.compile(optimizer=keras.optimizers.Adam(lr=0.03), \n#               loss='binary_crossentropy', metrics=[f1])\n\n# setup early stopping callback function\naccearlystop = keras.callbacks.EarlyStopping(\n    monitor=f1,     # look at the validation loss tf2.0 accuracy\n    min_delta=0.02,       # threshold to consider as no change\n    patience=5,             # stop if  epochs with no change\n    verbose=1, mode='max', restore_best_weights= True\n)\nlossearlystop = keras.callbacks.EarlyStopping(\n    monitor='val_loss',     # look at the validation loss tf2.0 accuracy\n    min_delta=0.02,       # threshold to consider as no change\n    patience=5,             # stop if  epochs with no change\n    verbose=1, mode='min', restore_best_weights= True\n)\n# callbacks_list = [earlystop]\nlrschedule = keras.callbacks.ReduceLROnPlateau(monitor='val_loss', \n                                 factor=0.05, patience=5, verbose=1)\n# callbacks_list = [lrschedule,accearlystop,lossearlystop]\n# callbacks_list = [accearlystop,lossearlystop]\ncallbacks_list = []\n\n\n# train the model on the new data for a few epochs\nSTEP_SIZE_TRAIN=train_data.n\nSTEP_SIZE_VALID=valid_data.n\nhistory = model_ft.fit_generator(\n            train_data,  # data from generator\n#             steps_per_epoch=1,    # should be number of batches per epoch\n            epochs=10,\n            callbacks=callbacks_list, \n            validation_data=valid_data, \n#             validation_steps = 1,\n            verbose=True)\n\nplot_history(history)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model_ft.save('fulltrainedResnet.h5')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss, f1score = model_ft.evaluate_generator(valid_data,verbose=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"type(valid_data)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def str2max(list_str,threshold):\n    max_id =[]\n    for i in list_str:\n        if i > threshold:\n            max_id.append(1)\n        else:\n            max_id.append(0)\n    return max_id\n\ndef evaluate(data,threshold):\n    score_dic={}\n    total, right = 0., 0.\n    positive_i=0.0\n    count = 0\n    for x_true, y_true in tqdm(data):\n        count = count +1\n        a = model_ft.predict(x_true)\n        y_pred_list=[]\n        for i in a:\n            label=str2max(i,threshold)\n            label=np.array(label)\n            y_pred_list.append(label)\n        y_pred_list=np.array(y_pred_list)\n        for i,j in zip(y_true.tolist(),y_pred_list.tolist()):\n            total+=1\n            if i==j:\n                right+=1\n        if (count == ceil(180/bsize)):\n            break\n    score_dic['acc']=right/total\n    score_dic['correct']=right\n    score_dic['total']=total\n    return score_dic\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"type(np.linspace(0,1.0,num=20))\nthresholds = np.linspace(0,1.0,num=20)\nthresholds[0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc_list = []\nfor threshold in np.arange(0,1.0,0.05):\n    acc = evaluate(valid_data,threshold)['acc']\n    acc_list.append(acc)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"max_acc = max(acc_list)\nmax_index = acc_list.index(max_acc)\n# thresholds = np.linspace(0,1.0,num=20)\nthresholds = np.arange(0,1.0,0.05)\n\nbest_threshold = thresholds[max_index]\nprint(\"best threshold is {} with the acc {}\".format(best_threshold,max_acc))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_score['acc']","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"test = pd.read_csv('../input/plant-pathology-2021-fgvc8/sample_submission.csv')\n\nfor img_name in tqdm(test['image']):\n    path = '../input/plant-pathology-2021-fgvc8/test_images/'+str(img_name)\n    with PIL.Image.open(path) as img:\n        img = img.resize((256,256))\n        img.save(f'./{img_name}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = datagen.flow_from_dataframe(\n    test,\n    directory = './',\n    x_col=\"image\",\n    y_col= None,\n    color_mode=\"rgb\",\n    target_size = (224,224),\n    classes=None,\n    class_mode=None,\n    batch_size=bsize,\n    shuffle=False,\n    seed=40,\n)\n\npreds = model_ft.predict(test_data)\nprint(preds)\npreds = preds.tolist()\n\nindices = []\nfor pred in preds:\n    temp = []\n    for category in pred:\n        if category>=best_threshold:\n            temp.append(pred.index(category))\n    if temp!=[]:\n        indices.append(temp)\n    else:\n        temp.append(np.argmax(pred))\n        indices.append(temp)\n    \nprint(indices)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = (train_data.class_indices)\nlabels = dict((v,k) for k,v in labels.items())\nprint(labels)\n\ntestlabels = []\n\n\nfor image in indices:\n    temp = []\n    for i in image:\n        temp.append(str(labels[i]))\n    testlabels.append(' '.join(temp))\n\nprint(testlabels)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"delfiles = tf.io.gfile.glob('./*.jpg')\n\nfor file in delfiles:\n    os.remove(file)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv('../input/plant-pathology-2021-fgvc8/sample_submission.csv')\nsub['labels'] = testlabels\nsub.to_csv('submission.csv', index=False)\nsub","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}