{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "_os2SNaj1njY"
},
"source": [
"# Image classification from scratch\n",
"\n",
"**Author:** [fchollet](https://twitter.com/fchollet)
\n",
"**Date created:** 2020/04/27
\n",
"**Last modified:** 2020/04/28
\n",
"**Description:** Training an image classifier from scratch on the Kaggle Cats vs Dogs dataset."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "hEPiUEtj1njZ"
},
"source": [
"## Introduction\n",
"\n",
"This example shows how to do image classification from scratch, starting from JPEG\n",
"image files on disk, without leveraging pre-trained weights or a pre-made Keras\n",
"Application model. We demonstrate the workflow on the Kaggle Cats vs Dogs binary\n",
" classification dataset.\n",
"\n",
"We use the `image_dataset_from_directory` utility to generate the datasets, and\n",
"we use Keras image preprocessing layers for image standardization and data augmentation.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vFcWzzPQ1nja"
},
"source": [
"## Setup\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "u_z-j-fY1njb"
},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"from tensorflow.keras import layers\n",
"import pydot\n",
"import os\n",
"from PIL import Image\n",
"import matplotlib.pyplot as plt\n",
"from keras.utils.vis_utils import plot_model"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "iusfbMxZ1njc"
},
"source": [
"## Load the data: the Cats vs Dogs dataset\n",
"\n",
"### Raw data download\n",
"\n",
"First, let's download the 786M ZIP archive of the raw data:\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5lujeYX81njd"
},
"source": [
"Now we have a `PetImages` folder which contain two subfolders, `Cat` and `Dog`. Each\n",
" subfolder contains image files for each category.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "jED1LIFr1njd",
"outputId": "bfed1264-9e8d-42d3-be60-3bde47503bf0"
},
"outputs": [],
"source": [
"!ls PetImages\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "iz07gyGU1nje"
},
"source": [
"### Filter out corrupted images\n",
"\n",
"When working with lots of real-world image data, corrupted images are a common\n",
"occurence. Let's filter out badly-encoded images that do not feature the string \"JFIF\"\n",
" in their header.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 244
},
"id": "nsPyGIsQ1nje",
"outputId": "1eeee067-5d8c-44ed-d2eb-5831ad806f47"
},
"outputs": [],
"source": [
"num_skipped = 0\n",
"print(\"here!\")\n",
"for folder_name in (\"Cat\", \"Dog\"):\n",
" folder_path = os.path.join(\"PetImages\", folder_name)\n",
" for fname in os.listdir(folder_path):\n",
" if fname[-1].lower() == 'g':\n",
" fpath = os.path.join(folder_path, fname)\n",
" #Image.open(fpath).convert('L').save(fpath)\n",
" try:\n",
" fobj = open(fpath, \"rb\")\n",
" is_jfif = tf.compat.as_bytes(\"JFIF\") in fobj.peek(10)\n",
" finally:\n",
" fobj.close()\n",
"\n",
" if not is_jfif:\n",
" num_skipped += 1\n",
" # Delete corrupted image\n",
" os.remove(fpath)\n",
"\n",
"print(\"Deleted %d images\" % num_skipped)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "n82W39da1njf"
},
"source": [
"## Generate a `Dataset`\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "m-KkH4-D1njf",
"outputId": "fdad103a-674e-4be3-b10d-c43fdbe25a11"
},
"outputs": [],
"source": [
"image_size = (180, 180)\n",
"batch_size = 32\n",
"\n",
"train_ds = tf.keras.preprocessing.image_dataset_from_directory(\n",
" \"PetImages\",\n",
" color_mode='grayscale',\n",
" validation_split=0.2,\n",
" subset=\"training\",\n",
" seed=1337,\n",
" image_size=image_size,\n",
" batch_size=batch_size,\n",
")\n",
"val_ds = tf.keras.preprocessing.image_dataset_from_directory(\n",
" \"PetImages\",\n",
" color_mode='grayscale',\n",
" validation_split=0.2,\n",
" subset=\"validation\",\n",
" seed=1337,\n",
" image_size=image_size,\n",
" batch_size=batch_size,\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "SWzlW2H-1njf"
},
"source": [
"## Visualize the data\n",
"\n",
"Here are the first 9 images in the training dataset. As you can see, label 1 is \"dog\"\n",
" and label 0 is \"cat\".\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 591
},
"id": "St7lj7zC1njg",
"outputId": "9a20d448-26a8-4e0b-9373-dd3b7051334b"
},
"outputs": [],
"source": [
"plt.figure(figsize=(10, 10))\n",
"for images, labels in train_ds.take(1):\n",
" for i in range(9):\n",
" ax = plt.subplot(3, 3, i + 1)\n",
" img = images[i].numpy().astype(\"uint8\").squeeze()\n",
" plt.imshow(img,cmap='gray', vmin=0, vmax=255)\n",
" plt.title(int(labels[i]))\n",
" plt.axis(\"off\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "03HkTWlO1njg"
},
"source": [
"## Using image data augmentation\n",
"\n",
"When you don't have a large image dataset, it's a good practice to artificially\n",
"introduce sample diversity by applying random yet realistic transformations to the\n",
"training images, such as random horizontal flipping or small random rotations. This\n",
"helps expose the model to different aspects of the training data while slowing down\n",
" overfitting.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "LM39ZkDl1njg"
},
"outputs": [],
"source": [
"data_augmentation = keras.Sequential(\n",
" [\n",
" layers.RandomFlip(\"horizontal\"),\n",
" layers.RandomRotation(0.1),\n",
" ]\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "G7dmLVej1njh"
},
"source": [
"Let's visualize what the augmented samples look like, by applying `data_augmentation`\n",
" repeatedly to the first image in the dataset:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 575
},
"id": "pI6hq_U01njh",
"outputId": "8d19c4ae-cce0-4143-fa3a-b4866eb1ad49"
},
"outputs": [],
"source": [
"plt.figure(figsize=(10, 10))\n",
"for images, _ in train_ds.take(1):\n",
" for i in range(9):\n",
" augmented_images = data_augmentation(images)\n",
" ax = plt.subplot(3, 3, i + 1)\n",
" img = augmented_images[0].numpy().astype(\"uint8\").squeeze()\n",
" plt.imshow(img,cmap='gray', vmin=0, vmax=255)\n",
" plt.axis(\"off\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "CElkI2-r1njh"
},
"source": [
"## Standardizing the data\n",
"\n",
"Our image are already in a standard size (180x180), as they are being yielded as\n",
"contiguous `float32` batches by our dataset. However, their RGB channel values are in\n",
" the `[0, 255]` range. This is not ideal for a neural network;\n",
"in general you should seek to make your input values small. Here, we will\n",
"standardize values to be in the `[0, 1]` by using a `Rescaling` layer at the start of\n",
" our model.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ETAHN-xe1njh"
},
"source": [
"## Two options to preprocess the data\n",
"\n",
"There are two ways you could be using the `data_augmentation` preprocessor:\n",
"\n",
"**Option 1: Make it part of the model**, like this:\n",
"\n",
"```python\n",
"inputs = keras.Input(shape=input_shape)\n",
"x = data_augmentation(inputs)\n",
"x = layers.Rescaling(1./255)(x)\n",
"... # Rest of the model\n",
"```\n",
"\n",
"With this option, your data augmentation will happen *on device*, synchronously\n",
"with the rest of the model execution, meaning that it will benefit from GPU\n",
" acceleration.\n",
"\n",
"**Option 2: apply it to the dataset**, so as to obtain a dataset that yields batches of\n",
" augmented images, like this:\n",
"\n",
"```python\n",
"augmented_train_ds = train_ds.map(\n",
" lambda x, y: (data_augmentation(x, training=True), y))\n",
"```\n",
"\n",
"We'll go with the first option.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "h_6iyi4P1njh"
},
"source": [
"## Configure the dataset for performance\n",
"\n",
"Let's make sure to use buffered prefetching so we can yield data from disk without\n",
" having I/O becoming blocking:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ofmdIqoK1njh"
},
"outputs": [],
"source": [
"train_ds = train_ds.prefetch(buffer_size=32)\n",
"val_ds = val_ds.prefetch(buffer_size=32)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "dVPOJ-x91nji"
},
"source": [
"## Build a model\n",
"\n",
"We'll build a small version of the Xception network. We haven't particularly tried to\n",
"optimize the architecture; if you want to do a systematic search for the best model\n",
" configuration, consider using\n",
"[KerasTuner](https://github.com/keras-team/keras-tuner).\n",
"\n",
"Note that:\n",
"\n",
"- We start the model with the `data_augmentation` preprocessor, followed by a\n",
" `Rescaling` layer.\n",
"- We include a `Dropout` layer before the final classification layer.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 312
},
"id": "DENybniZ1nji",
"outputId": "05bdae59-7d62-4401-f6a1-1ef4102acd32"
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def make_model(input_shape, num_classes):\n",
" inputs = keras.Input(shape=input_shape)\n",
" x = layers.Flatten()(inputs)\n",
" layer1 = layers.Dense(500,activation=\"relu\")(x)\n",
" if num_classes == 2:\n",
" activation = \"sigmoid\"\n",
" units = 1\n",
" else:\n",
" activation = \"softmax\"\n",
" units = num_classes\n",
" outputs = layers.Dense(units, activation=activation)(layer1)\n",
" return keras.Model(inputs, outputs)\n",
"\n",
"\n",
"model = make_model(input_shape=image_size, num_classes=2)\n",
"keras.utils.plot_model(model, show_shapes=True)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ixwtcs_s1nji"
},
"source": [
"## Train the model\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 636
},
"id": "mzaGa4mz1njj",
"outputId": "898a58c3-0a05-472f-8281-0bc779376278"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/50\n",
"586/586 [==============================] - 83s 139ms/step - loss: 195.7281 - accuracy: 0.5196 - val_loss: 71.4404 - val_accuracy: 0.5041\n",
"Epoch 2/50\n",
"586/586 [==============================] - 86s 146ms/step - loss: 35.0901 - accuracy: 0.5271 - val_loss: 9.5992 - val_accuracy: 0.5572\n",
"Epoch 3/50\n",
"586/586 [==============================] - 84s 144ms/step - loss: 16.7845 - accuracy: 0.5332 - val_loss: 5.5357 - val_accuracy: 0.5649\n",
"Epoch 4/50\n",
"193/586 [========>.....................] - ETA: 1:01 - loss: 11.0913 - accuracy: 0.5376"
]
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mmetrics\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"accuracy\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m )\n\u001b[0;32m---> 11\u001b[0;31m model.fit(\n\u001b[0m\u001b[1;32m 12\u001b[0m \u001b[0mtrain_ds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepochs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mepochs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcallbacks\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcallbacks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalidation_data\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mval_ds\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m )\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/keras/utils/traceback_utils.py\u001b[0m in \u001b[0;36merror_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0mfiltered_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 64\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 65\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# pylint: disable=broad-except\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0mfiltered_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_process_traceback_frames\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__traceback__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/keras/engine/training.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)\u001b[0m\n\u001b[1;32m 1214\u001b[0m _r=1):\n\u001b[1;32m 1215\u001b[0m \u001b[0mcallbacks\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mon_train_batch_begin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1216\u001b[0;31m \u001b[0mtmp_logs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1217\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdata_handler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshould_sync\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1218\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0masync_wait\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/util/traceback_utils.py\u001b[0m in \u001b[0;36merror_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[0mfiltered_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 149\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 150\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 151\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 152\u001b[0m \u001b[0mfiltered_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_process_traceback_frames\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__traceback__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/def_function.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 908\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 909\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mOptionalXlaContext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_jit_compile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 910\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 911\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 912\u001b[0m \u001b[0mnew_tracing_count\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexperimental_get_tracing_count\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/def_function.py\u001b[0m in \u001b[0;36m_call\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 940\u001b[0m \u001b[0;31m# In this case we have created variables on the first call, so we run the\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 941\u001b[0m \u001b[0;31m# defunned version which is guaranteed to never create variables.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 942\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_stateless_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# pylint: disable=not-callable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 943\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_stateful_fn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 944\u001b[0m \u001b[0;31m# Release the lock early so that multiple threads can perform the call\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/function.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 3128\u001b[0m (graph_function,\n\u001b[1;32m 3129\u001b[0m filtered_flat_args) = self._maybe_define_function(args, kwargs)\n\u001b[0;32m-> 3130\u001b[0;31m return graph_function._call_flat(\n\u001b[0m\u001b[1;32m 3131\u001b[0m filtered_flat_args, captured_inputs=graph_function.captured_inputs) # pylint: disable=protected-access\n\u001b[1;32m 3132\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/function.py\u001b[0m in \u001b[0;36m_call_flat\u001b[0;34m(self, args, captured_inputs, cancellation_manager)\u001b[0m\n\u001b[1;32m 1957\u001b[0m and executing_eagerly):\n\u001b[1;32m 1958\u001b[0m \u001b[0;31m# No tape is watching; skip to running the function.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1959\u001b[0;31m return self._build_call_outputs(self._inference_function.call(\n\u001b[0m\u001b[1;32m 1960\u001b[0m ctx, args, cancellation_manager=cancellation_manager))\n\u001b[1;32m 1961\u001b[0m forward_backward = self._select_forward_and_backward_functions(\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/function.py\u001b[0m in \u001b[0;36mcall\u001b[0;34m(self, ctx, args, cancellation_manager)\u001b[0m\n\u001b[1;32m 596\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0m_InterpolateFunctionError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 597\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcancellation_manager\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 598\u001b[0;31m outputs = execute.execute(\n\u001b[0m\u001b[1;32m 599\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msignature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 600\u001b[0m \u001b[0mnum_outputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_num_outputs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m/opt/miniconda3/lib/python3.8/site-packages/tensorflow/python/eager/execute.py\u001b[0m in \u001b[0;36mquick_execute\u001b[0;34m(op_name, num_outputs, inputs, attrs, ctx, name)\u001b[0m\n\u001b[1;32m 56\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[0mctx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mensure_initialized\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 58\u001b[0;31m tensors = pywrap_tfe.TFE_Py_Execute(ctx._handle, device_name, op_name,\n\u001b[0m\u001b[1;32m 59\u001b[0m inputs, attrs, num_outputs)\n\u001b[1;32m 60\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_NotOkStatusException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mKeyboardInterrupt\u001b[0m: "
]
}
],
"source": [
"epochs = 25\n",
"\n",
"callbacks = [\n",
" keras.callbacks.ModelCheckpoint(\"save_at_{epoch}.h5\"),\n",
"]\n",
"model.compile(\n",
" optimizer=keras.optimizers.Adam(1e-3),\n",
" loss=\"binary_crossentropy\",\n",
" metrics=[\"accuracy\"],\n",
")\n",
"model.fit(\n",
" train_ds, epochs=epochs, callbacks=callbacks, validation_data=val_ds,\n",
")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "smjeG0W21njj"
},
"source": [
"After 50 epochs, we're up to 64% accuracy on the test set, but still close to chance on the validation set.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "TO_jOMfA1njj"
},
"source": [
"## Run inference on new data\n",
"\n",
"Note that data augmentation and dropout are inactive at inference time.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "-0DzdyI_1njj"
},
"outputs": [],
"source": [
"img = keras.preprocessing.image.load_img(\n",
" \"calvin.jpg\", target_size=image_size, color_mode=\"grayscale\"\n",
")\n",
"img_array = keras.preprocessing.image.img_to_array(img)\n",
"print(img_array.shape)\n",
"img_array = tf.expand_dims(img_array, 0) # Create batch axis\n",
"\n",
"predictions = model.predict(img_array)\n",
"score = predictions[0]\n",
"print(\n",
" \"This image is %.2f percent cat and %.2f percent dog.\"\n",
" % (100 * (1 - score), 100 * score)\n",
")\n",
"#\"PetImages/Cat/6779.jpg\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"collapsed_sections": [],
"name": "cat_regression",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 1
}