Note
Go to the end to download the full example code.
Age estimation (regression) example
This tutorial aims to demonstrate the comparable accuracy of the Akida-compatible model to the traditional Keras model in performing an age estimation task.
It uses the UTKFace dataset, which includes images of faces and age labels, to showcase how well akida compatible model can predict the ages of individuals based on their facial features.
1. Load the UTKFace Dataset
The UTKFace dataset has 20,000+ diverse face images spanning 0 to 116 years. It includes age, gender, ethnicity annotations. This dataset is useful for various tasks like age estimation, face detection, and more.
Load the dataset from Brainchip data server using the load_data helper (decode JPEG images and load the associated labels).
from akida_models.utk_face.preprocessing import load_data
# Load the dataset
x_train, y_train, x_test, y_test = load_data()
Downloading data from https://data.brainchip.com/dataset-mirror/utk_face/UTKFace_preprocessed.tar.gz.
0/48742400 [..............................] - ETA: 0s
98304/48742400 [..............................] - ETA: 24s
614400/48742400 [..............................] - ETA: 7s
2105344/48742400 [>.............................] - ETA: 3s
3366912/48742400 [=>............................] - ETA: 3s
6463488/48742400 [==>...........................] - ETA: 1s
7897088/48742400 [===>..........................] - ETA: 1s
9371648/48742400 [====>.........................] - ETA: 1s
10944512/48742400 [=====>........................] - ETA: 1s
12566528/48742400 [======>.......................] - ETA: 1s
14180352/48742400 [=======>......................] - ETA: 1s
15532032/48742400 [========>.....................] - ETA: 1s
16834560/48742400 [=========>....................] - ETA: 1s
17252352/48742400 [=========>....................] - ETA: 1s
18317312/48742400 [==========>...................] - ETA: 1s
21413888/48742400 [============>.................] - ETA: 1s
22503424/48742400 [============>.................] - ETA: 0s
23912448/48742400 [=============>................] - ETA: 0s
25264128/48742400 [==============>...............] - ETA: 0s
26542080/48742400 [===============>..............] - ETA: 0s
27836416/48742400 [================>.............] - ETA: 0s
29073408/48742400 [================>.............] - ETA: 0s
30564352/48742400 [=================>............] - ETA: 0s
31711232/48742400 [==================>...........] - ETA: 0s
33030144/48742400 [===================>..........] - ETA: 0s
34242560/48742400 [====================>.........] - ETA: 0s
35241984/48742400 [====================>.........] - ETA: 0s
36577280/48742400 [=====================>........] - ETA: 0s
37814272/48742400 [======================>.......] - ETA: 0s
39075840/48742400 [=======================>......] - ETA: 0s
40443904/48742400 [=======================>......] - ETA: 0s
41517056/48742400 [========================>.....] - ETA: 0s
42336256/48742400 [=========================>....] - ETA: 0s
43728896/48742400 [=========================>....] - ETA: 0s
45137920/48742400 [==========================>...] - ETA: 0s
46301184/48742400 [===========================>..] - ETA: 0s
47693824/48742400 [============================>.] - ETA: 0s
48742400/48742400 [==============================] - 2s 0us/step
Download complete.
Akida models accept only uint8 tensors as inputs. Use uint8 raw data for Akida performance evaluation.
# For Akida inference, use uint8 raw data
x_test_akida = x_test.astype('uint8')
2. Load a pre-trained native Keras model
The model is a simplified version inspired from VGG architecture. It consists of a succession of convolutional and pooling layers and ends with two dense layers that outputs a single value corresponding to the estimated age.
The performance of the model is evaluated using the “Mean Absolute Error” (MAE). The MAE, used as a metric in regression problem, is calculated as an average of absolute differences between the target values and the predictions. The MAE is a linear score, i.e. all the individual differences are equally weighted in the average.
from akida_models import fetch_file
from tensorflow.keras.models import load_model
# Retrieve the model file from the BrainChip data server
model_file = fetch_file(fname="vgg_utk_face.h5",
origin="https://data.brainchip.com/models/AkidaV2/vgg/vgg_utk_face.h5",
cache_subdir='models')
# Load the native Keras pre-trained model
model_keras = load_model(model_file)
model_keras.summary()
Downloading data from https://data.brainchip.com/models/AkidaV2/vgg/vgg_utk_face.h5.
0/557632 [..............................] - ETA: 0s
122880/557632 [=====>........................] - ETA: 0s
557632/557632 [==============================] - 0s 0us/step
Download complete.
Model: "vgg_utk_face"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input (InputLayer) [(None, 32, 32, 3)] 0
rescaling (Rescaling) (None, 32, 32, 3) 0
conv_0 (Conv2D) (None, 30, 30, 32) 864
conv_0/BN (BatchNormalizat (None, 30, 30, 32) 128
ion)
conv_0/relu (ReLU) (None, 30, 30, 32) 0
conv_1 (Conv2D) (None, 30, 30, 32) 9216
conv_1/maxpool (MaxPooling (None, 15, 15, 32) 0
2D)
conv_1/BN (BatchNormalizat (None, 15, 15, 32) 128
ion)
conv_1/relu (ReLU) (None, 15, 15, 32) 0
dropout_3 (Dropout) (None, 15, 15, 32) 0
conv_2 (Conv2D) (None, 15, 15, 64) 18432
conv_2/BN (BatchNormalizat (None, 15, 15, 64) 256
ion)
conv_2/relu (ReLU) (None, 15, 15, 64) 0
conv_3 (Conv2D) (None, 15, 15, 64) 36864
conv_3/maxpool (MaxPooling (None, 8, 8, 64) 0
2D)
conv_3/BN (BatchNormalizat (None, 8, 8, 64) 256
ion)
conv_3/relu (ReLU) (None, 8, 8, 64) 0
dropout_4 (Dropout) (None, 8, 8, 64) 0
conv_4 (Conv2D) (None, 8, 8, 84) 48384
conv_4/BN (BatchNormalizat (None, 8, 8, 84) 336
ion)
conv_4/relu (ReLU) (None, 8, 8, 84) 0
conv_4/global_avg (GlobalA (None, 84) 0
veragePooling2D)
dropout_5 (Dropout) (None, 84) 0
dense_1 (Dense) (None, 64) 5376
dense_1/BN (BatchNormaliza (None, 64) 256
tion)
dense_1/relu (ReLU) (None, 64) 0
dense_2 (Dense) (None, 1) 65
=================================================================
Total params: 120561 (470.94 KB)
Trainable params: 119881 (468.29 KB)
Non-trainable params: 680 (2.66 KB)
_________________________________________________________________
# Compile the native Keras model (required to evaluate the MAE)
model_keras.compile(optimizer='Adam', loss='mae')
# Check Keras model performance
mae_keras = model_keras.evaluate(x_test, y_test, verbose=0)
print("Keras MAE: {0:.4f}".format(mae_keras))
Keras MAE: 6.0806
3. Load a pre-trained quantized Keras model
The above native Keras model is quantized and fine-tuned (QAT). The first convolutional layer of our model uses 8-bit weights, other layers are quantized using 4-bit weights, all activations are 4-bit.
from akida_models import vgg_utk_face_pretrained
# Load the pre-trained quantized model
model_quantized_keras = vgg_utk_face_pretrained()
model_quantized_keras.summary()
Downloading data from https://data.brainchip.com/models/AkidaV2/vgg/vgg_utk_face_i8_w4_a4.h5.
0/553784 [..............................] - ETA: 0s
114688/553784 [=====>........................] - ETA: 0s
553784/553784 [==============================] - 0s 0us/step
Download complete.
Model: "vgg_utk_face"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input (InputLayer) [(None, 32, 32, 3)] 0
rescaling (QuantizedRescal (None, 32, 32, 3) 0
ing)
conv_0 (QuantizedConv2D) (None, 30, 30, 32) 896
conv_0/relu (QuantizedReLU (None, 30, 30, 32) 64
)
conv_1 (QuantizedConv2D) (None, 30, 30, 32) 9248
conv_1/maxpool (QuantizedM (None, 15, 15, 32) 0
axPool2D)
conv_1/relu (QuantizedReLU (None, 15, 15, 32) 64
)
dropout_3 (QuantizedDropou (None, 15, 15, 32) 0
t)
conv_2 (QuantizedConv2D) (None, 15, 15, 64) 18496
conv_2/relu (QuantizedReLU (None, 15, 15, 64) 128
)
conv_3 (QuantizedConv2D) (None, 15, 15, 64) 36928
conv_3/maxpool (QuantizedM (None, 8, 8, 64) 0
axPool2D)
conv_3/relu (QuantizedReLU (None, 8, 8, 64) 128
)
dropout_4 (QuantizedDropou (None, 8, 8, 64) 0
t)
conv_4 (QuantizedConv2D) (None, 8, 8, 84) 48468
conv_4/relu (QuantizedReLU (None, 8, 8, 84) 0
)
conv_4/global_avg (Quantiz (None, 84) 2
edGlobalAveragePooling2D)
dropout_5 (QuantizedDropou (None, 84) 0
t)
dense_1 (QuantizedDense) (None, 64) 5440
dense_1/relu (QuantizedReL (None, 64) 2
U)
dense_2 (QuantizedDense) (None, 1) 65
dequantizer (Dequantizer) (None, 1) 0
=================================================================
Total params: 119929 (468.47 KB)
Trainable params: 119541 (466.96 KB)
Non-trainable params: 388 (1.52 KB)
_________________________________________________________________
# Compile the quantized Keras model (required to evaluate the MAE)
model_quantized_keras.compile(optimizer='Adam', loss='mae')
# Check Keras model performance
mae_quant = model_quantized_keras.evaluate(x_test, y_test, verbose=0)
print("Keras MAE: {0:.4f}".format(mae_quant))
Keras MAE: 5.8969
4. Conversion to Akida
The quantized Keras model is now converted into an Akida model. After conversion, we evaluate the performance on the UTKFace dataset.
from cnn2snn import convert
# Convert the model
model_akida = convert(model_quantized_keras)
model_akida.summary()
Model Summary
______________________________________________
Input shape Output shape Sequences Layers
==============================================
[32, 32, 3] [1, 1, 1] 1 8
______________________________________________
_________________________________________________________
Layer (type) Output shape Kernel shape
============ SW/conv_0-dequantizer (Software) ===========
conv_0 (InputConv2D) [30, 30, 32] (3, 3, 3, 32)
_________________________________________________________
conv_1 (Conv2D) [15, 15, 32] (3, 3, 32, 32)
_________________________________________________________
conv_2 (Conv2D) [15, 15, 64] (3, 3, 32, 64)
_________________________________________________________
conv_3 (Conv2D) [8, 8, 64] (3, 3, 64, 64)
_________________________________________________________
conv_4 (Conv2D) [1, 1, 84] (3, 3, 64, 84)
_________________________________________________________
dense_1 (Dense1D) [1, 1, 64] (84, 64)
_________________________________________________________
dense_2 (Dense1D) [1, 1, 1] (64, 1)
_________________________________________________________
dequantizer (Dequantizer) [1, 1, 1] N/A
_________________________________________________________
import numpy as np
# Check Akida model performance
y_akida = model_akida.predict(x_test_akida)
# Compute and display the MAE
mae_akida = np.sum(np.abs(y_test.squeeze() - y_akida.squeeze())) / len(y_test)
print("Akida MAE: {0:.4f}".format(mae_akida))
# For non-regression purposes
assert abs(mae_keras - mae_akida) < 0.5
Akida MAE: 5.8951
5. Estimate age on a single image
Select a random image from the test set for age estimation.
Print the Keras model’s age prediction using the model_keras.predict()
function.
Print the Akida model’s estimated age and the actual age associated with the image.
import matplotlib.pyplot as plt
# Estimate age on a random single image and display Keras and Akida outputs
id = np.random.randint(0, len(y_test) + 1)
age_keras = model_keras.predict(x_test[id:id + 1])
plt.imshow(x_test_akida[id], interpolation='bicubic')
plt.xticks([]), plt.yticks([])
plt.show()
print("Keras estimated age: {0:.1f}".format(age_keras.squeeze()))
print("Akida estimated age: {0:.1f}".format(y_akida[id].squeeze()))
print(f"Actual age: {y_test[id].squeeze()}")
1/1 [==============================] - ETA: 0s
1/1 [==============================] - 0s 120ms/step
Keras estimated age: 25.5
Akida estimated age: 23.8
Actual age: 27
Total running time of the script: (0 minutes 26.392 seconds)