整体思路
我们选择Q-学习、Sarsa学习、Sarsa($\lambda$)学习、Q($\lambda$)学习这四种能够在线增量式学习的算法进行实现,它们对应的算法流程均由下列步骤组成:
1、初始化操作;
2、从当前状态出发,根据选择的行为策略,发出相应的动作;
3、动作作用于环境,获得Transiton;
4、根据Transiton更新Q函数;
5、重复执行2)—4)步。
因此,因此在算法中,我们需要:
1、初始化的算法的超参数、Q函数、资格迹Z(S,A);
2、确定产生Trajectory的行为策略;
3、定义选择动作的函数;
4、定义环境对于每个(状态,动作)的返回值函数;
5、定义相应的更新Q函数的函数;
为了平衡Exploration和Exploitation的问题,四个算法的行为策略均采用$\epsilon$-贪婪策略:以$\epsilon$的概率取当前最佳的动作,以$1-\epsilon$的概率随机从所有可能的动作中选择。
参数$\epsilon$需要初始化,各个算法其余需要初始化的参数与它们各自的Q函数更新方式有关,下面是四种算法核心的Q函数更新公式:
Q-学习
Sarsa学习:
Sarsa($\lambda$)学习:
Q($\lambda$)学习:
其中,Sarsa($\lambda$)的资格迹可以选择如下两种形式:
Q($\lambda$)学习的资格迹定义为:

可以看到,四种算法均需要:学习率$\alpha$、折扣因子$\gamma$、贪婪参数$\epsilon$,而对于Q($\lambda$)学习和Sarsa($\lambda$)学习算法,还需要表示资格迹衰减的因子$\lambda$。
因为四个算法大部分的参数一致且使用同一个行为策略,因此考虑将这部分内容实现在父类RL_main中,四个算法不同的Q函数更新函数则在各自的子类中实现,因此整体代码的框架如下:
实现父类
接下来先实现父类中的动作选择函数choose_action():
状态检查函数如下:
实现子类Q学习
Q-学习子类只需要实现相应的Q表格更新函数,根据它的更新表达式:
相应的learning函数实现方式如下:
实现子类Sarsa学习
同样,根据Sarsa学习算法的Q函数更新公式:
Sarsa算法与Q学习算法非常接近,唯一的区别就在状态$S’$时选择动作的方式不同。相应的learning函数实现为:
|
|
实现子类Sarsa($\lambda$)
n-step下的Sarsa算法增加了每个(状态,动作)的资格迹计算,因此还需要在check_state_exist()函数中检查资格迹表格 self.eligibility_trace 是否存在对应状态的记录行,若没有,则跟Q表格类似,需要增加相应状态对应的资格迹记录行。因为Z(S,A)和Q(S,A)是在相同的状态集$S$和动作集$A$进行描述,差异仅仅在于记录的描述不同,因此初始化时使用相同的新增记录行。具体实现如下:
Q函数更新遵循的表达式为:
两种可选的资格迹定义为:
我们同时在learning函数中实现这两种定义
实现子类Q($\lambda$)
Q($\lambda$)对应的Q值更新表达式为:
Q($\lambda$)学习算法对应的资格迹定义如下所示,关于它的理解请参照本博文讲述Q($\lambda$)算法的小节:
与Sarsa($\lambda$)类似,需要重写继承自父类的函数check_state_exist(),实现方式与Sarsa($\lambda$)保持一致,这里只给出learning函数的实现方式:
|
|
根据Sarsa($\lambda$)和Q($\lambda$)的算法伪代码,每一个episode后的资格迹需要重新计算,因此在每个episode调用这两个子类的learning之前,需要重置资格迹表格元素值全部为0.
运行
算法的运行请参照莫烦大神的代码,上面四个算法中,Q-学习、Sarsa学习和Sarsa($\lambda$)的代码来自于莫烦的实现,但是莫烦没有实现Q($\lambda$),因此在它的基础上,实现了Watkins提出的Q($\lambda$)版本的实现。
参考文献
1、Richard S. Sutton. Reinforcement learning An introduction. Second edition. MIT Press;
2、莫烦实现的前三个算法;