Building a Facial Recognition Model using PCA & SVM Algorithms | by Mahnoor Javed | Towards Data Science

In this article, we will learn to use Principal Component Analysis and Support Vector Machines for building a facial recognition model.
First, let us understand what PCA and SVM are:
Principal Component Analysis (PCA) is a machine learning algorithm that is widely used in exploratory data analysis and for making predictive models. It is commonly used for dimensionality reduction by projecting each data point onto only the first few principal components to obtain lower-dimensional data while preserving as much of the data’s variation as possible.
Matt Brems’s article provides a comprehensive in-depth of the algorithm. For now, let us understand the algorithm in easier terms:
Suppose we have a problem at hand for which we are collecting data. Our dataset results in several variables, many features, all of which affect the results in different aspects. We may choose to eliminate certain features, but that would mean losing information, and we don't want that, right?
So another method of reducing the number of features (reducing data dimensionality) is to create new features by extracting the important information and dropping the least important ones. In this way, our information will not be lost and we will have reduced features, and there will be fewer chances of overfitting our model.
Support Vector Machine (SVM) is a supervised machine learning model used for two-group classification problems. After giving an SVM model set of labeled training data for each category, they’re able to categorize new test data.
SVM classifies data based on the plane that maximizes the margin. The SVM decision boundary is straight. SVM is a really good algorithm for image classification. Experimental results show that SVMs achieve significantly higher search accuracy than traditional query refinement schemes after just three to four rounds of relevance feedback. This is also true for image segmentation systems, including those using a modified version SVM that uses the privileged approach.
Marco Peixeiro’s article explains the need to have a maximal margin hyperplane to classify the data. Give it a read, you’ll understand SVMs better!
Faces are high dimensionality data consisting of a number of pixels. Data in high dimensionality is difficult to process and cannot be visualized using simple techniques like scatterplots for 2-dimensional data.
What we will do is to use PCA to reduce the high dimensionality of data and then feed it into the SVM classifier to classify the pictures.
Let us jump to the coding segment!
The following code example is taken from the sklearn documentation on eigenfaces. We will go through the code step by step to understand its intricacies and results.
First off, we will import the required libraries and modules. An in-depth discussion of why we are importing them will follow when their needs arise.
import pylab as plimport numpy as npfrom matplotlib import pyplot as pltfrom sklearn.model_selection import train_test_splitfrom sklearn.datasets import fetch_lfw_peoplefrom sklearn.model_selection import GridSearchCVfrom sklearn.metrics import classification_reportfrom sklearn.metrics import confusion_matrixfrom sklearn.decomposition import PCA as RandomizedPCAfrom sklearn.svm import SVC
Next, we will download the data into the disk and load it as NumPy arrays using fetch_lfw_people from sklearn.datasets:
lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)
The lfw dataset consists of a database of face photographs designed for studying the problem of unconstrained face recognition. The data set contains more than 13,000 images of faces collected from the web. Each face has been labeled with the name of the person pictured. 1680 of the people pictured have two or more distinct photos in the data set.
The images are in grayscale (pixel values = 0 — 255).
Next, we will introspect the image arrays to find the shape of the pictures. We will use the NumPy shape attribute that returns a tuple with each index having the number of corresponding elements.
n_samples, h, w = lfw_people.images.shapenp.random.seed(42)
As can be seen from the variable explorer, we have 1288 samples (pictures) with a height of 50 px and a width of 37 px (50 x 37 = 1850 features)
We will use the data arrays in the lfw_people utils.Bunch directly and store it in X. We will use this data in our further processing.
X = lfw_people.datan_features = X.shape[1]
The data in X has 1288 samples and 1850 features for each sample.
Next, we will define the labels which are the id of the person to whom the picture belongs.
y = lfw_people.targettarget_names = lfw_people.target_namesn_classes = target_names.shape[0]
Here, y represents the target which is the label of each picture. The label is further defined by the target_names variable which consists of the 7 names of the people to be identified.
target is a 1288x1 NumPy array that contains the values 0–6 of the name the 1288 pictures corresponds to. So if the id 0 has a target value of 5, it would refer to “Hugo Chavez”, as depicted in the target_names:
Hence, y is the target in numerical form, target_names is any of the target/labels by the name, and n_classes is the variable that stores the number of classes we have, in our case, we have 7:
Ariel SharonColin PowellDonald RumsfeldGeorge W BushGerhard SchröderHugo ChavezTony Blair
Ariel Sharon
Colin Powell
Donald Rumsfeld
George W Bush
Gerhard Schröder
Hugo Chavez
Tony Blair
Let us print out the variables:
print("Total dataset size:")print("n_samples: %d", n_samples)print("n_features: %d", n_features)print("n_classes: %d", n_classes)
So, we have 1288 samples (pictures), a total of 1850 features for each sample (50px x 37px) and 7 classes (the people).
Next, we will use the train_test_split module from sklearn.model_selection and split the data (X-features and y-labels)into training data and testing data, with 25% of the data used for testing and the remaining 75% to train the model.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
Here are the variables X-train, X_test, y_train & y_test:
Now, we will use PCA from sklearn.decomposition to select the top components to train the model. We have already imported PCA as RandomizedPCA in the first block of code.
In our case, we have a total of 966 features in the training set X_train , and we will reduce them to 50 using PCA (dimensionality reduction):
n_components = 50pca = RandomizedPCA(n_components=n_components, whiten=True).fit(X_train)
This process takes less than a second, which can be verified using the time function (let us skip that for now).
Now we will reshape our PCA components and define eigenfaces, which is the name given to a set of eigenvectors when used in the computer vision problem of human face recognition:
eigenfaces = pca.components_.reshape((n_components, h, w))
As the screenshot shows, the eigenfaces is a 50 x 50 x 37 NumPy array. The first 50 corresponds to the number of features, the n_components that we initialized a while back, and into which our PCA algorithm reduced to. The image dimensions in the eigenfaces are 50 x 37.
Next, we will use PCA’s transform on both X_train and X_test to reduce the dimensionality.
X_train_pca = pca.transform(X_train)X_test_pca = pca.transform(X_test)
As can be seen in the screenshot above, the dimensionality of both X_train and X_test were reduced through the PCA algorithm. Each had the features reduced from 1850 to 50 (as we defined in the algorithm).
Once we’re done with dimensionality reduction, we will now move towards classification. First, we will train the SVM Classification model.
We will use GridSearchCV, a library function that is an approach to hyperparameter tuning. It will methodically build and evaluate a model for each combination of algorithm parameters specified in a grid, and return the best estimator in clf.best_estimator. The parameters are given in the param_grid:
print("Fitting the classifier to the training set")param_grid = {         'C': [1e3, 5e3, 1e4, 5e4, 1e5],          'gamma': [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.1],          }clf = GridSearchCV(SVC(kernel='rbf', class_weight='balanced'), param_grid)clf = clf.fit(X_train_pca, y_train)print("Best estimator found by grid search:")print(clf.best_estimator_)
The best classifier for our data is SVC with the following parameters:
SVC(C=1000, class_weight = ‘balanced’, gamma=0.01)
Let us now predict the people's names on the test data. We will use the classifier which we found from GridSearchCV and which we had already fit on the training data.
print("Predicting the people names on the testing set")y_pred = clf.predict(X_test_pca)
Once the predictions are made, let us print the Classification Report which will display the precision, recall, F1-score, and support scores for the model. This gives a deeper intuition of the classifier’s behavior.
print(classification_report(y_test, y_pred, target_names=target_names))
Let us print the Confusion Matrix as well:
print(confusion_matrix(y_test, y_pred, labels=range(n_classes)))
The Confusion Matrix prints the values of True Positives, False Positives, and False Negatives and gives an overview of how the classifier is. In the above example,
Finally, we will plot both the portraits of the people as well as the eigenfaces! We will define 2 functions: title to plot the result of the prediction on a portion of the test set, and plot_gallery to evaluate the prediction by plotting them:
def title(y_pred, y_test, target_names, i):    pred_name = target_names[y_pred[i]].rsplit(' ', 1)[-1]    true_name = target_names[y_test[i]].rsplit(' ', 1)[-1]    return 'predicted: %s\ntrue:      %s' % (pred_name, true_name)def plot_gallery(images, titles, h, w, n_row=3, n_col=4):    """Helper function to plot a gallery of portraits"""    plt.figure(figsize=(1.8 * n_col, 2.4 * n_row))    plt.subplots_adjust(bottom=0, left=.01, right=.99, top=.90, hspace=.35)    for i in range(n_row * n_col):        plt.subplot(n_row, n_col, i + 1)        plt.imshow(images[i].reshape((h, w)), cmap=plt.cm.gray)        plt.title(titles[i], size=12)        plt.xticks(())        plt.yticks(())
Let us now plot the results of the prediction on a portion of the test set:
prediction_titles = [title(y_pred, y_test, target_names, i)                         for i in range(y_pred.shape[0])]plot_gallery(X_test, prediction_titles, h, w)
Let us now plot the eigenfaces. We will use the eigenfaces variable which we defined in the code block above.
eigenface_titles = ["eigenface %d" % i for i in range(eigenfaces.shape[0])]plot_gallery(eigenfaces, eigenface_titles, h, w)plt.show()
Lastly, let us plot the accuracy of the PCA + SVM Model for Facial Recognition:
from sklearn.metrics import accuracy_scorescore = accuracy_score(y_test, y_pred)print(score)
We get an accuracy score of 0.81! While it’s not a perfect score, and there is a lot of room for improvement, facial recognition with PCA and SVM gives us a starting point for further powerful algorithms!
In this article, we built a facial recognition model using PCA and SVM. The Principal Component Analysis algorithm was used to reduce the dimensions of the data we had; images having a number of pixel values! then we used SVM for classification by finding the best estimator by hyperparameter tuning. We were able to classify the portraits and got an accuracy score of 0.81.
What can we do to improve accuracy?
I’ll leave that to you 😃