基于代码和公式理解PPO算法实现

        作者本人也是一个野生AI爱好者,仅仅只是停留在表面理解问题。本次文章打算记录一下PPO算法,算是对自己前段时间学习的一个总结方便自己后续查看。

强化学习

        我对于强化学习粗浅的理解就是让模型在在大量随机数据中,不停的强化训练它,使得它能在很多的答案和数据中寻找一个和问题最相关的答案。如在语言大模型中,当用户问了一个问题后,实际上是有很多回答的,比如我问大模型,我想实现一个排序算法,这时候模型可以给我一个快速排序,也可以给我一个冒泡排序。但是实际上我们肯定是喜欢模型告诉我们的是快速排序这种时间复杂度低的一种。因此我们可以基于上面的例子给出一个初步的强化学习的公式:

                         {​{_{E}}^{}_{x~p[R(x))]}}^{} = \int R(x)P(\Theta )dx                   公式1

        公式1中R(x)表示每个答案的回报率,而P(x) 表示的是这个答案出现的概率。那实际上就是说强化学习的目标就是为了让整个模型在出答案的时候他的回报期望率最高。

代理学习

        在理解我们的代理学习之前,我们先做一个铺垫,我们先对公式1进行求导。

                                      {​{_{E}}^{}_{x~p[R(x))]}}^{} = \int R(x)P(\Theta )dx = \int R(x)\Delta P(\Theta )dx                                                                                                   公式2

                                                   \Delta logf(x) = \Delta f(x) / f(x)                                                                                                                     公式3

                                     \int R(x)\Delta P(\Theta )dx = \int R(x)\Delta log(P(\Theta ))P(\Theta )dx                                                                                                    公式4

        因为我们无法穷举所有的情况,因此我们可以采用大数定理的原理,对公式4进行近似等效。等效的公式如下:

                                \int R(x)\Delta logP(\Theta )P(\Theta ) \approx \frac{1}{n}\sum R(x)\Delta logP(\Theta )                                                                                                     公式5

                                      P(\Theta ) = P(s1,a1,s2,a2,s3,a3.......) = P(s1)*P(a1|s1)P(s2|a1,s1).....                                                                                                     公式6

                                 P(\Theta ) = \prod P(S1) \prod P(s_{t+1}|at, st) \prod P(at|st)                                                                                                   公式7

                        \frac{1}{n}\sum R(x)\Delta logP(\Theta ) = \frac{1}{n}\sum R(x)\sum_{1}^{t} \Delta logP(at|st) = \frac{1}{n}\sum_{1}^{n} \sum R(x)\Delta logP(at|st)                                                                                               公式8

       因为另外两项和其他网络无关,因此我们就得到了公式8

        由于上述的强化学习训练过程中,每次都要先产生一堆的结论,然后在给这些结论打分数,最后在和产生这些数据的概率一起算一个期望,然后在更新这个梯度使得期望不停的变大。但是这里有一个问题,就是我们每次更新完模型我们的概率就变了,这个时候我们就要重新生成一堆随机答案在进行评分。如果这样一直训练下去,就会发现模型吭哧吭哧的干了老半天,最后更新只做了几次,然后大部分时间都在随机产生答案中,这其实对于我们模型训练没有实质性的帮助。那我们有没有一种方法可以规避这种办法呢。有,聪明的科学家们就想到了用代理的方式去解决这种问题,这就是重要性采样的原理。我们将公式8进行变换,因为1/n 和前面的总和和后面的无关我们只针对后面的式子进行变化。

        \sum_{1}^{t}R(x) \Delta P(at|st) = t * \int R(x)\frac{P(\Theta )}{Q(\Theta )}\Delta logP(\Theta )Q(\Theta ) dx = \sum_{1}^{t}R(x)\frac{P(at, st)}{Q(at,st)}\Delta logP(at|st)                                                                                                    公式9

因为上面的期望实际上就是每个时刻的 (at,st)概率对应的前面的公式进行累加。又因为Q P 模型在t时刻面临的状态是一样的,所以公式9 又可以得到公式10

           \sum_{1}^{t}R(x)\frac{P(at, st)}{Q(at,st)}\Delta logP(at|st) = \sum_{1}^{t}R(x)\frac{P(at | st)}{Q(at | st)}\Delta logP(at|st)                                                                                             公式10

                    \sum_{1}^{t}R(x)\frac{P(at | st)}{Q(at | st)}\Delta logP(at|st) = \sum_{1}^{t}R(x)\frac{\Delta P(at|st)}{Q(at | st)}                                                                                                                    公式11

我们由公式11可以推导出对应的loss函数,因为loss就对梯度进行求积分吗,所以直接把delta拿掉就可以了。可以得到公式12

                 \frac{1}{n}\sum R(x)\sum_{1}^{t} \Delta logP(at|st) = \frac{1}{n}\sum_{1}^{n}\sum_{1}^{t}R(x)\frac{P(at|st)}{Q(at|st)}                                                                                                                 公式12

价值回报

       我们对R(x)进行调整一下,R(x)变成和t相关的一个回报。什么意思呢,就是当前状态St下,我们采取了action at时对后续的各种状态的一个回报。公式如下:

        R(t) = r(t) + R(t+1) + ........

公式意义是,t 时刻得到的回报,等于 t 时刻的瞬时回报 r(t) 以及他采取了这个动作以后,后续的t +1时刻的瞬时回报,一直到后续的所有的瞬时回报。

然后我们加上一个折扣就变成了

                R(t) =r(t) + rR(t+1) + r2 * R(t+2) + r3 * R(t+3) ......

R(t) = r(t) + r(R(t+1))

又因为R(t+1) 等效于V(t+1),为什么呢?因为t + 1时刻的回报,实际上他的期望就是t +1时刻的动作价值,毕竟我们我们实际上就是要训练网络V 去 拟合 R。

所以 t 时刻的回报就变成了  r(t) + rV(t+1) 的值。而我们动作价值回报实际上就是 t 时刻的均值回报。然后我们 t 时刻的回报就变成了 R = r(t) + rV(t+1) - v(t) 这就是 t 时刻得到的价值差,也叫时序差分的概念。

R(t -1) = r(t-1) + r * (r(t) + r * v(t+1))  

上面的公式就是代码中的 compute_rewards的核心里面。

最后的公式就变成了 

\frac{1}{n}\sum_{1}^{n}\sum_{1}^{t}R(x)\frac{P(at|st)}{Q(at|st)} = \frac{1}{n}\sum_{1}^{n}\sum_{1}^{t}R(t)\frac{P(at|st)}{Q(at|st)}

为什么要用时序差分呢?因为实际上每个时刻预期得到的回报价值应该是不一样的,这样实际上比直接用总回报价值代替的好处就是可以减少方差。这个怎么理解呢?就好比我们从公司到家的时间,大部分的时间都是30分钟,但是其实每次回家的时候的这个时间都是不一样的,因此如果我们采用走一步看一步来预估时间肯定是最好的。这样上面的网络就变成了一个网络就可以了,就是网络在出这个P的时候,就可以直接出t时刻的价值就可以了。

价值估计

        在大模型的强化学习中,价值reward怎么计算呢?我们实际上会训练一个评分模型,也叫reward模型,这个模型就是对大模型出来的语句进行一次评分。这个就相当于我们玩游戏一样,我们不管中途经过什么手段,最终如果打败了boss,那么最后的评分就是100分。如果打失败了评分就是0分。但是我们每个句子只有最后的一个得分,我们怎么得到每个步骤也就是每个token的reward呢?在tcl的代码中如下所示。

        for score, logprob, ref_logprob, mask in zip(scores, logprobs, ref_logprobs, masks):
            # compute KL penalty (from difference in logprobs)
            kl = self._kl_penalty(logprob, ref_logprob)
            kls.append(kl)
            non_score_reward = -self.kl_ctl.value * kl
            non_score_rewards.append(non_score_reward)
            reward = non_score_reward.clone()
            last_non_masked_index = mask.nonzero()[-1]

            # reward is preference model score + KL penalty
            reward[last_non_masked_index] += score
            rewards.append(reward)

        从代码中可以看出来,实际上的rewards就是每个散度 k l 散度 乘以一个固定值就得到了他的价值,然后在最后的尾巴 加上一个最后的score 代表最后一个词得到的价值。作者也比较愚钝,我是这么理解这部分的内容的,每个token的概率和参考值之间之间的差值就是他的价值,为什么这么理解呢,因为我们是希望我们比按当前采样得到的token 在当前时刻的概率越高越好,因为概率越高代表当前模型对当前说出的话越肯定。就像我们如果在当下我们考试成绩比学校里的学霸好,那我们当然就会觉得越肯定自己,也就是价值越高嘛。然后只不过最后的高考的成绩只会定夺在最后一个时刻,所以最后时刻的价值就会是整个句子的价值了。

其他代码讲解

        剩下的实际上在代码中不是很重要,比如我们Q实际上就是一次采样后,多次训练前的一个模型即可。然后我们Q 和 P之间的比率不能太高,因为差距太远了我们就会认为当前可能存在模型大偏差所以要对他进行截断如下所示。

        pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - self.config.cliprange, 1.0 + self.config.cliprange)

然后 V = V + a * (r(t) + rV(t+1) - V(t))。 

最后的loss = pg_loss + v_loss

V_loss = V(模型在采样后的数据迭代更新后得到的值) - returns(也就是当前得到的回报值)

Transformer模型是一种基于自注意力机制的神经网络模型,用于处理序列数据。它在机器翻译、文本生成、语言模型等自然语言处理任务中表现出色。Transformer模型的主要特点是摒弃了循环神经网络(RNN)和卷积神经网络(CNN),采用自注意力机制来处理输入序列和输出序列之间的依赖关系。 关于PPO算法,它是近年来在深度强化学习领域中广受关注的一种算法PPO全称为Proximal Policy Optimization,是一种改进的策略梯度算法。相较于传统的策略梯度算法PPO在训练过程中加入了一些特殊的限制条件,以避免策略更新过大的问题。这些限制条件包括截断重要性采样比率和剪切近似优势函数等。PPO算法能够解决深度强化学习中的探索问题,并且在各类游戏和机器人控制等任务上都取得了不错的效果。 由于你提到了代码,我就简单介绍一下PPO算法代码实现PPO算法通常使用深度神经网络来表示策略函数,并使用Actor-Critic框架进行训练。代码实现中需要定义神经网络模型、损失函数、优化器等,并在每个时间步骤中计算出当前状态下的动作概率和价值函数。然后根据这些概率和价值函数计算出策略梯度和价值函数损失,并通过反向传播算法更新神经网络参数。具体实现细节可以参考深度强化学习相关的开源代码库,如OpenAI的Spinning Up。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值