将二进制文件与sklearn模型一起保存

时间:2017-02-02 19:48:20

标签: machine-learning version-control scikit-learn

我正在尝试构建一个包含2个组件的服务。在组件1中,我通过创建Pipeline来使用sklearn训练机器学习模型。此模型使用joblib.dump(真正numpy_pickle.dump)进行序列化。组件2在云中运行,加载由(1)训练的模型,并使用它来标记它作为输入获得的文本。

我遇到了一个问题,在训练期间(组件1)我需要先将数据二值化,因为它是文本数据,这意味着模型在二进制化输入上训练,然后使用映射进行预测由二进制文件创建。当(2)基于模型进行预测时,我需要得到这个映射,以便我可以输出实际的文本标签。

我尝试将二进制化器添加到这样的管道中,认为该模型将具有映射本身:

p = Pipeline([
('binarizer', MultiLabelBinarizer()),
('vect', CountVectorizer(min_df=min_df, ngram_range=ngram_range)), 
('tfidf', TfidfTransformer()), 
('clf', OneVsRestClassifier(clf))
])

但是我收到以下错误:

model = p.fit(training_features, training_tags)
*** TypeError: fit_transform() takes 2 positional arguments but 3 were given

我的目标是确保将二进制化器和模型绑定在一起,以便消费者知道如何解码模型的输出。

这样做的现有范例是什么?我应该在我创建的其他对象中将二进制化器与模型一起序列化吗?是否有其他方法将二进制文件传递给Pipeline,以便我不必这样做,如果我这样做,我是否能够从模型中获取映射?

1 个答案:

答案 0 :(得分:5)

您应该将MultiLabelBinarizer添加到管道的直觉是解决此问题的正确方法。除了MultiLabelBinarizer.fit_transform不采用现在标准为sklearn估算器的fit_transform(self, X, y=None)方法签名之外,它本来有用。相反,它有一个我以前从未注意过的独特fit_transform(self, y)签名。由于这种差异,当你在管道上调用fit时,尝试training_tags作为第三个位置参数传递给具有两个位置参数的函数,这不是工作。

解决这个问题很棘手。我能想到解决它的最干净的方法是创建自己的MultiLabelBinarizer,它覆盖fit_transform并忽略它的第三个参数。尝试以下内容。

class MyMLB(MultiLabelBinarizer):
    def fit_transform(self, X, y=None):
        return super(MultiLabelBinarizer, self).fit_transform(X)

尝试将此添加到管道中代替MultiLabelBinarizer,看看会发生什么。如果您能够fit()管道,那么您的最后一个问题就是您的新MyMLB课程必须可以在任何系统上导入训练有素,酸洗的管道对象。最简单的方法是将MyMLB放入其自己的模块中,并将副本放在远程机器上,该副本将进行去酸洗并执行模型。那应该解决它。

我误解了MultiLabelBinarizer是如何工作的。它是输出变压器,而不是输入变压器。这不仅解释了该类的替代fit_transform()方法签名,而且还使其与包含在单个分类管道中的思想根本不相容,该管道仅限于转换输入和预测输出。但是,一切都没有丢失!

根据您的问题,您已经习惯将模型序列化为磁盘[某种形式] .pkl文件。您还应该能够序列化经过培训的MultiLabelBinarizer,然后将其解压缩并使用它来解压缩管道中的输出。我知道您正在使用joblib,但我会将此示例代码写完,就好像您正在使用pickle一样。我相信这个想法仍然适用。

X = <training_data>
y = <training_labels>

# Perform multi-label classification on class labels.
mlb = MultiLabelBinarizer()
multilabel_y = mlb.fit_transform(y)

p = Pipeline([
('vect', CountVectorizer(min_df=min_df, ngram_range=ngram_range)), 
('tfidf', TfidfTransformer()), 
('clf', OneVsRestClassifier(clf))
])

# Use multilabel classes to fit the pipeline.
p.fit(X, multilabel_y)

# Serialize both the pipeline and binarizer to disk.
with open('my_sklearn_objects.pkl', 'wb') as f:
    pickle.dump((mlb, p), f)

然后,将.pkl文件发送到远程信箱后......

# Hydrate the serialized objects.
with open('my_sklearn_objects.pkl', 'rb') as f:
    mlb, p = pickle.load(f)

X = <input data> # Get your input data from somewhere.

# Predict the classes using the pipeline
mlb_predictions = p.predict(X)

# Turn those classes into labels using the binarizer.
classes = mlb.inverse_transform(mlb_predictions)

# Do something with predicted classes.
<...>

这是这样做的范例吗?据我所知,是的。不仅如此,如果你想将它们保持在一起(我认为这是一个好主意)你可以将它们序列化为tuple,就像我在上面的例子中所做的那样,它们只保留在一个文件中。无需序列化自定义对象或类似的东西。

通过pickle 的模型序列化是运行之间的sklearn approved way to save estimators并在计算机之间移动它们。我以前曾多次成功使用过这个过程,包括在制作系统中取得成功。