<jupyter_start><jupyter_text><jupyter_code>from google.colab import drive
drive.mount('/content/drive')
#Importaciones
import random, os, shutil
import imageio
import cv2
import numpy as np
import PIL<jupyter_output><empty_output><jupyter_text>**Descomprimimos la base de datos (unzip)**<jupyter_code>from zipfile import ZipFile
cont = 0
for i in range(20):
  cont = cont + 1
  if cont < 10 and cont != 5 and cont != 6:
    file_name = '/content/drive/My Drive/Terravic_Facial_IR_Database/face0{}.zip'.format(cont)
  elif cont >= 10:
    file_name = '/content/drive/My Drive/Terravic_Facial_IR_Database/face{}.zip'.format(cont)
  with ZipFile(file_name, 'r') as zip: 
    zip.extractall('Terravic_Original')<jupyter_output><empty_output><jupyter_text>**Renombramos las clases de la base de datos** Esta operación es importante para poder manejar con mayor facilidad los nombres de las carpetas que contienen las imágenes de cada individuo, ya que se debe considerar que las colecciones 5 y 6 están dañadas y no es posible acceder a ellas.<jupyter_code>import os

for i in range(1,21):
  if i >= 7 and i <= 9:
    os.rename('Terravic_Original/face0{}'.format(i), 'Terravic_Original/face0{}'.format(i - 2))
  elif i >= 10 and i <= 11:
    os.rename('Terravic_Original/face{}'.format(i), 'Terravic_Original/face0{}'.format(i - 2))
  elif i >= 12:
    os.rename('Terravic_Original/face{}'.format(i), 'Terravic_Original/face{}'.format(i - 2))<jupyter_output><empty_output><jupyter_text>**Construcción de los conjuntos de entrenamiento, validación y prueba** **Ordenamos la base de datos aleatoriamente**<jupyter_code>os.mkdir('Terravic_Shuffled')
#Creación de carpetas
for i in range(18):
  if i < 9: #i = 0
    dest_train = 'Terravic_Shuffled/person0{}/'.format(i+1)
  elif i >= 9:
    dest_train = 'Terravic_Shuffled/person{}/'.format(i+1)
  os.mkdir(dest_train)


for index_class in range(18): #index_class = 0
  sample = 0
  index_class = index_class + 1 #index_class = 1
  if index_class < 10: 
    class_path = 'Terravic_Original/face0{}/'.format(index_class)
  elif index_class >= 10:
    class_path = 'Terravic_Original/face{}/'.format(index_class)

  #Extracción de las imágenes correspondientes a cada individuo
  lst = sorted(os.listdir(class_path))

  #Shuffling the dataset
  random.shuffle(lst)

  for file_name in lst:
    img_person = imageio.imread(class_path + file_name)
    if index_class < 10:
      if sample < 10:                                                       #0000.jpg
        imageio.imwrite('Terravic_Shuffled/person0{}/'.format(index_class) + '000{}.jpg'.format(sample), img_person)
      elif sample >= 10 and sample < 100:
        imageio.imwrite('Terravic_Shuffled/person0{}/'.format(index_class) + '00{}.jpg'.format(sample), img_person)
      elif sample >= 100 and sample < 1000:
        imageio.imwrite('Terravic_Shuffled/person0{}/'.format(index_class) + '0{}.jpg'.format(sample), img_person)
      else:
        imageio.imwrite('Terravic_Shuffled/person0{}/'.format(index_class) + '{}.jpg'.format(sample), img_person)
      
      sample = sample + 1

    else:
      if sample < 10:
        imageio.imwrite('Terravic_Shuffled/person{}/'.format(index_class) + '000{}.jpg'.format(sample), img_person)
      elif sample >= 10 and sample < 100:
        imageio.imwrite('Terravic_Shuffled/person{}/'.format(index_class) + '00{}.jpg'.format(sample), img_person)
      elif sample >= 100 and sample < 1000:
        imageio.imwrite('Terravic_Shuffled/person{}/'.format(index_class) + '0{}.jpg'.format(sample), img_person)
      else:
        imageio.imwrite('Terravic_Shuffled/person{}/'.format(index_class) + '{}.jpg'.format(sample), img_person)
      
      sample = sample + 1
#Comprobación
import os
for i in range(18):
  i = i +1
  if i < 10:
    print('total  images in test_original/person0{}:'.format(i), len(os.listdir('Terravic_Shuffled/person0{}/'.format(i))))
  else:
    print('total  images in test_original/person{}:'.format(i), len(os.listdir('Terravic_Shuffled/person{}/'.format(i))))<jupyter_output>total  images in test_original/person01: 227
total  images in test_original/person02: 620
total  images in test_original/person03: 592
total  images in test_original/person04: 487
total  images in test_original/person05: 1297
total  images in test_original/person06: 857
total  images in test_original/person07: 1117
total  images in test_original/person08: 283
total  images in test_original/person09: 434
total  images in test_original/person10: 2179
total  images in test_original/person11: 1417
total  images in test_original/person12: 1482
total  images in test_original/person13: 1125
total  images in test_original/person14: 1611
total  images in test_original/person15: 2632
total  images in test_original/person16: 2215
total  images in test_original/person17: 2539
total  images in test_original/person18: 1670<jupyter_text>**Creación de las carpetas de entrenamiento, validación y prueba, con sus respectivas carpetas para cada individuo**<jupyter_code>datasets = ['train', 'validation', 'test']

for dataset_name in datasets:
  os.mkdir(dataset_name)
  for i in range(18):
    if i < 9:
      dest_train = dataset_name+'/person0{}/'.format(i+1)
    elif i >= 9:
      dest_train = dataset_name+'/person{}/'.format(i+1)
    os.mkdir(dest_train)
def fill_dataset(src_dataset, dest_dataset, person_index, cont, limit):

  if person_index < 10:
    src_path = src_dataset+'/person0{}/'.format(person_index)
    dest_path = dest_dataset+'/person0{}/'.format(person_index)
  else:
    src_path = src_dataset+'/person{}/'.format(person_index)
    dest_path = dest_dataset+'/person{}/'.format(person_index)

  lst = sorted(os.listdir(src_path))
        
  for file_name in lst[cont:limit]:
    if cont < limit:
      img_original = imageio.imread(src_path + file_name)
      if cont < 10:
          imageio.imwrite(dest_path + '000{}.jpg'.format(cont), img_original)
      elif cont >= 10 and cont < 100:
          imageio.imwrite(dest_path + '00{}.jpg'.format(cont), img_original)
      else: 
          imageio.imwrite(dest_path + '0{}.jpg'.format(cont), img_original)
              
      cont = cont + 1<jupyter_output><empty_output><jupyter_text>**Asignación de imágenes a cada conjunto**<jupyter_code>for i in range (1, 19):
  if i < 10:
    total = len(os.listdir('Terravic_Shuffled/person0{}'.format(i)))
  else:
    total = len(os.listdir('Terravic_Shuffled/person{}'.format(i)))
  limit1 = total - 127
  if limit1 % 2 == 0:
    limit_validation = int(limit1 / 2)
    limit_test = limit_validation
  else:
    limit_validation = limit1 // 2
    limit_test = (limit1 // 2) + 1

  fill_dataset('Terravic_Shuffled','train', i, 0, 127)
  print(limit_validation + 127)
  fill_dataset('Terravic_Shuffled','validation', i, 127, limit_validation + 127)
  fill_dataset('Terravic_Shuffled','test', i, limit_validation + 127, limit_validation + limit_test + 127)
#Comprobación
import os
for i in range(18):
  i = i +1
  if i < 10:
    print('total  images in train/person0{}:'.format(i), len(os.listdir('train/person0{}/'.format(i))))
    print('total  images in validation/person0{}:'.format(i), len(os.listdir('validation/person0{}/'.format(i))))
    print('total  images in test/person0{}:'.format(i), len(os.listdir('test/person0{}/'.format(i))))
  else:
    print('total  images in train/person{}:'.format(i), len(os.listdir('train/person{}/'.format(i))))
    print('total  images in validation/person{}:'.format(i), len(os.listdir('validation/person{}/'.format(i))))
    print('total  images in test/person{}:'.format(i), len(os.listdir('test/person{}/'.format(i))))<jupyter_output>total  images in train/person01: 127
total  images in validation/person01: 50
total  images in test/person01: 50
total  images in train/person02: 127
total  images in validation/person02: 246
total  images in test/person02: 247
total  images in train/person03: 127
total  images in validation/person03: 232
total  images in test/person03: 233
total  images in train/person04: 127
total  images in validation/person04: 180
total  images in test/person04: 180
total  images in train/person05: 127
total  images in validation/person05: 585
total  images in test/person05: 585
total  images in train/person06: 127
total  images in validation/person06: 365
total  images in test/person06: 365
total  images in train/person07: 127
total  images in validation/person07: 495
total  images in test/person07: 495
total  images in train/person08: 127
total  images in validation/person08: 78
total  images in test/person08: 78
total  images in train/person09: 127
total  images in validation/person09: 153
total[...]<jupyter_text>**Sistema de reconocimiento facial** **Referenciamos los conjuntos de entrenamiento, validación y prueba**<jupyter_code>train_dir = os.path.join('train')
validation_dir = os.path.join('validation')
test_dir = os.path.join('test')<jupyter_output><empty_output><jupyter_text>**Carga de la arquitectura VGG16**<jupyter_code>from tensorflow.keras.applications import VGG16

conv_base = VGG16(weights='imagenet',
                  include_top=False,
                  input_shape=(72, 96, 3)) # input_shape = (largo, ancho, canales)

conv_base.summary()<jupyter_output>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5
58892288/58889256 [==============================] - 0s 0us/step
Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 72, 96, 3)]       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 72, 96, 64)        1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 72, 96, 64)        36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 36, 48, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 36, 48, 128)       [...]<jupyter_text>**Congelamos y descongelamos ciertas capas (Fine-tuning)**<jupyter_code>for layer in conv_base.layers:
    if layer.name[:6] == 'block5': 
      layer.trainable = True
    else:
      layer.trainable = False

conv_base.summary()<jupyter_output>Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 72, 96, 3)]       0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 72, 96, 64)        1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 72, 96, 64)        36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 36, 48, 64)        0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 36, 48, 128)       73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 36, 48, 128)       147584    
_____________________________________________________________[...]<jupyter_text>**Definición de la arquitectura (incluyendo el módulo de transfer learning)**<jupyter_code>from keras import layers
from keras import models
from keras.layers.normalization import BatchNormalization

model = models.Sequential()
model.add(conv_base)
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.BatchNormalization())
model.add(layers.Flatten())
model.add(layers.Dense(18, activation='softmax'))

model.summary()<jupyter_output>Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
vgg16 (Functional)           (None, 2, 3, 512)         14714688  
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 1, 1, 512)         0         
_________________________________________________________________
batch_normalization_1 (Batch (None, 1, 1, 512)         2048      
_________________________________________________________________
flatten_1 (Flatten)          (None, 512)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 18)                9234      
=================================================================
Total params: 14,725,970
Trainable params: 7,089,682
Non-trainable params: 7,636,288
___________________________________[...]<jupyter_text>**Compilación**<jupyter_code>from keras import optimizers
model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc']) #'acc' -> accuracy -> precisión<jupyter_output><empty_output><jupyter_text>**Definición de los generadores**<jupyter_code>#Using ImageDataGenerator to read images from directories
from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
        train_dir, # Target directory
        target_size=(72, 96), # All images are resized from 240x320 to 72x96
        batch_size= 9, 
        color_mode='rgb',
        class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
        validation_dir,
        target_size=(72, 96),
        batch_size=1,
        color_mode='rgb',
        class_mode='categorical')<jupyter_output>Found 2286 images belonging to 18 classes.
Found 10246 images belonging to 18 classes.<jupyter_text>**Entrenamiento**<jupyter_code>#Training and validation stages
history = model.fit(
      train_generator,
      steps_per_epoch=254, #70
      epochs= 30,
      validation_data=validation_generator,
      validation_steps=10246)<jupyter_output>Epoch 1/30
254/254 [==============================] - 56s 220ms/step - loss: 0.1400 - acc: 0.9668 - val_loss: 0.0080 - val_acc: 0.9997
Epoch 2/30
254/254 [==============================] - 56s 220ms/step - loss: 0.0073 - acc: 0.9987 - val_loss: 6.1551e-04 - val_acc: 1.0000
Epoch 3/30
254/254 [==============================] - 57s 223ms/step - loss: 2.2867e-04 - acc: 1.0000 - val_loss: 5.7451e-05 - val_acc: 1.0000
Epoch 4/30
254/254 [==============================] - 57s 225ms/step - loss: 0.0017 - acc: 0.9996 - val_loss: 4.3608e-04 - val_acc: 1.0000
Epoch 5/30
254/254 [==============================] - 57s 225ms/step - loss: 8.9068e-05 - acc: 1.0000 - val_loss: 0.0031 - val_acc: 0.9981
Epoch 6/30
254/254 [==============================] - 58s 227ms/step - loss: 4.8521e-05 - acc: 1.0000 - val_loss: 3.2463e-05 - val_acc: 1.0000
Epoch 7/30
254/254 [==============================] - 57s 225ms/step - loss: 2.0069e-05 - acc: 1.0000 - val_loss: 3.1821e-08 - val_acc: 1.0000
Epoch 8/30
254/254 [...]<jupyter_text>**Curvas de precisión y pérdida**<jupyter_code>import matplotlib.pyplot as plt

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'go', label='Precisión en el entrenamiento')
plt.plot(epochs, val_acc, 'r', label='Precisión en la validación')
plt.title('Precisión durante el entrenamiento y la validación')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'go', label='Pérdida en el entrenamiento')
plt.plot(epochs, val_loss, 'r', label='Pérdida en la validación')
plt.title('Pérdida durante el entrenamiento y la validación')
plt.legend()

plt.show()<jupyter_output><empty_output><jupyter_text>**Re-entrenamiento del modelo**<jupyter_code>from keras import layers
from keras import models
from keras.layers.normalization import BatchNormalization

model = models.Sequential()
model.add(conv_base)
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.BatchNormalization())
model.add(layers.Flatten())
model.add(layers.Dense(18, activation='softmax'))

from keras import optimizers
model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=['acc'])
#Training and validation stages
history = model.fit(
      train_generator,
      steps_per_epoch=254, #70
      epochs= 5)
#Guardamos el modelo
model.save('/content/drive/My Drive/face_recognition_model1.h5')
#Cargamos el modelo
from keras.models import load_model

model = load_model('/content/drive/My Drive/face_recognition_model1.h5')<jupyter_output><empty_output><jupyter_text>**Evaluación del modelo final**<jupyter_code>#Test stage
test_generator = test_datagen.flow_from_directory(
        test_dir,
        target_size=(72, 96),
        batch_size=1,
        color_mode='rgb',
        class_mode='categorical')

test_loss, test_acc = model.evaluate_generator(test_generator, steps=900)
print('Recognition rate: ', test_acc)<jupyter_output>Found 900 images belonging to 18 classes.
WARNING:tensorflow:From <ipython-input-26-156ea290fd46>:9: Model.evaluate_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
Please use Model.evaluate, which supports generators.
Recognition rate:  1.0<jupyter_text>**Predicciones**<jupyter_code>from PIL import Image #PILLOW

width = 96
height = 72
image_face = Image.open('train/person12/0029.jpg')
image_face = image_face.resize((width, height), Image.ANTIALIAS)
image_face = np.array(image_face)
image_face = image_face / 255.0 #-> #Tensor -> (72,96)
image_face = np.expand_dims(image_face, axis = 0) #TENSOR -> (1, 72, 96)
image_face = np.expand_dims(image_face, axis = -1) #TENSOR -> (1, 72, 96, 1)
image_face = np.stack((image_face[:,:,:,0], image_face[:,:,:,0], image_face[:,:,:,0]), axis=3) #TENSOR -> (1, 72, 96, 3)
print(image_face.shape)
prediction = model.predict(image_face)
print('La imagen pertenece a la persona', np.argmax(prediction)+1)
prediction<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14236771983250857, 'fraction_numerical': 0.0574800152264941, 'mean_word_length': 4.49611476389719, 'pattern_counts': {'":': 0, '<': 82, '<?xml version=': 0, '>': 81, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '500625', 'license': 'MIT', 'n_tokens_mistral': 6669, 'n_tokens_neox': 5836, 'n_words': 1343, 'path': 'Face_Recognition_System_Final.ipynb', 'repo_name': 'juanalverto/Face_Recognition_System', 'size': '18389'}