机器学习038:强化学习【DQN、PPO和策略梯度比较】
mindmaproot(强化学习三大经典算法)策略梯度Policy Gradient核心思想直接学习策略输入状态→输出动作概率关键特性处理连续动作自然探索性优点连续动作适用策略灵活缺点高方差样本效率低适用场景机器人控制连续控制任务DQN核心思想学习价值函数状态+动作→预期回报关键创新经验回放目标网络优点离散动作强样本效率较高从像素学习缺点无法处理连续动作可能高估价值适用场景视频游戏AI离散决策问题
想象一下:你养了一只小狗,想教会它“坐下”这个指令。你不会直接告诉它“坐下”,而是当它偶然坐下时,给它一块零食作为奖励。慢慢地,小狗就明白了“坐下=有零食”,于是更愿意坐下。这个过程就是强化学习的核心思想。
在强化学习中,DQN、PPO和策略梯度就像是三种不同的“训狗方法”,各有特色。今天,我们就来认识这三位“驯兽师”,看看它们如何教会AI在复杂环境中做出聪明的决策。
第一部分:分类归属——它们在AI大家族中的位置
1.1 宏观定位
强化学习是机器学习的一个分支,专门研究智能体如何通过与环境互动来学习最优策略。与我们熟悉的监督学习(有标准答案)不同,强化学习更像是“试错学习”。
如果把AI比作学生:
- 监督学习:老师直接告诉你“1+1=2”
- 无监督学习:给你一堆数字,让你自己找规律
- 强化学习:让你去玩一个游戏,赢了加分,输了扣分,你自己摸索怎么玩能得高分
策略梯度、DQN、PPO都属于强化学习算法,它们的共同目标是:教会AI做出一系列决策,以最大化长期奖励。
1.2 三位作者的“诞生记”
策略梯度(Policy Gradient, 1992年左右)
- 作者:Richard Sutton等强化学习先驱
- 时间:1990年代初期
- 背景:早期强化学习方法(如Q学习)在处理连续动作空间或高维问题时遇到困难
- 解决的问题:如何直接学习策略本身(即“在什么状态下应该做什么动作”的概率分布)
DQN(Deep Q-Network, 2013年)
- 作者:DeepMind团队(Volodymyr Mnih等)
- 时间:2013年论文,2015年《自然》杂志发表改进版
- 背景:深度学习兴起,研究者想结合深度神经网络与强化学习
- 解决的问题:让AI直接从图像像素学习玩电子游戏(如Atari游戏),无需人类先提取特征
PPO(Proximal Policy Optimization, 2017年)
- 作者:OpenAI团队(John Schulman等)
- 时间:2017年
- 背景:之前的策略梯度方法训练不稳定,容易“学坏”
- 解决的问题:如何让策略梯度稳定训练,避免一次更新太大导致策略崩溃
第二部分:底层原理——三种“训狗法”详解
让我们用一个共同的例子来理解这三种方法:教AI玩“平衡木”游戏(类似经典的CartPole)。
游戏规则:
- 一根杆子底部连接一个小车
- AI可以控制小车向左或向右移动
- 目标:保持杆子不倒
- 杆子每保持平衡1步得1分,倒了游戏结束
2.1 策略梯度(Policy Gradient)——凭感觉的艺术家
核心思想:直接学习“做什么”的概率
想象一下教小狗接飞盘:
- 你扔出飞盘
- 小狗尝试跳跃接住
- 如果接住了,你就强化它刚才那一跳的动作
- 下次类似情况,它更可能用类似方式跳
通俗解释:
策略梯度不关心“每个动作具体值多少分”,而是直接学习一个策略函数。这个函数输入当前状态(如杆子倾斜角度),输出各个动作的概率(如:左移60%,右移40%)。
训练过程:
- 用当前策略玩一局游戏,记录所有“状态-动作-奖励”
- 计算每个动作的“好坏程度”(优势值)
- 好的动作:增加其概率
- 坏的动作:减少其概率
关键设计:
- 参数化策略:策略是一个神经网络,输入状态,输出动作概率
- 梯度上升:沿着增加奖励的方向更新网络参数
伪代码逻辑:
1. 初始化策略网络(随机)
2. 重复很多次:
3. 用当前策略玩一局游戏
4. 计算每个动作的“优势”(这个动作比平均好多少)
5. 更新策略:好动作的概率↑,坏动作的概率↓
优点:
- 能处理连续动作(如方向盘转多少度)
- 探索自然(按概率选择,有一定随机性)
缺点:
- 训练不稳定,容易“一步错步步错”
- 样本效率低(需要很多次尝试)
2.2 DQN(Deep Q-Network)——精打细算的会计师
核心思想:先评价,再行动
想象教小狗去迷宫找食物:
- 你告诉小狗:“从A点到B点有5分,从B点到食物有10分”
- 小狗计算每条路径的总分
- 选择总分最高的路径
通俗解释:
DQN学习一个价值函数(Q函数),这个函数评估“在某个状态下,采取某个动作,预计能获得多少未来总奖励”。然后AI总是选择价值最高的动作。
两大创新:
-
经验回放(Experience Replay):
- 像人“复盘”一样,把过去的经历存起来
- 训练时随机抽取一些旧经历学习
- 避免“学了新的忘了旧的”
-
目标网络(Target Network):
- 用两个神经网络:一个在线更新,一个相对固定
- 避免“追着自己的尾巴跑”的不稳定问题
训练过程:
- 初始化Q网络
- 观察状态,用Q网络选择动作(有时随机探索)
- 执行动作,获得奖励和下一状态
- 将经历存入“记忆库”
- 从记忆库随机取一批数据训练Q网络
- 更新目标网络(缓慢更新)
贝尔曼方程(DQN的核心):
新Q值 = 即时奖励 + 折扣因子 × 下一状态的最佳Q值
通俗理解:
这个动作的价值 = 立刻得到的奖励 + 未来可能得到奖励的折扣总和
优点:
- 相对稳定
- 样本效率较高(可以重复学习旧数据)
- 在离散动作空间表现好
缺点:
- 难以处理连续动作(动作太多,算不过来)
- 可能高估Q值,导致过于乐观
2.3 PPO(Proximal Policy Optimization)——稳健的工程师
核心思想:小步快跑,别扯着蛋
想象训练跳水运动员:
- 运动员尝试一个新动作
- 教练评估这个动作比旧动作好多少
- 如果好一点,就保留这个改进
- 如果差很多,就退回来一点
- 每次只改进一点点,保证安全
通俗解释:
PPO也是一种策略梯度方法,但加了一个“约束”:新策略不能离旧策略太远。这样既改进策略,又避免一次改太多导致崩溃。
核心技巧:比例裁剪
- 计算新策略和旧策略的概率比
- 如果这个比例在(1-ε, 1+ε)之间,正常更新
- 如果超出这个范围,就裁剪到边界
- 保证每次更新都是“微调”而不是“大变”
训练过程:
- 用当前策略收集一批数据
- 计算每个动作的优势值
- 计算新策略与旧策略的概率比
- 裁剪这个比例,防止太大变化
- 用裁剪后的目标函数更新策略
PPO目标函数(简化):
最大化:裁剪后的概率比 × 优势值
通俗理解:
在“不太偏离旧策略”的前提下,尽量增加好动作的概率
优点:
- 非常稳定,不容易崩
- 样本效率高
- 调参相对简单
缺点:
- 理论复杂
- 需要更多计算资源
第三部分:局限性——没有完美的算法
3.1 策略梯度的局限
问题1:高方差
- 表现:训练过程像坐过山车,成绩忽高忽低
- 原因:完全依赖当前策略收集的数据,如果一批数据“运气好”,策略就往那个方向猛调;下批数据“运气差”,又往反方向猛调
- 类比:根据一次考试判断学习方法好坏,考好了就觉得方法完美,考差了就全盘否定
问题2:样本效率低
- 表现:需要玩很多很多局游戏才能学会
- 原因:每更新一次策略,就要用新策略重新收集数据,旧数据不能复用
- 类比:每次调整学习方法后,就把以前做的题都忘了,重新开始做新题
3.2 DQN的局限
问题1:无法处理连续动作
- 表现:只能处理“离散、有限”的动作(如:上、下、左、右)
- 原因:需要计算每个动作的Q值,如果动作是连续的(如方向盘角度从0到360度),有无数个可能,算不过来
- 类比:点菜时,如果菜单只有10道菜,可以每道都尝一下评分;如果有1000道菜,就尝不过来了
问题2:高估偏差
- 表现:可能过于乐观,高估某些动作的价值
- 原因:用最大值估计未来价值,误差会累积放大
- 类比:预估旅行时间时,总是假设每个环节都最佳情况,结果实际总是迟到
3.3 PPO的局限
问题1:计算复杂度高
- 表现:训练速度相对较慢
- 原因:需要维护新旧两个策略,计算概率比等额外信息
- 类比:写文章时,不仅写新版本,还要不断和旧版本对比,确保没有偏离太多
问题2:探索可能不足
- 表现:在需要大量探索的环境中可能陷入局部最优
- 原因:“小步更新”的保守特性,可能不敢尝试全新的策略
- 类比:总是微调现有方案,不敢尝试完全不同的新思路
第四部分:使用范围——何时用谁?
4.1 策略梯度适合的场景
适合:
- 连续动作空间:如机械臂控制(每个关节的角度是连续值)
- 随机策略有用:如石头剪刀布游戏(需要不可预测性)
- 策略本身简单:动作选择逻辑不复杂
不适合:
- 需要高效利用数据:数据收集成本高的场景(如机器人实际损坏成本高)
- 训练稳定性要求高:不能接受成绩大起大落的场景
4.2 DQN适合的场景
适合:
- 离散动作空间:如游戏(有限的按键)、推荐系统(有限的选项)
- 状态可观测:如游戏画面完全可见
- 需要从原始输入学习:如从像素直接学习
不适合:
- 连续控制:如自动驾驶(方向盘角度连续)
- 动作空间巨大:如围棋(虽然离散,但可能太多)
- 部分可观测环境:如扑克牌(看不到对手手牌)
4.3 PPO适合的场景
适合:
- 需要稳定训练:工业控制、机器人等不能接受训练崩溃的场景
- 连续或离散都行:通用性较好
- 复杂环境:如需要多智能体协作
不适合:
- 极度简单的环境:杀鸡用牛刀
- 计算资源有限:如手机端实时学习
- 需要极端探索:如完全未知的新环境
第五部分:应用场景——它们如何改变世界
5.1 游戏AI:从雅达利到星际争霸
案例1:DQN玩Atari游戏(DeepMind, 2015)
- 场景:经典雅达利2600游戏(如打砖块、太空入侵者)
- 方法:DQN
- 作用:输入原始像素,输出游戏手柄动作(左、右、开火等)
- 成就:在29款游戏中,超过人类专业玩家水平
- 为什么用DQN:游戏动作离散(几个按键),适合Q学习框架
案例2:AlphaStar玩星际争霸2(DeepMind, 2019)
- 场景:复杂即时战略游戏,需要长期规划、资源管理、多线操作
- 方法:基于PPO的算法
- 作用:控制数百个单位,制定宏观战略和微观操作
- 成就:击败人类世界冠军
- 为什么用PPO:动作空间巨大但结构化,需要稳定训练
5.2 机器人控制:从仿真到现实
案例3:机械臂抓取物体(OpenAI, 2018)
- 场景:让机械手学会抓取各种形状的物体
- 方法:PPO
- 作用:控制每个手指关节的角度和力度
- 成就:单手解魔方
- 为什么用PPO:连续动作空间,需要稳定安全的训练
案例4:四足机器人行走(Boston Dynamics灵感)
- 场景:让机器狗在复杂地形行走
- 方法:策略梯度/PPO
- 作用:控制每条腿的摆动幅度、频率、力度
- 应用:灾难救援、危险环境勘察
- 为什么用策略类方法:连续动作,需要适应未知地形
5.3 自动驾驶:虚拟训练,现实应用
案例5:自动驾驶决策系统
- 场景:在复杂交通环境中安全驾驶
- 方法:多种结合,PPO常用于高层决策
- 作用:决定变道、超车、跟车距离等
- 训练方式:先在模拟器中训练数百万公里,再迁移到现实
- 为什么用PPO:动作连续(方向盘角度、油门深度),安全要求高
5.4 推荐系统:个性化内容推送
案例6:视频推荐(如YouTube, TikTok)
- 场景:从海量内容中选择用户可能喜欢的下一个视频
- 方法:改进的DQN/策略梯度
- 作用:平衡“用户已知喜好”和“探索新兴趣”
- 挑战:用户的兴趣会变化,环境非静态
- 为什么用强化学习:考虑长期用户满意度,不只是点击率
5.5 能源管理:智能电网优化
案例7:数据中心冷却系统控制(Google, 2018)
- 场景:降低数据中心能耗,同时保证设备温度安全
- 方法:DQN/PPO
- 作用:控制风扇速度、冷却水流量等
- 成果:节能40%
- 为什么用强化学习:系统复杂,难以用传统规则描述最优控制
第六部分:实践案例——用Python实现平衡木游戏
下面我们用PPO算法(目前最实用的方法)来教AI玩平衡木游戏。即使你是初学者,也可以尝试运行这段代码!
"""
用PPO算法训练AI玩CartPole平衡木游戏
环境:一根杆子底部连接小车,AI控制小车左右移动保持杆子不倒
目标:杆子保持平衡的时间越长越好
"""
import gym
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.distributions import Categorical
import matplotlib.pyplot as plt
# 1. 定义策略网络(Actor)和价值网络(Critic)
class PolicyNetwork(nn.Module):
"""策略网络:输入状态,输出动作概率"""
def __init__(self, state_dim, action_dim):
super(PolicyNetwork, self).__init__()
self.fc = nn.Sequential(
nn.Linear(state_dim, 64),
nn.Tanh(),
nn.Linear(64, 64),
nn.Tanh(),
nn.Linear(64, action_dim),
nn.Softmax(dim=-1) # 输出概率,总和为1
)
def forward(self, state):
return self.fc(state)
class ValueNetwork(nn.Module):
"""价值网络:评估状态的价值"""
def __init__(self, state_dim):
super(ValueNetwork, self).__init__()
self.fc = nn.Sequential(
nn.Linear(state_dim, 64),
nn.Tanh(),
nn.Linear(64, 64),
nn.Tanh(),
nn.Linear(64, 1) # 输出一个价值标量
)
def forward(self, state):
return self.fc(state)
# 2. PPO算法主类
class PPO:
def __init__(self, state_dim, action_dim):
# 超参数
self.lr = 0.001 # 学习率
self.gamma = 0.99 # 折扣因子
self.clip_epsilon = 0.2 # PPO裁剪参数
self.update_epochs = 10 # 每次更新迭代次数
# 网络
self.policy_net = PolicyNetwork(state_dim, action_dim)
self.value_net = ValueNetwork(state_dim)
# 优化器
self.policy_optimizer = optim.Adam(self.policy_net.parameters(), lr=self.lr)
self.value_optimizer = optim.Adam(self.value_net.parameters(), lr=self.lr)
def select_action(self, state):
"""根据策略选择动作"""
state = torch.FloatTensor(state).unsqueeze(0)
probs = self.policy_net(state) # 获取动作概率
dist = Categorical(probs) # 创建分类分布
action = dist.sample() # 采样动作
return action.item(), dist.log_prob(action) # 返回动作和log概率
def compute_advantages(self, rewards, values, next_value, dones):
"""计算优势函数"""
advantages = []
gae = 0 # 广义优势估计
for t in reversed(range(len(rewards))):
if t == len(rewards) - 1:
next_value = next_value
else:
next_value = values[t+1]
delta = rewards[t] + self.gamma * next_value * (1 - dones[t]) - values[t]
gae = delta + self.gamma * 0.95 * gae * (1 - dones[t]) # 0.95是GAE参数
advantages.insert(0, gae)
return advantages
def update(self, states, actions, old_log_probs, returns, advantages):
"""更新网络参数"""
states = torch.FloatTensor(states)
actions = torch.LongTensor(actions)
old_log_probs = torch.FloatTensor(old_log_probs)
returns = torch.FloatTensor(returns).unsqueeze(1)
advantages = torch.FloatTensor(advantages).unsqueeze(1)
# 多次小批量更新
for _ in range(self.update_epochs):
# 1. 更新价值网络(Critic)
values = self.value_net(states)
value_loss = (values - returns).pow(2).mean()
self.value_optimizer.zero_grad()
value_loss.backward()
self.value_optimizer.step()
# 2. 更新策略网络(Actor)
probs = self.policy_net(states)
dist = Categorical(probs)
new_log_probs = dist.log_prob(actions)
# 计算概率比
ratio = (new_log_probs - old_log_probs).exp().unsqueeze(1)
# PPO裁剪目标函数
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - self.clip_epsilon, 1 + self.clip_epsilon) * advantages
policy_loss = -torch.min(surr1, surr2).mean()
self.policy_optimizer.zero_grad()
policy_loss.backward()
self.policy_optimizer.step()
# 3. 训练函数
def train_ppo():
# 创建环境
env = gym.make('CartPole-v1')
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
# 创建PPO智能体
agent = PPO(state_dim, action_dim)
# 训练参数
num_episodes = 500 # 训练轮数
max_steps = 1000 # 每轮最大步数
batch_size = 64 # 批量大小
rewards_history = []
print("开始训练PPO玩平衡木游戏...")
print("="*50)
for episode in range(num_episodes):
# 收集数据
states, actions, rewards, log_probs, dones = [], [], [], [], []
state = env.reset()
episode_reward = 0
for step in range(max_steps):
# 选择动作
action, log_prob = agent.select_action(state)
next_state, reward, done, _ = env.step(action)
# 存储数据
states.append(state)
actions.append(action)
rewards.append(reward)
log_probs.append(log_prob.item())
dones.append(done)
state = next_state
episode_reward += reward
if done:
break
# 计算价值
with torch.no_grad():
if dones[-1]:
next_value = 0
else:
next_state_tensor = torch.FloatTensor(next_state).unsqueeze(0)
next_value = agent.value_net(next_state_tensor).item()
# 计算回报和优势
values = agent.value_net(torch.FloatTensor(states)).squeeze().detach().numpy()
advantages = agent.compute_advantages(rewards, values, next_value, dones)
# 计算回报
returns = []
R = next_value
for r in reversed(rewards):
R = r + agent.gamma * R
returns.insert(0, R)
# 更新网络
agent.update(states, actions, log_probs, returns, advantages)
# 记录奖励
rewards_history.append(episode_reward)
# 每50轮打印一次进度
if (episode + 1) % 50 == 0:
avg_reward = np.mean(rewards_history[-50:])
print(f"轮次 {episode+1}/{num_episodes}, 最近50轮平均奖励: {avg_reward:.1f}")
# 如果平均奖励达到阈值,提前结束
if avg_reward >= 495:
print(f"🎉 提前完成任务!在第{episode+1}轮达到目标")
break
# 绘制学习曲线
plt.figure(figsize=(10, 5))
plt.plot(rewards_history)
plt.xlabel('训练轮次')
plt.ylabel('每轮总奖励')
plt.title('PPO在平衡木游戏中的学习曲线')
plt.grid(True)
plt.savefig('ppo_learning_curve.png')
plt.show()
# 演示训练好的智能体
print("\n演示训练好的智能体...")
state = env.reset()
total_reward = 0
for step in range(max_steps):
env.render() # 显示游戏画面
action, _ = agent.select_action(state)
state, reward, done, _ = env.step(action)
total_reward += reward
if done:
print(f"演示结束,坚持了{step+1}步,总奖励:{total_reward}")
break
env.close()
return agent
# 4. 主程序
if __name__ == "__main__":
# 设置随机种子,保证可重复性
torch.manual_seed(42)
np.random.seed(42)
# 开始训练
trained_agent = train_ppo()
print("\n" + "="*50)
print("训练完成!")
print("生成的 'ppo_learning_curve.png' 显示了学习过程")
print("一开始AI随机行动,杆子很快倒下")
print("随着训练,AI学会了微妙调整,保持杆子平衡更久")
代码说明:
- 环境:CartPole-v1,一个经典的强化学习测试环境
- 目标:控制小车左右移动,保持杆子不倒
- 网络结构:
- 策略网络(Actor):输入状态(4个数字),输出动作概率(2个动作:左/右)
- 价值网络(Critic):评估状态的价值
- PPO核心:
- 收集一批数据
- 计算优势函数(动作比平均好多少)
- 用裁剪的目标函数更新策略
- 用均方误差更新价值网络
- 运行结果:
- 开始:AI随机行动,杆子很快倒下(奖励<50)
- 训练后:AI学会微妙调整,保持平衡很久(奖励接近500)
如何运行:
- 安装必要库:
pip install gym torch matplotlib numpy - 复制上面的代码到
ppo_cartpole.py - 运行:
python ppo_cartpole.py - 观察学习曲线和最终演示
第七部分:思维导图与总结
7.1 强化学习三大算法对比思维导图
mindmap
root(强化学习三大经典算法)
策略梯度Policy Gradient
核心思想
直接学习策略
输入状态→输出动作概率
关键特性
处理连续动作
自然探索性
优点
连续动作适用
策略灵活
缺点
高方差
样本效率低
适用场景
机器人控制
连续控制任务
DQN
核心思想
学习价值函数
状态+动作→预期回报
关键创新
经验回放
目标网络
优点
离散动作强
样本效率较高
从像素学习
缺点
无法处理连续动作
可能高估价值
适用场景
视频游戏AI
离散决策问题
PPOProximal Policy Optimization
核心思想
约束策略更新
小步安全改进
关键技巧
比例裁剪
广义优势估计
优点
训练稳定
通用性强
调参友好
缺点
计算复杂
探索保守
适用场景
复杂连续控制
工业应用
多智能体系统
7.2 如何选择算法:决策流程图
7.3 一句话总结
- 策略梯度:像直觉派画家,凭感觉直接学习“怎么画”,适合连续动作但可能不稳定
- DQN:像精算师,先给每个选择打分再行动,适合离散决策且能高效学习
- PPO:像稳健的工程师,小步快跑确保安全,是目前最实用稳定的选择
7.4 给初学者的学习建议
- 先理解概念,再深究数学:理解“为什么要这样做”比“具体怎么做”更重要
- 从实践开始:运行上面的代码,观察AI如何从零学习
- 循序渐进:CartPole → Atari游戏 → 自定义环境
- 关注趋势:PPO是目前工业界最常用的方法,值得深入
- 理解局限:没有万能算法,只有适合场景的算法
结语:决策智能的未来
强化学习正从游戏走向现实,从虚拟走向物理世界。DQN开启了深度强化学习的新纪元,策略梯度提供了处理连续控制的方法,而PPO让这一切变得更稳定实用。
记住,这些算法本质上是让AI通过试错学习做决策。就像人类学习骑自行车:摔倒(负奖励)告诉我们什么动作不对,保持平衡(正奖励)告诉我们什么动作正确。通过无数次尝试,我们(和AI)都学会了这项技能。
随着技术进步,强化学习正在自动驾驶、机器人、医疗、金融等领域发挥越来越重要的作用。今天你学习的这三个算法,是打开决策智能大门的钥匙。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)