From fba133b9641ccdb7e965666684137871e998fe0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BD=A6=E5=9B=BD?= Date: Mon, 24 Jul 2023 03:01:21 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=87=8F=E5=8C=96?= =?UTF-8?q?=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙彦国 --- .../cv/Facenet_for_ACL/amct_python.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 ACL_TensorFlow/built-in/cv/Facenet_for_ACL/amct_python.py diff --git a/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/amct_python.py b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/amct_python.py new file mode 100644 index 000000000..a8ce8d880 --- /dev/null +++ b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/amct_python.py @@ -0,0 +1,52 @@ +import os +import numpy as np +import tensorflow as tf +import amct_tensorflow as amct +import argparse + +amct.set_logging_level(print_level="info", save_level="info") + + +def load_bin(bin_path): + input_list = [] + bin_path_list = os.listdir(bin_path) + bin_path_list.sort() + for idx in range(100): + input = np.fromfile(os.path.join(bin_path, bin_path_list[idx]), np.float32).reshape(160, 160, 3) + bin_path.append(input) + return np.array(input_list) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('model', type=str, + help='Path to the data directory containing aligned LFE face patches.') + parser.add_argument('calibration', type=str, help='Data preprocessing output.') + parser.add_argument('output', type=str, help='Data preprocessing output.') + args = parser.parse_args() + calibration_path = os.path.realpath(args.calibration) + model_path = os.path.realpath(args.model) + with tf.io.gfile.GFile(model_path, mode='rb') as model: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(model.read()) + tf.import_graph_def(graph_def, name='') + graph = tf.compat.v1.get_default_graph() + input_tensor = graph.get_tensor_by_name('input' + ':0') + output_tensor = graph.get_tensor_by_name('embeddings' + ':0') + + calibration_list = os.listdir(calibration_path) + input_bin = np.fromfile(os.path.join(calibration_path, calibration_list[0]), np.float32).reshape(1, 160, 160, 3) + with tf.compat.v1.Session() as session: + origin_prediction = session.run(output_tensor, feed_dict={input_tensor: input_bin}) + config_path = os.path.join(args.output, 'config.json') + amct.create_quant_config(config_path=config_path, graph=graph, skip_layers=[], batch_num=1) + record_path = os.path.join(args.output, 'record,txt') + amct.quantize_model(graph=graph, config_file=config_path, record_path=record_path) + batch = load_bin(calibration_path) + with tf.compat.v1.Session() as session: + session.run(tf.compat.v1.global_variables_initializer()) + session.run(output_tensor, feed_dict={input_tensor: batch}) + amct.save_model( + pb_model=model_path, outputs=['embeddings'], record_file=record_path, + save_path=os.path.join(args.output, 'facenet') + ) \ No newline at end of file -- Gitee From cc93ae93a9f625974ed23bd5a78d7030470986d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BD=A6=E5=9B=BD?= Date: Mon, 24 Jul 2023 03:22:06 +0000 Subject: [PATCH 2/4] update ACL_TensorFlow/built-in/cv/Facenet_for_ACL/README.md. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙彦国 --- .../built-in/cv/Facenet_for_ACL/README.md | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/README.md b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/README.md index 1c4624fe3..e738dd6f3 100644 --- a/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/README.md +++ b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/README.md @@ -34,6 +34,7 @@ cd Modelzoo-TensorFlow/ACL_TensorFlow/built-in/cv/Facenet_for_ACL python3 preprocess_data.py Path_of_Data_after_face_alignment Outpath_of_Data_after_face_alignment --use_fixed_image_standardization --lfw_batch_size 1 --use_flipped_images ``` + ### 3. 离线推理 @@ -51,9 +52,33 @@ cd Modelzoo-TensorFlow/ACL_TensorFlow/built-in/cv/Facenet_for_ACL pb模型样例(20180402): ``` - atc --framework=3 --model=./model/facenet_tf.pb --output=./model/facenet --soc_version=Ascend310 --insert_op_conf=./facenet_tensorflow.cfg --input_format=NHWC --input_shape=input:4,160,160,3 + atc --framework=3 --model=./model/facenet_tf.pb --output=./model/facenet --soc_version=Ascend310P3 --insert_op_conf=./facenet_tensorflow.cfg --input_format=NHWC --input_shape=input:64,160,160,3 ``` +### 4.量化 + +1.请自行下载amct工具包 https://support.huawei.com/carrier/navi?coltype=software#col=software&detailId=PBI1-256970475&path=PBI1-21430725/PBI1-21430756/PBI1-22892969/PBI1-23710427/PBI1-251168373 + +```shell +cd amct/amct_tf/ +pip3 install amct_tensorflow-2.16.8-py3-none-linux_x86_64.tar.gz +``` + +2.重新导出一份原始数据集用于量化 + +python3 pre_process_data_forquant.py Path_of_Data_after_face_alignment Outpath_of_Data_after_face_alignment --use_fixed_image_standardization --lfw_batch_size 1 --use_flipped_images + +3.量化模型 + +python3 amct_python.py ./facenet_20180408-102900.pb ./datasets_bin/data_image_bin_original ./quant + +4.pb转om + +mv ./quant/facenet_quantized.pb ./ + +atc --framework=3 --model=./model/facenet_quantized.pb --output=./model/facenet_quant --soc_version=Ascend310P3 --insert_op_conf=./facenet_tensorflow.cfg --input_format=NHWC --input_shape=input:64,160,160,3 + + - 编译程序 ``` @@ -79,3 +104,13 @@ cd Modelzoo-TensorFlow/ACL_TensorFlow/built-in/cv/Facenet_for_ACL | pb(20180402)| offline Inference | 12000 images | 99.550% | | pb(20180408)| offline Inference | 12000 images | 99.133% | +### 6.量化模型精度性能 + +1.执行推理,自行安装ais_bench工具 + +2.执行 python3 -m ais_bench --model ./facenet_quant.om --input datasets_bin/data_image_bin --output ./output --device 0 + +3. 精度验证 + +python3 post2.py ../datasets ../output/2023_05_11-10_55_20 ../datasets_bin/data_label_bin --lfw_batch_size 1 --distance_metric --use_flipped_images --subtract_mean + -- Gitee From 64ad3b2bcba215646a5169bc11395e346f06c785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BD=A6=E5=9B=BD?= Date: Mon, 24 Jul 2023 03:26:33 +0000 Subject: [PATCH 3/4] add ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/preprocess_data_forquant.py. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙彦国 --- .../script/preprocess_data_forquant.py | 257 ++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/preprocess_data_forquant.py diff --git a/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/preprocess_data_forquant.py b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/preprocess_data_forquant.py new file mode 100644 index 000000000..8563095c5 --- /dev/null +++ b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/preprocess_data_forquant.py @@ -0,0 +1,257 @@ +# MIT License +# +# Copyright (c) 2016 David Sandberg +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ============================================================================ +# Copyright 2021 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. +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow as tf +import numpy as np +import argparse +import os +import sys +from tensorflow.python.ops import data_flow_ops +from scipy import misc +# 1: Random rotate 2: Random crop 4: Random flip 8: Fixed image standardization 16: Flip +RANDOM_ROTATE = 1 +RANDOM_CROP = 2 +RANDOM_FLIP = 4 +FIXED_STANDARDIZATION = 8 +FLIP = 16 +def random_rotate_image(image): + angle = np.random.uniform(low=-10.0, high=10.0) + return misc.imrotate(image, angle, 'bicubic') + +def get_control_flag(control, field): + return tf.equal(tf.math.mod(tf.math.floordiv(control, field), 2), 1) + +def create_input_pipeline(input_queue, image_size, nrof_preprocess_threads, batch_size_placeholder): + images_and_labels_list = [] + for _ in range(nrof_preprocess_threads): + filenames, label, control = input_queue.dequeue() + images = [] + for filename in tf.unstack(filenames): + file_contents = tf.io.read_file(filename) + image = tf.image.decode_image(file_contents, 3) + image = tf.cond(get_control_flag(control[0], RANDOM_ROTATE), + lambda:tf.py_function(random_rotate_image, [image], tf.uint8), + lambda:tf.identity(image)) + image = tf.cond(get_control_flag(control[0], RANDOM_CROP), + lambda:tf.image.random_crop(image, image_size + (3,)), + lambda:tf.image.resize_with_crop_or_pad(image, image_size[0], image_size[1])) + image = tf.cond(get_control_flag(control[0], RANDOM_FLIP), + lambda:tf.image.random_flip_left_right(image), + lambda:tf.identity(image)) + image = tf.cond(get_control_flag(control[0], FIXED_STANDARDIZATION), + lambda:(tf.cast(image, tf.float32) - 127.5)/128.0, + #lambda:(tf.cast(image, tf.float32) - 0)/1, + lambda:tf.cast(tf.image.per_image_standardization(image),tf.float32)) + image = tf.cond(get_control_flag(control[0], FLIP), + lambda:tf.image.flip_left_right(image), + lambda:tf.identity(image)) + #pylint: disable=no-member + image.set_shape(image_size + (3,)) + images.append(image) + images_and_labels_list.append([images, label]) + + image_batch, label_batch = tf.train.batch_join( + images_and_labels_list, batch_size=batch_size_placeholder, + shapes=[image_size + (3,), ()], enqueue_many=True, + capacity=4 * nrof_preprocess_threads * 100, + allow_smaller_final_batch=True) + + return image_batch, label_batch +def evaluate(sess,output_path, enqueue_op,image_paths_placeholder,labels_placeholder,control_placeholder,batch_size_placeholder, labels, image_paths, actual_issame, batch_size, use_flipped_images,eval_input_queue,image_batch,use_fixed_image_standardization): + # Run forward pass to calculate embeddings + print('Runnning forward pass on LFW images') + # Enqueue one epoch of image paths and labels + nrof_embeddings = len(actual_issame)*2 # nrof_pairs * nrof_images_per_pair + nrof_flips = 2 if use_flipped_images else 1 + nrof_images = nrof_embeddings * nrof_flips + labels_array = np.expand_dims(np.arange(0,nrof_images),1) + image_paths_array = np.expand_dims(np.repeat(np.array(image_paths),nrof_flips),1) + control_array = np.zeros_like(labels_array, np.int32) + if use_fixed_image_standardization: + control_array += np.ones_like(labels_array)*FIXED_STANDARDIZATION + if use_flipped_images: + control_array += (labels_array % 2)*FLIP + sess.run(enqueue_op, {image_paths_placeholder: image_paths_array, labels_placeholder: labels_array, control_placeholder: control_array}) + + #embedding_size = int(embeddings.get_shape()[1]) + assert nrof_images % batch_size == 0, 'The number of LFW images must be an integer multiple of the LFW batch size' + nrof_batches = nrof_images // batch_size + print("#############nrof_batches",nrof_batches) + if not os.path.exists(os.path.join(output_path ,"data_image_bin_original/")): + os.makedirs(os.path.join(output_path ,"data_image_bin_original/")) + if not os.path.exists(os.path.join(output_path ,"data_label_bin_original/")): + os.makedirs(os.path.join(output_path ,"data_label_bin_original/")) + for i in range(nrof_batches): + ###########save bin ############### + feed_dict2 = {batch_size_placeholder:batch_size} + bin_image2 = os.path.join(output_path,"data_image_bin_original/{}.bin".format(str(i).zfill(6))) + bin_label2 = os.path.join(output_path,"data_label_bin_original/{}.bin".format(str(i).zfill(6))) + emb, lab = sess.run([image_batch,labels],feed_dict=feed_dict2) + #emb.astype(np.uint8).tofile(bin_image2) + emb.tofile(bin_image2) + lab.tofile(bin_label2) + if i % 10 == 9: + print('.', end='') + sys.stdout.flush() + print('') + ###################### +def main(args): + with tf.Graph().as_default(): + with tf.Session() as sess: + output_path = args.output_dir + # Read the file containing the pairs used for testing + pairs = read_pairs(os.path.expanduser(args.lfw_pairs)) + + # Get the paths for the corresponding images + paths, actual_issame = get_paths(os.path.expanduser(args.lfw_dir), pairs) + print(len(paths)) + print(len(actual_issame)) + #print(paths) + #print(actual_issame) + image_paths_placeholder = tf.compat.v1.placeholder(tf.string, shape=(None, 1), name='image_paths') + labels_placeholder = tf.compat.v1.placeholder(tf.int32, shape=(None, 1), name='labels') + batch_size_placeholder = tf.compat.v1.placeholder(tf.int32, name='batch_size') + control_placeholder = tf.compat.v1.placeholder(tf.int32, shape=(None, 1), name='control') + phase_train_placeholder = tf.compat.v1.placeholder(tf.bool, name='phase_train') + + nrof_preprocess_threads = 4 + image_size = (args.image_size, args.image_size) + eval_input_queue = data_flow_ops.FIFOQueue(capacity=2000000, + dtypes=[tf.string, tf.int32, tf.int32], + shapes=[(1,), (1,), (1,)], + shared_name=None, name=None) + eval_enqueue_op = eval_input_queue.enqueue_many( + [image_paths_placeholder, labels_placeholder, control_placeholder], name='eval_enqueue_op') + image_batch, label_batch = create_input_pipeline(eval_input_queue, image_size, nrof_preprocess_threads, + batch_size_placeholder) + + coord = tf.train.Coordinator() + threads =tf.train.start_queue_runners(coord=coord, sess=sess) + evaluate(sess,output_path,eval_enqueue_op, image_paths_placeholder,labels_placeholder,control_placeholder,batch_size_placeholder, label_batch, paths, actual_issame, args.lfw_batch_size,args.use_flipped_images,eval_input_queue,image_batch,args.use_fixed_image_standardization) + sess.run(eval_input_queue.close(cancel_pending_enqueues=True)) + coord.request_stop() + coord.join(threads) +def read_pairs(pairs_filename): + pairs = [] + with open(pairs_filename, 'r') as f: + for line in f.readlines()[1:]: + pair = line.strip().split() + pairs.append(pair) + return np.array(pairs) + +def get_paths(lfw_dir, pairs): + nrof_skipped_pairs = 0 + path_list = [] + issame_list = [] + for pair in pairs: + if len(pair) == 3: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[2]))) + issame = True + elif len(pair) == 4: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[2], pair[2] + '_' + '%04d' % int(pair[3]))) + issame = False + if os.path.exists(path0) and os.path.exists(path1): # Only add the pair if both paths exist + path_list += (path0, path1) + issame_list.append(issame) + else: + nrof_skipped_pairs += 1 + if nrof_skipped_pairs > 0: + print('Skipped %d image pairs' % nrof_skipped_pairs) + + return path_list, issame_list +def get_paths(lfw_dir, pairs): + nrof_skipped_pairs = 0 + path_list = [] + issame_list = [] + for pair in pairs: + if len(pair) == 3: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[2]))) + issame = True + elif len(pair) == 4: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[2], pair[2] + '_' + '%04d' % int(pair[3]))) + issame = False + if os.path.exists(path0) and os.path.exists(path1): # Only add the pair if both paths exist + path_list += (path0, path1) + issame_list.append(issame) + else: + nrof_skipped_pairs += 1 + if nrof_skipped_pairs > 0: + print('Skipped %d image pairs' % nrof_skipped_pairs) + + return path_list, issame_list + + +def add_extension(path): + if os.path.exists(path + '.jpg'): + return path + '.jpg' + elif os.path.exists(path + '.png'): + return path + '.png' + else: + raise RuntimeError('No file "%s" with extension png or jpg.' % path) + +def parse_arguments(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('lfw_dir', type=str, + help='Path to the data directory containing aligned LFW face patches.') + parser.add_argument('output_dir', type=str, help='Data preprocessing output.') + parser.add_argument('--lfw_batch_size', type=int, + help='Number of images to process in a batch in the LFW test set.', default=100) + parser.add_argument('--image_size', type=int, + help='Image size (height, width) in pixels.', default=160) + parser.add_argument('--lfw_pairs', type=str, + help='The file containing the pairs to use for validation.', default='../data/pairs.txt') + parser.add_argument('--lfw_nrof_folds', type=int, + help='Number of folds to use for cross validation. Mainly used for testing.', default=10) + parser.add_argument('--distance_metric', type=int, + help='Distance metric 0:euclidian, 1:cosine similarity.', default=0) + parser.add_argument('--use_flipped_images', + help='Concatenates embeddings for the image and its horizontally flipped counterpart.', action='store_true') + parser.add_argument('--subtract_mean', + help='Subtract feature mean before calculating distance.', action='store_true') + parser.add_argument('--use_fixed_image_standardization', + help='Performs fixed standardization of images.', action='store_true') + return parser.parse_args(argv) + +if __name__ == '__main__': + main(parse_arguments(sys.argv[1:])) \ No newline at end of file -- Gitee From 90eb4e49265935b7222eabbadcb02c5285493bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=BD=A6=E5=9B=BD?= Date: Mon, 24 Jul 2023 03:33:42 +0000 Subject: [PATCH 4/4] add ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/post2.py. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙彦国 --- .../cv/Facenet_for_ACL/script/post2.py | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/post2.py diff --git a/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/post2.py b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/post2.py new file mode 100644 index 000000000..94a2bbf3e --- /dev/null +++ b/ACL_TensorFlow/built-in/cv/Facenet_for_ACL/script/post2.py @@ -0,0 +1,309 @@ +# MIT License +# +# Copyright (c) 2016 David Sandberg +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# ============================================================================ +# Copyright 2021 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. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import argparse +import os +import sys +from sklearn import metrics +from scipy.optimize import brentq +from scipy import interpolate +from sklearn.model_selection import KFold +import math + +def main(args): + # Read the file containing the pairs used for testing + pairs = read_pairs(os.path.expanduser(args.lfw_pairs)) + # Get the paths for the corresponding images + paths, actual_issame = get_paths(os.path.expanduser(args.lfw_dir), pairs) + + batch_size = args.lfw_batch_size + nrof_folds = args.lfw_nrof_folds + distance_metric = args.distance_metric + subtract_mean = args.subtract_mean + use_flipped_images = args.use_flipped_images + + # Run forward pass to calculate embeddings + print('Runnning forward pass on LFW images') + + # Enqueue one epoch of image paths and labels + nrof_embeddings = len(actual_issame) * 2 # nrof_pairs * nrof_images_per_pair + nrof_flips = 2 if use_flipped_images else 1 + nrof_images = nrof_embeddings * nrof_flips + #labels_array = np.expand_dims(np.arange(0, nrof_images), 1) + + + embedding_size = int(512) + assert nrof_images % batch_size == 0, 'The number of LFW images must be an integer multiple of the LFW batch size' + nrof_batches = nrof_images // batch_size + emb_array = np.zeros((nrof_images, embedding_size)) + print("nrof_images",nrof_images) + lab_array = np.zeros((nrof_images,)) + for i in range(nrof_batches): + #feed_dict = {phase_train_placeholder: False, batch_size_placeholder: batch_size} + #emb, lab = sess.run([embeddings, labels], feed_dict=feed_dict) + #读取image_bin和label_bin + #print(paths[i]) + #names = paths[i].split('/')[-1].split('.')[-2] + #print(names) + out_image_name = os.path.join(args.input_dir,"{}_0.bin".format(str(i).zfill(6))) + #print(out_image_name) + emb = np.fromfile(out_image_name, dtype="float32").reshape(1, 512) + out_label_name = os.path.join(args.label_dir,"{}.bin".format(str(i).zfill(6))) + lab = np.fromfile(out_label_name, dtype="int32") + #print(lab) + ########## + lab_array[lab] = lab + emb_array[lab, :] = emb + if i % 10 == 9: + print('.', end='') + sys.stdout.flush() + #print('') + #print(lab_array) + embeddings = np.zeros((nrof_embeddings, embedding_size * nrof_flips)) + if use_flipped_images: + # Concatenate embeddings for flipped and non flipped version of the images + embeddings[:, :embedding_size] = emb_array[0::2, :] + embeddings[:, embedding_size:] = emb_array[1::2, :] + else: + embeddings = emb_array + print("lab_array",lab_array) + assert np.array_equal(lab_array, np.arange( + nrof_images)) == True, 'Wrong labels used for evaluation, possibly caused by training examples left in the input pipeline' + tpr, fpr, accuracy, val, val_std, far = evaluate(embeddings, actual_issame, nrof_folds=nrof_folds, + distance_metric=distance_metric, subtract_mean=subtract_mean) + + print('Accuracy: %2.5f+-%2.5f' % (np.mean(accuracy), np.std(accuracy))) + print('Validation rate: %2.5f+-%2.5f @ FAR=%2.5f' % (val, val_std, far)) + + auc = metrics.auc(fpr, tpr) + print('Area Under Curve (AUC): %1.3f' % auc) + eer = brentq(lambda x: 1. - x - interpolate.interp1d(fpr, tpr)(x), 0., 1.) + print('Equal Error Rate (EER): %1.3f' % eer) +def read_pairs(pairs_filename): + pairs = [] + with open(pairs_filename, 'r') as f: + for line in f.readlines()[1:]: + pair = line.strip().split() + pairs.append(pair) + return np.array(pairs) + +def add_extension(path): + if os.path.exists(path + '.jpg'): + return path + '.jpg' + elif os.path.exists(path + '.png'): + return path + '.png' + else: + raise RuntimeError('No file "%s" with extension png or jpg.' % path) +def get_paths(lfw_dir, pairs): + nrof_skipped_pairs = 0 + path_list = [] + issame_list = [] + for pair in pairs: + if len(pair) == 3: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[2]))) + issame = True + elif len(pair) == 4: + path0 = add_extension(os.path.join(lfw_dir, pair[0], pair[0] + '_' + '%04d' % int(pair[1]))) + path1 = add_extension(os.path.join(lfw_dir, pair[2], pair[2] + '_' + '%04d' % int(pair[3]))) + issame = False + if os.path.exists(path0) and os.path.exists(path1): # Only add the pair if both paths exist + path_list += (path0, path1) + issame_list.append(issame) + else: + nrof_skipped_pairs += 1 + if nrof_skipped_pairs > 0: + print('Skipped %d image pairs' % nrof_skipped_pairs) + + return path_list, issame_list +def evaluate(embeddings, actual_issame, nrof_folds=10, distance_metric=0, subtract_mean=False): + # 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, distance_metric=distance_metric, subtract_mean=subtract_mean) + 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, distance_metric=distance_metric, subtract_mean=subtract_mean) + return tpr, fpr, accuracy, val, val_std, far + + +def distance(embeddings1, embeddings2, distance_metric=0): + if distance_metric == 0: + # Euclidian distance + diff = np.subtract(embeddings1, embeddings2) + dist = np.sum(np.square(diff), 1) + elif distance_metric == 1: + # Distance based on cosine similarity + dot = np.sum(np.multiply(embeddings1, embeddings2), axis=1) + norm = np.linalg.norm(embeddings1, axis=1) * np.linalg.norm(embeddings2, axis=1) + similarity = dot / norm + dist = np.arccos(similarity) / math.pi + else: + raise 'Undefined distance metric %d' % distance_metric + + return dist + +def calculate_roc(thresholds, embeddings1, embeddings2, actual_issame, nrof_folds=10, distance_metric=0, + subtract_mean=False): + 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 = KFold(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) + + for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)): + if subtract_mean: + mean = np.mean(np.concatenate([embeddings1[train_set], embeddings2[train_set]]), axis=0) + else: + mean = 0.0 + dist = distance(embeddings1 - mean, embeddings2 - mean, distance_metric) + + # 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): + 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, distance_metric=0, + subtract_mean=False): + 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 = KFold(n_splits=nrof_folds, shuffle=False) + + val = np.zeros(nrof_folds) + far = np.zeros(nrof_folds) + + indices = np.arange(nrof_pairs) + + for fold_idx, (train_set, test_set) in enumerate(k_fold.split(indices)): + if subtract_mean: + mean = np.mean(np.concatenate([embeddings1[train_set], embeddings2[train_set]]), axis=0) + else: + mean = 0.0 + dist = distance(embeddings1 - mean, embeddings2 - mean, distance_metric) + + # 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): + 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 parse_arguments(argv): + parser = argparse.ArgumentParser() + + parser.add_argument('lfw_dir', type=str, + help='Path to the data directory containing aligned LFW face patches.') + parser.add_argument('input_dir', type=str, help='Data after inference.') + parser.add_argument('label_dir', type=str, help='label_dir.') + parser.add_argument('--lfw_batch_size', type=int, + help='Number of images to process in a batch in the LFW test set.', default=100) + + parser.add_argument('--lfw_pairs', type=str, + help='The file containing the pairs to use for validation.', default='../data/pairs.txt') + parser.add_argument('--lfw_nrof_folds', type=int, + help='Number of folds to use for cross validation. Mainly used for testing.', default=10) + parser.add_argument('--distance_metric', type=int, + help='Distance metric 0:euclidian, 1:cosine similarity.', default=0) + parser.add_argument('--use_flipped_images', + help='Concatenates embeddings for the image and its horizontally flipped counterpart.', + action='store_true') + parser.add_argument('--subtract_mean', + help='Subtract feature mean before calculating distance.', action='store_true') + + return parser.parse_args(argv) + + +if __name__ == '__main__': + main(parse_arguments(sys.argv[1:])) \ No newline at end of file -- Gitee