5.5 强化学习 (Reinforcement Learning - RL) 第五章:PyTorch 实战应用领域 - 5.5 强化学习 (Reinforcement Learning - RL) 详解与代码实践 5.5.1 强化学习概述:智能体在环境中学习 强化学习的核心思想是让 智能体 (Agent) 在 环境 (Environment) 中通过与环境的 交互 (Interaction) 学习最优策略,以达成特定的 目标 (Goal)。这种学习方式与人类的学习过程非常相似,我们通过尝试和错误,从环境中获得反馈 (奖励或惩罚),并根据反馈调整自己的行为,最终学会完成任务。 以下是一个强化学习的基本框架,可以用 Mermaid 的 图表示: 图 5.5.
5.5.1 强化学习概述:智能体在环境中学习
强化学习的核心思想是让 智能体 (Agent) 在 环境 (Environment) 中通过与环境的 交互 (Interaction) 学习最优策略,以达成特定的 目标 (Goal)。这种学习方式与人类的学习过程非常相似,我们通过尝试和错误,从环境中获得反馈 (奖励或惩罚),并根据反馈调整自己的行为,最终学会完成任务。
以下是一个强化学习的基本框架,可以用 Mermaid 的 graph TD 图表示:
图 5.5.1 强化学习的基本交互循环
解释:
智能体 (Agent): 是学习和决策的主体,例如一个游戏中的 AI 角色,或者一个控制机器人运动的程序。智能体的目标是在环境中采取行动以最大化累积奖励。
环境 (Environment): 是智能体所处的世界,它可以是虚拟的游戏环境,也可以是真实的物理世界。环境接收智能体的动作,并返回新的状态和奖励。
状态 (State) (s_t): 是对环境当前情况的描述,智能体根据当前状态来决定下一步的动作。状态可以是图像、传感器数据、游戏中的场景信息等。
动作 (Action) (a_t): 是智能体在当前状态下可以采取的行为。动作的集合称为 动作空间 (Action Space)。动作空间可以是离散的(例如,上下左右),也可以是连续的(例如,控制机器人的关节角度)。
奖励 (Reward) (r_{t+1}): 是环境对智能体采取的动作的反馈信号,用于评估动作的好坏。奖励可以是正面的(鼓励)或负面的(惩罚)。强化学习的目标是最大化累积奖励。
策略 (Policy) (π): 是智能体根据当前状态选择动作的规则,策略可以是确定性的(给定状态,输出唯一的动作),也可以是随机性的(给定状态,输出动作的概率分布)。强化学习的核心目标是学习一个最优策略。
价值函数 (Value Function): 用于评估在特定状态或状态-动作对下的“好坏”程度,帮助智能体做出决策。常见的价值函数包括:
状态价值函数 V(s): 评估在状态 s 下,遵循策略 π 所能获得的期望累积奖励。
动作价值函数 Q(s, a): 评估在状态 s 下,执行动作 a,并后续遵循策略 π 所能获得的期望累积奖励。
5.5.2 强化学习的关键要素
理解强化学习,需要掌握以下几个关键要素:
马尔可夫决策过程 (Markov Decision Process - MDP): 强化学习问题通常被建模为 MDP。MDP 假设环境具有马尔可夫性质,即未来状态只依赖于当前状态和动作,而与过去的状态无关。MDP 由以下五个要素组成:
状态集合 (S): 所有可能状态的集合。
动作集合 (A): 所有可能动作的集合。
转移概率 (P(s'|s, a)): 在状态 s 下执行动作 a 后,转移到状态 s' 的概率。
奖励函数 (R(s, a, s')): 在状态 s 下执行动作 a 后,转移到状态 s' 并获得的奖励。
折扣因子 (γ): 用于衡量未来奖励的重要性,取值范围为 [0, 1]。折扣因子越小,智能体越关注即时奖励;折扣因子越大,智能体越关注长期奖励。
策略 (Policy) 与价值函数 (Value Function): 策略是智能体行为的指南,价值函数是评估策略好坏的工具。强化学习的目标是找到一个最优策略,使其对应的价值函数最大化。
探索 (Exploration) 与利用 (Exploitation): 智能体需要在探索和利用之间进行权衡。
探索 (Exploration): 尝试新的动作,以发现环境中潜在的更优策略。
利用 (Exploitation): 根据已知的知识,选择当前认为最优的动作,以获得尽可能高的奖励。
在学习初期,智能体需要更多地进行探索,以了解环境;随着学习的深入,智能体可以更多地进行利用,以最大化累积奖励。
强化学习算法分类: 强化学习算法可以根据不同的标准进行分类,例如:
基于模型 (Model-Based) vs. 无模型 (Model-Free):
基于模型: 智能体尝试学习环境的模型(例如,转移概率和奖励函数),然后利用模型进行规划和决策。例如,动态规划 (Dynamic Programming)。
无模型: 智能体不显式地学习环境模型,而是直接学习策略或价值函数。例如,Q-Learning, SARSA, Policy Gradient。
基于价值 (Value-Based) vs. 基于策略 (Policy-Based) vs. Actor-Critic:
基于价值: 智能体学习价值函数,然后根据价值函数选择动作。例如,Q-Learning, DQN。
基于策略: 智能体直接学习策略,例如,Policy Gradient, A3C, PPO。
Actor-Critic: 结合了价值函数和策略的方法,Actor 负责学习策略,Critic 负责评估策略的好坏。例如,A2C, DDPG。
在线学习 (On-Policy) vs. 离线学习 (Off-Policy):
在线学习: 智能体使用当前策略产生的数据来更新当前策略。例如,SARSA, Policy Gradient。
离线学习: 智能体可以使用过去经验的数据(例如,来自其他策略的数据)来更新当前策略。例如,Q-Learning, DQN。
5.5.3 PyTorch 中的强化学习实践:基于 DQN 的 CartPole 示例
为了更好地理解强化学习在 PyTorch 中的应用,我们以经典的 CartPole 环境为例,演示如何使用 深度 Q 网络 (Deep Q-Network - DQN) 算法来训练一个智能体,使其能够平衡杆子。
CartPole 环境介绍:
CartPole 是 OpenAI Gym 中的一个经典环境。环境包含一个可以左右移动的小车,以及一个通过铰链连接在小车上的杆子。智能体的目标是通过控制小车的左右移动,使得杆子保持竖直状态尽可能长的时间。
环境状态 (State): CartPole 环境的状态包括 4 个维度:
小车位置 (Cart Position)
小车速度 (Cart Velocity)
杆子角度 (Pole Angle)
杆子角速度 (Pole Velocity)
动作空间 (Action Space): CartPole 环境的动作空间是离散的,包含 2 个动作:
0: 向左推动小车 (Push cart to the left)
1: 向右推动小车 (Push cart to the right)
奖励 (Reward): 每当杆子保持在竖直状态时,智能体获得 +1 的奖励。当杆子倾斜角度超过一定阈值,或者小车超出边界时,回合结束。
DQN 算法简介:
DQN 算法是一种基于价值的、离线的强化学习算法,它使用深度神经网络来近似 Q 函数,并结合了 经验回放 (Experience Replay) 和 目标网络 (Target Network) 等技术,以提高学习的稳定性和效率。
DQN 算法流程 (简要):
初始化:
初始化 Q 网络 (Q-network) 和目标网络 (Target network),目标网络参数与 Q 网络相同。
初始化经验回放缓冲区 (Replay buffer)。
循环迭代:
选择动作: 根据 ε-greedy 策略,以 ε 的概率选择随机动作进行探索,以 1-ε 的概率选择 Q 网络预测的 Q 值最大的动作进行利用。
执行动作并观察: 在环境中执行选择的动作,获得新的状态、奖励和是否回合结束的信息。
存储经验: 将 (状态, 动作, 奖励, 下一个状态, 是否回合结束) 存储到经验回放缓冲区。
经验回放: 从经验回放缓冲区中随机采样一批经验。
计算目标 Q 值: 使用目标网络和 Bellman 方程计算目标 Q 值。
更新 Q 网络: 使用目标 Q 值和当前 Q 网络预测的 Q 值计算损失函数 (例如,均方误差),并使用梯度下降算法更新 Q 网络参数。
更新目标网络: 每隔一定步数,将 Q 网络参数复制到目标网络。
重复步骤 2,直到训练完成。
PyTorch 代码实现:
以下是使用 PyTorch 实现 DQN 算法解决 CartPole 环境的代码示例,代码中包含了详细的注释:
import torch import torch.nn as nn import torch.optim as optim import gym import random import numpy as np # 超参数设置 BUFFER_SIZE = 10000 # 经验回放缓冲区大小 BATCH_SIZE = 64 # 批量大小 GAMMA = 0.99 # 折扣因子 TAU = 0.005 # 目标网络软更新参数 LR = 1e-4 # 学习率 UPDATE_EVERY = 4 # 每隔多少步更新一次 Q 网络 TARGET_UPDATE_EVERY = 100 # 每隔多少步更新一次目标网络 EPS_START = 0.9 # ε-greedy 策略的初始 ε 值 EPS_END = 0.05 # ε-greedy 策略的最终 ε 值 EPS_DECAY = 1000 # ε-greedy 策略的衰减率 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 定义 Q 网络 class QNetwork(nn.Module): def __init__(self, state_size, action_size, seed): super(QNetwork, self).__init__() self.seed = torch.manual_seed(seed) self.fc1 = nn.Linear(state_size, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_size) def forward(self, state): x = torch.relu(self.fc1(state)) x = torch.relu(self.fc2(x)) return self.fc3(x) # 定义经验回放缓冲区 class ReplayBuffer: def __init__(self, action_size, buffer_size, batch_size, seed): self.action_size = action_size self.buffer_size = buffer_size self.batch_size = batch_size self.memory = [] self.seed = random.seed(seed) self.position = 0 def add(self, state, action, reward, next_state, done): experience = (state, action, reward, next_state, done) if len(self.memory) < self.buffer_size: self.memory.append(experience) else: self.memory[self.position] = experience self.position = (self.position + 1) % self.buffer_size def sample(self): experiences = random.sample(self.memory, k=self.batch_size) states = torch.from_numpy(np.vstack([e[0] for e in experiences if e is not None])).float().to(device) actions = torch.from_numpy(np.vstack([e[1] for e in experiences if e is not None])).long().to(device) rewards = torch.from_numpy(np.vstack([e[2] for e in experiences if e is not None])).float().to(device) next_states = torch.from_numpy(np.vstack([e[3] for e in experiences if e is not None])).float().to(device) dones = torch.from_numpy(np.vstack([e[4] for e in experiences if e is not None]).astype(np.uint8)).float().to(device) return (states, actions, rewards, next_states, dones) def __len__(self): return len(self.memory) # DQN 智能体 class DQNAgent: def __init__(self, state_size, action_size, seed): self.state_size = state_size self.action_size = action_size self.seed = random.seed(seed) # Q 网络和目标网络 self.qnetwork_local = QNetwork(state_size, action_size, seed).to(device) self.qnetwork_target = QNetwork(state_size, action_size, seed).to(device) self.optimizer = optim.Adam(self.qnetwork_local.parameters(), lr=LR) # 经验回放缓冲区 self.memory = ReplayBuffer(action_size, BUFFER_SIZE, BATCH_SIZE, seed) # 学习步数计数器 self.t_step = 0 def step(self, state, action, reward, next_state, done): # 将经验添加到经验回放缓冲区 self.memory.add(state, action, reward, next_state, done) # 每隔 UPDATE_EVERY 步进行学习 self.t_step = (self.t_step + 1) % UPDATE_EVERY if self.t_step == 0: if len(self.memory) > BATCH_SIZE: experiences = self.memory.sample() self.learn(experiences, GAMMA) def act(self, state, eps=0.): state = torch.from_numpy(state).float().unsqueeze(0).to(device) self.qnetwork_local.eval() # 设置为评估模式 with torch.no_grad(): action_values = self.qnetwork_local(state) self.qnetwork_local.train() # 设置为训练模式 # ε-greedy 策略 if random.random() > eps: return np.argmax(action_values.cpu().data.numpy()) else: return random.choice(np.arange(self.action_size)) def learn(self, experiences, gamma): states, actions, rewards, next_states, dones = experiences # 获取目标 Q 值 Q_targets_next = self.qnetwork_target(next_states).detach().max(1)[0].unsqueeze(1) Q_targets = rewards + (gamma * Q_targets_next * (1 - dones)) # 获取本地 Q 值 Q_expected = self.qnetwork_local(states).gather(1, actions) # 计算损失函数 (均方误差) loss = nn.MSELoss()(Q_expected, Q_targets) # 最小化损失 self.optimizer.zero_grad() loss.backward() self.optimizer.step() # 软更新目标网络 self.soft_update(self.qnetwork_local, self.qnetwork_target, TAU) def soft_update(self, local_model, target_model, tau): for target_param, local_param in zip(target_model.parameters(), local_model.parameters()): target_param.data.copy_(tau*local_param.data + (1.0-tau)*target_param.data) # 主训练函数 def dqn(n_episodes=2000, max_t=1000, eps_start=EPS_START, eps_end=EPS_END, eps_decay=EPS_DECAY): env = gym.make('CartPole-v1') state_size = env.observation_space.shape[0] action_size = env.action_space.n agent = DQNAgent(state_size=state_size, action_size=action_size, seed=0) scores = [] eps = eps_start for i_episode in range(1, n_episodes+1): state = env.reset()[0] # Gym version update, now reset returns tuple score = 0 for t in range(max_t): action = agent.act(state, eps) next_state, reward, done, _, _ = env.step(action) # Gym version update, step returns 5 values agent.step(state, action, reward, next_state, done) state = next_state score += reward if done: break eps = max(eps_end, eps_decay*eps) # 线性衰减 epsilon scores.append(score) avg_score = np.mean(scores[-100:]) # 计算最近 100 个 episodes 的平均得分 print(f"\rEpisode {i_episode}\tAverage Score: {avg_score:.2f}\tEpsilon: {eps:.2f}", end="") if i_episode % 100 == 0: print(f"\rEpisode {i_episode}\tAverage Score: {avg_score:.2f}\tEpsilon: {eps:.2f}") if avg_score >= 195.0: # 当平均得分超过 195 时,认为训练完成 (CartPole-v1 的目标分数) print(f"\nEnvironment solved in {i_episode-100:d} episodes!\tAverage Score: {avg_score:.2f}") torch.save(agent.qnetwork_local.state_dict(), 'checkpoint.pth') break return scores if __name__ == '__main__': scores = dqn() import matplotlib.pyplot as plt plt.plot(np.arange(len(scores)), scores) plt.ylabel('Score') plt.xlabel('Episode #') plt.show()
代码详解:
超参数设置: 代码首先定义了一些重要的超参数,例如经验回放缓冲区大小、批量大小、折扣因子、学习率等。这些超参数会影响 DQN 算法的性能,需要根据具体问题进行调整。
QNetwork 类: 定义了 Q 网络的结构,这是一个简单的三层全连接神经网络。输入是状态 (state_size),输出是每个动作的 Q 值 (action_size)。
ReplayBuffer 类: 实现了经验回放缓冲区,用于存储智能体与环境交互产生的经验,并在训练时随机采样经验批次。
DQNAgent 类: 实现了 DQN 智能体,包含了 Q 网络、目标网络、经验回放缓冲区、优化器等组件,以及 step(), act(), learn() 等核心方法。
step() 方法:接收环境返回的经验,将其存储到经验回放缓冲区,并判断是否需要进行学习。
act() 方法:根据当前状态,使用 ε-greedy 策略选择动作。
learn() 方法:从经验回放缓冲区采样经验,计算目标 Q 值,更新 Q 网络参数,并软更新目标网络。
dqn() 函数: 是主训练函数,负责与 CartPole 环境交互,训练 DQN 智能体。
初始化 CartPole 环境和 DQN 智能体。
循环进行 episodes 训练,每个 episode 中:
重置环境。
循环进行 steps 交互,每个 step 中:
智能体根据当前状态选择动作。
环境执行动作,返回新的状态、奖励、是否回合结束等信息。
智能体将经验存储到经验回放缓冲区,并进行学习。
记录每个 episode 的得分,并计算最近 100 个 episodes 的平均得分。
根据平均得分判断是否训练完成。
主程序: 调用 dqn() 函数进行训练,并绘制得分曲线。
运行代码:
运行上述代码,可以看到 CartPole 智能体在训练过程中逐渐学会平衡杆子,得分不断提高。当平均得分达到 195 以上时,程序会输出 "Environment solved!",并保存训练好的 Q 网络模型参数。
5.5.4 总结与展望
本节详细介绍了强化学习的基本概念、关键要素和 DQN 算法,并结合 PyTorch 代码实践,演示了如何使用 DQN 算法解决 CartPole 环境问题。通过本节的学习,读者应该对强化学习有了初步的了解,并掌握了如何使用 PyTorch 构建和训练简单的强化学习模型。
强化学习是一个充满活力的研究领域,未来发展方向包括:
更强大的算法: 例如,基于模型的强化学习、元强化学习、多智能体强化学习等。
更广泛的应用: 例如,自动驾驶、机器人控制、推荐系统、医疗诊断、金融交易等。
与深度学习更紧密的结合: 深度强化学习 (Deep Reinforcement Learning) 已经成为强化学习的主流方向,未来将会有更多更深入的结合。
PyTorch 作为强大的深度学习框架,为强化学习的研究和应用提供了有力的工具。掌握 PyTorch 中的强化学习技术,将有助于我们应对更加复杂和具有挑战性的实际问题,并推动人工智能技术的进步。
进一步学习建议:
OpenAI Gym: 熟悉 OpenAI Gym 环境,尝试在更多环境中应用强化学习算法。
深度强化学习书籍和课程: 深入学习深度强化学习的理论和算法,例如,Richard S. Sutton 和 Andrew G. Barto 的《Reinforcement Learning: An Introduction》,David Silver 的 UCL 强化学习课程等。
PyTorch 官方文档和教程: 深入学习 PyTorch 的各种功能和模块,提高 PyTorch 编程能力。
阅读最新的强化学习论文: 关注最新的强化学习研究进展,了解最新的算法和应用。
希望本节内容能够帮助读者入门强化学习,并激发大家对强化学习的兴趣和热情!