読者です 読者をやめる 読者になる 読者になる

洋食の日記

洋食のことではなく、技術メモを書きます。たまにどうでも良いことも書きます。

scikit-learnで学習した分類器をjoblib.dumpで保存するときはcompressをTrueにするとファイルが一つにまとまって便利

scikit-learnで学習した分類器を保存する場合、joblib.dumpを使用するが、これだと、大量のnpyファイルが作られる。この場合、joblib.dumpのcompressを使うとよい。まず、例えば以下のような、train.pyがあるとする。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from sklearn.datasets import load_svmlight_file
from sklearn.multiclass import OneVsRestClassifier
from sklearn.svm import LinearSVC
from sklearn.externals import joblib

def main():
  # MNISTの訓練データセットを読み込む。
  tr_samples, tr_labels = load_svmlight_file('mnist.scale')

  # one-versus-restで線形SVM分類器を学習する。
  # n_jobsを-1にするとパラレルで実行してくれる。
  classifier = OneVsRestClassifier(LinearSVC(), n_jobs=-1)
  classifier.fit(tr_samples, tr_labels)

  # 学習した分類器を保存する。
  joblib.dump(classifier, 'svc.pkl')

if __name__ == '__main__':
  main()

これを実行すると、大量のnpyファイルが作られる。まあ美しくない。

$ ./train.py
$ ls -1
svc.pkl_01.npy
svc.pkl_02.npy
svc.pkl_03.npy
...
svc.pkl_51.npy
svc.pkl
train.py
mnist.scale
mnist.scale.t

学習した分類器を一つのファイルに保存したい場合は、joblib.dumpの部分でcompress引数にTrueを指定する。compressは0から9の整数をとり、値が大きくなるほど圧縮率が高まる。 Trueを指定するとcompress=3と同じになる。拡張子はなんでも良いけど、「圧縮しましたよ」ということで「.cmp」をつけてみた。

joblib.dump(classifier, 'svc.pkl.cmp', compress=True)

これを実行すると、学習した分類器が、たった一つのファイルにまとめられる。

$ ./train.py
$ ls -1
svc.pkl.cmp
train.py
mnist.scale
mnist.scale.t

読み込みは、圧縮しない場合と同様で、joblib.loadで良い。例えば以下のような、test.pyを用意する。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from sklearn.datasets import load_svmlight_file
from sklearn.externals import joblib
from sklearn.metrics import accuracy_score

def main():
  # MNISTのテストデータセットを読み込む。
  te_samples, te_labels = load_svmlight_file('mnist.scale.t')

  # 学習した分類器を読み込む。
  classifier = joblib.load('svc.pkl.cmp')

  # パラメータを表示してみる。
  print classifier

  # ラベルを推定する。
  pr_labels = classifier.predict(te_samples)

  # Accuracyを計算してみる。
  print accuracy_score(te_labels, pr_labels)

if __name__ == '__main__':
  main()

これを実行すると、one-versus-restな線形SVM分類器がちゃんと読み込まれているのがわかる。

$ ./test.py
OneVsRestClassifier(estimator=LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss=l2, multi_class=ovr, penalty=l2,
     random_state=None, tol=0.0001, verbose=0),
          estimator__C=1.0, estimator__class_weight=None,
          estimator__dual=True, estimator__fit_intercept=True,
          estimator__intercept_scaling=1, estimator__loss=l2,
          estimator__multi_class=ovr, estimator__penalty=l2,
          estimator__random_state=None, estimator__tol=0.0001,
          estimator__verbose=0, n_jobs=-1)
0.918

圧縮されているので、ファイルサイズもそれなりに小さい。 libsvmやliblinearをコマンドで使うと、重みとかパラメータが書かれたテキストファイルが作られる。 MNISTぐらいだと大したことないけど、特徴ベクトルの次元数が大きいと、ファイルサイズも大きくなってツラい。 そういう意味でも良い。

$ liblinear-train -q -s 2 mnist.scale mnist.scale.model
$ ls -l mnist.scale.model | awk '{print $4"\t"$8}'
147K    mnist.scale.model
$ ls -l svc.pkl.cmp | awk '{print $4"\t"$8}'
55K     svc.pkl.cmp