Cosine Similarity (코사인 유사도) 는 아주아주 유명한 개념이다. 두 벡터 간 내적을 각 벡터의 거리 (Norm) 의 곱으로 나누어 주면 두 벡터 간 cosine 각도가 나온다는 것이고, 이는 사실 고등학교 삼각함수에도 나오는 내용이다.
딥러닝에서 Cosine Similarity의 활용도는 정말 무궁무진한데 대표적인 것이 Transformer의 Attention에서 Dot Product를 하는 경우다. Cosine Similarity는 계산 과정에서 (1) Dot Product와 (2) Norm의 곱으로 나누어주는 과정, 두 가지가 필요한데 이중 (2)는 많은 딥러닝 Application에서 생략될 수 있는 것이 일반적으로 Feature를 L2-Normalization을 거치게 해 연산 과정을 생략시킬 수 있기 때문이다.
이외에도 개념적인 부분을 짚기에는 너무도 많은 글들이 Cosine Similarity에 대해 잘 설명하고 있어서 굳이 그럴 필요는 없겠다 싶다. 이 글은 -1~1의 치역을 가지는 Cosine Similarity 함수의 Bounds를 0~1로 바꾸는 방식을 소개하고, 이를 코딩으로 구현한다. Bounds를 0에서 1로 바꾸는 것은 다양한 상황에서 이용될 수 있는데, angle 기준으로 먼 벡터에 대해서 더 낮은 값을 (0에 가깝게) 부여하고 angle 기준으로 가까운 벡터에 더 높은 값을 (1에 가깝게) 부여하고자 하는 것이다.
일반적인 Cosine Similarity에서는 이므로 같은 방향에 있는 벡터는 1로 표현된다. 이므로 Orthogonal, 즉 관계가 없는 벡터들의 유사도는 0으로 표현된다. 마지막으로 이므로 정 반대에 있는 벡터들의 유사도는 -1로 표현된다.
arccos 함수는 -1~1의 정의역에 대해 0~pi 까지의 치역을 가지는 함수이므로 이를 pi로 나누어 normalize를 해 주면 angular distance를 구할 수 있게 된다. distance의 반대가 similarity이므로 우리는 1에서 Distance를 빼주어서 Similarity인 를 구해낼 수 있다.
def compute_cosine_similarity(matrix_a, matrix_b):
# matrix_a.shape, matrix_b.shape = [n_features, sequence_length]
cos_sim = cosine_similarity(matrix_a, matrix_b)[0][0]
cos_sim = np.clip(cos_sim, -1 + 1e-6, 1 - 1e-6)
# To mitigate numerical instability
angular_dis = np.arccos(cos_sim) / np.pi
return 1 - angular_dis
가운데 clip 메소드를 이용한 것은 -1과 1에 정의역이 가까울 때 arccos 함수의 계산 과정에서 numerical instability가 자주 발생하기 때문이다. 매우 작은 값을 이용해 clipping 해주어서 계산 과정을 stabilize한다.
벡터 간 angular distance가 멀수록 (에 가까울수록) 0에 가깝게 유사도를 맵핑하는 Angular similarity를 구현한다.