Skip to content
Snippets Groups Projects
Commit e0648f5f authored by minmin_liu's avatar minmin_liu
Browse files

add new model lresnet100e_ir to model_zoo

parent a8276182
No related branches found
No related tags found
No related merge requests found
Showing
with 1685 additions and 0 deletions
目录
- [lresnet100e_ir描述](#lresnet100e_ir描述)
- [数据集](#数据集)
- [环境要求](#环境要求)
- [快速入门](#快速入门)
- [脚本说明](#脚本说明)
- [脚本和样例代码](#脚本和样例代码)
- [脚本参数](#脚本参数)
- [训练过程](#训练过程)
- [分布式训练](#分布式训练)
- [评估过程](#评估过程)
- [评估](#评估)
- [导出mindir模型](#导出mindir模型)
- [模型描述](#模型描述)
- [性能](#性能)
- [训练性能](#训练性能)
- [评估性能](#评估性能)
- [随机情况说明](#随机情况说明)
- [ModelZoo主页](#ModelZoo主页)
<!-- /TOC -->
# lresnet100e_ir描述
使用深度卷积神经网络进行大规模人脸识别的特征学习中的主要挑战之一是设计适当的损失函数以增强判别能力。继SoftmaxLoss、Center Loss、A-Softmax Loss、Cosine Margin Loss之后,Arcface在人脸识别中具有更加良好的表现。Arcface是传统softmax的改进, 将类之间的距离映射到超球面的间距,论文给出了对此的清晰几何解释。我们还研究了一种更先进的残差单元设置,提出网络LResNet100E-IR用于人脸识别模型的训练。
[论文](https://arxiv.org/pdf/1801.07698v1.pdf): Deng J, Guo J, Xue N, et al. Arcface: Additive angular margin loss for deep face recognition[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2019: 4690-4699.
# 数据集
使用的训练数据集:[MS1MV2](https://github.com/deepinsight/insightface/wiki/Dataset-Zoo)
验证数据集:lfw,cfp-fp,agedb_30
训练集:5,822,653张图片,85742个类
# 环境要求
- 硬件:GPU
- 使用GPU处理器来搭建硬件环境。
- 框架
- [MindSpore](https://www.mindspore.cn/install)
- 如需查看详情,请参见如下资源:
- [MindSpore教程](https://www.mindspore.cn/tutorials/zh-CN/master/index.html)
- [MindSpore Python API](https://www.mindspore.cn/docs/api/zh-CN/master/index.html)
# 快速入门
通过官方网站安装MindSpore后,您可以按照如下步骤进行训练和评估:
```python
# 分布式训练运行示例
bash scripts/run_distribute_train.sh [DATA_PATH] [DEVICE_NUM]
# 单机训练运行示例
bash scripts/run_standalone_train.sh [DATA_PATH]
# 运行评估示例
bash scripts/run_eval.sh [EVAL_PATH] [CKPT_PATH]
```
## 脚本说明
## 脚本和样例代码
```path
└── lresnet100e_ir
├── README.md // LResNet100E-IR相关描述
├── scripts
├── run_distribute_train.sh // 用于分布式训练的shell脚本
├── run_standalone_train.sh // 用于单机训练的shell脚本
└── run_eval.sh // 用于评估的shell脚本
├──src
├── config.py //参数配置
├── dataset.py // 创建数据集
├── iresnet.py // ResNet架构
├── eval.py // 测试脚本
├── train.py // 训练脚本
├── export.py
├── requirements.txt
```
## 脚本参数
模型训练和评估过程中使用的参数可以在lresnet100e_ir_config_gpu.yaml中设置:
```python
"num_classes": 85742,
"image_size": 112,
"batch_size": 128,
"epoch_size": 25,
"schedule": [10, 16, 21],
"gamma": 0.1,
"lr": 0.1,
"momentum": 0.9,
"weight_decay": 5e-4
```
## 训练过程
### 分布式训练
```shell
bash scripts/run_distribute_train.sh [DATA_PATH] [DEVICE_NUM]
```
上述shell脚本将在后台运行分布训练。可以通过`train.log`文件查看结果。
采用以下方式达到损失值:
```log
epoch: 7 step: 5686, loss is 4.730966
epoch time: 6156469.326 ms, per step time: 1082.742 ms
epoch: 8 step: 5686, loss is 4.426299
epoch time: 6156968.563 ms, per step time: 1082.830 ms
...
epoch: 24 step: 5686, loss is 3.6390548
epoch time: 6154867.063 ms, per step time: 1082.460 ms
epoch: 25 step: 5686, loss is 3.3598282
epoch time: 6153828.222 ms, per step time: 1082.277 ms
```
## 评估过程
### 评估
- 在GPU环境运行时评估lfw、cfp_fp、agedb_30数据集
在运行以下命令之前,请检查用于评估的检查点路径。请将检查点路径设置为绝对全路径,例如“username/LResNet100E-IR/LResNet100EIR_5-25_5686.ckpt”。
```bash
bash scripts/run_eval.sh [EVAL_PATH] [CKPT_PATH]
```
上述python命令将在后台运行,您可以通过eval.log文件查看结果。测试数据集的准确性如下:
```bash
[lfw]Accuracy: 0.99700+-0.00277
[lfw]Accuracy-Flip: 0.99750+-0.00214
[cfp_fp]Accuracy: 0.97414+-0.00668
[cfp_fp]Accuracy-Flip: 0.97757+-0.00414
[agedb_30]Accuracy: 0.96833+-0.01195
[agedb_30]Accuracy-Flip: 0.96983+-0.01045
```
## 导出mindir模型
```python
python export.py --ckpt_file [CKPT_PATH] --file_name [FILE_NAME] --file_format [FILE_FORMAT]
```
参数`ckpt_file` 是必需的,`FILE_FORMAT` 必须在 ["AIR", "MINDIR"]中进行选择。
# 模型描述
## 性能
### 训练性能
| 参数 | LResNet100E-IR |
| ------------- | ------------------------------------------------------------ |
| 模型版本 | LResNet100E-IR |
| 资源 | GeForce RTX 3090 | | |
| 上传日期 | 2022-04-16 |
| MindSpore版本 | 1.5.0 |
| 数据集 | MS1MV2 |
| 训练参数 | lr=0.1; gamma=0.1 |
| 优化器 | SGD |
| 损失函数 | SoftmaxCrossEntropyWithLogits |
| 输出 | 概率 |
| 损失 | 3.3598282 |
| 总时间(8p) | 41h | | |
| 脚本 | [脚本路径](https://gitee.com/mindspore/models/tree/master/research/cv/lresnet100e_ir) |
### 评估性能
| 参数 | LResNet100E-IR |
| ------------- | ------------------------ |
| 模型版本 | LResNet100E-IR |
| 资源 | GeForce RTX 3090 |
| 上传日期 | 2022/04/16 |
| MindSpore版本 | 1.5.0 |
| 数据集 | lfw,cfp-fp,agedb_30 |
| 输出 | 概率 |
| 准确性 | lfw:0.997 cfp_fp:0.974 agedb_30:0.968 |
# 随机情况说明
网络的初始参数均为随即初始化。
# ModelZoo主页
请浏览官网[主页](https://gitee.com/mindspore/models)
\ No newline at end of file
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
'''
evaluation of lfw, calfw, cfp_fp, agedb_30, cplfw
'''
import datetime
import os
import pickle
from io import BytesIO
import mxnet as mx
import numpy as np
import sklearn
from sklearn.decomposition import PCA
from sklearn.model_selection import KFold
import matplotlib.pyplot as plt
from scipy import interpolate
import mindspore as ms
from mindspore.train.serialization import load_checkpoint, load_param_into_net
from mindspore import context
from src.iresnet import iresnet100
from src.config import config
class LFold:
'''
LFold
'''
def __init__(self, n_splits=2, shuffle=False):
self.n_splits = n_splits
if self.n_splits > 1:
self.k_fold = KFold(n_splits=n_splits, shuffle=shuffle)
def split(self, indices):
if self.n_splits > 1:
return self.k_fold.split(indices)
return [(indices, indices)]
def calculate_roc(thresholds,
embeddings1,
embeddings2,
actual_issame,
nrof_folds=10,
pca=0):
'''
calculate_roc
'''
assert embeddings1.shape[0] == embeddings2.shape[0]
assert embeddings1.shape[1] == embeddings2.shape[1]
nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
nrof_thresholds = len(thresholds)
k_fold = LFold(n_splits=nrof_folds, shuffle=False)
tprs = np.zeros((nrof_folds, nrof_thresholds))
fprs = np.zeros((nrof_folds, nrof_thresholds))
accuracy = np.zeros((nrof_folds))
indices = np.arange(nrof_pairs)
if pca == 0:
diff = np.subtract(embeddings1, embeddings2)
dist = np.sum(np.square(diff), 1)
for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):
if pca > 0:
print('doing pca on', fold_idx)
embed1_train = embeddings1[train_set]
embed2_train = embeddings2[train_set]
_embed_train = np.concatenate((embed1_train, embed2_train), axis=0)
pca_model = PCA(n_components=pca)
pca_model.fit(_embed_train)
embed1 = pca_model.transform(embeddings1)
embed2 = pca_model.transform(embeddings2)
embed1 = sklearn.preprocessing.normalize(embed1)
embed2 = sklearn.preprocessing.normalize(embed2)
diff = np.subtract(embed1, embed2)
dist = np.sum(np.square(diff), 1)
# Find the best threshold for the fold
acc_train = np.zeros((nrof_thresholds))
for threshold_idx, threshold in enumerate(thresholds):
_, _, acc_train[threshold_idx] = calculate_accuracy(
threshold, dist[train_set], actual_issame[train_set])
best_threshold_index = np.argmax(acc_train)
for threshold_idx, threshold in enumerate(thresholds):
tprs[fold_idx, threshold_idx], fprs[fold_idx, threshold_idx], _ = calculate_accuracy(
threshold, dist[test_set],
actual_issame[test_set])
_, _, accuracy[fold_idx] = calculate_accuracy(
thresholds[best_threshold_index], dist[test_set],
actual_issame[test_set])
tpr = np.mean(tprs, 0)
fpr = np.mean(fprs, 0)
return tpr, fpr, accuracy
def calculate_accuracy(threshold, dist, actual_issame):
'''
calculate_acc
'''
predict_issame = np.less(dist, threshold)
tp = np.sum(np.logical_and(predict_issame, actual_issame))
fp = np.sum(np.logical_and(predict_issame, np.logical_not(actual_issame)))
tn = np.sum(
np.logical_and(np.logical_not(predict_issame),
np.logical_not(actual_issame)))
fn = np.sum(np.logical_and(np.logical_not(predict_issame), actual_issame))
tpr = 0 if (tp + fn == 0) else float(tp) / float(tp + fn)
fpr = 0 if (fp + tn == 0) else float(fp) / float(fp + tn)
acc = float(tp + tn) / dist.size
return tpr, fpr, acc
def calculate_val(thresholds,
embeddings1,
embeddings2,
actual_issame,
far_target,
nrof_folds=10):
'''
calculate_val
'''
assert embeddings1.shape[0] == embeddings2.shape[0]
assert embeddings1.shape[1] == embeddings2.shape[1]
nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
nrof_thresholds = len(thresholds)
k_fold = LFold(n_splits=nrof_folds, shuffle=False)
val = np.zeros(nrof_folds)
far = np.zeros(nrof_folds)
diff = np.subtract(embeddings1, embeddings2)
dist = np.sum(np.square(diff), 1)
indices = np.arange(nrof_pairs)
for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):
# Find the threshold that gives FAR = far_target
far_train = np.zeros(nrof_thresholds)
for threshold_idx, threshold in enumerate(thresholds):
_, far_train[threshold_idx] = calculate_val_far(
threshold, dist[train_set], actual_issame[train_set])
if np.max(far_train) >= far_target:
f = interpolate.interp1d(far_train, thresholds, kind='slinear')
threshold = f(far_target)
else:
threshold = 0.0
val[fold_idx], far[fold_idx] = calculate_val_far(
threshold, dist[test_set], actual_issame[test_set])
val_mean = np.mean(val)
far_mean = np.mean(far)
val_std = np.std(val)
return val_mean, val_std, far_mean
def calculate_val_far(threshold, dist, actual_issame):
'''
calculate_val_far
'''
predict_issame = np.less(dist, threshold)
true_accept = np.sum(np.logical_and(predict_issame, actual_issame))
false_accept = np.sum(
np.logical_and(predict_issame, np.logical_not(actual_issame)))
n_same = np.sum(actual_issame)
n_diff = np.sum(np.logical_not(actual_issame))
val = float(true_accept) / float(n_same)
far = float(false_accept) / float(n_diff)
return val, far
def evaluate(embeddings, actual_issame, nrof_folds=10, pca=0):
'''
evaluate
'''
# Calculate evaluation metrics
thresholds = np.arange(0, 4, 0.01)
embeddings1 = embeddings[0::2]
embeddings2 = embeddings[1::2]
tpr, fpr, accuracy = calculate_roc(thresholds,
embeddings1,
embeddings2,
np.asarray(actual_issame),
nrof_folds=nrof_folds,
pca=pca)
thresholds = np.arange(0, 4, 0.001)
val, val_std, far = calculate_val(thresholds,
embeddings1,
embeddings2,
np.asarray(actual_issame),
1e-3,
nrof_folds=nrof_folds)
return tpr, fpr, accuracy, val, val_std, far
def load_bin(path, image_size):
'''
load evalset of .bin
'''
try:
with open(path, 'rb') as f:
bins, issame_list = pickle.load(f) # py2
except UnicodeDecodeError as _:
with open(path, 'rb') as f:
bins, issame_list = pickle.load(f, encoding='bytes') # py3
data_list = []
for _ in [0, 1]:
data = np.zeros(
(len(issame_list) * 2, 3, image_size[0], image_size[1]))
data_list.append(data)
for idx in range(len(issame_list) * 2):
_bin = bins[idx]
img = plt.imread(BytesIO(_bin), "jpg")
if img.shape[1] != image_size[0]:
img = mx.image.resize_short(img, image_size[0])
img = np.transpose(img, axes=(2, 0, 1))
for flip in [0, 1]:
if flip == 1:
img = np.flip(img, axis=2)
data_list[flip][idx][:] = img
return data_list, issame_list
def test(data_set, backbone, batch_size, nfolds=10):
'''
test
'''
print('testing verification..')
data_list = data_set[0]
issame_list = data_set[1]
embeddings_list = []
time_consumed = 0.0
for data in data_list:
embeddings = None
ba = 0
while ba < data.shape[0]:
bb = min(ba + batch_size, data.shape[0])
count = bb - ba
_data = data[bb - batch_size: bb]
time0 = datetime.datetime.now()
img = ((_data / 255) - 0.5) / 0.5
net_out = backbone(ms.Tensor(img, ms.float32))
_embeddings = net_out.asnumpy()
time_now = datetime.datetime.now()
diff = time_now - time0
time_consumed += diff.total_seconds()
if embeddings is None:
embeddings = np.zeros((data.shape[0], _embeddings.shape[1]))
embeddings[ba:bb, :] = _embeddings[(batch_size - count):, :]
ba = bb
embeddings_list.append(embeddings)
_xnorm = 0.0
_xnorm_cnt = 0
for embed in embeddings_list:
for i in range(embed.shape[0]):
_em = embed[i]
_norm = np.linalg.norm(_em)
_xnorm += _norm
_xnorm_cnt += 1
_xnorm /= _xnorm_cnt
embeddings = embeddings_list[0].copy()
embeddings = sklearn.preprocessing.normalize(embeddings)
_, _, acc, _, _, _ = evaluate(embeddings, issame_list, nrof_folds=nfolds)
acc1 = np.mean(acc)
std1 = np.std(acc)
embeddings = embeddings_list[0] + embeddings_list[1]
embeddings = sklearn.preprocessing.normalize(embeddings)
print(embeddings.shape)
print('infer time', time_consumed)
_, _, accuracy, _, _, _ = evaluate(
embeddings, issame_list, nrof_folds=nfolds)
acc2, std2 = np.mean(accuracy), np.std(accuracy)
return acc1, std1, acc2, std2, _xnorm, embeddings_list
def main():
'''
Evaluate trained network.
'''
context.set_context(device_id=config.device_id, mode=context.GRAPH_MODE)
image_size = [112, 112]
time0 = datetime.datetime.now()
model = iresnet100()
param_dict = load_checkpoint(config.checkpoint_file_path)
load_param_into_net(model, param_dict)
time_now = datetime.datetime.now()
diff = time_now - time0
print('model loading time', diff.total_seconds())
ver_list = []
ver_name_list = []
for name in config.target.split(','):
path = os.path.join(config.eval_url, name + ".bin")
if os.path.exists(path):
print('loading.. ', name)
data_set = load_bin(path, image_size)
ver_list.append(data_set)
ver_name_list.append(name)
length = len(ver_list)
for i in range(length):
acc1, std1, acc2, std2, xnorm, _ = test(
ver_list[i], model, config.eval_batch_size, config.nfolds)
print('[%s]XNorm: %f' % (ver_name_list[i], xnorm))
print('[%s]Accuracy: %1.5f+-%1.5f' % (ver_name_list[i], acc1, std1))
print('[%s]Accuracy-Flip: %1.5f+-%1.5f' %
(ver_name_list[i], acc2, std2))
if __name__ == '__main__':
main()
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
##############export checkpoint file into air, onnx, mindir models#################
python export.py
"""
import argparse
import numpy as np
from mindspore import dtype as mstype
from mindspore import Tensor, load_checkpoint, load_param_into_net, export, context
from src.iresnet import iresnet100
from src.config import config
parser = argparse.ArgumentParser(description='Classification')
parser.add_argument("--device_id", type=int, default=0, help="Device id")
parser.add_argument("--batch_size", type=int, default=64, help="batch size")
parser.add_argument("--ckpt_file", type=str, required=True, help="Checkpoint file path.")
parser.add_argument("--file_name", type=str, default="arcface", help="output file name.")
parser.add_argument('--file_format', type=str, choices=["AIR", "ONNX", "MINDIR"], default='AIR', help='file format')
parser.add_argument("--device_target", type=str, choices=["Ascend", "GPU", "CPU"], default="GPU",
help="device target")
parser.add_argument('--dataset_name', type=str, default='MS1MV2', choices=['MS1MV2'],
help='dataset name.')
args = parser.parse_args()
context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target)
if __name__ == '__main__':
if args.dataset_name != 'MS1MV2':
raise ValueError("dataset is not supported.")
net = iresnet100(config.num_classes)
assert args.ckpt_file is not None, "args.ckpt_file is None."
param_dict = load_checkpoint(args.ckpt_file)
load_param_into_net(net, param_dict)
input_arr = Tensor(np.ones([args.batch_size, 3, 112, 112]), mstype.float32)
export(net, input_arr, file_name=args.file_name, file_format=args.file_format)
# Builtin Configurations(DO NOT CHANGE THESE CONFIGURATIONS unless you know exactly what you are doing)
enable_modelarts: False
# Url for modelarts
data_url: ""
train_url: ""
checkpoint_url: ""
# Path for local
run_distribute: False
enable_profiling: False
data_path: "/cache/data"
output_path: "/cache/train"
load_path: "/cache/checkpoint_path/"
device_target: "GPU"
checkpoint_path: "./checkpoint/"
checkpoint_file_path: "lresnet100e_ir-25_5686.ckpt"
# ==============================================================================
# Training options
epoch_size: 25
batch_size: 128
device_id: 0
device_num: 1
use_pynative_mode: False
dataset_sink_mode: True
all_reduce_fusion_config: [8, 160, 320]
save_checkpoint_steps: 1000
keep_checkpoint_max: 5
# Learing rate settings
lr_init: 0.1
schedule: [10, 16, 21]
gamma: 0.1
# Optimizer settings
momentum: 0.9
weight_decay: 0.0005
loss_scale: 1024.0
# Dataset
num_classes: 85742
img_shape: [112, 112]
repeat_num: 1
# ==============================================================================
# Evaluate options
nfolds: 10
eval_batch_size: 64
eval_url: ""
target: "lfw,cfp_fp,agedb_30"
---
# Help description for each configuration
enable_modelarts: "Whether training on modelarts, default: False"
data_url: "Dataset url for obs"
train_url: "Training output url for obs"
checkpoint_url: "The location of checkpoint for obs"
data_path: "Dataset path for local"
output_path: "Training output path for local"
load_path: "The location of checkpoint for obs"
device_target: "Target device type, available: [Ascend, GPU, CPU]"
enable_profiling: "Whether enable profiling while training, default: False"
num_classes: "Class for dataset"
batch_size: "Batch size for training and evaluation"
epoch_size: "Total training epochs"
checkpoint_path: "The location of the checkpoint file"
checkpoint_file_path: "The location of the checkpoint file"
file_name: "Output file name"
file_format: "File format choices [AIR MINDIR ONNX]"
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
#!/bin/bash
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
echo "=============================================================================================================="
echo "Please run the script as: "
echo "bash run_standalone_train_for_gpu.sh [DATA_PATH] [DEVICE_NUM]"
echo "for example: bash run_standalone_train_gpu.sh /path/dataset 8"
echo "It is better to use absolute path."
echo "================================================================================================================="
if [ $# -ne 2 ]
then
echo "Usage: bash run_standalone_train_for_gpu.sh [DATA_PATH] [DEVICE_NUM]"
exit 1
fi
# check dataset path
if [ ! -d $1 ]
then
echo "error: DATA_PATH=$1 is not a directory"
exit 1
fi
export DEVICE_NUM=$2
export DATA_PATH=$1
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
echo "start training"
mpirun -n $DEVICE_NUM --allow-run-as-root --output-filename log_output --merge-stderr-to-stdout \
python train.py \
--device_num $DEVICE_NUM \
--data_url $DATA_PATH \
--run_distribute True \
> train.log 2>&1 &
\ No newline at end of file
#!/bin/bash
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
echo "=============================================================================================================="
echo "Please run the script as: "
echo "bash run_eval.sh [EVAL_PATH] [CKPT_PATH]"
echo "For example: bash run.sh path/evalset path/ckpt"
echo "It is better to use the absolute path."
echo "=============================================================================================================="
if [ $# -ne 2 ]
then
echo "Usage: bash run_eval.sh [EVAL_PATH] [CKPT_PATH]"
exit 1
fi
# check dataset path
if [ ! -d $1 ]
then
echo "error: DATASET_PATH=$1 is not a directory"
exit 1
fi
# check checkpoint path
if [ ! -f $2 ]
then
echo "error: CKPT_PATH=$2 is not a file"
exit 1
fi
export EVAL_PATH=$1
export CKPT_PATH=$2
echo "start inferring"
python eval.py \
--checkpoint_file_path $CKPT_PATH \
--device_id 0 \
--eval_url $EVAL_PATH \
--target lfw,cfp_fp,agedb_30 \
> eval.log 2>&1 &
\ No newline at end of file
#!/bin/bash
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
echo "=============================================================================================================="
echo "Please run the script as: "
echo "bash run_standalone_train_for_gpu.sh [DATA_PATH]"
echo "for example: bash run_standalone_train_gpu.sh /path/dataset"
echo "It is better to use absolute path."
echo "================================================================================================================="
if [ $# -ne 1 ]
then
echo "Usage: bash run_standalone_train_for_gpu.sh [DATA_PATH]"
exit 1
fi
# check dataset path
if [ ! -d $1 ]
then
echo "error: DATA_PATH=$1 is not a directory"
exit 1
fi
export DATA_PATH=$1
echo "start training"
python train.py \
--data_url $DATA_PATH \
--device_num 1 \
--run_distribute False \
> train.log 2>&1 &
\ No newline at end of file
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Parse arguments"""
import argparse
import ast
import os
from pprint import pformat, pprint
import yaml
class Config:
"""
Configuration namespace. Convert dictionary to members.
"""
def __init__(self, cfg_dict):
for k, v in cfg_dict.items():
if isinstance(v, (list, tuple)):
setattr(self, k, [Config(x) if isinstance(x, dict) else x for x in v])
else:
setattr(self, k, Config(v) if isinstance(v, dict) else v)
def __str__(self):
"""Get the string representation of the object"""
return pformat(self.__dict__)
def __repr__(self):
"""Get the string representation of the object"""
return self.__str__()
def parse_cli_to_yaml(parser, cfg, helper=None, choices=None, cfg_path="ssd300_config.yaml"):
"""
Parse command line arguments to the configuration according to the default yaml.
Args:
parser: Parent parser.
cfg: Base configuration.
helper: Helper description.
cfg_path: Path to the default yaml config.
"""
parser = argparse.ArgumentParser(description="[REPLACE THIS at config.py]",
parents=[parser])
helper = {} if helper is None else helper
choices = {} if choices is None else choices
for item in cfg:
if not isinstance(cfg[item], list) and not isinstance(cfg[item], dict):
help_description = helper[item] if item in helper else "Please reference to {}".format(cfg_path)
choice = choices[item] if item in choices else None
if isinstance(cfg[item], bool):
parser.add_argument("--" + item, type=ast.literal_eval, default=cfg[item], choices=choice,
help=help_description)
else:
parser.add_argument("--" + item, type=type(cfg[item]), default=cfg[item], choices=choice,
help=help_description)
args = parser.parse_args()
return args
def parse_yaml(yaml_path):
"""
Parse the yaml config file.
Args:
yaml_path: Path to the yaml config.
"""
with open(yaml_path, 'r') as fin:
try:
cfgs = yaml.load_all(fin.read(), Loader=yaml.FullLoader)
cfgs = [x for x in cfgs]
if len(cfgs) == 1:
cfg_helper = {}
cfg = cfgs[0]
cfg_choices = {}
elif len(cfgs) == 2:
cfg, cfg_helper = cfgs
cfg_choices = {}
elif len(cfgs) == 3:
cfg, cfg_helper, cfg_choices = cfgs
else:
raise ValueError("At most 3 docs (config description for help, choices) are supported in config yaml")
print(cfg_helper)
except:
raise ValueError("Failed to parse yaml")
return cfg, cfg_helper, cfg_choices
def merge(args, cfg):
"""
Merge the base config from yaml file and command line arguments.
Args:
args: Command line arguments.
cfg: Base configuration.
"""
args_var = vars(args)
for item in args_var:
cfg[item] = args_var[item]
return cfg
def get_config():
"""
Get Config according to the yaml file and cli arguments.
"""
parser = argparse.ArgumentParser(description="default name", add_help=False)
current_dir = os.path.dirname(os.path.abspath(__file__))
parser.add_argument("--config_path",
type=str,
default=os.path.join(current_dir, "../lresnet100e_ir_config_gpu.yaml"),
help="Config file path")
path_args, _ = parser.parse_known_args()
default, helper, choices = parse_yaml(path_args.config_path)
args = parse_cli_to_yaml(parser=parser, cfg=default, helper=helper, choices=choices, cfg_path=path_args.config_path)
final_config = merge(args, default)
pprint(final_config)
print("Please check the above information for the configurations", flush=True)
return Config(final_config)
config = get_config()
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""Create train or eval dataset."""
import mindspore.common.dtype as mstype
import mindspore.dataset as ds
import mindspore.dataset.vision.c_transforms as C
import mindspore.dataset.transforms.c_transforms as C2
def create_dataset(dataset_path, do_train, img_shape, repeat_num=1, batch_size=32, run_distribute=False):
"""
create a train or eval dataset
"""
if do_train:
if run_distribute:
from mindspore.communication.management import get_rank, get_group_size
data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=8, shuffle=True,
num_shards=get_group_size(), shard_id=get_rank())
else:
data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=8, shuffle=True)
else:
data_set = ds.ImageFolderDataset(dataset_path, num_parallel_workers=8, shuffle=True)
mean = [0.5 * 255, 0.5 * 255, 0.5 * 255]
std = [0.5 * 255, 0.5 * 255, 0.5 * 255]
# define map operations
if do_train:
trans = [
C.Decode(),
C.RandomHorizontalFlip(prob=0.5),
C.Normalize(mean=mean, std=std),
C.HWC2CHW()
]
else:
trans = [
C.Decode(),
C.CenterCrop(img_shape),
C.Normalize(mean=mean, std=std),
C.HWC2CHW()
]
type_cast_op = C2.TypeCast(mstype.int32)
data_set = data_set.map(operations=trans, input_columns="image", num_parallel_workers=8)
data_set = data_set.map(operations=type_cast_op, input_columns="label", num_parallel_workers=8)
# apply batch operations
data_set = data_set.batch(batch_size, drop_remainder=True)
# apply dataset repeat operation
data_set = data_set.repeat(repeat_num)
return data_set
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
python iresnet.py
"""
from mindspore import nn
import mindspore.ops as ops
__all__ = ['iresnet18', 'iresnet34', 'iresnet50', 'iresnet100']
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
"""
3x3 convolution with padding
# noqa: DAR201
"""
return nn.Conv2d(in_planes,
out_planes,
kernel_size=3,
stride=stride,
padding=dilation,
pad_mode='pad',
group=groups,
has_bias=False,
dilation=dilation)
def conv1x1(in_planes, out_planes, stride=1):
"""
1x1 convolution
# noqa: DAR201
"""
return nn.Conv2d(in_planes,
out_planes,
kernel_size=1,
stride=stride,
has_bias=False)
class IBasicBlock(nn.Cell):
'''IBasicBlock
'''
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None,
groups=1, base_width=64, dilation=1):
super(IBasicBlock, self).__init__()
if groups != 1 or base_width != 64:
raise ValueError(
'BasicBlock only supports groups=1 and base_width=64')
if dilation > 1:
raise NotImplementedError(
"Dilation > 1 not supported in BasicBlock")
self.bn1 = nn.BatchNorm2d(
inplanes,
eps=1e-05,
)
self.conv1 = conv3x3(inplanes, planes)
self.bn2 = nn.BatchNorm2d(
planes,
eps=1e-05,
)
self.prelu = nn.PReLU(planes)
self.conv2 = conv3x3(planes, planes, stride)
self.bn3 = nn.BatchNorm2d(
planes,
eps=1e-05,
)
self.downsample = downsample
self.stride = stride
def construct(self, x):
'''construct
# noqa: DAR201
'''
identity = x
out = self.bn1(x)
out = self.conv1(out)
out = self.bn2(out)
out = self.prelu(out)
out = self.conv2(out)
out = self.bn3(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
return out
class IResNet(nn.Cell):
'''IResNet
'''
fc_scale = 7 * 7
def __init__(self,
arch, block, layers, dropout=0, num_features=512,
groups=1, width_per_group=64, replace_stride_with_dilation=None, extract_feature=False):
super(IResNet, self).__init__()
self.arch = arch
self.inplanes = 64
self.dilation = 1
self.extract_feature = extract_feature
if replace_stride_with_dilation is None:
replace_stride_with_dilation = [False, False, False]
if len(replace_stride_with_dilation) != 3:
raise ValueError("replace_stride_with_dilation should be None "
"or a 3-element tuple, got {}".format(replace_stride_with_dilation))
self.groups = groups
self.base_width = width_per_group
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=3,
stride=1, padding=1, pad_mode='pad', has_bias=False)
self.bn1 = nn.BatchNorm2d(self.inplanes, eps=1e-05)
self.prelu = nn.PReLU(self.inplanes)
self.layer1 = self._make_layer(block, 64, layers[0], stride=2)
self.layer2 = self._make_layer(block,
128,
layers[1],
stride=2,
dilate=replace_stride_with_dilation[0])
self.layer3 = self._make_layer(block,
256,
layers[2],
stride=2,
dilate=replace_stride_with_dilation[1])
self.layer4 = self._make_layer(block,
512,
layers[3],
stride=2,
dilate=replace_stride_with_dilation[2])
self.bn2 = nn.BatchNorm2d(512 * block.expansion, eps=1e-05,)
self.dropout = nn.Dropout(keep_prob=1.0-dropout)
self.fc = nn.Dense(512 * block.expansion * self.fc_scale,
num_features)
self.features = nn.BatchNorm1d(num_features, eps=1e-05)
self.features.gamma.requires_grad = False
self.reshape = ops.Reshape()
self.flatten = ops.Flatten()
def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
'''make_layer
'''
downsample = None
previous_dilation = self.dilation
if dilate:
self.dilation *= stride
stride = 1
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.SequentialCell([
conv1x1(self.inplanes, planes * block.expansion, stride),
nn.BatchNorm2d(planes * block.expansion, eps=1e-05)
])
layers = []
layers.append(
block(self.inplanes, planes, stride, downsample, self.groups,
self.base_width, previous_dilation))
self.inplanes = planes * block.expansion
for _ in range(1, blocks):
layers.append(
block(self.inplanes,
planes,
groups=self.groups,
base_width=self.base_width,
dilation=self.dilation))
return nn.SequentialCell(layers)
def construct(self, x):
'''construct
# noqa: DAR201
'''
x = self.conv1(x)
x = self.bn1(x)
x = self.prelu(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.bn2(x)
x = self.flatten(x)
x = self.dropout(x)
x = self.fc(x)
x = self.features(x)
return x
def _iresnet(arch, block, layers, pretrained, **kwargs):
model = IResNet(arch, block, layers, **kwargs)
if pretrained:
raise ValueError()
return model
def iresnet18(pretrained=False, **kwargs):
return _iresnet('iresnet18', IBasicBlock, [2, 2, 2, 2], pretrained, **kwargs)
def iresnet34(pretrained=False, **kwargs):
return _iresnet('iresnet34', IBasicBlock, [3, 4, 6, 3], pretrained, **kwargs)
def iresnet50(pretrained=False, **kwargs):
return _iresnet('iresnet50', IBasicBlock, [3, 4, 14, 3], pretrained, **kwargs)
def iresnet100(pretrained=False, **kwargs):
return _iresnet('iresnet100', IBasicBlock, [3, 13, 30, 3], pretrained, **kwargs)
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
python train.py
"""
import numpy as np
import mindspore.nn as nn
from mindspore import context, Tensor
from mindspore.train.model import Model, ParallelMode
from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, TimeMonitor, LossMonitor
from mindspore.communication.management import init, get_rank
from mindspore.nn.loss import SoftmaxCrossEntropyWithLogits
from mindspore.ops import functional as F
from mindspore.common import set_seed, dtype
from mindspore.train.loss_scale_manager import FixedLossScaleManager
from src.dataset import create_dataset
from src.iresnet import iresnet100
from src.config import config
set_seed(1)
def lr_generator(lr_init, total_epochs, steps_per_epoch):
"""lr_generator
"""
lr_each_step = []
for i in range(total_epochs):
if i in config.schedule:
lr_init *= config.gamma
for _ in range(steps_per_epoch):
lr_each_step.append(lr_init)
lr_each_step = np.array(lr_each_step).astype(np.float32)
return Tensor(lr_each_step)
class LResNetWithLoss(nn.Cell):
"""
WithLossCell
"""
def __init__(self, net, num_classes, num_features=512):
super(LResNetWithLoss, self).__init__(auto_prefix=False)
self._net = net.to_float(dtype.float16)
self._fc = nn.Dense(num_features, num_classes).to_float(dtype.float16)
self._loss_fn = SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
def construct(self, data, label):
features = self._net(data)
output = self._fc(features)
out = F.cast(output, dtype.float32)
loss = self._loss_fn(out, label)
return loss
if __name__ == "__main__":
# set context and device init
train_epoch = config.epoch_size
if config.use_pynative_mode:
context.set_context(mode=context.PYNATIVE_MODE, device_target=config.device_target,
device_id=config.device_id, save_graphs=False, enable_graph_kernel=True)
else:
context.set_context(mode=context.GRAPH_MODE, device_target=config.device_target,
device_id=config.device_id, save_graphs=False, enable_graph_kernel=True)
if config.run_distribute:
init()
context.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL,
device_num=config.device_num,
all_reduce_fusion_config=config.all_reduce_fusion_config,
gradients_mean=True)
config.rank = get_rank()
else:
config.rank = 0
# define dataset
train_dataset = create_dataset(dataset_path=config.data_url, do_train=True,
img_shape=config.img_shape, repeat_num=config.repeat_num,
batch_size=config.batch_size, run_distribute=config.run_distribute)
step = train_dataset.get_dataset_size()
# define net
network = iresnet100()
train_net = LResNetWithLoss(network, config.num_classes)
# define lr
lr = lr_generator(config.lr_init, train_epoch, steps_per_epoch=step)
# define optimizer
optimizer = nn.SGD(params=train_net.trainable_params(),
learning_rate=lr / 512 * config.batch_size * config.device_num,
momentum=config.momentum, weight_decay=config.weight_decay, loss_scale=config.loss_scale)
loss_scale = FixedLossScaleManager(config.loss_scale, drop_overflow_update=False)
# define model
model = Model(train_net, optimizer=optimizer, loss_scale_manager=loss_scale)
time_cb = TimeMonitor(data_size=train_dataset.get_dataset_size())
loss_cb = LossMonitor()
cb = [time_cb, loss_cb]
if config.rank == 0:
config_ck = CheckpointConfig(save_checkpoint_steps=config.save_checkpoint_steps,
keep_checkpoint_max=config.keep_checkpoint_max)
ckpt_cb = ModelCheckpoint(prefix="lresnet100e_ir", config=config_ck,
directory=config.checkpoint_path)
cb.append(ckpt_cb)
# begin train
model.train(train_epoch, train_dataset,
callbacks=cb, dataset_sink_mode=config.dataset_sink_mode)
# Copyright 2022 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
'''
evaluation of lfw, calfw, cfp_fp, agedb_30, cplfw
'''
import datetime
import os
import pickle
import argparse
from io import BytesIO
import mxnet as mx
import numpy as np
import sklearn
from sklearn.decomposition import PCA
from sklearn.model_selection import KFold
import matplotlib.pyplot as plt
from scipy import interpolate
import mindspore as ms
from mindspore.train.serialization import load_checkpoint, load_param_into_net
from mindspore import context
from src.iresnet import iresnet100
class LFold:
'''
LFold
'''
def __init__(self, n_splits=2, shuffle=False):
self.n_splits = n_splits
if self.n_splits > 1:
self.k_fold = KFold(n_splits=n_splits, shuffle=shuffle)
def split(self, indices):
if self.n_splits > 1:
return self.k_fold.split(indices)
return [(indices, indices)]
def calculate_roc(thresholds,
embeddings1,
embeddings2,
actual_issame,
nrof_folds=10,
pca=0):
'''
calculate_roc
'''
assert embeddings1.shape[0] == embeddings2.shape[0]
assert embeddings1.shape[1] == embeddings2.shape[1]
nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
nrof_thresholds = len(thresholds)
k_fold = LFold(n_splits=nrof_folds, shuffle=False)
tprs = np.zeros((nrof_folds, nrof_thresholds))
fprs = np.zeros((nrof_folds, nrof_thresholds))
accuracy = np.zeros((nrof_folds))
indices = np.arange(nrof_pairs)
if pca == 0:
diff = np.subtract(embeddings1, embeddings2)
dist = np.sum(np.square(diff), 1)
for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):
if pca > 0:
print('doing pca on', fold_idx)
embed1_train = embeddings1[train_set]
embed2_train = embeddings2[train_set]
_embed_train = np.concatenate((embed1_train, embed2_train), axis=0)
pca_model = PCA(n_components=pca)
pca_model.fit(_embed_train)
embed1 = pca_model.transform(embeddings1)
embed2 = pca_model.transform(embeddings2)
embed1 = sklearn.preprocessing.normalize(embed1)
embed2 = sklearn.preprocessing.normalize(embed2)
diff = np.subtract(embed1, embed2)
dist = np.sum(np.square(diff), 1)
# Find the best threshold for the fold
acc_train = np.zeros((nrof_thresholds))
for threshold_idx, threshold in enumerate(thresholds):
_, _, acc_train[threshold_idx] = calculate_accuracy(
threshold, dist[train_set], actual_issame[train_set])
best_threshold_index = np.argmax(acc_train)
for threshold_idx, threshold in enumerate(thresholds):
tprs[fold_idx, threshold_idx], fprs[fold_idx, threshold_idx], _ = calculate_accuracy(
threshold, dist[test_set],
actual_issame[test_set])
_, _, accuracy[fold_idx] = calculate_accuracy(
thresholds[best_threshold_index], dist[test_set],
actual_issame[test_set])
tpr = np.mean(tprs, 0)
fpr = np.mean(fprs, 0)
return tpr, fpr, accuracy
def calculate_accuracy(threshold, dist, actual_issame):
'''calculate_acc
'''
predict_issame = np.less(dist, threshold)
tp = np.sum(np.logical_and(predict_issame, actual_issame))
fp = np.sum(np.logical_and(predict_issame, np.logical_not(actual_issame)))
tn = np.sum(
np.logical_and(np.logical_not(predict_issame),
np.logical_not(actual_issame)))
fn = np.sum(np.logical_and(np.logical_not(predict_issame), actual_issame))
tpr = 0 if (tp + fn == 0) else float(tp) / float(tp + fn)
fpr = 0 if (fp + tn == 0) else float(fp) / float(fp + tn)
acc = float(tp + tn) / dist.size
return tpr, fpr, acc
def calculate_val(thresholds,
embeddings1,
embeddings2,
actual_issame,
far_target,
nrof_folds=10):
'''
calculate_val
'''
assert embeddings1.shape[0] == embeddings2.shape[0]
assert embeddings1.shape[1] == embeddings2.shape[1]
nrof_pairs = min(len(actual_issame), embeddings1.shape[0])
nrof_thresholds = len(thresholds)
k_fold = LFold(n_splits=nrof_folds, shuffle=False)
val = np.zeros(nrof_folds)
far = np.zeros(nrof_folds)
diff = np.subtract(embeddings1, embeddings2)
dist = np.sum(np.square(diff), 1)
indices = np.arange(nrof_pairs)
for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)):
# Find the threshold that gives FAR = far_target
far_train = np.zeros(nrof_thresholds)
for threshold_idx, threshold in enumerate(thresholds):
_, far_train[threshold_idx] = calculate_val_far(
threshold, dist[train_set], actual_issame[train_set])
if np.max(far_train) >= far_target:
f = interpolate.interp1d(far_train, thresholds, kind='slinear')
threshold = f(far_target)
else:
threshold = 0.0
val[fold_idx], far[fold_idx] = calculate_val_far(
threshold, dist[test_set], actual_issame[test_set])
val_mean = np.mean(val)
far_mean = np.mean(far)
val_std = np.std(val)
return val_mean, val_std, far_mean
def calculate_val_far(threshold, dist, actual_issame):
'''calculate_val_far
'''
predict_issame = np.less(dist, threshold)
true_accept = np.sum(np.logical_and(predict_issame, actual_issame))
false_accept = np.sum(
np.logical_and(predict_issame, np.logical_not(actual_issame)))
n_same = np.sum(actual_issame)
n_diff = np.sum(np.logical_not(actual_issame))
val = float(true_accept) / float(n_same)
far = float(false_accept) / float(n_diff)
return val, far
def evaluate(embeddings, actual_issame, nrof_folds=10, pca=0):
'''evaluate
'''
# Calculate evaluation metrics
thresholds = np.arange(0, 4, 0.01)
embeddings1 = embeddings[0::2]
embeddings2 = embeddings[1::2]
tpr, fpr, accuracy = calculate_roc(thresholds,
embeddings1,
embeddings2,
np.asarray(actual_issame),
nrof_folds=nrof_folds,
pca=pca)
thresholds = np.arange(0, 4, 0.001)
val, val_std, far = calculate_val(thresholds,
embeddings1,
embeddings2,
np.asarray(actual_issame),
1e-3,
nrof_folds=nrof_folds)
return tpr, fpr, accuracy, val, val_std, far
def load_bin(path, image_size):
'''load evalset of .bin
'''
try:
with open(path, 'rb') as f:
bins, issame_list = pickle.load(f) # py2
except UnicodeDecodeError as _:
with open(path, 'rb') as f:
bins, issame_list = pickle.load(f, encoding='bytes') # py3
data_list = []
for _ in [0, 1]:
data = np.zeros(
(len(issame_list) * 2, 3, image_size[0], image_size[1]))
data_list.append(data)
for idx in range(len(issame_list) * 2):
_bin = bins[idx]
img = plt.imread(BytesIO(_bin), "jpg")
if img.shape[1] != image_size[0]:
img = mx.image.resize_short(img, image_size[0])
img = np.transpose(img, axes=(2, 0, 1))
for flip in [0, 1]:
if flip == 1:
img = np.flip(img, axis=2)
data_list[flip][idx][:] = img
return data_list, issame_list
def test(data_set, backbone, batch_size, nfolds=10):
'''test
'''
print('testing verification..')
data_list = data_set[0]
issame_list = data_set[1]
embeddings_list = []
time_consumed = 0.0
for data in data_list:
embeddings = None
ba = 0
while ba < data.shape[0]:
bb = min(ba + batch_size, data.shape[0])
count = bb - ba
_data = data[bb - batch_size: bb]
time0 = datetime.datetime.now()
img = ((_data / 255) - 0.5) / 0.5
net_out = backbone(ms.Tensor(img, ms.float32))
_embeddings = net_out.asnumpy()
time_now = datetime.datetime.now()
diff = time_now - time0
time_consumed += diff.total_seconds()
if embeddings is None:
embeddings = np.zeros((data.shape[0], _embeddings.shape[1]))
embeddings[ba:bb, :] = _embeddings[(batch_size - count):, :]
ba = bb
embeddings_list.append(embeddings)
_xnorm = 0.0
_xnorm_cnt = 0
for embed in embeddings_list:
for i in range(embed.shape[0]):
_em = embed[i]
_norm = np.linalg.norm(_em)
_xnorm += _norm
_xnorm_cnt += 1
_xnorm /= _xnorm_cnt
embeddings = embeddings_list[0].copy()
embeddings = sklearn.preprocessing.normalize(embeddings)
_, _, acc, _, _, _ = evaluate(embeddings, issame_list, nrof_folds=nfolds)
acc1 = np.mean(acc)
std1 = np.std(acc)
embeddings = embeddings_list[0] + embeddings_list[1]
embeddings = sklearn.preprocessing.normalize(embeddings)
print(embeddings.shape)
print('infer time', time_consumed)
_, _, accuracy, _, _, _ = evaluate(
embeddings, issame_list, nrof_folds=nfolds)
acc2, std2 = np.mean(accuracy), np.std(accuracy)
return acc1, std1, acc2, std2, _xnorm, embeddings_list
def main():
'''r
main function
'''
parser = argparse.ArgumentParser(description='do verification')
# general
parser.add_argument('--eval_url', help='')
parser.add_argument('--device_id', default=0, type=int, help='device id')
parser.add_argument('--target',
default='lfw,cfp_fp,agedb_30',
help='test targets.')
parser.add_argument('--ckpt_url', type=str, help='ckpt path')
parser.add_argument('--gpu', default=0, type=int, help='gpu id')
parser.add_argument('--batch-size', default=64, type=int, help='')
parser.add_argument('--max', default='', type=str, help='')
parser.add_argument('--nfolds', default=10, type=int, help='')
args = parser.parse_args()
context.set_context(device_id=args.device_id, mode=context.GRAPH_MODE)
image_size = [112, 112]
time0 = datetime.datetime.now()
model = iresnet100()
param_dict = load_checkpoint(args.ckpt_url)
load_param_into_net(model, param_dict)
time_now = datetime.datetime.now()
diff = time_now - time0
print('model loading time', diff.total_seconds())
ver_list = []
ver_name_list = []
for name in args.target.split(','):
path = os.path.join(args.eval_url, name + ".bin")
if os.path.exists(path):
print('loading.. ', name)
data_set = load_bin(path, image_size)
ver_list.append(data_set)
ver_name_list.append(name)
length = len(ver_list)
for i in range(length):
acc1, std1, acc2, std2, xnorm, _ = test(
ver_list[i], model, args.batch_size, args.nfolds)
print('[%s]XNorm: %f' % (ver_name_list[i], xnorm))
print('[%s]Accuracy: %1.5f+-%1.5f' % (ver_name_list[i], acc1, std1))
print('[%s]Accuracy-Flip: %1.5f+-%1.5f' %
(ver_name_list[i], acc2, std2))
if __name__ == '__main__':
main()
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment