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 中使用过去?