将棋AIで学ぶディープラーニングを読む

7.5.1 棋譜のクリーニング

2015,2016,2017年の3年分の棋譜をDwonLoadしてクリーニングした
結構時間がかかった。

kifu count : 82882
rate mean : 3080.1035267006105
rate median : 3054.0
rate max : 9630.0
rate min : 2500.0
約3倍のデータが集まった。

7.5.2 訓練データとテストデータに分割

total kifu num = 82883
train kifu num = 74594(90%)
test kifu num = 8289(10%)
それぞれのファイルのリストは
myproject/dlshogi/python_dlshogi_master/utilsに書き込まれた

将棋AIで学ぶディープラーニングを読む

6.4.6 Chainerのinnstall

lubuntu 16.04(GPUは積んでいない) にinstall ver 3.0.0
Chainer: A flexible framework for neural networks

wget https://github.com/chainer/chainer/archive/v3.0.0.tar.gz #最後のファイル名をinstallしたchainerのバージョンにあわせる
tar xzf v4.0.0.tar.gz
python chainer-4.0.0/examples/mnist/train_mnist.py

にいって手書き数値の認識(MNIST)を実行。 実行時間約10min、一致率 0.982

6.6 Chainerの基本

6.6.7 MNISTデータ・セット読み込み

train 60,000要素  
test 10,000要素  

GPU: -1 #-1はGPUなし  
# unit: 1000 中間層のユニットの数  
# Minibatch-size: 100 ミニバッチサイズ
# epoch: 20 エポック数

実行した後フォルダ内にmlp.model,mlp.stateが記録されている

将棋AIで学ぶディープラーニングを読む

7-3 python-shogi

クラス Piece(object) 
    def __init__(self, piece_type,color)
        piece_typeとcolorを登録
    def symbol(self)
        実行例
        silver = shogi.Piece(shogi.SILVER,shogi.BLACK)
        silver.symbol()
        'S'
    def japanese_symbol(self)
        実行例
        silver = shogi.Piece(shogi.SILVER,shogi.BLACK)
        silver.japanese_symbol()
        '銀'
    def japanese_symbol_with_direction(self)
        実行例
        silver = shogi.Piece(shogi.SILVER,shogi.BLACK)
        silver. japanese_symbol_with_direction()
        ' 銀'
        silver_w = shogi.Piece(shogi.SILVER,shogi.WHITE)
        silver_w. japanese_symbol_with_direction()
        'v銀'
    def is_promoted(self)
        実行例
        silver = shogi.Piece(shogi.SILVER,shogi.BLACK)
        silver = shogi.is_promoted()
        False
        つまり成り駒かどうかを尋ねている
    def .__reduce__()
        実行例
        silver = shogi.Piece(shogi.SILVER,shogi.BLACK)
        silver = shogi.__reduce__()
        (<function copyreg._reconstructor>,
        (shogi.Piece.Piece, object, None),
        {'color': 0, 'piece_type': 4})

クラス Move(object)  Move.py
    def __init__(self, from_square, to_square, promotion=False, drop_piece_type=None)
        from_square座標
        to_square座標
        promotion 成り動作フラグ
       drop_piece_type 打つ手ならdrop_piece_typeを設定
      を登録
    def usi(self)
        usi表記の操作
    def __bool__(self)
        from_squareとto_squareが両方とも設定がない場合True
    def def __hash__(self)
        to_square,from_square,promotionを16bitぐらいの変数に圧縮して返す(promotionが何bitで表現されるのか不明)
    def from_usi(cls, usi)
        実行例
         m = shogi.Move(shogi.C5,shogi.D5)
        Move.from_usi('5c5d')
        m.from_usi('5c5d')

将棋AIで学ぶディープラーニングを読む

7-3 python-shogi

クラス Board(object) 
    Boardクラス(主クラス)
    def __init__(self, sfen=None)
         self.pseudo_legal_moves 着手生成クラスのインスタンス
         self.legal_moves 着手生成クラスのインスタンス
         sfen文字列で局面を与えられていなければ初期局面で初期化(reset関数)
         sfen文字列が与えられていればそれで局面を初期化

    def reset(self)
         __init__から呼ばれ局面を初期化する
         self.piece_bb 駒種ごとに座標のbitboardを登録しておく
         self.pieces_in_hand 持ち駒を管理する配列、カラーごとにある。pieces_in_hand[BLACKmWHITE]
         self.occupied 駒が存在するbitboard座標
         self.king_squares KINGが存在するSQUARES座標、KINGは特別扱い
         self.pieces[81] SQUARES座標ごとに駒種を記録している
         self.turn 手番 初期値はBLACK
         self.move_number 手数 増やすのはpush関数減らすのはpop関数
         self.captured_piece_stack push関数で取った駒をキューに登録している。pop関数で取り出して削除
         self.move_stack push関数で渡されたmoveインスタンスをキューに登録している。pop関数で取り出して削除
         self.incremental_zobrist_hash  zobristハッシュ値を保持
         self.transpositions 局面のzobristハッシュ値を記録しており同一のハッシュ値のカウンテングをしている。is_fourfold_repetition関数などで千日手の判定に利用している
     def clear(self)
         局面のクリアに使用set_sfen関数からのみ呼び出し
     def piece_at(self, square)
         指定したSQUARES座標からPieceインスタンスを返す
     def piece_type_at(self, square)
         指定したSQUARES座標から駒種を返す
     def add_piece_into_hand(self, piece_type, color, count=1)
         指定した駒種の持ち駒数を1増やす
     def remove_piece_from_hand(self, piece_type, color)
         指定した駒種の持ち駒数を1減らす
     def has_piece_in_hand(self, piece_type, color)
         指定した駒種、カラーの持ち駒があるかチエックする
     def remove_piece_at(self, square, into_hand=False)
         指定した座標にある駒を取り除く関数push,pop関数のヘルパー
     def set_piece_at(self, square, piece, from_hand=False, into_hand=False)
         指定した座標に指定した駒種をセットする関数push,pop関数のヘルパー
     def generate_pseudo_legal_moves(self, pawns=True, lances=True, knights=True, silvers=True, golds=True,
        bishops=True, rooks=True,
        kings=True,
        prom_pawns=True, prom_lances=True, prom_knights=True, prom_silvers=True, prom_bishops=True, prom_rooks=True,
        pawns_drop=True, lances_drop=True, knights_drop=True, silvers_drop=True, golds_drop=True,
        bishops_drop=True, rooks_drop=True):
         合法手を生成するジネレータ式なので呼び出す側(generate_legal_moves関数、PseudoLegalMoveGeneratorクラスの__iter__関数他)からnextを呼ばれるとそのつどMoveクラスを返す
     def is_attacked_by(self, color, square, piece_types=PIECE_TYPES)
         指定座標にATTACKをかけている駒があればTrueそうでなければFalse
     def attacker_mask(self, color, square)
         指定座標にATTACKをかけている駒の移動bitboardを返す
     def attackers(self, color, square)
         指定座標にATTACKをかけている移動bitboardで初期化したSquareSetクラスを返す
     def is_check(self)
         自KINGに王手がかかっていたらTrue 
     def attacks_from(piece_type, square, occupied, move_color)
         指定座標に指定駒種、指定カラーの駒の移動bitboardを返す
     def is_suicide_or_check_by_dropping_pawn(self, move)
         渡されたmoveインスタンスが死に駒、詰め王手でないかcheckしている。was_suicide関数,was_check_by_dropping_pawn関数をヘルパーとして呼んでいる
     def is_game_over(self)
         内部でgenerate_legal_moves関数を呼び出し合法手がなかったらゲームオーバーと判定
     def is_checkmate(self)
         チエックメイトでなければFalse
     def is_stalemate(self)
         ステイルメイトでなければFalse
     def is_fourfold_repetition(self)
         千日手判定、千日手であればTrue
     def is_double_pawn(self, to_square, piece_type)
         2歩判定、これから打つ手が2歩になるか判定、なるならTrue
     def push(self, move)
         moveインスタンスを使って局面を更新
     def pop(self)
         局面を戻す
     def peek(self)
         move_stackの最後の要素にアクセス
     def sfen(self)
         sfen文字列を使って局面を構築
     def set_sfen(self, sfen)
         初期局面を構築
     def push_usi(self, usi)
         usi表記の着手文字列をパースして局面を更新
     def kif_pieces_in_hand_str(self, color)
         '先手の持駒:'colorをBLACKにした場合
         '後手の持駒:'colorをWHITEにした場合
     def kif_str(self)
         '後手の持駒:\n  9 8 7 6 5 4 3 2 1\n+---------------------------+\n|v香v桂v銀v金v玉v金v銀v桂v香|一\n| ・v飛 ・ ・ ・ ・ ・v角 ・|二\n|v歩v歩v歩v歩v歩v歩v歩v歩v歩|三\n| ・ ・ ・ ・ ・ ・ ・ ・ ・|四\n| ・ ・ ・ ・ ・ ・ ・ ・ ・|五\n| ・ ・ ・ ・ ・ ・ ・ ・ ・|六\n| 歩 歩 歩 歩 歩 歩 歩 歩 歩|七\n| ・ 角 ・ ・ ・ ・ ・ 飛 ・|八\n| 香 桂 銀 金 玉 金 銀 桂 香|九\n+---------------------------+\n先手の持駒:'
     def __repr__(self)
     def def __str__(self)
          l  n  s  g  k  g  s  n  l
          .  r  .  .  .  .  .  b  .
          p  p  p  p  p  p  p  p  p
          .  .  .  .  .  .  .  .  .
          .  .  .  .  .  .  .  .  .
          .  .  .  .  .  .  .  .  .
          P  P  P  P  P  P  P  P  P
          .  B  .  .  .  .  .  R  .
          L  N  S  G  K  G  S  N  L
     def __eq__(self, board)
         borad同士を比較する
     def __ne__(self, board)
         bitboard同士 or piece_bb同士 or pieces_in_hand同士  or turn同士 or move_number同士が不一致ならTrue
     def zobrist_hash(self, array=None)
         呼び出された時点でのzobrist_hash値を返す。
     def board_zobrist_hash(self, array=None)
         局面上の駒の配置で決まるzobrist_hash値を返す

クラス PseudoLegalMoveGenerator(object) 
     def __init__(self, board)
     def __bool__(self)
         board.generate_pseudo_legal_movesを呼び出し着手可能であればTrue
     der __iter__(self)
         board.generate_pseudo_legal_moves関数を呼び出し着手を返す
     def __contains__(self, move)
         board.is_pseudo_legal(move)を呼び出し引数のmoveが合法手かチエックしている

クラス SquareSet(object) 
     SquareSetクラスは移動bitboardを渡される
     def __init__(self, mask)
     def __len__(self)
         移動bitboardの1が立っている数を数えて返す
     def __iter__(self)
         移動bitboardをスキャンしてSquare座標に変換して返すジネレータ関数なので呼び出す側がnextで呼んで始めて返す
     def __contains__(self, square)
         指定したsquare座標が移動bitboardに含まれているかチエックして含まれていればTrueを返す

将棋AIで学ぶディープラーニングを読む

7-3 python-shogi

駒移動(bitboard)
def shogi.shift_down(b)
    bitboardを引数にとり、全てのbitを1rank下げた(BLACK側から見て)bitboardを返す。引数のbitboardには影響はない。
def shigi.shift_2_down(b)
    bitboardを引数にとり、全てのbitを2rank下げた(BLACK側から見て)bitboardを返す。引数のbitboardには影響はない。
def shogi.shift_up(b)
    bitboardを引数にとり、全てのbitを1rank上にあげるた(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_2_up(b)
    bitboardを引数にとり、全てのbitを2rank上にあげるた(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_right(b)
    bitboardを引数にとり、全てのbitを1bit右に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_2_right(b)
    bitboardを引数にとり、全てのbitを2bit右に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_left(b)
    bitboardを引数にとり、全てのbitを1bit左に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_2_left(b)
    bitboardを引数にとり、全てのbitを2bit左に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_up_left(b)
    bitboardを引数にとり、全てのbitを左上に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_up_right(b)
    bitboardを引数にとり、全てのbitを右上に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_down_left(b)
    bitboardを引数にとり、全てのbitを左下に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。
def shogi.shift_down_right(b)
    bitboardを引数にとり、全てのbitを右下に動かした(BLACK側から見て)bitboardを返す。引数のbitboardに影響はない。

駒成り判定
def can_promote(square, piece_type, color)
    指定座標、指定駒種、指定カラーでなることが可能であればTrueそうでなければFalse
def can_move_without_promotion(to_square, piece_type, color)
    piece_typeがPAWN,LANCE,KNIGHTについて移動先での死に駒判定を行っている。死に駒になるならFalseそうでなければTrue。

クラス Occupled(object) 
    Boardクラスの下でbitboardを管理している
    def __init__(self, occupied_by_black, occupied_by_white)
        self.by_color=black側,white側のbitboardを配列に入れている
        self.bits=black,white両方を1つのbitboardで管理している
        self.l90=左90度回転のbitboardを管理
        self.r45=右45度回転のbitboardを管理
        self.l45=左45度回転のbitboardを管理
    def __getitem__(self,key)
        Occupled[color]そのカラーのbitboardを返す
    def ixor(self,mask,color,square)
        maskで指定されたbitboardとOccupledで管理しているbitboardを排他bit演算してbitboardを更新させる
    def non_occupled(self)
        空いている座標をbitを立てたbitboardを返す
    def __eq__(self,occupled)
        bitboard同士を比較して同じであればTrueを返す
    def __ne__(self,occupled)
        bitboard同士を比較して異なればTrueを返す。等しければFalseを返す
    def __repr__(self)
         by_color配列をプリントする(10進数)
         print(bd.occupied)
         Occupied([2414337516468818170347520, 134022655])

将棋AIで学ぶディープラーニングを読む

7-3 python-shogi

fileが列、rankが行を表現する

for sq in shogi.SQUARES:
    print(shogi.file_index(sq),end="")
    if (sq+1) % 9 == 0:
        print("")
print("")
for sq in shogi.SQUARES:
    print(shogi.rank_index(sq),end="")
    if (sq+1) % 9 == 0:
        print("")

shogi.file_indexに座標を渡すと所属している列を返してくれる  
012345678  
012345678
012345678
012345678
012345678
012345678
012345678
012345678
012345678
#shogi.rank_indexに座標を渡すと所属する行を返してくれる
000000000
111111111
222222222
333333333
444444444
555555555
666666666
777777777
888888888

bitboardの全体に掛けるmaskと思われる
print(hex(shogi.BB_VOID))
#81 bit
print(hex(shogi.BB_ALL))

BB_SQUARESは座標ごと固有のbitboardを表している
bb=shogi.BB_SQUARES
for index,b in enumerate(bb):
    print("{0:>08x}  ".format(b),end="")
    if (index+1) % 9 == 0:
        print("")
結果
A rank 00000001  00000002  00000004  00000008  00000010  00000020  00000040  00000080  00000100  
B rank 00000200  00000400  00000800  00001000  00002000  00004000  00008000  00010000  00020000  
.....

BB_SQUARES_L90はBB_SQUARES盤を左90度回転させた配置  
BB_SQUARES_L45は左45度回転させた配置  
BB_SQUARES_R45は右45度回転させた配置  

BB_FILESはbitboardの列座標を検出できるmask
print(hex(shogi.BB_FILES[0]))
0x1008040201008040201

      file9  
A    0x01  
B    0x200  
C    0x40000  
D    0x8000000  
E    0x1000000000  
F    0x200000000000  
G    0x40000000000000  
H    0x8000000000000000  
I     0x1000000000000000000  
BB_RABKSは行座標を検出できる

将棋AIで学ぶディープラーニングを読む

7-3 python-shogi

pydlshogiは基本的なところはpython-shogiに依存している。

python-shogiはpythonだけで記述されたshogi用のライブラリ。

github.com

ファイル構成と主な役割
init.py:主な定数、変数、共通関数、主要なクラス
Consts.py その他定数
CAS.py CAS棋譜パースなど
KIF.py 棋譜パース
Move.py Moveクラス
Piece.py Pieceクラス

init.pyを頭から呼んでいくjupyter notebookに
shogiライブラリをimportしておく

print(shogi.PIECE_TYPES_WITHOUT_KING)

[1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14]
駒種の定義をしている配列のようである
PAWN:1
LANCE:2
KNIGHT:3
SILVER:4
GOLD:5
BISHOP:6
ROOK:7
PROM_PAWN:9
PROM_LANCE:10
PROM_KNIGHT:11
PROM_SILVER:12
PROM_BISHOP:13
PROM_ROOK:14
KING(8)はこの配列からはずしてあるようだ

print(shogi.MAX_PIECES_IN_HAND)

[0, 18, 4, 4, 4, 4, 2, 2, 0, 0, 0, 0, 0, 0, 0]
PAWNの最大持ち駒数:18枚
LANCEの最大持ち駒数:4枚
KNIGHTの最大持ち駒数:4枚
SILVERの最大持ち駒数:4枚
GOLDの最大持ち駒数:4枚
BISHOPの最大持ち駒数:2枚
ROOKの最大持ち駒数:2枚

print(shogi.PIECE_PROMOTED)

[None, 9, 10, 11, 12, None, 13, 14, None, None, None, None, None, None, None]
成り駒の駒種の配列
add_piece_into_hand,remove_piece_from_hand,has_piece_in_hand,push,pop関数で使われている。
成り駒を取った場合通常駒に変換してから持ち駒配列に入れる必要があるのでこの配列で変換している
PROM_PAWN:9->PAWN:1
PROM_LANCE:10->LANCE:2
PROM_KNIGHT:11->KNIGHT:3
PROM_SILVER:12->SILVER:4
PROM_BISHOP:13->BISHOP:6
PROM_ROOK:14->ROOK:7
通常駒種コード+8 = 成り駒コードではないので配列による変化にしているのかな

print(shogi.NUMBER_JAPANESE_NUMBER_SYMBOLS)

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
全角数字

print(shogi.NUMBER_JAPANESE_KANJI_SYMBOLS)

['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八']
漢数字

print(shogi.SQUARES)
    9  8  7  6  5  4  3  2  1  
 A  0  1  2  3  4  5  6  7  8  
 B  9 10 11 12 13 14 15 16 17  
 C 18 19 20 21 22 23 24 25 26  
 D 27 28 29 30 31 32 33 34 35  
 E 36 27 28 39 40 41 42 43 44  
 F 45 46 47 48 49 50 51 52 53  
 G 54 55 56 57 58 59 60 61 62  
 H 63 64 65 66 67 68 69 70 71  
 I  72 73 74 75 76 77 78 79 80  

盤の表示

print(shogi.SQUARES_L90)
SQUARES_L90 = [  
A1, B1, C1, D1, E1, F1, G1, H1, I1,  
A2, B2, C2, D2, E2, F2, G2, H2, I2,  
A3, B3, C3, D3, E3, F3, G3, H3, I3,  
A4, B4, C4, D4, E4, F4, G4, H4, I4,  
A5, B5, C5, D5, E5, F5, G5, H5, I5,  
A6, B6, C6, D6, E6, F6, G6, H6, I6,  
A7, B7, C7, D7, E7, F7, G7, H7, I7,  
A8, B8, C8, D8, E8, F8, G8, H8, I8,  
A9, B9, C9, D9, E9, F9, G9, H9, I9,  
]  

SQUARES盤を左90度回転させた配置

SQUARES_R45は右45度回転させた配置
SQUARES_L45は左45度回転させた配置