arcface论文:https://arxiv.org/pdf/1801.07698.pdf
说明文档:https://www.cnblogs.com/darkknightzh/p/8525287.html
tensorflow代码实现
参考流程图:
import tensorflow as tf
import math
#未考虑margin_b的情况,基本与下一个函数类似,可优先采用combine_loss_val,合理设置margin_a, #margin_m, margin_b, s四个参数即可
def arcface_loss(embedding, labels, w_init, out_num, s=64., m=0.5):
'''
:param embedding: the input embedding vectors
:param labels: the input labels, the shape should be eg: (batch_size, 1)
:param s: scalar value default is 64
:param out_num: output class num
:param m: the margin value, default is 0.5
:return: the final cacualted output, this output is send into the tf.nn.softmax directly
'''
cos_m = math.cos(m)
sin_m = math.sin(m)
with tf.variable_scope('arcface_loss'):
# inputs and weights norm
embedding_norm = tf.norm(embedding, axis=1, keep_dims=True)
embedding = tf.div(embedding, embedding_norm, name='norm_embedding')
weights = tf.get_variable(name='embedding_weights', shape=(embedding.get_shape().as_list()[-1], out_num),
initializer=w_init, dtype=tf.float32)
weights_norm = tf.norm(weights, axis=0, keep_dims=True)
weights_unit = tf.div(weights, weights_norm, name='norm_weights')
# cos(theta+m)
cos_t = tf.matmul(embedding, weights_unit, name='cos_t')
cos_t2 = tf.square(cos_t, name='cos_2')
sin_t2 = tf.subtract(1., cos_t2, name='sin_2')
sin_t = tf.sqrt(sin_t2, name='sin_t')
cos_mt = s * tf.subtract(tf.multiply(cos_t, cos_m), tf.multiply(sin_t, sin_m), name='cos_mt')
mask = tf.one_hot(labels, depth=out_num, name='one_hot_mask')
# mask = tf.squeeze(mask, 1)
inv_mask = tf.subtract(1., mask, name='inverse_mask')
s_cos_t = tf.multiply(s, cos_t, name='scalar_cos_t')
updated_logits = tf.add(tf.multiply(s_cos_t, inv_mask), tf.multiply(cos_mt, mask), name='arcface_loss_output')
loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=updated_logits))
return loss, weights
def combine_loss_val(embedding, labels, w_init, out_num, margin_a, margin_m, margin_b, s):
'''
This code is contributed by RogerLo. Thanks for you contribution.
:param embedding: the input embedding vectors
:param labels: the input labels, the shape should be eg: (batch_size, 1)
:param s: scalar value default is 64
:param out_num: output class num
:param m: the margin value, default is 0.5
:return: the final cacualted output, this output is send into the tf.nn.softmax directly
'''
weights = tf.get_variable(name='embedding_weights', shape=(embedding.get_shape().as_list()[-1], out_num),
initializer=w_init, dtype=tf.float32)
weights_unit = tf.nn.l2_normalize(weights, axis=0)
embedding_unit = tf.nn.l2_normalize(embedding, axis=1) * s
cos_t = tf.matmul(embedding_unit, weights_unit)
ordinal = tf.constant(list(range(0, embedding.get_shape().as_list()[0])), tf.int64)
ordinal_y = tf.stack([ordinal, labels], axis=1)
sel_cos_t = tf.gather_nd(cos_t, ordinal_y)
if margin_a != 1.0 or margin_m != 0.0 or margin_b != 0.0:
if margin_a == 1.0 and margin_m == 0.0:
s_m = s * margin_b
new_zy = sel_cos_t - s_m
else:
cos_value = sel_cos_t / s
t = tf.acos(cos_value)
if margin_a != 1.0:
t = t * margin_a
if margin_m > 0.0:
t = t + margin_m
body = tf.cos(t)
if margin_b > 0.0:
body = body - margin_b
new_zy = body * s
updated_logits = tf.add(cos_t, tf.scatter_nd(ordinal_y, tf.subtract(new_zy, sel_cos_t), cos_t.get_shape()))
loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=updated_logits))
return loss, weights