<jupyter_start><jupyter_text>Face Recognition using CNN  Step 1: At the first, you should load the required libraries:<jupyter_code>import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import SubsetRandomSampler
import pandas
from collections import Counter
from random import shuffle
import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>Import the Early Stopping Class<jupyter_code># import EarlyStopping
from pytorchtools import EarlyStopping<jupyter_output><empty_output><jupyter_text>* __Set default values for my parameters__<jupyter_code>batch_size = 64
validation_split = 0.2
num_folds= 2
num_epoch = 15
train_folder = './train_images'
test_folder = './test_images'
model_name = 'model'
model_folder = './models'
refresh_model = 30
patience = 4<jupyter_output><empty_output><jupyter_text>* __Set inputs__<jupyter_code>batch_size = int(input("Batch size: "))
validation_split = float(input("Percentage of the validation set (from 0 to 1): "))
num_fold = int(input("Number of folds. If there is more folds than network type, folds network type will loop: "))
num_epoch = int(input("Number of epoch: "))
train_folder = input("Train folder: ")
test_folder = input("Test folder: ")
model_name = input("Model name: ")
model_folder = input("Model folder: ")
refresh_model = int(input("Validating model rate: "))
patience = int(input("Patience for EarlyStopping: "))<jupyter_output>Patience for EarlyStopping: 7<jupyter_text>Step 2: * __Load labeled dataset and split it into a train data set and a validation dataset :__<jupyter_code>transform = transforms.Compose(
    [transforms.Grayscale(),
     transforms.ToTensor(),
     transforms.Normalize((0.5,), (0.5,))])

trainset = torchvision.datasets.ImageFolder(root=train_folder, transform=transform)

dataset_size = len(trainset)
fold_size = int(dataset_size / num_folds)
fold_split_size = int(np.floor(validation_split * fold_size))
indices = list(range(dataset_size))

best_model = []
train_loader = []
validation_loader = []
shuffle(indices)

for i in range(num_folds):

    split_start = dataset_size - (fold_split_size * (i + 1))
    split_end = split_start + fold_split_size
    train_indices = indices[0:split_start - 1] + indices[split_end + 1:dataset_size]
    val_indices = indices[split_start:split_end]
    train_sampler = SubsetRandomSampler(train_indices)
    valid_sampler = SubsetRandomSampler(val_indices)
    train_loader.append(torch.utils.data.DataLoader(trainset, batch_size=batch_size,
                                                    sampler=train_sampler, num_workers=2))
    validation_loader.append(torch.utils.data.DataLoader(trainset, batch_size=batch_size,
                                                         sampler=valid_sampler,num_workers=2))<jupyter_output><empty_output><jupyter_text>* __Load unlabeled dataset and creating loaders:__<jupyter_code>testset = torchvision.datasets.ImageFolder(root='./test_images',  transform=transform)

test_loader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('other', 'face')<jupyter_output><empty_output><jupyter_text>* __Visualizing some random training images :__<jupyter_code>def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()
# get some random training images
dataiter = iter(train_loader[0])
images, labels = dataiter.next()
# show images
imshow(torchvision.utils.make_grid([images[j] for j in range(4)]))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))<jupyter_output><empty_output><jupyter_text>* __Some stats about the train dataset:__<jupyter_code>def stat_img (train_loader):
    list_labels=[]
    for trainloader in train_loader:
        l = []
        for i, data in enumerate(trainloader,0):
            inputs, labels = data
            for j in range(len(labels)):
                l.append(classes[labels[j]])
            print(len(l))
        list_labels.append(l)
    
    return list_labels
labels = stat_img (train_loader)
test = dict()
for i in range(len(labels)):
    test[i] = Counter(labels[i])
df = pandas.DataFrame.from_dict(test, orient='index')
df.plot(kind='bar')
print("******************************************")
print("******* Description of the datasets ****** ")
print("******************************************")

for i in range(len(labels)):
    faces = df['face'][i]
    others = df['other'][i]
    print("*************** DATASET N° {0:1} ************* ".format(i))
    print("------------------------------------------")
    print(" Number of faces : ",faces)
    print(" Number of others: ",others)
    print(" Total           : ", faces+others)
    print("------------------------------------------")
    print(" Number of faces :  {0:.0%}".format(faces/(faces+others)))
    print(" Number of others:  {0:.0%}".format(others/(faces+others)))
    print("******************************************")<jupyter_output>******************************************
******* Description of the datasets ****** 
******************************************
*************** DATASET N° 0 ************* 
------------------------------------------
 Number of faces :  58331
 Number of others:  24216
 Total           :  82547
------------------------------------------
 Number of faces :  71%
 Number of others:  29%
******************************************
*************** DATASET N° 1 ************* 
------------------------------------------
 Number of faces :  58221
 Number of others:  24325
 Total           :  82546
------------------------------------------
 Number of faces :  71%
 Number of others:  29%
******************************************<jupyter_text>Step 3: * __Defining the architecture of the Neural Network__         1. First Architecture<jupyter_code>class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 6 * 6, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 6 * 6)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        x = F.softmax(x, 1)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features
net = Net()
print(net)<jupyter_output>Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=576, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=2, bias=True)
)<jupyter_text>2. Second Architecture<jupyter_code>class Net2(nn.Module):
    def __init__(self):
        super(Net2, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 10, 5)
        self.fc1 = nn.Linear(10 * 6 * 6, 242)
        self.fc2 = nn.Linear(242, 42)
        self.fc3 = nn.Linear(42, 2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 10 * 6 * 6)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        x = F.softmax(x, 1)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:]  # all dimensions except the batch dimension
        num_features = 1
        for s in size:
            num_features *= s
        return num_features
net2 = Net2()
print(net2)<jupyter_output>Net2(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(6, 10, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=360, out_features=242, bias=True)
  (fc2): Linear(in_features=242, out_features=42, bias=True)
  (fc3): Linear(in_features=42, out_features=2, bias=True)
)<jupyter_text>* __Training with the first architecture__<jupyter_code>def train_model(model,best_model):
    
    average_train = []
    average_valid = []

    for i in range(num_folds):
        
        # initialize the early_stopping object
        early_stopping = EarlyStopping(patience=patience, verbose=True)
        
        best_loss = float('inf')
        
        # to track the training loss as the model trains
        train_losses = []
        # to track the validation loss as the model trains
        valid_losses = []
        # to track the average training loss per epoch as the model trains
        avg_train_losses = []
        # to track the average validation loss per epoch as the model trains
        avg_valid_losses = [] 
        
        criterion = nn.MSELoss()
        optimizer = optim.SGD(model[i].parameters(), lr=0.001, momentum=0.9)
        
        print('Training fold %d' % i)  
        
        for epoch in range(1,num_epoch+1):
            ###################
            # train the model #
            ###################
            model[i].train() # prep model for training
            
            for batch, (data, target) in enumerate(train_loader[i], 1):

                # clear the gradients of all optimized variables
                optimizer.zero_grad()
                # forward pass: compute predicted outputs by passing inputs to the model
                output = model[i](data)
                
                target = torch.eye(2)[target.long()]
                # calculate the loss
                loss = criterion(output, target)
                # backward pass: compute gradient of the loss with respect to model parameters
                loss.backward()
                # perform a single optimization step (parameter update)
                optimizer.step()
                # record training loss
                train_losses.append(loss.item())

                if batch % refresh_model == 1:

                    ######################    
                    # validate the model #
                    ######################
                    model[i].eval() # prep model for evaluation
                    for data, target in validation_loader[i]:
                        # forward pass: compute predicted outputs by passing inputs to the model
                        output = model[i](data)
                        
                        target = torch.eye(2)[target.long()]
                        # calculate the loss
                        loss = criterion(output, target)
                        # record validation loss
                        valid_losses.append(loss.item())

                    # print training/validation statistics 
                    # calculate average loss over an epoch
                    train_loss = np.average(train_losses)
                    valid_loss = np.average(valid_losses)
                    avg_train_losses.append(train_loss)
                    avg_valid_losses.append(valid_loss)

                    epoch_len = len(str(num_epoch))
                    train_loader_len = len(str(len(train_loader[i])))
                    
                    

                    print_msg = (f'[{epoch:>{epoch_len}}/{num_epoch:>{epoch_len}}] ' +
                                 f'[{batch:>{epoch_len}}/{len(train_loader[i]):>{train_loader_len}}] ' +
                                 f'train_loss: {train_loss:.5f} ' +
                                 f'valid_loss: {valid_loss:.5f}')

                    print(print_msg)

                    # clear lists to track next epoch
                    train_losses = []
                    valid_losses = []
                    
                    if valid_loss < best_loss:
                        best_loss = valid_loss
                        best_model[i].load_state_dict(model[i].state_dict())

                    # early_stopping needs the validation loss to check if it has decresed, 
                    # and if it has, it will make a checkpoint of the current model
                    early_stopping(valid_loss, model[i])

                    if early_stopping.early_stop:
                        print("Early stopping")
                        break

            if early_stopping.early_stop:
                average_train.append(avg_train_losses)
                average_valid.append(avg_valid_losses)
                break

    return  model, best_model, average_train, average_valid
net_type = [Net().__class__, Net2().__class__]
net = [] 
best_net = []

for i in range(num_folds):
    net.append(net_type[i % len(net_type)]())
    best_net.append(net_type[i % len(net_type)]())

model, best_model, train_loss, valid_loss = train_model(net,best_net)
for i in range(num_folds):
    PATH = model_folder + "/" + model_name + str(i) +".pt"
    torch.save(best_model[i].state_dict(), PATH)<jupyter_output><empty_output><jupyter_text>Step 4: * __Some stats about the train dataset:__<jupyter_code>labels = stat_img (validation_loader)
test = dict()
for i in range(len(labels)):
    test[i] = Counter(labels[i])
df = pandas.DataFrame.from_dict(test, orient='index')
df.plot(kind='bar')
print("******************************************")
print("******* Description of the datasets ****** ")
print("******************************************")

for i in range(len(labels)):
    faces = df['face'][i]
    others = df['other'][i]
    print("*************** DATASET N° {0:1} ************* ".format(i))
    print("------------------------------------------")
    print(" Number of faces : ",faces)
    print(" Number of others: ",others)
    print(" Total           : ", faces+others)
    print("------------------------------------------")
    print(" Number of faces :  {0:.0%}".format(faces/(faces+others)))
    print(" Number of others:  {0:.0%}".format(others/(faces+others)))
    print("******************************************")<jupyter_output>******************************************
******* Description of the datasets ****** 
******************************************
*************** DATASET N° 0 ************* 
------------------------------------------
 Number of faces :  6439
 Number of others:  2733
 Total           :  9172
------------------------------------------
 Number of faces :  70%
 Number of others:  30%
******************************************
*************** DATASET N° 1 ************* 
------------------------------------------
 Number of faces :  6548
 Number of others:  2624
 Total           :  9172
------------------------------------------
 Number of faces :  71%
 Number of others:  29%
******************************************<jupyter_text>* __Visualizing the Loss and the Early Stopping Checkpoint__<jupyter_code>for i in range(len(labels)):
    # visualize the loss as the network trained
    fig = plt.figure(figsize=(10,8))
    plt.plot(range(1,len(train_loss[i])+1),train_loss[i], label='Training Loss')
    plt.plot(range(1,len(valid_loss[i])+1),valid_loss[i],label='Validation Loss')

    # find position of lowest validation loss
    minposs = valid_loss[i].index(min(valid_loss[i]))+1 
    plt.axvline(minposs, linestyle='--', color='r',label='Early Stopping Checkpoint')

    plt.xlabel('mini-batches')
    plt.ylabel('loss')
    plt.ylim(0, 0.3) # consistent scale
    plt.xlim(0, len(train_loss[i])+1)
    plt.grid(True)
    plt.legend()
    plt.tight_layout()
    plt.show()
    fig.savefig('loss_plot.png', bbox_inches='tight')<jupyter_output><empty_output><jupyter_text>Step 5: * __Some stats about the test dataset:__<jupyter_code>def stat_img_test (test_loader):
    list_labels=[]
    for i, data in enumerate(test_loader,0):
        inputs, labels = data
        for j in range(len(labels)):
            list_labels.append(classes[labels[j]])
    
    return list_labels
labels = stat_img_test (test_loader)
labels_counts = Counter(labels)
df = pandas.DataFrame.from_dict(labels_counts, orient='index')
df.plot(kind='bar')
faces = df[0]['face']
others = df[0]['other']

print("****** Description of the dataset ******* ")
print("-----------------------------------------")
print(" Number of faces : ",faces)
print(" Number of others: ",others)
print(" Total           : ", faces+others)
print("-----------------------------------------")
print(" Number of faces :  {0:.0%}".format(faces/(faces+others)))
print(" Number of others:  {0:.0%}".format(others/(faces+others)))
print("-----------------------------------------")<jupyter_output>****** Description of the dataset ******* 
-----------------------------------------
 Number of faces :  797
 Number of others:  6831
 Total           :  7628
-----------------------------------------
 Number of faces :  10%
 Number of others:  90%
-----------------------------------------<jupyter_text>* __Visualizing some random images from the validation dataset and comparing with the prediction  :__<jupyter_code>dataiter = iter(validation_loader[0])
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid([images[j] for j in range(4)]))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

net = Net()
PATH = './models/model.pt'
net.load_state_dict(torch.load(PATH))

outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
correct = 0
total = 0
with torch.no_grad():
    avg = []
    for i in range(num_folds):
        for data in iter(validation_loader[i]):
            images, labels = data
            outputs = net(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
            if total > 10000 :
                print('Accuracy of the network on the %2d test images: %d %%'  % (total, 100 * correct / total))
                tmp = float(100 * correct / total)
                avg.append(tmp)
                break
    
    print('Average accuracy of the network : {} %'.format(int(np.average(avg))))

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in iter(validation_loader[0]):
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
for i in range(2):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))<jupyter_output>Accuracy of other : 100 %
Accuracy of  face : 100 %<jupyter_text>* __Visualizing some random images from the test dataset and comparing with the prediction  :__<jupyter_code>dataiter = iter(test_loader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
correct = 0
total = 0
with torch.no_grad():
    for data in iter(test_loader):
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the %2d test images: %d %%'  % (total,
    100 * correct / total))
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in iter(test_loader):
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1
for i in range(2):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))
@torch.no_grad()
def get_all_preds(model, loader):
    all_preds = torch.tensor([])
    for batch in loader:
        images, labels = batch

        preds = model(images)
        all_preds = torch.cat(
            (all_preds, preds)
            ,dim=0
        )
    return all_preds
with torch.no_grad():
    prediction_loader = torch.utils.data.DataLoader(testset, batch_size=10000)
    train_preds = get_all_preds(net, test_loader)
def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt), horizontalalignment="center", color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
import matplotlib.pyplot as plt
import itertools

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(testset.targets, train_preds.argmax(dim=1))
plot_confusion_matrix(cm, classes)<jupyter_output>Confusion matrix, without normalization
[[6813   18]
 [ 103  694]]
{'fraction_non_alphanumeric': 0.18980643994211288, 'fraction_numerical': 0.02039616497829233, 'mean_word_length': 2.5448861814684194, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '609005', 'license': 'MIT', 'n_tokens_mistral': 6977, 'n_tokens_neox': 6597, 'n_words': 1608, 'path': 'Notebook.ipynb', 'repo_name': 'elghali97/Face-Recognition', 'size': '22112'}