时序差分更新汲取了动态规划方法中“自益”的思想,用现有价值更新价值估计,不需要等到回合结束也可以更新价值估计。所以时序差分更新可以用于回合制任务,也可用于连续制任务。
虽然这个出租车调度环境的SARSA算法例子属于离散非连续状态的,但是也可以某种程度上巩固一下之前在莫烦大佬那里学的Q-learning与SARSA知识,为之后的的学习奠定基础。
初始化环境
import gym
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
env = gym.make('Taxi-v2')
state = env.reset() # 初始化环境
taxirow, taxicol, passloc, destidx = env.unwrapped.decode(state) # decode state to get position of taxi and passenger
print ('出租车位置 = {}'.format((taxirow,taxicol)))
print ('乘客位置 = {}'.format(passloc))
print ('乘客目的地 = {}'.format(destidx))
env.render() # 渲染可视化,显示当前局势
env.step(1) # 走一步
初始化结果
这个坏境的观测是[0-500)的值,出租车的5x5位置;pass_loc乘客位置共有5个,即四个位置等待的状态+在车上的状态;destination有4个,表示目的地,全部状态总数是(5x5)x5x4=500,我调出了出租车坏境的源代码,下图一目了然
定义类并初始化
class SARSAAGENT:
def __init__(self,env,gamma = 0.9, learning_rate = 0.1, epsilon = .01):
self.gamma = gamma
self.learning_rate = learning_rate #学习率
self.epsilon = epsilon
self.action_n = env.action_space.n
self.q = np.zeros((env.observation_space.n, env.action_space.n)) #初始化大小为500*6的q表
ϵ-贪婪算法
强化学习本质上是exploit&explore的过程,那么这两者的概率是怎么决定呢?
设置ϵ=0.1,那么就表示有10%的概率会进行“探索”操作,而90%会进行“利用”操作
贪心策略
def decide(self,state):
if np.random.uniform()>self.epsilon: #贪心策略
action = self.q[state].argmax() #在q表中state中选择可以获得最大值的action
else:
action = np.random.randint(self.action_n)
return action
def learn(self, state, action, reward, state_, done, action_):
u = reward + self.gamma*self.q[state_, action_]* (1-done) #计算回报估计值
td_error = u - self.q[state, action] #计算通过动作估计与当前已知表的差值
self.q[state, action] += self.learning_rate*td_error #更新价值
至此我们agent的定义就完成了,这里用到的是SARSA算法,即用下一步的action计算回报的估计值。下一步的action_是实实在在根据原有的q表做过的,正因为如此这种算法更加保守。
u = reward + self.gamma*self.q[state_, action_]* (1-done)
这里的reward在Taxi-V2环境中定义的是,出租车完成任务可以得到20个奖励(收到车费),每次试图移动一格算作-1奖励(有点像汽车在路上跑烧油的感觉),当乘客不在拉客位置上或者车没开到目的地就让乘客下车时,获得-10奖励(乘客差评)。
智能体与环境的交互
def play_sarsa(env,agent,train = False, render = False):
episode_reward = 0
observation = env.reset() # 获得初始状态
action = agent.decide(observation)
while True:
if render:
env.render()
state_, reward, done,_ = env.step(action)
episode_reward += reward
action_ = agent.decide(state_)
if train:
agent.learn(observation,action,reward,state_,done,action_)
if done:
break
observation, action = state_, action_ #将下一步的状态和动作变成当前状态
return episode_reward
训练
episodes = 2000
episode_rewards = []
for episode in range (episodes):
episode_reward = play_sarsa(env, agent, train = True)
episode_rewards.append(episode_reward)
plt.plot(episode_rewards)
print(pd.DataFrame(agent.q))
单次训练得到的值是episode_reward,将每次训练的奖励都放在episode_rewards中,得到训练episodes次数的奖励值。
训练结果
可以看出总体的rewards在训练2500次以上后就收敛到-10左右,说明司机能很快的接到乘客并送到目的地。
状态价值函数&动作价值函数
对《强化学习》第一章的基本概念又忘掉了不少,现在重新复习一下
两者的区别在于是否做了动作a,而且两者可以互相表示。
状态价值函数可以这样理解:在当前状态下,采取不同的动作时有不同的概率,即概率pi,对q进行加权求和,即为状态价值函数v。值得注意的是,这里的q就是当前状态的动作价值函数。
动作价值函数可以这样理解:在当前状态价值v(s)下,采取的动作是确定的,但是跳转到下一状态s’和奖励r是不确定的,有转移概率Pr,此时的动作价值函数,即(当前动作确定奖励r+折扣因子gamma未确定的下一步状态s'带来的状态价值v(s'))转移概率Pr
以一个吃饭吃饱问题来说明情况
动力系统:s状态a动作下得到的r与s'对应关系
状态-->>动作,这里的策略pi是当前状态到动作的转移概率
状态价值函数和动作价值函数的值可由下式表示
学习的其他知识
如果seed中数字一样,则生成同一个随机数
若通过当前时间为seed起始数,可以产生近似完全随机的数字
random.seed( 10 )
print ("Random number with seed 10 : {}".format( random.random()))
random.seed(time.time())