libsvm

久しぶりにニュートン誌を読んだ。脳とニューロンですって。今のオイラーにぴったりな 話題だな。

巻頭に、The Human Connectome Projectで得られた、脳内の配線図が表示されてた。MRIを使い1200人の脳を調べた結果らしい。

脳味噌の中は複雑。多数の脳細胞(ニューロン)が相互接続されてる。ニューロン間は軸索に よって配線され、端にあるシナプスによって次にニューロンに接続。

これを超簡単にしたやつが、今までやってきた深層学習のモデルなんだな。簡単すぎるモデル化 でも、そこそこの成果が出てる。より深く本物の脳が解析されれば、その成果がコンピュータ上へフィードバックされるに違いない。

で、この配線を介して、電気信号が伝わるわけだけど、配線材なので絶縁が必要とか。絶縁材は、オリゴデンドロサイトとか言う糖の一種だそうだ。ずっと絶縁材で覆われているかと言うと、 そうではなくて、所々でこの絶縁が切れているらしい。

信号は、ナトリウムイオンとカリウムイオンのせめぎ合いで伝わるらしく、絶縁の切れ目から ナトリウムを補給してるらしい。絶縁が無いと、信号が伝わる速度は、秒速1mとか。絶縁が 有ると、秒速100mになるらしい。こうなると、自然も妙だよな。

シナプス間の信号伝達は、電気を一度グルタミン酸等の物質に変え、それを伝達。受け取った方は、その伝達物質を再び電気信号に戻すらしい。素材で結合していないから、ニューロンの 接続先を容易に変更出来るんだな。もう、なんと言ったらいいのやら。

更に驚くのは、元気なニューロン(活性化)だけではだめだそうだ。抑制的なニューロンも 必要とか。シナプスで信号を受け取るんだけど、それの機能をチョコレートや玄米に含まれる GABA等で妨げられるそうだ。

1000人あたり5から8人ぐらい発症すると言う、てんかんは、この抑制ニューロンの活動が 低下する事によっても起こるらしい。

コネクトームあたりを起点に、潜っていくと面白いかも。

scikit-learn

世には色々な機械学習用ツールが有る。が、本格的にやろうとするとGPUを入れろとなる。 これはもう、自分のマシンをスパコン化せいと言われているに等しい。 そう、スパコンってひたすら掛け算と足し算を行うやつ。掛け算と足し算を同時に 行えればスピードアップになるんで、積和を一発で行えるようになってるのかな? スパコン持っていないので、よく知らない。

そう言えば、アプル品を扱ってる店に行ったら、ipadはスパコンなんて言う キャチャーなポスターが貼ってあったような。そうか、オイラーは既にスパコンユーザーか? そろそろ電池が本格的にヘタってきているんで、アプルに依頼しないとな。

ひょっとして、電池はまだ元気ですって言い張られるのかな。そして超重症になった時、 新しいマシンにリプレースしましょとか言われたりしてね。本当にスパコンユーザーに なっちまうよ。

で、こんな世の中の流れに逆らって、GPUは当面サポートしませんと、庶民の味方をして くれてる機械学習のツールがある。そう、scikit-learnだ。

Will you add GPU support?

No, or at least not in the near future.
The main reason is that GPU support will introduce many software dependencies
and introduce platform specific issues.
scikit-learn is designed to be easy to install on a wide variety of platforms.
Outside of neural networks, GPUs don’t play a large role in machine learning
today, and much larger gains in speed can often be achieved by a careful
choice of algorithms.

意訳すると、GPUを入れようとすると、色々余計なやつが必要になって、簡単インストール 出来なくなるじゃん。そんな馬鹿力に頼る前に、頭を使ってアルゴリズムを磨け。そうだよな。 その通りですだ。まあ、普通にGPUを入れて喜ぶ人はゲーム大好き人だけでしょうからね。 そんなにみんながGPU持ってる訳じゃないんよ。

scikit-learn でニューラルネットワーク

今までニューラルネットワークをやってきたので、これをscikit-learnでやったら どうなるって思って、探してみた。

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier

iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
clf = MLPClassifier(solver="sgd",random_state=0,max_iter=10000)
clf.fit(X_train, y_train)
print (clf.score(X_test, y_test))

テストに使えるようにデータセットを内蔵してる。統計言語Rから取り入れたんだな。 有名なアヤメのデータベース。花弁と顎の大きさがデータとして与えられ、それを種類に よって分類するって事かな。種類が教師データになるのか。

データは、こんな感じ。詳しい説明は、iris.DESCR すれば出てくる。

In [51]: iris.target.shape
(150,)

In [56]: iris.data.shape
(150, 4)

150個のデータのうち30%を後の検証用に取っておき、70%を訓練用に使うとな。 MLPClassifierが深層学習機になるのかな。fitメソッドでトレーニングし。検証データで 正解率を弾き出す。

MLPClassifier(activation='relu', alpha=0.0001, batch_size='auto', beta_1=0.9,
       beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=(100,), learning_rate='constant',
       learning_rate_init=0.001, max_iter=10000, momentum=0.9,
       nesterovs_momentum=True, power_t=0.5, random_state=0, shuffle=True,
       solver='sgd', tol=0.0001, validation_fraction=0.1, verbose=False,
       warm_start=False)

 0.955555555556

実際に走らせてみると、学習機のパラメータを報告した後、結果が表示された。 わずか、これだけで済んでしまうのはありがたい。

実例に機械学習

やっぱり実例でやってみないとな。今まで何回も登場してる血圧測定データから 朝測ったものか寝る時のものかを占う事にする。

で、早速難儀なのが待ってた。CSVでデータが有るんだけど、それをどうやって 取り込むか。そして、アヤメのデータ風に加工するか。それさえ出来てしまえば、 後は公式に当てはめるが如くで済んでしまうはず。

あちこちを歩きながら、下記のコードを仕立てた。(後で使うSVMってのも入って いるけどね)

import pandas as pd
from sklearn.neural_network import MLPClassifier
from sklearn import svm
from sklearn import preprocessing

def get_bld(file):
    a = pd.read_csv(file, names=('dt','hi','lo','pl'))
    a['am'] = 0
    a['pm'] = 1
    a['t']  = a['dt'] % 100
    a['y']  = a['am'].where(a['t'] < 12, a['pm'])
    return a[['hi', 'lo', 'pl']], a['y']

def main():
    min_max_scaler = preprocessing.MinMaxScaler()
    X, Y = get_bld('old.csv')
#    clf = svm.SVC(gamma=0.001, C=5.)
    clf = MLPClassifier(solver="sgd",random_state=0,max_iter=10000)
    clf.fit(min_max_scaler.fit_transform(X), Y)
    U, V = get_bld('2016.csv')
    pred = clf.predict(min_max_scaler.fit_transform(U))
    print (pred)
    print (clf.score(min_max_scaler.fit_transform(U), V))

if __name__ == "__main__":
    main()

pandasを使って、CSVデータを読み込んでいる。ヘッダーが必須みたいなので、 パラメータとして与えている。これで元ファイルを変更する事なく 使えるので便利。

In [1]: import pandas as pd

In [2]: a = pd.read_csv('z.csv', names=('dt','hi','lo','pl'))

In [3]: a
         dt   hi  lo  pl
0  15111204  126  78  53
1  15111221  120  64  61
2  15111304  130  73  55
3  15111321  128  73  59
4  15111405  135  71  55

In [4]: a['hi']
0    126
1    120
2    130
3    128
4    135
Name: hi, dtype: int64

In [5]: a.ix[1,2]
64

一応、データフレームなんだけど、辞書のように列名を指定して取り出したり、インデックスを 指定して取り出せたり出来る。

教師データをdtから作り出すには、普通for文とかで回すけど、今回はダミー列を作り、 if式もどきのwhereを使って、新しい列にデータをコピーしてみた。

で、走らせてみると、見事に思考停止。結果は0.5 予想値は全て朝と出た。以前にも こういうのに見舞われたな。生データを与えると脳が飽和してしまうやつ。解決策は、 データを正規化する事。先を急ぐので、max-minの間でスケーリングしてくれる お助け関数に任せた。

octaveだと、列毎にスケーリングする事が、簡単に出来るそうだ。まあ、ここはPython陣地 だから、無い物ねだりはいけません。

>> data=rand(5,5)*1000
data =

   640.249   573.258   192.627    89.117   337.549
   161.464   108.248   876.324   444.880   203.637
   322.163   669.243   671.130   782.296   983.408
   545.488   656.322   429.707   574.102   174.384
   703.100   517.967   698.308   436.635   137.437

>> (data - repmat(min(data,[],1),size(data,1),1))*spdiags(1./(max(data,[],1)-min(data,[],1))',0,size(data,2),size(data,2))
ans =

   0.88396   0.82890   0.00000   0.00000   0.23655
   0.00000   0.00000   1.00000   0.51323   0.07825
   0.29669   1.00000   0.69988   1.00000   1.00000
   0.70901   0.97697   0.34676   0.69965   0.04367
   1.00000   0.73034   0.73963   0.50134   0.00000

走らせてみる。予測値のpredはうざいので、表示を止め、結果だけ。

In [11]: run -t bld.py
0.946502057613

IPython CPU timings (estimated):
  User   :       7.42 s.
  System :       0.02 s.
Wall time:       3.73 s.

Wall時間ってのは、文字通り壁にかかってる時計で測った時間。対してUser時間はUserモード でCPUを利用した時間。CPU 2個分が利用されたから、実時間の倍になってるんだな。 さすがインテル製のライブラリィーだ。2Coreをきちんと使ってくれている。

run -p (若しくは、prun)すると、何処で時間を喰っているか出てくるんだけど、色々なものを呼んでいて 捉えどころが無かったぞ。

で、チュートリアルにあるsvmとかでやってみると

In [12]: run -t bld.py
0.925925925926

IPython CPU timings (estimated):
  User   :       0.51 s.
  System :       0.00 s.
Wall time:       0.51 s.

こちらの方が断然速い。計算も軽いんだろうな。貧者の機械学習だな。ニューラルネットの 方は、GPUを買える富豪用だ。

svmは良く知らないので、調べてみるか。

libsvm

サポートベクターマシン(SVM)

これって色分け(ラベリング)されたデータが多数有る時、それを分ける境界線を引きたい。 なるべくなら、敵同士が接近してもいざこざが起きにくいように、裕度を取って引きたい って問題なんだな。

戦争用語で言うと、DMZ(非武装緩衝地帯)が広くなるように、国境を定めたい。 どの国も、国境がどうのこうの、あの島は元々この国のものだとやってますから、切実な 問題な訳よ。

国境問題と言えば、現代版の万里の長城(ミニ版)を築くと息巻いている、A家の家主が居ますなあ。

去年までは、向こう三軒両隣と、A家、C家、M家が仲良く融通しあって暮らしていました。 今年になって、A家の家主が代わりました。代わった途端に、M家側から侵入してくる、 ネズミやら猫の尿に悩まされていたのを断絶する為、壁を作ると宣言しました。 費用は、立て替えておくけど、そのうちM家に請求して出させる腹積りとか。概算費用は6兆円とか。

もっと仲良くやればいいのに。ああ、老人は赤ちゃん還りするから、本能だけで 行動するようになるんだな。だめだこりゃ。てんやわんやで、A家がひっくりかえらない ように祈るばかりですよ。

ああ、話がそれた。

10秒で設定可能なlibsvmで機械学習を行う

機械学習ライブラリ LIBSVM の話

サポートベクトルマシンで MNIST 手書き数字データを分類する

pythonでSVM実装

SVMを使いこなす!チェックポイント8つ

Python 機械学習

libsvm for octave

以前libsvmをrubyから使うってのが有った。有名なやつなんで、ソースを覗いてみる事に する。debianだと、cd /tmp; apt-get source libsbm すればいいんだな。

お取り寄せして中を覗いてみたら、python用とかjava用とかmatlab用とかのバインディングが 用意されてた。皆様に愛されている鉄板なライブラリィーな訳ね。

簡単なlibsvmの使い方(OCTAVE)

こういう人もおられるしね。オイラーもoctaveを拡張してみたいぞ。

sakae@debian:~/libsvm-3.12/matlab$ make octave
make: /usr/local/matlab/bin/mexext: コマンドが見つかりませんでした
make[1]: Entering directory '/home/sakae/libsvm-3.12/matlab'
g++ -Wall -O3 -fPIC -I/usr/include/octave -I.. -c svm_model_matlab.c
svm_model_matlab.c:5:17: fatal error: mex.h: そのようなファイルやディレクトリは ありません
 #include "mex.h"
                 ^
compilation terminated.
Makefile:47: recipe for target 'svm_model_matlab.o' failed
make[1]: *** [svm_model_matlab.o] Error 1
make[1]: Leaving directory '/home/sakae/libsvm-3.12/matlab'
Makefile:28: recipe for target 'octave' failed
make: *** [octave] Error 2

見事にoctave用の開発環境がが入っていませんねぇ。これだから、Linuxってやつは。。。。

で、環境は、liboctave-devを入れると整えられるそうなんだけど、やろうとしたら、 なんだかんだ、92Mもdiskを消費しますですってさ。たかがヘッダーファイル一つに、 これだけのリソースを割くのは、エコ精神に反するな。

こういう時は、OSをFreeBSDにスイッチしてportsの中を探してみます。科学に分類されて 置いてありました。

Our goal is to help users from other fields to easily use SVM as a tool. LIBSVM
provides a simple interface where users can easily link it with their own
programs. Main features of LIBSVM include

  * Different SVM formulations
  * Efficient multi-class classification
  * Cross validation for model selection
  * Probability estimates
  * Weighted SVM for unbalanced data
  * Both C++ and Java sources
  * GUI demonstrating SVM classification and regression
  * Python, R (also Splus), MATLAB, Perl, Ruby, Weka, Common LISP and LabVIEW
    interfaces. C# .NET code is available.
    It's also included in some learning environments: YALE and PCP.
  * Automatic model selection which can generate contour of cross valiation
    accuracy.

WWW: http://www.csie.ntu.edu.tw/~cjlin/libsvm/

そしてインストール。

Installing libsvm-3.21...
Some useful tools are installed to /usr/local/share/libsvm.
Most of them are written in Python, please install
lang/python before trying them.

===>   NOTICE:

The libsvm port currently does not have a maintainer. As a result, it is
more likely to have unresolved issues, not be up-to-date, or even be removed in
the future. To volunteer to maintain this port, please create an issue at:

あらら、どこも人手不足で、メンテナー募集の広告が出てきた。本家は台湾の大学か。 大陸に人が沢山居るんだから、誰か助けてあげて。同胞が作ってくれた宝を守るのは 義務ですよ。

FAQが有ったので、つらつらと見ていたら、こんなのが。libsvmに与えるデータの形は ちょっと特殊だからなあ。

Q: How to convert other data formats to LIBSVM format?

It depends on your data format. A simple way is to use libsvmwrite in the
libsvm matlab/octave interface. Take a CSV (comma-separated values) file in UCI
machine learning repository as an example. We download SPECTF.train. Labels are
in the first column. The following steps produce a file in the libsvm format.

matlab> SPECTF = csvread('SPECTF.train'); % read a csv file
matlab> labels = SPECTF(:, 1); % labels from the 1st column
matlab> features = SPECTF(:, 2:end);
matlab> features_sparse = sparse(features); % features must be in a sparse matri
matlab> libsvmwrite('SPECTFlibsvm.train', labels, features_sparse);

The tranformed data are stored in SPECTFlibsvm.train.

ここに出てる、sparseってどんな働きをするのだろう? その前に使うOSをoctaveが 入っているOpenBSDに変更しておく。

>> a=[4 5 3 2];
>> b=sparse(a)
b =

Compressed Column Sparse (rows = 1, cols = 4, nnz = 4 [100%])

  (1, 1) ->  4
  (1, 2) ->  5
  (1, 3) ->  3
  (1, 4) ->  2

早速やってみる。octaveを起動して、その上で make.mを走らせてあげればおk。

[ob: matlab]$ octave-cli -q
octave:1> make
[ob: matlab]$ ls *.mex
libsvmread.mex*     libsvmwrite.mex*    svmpredict.mex*     svmtrain.mex*

svmフォーマットのデータの読み書き、予測、トレーニング用の拡張が出来上がった。 後は、READMEに従って実行してみた。ちゃんと動いたので良しとしよう。

最後にこのmexファイルをちゃんとした場所に移しておこう。普通は、addpathを使って 登録するらしいけど、これにヒントを得て

>> path
Octave's search path contains the following directories:

.
/usr/local/lib/octave/4.0.2/site/oct/x86_64-unknown-openbsd6.0
/usr/local/lib/octave/site/oct/api-v50+/x86_64-unknown-openbsd6.0
/usr/local/lib/octave/site/oct/x86_64-unknown-openbsd6.0
/usr/local/share/octave/4.0.2/site/m
/usr/local/share/octave/site/api-v50+/m
/usr/local/share/octave/site/m
/usr/local/share/octave/site/m/startup
/usr/local/lib/octave/4.0.2/oct/x86_64-unknown-openbsd6.0
 :

とか出て来るので、

[ob: matlab]$ sudo mv *.mex /usr/local/lib/octave/site/oct/x86_64-unknown-openbsd6.0/

しておいた。