著者:石上椋一
毎年年末に開催されている競馬の「有馬記念」というレースをご存知でしょうか。今回は、その有馬記念の順位を予測するAIを開発した話を紹介します。2021年12月には、開発したAIを使って第66回有馬記念の順位を予測してみました。結果がどうだったのかについては、記事の最後に書いています。
シェルスクリプトマガジン Vol.77は以下のリンク先でご購入できます。![]()
![]()
図2 RankNetを実装するPythonコード
from tensorflow.keras import layers, Model, Input
from tensorflow.nn import leaky_relu
class RankNet(Model):
def __init__(self):
super().__init__()
self.dense = [layers.Dense(16, activation=leaky_relu),
layers.Dense(8, activation=leaky_relu)]
self.o = layers.Dense(1, activation='linear')
self.oi_minus_oj = layers.Subtract()
def call(self, inputs):
xi, xj = inputs
densei = self.dense[0](xi)
densej = self.dense[0](xj)
for dense in self.dense[1:]:
densei = dense(densei)
densej = dense(densej)
oi = self.o(densei)
oj = self.o(densej)
oij = self.oi_minus_oj([oi, oj])
output = layers.Activation('sigmoid')(oij)
return output
図3 データの整理とラベル付けをするPythonコード
import pandas as pd
import numpy as np
from itertools import combinations
years = [2020,2019,2018,2017,2016,2015,2014,2013,2012,2011]
# ここで任意のCSVファイルを指定する
# 今回はCSVにデータを残しているため、CSVから読み込んでいる
df = pd.read_csv(CSVFile)
df["タイム指数2-3"] = df["タイム指数2"] - df["タイム指数3"]
index_num = 0
xi = xj = pij = pair_ids = pair_query_id = []
for year in years:
one_year_Data = df[df['年数'] == year]
index_list = [i for i in range(len(one_year_Data))]
random.shuffle(index_list)
for pair_id in combinations(index_list, 2):
pair_query_id.append(year)
pair_ids.append(pair_id)
i = pair_id[0]
j = pair_id[1]
xi.append([one_year_Data.at[i+index_num,"タイム指数2"],
one_year_Data.at[i+index_num,"タイム指数2-3"],
one_year_Data.at[i+index_num,"上り"]])
xj.append([one_year_Data.at[j+index_num,"タイム指数2"],
one_year_Data.at[j+index_num,"タイム指数2-3"],
one_year_Data.at[j+index_num,"上り"]])
if one_year_Data.at[i+index_num,"順位"] == one_year_Data.at[j+index_num,"順位"] :
pij_com = 0.5
elif one_year_Data.at[i+index_num,"順位"] > one_year_Data.at[j+index_num,"順位"] :
pij_com = 0
else:
pij_com = 1
pij.append(pij_com)
index_num += len(one_year_Data)
index_list.clear()
xi = np.array(xi)
xj = np.array(xj)
pij = np.array(pij)
pair_query_id = np.array(pair_query_id)
図4 学習用データと評価用データを仕分けるPythonコード
from sklearn.model_selection import train_test_split
xi_train, xi_test, xj_train, xj_test, pij_train, pij_test, \
pair_id_train, pair_id_test = train_test_split(
xi, xj, pij, pair_ids, test_size=0.2, stratify=pair_query_id)
図5 コンパイルと学習を実施するPythonコード
ranknet = RankNet()
# コンパイル
ranknet.compile(optimizer='sgd', loss='binary_crossentropy',metrics=['accuracy'])
# 学習
ranknet.fit([xi_train, xj_train], pij_train,
epochs=85, batch_size=4,
validation_data=([xi_test, xj_test], pij_test))
図8 ResNet50を使った学習モデルを実装するPythonコード
from tensorflow.keras.applications.resnet50 import ResNet50
from keras.layers import Dense, Dropout, Input, Flatten
# ResNetの準備
input_tensor = Input(shape=(64, 64, 3))
resnet50 = ResNet50(include_top=False, weights='imagenet',
input_tensor=input_tensor)
# FC層の準備
fc_model = Sequential()
fc_model.add(Flatten(input_shape=resnet50.output_shape[1:]))
fc_model.add(Dense(512, activation='relu'))
fc_model.add(Dropout(0.3))
fc_model.add(Dense(2, activation='sigmoid'))
# モデルの準備
resnet50_model = Model(resnet50.input, fc_model(resnet50.output))
# ResNet50の一部の重みを固定
for layer in resnet50_model.layers[:100]:
layer.trainable = False
図9 コンパイルと学習を実施するPythonコード
# コンパイル
resnet50_model.compile(optimizer='sgd',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 学習
history = resnet50_model.fit(train_generator,
batch_size=4,
epochs=50, verbose=1,
validation_data=val_generator)