Performance Analysis of Face Recognition Out-of-Box ML/AI Workflows

Featured Photo by cottonbro studio, Pexels

Facial Recognition (FR) is a category of biometric software that maps an individual’s facial features mathematically and stores the data as a faceprint.

The goal of this case study is the Exploratory Data Analysis (EDA) and performance QC analysis of out-of-box ML/AI workflows tested on public-domain datasets and real-time webcam GUI.

Table of Contents

  1. The Face Dataset
  2. The Olivetti Dataset
  3. Real-Time Webcam
  4. Summary
  5. Explore More

The Face Dataset

In the field of FR, SVM has been shown to be superior to other techniques in terms of accuracy and robustness. SVM works well with small datasets, and it is also able to handle complex patterns and noisy data. This makes it an ideal algorithm for FR.

First, we need to import the FR data. The Scikit-Learn library provides inbuilt face recognition datasets including the popular one, the LFW (Labelled  Faces in The Wild) dataset. This dataset consists of around 14000 labeled images with each having a label associated with it. These images are of human faces and they have been manually annotated by researchers from around the world. 

Let’s set the working directory YOURPATH

import os
os.chdir(‘YOURPATH’)
os. getcwd()

and import the LFW dataset

from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60)

with the description

faces.DESCR

(see Appendix).

Let’s plot some of the faces using the matplotlib imshow method

import matplotlib.pyplot as plt

fig, splts = plt.subplots(2, 4)
for i, splts in enumerate(splts.flat):
splts.imshow(faces.images[i])
splts.set(xticks=[], yticks=[],
xlabel=faces.target_names[faces.target[i]])
plt.savefig(‘inputfacesbush.png’)

The LFW face dataset

Splitting the dataset using train_test_split with test_size=0.2

from sklearn.model_selection import train_test_split

X = faces.data
y = faces.target

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

X_train.shape

(1078, 2914)

X_test.shape

(270, 2914)

Let’s import SVC and RandomizedPCA from sklearn

from sklearn.svm import SVC
from sklearn.decomposition import PCA as RandomizedPCA
from sklearn.pipeline import make_pipeline

For dimensionality reduction
pca = RandomizedPCA(n_components=150, whiten=True, random_state=42)
svc = SVC(kernel=’rbf’, class_weight=’balanced’)
model = make_pipeline(pca, svc)

Let’s train our model using the above ML pipeline

model.fit(X_train, y_train)

PCA+SVC ML pipeline

Testing the model:

from sklearn.metrics import accuracy_score

predictions = model.predict(X_test)

accuracy_score(predictions, y_test)

0.762962962962963

Let’s compare erroneous predictions made by the model and actual values

from colorama import Fore

incorrect = 0

length = len(predictions)

print(“Actual\t\t\t\tPredicted\n”)

for i in range(len(predictions)):
if predictions[i] != y_test[i]: # if predictions and actual values are not equal
prediction_name = faces.target_names[predictions[predictions[i]]] # Getting the predicted name
actual_name = faces.target_names[y_test[y_test[i]]] # Getting the actual name
incorrect+=1
print(“{}\t\t\t{}”.format(Fore.GREEN + actual_name, Fore.RED+prediction_name))

print(“{} are classified as correct and {} are classified as incorrect!”.format(length-incorrect, incorrect))

Actual				Predicted

Junichiro Koizumi			George W Bush
Junichiro Koizumi			George W Bush
Gerhard Schroeder			Junichiro Koizumi
George W Bush			Tony Blair
George W Bush			Junichiro Koizumi
Colin Powell			Junichiro Koizumi
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Junichiro Koizumi			George W Bush
George W Bush			Junichiro Koizumi
Colin Powell			Tony Blair
George W Bush			George W Bush
Colin Powell			Junichiro Koizumi
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Junichiro Koizumi			George W Bush
Colin Powell			Junichiro Koizumi
Junichiro Koizumi			Junichiro Koizumi
Gerhard Schroeder			George W Bush
Gerhard Schroeder			Junichiro Koizumi
Junichiro Koizumi			George W Bush
Gerhard Schroeder			George W Bush
George W Bush			Tony Blair
Junichiro Koizumi			Junichiro Koizumi
George W Bush			Tony Blair
George W Bush			Junichiro Koizumi
George W Bush			George W Bush
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Colin Powell			George W Bush
George W Bush			Junichiro Koizumi
George W Bush			George W Bush
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Colin Powell			Junichiro Koizumi
Colin Powell			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Gerhard Schroeder			Junichiro Koizumi
Gerhard Schroeder			Junichiro Koizumi
Junichiro Koizumi			Tony Blair
Gerhard Schroeder			Junichiro Koizumi
George W Bush			Junichiro Koizumi
George W Bush			Junichiro Koizumi
Gerhard Schroeder			George W Bush
Gerhard Schroeder			Junichiro Koizumi
Junichiro Koizumi			Tony Blair
George W Bush			Junichiro Koizumi
Junichiro Koizumi			George W Bush
Colin Powell			Junichiro Koizumi
George W Bush			Tony Blair
George W Bush			Tony Blair
Junichiro Koizumi			Tony Blair
George W Bush			Junichiro Koizumi
Junichiro Koizumi			George W Bush
George W Bush			Junichiro Koizumi
Junichiro Koizumi			Junichiro Koizumi
Junichiro Koizumi			Tony Blair
Junichiro Koizumi			Junichiro Koizumi
Colin Powell			Tony Blair
Junichiro Koizumi			George W Bush
George W Bush			Junichiro Koizumi
Junichiro Koizumi			George W Bush
George W Bush			Tony Blair
206 are classified as correct and 64 are classified as incorrect!

Let’s look at the classification report

from sklearn.metrics import classification_report
print(classification_report(predictions, y_test, digits=2))

precision    recall  f1-score   support

           0       0.58      0.78      0.67         9
           1       0.82      0.81      0.82        52
           2       0.60      0.83      0.70        18
           3       0.92      0.73      0.81       124
           4       0.57      0.86      0.69        14
           5       0.40      1.00      0.57         6
           6       0.80      0.89      0.84         9
           7       0.68      0.68      0.68        38

    accuracy                           0.76       270
   macro avg       0.67      0.82      0.72       270
weighted avg       0.80      0.76      0.77       270

Let’s examine the confusion matrix

from sklearn.metrics import confusion_matrix

matrix = confusion_matrix(predictions, y_test)

matrix

array([[ 7,  0,  1,  1,  0,  0,  0,  0],
       [ 1, 42,  1,  3,  0,  2,  0,  3],
       [ 0,  1, 15,  0,  1,  0,  0,  1],
       [ 4,  6,  6, 90,  4,  4,  2,  8],
       [ 0,  0,  0,  0, 12,  2,  0,  0],
       [ 0,  0,  0,  0,  0,  6,  0,  0],
       [ 0,  0,  0,  0,  1,  0,  8,  0],
       [ 0,  2,  2,  4,  3,  1,  0, 26]], dtype=int64)

Let’s plot this matrix as the seaborn heatmap

import seaborn as sns
from sklearn import metrics
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

plt.figure(1, figsize=(12,8))
cm = metrics.confusion_matrix(predictions, y_test)
cmn = cm.astype(‘float’) / cm.sum(axis=1)[:, np.newaxis]
sns.heatmap(cmn,cmap=’Blues’,annot=True)
plt.ylabel(‘Actual’)
plt.xlabel(‘Predicted’)
plt.title(‘SVC Normalized Confusion Matrix’)
plt.savefig(‘pcasvcconfusionmatrixbushblair.png’)

SVC Normalized Confusion Matrix

Recall that a perfect classifier has a confusion matrix in which all the values are zero except diagonals.

The Olivetti Dataset

 Next, let’s apply FR to the Kaggle Olivetti dataset:

  • Face images taken between April 1992 and April 1994.
  • There are ten different image of each of 40 distinct people
  • There are 400 face images in the dataset
  • Face images were taken at different times, variying ligthing, facial express and facial detail
  • All face images have black background
  • The images are gray level
  • Size of each image is 64×64
  • Image pixel values were scaled to [0, 1] interval
  • Names of 40 people were encoded to an integer from 0 to 39.

Let’s import and/or install the key libraries

!pip install mglearn

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

Visualization
import matplotlib.pyplot as plt

Machine Learning
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn import metrics
import warnings
warnings.filterwarnings(‘ignore’)
print(“Warnings ignored!!”)
import mglearn

Warnings ignored!!

Let’s read the input data and print info

data=np.load(“olivetti_faces.npy”)
target=np.load(“olivetti_faces_target.npy”)

print(“There are {} images in the dataset”.format(len(data)))
print(“There are {} unique targets in the dataset”.format(len(np.unique(target))))
print(“Size of each image is {}x{}”.format(data.shape[1],data.shape[2]))
print(“Pixel values were scaled to [0,1] interval. e.g:{}”.format(data[0][0,:4]))

There are 400 images in the dataset
There are 40 unique targets in the dataset
Size of each image is 64x64
Pixel values were scaled to [0,1] interval. e.g:[0.30991736 0.3677686  0.41735536 0.44214877]

print(“unique target number:”,np.unique(target))

unique target number: [ 0  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]

data.shape

(400, 64, 64)

target.shape

(400,)

Let’s look at images of 40 Distinct People in the Olivetti Dataset

def show_40_distinct_people(images, unique_ids):
#Creating 4X10 subplots in 18×9 figure size
fig, axarr=plt.subplots(nrows=4, ncols=10, figsize=(18, 9))
#For easy iteration flattened 4X10 subplots matrix to 40 array
axarr=axarr.flatten()

#iterating over user ids
for unique_id in unique_ids:
    image_index=unique_id*10
    axarr[unique_id].imshow(images[image_index], cmap='gray')
    axarr[unique_id].set_xticks([])
    axarr[unique_id].set_yticks([])
    axarr[unique_id].set_title("face id:{}".format(unique_id))
plt.suptitle("There are 40 distinct people in the dataset")
plt.savefig('40_distinct_people.png')

show_40_distinct_people(data, np.unique(target))

Images of 40 Distinct People in the Olivetti Dataset

We can also see other people faces by selecting different values of subject_ids

show_10_faces_of_n_subject(images=data, subject_ids=[2,7, 23, 26, 38])

People faces with subject_ids=[2,7, 23, 26, 38]

show_10_faces_of_n_subject(images=data, subject_ids=[0,5, 21, 24, 36])

Let’s reshape images for our ML model
X=data.reshape((data.shape[0],data.shape[1]*data.shape[2]))
print(“X shape:”,X.shape)

X=data.reshape((data.shape[0],data.shape[1]*data.shape[2]))
print(“X shape:”,X.shape)

X shape: (400, 4096)

Let’s apply train_test_split with test_size=0.2

X_train, X_test, y_train, y_test=train_test_split(X, target, test_size=0.2, stratify=target, random_state=0)
print(“X_train shape:”,X_train.shape)
print(“y_train shape:{}”.format(y_train.shape))

X_train shape: (320, 4096)
y_train shape:(320,)

Let’s check the number of y-data samples per target class

y_frame=pd.DataFrame()
y_frame[‘subject ids’]=y_train
y_frame.groupby([‘subject ids’]).size().plot.bar(figsize=(15,8),title=”Number of Samples per Target Class”)
plt.savefig(‘numberofsamples.png’)

Number of Samples per Target Class

Principal Component Analysis (PCA) is the unsupervised ML technique that allows to reduce the dimension of the data by selecting the most important components. Let’s illustrate PCA with a simple synthetic 2D test

mglearn.plots.plot_pca_illustration()
plt.savefig(‘pca_illustration.png’)

The algorithm proceeds by finding the direction of the maximum variance labeled “Component 1”. 

Let’s implement PCA as follows

from sklearn.decomposition import PCA
pca=PCA(n_components=2)
pca.fit(X)
X_pca=pca.transform(X)

Let’s plot the 2D PCA projection of 10 people

number_of_people=10
index_range=number_of_people*10
fig=plt.figure(figsize=(10,8))
ax=fig.add_subplot(1,1,1)
scatter=ax.scatter(X_pca[:index_range,0],
X_pca[:index_range,1],
c=target[:index_range],
s=40,
cmap=plt.get_cmap(‘jet’, number_of_people)
)

ax.set_xlabel(“First Principle Component”)
ax.set_ylabel(“Second Principle Component”)
ax.set_title(“PCA projection of {} people”.format(number_of_people))

fig.colorbar(scatter)
plt.savefig(‘pcaprojection10people.png’)

2D PCA projection of 10 people

Let’s check the PCA explained variances vs components

pca=PCA()
pca.fit(X)

plt.figure(1, figsize=(12,8))

plt.plot(pca.explained_variance_, linewidth=2)

plt.xlabel(‘Components’)
plt.ylabel(‘Explained Variaces’)
plt.savefig(‘pcaexplainedvariances.png’)

PCA explained variances vs components

Let’s apply PCA with n_components=90 as the Optimum Number of principal components

n_components=90
pca=PCA(n_components=n_components, whiten=True)
pca.fit(X_train)

PCA(n_components=90, whiten=True)

We can apply PCA with svd_solver

PCA(copy=True, iterated_power=’auto’, n_components=90, random_state=None,
svd_solver=’auto’, tol=0.0, whiten=True)

and plot the low-resolution average face

fig,ax=plt.subplots(1,1,figsize=(8,8))
ax.imshow(pca.mean_.reshape((64,64)), cmap=”gray”)
ax.set_xticks([])
ax.set_yticks([])
ax.set_title(‘Average Face’)
plt.savefig(‘averageface.png’)

Average face

We can also plot all face eigenimages

number_of_eigenfaces=len(pca.components_)
eigen_faces=pca.components_.reshape((number_of_eigenfaces, data.shape[1], data.shape[2]))

cols=10
rows=int(number_of_eigenfaces/cols)
fig, axarr=plt.subplots(nrows=rows, ncols=cols, figsize=(15,15))
axarr=axarr.flatten()
for i in range(number_of_eigenfaces):
axarr[i].imshow(eigen_faces[i],cmap=”gray”)
axarr[i].set_xticks([])
axarr[i].set_yticks([])
axarr[i].set_title(“eigen id:{}”.format(i))
plt.suptitle(“All Eigen Faces”.format(10“=”, 10“=”))
plt.savefig(‘alleigenfaces.png’)

Let’s apply PCA to both train and test data

X_train_pca=pca.transform(X_train)
X_test_pca=pca.transform(X_test)

Let’s apply the SVC kernel to the PCA-transformed train/test data

clf = SVC()
clf.fit(X_train_pca, y_train)
y_pred = clf.predict(X_test_pca)
print(“accuracy score:{:.2f}”.format(metrics.accuracy_score(y_test, y_pred)))

accuracy score:0.94

print(metrics.f1_score(y_test, y_pred,average=’macro’))

0.9349999999999999

Let’s plot the SVC confusion matrix as the seaborn heatmap

import seaborn as sns
plt.figure(1, figsize=(12,8))
cm = metrics.confusion_matrix(y_test, y_pred)
cmn = cm.astype(‘float’) / cm.sum(axis=1)[:, np.newaxis]
sns.heatmap(cmn,annot=False,cmap=’Blues’)
plt.ylabel(‘Actual’)
plt.xlabel(‘Predicted’)
plt.xlabel(‘SVC Confusion Matrix’)
plt.savefig(‘confusionmatrixblue.png’)

40x40 SVC confusion matrix

Let’s print the classification report

print(metrics.classification_report(y_test, y_pred))

precision    recall  f1-score   support

           0       1.00      0.50      0.67         2
           1       1.00      1.00      1.00         2
           2       0.50      1.00      0.67         2
           3       1.00      1.00      1.00         2
           4       1.00      0.50      0.67         2
           5       1.00      1.00      1.00         2
           6       1.00      0.50      0.67         2
           7       1.00      0.50      0.67         2
           8       1.00      1.00      1.00         2
           9       1.00      1.00      1.00         2
          10       1.00      1.00      1.00         2
          11       1.00      1.00      1.00         2
          12       1.00      1.00      1.00         2
          13       1.00      1.00      1.00         2
          14       1.00      1.00      1.00         2
          15       0.67      1.00      0.80         2
          16       1.00      1.00      1.00         2
          17       1.00      1.00      1.00         2
          18       1.00      1.00      1.00         2
          19       1.00      1.00      1.00         2
          20       1.00      1.00      1.00         2
          21       1.00      0.50      0.67         2
          22       1.00      1.00      1.00         2
          23       1.00      1.00      1.00         2
          24       1.00      1.00      1.00         2
          25       1.00      0.50      0.67         2
          26       1.00      1.00      1.00         2
          27       1.00      1.00      1.00         2
          28       1.00      1.00      1.00         2
          29       1.00      1.00      1.00         2
          30       1.00      1.00      1.00         2
          31       1.00      1.00      1.00         2
          32       1.00      1.00      1.00         2
          33       1.00      1.00      1.00         2
          34       1.00      1.00      1.00         2
          35       1.00      1.00      1.00         2
          36       1.00      1.00      1.00         2
          37       1.00      1.00      1.00         2
          38       0.67      1.00      0.80         2
          39       0.50      1.00      0.67         2

    accuracy                           0.93        80
   macro avg       0.96      0.93      0.92        80
weighted avg       0.96      0.93      0.92        80

Let’s compare several ML classifiers

from sklearn.linear_model import SGDClassifier
from sklearn.ensemble import RandomForestClassifier

models=[]
models.append((‘LDA’, LinearDiscriminantAnalysis()))
models.append((“LR”,LogisticRegression()))
models.append((“NB”,GaussianNB()))
models.append((“KNN”,KNeighborsClassifier(n_neighbors=5)))
models.append((“DT”,DecisionTreeClassifier()))
models.append((“SVM”,SVC()))
models.append((“SGD”,SGDClassifier()))
models.append((‘RF’, RandomForestClassifier()))

for name, model in models:

clf=model

clf.fit(X_train_pca, y_train)

y_pred=clf.predict(X_test_pca)
print(10*"=","{} Result".format(name).upper(),10*"=")
print("Accuracy score:{:0.2f}".format(metrics.accuracy_score(y_test, y_pred)))
print()
========== LDA RESULT ==========
Accuracy score:0.95

========== LR RESULT ==========
Accuracy score:0.94

========== NB RESULT ==========
Accuracy score:0.91

========== KNN RESULT ==========
Accuracy score:0.75

========== DT RESULT ==========
Accuracy score:0.60

========== SVM RESULT ==========
Accuracy score:0.93

========== SGD RESULT ==========
Accuracy score:0.93

========== RF RESULT ==========
Accuracy score:0.94

Let’s check cross_val_score

from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
pca=PCA(n_components=n_components, whiten=True)
pca.fit(X)
X_pca=pca.transform(X)
for name, model in models:
kfold=KFold(n_splits=5, shuffle=True, random_state=0)

cv_scores=cross_val_score(model, X_pca, target, cv=kfold)
print("{} mean cross validations score:{:.2f}".format(name, cv_scores.mean()))
LDA mean cross validations score:0.97
LR mean cross validations score:0.94
NB mean cross validations score:0.80
KNN mean cross validations score:0.70
DT mean cross validations score:0.50
SVM mean cross validations score:0.88
SGD mean cross validations score:0.91
RF mean cross validations score:0.89

Let’s examine the LDA classifier

lr=LinearDiscriminantAnalysis()
lr.fit(X_train_pca, y_train)
y_pred=lr.predict(X_test_pca)
print(“Accuracy score:{:.2f}”.format(metrics.accuracy_score(y_test, y_pred)))

Accuracy score:0.96

Let’s plot the LDA Confusion Matrix

import seaborn as sns
plt.figure(1, figsize=(12,8))
cm = metrics.confusion_matrix(y_test, y_pred)
cmn = cm.astype(‘float’) / cm.sum(axis=1)[:, np.newaxis]
sns.heatmap(cmn,annot=False,cmap=’Blues’)
plt.ylabel(‘Actual’)
plt.xlabel(‘Predicted’)
plt.title(‘LDA Confusion Matrix’)
plt.savefig(‘ldconfusionmatrixblue.png’)

40x40 LDA Confusion Matrix

Let’s print the LDA classification report

print(“Classification Results:\n{}”.format(metrics.classification_report(y_test, y_pred)))

Classification Results:
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         2
           1       1.00      1.00      1.00         2
           2       0.67      1.00      0.80         2
           3       1.00      1.00      1.00         2
           4       1.00      0.50      0.67         2
           5       1.00      1.00      1.00         2
           6       1.00      1.00      1.00         2
           7       1.00      0.50      0.67         2
           8       1.00      1.00      1.00         2
           9       1.00      1.00      1.00         2
          10       1.00      1.00      1.00         2
          11       1.00      1.00      1.00         2
          12       1.00      1.00      1.00         2
          13       1.00      1.00      1.00         2
          14       1.00      1.00      1.00         2
          15       1.00      1.00      1.00         2
          16       1.00      1.00      1.00         2
          17       1.00      1.00      1.00         2
          18       1.00      1.00      1.00         2
          19       1.00      1.00      1.00         2
          20       1.00      1.00      1.00         2
          21       1.00      0.50      0.67         2
          22       1.00      1.00      1.00         2
          23       1.00      1.00      1.00         2
          24       1.00      1.00      1.00         2
          25       1.00      1.00      1.00         2
          26       1.00      1.00      1.00         2
          27       1.00      1.00      1.00         2
          28       1.00      1.00      1.00         2
          29       1.00      1.00      1.00         2
          30       1.00      1.00      1.00         2
          31       1.00      1.00      1.00         2
          32       1.00      1.00      1.00         2
          33       1.00      1.00      1.00         2
          34       1.00      1.00      1.00         2
          35       1.00      1.00      1.00         2
          36       1.00      1.00      1.00         2
          37       1.00      1.00      1.00         2
          38       0.67      1.00      0.80         2
          39       0.67      1.00      0.80         2

    accuracy                           0.96        80
   macro avg       0.97      0.96      0.96        80
weighted avg       0.97      0.96      0.96        80

Let’s run various ML optimization scenarios:

  • LogisticRegression + LeaveOneOut

from sklearn.model_selection import LeaveOneOut
loo_cv=LeaveOneOut()
clf=LogisticRegression()
cv_scores=cross_val_score(clf,
X_pca,
target,
cv=loo_cv)
print(“{} Leave One Out cross-validation mean accuracy score:{:.2f}”.format(clf.class.name,
cv_scores.mean()))

LogisticRegression Leave One Out cross-validation mean accuracy score:0.96
  • LDA + LeaveOneOut

from sklearn.model_selection import LeaveOneOut
loo_cv=LeaveOneOut()
clf=LinearDiscriminantAnalysis()
cv_scores=cross_val_score(clf,
X_pca,
target,
cv=loo_cv)
print(“{} Leave One Out cross-validation mean accuracy score:{:.2f}”.format(clf.class.name,
cv_scores.mean()))

LinearDiscriminantAnalysis Leave One Out cross-validation mean accuracy score:0.98
  • Hyperparameter Tuning: GridSearcCV

from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import LeaveOneOut
params={‘penalty’:[‘l1’, ‘l2’],
‘C’:np.logspace(0, 4, 10)
}
clf=LogisticRegression()

loo_cv=LeaveOneOut()
gridSearchCV=GridSearchCV(clf, params, cv=loo_cv)
gridSearchCV.fit(X_train_pca, y_train)
print(“Grid search fitted..”)
print(gridSearchCV.best_params_)
print(gridSearchCV.best_score_)
print(“grid search cross validation score:{:.2f}”.format(gridSearchCV.score(X_test_pca, y_test)))

Grid search fitted..
{'C': 1.0, 'penalty': 'l2'}
0.934375
grid search cross validation score:0.94
  • LR + PCA + OneVsRestClassifier

lr=LogisticRegression(C=1.0, penalty=”l2″)
lr.fit(X_train_pca, y_train)
print(“lr score:{:.2f}”.format(lr.score(X_test_pca, y_test)))

lr score:0.94

from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier

Target=label_binarize(target, classes=range(40))
print(Target.shape)
print(Target[0])

n_classes=Target.shape[1]

(400, 40)
[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0]

Splitting multi-class data

X_train_multiclass, X_test_multiclass, y_train_multiclass, y_test_multiclass=train_test_split(X, Target, test_size=0.2,
stratify=Target, random_state=0)

pca=PCA(n_components=n_components, whiten=True)
pca.fit(X_train_multiclass)

X_train_multiclass_pca=pca.transform(X_train_multiclass)
X_test_multiclass_pca=pca.transform(X_test_multiclass)

oneRestClassifier=OneVsRestClassifier(lr)

oneRestClassifier.fit(X_train_multiclass_pca, y_train_multiclass)
y_score=oneRestClassifier.decision_function(X_test_multiclass_pca)

For each class
precision = dict()
recall = dict()
average_precision = dict()
for i in range(n_classes):
precision[i], recall[i], _ = metrics.precision_recall_curve(y_test_multiclass[:, i],
y_score[:, i])
average_precision[i] = metrics.average_precision_score(y_test_multiclass[:, i], y_score[:, i])

A “micro-average”: quantifying score on all classes jointly
precision[“micro”], recall[“micro”], _ = metrics.precision_recall_curve(y_test_multiclass.ravel(),
y_score.ravel())
average_precision[“micro”] = metrics.average_precision_score(y_test_multiclass, y_score,
average=”micro”)
print(‘Average precision score, micro-averaged over all classes: {0:0.2f}’
.format(average_precision[“micro”]))

Average precision score, micro-averaged over all classes: 0.98

Let’s plot the average precision (AP) score, micro-averaged over all classes

!pip install funcsigs

from funcsigs import signature

step_kwargs = ({‘step’: ‘post’}
if ‘step’ in signature(plt.fill_between).parameters
else {})
plt.figure(1, figsize=(12,8))
plt.step(recall[‘micro’], precision[‘micro’], color=’b’, alpha=0.2,
where=’post’)
plt.fill_between(recall[“micro”], precision[“micro”], alpha=0.2, color=’b’,
**step_kwargs)

plt.xlabel(‘Recall’)
plt.ylabel(‘Precision’)
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title(
‘Average precision score, micro-averaged over all classes: AP={0:0.2f}’
.format(average_precision[“micro”]))
plt.savefig(‘averageprecisionscore.png’)

Average precision score, micro-averaged over all classes
  • LDA+LR Cascade Pipeline

from sklearn.pipeline import Pipeline
work_flows_std = list()
work_flows_std.append((‘lda’, LinearDiscriminantAnalysis()))
work_flows_std.append((‘logReg’, LogisticRegression(C=1.0, penalty=”l2″)))
model_std = Pipeline(work_flows_std)
model_std.fit(X_train, y_train)
y_pred=model_std.predict(X_test)
print(“Accuracy score:{:.2f}”.format(metrics.accuracy_score(y_test, y_pred)))
print(“Classification Results:\n{}”.format(metrics.classification_report(y_test, y_pred)))

Accuracy score:0.96
Classification Results:
              precision    recall  f1-score   support

           0       1.00      1.00      1.00         2
           1       1.00      1.00      1.00         2
           2       0.50      1.00      0.67         2
           3       1.00      1.00      1.00         2
           4       1.00      1.00      1.00         2
           5       1.00      1.00      1.00         2
           6       1.00      1.00      1.00         2
           7       1.00      0.50      0.67         2
           8       1.00      1.00      1.00         2
           9       1.00      1.00      1.00         2
          10       1.00      1.00      1.00         2
          11       1.00      1.00      1.00         2
          12       1.00      1.00      1.00         2
          13       1.00      1.00      1.00         2
          14       1.00      1.00      1.00         2
          15       1.00      1.00      1.00         2
          16       1.00      1.00      1.00         2
          17       1.00      1.00      1.00         2
          18       1.00      1.00      1.00         2
          19       1.00      1.00      1.00         2
          20       1.00      1.00      1.00         2
          21       1.00      0.50      0.67         2
          22       1.00      1.00      1.00         2
          23       1.00      1.00      1.00         2
          24       1.00      1.00      1.00         2
          25       1.00      0.50      0.67         2
          26       1.00      1.00      1.00         2
          27       1.00      1.00      1.00         2
          28       1.00      1.00      1.00         2
          29       1.00      1.00      1.00         2
          30       1.00      1.00      1.00         2
          31       1.00      1.00      1.00         2
          32       1.00      1.00      1.00         2
          33       1.00      1.00      1.00         2
          34       1.00      1.00      1.00         2
          35       1.00      1.00      1.00         2
          36       1.00      1.00      1.00         2
          37       1.00      1.00      1.00         2
          38       0.67      1.00      0.80         2
          39       1.00      1.00      1.00         2

    accuracy                           0.96        80
   macro avg       0.98      0.96      0.96        80
weighted avg       0.98      0.96      0.96        80
  • Unsupervised ML via KMeans Clusters

import scikitplot as skplt

import sklearn
from sklearn.model_selection import train_test_split

from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor, GradientBoostingClassifier, ExtraTreesClassifier
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA

import matplotlib.pyplot as plt

import sys
import warnings
warnings.filterwarnings(“ignore”)

print(“Scikit Plot Version : “, skplt.version)
print(“Scikit Learn Version : “, sklearn.version)
print(“Python Version : “, sys.version)

%matplotlib inline

Scikit Plot Version :  0.3.7
Scikit Learn Version :  1.1.3
Python Version :  3.9.16 (main, Jan 11 2023, 16:16:36) [MSC v.1916 64 bit (AMD64)]

skplt.cluster.plot_elbow_curve(KMeans(random_state=1),
X_train,
cluster_ranges=range(2, 20),
figsize=(8,6));
plt.savefig(‘elbowplot.png’)

Elbow plot

kmeans = KMeans(n_clusters=10, random_state=1)
kmeans.fit(X_train, y_train)
cluster_labels = kmeans.predict(X_test)
skplt.metrics.plot_silhouette(X_test, cluster_labels,
figsize=(8,6));
plt.savefig(‘silhouetteanalysis.png’)

Silhouette analysis
  • PCA Validation

pca = PCA(random_state=1)
pca.fit(X_train)

skplt.decomposition.plot_pca_component_variance(pca, figsize=(8,6));
plt.savefig(‘pcacomponentexplainedvariance.png’)

PCA Component Explained Variances

skplt.decomposition.plot_pca_2d_projection(pca, X_train, y_train,
figsize=(10,10),
cmap=”tab10″);
plt.savefig(‘pca_2d_projection.png’)

PCA 2D projection of 40 classes
  • Multi-class LR ROC and Precision-Recall Curves

log_reg=LogisticRegression(C=1.0, penalty=”l2″)
log_reg.fit(X_train, y_train)
Y_test_probs = log_reg.predict_proba(X_test)

skplt.metrics.plot_roc_curve(y_test, Y_test_probs,
title=”LR ROC Curve”, figsize=(12,6));
plt.savefig(‘LROCCURVE.png’)

ROC curve legend classes 0-16
LR ROC curve 40 classes

skplt.metrics.plot_precision_recall_curve(y_test, Y_test_probs,
title=”LR Precision-Recall Curve”, figsize=(12,6));
plt.savefig(‘LRprecisionrecallcurve.png’)

LR precision-recall curve 40 classes
LR precision-recall curve legend classes 25-39

Real-Time Webcam

Let’s look at the image snapshot officepeople3.jpg of a live webcam session. We need the extra xml file haarcascade_frontalface_default.xml. Both files are to be copied to the local directory YOURPATH. The three-step face detection workflow is as follows

import os
os.chdir(‘YOURPATH’)
os. getcwd()
import cv2
import sys

imagePath = ‘officepeople3.jpg’
cascPath = ‘haarcascade_frontalface_default.xml’

faceCascade = cv2.CascadeClassifier(cascPath)

Read and convert image:
image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Detect faces in the image:
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
# flags = cv2.cv.CV_HAAR_SCALE_IMAGE
)
print(“Found {0} faces!”.format(len(faces)))

Show face detections:

for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x+w, y+h), (255, 0, 0), 2)

cv2.imshow(“Faces found”, image)
cv2.waitKey(0)

Found 6 faces!
6 faces found

Summary

  • We have implemented and tested the iterative FR process of giving Python ML pipelines the ability to identify or verify different facial features. 
  • This is the three-phase process: Data Gathering, Train/Test/Validate the Recognizer, and Recognition with relevant ML error metrics.
  • FR ML algorithms have tasks called multi-label classifiers. 
  • We use cascades to solve the problem of detecting faces into multiple stages.
  • Cascades do a very rough and quick test for each block. If that block passes, does a more detailed test and so on.
  • We have been able to detect who the image represents in the photo using a simple PCA + supervised ML approach by detecting, manipulating and identifying the contours of the face.
  • This clearly shows how ML has rapidly taken charge in the world of AI. A day-to-day sample is Facebook, which automatically tags people found in a photo without tagging them manually.
  • FR can serve as an excellent security measure for time tracking and attendance.
  • FR is also cheap technology as there is less processing involved, like in other biometric techniques.

Explore More

Appendix The LFW Dataset

".. _labeled_faces_in_the_wild_dataset:\n\nThe Labeled Faces in the Wild face recognition dataset\n------------------------------------------------------\n\nThis dataset is a collection of JPEG pictures of famous people collected\nover the internet, all details are available on the official website:\n\n    http://vis-www.cs.umass.edu/lfw/\n\nEach picture is centered on a single face. The typical task is called\nFace Verification: given a pair of two pictures, a binary classifier\nmust predict whether the two images are from the same person.\n\nAn alternative task, Face Recognition or Face Identification is:\ngiven the picture of the face of an unknown person, identify the name\nof the person by referring to a gallery of previously seen pictures of\nidentified persons.\n\nBoth Face Verification and Face Recognition are tasks that are typically\nperformed on the output of a model trained to perform Face Detection. The\nmost popular model for Face Detection is called Viola-Jones and is\nimplemented in the OpenCV library. The LFW faces were extracted by this\nface detector from various online websites.\n\n**Data Set Characteristics:**\n\n    =================   =======================\n    Classes                                5749\n    Samples total                         13233\n    Dimensionality                         5828\n    Features            real, between 0 and 255\n    =================   =======================\n\nUsage\n~~~~~\n\n``scikit-learn`` provides two loaders that will automatically download,\ncache, parse the metadata files, decode the jpeg and convert the\ninteresting slices into memmapped numpy arrays. This dataset size is more\nthan 200 MB. The first load typically takes more than a couple of minutes\nto fully decode the relevant part of the JPEG files into numpy arrays. If\nthe dataset has  been loaded once, the following times the loading times\nless than 200ms by using a memmapped version memoized on the disk in the\n``~/scikit_learn_data/lfw_home/`` folder using ``joblib``.\n\nThe first loader is used for the Face Identification task: a multi-class\nclassification task (hence supervised learning)::\n\n  >>> from sklearn.datasets import fetch_lfw_people\n  >>> lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)\n\n  >>> for name in lfw_people.target_names:\n  ...     print(name)\n  ...\n  Ariel Sharon\n  Colin Powell\n  Donald Rumsfeld\n  George W Bush\n  Gerhard Schroeder\n  Hugo Chavez\n  Tony Blair\n\nThe default slice is a rectangular shape around the face, removing\nmost of the background::\n\n  >>> lfw_people.data.dtype\n  dtype('float32')\n\n  >>> lfw_people.data.shape\n  (1288, 1850)\n\n  >>> lfw_people.images.shape\n  (1288, 50, 37)\n\nEach of the ``1140`` faces is assigned to a single person id in the ``target``\narray::\n\n  >>> lfw_people.target.shape\n  (1288,)\n\n  >>> list(lfw_people.target[:10])\n  [5, 6, 3, 1, 0, 1, 3, 4, 3, 0]\n\nThe second loader is typically used for the face verification task: each sample\nis a pair of two picture belonging or not to the same person::\n\n  >>> from sklearn.datasets import fetch_lfw_pairs\n  >>> lfw_pairs_train = fetch_lfw_pairs(subset='train')\n\n  >>> list(lfw_pairs_train.target_names)\n  ['Different persons', 'Same person']\n\n  >>> lfw_pairs_train.pairs.shape\n  (2200, 2, 62, 47)\n\n  >>> lfw_pairs_train.data.shape\n  (2200, 5828)\n\n  >>> lfw_pairs_train.target.shape\n  (2200,)\n\nBoth for the :func:`sklearn.datasets.fetch_lfw_people` and\n:func:`sklearn.datasets.fetch_lfw_pairs` function it is\npossible to get an additional dimension with the RGB color channels by\npassing ``color=True``, in that case the shape will be\n``(2200, 2, 62, 47, 3)``.\n\nThe :func:`sklearn.datasets.fetch_lfw_pairs` datasets is subdivided into\n3 subsets: the development ``train`` set, the development ``test`` set and\nan evaluation ``10_folds`` set meant to compute performance metrics using a\n10-folds cross validation scheme.\n\n.. topic:: References:\n\n * `Labeled Faces in the Wild: A Database for Studying Face Recognition\n   in Unconstrained Environments.\n   <http://vis-www.cs.umass.edu/lfw/lfw.pdf>`_\n   Gary B. Huang, Manu Ramesh, Tamara Berg, and Erik Learned-Miller.\n   University of Massachusetts, Amherst, Technical Report 07-49, October, 2007.\n\n\nExamples\n~~~~~~~~\n\n:ref:`sphx_glr_auto_examples_applications_plot_face_recognition.py`\n"

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: