今回は棋譜を訓練データとして入力された局面から対局した場合の勝率を予想する価値ネットワークを作ります. 前回作った棋譜データを学習データとして利用します. 結論から申し上げてしまいますと,期待したほどの予測精度のモデルを作ることはできず価値ネットワーク単独で強いAIを作ることはできませんでした. 順を追ってコードを書いていきます. 本ページの最後に価値ネットワークの学習に使用したコードがまとめて掲載してあります.
from sklearn.model_selection import train_test_split
import tensorflow as tf
import numpy as np
from tensorflow import keras
import copy
from keras.callbacks import EarlyStopping
今回も画像データは前回と同じものを使います.正解データは「その局面から対局し勝利した場合を1」「引き分けだった場合を0.5」「負けた場合を0」とします. 正解データは前回作成したtarget_value.txtを使用します.
######### target の読み込み #########
target = []
f = open("target_value.txt","r")
for x in f:
num = float(x.rstrip("\n"))
target.append(num)
f.close()
target = np.array(target)
#########################################
########### image の読み込み ###########
images = np.zeros((len(target),2,8,8))
image = np.load("image.npy")
images = copy.deepcopy(image)
image = images
image = np.array(image,"float32")
勝った場合と負けた場合の2通りであれば損失関数にはbinary_cross_entoropyを使用したいところですが,今回は引き分けのデータがあるためmean_squared_errorを使用します. (3通りにしてsparce_cross_entoropyを使用したり,引き分けデータを削除してbinary_cross_entoropyとしてもよいかもしれません) 出力層の活性化関数は0から1の間で勝率を返してほしいのでsigmoid関数を使用します. 出力層以外は方策ネットワークの学習に使用したものと同じ13層の畳み込みニューラルネットワーク(CNN)を使用します. また,metricsにはaccuracyを使用していますが,引き分けのときは必ず不正解となってしまい正確ではありませんが,引き分けはそんなに多くないので目安として使用します.
model = keras.Sequential(
[
keras.layers.Permute((2,3,1), input_shape=(2,8,8)),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(1, kernel_size=1,use_bias=True),
keras.layers.Flatten(),
keras.layers.Dense(1, activation='sigmoid')
]
)
model.compile(optimizer='adam',
loss='mean_squared_error',
metrics=['accuracy'])
それではTensorFlowを使って価値ネットワークの学習を行います. target_value.txt image.npyのあるディレクトリで走らせることで学習が始まります. 今回は学習し終わったモデルの性能評価を実際のプレイでなく,model.evaluateによってしたいので訓練データ 性能評価用データ テストデータの3つにデータを分割します. 学習にはGeForce RTX 2080を利用して40分ほどかかります.
# 訓練データ 性能評価用データ テストデータの3つに分割する
x_train, x_test, y_train, y_test = train_test_split(image,target)
x_train, x_valid, y_train, y_valid = train_test_split(x_train,y_train)
x_train = tf.reshape(x_train, [-1, 2, 8, 8])
# EaelyStoppingの設定
early_stopping = EarlyStopping(
monitor='val_loss',
min_delta=0.0,
patience=2,
)
# 学習させる
history = model.fit(
x_train,
y_train,
epochs=100,
batch_size=1024,
validation_data=[x_valid, y_valid],
callbacks=[early_stopping] # CallBacksに設定
)
model.save('Value_network')
y_test2 = y_test[y_test!=0.5]
x_test2 = x_test[y_test!=0.5]
model.evaluate(x_test2,y_test2)
局面から次の一手を返してくれるわけではありませんが,全ての合法手に対して一手進めてみて,一手先の局面 つまり相手ターンの局面の勝率が最小になる手を選ぶようにすることで, 価値ネットワークを利用してオセロをプレイできるAIを作ることができます. 以下を前回のプログラムのBoardクラスに追加して,policy_step = env.value_action(MODEL) とすることでプレイできます
def value_action(self,value_net):
legall_list = self.legall_list()
first_board = copy.deepcopy(self.board)
first_turn = self.turn
# 自分の手番が前に来るようにする
if first_turn == 1:
enemy_turn = 2
else:
enemy_turn = 1
value_list = []
for move in legall_list:
copied_env = Othello()
copied_env.turn = first_turn
copied_env.board = copy.deepcopy(first_board)
copied_env.step(move)
# step後にターンを変える
copied_env.turn = enemy_turn
# 相手が置けるか調べる
enemy_legall_list = copied_env.legall_list()
# 置ける場合は相手の勝率をそのまま追加
if len(enemy_legall_list) > 0:
current_time_step = np.zeros((1,2,8,8))
current_time_step[0][0] = np.array(copied_env.board==enemy_turn,"float32")
current_time_step[0][1] = np.array(copied_env.board==first_turn,"float32")
value_list.append(np.array(value_net(current_time_step))[0][0])
# 相手がパスした場合は 1-(自分の勝率)=相手の勝率を追加
else:
current_time_step = np.zeros((1,2,8,8))
current_time_step[0][0] = np.array(copied_env.board==first_turn,"float32")
current_time_step[0][1] = np.array(copied_env.board==enemy_turn,"float32")
value_list.append(1-np.array(value_net(current_time_step))[0][0])
# print(value_list)
# return legall_list[np.argmax(np.array(value_list))]
return legall_list[np.argmin(np.array(value_list))]
単純な価値ネットワークの学習では満足のいく結果を得ることができませんでした. 訓練データに序盤と終盤の偏りが出ないようにしたりデータ数を増やすことで改善されるかもしれませんが,強いAIを作るのは難しそうです. 次回はマルチタスク学習を行うことで価値ネットワークの予測精度の向上を図ります.
from sklearn.model_selection import train_test_split
import tensorflow as tf
import numpy as np
from tensorflow import keras
import copy
from keras.callbacks import EarlyStopping
######### target の読み込み #########
target = []
f = open("target_value.txt","r")
for x in f:
num = float(x.rstrip("\n"))
target.append(num)
f.close()
target = np.array(target)
#########################################
########### image の読み込み ###########
images = np.zeros((len(target),2,8,8))
image = np.load("image.npy")
images = copy.deepcopy(image)
image = images
image = np.array(image,"float32")
# 訓練データ 性能評価用データ テストデータの3つに分割する
x_train, x_test, y_train, y_test = train_test_split(image,target)
x_train, x_valid, y_train, y_valid = train_test_split(x_train,y_train)
print("data splited")
model = keras.Sequential(
[
keras.layers.Permute((2,3,1), input_shape=(2,8,8)),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(128, kernel_size=3,padding='same',activation='relu'),
keras.layers.Conv2D(1, kernel_size=1,use_bias=True),
keras.layers.Flatten(),
keras.layers.Dense(1, activation='sigmoid')
]
)
model.compile(optimizer='adam',
loss='mean_squared_error',
metrics=['accuracy'])
x_train = tf.reshape(x_train, [-1, 2, 8, 8])
# EaelyStoppingの設定
early_stopping = EarlyStopping(
monitor='val_loss',
min_delta=0.0,
patience=2,
)
# 学習させる
history = model.fit(
x_train,
y_train,
epochs=100,
batch_size=1024,
validation_data=[x_valid, y_valid],
callbacks=[early_stopping] # CallBacksに設定
)
model.save(f'Value_network')