トップページ -> 深層学習(ディープラーニング)でオセロAIを作る -> 棋譜から価値ネットワークを学習する

棋譜から価値ネットワークを学習する

今回は棋譜を訓練データとして入力された局面から対局した場合の勝率を予想する価値ネットワークを作ります. 前回作った棋譜データを学習データとして利用します. 結論から申し上げてしまいますと,期待したほどの予測精度のモデルを作ることはできず価値ネットワーク単独で強い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)
                
            
71.5%程度の正解率で予測できていることが分かります.

実際にプレイする

局面から次の一手を返してくれるわけではありませんが,全ての合法手に対して一手進めてみて,一手先の局面 つまり相手ターンの局面の勝率が最小になる手を選ぶようにすることで, 価値ネットワークを利用してオセロをプレイできる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')
            
        

<- 前へ戻る 【目次に戻る】 次へ進む ->