{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":21154,"databundleVersionId":1243559,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-04-21T20:23:19.217391Z","iopub.execute_input":"2024-04-21T20:23:19.217731Z","iopub.status.idle":"2024-04-21T20:23:19.242331Z","shell.execute_reply.started":"2024-04-21T20:23:19.217703Z","shell.execute_reply":"2024-04-21T20:23:19.241543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q keras\n!pip install -q keras-core\n!pip install -q efficientnet\n!pip install -q tensorflow\n!pip install -q pydot\n!pip install -q graphviz","metadata":{"execution":{"iopub.status.busy":"2024-04-21T20:23:19.243576Z","iopub.execute_input":"2024-04-21T20:23:19.243833Z","iopub.status.idle":"2024-04-21T20:23:39.969794Z","shell.execute_reply.started":"2024-04-21T20:23:19.243808Z","shell.execute_reply":"2024-04-21T20:23:39.968625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\n\nprint(f\"Tensorflow version ${tf.__version__}\")","metadata":{"execution":{"iopub.status.busy":"2024-04-21T21:15:27.123132Z","iopub.execute_input":"2024-04-21T21:15:27.124130Z","iopub.status.idle":"2024-04-21T21:15:41.510534Z","shell.execute_reply.started":"2024-04-21T21:15:27.124092Z","shell.execute_reply":"2024-04-21T21:15:41.509705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TPU Distribution Strategies\n\nWe are going to be using the Tensor Processing Units (TPUs) in this competition. These are specialized co-processors built specifically for machine learning type computations. You can think of a single TPU as having 8 GPUs running in parallel to each other. We will need to tell Tensorflow how to best utilize the entire spread of 8 cores.","metadata":{}},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n#     print(f\"Running on TPU: ${tpu.master()}\")\nexcept ValueError:\n    tpu = None\n    \nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print(\"All devices: \", tf.config.list_logical_devices('TPU'))\nelse:\n    strategy = tf.distribute.get_strategy()\n\nprint(f\"REPLICAS: {strategy.num_replicas_in_sync}\")","metadata":{"execution":{"iopub.status.busy":"2024-04-21T21:15:59.148848Z","iopub.execute_input":"2024-04-21T21:15:59.149267Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading Kaggle data to a TPU\n\nThe TPU system reads from a specialized google cloud filesystem, called Google Cloud Storage (GCS). So we will need to find our kaggle data on GCS to use it in our models.\n\nTo use any other Kaggle dataset, replace `tpu-getting-started` with the path slug for that dataset. Non-Kaggle datasets are more difficult.","metadata":{}},{"cell_type":"code","source":"from kaggle_datasets import KaggleDatasets\n\n# GCS_DS_PATH = KaggleDatasets().get_gcs_path(\"tpu-getting-started\")\nGCS_DS_PATH = '/kaggle/input/tpu-getting-started'\nprint(GCS_DS_PATH)\n# !gsutil ls $GCS_DS_PATH","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_SIZE = [512, 512] # or 192, 224, 331, 512\nGCS_PATH = f\"{GCS_DS_PATH}/tfrecords-jpeg-{IMAGE_SIZE[0]}x{IMAGE_SIZE[1]}\"\n\nTRAINING_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/train/*.tfrec\")\nVALIDATION_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/val/*.tfrec\")\nTEST_FILENAMES = tf.io.gfile.glob(f\"{GCS_PATH}/test/*.tfrec\")\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.data.experimental import AUTOTUNE\n\nCLASSES = ['pink primrose',    'hard-leaved pocket orchid', \n           'canterbury bells', 'sweet pea',     \n           'wild geranium',    'tiger lily',           \n           'moon orchid',      'bird of paradise', \n           'monkshood',        'globe thistle',         # 00 - 09\n           \n           'snapdragon',       \"colt's foot\",               \n           'king protea',      'spear thistle', \n           'yellow iris',      'globe-flower',\n           'purple coneflower','peruvian lily',\n           'balloon flower',   'giant white arum lily', # 10 - 19\n           \n           'fire lily',        'pincushion flower',\n           'fritillary',       'red ginger',\n           'grape hyacinth',    'corn poppy',\n           'prince of wales feathers', 'stemless gentian',\n           'artichoke',        'sweet william',         # 20 - 29\n           \n           'carnation',        'garden phlox',\n           'love in the mist', 'cosmos',\n           'alpine sea holly', 'ruby-lipped cattleya',\n           'cape flower',      'great masterwort',\n           'siam tulip',       'lenten rose',           # 30 - 39\n           \n           'barberton daisy',  'daffodil',\n           'sword lily',       'poinsettia',\n           'bolero deep blue', 'wallflower',\n           'marigold',         'buttercup',\n           'daisy',            'common dandelion',      # 40 - 49\n           \n           'petunia',          'wild pansy',\n           'primula',          'sunflower',\n           'lilac hibiscus',   'bishop of llandaff',\n           'gaura',            'geranium',\n           'orange dahlia',    'pink-yellow dahlia',    # 50 - 59\n           \n           'cautleya spicata', 'japanese anemone',\n           'black-eyed susan', 'silverbush',\n           'californian poppy','osteospermum',\n           'spring crocus',    'iris',\n           'windflower',       'tree poppy',            # 60 - 69\n           \n           'gazania',          'azalea',\n           'water lily',       'rose',\n           'thorn apple',      'morning glory',\n           'passion flower',   'lotus',\n           'toad lily',        'anthurium',             # 70 - 79\n           \n           'frangipani',       'clematis',\n           'hibiscus',         'columbine',\n           'desert-rose',      'tree mallow',\n           'magnolia',         'cyclamen ',\n           'watercress',       'canna lily',            # 80 - 89\n           \n           'hippeastrum ',     'bee balm',\n           'pink quill',       'foxglove',\n           'bougainvillea',    'camellia',\n           'mallow',           'mexican petunia',\n           'bromelia',         'blanket flower',        # 90 - 99\n           \n           'trumpet creeper',  'blackberry lily',\n           'common tulip',     'wild rose']                                                                                                                                               # 100 - 102\n\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32)/255.0\n    image = tf.reshape(image, [*IMAGE_SIZE,3])\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFR_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"class\": tf.io.FixedLenFeature([], tf.int64)\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFR_FORMAT)\n    image = decode_image(example[\"image\"])\n    label = tf.cast(example[\"class\"], tf.int32)\n    return image, label\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFR_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"id\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFR_FORMAT)\n    image = decode_image(example[\"image\"])\n    idnum = example[\"id\"]\n    return image, idnum\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False # disable order, increase speed\n    \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)\n    dataset = dataset.with_options(ignore_order)\n    if labeled:\n        dataset = dataset.map(read_labeled_tfrecord, num_parallel_calls=AUTOTUNE)\n    else:\n        dataset = dataset.map(read_unlabeled_tfrecord, num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import re\nfrom tensorflow import keras\n\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\n# Random erasing, following https://github.com/zhunzhong07/Random-Erasing\n@tf.function\ndef random_black_box(image, prob_apply=0.25, min_area=0.02, max_area=0.4, min_aspect=0.3):\n    print(image.shape)\n    p = tf.random.uniform([], 0, 1)\n    if p <= prob_apply:\n        h,w,c = image.shape\n        origin_area = h*w\n        target_area = tf.random.uniform([], min_area, max_area) * origin_area\n        aspect_ratio = tf.random.uniform([], min_aspect, 1/min_aspect)\n        \n        erase_h = tf.cast(tf.round(tf.sqrt(target_area * aspect_ratio)), tf.int32)\n        erase_w = tf.cast(tf.round(tf.sqrt(target_area / aspect_ratio)), tf.int32)\n              \n        if erase_w < w:\n            x1 = tf.random.uniform([], 0, w-erase_w, dtype=tf.int32)\n        else:\n            erase_w = w\n            x1 = 0\n        if erase_h < h:\n            y1 = tf.random.uniform([], 0, h-erase_h, dtype=tf.int32)\n        else:\n            erase_h = h\n            y1 = 0\n        \n        image = tf.tensor_scatter_nd_update(image,\n          tf.stack(tf.meshgrid(x1+tf.range(erase_w), y1+tf.range(erase_h)), axis=-1),\n          tf.zeros([erase_h, erase_w, c]))\n        return image\n    else:\n        return image\n\nrng = tf.random.Generator.from_seed(4711, alg='philox')\ndef data_augment(image, label):\n    seed = rng.make_seeds(2)[0]\n    image = tf.image.stateless_random_brightness(image, 0.3, seed)\n    image = tf.image.stateless_random_contrast(image, 0.75, 1.25, seed)\n    image = tf.image.stateless_random_flip_left_right(image, seed)\n    image = tf.image.stateless_random_flip_up_down(image, seed)\n    image = random_black_box(image, prob_apply=0.40)\n    return image, label\n\ndef get_training_dataset():\n    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n    dataset = dataset.map(data_augment, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef get_validation_dataset(ordered=False):\n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n    \ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nNUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)\nNUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)\nNUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\n\nprint(f\"\"\"\n{NUM_TRAINING_IMAGES} training images\n{NUM_VALIDATION_IMAGES} validation images\n{NUM_TEST_IMAGES} unlabeled testing images\"\"\")\n\n# import re\n\n# BATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\n# rng = tf.random.Generator.from_seed(4711, alg='philox')\n# def data_augment(image, label):\n#     #seed = rng.make_seeds(2)[0]\n#     #image = tf.image.stateless_random_brightness(image, 0.1, seed)\n#     return image, label\n\n# def get_training_dataset():\n#     dataset = load_dataset(TRAINING_FILENAMES, labeled=True)\n#     dataset = dataset.map(data_augment, num_parallel_calls=AUTOTUNE)\n#     dataset = dataset.repeat()\n#     dataset = dataset.shuffle(2048)\n#     dataset = dataset.batch(BATCH_SIZE)\n#     dataset = dataset.prefetch(AUTOTUNE)\n#     return dataset\n\n# def get_validation_dataset(ordered=False):\n#     dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)\n#     dataset = dataset.batch(BATCH_SIZE)\n#     dataset = dataset.cache()\n#     dataset = dataset.prefetch(AUTOTUNE)\n#     return dataset\n    \n# def get_test_dataset(ordered=False):\n#     dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)\n#     dataset = dataset.batch(BATCH_SIZE)\n#     dataset = dataset.prefetch(AUTOTUNE)\n#     return dataset\n\n# def count_data_items(filenames):\n#     n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n#     return np.sum(n)\n\n# NUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)\n# NUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)\n# NUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\n\n# print(f\"\"\"\n# {NUM_TRAINING_IMAGES} training images\n# {NUM_VALIDATION_IMAGES} validation images\n# {NUM_TEST_IMAGES} unlabeled testing images\"\"\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Compute class weights\nfrom collections import Counter\ndataset = load_dataset(TRAINING_FILENAMES, labeled=True)\ndataset = dataset.map(lambda img,lbl: lbl, num_parallel_calls=AUTOTUNE)\ndataset = dataset.batch(BATCH_SIZE)\ndataset = dataset.prefetch(AUTOTUNE)\n\ncounter = Counter()\nfor batch in dataset:\n    counter.update([*batch.numpy()])\nmax_count = max([counter[i] for i in range(len(CLASSES))])\nweight_per_class = {id: max_count/counter[id] for id in range(len(CLASSES))}\ndel(counter)\ndel(dataset)\ndel(batch)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sns\nfrom matplotlib import pyplot\n\npyplot.figure(figsize=(20,10))\nsns.barplot(x=CLASSES, y=[weight_per_class[w] for w in range(len(CLASSES))],\n           palette=pyplot.cm.YlGnBu(\n               pyplot.Normalize()(\n                   np.array([weight_per_class[w] for w in range(len(CLASSES))])\n               )))\npyplot.xticks(rotation=60, horizontalalignment='right', size='small')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ds_train = get_training_dataset()\nds_val = get_validation_dataset()\nds_test = get_test_dataset()\n\nprint(f\"\"\"\nTraining data: {ds_train}\nValidation data: {ds_val}\nTest data: {ds_test}\n\"\"\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Training data shapes:\")\nfor image, label in ds_train.take(3):\n    print(image.numpy().shape, label.numpy().shape)\nprint(f\"Training data labels: \\n{label.numpy()}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Test data shapes:\")\nfor image, idnum in ds_test.take(3):\n    print(image.numpy().shape, idnum.numpy().shape)\nprint(f\"Test data labels: \\n{idnum.numpy().astype('U')}\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from matplotlib import pyplot\n\n# image, label = next(ds_train.take(1).as_numpy_iterator())\n# pyplot.figure(figsize=(20,20))\n# for i in range(16):\n#     pyplot.subplot(10,10,i+1)\n#     pyplot.imshow(image[i,:,:,:])\n#     pyplot.axis(\"off\")\n#     pyplot.title(CLASSES[label[i]])\nfrom matplotlib import pyplot\n\nimage, label = next(ds_train.take(1).as_numpy_iterator())\npyplot.figure(figsize=(20,20))\nfor i in range(16):\n    pyplot.subplot(4,4,i+1)\n    pyplot.imshow(image[i,:,:,:])\n    pyplot.axis(\"off\")\n    pyplot.title(CLASSES[label[i]])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with strategy.scope():\n    inputs = keras.Input(shape=[*IMAGE_SIZE, 3])\n    model = keras.applications.EfficientNetB7(\n        include_top=False, weights='imagenet', input_tensor=inputs, pooling='max',\n        input_shape=[*IMAGE_SIZE,3], classes=len(CLASSES))\n    model.trainable = False\n#     features = keras.layers.GlobalAveragePooling2D()(model.output)\n    features = keras.layers.BatchNormalization()(model.output)\n#     hidden = keras.layers.Dropout(0.5)(features)\n#     hidden = keras.layers.Dense(4096, activation='relu')(hidden)\n#     hidden = keras.layers.Dropout(0.4)(hidden)\n#     hidden = keras.layers.Dense(4096, activation='relu')(hidden)\n    hidden = keras.layers.Dropout(0.2)(features)\n    hidden = keras.layers.Dense(1024, activation='relu')(hidden)\n    hidden = keras.layers.Dropout(0.2)(hidden)\n    hidden = keras.layers.Dense(1024, activation='relu')(hidden)\n    outputs = keras.layers.Dense(len(CLASSES), activation=\"softmax\")(hidden)\n    model = keras.Model(inputs=inputs, outputs=outputs)\n# print(model.summary())\n# keras.utils.plot_model(model)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for layer in model.layers[-20:]:\n    if not isinstance(layer, keras.layers.BatchNormalization):\n        layer.trainable = True\n\nmodel.compile(\n    optimizer='adamax',\n    loss='sparse_categorical_crossentropy',\n    metrics=['sparse_categorical_accuracy']\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 96\nSTEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE\n\nlocal_save_options = tf.saved_model.SaveOptions(experimental_io_device='/job:localhost')\nhistory = model.fit(\n    ds_train,\n    validation_data=ds_val,\n    epochs=EPOCHS,\n    steps_per_epoch=STEPS_PER_EPOCH,\n    callbacks=[keras.callbacks.ModelCheckpoint(\n        \"weights.{epoch:02d}-{val_sparse_categorical_accuracy:.2f}.keras\",\n        monitor=\"val_sparse_categorical_accuracy\",\n        save_best_only=True\n#         options=local_save_options\n    )\n    ]\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pyplot.figure(figsize=(16,8))\npyplot.plot(history.history[\"loss\"])\npyplot.plot(history.history[\"val_loss\"])\npyplot.title(\"Loss vs. Validation Loss\")\npyplot.xlabel(\"Epoch\")\npyplot.legend([\"train\", \"valid.\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pyplot.figure(figsize=(16,8))\npyplot.plot(history.history[\"sparse_categorical_accuracy\"])\npyplot.plot(history.history[\"val_sparse_categorical_accuracy\"])\npyplot.title(\"Accuracy vs. Validation Accuracy\")\npyplot.xlabel(\"Epoch\")\npyplot.legend([\"train\", \"valid.\"])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Validation work\n\nAt this point it would be helpful to display a confusion matrix, compute F1-scores, precision, recall, and to look at examples of validation images and how they get classified. We'll add that later.","metadata":{}},{"cell_type":"markdown","source":"# Test predictions and submission\n\nTime to make predictions on our data set and prepare a submission!","metadata":{}},{"cell_type":"code","source":"ds_test = get_test_dataset(ordered=True)\nds_test_images = ds_test.map(lambda image,idnum: image)\nds_test_idnums = ds_test.map(lambda image,idnum: idnum)\n\nprint(\"Computing predictions...\")\nprobabilities = model.predict(ds_test_images)\npredictions = np.argmax(probabilities, axis=-1)\nprint(predictions)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.DataFrame({\n    'id': next(iter(ds_test_idnums.unbatch().batch(NUM_TEST_IMAGES))).numpy().astype(\"U\"),\n    'label': predictions\n}).set_index('id')\nsubmission","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}