洋食の日記

記事をです・ます調で書き始めれば良かったと後悔している人のブログです

Rumaleのカーネル法まわりを便利にした

はじめに

Ruby機械学習ライブラリであるRumaleに、前処理としてカーネル行列を計算するクラス、カーネルリッジ回帰による分類器を追加した。また、Nystroemカーネル近似では、サポートするカーネル関数がRBFカーネルだけであったが、多項式カーネルやシグモイドカーネルを追加した。これを、ver. 0.22.5としてリリースした。

rumale | RubyGems.org | your community gem host

使い方

Rumaleはgemコマンドでインストールできる。カーネルリッジ回帰による分類器で、特異ベクトルを求める必要があるので、numo-openblasも一緒にインストールする。

$ gem install rumale numo-openblas

カーネルリッジ回帰による分類器は、あるラベルが付与されてない/されてるを {-1, 1} の目的変数に変換し(ラベルが3種類あって、あるサンプルに3番目のラベルが付与されているとすると、[-1, -1, 1]という目的変数によるベクトルになる)、これに対して回帰を行い、推論では推定値が最も大きな値に関係するラベルを返す。Rumaleでは、カーネル法による推定器に対しては、カーネル行列を与えるようになっている。scikit-learnなどでは、サンプルを与えることが多い。これと同じ使い方ができるように、前処理としてカーネル行列を計算するクラスを追加した。Pipelineでつないで使用する。

これらを用いて、分類を行う例を示す。データセットには、LIBSVM Dataからletterをダウンロードした。

$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/letter.scale.t
$ wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/letter.scale.tr
require 'numo/openblas'
require 'rumale'

# データを読み込む.
x_train, y_train = Rumale::Dataset.load_libsvm_file('letter.scale.tr')
x_test, y_test = Rumale::Dataset.load_libsvm_file('letter.scale.t')

# 与えられたサンプルからカーネル行列を計算する KernelCalculator と
# カーネルリッジ回帰による分類器の KernelRidgeClassifier を Pipeline でつなぐ.
classifier = Rumale::Pipeline::Pipeline.new(
  steps: {
    ker: Rumale::Preprocessing::KernelCalculator.new(kernel: 'poly', gamma: 1, degree: 3, coef: 1),
    krc: Rumale::KernelMachine::KernelRidgeClassifier.new(reg_param: 1)
  }
)

# 分類器を学習する.
classifier.fit(x_train, y_train)

# 正確度を出力する.
puts(format("Accuracy: %.3f", classifier.score(x_test, y_test)))

これを実行すると以下のようになる。ロジスティック回帰では、正確度は0.763だったので、カーネル法を用いることで精度が向上することがわかる。

Accuracy: 0.917

おわりに

自分のなかで、1週間ほどカーネル法リバイバルが起きて、あれこれと追加した。カーネル法は、ニューラルネットワークの隆盛で、影が薄くなってしまったが、小さいデータセットであるとか条件によっては有効であると考える。関連して、ガウス過程の手法を追加したいと考えているが、Rumale::GPとか別Gemかな〜と思っている。

github.com