{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":21154,"databundleVersionId":1243559}],"dockerImageVersionId":31328,"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","trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:38:33.39221Z","iopub.execute_input":"2026-04-27T22:38:33.392653Z","iopub.status.idle":"2026-04-27T22:38:34.766459Z","shell.execute_reply.started":"2026-04-27T22:38:33.392515Z","shell.execute_reply":"2026-04-27T22:38:34.765538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Connect to TPU if available, otherwise use whatever device Kaggle gives us (GPU/CPU)\nimport tensorflow as tf\nimport numpy as np\n\ntry:\n    resolver = tf.distribute.cluster_resolver.TPUClusterResolver()\n    tf.config.experimental_connect_to_cluster(resolver)\n    tf.tpu.experimental.initialize_tpu_system(resolver)\n    strategy = tf.distribute.TPUStrategy(resolver)\nexcept:\n    strategy = tf.distribute.get_strategy()\n\nprint(\"Replicas:\", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:38:51.353081Z","iopub.execute_input":"2026-04-27T22:38:51.35399Z","iopub.status.idle":"2026-04-27T22:39:18.231496Z","shell.execute_reply.started":"2026-04-27T22:38:51.353946Z","shell.execute_reply":"2026-04-27T22:39:18.23071Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kaggle stores the data on Google Cloud Storage; this gets the path to it\nfrom kaggle_datasets import KaggleDatasets\nGCS_PATH = KaggleDatasets().get_gcs_path('competitions/tpu-getting-started') + '/tfrecords-jpeg-224x224'\n\nTRAIN_FILES = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVAL_FILES   = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILES  = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:40:01.451676Z","iopub.execute_input":"2026-04-27T22:40:01.452372Z","iopub.status.idle":"2026-04-27T22:40:02.224865Z","shell.execute_reply.started":"2026-04-27T22:40:01.45233Z","shell.execute_reply":"2026-04-27T22:40:02.223709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 104 flower class names (index = label number)\nCLASSES = ['pink primrose', 'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea', 'wild geranium', 'tiger lily', 'moon orchid', 'bird of paradise', 'monkshood', 'globe thistle', 'snapdragon', \"colt's foot\", 'king protea', 'spear thistle', 'yellow iris', 'globe-flower', 'purple coneflower', 'peruvian lily', 'balloon flower', 'giant white arum lily', 'fire lily', 'pincushion flower', 'fritillary', 'red ginger', 'grape hyacinth', 'corn poppy', 'prince of wales feathers', 'stemless gentian', 'artichoke', 'sweet william', 'carnation', 'garden phlox', 'love in the mist', 'cosmos', 'alpine sea holly', 'ruby-lipped cattleya', 'cape flower', 'great masterwort', 'siam tulip', 'lenten rose', 'barberton daisy', 'daffodil', 'sword lily', 'poinsettia', 'bolero deep blue', 'wallflower', 'marigold', 'buttercup', 'daisy', 'common dandelion', 'petunia', 'wild pansy', 'primula', 'sunflower', 'lilac hibiscus', 'bishop of llandaff', 'gaura', 'geranium', 'orange dahlia', 'pink-yellow dahlia', 'cautleya spicata', 'japanese anemone', 'black-eyed susan', 'silverbush', 'californian poppy', 'osteospermum', 'spring crocus', 'iris', 'windflower', 'tree poppy', 'gazania', 'azalea', 'water lily', 'rose', 'thorn apple', 'morning glory', 'passion flower', 'lotus', 'toad lily', 'anthurium', 'frangipani', 'clematis', 'hibiscus', 'columbine', 'desert-rose', 'tree mallow', 'magnolia', 'cyclamen ', 'watercress', 'canna lily', 'hippeastrum ', 'bee balm', 'pink quill', 'foxglove', 'bougainvillea', 'camellia', 'mallow', 'mexican petunia', 'bromelia', 'blanket flower', 'trumpet creeper', 'blackberry lily', 'common tulip', 'wild rose']\n\n# Turn raw JPEG bytes into a normalized image tensor\ndef decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0  # scale pixel values to 0-1\n    return tf.reshape(image, [224, 224, 3])\n\n# Read a training/validation record (image + label)\ndef read_labeled(example):\n    features = {'image': tf.io.FixedLenFeature([], tf.string), 'class': tf.io.FixedLenFeature([], tf.int64)}\n    x = tf.io.parse_single_example(example, features)\n    return decode_image(x['image']), tf.cast(x['class'], tf.int32)\n\n# Read a test record (image + id, no label)\ndef read_unlabeled(example):\n    features = {'image': tf.io.FixedLenFeature([], tf.string), 'id': tf.io.FixedLenFeature([], tf.string)}\n    x = tf.io.parse_single_example(example, features)\n    return decode_image(x['image']), x['id']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:40:13.539142Z","iopub.execute_input":"2026-04-27T22:40:13.539566Z","iopub.status.idle":"2026-04-27T22:40:13.549903Z","shell.execute_reply.started":"2026-04-27T22:40:13.539531Z","shell.execute_reply":"2026-04-27T22:40:13.549048Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\n# Simple augmentation: random flips help the model generalize\ndef augment(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    return image, label\n\n# Training set: shuffled, augmented, repeats forever\ntrain_ds = (tf.data.TFRecordDataset(TRAIN_FILES).map(read_labeled).map(augment)\n            .repeat().shuffle(2048).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE))\n\n# Validation/test sets: no shuffle, no augmentation\nval_ds  = tf.data.TFRecordDataset(VAL_FILES).map(read_labeled).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\ntest_ds = tf.data.TFRecordDataset(TEST_FILES).map(read_unlabeled).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)\n\n# Count training images (filenames are like '00-224x224-798.tfrec' where 798 = number of records)\nimport re\nNUM_TRAINING_IMAGES = sum(int(re.search(r'-([0-9]+)\\.', f).group(1)) for f in TRAIN_FILES)\nprint(NUM_TRAINING_IMAGES, 'training images')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:40:22.335151Z","iopub.execute_input":"2026-04-27T22:40:22.335896Z","iopub.status.idle":"2026-04-27T22:40:22.637462Z","shell.execute_reply.started":"2026-04-27T22:40:22.335857Z","shell.execute_reply":"2026-04-27T22:40:22.636533Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Grab one batch and show 16 images with their flower names\nimages, labels = next(iter(train_ds))\nplt.figure(figsize=(16, 16))\nfor i in range(16):\n    plt.subplot(4, 4, i+1)\n    plt.imshow(images[i])\n    plt.title(CLASSES[labels[i]])\n    plt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:41:20.064863Z","iopub.execute_input":"2026-04-27T22:41:20.065317Z","iopub.status.idle":"2026-04-27T22:41:25.128545Z","shell.execute_reply.started":"2026-04-27T22:41:20.065276Z","shell.execute_reply":"2026-04-27T22:41:25.127177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# MobileNetV2 pretrained on ImageNet; we freeze it and just train a small head on top\nfrom tensorflow import keras\n\nwith strategy.scope():\n    base = keras.applications.MobileNetV2(weights='imagenet', include_top=False, input_shape=[224, 224, 3])\n    base.trainable = False  # don't update the pretrained weights\n    model = keras.Sequential([\n        base,\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dense(1024, activation='relu'),\n        keras.layers.Dropout(0.5),  # randomly drops half the neurons during training (helps prevent overfitti","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:55:39.996459Z","iopub.status.idle":"2026-04-27T22:55:39.996732Z","shell.execute_reply.started":"2026-04-27T22:55:39.9966Z","shell.execute_reply":"2026-04-27T22:55:39.996617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 2\nSTEPS_PER_EPOCH = 50\n\nhistory = model.fit(train_ds, validation_data=val_ds, epochs=EPOCHS, steps_per_epoch=STEPS_PER_EPOCH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T22:55:57.763494Z","iopub.execute_input":"2026-04-27T22:55:57.763941Z","iopub.status.idle":"2026-04-27T23:29:44.902713Z","shell.execute_reply.started":"2026-04-27T22:55:57.763903Z","shell.execute_reply":"2026-04-27T23:29:44.901912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compare training loss vs validation loss across epochs\nplt.figure(figsize=(12, 5))\nplt.plot(history.history['loss'], label='train')\nplt.plot(history.history['val_loss'], label='val')\nplt.xlabel('Epoch')\nplt.title('Loss vs Validation Loss')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-27T23:43:37.130699Z","iopub.execute_input":"2026-04-27T23:43:37.131606Z","iopub.status.idle":"2026-04-27T23:43:37.383461Z","shell.execute_reply.started":"2026-04-27T23:43:37.131567Z","shell.execute_reply":"2026-04-27T23:43:37.382663Z"}},"outputs":[],"execution_count":null}]}