Sklearn Transformers:如何将编码器应用于多个列并在生产中重用?

Posted

技术标签:

【中文标题】Sklearn Transformers:如何将编码器应用于多个列并在生产中重用?【英文标题】:Sklearn Transformers: How to apply encoder to multiple columns and reuse it in production? 【发布时间】:2021-12-27 08:30:14 【问题描述】:

我在训练期间使用标签编码器,并希望通过保存并稍后加载来在生产中使用相同的编码器。无论我在网上找到什么解决方案,都只允许 Label Encoder 一次应用于单个列,如下所示:

for col in col_list:
    df[col]= df[[col]].apply(LabelEncoder().fit_transform)

在这种情况下,我该如何保存并稍后使用?因为我尝试拟合整个数据帧,但出现以下错误。

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
C:\Users\DA~1\AppData\Local\Temp/ipykernel_3884/730613134.py in <module>
----> 1 l_enc.fit_transform(df_join[le_col].astype(str))

~\anaconda3\envs\ReturnRate\lib\site-packages\sklearn\preprocessing\_label.py in fit_transform(self, y)
    113             Encoded labels.
    114         """
--> 115         y = column_or_1d(y, warn=True)
    116         self.classes_, y = _unique(y, return_inverse=True)
    117         return y

~\anaconda3\envs\ReturnRate\lib\site-packages\sklearn\utils\validation.py in column_or_1d(y, warn)
   1022         return np.ravel(y)
   1023 
-> 1024     raise ValueError(
   1025         "y should be a 1d array, got an array of shape  instead.".format(shape)
   1026     )

ValueError: y should be a 1d array, got an array of shape (3949037, 14) instead.

我想将标签编码器安装到具有 10 列(所有分类)的数据帧中,保存并稍后在生产中加载。

【问题讨论】:

您是否尝试对分类预测变量进行编码?如果是这样,您应该使用 onehot 编码而不是标签编码器。标签编码器适用于您的目标变量 我使用的是树分类器,它不需要一个热编码,而且类别数量非常多,因此标签编码。我知道标签编码器可以用于每一列并单独保存,但想知道是否有更简单的方法? 【参考方案1】:

首先,我想指出labelEncoder 用于编码目标变量。如果你在你的预测变量上应用 labelEncoder,你会让它们连续,例如 0、1、2、3 等,这可能没有意义。

对于分类预测变量,您应该使用onehotencoding。

如果你确定 labelencode,它是这样的:

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

df = pd.DataFrame('f1':np.random.choice(['a','b','c'],100),
'f2':np.random.choice(['x','y','z'],100))

col_list = ['f1','f2']

df[col_list].apply(LabelEncoder().fit_transform)

如果要保留编码器,可以将其存储在字典中:

le = 
for col in col_list:
    le[col] = LabelEncoder().fit(df[col].values)

le['f1'].transform(df['f1'])

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

for col in col_list:
    df[col] = le[col].transform(df[col])

我再次考虑一下使用 labelEncoding 是否正确。

【讨论】:

感谢您的回答。根据您的评论,该标签编码器适用于目标变量,当存在数千个类别时,您如何将类别(对于自变量)编码为数字(我也读到树分类器在 OHE 上的表现比 LE 更差)?我的意思是,我在想,要将这些文本类别转换为数字,标签编码是不错的选择,如果我在这里错了,请纠正我。 LE 仅在您的类别为序数时才有效,这意味着它们具有某种有意义的排名。如果不是,你只能做 OHE,或者你通过组合它们来减少类别的数量 我尝试了这个解决方案并将le 保存到pickle 文件中。加载此泡菜文件并拟合相同的数据后,我收到以下错误:ValueError: y contains previously unseen labels: 0。得到这个错误是相同的数据。附加到字典的编码器有可能是空的吗? 你覆盖了原来的 df。您可以保留原始类别的数据框副本【参考方案2】:

正如@StupidWolf 所说,LabelEncoder 应该仅用于编码目标变量。

scikit-learn 提供了多种编码特征向量分类变量的方法:

OneHotEncoder 将类别编码为一个热门数值 OrdinalEncoder 将类别编码为数值。

OrdinalEncoder 执行与LabelEncoder 相同的操作,但针对特征值。

您可以使用ColumnTransformer 将不同的预处理封装到一个对象中,稍后可以使用pickle 轻松保存,如下例所示:

from sklearn.compose import make_column_transformer, make_column_selector
from sklearn.preprocessing import OrdinalEncoder
import pandas as pd
import numpy as np

df = pd.DataFrame(
    
        "col_1": ["A", "B", "B", "D", "E", "F", "A", "B"],
        "col_2": ["X", "X", "X", "Y", "Y", "Z", "Z", "X"],
        "col_3": [42] * 8,
    
)
cols = ["col_1", "col_2"]

pre_processeing = make_column_transformer(
    (OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=np.nan), cols)
)


df.loc[:, cols] = pre_processeing.fit_transform(df[cols])

哪个输出:

   col_1  col_2  col_3
0    0.0    0.0     42
1    1.0    0.0     42
2    1.0    0.0     42
3    2.0    1.0     42
4    3.0    1.0     42
5    4.0    2.0     42
6    0.0    2.0     42
7    1.0    0.0     42

一旦安装好预处理,就可以使用pickle 轻松存储和加载它,如下所示:

import pickle

#Dump preprocessing
pickle.dump(pre_processeing, open("pre_processing.p", "wb"))

#Load preprocessing
pre_processeing = pickle.load(open("pre_processing.p", "rb"))

最后,我主张ColumnTransformer 具有以下好处:

可以轻松添加针对不同列的额外预处理(例如,StandardScaler 用于数值) 此预处理可以包含在 Pipeline 中,以拥有一个执行预测的端到端模型 这是一个可以使用pickle 轻松序列化的单个对象。

【讨论】:

【参考方案3】:

TL;DR使用特征引擎库中的SklearnTransformerWrapper

from feature_engine.wrappers import SklearnTransformerWrapper

multi_col_transformer = SklearnTransformerWrapper(
    transformer=# your sklearn- transformer,
    variables=# variables to apply transformation to
)

如果您正在寻找具有不同库功能引擎categorical encoders 的方法,请接受variables 参数以将计算应用于多个列。另一种方法是使用SklearnTransformerWrapper 将任何 Scikit-learn 转换器应用于一组变量。

from feature_engine.wrappers import SklearnTransformerWrapper
from sklearn.preprocessing import OrdinalEncoder

import pandas as pd
import joblib

transformer_name = 'multi_col_encoder.joblib'
cols_list = [
    'country',
    'category'
]

d_train = 
    'country': ['AR', 'AR', 'MEX', 'BR', 'MEX'],
    'category': ['Small', 'High', 'High', 'Medium', 'Small']


d_unknown = 
    'country': ['BR', 'AR', 'MEX'],
    'category': ['High', 'Medium', 'Small']


df_train = pd.DataFrame(d_train)
df_unknown = pd.DataFrame(d_unknown)

# Apply a sklearn enconder to multiple columns with SklearnTransformerWrapper
multi_col_oe = SklearnTransformerWrapper(
    transformer=OrdinalEncoder(
        handle_unknown='use_encoded_value', unknown_value=-1
    ),
    variables=cols_list
)

df_train = multi_col_oe.fit_transform(df_train)

df_train
>>>
country category
0   0.0 2.0
1   0.0 0.0
2   2.0 0.0
3   1.0 1.0
4   2.0 2.0

# Dump transformer to use later
joblib.dump(multi_col_oe, transformer_name)

# Load transformer with learned encoder logic
transformer_for_prod = joblib.load(transformer_name)

# Apply transformer to unknown data
transformer_for_prod.transform(df_unknown)
>>>
country category
0   1.0 0.0
1   0.0 1.0
2   2.0 2.0

正如@StupidWolf 所说,如果您假设分类变量中的顺序,您应该使用OrdinalEncoder 而不是LabelEncoder。尽管它们执行类似的计算,OrdinalEncoder 允许您处理以后可能遇到的未知值。

最后,Feature Engine Categorical Encoders 还提供了编码分类变量的其他可能性。

【讨论】:

以上是关于Sklearn Transformers:如何将编码器应用于多个列并在生产中重用?的主要内容,如果未能解决你的问题,请参考以下文章

如何下载 HuggingFace 模型“transformers.trainer.Trainer”?

如何使用 Transformers 库从 XLNet 的输出中获取单词

如何理解 BertModel 中回报的 hidden_​​states?(huggingface-transformers)

如何在 HuggingFace Transformers GPT-2 中使用过去?

如何在 TensorFlow 中使用 Hugging Face Transformers 库对自定义数据进行文本分类?

输入文件应该如何格式化以进行语言模型微调(BERT 通过 Huggingface Transformers)?