GIL's LAB

클래스 불균형 문제 (2) 탐색 방법 본문

데이터사이언스/머신러닝

클래스 불균형 문제 (2) 탐색 방법

GIL~ 2021. 9. 2. 17:50

1. 클래스 불균형 비율

클래스 불균형 문제가 있는지를 탐색하는 가장 직관적이고 쉬운 방법은 클래스 불균형 비율(imbalance ratio, IR)을 계산하는 것이다. 클래스 불균형 비율은 다음과 같이 계산할 수 있다.

 

위 식에서 NM은 다수 클래스 샘플 수를, Nm은 소수 클래스 샘플 수를 나타낸다.

일반적으로 이 비율이 9이상이면 클래스 불균형 문제가 심각하다고 하며, 4이상 9이하면 클래스 불균형 문제가 있다고 하고, 4미만이면 클래스 불균형 문제가 없다고 한다.

파이썬을 이용한 클래스 불균형 비율 계산

이제 파이썬을 이용하여 클래스 불균형 비율을 직접 계산해보자. 클래스 불균형 비율을 계산하는 방법은 여러가지지만, 여기서는 Pandas의 value_counts()를 활용한다.

가장 먼저 데이터를 불러오고, 특징과 클래스로 구분한다. 여기서 사용한 예제 데이터는 harberman.csv로 환자의 의료 기록을 바탕으로 이 환자가 5년 내에 사망할 지 (2) 생존할 지 (1)를 분류하는데 사용한다.

 

import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/jbrownlee/Datasets/master/haberman.csv', header = None)
X, Y = df.iloc[:, :-1], df.iloc[:, -1]

 

이 데이터의 헤더(컬럼명)이 없기에, 두 번째 줄에서 pd.read_csv의 header 키워드를 None이라고 설정했다. 또한, 마지막 열에 클래스 변수가 있으므로, 세 번째 줄에서 X에는 마지막 열 전까지만 슬라이싱을, Y는 마지막 열을 가져온다. 여기서 iloc는 암묵적 인덱서로서 df.iloc[a, b]는 df의 a행 b열 위치에 있는 값을 가져온다.

이제 value_counts를 사용하여 Y 변수의 분포를 확인한다. value_counts 함수는 Pandas의 객체인 Series에 포함된 값의 빈도를 출력하는데 사용한다. 직접 코드를 통해 이해해보도록 하자.

 

print(type(Y.value_counts())) print(Y.value_counts())

Y.value_counts()의 타입은 첫 번째 출력 행에서 보듯 Series이고, 이 Series는 인덱스가 Y의 값이고, value가 인덱스의 출현 빈도이다. 또한, 인덱스는 출현 빈도를 기준으로 내림차순으로 정렬되어있다. 다시 말해, 위의 결과에서 1과 2는 Y가 가지는 값이며, 225는 1이 총 225번 등장했음을, 81은 2가 81번 등장했음을 나타낸다. value_counts의 결과가 내림차순으로 정렬된 값의 빈도라는 것을 바탕으로, 첫 행의 인덱스가 다수 클래스, 값이 다수 클래스 샘플 수이고 마지막 행의 인덱스가 소수 클래스, 값이 소수 클래스 샘플 수임을 알 수 있다. 따라서 클래스 불균형 비율은 다음과 같이 계산 가능하다.

 

majority_class = Y.value_counts().index[0]
minority_class = Y.value_counts().index[-1]
num_majority_samples = Y.value_counts().iloc[0]
num_minority_samples = Y.value_counts().iloc[-1]
IR = num_majority_samples / num_minority_samples
print("다수 클래스:{}, 소수 클래스:{}, 불균형 비율: {}".format(majority_class, minority_class, IR))

 

출력 결과는 "다수 클래스:1, 소수 클래스:2, 불균형 비율: 2.7777777777777777"가 나옴을 확인할 수 있다.

클래스 불균형 비율의 한계

클래스 불균형 비율은 클래스 불균형을 나타내는 매우 직관적인 지표이지만, 이 수치가 높다고 해서 반드시 편향된 분류기를 학습하는 것이 아니라는 문제가 있다 (주의: 물론, 클래스 불균형 비율이 클수록 편향된 분류기를 학습할 가능성이 크긴 하다). 아래 그림을 확인해보자.

이 그림에서 사용된 데이터는 클래스 불균형 비율이 10으로 높은 편이지만 (즉, 파란색 클래스 샘플이 주황색 클래스 샘플보다 10배 많지만), 분류에는 전혀 지장이 없다. 즉, 이 데이터로 학습된 분류기(빨간색)는 소수 클래스 샘플인 주황색 클래스 샘플을 모두 정분류한다.

 


2. k-NN을 이용한 클래스 불균형 문제 탐색

클래스 불균형에 민감한 k-NN

k-최근접 이웃(k-nearest neighbor; k-NN)은 한 샘플의 k개 이웃의 클래스 정보를 바탕으로 분류 및 예측을 하는 지도학습 알고리즘이다. k-NN은 클래스 불균형에 매우 민감하며, 특히 k가 클수록 더 민감해진다. 그 이유를 문제 소개에서 사용했던 그림을 보며 생각해보자.

이 그림에서 주황색 샘플들의 가장 가까운 이웃은 모두 파란색이다. 심지어 선형 분류기로 정분류되었던 왼쪽에 있는 주황색 샘플의 이웃도 파란색이다. 이러한 현상이 발생하는 이유는 파란색 샘플이 상대적으로 너무 많아, 대부분 샘플에 대해 이웃 역시 파란색이 될 가능성이 커지기 때문이다. 그래도 k가 작으면 이웃 가운데 주황색의 비율이 높아질 가능성이 커지지만, k가 크면 그럴 가능성이 작아진다. 극단적인 예로, k가 21이라고 해보자 (참고: 주황색 샘플이 10개이다). 그러면 주황색 샘플끼리 아무리 잘 뭉쳐있더라도, 모든 샘플이 파란색으로 분류된다. 이러한 이유로 k-NN은 클래스 불균형 문제에 매우 민감하며, 클래스 불균형 문제가 있는 데이터에는 k-NN을 사용하지 않는 것이 바람직하다. 하지만 이러한 특성으로 인해, 클래스 불균형 문제 탐색과 해결에 사용하는 방법론들이 k-NN에 기반하고 있다.

탐색 방법

k-최근접 이웃(k-nearest neighbor; k-NN)을 이용한 클래스 불균형 문제 탐색은 매우 간단하다. 학습 데이터를 사용하여 학습한 k-NN 모델의 재현율을 기준으로 평가하면 된다. 이때, 소수 클래스 샘플 수에 비해 k가 너무 크면 클래스가 불균형하더라도 분류에 문제가 없는 데이터까지도 문제가 있다고 판단하게 되므로, k를 3이나 5로 설정하는 것이 바람직하다.

앞서 사용한 데이터를 사용하여 불균형 문제를 탐색해보자. 우선, 아래와 같이 sklearn.model_selection 모듈에 있는 train_test_split 함수를 사용하여, 데이터를 학습 데이터와 평가 데이터로 분할한다.

 

from sklearn.model_selection import train_test_split
Train_X, Test_X, Train_Y, Test_Y = train_test_split(X, Y)

 

그리고 Train_X와 Train_Y를 사용하여, KNN 모델 (k = 3)을 학습하고, 학습한 모델(kNN)을 재현율을 기준으로 평가한다.

 

from sklearn.neighbors import KNeighborsClassifier as KNN
kNN = KNN(n_neighbors = 3).fit(Train_X, Train_Y)
Y_hat = kNN.predict(Test_X)

from sklearn.metrics import recall_score
kNN_recall = recall_score(Test_Y, Y_hat)
print(kNN_recall)

 

출력 값이 0.7868이 나오는 것을 볼 수 있는데 (주의: train_test_split 함수를 사용하는 과정에서, 학습 데이터와 평가 데이터가 임의로 분할되므로, 이 수치가 나오지 않을 수도 있음), 이는 클래스 불균형 문제가 있다고 보기에는 어려운 수치이다. 보통, 클래스 불균형 문제가 있는 경우 k-NN 모델을 학습하게 되면, 재현율이 0이 나오는 것도 심심치 않게 볼 수 있다.

Comments