Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

Passion, Grace & Fire.

지니 계수(Gini coefficient) 본문

deeplearning

지니 계수(Gini coefficient)

vincenthanna 2020. 8. 25. 18:11

지니 계수의 정의

지니계수는 어떤 값의 분배상태를 표현하기 위한 로렌츠 곡선을 이용하여 값의 분배 정도를 수치화하는 방법이다.

머신러닝 분야에서는 Decision Tree Model의 성능평가를 하는 데 사용된다.

아래 이미지는 로렌츠 곡선으로, X축은 데이터 수의 누적 비율이고, Y축은 데이터 value의 누적 비율이다.

위 로렌츠 곡선에서 A를 Line of Equality와 로렌츠 곡선 사이의 영역이라 하고 B를 로렌츠 곡선의 영역이라고 하면 지니 계수의 값은 아래와 같다.

지니계수 = A / (A + B)

여러개의 값을 가진 데이터의 경우, 지니 계수는 아래 공식에 따라 구할 수 있다.

출처 : https://en.wikipedia.org/wiki/Gini_coefficient

코드 구현

여러 개의 데이터로 이루어진 list를 입력으로 받아 지니 계수를 구하는 코드

def gini(list_of_values):
    sorted_list = sorted(list_of_values)
    height, area = 0, 0
    for value in sorted_list:
        height += value
        area += height - value / 2.
    fair_area = height * len(list_of_values) / 2.
    return (fair_area - area) / fair_area

출처 :https://planspace.org/2013/06/21/how-to-calculate-gini-coefficient-from-raw-data-in-python/

머신 러닝 모델을 평가하는 데 사용하기 위해 label과 prediction을 입력으로 받고 계산된 계수를 리턴한다.

label을 정렬한 prediction의 인덱스로 재배열했을 때의 지니계수를 구하고, 그 값을 실제값의 지니계수 값으로 나눈 normalized gini coefficient를 사용한다.

예측값으로 정렬하여 계산한 지니계수와 원본값의 지니계수를 비교하는 것으로 동일할 경우 1.0을 리턴한다.

import numpy as np

def ginic(actual, pred):
    actual = np.asarray(actual) #In case, someone passes Series or list
    n = len(actual)
    # 예측값을 오름차순으로 정렬한 인덱스로 실제값을 재배열한다.
    a_s = actual[np.argsort(pred)]    
    a_c = a_s.cumsum()    
    '''
    a_c.sum() / a_s.sum() : 실제 값의 누적 비율의 총합    
    균등분포시의 누적총합(삼각형 크기) = n * 1 / 2
        (n+1)/2는 1부터 n까지의 합 n(n+1)/2를 n으로 나눈것이다.
        sum(1,2,3...n) = n(n + 1)/2 이므로,
        sum(1/n, 2/n, 3/n...n) = sum(1,2,3...n) / n = (n + 1)/2
    '''
    giniSum = a_c.sum() / a_s.sum() - (n + 1) / 2.0
    return giniSum / n # (균등분포시의 누적총합)*2 로 나눠준다.

def gini_normalizedc(a, p):
    if p.ndim == 2:#Required for sklearn wrapper
        p = p[:,1] #If proba array contains proba for both 0 and 1 classes, just pick class 1
    return ginic(a, p) / ginic(a, a)

출처 :https://www.kaggle.com/tezdhar/faster-gini-calculation

gini_normalizedc()는 -1.0 ~ 1.0 사이의 값을 리턴하는데, 원본과 prediction의 누적값이 동일할 때의 1.0의 값이 가장 크므로, loss로 사용하기 위해 음수로 변경해서 일치시 가장 작은 값을 갖게 한다.

def gini_xgb(preds, dtrain):
    labels = dtrain.get_label()
    # score
    gini_score = -eval_gini(labels, preds)
    return [('gini', gini_score)]

출처 :https://www.kaggle.com/aharless/xgboost-cv-lb-284

Gini coefficient as evaluation metric

Regression 문제에서 특정 데이터만 심하게 많은 경우(예를 들어 label이 1과 0으로 이루어진 데이터에서 거의 대부분이 0인 경우), MSE는 label과 다수를 차지하는 데이터만으로 이루어진 prediction 사이의 loss가 작은 값이 나온다.

이런 경우 모델이 모든 값을 다수 데이터로 예측하도록 잘못 train될 수 있다.

normalized gini coefficient에서는 동일 상황에서 상대적으로 큰 loss값을 산출해 낸다.

# 데이터가 imbalance한 경우 gini coefficient가 더 낫다는 것을 보여주는 예시 코드
from sklearn.metrics import mean_squared_error, mean_squared_log_error
import numpy as np

a = [0] * 97
for i in range(3):
    a.append(1)

p = [0] * 100

a = np.array(a)
p = np.array(p)
print("\nalmost 0")
print("mse:", mean_squared_error(a, p))
print("gini:", -gini_normalizedc(a, p))


a = [1] * 97
for i in range(3):
    a.append(0)    

p = [1] * 100

a = np.array(a)
p = np.array(p)
print("\nalmost 1")
print("mse:", mean_squared_error(a, p))
print("gini:", -gini_normalizedc(a, p))

 

출력:

almost 0  
mse: 0.03  
gini: -0.42955326460481097

almost 1  
mse: 0.03  
gini: 0.42955326460481064
Comments