From 2e7dce466b8aff82428370cbc889d8c08ddc1dba Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Mon, 5 Feb 2018 22:22:39 +0800 Subject: [PATCH 1/8] tiny --- src/common/face_preprocess.py | 2 +- src/data/dataset_clean.py | 3 ++- src/data/dataset_merge.py | 19 ++++++++++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/common/face_preprocess.py b/src/common/face_preprocess.py index 1c9525b..476c591 100644 --- a/src/common/face_preprocess.py +++ b/src/common/face_preprocess.py @@ -7,7 +7,7 @@ def parse_lst_line(line): vec = line.strip().split("\t") assert len(vec)>=3 aligned = False - if int(vec[0])==1: + if int(vec[0])>0: aligned = True image_path = vec[1] label = int(vec[2]) diff --git a/src/data/dataset_clean.py b/src/data/dataset_clean.py index d6d9403..83e8131 100644 --- a/src/data/dataset_clean.py +++ b/src/data/dataset_clean.py @@ -12,10 +12,11 @@ import time import sklearn from sklearn.decomposition import PCA from easydict import EasyDict as edict -import face_image from sklearn.cluster import DBSCAN import numpy as np +sys.path.append(os.path.join(os.path.dirname(__file__),'..', 'common')) +import face_image def do_clean(args): diff --git a/src/data/dataset_merge.py b/src/data/dataset_merge.py index a21c83d..5b5efa7 100644 --- a/src/data/dataset_merge.py +++ b/src/data/dataset_merge.py @@ -18,6 +18,9 @@ import numpy as np sys.path.append(os.path.join(os.path.dirname(__file__),'..', 'common')) import face_image +sys.path.append(os.path.join(os.path.dirname(__file__),'..', 'eval')) +import verification + def ch_dev(arg_params, aux_params, ctx): new_args = dict() new_auxs = dict() @@ -177,6 +180,20 @@ def main(args): _id_list.append( (_ds_id, identity, embedding) ) if test_limit>0 and pp>=test_limit: break + else: + _id_list = [] + data_set = verification.load_bin(args.exclude, image_size)[0][0] + print(data_set.shape) + data = nd.zeros( (1,3,image_size[0], image_size[1])) + for i in xrange(data_set.shape[0]): + data[0] = data_set[i] + db = mx.io.DataBatch(data=(data,)) + model.forward(db, is_train=False) + net_out = model.get_outputs() + embedding = net_out[0].asnumpy().flatten() + _norm=np.linalg.norm(embedding) + embedding /= _norm + _id_list.append( (i, i, embedding) ) #X = [] #for id_item in all_id_list: @@ -259,7 +276,7 @@ if __name__ == '__main__': parser.add_argument('--model', default='../model/softmax,50', help='path to load model.') parser.add_argument('--batch-size', default=32, type=int, help='') parser.add_argument('--param1', default=0.3, type=float, help='') - parser.add_argument('--param2', default=0.45, type=float, help='') + parser.add_argument('--param2', default=0.4, type=float, help='') parser.add_argument('--mode', default=1, type=int, help='') parser.add_argument('--test', default=0, type=int, help='') args = parser.parse_args() From a594347a4cad42ea85803b216a0cda913baabf47 Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Mon, 5 Feb 2018 22:22:57 +0800 Subject: [PATCH 2/8] helper classes --- src/data/dataset_c2c.py | 134 +++++++++++++++++++++++++++++++++++++++ src/data/dataset_info.py | 40 ++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/data/dataset_c2c.py create mode 100644 src/data/dataset_info.py diff --git a/src/data/dataset_c2c.py b/src/data/dataset_c2c.py new file mode 100644 index 0000000..064a8c0 --- /dev/null +++ b/src/data/dataset_c2c.py @@ -0,0 +1,134 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import os +import sys +import mxnet as mx +from mxnet import ndarray as nd +import random +import argparse +import cv2 +import time +import sklearn +from sklearn.decomposition import PCA +from easydict import EasyDict as edict +from sklearn.cluster import DBSCAN +import numpy as np + +sys.path.append(os.path.join(os.path.dirname(__file__),'..', 'common')) +import face_image + + +def main(args): + ctx = [] + cvd = os.environ['CUDA_VISIBLE_DEVICES'].strip() + if len(cvd)>0: + for i in xrange(len(cvd.split(','))): + ctx.append(mx.gpu(i)) + if len(ctx)==0: + ctx = [mx.cpu()] + print('use cpu') + else: + print('gpu num:', len(ctx)) + ctx_num = len(ctx) + path_imgrec = os.path.join(args.input, 'train.rec') + path_imgidx = os.path.join(args.input, 'train.idx') + imgrec = mx.recordio.MXIndexedRecordIO(path_imgidx, path_imgrec, 'r') # pylint: disable=redefined-variable-type + outf = open(os.path.join(args.input, 'c2c'), 'w') + s = imgrec.read_idx(0) + header, _ = mx.recordio.unpack(s) + assert header.flag>0 + print('header0 label', header.label) + header0 = (int(header.label[0]), int(header.label[1])) + #assert(header.flag==1) + imgidx = range(1, int(header.label[0])) + id2range = {} + seq_identity = range(int(header.label[0]), int(header.label[1])) + for identity in seq_identity: + s = imgrec.read_idx(identity) + header, _ = mx.recordio.unpack(s) + id2range[identity] = (int(header.label[0]), int(header.label[1])) + print('id2range', len(id2range)) + prop = face_image.load_property(args.input) + image_size = prop.image_size + print('image_size', image_size) + vec = args.model.split(',') + prefix = vec[0] + epoch = int(vec[1]) + print('loading',prefix, epoch) + model = mx.mod.Module.load(prefix, epoch, context = ctx) + model.bind(data_shapes=[('data', (args.batch_size, 3, image_size[0], image_size[1]))], label_shapes=[('softmax_label', (args.batch_size,))]) + nrof_images = 0 + nrof_removed = 0 + idx = 1 + id2label = {} + pp = 0 + for _id, v in id2range.iteritems(): + pp+=1 + if pp%100==0: + print('processing id', pp) + _list = range(*v) + ocontents = [] + for i in xrange(len(_list)): + _idx = _list[i] + #print('_idx', _id, _idx) + s = imgrec.read_idx(_idx) + ocontents.append(s) + #continue + embeddings = None + headers = [None]*len(ocontents) + #print(len(ocontents)) + ba = 0 + while True: + bb = min(ba+args.batch_size, len(ocontents)) + if ba>=bb: + break + _batch_size = bb-ba + _batch_size2 = max(_batch_size, ctx_num) + data = nd.zeros( (_batch_size2,3, image_size[0], image_size[1]) ) + label = nd.zeros( (_batch_size2,) ) + count = bb-ba + ii=0 + for i in xrange(ba, bb): + header, img = mx.recordio.unpack(ocontents[i]) + headers[i] = header + img = mx.image.imdecode(img) + img = nd.transpose(img, axes=(2, 0, 1)) + data[ii][:] = img + label[ii][:] = header.label + ii+=1 + while ii<_batch_size2: + data[ii][:] = data[0][:] + label[ii][:] = label[0][:] + ii+=1 + db = mx.io.DataBatch(data=(data,), label=(label,)) + model.forward(db, is_train=False) + net_out = model.get_outputs() + net_out = net_out[0].asnumpy() + if embeddings is None: + embeddings = np.zeros( (len(ocontents), net_out.shape[1])) + embeddings[ba:bb,:] = net_out[0:_batch_size,:] + ba = bb + embeddings = sklearn.preprocessing.normalize(embeddings) + emb_mean = np.mean(embeddings, axis=0, keepdims=True) + emb_mean = sklearn.preprocessing.normalize(emb_mean) + sim = np.dot(embeddings, emb_mean.T) + #print(sim.shape) + sims = sim.flatten() + assert len(_list)==len(sims) + assert len(_list)==len(ocontents) + for i in xrange(len(ocontents)): + _sim = sims[i] + _idx = _list[i] + outf.write("%d,%f\n"%(_idx, _sim)) + outf.close() + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='') + # general + parser.add_argument('--input', default='', type=str, help='') + parser.add_argument('--model', default='../model/softmax,50', help='path to load model.') + parser.add_argument('--batch-size', default=32, type=int, help='') + args = parser.parse_args() + main(args) + diff --git a/src/data/dataset_info.py b/src/data/dataset_info.py new file mode 100644 index 0000000..60c08dd --- /dev/null +++ b/src/data/dataset_info.py @@ -0,0 +1,40 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import os +import sys +import mxnet as mx +from mxnet import ndarray as nd +import random +import argparse +import cv2 +import time +import sklearn +from sklearn.decomposition import PCA +from easydict import EasyDict as edict +from sklearn.cluster import DBSCAN +import numpy as np + +sys.path.append(os.path.join(os.path.dirname(__file__),'..', 'common')) +import face_image + + +def main(args): + path_imgrec = os.path.join(args.input, 'train.rec') + path_imgidx = os.path.join(args.input, 'train.idx') + imgrec = mx.recordio.MXIndexedRecordIO(path_imgidx, path_imgrec, 'r') # pylint: disable=redefined-variable-type + s = imgrec.read_idx(0) + header, _ = mx.recordio.unpack(s) + assert header.flag>0 + print('header0 label', header.label) + header0 = (int(header.label[0]), int(header.label[1])) + print('identities', header0[1]-header0[0]) + print('images', header0[0]) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='') + # general + parser.add_argument('--input', default='', type=str, help='') + args = parser.parse_args() + main(args) + From ce17e1ac6f4fd7af77d09444608acfa8a6e7731d Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Mon, 5 Feb 2018 22:23:12 +0800 Subject: [PATCH 3/8] alignment3d init --- alignment3d/tfrecord2mx.py | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 alignment3d/tfrecord2mx.py diff --git a/alignment3d/tfrecord2mx.py b/alignment3d/tfrecord2mx.py new file mode 100644 index 0000000..c8d42d0 --- /dev/null +++ b/alignment3d/tfrecord2mx.py @@ -0,0 +1,47 @@ + +import tensorflow as tf +import cv2 +import sys +import mxnet as mx +import os +import numpy as np + +input_dir = sys.argv[1] +output_dir = './data' +writer = mx.recordio.MXIndexedRecordIO(os.path.join(output_dir, 'train.idx'), os.path.join(output_dir, 'train.rec'), 'w') + +idx = 1 +for _file in os.listdir(input_dir): + if not _file.endswith('tfrecords'): + continue + data_file = os.path.join(input_dir, _file) + for serialized_example in tf.python_io.tf_record_iterator(data_file): + example = tf.train.Example() + example.ParseFromString(serialized_example) + features = example.features.feature + image = features['image'].bytes_list.value[0] + width = features['width'].int64_list.value[0] + height = features['height'].int64_list.value[0] + image = np.fromstring(image, dtype=np.uint8) + image = cv2.imdecode(image, cv2.CV_LOAD_IMAGE_COLOR) + #print(image.shape) + n_landmarks = features['n_landmarks'].int64_list.value[0] + mask_index = features['mask_index'].bytes_list.value[0] + status = features['status'].int64_list.value[0] + gt_mask = features['gt_mask'].bytes_list.value[0] + gt_mask = np.fromstring(gt_mask, dtype=np.uint8) + #print(gt_mask.shape) + gt_pts = features['gt_pts'].bytes_list.value[0] + gt_pts = np.fromstring(gt_pts, dtype=np.float32) + #print(gt_pts.shape, n_landmarks) + #print(gt_pts) + #for k in features: + # print(k) + + #print(len(image),width, height, n_landmarks, status, gt_mask, gt_pts) + nlabel = list(gt_pts) + nheader = mx.recordio.IRHeader(0, nlabel, idx, 0) + s = mx.recordio.pack_img(nheader, image, quality=95, img_fmt='.jpg') + writer.write_idx(idx, s) + idx+=1 + From 2a1bae5d0b094a43954601d5d75241b57ac824eb Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Wed, 7 Feb 2018 22:01:30 +0800 Subject: [PATCH 4/8] add c2c-mode --- src/common/face_preprocess.py | 4 +- src/data.py | 69 +++++++++++++++++++++++++++++++++-- src/data/dataset_c2c.py | 6 ++- src/data/face2rec2.py | 18 ++++++--- src/train_softmax.py | 4 ++ 5 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/common/face_preprocess.py b/src/common/face_preprocess.py index 476c591..0b59828 100644 --- a/src/common/face_preprocess.py +++ b/src/common/face_preprocess.py @@ -6,9 +6,7 @@ from skimage import transform as trans def parse_lst_line(line): vec = line.strip().split("\t") assert len(vec)>=3 - aligned = False - if int(vec[0])>0: - aligned = True + aligned = int(vec[0]) image_path = vec[1] label = int(vec[2]) bbox = None diff --git a/src/data.py b/src/data.py index 958edb8..4ead151 100644 --- a/src/data.py +++ b/src/data.py @@ -59,7 +59,7 @@ class FaceImageIter(io.DataIter): path_imgrec = None, shuffle=False, aug_list=None, mean = None, rand_mirror = False, - c2c_threshold = 0.0, output_c2c = 0, + c2c_threshold = 0.0, output_c2c = 0, c2c_mode = -10, ctx_num = 0, images_per_identity = 0, data_extra = None, hard_mining = False, triplet_params = None, coco_mode = False, mx_model = None, @@ -74,6 +74,7 @@ class FaceImageIter(io.DataIter): s = self.imgrec.read_idx(0) header, _ = recordio.unpack(s) self.idx2cos = {} + self.idx2flag = {} self.idx2meancos = {} self.c2c_auto = False if output_c2c or c2c_threshold>0.0: @@ -82,7 +83,11 @@ class FaceImageIter(io.DataIter): if os.path.exists(path_c2c): for line in open(path_c2c, 'r'): vec = line.strip().split(',') - self.idx2cos[int(vec[0])] = float(vec[1]) + idx = int(vec[0]) + self.idx2cos[idx] = float(vec[1]) + self.idx2flag[idx] = 1 + if len(vec)>2: + self.idx2flag[idx] = int(vec[2]) else: self.c2c_auto = True self.c2c_step = 10000 @@ -91,10 +96,65 @@ class FaceImageIter(io.DataIter): self.header0 = (int(header.label[0]), int(header.label[1])) #assert(header.flag==1) self.imgidx = range(1, int(header.label[0])) - if c2c_threshold>0.0: + if c2c_mode==0: imgidx2 = [] for idx in self.imgidx: c = self.idx2cos[idx] + f = self.idx2flag[idx] + if f!=1: + continue + imgidx2.append(idx) + print('idx count', len(self.imgidx), len(imgidx2)) + self.imgidx = imgidx2 + elif c2c_mode==1: + imgidx2 = [] + for idx in self.imgidx: + c = self.idx2cos[idx] + f = self.idx2flag[idx] + if f==2 and c>=0.05: + continue + imgidx2.append(idx) + print('idx count', len(self.imgidx), len(imgidx2)) + self.imgidx = imgidx2 + elif c2c_mode==2: + imgidx2 = [] + for idx in self.imgidx: + c = self.idx2cos[idx] + f = self.idx2flag[idx] + if f==2 and c>=0.1: + continue + imgidx2.append(idx) + print('idx count', len(self.imgidx), len(imgidx2)) + self.imgidx = imgidx2 + elif c2c_mode==-1: + imgidx2 = [] + for idx in self.imgidx: + c = self.idx2cos[idx] + f = self.idx2flag[idx] + if f==2: + continue + if c<0.1: + continue + imgidx2.append(idx) + print('idx count', len(self.imgidx), len(imgidx2)) + self.imgidx = imgidx2 + elif c2c_mode==-2: + imgidx2 = [] + for idx in self.imgidx: + c = self.idx2cos[idx] + f = self.idx2flag[idx] + if f==2: + continue + if c<0.2: + continue + imgidx2.append(idx) + print('idx count', len(self.imgidx), len(imgidx2)) + self.imgidx = imgidx2 + elif c2c_threshold>0.0: + imgidx2 = [] + for idx in self.imgidx: + c = self.idx2cos[idx] + f = self.idx2flag[idx] if c=0: identities.append( (last[1], _id) ) - last[0] = item.label + last[0] = label last[1] = _id _id+=1 identities.append( (last[1], _id) ) item = edict() - item.flag = 1 + item.flag = 2 item.id = 0 item.label = [float(_id), float(_id+len(identities))] yield item @@ -82,6 +83,7 @@ def read_list(path_in): def image_encode(args, i, item, q_out): oitem = [item.id] + #print('flag', item.flag) if item.flag==0: fullpath = item.image_path header = mx.recordio.IRHeader(item.flag, item.label, item.id, 0) @@ -97,7 +99,7 @@ def image_encode(args, i, item, q_out): img = face_preprocess.preprocess(img, bbox = item.bbox, landmark=item.landmark, image_size='%d,%d'%(args.image_h, args.image_w)) s = mx.recordio.pack_img(header, img, quality=args.quality, img_fmt=args.encoding) q_out.put((i, s, oitem)) - else: #flag==1 or 2 + else: header = mx.recordio.IRHeader(item.flag, item.label, item.id, 0) #print('write', item.flag, item.id, item.label) s = mx.recordio.pack(header, '') @@ -133,6 +135,7 @@ def write_worker(q_out, fname, working_dir): s, item = buf[count] del buf[count] if s is not None: + #print('write idx', item[0]) record.write_idx(item[0], s) if count % 1000 == 0: @@ -250,7 +253,9 @@ if __name__ == '__main__': image_encode(args, i, item, q_out) if q_out.empty(): continue - _, s, _ = q_out.get() + _, s, item = q_out.get() + #header, _ = mx.recordio.unpack(s) + #print('write header label', header.label) record.write_idx(item[0], s) if cnt % 1000 == 0: cur_time = time.time() @@ -259,3 +264,4 @@ if __name__ == '__main__': cnt += 1 if not count: print('Did not find and list file with prefix %s'%args.prefix) + diff --git a/src/train_softmax.py b/src/train_softmax.py index a0267a6..ef4213b 100644 --- a/src/train_softmax.py +++ b/src/train_softmax.py @@ -143,6 +143,8 @@ def parse_args(): help='') parser.add_argument('--c2c-threshold', type=float, default=0.0, help='') + parser.add_argument('--c2c-mode', type=int, default=-10, + help='') parser.add_argument('--output-c2c', type=int, default=0, help='') parser.add_argument('--margin', type=int, default=4, @@ -673,6 +675,7 @@ def train_net(args): mean = mean, c2c_threshold = args.c2c_threshold, output_c2c = args.output_c2c, + c2c_mode = args.c2c_mode, ctx_num = args.ctx_num, images_per_identity = args.images_per_identity, data_extra = data_extra, @@ -695,6 +698,7 @@ def train_net(args): mean = mean, c2c_threshold = args.c2c_threshold, output_c2c = args.output_c2c, + c2c_mode = args.c2c_mode, ctx_num = args.ctx_num, images_per_identity = args.images_per_identity, data_extra = data_extra, From bc4b9dc684ea554fc07be8dedf1a4111fcb2a357 Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Wed, 7 Feb 2018 22:01:45 +0800 Subject: [PATCH 5/8] tiny --- src/align/align_celeb.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/align/align_celeb.py b/src/align/align_celeb.py index c4007f1..ae83b06 100644 --- a/src/align/align_celeb.py +++ b/src/align/align_celeb.py @@ -101,7 +101,8 @@ def main(args): pnet, rnet, onet = detect_face.create_mtcnn(sess, None) minsize = 100 # minimum size of face - threshold = [ 0.6, 0.7, 0.7 ] # three steps's threshold + #threshold = [ 0.6, 0.7, 0.7 ] # three steps's threshold + threshold = [ 0.6, 0.6, 0.3 ] # three steps's threshold factor = 0.709 # scale factor print(minsize) @@ -126,8 +127,9 @@ def main(args): v = datamap.get(person, None) if v is None: continue - if not img_id in v[1]: - continue + #TODO + #if not img_id in v[1]: + # continue labelid = v[0] img_str = base64.b64decode(vec[-1]) nparr = np.fromstring(img_str, np.uint8) @@ -148,7 +150,8 @@ def main(args): if fimage.bbox is not None: _bb = fimage.bbox _minsize = min( [_bb[2]-_bb[0], _bb[3]-_bb[1], img.shape[0]//2, img.shape[1]//2] ) - + else: + _minsize = min(img.shape[0]//5, img.shape[1]//5) bounding_boxes, points = detect_face.detect_face(img, _minsize, pnet, rnet, onet, threshold, factor) bindex = -1 nrof_faces = bounding_boxes.shape[0] From 666f019f534e25998ca3b51621864f28f8390853 Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Wed, 7 Feb 2018 22:19:33 +0800 Subject: [PATCH 6/8] lt13 --- src/train_softmax.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/train_softmax.py b/src/train_softmax.py index ef4213b..3b7545e 100644 --- a/src/train_softmax.py +++ b/src/train_softmax.py @@ -445,6 +445,21 @@ def get_symbol(args, arg_params, aux_params): triplet_loss = mx.symbol.mean(triplet_loss) #triplet_loss = mx.symbol.sum(triplet_loss)/(args.per_batch_size//3) extra_loss = mx.symbol.MakeLoss(triplet_loss) + elif args.loss_type==13: #triplet loss with insightface margin + nembedding = mx.symbol.L2Normalization(embedding, mode='instance', name='fc1n') + anchor = mx.symbol.slice_axis(nembedding, axis=0, begin=0, end=args.per_batch_size//3) + positive = mx.symbol.slice_axis(nembedding, axis=0, begin=args.per_batch_size//3, end=2*args.per_batch_size//3) + negative = mx.symbol.slice_axis(nembedding, axis=0, begin=2*args.per_batch_size//3, end=args.per_batch_size) + ap = anchor * positive + an = anchor * negative + ap = mx.symbol.sum(ap, axis=1, keepdims=1) #(T,1) + an = mx.symbol.sum(an, axis=1, keepdims=1) #(T,1) + ap = mx.symbol.arccos(ap) + an = mx.symbol.arccos(an) + triplet_loss = mx.symbol.Activation(data = (ap-an+args.margin_m), act_type='relu') + triplet_loss = mx.symbol.mean(triplet_loss) + #triplet_loss = mx.symbol.sum(triplet_loss)/(args.per_batch_size//3) + extra_loss = mx.symbol.MakeLoss(triplet_loss) elif args.loss_type==9: #coco loss centroids = [] for i in xrange(args.per_identities): @@ -532,7 +547,7 @@ def train_net(args): os.environ['BETA'] = str(args.beta) data_dir_list = args.data_dir.split(',') - if args.loss_type!=12: + if args.loss_type!=12 and args.loss_type!=13: assert len(data_dir_list)==1 data_dir = data_dir_list[0] args.use_val = False @@ -571,7 +586,7 @@ def train_net(args): args.images_per_identity = 2 elif args.loss_type==10 or args.loss_type==9: args.images_per_identity = 16 - elif args.loss_type==12: + elif args.loss_type==12 or args.loss_type==13: args.images_per_identity = 5 assert args.per_batch_size%3==0 assert args.images_per_identity>=2 @@ -626,7 +641,7 @@ def train_net(args): for i in xrange(args.per_identities): data_extra[c+i][i] = 1.0 c+=args.per_batch_size - elif args.loss_type==12: + elif args.loss_type==12 or args.loss_type==13: triplet_params = [args.triplet_bag_size, args.triplet_alpha, args.triplet_max_ap] elif args.loss_type==9: coco_mode = True @@ -665,7 +680,7 @@ def train_net(args): else: val_dataiter = None - if len(data_dir_list)==1 and args.loss_type!=12: + if len(data_dir_list)==1 and args.loss_type!=12 and args.loss_type!=13: train_dataiter = FaceImageIter( batch_size = args.batch_size, data_shape = data_shape, @@ -727,7 +742,7 @@ def train_net(args): _rescale = 1.0/args.ctx_num opt = optimizer.SGD(learning_rate=base_lr, momentum=base_mom, wd=base_wd, rescale_grad=_rescale) som = 20 - if args.loss_type==12: + if args.loss_type==12 or args.loss_type==13: som = 2 _cb = mx.callback.Speedometer(args.batch_size, som) From 7617d676ca2782dc321636ad7d1ceec2bf809aed Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Wed, 7 Feb 2018 23:49:27 +0800 Subject: [PATCH 7/8] lt13 --- src/train_softmax.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/train_softmax.py b/src/train_softmax.py index 3b7545e..eec7ac3 100644 --- a/src/train_softmax.py +++ b/src/train_softmax.py @@ -446,6 +446,9 @@ def get_symbol(args, arg_params, aux_params): #triplet_loss = mx.symbol.sum(triplet_loss)/(args.per_batch_size//3) extra_loss = mx.symbol.MakeLoss(triplet_loss) elif args.loss_type==13: #triplet loss with insightface margin + m = args.margin_m + sin_m = math.sin(m) + cos_m = math.cos(m) nembedding = mx.symbol.L2Normalization(embedding, mode='instance', name='fc1n') anchor = mx.symbol.slice_axis(nembedding, axis=0, begin=0, end=args.per_batch_size//3) positive = mx.symbol.slice_axis(nembedding, axis=0, begin=args.per_batch_size//3, end=2*args.per_batch_size//3) @@ -454,11 +457,17 @@ def get_symbol(args, arg_params, aux_params): an = anchor * negative ap = mx.symbol.sum(ap, axis=1, keepdims=1) #(T,1) an = mx.symbol.sum(an, axis=1, keepdims=1) #(T,1) - ap = mx.symbol.arccos(ap) - an = mx.symbol.arccos(an) - triplet_loss = mx.symbol.Activation(data = (ap-an+args.margin_m), act_type='relu') + #ap = mx.symbol.arccos(ap) + #an = mx.symbol.arccos(an) + #triplet_loss = mx.symbol.Activation(data = (ap-an+args.margin_m), act_type='relu') + body = ap*ap + body = 1.0-body + body = mx.symbol.sqrt(body) + body = body*sin_m + ap = ap*cos_m + ap = ap-body + triplet_loss = mx.symbol.Activation(data = (an-ap), act_type='relu') triplet_loss = mx.symbol.mean(triplet_loss) - #triplet_loss = mx.symbol.sum(triplet_loss)/(args.per_batch_size//3) extra_loss = mx.symbol.MakeLoss(triplet_loss) elif args.loss_type==9: #coco loss centroids = [] From ed34092f5e773703c6e120d7cbc906dee1ab31d9 Mon Sep 17 00:00:00 2001 From: Jia Guo Date: Thu, 8 Feb 2018 14:01:16 +0800 Subject: [PATCH 8/8] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 26f72ec..30cd605 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ ### Recent Update +  **`2018.02.07`**: We evaluate LFW,CFP,AgeDB-30 again after removing training set overlaps, the results almost stay the same. See [Results](#results) for detail. + **`2018.01.30`**: We provide a *LResNet50E-IR* model which can achieve **`99.80@LFW`** and **`97.64%`** at MegaFace 1M Acc. See [Pretrained-Models](#pretrained-models) for detail. **`2018.01.29`**: Caffe *LResNet34E-IR* model is available now. We get it by converting original MXNet model to Caffe format but there's some performance drop. See [Pretrained-Models](#pretrained-models) for detail. @@ -276,7 +278,13 @@ export MXNET_ENGINE_TYPE=ThreadedEnginePerDevice | ------- | ------ | --------- | --------- | ----------- | ------------- | | Ours | 99.7+ | 99.6+ | 97.1+ | 95.7+ | - | + We report the verification accuracy/performance after removing training set overlaps, to make our results more stable and reliable. `(C) means after cleaning` +| Dataset | Identities | Images | Identites(C) | Images(C) | Acc | Acc(C) | +| -------- | ---------- | ------- | ------------ | --------- | ----- | ------ | +| LFW | 85742 | 3850179 | 80995 | 3586128 | 99.83 | 99.81 | +| CFP-FP | 85742 | 3850179 | 83706 | 3736338 | 94.04 | 94.03 | +| AgeDB-30 | 85742 | 3850179 | 83775 | 3761329 | 98.08 | 97.87 | ### Contribution - Any type of PR or third-party contribution are welcome.