引言

今天在编写强化学习算法时,使用ptan库的时候,发现一个问题(ptan库是一个开源强化学习算法库)。

问题是这样的,在使用ptan库中的experience.py文件时,我发现env发出done=True时,并不会立即在当前步,也就是done时输出总奖励,而是要在下一步才输出总奖励,其原因如下:

                while True:
                    ...
                    ...
                    ...
                    if len(history) == self.steps_count and iter_idx % self.steps_delta == 0:
                        yield tuple(history)
                       states[idx] = next_state
                    if is_done:
                        # in case of very short episode (shorter than our steps count), send gathered history
                        if 0 < len(history) < self.steps_count:
                            yield tuple(history)
                        # generate tail of history
                        while len(history) > 1:
                            history.popleft()
                            yield tuple(history)
                        # vectorized envs are reset automatically
                        self.total_rewards.append(cur_rewards[idx])
                        self.total_steps.append(cur_steps[idx])
                        cur_rewards[idx] = 0.0
                        cur_steps[idx] = 0
                        states[idx] = env.reset()
                       ...
                       ...
                       ...

由于程序中有yield,因此首先判断是生成器,当每次调用的时候,程序会从yield后继续执行。因此done=True,进入判断函数体,并执行完最后一个yield后,就会将此回合最后一个样本返回。按理说与此同时,总奖励也应该一并给出了。但是如果按照此程序的写法,总奖励和总步数将会和新的状态,也就是reset后的新的状态生成的同时给出,在时间上并不同步。下图证明了在回合结束后,总奖励并未给出,而是当reset,进入下一个回合的时候,才会给出总奖励和总步数。

因此,在考虑了仅在step_count中最后一个样本yield后再对总奖励进行赋值的特点后,对代码进行修改如下:

                while True:
                    ...
                    ...
                    ...
                    if len(history) == self.steps_count and iter_idx % self.steps_delta == 0:
                        yield tuple(history)
                        states[idx] = next_state
                    if is_done:
                        # in case of very short episode (shorter than our steps count), send gathered history
                        if 0 < len(history) < self.steps_count:
                            if len(history) == 1:
                                self.total_rewards.append(cur_rewards[idx])
                                self.total_steps.append(cur_steps[idx])
                                cur_rewards[idx] = 0.0
                                cur_steps[idx] = 0
                            yield tuple(history)
                        # generate tail of history
                        while len(history)>1:
                            history.popleft()
                            if len(history) == 1:
                                self.total_rewards.append(cur_rewards[idx])
                                self.total_steps.append(cur_steps[idx])
                                cur_rewards[idx] = 0.0
                                cur_steps[idx] = 0
                            yield tuple(history)
                        # vectorized envs are reset automatically
                        states[idx] = env.reset()
                       ...
                       ...
                       ...

代码效果如下:

可以看到已经实现了期望的目标。

在这之后,我考虑将这个改进在github上进行提交给作者,进行参考。将整个PR的过程记录如下:

pull requestS过程

1、我们首先需要将作者的项目fork到自己的仓库中,点击网页上的fork即可;

2、然后clone我们自己远程仓库中的项目到本地,即git clone的环节;

3、新建分支。由于在一个成熟的项目中,通常会有多个分支,在这里一个标准的做法是,根据哪个分支新建分支:

      比如在项目中有master和test分支,如果当前在master分支,想基于test分支新建一个新分支并取名为new。可以有三种做法:

git checkout -b new test

     或者想基于云端的test分支新建new分支:

git checkout -b new origin/test

     或者我们先进入test分支,再新建new分支:

git checkout test
git checkout -b new

4、在新建的分支上进行改进;

5、修改完成后,可以使用git diff进行查看修改是否正确:

6、确认修改无误后,可以进行提交:

git add .
git commit -m "xxx"

7、然后提交到远端仓库,在这里还有一个小细节,由于我们在本地新建了一个分支,但是如果我们提交的时候,想将本地新建的分支提交到远端另一个分支,需要怎么做呢?

git push -u origin xxx(新分支名):master

8、这时本地的工作就做完了,需要在网页端点击pull requests:

再点击:

9、最后写清楚题目和修改说明就可以啦:

May the force be with you!