This project creates an algorithm for recognizing dog breeds using Keras and TensorFlow. If a dog is detected in the image, it will provide a guess at the dog's breed. If a human face is detected, it will provide an output of the dog breed that most resembles the human face. I completed this project as part of Udacity's Machine Learning Nanodegree.
To get started, I first imported a dataset of dog images.
from sklearn.datasets import load_files
from keras.utils import np_utils
import numpy as np
from glob import glob
#defines function to load train, test, and validation datasets
def load_dataset(path):
data = load_files(path)
dog_files = np.array(data['filenames'])
dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
return dog_files, dog_targets
#loads train, test, and validation datasets
train_files, train_targets = load_dataset('dogImages/train')
valid_files, valid_targets = load_dataset('dogImages/valid')
test_files, test_targets = load_dataset('dogImages/test')
#loads list of dog names
dog_names = [item[20:-1] for item in sorted(glob("dogImages/train/*/"))]
# print statistics about the dataset
print('There are %d total dog categories.' % len(dog_names))
print('There are %s total dog images.\n' % len(np.hstack([train_files, valid_files, test_files])))
print('There are %d training dog images.' % len(train_files))
print('There are %d validation dog images.' % len(valid_files))
print('There are %d test dog images.'% len(test_files))
Next, I imported a dataset of human images, where the file paths are stored in a numpy array called human_files
.
import random
random.seed(8675309)
#loads filenames in shuffled human dataset
human_files = np.array(glob("lfw/*/*"))
random.shuffle(human_files)
#prints statistics about the dataset
print('There are %d total human images.' % len(human_files))
To detect human faces in the images, I used OpenCV's implementation of Haar feature-based cascade classifiers to detect human faces in images. There are several pre-trained face detectors on OpenCV (https://github.com/opencv/opencv/tree/master/data/haarcascades). I used the frontal face detector. In order to use the OpenCV face detectors, the images must be converted to grayscale.
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
#extracts a pre-trained face detector
face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt.xml')
#loads color (BGR) image
img = cv2.imread(human_files[3])
#converts BGR image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#finds faces in image
faces = face_cascade.detectMultiScale(gray)
#prints number of faces detected in the image
print('Number of faces detected:', len(faces))
#gets bounding box for each detected face
for (x,y,w,h) in faces:
# add bounding box to color image
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
#converts BGR image to RGB for plotting
cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#displays the image, along with bounding box
plt.imshow(cv_rgb)
plt.show()
Next, I used the face dectector algorithm to create a function that outputs TRUE if a face is detected in the image.
#function that returns "True" if a face is detected in image stored at img_path
def face_detector(img_path):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray)
return len(faces) > 0
Next, I assessed the human face detection algorithm on a sample of 100 human and dog images. The output shows that the algorithm works well for human faces at 99% accuracy but does classify 11% of dog images as humans.
human_files_short = human_files[:100]
dog_files_short = train_files[:100]
## Tests the performance of the face_detector algorithm
humans_in_humans = 0
for human in human_files_short:
if face_detector(human):
humans_in_humans += 1
humans_in_dogs = 0
for dog in dog_files_short:
if face_detector(dog):
humans_in_dogs += 1
print ("Percent of humans faces detected in humans is {}".format(humans_in_humans))
print ("Percent of humans faces detected in dogs is {}".format(humans_in_dogs))
To detect dogs, I leveraged a pre-trained ResNet-50 model to detect dogs in images. The ResNet model has been trained on ImageNet. ImageNet is a very popular dataset used for image classification and other vision applications. This pre-trained ResNet model will return a prediction for the object that is contained in the image.
from keras.applications.resnet50 import ResNet50
#defines ResNet50 model
ResNet50_model = ResNet50(weights='imagenet')
from keras.preprocessing import image
from tqdm import tqdm
def path_to_tensor(img_path):
#loads RGB image as PIL.Image.Image type
img = image.load_img(img_path, target_size=(224, 224))
#converts PIL.Image.Image type to 3D tensor with shape (224, 224, 3)
x = image.img_to_array(img)
#converts 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor
return np.expand_dims(x, axis=0)
def paths_to_tensor(img_paths):
list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
return np.vstack(list_of_tensors)
from keras.applications.resnet50 import preprocess_input, decode_predictions
def ResNet50_predict_labels(img_path):
# returns prediction vector for image located at img_path
img = preprocess_input(path_to_tensor(img_path))
return np.argmax(ResNet50_model.predict(img))
Next, I created a function that detects if a dog is in the image. From the ImageNet dictionary, dog keys are 151-268 and include all dog categories. If the ResNet50 model returns a value in the number range, a dog is in the image.
###returns "True" if a dog is detected in the image stored at img_path
def dog_detector(img_path):
prediction = ResNet50_predict_labels(img_path)
return ((prediction <= 268) & (prediction >= 151))
After creating the function, I will assess my dog detector function in the same way I did for the human face detector. From the output, the dog detector performs well detecting 100% of the dogs and incorrectly classifying only 1% of the human faces.
dogs_in_humans = 0
for human in human_files_short:
if dog_detector(human):
dogs_in_humans += 1
dogs_in_dogs = 0
for dog in dog_files_short:
if dog_detector(dog):
dogs_in_dogs += 1
print ("Percent of dogs detected in humans is {}".format(dogs_in_humans))
print ("Percent of dogs detected in dogs is {}".format(dogs_in_dogs))
After creating my dog and face detection algorithms, I used transfer learning to create a convolutional neural network that can then identify dog breeds. I leveraged a pre-trained InceptionV3 model and its bottleneck features as the baseline. Below is the initial setup.
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
# pre-process the data for Keras
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255
from keras.callbacks import ModelCheckpoint
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, Dropout, Flatten, Dense
from keras.models import Sequential
### Obtains bottleneck features from the pre-trained Inception V3 CNN.
bottleneck_features = np.load('bottleneck_features/DogInceptionV3Data.npz')
train_InceptionV3 = bottleneck_features['train']
valid_InceptionV3 = bottleneck_features['valid']
test_InceptionV3 = bottleneck_features['test']
### Defines the architecture.
inceptionV3_model = Sequential()
inceptionV3_model.add(GlobalAveragePooling2D(input_shape=train_InceptionV3.shape[1:]))
inceptionV3_model.add(Dense(133, activation='softmax'))
inceptionV3_model.summary()
###Compiles the model.
inceptionV3_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
###Trains the model.
from keras.callbacks import ModelCheckpoint
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.inceptionV3.hdf5',
verbose=1, save_best_only=True)
inceptionV3_model.fit(train_InceptionV3, train_targets,
validation_data=(valid_InceptionV3, valid_targets),
epochs=5, batch_size=20, callbacks=[checkpointer], verbose=2)
###Loads the model weights with the best validation loss.
inceptionV3_model.load_weights('saved_models/weights.best.inceptionV3.hdf5')
After training my model, I tested the model on the test dataset of dog images. The result was 79%.
### Calculates classification accuracy on the test dataset.
inceptionV3_predictions = [np.argmax(inceptionV3_model.predict(np.expand_dims(feature, axis=0))) for feature in test_InceptionV3]
# reports test accuracy
test_accuracy = 100*np.sum(np.array(inceptionV3_predictions)==np.argmax(test_targets, axis=1))/len(inceptionV3_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)
Now that I have built my model, I then wrote a function that takes an image path and returns the dog breed.
from extract_bottleneck_features import *
def inceptionV3_predict_breed(img_path):
#extracts bottleneck features
bottleneck_feature = extract_InceptionV3(path_to_tensor(img_path))
#obtain predicted vector
predicted_vector = inceptionV3_model.predict(bottleneck_feature)
# return dog breed that is predicted by the model
return dog_names[np.argmax(predicted_vector)]
def dog_app_breed(img_path):
#first figure out if human or dog using the functions defined earlier
human_found = face_detector(img_path)
dog_found = dog_detector(img_path)
dog_type = inceptionV3_predict_breed(img_path)
#first checks if there is human or dog, else then first provide dog type, then provide human type. Did not include situation for both dog and human.
if not human_found and not dog_found:
print("No humans or dogs were found in the image. Please use another image.")
elif dog_found:
print("The dog looks like a...\n" + dog_type)
else:
print("Hello, your dog look like is a....\n" + dog_type)
I then tested my function on sample dog images. On the 5 images I used, the model was able to recognize correctly the dog breed.
import matplotlib.pyplot as plt
from scipy.misc import imread
def test_dog_identifier(img_path):
print("For image: " + img_path +"\n")
plt.imshow(imread(img_path))
plt.show()
print("\n")
dog_app_breed(img_path)
print("\n")
print("----------------------------------------")
test_dog_identifier('images\American_water_spaniel_00648.jpg')
test_dog_identifier('images\Labrador_retriever_06449.jpg')
test_dog_identifier('images\Welsh_springer_spaniel_08203.jpg')
test_dog_identifier('images\Curly-coated_retriever_03896.jpg')
test_dog_identifier('images\Brittany_02625.jpg')