{"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":"!nvidia-smi","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:14.996999Z","iopub.execute_input":"2022-04-11T13:10:14.997546Z","iopub.status.idle":"2022-04-11T13:10:15.721905Z","shell.execute_reply.started":"2022-04-11T13:10:14.997454Z","shell.execute_reply":"2022-04-11T13:10:15.721102Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -U keras-efficientnet-v2\n!pip install tensorflow_addons","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:15.724115Z","iopub.execute_input":"2022-04-11T13:10:15.724391Z","iopub.status.idle":"2022-04-11T13:10:32.211384Z","shell.execute_reply.started":"2022-04-11T13:10:15.724353Z","shell.execute_reply":"2022-04-11T13:10:32.210537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#%%\n# my simple example of sorghum classification. It uses original \"sorghum-id-fgvc-9\" images to train&inference, so it's very slow. I recommend use tfrecord dataset like https://www.kaggle.com/code/tchaye59/512x512-images-to-tfrecords\n# It's just sample, so you should tune the parameters for more score!\n\n\nimport tensorflow as tf\nimport keras_efficientnet_v2\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport os\n\nproj_dir = \"/kaggle/input/sorghum-id-fgvc-9/\"\noutput_dir = \"output/\"\n\ndf = pd.read_csv(proj_dir + \"train_cultivar_mapping.csv\")\n\nbase_path = proj_dir + \"train_images/\"\n\ndf[\"fullpath\"] = base_path + df[\"image\"] \n\n# omit NA images\nexists = []\n\nfor i in df[\"fullpath\"]:\n    if not os.path.exists(i):\n        exists.append(False)\n        print(i)\n    else:\n        exists.append(True)\n\ndf[\"exist\"] = pd.Series(exists)\ndf = df[df.exist]\nprint(\"available images: \" + str(len(df)))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-04-11T13:10:32.215025Z","iopub.execute_input":"2022-04-11T13:10:32.215259Z","iopub.status.idle":"2022-04-11T13:10:51.348463Z","shell.execute_reply.started":"2022-04-11T13:10:32.21523Z","shell.execute_reply":"2022-04-11T13:10:51.347441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 64\nAUTOTUNE = tf.data.experimental.AUTOTUNE\nTRAIN_SIZE = 0.95\nSHUFFLE_SIZE = 3000\ntest_data_paths = proj_dir + 'test/*.png'\nWIDTH = 256\nHEIGHT = 256\nEPOCHS = 1\nMAGNITUDE = 5\nmodel_name = \"simple_effnet\"\n\npaths = df[\"fullpath\"]\nlabels_str = df[\"cultivar\"]\n\n\nimage_count = len(paths)\nimage_count","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:51.354958Z","iopub.execute_input":"2022-04-11T13:10:51.35523Z","iopub.status.idle":"2022-04-11T13:10:51.375075Z","shell.execute_reply.started":"2022-04-11T13:10:51.355189Z","shell.execute_reply":"2022-04-11T13:10:51.371903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_to_index = dict((name, index) for index,name in enumerate(labels_str.unique()))\nlabel_to_index\n\nlabels_idx = labels_str.map(lambda x: label_to_index[x])\nlabels_idx","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:51.378692Z","iopub.execute_input":"2022-04-11T13:10:51.379757Z","iopub.status.idle":"2022-04-11T13:10:51.417631Z","shell.execute_reply.started":"2022-04-11T13:10:51.379691Z","shell.execute_reply":"2022-04-11T13:10:51.416826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_paths, val_paths, train_labels, val_labels = train_test_split(paths, labels_idx, train_size=TRAIN_SIZE, shuffle=True, random_state=42, stratify=labels_idx)","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:51.421244Z","iopub.execute_input":"2022-04-11T13:10:51.42149Z","iopub.status.idle":"2022-04-11T13:10:51.471295Z","shell.execute_reply.started":"2022-04-11T13:10:51.421458Z","shell.execute_reply":"2022-04-11T13:10:51.470412Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preprocess_image(path):\n    image = tf.image.decode_png(path, channels=3)\n    image = tf.image.resize(image, size=(HEIGHT,WIDTH))\n\n    return image\n\ndef load_and_preprocess_image(path):\n    image = tf.io.read_file(path)\n    return preprocess_image(image)\n\ndef load_and_preprocess_from_path_label(path, label):\n    return load_and_preprocess_image(path), label\n\n","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:51.474812Z","iopub.execute_input":"2022-04-11T13:10:51.47506Z","iopub.status.idle":"2022-04-11T13:10:51.487841Z","shell.execute_reply.started":"2022-04-11T13:10:51.475028Z","shell.execute_reply":"2022-04-11T13:10:51.483085Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# define RandAug class(from keras_efficientnet_v2 sample codes)\n# it's too slow, so commented out\n\nclass RandomProcessImage:\n    def __init__(self, target_shape=(300, 300), magnitude=0, keep_shape=False):\n        self.target_shape, self.magnitude, self.keep_shape = target_shape, magnitude, keep_shape\n        self.target_shape = target_shape if len(target_shape) == 2 else target_shape[:2]\n        if magnitude > 0:\n            from keras_efficientnet_v2 import augment\n\n            translate_const, cutout_const = 100, 40\n            # translate_const = int(target_shape[0] * 10 / magnitude)\n            # cutout_const = int(target_shape[0] * 40 / 224)\n            print(\">>>> RandAugment: magnitude = %d, translate_const = %d, cutout_const = %d\" % (magnitude, translate_const, cutout_const))\n            aa = augment.RandAugment(num_layers = 2, magnitude=magnitude, translate_const=translate_const, cutout_const=cutout_const)\n            # aa.available_ops = [\"AutoContrast\", \"Equalize\", \"Invert\", \"Rotate\", \"Posterize\", \"Solarize\", \"Color\", \"Contrast\", \"Brightness\", \"Sharpness\", \"ShearX\", \"ShearY\", \"TranslateX\", \"TranslateY\", \"Cutout\", \"SolarizeAdd\"]\n            # aa.available_ops = [\"AutoContrast\", \"Equalize\", \"Invert\", \"Rotate\", \"Posterize\", \"Solarize\", \"Color\", \"Contrast\", \"Brightness\", \"Sharpness\", \"TranslateX\", \"TranslateY\", \"SolarizeAdd\"]\n            self.process = lambda img: aa.distort(img)\n        elif magnitude == 0:\n            self.process = lambda img: tf.image.random_flip_left_right(img)\n        else:\n            self.process = lambda img: img\n\n    def __call__(self, datapoint):\n        image = datapoint\n        if self.keep_shape:\n            cropped_shape = tf.reduce_min(tf.keras.backend.shape(image)[:2])\n            image = tf.image.random_crop(image, (cropped_shape, cropped_shape, 3))\n\n        input_image = tf.image.resize(image, self.target_shape)\n        input_image = self.process(input_image)\n        # input_image = (tf.cast(input_image, tf.float32) - 127.5) / 128\n        return input_image\n\ndata_aug = RandomProcessImage((WIDTH, HEIGHT), MAGNITUDE, keep_shape=True)\n\n","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:51.488714Z","iopub.execute_input":"2022-04-11T13:10:51.488967Z","iopub.status.idle":"2022-04-11T13:10:53.875051Z","shell.execute_reply.started":"2022-04-11T13:10:51.488931Z","shell.execute_reply":"2022-04-11T13:10:53.874274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_path_label_ds = tf.data.Dataset.from_tensor_slices((train_paths, train_labels))\nval_path_label_ds = tf.data.Dataset.from_tensor_slices((val_paths, val_labels))\n\ntrain_image_label_ds = train_path_label_ds.map(load_and_preprocess_from_path_label,num_parallel_calls=AUTOTUNE)\nval_image_label_ds = val_path_label_ds.map(load_and_preprocess_from_path_label,num_parallel_calls=AUTOTUNE)\n\ntrain_ds = train_image_label_ds\ntrain_ds = train_ds.repeat()\ntrain_ds = train_ds.shuffle(buffer_size=SHUFFLE_SIZE)\ntrain_ds = train_ds.map(lambda x, y:(data_aug(x), y), num_parallel_calls=AUTOTUNE)\ntrain_ds = train_ds.batch(BATCH_SIZE)\ntrain_ds = train_ds.prefetch(buffer_size=AUTOTUNE)\ntrain_ds\n\nval_ds = val_image_label_ds.cache()\nval_ds = val_ds.batch(BATCH_SIZE)\nval_ds = val_ds.prefetch(buffer_size=AUTOTUNE)\nval_ds","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:53.876344Z","iopub.execute_input":"2022-04-11T13:10:53.877009Z","iopub.status.idle":"2022-04-11T13:10:54.270409Z","shell.execute_reply.started":"2022-04-11T13:10:53.876968Z","shell.execute_reply":"2022-04-11T13:10:54.2697Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\ndef show_image_batch(images: list):\n    \"\"\"\n    Displays a batch of image present in images\n    \"\"\"\n    fig = plt.figure(figsize=(10,5))\n    for idx in range(6):\n        ax = plt.subplot(2, 3, idx+1)\n        plt.imshow(images[idx])\n        plt.axis(\"off\")\n\ndef show_dataset(dataset):\n    batch = next(iter(dataset))\n    images, labels = batch\n\n    plt.figure(figsize=(10, 10))\n    for idx in range(9):\n        ax = plt.subplot(3, 3, idx + 1)\n        plt.imshow(images[idx].numpy().astype(\"uint8\"))\n        plt.title(\"Class: {}\".format(labels[idx].numpy()))\n        plt.axis(\"off\")\n\nshow_dataset(train_ds)","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:54.271542Z","iopub.execute_input":"2022-04-11T13:10:54.2718Z","iopub.status.idle":"2022-04-11T13:10:54.27614Z","shell.execute_reply.started":"2022-04-11T13:10:54.271748Z","shell.execute_reply":"2022-04-11T13:10:54.275439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Exclude model top layers by set num_classes=0\nbasemodel = keras_efficientnet_v2.EfficientNetV2B0(input_shape=(WIDTH, HEIGHT, 3), drop_connect_rate=0.2, num_classes=0, include_preprocessing=True, pretrained=\"imagenet\")\n\n# for this dataset, imagenet weights base `basemodel.trainable=False` is not suitable!\nbasemodel.trainable = True\n\nimage_input = tf.keras.layers.Input(shape=(WIDTH,HEIGHT,3))\nout = basemodel(image_input)\nout = tf.keras.layers.GlobalAveragePooling2D()(out)\nout = tf.keras.layers.Dropout(0.2)(out)\nout = tf.keras.layers.Dense(len(label_to_index), activation=\"softmax\")(out)\n\nmodel = tf.keras.Model(image_input, out)\n\nmodel.compile(optimizer=tf.keras.optimizers.Adam(), \n              loss='sparse_categorical_crossentropy',\n              metrics=[\"accuracy\"])\n\n\n#%%\n#thanks for https://www.kaggle.com/code/tchaye59/efficientnet-tensorflow-baseline-tpu\ncallback = tf.keras.callbacks.EarlyStopping(monitor='accuracy',mode='max', patience=20)\nckp_callback = tf.keras.callbacks.ModelCheckpoint(\n                                            filepath=f'model.h5',\n                                            save_weights_only=True,\n                                            monitor='accuracy',\n                                            mode='max',\n                                            options=tf.train.CheckpointOptions(experimental_io_device='/job:localhost'),\n                                            save_best_only=True)\nreduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='accuracy',mode='max',factor=0.2,patience=3, min_lr=1e-5)\ncallbacks=[callback,ckp_callback,reduce_lr]\n\n\n#%%\nmodel.summary()\n\n#%%\nsteps_per_epoch=tf.math.ceil(len(train_paths)/BATCH_SIZE).numpy()\nsteps_per_epoch","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:54.277477Z","iopub.execute_input":"2022-04-11T13:10:54.279036Z","iopub.status.idle":"2022-04-11T13:10:57.381421Z","shell.execute_reply.started":"2022-04-11T13:10:54.278996Z","shell.execute_reply":"2022-04-11T13:10:57.380684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = model.fit(train_ds, epochs=EPOCHS, validation_data=val_ds, steps_per_epoch=steps_per_epoch, callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:10:57.382723Z","iopub.execute_input":"2022-04-11T13:10:57.382983Z","iopub.status.idle":"2022-04-11T13:13:15.989059Z","shell.execute_reply.started":"2022-04-11T13:10:57.382949Z","shell.execute_reply":"2022-04-11T13:13:15.986507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nacc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs_range = range(EPOCHS)\n\nplt.figure(figsize=(8, 8))\nplt.subplot(1, 2, 1)\nplt.plot(epochs_range, acc, label='Training Accuracy')\nplt.plot(epochs_range, val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(1, 2, 2)\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:13:15.990098Z","iopub.status.idle":"2022-04-11T13:13:15.9905Z","shell.execute_reply.started":"2022-04-11T13:13:15.990287Z","shell.execute_reply":"2022-04-11T13:13:15.990308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\ntest_image_paths = list(glob.glob(test_data_paths))\ntest_image_paths = [str(path) for path in test_image_paths]\nlen(test_image_paths)\n#%%\npath_label_test = tf.data.Dataset.from_tensor_slices(test_image_paths)","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:13:15.991621Z","iopub.status.idle":"2022-04-11T13:13:15.992042Z","shell.execute_reply.started":"2022-04-11T13:13:15.991819Z","shell.execute_reply":"2022-04-11T13:13:15.991841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ds = path_label_test.map(load_and_preprocess_image, tf.data.experimental.AUTOTUNE)\ntest_ds = test_ds.batch(BATCH_SIZE)\ntest_ds = test_ds.prefetch(buffer_size=32)\n#%%\npredictions = model.predict(test_ds, batch_size=BATCH_SIZE, verbose=1)\n","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:13:15.992816Z","iopub.status.idle":"2022-04-11T13:13:15.993226Z","shell.execute_reply.started":"2022-04-11T13:13:15.992998Z","shell.execute_reply":"2022-04-11T13:13:15.99302Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\nprediction_output = [(\"filename\", \"cultivar\")]\n\nfor i in tqdm(range(len(predictions.argmax(axis=1)))):\n    pred_idx = predictions.argmax(axis=1)[i]\n    prediction_output.append((os.path.basename(test_image_paths[i]), [k for k, v in label_to_index.items() if v == pred_idx][0]))\n#%%\npd.DataFrame(prediction_output).to_csv(\"submission.csv\", index=None, header=None)","metadata":{"execution":{"iopub.status.busy":"2022-04-11T13:13:15.994705Z","iopub.status.idle":"2022-04-11T13:13:15.997828Z","shell.execute_reply.started":"2022-04-11T13:13:15.997548Z","shell.execute_reply":"2022-04-11T13:13:15.997575Z"},"trusted":true},"execution_count":null,"outputs":[]}]}