xuance icon indicating copy to clipboard operation
xuance copied to clipboard

Input data normalization

Open wjh-scut opened this issue 1 month ago • 19 comments

作者你好,我想请问下,对于多智能体任务,xuance是否实现了对于状态和动作的自动归一化呢?假如我想自定义环境的话,我想知道是否需要我自己实现归一化操作以及动作的缩放操作。谢谢! @wenzhangliu

wjh-scut avatar Oct 25 '25 04:10 wjh-scut

对状态,玄策没有做归一化处理,如果需要处理的话,可能要手动操作一下。例如可以在agent.store_experience()方法里操作。对动作,玄策需要用户在封装环境的时候确定好动作的最大和最小取值(连续动作),并且Actor网络的最后一层激活函数也开放给用户选择,这样可确保输出动作的取值范围在预想之内。如果还是不清楚的话,可以给出你的具体情况再分析。参考玄策文档里的自定义环境部分教程,也可能会帮到你.

wenzhangliu avatar Oct 25 '25 14:10 wenzhangliu

非常感谢您的回复,我目前计划使用gym-pybullet-drones来实现多无人机任务,采用的状态包括了gym-pybullet-drones中的KIN以及深度图。想请教您: 1.我注意到在 def _process_observation(self, observations):,似乎是一个状态归一化的处理,但它没有在多智能体类中实现。所以想问下,如果需要对于多智能体任务进行状态归一化,是否要参考这个进行修改? 2.另外还想请问下,我如果在自定义的环境中直接对状态归一化,是否也是可行的?这样就不用去改动玄策的框架了呢? 3.当我使用这种混合观测时,我是否需要对玄策的表征网络部分进行修改呢?

wjh-scut avatar Oct 26 '25 09:10 wjh-scut

以下是对你问题的回复:

  1. _process_observation(self, observations)方法目前确实只在单智能体情况下使用,多智能体系统一般很少用这个trick,由于多智能体的观测之间相互影响,在CTDE框架下使用该归一化方法可能会出现比较致命的问题,不过也可以先试试独立学习的情况下存在不存在问题。
  2. 若在自定义环境中直接对状态归一化,可能会存在一系列问题,例如如何保证一段数据(如动作)取自同一个策略,何时更新,用哪些数据更新,这些都可能是潜在的问题。
  3. 不需要对表征网络进行修改,最好是在表征之前做归一化,所以建议你可以尝试使用玄策提供的callback类,通过回调函数来勾住训练过程中的某个阶段,在回调函数里处理归一化的问题。具体可参考:\url{https://xuance.readthedocs.io/en/latest/documents/usage/custom_callback.html}

wenzhangliu avatar Oct 26 '25 16:10 wenzhangliu

非常感谢您的详细解答,还想请问您: 1.关于您提到的“一系列问题”: 您指出在自定义环境中直接进行状态归一化可能存在数据与策略一致性、更新时机等问题。请问这个警告是否主要针对需要根据训练数据动态更新统计量(如运行均值/标准差)的自适应归一化方法?对于固定范围归一化(例如,在 _computeObs 中直接将角度除以 np.pi,将速度除以预设的 MAX_SPEED),这种方法在自定义环境内部实现是否合理呢? 2.关于 Callback 的推荐: 您推荐使用 Callback 来处理归一化。请问这个建议是专门针对实现自适应归一化(运行均值/标准差)的吗?还是说,即便是固定范围归一化,通过 Callback 实现(而不是在 _computeObs 中)也会有额外的好处(或者甚至是必要的)? 3.自适应归一化的必要性: 考虑到我的任务中,我计划在 _computeObs 中先进行必要的固定范围归一化。我是否还需要额外通过 Callback 添加自适应归一化(运行均值/标准差)?是否有特定的训练现象(例如,训练非常不稳定、收敛极其缓慢、对学习率等超参数特别敏感)可以作为判断是否需要添加自适应归一化的指标? 4.您提供的这个自定义callback的链接似乎没有内容:

Image

wjh-scut avatar Oct 27 '25 03:10 wjh-scut

  1. 如果是固定范围/固定比例的归一化,在自定义环境里应该是合理的,我认为不会有其他影响。

  2. 在callback里写的话,可以最大限度地不改变强化学习底层代码,有助于你的调试。我在大部分算法中的不同位置嵌入了相应的hook,你可以在回调函数里任意发挥。Callback不是只针对归一化而设置的,而是考虑到用户有可能在强化学习训练过程的不同地方加入自己预处理的部分,或观察相应的变量,所以允许用户在不改变底层代码的前提下在对应的位置添加相应的代码。

  3. 对于如何实现归一化的问题,其最重要的原则就是训练、测试的归一化过程需保持一致,否则会得出错误的性能曲线。我不清楚你说的在_computeObs()中实现具体是指什么意思,总之这和回调函数不是一回事,你可以在回调函数里实现_computeObs()这一步操作。至于是否会导致训练不稳,这个可能要具体问题具体分析,一般是不会出现这种情况。

  4. 链接更新:https://xuance.readthedocs.io/en/latest/documents/usage/custom_callback.html

wenzhangliu avatar Oct 27 '25 16:10 wenzhangliu

非常感谢您的回答,我在自定义环境时,参照了multi_agent_env中的drones.py,遇到了以下问题,希望能得到您的帮助: 1.根据文档,在创建新的多智能体环境时,需要继承自RawMultiAgentEnv。我比较疑惑的是,新的环境中对于observation_space,action_space以及obs和action有什么特定的形式要求吗?例如,对于observation_space,文档中给出例子的是: self.observation_space = {k: Box(-np.inf, np.inf, obs_shape_i) for k in self.agents} 然而,在我自己定义的环境中,观测空间是一个字典,且其中的每个分量都有不同的范围,并非都是[-np.inf, np.inf,],如下:

return spaces.Dict({str(i): spaces.Dict({"state": spaces.Box(low=obs_lower_bound,
                                                                         high=obs_upper_bound,
                                                                         dtype=np.float32
                                                                         ),
                                                     #  深度图
                                                     "dep": spaces.Box(low=0.0,
                                                                       high=1.0,
                                                                       shape=(self.IMG_RES[1],
                                                                              self.IMG_RES[0],
                                                                              1),
                                                                       dtype=np.float32
                                                                       ),
                                                     }) for i in range(self.NUM_DRONES)})

在这种情况下,我应该如何将其转换为符合XuanCe要求的格式呢?此外,对于action_space以及obs和action应该满足什么形式要求呢? 2.对于self.state_space变量,我注意到drones.py中定义如下:

self.state_space = Box(-np.inf, np.inf, shape=[20, ])

我想知道self.state_space的定义规则是什么?它必须是上面这种形式吗?还是说对于不同算法,不同任务,它有不同的构建规则?

wjh-scut avatar Oct 29 '25 14:10 wjh-scut

  1. 多智能体环境中,观测空间由字典定义,key是智能体名称,value是对应的向量空间。如果你的环境中,每个智能体的观测空间是另外一个字典,建议标准化为一个向量空间。至于取值范围,可以任意给定,不影响算法运行(但是动作空间需要特别考虑)。

对于你的情况,建议将“state”和“dep”两个子空间合并为一个整体的向量观测空间。如果需要同时处理图像(即矩阵空间)和向量,这里给出的建议是,自定义representation模块,在该模块中将图像和向量单独处理,分别用CNN和MLP做初步的表征,将得到的向量拼接成一个向量作为repsentation的输出。这样就可以和玄策中的其他模块如Policy、learner等实现接口兼容。

  1. state_space是相对于单个智能体部分观测空间而设的全局状态空间。针对一些算法,如QMIX,算法本身在CTDE框架下的训练阶段需要访问全局状态空间,一般它比联合观测空间维度更低。在玄策中,一般这种全局状态空间会被定义为一个n维实向量空间,因此定义为当下的形式。

wenzhangliu avatar Oct 29 '25 15:10 wenzhangliu

谢谢您的解答。请问: 1.如果观测空间取值范围可以任意给定,那是否意味着观测空间取值范围并没有在玄策中起到什么作用?只是观测空间的维度会有作用?只需将观测空间取值范围都定义为[-np.inf, np.inf],算法也可以正确运行? 2.对于每个智能体的动作空间,我定义的是spaces.Box(low=act_lower_bound, high=act_upper_bound, dtype=np.float32),维度是4为,范围是[-1,1],这种情况下,我需要按照文档中给出例子来定义吗:

self.action_space = {k: Box(-np.inf, np.inf, act_shape_i, seed=config.env_seed) for k in self.agents}

还是说我需要做一些调整?例如将范围改为[-1,1],而不是[-np.inf, np.inf] 3.关于state_space,是需要我根据自己的任务来定义一个合适的全局状态空间,而非简单的将所有智能体的观测拼成一个联合观测空间?请问您是这个意思吗? 此外,state信息是否应该从info字典中提取呢?我看示例代码中的实现是随机采样:return self.state_space.sample(),似乎像是一种噪声。我是否应该在自定义环境中去实现def state(self)函数,并标准化为一个BOX类型的向量空间呢? 4.我注意到您在MADDPG,MASAC和MATD3的learner类中,都没有使用state去计算价值函数,而是直接使用了观测的拼接。在MAPPO_Learner中,您才根据use_global_state的值来选择是否使用state。请问同样是CTDE架构,为什么MADDPG,MASAC和MATD3不使用全局状态state呢?

wjh-scut avatar Oct 30 '25 03:10 wjh-scut

  1. 是的,可以这么理解。
  2. 都行。如果设置了activation_action参数,会在Actor网络(如果用到AC结构的话)最后一层经过激活函数限定在特定范围,如tanh将动作限定在[-1, 1]。如果不设置activation_action,则默认不激活,智能体动作输出在[-inf,inf],直接给环境可能会报错(如果你的环境动作范围不是这个范围),所以需要做诸如裁剪的操作。
  3. 如果你的算法用不到全局state,不用设置state_space应该也行。示例代码只是示例,因为不是一个特定的动作,只好随机采样一个作为本次的state。
  4. 用不用state要看这个算法的本身设计,不是每个算法都用到它。至于原因,建议读一下原始论文,玄策的初衷是尽最大可能忠于原文来复现这些算法。

wenzhangliu avatar Oct 31 '25 15:10 wenzhangliu

非常感谢您的解答。我仍然有一些疑问想请教您:

1.我目前尝试保持观测空间不变,也即每个智能体的观测空间是一个字典。在这种情况下,我自定义了representation模块单独处理了向量和图像,然后拼接了它们。同时,我还对MARLAgents类的_build_representation和_build_inputs函数进行了修改,以适配我的观测类型。之后我发现似乎OffPolicyMARLAgents类中的_build_memory和store_experience函数也需要去做相应的修改,而且我不确定是否还有其他地方需要修改。请问我的这种思路是否需要改动的地方比较多,所以不太可行? 2.假如按照您之前提到的将“state”和“dep”两个子空间合并为一个整体的向量观测空间,指的是把dep展平,然后与state拼接在一起吗?那么环境在计算观测信息时,就要输出这种形式的obs吗?如果这样的话,我在自定义的representation模块中,应该是需要将这个整体向量再拆分成“state”和“dep”,然后去分别处理吧?对于这种方案,我除了自定义的representation模块外,还需要对玄策的其他部分做改动吗? 3.请问假如采用dep展平的方式,这样会破坏它的空间结构,即使后续reshape回来也会破坏原始的空间邻近关系,就无法用CNN学习了。请问您有其他方式将“state”和“dep”两个子空间合并为一个整体的向量观测空间吗?还是我这里关于合并方式的理解有误?

很抱歉又麻烦您了,希望您能给我一些指导,谢谢!

wjh-scut avatar Nov 02 '25 13:11 wjh-scut

  1. 感谢你的补充,memory部分是我没有考虑到的,玄策中的memory对每个智能体的观测是按照np.array来存储,不是按照字典。所以针对你的情况,可能还要仿照玄策的MARL_OffPolicyBuffer做进一步修改。
  2. 根据你的情况描述,目前我能想到的改动大概就是环境、representation、memory和agent,其中环境部分要尽可能保持和玄策给的案例保持一致。对于每个智能体的观测都是一个字典的情况,是建议在representation中做特征提取和特征拼接。在这个过程中你不得不修改memory的结构,包括存(store)和取(sample)。为了尽可能减少修改,我建议尽可能将这部分特殊处理放在representation模块里,这样就不影响其他部分的兼容性。
  3. 我的理解,这种拼接方式并不会影响梯度反向传播,不会影响CNN的学习。

wenzhangliu avatar Nov 03 '25 06:11 wenzhangliu

非常感谢您的解答!

wjh-scut avatar Nov 04 '25 10:11 wjh-scut

您好,我想请问下:我在使用iddpg训练时,发现内存占用一直增加,最后会导致程序崩溃。这个是不是跟我的观测输入中包含图像有关啊?应该如何解决这个问题呢?降低buffer_size的大小是否有效?

wjh-scut avatar Nov 13 '25 14:11 wjh-scut

那有可能,如果观测输入是图像,要确保你存进replay Buffer里的图像数据类型是uint8而不是浮点型,否则内存占用率会急剧上升。如果改好了还是爆内存,那么剩下的办法就只有降低Buffer size了

wenzhangliu avatar Nov 13 '25 14:11 wenzhangliu

我这边的图像观测从环境输出时就已经normalized到[0,1]范围了,不是整数了,请问这种情况下是不是就不能用uint8了?

wjh-scut avatar Nov 13 '25 14:11 wjh-scut

不建议,若你的像素是6464=4096,那么一幅照片就要存40964 bytes=16kB,随便存64张图片,就需要16kB * 64=1GB内存了;反之,如果当初存的是uint8,则内存只是其四分之一。这还只是 $s_t$,还要考虑 $s_{t+1}$。。。所以一般不会这样做

wenzhangliu avatar Nov 13 '25 15:11 wenzhangliu

我目前采用了您之前的建议,将“state”和“dep”两个子空间合并为一个整体的向量观测空间,具体操作是将dep展平后与state拼接在一起。state我采用了浮点型以保持精度,这种情况下,即使dep设置为uint8,拼接后的向量还是浮点型吧?针对这种情况,请问您有什么好的解决方法吗?

wjh-scut avatar Nov 13 '25 15:11 wjh-scut

一般是这样。那这样的话就只有将state和dep分开存储了。在玄策里也很好实现,只需继承一下对应的Buffer类,重新写一下self.clear()成员方法,为self.data成员变量新增一个key-value对,用于保存dep,设置为uint8型。

wenzhangliu avatar Nov 14 '25 13:11 wenzhangliu

好的,谢谢您,我再尝试下

wjh-scut avatar Nov 15 '25 03:11 wjh-scut