在scikit-learn中将分类器保存到磁盘

254

如何将训练好的朴素贝叶斯分类器保存到磁盘并使用它来预测数据?

我从scikit-learn网站上获得了以下示例程序:

from sklearn import datasets
iris = datasets.load_iris()
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
y_pred = gnb.fit(iris.data, iris.target).predict(iris.data)
print "Number of mislabeled points : %d" % (iris.target != y_pred).sum()
6个回答

252
分类器只是可以像其他任何对象一样被pickle和dump的对象。继续你的例子:
import cPickle
# save the classifier
with open('my_dumped_classifier.pkl', 'wb') as fid:
    cPickle.dump(gnb, fid)    

# load it again
with open('my_dumped_classifier.pkl', 'rb') as fid:
    gnb_loaded = cPickle.load(fid)

1
运行得非常好!我一直在尝试使用np.savez并将其加载回来,但那从来没有起过作用。非常感谢。 - Kartos
21
在Python3中,使用pickle模块,它的工作原理与此完全相同。 - MCSH

243

你也可以使用joblib.dumpjoblib.load,它比默认的Python Pickler更有效地处理数值数组。

Joblib已包含在scikit-learn中:

>>> import joblib
>>> from sklearn.datasets import load_digits
>>> from sklearn.linear_model import SGDClassifier

>>> digits = load_digits()
>>> clf = SGDClassifier().fit(digits.data, digits.target)
>>> clf.score(digits.data, digits.target)  # evaluate training error
0.9526989426822482

>>> filename = '/tmp/digits_classifier.joblib.pkl'
>>> _ = joblib.dump(clf, filename, compress=9)

>>> clf2 = joblib.load(filename)
>>> clf2
SGDClassifier(alpha=0.0001, class_weight=None, epsilon=0.1, eta0=0.0,
       fit_intercept=True, learning_rate='optimal', loss='hinge', n_iter=5,
       n_jobs=1, penalty='l2', power_t=0.5, rho=0.85, seed=0,
       shuffle=False, verbose=0, warm_start=False)
>>> clf2.score(digits.data, digits.target)
0.9526989426822482

编辑:在Python 3.8+中,如果您使用pickle协议5(而不是默认协议),则现在可以使用pickle有效地对具有大型数值数组属性的对象进行封存。


1
但据我所知,流水线工作只适用于单个工作流程。如果我想构建模型并将其存储在磁盘上,然后停止执行,那么过了一周再回来尝试从磁盘加载模型,它会抛出一个错误: - venuktan
3
如果您想要停止和恢复“fit”方法的执行,那么没有办法做到。话虽如此,“joblib.load”应该不会在使用相同版本的scikit-learn库的Python中调用成功的“joblib.dump”后引发异常。 - ogrisel
12
如果您正在使用 IPython,请勿使用 --pylab 命令行标志或 %pylab 魔法命令,因为隐式的命名空间重载已知会破坏 pickle 过程。请改用显式导入和 %matplotlib inline 魔法命令。 - ogrisel
2
请参考scikit-learn文档以了解更多信息:http://scikit-learn.org/stable/tutorial/basic/tutorial.html#model-persistence - user1448319
1
之前保存的模型是否可以重新训练?特别是SVC模型? - Uday Sawant
显示剩余2条评论

136

你正在寻找的是sklearn中称为模型持久性的功能,相关文档记录在介绍模型持久性部分。

因此,您已经初始化了分类器并长时间训练它。

clf = some.classifier()
clf.fit(X, y)

接下来你有两个选择:

1)使用Pickle

import pickle
# now you can save it to a file
with open('filename.pkl', 'wb') as f:
    pickle.dump(clf, f)

# and later you can load it
with open('filename.pkl', 'rb') as f:
    clf = pickle.load(f)

2) 使用Joblib

from sklearn.externals import joblib
# now you can save it to a file
joblib.dump(clf, 'filename.pkl') 
# and later you can load it
clf = joblib.load('filename.pkl')

再读一遍上面提到的链接会很有帮助


以上的Joblib过程对我很有效。['clf'=模型名称用于文件中]。我在一个文件中使用joblib.dump(),并使用joblib.load()在另一个文件中加载模型以节省预测时间。 - Partha Sen
2
@jtlz2 Try import joblib - Juha Palomäki

37
在许多情况下,特别是在文本分类方面,仅存储分类器是不够的,您还需要存储向量化器,以便将来可以对输入进行向量化。
import pickle
with open('model.pkl', 'wb') as fout:
  pickle.dump((vectorizer, clf), fout)

未来使用案例:

with open('model.pkl', 'rb') as fin:
  vectorizer, clf = pickle.load(fin)

X_new = vectorizer.transform(new_samples)
X_new_preds = clf.predict(X_new)

在放弃向量化器之前,可以通过以下方式删除向量化器的stop_words_属性:

vectorizer.stop_words_ = None

为了使转储更加高效。此外,如果您的分类器参数是稀疏的(大多数文本分类示例都是如此),您可以将参数从密集转换为稀疏,这将在内存消耗、加载和转储方面产生巨大的差异。通过以下方法Sparsify模型:
clf.sparsify()

这将自动适用于SGDClassifier,但如果您知道模型是稀疏的(clf.coef_中有很多零),则可以通过以下方式手动将clf.coef_转换为csr scipy sparse matrix

clf.coef_ = scipy.sparse.csr_matrix(clf.coef_)

然后您可以更有效地存储它。


1
深入的回答!只是想补充一下,在SVC的情况下,它返回一个稀疏模型参数。 - Shayan Amani

6

sklearn估计器实现了一些方法,使您可以轻松保存估计器的相关训练属性。一些估计器自己实现了__getstate__方法,但其他估计器(例如GMM)只是使用基本实现,它仅仅保存对象的内部字典:

def __getstate__(self):
    try:
        state = super(BaseEstimator, self).__getstate__()
    except AttributeError:
        state = self.__dict__.copy()

    if type(self).__module__.startswith('sklearn.'):
        return dict(state.items(), _sklearn_version=__version__)
    else:
        return state

推荐的将模型保存到磁盘的方法是使用 pickle 模块:
from sklearn import datasets
from sklearn.svm import SVC
iris = datasets.load_iris()
X = iris.data[:100, :2]
y = iris.target[:100]
model = SVC()
model.fit(X,y)
import pickle
with open('mymodel','wb') as f:
    pickle.dump(model,f)

然而,您应该保存额外的数据,以便将来可以重新训练模型,否则会遭受严重后果(例如被锁定在旧版本的sklearn中)。
文档中可知:
为了能够使用未来版本的scikit-learn重建类似的模型,必须保存一些额外的元数据和已捕获的模型: - 训练数据,例如指向不可变快照的引用 - 用于生成模型的Python源代码 - scikit-learn的版本及其依赖项 - 在培训数据上获得的交叉验证分数
这对于依赖于Cython编写的tree.pyx模块(例如IsolationForest)的集成估计器尤其如此,因为它会创建与实现的耦合,而在sklearn版本之间不保证稳定。它曾经出现过向后不兼容的变化。
如果您的模型变得非常大,并且加载变得麻烦,您也可以使用更高效的joblib。从文档中可以看到:
引用: 特别是对于scikit的情况,使用joblib的pickle替代方案(joblib.dumpjoblib.load)可能更有趣,它对于内部带有大型numpy数组的对象更有效,通常是适配的scikit-learn估计器的情况,但只能将其pickle到磁盘而不能pickle到字符串中。

1
但是只能将其pickle到磁盘而不是字符串。但是您可以使用joblib将其pickle到StringIO中。这是我一直在做的事情。 - Matthew
我的当前项目正在做类似的事情,你知道这里的“训练数据,例如对不可变快照的引用”是什么意思吗?谢谢! - Daisy QL

2
自从0.21版本以来,sklearn.externals.joblib已被弃用,并将在v0.23中被移除:

/usr/local/lib/python3.7/site-packages/sklearn/externals/joblib/init.py:15: FutureWarning: sklearn.externals.joblib在0.21版本中已被弃用,在0.23版本中将被移除。请直接从joblib导入此功能,方法是:pip install joblib。如果在加载序列化的模型时引发此警告,则可能需要使用scikit-learn 0.21+重新序列化这些模型。
warnings.warn(msg, category=FutureWarning)


因此,您需要安装joblib
pip install joblib

最后将模型写入磁盘:

import joblib
from sklearn.datasets import load_digits
from sklearn.linear_model import SGDClassifier


digits = load_digits()
clf = SGDClassifier().fit(digits.data, digits.target)

with open('myClassifier.joblib.pkl', 'wb') as f:
    joblib.dump(clf, f, compress=9)

现在,要读取转储文件,您只需要运行以下命令:
with open('myClassifier.joblib.pkl', 'rb') as f:
    my_clf = joblib.load(f)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接