はじめに
RubyにPythonのscikit-learnに相当するライブラリがない様なので、作ってみることにした。ひとまず、Support Vector Machine(SVM)による多値分類が実装できたので、gemとして公開することにした。今後、他の機械学習アルゴリズムも追加していく。
svmkit | RubyGems.org | your community gem host
インストール
行列・ベクトルをあつかうのでNMatrixに依存する。
$ gem install svmkit
使い方
インターフェースは、scikit-learnライクにした。libsvm dataのサイトにあるpendigitsデータセットを読み込んで、分類するコードは以下の通り。 まずは、分類機の訓練から。
require 'svmkit' require 'libsvmloader' # libsvm形式の訓練データセットを読み込む。 samples, labels = LibSVMLoader.load_libsvm_file('pendigits', stype: :dense) # 特徴量の値を[0,1]の範囲に正規化する。 normalizer = SVMKit::Preprocessing::MinMaxScaler.new normalized = normalizer.fit_transform(samples) # RBFカーネル空間に(カーネル近似法で)射影する。 transformer = SVMKit::KernelApproximation::RBF.new(gamma: 2.0, n_components: 1024, random_seed: 1) transformed = transformer.fit_transform(normalized) # ベースとなる2値分類のSVMを定義して、 base_classifier = SVMKit::LinearModel::PegasosSVC.new(penalty: 1.0, max_iter: 50, batch_size: 20, random_seed: 1) # それを、One-vs.-Rest法で多値化する。 classifier = SVMKit::Multiclass::OneVsRestClassifier.new(estimator: base_classifier) # そして、分類器を学習する。 classifier.fit(transformed, labels) # 各種モデルを保存する。 File.open('trained_normalizer.dat', 'wb') { |f| f.write(Marshal.dump(normalizer)) } File.open('trained_transformer.dat', 'wb') { |f| f.write(Marshal.dump(transformer)) } File.open('trained_classifier.dat', 'wb') { |f| f.write(Marshal.dump(classifier)) }
そして、訓練済みの分類器を読み込んで、テストデータを分類するコード。
require 'svmkit' require 'libsvmloader' # libsvm形式のテストデータセットを読み込む。 samples, labels = LibSVMLoader.load_libsvm_file('pendigits.t', stype: :dense) # 各種モデルを読み込む。 normalizer = Marshal.load(File.binread('trained_normalizer.dat')) transformer = Marshal.load(File.binread('trained_transformer.dat')) classifier = Marshal.load(File.binread('trained_classifier.dat')) # 正規化とカーネル空間への射影を行う。 normalized = normalizer.transform(samples) transformed = transformer.transform(normalized) # テストデータセットのラベルを推定する。 # predicted_labels = classifier.predict(transformed, labels) # Accuracyを出力する。 puts(sprintf("Accuracy: %.1f%%", 100.0 * classifier.score(transformed, labels)))
実装よもやま話
- SVMのアルゴリズムには、確率的勾配降下法によるPegasosを選択した。 事前に、他のアルゴリズムも試したが、Pure Rubyでそれなりの速度で動くのがPegasosだった。 同じく速度の問題で、直接的なカーネル法による非線形化はあきらめて、ランダムプロジェクションによるカーネル近似を実装した。
- SVMの多値分類器化には、ひとまず、One-vs.-Rest(OvR)を選択した。scikit-learnでは、OvRなどを内包して、multiclassモジュールを意識的に使わなくて良いようになっている。SVMKitでは、明示的に使う方向とした。
- 学習済みのモデルの保存・読込には、Marshalによるシリアライズを使用することにした。 これは、scikit-learnでは、joblib.dumpを使用することに対応させてである。Marshal.loadを使うことに関しては、rubocop先輩サーセンといった感じ。
おわりに
今後は、scikit-learn内のベーシックな手法を実装していきたい。ある程度、中身が充実してきたら、gem名を変えると思う。次は、決定木系のアルゴリズムの実装かなぁ。つまらないものですが、よろしくお願い致します。