1
0
Fork 0

Compare commits

..

3 Commits

Author SHA1 Message Date
Petr Masopust e2adad4d9a EU AI Act 3 years ago
Petr Masopust 069ade5a0a Add random erasing 6 years ago
Petr Masopust 25ac925287 ResNext models 6 years ago
  1. 7
      README.md
  2. 7
      vectorizer/README.md
  3. 3
      vectorizer/ckpt/recongition3_37.pt
  4. 3
      vectorizer/ckpt/wider6_10.pt
  5. 14
      vectorizer/identification/dataloader.py
  6. 13
      vectorizer/identification/losses.py
  7. 13
      vectorizer/identification/train.py
  8. 38
      vectorizer/recognition/nets.py
  9. 8
      vectorizer/recognition/test.py
  10. 14
      vectorizer/recognition/train.py
  11. 4
      vectorizer/requirements.txt
  12. 2
      vectorizer/train-rec.sh
  13. 4
      vectorizer/vectorizer/server.py

@ -1,5 +1,11 @@
# Face recognition technology demo
## EU AI Act
Because [EU AI Act](https://artificialintelligenceact.eu/the-act/) I have to delete learned files. EU simply killed AI research like many others before because its over-regulation.
## Original preface
Mass faces identification and recognition in images.
**You must have git lfs installed before cloning this repo !**
@ -98,6 +104,7 @@ Papers:
* [ArcFace: Additive Angular Margin Loss for Deep Face Recognition](https://arxiv.org/abs/1801.07698)
* [SphereFace: Deep Hypersphere Embedding for Face Recognition](https://arxiv.org/abs/1704.08063)
* [CosFace: Large Margin Cosine Loss for Deep Face Recognition](https://arxiv.org/abs/1801.09414)
* [Random Erasing Data Augmentation](https://arxiv.org/abs/1708.04896)
## Licensing

@ -16,7 +16,7 @@ Set environment variables:
| --- | --- | --- |
| VS_PORT | 8080 | Port to listen (for Flask) |
| VS_FAN_MODEL | | Path to identification model |
| VS_REC_DEPTH | 50 | Recognition net depth |
| VS_REC_NET | resnet50 | Recognition net name |
| VS_REC_MODEL | | Path to recognition model |
Do not change configuration if you want run prepared docker-compose.
@ -112,7 +112,7 @@ python3 -m recognition.train \
--lfw_root ~/datasets/lfw \
--lfw_pair_list lfw_test_pair.txt \
--model_name recongition1 --batch_size 20 \
--loss adacos --print_freq 20 --depth 50
--loss adacos --print_freq 20 --net resnet50
```
| Argument | Description | Required / Default value |
@ -121,7 +121,7 @@ python3 -m recognition.train \
| --casia_root | Path to CASIA images | Yes |
| --lfw_root | Path to LFW dataset | Yes |
| --lfw_pair_list | Path to LFW pair list file (lfw_test_pair.txt - in this repository) | Yes |
| --depth | Resnet depth, must be one of 18, 34, 50, 101, 152 or 20 for sphere net | 50 |
| --net | Net name, must be one of resnet18, resnet34, resnet50, resnet101, resnet152, resnext50, resnext101 or spherenet | resnet50 |
| --epochs | Number of epochs | 50 |
| --batch_size | Batch size | 16 |
| --model_name | Model name prefix | Yes |
@ -154,6 +154,7 @@ Papers:
* [ArcFace: Additive Angular Margin Loss for Deep Face Recognition](https://arxiv.org/abs/1801.07698)
* [SphereFace: Deep Hypersphere Embedding for Face Recognition](https://arxiv.org/abs/1704.08063)
* [CosFace: Large Margin Cosine Loss for Deep Face Recognition](https://arxiv.org/abs/1801.09414)
* [Random Erasing Data Augmentation](https://arxiv.org/abs/1708.04896)
## Licensing

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:60c4b6850c06c00b086e0e3918e089f6bb181f0330a9ce1b60ac184e5b09c6e0
size 98498540

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d0c8b9095c6b85905e7236b253db4c445113ee5fccc272e558d65d52ab4c7523
size 155109396

@ -27,6 +27,7 @@ from torch.utils.data import Dataset
from torch.utils.data.sampler import Sampler
from PIL import Image, ImageEnhance, ImageFilter
from torchvision import transforms as T
class CSVDataset(Dataset):
@ -402,6 +403,19 @@ class Resizer(object):
return {'img': new_image, 'annot': annots, 'scale': scale}
class RandomEraser(object):
def __init__(self):
self.eraser = T.RandomErasing()
def __call__(self, sample):
image, annots, scales = sample['img'], sample['annot'], sample['scale']
image = self.eraser(image)
sample = {'img': image, 'annot': annots, 'scale': scales}
return sample
class Augmenter(object):
"""Convert ndarrays in sample to Tensors."""

@ -23,14 +23,11 @@ import torch.nn as nn
import torch.nn.functional as F
def memprint(a):
print(a.shape)
print(a.element_size() * a.nelement())
def calc_iou(a, b):
def calc_iou(a, b, is_cuda=False):
step = 20
IoU = torch.zeros((len(a), len(b))).cuda()
IoU = torch.zeros((len(a), len(b)))
if is_cuda:
IoU = IoU.cuda()
step_count = int(len(b) / step)
if len(b) % step != 0:
step_count += 1
@ -127,7 +124,7 @@ class FocalLoss(nn.Module):
classification = torch.clamp(classification, 1e-4, 1.0 - 1e-4)
IoU = calc_iou(anchor, bbox_annotation[:, :4]) # num_anchors x num_annotations
IoU = calc_iou(anchor, bbox_annotation[:, :4], self.is_cuda) # num_anchors x num_annotations
IoU_max, IoU_argmax = torch.max(IoU, dim=1) # num_anchors x 1

@ -32,7 +32,7 @@ from identification.model_level_attention import resnet18, resnet34, resnet50, r
from torch.utils.data import DataLoader
from identification.csv_eval import evaluate
from identification.dataloader import WIDERDataset, AspectRatioBasedSampler, collater, Resizer, Augmenter, Normalizer, \
CSVDataset
CSVDataset, RandomEraser
is_cuda = torch.cuda.is_available()
print('CUDA available: {}'.format(is_cuda))
@ -75,10 +75,10 @@ def main(args=None):
# Create the data loaders
if parser.wider_train is None:
dataset_train = CSVDataset(train_file=parser.csv_train, class_list=parser.csv_classes,
transform=transforms.Compose([Resizer(), Augmenter(), Normalizer()]))
transform=transforms.Compose([Resizer(), Augmenter(), Normalizer(), RandomEraser()]))
else:
dataset_train = WIDERDataset(train_file=parser.wider_train, img_prefix=parser.wider_train_prefix,
transform=transforms.Compose([Resizer(), Augmenter(), Normalizer()]))
transform=transforms.Compose([Resizer(), Augmenter(), Normalizer(), RandomEraser()]))
if parser.wider_val is None:
if parser.csv_val is None:
@ -175,9 +175,10 @@ def main(args=None):
img_data = img_data.cuda()
annot_data = annot_data.cuda()
print("GPU memory allocated: %d max memory allocated: %d memory cached: %d max memory cached: %d" % (
torch.cuda.memory_allocated() / 1024 ** 2, torch.cuda.max_memory_allocated() / 1024 ** 2,
torch.cuda.memory_cached() / 1024 ** 2, torch.cuda.max_memory_cached() / 1024 ** 2))
print("GPU memory allocated: %d max memory allocated: %d memory cached: %d max memory cached: %d" % (
torch.cuda.memory_allocated() / 1024 ** 2, torch.cuda.max_memory_allocated() / 1024 ** 2,
torch.cuda.memory_cached() / 1024 ** 2, torch.cuda.max_memory_cached() / 1024 ** 2))
classification_loss, regression_loss, mask_loss = retinanet([img_data, annot_data])
del img_data

@ -74,21 +74,43 @@ def sphere20():
return sphere20a()
def get_net_by_depth(depth):
if depth == 18:
def resnext50(pretrained=False, **kwargs):
"""Constructs a ResNext-50 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = models.resnext50_32x4d(num_classes=512, **kwargs)
return model
def resnext101(pretrained=False, **kwargs):
"""Constructs a ResNext-101 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = models.resnext101_32x8d(num_classes=512, **kwargs)
return model
def get_net_by_name(name):
if name == 'resnet18':
model = resnet18()
elif depth == 20:
elif name == 'spherenet':
model = sphere20()
elif depth == 34:
elif name == 'resnet34':
model = resnet34()
elif depth == 50:
elif name == 'resnet50':
model = resnet50()
elif depth == 101:
elif name == 'resnet101':
model = resnet101()
elif depth == 152:
elif name == 'resnet152':
model = resnet152()
elif name == 'resnext50':
model = resnext50()
elif name == 'resnext101':
model = resnext101()
else:
raise ValueError('Unsupported model depth %d, must be one of 18, 34, 50, 101, 152' % depth)
raise ValueError('Unsupported model %s, must be one of resnet18, resnet34, resnet50, resnet101, resnet152, spherenet, resnext50, resnext101' % name)
return model

@ -25,7 +25,7 @@ import argparse
from torch.utils.data import TensorDataset, DataLoader
from recognition.nets import get_net_by_depth
from recognition.nets import get_net_by_name
import torch
import numpy as np
from torch.nn import DataParallel
@ -135,8 +135,8 @@ def cal_accuracy(y_score, y_true):
def main(args=None):
parser = argparse.ArgumentParser(description='Testing script for face identification.')
parser.add_argument('--depth', help='Resnet depth, must be one of 18, 34, 50, 101, 152 or 20 for sphere', type=int,
default=50)
parser.add_argument('--net', help='Net name, must be one of resnet18, resnet34, resnet50 (default), resnet101, resnet152, resnext50, resnext101 or spherenet',
default='resnet50')
parser.add_argument('--parallel', help='Run training with DataParallel', dest='parallel',
default=False, action='store_true')
parser.add_argument('--model', help='Path to model')
@ -149,7 +149,7 @@ def main(args=None):
is_cuda = torch.cuda.is_available()
print('CUDA available: {}'.format(is_cuda))
model = get_net_by_depth(parser.depth)
model = get_net_by_name(parser.net)
if parser.parallel:
model = DataParallel(model)

@ -30,7 +30,7 @@ from torchvision import transforms as T
from recognition.angle import AngleLinear, CosFace, SphereFace, ArcFace, AdaCos
from recognition.focal_loss import FocalLoss
from recognition.nets import get_net_by_depth
from recognition.nets import get_net_by_name
from recognition.test import lfw_test2, get_pair_list, load_img_data
@ -50,7 +50,8 @@ class Dataset(torch.utils.data.Dataset):
T.RandomResizedCrop(imagesize),
T.RandomHorizontalFlip(),
T.ToTensor(),
normalize
normalize,
T.RandomErasing()
])
def __getitem__(self, index):
@ -81,8 +82,8 @@ def main(args=None):
parser.add_argument('--print_freq', help='Print every N batch (default 100)', type=int, default=100)
parser.add_argument('--epochs', help='Number of epochs', type=int, default=50)
parser.add_argument('--depth', help='Resnet depth, must be one of 18, 34, 50, 101, 152 or 20 for sphere', type=int,
default=50)
parser.add_argument('--net', help='Net name, must be one of resnet18, resnet34, resnet50, resnet101, resnet152, resnext50, resnext101 or spherenet',
default='resnet50')
parser.add_argument('--lr_step', help='Learning rate step (default 10)', type=int, default=10)
parser.add_argument('--lr', help='Learning rate (default 0.1)', type=float, default=0.1)
parser.add_argument('--weight_decay', help='Weight decay (default 0.0005)', type=float, default=0.0005)
@ -108,7 +109,7 @@ def main(args=None):
print('CUDA available: {}'.format(is_cuda))
imagesize = 224
model = get_net_by_depth(parser.depth)
model = get_net_by_name(parser.net)
# TODO split training dataset to train/validation and stop using test dataset for acc
train_dataset = Dataset(parser.casia_root, parser.casia_list, imagesize)
@ -172,8 +173,6 @@ def main(args=None):
start = time.time()
last_acc = 0.0
for i in range(parser.epochs):
scheduler.step()
model.train()
for ii, data in enumerate(trainloader):
data_input, label = data
@ -196,6 +195,7 @@ def main(args=None):
start = time.time()
scheduler.step()
model.eval()
acc = lfw_test2(model, identity_list, img_data, is_cuda=is_cuda)
print('Accuracy: %f' % acc)

@ -1,4 +1,4 @@
Flask
Pillow
https://download.pytorch.org/whl/cu100/torch-1.1.0-cp37-cp37m-linux_x86_64.whl
https://download.pytorch.org/whl/cu100/torchvision-0.3.0-cp37-cp37m-linux_x86_64.whl
torch
torchvision

@ -1,2 +1,2 @@
python3 -m recognition.train --casia_list ~/datasets/CASIA-maxpy-clean/train.txt --casia_root ~/datasets/CASIA-maxpy-clean --lfw_root ~/datasets/lfw \
--lfw_pair_list lfw_test_pair.txt --model_name recongition1 --batch_size 20 --loss adacos --print_freq 20 --depth 50
--lfw_pair_list lfw_test_pair.txt --model_name recongition1 --batch_size 20 --loss adacos --print_freq 20 --net resnet50

@ -24,7 +24,7 @@ from flask import Flask, request, abort, jsonify
from werkzeug.utils import secure_filename
import torch
from recognition.nets import get_net_by_depth
from recognition.nets import get_net_by_name
from torchvision import transforms as T
from PIL import Image
import identification.detector as fan
@ -38,7 +38,7 @@ if fan_file is None:
fan_model = fan.load_model(fan_file, is_cuda=is_cuda)
# load recognition model
rec_model = get_net_by_depth(int(os.environ.get('VS_REC_DEPTH', 50)))
rec_model = get_net_by_name(os.environ.get('VS_REC_NET', 'resnet50'))
rec_file = os.environ.get('VS_REC_MODEL', None)
if rec_file is None:
raise Exception('VS_REC_MODEL is mandatory parameter')

Loading…
Cancel
Save