任务概述:
数字迷宫任务,如下表所示,4、9、11、12、23、24、25是陷阱,15是出口,出生点位置任意,学习求生之路(达到15位置)
1 | 2 | 3 | 4(F) | 5 |
6 | 7 | 8 | 9(F) | 10 |
11(F) | 12(F) | 13 | 14 | 15(O) |
16 | 17 | 18 | 19 | 20 |
21 | 22 | 23(F) | 24(F) | 25(F) |
离散的解释:状态空间:1~25;动作空间:up down left right(上下左右),可见在这个任务中状态空间和动作空间都是离散的。
强化学习 状态值函数公式和状态-行为值函数
当前状态s,采取动作a,得到回报为R。
强化学习最终想要学习的目的是选择使状态值函数v最大的动作a,通常我们采取贪心策略即选择最大s状态下q(s,a)最大的a。
对模型的解释:
公式中就是我们所说的模型,也是转移概率,例如,在这个任务中,从状态7采取动作right达到状态8的概率是1(采取动作right,必定会达到8)。而在计算状态值函数是中,假设开始时候使用平均策略,采取上下左右四个动作的概率都是0.25(个人称之为训练策略)。我们通过初始策略,并且设定好回报R,已知P,那么每一步的状态值函数可以通过不断迭代是可以计算出来的,通过迭代结果可以去改变策略称为强化学习中有模型的动态规划。因此,在这个任务其实是一个已知模型的任务。
在接下来,我们假设我们不知道模型,即在这个任务中,从状态7采取动作right达到状态8的概率我们不知道,那么就需要无模型的方法,Q-learning就是解决无模型强化学习的,归根到底就是不断的尝试。可以理解为通过无数次的尝试把最终的状态值函数试了出来,理论上无限次尝试之后,试出来的
是无偏的。每经过一次试验,达到终止状态(本任务中为陷阱或者出口),根据最终的回报调整状态-行为值函数q,这种方式有一个高达上的名字叫蒙特卡罗方法。而一次试验中有好多步,根据每一步来调整这个状态-行为值函数q,我们叫它Q-learning。
Q-learning:
虽然我们之前说要想状态值函数v最大,当测试的时候采取贪心策略时,选择状态行为值函数q最大就可以实现目的,这里,我们给出Q-learning的核心更新公式:
Q(s,a)是s状态下采取a动作的值,s‘是采取了a动作后的状态(可能同样的s状态,采用动作a后的状态不同,算法),a’是s‘状态下最可能采取动作a’ (greedy算法(贪心算法)),
折扣因子。
,可以看作这次动作得到的状态行为值与已知的状态行为值之间的偏差,而
,可以看作当次偏差对整个状态行为值的影响(感觉有点像增量式PID的P)。
训练中采用了算法,而在更新和测试中才用了greedy策略。保证在训练过程中可以尝试不同的路径。
代码:
import random
import gym
'''
状态
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
4 9 11 12 23 24 25 是陷阱
15是出口
'''
class digitalmaze(gym.Env):
def __init__(self):
self.states=[i for i in range(1,26)] #状态1~25号
#终止状态
self.terminate_states=dict()
self.terminate_states[4] = 1
self.terminate_states[9] = 1
self.terminate_states[11] = 1
self.terminate_states[12] = 1
self.terminate_states[15] = 1
self.terminate_states[23] = 1
self.terminate_states[24] = 1
self.terminate_states[25] = 1
self.actions=['up','down','left','right'] #行为 上下左右
#回报
self.rewards=dict()
#下一步能到达15的行为都给予正回报
self.rewards['10_down']=10
self.rewards['14_right']=10
self.rewards['20_up']=10
#下一步进入陷阱党都给予负回报
#下一步到达4或9
self.rewards['3_right']=-1
self.rewards['5_left']=-1
self.rewards['8_right']=-1
self.rewards['10_left']=-1
self.rewards['14_up']=-1
#下一步到达11或12
self.rewards['6_down']=-1
self.rewards['7_down']=-1
self.rewards['13_left']=-1
self.rewards['17_up']=-1
self.rewards['16_up']=-1
#下一步到达23 24 25
self.rewards['22_right']=-1
self.rewards['18_down']=-1
self.rewards['19_down']=-1
self.rewards['20_down']=-1
self.gamma = 0.8 # 折扣因子
self.state = None
def getcurrentstate(self):
return self.state
def getGamma(self):
return self.gamma
def getTerminate_states(self):
return self.terminate_states
def getStates(self):
return self.states
def getAction(self):
return self.actions
#根据当前状态和行为,计算下一状态
def _t(self,s,a):
s_next = s
if a=='up' and s>=6:
s_next = s-5
elif a=='down' and s<=20:
s_next = s+5
elif a=='left' and s%5!=1:
s_next= s-1
elif a=='right' and s%5!=0:
s_next = s+1
return s_next
def _step(self,a):
state =self.state
if state in self.terminate_states:
return state, 0, True, {}
state_next =self._t(state,a)
is_terminal = False
self.state = state_next
if state_next in self.terminate_states:
is_terminal = True
if "%d_%s"%(state,a) not in self.rewards:
r=0.0
else:
r = self.rewards["%d_%s"%(state,a)]
return state_next, r, is_terminal, {} #返回下一个时刻状态、回报、是否终止、调试信息
def _reset(self):
self.state = self.states[int(random.random() * len(self.states))]
return self.state
def _seed(self, seed_num=0): #prevent error in new version of gym
pass
return
#-------------Q-learning---------------------
# 贪婪策略
def greedy(qfunc, state):
amax = 0
key = "%d_%s" % (state, actions[0])
qmax = qfunc[key]
for i in range(len(actions)): # 扫描动作空间得到最大动作值函数
key = "%d_%s" % (state, actions[i])
q = qfunc[key]
if qmax < q:
qmax = q
amax = i
return actions[amax]
def epsilon_greedy(qfunc, state, epsilon):
amax = 0
key = "%d_%s"%(state, actions[0])
qmax = qfunc[key]
for i in range(len(actions)): #扫描动作空间得到最大动作值函数
key = "%d_%s"%(state, actions[i])
q = qfunc[key]
if qmax < q:
qmax = q
amax = i
#概率部分
pro = [0.0 for i in range(len(actions))]
pro[amax] += 1-epsilon
for i in range(len(actions)):
pro[i] += epsilon/len(actions)
##选择动作
r = random.random()
s = 0.0
for i in range(len(actions)):
s += pro[i]
if s>= r: return actions[i]
return actions[len(actions)-1]
def qlearning(num_iter1,alpha,epsilon,states,actions,model):
gamma = model.getGamma()
qfunc = dict() #行为值函数
for s in states:
for a in actions:
qfunc["%d_%s"%(s,a)]=0.0
for iter1 in range(num_iter1):
s = model.reset()
a = actions[int(random.random() * len(actions))]
t = False
count = 0
while False==t and count<200:
key = "%d_%s"%(s,a)
s1,r,t,i = model.step(a) #返回下一个时刻状态、回报、是否终止、调试信息
a1 = greedy(qfunc, s1)
key1 = "%d_%s"%(s1,a1)
qfunc[key] = qfunc[key] + alpha * (r + gamma * qfunc[key1] - qfunc[key]) #目标策略为贪心策略
s = s1
a = epsilon_greedy(qfunc, s1, epsilon) #训练过程:行动策略为epsilon-贪心策略
count += 1
return qfunc
if __name__=="__main__":
gym.register(
id='digitalmaze-v0',
entry_point='digitalmaze:digitalmaze',
reward_threshold=25.0, )
maze=gym.make('digitalmaze-v0')
states = maze.getStates() # 获得网格世界的状态空间
actions = maze.getAction() # 获得网格世界的动作空间
maze.reset()
qfunc = dict()
qfunc = qlearning(num_iter1=10000, alpha=0.2, epsilon=0.2,states=states,actions=actions,model=maze)
for s in states:
for a in actions:
print("%d_%s" % (s, a),qfunc["%d_%s" % (s, a)])
#测试策略
print('按回车:开始测试')
input()
terminate_states = maze.getTerminate_states()
for i in range(20):
s0 = maze.reset()
t = False
count=0
if s0 in terminate_states:
print("刚开始就结束了 %d" % (s0))
else:
while False == t and count < 100:
a1 = greedy(qfunc, s0)
# 与环境进行一次交互,从环境中得到新的状态及回报
s1, r, t, i = maze.step(a1)
print(s0,'>', a1,'>',end=" ")
if True == t:
#打印终止状态
print(s1)
print("达到了终止位置 %d" % (s1))
# s1处的最大动作
s0 = s1
count += 1