96·进阶主题高级

强化学习入门:基本概念与 Q-Learning

reinforcement-learningtheory

第 96 课 - 强化学习入门:基本概念与 Q-Learning

1. 学习目标

  • 理解强化学习(RL)的核心要素:代理、环境、状态、动作、奖励。
  • 理解价值函数和策略的基本概念。
  • 掌握 Q-Learning 算法的基本原理和更新规则。
  • 能够实现一个简单的 Q-Learning 代理解决网格世界问题。
  • 了解探索与利用(Exploration vs. Exploitation)的权衡。

2. 核心概念

想象你在玩一个迷宫游戏,你就是 代理(Agent)。你身处的迷宫就是 环境(Environment)。你在迷宫中的每个位置称为一个 状态(State)。你可以选择上下左右移动,这就是 动作(Action)。每当你到达宝藏,游戏给你加分,这个分数就是 奖励(Reward)

强化学习 的目标是让代理通过与环境的交互(试错),学习一个 策略(Policy),即在每个状态下选择哪个动作,能使得长期累积的奖励最大化。

Q-Learning 是一种无模型(Model-Free)的强化学习算法。它学习一个 动作价值函数 Q(s, a),即在状态 s 下采取动作 a 后,期望能获得的未来累积折扣奖励总和。简单说,Q(s, a) 值越高,代表在状态 s 下执行动作 a 越“好”。

Q-Learning 更新规则(核心公式):

Q(s, a) ← Q(s, a) + α * [r + γ * max Q(s', a') - Q(s, a)]
  • α:学习率(Learning Rate),控制新信息覆盖旧信息的速度。
  • γ:折扣因子(Discount Factor),表示未来奖励的重要程度(0-1之间)。
  • r:立即奖励。
  • s':执行动作后进入的新状态。
  • max Q(s', a'):在新状态 s' 下所有可能动作中,Q值最大的那个。这代表了我们对未来最乐观的估计。

探索与利用:代理需要平衡“利用”已知的高价值动作和“探索”未知的动作以发现更好的路径。常用方法是 ε-贪婪策略(ε-greedy),即以概率 ε 随机探索,以概率 1-ε 选择当前Q值最高的动作。

3. 代码示例

我们将实现一个简单的 Q-Learning 代理,目标是从网格世界的左上角到达右下角的宝藏位置。我们使用一个 4x4 的网格。

import numpy as np
import random
import time

# --- 环境定义 ---
class GridWorld:
    def __init__(self, rows=4, cols=4):
        self.rows = rows
        self.cols = cols
        self.state = (0, 0)  # 起始状态 (行, 列)
        self.goal = (rows-1, cols-1)  # 目标状态
        # 定义动作:0-上,1-右,2-下,3-左
        self.actions = [0, 1, 2, 3]
        self.n_actions = len(self.actions)

    def reset(self):
        self.state = (0, 0)
        return self.state

    def step(self, action):
        """执行动作,返回新状态,奖励,是否结束"""
        row, col = self.state
        if action == 0:  # 上
            row = max(0, row - 1)
        elif action == 1:  # 右
            col = min(self.cols - 1, col + 1)
        elif action == 2:  # 下
            row = min(self.rows - 1, row + 1)
        elif action == 3:  # 左
            col = max(0, col - 1)

        self.state = (row, col)
        # 到达目标奖励为1,否则为-0.1(惩罚每一步,鼓励快速到达)
        reward = 1.0 if self.state == self.goal else -0.1
        done = (self.state == self.goal)
        return self.state, reward, done

    def render(self):
        """在终端打印当前网格状态"""
        grid = np.zeros((self.rows, self.cols), dtype=str)
        grid[:] = '.'
        grid[self.goal[0]][self.goal[1]] = 'G'
        grid[self.state[0]][self.state[1]] = 'A'
        print('\n'.join([' '.join(row) for row in grid]))
        print('---')

# --- Q-Learning 代理 ---
class QLearningAgent:
    def __init__(self, n_states, n_actions, lr=0.1, gamma=0.99, epsilon=1.0, epsilon_decay=0.995, epsilon_min=0.01):
        self.n_actions = n_actions
        self.lr = lr
        self.gamma = gamma
        self.epsilon = epsilon
        self.epsilon_decay = epsilon_decay
        self.epsilon_min = epsilon_min
        # 初始化Q表为0,形状为 [状态数量, 动作数量]
        # 注意:这里为了简化,我们将状态(row, col)映射到一个整数索引
        self.q_table = np.zeros((n_states, n_actions))

    def get_state_index(self, state):
        """将 (row, col) 状态映射为整数索引"""
        return state[0] * 4 + state[1]  # 假设网格大小为4x4

    def choose_action(self, state_idx):
        """ε-贪婪策略选择动作"""
        if random.random() < self.epsilon:
            return random.randint(0, self.n_actions - 1)  # 随机探索
        else:
            return np.argmax(self.q_table[state_idx])  # 选择Q值最大的动作

    def learn(self, state_idx, action, reward, next_state_idx, done):
        """根据Q-Learning公式更新Q表"""
        current_q = self.q_table[state_idx, action]
        # 如果episode结束,没有未来奖励
        if done:
            target_q = reward
        else:
            target_q = reward + self.gamma * np.max(self.q_table[next_state_idx])
        # 更新Q值
        self.q_table[state_idx, action] += self.lr * (target_q - current_q)
        # 衰减探索率
        if self.epsilon > self.epsilon_min:
            self.epsilon *= self.epsilon_decay

# --- 训练循环 ---
def train():
    env = GridWorld()
    agent = QLearningAgent(n_states=16, n_actions=4)
    episodes = 1000
    rewards_per_episode = []

    for ep in range(episodes):
        state = env.reset()
        state_idx = agent.get_state_index(state)
        total_reward = 0
        done = False

        while not done:
            # 1. 代理选择动作
            action = agent.choose_action(state_idx)
            # 2. 环境执行动作,返回新状态和奖励
            next_state, reward, done = env.step(action)
            next_state_idx = agent.get_state_index(next_state)
            # 3. 代理学习
            agent.learn(state_idx, action, reward, next_state_idx, done)
            # 4. 状态转移
            state_idx = next_state_idx
            total_reward += reward

        rewards_per_episode.append(total_reward)
        # 每100个episode打印一次进度
        if (ep + 1) % 100 == 0:
            avg_reward = np.mean(rewards_per_episode[-100:])
            print(f"Episode {ep+1}, Average Reward: {avg_reward:.2f}, Epsilon: {agent.epsilon:.3f}")

    # 训练结束,演示学到的策略
    print("\n--- 训练完成,演示最优路径 ---")
    state = env.reset()
    state_idx = agent.get_state_index(state)
    done = False
    steps = 0
    while not done and steps < 20:
        action = np.argmax(agent.q_table[state_idx])  # 总是选择Q值最大的动作
        print(f"Step {steps}: State {state}, Action {['上','右','下','左'][action]}")
        env.render()
        next_state, _, done = env.step(action)
        state_idx = agent.get_state_index(next_state)
        state = next_state
        steps += 1
    print(f"到达目标!总共用了 {steps} 步。")

if __name__ == "__main__":
    train()

4. 实践练习

练习 1:基础运行与观察 直接运行上述代码。观察随着训练进行,平均奖励和探索率(epsilon)是如何变化的。最终代理是否能找到一条到达目标的有效路径?

练习 2:调整超参数 尝试修改 train() 函数中 QLearningAgent 的初始化参数:

  1. 将学习率 lr 改为 0.010.5,观察训练过程有何不同?
  2. 将折扣因子 gamma 改为 0.50.9,这对代理的长期规划能力有何影响?
  3. 去掉或减慢 epsilon_decay,观察代理的行为(它会不会一直原地打转?)。

练习 3:修改环境 修改 GridWorld 类:

  1. 在网格中间添加一堵墙(例如,状态 (1,1)(2,1) 不可到达),代理该如何绕行?
  2. 改变奖励设计:将每步惩罚 -0.1 改为 0,或者将终点奖励 1 改为 10。这会影响代理的学习速度和路径选择吗?

5. 常见错误

  1. 忽略探索:如果一开始就将 epsilon 设为 0,代理只会利用当前已知信息,可能永远无法发现最优路径(特别是当初始Q值全为0时)。
  2. 折扣因子设置不当γ 接近1会让代理更看重未来奖励(可能学得慢但长远规划好),γ 接近0会让代理只关注眼前奖励。
  3. 学习率过高或过低:过高会导致Q值震荡,无法收敛;过低会导致学习速度过慢。
  4. 状态空间离散化不当:对于连续状态空间,需要更复杂的状态表示方法(如函数近似),直接使用表格型Q-Learning会失效。
  5. 忘记更新目标Q值:在实现更复杂的算法(如DQN)时,常见错误是错误地计算了目标 r + γ * max Q(s', a') 中的 Q 值,应该使用旧的网络参数或独立的目标网络。

6. 小结

  • 强化学习三要素:代理、环境、交互(状态、动作、奖励)。
  • 目标:学习一个策略 π,以最大化长期累积奖励 G
  • Q-Learning 核心:学习并更新一个动作价值表 Q(s, a),利用贝尔曼方程进行迭代。
  • 关键更新公式Q(s, a) ← Q(s, a) + α * [r + γ * max Q(s', a') - Q(s, a)]
  • 探索与利用:ε-贪婪策略是平衡两者的最简单有效的方法之一。
  • 代码实现要素:定义环境(状态、动作、转移、奖励)、初始化Q表、设计选择动作的策略、实现学习更新规则、循环训练。

Q-Learning 是理解强化学习的绝佳起点。它清晰地展示了价值迭代的思想,为后续学习更复杂的算法(如Deep Q-Networks, DQN)奠定了坚实的基础。接下来,你就可以尝试用神经网络来近似Q表,以解决状态空间巨大或连续的问题了。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「AutoML:自动化机器学习框架」 以巩固所学知识。