svmの実装といえば、 libsvm か SVMlight の2つ (だけじゃないが).
名前通り、確かに後者の方が早い気がする. それで別に、早いほうが精度が悪いかといえば必ずしもそうではないし. ただ、オプションの豊富さとか他ツールの豊富さは libsvm だから、 まずこちらを試すのが良いと思う.
http://www.okuma.nuee.nagoya-u.ac.jp/~sakaguti/wiki/index.php?LibSVM
以下の javascript コードはテストデータ形式にそったテキストを出力する。
// test.js
for (var i=0; i<100; ++i) {
var a = [];
for (var j=0; j<10; ++j) {
a[j] = Math.round(Math.random());
}
var t = a.reduce(function(x,y){return x+y});
t = t > 5 ? 1 : -1;
console.log("%d %s"
, t
, a.map(function(x, i){return (i+1)+':'+x}).join(' '));
}
-t <type-number>
例えば -t 0
で線形カーネルを使う. デフォルトは -t 2
のrbfカーネル.
以下のオプションを持つ。
-v <int>
-v 10
で、テストデータを10分割して、 クロスバリデーションをしてくれる.
実際に先ほどの test.js
で事例を作って訓練してみる.
# Makefile
do:
svm-train -t 0 -v 10 train.scale
train.scale: test.js
node test.js > train
svm-scale train > train.scale
出力はこうであった
(前略)
*.*
optimization finished, #iter = 102
nu = 0.112336
obj = -5.000000, rho = 0.999823
nSV = 38, nBSV = 1
Total nSV = 38
Cross Validation Accuracy = 100%
簡単すぎたようだ.
先程は、訓練データの中の一つのデータは10次元であった. 20次元にしてみる.
for (var i=0; i<100; ++i) {
var a = [];
for (var j=0; j<20; ++j) {
a[j] = Math.round(Math.random());
}
var t = a.reduce(function(x,y){return x+y});
t = t > 10 ? 1 : -1;
console.log("%d %s"
, t
, a.map(function(x, i){return (i+1)+':'+x}).join(' '));
}
$ svm-train -t 0 -v 10 train.scale
(前略)
..........*......................*
optimization finished, #iter = 2885
nu = 0.094086
obj = -4.186700, rho = 0.285305
nSV = 19, nBSV = 0
Total nSV = 19
Cross Validation Accuracy = 99%
次元数に余裕があるのがSVMである.
意味のある訓練データとして、パリティを学習させてみる. ちなみにパリティは線形分離不能なデータとして定番である.
for (var i=0; i<100; ++i) {
var a = [];
for (var j=0; j<10; ++j) {
a[j] = Math.round(Math.random());
}
var t = a.reduce(function(x,y){return x+y});
t = t % 2 ? 1 : -1;
console.log("%d %s"
, t
, a.map(function(x, i){return (i+1)+':'+x}).join(' '));
}
-t 0 : linear
-t 1 : poly
-t 2 : RBF
$ svm-train -t 0 -v 10 train.scale
(前略)
.........*
optimization finished, #iter = 860
nu = 0.669745
obj = -59.094108, rho = 0.338879
nSV = 65, nBSV = 53
Total nSV = 65
Cross Validation Accuracy = 61%
多項式
$ svm-train -t 1 -v 10 train.scale
(前略)
.*
optimization finished, #iter = 139
nu = 0.710394
obj = -43.537098, rho = 0.154695
nSV = 81, nBSV = 40
Total nSV = 81
Cross Validation Accuracy = 41%
RBF
$ svm-train -t 1 -v 10 train.scale
(前略)
*.*
optimization finished, #iter = 104
nu = 0.778061
obj = -51.085066, rho = 0.243366
nSV = 86, nBSV = 52
Total nSV = 86
Cross Validation Accuracy = 59%
for (var i=0; i<100; ++i) {
var a = [];
for (var j=0; j<10; ++j) {
a[j] = Math.round(Math.random());
}
var t = a.slice(0,2).reduce(function(x,y){return x+y});
t = t % 2 ? 1 : -1;
console.log("%d %s"
, t
, a.map(function(x, i){return (i+1)+':'+x}).join(' '));
}
10次元のうち、実は2次元で答えが決まっている.
kernel | linear | poly | RBF |
---|---|---|---|
Acc. | 52 % | 62% | 82% |
パラメータをほんとは調整しないとだけど.
デフォルトでは Accuracy しか用いない. Binary-class Cross Validation with Different Criteria で紹介されてるパッチ(?)で、他の評価尺度として
が選べるようになる. ただしコンパイル時点で選ばないといけないけど.