"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > DQN을 사용하여 체스 에이전트 구축

DQN을 사용하여 체스 에이전트 구축

2025-03-24에 게시되었습니다
검색:858

최근에 DQN 기반 체스 에이전트를 구현하려고했습니다.

이제 DQNS와 Chess가 어떻게 작용하는지 아는 사람이라면 누구나 그것이 바보 같은 아이디어라고 말할 것입니다.

그리고 ... 그것은 그랬지만 초보자로서 나는 그럼에도 불구하고 그것을 즐겼습니다. 이 기사에서는이 작업을 수행하는 동안 배운 통찰력을 공유 할 것입니다.


환경 이해.

에이전트 자체를 구현하기 전에 사용하는 환경에 익숙해지고 훈련 중에 에이전트와 상호 작용할 수 있도록 사용자 정의 래퍼를 만들어야했습니다.

  • 나는 Kaggle_environmentments 라이브러리에서 체스 환경을 사용했습니다.

     from kaggle_environments import make
     env = make("chess", debug=True)
    
  • 나는 또한 체스 게임을 구문 분석하고 검증하는 데 도움이되는 가벼운 파이썬 라이브러리 인 Chessnut도 사용했습니다.

     from Chessnut import Game
     initial_fen = env.state[0]['observation']['board']
     game=Game(env.state[0]['observation']['board'])
    
  • game = game (Env.state [0]

))

Building a Chess Agent using DQN

이 환경에서 보드 상태는 FEN 형식으로 저장됩니다.


보드의 모든 조각과 현재 활성 플레이어를 나타내는 소형 방법을 제공합니다. 그러나 신경망에 입력을 공급할 계획 이후 상태의 표현을 수정해야했습니다.

Building a Chess Agent using DQN

펜을 매트릭스 형식으로 변환합니다


보드에 12 가지 유형의 조각이 있기 때문에 보드의 각 유형의 상태를 나타내는 8x8 그리드의 12 개의 채널을 만들었습니다.

class EnvCust:
    def __init__(self):
        self.env = make("chess", debug=True)
        self.game=Game(env.state[0]['observation']['board'])
        print(self.env.state[0]['observation']['board'])
        self.action_space=game.get_moves();
        self.obs_space=(self.env.state[0]['observation']['board'])

    def get_action(self):
        return Game(self.env.state[0]['observation']['board']).get_moves();


    def get_obs_space(self):
        return fen_to_board(self.env.state[0]['observation']['board'])

    def step(self,action):
        reward=0
        g=Game(self.env.state[0]['observation']['board']);
        if(g.board.get_piece(Game.xy2i(action[2:4]))=='q'):
            reward=7
        elif g.board.get_piece(Game.xy2i(action[2:4]))=='n' or g.board.get_piece(Game.xy2i(action[2:4]))=='b' or g.board.get_piece(Game.xy2i(action[2:4]))=='r':
            reward=4
        elif g.board.get_piece(Game.xy2i(action[2:4]))=='P':
            reward=2
        g=Game(self.env.state[0]['observation']['board']);
        g.apply_move(action)
        done=False
        if(g.status==2):
            done=True
            reward=10
        elif g.status == 1:  
            done = True
            reward = -5 
        self.env.step([action,'None'])
        self.action_space=list(self.get_action())
        if(self.action_space==[]):
            done=True
        else:
            self.env.step(['None',random.choice(self.action_space)])
            g=Game(self.env.state[0]['observation']['board']);
            if g.status==2:
                reward=-10
                done=True

        self.action_space=list(self.get_action())
        return self.env.state[0]['observation']['board'],reward,done

클래스 Envcust : def __init __ (self) : self.env = make ( "체스", 디버그 = true) self.game = game (Env.state [0]

))) print (self.env.state [0] )) self.action_space = game.get_moves (); self.obs_space = (self.env.state [0]

))) def get_action (self) : 반환 게임 (self.env.state [0] ). get_moves (); def get_obs_space (self) : return fen_to_board (self.env.state [0]


)) DEF 단계 (자기, 행동) : 보상 = 0 g = game (self.env.state [0]

); if (g.board.get_piece (game.xy2i (action [2 : 4]) == 'q') : 보상 = 7 elif g.board.get_piece (game.xy2i (action [2 : 4]) == 'n'또는 g.board.get_piece (game.xy2i (action [2 : 4]) == 'b'또는 g.board.get_piece (game.xy2i (action [2 : 4]) == 'r': 보상 = 4 elif g.board.get_piece (game.xy2i (action [2 : 4]) == 'p': 보상 = 2 g = game (self.env.state [0]

Building a Chess Agent using DQN); g.apply_move (액션) 완료 = 거짓 if (g.status == 2) : 완료 = 참 보상 = 10 elif g.status == 1 : 완료 = 참 보상 = -5 self.env.step ([action, 'none']) self.action_space = list (self.get_action ()) if (self.action_space == []) : 완료 = 참 또 다른: self.env.step (

))) g = game (self.env.state [0]

); g.status == 2 인 경우 : 보상 = -10 완료 = 참 self.action_space = list (self.get_action ()) self.env.state [0]

, 보상, 완료

이 래퍼의 요점은 에이전트에 대한 보상 정책과 훈련 중에 환경과 상호 작용하는 데 사용되는 단계 함수를 제공하는 것이 었습니다.

Building a Chess Agent using DQN

나는 체크 메이트에게 긍정적 인 포인트를주고 적의 조각을 꺼내는 동안 게임을 잃어버린 부정적인 포인트를 가져 오는 보상 정책을 만들려고 노력했습니다. Building a Chess Agent using DQN

리플레이 버퍼 생성


보조 기능

import torch
import torch.nn as nn
import torch.optim as optim

class DQN(nn.Module):
    def __init__(self):  
        super(DQN, self).__init__()
        self.conv_layers = nn.Sequential(
            nn.Conv2d(12, 32, kernel_size=3, stride=1, padding=
            nn.ReLU(),  
            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),  
            nn.ReLU()
        )
        self.fc_layers = nn.Sequential(
            nn.Flatten(),
            nn.Linear(64 * 8 * 8, 256),
            nn.ReLU(), 
            nn.Linear(256, 128),
            nn.ReLU(), 
            nn.Linear(128, 4096)
        )

    def forward(self, x):
        x = x.unsqueeze(0)
        x = self.conv_layers(x)  
        x = self.fc_layers(x)  
        return x

    def predict(self, state, valid_action_indices):
        with torch.no_grad(): 
            q_values = self.forward(state) 
            q_values = q_values.squeeze(0)  


            valid_q_values = q_values[valid_action_indices]


            best_action_relative_index = valid_q_values.argmax().item()  
            max_q_value=valid_q_values.argmax()
            best_action_index = valid_action_indices[best_action_relative_index] 

            return max_q_value, best_action_index


Chessnut는 'A2A3'처럼 보이는 UCI 형식으로 법적 조치를 반환하지만 신경망과 상호 작용하기 위해 기본 패턴을 사용하여 각 동작을 별개의 색인으로 변환했습니다. 총 64 개의 사각형이 있으므로 각 움직임마다 64*64 개의 고유 인덱스를 갖기로 결정했습니다.



나는 64*64 동작이 모두 합법적이지 않다는 것을 알고 있지만 Chessnut을 사용하여 합법성을 처리 할 수 ​​있었고 패턴은 충분히 간단했습니다.

model = DQN().to(device)  # The current Q-network
target_network = DQN().to(device)  # The target Q-network
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
replay_buffer = ReplayBuffer(buffer_size=10000)
epsilon = 0.5  
gamma = 0.99 
batch_size=15
def train(episodes):
    for ep in range(1,episodes 1):
        print('Episode Number:',ep)
        myenv=EnvCust()
        done=False
        state=myenv.obs_space
        i=0
        while not done and i batch_size:
                mini_batch = replay_buffer.sample(batch_size)
                for e in mini_batch:
                    state, action, reward, next_state, done = e
                    g=Game(next_state)
                    act=g.get_moves();
                    ind_a=action_index(act)
                    input_state=torch.tensor(fen_to_board(next_state), dtype=torch.float32, requires_grad=True).to(device)
                    tpred,_=target_network.predict(input_state,ind_a)
                    target = reward   gamma * tpred * (1 - done)

                    act_ind=uci_to_action_index(action)
                    input_state2=torch.tensor(fen_to_board(state), dtype=torch.float32, requires_grad=True).to(device)
                    current_q_value =model(input_state2)[0,act_ind]

                    loss = (current_q_value - target) ** 2
                    optimizer.zero_grad()
                    loss.backward()
                    optimizer.step()
            if ep % 5 == 0:
                target_network.load_state_dict(model.state_dict())



신경망 구조 토치 가져 오기 Torch.nn을 nn으로 가져옵니다 Torch.optim을 최적으로 가져옵니다 클래스 DQN (NN.Module) : def __init __ (self) : Super (dqn, self) .__ init __ () self.conv_layers = nn.eseverential ( nn.conv2d (12, 32, kernel_size = 3, stride = 1, padding = nn.relu (), nn.conv2d (32, 64, kernel_size = 3, stride = 1, padding = 1), nn.relu () )) self.fc_layers = nn.eseverential ( nn.flatten (), nn.linear (64 * 8 * 8, 256), nn.relu (), Nn.Linear (256, 128), nn.relu (), Nn.Linear (128, 4096) )) def forward (self, x) : x = x.unsqueeze (0) x = self.conv_layers (x) x = self.fc_layers (x) 반환 x def predict (self, state, valid_action_indices) : Torch.no_grad ()로 : q_values ​​= self.forward (state) q_values ​​= q_values.squeeze (0) valid_q_values ​​= q_values ​​[valid_action_indices] best_action_relative_index = valid_q_values.argmax (). item () max_q_value = valid_q_values.argmax () best_action_index = valid_action_indices [best_action_relative_index] max_q_value, best_action_index를 반환합니다

Building a Chess Agent using DQN 에이전트 구현

model = dqn (). to (장치) # 현재 q-network target_network = dqn (). to (장치) # target q-network Optimizer = Torch.optim.adam (model.parameters (), lr = 1e-4) Replay_Buffer = ReplayBuffer (buffer_size = 10000) 엡실론 = 0.5 감마 = 0.99 batch_size = 15 데프 트레인 (에피소드) : 범위의 EP의 경우 (1, 에피소드 1) : print ( '에피소드 번호 :', ep) myenv = envcust () 완료 = 거짓 state = myenv.obs_space i = 0 끝나지 않고 I batch_size : mini_batch = replay_buffer.sample (batch_size) mini_batch의 e : 상태, 행동, 보상, next_state, done = e g = 게임 (next_state) act = g.get_moves (); ind_a = action_index (act) input_state = torch.tensor (fen_to_board (next_state), dtype = torch.float32, require_grad = true) .to (장치) tpred, _ = target_network.predict (input_state, ind_a) 대상 = 보상 감마 * tpred * (1- 완료) act_ind = uci_to_action_index (action) input_state2 = torch.tensor (fen_to_board (state), dtype = torch.float32, require_grad = true) .to (장치) current_q_value = model (input_state2) [0, act_ind] 손실 = (current_q_value -target) ** 2 Optimizer.zero_grad () loss.backward () Optimizer.step () ep % 5 == 0 인 경우 : Target_network.load_state_dict (model.state_dict ())

이것은 분명히 실제로 잘 수행 할 기회가없는 매우 기본적인 모델 이었지만 DQN이 어떻게 더 잘 작동하는지 이해하는 데 도움이되었습니다.


    릴리스 선언문 이 기사는 https://dev.to/ankit_upadhyay_1c38ae52c0/building-achess-agent-using-dqn-40po?
    최신 튜토리얼 더>

    부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

    Copyright© 2022 湘ICP备2022001581号-3