{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"},{"sourceId":15763408,"sourceType":"kernelVersion"}],"dockerImageVersionId":30626,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"cpJFD1tBs2RRFcqah3SV","version":1}}},"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-01-09T07:50:12.243157Z","iopub.execute_input":"2024-01-09T07:50:12.243929Z","iopub.status.idle":"2024-01-09T07:50:12.710126Z","shell.execute_reply.started":"2024-01-09T07:50:12.243887Z","shell.execute_reply":"2024-01-09T07:50:12.708860Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"xchwTNjg8C2fMHXQ3CYm","version":1},"outputId":null},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load the packages\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nkeras = tf.keras","metadata":{"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"aBjwuFwSiwxW775sSs73","version":1},"outputId":null},"execution":{"iopub.status.busy":"2024-01-10T08:03:44.691800Z","iopub.execute_input":"2024-01-10T08:03:44.692612Z","iopub.status.idle":"2024-01-10T08:03:56.746870Z","shell.execute_reply.started":"2024-01-10T08:03:44.692579Z","shell.execute_reply":"2024-01-10T08:03:56.745844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the data\nfrom sklearn.datasets import fetch_california_housing\nhouse = fetch_california_housing()\n# get the X and y\nX = house.data\ny = house.target\n# Split them into train and test data\nfrom sklearn.model_selection import train_test_split\nX_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.2,\n                                                   random_state = 42)","metadata":{"execution":{"iopub.status.busy":"2024-01-09T07:56:51.789197Z","iopub.execute_input":"2024-01-09T07:56:51.789670Z","iopub.status.idle":"2024-01-09T07:56:51.812366Z","shell.execute_reply.started":"2024-01-09T07:56:51.789633Z","shell.execute_reply":"2024-01-09T07:56:51.811310Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"TbtThuBbcJ5JgDztYeIA","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# now set up functional models\n# this is a very simple model\n\ninputs = keras.Input(shape = (8,))\nfeatures = keras.layers.Dense(16, activation = 'relu')(inputs)\noutputs = keras.layers.Dense(1)(features)\nmodel = keras.Model(inputs = inputs, outputs = outputs)","metadata":{"execution":{"iopub.status.busy":"2024-01-09T07:59:24.596448Z","iopub.execute_input":"2024-01-09T07:59:24.596910Z","iopub.status.idle":"2024-01-09T07:59:24.782459Z","shell.execute_reply.started":"2024-01-09T07:59:24.596872Z","shell.execute_reply":"2024-01-09T07:59:24.781283Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"ZXEA4rbB7hjx2qU4d41W","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# check the model summary\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-09T07:59:37.544626Z","iopub.execute_input":"2024-01-09T07:59:37.545039Z","iopub.status.idle":"2024-01-09T07:59:37.565860Z","shell.execute_reply.started":"2024-01-09T07:59:37.545004Z","shell.execute_reply":"2024-01-09T07:59:37.564440Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"qi8dM19sO1BA5hFFgBNR","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# compile and fit\nmodel.compile(loss='mse', optimizer = 'adam', metrics = ['mae'])\n# fit the model\nhist = model.fit(X_train, y_train, \n                epochs = 10,\n                batch_size = 32,\n                validation_split = 0.2\n                )","metadata":{"execution":{"iopub.status.busy":"2024-01-09T08:04:03.433739Z","iopub.execute_input":"2024-01-09T08:04:03.434178Z","iopub.status.idle":"2024-01-09T08:04:14.507237Z","shell.execute_reply.started":"2024-01-09T08:04:03.434146Z","shell.execute_reply":"2024-01-09T08:04:14.506134Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"AxnYOcRJZDukm4vZvvol","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# with that bg, we will study convnet\ninputs = keras.Input(shape = (28,28,1))\nx = keras.layers.Conv2D(filters = 32, kernel_size = (3,3), activation='relu')(inputs)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters = 64, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters = 128, kernel_size=3, activation='relu')(x)\nx = keras.layers.Flatten()(x)\noutputs = keras.layers.Dense(10, activation='softmax')(x)\ncvmod = keras.Model(inputs = inputs, outputs = outputs)","metadata":{"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"dZjT053EoKtgMO1E6hAc","version":1},"outputId":null},"execution":{"iopub.status.busy":"2024-01-10T08:01:30.075701Z","iopub.execute_input":"2024-01-10T08:01:30.075958Z","iopub.status.idle":"2024-01-10T08:01:30.408722Z","shell.execute_reply.started":"2024-01-10T08:01:30.075934Z","shell.execute_reply":"2024-01-10T08:01:30.407540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# what happens if we do not use maxpooling2d\ninputs = keras.Input(shape = (28,28,1))\nx = keras.layers.Conv2D(filters = 32, kernel_size = (3,3), activation='relu')(inputs)\n\nx = keras.layers.Conv2D(filters = 64, kernel_size=3, activation='relu')(x)\n\nx = keras.layers.Conv2D(filters = 128, kernel_size=3, activation='relu')(x)\nx = keras.layers.Flatten()(x)\noutputs = keras.layers.Dense(10, activation='softmax')(x)\ncvmod2 = keras.Model(inputs = inputs, outputs = outputs)","metadata":{"execution":{"iopub.status.busy":"2024-01-09T22:09:33.680248Z","iopub.execute_input":"2024-01-09T22:09:33.680816Z","iopub.status.idle":"2024-01-09T22:09:33.778470Z","shell.execute_reply.started":"2024-01-09T22:09:33.680781Z","shell.execute_reply":"2024-01-09T22:09:33.777022Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"CyrFZ3yle2ISTBsFlBBc","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# now see the cvmod2.summary()\ncvmod2.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-09T22:09:52.196272Z","iopub.execute_input":"2024-01-09T22:09:52.196745Z","iopub.status.idle":"2024-01-09T22:09:52.228344Z","shell.execute_reply.started":"2024-01-09T22:09:52.196710Z","shell.execute_reply":"2024-01-09T22:09:52.226860Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"lYFA4MkqJqHFnUZ7g81g","version":1},"outputId":{"project":"K1sQYw81b7A0pIsA75ni","block":"auH348ZEYsFHp23LLxCY","version":1}},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"See the number of output parameters, over 600000! It will take a long time to process and overfitting.","metadata":{"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"Tr8FZh5pz0iB3BoTSg5X","version":1}}}},{"cell_type":"code","source":"cvmod.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-09T22:10:50.068802Z","iopub.execute_input":"2024-01-09T22:10:50.069320Z","iopub.status.idle":"2024-01-09T22:10:50.113322Z","shell.execute_reply.started":"2024-01-09T22:10:50.069285Z","shell.execute_reply":"2024-01-09T22:10:50.111917Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"kYGiibNCvE6zCAdXEOCm","version":1},"outputId":{"project":"K1sQYw81b7A0pIsA75ni","block":"XtOGu4Gt2xbndTyC8lML","version":1}},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# now import the image data\nfrom tensorflow.keras.datasets import mnist","metadata":{"execution":{"iopub.status.busy":"2024-01-09T21:32:23.729069Z","iopub.execute_input":"2024-01-09T21:32:23.729684Z","iopub.status.idle":"2024-01-09T21:32:23.737379Z","shell.execute_reply.started":"2024-01-09T21:32:23.729648Z","shell.execute_reply":"2024-01-09T21:32:23.736230Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"s1shfzmziFv5M8iCV1G6","version":1},"outputId":null},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\ntrain_images = train_images.reshape((60000, 28, 28, 1))\ntrain_images = train_images.astype('float32') / 255\ntest_images = test_images.reshape((10000, 28, 28, 1))\ntest_images = test_images.astype('float32') / 255","metadata":{"execution":{"iopub.status.busy":"2024-01-09T21:32:29.713575Z","iopub.execute_input":"2024-01-09T21:32:29.714029Z","iopub.status.idle":"2024-01-09T21:32:30.519586Z","shell.execute_reply.started":"2024-01-09T21:32:29.713998Z","shell.execute_reply":"2024-01-09T21:32:30.517879Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"8l07f6KU1r2HXrjaFFjs","version":1},"outputId":{"project":"K1sQYw81b7A0pIsA75ni","block":"uhICcPEmFZB2aHb2hT8p","version":1}},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cvmod.compile(loss='sparse_categorical_crossentropy', optimizer='adam',\n             metrics = ['accuracy'])\ncvmod.fit(train_images, train_labels, batch_size = 64, \n          epochs = 5)","metadata":{"execution":{"iopub.status.busy":"2024-01-09T21:32:36.127318Z","iopub.execute_input":"2024-01-09T21:32:36.127770Z","iopub.status.idle":"2024-01-09T21:35:09.095860Z","shell.execute_reply.started":"2024-01-09T21:32:36.127739Z","shell.execute_reply":"2024-01-09T21:35:09.094281Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"DUogjouen3fi8FHKwjcJ","version":1},"outputId":{"project":"K1sQYw81b7A0pIsA75ni","block":"evTr8ztvoAF0cgKpCH22","version":1}},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loss, test_accuracy = cvmod.evaluate(test_images, test_labels)\nprint(test_accuracy)","metadata":{"execution":{"iopub.status.busy":"2024-01-09T21:37:52.524981Z","iopub.execute_input":"2024-01-09T21:37:52.525535Z","iopub.status.idle":"2024-01-09T21:37:55.455747Z","shell.execute_reply.started":"2024-01-09T21:37:52.525499Z","shell.execute_reply":"2024-01-09T21:37:55.454461Z"},"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"ngih1OuPqToBDp5fTs16","version":1},"outputId":{"project":"K1sQYw81b7A0pIsA75ni","block":"71hZrgq3cRDxtNYc0je0","version":1}},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Gives us 99.3% accuracy!!","metadata":{"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"mokgEEriax2sz4EWfeN3","version":1}}}},{"cell_type":"code","source":"# let's classify dogs or cats\n# first get the data\n!ls -al\n","metadata":{"iooxa":{"id":{"project":"K1sQYw81b7A0pIsA75ni","block":"jBCXy2g8r6NdhBGvsYlo","version":1},"outputId":null},"execution":{"iopub.status.busy":"2024-01-09T22:24:17.098416Z","iopub.execute_input":"2024-01-09T22:24:17.098887Z","iopub.status.idle":"2024-01-09T22:24:18.221123Z","shell.execute_reply.started":"2024-01-09T22:24:17.098854Z","shell.execute_reply":"2024-01-09T22:24:18.219875Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cd /kaggle/input","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:34:32.650634Z","iopub.execute_input":"2024-01-10T00:34:32.651412Z","iopub.status.idle":"2024-01-10T00:34:33.617668Z","shell.execute_reply.started":"2024-01-10T00:34:32.651363Z","shell.execute_reply":"2024-01-10T00:34:33.616540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!cp /kaggle/input/dogs-vs-cats/train.zip /kaggle/working/","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:01:49.611902Z","iopub.execute_input":"2024-01-10T08:01:49.612243Z","iopub.status.idle":"2024-01-10T08:01:55.233385Z","shell.execute_reply.started":"2024-01-10T08:01:49.612213Z","shell.execute_reply":"2024-01-10T08:01:55.232146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"These above steps need some explanation. We first identified the data sources dogs-vs-cats in the kaggle data sets. Then we clicked a plus sign on it, so that these datasets were downloaded to our /kaggle/input folder. Then we used the copy function to copy these files to our working directory for this project. So now in our working directory we have the test1.zip and train1.zip files. ","metadata":{}},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:02:01.886856Z","iopub.execute_input":"2024-01-10T08:02:01.887342Z","iopub.status.idle":"2024-01-10T08:02:02.877464Z","shell.execute_reply.started":"2024-01-10T08:02:01.887303Z","shell.execute_reply":"2024-01-10T08:02:02.876315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"See above?","metadata":{}},{"cell_type":"code","source":"!unzip -qq train.zip","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:02:05.823960Z","iopub.execute_input":"2024-01-10T08:02:05.824344Z","iopub.status.idle":"2024-01-10T08:02:12.677224Z","shell.execute_reply.started":"2024-01-10T08:02:05.824308Z","shell.execute_reply":"2024-01-10T08:02:12.675980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:02:18.441003Z","iopub.execute_input":"2024-01-10T08:02:18.441441Z","iopub.status.idle":"2024-01-10T08:02:19.502252Z","shell.execute_reply.started":"2024-01-10T08:02:18.441404Z","shell.execute_reply":"2024-01-10T08:02:19.501124Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os, shutil, pathlib","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:02:22.497835Z","iopub.execute_input":"2024-01-10T08:02:22.498826Z","iopub.status.idle":"2024-01-10T08:02:22.504732Z","shell.execute_reply.started":"2024-01-10T08:02:22.498765Z","shell.execute_reply":"2024-01-10T08:02:22.503545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"These are needed for the next steps","metadata":{}},{"cell_type":"code","source":"original_dir = pathlib.Path(\"train\") # identify old folder\nnew_base_dir = pathlib.Path(\"small\") #create a new folder","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:02:44.051085Z","iopub.execute_input":"2024-01-10T08:02:44.051929Z","iopub.status.idle":"2024-01-10T08:02:44.056130Z","shell.execute_reply.started":"2024-01-10T08:02:44.051892Z","shell.execute_reply":"2024-01-10T08:02:44.055161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls\n","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:35:45.160460Z","iopub.execute_input":"2024-01-10T00:35:45.161253Z","iopub.status.idle":"2024-01-10T00:35:46.134977Z","shell.execute_reply.started":"2024-01-10T00:35:45.161218Z","shell.execute_reply":"2024-01-10T00:35:46.133711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:35:50.431466Z","iopub.execute_input":"2024-01-10T00:35:50.432180Z","iopub.status.idle":"2024-01-10T00:35:51.391904Z","shell.execute_reply.started":"2024-01-10T00:35:50.432138Z","shell.execute_reply":"2024-01-10T00:35:51.390569Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# function to create a subset\ndef make_subset(subset_name, start_idx, end_idx):\n    for category in ['cat', 'dog']:\n        dir = new_base_dir / subset_name / category\n        os.makedirs(dir)\n        fnames = [f'{category}.{i}.jpg'\n                 for i in range(start_idx, end_idx)\n                 ]\n        for fname in fnames:\n            shutil.copyfile(src=original_dir / fname, dst = dir / fname)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:03:07.920122Z","iopub.execute_input":"2024-01-10T08:03:07.920859Z","iopub.status.idle":"2024-01-10T08:03:07.927238Z","shell.execute_reply.started":"2024-01-10T08:03:07.920817Z","shell.execute_reply":"2024-01-10T08:03:07.926409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"What that above function is doing:\nCopies cat and dog images from index\nstart_idx to index end_idx to the subdirectory new_base_dir/{subset_name}/cat (and /dog). \nNow we are going to create three subsets: train, test, and validation","metadata":{}},{"cell_type":"code","source":"make_subset('train', start_idx=0, end_idx = 1000)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:03:12.387098Z","iopub.execute_input":"2024-01-10T08:03:12.387801Z","iopub.status.idle":"2024-01-10T08:03:12.627226Z","shell.execute_reply.started":"2024-01-10T08:03:12.387764Z","shell.execute_reply":"2024-01-10T08:03:12.626443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"make_subset('validation', start_idx=1000, end_idx=1500)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:03:15.307099Z","iopub.execute_input":"2024-01-10T08:03:15.307727Z","iopub.status.idle":"2024-01-10T08:03:15.431866Z","shell.execute_reply.started":"2024-01-10T08:03:15.307693Z","shell.execute_reply":"2024-01-10T08:03:15.430719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"make_subset('test', start_idx=1500, end_idx=2500)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:03:18.766302Z","iopub.execute_input":"2024-01-10T08:03:18.766721Z","iopub.status.idle":"2024-01-10T08:03:19.013251Z","shell.execute_reply.started":"2024-01-10T08:03:18.766673Z","shell.execute_reply":"2024-01-10T08:03:19.012499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!ls","metadata":{"execution":{"iopub.status.busy":"2024-01-09T23:15:51.337129Z","iopub.execute_input":"2024-01-09T23:15:51.337862Z","iopub.status.idle":"2024-01-09T23:15:52.467271Z","shell.execute_reply.started":"2024-01-09T23:15:51.337821Z","shell.execute_reply":"2024-01-09T23:15:52.465078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"At the end of this exercise, we have 1000 each cat and dog images in the training folder, 500 each in the validation, and 1000 each in the test folder. This is a balanced binary classification. ","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# build the model\n# the model expects these RGB images to be 180x180 pixels\ninputs = keras.Input(shape = (180, 180, 3))\nx = keras.layers.Rescaling(1./255)(inputs) #image preprocessing, sweet!\nx = keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=256, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=256, kernel_size=3, activation='relu')(x)\nx = keras.layers.Flatten()(x)\noutputs = keras.layers.Dense(1, activation='sigmoid')(x)\n\n# model\nmodel = keras.Model(inputs=inputs, outputs=outputs)\n\n","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:04:08.196909Z","iopub.execute_input":"2024-01-10T08:04:08.197537Z","iopub.status.idle":"2024-01-10T08:04:09.506208Z","shell.execute_reply.started":"2024-01-10T08:04:08.197507Z","shell.execute_reply":"2024-01-10T08:04:09.505469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the model summary\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:04:14.763451Z","iopub.execute_input":"2024-01-10T08:04:14.763796Z","iopub.status.idle":"2024-01-10T08:04:14.802520Z","shell.execute_reply.started":"2024-01-10T08:04:14.763770Z","shell.execute_reply":"2024-01-10T08:04:14.799545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now compile the model\nmodel.compile(loss='binary_crossentropy',\n             optimizer='rmsprop',\n             metrics = ['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:04:32.511348Z","iopub.execute_input":"2024-01-10T08:04:32.511702Z","iopub.status.idle":"2024-01-10T08:04:32.530100Z","shell.execute_reply.started":"2024-01-10T08:04:32.511673Z","shell.execute_reply":"2024-01-10T08:04:32.529279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# OK, now the big deal\n# We have images in folders\n# We will have to process them\n# read picture files > decode from jpg to rgb > convert to floating point > resize to 180x180 > pack to batches\n# use image_dataset_from_directory() is the magic here!\nfrom tensorflow.keras.utils import image_dataset_from_directory\n\ntrain_dataset = image_dataset_from_directory(\n  new_base_dir / 'train',\n  image_size=(180,180),\n    batch_size=32\n)\n\nvalidation_dataset = image_dataset_from_directory(\n  new_base_dir / 'validation',\n  image_size=(180,180),\n    batch_size=32\n)\n\ntest_dataset = image_dataset_from_directory(\n  new_base_dir / 'test',\n  image_size=(180,180),\n    batch_size=32\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:41:58.712216Z","iopub.execute_input":"2024-01-10T08:41:58.712624Z","iopub.status.idle":"2024-01-10T08:41:59.121743Z","shell.execute_reply.started":"2024-01-10T08:41:58.712591Z","shell.execute_reply":"2024-01-10T08:41:59.121022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# do some callbacks\ncallbacks=[\n    keras.callbacks.ModelCheckpoint(\n     filepath='convnet_from_scratch.keras',\n     save_best=True,\n     monitor='val_loss'\n    )\n]","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:04:41.048969Z","iopub.execute_input":"2024-01-10T08:04:41.049822Z","iopub.status.idle":"2024-01-10T08:04:41.054137Z","shell.execute_reply.started":"2024-01-10T08:04:41.049784Z","shell.execute_reply":"2024-01-10T08:04:41.053252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# fit the model\nhist = model.fit(\ntrain_dataset,\nepochs = 20,\nvalidation_data = validation_dataset,\ncallbacks = callbacks\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:40:42.508148Z","iopub.execute_input":"2024-01-10T00:40:42.508584Z","iopub.status.idle":"2024-01-10T00:41:43.832976Z","shell.execute_reply.started":"2024-01-10T00:40:42.508549Z","shell.execute_reply":"2024-01-10T00:41:43.831906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"accuracy = hist.history['accuracy']\nval_acc = hist.history['val_accuracy']\nloss = hist.history['loss']\nval_loss = hist.history['val_loss']\nepochs = range(1, len(accuracy)+1)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:43:05.759918Z","iopub.execute_input":"2024-01-10T00:43:05.764751Z","iopub.status.idle":"2024-01-10T00:43:05.769757Z","shell.execute_reply.started":"2024-01-10T00:43:05.764713Z","shell.execute_reply":"2024-01-10T00:43:05.768750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(epochs, accuracy, 'bo', label='training accuracy')\nplt.plot(epochs, val_acc, 'b')\nplt.figure()\nplt.plot(epochs, loss, 'bo', label='training accuracy')\nplt.plot(epochs, val_loss, 'b')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:45:21.664489Z","iopub.execute_input":"2024-01-10T00:45:21.665135Z","iopub.status.idle":"2024-01-10T00:45:22.049768Z","shell.execute_reply.started":"2024-01-10T00:45:21.665100Z","shell.execute_reply":"2024-01-10T00:45:22.048738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"You can see in the above figure there is gross overfitting!","metadata":{}},{"cell_type":"code","source":"# let's do data augmentation\ndata_aug = keras.Sequential([\n    keras.layers.RandomFlip('horizontal'),\n    keras.layers.RandomRotation(0.1),\n    keras.layers.RandomZoom(0.2)\n])","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:48:42.634813Z","iopub.execute_input":"2024-01-10T00:48:42.635768Z","iopub.status.idle":"2024-01-10T00:48:42.648199Z","shell.execute_reply.started":"2024-01-10T00:48:42.635729Z","shell.execute_reply":"2024-01-10T00:48:42.647358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# take some random images to show how augmentation works\nplt.figure(figsize=(10,10))\nfor images, _ in train_dataset.take(1):\n    for i in range(9):\n        aug_images = data_aug(images)\n        ax = plt.subplot(3,3,i+1)\n        plt.imshow(aug_images[0].numpy().astype('uint8'))\n        plt.axis('off')","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:51:18.220899Z","iopub.execute_input":"2024-01-10T00:51:18.221727Z","iopub.status.idle":"2024-01-10T00:51:19.466853Z","shell.execute_reply.started":"2024-01-10T00:51:18.221691Z","shell.execute_reply":"2024-01-10T00:51:19.465924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lets build the model again\n# build the model\n# the model expects these RGB images to be 180x180 pixels\ninputs = keras.Input(shape = (180, 180, 3))\nx = data_aug(inputs)\nx = keras.layers.Rescaling(1./255)(inputs) #image preprocessing, sweet!\nx = keras.layers.Conv2D(filters=32, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=64, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=128, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=256, kernel_size=3, activation='relu')(x)\nx = keras.layers.MaxPooling2D(pool_size=2)(x)\nx = keras.layers.Conv2D(filters=256, kernel_size=3, activation='relu')(x)\nx = keras.layers.Flatten()(x)\nx = keras.layers.Dropout(0.5)(x)\noutputs = keras.layers.Dense(1, activation='sigmoid')(x)\n\n# model\nmodel = keras.Model(inputs=inputs, outputs=outputs)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:54:09.351585Z","iopub.execute_input":"2024-01-10T00:54:09.352446Z","iopub.status.idle":"2024-01-10T00:54:09.599711Z","shell.execute_reply.started":"2024-01-10T00:54:09.352409Z","shell.execute_reply":"2024-01-10T00:54:09.598887Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# rerun the model\nmodel.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])\nhist2 = model.fit(train_dataset,\n                 epochs=20,\n                 validation_data=validation_dataset,\n                 callbacks=callbacks)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:55:56.714379Z","iopub.execute_input":"2024-01-10T00:55:56.714780Z","iopub.status.idle":"2024-01-10T00:56:59.490606Z","shell.execute_reply.started":"2024-01-10T00:55:56.714752Z","shell.execute_reply":"2024-01-10T00:56:59.489648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"accuracy = hist2.history['accuracy']\nval_acc = hist2.history['val_accuracy']\nloss = hist2.history['loss']\nval_loss = hist2.history['val_loss']\nepochs = range(1, len(accuracy)+1)\n\nplt.plot(epochs, accuracy, 'bo', label='training accuracy')\nplt.plot(epochs, val_acc, 'b')\nplt.figure()\nplt.plot(epochs, loss, 'bo', label='training accuracy')\nplt.plot(epochs, val_loss, 'b')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:58:01.751824Z","iopub.execute_input":"2024-01-10T00:58:01.752233Z","iopub.status.idle":"2024-01-10T00:58:02.130650Z","shell.execute_reply.started":"2024-01-10T00:58:01.752204Z","shell.execute_reply":"2024-01-10T00:58:02.129771Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Slightly better!","metadata":{}},{"cell_type":"code","source":"test_loss, test_acc = model.evaluate(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T00:59:06.666289Z","iopub.execute_input":"2024-01-10T00:59:06.666660Z","iopub.status.idle":"2024-01-10T00:59:07.966256Z","shell.execute_reply.started":"2024-01-10T00:59:06.666632Z","shell.execute_reply":"2024-01-10T00:59:07.965322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# We are now going to use transfer learning on this data\n# take a pretrained model, feature extraction and fine tune\n# works great for small datasets\n# feature extraction \n# we are going to use vgg16 model bundled with keras\n# vgg16 is trained on ImageNet\nconv_base = keras.applications.vgg16.VGG16(\nweights = 'imagenet',\n    include_top = False,\n    input_shape=(180, 180, 3)\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:22:26.090153Z","iopub.execute_input":"2024-01-10T08:22:26.091119Z","iopub.status.idle":"2024-01-10T08:22:26.933369Z","shell.execute_reply.started":"2024-01-10T08:22:26.091081Z","shell.execute_reply":"2024-01-10T08:22:26.932338Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note:\n* weights = imagenet because it is trained on imagenet\n* include_top means whether we are going to use their dense connected classifier. We are not going to use as we only have two classes, they have 1000 classes, we do not need this\n* input shape is our shape which is 180, 180, 3 with RGB","metadata":{}},{"cell_type":"code","source":"conv_base.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:24:51.258185Z","iopub.execute_input":"2024-01-10T08:24:51.258906Z","iopub.status.idle":"2024-01-10T08:24:51.305594Z","shell.execute_reply.started":"2024-01-10T08:24:51.258872Z","shell.execute_reply":"2024-01-10T08:24:51.304729Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Extract features\n# write a function\ndef get_features_labels(dataset):\n    all_features = []\n    all_labels = []\n    for images, labels in dataset:\n        preprocessed_images = keras.applications.vgg16.preprocess_input(images)\n        features = conv_base.predict(preprocessed_images)\n        all_features.append(features)\n        all_labels.append(labels)\n    return np.concatenate(all_features), np.concatenate(all_labels)    ","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:40:42.902014Z","iopub.execute_input":"2024-01-10T08:40:42.902449Z","iopub.status.idle":"2024-01-10T08:40:42.908695Z","shell.execute_reply.started":"2024-01-10T08:40:42.902415Z","shell.execute_reply":"2024-01-10T08:40:42.907411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now set up train_features, train_labels\ntrain_features, train_labels = get_features_labels(train_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:42:19.417193Z","iopub.execute_input":"2024-01-10T08:42:19.417666Z","iopub.status.idle":"2024-01-10T08:42:36.141835Z","shell.execute_reply.started":"2024-01-10T08:42:19.417631Z","shell.execute_reply":"2024-01-10T08:42:36.140954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_features, val_labels = get_features_labels(validation_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:43:21.231008Z","iopub.execute_input":"2024-01-10T08:43:21.231780Z","iopub.status.idle":"2024-01-10T08:43:27.224919Z","shell.execute_reply.started":"2024-01-10T08:43:21.231738Z","shell.execute_reply":"2024-01-10T08:43:27.224077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_features, test_labels = get_features_labels(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:43:44.583655Z","iopub.execute_input":"2024-01-10T08:43:44.584026Z","iopub.status.idle":"2024-01-10T08:43:54.154686Z","shell.execute_reply.started":"2024-01-10T08:43:44.583990Z","shell.execute_reply":"2024-01-10T08:43:54.153704Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# see what vgg16 has done\ntrain_features.shape","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:51:05.323409Z","iopub.execute_input":"2024-01-10T08:51:05.323803Z","iopub.status.idle":"2024-01-10T08:51:05.331139Z","shell.execute_reply.started":"2024-01-10T08:51:05.323774Z","shell.execute_reply":"2024-01-10T08:51:05.330239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"You can see that for the training dataset,\n* vgg16 has identifed 2000 images\n* each image is now 5x5 heightxwidth\n* feature depth is now 512 (see their model.summary)\nOK, so this is what we will put into our Dense layers","metadata":{}},{"cell_type":"code","source":"# let's build the model\ninputs = keras.Input(shape=(5,5,512))\nx = keras.layers.Flatten()(inputs)\nx = keras.layers.Dense(256)(x)\nx = keras.layers.Dropout(0.5)(x)\noutputs = keras.layers.Dense(1, activation='sigmoid')(x)\n\n# then model\nmodel = keras.Model(inputs = inputs, outputs = outputs)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T08:55:03.139651Z","iopub.execute_input":"2024-01-10T08:55:03.140475Z","iopub.status.idle":"2024-01-10T08:55:03.182783Z","shell.execute_reply.started":"2024-01-10T08:55:03.140441Z","shell.execute_reply":"2024-01-10T08:55:03.181996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Explanation:\n* Input is obviously the input of everything that the pretrained model gave us which is 5,5,512\n* This will be flattened\n* we chose to have 256 nodes, could this be relu?\n* we chose to have random dropouts\n* the output layer is sigmoid as we have only one node","metadata":{}},{"cell_type":"code","source":"# now compile, fit etc\nmodel.compile(loss='binary_crossentropy',\n             optimizer = 'rmsprop',\n             metrics = ['accuracy'])\ncallbacks = [\n    keras.callbacks.ModelCheckpoint(\n        filepath=\"feature_extr.tf\",\n        save_best_only=True,\n        monitor='val_loss'\n    )\n]\nhist = model.fit(\ntrain_features,\n    train_labels,\n    epochs=20,\n    validation_data=(val_features, val_labels),\n    callbacks = callbacks\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:05:00.928175Z","iopub.execute_input":"2024-01-10T09:05:00.929048Z","iopub.status.idle":"2024-01-10T09:05:10.398229Z","shell.execute_reply.started":"2024-01-10T09:05:00.929015Z","shell.execute_reply":"2024-01-10T09:05:10.397325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note, that when we specify the filepath, we should use .tf or .txt or something, NOT .keras. For details, see\n[stackoverflow thread](https://stackoverflow.com/questions/76701617/the-following-arguments-are-not-supported-with-the-native-keras-format-opti)","metadata":{}},{"cell_type":"code","source":"# let's see the plots\nacc = hist.history['accuracy']\nval_acc = hist.history['val_accuracy']\nloss = hist.history['loss']\nval_loss = hist.history['val_loss']\nepochs = range(1, len(acc)+1)\n\nplt.plot(epochs, acc)\nplt.plot(epochs, val_acc)\nplt.figure()\nplt.plot(epochs, loss)\nplt.plot(epochs, val_loss)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:09:46.729417Z","iopub.execute_input":"2024-01-10T09:09:46.730493Z","iopub.status.idle":"2024-01-10T09:09:47.208358Z","shell.execute_reply.started":"2024-01-10T09:09:46.730455Z","shell.execute_reply":"2024-01-10T09:09:47.207390Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"These results are good, but not great. This is because we did not do image augmentation. That would need feature extraction with augmentation, and needs a GPU.","metadata":{}},{"cell_type":"code","source":"# freeze the conv_base\nconv_base = keras.applications.vgg16.VGG16(\n    weights='imagenet',\n    include_top = False\n)\nconv_base.trainable = False","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:23:56.706460Z","iopub.execute_input":"2024-01-10T09:23:56.707393Z","iopub.status.idle":"2024-01-10T09:23:57.021963Z","shell.execute_reply.started":"2024-01-10T09:23:56.707353Z","shell.execute_reply":"2024-01-10T09:23:57.021174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Freezing is done by setting the trainable to False","metadata":{}},{"cell_type":"code","source":"# Now we can augment\ndata_augment = keras.Sequential([\n    keras.layers.RandomFlip('horizontal'),\n    keras.layers.RandomRotation(0.1),\n    keras.layers.RandomZoom(0.2)\n]\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:19:47.597112Z","iopub.execute_input":"2024-01-10T09:19:47.597779Z","iopub.status.idle":"2024-01-10T09:19:47.610193Z","shell.execute_reply.started":"2024-01-10T09:19:47.597745Z","shell.execute_reply":"2024-01-10T09:19:47.609110Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# now build the model again\ninputs = keras.Input(shape=(180, 180, 3))\nx = data_augment(inputs)\nx = keras.applications.vgg16.preprocess_input(x)\nx = conv_base(x)\nx = keras.layers.Flatten()(x)\nx = keras.layers.Dense(256)(x)\nx = keras.layers.Dropout(0.5)(x)\noutputs = keras.layers.Dense(1, activation='sigmoid')(x)\n\n# then model\nmodel2 = keras.Model(inputs = inputs, outputs = outputs)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:30:38.960438Z","iopub.execute_input":"2024-01-10T09:30:38.960818Z","iopub.status.idle":"2024-01-10T09:30:39.221408Z","shell.execute_reply.started":"2024-01-10T09:30:38.960791Z","shell.execute_reply":"2024-01-10T09:30:39.220331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model2.compile(loss='binary_crossentropy',\n             optimizer = 'rmsprop',\n             metrics = ['accuracy'])\ncallbacks = [\n    keras.callbacks.ModelCheckpoint(\n        filepath=\"feature_extr_aug.tf\",\n        save_best_only=True,\n        monitor='val_loss'\n    )\n]\nhist = model2.fit(\ntrain_dataset,\n    epochs=20,\n    validation_data=validation_dataset,\n    callbacks = callbacks\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:31:04.081074Z","iopub.execute_input":"2024-01-10T09:31:04.081834Z","iopub.status.idle":"2024-01-10T09:34:10.603911Z","shell.execute_reply.started":"2024-01-10T09:31:04.081800Z","shell.execute_reply":"2024-01-10T09:34:10.602974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loss, test_accuracy = model2.evaluate(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:35:14.017985Z","iopub.execute_input":"2024-01-10T09:35:14.018370Z","iopub.status.idle":"2024-01-10T09:35:19.706136Z","shell.execute_reply.started":"2024-01-10T09:35:14.018338Z","shell.execute_reply":"2024-01-10T09:35:19.705341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This is pretty good!","metadata":{}},{"cell_type":"code","source":"# fine tune the model\n# we will freeze all layers till the fourth from last\nconv_base.trainable = True\nfor layer in conv_base.layers[:-4]:\n    layer.trainable = False","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:37:19.967631Z","iopub.execute_input":"2024-01-10T09:37:19.968751Z","iopub.status.idle":"2024-01-10T09:37:19.974921Z","shell.execute_reply.started":"2024-01-10T09:37:19.968716Z","shell.execute_reply":"2024-01-10T09:37:19.973917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# let's model again\nmodel2.compile(loss='binary_crossentropy',\n              optimizer = keras.optimizers.RMSprop(learning_rate = 0.0001),\n              metrics = ['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:39:27.545160Z","iopub.execute_input":"2024-01-10T09:39:27.545626Z","iopub.status.idle":"2024-01-10T09:39:27.559850Z","shell.execute_reply.started":"2024-01-10T09:39:27.545578Z","shell.execute_reply":"2024-01-10T09:39:27.559119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"callbacks = [\n    keras.callbacks.ModelCheckpoint(\n        filepath=\"fine_tune.tf\",\n        save_best_only=True,\n        monitor='val_loss'\n    )\n]\nhist = model2.fit(\ntrain_dataset,\n    epochs=20,\n    validation_data=validation_dataset,\n    callbacks = callbacks\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:40:47.473264Z","iopub.execute_input":"2024-01-10T09:40:47.473659Z","iopub.status.idle":"2024-01-10T09:44:37.333002Z","shell.execute_reply.started":"2024-01-10T09:40:47.473631Z","shell.execute_reply":"2024-01-10T09:44:37.332114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loss, test_accuracy = model2.evaluate(test_dataset)","metadata":{"execution":{"iopub.status.busy":"2024-01-10T09:44:58.862224Z","iopub.execute_input":"2024-01-10T09:44:58.862613Z","iopub.status.idle":"2024-01-10T09:45:04.554583Z","shell.execute_reply.started":"2024-01-10T09:44:58.862582Z","shell.execute_reply":"2024-01-10T09:45:04.553790Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}