본문 바로가기
바둑 ai

[바둑 AI 프로젝트 3일차] 데이터 구하는 곳 + MCTS

by Tiabet 2024. 2. 20.

최근에 바빠서 바둑 AI는 신경을 많이 못 쓰고 있다. 그래도 오늘 새로 공부한 내용들을 정리하고자 한다.

 

데이터 구하는 곳

처음엔 내가 바둑팝에서 저장한 기보들을 사용하려고 했는데 아무리 생각해도 이걸론 턱없이 부족할 것 같아서 데이터를 조금 더 찾아봤다. 그랬더니 바둑 공부하기도 굉장히 유용한 사이트들을 몇 개 찾았다.

 

https://online-go.com/

 

online-go.com에서 바둑을 즐기세요! | OGS

Online-Go.com은 온라인 바둑 게임을 즐길 수 있는 최고의 사이트입니다. 저희 커뮤니티 지원 사이트는 사용이 친근하고 편리하며 무료입니다, 오셔서 저희와 함께 바둑을 즐기세요!

online-go.com

online-go, 온라인 고라는 간단한 이름을 가진 전세계적인 바둑 사이트이다. 고급 기능을 제외하면 무료인 점도 아주 마음에 들고 인터페이스도 깔끔하다.

홈페이지 메인

여기 들어가면 이미 진행된 대국들까지 기보를 모두 확인할 수 있다.

둔 사람들의 급수도 뜨기 때문에 아주 유용하게 사용할 수 있을 것 같다.

 

MCTS

알파고가 만들어진 방식을 살펴보다 보니 딥러닝(강화학습) 외에도 MCTS 가 중요한 원리로 작용했다는 것을 알게 됐다. MCTS는 몬테카를로 트리탐색(Monte Carlo Tree Search)이라고 한다.

 

작용하는 방식은 간단히 정리하면 아래와 같다. (출처 : ChatGPT)

 

선택: 루트 노드에서 시작하여 리프 노드에 도달할 때까지 하위 노드를 선택합니다. 선택은 일반적으로 새로운 노드 탐색과 알려진 양호한 노드 활용의 균형을 맞추는 전략에 기반하며, 종종 상위 신뢰 구간(UCB1) 공식을 사용합니다.

확장: 게임이 끝나지 않는 한, 하나 이상의 하위 노드를 생성하고 그 중 하나를 선택합니다.

시뮬레이션: 새 노드에서 무작위 이동이나 가벼운 정책을 사용하여 시뮬레이션된 게임을 끝까지 플레이합니다.

역전파: 시뮬레이션 결과를 사용하여 루트에서 선택한 노드까지의 경로에 있는 노드의 정보를 업데이트합니다.

 

이것만 읽어서는 도무지 감이 오지 않는다. 코드는 다음과 같았다.

 

import random

class Node:
    def __init__(self, game_state, parent=None):
        self.game_state = game_state
        self.parent = parent
        self.children = []
        self.wins = 0
        self.visits = 0
        self.untried_actions = self.game_state.get_legal_actions()
    
    def UCB1(self, total_visits, c_param=1.41):
        if self.visits == 0:
            return float('inf')  # to ensure unvisited nodes are prioritized
        return self.wins / self.visits + c_param * (np.log(total_visits) / self.visits) ** 0.5

    def select_child(self):
        # Select a child node with highest UCB1 score
        return max(self.children, key=lambda x: x.UCB1(self.visits))

    def add_child(self, action, game_state):
        # Remove the action from untried and create a new child node for this action
        self.untried_actions.remove(action)
        child_node = Node(game_state=game_state, parent=self)
        self.children.append(child_node)
        return child_node

    def update(self, result):
        # Update this node - increment the visit count and update the win count based on the result
        self.visits += 1
        self.wins += result

def MCTS(root, iterations=1000):
    for _ in range(iterations):
        node = root
        # Selection
        while node.untried_actions == [] and node.children != []:
            node = node.select_child()
        
        # Expansion
        if node.untried_actions != []:
            action = random.choice(node.untried_actions)
            next_state = node.game_state.next_state(action)
            node = node.add_child(action, next_state)
        
        # Simulation
        current_state = node.game_state
        while not current_state.is_game_over():
            possible_moves = current_state.get_legal_actions()
            action = random.choice(possible_moves)
            current_state = current_state.next_state(action)
        
        # Backpropagation
        while node is not None:
            node.update(current_state.game_result(node.game_state.player))  # Assuming game_result returns a result relative to player
            node = node.parent

# This is a placeholder for your game state class
class GameState:
    def get_legal_actions(self):
        # Return a list of all legal actions from this state
        pass
    
    def next_state(self, action):
        # Return the new GameState after applying the action
        pass
    
    def is_game_over(self):
        # Return True if the game is over, False otherwise
        pass
    
    def game_result(self, player):
        # Return the game result from the perspective of the given player
        pass

# Example usage
# You would need to implement GameState specific to your game for this to work
# root_state = GameState(initial_configuration)
# root_node = Node(game_state=root_state)
# MCTS(root_node)

이런 식으로 객체들을 만들어서 실행함을 알 수 있었다. 다른 자료들을 보면서 같이 공부한 다음에 다시 정리해야겠다.

 

 

 

추가로 프로젝트를 진행하다보니 목표에 대해 고민이 생겼다. 처음엔 내 수준에 맞는 초급자 수준의 AI를 만드려고 했는데, 나는 이게 단순히 초급 기보들만 학습시키면 되는 줄 알았다. 하지만 아무튼 인공지능은 더 성능을 높이고자 하는 쪽으로 계산해나가기 때문에 모델을 좀 더 단순하게 만들고 학습률을 떨어트리는 식으로 진행해야 할 것 같다. 그리고 가장 중요한 것이 내 실력이 알고 보니 초급자가 아니었다. (앞서 소개한 사이트에서 측정한 결과 4~5급 정도의 수준으로 나옴) 바둑이 너무 재밌어서 많이 두다 보니 실력이 너무 올라버린 것 같다. 그런데 내가 주로 9x9 바둑만 두어서 19x19 바둑으로 옮겨가면 실력이 수직하락한다. 그래서 목표를 내가 즐겨두는 9x9 바둑 AI를 내 실력과 비슷한 수준으로 만드는 것으로 수정해야 할 것 같다.