デレマスで機械学習（実践編）

[前回](http://qiita.com/ratuz/items/a462997793031a28a4ad)の続きです。
[アイドルマスターシンデレラガールズ](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%A4%E3%83%89%E3%83%AB%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC_%E3%82%B7%E3%83%B3%E3%83%87%E3%83%AC%E3%83%A9%E3%82%AC%E3%83%BC%E3%83%AB%E3%82%BA)の183人（2017/4現在）のプロフィールデータから、３つのタイプ（Cu,Co,Pa）の予測を行います。

取得した項目は、以下の16つ。183×16の行列です。
[タイプ,名前,年齢,誕生,星座,血液型,身長,体重,B,W,H,利き手,出身地,趣味,CV,実装日]
※タイプだけ項目名が入っていなかったため手動で追加しました。

この内、今回は以下の６つのデータを使用し、タイプの予測を行います。
[年齢,身長,体重,B,W,H]

#データの整形
取得したデータのtypeはすべてobjectとなっているため、数値型に変換します。

```py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import csv
import pandas as pd
from pandas import DataFrame
import matplotlib
import matplotlib.pyplot as plt

def translate(df):

    #データのタイプをfloatに変換
    df['年齢']=df['年齢'].str.extract('([0-9]+)').astype(float)
    df['身長']=df['身長'].astype(float)
    df['体重']=df['体重'].str.extract('([0-9]+)').astype(float)
    df['B']=df['B'].str.extract('([0-9]+)').astype(float)
    df['W']=df['W'].str.extract('([0-9]+)').astype(float)
    df['H']=df['H'].str.extract('([0-9]+)').astype(float)

    #属性値を数値変換
    df.loc[df['属性'] == "Cu", '属性'] = 0
    df.loc[df['属性'] == "Co", '属性'] = 1
    df.loc[df['属性'] == "Pa", '属性'] = 2
    df['属性']=df['属性'].astype(int)

    return df

if __name__ == '__main__':
    #データ読み込み
    df = pd.read_csv('aimasudata.csv')
    df=translate(df)
```
- 年齢等のデータにはたまに日本語が混じっているため、数字だけを抽出するためにstr.extract('([0-9]+)')を使っています。\([永遠の○歳]→[○]となります。やったね！)
- 属性値は、SVMの判定に使用するために数値変換しています。

#データの確認
機械学習によって本当に判別可能かを確認するためにデータをグラフ化してみます。

```py
def checkdata(df,index):

    #各タイプ毎のデータを取得
    x1 = [df[index][df['属性']==0]]
    x2 = [df[index][df['属性']==1]]
    x3 = [df[index][df['属性']==2]]
    
    #ヒストグラムの生成
    plt.hist([x1,x2,x3], bins=16)

    #画像の保存
    plt.savefig("%s_graph.png" %index)

    #画像の表示
    plt.show()

if __name__ == '__main__':
    #データ読み込み
    df = pd.read_csv('row_data.csv')
    df=translate(df)
    checkdata(df,"年齢")
```
##結果
####年齢
![年齢_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/f9fc8013-810e-427e-76a0-2d03ecde8f96.png)
青：Cu、橙：Co、緑：Paです。
Coは年齢高めの割合が多い。

####身長
![身長_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/b2508b93-0180-a6bd-f5d4-a63af90be52d.png)
Cuは低め、Coは高めか。このデータが最も差が出ている。

####体重
![体重_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/7ab8afa9-7f5a-ad3d-a60f-66430d252675.png)
差は大きくないがCoがやや高め。全体的に軽すぎ。

####B
![B_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/fa654c04-586e-b8e8-104b-4b09d3679256.png)

####W
![W_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/d22f2e45-9c3c-fe3e-c6c0-c2bace13d6b3.png)

####H
![H_graph.png](https://qiita-image-store.s3.amazonaws.com/0/153318/48bc744e-9685-90a3-7a86-7a6215358329.png)
身体データ系は全体的にCoは値が高い。CuとPaの分離が微妙か？

#グリッドサーチ
今回は、SVMを用いて３つのクラス（Co,Cu,Pa）を判定します。
SVMを実装する際はパラメータ設定が必要ということなので、
まずはグリッドサーチを用いてSVM適用するパラメータを決定します。

[Scikit learnより グリッドサーチによるパラメータ最適化]
(http://qiita.com/SE96UoC5AfUt7uY/items/c81f7cea72a44a7bfd3a)

```py
def gridsearch(df):
    tuned_parameters = [{'C': [1, 10, 100, 1000, 10000], 'kernel': ['rbf'], 'gamma': [0.01,0.001, 0.0001]}]
    score = 'f1'
    clf = GridSearchCV(
        SVC(), # 識別器
        tuned_parameters, # 最適化したいパラメータセット
        cv=5, # 交差検定の回数
        scoring='%s_weighted' % score ) # モデルの評価関数の指定

    df = df.dropna(subset=['年齢','身長','体重','B','W','H'])
    X = df[['年齢','身長','体重','B','W','H']]
    y = df["属性"]

    clf.fit(X, y)

    print"mean score for cross-validation:\n"
    for params, mean_score, all_scores in clf.grid_scores_:
        print "{:.3f} (+/- {:.3f}) for {}".format(mean_score, all_scores.std() / 2, params)
    print clf.best_params_
```
## 結果
<img width="507" alt="スクリーンショット 2017-04-07 22.15.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/153318/7657f3ec-0f09-cbc3-4da8-b2f979e9e9b6.png">

C=100, gamma=0.0001のときに結果がもっともよくなるみたいです。

#SVMの実装
グリッドサーチで得たパラメータを用い、SVMを実装します。

```py
def dosvm(df):
    #欠損値のある行を削除
    df=df.dropna(subset=['年齢','身長','体重','B','W','H'])

    X = df[['年齢','身長','体重','B','W','H']]
    y = df["属性"]
   
    data_train,data_test,label_train,label_test=train_test_split(X,y,test_size=0.2)

    clf = svm.SVC(kernel="rbf",C=100,gamma=0.0001)
    clf.fit(data_train, label_train)
    result=clf.predict(data_test)
    cmat=confusion_matrix(label_test,result)
    acc=accuracy_score(label_test,result)
    
    print cmat
    print acc
```
##　結果
<img width="112" alt="スクリーンショット 2017-04-07 22.22.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/153318/b0752c93-d385-8483-420b-b36e620f5981.png">

100試行くらい行ったところ、だいたい0.45くらいの精度で判別できました。
混同行列を見る限り、やはりPaの予測がうまくできてないみたいです。

#メモ
- 始めた時は全く識別できないかと思っていたが、意外と識別できた。
- SVMを用いる際はパラメータの設定が必須。(設定せずやったら結果が0.3くらいになった)
- 今回6つのパラメータを用いてタイプの予測を行なったが、身長だけのパラメータを用いても約0.42くらいの精度が出る。逆に、身長を除いた5つのパラメータを使うと、約0.36の精度。このあたりの結果の原因分析の方法を学びたい
- 私はCoでした（予測通り）