例如,我有以下训练集。
name values0 Tony 1001 Smith 1102 Sam 1203 Shane 1304 Sam 1405 Ram 160
经过独热编码后变为
values 0 1 2 3 4 0 100 1 0 0 0 01 110 0 1 0 0 02 120 0 0 1 0 03 130 0 0 0 1 0 4 140 0 0 1 0 05 160 0 0 0 0 1
现在假设我在生产环境中拥有测试数据,其中Danny
是name
中的新类别:
name values0 Shane 2001 Danny 2102 Sam 2203 Tony 1804 Danny 150
对其进行独热编码后
values 0 1 2 3 0 200 1 0 0 0 1 210 0 1 0 02 220 0 0 1 03 180 0 0 0 14 150 0 1 0 0
基于以上情况,我有几个问题:
- 如何处理生产环境测试数据中类别变量的新类别或新值?
- 如何保持模型的输入特征大小(以上示例中,训练集为6,测试集为5)?
Tony
在训练集中是特征0,但在测试集中是特征3;这是否会影响训练模型对测试输入的预测?
回答:
如何处理生产环境测试数据中的新类别?
OneHotEncoder
针对这个问题有一个超参数:handle_unknown
handle_unknown{‘error’, ‘ignore’}, default=’error’当转换过程中出现未知类别特征时,是否引发错误或忽略(默认为引发错误)。当此参数设置为‘ignore’且在转换过程中遇到未知类别时,该特征的独热编码列将全部为零。在反向转换中,未知类别将被标记为None。
如您所见,此超参数有两个不同的值。如果您的测试中可能出现新类别(如您的例子中的Danny),我建议使用ignore
值:
enc = OneHotEncoder(handle_unknown='ignore')
如何保持模型的输入特征大小?类别的顺序如何?
模型将始终保持拟合数据的输入特征大小。例如,使用您提供的数据,如果您用训练数据拟合OneHotEncoder
,您将始终有6个输入。
并且,这些输入将始终具有与训练数据相同的类别。我的意思是,在您的数据中,特征0将始终指代Tony,特征1指代Smith,依此类推…
如果您想将拟合的OneHotEncoder传输到另一个脚本中,可以使用joblib
库。例如:
import joblibenc = OneHotEncoder(handle_unknown='error')enc.fit(data)joblib.dump(enc, 'encoder.joblib')
然后,在另一个脚本中加载:
enc = joblib.load('encoder.joblib')
澄清
最后,我想澄清一下过程以及您如何进行独热编码,因为我认为这并不完全清楚:
-
对于独热编码,首先,您需要
fit
到一个数据集上(几乎总是训练数据)。在这一步您做了什么?基本上,您是在告诉有多少类别、哪些类别以及类别的顺序(在您的情况下:您有6个类别,顺序为:Tony、Smith、…) -
然后,您可以使用之前拟合的OneHotEncoder通过
transform
转换任何数据。例如,您测试的结果将是:
Shane,即使它是您测试数据中出现的第一个类别,它仍将是特征3(因此在特征3中会有一个1,其他特征为零),因为它是在使用训练数据的fit
部分中定义的。
Danny,在特征中不会有任何1,因为这个名字在训练数据中没有出现。正如我们在问题1中所说,如果您将超参数handle_unknow
设置为error
,您将得到一个错误,如果设置为ignore
,您将继续使用所有特征为0。
因此,基本上,如您所见,您首先拟合到一个数据,然后应用您学到的知识来转换另一个数据。您只需拟合一次OneHotEncoder
即可。
注意:您可以通过执行fit_transform
一步完成拟合和转换训练数据