当前位置:   article > 正文

Q-Learning 简介:初学者教程(1)

q-learning

一、说明

        强化学习强调无模型学习算法,因此出现Q-Learning,Q-Learning算法酷似“有限状态自动机”模型,只是增加了奖励机制和Agent机制,而Agent与粒子群算法、蒙特卡洛算法是有关的。本文介绍这个算法框架。

二、QL框架

Q 学习标题

        强化学习 (RL) 是机器学习生态系统的一部分,其中代理通过与环境交互来学习,以获得实现目标的最佳策略。它与监督式机器学习算法完全不同,在监督式机器学习算法中,我们需要提取和处理这些数据。强化学习不需要数据。相反,它从环境和奖励系统中学习以做出更好的决策。

        例如,在马里奥视频游戏中,如果角色采取随机动作(例如向左移动),则根据该动作,它可能会获得奖励。采取行动后,代理(马里奥)处于新状态,该过程重复进行,直到游戏角色到达关卡终点或死亡。 

        此情节将重复多次,直到马里奥学会通过最大化奖励来驾驭环境。 

强化学习

        我们可以将强化学习分为五个简单的步骤:

  1. 代理在环境中处于状态零。
  2. 它将根据特定的策略采取行动。
  3. 它将根据该行动获得奖励或惩罚。
  4. 通过学习先前的动作并优化策略。 
  5. 该过程将重复,直到找到最佳策略。       

 

三、什么是 Q-Learning?

Q 学习是一种无模型、基于价值的离策略算法,它将根据代理的当前状态找到最佳的一系列操作。“Q”代表质量。质量表示该操作在最大化未来奖励方面有多大价值。  

基于模型的算法使用转换和奖励函数来估计最优策略并创建模型。相比之下,无模型算法则通过没有转换和奖励函数的经验来学习其行为的后果。 

基于价值的方法训练价值函数,以了解哪种状态更有价值并采取行动。另一方面,基于策略的方法直接训练策略,以了解在给定状态下应采取哪种行动。

离策略中,算法评估并更新与采取行动所用策略不同的策略。相反,在策略算法评估并改进采取行动所用相同策略。  

Q 学习中的关键术语

在我们了解 Q-learning 的工作原理之前,我们需要学习一些有用的术语来理解 Q-learning 的基础知识。 

  • 状态:代理在环境中的当前位置。 
  • 动作(a):代理在特定状态下采取的步骤。 
  • 奖励:对于每一个动作,代理都会得到奖励和惩罚。 
  • 情节:阶段的结束,此时代理无法采取新的行动。当代理已实现目标或失败时,就会发生这种情况。 
  • Q(S t+1 , a):在特定状态下执行操作的预期最佳 Q 值。 
  • Q(S t , A t ):是 Q(S t+1 , a) 的当前估计。
  • Q-Table:代理维护状态和动作集的 Q 表。
  • 时间差分(TD) :利用当前状态和动作以及之前的状态和动作 来估计Q(S t+1 , a)的预期值。

四、Q-Learning 如何工作?

        我们将以结冰的湖泊为例,详细了解 Q 学习的工作原理。在这个环境中,代理必须从起点穿过结冰的湖泊到达目标,而不能掉进洞里。最好的策略是走最短的路径到达目标。 

Q 学习可视化

4.1 Q-表

        代理将使用 Q 表根据环境中每个状态的预期奖励采取最佳行动。简而言之,Q 表是一组动作和状态的数据结构,我们使用 Q 学习算法来更新表中的值。 

4.2 Q 函数

        Q 函数使用贝尔曼方程,以状态(s)和动作(a)作为输入。该方程简化了状态值和状态动作值的计算。 

贝尔曼方程

五、Q 学习算法

Q 学习过程

5.1 初始化 Q 表

        我们首先初始化 Q 表。我们将根据动作数量构建列,根据状态数量构建行。

        在我们的示例中,角色可以上下左右移动。我们有四种可能的动作和四种状态(开始、空闲、错误路径和结束)。您还可以考虑掉入洞中的错误路径。我们将使用 0 值初始化 Q-Table。 

Q-表 1

5.2 选择一个动作

        第二步很简单。一开始,代理会选择采取随机动作(向下或向右),第二次运行时,它将使用更新的 Q 表来选择动作。 

5.3 执行操作

        选择一个动作并执行该动作将重复多次,直到训练循环停止。使用 Q 表选择第一个动作和状态。在我们的例子中,Q 表的所有值都是零。 

        然后,代理将向下移动并使用贝尔曼方程更新 Q 表。每次移动时,我们都会更新 Q 表中的值,并使用它来确定最佳行动方案。 

        最初,代理处于探索模式,并选择随机动作来探索环境。Epsilon 贪婪策略是一种平衡探索和利用的简单方法。epsilon 代表在探索机会较小的情况下选择探索和利用的概率。 

        开始时,epsilon 率较高,这意味着代理处于探索模式。在探索环境时,epsilon 会降低,代理会开始利用环境。在探索过程中,随着每次迭代,代理对估计 Q 值的信心会越来越强

Q 表 2

        在冰冻湖泊示例中,代理不知道环境,因此需要采取随机动作(向下移动)才能启动。如上图所示,Q 表使用贝尔曼方程进行更新。

5.4 衡量回报

        采取行动后,我们将衡量结果和回报。 

  • 达成目标的奖励为+1
  • 走错路(掉进洞里)的奖励是0
  • 在冰冻湖上空闲或移动的奖励也是 0。 

5.5 更新 Q 表

        我们将使用方程更新函数 Q(S t , A t )。它使用前一集的估计 Q 值、学习率和时间差异误差。时间差异误差是使用即时奖励、折扣后的最大预期未来奖励和前一个估计 Q 值计算的。 

该过程重复多次,直到 Q 表更新并且 Q 值函数最大化。 

Q 学习方程

        一开始,代理会探索环境以更新 Q 表。当 Q 表准备就绪时,代理将开始利用并开始做出更好的决策。 

        在湖面结冰的情况下,代理将学习采取最短路径到达目标并避免跳入洞中。

六、Q-Learning Python 教程 

        我们将使用 Gym 环境、Pygame 和 Numpy 从头开始​​构建 Q 学习模型。Python 教程是Thomas Simonini 的Notebook的修改版本。它包括初始化环境和 Q 表、定义贪婪策略、设置超参数、创建和运行训练循环和评估以及可视化结果。   

        如果您在创建和运行训练循环时遇到问题,您可以使用输出   检查代码源。

6.1 配置

设置虚拟显示

我们首先安装所有依赖项以生成回放视频(Gif)。我们需要一个虚拟屏幕(pyvirtualdisplay)来渲染环境并记录帧。 

注意:通过使用“%%capture”,我们抑制了 Jupyter 单元的输出。 

  1. %%capture
  2. !pip install pyglet==1.5.1
  3. !apt install python-opengl
  4. !apt install ffmpeg
  5. !apt install xvfb
  6. !pip3 install pyvirtualdisplay
  7. # Virtual display
  8. from pyvirtualdisplay import Display
  9. virtual_display = Display(visible=0, size=(1400, 900))
  10. virtual_display.start()
依赖项安装

我们现在将安装依赖项来帮助我们创建、运行和评估训练循环。 

  • gym:用于初始化FrozenLake-v1环境。
  • pygame:用于FrozenLake-v1 UI。
  • numPy:用于创建和处理 Q 表。
  1. %%capture
  2. !pip install gym==0.24
  3. !pip install pygame
  4. !pip install numpy
  5. !pip install imageio imageio_ffmpeg
导入包

我们现在将导入所需的库。 

  • Imageio 用于创建动画。 
  • tqdm 用于进度条。 
  1. import numpy as np
  2. import gym
  3. import random
  4. import imageio
  5. from tqdm.notebook import trange
冰冻湖健身房环境 

我们将使用冰冻湖健身库创建一个防滑的 4x4 环境。 

  • 有两个网格版本:“4x4”和“8x8”。
  • 如果“is_slippery=True”,则由于冰冻湖的滑溜性质,代理可能不会朝预定的方向移动。 

初始化环境之后,我们来进行环境分析。 

  1. env = gym.make("FrozenLake-v1",map_name="4x4",is_slippery=False)
  2. print("Observation Space", env.observation_space)
  3. print("Sample observation", env.observation_space.sample()) # display a random observation
环境中有 16 个独特的空间,以随机位置显示。 
  1. Observation Space Discrete(16)
  2. Sample observation 15
让我们发现动作的数量并显示随机动作。 

行动空间

  • 0:向左移动
  • 1:向下移动
  • 2:向右移动
  • 3:向上移动

奖励函数

  • 达成目标:+1
  • 掉坑:0
  • 停留在冰冻湖面:0
  1. print("Action Space Shape", env.action_space.n)
  2. print("Action Space Sample", env.action_space.sample())
Action Space Shape 4
Action Space Sample 1

6.2 创建并初始化Q表

Q 表的列表示动作,行表示状态。我们可以使用 OpenAI Gym 来查找动作空间和状态空间。然后我们将使用此信息来创建 Q 表。 

  1. state_space = env.observation_space.n
  2. print("There are ", state_space, " possible states")
  3. action_space = env.action_space.n
  4. print("There are ", action_space, " possible actions")
There are 16 possible states There are 4 possible actions

为了初始化 Q 表,我们将创建一个包含 state_space 和 action 空间的 Numpy 数组。我们将创建一个 16 X 4 数组。 

  1. def initialize_q_table(state_space, action_space):
  2.         Qtable = np.zeros((state_space, action_space))
  3.         return Qtable
  4. Qtable_frozenlake = initialize_q_table(state_space, action_space)

6.3 贪婪策略

在上一节中,我们了解了处理探索和利用权衡的 epsilon 贪婪策略。以 1 - ε 的概率,我们进行利用;以 ε 的概率,我们进行探索。 

在 epsilon_greedy_policy 中我们将:

  1. 生成0到1之间的随机数。
  2. 如果随机数大于 epsilon,我们将进行利用。这意味着代理将在给定状态下采取具有最高值的操作。
  3. 否则,我们将进行探索(采取随机行动)。 
  1. def epsilon_greedy_policy(Qtable, state, epsilon):
  2. random_int = random.uniform(0,1)
  3. if random_int > epsilon:
  4. action = np.argmax(Qtable[state])
  5. else:
  6. action = env.action_space.sample()
  7. return action
定义贪婪策略

我们现在知道 Q 学习是一种离策略算法,这意味着采取行动和更新函数的策略是不同的。 

在这个例子中,Epsilon Greedy 策略是代理策略,而 Greedy 策略是更新策略。 

贪婪策略也将是训练代理时的最终策略。它用于从 Q 表中选择最高的状态和动作值。

  1. def greedy_policy(Qtable, state):
  2.         action = np.argmax(Qtable[state])
  3.         return action
模型超参数

这些超参数用于训练循环,对其进行微调将会给你带来更好的结果。 

代理需要探索足够的状态空间才能学习良好的值近似值;我们需要逐步衰减 epsilon。如果衰减率很高,代理可能会卡住,因为它没有探索足够的状态空间。

  • 共有 10,000 个训练片段和 100 个评估片段
  • 学习率为0.7。
  • 我们使用“FrozenLake-v1”作为环境,每集最多有 99 步
  • 伽马(折扣率)为 0.95。
  • eval_seed:环境的评估种子。
  • 开始时探索的epsilon 概率为 1.0,最小概率为 0.05。
  • 埃普西隆概率的指数衰减率为0.0005。
  1. # Training parameters
  2. n_training_episodes = 10000
  3. learning_rate = 0.7
  4. # Evaluation parameters
  5. n_eval_episodes = 100
  6. # Environment parameters
  7. env_id = "FrozenLake-v1"
  8. max_steps = 99
  9. gamma = 0.95
  10. eval_seed = []
  11. # Exploration parameters
  12. max_epsilon = 1.0
  13. min_epsilon = 0.05
  14. decay_rate = 0.0005

6.4 模型训练 

在训练循环中,我们将:

  1. 为训练片段创建一个循环。
  2. 我们首先要减少 epsilon。因为在每一集中,我们需要的探索越来越少,而利用越来越多。 
  3. 重置环境。
  4. 创建一个嵌套循环以获取最大步数。
  5. 使用 epsilon 贪婪策略选择动作。 
  6. 采取行动(A t)并观察预期奖励(R t+1)和状态(S t+1)。
  7. 采取行动(a)并观察结果状态(s')和奖励(r)。
  8. 使用公式更新 Q 函数。 
  9. 如果“done = True”,则完成情节并打破循环。
  10. 最后,将当前状态更改为新状态。 
  11. 完成所有训练阶段后,该函数将返回更新的 Q 表。 
  1. def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
  2. for episode in trange(n_training_episodes):
  3. epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
  4. # Reset the environment
  5. state = env.reset()
  6. step = 0
  7. done = False
  8. # repeat
  9. for step in range(max_steps):
  10. action = epsilon_greedy_policy(Qtable, state, epsilon)
  11. new_state, reward, done, info = env.step(action)
  12. Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action])
  13. # If done, finish the episode
  14. if done:
  15. break
  16. # Our state is the new state
  17. state = new_state
  18. return Qtable

我们花了3秒完成10,000次训练。 

Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)

训练结果

我们可以看到,训练过的 Q 表具有值,代理现在将使用这些值来导航环境并实现目标。  

  1. Qtable_frozenlake
  1. array([[0.73509189, 0.77378094, 0.77378094, 0.73509189],
  2. [0.73509189, 0. , 0.81450625, 0.77378094],
  3. [0.77378094, 0.857375 , 0.77378094, 0.81450625],
  4. [0.81450625, 0. , 0.77378094, 0.77378094],
  5. [0.77378094, 0.81450625, 0. , 0.73509189],
  6. [0. , 0. , 0. , 0. ],
  7. [0. , 0.9025 , 0. , 0.81450625],
  8. [0. , 0. , 0. , 0. ],
  9. [0.81450625, 0. , 0.857375 , 0.77378094],
  10. [0.81450625, 0.9025 , 0.9025 , 0. ],
  11. [0.857375 , 0.95 , 0. , 0.857375 ],
  12. [0. , 0. , 0. , 0. ],
  13. [0. , 0. , 0. , 0. ],
  14. [0. , 0.9025 , 0.95 , 0.857375 ],
  15. [0.9025 , 0.95 , 1. , 0.9025 ],
  16. [0. , 0. , 0. , 0. ]])
评估

Evaluation_agent 运行“n_eval_episodes”事件并返回奖励的平均值和标准差。 

  1. 在循环中,我们首先检查是否存在评估种子。如果没有,那么我们将重置没有种子的环境。 
  2. 嵌套循环将运行至max_steps。
  3. 代理将使用 Q-Table 采取在给定状态下具有最大预期未来奖励的行动。 
  4. 计算獎勵。
  5. 改变状态。
  6. 如果完成(代理掉入洞中或者目标已实现),则打破循环。
  7. 附加结果。
  8. 最后,我们将使用这些结果来计算平均值和标准差。 
  1. def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):
  2. episode_rewards = []
  3. for episode in range(n_eval_episodes):
  4. if seed:
  5. state = env.reset(seed=seed[episode])
  6. else:
  7. state = env.reset()
  8. step = 0
  9. done = False
  10. total_rewards_ep = 0
  11. for step in range(max_steps):
  12. # Take the action (index) that have the maximum reward
  13. action = np.argmax(Q[state][:])
  14. new_state, reward, done, info = env.step(action)
  15. total_rewards_ep += reward
  16. if done:
  17. break
  18. state = new_state
  19. episode_rewards.append(total_rewards_ep)
  20. mean_reward = np.mean(episode_rewards)
  21. std_reward = np.std(episode_rewards)
  22. return mean_reward, std_reward

如你所见,我们获得了满分,标准差为零。这意味着我们的代理在所有 100 集中都达到了目标。 

  1. # Evaluate our Agent
  2. mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)
  3. print(f"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}")
Mean_reward=1.00 +/- 0.00

6.5 可视化结果

到目前为止,我们一直在玩数字,为了进行演示,我们需要创建一个代理从一开始直到达到目标的动画 Gif。 

  1. 我们将首先通过使用 0-500 的随机整数重置环境来创建状态。 
  2. 使用 rdb_array 创建图像数组来渲染环境。 
  3. 然后将“img”附加到“images”数组。 
  4. 在循环中,我们将使用 Q-Table 采取步骤并为每个步骤渲染图像。 
  5. 最后,我们将使用这个数组和 imageio 来创建每秒一帧的 Gif。 
  1. def record_video(env, Qtable, out_directory, fps=1):
  2. images = []
  3. done = False
  4. state = env.reset(seed=random.randint(0,500))
  5. img = env.render(mode='rgb_array')
  6. images.append(img)
  7. while not done:
  8. # Take the action (index) that have the maximum expected future reward given that state
  9. action = np.argmax(Qtable[state][:])
  10. state, reward, done, info = env.step(action) # We directly put next_state = state for recording logic
  11. img = env.render(mode='rgb_array')
  12. images.append(img)
  13. imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)
如果您在 Jupyter 笔记本中,则可以使用“IPython.display”图像函数显示 Gif。 
  1. video_path="/content/replay.gif"
  2. video_fps=1
  3. record_video(env, Qtable_frozenlake, video_path, video_fps)
  4. from IPython.display import Image
  5. Image('./replay.gif')
您现在可以与同事和同学分享这些结果或将其发布在社交媒体上。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/809962
推荐阅读
相关标签
  

闽ICP备14008679号