在 scikit-learn 中将分类器保存到磁盘
Posted
技术标签:
【中文标题】在 scikit-learn 中将分类器保存到磁盘【英文标题】:Save classifier to disk in scikit-learn 【发布时间】:2012-05-22 11:38:13 【问题描述】:如何将经过训练的朴素贝叶斯分类器保存到磁盘并用它来预测数据?
我有以下来自 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()
【问题讨论】:
【参考方案1】:分类器只是可以像其他任何东西一样被腌制和倾倒的对象。继续你的例子:
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)
编辑:如果您使用sklearn Pipeline,其中您的自定义转换器无法通过pickle(也不能通过joblib)序列化,那么使用Neuraxle 的custom ML Pipeline saving 是一种解决方案,您可以在其中定义自己的自定义@ 987654324@ 在每一步的基础上。如果在保存时定义了保存程序,则会为每个步骤调用保存程序,否则将使用 joblib 作为没有保存程序的步骤的默认值。
【讨论】:
像魅力一样工作!我一直在尝试使用 np.savez 并将其加载回来,但这并没有帮助。非常感谢。 在python3中,使用pickle模块,它的工作原理与此完全一样。【参考方案2】:您还可以使用joblib.dump 和joblib.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 对具有大型数值数组作为属性的对象进行高效酸洗。
【讨论】:
但据我了解,如果流水线是单个工作流的一部分,它就可以工作。如果我想构建模型将其存储在磁盘上并在那里停止执行。然后我一周后回来,尝试从磁盘加载模型,它给我一个错误: 如果这是您正在寻找的,则无法停止和恢复fit
方法的执行。话虽如此,如果您从具有相同版本 scikit-learn 库的 Python 调用 joblib.dump
成功后,joblib.load
不应引发异常。
如果您使用的是 IPython,请不要使用 --pylab
命令行标志或 %pylab
魔法,因为已知隐式命名空间重载会破坏酸洗过程。改用显式导入和 %matplotlib inline
魔法。
参见 scikit-learn 文档以供参考:scikit-learn.org/stable/tutorial/basic/…
是否可以重新训练以前保存的模型?特别是 SVC 模型?【参考方案3】:
sklearn.externals.joblib
自0.21
以来已被弃用,并将在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)
【讨论】:
【参考方案4】:sklearn
估计器实现的方法使您可以轻松地保存估计器的相关训练属性。一些估算器自己实现 __getstate__
方法,但其他估算器,例如 GMM
只使用 base implementation ,它只是保存对象内部字典:
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)。
来自documentation:
为了用未来版本重建一个类似的模型 scikit-learn,额外的元数据应该沿着腌制保存 型号:
训练数据,例如对不可变快照的引用
用于生成模型的python源码
scikit-learn 的版本及其依赖项
在训练数据上得到的交叉验证分数
对于依赖于用 Cython 编写的 tree.pyx
模块(例如 IsolationForest
)的 Ensemble 估计器尤其如此,因为它创建了与实现的耦合,这不能保证在 sklearn 版本之间保持稳定。过去它已经看到了向后不兼容的变化。
如果您的模型变得非常大并且加载变得麻烦,您还可以使用更高效的joblib
。来自文档:
在scikit的具体情况下,使用起来可能会更有趣 joblib 替换
pickle
(joblib.dump
&joblib.load
),即 在内部携带大型 numpy 数组的对象上更有效 拟合的 scikit-learn 估计器通常是这种情况,但只能 pickle 到磁盘而不是字符串:
【讨论】:
but can only pickle to the disk and not to a string
但你可以从 joblib 将它腌制到 StringIO 中。这就是我一直在做的事情。
我目前的项目正在做类似的事情,你知道The training data, e.g. a reference to a immutable snapshot
这里是什么吗? TIA!【参考方案5】:
在许多情况下,特别是对于文本分类,仅存储分类器是不够的,您还需要存储矢量化器,以便将来对输入进行矢量化处理。
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_)
然后您可以更有效地存储它。
【讨论】:
有见地的回答!只是想在 SVC 的情况下添加,它返回一个稀疏模型参数。【参考方案6】:您要查找的内容在 sklearn 单词中称为 模型持久性,它记录在 introduction 和 model persistence 部分中。
所以你已经初始化了你的分类器并用它训练了很长时间
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() 将模型加载到另一个文件中以节省预测时间。 @jtlz2 试试import joblib
以上是关于在 scikit-learn 中将分类器保存到磁盘的主要内容,如果未能解决你的问题,请参考以下文章
随着时间的推移更新 NaiveBayes 分类器(在 scikit-learn 中)