{"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":"## Sorghum -100 Cultivar Identification with CNN","metadata":{}},{"cell_type":"markdown","source":"## Import Packages","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport tensorflow as tf\nimport pandas as pd\nimport os\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nimport glob","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:27.880822Z","iopub.execute_input":"2022-03-18T01:56:27.881309Z","iopub.status.idle":"2022-03-18T01:56:33.054754Z","shell.execute_reply.started":"2022-03-18T01:56:27.881271Z","shell.execute_reply":"2022-03-18T01:56:33.054035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Configuration","metadata":{}},{"cell_type":"code","source":"class Config:\n    image_size = 128\n    batch_size = 128\n    dataset_name = \"sorghum-id-fgvc-9\"\n    output_dataset_name = \"sorghum-100-cultivar-identification-output\"\n    is_training = False\nconfig = Config()","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:46.182244Z","iopub.execute_input":"2022-03-18T01:56:46.182523Z","iopub.status.idle":"2022-03-18T01:56:46.189093Z","shell.execute_reply.started":"2022-03-18T01:56:46.182493Z","shell.execute_reply":"2022-03-18T01:56:46.188483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import dataset","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv(f\"../input/{config.dataset_name}/train_cultivar_mapping.csv\")\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:48.268328Z","iopub.execute_input":"2022-03-18T01:56:48.269007Z","iopub.status.idle":"2022-03-18T01:56:48.322537Z","shell.execute_reply.started":"2022-03-18T01:56:48.268967Z","shell.execute_reply":"2022-03-18T01:56:48.321828Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Distribution of Sample Count for different categories","metadata":{}},{"cell_type":"code","source":"train.cultivar.value_counts().hist()","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:50.369637Z","iopub.execute_input":"2022-03-18T01:56:50.369914Z","iopub.status.idle":"2022-03-18T01:56:50.636288Z","shell.execute_reply.started":"2022-03-18T01:56:50.369885Z","shell.execute_reply":"2022-03-18T01:56:50.635634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Number of Classes","metadata":{}},{"cell_type":"code","source":"unique_cultivars = list(train.cultivar.unique())\nnum_classes = len(unique_cultivars)\nnum_classes","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:52.960822Z","iopub.execute_input":"2022-03-18T01:56:52.961382Z","iopub.status.idle":"2022-03-18T01:56:52.970789Z","shell.execute_reply.started":"2022-03-18T01:56:52.961344Z","shell.execute_reply":"2022-03-18T01:56:52.970018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train[\"file_path\"] = train[\"image\"].apply(lambda image: f\"../input/{config.dataset_name}/train_images/\" + image)\ntrain[\"cultivar_index\"] = train[\"cultivar\"].map(lambda item: unique_cultivars.index(item))\ntrain[\"is_exist\"] = train[\"file_path\"].apply(lambda file_path: os.path.exists(file_path))\ntrain = train[train.is_exist==True]\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:56:54.337081Z","iopub.execute_input":"2022-03-18T01:56:54.337596Z","iopub.status.idle":"2022-03-18T01:57:39.089457Z","shell.execute_reply.started":"2022-03-18T01:56:54.337556Z","shell.execute_reply":"2022-03-18T01:57:39.088711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Modeling","metadata":{}},{"cell_type":"code","source":"def preprocess(image_url, target):\n    image_string = tf.io.read_file(image_url)\n    image = tf.image.decode_jpeg(image_string, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.image.central_crop(image, 1.0)\n    image = tf.image.resize(image, (config.image_size, config.image_size))\n    return image, target","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:57:39.091069Z","iopub.execute_input":"2022-03-18T01:57:39.091266Z","iopub.status.idle":"2022-03-18T01:57:39.096327Z","shell.execute_reply.started":"2022-03-18T01:57:39.091243Z","shell.execute_reply":"2022-03-18T01:57:39.095684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def block(x, filters, kernel_size, repetitions, pool_size=2, strides=2):\n    for i in range(repetitions):\n        x = tf.keras.layers.Conv2D(filters, kernel_size, activation='relu', padding='same')(x)\n    x = tf.keras.layers.MaxPooling2D(pool_size, strides)(x)\n    return x\n\ndef get_model():\n    inputs = tf.keras.Input((config.image_size, config.image_size , 3))\n    x = block(inputs, 8, 3, 2)\n    x = tf.keras.layers.Dropout(0.2)(x)\n    x = block(x, 16, 3, 2)\n    x = tf.keras.layers.Dropout(0.2)(x)\n    x = block(x, 32, 3, 2)\n    x = tf.keras.layers.Dropout(0.2)(x)\n    x = block(x, 64, 3, 2)\n    x = tf.keras.layers.Dropout(0.2)(x)\n    x = block(x, 128, 3, 2)\n    x = tf.keras.layers.Dropout(0.2)(x)\n    x = tf.keras.layers.GlobalAveragePooling2D()(x)\n    output = tf.keras.layers.Dense(num_classes, activation=\"softmax\")(x)\n    model = tf.keras.Model(inputs=inputs, outputs=output)\n    model.compile(loss=\"sparse_categorical_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])\n    return model","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:57:39.097693Z","iopub.execute_input":"2022-03-18T01:57:39.098649Z","iopub.status.idle":"2022-03-18T01:57:39.111275Z","shell.execute_reply.started":"2022-03-18T01:57:39.098614Z","shell.execute_reply":"2022-03-18T01:57:39.110559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's take a look at this Model:","metadata":{}},{"cell_type":"code","source":"model = get_model()\nmodel.summary()\ntf.keras.utils.plot_model(model, show_shapes=True)","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:57:39.113362Z","iopub.execute_input":"2022-03-18T01:57:39.113651Z","iopub.status.idle":"2022-03-18T01:57:43.082247Z","shell.execute_reply.started":"2022-03-18T01:57:39.113616Z","shell.execute_reply":"2022-03-18T01:57:43.080644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Training","metadata":{}},{"cell_type":"code","source":"tf.keras.backend.clear_session()\nmodels = []\nhistorys = []\nis_k_fold = False\nif is_k_fold:\n    kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n    best_fold = 0\n    for fold, (train_indices, val_indices) in enumerate(kfold.split(train, train[\"cultivar_index\"])):\n        if best_fold != None and fold != best_fold:\n            continue\n        train_df = train.iloc[train_indices]\n        val_df= train.iloc[val_indices]\n        checkpoint_path = f\"model_{fold}.h5\"\n        checkpoint = tf.keras.callbacks.ModelCheckpoint(\n            checkpoint_path, \n            save_best_only=True\n        )\n        early_stop = tf.keras.callbacks.EarlyStopping(\n            min_delta=1e-4, \n            patience=10\n        )\n        reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(\n            factor=0.3,\n            patience=2, \n            min_lr=1e-7\n        )\n        callbacks = [early_stop, checkpoint, reduce_lr]\n        train_ds = tf.data.Dataset.from_tensor_slices((train_df[\"file_path\"], train_df[\"cultivar_index\"])).map(preprocess).shuffle(512).batch(config.batch_size).cache().prefetch(tf.data.AUTOTUNE)\n        val_ds = tf.data.Dataset.from_tensor_slices((val_df[\"file_path\"], val_df[\"cultivar_index\"])).map(preprocess).batch(config.batch_size).cache().prefetch(tf.data.AUTOTUNE)\n        model = get_model()\n        if config.is_training:\n            history = model.fit(train_ds, epochs=300, validation_data=val_ds, callbacks=callbacks)\n            for metrics in [(\"loss\", \"val_loss\"), (\"accuracy\", \"val_accuracy\")]:\n                pd.DataFrame(history.history, columns=metrics).plot()\n                plt.show()\n            model.load_weights(checkpoint_path)\n            historys.append(history)\n        else:\n            model.load_weights(f\"../input/{config.output_dataset_name}/{checkpoint_path}\")\n        models.append(model)\nelse:\n    checkpoint_path = \"model.h5\"\n    model = get_model()\n    if config.is_training:\n        train_df, val_df = train_test_split(train, test_size=0.2, random_state=42)\n        train_ds = tf.data.Dataset.from_tensor_slices((train_df[\"file_path\"], train_df[\"cultivar_index\"])).map(preprocess).shuffle(512).batch(config.batch_size).cache().prefetch(tf.data.AUTOTUNE)\n        val_ds = tf.data.Dataset.from_tensor_slices((val_df[\"file_path\"], val_df[\"cultivar_index\"])).map(preprocess).batch(config.batch_size).cache().prefetch(tf.data.AUTOTUNE)\n        checkpoint = tf.keras.callbacks.ModelCheckpoint(\n            checkpoint_path, \n            save_best_only=True\n        )\n        early_stop = tf.keras.callbacks.EarlyStopping(\n            min_delta=1e-4, \n            patience=10\n        )\n        reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(\n            factor=0.3,\n            patience=2, \n            min_lr=1e-7\n        )\n        callbacks = [checkpoint]\n        history = model.fit(train_ds, epochs=100, validation_data=val_ds, callbacks=callbacks)\n        for metrics in [(\"loss\", \"val_loss\"), (\"accuracy\", \"val_accuracy\")]:\n            pd.DataFrame(history.history, columns=metrics).plot()\n            plt.show()\n        model.load_weights(checkpoint_path) \n    else:\n        model.load_weights(f\"../input/{config.output_dataset_name}/{checkpoint_path}\")\n    models.append(model)","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:57:43.084132Z","iopub.execute_input":"2022-03-18T01:57:43.08439Z","iopub.status.idle":"2022-03-18T01:57:43.288832Z","shell.execute_reply.started":"2022-03-18T01:57:43.084358Z","shell.execute_reply":"2022-03-18T01:57:43.288053Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"def preprocess_test(image_url):\n    image_string = tf.io.read_file(image_url)\n    image = tf.image.decode_jpeg(image_string, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.image.central_crop(image, 1.0)\n    image = tf.image.resize(image, (config.image_size, config.image_size))\n    return image, 0\n\ndef inference(models, test_ds):\n    total_results = []\n    for model in models:\n        total_results.append(model.predict(test_ds))\n    results = np.mean(total_results, axis=0)\n    return np.argmax(results, axis=-1)","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:57:43.290377Z","iopub.execute_input":"2022-03-18T01:57:43.290651Z","iopub.status.idle":"2022-03-18T01:57:43.298415Z","shell.execute_reply.started":"2022-03-18T01:57:43.290612Z","shell.execute_reply":"2022-03-18T01:57:43.297701Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nif not config.is_training:\n    submission = pd.read_csv(f\"../input/{config.dataset_name}/sample_submission.csv\")\n    submission[\"file_path\"] = submission[\"filename\"].apply(lambda filename: f\"../input/{config.dataset_name}/test/{filename}\")\n    test_ds = tf.data.Dataset.from_tensor_slices((submission[\"file_path\"])).map(preprocess_test).batch(config.batch_size).cache().prefetch(tf.data.AUTOTUNE)\n    y_pred = inference(models, test_ds)\n    np_unique_cultivars = np.array(unique_cultivars) \n    cultivars = list(np_unique_cultivars[y_pred.reshape(-1)])\n    submission[\"cultivar\"] = cultivars\n    submission[[\"filename\", \"cultivar\"]].to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-18T01:58:10.179425Z","iopub.execute_input":"2022-03-18T01:58:10.18017Z","iopub.status.idle":"2022-03-18T02:13:58.268264Z","shell.execute_reply.started":"2022-03-18T01:58:10.180132Z","shell.execute_reply":"2022-03-18T02:13:58.267491Z"},"trusted":true},"execution_count":null,"outputs":[]}]}