[머신러닝] 손실함수의 종류

전래창·2021년 11월 11일
0

머신러닝

목록 보기
9/11

손실함수(loss function)

  • 손실 함수(loss function)란?
    머신러닝 혹은 딥러닝 모델의 출력값과 사용자가 원하는 출력값의 오차를 의미

손실함수는 정답(y)와 예측(^y)를 입력으로 받아 실숫값 점수를 만드는데, 이 점수가 높을수록 모델이 안좋은 것

손실함수의 함수값이 최소화 되도록 하는 가중치(weight)와 편향(bias)를 찾는 것이 딥러닝 학습의 목표

손실함수의 종류

MSE(Mean Squared Error)

예측한 값과 실제 값 사이의 평균 제곱 오차를 정의한다. 공식이 매우 간단하며, 차가 커질수록 제곱 연산으로 인해서 값이 더욱 뚜렷해진다. 그리고 제곱으로 인해서 오차가 양수이든 음수이든 누적 값을 증가시킨다.

RMSE(Root Mean Squared Error)

MSE에 루트(√)를 씌운 것으로 MSE와 기본적으로 동일하다. MSE 값은 오류의 제곱을 구하기 때문에 실제 오류 평균보다 더 커지는 특성이 있어 MSE에 루트를 씌운 RMSE 은 값의 왜곡을 줄여준다.

Binary Crossentropy

MSE와 RMSE와 다르게 Crossentropy는 개념은 추후의 포스트에서 설명하기로 한다.

실제 레이블과 예측 레이블 간의 교차 엔트로피 손실을 계산한다. 2개의 레이블 클래스(0, 1로 가정)가 있을 때 Binary Crossentropy를 사용하면 좋다.
활성화 함수 : sigmoid 사용 (출력값이 0과 1사이의 값)
수식은 아래와 같으며
아래 함수에 예측값(Yi) 과 실제값(ti) 에 1을 대입하면, 수식은 0에 수렴하게 됨
아래 함수에 예측값(Yi =0)과 실제값(ti = 1)을 대입한다면, 수식은 양의 무한대가 됨

  • 케라스에서 지원하는 binary crossentropy
import tensorflow as tf
import keras

tf.keras.losses.BinaryCrossentropy(from_logits=False, label_smoothing=0, reduction="auto", name="binary_crossentropy")
  • pytorch에서 지원하는 binary crossentropy
    신경망의 출력을 가장한 랜덤 벡터에 시그모이드 활성화 함수를 적용해 이진 벡터인 probabilities를 만든다. 그 다음 target을 0과 1로 이루어진 벡터로 만들어서 손실을 계산한다.
import torch
import torch.nn as nn

bce_loss = nn.BCELoss()
sigmoid = nn.Sigmoid()
probabilities = sigmoid(torch.randn(4, 1, requires_grad=True)
targets = torch.tensor([1, 0, 1, 0], dtype = torch.float32).view(4, 1)
loss = bce_loss(probabilities, targets)
print(probabilities)
print(loss)

Categorical Crossentropy

레이블 클래스가 2개 초과일 경우 binary가 아닌 categorical을 사용한다. 보통 softmax 활성화함수 다음에 연계되어 사용되므로 softmax loss로도 불린다.
출력을 클래스 소속 확률에 대한 예측으로 이해할 수 있는 문제에서 사용
라벨이 (0,0,1,0,0) , (0,1,0,0,0) 과 같이 one-hot encoding 된 형태로 제공될 때 사용 가능

아래 수식에서 C 는 클래스의 개수
실제값과 예측값이 모두 동일하게 될 경우 손실함수의 값은 0이 나옴
실제값과 예측값이 다를 경우 수식에 대입하면 양의 무한대로 발산

  • 케라스에서 지원하는 categorical crossentropy
tf.keras.losses.CategoricalCrossentropy(from_logits=False, label_smoothing = 0, reduction="auto", name = 'catogorical_crossentropy")
  • pytorch에서 지원하는 binary crossentropy
import torch
import torch.nn as nn

ce_loss = nn.CrossEntropyLoss()
outputs = torch.randn(3, 5, requires_grad = True)
targets = torch.tensor([1, 0, 3], dtype = torch.int64)
loss = ce_loss(outputs, targets)
print(loss)


랜덤한 벡터를 출력으로 가정하고, 그 target 벡터를 정수 벡터로 만든다. 파이토치의 CrossEntropyLoss 클래스는 각 입력이 클래스 하나에 속하고 각 클래스에는 고유한 인덱스가 있다고 가정하기 때문이다.

Sparse categorical crossentropy

범주형 교차 엔트로피와 동일하게 멀티 클래스 분류에 사용
one-hot encoding 된 상태일 필요 없이 정수 인코딩 된 상태에서 수행 가능
라벨이 (1,2,3,4) 이런식으로 정수형태일때 사용!

tf.keras.losses.sparse_categorical_crossentropy(y_true, y_pred, from_logits=False, axis = 1)

Focal loss

Focal loss는 페이스북의 Lin et al. 이 소개했습니다.
RetinaNet 모델을 학습시키는데 Focal loss가 한단계 객체 탐색기를 향상시킵니다.

Focal loss는 분류 에러에 근거한 loss에 가중치를 부여하는데, 샘플이 CNN에 의해 이미 올바르게 분류되었다면 그것에 대한 가중치는 감소합니다. 즉, 좀 더 문제가 있는 loss에 더 집중하는 방식으로 불균형한 클래스 문제를 해결하였습니다.


Focal loss는 Sigmoid activation을 사용하기 때문에, Binary Cross-Entropy loss라고도 할 수 있습니다.
특별히, r = 0 일때 Focal loss는 Binary Cross Entropy Loss와 동일합니다.

  • tensorflow를 기반한 keras 코드
from keras import backend as K
import tensorflow as tf

def focal_loss(gamma=2, alpha=.25):
    def focal_loss_fixed(y_ture, y_pred):
    	pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
        pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
        return -K.mean(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1)) - K.mean((1 - alpha) *K.pow(pt_0, gamma) * K.log(1. - pt_0))
    return focla_loss_fixed
  • pytorch를 기반한 코드
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
 
class FocalLoss(nn.Module):
    def init(self, gamma=0, alpha=None, size_average=True):
        super(FocalLoss, self).init()
        self.gamma = gamma
        self.alpha = alpha
        if isinstance(alpha,(float,int,long)): self.alpha = torch.Tensor([alpha,1-alpha])
        if isinstance(alpha,list): self.alpha = torch.Tensor(alpha)
        self.size_average = size_average

    def forward(self, input, target):
        if input.dim()>2:
            input = input.view(input.size(0),input.size(1),-1)  # N,C,H,W => N,C,H*W
            input = input.transpose(1,2)    # N,C,H*W => N,H*W,C
            input = input.contiguous().view(-1,input.size(2))   # N,H*W,C => N*H*W,C
        target = target.view(-1,1)

        logpt = F.log_softmax(input)
        logpt = logpt.gather(1,target)
        logpt = logpt.view(-1)
        pt = Variable(logpt.data.exp())

        if self.alpha is not None:
            if self.alpha.type()!=input.data.type():
                self.alpha = self.alpha.type_as(input.data)
            at = self.alpha.gather(0,target.data.view(-1))
            logpt = logpt * Variable(at)

        loss = -1 * (1-pt)**self.gamma * logpt
        if self.size_average: return loss.mean()
        else: return loss.sum()
profile
머신러닝 개발자

0개의 댓글