深度强化学习课程文档
实践操作
并获得增强的文档体验
开始使用
实践操作
现在我们已经学习了 Q-学习算法,让我们从头开始实现它,并在两个环境中训练我们的 Q-学习智能体
- Frozen-Lake-v1(非光滑和光滑版本) ☃️:我们的智能体需要从起始状态 (S) 到达目标状态 (G),方法是仅在冰冻的瓷砖 (F) 上行走并避开洞 (H)。
- 自动驾驶出租车 🚖 将需要学习导航城市,以便将乘客从 A 点运送到 B 点。

借助排行榜,您将能够与其他同学比较您的结果,并交流最佳实践以提高您智能体的分数。谁将赢得单元 2 的挑战?
要验证此实践操作是否符合认证流程,您需要将您训练好的出租车模型推送到 Hub,并获得 >= 4.5 的结果。
要查找您的结果,请转到排行榜并找到您的模型,结果 = 平均奖励 - 奖励标准差
有关认证流程的更多信息,请查看此部分 👉 https://huggingface.co/deep-rl-course/en/unit0/introduction#certification-process
您可以在此处查看您的进度 👉 https://huggingface.co/spaces/ThomasSimonini/Check-my-progress-Deep-RL-Course
要开始实践操作,请单击“在 Colab 中打开”按钮 👇
我们强烈建议学生使用 Google Colab 进行实践练习,而不是在他们的个人计算机上运行它们。
通过使用 Google Colab,您可以专注于学习和实验,而无需担心设置环境的技术方面。
单元 2:基于 FrozenLake-v1 ⛄ 和 Taxi-v3 🚕 的 Q-学习

在本笔记本中,您将从头开始编写您的第一个强化学习智能体,以使用 Q-学习玩 FrozenLake ❄️,与社区分享,并尝试不同的配置。
⬇️ 这里有一个示例,说明您将在几分钟内实现什么。 ⬇️

🎮 环境:
📚 强化学习库:
- Python 和 NumPy
- Gymnasium
我们一直在努力改进我们的教程,所以如果您在本笔记本中发现任何问题,请在 GitHub 存储库上打开一个 issue。
本笔记本的目标 🏆
在本笔记本结束时,您将
- 能够使用 Gymnasium,环境库。
- 能够从头开始编写 Q-学习智能体。
- 能够将您训练好的智能体和代码推送到 Hub,并附带精彩的视频回放和评估分数 🔥。
本笔记本来自深度强化学习课程

在本免费课程中,您将
- 📖 在理论和实践中学习深度强化学习。
- 🧑💻 学习使用著名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、CleanRL 和 Sample Factory 2.0。
- 🤖 在独特环境中训练智能体
更多信息请查看 📚 教学大纲 👉 https://simoninithomas.github.io/deep-rl-course
不要忘记注册课程(我们正在收集您的电子邮件,以便能够在每个单元发布时向您发送链接,并向您提供有关挑战和更新的信息)。
保持联系的最佳方式是加入我们的 Discord 服务器,与社区和我们交流 👉🏻 https://discord.gg/ydHrjt3WP5
先决条件 🏗️
在深入学习笔记本之前,您需要
🔲 📚 通过阅读单元 2 来学习 Q-学习 🤗
Q-学习小回顾
Q-学习 是强化学习算法,它
训练 Q 函数,一个动作价值函数,它在内部存储器中由 Q 表 编码,该表包含所有状态-动作对值。
给定一个状态和动作,我们的 Q 函数将在 Q 表中搜索相应的值。

当训练完成时,我们得到了一个最优的 Q 函数,也就是最优的 Q 表。
如果我们有一个最优的 Q 函数,我们就有一个最优策略,因为我们知道对于每个状态,要采取的最佳动作。

但是,在开始时,我们的 Q 表是无用的,因为它为每个状态-动作对提供任意值(大多数时候我们将 Q 表初始化为 0 值)。但是,当我们探索环境并更新我们的 Q 表时,它将为我们提供越来越好的近似值

这是 Q-学习伪代码

让我们编写我们的第一个强化学习算法 🚀
要验证此实践操作是否符合认证流程,您需要将您训练好的出租车模型推送到 Hub,并获得 >= 4.5 的结果。
要查找您的结果,请转到排行榜并找到您的模型,结果 = 平均奖励 - 奖励标准差
有关认证流程的更多信息,请查看此部分 👉 https://huggingface.co/deep-rl-course/en/unit0/introduction#certification-process
安装依赖项并创建虚拟显示器 🔽
在笔记本中,我们需要生成回放视频。为此,使用 Colab,我们需要有一个虚拟屏幕来渲染环境(从而记录帧)。
因此,以下单元格将安装库并创建和运行虚拟屏幕 🖥
我们将安装多个库:
gymnasium
:包含 FrozenLake-v1 ⛄ 和 Taxi-v3 🚕 环境。pygame
:用于 FrozenLake-v1 和 Taxi-v3 UI。numpy
:用于处理我们的 Q 表。
Hugging Face Hub 🤗 作为一个中心位置,任何人都可以共享和探索模型和数据集。它具有版本控制、指标、可视化和其他功能,使您可以轻松地与他人协作。
您可以在此处查看所有可用的深度强化学习模型(如果它们使用 Q 学习)👉 https://huggingface.co/models?other=q-learning
pip install -r https://raw.githubusercontent.com/huggingface/deep-rl-class/main/notebooks/unit2/requirements-unit2.txt
sudo apt-get update sudo apt-get install -y python3-opengl apt install ffmpeg xvfb pip3 install pyvirtualdisplay
为了确保使用新安装的库,有时需要重启笔记本运行时。下一个单元格将强制运行时崩溃,因此您需要重新连接并从此处开始运行代码。感谢这个技巧,我们将能够运行我们的虚拟屏幕。
import os
os.kill(os.getpid(), 9)
# Virtual display
from pyvirtualdisplay import Display
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()
导入包 📦
除了已安装的库之外,我们还使用
random
:生成随机数(这对于 epsilon-greedy 策略很有用)。imageio
:生成回放视频。
import numpy as np
import gymnasium as gym
import random
import imageio
import os
import tqdm
import pickle5 as pickle
from tqdm.notebook import tqdm
我们现在准备好编写我们的 Q-学习算法 🔥
第 1 部分:冰湖 ⛄(非光滑版本)
创建并理解冰湖环境 ⛄(( https://gymnasium.org.cn/environments/toy_text/frozen_lake/ )
💡 当您开始使用环境时,一个好的习惯是查看其文档
👉 https://gymnasium.org.cn/environments/toy_text/frozen_lake/
我们将训练我们的 Q-学习智能体从起始状态 (S) 导航到目标状态 (G),方法是仅在冰冻的瓷砖 (F) 上行走并避开洞 (H)。
我们可以有两种尺寸的环境
map_name="4x4"
:4x4 网格版本map_name="8x8"
:8x8 网格版本
环境有两种模式
is_slippery=False
:由于冰湖的非光滑性(确定性),智能体始终沿预期方向移动。is_slippery=True
:由于冰湖的光滑性(随机性),智能体可能并不总是沿预期方向移动。
现在,让我们保持简单,使用 4x4 地图和非光滑性。我们添加一个名为 render_mode
的参数,该参数指定环境应如何可视化。在我们的例子中,因为我们想在最后录制环境的视频,所以我们需要将 render_mode 设置为 rgb_array。
正如 文档中解释的那样,“rgb_array”:返回一个表示环境当前状态的单帧。帧是一个 np.ndarray,形状为 (x, y, 3),表示 x 乘 y 像素图像的 RGB 值。
# Create the FrozenLake-v1 environment using 4x4 map and non-slippery version and render_mode="rgb_array"
env = gym.make() # TODO use the correct parameters
解决方案
env = gym.make("FrozenLake-v1", map_name="4x4", is_slippery=False, render_mode="rgb_array")
您可以像这样创建自己的自定义网格
desc=["SFFF", "FHFH", "FFFH", "HFFG"]
gym.make('FrozenLake-v1', desc=desc, is_slippery=True)
但我们现在将使用默认环境。
让我们看看环境是什么样的:
# We create our environment with gym.make("<name_of_the_environment>")- `is_slippery=False`: The agent always moves in the intended direction due to the non-slippery nature of the frozen lake (deterministic).
print("_____OBSERVATION SPACE_____ \n")
print("Observation Space", env.observation_space)
print("Sample observation", env.observation_space.sample()) # Get a random observation
我们看到 Observation Space Shape Discrete(16)
,观察是一个整数,表示智能体当前位置为 current_row * ncols + current_col(行和列都从 0 开始)。
例如,4x4 地图中的目标位置可以计算如下:3 * 4 + 3 = 15。可能的观察数量取决于地图的大小。例如,4x4 地图有 16 个可能的观察。
例如,这是状态 = 0 的样子

print("\n _____ACTION SPACE_____ \n")
print("Action Space Shape", env.action_space.n)
print("Action Space Sample", env.action_space.sample()) # Take a random action
动作空间(智能体可以采取的一组可能动作)是离散的,有 4 个可用动作 🎮
- 0:向左
- 1:向下
- 2:向右
- 3:向上
奖励函数 💰
- 到达目标:+1
- 到达洞:0
- 到达冰面:0
创建并初始化 Q 表 🗄️
(👀 伪代码的步骤 1)

现在是时候初始化我们的 Q 表了!为了知道要使用多少行(状态)和列(动作),我们需要知道动作和观察空间。我们已经从之前知道了它们的值,但我们希望以编程方式获得它们,以便我们的算法可以推广到不同的环境。Gym 为我们提供了一种方法:env.action_space.n
和 env.observation_space.n
state_space =
print("There are ", state_space, " possible states")
action_space =
print("There are ", action_space, " possible actions")
# Let's create our Qtable of size (state_space, action_space) and initialized each values at 0 using np.zeros. np.zeros needs a tuple (a,b)
def initialize_q_table(state_space, action_space):
Qtable =
return Qtable
Qtable_frozenlake = initialize_q_table(state_space, action_space)
解决方案
state_space = env.observation_space.n
print("There are ", state_space, " possible states")
action_space = env.action_space.n
print("There are ", action_space, " possible actions")
# Let's create our Qtable of size (state_space, action_space) and initialized each values at 0 using np.zeros
def initialize_q_table(state_space, action_space):
Qtable = np.zeros((state_space, action_space))
return Qtable
Qtable_frozenlake = initialize_q_table(state_space, action_space)
定义贪婪策略 🤖
请记住,由于 Q-Learning 是一种离策略算法,我们有两种策略。这意味着我们使用不同的策略来进行动作和更新价值函数。
- Epsilon-greedy 策略(行动策略)
- 贪婪策略(更新策略)
贪婪策略也将是 Q-learning 智能体完成训练后我们将拥有的最终策略。贪婪策略用于使用 Q 表选择动作。

def greedy_policy(Qtable, state):
# Exploitation: take the action with the highest state, action value
action =
return action
解决方案
def greedy_policy(Qtable, state):
# Exploitation: take the action with the highest state, action value
action = np.argmax(Qtable[state][:])
return action
定义 epsilon-greedy 策略 🤖
Epsilon-greedy 是处理探索/利用权衡的训练策略。
epsilon-greedy 的想法
以 概率 1 - ɛ:我们进行利用(即我们的智能体选择具有最高状态-动作对值的动作)。
以 概率 ɛ:我们进行探索(尝试随机动作)。
随着训练的继续,我们逐步降低 epsilon 值,因为我们将需要越来越少的探索和更多的利用。

def epsilon_greedy_policy(Qtable, state, epsilon):
# Randomly generate a number between 0 and 1
random_num =
# if random_num > greater than epsilon --> exploitation
if random_num > epsilon:
# Take the action with the highest value given a state
# np.argmax can be useful here
action =
# else --> exploration
else:
action = # Take a random action
return action
解决方案
def epsilon_greedy_policy(Qtable, state, epsilon):
# Randomly generate a number between 0 and 1
random_num = random.uniform(0, 1)
# if random_num > greater than epsilon --> exploitation
if random_num > epsilon:
# Take the action with the highest value given a state
# np.argmax can be useful here
action = greedy_policy(Qtable, state)
# else --> exploration
else:
action = env.action_space.sample()
return action
定义超参数 ⚙️
与探索相关的超参数是最重要的参数之一。
- 我们需要确保我们的智能体充分探索状态空间,以学习良好的价值近似。为此,我们需要逐步衰减 epsilon。
- 如果您将 epsilon 降低得太快(衰减率太高),您将承担智能体卡住的风险,因为您的智能体没有充分探索状态空间,因此无法解决问题。
# Training parameters
n_training_episodes = 10000 # Total training episodes
learning_rate = 0.7 # Learning rate
# Evaluation parameters
n_eval_episodes = 100 # Total number of test episodes
# Environment parameters
env_id = "FrozenLake-v1" # Name of the environment
max_steps = 99 # Max steps per episode
gamma = 0.95 # Discounting rate
eval_seed = [] # The evaluation seed of the environment
# Exploration parameters
max_epsilon = 1.0 # Exploration probability at start
min_epsilon = 0.05 # Minimum exploration probability
decay_rate = 0.0005 # Exponential decay rate for exploration prob
创建训练循环方法

训练循环如下所示
For episode in the total of training episodes:
Reduce epsilon (since we need less and less exploration)
Reset the environment
For step in max timesteps:
Choose the action At using epsilon greedy policy
Take the action (a) and observe the outcome state(s') and reward (r)
Update the Q-value Q(s,a) using Bellman equation Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]
If done, finish the episode
Our next state is the new state
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
for episode in tqdm(range(n_training_episodes)):
# Reduce epsilon (because we need less and less exploration)
epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
# Reset the environment
state, info = env.reset()
step = 0
terminated = False
truncated = False
# repeat
for step in range(max_steps):
# Choose the action At using epsilon greedy policy
action =
# Take action At and observe Rt+1 and St+1
# Take the action (a) and observe the outcome state(s') and reward (r)
new_state, reward, terminated, truncated, info =
# Update Q(s,a):= Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]
Qtable[state][action] =
# If terminated or truncated finish the episode
if terminated or truncated:
break
# Our next state is the new state
state = new_state
return Qtable
解决方案
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
for episode in tqdm(range(n_training_episodes)):
# Reduce epsilon (because we need less and less exploration)
epsilon = min_epsilon + (max_epsilon - min_epsilon) * np.exp(-decay_rate * episode)
# Reset the environment
state, info = env.reset()
step = 0
terminated = False
truncated = False
# repeat
for step in range(max_steps):
# Choose the action At using epsilon greedy policy
action = epsilon_greedy_policy(Qtable, state, epsilon)
# Take action At and observe Rt+1 and St+1
# Take the action (a) and observe the outcome state(s') and reward (r)
new_state, reward, terminated, truncated, info = env.step(action)
# Update Q(s,a):= Q(s,a) + lr [R(s,a) + gamma * max Q(s',a') - Q(s,a)]
Qtable[state][action] = Qtable[state][action] + learning_rate * (
reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action]
)
# If terminated or truncated finish the episode
if terminated or truncated:
break
# Our next state is the new state
state = new_state
return Qtable
训练 Q-Learning 智能体 🏃
Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)
让我们看看现在的 Q-Learning 表格是什么样子的 👀
Qtable_frozenlake
评估方法 📝
- 我们定义了评估方法,我们将使用该方法来测试我们的 Q-Learning 智能体。
def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):
"""
Evaluate the agent for ``n_eval_episodes`` episodes and returns average reward and std of reward.
:param env: The evaluation environment
:param n_eval_episodes: Number of episode to evaluate the agent
:param Q: The Q-table
:param seed: The evaluation seed array (for taxi-v3)
"""
episode_rewards = []
for episode in tqdm(range(n_eval_episodes)):
if seed:
state, info = env.reset(seed=seed[episode])
else:
state, info = env.reset()
step = 0
truncated = False
terminated = False
total_rewards_ep = 0
for step in range(max_steps):
# Take the action (index) that have the maximum expected future reward given that state
action = greedy_policy(Q, state)
new_state, reward, terminated, truncated, info = env.step(action)
total_rewards_ep += reward
if terminated or truncated:
break
state = new_state
episode_rewards.append(total_rewards_ep)
mean_reward = np.mean(episode_rewards)
std_reward = np.std(episode_rewards)
return mean_reward, std_reward
评估我们的 Q-Learning 智能体 📈
- 通常,您应该获得 1.0 的平均奖励。
- 由于状态空间非常小 (16),因此环境相对容易。您可以尝试做的是将其替换为湿滑版本,这将引入随机性,使环境更加复杂。
# Evaluate our Agent
mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)
print(f"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}")
将我们训练好的模型发布到 Hub 🔥
现在我们看到了训练后的良好结果,我们可以使用一行代码将我们训练好的模型发布到 Hub 🤗。
这是一个模型卡的示例

在底层,Hub 使用基于 git 的存储库(如果您不知道 git 是什么,请不要担心),这意味着您可以在实验和改进智能体时使用新版本更新模型。
请勿修改此代码
from huggingface_hub import HfApi, snapshot_download
from huggingface_hub.repocard import metadata_eval_result, metadata_save
from pathlib import Path
import datetime
import json
def record_video(env, Qtable, out_directory, fps=1):
"""
Generate a replay video of the agent
:param env
:param Qtable: Qtable of our agent
:param out_directory
:param fps: how many frame per seconds (with taxi-v3 and frozenlake-v1 we use 1)
"""
images = []
terminated = False
truncated = False
state, info = env.reset(seed=random.randint(0, 500))
img = env.render()
images.append(img)
while not terminated or truncated:
# Take the action (index) that have the maximum expected future reward given that state
action = np.argmax(Qtable[state][:])
state, reward, terminated, truncated, info = env.step(
action
) # We directly put next_state = state for recording logic
img = env.render()
images.append(img)
imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)
def push_to_hub(repo_id, model, env, video_fps=1, local_repo_path="hub"):
"""
Evaluate, Generate a video and Upload a model to Hugging Face Hub.
This method does the complete pipeline:
- It evaluates the model
- It generates the model card
- It generates a replay video of the agent
- It pushes everything to the Hub
:param repo_id: repo_id: id of the model repository from the Hugging Face Hub
:param env
:param video_fps: how many frame per seconds to record our video replay
(with taxi-v3 and frozenlake-v1 we use 1)
:param local_repo_path: where the local repository is
"""
_, repo_name = repo_id.split("/")
eval_env = env
api = HfApi()
# Step 1: Create the repo
repo_url = api.create_repo(
repo_id=repo_id,
exist_ok=True,
)
# Step 2: Download files
repo_local_path = Path(snapshot_download(repo_id=repo_id))
# Step 3: Save the model
if env.spec.kwargs.get("map_name"):
model["map_name"] = env.spec.kwargs.get("map_name")
if env.spec.kwargs.get("is_slippery", "") == False:
model["slippery"] = False
# Pickle the model
with open((repo_local_path) / "q-learning.pkl", "wb") as f:
pickle.dump(model, f)
# Step 4: Evaluate the model and build JSON with evaluation metrics
mean_reward, std_reward = evaluate_agent(
eval_env, model["max_steps"], model["n_eval_episodes"], model["qtable"], model["eval_seed"]
)
evaluate_data = {
"env_id": model["env_id"],
"mean_reward": mean_reward,
"n_eval_episodes": model["n_eval_episodes"],
"eval_datetime": datetime.datetime.now().isoformat(),
}
# Write a JSON file called "results.json" that will contain the
# evaluation results
with open(repo_local_path / "results.json", "w") as outfile:
json.dump(evaluate_data, outfile)
# Step 5: Create the model card
env_name = model["env_id"]
if env.spec.kwargs.get("map_name"):
env_name += "-" + env.spec.kwargs.get("map_name")
if env.spec.kwargs.get("is_slippery", "") == False:
env_name += "-" + "no_slippery"
metadata = {}
metadata["tags"] = [env_name, "q-learning", "reinforcement-learning", "custom-implementation"]
# Add metrics
eval = metadata_eval_result(
model_pretty_name=repo_name,
task_pretty_name="reinforcement-learning",
task_id="reinforcement-learning",
metrics_pretty_name="mean_reward",
metrics_id="mean_reward",
metrics_value=f"{mean_reward:.2f} +/- {std_reward:.2f}",
dataset_pretty_name=env_name,
dataset_id=env_name,
)
# Merges both dictionaries
metadata = {**metadata, **eval}
model_card = f"""
# **Q-Learning** Agent playing1 **{env_id}**
This is a trained model of a **Q-Learning** agent playing **{env_id}** .
## Usage
model = load_from_hub(repo_id="{repo_id}", filename="q-learning.pkl")
# Don't forget to check if you need to add additional attributes (is_slippery=False etc)
env = gym.make(model["env_id"])
"""
evaluate_agent(env, model["max_steps"], model["n_eval_episodes"], model["qtable"], model["eval_seed"])
readme_path = repo_local_path / "README.md"
readme = ""
print(readme_path.exists())
if readme_path.exists():
with readme_path.open("r", encoding="utf8") as f:
readme = f.read()
else:
readme = model_card
with readme_path.open("w", encoding="utf-8") as f:
f.write(readme)
# Save our metrics to Readme metadata
metadata_save(readme_path, metadata)
# Step 6: Record a video
video_path = repo_local_path / "replay.mp4"
record_video(env, model["qtable"], video_path, video_fps)
# Step 7. Push everything to the Hub
api.upload_folder(
repo_id=repo_id,
folder_path=repo_local_path,
path_in_repo=".",
)
print("Your model is pushed to the Hub. You can view your model here: ", repo_url)
.
通过使用 push_to_hub
,您可以评估、记录回放、生成智能体的模型卡并将其推送到 Hub。
这样
- 您可以展示我们的工作 🔥
- 您可以可视化您的智能体玩游戏 👀
- 您可以与社区分享其他人可以使用的智能体 💾
- 您可以访问排行榜 🏆 以查看您的智能体与同学相比表现如何 👉 https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard
为了能够与社区分享您的模型,还需要遵循三个步骤
1️⃣ (如果尚未完成)创建 HF 帐户 ➡ https://huggingface.co/join
2️⃣ 登录,然后您需要存储来自 Hugging Face 网站的身份验证令牌。
- 创建一个新令牌 (https://huggingface.co/settings/tokens) 并具有写入角色

from huggingface_hub import notebook_login
notebook_login()
如果您不想使用 Google Colab 或 Jupyter Notebook,则需要改用此命令:huggingface-cli login
(或 login
)
3️⃣ 现在我们准备好使用 push_to_hub()
函数将我们训练好的智能体推送到 🤗 Hub 🔥
- 让我们创建包含超参数和 Q_table 的模型字典。
model = {
"env_id": env_id,
"max_steps": max_steps,
"n_training_episodes": n_training_episodes,
"n_eval_episodes": n_eval_episodes,
"eval_seed": eval_seed,
"learning_rate": learning_rate,
"gamma": gamma,
"max_epsilon": max_epsilon,
"min_epsilon": min_epsilon,
"decay_rate": decay_rate,
"qtable": Qtable_frozenlake,
}
让我们填写 push_to_hub
函数
repo_id
:将要创建/更新的 Hugging Face Hub 存储库的名称(repo_id = {username}/{repo_name})
💡 好的repo_id
是{username}/q-{env_id}
model
:我们的模型字典,其中包含超参数和 Q 表。env
:环境。commit_message
:提交消息
model
username = "" # FILL THIS
repo_name = "q-FrozenLake-v1-4x4-noSlippery"
push_to_hub(repo_id=f"{username}/{repo_name}", model=model, env=env)
恭喜 🥳 您刚刚从头开始实施、训练和上传了您的第一个强化学习智能体。FrozenLake-v1 no_slippery 是一个非常简单的环境,让我们尝试一个更难的环境 🔥。
第 2 部分:Taxi-v3 🚖
创建并理解 Taxi-v3 🚕
💡 当您开始使用环境时,一个好的习惯是查看其文档
👉 https://gymnasium.org.cn/environments/toy_text/taxi/
在 Taxi-v3
🚕 中,网格世界中有四个指定的地点,分别用 R(红色)、G(绿色)、Y(黄色) 和 B(蓝色) 表示。
当 episode 开始时,出租车从一个随机的方格开始,乘客在一个随机的位置。出租车开到乘客的位置,接上乘客,开到乘客的目的地(四个指定地点中的另一个),然后放下乘客。一旦乘客被放下,episode 结束。

env = gym.make("Taxi-v3", render_mode="rgb_array")
有 500 个离散状态,因为有 25 个出租车位置、乘客的 5 个可能位置(包括乘客在出租车上的情况)和 4 个目的地位置。
state_space = env.observation_space.n
print("There are ", state_space, " possible states")
action_space = env.action_space.n
print("There are ", action_space, " possible actions")
动作空间(智能体可以采取的可能动作的集合)是离散的,有 6 个可用动作 🎮
- 0:向南移动
- 1:向北移动
- 2:向东移动
- 3:向西移动
- 4:接乘客
- 5:送乘客下车
奖励函数 💰
- 每步 -1,除非触发其他奖励。
- +20 运送乘客。
- 非法执行“接客”和“送客”动作 -10。
# Create our Q table with state_size rows and action_size columns (500x6)
Qtable_taxi = initialize_q_table(state_space, action_space)
print(Qtable_taxi)
print("Q-table shape: ", Qtable_taxi.shape)
定义超参数 ⚙️
⚠ 请勿修改 EVAL_SEED:eval_seed 数组允许我们为每个同学使用相同的出租车起始位置来评估您的智能体
# Training parameters
n_training_episodes = 25000 # Total training episodes
learning_rate = 0.7 # Learning rate
# Evaluation parameters
n_eval_episodes = 100 # Total number of test episodes
# DO NOT MODIFY EVAL_SEED
eval_seed = [
16,
54,
165,
177,
191,
191,
120,
80,
149,
178,
48,
38,
6,
125,
174,
73,
50,
172,
100,
148,
146,
6,
25,
40,
68,
148,
49,
167,
9,
97,
164,
176,
61,
7,
54,
55,
161,
131,
184,
51,
170,
12,
120,
113,
95,
126,
51,
98,
36,
135,
54,
82,
45,
95,
89,
59,
95,
124,
9,
113,
58,
85,
51,
134,
121,
169,
105,
21,
30,
11,
50,
65,
12,
43,
82,
145,
152,
97,
106,
55,
31,
85,
38,
112,
102,
168,
123,
97,
21,
83,
158,
26,
80,
63,
5,
81,
32,
11,
28,
148,
] # Evaluation seed, this ensures that all classmates agents are trained on the same taxi starting position
# Each seed has a specific starting state
# Environment parameters
env_id = "Taxi-v3" # Name of the environment
max_steps = 99 # Max steps per episode
gamma = 0.95 # Discounting rate
# Exploration parameters
max_epsilon = 1.0 # Exploration probability at start
min_epsilon = 0.05 # Minimum exploration probability
decay_rate = 0.005 # Exponential decay rate for exploration prob
训练我们的 Q-Learning 智能体 🏃
Qtable_taxi = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_taxi) Qtable_taxi
创建模型字典 💾 并将我们训练好的模型发布到 Hub 🔥
- 我们创建一个模型字典,其中将包含所有用于重现性的训练超参数和 Q 表。
model = {
"env_id": env_id,
"max_steps": max_steps,
"n_training_episodes": n_training_episodes,
"n_eval_episodes": n_eval_episodes,
"eval_seed": eval_seed,
"learning_rate": learning_rate,
"gamma": gamma,
"max_epsilon": max_epsilon,
"min_epsilon": min_epsilon,
"decay_rate": decay_rate,
"qtable": Qtable_taxi,
}
username = "" # FILL THIS
repo_name = "" # FILL THIS
push_to_hub(repo_id=f"{username}/{repo_name}", model=model, env=env)
现在它已在 Hub 上,您可以使用排行榜 🏆 将您的 Taxi-v3 结果与您的同学进行比较 👉 https://huggingface.co/spaces/huggingface-projects/Deep-Reinforcement-Learning-Leaderboard

第 3 部分:从 Hub 加载 🔽
Hugging Face Hub 🤗 最令人惊叹的地方在于您可以轻松地从社区加载强大的模型。
从 Hub 加载已保存的模型非常容易
- 您可以访问 https://huggingface.co/models?other=q-learning 以查看所有已保存的 q-learning 模型的列表。
- 您选择一个并复制其 repo_id

- 然后我们只需要将
load_from_hub
与
- repo_id
- 文件名:存储库中已保存的模型。
请勿修改此代码
from urllib.error import HTTPError
from huggingface_hub import hf_hub_download
def load_from_hub(repo_id: str, filename: str) -> str:
"""
Download a model from Hugging Face Hub.
:param repo_id: id of the model repository from the Hugging Face Hub
:param filename: name of the model zip file from the repository
"""
# Get the model from the Hub, download and cache the model on your local disk
pickle_model = hf_hub_download(repo_id=repo_id, filename=filename)
with open(pickle_model, "rb") as f:
downloaded_model_file = pickle.load(f)
return downloaded_model_file
.
model = load_from_hub(repo_id="ThomasSimonini/q-Taxi-v3", filename="q-learning.pkl") # Try to use another model
print(model)
env = gym.make(model["env_id"])
evaluate_agent(env, model["max_steps"], model["n_eval_episodes"], model["qtable"], model["eval_seed"])
model = load_from_hub(
repo_id="ThomasSimonini/q-FrozenLake-v1-no-slippery", filename="q-learning.pkl"
) # Try to use another model
env = gym.make(model["env_id"], is_slippery=False)
evaluate_agent(env, model["max_steps"], model["n_eval_episodes"], model["qtable"], model["eval_seed"])
一些额外的挑战 🏆
最好的学习方式是自己尝试!正如您所看到的,当前的智能体表现不佳。作为第一个建议,您可以训练更多步骤。经过 1,000,000 步的训练,我们看到了一些出色的结果!
在排行榜中,您将找到您的智能体。您能登上榜首吗?
以下是一些登上排行榜的想法
- 训练更多步骤
- 通过查看您的同学所做的事情,尝试不同的超参数。
- 将您新的训练模型推送到 Hub 🔥
在冰上行走和驾驶出租车对您来说太无聊了吗?尝试更改环境,为什么不使用 FrozenLake-v1 湿滑版本?查看它们如何工作使用 gymnasium 文档,玩得开心 🎉。
恭喜 🥳,您刚刚实施、训练和上传了您的第一个强化学习智能体。
理解 Q-Learning 是理解基于价值的方法的重要一步。
在下一个关于深度 Q-Learning 的单元中,我们将看到,虽然创建和更新 Q 表是一个好策略 —— 但是,它不具有可扩展性。
例如,想象一下您创建了一个学习玩 Doom 的智能体。

Doom 是一个大型环境,具有巨大的状态空间(数百万种不同的状态)。为该环境创建和更新 Q 表将效率低下。
这就是为什么我们将在下一个单元中学习深度 Q-Learning,这是一种算法,我们使用神经网络来近似给定状态的每个动作的不同 Q 值。

Unit 3 见! 🔥