728x90
모델 성능 평가지표(Metric)
- 실제값과 모델에 의해 예측된 값을 비교하여 모델의 성능을 측정하는 것
- 모델 평가 목적: Over FItting을 방지하고 최적의 모델을 찾기 위해서
회귀 문제 성능측정
사이킷런에서 제공해주는 데이터셋
- 당뇨병 진행도를 예측하는 데이터셋
데이터 가져오기
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
diabetes.data, diabetes.target
학습셋과 검증셋 분리
SEED = 42
from sklearn.model_selection import train_test_split
x_train, x_valid, y_train, y_valid = train_test_split(data,target,random_state = SEED)
x_train.shape, x_valid.shape, y_train.shape, y_valid.shape
=> ((331, 10), (111, 10), (331,), (111,))
Linear Regression 모델을 이용한 학습 및 예측
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(x_train,y_train)
pred = model.predict(x_valid)
pred
=>
array([137.94979889, 182.53621462, 129.85554049, 292.55738727,
124.86559124, 89.73987275, 255.96040544, 177.64691719,
87.88678516, 107.93733249, 93.26418526, 171.11859131,
56.06450108, 207.22092723, 99.79206335, 131.71477524,
215.86564416, 252.18825848, 201.56370519, 220.38853067,
204.35710556, 88.90708813, 68.53068596, 190.02202826,
153.9129781 , 164.02499711, 192.83524491, 187.16510251,
46.60505083, 109.31176844, 175.05178809, 87.8091125 ,
130.37787111, 186.56591449, 172.474085 , 188.61455112,
124.0734688 , 119.16492145, 147.74614571, 59.27160039,
70.90301607, 109.30498566, 170.97369589, 156.56765896,
168.68372176, 61.54562801, 71.19297293, 111.94403965,
54.31821851, 165.81456881, 152.78238871, 63.80826526,
110.07732165, 108.6406539 , 179.16116053, 156.79589523,
93.28778814, 212.92031906, 119.28369688, 70.70909587,
186.68094494, 205.65924203, 140.02745143, 106.54570273,
131.56044364, 204.17886366, 173.28771583, 168.65292359,
123.58530603, 145.48887871, 182.55324099, 200.92532802,
234.1265505 , 148.65425454, 82.2675761 , 162.10939001,
191.415392 , 208.42448442, 159.81021311, 207.0829118 ,
108.51857719, 141.86705133, 51.8871943 , 52.84276976,
114.22398715, 77.83689884, 82.21006125, 56.81868815,
167.18534084, 188.0216126 , 153.36919669, 242.28745175,
108.00705808, 63.19263597, 55.15197266, 197.58205898,
248.94621618, 184.27491828, 104.75983131, 63.4507554 ,
196.41823897, 110.04940238, 296.72023354, 98.13506422,
151.08851299, 105.09389123, 131.61291086, 128.2943136 ,
165.9988908 , 185.58555347, 111.88333319])
회귀 평가 지표
회귀 평가 지표는 이해하고, 각 특징을 파악하려 잘 쓰기만 하면 된다.
MSE(Mean Squared Error)
- 실제값과 예측값의 차이를 제곱한 뒤 평균화
- 이상치에 민감
- 직관적이지 못하다.
- 평가지표로 사용하는 것보다 주로 손실함수로 사용된다.
from sklearn.metrics import mean_squared_error # 예측값과 정답값의 차이
mse = mean_squared_error(y_valid,pred)
mse
=> 2848.2953079329445
RMSE(Root Mean Squared Error)
- MSE에 루트
- 이상치에 민감
- 평가지표로 많이 사용
import numpy as np # 예측값과 정답값의 차이
np.sqrt(mse)
=> 53.36942296795932
MAE(Mean Absolute Error)
- 실제값과 예측값의 차이를 절대값으로 변환해서 평균화
from sklearn.metrics import mean_absolute_error # 예측값과 정답값의 차이
mae = mean_absolute_error(y_valid,pred)
mae
=> 41.548363283252066
MAPE(Mean Absolute Percentage Error)
- 실제값에 대한 절대오차 비율의 평균을 퍼센트로 표현
- y가 0에 가까워질수록 분모가 작아지므로 수치가 커진다.
- y가 0이면 계산이 안된다. (0을 제외하고 계산)
def mape(true,pred):
return np.mean(np.abs((true - pred) / true))
mape(y_valid,pred)
=> 0.3731095161631557
SMAPE(Symmetric Mean Absolute Percentage Error)
- 기존의 MAPE의 단점을 보완
- MAPE와 다른점은 각 실제값과 예측값을 절대값으로 변경 후 합으로 나눈다.
- MAPE와 다르게 실제값의 0이 존재해도 계산이 가능하다.
- 예측값이 실제값보다 작을 때, 분모가 작아지기 때문에 오차가 커지게 된다. 즉, 과소추정에 대한 패널티를 줄 수 있다.
def smape(true,pred):
error = np.abs(true-pred) / (np.abs(true) + np.abs(pred))
return np.mean(error)
smape(y_valid,pred)
=> 0.15271360402048711
평가지표는 대부분 1에 가까울수록 좋다.
분류문제 성능 측정
사이킷런에서 제공해주는 데이터셋
- 0~9 손글씨 이미지 받아오기
from sklearn.datasets import load_digits
digits = load_digits()
8 X 8 크기의 이미지가 Flatten 되어있다.
digits.target[:4]
=> array([0, 1, 2, 3])
슬라이싱을 이용하여 값 확인
import matplotlib.pyplot as plt
fig,ax = plt.subplots(2,2)
ax[0,0].imshow(digits.data[0].reshape(8,8),cmap = "gray")
ax[0,1].imshow(digits.data[1].reshape(8,8),cmap = "gray")
ax[1,0].imshow(digits.data[2].reshape(8,8),cmap = "gray")
ax[1,1].imshow(digits.data[3].reshape(8,8),cmap = "gray")
plt.show()
- 3을 맞추는 문제로 재정의
data = digits.data
target = (digits.target == 3).astype(int)
target.mean() # 0과 1의 수에 대한 평균
=> 0.1018363939899833
혼동행렬(Confusion Matrix)
- 이진분류에서 성능지표로 활용되는 행렬
- 이진 분류에서 예측 오류가 어느정도 되는지와 어떠한 유형의 예측 오류가 발생하는지를 나타내는 지표
- Actual: 실제값, predicted: 모델의 예측값
- Precision (정밀도)
- 양성(1)으로 예측한 값들 중에 맞춘 비율
- Recall (재현율)
- TPR
- 실제 양성값들 중에 맞춘 비율
- TP가 중요하다. 1을 중점으로 판별
- FPR = FP/TN+FP (1-Specificity)
- 실제 음성값들 중에 못 맞춘 비율
TN,FP,FN,TP 안에 해당되는 개수가 들어간다. T=맞췄다. N=0으로 예측, FP는 반대이다.
임계값(thresholds)
- 모델을 분류해서 확률(0~1) 또는 음수에서 양수사이에 실수를 예측값으로 출력
- 사이킷런에서는 predict_proba, decision_function 메소드를 제공
- predict_proba : 0.5 이상이면 1로 예측 (경우에 따라 수치를 변경하면서 적용)
- decision_function: 0 이상이면 1로 예측
Accuracy 의 한계
- 오류 중에서 FN 오류를 줄이는 것이 더 중요한 경우 (암인데 정상이라 한 경우)
- 오류 중에 FP 오류를 줄이는 것이 더 중요한 경우 (좋은 날씨(1), 나쁜날씨(0) 좋은 날씨를 맞춰 체육대회 날 맞추기)
- 정확도는 위에 두가지 오류에 정도의 차이를 구분할 수 없기 때문에 적절한 성능지표가 될 수 없다.
- 특별한 경우가 아니면 잘 사용하지 않는다. (0과 1의 비율이 50% 이면 사용)
- 음성(0)이 양성(1)보다 훨씬 많은 경우 음성(0)으로만 예측해도 높은 정확도를 보이기 때문에 적절한 성능지표가 될 수 없다.
- Dummy
from prompt_toolkit.layout import dummy
from sklearn.dummy import DummyClassifier
dummy = DummyClassifier(strategy='most_frequent')
dummy.fit(x_train,y_train)
pred_dummy = dummy.predict(x_valid)
- Decision Tree
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(max_depth=3,random_state=SEED)
tree.fit(x_train,y_train)
pred_tree = tree.predict(x_valid)
- 정확도 평가
from sklearn.metrics import accuracy_score
score = accuracy_score(y_valid,pred_dummy)
print(f"dummy: {score}") # 모델을 적용한 것이 아닌데도 정확도가 높다.
score = accuracy_score(y_valid, pred_tree)
print(f"tree: {score}")
=> dummy: 0.8977777777777778
tree: 0.9688888888888889
수치가 이상하여 평가지표로 활용되지 않는다.
Precision vs Recall
- FP를 줄이는 것이 목표일 때 Precision 사용
- FN을 줄이는 것이 목표일 때 Recall 사용
from sklearn.metrics import precision_score, recall_score
precision_score(y_valid,pred_dummy) # tp가 0이라 0
recall_score(y_valid,pred_dummy)
=> 0.0
precision_score(y_valid,pred_tree), recall_score(y_valid,pred_tree)
=> (0.8636363636363636, 0.8260869565217391)
Precision-Recall Trade-Off
- 확률을 낮출수록 정밀도가 떨어지게 되고, 재현율이 올라간다.
- 절충관계라 단독으로 사용하지 않는다.
threshold = 0.9 # 0.9와 0.1의 차이가 크게 난다.
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid,pred), recall_score(y_valid,pred)
=> (0.9024390243902439, 0.8043478260869565) ==> 0.9
=> (0.6268656716417911, 0.9130434782608695) ==> 0.1
F1-score
- Precision 과 Recall의 조화평균
- 정밀도와 재현율이 어느 한쪽으로 치우치지 않았을 때 높은 점수가 나온다.
- Precision과 Recall은 Trade-off 관계이기 때문에, 이 둘의 조화 평균값인 F1-Score를 많이 사용한다.
F1=2∗Precision∗RecallPrecision+Recall
- precision = 0.1, recall = 0.9이라 가정해보자
(0.1+0.9) / 2 # 산술평균
=> 0.5
2 * 0.1 * 0.9 / (0.1+0.9) # 조화평균 (한쪽에 치우쳐 있어 점수가 낮게 나옴)
=> 0.18000000000000002
한쪽으로 치우쳐 값이 낮게 나오는 것을 알 수 있다.
- recall, precision, F1-score 한번에 보기
from sklearn.metrics import classification_report # 다중분류에서 사용한다! (이진분류는 굳이?)
print("##dummy##")
print(classification_report(y_valid,pred_dummy))
print("###tree###")
print(classification_report(y_valid,pred_tree))
=>
##dummy##
precision recall f1-score support
0 0.90 1.00 0.95 404
1 0.00 0.00 0.00 46
accuracy 0.90 450
macro avg 0.45 0.50 0.47 450
weighted avg 0.81 0.90 0.85 450
###tree###
precision recall f1-score support
0 0.98 0.99 0.98 404
1 0.86 0.83 0.84 46
accuracy 0.97 450
macro avg 0.92 0.91 0.91 450
weighted avg 0.97 0.97 0.97 450
다중 분류에서 사용된다.
ROC curve(Receiver Operating Characteristic curve)
- FPR를 X축으로, TPR을 Y축으로 해서 둘간의 관계를 표현한 그래프
- FPR = FP / (FP+TN)
- TPR = TP / (FN+TP)
- FPR이 천천히 진행하면서 TPR이 빠르게 진행해야 범위가 넓고 좋다.
- ROC Curve
pred_dummy = dummy.predict_proba(x_valid)[:,1] # 1에 대한 확률만 가져오겠다.
pred_tree = tree.predict_proba(x_valid)[:,1]
from sklearn.metrics import RocCurveDisplay
fig,ax = plt.subplots()
RocCurveDisplay.from_predictions(y_valid,pred_dummy, ax = ax) # (정답값, 모델에 대한 확률값,ax) dummy = 0.5
RocCurveDisplay.from_predictions(y_valid,pred_tree, ax = ax)
plt.show()
위 그림과 같이 FPR이 천천히 진행하면서 TPR이 빠르게 진행하면 범위가 넓어진다.
범위가 넓을수록 성능은 좋다고 볼 수 있다.
AUROC(ROC AUC)
- ROC Curve의 밑부분 면적
- 넓을수록 모형 성능이 좋다.
- 확률로 대처하기 때문에 인계값의 어떻게 선택되었는지 무관하게 모델의 예측 품질을 측정할 수 있다.
- Poor model (0.5 ~ 0.7) 형편없는 모델
- Fair model(0.7 ~ 0.8) 괜찮은 모델
- Good model (0.8 ~ 0.9) 좋은 모델
- Excellent model (0.9 ~ 1.0) 현실에서 나오기 힘든 엄청난 모델
Multi classification 에서의 F1-score
- micro (전체 클래스에 대하여 TP,FP,FN를 구한 뒤 F1을 계산)
- macro (각 클래스에 대하여 F1-score를 구한 뒤 산술 평균)
- weigthed (각 클래스에 대하여 F1-score를 구한 뒤 각 클래스가 차지하는 비율에 따라 가중 평균)
digits = load_digits()
data = digits.data
target = digits.target
x_train,x_valid,y_train,y_valid = train_test_split(data,target,random_state = SEED)
x_train.shape, x_valid.shape, y_train.shape ,y_valid.shape
=> ((1347, 64), (450, 64), (1347,), (450,))
print(classification_report(y_valid,pred))
=>
precision recall f1-score support
0 1.00 0.91 0.95 43
1 0.32 0.32 0.32 37
2 0.56 0.66 0.60 38
3 0.87 0.85 0.86 46
4 0.80 0.78 0.79 55
5 0.73 0.19 0.30 59
6 0.95 0.89 0.92 45
7 0.90 0.68 0.78 41
8 0.37 0.61 0.46 38
9 0.52 0.85 0.65 48
accuracy 0.67 450
macro avg 0.70 0.67 0.66 450
weighted avg 0.71 0.67 0.66 450
0은 잘 맞췄고, 5는 잘 못 맞췄다. (recall = 실제값 중 맞춘 비율, f1 치우침을 반영한 비율)
f1_score(y_valid,pred,average="micro")
=> 0.6688888888888889
-------------------------------------------------------
f1_score(y_valid,pred,average="macro")
=> 0.6620047429878985
-------------------------------------------------------
f1_score(y_valid,pred,average="weighted") # 잘 안쓴다.
=> 0.6616398619763888
못 맞추는 클래스에서 대해서 패널티를 주고 싶다면 macro 사용 아니면 micro 사용
소프트맥스 함수(Softmax Function)
- 각 클래스에 대한 확률을 출력
- 입력받은 값들을 0 ~ 1 사이의 값들로 모두 정규화하며 출력값들의 합은 항상 1이 되는 특성을 가진 함수
tree.predict_proba(x_valid).sum(axis = 1) # 확률을 다 더하니 1이 나온다.
=>
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1.])
음의 로그함수
- x에 1이 들어가면 0이고, x가 0에 가까울수록 기하급수적으로 늘어난다.
Multi classification 에서의 logloss (다중분류에서 자주 사용)
- 모델이 예측한 확률값을 반영해서 평가한다.
- 0에 가까울수록 좋은 모델
- 정답에 해당하는 확률값들을 음의 로그함수에 넣어서 나온 값들을 평균내서 평가
- 낮은 확률로 정답을 예측한 클래스에 패널티를 주기 위해 사용
0에 가까울수록 수치가 커지기 때문에 패널티를 주기 쉽다.
from sklearn.metrics import log_loss
pred = tree.predict_proba(x_valid)
pred
=>
array([[0. , 0.00819672, 0. , ..., 0. , 0.05737705,
0. ],
[0. , 0.13461538, 0. , ..., 0.01923077, 0.03205128,
0.42307692],
[0. , 0.04724409, 0.05511811, ..., 0.00787402, 0.02362205,
0.03937008],
...,
[0. , 0. , 0.73239437, ..., 0.13380282, 0.04225352,
0.01408451],
[0. , 0.00819672, 0. , ..., 0. , 0.05737705,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ]])
log_loss(y_valid,pred)
=> 2.1078887104736785
y_valid 자동으로 원핫인코딩된다.
복습하다가 셔플을 왜하는지에 궁금했다.
데이터를 셔플하는 이유는 수집단계에서 어떠한 규칙에 의해 정렬될 가능성이 크기 때문에
데이터의 분포가 편향적이게 되고 학습에 방해를 준다는 것을 알게 되었다.
728x90
'AI 공부 > 머신러닝' 카테고리의 다른 글
(머신러닝) 머신러닝 모델 (2) | 2022.09.13 |
---|---|
(머신러닝) sklearn (0) | 2022.09.11 |
(머신러닝) 교차검증과 과적합 (0) | 2022.09.07 |
(머신러닝) 결측치 및 스케일링 (0) | 2022.09.07 |
인공지능과 머신러닝의 개념 (2) | 2022.09.05 |
댓글