sklearn 管道在应用于测试集时不记得其状态

Posted

技术标签:

【中文标题】sklearn 管道在应用于测试集时不记得其状态【英文标题】:sklearn pipeline not remembering its state when applied on the testing set 【发布时间】:2020-06-21 20:37:59 【问题描述】:

当在训练集上调用我的管道的 fit_transform 方法,然后在测试集上调用 transform 方法时,管道正在根据测试集更新其内部状态,而不是简单地应用在训练中学到的知识放。 例如。给定以下数据框:

df = pd.DataFrame(
    'Sex':['female', 'male', 'male', 'male', 'female', 'female','neutral', 'male'],
    'Survived':['no', 'no', 'yes', 'no', 'yes', 'no', 'yes', 'no']
)

并将其拆分为 X_train、X_test、y_train、y_test: 重要提示:请注意,我的拆分方式是只有测试集的值为“中性”。

X_train = df.loc[:4,'Sex']
y_train = df.loc[:4,'Survived']

X_test = df.loc[5:, 'Sex']
y_test = df.loc[5:, 'Survived']

下面我创建了一个名为 Dummifier 的转换器并插入到我的管道中(为了简单起见,管道这里只有一种方法):

class Dummifier(BaseEstimator, TransformerMixin):

    def fit(self, X, y=None):
        return self

    def transform(self, X, y=None):
        X_dummies = pd.get_dummies(X)

        return X_dummies


my_pipe = Pipeline([
    ('get_dummies', Dummifier())
])

现在,当我在训练集中调用fit_transform,然后在测试集上调用变换方法时,给定的输出如下:

X_train_trans = my_pipe.fit_transform(X_train, y_train)
X_test_trans = my_pipe.transform(X_test)

Output:
    female male neutral
5   1      0    0
6   0      0    1
7   0      1    0

问题:既然训练集中没有值“中性”,为什么转换器现在在测试集中创建一个“中性”列?

预期的输出是:

Output:
    female male 
5   1      0    
6   0      0    
7   0      1    

我已经尝试使用来自 sklearn 的 OneHotEncoder,但输出基本相同。

【问题讨论】:

只是因为你的transform方法没有使用任何状态,而pd.get_dummies没有状态。 您能再解释一下吗?我想当我调用方法“fit_transform”时,我的管道对象会从数据中学习并存储这个状态。当我调用“transform”时,管道将采用之前的状态来转换新数据,因为我使用的是相同的管道对象。 是的,但是您的转换不使用任何状态,它只是调用 pd.get_dummies,您在该代码的哪个位置使用在 fit_transform 中学习的状态? 我明白你的意思,但我不知道如何将它翻译成代码。这正是我正在寻找的答案。 【参考方案1】:

OneHotEncoderhandle_unknown 参数需要设置为ignore 以满足您的要求。这可能会有所帮助!

from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import Pipeline
import pandas as pd


df = pd.DataFrame(
    'Sex': ['female', 'male', 'male', 'male', 'female',
            'female', 'neutral', 'male'],
    'Survived': ['no', 'no', 'yes', 'no', 'yes', 'no', 'yes', 'no']
)


X_train = df.loc[:4, 'Sex'].to_frame()
y_train = df.loc[:4, 'Survived']

X_test = df.loc[5:, 'Sex'].to_frame()
y_test = df.loc[5:, 'Survived']


my_pipe = Pipeline([
    ('get_dummies', OneHotEncoder(handle_unknown='ignore'))
])

my_pipe.fit_transform(X_train)

print(my_pipe.transform(X_test).toarray())


# [[1. 0.]
#  [0. 0.]
#  [0. 1.]]

【讨论】:

【参考方案2】:

按照您的代码原样,您的两个转换输出如下:

X_train_trans

    female  male
0     1      0
1     0      1
2     0      1
3     0      1
4     1      0

X_test_trans

    female  male  neutral
5     1      0       0
6     0      0       1
7     0      1       0

您的问题是:为什么转换器现在在测试集中创建一个“中性”列?看起来原因是因为你声明X_test_trans设置为X_test_trans = my_pipe.transform(X_test),它接受X_test数据,如下:

X_test

5     female
6    neutral
7       male

代码完全按照您的要求执行。所以让我们想一个解决方案:

from sklearn.preprocessing import OneHotEncoder

df = pd.DataFrame(
    'Sex':['female', 'male', 'male', 'male', 'female', 'female','neutral', 'male'],
    'Survived':['no', 'no', 'yes', 'no', 'yes', 'no', 'yes', 'no']
)

features = pd.DataFrame(OneHotEncoder().fit_transform(df['Sex'].values.reshape(-1, 1)).toarray())

one-hot 编码器会将您的三个类别转换为学习算法可以识别的格式。在此阶段之后,您可以开始将数据拆分为测试和训练using train_test_split:

from sklearn.model_selection import train_test_split

features = pd.DataFrame(OneHotEncoder().fit_transform(df['Sex'].values.reshape(-1, 1)).toarray())
labels = df['Survived']

X_train, X_test, y_train, y_test = train_test_split(features, labels)

【讨论】:

我不能对变量进行热编码然后“train_test_split”,因为测试数据会泄露,导致性能过于乐观。 好点。我建议看看@Venkatachalam 提供的答案

以上是关于sklearn 管道在应用于测试集时不记得其状态的主要内容,如果未能解决你的问题,请参考以下文章

将多个预处理步骤应用于 sklearn 管道中的列

为啥 random.seed() 在生成数据集时不起作用?

[onDragEnd离开视口时不触发

sklearn:应用相同的缩放来训练和预测管道

管道和网格搜索的 SKLearn 错误

在 sklearn 管道中对分类变量实施 KNN 插补