You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

REINFORCE算法实现求助:CartPole任务无性能提升

问题:REINFORCE算法实现后CartPole-v0奖励不提升

我正尝试按照Sutton与Barto所著《强化学习导论》中的内容实现REINFORCE算法,经过数小时调试仍无法定位问题,自我感觉实现逻辑正确。但在CartPole-v0环境中,智能体的每回合累计奖励未随训练回合数提升,恳请技术帮助。

#Import libraries
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import gym
import matplotlib.pyplot as plt
import numpy as np
from torch.distributions import Categorical

class Actor(nn.Module):
    
    """
    Use a ANN to construct a parameterized policy

    Input layer: observation space length
    First hidden layer: 128
    ReLU activation
    second hidden layer: 256
    ReLU activation
    Output layer: action space length
    Softmax activation
    """
    
    def __init__(self, state_size, action_size):
        super(Actor, self).__init__()
        self.state_size = state_size
        self.action_size = action_size
        self.linear1 = nn.Linear(self.state_size,64)
        self.linear2 = nn.Linear(64, self.action_size)
        
        
    def forward(self,state):
        x = F.relu(self.linear1(state))
        x = self.linear2(x)
        x = F.softmax(x, dim=-1)
        return x
    
    def select_action(self,state):
        
        #convert state to float tensor, add 1 dimension, allocate tensor on device
        state = torch.from_numpy(state).float().unsqueeze(0).to(device)
        action_probs = self.forward(state)
        #sample an action using the probability distribution
        dist = Categorical(action_probs)
        action = dist.sample()
        
        return action.item(), dist.log_prob(action)
    
    

device = "cuda" if torch.cuda.is_available() else "cpu" #Set device
episodes = 1000
step = 0
lr = 0.005
gamma = 0.9999
env = gym.make('CartPole-v0') # import cartpole environment
env.seed(0)
"""
The cartpole environment has an observation space of length 4
1) Cart Position, 2) Cart Velocity, 3) Pole Angle, 4) Pole Angular Velocity

The action space is of length two
1) Push cart to the left, Push cart to the right
"""
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
actor = Actor(state_size, action_size).to(device)
optimizer_actor = optim.Adam(actor.parameters(), lr = lr) # import optimizer

for i in range(episodes):
    #Loop forever

    state = env.reset() # Initialize S
    cumulative_reward = 0
    done = False
    actions,states, rewards = [], [], []
    #print(1)
    while not done:
        # Generate episode S0, A0, R1,.. ,S_{T-1}, A_{T-1},RT, following pi(·|·, theta)
        
        #get action and log probability
        action, logp = actor.select_action(state)
        
        state, reward, done, _ = env.step(action) #take action in environment
        rewards.append(reward)
        actions.append(action)
        states.append(state)
        cumulative_reward +=reward
        #Store episode trajectory
        
    discounted_returns = []
    for t in range(len(rewards)):
        G = 0.0
        for k, r in enumerate(rewards[t:]):
            G += (gamma**k)*r
        discounted_returns.append(G)
    
    for state, action, G in zip(states, actions, discounted_returns):
        
        
        state = torch.from_numpy(state).float().unsqueeze(0).to(device)
        probs = actor(state)
        dist = Categorical(probs=probs)    
        action = torch.tensor(action, dtype=torch.int).to(device)
        logp = dist.log_prob(action)
        loss = - logp*G
        
        
        optimizer_actor.zero_grad()
        loss.backward()
        optimizer_actor.step()
        
    
    print('Episode {} Score: {:.2f}'.format(i, cumulative_reward))

问题分析与修复方案

你的代码存在几个关键问题,导致奖励无法提升:

1. 状态存储错误

回合循环中,你执行env.step(action)后才把状态存入states列表,但REINFORCE需要的是执行动作前的状态,而非动作后的状态。这会导致策略梯度计算时使用错误状态,完全偏离正确更新方向。

修复:在执行动作前保存当前状态

while not done:
    states.append(state)  # 先存动作前的状态
    action, logp = actor.select_action(state)
    state, reward, done, _ = env.step(action)
    rewards.append(reward)
    actions.append(action)
    cumulative_reward += reward

2. 梯度更新时机错误

当前对每个状态-动作对单独执行optimizer.step(),相当于单样本更新,训练稳定性极差。正确做法是累加整个回合的损失,再统一执行一次反向传播和优化。

修复后的更新逻辑:

total_loss = 0.0
for state, action, G in zip(states, actions, discounted_returns):
    state = torch.from_numpy(state).float().unsqueeze(0).to(device)
    probs = actor(state)
    dist = Categorical(probs=probs)    
    action = torch.tensor(action, dtype=torch.int64).to(device)  # 用int64避免类型错误
    logp = dist.log_prob(action)
    total_loss += -logp * G

optimizer_actor.zero_grad()
total_loss.backward()
optimizer_actor.step()

3. 超参数不合理

  • lr=0.005过大,容易导致梯度震荡,建议调低至0.001或0.0005
  • gamma=0.9999接近1,会让折扣回报的方差极大,建议改为0.99或0.95平衡偏差与方差

4. 折扣回报计算效率优化(非核心但建议)

原嵌套循环计算折扣回报的时间复杂度为O(T²),可改为反向遍历的O(T)方式:

discounted_returns = []
G = 0.0
for r in reversed(rewards):
    G = r + gamma * G
    discounted_returns.insert(0, G)

修复后的完整代码
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import gym
import numpy as np
from torch.distributions import Categorical

class Actor(nn.Module):
    def __init__(self, state_size, action_size):
        super(Actor, self).__init__()
        self.state_size = state_size
        self.action_size = action_size
        self.linear1 = nn.Linear(self.state_size, 64)
        self.linear2 = nn.Linear(64, self.action_size)
        
    def forward(self, state):
        x = F.relu(self.linear1(state))
        x = self.linear2(x)
        x = F.softmax(x, dim=-1)
        return x
    
    def select_action(self, state):
        state = torch.from_numpy(state).float().unsqueeze(0).to(device)
        action_probs = self.forward(state)
        dist = Categorical(action_probs)
        action = dist.sample()
        return action.item(), dist.log_prob(action)

device = "cuda" if torch.cuda.is_available() else "cpu"
episodes = 1000
lr = 0.001  # 调低学习率
gamma = 0.99  # 调整gamma
env = gym.make('CartPole-v0')
env.seed(0)

state_size = env.observation_space.shape[0]
action_size = env.action_space.n
actor = Actor(state_size, action_size).to(device)
optimizer_actor = optim.Adam(actor.parameters(), lr=lr)

for i in range(episodes):
    state = env.reset()
    cumulative_reward = 0
    done = False
    actions, states, rewards = [], [], []
    
    while not done:
        states.append(state)  # 保存动作前的状态
        action, logp = actor.select_action(state)
        state, reward, done, _ = env.step(action)
        rewards.append(reward)
        actions.append(action)
        cumulative_reward += reward
    
    # 高效计算折扣回报
    discounted_returns = []
    G = 0.0
    for r in reversed(rewards):
        G = r + gamma * G
        discounted_returns.insert(0, G)
    
    # 累加整个回合的损失
    total_loss = 0.0
    for state, action, G in zip(states, actions, discounted_returns):
        state = torch.from_numpy(state).float().unsqueeze(0).to(device)
        probs = actor(state)
        dist = Categorical(probs=probs)
        action = torch.tensor(action, dtype=torch.int64).to(device)
        logp = dist.log_prob(action)
        total_loss += -logp * G
    
    # 统一更新梯度
    optimizer_actor.zero_grad()
    total_loss.backward()
    optimizer_actor.step()
    
    print('Episode {} Score: {:.2f}'.format(i, cumulative_reward))

修改后,智能体的回合奖励应随训练逐步提升,最终稳定达到CartPole-v0的最大奖励(200)。

内容的提问来源于stack exchange,提问作者Sotiris Stamnas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 14:36:02