Reconocimiento de caras con Keras

Daniel Lerch
Neuron4
Published in
4 min readApr 8, 2019

En esta receta vamos a ver como reconocer caras usando Keras. Concretamente, veremos como calcular cuán similares son dos caras, lo que nos permitirá saber si podemos considerar que las dos caras comparadas corresponden a la misma persona.

Preparación del modelo

Usaremos una red pre-entrenada, de forma similar a como lo hemos hecho en artículos anteriores. En este caso, usaremos VGG Face. Sus autores proporcionan los pesos para redes de tipo MatConvNet, Torch y Caffe. Dado que vamos a usar Keras, necesitamos unos pesos compatibles. Puede descargarlos aquí.

El modelo VGG Face puede crearse en Keras con las siguientes lineas:

model = Sequential()
model.add(ZeroPadding2D((1,1),input_shape=(224,224, 3)))
model.add(Convolution2D(64, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(64, (3, 3), activation=’relu’))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, (3, 3), activation=’relu’))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, (3, 3), activation=’relu’))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, (3, 3), activation=’relu’))model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(Convolution2D(4096, (7, 7), activation=’relu’))
model.add(Dropout(0.5))
model.add(Convolution2D(4096, (1, 1), activation=’relu’))
model.add(Dropout(0.5))
model.add(Convolution2D(2622, (1, 1)))
model.add(Flatten())
model.add(Activation(‘softmax’))

A continuación cargaremos los pesos que hemos descargado para poder partir de una red entrenada y prepararemos el modelo para su uso.

model.load_weights(‘vgg_face_weights.h5’)model = Model(inputs=model.layers[0].input,
outputs=model.layers[-2].output)

Básicamente, lo que hemos hecho después de cargar los pesos es eliminar las dos últimas capas. Así, cuando la red procese una imagen obtendremos como salida un vector de 2622 dimensiones que representa su contenido.

Preparando la imagen para la red

Para que la red pueda procesar las imágenes con las que queramos trabajar, necesitaremos algunas transformaciones. Por ejemplo, necesitaremos que la imagen tenga las dimensiones correspondientes a la entrada de la red. Este pre-procesamiento lo haremos con el siguiente código:

img = load_img(image_path, target_size=(224, 224))
img = img_to_array(img)
img = np.expand_dims(img, axis=0)
img = preprocess_input(img)

Procesando la imagen

Una vez disponemos de la imagen preparada la usamos como entrada de la red recogiendo como salida su representación en un vector de 2622 dimensiones.

representation = model.predict(img)[0,:]

Similitud entre dos vectores

Disponemos pues, de un procedimiento que nos permite representar una imagen como un vector. Hemos usado para ello una red entrenada para reconocer caras, por lo que dicho vector, va a representar las características de la cara que estamos analizando. Por lo tanto, los vectores que representan a dos fotos distintas de una misma cara van a ser más similares que los dos vectores que representan a dos caras distintas.

Necesitaremos pues, una métrica que nos permite ver cuán similares son dos vectores. Existen diferentes métricas válidas, pero nosotros, para este ejemplo usaremos la similitud coseno.

Podemos calcular la similitud coseno con el siguiente código:

r1 = model.predict(img1)[0,:]
r2 = model.predict(img2)[0,:]
a = np.matmul(np.transpose(r1), r2)
b = np.sum(np.multiply(r1, r1))
c = np.sum(np.multiply(r2, r2))
cosine_similarity = 1 - (a / (np.sqrt(b) * np.sqrt(c)))

Reconocimiento de caras

Finalmente, para saber si una cara es la que buscamos, solo tenemos que compararla con una foto de muestra. Si la distancia es pequeña consideraremos que se trata de la misma persona.

La dificultad principal es saber que distancia es suficientemente pequeña como para considerar que dos caras son iguales. He hecho algunos experimentos y parece que 0.4 es un buen valor, aunque el lector puede ajustar este valor según considere.

El código para comparar dos caras es sencillo, podría ser como el siguiente:

if cosine_similarity < 0.4:
print("Son la misma persona")
else:
print("No son la misma persona")

Ejemplos

Veamos la distancia entre las fotos de algunos de los más conocidos expertos en el área del Deep Learning.

Por ejemplo, la similitud coseno entre las dos imágenes de Geoff Hinton (primera y segunda imagen) es de 0'1616, la similitud coseno entre las dos imágenes de Yan LeCun (tercera y cuarta imagen) es de 0'2223 y la similitud coseno entre las dos imágenes de Yoshua bengio (últimas dos imágenes) es de 0'3329. Consideraríamos pues, que los pares indicados corresponden a las mismas personas. Interesante el último caso, pues una de las imágenes tiene muy mala calidad, aunque conseguimos detectar que se trata de la misma cara.

Sin embargo, podemos ver que entre caras correspondientes a diferentes personas la similitud coseno es más grande. Por ejemplo, entre la primera y la ultima imagen la similitud coseno es de 0'5248 y entre la segunda y la tercera es de 0'4549.

La receta

Finalmente os dejo la receta.

--

--