[强化学习-2] 机器人找金币例子

目的

本文介绍一个简单的案例。目标如下:
image.png
我们的机器人在上面的地图上行走,目的是要找到宝藏,如果进入骷髅头,游戏就失败。

定义状态空间

我们对地图上的小方格进行编号: 1 - 8
image.png
代码简单表示如下:

self.states = [1,2,3,4,5,6,7,8] #状态空间

同时定义终止状态:

self.terminate_states = dict()  #终止状态为字典格式
self.terminate_states[6] = 1
self.terminate_states[7] = 1
self.terminate_states[8] = 1

定义动作空间

动作空间很简单:上,下,左,右

self.actions = ['n','e','s','w']

定义回报函数

self.rewards = dict();        #回报的数据结构为字典
self.rewards['1_s'] = -1.0
self.rewards['3_s'] = 1.0
self.rewards['5_s'] = -1.0

构造我们的环境,目的如下:

(如果不知道怎么用 gym 画图,请跳转:https://www.jianshu.com/p/b3c4d2b95c58)
代码如下:

    def render(self, mode='human', close=False):
        # 创建地图
        lines = []
        line1 = rendering.Line((0, 0), (0, 200))
        lines.append(line1)
        line2 = rendering.Line((120, 0), (120, 200))
        lines.append(line2)
        line3 = rendering.Line((0, 200), (600, 200))
        lines.append(line3)
        line4 = rendering.Line((0, 100), (600, 100))
        lines.append(line4)
        line5 = rendering.Line((240, 0), (240, 200))
        lines.append(line5)
        line6 = rendering.Line((360, 0), (360, 200))
        lines.append(line6)
        line7 = rendering.Line((480, 0), (480, 200))
        lines.append(line7)
        line8 = rendering.Line((600, 0), (600, 200))
        lines.append(line8)
        line9 = rendering.Line((0, 0), (120, 0))
        lines.append(line9)
        line10 = rendering.Line((240, 0), (360, 0))
        lines.append(line10)
        line11 = rendering.Line((480, 0), (600, 0))
        lines.append(line11)
        # 创建骷髅
        kulos = []
        kulo1 = rendering.make_circle(40)
        kulo1_transiform = rendering.Transform(translation=(60, 50))
        kulo1.add_attr(kulo1_transiform)
        kulo1.set_color(0, 0, 0)
        kulos.append(kulo1)

        kulo2 = rendering.make_circle(40)
        kulo2_transiform = rendering.Transform(translation=(540, 50))
        kulo2.add_attr(kulo2_transiform)
        kulo2.set_color(0, 0, 0)
        kulos.append(kulo2)

        # 创建宝藏
        golds = []
        gold = rendering.make_circle(40)
        circletrans = rendering.Transform(translation=(300, 50))
        gold.add_attr(circletrans)
        gold.set_color(1, 0.9, 0)
        golds.append(gold)

        # 创建机器人
        robots = []
        robot = rendering.make_circle(40)
        robot.set_color(0.8, 0.6, 0.4)
        robot_transilation = rendering.Transform(translation=(420, 150))
        robot.add_attr(robot_transilation)
        robots.append(robot)

        transiform = rendering.Transform(translation=(50, 50))
        for line_ in lines:
            line_.set_color(0, 0, 0)
            line_.add_attr(transiform)
            self.viewer.add_geom(line_)
        for kulo_ in kulos:
            kulo_.add_attr(transiform)
            self.viewer.add_geom(kulo_)
        for gold_ in golds:
            gold_.add_attr(transiform)
            self.viewer.add_geom(gold_)
        for robot_ in robots:
            robot_.add_attr(transiform)
            self.viewer.add_geom(robot_)

        return self.viewer.render(return_rgb_array=mode == 'rgb_array')

结果

本例子用的算法是DQN, 读者可以先不管DQN是什么

test.gif

全部代码:https://github.com/WinddyAkoky/reinformence-learning/tree/master

<think>我们正在讨论PPO(近端策略优化)的概念和原理,目标是让初学者也能理解。我会结合之前的Actor-Critic架构来讲解,因为PPO是基于Actor-Critic的一种改进算法。 首先,回顾一下Actor-Critic架构: - Actor负责选择动作(策略) - Critic负责评估状态或动作的价值 但是,传统的策略梯度方法(包括Actor-Critic)在训练时存在一个问题:策略更新步长过大可能导致性能崩溃(即策略突然变得很差)。PPO就是为了解决这个问题而提出的。 PPO的核心思想:限制每次策略更新的幅度,确保新策略不会偏离旧策略太远。这样就能稳定训练。 我们可以通过一个比喻来理解: 假设你在学习骑自行车(Actor),你的朋友(Critic)在旁边指导你。如果朋友每次都说“你应该大幅度向左转”或“大幅度向右转”,你可能会摔倒。而PPO就像是一个谨慎的朋友,每次只建议你微调方向,这样你就能平稳地学会骑车。 PPO的数学原理: 它使用一个“裁剪”的替代目标函数(clipped surrogate objective)来限制策略更新的幅度。 定义新旧策略的比率: $$ r_t(\theta) = \frac{\pi_{\theta}(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)} $$ 其中,$\theta$是策略参数,$\pi_{\theta}(a_t|s_t)$是新策略选择动作$a_t$的概率,$\pi_{\theta_{\text{old}}}(a_t|s_t)$是旧策略选择该动作的概率。 传统的策略梯度方法会直接最大化这个比率的期望乘以优势函数(由Critic估计): $$ L^{\text{CPI}}(\theta) = \mathbb{E}_t \left[ r_t(\theta) A_t \right] $$ 其中,$A_t$是优势函数(表示动作比平均情况好多少)。 PPO则修改目标函数,加入裁剪操作: $$ L^{\text{CLIP}}(\theta) = \mathbb{E}_t \left[ \min\left( r_t(\theta) A_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t \right) \right] $$ 这里,$\epsilon$是一个超参数(通常取0.1或0.2),用于限制比率的变化范围。具体来说,clip函数将比率限制在$[1-\epsilon, 1+\epsilon]$之间。 这个目标函数的设计意图是: - 当优势函数$A_t$为正(即动作比平均好)时,我们希望增加该动作的概率,但通过clip操作限制比率不超过$1+\epsilon$,从而避免更新幅度过大。 - 当优势函数$A_t$为负(即动作比平均差)时,我们希望减小该动作的概率,但通过clip操作限制比率不低于$1-\epsilon$。 这样,策略更新就被限制在一个安全的范围内,从而避免了训练的不稳定。 举个例子:训练一个机器人走路 状态:机器人的关节角度、速度等 动作:给关节施加的扭矩 奖励:机器人前进的距离(越远奖励越高) 使用PPO算法: 1. 初始化策略(Actor)和价值函数(Critic) 2. 收集数据:用当前策略让机器人在环境中交互,得到一系列状态、动作、奖励 3. 计算每个动作的优势函数$A_t$(使用Critic) 4. 计算新旧策略的比率$r_t(\theta)$ 5. 计算裁剪后的目标函数$L^{\text{CLIP}}(\theta)$ 6. 更新策略参数$\theta$以最大化$L^{\text{CLIP}}(\theta)$ 7. 更新Critic(例如最小化价值函数的均方误差) 8. 重复2-7步直到收敛 为什么PPO如此成功? - 它结合了策略梯度方法的灵活性和稳定性 - 相比其他约束策略更新的方法(如TRPO),PPO实现更简单,计算效率更高 - 在实践中表现鲁棒,成为许多强化学习任务的首选算法 引用说明:PPO首次亮相于论文《Proximal Policy Optimization Algorithms》[^1],并且被广泛应用于各种领域,包括大型语言模型(LLM)的优化。 下面是一个简化的PPO伪代码示例(帮助理解): ```python import torch import torch.optim as optim # 定义策略网络(Actor)和价值网络(Critic) policy_net = PolicyNetwork() value_net = ValueNetwork() optimizer = optim.Adam(list(policy_net.parameters()) + list(value_net.parameters()), lr=0.001) # 超参数 epsilon = 0.2 # 裁剪范围 epochs = 10 # 每次数据收集后更新次数 for episode in range(total_episodes): # 收集数据(状态、动作、奖励、下一个状态) states, actions, rewards, next_states = collect_trajectories(env, policy_net) # 计算优势函数和回报(使用Critic) advantages = compute_advantages(rewards, value_net, states, next_states) # 计算旧策略的概率(用于比率) old_probs = policy_net.get_prob(states, actions).detach() # 不计算梯度 # 更新多个epoch for _ in range(epochs): # 计算新策略的概率 new_probs = policy_net.get_prob(states, actions) ratios = new_probs / old_probs # 比率 # 裁剪目标函数 surr1 = ratios * advantages surr2 = torch.clamp(ratios, 1-epsilon, 1+epsilon) * advantages policy_loss = -torch.min(surr1, surr2).mean() # 价值函数损失(Critic) values = value_net(states) value_loss = torch.mean((values - rewards) ** 2) # 总损失(通常加上一个熵正则项以鼓励探索) total_loss = policy_loss + value_loss # 梯度下降 optimizer.zero_grad() total_loss.backward() optimizer.step() ``` 注意:实际实现会更复杂,包括并行采样、归一化优势函数等,但以上代码展示了核心思想。 总结PPO的三大优势: 1. **稳定性**:通过裁剪避免破坏性的大步更新 2. **高效性**:比TRPO等约束方法计算更简单 3. **通用性**:适用于离散和连续动作空间 希望这个解释和示例能帮助你理解PPO!如果你有更多问题,欢迎继续提问。</think>### PPO(近端策略优化)原理详解(小白版) #### 一、核心概念:**安全更新策略** 想象教机器人走路: - 传统方法:直接给新指令"步子迈大50%",可能摔倒💥 - **PPO方法**:谨慎建议"步子最多增大20%",避免危险动作[^1] ```mermaid graph LR A[旧策略] -->|安全更新范围| B[新策略] B -->|稳定进步| C[更好性能] ``` #### 二、解决的核心问题 传统策略梯度方法的缺陷: 1. **步长过大**:一次更新可能让策略崩溃(如从80分变成30分) 2. **样本效率低**:需要大量新数据才能更新策略 > 💡 **PPO突破**:用数学约束确保每次更新都在"安全区"[^1] #### 三、核心原理(三步走) 1. **收集数据** - 用当前策略玩游戏(如超级马里奥) - 记录:状态(画面)、动作(跳跃)、奖励(吃到金币+1) 2. **计算优势** - 关键公式:$优势值 = 实际奖励 - 预期奖励$ $$ A_t = R_t - V(s_t) $$ - 举例:预期得1分但实际得3分 → 优势值=+2(好动作!) 3. **安全更新策略**(核心创新) - 限制新旧策略差异: $$ r_t(\theta) = \frac{\text{新策略动作概率}}{\text{旧策略动作概率}} $$ - 裁剪约束:强制 $ r_t(\theta) $ 在 $ [1-\epsilon, 1+\epsilon] $ 之间(通常 $\epsilon=0.2$) $$ L^{CLIP} = \min\left( r_t(\theta)A_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)A_t \right) $$ #### 四、通俗示例:训练AI玩贪吃蛇 | 步骤 | 传统方法风险 | PPO解决方案 | |------|--------------|------------| | 初始策略 | 直走概率80% | 保持相同策略 | | 发现右侧有食物 | 突然改为右转概率100% → 可能撞墙 | 限制调整:右转概率最多增至90% | | 更新后结果 | 可能撞墙死亡 | 安全吃到食物 | > ✅ **PPO优势**:既吃到食物(提升性能),又避免撞墙(保持稳定) #### 五、关键创新:裁剪机制图解 ```mermaid graph LR A[优势值A>0 好动作] --> B{更新幅度检查} B -->|r<1+ε| C[正常放大动作概率] B -->|r>1+ε| D[强制按1+ε上限放大] E[优势值A<0 坏动作] --> F{更新幅度检查} F -->|r>1-ε| G[正常减小动作概率] F -->|r<1-ε| H[强制按1-ε下限减小] ``` #### 六、为什么PPO如此成功? 1. **训练稳定性**:避免策略崩溃(尤其重要任务如无人机控制) 2. **样本高效**:同一批数据可多次重复使用(传统方法只能用一次) 3. **通用性强**:适用于游戏AI、机器人控制、金融交易等[^1] 4. **实现简单**:比TRPO(其前身)计算复杂度低 #### 七、代码逻辑示意(伪代码) ```python # 超参数设置 CLIP_EPSILON = 0.2 # 安全更新范围±20% for 更新轮次 in range(1000): # 1. 用当前策略收集数据 states, actions, rewards = env.play_with_current_policy() # 2. 计算优势值(Critic评估) advantages = calculate_advantages(rewards, critic_net) # 3. 计算新旧策略概率比 old_probs = get_old_action_prob(states, actions) new_probs = policy_net(states, actions) ratios = new_probs / old_probs # r(θ) # 4. 裁剪目标函数 surr1 = ratios * advantages surr2 = torch.clamp(ratios, 1-CLIP_EPSILON, 1+CLIP_EPSILON) * advantages policy_loss = -torch.min(surr1, surr2).mean() # 负号因为要最大化 # 5. 更新网络 optimizer.zero_grad() policy_loss.backward() optimizer.step() ``` #### 八、典型应用场景 1. **游戏AI训练**:Dota2、星际争霸等复杂游戏(OpenAI Five) 2. **机器人控制**:机械臂抓取(避免突然剧烈动作) 3. **自动驾驶**:平稳转向决策(防止急转弯) 4. **对话系统**:生成连贯自然的回复(避免逻辑跳跃) > 🌟 **核心价值**:PPO在性能提升和训练稳定性间取得完美平衡,成为强化学习领域最流行算法之一[^1] --- ### 相关问题 1. PPO中的裁剪系数$\epsilon$如何影响训练效果?不同任务中该如何设置? 2. 如何理解PPO与TRPO(信赖域策略优化)的区别与联系? 3. 在PPO实现中,Critic网络(价值函数)具体如何训练? 4. PPO如何处理连续动作空间(如机器人关节角度控制)? 5. 为什么PPO特别适合大规模分布式训练?它在ChatGPT训练中起什么作用?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WinddyAkoky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值