用PyTorch和PPO训练AI玩超级马里奥,我踩过的那些版本兼容的坑(附完整代码)

张开发
2026/4/16 7:27:59 15 分钟阅读

分享文章

用PyTorch和PPO训练AI玩超级马里奥,我踩过的那些版本兼容的坑(附完整代码)
用PyTorch和PPO训练AI玩超级马里奥从环境配置到实战调优全指南当经典游戏遇上现代强化学习算法会碰撞出怎样的火花最近我在尝试用PyTorch实现PPO算法训练AI玩《超级马里奥兄弟》时发现这个看似简单的项目背后隐藏着不少暗礁。从gym到gymnasium的版本迁移从环境封装到奖励函数设计每一步都可能让你在复现过程中踩坑。本文将分享一套经过实战验证的完整解决方案包含环境配置、算法实现和调优技巧助你避开我走过的弯路。1. 环境配置避开版本兼容的雷区在开始编码之前正确的环境配置是项目成功的第一步。我最初直接复制了某GitHub仓库的requirements.txt结果陷入了无尽的依赖冲突中。以下是经过反复验证的稳定环境配置方案# 关键依赖版本清单 Python 3.11.7 PyTorch 2.0.1cu118 gym 0.23.0 # 注意不是gymnasium nes_py 8.1.8 gym_super_mario_bros 7.3.0 opencv-python 4.8.1注意虽然OpenAI的gym已停止维护但当前super_mario_bros仍基于gym开发。直接使用gymnasium会导致包装器接口不兼容。常见的版本冲突问题及解决方案问题1AttributeError: TimeLimit object has no attribute reward_range原因gym版本过高0.23.0与nes_py不兼容解决降级到gym 0.23.0问题2TypeError: __init__() got an unexpected keyword argument new_step_api原因尝试使用gymnasium的API调用gym环境解决统一使用gym 0.23.0的接口规范问题3RuntimeError: Expected 4D input for 4D weight...原因PyTorch版本差异导致张量维度检查更严格解决确保输入张量形状为(batch_size, channels, height, width)2. 环境封装打造适合RL训练的游戏界面原始的马里奥环境直接输出RGB图像这会导致训练效率低下。我们需要通过多层封装将其转化为适合强化学习的格式class ProcessFrameWrapper(gym.ObservationWrapper): def __init__(self, env): super().__init__(env) self.observation_space gym.spaces.Box(low0, high255, shape(84, 84, 1), dtypenp.uint8) def observation(self, obs): # 转换为灰度图并调整尺寸 obs cv2.cvtColor(obs, cv2.COLOR_RGB2GRAY) obs cv2.resize(obs, (84, 84), interpolationcv2.INTER_AREA) return np.expand_dims(obs, axis-1) class FrameStackWrapper(gym.Wrapper): def __init__(self, env, num_frames4): super().__init__(env) self.num_frames num_frames self.frames deque(maxlennum_frames) self.observation_space gym.spaces.Box( low0, high255, shape(num_frames, 84, 84), dtypenp.uint8 ) def reset(self): obs self.env.reset() for _ in range(self.num_frames): self.frames.append(obs) return self._get_obs() def step(self, action): obs, reward, done, info self.env.step(action) self.frames.append(obs) return self._get_obs(), reward, done, info def _get_obs(self): return np.stack(self.frames, axis0)关键封装技术说明封装技术作用参数优化建议灰度处理减少输入维度使用cv2.COLOR_RGB2GRAY转换尺寸调整统一输入规格84x84是经典尺寸平衡信息保留与计算量帧堆叠提供时序信息4帧堆叠效果最佳过多会导致动作延迟3. PPO算法实现核心代码解析PPO(Proximal Policy Optimization)是目前最流行的on-policy强化学习算法之一。以下是针对马里奥游戏优化的实现class PPONetwork(nn.Module): def __init__(self, input_shape, n_actions): super().__init__() self.conv nn.Sequential( nn.Conv2d(input_shape[0], 32, kernel_size8, stride4), nn.ReLU(), nn.Conv2d(32, 64, kernel_size4, stride2), nn.ReLU(), nn.Conv2d(64, 64, kernel_size3, stride1), nn.ReLU() ) conv_out_size self._get_conv_out(input_shape) self.actor nn.Sequential( nn.Linear(conv_out_size, 512), nn.ReLU(), nn.Linear(512, n_actions) ) self.critic nn.Sequential( nn.Linear(conv_out_size, 512), nn.ReLU(), nn.Linear(512, 1) ) def _get_conv_out(self, shape): o self.conv(torch.zeros(1, *shape)) return int(np.prod(o.size())) def forward(self, x): conv_out self.conv(x).view(x.size()[0], -1) return self.actor(conv_out), self.critic(conv_out) class PPOAgent: def __init__(self, env, lr3e-4, gamma0.99, gae_lambda0.95, clip_epsilon0.2, batch_size64, n_epochs10): self.env env self.net PPONetwork(env.observation_space.shape, env.action_space.n).float() self.optimizer optim.Adam(self.net.parameters(), lrlr) self.gamma gamma self.gae_lambda gae_lambda self.clip_epsilon clip_epsilon self.batch_size batch_size self.n_epochs n_epochs def compute_gae(self, rewards, values, dones): # GAE计算实现 pass def update(self, samples): # PPO核心更新逻辑 pass def train(self, total_timesteps1e6): # 训练循环实现 passPPO参数设置经验值学习率3e-4Actor和Critic可分别设置折扣因子γ0.99适用于长周期奖励GAE参数λ0.95平衡偏差与方差Clip范围ε0.2防止策略更新过大Batch大小64根据显存调整训练轮数10每次采样数据后更新次数4. 实战调优让马里奥真正学会闯关即使算法实现正确直接训练也很难让马里奥通关。以下是经过验证的调优技巧奖励函数设计原始环境的奖励信号过于稀疏需要精心设计class CustomRewardWrapper(gym.Wrapper): def __init__(self, env): super().__init__(env) self.current_score 0 self.current_x 0 self.max_x 0 def step(self, action): state, reward, done, info self.env.step(action) # 基础奖励 reward info[x_pos] - self.current_x # 向右移动奖励 self.current_x info[x_pos] # 特别事件奖励 if info[flag_get]: reward 500 # 通关大奖 elif info[life] 2: reward - 100 # 死亡惩罚 # 进度奖励 if info[x_pos] self.max_x: reward 10 * (info[x_pos] - self.max_x) self.max_x info[x_pos] return state, reward / 10.0, done, info训练技巧课程学习先从简单关卡开始1-1再逐步增加难度动作空间优化使用SIMPLE_MOVEMENT而非COMPLEX_MOVEMENT帧跳过每4帧执行一次动作平衡反应速度与训练效率早停机制当连续100步x位置无变化时终止episode可视化监控使用TensorBoard记录关键指标from torch.utils.tensorboard import SummaryWriter writer SummaryWriter() for episode in range(num_episodes): # ...训练逻辑... writer.add_scalar(Reward/episode, episode_reward, episode) writer.add_scalar(Position/max_x, max_x, episode)当你的马里奥开始在1-1关卡稳定通过时可以尝试以下进阶优化引入LSTM处理长时序依赖添加好奇心驱动探索(ICM)使用分布式PPO加速训练这个项目最让我意外的发现是即使使用相同的算法和参数设置不同的随机种子可能导致完全不同的训练结果。有次马里奥在200episode就学会了跳跃障碍而另一次训练了1000episode仍然在第一个坑前徘徊。这提醒我们在强化学习实践中耐心和多次尝试往往比调参更重要。

更多文章