自学围棋的AlphaGo Zero,你也可以造一个( 二 )


8 self.fc2 = nn.Linear(256, 1)
9
10
11 def forward(self, x):
12 x = F.relu(self.bn(self.conv(x)))
13 x = x.view(-1, self.outplanes - 1)
14 x = F.relu(self.fc1(x))
15 winning = F.tanh(self.fc2(x))
16 return winning
未雨绸缪的树
狗零,还有一个很主要的组成部分,就是蒙特卡洛树搜索 (MCTS)。
它可以让AI棋手提前找出,胜率最高的落子点 。
在模仿器里,模仿对方的下一手,以及再下一手,给出应对之策,所以提前的远不止是一步 。
节点 (Node)
树上的每一个节点,都代表一种不同的局面,有不同的统计数据:
每个节点被经过的次数n,总动作值w,经过这一点的先验概率p,平均动作值q (q=w/n) ,还有从别处来到这个节点走的那一步,以及从这个节点动身、所有可能的下一步 。
1class Node:
2 def __init__(self, parent=None, proba=None, move=None):
3 self.p = proba
4 self.n = 0
5 self.w = 0
6 self.q = 0
7 self.children = []
8 self.parent = parent
9 self.move = move
安排 (Rollout)
第一步是PUCT (多项式上置信树) 算法,选择能让PUCT函数 (下图) 的某个变体 (Variant) 最大化,的走法 。

自学围棋的AlphaGo Zero,你也可以造一个

文章插图
 写成代码的话——
1def select(nodes, c_puct=C_PUCT):
2 " Optimized version of the selection based of the PUCT formula "
3
4 total_count = 0
5 for i in range(nodes.shape[0]):
6 total_count += nodes[i][1]
7
8 action_scores = np.zeros(nodes.shape[0])
9 for i in range(nodes.shape[0]):
10 action_scores[i] = nodes[i][0] + c_puct * nodes[i][2] * \
11 (np.sqrt(total_count) / (1 + nodes[i][1]))
12
13 equals = np.where(action_scores == np.max(action_scores))[0]
14 if equals.shape[0] > 0:
15 return np.random.choice(equals)
16 return equals[0]
停止 (Ending)
选择在不停地进行,直至达到一个叶节点 (Leaf Node) ,而这个节点还没有往下生枝 。
1def is_leaf(self):
2 """ Check whether a node is a leaf or not """
3
4 return len(self.children) == 0
到了叶节点,那里的一个随机状况就会被评估,得出所有“下一步”的概率 。
所有被禁的落子点,概率会变成零,然后重新把总概率归为1 。
然后,这个叶节点就会生出枝节 (都是可以落子的地位,概率不为零的那些)。代码如下——
1def expand(self, probas):
2 self.children = [Node(parent=self, move=idx, proba=probas[idx]) \
3 for idx in range(probas.shape[0]) if probas[idx] > 0]
更新一下
枝节生好之后,这个叶节点和它的妈妈们,身上的统计数据都会更新,用的是下面这两串代码 。
1def update(self, v):
2 """ Update the node statistics after a rollout """
3
4 self.w = self.w + v
5 self.q = self.w / self.n if self.n > 0 else 0
1while current_node.parent:
2 current_node.update(v)
3 current_node = current_node.parent
选择落子点
模仿器搭好了,每个可能的“下一步”,都有了自己的统计数据 。
依照这些数据,算法会选择其中一步,真要落子的处所 。
选择有两种,一就是选择被模仿的次数最多的点 。试用于测试和实战 。
另外一种,随机 (Stochastically) 选择,把节点被经过的次数转换成概率散布,用的是以下代码——
1 total = np.sum(action_scores)
2 probas = action_scores / total
3 move = np.random.choice(action_scores.shape[0], p=probas)
后者实用于训练,让AlphaGo摸索更多可能的选择 。
三位一体的修炼
狗零的修炼分为三个进程,是异步的 。
一是自对弈 (Self-Play) ,用来生成数据 。
1def self_play():
2 while True:
3 new_player, checkpoint = load_player()
4 if new_player:
5 player = new_player
6
7 ## Create the self-play match queue of processes
8 results = create_matches(player, cores=PARALLEL_SELF_PLAY,
9 match_number=SELF_PLAY_MATCH)
10 for _ in range(SELF_PLAY_MATCH):
11 result = results.get()
12 db.insert({
13 "game": result,
14 "id": game_id
15 })
16 game_id += 1
二是训练 (Training) ,拿新颖生成的数据,来改良当前的神经网络 。


推荐阅读