faceta-algo-server/deepface/models/facial_recognition/VGGFace.py

163 lines
5.4 KiB
Python

# built-in dependencies
from typing import List
# 3rd party dependencies
import numpy as np
# project dependencies
from deepface.commons import package_utils, weight_utils
from deepface.modules import verification
from deepface.models.FacialRecognition import FacialRecognition
from deepface.commons.logger import Logger
logger = Logger()
# ---------------------------------------
tf_version = package_utils.get_tf_major_version()
if tf_version == 1:
from keras.models import Model, Sequential
from keras.layers import (
Convolution2D,
ZeroPadding2D,
MaxPooling2D,
Flatten,
Dropout,
Activation,
)
else:
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import (
Convolution2D,
ZeroPadding2D,
MaxPooling2D,
Flatten,
Dropout,
Activation,
)
# ---------------------------------------
# pylint: disable=too-few-public-methods
class VggFaceClient(FacialRecognition):
"""
VGG-Face model class
"""
def __init__(self):
self.model = load_model()
self.model_name = "VGG-Face"
self.input_shape = (224, 224)
self.output_shape = 4096
def forward(self, img: np.ndarray) -> List[float]:
"""
Generates embeddings using the VGG-Face model.
This method incorporates an additional normalization layer,
necessitating the override of the forward method.
Args:
img (np.ndarray): pre-loaded image in BGR
Returns
embeddings (list): multi-dimensional vector
"""
# model.predict causes memory issue when it is called in a for loop
# embedding = model.predict(img, verbose=0)[0].tolist()
# having normalization layer in descriptor troubles for some gpu users (e.g. issue 957, 966)
# instead we are now calculating it with traditional way not with keras backend
embedding = self.model(img, training=False).numpy()[0].tolist()
embedding = verification.l2_normalize(embedding)
return embedding.tolist()
def base_model() -> Sequential:
"""
Base model of VGG-Face being used for classification - not to find embeddings
Returns:
model (Sequential): model was trained to classify 2622 identities
"""
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"))
return model
def load_model(
url="https://github.com/serengil/deepface_models/releases/download/v1.0/vgg_face_weights.h5",
) -> Model:
"""
Final VGG-Face model being used for finding embeddings
Returns:
model (Model): returning 4096 dimensional vectors
"""
model = base_model()
weight_file = weight_utils.download_weights_if_necessary(
file_name="vgg_face_weights.h5", source_url=url
)
model = weight_utils.load_model_weights(
model=model, weight_file=weight_file
)
# 2622d dimensional model
# vgg_face_descriptor = Model(inputs=model.layers[0].input, outputs=model.layers[-2].output)
# 4096 dimensional model offers 6% to 14% increasement on accuracy!
# - softmax causes underfitting
# - added normalization layer to avoid underfitting with euclidean
# as described here: https://github.com/serengil/deepface/issues/944
base_model_output = Sequential()
base_model_output = Flatten()(model.layers[-5].output)
# keras backend's l2 normalization layer troubles some gpu users (e.g. issue 957, 966)
# base_model_output = Lambda(lambda x: K.l2_normalize(x, axis=1), name="norm_layer")(
# base_model_output
# )
vgg_face_descriptor = Model(inputs=model.input, outputs=base_model_output)
return vgg_face_descriptor