{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Introduction","metadata":{}},{"cell_type":"markdown","source":"Notebook is part of my course on machine learning focused on Deep Learning, assignment is on Computer Vision","metadata":{}},{"cell_type":"markdown","source":"## Scope","metadata":{}},{"cell_type":"markdown","source":"Project is to demonstrate capability to implement CNN for image detection. Selected dataset is the Dogs Vs. Cats competition dataset.","metadata":{}},{"cell_type":"markdown","source":"## Goals","metadata":{}},{"cell_type":"markdown","source":"* Preprocess Data\n* Build CNN model from scratch\n* Build CNN model using transfer learning\n* Evaluate and compare results","metadata":{}},{"cell_type":"markdown","source":"###","metadata":{}},{"cell_type":"markdown","source":"# Environment Setup","metadata":{}},{"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":"2025-10-26T20:43:01.798890Z","iopub.execute_input":"2025-10-26T20:43:01.799578Z","iopub.status.idle":"2025-10-26T20:43:01.804901Z","shell.execute_reply.started":"2025-10-26T20:43:01.799554Z","shell.execute_reply":"2025-10-26T20:43:01.804319Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nimport zipfile\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.models import Sequential\nfrom functools import partial\nfrom tensorflow.keras import Input\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.optimizers import Adam\n\nplt.rc('font', size=14)\nplt.rc('axes', labelsize=14, titlesize=14)\nplt.rc('legend', fontsize=14)\nplt.rc('xtick', labelsize=10)\nplt.rc('ytick', labelsize=10)\n\ntf.random.set_seed(72)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:42:37.526569Z","iopub.execute_input":"2025-10-26T20:42:37.527350Z","iopub.status.idle":"2025-10-26T20:42:50.669377Z","shell.execute_reply.started":"2025-10-26T20:42:37.527322Z","shell.execute_reply":"2025-10-26T20:42:50.668741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Nothing particularly of note here, trying to get better about having all of my imports lumped together. Still a work in progress.","metadata":{}},{"cell_type":"markdown","source":"# Importing Data","metadata":{}},{"cell_type":"markdown","source":"We covered accessing zipped files in lecture, but looking through other submissions I found the zipfile and the command line methods. Liked the zipfile method best, so keeping that and commenting out the alternative option.","metadata":{}},{"cell_type":"code","source":"zip_dir = '/kaggle/input/dogs-vs-cats'\nwith zipfile.ZipFile(os.path.join(zip_dir, 'train.zip'), 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/train')\n\nwith zipfile.ZipFile(os.path.join(zip_dir, 'test1.zip'), 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/test')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:43:12.811426Z","iopub.execute_input":"2025-10-26T20:43:12.811981Z","iopub.status.idle":"2025-10-26T20:43:24.262831Z","shell.execute_reply.started":"2025-10-26T20:43:12.811956Z","shell.execute_reply":"2025-10-26T20:43:24.262258Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#!unzip -q /kaggle/input/dogs-vs-cats/train.zip -d /kaggle/working/train\n#!unzip -q /kaggle/input/dogs-vs-cats/test1.zip -d /kaggle/working/test","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T03:29:42.554809Z","iopub.execute_input":"2025-10-26T03:29:42.555164Z","iopub.status.idle":"2025-10-26T03:31:51.331930Z","shell.execute_reply.started":"2025-10-26T03:29:42.555140Z","shell.execute_reply":"2025-10-26T03:31:51.330622Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_path = '/kaggle/working/train/train'\ntest_path = '/kaggle/working/test/test1'\n\nprint(\"Number of training images:\", len(os.listdir(train_path)))\nprint(\"Number of test images:\", len(os.listdir(test_path)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:43:31.051040Z","iopub.execute_input":"2025-10-26T20:43:31.051331Z","iopub.status.idle":"2025-10-26T20:43:31.073636Z","shell.execute_reply.started":"2025-10-26T20:43:31.051310Z","shell.execute_reply":"2025-10-26T20:43:31.073072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import shutil\n\ntrain_path = '/kaggle/working/train/train'\nsorted_train_path = '/kaggle/working/train/sorted_train'\n\nos.makedirs(os.path.join(sorted_train_path, 'dogs'), exist_ok=True)\nos.makedirs(os.path.join(sorted_train_path, 'cats'), exist_ok=True)\n\nfor filename in os.listdir(train_path):\n    if filename.startswith('dog'):\n        shutil.move(os.path.join(train_path, filename), os.path.join(sorted_train_path, 'dogs', filename))\n    elif filename.startswith('cat'):\n        shutil.move(os.path.join(train_path, filename), os.path.join(sorted_train_path, 'cats', filename))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Exploration","metadata":{}},{"cell_type":"markdown","source":"## Feature Engineering","metadata":{}},{"cell_type":"markdown","source":"Image dataset has significant dimension variance, need to standardize the image sizes","metadata":{}},{"cell_type":"code","source":"img_size = (224, 224)  \n\ndatagen = ImageDataGenerator(\n    rescale=1./255,       \n    validation_split=0.2  \n)\n\ntrain_gen = datagen.flow_from_directory(\n    '/kaggle/working/train/sorted_train',\n    target_size=img_size,     \n    batch_size=32,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_directory(\n    '/kaggle/working/train/sorted_train',\n    target_size=img_size,\n    batch_size=32,\n    class_mode='binary',\n    subset='validation'\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:43:40.259232Z","iopub.execute_input":"2025-10-26T20:43:40.259919Z","iopub.status.idle":"2025-10-26T20:43:40.530597Z","shell.execute_reply.started":"2025-10-26T20:43:40.259894Z","shell.execute_reply":"2025-10-26T20:43:40.529881Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images_batch, labels_batch = next(train_gen)\n\nimg = images_batch[0]\nimg2 = images_batch[1]\nlabel = labels_batch[0]\nlabel2 = labels_batch[1]\n\n# Plot\nplt.imshow(img)  \nplt.title('Dog' if label == 1 else 'Cat')\nplt.axis('off')\nplt.show()\n\nplt.imshow(img2)\nplt.title('Dog' if label2 == 1 else 'Cat')\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:52:38.752593Z","iopub.execute_input":"2025-10-26T20:52:38.753300Z","iopub.status.idle":"2025-10-26T20:52:39.050084Z","shell.execute_reply.started":"2025-10-26T20:52:38.753274Z","shell.execute_reply":"2025-10-26T20:52:39.049374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_datagen = ImageDataGenerator(rescale=1./255)\n\ntest_gen = test_datagen.flow_from_directory(\n    '/kaggle/working/test',  \n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=None,  \n    shuffle=False     \n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:17:39.323318Z","iopub.execute_input":"2025-10-26T21:17:39.323936Z","iopub.status.idle":"2025-10-26T21:17:39.418881Z","shell.execute_reply.started":"2025-10-26T21:17:39.323911Z","shell.execute_reply":"2025-10-26T21:17:39.418142Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Building","metadata":{}},{"cell_type":"markdown","source":"Need to build two models for this assignment, first one is going to be from scratch, using the code from our textbook as a starting point.","metadata":{}},{"cell_type":"markdown","source":"## Model 1: Built from Scratch","metadata":{}},{"cell_type":"code","source":"DefaultConv2D = partial(tf.keras.layers.Conv2D, kernel_size=3, padding=\"same\",\n                        activation=\"relu\", kernel_initializer=\"he_normal\")\n\nmodel_1 = tf.keras.Sequential([\n    Input(shape=(224, 224, 3)),\n    DefaultConv2D(filters=64, kernel_size=7,),\n    tf.keras.layers.MaxPool2D(),\n    DefaultConv2D(filters=128),\n    DefaultConv2D(filters=128),\n    tf.keras.layers.MaxPool2D(),\n    DefaultConv2D(filters=256),\n    DefaultConv2D(filters=256),\n    tf.keras.layers.MaxPool2D(),\n    tf.keras.layers.Flatten(),\n    tf.keras.layers.Dense(units=128, activation=\"relu\",\n                          kernel_initializer=\"he_normal\"),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(units=64, activation=\"relu\",\n                          kernel_initializer=\"he_normal\"),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(units=1, activation=\"sigmoid\")\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:29:18.617829Z","iopub.execute_input":"2025-10-26T21:29:18.618173Z","iopub.status.idle":"2025-10-26T21:29:18.688858Z","shell.execute_reply.started":"2025-10-26T21:29:18.618148Z","shell.execute_reply":"2025-10-26T21:29:18.688333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_1.compile(\n    optimizer=Adam(learning_rate=1e-4),\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:29:21.989442Z","iopub.execute_input":"2025-10-26T21:29:21.990074Z","iopub.status.idle":"2025-10-26T21:29:21.998444Z","shell.execute_reply.started":"2025-10-26T21:29:21.990050Z","shell.execute_reply":"2025-10-26T21:29:21.997687Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model 2: Transfer Learning","metadata":{}},{"cell_type":"markdown","source":"Second model is going to be using the MobileNetV2 as it is small, lightweight, and from my research well suited for my task","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import MobileNetV2\n\nbase_model = MobileNetV2(\n    weights='imagenet',        \n    include_top=False,         \n    input_shape=(224, 224, 3) \n)\n\nbase_model.trainable = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:44:07.494319Z","iopub.execute_input":"2025-10-26T20:44:07.494655Z","iopub.status.idle":"2025-10-26T20:44:09.849601Z","shell.execute_reply.started":"2025-10-26T20:44:07.494631Z","shell.execute_reply":"2025-10-26T20:44:09.849021Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_2 = Sequential([\n    base_model,\n    GlobalAveragePooling2D(),  \n    Dense(128, activation='relu', kernel_initializer='he_normal'),\n    Dropout(0.5),\n    Dense(64, activation='relu', kernel_initializer='he_normal'),\n    Dropout(0.5),\n    Dense(1, activation='sigmoid')  \n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:44:12.298553Z","iopub.execute_input":"2025-10-26T20:44:12.298825Z","iopub.status.idle":"2025-10-26T20:44:12.336228Z","shell.execute_reply.started":"2025-10-26T20:44:12.298805Z","shell.execute_reply":"2025-10-26T20:44:12.335480Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_2.compile(\n    optimizer=Adam(learning_rate=1e-4),\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:44:15.649555Z","iopub.execute_input":"2025-10-26T20:44:15.650247Z","iopub.status.idle":"2025-10-26T20:44:15.658752Z","shell.execute_reply.started":"2025-10-26T20:44:15.650225Z","shell.execute_reply":"2025-10-26T20:44:15.658185Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"markdown","source":"First time was abysmally slow, then remembered about the GPU setting. Made training significantly shorter.","metadata":{}},{"cell_type":"markdown","source":"## Model 1 Training","metadata":{}},{"cell_type":"code","source":"history_1 = model_1.fit(\n    train_gen,               \n    validation_data=val_gen, \n    epochs=5,               \n    steps_per_epoch=train_gen.samples // train_gen.batch_size,\n    validation_steps=val_gen.samples // val_gen.batch_size)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:29:26.339194Z","iopub.execute_input":"2025-10-26T21:29:26.339733Z","iopub.status.idle":"2025-10-26T21:35:43.455780Z","shell.execute_reply.started":"2025-10-26T21:29:26.339709Z","shell.execute_reply":"2025-10-26T21:35:43.455185Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model 2 Training","metadata":{}},{"cell_type":"code","source":"history_2 = model_2.fit(\n    train_gen,               \n    validation_data=val_gen, \n    epochs=5,               \n    steps_per_epoch=train_gen.samples // train_gen.batch_size,\n    validation_steps=val_gen.samples // val_gen.batch_size)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:44:20.769975Z","iopub.execute_input":"2025-10-26T20:44:20.770643Z","iopub.status.idle":"2025-10-26T20:47:47.139319Z","shell.execute_reply.started":"2025-10-26T20:44:20.770619Z","shell.execute_reply":"2025-10-26T20:47:47.138526Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluation","metadata":{}},{"cell_type":"markdown","source":"Evaluating model performance is going to be done by looking at confusion matrixes, as well as training performance","metadata":{}},{"cell_type":"markdown","source":"## Model 1 Confusion Matrix","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport math\n\n\nval_gen.reset() \ny_pred_prob = model_1.predict(val_gen, steps=math.ceil(val_gen.samples / val_gen.batch_size))\n\n\ny_pred = (y_pred_prob > 0.5).astype(int).reshape(-1)\n\n\ny_true = val_gen.classes  \n\nval_gen = datagen.flow_from_directory(\n    '/kaggle/working/train/sorted_train',\n    target_size=(224,224),\n    batch_size=32,\n    class_mode='binary',\n    subset='validation',\n    shuffle=False   \n)\n\n\ncm = confusion_matrix(y_true, y_pred)\n\n\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=val_gen.class_indices.keys(), yticklabels=val_gen.class_indices.keys())\nplt.xlabel('Predicted')\nplt.ylabel('Actual')\nplt.title('Confusion Matrix')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:46:53.925162Z","iopub.execute_input":"2025-10-26T21:46:53.925735Z","iopub.status.idle":"2025-10-26T21:47:03.925207Z","shell.execute_reply.started":"2025-10-26T21:46:53.925711Z","shell.execute_reply":"2025-10-26T21:47:03.924559Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"~~False IDs are a little on the high side, suggests model isn't quite robust enough, I feel like I could definitely increase the filters to get a better result, but trying to keep run times realistic for training~~","metadata":{}},{"cell_type":"markdown","source":"Above comment was from less robust version of the model, increasing filters for the Conv2d layers drastically reduced false labeling.","metadata":{}},{"cell_type":"markdown","source":"## Model 2 Confusion Matrix","metadata":{}},{"cell_type":"code","source":"val_gen.reset() \ny_pred_prob = model_2.predict(val_gen, steps=math.ceil(val_gen.samples / val_gen.batch_size))\n\ny_pred = (y_pred_prob > 0.5).astype(int).reshape(-1)\n\ny_true = val_gen.classes  \n\nval_gen = datagen.flow_from_directory(\n    '/kaggle/working/train/sorted_train',\n    target_size=(224,224),\n    batch_size=32,\n    class_mode='binary',\n    subset='validation',\n    shuffle=False  \n)\n\ncm = confusion_matrix(y_true, y_pred)\n\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=val_gen.class_indices.keys(), yticklabels=val_gen.class_indices.keys())\nplt.xlabel('Predicted')\nplt.ylabel('Actual')\nplt.title('Confusion Matrix')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:05:16.513890Z","iopub.execute_input":"2025-10-26T21:05:16.514627Z","iopub.status.idle":"2025-10-26T21:05:32.482590Z","shell.execute_reply.started":"2025-10-26T21:05:16.514602Z","shell.execute_reply":"2025-10-26T21:05:32.481970Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"False labels are significantly reduced in the transfer learning model. Good sign that I my implementation is correct.","metadata":{}},{"cell_type":"markdown","source":"## Model Performance Comparison","metadata":{}},{"cell_type":"code","source":"h1 = history_1.history\nh2 = history_2.history\n\nepochs_1 = range(1, len(h1['loss']) + 1)\nepochs_2 = range(1, len(h2['loss']) + 1)\n\nplt.figure(figsize=(16, 6))\n\n# Plot Training & Validation Loss \nplt.subplot(1, 2, 1)\nplt.plot(epochs_1, h1['loss'], 'b-', label='Model 1 Training Loss')\nplt.plot(epochs_1, h1['val_loss'], 'b--', label='Model 1 Validation Loss')\nplt.plot(epochs_2, h2['loss'], 'r-', label='Model 2 Training Loss')\nplt.plot(epochs_2, h2['val_loss'], 'r--', label='Model 2 Validation Loss')\nplt.title('Training vs Validation Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\n\n# Plot Training & Validation Accuracy\nplt.subplot(1, 2, 2)\nplt.plot(epochs_1, h1['accuracy'], 'b-', label='Model 1 Training Accuracy')\nplt.plot(epochs_1, h1['val_accuracy'], 'b--', label='Model 1 Validation Accuracy')\nplt.plot(epochs_2, h2['accuracy'], 'r-', label='Model 2 Training Accuracy')\nplt.plot(epochs_2, h2['val_accuracy'], 'r--', label='Model 2 Validation Accuracy')\nplt.title('Training vs Validation Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T20:57:49.808777Z","iopub.execute_input":"2025-10-26T20:57:49.809055Z","iopub.status.idle":"2025-10-26T20:57:50.187760Z","shell.execute_reply.started":"2025-10-26T20:57:49.809037Z","shell.execute_reply":"2025-10-26T20:57:50.187046Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* Model 1 gradually improves on loss, though starts rather high.\n* Model 1 starts at around 50% accuracy, ends at a respectable 80%\n* Model 2 starts off at 20% loss and quickly drops to below 10%\n* Model 2 begins with over 90% accuracy and settles at around 98%","metadata":{}},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"markdown","source":"Based on performance, model 2 is the one I will be using to generate my submission csv","metadata":{}},{"cell_type":"code","source":"steps = int(np.ceil(test_gen.samples / test_gen.batch_size))\n\npred_probs = model_2.predict(test_gen, steps=steps)\n\n\npred_labels = (pred_probs > 0.5).astype(int).reshape(-1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:21:53.037663Z","iopub.execute_input":"2025-10-26T21:21:53.038493Z","iopub.status.idle":"2025-10-26T21:22:19.278570Z","shell.execute_reply.started":"2025-10-26T21:21:53.038466Z","shell.execute_reply":"2025-10-26T21:22:19.278005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filenames = test_gen.filenames\nresults = list(zip(filenames, pred_labels))\n\n\nfor fname, label in results[:5]:\n    print(fname, 'Dog' if label==1 else 'Cat')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:22:23.588395Z","iopub.execute_input":"2025-10-26T21:22:23.589104Z","iopub.status.idle":"2025-10-26T21:22:23.596090Z","shell.execute_reply.started":"2025-10-26T21:22:23.589079Z","shell.execute_reply":"2025-10-26T21:22:23.595409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    'id': filenames,\n    'label': pred_labels\n})\nsubmission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-26T21:26:31.889650Z","iopub.execute_input":"2025-10-26T21:26:31.890191Z","iopub.status.idle":"2025-10-26T21:26:31.908506Z","shell.execute_reply.started":"2025-10-26T21:26:31.890168Z","shell.execute_reply":"2025-10-26T21:26:31.907736Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Reflection","metadata":{}},{"cell_type":"markdown","source":"* This is the first project where using the GPU acceleration made a sizable difference, initial version of scratch model was trimmed down significantly to reduce training time.\n* Initial version had a slimmed down filter count for the scratch CNN model. Wanted to keep the epochs the same for comparison to the transfer model,\n* Future version of this would allow for greater epochs for training time, and pushing the limits further for how many filters and neurons for the dense layers would allow me to get closer to the transfer learning model","metadata":{}}]}