jubatusの分類器：後編(非線形分類器、パラメータごとの可視化)

この記事は[Jubatus Advent Calendar](http://qiita.com/advent-calendar/2016/jubatus) 20日目の記事です．

本日は、[昨日の記事](http://qiita.com/imaimai1125/items/2a126cf04e64432f1bfd)の続きでJubatus分類器の中身について解説します！


# 非線形分類器 : k近傍法
jubaclassifierでは、非線形分類器としてk近傍法を用意しています。
k近傍法は、新しくデータが入ってきたときに、下記の要領で自分のクラスを決定します

1. データの近傍に存在するデータをk個探索する
2. 得られたk個のデータについてラベルごとにスコアを求める
3. スコアの大きいほうが自分のクラスとなる

図示すると、次のようなイメージです 

![knn.png](https://qiita-image-store.s3.amazonaws.com/0/151502/d44ec73f-4ba7-a64a-5d3c-bcc2f6e2dc5e.png)


図でも書いてあるとおり、**距離の測り方(distance)**を、いろいろいじることができます。
Jubatusには

- **厳密な近傍計算**を行う`euclidean`, `cosine`
- **ハッシュ**に基づき近傍計算を行う`euclid_lsh`, `lsh`, `minhash`を用意をしているので、順に説明していきます

## 厳密な近傍計算

- 設定 : i番目の点とj番目の点の距離$d(x_i,x_j)$を求める

### euclidian
単純なユークリッド距離で差を計算します

```math
\begin{eqnarray}
d(\mathbf{x_i},\mathbf{x_j})=||\mathbf{x_i}-\mathbf{x_j}||
\end{eqnarray}
```
### cosine
原点からの角度(厳密には$\cos{\theta}$ )によって類似度を出します。ベクトルの大きさは関係ありません。

```math
\begin{eqnarray}
d(\mathbf{x_i},\mathbf{x_j})=\frac{\mathbf{x_i}\cdot\mathbf{x_j}}{|\mathbf{x_i}||\mathbf{x_j}|}
\end{eqnarray}
```

二つを図示すると、こんな感じ。
![euclidian_cosine.png](https://qiita-image-store.s3.amazonaws.com/0/151502/8444aa19-fb9c-52b6-7fc7-381b270021ba.png)

## ハッシュに基づく近傍計算
Jubatusでは、高速にアルゴリズムを動作させるため、ハッシュ関数を用いて計算を行うことがあります。

- i番目の点とj番目の点の距離$d(x_i,x_j)$を求める

### LSH
Local Sensitivity Hash の略です。
ランダムベクトルを用意してハッシュを作り、二つのベクトルの近似的な**コサイン類似度**を求める方法です。

![hash.png](https://qiita-image-store.s3.amazonaws.com/0/151502/353df211-a6c4-d469-467b-92610a3f3674.png)

ハミング距離によって近似した$\theta$をつかい、コサイン類似度とします。
なお、k近傍法でほしいのは**コサイン類似度の順序**です。$1-\frac{\theta}{\pi}$ も $\cos\theta$ もどちらも($0<\theta<\pi$)において**単調減少関数**なので、Jubatusでは計算量削減の観点から、わざわざ$\cos\theta$を計算することなく, $1-\frac{\theta}{\pi}$ の値をコサイン類似度として採用しています。

### Euclid LSH
LSHで近似的に求めた$\theta$を使って、二つのベクトルの距離を求める方法です。
![ELSH.png](https://qiita-image-store.s3.amazonaws.com/0/151502/8c738be6-10e6-d973-accb-df9799284748.png)

### MinHash
Min-Wise Independent Permutationの略称で、類似度指標である**Jaccard係数**を、ハッシュを用いて近似的に求める手法です。
(※後日加筆するかもです。。。)


 
# パラメータごとの挙動の違い
ここまで、線形分類器と非線形分類器のアルゴリズムを紹介してきました。
お分かりだと思いますが、それぞれの分類器では、自分たちで設定できるパラメータが存在します。
下の表は、`jubaclassifier`において設定できるパラメータたちです。

![parameters.png](https://qiita-image-store.s3.amazonaws.com/0/151502/7930b9a3-7ec1-3c6f-a9a0-13c4adb24120.png)


先日、@shiodatさんがアルゴリズムごとの挙動の違いについては説明してくれたので、私は**パラメータによるの挙動の違い**について少し見てみようかなと思います

## regularization_weight
今回は、**AROW**を例にとり、`regularization_weight`による違いを見てみたいと思います。
特に、**iteration=40 近辺のバタつき**に注目してみてください。
regularization_weightが大きいとばたつきが大きくなることがわかると思います。

- `reguralization_weight`=0.1 
![AROW_0.1.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/c0cc753f-f66e-8e69-ae3b-1d6d97816db2.gif)
- `reguralization_weight`=10.0  
![AROW_10.0.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/edbbe056-21e3-f6a0-a280-1355fdc1f491.gif)

 
## nearest_neighbor_num
(distance : euclidean, local_sensitivity = 1.0)

近傍点の数が少ないと、**1点の周りに特化した境界面**になります。そのため、きちんとすべてを分類できますが、**過学習**を起こしがちになります。
近傍点の数を増やしていくと、周りを見ながら近傍点を設定するので、境界面はそこまでぐにゃっと曲がることはありません。その反面で、少し分類しきれない点も出てきます

- `nearest_neighbor_num`=1
![euclidean_num1_S1.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/a8437bc1-35eb-19c7-4462-ca99ab123763.gif)
- `nearest_neighbor_num`=15 
![euclidean_num15_S1.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/512f1284-e059-29e7-74a8-4c0124f83258.gif)



## local_sensitivity
(distance : euclidean, nearest_neighbor_num = 5)

今度は、近傍点を固定してlocal_sensitivityを変えつつ、挙動を見てみます。
`local_sensitivity` = 10を見ればわかるように、近傍点を5個選んでいるのにもかかわらず、**直近の点しかほとんど見ていない**ような挙動になっていることがわかると思います。


- `local_sensitivity` = 1.0
![euclidean_num5_S1.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/d5c22731-7707-9244-0980-6ec50a0b17ef.gif)
- `local_sensitivity` = 10.0
![euclidean_num5_S10.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/962d1e18-82af-0691-bd12-ebf0dd0bddc5.gif)


## hash_num
(nearest_neighbor_num = 5, local_sensitivity = 1)

ハッシュの個数を指定して、挙動を見てみます。
`hash_num`が少ないと学習速度が速い分、近似精度が悪くなるというトレードオフがあります。

今回はminhashを例にとり見てみます。
やはり極端に少ない`hash_num`では全然分類できておらず、
`hash_num`を増やすと精度が上がることが見て取れると思います。

- `hash_num` = 4  
![NNminhash_num5_S1_n4.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/4464abb5-1557-b634-8547-767bbaff9c47.gif)
- `hash_num` = 1024  
![NNminhash_num5_S1_n1024.gif](https://qiita-image-store.s3.amazonaws.com/0/151502/9f155180-0b65-6831-2d50-9921c145e009.gif)
 
## hash_numと精度
これだけでは、どのくらい精度に差があるかわからないかもしれませんので、
`hash_num`による精度の変化を調べてみました。

- 使うデータセット : **digit**
下図のような画像データから、0-9を当てるタスクです。
![digit.png](https://qiita-image-store.s3.amazonaws.com/0/151502/aeceeadc-27cb-cd14-3bd6-56a1e62024b0.png)

- 評価 : Cross Validationを4回行い、**正解率の平均**
- 固定するパラメータ : `nearest_neighbor_num`=5, `local_sensitivity`=1.0
- `hash_num`={4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}

結果はこちら
![result1.png](https://qiita-image-store.s3.amazonaws.com/0/151502/6ff58bec-ec4a-bd58-0368-4cb5c6161377.png)


縦軸を拡大したver.がこちら
![result2.png](https://qiita-image-store.s3.amazonaws.com/0/151502/815f344b-1d7a-932c-3da9-1189dfbe560d.png)


これをみると、**`hash_num`=256**くらいで厳密計算の`euclidean`や`cosine`と遜色ない精度を出せていることがわかります。
(これはデータセットによるところが大きいので、あくまで参考程度にしてください）





## 最適なパラメータを見つけるには
このように、パラメータによって挙動、精度が大きく変わるのですが、
「じゃあどのアルゴリズムとどのパラメータを使えばいいの？」
といわれると、なかなか難しいです。こういうのは、人の手で行うより機械にパラメータチューニングをして欲しいものなのですが、
**Jubakit**と**scikit-learn**を組み合わせれば、それができます！

@__john_smith__さんの記事で詳しく説明していただいていますが、
`jubakit`と`hyperopt`を使うことにより、
学習用のパラメータ最適化を行えるようになっています。

(詳細は、[advent calendar 18日目](http://dev.classmethod.jp/machine-learning/use-jubakit/)をご覧ください)

このように、機械的にパラメータが見つけられるようになると、コストもグンと下がりますね！

# まとめ
長くなりましたが、2日に渡って、分類器の中身を中心に説明してきました。
たまには中身のアルゴリズムに触れることで、機械学習に関する理解が深まり新たなアイディアが生まれるかもしれません。

さて、明日は誰が書いてくれるのでしょうか、楽しみです。