{"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":"# Importing Data","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom tensorflow.keras import layers,models\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications.densenet import DenseNet121\nimport warnings\nwarnings.simplefilter(\"ignore\")\nfrom PIL import Image\nfrom  tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.097252Z","iopub.execute_input":"2022-04-08T16:01:57.097554Z","iopub.status.idle":"2022-04-08T16:01:57.103288Z","shell.execute_reply.started":"2022-04-08T16:01:57.097499Z","shell.execute_reply":"2022-04-08T16:01:57.102585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nDir = '../input/cassava-leaf-disease-classification'\nCFG = {\n    'input_dir': \"../input/cassava-leaf-disease-classification\",\n    'train_folder': \"train_images\",\n    'test_folder': \"test_images\",\n}","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.209252Z","iopub.execute_input":"2022-04-08T16:01:57.209815Z","iopub.status.idle":"2022-04-08T16:01:57.215479Z","shell.execute_reply.started":"2022-04-08T16:01:57.209775Z","shell.execute_reply":"2022-04-08T16:01:57.214626Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.323575Z","iopub.execute_input":"2022-04-08T16:01:57.324259Z","iopub.status.idle":"2022-04-08T16:01:57.350273Z","shell.execute_reply.started":"2022-04-08T16:01:57.324221Z","shell.execute_reply":"2022-04-08T16:01:57.349583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df['label'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.426746Z","iopub.execute_input":"2022-04-08T16:01:57.427313Z","iopub.status.idle":"2022-04-08T16:01:57.434913Z","shell.execute_reply.started":"2022-04-08T16:01:57.42727Z","shell.execute_reply":"2022-04-08T16:01:57.434031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score\ny_pred = [3] * len(train_df.label)\nprint(\"The baseline accuracy is {}\".format(accuracy_score(y_pred, train_df.label)))","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.556237Z","iopub.execute_input":"2022-04-08T16:01:57.556863Z","iopub.status.idle":"2022-04-08T16:01:57.574986Z","shell.execute_reply.started":"2022-04-08T16:01:57.556819Z","shell.execute_reply":"2022-04-08T16:01:57.574085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"Batch_size = 16\nimg_height, img_width = 300, 300","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.645096Z","iopub.execute_input":"2022-04-08T16:01:57.645594Z","iopub.status.idle":"2022-04-08T16:01:57.64969Z","shell.execute_reply.started":"2022-04-08T16:01:57.645555Z","shell.execute_reply":"2022-04-08T16:01:57.64874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Augmentation","metadata":{}},{"cell_type":"code","source":"train_df['label'] = train_df['label'].astype('str')\ngen = ImageDataGenerator(\n    horizontal_flip = True,\n    vertical_flip = True,\n    validation_split = 0.2,\n) \n\ntrain_datagen = gen.flow_from_dataframe(\n    train_df,\n    directory = os.path.join(Dir, \"train_images\"),\n    batch_size = Batch_size,\n    target_size = (img_height, img_width),\n    subset = \"training\",\n    seed = 42,\n    x_col = \"image_id\",\n    y_col = \"label\",\n    class_mode = \"categorical\"\n)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:01:57.750922Z","iopub.execute_input":"2022-04-08T16:01:57.751692Z","iopub.status.idle":"2022-04-08T16:02:06.34998Z","shell.execute_reply.started":"2022-04-08T16:01:57.751654Z","shell.execute_reply":"2022-04-08T16:02:06.349152Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are 17118 training images.","metadata":{}},{"cell_type":"code","source":"val_gen = ImageDataGenerator(\n    validation_split = 0.2\n)\n\nval_datagen = val_gen.flow_from_dataframe(\n    train_df,\n    directory = os.path.join(Dir, \"train_images\"),\n    batch_size = Batch_size,\n    target_size = (img_height, img_width),\n    subset = \"validation\",\n    seed = 42,\n    x_col = \"image_id\",\n    y_col = \"label\",\n    class_mode = \"categorical\"\n)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:06.351708Z","iopub.execute_input":"2022-04-08T16:02:06.352127Z","iopub.status.idle":"2022-04-08T16:02:06.539549Z","shell.execute_reply.started":"2022-04-08T16:02:06.352088Z","shell.execute_reply":"2022-04-08T16:02:06.538726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are 4279 validation images.","metadata":{}},{"cell_type":"code","source":"len(train_datagen), len(val_datagen)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:06.540726Z","iopub.execute_input":"2022-04-08T16:02:06.541592Z","iopub.status.idle":"2022-04-08T16:02:06.548077Z","shell.execute_reply.started":"2022-04-08T16:02:06.541553Z","shell.execute_reply":"2022-04-08T16:02:06.547177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Calculation**:\n\nThe length of training images is basically 21397 * 0.8 / 16 = 1070 as the generator returns the batches.\n\nSimilarly the length of validation images is 21397 * 0.2 / 16 = 268.","metadata":{}},{"cell_type":"code","source":"img, label = next(train_datagen)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:06.550792Z","iopub.execute_input":"2022-04-08T16:02:06.551485Z","iopub.status.idle":"2022-04-08T16:02:06.683604Z","shell.execute_reply.started":"2022-04-08T16:02:06.551447Z","shell.execute_reply":"2022-04-08T16:02:06.682808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"next() is used to get the next batch of images and labels.","metadata":{}},{"cell_type":"code","source":"Steps_per_train = train_datagen.n / train_datagen.batch_size\nSteps_per_val = val_datagen.n / val_datagen.batch_size","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:06.685722Z","iopub.execute_input":"2022-04-08T16:02:06.685938Z","iopub.status.idle":"2022-04-08T16:02:06.691214Z","shell.execute_reply.started":"2022-04-08T16:02:06.68591Z","shell.execute_reply":"2022-04-08T16:02:06.690434Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Build model","metadata":{}},{"cell_type":"markdown","source":"# Transfer Learning","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications.efficientnet import EfficientNetB3\ndef create_model():\n    model = models.Sequential()\n    model.add(EfficientNetB3(include_top = False, weights = '../input/myefficientnetb3/efficientnetb3_notop.h5',\n                             input_shape = (img_height, img_width, 3),  drop_connect_rate=0.3))\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.Flatten())\n    model.add(layers.Dense(256, activation = \"relu\"))\n    model.add(layers.Dropout(0.3))\n    model.add(layers.Dense(5, activation='softmax'))\n    model.build((300,256))\n    loss = tf.keras.losses.CategoricalCrossentropy(\n        label_smoothing=0.0001,\n        name='categorical_crossentropy'\n    )\n    model.compile(optimizer = Adam(),\n                  loss = loss,\n                  metrics = [\"categorical_accuracy\"])\n    return model\n\nmodel = create_model()\nmodel.layers[0].trainable = False","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:06.692968Z","iopub.execute_input":"2022-04-08T16:02:06.693511Z","iopub.status.idle":"2022-04-08T16:02:56.728228Z","shell.execute_reply.started":"2022-04-08T16:02:06.69347Z","shell.execute_reply":"2022-04-08T16:02:56.72751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:56.729386Z","iopub.execute_input":"2022-04-08T16:02:56.731318Z","iopub.status.idle":"2022-04-08T16:02:56.757501Z","shell.execute_reply.started":"2022-04-08T16:02:56.731285Z","shell.execute_reply":"2022-04-08T16:02:56.756755Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tf.keras.utils.plot_model(model)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:56.759062Z","iopub.execute_input":"2022-04-08T16:02:56.759423Z","iopub.status.idle":"2022-04-08T16:02:56.951696Z","shell.execute_reply.started":"2022-04-08T16:02:56.759385Z","shell.execute_reply":"2022-04-08T16:02:56.950828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training classifier","metadata":{}},{"cell_type":"code","source":"rlronp=tf.keras.callbacks.ReduceLROnPlateau(monitor=\"val_loss\",\n                                            factor=0.2,\n                                            mode = \"min\",\n                                            min_lr=1e-6,\n                                            patience=2, \n                                            verbose=1)\n\nestop=tf.keras.callbacks.EarlyStopping(monitor=\"val_loss\", \n                                       mode= \"min\",\n                                       patience=3, \n                                       verbose=1,\n                                       restore_best_weights=True)\n\nhistory = model.fit_generator(\n    train_datagen,\n    steps_per_epoch = Steps_per_train,\n    epochs = 3,\n    validation_data = val_datagen,\n    validation_steps = Steps_per_val,\n    callbacks = [rlronp,estop]\n)\n","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:02:56.953582Z","iopub.execute_input":"2022-04-08T16:02:56.955742Z","iopub.status.idle":"2022-04-08T16:43:37.90955Z","shell.execute_reply.started":"2022-04-08T16:02:56.95567Z","shell.execute_reply":"2022-04-08T16:43:37.908719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fine tuning","metadata":{}},{"cell_type":"code","source":"model.layers[0].trainable = True\nmodel.compile(optimizer = Adam(1e-5),\n            loss = 'categorical_crossentropy',\n            metrics = [\"categorical_accuracy\"])\nhistory = model.fit_generator(\n    train_datagen,\n    steps_per_epoch = Steps_per_train,\n    epochs = 2,\n    validation_data = val_datagen,\n    validation_steps = Steps_per_val,\n    callbacks = [rlronp,estop]\n)\nmodel.save(\"Casava_Model\"+ \".h5\")","metadata":{"execution":{"iopub.status.busy":"2022-04-08T16:56:41.32614Z","iopub.execute_input":"2022-04-08T16:56:41.32641Z","iopub.status.idle":"2022-04-08T17:29:07.688882Z","shell.execute_reply.started":"2022-04-08T16:56:41.32638Z","shell.execute_reply":"2022-04-08T17:29:07.688077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plots between Accuracy and Loss","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\ntrain_acc = history.history[\"categorical_accuracy\"]\nval_acc = history.history[\"val_categorical_accuracy\"]\nepochs = range(1, len(train_acc)+1)\nplt.plot(epochs, train_acc, \"bo\", label = \"Training Accuracy\")\nplt.plot(epochs, val_acc, \"b\", label = \"Validation Accuracy\")\nplt.title(\"Training and Validation Accuracy\")\nplt.legend()","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:32:08.546353Z","iopub.execute_input":"2022-04-08T17:32:08.547038Z","iopub.status.idle":"2022-04-08T17:32:08.782042Z","shell.execute_reply.started":"2022-04-08T17:32:08.546993Z","shell.execute_reply":"2022-04-08T17:32:08.781366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nplt.figure(figsize=(8,6))\ntrain_loss = history.history[\"loss\"]\nval_loss = history.history[\"val_loss\"]\nepochs = range(1, len(train_loss)+1)\nplt.plot(epochs, train_loss, \"bo\", label = \"Training Loss\")\nplt.plot(epochs, val_loss, \"b\", label = \"Validation Loss\")\nplt.title(\"Training and Validation Loss\")\nplt.legend()","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:32:24.635613Z","iopub.execute_input":"2022-04-08T17:32:24.636203Z","iopub.status.idle":"2022-04-08T17:32:24.859777Z","shell.execute_reply.started":"2022-04-08T17:32:24.636163Z","shell.execute_reply":"2022-04-08T17:32:24.858471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submit","metadata":{}},{"cell_type":"code","source":"from PIL import Image\nimport pandas as pd\nimport numpy as np\nfrom numpy import random\nimport os\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nfrom tqdm import tqdm\ncrop_size=300\n","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:33:57.732308Z","iopub.execute_input":"2022-04-08T17:33:57.732621Z","iopub.status.idle":"2022-04-08T17:33:57.737893Z","shell.execute_reply.started":"2022-04-08T17:33:57.732589Z","shell.execute_reply":"2022-04-08T17:33:57.736852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def scan_over_image(img_path, crop_size=300):\n    '''\n    Will extract 512x512 images covering the whole original image\n    with some overlap between images\n    根据地址读取一张原始图片，剪裁成4张512*512大小的图片\n    '''\n    \n    img = Image.open(img_path)\n    img_height, img_width = img.size\n    img = np.array(img)\n    \n    y = random.randint(0,img_height-crop_size)\n    x = random.randint(0,img_width-crop_size)\n\n    x_img_origins = [0,img_width-crop_size]\n    y_img_origins = [0,img_height-crop_size]\n    img_list = []\n    for x in x_img_origins:\n        for y in y_img_origins:\n            img_list.append(img[x:x+crop_size , y:y+crop_size,:])\n  \n    return np.array(img_list)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:35:32.859198Z","iopub.execute_input":"2022-04-08T17:35:32.859673Z","iopub.status.idle":"2022-04-08T17:35:32.868569Z","shell.execute_reply.started":"2022-04-08T17:35:32.859635Z","shell.execute_reply":"2022-04-08T17:35:32.865594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_time_augmentation_layers = tf.keras.Sequential(\n    [\n        #反转，缩放，对比度\n        layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        layers.experimental.preprocessing.RandomZoom((-0.2, 0)),\n        layers.experimental.preprocessing.RandomContrast((0.2,0.2))\n    ]\n)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:33:04.029892Z","iopub.execute_input":"2022-04-08T17:33:04.030495Z","iopub.status.idle":"2022-04-08T17:33:04.046894Z","shell.execute_reply.started":"2022-04-08T17:33:04.030458Z","shell.execute_reply":"2022-04-08T17:33:04.046192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict_and_vote(image_filename, folder, TTA_runs=4):\n    '''\n    Run the model over 4 local areas of the given image,\n    before making a decision depending on the most predicted\n    disease.\n    '''\n    \n    #apply TTA to each of the 4 images and sum all predictions for each local image\n    localised_predictions = []\n    local_image_list = scan_over_image(folder+image_filename)\n    #将4个剪裁图分别进行4种增强（1张原图对应16张增强图片），\n    for local_image in local_image_list:\n        #把图片复制成4份 转化成张量类型\n        duplicated_local_image = tf.convert_to_tensor(np.array([local_image for i in range(TTA_runs)]))\n        #4个图片经过数据增强层得到4个\n        augmented_images = test_time_augmentation_layers(duplicated_local_image)\n        #将4个图片放入模型预测的到四个结果\n        predictions = model.predict(augmented_images)\n        #将4个结果的概率相加的到1个剪裁图片的预测\n        localised_predictions.append(np.sum(predictions, axis=0))\n    \n    #sum all predictions from all 4 images and retrieve the index of the highest value\n    #将4个剪裁图片相加得到1个\n    global_predictions = np.sum(np.array(localised_predictions),axis=0)\n    #选出概率最高的那个标签\n    final_prediction = np.argmax(global_predictions)\n    \n    return final_prediction","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:33:06.494185Z","iopub.execute_input":"2022-04-08T17:33:06.494966Z","iopub.status.idle":"2022-04-08T17:33:06.502386Z","shell.execute_reply.started":"2022-04-08T17:33:06.494916Z","shell.execute_reply":"2022-04-08T17:33:06.501683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def run_predictions_over_image_list(image_list, folder):\n    \"\"\"\n    预测数据集\n    \"\"\"\n    #用tqdm进度条可视化进度\n    predictions = [] \n    with tqdm(total=len(image_list)) as pbar:\n        for image_filename in image_list:\n            predictions.append(predict_and_vote(image_filename, folder))\n            #每完成一张图片预测进度+1\n            pbar.update(1)\n    return predictions","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:33:10.620749Z","iopub.execute_input":"2022-04-08T17:33:10.621003Z","iopub.status.idle":"2022-04-08T17:33:10.627029Z","shell.execute_reply.started":"2022-04-08T17:33:10.620975Z","shell.execute_reply":"2022-04-08T17:33:10.625729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#导入测试数据地址\ntest_folder = '../input/cassava-leaf-disease-classification/test_images/'\n#创建提交格式\nsubmission_df = pd.DataFrame(columns={\"image_id\",\"label\"})\n#将测试图片文件名列表写入DF，标签全部设为0\nsubmission_df[\"image_id\"] =  os.listdir(test_folder)\nsubmission_df[\"label\"] = 0","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:33:14.51678Z","iopub.execute_input":"2022-04-08T17:33:14.517314Z","iopub.status.idle":"2022-04-08T17:33:14.533327Z","shell.execute_reply.started":"2022-04-08T17:33:14.517275Z","shell.execute_reply":"2022-04-08T17:33:14.53248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df[\"label\"] = run_predictions_over_image_list(submission_df[\"image_id\"], test_folder)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:35:37.341396Z","iopub.execute_input":"2022-04-08T17:35:37.342124Z","iopub.status.idle":"2022-04-08T17:35:39.927466Z","shell.execute_reply.started":"2022-04-08T17:35:37.342086Z","shell.execute_reply":"2022-04-08T17:35:39.92676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-04-08T17:35:46.503053Z","iopub.execute_input":"2022-04-08T17:35:46.503356Z","iopub.status.idle":"2022-04-08T17:35:46.510205Z","shell.execute_reply.started":"2022-04-08T17:35:46.503324Z","shell.execute_reply":"2022-04-08T17:35:46.509297Z"},"trusted":true},"execution_count":null,"outputs":[]}]}