이런 불균형한 데이터에서 모델의 성능을 올려보고자 제시된 방법이 대표적으로 오버 샘플링과 언더 샘플링이다. 그중 이 포스팅에서 소개할 SMOTE-NC는 오버 샘플링에서 주로 사용되는 SMOTE의 확장된 버전이다. 여기서 오버 샘플링은 불균형한 데이터셋에서 소수 클래스의 샘플을 증가시켜 균형을 맞추는 기법을 의미한다.
오버 샘플링
앞에서 이야기했듯 오버 샘플링은 소수 클래스의 샘플을 증가시켜 데이터셋의 클래스 간 균형을 조정한다. 이를 통해 모델이 소수 클래스를 더 잘 학습할 수 있다. 오버 샘플링은 소수 클래스의 샘플을 복제하거나 새로운 합성 샘플을 생성하여 데이터셋에 추가하는 방식으로 수행된다.
오버 샘플링은 분류 모델의 성능을 향상시킬 수 있는 방법 중 하나이지만, 과적합(overfitting) 문제가 발생할 수 있다. 따라서 적절한 오버 샘플링 기법과 하이퍼 파라미터 값을 적절하게 조절하는 것이 중요하다. 또 주의 해야하는 점은 오버 샘플링은 학습 데이터에만 적용되어야 하며, 검증 데이터나 테스트 데이터에는 적용해서는 안 된다는 점이다. 또한, 오버 샘플링은 모든 상황에서 효과적이지 않을 수 있으므로 데이터셋의 특성과 문제에 따라 적절한 오버 샘플링 방법을 선택해야 한다.
대표적인 오버 샘플링 기법에는 SMOTE가 있다.
SMOTE(Synthetic Minority Over-sampling Technique)
SMOTE는 합성소수표집법이라고 하기도 하며, 소수 클래스 샘플 간의 가장 가까운 이웃(K-NN 알고리즘)을 찾아 그 사이에 새로운 합성 샘플을 생성하는 방법이다. 이러한 합성 샘플은 소수 클래스의 특성을 유지하면서 다양성을 높여준다. 오버 샘플링 기법 중 단순 무작위 추출을 통해 데이터의 수를 늘리는 방법도 존재하는데, 데이터를 단순하게 복사하기 때문에 과적합 문제가 발생한다. 반면에 SMOTE는 알고리즘을 기반으로 생성하면서 과적합 문제가 발생할 확률을 낮춘다.
SMOTE의 동작 과정은 다음과 같다.
소수 클래스의 샘플을 선택한다.
선택한 소수 클래스 샘플과 가장 가까운 이웃을 찾는다. 일반적으로 유클리디안 거리를 사용하여 가장 가까운 이웃을 판단한다.
선택한 샘플과 이웃 사이에서 새로운 합성 샘플을 생성한다. 이 때, 샘플 간의 거리를 고려하여 새로운 합성 샘플의 특성 값을 결정한다.
위의 과정을 원하는 수의 합성 샘플을 생성할 때까지 반복한다.
SMOTE의 주요 아이디어는 소수 클래스의 결정 경계를 따라 합성 샘플을 생성하여 소수 클래스의 특성을 더욱 잘 반영하는 것이다. 이로 인해 모델이 소수 클래스를 더 잘 학습할 수 있게 된다.
SMOTE-NC
SMOTE는 연속형 데이터에만 사용 가능하다는 단점을 가지고 있다. 따라서 SMOTE의 확장된 버전인 SMOTE-NC는 SMOTE-NC가 제시되었다. "NC"는 "Nominal and Continuous"를 나타내며, 이는 SMOTE-NC가 명목형(범주형) 및 연속형 데이터를 모두 처리할 수 있다는 것을 의미한다.
SMOTE-NC는 기존 SMOTE 알고리즘과 유사한 방식으로 작동하지만, 명목형 특성을 적절하게 처리하기 위해 추가적인 단계를 수행한다.
소수 클래스 샘플을 기준으로 가장 가까운 이웃을 찾는다.
소수 클래스 샘플과 가장 가까운 이웃 사이에서 합성 샘플을 생성한다.
명목형 특성 간의 유사도를 고려하여 명목형 특성에 대한 합성 샘플 값을 결정한다.
연속형 특성에 대한 합성 샘플 값은 기존 SMOTE와 동일한 방식으로 결정한다.
SMOTE-NC는 명목형 특성을 처리하는 기능을 추가하여 소수 클래스의 명목형 특성 분포를 잘 모사하는 합성 샘플을 생성한다.
from sklearn.model_selection import train_test_split
# 오버 샘플링을 위한 라이브러리
from imblearn.over_sampling import SMOTENC
# 훈련 데이터와 테스트 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
categorical = [idx for idx, dtype in enumerate(X_train.dtypes) if dtype == 'int64']
# 오버 샘플링
smotenc = SMOTENC(categorical_features=categorical, random_state=42)
X_train_smotenc, y_train_smotenc = smotenc.fit_resample(X_train, y_train)
SMOTE-NC는매개변수로 categorical_features가 인덱스 어디에 존재하는지에 대한 정보를 받아야한다. 위 코드에서 사용한 데이터에서는 범주형 데이터에 인코딩을 진행해 int64의 데이터 타입을 가진 열이 범주형 데이터에 해당되다보니 코드를 저렇게 구성했다. 본인이 사용할 데이터를 잘 파악해서 변형 후 사용해야한다.