ML 学习站
跳到正文

Q-Learning 算法

表格型 Q-Learning 与 ε-贪心探索。

35 分钟3 / 51,608
加载中...

Q-Learning 是一种经典的强化学习算法,于 1989 年由 Watkins 提出。它通过迭代逼近最优 Q 函数,不依赖环境模型,理论保证收敛。核心概念包括 TD(时间差分)目标和贝尔曼方程。TD 目标利用当前奖励和未来估计的差值来更新 Q 值。读者学习后,能够在状态空间较小、动作离散且环境可采样的任务中应用 Q-Learning,例如在 OpenAI Gym 的 FrozenLake 环境中达到 80% 以上的胜率。Q-Learning 的关键参数包括学习率(lr)、折扣因子(gamma)和探索策略(ε-贪心)。ε-贪心策略通过平衡探索和利用来优化学习过程。此外,Q-Learning 适用于状态空间较小且动作离散的任务,对于状态空间较大或连续的任务,建议使用基于神经网络的 DQN 算法。

Q-Learning 算法

Q-Learning 是最经典的强化学习算法,1989 年被提出。它不依赖环境模型,直接学最优 Q 函数,理论保证收敛。

核心思想

回忆最优贝尔曼方程:

Q*(s, a) = R(s, a) + γ * sum_{s'} P(s'|s, a) * max_{a'} Q*(s', a')

Q-Learning 的想法:既然 Q* 满足这个方程,我就用迭代逼近它。每走一步,更新 Q:

Q(s, a) ← Q(s, a) + α * [ R(s, a) + γ * max_{a'} Q(s', a') - Q(s, a) ]
              ↑              ↑                                       ↑
          当前估计        TD 目标(立即奖励 + 折扣的未来)         当前估计

α 是学习率,γ 是折扣因子。这个更新在 1989 年被 Watkins 证明,只要所有 (s, a) 对被访问无穷次, Q 一定收敛到 Q*。

关键概念:TD 目标

TD_target = R + γ * max_{a'} Q(s', a')
TD_error  = TD_target - Q(s, a)   (这是要"惊讶"多少)

直觉:看到奖励 R,加上对未来的最好估计,如果比现在的估计大,说明 Q(s, a) 偏低,往上更新。

TD = Temporal Difference = 时间差分。因为是用"现在的估计"和"下一步的估计"之差来更新,所以叫时间差分学习。

完整 Q-Learning 算法

import numpy as np
from collections import defaultdict

class QLearning:
    def __init__(self, n_states, n_actions, lr=0.1, gamma=0.99, epsilon=0.1):
        self.Q = np.zeros((n_states, n_actions))  # Q 表
        self.lr = lr
        self.gamma = gamma
        self.epsilon = epsilon

    def select_action(self, state):
        """ε-贪心: 小概率随机, 大概率选当前最优"""
        if np.random.random() < self.epsilon:
            return np.random.randint(self.Q.shape[1])
        return np.argmax(self.Q[state])

    def update(self, state, action, reward, next_state, done):
        """核心: Q-Learning 更新"""
        if done:
            target = reward
        else:
            target = reward + self.gamma * np.max(self.Q[next_state])
        # 增量更新
        self.Q[state, action] += self.lr * (target - self.Q[state, action])

实战:FrozenLake 环境

OpenAI Gym 的 FrozenLake(冰面滑行,经典入门):

SFFF        S = 起点, 安全
FHFH        F = 冰面, 可走
FFFH        H = 洞, 掉进去就输
HFFG        G = 目标, 奖励 +1
import gym

env = gym.make("FrozenLake-v1", is_slippery=False)  # 不滑的话简单
agent = QLearning(env.observation_space.n, env.action_space.n,
                  lr=0.1, gamma=0.99, epsilon=0.1)

# 训练
episodes = 5000
for ep in range(episodes):
    state, _ = env.reset()
    done = False
    while not done:
        action = agent.select_action(state)
        next_state, reward, terminated, truncated, _ = env.step(action)
        done = terminated or truncated
        agent.update(state, action, reward, next_state, done)
        state = next_state

# 测试
wins = 0
for _ in range(100):
    state, _ = env.reset()
    done = False
    while not done:
        action = np.argmax(agent.Q[state])
        state, reward, terminated, truncated, _ = env.step(action)
        done = terminated or truncated
    if reward == 1:
        wins += 1
print(f"胜率: {wins}%")

跑 5000 轮应该能达到 80%+ 胜率

探索 vs 利用:ε-贪心家族

ε-贪心是入门级探索策略,实践中通常用更聪明的:

1. ε-贪心(本节)

P(随机) = ε
P(最优) = 1 - ε

简单但有效。问题:探索均匀,没区分"哪些动作已知很烂"和"哪些未知"。

2. ε 衰减(常用)

# 训练初期多探索, 后期多用最优
epsilon = max(0.01, 0.5 * 0.99 ** episode)

3. UCB(Upper Confidence Bound)

同时考虑"估计值"和"不确定性":

a = argmax [ Q(s, a) + c * sqrt(ln(t) / N(s, a)) ]
                       ↑
                  访问越少 → bonus 越大

4. 玻尔兹曼探索

按 Q 值的 softmax 概率采样:

P(a|s) ∝ exp(Q(s, a) / T)

T 是温度,T 大 → 更均匀(多探索),T 小 → 更贪心(多利用)。

Q-Learning 的局限

Q-Learning 假设 Q 表存在,但实际问题里状态空间可能无限(游戏画面、机器人传感器)。Q 表爆炸。

解决:用神经网络逼近 Q 函数——这就是下一章的 DQN。

Q-Learning 调参经验

参数推荐范围影响
lr (学习率)0.05 ~ 0.5太小学得慢,太大不稳定
gamma (折扣)0.9 ~ 0.99越大越"有远见",越小越"短视"
epsilon0.05 ~ 0.2(衰减)太大学不到最优,太小陷入局部
episodes看任务简单的 1k, 复杂的 1M+

实战:出租车问题

比 FrozenLake 复杂一点:

env = gym.make("Taxi-v3")
agent = QLearning(env.observation_space.n, env.action_space.n,
                  lr=0.1, gamma=0.99, epsilon=0.1)

# 训练
for ep in range(10000):
    state, _ = env.reset()
    done = False
    while not done:
        action = agent.select_action(state)
        ns, r, term, trunc, _ = env.step(action)
        done = term or trunc
        agent.update(state, action, r, ns, done)
        state = ns

# Q 表可视化
import numpy as np
print(f"训练后 Q 表: {agent.Q.shape}")
print(f"非零 Q 值: {(agent.Q != 0).sum()}")

总结:从 Q-Learning 到 DQN

Q-Learning 适合:

  • 状态空间小(< 几万个)
  • 离散动作
  • 环境模型已知或能采样

不适合:

  • 状态空间大/连续
  • 状态是图像(几百万像素)
  • 实时决策

下一章:用神经网络代替 Q 表 → DQN。

小结

  • Q-Learning 直接学最优 Q 函数,不依赖环境模型
  • 更新公式: Q(s,a) ← Q(s,a) + α * [R + γ*max Q(s', a') - Q(s,a)]
  • ε-贪心及变种解决探索问题
  • 表格型 Q-Learning 只适合小状态空间
  • DQN 用神经网络逼近 Q(下章)

练习思考

  1. 在 FrozenLake 把 is_slippery 改成 True(冰面真滑),Q-Learning 还能学出来吗?为什么?
  2. ε=0(完全贪心)会发生什么?为什么?
  3. 尝试用玻尔兹曼探索替代 ε-贪心,对比在 FrozenLake 上的训练效果。

章末小测验

检验你对《Q-Learning 算法》的掌握程度。

1

Q-Learning 算法的主要特点是什么?

2

关于 TD 目标,以下哪些说法是正确的?

3

ε-贪心策略的缺点是什么?

4

Q-Learning 在哪些情况下表现良好?

5

以下哪些方法可以改进 Q-Learning 的探索策略?

讨论区(0)

加载评论中...