2017年10月22日日曜日

pythonで競艇の予測プログラムをサクッと作る

フローを整理してプログラムの目的を明確にする

ブログを始めるにあたりプログラムの可読性を高めるためにフローを図のように統一しました。これによりプログラムの位置付けが明確になり読みやすくなりました。またデータベースを使うことで作業の区切りをつけて、取り扱うデータを頭の中で整理しやすくなってます。その際メモリー容量とか演算スピードとかは一切考えてません。あくまでデータをフラットに使って情報の可読性を最優先してます。プログラムは以下の通り細分化して参照したいプログラムを探しやすくしましたので興味がありましたら参照して下さい。

競艇の予測プログラムがこんなに簡単に!

先に2016年女子の2964回分のレース結果をデータベースに用意して、次のレース予測を行うプログラムが以下の通りとなります。SQLを作り上げるクラスを準備して、SQLを実行する関数で2016年のレース結果をデータベースから読み出します。この2016年のレース結果から2017年に実施されるレースを予測するのがpredict関数になります。


from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.callbacks import EarlyStopping
import pandas as pd, numpy as np
import psycopg2,sys

class SQL:
    """ SQL script """
    def __init__(self) :
        self.SQL = "SELECT COUNT(*) "
        self.SQL = self.SQL + "FROM SAMPLE_1 ;"
    def pastData(self,date) :
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE DATE_PART('YEAR',date) = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ORDER BY date ;"
    def predictData(self,date):
        self.SQL = "SELECT * "
        self.SQL = self.SQL + "FROM SAMPLE_1 "
        self.SQL = self.SQL + "WHERE date = '"
        self.SQL = self.SQL + date
        self.SQL = self.SQL + "' ;"

def execute_SQL_pandas(SQL) :
    """ Read race result from DataBase """
    try :
        conn = psycopg2.connect(database="ddddddd", user="uuuuu", password="xxxxx", host="127.0.0.1", port="oooo")    
        results = pd.read_sql(SQL,conn)        
        return results
    except psycopg2.OperationalError as e:        
        print('Unable to connect!\n{0}').format(e)
        sys.exit(1)    
    finally:
        if conn:
            conn.close()

def predict(pastData,predictData) :
    """Predict boat race """
    #過去のレース結果にこれから始まるデータを追加する
    pastData = pastData.append(predictData, ignore_index=True)
    #レーサー毎の勝率を正規化する
    pastData["white_racer"] /= max(pastData['white_racer']) 
    pastData["black_racer"] /= max(pastData['black_racer']) 
    pastData["red_racer"] /= max(pastData['red_racer']) 
    pastData["blue_racer"] /= max(pastData['blue_racer']) 
    pastData["yellow_racer"] /= max(pastData['yellow_racer']) 
    pastData["green_racer"] /= max(pastData['green_racer']) 
    #学習用のデータを一つにまとめる
    inputData = pastData[["white_racer",
                          "black_racer",
                          "red_racer",
                          "blue_racer",
                          "yellow_racer",
                          "green_racer"
                  ]].as_matrix()
    #結果の識別(今回は6種類)
    label_no = 6
    label = {"1":[1,0,0,0,0,0], 
             "2":[0,1,0,0,0,0], 
             "3":[0,0,1,0,0,0], 
             "4":[0,0,0,1,0,0], 
             "5":[0,0,0,0,1,0],
             "6":[0,0,0,0,0,1]}
    #結果をラベルに置き換える
    resultLabel = np.empty((len(pastData),label_no))
    for i, v in enumerate(pastData["result"]):
        resultLabel[i] = label[v]
    #訓練データとテストデータを分ける 
    n = int(len(pastData)/3)*2 + 1
    trainIn, trainLabel = inputData[1:n], resultLabel[1:n]
    testIn,  testLabel  = inputData[n:len(pastData)-12], resultLabel[n:len(pastData)-12] 


    #モデルの構造を定義
    model = Sequential()
    model.add(Dense(512, input_shape=(6,)))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dropout(0.1))

    model.add(Dense(label_no))
    model.add(Activation('softmax'))

    #モデルを構築
    model.compile(
        loss='categorical_crossentropy',
        optimizer="rmsprop",
        metrics=['accuracy'])

    #データで訓練開始
    model.fit(
        trainIn, trainLabel,
        batch_size=100,
        nb_epoch=20,
        validation_split=0.1,
        callbacks=[EarlyStopping(monitor='val_loss', patience=2)],
        verbose=1)

    #モデルの評価を開始
    score = model.evaluate(testIn, testLabel)
    #レースの予想を開始
    predictInData = inputData[len(pastData)-12:len(pastData)]
    predictions = model.predict(predictInData)
    
    return score,predictions

if __name__ == '__main__':
    
    #過去のレース結果をデータベースから読み出す
    sql = SQL()
    sql.pastData('2016')
    pastData = execute_SQL_pandas(sql.SQL)
    
    #予想するレースのデータをデータベースから読み出す
    sql.predictData('2017-01-14')
    predictData = execute_SQL_pandas(sql.SQL)
    #予想する
    score,predictions = predict(pastData,predictData)
    

    


競艇の予測プログラムの実行結果(正解率:52%)

2016年のレース結果のデータは以下の通りです。各レーサの勝率を元に人口知能に学習させてます。これが2964回のレース分あります。


pastData.ix[0]
Out[62]: 
date                  2016-01-09
race_no                        1
white_racer                  9.5 ←1号艇レーサーの勝率
black_racer                 20.6 ←2号艇レーサーの勝率
red_racer                   17.8 ←3号艇レーサーの勝率
blue_racer                   2.3 ←4号艇レーサーの勝率
yellow_racer                28.3 ←5号艇レーサーの勝率
green_racer                 23.2 ←6号艇レーサーの勝率
result                         5  ←結果(5号艇が1位)
Name: 0, dtype: object
    

プログラムの実行結果は、正解率は52%で3号艇が0.36で一番高く結果は3号艇で大当たり


In [63]:score[1]
Out[63]: 0.52000000042793082 ←正解率52%

In [64]:
predictions[0]         予測結果
Out[64]:              ↓
array([ 0.18952873,  0.29357207,  0.36467475,  0.08164515,  0.04051487,  0.03006445], dtype=float32)  
predictData.ix[0]
Out[65]: 
date                  2017-01-14
race_no                        1
white_racer                  5.1
black_racer                   18
red_racer                   31.6 ←勝率の高さで学習しているのが分かる
blue_racer                   4.3
yellow_racer                 7.2
green_racer                    3
result                         3
Name: 0, dtype: object
    

第2レースの予測結果は、3号艇が0.40で一番高く、結果は3号艇で大当たり


predictions[1]
Out[67]: 
array([ 0.16596539,  0.30057701,  0.40548053,  0.08547471,  0.02496851,  0.01753386], dtype=float32)

predictData.ix[1]
Out[68]: 
date                  2017-01-14
race_no                        2
white_racer                  2.7
black_racer                   24
red_racer                     41
blue_racer                    12
yellow_racer                 5.6
green_racer                    0
result                         3
Name: 1, dtype: object
    

第3レースの予測結果は、1号艇が0.30で一番高く結果は4号艇で外れ。


predictions[2]
Out[69]: 
array([ 0.30444902,  0.1886065 ,  0.06484132,  0.27285177,  0.14985009,  0.01940131], dtype=float32)

predictData.ix[2]
Out[70]: 
date                  2017-01-14
race_no                        3
white_racer                   16
black_racer                   15
red_racer                    3.3
blue_racer                  31.8
yellow_racer                43.6
green_racer                   10
result                         4
Name: 2, dtype: object
    


結論:予測プログラムは意外と使えそう

最後のレース結果は単純に勝率の高さだけでなくスタート位置を考慮していることを伺わせるような予想を立てています。勝率が一番高い5号艇よりも4号艇の方が確率が高くなっているところに可能性を感じます。正解率を100%にすることは絶対に無理ですが、モーターの勝率やボート勝率を含めたり、あるいはスタート位置からの勝率などを計算し直すことで現状の正解率を60%とか70%まで高めることはできそうです。(現在検証中)

このようにpythonプログラムって簡単じゃないですか?pythonをイチから完璧に覚えてベストはプログラムを目指そうとするとプログラムを書き始められるまでかなりの時間を要することになります。ベストじゃなくベターなプログラム(既に実績のあるプログラム)ならこんな感じで今からでもスタートできます。興味のある方はブログを御覧下さい。

This Is The Oldest Page