PPO算法在MountainCarContinuous-v0环境中的实战调优

1. 从零理解MountainCarContinuous-v0与PPO算法

如果你刚开始接触强化学习,看到MountainCarContinuous-v0这个环境名可能会有点懵。简单来说,这就是一个“小车爬山”的模拟游戏。想象一下,你的车被困在一个U型山谷的底部,发动机马力不够,没法直接冲上右边的山顶。你的任务就是通过控制油门(给一个向左或向右的加速度),让小车通过左右摆动积累动量,最终“荡”到山顶。这个环境的难点在于,小车自身的动力不足以直接克服重力爬坡,必须学会“荡秋千”的策略,这非常考验智能体的探索和学习能力。

为什么这个环境在强化学习社区里这么经典?因为它有几个典型的挑战:稀疏奖励连续动作空间。所谓稀疏奖励,就是小车在到达山顶之前,每一步得到的奖励都是负数(比如-0.1),只有成功登顶才会获得一个大的正奖励(比如+100)。这就像在茫茫大海里找一座小岛,大部分时间你都在“受罚”,只有找到目标才能“得救”,学习信号非常微弱。而连续动作空间意味着,你的控制输出不是一个简单的“左”或“右”的离散指令,而是一个在[-1, 1]区间内任意取值的实数,代表施加的力的大小和方向。这比离散动作复杂得多。

为了解决这类问题,我们请出了今天的主角:PPO(近端策略优化)算法。PPO是OpenAI在2017年提出的一种策略梯度算法,它之所以流行,核心在于两个字:“稳定”。在它之前,很多策略梯度方法像一匹难以驯服的野马,学习过程容易崩溃,参数更新步长稍微大一点,性能就可能一落千丈。PPO通过一个巧妙的“裁剪”机制,把每次策略更新的幅度限制在一个安全的范围内,从而保证了训练的稳定性。对于MountainCarContinuous-v0这种需要精细控制力度的环境,PPO的稳定特性让它成为了一个非常合适的选择。接下来,我们就一起动手,看看如何把这匹“好马”驯服,让它在这个经典环境里跑出好成绩。

2. 搭建你的第一个PPO智能体:代码骨架详解

纸上谈兵终觉浅,咱们直接上代码。下面我会带你一步步搭建PPO智能体的核心部分,并解释每一块“积木”的作用。别担心,我会尽量用大白话把原理讲清楚。

首先,智能体的大脑由两个神经网络组成:Actor(演员)Critic(评论家)。你可以把Actor想象成赛车手,它负责做决策,根据当前的路况(状态)决定踩多大油门(动作)。而Critic则像副驾驶的教练,它不直接开车,而是评价赛车手当前所处位置的价值有多高,为后续的策略更新提供指导。

我们先来看看Actor网络是怎么构建的。它的输入是小车的状态(位置和速度两个数字),输出是一个动作分布。在连续动作空间里,我们通常假设这个分布是高斯分布(正态分布),所以网络需要输出两个东西:动作的均值(mu)和标准差(std)。均值决定了最可能采取的动作,标准差则代表了探索的随机性大小。一开始标准差可以大一些,让小车多尝试各种动作;随着学习进行,标准差会逐渐减小,策略趋于稳定。

import torch
import torch.nn as nn
import torch.nn.functional as F

def orthogonal_init(layer, gain=1.0):
    """正交初始化,一个小技巧,能让网络训练更稳定"""
    nn.init.orthogonal_(layer.weight, gain=gain)
    nn.init.constant_(layer.bias, 0)

class Actor(nn.Module):
    def __init__(self, state_dim, action_dim):
        super(Actor, self).__init__()
        self.fc1 = nn.Linear(state_dim, 128)
        self.fc2 = nn.Linear(128, 128)
        self.mu_head = nn.Linear(128, action_dim)
        # 将log_std定义为可学习参数,确保标准差总是正数
        self.log_std = nn.Parameter(torch.zeros(action_dim))

        # 使用正交初始化
        orthogonal_init(self.fc1)
        orthogonal_init(self.fc2)
        orthogonal_init(self.mu_head, gain=0.01) # 输出层增益小一点

    def forward(self, x):
        x = torch.tanh(self.fc1(x))
        x = torch.tanh(self.fc2(x))
        mu = torch.tanh(self.mu_head(x))  # 用tanh把均值限制在[-1,1]之间,符合动作空间要求
        std = torch.exp(self.log_std)     # 取指数,得到正的标准差
        return mu, std

    def get_dist(self, state):
        """根据状态生成动作分布"""
        mean, st
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值