機械学習で競馬予想（その4）

## はじめに
特にFujitsu Advent Calendar 2016にちなんだ内容でなく、自分の趣味（継続中の開発の続き）を書きます。
※ 当記事は会社を代表するものではなく、個人の意見です。
（本業は新規事業立ち上げなどで開発職ではありません）

### 継続中の開発
[開発しているSIVAについて]
［facebook］
https://www.facebook.com/AIkeiba/
［Twitter］
https://twitter.com/Siva_keiba
随時実況していきますので、いいね！フォローお願いします。

## 今回のやること
[前回](http://qiita.com/tsunaki/items/82879dbe3cb7a6217eb0)の続き。
ディープラーニング（深層学習）による予測についてのサンプルコードを書きます。

始めるにあたり必要なスキルは[こちら](http://qiita.com/tsunaki/items/d440fa06f763b0bf17f7)を参考してください。

## 今回のライブラリ

TensorFlow:https://www.tensorflow.org/
[前回](http://qiita.com/tsunaki/items/d440fa06f763b0bf17f7)のデータを使います。
インストール方法は以下の記事などを参考にしてもらえばと思います。

[Pythonの環境構築からTensorFlowインストール](http://qiita.com/bohemian916/items/4f0736dcea932a162d9e)
  
## 予測モデル作成と実験(前と同じです)

以下のプログラムを準備してモデルの作成
###用意したデータは以下のCSV（JRA-VANより）

| ラベル名    | 説明 |
| :-----------|:--------------|:--------------|
|開催日|yyyy-mm-dd|
|競馬場|
|レース番号|
|レース名|
|コース|ダート|
|周回|ダートか芝の場合、右回りなら「右」、左回りなら「左」|
|距離|[m]|
|馬場状態|良（りょう）|
|賞金|[万円]|
|頭数|
|着順|
|枠番|
|馬番|
|馬名|
|性別|
|年齢|
|騎手|
|タイム|[s]|
|着差|前着の馬との差のこと,クビ,|大差|
|通過順|
|上り3F|ラスト600mのタイム[s]|
|斥量|[kg]|
|馬体重|[kg]|
|増減|前レースからの馬体重変化[kg]|
|人気|oddsの降順の番号|
|オッズ|
|ブリンカー|ブリンカー(目隠し)ありの場合、「B」|
|調教師|
|調教コメント|
|調教評価|


※ブリンカー、調教師、調教コメント以外をパラメータにしてます。

###予測プログラム(trainNN.py)

データと合わせて[Git](https://github.com/tsunaki00/horse_racing)にあげときました。

```python
# -*- coding: utf-8 -*-
import csv
import random
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction import DictVectorizer
from sklearn import preprocessing

class TrainNN:

  def __init__(self) : 
    # Parameters
    self.learning_rate = 0.01     # 学習率 高いとcostの収束が早まる
    self.training_epochs = 50     # 学習全体をこのエポック数で区切り、区切りごとにcostを表示する

    self.batch_choice = 500
    self.batch_size = 0           # 学習1回ごと( sess.run()ごと )に訓練データをいくつ利用するか
    self.display_step = 1         # 1なら毎エポックごとにcostを表示
    self.train_size = 500         # 全データの中でいくつ訓練データに回すか
    self.step_size =  500         # 何ステップ学習するか

    # Network Parameters
    self.n_hidden_1 = 64      # 隠れ層1のユニットの数
    self.n_hidden_2 = 64      # 隠れ層2のユニットの数

    self.n_input = 27          # 与える変数の数
    self.n_classes = 0        # 分類するクラスの数

  def load_csv(self):
    file_name = "data/jra_race_resultNN.csv"
    df = pd.read_csv(file_name)
    ## 文字列の数値化
    labelEncoder = preprocessing.LabelEncoder()
    df['area_name'] = labelEncoder.fit_transform(df['area_name'])
    df['race_name'] = labelEncoder.fit_transform(df['race_name'])
    df['track'] = labelEncoder.fit_transform(df['track'])
    df['run_direction'] = labelEncoder.fit_transform(df['run_direction'])
    df['track_condition'] = labelEncoder.fit_transform(df['track_condition'])
    df['horse_name'] = labelEncoder.fit_transform(df['horse_name'])
    df['horse_sex'] = labelEncoder.fit_transform(df['horse_sex'])
    df['jockey_name'] = labelEncoder.fit_transform(df['jockey_name'])
    df['margin'] = labelEncoder.fit_transform(df['margin'])
    df['is_blinkers'] = labelEncoder.fit_transform(df['is_blinkers'])
    df['trainer_name'] = labelEncoder.fit_transform(df['trainer_name'])
    df['comments_by_trainer'] = labelEncoder.fit_transform(df['comments_by_trainer'])
    df['evaluation_by_trainer'] = labelEncoder.fit_transform(df['evaluation_by_trainer'])
    df['dhorse_weight'] = labelEncoder.fit_transform(df['dhorse_weight'])
    x_np = np.array(df[['area_name', 'race_number', 'race_name', 'track', 'run_direction',
                       'distance', 'track_condition', 'purse', 'heads_count', 
                       'post_position', 'horse_number', 'horse_name', 'horse_sex', 'horse_age', 
                       'jockey_name', 'time', 'margin', 'time3F', 
                       'load_weight', 'horse_weight', 'dhorse_weight', 'odds_order', 
                       'odds', 'is_blinkers', 'trainer_name', 'comments_by_trainer', 
                        'evaluation_by_trainer'
    ]].fillna(0))
    # 結果
    d = df[['finish_order']].to_dict('record')

    self.vectorizer = DictVectorizer(sparse=False)
    y_np = self.vectorizer.fit_transform(d)
    self.n_classes = len(self.vectorizer.get_feature_names())

    # データを訓練データとテストデータに分ける
    [self.x_train, self.x_test] = np.vsplit(x_np, [self.train_size]) 
    [self.y_train, self.y_test] = np.vsplit(y_np, [self.train_size])
    self.batch_size = self.train_size

  # Create model
  def multilayer_perceptron(self, x, weights, biases):
    # Hidden layer with RELU activation
    layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
    layer_1 = tf.nn.relu(layer_1)
    # Hidden layer with RELU activation
    layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
    layer_2 = tf.nn.relu(layer_2)
    # Output layer with linear activation
    out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
    return out_layer

  def train(self) :
    # tf Graph input
    x = tf.placeholder("float", [None, self.n_input])
    y = tf.placeholder("float", [None, self.n_classes])
    
    # Store layers weight & bias
    weights = {
      'h1': tf.Variable(tf.random_normal([self.n_input, self.n_hidden_1]), name="h1"),
      'h2': tf.Variable(tf.random_normal([self.n_hidden_1, self.n_hidden_2]), name="h2"),
      'out': tf.Variable(tf.random_normal([self.n_hidden_2, self.n_classes]), name="wout")
    }
    # バイアスの設定
    biases = {
      'b1': tf.Variable(tf.random_normal([self.n_hidden_1]), name="b1"),
      'b2': tf.Variable(tf.random_normal([self.n_hidden_2]), name="b2"),
      'out': tf.Variable(tf.random_normal([self.n_classes]), name="bout")
    }

    # Construct model
    pred = self.multilayer_perceptron(x, weights, biases)

    # Define loss and optimizer
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))

    correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    # Logistic Regression  AdamOptimizer GradientDescentOptimizer
    optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate).minimize(cost)

    # Initializing the variables
    init = tf.initialize_all_variables()
    saver = tf.train.Saver()
    # Launch the graph
    
    with tf.Session() as sess:
      tf.scalar_summary("cost", cost)
      tf.scalar_summary("accuracy", accuracy)
      merged = tf.merge_all_summaries()
      writer = tf.train.SummaryWriter("logs/tensorflow_log", sess.graph_def)    

      sess.run(init)
      # Training cycle
      for epoch in range(self.training_epochs):
        avg_cost = 0.
        # Loop over step_size
        for i in range(self.step_size):
          # 訓練データから batch_size で指定した数をランダムに取得
          ind = np.random.choice(self.batch_size, self.batch_choice)
          x_train_batch = self.x_train[ind]
          y_train_batch = self.y_train[ind]
          # Run optimization op (backprop) and loss op (to get loss value)
          _, c = sess.run([optimizer, cost], feed_dict={x: x_train_batch, y: y_train_batch})
          avg_cost += c / self.step_size

        # Display logs per epoch step
        if epoch % self.display_step == 0:
          print "Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(avg_cost)
          # Compute average loss
          summary_str, acc = sess.run([merged, accuracy], feed_dict={x: x_train_batch, y: y_train_batch})
          writer.add_summary(summary_str, epoch)
          print "Accuracy:", acc
          ## modelの保存
          # name_model_file = 'model_epoch_' + str(epoch+1) + '.ckpt'
          # save_path = saver.save(sess, 'model/tensorflow/' + name_model_file)
            
      # Test model
      correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
      # Calculate accuracy
      accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
      print "Accuracy:", accuracy.eval(session=sess, feed_dict={x: self.x_test, y: self.y_test})

if __name__ == "__main__":
  trainNN = TrainNN()
  trainNN.load_csv()
  trainNN.train()
  
```
※ pythonおよびデTensorFlowについては勉強中ですのでプログラムに誤りがあるかもです。

### 結果

```
$ python trainNN.py 
Epoch: 0001 cost= 28999.539070313
Accuracy: 0.183333
Epoch: 0002 cost= 10636.817809570
Accuracy: 0.146667
Epoch: 0003 cost= 8187.893877930
Accuracy: 0.216667
Epoch: 0004 cost= 6673.761091797
Accuracy: 0.183333
Epoch: 0005 cost= 5364.944535645
Accuracy: 0.196667
Epoch: 0006 cost= 4300.876705078
Accuracy: 0.176667
Epoch: 0007 cost= 3560.373814697
Accuracy: 0.27
Epoch: 0008 cost= 2843.689293701
Accuracy: 0.203333
Epoch: 0009 cost= 1605.616168819
Accuracy: 0.1
Epoch: 0010 cost= 21.059406254
Accuracy: 0.0666667
Epoch: 0011 cost= 9.320877672
Accuracy: 0.0566667
Epoch: 0012 cost= 5.621849694
Accuracy: 0.0866667
Epoch: 0013 cost= 4.749191205
Accuracy: 0.07
Epoch: 0014 cost= 4.069611630
Accuracy: 0.0733333
Epoch: 0015 cost= 3.946254434
Accuracy: 0.0566667
Epoch: 0016 cost= 3.253856863
Accuracy: 0.0433333
Epoch: 0017 cost= 3.309734127
Accuracy: 0.07
Epoch: 0018 cost= 2.978426178
Accuracy: 0.0566667
Epoch: 0019 cost= 2.970437231
Accuracy: 0.0533333
Epoch: 0020 cost= 2.999267270
Accuracy: 0.0566667
Epoch: 0021 cost= 3.828851234
Accuracy: 0.0766667
Epoch: 0022 cost= 2.859027134
Accuracy: 0.0666667
Epoch: 0023 cost= 2.799751988
Accuracy: 0.08
Epoch: 0024 cost= 2.799972694
Accuracy: 0.06
Epoch: 0025 cost= 2.799448245
Accuracy: 0.0633333
Epoch: 0026 cost= 2.799542419
Accuracy: 0.06
Epoch: 0027 cost= 2.798464981
Accuracy: 0.0933333
Epoch: 0028 cost= 2.799431955
Accuracy: 0.07
Epoch: 0029 cost= 2.798901843
Accuracy: 0.05
Epoch: 0030 cost= 2.797275106
Accuracy: 0.0366667
Epoch: 0031 cost= 2.799933175
Accuracy: 0.0566667
Epoch: 0032 cost= 2.799303545
Accuracy: 0.07
Epoch: 0033 cost= 2.799922926
Accuracy: 0.0533333
Epoch: 0034 cost= 2.799450584
Accuracy: 0.0833333
Epoch: 0035 cost= 2.800814153
Accuracy: 0.08
Epoch: 0036 cost= 2.799030569
Accuracy: 0.0733333
Epoch: 0037 cost= 2.799764482
Accuracy: 0.0566667
Epoch: 0038 cost= 2.799920460
Accuracy: 0.0833333
Epoch: 0039 cost= 2.799320694
Accuracy: 0.09
Epoch: 0040 cost= 2.800959777
Accuracy: 0.0766667
Epoch: 0041 cost= 2.799612596
Accuracy: 0.09
Epoch: 0042 cost= 2.800021134
Accuracy: 0.0666667
Epoch: 0043 cost= 2.799918224
Accuracy: 0.0666667
Epoch: 0044 cost= 2.799846961
Accuracy: 0.06
Epoch: 0045 cost= 2.799782973
Accuracy: 0.06
Epoch: 0046 cost= 2.798343555
Accuracy: 0.0633333
Epoch: 0047 cost= 2.800401002
Accuracy: 0.0633333
Epoch: 0048 cost= 2.799323093
Accuracy: 0.06
Epoch: 0049 cost= 2.799231728
Accuracy: 0.0733333
Epoch: 0050 cost= 2.800211056
Accuracy: 0.0633333
Accuracy: 0.0697074

$ tensorboard --logdir=logs/

```

### tensorboard

![Screen Shot 0028-12-17 at 23.12.08.png](https://qiita-image-store.s3.amazonaws.com/0/52867/5dd7ef60-e4eb-931f-7d97-eea89440f085.png "Screen Shot 0028-12-17 at 23.12.08.png")

![Screen Shot 0028-12-17 at 23.13.23.png](https://qiita-image-store.s3.amazonaws.com/0/52867/973e4d8d-b707-ac27-bfbe-50e06daae352.png "Screen Shot 0028-12-17 at 23.13.23.png")


0.6%。。。全くあたりませんね（笑）
※ もちろんですがSIVAで開発しているアルゴリズムは全く違います。
もう少し勉強してまた書きます！

## さいごに（ご参考）
SIVAについては的中率は約40%で回収率は安定して100%前後です。

![CzMZW9OUkAAab_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/52867/96bd7971-8f2c-58ac-e6b7-fb8f36fa7adc.jpeg "CzMZW9OUkAAab_1.jpg")

[開発しているSIVAについて]
［facebook］
https://www.facebook.com/AIkeiba/
［Twitter］
https://twitter.com/Siva_keiba
随時実況していきますので、いいね！フォローお願いします。