【Python】競艇の3連単予想をしようしてみた、けど…
単勝予想のプログラムがなんとなく完成した感じがしたので突発的に3連単予想をしてみることにしました。
関連記事
方法
単勝予想のプログラムのコピーを取り、少しだけモデルをチューニングし直して作成してみました。
単勝予想のソースコードは以下の記事を参照して下さい。
実際の変更点は、
- 教師あり学習の答えカラムの変更
- ニューラルネットワークの再構築
- それぞれの重み、モデルを保存して予想プログラムにて使用する
答えカラムの変更
訓練用のデータは変更する必要がないので、答えカラムだけ変更していきます。
上の図はそれぞれ、左の列が艇番、右の列が答えです。
例えば1位を答えとする場合、艇番1が1位だったとするとその値を1としてそれ以外(2~6位)は0(意味のもたない値)としています。
2位、3位も同じように変更していきます。
画像は左から順番に、1位、2位、3位のデータの例を表しています。
これによって単純ですが、2位、3位用のデータ用意できました。
ソースコードと説明
学習の仕方は過去記事に掲載した通りなので、今回は学習結果を保存した.jsonと.h5のファイルを呼び出して予想します。
予想するときに楽に実行するために、処理を関数化しています。
モジュール呼び出し
import pandas as pd from sklearn.model_selection import train_test_split from tensorflow.python.keras import models, layers, regularizers, callbacks from tensorflow.python.keras.models import model_from_json
- Pandas
- Scikit-Learn
- Keras
を使用しています。
モデル・重みの呼び出し
def model_weight(): # データ40000件 model_1st = model_from_json(open("pre_1.json", "r").read()) model_1st.load_weights("pre_1.h5") model_2nd = model_from_json(open("pre_2.json", "r").read()) model_2nd.load_weights("pre_2.h5") model_3rd = model_from_json(open("pre_3.json", "r").read()) model_3rd.load_weights("pre_3.h5") return model_1st, model_2nd, model_3rd
model_from_json
学習のときに保存したJSON文字列から同じアーキテクチャのモデルを最インスタンス化します。
このとき、モデルの重みは初期化されます。
load_weights
モデルは取得できましたが、重要なモデルの重みが初期化されてしまっているので、重みも別で取得する必要があります。
load_weightsはモデルの重みをHDF5形式のファイルから読み込むことができます。
Keras Documentationにさらに詳しく書かれています。
平均値、標準偏差を取得
def mean_and_std(): """学習用のデータから平均値と標準偏差を抽出 """ names = ["y", "Place", "Number", "Name", "Age", "Live", "Weight", "Rank", "A_1st", "A_2nd", "B_1st", "B_2nd", "Moter_No", "Moter_2nd", "Bote_No", "Bote_2nd"] df = pd.read_csv('program_data.csv', names = names).drop(["Name", "Live", "Number"], axis=1).fillna(0) df_dummies = pd.get_dummies(df["Rank"]) df_dummies.astype("float32") df = df.drop('Rank', axis=1) df.astype("float32") df = pd.merge(df, df_dummies, how="left", left_index=True, right_index=True) X = df.iloc[:, 1:].values.astype("float32") y = df.iloc[:, 0].values.astype("float32") X_train, y_train, X_test, y_test = train_test_split( X, y, test_size=0.2, random_state=0) mean = X_train.mean(axis=0) std = X_train.std(axis=0) return mean, std
訓練用のデータから正則化のための平均値と標準偏差を取得します。
データの変更と予測
def edit_and_prediction(date, data_list): for num, df_test in enumerate(data_list, start=1): names = ["Place", "Number", "Name", "Age", "Live", "Weight", "Rank", "A_1st", "A_2nd", "B_1st", "B_2nd", "Moter_No", "Moter_2nd", "Bote_No", "Bote_2nd"] df_rank = pd.DataFrame([[None, None, None, None, None, None, "A1", None, None, None, None, None, None, None, None], [None, None, None, None, None, None, "A2", None, None, None, None, None, None, None, None], [None, None, None, None, None, None, "B1", None, None, None, None, None, None, None, None], [None, None, None, None, None, None, "B2", None, None, None, None, None, None, None, None]], columns=names) df_test = df_test.append(df_rank, ignore_index=True) df_test = df_test.drop(["Name", "Number", "Live"], axis=1) df_dummies2 = pd.get_dummies(df_test["Rank"]).astype("float32") df_test_merge = pd.merge(df_test, df_dummies2, how="left", left_index=True, right_index=True) df_test_merge = df_test_merge.dropna().drop("Rank", axis=1) df_test = df_test_merge.astype('float32') df_test -= mean df_test /= std pre_1st = model_1st.predict(df_test) pre_2nd = model_2nd.predict(df_test) pre_3rd = model_3rd.predict(df_test) with open("race_predict.txt", "a", encoding="utf-8") as f: f.write("""\ {} : {}Rの予想結果 ------------------------""".format(date, num)) f.write("\n1st Predict") for i, predict_1st in enumerate(pre_1st, start=1): f.write("\n {} : {:6.2f}%".format(i, float(predict_1st)*100)) f.write("\n\n2nd Predict") for i, predict_2nd in enumerate(pre_2nd, start=1): f.write("\n {} : {:6.2f}%".format(i, float(predict_2nd)*100)) f.write("\n\n3rd Predict") for i, predict_3rd in enumerate(pre_3rd, start=1): f.write("\n {} : {:6.2f}%".format(i, float(predict_3rd)*100))
edit_and_prediction()関数を作成します。
第一引数には、その日の日付。
第二引数には、1回のレース分(6行)のデータを1つ分として入っているリストが入ります。
処理の流れ
- data_listを1つずつ取得、enumerateメソッドによりレース番号を取得(num)
- A1, A2, B1, B2の階級を追加(含まれない階級が存在した時、pd.get_dummiesのときに差が生じる。)
- いらない情報を捨てて、get_dummiesによってOneHotEncodingしたデータを結合。
- データの正則化
- 予測して出力
main関数
if __name__ == "__main__": date="0402" # 元のデータから平均値、標準偏差を抽出 mean, std = mean_and_std() # モデルを取得 model_1st, model_2nd, model_3rd = model_weight() # 予測するDataFrameのリスト(1R~12R)を取得 data_list = create_df(date) # DFの編集と予測 edit_and_prediction(date, data_list)
main関数でそれぞれの関数を呼び出して予測させます。
create_df()関数に関しては、ソースコードを書き記してもいいですが、データを表記しないと説明のしようがないので省略させていただきます。
結果
予測してみた結果、以下のようになりました。
0402 : 1Rの予想結果 ------------------------ 1st Predict 1 : 53.50% 2 : 8.69% 3 : 6.38% 4 : 3.82% 5 : 2.91% 6 : 3.22% 2nd Predict 1 : 20.38% 2 : 13.26% 3 : 15.06% 4 : 11.84% 5 : 8.43% 6 : 14.01% 3rd Predict 1 : 16.79% 2 : 16.04% 3 : 16.55% 4 : 17.17% 5 : 16.40% 6 : 18.44%
1Rのみ抜粋して表示しています。
1st Prediction
1位予想を見てみると、1番が53%となっています。
決して分類率は高くないですが、他に比べればちゃんと分類できていると言えます。
2nd Prediction
次に2位予想を見てみましょう。
1位と比べてだいぶ分類率が低くなっています。
他の予測をみてみても、最大分類率は24~25ほどでほとんど分類できていません。
数%の差では性格な予想をすることは不可能に近いです。
この時点であまり良い結果ではないですが、3位予想に写りましょう。
3rd Prediction
もう全くどれを選んでいいのかわかりませんね。
これはもう全く分類できていないと言った方がいいです。
これはまた一から開発しなおす必要がありますね。
まとめ
現時点では、私の力で予想することはできませんでした。
モデルのチューニングをし直したり、ニューラルネットの再構築、さらなる知識を得て全く新しい方法で開発する、など方法はさまざまですからこれからも勉強をがんばりたいと思います。
またなんらかの足掻きが見つかったときは、記事を更新します。