최근에 바빠서 바둑 AI는 신경을 많이 못 쓰고 있다. 그래도 오늘 새로 공부한 내용들을 정리하고자 한다.
데이터 구하는 곳
처음엔 내가 바둑팝에서 저장한 기보들을 사용하려고 했는데 아무리 생각해도 이걸론 턱없이 부족할 것 같아서 데이터를 조금 더 찾아봤다. 그랬더니 바둑 공부하기도 굉장히 유용한 사이트들을 몇 개 찾았다.
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를 내 실력과 비슷한 수준으로 만드는 것으로 수정해야 할 것 같다.
'바둑 ai' 카테고리의 다른 글
[바둑 ai 만들기 프로젝트] 2일차 - alphago 관련 논문 정리 (0) | 2024.02.02 |
---|---|
[바둑 AI 만들기 프로젝트] 1일차 - 주제 설정 (4) | 2024.01.26 |