Python的copy.deepcopy无法正确复制Gym环境LunarLanderContinuous-v2的问题求解
我之前也碰到过类似的Gym环境复制问题,确实用标准库的copy.deepcopy处理这类环境时容易踩坑——很多Gym环境的类并没有正确实现__deepcopy__方法来处理所有自定义属性,就像你遇到的这样,复制后的环境居然自动切换成了离散版本的配置,属实让人头大。
先还原一下你遇到的问题场景,代码如下:
import gym, copy env = gym.make('LunarLanderContinuous-v2') print(env.action_space) print(env.continuous) env1 = copy.deepcopy(env) # env1.continuous = True print(env1.action_space) print(env1.continuous)
运行后输出:
action_space Box(-1.0, 1.0, (2,), float32)
continuous True
action_space Discrete(4)
continuous False
问题原因分析
出现这个问题的核心原因是,LunarLanderContinuous-v2本质是基于基础的LunarLander环境修改了continuous参数,但copy.deepcopy在复制时没有正确保留这些参数配置,反而重置回了默认的离散版本属性。Gym的很多环境类内部的属性初始化逻辑并没有考虑deepcopy的场景,导致复制过程中关键的配置属性(比如continuous)没有被正确拷贝,进而触发了环境属性的错误重置。
可行的解决办法
这里有几个实用的解决思路,你可以根据自己的使用场景选择:
手动同步关键配置属性:这是最直接的临时解决方案,在deepcopy之后,手动把原环境的关键属性赋值给复制后的环境,就像你代码里注释掉的那行其实就有效果,扩展一下:
env1 = copy.deepcopy(env) # 手动同步核心配置属性 env1.continuous = env.continuous env1.action_space = env.action_space # 如果有其他必要的配置属性,比如observation_space也可以同步这样就能让复制后的环境和原环境保持一致的配置了。
重新创建环境并同步状态:如果担心deepcopy带来的其他潜在问题,也可以直接重新创建一个相同配置的环境,然后把原环境的运行状态同步过去(如果需要保留当前环境的运行进度的话):
env1 = gym.make('LunarLanderContinuous-v2') # 同步原环境的运行状态(如果原环境已经执行过步骤) env1.state = env.state env1.steps_beyond_done = env.steps_beyond_done # 其他需要同步的状态属性可以根据实际运行需求添加这种方式更稳妥,因为直接用正确的配置初始化环境,从根源上避免了deepcopy的未知问题。
自定义环境类的__deepcopy__方法:如果需要频繁复制这个环境,可以给LunarLander的环境类添加自定义的
__deepcopy__方法,确保所有关键属性都被正确复制。不过这种方式需要修改环境类的代码,适合有一定源码修改经验的场景:from gym.envs.box2d.lunar_lander import LunarLander def custom_deepcopy(self, memo): # 用当前环境的配置创建新实例 new_env = LunarLander(continuous=self.continuous) # 同步运行状态 new_env.state = self.state.copy() new_env.steps_beyond_done = self.steps_beyond_done # 可以根据需要添加其他需要复制的属性 memo[id(self)] = new_env return new_env # 替换原类的__deepcopy__方法 LunarLander.__deepcopy__ = custom_deepcopy这样之后再用
copy.deepcopy复制环境时,就会按照我们定义的逻辑来处理,不会再出现配置错误的情况了。
备注:内容来源于stack exchange,提问作者PerceptualRobotics

