Convolutional Neural Networks (CNNs)
Convolutional Neural Networks (CNNs) represent a groundbreaking advancement in the field of deep learning, particularly for tasks related to computer vision. Unlike traditional neural networks that struggle with spatial relationships in image data, CNNs are specifically designed to efficiently handle grid-like data, making them highly effective for tasks such as image recognition, object detection, and image segmentation.
The key architectural innovation of CNNs lies in the use of convolutional layers. These layers employ convolutional operations to scan input data with learnable filters, capturing hierarchical features from local regions. By utilizing shared weights and biases across the entire input space, CNNs can efficiently recognize patterns, edges, and textures in images, enabling them to learn hierarchical representations of visual information automatically.
The typical CNN architecture consists of multiple convolutional layers, followed by pooling layers to reduce spatial dimensions and decrease computational load. Fully connected layers are often added towards the end to perform classification based on the extracted features.
CNNs have demonstrated remarkable success in various computer vision tasks, including image classification competitions like ImageNet. Their ability to automatically learn hierarchical features from raw pixel values has led to advancements in fields such as medical imaging, autonomous vehicles, and facial recognition. In short, the following table shows the most representative CNN layers.
CNN LAYER | Description |
Convolutional Layer | Applies convolutional operations to input data using filters (kernels) to detect patterns and features. Each filter captures different aspects of the input. |
Pooling Layer | Reduces spatial dimensions of the input by downsampling, commonly using max pooling or average pooling. This helps to decrease computational load and retain essential features. |
Batch Normalization | Normalizes the input of a layer to have zero mean and unit variance, improving convergence and generalization. It is often applied before or after the activation function. |
Activation Layer (ReLU) | Introduces non-linearity by applying the Rectified Linear Unit (ReLU) activation function to the output of a layer, allowing the network to learn complex representations. |
Dropout Layer | Randomly drops a fraction of neurons during training, preventing overfitting and enhancing the network's robustness. It promotes generalization by avoiding reliance on specific neurons. |
Flatten Layer | Converts the multi-dimensional output of the previous layer into a one-dimensional vector, preparing the data for input to fully connected layers. |
Fully Connected Layer | Traditional neural network layer where each neuron is connected to every neuron in the previous and subsequent layers. Often used for classification tasks at the end of the network. |
Global Average Pooling | Computes the average value of each feature map, producing a global representation of the input. It reduces the spatial dimensions while preserving important information. | |
Transfer Learning
Transfer learning is a powerful technique in the realm of machine learning, where knowledge gained from solving one problem is applied to a different but related problem. In the context of deep learning and neural networks, transfer learning involves using pre-trained models on a source task and leveraging their learned representations for a target task.
For CNNs, transfer learning is particularly effective due to the hierarchical nature of learned features. Pre-trained models, often trained on massive datasets, have already acquired rich feature representations that can be valuable for new tasks with limited labeled data.
There are two primary approaches to transfer learning:
Transfer learning is widely employed in various domains, from natural language processing to computer vision. It not only accelerates model training but also enhances performance, especially when dealing with limited labeled data for the target task.
In summary, Convolutional Neural Networks and Transfer Learning are pivotal concepts in contemporary machine learning, playing a crucial role in advancing the state-of-the-art in image-related tasks and beyond. Their combination has proven instrumental in achieving high performance even with limited data, making them indispensable tools in the machine learning practitioner's toolkit
ABOUT THE PROJECT:
The dataset was acquired from tomato crops within a greenhouse, specifically focusing on plants affected by various illnesses. To capture the characteristics of these illnesses, an autonomous robot was deployed to take images of the leaves while following a predefined route. Subsequently, domain experts meticulously annotated the dataset, categorizing the leaves into four distinct labels: Healthy leaves, dried leaves, leaves with stains, and leaves with yellow stains. The dataset, comprising these annotated images, is available for access and exploration at the following Kaggle link: Tomato Leaf Illness Detection Dataset.
Step 1: In the first step, access the dataset from the provided link in the preceding section. To facilitate the organization of your project, three Python functions have been created for unzipping the file into the target folder. You can refer to this tutorial (link) for guidance on structuring your project effectively. Once the dataset is successfully placed in the designated folder, the subsequent function is designed to inspect the contents, listing the folders and files present. Then, the function "get_labels" gets the name and number of samples of each label. Lastly, the final function generates visualizations, specifically plotting three images for each label.
import os
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import random
import numpy as np
from sklearn import manifold
from PIL import Image
import matplotlib.pyplot as plt
import zipfile
import tensorflow as tf
from tensorflow.keras.applications import EfficientNetB0
img_height = 224
img_width = 224
batch_size = 32
epochs = 20
callback = tf.keras.callbacks.EarlyStopping(monitor='val_accuracy', patience=5)
#################################################################
dataset_path = "dataset_path"
train_path = "trainset_path"
test_path = "test_path"
#################################################################
def check_data(dataset):
"""
Check data in a dataset
Args:
dataset: The dataset directory to check
Returns:
None: Does not return anything
- Checks for any errors or invalid files in the dataset
- Logs any errors or issues found with the files to a report
- Returns nothing after completing the check"""
for dirpath, dirnames, filenames in os.walk(dataset):
print(f"There are {len(dirnames)} directories and {len(filenames)} images in '{dirpath}'.")
def get_labels(train_path):
labels=[]
tam = []
for cont, i in enumerate((os.listdir(train_path))):
labels.append(i)
for cont, i in enumerate (labels):
num = len(os.listdir(train_path+"/"+i))
tam.append(num)
print("Label ", labels[cont], ": ", num)
tam_labels = np.array(tam)
return labels, tam_labels
def view_n_images(target_dir, target_class,n):
target_path = target_dir+"/"+target_class
file_names = os.listdir(target_path)
target_images = random.sample(file_names, n)
# Plot images
plt.figure(figsize=(15, 6))
for i, img in enumerate(target_images):
img_path = target_path + "/" + img
plt.subplot(1, 3, i+1)
plt.imshow(mpimg.imread(img_path))
plt.title(target_class)
plt.axis("off")
Step 2: Once the dataset is checked and the path is correctly set, the dataset must be split preferably into three parts: the training set, the validation set, and the test set. Furthermore, Transfer learning usually uses the data augmentation technique to provide better features to the model and avoid overfitting the model. Therefore, two methods were written in this section.
def split_tratin_test_set(path_data_source,batch_size,img_height, img_width):
"""
Splits training and test datasets
Args:
train_dir: Path to training directory
test_dir: Path to test directory
Returns:
train_data: Training dataset
validation_data: Validation dataset
test_data: Test dataset
Splits training images into train and validation sets. Imports test images separately. Converts all images to specified height and width.
- Imports data from train and test directories
- Splits train data into train and validation sets
- Converts images to specified height and width
- Returns train, validation and test datasets
"""
train_dir = path_data_source + "/" + "train"
test_dir = path_data_source + "/" + "test"
# Import data from directories and turn it into batches
train_data = tf.keras.preprocessing.image_dataset_from_directory(train_dir,
seed=123,
label_mode="categorical",
batch_size=batch_size, # number of images to process validation_split=0.2,
subset="training",
image_size=(img_height, img_width))
# convert all images to be 224 x 224
validation_data = tf.keras.preprocessing.image_dataset_from_directory(train_dir,
seed=123,
label_mode="categorical",
batch_size=batch_size, # number of images to process me
validation_split=0.2,
subset="validation",
image_size=(img_height, img_width))
# convert all images to be 224 x 224
test_data = tf.keras.preprocessing.image_dataset_from_directory(test_dir,
seed=123,
label_mode="categorical",
batch_size=batch_size, # number of images to process
image_size=(img_height, img_width))
# convert all images to be 224 x 224
return train_data, validation_data, test_data
.
img_augmentation = tf.keras.models.Sequential(
[
tf.keras.layers.experimental.preprocessing.RandomRotation(0.40),
tf.keras.layers.experimental.preprocessing.RandomTranslation(
height_factor=0.1,
width_factor=0.1),
tf.keras.layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
tf.keras.layers.RandomContrast(
factor=0.2),
],
name="img_augmentation",
)
Step 3. In this tutorial, we used a well-known CNN architecture, which was already pre-trained with a large dataset (imagenet). This model was downloaded from the TensorFlow HUB, where ML models are stored for further applications. In this repository, the whole model or the feature extractor can be found. We recommend using the whole model and adding at the top of the architecture a Dense Layer with the number of labels of the target dataset, in this case is four labels.
def build_model(num_classes, aprov_pre):
"""
Builds a transfer learning model for image classification
Args:
num_classes: {Number of classes for the classification task}
aprov_pre: {Whether to use preprocessing augmentation}
Returns:
model: {The Keras model}
Processing Logic:
- Builds an EfficientNetB0 model with pretrained weights
- Freezes the pretrained weights
- Adds global average pooling, dropout and prediction layers on top
- Compiles the model with Adam optimizer and categorical crossentropy loss
"""
inputs = tf.keras.layers.Input(shape=(img_height, img_width, 3))
if aprov_pre:
x = img_augmentation(inputs)
model = EfficientNetB0(
include_top=False,
input_tensor=x,
weights="imagenet")
print("preprocessing:", aprov_pre)
else:
model = EfficientNetB0(
include_top=False,
input_tensor=inputs,
weights="imagenet")
print("preprocessing:", aprov_pre)
# Freeze the pretrained weights
model.trainable = False
# Rebuild top
x = tf.keras.layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
x = tf.keras.layers.BatchNormalization()(x)
top_dropout_rate = 0.2
x = tf.keras.layers.Dropout(top_dropout_rate, name="top_dropout")(x)
outputs = tf.keras.layers.Dense(
num_classes,
activation="softmax",
name="pred")(x)
# Compile
model = tf.keras.Model(inputs, outputs, name="EfficientNet")
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-2)
model.compile(
optimizer=optimizer,
loss="categorical_crossentropy",
metrics=["accuracy"])
return model
def unfreeze_model(model, num):
"""Unfreeze top layers of a model
Args:
model: The model to unfreeze layers for
num: The number of top layers to unfreeze
Returns:
model: The model with top layers unfrozen
Processing Logic:
- Loop through layers from num to the end of the model
- Set trainable to True for layers that are not BatchNormaliztion layers
- Compile the model with an Adam optimizer with learning rate of 1e-4"""
# We unfreeze the top num layers while leaving BatchNorm layers frozen
for layer in model.layers[num:]:
if not isinstance(layer, tf.keras.layers.BatchNormalization):
layer.trainable = True
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
model.compile(
optimizer=optimizer, loss="binary_crossentropy", metrics=["accuracy"]
).
Step 4. Since the model was built in the previous step, it is time to be trained with the new dataset. In this step, all code is merged, and the next step also was called here to test and evaluate the model.
def train_model(model, train_data, validation_data, test_data, callback, path_model_destination,epochs, name):
start = datetime.now()
history = model.fit(train_data,
epochs=epochs,
steps_per_epoch=len(train_data),
validation_data=validation_data,
# Go through less of the validation data so epochs are
# faster (we want faster experiments!)
validation_steps=int(len(validation_data)),
callbacks=[callback],
verbose=1,
)
end = datetime.now()
# find difference loop start and end time and display
td = (end - start)
model.save(
path_model_destination +
name +
".h5")
print("Exceuction time:", td)
results(model, test_data, name, path_model_destination )
plot_loss_curves(history, name, path_model_destination)
return model, history
Step 5. Once the model was trained, it can be tested by using the test set. If the results are promising, the model also could be saved for production. Finally, some methods aim to print some figures about the model's performance. Remember that this step was embedded in the previous step.
def results(model, test_data, name,path_model_destination):
"""
Calculates model performance metrics on test data
Args:
model: The trained model
test_data: The test data
name: The name of the model
Returns:
None: Does not return anything, just prints metrics
Processing Logic:
- Predicts classes for test data using model
- Extracts true and predicted classes
- Calculates precision, recall, f1 score and mean absolute error
- Prints classification report and confusion matrix
- Plots and saves confusion matrix
"""
y_pred = []
results = model.predict(test_data)
for i in results:
y_pred.append(np.argmax(i))
y_test = []
for test_image, test_label in test_data:
for t in test_label:
print(np.array(t))
y_test.append(np.argmax(t))
print("")
print(
"Precision: {}%".format(
100 *
metrics.precision_score(
y_test,
y_pred,
average="weighted")))
print(
"Recall: {}%".format(
100 *
metrics.recall_score(
y_test,
y_pred,
average="weighted")))
print(
"f1_score: {}%".format(
100 *
metrics.f1_score(
y_test,
y_pred,
average="weighted")))
print("Error: {}%".format(metrics.mean_absolute_error(y_test, y_pred)))
report = classification_report(y_test, y_pred)
matrix = confusion_matrix(y_test, y_pred)
print('\\Report\n')
print(report)
ax = plt.plot()
ax = sns.heatmap(matrix, annot=True, cmap='Blues')
ax.set_title('Confusion Matrix with labels\n\n')
ax.set_xlabel('\nPredicted Values')
ax.set_ylabel('Actual Values ')
plt.savefig(
path_model_destination +
"cf_efficient_" +
str(name) +
".png")
# Display the visualization of the Confusion Matrix.
plt.show()
def plot_loss_curves(history, name, path_model_destination):
"""
Plots loss and accuracy curves from the Keras model history
Args:
history: Keras model history object
name: Name of the model
Returns:
None: Does not return anything, saves figures to file
Processing Logic:
- Extracts loss and accuracy values from history
- Plots loss vs epochs and saves figure
- Plots accuracy vs epochs and saves figure
"""
loss = history.history['loss']
val_loss = history.history['val_loss']
accuracy = history.history['accuracy']
val_accuracy = history.history['val_accuracy']
epochs = range(len(history.history['loss']))
# Plot loss
plt.plot(epochs, loss, label='training_loss')
plt.plot(epochs, val_loss, label='val_loss')
plt.title('Loss')
plt.xlabel('Epochs')
plt.legend()
plt.savefig(path_model_destination +
"Loss_" +
str(name) + ".png")
# Plot accuracy
plt.figure()
plt.plot(epochs, accuracy, label='training_accuracy')
plt.plot(epochs, val_accuracy, label='val_accuracy')
plt.title('Accuracy')
plt.xlabel('Epochs')
plt.legend()
plt.savefig(path_model_destination +
"ACC_" +
str(name) + ".png")
3These functions can run in different Python environments, and they can be called in this way:
check_data (dataset_path)
Out:
There are 2 directories and 0 images in 'your_path'.
There are 4 directories and 0 images in 'your_path/dataset/test'.
There are 0 directories and 24 images in 'your_path/dataset/test\dried_leaves'.
There are 0 directories and 27 images in 'your_path/dataset/test\healthy_leaves'.
There are 0 directories and 21 images in 'your_path/dataset/test\leaves_with_stains'.
There are 0 directories and 28 images in 'your_path/dataset/test\leaves_yellow_stains'.
There are 4 directories and 0 images in 'your_path/dataset/train'.
There are 0 directories and 99 images in 'your_path/dataset/train\dried_leaves'.
There are 0 directories and 108 images in 'your_path/dataset/train\healthy_leaves'.
There are 0 directories and 64 images in 'your_path/dataset/train\leaves_with_stains'.
There are 0 directories and 113 images in ''your_path/dataset/train\leaves_yellow_stains'.
labels, tam_labels = get_labels (train_path)
Out:
Label dried_leaves : 99
Label healthy_leaves : 108
Label leaves_with_stains : 64
Label leaves_yellow_stains : 113
for i in labels:
view_n_images (train_path, i, 3)
Out:




train_data, validation_data, test_data = split_train_test_set(path_data_source,batch_size,img_height, img_width)
model = build_model(4, False) # we don't want to use data augmentation, otherwise set in True
train_model(model, train_data, validation_data, test_data, callback, 'your_model_destination_path',epochs, "Efficient_model")
Train a fine-tunned model with data augmentation.
model = build_model(4, True)
fine_tunned_model = unfreeze_model(model, 20) # unfreeze the last 20 layers
train_model(fine_tunned_model,train_data,validation_data, test_data, callback, 'your_model_destination_path',epochs, "Efficient_model")
If you are interested in digging into this topic, you can follow this project: Transfer Learning Benchmarking with IoT data