强化学习基础篇(五):Dyna-Q 算法

简介:  在强化学习中,“模型”通常指与智能体交互的环境模型,即对环境的状态转移概率和奖励函数进行建模。根据是否具有环境模型,强化学习算法分为两种:基于模型的强化学习(model-based reinforcement learning)和无模型的强化学习(model-free reinforcement learning)。无模型的强化学习根据智能体与环境交互采样到的数据直接进行策略提升或者价值估计,第 5 章讨论的两种时序差分算法,即 Sarsa 和 Q-learning 算法,便是两种无模型的强化学习方法,本书在后续章节中将要介绍的方法也大多是无模型的强化学习算法。

文章转于 伯禹学习平台-动手学强化学习 (强推)


本文所有代码均可在jupyter notebook运行


与君共勉,一起学习。


1. 简介


 在强化学习中,“模型”通常指与智能体交互的环境模型,即对环境的状态转移概率和奖励函数进行建模。根据是否具有环境模型,强化学习算法分为两种:基于模型的强化学习(model-based reinforcement learning)和无模型的强化学习(model-free reinforcement learning)。无模型的强化学习根据智能体与环境交互采样到的数据直接进行策略提升或者价值估计,第 5 章讨论的两种时序差分算法,即 Sarsa 和 Q-learning 算法,便是两种无模型的强化学习方法,本书在后续章节中将要介绍的方法也大多是无模型的强化学习算法。在基于模型的强化学习中,模型可以是事先知道的,也可以是根据智能体与环境交互采样到的数据学习得到的,然后用这个模型帮助策略提升或者价值估计。前面讨论的两种动态规划算法,即策略迭代和价值迭代,则是基于模型的强化学习方法,在这两种算法中环境模型是事先已知的。本章即将介绍的 Dyna-Q 算法也是非常基础的基于模型的强化学习算法,不过它的环境模型是通过采样数据估计得到的。


 强化学习算法有两个重要的评价指标:一个是算法收敛后的策略在初始状态下的期望回报,另一个是样本复杂度,即算法达到收敛结果需要在真实环境中采样的样本数量。基于模型的强化学习算法由于具有一个环境模型,智能体可以额外和环境模型进行交互,对真实环境中样本的需求量往往就会减少,因此通常会比无模型的强化学习算法具有更低的样本复杂度。但是,环境模型可能并不准确,不能完全代替真实环境,因此基于模型的强化学习算法收敛后其策略的期望回报可能不如无模型的强化学习算法。


2. Dyna-Q


 Dyna-Q 算法是一个经典的基于模型的强化学习算法。如图 6-1 所示,Dyna-Q 使用一种叫做 Q-planning 的方法来基于模型生成一些模拟数据,然后用模拟数据和真实数据一起改进策略。Q-planning 每次选取一个曾经访问过的状态s ,采取一个曾经在该状态下执行过的动作a aa,通过模型得到转移后的状态s ′ 以及奖励r,并根据这个模拟数据( s , a , r , s ′ ) ,用 Q-learning 的更新方式来更新动作价值函数。


下面我们来看一下 Dyna-Q 算法的具体流程:


ccb8ac28b2a34fc9a87fb0cddfa8764b.png


 可以看到,在每次与环境进行交互执行一次 Q-learning 之后,Dyna-Q 会做 n 次 Q-planning。其中 Q-planning 的次数 N 是一个事先可以选择的超参数,当其为 0 时就是普通的 Q-learning。值得 注意的是,上述 Dyna-Q 算法是执行在一个离散并且确定的环境中,所以当看到一条经验数据 ( s , a , r , s ′ ) 时,可以直接对模型做出更新,即 M ( s , a ) ← r , s ′ 。


36bcd13c73654b6a87579eb6f1bc583a.png


3. Dyna-Q 代码实践


 我们在悬崖漫步环境中执行过 Q-learning 算法,现在也在这个环境中实现 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


 接下来对结果进行可视化,通过调整参数,我们可以观察 Q-planning 步数对结果的影响(另见彩插图 3)。若 Q-planning 步数为 0,Dyna-Q 算法则退化为 Q-learning。


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步数为:0
# Iteration 0: 100%|██████████| 30/30 [00:00<00:00, 615.42it/s, episode=30,
# return=-138.400]
# Iteration 1: 100%|██████████| 30/30 [00:00<00:00, 1079.50it/s, episode=60,
# return=-64.100]
# Iteration 2: 100%|██████████| 30/30 [00:00<00:00, 1303.35it/s, episode=90,
# return=-46.000]
# Iteration 3: 100%|██████████| 30/30 [00:00<00:00, 1169.51it/s, episode=120,
# return=-38.000]
# Iteration 4: 100%|██████████| 30/30 [00:00<00:00, 1806.96it/s, episode=150,
# return=-28.600]
# Iteration 5: 100%|██████████| 30/30 [00:00<00:00, 2303.21it/s, episode=180,
# return=-25.300]
# Iteration 6: 100%|██████████| 30/30 [00:00<00:00, 2473.64it/s, episode=210,
# return=-23.600]
# Iteration 7: 100%|██████████| 30/30 [00:00<00:00, 2344.37it/s, episode=240,
# return=-20.100]
# Iteration 8: 100%|██████████| 30/30 [00:00<00:00, 1735.84it/s, episode=270,
# return=-17.100]
# Iteration 9: 100%|██████████| 30/30 [00:00<00:00, 2827.94it/s, episode=300,
# return=-16.500]
# Q-planning步数为:2
# Iteration 0: 100%|██████████| 30/30 [00:00<00:00, 425.09it/s, episode=30,
# return=-53.800]
# Iteration 1: 100%|██████████| 30/30 [00:00<00:00, 655.71it/s, episode=60,
# return=-37.100]
# Iteration 2: 100%|██████████| 30/30 [00:00<00:00, 799.69it/s, episode=90,
# return=-23.600]
# Iteration 3: 100%|██████████| 30/30 [00:00<00:00, 915.34it/s, episode=120,
# return=-18.500]
# Iteration 4: 100%|██████████| 30/30 [00:00<00:00, 1120.39it/s, episode=150,
# return=-16.400]
# Iteration 5: 100%|██████████| 30/30 [00:00<00:00, 1437.24it/s, episode=180,
# return=-16.400]
# Iteration 6: 100%|██████████| 30/30 [00:00<00:00, 1366.79it/s, episode=210,
# return=-13.400]
# Iteration 7: 100%|██████████| 30/30 [00:00<00:00, 1457.62it/s, episode=240,
# return=-13.200]
# Iteration 8: 100%|██████████| 30/30 [00:00<00:00, 1743.68it/s, episode=270,
# return=-13.200]
# Iteration 9: 100%|██████████| 30/30 [00:00<00:00, 1699.59it/s, episode=300,
# return=-13.500]
# Q-planning步数为:20
# Iteration 0: 100%|██████████| 30/30 [00:00<00:00, 143.91it/s, episode=30,
# return=-18.500]
# Iteration 1: 100%|██████████| 30/30 [00:00<00:00, 268.53it/s, episode=60,
# return=-13.600]
# Iteration 2: 100%|██████████| 30/30 [00:00<00:00, 274.53it/s, episode=90,
# return=-13.000]
# Iteration 3: 100%|██████████| 30/30 [00:00<00:00, 264.25it/s, episode=120,
# return=-13.500]
# Iteration 4: 100%|██████████| 30/30 [00:00<00:00, 263.58it/s, episode=150,
# return=-13.500]
# Iteration 5: 100%|██████████| 30/30 [00:00<00:00, 245.27it/s, episode=180,
# return=-13.000]
# Iteration 6: 100%|██████████| 30/30 [00:00<00:00, 257.16it/s, episode=210,
# return=-22.000]
# Iteration 7: 100%|██████████| 30/30 [00:00<00:00, 257.08it/s, episode=240,
# return=-23.200]
# Iteration 8: 100%|██████████| 30/30 [00:00<00:00, 261.12it/s, episode=270,
# return=-13.000]
# Iteration 9: 100%|██████████| 30/30 [00:00<00:00, 213.01it/s, episode=300,
# return=-13.400]


 从上述结果中我们可以很容易地看出,随着 Q-planning 步数的增多,Dyna-Q 算法的收敛速度也随之变快。当然,并不是在所有的环境中,都是 Q-planning 步数越大则算法收敛越快,这取决于环境是否是确定性的,以及环境模型的精度。在上述悬崖漫步环境中,状态的转移是完全确定性的,构建的环境模型的精度是最高的,所以可以通过增加 Q-planning 步数来直接降低算法的样本复杂度。


4. 小结


 本章讲解了一个经典的基于模型的强化学习算法 Dyna-Q,并且通过调整在悬崖漫步环境下的 Q-planning 步数,直观地展示了 Q-planning 步数对于收敛速度的影响。我们发现基于模型的强化学习算法 Dyna-Q 在以上环境中获得了很好的效果,但这些环境比较简单,模型可以直接通过经验数据得到。如果环境比较复杂,状态是连续的,或者状态转移是随机的而不是决定性的,如何学习一个比较准确的模型就变成非常重大的挑战,这直接影响到基于模型的强化学习算法能否应用于这些环境并获得比无模型的强化学习更好的效果。


相关资源来自:伯禹学习平台-动手学强化学习


35f0c043b96a4143bb9612b6bc0f1c4b.png

目录
相关文章
|
2月前
|
机器学习/深度学习 算法 机器人
多代理强化学习综述:原理、算法与挑战
多代理强化学习是强化学习的一个子领域,专注于研究在共享环境中共存的多个学习代理的行为。每个代理都受其个体奖励驱动,采取行动以推进自身利益;在某些环境中,这些利益可能与其他代理的利益相冲突,从而产生复杂的群体动态。
260 5
|
5天前
|
机器学习/深度学习 算法
强化学习之父Richard Sutton给出一个简单思路,大幅增强所有RL算法
Richard Sutton领导的团队提出了一种称为“奖励中心化”的方法,通过从观察到的奖励中减去其经验平均值,使奖励更加集中,显著提高了强化学习算法的性能。该方法在解决持续性问题时表现出色,尤其是在折扣因子接近1的情况下。论文地址:https://arxiv.org/pdf/2405.09999
30 15
|
23天前
|
机器学习/深度学习 人工智能 算法
探索人工智能中的强化学习:原理、算法与应用
探索人工智能中的强化学习:原理、算法与应用
|
23天前
|
机器学习/深度学习 人工智能 算法
探索人工智能中的强化学习:原理、算法及应用
探索人工智能中的强化学习:原理、算法及应用
|
4月前
|
机器学习/深度学习 算法 TensorFlow
深入探索强化学习与深度学习的融合:使用TensorFlow框架实现深度Q网络算法及高效调试技巧
【8月更文挑战第31天】强化学习是机器学习的重要分支,尤其在深度学习的推动下,能够解决更为复杂的问题。深度Q网络(DQN)结合了深度学习与强化学习的优势,通过神经网络逼近动作价值函数,在多种任务中表现出色。本文探讨了使用TensorFlow实现DQN算法的方法及其调试技巧。DQN通过神经网络学习不同状态下采取动作的预期回报Q(s,a),处理高维状态空间。
70 1
|
4月前
|
机器学习/深度学习 存储 算法
强化学习实战:基于 PyTorch 的环境搭建与算法实现
【8月更文第29天】强化学习是机器学习的一个重要分支,它让智能体通过与环境交互来学习策略,以最大化长期奖励。本文将介绍如何使用PyTorch实现两种经典的强化学习算法——Deep Q-Network (DQN) 和 Actor-Critic Algorithm with Asynchronous Advantage (A3C)。我们将从环境搭建开始,逐步实现算法的核心部分,并给出完整的代码示例。
335 1
|
4月前
|
测试技术 数据库
探索JSF单元测试秘籍!如何让您的应用更稳固、更高效?揭秘成功背后的测试之道!
【8月更文挑战第31天】在 JavaServer Faces(JSF)应用开发中,确保代码质量和可维护性至关重要。本文详细介绍了如何通过单元测试实现这一目标。首先,阐述了单元测试的重要性及其对应用稳定性的影响;其次,提出了提高 JSF 应用可测试性的设计建议,如避免直接访问外部资源和使用依赖注入;最后,通过一个具体的 `UserBean` 示例,展示了如何利用 JUnit 和 Mockito 框架编写有效的单元测试。通过这些方法,不仅能够确保代码质量,还能提高开发效率和降低维护成本。
57 0
|
5月前
|
机器学习/深度学习 存储 数据采集
强化学习系列:A3C算法解析
【7月更文挑战第13天】A3C算法作为一种高效且广泛应用的强化学习算法,通过结合Actor-Critic结构和异步训练的思想,实现了在复杂环境下的高效学习和优化策略的能力。其并行化的训练方式和优势函数的引入,使得A3C算法在解决大规模连续动作空间和高维状态空间的问题上表现优异。未来,随着技术的不断发展,A3C算法有望在更多领域发挥重要作用,推动强化学习技术的进一步发展。
|
6月前
|
机器学习/深度学习 分布式计算 算法
在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)
【6月更文挑战第28天】在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)、数据规模与特性(大数据可能适合分布式算法或深度学习)、性能需求(准确性、速度、可解释性)、资源限制(计算与内存)、领域知识应用以及实验验证(交叉验证、模型比较)。迭代过程包括数据探索、模型构建、评估和优化,结合业务需求进行决策。
63 0
|
7月前
|
机器学习/深度学习 算法
算法人生(2):从“强化学习”看如何“活在当下”
本文探讨了强化学习的原理及其在个人生活中的启示。强化学习强调智能体在动态环境中通过与环境交互学习最优策略,不断迭代优化。这种思想类似于“活在当下”的哲学,要求人们专注于当前状态和决策,不过分依赖历史经验或担忧未来。活在当下意味着全情投入每一刻,不被过去或未来牵绊。通过减少执着,提高觉察力和静心练习,我们可以更好地活在当下,同时兼顾历史经验和未来规划。文章建议实践静心、时间管理和接纳每个瞬间,以实现更低焦虑、更高生活质量的生活艺术。
下一篇
DataWorks