1

OpenAI 的基线使用以下代码返回 aLazyFrames而不是串联的 numpy 数组以节省内存。这个想法是利用一个 numpy 数组可以同时保存在不同列表中的事实,因为列表只保存引用而不是对象本身。但是,在 的实现中LazyFrames,它进一步将串联的 numpy 数组保存在 中self._out,在这种情况下,如果每个LazyFrames对象都至少被调用一次,它将始终在其中保存一个串联的 numpy 数组,这似乎根本没有节省任何内存. 那么有什么意义LazeFrames呢?还是我误解了什么?

class FrameStack(gym.Wrapper):
    def __init__(self, env, k):
        """Stack k last frames.

        Returns lazy array, which is much more memory efficient.

        See Also
        --------
        baselines.common.atari_wrappers.LazyFrames
        """
        gym.Wrapper.__init__(self, env)
        self.k = k
        self.frames = deque([], maxlen=k)
        shp = env.observation_space.shape
        self.observation_space = spaces.Box(low=0, high=255, shape=(shp[:-1] + (shp[-1] * k,)), dtype=env.observation_space.dtype)

    def reset(self):
        ob = self.env.reset()
        for _ in range(self.k):
            self.frames.append(ob)
        return self._get_ob()

    def step(self, action):
        ob, reward, done, info = self.env.step(action)
        self.frames.append(ob)
        return self._get_ob(), reward, done, info

    def _get_ob(self):
        assert len(self.frames) == self.k
        return LazyFrames(list(self.frames))

class LazyFrames(object):
    def __init__(self, frames):
        """This object ensures that common frames between the observations are only stored once.
        It exists purely to optimize memory usage which can be huge for DQN's 1M frames replay
        buffers.

        This object should only be converted to numpy array before being passed to the model.

        You'd not believe how complex the previous solution was."""
        self._frames = frames
        self._out = None

    def _force(self):
        if self._out is None:
            self._out = np.concatenate(self._frames, axis=-1)
            self._frames = None
        return self._out

    def __array__(self, dtype=None):
        out = self._force()
        if dtype is not None:
            out = out.astype(dtype)
        return out

    def __len__(self):
        return len(self._force())

    def __getitem__(self, i):
        return self._force()[i]

    def count(self):
        frames = self._force()
        return frames.shape[frames.ndim - 1]

    def frame(self, i):
        return self._force()[..., I]
4

1 回答 1

0

实际上,我来到这里是想了解这如何节省了任何记忆!但是您提到列表存储对基础数据的引用,而 numpy 数组存储该数据的副本,我认为您对此是正确的。

回答你的问题,你是对的!调用时_force,它会self._out使用 numpy 数组填充项目,从而扩展内存。但您调用_force(在 的任何 API 函数中调用LazyFrame)之前,self._outNone. 所以想法是在需要基础数据之前不要调用_force(因此,不要调用任何LazyFrames方法),因此其文档字符串中的警告“此对象在传递给之前只应转换为 numpy 数组”模型”。

请注意,当self._out被数组填充时,它也会清除self._frames, 以便它不存储重复信息(从而损害其仅存储所需数量的整个目的)。

此外,在同一个文件中,您会找到ScaledFloatFrame带有此文档字符串的文件:

    Scales the observations by 255 after converting to float.
    This will undo the memory optimization of LazyFrames,
    so don't use it with huge replay buffers.
于 2020-05-29T00:31:17.797 回答