文件名 Q-learning.md

Q-learning

本文目录

正文

个人理解:

我理解Q-learning其实就是绘制一张Q表格,表格的列数是智能体能够执行的动作数(例如动作为前、后则为2),表格的行数是状态空间的数量(例如状态空间为2*8则为16)。Q表格中每一个单元格的内容,对应当智能体处于某个位置时 执行某个动作的 预期累积奖励 。

  • 位置与状态 属于同一概念。
  • 这里不涉及深度学习,只是利用智能体按采用结果,走不同路 得到的奖励,进行迭代更新Qtable。

下面是一个 16 状态空间,4 动作的环境运行结果,在训练完成后,会得到一个Q table,每个位置对应智能体身处哪个位置,走哪一步的预期奖励。

参数解析

  • 学习率 α : 学习率决定了智能体在每一次学习中,新信息对旧Q值的影响程度。简单来说,它控制了你“相信”新经验的程度。
  • **折扣因子 γ **: 它决定了智能体是更看重眼前的即时奖励,还是更看重未来的长期奖励。
  • 探索率 ϵ : 决定了智能体在“探索”未知动作和“利用”已知最优动作之间如何进行权衡。
    • 通常在训练初期设置较高的 ϵ 值,鼓励探索;随着训练的进行,逐渐减小 ϵ 的值,让智能体更多地利用所学到的知识。这种策略被称为“ϵ-greedy”策略。

Qtable初始化

全部初始化为零 (Zeros Initialization)

  • 这是最简单、最常用的方法。所有Q表格中的值都被初始化为0。
  • 优点:简单易实现,且在大多数情况下效果很好。
  • 原理:初始状态下,智能体对任何状态下的任何动作都没有任何偏好,所有动作都被认为是等价的,因此探索会非常随机。随着训练的进行,智能体通过与环境的互动,会慢慢学习到真实的Q值。

全部初始化为较小的随机值 (Small Random Values)

  • 所有Q表格中的值都被初始化为较小的、接近0的随机数。
  • 优点:可以打破对称性,确保智能体在一开始不会对所有动作都保持完全相同的偏好。在某些复杂问题中,这可能有助于加速学习过程。

代码实践,冰湖问题:

import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
import random

# 1. 初始化环境
# is_slippery=True 开启了随机性,让问题更具挑战
env = gym.make('FrozenLake-v1', is_slippery=True)

# 2. 定义超参数 (Hyperparameters)
# 这些参数可以调整,以观察对学习效果的影响
total_episodes = 20000       # 总共玩多少局游戏
learning_rate = 0.1          # 学习率 alpha
gamma = 0.99                 # 折扣因子 gamma

epsilon = 1.0                # 初始探索率
max_epsilon = 1.0            # 探索率最大值
min_epsilon = 0.01           # 探索率最小值
decay_rate = 0.0005          # 探索率的衰减率

# 3. 初始化Q-Table
state_space_size = env.observation_space.n
action_space_size = env.action_space.n
q_table = np.zeros((state_space_size, action_space_size))

# 用于记录训练过程中的成功率
rewards_per_episode = []
success_rate_per_episode = []
log_interval = 1000 # 每1000轮记录一次

# 4. 训练主循环
for episode in range(total_episodes):
    # 重置环境,开始新的一局
    state, info = env.reset()
    terminated = False
    
    while not terminated:
        # 4.1 Epsilon-Greedy 策略选择动作
        exploration_rate_threshold = random.uniform(0, 1)
        if exploration_rate_threshold > epsilon:
            # 利用 (Exploitation): 选择当前状态下Q值最大的动作
            action = np.argmax(q_table[state, :])
        else:
            # 探索 (Exploration): 随机选择一个动作
            action = env.action_space.sample()

        # 4.2 执行动作并观察结果
        new_state, reward, terminated, truncated, info = env.step(action)
        
        # 如果游戏因为掉洞里或超时而结束,但没有到达终点,我们就给一个小的负奖励
        # 原始环境只有在到达终点时奖励为1,其余都为0。这样的稀疏奖励很难学习。
        # 这里我们做一点小小的奖励工程 (Reward Engineering)
        if terminated and reward == 0:
            reward = -0.1 # 掉洞里
        elif not terminated:
            reward = -0.01 # 每走一步都消耗一点成本

        # 4.3 更新Q-Table (Q-Learning核心公式)
        old_value = q_table[state, action]
        next_max = np.max(q_table[new_state, :])
        
        new_value = old_value + learning_rate * (reward + gamma * next_max - old_value)
        q_table[state, action] = new_value
        
        # 更新状态
        state = new_state

    # 4.4 探索率衰减
    # 随着训练进行,我们越来越相信Q-Table,所以减少探索
    epsilon = min_epsilon + (max_epsilon - min_epsilon) * np.exp(-decay_rate * episode)
    
    # 记录该轮的最终奖励(成功为1,失败为-0.1)
    rewards_per_episode.append(reward)
    
    # 每隔一段时间计算并记录成功率
    if (episode + 1) % log_interval == 0:
        successful_episodes = sum(1 for r in rewards_per_episode[-log_interval:] if r > 0)
        success_rate = successful_episodes / log_interval
        success_rate_per_episode.append(success_rate)
        print(f"Episode {episode+1}/{total_episodes} | "
              f"Success Rate (last {log_interval} ep): {success_rate:.2f} | "
              f"Epsilon: {epsilon:.4f}")

env.close()

# 5. 产出与评估
print("\n--- 训练完成 ---")
print("最终学到的Q-Table:")
print(q_table)

# 5.1 图表:展示成功率的变化趋势
plt.figure(figsize=(12, 6))
plt.plot(range(log_interval, total_episodes + 1, log_interval), success_rate_per_episode)
plt.title('Success Rate Over Episodes')
plt.xlabel(f'Episodes (x{log_interval})')
plt.ylabel('Success Rate')
plt.grid(True)
plt.show()

# 5.2 评估学习到的策略
print("\n--- 开始评估学习到的策略 ---")
total_eval_episodes = 100
successful_trips = 0

eval_env = gym.make('FrozenLake-v1', is_slippery=True)

for episode in range(total_eval_episodes):
    state, info = eval_env.reset()
    terminated = False
    
    while not terminated:
        # 在评估阶段,我们不再探索,完全使用贪心策略
        action = np.argmax(q_table[state, :])
        new_state, reward, terminated, truncated, info = eval_env.step(action)
        state = new_state
        
        if terminated and reward == 1.0:
            successful_trips += 1

eval_env.close()

print(f"在 {total_eval_episodes} 次评估中,成功到达终点的次数: {successful_trips}")
print(f"最终策略成功率: {100 * successful_trips / total_eval_episodes}%")