4. 强化学习入门
任务说明
在一个5×5的网格世界中,智能体从左上角(0,0)出发,需要到达右下角(4,4)的目标位置。网格中包含若干障碍物,智能体碰到障碍物或试图越界时会停留在原地并获得惩罚。本案例实现标准的Q-learning算法,通过不断试错学习最优策略,并输出学习过程中的关键指标。
环境与依赖
仅需Python 3.6+,无需第三方库(使用random模块)。代码完全自包含,可直接运行。
完整实现
import random
# 网格世界定义
GRID_SIZE = 5
GOAL = (4, 4)
OBSTACLES = {(1, 1), (2, 2), (3, 1), (1, 3)}
# 动作: 0=上, 1=下, 2=左, 3=右
ACTIONS = [(-1, 0), (1, 0), (0, -1), (0, 1)]
ACTION_NAMES = ['上', '下', '左', '右']
# Q-learning 超参数
ALPHA = 0.1 # 学习率
GAMMA = 0.9 # 折扣因子
EPSILON = 0.1 # 探索率
EPISODES = 500 # 训练回合数
# 初始化Q表: 状态(s) -> 动作(a) 的Q值
Q = {}
for i in range(GRID_SIZE):
for j in range(GRID_SIZE):
if (i, j) not in OBSTACLES:
Q[(i, j)] = [0.0] * 4
def is_valid(state):
"""检查状态是否合法(在网格内且不是障碍物)"""
i, j = state
return 0 <= i < GRID_SIZE and 0 <= j < GRID_SIZE and state not in OBSTACLES
def step(state, action_idx):
"""执行动作,返回(新状态, 奖励, 是否终止)"""
i, j = state
di, dj = ACTIONS[action_idx]
new_state = (i + di, j + dj)
# 越界或撞障碍物:停留原地,给予惩罚
if not is_valid(new_state):
return state, -1.0, False
# 到达目标
if new_state == GOAL:
return new_state, 10.0, True
# 普通移动
return new_state, 0.0, False
def choose_action(state):
"""ε-greedy策略选择动作"""
if random.random() < EPSILON:
return random.randint(0, 3)
# 贪心选择Q值最大的动作(随机打破平局)
q_values = Q[state]
max_q = max(q_values)
best_actions = [a for a, q in enumerate(q_values) if q == max_q]
return random.choice(best_actions)
def train():
"""训练Q-learning,返回每回合的步数和总奖励"""
steps_per_episode = []
rewards_per_episode = []
for episode in range(EPISODES):
state = (0, 0)
total_reward = 0
steps = 0
done = False
while not done and steps < 100: # 防止死循环
action = choose_action(state)
next_state, reward, done = step(state, action)
# Q-learning 更新公式
best_next_q = max(Q[next_state]) if not done else 0
Q[state][action] += ALPHA * (
reward + GAMMA * best_next_q - Q[state][action]
)
state = next_state
total_reward += reward
steps += 1
steps_per_episode.append(steps)
rewards_per_episode.append(total_reward)
return steps_per_episode, rewards_per_episode
def extract_policy():
"""从训练好的Q表中提取确定性策略"""
policy = {}
for state in Q:
if state == GOAL:
policy[state] = '目标'
continue
best_action = max(range(4), key=lambda a: Q[state][a])
policy[state] = ACTION_NAMES[best_action]
return policy
def print_policy(policy):
"""打印策略网格"""
print("学习到的策略(每个格子的最优动作):")
for i in range(GRID_SIZE):
row = []
for j in range(GRID_SIZE):
if (i, j) in OBSTACLES:
row.append('■')
elif (i, j) == GOAL:
row.append('★')
else:
row.append(policy[(i, j)])
print(' '.join(f'{cell:<2}' for cell in row))
# 主程序
if __name__ == '__main__':
random.seed(42) # 固定随机种子,保证可复现
steps, rewards = train()
policy = extract_policy()
# 输出训练结果摘要
print(f"训练完成:共{EPISODES}回合")
print(f"最后10回合平均步数: {sum(steps[-10:]) / 10:.1f}")
print(f"最后10回合平均总奖励: {sum(rewards[-10:]) / 10:.2f}")
print(f"首次到达目标的回合: {next((i+1 for i, s in enumerate(steps) if s < 100), '未到达')}")
print()
print_policy(policy)
# 验证:用训练好的策略走一遍,确认能到达目标
state = (0, 0)
path = [state]
for _ in range(20):
if state == GOAL:
break
action_idx = max(range(4), key=lambda a: Q[state][a])
state, _, done = step(state, action_idx)
path.append(state)
if done:
break
print(f"\n验证路径: {' -> '.join(str(p) for p in path)}")
print(f"验证结果: {'成功到达目标' if path[-1] == GOAL else '未能到达目标'}")
运行方式
python q_learning_grid.py
验证方法与判定标准
运行程序后,观察以下输出以确认实现正确:
- 策略合理性:打印的策略网格中,每个非障碍物格子应指向能逐步接近目标的方向,且不存在指向障碍物或明显绕远的动作。
- 收敛性指标:最后10回合的平均步数应显著小于早期回合(通常小于15步),平均总奖励应为正值(接近10)。
- 确定性验证:程序末尾会使用训练好的策略(贪心)从起点走一遍,输出路径应以(0,0)开始、以(4,4)结束,并显示“成功到达目标”。
由于设置了随机种子(seed=42),每次运行结果完全一致,便于复现与比对。
学习过程可视化
图中展示了训练完成后提取的确定性策略:每个非障碍物格子的箭头表示该状态下Q值最大的动作。可以看到智能体学会了绕开障碍物、以最短路径到达目标。训练曲线示意了随着回合增加,到达目标所需的步数逐渐减少并趋于稳定,反映了Q-learning的收敛过程。
常见问题
- 如果训练不收敛:检查学习率α是否过大(建议0.05~0.2),或增加训练回合数至1000。
- 如果策略出现振荡:适当降低探索率ε(如从0.1降至0.05),或采用ε衰减策略。
- 修改网格:直接调整
OBSTACLES集合或GRID_SIZE即可适配不同环境。