获得一致的火车/生产数据的 one-hot 编码

数据挖掘 Python 熊猫 虚拟变量
2021-09-25 08:34:23

我正在构建一个需要用户输入的应用程序。目前,在训练集上,我运行以下代码,其中data是一个熊猫数据框,其中包含分类列和数值列的组合。

dummified_data = data.get_dummies()
train_data = dummified_data[:10000]
test_data = dummified_data[10000:12000]

目前,我有一个手写函数,它接收用户输入的数据并将其转换为类似虚拟数据的格式。随着列数/分类变量大小的增长,这似乎不可持续。

有没有办法一致地对训练数据和生产数据进行虚拟化?

3个回答

如果我正确理解您的问题,您想确保编码的顺序始终相同。你试过sklearn- 更具体sklearn.preprocessing.OneHotEncoder吗?

它的工作方式是你fit(或fit_transform)在你的训练样本上。然后你保存你的编码器的状态(例如你可以pickle)。然后在生产中加载此编码器并进行转换。这应该提供相同的结果。

如果由于某种原因您想远离 scikit,您还可以从您的训练样本中创建一个 dict,然后将此 dict 应用于生产 - 这应该可以通过 pandas 内置功能实现。

通过. sklearn.preprocessing.OneHotEncoder_ sklearn.pipeline.Pipeline管道将保存您fit训练数据上的状态,并对生产数据应用相同的功能

例子 :

pipeline1 = Pipeline([
                ('OneHotEncoder', OneHotEncoder())
            ])
pipeline1.fit(trainingdata.column1.values.reshape(-1,1))

这就是您创建包含 onehotencoder 的管道的方式,将您的数据放在管道上。剩下的就是将你的管道转储到一个文件中,稍后在你的生产环境中加载它,然后在你加载的管道上调用转换方法:

joblib.dump(pipeline1,"pipeline1.joblib")
# Production environment
pipeline1 = joblib.load('pipeline1.joblib')
momo = pipeline1.transform(productiondata.column1.values.reshape(-1,1)).toarray()

在这里,变量momo包含您的生产数据以及应用到它的管道(包含 one-hot 编码操作)。

我们不使用Pipeline,所以我们只需要保存OneHotEncoder状态以在不同的过程中使用。 pickle由于安全性和向后兼容性问题而joblib被皱眉,因此也被皱眉。更准确地说,我们希望找到一种更好/不同的方法来从训练中保存编码器状态,并在以后的评分中使用它。我们最终做了以下事情:

  1. 像往常一样在训练期间适应/变换
  2. 从训练时编码器中获取有序类别并将其保存为 json/yaml。
  3. 在生产运行期间,阅读有序类别并使用这些类别来补充新的编码。它需要一点动力才能开始(见下面的例子)。
  4. 更重要的是,它允许我们在生产过程中改变编码器的行为,例如使其适应迄今为止未知的类别。

以下是演示该方法的示例代码:

import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder

ds = pd.DataFrame(
    {
        "colors": ["red", "red", "white", "blue"],
        "fruits": ["apple", "orange", "apple", "orange"],
    }
)

这是我们的示例数据集

colors  fruits
0   red apple
1   red orange
2   white   apple
3   blue    orange

让我们对数据进行编码:

encoder_training = OneHotEncoder(sparse=False)
encoded_colors = encoder_training.fit_transform(np.array(ds.colors).reshape(-1, 1))
encoded_colors

编码如下:

array([[0., 1., 0.],
       [0., 1., 0.],
       [0., 0., 1.],
       [1., 0., 0.]])

正如预期的那样,这个不能容忍未知类别。

encoder_training.transform(np.array("black").reshape(-1, 1))

这将给出以下错误。

ValueError: Found unknown categories ['black'] in column 0 during transform

然而,在拟合之后,编码器包含已编码用于生成编码的类别的有序列表(因此模型正在使用这些类别)

color_categories = encoder_training.categories_

这是它的内容

color_categories

[array(['blue', 'red', 'white'], dtype=object)]

现在让我们为生产/评分创建另一个编码。但是请注意,这需要有不同的行为来处理unknown值。为什么?因为通常对生产的需求(有时甚至是负责人)可能与培训的需求不同。例如,如果编码器在包括测试/保留集在内的整个训练数据集上运行,那么它永远不必处理未知类别。

encoder_production = OneHotEncoder(
    handle_unknown="ignore", sparse=False, categories=color_categories
)

我们为它提供了从训练编码器中获取的有序类别数组。在实践中,这将在训练期间序列化为 json/yaml,并在生产 python 过程中加载回。注意:我sparse=False只是为了方便查看示例中的编码。

奇怪的是,我们不能直接使用它,首先我们需要引导它!这可以是任何东西。为避免任何类型不匹配问题,我们只需使用类别列表中的第一项

encoder_production.fit(np.array(color_categories[0][0]).reshape(-1, 1))

现在我们的编码器已经准备好迎接黄金时段了!

encoder_production.transform(np.array("red").reshape(-1, 1))

这打印:

array([[0., 1., 0.]])

与训练实例不同,这个实例可以容忍未知值

encoder_production.transform(np.array("black").reshape(-1, 1))

这打印:

array([[0., 0., 0.]])