我正在尝试构建一个包含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
,以便我不必这样做,如果我这样做,我是否能够从模型中获取映射?
答案 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并在计算机之间移动它们。我以前曾多次成功使用过这个过程,包括在制作系统中取得成功。