Python/Pytorch

[Pytorch] BCELoss, BCEWithLogitsLoss, CrossEntropyLoss

언킴 2022. 9. 3. 16:39
반응형

Contents

     

     

    PyTorch 에서는 이진 분류(Binary Classification)를 할 때 목적 함수 혹은 손실 함수로 사용할 수 있는 BCELoss와 BCEWithLogitLoss 가 존재한다. 두 함수는 모두 모델이 예측한 값과 실제 값 간의 차이를 비교하는 함수지만, 조금 다른 방식으로 계산된다. 두 함수의 차이점에 대해서 알아보자. 

     

    BCELoss

    BCELoss는 모델의 구조 상에 마지막 Layer가 Sigmoid 혹은 Softmax로 되어 있는 경우 이를 사용한다. 즉, 모델의 출력이 각 라벨에 대한 확률값으로 구성되었을 때 사용이 가능하다. 

    import torch 
    import torch.nn as nn 
    
    m = nn.Sigmoid()
    bce_criterion = nn.BCELoss()
    target = torch.ones([10], dtype=torch.float32)
    inputs = torch.full([10], 1.3)
    
    logit_inputs = m(inputs)
    bce_criterion(logit_inputs, target)
    
    # tensor(0.2410)

    이때 inputs은 logitstic을 거치지 않았기 때문에 확률 값이 아니라 그 값 그대로 출력이 된다. 이때 nn.Sigmoid를 사용해서 각각의 x가 1일 확률에 대한 값을 반환한 후 Loss를 측정하여야 한다. 

     

    BCEWithLogitsLoss 

    BCEWithLogitsLoss는 이름에서도 유추해볼 수 있듯 BCELoss를 위 과정에서 확률값(Logits)으로 변환하지 않더라도 계산되는 것을 의미한다. 

     

    target = torch.ones([10], dtype=torch.float32)
    inputs = torch.full([10], 1.3)
    logit_criterion = torch.nn.BCEWithLogitsLoss()
    logit_criterion(inputs, target)
    
    # tensor(0.2410)

     

    결과적으로 두 함수의 결과값은 동일하게 나온다. 모델을 학습할 때 모델의 구조를 확인하고 마지막에 Softmax 혹은 Sigmoid로 되어서 확률값으로 도출이 된다면 BCELoss를 사용하는 것이 적합하고, 확률 값이 아니라면, 확률 값으로 변환해주는 BCEWithLogitsLoss를 사용하는 것이 적합하다. 

     

     

    CrossEntropyLoss

    이전에 다룬 BCELoss와 BCEWithLogitsLoss는 Binary Classification을 위한 손실 함수다. 반면에 CrossEntropyLoss는 다중 분류를 위한 손실 함수다. 예를 들어, 라벨이 5개라고 한다면 입력은 각 라벨에 대한 확률값을 표현하고, 정답 라벨은 라벨 값 혹은 라벨에 대한 확률값으로 표현할 수 있다. 

     

    import torch
    import torch.nn as nn 
    
    CE_loss = nn.CrossEntropyLoss()
    inputs = torch.randn(3, 5, requires_grad=True)
    label_target = torch.empty(3, dtype=torch.long).random_(5)
    output = CE_loss(inputs, label_target)
    
    
    print(inputs)
    print(label_target)
    
    print(output)
    
    m = nn.Sigmoid()
    output = CE_loss(m(inputs), label_target)
    print(output)
    
    # tensor([[ 0.0893,  0.6787,  1.0965, -0.8594,  1.0114],
    #         [-1.6852, -0.4836,  0.8726,  0.5809, -1.7477],
    #         [-2.4419,  1.0551, -1.0063,  0.2250,  0.1060]], requires_grad=True)
    # tensor([0, 1, 4])
    # tensor(1.9633, grad_fn=<NllLossBackward0>)
    # tensor(1.6332, grad_fn=<NllLossBackward0>)

     

    CrossEntopyLoss는 내부적으로 LogSoftmax와 NLLLoss가 결합된 형태로 되어 있다.만약 해당 값을 계산할 때 Softmax를 한 번 더 사용하게 되면 Softmax가 두 번 적용되면서 Loss 값을 계산하는 것에 있어 오류가 생성된다. CrossEntropyLoss의 수식은 아래와 같다.

    \[ l_n = -w_{y_n} \log \frac{\exp(x_{n, y_n})}{\sum^C_{c=1} \exp(x_{n,c})} \]

    LogSoftmax는 입력값에 Softmax를 취하고, Log를 취한 것을 의미하며, NLLLoss는 Negative Log Likelihood를 의미한다. 

     

    # Example of target with class probabilities
    inputs = torch.randn(3, 5, requires_grad=True)
    prob_target = torch.randn(3, 5).softmax(dim=1)
    output = CE_loss(inputs, prob_target)
    output.backward()

    이전 경우처럼 정답 라벨이 1, 2, 3과 같이 정수형태로 되어 있는 경우가 있을 수도 있고, 위 경우처럼 확률값으로 나타나 있을 수도 있다. 모델의 출력 혹은 데이터셋에 따라 적절히 맞는 손실 함수를 사용하면 된다.