在多个程序中正确使用 Scikit 的 LabelEncoder

Posted

技术标签:

【中文标题】在多个程序中正确使用 Scikit 的 LabelEncoder【英文标题】:Using Scikit's LabelEncoder correctly across multiple programs 【发布时间】:2015-04-23 18:29:21 【问题描述】:

我手头的基本任务是

a) 读取一些制表符分隔的数据。

b) 做一些基本的预处理

c) 对于每个分类列,使用LabelEncoder 创建一个映射。这有点像这样

mapper=
#Converting Categorical Data
for x in categorical_list:
     mapper[x]=preprocessing.LabelEncoder()

for x in categorical_list:
     df[x]=mapper[x].fit_transform(df.__getattr__(x))

其中df 是pandas 数据框,categorical_list 是需要转换的列标题列表。

d) 使用 pickle 训练分类器并将其保存到磁盘

e) 现在在另一个程序中,保存的模型被加载。

f) 加载测试数据并执行相同的预处理。

g) LabelEncoder's 用于转换分类数据。

h) 模型用于预测。

现在我的问题是,g) 步骤会正常工作吗?

正如LabelEncoder 的文档所述

It can also be used to transform non-numerical labels (as long as 
they are hashable and comparable) to numerical labels.

那么每个条目每次都会散列到完全相同的值吗?

如果否,有什么好的方法可以解决这个问题。有什么方法可以检索编码器的映射?还是与 LabelEncoder 完全不同的方式?

【问题讨论】:

你可以试试这个,但是这个想法是哈希对于相同的输入是相同的 为什么不腌制这些mappers? 我试过...它只是转储 ...我如何获得这些键值对?? 【参考方案1】:

根据LabelEncoder 实现,当且仅当您在测试时fit LabelEncoders 使用具有完全相同的唯一值集的数据时,您所描述的管道才能正常工作。

有一种方法可以重用您在训练期间获得的 LabelEncoders。 LabelEncoder 只有一个属性,即classes_。可以腌制一下,然后恢复就好了

火车:

encoder = LabelEncoder()
encoder.fit(X)
numpy.save('classes.npy', encoder.classes_)

测试

encoder = LabelEncoder()
encoder.classes_ = numpy.load('classes.npy')
# Now you should be able to use encoder
# as you would do after `fit`

这似乎比使用相同的数据重新拟合更有效。

【讨论】:

这也是我想到的第一个解决方案。问题是,如果我之前编码的列有不同的值怎么办?这些唯一值不会出现在 LabelEncoder 中(也不会出现在我的模型中)。这里可能有什么解决方案? @nope:除了忽略这个功能,我没有看到任何解决方案,希望模型的性能不会显着下降。 您可以使用重新创建选项创建函数。如果数据集发生更改,则重新创建 classes.npy 文件。 @nope:您可以引入一个额外的类来表示训练期间映射的看不见的值,是的,该类在训练期间不会在任何地方使用。但是一旦你开始测试,你很可能会得到一些看不见的值。您的编码器将能够处理它,并将其简单地映射到之前创建的类,即“未见”。 我设法创建了文件,但是,在加载过程中它是一个空数组。有什么解决办法吗?【参考方案2】:

对我来说,最简单的方法是将每列的 LabelEncoder 导出为 .pkl 文件。使用fit_transform()函数后,你必须为每一列导出编码器

例如

from sklearn.preprocessing import LabelEncoder
import pickle
import pandas as pd
df_train = pd.read_csv('traing_data.csv')
le = LabelEncoder()    
df_train['Departure'] = le.fit_transform(df_train['Departure'])
#exporting the departure encoder
output = open('Departure_encoder.pkl', 'wb')
pickle.dump(le, output)
output.close()

然后在测试项目中,可以加载LabelEncoder对象,直接应用transform()函数

from sklearn.preprocessing import LabelEncoder
import pandas as pd
df_test = pd.read_csv('testing_data.csv')
#load the encoder file
import pickle 
pkl_file = open('Departure_encoder.pkl', 'rb')
le_departure = pickle.load(pkl_file) 
pkl_file.close()
df_test['Departure'] = le_departure.transform(df_test['Departure'])

【讨论】:

AttributeError: 'LabelEncoder' 对象没有属性 'classes_' @ArunGeorge 我相信我的解决方案没有提及classes_,请再试一次并告诉我是否可以提供帮助 鉴于您可能有多个要转换的列...您也可以将所有变量放入 sklearn 管道中,然后只保存 1 个对象吗?【参考方案3】:

对我有用的是LabelEncoder().fit(X_train[col]),为每个分类列col 腌制这些对象,然后在验证数据集中重用相同的对象来转换相同的分类列col。基本上,您的每个分类列都有一个标签编码器对象。

    所以fit() 在训练数据上并腌制与训练数据帧X_train 中每一列对应的对象/模型。 对于验证集X_cv 的列中的每个col,加载相应的对象/模型并通过访问转换函数来应用转换:transform(X_cv[col])

【讨论】:

【参考方案4】:

因为我没有找到其他关于名义/分类编码的帖子。我扩展了上述解决方案并分享了我的 OrdinalEncoder 方法(这可能是作者的意图)

我使用 OrdinalEncoder 执行了以下操作(但也应该使用 LabelEncoder)。请注意,我使用的是categories_ 而不是classes_

    创建编码器字典 用 numpy 保存 用 numpy 加载它 遍历字典并对每一列应用转换

注意:np 代表 numpy。

# ------- step 1 and 2 in the file/cell where the encoding shall be exported

    encoder_dict = dict()

    for nom in nominal_columns:
        enc = enc.fit(df[[nom]])
        df[[nom]] = enc.transform(df[[nom]])
        encoder_dict[nom] = [[str(cat) for cat in sublist] for sublist in enc.categories_]

    np.save('FILE_NAME.npy', encoder_dict)




# ------------ step 3 and 4 in the file where encoding shall be imported

enc = OrdinalEncoder()
encoder_dict = np.load('FILE_NAME.npy', allow_pickle=True).tolist()

    for nom in encoder_dict:
        for col in df.columns:
            if nom == col:
                enc.categories_ = encoder_dict[nom]
                df[[col]] = enc.transform(df[[col]])
    return df

【讨论】:

我为 OneHotEncoder 执行此操作,但出现错误:AttributeError: 'OneHotEncoder' object has no attribute 'drop_idx_'【参考方案5】:

如果您已经通过 pickle 保存模型,我会为预处理工具做同样的事情。

一种方法是将所有内容组合到一个类中:

class MyClassifier():
    def load_data(self):
        ...
    def fit(self):
        self.first_column_encoder = preprocessing.LabelEncoder()
        self.first_column_encoder.fit(...)
        ...
        self.second_column_encoder = preprocessing.LabelEncoder()
        self.second_column_encoder.fit(...)
        ...
        self.model = KNearestNeighbors(...)
        self.model.fit(...)
my_classifier = MyClassifier()
my_classifier.fit()

pickle.dump(my_classifier, file)

注意:您可能希望对输入类别使用 OrdinalEncoder 而不是 LabelEncoder

【讨论】:

【参考方案6】:

您可以在使用“le”对象对值进行编码后执行此操作:

encoding = 
for i in list(le.classes_):
    encoding[i]=le.transform([i])[0]

您将获得带有编码的“编码”字典以供以后使用,例如,您可以使用 pandas 将此字典导出为 csv。

【讨论】:

这不起作用,因为 OP 的步骤 e) 明确表示“在不同的程序中”。

以上是关于在多个程序中正确使用 Scikit 的 LabelEncoder的主要内容,如果未能解决你的问题,请参考以下文章

文本分类任务的最佳 scikit 分类器

如何使用 scikit 线性回归模型同时求解多个独立的时间序列

如何在 scikit-learn 中使用正确的 pyprint?

如何在 scikit-learn 中正确加载文本数据?

scikit-learn 在管道中使用多个类预处理 SVM

如何使用热门单词创建特征向量(scikit-learn中的特征选择)