关注:5 发布时间:2021-07-26 20:47:01
导语本文整理了自学围棋的alphago经验知识,帮助您全面了解,小白也能成为高手,跟随小编一起来看一看吧!
遥想当年,alphago的掌握版本,在完胜柯洁九段之后不久,就被后辈alphago zero(简称狗零)击溃了。
从一只完全不懂围棋的ai,到打败大师,狗零只用了21天。
而且,它不需要用人类知识来喂养,成为棋手全靠自学。
如果能培育这样一只ai,即便自己不会下棋,也可以很骄傲吧。
于是,来自巴黎的少年dylan djian(简称小笛) ,就照着狗零的论文去实现了一下。
他给自己的人工智能棋手起名supergo,也提供了代码(传送门见文底) 。
除此之外,还有教程——
一个身子两个头
智能体分成三个部分:
一是特征提取器(特征提取器),二是策略网络(政策网络),三是价值网络(价值网络).
于是,狗零也被亲切地称为"双头怪"。特征提取器是身子,其他两个网络是脑子。
特征提取器
特征提取模型,是个残差网络(resnet),就是给普通美国有线电视新闻网加上了跳层连接(跳过连接),让梯度的传播更加通畅。
跳跃的样子,写成代码就是:
1clasbasicblock(nn .模块):
2 """
3具有2个卷积和一个跳跃连接的基本剩余块
四在比较后一次线性单元激活之前。
5 """
6
7def__init__(自,平面内,平面,步幅=1,下采样=无):
8 super(基本锁定,自锁定).__init__
9
10 self.conv1=nn .conv2d(平面,平面,内核大小=3,
11步幅=步幅,填充=1,偏差=假)
12 self.bn1=nn .batchnorm2d(平面)
13
14 self.conv2=nn .conv2d(平面,平面kernel_size=3,
15步幅=步幅,填充=1,偏差=假)
16 self.bn2=nn .batchnorm2d(平面)
17
18
19defforward(self,x):
20残差=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输出=剩余
29 out=f.relu(out)
30
31返回
然后,把它加到特征提取模型里面去:
一类提取器(nn .模块):
2def__init__(自身、输入、输出):
3超级(提取器,自)。__init__
4 self.conv1=nn .conv2d(输入平面,输出平面,步幅=1,
5内核_大小=3,填充=1,偏差=假)
6 self.bn1=nn .batchnorm2d(输出通道)
七
8forblockinrange(blocks):
9 setattr(self," res{} " .格式(块),\
10基本锁定(输出通道,输出通道))
11
12
13前进(自我,x):
14 x=f . relu(self。bn 1(自我。con v1(x)))
15 for blockinrange(blocks-1):
16 x=getattr(self," { res } " .格式(块))(x)
17
18 feature_maps=getattr(self," { res } " .格式(blocks - 1))(x)
19returnfeature _ maps
策略网络
策略网络就是普通的美国有线电视新闻网了,里面有个批量标准化(批量标准化),还有一个全连接层,输出概率分布。
1类政策网(nn .模块):
2def__init__(自身、输入、输出):
3超级(policynet,self).__init__
4自我。输出通道=输出通道
5 self.conv=nn .con v2d(平面内,1,kernel_size=1)
6 self.bn=nn .batchnom2d(1)
7 self.logsoftmax=nn .logsoftmax(dim=1)
8 self.fc=nn .线性(输出通道- 1,输出通道)
9
10
11前进(自我,x):
12 x=f.relu(self.bn(self.conv(x)))
13 x=x.view(-1,self。输出通道-1)
14 x=self.fc(x)
15 probas=self.logsoftmax(x).exp
16
17returnprobas
价值网络
这个网络稍微复杂一点。除了标配之外,还要再多加一个全连接层。比较后,用双曲正切(双曲正切)算出(-1,1) 之间的数值,来表示当前状态下的赢面多大。
代码长这样——
1clasvaluenet(nn .模块):
2def__init__(自身、输入、输出):
3 super(valuenet,self).__init__
4自我。输出通道=输出通道
5 self.conv=nn .con v2d(平面内,1,kernel_size=1)
6 self.bn=nn .batchnom2d(1)
7 self.fc1=nn .线性(输出通道- 1,256)
8 self.fc2=nn .线性(256,1)
9
10
11前进(自我,x):
12 x=f.relu(self.bn(self.conv(x)))
13 x=x.view(-1,self。输出通道-1)
14 x=f.relu(self.fc1(x))
15 winding=f . tanh(self . fc2(x))
16returnwinning
未雨绸缪
狗零的另一个重要组成部分是蒙特卡洛树搜索(mcts)。
它允许ai玩家提前找出胜率比较高的地方。
在模拟器中,对手的下一手牌是模拟的,下一手牌是给的,所以远远一步多。
节点
树上的每个节点代表不同的情况,并具有不同的统计数据:
每个节点已经通过的次数n,总动作值w,通过这个点的先验概率p,平均动作值q (q=w/n),从其他地方来到这个节点的步骤,以及从这个节点开始的所有可能的后续步骤。
1classnode:
2def__init__(自身,父项=无,proba=无,move=无):
3 self.p=proba
4 self.n=0
5 self.w=0
6 self.q=0
7 self.children=[]
8 self.parent=家长
9 .移动=移动
卷展栏(卷展栏)
第一步是puct(多项式上的置信树)算法,该算法选择能够比较大化puct函数的变体的行走方法(如下)。
用代码写的,——
1选择(节点,c_puct=c_puct):
2“基于puct公式选择的优化版本”
三
4 total_count=0
5 foriirange(nodes . shape[0]):
6 total_count=nodes[i][1]
七
8 action _ scores=np . zeros(nodes . shape[0])
9 forinrange(nodes . shape[0]):
10 action _ scores[i]=nodes[i][0]c _ puct * nodes[i][2]* \
11 (np.sqrt(total_count)/(1个节点[i][1])
12
13等于=np . where(action _ scores==np . max(action _ scores))[0]
14ifequals.shape[0] 0:
15returnnp.random.choice(等于)
16returnequal
s[0]
结束(结束)
选择在不停地进行,直至到达一个叶节点(叶节点),而这个节点还没有往下生枝。
1defis_leaf(self):
2 """检查节点是否为叶" " "
3
4 returnlen(self。儿童)=0
到了叶节点,那里的一个随机状态就会被评估,得出所有"下一步"的概率。
所有被禁的落子点,概率会变成零,然后重新把总概率归为1。
然后,这个叶节点就会生出枝节(都是可以落子的位置,概率不为零的那些) 。代码如下——
1defexpand(self,probas):
2自我。子节点=[节点(父节点=自身,移动=idx,proba=probas[idx]) \
3 foridxinnrange(probas。形状[0])if probas[idx]0]
更新一下
枝节生好之后,这个叶节点和它的妈妈们,身上的统计数据都会更新,用的是下面这两串代码。
1defupdate(self,v):
2 """在展示后更新节点统计信息" " "
3
4 self.w=self.w v
5自我。q=self。w/self。nif self。n 0 else 0
1whilecurrent_node.parent:
2 current_node.update(v)
3电流_节点=电流_节点。父母
选择落子点
模拟器搭好了,每个可能的"下一步",都有了自己的统计数据。
按照这些数据,算法会选择其中一步,真要落子的地方。
选择有两种,一就是选择被模拟的次数比较多的点。试用于测试和实战。
另外一种,随机(随机)选择,把节点被经过的次数转换成概率分布,用的是以下代码——
一总计=np.sum(action_scores)
2 probas=action_scores/total
3移动=np。随机。选择(动作分数。shape[0],p=probas)
后者适用于训练,让阿尔法围棋探索更多可能的选择。
三位一体的修炼
狗零的修炼分为三个过程,是异步的。
一是自对弈(自玩),用来生成数据。
1defself_play:
2whiletrue:
3新_玩家,检查点=load_player
4ifnew_player:
5玩家=新_玩家
6
7 ##创建进程的自运行匹配队列
8个结果=create_matches(player,cores=parallel_self_play,
9 match_number=self_play_match)
10 for _ in范围(self _ play _ match):
11 result=results.get
12 db.insert({
13 "游戏" :结果,
14 "id":游戏id(_ d)
15 })
16 game_id=1
二是训练(培训),拿新鲜生成的数据,来改进当前的神经网络。
1deftrain:
2标准=alphaloss
3数据集=自交数据集
四播放器,检查点=load_player(current_time,loaded_version)
5 optimizer=create _ optimizer(player,lr,
6参数=检查点["优化器"])
7 best_player=deepcopy(player)
8 dataloader=dataloader(数据集,collate_fn=collate_fn,\
9批处理大小=批处理大小,洗牌=真)
10
11whiletrue:
12forbatch_idx,(state,move,winner)in numerate(data loader):
13
14 # # #评估当前网络的副本
15 if total _ ite % train _ steps==0:
16 pending_player=deepcopy(播放器)
17结果=评估(待定_玩家,比较佳_玩家)
18
19ifresult:
20比较佳玩家=待定玩家
21
22示例={
23 "state": state,
24 "winner": winner,
25 "移动" :移动
26 }
27 optimizer.zero_grad
28赢家,probas=pending_player.predict(示例["state"])
29
30损失=标准(获胜者,例如["获胜者"],\
31 probas,示例["移动"])
32亏损。倒退
33 optimizer.step
34
35 ##取新游戏
36 if total _ ite % refresh _ tick==0:
37 last_id=fetch_new_games(集合,数据集,last_id)
训练用的损失函数表示如下:
一类丢失(torch.nn.module):
2def__init__(self):
3超级(阿尔法损失,自我).__init__
四
5defforward(self,pred_winner,winner,pred_probas,probas):
6 value _ error=(winner-pred _ winner)* * 2
7策略_错误=torch。sum((-probas *
8 (1e-6 pred_probas).log),1)
9 total _ error=(value _ error。视图(-1)策略_错误).平均值
10returntotal_error
第三个是评估受过训练的代理是否比生成数据的代理更好(比较佳人选返回第一步并继续生成数据)。
1defevaluate(player,new_player):
2结果=玩(玩家,对手=新_玩家)
3个black_wins=0
4 white_wins=0
五
6forresultinresults:
7ifresult[0]==1:
8个white_wins=1
9elifresult[0]==0:
10个black_wins=1
11
12 # # #检查受训玩家(黑)是否优于
13 ##当前比较佳玩家取决于门槛
14ifblack_wins=eval_thresh * len(结果):
15返回真
16returnfalse
第三部分很重要。只有不断选择比较佳网络生成高质量数据,才能提高ai的棋艺。
只有重复这三个环节,才能培养出一个强有力的棋手。
对ai go感兴趣的人也可以试试这个pytorch实现。
比较初取自量子位,原dylan djian。
代码实现门户:
网页链接
教程原始门户:
网页链接
alphago零论文门户:
网页链接
上一篇:国内搜索引擎排行榜
下一篇:怎么根据isbn码查询书籍信息
4853位用户关注
3140位用户关注
2511位用户关注
2295位用户关注
1800位用户关注
977位用户关注