返回

解决 Stable Baselines 'MIN_LEVEL' 报错: Gym 版本冲突与方案

Ai

踩坑指南:解决 Stable Baselines 运行报错 module 'gym.logger' has no attribute 'MIN_LEVEL'

写强化学习代码的时候,用 stable-baselines 这个库还挺顺手的。但有时候,冷不丁就会遇到一些奇奇怪怪的报错,比如这个 'gym.logger' has no attribute 'MIN_LEVEL'。今天咱们就来盘一盘这个错误,看看它到底是何方神圣,又该怎么解决。

问题在哪?

当你兴致勃勃地想用 stable-baselines 跑个 PPO 或者 DDPG 之类的算法时,可能刚初始化模型,比如像下面这样:

import gym
from stable_baselines.common.policies import MlpPolicy
from stable_baselines.common import make_vec_env
from stable_baselines import PPO2 # 或者其他算法,比如 A2C, DDPG 等

# 创建环境,这里用 CartPole 做例子
env = make_vec_env('CartPole-v1', n_envs=4)

# 初始化模型,问题通常就出在这!
model = PPO2(MlpPolicy, env, verbose=1)

# 下面的学习、保存、加载代码暂时还跑不到
# model.learn(total_timesteps=25000)
# model.save("ppo2_cartpole")
# ...

啪!程序就可能给你甩来一脸错误信息,关键部分长这样:

Traceback (most recent call last):
  ... # 中间一堆调用栈信息
  File "~/miniconda3/envs/tf15/lib/python3.7/site-packages/stable_baselines/common/base_class.py", line 1129, in __enter__
    self.gym_level = gym.logger.MIN_LEVEL
AttributeError: module 'gym.logger' has no attribute 'MIN_LEVEL'

看到 AttributeError 就头大,意思是 gym.logger 这个模块下面,根本就没有 MIN_LEVEL 这个属性或者变量。这就奇怪了,stable-baselines 官方示例代码都可能出错,难道是打开方式不对?

为啥会出现这个错误?(原因分析)

这问题十有八九是 库版本冲突 造成的,特别是 stable-baselinesgym 这两个库之间的爱恨情仇。

咱们来捋一捋:

  1. stable-baselines 的年代: 你遇到的这个 stable-baselines (注意,不是 stable-baselines3) 是一个相对较老的库。它主要设计用来和 TensorFlow 1.x 配合使用(就像问题里提到的 TF 1.15.0)。这个库在开发的时候,依赖的是当时主流的 gym 版本。

  2. gym 的变迁: OpenAI 的 gym 库也在不断发展。在某个版本(具体来说是 0.21.0 版本 左右)之后,gym 进行了一次比较大的 API 更新和内部结构调整。其中就包括了日志(logging)系统的修改。在旧版本的 gym 中,gym.logger 下面确实有一个叫做 MIN_LEVEL 的东西,用来控制日志输出的级别。

  3. 冲突点: stable-baselines 的代码(特别是内部用于控制日志输出详细程度的 SetVerbosity 类)是按照旧版 gym 的 API 来写的。它期望能找到 gym.logger.MIN_LEVEL。但是,如果你环境里安装的 gym 是一个比较新的版本 (比如 >= 0.21.0),那么这个 MIN_LEVEL 可能已经被移除或者改名了。这就导致了 AttributeError —— 你想找的东西,在新版 gym 里已经没了!

简单说就是:老库(stable-baselines)想用老接口(gym.logger.MIN_LEVEL),但你装了个新库(新版 gym),新库把老接口给改了。卒。

特别是当你使用 Conda 或 Pip 安装 stable-baselines 时,包管理器可能会自动帮你安装最新版本的 gym 作为依赖,这就很容易触发这个版本不兼容的问题。

怎么解决?(解决方案)

知道了原因,解决起来就有方向了。主要是围绕着让 stable-baselinesgym 的版本匹配起来。

方案一:降低 Gym 版本(推荐)

这是最直接也通常是最稳妥的方法。既然 stable-baselines 需要旧版的 gym API,那咱们就给它装个旧版的 gym

  • 原理:
    安装一个与你使用的 stable-baselines 版本兼容的 gym 版本(通常是 0.21.0 之前的版本,比如 0.20.0 或更早)。这样 stable-baselines 代码就能找到它需要的 gym.logger.MIN_LEVEL 了。

  • 操作步骤:

    1. 卸载当前 Gym:
      在你的 Conda 环境(或虚拟环境)中,先卸载掉可能存在的新版本 gym

      pip uninstall gym gymnasium # gymnasium 也可能装了,顺手卸了
      # 或者如果用 conda 安装的
      # conda remove gym gymnasium
      
    2. 安装指定版本的 Gym:
      安装一个明确的、较旧的版本。0.20.0 是一个比较常见的选择,通常能与 stable-baselines 配合良好。

      pip install gym==0.20.0
      

      或者尝试安装明确小于 0.21.0 的版本约束:

      pip install "gym<0.21.0"
      

      你可以先试试 0.20.0,如果不行再尝试更早的版本,比如 0.19.0

    3. 验证安装:
      可以检查一下 gym 的版本是否确实降级成功了。

      pip show gym
      

      输出信息里应该显示 Version: 0.20.0 (或者你安装的其他旧版本)。

  • 代码示例:
    完成上述降级操作后,你之前报错的代码应该就能正常运行了:

    import gym
    from stable_baselines.common.policies import MlpPolicy
    from stable_baselines.common import make_vec_env
    from stable_baselines import PPO2
    
    print(f"Using Gym version: {gym.__version__}") # 可以加一行打印确认版本
    
    # 现在这步应该不会报错了
    env = make_vec_env('CartPole-v1', n_envs=4)
    model = PPO2(MlpPolicy, env, verbose=1)
    print("Model initialized successfully!")
    
    # 后续代码...
    # model.learn(total_timesteps=1000) # 缩短点时间,快速验证
    # model.save("ppo2_cartpole_fixed")
    
  • 安全建议:
    使用较旧版本的库理论上可能错过一些安全补丁。不过,对于 gym 这样一个主要用于模拟环境的库,且版本回退不算太久远(比如到 0.20.0),在隔离的开发环境(如 Conda 环境)中使用,风险通常较低。关键是确保你的训练环境和生产环境的网络隔离。

  • 进阶使用技巧:
    为了保证环境的可复现性,强烈建议将项目的所有依赖(包括明确指定的 gym 版本)固定下来。可以使用 pip freeze > requirements.txt 命令生成依赖列表文件,或者在 Conda 环境中使用 conda env export > environment.yml。下次创建环境时,可以直接从这些文件安装,确保所有依赖版本一致。

方案二:迁移到 Stable-Baselines3

如果你的项目不是非得用 TensorFlow 1.x 和旧版的 stable-baselines,那么升级到 stable-baselines3 是一个更现代、更推荐的选择。

  • 原理:
    stable-baselines3stable-baselines 的精神续作,基于 PyTorch (也间接支持 TensorFlow 2.x via Tianshou 或其他库封装),并且被积极维护。它设计时就考虑了与较新版本 gym(甚至包括后来的 gymnasium)的兼容性。迁移到 SB3 可以一劳永逸地解决旧库版本兼容性的问题。

  • 操作步骤:

    1. 卸载旧库 (可选但推荐):
      如果确认不再需要旧版 stable-baselines,可以先卸载它。

      pip uninstall stable-baselines
      
    2. 安装 Stable-Baselines3:
      安装 stable-baselines3。它默认使用 PyTorch。

      pip install stable-baselines3[extra] # [extra] 会包含常用依赖,如 atari 环境支持
      

      同时,确保你的 gym 版本较新,或者使用 gymnasium (推荐,gym 0.26 后更名为 gymnasium):

      pip install gymnasium # 安装 gymnasium
      # 或者更新 gym 到较新版
      # pip install --upgrade gym
      

      注意:stable-baselines3 目前推荐使用 gymnasium

    3. 修改代码:
      stable-baselines3 的 API 和旧版 stable-baselines 有些不同。你需要相应地修改你的导入语句和模型初始化等代码。

  • 代码示例 (使用 SB3 和 Gymnasium):
    原来的 PPO2 示例用 SB3 和 Gymnasium 实现大致如下:

    import gymnasium as gym # 注意导入 gymnasium
    # from stable_baselines3.common.env_util import make_vec_env # SB3 用自己的 make_vec_env
    # 注意:SB3 更推荐使用 SubprocVecEnv 或 DummyVecEnv 手动创建向量化环境
    from stable_baselines3.common.vec_env import DummyVecEnv, SubprocVecEnv
    from stable_baselines3 import PPO # 算法名字可能简化了,没有 '2'
    from stable_baselines3.common.env_util import make_vec_env # 也可以用这个助手函数
    
    # 使用 SB3 的 make_vec_env (推荐)
    # 这个函数会自动处理环境创建和向量化
    env = make_vec_env('CartPole-v1', n_envs=4)
    
    # 或者手动创建 VecEnv
    # def make_env(rank: int, seed: int = 0):
    #     """Helper function to create environment"""
    #     def _init():
    #         env = gym.make('CartPole-v1')
    #         # Deprecated in gymnasium
    #         # env.seed(seed + rank)
    #         # Use reset with seed instead for Gymnasium >= 0.26
    #         # env.reset(seed=seed+rank)
    #         return env
    #     # set_global_seeds(seed) # deprecated in SB3 >= 2.0
    #     return _init
    # num_cpu = 4
    # env = SubprocVecEnv([make_env(i) for i in range(num_cpu)])
    
    # 初始化模型,注意 API 变化
    # policy='MlpPolicy' 是默认的,也可以显式指定
    model = PPO('MlpPolicy', env, verbose=1)
    print("SB3 Model initialized successfully!")
    
    # 学习、保存、加载
    model.learn(total_timesteps=25000)
    model.save("ppo_cartpole_sb3")
    
    del model # remove to demonstrate saving and loading
    
    model = PPO.load("ppo_cartpole_sb3")
    
    # 享受训练好的智能体 (Gymnasium API)
    vec_env = model.get_env()
    obs = vec_env.reset()
    while True:
        action, _states = model.predict(obs, deterministic=True)
        obs, rewards, terminated, truncated, info = vec_env.step(action) # 注意返回值的变化
        dones = terminated | truncated # 在 SB3 和 Gymnasium 中,通常这样组合 done 信号
        vec_env.render("human")
        # 如果所有环境都结束了,可以考虑重置(VecEnv 会自动处理)
        if dones.any():
           pass # VecEnv handles auto-resetting by default
    
  • 优势:

    • 使用最新的、被积极维护的库。
    • 享受 PyTorch 生态(或者 TensorFlow 2.x,如果通过其他方式集成)。
    • 与最新的 gym/gymnasium 兼容,可以使用更多新环境和特性。
    • 通常有更好的性能和更多的算法选择。
  • 注意事项:

    • 需要修改现有代码以适应 stable-baselines3 的 API。
    • 如果项目强制要求 TensorFlow 1.x,则此方案不适用。
    • 需要熟悉 PyTorch 的基本概念(如果选用 PyTorch 后端)。

总结一下

遇到 stable_baselinesAttributeError: module 'gym.logger' has no attribute 'MIN_LEVEL' 这个错误,基本可以断定是 stable-baselines (旧版) 和 gym (新版) 之间的版本不兼容导致的。

  • 最快修复: 降级 gym0.20.0 或更早版本 (pip install gym==0.20.0)。
  • 长远之计: 如果条件允许,迁移到 stable-baselines3,并使用 gymnasium。这能让你跟上技术发展的步伐,并避免未来可能出现的类似兼容性问题。

选择哪种方案取决于你的项目需求、时间以及对 TensorFlow 1.x 的依赖程度。希望这篇分析能帮你顺利解决这个拦路虎!