League of Legendsの試合データから勝敗を判別する機械学習③

#今回やること
　[前回](https://qiita.com/hyo_07/items/fb501e985f4dec6d2810)は、APIを用いて試合の戦績データを収集するプログラムを作成しました。今回は、それを使って実際にデータを収集し、保存。そして、教師ありの機械学習にかけてみることをします。

　また、機械学習に関しては先日、「Python 機械学習プログラミング」という、impress社から発売されている本を購入したため、こちらを参考にほぼコードのコピペのような形ではありますが、実践してみました。[リンク](https://book.impress.co.jp/books/1117101099)

#データの収集と保存
　まず、前回のコードはあくまで収集したデータを出力しているだけだったので、それらのデータをCSVで保存をするために、最後の方に手を加えました。また、それらにはPythonのモジュールの一つであるPandasを用いました。

```lol.py
df1 = pd.DataFrame(data_list, columns=decision_list)
df2 = pd.DataFrame(result_lsit)

df1.to_csv("dataset.csv",sep=",", mode='a',header=False)
df2.to_csv("rabel.csv",sep=",", mode='a',header=False)
```
　前回示したコードに、これらを追加しました。df1は、戦績データをコラムにデータの辞書のキーを、df2は勝敗結果を代入しています。そしてそれらを、dataset.csv と rabel.csv に保存をしました。本来はパス指定をしなければなりませんが、今回は同じディレクトリにファイルを作成したため、このようにしました。
　mode = a は、一度に20試合分までしか取得できないため、何回もこのプログラムを実行します。そのため、先ほどのファイルに追記していきたいため、追記のモードにする引数を設定しています。
　また、header = False としていますが、これは追記をする際にheaderを入れてしまうと20試合ごとに間にコラムが入ってしまうのを防ぐためです。なので、初回のみはTrueにし、2回目以降はFalseとしました。

実際に集めたデータはgithubに上げました(最近githubの使い方を少し勉強しました)。[dataset.csv](https://github.com/hyo07/lolapi/blob/master/dataset.csv), [rabel.csv](https://github.com/hyo07/lolapi/blob/master/rabel.csv)

#教師あり学習を実践
では、実際に学習にかけてみます。今回は scikit-learn を使い、その中でも強力な学習アルゴリズムの一つであるサ**ポートベクトルマシン**(以下SVM)を用いました。

まず使用するモジュールのインポートをします。

```lol_learning.py
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
```
pandasはCSVファイルを読み込むため、残りの三つはのちほど説明したいと思います。

次はデータセットの読み込みと、それらをトレーニング用データとテスト用データに分けます。

```lol_learning.py
X = pd.read_csv("~/dataset.csv", index_col=0)
y = pd.read_csv("~/rabel.csv", index_col=0)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5, random_state=1)
```
Xに戦績データ、yに勝敗ラベルを入れます。このとき、pandasを用いてデータを読み込むわけですが、今indexの指定をしないと、関係ない列番号が入ってしまうため、index_col=0 とします。

Xとyを設定したら、次にトレーニングとテストのデータに分けます。ここで、モジュールの2個目にある、 train_test_split を用います。
このモジュールは、トレーニング用とテスト用に分割を出来る関数で、test_size でテストデータに設定することができ、ひとまず5：5に割り振ってみました。

次に、データの標準化を行います。ここで三つ目のモジュールを用います。

```lol_learning.py
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)
```
sc.fit で、トレーニングデータから平均と標準偏差を計算し、それらで sc.transform でトレーニングデータとテストデータを標準化しています。この標準化については、行った場合と行わなかった場合で比較をしたいと思います。

続いて、ようやく機械学習にかけます。

```lol_learning.py
svm = SVC(kernel="linear", C=1.0, random_state=1)
svm.fit(X_train_std, y_train)

print("トレーニングデータでの正解率: %.2f" % svm.score(X_train_std, y_train))
print("テストデータでの正解率: %.2f" % svm.score(X_test_std, y_test))
```
　四つ目の最後のモジュールを用います。この sklearn.svm の中に、SVMがあります。SVC() でSVMのインスタンスを生成したあと、svm.fitでモデルの学習を行っています。トレーニングを学習、テストで試験と分けていますので、ここでの引数はtrainを用いています。これで、学習ができ、出力結果がこうなりました。
![aiaia.png](https://qiita-image-store.s3.amazonaws.com/0/278834/4f3b7357-3636-02cf-bc50-4caee85ac5b6.png)
学習が行えてるかの確認のためのトレーニングデータの勝敗結果の予測をした正解率が97%、実際にテストデータで予測してみた正解率が83%となりました。

#標準化しなかったときと、したときの比較
ためしに、先ほどの標準化をするしないでどう違うのかを見てみました。
まず、標準化を行わなかった場合がこちら
![not.png](https://qiita-image-store.s3.amazonaws.com/0/278834/3df552ea-f5b0-85ae-1288-8189668c60fc.png)
(標準化を行っていないため、X_train_std ではなく、X_train になっています)
続いて、先ほどと同じ画像ですが、標準化を行ったものがこちら
![aiaia.png](https://qiita-image-store.s3.amazonaws.com/0/278834/06f9c34d-ee68-2237-3133-f988eac51eb8.png)

こうみると、標準化を行うとトレーニングデータの正解率が11%、テストデータの正解率が7%違っているのが分かります。
また、それぞれのトレーニングデータとテストデータの正解率の差も、10%と14%で違っています。

#トレーニングデータとテストデータの分割の割合
今までは、トレーニングデータとテストデータを5：5にして学習を行いました。今度は、同じ条件で、10%から90%までの割合を見比べてみたいと思います。

```python
for size in np.arange(0.1, 1.0, 0.1):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=size, random_state=1)
```
割り振りの外側にfor文を入れることで、10%から90%までの正解率を出してみました。

| テストデータの割合 | 10% | 20% | 30% | 40% | 50% | 60% | 70% | 80% | 90% |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|  トレーニングデータの正解率 | 89%  | 89%  |  89% | 90%  | 93%  | 91%  | 100%  | 100%  | 100%  |
| テストデータの正解率  | 92%  | 86%  | 82%  | 81%  | 84%  | 82%  | 78%  | 75%  | 78%  |
このような結果になりました。
　トレーニングデータの割合が大きくなるほど、学習の確認のためにトレーニングデータの予測をすると正解率がなぜか上がっていき、最終的に100%になってしまいました。
　一方、テストデータは10%の時だけ値が大きく、その後上がり下がりをしています。これは、テストデータが10%しかないため、試行回数が少なくたまたま正解率が上がったしまったのではないかと思います。勿論テストデータの割合が少ない＝トレーニングをより多くできる。ということになるので、全体的に左にいくほど正解率が上がっているように見えるのは正しいのではないかと思います。ですが、やはりデータ数が少ない(250個ほど)ため、ほんの微々たるものでしかありません。

#まとめ
　実際に機械学習を実践してみましたが、やはりコピペなところが多く、理論的なことを理解できていない。そしてなにより、試合のデータから結果の予測が出来た。だから？といったレベルのものでしかありません。そもそも終わった試合のデータから終わった試合の結果を予測しているのですから、もはや予測とは？といった感じですらあります。

取り敢えず何でもいいから機械学習に触れてみよう。という思いつきから試してみましたが、何か意味あるものを出来た実感はないため、次は別のことで予測したという意味があるもの、もしくはこれを改善して意味あるのものに出来たらと思います。

今回のコードは[こちら](https://github.com/hyo07/lolapi/blob/master/lol_learning.py)