BCELOSS

TEMP·2021년 10월 12일
1

Torch_LOSS

목록 보기
5/5

classification이나 segmenation인데 class가 두가지인 경우 즉, 이진분류이다.
물론 그냥 multiclass의 CEE를 사용해도 되지만 아마 timecost의 차이가 있을거다.
그것까지 해보겠다.

기본적으로 multiclass일때는 input shape에는 class가 있지만 target shape에는 class가 없다.
여기 이진분류에서는 input shape에도 class가 없다.
당연한데 어차피 loss에 들어갈 숫자는 probability이고 따라서 나머지의 class는 1-p 이다.
득, 정답의 확률을 아니까 오답의 확률도 안다는 거다.
따라서 model의 마지막 output shape에서 class을 1로 만들어 주고 필요하다면 squeeze까지 해줘야 한다.

torch.nn.BCELoss

https://pytorch.org/docs/stable/generated/torch.nn.BCELoss.html
class이다.

음 대충 문서를 보니 segmenatation이라면 (classification이면 flatten하면 됨.)
output shape = ( batch × R × B × G ) 이며 probability로 바꿔주는 과정이 필요하다.
target shape = ( batch × R × B × G ) 이며 target은 0또는 1만 가능하다.
또 CEE와는 다르게 dtype을 torch.long으로 바꿔줄 필요 없다.
(참고로 dtype바꾸는건 timecost 신경 안써도 될만큼 빠르다.)

예시를 해보자.

m = nn.Sigmoid()
loss = nn.BCELoss(reduction='mean')
output = torch.randn(64,112,112, requires_grad=True)
target = torch.empty(64,112,112).random_(2)
loss_value = loss(m(output), target)
loss_value.backward()

output.shape
----------------------------------------------------------
torch.Size([64, 112, 112])



target.shape
----------------------------------------------------------
torch.Size([64, 112, 112])




loss_value
----------------------------------------------------------
tensor(0.8067, grad_fn=<BinaryCrossEntropyBackward>)

추가적으로 위 예시에서 sum으로 바꾼다면 loss_value에 112×112×64를 나눠야 한다.

그래 뭐 이제 이정도는 쉽게 캐치할 수 있다.
source code를 보자.

https://pytorch.org/docs/stable/_modules/torch/nn/modules/loss.html#BCELoss

보니까 이 class의 실질적인 계산 함수는 F.binary_cross_entropy이다.
따라서 찾아보자.

https://pytorch.org/docs/stable/generated/torch.nn.functional.binary_cross_entropy.html
공식문서인데 뭐 class의 설명과 같다,
혹시 몰라 이의 source code를 보니 역시나 C-coding function인 torch._C._nn.binary_cross_entropy이다. 당연하게도 이를 찾지는 못한다. (찾을수는 있을지 몰라도 난 c 모른다.)

뭐 쉬워이니 직접 custom해보자.
이해하는데는 custom이 제일 좋은거 같다.

def custom_loss(output, target):
    tensor = -(target*torch.log(output) + (1-target)*torch.log(1-output))
    return torch.sum(tensor)


custom_loss(m(output), target)/112/112/64
---------------------------------------------------------------
tensor(0.8056, grad_fn=<DivBackward0>)

위에랑 값이 달라보이긴 하는데 rand때문에 그런거고 몇번 해보니 계속 같다.
즉, 잘만들었다.

참고로 train에 내가 만든건 안쓴다. 중간에 nan 나올꺼다 분명. 단지 이해해보고자 함이다.

torch.nn.BCEWithLogitsLoss

문서를 보기전에도 뭔지 알겠다.
아마 singmoid가 output에 적용되어 있을것이다.
즉 위에것은 model에 sigmoid가 있는경우 이건 없는경우 보통 후자를 사용하는거 같다.
굳이 model에서 probability를 계산할 필요가 없다. 예를들어 accuracy 계산하는데 필요 없지 않은가?
어차피 argmax 사용할껀데 굳이 왜 probability를 추가적으로 계산하는가.

https://pytorch.org/docs/stable/generated/torch.nn.BCEWithLogitsLoss.html
역시 Sigmoid layer and the BCELoss 를 합친거라고 쓰여있다.

source code를 보자.
https://pytorch.org/docs/stable/_modules/torch/nn/modules/loss.html#BCEWithLogitsLoss

실질계산함수는 F.binary_cross_entropy_with_logits이다. 이거를 보자. 사실 안봐도 위의 함수에 sigmoid 추가한것이다.

https://pytorch.org/docs/stable/generated/torch.nn.functional.binary_cross_entropy_with_logits.html

이건 뭐 custom 할필요도 없는게 위에걸 그대로 쓰되 sigmoide 추가해주면 된다.
해보자.

loss = nn.BCEWithLogitsLoss(reduction='mean')
output = torch.randn(64,112,112, requires_grad=True)
target = torch.empty(64,112,112).random_(2)
loss_value= loss(output, target)
loss_value

def custom_(output, target):
    output = torch.sigmoid(output)
    tensor = -(target*torch.log(output) + (1-target)*torch.log(1-output))
    return torch.sum(tensor)

custom_(output, target)/112/112/64

두개의 값이 같고 추가적으로 위의 두개포함해서 4개의 값이 모두 같다.
딱 sigmoid의 차이이다.

0개의 댓글