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


> 僕はSVMが大好きです．シュパッてきれいに分類できている姿を見ると，かっこよくてドキドキします．

> 今回は，SVMの性能を最大限に引き出すために知っておくと役立つことを書いていこうと思います．ちょっとチューニングを行うだけで，10%〜20%精度が向上するなんてことはよくあります．

> なお，本記事は__使いこなし方__にフォーカスしているので，理論的なことを知りたい方は別途確認して下さい．

# 特徴量の作成

> まずは，適切な特徴量を作成するにあたって注意すべきことを2つ紹介します．

## 1. スケーリング

![svm1-1.png](https://qiita-image-store.s3.amazonaws.com/0/7113/ab3a3ef6-825e-6eda-143e-2acf2b0322ca.png "svm1-1.png")

> スケーリングとは，特徴量のとりうる値の範囲をあらかじめ調整してあげることです．

### なぜスケーリングするの？

> 理由は2つあります．

> - 大きい値の範囲をとる特徴量に引きずられないようにします．[0,10]での1と2の違いは1だけですが，[0,10000]での1の100の違いよりもずっと重要です．すなわち，これらを対等に比較するために，それぞれの特徴量を同じ範囲にスケールしてあげる必要があります．

> - カーネル関数は特徴ベクトルの内積を用いて計算するので，スケーリングを行わずに計算すると，大きい値×小さい値となり，情報落ちする可能性があります．

### libsvmでスケーリング

> libsvm は，メジャーなオープンソースSVMライブラリです．libsvm では，以下のコマンド1発でスケーリングしてくれます．

```
svm-scale -l 0 -u 1 -s scale_data feature1 > feature1.scale
```

> l, u にそれぞれとりうる範囲の最大値，最小値を指定します．また，スケール尺度を scale_data として保存できます．
> 訓練データとテストデータといったように，特徴量が複数のファイルに分かれている場合は，全て同じ尺度でスケーリングを行わなければなりません．以下のようにして，保存した scale_data を用いてスケーリングを行うことができます．

```
svm-scale -r scale_data feature2 > feature2.scale
```

> 「尺度を保存しなくても全部 [0,1] でスケーリングすれば大丈夫っしょ〜(^∀^)」といって以下のようにしてはいけません．(理由は分かりますね？)

```
svm-scale -l 0 -u 1 feature1 > feature1.scale
svm-scale -l 0 -u 1 feature2 > feature2.scale
```

## 2. カテゴリ特徴量

![svm2-2.png](https://qiita-image-store.s3.amazonaws.com/0/7113/f615601e-4eec-3d87-6d80-7f7127050533.png "svm2-2.png")

> 1つの特徴量で3つ以上の値をとるものに関しては，複数の2値の特徴量に分解します．(とる値の数だけ特徴量が生成されます．) 特徴量の数が多くなりすぎない限りは，このようにすることで分類がうまくいきやすくなります．上の図でも，左図はこのままでは線形分離できませんが，右図はできますよね？

```
[-1, 0, 1]                        # 3つ以上の値をとる場合は
[(1, 0, 0), (0, 1, 0), (0, 0, 1)] # こうしてあげる
```

# モデル選択

> 次に，カーネル関数とパラメータをどう選択すればよいのかを紹介します．

## 3. カーネル関数

> カーネル関数は，線形カーネル，多項式カーネル，RBFカーネル，シグモイドカーネル… と数多くありますが，基本的には__RBFカーネル一択__だと思って大丈夫です．
> ただ，もちろん他のカーネルを使うともっとうまくいくこともあります．例えば，データ数と比べて特徴量の数が圧倒的に多いような場合は，線形カーネルがうまくいきやすいです．

## 4. パラメータ

> カーネル関数はRBFカーネルに決まりました．次に，以下の2つのパラメータをチューニングしていきます．(利用するカーネル関数の種類によって，チューニングするパラメータの個数は変わります)

> - γ : RBFカーネルで用いるパラメータ．
> - C : コストを表すパラメータ．大きくなるほど誤りの際のペナルティが大きくなる(すなわち誤りに厳しくなる)．

> C に関しては，大きくするほど過学習をおこしやすくなるので要注意です．データ数が大きくなると外れ値というものは一定数存在してしまうものなので，全てをきれいに分類しようと思ってはいけません．汎用性を損なわない程度のコストにとどめましょう．

> 以下の図の左がコスト小の時，右がコスト大の時のイメージです．これらのデータに関しては右図では100%の精度で分類できていますが，右の方が汎用性のある分類であるといいきれるでしょうか？

![svm3-3.png](https://qiita-image-store.s3.amazonaws.com/0/7113/a69ee22b-f18b-3a25-ea8c-c722d70d17dd.png "svm3-3.png")

### どうやってパラメータを決めるの？

> __グリッド探索__という方法を用いて最適な γ, C の組み合わせを求めます．以下のように γ, C の2次元空間をグリッドに分割し，その交点で最適だと思われるものを求めます．初めは浅く探索して最適箇所の予想を立て，次にその部分を深く探索する，といった方法がよく取られます．

![svm4-2.png](https://qiita-image-store.s3.amazonaws.com/0/7113/0a80bbbb-a889-45b6-a6cd-60113bbcaef8.png "svm4-2.png")

> ただ研究では，チューニングせずにあえてデフォルトパラメータを使う場合も多くあります．「僕たちの手法ではチューニングなんかしなくてもこんだけの精度出るんだよ！」ということを強調するためです．

# 評価

> 次は，評価方法や，評価の際に注意すべき点について紹介していきます．

## 5. クロスバリデーション

> 性能評価の際に，データ数が少なくて訓練データとテストデータを両方用意するのが難しいことは多々あります．そのような時には，クロスバリデーション(交差検定)を用います．

> クロスバリデーションでは，データを複数に分割し，その中の1つをテストデータ，残りを訓練データとして評価を行います．
> 例えば，データをA,B,Cに分けた場合，以下の3つを行い，その平均をSVMの分類精度とします．

> - Aをテストデータ，残りのB,Cを訓練データとして分類精度を求める．
> - Bをテストデータ，残りのA,Cを訓練データとして分類精度を求める．
> - Cをテストデータ，残りのA,Bを訓練データとして分類精度を求める．

> データをk個に分割した場合を k-fold クロスバリデーションと呼びます．(上の例は 3-fold)

### libsvmでクロスバリデーション

> libsvmでは，-v オプションで分割数(fold)を指定して，クロスバリデーションをおこなってくれます．

```
svm-train -v 10 feature.scale # feature.scale を用いて 10-fold クロスバリデーション
```

## 6. 不均衡データ問題

> 実世界の問題は，99%と1%といったように，クラスのデータ数に大きく差が開いている場合がよくあります．例えば重病患者とそうでない人を分類したいなどの場合は，クラスのデータ数にかなりの偏りが生じ，予測精度が大きく低下してしまいます．

> +1: 990件 と -1: 10件 のデータ場合，後者の10件をいかに検出し，分類できるのかが重要になってきますが，そのままSVMを構築してしまうと，多くの場合，全て +1 に分類されてしまうことになります．
> 評価の際，これは結果だけを見ると99%の精度(accuracy)で分類できていることに注意して下さい．この数値にだまされてはいけません．肝心の10件を分類することができていないので，これでは意味がありません．

> 大きく分けて，以下の2つの対策方法があります．

### パラメータC(コスト)を大きくする

> 4.パラメータのところで紹介したパラメータCを大きくすることで，誤りに厳しくすることができます．
>
> libsvmでは，全体のコストを-cオプションで設定できる他に，各クラスごとのパラメータを-wiオプション(iはクラス)で設定することができます．上記の +1:990件 と -1:10件 の場合，-1のクラスのコストをより大きくしたいので，例えば以下のように設定することで -1クラスに対して誤りを厳しくすることができます．

```
svm-train -c 10 -w1 1 -w-1 5 feature.scale
```

### データ数をそろえる

> データ数が揃っていないなら揃えましょう．以下に3つの揃え方をあげます．

__大きい方のクラスを小さい方のクラスにそろえる(アンダーサンプリング)__

> 大きい方のクラスのデータ数を減らします．この際，ランダムにサンプリングしてしまうと，まんべんなくデータを残すことができない可能性があります．なので，クラス内のデータをクラスタリングし，それぞれから一定数サンプリングする，といった方法がよくとられます．

__小さい方のクラスを大きい方のクラスにそろえる(オーバーサンプリング)__

> 上記とは逆で，小さい方のクラスのデータ数を増やします．単にデータを__複製__すると過学習をおこしやすいので，k-NNなどを用いて人工的に __捏造__ する方法がよくとられます．

__アンダーサンプリングとオーバーサンプリングを組み合わせる(ハイブリッド)__

> これらの両方を用いて揃えます．SMOTEというアルゴリズムが有名なので，興味のある人は調べてみて下さい．だたSMOTEは少し古く，最近はよりよい手法が提案されています．

# その他

> 最後に，SVMのさらなる使い方について紹介しよう思います．

## 7.多クラス分類

> SVMで多クラス分類を行いたい時は，以下の2つの方法があります

### 1対他分類法

![svm7-2.png](https://qiita-image-store.s3.amazonaws.com/0/7113/b67aaa60-2e7c-3995-c314-425dfda9e3b6.png "svm7-2.png")

> 1対他分類法は，あるクラス と その他のクラス全部 でSVMを構築していき，それらをもとに多クラス分類を行う方法です．nクラス分類ではn個のSVMが必要となります．

### 1対1分類法

![svm6-2.png](https://qiita-image-store.s3.amazonaws.com/0/7113/1467a062-2fa5-fd4d-d70d-84cdd9c58fbf.png "svm6-2.png")

> 1対1分類法は，2クラスをペアを作っていき，それらを分類するSVMを構築します．nクラスに対して nC2 個のSVMが必要となり，クラス数が多い場合は計算コストがかかります．libsvm の多クラス分類は，1対1分類法が実装されています．

## 8. アンサンブル学習

> アンサンブル学習は，1つの分類に対して独立した複数の分類器を構築し，それらをうまく融合させることで汎化性を高める学習法です．

> 例えば，3,000件の訓練データがある場合，それらのデータをもとに1つのSVMを構築するのが一般的な方法ですが，アンサンブル学習では，1,000件ずつ3つに訓練データを分割して，それぞれでSVMを構築します．そしてあるテストデータに対して，3つのSVMでの分類結果がそれぞれ +1，-1，-1となった場合，多数決で -1 にする，といったようなことをします．

>もちろんこれは具体的な1つの例にすぎず，詳しくは紹介しませんが融合方法は数多くあります．決定木を用いてアンサンブル学習を行う__ランダムフォレスト__は有名でよく使われています．