{"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":"## Imports","metadata":{}},{"cell_type":"code","source":"#Importing necessary modules\nimport pandas as pd\nfrom sklearn.preprocessing import MultiLabelBinarizer\nimport numpy as np\nimport os\nimport cv2\nimport matplotlib.pyplot as plt\nimport shutil\nimport math\nimport pickle","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Importing modules for model implementation and trainning\nimport tensorflow_addons as tfa\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Sequential, load_model, Model\nfrom tensorflow.keras.layers import Dense, Flatten, Dropout, GlobalAveragePooling2D\nfrom tensorflow.keras.optimizers import SGD, Adam\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications.densenet import DenseNet169, preprocess_input\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## The trainning images were resized to 224x224 before trainning\n","metadata":{}},{"cell_type":"code","source":"\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Paths","metadata":{}},{"cell_type":"code","source":"train_csv_path = './train.csv'\nresized_imgs_path = './resized_images'","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Reading 'train.csv'","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(train_csv_path)\ndf[\"labels\"] = df[\"labels\"].apply(lambda x:x.split(\" \"))\ndf.head()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Encoding labels","metadata":{}},{"cell_type":"code","source":"# One hot enconding\nmlb = MultiLabelBinarizer()\nonehot_labels = mlb.fit_transform(df[\"labels\"])\nprint(mlb.classes_)\n\n# Dataframe with one hot encoding labels\ndf_labels = pd.DataFrame(onehot_labels, columns=mlb.classes_, index=df.index)\ndf_labels","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data augmentation and training batches","metadata":{}},{"cell_type":"code","source":"# Data augmentation\nIMG_SIZE = [224, 224]\ngenerator = ImageDataGenerator(\n                            rotation_range=5,\n                            zoom_range=0.1,\n                            shear_range=0.05,\n                            horizontal_flip=True,\n                            validation_split=0.2,\n                            preprocessing_function= preprocess_input)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Generating training batches\n\ntrain_generator = generator.flow_from_dataframe(\n        dataframe= df,\n        subset= 'training',\n        directory= resized_imgs_path,\n        x_col= 'image',\n        y_col= 'labels',\n        target_size= IMG_SIZE,\n        shuffle= True,\n        seed = 40,\n        batch_size= 8,\n        color_mode = 'rgb',\n        class_mode= 'categorical')\n\ntest_generator = generator.flow_from_dataframe(\n        dataframe= df,\n        subset= 'validation',\n        directory= resized_imgs_path,\n        x_col= 'image',\n        y_col= 'labels',\n        target_size= IMG_SIZE,\n        shuffle= True,\n        seed = 40,\n        batch_size= 8,\n        color_mode = 'rgb',\n        class_mode= 'categorical')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training\n- Densenet169 with fine tunning\n- ReduceLRonPlateau\n- EarlyStopping\n- Optimizer: SGD with initial learning rate 0.0001 and momentum 0.9\n- Metrics: Accuracy and F1 score","metadata":{}},{"cell_type":"code","source":"# Download pre-treined weights\nbase_model = DenseNet169(include_top=False, weights='imagenet', input_shape= IMG_SIZE + [3])\n\n# Adding top layers\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dense(128, activation='relu')(x)\nx = Dropout(0.2)(x)\nx = Dense(64, activation='relu')(x)\nprediction = Dense(6, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=prediction)\n\n# Metric used to evaluate model\nf1 = tfa.metrics.F1Score(num_classes=6, average='macro')\n\n# Early stopping - Stop trainning if the metric 'val_f1_score' does not improve\nes= EarlyStopping(\n    patience=5, \n    monitor='val_f1_score', \n    mode='max', \n    restore_best_weights=True)\n\n# ReduceLRonPlateau - Reduce learning rate if the metric 'val_loss' does not improve\nlr= tf.keras.callbacks.ReduceLROnPlateau(\n    monitor=\"val_loss\",\n    factor=0.1,\n    patience=3,\n    verbose=1,\n    mode=\"min\",\n    min_delta=0.01\n)\n\n\n# Compiling model\nmodel.compile(loss='binary_crossentropy',\n                    optimizer=SGD(learning_rate= 0.0001, momentum=0.9),\n                    metrics=['accuracy', f1])\n# Train\nhistory = model.fit(x=train_generator, validation_data=test_generator, epochs=50, verbose=1, callbacks=[lr, es])","metadata":{"_kg_hide-output":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### My output","metadata":{}},{"cell_type":"code","source":"\"\"\"\nEpoch 1/50\n1864/1864 [==============================] - 364s 188ms/step - loss: 0.5194 - accuracy: 0.2956 - f1_score: 0.1968 - val_loss: 0.3470 - val_accuracy: 0.5762 - val_f1_score: 0.4155\nEpoch 2/50\n1864/1864 [==============================] - 352s 189ms/step - loss: 0.3407 - accuracy: 0.5807 - f1_score: 0.4407 - val_loss: 0.2411 - val_accuracy: 0.7335 - val_f1_score: 0.6551\nEpoch 3/50\n1864/1864 [==============================] - 343s 184ms/step - loss: 0.2664 - accuracy: 0.6954 - f1_score: 0.6180 - val_loss: 0.1828 - val_accuracy: 0.8089 - val_f1_score: 0.7650\nEpoch 4/50\n1864/1864 [==============================] - 349s 187ms/step - loss: 0.2252 - accuracy: 0.7462 - f1_score: 0.6959 - val_loss: 0.1561 - val_accuracy: 0.8336 - val_f1_score: 0.8020\nEpoch 5/50\n1864/1864 [==============================] - 355s 190ms/step - loss: 0.2018 - accuracy: 0.7816 - f1_score: 0.7349 - val_loss: 0.1379 - val_accuracy: 0.8588 - val_f1_score: 0.8308\nEpoch 6/50\n1864/1864 [==============================] - 357s 191ms/step - loss: 0.1863 - accuracy: 0.8009 - f1_score: 0.7606 - val_loss: 0.1305 - val_accuracy: 0.8634 - val_f1_score: 0.8364\nEpoch 7/50\n1864/1864 [==============================] - 356s 191ms/step - loss: 0.1697 - accuracy: 0.8209 - f1_score: 0.7800 - val_loss: 0.1208 - val_accuracy: 0.8763 - val_f1_score: 0.8545\nEpoch 8/50\n1864/1864 [==============================] - 356s 191ms/step - loss: 0.1673 - accuracy: 0.8214 - f1_score: 0.7886 - val_loss: 0.1162 - val_accuracy: 0.8792 - val_f1_score: 0.8567\nEpoch 9/50\n1864/1864 [==============================] - 363s 195ms/step - loss: 0.1611 - accuracy: 0.8293 - f1_score: 0.7971 - val_loss: 0.1119 - val_accuracy: 0.8790 - val_f1_score: 0.8541\nEpoch 10/50\n1864/1864 [==============================] - 368s 198ms/step - loss: 0.1533 - accuracy: 0.8427 - f1_score: 0.8093 - val_loss: 0.1102 - val_accuracy: 0.8870 - val_f1_score: 0.8613\nEpoch 11/50\n1864/1864 [==============================] - 370s 199ms/step - loss: 0.1505 - accuracy: 0.8441 - f1_score: 0.8104 - val_loss: 0.1081 - val_accuracy: 0.8929 - val_f1_score: 0.8718\nEpoch 12/50\n1864/1864 [==============================] - 361s 194ms/step - loss: 0.1440 - accuracy: 0.8479 - f1_score: 0.8106 - val_loss: 0.1040 - val_accuracy: 0.8937 - val_f1_score: 0.8708\nEpoch 13/50\n1864/1864 [==============================] - 367s 197ms/step - loss: 0.1355 - accuracy: 0.8605 - f1_score: 0.8282 - val_loss: 0.1032 - val_accuracy: 0.8956 - val_f1_score: 0.8730\n\nEpoch 00013: ReduceLROnPlateau reducing learning rate to 9.999999747378752e-06.\nEpoch 14/50\n1864/1864 [==============================] - 368s 198ms/step - loss: 0.1319 - accuracy: 0.8677 - f1_score: 0.8375 - val_loss: 0.1011 - val_accuracy: 0.8935 - val_f1_score: 0.8706\nEpoch 15/50\n1864/1864 [==============================] - 363s 195ms/step - loss: 0.1345 - accuracy: 0.8655 - f1_score: 0.8337 - val_loss: 0.1016 - val_accuracy: 0.8943 - val_f1_score: 0.8746\nEpoch 16/50\n1864/1864 [==============================] - 360s 193ms/step - loss: 0.1320 - accuracy: 0.8634 - f1_score: 0.8333 - val_loss: 0.1020 - val_accuracy: 0.8929 - val_f1_score: 0.8695\n\nEpoch 00016: ReduceLROnPlateau reducing learning rate to 9.999999747378752e-07.\nEpoch 17/50\n1864/1864 [==============================] - 356s 191ms/step - loss: 0.1308 - accuracy: 0.8698 - f1_score: 0.8399 - val_loss: 0.1025 - val_accuracy: 0.8921 - val_f1_score: 0.8706\nEpoch 18/50\n1864/1864 [==============================] - 367s 197ms/step - loss: 0.1357 - accuracy: 0.8587 - f1_score: 0.8277 - val_loss: 0.1011 - val_accuracy: 0.8972 - val_f1_score: 0.8747\nEpoch 19/50\n1864/1864 [==============================] - 366s 196ms/step - loss: 0.1363 - accuracy: 0.8606 - f1_score: 0.8314 - val_loss: 0.1002 - val_accuracy: 0.8951 - val_f1_score: 0.8750\nEpoch 20/50\n1864/1864 [==============================] - 370s 198ms/step - loss: 0.1309 - accuracy: 0.8667 - f1_score: 0.8349 - val_loss: 0.0991 - val_accuracy: 0.8980 - val_f1_score: 0.8785\nEpoch 21/50\n1864/1864 [==============================] - 359s 192ms/step - loss: 0.1355 - accuracy: 0.8602 - f1_score: 0.8337 - val_loss: 0.1024 - val_accuracy: 0.8951 - val_f1_score: 0.8729\nEpoch 22/50\n1864/1864 [==============================] - 367s 197ms/step - loss: 0.1285 - accuracy: 0.8716 - f1_score: 0.8398 - val_loss: 0.1006 - val_accuracy: 0.8951 - val_f1_score: 0.8715\n\nEpoch 00022: ReduceLROnPlateau reducing learning rate to 9.999999974752428e-08.\nEpoch 23/50\n1864/1864 [==============================] - 365s 196ms/step - loss: 0.1320 - accuracy: 0.8618 - f1_score: 0.8287 - val_loss: 0.1013 - val_accuracy: 0.8945 - val_f1_score: 0.8731\nEpoch 24/50\n1864/1864 [==============================] - 368s 197ms/step - loss: 0.1317 - accuracy: 0.8650 - f1_score: 0.8345 - val_loss: 0.1005 - val_accuracy: 0.8964 - val_f1_score: 0.8769\nEpoch 25/50\n1864/1864 [==============================] - 368s 197ms/step - loss: 0.1302 - accuracy: 0.8684 - f1_score: 0.8359 - val_loss: 0.1009 - val_accuracy: 0.8959 - val_f1_score: 0.8734\n\nEpoch 00025: ReduceLROnPlateau reducing learning rate to 1.0000000116860975e-08.\n\"\"\"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Saving model and trainning history","metadata":{}},{"cell_type":"code","source":"model.save(\"densenet169_100ft.h5\")\nwith open('densenet169_history', 'wb') as file_pi:\n    pickle.dump(history.history, file_pi)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training results\n- ReduceOnPlateau did not improve results","metadata":{}},{"cell_type":"code","source":"# Plotting f1-score history\nplt.plot(history.history['f1_score'])\nplt.plot(history.history['val_f1_score'])\nplt.title('model f1-score')\nplt.ylabel('f1-score')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plotting loss history\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plotting learning rate history\nplt.plot(history.history['lr'])\nplt.title('model learning rate')\nplt.ylabel('lr')\nplt.xlabel('epoch')\nplt.yscale(\"log\")\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Predictions with test images","metadata":{}},{"cell_type":"code","source":"# Load model\nmodel = load_model(\"../input/vgg16-imagenet-pre-treined-model/densenet169_100ft.h5\")\n\nfilenames = []\ntest_imgs_path = '../input/plant-pathology-2021-fgvc8/test_images'\npreds = []\n\n# Build numpy array with predictions\nfor img in os.listdir(test_imgs_path):\n    filenames.append(img)\n    img = os.path.join(test_imgs_path, img)\n    img_array = cv2.imread(img)\n    \n    #Pre-processing input\n    img_array = cv2.resize(img_array, (224, 224))\n    img_array = cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB)\n    img_array = preprocess_input(img_array)\n    img_array = np.expand_dims(img_array, axis=0)\n    \n    #Prediction\n    if len(preds) != 0:\n        preds = np.vstack([preds, model.predict(img_array)])\n    else:\n        preds = model.predict(img_array)\n\n# Processing predictions to avoid weird classifications \nthreshold = 0.4\nfor i, pred in enumerate(preds):\n    argmax = np.argmax(pred)\n    if not (pred > threshold).any():\n        preds[i][argmax] = 1\n    elif argmax == 2:\n        preds[i] = np.array([0, 0, 1, 0, 0, 0])\n    else:\n        preds[i][2] = 0\n        \n# Applying treshold\npreds = (preds > threshold).astype(int)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Generate submit file","metadata":{}},{"cell_type":"code","source":"predictions=[]\nlabels = mlb.classes_\nfor row in preds:\n    l=[]\n    for index,cls in enumerate(row):\n        if cls:\n            l.append(labels[index])\n    predictions.append(\" \".join(l))\n    \nresults=pd.DataFrame({\"image\":filenames,\n                      \"labels\":predictions})\nresults.to_csv(\"./submission.csv\",index=False)\nresults","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}