Thinkful Capstone 4
In this final Capstone I was responsible for creating a deep learning model that would analyze images passed to it. In this Capstone I decided to process images that represent chickens, and not chickens and determine whether VGG16 or ResNet50 would be better to use.
I hope to eventually use this at home where I have chickens, and eventually other animals, and would like to process images through an at home security system and my home lab. The below PowerPoint compares the models and can also be found here on Github.
Below is my notebook, and the Github page can be viewed here.
# Import TensorFlow and Keras libraries necessary for building neural networks.
# Load VGG16 and ResNet50 models pre-trained on ImageNet dataset for transfer learning.
# Import ImageDataGenerator for easy data manipulation and augmentation.
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.applications import VGG16, ResNet50
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import numpy as np
from math import ceil
# Setup image data generators for training and validation sets with rescaling and a 20% data split for validation.
# Configure data paths, target image size, batch size, and type of classification (binary).
# Initialize VGG16 and ResNet50 with pre-trained ImageNet weights, set as non-trainable to retain learned features.
# Extend these models by adding a flatten layer and two dense layers for the classification task.
# Compile the models using binary cross-entropy loss and Adam optimizer for training.
train_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)
train_generator = train_datagen.flow_from_directory(
'/content/drive/MyDrive/chicken_photos/',
target_size=(224, 224),
batch_size=32,
class_mode='binary',
subset='training')
validation_generator = train_datagen.flow_from_directory(
'/content/drive/MyDrive/chicken_photos/',
target_size=(224, 224),
batch_size=32,
class_mode='binary',
subset='validation')
# Calculate steps per epoch and validation steps
steps_per_epoch = train_generator.n // train_generator.batch_size
validation_steps = validation_generator.n // validation_generator.batch_size
# Load pre-trained VGG16 model
vgg16_base = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
vgg16_base.trainable = False # Freeze the convolutional base
# Add custom layers
vgg16_model = models.Sequential([
vgg16_base,
layers.Flatten(),
layers.Dense(256, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
# Load pre-trained ResNet50 model
resnet50_base = ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
resnet50_base.trainable = False # Freeze the convolutional base
# Add custom layers
resnet50_model = models.Sequential([
resnet50_base,
layers.Flatten(),
layers.Dense(256, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
# Compile VGG16 model
vgg16_model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# Compile ResNet50 model
resnet50_model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# Train the VGG16 model
vgg16_history = vgg16_model.fit(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=10,
validation_data=validation_generator,
validation_steps=validation_steps)
# Train the ResNet50 model
resnet50_history = resnet50_model.fit(
train_generator,
steps_per_epoch=steps_per_epoch,
epochs=10,
validation_data=validation_generator,
validation_steps=validation_steps)
Output confirms successful reading of 332 training images and 332 validation images from the specified directory, categorized into 2 classes.
Found 779 images belonging to 2 classes.
Found 193 images belonging to 2 classes.
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5
58889256/58889256 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/resnet/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5
94765736/94765736 [==============================] - 1s 0us/step
Epoch 1/10
24/24 [==============================] - 712s 29s/step - loss: 0.4888 - accuracy: 0.8849 - val_loss: 0.1331 - val_accuracy: 0.9844
Epoch 2/10
24/24 [==============================] - 676s 28s/step - loss: 0.0213 - accuracy: 0.9920 - val_loss: 0.1705 - val_accuracy: 0.9740
Epoch 3/10
24/24 [==============================] - 742s 31s/step - loss: 0.0047 - accuracy: 1.0000 - val_loss: 0.1492 - val_accuracy: 0.9844
Epoch 4/10
24/24 [==============================] - 684s 29s/step - loss: 0.0020 - accuracy: 1.0000 - val_loss: 0.1600 - val_accuracy: 0.9844
Epoch 5/10
24/24 [==============================] - 678s 28s/step - loss: 0.0014 - accuracy: 1.0000 - val_loss: 0.1593 - val_accuracy: 0.9844
Epoch 6/10
24/24 [==============================] - 677s 28s/step - loss: 0.0011 - accuracy: 1.0000 - val_loss: 0.1557 - val_accuracy: 0.9844
Epoch 7/10
24/24 [==============================] - 674s 28s/step - loss: 9.4978e-04 - accuracy: 1.0000 - val_loss: 0.1600 - val_accuracy: 0.9844
Epoch 8/10
24/24 [==============================] - 685s 29s/step - loss: 7.3625e-04 - accuracy: 1.0000 - val_loss: 0.1613 - val_accuracy: 0.9844
Epoch 9/10
24/24 [==============================] - 687s 29s/step - loss: 7.1922e-04 - accuracy: 1.0000 - val_loss: 0.1608 - val_accuracy: 0.9844
Epoch 10/10
24/24 [==============================] - 742s 31s/step - loss: 5.8474e-04 - accuracy: 1.0000 - val_loss: 0.1571 - val_accuracy: 0.9844
Epoch 1/10
24/24 [==============================] - 306s 12s/step - loss: 4.3146 - accuracy: 0.5408 - val_loss: 0.5013 - val_accuracy: 0.7552
Epoch 2/10
24/24 [==============================] - 307s 13s/step - loss: 0.5852 - accuracy: 0.7403 - val_loss: 0.5043 - val_accuracy: 0.7396
Epoch 3/10
24/24 [==============================] - 265s 11s/step - loss: 0.3710 - accuracy: 0.8514 - val_loss: 0.2887 - val_accuracy: 0.8906
Epoch 4/10
24/24 [==============================] - 272s 11s/step - loss: 0.3227 - accuracy: 0.8728 - val_loss: 0.2745 - val_accuracy: 0.8958
Epoch 5/10
24/24 [==============================] - 300s 13s/step - loss: 0.3542 - accuracy: 0.8380 - val_loss: 0.2944 - val_accuracy: 0.8854
Epoch 6/10
24/24 [==============================] - 299s 12s/step - loss: 0.2947 - accuracy: 0.8822 - val_loss: 0.3692 - val_accuracy: 0.8438
Epoch 7/10
24/24 [==============================] - 285s 12s/step - loss: 0.2595 - accuracy: 0.9036 - val_loss: 0.2806 - val_accuracy: 0.8906
Epoch 8/10
24/24 [==============================] - 291s 12s/step - loss: 0.2360 - accuracy: 0.9130 - val_loss: 0.2982 - val_accuracy: 0.8958
Epoch 9/10
24/24 [==============================] - 258s 11s/step - loss: 0.2192 - accuracy: 0.9170 - val_loss: 0.2874 - val_accuracy: 0.9062
Epoch 10/10
24/24 [==============================] - 265s 11s/step - loss: 0.2329 - accuracy: 0.9143 - val_loss: 0.2869 - val_accuracy: 0.9062
# Train the VGG16 and ResNet50 models using the training data with specified steps per epoch.
# Evaluate the models using the validation data to understand their performance.
# Display detailed classification reports for both models, showing precision, recall, and F1-score.
test_directory_path = '/content/drive/MyDrive/test_photos/'
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
test_directory_path,
target_size=(224, 224),
batch_size=32,
class_mode='binary',
shuffle=False) # No need to shuffle test data
# Check if the test generator has loaded any data
if test_generator.n == 0:
raise ValueError("No images found in test directory")
# Calculate the correct number of steps for the predict method
test_steps = ceil(test_generator.n / test_generator.batch_size)
# Predict with VGG16 model
vgg16_predictions = vgg16_model.predict(test_generator, steps=test_steps)
# Predict with ResNet50 model
resnet50_predictions = resnet50_model.predict(test_generator, steps=test_steps)
# Flatten predictions and truncate to match the number of labels
vgg16_predictions = vgg16_predictions.flatten()[:test_generator.n]
resnet50_predictions = resnet50_predictions.flatten()[:test_generator.n]
# Convert predictions to binary (0 or 1)
vgg16_predictions = [1 if x > 0.5 else 0 for x in vgg16_predictions]
resnet50_predictions = [1 if x > 0.5 else 0 for x in resnet50_predictions]
# True labels
true_labels = test_generator.classes
# Ensure the predictions and labels have the same length
assert len(vgg16_predictions) == len(true_labels), "Mismatch in VGG16 samples"
assert len(resnet50_predictions) == len(true_labels), "Mismatch in ResNet50 samples"
# Evaluate models
print("VGG16 Model Performance:")
print(classification_report(true_labels, vgg16_predictions))
print("ResNet50 Model Performance:")
print(classification_report(true_labels, resnet50_predictions))
Output shows training process details for both models and their final classification metrics on the validation set.
VGG16 model achieves around 85% accuracy, while ResNet50 model has about 71% accuracy, with detailed performance stats per class.
Found 332 images belonging to 2 classes.
11/11 [==============================] - 216s 19s/step
11/11 [==============================] - 72s 6s/step
VGG16 Model Performance:
precision recall f1-score support
0 1.00 0.64 0.78 143
1 0.79 1.00 0.88 189
accuracy 0.85 332
macro avg 0.89 0.82 0.83 332
weighted avg 0.88 0.85 0.84 332
ResNet50 Model Performance:
precision recall f1-score support
0 0.90 0.36 0.52 143
1 0.67 0.97 0.79 189
accuracy 0.71 332
macro avg 0.78 0.67 0.65 332
weighted avg 0.77 0.71 0.67 332