自学围棋的AlphaGo Zero,你也可以造一个
遥想当年,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论文传送门:
网页链接
alphago围棋排行榜
人类终于在围棋上赢了AI!业余棋手击败了顶级AI,但别高兴得太早
图片来源:pixabay
2016年,AlphaGo战胜了前围棋世界冠军李世石,登上各大新闻头条。这不免让人想起1997年在国际象棋上战胜人类的IBM深蓝,但不同的是,国际象棋的棋盘是8×8,而围棋的棋盘是19×19,每一步棋多出来的可能性让棋局的可能性呈指数暴增,让IBM深蓝的穷举策略变得不可能。但在蒙特卡洛树和深度神经网络的加持下,AI围棋程序AlphaGo展现出了魔法一般的能力,让人类无从招架。
随着AlphaGo的胜利,AI对人类的威胁,不论是科幻中控制人类社会的AI失控,还是现实中的失业风险,都引起了越来越多的注意,这与现目前人们对ChatGPT和GPT-4的担忧别无二致。不过,当时败北的李世石却发出了不一样的声音:“它的风格完全不同,和它对弈的体验非同一般,我花了点时间才适应。AlphaGo让我意识到,我在围棋上还能继续精进。”同时,几个月前就输给AlphaGo的欧洲围棋冠军选手樊麾也表示,与AlphaGo的对弈让他对围棋有了“完全不同”的看法,这在后来甚至提升了他的排名。
人工智能究竟有没有改变顶级棋手的思考方式,这很难研究。但是几十年来职业棋手对弈时的棋谱则为研究人员提供了宝贵的参考。3月13日在《美国科学院刊》上发表的一篇论文显示,自从围棋AI出现后,人类选手在围棋竞赛中的水平确实提高了。
DeepMind首席科学家、AlphaGo项目负责人戴维·西尔弗(David Silver,并未参与此项研究)表示:“看到人类玩家能这么快适应,并将新发现融入自己的对局中,真的令人惊讶。论文结果表明,人类能很快适应这些变化,并在此基础上更进一步,提升自己。”
为了查明围棋AI是否促使人类找到了新的围棋策略,香港城市大学的助理教授申敏奎和同事使用了一个数据库网站Games of Go on Download,其中收录了1950至2021年期间580万次棋局的棋谱。之所以从1950年开始记录数据,是因为1950年是现代围棋规则建立的那一年。
为了梳理580万盘棋局,研究团队必须创造一套程序来评估每一步决策的质量。研究团队用到了人工智能系统KataGo,评估人类每一步棋的胜率,并将它和AI决策的胜率进行比较,并以此评判人类棋手每一步棋的水平。DeepMind的AlphaGo并没有公开,一般认为KataGo是和AlphaGo同等级的AI围棋程序。
研究人员用AI胜率分析了2016年以前和2016年以后的数据。结果非常明显,在AI击败人类围棋冠军之前的66年里,人类的决策水平在AI看来保持一致,或者说停滞不前,而在2016年AlphaGo的胜利后,人类棋手的决策质量开始攀升——人类虽然可能下不过围棋AI,但在围棋AI的指导下,人类围棋选手的决策质量也在不断攀升。
掌握了这些数据,研究人员又开发出了一种方法,来查明人类棋手下出的棋中,究竟哪一步是新颖的——这指的是这种下法没有出现在以前的对弈记录中。在围棋对弈的创新型分析中,研究人员跟踪了每局棋的前60手,并在新下法出现时进行标记。比如,如果一场比赛中到第9手时才和传统下法出现差异,而另一场比赛中到第15手才出现差异,那么前一场比赛就比另一场比赛更多变、更新奇。
结果显示,在2016年之后,人类棋手的下法更多变了。而且有趣的是,在2016年之前,大多数新颖的下法往往会降低整体的胜率;而2016年之后则反了过来——新颖的下法反而会提升整体胜率。很显然,单纯的“背AI棋谱”并不能达成这样的效果,AI真的帮人类找到了更科学的下棋方法。
但情况在最近发生了转变。最近,美国棋手凯琳·佩林(Kellin Pelrine)站了出来,他并非专业棋手,甚至也不是顶尖业余棋手,却能和顶级围棋AI取得14:1的战绩。
他所用下法是一种专用于对抗AI的方法,如果用这种方法和人类玩家对弈,你一定会输得很惨。并且,最令人失望的是,就连这种专门针对AI的下法,也是由计算机程序算出来的。
美国加州科技公司FAR AI的首席执行官亚当·格利夫(Adam Gleave)设计了这个程序,他表示:“开发这套系统出乎意料的容易。”他补充道,该软件与顶级围棋AI KataGo进行了超过100万场对局,就是为了找到人类棋手可以利用的盲点。
这个由计算机程序找到的对抗AI的策略是这样的:慢慢地在棋盘外圈连起来一连串棋子,试图将这一串棋子围成一个大“包围圈”,同时,在棋盘的其他角落下一些分散的棋子来分散AI的注意力。使用这种策略,AI很难发现你已经悄悄围起来了一大片区域,最终你能通过这种方法占据更多目,战胜不可一世的围棋AI。
但是如果你用这种方式来和人类棋手对弈,对方大概率会觉得你是一个刚刚掌握围棋规则的新手,只想过来随便玩玩。毕竟这种下法的目的太明显了——就是想用棋子围起来一大片区域。只要对方掌握了基本的围棋规则,用这种下法基本就没有胜率。因为只要你的“包围圈”还没有合拢,对方就能在几手棋内轻松破解整个包围圈。这时,因为你在包围圈上浪费了太多棋子,已经不可能再赢得这场比赛了。
但AI就是识别不出潜在的危险,也无法在包围圈合拢前破解整个包围圈。当然,在棋盘的其他角落分散下一些棋子来吸引AI的注意还是需要一些水平的。据佩林的说法,至少也要是比较强的爱好者才能用这种办法战胜AI。
研究人员表示,KataGo之所以失手,可能是因为在它的对抗性演练中,并没有经历过足够多的此类下法——或者说,这种制造“包围圈”的下法往往在刚开始时就因为浪费棋子太多,而被认为注定落败,所以围棋AI也没有意识到如果让“包围圈”合围,自己就会落败。
AlphaGo的出现让人类围棋水平快速提升,申敏奎表示:“我们不应将人工智能视作人类的威胁,而是一种能增强我们能力的宝贵工具”。在ChatGPT和GPT-4爆火的今天,这种观点能给人更多希望。
但是,这一切并非没有风险,就像在AI已经统治围棋界的今天,仍有一部分在人类看来完全不成立的下法却能战胜AI。亚当·格利夫表示:“我们能看到,非常庞大的AI系统在几乎没有被验证的情况下就被大规模部署了。”这究竟给我们带来了更多的便利,还是带来了更多的风险,谁都无法回答这个问题。
参考链接:
https://www.scientificamerican.com/article/ais-victories-in-go-inspire-better-human-game-playing/
https://arstechnica.com/information-technology/2023/02/man-beats-machine-at-go-in-human-victory-over-ai/
- 02-19科技
linux查端口号命令
- 02-13生活
菜篮子有很多油如何洗
- 05-06美食
鸡头米的做法
- 08-12科技
七彩虹主板组装电脑设置一键U盘启动的方法
- 11-14健康
上海新冠疫苗怎么预约
- 01-09科技
w10传递优化文件能删吗
- 08-22教育
如何在excel中快速的进行筛选
- 02-18生活
自来水地底下冻了怎么办
推荐
- 1香肠抽真空常温下能放多久413
- 2苏州过年习俗253
- 3腊肠酸酸的咋回事489
- 4酸奶机可以做什么美食406
- 5低碳生活的调查报告199
- 6筷子为什么不能用开水煮149