【python】蒙特卡洛树搜索(MCTS)简单实现
过程包括以下四步:
选择 Selection:从根节点 R 开始,递归选择最优的⼦节点(后⾯会解释)直到达到叶⼦节点 L。
扩展 Expansion:如果 L 不是⼀个终⽌节点(也就是,不会导致博弈游戏终⽌)那么就创建⼀个或者更多的字⼦节点,选择其中⼀个 C。模拟 Simulation:从 C 开始运⾏⼀个模拟的输出,直到博弈游戏结束。
反向传播 Backpropagation:⽤模拟的结果输出更新当前⾏动序列。
代码实现:
import sys
import math
import random
import numpy as np
AVAILABLE_CHOICES = [1, -1, 2, -2]
AVAILABLE_CHOICE_NUMBER = len(AVAILABLE_CHOICES)
MAX_ROUND_NUMBER = 10
class Node(object):
def __init__(lf):
lf.parent = None
lf.children=[]
lf.visit_times=0
lf.quality_value = 0.0
lf.state=None
def t_state(lf,state):
lf.state = state
def get_state(lf):
return lf.state
def t_parent(lf,parent):
lf.parent = parent
def get_parent(lf):
return lf.parent
def t_children(lf,children):
lf.children = children
def get_children(lf):
return lf.children
def get_visit_times(lf):
return lf.visit_times
def t_visit_times(lf, times):
lf.visit_times = times
def visit_times_add_one(lf):
lf.visit_times +=1
def get_quality_value(lf):
return lf.quality_value
def t_quality_value(lf, value):
lf.quality_value = value
def quality_value_add_n(lf,n):
lf.quality_value +=n
def is_all_expand(lf):红茶绿茶
if len(lf.children)==AVAILABLE_CHOICE_NUMBER
黄刺蛾if len(lf.children)==AVAILABLE_CHOICE_NUMBER
return True
el:
return Fal
def add_child(lf,sub_node):
sub_node.t_parent(lf)
lf.children.append(sub_node)
def __repr__(lf):
return "Node:{},Q/N:{}/{},state:{}".format(hash(lf),lf.quality_value,lf,visit_times,lf.state)
class State(object):#某游戏的状态,例如模拟⼀个数相加等于1的游戏老老年
def __init__(lf):
lf.current_value=0.0#当前数
lf.current_round_index=0#第⼏轮
lf.cumulative_choices = []#选择过程记录
def is_terminal(lf):#判断游戏是否结束
if lf.current_round_index = MAX_ROUND_NUMBER-1
return True
el:
return Fal
def compute_reward(lf):#当前得分,越接近1分值越⾼
return -abs(1-lf.current_value)
def t_current_value(lf,value):
lf.current_value=value
def t_current_round_index(lf,round):
lf.current_round_index=round
def t_cumulative_choices(lf,choices):
lf.cumulative_choices=choices
def get_next_state_with_random_choice(lf):#得到下个状态
random_choice=random.choice([choice for choice in AVAILABLE_CHOICES])
next_state=State()
next_state.t_current_value(lf.current_value+random_choice)
next_state.t_current_round_index(lf.current_round_index+1)
next_state.t_cumulative_choices(lf.cumulative_choices+[random_choice])
return next_state
def monte_carlo_tree_arch(node):#蒙特卡洛树搜索总函数
computation_budget=1000
for i in range(computation_budget):
expend_node = tree_policy(node)
reward = default_policy(expand_node)
backup(expand_node,reward)山海经凶兽
best_next_node = best_child(node,Fal)
return best_next_node
def best_chile(node,is_exploration):#若⼦节点都扩展完了,求UCB值最⼤的⼦节点
best_score=-sys.maxize
best_sub_node = None
for sub_node _children():
if is_exploration:
C=1/math.sqrt(2.0)
el:
C=0.0
left=_quality_value()/_visit_times()
right=2.0*math._visit_times())/_visit_times()
score=left+C*math.sqrt(right)
if score>best_score:
best_sub_node = sub_node
return best_sub_node
def expand(node):#得到未扩展的⼦节点
tried_sub_node_states= [_state() for sub_node _children()]
new_state = _state().get_next_state_with_random_choice()
while new_state in tried_sub_node_states:
new__state().get_next_state_with_random_choice()
sub_node=Node()
sub_node.t_state(new_state)阅读推广
node.add_child(sub_node)
return sub_node
return sub_node
def tree_policy(node):#选择⼦节点的策略
_state().is_terminal()==Fal:
if node.is_all_expand():
node=best_child(node,True)
el:
sub_node = expand(node)
return sub_node
return node
def defaut_policy(node):
current_state = _state()
while current_state.is_terminal==Fal:
current_state = _next_state_with_random_choice()
final_state_reward=pute_reward()
涮火锅的食材有哪些return final_state_reward
def backup(node,reward):
while node != None:
node.visit_times_add_one()
node.quality_value_add_n(reward)
node = node.parent
提升
很多种 MCTS 强化的技术已经出现了。这些基本上可以归纳为领域知识或者领域独⽴两⼤类。
领域知识
特定博弈游戏的领域知识可以⽤在树上来过滤掉不合理的⾏动或者在模拟过程中产⽣重要的对局(更接近⼈类对⼿的表现)。这意味着交战结果将会更加的现实⽽不是随机的模拟,所以节点只需要少量的迭代就能给出⼀个现实的收益值。
领域知识可以产⽣巨⼤的性能提升,但在速度和⼀般性上也会有⼀定的损失。
领域独⽴
领域独⽴强化能够应⽤到所有的问题领域中。这些⼀般⽤在树种(如 AMAF),还有⼀些⽤在模拟(如 在交战时倾向于胜利的⾏动)。领域独⽴强化并不和特定的领域绑定,具有⼀般性,这也是当前研究的重⼼所在。
AlphaGo的基本原理
围棋是⼀类完全信息的博弈游戏。然⽽,其庞⼤的搜索空间,以及局⾯棋势的复杂度,使得传统的剪枝搜索算法在围棋⾯前都望⽽却步。在AlphaGo出现之前,MCTS算法算是⼀类⽐较有效的算法。它通过重复性地模拟两个players的对弈结果,给出对局⾯s的⼀个估值v(s)(Monte Carlo rollouts);并选择估值最⾼的⼦节点作为当前的策略(policy)。基于MCTS的围棋博弈程序已经达到了业余爱好者的⽔平。
然⽽,传统的MCTS算法的局限性在于,它的估值函数或是策略函数都是⼀些局⾯特征的浅层组合,往往很难对⼀个棋局有⼀个较为精准的判断。为此,AlphaGo的作者训练了两个卷积神经⽹络来帮助MCTS算法制定策略:⽤于评估局⾯的value network,和⽤于决策的policy network。(后⾯会看到,这两个⽹络的主要区别是在输出层:前者是⼀个标量;后者则对应着棋盘上的⼀个概率分布。)
⾸先,Huang等⼈利⽤⼈类之间的博弈数据训练了两个有监督学习的policy network:pσ(SL policy network)和pπ(fast rollout policy network)。后者⽤于在MCTS的rollouts中快速地选择策略。接下
来,他们在pσ的基础上通过⾃我对弈训练了⼀个强化学习版本的policy network:pρ(RL policy network)。与⽤于预测⼈类⾏为的pσ不同,pρ的训练⽬标被设定为最⼤化博弈收益(即赢棋)所对应的策略。最后,在⾃我对弈⽣成的数据集上,Huang等⼈⼜训练了⼀个value network:vθ,⽤于对当前棋局的赢家做⼀个快速的预估。
pipeline of neural networks
因此,⽤⼀句话简单概括⼀下AlphaGo的基本原理:在MCTS的框架下引⼊两个卷积神经⽹络policy network和value network以改进纯随机的Monte Carlo模拟,并借助supervid learning和reinforcement learning训练这两个⽹络。
接下来将对AlphaGo的细节进⾏展开讨论。
有监督学习的Policy Networks
Huang等⼈⾸先训练了⼀个有监督的Policy Network⽤来模拟⼈类专家的⾛⼦。SL policy network是⼀个卷积神经⽹络;其输出层是⼀个Softmax分类器,⽤来计算在给定的棋⾯状态s下每⼀个位置的落⼦概率pσ(a|s)。对⼀个棋⾯状态s的描述如下:
input features for policy networks
(这⾥的Features对应着卷积神经⽹络⾥的Channels。)
经过⼈类⾼⼿三千万步围棋⾛法的训练后,SL policy network模拟⼈类落⼦的准确率已经达到了57%;相应地,⽹络的棋⼒也得到⼤⼤的提升。但是,如果直接⽤这个⽹络与⼈类⾼⼿,甚⾄是MCTS的博弈程序进⾏对弈,依然是输⾯居多。⽽且,这个⽹络的⾛⼦太慢了!平均每步3ms的响应时间,使得这个⽹络很难被直接⽤于MCTS的rollout中进⾏策略的随机。因此,Huang等⼈通过提取⼀些pattern features⼜训练了⼀个更快速(响应时间达到了2μs)但准确率有所降低(24.2%)的rollout policy network: pπ。
强化学习的Policy Networks
接下来,为了进⼀步提⾼policy network的对弈能⼒,Huang等⼈⼜采⽤⼀种policy gradient reinforcement learning的技术,训练了⼀个RL policy network:pρ。这个⽹络的结构与SL policy network的⽹络结构相同,依然是⼀个输出为给定状态下落⼦概率的卷积神经⽹络。⽹络的参数被初始化为pσ的参数;接下来,通过不断地⾃我对弈(与历史版本),⽹络的权重向着收益最⼤化的⽅向进化。此时,⽹络的学习⽬标不再是模拟⼈类的⾛法,⽽是更为终极的⽬标:赢棋。
具体来说,我们定义了⼀个reward function r(st):对于⾮终⽌的时间步t<T,总有r(st)=0。每⼀步的收益z(t)被定义为±r(sT):即对当前玩家⽽⾔对弈的最终结果(+1代表赢棋;−1代表输棋)。⽹络的权重
通过随机梯度上升法进⾏调整:
Δρ∝∂logpρ(at|st)∂ρzt
通过这种⽅式训练出来的RL policy network,在与SL policy network对弈时已有80%的赢⾯。即便是与依赖Monte Carlo搜索的围棋博弈程序相⽐,不依赖任何搜索的RL policy network,也已经达到了85%的赢⾯。
强化学习的Value Networks
最后,Huang等⼈⼜开始寻求⼀个能快速预估棋⾯价值(棋势)的Value Network。⼀个棋⾯的价值函数vp(s),被定义为在给定的⼀组对弈策略p的情况下,从状态s出发,最终的期望收益(也即赢棋的概率):
vp(s)=E[zt|st=s,at…T∈p]
显然,理想情况下,我们想知道的是在双⽅均采⽤最优策略的条件下得到的最优期望收益v∗(s)。然⽽,我们并不知道什么才是最优的策略。因此,在实际应⽤中,Huang等⼈采⽤了⽬前最强的策略函数pρ(RL policy network )来计算⼀个棋⾯的价值vpρ(s),并训练了⼀个value network vθ(s)来拟合这个价值函数:vθ(s)≈vpρ(s)≈v∗(s)。
细胞膜功能Value Network的⽹络结构与前⾯的Policy Network类似,也是⼀个卷积神经⽹络,只是输出层变成了⼀个单神经元的标量。我们可以通过构造⼀组(s,z)的训练数据,并⽤随机梯度下降法最⼩化⽹络的输出vθ(s)与⽬标收益z的均⽅差,来调整⽹络的参数:
Δθ∝∂vθ(s)∂θ(z−vθ(s))
在构造训练数据时有⼀些技巧。如果我们从⼈类对弈的完整棋局中抽取⾜够数量的训练数据,很容易出现过拟合的问题。这是因为,在同⼀轮棋局中的两个棋⾯的相关性很强(往往只相差⼏个棋⼦);此时,⽹络很容易记住这些棋⾯的最终结果,⽽对新棋⾯的泛化能⼒很弱。为了解决这个问题,Huang等⼈再次祭出强化学习的⼤杀器:通过RL policy network的⾃我对弈,产⽣了三千万个从不同棋局中提取出来的棋⾯-收益组合的训练数据。基于这份数据训练出来的Value Network,在对⼈类对弈结果的预测中,已经远远超过了使⽤fast rollout policy network的MCTS的准确率;即便是与使⽤RL policy network的MCTS相⽐,也已是不遑多让(⽽Value Network的计算效率更⾼)。
accuracy of value network
整合
到这⾥,我们⼿头上已经有⼀个⽜逼但是巨慢的SL policy network;有⼀个不那么⽜逼但是很快的fast
policy network;有⼀个⼀⼼只想着如何赢棋的RL policy network;还有⼀个能⼀眼洞穿棋局的value network。那么,将这些networks放在⼀起互相补⾜,会得到什么呢?
答案就是AlphaGo。⽽把这些networks整合在⼀起的框架,就是MCTS算法。
与经典的MCTS算法类似,APV-MCTS(asynchronous policy and value MCTS)的每⼀轮模拟也包含四个步骤:
Selection:APV-MCTS搜索树中的每条连边(s,a)都包含三个状态:决策收益Q(s,a),访问次数N(s,a),和⼀个先验概率P(s,a)。这三个状态共同决定了对⼀个节点下⾏为的选择:
at=argmaxa(Q(st,a)+u(st,a))
其中,u(s,a)∝P(s,a)1+N(s,a)
Expansion:步骤1中的lection终⽌于叶⼦节点。此时,要对叶⼦节点进⾏扩展。这⾥采⽤SL policy network pσ计算出叶⼦节点上每个⾏为的概率,并作为先验概率P(sL,a)存储下来。
Evaluation。使⽤value network vθ(s)和fast rollout policy network pπ模拟得到的博弈结果对当前访问到的叶⼦节点进⾏估值:
V(sL)=(1−λ)vθ(sL)+λzL
Backup。更新这⼀轮模拟中所有访问到的路径的状态:
N(s,a)=∑i=1n1(s,a,i)
Q(s,a)=1N(s,a)∑i=1n1(s,a,i)V(siL)
其中,n是模拟的总次数;1(s,a,i)标⽰第i轮模拟中是否经过边(s,a);siL是第i轮模拟中访问到的叶⼦节点。
西安研学下图展⽰了⼀轮模拟的动态过程。
MCTS with policy networks and value networks
模拟结束后,算法会选择访问次数N(s,a)最⼤的策略a作为当前的⾛⼦策略。
值得注意的是,在整个模拟的过程中,我们见到了SL policy network(⽤于Expansion中先验概率的计算);见到了fast rollout policy network(⽤于Evaluation中的快速⾛⼦);见到了value network(⽤于Evaluation中对棋势的预估)。等等,RL policy network去哪了?为什么不⽤RL policy network替代SL policy network?明明RL policy network有着更强的棋⼒啊(85%的赢⾯)?
这是因为,与RL policy network相⽐,由⼈类专家⾛法训练出来的SL policy network在策略上的多样性更强;因此更适⽤于MCTS中的搜索。但是,⽤RL policy network的⾃我对弈结果训练出来的value network的泛化能⼒就要⽐SL policy network训练出来的value network要强得多了。