深度强化学习中的Q学习算法详解
1. 策略选择与Q函数
在强化学习中,我们大部分时间会选择最大价值的行动。不过,为了提高模型的探索能力,我们通常会保留一个小概率的随机选择,即选择非最大行动价值对。这个随机探索的概率被称为epsilon,基于此的策略被称为epsilon - 贪婪策略。与之相对,如果我们一直只选择最大值而不进行探索,那就是贪婪策略。
初始时,我们可能不知道最优的行动价值函数,也就无法得到最优策略。我们需要迭代行动价值函数,找到能带来最大奖励的那个,这就是最优Q函数,记为Q ,进而得到最优策略Pi 。
我们使用神经网络来学习这个Q函数,因为神经网络是通用函数逼近器。一旦我们得到了行动价值函数,智能体就能学习到问题的最优策略。
1.1 Bellman方程
重新定义目标方程,我们可以得到:
[Q(s,a)=r_{t + 1}+\gamma r_{t + 2}+\gamma^2 r_{t + 3}+\cdots]
递归定义这个方程,就得到了Bellman方程:
[Q(s,a)=r+\gamma Q(s’,\pi(s’))]
简单来说,Bellman方程表明每个点的回报等于下一个时间步的估计奖励加上后续状态的折扣奖励。任何策略的值函数都遵循Bellman方程。
1.2 寻找最优Q函数
如果我们有了最优Q函数,就可以通过选择回报最高的行动来找到最优策略。
1.3 深度Q学习
深度Q学习算法使用神经网络来解决Q学习问题,对于具有连续空间的强化学习问题(即不终止的任务)效果很好。
我们之前提到了价值函数(V)和行动价值函数(Q),由于神经网络是通用函数逼近器,我们可以假设它们是带有可训练权重的神经网络。价值函数会输入状态和网络权重,输出当前状态的值。我们需要计算某种误差,将其反向传播到网络中,并使用梯度下降进行训练。我们要将网络的输出(价值函数)与我们认为的最优值进行比较。
根据Bellman方程,我们可以通过考虑下一个状态的值来计算期望Q,通过考虑到目前为止的累积奖励来计算当前Q。使用这两个Q函数之间的均方误差(MSE)作为损失。当误差较大时,研究人员建议使用平均绝对误差代替MSE,这种损失被称为Huber损失。误差公式如下:
[Error (Loss)=R+\gamma \max(Q_{target}) - Q_{current}]
其中,(Q_{target}) 使用贪婪策略,(Q_{current}) 使用epsilon - 贪婪策略。
1.4 训练循环
训练循环的代码如下:
# 初始化权重
w = ... # 随机初始化
# 采用epsilon - 贪婪策略
π = epsilon - greedy
# 遍历所有回合
for i_episode in range(num_episodes):
# 重置环境
env.reset()
# 获取上一帧画面
last_screen = get_screen()
# 获取当前帧画面
current_screen = get_screen()
# 当前状态为两帧画面之差
state = current_screen - last_screen
# 遍历每个时间步
for t in count():
# 根据当前状态选择行动
action = select_action(state)
# 执行行动,获取奖励和完成标志
_, reward, done, _ = env.step(action.item())
reward = torch.tensor([reward], device=device)
# 观察新状态
last_screen = current_screen
current_screen = get_screen()
if not done:
next_state = current_screen - last_screen
else:
next_state = None
# 将转移存储到记忆中
memory.push(state, action, next_state, reward)
# 转移到下一个状态
state = next_state
# 进行一次优化步骤
optimize_model()
if done:
break
# 每TARGET_UPDATE个回合更新一次目标网络
if i_episode % TARGET_UPDATE == 0:
target_net.load_state_dict(policy_net.state_dict())
env.close()
这里使用epsilon - 贪婪策略选择行动并更新策略,这是一种在线策略算法。在线策略算法学习速度快,收敛迅速,但学习的策略和决策策略紧密相关。与之相对的是离线策略算法,Q学习就是一种离线策略算法,它有两个策略:用于推断行动的epsilon - 贪婪策略(策略网络)和用于更新的贪婪策略(目标网络)。我们会定期将策略网络的权重复制到目标网络,以避免追逐一个移动的目标。
1.5 经验回放
为了改进算法,我们可以添加一个有限的经验记忆库,每个事务是一个元组,包含状态、采取的行动、下一个状态和奖励。
from collections import namedtuple
Transition = namedtuple('Transition',
('state', 'action', 'next_state', 'reward'))
class ReplayMemory(object):
def __init__(self, capacity):
self.capacity = capacity
self.memory = []
self.position = 0
def push(self, *args):
if len(self.memory) < self.capacity:
self.memory.append(None)
self.memory[self.position] = Transition(*args)
self.position = (self.position + 1) % self.capacity
def sample(self, batch_size):
return random.sample(self.memory, batch_size)
def __len__(self):
return len(self.memory)
memory = ReplayMemory(10000)
我们会在优化模型时随机采样一些经验进行学习。
1.6 Gym环境
我们使用OpenAI的Gym来获取环境参数,这里以训练一个平衡杆为例。
import gym
import torch
env = gym.make('CartPole-v0').unwrapped
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
在这个环境中,每个观察或状态有四个值:位置、速度、杆的角度和杆尖的速度,可采取的行动是向左或向右移动。
1.7 获取屏幕函数
import numpy as np
import torchvision.transforms as T
from PIL import Image
screen_width = 600
def get_screen():
screen = env.render(mode='rgb_array').transpose((2, 0, 1))
screen = screen[:, 160:320]
world_width = env.x_threshold * 2
scale = screen_width / world_width
cart_location = int(env.state[0] * scale + screen_width / 2.0)
view_width = 320
if cart_location < view_width // 2:
slice_range = slice(view_width)
elif cart_location > (screen_width - view_width // 2):
slice_range = slice(-view_width, None)
else:
slice_range = slice(cart_location - view_width // 2,
cart_location + view_width // 2)
screen = screen[:, :, slice_range]
screen = np.ascontiguousarray(screen, dtype=np.float32) / 255
screen = torch.from_numpy(screen)
resize = T.Compose([T.ToPILImage(),
T.Resize(40, interpolation=Image.CUBIC),
T.ToTensor()])
return resize(screen).unsqueeze(0).to(device)
这个函数会从环境中获取屏幕,裁剪出以平衡杆为中心的正方形图像,将其转换为张量,进行一些变换,添加一个维度并返回。
1.8 定义网络
import torch.nn as nn
import torch.nn.functional as F
class DQN(nn.Module):
def __init__(self):
super(DQN, self).__init__()
self.conv1 = nn.Conv2d(3, 16, kernel_size=5, stride=2)
self.bn1 = nn.BatchNorm2d(16)
self.conv2 = nn.Conv2d(16, 32, kernel_size=5, stride=2)
self.bn2 = nn.BatchNorm2d(32)
self.conv3 = nn.Conv2d(32, 32, kernel_size=5, stride=2)
self.bn3 = nn.BatchNorm2d(32)
self.head = nn.Linear(448, 2)
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = F.relu(self.bn2(self.conv2(x)))
x = F.relu(self.bn3(self.conv3(x)))
return self.head(x.view(x.size(0), -1))
policy_net = DQN().to(device)
target_net = DQN().to(device)
target_net.load_state_dict(policy_net.state_dict())
target_net.eval()
我们定义了两个网络:策略网络和目标网络。将策略网络的权重复制到目标网络,目标网络处于评估模式,这样在反向传播时不会更新其权重。我们在每个步骤推断策略网络,但会定期更新目标网络。
1.9 选择行动函数
import math
import random
EPS_START = 0.9
EPS_END = 0.05
EPS_DECAY = 200
steps_done = 0
def select_action(state):
global steps_done
eps_threshold = EPS_END + (EPS_START - EPS_END) * \
math.exp(-1. * steps_done / EPS_DECAY)
steps_done += 1
sample = random.random()
if sample > eps_threshold:
with torch.no_grad():
return policy_net(state).max(1)[1].view(1, 1)
else:
return torch.tensor([[random.randrange(2)]],
device=device, dtype=torch.long)
这个函数使用epsilon - 贪婪策略选择行动,在一定比例的时间内从策略网络推断行动,但也有eps_threshold的概率随机选择行动。
1.10 训练循环
from itertools import count
import torch.optim as optim
num_episodes = 20
TARGET_UPDATE = 5
for i_episode in range(num_episodes):
env.reset()
last_screen = get_screen()
current_screen = get_screen()
state = current_screen - last_screen
for t in count():
action = select_action(state)
_, reward, done, _ = env.step(action.item())
reward = torch.tensor([reward], device=device)
last_screen = current_screen
current_screen = get_screen()
if not done:
next_state = current_screen - last_screen
else:
next_state = None
memory.push(state, action, next_state, reward)
state = next_state
optimize_model()
if done:
break
if i_episode % TARGET_UPDATE == 0:
target_net.load_state_dict(policy_net.state_dict())
env.close()
在每个回合中,我们重置环境,获取当前状态,选择行动,执行行动并获取奖励和新状态,将转移存储到记忆中,然后进行优化。每5个回合更新一次目标网络。
1.11 优化模型函数
BATCH_SIZE = 64
GAMMA = 0.999
optimizer = optim.RMSprop(policy_net.parameters())
def optimize_model():
if len(memory) < BATCH_SIZE:
return
transitions = memory.sample(BATCH_SIZE)
batch = Transition(*zip(*transitions))
state_batch = torch.cat(batch.state)
action_batch = torch.cat(batch.action)
state_values = policy_net(state_batch)
state_action_values = state_values.gather(1, action_batch)
non_final_mask = torch.tensor(tuple(map(
lambda s: s is not None,
batch.next_state)),
device=device,
dtype=torch.uint8)
non_final_next_states = torch.cat([s for s in batch.next_state if s is not None])
next_state_values = torch.zeros(BATCH_SIZE, device=device)
next_state_values[non_final_mask] = target_net(non_final_next_states).max(1)[0].detach()
reward_batch = torch.cat(batch.reward)
expected_state_action_values = (next_state_values * GAMMA) + reward_batch
loss = F.smooth_l1_loss(state_action_values, expected_state_action_values.unsqueeze(1))
optimizer.zero_grad()
loss.backward()
for param in policy_net.parameters():
param.grad.data.clamp_(-1, 1)
optimizer.step()
这个函数是优化的核心部分。我们从记忆库中采样经验,将状态、行动和奖励转换为批次,通过策略网络获取状态值,进而得到实际Q函数。然后,我们创建一个掩码来区分非最终状态和最终状态,通过目标网络计算期望Q函数,计算损失并将误差反向传播到策略网络。我们还使用梯度裁剪来确保梯度不会过大。
1.12 训练方式对比
训练神经网络需要一些时间,因为过程中需要渲染每一帧并计算误差。我们也可以采用更简单的方法,直接使用速度和位置来构建损失函数,这样训练时间会更短,因为不需要渲染每一帧,直接从env.state获取输入即可。
1.13 算法改进方向
该算法有很多改进方向,例如为智能体添加想象力,使其能更好地探索,在脑海中想象行动并做出更好的预测。
1.14 总结
我们学习了无监督学习中的一个全新领域:强化学习。我们了解了如何为强化学习问题构建模型,并训练了一个模型,使其能够根据环境提供的一些测量值学习如何平衡一个平衡杆。这些知识可以应用于教机器人走路、驾驶汽车和玩游戏等,是深度学习较为实际的应用之一。
1.15 流程图
graph TD;
A[初始化环境和网络] --> B[开始回合];
B --> C[获取当前状态];
C --> D[选择行动];
D --> E[执行行动并获取奖励和新状态];
E --> F[存储转移到记忆中];
F --> G[优化模型];
G --> H{回合是否结束};
H -- 否 --> C;
H -- 是 --> I{是否更新目标网络};
I -- 是 --> J[更新目标网络权重];
I -- 否 --> K[结束当前回合];
K --> B;
1.16 表格:主要参数说明
| 参数 | 说明 |
|---|---|
| EPS_START | epsilon的起始值 |
| EPS_END | epsilon的最终值 |
| EPS_DECAY | epsilon的衰减率 |
| BATCH_SIZE | 批次大小 |
| GAMMA | 折扣因子 |
| TARGET_UPDATE | 目标网络更新周期 |
| num_episodes | 回合数 |
2. 核心概念总结
2.1 策略类型
在强化学习中,策略的选择对智能体的学习和决策起着关键作用。以下是两种主要策略的详细对比:
| 策略类型 | 描述 | 优点 | 缺点 |
| ---- | ---- | ---- | ---- |
| epsilon - 贪婪策略 | 大部分时间选择最大价值的行动,但保留小概率随机选择非最大行动价值对,随机探索概率为epsilon | 提高模型的探索能力,避免陷入局部最优 | 探索过程可能引入噪声,影响学习效率 |
| 贪婪策略 | 始终只选择最大值,不进行探索 | 决策简单直接,在某些已知环境中可快速收敛到局部最优 | 容易陷入局部最优,无法充分探索环境 |
2.2 网络结构
我们定义了两个重要的神经网络:策略网络(policy_net)和目标网络(target_net)。它们的结构和作用如下:
import torch.nn as nn
import torch.nn.functional as F
class DQN(nn.Module):
def __init__(self):
super(DQN, self).__init__()
self.conv1 = nn.Conv2d(3, 16, kernel_size=5, stride=2)
self.bn1 = nn.BatchNorm2d(16)
self.conv2 = nn.Conv2d(16, 32, kernel_size=5, stride=2)
self.bn2 = nn.BatchNorm2d(32)
self.conv3 = nn.Conv2d(32, 32, kernel_size=5, stride=2)
self.bn3 = nn.BatchNorm2d(32)
self.head = nn.Linear(448, 2)
def forward(self, x):
x = F.relu(self.bn1(self.conv1(x)))
x = F.relu(self.bn2(self.conv2(x)))
x = F.relu(self.bn3(self.conv3(x)))
return self.head(x.view(x.size(0), -1))
policy_net = DQN().to(device)
target_net = DQN().to(device)
target_net.load_state_dict(policy_net.state_dict())
target_net.eval()
- 策略网络(policy_net) :用于实时推断智能体的行动,在每个步骤中都进行使用。
- 目标网络(target_net) :定期从策略网络复制权重,处于评估模式,在反向传播时不更新其权重,用于计算期望Q值,避免目标值的频繁变动,使学习过程更加稳定。
2.3 关键函数及作用
以下是代码中几个关键函数的详细说明:
| 函数名 | 作用 |
| ---- | ---- |
|
select_action(state)
| 使用epsilon - 贪婪策略选择行动,根据随机数和eps_threshold决定是从策略网络推断行动还是随机选择行动 |
|
optimize_model()
| 优化模型的核心函数,从记忆库中采样经验,计算实际Q值和期望Q值,计算损失并反向传播误差到策略网络,同时使用梯度裁剪确保梯度稳定 |
|
get_screen()
| 从环境中获取屏幕图像,裁剪出以平衡杆为中心的正方形图像,转换为张量并进行必要的变换,用于表示当前状态 |
2.4 训练流程
整个训练过程可以用以下流程图表示:
graph LR;
A[初始化环境和网络] --> B[初始化参数和变量];
B --> C[开始回合循环];
C --> D[重置环境并获取初始状态];
D --> E[开始时间步循环];
E --> F[选择行动];
F --> G[执行行动并获取奖励和新状态];
G --> H[存储转移到记忆中];
H --> I[优化模型];
I --> J{回合是否结束};
J -- 否 --> E;
J -- 是 --> K{是否更新目标网络};
K -- 是 --> L[更新目标网络权重];
K -- 否 --> M[结束当前回合];
M --> C;
具体步骤如下:
1.
初始化
:初始化环境、网络(策略网络和目标网络)、参数(如epsilon、批次大小、折扣因子等)和变量(如步数计数器)。
2.
回合循环
:开始多个回合的训练,每个回合代表一次完整的任务执行。
3.
重置环境
:在每个回合开始时,重置环境并获取初始状态。
4.
时间步循环
:在每个回合内,按时间步进行循环,直到回合结束。
5.
选择行动
:使用
select_action
函数根据当前状态选择行动。
6.
执行行动
:执行选择的行动,从环境中获取奖励和新状态。
7.
存储转移
:将当前状态、行动、下一个状态和奖励存储到记忆库中。
8.
优化模型
:调用
optimize_model
函数进行模型优化。
9.
回合结束判断
:判断当前回合是否结束,如果未结束则继续时间步循环;如果结束,则判断是否需要更新目标网络。
10.
更新目标网络
:根据设定的更新周期,决定是否更新目标网络的权重。
2.5 算法改进思路
虽然当前的Q学习算法已经取得了不错的效果,但仍有一些改进的方向:
-
添加想象力
:为智能体添加想象力模块,使其能够在脑海中模拟未来的行动和结果,从而更好地探索环境,做出更优的决策。例如,可以使用生成模型来预测未来可能的状态和奖励。
-
多策略融合
:结合多种不同的策略,根据环境的不同阶段和状态动态选择合适的策略,以平衡探索和利用的关系。
-
自适应参数调整
:根据学习过程中的反馈动态调整参数,如epsilon的衰减率、折扣因子等,使算法能够更好地适应不同的环境和任务。
2.6 应用拓展
强化学习的应用非常广泛,除了平衡杆问题,还可以应用于以下领域:
-
机器人控制
:教机器人完成各种任务,如走路、抓取物体、导航等。
-
自动驾驶
:训练自动驾驶汽车在复杂的交通环境中做出决策,如加速、减速、转弯等。
-
游戏开发
:使游戏智能体能够学习并掌握游戏策略,如玩棋类游戏、电子竞技游戏等。
通过不断地改进和优化算法,强化学习在更多领域的应用前景将更加广阔。
超级会员免费看
2860

被折叠的 条评论
为什么被折叠?



