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

基于StratifiedKFold的GridSearchCV中,如何在各折训练集的两类样本中分别识别并剔除异常值?

基于StratifiedKFold的GridSearchCV中,如何在各折训练集的两类样本中分别识别并剔除异常值?

嘿,这个问题抓得特别准——你已经意识到全数据集处理异常值会导致数据泄露,这可是建模里的关键坑!接下来咱们一步步搞定在分层交叉验证的每折训练集里,针对患病和健康两类样本分别处理异常值的问题:

核心思路

要解决这个问题,核心是只在每折的训练集上做异常值处理,绝对不能碰测试集的信息,而且要按类别(患病/健康)分别检测剔除。我们可以通过自定义一个符合sklearn接口的预处理类,把它嵌入到Pipeline里,再和StratifiedKFold+GridSearchCV结合起来。

步骤1:自定义按类别剔除异常值的预处理类

这个类需要实现sklearn的BaseEstimator和TransformerMixin接口,这样才能和Pipeline兼容。它会在拟合阶段(只针对训练集)计算每类样本的异常值边界,转换阶段用这些边界筛选训练集的样本,测试集则保持原样。

import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin

class ClassSpecificOutlierRemover(BaseEstimator, TransformerMixin):
    def __init__(self, threshold=1.5):
        self.threshold = threshold  # IQR的倍数,可根据需求调整
        self.lower_bounds = {}  # 存储每个类别的特征下界
        self.upper_bounds = {}  # 存储每个类别的特征上界

    def fit(self, X, y):
        # 只在训练集上拟合:按类别计算每个特征的IQR边界
        for class_label in np.unique(y):
            X_class = X[y == class_label]
            q1 = np.percentile(X_class, 25, axis=0)
            q3 = np.percentile(X_class, 75, axis=0)
            iqr = q3 - q1
            self.lower_bounds[class_label] = q1 - self.threshold * iqr
            self.upper_bounds[class_label] = q3 + self.threshold * iqr
        return self

    def transform(self, X, y=None):
        # 转换阶段:训练集传y,按边界筛选;测试集不传y,直接返回原数据
        if y is None:
            return X
        X_clean = []
        y_clean = []
        for class_label in np.unique(y):
            mask_class = y == class_label
            X_class = X[mask_class]
            y_class = y[mask_class]
            # 用拟合好的边界筛选当前类别的正常样本
            mask_valid = np.all((X_class >= self.lower_bounds[class_label]) & 
                               (X_class <= self.upper_bounds[class_label]), axis=1)
            X_clean.append(X_class[mask_valid])
            y_clean.append(y_class[mask_valid])
        return np.vstack(X_clean), np.hstack(y_clean)

步骤2:构建Pipeline并整合到GridSearchCV里

把自定义的异常值处理类和分类器(AdaBoost/RandomForest)放到Pipeline里,再搭配StratifiedKFold做交叉验证,这样每折的训练集都会先经过异常值处理,再训练分类器,完全避免数据泄露。

from sklearn.pipeline import Pipeline
from sklearn.model_selection import StratifiedKFold, GridSearchCV
from sklearn.ensemble import AdaBoostClassifier, RandomForestClassifier

# 以AdaBoost为例,RandomForest只需替换classifier即可
pipe = Pipeline([
    ('outlier_remover', ClassSpecificOutlierRemover()),
    ('classifier', AdaBoostClassifier())
])

# 设置网格搜索的参数:可以把异常值阈值也加入搜索,找到最优标准
param_grid = {
    'outlier_remover__threshold': [1.0, 1.5, 2.0],
    'classifier__n_estimators': [50, 100, 200],
    'classifier__learning_rate': [0.1, 0.5, 1.0]
}

# 初始化分层交叉验证,确保每折的类别分布和原数据集一致
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 初始化网格搜索,用你关注的平均精度作为评分指标
grid_search = GridSearchCV(pipe, param_grid, cv=skf, scoring='average_precision', n_jobs=-1)

# 拟合数据,这里要传X和y,因为自定义类需要标签来分组
grid_search.fit(X, y)

# 查看最佳结果
print(f"最佳平均精度: {grid_search.best_score_:.4f}")
print(f"最佳参数组合: {grid_search.best_params_}")

几个关键注意点

  • 异常值检测方法:我这里用的是鲁棒性强的IQR方法,如果你的数据近似正态分布,也可以换成Z-score(计算每个样本的Z值,剔除绝对值大于3的),或者用Isolation Forest这类模型,只需修改fit和transform里的逻辑就行。
  • 测试集不处理:测试集绝对不能做异常值剔除,否则会引入数据泄露——我们只能用训练集的信息来判断什么是异常值。
  • 超参数优化:把异常值的阈值也加入网格搜索,能帮你找到最适合当前数据集的异常值筛选标准,进一步提升模型性能。

备注:内容来源于stack exchange,提问作者hanpat99

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 09:29:39