今回の目標
前回作成したモデルとの対戦プログラムを作成します。
ここから本編
使用するのはこれまでも何度も使ってきたBitBoardクラスと前回作成した学習済みモデルです。
BitBoard.py
オセロをするための基本クラスを置いたファイル。
中身は省略します。
net.py
ニューラルネットワークを定義したクラスを置いたファイル。
前回同様、4層全て全結合層で活性化関数はtanhです。
import chainer
import chainer.functions as F
import chainer.links as L
class Net(chainer.Chain):
def __init__(self):
n_in = 128
n_hidden = 128
n_out = 1
super().__init__()
with self.init_scope():
self.l1 = L.Linear(n_in, n_hidden)
self.l2 = L.Linear(n_hidden, n_hidden)
self.l3 = L.Linear(n_hidden, n_hidden)
self.l4 = L.Linear(n_hidden, n_out)
def __call__(self, x):
h = F.tanh(self.l1(x))
h = F.tanh(self.l2(h))
h = F.tanh(self.l3(h))
h = self.l4(h)
return h
osero_deep_AI.py
前回作成したモデルとの対戦を行うためのクラスを置いたファイル。
インポート、定義部分
盤面複製のためのdeepcopy、予測値がかぶった時のためのランダム、モデル操作のためchainer、盤面をモデルに投げる際の整形のためnumpyをインポートしています。
BitBoardとnetは前述の通り。
from copy import deepcopy
from random import randint
import chainer
import numpy as np
from BitBoard import osero
import net
BLACK = 0
WHITE = 1
コンストラクタ
思考方法に今回のモデルを追加し、モデルとの対戦準備を整えます。
class osero_deep_AI(osero):
def __init__(self, player=BLACK, read_goal=[1, 1], eva=[0, 0],\
player_method=osero.PLAY_WAY["human"]):
osero.PLAY_WAY["osero_deep_AI"] = len(osero.PLAY_WAY)
if player == BLACK:
black_method = player_method
white_method = osero.PLAY_WAY["osero_deep_AI"]
else:
black_method = osero.PLAY_WAY["osero_deep_AI"]
white_method = player_method
super().__init__(\
black_method,
white_method,
read_goal=read_goal,
eva=eva
)
self.think.append(self.osero_deep_AI)
self.model_setup()
model_setup
ニューラルネットワークを呼び出します。
def model_setup(self) -> None:
self.net = net.Net()
chainer.serializers.load_npz("deep_AI.net", self.net)
reshape
盤面を、モデルに投げられる形に変更します。
def reshape(self, now_board: dict) -> np.ndarray:
data = []
if self.turn:
my = ["b_u", "b_d"]
opp = ["w_u", "w_d"]
else:
my = ["w_u", "w_d"]
opp = ["b_u", "b_d"]
for i in range(32):
data.append(int(now_board[my[0]] & 1 << i))
data.append(int(now_board[opp[0]] & 1 << i))
for i in range(32):
data.append(int(now_board[my[1]] & 1 << i))
data.append(int(now_board[opp[1]] & 1 << i))
return np.array([data], dtype=np.float32)
predict
盤面から最終結果の予測値を返します。
def predict(self, now_board: dict) -> float:
x = self.reshape(now_board)
return self.net(x).array[0][0]
osero_deep_AI
置ける場所の中から予測値が最大となる場所を探し、その位置に置きます。
なおこのメソッドは、親クラスのcheckメソッドにより、そのターンに置ける場所が一か所でもある場合のみ呼び出されます。そのため最低一か所は置ける場所があるという前提で作られています。
def osero_deep_AI(self) -> None:
max_score = -100.
line_ans, col_ans = [-1], [-1]
place_num = 0
for i in range(8):
for j in range(8):
if not self.check(i, j, self.bw, self.turn):
continue
board_leaf = deepcopy(self.bw)
self.put(i, j, board_leaf, self.turn)
score = self.deep_AI_think(\
board_leaf,
not self.turn,
1,
self.read_goal[self.turn]
)
if score > max_score:
max_score = score
place_num = 0
line_ans = [i]
col_ans = [j]
elif score == max_score:
place_num += 1
line_ans.append(i)
col_ans.append(j)
if place_num:
place = randint(0, place_num)
line_ans[0] = line_ans[place]
col_ans[0] = col_ans[place]
print("line:\t%d\ncol:\t%d" % (line_ans[0]+1, col_ans[0]+1))
self.put(line_ans[0], col_ans[0], self.bw, self.turn)
deep_AI_think
再帰的に目的のターン数まで進み、その段階での予測値を返します。
def deep_AI_think(self, now_board: dict, turn: bool,\
num: int, read_goal: int) -> float:
if num == read_goal:
return self.predict(now_board)
score = 0
place_num = 0
for i in range(8):
for j in range(8):
if not self.check(i, j, now_board, turn):
continue
place_num += 1
board_leaf = deepcopy(now_board)
self.put(i, j, board_leaf, turn)
score += self.deep_AI_think(\
board_leaf,
not turn,
num + 1,
read_goal
)
if place_num:
return score / place_num
else:
return self.predict(now_board)
play
試合を行います。
def play(self) -> None:
can, old_can = True, True
self.printb()
can = self.check_all()
while can or old_can:
if can:
if self.turn:
print("black turn")
self.think[self.black_method]()
else:
print("white turn")
self.think[self.white_method]()
self.printb()
self.turn = not self.turn
old_can = can
can = self.check_all()
self.count_last()
実行部分
オブジェクトの作成と試合実行を行います。
引数playerを変更することで黒と白どちらでプレイするかを変更でき、player_methodを変更することでAIと対戦するときの思考方法を変更できます。
humanを指定することで自分がAIと対戦でき、randomやnhand、nleastなどに変更することも可能です。
if __name__ == "__main__":
a = osero_deep_AI(player=BLACK, player_method=osero.PLAY_WAY["human"])
a.play()
実際にやってみた
機械学習の時よりも強くなった気はしますが、まだまだ人間に勝てるほどの強さはありません。
フルバージョン
次回は
最初予想していたよりはるかに長い時間がかかりましたが、これでランダム・探索法・機械学習・深層学習の四手法でのオセロが完成しました。
それぞれを戦わせ、その強さを客観的に評価してみたいと思います。