2017-02-02 57 views
1

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

我遇到了一个问题,在培训期间(组件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,这样我就不必这样做了,如果我这样做了,我能从模型中获得映射吗?

回答

2

您的直觉应该将MultiLabelBinarizer添加到管道中是解决此问题的正确方法。它会起作用,但MultiLabelBinarizer.fit_transform不采用fit_transform(self, X, y=None)方法签名,现在是sklearn估计器的标准。相反,它有一个我从未注意过的独特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是如何工作的。它是输出变压器,而不是输入变压器。这不仅解释了该类别的替代方法签名,而且还使其基本上与包含在单一分类管道中的想法不兼容,该管道仅限于转换输入和预测输出。但是,一切都不会丢失!

根据你的问题,你已经习惯于将你的模型序列化到磁盘上[某种形式] .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 et al。是运行之间的sklearn approved way to save estimators并在计算机之间移动它们。我之前多次成功地使用过这个过程,包括成功的生产系统。

+0

感谢您的建议!我认为这很接近,但还没有奏效。我使用了你建议的类定义作为管道中的第一步,但是我得到'*** AttributeError:'numpy.ndarray'对象没有属性'lower''。我认为最后一行应该以'fit_transform(y)'结尾,因为我想要转换标记,而不是输入文本,但即使我修复了这个问题,由于_pre_transform被写入的方式也会导致相同的错误标签最终会覆盖'Xt = transform.fit_transform(Xt,y,** fit_params_steps [name])')中的训练数据。也许我还错过了什么? – LateCoder