Convert Figma logo to code with AI

ZhaoJ9014 logoface.evoLVe

🔥🔥High-Performance Face Recognition Library on PaddlePaddle & PyTorch🔥🔥

3,414
759
3,414
92

Top Related Projects

State-of-the-art 2D and 3D Face Analysis Project

The world's simplest facial recognition api for Python and the command line

13,708

Face recognition using Tensorflow

15,090

Face recognition with deep neural networks.

11,722

A Lightweight Face Recognition and Facial Attribute Analysis (Age, Gender, Emotion and Race) Library for Python

Pretrained Pytorch face detection (MTCNN) and facial recognition (InceptionResnet) models

Quick Overview

face.evoLVe is a comprehensive face recognition toolkit that provides high-performance CNN-based face recognition models and training methods. It aims to facilitate research and development in face recognition technology by offering pre-trained models, training pipelines, and evaluation tools.

Pros

  • Offers a variety of pre-trained models for different face recognition tasks
  • Provides comprehensive training and evaluation pipelines
  • Supports multiple popular face recognition datasets
  • Includes data preprocessing and augmentation techniques

Cons

  • Limited documentation for some advanced features
  • Requires significant computational resources for training large models
  • May have a steep learning curve for beginners in face recognition
  • Dependency on specific versions of libraries may cause compatibility issues

Code Examples

  1. Loading a pre-trained model:
from backbone.model_irse import IR_SE_50
from utils import load_state_dict

model = IR_SE_50([112, 112])
state_dict = load_state_dict('path/to/model_ir_se50.pth')
model.load_state_dict(state_dict)
  1. Preprocessing an image for face recognition:
from data_processor.data_processor import preprocess

img = preprocess(img_path, landmark=True)
  1. Extracting face embeddings:
import torch

with torch.no_grad():
    embedding = model(img.unsqueeze(0)).squeeze().cpu().numpy()

Getting Started

  1. Clone the repository:

    git clone https://github.com/ZhaoJ9014/face.evoLVe.git
    cd face.evoLVe
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Download pre-trained models:

    mkdir pretrained
    # Download model files and place them in the 'pretrained' directory
    
  4. Run the demo script:

    python demo.py
    

Competitor Comparisons

State-of-the-art 2D and 3D Face Analysis Project

Pros of insightface

  • More comprehensive, offering a wider range of face-related tasks and models
  • Better documentation and examples, making it easier for users to get started
  • More active development and community support

Cons of insightface

  • Potentially more complex to use due to its broader scope
  • May require more computational resources for some models

Code Comparison

insightface:

import insightface
model = insightface.app.FaceAnalysis()
model.prepare(ctx_id=0, det_size=(640, 640))
img = insightface.utils.get_image('t1')
faces = model.get(img)

face.evoLVe:

from face_evolve import FaceNet
model = FaceNet()
model.load_weights('path/to/weights')
embeddings = model.predict(images)

The code snippets demonstrate that insightface offers a more comprehensive face analysis solution out-of-the-box, while face.evoLVe focuses primarily on face recognition and embedding generation.

insightface provides a higher-level API that handles multiple face-related tasks, including detection and analysis, in a single call. face.evoLVe, on the other hand, requires more manual setup and is more focused on generating face embeddings for recognition tasks.

Both libraries have their strengths, with insightface being more versatile and face.evoLVe potentially offering more flexibility for specific face recognition applications.

The world's simplest facial recognition api for Python and the command line

Pros of face_recognition

  • Easier to use and install, with a simpler API
  • Better documentation and examples for beginners
  • Supports multiple face recognition tasks out-of-the-box (detection, encoding, comparison)

Cons of face_recognition

  • Less flexible for advanced users and custom model training
  • May have lower accuracy on challenging datasets compared to face.evoLVe
  • Limited support for deep learning frameworks (primarily uses dlib)

Code Comparison

face_recognition:

import face_recognition

image = face_recognition.load_image_file("image.jpg")
face_locations = face_recognition.face_locations(image)
face_encodings = face_recognition.face_encodings(image, face_locations)

face.evoLVe:

from backbone.model_irse import IR_SE_50
from util.extract_feature import extract_feature

model = IR_SE_50([112, 112])
model.load_state_dict(torch.load('model_ir_se50.pth'))
features = extract_feature(model, data_loader, device)

The face_recognition code is more straightforward for basic tasks, while face.evoLVe offers more control over the model and feature extraction process. face.evoLVe requires more setup and understanding of deep learning concepts but provides greater flexibility for advanced users.

13,708

Face recognition using Tensorflow

Pros of facenet

  • More established project with a larger community and longer history
  • Extensive documentation and examples for various use cases
  • Supports multiple deep learning frameworks (TensorFlow and PyTorch)

Cons of facenet

  • Less frequent updates and maintenance compared to face.evoLVe
  • Older codebase may not incorporate some of the latest advancements in face recognition

Code Comparison

facenet:

embeddings = facenet.get_embeddings(images)
distances = np.sqrt(np.sum(np.square(np.subtract(embeddings[0], embeddings[1:])), axis=1))

face.evoLVe:

features = face_evolve.extract_features(images)
similarities = face_evolve.compute_similarity(features[0], features[1:])

Both projects provide similar functionality for face recognition tasks, but face.evoLVe offers a more modern and actively maintained codebase with potentially improved performance. facenet, on the other hand, has a larger community and more extensive documentation, making it easier for beginners to get started. The choice between the two depends on specific project requirements and the need for cutting-edge features versus established community support.

15,090

Face recognition with deep neural networks.

Pros of OpenFace

  • More established project with a longer history and larger community
  • Provides pre-trained models for immediate use
  • Offers comprehensive documentation and tutorials

Cons of OpenFace

  • Less frequent updates and maintenance
  • Limited to specific deep learning frameworks (Torch)
  • Fewer options for customization and fine-tuning

Code Comparison

OpenFace:

import openface

align = openface.AlignDlib("models/dlib/shape_predictor_68_face_landmarks.dat")
net = openface.TorchNeuralNet("models/openface/nn4.small2.v1.t7", 96)
rep = net.forward(align.align(96, img, bb))

face.evoLVe:

from face_evolve import FaceEvolve

model = FaceEvolve(backbone_type='ResNet50', use_se=True)
model.load_state_dict(torch.load('path/to/model.pth'))
features = model(images)

Summary

OpenFace is a more mature project with ready-to-use models and extensive documentation, making it suitable for quick implementation. However, it lacks recent updates and flexibility. face.evoLVe, while newer, offers more customization options and supports modern deep learning frameworks, but may require more setup and expertise to use effectively.

11,722

A Lightweight Face Recognition and Facial Attribute Analysis (Age, Gender, Emotion and Race) Library for Python

Pros of deepface

  • More comprehensive face analysis capabilities, including age, gender, emotion, and race prediction
  • Easier to use with a high-level API and pre-trained models
  • Better documentation and examples for quick implementation

Cons of deepface

  • Less focus on face recognition performance optimization
  • Fewer options for fine-tuning and customizing models
  • Limited support for large-scale face recognition tasks

Code Comparison

deepface:

from deepface import DeepFace

result = DeepFace.verify("img1.jpg", "img2.jpg")
print("Is same person:", result["verified"])

face.evoLVe:

from backbone.model_irse import IR_50
from util.extract_feature import extract_feature

model = IR_50([112, 112])
img_feats = extract_feature(model, img_list, batch_size)

The deepface code snippet demonstrates its simplicity for face verification tasks, while face.evoLVe's code shows a more low-level approach, allowing for greater customization but requiring more setup and understanding of the underlying architecture.

deepface is better suited for developers who need quick implementation of various face analysis tasks, while face.evoLVe may be preferred by researchers or those requiring more control over the face recognition process and performance optimization.

Pretrained Pytorch face detection (MTCNN) and facial recognition (InceptionResnet) models

Pros of facenet-pytorch

  • More focused on PyTorch implementation, making it easier for PyTorch users
  • Provides pre-trained models and utilities for face recognition tasks
  • Actively maintained with recent updates and contributions

Cons of facenet-pytorch

  • Limited to FaceNet architecture, while face.evoLVe offers multiple models
  • Less comprehensive documentation compared to face.evoLVe
  • Fewer training and evaluation options than face.evoLVe

Code Comparison

facenet-pytorch:

from facenet_pytorch import MTCNN, InceptionResnetV1

mtcnn = MTCNN()
resnet = InceptionResnetV1(pretrained='vggface2').eval()

img = Image.open('path/to/image.jpg')
img_cropped = mtcnn(img)
img_embedding = resnet(img_cropped.unsqueeze(0))

face.evoLVe:

from backbone.model_irse import IR_SE_50
from util.extract_feature import extract_feature

model = IR_SE_50([112, 112])
model.load_state_dict(torch.load('path/to/model.pth'))

img = cv2.imread('path/to/image.jpg')
img_embedding = extract_feature(img, model, device)

Both repositories provide implementations for face recognition tasks, but facenet-pytorch focuses on the FaceNet architecture with PyTorch, while face.evoLVe offers a broader range of models and training options. facenet-pytorch is more user-friendly for PyTorch users, while face.evoLVe provides more comprehensive features and documentation.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

face.evoLVe: High-Performance Face Recognition Library based on PaddlePaddle & PyTorch

  • Evolve to be more comprehensive, effective and efficient for face related analytics & applications! (WeChat News)
  • About the name:
    • "face" means this repo is dedicated for face related analytics & applications.
    • "evolve" means unleash your greatness to be better and better. "LV" are capitalized to acknowledge the nurturing of Learning and Vision (LV) group, Nation University of Singapore (NUS).
  • This work was done during Jian Zhao served as a short-term "Texpert" Research Scientist at Tencent FiT DeepSea AI Lab, Shenzhen, China.
AuthorJian Zhao
Homepagehttps://zhaoj9014.github.io

License

The code of face.evoLVe is released under the MIT License.


News

:white_check_mark: CLOSED 02 September 2021: Baidu PaddlePaddle officially merged face.evoLVe to faciliate researches and applications on face-related analytics (Official Announcement).

:white_check_mark: CLOSED 03 July 2021: Provides training code for the paddlepaddle framework.

:white_check_mark: CLOSED 04 July 2019: We will share several publicly available datasets on face anti-spoofing/liveness detection to facilitate related research and analytics.

:white_check_mark: CLOSED 07 June 2019: We are training a better-performing IR-152 model on MS-Celeb-1M_Align_112x112, and will release the model soon.

:white_check_mark: CLOSED 23 May 2019: We share three publicly available datasets to facilitate research on heterogeneous face recognition and analytics. Please refer to Sec. Data Zoo for details.

:white_check_mark: CLOSED 23 Jan 2019: We share the name lists and pair-wise overlapping lists of several widely-used face recognition datasets to help researchers/engineers quickly remove the overlapping parts between their own private datasets and the public datasets. Please refer to Sec. Data Zoo for details.

:white_check_mark: CLOSED 23 Jan 2019: The current distributed training schema with multi-GPUs under PyTorch and other mainstream platforms parallels the backbone across multi-GPUs while relying on a single master to compute the final bottleneck (fully-connected/softmax) layer. This is not an issue for conventional face recognition with moderate number of identities. However, it struggles with large-scale face recognition, which requires recognizing millions of identities in the real world. The master can hardly hold the oversized final layer while the slaves still have redundant computation resource, leading to small-batch training or even failed training. To address this problem, we are developing a highly-elegant, effective and efficient distributed training schema with multi-GPUs under PyTorch, supporting not only the backbone, but also the head with the fully-connected (softmax) layer, to facilitate high-performance large-scale face recognition. We will added this support into our repo.

:white_check_mark: CLOSED 22 Jan 2019: We have released two feature extraction APIs for extracting features from pre-trained models, implemented with PyTorch build-in functions and OpenCV, respectively. Please check ./util/extract_feature_v1.py and ./util/extract_feature_v2.py.

:white_check_mark: CLOSED 22 Jan 2019: We are fine-tuning our released IR-50 model on our private Asia face data, which will be released soon to facilitate high-performance Asia face recognition.

:white_check_mark: CLOSED 21 Jan 2019: We are training a better-performing IR-50 model on MS-Celeb-1M_Align_112x112, and will replace the current model soon.


Contents


face.evoLVe for High-Performance Face Recognition

Introduction

:information_desk_person:

  • This repo provides a comprehensive face recognition library for face related analytics & applications, including face alignment (detection, landmark localization, affine transformation, etc.), data processing (e.g., augmentation, data balancing, normalization, etc.), various backbones (e.g., ResNet, IR, IR-SE, ResNeXt, SE-ResNeXt, DenseNet, LightCNN, MobileNet, ShuffleNet, DPN, etc.), various losses (e.g., Softmax, Focal, Center, SphereFace, CosFace, AmSoftmax, ArcFace, Triplet, etc.) and bags of tricks for improving performance (e.g., training refinements, model tweaks, knowledge distillation, etc.).
  • The current distributed training schema with multi-GPUs under PyTorch and other mainstream platforms parallels the backbone across multi-GPUs while relying on a single master to compute the final bottleneck (fully-connected/softmax) layer. This is not an issue for conventional face recognition with moderate number of identities. However, it struggles with large-scale face recognition, which requires recognizing millions of identities in the real world. The master can hardly hold the oversized final layer while the slaves still have redundant computation resource, leading to small-batch training or even failed training. To address this problem, this repo provides a highly-elegant, effective and efficient distributed training schema with multi-GPUs under PyTorch, supporting not only the backbone, but also the head with the fully-connected (softmax) layer, to facilitate high-performance large-scale face recognition.
  • All data before & after alignment, source codes and trained models are provided.
  • This repo can help researchers/engineers develop high-performance deep face recognition models and algorithms quickly for practical use and deployment.

Pre-Requisites

:cake:

  • Linux or macOS
  • Python 3.7 (for training & validation) and Python 2.7 (for visualization w/ tensorboardX)
  • PyTorch 1.0 (for traininig & validation, install w/ pip install torch torchvision)
  • MXNet 1.3.1 (optional, for data processing, install w/ pip install mxnet-cu90)
  • TensorFlow 1.12 (optional, for visualization, install w/ pip install tensorflow-gpu)
  • tensorboardX 1.6 (optional, for visualization, install w/ pip install tensorboardX)
  • OpenCV 3.4.5 (install w/ pip install opencv-python)
  • bcolz 1.2.0 (install w/ pip install bcolz)

While not required, for optimal performance it is highly recommended to run the code using a CUDA enabled GPU. We used 4-8 NVIDIA Tesla P40 in parallel.


Usage

:orange_book:

  • Clone the repo: git clone https://github.com/ZhaoJ9014/face.evoLVe.PyTorch.git.
  • mkdir data checkpoint log at appropriate directory to store your train/val/test data, checkpoints and training logs.
  • Prepare your train/val/test data (refer to Sec. Data Zoo for publicly available face related databases), and ensure each database folder has the following structure:
    ./data/db_name/
            -> id1/
                -> 1.jpg
                -> ...
            -> id2/
                -> 1.jpg
                -> ...
            -> ...
                -> ...
                -> ...
    
  • Refer to the codes of corresponding sections for specific purposes.

Face Alignment

:triangular_ruler:

  • This section is based on the work of MTCNN.
  • Folder: ./align
  • Face detection, landmark localization APIs and visualization toy example with ipython notebook:
    from PIL import Image
    from detector import detect_faces
    from visualization_utils import show_results
    
    img = Image.open('some_img.jpg') # modify the image path to yours
    bounding_boxes, landmarks = detect_faces(img) # detect bboxes and landmarks for all faces in the image
    show_results(img, bounding_boxes, landmarks) # visualize the results
    
  • Face alignment API (perform face detection, landmark localization and alignment with affine transformations on a whole database folder source_root with the directory structure as demonstrated in Sec. Usage, and store the aligned results to a new folder dest_root with the same directory structure):
    python face_align.py -source_root [source_root] -dest_root [dest_root] -crop_size [crop_size]
    
    # python face_align.py -source_root './data/test' -dest_root './data/test_Aligned' -crop_size 112
    
  • For macOS users, there is no need to worry about *.DS_Store files which may ruin your data, since they will be automatically removed when you run the scripts.
  • Keynotes for customed use: 1) specify the arguments of source_root, dest_root and crop_size to your own values when you run face_align.py; 2) pass your customed min_face_size, thresholds and nms_thresholds values to the detect_faces function of detector.py to match your practical requirements; 3) if you find the speed using face alignment API is a bit slow, you can call face resize API to firstly resize the image whose smaller size is larger than a threshold (specify the arguments of source_root, dest_root and min_side to your own values) before calling the face alignment API:
    python face_resize.py
    

Data Processing

:bar_chart:

  • Folder: ./balance
  • Remove low-shot data API (remove the low-shot classes with less than min_num samples in the training set root with the directory structure as demonstrated in Sec. Usage for data balance and effective model training):
    python remove_lowshot.py -root [root] -min_num [min_num]
    
    # python remove_lowshot.py -root './data/train' -min_num 10
    
  • Keynotes for customed use: specify the arguments of root and min_num to your own values when you run remove_lowshot.py.
  • We prefer to include other data processing tricks, e.g., augmentation (flip horizontally, scale hue/satuation/brightness with coefficients uniformly drawn from [0.6,1.4], add PCA noise with a coefficient sampled from a normal distribution N(0,0.1), etc.), weighted random sampling, normalization, etc. to the main training script in Sec. Training and Validation to be self-contained.

Training and Validation

:coffee:

  • Folder: ./
  • Configuration API (configurate your overall settings for training & validation) config.py:
    import torch
    
    configurations = {
        1: dict(
            SEED = 1337, # random seed for reproduce results
    
            DATA_ROOT = '/media/pc/6T/jasonjzhao/data/faces_emore', # the parent root where your train/val/test data are stored
            MODEL_ROOT = '/media/pc/6T/jasonjzhao/buffer/model', # the root to buffer your checkpoints
            LOG_ROOT = '/media/pc/6T/jasonjzhao/buffer/log', # the root to log your train/val status
            BACKBONE_RESUME_ROOT = './', # the root to resume training from a saved checkpoint
            HEAD_RESUME_ROOT = './', # the root to resume training from a saved checkpoint
    
            BACKBONE_NAME = 'IR_SE_50', # support: ['ResNet_50', 'ResNet_101', 'ResNet_152', 'IR_50', 'IR_101', 'IR_152', 'IR_SE_50', 'IR_SE_101', 'IR_SE_152']
            HEAD_NAME = 'ArcFace', # support:  ['Softmax', 'ArcFace', 'CosFace', 'SphereFace', 'Am_softmax']
            LOSS_NAME = 'Focal', # support: ['Focal', 'Softmax']
    
            INPUT_SIZE = [112, 112], # support: [112, 112] and [224, 224]
            RGB_MEAN = [0.5, 0.5, 0.5], # for normalize inputs to [-1, 1]
            RGB_STD = [0.5, 0.5, 0.5],
            EMBEDDING_SIZE = 512, # feature dimension
            BATCH_SIZE = 512,
            DROP_LAST = True, # whether drop the last batch to ensure consistent batch_norm statistics
            LR = 0.1, # initial LR
            NUM_EPOCH = 125, # total epoch number (use the firt 1/25 epochs to warm up)
            WEIGHT_DECAY = 5e-4, # do not apply to batch_norm parameters
            MOMENTUM = 0.9,
            STAGES = [35, 65, 95], # epoch stages to decay learning rate
    
            DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu"),
            MULTI_GPU = True, # flag to use multiple GPUs; if you choose to train with single GPU, you should first run "export CUDA_VISILE_DEVICES=device_id" to specify the GPU card you want to use
            GPU_ID = [0, 1, 2, 3], # specify your GPU ids
            PIN_MEMORY = True,
            NUM_WORKERS = 0,
    ),
    }
    
  • Train & validation API (all folks about training & validation, i.e., import package, hyperparameters & data loaders, model & loss & optimizer, train & validation & save checkpoint) train.py. Since MS-Celeb-1M serves as an ImageNet in the filed of face recognition, we pre-train the face.evoLVe models on MS-Celeb-1M and perform validation on LFW, CFP_FF, CFP_FP, AgeDB, CALFW, CPLFW and Vggface2_FP. Let's dive into details together step by step.
    • Import necessary packages:
      import torch
      import torch.nn as nn
      import torch.optim as optim
      import torchvision.transforms as transforms
      import torchvision.datasets as datasets
      
      from config import configurations
      from backbone.model_resnet import ResNet_50, ResNet_101, ResNet_152
      from backbone.model_irse import IR_50, IR_101, IR_152, IR_SE_50, IR_SE_101, IR_SE_152
      from head.metrics import ArcFace, CosFace, SphereFace, Am_softmax
      from loss.focal import FocalLoss
      from util.utils import make_weights_for_balanced_classes, get_val_data, separate_irse_bn_paras, separate_resnet_bn_paras, warm_up_lr, schedule_lr, perform_val, get_time, buffer_val, AverageMeter, accuracy
      
      from tensorboardX import SummaryWriter
      from tqdm import tqdm
      import os
      
    • Initialize hyperparameters:
      cfg = configurations[1]
      
      SEED = cfg['SEED'] # random seed for reproduce results
      torch.manual_seed(SEED)
      
      DATA_ROOT = cfg['DATA_ROOT'] # the parent root where your train/val/test data are stored
      MODEL_ROOT = cfg['MODEL_ROOT'] # the root to buffer your checkpoints
      LOG_ROOT = cfg['LOG_ROOT'] # the root to log your train/val status
      BACKBONE_RESUME_ROOT = cfg['BACKBONE_RESUME_ROOT'] # the root to resume training from a saved checkpoint
      HEAD_RESUME_ROOT = cfg['HEAD_RESUME_ROOT']  # the root to resume training from a saved checkpoint
      
      BACKBONE_NAME = cfg['BACKBONE_NAME'] # support: ['ResNet_50', 'ResNet_101', 'ResNet_152', 'IR_50', 'IR_101', 'IR_152', 'IR_SE_50', 'IR_SE_101', 'IR_SE_152']
      HEAD_NAME = cfg['HEAD_NAME'] # support:  ['Softmax', 'ArcFace', 'CosFace', 'SphereFace', 'Am_softmax']
      LOSS_NAME = cfg['LOSS_NAME'] # support: ['Focal', 'Softmax']
      
      INPUT_SIZE = cfg['INPUT_SIZE']
      RGB_MEAN = cfg['RGB_MEAN'] # for normalize inputs
      RGB_STD = cfg['RGB_STD']
      EMBEDDING_SIZE = cfg['EMBEDDING_SIZE'] # feature dimension
      BATCH_SIZE = cfg['BATCH_SIZE']
      DROP_LAST = cfg['DROP_LAST'] # whether drop the last batch to ensure consistent batch_norm statistics
      LR = cfg['LR'] # initial LR
      NUM_EPOCH = cfg['NUM_EPOCH']
      WEIGHT_DECAY = cfg['WEIGHT_DECAY']
      MOMENTUM = cfg['MOMENTUM']
      STAGES = cfg['STAGES'] # epoch stages to decay learning rate
      
      DEVICE = cfg['DEVICE']
      MULTI_GPU = cfg['MULTI_GPU'] # flag to use multiple GPUs
      GPU_ID = cfg['GPU_ID'] # specify your GPU ids
      PIN_MEMORY = cfg['PIN_MEMORY']
      NUM_WORKERS = cfg['NUM_WORKERS']
      print("=" * 60)
      print("Overall Configurations:")
      print(cfg)
      print("=" * 60)
      
      writer = SummaryWriter(LOG_ROOT) # writer for buffering intermedium results
      
    • Train & validation data loaders:
      train_transform = transforms.Compose([ # refer to https://pytorch.org/docs/stable/torchvision/transforms.html for more build-in online data augmentation
          transforms.Resize([int(128 * INPUT_SIZE[0] / 112), int(128 * INPUT_SIZE[0] / 112)]), # smaller side resized
          transforms.RandomCrop([INPUT_SIZE[0], INPUT_SIZE[1]]),
          transforms.RandomHorizontalFlip(),
          transforms.ToTensor(),
          transforms.Normalize(mean = RGB_MEAN,
                               std = RGB_STD),
      ])
      
      dataset_train = datasets.ImageFolder(os.path.join(DATA_ROOT, 'imgs'), train_transform)
      
      # create a weighted random sampler to process imbalanced data
      weights = make_weights_for_balanced_classes(dataset_train.imgs, len(dataset_train.classes))
      weights = torch.DoubleTensor(weights)
      sampler = torch.utils.data.sampler.WeightedRandomSampler(weights, len(weights))
      
      train_loader = torch.utils.data.DataLoader(
          dataset_train, batch_size = BATCH_SIZE, sampler = sampler, pin_memory = PIN_MEMORY,
          num_workers = NUM_WORKERS, drop_last = DROP_LAST
      )
      
      NUM_CLASS = len(train_loader.dataset.classes)
      print("Number of Training Classes: {}".format(NUM_CLASS))
      
      lfw, cfp_ff, cfp_fp, agedb, calfw, cplfw, vgg2_fp, lfw_issame, cfp_ff_issame, cfp_fp_issame, agedb_issame, calfw_issame, cplfw_issame, vgg2_fp_issame = get_val_data(DATA_ROOT)
      
    • Define and initialize model (backbone & head):
      BACKBONE_DICT = {'ResNet_50': ResNet_50(INPUT_SIZE), 
                       'ResNet_101': ResNet_101(INPUT_SIZE), 
                       'ResNet_152': ResNet_152(INPUT_SIZE),
                       'IR_50': IR_50(INPUT_SIZE), 
                       'IR_101': IR_101(INPUT_SIZE), 
                       'IR_152': IR_152(INPUT_SIZE),
                       'IR_SE_50': IR_SE_50(INPUT_SIZE), 
                       'IR_SE_101': IR_SE_101(INPUT_SIZE), 
                       'IR_SE_152': IR_SE_152(INPUT_SIZE)}
      BACKBONE = BACKBONE_DICT[BACKBONE_NAME]
      print("=" * 60)
      print(BACKBONE)
      print("{} Backbone Generated".format(BACKBONE_NAME))
      print("=" * 60)
      
      HEAD_DICT = {'ArcFace': ArcFace(in_features = EMBEDDING_SIZE, out_features = NUM_CLASS, device_id = GPU_ID),
                   'CosFace': CosFace(in_features = EMBEDDING_SIZE, out_features = NUM_CLASS, device_id = GPU_ID),
                   'SphereFace': SphereFace(in_features = EMBEDDING_SIZE, out_features = NUM_CLASS, device_id = GPU_ID),
                   'Am_softmax': Am_softmax(in_features = EMBEDDING_SIZE, out_features = NUM_CLASS, device_id = GPU_ID)}
      HEAD = HEAD_DICT[HEAD_NAME]
      print("=" * 60)
      print(HEAD)
      print("{} Head Generated".format(HEAD_NAME))
      print("=" * 60)
      
    • Define and initialize loss function:
      LOSS_DICT = {'Focal': FocalLoss(), 
                   'Softmax': nn.CrossEntropyLoss()}
      LOSS = LOSS_DICT[LOSS_NAME]
      print("=" * 60)
      print(LOSS)
      print("{} Loss Generated".format(LOSS_NAME))
      print("=" * 60)
      
    • Define and initialize optimizer:
      if BACKBONE_NAME.find("IR") >= 0:
          backbone_paras_only_bn, backbone_paras_wo_bn = separate_irse_bn_paras(BACKBONE) # separate batch_norm parameters from others; do not do weight decay for batch_norm parameters to improve the generalizability
          _, head_paras_wo_bn = separate_irse_bn_paras(HEAD)
      else:
          backbone_paras_only_bn, backbone_paras_wo_bn = separate_resnet_bn_paras(BACKBONE) # separate batch_norm parameters from others; do not do weight decay for batch_norm parameters to improve the generalizability
          _, head_paras_wo_bn = separate_resnet_bn_paras(HEAD)
      OPTIMIZER = optim.SGD([{'params': backbone_paras_wo_bn + head_paras_wo_bn, 'weight_decay': WEIGHT_DECAY}, {'params': backbone_paras_only_bn}], lr = LR, momentum = MOMENTUM)
      print("=" * 60)
      print(OPTIMIZER)
      print("Optimizer Generated")
      print("=" * 60)
      
    • Whether resume from a checkpoint or not:
      if BACKBONE_RESUME_ROOT and HEAD_RESUME_ROOT:
          print("=" * 60)
          if os.path.isfile(BACKBONE_RESUME_ROOT) and os.path.isfile(HEAD_RESUME_ROOT):
              print("Loading Backbone Checkpoint '{}'".format(BACKBONE_RESUME_ROOT))
              BACKBONE.load_state_dict(torch.load(BACKBONE_RESUME_ROOT))
              print("Loading Head Checkpoint '{}'".format(HEAD_RESUME_ROOT))
              HEAD.load_state_dict(torch.load(HEAD_RESUME_ROOT))
          else:
              print("No Checkpoint Found at '{}' and '{}'. Please Have a Check or Continue to Train from Scratch".format(BACKBONE_RESUME_ROOT, HEAD_RESUME_ROOT))
          print("=" * 60)
      
    • Whether use multi-GPU or not:
      if MULTI_GPU:
          # multi-GPU setting
          BACKBONE = nn.DataParallel(BACKBONE, device_ids = GPU_ID)
          BACKBONE = BACKBONE.to(DEVICE)
      else:
          # single-GPU setting
          BACKBONE = BACKBONE.to(DEVICE)
      
    • Minor settings prior to training:
      DISP_FREQ = len(train_loader) // 100 # frequency to display training loss & acc
      
      NUM_EPOCH_WARM_UP = NUM_EPOCH // 25  # use the first 1/25 epochs to warm up
      NUM_BATCH_WARM_UP = len(train_loader) * NUM_EPOCH_WARM_UP  # use the first 1/25 epochs to warm up
      batch = 0  # batch index
      
    • Training & validation & save checkpoint (use the first 1/25 epochs to warm up -- gradually increase LR to the initial value to ensure stable convergence):
      for epoch in range(NUM_EPOCH): # start training process
          
          if epoch == STAGES[0]: # adjust LR for each training stage after warm up, you can also choose to adjust LR manually (with slight modification) once plaueau observed
              schedule_lr(OPTIMIZER)
          if epoch == STAGES[1]:
              schedule_lr(OPTIMIZER)
          if epoch == STAGES[2]:
              schedule_lr(OPTIMIZER)
      
          BACKBONE.train()  # set to training mode
          HEAD.train()
      
          losses = AverageMeter()
          top1 = AverageMeter()
          top5 = AverageMeter()
      
          for inputs, labels in tqdm(iter(train_loader)):
      
              if (epoch + 1 <= NUM_EPOCH_WARM_UP) and (batch + 1 <= NUM_BATCH_WARM_UP): # adjust LR for each training batch during warm up
                  warm_up_lr(batch + 1, NUM_BATCH_WARM_UP, LR, OPTIMIZER)
      
              # compute output
              inputs = inputs.to(DEVICE)
              labels = labels.to(DEVICE).long()
              features = BACKBONE(inputs)
              outputs = HEAD(features, labels)
              loss = LOSS(outputs, labels)
      
              # measure accuracy and record loss
              prec1, prec5 = accuracy(outputs.data, labels, topk = (1, 5))
              losses.update(loss.data.item(), inputs.size(0))
              top1.update(prec1.data.item(), inputs.size(0))
              top5.update(prec5.data.item(), inputs.size(0))
      
              # compute gradient and do SGD step
              OPTIMIZER.zero_grad()
              loss.backward()
              OPTIMIZER.step()
              
              # dispaly training loss & acc every DISP_FREQ
              if ((batch + 1) % DISP_FREQ == 0) and batch != 0:
                  print("=" * 60)
                  print('Epoch {}/{} Batch {}/{}\t'
                        'Training Loss {loss.val:.4f} ({loss.avg:.4f})\t'
                        'Training Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
                        'Training Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(
                      epoch + 1, NUM_EPOCH, batch + 1, len(train_loader) * NUM_EPOCH, loss = losses, top1 = top1, top5 = top5))
                  print("=" * 60)
      
              batch += 1 # batch index
      
          # training statistics per epoch (buffer for visualization)
          epoch_loss = losses.avg
          epoch_acc = top1.avg
          writer.add_scalar("Training_Loss", epoch_loss, epoch + 1)
          writer.add_scalar("Training_Accuracy", epoch_acc, epoch + 1)
          print("=" * 60)
          print('Epoch: {}/{}\t'
                'Training Loss {loss.val:.4f} ({loss.avg:.4f})\t'
                'Training Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
                'Training Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(
              epoch + 1, NUM_EPOCH, loss = losses, top1 = top1, top5 = top5))
          print("=" * 60)
      
          # perform validation & save checkpoints per epoch
          # validation statistics per epoch (buffer for visualization)
          print("=" * 60)
          print("Perform Evaluation on LFW, CFP_FF, CFP_FP, AgeDB, CALFW, CPLFW and VGG2_FP, and Save Checkpoints...")
          accuracy_lfw, best_threshold_lfw, roc_curve_lfw = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, lfw, lfw_issame)
          buffer_val(writer, "LFW", accuracy_lfw, best_threshold_lfw, roc_curve_lfw, epoch + 1)
          accuracy_cfp_ff, best_threshold_cfp_ff, roc_curve_cfp_ff = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, cfp_ff, cfp_ff_issame)
          buffer_val(writer, "CFP_FF", accuracy_cfp_ff, best_threshold_cfp_ff, roc_curve_cfp_ff, epoch + 1)
          accuracy_cfp_fp, best_threshold_cfp_fp, roc_curve_cfp_fp = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, cfp_fp, cfp_fp_issame)
          buffer_val(writer, "CFP_FP", accuracy_cfp_fp, best_threshold_cfp_fp, roc_curve_cfp_fp, epoch + 1)
          accuracy_agedb, best_threshold_agedb, roc_curve_agedb = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, agedb, agedb_issame)
          buffer_val(writer, "AgeDB", accuracy_agedb, best_threshold_agedb, roc_curve_agedb, epoch + 1)
          accuracy_calfw, best_threshold_calfw, roc_curve_calfw = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, calfw, calfw_issame)
          buffer_val(writer, "CALFW", accuracy_calfw, best_threshold_calfw, roc_curve_calfw, epoch + 1)
          accuracy_cplfw, best_threshold_cplfw, roc_curve_cplfw = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, cplfw, cplfw_issame)
          buffer_val(writer, "CPLFW", accuracy_cplfw, best_threshold_cplfw, roc_curve_cplfw, epoch + 1)
          accuracy_vgg2_fp, best_threshold_vgg2_fp, roc_curve_vgg2_fp = perform_val(MULTI_GPU, DEVICE, EMBEDDING_SIZE, BATCH_SIZE, BACKBONE, vgg2_fp, vgg2_fp_issame)
          buffer_val(writer, "VGGFace2_FP", accuracy_vgg2_fp, best_threshold_vgg2_fp, roc_curve_vgg2_fp, epoch + 1)
          print("Epoch {}/{}, Evaluation: LFW Acc: {}, CFP_FF Acc: {}, CFP_FP Acc: {}, AgeDB Acc: {}, CALFW Acc: {}, CPLFW Acc: {}, VGG2_FP Acc: {}".format(epoch + 1, NUM_EPOCH, accuracy_lfw, accuracy_cfp_ff, accuracy_cfp_fp, accuracy_agedb, accuracy_calfw, accuracy_cplfw, accuracy_vgg2_fp))
          print("=" * 60)
      
          # save checkpoints per epoch
          if MULTI_GPU:
              torch.save(BACKBONE.module.state_dict(), os.path.join(MODEL_ROOT, "Backbone_{}_Epoch_{}_Batch_{}_Time_{}_checkpoint.pth".format(BACKBONE_NAME, epoch + 1, batch, get_time())))
              torch.save(HEAD.state_dict(), os.path.join(MODEL_ROOT, "Head_{}_Epoch_{}_Batch_{}_Time_{}_checkpoint.pth".format(HEAD_NAME, epoch + 1, batch, get_time())))
          else:
              torch.save(BACKBONE.state_dict(), os.path.join(MODEL_ROOT, "Backbone_{}_Epoch_{}_Batch_{}_Time_{}_checkpoint.pth".format(BACKBONE_NAME, epoch + 1, batch, get_time())))
              torch.save(HEAD.state_dict(), os.path.join(MODEL_ROOT, "Head_{}_Epoch_{}_Batch_{}_Time_{}_checkpoint.pth".format(HEAD_NAME, epoch + 1, batch, get_time())))
      
  • Now, you can start to play with face.evoLVe and run train.py. User friendly information will popped out on your terminal:
    • About overall configuration:

    • About number of training classes:

    • About backbone details:

    • About head details:

    • About loss details:

    • About optimizer details:

    • About resume training:

    • About training status & statistics (when batch index reachs DISP_FREQ or at the end of each epoch):

    • About validation statistics & save checkpoints (at the end of each epoch):

  • Monitor on-the-fly GPU occupancy with watch -d -n 0.01 nvidia-smi.
  • Please refer to Sec. Model Zoo for specific model weights and corresponding performance.
  • Feature extraction API (extract features from pre-trained models) ./util/extract_feature_v1.py (implemented with PyTorch build-in functions) and ./util/extract_feature_v2.py (implemented with OpenCV).
  • Visualize training & validation statistics with tensorboardX (see Sec. Model Zoo):
    tensorboard --logdir /media/pc/6T/jasonjzhao/buffer/log
    

Data Zoo

:tiger:

DatabaseVersion#Identity#Image#Frame#VideoDownload Link
LFWRaw5,74913,233--Google Drive, Baidu Drive
LFWAlign_250x2505,74913,233--Google Drive, Baidu Drive
LFWAlign_112x1125,74913,233--Google Drive, Baidu Drive
CALFWRaw4,02512,174--Google Drive, Baidu Drive
CALFWAlign_112x1124,02512,174--Google Drive, Baidu Drive
CPLFWRaw3,88411,652--Google Drive, Baidu Drive
CPLFWAlign_112x1123,88411,652--Google Drive, Baidu Drive
CASIA-WebFaceRaw_v110,575494,414--Baidu Drive
CASIA-WebFaceRaw_v210,575494,414--Google Drive, Baidu Drive
CASIA-WebFaceClean10,575455,594--Google Drive, Baidu Drive
MS-Celeb-1MClean100,0005,084,127--Google Drive
MS-Celeb-1MAlign_112x11285,7425,822,653--Google Drive
Vggface2Clean8,6313,086,894--Google Drive
Vggface2_FPAlign_112x112----Google Drive, Baidu Drive
AgeDBRaw57016,488--Google Drive, Baidu Drive
AgeDBAlign_112x11257016,488--Google Drive, Baidu Drive
IJB-AClean5005,39620,3692,085Google Drive, Baidu Drive
IJB-BRaw1,84521,79855,0267,011Google Drive
CFPRaw5007,000--Google Drive, Baidu Drive
CFPAlign_112x1125007,000--Google Drive, Baidu Drive
UmdfacesAlign_112x1128,277367,888--Google Drive, Baidu Drive
CelebARaw10,177202,599--Google Drive, Baidu Drive
CACD-VSRaw2,000163,446--Google Drive, Baidu Drive
YTFAlign_344x3441,595-3,425621,127Google Drive, Baidu Drive
DeepGlintAlign_112x112180,8556,753,545--Google Drive
UTKFaceAlign_200x200-23,708--Google Drive, Baidu Drive
BUAA-VisNirAlign_287x2871505,952--Baidu Drive, PW: xmbc
CASIA NIR-VIS 2.0Align_128x12872517,580--Baidu Drive, PW: 883b
Oulu-CASIARaw8065,000--Baidu Drive, PW: xxp5
NUAA-ImposterDBRaw1512,614--Baidu Drive, PW: if3n
CASIA-SURFRaw1,000--21,000Baidu Drive, PW: izb3
CASIA-FASDRaw50--600Baidu Drive, PW: h5un
CASIA-MFSDRaw50--600
Replay-AttackRaw50--1,200
WebFace260MRaw24M2M-https://www.face-benchmark.org/
  • Remark: unzip CASIA-WebFace clean version with
    unzip casia-maxpy-clean.zip    
    cd casia-maxpy-clean    
    zip -F CASIA-maxpy-clean.zip --out CASIA-maxpy-clean_fix.zip    
    unzip CASIA-maxpy-clean_fix.zip
    
  • Remark: after unzip, get image data & pair ground truths from AgeDB, CFP, LFW and VGGFace2_FP align_112x112 versions with
    import numpy as np
    import bcolz
    import os
    
    def get_pair(root, name):
        carray = bcolz.carray(rootdir = os.path.join(root, name), mode='r')
        issame = np.load('{}/{}_list.npy'.format(root, name))
        return carray, issame
    
    def get_data(data_root):
        agedb_30, agedb_30_issame = get_pair(data_root, 'agedb_30')
        cfp_fp, cfp_fp_issame = get_pair(data_root, 'cfp_fp')
        lfw, lfw_issame = get_pair(data_root, 'lfw')
        vgg2_fp, vgg2_fp_issame = get_pair(data_root, 'vgg2_fp')
        return agedb_30, cfp_fp, lfw, vgg2_fp, agedb_30_issame, cfp_fp_issame, lfw_issame, vgg2_fp_issame
    
    agedb_30, cfp_fp, lfw, vgg2_fp, agedb_30_issame, cfp_fp_issame, lfw_issame, vgg2_fp_issame = get_data(DATA_ROOT)
    
  • Remark: We share MS-Celeb-1M_Top1M_MID2Name.tsv (Google Drive, Baidu Drive), VGGface2_ID2Name.csv (Google Drive, Baidu Drive), VGGface2_FaceScrub_Overlap.txt (Google Drive, Baidu Drive), VGGface2_LFW_Overlap.txt (Google Drive, Baidu Drive), CASIA-WebFace_ID2Name.txt (Google Drive, Baidu Drive), CASIA-WebFace_FaceScrub_Overlap.txt (Google Drive, Baidu Drive), CASIA-WebFace_LFW_Overlap.txt (Google Drive, Baidu Drive), FaceScrub_Name.txt (Google Drive, Baidu Drive), LFW_Name.txt (Google Drive, Baidu Drive), LFW_Log.txt (Google Drive, Baidu Drive) to help researchers/engineers quickly remove the overlapping parts between their own private datasets and the public datasets.
  • Due to release license issue, for other face related databases, please make contact with us in person for more details.

Model Zoo

:monkey:

  • Model

    BackboneHeadLossTraining DataDownload Link
    IR-50ArcFaceFocalMS-Celeb-1M_Align_112x112Google Drive, Baidu Drive
    • Setting

      INPUT_SIZE: [112, 112]; RGB_MEAN: [0.5, 0.5, 0.5]; RGB_STD: [0.5, 0.5, 0.5]; BATCH_SIZE: 512 (drop the last batch to ensure consistent batch_norm statistics); Initial LR: 0.1; NUM_EPOCH: 120; WEIGHT_DECAY: 5e-4 (do not apply to batch_norm parameters); MOMENTUM: 0.9; STAGES: [30, 60, 90]; Augmentation: Random Crop + Horizontal Flip; Imbalanced Data Processing: Weighted Random Sampling; Solver: SGD; GPUs: 4 NVIDIA Tesla P40 in Parallel
      
    • Training & validation statistics

    • Performance

      LFWCFP_FFCFP_FPAgeDBCALFWCPLFWVggface2_FP
      99.7899.6998.1497.5395.8792.4595.22
  • Model

    BackboneHeadLossTraining DataDownload Link
    IR-50ArcFaceFocalPrivate Asia Face DataGoogle Drive, Baidu Drive
    • Setting

      INPUT_SIZE: [112, 112]; RGB_MEAN: [0.5, 0.5, 0.5]; RGB_STD: [0.5, 0.5, 0.5]; BATCH_SIZE: 1024 (drop the last batch to ensure consistent batch_norm statistics); Initial LR: 0.01 (initialize weights from the above model pre-trained on MS-Celeb-1M_Align_112x112); NUM_EPOCH: 80; WEIGHT_DECAY: 5e-4 (do not apply to batch_norm parameters); MOMENTUM: 0.9; STAGES: [20, 40, 60]; Augmentation: Random Crop + Horizontal Flip; Imbalanced Data Processing: Weighted Random Sampling; Solver: SGD; GPUs: 8 NVIDIA Tesla P40 in Parallel
      
    • Performance (please perform evaluation on your own Asia face benchmark dataset)

  • Model

    BackboneHeadLossTraining DataDownload Link
    IR-152ArcFaceFocalMS-Celeb-1M_Align_112x112Baidu Drive, PW: b197
    • Setting

      INPUT_SIZE: [112, 112]; RGB_MEAN: [0.5, 0.5, 0.5]; RGB_STD: [0.5, 0.5, 0.5]; BATCH_SIZE: 256 (drop the last batch to ensure consistent batch_norm statistics); Initial LR: 0.01; NUM_EPOCH: 120; WEIGHT_DECAY: 5e-4 (do not apply to batch_norm parameters); MOMENTUM: 0.9; STAGES: [30, 60, 90]; Augmentation: Random Crop + Horizontal Flip; Imbalanced Data Processing: Weighted Random Sampling; Solver: SGD; GPUs: 4 NVIDIA Geforce RTX 2080 Ti in Parallel
      
    • Training & validation statistics

    • Performance

      LFWCFP_FFCFP_FPAgeDBCALFWCPLFWVggface2_FP
      99.8299.8398.3798.0796.0393.0595.50

Achievement

:confetti_ball:

  • 2017 No.1 on ICCV 2017 MS-Celeb-1M Large-Scale Face Recognition Hard Set/Random Set/Low-Shot Learning Challenges. WeChat News, NUS ECE News, NUS ECE Poster, Award Certificate for Track-1, Award Certificate for Track-2, Award Ceremony.

  • 2017 No.1 on National Institute of Standards and Technology (NIST) IARPA Janus Benchmark A (IJB-A) Unconstrained Face Verification challenge and Identification challenge. WeChat News.

  • State-of-the-art performance on

    • MS-Celeb-1M (Challenge1 Hard Set Coverage@P=0.95: 79.10%; Challenge1 Random Set Coverage@P=0.95: 87.50%; Challenge2 Development Set Coverage@P=0.99: 100.00%; Challenge2 Base Set Top 1 Accuracy: 99.74%; Challenge2 Novel Set Coverage@P=0.99: 99.01%).
    • IJB-A (1:1 Veification TAR@FAR=0.1: 99.6%±0.1%; 1:1 Veification TAR@FAR=0.01: 99.1%±0.2%; 1:1 Veification TAR@FAR=0.001: 97.9%±0.4%; 1:N Identification FNIR@FPIR=0.1: 1.3%±0.3%; 1:N Identification FNIR@FPIR=0.01: 5.4%±4.7%; 1:N Identification Rank1 Accuracy: 99.2%±0.1%; 1:N Identification Rank5 Accuracy: 99.7%±0.1%; 1:N Identification Rank10 Accuracy: 99.8%±0.1%).
    • IJB-C (1:1 Veification TAR@FAR=1e-5: 82.6%).
    • Labeled Faces in the Wild (LFW) (Accuracy: 99.85%±0.217%).
    • Celebrities in Frontal-Profile (CFP) (Frontal-Profile Accuracy: 96.01%±0.84%; Frontal-Profile EER: 4.43%±1.04%; Frontal-Profile AUC: 99.00%±0.35%; Frontal-Frontal Accuracy: 99.64%±0.25%; Frontal-Frontal EER: 0.54%±0.37%; Frontal-Frontal AUC: 99.98%±0.03%).
    • CMU Multi-PIE (Rank1 Accuracy Setting-1 under ±90°: 76.12%; Rank1 Accuracy Setting-2 under ±90°: 86.73%).
    • MORPH Album2 (Rank1 Accuracy Setting-1: 99.65%; Rank1 Accuracy Setting-2: 99.26%).
    • CACD-VS (Accuracy: 99.76%).
    • FG-NET (Rank1 Accuracy: 93.20%).

Acknowledgement

:two_men_holding_hands:


Citation

:bookmark_tabs:

  • Please consult and consider citing the following papers:

    @article{wu20223d,
    title={3D-Guided Frontal Face Generation for Pose-Invariant Recognition},
    author={Wu, Hao and Gu, Jianyang and Fan, Xiaojin and Li, He and Xie, Lidong and Zhao, Jian},
    journal={T-IST},
    year={2022}
    }
    
    
    @article{wang2021face,
    title={Face.evoLVe: A High-Performance Face Recognition Library},
    author={Wang, Qingzhong and Zhang, Pengfei and Xiong, Haoyi and Zhao, Jian},
    journal={arXiv preprint arXiv:2107.08621},
    year={2021}
    }
    
    
    @article{tu2021joint,
    title={Joint Face Image Restoration and Frontalization for Recognition},
    author={Tu, Xiaoguang and Zhao, Jian and Liu, Qiankun and Ai, Wenjie and Guo, Guodong and Li, Zhifeng and Liu, Wei and Feng, Jiashi},
    journal={T-CSVT},
    year={2021}
    }
    
    
    @article{zhao2020towards,
    title={Towards age-invariant face recognition},
    author={Zhao, Jian and Yan, Shuicheng and Feng, Jiashi},
    journal={T-PAMI},
    year={2020}
    }
    
    
    @article{zhao2019recognizing,
    title={Recognizing Profile Faces by Imagining Frontal View},
    author={Zhao, Jian and Xing, Junliang and Xiong, Lin and Yan, Shuicheng and Feng, Jiashi},
    journal={IJCV},
    pages={1--19},
    year={2019}
    }    
    
    
    @inproceedings{zhao2019multi,
    title={Multi-Prototype Networks for Unconstrained Set-based Face Recognition},
    author={Zhao, Jian and Li, Jianshu and Tu, Xiaoguang and Zhao, Fang and Xin, Yuan and Xing, Junliang and Liu, Hengzhu and Yan, Shuicheng and Feng, Jiashi},
    booktitle={IJCAI},
    year={2019}
    }
    
    
    @inproceedings{zhao2019look,
    title={Look Across Elapse: Disentangled Representation Learning and Photorealistic Cross-Age Face Synthesis for Age-Invariant Face Recognition},
    author={Zhao, Jian and Cheng, Yu and Cheng, Yi and Yang, Yang and Lan, Haochong and Zhao, Fang and Xiong, Lin and Xu, Yan and Li, Jianshu and Pranata, Sugiri and others},
    booktitle={AAAI},
    year={2019}
    }
    
    
    @article{zhao20183d,
    title={3D-Aided Dual-Agent GANs for Unconstrained Face Recognition},
    author={Zhao, Jian and Xiong, Lin and Li, Jianshu and Xing, Junliang and Yan, Shuicheng and Feng, Jiashi},
    journal={T-PAMI},
    year={2018}
    }
    
    
    @inproceedings{zhao2018towards,
    title={Towards Pose Invariant Face Recognition in the Wild},
    author={Zhao, Jian and Cheng, Yu and Xu, Yan and Xiong, Lin and Li, Jianshu and Zhao, Fang and Jayashree, Karlekar and Pranata,         Sugiri and Shen, Shengmei and Xing, Junliang and others},
    booktitle={CVPR},
    pages={2207--2216},
    year={2018}
    }
    
    
    @inproceedings{zhao3d,
    title={3D-Aided Deep Pose-Invariant Face Recognition},
    author={Zhao, Jian and Xiong, Lin and Cheng, Yu and Cheng, Yi and Li, Jianshu and Zhou, Li and Xu, Yan and Karlekar, Jayashree and       Pranata, Sugiri and Shen, Shengmei and others},
    booktitle={IJCAI},
    pages={1184--1190},
    year={2018}
    }
    
    
    @inproceedings{zhao2018dynamic,
    title={Dynamic Conditional Networks for Few-Shot Learning},
    author={Zhao, Fang and Zhao, Jian and Yan, Shuicheng and Feng, Jiashi},
    booktitle={ECCV},
    pages={19--35},
    year={2018}
    }
    
    
    @inproceedings{zhao2017dual,
    title={Dual-agent gans for photorealistic and identity preserving profile face synthesis},
    author={Zhao, Jian and Xiong, Lin and Jayashree, Panasonic Karlekar and Li, Jianshu and Zhao, Fang and Wang, Zhecan and Pranata,           Panasonic Sugiri and Shen, Panasonic Shengmei and Yan, Shuicheng and Feng, Jiashi},
    booktitle={NeurIPS},
    pages={66--76},
    year={2017}
    }
    
    
    @inproceedings{zhao122017marginalized,
    title={Marginalized cnn: Learning deep invariant representations},
    author={Zhao12, Jian and Li, Jianshu and Zhao, Fang and Yan13, Shuicheng and Feng, Jiashi},
    booktitle={BMVC},
    year={2017}
    }
    
    
    @inproceedings{cheng2017know,
    title={Know you at one glance: A compact vector representation for low-shot learning},
    author={Cheng, Yu and Zhao, Jian and Wang, Zhecan and Xu, Yan and Jayashree, Karlekar and Shen, Shengmei and Feng, Jiashi},
    booktitle={ICCVW},
    pages={1924--1932},
    year={2017}
    }
    
    
    @inproceedings{wangconditional,
    title={Conditional Dual-Agent GANs for Photorealistic and Annotation Preserving Image Synthesis},
    author={Wang, Zhecan and Zhao, Jian and Cheng, Yu and Xiao, Shengtao and Li, Jianshu and Zhao, Fang and Feng, Jiashi and Kassim, Ashraf},
    booktitle={BMVCW},
    }