强化学习库torchrl介绍和强化学习五问

其实很多时候我们可能会有以下的困惑

  1. PPO和DDPG的区别, 可以同时使用PPO和DDPG吗?
  2. 哪些算法可以使用importance sampling?
  3. 哪些算法可以使用replay buffer?
  4. TD0, TD1, TD lambda, gae的区别是什么,哪些算法可以使用?
  5. on-policy和off-policy的区别是什么,如何判断是否是on-policy?

torchrl是一个基于pytorch的强化学习库,我发现根据torchrl的结构可以对强化学习知识点有更加深入的理解,相信在学习了torchrl之后,我们之前的疑惑可以获得解决。下面将我的理解记录如下:

torch rl的输入和输出都是一个叫做tensordict的类,可以理解为本来python函数的输入是一个数组,或者字典,现在torch rl直接强制要求输入是一个字典。使用这个方法增加了灵活性,可以使得模块可以更好的重用。

一个完整的强化学习包含了不同部分:

名称解释torch rl基类是否必须
环境用来采集数据EnvBase
actor用来生成策略Actor
critic估计状态的价值ValueOperator
loss function计算actor损失和critic损失LossModule
replay buffer存储从环境中采样的数据,类似于一个datasetReplayBuffer
collectoractor从环境中收集数据的过程抽象为一个类DataCollectorBase
trainer整个训练过程的抽象Trainer

下面是使用torchrl实现了PPO算法:

import torch
from tensordict.nn import TensorDictModule
from tensordict.nn.distributions import NormalParamExtractor
from torch import nn

from torchrl.collectors import SyncDataCollector
from torchrl.data.replay_buffers import TensorDictReplayBuffer, \
    LazyTensorStorage, SamplerWithoutReplacement
from torchrl.envs.libs.gym import GymEnv
from torchrl.modules import ProbabilisticActor, ValueOperator, TanhNormal
from torchrl.objectives import ClipPPOLoss
from torchrl.objectives.value import GAE

env = GymEnv("Pendulum-v1")
model = TensorDictModule(
    nn.Sequential(
        nn.Linear(3, 128), nn.Tanh(),
        nn.Linear(128, 128), nn.Tanh(),
        nn.Linear(128, 128), nn.Tanh(),
        nn.Linear(128, 2),
        NormalParamExtractor()
    ),
    in_keys=["observation"],
    out_keys=["loc", "scale"]
)
critic = ValueOperator(
    nn.Sequential(
        nn.Linear(3, 128), nn.Tanh(),
        nn.Linear(128, 128), nn.Tanh(),
        nn.Linear(128, 128), nn.Tanh(),
        nn.Linear(128, 1),
    ),
    in_keys=["observation"],
)
actor = ProbabilisticActor(
    model,
    in_keys=["loc", "scale"],
    distribution_class=TanhNormal,
    distribution_kwargs={"min": -1.0, "max": 1.0},
    return_log_prob=True
    )
buffer = TensorDictReplayBuffer(
    LazyTensorStorage(1000),
    SamplerWithoutReplacement()
    )
collector = SyncDataCollector(
    env,
    actor,
    frames_per_batch=1000,
    total_frames=1_000_000
    )
loss_fn = ClipPPOLoss(actor, critic, gamma=0.99)
optim = torch.optim.Adam(loss_fn.parameters(), lr=2e-4)
adv_fn = GAE(value_network=critic, gamma=0.99, lmbda=0.95, average_gae=True)
for data in collector:  # collect data
    for epoch in range(10):
        adv_fn(data)  # compute advantage
        buffer.extend(data.view(-1))
        for i in range(20):  # consume data
            sample = buffer.sample(50)  # mini-batch
            loss_vals = loss_fn(sample)
            loss_val = sum(
                value for key, value in loss_vals.items() if
                key.startswith("loss")
                )
            loss_val.backward()
            optim.step()
            optim.zero_grad()
    print(f"avg reward: {data['next', 'reward'].mean().item(): 4.4f}")

可以看出,整个过程就是组合了不同的部分,使得一个复杂的强化学习过程变得模块化。唯一小小的区别在于这里没有使用trainer,而是手动迭代了。

下面对每个部分进行解释:

1 EnvBase

具体的位置: https://github.dev/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/envs/common.py
可以使用torchrl自带的环境,如果是一个自己实现的环境:

  1. 继承EnvBase类
  2. 实现_step, _reset方法
  3. 指定env的输入输出的key和形状:
    名称解释
    action_spec动作空间的shape
    reward_spec奖励的shape
    done_spec一个trajectory是否结束的shape
    observation_spec观测空间的shape

注意每对xxx_spec赋值一次都会将key和value收集到full_xxx_spec这个属性中,也可以直接对full_xxx_spec赋值,这样就支持了一个环境有多个reward,多个action等等,用于multi actor非常有用

如果希望对环境的输入输出进行映射,那么可以用Transform这个类,具体见:https://github.dev/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/envs/common.py

2 Actor

具体见:https://github.dev/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/envs/common.py

Actor类主要有两个可以使用的:

  • Actor: 输出的是一个值
  • ProbabilisticActor: 输出的是一个概率分布
  • QValueActor: 使用DQN的时候用

Actor和ProbabilisticActor的区别:

Actor网络输出是一个动作,而ProbabilisticActor输出是动作分布的参数,它们对应了确定和随机两种情况。确定的动作目标函数中对s,a的分布没有要求,而随机动作中目标函数对s,a的分布要求是符合当前策略的分布。因此当使用随机动作时,之前策略采集的数据无法再次使用(策略在迭代过程中被修改了),此时需要使用importace sampling技术,修改reward的权值。

另外Actor只能用于连续的动作空间,而ProbabilisticActor可以用于连续和离散的动作空间。对于离散的空间,只需要将输出的n个维度作为n个动作的概率,对于连续的空间,将输入作为分布的参数,然后对分布进行采样。

比较项ActorProbabilisticActor
actor网络输出值动作本身动作概率分布的参数
对应神经网络算法DDPG其他绝大多数算法
目标函数对s,a分布的要求s,a分布符合当前的策略
是否需要importance sampling
策略类型off-policyon-policy
动作空间是否连续都行

一般建议使用的是ProbabilisticActor

3 ValueOperator

注意ValueOperator选择两种情况,

  • 如果in_key中有action,那么输出的是state-action value
  • 如果in_key中只有observation,那么输出的是state value
比较项in_key 中有actionin_key中没有action
输出state_valuestate_action_value
能否使用gae不能
动作是否需要确定随机确定(DDPG)
  1. gae计算需要使用state value,如果说没有state value,就无法计算gae
  2. 确定动作的目标函数需要state_action_value,而随机动作的目标函数需要state_value

4 ActorValueOperator

torch rl支持分别设置actor和critic,即Actor类和ValueOperator类,也支持通过一个ActorCriticOperator同时设置actor和critic。
具体看文档

5 LossModule: loss_value

具体见 https://github.com/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/objectives/value/functional.py#L117

对于value loss来说,需要指定估计目标价值的方法,可以理解为value net需要拟合到一个标签,或者ground truth,而强化学习中这个ground truth是未知的,需要根据当前采样+当前value net的输出进行计算,这些计算方法主要区别在于使用多少步迭代的数据进行估计。比如采样的数据是
a 1 , s 1 , r 1 , a 2 , s 2 , r 2 , . . . . , a n , s n , r n a_1, s_1, r_1, a_2, s_2, r_2, ...., a_n, s_n, r_n a1,s1,r1,a2,s2,r2,....,an,sn,rn
假设在sn之后到达了结束状态,那么算法计算 s 1 s_1 s1状态价值ground truth的方法是:

算法随机动作计算方法确定动作计算方法
TD(0) r 1 + c r i t i c ( s 2 ) r_1 + critic(s_2) r1+critic(s2) r 1 + c r i t i c ( s 2 , a 2 ) r_1 + critic(s_2, a_2) r1+critic(s2,a2)
TD(1) r 1 + r 2 + . . . + c r i t i c ( s n ) r_1 + r_2 + ... + critic(s_n) r1+r2+...+critic(sn) r 1 + r 2 + . . . + c r i t i c ( s n , a n ) r_1 + r_2 + ... + critic(s_n, a_n) r1+r2+...+critic(sn,an)
TD(lambda)考虑了TD(0), … TD(1)的加权求和考虑了TD(0), … TD(1)的加权求和
gae r 1 − c r i t i c ( s 1 ) + c r i t i c ( s 2 ) r_1 - critic(s1)+critic(s_2) r1critic(s1)+critic(s2)无法计算

简单来说:

  • TD0是取一步进行计算,TD1是取整个trajectory进行计算,
  • TD(lambda)是TD(0), … TD(1)这个序列的加权平均。具体的更新方法如下:
    G t ( n ) = γ r 1 + γ 2 r 2 + . . . γ n − 1 r n − 1 + γ n V ( S t + n ) G t λ = ( 1 − λ ) ∑ n = 1 ∞ λ n − 1 G t ( n ) G_t^{(n)} = \gamma r_1 +\gamma^2 r_2 + ...\gamma^{n-1} r_{n-1} + \gamma^n V(S_{t+n})\\G_t^\lambda = (1-\lambda)\sum_{n=1}^\infin\lambda^{n-1}G_t^{(n)} Gt(n)=γr1+γ2r2+...γn1rn1+γnV(St+n)Gtλ=(1λ)n=1λn1Gt(n)
  • advantage:优势函数是估计的action value。将原本的action value减去了state value,这样advatage > 0说明动作高于平均值, advantage < 0说明动作低于平均值,减小了方差。
    也就是说advantage可以和TD(0)到TD(1)或者TD(lambda)结合,就是这几个值减去state value。
  • GAE是将不同步的advantage进行加权求和了

另外在reward之前会有一个系数,具体看代码实现:

# TD0
advantage = reward + gamma * not_terminated * next_state_value

# TD1
gamma = [g1, g2, g3, g4]
value = [v1, v2, v3, v4]
return = [
  v1 + g1 v2 + g1 g2 v3 + g1 g2 g3 v4,
  v2 + g2 v3 + g2 g3 v4,
  v3 + g3 v4,
  v4,
]

使用两个网络/暂缓更新机制:

这是为了防止value net或者action net更新太快导致模型不稳定。这个不是必须的,可以酌情使用。在torchrl的损失函数中专门有个参数:delay_actor和delay_value可以控制是否需要暂缓更新。

具体请看https://github.com/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/objectives/ddpg.py

delay_actor (bool, optional): whether to separate the target actor networks from the actor networks used for
            data collection. Default is ``False``.
delay_value (bool, optional): whether to separate the target value networks from the value networks used for
            data collection. Default is ``True``.

6 LossModule: loss_actor

实际使用时,不同的算法选择不同的损失函数

损失函数解释
A2CLoss随机动作损失函数是:-action_prop * state_value
DDPGLoss确定动作损失函数是: -state_action_value
ClipPPOLoss通过裁剪actor loss,减小策略更新的速度,使得策略更加稳定,公式为:
loss = -min( weight * advantage, min(max(weight, 1-eps), 1+eps) * advantage)
KLPENPPOLoss通过裁剪actor loss,减小策略更新的速度,使得策略更加稳定,公式为:
loss = -min( weight * advantage, min(max(weight, 1-eps), 1+eps) * advantage)

还有些其他的损失,比如SAC,TD3等等,这里先不说了,之后会有相关算法的完整总结。

7 ReplayBuffer

类似与dataset,对于off-policy的直接用,对于on-policy的,需要确保使用了importance sampling再用。很多算法在loss中就内置了importance sampling,所以说基本上都可以用。

8 Collector

用来采集数据的,采集好的数据放入replay buffer,可以用来训练

9 Trainer

https://github.com/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/trainers/trainers.py

相当于一个外循环套着一个内循环:在外循环里面收集数据,然后填充replay buffer。如果收集到足够的数据,就更新若干次。

def train(self):
     if self.progress_bar:
         self._pbar = tqdm(total=self.total_frames)
         self._pbar_str = {}

     for batch in self.collector:
         batch = self._process_batch_hook(batch)
         current_frames = (
             batch.get(("collector", "mask"), torch.tensor(batch.numel()))
             .sum()
             .item()
             * self.frame_skip
         )
         self.collected_frames += current_frames
         self._pre_steps_log_hook(batch)

         if self.collected_frames > self.collector.init_random_frames:
             self.optim_steps(batch)
         self._post_steps_hook()

         self._post_steps_log_hook(batch)

         if self.progress_bar:
             self._pbar.update(current_frames)
             self._pbar_description()

         if self.collected_frames >= self.total_frames:
             self.save_trainer(force_save=True)
             break
         self.save_trainer()

     self.collector.shutdown()

optim_steps的内部其实也是一个循环,在这个循环中更新参数

问题一: PPO和DDPG的区别, 可以同时使用PPO和DDPG吗?

PPO修改的是critic loss,对critic网络的loss进行了裁剪,主要有两种方法,对应了两个PPO loss,一个方法是裁剪loss 函数,另一个方法是在损失函数中加入KL散度进行调整,两种方法都是希望损失函数不要变化太大,从而更新太多引起模型不稳定。
DDPG修改的是actor loss,将随机动作变为确定动作。

可以,PPO裁剪的是critic的损失,而DDPG是修改为确定的动作,如果希望PPO输出的是一个确定的动作,那么就是PPO和DDPG结合了。结合之后的算法变为了off policy的算法

问题二:哪些算法可以使用importance sampling?

只有on-policy算法需要,比如PPO, A2C之类的,对于DDPG,DQN是不需要的
换句话说,输出的是一个确定的策略,而不是一个分布,那么不需要,否则需要。

问题三:哪些算法可以使用replay buffer?

输出确定策略的都能用,输出随机策略的,如果用了Importance sampling也能用。

问题四:TD0, TD1, TD lambda的区别是什么,哪些算法可以使用?

TD1, TD0, TD lambda都能用,而gae需要能算state value的方法才能用,一般来说只有输出动作分布的才能算state value,因此gae只能在输出随机分布的算法中使用,对于DDPG无法使用,因为无法计算状态的价值,只能获得状态动作对的价值。因此DDPG无法使用,而PPO, A2C是可以使用的

从torchrl的实现中也可以看出,DDPG是不支持gae的
https://github.com/pytorch/rl/blob/bf264e0e24971fc05ec42b571de7b8df84043a51/torchrl/objectives/ddpg.py

if value_type == ValueEstimators.TD1:
	self._value_estimator = TD1Estimator(value_network=self.actor_critic, **hp)
elif value_type == ValueEstimators.TD0:
    self._value_estimator = TD0Estimator(value_network=self.actor_critic, **hp)
elif value_type == ValueEstimators.GAE:
    raise NotImplementedError(
        f"Value type {value_type} it not implemented for loss {type(self)}."
    )
elif value_type == ValueEstimators.TDLambda:
    self._value_estimator = TDLambdaEstimator(
        value_network=self.actor_critic, **hp
    )
else:
    raise NotImplementedError(f"Unknown value type {value_type}")

问题五:on-policy和off-policy的区别是什么,如何判断是否是on-policy?

最准确的回答是:看actor或者critic的损失函数,如果损失函数中有对s,a的分布有要求,那么就是on-policy的,否则是off-policy的

一般来说,如果使用了输出随机动作,那么actor的损失函数大概率是对s,a分布有要求的,因此是on-policy的,如果使用了输出确定动作的,比如DDPG,那么actor损失函数大概率是对s,a分布无要求的,因此是off-policy的。

另外不要根据动作是否连续进行判断,因为有时候输出的是高斯分布的均值和方差,然后在这个高斯分布中采样,这种虽然获得的也是连续的动作空间,但是输出的仍然是一个分布,因此是一个on-policy的。

有些人认为,对于DQN来说,根本没有actor函数,直接通过critic选择策略,因此action的分布永远是固定的,也没有这个问题。上面这个看法是错误的,DQN的action分布是会改变的,选择某个动作的概率有时候是0,有时候是1,怎么能说概率分布不变呢。DQN是off-policy的原因是DQN的损失函数中不对s,a的分布做要求,因此s,a分布改变也没有关系。

确定策略和随机策略对比

对比随机策略确定策略
代表算法PPO, A2C, …DDPG
动作的含义离散的分布,或者连续分布的参数动作值本身
Actor的in_keyobservationaction, observation
Actor的out_keystate_valueaction_state_value
可以使用的value估计方法TD0, TD1, TDlambda, gaeTD0, TD1,TDlambda
是否需要importance samping
是否可以直接使用replay buffer
actor 目标函数-state_value * action_prob-state_action_value
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
深度强化学习 深度强化学习是一种新兴的机器学习领域,它将深度学习和强化学习结合在一起,以实现更高级别的自主决策。深度学习是一种机器学习技术,它利用神经网络来模拟人类大脑的工作方式,从而实现对数据的复杂处理和分析。强化学习是一种机器学习技术,它通过试错过程来学习行为,以最大化预期的奖励。深度强化学习结合了这两种技术,使得机器可以更好地理解环境和采取正确的行动。 深度强化学习的主要优点是,它可以处理大量的非结构化数据,并且可以从数据中提取出有意义的特征。此外,深度强化学习可以通过对数据的自我学习来不断改进自己的性能,从而在复杂的环境中实现更高水平的自主决策。 分层强化学习 分层强化学习是一种强化学习技术,它利用分层结构来实现更高级别的自主决策。在传统的强化学习中,智能体需要在一个非常大的状态空间中进行决策,这往往会导致计算量很大,并且容易受到局部最优解的影响。分层强化学习通过将决策过程分解成多个较小的子过程,从而降低决策空间的复杂度,并且可以更好地应对复杂的环境。 分层强化学习的主要优点是,它可以通过分解决策过程来降低计算量,并且可以防止智能体被困在局部最优解中。此外,分层强化学习还可以通过学习更高层次的决策规则来实现更高水平的自主决策。 总结 深度强化学习和分层强化学习是两种不同的强化学习技术,它们分别利用深度学习和分层结构来实现更高级别的自主决策。深度强化学习可以处理大量的非结构化数据,并且可以通过自我学习来不断改进自己的性能。分层强化学习可以通过分解决策过程来降低计算量,并且可以防止智能体被困在局部最优解中。这两种技术都可以在复杂的环境中实现更高水平的自主决策,并且都具有很大的潜力来推动人工智能的发展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值