{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.15","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":"markdown","source":"**[Intro to Deep Learning Home Page](https://www.kaggle.com/learn/intro-to-deep-learning)**\n\n---\n","metadata":{}},{"cell_type":"markdown","source":"# Introduction\n\nLarge deep learning models can take a long time to train.  To accelerate training, you can use \"hardware accelerators\" like GPUs (Graphics Processing Units) and [**TPUs**](https://www.kaggle.com/docs/tpu) (Tensor Processing Units). \n\n<center>\n<img src=\"https://i.imgur.com/ZLJBzWp.png\" width=30%><br/>\n</center>\n\nOn Kaggle, you can train deep learning models with GPUs and TPUs for free!\n- You can use 30 hours per week (sometimes more) of GPUs and up to 9 hours at a time in a single session.\n- You can use 30 hours per week of TPUs and up to 3 hours at a time in a single session.\n\nIn this tutorial, you'll use a TPU to train a deep learning model and submit the results to the [**Petals to the Metal**](https://www.kaggle.com/c/tpu-getting-started) competition.  \n\nWe won't dive into the details, and you should not understand all of the code that you see here.  At the end of this tutorial, we recommend additional resources that you can use to learn more.\n\n> To follow along, create you own editable copy of this notebook by [**clicking here**](https://www.kaggle.com/kernels/fork/10187222).\n\n\n# Step 1: Connect to the TPU\n\nWe begin by importing several Python packages and connecting to the TPU.","metadata":{"papermill":{"duration":0.014434,"end_time":"2020-09-10T21:12:09.281352","exception":false,"start_time":"2020-09-10T21:12:09.266918","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import math, re, os\nimport tensorflow as tf\nimport numpy as np\nfrom matplotlib import pyplot as plt\nfrom kaggle_datasets import KaggleDatasets\nfrom sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\nprint(\"Tensorflow version \" + tf.__version__)\nAUTO = tf.data.experimental.AUTOTUNE\n\n# Detect TPU, return appropriate distribution strategy\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver() \n    print('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)\nelse:\n    strategy = tf.distribute.get_strategy() \n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"execution":{"iopub.status.busy":"2024-10-19T17:55:43.418636Z","iopub.execute_input":"2024-10-19T17:55:43.419279Z","iopub.status.idle":"2024-10-19T17:56:09.138188Z","shell.execute_reply.started":"2024-10-19T17:55:43.419241Z","shell.execute_reply":"2024-10-19T17:56:09.137088Z"},"papermill":{"duration":10.966688,"end_time":"2020-09-10T21:12:20.267087","exception":false,"start_time":"2020-09-10T21:12:09.300399","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"If you see `Running on TPU` in the output above, that means that your Kaggle notebook has successfully connected to the TPU.\n\n# Step 2: Access and configure the data\n\nThe next code cell will let the TPU access the competition data.  (Before running this cell, you have to attach the [**competition dataset**](https://www.kaggle.com/c/tpu-getting-started) to the notebook, and we have already done this for you.)\n\nIt also defines several settings that we'll need to use when working with the data (like the image size and filenames).","metadata":{"papermill":{"duration":0.01365,"end_time":"2020-09-10T21:12:20.295034","exception":false,"start_time":"2020-09-10T21:12:20.281384","status":"completed"},"tags":[]}},{"cell_type":"code","source":"IMAGE_SIZE = [512, 512]                   \nEPOCHS = 12\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\n\nGCS_DS_PATH = KaggleDatasets().get_gcs_path()\nGCS_PATH = GCS_DS_PATH + '/tfrecords-jpeg-512x512'\n\nTRAINING_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')\nVALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')\nTEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec') \n\nCLASSES = ['pink primrose',    'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea',     'wild geranium',     'tiger lily',           'moon orchid',              'bird of paradise', 'monkshood',        'globe thistle',         # 00 - 09\n           'snapdragon',       \"colt's foot\",               'king protea',      'spear thistle', 'yellow iris',       'globe-flower',         'purple coneflower',        'peruvian lily',    'balloon flower',   'giant white arum lily', # 10 - 19\n           'fire lily',        'pincushion flower',         'fritillary',       'red ginger',    'grape hyacinth',    'corn poppy',           'prince of wales feathers', 'stemless gentian', 'artichoke',        'sweet william',         # 20 - 29\n           'carnation',        'garden phlox',              'love in the mist', 'cosmos',        'alpine sea holly',  'ruby-lipped cattleya', 'cape flower',              'great masterwort', 'siam tulip',       'lenten rose',           # 30 - 39\n           'barberton daisy',  'daffodil',                  'sword lily',       'poinsettia',    'bolero deep blue',  'wallflower',           'marigold',                 'buttercup',        'daisy',            'common dandelion',      # 40 - 49\n           'petunia',          'wild pansy',                'primula',          'sunflower',     'lilac hibiscus',    'bishop of llandaff',   'gaura',                    'geranium',         'orange dahlia',    'pink-yellow dahlia',    # 50 - 59\n           'cautleya spicata', 'japanese anemone',          'black-eyed susan', 'silverbush',    'californian poppy', 'osteospermum',         'spring crocus',            'iris',             'windflower',       'tree poppy',            # 60 - 69\n           'gazania',          'azalea',                    'water lily',       'rose',          'thorn apple',       'morning glory',        'passion flower',           'lotus',            'toad lily',        'anthurium',             # 70 - 79\n           'frangipani',       'clematis',                  'hibiscus',         'columbine',     'desert-rose',       'tree mallow',          'magnolia',                 'cyclamen ',        'watercress',       'canna lily',            # 80 - 89\n           'hippeastrum ',     'bee balm',                  'pink quill',       'foxglove',      'bougainvillea',     'camellia',             'mallow',                   'mexican petunia',  'bromelia',         'blanket flower',        # 90 - 99\n           'trumpet creeper',  'blackberry lily',           'common tulip',     'wild rose']                                                                                                                                               # 100 - 102\n\ndef count_data_items(filenames):\n    # the number of data items is written in the name of the .tfrec files, i.e. flowers00-230.tfrec = 230 data items\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)\nprint('{} training images, {} validation images, {} unlabeled test images'.format(NUM_TRAINING_IMAGES, NUM_VALIDATION_IMAGES, NUM_TEST_IMAGES))","metadata":{"execution":{"iopub.status.busy":"2024-10-19T17:56:09.139930Z","iopub.execute_input":"2024-10-19T17:56:09.140310Z","iopub.status.idle":"2024-10-19T17:56:09.206727Z","shell.execute_reply.started":"2024-10-19T17:56:09.140281Z","shell.execute_reply":"2024-10-19T17:56:09.205406Z"},"papermill":{"duration":0.643619,"end_time":"2020-09-10T21:12:20.95253","exception":false,"start_time":"2020-09-10T21:12:20.308911","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 3: Define a model\n\nWe define a model architecture.","metadata":{"papermill":{"duration":0.014113,"end_time":"2020-09-10T21:12:20.981087","exception":false,"start_time":"2020-09-10T21:12:20.966974","status":"completed"},"tags":[]}},{"cell_type":"code","source":"with strategy.scope():\n    pretrained_model = tf.keras.applications.VGG16(weights='imagenet', include_top=False ,input_shape=[*IMAGE_SIZE, 3])\n    pretrained_model.trainable = False # False = transfer learning, True = fine-tuning\n    \n    model = tf.keras.Sequential([\n        pretrained_model,\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(len(CLASSES), activation='softmax')\n    ])\n        \n    model.compile(\n        optimizer='adam',\n        loss = 'sparse_categorical_crossentropy',\n        metrics=['sparse_categorical_accuracy']\n    )\n    \n    model.summary()","metadata":{"execution":{"iopub.status.busy":"2024-10-19T18:17:46.917760Z","iopub.execute_input":"2024-10-19T18:17:46.918136Z","iopub.status.idle":"2024-10-19T18:17:48.092496Z","shell.execute_reply.started":"2024-10-19T18:17:46.918106Z","shell.execute_reply":"2024-10-19T18:17:48.091488Z"},"papermill":{"duration":3.320477,"end_time":"2020-09-10T21:12:24.31591","exception":false,"start_time":"2020-09-10T21:12:20.995433","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 4: Train the model\n\nThere are several helper functions that we'll need to use to train a model.  These are defined in the hidden code cell below.","metadata":{"papermill":{"duration":0.01878,"end_time":"2020-09-10T21:12:24.355136","exception":false,"start_time":"2020-09-10T21:12:24.336356","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def decode_image(image_data):\n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0  # convert image to floats in [0, 1] range\n    image = tf.reshape(image, [*IMAGE_SIZE, 3]) # explicit size needed for TPU\n    return image\n\ndef read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"class\": tf.io.FixedLenFeature([], tf.int64),  # shape [] means single element\n    }\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    label = tf.cast(example['class'], tf.int32)\n    return image, label # returns a dataset of (image, label) pairs\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"id\": tf.io.FixedLenFeature([], tf.string),  # shape [] means single element\n        # class is missing, this competitions's challenge is to predict flower classes for the test dataset\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image, idnum # returns a dataset of image(s)\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    # Read from TFRecords. For optimal performance, reading from multiple files at once and\n    # disregarding data order. Order does not matter since we will be shuffling the data anyway.\n\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=AUTO) # automatically interleaves reads from multiple files\n    dataset = dataset.with_options(ignore_order) # uses data as soon as it streams in, rather than in its original order\n    dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    # returns a dataset of (image, label) pairs if labeled=True or (image, id) pairs if labeled=False\n    return dataset\n\ndef data_augment(image, label):\n    # data augmentation. Thanks to the dataset.prefetch(AUTO) statement in the next function (below),\n    # this happens essentially for free on TPU. Data pipeline code is executed on the \"CPU\" part\n    # of the TPU while the TPU itself is computing gradients.\n    image = tf.image.random_flip_left_right(image)\n    #image = tf.image.random_saturation(image, 0, 2)\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=AUTO)\n    dataset = dataset.repeat() # the training dataset must repeat for several epochs\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)\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(AUTO) # prefetch next batch while training (autotune prefetch buffer size)\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(AUTO) # prefetch next batch while training (autotune prefetch buffer size)\n    return dataset","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-10-19T17:56:13.285837Z","iopub.execute_input":"2024-10-19T17:56:13.286195Z","iopub.status.idle":"2024-10-19T17:56:13.297450Z","shell.execute_reply.started":"2024-10-19T17:56:13.286169Z","shell.execute_reply":"2024-10-19T17:56:13.296583Z"},"papermill":{"duration":0.050957,"end_time":"2020-09-10T21:12:24.426255","exception":false,"start_time":"2020-09-10T21:12:24.375298","status":"completed"},"tags":[],"trusted":true,"jupyter":{"source_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The next code cell trains the model.","metadata":{"papermill":{"duration":0.018429,"end_time":"2020-09-10T21:12:24.463547","exception":false,"start_time":"2020-09-10T21:12:24.445118","status":"completed"},"tags":[]}},{"cell_type":"code","source":"STEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE\nhistory = model.fit(get_training_dataset(), steps_per_epoch=STEPS_PER_EPOCH, epochs=EPOCHS, validation_data=get_validation_dataset())","metadata":{"execution":{"iopub.status.busy":"2024-10-19T18:17:52.985181Z","iopub.execute_input":"2024-10-19T18:17:52.985526Z","iopub.status.idle":"2024-10-19T18:30:36.663009Z","shell.execute_reply.started":"2024-10-19T18:17:52.985495Z","shell.execute_reply":"2024-10-19T18:30:36.661915Z"},"papermill":{"duration":224.874389,"end_time":"2020-09-10T21:16:09.356669","exception":false,"start_time":"2020-09-10T21:12:24.48228","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We can see that accuracy (`sparse_categorical_accuracy`) increases with training epoch, while the loss decreases over time: which is what we want!\n\n# Step 5: Visualization\n\nNext, we'll visualize how the model performs with some images from the validation set.\n\nWe begin by defining a (hidden) code cell with some helper functions.","metadata":{"papermill":{"duration":0.512308,"end_time":"2020-09-10T21:16:10.38032","exception":false,"start_time":"2020-09-10T21:16:09.868012","status":"completed"},"tags":[]}},{"cell_type":"code","source":"def batch_to_numpy_images_and_labels(data):\n    images, labels = data\n    numpy_images = images.numpy()\n    numpy_labels = labels.numpy()\n    if numpy_labels.dtype == object: # binary string in this case, these are image ID strings\n        numpy_labels = [None for _ in enumerate(numpy_images)]\n    # If no labels, only image IDs, return None for labels (this is the case for test data)\n    return numpy_images, numpy_labels\n\ndef title_from_label_and_target(label, correct_label):\n    if correct_label is None:\n        return CLASSES[label], True\n    correct = (label == correct_label)\n    return \"{} [{}{}{}]\".format(CLASSES[label], 'OK' if correct else 'NO', u\"\\u2192\" if not correct else '',\n                                CLASSES[correct_label] if not correct else ''), correct\n\ndef display_one_flower(image, title, subplot, red=False, titlesize=16):\n    plt.subplot(*subplot)\n    plt.axis('off')\n    plt.imshow(image)\n    if len(title) > 0:\n        plt.title(title, fontsize=int(titlesize) if not red else int(titlesize/1.2), color='red' if red else 'black', fontdict={'verticalalignment':'center'}, pad=int(titlesize/1.5))\n    return (subplot[0], subplot[1], subplot[2]+1)\n\ndef display_batch_of_images(databatch, predictions=None):\n    \"\"\"This will work with:\n    display_batch_of_images(images)\n    display_batch_of_images(images, predictions)\n    display_batch_of_images((images, labels))\n    display_batch_of_images((images, labels), predictions)\n    \"\"\"\n    # data\n    images, labels = batch_to_numpy_images_and_labels(databatch)\n    if labels is None:\n        labels = [None for _ in enumerate(images)]\n        \n    # auto-squaring: this will drop data that does not fit into square or square-ish rectangle\n    rows = int(math.sqrt(len(images)))\n    cols = len(images)//rows\n        \n    # size and spacing\n    FIGSIZE = 13.0\n    SPACING = 0.1\n    subplot=(rows,cols,1)\n    if rows < cols:\n        plt.figure(figsize=(FIGSIZE,FIGSIZE/cols*rows))\n    else:\n        plt.figure(figsize=(FIGSIZE/rows*cols,FIGSIZE))\n    \n    # display\n    for i, (image, label) in enumerate(zip(images[:rows*cols], labels[:rows*cols])):\n        title = '' if label is None else CLASSES[label]\n        correct = True\n        if predictions is not None:\n            title, correct = title_from_label_and_target(predictions[i], label)\n        dynamic_titlesize = FIGSIZE*SPACING/max(rows,cols)*40+3 # magic formula tested to work from 1x1 to 10x10 images\n        subplot = display_one_flower(image, title, subplot, not correct, titlesize=dynamic_titlesize)\n    \n    #layout\n    plt.tight_layout()\n    if label is None and predictions is None:\n        plt.subplots_adjust(wspace=0, hspace=0)\n    else:\n        plt.subplots_adjust(wspace=SPACING, hspace=SPACING)\n    plt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-10-19T18:30:36.665370Z","iopub.execute_input":"2024-10-19T18:30:36.665640Z","iopub.status.idle":"2024-10-19T18:30:36.682398Z","shell.execute_reply.started":"2024-10-19T18:30:36.665612Z","shell.execute_reply":"2024-10-19T18:30:36.681630Z"},"papermill":{"duration":0.565353,"end_time":"2020-09-10T21:16:11.492137","exception":false,"start_time":"2020-09-10T21:16:10.926784","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pictures with red titles show examples where the model made a mistake.  Titles in black show correct classifications.","metadata":{"papermill":{"duration":0.503123,"end_time":"2020-09-10T21:16:12.511521","exception":false,"start_time":"2020-09-10T21:16:12.008398","status":"completed"},"tags":[]}},{"cell_type":"code","source":"dataset = get_validation_dataset()\ndataset = dataset.unbatch().batch(20)\nbatch = iter(dataset)\n\nimages, labels = next(batch)\nprobabilities = model.predict(images)\npredictions = np.argmax(probabilities, axis=-1)\ndisplay_batch_of_images((images, labels), predictions)","metadata":{"execution":{"iopub.status.busy":"2024-10-19T18:30:36.683297Z","iopub.execute_input":"2024-10-19T18:30:36.683553Z","iopub.status.idle":"2024-10-19T18:30:48.401377Z","shell.execute_reply.started":"2024-10-19T18:30:36.683526Z","shell.execute_reply":"2024-10-19T18:30:48.400576Z"},"papermill":{"duration":18.902845,"end_time":"2020-09-10T21:16:31.958003","exception":false,"start_time":"2020-09-10T21:16:13.055158","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Step 6: Make a submission\n\nIf you have not yet already, begin by creating you own editable copy of this notebook by [**clicking here**](https://www.kaggle.com/kernels/fork/10187222).  Then, submit to the competition by following these steps:\n1. Begin by clicking on the blue **Save Version** button in the top right corner of the window.  This will generate a pop-up window.  \n2. Ensure that the **Save and Run All** option is selected, and then click on the blue **Save** button.\n3. This generates a window in the bottom left corner of the notebook.  After it has finished running, click on the number to the right of the **Save Version** button.  This pulls up a list of versions on the right of the screen.  Click on the ellipsis **(...)** to the right of the most recent version, and select **Open in Viewer**.  This brings you into view mode of the same page. You will need to scroll down to get back to these instructions.\n4. Click on the **Output** tab on the right of the screen.  Then, click on the blue **Submit** button to submit your results to the leaderboard.\n\nYou have now successfully submitted to the competition!\n\nIf you want to keep working to improve your performance, select the blue **Edit** button in the top right of the screen. Then you can change your code and repeat the process. There's a lot of room to improve, and you will climb up the leaderboard as you work.\n","metadata":{"papermill":{"duration":0.531578,"end_time":"2020-09-10T21:16:33.023286","exception":false,"start_time":"2020-09-10T21:16:32.491708","status":"completed"},"tags":[]}},{"cell_type":"code","source":"test_ds = get_test_dataset(ordered=True) # since we are splitting the dataset and iterating separately on images and ids, order matters.\n\nprint('Computing predictions...')\ntest_images_ds = test_ds.map(lambda image, idnum: image)\nprobabilities = model.predict(test_images_ds)\npredictions = np.argmax(probabilities, axis=-1)\nprint(predictions)\n\nprint('Generating submission.csv file...')\ntest_ids_ds = test_ds.map(lambda image, idnum: idnum).unbatch()\ntest_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES))).numpy().astype('U') # all in one batch\nnp.savetxt('submission.csv', np.rec.fromarrays([test_ids, predictions]), fmt=['%s', '%d'], delimiter=',', header='id,label', comments='')\n!head submission.csv","metadata":{"execution":{"iopub.status.busy":"2024-10-19T18:30:48.402953Z","iopub.execute_input":"2024-10-19T18:30:48.403215Z","iopub.status.idle":"2024-10-19T18:32:33.502365Z","shell.execute_reply.started":"2024-10-19T18:30:48.403189Z","shell.execute_reply":"2024-10-19T18:32:33.500939Z"},"papermill":{"duration":28.146178,"end_time":"2020-09-10T21:17:01.702744","exception":false,"start_time":"2020-09-10T21:16:33.556566","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Learn more\n\nIn this tutorial, you saw how to leverage TPUs to quickly make a submission to a Kaggle competition.  \n\nIf you took Kaggle Learn's [**Intro to Deep Learning course**](https://www.kaggle.com/learn/intro-to-deep-learning), then some of the code should look familiar; for instance, you define a neural network architecture with `tf.keras.Sequential()`, before compiling and training the model with `model.compile()` and `model.fit()`.\n\nIf you're interested in learning about the details of the code presented here, we'll soon release a new **Computer Vision course** that will help you understand the code in **Step 3** of this notebook, by teaching you about topics like convolutional neural networks and transfer learning.  Until then, you're encouraged to check out the [**Petals to the Metal discussion forums**](https://www.kaggle.com/c/tpu-getting-started/discussion) to continue your leanring!","metadata":{"papermill":{"duration":0.530961,"end_time":"2020-09-10T21:17:02.761421","exception":false,"start_time":"2020-09-10T21:17:02.23046","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"---\n**[Intro to Deep Learning Home Page](https://www.kaggle.com/learn/intro-to-deep-learning)**\n\n\n\n\n\n*Have questions or comments? Visit the [Learn Discussion forum](https://www.kaggle.com/learn-forum) to chat with other Learners.*","metadata":{}}]}