はじめに
Ruby 25周年の記念日に何かSVMKitをバージョンアップしたくて準備を進めていた。無事に単純ベイズ(Naive Bayes, NB)分類器を追加した状態でバージョンアップできた。Scikit-learnにならってGaussian、Multinomial、Bernoulliの各種分布のアルゴリズムを追加した。この他の変更点は、Factorization Machineによる分類器に、ロス関数を選択するパラメータを追加して、hingeとlogisticを選べるようにした。
svmkit | RubyGems.org | your community gem host
使い方
まずSVMKitをインストールする。線形代数の計算で使用しているNumo::NArrayもインストールされる。
$ gem install svmkit
次に、データを用意する。今回は、単純ベイズということから、LIBSVM DATAより、文書データセットであるnews20を取得した。 TFにより重み付けされた文書ベクトルのデータセットとなる。
$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/news20.t.bz2 $ bunzip2 news20.t.bz2
Multinomialな単純ベイズの分類精度を、5交差検定でF値でみる場合、以下のようになる。
require 'svmkit' # LIBSVM形式のnews20データを読み込む. # ※やや大きいファイルなため全体的に遅め. samples, labels = SVMKit::Dataset.load_libsvm_file('news20.t') # Multinomialな単純ベイズを定義する. # 平滑化パラメータは基本的には1.0で良い. mnb = SVMKit::NaiveBayes::MultinomialNB.new(smoothing_param: 1.0) # 評価尺度はマクロ平均なF値で. ev = SVMKit::EvaluationMeasure::FScore.new(average: 'macro') # 5-交差検定で評価する. kf = SVMKit::ModelSelection::StratifiedKFold.new(n_splits: 5, shuffle: true, random_seed: 1) cv = SVMKit::ModelSelection::CrossValidation.new(estimator: mnb, splitter: kf, evaluator: ev) report = cv.perform(samples, labels) # 結果を出力する. mean_f_score = report[:test_score].inject(:+) / kf.n_splits puts(sprintf("Mean F1-Score: %.1f%%", 100.0 * mean_f_score))
これを実行すると以下の様になる。
$ ruby svmkit_nb_cv.rb Mean F1-Score: 73.0%
ちなみに、線形SVMでは、以下のようになる(コメントは一部省略)。 TFによる文書ベクトルの文書分類では、Multinomialな単純ベイズが有効であることがわかる。
require 'svmkit' samples, labels = SVMKit::Dataset.load_libsvm_file('news20.t') # 線形SVMによる分類器を作成し,それをone-vs-restで多値分類器にする. svc = SVMKit::LinearModel::SVC.new(reg_param: 1.0, max_iter: 1000, random_seed: 1) ovr_svc = SVMKit::Multiclass::OneVsRestClassifier.new(estimator: svc) ev = SVMKit::EvaluationMeasure::FScore.new(average: 'macro') kf = SVMKit::ModelSelection::StratifiedKFold.new(n_splits: 5, shuffle: true, random_seed: 1) cv = SVMKit::ModelSelection::CrossValidation.new(estimator: ovr_svc, splitter: kf, evaluator: ev) report = cv.perform(samples, labels) mean_f_score = report[:test_score].inject(:+) / kf.n_splits puts(sprintf("Mean F1-Score: %.1f%%", 100.0 * mean_f_score))
$ ruby svmkit_svc_cv.rb Mean F1-Score: 69.1%
おわりに
Ruby 25周年おめでとうございます!!