策略梯度算法
RL有3个重要的组成部分,演员,环境和奖励函数,对于AlphaGo而言演员就是Alphago,决定落子的位置,环境就是对手,奖励函数就是围棋规则。
在Rl当中,环境和奖励函数是不可控的,我们唯一需要做的是调整演员的策略,使得演员能够得到最大的奖励——演员中的策略决定了演员的动作,即给定一个输入,输出演员现在能够执行的动作
对于策略π,假如我们用深度学习来做强化学习,π就是神经网络,常用θ代表π的参数,策略是1个网络,输入是游戏画面,输出是我们要执行的动作,可以简化为一个多分类问题,有几个动作,输出层就有几个神经元,输出层输出各个动作的执行概率。
环境决定游戏的结束。
将一场游戏称之为一个回合,对于蒙特卡洛而言,需要等到一个回合结束之后才能计算,将这一回合的所有得到的奖励都加起来就是总奖励(total reward),我们用R表示,演员需要最大化它可以得到的奖励
环境是一个函数,可能是神经网络的,也可能是rule-based的,环境一开始吐出一个状态s1,接下来演员看到状态s1之后,吐出动作a1,环境将a1作为输入,再吐出s2,…,不断迭代的过程,直到环境决定需要停止的时候。
一场游戏中,将所有状态和动作组合起来,就是一个轨迹,即τ={s1,a1,s2,a2,⋯,st,at}
给定演员的参数θ,我们可以计算某个轨迹τ发生的概率:
pθ(τ)=p(s1)pθ(a1∣s1)p(s2∣s1)pθ(a2∣s2)p(s3∣s2,a2)⋯=p(s1)∏t=1Tpθ(at∣st)p(st+1∣st,at)
p(st+1∣st,at)代表环境,我们能控制的是pθ(at∣st),给定st,采取的动作at取决于参数θ.
对于一个回合中,将轨迹中所有的奖励加起来得到了R(τ),代表1个轨迹τ的奖励.
我们需要做的是调整θ,使R(τ)越大越好,但是实际上R(τ)是一个随机变量,能够计算的是R(τ)的期望值,给定一组参数θ,可以计算rθ的期望值为:
R^θ=∑τR(τ)pθ(τ)
穷举所有可能的轨迹τ,每一个轨迹τ都有一个概率,根据θ算出某一个轨迹τ出现的概率,接下来计算τ的总奖励,总奖励使用τ出现的概率进行加权,对所有的τ进行求和,就是期望值:R^θ=∑τR(τ)pθ(θ)=Er∼pθ(τ)[R(τ)]
从分布pθ(τ)采样1个轨迹τ,计算R(τ)的轨迹,就是期望奖励(expected reward),最大化期望奖励,采用梯度上升(gradient ascent)来最大化期望奖励,先对R^θ计算梯度,∇R^θ=∑τR(τ)∇pθ(τ)
只有∇pθ(τ)与θ有关,由∇f(x)=f(x)∇logf(x)得:
pθ(τ)∇pθ(τ)=∇logpθ(τ)
∇R^θ=τ∑R(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)pθ(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)∇logpθ(τ)=Eτ∼pθ(τ)[Rτ∇logpθ(τ)]
实际上这个连续的期望无法计算,我们需要将其离散采样后取均值:
Eτ∼pθ(τ)[Rτ∇logpθ(τ)]=N1n=1∑NR(τn)∇logpθ(τn)=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
又因为
pθ(τ)=p(s1)∏t=1Tpθ(at∣st)p(st+1∣st,at)
有
∇logpθ(τ)=∇(logp(s1)+t=1∑Tlogpθ(at∣st)+t=1∑Tlogp(st+1∣st,at))=∇t=1∑Tlogpθ(at∣st)=t=1∑T∇logpθ(at∣st)
上式中logp(s1)和log(st+1∣st,at)由环境决定,和θ无关,因此求梯度为0;
∇R^θ=Eτ∼pθ(τ)[Rτ∇logpθ(τ)]=N1n=1∑NR(τn)∇logpθ(τn)=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
在我们采样得到的数据当中,采样到在某一个状态st要执行某一个动作at,(st,at)是整个轨迹τ的里面某一个状态的对,假设我们在st执行at,最后发现τ的奖励是正的,那就增加在st执行at的概率,反之则减小:
梯度上升:
θ←θ+η∇R^θ
用参数为θ的的agent和环境交互,也就是拿已经训练过的agent和环境交互,交互完之后,就可以得到大量的游戏的数据,记录每一回合得到的轨迹τ和得到的奖励R(τ)
将每一局采集到的数据带入:
∇R^θ=N1∑n=1N∑t=1TnR(τn)∇logpθ(atn∣stn)
然后梯度上升更新参数,随后就在更新模型和数据采样之间迭代。
一般策略梯度(policy gradient,PG)采样的数据只会用1次,采样过的数据在更新参数之后就会丢掉重新采样。
将RL看作1个多分类问题时,在一般的分类问题中,我们在实现分类的时候,目标函数都会写成最小化交叉熵(cross entropy),最小化交叉熵就是最大化对数似然,目标函数就是最大化或者最小化的对象,因为我们现在是最大大话似然,所以需要最大化:N1∑n=1N∑t=1Tnpθ(atn∣stn)
对于RL问题,就是在一般的分类问题的损失前面乘上一个权重,整局游戏得到的总奖励R(τ),而不是在状态sz做出动作a的阶段奖励:N1∑n=1N∑t=1TnR(τn)logpθ(atn∣stn)
对于每一回合的每一笔训练数据都加上R(τn)加权
策略梯度实现技巧
技巧1:添加基线
添加baseline,∇R^θ=N1∑n=1N∑t=1TnR(τn)∇logpθ(atn∣stn),我们总想着如果哪一步得到的奖励高,那么该步的概率就提高,也就是希望乘以的权重就更高,但是这是一个理想的情况,实际上我们采样应该是一个期望,对一个轨迹中所有的a和s进行加和后求平均,但是因为是采样,所以只采集到了少量的s和a,以动作a,b,c为例,假如三者都是好动作奖励都很正点,但是由于a没有被采样到,这下可完蛋了,b,c的概率就会提高,a的概率就会下降,但是这样却不是合理的,这就是问题之所在
解决方法:我们可以将奖励减去b,即:∇R^θ=N1∑n=1N∑t=1Tn(R(τ)−b)∇logpθ(atn∣stn)
其中,b称之为基线,通过这种方法就可以让R(τ)−b有正有负了。但是b怎么设置呢?一般的有,b=E[R(τ)],所以需要在训练的时候不断的取R(τ)的值,将这个均值作为b来使用
技巧2:分配合适的分数
∇R^θ=N1∑n=1N∑t=1Tn(R(τ)−b)∇logpθ(atn∣stn),对于一局游戏当中,有一些动作是好的,但也有坏的,一律都减去这个b,这并不公平,若是整局游戏结果不好,并不代表每一步都不好,在采样次数不够多的情况下,我们需要给每一个状态-动作对分配合理的分数。
一种做法是:计算某一个s−a对的奖励时,不把整局游戏的奖励全部加起来,只计算这个动作执行之后得到的奖励,因为这场游戏在执行这个动作发生之前的事情和执行这个动作没有关系,所以在执行这个动作之前的奖励都不算这个动作做出的贡献,将执行完这个动作之后的奖励加起来,才算是这个动作的贡献,于是有:∇R^θ=N1∑n=1N∑t=1Tn(∑t′=tTnrt′n−b)∇logpθ(atn∣stn),然后对未来的奖励做一个折扣,∇R^θ=N1∑n=1N∑t=1Tn(∑t′=tTnγ(t′−t)rt′n−b)∇logpθ(atn∣stn),依旧是老盆友γ折扣因子,嗯嗯。
REINFORCE:蒙特卡洛策略梯度
蒙特卡洛方法可以理解为算法经过一个回合之后,再利用这个回合的数据去学习,做一次更新,因为我们已经获得了整个回合的数据,所以每一步的数据自然也获得了,计算每个步骤的未来回报Gt,代表我们从这个步骤往后,能够得到的奖励之和。
一个最经典的算法是REINFORCE:
先获取每个步骤的奖励,然后计算每个步骤的未来总奖励Gt:∇R^θ=N1∑n=1N∑t=1TnGtn∇logπθ(atn∣stn),优化每一个动作的输出,伪代码是:设计一个函数输入时每个步骤获取的奖励,输出是每一个步骤的未来总奖励:Gt=∑k=t+1Tγk−t−1rk=rt+1+Gt+1
我们总是从后向前一步步的推,从GT算到G1,REINFORCE的伪代码:
input:可微调的参数π(a∣s,θ)
para:步长α>0
初始化的策略:θ∈Rd′
对每一个回合进行循环:
根据policyπ生成st,at,⋯
对每一个回合中的每一步进行循环,t=0,1,…,T-1:
G←k=t+1∑Tγk−t−1rk;θ←θ+αγtG∇logπ(at∣st,θ)
one-hot node:嗯老朋友不做介绍
对于多分类问题,在监督学习的优化中,我们将输入传给神经网络后得到输出,输出各种分类的概率,再计算交叉熵,即该输入真实的标签Yi′和输出Ti之间的crossentropy−∑Yi′.log(Yi),我们希望尽可能的减小crossentropyloss,计算出的交叉熵可以直接交给optimizer优化,然后进行para-update,这是一般的深度学习中简单的多分类问题的流程。
类似的,策略梯度预测每一个状态下应该需要输出的动作的概率,即输入概率st,输出at的概率,我们给动作one-hotnode,将神经网络的输出和实际的真实动作计算交叉熵。但是实际中的动作并不一定是正确的动作,我们没有标签来指导动作的正确与否,这个时候就乘以一个Gt,相当于对当前的真实动作的评价,于是我们有了−Gt∑Yi′.log(Yi)
实际上计算策略梯度损失的时候,就是对实际执行的动作取独热编码,和预测的动作的概率,二者相乘,得到了logπ(at∣st,θ),这就是我们需要的损失,对于整个轨迹中的所有损失加起来,给Adam optimizer优化参数
手绘图源文件:Drawing 2026-10-05 20.22.02.excalidraw
首先我们