You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对高度不平衡二分类数据集进行自定义采样以划分训练测试集?

嗨,针对你的高度不平衡数据集问题,我来给你梳理几种实用的自定义采样实现方式,都是在你现有train_test_split代码基础上扩展的,而且要记住:采样只应该在训练集上进行,绝对不能碰测试集,避免数据泄露影响模型评估的准确性。

一、欠采样(减少多数类样本)

欠采样是从数量多的标签0样本里挑选一部分,让它和标签1的样本数量接近。

1. 随机欠采样(简单直接)

你可以用imblearn库的工具,也可以手动实现:

方法一:用imblearn库

先安装依赖(如果没装的话):

pip install imblearn

然后修改代码(注意:sklearn.cross_validation已经废弃,建议替换为sklearn.model_selection):

from sklearn.model_selection import train_test_split
from imblearn.under_sampling import RandomUnderSampler

X = Actual_DataFrame.copy()
y = X.pop('Attrition')

# 先划分训练测试集,保持分层抽样
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.20, random_state=42, stratify=y)

# 初始化欠采样器:sampling_strategy设为'auto'会自动让两类样本数量一致
rus = RandomUnderSampler(sampling_strategy='auto', random_state=42)
X_train_resampled, y_train_resampled = rus.fit_resample(X_train, y_train)

# 现在X_train_resampled和y_train_resampled就是平衡后的训练集了

方法二:手动实现随机欠采样

如果不想用第三方库,自己写也很简单:

import pandas as pd

# 划分训练测试集后,合并成完整训练DataFrame
train_df = pd.concat([X_train, y_train], axis=1)

# 分离多数类(标签0)和少数类(标签1)
class_0 = train_df[train_df['Attrition'] == 0]
class_1 = train_df[train_df['Attrition'] == 1]

# 从多数类里随机挑选和少数类数量相等的样本
class_0_sampled = class_0.sample(n=len(class_1), random_state=42)

# 合并成平衡训练集
balanced_train_df = pd.concat([class_0_sampled, class_1], axis=0)

# 拆分回特征和标签
X_train_resampled = balanced_train_df.drop('Attrition', axis=1)
y_train_resampled = balanced_train_df['Attrition']
二、过采样(增加少数类样本)

过采样是对数量少的标签1样本进行复制或生成合成样本,让它和标签0的数量匹配。

1. 随机过采样(简单复制)

手动实现逻辑如下:

import pandas as pd

# 划分训练测试集后合并训练集
train_df = pd.concat([X_train, y_train], axis=1)

class_0 = train_df[train_df['Attrition'] == 0]
class_1 = train_df[train_df['Attrition'] == 1]

# 重复采样少数类样本,直到和多数类数量一致(replace=True允许重复采样)
class_1_sampled = class_1.sample(n=len(class_0), replace=True, random_state=42)

# 合并成平衡训练集
balanced_train_df = pd.concat([class_0, class_1_sampled], axis=0)

X_train_resampled = balanced_train_df.drop('Attrition', axis=1)
y_train_resampled = balanced_train_df['Attrition']

2. SMOTE合成过采样(生成新样本)

SMOTE通过插值生成新的少数类样本,比单纯复制更能避免过拟合,同样用imblearn实现:

from imblearn.over_sampling import SMOTE

# 划分训练测试集后
smote = SMOTE(sampling_strategy='auto', random_state=42)
X_train_resampled, y_train_resampled = smote.fit_resample(X_train, y_train)
三、混合采样(欠采样+过采样结合)

单独欠采样可能丢失多数类信息,单独过采样容易过拟合,混合采样是折中方案,比如先用SMOTE过采样少数类,再对多数类做欠采样:

from imblearn.combine import SMOTEENN

smote_enn = SMOTEENN(random_state=42)
X_train_resampled, y_train_resampled = smote_enn.fit_resample(X_train, y_train)
四、完全自定义采样逻辑

如果你想根据特定规则采样(比如选择多数类中特征分布和少数类接近的样本),可以自己写逻辑,比如基于KNN选择近邻多数类样本:

from sklearn.neighbors import NearestNeighbors
import numpy as np
import pandas as pd

# 划分训练测试集后
train_df = pd.concat([X_train, y_train], axis=1)
class_0 = train_df[train_df['Attrition'] == 0]
class_1 = train_df[train_df['Attrition'] == 1]

# 用少数类样本训练KNN,找到每个少数类样本的5个近邻多数类样本
nn = NearestNeighbors(n_neighbors=5)
nn.fit(class_0.drop('Attrition', axis=1))
_, indices = nn.kneighbors(class_1.drop('Attrition', axis=1))

# 收集这些近邻样本并去重
selected_class_0_indices = np.unique(indices.flatten())
class_0_sampled = class_0.iloc[selected_class_0_indices]

# 合并成平衡训练集
balanced_train_df = pd.concat([class_0_sampled, class_1], axis=0)
X_train_resampled = balanced_train_df.drop('Attrition', axis=1)
y_train_resampled = balanced_train_df['Attrition']

最后提醒:采样后训练模型时,评估一定要用原始的X_test和y_test,这样才能真实反映模型在不平衡数据上的泛化能力。

内容的提问来源于stack exchange,提问作者Student of the Digital World

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:15:36