
Machine learning 모델에 기반한 Score모델을 평가할때 어떤 지표들을 활용할 수 있는지 정리해보고자 한다.
Score모델을 평가할 때 쓰이는 지표는 AUC, KS가 있다. 그리고 안정성을 평가하기 위한 지표로는 PSI가 있다.
오늘은 지난 포스트에 이어 KS에 대해 알아보려고 한다.
K-S 통계량 (Kolmogorov-Smirnov)
Kolmogorov-Smirnow(KS) 통계량은 우량 집단과 불량진답의 누적 분포의 차이를 나타내는 지표로, 신용평가모형의 변별력 평가 시 주요 판별 통계량으로 활용된다. 즉, 2개의 집단이 동일한 분포를 이루고 있는지 검증하는 지표라고 할 수 있다.
표를 기반으로 KS 지표를 해석해 보고자 한다. 먼저, Score에 대한 파란선과 빨간선은 각 집단의 누적 분포를 의미한다. 이때, 두 누적 분포를 비교하고 그 사이의 최대 차이를 반환하는 것이 KS 통계량이다.
이 경우, 특정 분포를 가정하지 않고 진행하는 방법이라 분포에 제한이 없는 방식이다. 따라서 비모수 검정이다.
결과 값을 기반으로 두 집단의 분포가 얼마가 차이나는지 통계적으로 알 수 있고, 이를 통해 두 집단을 얼마나 잘 구별할 수 있는지 이해할 수 있다.
통계량에 따른 등급은 아래와 같다.
기준 | 등급 |
< 0.2 | none |
0.2 ~ 0.3 | low |
0.3 ~ 0.4 | medium |
0.4 ~ 0.5 | high |
0.5 ~ 0.9 | extremly high |
> 0.9 | anomaly |
예시 with Python
KS값을 구하기 위해 아래와 같은 단계를 거쳐 계산해야 한다.
- KS를 계산하기 전 두 개의 변수가 필요하다.
- 바이너리여야 하는 종속변수와 통계 모델에서 생성된 예측 확률 점수다.
- 혹시, 다중분류에서 활용할 경우, OvR방식 혹은 OvO 방식으로 접근해 값을 구할 수 있다.
- 확률을 10개 부분으로 나누어 십분위수를 만듭니다. 첫번째 십분위수에는 가장 높은 확률 점수가 포함되어야 한다.
- 각 십분위에서 Event 및 Non-event의 누적 퍼센트를 계산한 다음 이 두 누적 분포의 차이를 계산한다.
- KS 통계량은 그 중에서 차이가 최대인 곳이다.
- KS가 상위 3분위 안에 있고 40점 이상이면 좋은 예측 모형으로 간주된다.
구현
데이터의 경우, y칼럼에 있는 값은 종속변수 이며, p칼럼에 있는 값은 예측된 값이다.
import pandas as pd
import numpy as np
df = pd.read_csv("https://raw.githubusercontent.com/deepanshu88/data/master/data.csv")
def ks(data=None,target=None, prob=None):
data['target0'] = 1 - data[target]
data['bucket'] = pd.qcut(data[prob], 10)
grouped = data.groupby('bucket', as_index = False)
kstable = pd.DataFrame()
kstable['min_prob'] = grouped.min()[prob]
kstable['max_prob'] = grouped.max()[prob]
kstable['events'] = grouped.sum()[target]
kstable['nonevents'] = grouped.sum()['target0']
kstable = kstable.sort_values(by="min_prob", ascending=False).reset_index(drop = True)
kstable['event_rate'] = (kstable.events / data[target].sum()).apply('{0:.2%}'.format)
kstable['nonevent_rate'] = (kstable.nonevents / data['target0'].sum()).apply('{0:.2%}'.format)
kstable['cum_eventrate']=(kstable.events / data[target].sum()).cumsum()
kstable['cum_noneventrate']=(kstable.nonevents / data['target0'].sum()).cumsum()
kstable['KS'] = np.round(kstable['cum_eventrate']-kstable['cum_noneventrate'], 3) * 100
#Formating
kstable['cum_eventrate']= kstable['cum_eventrate'].apply('{0:.2%}'.format)
kstable['cum_noneventrate']= kstable['cum_noneventrate'].apply('{0:.2%}'.format)
kstable.index = range(1,11)
kstable.index.rename('Decile', inplace=True)
pd.set_option('display.max_columns', 9)
print(kstable)
#Display KS
from colorama import Fore
print(Fore.RED + "KS is " + str(max(kstable['KS']))+"%"+ " at decile " + str((kstable.index[kstable['KS']==max(kstable['KS'])][0])))
return(kstable)
mydf = ks(data=df,target="y", prob="p")
위와 같이 함수를 호출해 정의할 경우, mydf에는 위 표와 같은 값이 들어있게 된다.
시각화
구한 결과값을 기반으로 시각화를 해볼 경우, 아래와 같이 구현하면 된다.
max_idx = mydf["KS"].idxmax()
cum_event_rate_max = mydf['cum_eventrate'].str.replace("%","").astype(float).tolist()[max_idx]
cum_non_event_rate_max = mydf['cum_noneventrate'].str.replace("%","").astype(float).tolist()[max_idx]
y_min = min([cum_event_rate_max , cum_non_event_rate_max])
y_max = max([cum_event_rate_max , cum_non_event_rate_max])
import matplotlib.pyplot as plt
plt.plot(range(1,11), mydf['cum_eventrate'].str.replace("%","").astype(float).tolist(),"o-", label="cumsum event rate")
plt.plot(range(1,11), mydf['cum_noneventrate'].str.replace("%","").astype(float).tolist() , "o-",label="cumsum non-event rate")
plt.vlines(x=max_idx+1 , ymin= y_min, ymax = y_max,color='r',label=f"KS : {y_max-y_min}")
plt.legend()
+ 참고)
Score모형의 변별력 평가시 주요 판별 통계량 뿐만 아니라, 변수간의 분포변화를 모니터링하는 data drift와 에서도 활용할 수 있다.
from scipy.stats import ks_2samp
df = pd.read_csv("https://raw.githubusercontent.com/deepanshu88/data/master/data.csv")
ks_2samp(df.loc[df.y==0,"p"], df.loc[df.y==1,"p"])
# KstestResult(statistic=0.6033333333333334, pvalue=1.5892772996981769e-31)
* 출처
https://www.niceinfo.co.kr/creditrating/bi_score_4.nice
NICE평가정보㈜
신용희망캠페인 서비스가 정비중입니다. 1588-2486으로 연락바랍니다.
www.niceinfo.co.kr
댓글