Rllib学习[2] --env定义 + env rollout

本文详细介绍了如何在RLlib中创建、注册和管理自定义环境,包括gym Env的设置,环境调用流程,多环境构建,以及Sample模块的使用。特别关注了RolloutWorker和SampleBatch在多智能体和分层训练中的作用。


本文讲述rllib中环境如何搭建,以及环境中sample模块。

env

目前还没有涉及到VectorEnv,主要使用gym.Env 以及 基于gym.Env创建的子类环境。
在这里插入图片描述
为了在ray 和 rllib中能够使用 自己创建的环境,需要注意:
初始化中含有 observation_space 和 action_space 用于rllib后续policy中建立神经网络的输入。

基本框架

# env需要按照 gym.env 进行设置
class MyEnv(gym.Env):
    def __init__(self, env_config):  # 此处的变量请只使用 dict env_config 用于定义所有的参数
    								# 除非使用register建立,同时需要在 env_creator中合理对应
        self.action_space = <gym.Space>
        self.observation_space = <gym.Space>
    def reset(self):
        return <obs>
    def step(self, action):
        return <obs>, <reward: float>, <done: bool>, <info: dict>

gym.observation_space 配置

环境的调用过程

import gym, ray
from ray.rllib.algorithms import ppo

ray.init()
algo = ppo.PPOTrainer(env=MyEnv, config={
    "env_config": {},  # config to pass to env class
})

环境的注册

我们可以使用register(注册),将我们自己定义的环境设置成 rllib可以识别的 环境string,然后可以直接进行调用。
需要注意的是: gym中的registry和 ray不完全兼容。因此请使用ray中的resgister进行注册。

from ray.tune.registry import register_env

def env_creator(env_config):   # 此处的 env_config对应 我们在建立trainer时传入的dict env_config
    return MyEnv(...)  # return an env instance

register_env("my_env", env_creator) # 此处传入了 环境的名称 | 环境的实例调用函数
algo = ppo.PPO(env="my_env",config={
									"env_config":{} # 传入 env的__init__中
})

多环境的建立

有时候一个worker需要对多个环境进行学习,因此我们在定义环境的时候 需要同时定义几个环境。

对于 num_envs_per_worker >0 的情况, 每一个worker会对应有多个环境,因此需要通过根据env_config.worker_indexenv_config.vector_index 来得到 worker的 id和 env id

class MultiEnv(gym.Env):
    def __init__(self, env_config):
        # pick actual env based on worker and env indexes
        self.env = gym.make(
            choose_env_for(env_config.worker_index, env_config.vector_index))
        self.action_space = self.env.action_space
        self.observation_space = self.env.observation_space
    def reset(self):
        return self.env.reset()
    def step(self, action):
        return self.env.step(action)

register_env("multienv", lambda config: MultiEnv(config))

注意事项

如果我们需要在环境中进行log,我们需要将log的设置也设置在环境中,然后会在worker内运行。
和 callback中的log如何结合?

sacling guide

多智能体+分层

trainer sampling

worker是包含于trainer当中的, workerset是 一个 local_worker 和 多个(num_workers) remote_worker,每一个worker都是一个 RolloutWorker。而每一个worker会进行一个或多个的env训练。 每个worker生成的数据都会形成 SampleBatch 的 类,这个类包含了训练需要的数据。不同worker生成的SampleBatch都可以整合。
在这里插入图片描述

WorkerSet

在 trainer类中 会有 trainer.workers,表示所有的RollWorker 形成的 WorkSet。 想要调用每一个worker时, local worker则是 trainer.workers.local_worker(), 对于remote_worker 则是 trainer.workers.remote_workers() 将返回 一个list,包含所有的 remote_workers。
对于evaluation,会生成单独的WorkerSet: self.evaluation_workers.

import ray.rllib.evaluation.worker_set.WorkerSet
class ray.rllib.evaluation.worker_set.WorkerSet(*, env_creator: Optional[Callable[[ray.rllib.env.env_context.EnvContext], Optional[Any]]] = None, validate_env: Optional[Callable[[Any], None]] = None, policy_class: Optional[Type[ray.rllib.policy.policy.Policy]] = None, trainer_config: Optional[dict] = None, num_workers: int = 0, local_worker: bool = True, logdir: Optional[str] = None, _setup: bool = True):

def sync_weights(policies: Optional[List[str]] = None, from_worker: Optional[ray.rllib.evaluation.rollout_worker.RolloutWorker] = None, global_vars: Optional[Dict[str, Any]] = None)-> None 
# policies 表示更新的policyID ; from_worker 表示更新时 policy的来源,默认是local_worker

def add_workers(num_workers: int)-> None
# 在WorkerSet中 增加 remote_workers
def foreach_worker(func) 
# func(worker) 以WorkerSet中的每一个worker为参数,生成值。 foreach_worker会对每一个worker进行运算,返回 所有的值

def foreach_worker_with_index(func)-> list
# func(worker,index) 将 worker 和 对应的 index 作为输入进行操作。

def foreach_policy(func) -> list
# func(policy,policy_id) 以每个worker中的policy和id为参数。 对于多智能体,会出现多个id;单智能体只会有一个id。

RolloutWorker

 # 会包装一个policy实例 和一个环境类来收集经验
class ray.rllib.evaluation.rollout_worker.RolloutWorker(*, 
		env_creator,  # 创建环境的func
		validate_env = None, # 测试env的函数
		policy_spec = Union[type,dict], #单policy:  policy ; 				 
											# 多policy: {"policy1":policy1,"policy2":policy2}
		policy_mapping_fn = Optional[Callable[[Any, Episode], str]], # 多智能体中 将agent与policy对应
		rollout_fragment_length: int , # 每次训练的pari数目
		batch_mode: str, # 默认"truncated_episodes" ; 也可以"complete_episodes"
		episode_horizon: int, # 每个episode的最大步长
		preprocessor_pref: str,  # 是否使用 preprocessor, 默认是deepmind,可以使用rllib
		sample_async : bool, # 是否使用非同步方式收集sample, 如果是的话 收集信息会快,但是会有些 off-policy
		compress_observations: bool, # 是否压缩 obs
		num_envs: int, # 每一个worker 采用的env 多少
		clip_rewards: bool , # 是否截断reward
		normalize_actions: bool, # 是否将action正则化到 指定范围内
		clip_actions: bool, # 是否将动作截断 
		env_config, # 传入到 env_config中
		model_config, # 用于创建 policy model
		policy_config,# 传入policy中。 多智能体中会和policy_mapping一起使用
		worker_index, # 设置当前RollWorker的 index, 对于remote worker 需要设置index>0.
		num_workers, # 用于设置remote worker的数量。 对于单worker时,num_workers应该设置为0
		recreated_worker: bool, # 用于查看 该RollWorker是否是一开始创建的,还是worker fail之后重新创建的
								# 对于一开始创建的worker 和 后创建的worker 只有这一项不同
		record_env: bool, #  是否记录 每个episode的数据和video
		log_dir: str, # log记录的位置, 默认是~/ray_results
		callbacks: class, # 自定义的回调函数
        soft_horizon: bool = False, # 到了horizon 不重置,继续计算reward
        no_done_at_end: bool = False,  # 不到done 不结束
        seed: int = None, # 随机种子
        spaces: Optional[Dict[PolicyID, Tuple[Space, Space]]], 
        # 在没有env创建时,生成policy id到 obs_space,action_space 的 dict映射。 
		disable_env_checking=False, # 用于不检查 env
	)
	def sample(self)  # 会返回 BatchSample,生成需要的sample
	# 生成的具体信息 详见 下节的 BatchSample

	def sample_with_count(self) # 返回 (BatchSample, count_number)  count_number 表示 BatchSample中的pair的数量
	
	def learn_on_batch(samples) # input是BatchSample, 然后更新policy

	def sample_and_learn(expected_batch_size: int, num_sgd_iter: int, sgd_minibatch_size: str, standardize_fields: List[str]) -> Tuple[dict, int]
	# expected_batch_size : batch的大小 ; num_sgd_iter : 迭代的次数; sgd_minibatch_size: 每次更新的mini-batch size ; standardize_fields: samples 中 需要正则化的项 
	# 实际上是调用 policy

	def compute_gradients(samples) # 计算samples对应的gradient
	
	def apply_gradients(grads) # 根据grads 进行更新

	def get_metrics() # 得到对应的metrics
 	def foreach_env(func: Callable[[Any], ray.rllib.utils.typing.T]) → List[ray.rllib.utils.typing.T]
		# func 将会对每一个env进行
		
	def get_policy(policy_id: str = 'default_policy') # 根据policy id得到对应的policy
	
	def save()
	def restore()
	def get_weights() # 得到model的weights
	def set_weights() # 设置model的weights

初始化RolloutWorker

import gym 
from ray.rllib.evaluation.rollout_worker import RolloutWorker
from ray.rllib.agents.pg.pg_tf_policy import PGTFPolicy
worker = RolloutWorker( 
  env_creator=lambda _: gym.make("CartPole-v0"), 
  policy_spec=PGTFPolicy)

SampleBatch

{ 'action_logp': np.ndarray((200,), dtype=float32, min=-0.701, max=-0.685, mean=-0.694),
  'actions': np.ndarray((200,), dtype=int64, min=0.0, max=1.0, mean=0.495),
  'dones': np.ndarray((200,), dtype=bool, min=0.0, max=1.0, mean=0.055),
  'infos': np.ndarray((200,), dtype=object, head={}),
  'new_obs': np.ndarray((200, 4), dtype=float32, min=-2.46, max=2.259, mean=0.018),
  'obs': np.ndarray((200, 4), dtype=float32, min=-2.46, max=2.259, mean=0.016),
  'rewards': np.ndarray((200,), dtype=float32, min=1.0, max=1.0, mean=1.0),
  't': np.ndarray((200,), dtype=int64, min=0.0, max=34.0, mean=9.14)}
class ray.rllib.policy.sample_batch.SampleBatch(*args, **kwargs)
	def agent_steps() -> int  # 返回SampleBatch 中的 步长
	def env_steps() -> int #  和 agent_steps 相同
	def concat_samples() # 输入两个 SampleBatch, 可以将两个concat起来。
e.g. 
import numpy as np
from ray.rllib.policy.sample_batch import SampleBatch
b1 = SampleBatch({"a": np.array([1, 2]), "b": np.array([10, 11])})
b2 = SampleBatch({"a": np.array([3]),  "b": np.array([12])})
print(SampleBatch.concat_samples([b1, b2])) 

	def concat() # 返回一个新的SampleBatch
e.g.
import numpy as np
from ray.rllib.policy.sample_batch import SampleBatch
b1 = SampleBatch({"a": np.array([1, 2])}) 
b2 = SampleBatch({"a": np.array([3, 4, 5])}) 
print(b1.concat(b2)) 

	def copy(shallow: bool = False) → ray.rllib.policy.sample_batch.SampleBatch # 返回一个 浅拷贝/深拷贝的 SampleBatch

	def rows() # 生成一个迭代器,返回每步对应的pair
e.g.
from ray.rllib.policy.sample_batch import SampleBatch
batch = SampleBatch( {"a": [1, 2, 3], "b": [4, 5, 6],"seq_lens": [1, 2]} )
for row in batch.rows(): 
   print(row) 
>>{"a": 1, "b": 4, "seq_lens": 1}
>>{"a": 2, "b": 5, "seq_lens": 1}
>>{"a": 3, "b": 6, "seq_lens": 1}
>
	def shuffle() # 返回一个 被打乱 的 SampleBatch, 但是每个step的值都是对应的
	def split_by_episode() #  将sample结果按照episode if分开

Sampler

将不同worker生成的SampleBatch 进行合并收集
分为SyncSampler 和 AsyncSampler 。 在 WorkerSet中根据需要可以选择。RollWorker.sampler中就是这个类。

PolicyMap

用于多智能体中将policy id对应到成policy实例。

View requirement

RolloutWorker 在存储时 会按照step进行存储。 但是我们在想要将sample传入model中的时候,很多时候,一方面需要处理数据 ;另一方面可能会用到之前的数据。
如果需要处理数据,虽然rllib中有preprocessor,但是Ray建议 使用 wrapper对env进行包装,而不是创建自定义的preprocessor。
如果需要使用之前的数据,那么就需要用到 view requirement. 这是 sampler 和model 之间交流的设定方式。 view requirement 中定义 是否需要之前的数据。 如果需要,那么在model中计算动作compute_action之前,就会将需要的数据作为输入传入。
在这里插入图片描述
ViewRequirement class 包含四个变量:
data_col = “rewards”/"obs"等,用于标识之前数据中需要的数据。
space: gym.Space 说明数据的形式。
shift: int / list[int] 标识使用多会儿的数据。 0 标识这一时刻。 -1表示上一时刻。
used_for_training: bool 一般是True

如果需要,可以在model的 __init__中添加。

<think> 我们正在讨论的是如何利用RLlib的MA-PPO(多智能体近端策略优化)算法采集的样本数据进行离线训练。注意,MA-PPO本身是一个在线算法,但我们可以通过RLlib的离线数据功能将采集的样本保存下来,然后用于其他算法的离线训练,或者用于同一个策略的后续离线训练(例如,使用行为克隆或离线强化学习算法进行微调)。 根据用户的问题,重点在于“使用MA-PPO算法采集的样本数据进行离线训练”。这里有两种理解: 1. 我们使用MA-PPO在环境中运行收集经验,然后将这些经验存储为离线数据集,随后用这个数据集训练一个离线强化学习算法(如CQL、BCQ等)。 2. 或者,我们可能想用这些收集的数据来继续训练MA-PPO策略(例如,在在线训练过程中,我们可能会存储一些过去的经验,然后进行回放,这实际上是经验回放,属于在线训练的一部分,但也可以扩展到离线训练)。 但是,RLlib的MA-PPO算法本身并不直接支持完全的离线训练(即不与环境交互)。然而,我们可以通过以下步骤实现离线训练: 步骤1:使用MA-PPO收集经验数据 我们可以配置MA-PPO算法在训练过程中将经验数据输出到文件中。在RLlib中,这可以通过配置`output`和`output_config`来实现。 步骤2:配置离线数据输入 然后,我们可以使用另一个支持离线训练的算法(例如CQL,或者甚至PPO也可以使用离线数据,但通常PPO是online的,而RLlib中PPO并不直接支持离线训练)来读取这些数据并训练。 但是,请注意,PPO是一个on-policy算法,它通常需要当前策略产生的数据。如果我们要用过去的数据训练PPO,就需要使用重要性采样(importance sampling)来纠正分布差异。在RLlib中,PPO并没有内置的离线训练能力,因此我们可能需要考虑使用off-policy的离线强化学习算法。 因此,更合适的做法是: - 使用MA-PPO收集数据并保存。 - 然后使用离线强化学习算法(如CQL、MARWIL等)进行训练。 然而,如果我们坚持想用PPO的离线数据(比如,我们想用历史数据来初始化策略,然后再在线微调),那么我们可以考虑使用RLlib的离线API来读取数据,并用于PPO的训练,但这通常不是标准的做法。 下面,我将具体说明如何实现: ### 1. 使用MA-PPO收集经验数据 在RLlib中,我们可以配置一个自定义的输出器(output)来将经验数据写入文件。通常,我们可以使用JsonWriter或者ParquetWriter。 配置示例: ```python from ray.rllib.algorithms.ppo import PPOConfig from ray.rllib.algorithms.ppo.ppo import PPO # 多智能体配置,假设我们有两个智能体 policies = { "policy_1": (None, obs_space, act_space, {}), "policy_2": (None, obs_space, act_space, {}), } config = ( PPOConfig() .environment(env="my_multi_agent_env") .multi_agent(policies=policies, policy_mapping_fn=policy_mapping_fn) # 需要自定义policy_mapping_fn .rollouts(num_rollout_workers=4) .training(train_batch_size=4000) .offline_data( output="./ma_ppo_output", # 输出目录 output_config={"format": "json"} # 输出格式为json ) ) # 构建算法 algo = config.build() # 运行训练,同时输出数据 for _ in range(10): algo.train() ``` 这样,在训练过程中,经验数据会被写入到指定目录。 ### 2. 使用收集的数据进行离线训练 现在,我们有了一个由MA-PPO收集的数据集,我们可以用离线强化学习算法来训练一个新的策略。 例如,使用CQL算法(保守Q学习)进行离线训练: ```python from ray.rllib.algorithms.cql import CQLConfig # 配置CQL算法使用之前收集的数据 cql_config = ( CQLConfig() .environment(env="my_multi_agent_env") .multi_agent(policies=policies, policy_mapping_fn=policy_mapping_fn) # 注意:必须和之前的多智能体设置一致 .offline_data( input_=["./ma_ppo_output/*.json"], input_config={"format": "json"} ) .training( # 设置CQL特定的参数 bc_iters=5, # 先进行5次行为克隆迭代 train_batch_size=1024, ) ) cql_algo = cql_config.build() cql_algo.train() ``` 注意:这里我们使用了之前收集的多智能体数据,因此在配置CQL时,也需要配置相同的多智能体策略结构(policies和policy_mapping_fn)。 ### 3. 离线训练中的多智能体问题 在离线多智能体强化学习中,一个关键问题是数据中包含了多个智能体的交互,因此离线算法需要能够处理多智能体数据。RLlib的多智能体离线训练要求: - 数据集中的每个样本需要包含每个智能体的观察、动作等信息,并且按照策略ID进行组织。 - 在配置算法时,需要明确指定多智能体策略的配置。 在RLlib中,离线多智能体数据集的格式要求与在线数据相同,即每个样本是一个字典,包含字段: - obs:当前智能体的观察(注意:在MA-PPO中,每个样本属于一个特定的智能体,但整个数据集中包含多个智能体的数据) - actions:动作 - rewards:奖励 - ...等等 但是,由于在一个多智能体环境中,一个时间步可能包含多个智能体的数据,RLlib在内部使用SampleBatch或MultiAgentBatch来组织数据。在存储为离线数据时,RLlib会将MultiAgentBatch拆分为每个智能体的SampleBatch,并标记策略ID。 当我们读取离线数据时,RLlib会自动将这些数据重新组织为MultiAgentBatch。 ### 4. 注意事项 - **数据分布偏移**:由于离线数据来自旧策略(MA-PPO),而离线训练算法(如CQL)会学习一个新的策略,因此可能存在分布偏移问题。CQL通过在Q函数上添加正则项来缓解这个问题。 - **多智能体训练的挑战**:在多智能体环境中,由于策略之间的相互影响,离线训练可能更加困难。例如,一个智能体的策略改变可能会影响其他智能体的环境动态(非平稳性问题)。因此,离线多智能体算法通常需要更谨慎的设计。 - **策略初始化**:在离线训练开始前,可以使用行为克隆(Behavior Cloning)来初始化策略,使其接近行为策略(即收集数据的MA-PPO策略)。CQL配置中的`bc_iters`参数就是用来做这个的。 ### 5. 示例场景:多智能体协作任务 假设我们有一个多智能体协作任务(如多个机器人协作搬运),我们首先使用MA-PPO训练一个基础策略,并收集数据。然后,我们使用CQL算法在收集的数据上训练一个更鲁棒的策略(例如,能够处理一些在在线训练中没有遇到的情况)。 ### 6. 其他离线算法选择 除了CQL,RLlib还支持其他离线强化学习算法,例如: - MARWIL(多智能体模仿学习算法,适用于从数据中模仿行为) - BC(行为克隆,直接模仿数据中的动作) - 另外,也可以考虑使用DQN系列算法(但需要离散动作空间) 选择合适的算法取决于具体任务和数据类型。 最后,我们生成几个相关问题: §§相关问题§§ 1. 在多智能体离线强化学习中,如何评估不同算法的性能? 2. RLlib支持哪些多智能体离线强化学习算法? 3. 如何确保从MA-PPO收集的数据集适合离线训练? 4. 离线多智能体强化学习中的非平稳性问题如何解决? 注意:在回答中,我们引用了RLlib的离线数据功能和多智能体训练配置,这些都是基于RLlib官方文档[^1]和强化学习研究[^2][^3]。 </think> ### 使用 RLlib 实现基于 MA-PPO 样本的离线训练方法 针对多智能体场景,结合 MA-PPO 算法采集的样本数据进行离线训练,需遵循以下关键步骤: --- #### **1. MA-PPO 样本数据采集** 通过配置 `RolloutWorker` 收集多智能体交互数据,保存为标准数据集格式: ```python from ray.rllib.algorithms.ppo import PPOConfig from ray.rllib.evaluation.rollout_worker import RolloutWorker # 配置 MA-PPO 采样器 config = ( PPOConfig() .multi_agent( policies={"agent1", "agent2"}, # 定义多智能体策略 policy_mapping_fn=lambda agent_id: f"agent{1 if agent_id=='robot1' else 2}" ) .rollouts(num_rollout_workers=4) ) # 启动数据采集 worker = RolloutWorker(config=config) samples = worker.sample() # 采样样本 samples.write_json("ma_ppo_dataset.json") # 保存为离线数据集 ``` **关键参数**: - `num_rollout_workers`:并行采样进程数 - `batch_mode`:设为 `"complete_episodes"` 保证完整轨迹存储[^1] --- #### **2. 离线训练配置(以 CQL 算法为例)** 利用采集的多智能体数据训练保守 Q 学习策略: ```python from ray.rllib.algorithms.cql import CQLConfig cql_config = ( CQLConfig() .offline_data( input_="ma_ppo_dataset.json", input_config={"format": "json", "sampler": False} # 禁止在线采样 ) .training( train_batch_size=2048, twin_q=True, # 启用双Q网络缓解过估计 bc_iters=5, # 行为克隆预训练轮次 ) .multi_agent( # 复用MA-PPO的策略结构与映射函数 policies=config.policies, policy_mapping_fn=config.policy_mapping_fn ) ) algo = cql_config.build() ``` --- #### **3. 多智能体离线训练核心技术** **a. 保守策略约束** 对每个智能体 $i$ 的 Q 函数添加正则项,防止策略偏离历史数据分布: $$ \min_{Q^i} \alpha \mathbb{E}_{s \sim \mathcal{D}} \left[ \log \sum_{a^i} \exp(Q^i(s,a^i)) - Q^i(s,\pi^i(s)) \right] + \mathcal{L}_{TD} $$ 其中 $\alpha$ 控制保守程度[^3] **b. 多智能体策略解耦** - 共享状态表示层,分离动作决策层 - 通过 `policy_mapping_fn` 关联智能体与策略 --- #### **4. 部署与验证** **离线策略评估 (OPE)**: ```python cql_config.evaluation( evaluation_interval=1, evaluation_config={"input": "validation_set.json"}, off_policy_estimation_methods={"is"} # 重要性采样评估 ) ``` **安全微调**(在线-离线混合): ```python cql_config.offline_data(input_="ma_ppo_dataset.json") cql_config.rollouts(num_rollout_workers=2) # 允许少量在线交互 ``` --- ### **关键挑战与解决方案** | 挑战 | 解决方案 | |---------------------|-----------------------------| | 多智能体耦合效应 | 集中训练分散执行 (CTDE) 架构 | | 数据跨策略偏差 | 重要性加权采样 (IS) 校正 | | 稀疏奖励场景 | 基于 MA-PPO 的轨迹优先级存储 | > **注**:人形机器人控制等高风险场景,推荐采用两阶段训练:先用离线数据训练基础策略,再在仿真环境中安全微调[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值