[K3GTSRB2] - First convolutions¶
Episode 2 : First convolutions and first classification of our traffic signs, using Keras3Objectives :¶
- Recognizing traffic signs
- Understand the principles and architecture of a convolutional neural network for image classification
The German Traffic Sign Recognition Benchmark (GTSRB) is a dataset with more than 50,000 photos of road signs from about 40 classes.
The final aim is to recognise them !
Description is available there : http://benchmark.ini.rub.de/?section=gtsrb&subsection=dataset
IMPORTANT : To be able to use this notebook and the following, you must have generated the enhanced datasets in <dataset_dir>/enhanced via the notebook 01-Preparation-of-data.ipynb
What we're going to do :¶
- Read H5 dataset
- Build a model
- Train the model
- Evaluate the model
Step 1 - Import and init¶
1.1 - Python stuff¶
import os
os.environ['KERAS_BACKEND'] = 'torch'
import keras
import numpy as np
import matplotlib.pyplot as plt
import h5py
import os,time,sys
from importlib import reload
# Init Fidle environment
import fidle
run_id, run_dir, datasets_dir = fidle.init('K3GTSRB2')
FIDLE - Environment initialization
Version : 2.3.0 Run id : K3GTSRB2 Run dir : ./run/K3GTSRB2 Datasets dir : /gpfswork/rech/mlh/uja62cb/fidle-project/datasets-fidle Start time : 03/03/24 21:17:18 Hostname : r3i7n8 (Linux) Tensorflow log level : Warning + Error (=1) Update keras cache : False Update torch cache : False Save figs : ./run/K3GTSRB2/figs (True) keras : 3.0.4 numpy : 1.24.4 sklearn : 1.3.2 yaml : 6.0.1 matplotlib : 3.8.2 pandas : 2.1.3 torch : 2.1.1
1.2 - Parameters¶
scale
is the proportion of the dataset that will be used during the training. (1 mean 100%)
A 20% 24x24 dataset, with 5 epochs and a scale of 1, need 3'30 on a CPU laptop.
fit_verbosity
is the verbosity during training : 0 = silent, 1 = progress bar, 2 = one line per epoch
enhanced_dir = './data'
# enhanced_dir = f'{datasets_dir}/GTSRB/enhanced'
dataset_name = 'set-24x24-L'
batch_size = 64
epochs = 5
scale = 1
fit_verbosity = 1
Override parameters (batch mode) - Just forget this cell
fidle.override('enhanced_dir', 'dataset_name', 'batch_size', 'epochs', 'scale', 'fit_verbosity')
** Overrided parameters : ** enhanced_dir : /gpfswork/rech/mlh/uja62cb/fidle-project/datasets-fidle/GTSRB/enhanced dataset_name : set-24x24-L batch_size : 64 epochs : 5 scale : 1 fit_verbosity : 2
Step 2 - Load dataset¶
We're going to retrieve a previously recorded dataset.
For example: set-24x24-L
def read_dataset(enhanced_dir, dataset_name, scale=1):
'''
Reads h5 dataset
Args:
filename : datasets filename
dataset_name : dataset name, without .h5
Returns:
x_train,y_train, x_test,y_test data, x_meta,y_meta
'''
# ---- Read dataset
#
chrono=fidle.Chrono()
chrono.start()
filename = f'{enhanced_dir}/{dataset_name}.h5'
with h5py.File(filename,'r') as f:
x_train = f['x_train'][:]
y_train = f['y_train'][:]
x_test = f['x_test'][:]
y_test = f['y_test'][:]
x_meta = f['x_meta'][:]
y_meta = f['y_meta'][:]
# ---- Rescale
#
print('Original shape :', x_train.shape, y_train.shape)
x_train,y_train, x_test,y_test = fidle.utils.rescale_dataset(x_train,y_train,x_test,y_test, scale=scale)
print('Rescaled shape :', x_train.shape, y_train.shape)
# ---- Shuffle
#
x_train,y_train=fidle.utils.shuffle_np_dataset(x_train,y_train)
# ---- done
#
duration = chrono.get_delay()
size = fidle.utils.hsize(os.path.getsize(filename))
print(f'\nDataset "{dataset_name}" is loaded and shuffled. ({size} in {duration})')
return x_train,y_train, x_test,y_test, x_meta,y_meta
# ---- Read dataset
#
x_train,y_train,x_test,y_test, x_meta,y_meta = read_dataset(enhanced_dir, dataset_name, scale)
Original shape : (39209, 24, 24, 1) (39209,) Datasets have been resized with a factor 1 Rescaled shape : (39209, 24, 24, 1) (39209,) Datasets have been shuffled. Dataset "set-24x24-L" is loaded and shuffled. (114.6 Mo in 0:00:00)
Step 3 - Have a look to the dataset¶
We take a quick look as we go by...
print("x_train : ", x_train.shape)
print("y_train : ", y_train.shape)
print("x_test : ", x_test.shape)
print("y_test : ", y_test.shape)
fidle.scrawler.images(x_train, y_train, range(12), columns=6, x_size=2, y_size=2, save_as='01-dataset-medium')
fidle.scrawler.images(x_train, y_train, range(36), columns=12, x_size=1, y_size=1, save_as='02-dataset-small')
x_train : (39209, 24, 24, 1) y_train : (39209,) x_test : (12630, 24, 24, 1) y_test : (12630,)
# ------------------------------------------------------------------
# -- A simple model, for 24x24 or 48x48 images --
# ------------------------------------------------------------------
#
def get_model_01(lx,ly,lz):
model = keras.models.Sequential()
model.add( keras.layers.Input((lx,ly,lz)) )
model.add( keras.layers.Conv2D(96, (3,3), activation='relu' ))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.2))
model.add( keras.layers.Conv2D(192, (3, 3), activation='relu'))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.2))
model.add( keras.layers.Flatten())
model.add( keras.layers.Dense(1500, activation='relu'))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Dense(43, activation='softmax'))
return model
# ------------------------------------------------------------------
# -- A more sophisticated model, for 48x48 images --
# ------------------------------------------------------------------
#
def get_model_02(lx,ly,lz):
model = keras.models.Sequential()
model.add( keras.layers.Input((lx,ly,lz)) )
model.add( keras.layers.Conv2D(32, (3,3), activation='relu'))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Conv2D(64, (3, 3), activation='relu'))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Conv2D(128, (3, 3), activation='relu'))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Conv2D(256, (3, 3), activation='relu'))
model.add( keras.layers.MaxPooling2D((2, 2)))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Flatten())
model.add( keras.layers.Dense(1152, activation='relu'))
model.add( keras.layers.Dropout(0.5))
model.add( keras.layers.Dense(43, activation='softmax'))
return model
Step 5 - Train the model¶
Get the shape of my data :
(n,lx,ly,lz) = x_train.shape
print("Images of the dataset have this folowing shape : ",(lx,ly,lz))
Images of the dataset have this folowing shape : (24, 24, 1)
Get and compile a model, with the data shape :
model = get_model_01(lx,ly,lz)
model.summary()
model.compile(optimizer = 'adam',
loss = 'sparse_categorical_crossentropy',
metrics = ['accuracy'])
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 22, 22, 96) │ 960 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 11, 11, 96) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dropout (Dropout) │ (None, 11, 11, 96) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ conv2d_1 (Conv2D) │ (None, 9, 9, 192) │ 166,080 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 4, 4, 192) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dropout_1 (Dropout) │ (None, 4, 4, 192) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ flatten (Flatten) │ (None, 3072) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense (Dense) │ (None, 1500) │ 4,609,500 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dropout_2 (Dropout) │ (None, 1500) │ 0 │ ├─────────────────────────────────┼───────────────────────────┼────────────┤ │ dense_1 (Dense) │ (None, 43) │ 64,543 │ └─────────────────────────────────┴───────────────────────────┴────────────┘
Total params: 4,841,083 (18.47 MB)
Trainable params: 4,841,083 (18.47 MB)
Non-trainable params: 0 (0.00 B)
Train it :
chrono=fidle.Chrono()
chrono.start()
# ---- Shuffle train data
x_train,y_train=fidle.utils.shuffle_np_dataset(x_train,y_train)
# ---- Train
history = model.fit( x_train, y_train,
batch_size = batch_size,
epochs = epochs,
verbose = fit_verbosity,
validation_data = (x_test, y_test))
chrono.show()
Datasets have been shuffled. Epoch 1/5 613/613 - 12s - 20ms/step - accuracy: 0.6491 - loss: 1.2700 - val_accuracy: 0.8642 - val_loss: 0.5297 Epoch 2/5 613/613 - 11s - 18ms/step - accuracy: 0.9273 - loss: 0.2465 - val_accuracy: 0.8990 - val_loss: 0.4158 Epoch 3/5 613/613 - 11s - 19ms/step - accuracy: 0.9609 - loss: 0.1343 - val_accuracy: 0.9207 - val_loss: 0.3134 Epoch 4/5 613/613 - 11s - 18ms/step - accuracy: 0.9723 - loss: 0.0942 - val_accuracy: 0.9348 - val_loss: 0.2718 Epoch 5/5 613/613 - 11s - 19ms/step - accuracy: 0.9777 - loss: 0.0740 - val_accuracy: 0.9428 - val_loss: 0.2570 Duration : 57.96 seconds
Step 5 - Evaluate¶
max_val_accuracy = max(history.history["val_accuracy"])
print("Max validation accuracy is : {:.4f}".format(max_val_accuracy))
Max validation accuracy is : 0.9428
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss : {:5.4f}'.format(score[0]))
print('Test accuracy : {:5.4f}'.format(score[1]))
Test loss : 0.2566 Test accuracy : 0.9428
fidle.end()
End time : 03/03/24 21:18:26
Duration : 00:01:09 557ms
This notebook ends here :-)
https://fidle.cnrs.fr
- Try the different models
- Try with different datasets
- Test different hyperparameters (epochs, batch size, optimization, etc.)
- Create your own model