Q-Learning是一种在强化学习中广泛应用的算法,它属于值迭代方法的一种,能够通过与环境交互来学习最优策略。Q-Learning的核心思想是学习一个动作价值函数(Q-Function),这个函数可以预测从某个状态采取某个动作后所能获得的长期奖励。
1、Q-Learning的基本原理
Q-Learning的目标是找到一个Q表(Q-table),它表示了在给定状态下执行每个可能动作的价值。这个表中的每个条目Q(s,a)Q(s,a)代表了从状态s出发,执行动作a后,预期可得的累积奖励(即折扣后的未来奖励总和)。
2、更新规则
Q-Learning使用以下更新规则来逐渐改进Q表中的估计:
3、ε-greedy策略
为了平衡探索(exploration)和利用(exploitation),Q-Learning通常采用ε-greedy策略。这意味着以概率ε随机选择动作进行探索,以概率1−ε选择当前Q值最高的动作进行利用。随着学习的进行,ε值会逐渐减小,使得算法更加依赖于已有的知识。
还有一些其他的策略,比如上置信界以及汤普森,后面有机会再说。。。。。
4、收敛性
Q-Learning的一个重要特性是,在满足一定的条件(如充分的探索和有限的状态-动作空间)下,它能够收敛到最优策略,悬崖漫步就看总反馈值是否收敛。
4、实施步骤
- 初始化Q表为零或任意小数值。
- 对于每个episode:
- 观察初始状态s0。
- 对于每个时间步:
- 根据ε-greedy策略选择动作at。
- 执行动作atat,观察奖励rt+1和新状态st+1。
- 使用Q-Learning更新规则更新Q(st,at)。
- 将st+1设置为新的当前状态st。
- 当达到终止状态时,结束该episode,并开始下一个episode。
Q-Learning在实际应用中非常广泛,尤其是在游戏、机器人控制和推荐系统等领域。然而,当状态空间或动作空间非常大时,直接使用Q-table的方式可能不再可行,这时通常会使用函数逼近的方法(如深度Q网络DQN)来近似Q函数。
以悬崖漫步游戏环境为例:
import numpy as np
import gymnasium as gym
import matplotlib.pyplot as plt
# 定义环境
env = gym.make('CliffWalking-v0', render_mode="human")
# 定义4个动作 上下左右
ACTIONS = env.action_space.n
# 定义环境参数
ROWS = 4
COLS = 12
# 定义 Q-learning 参数
EPISODES = 500
ALPHA = 0.1 # 初始学习率
GAMMA = 0.99 # 折扣因子
EPSILON = 0.1 # 探索率
loss_count = 0 # 值随时间衰减的 -贪婪算法
# 初始化 Q-table
q_table = np.zeros([ROWS * COLS, ACTIONS])
# 创建列表用于存储每一轮的总奖励
rewards_list = []
# 定义一个函数根据 Q-table 和 ε-greedy 策略选择动作
def choose_action(state, q_table, epsilon):
global loss_count
loss_count += 1
if np.random.random() < 1 / loss_count:
# 随机选择一个动作
return np.random.randint(ACTIONS)
else:
# 选择具有最大 Q 值的动作
return np.argmax(q_table[state])
# 定义一个函数实现 Q-learning 算法
def q_learning():
# 进行多次训练以更新 Q-table
global episode
for episode in range(EPISODES):
# 从起始状态开始
state, _ = env.reset()
done = False
count = 0
steps = 0
total_reward = 0
# 在每个训练回合中不断采取动作,直到达到终点
print("Episode Start: {}".format(episode))
while not done and count < 200:
count += 1
# 选择一个动作
action_index = choose_action(state, q_table, EPSILON)
# 获取下一个状态、奖励和是否到达终点
next_state, reward, done, _, _ = env.step(action_index)
total_reward += reward
steps += 1
# 更新 Q-table
q_table[state][action_index] = q_table[state][action_index] + ALPHA * (
reward + GAMMA * np.max(q_table[next_state]) - q_table[state][action_index]
)
# 更新当前状态
state = next_state
# 打印每一集的总结
print(f"Episode {episode + 1} finished after {steps} steps with total reward {total_reward}")
# 将这一轮的总奖励添加到列表中
rewards_list.append(total_reward)
# 绘制每一轮的总奖励
plt.figure(figsize=(10, 5))
plt.plot(rewards_list)
plt.title('Total Reward per Episode')
plt.xlabel('Episode')
plt.ylabel('Total Reward')
plt.grid(True)
plt.show()
# 关闭环境
env.close()
# 返回训练好的 Q-table
return q_table
q = q_learning()
print(q)
随着训练次数的增加,ε值会逐渐减小,使得算法更加依赖于已有的知识,差不多30次左右小人就能吃到饼干了
然后从最终的总奖励值图可以看到,在300次左右就能达到收敛
对比使用固定ε值的代码,可以看到收敛趋势是有 但是效果并不好
增加了episode训练次数也不行
重点还是对于Q表的更新策略:
公式!!!
5、gym的环境原理代码如下:
熟悉环境的设置有利于我们去应用在其他的项目上,所以gym的原理需要了解,只不过gym对环境进行了gui的封装
import numpy as np
import matplotlib.pyplot as plt
class CliffWalkingEnv:
def __init__(self):
self.grid_size = 4
self.start = (0, 0)
self.goal = (0, self.grid_size - 1)
self.cliff = [(i, self.grid_size - 1) for i in range(1, self.grid_size - 1)]
self.agent_position = self.start
def step(self, action):
# 动作编码:0=up, 1=down, 2=left, 3=right
dx, dy = [(0, -1), (0, 1), (-1, 0), (1, 0)][action]
x, y = self.agent_position
new_x, new_y = x + dx, y + dy
# 检查新位置是否有效
if 0 <= new_x < self.grid_size and 0 <= new_y < self.grid_size:
self.agent_position = (new_x, new_y)
else:
return self.agent_position, -1, False
# 检查是否落在悬崖上
if self.agent_position in self.cliff:
self.agent_position = self.start
return self.agent_position, -100, False
# 检查是否到达目标
if self.agent_position == self.goal:
return self.agent_position, 0, True
return self.agent_position, -1, False
def reset(self):
self.agent_position = self.start
return self.agent_position