洋食の日記

「だ・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

Rumale::SVMにRandom Recursive SVMによる分類器を追加した

はじめに

Random Recursive Support Vector Machine(R2SVM)は、ランダム射影による特徴変換を繰り返すことで、線形SVM非線形な分類器を実現する手法である。また、Rumale::SVMは、Ruby機械学習ライブラリであるRumaleと同様のインターフェースで、様々なSVMアルゴリズムを利用できるGemである。このRumale::SVMに、R2SVMを実装した。

Vinyals, O., Jia, Y., Deng, L., and Darrell, T., "Learning with Recursive Perceptual Representations," In Proc. NIPS’12, pp. 2825–2833, 2012.

インストールと使い方

Rumale::SVMのインストールは、gemコマンドで行う。

gem install rumale-svm

例として、LIBSVM Dataにあるletterデータセットの分類を行う。

wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/letter.scale.tr
wget https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/letter.scale.t

分類性能の評価のためrumale-evaluation_measureをインストールする。

gem install rumale-evaluation_measure

letterのデータを分類して、その分類の正確度を出力するスクリプトは以下の様になる。

require 'rumale/dataset'
require 'rumale/evaluation_measure'
require 'rumale/svm'

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

# R2-SVMによる分類器を生成する.
# パラメータである隠れ層の数は2, 重みパラメータは0.9, 正則化パラメータは1.0とした.
classifier = Rumale::SVM::RandomRecursiveSVC.new(n_hidden_layers: 2, beta: 0.9, reg_param: 1.0, random_seed: 42)

# 学習データで学習する.
classifier.fit(x_train, y_train)

# テストデータのラベルを推定する.
y_pred = classifier.predict(x_test)

# 推定結果の正確度を出力する.
acc = Rumale::EvaluationMeasure::Accuracy.new
puts format('Accuracy: %.1f%%', (100.0 * acc.score(y_test, y_pred)))

これを実行すると正確度は72.6%となった。

$ ruby example.rb
Accuracy: 72.6%

比較のために線形SVMを試してみる。分類器の生成の箇所を以下で置き換えればよい。

classifier = Rumale::SVM::LinearSVC.new(reg_param: 1, random_seed: 42)

結果として、線形SVMの正確度は69.7%となった。R2SVMのほうが、良い分類性能を得ている。

$ ruby example.rb
Accuracy: 69.7%

ちなみに、RBFカーネルカーネルSVMだと正確度は94.6%と、R2SVMより良い数字が得られる...まあ合う合わないがあるようだ。

アルゴリズムの説明

R2SVMについて簡単に説明する。あるD次元の入力データを\mathbf{x}_{1}\in\mathbb{R}^{D}とする。データはC個のクラスに分けられている。また、この入力データに対する線形SVMの出力を\mathbf{o}_{1}\in\mathbb{R}^{C}とする。これらと、正規分布N(0,1)に従うランダム射影行列をW_{2,1}\in\mathbb{R}^{D\times C}を用いて、以下のように特徴変換を行う。

 \displaystyle
\mathbf{x}_{2}=\sigma(\mathbf{x}_{1}+\beta W_{2,1}\mathbf{o}_{1})

ここで、\sigma(\cdot)シグモイド関数であり、\betaは元のデータ\mathbf{x}_{1}をどれくらい移動するかを制御する重みパラメータとなる。 こうして得られた\mathbf{x}_{2}を入力として線形SVMを学習して出力\mathbf{o}_{2}を得て、ランダム射影行列で...と同様の変換をフィードフォワード的に繰り返す。あるl層の変換は次の様になる。

 \displaystyle
\mathbf{x}_{l+1}=\sigma(\mathbf{x}_{1}+\beta \sum_{i=1}^{l}W_{i+1,i}\mathbf{o}_{i})

出力を連結するのではなく、ランダム射影して足し合わせるのが面白い。

よくある2-moonsデータで、R2SVMの決定境界を示すと次のようになる。

半円の端が誤分類されているが、まあデータ分布に沿った非線形な決定境界となっている。

おわりに

Random Recursive SVM(R2SVM)は、線形SVMをdeepにする方法の一つである。隠れ層の変換は、線形SVMに限らず、各クラスに対する予測スコア的なものを出力できる学習アルゴリズムであれば適用できる。発想は面白いが、手元にあるデータでは、カーネルSVMを超えるようなことはなかったので、実用性はちょっとアレかもしれない。

github.com