Building a Rock-Paper-Scissors Image Classifier with TensorFlow and Keras (99.22% Val Accuracy) | by Ridwan Halim


Donate and support me: Socialbuzz or Saweria

To get started, let’s install the necessary libraries. Run the following command to install wget:

!pip install wget

# output:
# Collecting wget
# Downloading wget-3.2.zip (10 kB)
# Preparing metadata (setup.py) ... done
# Building wheels for collected packages: wget
# Building wheel for wget (setup.py) ... done
# Created wheel for wget: filename=wget-3.2-py3-none-any.whl size=9656 sha256=a380c683fcbf16ff5140515c833ba8e6277e58f6deaea235779681181888be07
# Stored in directory: /root/.cache/pip/wheels/8b/f1/7f/5c94f0a7a505ca1c81cd1d9208ae2064675d97582078e6c769
# Successfully built wget
# Installing collected packages: wget
# Successfully installed wget-3.2

Next, we’ll import the libraries and modules required for building the image classifier:

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
import matplotlib.pyplot as plt
from google.colab import files
import numpy as np
import zipfile
import os
import wget
import glob
import time
import requests
from PIL import Image
from io import BytesIO

Dataset Processor

The DatasetProcessor class handles the downloading, extracting, and preparing of the dataset required for training the model:

class DatasetProcessor:
def __init__(self, url, dataset_zip, dataset_dir):
self.url = url
self.dataset_zip = dataset_zip
self.dataset_dir = dataset_dir

def download_and_extract(self):
wget.download(self.url, self.dataset_zip)
with zipfile.ZipFile(self.dataset_zip, 'r') as zip_ref:
zip_ref.extractall(self.dataset_dir)
print("Dataset downloaded and extracted successfully.")

def prepare_data_generators(self):
base_dir = os.path.join(self.dataset_dir, 'rockpaperscissors', 'rps-cv-images')

if not os.path.exists(base_dir):
raise Exception(f"Base directory {base_dir} does not exist.")

categories = ['rock', 'paper', 'scissors']
for category in categories:
if len(glob.glob(f"{base_dir}/{category}/*")) == 0:
raise Exception(f"No images found in category {category}.")

train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
validation_split=0.4 # 40% for validation
)
self.train_generator = train_datagen.flow_from_directory(
base_dir,
target_size=(150, 150),
batch_size=32,
class_mode='categorical',
subset='training' # Set as training data
)
self.validation_generator = train_datagen.flow_from_directory(
base_dir,
target_size=(150, 150),
batch_size=32,
class_mode='categorical',
subset='validation' # Set as validation data
)
print("Data generators prepared successfully.")
return self.train_generator, self.validation_generator

Model Builder

The ModelBuilder class defines and compiles the CNN model architecture and trains the model using the prepared dataset:

class ModelBuilder:
def __init__(self):
self.model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.5),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(3, activation='softmax')
])

def compile_model(self):
self.model.compile(
loss='categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy']
)
print("Model compiled successfully.")

def train_model(self, train_generator, validation_generator):
start_time = time.time()
self.history = self.model.fit(
train_generator,
steps_per_epoch=train_generator.samples // 32,
epochs=75,
validation_data=validation_generator,
validation_steps=validation_generator.samples // 32,
verbose=2
)
end_time = time.time()
total_time = end_time - start_time
avg_time_per_epoch = total_time / 75

print(f"Total training time : {total_time:.3f} seconds ({total_time / 60:.3f} minutes)")
print(f"Average time per epoch: {avg_time_per_epoch:.3f} seconds/epoch")
print("Model trained successfully.")

def evaluate_model(self):
acc = self.history.history['accuracy'][-1]
val_acc = self.history.history['val_accuracy'][-1]
print(f"Training accuracy : {acc:.5f}")
print(f"Validation accuracy : {val_acc:.5f}")
assert val_acc >= 0.85, "Validation accuracy is below 85%"
print("Model evaluation completed successfully.")

def plot_training_history(self):
# accuracy values
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(self.history.history['accuracy'])
plt.plot(self.history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')

# loss values
plt.subplot(1, 2, 2)
plt.plot(self.history.history['loss'])
plt.plot(self.history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
print("Training history plotted successfully.")

Image Predictor

The ImagePredictor class predicts the class of an uploaded image using the trained model:

class ImagePredictor:
def __init__(self, model, class_labels):
self.model = model
self.class_labels = class_labels

def predict_image(self):
uploaded = files.upload()
for fn in uploaded.keys():
path = fn
img = image.load_img(path, target_size=(150, 150))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor /= 255.
prediction = self.model.predict(img_tensor)
predicted_class = np.argmax(prediction, axis=1)
predicted_label = self.class_labels[predicted_class[0]]

plt.imshow(img)
plt.axis('off')
plt.title(f"Predicted: {predicted_label}")
plt.show()

print(f"The uploaded image is a {predicted_label}")

Process Dataset

In this section, we’ll download, extract, and prepare the dataset for training the model.

40% (train: 1314, validation: 874)

dataset_processor = DatasetProcessor(
url='https://github.com/dicodingacademy/assets/releases/download/release/rockpaperscissors.zip',
dataset_zip='rockpaperscissors.zip',
dataset_dir='rockpaperscissors'
)
dataset_processor.download_and_extract()
train_generator, validation_generator = dataset_processor.prepare_data_generators()

# output:
# Dataset downloaded and extracted successfully.
# Found 1314 images belonging to 3 classes.
# Found 874 images belonging to 3 classes.
# Data generators prepared successfully.

The Model Controller section involves building, compiling, training, and evaluating the CNN model using the prepared dataset.

Build and Compile Model

# Initialize model builder
model_builder = ModelBuilder()
# Compile the model
model_builder.compile_model()

# output:
# Model compiled successfully.

Train Model

model_builder.train_model(train_generator, validation_generator)

# output:
# ...
# Epoch 64/75
# 41/41 - 19s - loss: 0.0244 - accuracy: 0.9922 - val_loss: 0.0501 - val_accuracy: 0.9919 - 19s/epoch - 461ms/step
# Epoch 65/75
# 41/41 - 15s - loss: 0.0319 - accuracy: 0.9922 - val_loss: 0.0591 - val_accuracy: 0.9850 - 15s/epoch - 377ms/step
# ...
# Total training time : 1454.468 seconds (24.241 minutes)
# Average time per epoch: 19.393 seconds/epoch
# Model trained successfully.

Evaluate Model

# Evaluate the model
model_builder.evaluate_model()

# output: this output may difference based on epoch.
# Training accuracy : 0.98128
# Validation accuracy : 0.98611
# Model evaluation completed successfully.

model_builder.plot_training_history()

# output:
# Training history plotted successfully.

Accuracy
# Initialize image predictor
image_predictor = ImagePredictor(model=model_builder.model, class_labels=['rock', 'paper', 'scissors'])
# Predict image
image_predictor.predict_image()

# output:
# No file chosen Upload widget is only available when the cell has been executed in the current browser session. Please rerun this cell to enable.
# Saving scissors.png to scissors.png
# 1/1 [==============================] - 0s 395ms/step
# image
# The uploaded image is a scissors

Scissors
Scissors (hands)

In this tutorial, we’ve built a Rock-Paper-Scissors image classifier using TensorFlow and Keras. We started by collecting and preprocessing the data, then constructed and trained a CNN model. Finally, we evaluated the model and demonstrated how to make predictions on new images. The training process resulted in a final loss of 0.0244, accuracy of 0.9922, validation loss of 0.0501, and validation accuracy of 0.9919, with an average epoch duration of 19 seconds and 461 milliseconds per step. This high accuracy indicates that our model performs exceptionally well on the Rock-Paper-Scissors classification task.

Happy coding!

My Github Repo ipynb: Click here
Colab Google: Click here
Donate: Socialbuzz or Saweria



Source link

Be the first to comment

Leave a Reply

Your email address will not be published.


*