一文读懂强化学习:从Q-learning到PPO
强化学习作为机器学习的核心分支之一,凭借"试错学习"的特性,成为实现智能体自主决策的关键技术。从经典的Q-learning到工业界主流的PPO(Proximal Policy Optimization),算法的演进始终围绕"更稳定、更高效、更易落地"展开。本文将从基础原理入手,结合实战代码,带你打通从Q-learning到PPO的学习链路。


- 一文读懂强化学习:从Q-learning到PPO
-
- 一、强化学习核心概念速览
- 二、经典入门:Q-learning算法
- 三、工业界主流:PPO算法
- [四、Q-learning vs PPO:核心差异](#四、Q-learning vs PPO:核心差异)
- 五、强化学习学习建议
一、强化学习核心概念速览
在深入算法前,先理清强化学习的核心要素,这是理解所有算法的基础:
- 智能体(Agent):做出决策的主体(如游戏AI、机器人);
- 环境(Environment):智能体交互的外部场景(如迷宫、Atari游戏);
- 状态(State):环境的当前特征(如CartPole中杆子的角度、小车位置);
- 动作(Action):智能体的决策(如向左/向右推动小车);
- 奖励(Reward):环境对动作的反馈(如小车保持平衡则奖励+1,倒下则奖励0);
- 策略(Policy):智能体选择动作的规则(核心优化目标);
- 价值函数(Value Function):评估某个状态/动作的长期收益。
简单来说,强化学习的目标就是让智能体通过不断与环境交互,学习最优策略,最大化累计奖励。
二、经典入门:Q-learning算法
Q-learning是基于值函数的经典强化学习算法,核心是学习"状态-动作对"的价值(即Q值),表示在某个状态下执行某个动作能获得的长期奖励。
1. Q-learning核心原理
Q-learning的核心是贝尔曼方程 ,用于迭代更新Q值:
Q ( s , a ) ← Q ( s , a ) + α [ r + γ max a ′ Q ( s ′ , a ′ ) − Q ( s , a ) ] Q(s,a) \leftarrow Q(s,a) + \alpha \left[ r + \gamma \max_{a'} Q(s',a') - Q(s,a) \right] Q(s,a)←Q(s,a)+α[r+γa′maxQ(s′,a′)−Q(s,a)]
- α \alpha α:学习率(0<α≤1),控制每次更新的幅度;
- γ \gamma γ:折扣因子(0≤γ≤1),衡量未来奖励的重要性;
- r r r:当前动作获得的即时奖励;
- max a ′ Q ( s ′ , a ′ ) \max_{a'} Q(s',a') maxa′Q(s′,a′):下一个状态 s ′ s' s′中所有动作的最大Q值。
Q-learning的核心逻辑:用"当前Q值"和"即时奖励+未来最优Q值"的差值,逐步修正Q值,最终逼近最优Q表。
2. Q-learning实战:迷宫游戏AI
我们用经典的迷宫环境实现Q-learning,让智能体学会从起点走到终点。
环境说明
迷宫大小为4x4,状态0为起点,状态15为终点,灰色格子(如状态1、6)为障碍物,智能体可执行上/下/左/右4个动作,走到障碍物奖励-1,走到终点奖励10,其他位置奖励-0.1(鼓励尽快到达终点)。
完整代码
python
import numpy as np
import random
# 1. 构建迷宫环境
class MazeEnv:
def __init__(self):
self.size = 4 # 4x4迷宫
self.state = 0 # 初始状态(起点)
self.end_state = 15 # 终点
# 障碍物位置
self.obstacles = [1, 6, 7, 10]
def reset(self):
"""重置环境,返回初始状态"""
self.state = 0
return self.state
def step(self, action):
"""执行动作,返回(next_state, reward, done)"""
# 动作映射:0-上,1-下,2-左,3-右
row = self.state // self.size
col = self.state % self.size
if action == 0: # 上
new_row = row - 1
new_col = col
elif action == 1: # 下
new_row = row + 1
new_col = col
elif action == 2: # 左
new_row = row
new_col = col - 1
elif action == 3: # 右
new_row = row
new_col = col + 1
else:
raise ValueError("动作只能是0-3")
# 边界检查
if new_row < 0 or new_row >= self.size or new_col < 0 or new_col >= self.size:
return self.state, -1, False # 撞墙,状态不变,奖励-1
new_state = new_row * self.size + new_col
# 障碍物检查
if new_state in self.obstacles:
return self.state, -1, False # 碰到障碍物,状态不变,奖励-1
# 终点检查
if new_state == self.end_state:
return new_state, 10, True # 到达终点,奖励10,结束
# 普通移动
self.state = new_state
return new_state, -0.1, False
# 2. 实现Q-learning智能体
class QLearningAgent:
def __init__(self, n_states, n_actions, alpha=0.1, gamma=0.9, epsilon=0.1):
self.n_actions = n_actions
# 初始化Q表:n_states x n_actions,初始值为0
self.q_table = np.zeros((n_states, n_actions))
self.alpha = alpha # 学习率
self.gamma = gamma # 折扣因子
self.epsilon = epsilon # 探索率(ε-贪心策略)
def choose_action(self, state):
"""ε-贪心策略选择动作:ε概率探索,1-ε概率利用"""
if random.uniform(0, 1) < self.epsilon:
# 探索:随机选动作
action = random.randint(0, self.n_actions - 1)
else:
# 利用:选当前状态下Q值最大的动作
action = np.argmax(self.q_table[state, :])
return action
def learn(self, state, action, reward, next_state):
"""更新Q表"""
# 贝尔曼方程
q_predict = self.q_table[state, action]
q_target = reward + self.gamma * np.max(self.q_table[next_state, :])
self.q_table[state, action] += self.alpha * (q_target - q_predict)
# 3. 训练智能体
if __name__ == "__main__":
env = MazeEnv()
agent = QLearningAgent(n_states=16, n_actions=4)
episodes = 1000 # 训练轮数
for episode in range(episodes):
state = env.reset()
done = False
total_reward = 0
while not done:
action = agent.choose_action(state)
next_state, reward, done = env.step(action)
agent.learn(state, action, reward, next_state)
state = next_state
total_reward += reward
# 每100轮打印一次训练进度
if (episode + 1) % 100 == 0:
print(f"Episode {episode+1}, Total Reward: {total_reward:.1f}")
# 打印训练后的Q表(重点看起点和终点附近的Q值)
print("\n训练后的Q表(部分):")
print("起点(状态0)的Q值:", agent.q_table[0])
print("终点前一步(状态14)的Q值:", agent.q_table[14])
代码说明
- 环境类(MazeEnv):模拟迷宫规则,处理动作执行、边界/障碍物检查,返回奖励和新状态;
- 智能体类(QLearningAgent) :核心是
choose_action(ε-贪心策略平衡探索与利用)和learn(贝尔曼方程更新Q表); - 训练过程:循环1000轮,每轮从起点重置,直到到达终点,最终Q表会收敛到最优值。
运行结果
训练完成后,起点(状态0)的Q值会明显偏向"向右"或"向下"的最优动作,终点前一步(状态14)的Q值中"向右"动作的Q值会远高于其他动作,说明智能体已学会最优路径。
3. Q-learning的局限性
Q-learning虽然简单易实现,但存在明显短板:
- 依赖Q表存储,仅适用于离散、小规模状态空间(如4x4迷宫),无法处理连续状态(如自动驾驶的传感器数据);
- ε-贪心策略的探索效率低,容易陷入局部最优;
- 对超参数(α、γ、ε)敏感,调参成本高。
三、工业界主流:PPO算法
为解决值函数方法的局限性,策略梯度方法应运而生------直接优化策略函数,而非价值函数。PPO是策略梯度的改进版,凭借"稳定、高效、易实现"成为当前强化学习的主流算法。
1. PPO核心原理
PPO的核心是近端策略优化 ,通过限制策略更新的幅度(Clip机制),避免策略突变导致训练崩溃:
L C L I P ( θ ) = E ^ t [ min ( r t ( θ ) A ^ t , clip ( r t ( θ ) , 1 − ϵ , 1 + ϵ ) A ^ t ) ] L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min(r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t) \right] LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)]
- r t ( θ ) r_t(\theta) rt(θ):新旧策略的概率比;
- A ^ t \hat{A}_t A^t:优势函数(衡量动作的好坏);
- clip \text{clip} clip:将策略更新幅度限制在 [ 1 − ϵ , 1 + ϵ ] [1-\epsilon, 1+\epsilon] [1−ϵ,1+ϵ](通常ε=0.2)。
简单来说,PPO通过"裁剪"策略更新的幅度,保证每一步更新都在"安全范围"内,既避免训练崩溃,又能稳定提升策略。
2. PPO实战:CartPole平衡
我们用OpenAI Gym的CartPole环境实现PPO,让智能体学会平衡小车和杆子(经典强化学习入门任务)。
前置条件
先安装依赖:
bash
pip install gymnasium torch numpy
完整代码
python
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import gymnasium as gym
# 1. 定义策略网络(Actor-Critic架构)
class ActorCritic(nn.Module):
def __init__(self, state_dim, action_dim, hidden_dim=64):
super(ActorCritic, self).__init__()
# 策略网络(Actor):输出动作概率
self.actor = nn.Sequential(
nn.Linear(state_dim, hidden_dim),
nn.Tanh(),
nn.Linear(hidden_dim, hidden_dim),
nn.Tanh(),
nn.Linear(hidden_dim, action_dim),
nn.Softmax(dim=-1)
)
# 价值网络(Critic):输出状态价值
self.critic = nn.Sequential(
nn.Linear(state_dim, hidden_dim),
nn.Tanh(),
nn.Linear(hidden_dim, hidden_dim),
nn.Tanh(),
nn.Linear(hidden_dim, 1)
)
def get_action(self, state):
"""输入状态,输出动作和对数概率"""
state = torch.tensor(state, dtype=torch.float32)
probs = self.actor(state)
dist = torch.distributions.Categorical(probs)
action = dist.sample()
return action.item(), dist.log_prob(action)
def evaluate(self, state, action):
"""评估动作的对数概率和状态价值"""
state = torch.tensor(state, dtype=torch.float32)
action = torch.tensor(action, dtype=torch.int64)
probs = self.actor(state)
dist = torch.distributions.Categorical(probs)
log_prob = dist.log_prob(action)
value = self.critic(state)
return log_prob, value
# 2. 实现PPO智能体
class PPOAgent:
def __init__(self, state_dim, action_dim, lr=3e-4, gamma=0.99, clip_epsilon=0.2, k_epochs=4):
self.model = ActorCritic(state_dim, action_dim)
self.optimizer = optim.Adam(self.model.parameters(), lr=lr)
self.gamma = gamma
self.clip_epsilon = clip_epsilon
self.k_epochs = k_epochs # 每批数据训练轮数
# 存储轨迹数据
self.states = []
self.actions = []
self.log_probs = []
self.rewards = []
self.dones = []
def store_data(self, state, action, log_prob, reward, done):
"""存储单步轨迹数据"""
self.states.append(state)
self.actions.append(action)
self.log_probs.append(log_prob)
self.rewards.append(reward)
self.dones.append(done)
def compute_advantages(self):
"""计算优势函数和累计回报"""
advantages = []
advantage = 0
returns = []
return_ = 0
# 从后往前计算
for i in reversed(range(len(self.rewards))):
# 累计回报:r_t + γ*r_{t+1} + ... + γ^n*r_{t+n}
return_ = self.rewards[i] + self.gamma * return_ * (1 - self.dones[i])
returns.insert(0, return_)
# 优势函数:A_t = Q_t - V_t = (r_t + γ*V_{t+1}) - V_t
value = self.model.critic(torch.tensor(self.states[i], dtype=torch.float32)).item()
next_value = self.model.critic(torch.tensor(self.states[i+1], dtype=torch.float32)).item() if i < len(self.states)-1 else 0
td_error = self.rewards[i] + self.gamma * next_value * (1 - self.dones[i]) - value
advantage = td_error + self.gamma * 0.95 * advantage * (1 - self.dones[i]) # GAE
advantages.insert(0, advantage)
# 标准化优势函数
advantages = torch.tensor(advantages, dtype=torch.float32)
advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)
returns = torch.tensor(returns, dtype=torch.float32)
return advantages, returns
def update(self):
"""PPO策略更新"""
# 准备数据
old_log_probs = torch.tensor(self.log_probs, dtype=torch.float32)
states = torch.tensor(self.states, dtype=torch.float32)
actions = torch.tensor(self.actions, dtype=torch.int64)
advantages, returns = self.compute_advantages()
# 多轮训练
for _ in range(self.k_epochs):
# 计算当前策略的对数概率和价值
current_log_probs, values = self.model.evaluate(states, actions)
# 策略比率:r_t(θ) = π_θ(a_t|s_t) / π_θ_old(a_t|s_t)
ratio = torch.exp(current_log_probs - old_log_probs.detach())
# Clip损失
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - self.clip_epsilon, 1 + self.clip_epsilon) * advantages
actor_loss = -torch.min(surr1, surr2).mean()
# 价值损失(MSE)
critic_loss = nn.MSELoss()(values.squeeze(), returns)
# 总损失
total_loss = actor_loss + 0.5 * critic_loss
# 反向传播
self.optimizer.zero_grad()
total_loss.backward()
self.optimizer.step()
# 清空轨迹数据
self.states = []
self.actions = []
self.log_probs = []
self.rewards = []
self.dones = []
# 3. 训练PPO智能体
if __name__ == "__main__":
# 初始化环境(CartPole-v1)
env = gym.make("CartPole-v1")
state_dim = env.observation_space.shape[0] # 状态维度:4
action_dim = env.action_space.n # 动作维度:2(左/右)
# 初始化智能体
agent = PPOAgent(state_dim, action_dim)
episodes = 200 # 训练轮数
max_steps = 500 # 每轮最大步数
for episode in range(episodes):
state, _ = env.reset()
total_reward = 0
for step in range(max_steps):
# 选择动作
action, log_prob = agent.model.get_action(state)
# 执行动作
next_state, reward, done, truncated, _ = env.step(action)
total_reward += reward
# 存储数据
agent.store_data(state, action, log_prob, reward, done or truncated)
state = next_state
# 每200步更新一次策略(或结束时)
if (step + 1) % 200 == 0 or done or truncated:
agent.update()
break
# 打印训练进度
print(f"Episode {episode+1}, Total Reward: {total_reward}")
# 当奖励稳定在500时,训练完成
if total_reward >= 500:
print("训练完成!智能体已稳定平衡CartPole")
break
代码说明
- Actor-Critic网络:Actor输出动作概率(策略),Critic输出状态价值(评估);
- PPOAgent类 :核心是
compute_advantages(计算优势函数,衡量动作好坏)和update(Clip机制限制策略更新幅度); - 训练过程:每轮与环境交互,存储轨迹数据,累计一定步数后更新策略,直到奖励稳定在500(CartPole的最大奖励)。
运行结果
训练100-200轮后,智能体可稳定将CartPole平衡500步,说明策略已收敛到最优。
四、Q-learning vs PPO:核心差异
| 维度 | Q-learning | PPO |
|---|---|---|
| 算法类型 | 值函数方法 | 策略梯度方法(Actor-Critic) |
| 状态空间 | 适合离散、小规模 | 适合连续/离散、大规模 |
| 训练稳定性 | 易震荡,超参数敏感 | 稳定,Clip机制限制更新幅度 |
| 落地难度 | 简单,仅需Q表 | 稍复杂,但有成熟框架支持 |
| 工业应用 | 小规模场景(如简单游戏) | 主流(如机器人、游戏AI、推荐系统) |
五、强化学习学习建议
- 先掌握基础:理解马尔可夫决策过程(MDP)、贝尔曼方程、策略/价值函数等核心概念;
- 从代码入手:先实现Q-learning,理解"值迭代"的逻辑,再过渡到PPO的"策略优化";
- 借助成熟框架:工业界常用Stable Baselines3、RLlib等库,可直接调用PPO等算法,聚焦业务场景;
- 关注实际问题:强化学习的核心痛点是样本效率低、泛化能力弱,可重点学习数据增强、预训练等优化方法。
总结
- Q-learning是强化学习入门的经典值函数算法,核心是通过贝尔曼方程迭代更新Q表,适合离散、小规模场景;
- PPO是工业界主流的策略梯度算法,通过Clip机制限制策略更新幅度,训练稳定、适配复杂场景,是强化学习落地的首选;
- 从Q-learning到PPO的学习,本质是从"值迭代"到"策略优化"的思维转变,代码实操是理解算法的关键。
强化学习的学习没有捷径,唯有结合原理和实战,才能真正掌握其核心逻辑。希望本文的代码和解析能帮你打通从入门到实战的链路,后续可尝试将PPO应用到更复杂的场景(如Atari游戏、机器人控制),进一步深化理解。
✨ 坚持用 清晰的图解 +易懂的硬件架构 + 硬件解析, 让每个知识点都 简单明了 !
🚀 个人主页 :一只大侠的侠 · CSDN
💬 座右铭 : "所谓成功就是以自己的方式度过一生。"
