酷知网经验知识百科全书!
你好!请登录
登录
WPS官网 WPS下载 telegram下载 telegram下载 telegram官网 WhatsApp網頁版登錄 WhatsApp下载 WhatsApp网页版登录

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

829次浏览 | 2026.03.29 23:50:02 更新
来源 :互联网
最佳经验
本文由作者推荐
  • 01

    遥想当年,AlphaGo的Master版本,在完胜柯洁九段之后不久,就被后辈AlphaGo Zero (简称狗零) 击溃了。

    从一只完全不懂围棋的AI,到打败Master,狗零只用了21天。

    而且,它不需要用人类知识来喂养,成为顶尖棋手全靠自学。

    如果能培育这样一只AI,即便自己不会下棋,也可以很骄傲吧。

    于是,来自巴黎的少年Dylan Djian (简称小笛) ,就照着狗零的论文去实现了一下。

    他给自己的AI棋手起名SuperGo,也提供了代码 (传送门见文底) 。

    除此之外,还有教程——

    一个身子两个头

    智能体分成三个部分:

    一是特征提取器 (Feature Extractor) ,二是策略网络 (Policy Network) ,三是价值网络(Value Network) 。

    于是,狗零也被亲切地称为“双头怪”。特征提取器是身子,其他两个网络是脑子。

    特征提取器

    特征提取模型,是个残差网络 (ResNet) ,就是给普通CNN加上了跳层连接 (Skip Connection) , 让梯度的传播更加通畅。

    跳跃的样子,写成代码就是:

    1class BasicBlock(nn.Module):

    2 """

    3 Basic residual block with 2 convolutions and a skip connection

    4 before the last ReLU activation.

    5 """

    6

    7 def __init__(self, inplanes, planes, stride=1, downsample=None):

    8 super(BasicBlock, self).__init__()

    9

    10 self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=3,

    11 stride=stride, padding=1, bias=False)

    12 self.bn1 = nn.BatchNorm2d(planes)

    13

    14 self.conv2 = nn.Conv2d(planes, planes, kernel_size=3,

    15 stride=stride, padding=1, bias=False)

    16 self.bn2 = nn.BatchNorm2d(planes)

    17

    18

    19 def forward(self, x):

    20 residual = x

    21

    22 out = self.conv1(x)

    23 out = F.relu(self.bn1(out))

    24

    25 out = self.conv2(out)

    26 out = self.bn2(out)

    27

    28 out += residual

    29 out = F.relu(out)

    30

    31 return out

    然后,把它加到特征提取模型里面去:

    1class Extractor(nn.Module):

    2 def __init__(self, inplanes, outplanes):

    3 super(Extractor, self).__init__()

    4 self.conv1 = nn.Conv2d(inplanes, outplanes, stride=1,

    5 kernel_size=3, padding=1, bias=False)

    6 self.bn1 = nn.BatchNorm2d(outplanes)

    7

    8 for block in range(BLOCKS):

    9 setattr(self, "res{}".format(block), \

    10 BasicBlock(outplanes, outplanes))

    11

    12

    13 def forward(self, x):

    14 x = F.relu(self.bn1(self.conv1(x)))

    15 for block in range(BLOCKS - 1):

    16 x = getattr(self, "res{}".format(block))(x)

    17

    18 feature_maps = getattr(self, "res{}".format(BLOCKS - 1))(x)

    19 return feature_maps

    策略网络

    策略网络就是普通的CNN了,里面有个批量标准化 (Batch Normalization) ,还有一个全连接层,输出概率分布。

     

    1class PolicyNet(nn.Module):

    2 def __init__(self, inplanes, outplanes):

    3 super(PolicyNet, self).__init__()

    4 self.outplanes = outplanes

    5 self.conv = nn.Conv2d(inplanes, 1, kernel_size=1)

    6 self.bn = nn.BatchNorm2d(1)

    7 self.logsoftmax = nn.LogSoftmax(dim=1)

    8 self.fc = nn.Linear(outplanes - 1, outplanes)

    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 = self.fc(x)

    15 probas = self.logsoftmax(x).exp()

    16

    17 return probas

    价值网络

    这个网络稍微复杂一点。除了标配之外,还要再多加一个全连接层。最后,用双曲正切 (Hyperbolic Tangent) 算出 (-1,1) 之间的数值,来表示当前状态下的赢面多大。

    代码长这样——

    1class ValueNet(nn.Module):

    2 def __init__(self, inplanes, outplanes):

    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) / (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) ,拿新鲜生成的数据,来改进当前的神经网络。

    1def train():

    2 criterion = AlphaLoss()

    3 dataset = SelfPlayDataset()

    4 player, checkpoint = load_player(current_time, loaded_version)

    5 optimizer = create_optimizer(player, lr,

    6 param=checkpoint['optimizer'])

    7 best_player = deepcopy(player)

    8 dataloader = DataLoader(dataset, collate_fn=collate_fn, \

    9 batch_size=BATCH_SIZE, shuffle=True)

    10

    11 while True:

    12 for batch_idx, (state, move, winner) in enumerate(dataloader):

    13

    14 ## Evaluate a copy of the current network

    15 if total_ite % TRAIN_STEPS == 0:

    16 pending_player = deepcopy(player)

    17 result = evaluate(pending_player, best_player)

    18

    19 if result:

    20 best_player = pending_player

    21

    22 example = {

    23 'state': state,

    24 'winner': winner,

    25 'move' : move

    26 }

    27 optimizer.zero_grad()

    28 winner, probas = pending_player.predict(example['state'])

    29

    30 loss = criterion(winner, example['winner'], \

    31 probas, example['move'])

    32 loss.backward()

    33 optimizer.step()

    34

    35 ## Fetch new games

    36 if total_ite % REFRESH_TICK == 0:

    37 last_id = fetch_new_games(collection, dataset, last_id)

    训练用的损失函数表示如下:

    1class AlphaLoss(torch.nn.Module):

    2 def __init__(self):

    3 super(AlphaLoss, self).__init__()

    4

    5 def forward(self, pred_winner, winner, pred_probas, probas):

    6 value_error = (winner - pred_winner) ** 2

    7 policy_error = torch.sum((-probas *

    8 (1e-6 + pred_probas).log()), 1)

    9 total_error = (value_error.view(-1) + policy_error).mean()

    10 return total_error

    三是评估 (Evaluation) ,看训练过的智能体,比起正在生成数据的智能体,是不是更优秀了 (最优秀者回到第一步,继续生成数据) 。

    1def evaluate(player, new_player):

    2 results = play(player, opponent=new_player)

    3 black_wins = 0

    4 white_wins = 0

    5

    6 for result in results:

    7 if result[0] == 1:

    8 white_wins += 1

    9 elif result[0] == 0:

    10 black_wins += 1

    11

    12 ## Check if the trained player (black) is better than

    13 ## the current best player depending on the threshold

    14 if black_wins >= EVAL_THRESH * len(results):

    15 return True

    16 return False

    第三部分很重要,要不断选出最优的网络,来不断生成高质量的数据,才能提升AI的棋艺。

    三个环节周而复始,才能养成强大的棋手。

    有志于AI围棋的各位,也可以试一试这个PyTorch实现。

    本来摘自量子位,原作 Dylan Djian。

    代码实现传送门:

    网页链接

    教程原文传送门:

    网页链接

    AlphaGo Zero论文传送门:

    网页链接

声明:本篇经验系酷知网「www.coozhi.com」原创,转载请注明出处。

收到552个赞
十大微电影排行榜
假期周末闲暇之际,看一场好看的电影,放松心情,酷知网小编为大家整理了值得一看的十大微电影排行榜单哦。
最好看的穿越剧排行榜前十名
随着影视业的不断发展,我们现在市面上有着很多类型的电视剧,比如:谍战、古装、喜剧等等,如果说这好玩的恐怕就是穿越剧了,今天就一起看看最好看的穿越剧排行榜前十名。
韩国女明星排行榜前十名
大家都知道韩国这个国家虽然不大,但是整形和美女却非常的有名。在这里重点要说的就是女明星了,不仔细看以为韩国女明星都是一个模样复制的。今天就来盘点下韩国女明星排行榜前十名。
狙击电影排行榜前十名 好看的狙击手电影
狙击类型的电影往往给人的感觉就是冷酷、紧张、刺激等等,一般情况下男生看这类的电影居多,估计是每个男生都有当兵的梦想吧。今天就来盘点下狙击电影排行榜前十名 好看的狙击手电影。
悬疑片排行榜前十名
电影种类有很多种,有科幻、战争、爱情、谍战等等,不同类型的电影对应着不同的受众人群。而有一部分人群却喜欢悬疑片,喜欢在困惑中找寻自我。一起来看看悬疑片排行榜前十名。
国产电影排行榜前十名电影
中国的电影事业发展的很迅速,拍摄出很多质量不错的电影,那么国产电影排行榜前十名都有哪些呢?让我们一起去了解吧。
国内搜索引擎排行榜
网络的发达最重要的一点就是我们可以在互联网上搜集到很多的信息,搜集信息的载体就是搜索引擎了,比较常见的就是百度等等,那么还有哪些浏览器呢?一起学习下国内搜索引擎排行榜。
欧美电影排行榜前十
欧美的电影业向来发达,一向执世界电影业的牛耳,是世界电影业的风向标,那么欧美电影排行榜前十名都有哪些电影呢?下面是这方面内容的介绍。
美国电影排行榜前十 豆瓣高分
美国电影在全球影坛中也占据着非常重要的地位,毕竟好莱坞就是诞生在这个国家。其中科幻类型的电影在美国有着很高的票房,可以说全球最近几年的科幻电影大多出自美国,可见美国对科技的
婚纱摄影排行榜前十名
结婚,对于每对新人来说都是非常重要的事情,毕竟每个人一辈子只结婚一次,所以婚礼上的各种细节都得想的周全些。但是结婚前还有件事情必须做,那就是拍摄婚纱照。想要拍摄出好的婚纱照
经典电影排行榜前十名,一生必看的经典
世界各地每年都有很多电影推出,观众都目不暇接,都不知道应该选择哪些电影观看,下面是经典电影排行榜前十名,一生必看的经典电影的介绍。
科幻片排行榜前十名
近年来随着各国科技的不断发展,电影中也越来越多的出现了科幻的影子。从大海深处到外太空,电影真实的向我们展示了未来战争的雏形,震撼人的心理。今天就来一起欣赏下科幻片排行榜前十
关于我们 | 版权声明 | 免责声明 | 联系我们 | Sitemap
免责声明:酷知网所有文字、图片、视频、音频等资料均来自互联网,不代表本站赞同其观点,内容仅代表作者本人意见,若因此产生任何纠纷作者本人负责,本站亦不为其版权负责! 如有问题,请联系我们
CopyRight©2018 荆门市念若网络科技有限公司 版权所有 鄂ICP备2024034976号-1
WPS官网 WPS下载 telegram下载 telegram下载 WhatsApp网页版登录 WhatsApp网页版