Microsoft ML-For-Beginners项目:强化学习之CartPole平衡问题详解
引言:从离散到连续状态空间的跨越
在之前的强化学习课程中,我们学习了如何解决离散状态空间的问题。但在现实世界中,很多问题都涉及到连续状态空间,比如自动驾驶、机器人控制等。本文将通过Microsoft ML-For-Beginners项目中的CartPole平衡问题,带您深入理解如何将Q-Learning应用于连续状态空间。
CartPole问题概述
CartPole是一个经典的强化学习基准问题,模拟了一个水平移动的小车顶部竖立着一根杆子。我们的目标是通过左右移动小车来保持杆子竖直不倒。这个问题看似简单,却包含了强化学习的核心挑战:
- 连续状态空间:杆子的角度、位置等都是连续值
- 延迟奖励:只有在杆子倒下时才会得到负面反馈
- 实时控制:需要在每个时间步做出正确的动作
环境搭建与基础认知
1. OpenAI Gym环境初始化
OpenAI Gym提供了标准化的强化学习环境接口,我们首先初始化CartPole环境:
import gym
env = gym.make("CartPole-v1")
环境初始化后,我们需要了解两个关键属性:
- 动作空间(Action Space):离散的,包含两个动作(左/右)
- 观察空间(Observation Space):四个连续值(小车位置、速度、杆角度、角速度)
2. 环境交互基础
与环境的交互遵循"观察-动作-反馈"循环:
obs = env.reset() # 重置环境,获取初始观察值
done = False
while not done:
action = env.action_space.sample() # 随机选择动作
obs, reward, done, info = env.step(action) # 执行动作
状态离散化:连续到离散的转换
Q-Learning需要离散的状态空间,因此我们需要将连续的观察值离散化。这里介绍两种主要方法:
方法一:线性缩放取整
def discretize(x):
return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int))
这种方法简单直接,通过将各维度值除以特定系数后取整,但可能导致状态空间过大。
方法二:分箱离散化
def create_bins(interval, num_bins):
return np.linspace(interval[0], interval[1], num_bins+1)
bins = [create_bins(intv, num) for intv, num in zip(intervals, bin_counts)]
def discretize_bins(x):
return tuple(np.digitize(x[i], bins[i]) for i in range(4))
分箱法可以更精确地控制状态空间大小,但需要预先知道各维度的合理范围。
Q-Learning算法实现
1. Q表结构设计
由于状态空间可能很大,我们使用字典而非数组存储Q值:
Q = {} # (state, action) -> Q-value
def qvalues(state):
return [Q.get((state, a), 0) for a in actions]
2. 核心训练流程
训练过程包含以下几个关键步骤:
- 初始化环境:每次训练开始重置环境
- 选择动作:ε-贪心策略平衡探索与利用
- 执行动作:获取新状态和奖励
- 更新Q值:使用Bellman方程
- 记录结果:跟踪训练进度
alpha = 0.3 # 学习率
gamma = 0.9 # 折扣因子
epsilon = 0.9 # 探索率
for epoch in range(100000):
obs = env.reset()
done = False
while not done:
s = discretize(obs)
# ε-贪心动作选择
if random.random() < epsilon:
a = random.choices(actions, weights=probs(qvalues(s)))[0]
else:
a = random.randint(0, 1)
# 执行动作
obs, rew, done, _ = env.step(a)
ns = discretize(obs)
# Q值更新
Q[(s,a)] = (1-alpha)*Q.get((s,a),0) + alpha*(rew + gamma*max(qvalues(ns)))
3. 训练优化技巧
- 动态调整超参数:随着训练进行,逐渐降低学习率α和探索率ε
- 保留最佳Q表:记录训练过程中表现最好的Q表
- 滑动平均奖励:使用滑动窗口计算平均奖励,更准确评估性能
训练结果分析与可视化
1. 原始奖励曲线
直接绘制每个episode的奖励:
plt.plot(rewards)
这种视图噪声较大,难以识别趋势。
2. 滑动平均奖励
使用滑动窗口平均可以更清晰地看到训练进展:
def running_average(x, window):
return np.convolve(x, np.ones(window)/window, mode='valid')
plt.plot(running_average(rewards, 100))
理想情况下,我们应该看到平均奖励逐渐上升并稳定在195以上。
模型评估与应用
1. 测试训练好的模型
使用训练好的Q表运行环境:
obs = env.reset()
done = False
while not done:
s = discretize(obs)
env.render()
# 选择最佳动作
a = np.argmax(qvalues(s))
obs, _, done, _ = env.step(a)
env.close()
2. 性能提升技巧
- 使用最佳Q表:训练过程中保存的最高分Q表往往表现更好
- 确定性策略:测试时直接选择最优动作,而非按概率采样
- 状态离散化优化:尝试不同的离散化方法,找到最佳状态表示
挑战与进阶
- 超参数调优:尝试不同的α、γ、ε组合,观察对训练效果的影响
- 高级离散化:设计更合理的状态离散化方法,减少信息损失
- 算法改进:尝试使用DQN等深度强化学习方法处理原始连续状态
结语
通过CartPole问题,我们学习了如何将Q-Learning应用于连续状态空间。虽然Q-Learning在简单问题上表现良好,但对于更复杂的问题,可能需要考虑深度Q网络(DQN)等更高级的方法。希望本文能帮助您建立强化学习的基础,并为后续更复杂的问题解决奠定基础。
记住,强化学习的核心在于智能体与环境的不断交互学习,正如我们人类通过试错来学习新技能一样。保持耐心,持续实验,您将能够掌握这门强大的机器学习技术!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考