强化学习代码实操和讲解(二)
引言
这里是强化学习代码实操第二部分,对应书上第三章有限马尔可夫决策过程的内容。本次实操主要运用了网格世界这一经典的强化学习环境(下面会略作介绍),向我们展示了本章的核心内容——贝尔曼方程的应用以及最优策略的判定方法。看过书的朋友可能会注意到本章其实没有提供具体的算法伪代码,在本次实操中其实运用了下一章中的价值迭代和策略迭代的方法来求解,如果看过下一章可能会更好理解一些。再次贴上代码出处:代码出处
背景介绍
上图就是格子世界的环境展示。每个格子代表一个环境中的状态(state)。在每个格子中有四个可选的动作(action),每个动作都会使智能体在对应的方向上前进一格。如果动作会使智能体出界,那么智能体则会在原地不动,并且获得一个-1的收益(reward)。在状态A中,智能体做任何动作都会转移到A’并且获得+10的收益;在状态B中,智能体做任何动作都会转移到B’并获得+5的收益。在其他情况下智能体的收益都是0。现在我们要来求解每个状态在给定策略 π \pi π下的状态价值( v π v_\pi vπ),最优价值函数( v ∗ v_* v∗)以及其对应的最优策略 π ∗ \pi_* π∗。
重点代码解析
环境设置
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.table import Table
matplotlib.use('Agg')
#下列网格参数设定的具体意义参见P58
WORLD_SIZE=5 #网格世界的长宽都是5
A_POS=[0,1] #标记A的位置
A_PRIME_POS=[4,1] #标记A'的位置
B_POS=[0,3] #标记B的位置
B_PRIME_POS=[2,3] #标记B'的位置
DISCOUNT=0.9 #设定折扣系数为0.9
ACTIONS=[np.array([0,-1]),
np.array([-1,0]),
np.array([0,1]),
np.array([1,0])] #设定动作,由于网格世界是二维平面,所以用一个二元的向量来表示每个动作
ACTION_FIGS=['←','↑','→','↓'] #设定每个动作对应的箭头,方便画图
ACTION_PROB=0.25 #由于在本实验中仅仅估计价值函数,并根据收敛的最优价值采取最优策略,所以探索的过程中并没有对策略(选择方向的概率)进行修改,
#所以每个向任意方向的行动都是等概率的,最后根据收敛的最优价值函数获得最优策略
首先引入了一些画图和数学计算的包,之后根据问题描述设置了格子世界的参数,比如格子的数量和A、B等点的位置。由于格子世界是一个二维的世界,虽然不能斜着走,但是用一个二维的向量表示动作也是十分巧妙易懂的。值得注意的是,此处的ACTION_PROB指的是向各个位置移动的概率,也就是策略 π \pi π,由于本章主要是估计给定策略下的状态价值函数,所以并没有对原策略进行改进,仅仅是在后面通过最优价值函数直接导出最优策略,所以这里的动作概率在估计过程中是一个定值。
step函数:接收环境奖励和状态转移
def step(state,action):
if state==A_POS:
return A_PRIME_POS,10 #在A处做任何动作都会转移到A'处并获得+10回报
if state==B_POS:
return B_PRIME_POS,5 #在B处做任何动作都会转移到B'处并获得+5回报
next_state=(np.array(state)+action).tolist()
x,y=next_state #否则把当前位置加上相应动作向量获得新位置
if x<0 or x>=WORLD_SIZE or y<0 or y>=WORLD_SIZE:
reward=-1.0
next_state=state
else:
reward=0 #如果出界,位置不变并获得-1回报,如果没出界且不在A,B处,则获得0回报
return next_state,reward
#对每一步行动之后的状态和回报(return)进行计算
在格子世界中的状态转移与奖励都十分好定义,因为问题描述中已经说清楚所有可能性,因而我们只要用if的组合就能区别各种情况。这里向量化动作的优势就显现出来,我们只要把当前状态和动作这两个向量相加就能很方便地得到下一个状态向量。
figure_3_2:根据给定策略估计状态价值函数
def figure_3_2():
value = np.zeros((WORLD_SIZE, WORLD_SIZE))
while True:
# keep iteration until convergence
new_value = np.zeros_like(value)
for i in range(WORLD_SIZE):
for j in range(WORLD_SIZE): #对每一个网格进行动作的试探和价值函数迭代
for action in ACTIONS:
(next_i, next_j), reward = step([i, j], action) #对每个状态下的每个动作记录状态转移和收益(reward)
# bellman equation
new_value[i, j] += ACTION_PROB * (reward + DISCOUNT * value[next_i, next_j]) #根据贝尔曼方程估计新的状态价值,这里的策略用的是等概率动作,其实还用到了下一章的动态规划
if np.sum(np.abs(value - new_value)) < 1e-4: #收敛的时候结束循环,并用