[AI 이론] 손실 함수 한방에 끝내기 (Loss Function)
- AI & Data/이론
- 2022. 11. 11.
손실 함수(Loss Function)
Loss Fucntion이란?
Loss Function은 모델의 Output이 얼마나 틀렸는지를 나타내는 척도입니다.
즉, Loss가 작을수록 좋다는 뜻이죠.
또한 모델의 Parameter를 얼마나 수정할지 정하는데도 사용합니다.
Parameter를 수정한다는 건 무슨 뜻일까요?
Loss Function에서 구한 오차를 Optimizer를 활용해 Back Propagation을 진행합니다.
그 결과 Parameter 들을 보다 나은 값으로 Update 할 수 있으며 이 과정을 반복합니다.
최종 목표는 오차를 최소로 만드는 Parameter를 구하는 것입니다.
즉, 성능이 좋은 모델을 만드는 거죠.
(Back Propagation은 Loss Function을 미분하는 것으로 시작하기 때문에 미분의 형태가 중요합니다.)
Loss Function은 Cost Function이라고도 합니다. 관점에 따라서 Objective Function으로도 부르죠.
하지만 서로 구분할 필요는 있습니다.
- Loss Function: 하나의 데이터에 대한 오차를 최소화하기 위해 정의된 함수입니다.
- Cost Function: 모든 오차를 일반적으로 최소화하기 위해 정의된 함수입니다.
- Objective Function: 어떤 값을 최대화 혹은 최소화하기 위해 정의된 함수입니다.
이들은 "Objective Function ⊃ Cost Function ⊃ Loss Fucntion"의 관계입니다.
AI, ML, DL처럼 서로 혼용되지만 구분은 필요하죠.
Loss Function의 종류
Loss Function은 그 종류가 다양합니다.
알고리즘과 조건에 따라서 나은 Loss Function을 선택해서 사용해야 하죠.
일반적으로 회귀 알고리즘에는 MSE를 분류 알고리즘에는 Cross-Entropy를 주로 사용합니다.
하지만 각 모델의 특징에따라 다른 함수들을 사용하기도 하죠.
이번 글에서는 대표적인 Loss Function 몇 가지에 대해서 소개할게요.
MSE (Mean Squared Error)
회귀 알고리즘에서 사용한다고 말씀드린 MSE입니다.
MSE는 Mean Squared Error. 즉, 평균 제곱 오차라는 뜻입니다.
모델의 예측 값과 실제 값 사이의 오차를 제곱해 평균을 구는 것이죠.
MSE는 SE(Squared Error), SSE(Sum of Squares for Error)를 알면 이해하기 편합니다.
- SE (Squared Error, 제곱 오차)
- 예측 값과 실제 값의 차이(오차)를 제곱한 값입니다.
- 오차를 제곱한 이유는 부호를 없애기 위해서입니다.
크기에 집중하기 위해 방향은 제거하는 거죠! - SSE, MSE, RMSE는 모두 SE에서 출발했습니다.
- SSE (Sum of Squares for Error, 오차 제곱 합)
- SE들의 합입니다.
- 가중치가 적절한지 판단할 지표 중 하나죠.
- SSE를 기반으로 MSE가 나왔습니다.
결국 MSE는 SSE의 평균이었습니다.
왜 굳이 평균을 사용하는 걸까요? 🤔
SSE를 그대로 사용하게 되면 데이터가 많아질수록 어마어마하게 커집니다. (제곱의 영향이 있는 거죠. 😱)
그런데 SSE가 커지는 이유가 정말 데이터가 많기 때문인지, 실제 오차가 크기 때문인지 알 수 없게 되죠.
그래서 데이터 양의 영향을 없애기 위해 평균값을 사용합니다.
제곱의 영향은 모델의 학습에도 바로 나옵니다.
오차가 큰 data는 MSE값이 훨씬 크게 나오겠죠?
모델은 이렇게 Error가 큰 data나 dimension의 Error를 줄이는 방향으로 학습하게 됩니다.
전체적으로 Loss들이 비슷한 값이 될 때까지 변동을 크게 가져가면서 줄이죠.
그럼 직접 코딩해서 확인해볼게요.
결과를 보면 데이터 양에 따른 MSE와 SSE값을 비교할 수 있습니다.
import numpy as np
def MSE(target, pred):
return (1/len(target)) * np.sum((target - pred)**2)
def SSE(target, pred):
return (0.5) * np.sum((target - pred)**2)
def data_maker(data_len, index):
label = np.zeros((data_len,))
pred = np.full((data_len,), 0.05)
label[index] = 1
pred[index] = 0.8
return label, pred
label1, pred1 = data_maker(100, 30)
label2, pred2 = data_maker(10000, 30)
print(label1)
print(pred1)
print("____________SSE____________")
print("SSE at Data 100: ", np.round(SSE(label1, pred1), 5))
print("SSE at Data 10000: ", np.round(SSE(label2, pred2), 5))
print("____________MSE____________")
print("MSE at Data 100: ", np.round(MSE(label1, pred1), 5))
print("MSE at Data 10000: ", np.round(MSE(label2, pred2), 5))
RMSE (Root Mean Squared Error)
식과 이름에서 느낌이 오시나요?
RMSE는 MSE에 Root를 씌운 값입니다.
왜 Root를 씌웠을까요?
MSE는 Error 제곱의 평균입니다.
이는 실제 Error와 차이가 큽니다. 보정이 필요하겠네요.
하지만 Error가 큰 data에 focus를 맞춰 학습시킨다는 장점은 유지시키는 방법이 필요합니다.
그래서 Root를 사용한거죠!
RMSE도 Error와 차이는 있지만, MSE보다 유사하면서 학습에 도움이 되는 장점은 살아있습니다. 👍
RMSE도 MSE처럼 회귀 모델에서 주로 사용합니다.
이제 직접 코드로 구현하고 비교까지 해볼게요.
import numpy as np
def RMSE(target, pred):
return np.sqrt((1/len(target)) * np.sum((target - pred)**2))
def MSE(target, pred):
return (1/len(target)) * np.sum((target - pred)**2)
def SSE(target, pred):
return (0.5) * np.sum((target - pred)**2)
def data_maker(data_len, index):
label = np.zeros((data_len,))
# 0~0.3 사이 값을 0.01간격으로 갖는 sample 모집단
pred_sample = np.arange(0, 0.3, 0.01)
random_data_len = int(data_len/2)
pred_1 = np.random.choice(pred_sample, random_data_len, replace=True) # data 중 절반은 random
pred_2 = np.zeros((data_len - random_data_len)) # data 중 절반은 zero
pred = np.concatenate((pred_1, pred_2), axis=0) # 두 배열을 하나로!
np.random.shuffle(pred) # shuffle
# 오차를 구성
label[index] = 1
pred[index] = 0.95
return label, pred
label, pred = data_maker(10000, 30)
print(pred[:100])
print("SSE: ", np.round(SSE(label, pred), 5))
print("MSE: ", np.round(MSE(label, pred), 5))
print("RMSE: ", np.round(RMSE(label, pred), 5))
코드와 결과를 볼게요.
먼저 "label = 1", "pred = 0.95"로 설정해서 실제 오차 "Error = 1"라는 점을 기억하고 시작하죠.
SSE가 월등하게 큽니다. 이는 "데이터 수"에서 받은 영향이 크기 때문이죠.
MSE는 Error를 제곱해서 보기 때문에, 오차가 가장 적게 나왔네요.
RMSE가 실제 오차와 가장 유사한 값이 나왔네요. MSE에서 제곱된 Error를 보정하기 때문이죠.
CEE (Cross Entropy Error)
CEE는 일반적으로 "분류 알고리즘"에서 사용합니다.
설명을 시작하기 전에 Entropy를 먼저 이해하는 것이 좋아서 간단하게 보고 넘어가겠습니다.
Entropy
Entropy는 Egnergy + Trope의 합성어라고 해요.
"모든 변화는 Entropy가 증가하는 방향으로 일어난다"는 말과 함께 무질서도라고 알려져 있죠.
Entropy를 통계학 관점에서 보면 "불확실성에 대한 척도"입니다.
불확실성이 커지면 Entropy도 커지죠. (계산은 확률을 이용합니다.)
예를 들어 볼게요. 상자가 2개 있습니다.
첫 상자에는 파란색 공만 들어있군요.
어떤 공을 꺼내도 모두 파란색 공이니까 불확실성은 없겠죠. 이때 Entropy는 '0'입니다.
다음 상자에는 파란색 공과 주황색 공이 같은 수만큼 들어있습니다.
이때는 어떤 공이 나올 확률이 동일하기 때문에 예측이 힘들죠.
즉, 불확실성이 큰 경우이기 때문에 Entropy가 크겠네요.
식을 보면 "q와 p"가 하나의 식에서 사용되고 있죠?
(⚠️주의⚠️cross-entropy에서는 자연로그 ln을 주로 사용합니다.)
서로 다른 Entropy를 교차해서 그 오차를 본다는 점이 Cross-Entropy라는 의미로 사용됩니다.
Error는 실제 Cross-Entropy와 예측 Cross-Entropy의 차이겠죠.
그리고 그 차이를 줄이는 방향으로 학습을 시킬 겁니다.
감이 오시나요? 그래도 조금만 더 설명해보죠.
대체 어떻게 Loss Function으로 사용되는 걸까요?
"개, 고양이, 호랑이"의 이미지를 분류하는 다중 분류 모델의 학습과정을 생각해보죠.
첫 학습으로 "호랑이" 이미지를 Input으로 넣고 예측 결과가 "[0.5, 0.1, 0.3]"으로 나왔다고 가정해 봅시다.
모델 입장에서는 "개일 확률=50%", "고양이일 확률=10%", "호랑이일 확률=30%"라는 뜻입니다.
결과는 확률로 나왔지만 실제 Input으로 사용된 이미지는 "호랑이"로 정해져 있죠.
즉, "[0, 0, 1]"이 정답이라는 뜻입니다.
(이런 식으로 Class 중 하나만 "1"의 값을 갖는 것을 One-hot Encoding이라 합니다. 맨 아래에서 더 설명할게요.)
첫 학습의 결과를 CEE로 계산하면 이렇게 되겠군요.
-{0 * ln(0.5) + 0 * ln(0.1) + 1 * ln(0.3)} = 1.204
이렇게 계산된 값을 Loss로 사용하고 Loss를 줄이는 방향으로 모델을 학습시킵니다.
가장 이상적인 모델은 Loss가 "0"이겠네요.
이번엔 직접 구현해보겠습니다.
예측 값은 학습이 잘 진행된다는 가정 하에 임의로 설정했습니다.
"호랑이"를 예측하는 확률이 높아질수록 CEE값은 작아지는 것을 확인할 수 있네요.
import numpy as np
def CEE(label, pred):
delta = 1e-7 # log함수의 발산을 막기 위한 수
return -np.sum(label * np.log(pred+delta)) # 자연로그 ln
# 1st
label = np.array([0, 0, 1])
pred = np.array([0.5, 0.1, 0.3])
CEE(label, pred) # 1.204
# n-th
label = np.array([0, 0, 1])
pred = np.array([0.1, 0.12, 0.78])
CEE(label, pred) # 0.248
BCEE (Binary Cross Entropy Error)
BCEE는 이진 분류에 사용되는 방식입니다.
예를 들면 "T/F, +/-, 남/여"처럼 2개의 Class를 분류할 때 사용하는 방식이죠.
앞서 살펴본 CEE처럼 예측값은 0~1 사이의 값으로 확률처럼 이해하시면 되겠습니다.
이진 분류이므로 사용하는 Activation Function은 Sigmoid나 tanh를 사용할 수 있겠네요.
CCEE (Categorical Cross Entropy Error)
CCEE는 분류할 Class가 3개 이상인 다중 분류에 사용됩니다.
앞서 살펴본 "개, 고양이, 호랑이"처럼 말이죠.
이 경우에는 Label이 One-hot vector의 형태로 제공되어야 합니다.
"개, 고양이, 호랑이"의 경우를 볼까요?
[[1, 0, 0]
[0, 1, 0]
[0, 0, 1]]
이러한 형태로 Label이 제공되어야 한다는 뜻입니다.
이렇게 Label을 One-hot vector의 형식으로 변환해주는 것은 One-hot Encoding이라 합니다.
여기까지 "Loss Function"에 대해 알아봤어요.
다음에는 "딥러닝 모델의 학습"에 대해 알아보겠습니다.
부족한 글이었지만 도움이 되기를 바랍니다. 😊
'AI & Data > 이론' 카테고리의 다른 글
[AI 이론] 딥러닝 - 이미지 처리 이론과 실습 (Ft. MNIST) (0) | 2022.11.16 |
---|---|
[AI 이론] 딥러닝 모델의 학습 방법과 개념 (Ft. Tensorflow, Keras) (0) | 2022.11.14 |
[AI 이론] 활성 함수 한방에 끝내기 (Activation Function) (0) | 2022.11.10 |
[AI 이론] 딥러닝 - 퍼셉트론 한방에 끝내기 (Perceptron) (0) | 2022.11.08 |
[AI Algorithm] 지도학습 - 분류 한방에 끝내기 (Classification / Ft. Decision Tree) (0) | 2022.11.08 |