Hacer su propio sistema de reconocimiento de rostros

El reconocimiento de rostros es la última tendencia en lo que respecta a la autenticación de usuarios. Apple lanzó recientemente su nuevo iPhone X que usa Face ID para autenticar usuarios. OnePlus 5 recibirá pronto la característica de desbloqueo facial de OnePlus 5T. Y Baidu usa el reconocimiento facial en lugar de tarjetas de identificación para permitir que sus empleados ingresen a sus oficinas . Estas aplicaciones pueden parecer mágicas para mucha gente. Pero en este artículo nuestro objetivo es desmitificar el tema enseñándole cómo hacer su propia versión simplificada de un sistema de reconocimiento facial en Python.

Enlace de Github para aquellos a los que no les gusta leer y solo quieren el código

Fondo

Antes de entrar en los detalles de la implementación, quiero analizar los detalles de FaceNet. ¿Cuál es la red que usaremos en nuestro sistema?

FaceNet

FaceNet es una red neuronal que aprende un mapeo de imágenes faciales a un espacio euclidiano compacto donde las distancias corresponden a una medida de similitud de caras. Es decir, cuanto más similares sean las dos imágenes faciales, menor será la distancia entre ellas.

Pérdida de triplete

FaceNet usa un método de pérdida distinto llamado Triplet Loss para calcular la pérdida. La pérdida de triplete minimiza la distancia entre un anclaje y un positivo, imágenes que contienen la misma identidad y maximiza la distancia entre el ancla y un negativo, imágenes que contienen diferentes identidades.

Figura 1: ecuación de pérdida de triplete

  • f (a) se refiere a la codificación de salida del ancla
  • f (p) se refiere a la codificación de salida de los positivos
  • f (n) se refiere a la codificación de salida del negativo
  • alpha es una constante utilizada para asegurarse de que la red no intente optimizar hacia f (a) – f (p) = f (a) – f (n) = 0.
  • […] + es igual a max (0, suma)

Redes siamesas

Figura 2: Un ejemplo de una red siamesa que utiliza imágenes de caras como entrada y genera una codificación de 128 números de la imagen. Fuente: Coursera

FaceNet es una red siamesa. Una red siamesa es un tipo de arquitectura de red neuronal que aprende a diferenciar entre dos entradas. Esto les permite saber qué imágenes son similares y cuáles no. Estas imágenes pueden contener caras.

Las redes siamesas constan de dos redes neuronales idénticas, cada una con los mismos pesos exactos. Primero, cada red toma una de las dos imágenes de entrada como entrada. Luego, las salidas de las últimas capas de cada red se envían a una función que determina si las imágenes contienen la misma identidad.

En FaceNet, esto se hace calculando la distancia entre las dos salidas.

Implementación

Ahora que hemos aclarado la teoría, podemos saltar directamente a la implementación.

En nuestra implementación vamos a utilizar Keras y Tensorflow . Además, estamos utilizando dos archivos de utilidad que obtuvimos del repositorio deeplearning.ai para abstraer todas las interacciones con la red FaceNet .:

  • fr_utils.py contiene funciones para alimentar imágenes a la red y obtener la codificación de imágenes
  • inception_blocks_v2.py contiene funciones para preparar y compilar la red FaceNet

Compilando la red FaceNet

Lo primero que tenemos que hacer es compilar la red FaceNet para que podamos usarla para nuestro sistema de reconocimiento de rostros.

 importación os 
importar glob
importar numpy como np
importar cv2
importar tensorflow como tf
de la importación fr_utils *
de inception_blocks_v2 import *
desde keras importar backend como K
 K.set_image_data_format ('channels_first') 
 FRmodel = faceRecoModel (input_shape = (3, 96, 96)) 
 def triplet_loss (y_true, y_pred, alpha = 0.3): 
ancla, positivo, negativo = y_pred [0], y_pred [1], y_pred [2]

pos_dist = tf.reduce_sum ( tf.square ( tf.subtract (ancla,
positivo)), eje = -1)
neg_dist = tf.reduce_sum ( tf.square ( tf.subtract (ancla,
negativo)), eje = -1)
basic_loss = tf.add ( tf.subtract (pos_dist, neg_dist), alpha)
pérdida = tf.reduce_sum ( tf.maximum (basic_loss, 0.0))

pérdida de retorno
 FRmodel.compile (optimizer = 'adam', loss = triplet_loss, metrics = ['accuracy']) 
load_weights_from_FaceNet (FRmodel)

Comenzaremos por inicializar nuestra red con una forma de entrada de (3, 96, 96). Eso significa que los canales Rojo-Verde-Azul (RGB) son la primera dimensión del volumen de imagen alimentado a la red. Y que todas las imágenes que se envían a la red deben ser imágenes de 96×96 píxeles.

A continuación definiremos la función de pérdida de triplete. La función en el fragmento de código anterior sigue la definición de la ecuación de pérdida de triplete que definimos en la sección anterior.

Si no está familiarizado con ninguna de las funciones de Tensorflow utilizadas para realizar el cálculo, le recomiendo leer la documentación (para la cual he agregado enlaces para cada función), ya que mejorará su comprensión del código. Pero comparar la función con la ecuación en la Figura 1 debería ser suficiente.

Una vez que tenemos nuestra función de pérdida, podemos compilar nuestro modelo de reconocimiento facial usando Keras. Y usaremos el Optimizador Adam para minimizar la pérdida calculada por la función Triplet Loss.

Preparar una base de datos

Ahora que hemos compilado FaceNet, vamos a preparar una base de datos de individuos que queremos que reconozca nuestro sistema. Vamos a usar todas las imágenes contenidas en nuestras imágenes directorio para nuestra base de datos de individuos.

NOTA: Solo vamos a usar una imagen de cada individuo en nuestra implementación. ¡La razón es que la red FaceNet es lo suficientemente potente como para que solo se necesite una imagen de un individuo para reconocerla!

 def prepare_database (): 
base de datos = {}
 para el archivo en glob.glob ("images / *"): 
identity = os.path.splitext (os.path.basename (file)) [0]
database [identity] = img_path_to_encoding (file, FRmodel)
 volver base de datos 

Para cada imagen, convertiremos los datos de la imagen a una codificación de 128 números flotantes. Hacemos esto llamando a la función img_path_to_encoding . La función toma una ruta a una imagen y la alimenta a nuestra red de reconocimiento de rostros. Luego, devuelve la salida de la red, que es la codificación de la imagen.

Una vez que hemos agregado la codificación para cada imagen a nuestra base de datos, ¡nuestro sistema finalmente puede comenzar a reconocer individuos!

Reconociendo una cara

Como se discutió en la sección Antecedentes, FaceNet está entrenado para minimizar la distancia entre las imágenes del mismo individuo y maximizar la distancia entre las imágenes de diferentes individuos. Nuestra implementación utiliza esta información para determinar qué individuo es más probable que sea la nueva imagen alimentada a nuestro sistema.

 def who_is_it (imagen, base de datos, modelo): 
encoding = img_to_encoding (imagen, modelo)

min_dist = 100
identidad = Ninguna

# Bucle sobre los nombres y codificaciones del diccionario de la base de datos.
para (nombre, db_enc) en database.items ():
dist = np.linalg.norm (db_enc - codificación)
 print ('distancia para% s es% s'% (nombre, dist)) 
 if dist <min_dist: 
min_dist = dist
identidad = nombre

si min_dist> 0.52:
devolver ninguno
más:
identidad de retorno

La función anterior alimenta la nueva imagen en una función de utilidad llamada img_to_encoding . La función procesa una imagen usando FaceNet y devuelve la codificación de la imagen. Ahora que tenemos la codificación, podemos encontrar al individuo al que probablemente pertenece la imagen.

Para encontrar al individuo, pasamos por nuestra base de datos y calculamos la distancia entre nuestra nueva imagen y cada individuo en la base de datos. El individuo con la distancia más baja a la nueva imagen se elige como el candidato más probable.

Finalmente, debemos determinar si la imagen candidata y la nueva imagen contienen la misma persona o no. Dado que al final de nuestro ciclo solo hemos determinado el individuo más probable. Aquí es donde entra en juego el siguiente fragmento de código.

 si min_dist> 0.52: 
devolver ninguno
más:
identidad de retorno
  • Si la distancia es superior a 0.52, determinamos que el individuo en la nueva imagen no existe en nuestra base de datos.
  • Pero, si la distancia es igual o menor a 0.52, ¡entonces determinamos que son el mismo individuo!

Ahora la parte difícil aquí es que el valor 0.52 se logró a través de prueba y error en mi nombre para mi conjunto de datos específico. El mejor valor podría ser mucho más bajo o ligeramente más alto y dependerá de su implementación y datos. ¡Recomiendo probar diferentes valores y ver qué se adapta mejor a su sistema!

Construyendo un sistema usando Reconocimiento de Rostros

Ahora que conocemos los detalles sobre cómo reconocemos a una persona que usa un algoritmo de reconocimiento facial, podemos empezar a divertirnos con eso.

En el repositorio de Github al que me he vinculado al principio de este artículo hay una demostración que usa la cámara web de una computadora portátil para alimentar marcos de video a nuestro algoritmo de reconocimiento de rostros. Una vez que el algoritmo reconoce a un individuo en el marco, la demostración reproduce un mensaje de audio que da la bienvenida al usuario que usa el nombre de su imagen en la base de datos. La Figura 3 muestra un ejemplo de la demostración en acción.

Figura 3: una imagen capturada en el momento exacto en que la red reconoce al individuo en la imagen. El nombre de la imagen en la base de datos era "skuli.jpg", por lo que el mensaje de audio que se reprodujo fue "¡Bienvenido skuli, que tengas un buen día!"

Conclusión

¡Ahora ya deberías estar familiarizado con cómo funcionan los sistemas de reconocimiento de rostros y cómo crear tu propio sistema simplificado de reconocimiento de rostros usando una versión pre-capacitada de la red FaceNet en Python!

Si quieres jugar con la demostración en el repositorio de Github y agregar imágenes de personas que conoces, sigue adelante y bifurca el repositorio.

¡Diviértete con la demostración e impresiona a todos tus amigos con tu increíble conocimiento del reconocimiento facial!

Texto original en inglés.