欢迎光临
我们一直在努力

一文读懂强化学习:从Q-learning到PPO

一文读懂强化学习:从Q-learning到PPO

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

在这里插入图片描述

在这里插入图片描述

  • 一文读懂强化学习:从Q-learning到PPO
    • 一、强化学习核心概念速览
    • 二、经典入门:Q-learning算法
      • 1. Q-learning核心原理
      • 2. Q-learning实战:迷宫游戏AI
        • 环境说明
        • 完整代码
        • 代码说明
        • 运行结果
      • 3. Q-learning的局限性
    • 三、工业界主流:PPO算法
      • 1. PPO核心原理
      • 2. PPO实战:CartPole平衡
        • 前置条件
        • 完整代码
        • 代码说明
        • 运行结果
    • 四、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+γamaxQ(s,a)Q(s,a)]

  • α

    \\alpha

    α:学习率(0<α≤1),控制每次更新的幅度;

  • γ

    \\gamma

    γ:折扣因子(0≤γ≤1),衡量未来奖励的重要性;

  • r

    r

    r:当前动作获得的即时奖励;

  • max

    a

    Q

    (

    s

    ,

    a

    )

    \\max_{a'} Q(s',a')

    maxaQ(s,a):下一个状态

    s

    s'

    s中所有动作的最大Q值。

Q-learning的核心逻辑:用“当前Q值”和“即时奖励+未来最优Q值”的差值,逐步修正Q值,最终逼近最优Q表。

2. Q-learning实战:迷宫游戏AI

我们用经典的迷宫环境实现Q-learning,让智能体学会从起点走到终点。

环境说明

迷宫大小为4×4,状态0为起点,状态15为终点,灰色格子(如状态1、6)为障碍物,智能体可执行上/下/左/右4个动作,走到障碍物奖励-1,走到终点奖励10,其他位置奖励-0.1(鼓励尽快到达终点)。

完整代码

import numpy as np
import random

# 1. 构建迷宫环境
class MazeEnv:
def __init__(self):
self.size = 4 # 4×4迷宫
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表存储,仅适用于离散、小规模状态空间(如4×4迷宫),无法处理连续状态(如自动驾驶的传感器数据);
  • ε-贪心策略的探索效率低,容易陷入局部最优;
  • 对超参数(α、γ、ε)敏感,调参成本高。

三、工业界主流: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,让智能体学会平衡小车和杆子(经典强化学习入门任务)。

前置条件

先安装依赖:

pip install gymnasium torch numpy

完整代码

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-learningPPO
算法类型 值函数方法 策略梯度方法(Actor-Critic)
状态空间 适合离散、小规模 适合连续/离散、大规模
训练稳定性 易震荡,超参数敏感 稳定,Clip机制限制更新幅度
落地难度 简单,仅需Q表 稍复杂,但有成熟框架支持
工业应用 小规模场景(如简单游戏) 主流(如机器人、游戏AI、推荐系统)

五、强化学习学习建议

  • 先掌握基础:理解马尔可夫决策过程(MDP)、贝尔曼方程、策略/价值函数等核心概念;
  • 从代码入手:先实现Q-learning,理解“值迭代”的逻辑,再过渡到PPO的“策略优化”;
  • 借助成熟框架:工业界常用Stable Baselines3、RLlib等库,可直接调用PPO等算法,聚焦业务场景;
  • 关注实际问题:强化学习的核心痛点是样本效率低、泛化能力弱,可重点学习数据增强、预训练等优化方法。
  • 总结

  • Q-learning是强化学习入门的经典值函数算法,核心是通过贝尔曼方程迭代更新Q表,适合离散、小规模场景;
  • PPO是工业界主流的策略梯度算法,通过Clip机制限制策略更新幅度,训练稳定、适配复杂场景,是强化学习落地的首选;
  • 从Q-learning到PPO的学习,本质是从“值迭代”到“策略优化”的思维转变,代码实操是理解算法的关键。
  • 强化学习的学习没有捷径,唯有结合原理和实战,才能真正掌握其核心逻辑。希望本文的代码和解析能帮你打通从入门到实战的链路,后续可尝试将PPO应用到更复杂的场景(如Atari游戏、机器人控制),进一步深化理解。


    ✨ 坚持用 清晰的图解 +易懂的硬件架构 + 硬件解析, 让每个知识点都 简单明了 ! 🚀 个人主页 :一只大侠的侠 · CSDN

    💬 座右铭 : “所谓成功就是以自己的方式度过一生。” 在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 一文读懂强化学习:从Q-learning到PPO
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址