第4章:Q-Learning与表格方法:Q表更新原理、ε-贪婪策略、在简单栅格环境中的无人机导航
各位同学,欢迎来到第四章。这一章我们终于要动手了——让无人机学会自己找路。
前面几章我们聊了强化学习的基本框架,马尔可夫决策过程,还有贝尔曼方程。说实话,那些都是理论铺垫。今天我们要讲的,是强化学习里最经典、最直观的算法之一:Q-Learning。
我个人觉得,Q-Learning 就像强化学习的「Hello World」。你把这个搞懂了,后面那些深度网络、策略梯度什么的,理解起来会顺畅很多。
4.1 什么是Q表?
先问大家一个问题:如果你是一个无人机,在栅格环境里飞行,你怎么知道下一步该往哪走?
最简单的办法——记笔记。
你每到一个格子,就掏出小本本,记下:「在这个格子,往东走能得多少分?往西走呢?」
这个「小本本」,就是Q表。
Q表本质上是一个二维表格:
- 行:所有可能的状态(比如无人机所在的栅格坐标)
- 列:所有可能的动作(上、下、左、右、悬停等)
- 单元格的值:Q(s, a),表示在状态s下执行动作a的「长期回报期望」
我刚开始做无人机项目时,环境是20×20的栅格,状态数400个,动作5个。Q表就是400行×5列,一共2000个格子。手动初始化全零,然后一点点填满。
核心思想:Q表就是无人机的「经验手册」。它告诉无人机:在每一个位置,每一个动作到底值不值得做。
4.2 Q表更新原理
好,现在无人机有了小本本。但它怎么往本子上记东西呢?
这就是Q-Learning的核心公式了。别怕,我拆开讲:
Q(s, a) ← Q(s, a) + α [ r + γ * max_a' Q(s', a') - Q(s, a) ]
这个公式看着唬人,其实就三部分:
- 当前估计:Q(s, a) —— 你原来觉得这个动作值多少
- 实际收获:r + γ * max_a' Q(s', a') —— 你执行完动作后,实际拿到了奖励r,再加上未来能拿到的最大价值
- 误差修正:两者相减,乘以学习率α,然后加回去
说白了就是:用实际经验来修正你的估计。
举个例子。无人机在格子(2,3),它选择「向右走」。执行后:
- 它拿到了即时奖励 r = -1(因为移动一步有能耗惩罚)
- 它到了新格子(2,4),发现这个格子未来最好动作的价值是 max Q(s', a') = 0.8
- 假设 γ = 0.9,那么实际收获 = -1 + 0.9 × 0.8 = -0.28
- 原来它觉得向右走值 0.5,现在发现实际只有 -0.28
- 于是它把Q值往下调一点:新Q = 0.5 + α × (-0.28 - 0.5)
嗯,这里要注意:学习率α不能太大也不能太小。我踩过这个坑——α设成0.9,结果Q值震荡得像过山车,无人机永远不收敛。后来我习惯设0.1到0.3之间,稳得很。
我的经验:α = 0.1 适合大多数场景。如果环境变化剧烈(比如障碍物会移动),可以适当调大到0.3。但别超过0.5,否则你会后悔的。
4.3 ε-贪婪策略:探索与利用的平衡
现在无人机有了Q表,也知道怎么更新了。但有个问题:它该不该每次都选Q值最大的动作?
你想想看,如果每次都选最好的,那它永远不知道有没有更好的路。这就是经典的探索-利用困境。
解决方案?ε-贪婪策略。
做法很简单:
- 以概率 ε 随机选一个动作(探索)
- 以概率 1-ε 选Q值最大的动作(利用)
我习惯把ε设成0.1,也就是90%的时间利用经验,10%的时间探索新路。
但这里有个技巧:ε可以随时间衰减。
刚开始训练时,无人机啥都不懂,多探索探索。训练到后期,经验丰富了,就少探索多利用。
# ε衰减策略示例
epsilon = 1.0
epsilon_min = 0.01
epsilon_decay = 0.995
for episode in range(total_episodes):
# 训练...
epsilon = max(epsilon_min, epsilon * epsilon_decay)
我曾经在一个复杂迷宫里试过固定ε=0.1,结果无人机绕了5000个回合还没找到最优路径。后来改成衰减策略,1500回合就收敛了。差距就这么大。
避坑指南:ε衰减太快也不行。我试过decay=0.9,结果ε从1.0掉到0.01只用了50个回合,无人机还没探索够就「固化」了,最后找到的是次优解。建议decay设在0.995到0.999之间。
4.4 在简单栅格环境中的无人机导航
理论讲完了,咱们来实战。下面是一个完整的Q-Learning无人机导航示例。
环境设定:
- 10×10的栅格世界
- 起点:左上角 (0, 0)
- 终点:右下角 (9, 9),到达得+100分
- 障碍物:随机分布,撞上得-50分并回到起点
- 每走一步:-1分(鼓励走最短路径)
动作空间:上、下、左、右
import numpy as np
import random
# 环境参数
GRID_SIZE = 10
START = (0, 0)
GOAL = (9, 9)
OBSTACLES = [(3,3), (3,4), (3,5), (7,7), (8,7)]
# Q表初始化
Q = np.zeros((GRID_SIZE, GRID_SIZE, 4)) # 4个动作
# 超参数
alpha = 0.1
gamma = 0.9
epsilon = 1.0
epsilon_min = 0.01
epsilon_decay = 0.995
episodes = 2000
# 动作映射
actions = [(-1,0), (1,0), (0,-1), (0,1)] # 上、下、左、右
def get_next_state(state, action_idx):
dx, dy = actions[action_idx]
nx, ny = state[0] + dx, state[1] + dy
# 边界检查
if nx < 0 or nx >= GRID_SIZE or ny < 0 or ny >= GRID_SIZE:
return state # 撞墙,原地不动
return (nx, ny)
def get_reward(state, next_state):
if next_state == GOAL:
return 100
if next_state in OBSTACLES:
return -50
return -1
# 训练循环
for ep in range(episodes):
state = START
done = False
while not done:
# ε-贪婪选择动作
if random.random() < epsilon:
action = random.randint(0, 3)
else:
action = np.argmax(Q[state[0], state[1], :])
# 执行动作
next_state = get_next_state(state, action)
reward = get_reward(state, next_state)
# Q表更新
best_next = np.max(Q[next_state[0], next_state[1], :])
td_target = reward + gamma * best_next
td_error = td_target - Q[state[0], state[1], action]
Q[state[0], state[1], action] += alpha * td_error
# 状态转移
state = next_state
if state == GOAL or state in OBSTACLES:
done = True
# ε衰减
epsilon = max(epsilon_min, epsilon * epsilon_decay)
if ep % 200 == 0:
print(f"Episode {ep}, Epsilon: {epsilon:.3f}")
print("训练完成!")
这段代码跑完,Q表就训练好了。你可以用训练好的Q表来规划路径:从起点开始,每一步都选Q值最大的动作,直到到达终点。
4.5 Q-Learning的局限与思考
Q-Learning很强大,但它有硬伤。
第一个问题:状态空间爆炸
刚才的例子是10×10的栅格,状态数100个。如果换成100×100呢?10000个状态。再考虑连续坐标?无穷多个状态。Q表根本存不下。
这就是为什么后来有了深度Q网络(DQN)——用神经网络代替Q表。
第二个问题:收敛速度
Q-Learning是逐状态更新的。你想想看,100个状态,每个状态4个动作,每个动作要反复更新几十次才能收敛。如果状态多了,训练时间会指数级增长。
我做过一个实验:50×50的栅格,Q-Learning跑了整整一个晚上才收敛。换成DQN,两个小时就搞定了。
总结一下:Q-Learning适合状态空间小、离散动作的场景。它是理解强化学习的基础,但实际工程中,我们更多用它的进阶版本。
4.6 本章核心知识图谱
下面这张图,是我自己画的Q-Learning核心流程。你看一遍,应该就能把整个逻辑串起来了。
这张图把整个流程串起来了。你从左上角开始看,顺着箭头走一遍,就能理解无人机是怎么一步步学会导航的。
4.7 写在最后
Q-Learning虽然简单,但它是强化学习的基石。我到现在做项目,遇到小规模离散状态空间的问题,第一反应还是用Q表试试——因为它快、稳、好调试。
下一章我们会聊深度Q网络,也就是DQN。到时候你会发现,DQN的核心思想和Q-Learning一模一样,只是把Q表换成了神经网络。所以,把这一章吃透,后面会轻松很多。
好了,去动手写代码吧。记住:纸上得来终觉浅,绝知此事要coding。