Metrics
회귀 모형의 평가 지표
회귀(regression) 모형을 평가하는 방법 중 가장 많이 쓰이는 방법은 아마도 MAE와 MSE일 것이다.
- MAE(Mean Absolute Error)
- MSE(Mean Squared Error)
실제값과 예측값의 차이를 오차(Error)라고 한다면, 그 값에 절대값(Absolute)을 취한 후 평균(Mean)을 해주면 MAE가 되고, 제곱(Square)을 한 후 평균(Mean)을 취하면 MSE가 된다.
\[\textrm{MAE} = \frac{\sum_{i=1}^N |y_i – \hat{y_i}|}{N}\]
\[\textrm{MSE} = \frac{\sum_{i=1}^N (y_i – \hat{y_i})^2}{N}\]
여기서 \(i=1..N\) 은 적합에 사용한 훈련(train) 데이터일 수도 있고, 평가(test) 데이터일 수도 있지만, 보통 훈련 데이터와 별도로 준비된 평가 데이터에 대해 구한다. 훈련 데이터에 대해 MAE, MSE를 구할 경우, 과적합된 모형에 좋은 점수를 주게 될 가능성이 크다. 모형이 새로운 데이터에 대해 어떤 성능을 보일 것인가를 알고 싶다면, 다시 말해 일반화 성능을 알고 싶다면 새로운 데이터를 대변하는 데이터를 평가 데이터로 써야 할 것이다.
전통적인 선형 회귀 모형에서 많이 활용하는 \(R^2\) 은 보통 전체 분산 대비 설명 가능한 분산으로 많이 알려져 있다. 설명 불가능한 분산은 전체 분산에서 오차의 분산를 제외한 부분이고, 다음의 식이 가능하다.1
[1]: 엄밀히 말해 오차와 잔차를 구분할 필요가 있다. 보통 오차는 실제 평균과 실제 값의 차이( \(\mathbb{E}[y|x]-y_i\) )이고 잔차는 예측값과 실제값의 차이( \(\widehat{\mathbb{E}[y|x]}-y_i\) )이다.
실제값과 예측값의 상관계수라고 생각할 수도 있다.
\[R^2 = \frac{\mathbb{V}\textrm{ar}(y)-\textrm{MSE}}{\mathbb{V}\textrm{ar}(y)}\]
패키지 caret
R의 대표적인 기계학습 패키지인 caret
에서 구현한 회귀 모형 평가 지표는 위의 세 가지가 전부이다.
ytrue = c(0.9,1.8,2.2,3.4,4.9)
ypred = c(1,2,3,4,5)
caret::RMSE(ypred, ytrue)
## [1] 0.4604346
caret::R2(ypred, ytrue)
## [1] 0.9588015
caret::MAE(ytrue, ypred)
## [1] 0.36
사실 위의 지표를 직접 구하는 것이 어렵진 않다.
caret::RMSE
## function (pred, obs, na.rm = FALSE) ## sqrt(mean((pred - obs)^2, na.rm = na.rm)) ## <bytecode: 0x0000024aa4e3c470> ## <environment: namespace:caret>
caret::R2
## function (pred, obs, formula = "corr", na.rm = FALSE) ## { ## n <- sum(complete.cases(pred)) ## switch(formula, corr = cor(obs, pred, use = ifelse(na.rm, ## "complete.obs", "everything"))^2, traditional = 1 - (sum((obs - ## pred)^2, na.rm = na.rm)/((n - 1) * var(obs, na.rm = na.rm)))) ## } ## <bytecode: 0x0000024aa4e3d008> ## <environment: namespace:caret>
caret::MAE
## function (pred, obs, na.rm = FALSE) ## mean(abs(pred - obs), na.rm = na.rm) ## <bytecode: 0x0000024aa4e3b7e8> ## <environment: namespace:caret>
여기서 변수 명명 규칙을 잠깐 살펴보자. 가장 대중적인 명명은 다음과 같다.
## 실제값 예측값 ## 1 obs pred ## 2 ytrue ypred ## 3 y yhat
개인적으로 ytrue
, ypred
의 조합을 좋아하지만 train
, test
데이터 셋을 나눌때에는 다소 거추장스럽기도 하다. (예. ytrue_train
, ytrue_test
, ypred_train
, ypred_test
)
다른 평가 지표
절대 오차가 아니라 상대 오차가 중요하다면 MAPE와 MSLE를 쓸 수 있다. (MAPE는 시계열 분석에서 자주 사용하는 듯 하다.)
- MAPE : Mean Absolute Percent Error
- MSLE : Mean Squared Log Error
먼저 MAPE를 알아보자.
MAPE
MAPE는 다음과 같이 구한다.
\[\textrm{MAPE} = \sum_{i=1}^N \frac{|y_i-\hat{y_i}|}{|y_i|}\]
논리는 간단하다. 만약 사과를 재배할 때, 사과를 3개 수확했는데 4개로 추정한 경우와, 사과를 1012개를 수확했는데, 1013개로 수확한 경우를 비교하자면, 그 차이를 동일하게 보기 힘들다는 것이다.
모형 A는 사과를 3개 수확했을 때 4개로 추정하고, 사과 1000개를 수확했을 때, 1100개로 추정한다. 그리고 모형 B는 사과를 3개 수확했을 때 103개로 추정하고, 사과 1000개를 수확했을 때, 1001개로 추정한다.
모형 A와 모형 B가 동일하게 좋다고 생각한다면 MAE를 쓰면 되지만, 모형 A가 더 낫다고 생각한다면 MAPE의 관점과 비슷한 것이다. 실제값이 크다면, 오차도 커지기 마련이다. 따라서 오차가 절대적으로 모형평가에 반영되는 것이 아니라, 오차는 실제값에 상대적으로 모형평가에 고려된다.
MAPE의 단점
MAPE의 가장 큰 단점으로 지적된 것은 실제값이 0과 가까워질 수록 오차의 영향력이 무한대( \(\infty\) )로 커진다는 점이다. 특히 실제값이 0인 경우에는 계산이 불가능하기 때문에 실제값이 0인 경우를 제외하고 계산하기도 하지만, 그렇다면 실제값이 0인 경우에는 모형이 어떤 예측을 내놓아도 모형 평가는 달라지지 않는다는 또다른 문제점을 야기한다.
다음의 예는 0과 가까운 실제값에 존재할 때의 문제를 극명하게 보여준다. 0에 가까운 실제값에 대해 조금만 예측값이 달라지면, 그 모형은 그렇지 않은 모형에 비해 나쁜 모형이 된다(MAPE는 작을 수록 좋은 모형이다).
ypred = c(0.1, 0.01, 0.4, 0.6)
ytrue = c(0.01, 0.01, 0.5, 1)
mape_ = Metrics::mape(ytrue, ypred)
plot(ytrue, ypred, xlim=c(0,1), ylim=c(0,1), main=paste0('MAPE = ', mape_))
abline(a=0, b=1, lty='dotted')
ypred = c(0.012, 0.01, 0.35, 0.2)
ytrue = c(0.01, 0.01, 0.5, 1)
mape_ = Metrics::mape(ytrue, ypred)
plot(ytrue, ypred, xlim=c(0,1), ylim=c(0,1), main=paste0('MAPE = ', mape_))
abline(a=0, b=1, lty='dotted')
sMAPE(symmetric MAPE)
MAPE의 이런 단점을 해결하는 방법의 하나는 분모의 ytrue
를 ytrue+ypred
로 쓰는 것이다. 이를 symmetric MAPE라고 한다. sMAPE는 MAPE와 다르게, 실제값과 예측값이 바뀌어도 동일한 값을 산출한다. 위의 예에서 sMAPE를 구해보자.
ypred = c(0.1, 0.01, 0.4, 0.6)
ytrue = c(0.01, 0.01, 0.5, 1)
smape_ = Metrics::smape(ytrue, ypred)
plot(ytrue, ypred, xlim=c(0,1), ylim=c(0,1), main=paste0('sMAPE = ', round(smape_, 2)))
abline(a=0, b=1, lty='dotted')
ypred = c(0.012, 0.01, 0.35, 0.2)
ytrue = c(0.01, 0.01, 0.5, 1)
smape_ = Metrics::smape(ytrue, ypred)
plot(ytrue, ypred, xlim=c(0,1), ylim=c(0,1), main=paste0('MAPE = ', round(smape_, 2)))
abline(a=0, b=1, lty='dotted')
그런데 위의 그림은 실제값과 예측값을 모두 그래프에 담고 있지만, 그 상대적 영향력을 무시하고 있는 듯 하다. 한 가지 방법은 log
함수를 적용하는 것이다.
\(\log\)-Error
ypred1 = c(0.1, 0.01, 0.4, 0.6)
ypred2 = c(0.012, 0.01, 0.35, 0.2)
ytrue = c(0.01, 0.01, 0.5, 1)
plot(log(ytrue), log(ypred1), xlim=c(-5,1), ylim=c(-5,1))
points(log(ytrue), log(ypred2), pch='x')
abline(a=0, b=1)
위의 그래프를 보면 첫 번째 모형의 예측값(동그라미)가 두 번째 모형의 예측값(x)보다 실제값(대각선)에서 멀리 떨어져 있음을 확인할 수 있다. AE(Absolute Error)가 아니라 log-실제값과 log-예측값의 차이를 그렸기 때문이다.
Metrics
패키지의 sle
는 Squared Log Error를 구해준다. ale
(Absolute Log Error)라는 함수가 없어서 아쉽긴 하다.
\[\textrm{ALE} = |\log(y)-\log(\hat{y})|\]
위 식은 다음과 같이 변형할 수 있다.
\[|\log(\hat{y})-\log(y)| = \left|\log\left(\frac{\hat{y}}{y}\right)\right| \]
하지만 sle
도 그렇고 실제값이 0일 때 구할 수 없다는 단점 때문에 보통 실제값과 예측값이 모두 +1를 해준다.
\[\textrm{ALE} = |\log(y+1)-\log(\hat{y}+1)|\]
\[\textrm{SLE} = (\log(y+1)-\log(\hat{y}+1))^2\]
그리고 이에 평균을 구하는 것이 성능을 구하는 수순이이다. 제곱을 했다면 Root까지 씌워준다면 얼추 scale을 맞출 수 있다.
우리는 다음의 사이트에 log-오차와 MAPE의 관계에 대해 살펴볼 수 있다.
사실 \(\log(y+1)\) 에서 \(+1\) 은 다소 임의적으로 보인다. 만약 \(\log(y+10)\) 을 해주면 왜 안 되는가? \(+1\) 을 해주면 매우 작은 참값에서 조그마한 차이에 대해 큰 비중을 두는 것을 방지하는 것과 마찬가지로, \(+10\) 을 해주면 10 이하의 값에서의 차이에 대해 가중치를 적게 해준다고 생각할 수도 있다.
예를 들어 \(\hat{y}=1.1\times y\) 일때, \(\log(\hat{y})-\log(y)\) 는 \(y\) 에 관계 없이 일정하다. 다시 말해 오차와 실제값에 비에 의해 모형이 평가된다.
반면 \(\log(\hat{y}+1)-\log(y)\) 는 다음과 같은 그래프를 띈다. 0 부분에서 1.1배의 차이에 대해서는 거의 0의 가중치를 주고 있고, 약 10 이상의 구간에서는 거의 동일하게 유지되고 있음을 관찰할 수 있다.
curve(log(1.1*x+1)-log(x+1), xlim=c(0,20))
이에 반해 \(\log(y+10)\) 을 해준다면, 10 이하 부분에서 1.1배의 차이에 대해 가중치가 앞의 그래프보다 작아졌음을 확인할 수 있다.
curve(log(1.1*x+10)-log(x+10), xlim=c(0,20))
결론
다양한 평가 방법이 존재하면, 상황에 따라 적당한 방법을 선택하자.
모두 한꺼번에 적용하기
ypred = c(0.1, 0.01, 0.4, 0.6)
ytrue = c(0.01, 0.01, 0.5, 1)
library(Metrics)
## Warning: package 'Metrics' was built under R version 3.6.3
metrics = list(mae=mae, mse=mse, mape=mape, smape=smape, msle=msle)
sapply(metrics, function(x) x(ytrue, ypred))
## mae mse mape smape msle ## 0.14750000 0.04452500 2.40000000 0.58964646 0.01545984
## 좀더 직관적으로 사용하려면...
allmetrics = function(ytrue, ypred) {
sapply(metrics, function(x) x(ytrue, ypred))
}
allmetrics(ytrue, ypred)
## mae mse mape smape msle ## 0.14750000 0.04452500 2.40000000 0.58964646 0.01545984
Leave a comment