在之前学习的过程中我了解到深度学习中很重要的一个概念是反向传播,最近看论文发现深度强化学习(DRL)有各种各样的方法,但是却很难区分他们的损失函数的计算以及反向传播的过程有何不同。在有监督的学习中,损失可以理解为预测值和真实值之间的距离,那么在这里的损失指的是什么?是否也涉及到两个值之间的差距呢,具体是哪两个东西之间的拟合(学习)?如何拟合?这里通过举例对几种强化学习方法进行比较。
问题设计
假设有一个3×3的迷宫(如上图),用S表示起点(入口)、E表示终点(出口)、O表示通路、W表示墙壁,目标是从S到E找到一条通路。
Q-learning
原理
Q-learning是一种基于值函数的强化学习方法,其核心思想是通过更新和维护动作值函数
Q
(
s
,
a
)
Q(s,a)
Q(s,a)来间接推导出最优策略,使用Q-table存储每个状态-动作对的期望奖励。与深度学习中的反向传播不同,Q-learning在传统实现中并不依赖反向传播。
Q
(
s
,
a
)
Q(s,a)
Q(s,a)的更新公式为贝尔曼方程:
Q
(
s
,
a
)
←
Q
(
s
,
a
)
+
α
[
r
+
γ
max
a
′
Q
(
s
′
,
a
′
)
−
Q
(
s
,
a
)
]
Q(s,a)←Q(s,a)+α[r+γ\max_{a'}Q(s',a')-Q(s,a)]
Q(s,a)←Q(s,a)+α[r+γa′maxQ(s′,a′)−Q(s,a)]
其中,
α
α
α是学习率,
γ
γ
γ是折扣因子,
r
r
r是当前奖励,
s
′
s'
s′是下一个状态。这是一个迭代更新公式,直接对Q-table进行更新,无需显式的梯度计算或反向传播,Q-table存储了所有状态-动作对的值,更新过程是逐个元素地调整表格中的值。
模拟
参数设计
- 奖励规则:到达终点奖励+100;到达墙壁或超出迷宫奖励-10;其他情况奖励分值为6-到出口最短路线距离(假设这些距离提前都计算好了)。比如走到(1,0)+3,走到(2,2)+4,走到(0,1)-10,走到(0,2)+100。
- 贝尔曼方程参数: α = 0.8 α=0.8 α=0.8; γ = 0.2 γ=0.2 γ=0.2。
Q-table初始化
初始化一个Q-table,大小为状态数×行为数
。对于3×3的迷宫,状态数为9(每个格子为一个状态),行为数为4(上下左右)。初始化Q-table为0:
学习过程
第一次
状态和状态转移:
状态s | 行为(随机) | 下一个状态s’ |
---|---|---|
(0,0) | UP | × |
更新Q-table:
原始Q[(0,0), UP] | 奖励 | 下一状态能获得的最大奖励 | 更新后的Q[(0,0), UP] |
---|---|---|---|
0 | -10 | 0 | -8 |
结果:
第二次
状态和状态转移:
状态s | 行为(随机) | 下一个状态s’ |
---|---|---|
(0,0) | DOWN | (1,0) |
更新Q-table:
原始Q[(0,0), DOWN] | 奖励 | 下一状态能获得的最大奖励 | 更新后的Q[(0,0), DOWN] |
---|---|---|---|
0 | +3 | 0 | 2.4 |
结果:
第三次
状态s | 行为(随机) | 下一个状态s’ | 原始Q[(1,0), RIGHT] | 奖励 | 下一状态能获得的最大奖励 | 更新后的Q[(1,0), RIGHT] |
---|---|---|---|---|---|---|
(1,0) | RIGHT | (1,1) | 0 | +4 | 0 | 3.2 |
结果:
中间省略1000次
状态s | 行为(随机) | 下一个状态s’ | 原始Q[(1,1), LEFT] | 奖励 | 下一状态能获得的最大奖励 | 更新后的Q[(1,1), LEFT] |
---|---|---|---|---|---|---|
(1,1) | LEFT | (1,0) | 0 | +3 | 3.2 | 2.912 |
状态s | 行为(随机) | 下一个状态s’ | 原始Q[(1,0), DOWN] | 奖励 | 下一状态能获得的最大奖励 | 更新后的Q[(1,0), DOWN] |
---|---|---|---|---|---|---|
(1,0) | DOWN | (2,0) | 0 | +2 | 0 | 1.6 |
自己抽签和计算太麻烦了,于是写了代码大致模拟了一下。
# Dangerous
import random
keys = [(0,0,'up'), (0,0,'down'), (0,0,'left'), (0,0,'right'),
(0,1,'up'), (0,1,'down'), (0,1,'left'), (0,1,'right'),
(0,2,'up'), (0,2,'down'), (0,2,'left'), (0,2,'right'),
(1,0,'up'), (1,0,'down'), (1,0,'left'), (1,0,'right'),
(1,1,'up'), (1,1,'down'), (1,1,'left'), (1,1,'right'),
(1,2,'up'), (1,2,'down'), (1,2,'left'), (1,2,'right'),
(2,0,'up'), (2,0,'down'), (2,0,'left'), (2,0,'right'),
(2,1,'up'), (2,1,'down'), (2,1,'left'), (2,1,'right'),
(2,2,'up'), (2,2,'down'), (2,2,'left'), (2,2,'right')]
values = [-8,2.4,0,0,0,0,0,0,0,0,0,0,0,0,0,3.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # 经历三次学习后的值
rewards = [[2, -10, 100], [3, 4, 5], [2, -10, 4]]
action_list = ['up', 'down', 'left', 'right']
qTable = dict(zip(keys, values)) # 创建初始列表
alpha = 0.8
gamma = 0.2
now_x = 1
now_y = 1
def transfer_x(x, act):
if act == 'up': return x-1
elif act == 'down': return x+1
else: return x
def transfer_y(y, act):
if act == 'left': return y-1
elif act == 'right': return y+1
else: return y
for i in range(1000): # 学习1000次
action = random.choice(action_list) # 行为
next_x = transfer_x(now_x, action) # 下一个状态s'
next_y = transfer_y(now_y, action)
before_q = qTable[(now_x, now_y, action)] # 原始Q
if next_x not in [0,1,2] or next_y not in [0,1,2]: # 奖励
reward = -10
else: reward = rewards[next_x][next_y]
if next_x not in [0, 1, 2] or next_y not in [0, 1, 2]: # 下一状态能获得的最大奖励
max_reward = 0
else:
max_reward = 0
for a in action_list:
r = qTable[(next_x, next_y, a)]
if r > max_reward: max_reward = r
qTable[(now_x, now_y, action)] = before_q + alpha * (reward + gamma * max_reward - before_q) # 更新后的Q值
random_start_list = [(0,0), (1,0), (1,1), (1,2), (2,0), (2,2)]
random_start = random.choice(random_start_list)
random_p = random.randint(1,10)
random_flag = True if random_p == 1 else False
if next_x == 0 and next_y == 2: # 如果走到终点就从随机位置开始
now_x = random_start[0]
now_y = random_start[1]
elif next_x not in [0, 1, 2] or next_y not in [0, 1, 2]:
# 如果走出去就不改变当前位置(以0.1的概率随机初始位置)
if random_flag:
now_x = random_start[0]
now_y = random_start[1]
elif next_y == 1 and (next_x == 0 or next_x == 2):
# 如果走到墙上就不改变当前位置(以0.1的概率随机初始位置)
if random_flag:
now_x = random_start[0]
now_y = random_start[1]
else: # 或者以0.1的概率随机初始位置
now_x = next_x
now_y = next_y
if random_flag:
now_x = random_start[0]
now_y = random_start[1]
# 打印Q表
for key in qTable:
print("key:" + str(key) + " " + str(qTable[key]))
结果
得到结果如下:
其中阴影部分是墙和终点的位置,由于无法到达墙、到达终点后游戏结束,所以这两个位置不会更新Q值。
上表的解释:在(0,0)位置时(对应当前状态),Q值在行为"down"处最大为4.8,因此应该向下走,下一个状态是(1,0)。其余位置类似。
红色箭头表示每一个状态的最优行为,绿色箭头表示用该方法计算得出的最优路线。
DQN(深度Q-learning)
DQN和Q学习的主要区别在于如何表示和更新Q值,DQN使用神经网络来近似Q值函数,而不是使用表格。
经验回放
经验回放(Experience Replay)是强化学习中的一个重要技术,主要用于解决强化学习算法中的一些问题,提高算法的稳定性和学习效率。
具体来说,就是不每次都立刻进行Q值计算和反向传播,而是将每次的<状态、动作、状态转移>
(称为经验样本或经验元组)先放到缓冲区,然后每次从缓冲区取一部分一次性计算。类似计算机视觉中同时对数据集中的多张图片进行处理,大小表示为batch_size。
原理
其实就是将Q学习中需要查Q-table的时候都用网络替换。旧的Q表值和奖励共同作用下更新为新的Q表值。
(我以前一直以为是要让Q值越来越拟合奖励,所以很奇怪,明明奖励的公式我们已经知道了,还要怎么去学习呢,其实奖励只是作为一个已知的数值加入到计算中,并不是说我们要去模拟奖励的生成方式。)
Q-learning中的贝尔曼方程:
Q
(
s
,
a
)
←
Q
(
s
,
a
)
+
α
[
r
+
γ
max
a
′
Q
(
s
′
,
a
′
)
−
Q
(
s
,
a
)
]
Q(s,a)←Q(s,a)+α[r+γ\max_{a'}Q(s',a')-Q(s,a)]
Q(s,a)←Q(s,a)+α[r+γa′maxQ(s′,a′)−Q(s,a)]
可以写为:
Q
(
s
,
a
)
←
(
1
−
α
)
Q
(
s
,
a
)
+
α
[
r
+
γ
max
a
′
Q
(
s
′
,
a
′
)
]
Q(s,a)←(1-α)Q(s,a)+α[r+γ\max_{a'}Q(s',a')]
Q(s,a)←(1−α)Q(s,a)+α[r+γa′maxQ(s′,a′)]
就像加权求和,它在原来Q的基础上加上了一部分奖励作用下的分数。
深度Q-learning中的损失函数:
L
(
θ
)
=
E
s
,
a
,
r
,
s
′
[
(
Q
θ
(
s
,
a
)
−
(
r
+
γ
max
a
′
Q
θ
−
(
s
′
,
a
′
)
)
)
2
]
L(θ)=\mathbb{E}_{s,a,r,s'}[(Q_θ(s,a)-(r+γ\max_{a'}Q_{θ^-}(s',a')))^2]
L(θ)=Es,a,r,s′[(Qθ(s,a)−(r+γa′maxQθ−(s′,a′)))2]
它就是使计算得到的Q值逐渐趋于稳定,基本上等于那个奖励作用下的分数,这个分数不仅考虑了当前奖励,还考虑了下一步能够得到的分数。注意上式中有两组参数,分别为 θ θ θ和 θ − θ^- θ−,对应两个网络(结构一样)。
-
Q
θ
(
s
,
a
)
Q_θ(s,a)
Qθ(s,a)通过主网络训练得到
主网络(Main Network)也称为在线网络(Online Network)或当前网络(Current Network)。主网络负责学习环境中的状态-动作值函数(Q函数),即预测在给定状态下采取某个动作所能获得的预期回报。主网络通过梯度下降等优化算法不断更新其参数,以最小化预测Q值和实际Q值之间的差距。主网络直接与环境交互,根据采集到的经验(状态、动作、奖励、下一个状态)进行学习。 -
max
a
′
Q
θ
−
(
s
′
,
a
′
)
\max_{a'}Q_{θ^-}(s',a')
maxa′Qθ−(s′,a′)由目标网络计算
目标网络(Target Network)是主网络的一个延迟更新版本,其参数定期从主网络复制过来。目标网络不直接参与学习,而是为主网络提供一个稳定的训练目标。在计算Q值的目标值时,使用目标网络来预测下一个状态下的Q值,这样可以减少学习过程中的方差,使学习更加稳定。目标网络的更新频率通常低于主网络,例如,每几千个时间步更新一次。
说明通过神经网络得到的Q值不仅体现了当前决策的奖励,还综合了未来的奖励。
模拟
参数设计
- 奖励规则:到达终点奖励+100;到达墙壁或超出迷宫奖励-10;其他情况奖励分值为6-到出口最短路线距离(假设这些距离提前都计算好了)。比如走到(1,0)+3,走到(2,2)+4,走到(0,1)-10,走到(0,2)+100。迷宫格内的奖励矩阵可以表示为:
- 贝尔曼方程参数: α = 0.001 α=0.001 α=0.001; γ = 0.99 γ=0.99 γ=0.99。
- 网络输入:状态 s s s,由二维坐标表示。从上往下三行的行号分别为0、1、2,从左到右三列的列标号分别为0,1,2,(0,0)是起点位于左上角,(0,2)是终点位于右上角。
- 网络输出:每个动作的Q值。
- 隐藏层架构:1层,16个神经元,激活函数为ReLU。
- ε-贪婪策略:初始阶段,以较高概率ϵ(设置为1.0)随机选择动作。随着训练进行,逐渐降低ϵ(最后衰减到0.01),更多地依赖网络预测的动作。
- 经验回放:缓冲区大小为 10 , 000 10,000 10,000, b a t c h s i z e = 32 batch_{size}=32 batchsize=32。
- 同步目标网络步长为100。
学习过程
- 初始化将智能体放置在起点
(0,0)
。 - 选择动作。使用ε-贪婪策略选择动作a。如果 r a n d o m < ϵ random<ϵ random<ϵ,随机选择动作。否则,选择 a r g max a Q θ ( s , a ) arg\max_aQ_θ(s,a) argmaxaQθ(s,a)。
- 执行动作:根据选定的动作 a a a,更新智能体的位置 s ′ s′ s′。如果超出迷宫范围或撞到障碍墙,给予奖励 -10 并结束回合。如果到达终点 (0, 2),给予奖励 100 并结束回合。否则,根据奖励矩阵 R R R给出奖励 r r r。
- 存储经验:将经验存入缓冲区。
- 采样并更新网络:从缓冲区中随机采样一批数据计算目标值。
更新主网络参数 θ θ θ以最小化损失函数 L ( θ ) L(θ) L(θ)。 - 同步目标网络:每隔若干步,将主网络参数复制到目标网络。
- 测试(关闭探索,使用网络预测的动作观察智能体是否能够成功从起点走到终点)。
【欢迎指正】