Are you looking for a simple, flexible, and powerful deep learning library, and build a nice GUI for them? You can deliver enterprise-grade AI solutions easily by combining Keras and Python4Delphi library, inside Delphi and C++Builder.
Keras is a high-level neural networks API for Python. Keras acts as an interface for the TensorFlow library.
Keras is designed for human beings, not machines. Keras follows best practices for reducing cognitive load: It offers consistent and simple APIs, minimizes the number of user actions required for common use cases, and it provides clear and actionable error messages.
It’s popularity? You don’t need to worry! Keras has extensive documentation and developer guides, and also the most used deep learning framework among top-5 winning teams on Kaggle. Keras was also the 10th most cited tool in the KDnuggets 2018 software poll and registered a 22% usage. Keras is used by CERN, NASA, NIH, and many more scientific organizations around the world (and yes, Keras is used at the LHC).
This post will guide you on how to run the Keras library to train neural networks and use Python for Delphi to display it in the Delphi Windows GUI app.
First, open and run our Python GUI using project Demo01
from Python4Delphi with RAD Studio. Then insert the script into the lower Memo
, click the Execute script
button, and get the result in the upper Memo
. You can find the Demo01
source on GitHub. The behind the scene details of how Delphi manages to run your Python code in this amazing Python GUI can be found at this link.
This post will introduce you to how to perform image classification from scratch, starting from JPEG
image files on disk and we will run it in Python GUI. In this tutorial, we will use the famous Kaggle’s Cats vs Dogs binary classification dataset, and we will train neural networks to classify those images.
Table of Contents
1. Get the Dataset
First, let’s download the 786M ZIP archive of the raw data, by using this curl
command in your Windows cmd:
1 |
curl -O https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_3367a.zip |
And unzip
the dataset:
1 |
unzip -q kagglecatsanddogs_3367a.zip |
Now we have a PetImages
folder that contains two subfolders, Cat
and Dog
. Each subfolder contains image files for each category.
2. Import Libraries
1 2 3 |
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers |
If you are a new user of Python4Delphi, Delphi, or RAD Studio, importing these libraries may cause some errors, please read this link to help you to fix them.
3. Filter out the Corrupted Images
When working with lots of real-world image data, corrupted images are a one of the common problems. Let’s filter out badly-encoded images that do not feature the string “JFIF
” in their header, using this code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Filter out corrupted images import os num_skipped = 0 for folder_name in ("Cat", "Dog"): folder_path = os.path.join("PetImages", folder_name) for fname in os.listdir(folder_path): fpath = os.path.join(folder_path, fname) try: fobj = open(fpath, "rb") is_jfif = tf.compat.as_bytes("JFIF") in fobj.peek(10) finally: fobj.close() if not is_jfif: num_skipped += 1 # Delete corrupted image os.remove(fpath) print("Deleted %d images" % num_skipped) |
4. Split the Dataset into the Training and Validation Set
This is the code to split your dataset
into 80%
for training
set, and 20%
for validation
set:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
image_size = (180, 180) batch_size = 32 train_ds = tf.keras.preprocessing.image_dataset_from_directory( "PetImages", validation_split=0.2, subset="training", seed=1337, image_size=image_size, batch_size=batch_size, ) val_ds = tf.keras.preprocessing.image_dataset_from_directory( "PetImages", validation_split=0.2, subset="validation", seed=1337, image_size=image_size, batch_size=batch_size, ) |
5. Visualize the Data
1 2 3 4 5 6 7 8 9 10 11 |
# Visualize the data import matplotlib.pyplot as plt plt.figure(figsize=(10, 10)) for images, labels in train_ds.take(1): for i in range(9): ax = plt.subplot(3, 3, i + 1) plt.imshow(images[i].numpy().astype("uint8")) plt.title(int(labels[i])) plt.axis("off") plt.show() |
This is the results of all operations above, performed in Python GUI by Python4Delphi:
The “0
” label means “Cat
”, while the “1
” label means “Dog
”.
6. Perform Image Data Augmentation
When you don’t have a large image dataset, it’s a good practice to artificially introduce sample diversity by applying random yet realistic transformations to the training images, such as random horizontal flipping
or small random rotations
. This helps expose the model to different aspects of the training data while slowing down overfitting. This is the code for random flip and random rotation operations:
1 2 3 4 5 6 7 |
# Using image data augmentation data_augmentation = keras.Sequential( [ layers.experimental.preprocessing.RandomFlip("horizontal"), layers.experimental.preprocessing.RandomRotation(0.1), ] ) |
Let’s visualize what the augmented samples look like, by applying data_augmentation
repeatedly to the first image in the dataset:
1 2 3 4 5 6 7 8 9 |
# Visualize plt.figure(figsize=(10, 10)) for images, _ in train_ds.take(1): for i in range(9): augmented_images = data_augmentation(images) ax = plt.subplot(3, 3, i + 1) plt.imshow(augmented_images[0].numpy().astype("uint8")) plt.axis("off") plt.show() |
This is the results of the data augmentation, performed in Python GUI by Python4Delphi:
7. Standardize the Data
Our images are already in a standard size (180x180)
, as they are being yielded as contiguous float32
batches by our dataset. However, their RGB channel values are in the [0, 255]
range. This is not ideal for a neural network; in general, you should seek to make your input values small. Here, we will standardize values to be in the [0, 1]
by using a Rescaling
layer at the start of our model.
This tutorial is designed to be performed in a CPU. So the preprocessing for the augmented images will happen asynchronously, and will be buffered before going into the model, like this:
1 2 |
# Preprocess the data by applying it to the dataset (for training in CPU) augmented_train_ds = train_ds.map(lambda x, y: (data_augmentation(x, training=True), y)) |
8. Configure the Dataset for Performance
Let’s make sure to use buffered prefetching so we can yield data from disk without having I/O becoming blocking:
1 2 3 |
# Configure the dataset for performance train_ds = train_ds.prefetch(buffer_size=32) val_ds = val_ds.prefetch(buffer_size=32) |
9. Build a Deep Learning Model
We’ll build a small version of the Xception
network (authored by François Chollet). We haven’t particularly tried to optimize the architecture; if you want to do a systematic search for the best model configuration, consider using Keras Tuner.
Note that:
- We start the model with the
data_augmentation
preprocessor, followed by aRescaling
layer. - We include a
Dropout
layer before the final classification layer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# Build a model def make_model(input_shape, num_classes): inputs = keras.Input(shape=input_shape) # Image augmentation block x = data_augmentation(inputs) # Entry block x = layers.experimental.preprocessing.Rescaling(1.0 / 255)(x) x = layers.Conv2D(32, 3, strides=2, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) x = layers.Conv2D(64, 3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) previous_block_activation = x # Set aside residual for size in [128, 256, 512, 728]: x = layers.Activation("relu")(x) x = layers.SeparableConv2D(size, 3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) x = layers.SeparableConv2D(size, 3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.MaxPooling2D(3, strides=2, padding="same")(x) # Project residual residual = layers.Conv2D(size, 1, strides=2, padding="same")( previous_block_activation ) x = layers.add([x, residual]) # Add back residual previous_block_activation = x # Set aside next residual x = layers.SeparableConv2D(1024, 3, padding="same")(x) x = layers.BatchNormalization()(x) x = layers.Activation("relu")(x) x = layers.GlobalAveragePooling2D()(x) if num_classes == 2: activation = "sigmoid" units = 1 else: activation = "softmax" units = num_classes x = layers.Dropout(0.5)(x) outputs = layers.Dense(units, activation=activation)(x) return keras.Model(inputs, outputs) model = make_model(input_shape=image_size + (3,), num_classes=2) keras.utils.plot_model(model, show_shapes=True) |
10. Train the Model
This is the code to train the deep learning model, with 50
training epochs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Train the model epochs = 50 callbacks = [ keras.callbacks.ModelCheckpoint("save_at_{epoch}.h5"), ] model.compile( optimizer=keras.optimizers.Adam(1e-3), loss="binary_crossentropy", metrics=["accuracy"], ) model.fit( train_ds, epochs=epochs, callbacks=callbacks, validation_data=val_ds, |
Warning: Training this model on a regular laptop will take around 1 until 2.5 hours for each training epoch.
Congratulations, now you have learned how to run the Keras library to train neural networks and use Python for Delphi to display it in the Delphi Windows GUI app! Now you can try lots more sophisticated examples from these documentations using the Keras library and Python4Delphi.
Check out the keras
library for Python and use it in your projects: https://pypi.org/project/Keras/ and
Check out Python4Delphi
which easily allows you to build Python GUIs for Windows using Delphi: https://github.com/pyscripter/python4delphi
References & further readings
[1] Chollet, F. (2022).
Image classification from scratch. Keras documentation. keras.io/examples/vision/image_classification_from_scratch
[2] Keras. (2023).
Code examples. Keras documentation. keras.io/examples
[3] Keras. (2023).
Keras: Simple. Flexible. Powerful. Keras Website. keras.io