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


3 super(ValueNet, self).__init__()
4 self.outplanes = outplanes
5 self.conv = nn.Conv2d(inplanes, 1, kernel_size=1)
6 self.bn = nn.BatchNorm2d(1)
7 self.fc1 = nn.Linear(outplanes - 1, 256)
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) 最大化,的走法 。
写成代码的话——
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) / (1nodes[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.wv
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探索更多可能的选择 。

推荐阅读