├── .gitignore
├── CONTRIBUTING.md
├── License.txt
├── README.md
├── attribute
└── M3DFEL
│ ├── README.md
│ ├── datasets
│ ├── __init__.py
│ ├── dataset_DFEW.py
│ └── video_transform.py
│ ├── figs
│ ├── CVPR_architecture.png
│ └── CVPR_result.png
│ ├── main.py
│ ├── models
│ ├── M3DFEL.py
│ └── __init__.py
│ ├── options.py
│ ├── requirements.txt
│ ├── solver.py
│ └── utils.py
├── generation
└── uiface
│ ├── README.md
│ ├── configs
│ ├── dataset
│ │ └── CASIA_file.yaml
│ ├── diffusion
│ │ └── ddpm.yaml
│ ├── model
│ │ └── unet_cond_ca_cpd25_uncond20.yaml
│ ├── sample_ddim_config.yaml
│ └── train_config.yaml
│ ├── diffusion
│ ├── __init__.py
│ └── ddpm.py
│ ├── main.py
│ ├── models
│ ├── __init__.py
│ ├── autoencoder
│ │ ├── __init__.py
│ │ ├── first_stage_config.yaml
│ │ ├── modules.py
│ │ ├── quantization.py
│ │ └── vqgan.py
│ └── diffusion
│ │ ├── __init__.py
│ │ ├── nn.py
│ │ ├── unet.py
│ │ └── util.py
│ ├── pipeline.png
│ ├── requirements.txt
│ ├── sample.py
│ └── utils
│ ├── CASIA_dataset.py
│ ├── __init__.py
│ ├── checkpoint.py
│ ├── colored.py
│ ├── ema.py
│ └── helpers.py
├── quality
├── README.md
├── backbone
│ ├── __init__.py
│ ├── loss.py
│ └── metric.py
├── demo_imgs
│ ├── 1.jpg
│ ├── 2.jpg
│ └── 3.jpg
├── docs
│ ├── framework.png
│ └── res.png
├── eval.py
├── generate_pseudo_labels
│ ├── extract_embedding
│ │ ├── config_test.py
│ │ ├── dataset
│ │ │ ├── __init__.py
│ │ │ └── dataset_txt.py
│ │ ├── extract_feats.py
│ │ └── model
│ │ │ ├── MobileFaceNet_MS1M.pth
│ │ │ ├── model.py
│ │ │ └── model_mobilefaceNet.py
│ ├── gen_datalist.py
│ └── gen_pseudo_labels.py
├── local_train.sh
├── train.py
└── train_config.py
├── recognition
├── README.md
├── deploy
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── converter
│ │ ├── convert_onnx2tnn.sh
│ │ ├── export_onnx.py
│ │ └── onnx_inference.py
│ ├── core
│ │ ├── face_feature.cpp
│ │ ├── face_feature.h
│ │ ├── timer.h
│ │ └── util.h
│ ├── data
│ │ └── brucelee.jpg
│ ├── scripts
│ │ └── build_linux_x86.sh
│ └── test
│ │ └── test.cpp
├── doc
│ ├── framework.png
│ └── logo.png
├── local_train.sh
├── requirements.txt
├── tasks
│ ├── dctdp
│ │ ├── README.md
│ │ ├── doc
│ │ │ ├── main.png
│ │ │ ├── result1.png
│ │ │ ├── result2.png
│ │ │ └── result3.png
│ │ ├── local_train.sh
│ │ ├── model_irse_dct.py
│ │ ├── requirements.txt
│ │ ├── train.py
│ │ ├── train.yaml
│ │ └── utils.py
│ ├── duetface
│ │ ├── README.md
│ │ ├── base_task.py
│ │ ├── duetface_model.py
│ │ ├── local_backbones.py
│ │ ├── pfld_model.py
│ │ ├── pipeline.png
│ │ ├── requirements.txt
│ │ ├── train.py
│ │ └── train.yaml
│ ├── ekd
│ │ ├── README.md
│ │ ├── dataset.py
│ │ ├── distillation
│ │ │ ├── cc.py
│ │ │ ├── cosine_distance.py
│ │ │ ├── darkrank.py
│ │ │ ├── ekd.py
│ │ │ ├── fitnet.py
│ │ │ ├── rkd.py
│ │ │ ├── similarity_perserving.py
│ │ │ └── simple_kd.py
│ │ ├── doc
│ │ │ ├── method.png
│ │ │ ├── result1.png
│ │ │ ├── result2.png
│ │ │ └── result3.png
│ │ ├── train.py
│ │ └── train.yaml
│ ├── minusface
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── local_train.sh
│ │ ├── minusface.py
│ │ ├── pipeline.png
│ │ ├── requirements.txt
│ │ ├── train.py
│ │ ├── train.yaml
│ │ └── utils.py
│ ├── partialface
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── base_task.py
│ │ ├── local_train.sh
│ │ ├── pipeline.png
│ │ ├── requirements.txt
│ │ ├── train.py
│ │ ├── train.yaml
│ │ └── utils.py
│ └── slerpface
│ │ ├── .gitignore
│ │ ├── encrypt_template.sh
│ │ ├── encrypt_template
│ │ └── enrollment.py
│ │ ├── extract_template.sh
│ │ ├── extract_template
│ │ ├── utils.py
│ │ └── verification.py
│ │ ├── match_template.sh
│ │ ├── match_template
│ │ └── gen_sim.py
│ │ ├── modules
│ │ └── model.py
│ │ ├── pipeline.png
│ │ ├── readme.md
│ │ └── requirements.txt
├── test
│ ├── IJB_Evaluation.py
│ ├── README.md
│ ├── __init__.py
│ ├── datasets.py
│ ├── extract_features.py
│ ├── utils.py
│ ├── verification.py
│ └── verification_rfw.py
├── tools
│ ├── convert_new_index.py
│ ├── decode.py
│ └── img2tfrecord.py
├── torchkit
│ ├── __init__.py
│ ├── backbone
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── fbnets
│ │ │ ├── fbnet_builder.py
│ │ │ ├── fbnet_modeldef.json
│ │ │ └── layers
│ │ │ │ ├── __init__.py
│ │ │ │ ├── batch_norm.py
│ │ │ │ └── misc.py
│ │ ├── model_efficientnet.py
│ │ ├── model_ghostnet.py
│ │ ├── model_irse.py
│ │ ├── model_mobilefacenet.py
│ │ └── model_resnet.py
│ ├── data
│ │ ├── __init__.py
│ │ ├── dataset.py
│ │ ├── example_pb2.py
│ │ ├── feature_pb2.py
│ │ ├── parser.py
│ │ └── sampler.py
│ ├── head
│ │ ├── __init__.py
│ │ ├── distfc
│ │ │ ├── arcface.py
│ │ │ ├── common.py
│ │ │ ├── cosface.py
│ │ │ ├── curricularface.py
│ │ │ ├── normface.py
│ │ │ └── partial_fc.py
│ │ └── localfc
│ │ │ ├── arcface.py
│ │ │ ├── cifp.py
│ │ │ ├── common.py
│ │ │ ├── cosface.py
│ │ │ └── curricularface.py
│ ├── hooks
│ │ ├── __init__.py
│ │ ├── base_hook.py
│ │ ├── checkpoint_hook.py
│ │ ├── learning_rate_hook.py
│ │ ├── log_hook.py
│ │ └── summary_hook.py
│ ├── loss
│ │ ├── __init__.py
│ │ ├── ddl.py
│ │ └── dist_softmax.py
│ ├── task
│ │ ├── __init__.py
│ │ └── base_task.py
│ ├── tfrecord
│ │ └── protos
│ │ │ ├── example.proto
│ │ │ └── feature.proto
│ └── util
│ │ ├── __init__.py
│ │ ├── checkpoint.py
│ │ ├── distributed_functions.py
│ │ └── utils.py
├── train.py
└── train.yaml
└── security
├── README.md
├── common
├── __init__.py
├── data
│ ├── __init__.py
│ ├── base_dataloader.py
│ └── base_transform.py
├── losses
│ ├── __init__.py
│ └── euclidean_loss.py
├── optimizers
│ └── __init__.py
├── schedulers
│ └── __init__.py
├── task
│ ├── __init__.py
│ ├── base_task.py
│ └── fas
│ │ ├── __init__.py
│ │ └── modules.py
└── utils
│ ├── __init__.py
│ ├── cli_utils.py
│ ├── distribute_utils.py
│ ├── face_utils.py
│ ├── logger_utils.py
│ ├── meters.py
│ ├── metrics.py
│ ├── misc.py
│ ├── model_init.py
│ └── parameters.py
├── doc
├── intro.png
└── logo.png
├── requirements.txt
└── tasks
├── Adv-Attack-Defense
├── Adv-Makeup
│ ├── Poisson_Image_Editing
│ │ └── poisson_image_editing_makeup.py
│ ├── README.md
│ ├── configs
│ │ └── config.yaml
│ ├── datasets
│ │ ├── Datasets_Makeup
│ │ │ └── landmark_aligned_600.txt
│ │ └── dataset.py
│ ├── doc
│ │ ├── asr.png
│ │ ├── logo.png
│ │ └── physical_vis_pose.png
│ ├── models
│ │ ├── __init__.py
│ │ ├── makeup_attack.py
│ │ ├── networks.py
│ │ └── utils.py
│ ├── test.py
│ └── train.py
└── Sibling-Attack
│ ├── README.md
│ ├── attack.py
│ ├── configs
│ └── config.yaml
│ ├── datasets
│ ├── FFHQ-select-aligned-label.txt
│ ├── celebaHQ-select-aligned-label.txt
│ └── lfw-select-aligned-label.txt
│ ├── doc
│ ├── gradcam.png
│ ├── main_fig.png
│ ├── table2.png
│ └── table3.png
│ ├── models
│ └── models.py
│ └── utils
│ └── utils.py
├── Face-Anti-Spoofing
├── ANRL
│ ├── README.md
│ ├── configs
│ │ ├── ICM2O.yaml
│ │ ├── IOM2C.yaml
│ │ ├── OCI2M.yaml
│ │ └── OCM2I.yaml
│ ├── datasets
│ │ ├── DG_dataset.py
│ │ ├── __init__.py
│ │ └── factory.py
│ ├── doc
│ │ ├── ROC.png
│ │ ├── framework.png
│ │ ├── results_table.png
│ │ └── tSNE.png
│ ├── models
│ │ ├── __init__.py
│ │ ├── base_block.py
│ │ └── framework.py
│ ├── train.py
│ └── train.sh
└── DCN
│ ├── README.md
│ ├── configs
│ ├── Oulu_1.yaml
│ ├── Oulu_2.yaml
│ ├── Oulu_3_1.yaml
│ └── Oulu_4_1.yaml
│ ├── datasets
│ ├── __init__.py
│ ├── dataset.py
│ ├── factory.py
│ └── permutations_30.npy
│ ├── doc
│ ├── TSNE.png
│ ├── table.png
│ └── table_2.png
│ ├── models
│ ├── __init__.py
│ ├── base_block.py
│ └── framework.py
│ ├── train.py
│ └── train.sh
└── Face-Forgery-Detection
├── DCL
├── README.md
├── configs
│ ├── test.yaml
│ └── train.yaml
├── datasets
│ ├── __init__.py
│ ├── celeb_df.py
│ ├── factory.py
│ ├── ffpp.py
│ ├── utils
│ │ ├── __init__.py
│ │ ├── data_structure.py
│ │ ├── random_patch.py
│ │ └── srm.py
│ └── wild_deepfake.py
├── doc
│ └── main.png
├── models
│ ├── __init__.py
│ ├── base_net.py
│ └── dcl.py
├── requirements.txt
├── test.py
└── train.py
└── STIL
├── README.md
├── configs
└── ffpp.yaml
├── datasets
├── __init__.py
├── factory.py
└── video_dataset.py
├── doc
└── stil.jpg
├── models
├── __init__.py
├── ops.py
└── stil.py
├── test.py
├── test.sh
├── train.py
├── train.sh
└── utils
├── __init__.py
├── lr_utils.py
└── metrics.py
/.gitignore:
--------------------------------------------------------------------------------
1 | */__pycache__
2 | *.pyc
3 | *.log
4 | logs/
5 | .vscode/
6 | .idea/
7 | *.swp
8 | rpf.yaml
9 | *.bak
10 | build/
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Request for contributions
2 |
3 | Please contribute to this repository if any of the following is true:
4 | - You have expertise in community development, communication, or education
5 | - You want open source communities to be more collaborative and inclusive
6 | - You want to help lower the burden to first time contributors
7 |
8 | # How to contribute
9 |
10 | Prerequisites:
11 |
12 | - Familiarity with [pull requests](https://help.github.com/articles/using-pull-requests) and [issues](https://guides.github.com/features/issues/).
13 | - Knowledge of [Markdown](https://help.github.com/articles/markdown-basics/) for editing `.md` documents.
14 |
15 | Please make sure the following conditions are met before raising the issue:
16 |
17 | - It must be a bug or a new function
18 | - It must be a TFace related issue.
19 | - You have been searched in issues, and no similar issue or solution has been found.
--------------------------------------------------------------------------------
/attribute/M3DFEL/README.md:
--------------------------------------------------------------------------------
1 | # M3DFEL
2 | Official code for [Rethinking the Learning Paradigm for Dynamic Facial Expression Recognition](https://openaccess.thecvf.com/content/CVPR2023/papers/Wang_Rethinking_the_Learning_Paradigm_for_Dynamic_Facial_Expression_Recognition_CVPR_2023_paper.pdf) (accepted by CVPR2023).
3 |
4 | We propose a weakly supervised approach to model Dynamic Facial Expression Recognition (DFER) as a Multi-Instance Learning (MIL) problem.
5 |
6 | The Multi-3D Dynamic Facial Expression Learning (M3DFEL) framework provides a unified solution to the weakly supervised problem and model the imbalanced short- and long-term temporal relationships in DFER.
7 |
8 |
9 |
10 | ## Results on DFEW
11 |
12 |
13 |
14 | ## Data Preparation
15 |
16 | Download [DFEW](https://dfew-dataset.github.io/) and unzip the files to make sure:
17 |
18 | ```
19 | .
20 | ├── Clip
21 | │ └── clip_224x224
22 | │ ├── 00001
23 | │ ├── 00002
24 | │ └── ...
25 | └── EmoLabel_DataSplit
26 | ├── test(single-labeled)
27 | └── train(single-labeled)
28 | ```
29 |
30 | ## Installation
31 | ```
32 | pip install -r requirements.txt
33 | ```
34 |
35 |
36 | ## Training
37 | ```
38 | python main.py --data_root path-to-DFEW
39 | ```
40 |
41 | ## Citation
42 | If you find our work useful in your research, please consider citing:
43 | ```
44 | @inproceedings{wang2023rethinking,
45 | title={Rethinking the Learning Paradigm for Dynamic Facial Expression Recognition},
46 | author={Wang, Hanyang and Li, Bo and Wu, Shuang and Shen, Siyuan and Liu, Feng and Ding, Shouhong and Zhou, Aimin},
47 | booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
48 | pages={17958--17968},
49 | year={2023}
50 | }
51 | ```
--------------------------------------------------------------------------------
/attribute/M3DFEL/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | from .dataset_DFEW import DFEWDataset
2 | import torch
3 |
4 |
5 | def create_dataloader(args, mode):
6 | """create dataloader according to args and training/testing mode
7 |
8 | Args:
9 | args
10 | mode: String("train" or "test")
11 |
12 | Returns:
13 | dataloader
14 | """
15 | dataset = DFEWDataset(args, mode)
16 |
17 | dataloader = None
18 |
19 | # return train_dataset or test_dataset according to the mode
20 | if mode == "train":
21 | dataloader = torch.utils.data.DataLoader(dataset,
22 | batch_size=args.batch_size,
23 | shuffle=True,
24 | num_workers=args.workers,
25 | pin_memory=True,
26 | drop_last=True)
27 | elif mode == "test":
28 | dataloader = torch.utils.data.DataLoader(dataset,
29 | batch_size=args.batch_size,
30 | shuffle=False,
31 | num_workers=args.workers,
32 | pin_memory=True)
33 | return dataloader
34 |
--------------------------------------------------------------------------------
/attribute/M3DFEL/figs/CVPR_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/attribute/M3DFEL/figs/CVPR_architecture.png
--------------------------------------------------------------------------------
/attribute/M3DFEL/figs/CVPR_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/attribute/M3DFEL/figs/CVPR_result.png
--------------------------------------------------------------------------------
/attribute/M3DFEL/main.py:
--------------------------------------------------------------------------------
1 | from options import Options
2 | from solver import Solver
3 |
4 |
5 | def main():
6 | """Run the whole training process through solver
7 | Change the options according to your situation, especially workers, gpu_ids, batch_size and epochs
8 | """
9 | args = Options().parse()
10 | solver = Solver(args)
11 | solver.run()
12 |
13 |
14 | if __name__ == '__main__':
15 | main()
16 |
--------------------------------------------------------------------------------
/attribute/M3DFEL/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .M3DFEL import M3DFEL
2 |
3 |
4 | def create_model(args):
5 | """create model according to args
6 |
7 | Args:
8 | args
9 | """
10 | model = M3DFEL(args)
11 |
12 | return model
13 |
--------------------------------------------------------------------------------
/attribute/M3DFEL/requirements.txt:
--------------------------------------------------------------------------------
1 | torch
2 | timm
3 | einops
4 | sklearn
5 | seaborn
6 | matplotlib
7 | numpy
8 | torchvision
9 | pillow
--------------------------------------------------------------------------------
/attribute/M3DFEL/utils.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import torch
3 | from timm.scheduler.cosine_lr import CosineLRScheduler
4 | from einops import rearrange
5 |
6 |
7 | def build_scheduler(args, optimizer, n_iter_per_epoch):
8 | """Build the scheduler
9 | The CosineLRScheduler schedules every iter in every epoch, so it needs n_iter_per_epoch instead of epochs
10 |
11 | Args:
12 | optimizer
13 | n_iter_per_epoch:
14 | """
15 | num_steps = int(args.epochs * n_iter_per_epoch)
16 | warmup_steps = int(args.warmup_epochs * n_iter_per_epoch)
17 |
18 | # use the Cosine LR Scheduler for training
19 | lr_scheduler = CosineLRScheduler(
20 | optimizer,
21 | t_initial=num_steps,
22 | lr_min=args.min_lr,
23 | warmup_lr_init=args.warmup_lr,
24 | warmup_t=warmup_steps,
25 | cycle_limit=1,
26 | t_in_epochs=False,
27 | )
28 |
29 | return lr_scheduler
30 |
31 |
32 | class DMIN(nn.Module):
33 | """The Dynamic Multi Instance Normalization Module
34 | This module dynamicly normlizes the instances and bags
35 |
36 | Args:
37 | optimizer
38 | n_iter_per_epoch:
39 | """
40 |
41 | def __init__(self, num_features, eps=1e-5, momentum=0.9):
42 | super(DMIN, self).__init__()
43 |
44 | # init the DMIN module
45 | self.eps = eps
46 | self.momentum = momentum
47 | self.weight = nn.Parameter(torch.ones(1, num_features, 1))
48 | self.bias = nn.Parameter(torch.zeros(1, num_features, 1))
49 | self.mean_weight = nn.Parameter(torch.ones(2))
50 | self.var_weight = nn.Parameter(torch.ones(2))
51 |
52 | self.weight.data.fill_(1)
53 | self.bias.data.zero_()
54 |
55 | def forward(self, x):
56 |
57 | x = rearrange(x, 'b c n -> b n c')
58 | # calculate the mean and var of input
59 | mean_in = x.mean(-1, keepdim=True)
60 | var_in = x.var(-1, keepdim=True)
61 |
62 | mean_ln = mean_in.mean(1, keepdim=True)
63 | temp = var_in + mean_in ** 2
64 | var_ln = temp.mean(1, keepdim=True) - mean_ln ** 2
65 |
66 | softmax = nn.Softmax(0)
67 | mean_weight = softmax(self.mean_weight)
68 | var_weight = softmax(self.var_weight)
69 |
70 | # caculated the weighted mean and var according to the learned weights
71 | mean = mean_weight[0] * mean_in + mean_weight[1] * mean_ln
72 | var = var_weight[0] * var_in + var_weight[1] * var_ln
73 |
74 | # regulize x according to the calculated results
75 | x = (x-mean) / (var+self.eps).sqrt()
76 | x = x * self.weight + self.bias
77 | x = rearrange(x, 'b n c -> b c n')
78 |
79 | return x
80 |
--------------------------------------------------------------------------------
/generation/uiface/configs/dataset/CASIA_file.yaml:
--------------------------------------------------------------------------------
1 | constants:
2 | input_channels: 3
3 | n_classes: 0
4 | image_size: 128
5 |
6 | latent_diffusion: True
7 |
8 | dataset:
9 | _target_: utils.CASIA_dataset.SamplesWithEmbeddingsFileDataset
10 |
11 | samples_root: " "
12 | embeddings_file_path: "dataset/elastic_embed.npy"
13 | images_name_file_path: "dataset/image_names.pkl"
14 | sample_file_ending: ".jpg"
15 | sample_loader:
16 | _target_: utils.CASIA_dataset.PILImageLoader
17 | sample_transform:
18 | _target_: torchvision.transforms.Compose
19 | transforms:
20 | - _target_: torchvision.transforms.Resize
21 | size:
22 | - 128
23 | - 128
24 | - _target_: torchvision.transforms.ToTensor
25 | - _target_: torchvision.transforms.RandomHorizontalFlip
26 | p: 0.5
27 |
28 |
--------------------------------------------------------------------------------
/generation/uiface/configs/diffusion/ddpm.yaml:
--------------------------------------------------------------------------------
1 | _target_: diffusion.ddpm.DenoisingDiffusionProbabilisticModel
2 | eps_model: ${model}
3 | T: 1000
4 |
5 | schedule_type: "linear" # ['linear', 'cosine', 'cosine_warped]
6 |
7 | schedule_k: 1
8 | schedule_beta_min: 0.000 # if this is too high, noise is added too fast
9 | schedule_beta_max: 0.100 # if this is too high, the cosine schedule does not work
10 |
11 | criterion:
12 | _target_: torch.nn.MSELoss
--------------------------------------------------------------------------------
/generation/uiface/configs/model/unet_cond_ca_cpd25_uncond20.yaml:
--------------------------------------------------------------------------------
1 | # PARAMS: 216341187
2 | _target_: models.diffusion.unet.ConditionalUNet
3 | input_channels: 3
4 | initial_channels: 96
5 | channel_multipliers:
6 | - 1
7 | - 2
8 | - 2
9 | - 2
10 | is_attention:
11 | - false
12 | - true
13 | - true
14 | - true
15 | attention_heads: -1
16 | attention_head_channels: 32
17 | n_blocks_per_resolution: 2
18 |
19 | condition_type: "CA"
20 | is_context_conditional: True
21 | n_context_classes: 0
22 | context_input_channels: 512
23 | context_channels: 256
24 | learn_empty_context: True # add learnable embedding to the model for empty context
25 | context_dropout_probability: 0.25
26 | unconditioned_probability: 0.2
27 |
28 |
--------------------------------------------------------------------------------
/generation/uiface/configs/sample_ddim_config.yaml:
--------------------------------------------------------------------------------
1 | defaults:
2 | - _self_
3 | - override hydra/hydra_logging: disabled
4 | - override hydra/job_logging: disabled
5 | checkpoint:
6 | path: " "
7 | global_step: null
8 | use_non_ema: True
9 |
10 | diffusion_cfg_path: "./configs/model/unet_cond_ca_cpd25_uncond20.yaml"
11 |
12 | VQEncoder_path: ""
13 | VQDecoder_path: ""
14 |
15 | sampling:
16 | gpu_idx: 0
17 | seed: 41
18 | contexts_file : ""
19 | save_dir: " "
20 | save_mode: rgb
21 | batch_size: 50
22 | n_contexts: 10000
23 | num_workers: 1
24 | pin_memory: true
25 | n_samples_per_context: 50
26 | sample_config:
27 | cfg_scale: 1.0
28 | MSE_th: 0.006
29 | step_th: 500
30 |
31 | hydra:
32 | output_subdir: null
33 | run:
34 | dir: .
35 |
--------------------------------------------------------------------------------
/generation/uiface/configs/train_config.yaml:
--------------------------------------------------------------------------------
1 | defaults:
2 | - _self_
3 | - diffusion: ddpm
4 | - model: unet_cond_ca_cpd25_uncond20
5 | - dataset: CASIA_file
6 |
7 | latent_diffusion: true
8 |
9 | constants:
10 | seed: 0
11 | image_size: -1
12 | input_channels: -1
13 | n_classes: 0
14 |
15 | training:
16 | sample_weight_path: null
17 | precision: 16
18 | batch_size: 64
19 | num_workers: 8
20 | pin_memory: true
21 | steps: 900_000
22 | steps_between_sampling: 10000
23 | steps_between_eval: 50000
24 | steps_between_logging: 500
25 | steps_of_checkpoints:
26 | - 100_000
27 | - 250_000
28 | - 400_000
29 | - 600_000
30 | - 800_000
31 | - 900_000
32 | context_dropout: 0
33 | context_permutation: 0.0
34 | ema:
35 | _partial_: true
36 | _target_: utils.ema.EMAModel
37 | inv_gamma: 1.0
38 | power: 0.75
39 | min_value: 0.0
40 | max_value: 0.9999
41 | optimizer:
42 | _partial_: true
43 | _target_: torch.optim.Adam
44 | lr: 1e-4
45 | lr_scheduler:
46 | _partial_: true
47 | _target_: torch.optim.lr_scheduler.CosineAnnealingWarmRestarts
48 | T_0: 10_000
49 | T_mult: 2
50 | checkpoint:
51 | restore: false
52 | path: ""
53 | VQEncoder: ""
54 | VQDecoder: ""
55 |
56 | hydra:
57 | job:
58 | chdir: true
59 | run:
60 | dir: ./logs/${now:%Y-%m-%d}/${now:%H-%M-%S}
--------------------------------------------------------------------------------
/generation/uiface/diffusion/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/diffusion/__init__.py
--------------------------------------------------------------------------------
/generation/uiface/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/models/__init__.py
--------------------------------------------------------------------------------
/generation/uiface/models/autoencoder/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/models/autoencoder/__init__.py
--------------------------------------------------------------------------------
/generation/uiface/models/autoencoder/first_stage_config.yaml:
--------------------------------------------------------------------------------
1 | target: ldm.models.autoencoder.VQModelInterface
2 | params:
3 | embed_dim: 3
4 | n_embed: 8192
5 | ddconfig:
6 | double_z: false
7 | z_channels: 3
8 | resolution: 256
9 | in_channels: 3
10 | out_ch: 3
11 | ch: 128
12 | ch_mult:
13 | - 1
14 | - 2
15 | - 4
16 | num_res_blocks: 2
17 | attn_resolutions: []
18 | dropout: 0.0
19 | lossconfig:
20 | target: torch.nn.Identity
21 |
--------------------------------------------------------------------------------
/generation/uiface/models/autoencoder/vqgan.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from models.autoencoder.modules import Decoder, Encoder
3 | from models.autoencoder.quantization import VectorQuantizer2 as VectorQuantizer
4 | from omegaconf import OmegaConf
5 |
6 |
7 | class VQEncoderInterface(torch.nn.Module):
8 |
9 | def __init__(self, first_stage_config_path: str, encoder_state_dict_path: str):
10 | super().__init__()
11 |
12 | embed_dim = 3
13 |
14 | config = OmegaConf.load(first_stage_config_path)
15 | dd_config = config.params.ddconfig
16 |
17 | self.encoder = Encoder(**dd_config)
18 | self.quant_conv = torch.nn.Conv2d(dd_config["z_channels"], embed_dim, 1)
19 |
20 | state_dict = torch.load(encoder_state_dict_path)
21 | self.load_state_dict(state_dict)
22 |
23 | def forward(self, x):
24 | h = self.encoder(x)
25 | h = self.quant_conv(h)
26 | return h
27 |
28 |
29 | class VQDecoderInterface(torch.nn.Module):
30 |
31 | def __init__(self, first_stage_config_path: str, decoder_state_dict_path: str):
32 | super().__init__()
33 |
34 | embed_dim = 3
35 | n_embed = 8192
36 |
37 | config = OmegaConf.load(first_stage_config_path)
38 | dd_config = config.params.ddconfig
39 |
40 | self.decoder = Decoder(**dd_config)
41 |
42 | self.quantize = VectorQuantizer(n_embed, embed_dim, beta=0.25)
43 | self.post_quant_conv = torch.nn.Conv2d(embed_dim, dd_config["z_channels"], 1)
44 |
45 | state_dict = torch.load(decoder_state_dict_path)
46 | self.load_state_dict(state_dict)
47 |
48 | def forward(self, h, force_not_quantize=False):
49 | # also go through quantization layer
50 | if not force_not_quantize:
51 | quant, emb_loss, info = self.quantize(h)
52 | else:
53 | quant = h
54 | quant = self.post_quant_conv(quant)
55 | dec = self.decoder(quant)
56 | return dec
57 |
--------------------------------------------------------------------------------
/generation/uiface/models/diffusion/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/models/diffusion/__init__.py
--------------------------------------------------------------------------------
/generation/uiface/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/pipeline.png
--------------------------------------------------------------------------------
/generation/uiface/requirements.txt:
--------------------------------------------------------------------------------
1 | einops==0.6.1
2 | hydra-core
3 | matplotlib==3.7.2
4 | numpy==1.24.4
5 | omegaconf==2.3.0
6 | pandas==2.0.3
7 | pyeer==0.5.5
8 | pytorch_lightning==1.5.9
9 | Requests==2.31.0
10 | scikit_learn==1.3.0
11 | scipy
12 | seaborn==0.12.2
13 | scikit-image==0.21.0
14 | tqdm==4.66.1
15 |
--------------------------------------------------------------------------------
/generation/uiface/utils/CASIA_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pickle
3 | from typing import Callable
4 |
5 | import numpy as np
6 | import torch
7 | from PIL import Image
8 | from torchvision.datasets import DatasetFolder
9 |
10 |
11 | class PILImageLoader:
12 |
13 | def __call__(self, path):
14 | with open(path, "rb") as f:
15 | img = Image.open(f)
16 | return img.convert("RGB")
17 |
18 |
19 | class SamplesWithEmbeddingsFileDataset(torch.utils.data.Dataset):
20 |
21 | def __init__(
22 | self,
23 | samples_root: str,
24 | embeddings_file_path: str,
25 | images_name_file_path: str,
26 | sample_file_ending: str = ".jpg",
27 | sample_loader: Callable = None,
28 | sample_transform: Callable = None,
29 | ):
30 | super(SamplesWithEmbeddingsFileDataset, self).__init__()
31 |
32 | self.sample_loader = sample_loader
33 | self.transform = sample_transform
34 | self.embeddings_file_path = embeddings_file_path
35 | self.samples_root = samples_root
36 | self.samples = self.build_samples(
37 | embeddings_file_path=embeddings_file_path,
38 | images_name_file_path=images_name_file_path,
39 | sample_root=samples_root,
40 | sample_file_ending=sample_file_ending,
41 | )
42 | print("Total images:")
43 | print(len(self.samples))
44 |
45 | @staticmethod
46 | def build_samples(
47 | embeddings_file_path: str,
48 | images_name_file_path: str,
49 | sample_root: str,
50 | sample_file_ending: str,
51 | ):
52 | # normed templates: (n, 512)
53 | content = np.load(embeddings_file_path)
54 | print("CASIA contexts file loaded")
55 |
56 | with open(images_name_file_path, "rb") as f:
57 | image_names = pickle.load(f)
58 | print("CASIA file names file loaded")
59 |
60 | samples = []
61 | total_images = len(image_names)
62 | for index in range(total_images):
63 | embed = content[index]
64 | image_path = os.path.join(sample_root, image_names[index])
65 | samples.append((image_path, embed))
66 | return samples
67 |
68 | def __getitem__(self, index: int):
69 | image_path, embedding_npy = self.samples[index]
70 | image = self.sample_loader(image_path)
71 | embedding = torch.from_numpy(embedding_npy)
72 | if self.transform is not None:
73 | image = self.transform(image)
74 | return image, embedding
75 |
76 | def __len__(self):
77 | return len(self.samples)
78 |
--------------------------------------------------------------------------------
/generation/uiface/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/generation/uiface/utils/__init__.py
--------------------------------------------------------------------------------
/generation/uiface/utils/checkpoint.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 |
4 | def checkpoint(func, inputs, params, flag):
5 | """
6 | Evaluate a function without caching intermediate activations, allowing for
7 | reduced memory at the expense of extra compute in the backward pass.
8 | :param func: the function to evaluate.
9 | :param inputs: the argument sequence to pass to `func`.
10 | :param params: a sequence of parameters `func` depends on but does not
11 | explicitly take as arguments.
12 | :param flag: if False, disable gradient checkpointing.
13 | """
14 | if flag:
15 | args = tuple(inputs) + tuple(params)
16 | return CheckpointFunction.apply(func, len(inputs), *args)
17 | else:
18 | return func(*inputs)
19 |
20 |
21 | class CheckpointFunction(torch.autograd.Function):
22 | @staticmethod
23 | def forward(ctx, run_function, length, *args):
24 | ctx.run_function = run_function
25 | ctx.input_tensors = list(args[:length])
26 | ctx.input_params = list(args[length:])
27 |
28 | with torch.no_grad():
29 | output_tensors = ctx.run_function(*ctx.input_tensors)
30 | return output_tensors
31 |
32 | @staticmethod
33 | def backward(ctx, *output_grads):
34 | ctx.input_tensors = [x.detach().requires_grad_(True) for x in ctx.input_tensors]
35 | with torch.enable_grad():
36 | shallow_copies = [x.view_as(x) for x in ctx.input_tensors]
37 | output_tensors = ctx.run_function(*shallow_copies)
38 | input_grads = torch.autograd.grad(
39 | output_tensors,
40 | ctx.input_tensors + ctx.input_params,
41 | output_grads,
42 | allow_unused=True,
43 | )
44 | del ctx.input_tensors
45 | del ctx.input_params
46 | del output_tensors
47 | return (None, None) + input_grads
48 |
--------------------------------------------------------------------------------
/generation/uiface/utils/colored.py:
--------------------------------------------------------------------------------
1 | # https://github.com/hfeeki/termcolor/blob/master/termcolor.py
2 |
3 | import os
4 | import re
5 |
6 | ATTRIBUTES = dict(
7 | list(
8 | zip(
9 | ["bold", "dark", "", "underline", "blink", "", "reverse", "concealed"],
10 | list(range(1, 9)),
11 | )
12 | )
13 | )
14 | del ATTRIBUTES[""]
15 |
16 | ATTRIBUTES_RE = "\033\\[(?:%s)m" % "|".join(["%d" % v for v in ATTRIBUTES.values()])
17 |
18 | HIGHLIGHTS = dict(
19 | list(
20 | zip(
21 | [
22 | "on_grey",
23 | "on_red",
24 | "on_green",
25 | "on_yellow",
26 | "on_blue",
27 | "on_magenta",
28 | "on_cyan",
29 | "on_white",
30 | ],
31 | list(range(40, 48)),
32 | )
33 | )
34 | )
35 |
36 | HIGHLIGHTS_RE = "\033\\[(?:%s)m" % "|".join(["%d" % v for v in HIGHLIGHTS.values()])
37 |
38 | COLORS = dict(
39 | list(
40 | zip(
41 | [
42 | "grey",
43 | "red",
44 | "green",
45 | "yellow",
46 | "blue",
47 | "magenta",
48 | "cyan",
49 | "white",
50 | ],
51 | list(range(30, 38)),
52 | )
53 | )
54 | )
55 |
56 | COLORS_RE = "\033\\[(?:%s)m" % "|".join(["%d" % v for v in COLORS.values()])
57 |
58 | RESET = "\033[0m"
59 | RESET_RE = "\033\\[0m"
60 |
61 |
62 | def colored(text, color=None, on_color=None, attrs=None):
63 | if os.getenv("ANSI_COLORS_DISABLED") is None:
64 | fmt_str = "\033[%dm%s"
65 | if color is not None:
66 | text = re.sub(COLORS_RE + "(.*?)" + RESET_RE, r"\1", text)
67 | text = fmt_str % (COLORS[color], text)
68 | if on_color is not None:
69 | text = re.sub(HIGHLIGHTS_RE + "(.*?)" + RESET_RE, r"\1", text)
70 | text = fmt_str % (HIGHLIGHTS[on_color], text)
71 | if attrs is not None:
72 | text = re.sub(ATTRIBUTES_RE + "(.*?)" + RESET_RE, r"\1", text)
73 | for attr in attrs:
74 | text = fmt_str % (ATTRIBUTES[attr], text)
75 | return text + RESET
76 | else:
77 | return text
78 |
--------------------------------------------------------------------------------
/quality/README.md:
--------------------------------------------------------------------------------
1 | # SDD-FIQA: Unsupervised Face Image Quality Assessment with Similarity Distribution Distance
2 |
3 | ## Introduction
4 | ####
5 | In recent years, Face Image Quality Assessment (FIQA) has become an indispensable part of the face recognition system to guarantee the stability and reliability of recognition performance in an unconstrained scenario. In this work, we argue that a high-quality face image should be similar to its intra-class samples and dissimilar to its inter-class samples. Thus, we propose a novel unsupervised FIQA method that incorporates Similarity Distribution Distance for Face Image Quality Assessment (SDD-FIQA). Our method generates quality pseudo-labels by calculating the Wasserstein Distance (WD) between the intra-class and inter-class similarity distributions. With these quality pseudo-labels, we are capable of training a regression network for quality prediction. Extensive experiments on benchmark datasets demonstrate that the proposed SDD-FIQA surpasses the state-of-the-arts by an impressive margin. Meanwhile, our method shows good generalization across different recognition systems.
6 |
7 |
8 |
9 | ## Generation of Quality Pseudo-Labels
10 | 2. Run './generate_pseudo_labels/gen_datalist.py' to obtain the data list.
11 | 3. Run './generate_pseudo_labels/extract_embedding/extract_feats.py' to extract the embeddings of face image.
12 | 4. Run './generate_pseudo_labels/gen_pseudo_labels.py' to calculate the quality pseudo-labels.
13 |
14 |
15 | ## Training of Quality Regression Model
16 | 1. Replace the data path with your local path on `train_confing.py`.
17 | 2. Run local_train.sh.
18 |
19 | ## Prediction of FIQA
20 | Run './eval.py' to predict face quality score.
21 | We provide the pre-trained model on the refined MS1M dataset with IR50: [googledrive](https://drive.google.com/file/d/1AM0iWVfSVWRjCriwZZ3FXiUGbcDzkF25/view?usp=sharing)
22 |
23 | ## Results
24 |
25 |
26 | ## Citing this Repository
27 | If you find this code useful in your research, please consider citing us:
28 | ```
29 | @InProceedings{SDD-FIQA2021,
30 | author={Ou, Fu-Zhao and Chen, Xingyu and Zhang, Ruixin and Huang, Yuge and Li, Shaoxin and Li, Jilin and Li, Yong and Cao, Liujuan and Wang, Yuan-Gen},
31 | title = {{SDD-FIQA}: Unsupervised Face Image Quality Assessment with Similarity Distribution Distance},
32 | booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},
33 | year = {2021},
34 | }
35 |
36 | ```
37 |
--------------------------------------------------------------------------------
/quality/backbone/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/backbone/__init__.py
--------------------------------------------------------------------------------
/quality/backbone/loss.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 |
4 |
5 | # focal loss for training recognition model
6 | class FocalLoss(nn.Module):
7 | '''
8 | focal loss for training recognition model
9 | '''
10 | def __init__(self, gamma=0, eps=1e-7):
11 | super(FocalLoss, self).__init__()
12 | self.gamma = gamma
13 | self.eps = eps
14 | self.ce = torch.nn.CrossEntropyLoss()
15 | def forward(self, input, target):
16 | logp = self.ce(input, target)
17 | p = torch.exp(-logp)
18 | loss = (1 - p) ** self.gamma * logp
19 | return loss.mean()
20 |
--------------------------------------------------------------------------------
/quality/demo_imgs/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/demo_imgs/1.jpg
--------------------------------------------------------------------------------
/quality/demo_imgs/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/demo_imgs/2.jpg
--------------------------------------------------------------------------------
/quality/demo_imgs/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/demo_imgs/3.jpg
--------------------------------------------------------------------------------
/quality/docs/framework.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/docs/framework.png
--------------------------------------------------------------------------------
/quality/docs/res.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/docs/res.png
--------------------------------------------------------------------------------
/quality/eval.py:
--------------------------------------------------------------------------------
1 | import os
2 | import os.path as osp
3 | import torch
4 | import torch.nn as nn
5 | import torch.optim as opti
6 | from tqdm import tqdm
7 | import torchvision.transforms as T
8 | from generate_pseudo_labels.extract_embedding.model import model
9 | import numpy as np
10 | from scipy import stats
11 | import pdb
12 | from PIL import Image
13 |
14 |
15 | def read_img(imgPath): # read image & data pre-process
16 | data = torch.randn(1, 3, 112, 112)
17 | transform = T.Compose([
18 | T.Resize((112, 112)),
19 | T.ToTensor(),
20 | T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
21 | ])
22 | img = Image.open(imgPath).convert("RGB")
23 | data[0, :, :, :] = transform(img)
24 | return data
25 |
26 |
27 | def network(eval_model, device):
28 | net = model.R50([112, 112], use_type="Qua").to(device)
29 | net_dict = net.state_dict()
30 | data_dict = {
31 | key.replace('module.', ''): value for key, value in torch.load(eval_model, map_location=device).items()}
32 | net_dict.update(data_dict)
33 | net.load_state_dict(net_dict)
34 | net.eval()
35 | return net
36 |
37 | if __name__ == "__main__":
38 | imgpath = './demo_imgs/1.jpg' # [1,2,3.jpg]
39 | device = 'cpu' # 'cpu' or 'cuda:x'
40 | eval_model = './model/SDD_FIQA_checkpoints_r50.pth' # checkpoint
41 | net = network(eval_model, device)
42 | input_data = read_img(imgpath)
43 | pred_score = net(input_data).data.cpu().numpy().squeeze()
44 | print(f"Quality score = {pred_score}")
45 |
--------------------------------------------------------------------------------
/quality/generate_pseudo_labels/extract_embedding/config_test.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torchvision.transforms as T
3 |
4 |
5 | class Config:
6 | # dataset
7 | data_root = ''
8 | img_list = '../DATA.labelpath'
9 | eval_model = './model/MobileFaceNet_MS1M.pth'
10 | outfile = '../feats_npy/Embedding_Features.npy'
11 | # data preprocess
12 | transform = T.Compose([
13 | T.Resize((112, 112)),
14 | T.ToTensor(),
15 | T.Normalize(mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5]),
16 | ])
17 | # network settings
18 | backbone = 'MFN' # [MFN, R_50]
19 | device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
20 | multi_GPUs = [0,1,2,3,4,5,6]
21 | embedding_size = 512
22 | batch_size = 2000
23 | pin_memory = True
24 | num_workers = 4
25 | config = Config()
26 |
--------------------------------------------------------------------------------
/quality/generate_pseudo_labels/extract_embedding/dataset/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/generate_pseudo_labels/extract_embedding/dataset/__init__.py
--------------------------------------------------------------------------------
/quality/generate_pseudo_labels/extract_embedding/model/MobileFaceNet_MS1M.pth:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/quality/generate_pseudo_labels/extract_embedding/model/MobileFaceNet_MS1M.pth
--------------------------------------------------------------------------------
/quality/generate_pseudo_labels/gen_datalist.py:
--------------------------------------------------------------------------------
1 | from torch.utils.data import DataLoader
2 | from torchvision.datasets import ImageFolder
3 | from tqdm import tqdm
4 |
5 |
6 | def gentxt(data_root, outfile): # generate data file via traveling the target dataset
7 | '''
8 | Use ImageFolder method to travel the target dataset
9 | Save to two files including ".label" and ".labelpath"
10 | '''
11 | # output file1
12 | outfile1 = open(outfile, 'w')
13 | # output file2
14 | outfile2 = open(outfile+'path', 'w')
15 | data = ImageFolder(data_root)
16 | count = 0
17 | tqdm(data.imgs)
18 | # travel the target dataset
19 | for index, value in enumerate(data.imgs):
20 | count += 1
21 | img = '/'+'/'.join(value[0].split('/')[-2:])
22 | outfile1.write(img + '\n')
23 | outfile2.write(value[0] + '\t' + str(value[1]) + '\n')
24 | return count
25 |
26 |
27 | if __name__ == "__main__": # obtain data list
28 | '''
29 | This method is to obtain data list from dataset
30 | and save to txt files
31 | '''
32 | outfile = './DATA.label'
33 | data_root = '/data/home/fuzhaoou/Dataset/MS1M/MS1M_arcface_112'
34 | print(gentxt(data_root, outfile))
35 |
--------------------------------------------------------------------------------
/quality/local_train.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -d "logs" ]; then
4 | mkdir logs
5 | fi
6 | export CUDA_VISIBLE_DEVICES='0,1,2,3,4,5,6,7'
7 | nohup python -u train.py > logs/$(date +%F-%H-%M-%S).log 2>&1 &
8 |
--------------------------------------------------------------------------------
/quality/train_config.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torchvision.transforms as T
3 |
4 |
5 | class Config:
6 | # dataset
7 | img_list = './generate_pseudo_labels/labels/pseudo_quality_score.txt'
8 | finetuning_model = './generate_pseudo_labels/extract_embedding/model/MobileFaceNet_MS1M.pth'
9 | # save settings
10 | checkpoints = "./checkpoints/MS1M_Quality_Regression/S1"
11 | checkpoints_name = "MFN"
12 | # data preprocess
13 | transform = T.Compose([
14 | T.RandomHorizontalFlip(),
15 | T.ToTensor(),
16 | T.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
17 | ])
18 | # training settings
19 | device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
20 | seed = 0
21 | multi_GPUs = [0,1,2,3,4,5,6,7]
22 | backbone = 'MFN' # [MFN, R_50]
23 | pin_memory = True
24 | num_workers = 12
25 | batch_size = 5000
26 | epoch = 20
27 | lr = 0.0001
28 | stepLR = [5, 10]
29 | weight_decay = 0.0005
30 | display = 100
31 | saveModel_epoch = 1
32 | loss = 'SmoothL1' # ['L1', 'L2', 'SmoothL1']
33 |
34 | config = Config()
35 |
--------------------------------------------------------------------------------
/recognition/deploy/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1)
2 | project(facefeature-tnn CXX)
3 |
4 | set(CMAKE_BUILD_TYPE Release)
5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fPIC -std=c++11")
6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -fPIC -pthread")
7 |
8 | option(TNN_OPENVINO_ENABLE "with openvino lib" ON)
9 | option(LOG_ENABLE "with log print" OFF)
10 |
11 | message(${TNN_LIB_DIR})
12 | message(${TNN_INCLUDE_DIR})
13 | message(${OpenCV_DIR})
14 | message(${GFLAGS_DIR})
15 |
16 | if (${TNN_OPENVINO_ENABLE})
17 | add_definitions(-D_OPENVINO_)
18 | endif()
19 |
20 | if (${LOG_ENABLE})
21 | add_definitions(-D_LOG_)
22 | endif()
23 |
24 | find_package(OpenCV 3 REQUIRED)
25 | include_directories(${OpenCV_INCLUDE_DIRS})
26 |
27 | include_directories(${TNN_INCLUDE_DIR})
28 | link_directories(${TNN_LIB_DIR})
29 | link_libraries(TNN)
30 |
31 | add_subdirectory(${GFLAGS_DIR} ${GFLAGS_DIR})
32 | get_target_property(GFLAGS_INCLUDE_DIRS gflags INTERFACE_INCLUDE_DIRECTORIES)
33 | include_directories(BEFORE "${GFLAGS_INCLUDE_DIRS}")
34 | link_libraries(gflags)
35 |
36 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/core/)
37 | file(GLOB_RECURSE
38 | ALL_SOURCE_FILES
39 | ${CMAKE_CURRENT_SOURCE_DIR}/core/**.cpp
40 | )
41 |
42 | add_library(FaceFeature SHARED ${ALL_SOURCE_FILES})
43 | add_executable(test test/test.cpp)
44 | target_link_libraries(test FaceFeature ${OpenCV_LIBS})
45 |
46 |
--------------------------------------------------------------------------------
/recognition/deploy/README.md:
--------------------------------------------------------------------------------
1 | ## Deploy
2 |
3 | ### Introduction
4 | After finishing the train, we deploy the model based on [TNN](https://github.com/Tencent/TNN) framework. TNN is a high-performance, lightweight neural network inference framework open sourced by Tencent Youtu Lab, and supports for ARM CPU/X86 CPU/NV GPU/ devcies.
5 |
6 | ### Preparation
7 |
8 | Download TNN source and compile core library and model-converter tools, please refer to the official guides, [Compile converter](https://github.com/Tencent/TNN/blob/master/doc/en/user/convert_en.md), [Compile TNN](https://github.com/Tencent/TNN/blob/master/doc/en/user/compile_en.md)
9 |
10 | ### Convert
11 | 1. Convert PyTorch checkpoint to ONNX model
12 | ```
13 | python3 converter/export_onnx.py -h
14 | ```
15 | The output shows
16 | ``` bash
17 | usage: export_onnx.py [-h] --ckpt_path CKPT_PATH --onnx_name ONNX_NAME
18 | --model_name MODEL_NAME
19 |
20 | export pytorch model to onnx
21 |
22 | optional arguments:
23 | -h, --help show this help message and exit
24 | --ckpt_path CKPT_PATH
25 | --onnx_name ONNX_NAME
26 | --model_name MODEL_NAME
27 | ```
28 |
29 | 2. Convert ONNX model to TNN model
30 |
31 | ``` bash
32 | ./converter/convert_onnx2tnn.sh
33 | ```
34 |
35 | ### Compile
36 |
37 | 1. Linux x86
38 |
39 | Please compile TNN by using `build_linux_openvino.sh`. We suggest using the OpenVINO backend in x86 cpus for high performance and TNN integrates the OpenVINO interfaces.
40 | Run below scripts to compile and test deploy codes.
41 | ```
42 | ./scripts/build_linux_x86.sh
43 |
44 | ./build/test -h
45 | -h
46 | -img
47 | -proto
48 | -model
49 |
50 | ```
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/recognition/deploy/converter/convert_onnx2tnn.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ $# != 2 ]; then
4 | echo "$0 "
5 | exit 0
6 | fi
7 |
8 | if [ -z $TNN_ROOT ]; then
9 | echo "Please set env [TNN_ROOT]";
10 | exit -1;
11 | fi
12 |
13 | tnn_converter_dir=${TNN_ROOT}tools/convert2tnn/
14 | onnx_path=$(readlink -f "$1")
15 | echo $onnx_path
16 |
17 | output_dir=$(readlink -f "$2")
18 | echo $output_dir
19 | cd $tnn_converter_dir
20 | python converter.py onnx2tnn $onnx_path -in input:1,3,112,112 -o $output_dir
21 |
--------------------------------------------------------------------------------
/recognition/deploy/converter/export_onnx.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import argparse
4 | import torch.onnx
5 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..'))
6 | from torchkit.backbone import get_model
7 | from torchkit.util.utils import l2_norm
8 |
9 |
10 | parser = argparse.ArgumentParser(description='export pytorch model to onnx')
11 | parser.add_argument('--ckpt_path', default=None, type=str, required=True, help='')
12 | parser.add_argument('--onnx_name', default=None, type=str, required=True, help='')
13 | parser.add_argument('--model_name', default=None, type=str, required=True, help='')
14 | args = parser.parse_args()
15 |
16 |
17 | def main():
18 | net = get_model(args.model_name)
19 | input_size = [112, 112]
20 | torch_model = net(input_size)
21 | if not os.path.isfile(args.ckpt_path):
22 | print("Invalid ckpt path: %s" % args.ckpt_path)
23 | return
24 | torch_model.load_state_dict(torch.load(args.ckpt_path))
25 | torch_model.eval()
26 |
27 | batch_size = 1
28 | x = torch.randn(batch_size, 3, input_size[0], input_size[1], requires_grad=True)
29 | torch_out = torch_model(x)
30 | torch_out = l2_norm(torch_out)
31 | torch.onnx.export(torch_model,
32 | x,
33 | "%s.onnx" % args.onnx_name,
34 | export_params=True,
35 | opset_version=10,
36 | do_constant_folding=True,
37 | input_names = ['input'],
38 | output_names = ['output'])
39 | print("finished")
40 |
41 |
42 | if __name__ == '__main__':
43 | main()
44 |
--------------------------------------------------------------------------------
/recognition/deploy/converter/onnx_inference.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import cv2
3 | import numpy as np
4 | import onnxruntime
5 | import torchvision.transforms as transforms
6 |
7 |
8 | parser = argparse.ArgumentParser(description='onnx inference')
9 | parser.add_argument('--onnx_path', default=None, type=str, required=True, help='')
10 | args = parser.parse_args()
11 |
12 |
13 | def l2_norm(x):
14 | """ l2 normalize
15 | """
16 | output = x / np.linalg.norm(x)
17 | return output
18 |
19 |
20 | def to_numpy(tensor):
21 | return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()
22 |
23 |
24 | def get_test_transform():
25 | test_transform = transforms.Compose([
26 | transforms.ToTensor(),
27 | transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])
28 | return test_transform
29 |
30 |
31 | def main():
32 | img = cv2.imread("brucelee.jpg")
33 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
34 | img = get_test_transform()(img)
35 | img = img.unsqueeze_(0)
36 | print(img.shape)
37 | onnx_path = args.onnx_path
38 | session = onnxruntime.InferenceSession(onnx_path)
39 | inputs = {session.get_inputs()[0].name: to_numpy(img)}
40 | outs = session.run(None, inputs)[0]
41 | print(outs.shape)
42 | outs = l2_norm(outs).squeeze()
43 | ss = ""
44 | for x in outs:
45 | ss = ss + "%.6f " % x
46 | print(ss)
47 |
48 |
49 | if __name__ == '__main__':
50 | main()
51 |
--------------------------------------------------------------------------------
/recognition/deploy/core/face_feature.h:
--------------------------------------------------------------------------------
1 | #ifndef _FACE_FEATURE_H_
2 | #define _FACE_FEATURE_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | class TNNInstance{
15 | public:
16 | TNNInstance() {}
17 | ~TNNInstance() {}
18 | TNN_NS::Status Init(const std::string&, const std::string& model);
19 | TNN_NS::Status Forward(TNN_NS::Mat& input_mat, std::vector& feature);
20 | TNN_NS::DimsVector GetInputDims(std::string layer_name = "");
21 | TNN_NS::DimsVector GetOutputDims(std::string layer_name = "");
22 |
23 | private:
24 | std::shared_ptr instance_ = nullptr;
25 | std::map input_dims_;
26 | std::map output_dims_;
27 | std::map> output_mats_;
28 | };
29 |
30 | #endif // _FACE_FEATURE_H_
--------------------------------------------------------------------------------
/recognition/deploy/core/timer.h:
--------------------------------------------------------------------------------
1 | #ifndef _TIMER_H_
2 | #define _TIMER_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | using std::chrono::time_point;
11 | using std::chrono::system_clock;
12 | using std::chrono::duration_cast;
13 | using std::chrono::microseconds;
14 |
15 | class Timer {
16 | public:
17 | explicit Timer(std::string timer_info) {
18 | timer_info_ = timer_info;
19 | Reset();
20 | }
21 | void Start() {
22 | start_ = system_clock::now();
23 | }
24 | void Stop() {
25 | stop_ = system_clock::now();
26 | float delta = duration_cast(stop_ - start_).count() / 1000.0f;
27 | min_ = static_cast(fmin(min_, delta));
28 | max_ = static_cast(fmax(max_, delta));
29 | sum_ += delta;
30 | count_++;
31 | }
32 | void Reset() {
33 | min_ = FLT_MAX;
34 | max_ = FLT_MIN;
35 | sum_ = 0.0f;
36 | count_ = 0;
37 | stop_ = start_ = system_clock::now();
38 | }
39 | void Print() {
40 | char min_str[16];
41 | snprintf(min_str, 16, "%6.3f", min_);
42 | char max_str[16];
43 | snprintf(max_str, 16, "%6.3f", max_);
44 | char avg_str[16];
45 | snprintf(avg_str, 16, "%6.3f", sum_ / static_cast(count_));
46 | printf("%-15s cost: min = %-8s ms | max = %-8s ms | avg = %-8s ms \n", timer_info_.c_str(),
47 | min_str, max_str, avg_str);
48 | }
49 |
50 | private:
51 | float min_;
52 | float max_;
53 | float sum_;
54 | std::string timer_info_;
55 | time_point start_;
56 | time_point stop_;
57 | int count_;
58 | };
59 |
60 | #endif // _TIMER_H_
--------------------------------------------------------------------------------
/recognition/deploy/core/util.h:
--------------------------------------------------------------------------------
1 | #ifndef _UTIL_H_
2 | #define _UTIL_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | inline bool CheckResult(std::string desc, TNN_NS::Status result) {
10 | if (result != 0) {
11 | #ifdef _LOG_
12 | fprintf(stderr, "[%s] failed: %s \n", desc.c_str(), result.description().c_str());
13 | #endif
14 | return false;
15 | } else {
16 | #ifdef _LOG_
17 | fprintf(stdout, "[%s] success! \n", desc.c_str());
18 | #endif
19 | return true;
20 | }
21 | }
22 |
23 | inline float norm(std::vector &fin) {
24 | float sum = 0.f;
25 | for (float v : fin) {
26 | sum += v * v;
27 | }
28 | return sqrt(sum);
29 | }
30 |
31 | inline std::vector l2_normlize(std::vector & fin) {
32 | float div = norm(fin);
33 | std::vector vout;
34 | for (auto it = fin.begin(); it != fin.end(); it++) {
35 | float value = (*it) / div;
36 | vout.push_back(value);
37 | }
38 | return vout;
39 | }
40 |
41 | inline int ReadFile(const std::string& file_path, std::string &data) {
42 | std::ifstream file(file_path, std::ios::binary);
43 | if (!file) {
44 | fprintf(stderr, ("file %s not exist\n", file_path.c_str()));
45 | return -1;
46 | }
47 |
48 | file.seekg(0, std::ios::end);
49 | std::streampos fileSize = file.tellg();
50 | file.seekg(0, std::ios::beg);
51 | if (fileSize <= 0) {
52 | fprintf(stderr, ("file %s is empty\n", file_path.c_str()));
53 | return -2;
54 | }
55 |
56 | data = std::string(std::istreambuf_iterator(file), std::istreambuf_iterator());
57 | return 0;
58 | }
59 |
60 | #endif
--------------------------------------------------------------------------------
/recognition/deploy/data/brucelee.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/deploy/data/brucelee.jpg
--------------------------------------------------------------------------------
/recognition/deploy/scripts/build_linux_x86.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ -z $TNN_ROOT ]; then
4 | echo "Please set env [TNN_ROOT]";
5 | exit -1;
6 | fi
7 |
8 | if [ -z $OpenCV_DIR ]; then
9 | echo "Please set env [OpenCV_DIR]";
10 | exit -1;
11 | fi
12 |
13 | TNN_LIB_DIR=${TNN_ROOT}/scripts/x86_linux_release/lib/
14 | TNN_INCLUDE_DIR=${TNN_ROOT}/scripts/x86_linux_release/include/
15 | GFLAGS_DIR=${TNN_ROOT}/third_party/gflags/
16 | OpenCV_DIR=${OpenCV_DIR}
17 |
18 |
19 | if [ ! -d ${TNN_LIB_PATH} ]; then
20 | echo "TNN not build success, please build TNN first";
21 | exit -2;
22 | fi
23 |
24 | rm -rf ./build
25 | mkdir build
26 | cd build
27 | cmake3 .. \
28 | -DTNN_LIB_DIR=${TNN_LIB_DIR} \
29 | -DTNN_INCLUDE_DIR=${TNN_INCLUDE_DIR} \
30 | -DOpenCV_DIR=${OpenCV_DIR} \
31 | -DGFLAGS_DIR=${GFLAGS_DIR} \
32 | -DTNN_OPENVINO_ENABLE=ON \
33 | -DLOG_ENABLE=OFF
34 | make
35 |
--------------------------------------------------------------------------------
/recognition/deploy/test/test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "gflags/gflags.h"
8 | #include "face_feature.h"
9 | #include "util.h"
10 | #include "timer.h"
11 |
12 | using namespace std;
13 | using namespace cv;
14 |
15 | DEFINE_string(img, "./data/brucelee.jpg", "input img");
16 | DEFINE_string(proto, "", "tnn proto path");
17 | DEFINE_string(model, "", "tnn model path");
18 | DEFINE_bool(h, false, "help");
19 |
20 | void ShowUsage() {
21 | printf(" -h \n");
22 | printf(" -img \n");
23 | printf(" -proto \n");
24 | printf(" -model \n");
25 | }
26 |
27 | bool ParseAndCheckCommandLine(int argc, char* argv[]) {
28 | gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);
29 | if (FLAGS_h) {
30 | ShowUsage();
31 | return false;
32 | }
33 | if (FLAGS_proto.empty()) {
34 | printf("tnn proto path is not set\n");
35 | ShowUsage();
36 | return false;
37 | }
38 |
39 | if (FLAGS_model.empty()) {
40 | printf("tnn model path is not set\n");
41 | ShowUsage();
42 | return false;
43 | }
44 | return true;
45 | }
46 |
47 | int main(int argc, char* argv[]) {
48 | if (!ParseAndCheckCommandLine(argc, argv))
49 | return -1;
50 |
51 | Mat img = imread(FLAGS_img);
52 | cvtColor(img, img, cv::COLOR_BGR2RGB);
53 | printf("image width: %d, height: %d\n", img.cols, img.rows);
54 | string proto_path = FLAGS_proto;
55 | string model_path = FLAGS_model;
56 |
57 | string proto, model;
58 | int ret = 0;
59 | ret = ReadFile(proto_path, proto);
60 | if (ret) return ret;
61 | ret = ReadFile(model_path, model);
62 | if (ret) return ret;
63 |
64 | TNNInstance* instance = new TNNInstance();
65 | TNN_NS::Status status;
66 | status = instance->Init(proto, model);
67 | if (status != 0) {
68 | return status;
69 | }
70 | TNN_NS::DimsVector input_dims = instance->GetInputDims();
71 | TNN_NS::Mat input_mat = TNN_NS::Mat(TNN_NS::DEVICE_NAIVE, TNN_NS::N8UC3, input_dims, img.data);
72 |
73 | vector feature;
74 | status = instance->Forward(input_mat, feature);
75 | if (status != 0) {
76 | return status;
77 | }
78 | for (float i : feature)
79 | printf("%f ", i);
80 | printf("\n");
81 |
82 | Timer timer("feature");
83 |
84 | for (size_t i = 0; i < 100; i++) {
85 | timer.Start();
86 | status = instance->Forward(input_mat, feature);
87 | if (status != 0)
88 | return status;
89 | timer.Stop();
90 | }
91 | timer.Print();
92 |
93 | return 0;
94 | }
95 |
--------------------------------------------------------------------------------
/recognition/doc/framework.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/doc/framework.png
--------------------------------------------------------------------------------
/recognition/doc/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/doc/logo.png
--------------------------------------------------------------------------------
/recognition/local_train.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -d "logs" ]; then
4 | mkdir logs
5 | fi
6 |
7 | if [ ! -d "ckpt" ]; then
8 | mkdir ckpt
9 | fi
10 |
11 | export CUDA_VISIBLE_DEVICES='0,1,2,3,4,5,6,7'
12 | nohup python -u -m torch.distributed.launch --nproc_per_node=8 --nnodes=1 train.py > logs/$(date +%F-%H-%M-%S).log 2>&1 &
--------------------------------------------------------------------------------
/recognition/requirements.txt:
--------------------------------------------------------------------------------
1 | dareblopy
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/README.md:
--------------------------------------------------------------------------------
1 | # Privacy-Preserving Face Recognition with Learnable Privacy Budgets in Frequency Domain
2 |
3 | ## Introduction
4 |
5 | Face recognition technology has been used in many fields due to its high recognition accuracy, including the face unlocking of mobile devices, community access control systems, and city surveillance. As the current high accuracy is guaranteed by very deep network structures, facial images often need to be transmitted to third-party servers with high computational power for inference. However, facial images visually reveal the user's identity information. In this process, both untrusted service providers and malicious users can significantly increase the risk of a personal privacy breach. Current privacy-preserving approaches to face recognition are often accompanied by many side effects, such as a significant increase in inference time or a noticeable decrease in recognition accuracy. This paper proposes a privacy-preserving face recognition method using differential privacy in the frequency domain. Due to the utilization of differential privacy, it offers a guarantee of privacy in theory. Meanwhile, the loss of accuracy is very slight. This method first converts the original image to the frequency domain and removes the direct component termed DC. Then a privacy budget allocation method can be learned based on the loss of the back-end face recognition network within the differential privacy framework. Finally, it adds the corresponding noise to the frequency domain features. Our method performs very well with several classical face recognition test sets according to the extensive experiments.
6 |
7 | ## Method
8 |
9 |
10 |
11 |
12 | ## Experiments
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Citation
23 | If you find this code useful in your research, please consider citing us:
24 | ```
25 | @article{ji2022privacy,
26 | title={Privacy-Preserving Face Recognition with Learnable Privacy Budgets in Frequency Domain},
27 | author={Ji, Jiazhen and Wang, Huan and Huang, Yuge and Wu, Jiaxiang and Xu, Xingkun and Ding, Shouhong and Zhang, ShengChuan and Cao, Liujuan and Ji, Rongrong},
28 | journal={arXiv preprint arXiv:2207.07316},
29 | year={2022}
30 | }
31 |
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/doc/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/dctdp/doc/main.png
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/doc/result1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/dctdp/doc/result1.png
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/doc/result2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/dctdp/doc/result2.png
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/doc/result3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/dctdp/doc/result3.png
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/local_train.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -d "logs" ]; then
4 | mkdir logs
5 | fi
6 |
7 | if [ ! -d "ckpt" ]; then
8 | mkdir ckpt
9 | fi
10 |
11 | export CUDA_VISIBLE_DEVICES='0'
12 | nohup python3 -u -m torch.distributed.launch --nproc_per_node=1 --nnodes=1 train.py > logs/$(date +%F-%H-%M-%S).log 2>&1 &
13 |
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/requirements.txt:
--------------------------------------------------------------------------------
1 | dareblopy
2 | torchjpeg
3 | torchsummary
4 |
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '' # to fill, the parent root where your train/val/test data are stored
3 | INDEX_ROOT: '' #to fill
4 | DATASETS: # the dataset index name
5 | - name: TFR-refined_ms1m_112_shuf #TFR-vggface2_shuf
6 | batch_size: 64
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.4
10 |
11 | BACKBONE_RESUME: ""
12 | HEAD_RESUME: ""
13 | META_RESUME: ""
14 |
15 | # BACKBONE_NAME: 'EfficientNetB0'
16 | INPUT_SIZE: [112, 112] # support: [112, 112] and [224, 224]
17 | BACKBONE_NAME: 'IR_50' # Must be a model of the IR series
18 | EMBEDDING_SIZE: 512 # feature dimension
19 |
20 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
21 | LOG_ROOT: './tensorboard' # the root to log your train/val status
22 |
23 | DIST_FC: true
24 | HEAD_NAME: "ArcFace" # support: ['ArcFace', 'CurricularFace', 'CosFace']
25 | LOSS_NAME: 'DistCrossEntropy' # support: ['DistCrossEntropy', 'Softmax']
26 |
27 | RGB_MEAN: [0.5, 0.5, 0.5] # for normalize inputs to [-1, 1]
28 | RGB_STD: [0.5, 0.5, 0.5]
29 |
30 | LRS: [0.1, 0.01, 0.001, 0.0001] # initial LR
31 | LRS_NOISE: [0.1, 0.01, 0.001, 0.0001]
32 |
33 | WARMUP_STEP: -1
34 | STAGES: [10, 18, 22] # epoch stages to decay learning rate
35 |
36 | START_EPOCH: 0 # start epoch
37 | NUM_EPOCH: 24 # total epoch number
38 | SAVE_EPOCHS: [10, 18, 22, 24]
39 |
40 | WEIGHT_DECAY: 0.0005 # do not apply to batch_norm parameters
41 | MOMENTUM: 0.9
42 |
43 | WORLD_SIZE: 1
44 | RANK: 0
45 | LOCAL_RANK: 0
46 | DIST_BACKEND: 'nccl'
47 | DIST_URL: 'env://'
48 |
49 | NUM_WORKERS: 8
50 |
51 | AMP: True # fp16 for backbone
52 |
--------------------------------------------------------------------------------
/recognition/tasks/dctdp/utils.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 | from torchjpeg import dct
5 |
6 | def images_to_batch(x):
7 | x = (x + 1) / 2 * 255
8 | x = F.interpolate(x, scale_factor=8, mode='bilinear', align_corners=True)
9 | if x.shape[1] != 3:
10 | print("Wrong input, Channel should equals to 3")
11 | return
12 | x = dct.to_ycbcr(x) # comvert RGB to YCBCR
13 | x -= 128
14 | bs, ch, h, w = x.shape
15 | block_num = h // 8
16 | x = x.view(bs * ch, 1, h, w)
17 | x = F.unfold(x, kernel_size=(8, 8), dilation=1, padding=0,
18 | stride=(8, 8))
19 | x = x.transpose(1, 2)
20 | x = x.view(bs, ch, -1, 8, 8)
21 | dct_block = dct.block_dct(x)
22 | dct_block = dct_block.view(bs, ch, block_num, block_num, 64).permute(0, 1, 4, 2, 3)
23 | dct_block = dct_block[:, :, 1:, :, :] # remove DC
24 | dct_block = dct_block.reshape(bs, -1, block_num, block_num)
25 | return dct_block
26 |
27 |
28 | class NoisyActivation(nn.Module):
29 | def __init__(self, input_shape=112, budget_mean=4, sensitivity=None):
30 | super(NoisyActivation, self).__init__()
31 | self.h, self.w = input_shape, input_shape
32 | if sensitivity is None:
33 | sensitivity = torch.ones([189, self.h, self.w]).cuda()
34 | self.sensitivity = sensitivity.reshape(189 * self.h * self.w)
35 | self.given_locs = torch.zeros((189, self.h, self.w))
36 | size = self.given_locs.shape
37 | self.budget = budget_mean * 189 * self.h * self.w
38 | self.locs = nn.Parameter(torch.Tensor(size).copy_(self.given_locs))
39 | self.rhos = nn.Parameter(torch.zeros(size))
40 | self.laplace = torch.distributions.laplace.Laplace(0, 1)
41 | self.rhos.requires_grad = True
42 | self.locs.requires_grad = True
43 |
44 | def scales(self):
45 | softmax = nn.Softmax()
46 | return (self.sensitivity / (softmax(self.rhos.reshape(189 * self.h * self.w))
47 | * self.budget)).reshape(189, self.h, self.w)
48 |
49 | def sample_noise(self):
50 | epsilon = self.laplace.sample(self.rhos.shape).cuda()
51 | return self.locs + self.scales() * epsilon
52 |
53 | def forward(self, input):
54 | noise = self.sample_noise()
55 | output = input + noise
56 | return output
57 |
58 | def aux_loss(self):
59 | scale = self.scales()
60 | loss = -1.0 * torch.log(scale.mean())
61 | return loss
62 |
--------------------------------------------------------------------------------
/recognition/tasks/duetface/base_task.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import torch
4 | import torch.nn.init as init
5 | from tasks.duetface.duetface_model import DuetFaceModel
6 |
7 | from ..head import get_head
8 | from ..util import get_class_split
9 |
10 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..'))
11 |
12 | # ================================================
13 | # do NOT run this script directly
14 | # append the following make_interactive_models() function to base_task.py
15 | # right after make_models()
16 | # ================================================
17 |
18 |
19 | def make_interactive_models(self):
20 | sub_channels = self.cfg['SUB_CHS']
21 | main_feature_size = self.cfg['FEATURE_SIZE']
22 | sub_feature_size = self.cfg['SUB_FEATURE_SIZE']
23 |
24 | num_sub_channels = len(sub_channels) * 3
25 | self.backbone = DuetFaceModel(num_sub_channels, main_feature_size, sub_feature_size,
26 | main_model_name=self.cfg['BACKBONE_NAME'],
27 | sub_model_name=self.cfg['SUB_BACKBONE_NAME'])
28 |
29 | # load pre-trained client-side model from a given checkpoint
30 | if self.cfg['LOAD_CKPT']:
31 | ckpt_path = self.cfg['CKPT_PATH']
32 | if not os.path.exists(ckpt_path):
33 | raise RuntimeError("%s not exists" % ckpt_path)
34 |
35 | model_dict = self.backbone.sub_model.state_dict()
36 | pretrained_dict = torch.load(ckpt_path)
37 | pretrained_dict = {k.split('.', 1)[1]: v for k, v in pretrained_dict.items() if
38 | k.split('.', 1)[1] in model_dict}
39 |
40 | model_dict.update(pretrained_dict)
41 | self.backbone.sub_model.load_state_dict(model_dict)
42 |
43 | self.backbone = self.backbone.cuda()
44 | # logging.info("DuetFace Backbone Generated")
45 |
46 | # make heads, the rest are identical to that in make_model()
47 | embedding_size = self.cfg['EMBEDDING_SIZE']
48 | self.class_shards = []
49 | metric = get_head(self.cfg['HEAD_NAME'], dist_fc=self.dist_fc)
50 |
51 | for name, branch in self.branches.items():
52 | class_num = self.class_nums[name]
53 | class_shard = get_class_split(class_num, self.world_size)
54 | self.class_shards.append(class_shard)
55 | # logging.info('Split FC: {}'.format(class_shard))
56 |
57 | init_value = torch.FloatTensor(embedding_size, class_num)
58 | init.normal_(init_value, std=0.01)
59 | head = metric(in_features=embedding_size,
60 | gpu_index=self.rank,
61 | weight_init=init_value,
62 | class_split=class_shard,
63 | scale=branch.scale,
64 | margin=branch.margin)
65 | del init_value
66 | head = head.cuda()
67 | self.heads[name] = head
68 |
--------------------------------------------------------------------------------
/recognition/tasks/duetface/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/duetface/pipeline.png
--------------------------------------------------------------------------------
/recognition/tasks/duetface/requirements.txt:
--------------------------------------------------------------------------------
1 | dareblopy
2 | torchjpeg
3 | torchsummary
4 | shapely
--------------------------------------------------------------------------------
/recognition/tasks/duetface/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '' # to fill, the parent root where your train/val/test data are stored
3 | INDEX_ROOT: '' # to fill
4 | DATASETS: # the dataset index name
5 | - name: # to fill, the dataset name
6 | batch_size: 64
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.5
10 |
11 | BACKBONE_RESUME: ""
12 | HEAD_RESUME: ""
13 | META_RESUME: ""
14 |
15 | INPUT_SIZE: [ 112, 112 ]
16 | BACKBONE_NAME: 'IR_18' # IR_50
17 | EMBEDDING_SIZE: 512 # feature dimension
18 |
19 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
20 | LOG_ROOT: './tensorboard' # the root to log your train/val status
21 |
22 | DIST_FC: true
23 | HEAD_NAME: "ArcFace" # support: ['ArcFace', 'CurricularFace', 'CosFace']
24 | LOSS_NAME: 'DistCrossEntropy' # support: ['DistCrossEntropy', 'Softmax']
25 |
26 | RGB_MEAN: [ 0.5, 0.5, 0.5 ]
27 | RGB_STD: [ 0.5, 0.5, 0.5 ]
28 |
29 | LRS: [ 0.1, 0.01, 0.001, 0.0001 ]
30 | WARMUP_STEP: -1
31 | STAGES: [ 10, 18, 22 ]
32 |
33 | START_EPOCH: 0
34 | NUM_EPOCH: 24
35 | SAVE_EPOCHS: [ 16, 20, 24 ]
36 |
37 | WEIGHT_DECAY: 0.0005
38 | MOMENTUM: 0.9
39 |
40 | WORLD_SIZE: 1
41 | RANK: 0
42 | LOCAL_RANK: 0
43 | DIST_BACKEND: 'nccl'
44 | DIST_URL: 'env://'
45 |
46 | NUM_WORKERS: 8
47 |
48 | AMP: false # fp16 for backbone
49 |
50 |
51 | # DuetFace configurations
52 | MODE: INT # mode of training, support: RGB (baseline), INT
53 | SUB_CHS: [ 0, 1, 2, 3, 4, 5, 8, 9, 16, 24 ] # channels assigned to clients
54 | SUB_BACKBONE_NAME: 'MobileFaceNet' # client-side backbone
55 | FEATURE_SIZE: 512 # output feature size of server-side model
56 | SUB_FEATURE_SIZE: 512 # feature size of client-side model
57 | SUB_WARMUP_STEP: 0 # pre-training steps of client-side model, set to 0 if load checkpoint
58 | LOAD_CKPT: true # for end-to-end training, set to true to load following checkpoints
59 | CKPT_PATH: '' # to fill, checkpoint path of pre-trained client-side model
60 | LANDMARK_CKPT_PATH: '' # to fill, checkpoint path of PFLD facial landmark detector
61 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/README.md:
--------------------------------------------------------------------------------
1 | # Evaluation-oriented knowledge distillation for deep face recognition
2 |
3 | ## Introduction
4 |
5 | Knowledge distillation (KD) is a widely-used technique that utilizes large networks to improve the performance of compact models. Previous KD approaches usually aim to guide the student to mimic the teacher’s behavior completely in the representation space. However, such one-to-one corresponding constraints may lead to inflexible knowledge transfer from the teacher to the student, especially those with low model capacities. Inspired by the ultimate goal of KD methods, we propose a novel Evaluation oriented KD method (EKD) for deep face recognition to directly reduce the performance gap between the teacher and student models during training. Specifically, we adopt the commonly used evaluation metrics in face recognition, i.e., False Positive Rate (FPR) and True Positive Rate (TPR) as the performance indicator. According to the evaluation protocol, the critical pair relations that cause the TPR and FPR difference between the teacher and student models are selected. Then, the critical relations in the student are constrained to approximate the corresponding ones in the teacher by a novel rank-based loss function, giving more flexibility to the student with low capacity. Extensive experimental results on popular benchmarks demonstrate the superiority of our EKD over state-of-the-art competitors.
6 |
7 | ## Method
8 |
9 |
10 |
11 |
12 | ## Experiments
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Citation
23 | If you find this code useful in your research, please consider citing us:
24 | ```
25 | @inproceedings{
26 | title={Evaluation-oriented knowledge distillation for deep face recognition},
27 | author={Yuge Huang, Jiaxiang Wu, Xingkun Xu, Shouhong Ding},
28 | booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
29 | year={2022}
30 | }
31 | ```
--------------------------------------------------------------------------------
/recognition/tasks/ekd/dataset.py:
--------------------------------------------------------------------------------
1 | import math
2 | import random
3 | from collections import defaultdict
4 | from torch.utils.data.sampler import Sampler
5 |
6 |
7 | def create_label2index(dataset):
8 | """ create label2index map for BalancedBatchSampler,
9 | dataset is a SingleDataset object
10 | """
11 | label2index = defaultdict(list)
12 | for i, sample in enumerate(dataset.inputs):
13 | label = sample[-1]
14 | label2index[label].append(i)
15 | return label2index
16 |
17 |
18 | class BalancedBatchSampler(Sampler):
19 | """ BalancedBatchSampler class
20 | Each batch includes `general_batch_size` general samples and `balanced_batch_size` balanced samples,
21 | general samples are directly randomly sampled, But balanced samples means picking `subsample_size`
22 | samples for each label.
23 | """
24 | def __init__(self, labels2index, general_batch_size,
25 | balanced_batch_size, world_size,
26 | total_size, subsample_size=4):
27 | self.general_batch_size = general_batch_size
28 | self.balanced_batch_size = balanced_batch_size
29 | self.subsample_size = subsample_size
30 | self.labels2index = labels2index
31 | self.total_size = total_size
32 | self.world_size = world_size
33 |
34 | self.samples = []
35 | for _, inds in self.labels2index.items():
36 | self.samples.extend(inds)
37 |
38 | def __iter__(self):
39 | for _ in range(self.__len__()):
40 | inds = []
41 | # random sample
42 | random_samples = random.sample(self.samples, self.general_batch_size)
43 | inds.extend(random_samples)
44 |
45 | # balanced sample
46 | sample_labels = random.sample(self.labels2index.keys(),
47 | self.balanced_batch_size // self.subsample_size)
48 | for each_label in sample_labels:
49 | each_label_indexes = self.labels2index[each_label]
50 | if len(each_label_indexes) > self.subsample_size:
51 | each_label_inds = random.sample(each_label_indexes, self.subsample_size)
52 | elif len(each_label_indexes) == self.subsample_size:
53 | each_label_inds = each_label_indexes
54 | else:
55 | repeat_inds = each_label_indexes * math.ceil(self.subsample_size / len(each_label_indexes))
56 | each_label_inds = repeat_inds[:self.subsample_size]
57 | inds.extend(each_label_inds)
58 | yield inds
59 |
60 | def __len__(self):
61 | return self.total_size // self.general_batch_size // self.world_size
62 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/cc.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | import torch.nn as nn
4 | import torch.nn.functional as F
5 |
6 |
7 | class CC(nn.Module):
8 | """ Correlation Congruence for Knowledge Distillation. ICCV 2019
9 | """
10 | def __init__(self, gamma=0.4, p=2):
11 | super().__init__()
12 | self.gamma = gamma
13 | self.p = p
14 |
15 | def forward(self, feat_s, feat_t):
16 | corr_mat_s = self.get_correlation_matrix(feat_s)
17 | corr_mat_t = self.get_correlation_matrix(feat_t)
18 | loss = F.mse_loss(corr_mat_s, corr_mat_t)
19 | return loss
20 |
21 | def get_correlation_matrix(self, feat):
22 | feat = F.normalize(feat)
23 | sim_mat = torch.matmul(feat, feat.t())
24 | corr_mat = torch.zeros_like(sim_mat)
25 | for p in range(self.p):
26 | corr_mat += math.exp(-2*self.gamma) * (2*self.gamma)**p / \
27 | math.factorial(p) * torch.pow(sim_mat, p)
28 | return corr_mat
29 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/cosine_distance.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 |
5 |
6 | class CosineDistance(nn.Module):
7 | """ Feature-Feature cosine distance
8 | """
9 |
10 | def __init__(self):
11 | super(CosineDistance, self).__init__()
12 |
13 | def forward(self, f_s, f_t):
14 | batch_size = f_s.size(0)
15 | # just to make sure the tensor is 2D
16 | f_s = f_s.view(batch_size, -1)
17 | f_t = f_t.view(batch_size, -1)
18 | # normalize the feature
19 | f_s = F.normalize(f_s)
20 | f_t = F.normalize(f_t)
21 |
22 | distance = F.cosine_similarity(f_s, f_t, dim=1, eps=1e-8)
23 | loss = 1 - torch.mean(distance)
24 | return loss
25 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/darkrank.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 |
5 |
6 | def euclidean_dist(x, y):
7 | """
8 | Args:
9 | x: pytorch Variable, with shape [m, d]
10 | y: pytorch Variable, with shape [n, d]
11 | Returns:
12 | dist: pytorch Variable, with shape [m, n]
13 | """
14 |
15 | m, n = x.size(0), y.size(0)
16 | xx = torch.pow(x, 2).sum(1, keepdim=True).expand(m, n)
17 | yy = torch.pow(y, 2).sum(1, keepdim=True).expand(n, m).t()
18 | dist = xx + yy
19 | dist = torch.addmm(dist, x, y.t(), beta=1, alpha=-2)
20 | dist = dist.clamp(min=1e-12).sqrt() # for numerical stability
21 | return dist
22 |
23 |
24 | class HardDarkRank(nn.Module):
25 | """ DarkRank: Accelerating Deep Metric Learning via Cross Sample Similarities Transfer. AAAI 2018
26 | """
27 | def __init__(self, alpha=3, beta=3, permute_len=3):
28 | super().__init__()
29 | self.alpha = alpha
30 | self.beta = beta
31 | self.permute_len = permute_len
32 |
33 | def forward(self, g_s, g_t):
34 | g_s = F.normalize(g_s)
35 | g_t = F.normalize(g_t)
36 |
37 | score_teacher = -1 * self.alpha * \
38 | euclidean_dist(g_t, g_t).pow(self.beta)
39 | score_student = -1 * self.alpha * \
40 | euclidean_dist(g_s, g_s).pow(self.beta)
41 |
42 | permute_idx = score_teacher.sort(dim=1, descending=True)[
43 | 1][:, 1:(self.permute_len+1)]
44 | ordered_student = torch.gather(score_student, 1, permute_idx)
45 |
46 | log_prob = (ordered_student - torch.stack([torch.logsumexp(
47 | ordered_student[:, i:], dim=1) for i in range(permute_idx.size(1))], dim=1)).sum(dim=1)
48 | loss = (-1 * log_prob).mean()
49 | return loss
50 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/fitnet.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 |
4 | class HintLoss(nn.Module):
5 | """ Fitnets: hints for thin deep nets, ICLR 2015
6 | """
7 | def __init__(self):
8 | super().__init__()
9 | self.crit = nn.MSELoss()
10 |
11 | def forward(self, f_s, f_t):
12 | loss = self.crit(f_s, f_t)
13 | return loss
14 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/rkd.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 |
5 |
6 | class RKDLoss(nn.Module):
7 | """ Relational Knowledge Disitllation, CVPR2019
8 | """
9 | def __init__(self, w_d=25, w_a=50):
10 | super().__init__()
11 | self.w_d = w_d
12 | self.w_a = w_a
13 |
14 | def forward(self, f_s, f_t):
15 | student = f_s.view(f_s.shape[0], -1)
16 | teacher = f_t.view(f_t.shape[0], -1)
17 |
18 | # RKD distance loss
19 | with torch.no_grad():
20 | t_d = self.pdist(teacher, squared=False)
21 | mean_td = t_d[t_d > 0].mean()
22 | t_d = t_d / mean_td
23 |
24 | d = self.pdist(student, squared=False)
25 | mean_d = d[d > 0].mean()
26 | d = d / mean_d
27 |
28 | loss_d = F.smooth_l1_loss(d, t_d)
29 |
30 | # RKD Angle loss
31 | with torch.no_grad():
32 | td = (teacher.unsqueeze(0) - teacher.unsqueeze(1))
33 | norm_td = F.normalize(td, p=2, dim=2)
34 | t_angle = torch.bmm(norm_td, norm_td.transpose(1, 2)).view(-1)
35 |
36 | sd = (student.unsqueeze(0) - student.unsqueeze(1))
37 | norm_sd = F.normalize(sd, p=2, dim=2)
38 | s_angle = torch.bmm(norm_sd, norm_sd.transpose(1, 2)).view(-1)
39 |
40 | loss_a = F.smooth_l1_loss(s_angle, t_angle)
41 |
42 | loss = self.w_d * loss_d + self.w_a * loss_a
43 |
44 | return loss
45 |
46 | @staticmethod
47 | def pdist(e, squared=False, eps=1e-12):
48 | e_square = e.pow(2).sum(dim=1)
49 | prod = e @ e.t()
50 | res = (e_square.unsqueeze(1) + e_square.unsqueeze(0) - 2 * prod).clamp(min=eps)
51 |
52 | if not squared:
53 | res = res.sqrt()
54 |
55 | res = res.clone()
56 | res[range(len(e)), range(len(e))] = 0
57 | return res
58 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/similarity_perserving.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 |
5 |
6 | class SimPerserving(nn.Module):
7 | """ Similarity-Preserving Knowledge Distillation, ICCV2019
8 | """
9 |
10 | def __init__(self):
11 | super().__init__()
12 |
13 | def forward(self, g_s, g_t):
14 | return self.similarity_loss(g_s, g_t)
15 |
16 | # different from the origin paper, here use the embedding feature
17 | def similarity_loss(self, f_s, f_t):
18 | batch_size = f_s.size(0)
19 | # just to make sure the tensor is 2D
20 | f_s = f_s.view(batch_size, -1)
21 | f_t = f_t.view(batch_size, -1)
22 | # normalize the feature
23 | f_s = F.normalize(f_s)
24 | f_t = F.normalize(f_t)
25 | s_similarity = torch.mm(f_s, torch.t(f_s))
26 | t_similarity = torch.mm(f_t, torch.t(f_t))
27 |
28 | diff_similarity = s_similarity - t_similarity
29 | diff_norm = torch.norm(diff_similarity)
30 | loss = diff_norm * diff_norm / (batch_size * batch_size)
31 |
32 | return loss
33 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/distillation/simple_kd.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import torch.nn.functional as F
3 |
4 |
5 | class DistillKL(nn.Module):
6 | """ Distilling the Knowledge in a Neural Network, NIPSW 2015
7 | """
8 | def __init__(self, t=4):
9 | super().__init__()
10 | self.t = t
11 |
12 | def forward(self, y_s, y_t):
13 | p_s = F.log_softmax(y_s/self.t, dim=1)
14 | p_t = F.softmax(y_t/self.t, dim=1)
15 | loss = F.kl_div(p_s, p_t, size_average=False) * (self.t**2) / y_s.shape[0]
16 | return loss
17 |
--------------------------------------------------------------------------------
/recognition/tasks/ekd/doc/method.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/ekd/doc/method.png
--------------------------------------------------------------------------------
/recognition/tasks/ekd/doc/result1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/ekd/doc/result1.png
--------------------------------------------------------------------------------
/recognition/tasks/ekd/doc/result2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/ekd/doc/result2.png
--------------------------------------------------------------------------------
/recognition/tasks/ekd/doc/result3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/ekd/doc/result3.png
--------------------------------------------------------------------------------
/recognition/tasks/ekd/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '/youtu-face/ata_root/remote_tfrecord' # the parent root where your train/val/test data are stored
3 | INDEX_ROOT: '/youtu-face/train_data/'
4 | DATASETS: # the dataset index name
5 | - name: TFR-refined_ms1m_112_shuf
6 | batch_size: 64
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.4
10 |
11 | T_BACKBONE_RESUME: ""
12 | T_BACKBONE_NAME: 'IR_50'
13 |
14 | BACKBONE_RESUME: ""
15 | HEAD_RESUME: ""
16 | BACKBONE_NAME: 'MobileFaceNet'
17 | HEAD_NAME: "ArcFace"
18 |
19 | META_RESUME: ""
20 |
21 | GENERAL_BATCH_SIZE: 32
22 | BALANCED_BATCH_SIZE: 32
23 |
24 | INPUT_SIZE: [112, 112] # support: [112, 112] and [224, 224]
25 | EMBEDDING_SIZE: 512 # feature dimension
26 |
27 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
28 | LOG_ROOT: './tensorboard' # the root to log your train/val status
29 |
30 | DIST_FC: true
31 |
32 | RGB_MEAN: [0.5, 0.5, 0.5] # for normalize inputs to [-1, 1]
33 | RGB_STD: [0.5, 0.5, 0.5]
34 |
35 | LRS: [0.1, 0.01, 0.001, 0.0001] # initial LR
36 | WARMUP_STEP: -1
37 | STAGES: [10, 18, 22] # epoch stages to decay learning rate
38 |
39 | START_EPOCH: 0 # start epoch
40 | NUM_EPOCH: 26 # total epoch number
41 | SAVE_EPOCHS: [1, 26]
42 |
43 | WEIGHT_DECAY: 0.0005 # do not apply to batch_norm parameters
44 | MOMENTUM: 0.9
45 |
46 | WORLD_SIZE: 1
47 | RANK: 0
48 | LOCAL_RANK: 0
49 | DIST_BACKEND: 'nccl'
50 | DIST_URL: 'env://'
51 |
52 | NUM_WORKERS: 8
53 |
--------------------------------------------------------------------------------
/recognition/tasks/minusface/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/minusface/__init__.py
--------------------------------------------------------------------------------
/recognition/tasks/minusface/local_train.sh:
--------------------------------------------------------------------------------
1 | export CUDA_VISIBLE_DEVICES='0'
2 | python3 -u -m torch.distributed.launch --nproc_per_node=1 --nnodes=1 train.py
3 |
--------------------------------------------------------------------------------
/recognition/tasks/minusface/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/minusface/pipeline.png
--------------------------------------------------------------------------------
/recognition/tasks/minusface/requirements.txt:
--------------------------------------------------------------------------------
1 | dareblopy
2 | torchjpeg
--------------------------------------------------------------------------------
/recognition/tasks/minusface/train.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..'))
5 | from tasks.partialface.train import TrainTask
6 |
7 | # parts of MinusFace's code requires the prior repository of PartialFace to sustain
8 | # please refer to ../partialface for details
9 |
10 | def main():
11 | task_dir = os.path.dirname(os.path.abspath(__file__))
12 | task = TrainTask(os.path.join(task_dir, 'train.yaml'))
13 | task.init_env()
14 | task.train()
15 |
16 |
17 | if __name__ == '__main__':
18 | main()
19 |
--------------------------------------------------------------------------------
/recognition/tasks/minusface/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '' # [fill in this blank] the parent directory where your train/val/test data are stored
3 | INDEX_ROOT: '' # [fill in this blank] the parent directory for index
4 | DATASETS:
5 | - name: # [fill in this blank] the name of your dataset
6 | batch_size: 64
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.5
10 |
11 | BACKBONE_RESUME: ""
12 | HEAD_RESUME: ""
13 | META_RESUME: ""
14 |
15 | INPUT_SIZE: [ 112, 112 ]
16 | BACKBONE_NAME: 'IR_18' # support: ['IR_18', 'IR_50']
17 | EMBEDDING_SIZE: 512
18 |
19 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
20 | LOG_ROOT: './tensorboard' # the root to log your train/val status
21 |
22 | DIST_FC: true
23 | HEAD_NAME: "ArcFace" # support: ['ArcFace', 'CurricularFace', 'CosFace']
24 | LOSS_NAME: 'DistCrossEntropy' # support: ['DistCrossEntropy', 'Softmax']
25 |
26 | RGB_MEAN: [ 0.5, 0.5, 0.5 ] # for normalize inputs to [-1, 1]
27 | RGB_STD: [ 0.5, 0.5, 0.5 ]
28 |
29 | LRS: [ 0.01, 0.001, 0.0001, 0.00001 ]
30 | WARMUP_STEP: -1
31 | STAGES: [ 10, 18, 22 ]
32 |
33 | START_EPOCH: 0 # start epoch
34 | NUM_EPOCH: 24 # total epoch number
35 | SAVE_EPOCHS: [ 1, 10, 16, 20, 24 ]
36 |
37 | WEIGHT_DECAY: 0.0005 # do not apply to batch_norm parameters
38 | MOMENTUM: 0.9
39 |
40 | WORLD_SIZE: 1
41 | RANK: 0
42 | LOCAL_RANK: 0
43 | DIST_BACKEND: 'nccl'
44 | DIST_URL: 'env://'
45 |
46 | NUM_WORKERS: 8
47 |
48 | AMP: false # fp16 for backbone
49 |
50 | # MinusFace
51 | METHOD: MinusFace
52 | TASK: stage1 # toy, stage1, stage2
53 | NUM_AUG: 3 # multiplier for data augmentation
54 | TASK_BACKBONE: 'IR_18' # IR_18, IR_50
55 | PRETRAIN_CKPT: '' # [fill in this blank] to train the recognition model requires pretrained MinusFace checkpoint
56 | TASK_VER: 3
--------------------------------------------------------------------------------
/recognition/tasks/partialface/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_task import LocalBaseTask
2 | from .utils import form_training_batch
--------------------------------------------------------------------------------
/recognition/tasks/partialface/local_train.sh:
--------------------------------------------------------------------------------
1 | export CUDA_VISIBLE_DEVICES='0'
2 | python -u -m torch.distributed.launch --nproc_per_node=1 --nnodes=1 train.py
3 |
--------------------------------------------------------------------------------
/recognition/tasks/partialface/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/partialface/pipeline.png
--------------------------------------------------------------------------------
/recognition/tasks/partialface/requirements.txt:
--------------------------------------------------------------------------------
1 | dareblopy
2 | torchjpeg
--------------------------------------------------------------------------------
/recognition/tasks/partialface/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '' # [fill in this blank] the parent directory where your train/val/test data are stored
3 | INDEX_ROOT: '' # [fill in this blank] the parent directory for index
4 | DATASETS:
5 | - name: # [fill in this blank] the name of your dataset
6 | batch_size: 64
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.5
10 |
11 | BACKBONE_RESUME: ""
12 | HEAD_RESUME: ""
13 | META_RESUME: ""
14 |
15 | INPUT_SIZE: [ 112, 112 ]
16 | BACKBONE_NAME: 'IR_18' # support: ['IR_18', 'IR_50']
17 | EMBEDDING_SIZE: 512
18 |
19 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
20 | LOG_ROOT: './tensorboard' # the root to log your train/val status
21 |
22 | DIST_FC: true
23 | HEAD_NAME: "ArcFace" # support: ['ArcFace', 'CurricularFace', 'CosFace']
24 | LOSS_NAME: 'DistCrossEntropy' # support: ['DistCrossEntropy', 'Softmax']
25 |
26 | RGB_MEAN: [ 0.5, 0.5, 0.5 ] # for normalize inputs to [-1, 1]
27 | RGB_STD: [ 0.5, 0.5, 0.5 ]
28 |
29 | LRS: [ 0.1, 0.01, 0.001, 0.0001 ] # initial LR
30 | WARMUP_STEP: -1
31 | STAGES: [ 10, 18, 22 ] # epoch stages to decay learning rate
32 |
33 | START_EPOCH: 0 # start epoch
34 | NUM_EPOCH: 24 # total epoch number
35 | SAVE_EPOCHS: [ 1, 10, 16, 20, 24 ]
36 |
37 |
38 | WEIGHT_DECAY: 0.0005 # do not apply to batch_norm parameters
39 | MOMENTUM: 0.9
40 |
41 | WORLD_SIZE: 1
42 | RANK: 0
43 | LOCAL_RANK: 0
44 | DIST_BACKEND: 'nccl'
45 | DIST_URL: 'env://'
46 |
47 | NUM_WORKERS: 8
48 |
49 | AMP: false # fp16 for backbone
50 |
51 | # PartialFace
52 | METHOD: PartialFace
53 | NUM_AUG: 3 # r, multiplier for data augmentation
54 | NUM_CHS: 9 # s, channels within each rank, 9 by default
55 | # To modify S, P, please set them explicitly in partialface/utils.py
56 |
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/.gitignore:
--------------------------------------------------------------------------------
1 | templates
2 | ckpt
3 | pair_list
4 |
5 | # Python
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 | *.so
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # Virtual Environment
28 | venv/
29 | env/
30 | ENV/
31 | .env
32 |
33 | # IDE
34 | .idea/
35 | .vscode/
36 | *.swp
37 | *.swo
38 |
39 | # Logs
40 | *.log
41 | logs/
42 |
43 | # Local configuration
44 | config.local.yaml
45 | *.local.json
46 | *.local.yml
47 |
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/encrypt_template.sh:
--------------------------------------------------------------------------------
1 | python3 -m tasks.slerpface.encrypt_template.enrollment \
2 | --templates_folder ./tasks/slerpface/templates \
3 | --key CFP \
4 | --alpha 0.9 \
5 | --drop_rate 0.5 \
6 | --group_size 16
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/extract_template.sh:
--------------------------------------------------------------------------------
1 | ### get SlerpFace model features ###
2 | python3 -um tasks.slerpface.extract_template.verification \
3 | --model_path=./tasks/slerpface/ckpt/Backbone_Epoch_24_checkpoint.pth \
4 | --data_root=/remote-home/share/yxmi/datasets/val_data \
5 | --backbone=IR_50 \
6 | --batch_size=512 \
7 | --output_dir=./tasks/slerpface/templates \
8 | --gpu_ids=0
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/match_template.sh:
--------------------------------------------------------------------------------
1 | python3 -m tasks.slerpface.match_template.gen_sim \
2 | --templates_folder ./tasks/slerpface/templates \
3 | --key CFP \
4 | --alpha 0.9 \
5 | --group_size 16
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/tasks/slerpface/pipeline.png
--------------------------------------------------------------------------------
/recognition/tasks/slerpface/requirements.txt:
--------------------------------------------------------------------------------
1 | torch>=1.7.1
2 | torchvision>=0.8.2
3 | numpy>=1.19.2
4 | scipy>=1.6.0
5 | pillow>=8.0.0
6 | tqdm>=4.50.0
7 |
--------------------------------------------------------------------------------
/recognition/test/README.md:
--------------------------------------------------------------------------------
1 | ### Verification test
2 |
3 | LFW, CFP-FP, CPLFW, AgeDB, CALFW are popular verification datasets in face recognition task, below is test steps.
4 |
5 | 1. Download the bin files and save into `val_data_dir`, the test checkpoint path is `ckpt_path`.
6 | 2. Run test codes:
7 | ``` bash
8 | export CUDA_VISIBLE_DEVICES='0'
9 | python -u verification.py --ckpt_path=$ckpt_path --data_root=$val_data_dir
10 | ```
11 |
12 | RFW is the common test datasets for fairness, the test code is `verification_rfw.py`
13 |
14 | ### 1:1 test
15 |
16 | IJB-B and IJB-C are most common large-scale face 1:1 test protocols.
17 | 1. Download the raw image data and meta files, saved into `data_root`
18 | 2. Run test codes:
19 | ``` bash
20 | # extract face features
21 | export CUDA_VISIBLE_DEVICES='0,1,2,3'
22 | python -u extract_features.py --ckpt_path=$ckpt_path --backbone=$backbone_name --gpu_ids=$gpu_ids \
23 | --batch_size=512 --data_root=$data_root \
24 | --filename_list=$pts_score_file \
25 | --output_dir=$output_dir
26 | # evaluation
27 | python -u IJB_Evaluation.py --dataset=$dataset --meta_dir=$meta_dir \
28 | --feature="${output_dir}"/"feature.npy" --face_scores=${output_dir}/"faceness_scores.npy" \
29 | --output_name=${output_dir}/"similarity.npy"
30 |
31 | ```
--------------------------------------------------------------------------------
/recognition/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/recognition/test/__init__.py
--------------------------------------------------------------------------------
/recognition/tools/convert_new_index.py:
--------------------------------------------------------------------------------
1 | import os
2 | import argparse
3 |
4 |
5 | def parse_args():
6 | parser = argparse.ArgumentParser(
7 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
8 | description='convert training index file')
9 | parser.add_argument('--old', default=None, type=str, required=True,
10 | help='path to old training list')
11 | parser.add_argument('--tfr_index', default=None, type=str, required=True,
12 | help='path to tfrecord index file')
13 | parser.add_argument('--new', default=None, type=str, required=True,
14 | help='path to new training list')
15 | args = parser.parse_args()
16 | return args
17 |
18 |
19 | def build_dict(tfr_index):
20 | d = {}
21 | print("reading {}".format(tfr_index))
22 | tfr_name = os.path.basename(tfr_index).replace('.index', '')
23 | with open(tfr_index, 'r') as f:
24 | for line in f:
25 | file_name, shard_index, offset = line.rstrip().split('\t')
26 | d[file_name] = '{}\t{}\t{}'.format(tfr_name, shard_index, offset)
27 | print("build dict done")
28 | return d
29 |
30 |
31 | def convert(index_file, d, out_index_file):
32 | print("write to new index file {}".format(out_index_file))
33 | with open(index_file, 'r') as f, open(out_index_file, 'w') as out_f:
34 | for line in f:
35 | if '\t' in line:
36 | file_name, label = line.rstrip().split('\t')
37 | else:
38 | file_name, label = line.rstrip().split(' ')
39 | tfr_string = d.get(file_name, None)
40 | if tfr_string is None:
41 | print(file_name + " failed")
42 | continue
43 | out_f.write(tfr_string + '\t{}'.format(label) + '\n')
44 |
45 |
46 | def main():
47 | args = parse_args()
48 | d = build_dict(args.tfr_index)
49 | convert(args.old, d, args.new)
50 |
51 |
52 | if __name__ == '__main__':
53 | main()
54 |
--------------------------------------------------------------------------------
/recognition/tools/decode.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..'))
4 | import argparse
5 | import struct
6 | from torchkit.data import example_pb2
7 |
8 |
9 | def parse_args():
10 | parser = argparse.ArgumentParser(
11 | formatter_class=argparse.ArgumentDefaultsHelpFormatter,
12 | description='decode tfrecord')
13 | parser.add_argument('--tfrecords_dir', default=None, type=str, required=True,
14 | help='path to the output of tfrecords dir path')
15 | parser.add_argument('--output_dir', default=None, type=str, required=True,
16 | help='path to the output of decoded imgs')
17 | parser.add_argument('--limit', default=10, type=int, required=True,
18 | help='limit num of decoded samples')
19 |
20 | args = parser.parse_args()
21 | return args
22 |
23 |
24 | def parser(feature_list):
25 | for key, feature in feature_list:
26 | if key == 'image':
27 | image_raw = feature.bytes_list.value[0]
28 | return image_raw
29 | raise ValueError("No key=image in feature list")
30 |
31 |
32 | def get_record(record_file, offset):
33 | with open(record_file, 'rb') as ifs:
34 | ifs.seek(offset)
35 | byte_len_crc = ifs.read(12)
36 | proto_len = struct.unpack('Q', byte_len_crc[:8])[0]
37 | # proto,crc
38 | pb_data = ifs.read(proto_len)
39 | if len(pb_data) < proto_len:
40 | print("read pb_data err,proto_len:%s pb_data len:%s" % (proto_len, len(pb_data)))
41 | return None
42 | example = example_pb2.Example()
43 | example.ParseFromString(pb_data)
44 | # keep key value in order
45 | feature = sorted(example.features.feature.items())
46 | record = parser(feature)
47 | return record
48 |
49 |
50 | def main():
51 | args = parse_args()
52 | tfrecords_dir = os.path.normpath(args.tfrecords_dir)
53 | tfrecords_name = tfrecords_dir.split('/')[-1]
54 | output_dir = args.output_dir
55 | if not os.path.isdir(output_dir):
56 | os.makedirs(output_dir)
57 | limit = args.limit
58 | print(tfrecords_dir)
59 | print(tfrecords_name)
60 |
61 | index_file = os.path.join(tfrecords_dir, '%s.index' % tfrecords_name)
62 | with open(index_file, 'r') as f:
63 | for line_i, line in enumerate(f):
64 | if line_i >= limit:
65 | break
66 | raw_img_path, tfr_idx, tfr_offset = line.rstrip().split('\t')
67 | record_file = os.path.join(tfrecords_dir, "%s-%05d.tfrecord" % (tfrecords_name, int(tfr_idx)))
68 | image_raw = get_record(record_file, int(tfr_offset))
69 | img_path = os.path.join(output_dir, os.path.basename(raw_img_path))
70 | f = open(img_path, 'wb')
71 | f.write(image_raw)
72 | f.close()
73 |
74 | if __name__ == "__main__":
75 | main()
76 |
--------------------------------------------------------------------------------
/recognition/torchkit/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '0.2.0_beta'
2 |
--------------------------------------------------------------------------------
/recognition/torchkit/backbone/__init__.py:
--------------------------------------------------------------------------------
1 | from functools import partial
2 | from .model_resnet import ResNet_50, ResNet_101, ResNet_152
3 | from .model_irse import IR_18, IR_34, IR_50, IR_101, IR_152, IR_200
4 | from .model_irse import IR_SE_50, IR_SE_101, IR_SE_152, IR_SE_200
5 | from .model_mobilefacenet import MobileFaceNet
6 | from .model_efficientnet import EfficientNetB0, EfficientNetB1
7 | from .model_ghostnet import GhostNet
8 | from .fbnets.fbnet_builder import get_fbnet_model
9 |
10 | _model_dict = {
11 | 'ResNet_50': ResNet_50,
12 | 'ResNet_101': ResNet_101,
13 | 'ResNet_152': ResNet_152,
14 | 'IR_18': IR_18,
15 | 'IR_34': IR_34,
16 | 'IR_50': IR_50,
17 | 'IR_101': IR_101,
18 | 'IR_152': IR_152,
19 | 'IR_200': IR_200,
20 | 'IR_SE_50': IR_SE_50,
21 | 'IR_SE_101': IR_SE_101,
22 | 'IR_SE_152': IR_SE_152,
23 | 'IR_SE_200': IR_SE_200,
24 | 'MobileFaceNet': MobileFaceNet,
25 | 'EfficientNetB0': EfficientNetB0,
26 | 'EfficientNetB1': EfficientNetB1,
27 | 'GhostNet': GhostNet,
28 | 'fbnet_a': partial(get_fbnet_model, "fbnet_a"),
29 | 'fbnet_b': partial(get_fbnet_model, "fbnet_b"),
30 | 'fbnet_c': partial(get_fbnet_model, "fbnet_c"),
31 | }
32 |
33 |
34 | def get_model(key):
35 | """ Get different backbone network by key,
36 | support ResNet50, ResNet_101, ResNet_152
37 | IR_18, IR_34, IR_50, IR_101, IR_152, IR_200,
38 | IR_SE_50, IR_SE_101, IR_SE_152, IR_SE_200,
39 | EfficientNetB0, EfficientNetB1.
40 | MobileFaceNet, FBNets.
41 | """
42 | if key in _model_dict.keys():
43 | return _model_dict[key]
44 | else:
45 | raise KeyError("not support model {}".format(key))
46 |
--------------------------------------------------------------------------------
/recognition/torchkit/backbone/fbnets/layers/__init__.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 | from .batch_norm import FrozenBatchNorm2d
4 | from .misc import Conv2d
5 | from .misc import ConvTranspose2d
6 | from .misc import BatchNorm2d
7 | from .misc import interpolate
8 | from .misc import _NewEmptyTensorOp
9 |
10 |
11 | __all__ = ["Conv2d", "ConvTranspose2d", "interpolate",
12 | "BatchNorm2d", "FrozenBatchNorm2d", "_NewEmptyTensorOp"
13 | ]
14 |
--------------------------------------------------------------------------------
/recognition/torchkit/backbone/fbnets/layers/batch_norm.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from torch import nn
3 |
4 | class FrozenBatchNorm2d(nn.Module):
5 | """
6 | BatchNorm2d where the batch statistics and the affine parameters
7 | are fixed
8 | """
9 |
10 | def __init__(self, n):
11 | super(FrozenBatchNorm2d, self).__init__()
12 | self.register_buffer("weight", torch.ones(n))
13 | self.register_buffer("bias", torch.zeros(n))
14 | self.register_buffer("running_mean", torch.zeros(n))
15 | self.register_buffer("running_var", torch.ones(n))
16 |
17 | def forward(self, x):
18 | scale = self.weight * self.running_var.rsqrt()
19 | bias = self.bias - self.running_mean * scale
20 | scale = scale.reshape(1, -1, 1, 1)
21 | bias = bias.reshape(1, -1, 1, 1)
22 | return x * scale + bias
23 |
--------------------------------------------------------------------------------
/recognition/torchkit/data/__init__.py:
--------------------------------------------------------------------------------
1 | from .dataset import SingleDataset, MultiDataset
2 | from .parser import IndexParser, ImgSampleParser, TFRecordSampleParser
3 | from .sampler import MultiDistributedSampler
4 |
5 | __all__ = [
6 | 'SingleDataset',
7 | 'MultiDataset',
8 | 'IndexParser',
9 | 'ImgSampleParser',
10 | 'TFRecordSampleParser',
11 | 'MultiDistributedSampler',
12 | ]
13 |
--------------------------------------------------------------------------------
/recognition/torchkit/data/parser.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import dareblopy as db
4 | from . import example_pb2
5 |
6 |
7 | class IndexParser(object):
8 | """ Class for Line parser.
9 | """
10 | def __init__(self) -> None:
11 | self.sample_num = 0
12 | self.class_num = 0
13 |
14 | def __call__(self, line):
15 | line_s = line.rstrip().split('\t')
16 | if len(line_s) == 2:
17 | # Default line format
18 | img_path, label = line_s
19 | label = int(label)
20 | self.sample_num += 1
21 | self.class_num = max(self.class_num, label)
22 | return (img_path, label)
23 | elif len(line_s) == 4:
24 | # IndexTFRDataset line format
25 | tfr_name, tfr_index, tfr_offset, label = line_s
26 | label = int(label)
27 | tfr_file = "{0}/{0}-{1:05d}.tfrecord".format(tfr_name, int(tfr_index))
28 | tfr_offset = int(tfr_offset)
29 | self.sample_num += 1
30 | self.class_num = max(self.class_num, label)
31 | return (tfr_file, tfr_offset, label)
32 | else:
33 | raise RuntimeError("IndexParser line length %d not supported" % len(line_s))
34 |
35 | def reset(self):
36 | self.sample_num = 0
37 | self.class_num = 0
38 |
39 |
40 | class ImgSampleParser(object):
41 | """ Class for Image Sample parser
42 | """
43 | def __init__(self, transform) -> None:
44 | self.transform = transform
45 |
46 | def __call__(self, path, label):
47 | image = cv2.imread(path)
48 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
49 | if self.transform is not None:
50 | image = self.transform(image)
51 | return image, label
52 |
53 |
54 | class TFRecordSampleParser(object):
55 | """ Class for TFRecord Sample parser
56 | """
57 | def __init__(self, transform) -> None:
58 | self.transform = transform
59 | self.file_readers = dict()
60 |
61 | def __call__(self, record_path, offset, label):
62 | rr = self.file_readers.get(record_path, None)
63 | if rr is None:
64 | rr = db.RecordReader(record_path)
65 | self.file_readers[record_path] = rr
66 | pb_data = rr.read_record(offset)
67 | example = example_pb2.Example()
68 | example.ParseFromString(pb_data)
69 | image_raw = example.features.feature['image'].bytes_list.value[0]
70 | image = cv2.imdecode(np.frombuffer(image_raw, np.uint8), cv2.IMREAD_COLOR)
71 | image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
72 | if self.transform is not None:
73 | image = self.transform(image)
74 | return image, label
75 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/__init__.py:
--------------------------------------------------------------------------------
1 | def get_head(key, dist_fc):
2 | """ Get different classification head functions by key, support NormFace CosFace, ArcFace, CurricularFace.
3 | If distfc is True, the weight is splited equally into all gpus and calculated in parallel
4 | """
5 | if dist_fc:
6 | from .distfc.arcface import ArcFace
7 | from .distfc.cosface import CosFace
8 | from .distfc.curricularface import CurricularFace
9 | from .distfc.normface import NormFace
10 | from .distfc.partial_fc import PartialFC
11 | _head_dict = {
12 | 'CosFace': CosFace,
13 | 'ArcFace': ArcFace,
14 | 'CurricularFace': CurricularFace,
15 | 'NormFace': NormFace,
16 | 'PartialFC': PartialFC,
17 | }
18 | else:
19 | from .localfc.cosface import CosFace
20 | from .localfc.arcface import ArcFace
21 | from .localfc.curricularface import CurricularFace
22 | from .localfc.cifp import Cifp
23 | _head_dict = {
24 | 'CosFace': CosFace,
25 | 'ArcFace': ArcFace,
26 | 'CurricularFace': CurricularFace,
27 | 'Cifp': Cifp,
28 | }
29 | if key in _head_dict.keys():
30 | return _head_dict[key]
31 | else:
32 | raise KeyError("not support head {}".format(key))
33 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/distfc/arcface.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | from torchkit.util.utils import l2_norm
4 | from .common import CommonFace
5 |
6 |
7 | class ArcFace(CommonFace):
8 | """ Implement of ArcFace (https://arxiv.org/pdf/1801.07698v1.pdf)
9 | """
10 | def __init__(self,
11 | in_features,
12 | gpu_index,
13 | weight_init,
14 | class_split,
15 | scale=64.0,
16 | margin=0.5,
17 | easy_margin=False):
18 | """ Args:
19 | in_features: size of input features
20 | gpu_index: gpu worker index for model parallel
21 | weight_init: weight initial value
22 | class_split: class num shards
23 | scale: scale of input feature
24 | margin: margin
25 | """
26 | super(ArcFace, self).__init__(in_features, gpu_index, weight_init, class_split)
27 | self.scale = scale
28 | self.margin = margin
29 | self.easy_margin = easy_margin
30 | self.cos_m = math.cos(margin)
31 | self.sin_m = math.sin(margin)
32 | self.theta = math.cos(math.pi - margin)
33 | self.sinmm = math.sin(math.pi - margin) * margin
34 |
35 | def forward(self, embeddings, labels):
36 | index, part_labels, cos_theta, original_logits = self._calc_logits(embeddings, labels)
37 | target_logit = cos_theta[index, part_labels[index].view(-1)]
38 |
39 | sin_theta = torch.sqrt(1.0 - torch.pow(target_logit, 2))
40 | cos_theta_m = target_logit * self.cos_m - sin_theta * self.sin_m # cos(target+margin)
41 | if self.easy_margin:
42 | final_target_logit = torch.where(target_logit > 0, cos_theta_m,
43 | target_logit)
44 | else:
45 | final_target_logit = torch.where(target_logit > self.theta,
46 | cos_theta_m,
47 | target_logit - self.sinmm)
48 |
49 | cos_theta[index, part_labels[index].view(-1)] = final_target_logit
50 | cos_theta = cos_theta * self.scale
51 |
52 | return cos_theta, part_labels, original_logits * self.scale
53 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/distfc/common.py:
--------------------------------------------------------------------------------
1 | from itertools import accumulate
2 | import logging
3 | import torch
4 | import torch.nn as nn
5 | from torch.nn import Parameter
6 | from torchkit.util.utils import l2_norm
7 |
8 |
9 | class CommonFace(nn.Module):
10 | """ CommonFace head
11 | """
12 | def __init__(self,
13 | in_features,
14 | gpu_index,
15 | weight_init,
16 | class_split):
17 | """ Args:
18 | in_features: size of input features
19 | gpu_index: gpu worker index for model parallel
20 | weight_init: weight initial value
21 | class_split: class num shards
22 | """
23 | super(CommonFace, self).__init__()
24 | self.in_features = in_features
25 | self.gpu_index = gpu_index
26 | self.out_features = class_split[gpu_index]
27 | self.shard_start = []
28 | self.shard_start.append(0)
29 | self.shard_start.extend(accumulate(class_split))
30 | logging.info('FC Start Point: {}'.format(self.shard_start))
31 |
32 | select_weight_init = weight_init[:, self.shard_start[self.gpu_index]:
33 | self.shard_start[self.gpu_index + 1]]
34 |
35 | self.kernel = Parameter(select_weight_init.clone())
36 |
37 | def _calc_logits(self, embeddings, labels):
38 | """ calculate original logits
39 | """
40 | embeddings = l2_norm(embeddings, axis=1)
41 | kernel_norm = l2_norm(self.kernel, axis=0)
42 | cos_theta = torch.mm(embeddings, kernel_norm)
43 | cos_theta = cos_theta.clamp(-1, 1)
44 | with torch.no_grad():
45 | original_logits = cos_theta.clone()
46 | labels = labels.view(-1, 1)
47 | part_labels = self._generate_part_labels(labels)
48 | index = torch.where(part_labels != -1)[0]
49 | return index, part_labels, cos_theta, original_logits
50 |
51 | def _generate_part_labels(self, labels):
52 | with torch.no_grad():
53 | part_labels = labels.clone()
54 | shad_start = self.shard_start[self.gpu_index]
55 | shad_end = self.shard_start[self.gpu_index + 1]
56 | label_mask = torch.ge(part_labels, shad_start) & torch.lt(part_labels, shad_end)
57 |
58 | part_labels[~label_mask] = -1
59 | part_labels[label_mask] -= shad_start
60 |
61 | return part_labels
62 |
63 | def forward(self, embeddings, labels):
64 | raise NotImplementedError()
65 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/distfc/cosface.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from torchkit.util.utils import l2_norm
3 | from .common import CommonFace
4 |
5 |
6 | class CosFace(CommonFace):
7 | """ Implement of CosFace (https://arxiv.org/abs/1801.09414)
8 | """
9 | def __init__(self,
10 | in_features,
11 | gpu_index,
12 | weight_init,
13 | class_split,
14 | scale=64.0,
15 | margin=0.4):
16 | """ Args:
17 | in_features: size of input features
18 | gpu_index: gpu worker index for model parallel
19 | weight_init: weight initial value
20 | class_split: class num shards
21 | scale: scale of input feature
22 | margin: margin
23 | """
24 | super(CosFace, self).__init__(in_features, gpu_index, weight_init, class_split)
25 |
26 | self.scale = scale
27 | self.margin = margin
28 |
29 | def forward(self, embeddings, labels):
30 | index, part_labels, cos_theta, original_logits = self._calc_logits(embeddings, labels)
31 | target_logit = cos_theta[index, part_labels[index].view(-1)]
32 |
33 | final_target_logit = target_logit - self.margin
34 |
35 | cos_theta[index, part_labels[index].view(-1)] = final_target_logit
36 | cos_theta = cos_theta * self.scale
37 |
38 | return cos_theta, part_labels, original_logits * self.scale
39 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/distfc/curricularface.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | from torch.distributed import ReduceOp
4 | from torchkit.util.utils import l2_norm
5 | from .common import CommonFace
6 |
7 |
8 | class CurricularFace(CommonFace):
9 | """ Implement of CurricularFace (https://arxiv.org/abs/2004.00288)
10 | """
11 | def __init__(self,
12 | in_features,
13 | gpu_index,
14 | weight_init,
15 | class_split,
16 | scale=64.0,
17 | margin=0.5,
18 | alpha=0.1):
19 | """ Args:
20 | in_features: size of input features
21 | gpu_index: gpu worker index for model parallel
22 | weight_init: weight initial value
23 | class_split: class num shards
24 | scale: scale of input feature
25 | margin: margin
26 | alpha: alpha
27 | """
28 | super(CurricularFace, self).__init__(in_features, gpu_index, weight_init, class_split)
29 | self.scale = scale
30 | self.margin = margin
31 | self.alpha = alpha
32 | self.cos_m = math.cos(margin)
33 | self.sin_m = math.sin(margin)
34 | self.theta = math.cos(math.pi - margin)
35 | self.sinmm = math.sin(math.pi - margin) * margin
36 | self.register_buffer('t', torch.zeros(1))
37 |
38 | def forward(self, embeddings, labels):
39 | index, part_labels, cos_theta, original_logits = self._calc_logits(embeddings, labels)
40 | target_logit = torch.zeros(embeddings.size(0), device=embeddings.device)
41 |
42 | target_logit[index] = cos_theta[index, part_labels[index].view(-1)]
43 |
44 | torch.distributed.all_reduce(target_logit, ReduceOp.SUM)
45 |
46 | sin_theta = torch.sqrt(1.0 - torch.pow(target_logit, 2))
47 | cos_theta_m = target_logit * self.cos_m - sin_theta * self.sin_m # cos(target+margin)
48 |
49 | hard_sample_mask = cos_theta > cos_theta_m.view(-1, 1)
50 | # print('hard_sample_mask', hard_sample_mask.size())
51 | hard_example = cos_theta[hard_sample_mask]
52 | final_target_logit = torch.where(target_logit > self.theta,
53 | cos_theta_m,
54 | target_logit - self.sinmm)
55 | with torch.no_grad():
56 | self.t = target_logit.mean() * self.alpha + (1 - self.alpha) * self.t
57 | cos_theta[hard_sample_mask] = hard_example * (self.t + hard_example)
58 | cos_theta[index, part_labels[index].view(-1)] = final_target_logit[index]
59 | cos_theta = cos_theta * self.scale
60 | return cos_theta, part_labels, original_logits * self.scale
61 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/distfc/normface.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from torchkit.util.utils import l2_norm
3 | from .common import CommonFace
4 |
5 |
6 | class NormFace(CommonFace):
7 | """ Implement of NormFace
8 | """
9 | def __init__(self,
10 | in_features,
11 | gpu_index,
12 | weight_init,
13 | class_split,
14 | scale=64.):
15 | """ Args:
16 | in_features: size of input features
17 | gpu_index: gpu worker index for model parallel
18 | weight_init: weight initial value
19 | class_split: class num shards
20 | scale: scale of input feature
21 | """
22 | super(NormFace).__init__(in_features, gpu_index, weight_init, class_split)
23 | self.scale = scale
24 |
25 | def forward(self, inputs, labels):
26 | inputs_norm = l2_norm(inputs, axis=1)
27 | kernel_norm = l2_norm(self.kernel, axis=0)
28 | cos_theta = torch.mm(inputs_norm, kernel_norm)
29 | cos_theta = cos_theta.clamp(-1, 1)
30 | output = cos_theta * self.scale
31 |
32 | return output
33 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/localfc/arcface.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from __future__ import division
3 | import math
4 | import torch
5 | import torch.nn as nn
6 | from torch.nn import Parameter
7 | from torchkit.head.localfc.common import calc_logits
8 |
9 |
10 | class ArcFace(nn.Module):
11 | """ Implement of ArcFace (https://arxiv.org/pdf/1801.07698v1.pdf)
12 | """
13 | def __init__(self,
14 | in_features,
15 | out_features,
16 | scale=64.0,
17 | margin=0.5,
18 | easy_margin=False):
19 | """ Args:
20 | in_features: size of input features
21 | out_features: size of output features
22 | scale: scale of input feature
23 | margin: margin
24 | """
25 | super(ArcFace, self).__init__()
26 | self.in_features = in_features
27 | self.out_features = out_features
28 |
29 | self.scale = scale
30 | self.margin = margin
31 |
32 | self.kernel = Parameter(torch.FloatTensor(in_features, out_features))
33 | # nn.init.xavier_uniform_(self.kernel)
34 | nn.init.normal_(self.kernel, std=0.01)
35 | # init.kaiming_uniform_(self.kernel, a=math.sqrt(5))
36 |
37 | self.easy_margin = easy_margin
38 | self.cos_m = math.cos(margin)
39 | self.sin_m = math.sin(margin)
40 | self.th = math.cos(math.pi - margin)
41 | self.mm = math.sin(math.pi - margin) * margin
42 |
43 | def forward(self, embeddings, labels):
44 | cos_theta, origin_cos = calc_logits(embeddings, self.kernel)
45 | target_logit = cos_theta[torch.arange(0, embeddings.size(0)), labels].view(-1, 1)
46 |
47 | sin_theta = torch.sqrt(1.0 - torch.pow(target_logit, 2))
48 | cos_theta_m = target_logit * self.cos_m - sin_theta * self.sin_m # cos(target+margin)
49 | if self.easy_margin:
50 | final_target_logit = torch.where(target_logit > 0, cos_theta_m, target_logit)
51 | else:
52 | final_target_logit = torch.where(target_logit > self.th, cos_theta_m, target_logit - self.mm)
53 |
54 | cos_theta.scatter_(1, labels.view(-1, 1).long(), final_target_logit)
55 | output = cos_theta * self.scale
56 |
57 | return output, origin_cos * self.scale
58 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/localfc/common.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from torchkit.util.utils import l2_norm
4 |
5 |
6 | def calc_logits(embeddings, kernel):
7 | """ calculate original logits
8 | """
9 | embeddings = l2_norm(embeddings, axis=1)
10 | kernel_norm = l2_norm(kernel, axis=0)
11 | cos_theta = torch.mm(embeddings, kernel_norm)
12 | cos_theta = cos_theta.clamp(-1, 1) # for numerical stability
13 | with torch.no_grad():
14 | origin_cos = cos_theta.clone()
15 | return cos_theta, origin_cos
16 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/localfc/cosface.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from __future__ import division
3 | import math
4 | import torch
5 | import torch.nn as nn
6 | from torch.nn import Parameter
7 | from torchkit.head.localfc.common import calc_logits
8 |
9 |
10 | class CosFace(nn.Module):
11 | """ Implement of CosFace (https://arxiv.org/abs/1801.09414)
12 |
13 | """
14 | def __init__(self,
15 | in_features,
16 | out_features,
17 | scale=64.0,
18 | margin=0.40):
19 | """ Args:
20 | in_features: size of each input features
21 | out_features: size of each output features
22 | scale: norm of input feature
23 | margin: margin
24 | """
25 | super(CosFace, self).__init__()
26 | self.in_features = in_features
27 | self.out_features = out_features
28 |
29 | self.scale = scale
30 | self.margin = margin
31 |
32 | self.kernel = Parameter(torch.FloatTensor(in_features, out_features))
33 | # nn.init.xavier_uniform_(self.kernel)
34 | nn.init.normal_(self.kernel, std=0.01)
35 | # init.kaiming_uniform_(self.kernel, a=math.sqrt(5))
36 |
37 | def forward(self, embeddings, labels):
38 | cos_theta, origin_cos = calc_logits(embeddings, self.kernel)
39 | target_logit = cos_theta[torch.arange(0, embeddings.size(0)), labels].view(-1, 1)
40 |
41 | final_target_logit = target_logit - self.margin
42 |
43 | cos_theta.scatter_(1, labels.view(-1, 1).long(), final_target_logit)
44 | output = cos_theta * self.scale
45 |
46 | return output, origin_cos * self.scale
47 |
--------------------------------------------------------------------------------
/recognition/torchkit/head/localfc/curricularface.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from __future__ import division
3 | import torch
4 | import torch.nn as nn
5 | from torch.nn import Parameter
6 | import math
7 | from torchkit.util.utils import l2_norm
8 | from torchkit.head.localfc.common import calc_logits
9 |
10 |
11 | class CurricularFace(nn.Module):
12 | """ Implement of CurricularFace (https://arxiv.org/abs/2004.00288)
13 | """
14 |
15 | def __init__(self,
16 | in_features,
17 | out_features,
18 | scale=64.0,
19 | margin=0.5,
20 | alpha=0.1):
21 | """ Args:
22 | in_features: size of each input features
23 | out_features: size of each output features
24 | scale: norm of input feature
25 | margin: margin
26 | """
27 | super(CurricularFace, self).__init__()
28 | self.in_features = in_features
29 | self.out_features = out_features
30 | self.margin = margin
31 | self.scale = scale
32 | self.alpha = alpha
33 | self.cos_m = math.cos(margin)
34 | self.sin_m = math.sin(margin)
35 | self.threshold = math.cos(math.pi - margin)
36 | self.mm = math.sin(math.pi - margin) * margin
37 | self.kernel = Parameter(torch.Tensor(in_features, out_features))
38 | self.register_buffer('t', torch.zeros(1))
39 | nn.init.normal_(self.kernel, std=0.01)
40 |
41 | def forward(self, embeddings, labels):
42 | cos_theta, origin_cos = calc_logits(embeddings, self.kernel)
43 | target_logit = cos_theta[torch.arange(0, embeddings.size(0)), labels].view(-1, 1)
44 |
45 | sin_theta = torch.sqrt(1.0 - torch.pow(target_logit, 2))
46 | cos_theta_m = target_logit * self.cos_m - sin_theta * self.sin_m # cos(target+margin)
47 | mask = cos_theta > cos_theta_m
48 | final_target_logit = torch.where(target_logit > self.threshold, cos_theta_m, target_logit - self.mm)
49 |
50 | hard_example = cos_theta[mask]
51 | with torch.no_grad():
52 | self.t = target_logit.mean() * self.alpha + (1 - self.alpha) * self.t
53 | cos_theta[mask] = hard_example * (self.t + hard_example)
54 | cos_theta.scatter_(1, labels.view(-1, 1).long(), final_target_logit)
55 | output = cos_theta * self.scale
56 |
57 | return output, origin_cos * self.scale
58 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/__init__.py:
--------------------------------------------------------------------------------
1 | from .checkpoint_hook import CheckpointHook
2 | from .log_hook import LogHook
3 | from .summary_hook import SummaryHook
4 | from .learning_rate_hook import LearningRateHook
5 |
6 | __all__ = [
7 | 'CheckpointHook',
8 | 'LogHook',
9 | 'SummaryHook',
10 | 'LearningRateHook',
11 | ]
12 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/base_hook.py:
--------------------------------------------------------------------------------
1 | class Hook(object):
2 | """ BaseClass for Hook
3 | """
4 | stages = ('before_run', 'before_train_epoch', 'before_train_iter',
5 | 'after_train_iter', 'after_train_epoch', 'after_run')
6 |
7 | def before_run(self, *args):
8 | pass
9 |
10 | def before_train_epoch(self, *args):
11 | pass
12 |
13 | def before_train_iter(self, *args):
14 | pass
15 |
16 | def after_train_iter(self, *args):
17 | pass
18 |
19 | def after_train_epoch(self, *args):
20 | pass
21 |
22 | def after_run(self, *args):
23 | pass
24 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/checkpoint_hook.py:
--------------------------------------------------------------------------------
1 | from .base_hook import Hook
2 |
3 |
4 | class CheckpointHook(Hook):
5 | """ CheckpointHook
6 | """
7 | def __init__(self, save_epochs):
8 | self.save_epochs = save_epochs
9 |
10 | def before_run(self, task):
11 | task.load_pretrain_model()
12 |
13 | def after_train_epoch(self, task, epoch):
14 | if epoch + 1 in self.save_epochs:
15 | task.save_ckpt(epoch + 1)
16 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/learning_rate_hook.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from bisect import bisect
3 | from .base_hook import Hook
4 |
5 |
6 | def set_optimizer_lr(optimizer, lr):
7 | if isinstance(optimizer, dict):
8 | backbone_opt, head_opts = optimizer['backbone'], optimizer['heads']
9 | for param_group in backbone_opt.param_groups:
10 | param_group['lr'] = lr
11 | for _, head_opt in head_opts.items():
12 | for param_group in head_opt.param_groups:
13 | param_group['lr'] = lr
14 | else:
15 | for param_group in optimizer.param_groups:
16 | param_group['lr'] = lr
17 |
18 |
19 | def warm_up_lr(step, warmup_step, init_lr, optimizer):
20 | """ Warm up learning rate when batch step below warmup steps
21 | """
22 |
23 | lr = step * init_lr / warmup_step
24 | if step % 500 == 0:
25 | logging.info("Current step {}, learning rate {}".format(step, lr))
26 |
27 | set_optimizer_lr(optimizer, lr)
28 |
29 |
30 | def adjust_lr(epoch, learning_rates, stages, optimizer):
31 | """ Decay the learning rate based on schedule
32 | """
33 |
34 | pos = bisect(stages, epoch)
35 | lr = learning_rates[pos]
36 | logging.info("Current epoch {}, learning rate {}".format(epoch + 1, lr))
37 |
38 | set_optimizer_lr(optimizer, lr)
39 |
40 |
41 | class LearningRateHook(Hook):
42 | """ LearningRate Hook, adjust learning rate in training
43 | """
44 | def __init__(self,
45 | learning_rates,
46 | stages,
47 | warmup_step):
48 | """ Create a ``LearningRateHook`` object
49 |
50 | Args:
51 | learning_rates: all learning rates value
52 | stages: learning rate adjust stages value
53 | warmup_step: step num of warmup
54 | """
55 |
56 | self.learning_rates = learning_rates
57 | self.stages = stages
58 | if len(self.learning_rates) != len(self.stages) + 1:
59 | raise RuntimeError("Learning_rates size should be one larger than stages size")
60 | self.init_lr = self.learning_rates[0]
61 | self.warmup_step = warmup_step
62 |
63 | def before_train_iter(self, task, step, epoch):
64 | global_step = epoch * task.step_per_epoch + step
65 | if self.warmup_step > 0 and global_step <= self.warmup_step:
66 | warm_up_lr(global_step, self.warmup_step, self.init_lr, task.opt)
67 |
68 | def before_train_epoch(self, task, epoch):
69 | adjust_lr(epoch, self.learning_rates, self.stages, task.opt)
70 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/log_hook.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from ..util import AverageMeter
3 | from .base_hook import Hook
4 |
5 |
6 | class LogHook(Hook):
7 | """ LogHook, print log info in training
8 | """
9 | def __init__(self, freq, rank):
10 | """ Create a LogHook object
11 |
12 | Args:
13 | freq: step interval
14 | rank: work rank in ddp
15 | """
16 |
17 | self.freq = freq
18 | self.rank = rank
19 |
20 | def before_train_epoch(self, task, epoch):
21 | task.log_buffer.clear()
22 |
23 | def after_train_iter(self, task, step, epoch):
24 | """ Print log info after every training step
25 | """
26 |
27 | if self.rank != 0:
28 | return
29 | if step == 0 or (step + 1) % self.freq == 0:
30 | time_cost = task.log_buffer['time_cost']
31 | logging.info("Epoch {} / {}, batch {} / {}, {:.4f} sec/batch".format(
32 | epoch + 1, task.epoch_num, step + 1, task.step_per_epoch, time_cost))
33 |
34 | log_str = " " * 25
35 | for k, v in task.log_buffer.items():
36 | if k == 'time_cost':
37 | continue
38 | if isinstance(v, list):
39 | s = ', '.join(['%.6f' % x.val for x in v])
40 | elif isinstance(v, AverageMeter):
41 | s = '%.6f' % v.val
42 | else:
43 | s = str(v)
44 | log_str += '%s = [%s] ' % (k, s)
45 | print(log_str)
46 |
--------------------------------------------------------------------------------
/recognition/torchkit/hooks/summary_hook.py:
--------------------------------------------------------------------------------
1 | from torch.utils.tensorboard.writer import SummaryWriter
2 | from ..util import AverageMeter
3 | from .base_hook import Hook
4 |
5 |
6 | class SummaryHook(Hook):
7 | """ SummaryHook, write tensorboard summery in training
8 | """
9 | def __init__(self, log_root, freq, rank):
10 | """ Create A SummaryHook object
11 |
12 | Args:
13 | log_root: tensorboard summary root path
14 | freq: step interval
15 | rank: gpu rank
16 | """
17 |
18 | self.writer = SummaryWriter(log_root) if rank == 0 else None
19 | self.freq = freq
20 |
21 | def after_train_iter(self, task, step, epoch):
22 | if self.writer is None:
23 | return
24 | if step == 0 or (step + 1) % self.freq == 0:
25 | global_step = step + epoch * task.step_per_epoch
26 | scalars = task.summary.get('scalars', {})
27 | for k, v in scalars.items():
28 | if isinstance(v, list):
29 | for i, x in enumerate(v):
30 | self.writer.add_scalar('%s_%d' % (k, i), x.val,
31 | global_step=global_step)
32 | elif isinstance(v, AverageMeter):
33 | self.writer.add_scalar(k, v.val, global_step=global_step)
34 |
35 | histograms = task.summary.get('histograms', {})
36 | for k, v in histograms.items():
37 | if isinstance(v, list):
38 | for i, x in enumerate(v):
39 | self.writer.add_histogram('%s_%d' % (k, i), x.val,
40 | global_step=global_step)
41 | elif isinstance(v, AverageMeter):
42 | self.writer.add_histogram(k, v.val, global_step=global_step)
43 |
44 | def after_run(self, *args):
45 | if self.writer:
46 | self.writer.close()
47 |
--------------------------------------------------------------------------------
/recognition/torchkit/loss/__init__.py:
--------------------------------------------------------------------------------
1 | from torch.nn import CrossEntropyLoss
2 | from .dist_softmax import DistCrossEntropy
3 | from .ddl import DDL
4 |
5 | _loss_dict = {
6 | 'Softmax': CrossEntropyLoss(),
7 | 'DistCrossEntropy': DistCrossEntropy(),
8 | 'DDL': DDL()
9 | }
10 |
11 |
12 | def get_loss(key):
13 | """ Get different training loss functions by key,
14 | support Softmax(distfc = False), DistCrossEntropy (distfc = True), and DDL.
15 | """
16 | if key in _loss_dict.keys():
17 | return _loss_dict[key]
18 | else:
19 | raise KeyError("not support loss {}".format(key))
20 |
--------------------------------------------------------------------------------
/recognition/torchkit/loss/dist_softmax.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.distributed as dist
4 | from torch.distributed import ReduceOp
5 |
6 |
7 | class DistCrossEntropyFunc(torch.autograd.Function):
8 | """ CrossEntropy loss is calculated in parallel, allreduce all logits into single gpu and calculate loss.
9 | Implemented of ArcFace (https://arxiv.org/pdf/1801.07698v1.pdf):
10 | """
11 | @staticmethod
12 | def forward(ctx, logit_part, part_labels):
13 | ctx.batch_size = logit_part.size(0)
14 | # for numerical stability
15 | logit_part_max, _ = torch.max(logit_part, dim=1, keepdim=True)
16 | dist.all_reduce(logit_part_max, ReduceOp.MAX)
17 | logit_part = logit_part - logit_part_max
18 |
19 | # get exp sum
20 | exp_logit = torch.exp(logit_part)
21 | exp_sum = torch.sum(exp_logit, dim=1, keepdim=True)
22 | torch.distributed.all_reduce(exp_sum, ReduceOp.SUM)
23 | log_exp_sum = torch.log(exp_sum)
24 | log_softmax = logit_part - log_exp_sum
25 | ctx.log_softmax = log_softmax
26 | index = torch.where(part_labels != -1)[0]
27 | label_mask = torch.zeros(index.size()[0],
28 | log_softmax.size()[1],
29 | device=log_softmax.device)
30 | label_mask.scatter_(1, part_labels[index], 1)
31 | ctx.label_mask = label_mask
32 | ctx.index = index
33 |
34 | loss = torch.zeros(log_softmax.size()[0], 1, device=log_softmax.device)
35 | loss[index] = log_softmax[index].gather(1, part_labels[index])
36 | torch.distributed.all_reduce(loss, ReduceOp.SUM)
37 | loss = -torch.mean(loss)
38 |
39 | return loss
40 |
41 | @staticmethod
42 | def backward(ctx, loss_grad):
43 | """
44 | Args:
45 | loss_grad (torch.Tensor): gradient backward by last layer
46 | Returns:
47 | gradients for each input in forward function
48 | `None` gradients for one-hot label
49 | """
50 | logit_grad = torch.exp(ctx.log_softmax)
51 | logit_grad[ctx.index] -= ctx.label_mask
52 | logit_grad = loss_grad.item() * logit_grad / ctx.batch_size
53 | return logit_grad, None
54 |
55 |
56 | class DistCrossEntropy(nn.Module):
57 | def __init__(self):
58 | super(DistCrossEntropy, self).__init__()
59 |
60 | def forward(self, logit_part, label_part):
61 | return DistCrossEntropyFunc.apply(logit_part, label_part)
62 |
--------------------------------------------------------------------------------
/recognition/torchkit/task/__init__.py:
--------------------------------------------------------------------------------
1 | from .base_task import BaseTask
2 |
3 | __all__ = ['BaseTask']
4 |
--------------------------------------------------------------------------------
/recognition/torchkit/tfrecord/protos/example.proto:
--------------------------------------------------------------------------------
1 | // Protocol messages for describing input data Examples for machine learning
2 | // model training or inference.
3 | syntax = "proto3";
4 |
5 | import "feature.proto";
6 | option cc_enable_arenas = true;
7 |
8 | package face;
9 |
10 | message Example {
11 | Features features = 1;
12 | };
13 |
14 | message SequenceExample {
15 | Features context = 1;
16 | FeatureLists feature_lists = 2;
17 | };
18 |
--------------------------------------------------------------------------------
/recognition/torchkit/tfrecord/protos/feature.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | option cc_enable_arenas = true;
3 |
4 | package face;
5 |
6 | // Containers to hold repeated fundamental values.
7 | message BytesList {
8 | repeated bytes value = 1;
9 | }
10 | message FloatList {
11 | repeated float value = 1 [packed = true];
12 | }
13 | message Int64List {
14 | repeated int64 value = 1 [packed = true];
15 | }
16 |
17 | // Containers for non-sequential data.
18 | message Feature {
19 | // Each feature can be exactly one kind.
20 | oneof kind {
21 | BytesList bytes_list = 1;
22 | FloatList float_list = 2;
23 | Int64List int64_list = 3;
24 | }
25 | };
26 |
27 | message Features {
28 | // Map from feature name to feature.
29 | map feature = 1;
30 | };
31 |
32 | message FeatureList {
33 | repeated Feature feature = 1;
34 | };
35 |
36 | message FeatureLists {
37 | // Map from feature name to feature list.
38 | map feature_list = 1;
39 | };
40 |
--------------------------------------------------------------------------------
/recognition/torchkit/util/__init__.py:
--------------------------------------------------------------------------------
1 | from .checkpoint import CkptLoader, CkptSaver
2 | from .utils import AverageMeter, Timer
3 | from .utils import separate_irse_bn_paras, separate_resnet_bn_paras
4 | from .utils import load_config, get_class_split
5 | from .utils import accuracy_dist, accuracy
6 | from .distributed_functions import AllGather
7 |
8 | __all__ = [
9 | 'CkptLoader',
10 | 'CkptSaver',
11 | 'AverageMeter',
12 | 'Timer',
13 | 'separate_irse_bn_paras',
14 | 'separate_resnet_bn_paras',
15 | 'load_config',
16 | 'get_class_split',
17 | 'accuracy_dist',
18 | 'accuracy',
19 | 'AllGather',
20 | ]
21 |
--------------------------------------------------------------------------------
/recognition/torchkit/util/distributed_functions.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.distributed as dist
3 | from torch.autograd import Function
4 | from torch.distributed import ReduceOp
5 |
6 |
7 | class AllGatherFunc(Function):
8 | """ AllGather op with gradient backward
9 | """
10 | @staticmethod
11 | def forward(ctx, tensor, world_size):
12 | gather_list = [torch.zeros_like(tensor) for _ in range(world_size)]
13 | dist.all_gather(gather_list, tensor)
14 | return tuple(gather_list)
15 |
16 | @staticmethod
17 | def backward(ctx, *grads):
18 | world_size = dist.get_world_size()
19 | rank = dist.get_rank()
20 | grad_list = list(grads)
21 | grad_out = torch.zeros_like(grad_list[rank], requires_grad=True)
22 | dist.reduce_scatter(grad_out, grad_list, op=ReduceOp.SUM)
23 | # Gradient correction for DistCrossEntropy
24 | grad_out = grad_out * world_size
25 | return (grad_out, None)
26 |
27 |
28 | AllGather = AllGatherFunc.apply
29 |
--------------------------------------------------------------------------------
/recognition/train.yaml:
--------------------------------------------------------------------------------
1 | SEED: 1337 # random seed for reproduce results
2 | DATA_ROOT: '/youtu-face/ata_root/remote_tfrecord' # the parent root where your train/val/test data are stored
3 | INDEX_ROOT: '/youtu-face/train_data/'
4 | DATASETS: # the dataset index name
5 | - name: TFR-deepglint_360k_new
6 | batch_size: 128
7 | weight: 1.0
8 | scale: 64
9 | margin: 0.4
10 |
11 | BACKBONE_RESUME: ""
12 | HEAD_RESUME: ""
13 | META_RESUME: ""
14 |
15 | # BACKBONE_NAME: 'EfficientNetB0'
16 | INPUT_SIZE: [112, 112] # support: [112, 112] and [224, 224]
17 | BACKBONE_NAME: 'IR_50'
18 | EMBEDDING_SIZE: 512 # feature dimension
19 |
20 | MODEL_ROOT: './ckpt/' # the root to buffer your checkpoints
21 | LOG_ROOT: './tensorboard' # the root to log your train/val status
22 |
23 | DIST_FC: true
24 | HEAD_NAME: "PartialFC" # support: ['ArcFace', 'CurricularFace', 'CosFace']
25 | LOSS_NAME: 'DistCrossEntropy' # support: ['DistCrossEntropy', 'Softmax']
26 |
27 | RGB_MEAN: [0.5, 0.5, 0.5] # for normalize inputs to [-1, 1]
28 | RGB_STD: [0.5, 0.5, 0.5]
29 |
30 | LRS: [0.1, 0.01, 0.001, 0.0001, 0.00001] # initial LR
31 | WARMUP_STEP: -1
32 | STAGES: [8, 12, 15, 18] # epoch stages to decay learning rate
33 |
34 | START_EPOCH: 0 # start epoch
35 | NUM_EPOCH: 20 # total epoch number
36 | SAVE_EPOCHS: [1, 15, 18, 20]
37 |
38 | WEIGHT_DECAY: 0.0005 # do not apply to batch_norm parameters
39 | MOMENTUM: 0.9
40 |
41 | WORLD_SIZE: 1
42 | RANK: 0
43 | LOCAL_RANK: 0
44 | DIST_BACKEND: 'nccl'
45 | DIST_URL: 'env://'
46 |
47 | NUM_WORKERS: 8
48 |
49 | AMP: true # fp16 for backbone
50 |
--------------------------------------------------------------------------------
/security/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/common/__init__.py
--------------------------------------------------------------------------------
/security/common/data/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - create_base_transforms (function):
4 | create base transforms for training, validation and testing
5 | - create_base_dataloader (function):
6 | create base dataloader for training, validation and testing
7 | """
8 | from .base_transform import create_base_transforms
9 | from .base_dataloader import create_base_dataloader
10 |
--------------------------------------------------------------------------------
/security/common/data/base_dataloader.py:
--------------------------------------------------------------------------------
1 | import torch.utils.data as data
2 |
3 |
4 | def create_base_dataloader(args, dataset, split):
5 | """Base data loader
6 |
7 | Args:
8 | args: Dataset config args
9 | split (string): Load "train", "val" or "test"
10 |
11 | Returns:
12 | [dataloader]: Corresponding Dataloader
13 | """
14 | sampler = None
15 | if args.distributed:
16 | sampler = data.distributed.DistributedSampler(dataset)
17 |
18 | shuffle = True if sampler is None and split == 'train' else False
19 | batch_size = getattr(args, split).batch_size
20 | num_workers = args.num_workers if 'num_workers' in args else 8
21 |
22 | dataloader = data.DataLoader(dataset,
23 | batch_size=batch_size,
24 | shuffle=shuffle,
25 | sampler=sampler,
26 | num_workers=num_workers,
27 | pin_memory=True,
28 | drop_last=True)
29 | return dataloader
30 |
--------------------------------------------------------------------------------
/security/common/data/base_transform.py:
--------------------------------------------------------------------------------
1 | import albumentations as alb
2 | from albumentations.pytorch.transforms import ToTensorV2
3 |
4 |
5 | def create_base_transforms(args, split='train'):
6 | """Base data transformation
7 |
8 | Args:
9 | args: Data transformation args
10 | split (str, optional): Defaults to 'train'.
11 |
12 | Returns:
13 | [transform]: Data transform
14 | """
15 | num_segments = args.num_segments if 'num_segments' in args else 1
16 | additional_targets = {}
17 | for i in range(1, num_segments):
18 | additional_targets[f'image{i}'] = 'image'
19 |
20 | if split == 'train':
21 | base_transform = alb.Compose([
22 | alb.HorizontalFlip(),
23 | alb.Resize(args.image_size, args.image_size),
24 | alb.Normalize(mean=args.mean, std=args.std),
25 | ToTensorV2(),
26 | ], additional_targets=additional_targets)
27 |
28 | elif split == 'val':
29 | base_transform = alb.Compose([
30 | alb.Resize(args.image_size, args.image_size),
31 | alb.Normalize(mean=args.mean, std=args.std),
32 | ToTensorV2(),
33 | ], additional_targets=additional_targets)
34 |
35 | elif split == 'test':
36 | base_transform = alb.Compose([
37 | alb.Resize(args.image_size, args.image_size),
38 | alb.Normalize(mean=args.mean, std=args.std),
39 | ToTensorV2(),
40 | ], additional_targets=additional_targets)
41 |
42 | return base_transform
43 |
--------------------------------------------------------------------------------
/security/common/losses/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the loss functions used in the project:
3 | - pytorch official losses
4 | - euclidean_loss
5 | """
6 | from torch.nn.modules.loss import *
7 | from .euclidean_loss import EuclideanLoss
8 |
--------------------------------------------------------------------------------
/security/common/losses/euclidean_loss.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 |
4 |
5 | class EuclideanLoss(nn.Module):
6 | '''Compute euclidean distance between two tensors
7 | '''
8 |
9 | def __init__(self, reduction=None):
10 | super(EuclideanLoss, self).__init__()
11 | self.reduction = reduction
12 |
13 | def forward(self, x, y):
14 |
15 | n = x.size(0)
16 | m = n
17 | d = x.size(1)
18 | y = y.unsqueeze(0).expand(n, d)
19 |
20 | x = x.unsqueeze(1).expand(n, m, d)
21 | y = y.unsqueeze(0).expand(n, m, d)
22 |
23 | if self.reduction == 'mean':
24 | return torch.pow(x - y, 2).mean()
25 |
26 | elif self.reduction == 'sum':
27 | return torch.pow(x - y, 2).sum()
28 |
--------------------------------------------------------------------------------
/security/common/optimizers/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the optimizers used in the project:
3 | - pytorch official optimizers
4 | """
5 | from torch.optim import *
6 |
--------------------------------------------------------------------------------
/security/common/schedulers/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the schedulers used in the project:
3 | - timm schedulers
4 | """
5 | from timm.scheduler import *
6 |
--------------------------------------------------------------------------------
/security/common/task/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - BaseTask (class):
4 | base task for training, validation and testing
5 | """
6 | from .base_task import BaseTask
7 |
--------------------------------------------------------------------------------
/security/common/task/fas/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - test_module (class):
4 | function for validation and testing
5 | """
6 | from .modules import test_module
7 |
--------------------------------------------------------------------------------
/security/common/task/fas/modules.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import numpy as np
4 | from tqdm import tqdm
5 |
6 | import torch
7 |
8 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..'))
9 | from common.utils import *
10 |
11 |
12 | def test_module(model, test_data_loaders, forward_function, device='cuda', distributed=False):
13 | """Test module for Face Anti-spoofing
14 |
15 | Args:
16 | model (nn.module): fas model
17 | test_data_loaders (torch.dataloader): list of test data loaders
18 | forward_function (function): model forward function
19 | device (str, optional): Defaults to 'cuda'.
20 | distributed (bool, optional): whether to use distributed training. Defaults to False.
21 |
22 | Returns:
23 | y_preds (list): predictions
24 | y_trues (list): ground truth labels
25 | """
26 | prob_dict = {}
27 | label_dict = {}
28 |
29 | y_preds = []
30 | y_trues = []
31 |
32 | model.eval()
33 | for loaders in test_data_loaders:
34 | for iter, datas in enumerate(tqdm(loaders)):
35 | with torch.no_grad():
36 | images = datas[0].to(device)
37 | targets = datas[1].to(device)
38 | map_GT = datas[2].to(device)
39 | img_path = datas[3]
40 | probs = forward_function(images)
41 |
42 | if not distributed:
43 | probs = probs.cpu().data.numpy()
44 | label = targets.cpu().data.numpy()
45 |
46 | for i in range(len(probs)):
47 | # the image of the same video share the same video_path
48 | video_path = img_path[i].rsplit('/', 1)[0]
49 | if (video_path in prob_dict.keys()):
50 | prob_dict[video_path].append(probs[i])
51 | label_dict[video_path].append(label[i])
52 | else:
53 | prob_dict[video_path] = []
54 | label_dict[video_path] = []
55 | prob_dict[video_path].append(probs[i])
56 | label_dict[video_path].append(label[i])
57 | else:
58 | y_preds.extend(probs)
59 | y_trues.extend(targets)
60 |
61 | if not distributed:
62 | y_preds = []
63 | y_trues = []
64 | for key in prob_dict.keys():
65 | # calculate the scores in video-level via averaging the scores of the images from the same videos
66 | avg_single_video_prob = sum(prob_dict[key]) / len(prob_dict[key])
67 | avg_single_video_label = sum(label_dict[key]) / len(label_dict[key])
68 | y_preds = np.append(y_preds, avg_single_video_prob)
69 | y_trues = np.append(y_trues, avg_single_video_label)
70 |
71 | return y_preds, y_trues
72 |
--------------------------------------------------------------------------------
/security/common/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - parameters: (deprecated)
4 | - cli_utils:
5 | client parameters utils
6 | - logger_utils:
7 | logger utils
8 | - distribute_utils:
9 | tensor reduce and gather utils in distributed training
10 | - face_utils:
11 | face crop functions
12 | - misc:
13 | training misc utils
14 | - meters:
15 | training meters
16 | - metrics:
17 | calculate metrics
18 | - model_init:
19 | model weight initialization functions
20 | """
21 | from .parameters import get_parameters
22 | from .cli_utils import get_params
23 | from .logger_utils import get_logger
24 | from .distribute_utils import reduce_tensor, gather_tensor
25 | from .face_utils import add_face_margin, get_face_box
26 | from .misc import set_seed, setup, init_exam_dir, init_wandb_workspace, save_test_results
27 | from .meters import AverageMeter, ProgressMeter
28 | from .metrics import find_best_threshold, cal_metrics
29 | from .model_init import *
30 |
--------------------------------------------------------------------------------
/security/common/utils/distribute_utils.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.distributed as dist
3 |
4 |
5 | def reduce_tensor(tensor, mean=True):
6 | """Reduce tensor in the distributed settting.
7 |
8 | Args:
9 | tensor (torch.tensor):
10 | Input torch tensor to reduce.
11 | mean (bool, optional):
12 | Whether to apply mean. Defaults to True.
13 |
14 | Returns:
15 | [torch.tensor]: Returned reduced torch tensor or.
16 | """
17 | rt = tensor.clone() # The function operates in-place.
18 | dist.all_reduce(rt, op=dist.ReduceOp.SUM)
19 | if mean:
20 | rt /= dist.get_world_size()
21 | return rt
22 |
23 |
24 | def gather_tensor(inp, world_size=None, dist_=True, to_numpy=False):
25 | """Gather tensor in the distributed setting.
26 |
27 | Args:
28 | inp (torch.tensor):
29 | Input torch tensor to gather.
30 | world_size (int, optional):
31 | Dist world size. Defaults to None. If None, world_size = dist.get_world_size().
32 | dist_ (bool, optional):
33 | Whether to use all_gather method to gather all the tensors. Defaults to True.
34 | to_numpy (bool, optional):
35 | Whether to return numpy array. Defaults to False.
36 |
37 | Returns:
38 | (torch.tensor || numpy.ndarray): Returned tensor or numpy array.
39 | """
40 | inp = torch.stack(inp)
41 | if dist_:
42 | if world_size is None:
43 | world_size = dist.get_world_size()
44 | gather_inp = [torch.ones_like(inp) for _ in range(world_size)]
45 | dist.all_gather(gather_inp, inp)
46 | gather_inp = torch.cat(gather_inp)
47 | else:
48 | gather_inp = inp
49 |
50 | if to_numpy:
51 | gather_inp = gather_inp.cpu().numpy()
52 |
53 | return gather_inp
54 |
--------------------------------------------------------------------------------
/security/common/utils/face_utils.py:
--------------------------------------------------------------------------------
1 | """Crop face from image via landmarks
2 | """
3 |
4 |
5 | def add_face_margin(x, y, w, h, margin=0.5):
6 | """Add marigin to face bounding box
7 | """
8 | x_marign = int(w * margin / 2)
9 | y_marign = int(h * margin / 2)
10 |
11 | x1 = x - x_marign
12 | x2 = x + w + x_marign
13 | y1 = y - y_marign
14 | y2 = y + h + y_marign
15 |
16 | return x1, x2, y1, y2
17 |
18 |
19 | def get_face_box(img, landmarks, margin):
20 | """Get faca bounding box from landmarks
21 |
22 | Args:
23 | img (np.array): input image
24 | landmarks (np.array): face landmarks
25 | margin (float): margin for face box
26 |
27 | Returns:
28 | list: face bouding box
29 | """
30 | # load the positions of five landmarks
31 | x_list = [
32 | int(float(landmarks[6])),
33 | int(float(landmarks[8])),
34 | int(float(landmarks[10])),
35 | int(float(landmarks[12])),
36 | int(float(landmarks[14]))
37 | ]
38 | y_list = [
39 | int(float(landmarks[7])),
40 | int(float(landmarks[9])),
41 | int(float(landmarks[11])),
42 | int(float(landmarks[13])),
43 | int(float(landmarks[15]))
44 | ]
45 |
46 | x, y = min(x_list), min(y_list)
47 | w, h = max(x_list) - x, max(y_list) - y
48 |
49 | side = w if w > h else h
50 |
51 | # add margin
52 | x1, x2, y1, y2 = add_face_margin(x, y, side, side, margin)
53 | max_h, max_w = img.shape[:2]
54 | x1 = max(0, x1)
55 | y1 = max(0, y1)
56 | x2 = min(max_w, x2)
57 | y2 = min(max_h, y2)
58 |
59 | return x1, x2, y1, y2
60 |
--------------------------------------------------------------------------------
/security/common/utils/meters.py:
--------------------------------------------------------------------------------
1 | class AverageMeter(object):
2 | '''
3 | The Class AverageMeter record the metrics during the training process
4 | Examples:
5 | >>> acces = AverageMeter('_Acc', ':.5f')
6 | >>> acc = (prediction == labels).float().mean()
7 | >>> acces.update(acc)
8 | '''
9 | def __init__(self, name='metric', fmt=':f'):
10 | self.name = name
11 | self.fmt = fmt
12 | self.reset()
13 |
14 | def reset(self):
15 | self.val = 0
16 | self.avg = 0
17 | self.sum = 0
18 | self.count = 0
19 |
20 | def update(self, val, n=1):
21 | self.val = val
22 | self.sum += val * n
23 | self.count += n
24 | self.avg = self.sum / self.count
25 |
26 | def __str__(self):
27 | fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
28 | return fmtstr.format(**self.__dict__)
29 |
30 |
31 | class ProgressMeter(object):
32 | '''
33 | The ProgressMeter to record all AverageMeter and print the results
34 | Examples:
35 | >>> acces = AverageMeter('_Acc', ':.5f')
36 | >>> progress = ProgressMeter(epoch_size, [acces])
37 | >>> progress.display(iterations)
38 | '''
39 | def __init__(self, num_batches, meters, prefix=""):
40 | self.batch_fmtstr = self._get_batch_fmtstr(num_batches)
41 | self.meters = meters
42 | self.prefix = prefix
43 |
44 | def display(self, batch):
45 | entries = [self.prefix + self.batch_fmtstr.format(batch)]
46 | entries += [str(meter) for meter in self.meters]
47 | # print('\t'.join(entries))
48 | return '\t'.join(entries)
49 |
50 | def _get_batch_fmtstr(self, num_batches):
51 | num_digits = len(str(num_batches // 1))
52 | fmt = '{:' + str(num_digits) + 'd}'
53 | return '[' + fmt + '/' + fmt.format(num_batches) + ']'
54 |
--------------------------------------------------------------------------------
/security/common/utils/metrics.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from easydict import EasyDict
3 | from sklearn.metrics import roc_curve, auc, confusion_matrix
4 | from scipy.optimize import brentq
5 | from scipy.interpolate import interp1d
6 |
7 |
8 | def find_best_threshold(y_trues, y_preds):
9 | '''
10 | This function is utilized to find the threshold corresponding to the best ACER
11 | Args:
12 | y_trues (list): the list of the ground-truth labels, which contains the int data
13 | y_preds (list): the list of the predicted results, which contains the float data
14 | '''
15 | print("Finding best threshold...")
16 | best_thre = 0.5
17 | best_metrics = None
18 | candidate_thres = list(np.unique(np.sort(y_preds)))
19 | for thre in candidate_thres:
20 | metrics = cal_metrics(y_trues, y_preds, threshold=thre)
21 | if best_metrics is None:
22 | best_metrics = metrics
23 | best_thre = thre
24 | elif metrics.ACER < best_metrics.ACER:
25 | best_metrics = metrics
26 | best_thre = thre
27 | print(f"Best threshold is {best_thre}")
28 | return best_thre, best_metrics
29 |
30 |
31 | def cal_metrics(y_trues, y_preds, threshold=0.5):
32 | '''
33 | This function is utilized to calculate the performance of the methods
34 | Args:
35 | y_trues (list): the list of the ground-truth labels, which contains the int data
36 | y_preds (list): the list of the predicted results, which contains the float data
37 | threshold (float, optional):
38 | 'best': calculate the best results
39 | 'auto': calculate the results corresponding to the thresholds of EER
40 | float: calculate the results of the specific thresholds
41 | '''
42 |
43 | metrics = EasyDict()
44 |
45 | fpr, tpr, thresholds = roc_curve(y_trues, y_preds)
46 | metrics.AUC = auc(fpr, tpr)
47 |
48 | metrics.EER = brentq(lambda x: 1. - x - interp1d(fpr, tpr)(x), 0., 1.)
49 | metrics.Thre = float(interp1d(fpr, thresholds)(metrics.EER))
50 |
51 | if threshold == 'best':
52 | _, best_metrics = find_best_threshold(y_trues, y_preds)
53 | return best_metrics
54 |
55 | elif threshold == 'auto':
56 | threshold = metrics.Thre
57 |
58 | prediction = (np.array(y_preds) > threshold).astype(int)
59 |
60 | res = confusion_matrix(y_trues, prediction, labels=[0, 1])
61 | TP, FN = res[0, :]
62 | FP, TN = res[1, :]
63 | metrics.ACC = (TP + TN) / len(y_trues)
64 |
65 | TP_rate = float(TP / (TP + FN))
66 | TN_rate = float(TN / (TN + FP))
67 |
68 | metrics.APCER = float(FP / (TN + FP))
69 | metrics.BPCER = float(FN / (FN + TP))
70 | metrics.ACER = (metrics.APCER + metrics.BPCER) / 2
71 |
72 | return metrics
73 |
--------------------------------------------------------------------------------
/security/common/utils/misc.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 | import wandb
4 | import shutil
5 | import time
6 | import datetime
7 | import warnings
8 | import torch
9 | import numpy as np
10 |
11 |
12 | def set_seed(SEED):
13 | """This function set the random seed for the training process
14 |
15 | Args:
16 | SEED (int): the random seed
17 | """
18 | if SEED:
19 | random.seed(SEED)
20 | np.random.seed(SEED)
21 | torch.manual_seed(SEED)
22 | torch.cuda.manual_seed(SEED)
23 | torch.cuda.manual_seed_all(SEED)
24 | torch.backends.cudnn.deterministic = True
25 |
26 |
27 | def setup(cfg):
28 | if getattr(cfg, 'torch_home', None):
29 | os.environ['TORCH_HOME'] = cfg.torch_home
30 | warnings.filterwarnings("ignore")
31 | seed = cfg.seed
32 | set_seed(seed)
33 |
34 |
35 | def init_exam_dir(cfg):
36 | if cfg.local_rank == 0:
37 | if not os.path.exists(cfg.exam_dir):
38 | os.makedirs(cfg.exam_dir)
39 | ckpt_dir = os.path.join(cfg.exam_dir, 'ckpt')
40 | if not os.path.exists(ckpt_dir):
41 | os.makedirs(ckpt_dir)
42 |
43 |
44 | def init_wandb_workspace(cfg):
45 | """This function initializes the wandb workspace
46 | """
47 | if cfg.wandb.name is None:
48 | cfg.wandb.name = cfg.config.split('/')[-1].replace('.yaml', '')
49 | wandb.init(**cfg.wandb)
50 | allow_val_change = False if cfg.wandb.resume is None else True
51 | wandb.config.update(cfg, allow_val_change)
52 | wandb.save(cfg.config)
53 | if cfg.debug or wandb.run.dir == '/tmp':
54 | cfg.exam_dir = 'wandb/debug'
55 | if os.path.exists(cfg.exam_dir):
56 | shutil.rmtree(cfg.exam_dir)
57 | os.makedirs(cfg.exam_dir, exist_ok=True)
58 | else:
59 | cfg.exam_dir = os.path.dirname(wandb.run.dir)
60 | os.makedirs(os.path.join(cfg.exam_dir, 'ckpts'), exist_ok=True)
61 | return cfg
62 |
63 |
64 | def save_test_results(img_paths, y_preds, y_trues, filename='results.log'):
65 | assert len(y_trues) == len(y_preds) == len(img_paths)
66 |
67 | with open(filename, 'w') as f:
68 | for i in range(len(img_paths)):
69 | print(img_paths[i], end=' ', file=f)
70 | print(y_preds[i], file=f)
71 | print(y_trues[i], end=' ', file=f)
72 |
--------------------------------------------------------------------------------
/security/common/utils/parameters.py:
--------------------------------------------------------------------------------
1 | import json
2 | import argparse
3 | from omegaconf import OmegaConf
4 |
5 |
6 | def get_parameters():
7 | """define the parameter for training
8 |
9 | Args:
10 | --config (string): the path of config files
11 | --distributed (int): train the model in the mode of DDP or Not, default: 1
12 | --local_rank (int): define the rank of this process
13 | --world_size (int): define the Number of GPU
14 | """
15 | parser = argparse.ArgumentParser()
16 | parser.add_argument('-c', '--config', type=str, default='configs/train.yaml')
17 | parser.add_argument('--distributed', type=int, default=1)
18 | parser.add_argument('--local_rank', type=int, default=0)
19 | parser.add_argument('--world_size', type=int, default=1)
20 | parser.add_argument('--sync-bn', action='store_true', default=False)
21 | parser.add_argument('--debug', action='store_true', default=False)
22 | args = parser.parse_args()
23 |
24 | _C = OmegaConf.load(args.config)
25 | _C.merge_with(vars(args))
26 |
27 | if _C.debug:
28 | _C.train.epochs = 2
29 |
30 | return _C
31 |
32 |
33 | if __name__ == '__main__':
34 | args = get_parameters()
35 | print(args)
36 |
--------------------------------------------------------------------------------
/security/doc/intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/doc/intro.png
--------------------------------------------------------------------------------
/security/doc/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/doc/logo.png
--------------------------------------------------------------------------------
/security/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/requirements.txt
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/configs/config.yaml:
--------------------------------------------------------------------------------
1 | checkpoint_dir: ''
2 | data_dir: './datasets/Datasets_Makeup'
3 | model_dir: './models_meta_tar_ir152'
4 | after_dir: 'after_aligned_600'
5 | before_dir: 'before_aligned_600'
6 | log_dir: 'log'
7 | lmk_name: 'landmark_aligned_600.pk'
8 | use_se: True
9 | pretrained: False
10 | train_model_name_list: ['irse50', 'facenet', 'mobile_face']
11 | val_model_name_list: ['ir152']
12 |
13 | lr: 0.001
14 | update_lr_m: 0.001
15 | epoch_steps: 300
16 | gpu: 0
17 | n_threads: 8
18 | batch_size: 4
19 | input_dim: 3
20 |
21 | resize_size: [420, 160]
22 | eye_area: [9, 10, 11, 19, 84, 29, 79, 28, 24, 73, 70, 75, 74, 13, 15, 14, 22]
23 | mean: [0.5, 0.5, 0.5]
24 | std: [0.5, 0.5, 0.5]
25 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/datasets/Datasets_Makeup/landmark_aligned_600.txt:
--------------------------------------------------------------------------------
1 | # Save your own landmarks as the pikle file!
2 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/asr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/asr.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/logo.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/physical_vis_pose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Adv-Makeup/doc/physical_vis_pose.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/models/__init__.py:
--------------------------------------------------------------------------------
1 | """MakeupAttack model definition
2 | """
3 | from .makeup_attack import MakeupAttack
4 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Adv-Makeup/train.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import sys
3 | import os
4 | import torchvision
5 | import argparse
6 | from omegaconf import OmegaConf
7 |
8 | from models.makeup_attack import *
9 | from datasets.dataset import *
10 |
11 | args = OmegaConf.load('./configs/config.yaml')
12 | device = torch.device('cuda:{}'.format(args.gpu)) if args.gpu >= 0 else torch.device('cpu')
13 |
14 |
15 | def model_save(model, ep, target_name):
16 | if (ep + 1) % 50 == 0:
17 | os.makedirs(os.path.join(args.model_dir, target_name.split('.')[0]), exist_ok=True)
18 | print('--- save the model @ ep %d ---' % (ep))
19 | # Save the params of encoder in the generator
20 | torch.save(model.enc.state_dict(),
21 | '%s/%05d_enc.pth' % (os.path.join(args.model_dir, target_name.split('.')[0]), ep))
22 | # Save the params of decoder in the generator
23 | torch.save(model.dec.state_dict(),
24 | '%s/%05d_dec.pth' % (os.path.join(args.model_dir, target_name.split('.')[0]), ep))
25 | # Save the params of discriminator
26 | torch.save(model.discr.state_dict(),
27 | '%s/%05d_discr.pth' % (os.path.join(args.model_dir, target_name.split('.')[0]), ep))
28 |
29 |
30 | def train_single_epoch(model, train_loader, target_name):
31 | for it, data in enumerate(train_loader):
32 | # Un-makeup eye area images (attacker's)
33 | before_img = data[0].to(device).detach()
34 | # Real-world makeup eye area images
35 | after_img = data[1].to(device).detach()
36 | # Un-makeup images' path
37 | before_path = data[2]
38 |
39 | # Update the eye makeup discriminator
40 | model.update_discr(before_img, after_img)
41 | # Update the eye makeup generator
42 | model.update_gen(before_img, before_path, target_name)
43 |
44 |
45 | def train(model, train_loader, target_name):
46 | for ep in range(args.epoch_steps):
47 | # Re-init all the params for each training epoch
48 | model.res_init(ep)
49 |
50 | # Update a single epoch
51 | train_single_epoch(model, train_loader, target_name)
52 |
53 | # Save the visualized images during training and output the training logs
54 | model.visualization(ep, len(train_loader))
55 |
56 | # Save model
57 | model_save(model, ep, target_name)
58 |
59 |
60 | def main():
61 | for target_name in os.listdir(args.data_dir + '/target_aligned_600'):
62 | print("Target: %s" % (target_name))
63 | # Initialize the Adv-Makeup, networks and optimizers
64 | model = MakeupAttack(args)
65 | # Initialize the data-loader
66 | dataset = dataset_makeup(args)
67 | train_loader = torch.utils.data.DataLoader(dataset, batch_size=args.batch_size, shuffle=True,
68 | num_workers=args.n_threads)
69 | train(model, train_loader, target_name)
70 |
71 | if __name__ == '__main__':
72 | main()
73 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/configs/config.yaml:
--------------------------------------------------------------------------------
1 | attack:
2 | gpu: 0
3 | outer_loops: 200
4 | alpha: 0.01
5 | eps: 0.15
6 | gamma: 0.1
7 | inner_loops: 5
8 |
9 | dataset:
10 | dataset_name: celebaHQ
11 | dataset_dir: datasets/celebaHQ-select-aligned
12 | dataset_txt: datasets/celebaHQ-select-aligned-label.txt
13 | input_size: [112, 112]
14 | mean: [0.5, 0.5, 0.5]
15 | std: [0.5, 0.5, 0.5]
16 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/datasets/FFHQ-select-aligned-label.txt:
--------------------------------------------------------------------------------
1 | aligned_00140.jpg
2 | aligned_00239.jpg
3 | aligned_01239.jpg
4 | aligned_02849.jpg
5 | aligned_02959.jpg
6 | aligned_03812.jpg
7 | aligned_03936.jpg
8 | aligned_04988.jpg
9 | aligned_05267.jpg
10 | aligned_05516.jpg
11 | aligned_05953.jpg
12 | aligned_07421.jpg
13 | aligned_07649.jpg
14 | aligned_10028.jpg
15 | aligned_10152.jpg
16 | aligned_10370.jpg
17 | aligned_10917.jpg
18 | aligned_11054.jpg
19 | aligned_11669.jpg
20 | aligned_11773.jpg
21 | aligned_11910.jpg
22 | aligned_13291.jpg
23 | aligned_13869.jpg
24 | aligned_14007.jpg
25 | aligned_14560.jpg
26 | aligned_14902.jpg
27 | aligned_16142.jpg
28 | aligned_16272.jpg
29 | aligned_17356.jpg
30 | aligned_18320.jpg
31 | aligned_20966.jpg
32 | aligned_21871.jpg
33 | aligned_22324.jpg
34 | aligned_23683.jpg
35 | aligned_24093.jpg
36 | aligned_25703.jpg
37 | aligned_25706.jpg
38 | aligned_25942.jpg
39 | aligned_26130.jpg
40 | aligned_27132.jpg
41 | aligned_28311.jpg
42 | aligned_29525.jpg
43 | aligned_29785.jpg
44 | aligned_30934.jpg
45 | aligned_31001.jpg
46 | aligned_31538.jpg
47 | aligned_32107.jpg
48 | aligned_32376.jpg
49 | aligned_32857.jpg
50 | aligned_32994.jpg
51 | aligned_33143.jpg
52 | aligned_33331.jpg
53 | aligned_33892.jpg
54 | aligned_34914.jpg
55 | aligned_35351.jpg
56 | aligned_35381.jpg
57 | aligned_37009.jpg
58 | aligned_37210.jpg
59 | aligned_37364.jpg
60 | aligned_37567.jpg
61 | aligned_37588.jpg
62 | aligned_37992.jpg
63 | aligned_38159.jpg
64 | aligned_38221.jpg
65 | aligned_39010.jpg
66 | aligned_39406.jpg
67 | aligned_39710.jpg
68 | aligned_40229.jpg
69 | aligned_41278.jpg
70 | aligned_41331.jpg
71 | aligned_41992.jpg
72 | aligned_42232.jpg
73 | aligned_42587.jpg
74 | aligned_42768.jpg
75 | aligned_42847.jpg
76 | aligned_43993.jpg
77 | aligned_44402.jpg
78 | aligned_44415.jpg
79 | aligned_44979.jpg
80 | aligned_45467.jpg
81 | aligned_47472.jpg
82 | aligned_47840.jpg
83 | aligned_48643.jpg
84 | aligned_49079.jpg
85 | aligned_51627.jpg
86 | aligned_52221.jpg
87 | aligned_52268.jpg
88 | aligned_53710.jpg
89 | aligned_54446.jpg
90 | aligned_54732.jpg
91 | aligned_54869.jpg
92 | aligned_55209.jpg
93 | aligned_56084.jpg
94 | aligned_56164.jpg
95 | aligned_56594.jpg
96 | aligned_57225.jpg
97 | aligned_57680.jpg
98 | aligned_57926.jpg
99 | aligned_58539.jpg
100 | aligned_58727.jpg
101 | aligned_60641.jpg
102 | aligned_60748.jpg
103 | aligned_62266.jpg
104 | aligned_62760.jpg
105 | aligned_63613.jpg
106 | aligned_64876.jpg
107 | aligned_65104.jpg
108 | aligned_65826.jpg
109 | aligned_69731.jpg
110 | aligned_69832.jpg
111 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/datasets/celebaHQ-select-aligned-label.txt:
--------------------------------------------------------------------------------
1 | aligned_000004.jpg
2 | aligned_000009.jpg
3 | aligned_000014.jpg
4 | aligned_000016.jpg
5 | aligned_000025.jpg
6 | aligned_000030.jpg
7 | aligned_000044.jpg
8 | aligned_000051.jpg
9 | aligned_000052.jpg
10 | aligned_000074.jpg
11 | aligned_012853.jpg
12 | aligned_087916.jpg
13 | aligned_100434.jpg
14 | aligned_105409.jpg
15 | aligned_127741.jpg
16 | aligned_195210.jpg
17 | aligned_199359.jpg
18 | aligned_003644.jpg
19 | aligned_008938.jpg
20 | aligned_009032.jpg
21 | aligned_019197.jpg
22 | aligned_020432.jpg
23 | aligned_024581.jpg
24 | aligned_025036.jpg
25 | aligned_031553.jpg
26 | aligned_032421.jpg
27 | aligned_036170.jpg
28 | aligned_044363.jpg
29 | aligned_053200.jpg
30 | aligned_057862.jpg
31 | aligned_063305.jpg
32 | aligned_078168.jpg
33 | aligned_085135.jpg
34 | aligned_088664.jpg
35 | aligned_094973.jpg
36 | aligned_105634.jpg
37 | aligned_111062.jpg
38 | aligned_111787.jpg
39 | aligned_131067.jpg
40 | aligned_136378.jpg
41 | aligned_144204.jpg
42 | aligned_147809.jpg
43 | aligned_151944.jpg
44 | aligned_152193.jpg
45 | aligned_154232.jpg
46 | aligned_159405.jpg
47 | aligned_163521.jpg
48 | aligned_167947.jpg
49 | aligned_186090.jpg
50 | aligned_186382.jpg
51 | aligned_186913.jpg
52 | aligned_187454.jpg
53 | aligned_192699.jpg
54 | aligned_193013.jpg
55 | aligned_195919.jpg
56 | aligned_196129.jpg
57 | aligned_200381.jpg
58 | aligned_201253.jpg
59 | aligned_000618.jpg
60 | aligned_001422.jpg
61 | aligned_001435.jpg
62 | aligned_002768.jpg
63 | aligned_004839.jpg
64 | aligned_005675.jpg
65 | aligned_006592.jpg
66 | aligned_007102.jpg
67 | aligned_008484.jpg
68 | aligned_012069.jpg
69 | aligned_012902.jpg
70 | aligned_014841.jpg
71 | aligned_015855.jpg
72 | aligned_015915.jpg
73 | aligned_016010.jpg
74 | aligned_016346.jpg
75 | aligned_017793.jpg
76 | aligned_017927.jpg
77 | aligned_019238.jpg
78 | aligned_019712.jpg
79 | aligned_023907.jpg
80 | aligned_025389.jpg
81 | aligned_025883.jpg
82 | aligned_025919.jpg
83 | aligned_026031.jpg
84 | aligned_027520.jpg
85 | aligned_027677.jpg
86 | aligned_027920.jpg
87 | aligned_028423.jpg
88 | aligned_029772.jpg
89 | aligned_030535.jpg
90 | aligned_030720.jpg
91 | aligned_031349.jpg
92 | aligned_032755.jpg
93 | aligned_033108.jpg
94 | aligned_033274.jpg
95 | aligned_036555.jpg
96 | aligned_038443.jpg
97 | aligned_038582.jpg
98 | aligned_038869.jpg
99 | aligned_039855.jpg
100 | aligned_039957.jpg
101 | aligned_041523.jpg
102 | aligned_042262.jpg
103 | aligned_043323.jpg
104 | aligned_044826.jpg
105 | aligned_046483.jpg
106 | aligned_047019.jpg
107 | aligned_047467.jpg
108 | aligned_048460.jpg
109 | aligned_048781.jpg
110 | aligned_049081.jpg
111 |
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/gradcam.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/gradcam.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/main_fig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/main_fig.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/table2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/table2.png
--------------------------------------------------------------------------------
/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/table3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Adv-Attack-Defense/Sibling-Attack/doc/table3.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/configs/ICM2O.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: DG_Dataset
7 | DG_Dataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list1_path: /path/to/Replay_Real_list
11 | train_neg_list1_path: /path/to/Replay_Fake_list
12 | train_pos_list2_path: /path/to/CASIA_Real_list
13 | train_neg_list2_path: /path/to/CASIA_Fake_list
14 | train_pos_list3_path: /path/to/MSU_Real_list
15 | train_neg_list3_path: /path/to/MSU_Fake_list
16 | val_pos_list_path: /path/to/Oulu_Real_list
17 | val_neg_list_path: /path/to/Oulu_Fake_list
18 | test_pos_list_path: /path/to/Oulu_Real_list
19 | test_neg_list_path: /path/to/Oulu_Fake_list
20 | img_mode: rgb_hsv
21 | depth_map: True
22 | depth_map_size: 32
23 | crop_face_from_5points: False
24 | margin: 2
25 | return_path: True
26 |
27 | transform:
28 | image_size: 256
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: Framework
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 | mid_ch: 384
40 | AdapNorm: True
41 | AdapNorm_attention_flag: 1layer
42 | model_initial: kaiming
43 |
44 | loss:
45 | name: CrossEntropyLoss
46 | weight: 1.0
47 | params:
48 | reduction: mean
49 |
50 | loss_2:
51 | name: MSELoss
52 | weight: 0.1
53 | params:
54 | reduction: mean
55 |
56 | loss_3:
57 | name: EuclideanLoss
58 | domain_weight: 0.001
59 | discri_weight: 0.001
60 | params:
61 | reduction: mean
62 |
63 | optimizer:
64 | name: optimizer_name
65 | params:
66 | lr: learning_rate
67 | weight_decay: weight_decay_rate
68 |
69 | scheduler:
70 | name: scheduler_name
71 | params:
72 | decay_t: decay_t
73 | decay_rate: decay_rate
74 |
75 | train:
76 | epochs: 50
77 | batch_size: batchsize
78 | print_interval: 10
79 | metasize: 2
80 | meta_step_size: 0.0001
81 |
82 | val:
83 | batch_size: batchsize
84 |
85 | test:
86 | batch_size: batchsize
87 | record_results: True
88 |
89 | wandb:
90 | project: ANRL
91 | group: ICMtoO
92 | mode: offline
93 | job_type:
94 | id:
95 | resume:
96 | save_code: True
97 | name:
98 | notes:
99 |
100 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/configs/IOM2C.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: DG_Dataset
7 | DG_Dataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list1_path: /path/to/Replay_Real_list
11 | train_neg_list1_path: /path/to/Replay_Fake_list
12 | train_pos_list2_path: /path/to/Oulu_Real_list
13 | train_neg_list2_path: /path/to/Oulu_Fake_list
14 | train_pos_list3_path: /path/to/MSU_Real_list
15 | train_neg_list3_path: /path/to/MSU_Fake_list
16 | val_pos_list_path: /path/to/CASIA_Real_list
17 | val_neg_list_path: /path/to/CASIA_Fake_list
18 | test_pos_list_path: /path/to/CASIA_Real_list
19 | test_neg_list_path: /path/to/CASIA_Fake_list
20 | img_mode: rgb_hsv
21 | depth_map: True
22 | depth_map_size: 32
23 | crop_face_from_5points: False
24 | margin: 2
25 | return_path: True
26 |
27 | transform:
28 | image_size: 256
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: Framework
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 | mid_ch: 384
40 | AdapNorm: True
41 | AdapNorm_attention_flag: 1layer
42 | model_initial: kaiming
43 |
44 | loss:
45 | name: CrossEntropyLoss
46 | weight: 1.0
47 | params:
48 | reduction: mean
49 |
50 | loss_2:
51 | name: MSELoss
52 | weight: 0.1
53 | params:
54 | reduction: mean
55 |
56 | loss_3:
57 | name: EuclideanLoss
58 | domain_weight: 0.001
59 | discri_weight: 0.001
60 | params:
61 | reduction: mean
62 |
63 | optimizer:
64 | name: optimizer_name
65 | params:
66 | lr: learning_rate
67 | weight_decay: weight_decay_rate
68 |
69 | scheduler:
70 | name: scheduler_name
71 | params:
72 | decay_t: decay_t
73 | decay_rate: decay_rate
74 |
75 | train:
76 | epochs: 50
77 | batch_size: batchsize
78 | print_interval: 10
79 | metasize: 2
80 | meta_step_size: 0.0001
81 |
82 | val:
83 | batch_size: batchsize
84 |
85 | test:
86 | batch_size: batchsize
87 | record_results: True
88 |
89 | wandb:
90 | project: ANRL
91 | group: IOMtoC
92 | mode: offline
93 | job_type:
94 | id:
95 | resume:
96 | save_code: True
97 | name:
98 | notes:
99 |
100 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/configs/OCI2M.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: DG_Dataset
7 | DG_Dataset:
8 | train_pos_list1_path: /path/to/Oulu_Real_list
9 | train_neg_list1_path: /path/to/Oulu_Fake_list
10 | train_pos_list2_path: /path/to/CASIA_Real_list
11 | train_neg_list2_path: /path/to/CASIA_Fake_list
12 | train_pos_list3_path: /path/to/Replay_Real_list
13 | train_neg_list3_path: /path/to/Replay_Fake_list
14 | val_pos_list_path: /path/to/MSU_Real_list
15 | val_neg_list_path: /path/to/MSU_Fake_list
16 | test_pos_list_path: /path/to/MSU_Real_list
17 | test_neg_list_path: /path/to/MSU_Fake_list
18 | img_mode: rgb_hsv
19 | depth_map: True
20 | depth_map_size: 32
21 | rcrop_face_from_5points: False
22 | margin: 2
23 | return_path: True
24 |
25 | transform:
26 | image_size: 256
27 | mean: [0.5, 0.5, 0.5]
28 | std: [0.5, 0.5, 0.5]
29 | num_segments: 2
30 |
31 | model:
32 | name: Framework
33 | ckpt_path:
34 | resume:
35 | params:
36 | in_ch: 6
37 | mid_ch: 384
38 | AdapNorm: True
39 | AdapNorm_attention_flag: 1layer
40 | model_initial: kaiming
41 |
42 | loss:
43 | name: CrossEntropyLoss
44 | weight: 1.0
45 | params:
46 | reduction: mean
47 |
48 | loss_2:
49 | name: MSELoss
50 | weight: 0.1
51 | params:
52 | reduction: mean
53 |
54 | loss_3:
55 | name: EuclideanLoss
56 | domain_weight: 0.001
57 | discri_weight: 0.001
58 | params:
59 | reduction: mean
60 |
61 | optimizer:
62 | name: optimizer_name
63 | params:
64 | lr: learning_rate
65 | weight_decay: weight_decay_rate
66 |
67 | scheduler:
68 | name: scheduler_name
69 | params:
70 | decay_t: decay_t
71 | decay_rate: decay_rate
72 |
73 | train:
74 | epochs: 50
75 | batch_size: batchsize
76 | print_interval: 10
77 | metasize: 2
78 | meta_step_size: 0.0001
79 |
80 | val:
81 | batch_size: batchsize
82 |
83 | test:
84 | batch_size: batchsize
85 | record_results: True
86 |
87 | wandb:
88 | project: ANRL
89 | group: OCI2M
90 | mode: offline
91 | job_type:
92 | id:
93 | resume:
94 | save_code: True
95 | name:
96 | notes:
97 |
98 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/configs/OCM2I.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: DG_Dataset
7 | DG_Dataset:
8 | train_pos_list1_path: /path/to/Oulu_Real_list
9 | train_neg_list1_path: /path/to/Oulu_Fake_list
10 | train_pos_list2_path: /path/to/CASIA_Real_list
11 | train_neg_list2_path: /path/to/CASIA_Fake_list
12 | train_pos_list3_path: /path/to/MSU_Real_list
13 | train_neg_list3_path: /path/to/MSU_Real_list
14 | val_pos_list_path: /path/to/Replay_Real_list
15 | val_neg_list_path: /path/to/Replay_Fake_list
16 | test_pos_list_path: /path/to/Replay_Real_list
17 | test_neg_list_path: /path/to/Replay_Fake_list
18 | img_mode: rgb_hsv
19 | depth_map: True
20 | depth_map_size: 32
21 | return_path: False
22 |
23 | transform:
24 | image_size: 256
25 | mean: [0.5, 0.5, 0.5]
26 | std: [0.5, 0.5, 0.5]
27 | num_segments: 2
28 |
29 | model:
30 | name: Framework
31 | ckpt_path:
32 | resume:
33 | params:
34 | in_ch: 6
35 | mid_ch: 384
36 | AdapNorm: True
37 | AdapNorm_attention_flag: 1layer
38 | model_initial: kaiming
39 |
40 | loss:
41 | name: CrossEntropyLoss
42 | weight: 1.0
43 | params:
44 | reduction: mean
45 |
46 | loss_2:
47 | name: MSELoss
48 | weight: 0.1
49 | params:
50 | reduction: mean
51 |
52 | loss_3:
53 | name: EuclideanLoss
54 | domain_weight: 0.001
55 | discri_weight: 0.001
56 | params:
57 | reduction: mean
58 |
59 | optimizer:
60 | name: optimizer_name
61 | params:
62 | lr: learning_rate
63 | weight_decay: weight_decay_rate
64 |
65 | scheduler:
66 | name: scheduler_name
67 | params:
68 | decay_t: decay_t
69 | decay_rate: decay_rate
70 |
71 | train:
72 | epochs: 50
73 | batch_size: batchsize
74 | print_interval: 10
75 | metasize: 2
76 | meta_step_size: 0.0001
77 |
78 | val:
79 | batch_size: batchsize
80 |
81 | test:
82 | batch_size: batchsize
83 | record_results: True
84 |
85 | wandb:
86 | project: ANRL
87 | group: OCMtoI
88 | mode: offline
89 | job_type:
90 | id:
91 | resume:
92 | save_code: True
93 | name:
94 | notes:
95 |
96 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | create_dataloader: defines the dataloader
3 | '''
4 | from .factory import create_dataloader
5 |
6 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/datasets/factory.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import albumentations as alb
4 |
5 | from .DG_dataset import DG_Dataset
6 |
7 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', '..', '..'))
8 | from common.data import create_base_transforms, create_base_dataloader
9 |
10 |
11 | def create_dg_data_transforms(args, split='train'):
12 | '''
13 | define the domain generalization transforms accoding to different parameters
14 | Args:
15 | args ([type]): contain the specific parmaters
16 | split (str, optinal):
17 | 'train': to generate the domain generalization transforms for training
18 | 'val': to generate the domain generalization transforms for validation
19 | 'test': to generaate the domain generalization transforms for testing
20 | '''
21 | base_transform = create_base_transforms(args, split=split)
22 | if split == 'train':
23 | dg_aug_transform = alb.Compose([
24 | alb.HueSaturationValue(p=0.1),
25 | ])
26 | data_transform = alb.Compose([*dg_aug_transform, *base_transform])
27 |
28 | else:
29 | data_transform = base_transform
30 |
31 | return data_transform
32 |
33 |
34 | def create_dataloader(args, split='train', category=None, print_info=False):
35 | kwargs = getattr(args.dataset, args.dataset.name)
36 | dg_transform = create_dg_data_transforms(args.transform, split=split)
37 | dg_dataset = eval(args.dataset.name)(transform=dg_transform,
38 | split=split,
39 | category=category,
40 | print_info=print_info,
41 | **kwargs)
42 | dg_dataloader = create_base_dataloader(args, dg_dataset, split=split)
43 | return dg_dataloader
44 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/doc/ROC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/ANRL/doc/ROC.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/doc/framework.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/ANRL/doc/framework.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/doc/results_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/ANRL/doc/results_table.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/doc/tSNE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/ANRL/doc/tSNE.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/models/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Module:
3 | FeatEmbedder: Classify the input features
4 | Shape:
5 | - Input: (B, 384, 32, 32)
6 | - Output: (B, 2)
7 | FeatExtractor: Extract the features from the input images
8 | Shape:
9 | - Input: (B, 6, 256, 256)
10 | - Ouput: (B, 384, 32, 32)
11 | DepthEstmator: generate the depth map for the input features
12 | Shape:
13 | - Input: (B, 382, 32, 32)
14 | - Ouput: (B, 1, 32, 32)
15 | Framork: Contain the above three modules
16 | Shape:
17 | - Input: (B, 6, 256, 256)
18 | - Output:
19 | (B, 2)
20 | (B, 1, 32, 32)
21 | (B, 384, 32, 32)
22 | '''
23 | from .framework import Framework, FeatEmbedder, FeatExtractor, DepthEstmator
24 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/ANRL/train.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | if [ $# -lt 3 ]; then
4 | echo "USAGE: sh ./train.sh GPU_NUM CONFIG_PATH PORT"
5 | exit -1
6 | fi
7 |
8 | python3 -u -m torch.distributed.launch --nproc_per_node=$1 --master_port $3 ./train.py -c $2
9 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/configs/Oulu_1.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: FASDataset
7 | FASDataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list_path: /path/to/Oulu_Protocol_1_train_pos_list
11 | train_neg_list_path: /path/to/Oulu_Protocol_1_train_neg_list
12 | val_pos_list_path: /path/to/Oulu_Protocol_1_val_pos_list
13 | val_neg_list_path: /path/to/Oulu_Protocol_1_val_neg_list
14 | test_pos_list_path: /path/to/Oulu_Protocol_1_test_pos_list
15 | test_neg_1_list_path: /path/to/Oulu_Protocol_1_test_neg_1_list
16 | test_neg_2_list_path: /path/to/Oulu_Protocol_1_test_neg_2_list
17 | img_mode: rgb_hsv
18 | reflection_map: True
19 | reflection_map_size: 32
20 | img_size: 255
21 | crop_face_from_5points: False
22 | margin: 2
23 | return_path: True
24 | permutation_path: /path/to/permutations_30.npy
25 | grid_size: 3
26 |
27 | transform:
28 | image_size: 255
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: DCN
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 |
40 | loss:
41 | name: MSELoss
42 | weight: 1.0
43 | params:
44 | reduction: mean
45 |
46 | loss_2:
47 | name: MSELoss
48 | weight: 0.1
49 | params:
50 | reduction: mean
51 |
52 | optimizer:
53 | name: optimizer_name
54 | params:
55 | lr: learning_rate
56 | weight_decay: weight_decay_rate
57 |
58 | scheduler:
59 | name: scheduler_name
60 | params:
61 | decay_t: decay_t
62 | decay_rate: decay_rate
63 |
64 | train:
65 | epochs: 50
66 | batch_size: batchsize
67 | print_interval: 10
68 |
69 | val:
70 | batch_size: batchsize
71 |
72 | test:
73 | batch_size: batchsize
74 | record_results: True
75 |
76 | wandb:
77 | project: DCN
78 | group: Oulu_1
79 | mode: offline
80 | job_type:
81 | id:
82 | resume:
83 | save_code: True
84 | name:
85 | notes:
86 |
87 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/configs/Oulu_2.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: FASDataset
7 | FASDataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list_path: /path/to/Oulu_Protocol_1_train_pos_list
11 | train_neg_list_path: /path/to/Oulu_Protocol_1_train_neg_list
12 | val_pos_list_path: /path/to/Oulu_Protocol_1_val_pos_list
13 | val_neg_list_path: /path/to/Oulu_Protocol_1_val_neg_list
14 | test_pos_list_path: /path/to/Oulu_Protocol_1_test_pos_list
15 | test_neg_1_list_path: /path/to/Oulu_Protocol_1_test_neg_1_list
16 | test_neg_2_list_path: /path/to/Oulu_Protocol_1_test_neg_2_list
17 | img_mode: rgb_hsv
18 | reflection_map: True
19 | reflection_map_size: 32
20 | img_size: 255
21 | crop_face_from_5points: False
22 | margin: 2
23 | return_path: True
24 | permutation_path: /path/to/permutations_30.npy
25 | grid_size: 3
26 |
27 | transform:
28 | image_size: 255
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: DCN
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 |
40 | loss:
41 | name: MSELoss
42 | weight: 1.0
43 | params:
44 | reduction: mean
45 |
46 | loss_2:
47 | name: MSELoss
48 | weight: 0.1
49 | params:
50 | reduction: mean
51 |
52 | optimizer:
53 | name: optimizer_name
54 | params:
55 | lr: learning_rate
56 | weight_decay: weight_decay_rate
57 |
58 | scheduler:
59 | name: scheduler_name
60 | params:
61 | decay_t: decay_t
62 | decay_rate: decay_rate
63 |
64 | train:
65 | epochs: 50
66 | batch_size: batchsize
67 | print_interval: 10
68 |
69 | val:
70 | batch_size: batchsize
71 |
72 | test:
73 | batch_size: batchsize
74 | record_results: True
75 |
76 | wandb:
77 | project: DCN
78 | group: Oulu_2
79 | mode: offline
80 | job_type:
81 | id:
82 | resume:
83 | save_code: True
84 | name:
85 | notes:
86 |
87 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/configs/Oulu_3_1.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: FASDataset
7 | FASDataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list_path: /path/to/Oulu_Protocol_1_train_pos_list
11 | train_neg_list_path: /path/to/Oulu_Protocol_1_train_neg_list
12 | val_pos_list_path: /path/to/Oulu_Protocol_1_val_pos_list
13 | val_neg_list_path: /path/to/Oulu_Protocol_1_val_neg_list
14 | test_pos_list_path: /path/to/Oulu_Protocol_1_test_pos_list
15 | test_neg_1_list_path: /path/to/Oulu_Protocol_1_test_neg_1_list
16 | test_neg_2_list_path: /path/to/Oulu_Protocol_1_test_neg_2_list
17 | img_mode: rgb_hsv
18 | reflection_map: True
19 | reflection_map_size: 32
20 | img_size: 255
21 | crop_face_from_5points: False
22 | margin: 2
23 | return_path: True
24 | permutation_path: /path/to/permutations_30.npy
25 | grid_size: 3
26 |
27 | transform:
28 | image_size: 255
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: DCN
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 |
40 | loss:
41 | name: MSELoss
42 | weight: 1.0
43 | params:
44 | reduction: mean
45 |
46 | loss_2:
47 | name: MSELoss
48 | weight: 0.1
49 | params:
50 | reduction: mean
51 |
52 | optimizer:
53 | name: optimizer_name
54 | params:
55 | lr: learning_rate
56 | weight_decay: weight_decay_rate
57 |
58 | scheduler:
59 | name: scheduler_name
60 | params:
61 | decay_t: decay_t
62 | decay_rate: decay_rate
63 |
64 | train:
65 | epochs: 50
66 | batch_size: batchsize
67 | print_interval: 10
68 |
69 | val:
70 | batch_size: batchsize
71 |
72 | test:
73 | batch_size: batchsize
74 | record_results: True
75 |
76 | wandb:
77 | project: DCN
78 | group: Oulu_3_1
79 | mode: offline
80 | job_type:
81 | id:
82 | resume:
83 | save_code: True
84 | name:
85 | notes:
86 |
87 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/configs/Oulu_4_1.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: /path/to/torch
3 | num_workers: 10
4 |
5 | dataset:
6 | name: FASDataset
7 | FASDataset:
8 | use_LMDB: False
9 | LMDB_root: /path/to/lmdb_database
10 | train_pos_list_path: /path/to/Oulu_Protocol_1_train_pos_list
11 | train_neg_list_path: /path/to/Oulu_Protocol_1_train_neg_list
12 | val_pos_list_path: /path/to/Oulu_Protocol_1_val_pos_list
13 | val_neg_list_path: /path/to/Oulu_Protocol_1_val_neg_list
14 | test_pos_list_path: /path/to/Oulu_Protocol_1_test_pos_list
15 | test_neg_1_list_path: /path/to/Oulu_Protocol_1_test_neg_1_list
16 | test_neg_2_list_path: /path/to/Oulu_Protocol_1_test_neg_2_list
17 | img_mode: rgb_hsv
18 | reflection_map: True
19 | reflection_map_size: 32
20 | img_size: 255
21 | crop_face_from_5points: False
22 | margin: 2
23 | return_path: True
24 | permutation_path: /path/to/permutations_30.npy
25 | grid_size: 3
26 |
27 | transform:
28 | image_size: 255
29 | mean: [0.5, 0.5, 0.5]
30 | std: [0.5, 0.5, 0.5]
31 | num_segments: 2
32 |
33 | model:
34 | name: DCN
35 | ckpt_path:
36 | resume:
37 | params:
38 | in_ch: 6
39 |
40 | loss:
41 | name: MSELoss
42 | weight: 1.0
43 | params:
44 | reduction: mean
45 |
46 | loss_2:
47 | name: MSELoss
48 | weight: 0.1
49 | params:
50 | reduction: mean
51 |
52 | optimizer:
53 | name: optimizer_name
54 | params:
55 | lr: learning_rate
56 | weight_decay: weight_decay_rate
57 |
58 | scheduler:
59 | name: scheduler_name
60 | params:
61 | decay_t: decay_t
62 | decay_rate: decay_rate
63 |
64 | train:
65 | epochs: 50
66 | batch_size: batchsize
67 | print_interval: 10
68 |
69 | val:
70 | batch_size: batchsize
71 |
72 | test:
73 | batch_size: batchsize
74 | record_results: True
75 |
76 | wandb:
77 | project: DCN
78 | group: Oulu_4_1
79 | mode: offline
80 | job_type:
81 | id:
82 | resume:
83 | save_code: True
84 | name:
85 | notes:
86 |
87 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | create_dataloader: defines the dataloader
3 | '''
4 | from .factory import create_dataloader
5 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/datasets/factory.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import albumentations as alb
4 |
5 | from .dataset import FASDataset
6 |
7 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', '..', '..'))
8 | from common.data import create_base_transforms, create_base_dataloader
9 |
10 |
11 | def create_data_transforms(args, split='train'):
12 | '''
13 | define the transforms accoding to different parameters
14 | Args:
15 | args ([type]): contain the specific parmaters
16 | split (str, optinal):
17 | 'train': to generate the transforms for training
18 | 'val': to generate the transforms for validation
19 | 'test': to generaate the transforms for testing
20 | '''
21 | base_transform = create_base_transforms(args, split=split)
22 | if split == 'train':
23 | aug_transform = alb.Compose([
24 | alb.HueSaturationValue(p=0.1),
25 | ])
26 | data_transform = alb.Compose([*aug_transform, *base_transform])
27 |
28 | else:
29 | data_transform = base_transform
30 |
31 | return data_transform
32 |
33 |
34 | def create_dataloader(args, split='train', category=None, print_info=False):
35 | kwargs = getattr(args.dataset, args.dataset.name)
36 | transform = create_data_transforms(args.transform, split=split)
37 | dataset = eval(args.dataset.name)(transform=transform,
38 | split=split,
39 | category=category,
40 | print_info=print_info,
41 | **kwargs)
42 | dataloader = create_base_dataloader(args, dataset, split=split)
43 | return dataloader
44 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/datasets/permutations_30.npy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/DCN/datasets/permutations_30.npy
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/doc/TSNE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/DCN/doc/TSNE.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/doc/table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/DCN/doc/table.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/doc/table_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Anti-Spoofing/DCN/doc/table_2.png
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/models/__init__.py:
--------------------------------------------------------------------------------
1 | '''
2 | Module:
3 | DCN: contain the different modules
4 | Shape:
5 | - Input: (B, 6, 255, 255)
6 | - Ouput:
7 | (B, 1, 32, 32)
8 | (B, 9, 9)
9 | '''
10 | from .framework import DCN
11 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/models/base_block.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import numpy as np
4 |
5 | import torch
6 | from torch import nn
7 | import torch.nn.functional as F
8 |
9 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', '..', '..'))
10 | from common.utils.model_init import init_weights
11 |
12 |
13 | class Conv2dBlock(nn.Module):
14 | '''
15 | Args:
16 | First_block (bool):
17 | 'True': indicate the modules is the first block and the input channels is 64
18 | '''
19 |
20 | def __init__(self, First_block=False):
21 | super(Conv2dBlock, self).__init__()
22 | self.net = []
23 | if First_block:
24 | self.net += [nn.Conv2d(64, 128, 3, 1, 1, bias=True)]
25 | else:
26 | self.net += [nn.Conv2d(128, 128, 3, 1, 1, bias=True)]
27 |
28 | self.net += [nn.ELU()]
29 | self.net += [nn.BatchNorm2d(128)]
30 |
31 | self.net += [nn.Conv2d(128, 196, 3, 1, 1, bias=True)]
32 | self.net += [nn.ELU()]
33 | self.net += [nn.BatchNorm2d(196)]
34 |
35 | self.net += [nn.Conv2d(196, 128, 3, 1, 1, bias=True)]
36 | self.net += [nn.ELU()]
37 | self.net += [nn.BatchNorm2d(128)]
38 |
39 | self.net += [nn.AvgPool2d(2, stride=2)]
40 |
41 | self.net = nn.Sequential(*self.net)
42 | init_weights(self.net, init_type='kaiming')
43 |
44 | def forward(self, x):
45 | return self.net(x)
46 |
--------------------------------------------------------------------------------
/security/tasks/Face-Anti-Spoofing/DCN/train.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | if [ $# -lt 3 ]; then
4 | echo "USAGE: sh ./train.sh GPU_NUM CONFIG_PATH PORT"
5 | exit -1
6 | fi
7 |
8 | python3 -u -m torch.distributed.launch --nproc_per_node=$1 --master_port $3 ./train.py -c $2
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/configs/test.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: 'PATH TO YOUR TORCH HOME'
3 | froze: False
4 |
5 | dataset:
6 | name: 'celeb_df'
7 | pair: False
8 | ffpp:
9 | data_root: 'PATH TO YOUR DATA ROOT'
10 | data_types: ''
11 | compressions: 'c23'
12 | num_frames: 50
13 | methods:
14 | has_mask: False
15 | mask_size: 10
16 | pair: True
17 | alb: True
18 | celeb_df:
19 | data_root: 'PATH TO YOUR DATA ROOT'
20 | data_types: 'images_v1'
21 | num_frames: 25
22 | sample: 0
23 |
24 | wild_deepfake:
25 | root: 'PATH TO YOUR DATA ROOT'
26 |
27 |
28 |
29 |
30 | test:
31 | batch_size: 32
32 | threshold: 0.5
33 | record_results: True
34 |
35 |
36 |
37 | train:
38 | batch_size: 32
39 | threshold: 0.5
40 | record_results: True
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/configs/train.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home: 'PATH TO YOUR TORCH HOME'
3 | froze: False
4 | use_amp: False
5 | dataset:
6 | name: 'FaceForensics'
7 | moco: True
8 | FaceForensics:
9 | data_root: 'PATH TO YOUR DATA ROOT'
10 | data_types: ''
11 | compressions: 'c23'
12 | num_frames: 50
13 | methods:
14 | has_mask: True
15 | mask_size: 10
16 | pair: True
17 | corr_pair: False
18 | random_patch: 3
19 | srm: 0.5
20 | diff_frame: 0.5
21 | image_size: 299
22 | CelebDF:
23 | data_root: 'PATH TO YOUR DATA ROOT'
24 | data_types: ''
25 | num_frames: 25
26 | pair: True
27 | random_patch: 3
28 | sample: 0
29 |
30 |
31 | transform:
32 | image_size: 299
33 | mean: [0.5, 0.5, 0.5]
34 | std: [0.5, 0.5, 0.5]
35 |
36 |
37 | model:
38 | name: 'DCL'
39 | ckpt_path:
40 | params:
41 | dim: 100
42 | K: 32768
43 | m: 0.999
44 | T: 0.07
45 | threshold: 5
46 |
47 | base_model:
48 | name: BinaryClassifier
49 | params:
50 | encoder: 'tf_efficientnet_b4_ns'
51 | num_classes: 2
52 | drop_rate: 0.2
53 | has_feature: True
54 | pretrained: True
55 |
56 |
57 | loss:
58 | name: CrossEntropyLoss
59 | weight: 1.0
60 | params:
61 | reduction: mean
62 |
63 |
64 | optimizer:
65 | name: 'Adam'
66 | params:
67 | lr: 2.0e-4
68 | weight_decay: 1.0e-5
69 |
70 |
71 | scheduler:
72 | name: StepLRScheduler
73 | params:
74 | decay_t: 10
75 | decay_rate: 0.1
76 |
77 |
78 | train:
79 | warmup: 3
80 | epochs: 30
81 | batch_size: 32
82 | print_interval: 100
83 | val_interval: 1
84 |
85 |
86 | val:
87 | batch_size: 32
88 |
89 |
90 | test:
91 | batch_size: 32
92 | record_results: True
93 |
94 |
95 | wandb:
96 | project: Face-Forgery-Detection
97 | group: DCL
98 | save_code: True
99 | name:
100 | resume:
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - FaceForensics (class):
4 | dataset class for FaceForensics++ dataset.
5 | - CelebDF (class):
6 | dataset class for CelebDF dataset.
7 | - WildDeepfake (class):
8 | dataset class for WildDeepfake dataset.
9 | - create_dataloader (function):
10 | helper function to set up train, val and test dataloader
11 | """
12 | from .factory import create_dataloader
13 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/datasets/factory.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import albumentations as alb
4 |
5 | from .ffpp import FaceForensics
6 | from .celeb_df import CelebDF
7 | from .wild_deepfake import WildDeepfake
8 |
9 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', '..', '..'))
10 |
11 | from common.data import create_base_transforms, create_base_dataloader
12 |
13 |
14 | def create_data_transforms(args, split='train'):
15 | """Create data transofrms
16 |
17 | Args:
18 | args: data transforms configs
19 | split (str, optional): split for train, val or test. Defaults to 'train'.
20 |
21 | Returns:
22 | albumentation: pytorch data augmentations
23 | """
24 | base_transform = create_base_transforms(args, split=split)
25 |
26 | if split == 'train':
27 | aug_transform = alb.Compose([
28 | alb.Rotate(limit=30),
29 | alb.Cutout(1, 25, 25, p=0.1),
30 | alb.RandomResizedCrop(256, 256, scale=(0.5, 1.0), p=0.5),
31 | alb.Resize(args.image_size, args.image_size),
32 | alb.HorizontalFlip(),
33 | alb.ToGray(p=0.1),
34 | alb.GaussNoise(p=0.1),
35 | alb.OneOf([
36 | alb.RandomBrightnessContrast(),
37 | alb.FancyPCA(),
38 | alb.HueSaturationValue(),
39 | ], p=0.7),
40 | alb.GaussianBlur(blur_limit=3, p=0.05),
41 | ])
42 | data_transform = alb.Compose([*aug_transform, *base_transform])
43 |
44 | elif split == 'val':
45 | data_transform = base_transform
46 |
47 | elif split == 'test':
48 | data_transform = base_transform
49 |
50 | return data_transform
51 |
52 |
53 | def create_dataloader(args, split='train'):
54 | """create data loader
55 |
56 | Args:
57 | args: data loader configs
58 | split (str, optional): split for train, val or test. Defaults to 'train'.
59 |
60 | Returns:
61 | [type]: [description]
62 | """
63 | dataset_params = getattr(args.dataset, args.dataset.name)
64 | transform = create_data_transforms(args.transform, split=split)
65 | dataset = eval(args.dataset.name)(transform=transform, split=split, **dataset_params)
66 | dataloader = create_base_dataloader(args, dataset, split=split)
67 | return dataloader
68 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/datasets/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - data_structure (function):
4 | helper function to set up FaceForenisics++ dataset
5 | - SRM (function):
6 | SRM related weights and convolutional layers
7 | - RandomPatch (class):
8 | random patch data augmentation
9 | """
10 | from .data_structure import *
11 | from .srm import setup_srm_weights, setup_srm_layer
12 | from .random_patch import RandomPatch
13 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/doc/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Forgery-Detection/DCL/doc/main.png
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/models/__init__.py:
--------------------------------------------------------------------------------
1 | """model zoo"""
2 | from .base_net import BinaryClassifier
3 | from .dcl import DCL
4 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/models/base_net.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import timm
3 |
4 | import torch
5 | import torch.nn as nn
6 | import torch.nn.functional as F
7 |
8 | __all__ = ['BinaryClassifier']
9 |
10 | MODEL_DICTS = {}
11 | MODEL_DICTS.update(timm.models.__dict__)
12 |
13 |
14 | class BinaryClassifier(nn.Module):
15 | def __init__(self, encoder, num_classes=2, drop_rate=0.2, has_feature=False, pretrained=False, **kwargs) -> None:
16 | """Base binary classifier
17 | Args:
18 | encoder ([nn.Module]): Backbone of the DCL
19 | num_classes (int, optional): Defaults to 2.
20 | drop_rate (float, optional): Defaults to 0.2.
21 | has_feature (bool, optional): Wthether to return feature maps. Defaults to False.
22 | pretrained (bool, optional): Whether to use a pretrained model. Defaults to False.
23 | """
24 | super().__init__()
25 | self.encoder = MODEL_DICTS[encoder](pretrained=pretrained, **kwargs)
26 | for k, v in kwargs.items():
27 | setattr(self, k, v)
28 |
29 | if hasattr(self.encoder, 'get_classifier'):
30 | self.num_features = self.encoder.get_classifier().in_features
31 | else:
32 | self.num_features = self.encoder.last_channel
33 |
34 | self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
35 | self.dropout = nn.Dropout(drop_rate)
36 | self.has_feature = has_feature
37 | self.feature_squeeze = nn.Conv2d(self.num_features, 1, 1)
38 | self.fc = nn.Linear(self.num_features, num_classes)
39 |
40 | def forward(self, x):
41 | featuremap = self.encoder.forward_features(x)
42 | x = self.global_pool(featuremap).flatten(1)
43 | x = self.dropout(x)
44 | x = self.fc(x)
45 | if self.has_feature:
46 | return x, featuremap
47 | return x
48 |
49 |
50 | if __name__ == '__main__':
51 | name = "tf_efficientnet_b4_ns"
52 | device = 'cpu'
53 | model = BinaryClassifier(name)
54 | model = model.to(device)
55 | model.eval()
56 | with torch.no_grad():
57 | inputs = torch.rand(4, 3, 224, 224)
58 | inputs = inputs.to(device)
59 | out = model(inputs)
60 | print(out.shape)
61 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/DCL/requirements.txt:
--------------------------------------------------------------------------------
1 | albumentations==0.5.1
2 | easydict==1.9
3 | timm==0.4.12
4 | omegaconf==2.0.5
5 | nni==2.1
6 | tensorboardX==2.1
7 | wandb
8 | loguru
9 | rich
10 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/configs/ffpp.yaml:
--------------------------------------------------------------------------------
1 | seed: 1234
2 | torch_home:
3 |
4 | method: Deepfakes # should be one of ['Deepfakes', 'Face2Face', 'FaceSwap', 'NeuralTextures']
5 | compression: c23 # should be one of ['c23', 'c40']
6 | exam_dir: data_${method}_${compression}
7 |
8 | transform_params:
9 | image_size: 224
10 | mean: [0.485, 0.456, 0.406]
11 | std: [0.229, 0.224, 0.225]
12 |
13 | train:
14 | batch_size: 10
15 | num_workers: 8
16 | print_info_step_freq: 1
17 | max_epoches: 100
18 | use_warmup: True
19 | warmup_epochs: 1
20 | dataset:
21 | name: FFPP_Dataset
22 | params:
23 | root: data/ffpp_videos
24 | face_info_path: data/ffpp_face_rects.pkl
25 | method: ${method}
26 | compression: ${compression}
27 | split: train
28 | num_segments: 8
29 | sparse_span: 150
30 |
31 | test:
32 | batch_size: 10
33 | num_workers: 8
34 | dataset:
35 | name: FFPP_Dataset
36 | params:
37 | root: data/ffpp_videos
38 | face_info_path: data/ffpp_face_rects.pkl
39 | method: ${method}
40 | compression: ${compression}
41 | split: val
42 | num_segments: 16
43 | sparse_span: 150
44 |
45 | model:
46 | name: STIL_Model
47 | params:
48 | num_class: 2
49 | num_segment: 8
50 | resume:
51 | only_resume_model: False
52 | not_resume_layer_names:
53 |
54 | optimizer:
55 | name: Adam
56 | params:
57 | lr: 0.001
58 | weight_decay: 1.0e-5
59 |
60 | loss:
61 | name: CrossEntropyLoss
62 | params:
63 |
64 | scheduler:
65 | name: piecewise
66 | decay_epochs: [5, 20]
67 | decay_rates: [0.1, 0.05]
68 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains the following main classes/functions:
3 | - FFPP_Dataset (class):
4 | dataset class for ff++ dataset.
5 | - get_dataloader (function):
6 | helper function to set up train/test dataloader
7 | """
8 | from .video_dataset import *
9 | from .factory import *
10 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/datasets/factory.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | from omegaconf import OmegaConf
4 | import torch.utils.data as data
5 |
6 | sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', '..', '..', '..'))
7 | from common.data import create_base_transforms, create_base_dataloader
8 |
9 | from .video_dataset import *
10 |
11 |
12 | def get_dataloader(args, split):
13 | """Set dataloader.
14 |
15 | Args:
16 | args (object): Args load from get_params function.
17 | split (str): One of ['train', 'test']
18 | """
19 | transform = create_base_transforms(args.transform_params, split=split)
20 |
21 | dataset_cfg = getattr(args, split).dataset
22 | dataset_params = OmegaConf.to_container(dataset_cfg.params, resolve=True)
23 | dataset_params['transform'] = transform
24 |
25 | _dataset = eval(dataset_cfg.name)(**dataset_params)
26 |
27 | _dataloader = create_base_dataloader(args, _dataset, split=split)
28 |
29 | return _dataloader
30 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/doc/stil.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tencent/TFace/4939d8edc6c800ce7de1a0a1dcd1b7cf98c18031/security/tasks/Face-Forgery-Detection/STIL/doc/stil.jpg
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/models/__init__.py:
--------------------------------------------------------------------------------
1 | """STIL model definition
2 | """
3 | from .stil import STIL_Model
4 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/models/stil.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | import numpy as np
4 | import torch.nn as nn
5 | import torch.nn.functional as F
6 |
7 | try:
8 | from models.ops import *
9 | except:
10 | from ops import *
11 |
12 |
13 | class STIL_Model(nn.Module):
14 | def __init__(self,
15 | num_class=2,
16 | num_segment=8,
17 | add_softmax=False,
18 | **kwargs):
19 | """ Model Builder for STIL model.
20 | STIL: Spatiotemporal Inconsistency Learning for DeepFake Video Detection (https://arxiv.org/abs/2109.01860)
21 |
22 | Args:
23 | num_class (int, optional): Number of classes. Defaults to 2.
24 | num_segment (int, optional): Number of segments (frames) fed to the model. Defaults to 8.
25 | add_softmax (bool, optional): Whether to add softmax layer at the end. Defaults to False.
26 | """
27 | super().__init__()
28 |
29 | self.num_class = num_class
30 | self.num_segment = num_segment
31 |
32 | self.add_softmax = add_softmax
33 |
34 | self.build_model()
35 |
36 |
37 | def build_model(self):
38 | """
39 | Construct the model.
40 | """
41 | self.base_model = scnet50_v1d(self.num_segment, pretrained=True)
42 |
43 | fc_feature_dim = self.base_model.fc.in_features
44 | self.base_model.fc = nn.Linear(fc_feature_dim, self.num_class)
45 |
46 | if self.add_softmax:
47 | self.softmax_layer = nn.Softmax(dim=1)
48 |
49 |
50 | def forward(self, x):
51 | """Forward pass of the model.
52 |
53 | Args:
54 | x (torch.tensor): input tensor of shape (n, t*c, h, w). n is the batch_size, t is num_segment
55 | """
56 | # img channel default to 3
57 | img_channel = 3
58 |
59 | # x: [n, tc, h, w] -> [nt, c, h, w]
60 | # out: [nt, num_class]
61 | out = self.base_model(
62 | x.view((-1, img_channel) + x.size()[2:])
63 | )
64 |
65 | out = out.view(-1, self.num_segment, self.num_class) # [n, t, num_class]
66 | out = out.mean(1, keepdim=False) # [n, num_class]
67 |
68 | if self.add_softmax:
69 | out = self.softmax_layer(out)
70 |
71 | return out
72 |
73 |
74 | def set_segment(self, num_segment):
75 | """Change num_segment of the model.
76 | Useful when the train and test want to feed different number of frames.
77 |
78 | Args:
79 | num_segment (int): New number of segments.
80 | """
81 | self.num_segment = num_segment
82 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/test.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | # Manipulation method. One of ['Deepfakes', 'Face2Face', 'FaceSwap', 'NeuralTextures'].
4 | METHOD=Deepfakes
5 | # video compression rate. One of ['c23', 'c40'].
6 | COMPRESSION=c23
7 | # model path
8 | MODEL_PATH=xxx.tar
9 |
10 | python3 -m torch.distributed.launch --nproc_per_node=8 --master_port 12345 test.py -c configs/ffpp.yaml --method ${METHOD} --compression ${COMPRESSION} --model.resume ${MODEL_PATH}
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/train.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | # Manipulation method. One of ['Deepfakes', 'Face2Face', 'FaceSwap', 'NeuralTextures'].
4 | METHOD=Deepfakes
5 | # video compression rate. One of ['c23', 'c40'].
6 | COMPRESSION=c23
7 |
8 | python3 -m torch.distributed.launch --nproc_per_node=8 --master_port 12345 train.py -c configs/ffpp.yaml --method ${METHOD} --compression ${COMPRESSION}
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """Utils for STIL
2 | """
3 | from .lr_utils import lr_tuner
4 | from .metrics import compute_metrics
5 |
--------------------------------------------------------------------------------
/security/tasks/Face-Forgery-Detection/STIL/utils/metrics.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn.functional as F
3 |
4 |
5 | def compute_metrics(model_outputs, labels):
6 | """
7 | Compute the accuracy metrics.
8 | """
9 | real_probs = F.softmax(model_outputs, dim=1)[:, 0]
10 | bin_preds = (real_probs <= 0.5).int()
11 | bin_labels = (labels != 0).int()
12 |
13 | real_cnt = (bin_labels == 0).sum()
14 | fake_cnt = (bin_labels == 1).sum()
15 |
16 | acc = (bin_preds == bin_labels).float().mean()
17 |
18 | real_acc = (bin_preds == bin_labels)[torch.where(bin_labels == 0)].sum() / (real_cnt + 1e-12)
19 | fake_acc = (bin_preds == bin_labels)[torch.where(bin_labels == 1)].sum() / (fake_cnt + 1e-12)
20 |
21 | return acc.item(), real_acc.item(), fake_acc.item(), real_cnt.item(), fake_cnt.item()
22 |
--------------------------------------------------------------------------------