예전에 Curricular 관련 내용 중 구현된 코드를 찾을 수 없어 Tensorflow로 직접 구현했었다.
다시 정리하는 글을 쓰고자 한다.
- ArcFace = 코사인 유사도에서 True class에 대하여 마진 패널티 추가
- CurricularFace = ArcFace + (False class들 중에 hard sample 가중치 추가)
위와 같이 이해하였다.
코사인 유사도
- 코사인 유사도는 내적 공식으로 부터 얻을 수 있다.
- 내적 공식을 다음과 같다.
w⋅x=w1x1+w2x2+...=∣w∣∣x∣cosθ
- 즉, 두 벡터간 코사인 유사도 값은 다음과 같다.
cosθ=∣w∣∣x∣w⋅x
- 내적 연산은 matmul 연산으로 가능하다.
- 해당 코드 부분을 보면 다음과 같다.
x = tf.nn.l2_normalize(embedding, axis=1)
w = tf.nn.l2_normalize(self._w, axis=0)
cosine_sim = tf.matmul(x, w)
ArcFace (Positive Forward)
- True에 해당하는 클래스 처리 방법이다.
- 코사인 유사도 범위는 [-1, 1]이다.
- 두 벡터의 각도가 0이면 1, 2π이면 0, π이면 -1 이다.
- 코사인 유사도는 위 코드와 같이 임베딩 벡터와 W벡터가 된다. (W는 학습파라미터)
- 학습이 진행될 수록 W는 자리를 잡게 되고 그에 따라 임베딩도 변화한다.

def positive_forward(self, y_logit):
cosine_sim = y_logit
theta_margin = tf.math.acos(cosine_sim) + self.margin
y_logit_pos = tf.math.cos(theta_margin)
return y_logit_pos
Negative Forward
- False에 해당하는 클래스들의 처리방법이다.
- 마진을 추가하여 조정한 positive의 코사인 유사도 보다 False에 해당하는 class의 코사인 유사도가 높으면 그 해당 데이터는 hard sample로 간주한다.
- hard sample에 대한 negative forward의 수식이 바뀌는데 다음과 같다.

def negative_forward(self, y_logit_pos_masked, y_logit):
hard_sample_mask = y_logit_pos_masked < y_logit
y_logit_neg = tf.where(hard_sample_mask, tf.square(y_logit)+self.t*y_logit, y_logit)
return y_logit_neg
적응형 t 변수
- positive에 대한 코사인 유사도 값이 높을수록 t 변수는 높게 조정되어 Negative Forward에서 hard sample에 대한 가중치가 강화된다.
- t 변수 수식은 다음과 같다.

softmax head
- 코사인 유사도 범위는 [-1, 1]이므로 이를 바로 softmax 계산하면 좋지 않다.
- 따라서 보통 30 정도 scale 값을 곱해준다.
- 여기서 더 작은 값을 곱하면, calibration 효과가 있다고 한다.

Softmax VS Softmax with CurricularFace
- 오른쪽이 curricular_face를 적용한 결과이다.
- 자세한 코드는 아래 깃허브 참조

Reference