한 클래스의 데이터가 다른 클래스에 비해 훨씬 많을 때를 의미한다.
이러한 불균형은 모델의 학습 과정에 영향을 미쳐, 특정 유형의 데이터에 과도하게 적응하게 만들고 다른 유형을 무시하게 한다. 예를 들어, 은행 사기 탐지 시스템에서 정상 거래는 많지만 사기 거래는 적은 경우, 모델이 사기 거래를 제대로 인식하지 못할 수 있다. 이런 문제는 모델의 성능 저하, 일반화 능력 감소 등 다양한 부정적인 결과를 초래한다.
대표적인 해결 방법으로 언더샘플링과 오버샘플링이 있는데, 각각 장단점을 가지고 있으며, 상황에 따라 선택적으로 또는 함께 사용된다.
불균형이 문제가 되는 경우
일반적으로 이진 분류 문제에서 문제가 된다. 불균형이 문제로 여겨지는 비율은 다음과 같은 기준으로 판단한다.
일반적인 기준
불균형 처리 방법
•
Resampling
Under Sampling
Over Sampling
class weight
데이콘 실전 문제 적용해보기
class KFoldDataset(BaseKFoldDataset):
X: pd.DataFrame
y: pd.Series
X_predict: pd.DataFrame
use_oversampling: bool = False
use_undersampling: bool = False
seed: int = 2023
def setup(self) -> None:
self.X, self.X_predict = minmax_scaling(self.X, self.X_predict)
def setup_folds(self, num_folds: int) -> None:
kfold = StratifiedKFold(num_folds, shuffle=True, random_state=self.seed)
self.splits = [split for split in kfold.split(self.X, self.y)]
def setup_fold_index(self, fold_index: int) -> None:
train_indices, val_indices = self.splits[fold_index]
self.X_train, self.y_train = self.X.iloc[train_indices], self.y.iloc[train_indices]
self.X_val, self.y_val = self.X.iloc[val_indices], self.y.iloc[val_indices]
def setup_fold_end(self) -> None:
if self.use_oversampling:
value = self.y_train.value_counts().get(1)
smote = SMOTE(sampling_strategy={3:value}, random_state=self.seed)
self.X_train, self.y_train = smote.fit_resample(self.X_train, self.y_train)
if self.use_undersampling:
value = self.y_train.value_counts().get(13)
under_sampler = RandomUnderSampler(sampling_strategy={0:value, 7:value}, random_state=self.seed)
self.X_train, self.y_train = under_sampler.fit_resample(self.X_train, self.y_train)
Python
복사
경진대회 예시
•
데이콘 Basic 음악 장르 분류 AI 경진대회 4위 의 코드 일부
class KFoldDataset(BaseKFoldDataset):
X: pd.DataFrame
y: pd.Series
X_predict: pd.DataFrame
use_oversampling: bool = False
use_undersampling: bool = False
seed: int = 2023
def setup(self) -> None:
self.X, self.X_predict = minmax_scaling(self.X, self.X_predict)
def setup_folds(self, num_folds: int) -> None:
kfold = StratifiedKFold(num_folds, shuffle=True, random_state=self.seed)
self.splits = [split for split in kfold.split(self.X, self.y)]
def setup_fold_index(self, fold_index: int) -> None:
train_indices, val_indices = self.splits[fold_index]
self.X_train, self.y_train = self.X.iloc[train_indices], self.y.iloc[train_indices]
self.X_val, self.y_val = self.X.iloc[val_indices], self.y.iloc[val_indices]
def setup_fold_end(self) -> None:
if self.use_oversampling:
value = self.y_train.value_counts().get(1)
smote = SMOTE(sampling_strategy={3:value}, random_state=self.seed)
self.X_train, self.y_train = smote.fit_resample(self.X_train, self.y_train)
if self.use_undersampling:
value = self.y_train.value_counts().get(13)
under_sampler = RandomUnderSampler(sampling_strategy={0:value, 7:value}, random_state=self.seed)
self.X_train, self.y_train = under_sampler.fit_resample(self.X_train, self.y_train)
...
Python
복사
•
kfold = StratifiedKFold(n_splits=5, random_state=41, shuffle=True)
test_probs_fold = []
for train_index, valid_index in kfold.split(train_x_raw, train_y_raw):
train_x_fold, train_y_fold = train_x_raw.iloc[train_index], train_y_raw.iloc[train_index]
valid_x_fold, valid_y_fold = train_x_raw.iloc[valid_index], train_y_raw.iloc[valid_index]
### step1 ###
# scaling
scaler_ = MinMaxScaler()
scaler_.fit(train_x_fold)
train_x_sc = pd.DataFrame(scaler_.transform(train_x_fold), columns=train_x_fold.columns, index=train_x_fold.index)
unlabeled_x_sc = pd.DataFrame(scaler_.transform(unlabeled_x), columns=unlabeled_x.columns, index=unlabeled_x.index)
probs = []
for i in range(0, 10):
# resampling
sampler_ = RandomUnderSampler(random_state=i)
train_x_rs, train_y_rs = sampler_.fit_resample(train_x_sc, train_y_fold)
# training
model = MLPClassifier(random_state=8, hidden_layer_sizes=(12,))
model.fit(train_x_rs, train_y_rs)
# inference for pseudo-labeling
probs.append(model.predict_proba(unlabeled_x_sc))
preds = np.where(np.mean(np.array(probs)[:,:,1], axis=0) < 0.75, 0, 1)
Python
복사
•
월간 데이콘 뉴스 토픽 분류 AI 경진대회 27위 의 코드 일부
Python
복사