728x90
1. Cosin Similarity Loss
import torch
import torch.nn.functional as F
import torch.nn as nn
feat1 = torch.tensor([0.1,0.2,0.3], dtype=torch.float32)
feat2 = torch.tensor([0.4,0.2,0.5], dtype=torch.float32)
loss = F.cosine_similarity(feat1, feat2) # negative cosin loss
loss = -F.cosine_similarity(feat1, feat2) # positive cosin loss
- 두 벡터(feature)를 가깝게 혹은 멀게 만들도록 학습합니다.
- 저는 여기서 의문점이 들었습니다.
- 위의 수식의 역전파는 어떻게 흐를까? 어떤 값들이 흐르도록 유도될까?
- 직관적으로 코사인유사도는 방향성에대해서 -1~1사이로 나타내지만, 과연 학습을 통해서 어떠한 미분값이 흘러 두 벡터가 유사해지도록 만드는 것일까?
- 미분값을 수식적으로 이해하고 경향을 파악하고 싶었습니다. 마치 "MSE loss (y,t)는 직관적으로 예측값(y)과 정답(t)과의 차이를 미분값으로 전파하여, 둘의 차이를 좁히는 방향으로 즉, loss가 최소가 되도록 weight를 학습하는 구나"라고 이해하는 것처럼 말입니다.
2. Backpropagation of Cosin Similarity Loss
- 미분값을 직접 손으로 편미분하여 구하고자 하였지만, 부족한 수학실력은 시간만 낭비해 버렸습니다.
- 구글링을 통해 아래와 같이 미분과정을 알 수 있었습니다.
- 간단히 notation은 다음과 같습니다.
a, b : n-dim vector로써 여러 값들을 가지고 있습니다. a1은 첫번째 인자 값입니다.
|a|, |b| : L2-norm(a, p=2)를 수행한 것입니다. 정규화라고 생각하시면 되겠습니다. - 현재 위의 수식은 negative cosin sim을 나타내고 있습니다. loss로 positive (두 벡터가 유사해지도록)로 사용하기 위해서는 마이너스(-)를 곱해주어야 합니다.
- 왜 마이너스를 곱해야 하는가에 대해서 명확히 말씀드릴 수는 없습니다만, 학습시에 loss는 최소가 되는 방향으로 흐르기 때문에, 마이너스를 곱하지 않으면 두 벡터의 유사도 값은 1에 가깝게 되기보다는 0에 가깝게 되서 멀어지게 학습이 됩니다. 반면에, 마이너스를 곱하면, 두 벡터의 유사도 값이 -1에 가까울수록(loss가 최소가 될수록) 두 벡터는 비슷하게 표현되는 것입니다.
- 위 수식의 미분값을 pytorch 코드로 진짜 맞는지 아래와 같이 검증해보았습니다.
3. Pytorch for Backpropagation of Cosin Similarity Loss
- 아래와 같은 코드를 수행하고, 디버그로 params의 grad 값과, 수식을 그대로 적용한 grad_y_all의 값을 비교하였습니다.
import torch
import torch.nn.functional as F
import torch.nn as nn
# input
x = torch.tensor([[0.4, 0.2], [0.3, 0.7]], dtype=torch.float32)
# trainable params setting
weights = torch.zeros((2, 2),dtype=torch.float32)
params = nn.Parameter(weights)
# forward
y = x + params
# L2-normalize
y_L2_normalize = F.normalize(y, dim=1, p=2)
# cosin-similarity distance
dist = torch.matmul(y_L2_normalize[0], y_L2_normalize[1].t())
# backpropagation
loss = dist # negative cosin-sim loss, 두 벡터 유사하지 않도록 함.
#loss = -dist # positive cosin-sim loss, 두 벡터 유사해지도록함.
loss.backward()
# 미분 수식 맞는지 확인
grad_y_all = torch.zeros((2,2), dtype=torch.float32)
L2_norm = torch.norm(y, dim=1, p=2)
grad_y_all[0][0] = y[1][0]/(L2_norm[0]*L2_norm[1]) - dist*y[0][0]/(L2_norm[0]*L2_norm[0])
grad_y_all[1][0] = y[0][1]/(L2_norm[0]*L2_norm[1]) - dist*y[1][0]/(L2_norm[1]*L2_norm[1])
grad_y_all[0][1] = y[1][1]/(L2_norm[0]*L2_norm[1]) - dist*y[0][1]/(L2_norm[0]*L2_norm[0])
grad_y_all[1][1] = y[0][1]/(L2_norm[0]*L2_norm[1]) - dist*y[1][1]/(L2_norm[1]*L2_norm[1])
- 결론적으로, 수식과 동일한 gradient가 pytorch에서도 흐르는 것을 확인하였습니다.
4. 고찰
- 그렇다면 저 복잡한 수식의 값이 역전파로 흘러서 두 feature가 유사해지도록 학습이 될텐데, 직관적으로는 납득이 가지 않았습니다. 사실 지금도 고민중입니다... ㅎㅎ ㅠㅠ
- 여러 의견을 공유하면 좋겠습니다
728x90
'Deep Learning (AI) > Training Loss' 카테고리의 다른 글
[Pytorch] Negative Pair Loss 코드 (4) | 2023.07.21 |
---|---|
[Pytorch] L1-loss, L2-loss, KLD-loss 역전파 (13) | 2023.07.20 |
[Pytorch] Softmax with CrossEntropyLoss 역전파 (3) | 2023.07.20 |
[Pytorch] torch kld-loss (KD-loss) 코드 (7) | 2023.07.14 |