【动手学强化学习】第 6 章 Dyna-Q 算法知识点总结
- 本章知识点
- 基于模型的强化学习与无模型的强化学习方法简介
- 无模型的强化学习方法
- 基于模型的强化学习方法
- 强化学习算法的评价指标
- Dyna-Q算法
- Dyna-Q 算法的具体流程
- Dyna-Q 代码实践
本章知识点
基于模型的强化学习与无模型的强化学习方法简介
- 在强化学习中,“模型”通常指与智能体交互的环境模型,即对环境的状态转移概率和奖励函数进行建模
- 根据是否具有环境模型,强化学习算法分为两种:基于模型的强化学习(model-based reinforcement learning)和无模型的强化学习(model-free reinforcement learning)
无模型的强化学习方法
- 无模型的强化学习根据智能体与环境交互采样到的数据直接进行策略提升或者价值估计
- 第 5 章讨论的两种时序差分算法,即 Sarsa 和 Q-learning 算法,便是两种无模型的强化学习方法
- 《动手学强化学习》在后续章节中将要介绍的方法也大多是无模型的强化学习算法
基于模型的强化学习方法
- 在基于模型的强化学习中,模型可以是事先知道的,也可以是根据智能体与环境交互采样到的数据学习得到的,然后用这个模型帮助策略提升或者价值估计
- 第 4 章讨论的两种动态规划算法,即策略迭代和价值迭代,则是基于模型的强化学习方法,在这两种算法中环境模型是事先已知的
- 本章即将介绍的 Dyna-Q 算法是非常基础的基于模型的强化学习算法,但其环境模型是通过采样数据估计得到的
强化学习算法的评价指标
- 强化学习算法有两个重要的评价指标:期望回报,样本复杂度
- 期望回报是指算法收敛后的策略在初始状态下的期望回报
- 样本复杂度是指算法达到收敛结果需要在真实环境中采样的样本数量
- 基于模型的强化学习算法由于具有一个环境模型,智能体可以额外和环境模型进行交互,对真实环境中样本的需求量往往就会减少,因此通常会比无模型的强化学习算法具有更低的样本复杂度。但是,环境模型可能并不准确,不能完全代替真实环境,因此基于模型的强化学习算法收敛后其策略的期望回报可能不如无模型的强化学习算法
【补充内容】:样本效率与样本复杂度的区别和关系
- 样本复杂度(Sample Complexity):样本复杂度通常指的是学习一个任务所需的样本数量的上限。换句话说,这是一个理论上的度量,用来描述一个学习算法至少需要多少个样本才能达到一定的性能标准(例如,以一定的概率达到特定的准确度)。样本复杂度是一个理论上的界限,它通常与算法的收敛速度和所需的数据量有关。
- 样本效率(Sample Efficiency):样本效率则是一个更实际的概念,它描述的是算法在实际应用中使用有限样本进行学习的能力。样本效率高的算法能够在较少的数据上快速学习并做出准确的预测或决策。样本效率通常与算法的泛化能力有关,即算法能够多好地将从训练数据中学到的知识应用到新的、未见过的数据上
- 样本复杂度更多地关注理论上的最小样本数量,而样本效率则关注实际应用中算法使用样本的能力。两者都很重要,因为理论上的样本复杂度可以指导算法设计,而实际的样本效率则决定了算法在现实世界中的可行性和性能
Dyna-Q算法
- Dyna-Q 算法是一个经典的基于模型的强化学习算法
- Dyna-Q 使用一种叫做 Q-planning 的方法来基于模型生成一些模拟数据,然后用模拟数据和真实数据一起改进策略
- Q-planning 每次选取一个曾经访问过的状态 s s s,采取一个曾经在该状态下执行过的动作 a a a,通过模型得到转移后的状态 s ′ s' s′以及奖励 r r r,并根据这个模拟数据,用 Q-learning 的更新方式来更新动作价值函数
Dyna-Q 算法的具体流程
- 在每次与环境进行交互执行一次 Q-learning 之后,Dyna-Q 会做 N N N次Q-planning
- 其中 Q-planning 的次数 N N N是一个事先可以选择的超参数,当其为 0 时就是普通的 Q-learning
- 注意:上述 Dyna-Q 算法是执行在一个离散并且确定的环境中,所以当看到一条经验数据 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s′)时,是与真实环境交互的得到哦,可以直接对模型做出更新, 即 M ( s , a ) ← r , s ′ M(s,a) \leftarrow r,s' M(s,a)←r,s′
Dyna-Q 代码实践
悬崖漫步环境代码
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm
import random
import time
class CliffWalkingEnv:
def __init__(self, ncol, nrow):
self.nrow = nrow
self.ncol = ncol
self.x = 0 # 记录当前智能体位置的横坐标
self.y = self.nrow - 1 # 记录当前智能体位置的纵坐标
def step(self, action): # 外部调用这个函数来改变当前位置
# 4种动作, change[0]:上, change[1]:下, change[2]:左, change[3]:右。坐标系原点(0,0)
# 定义在左上角
change = [[0, -1], [0, 1], [-1, 0], [1, 0]]
self.x = min(self.ncol - 1, max(0, self.x + change[action][0]))
self.y = min(self.nrow - 1, max(0, self.y + change[action][1]))
next_state = self.y * self.ncol + self.x
reward = -1
done = False
if self.y == self.nrow - 1 and self.x > 0: # 下一个位置在悬崖或者目标
done = True
if self.x != self.ncol - 1:
reward = -100
return next_state, reward, done
def reset(self): # 回归初始状态,起点在左上角
self.x = 0
self.y = self.nrow - 1
return self.y * self.ncol + self.x
在 Q-learning 的代码上进行简单修改,实现 Dyna-Q 的主要代码
最主要的修改是加入了环境模型model,用一个字典表示,每次在真实环境中收集到新的数据,就把它加入字典
根据字典的性质,若该数据本身存在于字典中,便不会再一次进行添加。在 Dyna-Q 的更新中,执行完 Q-learning 后,会立即执行 Q-planning
class DynaQ:
""" Dyna-Q算法 """
def __init__(self,
ncol,
nrow,
epsilon,
alpha,
gamma,
n_planning,
n_action=4):
self.Q_table = np.zeros([nrow * ncol, n_action]) # 初始化Q(s,a)表格
self.n_action = n_action # 动作个数
self.alpha = alpha # 学习率
self.gamma = gamma # 折扣因子
self.epsilon = epsilon # epsilon-贪婪策略中的参数
self.n_planning = n_planning #执行Q-planning的次数, 对应1次Q-learning
self.model = dict() # 环境模型
def take_action(self, state): # 选取下一步的操作
if np.random.random() < self.epsilon:
action = np.random.randint(self.n_action)
else:
action = np.argmax(self.Q_table[state])
return action
def q_learning(self, s0, a0, r, s1):
td_error = r + self.gamma * self.Q_table[s1].max(
) - self.Q_table[s0, a0]
self.Q_table[s0, a0] += self.alpha * td_error
def update(self, s0, a0, r, s1):
self.q_learning(s0, a0, r, s1)
self.model[(s0, a0)] = r, s1 # 将数据添加到模型中
for _ in range(self.n_planning): # Q-planning循环
# 随机选择曾经遇到过的状态动作对
(s, a), (r, s_) = random.choice(list(self.model.items()))
self.q_learning(s, a, r, s_)
Dyna-Q 算法在悬崖漫步环境中的训练函数,它的输入参数是 Q-planning 的步数
def DynaQ_CliffWalking(n_planning):
ncol = 12
nrow = 4
env = CliffWalkingEnv(ncol, nrow)
epsilon = 0.01
alpha = 0.1
gamma = 0.9
agent = DynaQ(ncol, nrow, epsilon, alpha, gamma, n_planning)
num_episodes = 300 # 智能体在环境中运行多少条序列
return_list = [] # 记录每一条序列的回报
for i in range(10): # 显示10个进度条
# tqdm的进度条功能
with tqdm(total=int(num_episodes / 10),
desc='Iteration %d' % i) as pbar:
for i_episode in range(int(num_episodes / 10)): # 每个进度条的序列数
episode_return = 0
state = env.reset()
done = False
while not done:
action = agent.take_action(state)
next_state, reward, done = env.step(action)
episode_return += reward # 这里回报的计算不进行折扣因子衰减
agent.update(state, action, reward, next_state)
state = next_state
return_list.append(episode_return)
if (i_episode + 1) % 10 == 0: # 每10条序列打印一下这10条序列的平均回报
pbar.set_postfix({
'episode':
'%d' % (num_episodes / 10 * i + i_episode + 1),
'return':
'%.3f' % np.mean(return_list[-10:])
})
pbar.update(1)
return return_list
结果可视化
np.random.seed(0)
random.seed(0)
n_planning_list = [0, 2, 20]
for n_planning in n_planning_list:
print('Q-planning步数为:%d' % n_planning)
time.sleep(0.5)
return_list = DynaQ_CliffWalking(n_planning)
episodes_list = list(range(len(return_list)))
plt.plot(episodes_list,
return_list,
label=str(n_planning) + ' planning steps')
plt.legend()
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Dyna-Q on {}'.format('Cliff Walking'))
plt.show()
- 从上述结果中可以很容易地看出,随着 Q-planning 步数的增多,Dyna-Q 算法的收敛速度也随之变快
- 当然,并不是在所有的环境中,都是 Q-planning 步数越大则算法收敛越快,这取决于环境是否是确定性的,以及环境模型的精度
- 在上述悬崖漫步环境中,状态的转移是完全确定性的,构建的环境模型的精度是最高的,所以可以通过增加 Q-planning 步数来直接降低算法的样本复杂度
- 基于模型的强化学习算法 Dyna-Q 在以上环境中获得了很好的效果,但这些环境比较简单,模型可以直接通过经验数据得到
- 如果环境比较复杂,状态是连续的,或者状态转移是随机的而不是确定性的,如何学习一个比较准确的模型就变成非常重大的挑战,这直接影响到基于模型的强化学习算法能否应用于这些环境并获得比无模型的强化学习更好的效果