jubatus-exampleを全部動かしてみる(後編)

この記事は[Jubatus Advent Calendar](http://qiita.com/advent-calendar/2016/jubatus) 4日目の記事です。遅刻してごめんなさい。

引き続きjubatus exampleを実行していきます。
後編ではやや準備に手間取るものをやっていきます。

# 後編で実行するexample

- network intrusion detection(anomaly)
- mnist(classifier)
- twitter streaming lang(classifier)
- twitter streaming location (classifier)
- winequality (regression)
- malware classification (classifier)

# network intrusion detection
network intrusion detectionはjubatusの異常検知jubaanomalyを用いた簡単なサンプルで、KDDCup 1999 のデータセットを利用して、ネットワーク侵入検出を行います。

## とりあえず動かしてみる

### データセットの準備
KDDのデータセットをダウンロードし、リネームします。

```
$ wget http://kdd.ics.uci.edu/databases/kddcup99/kddcup.data_10_percent.gz
$ gunzip kddcup.data_10_percent.gz
$ mv kddcup.data_10_percent kddcup.data_10_percent.txt
```

### jubatusサーバの起動
次にjubatusサーバを起動します。

```
$ jubaanomaly -f config.json
```

### クライアントの実行
クライアントプログラムを実行します。
python ディレクトリに移動し、anomaly.pyを実行します。

```
$ cd python
$ python anomaly.py
id_with_score{id: 194, score: 1.0000441074371338} normal.
id_with_score{id: 494, score: 1.4595649242401123} normal.
id_with_score{id: 1127, score: 1.0642377138137817} normal.
...
id_with_score{id: 43209, score: 0.9213032126426697} smurf.
id_with_score{id: 43214, score: 0.975369930267334} smurf.
```

無事に動作すれば上記のような結果が出力されます。
出力されているのは登録したデータのIDとそのanomalyスコア、そして元々ついていたラベルです。
anomalyスコアが高ければ高いほど通常のデータから離れたデータ、すなわち異常な値だとjubatusは判断しています。

# mnist
続いてのexampleはmnistです。有名な手書き文字認識のデータセットをjubatusの分類器で解くexampleになります。
jubatusは1.0.0で画像からの特徴抽出を行うプラグインが標準で提供されるようになります。このexampleではその機能を使います。画像特徴抽出プラグインを使うためには別途OpenCVがインストールされている必要があります。

## とりあえず動かしてみる

### データの準備
データはあらかじめjubatus-exampleリポジトリにあるので解凍するだけです。

```
$ tar xvfz mnist_jpg_1000.tar.gz
```

### jubatusサーバの起動

```
$ jubaclassifier -f config.json
```

### クライアントの実行
python ディレクトリに移動し、スクリプトを実行します。

```
$ python jubamnist.py
num of train data : 900
num of test  data : 100
train
..............
test
ooooxxooooooooxoooooooooooooooooooooooooooooooooooooooooooooooooxooooooooooooooxooxooooooooooxoooooo

OK : 93, NG :7, Accuracy : 0.930000
```

無事に動作すると上記のような出力がなされます。
学習に用いたデータの数、テストに用いたデータの数、そして分析結果が表示されます。
jubatusの画像特徴抽出プラグインについては[公式ドキュメント](http://jubat.us/ja/fv_convert.html#id15)をご覧ください。

# twitter streaming analysis
続いてtwitter streaming analysisです。
これは各言語版のwikipedaのダンプデータを学習させて、tweetの言語を分類する問題です。

## とりあえず動かしてみる

### データの準備

まずは学習用のwikipediaのダンプデータをダウンロードしてきます。
データ量が大きいので注意してください。

```
日本語
$ wget http://dumps.wikimedia.org/jawiki/latest/jawiki-latest-abstract.xml
英語
$ wget http://dumps.wikimedia.org/enwiki/latest/enwiki-latest-abstract.xml
ドイツ語
$ wget http://dumps.wikimedia.org/dewiki/latest/dewiki-latest-abstract.xml
...
```

### Twitter APIキーの取得
TwitterのAPIを取得するにはアクセスキーが必要となります。
Twitter developersの[Application Management](https://apps.twitter.com/)から新しいアプリを作成し、

- Consumer Key
- Consumer Secret
- Access Token
- Access Token Secret

の4つを取得します。
アプリの名前や、説明、URLは適当なもので構いません。

次にTwitter APIキーをjubatus-example/twitter_streaming_lang/python/の下にある、classify-twitter-stream.pyの26行目から29行目に書き込みます。

```python:classify-twitter-stream.py
...
consumer_key = 'XXXXXXXXXXXXXXXXXXXX'  # 自分のkeyを記入する
consumer_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
access_key = 'XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
access_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
...
```

また、pythonクライアントの実行にtweepyが必要になるのでインストールしておきます。

```
$ pip install tweepy
```

### サーバの起動
jubatus サーバを起動します。

```
$ jubaclassifier -c 12 -f twitter_streaming_lang.json
```

### クライアントの実行
今回のexampleは複数の学習と分析を同時に実行することが出来ます。
wikipediaのダンプデータはかなり量が多いので、学習の終了を待っていると時間がかかってしまいます。なので、日本語、英語、ドイツ語をjubatusに学習させつつ、同時にtwitterストリームの分析も行ってしまいます。

#### 学習を実行する

```
$ python train-wiki-abstract.py ja jawiki-latest-abstract.xml &
$ python train-wiki-abstract.py en enwiki-latest-abstract.xml &
$ python train-wiki-abstract.py de dewiki-latest-abstract.xml &
```
第一引数が学習させる言語のラベル、第二引数が学習させるデータです。これで3つの学習が同時に走ることになります。

#### 分析させる

分析クライアントではパブリックタイムラインに流れるtweetの言語を分類します。

```
$ python classify-twitter-stream.py ja 
en RT @Bob_Janke: what happens if a ghost hunter dies in your house?
ja 熱くなりすぎると判断が鈍る。頭はいつもクールじゃなきゃ　森岡隆三
en RT @E_Geerlings: #ProBowlVote 

@RAIDERS @52Mack_ is the 2nd player in franchise history to record a sack in 7 straight games. Longe… 

```

第一引数に指定した言語に分類されたtweetが赤字で表示されます。


# twitter streaming location

続いては同じくtwitterの分類を行うexampleです。ここでは位置情報を学習し、ユーザの入力した文章がどの場所で発信されたものかを推定します。

## とりあえず動かしてみる

### 準備
twitter streaming lang同様、Twitter API keyを書き込みます。
twitter_streaming_location/python/train.py の23行目から26行目を書き換えます。

```python:train.py
...
# Fill in your keys here:                                                                                             
consumer_key = 'XXXXXXXXXXXXXXXXXXXX' # それぞれ自分のkeyを記入する
consumer_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
access_key = 'XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
access_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
```

### jubatusサーバの起動

```
$ jubaclassifier -f twitter_streaming_location.json -t 0
```

`-t` オプションはjubatusサーバのRPCのタイムアウト時間を設定するパラメータです。
0にするとタイムアウトしなくなります。

### クライアントの実行
まずは学習スクリプトを実行します。

```
$ python train.py
Kyusyu I'm at 福工大前駅 in 福岡市, 福岡県 https://t.co/bpgEzTBwq8
Tokyo おはようございます！週明け暖かくなるような予報でしたね！
今日のモーニングコーヒーホットがブラジル、アイスがコスタリカです☕️
今週もコーヒーブレイクを挟みながらいきましょう♪♪ https://t.co/dGSYHgj10K
...
```

実行すると上記のように位置情報付きのツイートから都道府県をラベルとして取り出し、学習していきます。
なお、train.pyの中をよく読んでもらえるとわかりますが、"Tokyo"、"Hokkaido"、"Kyushu"というラベルは座標情報を頑張って変換しています。

```python:train.py
def train_tweets():
    tokyo    = LocationFence("Tokyo",    138.946381, 35.523285, 139.953232, 35.906849)
    hokkaido = LocationFence("Hokkaido", 139.546509, 41.393294, 145.742798, 45.729191)
    kyusyu   = LocationFence("Kyusyu",   129.538879, 31.147006, 131.856995, 33.934245)
```

適当に学習させたら次は分析スクリプトを実行します。

```
$ python classify.py "紅葉がきれい"
Estimated Location for 紅葉がきれい:
  Tokyo (0.008452329784631729)
  Kyusyu (0.0)
  Hokkaido (-0.008452329784631729)
```

第一引数に位置情報を分析したい文章を与えると、それがどこで発言されたものかを推定します。
今の時期、紅葉という発言は東京っぽいという結果になりました。


さて、2日目の記事で、pythonは全てのexampleで実装されているということを書きましたが、途中で誤りだと気づきました。
ここから扱うwinequalityとmalware classificationとはそれぞれruby, c++クライアントしか実装されていません。申し訳ありませんが、ここだけrubyとc++で実行していきます。

# winequality
winequalityは[こちら](https://archive.ics.uci.edu/ml/datasets/Wine+Quality)で公開されているデータセットを用いた回帰分析のexampleです。ワインの属性情報から品質を推定します。

## とりあえず動かしてみる

### jubatusサーバの起動
jubatusサーバを起動します。中編のrentのexampleであったようにconfigファイルを書き換える必要があります。

```
$ sed s/PA/PA1/ config.json > config_PA1.json
$ jubaregression -f config_PA1.json
```

### クライアントの実行
次にruby ディレクトリに移動し、クライアントプログラムを実行していきます。
ちなみに自分の手元のrubyのバージョンは1.9.3です。

```
$ cd ruby
$ ruby train.rb
train by ../winequality-white.csv
```
出力は学習に使ったデータファイルです。

学習が終わったら分析をさせていきます。

```
$ ruby test.rb
test by ../winequality-white.csv
average 1.7489207577874535 error
```

分析をさせてみた誤差の平均が出力されます。

# malware classification 

malware classificationはjubaclassifierを使ったマルウェア分類のexampleです。なお、残念ながら現在データの配布が行われていないため、一部発掘できたデータのみでの動作になります。

## とりあえず動かしてみる
今回はjubatus-example/malware_classificationのディレクトリにいる状態を想定して実行していきます。

### データの準備
まずはデータセットをダウンロードして来て解凍します。

```
$ wget http://download.jubat.us/malheur.zip
$ unzip malheur.zip
$ tar xvfz 20090802_mist.tar.gz 20090803_mist.tar.gz
```

### プラグインのインストール

次に、プラグインをインストールします。
wafを動かすときはまたpython2系を使ってください。

```
$ cd cpp
$ cd word_ngram_plugin
$ ./waf configure
$ ./waf build
$ cp build/libword_ngram.so /opt/jubatus/lib/jubatus/plugin
$ cd ../
```

### クライアントのビルド

次にクライアントプログラムをビルドします。

```
$ ./waf configure
$ ./waf build
```

### jubatusサーバの起動

```
$ jubaclassifier -f ../config.json -t 0
```

### クライアントの実行

#### 学習

```
$ ./build/ml_train ../20090802.csv ../20090802/
Loading.. ../20090802.csv
Target size: 3750
```

第一引数に学習させるデータのcsv, 第二引数に対応するMIST形式のディレクトリを指定します。
なお、RPC timeout errorが頻発する場合は、ml_train.cpp, ml_analysis.cppの`kTimeout`という変数を調整してください。0.0にすればTimeoutはなくなります。

#### 分析

今度は分析プログラムを実行します。

```
$ ./build/ml_classify ../20090803.csv ../20090803 
...
{"label":"Allaple","input":"6e6e9c451360bec1fe98387132ae8fcb1634982d","estimate":[{"Swisyn":0.131581574678},{"Swisyn":0.131581574678},{"Swisyn":0.131581574678},{"Swisyn":0.131581574678},{"Swisyn":0.131581574678},{"Swisyn":0.131581574678},{"Swisyn"
...
```

正解ラベルが"label"に出力され、分類結果が"estimate"に順に格納されて表示されます。
が、何か様子がおかしいです。プログラムに実装の誤りがあったので修正して実行しなおします。ml_classify.cppの30行目と84行目を書き換えます

```diff:ml_classify.cpp
...
bool estimate_compare(const estimate_result& lhs, const estimate_result& rhs)
{
# 30行目
+  return rhs.score < lhs.score; 
-  return rhs.score < rhs.score;
}

...
for (size_t j = 0; j < result.size(); ++j) {
  json o(new json_object());
# 84行目
+ o.add(result[j].label, new json_float(result[j].score));
- o.add(result[i].label, new json_float(result[i].score));

```

再度ビルドして実行します

```
$ ./build/ml_classify ../20090803.csv ../20090803 
...
{"label":"Texel","input":"d224b380bd9df216fbe0c11a9ac160268bdc607e","estimate":[{"Texel":0.630960345268},{"NothingFound":0.482750713825},{"Droco":0.348297894001},{"Sality":0.281127333641},
...
```

今度は正しく結果が表示されます。"estimate"には分類スコアが高い順に、全てのラベルとスコアの組が出力されています。


#おわりに
大幅に遅刻しましたが、以上でjubatus-exampleを全部動かしてみるを終わります。
少しでも皆さまがjubatusに興味を持っていただけたら幸いです。
また、記事の執筆中に見つけた不具合を直すプルリクエストを試しに書いてみた、みたいな人も出てきてくれると嬉しいです。

アルゴリズムの詳細など結構解説を省いてしまった部分も多いので、後の人たちがフォローしてくれると期待しています。
次は[@k_oi](http://qiita.com/k_oi)さんがembedded-jubatus-python(jubatusサーバを立てずにpythonから直接jubatusの機械学習機能を使う機能)の性能について書いてくれます。ご期待ください。