{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# End-to-end Multi-class Dog Breed Classification \n## 1. Problem\nWe want to find out a dog breed by photo.\n## 2. Data\nThe data we're using is from Kaggle's dog breed identification competition\n## 3. Evaluation\nThe evaluation is a file with predictions probabilities for each dog bread of each test image.\n## 4. Features \n* We're dealing with images (unstructured data) so it's probably we use deep learning/transfer learning\n* It's 120 breeds of dogs (120 classes)\n* There are around 10000+ images in the train set (labeled)\n* There are around 10000+ images in test set (no labels)\n\n","metadata":{"id":"LcWBtZ7vhM_h"}},{"cell_type":"code","source":"#Unzip uploaded data\n#!unzip \"/content/drive/MyDrive/dog-vision/dog-breed-identification.zip\"","metadata":{"id":"HP9yiJL7ePYA"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Get our workspace ready\n* Import TensorFlow 2.x\n* Import TensorFlow Hub\n* Make sure we using GPU","metadata":{"id":"3U554PZ_kNko"}},{"cell_type":"code","source":"# import TensorFlow\nimport tensorflow as tf\nprint(\"TF version: \", tf.__version__)","metadata":{"id":"TCZsP14zerHf","executionInfo":{"status":"ok","timestamp":1635140793827,"user_tz":-180,"elapsed":1911,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"13152cb8-234c-475c-db16-331a9a61b3ac"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow_hub as hub\nprint(\"TF Hub version: \",hub.__version__)","metadata":{"id":"BUEMA7E7kbYo","executionInfo":{"status":"ok","timestamp":1635140794279,"user_tz":-180,"elapsed":464,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"5de2dbff-7584-494b-8e3c-d9dffb4c1527"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check for GPU availability\nprint(\"GPU\",\"available\" if tf.config.list_physical_devices(\"GPU\") else \"not available :(\")","metadata":{"id":"OctR1USyn11g","executionInfo":{"status":"ok","timestamp":1635140794764,"user_tz":-180,"elapsed":12,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"aeddd64d-f24b-416e-9c59-21a1079e53e2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Getting our data ready (turning into tensors)","metadata":{"id":"Ve8C3z2M1LrH"}},{"cell_type":"code","source":"import pandas as pd\nlabels_csv=pd.read_csv(\"/content/drive/MyDrive/dog-vision/labels.csv\")\nprint(labels_csv.describe())\nprint(labels_csv.head())","metadata":{"id":"7VTq0tA0oRcB","executionInfo":{"status":"ok","timestamp":1635140797623,"user_tz":-180,"elapsed":620,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"c038b52e-5d42-4efc-81eb-d06223281a64"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# How many images are there for each breed?\nlabels_csv[\"breed\"].value_counts().plot.bar(figsize=(20,10))","metadata":{"id":"i-Q-UIw714d1","executionInfo":{"status":"ok","timestamp":1635140800740,"user_tz":-180,"elapsed":3118,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"80642b87-24ba-46de-8bd6-a6bac443ecfd"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's view an image\nfrom IPython.display import Image\nImage(\"/content/drive/MyDrive/dog-vision/train/000bec180eb18c7604dcecc8fe0dba07.jpg\")","metadata":{"id":"cn2qzMXR2P1t","executionInfo":{"status":"ok","timestamp":1635140838293,"user_tz":-180,"elapsed":37556,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"ba54938f-7194-48df-82f4-51782645142f"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Getting images and their labels","metadata":{"id":"1Tm9Kne63qYc"}},{"cell_type":"code","source":"filenames = [\"/content/drive/MyDrive/dog-vision/train/\" +fname for fname in labels_csv[\"id\"]+\".jpg\"]","metadata":{"id":"OnBAfH9d39u8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"filenames[:10]","metadata":{"id":"6uFPXTU74ONJ","executionInfo":{"status":"ok","timestamp":1635140838296,"user_tz":-180,"elapsed":32,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"60febe8d-1a0c-4a30-f2ec-72725ff8c9d7"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# CHeck if number of filenames matches number of actual files\nimport os\nif len(os.listdir(\"/content/drive/MyDrive/dog-vision/train/\"))==len(filenames):\n  print(\"Filenames match actual amount of files, proceed!\")\nelse:\n  print(\"Filenames do NOT match the actual amount of files, Check the train directory!\")","metadata":{"id":"qOSD6yxD4auq","executionInfo":{"status":"ok","timestamp":1635140838298,"user_tz":-180,"elapsed":24,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"81426db3-21a7-43dc-c94a-a369f3fa6a8b"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Image(filenames[9000])","metadata":{"id":"7ej0mH305I_v","executionInfo":{"status":"ok","timestamp":1635140838943,"user_tz":-180,"elapsed":662,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"8db8881b-6dea-40f2-9f79-3b333536eef8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nlabels=labels_csv[\"breed\"].to_numpy()\nlabels ","metadata":{"id":"qGRI9-yp5xCi","executionInfo":{"status":"ok","timestamp":1635140838944,"user_tz":-180,"elapsed":47,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"d564faca-b2be-4ac2-823e-768607cc873b"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if len(labels)==len(filenames):\n  print(\"OK\")","metadata":{"id":"LP6jgZgj6n2k","executionInfo":{"status":"ok","timestamp":1635140838945,"user_tz":-180,"elapsed":32,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"21e04eea-bcf6-43da-dbea-c2f0fbaaaaa9"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"unique_breeds=np.unique(labels)","metadata":{"id":"xEqVymll7DpK"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(unique_breeds)","metadata":{"id":"wq-4g-OE7KKo","executionInfo":{"status":"ok","timestamp":1635140838947,"user_tz":-180,"elapsed":26,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"b8d6bcda-f9d3-407f-a816-8a8c2543a35d"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Turning labels into booleans\nboolean_labels=[label==unique_breeds for label in labels]\nboolean_labels[:2]","metadata":{"id":"KR8-BE0H7PbK","executionInfo":{"status":"ok","timestamp":1635140838948,"user_tz":-180,"elapsed":21,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"75516ce2-ce24-40b7-8fab-74f0aebb530a"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Turning into integers\nprint(labels[2])\nprint(boolean_labels[2].astype(int))","metadata":{"id":"nYbDKOf67wPd","executionInfo":{"status":"ok","timestamp":1635140838949,"user_tz":-180,"elapsed":17,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"50ba32c9-c521-4a2a-be29-2480aa5a64bf"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Creating our own validation set","metadata":{"id":"hBlpQFcc_0tv"}},{"cell_type":"code","source":"X = filenames","metadata":{"id":"6_U0OveZAhF-"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y = boolean_labels","metadata":{"id":"bdFE5FFiAjG8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# We're going to start with 1000 imagesand increase as needed.\nNUM_IMAGES = 1000 #@param{type:\"slider\",min:1000,max:10000,step:100} ","metadata":{"id":"3KJkSgmoAl9y"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Split data\nfrom sklearn.model_selection import train_test_split\nX_train,X_val,y_train,y_val=train_test_split(X[:NUM_IMAGES],\n                                             y[:NUM_IMAGES],\n                                             test_size=0.2,\n                                             random_state=42)\nlen(X_train),len(y_train),len(X_val),len(y_val)","metadata":{"id":"J7DQftP3BJAY","executionInfo":{"status":"ok","timestamp":1635140839245,"user_tz":-180,"elapsed":6,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"a4e2e05a-6bb8-4f2d-a94c-39520471e061"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preprocessing images (turning into tensors)\n\n1. Take an image filepath as input\n2. Use TensorFlow to read the file and save it to variable,'image'\n3. Turn our 'image' into Tensors\n4. Resize the image to be a shape of (224, 224)\n5. Return the modified 'image' ","metadata":{"id":"gOrHadTtBvbh"}},{"cell_type":"code","source":"IMG_SIZE=224\ndef process_image(image_path,img_size=IMG_SIZE):\n  \"\"\"\n  Takes an image filepath and turns an image into Tensor\n  \"\"\"\n  # Read an image file\n  image=tf.io.read_file(image_path)\n  # Turn the jpeg into numerical Tensor (RGB channels)\n  image = tf.image.decode_jpeg(image,channels=3)\n  # Convert the color channel values from 0-255 to 0-1 values\n  image = tf.image.convert_image_dtype(image, tf.float32)\n  # Resize\n  image=tf.image.resize(image,size=[img_size,img_size])\n  return image","metadata":{"id":"jl5aILq8CNX0"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Turning our data into batches\n\n(process 32 images (this is batch size!) in every loop)\n","metadata":{"id":"UFYzmVKhHlA5"}},{"cell_type":"code","source":"def get_image_label(image_path, label):\n  \"\"\"\n  Processes the image and returns a typle of (image, label)\n  \"\"\"\n  image=process_image(image_path)\n  return image, label","metadata":{"id":"Qp2z28Gvq0Bo"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we've got a way to turn our data into tuples of Tensors in form (image, label), let's make a function to turn all our data into batches!","metadata":{"id":"EUrB0wsrr1fq"}},{"cell_type":"code","source":"# Define the batch size, 32 is a good start\nBATCH_SIZE = 32 #@param {type:\"slider\", min:32, max:128, step:16}","metadata":{"id":"ZjMw31VBK8NU"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_data_batches(X, y=None,batch_size=BATCH_SIZE, valid_data=False, test_data= False):\n  \"\"\"\n  Creates batches of data out of image (X) and label (y) pairs.\n  Shuffles the data if it's training data but doesn't shuffle if it's validation data\n  Also accepts test data as input (no labels)\n  \"\"\"\n  if test_data:\n    print(\"Creating test data batches...\")\n    data=tf.data.Dataset.from_tensor_slices((tf.constant(X)))\n    data_batch = data.map(process_image).batch(batch_size)\n    return data_batch\n  elif valid_data:\n    print(\"Creating validation data batches...\")\n    data=tf.data.Dataset.from_tensor_slices((tf.constant(X),\n                                             tf.constant(y)))\n    data_batch=data.map(get_image_label).batch(batch_size)\n    return data_batch\n  else:\n    print(\"Create training data batches...\")\n    data=tf.data.Dataset.from_tensor_slices((tf.constant(X),\n                                             tf.constant(y)))\n    data=data.shuffle(buffer_size=len(X))\n    data=data.map(get_image_label)\n    data_batch=data.batch(batch_size)\n    return data_batch","metadata":{"id":"JyloZC7JLeux"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#create training and validation data batches\ntrain_data=create_data_batches  (X_train,y_train)\nval_data=create_data_batches(X_val,y_val,valid_data=True)","metadata":{"id":"4oZkBevBOJDm","executionInfo":{"status":"ok","timestamp":1635140844638,"user_tz":-180,"elapsed":321,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"05fcc016-3ab2-403a-d091-5883607aa162"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.element_spec, val_data.element_spec","metadata":{"id":"YraefqTZQcO6","executionInfo":{"status":"ok","timestamp":1635140844639,"user_tz":-180,"elapsed":29,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"2c9021d9-f2f1-4f11-8c86-b8b1696a90bf"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualizing Data Bathes\n\nOur data now in bathes, however, this can be a little hard to understand, let's visualize it","metadata":{"id":"yA5Duw4ZRhcf"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef show_25_images(images, labels):\n  plt.figure(figsize=(10, 10))\n  for i in range(25):\n    ax = plt. subplot(5,5,i+1)\n    plt.imshow(images[i])\n    plt.title(unique_breeds[labels[i].argmax()])\n    plt.axis(\"off\")","metadata":{"id":"wNHPqYj-UgTq"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images, train_labels = next(train_data.as_numpy_iterator())\nshow_25_images(train_images,train_labels)","metadata":{"id":"gsQPT4IfVj6l","executionInfo":{"status":"ok","timestamp":1635140850551,"user_tz":-180,"elapsed":5595,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"85e16aa5-760d-4535-d4e3-307e41f5cb40"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Building a model\nBefore we build a model, there are a few things we need to define:\n* The input shape to our model\n* The output shapes of our model\n* The URL of model we want to use","metadata":{"id":"cnlcV-UXWAtJ"}},{"cell_type":"code","source":"# Setup input shape\nINPUT_SHAPE = [None, IMG_SIZE, IMG_SIZE, 3] #batch, height, width,color channels\n# Setup output shape\nOUTPUT_SHAPE= len(unique_breeds)\n# Setup model URL from TensorFlow Hub\nMODEL_URL=\"https://tfhub.dev/google/imagenet/mobilenet_v2_130_224/classification/5\"","metadata":{"id":"YKvdOFj8Y-J4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we've got our inputs and outputs and model ready. \nLet's put them together into Keras deep learning model.\n\nLet's create a function, whitch:\n* Takes input shape, output shape and the model as parameters\n* Defines the layers in a Keras model in sequential fashion\n* Compiles the model (says it should be evaluated and improved)\n* Builds the model (tells the model the input shape it'll be getting)\n* Returns the model\n","metadata":{"id":"7NN1E5cyZQna"}},{"cell_type":"code","source":"def create_model(input_shape=INPUT_SHAPE, output_shape=OUTPUT_SHAPE, model_url=MODEL_URL):\n  print(\"Building model with: \", model_url)\n\n  # Setup the model layers\n  model=tf.keras.Sequential([hub.KerasLayer(model_url),\n                             tf.keras.layers.Dense(units=output_shape,\n                                                   activation=\"softmax\")\n                             ])\n  # Compile the model\n  model.compile(\n      loss=tf.keras.losses.CategoricalCrossentropy(),\n      optimizer=tf.keras.optimizers.Adam(),\n      metrics=[\"accuracy\"]\n  )\n  # Build the model\n  model.build(input_shape)\n  return model","metadata":{"id":"TbZHjHZzVHY3"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model=create_model()\nmodel.summary()","metadata":{"id":"KPVlz80dj_Qx","executionInfo":{"status":"ok","timestamp":1635140854245,"user_tz":-180,"elapsed":3702,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"e2cb7fd0-d111-48ad-deb6-9183350f21fe"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Creating callbacks\nCallbacks are helper functions a model can use during training to do such things as save its progress, check its progress or stop training if a model stops improving\n\nWe'll create two callbacks:\none for TensorBoard to track our model progress\nsecond for early stopping, which prevents our model from training for too long.\n\n### TensorBoard Callback\n1. load the tensorboard notebook extention\n2. create a tensorboard callback which is able to save logs to directory and pass it to our model's 'fit()' function\n3. visualize our model's training logs with the '%tensorboard' magic function (we'll do this after model training","metadata":{"id":"qwIwxDU3klDq"}},{"cell_type":"code","source":"# Load TensorBoard notebook extention\n%load_ext tensorboard","metadata":{"id":"fKVvCjQhqd9D"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import datetime\n\n# Create a function to build a tb callback\ndef create_tensorboard_callback():\n  logdir=os.path.join(\"/content/drive/MyDrive/dog-vision/logs\",\n                      datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\"))\n  return tf.keras.callbacks.TensorBoard(logdir)\n  ","metadata":{"id":"u-1y4SDqr9Q6"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create early stopping callback\nearly_stopping=tf.keras.callbacks.EarlyStopping(monitor=\"val_accuracy\",\n                                                patience=3)","metadata":{"id":"oNAZ9cdissHa"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training a model (on subset of data)\n","metadata":{"id":"owjNp-0qtjL5"}},{"cell_type":"code","source":"NUM_EPOCHS = 100 #@param {type:\"slider\", min:10, max:100, step:10}\n","metadata":{"id":"KVdNTOFouFWU"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's create a function which trains a model.\n* create a model\n* setup callback\n* call the fit()\n* return the model","metadata":{"id":"kYprGx7vuhRK"}},{"cell_type":"code","source":"def train_model():\n  \"\"\"\n  Trains a given model and returns the trained version\n  \"\"\"\n  # Create a model\n  model=create_model()\n  # Create a new tensorboard session every time we train a model\n  tensorboard=create_tensorboard_callback()\n  # Fit the model to data\n  model.fit(x=train_data,\n            epochs=NUM_EPOCHS,\n            validation_data=val_data,\n            validation_freq=1,\n            callbacks=[tensorboard, early_stopping])\n  return model\nmodel=train_model()","metadata":{"id":"ouNKBZQSu1Ez","executionInfo":{"status":"ok","timestamp":1635141041655,"user_tz":-180,"elapsed":187420,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"008accf6-8c38-4f02-f6f7-391dd9701d89"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Checking the TensorBoard logs\nThe TensorBoard magic function (%tensorboard) will access the logs directory and visualize it's contents","metadata":{"id":"L8PHqQT1v5pT"}},{"cell_type":"code","source":"%tensorboard --logdir /content/drive/MyDrive/dog-vision/logs","metadata":{"id":"MVVo-6xVyMd6","executionInfo":{"status":"ok","timestamp":1635141045732,"user_tz":-180,"elapsed":4114,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"ea8971e7-95f8-49b4-94cc-a08d991bbed5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make predictions on the validation data\npredictions = model.predict(val_data, \n                            verbose=1)\npredictions","metadata":{"id":"Cp-12TJWyUOi","executionInfo":{"status":"ok","timestamp":1635141049733,"user_tz":-180,"elapsed":4003,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"9a6bc080-a989-4b02-cefb-d60dda6d4bfe"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# First prediction\nindex=42\nprint(predictions[index])\nprint(f\"Max value (probability of prediction): {np.max(predictions[index])}\")\nprint(f\"Max index: {np.argmax(predictions[index])}\")\nprint(f\"Predicted label: {unique_breeds[np.argmax(predictions[index])]}\")","metadata":{"id":"gBUMo60FzrYr","executionInfo":{"status":"ok","timestamp":1635141049735,"user_tz":-180,"elapsed":26,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"c48a0165-33d5-4da6-d0d1-b7215044bd46"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_pred_label(prediction_probabilities):\n  \"\"\"\n  Turns an array of prediction probabilities into a label.\n  \"\"\"\n  return unique_breeds[np.argmax(prediction_probabilities)]\n\npred_label= get_pred_label(predictions[81])\npred_label","metadata":{"id":"uWGKYp5W0uFc","executionInfo":{"status":"ok","timestamp":1635141049737,"user_tz":-180,"elapsed":19,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"2927ffaf-16af-42d4-d3ce-95ec72313a0c"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def unbatchify(data):\n  \"\"\"\n  Takes a batch dataset of (image, label) tensors and returns separate arrays of images and labels\n  \"\"\"\n  images_ = []\n  labels_ = []\n\n  for image, label in data.unbatch().as_numpy_iterator():\n    images_.append(image)\n    labels_.append(unique_breeds[np.argmax(label)])\n  return images_, labels_\nval_images, val_labels = unbatchify(val_data)\nval_images[0], val_labels[0]","metadata":{"id":"I6QNU9r1XrH-","executionInfo":{"status":"ok","timestamp":1635141357116,"user_tz":-180,"elapsed":870,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"bb4b24f3-ccea-42fb-8e0e-bf2dd174ee62"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"get_pred_label(val_labels[0])","metadata":{"id":"ivjYxjJ5ap6-","executionInfo":{"status":"ok","timestamp":1635141359073,"user_tz":-180,"elapsed":248,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"dc3c3080-8ca9-48b0-ec75-6eb041a96817"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We'll create a function which:\n* Takes an array of prediction probabilities, an arrat of truth labels and an array of images and integer.\n* Converts the prediction probabilities to a predicted label\n* Plot the predicted label, its predicted probability, the truth label and the target image on a single plot","metadata":{"id":"VYYva2xXavR4"}},{"cell_type":"code","source":"def plot_pred(prediction_probabilities, labels, images, n=1):\n  \"\"\"\n  View the prediction, ground truth and image for sample n\n  \"\"\"\n  pred_prob, true_label, image = prediction_probabilities[n], labels[n], images[n]\n  pred_label = get_pred_label(pred_prob)\n  plt.imshow(image)\n  plt.xticks([])\n  plt.yticks([]) \n  # Change color of title depending on prediction is right or wrong\n  if pred_label==true_label:\n    color=\"green\"\n  else:\n    color=\"red\"\n\n  plt.title(\"{} {:2.0f}% {}\".format(pred_label, \n                                    np.max(pred_prob)*100,\n                                    true_label),\n            color = color)\n  pass\n","metadata":{"id":"k9MBVebgb2UH"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_pred(prediction_probabilities=predictions, labels=val_labels, images=val_images,n=1)","metadata":{"id":"E4uaFY4kdF5f","executionInfo":{"status":"ok","timestamp":1635141365472,"user_tz":-180,"elapsed":802,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"cf4cd9b1-3f16-471e-b164-88a3f67ff260"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_pred_conf(prediction_probabilities,labels,n=1):\n  \"\"\"\n  Plus the top 10 highest prediction confidences along with the truth label for sample n.\n  \"\"\"\n  pred_prob, true_label = prediction_probabilities[n], labels[n]\n\n  # Get the predicted label\n  pred_label = get_pred_label(pred_prob)\n\n  # Find the top 10 prediction confidence indexes\n  top_10_pred_indexes = pred_prob.argsort()[-10:][::-1]\n  \n  # Find the top 10 prediction confidence values\n  top_10_pred_values = pred_prob[top_10_pred_indexes]\n\n  #Find the top 10 prediction labels\n  top_10_pred_labels = unique_breeds[top_10_pred_indexes]\n\n  #Setup plot\n  top_plot = plt.bar(np.arange(len(top_10_pred_labels)),\n                     top_10_pred_values,\n                     color=\"grey\")\n  plt.xticks(np.arange(len(top_10_pred_labels)), \n             labels=top_10_pred_labels, \n             rotation=\"vertical\")\n  # Change color of true label\n  if np.isin(true_label, top_10_pred_labels):\n    top_plot[np.argmax(top_10_pred_labels == true_label)].set_color(\"green\")\n  else:\n    pass\n    ","metadata":{"id":"ESXniSBXdgmI"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_pred_conf(prediction_probabilities=predictions,\n               labels=val_labels,\n               n=9)","metadata":{"id":"_qU8dp0U_P19","executionInfo":{"status":"ok","timestamp":1635142011119,"user_tz":-180,"elapsed":571,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"1d1c996f-7cff-4c60-b2ae-b3a0dd8b778e"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we've got some functions to visualize our predictions and evaluate our model, let's check out a few.","metadata":{"id":"OOj-csArBbd2"}},{"cell_type":"code","source":"i_multiplier = 20\nnum_rows = 3\nnum_cols = 2\nnum_images = num_rows * num_cols\nplt.figure(figsize=(10*num_cols, 5 * num_rows))\nfor i in range(num_images):\n  plt.subplot(num_rows, 2*num_cols, 2*i+1)\n  plot_pred(prediction_probabilities=predictions,\n            labels=val_labels,\n            images=val_images,\n            n=i+i_multiplier)\n  plt.subplot(num_rows, 2*num_cols, 2*i+2)\n  plot_pred_conf(prediction_probabilities=predictions,\n                 labels=val_labels,\n                 n=i+i_multiplier)\nplt.tight_layout(h_pad=1.0)\nplt.show()","metadata":{"id":"cPvo98AWEs-X","executionInfo":{"status":"ok","timestamp":1635142762789,"user_tz":-180,"elapsed":3075,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"5d003faf-734c-44eb-cf76-9fbd0283a6d2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Saving and Reloading a trained model","metadata":{"id":"_KSHWEaqFna3"}},{"cell_type":"code","source":"def save_model(model, suffix=None):\n  \"\"\"\n  Saves a given model in a models directory and appends a string suffix.\n  \"\"\"\n  model_dir = os.path.join(\"/content/drive/MyDrive/dog-vision/models\",\n                          datetime.datetime.now().strftime(\"%Y%m%d-%H%M%s\"))\n  model_path=model_dir + \"-\" + suffix + \".h5\"\n  print(f\"Saving model to: {model_path}...\")\n  model.save(model_path)\n  return model_path\n","metadata":{"id":"tjILMLVTHKjY"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_model(model_path):\n  \"\"\"\n  Loads a saved model from specified path\n  \"\"\"\n  print(f\"Loading a model from path: {model_path} ...\")\n  model = tf.keras.models.load_model(model_path,\n                                     custom_objects={\"KerasLayer\":hub.KerasLayer})\n  return model","metadata":{"id":"NZuk3jvzIPro"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Save our model trained on 1000 images\nsave_model(model, suffix=\"1000-images-mobilenetv3-Adam\")","metadata":{"id":"hGd93D7fI6JI","executionInfo":{"status":"ok","timestamp":1635143478908,"user_tz":-180,"elapsed":577,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"8bbf41e3-5f90-4e79-8a1e-d74e0905b5e3"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load a trained model\nloaded_1000_image_model = load_model(\"/content/drive/MyDrive/dog-vision/models/20211025-06311635143481-1000-images-mobilenetv3-Adam.h5\")","metadata":{"id":"RT_GfDrMJRT3","executionInfo":{"status":"ok","timestamp":1635143578972,"user_tz":-180,"elapsed":5512,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"06d0ba80-19e4-44bd-cc82-c08bc31ecbfe"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.evaluate(val_data)","metadata":{"id":"d4CcBChKJt5P","executionInfo":{"status":"ok","timestamp":1635143650051,"user_tz":-180,"elapsed":1547,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"d373e13e-d573-4faf-a2f3-d84da0142161"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loaded_1000_image_model.evaluate(val_data)","metadata":{"id":"fnjceMZWKAOI","executionInfo":{"status":"ok","timestamp":1635143665215,"user_tz":-180,"elapsed":1885,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"ee0b6074-19a6-4658-f3b0-19c48f1bfb49"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training a big dog model (on full data!)","metadata":{"id":"jPAWWvuSKDyI"}},{"cell_type":"code","source":"# Create a data batch with the full data set\nfull_data=create_data_batches(X,y)","metadata":{"id":"WDFc7U0_Kb1Y","executionInfo":{"status":"ok","timestamp":1635143852179,"user_tz":-180,"elapsed":357,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"d868894f-2f8d-4fe6-b9ad-283f6326480f"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a model for full model\nfull_model=create_model()","metadata":{"id":"_Gudim5EKx2n","executionInfo":{"status":"ok","timestamp":1635143982123,"user_tz":-180,"elapsed":3392,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"5f8cc21b-3e1c-484d-eacc-3aca67c7e815"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create full model callbacks\nfull_model_tensorboard = create_tensorboard_callback()\n# No validation set when training on all the data, so we can't monitor  validation accuracy\nfull_model_early_stopping= tf.keras.callbacks.EarlyStopping(monitor=\"accuracy\",\n                                                            patience=3)","metadata":{"id":"pDHudBxQLQ12"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Note:** Running the cell below will take ~30 min","metadata":{"id":"lxTa3P0bME31"}},{"cell_type":"code","source":"# Fit full model to the full data\nfull_model.fit(x=full_data,\n               epochs=NUM_EPOCHS,\n               callbacks=[full_model_tensorboard, full_model_early_stopping])","metadata":{"id":"icT5W5B_LsNA","executionInfo":{"status":"ok","timestamp":1635146729303,"user_tz":-180,"elapsed":2432994,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"61414948-71e9-4fe5-b2d0-ec0011228efc"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"save_model(full_model,suffix=\"full-image-set-mobilenetv2-Adam\")","metadata":{"id":"tyll6eENMeZI"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loaded_full_model=load_model(\"/content/drive/MyDrive/dog-vision/models/20211025-07281635146918-full-image-set-mobilenetv2-Adam.h5\")","metadata":{"id":"S0dXNuWYWej8","executionInfo":{"status":"ok","timestamp":1635147293229,"user_tz":-180,"elapsed":5075,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"bf8f88e3-9f34-4cd5-d4c2-3abd435e9478"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Making predictions on test data set","metadata":{"id":"LjbCr9IgYfD3"}},{"cell_type":"code","source":"test_path=\"/content/drive/MyDrive/dog-vision/test/\"\ntest_filenames=[test_path + fname for fname in os.listdir(test_path)]\ntest_filenames[:10]","metadata":{"id":"eweuy443YsrF","executionInfo":{"status":"ok","timestamp":1635147763274,"user_tz":-180,"elapsed":263,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"e9df75bb-9177-4f6b-af29-620f67570d0d"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create test data batch\ntest_data = create_data_batches(test_filenames, test_data = True)","metadata":{"id":"GXCqY2qqZsvf","executionInfo":{"status":"ok","timestamp":1635147859307,"user_tz":-180,"elapsed":250,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"5e4cf261-0b46-4b45-f3c4-043c0e584421"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Note:** Running the cell below takes time!","metadata":{"id":"Rujvbc5oagxB"}},{"cell_type":"code","source":"# Make predictions on test data\ntest_predictions = loaded_full_model.predict(test_data,\n                                             verbose=1)","metadata":{"id":"gGQUzSZEaDPW","executionInfo":{"status":"ok","timestamp":1635148982793,"user_tz":-180,"elapsed":982614,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"075bd701-5883-4f4f-e49d-9dc697c42d18"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"np.savetxt(\"/content/drive/MyDrive/dog-vision/preds_array.csv\", test_predictions, delimiter=\",\")","metadata":{"id":"-h5ZaYwDamo9","executionInfo":{"status":"ok","timestamp":1635149028372,"user_tz":-180,"elapsed":1701,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Preparing test dataset predictions for Kaggle\n\nWe'll going to:\n* Create a pandas DataFrame with an ID and column for each dog breed\n* Add data to the ID column by extracting the test image ID's from their filenames\n* Add data (the prediction probabilities) to each of the dog breed columns\n* Export the DataFrame to csv","metadata":{"id":"opSlRXTKcnma"}},{"cell_type":"code","source":"preds_df = pd.DataFrame(columns=[\"id\"]+list(unique_breeds))\npreds_df.head()","metadata":{"id":"es85ahp4elC-","executionInfo":{"status":"ok","timestamp":1635149314667,"user_tz":-180,"elapsed":238,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"d7a96db6-0360-40d2-cc45-0d4c7418b919"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_ids = [os.path.splitext(path)[0] for path in os.listdir(test_path)]","metadata":{"id":"WCwrWInHfneP","executionInfo":{"status":"ok","timestamp":1635149578732,"user_tz":-180,"elapsed":215,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df[\"id\"]=test_ids","metadata":{"id":"4nwyZ8kif8Zo","executionInfo":{"status":"ok","timestamp":1635149621855,"user_tz":-180,"elapsed":243,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df[list(unique_breeds)]=test_predictions\npreds_df.head()","metadata":{"id":"-dwXeKo0gpzO","executionInfo":{"status":"ok","timestamp":1635149693614,"user_tz":-180,"elapsed":568,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"7194ad27-391a-4d7f-fce3-d14a1ec77979"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df.to_csv(\"/content/drive/MyDrive/dog-vision/full_model_predictions_submisiion_1_mobilenetv2.csv\", index=False)","metadata":{"id":"lShGo0dzhD73","executionInfo":{"status":"ok","timestamp":1635149797281,"user_tz":-180,"elapsed":2279,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Making predictions on custom images\n\nTo make predictions on custom images, we'll:\n* Get the filepaths\n* Turn to data batches using 'create_data_batches()', test_data=True\n* Pass the custom image data to predict() function of our model\n* Convert the output probabilities to prediction labels\n* Compare the prediction labels to the custom image","metadata":{"id":"tdnSyR4NhaI-"}},{"cell_type":"code","source":"custom_path=\"/content/drive/MyDrive/dog-vision/custom/\"\ncustom_image_paths=[custom_path + fname for fname in os.listdir(custom_path)]","metadata":{"id":"CGllmLonj92P","executionInfo":{"status":"ok","timestamp":1635150596610,"user_tz":-180,"elapsed":193,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_image_paths","metadata":{"id":"Ub-9ZfKQkggH","executionInfo":{"status":"ok","timestamp":1635150600623,"user_tz":-180,"elapsed":209,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"8fd6afb6-f39a-4fe7-d0fd-181fef459f75"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_data=create_data_batches(custom_image_paths, test_data=True)","metadata":{"id":"708_tK37khee","executionInfo":{"status":"ok","timestamp":1635150716216,"user_tz":-180,"elapsed":371,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"01046c5c-0b38-4637-8bee-97335ee67023"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_preds=loaded_full_model.predict(custom_data)","metadata":{"id":"LCrsi80zk9qP","executionInfo":{"status":"ok","timestamp":1635150755676,"user_tz":-180,"elapsed":918,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_pred_labels=[get_pred_label(custom_preds[i]) for i in range(len(custom_preds))]\ncustom_pred_labels","metadata":{"id":"pGL1E0n4lHJQ","executionInfo":{"status":"ok","timestamp":1635150826854,"user_tz":-180,"elapsed":223,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"c5ee89de-d8a7-4331-cc6b-c3e7163f53b4"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"custom_images=[]\nfor image in custom_data.unbatch().as_numpy_iterator():\n  custom_images.append(image)","metadata":{"id":"j2QcpUOWlYlo","executionInfo":{"status":"ok","timestamp":1635150924215,"user_tz":-180,"elapsed":202,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,10))\nfor i, image in enumerate(custom_images):\n  plt.subplot(1,3,i+1)\n  plt.xticks([])\n  plt.yticks([])\n  plt.title(custom_pred_labels[i])\n  plt.imshow(image)\n","metadata":{"id":"uCS6m6CBlwe2","executionInfo":{"status":"ok","timestamp":1635151029165,"user_tz":-180,"elapsed":534,"user":{"displayName":"Mykhailo Petrushanskyi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"12021221351791176005"}},"outputId":"30f0cd71-4bdd-4be9-8971-5c03c72b76d8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"I9gIcnUMmFPp"},"execution_count":null,"outputs":[]}]}